Skip to content

hexapode/git-why

Repository files navigation

git-why

Capture the "why" behind every code change.

git-why is a CLI tool that automatically captures AI reasoning traces — the prompts, thinking, and decisions behind code changes — and stores them alongside your source code. It bridges the gap between git blame (which shows who and when) and the context that explains why.

$ git why show src/auth.py

## 2026-04-11 — Add OAuth2 with refresh tokens
### Prompt
> Replace session-based auth with OAuth2, support refresh tokens
### Reasoning
Chose the authorization code flow over implicit grant because we need
refresh tokens for long-lived sessions. Used PKCE to avoid storing
client secrets in the SPA.
### Changes
- Edit src/auth.py — replaced session middleware with OAuth2 handler
- Edit src/config.py — added OAUTH_* environment variables

Features

  • Automatic capture — Pre-commit hook silently records reasoning at commit time
  • Tool-agnostic — Plugin system supports Claude Code, Cursor, Copilot, Aider, and more
  • Per-file traces — Each source file gets its own .why/<path>.md with full history
  • Session manifests — Optionally store complete AI conversation transcripts
  • Git-native — Clean diffs, union merge strategy, .whyignore patterns
  • Zero dependencies — Just bash (>=3.2), jq, and git
  • Cross-platform — macOS, Linux, Windows (Git Bash / WSL)

Install

Homebrew (macOS)

brew install hexapode/git-why/git-why

Curl

curl -fsSL https://raw.githubusercontent.com/hexapode/git-why/main/install.sh | bash

Make

git clone https://github.com/hexapode/git-why
cd git-why
make install

Quick start

# Initialize in your repo
git why init

# That's it — traces are captured automatically on each commit.
# To capture manually:
git why capture

# View traces
git why show src/auth.py

# Search across all traces
git why search "OAuth"

git why init creates a .why/ directory and installs a pre-commit hook. From then on, every commit automatically extracts reasoning from your active AI session and records it.

Commands

Command Description
git why init Set up .why/ directory and install hooks
git why capture Extract traces from the active AI session
git why capture -m "reason" Record a manual (human-written) trace
git why show <file> Display reasoning history for a file
git why blame <file> Git blame annotated with reasoning
git why log Git log with trace indicators
git why search <query> Full-text search across all traces
git why status Show configuration and state

How it works

AI Session (Claude Code, Cursor, …)
        ↓
   provider detects active session
        ↓
   extracts & normalizes conversation
        ↓
   filters reasoning per changed file
        ↓
   writes .why/<path>.md (newest first)
        ↓
   stages .why/ alongside your commit

Traces are plain Markdown files stored in .why/, mirroring your source tree. They're designed to produce clean git diffs (entries are always prepended, never edited) and merge cleanly across branches (union merge strategy).

Trace format

Each .why/<path>.md file contains entries like:

<!-- why:entry session="abc-123" date="2026-04-11T19:07:51Z" tool="claude-code" spec="1" -->
## 2026-04-11 — Add retry logic to API client

### Prompt
> Make the API client retry on 5xx errors with exponential backoff

### Reasoning
Added retry with exponential backoff (base 1s, max 30s, 3 attempts).
Chose jitter to avoid thundering herd. Did not retry on 4xx since those
indicate client errors that won't resolve on retry.

### Changes
- Edit src/api.py — added retry wrapper with backoff
<!-- /why:entry -->

See RFC-GIT-WHY.md for the full specification.

Configuration

.why/.whyignore

Exclude files from trace capture using gitignore-style patterns:

generated/
*.min.js
node_modules/

Skip capture for a single commit

GIT_WHY_SKIP=1 git commit -m "quick fix"

Hook managers

git why init auto-detects and configures your hook manager:

Providers — use any AI tool

git-why uses a provider plugin system so every developer on the team can use their preferred AI coding assistant. The pre-commit hook auto-detects which tool is active and captures reasoning transparently.

Provider Status Session source
Claude Code Included ~/.claude/projects/ JSONL files
GitHub Copilot Included VS Code workspaceStorage/ JSON/JSONL files
OpenAI Codex Included ~/.codex/sessions/ JSONL files
Cursor Coming soon
Aider Coming soon
Windsurf Coming soon
Manual Built-in git why capture -m "reason"

Mixed teams, unified traces

Different developers can use different tools — traces all land in the same .why/ directory in the same format:

$ git why log

  a3f1c9d ✦ Add retry to auth refresh      (tool: claude-code, 2 .why files)
  b7e2d4a ✦ Refactor API pagination         (tool: cursor, 1 .why file)
  c91f0e3 ✦ Fix race condition in cache     (tool: human, 1 .why file)
  d4a8b2c   Bump dependencies               (no trace)

Every entry records which tool produced it via the tool attribute. Readers don't need to know or care — git why show, blame, and search work the same regardless of the source.

Adding a new provider

Copy bin/providers/_template.sh and implement 5 functions:

  1. provider_detect — find the tool's active session
  2. provider_session_id — return a stable identifier
  3. provider_metadata — extract tool version info
  4. provider_extract_conversation — parse the full conversation
  5. provider_extract_for_file — filter reasoning for a specific file

See CONTRIBUTING-PROVIDER.md for the full guide with examples.

Requirements

  • bash >= 3.2
  • jq
  • git

License

MIT

About

Know why code is here

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors