surajsrivastav/gitwhy

Intent-aware Git CLI — wraps gh with provenance tracking

2

stars

2

commits

Go

primary language

Jul 14, 2026

updated

README

Beta — gitwhy is under active development. Feedback welcome via GitHub Issues.

gitwhy (ghw)

Go Version License Test Build Release Stars

Every git commit already knows what changed. gitwhy remembers why.

It 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, $COPILOT_MODEL, $AI_AGENT), 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.

Two steps. Zero flags. Plain git commit.

Quick start

ghw init
git commit -m "feat: add login handler"   # standard git, nothing special

# Later — what actually happened here?
ghw why HEAD
  gitwhy provenance record
  ─────────────────────────
  schema:    gitwhy/v1
  target:    commit a3f1d8c
  by:        agent:claude-code
  when:      2026-06-25T10:00:00Z

  intent:    add login handler
  origin:    spec

  context:
    ticket:   PROJ-42
    prompt:   unknown
    model:    claude-sonnet-4-6
    branch:   feature/PROJ-42-login

That's the entire default workflow. ghw init once, then never think about it again.

How the hook works (the Agent Interceptor)

ghw init installs a single script into .git/hooks/post-commit. Every time you (or an AI agent) run git commit, that hook fires automatically and silently captures:

What it sniffsHow
Who / what agentSniffs $AI_AGENT, $COPILOT_AGENT_MODEL — detects Claude Code, Copilot, Cursor, etc.
Which modelReads $CLAUDE_CODE_MODEL, $COPILOT_MODEL, $ANTHROPIC_MODEL, $OPENAI_MODEL
Why (intent)Pulls the subject line from your commit message
Ticket numberScans git branch for PROJ-123 patterns
Origin (human vs spec)Infers from conventional commit type (featspec, chorehuman)
Prompt (if AI)Captures $COPILOT_AGENT_PROMPT or $CLAUDE_CODE_PROMPT

All 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.

What gets captured automatically

Just write a normal commit message. gitwhy parses it without any flags.

git commit -m "feat: add login handler"
intent:    add login handler      ← from commit message description
origin:    spec                   ← inferred from "feat" type
ticket:    PROJ-42                ← parsed from branch feature/PROJ-42-login
model:     claude-sonnet-4-6      ← detected from $COPILOT_MODEL / $CLAUDE_CODE_MODEL
by:        agent:claude-code      ← detected from $AI_AGENT env var
branch:    feature/PROJ-42-login  ← from git

Commit message → intent

gitwhy reads conventional commit format and maps it to provenance fields automatically:

Commit messageintentorigin
feat: add login handleradd login handlerspec
fix: null pointer in authnull pointer in authspec
perf: cache token lookupcache token lookupspec
chore: update depsupdate depshuman
docs: add API examplesadd API exampleshuman
test: cover edge casescover edge caseshuman
feat!: breaking auth changeBREAKING: breaking auth changespec

Non-conventional messages fall back to LLM summarization (if configured) then "unknown".

Branch name → ticket

gitwhy scans the branch name for a PROJECT-123 pattern and sets it as the ticket automatically. No flags needed.

feature/PROJ-42-login   →  ticket: PROJ-42
fix/AUTH-7-token-null   →  ticket: AUTH-7
main                    →  ticket: unknown

Agent and model → attribution

gitwhy reads environment variables set by AI tools at commit time:

ToolEnv var readCaptured as
Claude CodeAI_AGENT=claude-code/...by: agent:claude-code
Claude CodeCLAUDE_CODE_MODELmodel: claude-sonnet-4-6
GitHub Copilot CLICOPILOT_AGENT_MODEL or COPILOT_MODELby: copilot, model: gpt-4o
GitHub Copilot CLICOPILOT_AGENT_PROMPTprompt: ...
Any toolANTHROPIC_MODEL, OPENAI_MODEL, GITHUB_MODEL, AI_MODELmodel: ...

If no env var is found, model falls back to default_model in .gitwhy/config.yaml, then "unknown".

Set a default model once, and every commit picks it up:

ghw config set default_model claude-sonnet-4-6

Why not just use git log or git blame?

git loggit blamegitwhy
Shows what changed
Shows who changed it
Shows why it changed
Captures AI model used
Links ticket/spec
Distinguishes human vs AI
Zero-friction (plain git commit)

git log and git blame tell 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.

Commands

If you want to...Run this
Set up the hook in a repoghw init
Check hook health and last captureghw status
See provenance for a commitghw why HEAD
Browse annotated historyghw log --why
Export all recordsghw audit export
Set default modelghw config set default_model claude-sonnet-4-6
Toggle LLM summaryghw config set summary.enabled false

Configuration

Tweak behavior in .gitwhy/config.yaml:

