{"slug": "capture-why-ai-wrote-each-commit-stored-in-git-notes", "title": "Capture why AI wrote each commit, stored in Git-notes", "summary": "Gitwhy, a new open-source tool in beta, automatically records AI provenance for every git commit by installing a post-commit hook that captures environment variables such as $CLAUDE_CODE_MODEL, $COPILOT_MODEL, and $AI_AGENT, along with branch and commit message data, storing the information in git-notes. The tool, which requires no flags and works with standard git commit commands, infers intent, origin, ticket numbers, and model details, providing a provenance record via 'ghw why HEAD'.", "body_md": "Beta— gitwhy is under active development. Feedback welcome via[GitHub Issues].\n\nEvery `git commit`\n\nalready knows *what* changed. gitwhy remembers *why*.\n\nIt drops a single post-commit hook into your repo. That hook is a passive listener — it catches the environment variables AI tools leave behind (`$CLAUDE_CODE_MODEL`\n\n, `$COPILOT_MODEL`\n\n, `$AI_AGENT`\n\n), parses your branch name for a ticket, reads your commit message for intent, and writes a provenance record to git-notes. All without you typing a single flag.\n\n**Two steps. Zero flags. Plain git commit.**\n\n```\nghw init\ngit commit -m \"feat: add login handler\"   # standard git, nothing special\n\n# Later — what actually happened here?\nghw why HEAD\ngitwhy provenance record\n  ─────────────────────────\n  schema:    gitwhy/v1\n  target:    commit a3f1d8c\n  by:        agent:claude-code\n  when:      2026-06-25T10:00:00Z\n\n  intent:    add login handler\n  origin:    spec\n\n  context:\n    ticket:   PROJ-42\n    prompt:   unknown\n    model:    claude-sonnet-4-6\n    branch:   feature/PROJ-42-login\n```\n\nThat's the entire default workflow. `ghw init`\n\nonce, then never think about it again.\n\n`ghw init`\n\ninstalls a single script into `.git/hooks/post-commit`\n\n. Every time you (or an AI agent) run `git commit`\n\n, that hook fires automatically and silently captures:\n\n| What it sniffs | How |\n|---|---|\nWho / what agent |\nSniffs `$AI_AGENT` , `$COPILOT_AGENT_MODEL` — detects Claude Code, Copilot, Cursor, etc. |\nWhich model |\nReads `$CLAUDE_CODE_MODEL` , `$COPILOT_MODEL` , `$ANTHROPIC_MODEL` , `$OPENAI_MODEL` |\nWhy (intent) |\nPulls the subject line from your commit message |\nTicket number |\nScans `git branch` for `PROJ-123` patterns |\nOrigin (human vs spec) |\nInfers from conventional commit type (`feat` → `spec` , `chore` → `human` ) |\nPrompt (if AI) |\nCaptures `$COPILOT_AGENT_PROMPT` or `$CLAUDE_CODE_PROMPT` |\n\nAll of these environment variables are ephemeral — they exist only while the AI tool is running and vanish the moment the process exits. The hook intercepts them before they disappear.\n\nJust write a normal commit message. gitwhy parses it without any flags.\n\n```\ngit commit -m \"feat: add login handler\"\nintent:    add login handler      ← from commit message description\norigin:    spec                   ← inferred from \"feat\" type\nticket:    PROJ-42                ← parsed from branch feature/PROJ-42-login\nmodel:     claude-sonnet-4-6      ← detected from $COPILOT_MODEL / $CLAUDE_CODE_MODEL\nby:        agent:claude-code      ← detected from $AI_AGENT env var\nbranch:    feature/PROJ-42-login  ← from git\n```\n\ngitwhy reads [conventional commit](https://www.conventionalcommits.org/) format and maps it to provenance fields automatically:\n\n| Commit message | intent | origin |\n|---|---|---|\n`feat: add login handler` |\n`add login handler` |\n`spec` |\n`fix: null pointer in auth` |\n`null pointer in auth` |\n`spec` |\n`perf: cache token lookup` |\n`cache token lookup` |\n`spec` |\n`chore: update deps` |\n`update deps` |\n`human` |\n`docs: add API examples` |\n`add API examples` |\n`human` |\n`test: cover edge cases` |\n`cover edge cases` |\n`human` |\n`feat!: breaking auth change` |\n`BREAKING: breaking auth change` |\n`spec` |\n\nNon-conventional messages fall back to LLM summarization (if configured) then `\"unknown\"`\n\n.\n\ngitwhy scans the branch name for a `PROJECT-123`\n\npattern and sets it as the ticket automatically. No flags needed.\n\n```\nfeature/PROJ-42-login   →  ticket: PROJ-42\nfix/AUTH-7-token-null   →  ticket: AUTH-7\nmain                    →  ticket: unknown\n```\n\ngitwhy reads environment variables set by AI tools at commit time:\n\n| Tool | Env var read | Captured as |\n|---|---|---|\n| Claude Code | `AI_AGENT=claude-code/...` |\n`by: agent:claude-code` |\n| Claude Code | `CLAUDE_CODE_MODEL` |\n`model: claude-sonnet-4-6` |\n| GitHub Copilot CLI | `COPILOT_AGENT_MODEL` or `COPILOT_MODEL` |\n`by: copilot` , `model: gpt-4o` |\n| GitHub Copilot CLI | `COPILOT_AGENT_PROMPT` |\n`prompt: ...` |\n| Any tool | `ANTHROPIC_MODEL` , `OPENAI_MODEL` , `GITHUB_MODEL` , `AI_MODEL` |\n`model: ...` |\n\nIf no env var is found, `model`\n\nfalls back to `default_model`\n\nin `.gitwhy/config.yaml`\n\n, then `\"unknown\"`\n\n.\n\nSet a default model once, and every commit picks it up:\n\n```\nghw config set default_model claude-sonnet-4-6\n```\n\n`git log` |\n`git blame` |\ngitwhy |\n|\n|---|---|---|---|\n| Shows what changed | ✅ | ✅ | ✅ |\n| Shows who changed it | ✅ | ✅ | ✅ |\nShows why it changed |\n❌ | ❌ | ✅ |\n| Captures AI model used | ❌ | ❌ | ✅ |\n| Links ticket/spec | ❌ | ❌ | ✅ |\n| Distinguishes human vs AI | ❌ | ❌ | ✅ |\nZero-friction (plain `git commit` ) |\n✅ | ✅ | ✅ |\n\n`git log`\n\nand `git blame`\n\ntell you the **what** and **who**. gitwhy adds the **why**, **what model**, and **what spec** — the context that matters six months later when you're debugging AI-generated code.\n\n| If you want to... | Run this |\n|---|---|\n| Set up the hook in a repo | `ghw init` |\n| Check hook health and last capture | `ghw status` |\n| See provenance for a commit | `ghw why HEAD` |\n| Browse annotated history | `ghw log --why` |\n| Export all records | `ghw audit export` |\n| Set default model | `ghw config set default_model claude-sonnet-4-6` |\n| Toggle LLM summary | `ghw config set summary.enabled false` |\n\nTweak behavior in `.gitwhy/config.yaml`\n\n:\n\n```\nbackend: git-notes\nauto_capture:\n  enabled: true\n  default_by: agent:opencode\nsummary:\n  enabled: true\n  command: llm\n  mode: filenames\n```\n\nEverything above happens automatically. But if you ever need to override what the hook captured — for an edge case, a CI commit, or a manual annotation — pass flags to `ghw commit`\n\n:\n\n| Flag | What it does |\n|---|---|\n`--by` |\nWho: `human` , `copilot` , `agent:<name>` |\n`--intent` |\nWhy: one-line description |\n`--origin` |\nSource: `human` , `spec` , `prompt` , `template` , `upstream` |\n`--ticket` |\nReference: e.g. `Ticket-42` |\n`--spec` |\nSpec driving the change |\n`--spec-hash` |\nSpec content hash |\n`--prompt` |\nPrompt text (if AI-generated) |\n`--model` |\nModel name (overrides env detection) |\n`-m / --message` |\nCommit message |\n\n```\n# Override auto-detected values for a specific commit\nghw commit --by human --intent \"harden auth middleware\" --ticket SEC-99\n```\n\n**Prerequisites:** [Git](https://git-scm.com/), the [GitHub CLI](https://cli.github.com/) (`gh`\n\n), and optionally [Go](https://go.dev/dl/) 1.21+ for building from source.\n\n```\nbrew install surajsrivastav/tap/ghw\n```\n\nDownload the latest release for your platform from the [releases page](https://github.com/surajsrivastav/gitwhy/releases):\n\n```\n# macOS (Apple Silicon)\ncurl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_darwin_arm64.tar.gz | tar xz\nsudo mv ghw /usr/local/bin/\n\n# macOS (Intel)\ncurl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_darwin_amd64.tar.gz | tar xz\nsudo mv ghw /usr/local/bin/\n\n# Linux (x86_64)\ncurl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_linux_amd64.tar.gz | tar xz\nsudo mv ghw /usr/local/bin/\n\n# Linux (ARM64)\ncurl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_linux_arm64.tar.gz | tar xz\nsudo mv ghw /usr/local/bin/\n\n# Windows (PowerShell)\ncurl -sLO https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_windows_amd64.zip\nExpand-Archive gitwhy_windows_amd64.zip -DestinationPath ~\\bin\ngo install github.com/surajsrivastav/gitwhy@latest\ngit clone https://github.com/surajsrivastav/gitwhy.git\ncd gitwhy\nmake build\nsudo mv ghw /usr/local/bin/\ncurl -sSfL https://raw.githubusercontent.com/surajsrivastav/gitwhy/master/install.sh | sh\ncmd/          - CLI commands\npkg/\n  provenance/ - What a record looks like\n  config/     - Reading/writing .gitwhy/config.yaml\n  storage/    - Where records live (git-notes or files)\n  drift/      - Tracking spec changes over time\n  audit/      - Reports and exports\n  passthrough/ - Handing unknown commands to `gh`\nmake test       # run all tests\nmake coverage   # coverage report\nmake vet        # check for issues\n```\n\nMIT — see [LICENSE](/surajsrivastav/gitwhy/blob/master/LICENSE).\n\nCheck the [PRD](/surajsrivastav/gitwhy/blob/master/PRD.md) for detailed specs, or open an issue on GitHub.", "url": "https://wpnews.pro/news/capture-why-ai-wrote-each-commit-stored-in-git-notes", "canonical_source": "https://github.com/surajsrivastav/gitwhy", "published_at": "2026-08-25 02:27:55+00:00", "updated_at": "2026-08-25 02:42:45.146241+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "ai-agents"], "entities": ["gitwhy", "Claude Code", "GitHub Copilot CLI"], "alternates": {"html": "https://wpnews.pro/news/capture-why-ai-wrote-each-commit-stored-in-git-notes", "markdown": "https://wpnews.pro/news/capture-why-ai-wrote-each-commit-stored-in-git-notes.md", "text": "https://wpnews.pro/news/capture-why-ai-wrote-each-commit-stored-in-git-notes.txt", "jsonld": "https://wpnews.pro/news/capture-why-ai-wrote-each-commit-stored-in-git-notes.jsonld"}}