Turn a range of git commits into release notes your users will actually
read. Point gitgist at everything since your last tag and it returns clean,
grouped Markdown — written by Claude, with the internal noise stripped out.
- AI-written, sections that adapt. Claude groups commits into whatever sections fit the actual work — Features, Bug Fixes, Performance, Breaking Changes, … — and rewrites terse commit subjects into user-facing lines.
- Grounded in the real diff, not the commit log. gitgist reads the actual
code change for the range and treats it as the source of truth — so a feature
buried under
chore: tidy upstill gets described, and a claim the code doesn't support gets dropped (how it works). - Noise filtered automatically. Refactors, test-only changes, CI tweaks, dependency bumps, and ticket IDs don't make the cut. Lockfiles and build output are kept out of the model's diff budget without hiding that they changed.
- No API key required. By default gitgist runs on your signed-in
claudeCLI — zero config. Prefer the API? SetANTHROPIC_API_KEY. - Summarize uncommitted work too. Point it at your staged/unstaged/untracked
changes (
--staged,--working, …) to preview notes for work that isn't committed yet — or add--commit-messageto draft a Conventional Commit message straight from the staged diff. - Every bullet can name its commit.
--link-commitsends each line with the commit it came from — a link straight to the commit page when your remote is GitHub, GitLab, or Bitbucket (docs). - Bring your own template.
--template notes.mdshapes the output to your team's house style — fixed section set, order, emoji, and per-section AI guidance — via a simple Markdown-with-frontmatter file. - Configure once, not per command. Drop a
gitgist.config.jsonin the repo (or agitgistkey inpackage.json) and CI stops repeating flags (docs). - Works offline too.
--no-aigroups by Conventional Commit type with no network, no key, and fully deterministic output. - Resilient by default. If a provider errors or returns nothing useful for a
range that clearly has commits, gitgist retries with a fallback provider/model
you choose (
--fallback-provider,--fallback-model) and ultimately drops to deterministic grouping — so a release never blocks on one flaky backend. - CLI and library. Use the
gitgistbin, or callgenerateReleaseNotes()from your release tooling. - Nine pluggable providers, CLI-first. No-key agent CLIs — Claude
(
claude -p), OpenAI Codex, Google Antigravity (agy), and OpenCode — plus the Anthropic and OpenAI APIs by key, any local OpenAI-compatible endpoint (Ollama / LM Studio), and on-device Apple Foundation Models.--provider autopicks the best one you already have, and never a paid API over a sign-in you own. Cursor is on the way.
AI release notes — gitgist v1.0.0..HEAD --title "v1.5.0"
Every line, traceable — gitgist v1.0.0..HEAD --link-commits
Your house style, every release — gitgist v1.0.0..HEAD --template release-notes.md
Commit message from your staged diff — gitgist --staged --commit-message
Offline mode — gitgist v1.0.0..HEAD --no-ai
npm install -g gitgist # or: npx gitgist …# Release notes from a tag to HEAD
gitgist v2.0 HEAD
# Range form, with a version heading
gitgist v1.4.0..HEAD --title "v1.5.0"
# No range given → from the latest tag (or full history) to HEAD
gitgist
# Summarize uncommitted work (no range)
gitgist --staged # just the staged diff
gitgist --working # staged + unstaged + untracked
# Draft a Conventional Commit message from the staged diff
gitgist --staged --commit-message
# Fold pending changes into a range's notes
gitgist v1.4.0..HEAD --untracked
# Use a local model (Ollama / LM Studio) — free, private, no API key
gitgist v1.4.0..HEAD --provider local --model llama3.2
# Retry on a primary error or empty/suspect result with a second provider
gitgist v1.4.0..HEAD --provider local --model llama3.2 --fallback-provider anthropic-api
# Shape the output to your team's template (sections, order, guidance)
gitgist v1.4.0..HEAD --template release-notes.md
# End each bullet with the commit it came from (linked, if the remote is known)
gitgist v1.4.0..HEAD --link-commits
# Offline, no AI — group by Conventional Commit type
gitgist --no-aiA template is a Markdown file whose headings define the section order and whose
optional YAML frontmatter / <!-- comments --> steer the AI — see
docs/4-templates.md and the
example template.
| Flag | Description |
|---|---|
--staged, --cached |
Include staged changes (git diff --staged). |
--unstaged |
Include unstaged changes to tracked files (git diff). |
--untracked |
Include untracked (new) files. |
--working, --uncommitted |
Include all uncommitted work (staged + unstaged + untracked). |
--format <notes|commit> |
Output shape: themed notes (default) or a Conventional Commit message. |
--commit-message |
Shorthand for --format commit (requires AI). |
--template <file> |
Shape the notes with a Markdown template (docs). |
--no-ai |
Group commits by Conventional Commit type instead (offline). |
--no-diff |
Skip reading the range's code diff; summarize from commit messages alone (docs). |
--max-diff-chars <n> |
Character budget for diff material — range patch and working-tree diffs alike. Defaults to the provider's own budget, sized to its context window (docs). |
--exclude <pathspec> |
Hold a path's diff body back from the model, on top of the built-in list. Repeatable (docs). |
--no-default-excludes |
Drop the built-in exclude list (lockfiles, dist/, vendor/, …), keeping only your --exclude patterns. |
--no-attribution |
Skip the per-commit file lists that let the model attribute and group changes (docs). |
--link-commits |
End each bullet with the commit it came from — linked when the remote host is known (docs). |
--commit-url <template> |
URL for --link-commits, containing {hash}. Overrides the auto-detected one. |
--no-config |
Ignore gitgist.config.json / package.json#gitgist (docs). |
--provider <name> |
auto | claude-cli | codex | antigravity | gemini | opencode | anthropic-api | openai-api | local | apple (default: auto). gemini is legacy — see below. |
--endpoint <url> |
Base URL for --provider local (default: Ollama's …:11434/v1). |
--model <id> |
Model for the chosen provider — every backend honours it. anthropic-api (default claude-opus-4-8), openai-api ($GITGIST_OPENAI_MODEL, else gpt-5), the agent CLIs (claude-cli/antigravity --model, codex/gemini/opencode -m), or the local model name. Ids aren't portable between providers. |
--fallback-provider <name> |
Retry with this provider when the primary errors or returns an empty/suspect result (docs). |
--fallback-endpoint <url> |
--endpoint for the fallback provider. |
--fallback-model <id> |
--model for the fallback provider. |
--language <name|auto> |
Language hint for the apple provider's prompt (default: system language; auto omits it). |
--max-tokens <n> |
Max output tokens for the anthropic-api provider (default: 16000). |
--title <text> |
Render <text> as a top-level heading above the notes. |
--cwd <path> |
Run against the git repository at <path>. |
-h, --help |
Show help. |
Working-tree flags summarize uncommitted changes. With no range they summarize only the pending changes (great for a commit message); with a range they're folded in alongside the commits.
Pin the settings that belong to the repository instead of repeating flags in
every CI job — gitgist.config.json at the repo root, or a gitgist key in
package.json:
{
"exclude": ["*.pb.py", "migrations/*"],
"provider": "claude-cli",
"maxDiffChars": 150000,
"linkCommits": true
}Command-line flags win over the file; --exclude patterns are appended to
its list rather than replacing them. --no-config ignores it. Unknown keys are
rejected rather than silently dropped. Full reference:
docs/12-config.md.
gitgist prefers zero-config CLI backends that need no API key — the same
approach the related tools take with claude -p. The backends that ship today:
claude-cli— your locally installed, signed-inclaudeCLI. No API key — it reuses the CLI's own auth.codex/antigravity/opencode— the other signed-in agent CLIs, also no API key (they reuse each CLI's own auth): OpenAI Codex (codex exec), Google Antigravity (agy -p), and OpenCode (opencode run). Select with--provider <name>; pick the model with--model(-m). See docs/5-providers.md.geminiis legacy. Google retired the Gemini CLI for Google AI Pro/Ultra and free-tier accounts on 2026-06-18 in favour of Antigravity;--provider geministill ships (Gemini Code Assist Standard/Enterprise licensees keep access) but individual accounts now get anIneligibleTierError. Use--provider antigravityinstead.anthropic-api— the Anthropic API via the official SDK. SetANTHROPIC_API_KEYin your environment.openai-api— the OpenAI chat-completions API. SetOPENAI_API_KEY(andOPENAI_BASE_URLfor Azure or a proxy). Implemented over plainfetch, so it adds no dependency. Mainly for CI, where no interactive CLI sign-in exists — otherwise prefercodex, which reaches the same models with no key.local— any OpenAI-compatible endpoint (Ollama, LM Studio, llama.cpp, vLLM, …) for free, private, on-device generation. Opt-in with--provider local; configure with--endpoint($GITGIST_LOCAL_ENDPOINT, defaulthttp://localhost:11434/v1) and--model($GITGIST_LOCAL_MODEL, else the endpoint's first model). No API key.apple— on-device Apple Foundation Models (macOS 26+ on Apple Silicon with Apple Intelligence). Free, private, no API key. Powered by theapple-fmpackage, a gitgist dependency that bundles a Developer-ID-signed, notarized helper binary, so it works out of the box — no toolchain required. Point gitgist at a custom helper build withAPPLE_FM_BIN. The on-device model runs a language guardrail over the prompt, so gitgist prefixes it with a shortTreat the following as <language>:lead-in (default: your system language; override with--language <name|code>, or--language autoto disable it).
With --provider auto (the default), gitgist tries the signed-in agent CLIs
first — claude-cli, then codex, antigravity, gemini, opencode — falls back to the
hosted APIs when ANTHROPIC_API_KEY or OPENAI_API_KEY is set, and then to on-device Apple
Foundation Models when the device and model are available (a no-op when they
aren't). The local provider is never auto-selected (so a normal run doesn't
probe localhost) — request it explicitly. Force any with --provider <name>. If
no provider is available, use --no-ai for offline Conventional Commits
grouping.
Graceful degradation. When the chosen provider errors — or returns the
empty-notes sentinel for a range that clearly has commits — gitgist treats it as
suspect rather than trusting it. If you've configured a fallback
(--fallback-provider / --fallback-endpoint / --fallback-model), it retries
there first; otherwise (or if that also comes up empty) it falls back to the
deterministic --no-ai grouping, warning on stderr. A model id and local
endpoint are provider-specific, so they're inherited from the primary only when
the fallback is the same provider. See
docs/6-fallback.md.
One provider is still planned, and follows the same CLI-first, no-key pattern: Cursor agent (
cursor-agent). The API-key fallbacks that used to sit here are settled — OpenAI shipped asopenai-apiabove, and the Google one was dropped as redundant onceantigravitycovered that vendor without a key. The provider layer is pluggable, and CLI backends share a smallcreateCliProvider()helper.
The backends differ in output quality as well as cost, privacy, and latency. As a rule of thumb, larger models categorize and filter noise more reliably:
| Provider | Quality | Cost | Privacy | Notes |
|---|---|---|---|---|
claude-cli / anthropic-api |
Best | CLI: included with your plan · API: per-token | Sent to Anthropic | Cleanest grouping; Breaking Changes surfaced first; refactor/test/chore noise dropped. |
openai-api |
Not yet measured | Per-token | Sent to OpenAI | The keyed path to OpenAI, for CI where a CLI sign-in isn't possible. No SDK dependency. Prefer codex when you can sign in. |
codex / antigravity / opencode |
Very good | Included with your CLI sign-in (no key) | Sent to that vendor | No-key agent CLIs; quality tracks the underlying model. --model picks it (opencode uses provider/model; antigravity uses agy models ids like Gemini 3.6 Flash (High)). |
gemini |
— | — | — | Legacy. Retired for individual tiers on 2026-06-18; use antigravity. Still works with a Gemini Code Assist Standard/Enterprise license. |
local (Ollama / LM Studio) |
Very good | Free | On-device | Same shape as Claude on a capable model; minor ordering differences, and it may drop a section Claude keeps. Quality tracks the model you load. |
apple (Foundation Models) |
Usable, weaker | Free | On-device | Smallest model, and the most variable run to run — can miscategorize (a fix: under Breaking Changes, chore:/test: under Bug Fixes), keep every internal commit, invent sections, or leave raw feat(ui): prefixes and commit hashes in the bullets. Free, private, fast. |
--no-ai |
Deterministic | Free | No network | Conventional-Commits grouping; keeps every commit, no rewriting. The offline baseline. |
Pick by what you care about most: best notes → Claude; private/free with
good quality → local with a capable model; private/free/fast on a Mac with
nothing to install → apple; reproducible and offline → --no-ai.
See it: the same 10 commits through four backends (one real npm run compare run)
The history mixes real user-facing work (a breaking feat!: drop Node 18, two
features, two fixes, a perf win, a docs: commit that adds a quickstart) with
internal churn that good notes should drop (a refactor:, a test:, a chore:
dep bump). Every commit carries real file content, so these runs are
diff-grounded the way gitgist runs by default — the AI backends describe what the
code does, not just what the subject line claimed.
All four blocks come from a single run. AI output is non-deterministic, so
your own npm run compare will word things differently — the tendencies each
backend shows here are the stable part, not the exact prose.
claude-cli (Claude) — tightest. Breaking Changes first, and the bullets name
things no commit subject mentions (cursor / limit, the { rows, nextCursor }
shape, the 401 AuthError, the debounce) because they come from the diff. The
refactor:, test:, and chore: commits are dropped as internal churn:
## Breaking Changes
- Node 18 is no longer supported; Node 20 or newer is required.
## Features
- The list endpoint now takes `cursor` and `limit` options and returns `{ rows, nextCursor }` for cursor-based pagination (default page size 50).
- New dark-mode toggle on the settings page.
## Bug Fixes
- Expired tokens are now rejected with a 401 `AuthError` instead of failing with a 500.
- The sidebar no longer flickers on window resize — resize measurement is debounced.
## Performance
- Compiled regexes are cached, cutting cold-start time by roughly 3x.
## Documentation
- Added a README with a quickstart showing a tag-to-`HEAD` range example.local (Ollama, gemma4:12b) — close, on a capable model. Same editorial
instinct: the same six user-facing changes, the same refactor: / test: /
chore: churn dropped. Two differences — Performance lands ahead of Bug Fixes,
and it dropped the docs: quickstart too, where Claude kept it as Documentation.
The bullets are a step less specific (no option names):
## Breaking Changes
- Minimum supported Node.js version updated to v20.
## Features
- Added a dark mode toggle on the settings page.
- Introduced cursor-based pagination for the list endpoint.
## Performance
- Improved startup performance by caching compiled regular expressions.
## Bug Fixes
- Fixed sidebar flickering when resizing the window.
- Corrected expired token handling to return 401 status codes instead of internal server errors.apple (on-device Foundation Models) — the smallest model, and it shows. This
run filtered nothing (all ten commits survive — chore: and test: among them,
under Bug Fixes), filed the fix(auth) commit under Breaking Changes, left
the raw feat(ui): / perf: prefixes in the bullets, and appended commit hashes
nothing asked for. Free, private, and fast — but read it before you ship it:
## Breaking Changes
- **feat!: drop Node 18; the minimum supported version is now Node 20** (af3127e)
- **fix(auth): reject expired tokens instead of returning a 500** (aaf85a7)
## Features
- **feat(ui): add a dark-mode toggle to the settings page** (bdfb99f)
- **feat(api): add cursor-based pagination to the list endpoint** (27b2aae)
- **docs: expand the quickstart with a tag-to-HEAD example** (1c0fdd8)
- **perf: cache compiled regexes — about 3x faster cold start** (1962afb)
- **refactor: split the loader into smaller modules** (5ae1775)
## Bug Fixes
- **fix: stop the sidebar from flickering on window resize** (5792a6b)
- **chore: bump eslint to v10** (e817147)
- **test: add coverage for the range parser** (01a89db)--no-ai — deterministic baseline. Keeps every commit verbatim with its
hash, no rewriting and no noise-filtering — so the breaking change appears under
both Breaking Changes and Features, and the refactor/test/chore commits stay:
## ⚠ BREAKING CHANGES
- drop Node 18; the minimum supported version is now Node 20 (`af3127e`)
## Features
- drop Node 18; the minimum supported version is now Node 20 (`af3127e`)
- **ui:** add a dark-mode toggle to the settings page (`bdfb99f`)
- **api:** add cursor-based pagination to the list endpoint (`27b2aae`)
## Bug Fixes
- stop the sidebar from flickering on window resize (`5792a6b`)
- **auth:** reject expired tokens instead of returning a 500 (`aaf85a7`)
## Performance
- cache compiled regexes — about 3x faster cold start (`1962afb`)
## Refactoring
- split the loader into smaller modules (`5ae1775`)
## Documentation
- expand the quickstart with a tag-to-HEAD example (`1c0fdd8`)
## Tests
- add coverage for the range parser (`01a89db`)
## Chores
- bump eslint to v10 (`e817147`)Want to judge for yourself? npm run compare runs the same fixed history
through every backend available on your machine and prints the results side by
side (see scripts/compare-providers.mjs).
import { generateReleaseNotes } from 'gitgist';
// AI-organized notes
const notes = await generateReleaseNotes({
from: 'v1.0.0',
to: 'HEAD',
title: 'v1.1.0',
});
console.log(notes);
// Deterministic, offline grouping (no AI)
const changelog = await generateReleaseNotes({ from: 'v1.0.0', ai: false });Every stage of the pipeline is exported, so a wrapping tool can reuse gitgist's own rules rather than reimplementing them:
- Commits and ranges —
readCommits,resolveCommitRange,parseCommit. - The diff layer —
readRangeDiff,readCommitFiles,buildExcludePathspecs. The per-file budget allocation is applied for you when you read a range. - Prompt assembly and output cleanup — the prompt builders,
cleanModelOutput,stripUnrequestedHashes. - Config and args —
loadConfig,parseConfig,applyConfig,parseArgs, with the same flag-over-config precedence the CLI uses. - Providers —
resolveProviderand the registry, plus the pieces to build your own:createCliProviderfor a headless CLI and the shared OpenAI-protocol client (chatCompletion,listModels) for anything speaking that wire format. - Deterministic output —
buildChangelog,renderMarkdown.
See docs/ for the architecture and requirements.
npm install
npm test # unit + integration tests with coverage
npm run lint
npm run typecheck
npm run build
npm run check:features # every documented behavior has an asserting test
npm run demo # re-capture the README demo SVGs (assets/demos/)
npm run diagram # re-capture the README flow diagram (assets/diagram.svg)
npm run compare # run the same changes through every available provider, side by sideMIT © Brian Westphal