From 758c8a0df1e3f7a5dd94f9db9eaa713e2aad53e4 Mon Sep 17 00:00:00 2001 From: SHL0MS Date: Fri, 3 Apr 2026 15:43:32 -0400 Subject: [PATCH] feat: add /diff command to show git changes in working directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shows staged and unstaged changes in the current working directory. /diff shows stat summary + full diff, /diff --stat shows summary only. Uses git diff directly — no checkpoint system required. Works in any git repository. Closes #4250 --- cli.py | 62 ++++++++++++++++++++++++++++++++++++++++++ hermes_cli/commands.py | 2 ++ 2 files changed, 64 insertions(+) diff --git a/cli.py b/cli.py index fa32ae9119d8..460e818af5a3 100644 --- a/cli.py +++ b/cli.py @@ -3674,6 +3674,66 @@ def _handle_branch_command(self, cmd_original: str) -> None: ) _cprint(f" Original session: {parent_session_id}") _cprint(f" Branch session: {new_session_id}") + def _handle_diff_command(self, cmd: str): + """Show git diff of changes in the current working directory.""" + import subprocess as _sp + + cwd = os.getenv("TERMINAL_CWD", os.getcwd()) + parts = cmd.split()[1:] + stat_only = "--stat" in parts + + # Check if we're in a git repo + try: + _sp.run( + ["git", "rev-parse", "--is-inside-work-tree"], + cwd=cwd, capture_output=True, check=True, timeout=5, + ) + except Exception: + _cprint(f" {_DIM}Not a git repository.{_RST}") + return + + try: + # Show stat summary + stat_result = _sp.run( + ["git", "diff", "--stat"], + cwd=cwd, capture_output=True, text=True, timeout=10, + ) + staged_result = _sp.run( + ["git", "diff", "--cached", "--stat"], + cwd=cwd, capture_output=True, text=True, timeout=10, + ) + + stat_out = stat_result.stdout.strip() + staged_out = staged_result.stdout.strip() + + if not stat_out and not staged_out: + _cprint(f" {_DIM}No changes.{_RST}") + return + + if staged_out: + _cprint(f"\n {_BOLD}Staged:{_RST}") + self.console.print(_rich_text_from_ansi(staged_out)) + if stat_out: + _cprint(f"\n {_BOLD}Unstaged:{_RST}") + self.console.print(_rich_text_from_ansi(stat_out)) + + if stat_only: + return + + # Show full diff + diff_result = _sp.run( + ["git", "diff", "--cached"] if staged_out and not stat_out else ["git", "diff"], + cwd=cwd, capture_output=True, text=True, timeout=30, + ) + diff_out = diff_result.stdout.strip() + if diff_out: + _cprint("") + self.console.print(_rich_text_from_ansi(diff_out)) + + except _sp.TimeoutExpired: + _cprint(f" {_DIM}Git diff timed out.{_RST}") + except Exception as e: + _cprint(f" {_DIM}Git diff error: {e}{_RST}") def save_conversation(self): """Save the current conversation to a file.""" @@ -4586,6 +4646,8 @@ def process_command(self, command: str) -> bool: self._status_bar_visible = not self._status_bar_visible state = "visible" if self._status_bar_visible else "hidden" self.console.print(f" Status bar {state}") + elif canonical == "diff": + self._handle_diff_command(cmd_original) elif canonical == "verbose": self._toggle_verbose() elif canonical == "yolo": diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index 70d9cb8aa306..52e717089be0 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -93,6 +93,8 @@ class CommandDef: args_hint="[name]"), CommandDef("statusbar", "Toggle the context/model status bar", "Configuration", cli_only=True, aliases=("sb",)), + CommandDef("diff", "Show git diff of changes in the current working directory", "Info", + cli_only=True, args_hint="[--stat]"), CommandDef("verbose", "Cycle tool progress display: off -> new -> all -> verbose", "Configuration", cli_only=True, gateway_config_gate="display.tool_progress_command"),