ci/hooks/SMART_ON_STOP_SETUP.md
Smart fingerprinting has been integrated into your existing on_stop hook at ~/.claude/hooks/check-on-stop.py. No setup required!
The on_stop hook runs every time an agent session ends, even if:
This is wasteful for expensive operations like:
Uses git status --porcelain to fingerprint the current repo state. Only executes hook if:
Fingerprint storage: .cache/last_agent_changes_fingerprint
~/.claude/hooks/check-on-stop.py - Your existing on_stop hook now includes:
get_current_fingerprint() - Get MD5 of git status --porcelainshould_skip_hook() - Determine if lint+tests should runNo configuration changes needed - Claude Code already uses this hook!
Agent session ends
→ Check git status --porcelain (has changes)
→ Save fingerprint: MD5("M src/file.cpp\nA tests/new.cpp")
→ Run hook (first_run)
Agent session ends
→ Check git status --porcelain (has changes)
→ Compute fingerprint: MD5("M src/file.cpp\nA tests/new.cpp")
→ Compare with stored: MATCH ✓
→ Skip hook (same_changes) ⏭️
Agent session ends
→ Check git status --porcelain (different changes)
→ Compute fingerprint: MD5("M src/file2.cpp\nD tests/old.cpp")
→ Compare with stored: DIFFERENT ✗
→ Save new fingerprint
→ Run hook (new_changes) 🔧
Agent session ends
→ Check git status --porcelain (CLEAN)
→ Skip hook (no_changes) ⏭️
The hook prints one of:
no_changes - Repository is clean (no modifications)first_run - First time seeing this fingerprintsame_changes - Fingerprint unchanged since last hooknew_changes - Fingerprint differs from last hookerror - Git error (runs hook to be safe)The hook runs lint and C++ tests in parallel when:
If lint fails, tests are cancelled and only lint errors are shown. Otherwise, both results are reported.
Output messages:
⏭️ Skipping lint+tests (no new changes) - Fingerprint unchanged, hook skipped🔧 Running lint and tests (new changes detected) - Fingerprint differs, hook running| Scenario | Before | After |
|---|---|---|
| Agent makes no changes | Hook runs (wasted) | Hook skipped ⏭️ |
| Agent makes same changes twice | Hook runs twice (wasteful) | Hook runs once ⏭️ |
| Agent makes new changes | Hook runs ✓ | Hook runs ✓ |
| Repo is committed (clean) | Hook runs (wasted) | Hook skipped ⏭️ |
git command available in PATHPath(__file__).parent.parent.parent to locate repogit status --porcelain output (includes untracked files).cache/ directory (created automatically, not committed)Check what fingerprint was recorded:
cat .cache/last_agent_changes_fingerprint
# Output: {"fingerprint": "a1b2c3d4e5f6..."}
See current status:
git status --porcelain
.cache/last_agent_changes_fingerprint existsgit status --porcelain shows changesrm .cache/last_agent_changes_fingerprint~/.claude/hooks/check-on-stop.py is executable.cache/ directory is writable by Claude Code