feat: git post-commit hook + cli install-git-hook#37
Open
jakduch wants to merge 4 commits into
Open
Conversation
Tracks which Claude Code sessions led to which commits.
- hooks/post-commit: bash script that appends {repo, sha, message,
author, timestamp, session_id} to ~/.claude/git-trace.jsonl after
every commit. Self-detects session_id via $CLAUDE_SESSION_ID env
or the most-recently-modified JSONL in ~/.claude/projects/.
- cli.py: 'install-git-hook' installs per-repo (default) or --global,
sets core.hooksPath, and ships the hook into the repo as a visible
file (not hidden inside .git/hooks/).
- dashboard.py: _load_git_trace() reads the JSONL, the field
'git_trace_recent' (last 50) is included in /api/data, and an
insight card on the dashboard shows the top sessions producing
commits in the last 7 days.
- tests/test_git_hook.py: 14 tests across hook script, hook execution
against a temp repo, the installer CLI, and the loader.
b786834 to
bbe7e18
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this add and why do you believe it belongs in this dashboard?
This adds a tiny, dependency-free
post-commitgit hook plus acli.py install-git-hookcommand that wires it into the user's repos. The hook writes one JSON record per commit to~/.claude/git-trace.jsonl(repo,sha,message,author,timestamp,session_id), andget_dashboard_datasurfaces the last 50 entries to the frontend, where a new insight card lists the top sessions that produced commits in the last 7 days, linkified to the existing session-detail panel.The whole point of a personal Claude Code usage dashboard is the question "what did I actually get out of this token spend?" — and the most concrete answer is the commits that landed. Today the dashboard can show that session
abc123cost $4.20 and ran 38 turns, but it can't say that's the session that shipped the auth refactor. This PR closes that loop: every commit tags itself with the session that produced it (via$CLAUDE_SESSION_ID, with a 10-minute most-recently-touched-JSONL fallback for plain CLI sessions), so the dashboard finally connects spend to outcome. It's opt-in viainstall-git-hook, scoped per-repo by default (or--global), and the hook alwaysexit 0s so it can never block a commit.Checklist
Code correctness
calcCost()calls pass 6 arguments:(model, inp, out, cache_read, cache_creation, cache_1h)— N/A, no newcalcCostcall sites`), not escaped ones (\`)renderGitTraceCard/selectSessionByFullId/escare all hoisted function declarationsshutil/subprocessTests
python3 -m unittest discover -s tests -v— all 204 passingpython3 -m unittest tests.test_browser -v— all 6 passingtests/test_git_hook.py(13 tests: hook script presence, end-to-end commit + JSONL append, JSON-escaping of tricky messages,install-git-hookper-repo install, outside-repo exit code,_load_git_traceparsing + corruption tolerance +/api/datapayload integration)Scope
dashboard.py,scanner.py,cli.py,pricing.py,cowork.py,tests/) — or I've explained below why a new file is neededNew files needed for this feature:
hooks/post-commit— the shipped bash hook. Has to live as a standalone file soinstall-git-hookcanshutil.copy2it into<repo>/.claude-usage-hooks/or~/.git-hooks/, and sochmod +xis meaningful in source control.tests/test_git_hook.py— co-located with the rest of the test suite.Audit fixes applied on top of the original branch
hooks/post-commit: refactored the SESSION_ID-resolution block. The original nested"$(find ... | while ...; mt="$(perl -e '...')"; done | sort ...)"tripped a bash parser quirk (apostrophes inside#comments inside the outer$(...)) — a cleanbash -nwouldunexpected EOF while looking for matching'. Extracted the find/while pipeline into a_claude_usage_find_recent_jsonlhelper so the outer command substitution stays a single function call, and dropped the bare apostrophes from comments. Without this fix, the hook fails to run on commit (so no JSONL record is ever written).tests/test_git_hook.py: compared paths againstPath(self.repo).resolve()instead ofstr(self.repo). On macOStempfile.mkdtemp()returns/tmp/...butgit rev-parse --show-toplevelresolves the/tmp -> /private/tmpsymlink, so the raw equality check failed on every Mac..github/PULL_REQUEST_TEMPLATE.md(the branch had accidentally deleted it).