diff --git a/cli.py b/cli.py index c48696a1e14d..b9bd5dfcfac3 100644 --- a/cli.py +++ b/cli.py @@ -8024,6 +8024,66 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): return True + 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 JSON snapshot under ~/.hermes/sessions/saved/. @@ -9688,6 +9748,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): 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 == "battery": self._handle_battery_command(cmd_original) elif canonical == "timestamps": diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index 2ba32d598b8e..c00201021d06 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -151,6 +151,8 @@ COMMAND_REGISTRY: list[CommandDef] = [ CommandDef("timestamps", "Toggle [HH:MM] timestamps on messages and /history", "Configuration", cli_only=True, args_hint="[on|off|status]", subcommands=("on", "off", "status"), aliases=("ts",)), + 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 -> log", "Configuration", cli_only=True, gateway_config_gate="display.tool_progress_command"),