backend: git-notes
auto_capture:
  enabled: true
  default_by: agent:opencode
summary:
  enabled: true
  command: llm
  mode: filenames

Advanced: Manual flags / overrides

Everything 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:

FlagWhat it does
--byWho: human, copilot, agent:<name>
--intentWhy: one-line description
--originSource: human, spec, prompt, template, upstream
--ticketReference: e.g. Ticket-42
--specSpec driving the change
--spec-hashSpec content hash
--promptPrompt text (if AI-generated)
--modelModel name (overrides env detection)
-m / --messageCommit message
# Override auto-detected values for a specific commit
ghw commit --by human --intent "harden auth middleware" --ticket SEC-99

Install

Prerequisites: Git, the GitHub CLI (gh), and optionally Go 1.21+ for building from source.

macOS (Homebrew)

brew install surajsrivastav/tap/ghw

Pre-built binary (any OS)

Download the latest release for your platform from the releases page:

# macOS (Apple Silicon)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_darwin_arm64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# macOS (Intel)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_darwin_amd64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# Linux (x86_64)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_linux_amd64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# Linux (ARM64)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_linux_arm64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# Windows (PowerShell)
curl -sLO https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_windows_amd64.zip
Expand-Archive gitwhy_windows_amd64.zip -DestinationPath ~\bin

Via Go (if you have Go installed)

go install github.com/surajsrivastav/gitwhy@latest

Or build from source

git clone https://github.com/surajsrivastav/gitwhy.git
cd gitwhy
make build
sudo mv ghw /usr/local/bin/

Install script

curl -sSfL https://raw.githubusercontent.com/surajsrivastav/gitwhy/master/install.sh | sh

Project structure

cmd/          - CLI commands
pkg/
  provenance/ - What a record looks like
  config/     - Reading/writing .gitwhy/config.yaml
  storage/    - Where records live (git-notes or files)
  drift/      - Tracking spec changes over time
  audit/      - Reports and exports
  passthrough/ - Handing unknown commands to `gh`

Testing

make test       # run all tests
make coverage   # coverage report
make vet        # check for issues

License

MIT — see LICENSE.

Questions?

Check the PRD for detailed specs, or open an issue on GitHub.

Contributors

claude

1 commits

surajsrivastav/gitwhy

Intent-aware Git CLI — wraps gh with provenance tracking

2

stars

2

commits

Go

primary language

Jul 14, 2026

updated

README

Beta — gitwhy is under active development. Feedback welcome via GitHub Issues.

gitwhy (ghw)

Go Version License Test Build Release Stars

Every git commit already knows what changed. gitwhy remembers why.

It 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, $COPILOT_MODEL, $AI_AGENT), 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.

Two steps. Zero flags. Plain git commit.

Quick start

ghw init
git commit -m "feat: add login handler"   # standard git, nothing special

# Later — what actually happened here?
ghw why HEAD
  gitwhy provenance record
  ─────────────────────────
  schema:    gitwhy/v1
  target:    commit a3f1d8c
  by:        agent:claude-code
  when:      2026-06-25T10:00:00Z

  intent:    add login handler
  origin:    spec

  context:
    ticket:   PROJ-42
    prompt:   unknown
    model:    claude-sonnet-4-6
    branch:   feature/PROJ-42-login

That's the entire default workflow. ghw init once, then never think about it again.

How the hook works (the Agent Interceptor)

ghw init installs a single script into .git/hooks/post-commit. Every time you (or an AI agent) run git commit, that hook fires automatically and silently captures:

What it sniffsHow
Who / what agentSniffs $AI_AGENT, $COPILOT_AGENT_MODEL — detects Claude Code, Copilot, Cursor, etc.
Which modelReads $CLAUDE_CODE_MODEL, $COPILOT_MODEL, $ANTHROPIC_MODEL, $OPENAI_MODEL
Why (intent)Pulls the subject line from your commit message
Ticket numberScans git branch for PROJ-123 patterns
Origin (human vs spec)Infers from conventional commit type (featspec, chorehuman)
Prompt (if AI)Captures $COPILOT_AGENT_PROMPT or $CLAUDE_CODE_PROMPT

All 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.

What gets captured automatically

Just write a normal commit message. gitwhy parses it without any flags.

git commit -m "feat: add login handler"
intent:    add login handler      ← from commit message description
origin:    spec                   ← inferred from "feat" type
ticket:    PROJ-42                ← parsed from branch feature/PROJ-42-login
model:     claude-sonnet-4-6      ← detected from $COPILOT_MODEL / $CLAUDE_CODE_MODEL
by:        agent:claude-code      ← detected from $AI_AGENT env var
branch:    feature/PROJ-42-login  ← from git

