From d698fbf06f5ceac8052a3ee991422a25ed743b3a Mon Sep 17 00:00:00 2001 From: worldmozara Date: Tue, 4 Aug 2026 21:38:20 +0800 Subject: [PATCH] core/tools: rename run_command to run_argv (argv param) --- src/plyngent/tools/__init__.py | 2 +- src/plyngent/tools/danger.py | 16 +++---- src/plyngent/tools/process/__init__.py | 4 +- src/plyngent/tools/process/command_exec.py | 2 +- .../process/{run_command.py => run_argv.py} | 12 +++--- tests/test_cli/test_limits.py | 6 +-- tests/test_tools/test_danger.py | 14 +++---- tests/test_tools/test_process.py | 42 +++++++++---------- 8 files changed, 49 insertions(+), 49 deletions(-) rename src/plyngent/tools/process/{run_command.py => run_argv.py} (80%) diff --git a/src/plyngent/tools/__init__.py b/src/plyngent/tools/__init__.py index 1daaca4..56f221b 100644 --- a/src/plyngent/tools/__init__.py +++ b/src/plyngent/tools/__init__.py @@ -41,7 +41,7 @@ from .process import ask_into_pty as ask_into_pty from .process import close_pty as close_pty from .process import open_pty as open_pty from .process import read_pty as read_pty -from .process import run_command as run_command +from .process import run_argv as run_argv from .process import run_command_batch as run_command_batch from .process import write_pty as write_pty from .process import write_pty_keys as write_pty_keys diff --git a/src/plyngent/tools/danger.py b/src/plyngent/tools/danger.py index 58cde2c..921f356 100644 --- a/src/plyngent/tools/danger.py +++ b/src/plyngent/tools/danger.py @@ -59,8 +59,8 @@ def _basename(argv0: str) -> str: return name.lower() -def _as_argv(args: Mapping[str, object]) -> list[str] | None: - command = args.get("command") +def _as_argv(args: Mapping[str, object], *, key: str = "command") -> list[str] | None: + command = args.get(key) if not isinstance(command, list) or not command: return None out: list[str] = [] @@ -110,7 +110,7 @@ def _shell_or_dash_c_reason(argv: Sequence[str], *, via: str) -> str | None: """Confirm interactive shells and ``*-c`` one-liners so the user can inspect argv. Multi-line reason (shown inside the CLI confirm box). ``via`` is a short - label such as ``run_command`` or ``open_pty``. + label such as ``run_argv`` or ``open_pty``. For ``-c`` scripts, argv shows ``$(command)`` instead of inlining the body; the full script is printed below untruncated, with every line indented. @@ -181,11 +181,11 @@ def _delete_path_reason(args: Mapping[str, object]) -> str | None: return f"delete path {path!r}{extra}" -def _run_command_reason(args: Mapping[str, object]) -> str | None: - argv = _as_argv(args) +def _run_argv_reason(args: Mapping[str, object]) -> str | None: + argv = _as_argv(args, key="argv") if argv is None: return None - return _shell_or_dash_c_reason(argv, via="run_command") + return _shell_or_dash_c_reason(argv, via="run_argv") def _batch_step_argv(item: object) -> list[str] | None: @@ -262,8 +262,8 @@ def classify_danger(name: str, args: Mapping[str, object]) -> str | None: # noq return _copy_path_reason(args) if name == "write_file": return _write_file_reason(args) - if name == "run_command": - return _run_command_reason(args) + if name == "run_argv": + return _run_argv_reason(args) if name == "run_command_batch": return _run_command_batch_reason(args) if name == "open_pty": diff --git a/src/plyngent/tools/process/__init__.py b/src/plyngent/tools/process/__init__.py index faa85d4..c68b77e 100644 --- a/src/plyngent/tools/process/__init__.py +++ b/src/plyngent/tools/process/__init__.py @@ -4,13 +4,13 @@ from .open_pty import open_pty as open_pty from .pty_terminal import decode_write_data as decode_write_data from .pty_terminal import sanitize_pty_output_for_tool as sanitize_pty_output_for_tool from .read_pty import read_pty as read_pty -from .run_command import run_command as run_command +from .run_argv import run_argv as run_argv from .run_command_batch import run_command_batch as run_command_batch from .write_pty import write_pty as write_pty from .write_pty_keys import write_pty_keys as write_pty_keys PROCESS_TOOLS = [ - run_command, + run_argv, run_command_batch, open_pty, read_pty, diff --git a/src/plyngent/tools/process/command_exec.py b/src/plyngent/tools/process/command_exec.py index 3b4f726..270cfbc 100644 --- a/src/plyngent/tools/process/command_exec.py +++ b/src/plyngent/tools/process/command_exec.py @@ -83,7 +83,7 @@ async def execute_argv( timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS, mix_stderr: bool = False, ) -> CommandStepResult: - """Run one argv command; no shell. Used by run_command and run_command_batch.""" + """Run one argv command; no shell. Used by run_argv and run_command_batch.""" if not command: msg = "command argv must not be empty" raise WorkspaceError(msg) diff --git a/src/plyngent/tools/process/run_command.py b/src/plyngent/tools/process/run_argv.py similarity index 80% rename from src/plyngent/tools/process/run_command.py rename to src/plyngent/tools/process/run_argv.py index 814dfbb..f9a7741 100644 --- a/src/plyngent/tools/process/run_command.py +++ b/src/plyngent/tools/process/run_argv.py @@ -11,21 +11,21 @@ from .command_exec import ( @tool(tags=ToolTag.LOCAL | ToolTag.INSTANCE_STATE | ToolTag.YOLO) -async def run_command( - command: list[str], +async def run_argv( + argv: list[str], *, cwd: str = ".", timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS, stdin: str | None = None, env: dict[str, str] | None = None, ) -> str: - """Run ``command`` (argv, no shell) under the workspace; capture stdout/stderr.""" - shape_error = argv_shape_error(command) + """Run ``argv`` (argv, no shell) under the workspace; capture stdout/stderr.""" + shape_error = argv_shape_error(argv) if shape_error is not None: - return f"error: `run_command` {shape_error}" + return f"error: `run_argv` {shape_error}" try: result = await execute_argv( - command, + argv, cwd=cwd, env=env, stdin=stdin, diff --git a/tests/test_cli/test_limits.py b/tests/test_cli/test_limits.py index 9058706..1125be8 100644 --- a/tests/test_cli/test_limits.py +++ b/tests/test_cli/test_limits.py @@ -30,10 +30,10 @@ def test_prompt_confirm_tool_default_deny() -> None: def test_format_tool_confirm_box_multiline() -> None: - reason = "run_command: python3 -c (review code before allow)\nargv:\n python3 -c print(1)\n-c code:\n print(1)" - box = format_tool_confirm_box("run_command", reason) + reason = "run_argv: python3 -c (review code before allow)\nargv:\n python3 -c print(1)\n-c code:\n print(1)" + box = format_tool_confirm_box("run_argv", reason) assert "┌" in box and "└" in box - assert "confirm · tool 'run_command'" in box + assert "confirm · tool 'run_argv'" in box assert "python3 -c" in box assert "print(1)" in box # Distinct lines, not one jammed row diff --git a/tests/test_tools/test_danger.py b/tests/test_tools/test_danger.py index 9b10e44..22fe9cd 100644 --- a/tests/test_tools/test_danger.py +++ b/tests/test_tools/test_danger.py @@ -43,9 +43,9 @@ def test_classify_write_overwrite_only(tmp_path: Path) -> None: def test_classify_safe_tools() -> None: assert classify_danger("read_file", {"path": "a"}) is None assert classify_danger("listdir", {"path": "."}) is None - assert classify_danger("run_command", {"command": ["echo", "hi"]}) is None + assert classify_danger("run_argv", {"argv": ["echo", "hi"]}) is None assert classify_danger("open_pty", {"command": ["true"]}) is None - assert classify_danger("run_command", {"command": ["ls", "-la"]}) is None + assert classify_danger("run_argv", {"argv": ["ls", "-la"]}) is None def test_classify_run_command_batch_risky() -> None: @@ -71,18 +71,18 @@ def test_classify_run_command_batch_risky() -> None: def test_classify_shell_and_dash_c() -> None: - r = classify_danger("run_command", {"command": ["bash", "-c", "rm -rf /"]}) + r = classify_danger("run_argv", {"argv": ["bash", "-c", "rm -rf /"]}) assert r is not None assert "bash -c" in r assert "rm -rf" in r - r2 = classify_danger("run_command", {"command": ["python3", "-c", "print(1)"]}) + r2 = classify_danger("run_argv", {"argv": ["python3", "-c", "print(1)"]}) assert r2 is not None assert "python" in r2 assert "-c" in r2 assert "print(1)" in r2 - r_py = classify_danger("run_command", {"command": ["python", "-c", "import os"]}) + r_py = classify_danger("run_argv", {"argv": ["python", "-c", "import os"]}) assert r_py is not None and "python -c" in r_py r3 = classify_danger("open_pty", {"command": ["bash"]}) @@ -118,8 +118,8 @@ async def test_confirm_deny_with_comment() -> None: def test_shell_confirm_formats_command_placeholder() -> None: script = chr(10).join(["line1", "line2", "line3"]) reason = classify_danger( - "run_command", - {"command": ["bash", "-c", script]}, + "run_argv", + {"argv": ["bash", "-c", script]}, ) assert reason is not None assert "$(command)" in reason diff --git a/tests/test_tools/test_process.py b/tests/test_tools/test_process.py index 4154622..4c89843 100644 --- a/tests/test_tools/test_process.py +++ b/tests/test_tools/test_process.py @@ -9,7 +9,7 @@ from plyngent.tools.process import ( close_pty, open_pty, read_pty, - run_command, + run_argv, run_command_batch, write_pty, write_pty_keys, @@ -41,32 +41,32 @@ def _py(code: str) -> list[str]: return [sys.executable, "-c", code] -async def test_run_command_echo(workspace: object) -> None: +async def test_run_argv_echo(workspace: object) -> None: del workspace - out = await call_async(run_command, _py("print('hi')")) + out = await call_async(run_argv, _py("print('hi')")) assert "exit_code=0" in out assert "hi" in out -async def test_run_command_string_command_error(workspace: object) -> None: +async def test_run_argv_string_command_error(workspace: object) -> None: del workspace - out = await call_async(run_command, "ls -la") + out = await call_async(run_argv, "ls -la") assert "error:" in out assert "only accepts an array" in out assert "executable not found" not in out -async def test_run_command_stringified_json_array_error(workspace: object) -> None: +async def test_run_argv_stringified_json_array_error(workspace: object) -> None: del workspace - out = await call_async(run_command, '["ls", "-la"]') + out = await call_async(run_argv, '["ls", "-la"]') assert "error:" in out assert "only accepts an array" in out assert "executable not found" not in out -async def test_run_command_non_string_args_error(workspace: object) -> None: +async def test_run_argv_non_string_args_error(workspace: object) -> None: del workspace - out = await call_async(run_command, ["ls", 1]) + out = await call_async(run_argv, ["ls", 1]) assert "error:" in out assert "array of string args" in out @@ -157,32 +157,32 @@ async def test_run_command_batch_mix_stderr(workspace: object) -> None: assert "out" in out and "err" in out -async def test_run_command_denied(workspace: object) -> None: +async def test_run_argv_denied(workspace: object) -> None: del workspace - out = await call_async(run_command, ["rm", "-rf", "."]) + out = await call_async(run_argv, ["rm", "-rf", "."]) assert "denied" in out -async def test_run_command_cwd(workspace: object) -> None: +async def test_run_argv_cwd(workspace: object) -> None: assert isinstance(workspace, Path) sub = workspace / "sub" sub.mkdir() _ = (sub / "f.txt").write_text("z", encoding="utf-8") - out = await call_async(run_command, _py("import os; print('\\n'.join(os.listdir()))"), cwd="sub") + out = await call_async(run_argv, _py("import os; print('\\n'.join(os.listdir()))"), cwd="sub") assert "f.txt" in out -async def test_run_command_timeout(workspace: object) -> None: +async def test_run_argv_timeout(workspace: object) -> None: del workspace - out = await call_async(run_command, _py("import time; time.sleep(5)"), timeout_seconds=0.2) + out = await call_async(run_argv, _py("import time; time.sleep(5)"), timeout_seconds=0.2) assert "timed_out=true" in out -async def test_run_command_timeout_keeps_partial_output(workspace: object) -> None: +async def test_run_argv_timeout_keeps_partial_output(workspace: object) -> None: del workspace # Print then sleep past the timeout so communicate has partial stdout after kill. out = await call_async( - run_command, + run_argv, _py("import sys, time; sys.stdout.write('partial-out'); sys.stdout.flush(); time.sleep(5)"), timeout_seconds=0.3, ) @@ -190,10 +190,10 @@ async def test_run_command_timeout_keeps_partial_output(workspace: object) -> No assert "partial-out" in out -async def test_run_command_stdin(workspace: object) -> None: +async def test_run_argv_stdin(workspace: object) -> None: del workspace out = await call_async( - run_command, + run_argv, _py("import sys; print(sys.stdin.read(), end='')"), stdin="hello-stdin\n", ) @@ -201,10 +201,10 @@ async def test_run_command_stdin(workspace: object) -> None: assert "hello-stdin" in out -async def test_run_command_env(workspace: object) -> None: +async def test_run_argv_env(workspace: object) -> None: del workspace out = await call_async( - run_command, + run_argv, _py("import os; print(os.environ.get('PLYNGENT_TEST_VAR', ''), end='')"), env={"PLYNGENT_TEST_VAR": "from-env"}, )