from __future__ import annotations from typing import Any from core.tools.base import BaseTool, ToolContext from core.tools.registry import registry from core.events import bus from core.subprocess import run_command class PidTool(BaseTool): """ Process inspection and management tool. Provides visibility into running system processes and optional control. """ name = "pid" description = "Process listing, lookup, and management" # ========================================================= # EXECUTE # ========================================================= def execute(self, payload: dict[str, Any], ctx: ToolContext): action = str(payload.get("action", "list")).strip() bus.log( "PID", "pid_execute", "INFO", {"action": action} ) match action: case "list": return self.list_processes(payload) case "find": return self.find_process(payload) case "details": return self.process_details(payload) case "kill": return self.kill_process(payload, ctx) case _: raise ValueError(f"Unknown pid action: {action}") # ========================================================= # LIST PROCESSES # ========================================================= def list_processes(self, payload: dict[str, Any]): limit = payload.get("limit", 50) result = run_command( cmd=["tasklist"], ) if result.get("return_code") != 0: return { "status": "error", "stderr": result.get("stderr", "") } lines = result.get("stdout", "").splitlines() processes = [] for line in lines[3:]: # skip header rows parts = line.split() if len(parts) < 2: continue processes.append({ "name": parts[0], "pid": parts[1] }) if len(processes) >= limit: break return { "count": len(processes), "processes": processes } # ========================================================= # FIND PROCESS # ========================================================= def find_process(self, payload: dict[str, Any]): name = payload.get("name") if not isinstance(name, str): raise ValueError("name must be string") result = run_command( cmd=["tasklist", "/FI", f"IMAGENAME eq {name}"], ) return { "name": name, "raw": result.get("stdout", ""), "stderr": result.get("stderr", "") } # ========================================================= # PROCESS DETAILS # ========================================================= def process_details(self, payload: dict[str, Any]): pid = payload.get("pid") if not isinstance(pid, int): raise ValueError("pid must be int") result = run_command( cmd=["wmic", "process", "where", f"ProcessId={pid}", "get", "ProcessId,Name,CommandLine"], ) return { "pid": pid, "raw": result.get("stdout", ""), "stderr": result.get("stderr", "") } # ========================================================= # KILL PROCESS # ========================================================= def kill_process(self, payload: dict[str, Any], ctx: ToolContext): pid = payload.get("pid") force = payload.get("force", False) if not isinstance(pid, int): raise ValueError("pid must be int") if ctx.dry_run: return { "dry_run": True, "pid": pid, "force": force, "message": "Would terminate process" } cmd = ["taskkill", "/PID", str(pid)] if force: cmd.append("/F") result = run_command(cmd) return { "pid": pid, "status": "success" if result.get("return_code") == 0 else "error", "stdout": result.get("stdout", ""), "stderr": result.get("stderr", "") } # ========================================================= # REGISTER TOOL # ========================================================= registry.register(PidTool())