Mark where you've been. Cairn reads your diff, understands the intent behind the changes, and writes your commit messages, PR descriptions, changelogs, and standup summaries — so the trail is always readable.
No dependencies. No MCP server. No build step.
Git history is a graveyard of fix, wip, and misc.
Not because the work was unclear — because writing a good commit message after finishing a feature takes context you've already moved past. Cairn reads what you actually changed and writes the message for you: semantic, conventional, and ready to paste.
| Command | What it does |
|---|---|
/cairn-commit |
Generate a Conventional Commits message from staged diff |
/cairn-pr |
Generate a PR title and description from branch diff |
/cairn-changelog |
Generate a CHANGELOG entry from a commit range |
/cairn-summary |
Plain-language standup, Slack message, or formal summary |
Recommended — global, available in every project:
curl -fsSL https://raw.githubusercontent.com/ValentinFigue/cairn/main/install.sh | bash -s globalThis installs all four cairn commands, the cairn CLI to ~/.local/bin/, the enforce-cairn (PreToolUse) and post-cairn (PostToolUse) hooks, and configures the required Bash, Read, and Write permissions in ~/.claude/settings.json so git commands run without permission prompts.
With documentation rules — also injects project behaviour rules into ~/.claude/CLAUDE.md:
curl -fsSL https://raw.githubusercontent.com/ValentinFigue/cairn/main/install.sh | bash -s global --claude-mdLocal only — available in this project only:
curl -fsSL https://raw.githubusercontent.com/ValentinFigue/cairn/main/install.sh | bashManual one-liner (no script):
# Global
mkdir -p ~/.claude/commands
for cmd in cairn-commit cairn-pr cairn-changelog cairn-summary; do
curl -fsSL -o ~/.claude/commands/${cmd}.md \
https://raw.githubusercontent.com/ValentinFigue/cairn/main/.claude/commands/${cmd}.md
done
# Local
mkdir -p .claude/commands
for cmd in cairn-commit cairn-pr cairn-changelog cairn-summary; do
curl -fsSL -o .claude/commands/${cmd}.md \
https://raw.githubusercontent.com/ValentinFigue/cairn/main/.claude/commands/${cmd}.md
doneIf installing manually, add "Bash", "Read", and "Write" to permissions.allow in the relevant settings.json to avoid prompts when cairn reads diffs.
Restart Claude Code. The cairn commands are immediately available.
To uninstall:
# Global
curl -fsSL https://raw.githubusercontent.com/ValentinFigue/cairn/main/uninstall.sh | bash -s global --claude-md
# Local
curl -fsSL https://raw.githubusercontent.com/ValentinFigue/cairn/main/uninstall.sh | bashHook bypass:
The hooks are non-blocking nudges. To silence a specific nudge, append a bypass marker to the git command:
git commit -m "wip" # cairn:skip — silence cairn only
git push origin main # suite:skip — silence all suite hooks# cairn:skip silences cairn's nudge for that command. # suite:skip silences all installed suite hooks (cairn, temper, whetstone, bonsai) simultaneously. Bash treats these as comments, so the command runs unchanged.
Stage your changes, then run /cairn-commit in Claude Code:
git add src/auth/token.py
/cairn-commit
On your feature branch, run /cairn-pr:
/cairn-pr
/cairn-pr --base=develop
/cairn-changelog
/cairn-changelog --from=v0.1.0 --version=0.2.0
/cairn-summary
/cairn-summary --format=slack
/cairn-summary --from=v0.1.0 --format=paragraph
| Command | What it does |
|---|---|
/cairn-commit |
Conventional Commits message from staged diff |
/cairn-commit --style=plain |
Plain imperative-mood message |
/cairn-commit --off |
Skip this run |
/cairn-pr |
PR title + description (auto-detects base branch) |
/cairn-pr --base=develop |
Diff against develop instead of main |
/cairn-pr --style=plain |
Plain PR title |
/cairn-changelog |
CHANGELOG entry from last tag to HEAD |
/cairn-changelog --from=v0.1.0 --version=0.2.0 |
Specify range and version |
/cairn-changelog --style=plain |
Flat bullet list, no type grouping |
/cairn-summary |
Standup summary of yesterday's commits |
/cairn-summary --format=slack |
Slack-ready paragraph |
/cairn-summary --format=paragraph |
Formal prose summary |
/cairn-summary --from=v0.1.0 |
Summary from a specific tag |
feat(auth): add token expiry validation on login
Tokens were accepted past their expiry window when the clock skew
tolerance was set above 30s. Adds a hard ceiling regardless of tolerance.
Closes #412
If your staged diff spans multiple unrelated areas, cairn flags it and suggests separate commits:
Commit 1 — auth:
git commit -m "feat(auth): add token expiry validation on login"
Commit 2 — docs:
git commit -m "docs: update token lifecycle diagram"
Consider splitting this into separate commits.
PR Title:
feat(auth): add token expiry validation and clock-skew ceiling
Description:
## Summary
- Add hard ceiling on token expiry regardless of clock-skew tolerance
- Fix accepted-past-expiry bug when tolerance exceeded 30s
- Update token lifecycle diagram in docs
## Changes
- `src/auth/token.py` — validates expiry window with ceiling
- `docs/token-lifecycle.md` — updated diagram
## Test plan
- [ ] Run `pytest tests/auth/` — all tests pass
- [ ] Manual test: set tolerance > 30s, verify expired token is rejected
- [ ] Review token lifecycle diagram renders correctly
## [0.2.0] — 2026-05-06
### Added
- Token expiry validation with configurable ceiling in the auth flow
- Lifecycle diagram updated to reflect new expiry model
### Fixed
- Tokens no longer accepted past their expiry window when clock-skew tolerance exceeds 30sYesterday:
- Added hard token expiry ceiling to the auth flow — expired tokens now rejected regardless of tolerance
- Fixed the clock-skew edge case that was causing intermittent auth failures in staging
- Updated the token lifecycle diagram and related docs
Today:
- (fill in your plans)
Cairn also warns before generating if it detects common secret patterns in your diff — API keys, tokens, or PEM blocks staged by accident.
Cairn resolves settings in three layers, lowest to highest priority:
1. Global config (~/.claude/cairn.config) — your personal defaults across all projects
2. Local config (./cairn.config) — project-level overrides
3. Per-run flags ($ARGUMENTS) — always win, override both config files
| Key | Default | Description |
|---|---|---|
enabled |
true |
Enable/disable /cairn-commit at runtime |
style |
conventional |
Default style for /cairn-commit |
pr.base |
auto | Default base branch for /cairn-pr |
pr.style |
conventional |
Default PR title style |
pr.template_file |
— | Path to PR description template (e.g. .github/pull_request_template.md) |
pr.rules_file |
— | Path to prose generation rules (e.g. .cairn/pr-rules.md) |
changelog.style |
conventional |
Default changelog grouping style |
changelog.extra_types |
— | Comma-separated extra conventional types (e.g. hotfix,release) |
changelog.exclude_paths |
— | Comma-separated path prefixes to exclude |
summary.format |
standup |
Default output format for /cairn-summary |
summary.window |
1 day ago |
Default time window for /cairn-summary |
enabled: true
style: conventional
pr.base: develop
pr.rules_file: .cairn/pr-rules.md
summary.format: slack
The pr.rules_file is freeform prose that shapes how /cairn-pr generates descriptions. Example .cairn/pr-rules.md:
- Emphasize WHY changes were made, not just what changed
- Keep the summary to 3 bullets maximum
- If the branch name contains a ticket number (e.g. PROJ-123), include it in the PR title
- Omit the "Changes" file list if fewer than 3 files changed
- Use technical language appropriate for code reviewA global install also provides a cairn command for managing your setup:
cairn status # install state + config summary
cairn config show # full effective config with sources
cairn disable local # silence /cairn-commit for this project
cairn disable global # silence everywhere
cairn enable local # restore
cairn config set --style=plain # plain style for this project
cairn config set --style=conventional --global # conventional everywhere
cairn config set --pr-base=develop # default PR base branch
cairn config set --pr-rules=.cairn/pr-rules.md # set rules file
cairn config set "--summary-window=1 week ago" # widen summary window
cairn config reset local # wipe project overrides
cairn update # pull latest command files
cairn uninstall global --claude-md # full removalRun cairn help for the full reference.
-
/cairn-commit— generate a Conventional Commits message from staged diff -
/cairn-pr— generate a full PR title and description from the branch diff vs base -
/cairn-changelog— generate a CHANGELOG entry from a commit range -
/cairn-summary— plain-language standup summary of what changed and why -
cairn.configsupport for extra conventional types, exclude paths, and per-command settings -
cairn disable/enablerespected by the command file at runtime - MCP server upgrade for richer git integration
- Workflow guide: run
/temperto review the diff →/cairn-committo narrate it (temper→cairn handoff)
Cairn is part of a four-tool suite. Each tool covers a different moment in the development loop:
| Tool | When | What it does |
|---|---|---|
| whetstone | Before you build | Critiques plans — sharpens the approach before any code is written |
| bonsai | While you build | AST-safe rename, move, and dead-code detection for Python and TypeScript |
| temper | After you build | Critiques diffs before commit — catches issues while context is live |
| cairn | When you ship | Narrates commits, PRs, and changelogs from the actual diff |
The highest-value handoff in the suite: after temper finds no blockers, run /cairn-commit immediately — the review is fresh and the staged diff is ready.
MIT — see LICENSE.