From f8b73cc3d775a1e781c569f4ad572fc067900fa6 Mon Sep 17 00:00:00 2001 From: can1357 Date: Tue, 17 Mar 2026 19:26:28 +0100 Subject: [PATCH] feat(coding-agent/ipy): added command tracking and exit code handling to ShellResult - Added `args` field to ShellResult to capture the executed command. - Added `exit_code` property to ShellResult as an alias for `returncode`. - Added `check_returncode()` method to ShellResult to raise CalledProcessError on non-zero exit codes. - Renamed `code` field to `returncode` in ShellResult with backward-compatible `code` property. - Updated `run()` command documentation to clarify available ShellResult fields. --- packages/coding-agent/CHANGELOG.md | 10 +++++++ packages/coding-agent/src/ipy/prelude.py | 33 ++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index a91157354..6b9db44d6 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog ## [Unreleased] +### Added + +- Added `args` field to ShellResult to capture the executed command +- Added `exit_code` property to ShellResult as an alias for `returncode` +- Added `check_returncode()` method to ShellResult to raise CalledProcessError on non-zero exit codes + +### Changed + +- Renamed `code` field to `returncode` in ShellResult (accessible via `code` property for backward compatibility) +- Updated `run()` command documentation to clarify available ShellResult fields ## [13.12.9] - 2026-03-17 ### Added diff --git a/packages/coding-agent/src/ipy/prelude.py b/packages/coding-agent/src/ipy/prelude.py index c09f01962..e6f3a7cdf 100644 --- a/packages/coding-agent/src/ipy/prelude.py +++ b/packages/coding-agent/src/ipy/prelude.py @@ -305,23 +305,40 @@ if "__omp_prelude_loaded__" not in globals(): class ShellResult: """Result from shell command execution.""" - __slots__ = ("stdout", "stderr", "code") - def __init__(self, stdout: str, stderr: str, code: int): + __slots__ = ("args", "stdout", "stderr", "returncode") + def __init__(self, args: str, stdout: str, stderr: str, returncode: int): + self.args = args self.stdout = stdout self.stderr = stderr - self.code = code + self.returncode = returncode + + @property + def code(self) -> int: + return self.returncode + + @property + def exit_code(self) -> int: + return self.returncode + + def check_returncode(self) -> None: + if self.returncode != 0: + raise subprocess.CalledProcessError( + self.returncode, self.args, output=self.stdout, stderr=self.stderr + ) + def __repr__(self): - if self.code == 0: + if self.returncode == 0: return "" - return f"exit code {self.code}" + return f"exit code {self.returncode}" + def __bool__(self): - return self.code == 0 + return self.returncode == 0 def _make_shell_result(proc: subprocess.CompletedProcess[str], cmd: str) -> ShellResult: """Create ShellResult and emit status.""" output = proc.stdout + proc.stderr if proc.stderr else proc.stdout _emit_status("sh", cmd=cmd[:80], code=proc.returncode, output=output[:500]) - return ShellResult(proc.stdout, proc.stderr, proc.returncode) + return ShellResult(cmd, proc.stdout, proc.stderr, proc.returncode) import signal as _signal @@ -356,7 +373,7 @@ if "__omp_prelude_loaded__" not in globals(): @_category("Shell") def run(cmd: str, *, cwd: str | Path | None = None, timeout: int | None = None) -> ShellResult: - """Run a shell command.""" + """Run a shell command. Returns ShellResult with stdout/stderr and returncode/exit_code fields.""" shell_path = shutil.which("bash") or shutil.which("sh") or "/bin/sh" args = [shell_path, "-c", cmd] return _run_with_interrupt(args, str(cwd) if cwd else None, timeout, cmd)