feat(synopsis): add AI-generated commit narrative synopses#566
Open
feat(synopsis): add AI-generated commit narrative synopses#566
Conversation
5c38927 to
9998386
Compare
da07bd4 to
e183217
Compare
963c34e to
d56d1f9
Compare
jwiegley
added a commit
that referenced
this pull request
Apr 9, 2026
Address Devin review comments on PR #566: - Use current_git_ai_exe() instead of current_exe() to resolve through symlinks, preventing dispatch failure when running as the git shim. - Add #[cfg(windows)] block with CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP to prevent console window flash on Windows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jwiegley
added a commit
that referenced
this pull request
Apr 15, 2026
Address Devin review comments on PR #566: - Use current_git_ai_exe() instead of current_exe() to resolve through symlinks, preventing dispatch failure when running as the git shim. - Add #[cfg(windows)] block with CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP to prevent console window flash on Windows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
902e428 to
3b06645
Compare
jwiegley
added a commit
that referenced
this pull request
Apr 16, 2026
Address Devin review comments on PR #566: - Use current_git_ai_exe() instead of current_exe() to resolve through symlinks, preventing dispatch failure when running as the git shim. - Add #[cfg(windows)] block with CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP to prevent console window flash on Windows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3b06645 to
5435e91
Compare
Git commits capture *what* changed (the diff) and a brief *why* (the
commit message), but they lose the rich context of *how the developer
got there*. When AI-assisted tools like Claude Code are used, there is
often a conversational trail — hypotheses explored, approaches debated,
dead ends encountered, design tradeoffs weighed — that evaporates the
moment the commit is made.
This feature adds `git ai synopsis`, an opt-in command that generates a
narrative, blog-article-style document for any commit. Future readers of
the code get the full story: not just the diff, but the thinking behind
it.
Three input sources are collected and sent to the Anthropic Claude API:
1. **AI conversation context** — the Claude Code JSONL session file
for the repository is located automatically under
`~/.claude/projects/<project-hash>/`, parsed, and filtered to
exchanges within a configurable time window (default: 60 min).
Conversation loading is non-fatal; if it fails the synopsis is
generated from the diff and commit message alone.
2. **The diff** — `git show --stat` and `git show -U<N>` are run
against the target commit to produce a stat summary and a unified
diff with expanded context (default: 10 lines). Large diffs are
truncated to stay within the model's context window.
3. **The commit message** — retrieved via `git log -1 --format=%B`.
A structured prompt instructs Claude to write a technical blog post with
six sections: TL;DR, Background and Motivation, The Journey, The
Solution, Key Files Changed, and Reflections. Target length is
configurable (brief / standard / detailed).
The generated synopsis is stored as a git note under
`refs/notes/ai-synopsis`, using the same stdin-piped `git notes add`
pattern already used by the authorship tracking system. Notes can be
pushed and pulled alongside the repository.
```
git ai synopsis generate [--commit <sha>] [--model <m>]
[--api-key <key>] [--length brief|standard|detailed]
[--conversation <path>] [--no-conversation]
[--notes-ref <ref>] [--dry-run]
git ai synopsis show [<commit>] # default: HEAD
git ai synopsis list
```
- `ANTHROPIC_API_KEY` or `GIT_AI_SYNOPSIS_API_KEY` — API key
- `GIT_AI_SYNOPSIS_MODEL` — model override (default: claude-opus-4-6)
- `GIT_AI_SYNOPSIS=1` — enable auto-generation on every commit
```
src/synopsis/
types.rs — Synopsis, SynopsisMetadata, ConversationLog, DiffBundle, ...
config.rs — SynopsisConfig with env-var defaults
conversation.rs — Claude Code JSONL parser and time-window filter
collector.rs — diff, commit message, and conversation collection
generator.rs — Anthropic Messages API call and prompt construction
storage.rs — git notes read/write under refs/notes/ai-synopsis
commands.rs — generate, show, list subcommand handlers
```
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude Code stores conversation files at: ~/.claude/projects/-Users-foo-myrepo/<uuid>.jsonl The project hash is derived by replacing `/` with `-`, which produces a leading `-` for absolute Unix paths. The original implementation stripped this leading dash, so `find_claude_code_conversation` would look for `Users-foo-myrepo` instead of `-Users-foo-myrepo` and always come up empty. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…kend Three related improvements: **Automation via GIT_AI_SYNOPSIS=1** The post-commit hook now checks GIT_AI_SYNOPSIS. When set, it spawns `git-ai synopsis generate` as a detached background process immediately after the commit lands, so the terminal is not blocked. On Unix the child is moved into its own process group to avoid receiving signals meant for the parent session. **ANTHROPIC_BASE_URL support** The SynopsisConfig now reads the standard ANTHROPIC_BASE_URL env var (the same variable used by the Anthropic SDK and most proxies) as the API base URL override. Previously the only way to change the base URL was to edit source code. **`claude` CLI backend (--via-claude)** A new GenerationBackend::ClaudeCli variant pipes the prompt directly to `claude --print` instead of calling the Anthropic API. This uses Claude Code's existing authentication — no separate API key is needed. Select it with --via-claude on the command line, or set: GIT_AI_SYNOPSIS_BACKEND=claude Usage examples: # Use the claude CLI (no API key required) git ai synopsis generate --via-claude # Use a corporate API gateway ANTHROPIC_BASE_URL=https://my-proxy/anthropic git ai synopsis generate # Auto-generate for every commit (background) GIT_AI_SYNOPSIS=1 GIT_AI_SYNOPSIS_BACKEND=claude git commit -m "..." Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix six clippy lints that were failing CI across all platforms:
- collapsible_str_replace: use replace(['\\', '/'], "-") array pattern
- collapsible_if: merge nested if-let blocks with &&
- unnecessary_map_or: use is_none_or() instead of map_or(true, ...)
- manual_range_contains: use !(200..300).contains(&status)
- single_char_add_str: use push('\n') instead of push_str("\n")
- manual_div_ceil: use .div_ceil(4) instead of (len + 3) / 4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Collapse nested if in fetch_hooks.rs (clippy::collapsible_if) - Fix formatting in fetch_hooks.rs and remove leading blank line in main.rs - Use floor_char_boundary for safe UTF-8 truncation in conversation.rs - Remove GIT_AI env var from spawned synopsis background process Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve symlinks and relative path components before reading the Claude Code conversation JSONL file. Without canonicalization, paths containing symlinks or relative segments could fail to resolve correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address Devin review comments on PR #566: - Use current_git_ai_exe() instead of current_exe() to resolve through symlinks, preventing dispatch failure when running as the git shim. - Add #[cfg(windows)] block with CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP to prevent console window flash on Windows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5435e91 to
c707f81
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.

