claude-git-hooks
v3.11.3
Published
Git hooks with Claude CLI for code analysis and automatic commit messages
Maintainers
Readme
Claude Git Hooks
AI-powered git hooks for automatic commit messages, code analysis, and PR creation.
Requirements
- Node.js >=16.9.0
- Git
- Claude CLI (authenticated)
Quick Start
npm install -g claude-git-hooks
cd your-project
claude-hooks installInstall verifies Node.js, npm, Git and the Claude CLI. --headless makes the install
non-interactive (CI/containers) but still runs those checks — it changes interaction,
never scope. To skip them deliberately:
claude-hooks install --headless # non-interactive, checks run
claude-hooks install --headless --skip-dependency-checks # opt out, prints a warningUsage
Auto Commit Message
git commit -m "auto"
# Claude generates message from branch name + changes
# Format: [TASK-ID] type: description
# Example: branch feature/IX-123-auth → [IX-123] feat: add authenticationCreate PR
claude-hooks create-pr develop
# Automatically pushes branch if unpublished (with confirmation)
# Analyzes diff, generates title/description, creates PR on GitHubAuto-push feature (v2.11.0):
- Detects unpublished branches or unpushed commits
- Shows commit preview before pushing
- Prompts for confirmation (configurable)
- Handles diverged branches gracefully
Merge strategy awareness (v2.25.0):
- Auto-detects required strategy from branch naming:
feature/*/release-fix/*→ squash,release-candidate/*/hotfix/*/→ main→ merge commit - Displays strategy in PR preview; adds
merge-strategy:squashormerge-strategy:merge-commitlabel - Prepends body reminder for merge-commit PRs; unknown patterns prompt user to select manually
Automatic label resolution (v2.31.0):
- Resolves labels from 5 rule types: preset, size, quality, strategy, and defaults
- Remote config priority (your organisation's
git-hooks-configrepository,labels.json) with local fallback - Size labels:
size:S(<10 files),size:M(10-50),size:L(50-100),size:XL(>100) — thresholds configurable remotely - Quality labels:
breaking-change,security,performancefrom analysis result - Each rule type independently enabled/disabled via remote config
Team Configuration
Label rules, PR categories, and role-based permissions are managed centrally in a git-hooks-config repository belonging to your organisation. Changes there take effect immediately across all governed repositories — no tool update or deploy required. The repository is resolved from your configuration; see claude-hooks setup-github.
Code Knowledge Library (optional)
Several commands maintain a repository's Code Knowledge Library — per-file Markdown "books"
extracted from source. create-pr regenerates stale books, bump-version and create-release
verify them, pre-commit flags a book whose source changed, and close-release refuses to close
on drift. All of it is skipped silently in a repository that has no Library, which is most of them.
Two pieces have to be present for any of it to run:
| Piece | What it is | Where it comes from |
|---|---|---|
| Content | .library/ with resolver.yaml, books and indexes | Committed in the repository |
| Engine | The code that extracts and checks books | A package you declare, or a copy vendored into .library/ |
claude-git-hooks does not bundle an engine, and names none in its own code: it is a public package, and hardcoding one organisation's engine would ship a dependency most consumers cannot resolve. A repository says which engine maintains its books:
// .claude-hooks.json at the repository root — tracked, and never published
{
"libraryEngine": "@your-scope/librarian"
}claudeHooks.libraryEngine in package.json works too. Prefer .claude-hooks.json when the
engine's name identifies your organisation and your package is public: package.json is published
and rendered on the npm page, and .claude-hooks.json is neither.
Install the engine where the Library is maintained. Keep it out of dependencies — and out of
devDependencies if it lives in a private repository, or npm ci will fail for anyone without
access, CI included. CLAUDE_HOOKS_LIBRARY_ENGINE=@your-scope/librarian overrides the declaration
for a single run.
An engine is expected to expose three entry points: its package root (the pipeline), /paths
(path resolution) and /staleness (book checking).
No configuration needed if the engine is vendored. A repository that carries its own engine at
.library/librarian/index.js, .library/paths.js and .library/tools/staleness.js is used as-is;
that was the only layout before the engine became installable, and it still works.
With neither, a repository that has books reports as content-only: the commands say so once and
carry on rather than failing.
Token Setup
claude-hooks setup-github # Configure GitHub token for PR creation
claude-hooks setup-linear # Configure Linear token for ticket enrichmentOption 1 - Settings file (recommended):
// .claude/settings.local.json (gitignored)
{ "githubToken": "ghp_..." }Option 2 - Environment variable:
export GITHUB_TOKEN="ghp_..."Create token at https://github.com/settings/tokens with scopes: repo, read:org
Linting & Formatting
Runs formatters and linters on staged files automatically during pre-commit, or on demand:
# Lint staged files (default)
claude-hooks lint
# Lint all files in a directory
claude-hooks lint src/
# Lint specific files
claude-hooks lint file1.js file2.java
# Mix of directories and files
claude-hooks lint src/ lib/utils/ file.jsTools per preset (configured via your organisation's remote config repository):
| Preset | Tools |
| ----------- | ----------------------------- |
| frontend | Prettier, ESLint |
| backend | Spotless |
| fullstack | Prettier, ESLint, Spotless |
| database | sqlfluff |
| ai | Prettier, ESLint |
| default | Prettier, ESLint |
Behavior:
- Formatters run first (Prettier), then linters (ESLint) — format before lint
- Auto-fix enabled by default — fixes and re-stages files automatically
- Missing tools are skipped with install instructions (never blocks)
- Unfixable issues are forwarded to the Claude judge for semantic resolution
- Tool-to-preset mapping is fetched from remote config (team-controlled, no release needed)
Analyze Code (Interactive Review)
Run interactive code analysis before committing:
# Analyze staged changes (default)
claude-hooks analyze
# Analyze unstaged changes
claude-hooks analyze --unstaged
# Analyze all tracked files
claude-hooks analyze --allWhat it does:
- Analyzes selected file scope (staged, unstaged, or all)
- Shows all issues (INFO, MINOR, MAJOR, CRITICAL, BLOCKER)
- Interactive prompt with options:
- Continue: Creates commit automatically with auto-generated message
- Abort: Generate resolution prompt and fix issues
- View: Show detailed issue list
- Executes
git commit -m "auto" --no-verifyon confirmation - Works outside git hooks (no stdin limitations)
Use case: Complete analysis-to-commit workflow in one command.
Analyze Diff (without creating PR)
claude-hooks analyze-diff develop
# Generates PR metadata without creatingAnalyze PR from GitHub URL
claude-hooks analyze-pr https://github.com/owner/repo/pull/123
# Fetches PR, applies preset guidelines, posts review comments
claude-hooks analyze-pr https://github.com/owner/repo/pull/123 --dry-run
# Analyze without posting comments
claude-hooks analyze-pr https://github.com/owner/repo/pull/123 --preset backend --model opus
# Override preset and model- Auto-detects preset from PR labels, Linear ticket labels, or file extensions
- Enriches with Linear ticket context when
[AUT-1234]found in PR title - Interactive comment workflow: confirm/skip each finding before posting
- Classifies into inline (file:line) and general (review-level) categories
Bump Version
Automatic version management with CHANGELOG generation and Git tagging:
# Bump patch version (1.0.0 → 1.0.1)
claude-hooks bump-version patch
# Bump with suffix
claude-hooks bump-version minor --suffix SNAPSHOT # → 1.1.0-SNAPSHOT
# Bump and generate CHANGELOG
claude-hooks bump-version major --update-changelog
# Preview without applying
claude-hooks bump-version patch --dry-run
# Push tag immediately (otherwise pushed by create-pr)
claude-hooks bump-version patch --push
# Manual workflow (skip automatic commit)
claude-hooks bump-version patch --no-commitWhat it does:
- Detects project type (Node.js, Maven, or monorepo with both)
- Updates
package.jsonand/orpom.xml - Generates CHANGELOG entry with Claude (analyzes commits)
- Commits changes automatically with conventional commit format
- Creates annotated Git tag with
vprefix (e.g.,v2.7.0) - Tags stay local by default (use
--pushto push immediately, or letcreate-prhandle it)
Version workflow:
2.7.0 → 2.8.0-SNAPSHOT # Start development
2.8.0-SNAPSHOT → 2.8.0-RC # Release candidate
2.8.0-RC → 2.8.0 # Final releaseIntegration with create-pr:
- Validates version alignment (package.json, pom.xml, CHANGELOG, tags)
- Detects and prompts to push unpushed tags
- Warns if local version ≤ remote version
Generate CHANGELOG
Standalone CHANGELOG generation (without version bump):
# Auto-detect version from package.json/pom.xml
claude-hooks generate-changelog
# Specific version with release marking
claude-hooks generate-changelog 2.7.0 --release
# Compare against different base branch
claude-hooks generate-changelog --base-branch developWhat it does:
- Analyzes commits since last tag using Claude
- Categorizes by Conventional Commits types (feat, fix, refactor, etc.)
- Generates Keep a Changelog format entries
- Discovers all CHANGELOG.md files (monorepo aware); prompts to select when multiple found
- Updates the selected CHANGELOG.md automatically
- Useful when
bump-version --update-changelogfails
Disable/Enable Hooks
claude-hooks disable [pre-commit|prepare-commit-msg]
claude-hooks enable [pre-commit|prepare-commit-msg]Uninstall
claude-hooks uninstallPresets
claude-hooks presets # List available
claude-hooks --set-preset backend # Set preset
claude-hooks preset current # Show current| Preset | Extensions | Focus | | --------- | ---------------------------------------- | ------------------------ | | backend | .java, .xml, .yml, .yaml | Spring Boot, JPA, OWASP | | frontend | .js, .jsx, .ts, .tsx, .css, .scss, .html | React, XSS, a11y | | fullstack | all above | API contract consistency | | database | .sql | SQL injection, indexes | | liquibase | .yaml, .sql | Liquibase changesets, rollback, T-SQL | | ai | .js, .json, .md, .sh | Node.js, Claude API | | default | multiple | General quality |
Skip Analysis
git commit --no-verify -m "message"Judge Auto-Fix (opt-in)
The judge always reports its findings and the concrete patch it would apply. Whether that patch is written to your working tree is opt-in and off by default.
git commit -m "message" # default: fixes are suggested, not applied
CLAUDE_HOOKS_FIX=1 git commit -m "message" # apply fixes and stage them
CLAUDE_HOOKS_FIX=0 git commit -m "message" # force off, even if config enables itPersistently, via .claude/config.json or the team's remote settings.json:
{ "judge": { "autoFix": true } }Precedence: CLAUDE_HOOKS_FIX > user config > remote/team settings > packaged default (false).
A suggested-but-unapplied fix still blocks the commit — the defect is real and still present. Suggestion mode removes the mutation, not the quality gate.
Why off by default: field data across multiple sessions put auto-fixes at roughly 50/50 between resolving the issue and introducing an error that had to be reverted. Until fix quality is measured per issue category, the safe default is to show the patch and let you apply it.
Claude transport (CLI vs SDK)
How claude-hooks reaches Claude is an explicit configuration choice, independent of how you invoke it. The default is the Claude CLI, using your normal interactive login:
{ "claude": { "spawn": { "transport": "cli" } } }Setting transport to "sdk" routes calls through the Anthropic SDK instead, which
requires ANTHROPIC_API_KEY in the environment. This is a parked capability, not a
supported configuration — spend is expected to stay on personal login tokens, so the
SDK path is retained (and deliberately exercised by claude-hooks install --verify-sdk)
but is not funded. Leave it on cli unless that changes.
Where it can be set, and where it cannot:
| Channel | Works? |
|---|---|
| lib/defaults.json (packaged default) | ✅ "cli" |
| Team remote settings.json → claude.spawn.transport | ✅ deep-merged over the default |
| Per-call transport option in code | ✅ overrides both |
| User .claude/config.json | ❌ silently ignored — resolveSpawnConfig() reads the resolved claude section directly and never consults user config |
That last row is a real limitation, not an oversight in this table: wiring user config into
spawn resolution would also change how claude.defaultModel resolves for existing callers,
which is out of scope for a transport switch.
--headlessdoes not select a transport. It means non-interactive — skip prompts — and nothing more. It used to also mean "use the SDK", which made--format json(which required--headless) unreachable without an API key. Those meanings are now separate, and--format jsonworks on its own. LikewiseCLAUDE_HOOKS_HEADLESS=1no longer has any effect in the git hooks; they warn if it is set.
Debug Mode
claude-hooks --debug true|false|statusExtensions
Third-party tools plug into the pre-commit hook as isolated child processes. They are opt-in:
an extension runs only when .claude/config.json names it, and remote team config may disable one
but never enable it.
In v1 the wiring is narrower than the manifest schema: only pre-commit:pre-analysis and
pre-commit:post-analysis are wired, and only sync-joining subscriptions there actually run. An
async subscription — or one to any of the other ten hook points a manifest accepts — validates
cleanly and shows as enabled in claude-hooks extension list, but is never launched and emits no
metric.
claude-hooks extension list # Installed extensions, activation and breaker state
claude-hooks extension enable <name> # Clear a tripped circuit breaker
claude-hooks extension disable <name> # Trip the breaker by handAn extension that fails three times in a row is disabled automatically and stops being launched, so
a broken integration costs three failed invocations rather than every commit. The trip lands on the
third commit — or the second, if the extension subscribes to both wired pre-commit points and is
therefore launched twice per commit. A new manifest version clears the breaker and restores the full
three strikes. enable clears it too, but it does not opt the extension in, which stays a
config decision.
Extension findings are reported, never judged. An extension runs an SDLC process parallel to the analysis — a linter, a scanner, a licence check — so its output is a pass/fail fact about the repo rather than a second opinion on the code. Its findings are therefore excluded from the judge entirely: they neither trigger a judge run nor appear in its input, and a clean commit stays a one-spawn commit no matter how much an extension reports. They are still shown, still counted in the totals, and still summarised in the "reported, not blocking" notice.
By default a finding also cannot block: it is clamped to info and stamped with the extension
category, both of which the gate demotes. Adding "allowBlocking": true beside "enabled": true
in an extension's entry lifts both, and does so deterministically — because the judge never
sees the finding, nothing can dismiss it as a false positive. There is no allowJudging grant.
The opt-in must sit inside overrides, in a config declaring "version": "2.8.0". A section
written at the top level is ignored — with a warning, but the extension will not run:
{
"version": "2.8.0",
"overrides": { "extensions": { "entries": { "my-extension": { "enabled": true } } } }
}Telemetry
claude-hooks telemetry show # View statistics
claude-hooks telemetry clear # Clear dataHelp & Issue Reporting
claude-hooks help "how do presets work?" # AI-powered help (navigates .library/)
claude-hooks help --report-issue # Interactive GitHub issue creation
claude-hooks --help # Static command referenceBatch Info
claude-hooks batch-info
# Shows: orchestration model, threshold, default analysis model
# Shows: per-model average analysis time and orchestration overhead (from telemetry)Third-Party Integrations (extension points)
Third-party tools plug in through the extension-point contract — isolated child processes
speaking JSON over stdio, opt-in per extension, and unable to block a commit unless explicitly
granted. The in-process skillRegistry integration this replaced was removed with the contract
(AUT-4409); a skillRegistry block left in your config is ignored and reported once on load.
Activation requires both a local install and a config opt-in:
| Installed locally | Enabled in config | Behaviour | | --- | --- | --- | | yes | yes | runs, bounded by the manifest timeout | | no | yes | one warning naming what is missing | | yes | no | nothing — opt-in is mandatory (INV-5) | | no | no | nothing |
claude-hooks extension list # installed extensions, activation and breaker state
claude-hooks extension enable <name> # clear a tripped circuit breaker
claude-hooks extension disable <name> # trip the breaker by handTools that need no adapter at all can be run straight from config as triggers — before or after any claude-hooks command, sync or async:
{ "on": "create-pr", "when": "after", "mode": "async", "timeoutMs": 30000,
"run": ["automation-skills", "retro"] }Writing or reintegrating an extension: see docs/EXTENSIONS.md — §8 for triggers (no adapter), §2–6 for contributions (JSON envelope), §9 for skills.
Other Commands
claude-hooks status # Show hook status
claude-hooks update # Update to latest versionArchitecture Reference (AI Context)
Design Philosophy
Modular, decoupled, reusable code. Each component has a single responsibility and can be tested/modified independently.
Directory Map
| Path | Purpose | Key Exports |
| ------------------ | -------------------------------------------------------- | ---------------------------------------- |
| bin/claude-hooks | Thin CLI router - argument parsing, command dispatch | - |
| lib/commands/ | Command modules - one file per CLI command | See table below |
| lib/config.js | Config system - load/merge configuration | getConfig() |
| lib/hooks/ | Git hook logic - Node.js implementations | pre-commit.js, prepare-commit-msg.js |
| lib/utils/ | Reusable utilities - shared across commands | See table below |
| templates/ | Static assets - bash wrappers, prompts, presets | Copied during install |
Command Modules (lib/commands/)
| Module | Purpose | Key Exports |
| ----------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| helpers.js | Shared CLI utilities - colors, output, platform detection | colors, error(), success(), info(), checkGitRepo(), Entertainment |
| install.js | Installation logic - dependencies, hooks, templates | runInstall(), extractLegacySettings() |
| hooks.js | Hook management - enable, disable, status, uninstall | runEnable(), runDisable(), runStatus(), runUninstall() |
| analyze.js | Interactive code analysis - analyze before committing | runAnalyze() |
| analyze-diff.js | Diff analysis - generate PR metadata from git diff | runAnalyzeDiff() |
| analyze-pr.js | PR analysis - analyze GitHub PR with team guidelines | runAnalyzePr() |
| create-pr.js | PR creation - full workflow via Octokit | runCreatePr() |
| bump-version.js | Version management - bump with CHANGELOG and Git tag | runBumpVersion() |
| generate-changelog.js | CHANGELOG generation - standalone command | runGenerateChangelog() |
| setup-github.js | Token setup - interactive GitHub configuration | runSetupGitHub() |
| setup-linear.js | Token setup - interactive Linear configuration | runSetupLinear() |
| presets.js | Preset management - list, set, show current | runShowPresets(), runSetPreset(), runCurrentPreset() |
| update.js | Self-update - check and install latest version | runUpdate() |
| migrate-config.js | Config migration - legacy to the current format | runMigrateConfig() |
| debug.js | Debug toggle - enable/disable verbose logging | runSetDebug() |
| telemetry-cmd.js | Telemetry commands - show/clear statistics | runShowTelemetry(), runClearTelemetry() |
| diff-batch-info.js | Batch info - orchestration config + per-model speed telemetry | runDiffBatchInfo() |
| help.js | Help, AI librarian, report-issue | runShowHelp(), showStaticHelp(), runShowVersion() |
Utility Modules (lib/utils/)
| Module | Purpose | Key Exports |
| ------------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| analysis-engine.js | Shared analysis logic - file data, orchestration, results (v2.13.0) | buildFilesData(), runAnalysis(), consolidateResults(), displayResults() |
| diff-analysis-orchestrator.js | Intelligent orchestration - semantic batch grouping via Opus (v2.20.0) | orchestrateBatches(), buildFileOverview(), detectDependencies() |
| claude-client.js | Claude CLI wrapper - spawn, retry, model override | analyzeCode(), executeClaudeWithRetry(), extractJSON() |
| prompt-builder.js | Prompt construction - load templates, replace variables, inject commit context | buildAnalysisPrompt(), loadPrompt() |
| git-operations.js | Git abstractions - staged files, diff, branch comparison | getStagedFiles(), getDiff(), getRepoRoot(), resolveBaseBranch(), getDiffBetweenRefs() |
| pr-metadata-engine.js | PR metadata generation - branch context, diff reduction (v2.14.0) | getBranchContext(), buildDiffPayload(), generatePRMetadata(), analyzeBranchForPR() |
| github-api.js | Octokit integration - PR creation, PR analysis, token validation, content fetching | createPullRequest(), fetchPullRequest(), fetchPullRequestFiles(), createPullRequestReview(), parseGitHubPRUrl(), validateToken(), saveGitHubToken(), fetchFileContent(), fetchDirectoryListing(), createIssue() |
| token-store.js | Token persistence - centralized settings.local.json read/write | loadToken(), saveToken(), hasToken(), loadLocalSettings() |
| linear-connector.js | Linear integration - ticket context fetching with retry | fetchTicket(), extractLinearTicketFromTitle(), testConnection(), loadLinearToken() |
| pr-statistics.js | PR statistics - write-only JSONL analytics | recordPRAnalysis() |
| github-client.js | GitHub helpers - repo parsing, config-based reviewers | getReviewersForFiles(), parseGitHubRepo() |
| reviewer-selector.js | Team-based reviewer selection - GitHub Teams API resolution (v2.32.0) | selectReviewers() |
| preset-loader.js | Preset system - load tech-stack configurations | loadPreset(), listPresets() |
| task-id.js | Task ID extraction - Jira, GitHub, Linear patterns | getOrPromptTaskId(), formatWithTaskId() |
| judge.js | Auto-fix judge - LLM verdict + search/replace fixes (v2.20.0) | judgeAndFix(), applyFix() |
| resolution-prompt.js | Issue resolution - AI-friendly fix prompts | generateResolutionPrompt() |
| logger.js | Logging system - centralized output with debug mode | info(), warning(), error(), debug() |
| interactive-ui.js | CLI UI components - previews, prompts, spinners | showPRPreview(), promptConfirmation(), promptMenu() |
| telemetry.js | Local telemetry - track JSON parsing, retries | recordEvent(), displayStatistics() |
Execution Flow
Pre-commit Hook
git commit → templates/pre-commit (bash wrapper)
→ lib/hooks/pre-commit.js
→ getStagedFiles() → filterFiles() by preset extensions + size
→ buildFilesData() → runAnalysis() (via analysis-engine.js)
→ displayResults() → show quality gate status
→ judge evaluates ALL issues (any severity):
→ TRUE_ISSUE, judge.autoFix off (DEFAULT): patch printed as a suggestion,
working tree untouched, issue stays unresolved
→ TRUE_ISSUE, judge.autoFix on: auto-fix via search/replace + git add
→ FALSE_POSITIVE: dismissed
→ LATENT: recorded, never fixed, never blocking (no caller reaches it today)
→ ALL resolved: exit 0 (pass)
→ ANY unresolved: generates resolution prompt → exit 1 (block)
→ judge failure: user warned → exit 1 (block)
→ judge disabled: original quality gate (blocks on critical/blocker only)Note: For interactive review of non-blocking issues, use claude-hooks analyze before committing.
Commit Message Generation
git commit -m "auto" → templates/prepare-commit-msg (bash wrapper)
→ lib/hooks/prepare-commit-msg.js
→ extract task-id from branch name
→ generate message via Claude
→ write to COMMIT_EDITMSGPR Metadata Generation (analyze-diff / create-pr)
claude-hooks analyze-diff|create-pr → bin/claude-hooks (router)
→ lib/commands/analyze-diff.js or create-pr.js (thin wrapper)
→ analyzeBranchForPR() (pr-metadata-engine.js)
→ resolveBaseBranch() (git-operations.js)
→ getDiffBetweenRefs() + getCommitsBetweenRefs()
→ buildDiffPayload() with tiered reduction (context → proportional → stat-only)
→ executeClaudeWithRetry() → PRMetadata
→ analyze-diff: formats to console + saves JSON
→ create-pr: additionally creates PR via Octokit APIPR Analysis (analyze-pr)
claude-hooks analyze-pr <url> → bin/claude-hooks (router)
→ lib/commands/analyze-pr.js
→ parseGitHubPRUrl() → fetchPullRequest() + fetchPullRequestFiles()
→ extractLinearTicketFromTitle() → fetchTicket() (optional enrichment)
→ resolvePreset() (labels → ticket → auto-detect → default)
→ loadPrompt('ANALYZE_PR.md') with preset guidelines + categories
→ executeClaudeWithRetry() → extractJSON() → normalizeCategory()
→ interactive comment workflow (confirm/skip)
→ createPullRequestReview() → inline + general comments
→ recordPRAnalysis() (JSONL statistics)Config Priority
defaults (lib/config.js)
< user config (.claude/config.json)
< preset config (.claude/presets/{name}/)Preset always wins - tech-stack specific has priority over user preferences.
claude.spawndoes not follow this chain. Model, timeouts and transport resolve fromlib/defaults.jsonmerged with the team's remotesettings.json. User.claude/config.jsonhydrates onlyanalysis,commitMessageandlinting, so aclaude.spawnblock placed there is silently ignored. Change those knobs in the remote settings file.
Environment variables
| Variable | Effect |
|---|---|
| CLAUDE_HOOKS_FIX=1 / =0 | Force judge auto-fix on or off for one hook run |
| CLAUDE_HOOKS_PREFER_GLOBAL=1 | Make the hook shim resolve the globally installed package instead of the local checkout. Only relevant inside the claude-git-hooks repo itself, where the shim self-hosts so that a hook change gates itself |
| CLAUDE_HOOKS_HEADLESS=1 | Inert. Once selected the SDK transport; now warns and does nothing. Use claude.spawn.transport |
Config Format (v2.8.0)
The version field is a config format version, unrelated to the package version. It is
matched by major, so any 2.x or 3.x config is read as-is and 2.x configs keep working
indefinitely — there is no forced migration.
New configs are still written as 2.8.0, deliberately. A config declaring a version an older
installation does not recognise is fatal there — every release before this one compares the string
exactly and fails the hook — so the reader learns to accept the next format before anything starts
writing it. Nothing behavioural depends on which supported version the string names.
{
"version": "2.8.0",
"preset": "backend",
"overrides": {
"github": {
"pr": {
"defaultBase": "develop",
"reviewers": ["user"],
"autoPush": true, // Auto-push unpublished branches (v2.11.0)
"pushConfirm": true, // Prompt before push (v2.11.0)
"showCommits": true, // Show commit preview (v2.11.0)
"verifyRemote": true // Verify remote exists (v2.11.0)
}
}
}
}Key Patterns
- Factory:
preset-loader.js- dynamic config per tech-stack - Template Method:
prompt-builder.js- prompts from .md templates - Strategy:
analysis-engine.js- sequential vs orchestrated analysis - Adapter:
git-operations.js- git commands to JS functions - Command:
lib/commands/*.js- one module per CLI command
Modification Guide
| To change... | Edit... |
| ------------------------- | ------------------------------------------------- |
| CLI argument parsing | bin/claude-hooks |
| Install workflow | lib/commands/install.js |
| PR creation flow | lib/commands/create-pr.js |
| PR analysis from URL | lib/commands/analyze-pr.js |
| Analysis logic | lib/hooks/pre-commit.js |
| Message generation | lib/hooks/prepare-commit-msg.js |
| Prompt templates | templates/*.md or .claude/prompts/*.md |
| Preset definitions | templates/presets/{name}/ |
| Config defaults | lib/config.js |
| GitHub integration | lib/utils/github-api.js |
| Claude CLI calls | lib/utils/claude-client.js |
| Batch orchestration logic | lib/utils/diff-analysis-orchestrator.js |
| Orchestration prompt | templates/DIFF_ANALYSIS_ORCHESTRATION_PROMPT.md |
| Auto-fix judge logic | lib/utils/judge.js |
File Structure
claude-git-hooks/
├── bin/claude-hooks # Thin CLI router - dispatch to commands
├── lib/
│ ├── config.js # Config system - load and merge
│ ├── commands/ # Command modules - one per CLI command
│ │ ├── helpers.js # Shared utilities - colors, output
│ │ ├── install.js # Install command - hooks, templates
│ │ ├── hooks.js # Hook management - enable/disable
│ │ ├── analyze-diff.js # Diff analysis - PR metadata
│ │ ├── analyze-pr.js # PR analysis - GitHub PR review
│ │ ├── create-pr.js # PR creation - Octokit workflow
│ │ ├── setup-github.js # Token setup - interactive GitHub config
│ │ ├── setup-linear.js # Token setup - interactive Linear config
│ │ ├── presets.js # Preset commands - list/set
│ │ ├── update.js # Self-update - npm latest
│ │ ├── migrate-config.js # Migration - legacy to current format
│ │ ├── debug.js # Debug mode - toggle verbose
│ │ ├── telemetry-cmd.js # Telemetry - show/clear stats
│ │ ├── diff-batch-info.js # Batch info - orchestration config
│ │ └── help.js # Help display - usage info
│ ├── hooks/ # Git hooks - Node.js logic
│ │ ├── pre-commit.js # Pre-commit analysis
│ │ └── prepare-commit-msg.js # Message generation
│ └── utils/ # Reusable modules - shared logic
│ ├── diff-analysis-orchestrator.js # Intelligent batch orchestration
│ ├── judge.js # Auto-fix judge (v2.20.0)
│ └── token-store.js # Token persistence - settings.local.json
├── .library/ # Code Knowledge Library - auto-generated module docs
│ ├── books/ # One book per source module (auto + manual sections)
│ ├── extractor/ # Tree-sitter AST tooling
│ │ ├── extract.js # Source → book auto-section generator
│ │ ├── parser.js # WASM parser init and grammar loading
│ │ └── adapters/ # Language-specific CST → normalized AST
│ └── templates/ # Book schema and category reference
├── templates/
│ ├── pre-commit # Bash wrapper - invokes Node.js
│ ├── prepare-commit-msg # Bash wrapper - invokes Node.js
│ ├── DIFF_ANALYSIS_ORCHESTRATION_PROMPT.md # Opus orchestration prompt
│ ├── *.md # Other prompt templates
│ └── presets/ # Preset configurations
└── test/unit/ # Jest testsAnalysis Routing (v2.20.0)
Two-tier strategy based on file count:
| Files | Strategy | Details | | ----- | ----------------------------- | ------------------------------------------------------------------------------ | | 1–2 | Sequential | Single Claude call, full context | | 3+ | Intelligent orchestration | Opus groups files semantically, assigns model per batch, shared commit context |
Orchestration flow (3+ files):
- Opus reads a lightweight file overview + detected cross-file dependencies
- Groups related files into semantically coherent batches
- Assigns model per batch:
haiku(config/docs),sonnet(business logic),opus(security-critical) - All batches run in parallel; each receives a shared commit overview header
- Falls back to one-file-per-batch if orchestration fails
Inspect orchestration settings:
claude-hooks batch-info # Shows config + per-model avg speed from telemetryHardcoded Defaults
- Max file size: 1MB
- Max files per commit: unlimited by default (set
analysis.maxFilesto cap) - Orchestrator threshold: 3 files
- Orchestrator model: opus (internal, not configurable)
- Extensions: discovered from
.claude/extensions/, every one disabled until opted in per extension; blocking requires an explicitallowBlockinggrant
Contributing
Branching Strategy
This repository follows simplified GitHub Flow:
- Main branch:
main(protected) - Feature branches:
feature/issue-descriptionorfeature/TASK-ID-description - Fix branches:
fix/issue-description - Hotfix branches:
hotfix/urgent-description
Rules:
- All changes require PR to
main - No direct commits to
main - PRs require review (auto-merge NOT allowed)
Development Process
1. Initial setup
git clone <your fork or source checkout>
cd git-hooks
npm install
npm link # Install globally as symlink for development2. Create feature branch
git checkout -b feature/new-functionality
# or with task-id: feature/IX-123-new-functionality3. Develop and test
npm run lint # ESLint over lib/ bin/ test/ scripts/
# (fails on warnings too: --max-warnings 0)
npm run lint:fix # Auto-fix ESLint issues
npm run format # Format with Prettier (writes)
npm run format:check # Verify formatting WITHOUT writing — this is the CI gate
npm run test:e2e # SDLC e2e; --local-remote is the DEFAULT: a throwaway bare repo
# stands in for origin, so no push rights are needed and origin
# is never touched
npm run test:e2e:origin # same suite with --against-origin: pushes test branches and
# deletes remote refs. Requires push authority.
#
# bash test/manual/sdlc-stability-check.sh --help # full flag list
npm run test # Run all tests (Jest)
npm run test:watch # Tests in watch mode
npm run test:coverage # Coverage report4. Local testing in a test repo
cd /path/to/test-repo
claude-hooks install --force --skip-auth # Reinstall from symlink
git commit -m "test" # Test hook (npm link is active, no reinstall needed)5. Create PR
git push -u origin feature/new-functionality
claude-hooks analyze-diff main # Generate PR metadata
claude-hooks create-pr main # Or create PR directly on GitHubCI/CD
No automated CI/CD (GitHub Actions pending). Manual verifications before merge:
npm run lintpassesnpm run test— all tests pass- Manual testing in test repo
- Peer code review
- CHANGELOG.md updated
Versioning
Follows Semantic Versioning (MAJOR.MINOR.PATCH):
- MAJOR: Breaking changes (config format, CLI arguments)
- MINOR: New features without breaking changes
- PATCH: Bug fixes
npm version patch # 2.6.1 → 2.6.2
npm version minor # 2.6.1 → 2.7.0
npm version major # 2.6.1 → 3.0.0
npm publish # Publish to NPMConventional Commits
Format: <type>(<scope>): <subject>
| Type | Purpose |
|------|---------|
| feat | New functionality |
| fix | Bug fix |
| docs | Documentation only |
| style | Formatting (no logic change) |
| refactor | Refactoring without functional change |
| test | Add or modify tests |
| chore | Maintenance (deps, config) |
| perf | Performance improvements |
Optional scopes: hooks, presets, github, cli, config, windows
feat(presets): add database preset for SQL analysis
fix(windows): resolve spawn ENOENT with .cmd files
docs: update CLAUDE.md with architecture details
chore(deps): upgrade @octokit/rest to v21More information: see the source repository for your installation.