Commit message → intent

gitwhy reads conventional commit format and maps it to provenance fields automatically:

Commit messageintentorigin
feat: add login handleradd login handlerspec
fix: null pointer in authnull pointer in authspec
perf: cache token lookupcache token lookupspec
chore: update depsupdate depshuman
docs: add API examplesadd API exampleshuman
test: cover edge casescover edge caseshuman
feat!: breaking auth changeBREAKING: breaking auth changespec

Non-conventional messages fall back to LLM summarization (if configured) then "unknown".

Branch name → ticket

gitwhy scans the branch name for a PROJECT-123 pattern and sets it as the ticket automatically. No flags needed.

feature/PROJ-42-login   →  ticket: PROJ-42
fix/AUTH-7-token-null   →  ticket: AUTH-7
main                    →  ticket: unknown

Agent and model → attribution

gitwhy reads environment variables set by AI tools at commit time:

ToolEnv var readCaptured as
Claude CodeAI_AGENT=claude-code/...by: agent:claude-code
Claude CodeCLAUDE_CODE_MODELmodel: claude-sonnet-4-6
GitHub Copilot CLICOPILOT_AGENT_MODEL or COPILOT_MODELby: copilot, model: gpt-4o
GitHub Copilot CLICOPILOT_AGENT_PROMPTprompt: ...
Any toolANTHROPIC_MODEL, OPENAI_MODEL, GITHUB_MODEL, AI_MODELmodel: ...

If no env var is found, model falls back to default_model in .gitwhy/config.yaml, then "unknown".

Set a default model once, and every commit picks it up:

ghw config set default_model claude-sonnet-4-6

Why not just use git log or git blame?

git loggit blamegitwhy
Shows what changed
Shows who changed it
Shows why it changed
Captures AI model used
Links ticket/spec
Distinguishes human vs AI
Zero-friction (plain git commit)

git log and git blame tell 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.

Commands

If you want to...Run this
Set up the hook in a repoghw init
Check hook health and last captureghw status
See provenance for a commitghw why HEAD
Browse annotated historyghw log --why
Export all recordsghw audit export
Set default modelghw config set default_model claude-sonnet-4-6
Toggle LLM summaryghw config set summary.enabled false

Configuration

Tweak behavior in .gitwhy/config.yaml:

backend: git-notes
auto_capture:
  enabled: true
  default_by: agent:opencode
summary:
  enabled: true
  command: llm
  mode: filenames

Advanced: Manual flags / overrides

Everything 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:

FlagWhat it does
--byWho: human, copilot, agent:<name>
--intentWhy: one-line description
--originSource: human, spec, prompt, template, upstream
--ticketReference: e.g. Ticket-42
--specSpec driving the change
--spec-hashSpec content hash
--promptPrompt text (if AI-generated)
--modelModel name (overrides env detection)
-m / --messageCommit message
# Override auto-detected values for a specific commit
ghw commit --by human --intent "harden auth middleware" --ticket SEC-99

Install

Prerequisites: Git, the GitHub CLI (gh), and optionally Go 1.21+ for building from source.

macOS (Homebrew)

brew install surajsrivastav/tap/ghw

Pre-built binary (any OS)

Download the latest release for your platform from the releases page:

# macOS (Apple Silicon)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_darwin_arm64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# macOS (Intel)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_darwin_amd64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# Linux (x86_64)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_linux_amd64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# Linux (ARM64)
curl -sL https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_linux_arm64.tar.gz | tar xz
sudo mv ghw /usr/local/bin/

# Windows (PowerShell)
curl -sLO https://github.com/surajsrivastav/gitwhy/releases/latest/download/gitwhy_windows_amd64.zip
Expand-Archive gitwhy_windows_amd64.zip -DestinationPath ~\bin

Via Go (if you have Go installed)

go install github.com/surajsrivastav/gitwhy@latest

Or build from source

git clone https://github.com/surajsrivastav/gitwhy.git
cd gitwhy
make build
sudo mv ghw /usr/local/bin/

Install script

curl -sSfL https://raw.githubusercontent.com/surajsrivastav/gitwhy/master/install.sh | sh

Project structure

cmd/          - CLI commands
pkg/
  provenance/ - What a record looks like
  config/     - Reading/writing .gitwhy/config.yaml
  storage/    - Where records live (git-notes or files)
  drift/      - Tracking spec changes over time
  audit/      - Reports and exports
  passthrough/ - Handing unknown commands to `gh`

Testing

make test       # run all tests
make coverage   # coverage report
make vet        # check for issues

License

MIT — see LICENSE.

Questions?

Check the PRD for detailed specs, or open an issue on GitHub.

Contributors

claude

1 commits

Languages

Go

95.8%

HTML

3.3%