From 408d4019b33cd2e3cf574478bfe74916cd05331d Mon Sep 17 00:00:00 2001 From: John Reilly Pospos Date: Sat, 14 Feb 2026 12:57:33 +1000 Subject: [PATCH] Fix git commits re-populating dirty-files in default mode (#22) In default mode, after the memory-updater processes files and SubagentStop clears dirty-files, a git commit via Bash would re-trigger PostToolUse, extract committed files via git diff-tree, and re-add them to dirty-files. This caused the Stop hook to fire again for already-processed files. Add early-return guard so default mode skips git commit processing (files are already tracked via Edit/Write hooks). Also fix existing test to run commit context enrichment in gitmode where it belongs. --- CLAUDE.md | 2 ++ scripts/post-tool-use.py | 8 ++++++-- tests/test_hooks.py | 22 ++++++++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 51d8381..3d12286 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -122,6 +122,8 @@ Recent design decisions from commit history: - Template enforcement added to ensure consistent CLAUDE.md structure - Git commit context enrichment for better change tracking - Configurable trigger modes (default vs gitmode) +- Windows compatibility: python3/python fallback pattern in hook commands +- Default mode optimization: Skip git commit tracking (files already tracked via Edit/Write) diff --git a/scripts/post-tool-use.py b/scripts/post-tool-use.py index 468a5bb..62083e4 100644 --- a/scripts/post-tool-use.py +++ b/scripts/post-tool-use.py @@ -9,8 +9,8 @@ Supports configurable trigger modes: - default: Track Edit/Write/Bash operations (current behavior) - gitmode: Only track git commits -When a git commit is detected, enriches each file path with inline commit -context: /path/to/file [hash: commit message] +In gitmode, when a git commit is detected, enriches each file path with +inline commit context: /path/to/file [hash: commit message] """ from __future__ import annotations @@ -262,6 +262,10 @@ def main(): if trigger_mode == "gitmode" and not is_git_commit: return + # In default mode, skip git commits (files already tracked via Edit/Write hooks) + if trigger_mode == "default" and is_git_commit: + return + files_to_track = [] commit_context = None diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 6cc982c..363dacd 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -336,6 +336,19 @@ class TestPostToolUseHook: assert "deleted.py" in content assert "/dev/null" not in content + def test_skip_git_commit_in_default_mode(self, tmp_path): + """Hook skips git commit commands in default mode (files tracked via Edit/Write).""" + env = {"CLAUDE_PROJECT_DIR": str(tmp_path)} + subprocess.run( + [sys.executable, SCRIPTS_DIR / "post-tool-use.py"], + env={**os.environ, **env}, + input=self._make_bash_input("git commit -m 'Add feature'"), + capture_output=True, + text=True, + ) + dirty_file = tmp_path / ".claude" / "auto-memory" / "dirty-files" + assert not dirty_file.exists() + class TestStopHook: """Tests for trigger.py Stop hook behavior.""" @@ -753,10 +766,15 @@ class TestGitCommitContext: sys.modules.pop("post-tool-use", None) def test_commit_enriches_dirty_files_with_context(self, tmp_path): - """Git commit command enriches dirty files with inline context.""" + """Git commit command enriches dirty files with inline context in gitmode.""" # Initialize git repo self._init_git_repo(tmp_path) + # Set up gitmode config (commit enrichment only applies in gitmode) + config_dir = tmp_path / ".claude" / "auto-memory" + config_dir.mkdir(parents=True, exist_ok=True) + (config_dir / "config.json").write_text(json.dumps({"triggerMode": "gitmode"})) + # Create and commit a file test_file = tmp_path / "module.py" test_file.write_text("# module") @@ -778,7 +796,7 @@ class TestGitCommitContext: ) # Check dirty files contain commit context - dirty_file = tmp_path / ".claude" / "auto-memory" / "dirty-files" + dirty_file = config_dir / "dirty-files" assert dirty_file.exists() content = dirty_file.read_text()