Summary
git ai synopsiscommand that generates blog-article-style narrative descriptions of commits, capturing the full story behind a change — not just the diff, but the thinking and exploration that led to it~/.claude/projects/), the commit diff, and the commit message, then sends them to the Anthropic Claude APIrefs/notes/ai-synopsis, which can be pushed/pulled alongside the repositoryMotivation
Traditional commits record what changed. When working with AI assistants, there's rich context — dead ends explored, tradeoffs weighed, approaches debated — that gets lost at commit time. This feature captures that context as a readable narrative for future readers of the code.
New Commands
Implementation Details
Module layout (
src/synopsis/):types.rsSynopsis,SynopsisMetadata,ConversationLog,DiffBundle,SynopsisInputconfig.rsSynopsisConfigwith defaults from env varsconversation.rscollector.rsgenerator.rsminreqstorage.rsrefs/notes/ai-synopsiscommands.rsgenerate,show,listCLI subcommandsConversation auto-detection: Claude Code stores sessions as JSONL files under
~/.claude/projects/<project-hash>/. The hash is derived by replacing/path separators with-and stripping the leading-. The most recently modified.jsonlfile in that directory is used and filtered to exchanges within the configurable time window (default: 60 min before the last exchange).API call: Uses
minreq(already a project dependency) to POST tohttps://api.anthropic.com/v1/messages. RequiresANTHROPIC_API_KEY(or--api-key). Model defaults toclaude-opus-4-6, overridable viaGIT_AI_SYNOPSIS_MODELor--model.Storage: Synopsis JSON (metadata + markdown content) is stored as a git note via
git notes --ref=ai-synopsis add -f -F -, using the same stdin-piped pattern as the existing authorship tracking system.Test Plan
cargo test --lib synopsis)cargo build)git ai synopsis generateon a real repo with Claude Code sessiongit ai synopsis show HEADreads back the stored notegit ai synopsis generate --dry-runshows prompt without API callgit ai synopsis generate --no-conversationworks without a JSONL file present🤖 Generated with Claude Code