ducklm/app/tools/plugins/shell_exec/__init__.py

36 lines
1.4 KiB
Python

from __future__ import annotations
from app.core.contracts import ToolResult, UserTask
from app.tools.base import BaseTool
from app.tools.sandbox import ToolSandbox
class Tool(BaseTool):
name = "shell_exec"
description = "Execute shell commands"
def __init__(self, sandbox: ToolSandbox) -> None:
self._sandbox = sandbox
def execute(self, task: UserTask, args: dict[str, object]) -> ToolResult:
command = str(args.get("command", "")).strip()
if not command:
return ToolResult(tool=self.name, ok=False, error="Missing command", metadata={"exit_code": -1})
cwd = args.get("cwd")
stdin_secret = args.get("stdin_secret")
completed = self._sandbox.run_shell(
command=command,
cwd=str(cwd) if cwd else None,
stdin_data=str(stdin_secret) if stdin_secret is not None else None,
)
output = completed.stdout if completed.returncode == 0 else completed.stderr or completed.stdout
grep_no_matches = "grep" in command and completed.returncode == 1 and not completed.stderr
ok = completed.returncode == 0 or grep_no_matches
return ToolResult(
tool=self.name,
ok=ok,
output=output,
error=None if ok else f"Command failed with exit code {completed.returncode}",
metadata={"exit_code": completed.returncode, "no_matches": grep_no_matches},
)