claude-git-hooks 2.35.2 → 2.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -1,1395 +1,35 @@
1
1
  # Repository Context
2
2
 
3
- ## Purpose
3
+ `claude-git-hooks` — intelligent Git hooks system integrating Claude CLI for code analysis, commit message generation, PR creation, and release workflow automation.
4
4
 
5
- `claude-git-hooks` is an intelligent Git hooks system that integrates Claude CLI to automate code analysis, commit message generation, and PR creation. It functions as a pre-commit code quality tool (for pre-commit code quality checks) that blocks commits with critical issues and assists throughout the development workflow.
5
+ ## Context Acquisition
6
6
 
7
- **Main use cases:**
7
+ All project knowledge lives in [`.library/`](.library/). Start at [`.library/index.md`](.library/index.md).
8
8
 
9
- 1. **Pre-commit linting**: Runs formatters and linters (Prettier, ESLint, Spotless, sqlfluff) on staged files before Claude analysis — fast, deterministic, auto-fix enabled by default
10
- 2. **Pre-commit analysis**: Detects security issues, bugs, and code smells before each commit (blocks on CRITICAL/BLOCKER only)
11
- 3. **Interactive analysis**: `claude-hooks analyze` - review all issues (INFO to BLOCKER) interactively before committing
12
- 4. **Automatic messages**: Write `git commit -m "auto"` and Claude generates the message in Conventional Commits format with task-id extracted from branch
13
- 5. **PR analysis**: `claude-hooks analyze-diff [branch]` generates title, description, and test plan for PRs
14
- 6. **PR review**: `claude-hooks analyze-pr <url>` analyzes a GitHub PR with preset guidelines, Linear ticket enrichment, and posts review comments
15
- 7. **PR creation**: `claude-hooks create-pr [branch]` creates the PR on GitHub with automatic metadata (reviewers from CODEOWNERS, labels by preset, merge strategy auto-detected from branch naming)
16
- 8. **Linting**: `claude-hooks lint [paths...]` runs formatters and linters on staged files, directories, or specific files — supports Prettier, ESLint, Spotless, sqlfluff per preset (remote config priority with local fallback)
17
- 9. **Coupling detection**: `claude-hooks check-coupling` scans open PRs targeting a base branch, computes file overlap, and reports which features are coupled (share modified files) — helps TL make informed decisions before cutting a release
18
- 10. **Shadow management**: `claude-hooks shadow <analyze|reset|sync>` manages the shadow branch lifecycle — analyze divergence vs main and active RC, reset shadow to a clean copy of main, or sync shadow with a source branch (RC, develop, feature)
19
- 11. **Release creation**: `claude-hooks create-release <major|minor|patch>` creates a release-candidate branch from develop, bumps version files, commits, pushes, and deploys to shadow — replaces the 8 manual steps executed every Tuesday by the Tech Lead
20
- 12. **Feature revert**: `claude-hooks revert-feature <task-id>` finds a squash-merged feature commit by task ID in the current release-candidate, checks coupling with other RC features, reverts it, pushes, and optionally re-deploys shadow
21
- 13. **Release closure**: `claude-hooks close-release [description]` finalizes the active release-candidate — soft-resets onto main, creates a single clean commit, force-pushes, and creates a PR to main with the merge-commit strategy
9
+ Load context based on your current task:
22
10
 
23
- ## Architecture
11
+ | Task | Load |
12
+ |------|------|
13
+ | Writing or modifying code | [`.library/conventions.md`](.library/conventions.md) — coding standards, testing patterns |
14
+ | Understanding a source module | [`.library/by-code/`](.library/by-code/) — find the book for that file |
15
+ | Understanding a business workflow | [`.library/by-domain/`](.library/by-domain/) — commit pipeline, release management, PR analysis, GitHub integration |
16
+ | Adding a new CLI command | [`.library/by-task-type/add-new-command.md`](.library/by-task-type/add-new-command.md) — 7-book reading sequence |
17
+ | Updating the library after code changes | [`.library/README.md`](.library/README.md) — book schema, template, creation steps |
24
18
 
25
- ### Technology Stack
19
+ Books follow a standard template at `.library/templates/book-template.md`. After modifying a module, update its book and the relevant shelf index.
26
20
 
27
- - **Runtime**: Node.js >=16.9.0 (compatible up to 24+)
28
- - **Module type**: ES6 modules (`"type": "module"` in package.json)
29
- - **Main dependencies**:
30
- - `@octokit/rest`: GitHub client for PR creation (v2.5.0+)
31
- - **Dev dependencies**: Jest, ESLint, Prettier
32
- - **Distribution**: NPM global package (`npm install -g claude-git-hooks`)
21
+ ## Global Rules
33
22
 
34
- ### Design Philosophy
23
+ These behavioral rules apply to all work in this repository:
35
24
 
36
- **Modular, decoupled, reusable code.** Each component has a single responsibility:
25
+ 1. **Do NOT perform git add, commit, or push unless explicitly asked** git flow is handled by the human user
26
+ 2. **Do NOT add dependencies without justification** — only 1 runtime dep (`@octokit/rest`); verify no built-in alternative exists
27
+ 3. **Do NOT modify `.gitignore` without consultation** — `.claude/` is ignored by design
28
+ 4. **Do NOT commit secrets** — tokens go in `.claude/settings.local.json` (gitignored) or env vars, never in tracked files
29
+ 5. **Do NOT make breaking changes without MAJOR version bump** — config format, CLI arguments, template format changes require MAJOR
30
+ 6. **Do NOT hardcode absolute paths** — use `getRepoRoot()` from `git-operations.js`; all paths relative to repo root
31
+ 7. **Do NOT use `console.log`** — always use `logger.js`: `info()`, `warning()`, `error()`, `debug()`
32
+ 8. **Platform-specific care** — use `path.join()` (no hardcoded `/`); test on Windows, Linux, macOS
33
+ 9. **Input sanitization** — use `sanitize.js` for user inputs in shell commands; avoid `shell: true` in `spawn()`
37
34
 
38
- - **bin/claude-hooks**: Thin CLI router - argument parsing, command dispatch, and authorization guard
39
- - **lib/commands/**: Command modules - one file per CLI command, self-contained logic
40
- - **lib/hooks/**: Git hook logic - Node.js implementations invoked by bash wrappers
41
- - **lib/utils/**: Reusable utilities - shared across commands, no CLI dependencies
42
- - **templates/**: Static assets - copied during installation, user-customizable
43
-
44
- This separation enables:
45
-
46
- - **Testability**: Each module can be unit tested independently
47
- - **Maintainability**: Changes to one command don't risk breaking others
48
- - **Discoverability**: Find code by filename matching command name
49
- - **Extensibility**: Add new commands by creating new module files
50
-
51
- ### Directory Structure
52
-
53
- ```
54
- claude-git-hooks/
55
- ├── bin/
56
- │ └── claude-hooks # Thin CLI router - uses cli-metadata.js registry for dispatch
57
- ├── lib/
58
- │ ├── cli-metadata.js # Command registry - single source of truth for CLI commands, flags, descriptions
59
- │ ├── config.js # Config system - load/merge with priority
60
- │ ├── commands/ # Command modules - one file per CLI command
61
- │ │ ├── helpers.js # Shared CLI utilities - colors, output, platform
62
- │ │ ├── install.js # Install command - dependencies, hooks, templates, linter check
63
- │ │ ├── hooks.js # Hook management - enable, disable, status, uninstall
64
- │ │ ├── analyze-diff.js # Diff analysis - generate PR metadata from git diff
65
- │ │ ├── analyze-pr.js # PR analysis - analyze GitHub PR with team guidelines
66
- │ │ ├── check-coupling.js # Coupling detection - detect coupled PRs before release (v2.23.0)
67
- │ │ ├── shadow.js # Shadow management - analyze/reset/sync shadow branch lifecycle (v2.24.0)
68
- │ │ ├── create-release.js # Release creation - RC branch from develop, version bump, shadow deploy (v2.27.0)
69
- │ │ ├── revert-feature.js # Feature revert - find by task-id, coupling check, git revert, push, shadow (v2.28.0)
70
- │ │ ├── close-release.js # Release closure - soft-reset, single commit, force-push, PR to main (v2.29.0)
71
- │ │ ├── create-pr.js # PR creation - full Octokit workflow
72
- │ │ ├── setup-github.js # Token setup - interactive GitHub configuration
73
- │ │ ├── setup-linear.js # Token setup - interactive Linear configuration
74
- │ │ ├── presets.js # Preset management - list, set, show current
75
- │ │ ├── update.js # Self-update - check and install latest version
76
- │ │ ├── migrate-config.js # Config migration - legacy to v2.8.0 format
77
- │ │ ├── debug.js # Debug toggle - enable/disable verbose logging
78
- │ │ ├── telemetry-cmd.js # Telemetry commands - show/clear statistics
79
- │ │ ├── bump-version.js # Version management - bump with commit, CHANGELOG and tags
80
- │ │ ├── generate-changelog.js # CHANGELOG generation - standalone command
81
- │ │ ├── diff-batch-info.js # Batch info - orchestration config + speed telemetry (v2.20.0)
82
- │ │ ├── lint.js # Lint command - run linters on staged files, dirs, or files
83
- │ │ └── help.js # Help, AI help, and report-issue commands
84
- │ ├── hooks/ # Git hooks - Node.js implementations
85
- │ │ ├── pre-commit.js # Pre-commit analysis - code quality gate
86
- │ │ └── prepare-commit-msg.js # Message generation - auto commit messages
87
- │ └── utils/ # Reusable modules - shared logic
88
- │ ├── tool-runner.js # Generic tool executor - resolve, spawn, parse, auto-fix (v2.34.0)
89
- │ ├── linter-runner.js # Linter orchestration - preset mapping, Prettier/ESLint/Spotless/sqlfluff, remote config (v2.34.0)
90
- │ ├── analysis-engine.js # Shared analysis logic - file data, 3-tier routing, results (v2.13.0+)
91
- │ ├── diff-analysis-orchestrator.js # Intelligent batch orchestration via Opus (v2.20.0)
92
- │ ├── claude-client.js # Claude CLI wrapper - spawn, retry, model override
93
- │ ├── prompt-builder.js # Prompt construction - load templates, replace vars
94
- │ ├── git-operations.js # Git abstractions - staged files, diff, repo root, push, commit, checkout, merge, reset, force-push, delete-remote-branch, divergence
95
- │ ├── file-utils.js # File operations - repo-relative paths
96
- │ ├── logger.js # Logging system - centralized output, debug mode
97
- │ ├── preset-loader.js # Preset system - load tech-stack configurations
98
- │ ├── pr-metadata-engine.js # PR metadata generation - branch context, diff reduction, metadata (v2.14.0)
99
- │ ├── github-api.js # Octokit integration - PR creation, PR analysis, token validation, Teams API
100
- │ ├── github-client.js # GitHub helpers - repo parsing, config-based reviewers (fallback)
101
- │ ├── reviewer-selector.js # Team-based reviewer selection - resolve team members, exclude author (v2.32.0)
102
- │ ├── authorization.js # Role-based authorization - PROTECTED_COMMANDS, authorizeCommand(), fail-closed (v2.23.0)
103
- │ ├── remote-config.js # Remote config fetcher - cached JSON from git-hooks-config repo (v2.31.0)
104
- │ ├── label-resolver.js # Label resolution - 5-rule engine with remote + local fallback (v2.31.0)
105
- │ ├── token-store.js # Token persistence - centralized settings.local.json read/write
106
- │ ├── linear-connector.js # Linear API - ticket context fetching with retry
107
- │ ├── coupling-detector.js # Coupling algorithm - Union-Find transitive grouping (v2.23.0)
108
- │ ├── pr-statistics.js # PR statistics - write-only JSONL analytics
109
- │ ├── task-id.js # Task ID extraction - Jira, GitHub, Linear patterns
110
- │ ├── interactive-ui.js # CLI UI components - previews, prompts, spinners
111
- │ ├── judge.js # Auto-fix judge - LLM verdict + search/replace fixes (v2.20.0)
112
- │ ├── resolution-prompt.js # Issue resolution - AI-friendly fix prompts
113
- │ ├── installation-diagnostics.js # Installation diagnostics - error context
114
- │ ├── claude-diagnostics.js # Claude errors - rate limit, auth, formatting
115
- │ ├── which-command.js # Executable resolution - cross-platform paths
116
- │ ├── sanitize.js # Input sanitization - shell safety
117
- │ ├── telemetry.js # Local telemetry - track JSON parsing, retries
118
- │ ├── version-manager.js # Version detection - parse, increment, validate, per-file targets (v2.12.0)
119
- │ ├── git-tag-manager.js # Git tag operations - create, list, compare, push, isSemverTag (v2.12.0)
120
- │ └── changelog-generator.js # CHANGELOG generation - Claude-powered analysis (v2.12.0)
121
- ├── templates/ # Static assets - copied during install
122
- │ ├── pre-commit # Bash wrapper - invokes lib/hooks/pre-commit.js
123
- │ ├── prepare-commit-msg # Bash wrapper - invokes lib/hooks/prepare-commit-msg.js
124
- │ ├── check-version.sh # Version check - auto-update prompt
125
- │ ├── CLAUDE_PRE_COMMIT.md # Analysis criteria - evaluation guidelines
126
- │ ├── CLAUDE_ANALYSIS_PROMPT.md # Analysis prompt - code review template
127
- │ ├── CLAUDE_RESOLUTION_PROMPT.md # Resolution prompt - structured JSON output for judge + manual use
128
- │ ├── ANALYZE_DIFF.md # PR analysis - diff review template
129
- │ ├── ANALYZE_PR.md # PR analysis - GitHub PR review template with category injection
130
- │ ├── GENERATE_CHANGELOG.md # CHANGELOG generation - commit analysis template (v2.12.0)
131
- │ ├── DIFF_ANALYSIS_ORCHESTRATION_PROMPT.md # Orchestration prompt - Opus batch grouping (v2.20.0)
132
- │ ├── HELP_QUERY.md # AI help - question answering with NEED_MORE_CONTEXT protocol (v2.18.0)
133
- │ ├── HELP_REPORT_ISSUE.md # Report issue - question generation from templates (v2.18.0)
134
- │ ├── HELP_COMPOSE_ISSUE.md # Report issue - issue body composition from answers (v2.18.0)
135
- │ ├── config.example.json # Config example - minimal setup
136
- │ ├── config.advanced.example.json # Config advanced - all options documented
137
- │ ├── settings.local.example.json # Local settings - token storage template
138
- │ └── CUSTOMIZATION_GUIDE.md # Customization - preset creation guide
139
- └── test/ # Tests - Jest with ES6 modules
140
- └── unit/
141
- └── *.test.js
142
- ```
143
-
144
- ### Execution Architecture
145
-
146
- **1. Git Hooks (bash wrappers → Node.js)**
147
-
148
- ```
149
- .git/hooks/pre-commit (bash)
150
-
151
- node lib/hooks/pre-commit.js
152
- ↓ (reads config + preset)
153
- lib/config.js → merges: defaults < user config < preset config
154
- ↓ (gets staged files)
155
- lib/utils/git-operations.js → getStagedFiles()
156
- ↓ (filters by preset extensions + size)
157
- lib/utils/file-operations.js → filterFiles()
158
- ↓ (builds file data + runs analysis)
159
- lib/utils/analysis-engine.js → buildFilesData(), runAnalysis()
160
- ↓ (2-tier routing: 1-2 files→sequential, 3+→Opus orchestration)
161
- lib/utils/diff-analysis-orchestrator.js → orchestrateBatches() [if N ≥ 3]
162
- lib/utils/claude-client.js → analyzeCode(prompt, { model }) per batch [parallel]
163
- ↓ (displays results)
164
- lib/utils/analysis-engine.js → displayResults()
165
- ↓ (if blocking issues found)
166
- lib/utils/resolution-prompt.js → generates claude_resolution_prompt.md
167
-
168
- exit 1 (blocks commit) or exit 0 (allows commit)
169
- ```
170
-
171
- **2. Configuration System (priorities)**
172
-
173
- ```
174
- defaults (lib/config.js)
175
- ↓ overridden by
176
- user config (.claude/config.json)
177
- ↓ overridden by
178
- preset config (.claude/presets/{name}/config.json) ← HIGHEST PRIORITY
179
- ```
180
-
181
- **Rationale**: User configures general preferences, preset provides tech-stack-specific overrides.
182
-
183
- **Team-wide remote config** ([`mscope-S-L/git-hooks-config`](https://github.com/mscope-S-L/git-hooks-config)):
184
-
185
- - `labels.json` — PR label rules (fetched by `remote-config.js`, consumed by `label-resolver.js`)
186
- - `formatters.json` — preset-to-tools mapping for linting/formatting (fetched by `remote-config.js`, consumed by `linter-runner.js`)
187
- - `permissions.json` — role-based authorization (fetched directly by `authorization.js`, fail-closed)
188
- - Changes take effect immediately across all governed repos — no tool update needed
189
-
190
- **Config format (v2.8.0):**
191
-
192
- ```json
193
- {
194
- "version": "2.8.0",
195
- "preset": "backend",
196
- "overrides": {
197
- "github": {
198
- "pr": {
199
- "defaultBase": "develop",
200
- "reviewers": ["user"],
201
- "autoPush": true, // Auto-push unpublished branches (v2.11.0)
202
- "pushConfirm": true, // Prompt before push (v2.11.0)
203
- "showCommits": true, // Show commit preview (v2.11.0)
204
- "verifyRemote": true // Verify remote exists (v2.11.0)
205
- }
206
- }
207
- }
208
- }
209
- ```
210
-
211
- **Hardcoded defaults (v2.8.0+):**
212
-
213
- | Parameter | Value | Notes |
214
- | ---------------------- | ------- | --------------------------------------------------- |
215
- | Max file size | 1MB | Files larger are skipped |
216
- | Max files per commit | 30 | Excess files trigger warning |
217
- | Orchestrator threshold | 3 files | Commits with ≥3 files use Opus orchestration |
218
- | Orchestrator model | opus | Internal constant — not user-configurable |
219
- | Orchestrator timeout | 60s | Lightweight call (file overview only) |
220
- | Analysis timeout | 300s | Per-batch timeout |
221
- | Commit msg timeout | 300s | Message generation timeout |
222
- | PR metadata timeout | 180s | Engine default (reads config) |
223
- | Judge model | sonnet | Default, configurable via `config.judge.model` |
224
- | Judge timeout | 120s | Per-judge call timeout |
225
- | PR analysis model | sonnet | Default, configurable via `config.prAnalysis.model` |
226
- | PR analysis timeout | 300s | Per-analysis Claude call |
227
- | Linting enabled | true | Runs linters before Claude analysis |
228
- | Linting auto-fix | true | Auto-fix and re-stage files |
229
- | Linting fail on error | true | Block commit on linting errors |
230
- | Linting fail on warn | false | Do not block on warnings |
231
- | Linting timeout | 30s | Per-linter timeout |
232
-
233
- **Judge behavior (v2.20.0):**
234
-
235
- - Enabled by default (`config.judge?.enabled !== false`)
236
- - Runs on **all issues** (any severity), not just blockers
237
- - **Any unresolved issue blocks the commit** — no issue passes without judge approval
238
- - Judge failure (timeout, JSON parse error, module load) → user warned → commit blocked
239
- - No retries — failed fixes stay as unresolved issues
240
- - When disabled (`config.judge.enabled: false`), falls back to original quality gate (blocks on CRITICAL/BLOCKER only)
241
- - Resolution prompt file is only generated if issues remain after the judge
242
-
243
- **3. Presets System**
244
-
245
- | Preset | Extensions | Stack | Key Verifications |
246
- | ----------- | -------------------------------------------------------------------------- | ------------------------ | --------------------------------------------------- |
247
- | `backend` | `.java`, `.xml`, `.yml`, `.yaml` | Spring Boot + SQL Server | REST API, JPA, OWASP Top 10, SQL injection |
248
- | `frontend` | `.js`, `.jsx`, `.ts`, `.tsx`, `.css`, `.scss`, `.html` | React + Material-UI | Hooks, XSS, a11y, performance |
249
- | `fullstack` | backend + frontend | Spring Boot + React | **API contract consistency** (priority) |
250
- | `database` | `.sql` | SQL Server | SQL injection, UPDATE/DELETE without WHERE, indexes |
251
- | `ai` | `.js`, `.json`, `.md`, `.sh` | Node.js + Claude API | Prompts, API key security, cross-platform |
252
- | `default` | `.js`, `.sh`, `.py`, `.rb`, `.pl`, `.sql`, `.yaml`, `.json`, `.xml`, `.md` | Multiple | Quality and security fundamentals |
253
-
254
- **4. Analysis Routing (v2.20.0)**
255
-
256
- Three-tier strategy based on file count:
257
-
258
- | Files | Strategy |
259
- | ------ | ----------------------------------------------------------------------------------------------------- |
260
- | 1–2 | Sequential — single Claude call |
261
- | **3+** | **Intelligent orchestration** — Opus orchestrator, semantic grouping, per-batch model, shared context |
262
-
263
- **Orchestration flow (3+ files):**
264
-
265
- ```
266
- staged files (N ≥ 3)
267
-
268
- [ORCHESTRATOR — Opus, 60s timeout]
269
- Input: file overview table + detected cross-file deps (JS/TS/Java/Python regex)
270
- Output: { batches: [{ filePaths, rationale, model }] }
271
-
272
- For each batch:
273
- → buildAnalysisPrompt(batchFiles + commonContext + batchRationale)
274
- → analyzeCode(prompt, { model: batch.model }) ← per-batch model
275
- [all batches run in parallel via Promise.all]
276
-
277
- consolidateResults()
278
- ```
279
-
280
- - **Orchestration model**: `opus` — hardcoded internal constant, not user-configurable
281
- - **Threshold**: `ORCHESTRATOR_THRESHOLD = 3` — internal constant in `analysis-engine.js`
282
- - **Fallback**: any orchestration failure → one-file-per-batch with haiku
283
- - **Common context**: each batch prompt prefixed with commit overview, dep graph, and batch rationale
284
-
285
- ### Key Module Exports
286
-
287
- **Command Modules (`lib/commands/`):**
288
-
289
- | Module | Purpose | Key Exports |
290
- | ----------------------- | --------------------------- | -------------------------------------------------------------------------------------------------- |
291
- | `helpers.js` | Shared CLI utilities | `colors`, `error()`, `success()`, `info()`, `checkGitRepo()`, `getGitHooksPath()`, `Entertainment` |
292
- | `install.js` | Installation logic | `runInstall()`, `extractLegacySettings()` |
293
- | `hooks.js` | Hook management | `runEnable()`, `runDisable()`, `runStatus()`, `runUninstall()` |
294
- | `analyze.js` | Interactive code analysis | `runAnalyze()` |
295
- | `analyze-diff.js` | Diff analysis | `runAnalyzeDiff()` |
296
- | `analyze-pr.js` | PR analysis from URL | `runAnalyzePr()`, `normalizeCategory()` |
297
- | `check-coupling.js` | Coupling detection | `runCheckCoupling()` |
298
- | `shadow.js` | Shadow branch management | `runShadow()` — routes to analyze/reset/sync subcommands (v2.24.0) |
299
- | `create-release.js` | Release candidate creation | `runCreateRelease()` — RC branch from develop, version bump, push, shadow deploy (v2.27.0) |
300
- | `revert-feature.js` | Feature revert in RC | `runRevertFeature()` — find by task-id, coupling check, revert, push, shadow sync (v2.28.0) |
301
- | `close-release.js` | Release candidate closure | `runCloseRelease()` — soft-reset, single commit, force-push, PR to main (v2.29.0) |
302
- | `create-pr.js` | PR creation | `runCreatePr()`, `detectMergeStrategy()` (private) |
303
- | `bump-version.js` | Version management | `runBumpVersion()` |
304
- | `generate-changelog.js` | CHANGELOG generation | `runGenerateChangelog()` |
305
- | `setup-github.js` | Token setup (GitHub) | `runSetupGitHub()` |
306
- | `setup-linear.js` | Token setup (Linear) | `runSetupLinear()` |
307
- | `presets.js` | Preset management | `runShowPresets()`, `runSetPreset()`, `runCurrentPreset()` |
308
- | `update.js` | Self-update | `runUpdate()` |
309
- | `migrate-config.js` | Config migration | `runMigrateConfig()` |
310
- | `debug.js` | Debug toggle | `runSetDebug()` |
311
- | `telemetry-cmd.js` | Telemetry commands | `runShowTelemetry()`, `runClearTelemetry()` |
312
- | `diff-batch-info.js` | Batch info display | `runDiffBatchInfo()` |
313
- | `lint.js` | Lint command | `runLint()`, `resolvePaths()` |
314
- | `help.js` | Help, AI help, report-issue | `runShowHelp()`, `showStaticHelp()`, `runShowVersion()` |
315
-
316
- **Utility Modules (`lib/utils/`):**
317
-
318
- | Module | Purpose | Key Exports |
319
- | ------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
320
- | `lib/cli-metadata.js` | Command registry | `commands`, `buildCommandMap()`, `generateCompletionData()`, `PRESET_NAMES`, `HOOK_NAMES`, `BUMP_TYPES` |
321
- | `lib/config.js` | Config system | `getConfig()` |
322
- | `tool-runner.js` | Generic tool executor | `isToolAvailable()`, `filterFilesByTool()`, `runTool()`, `runToolFix()`, `runToolWithAutoFix()`, `displayToolResult()` (v2.34.0) |
323
- | `linter-runner.js` | Linter orchestration | `runLinters()`, `displayLintResults()`, `checkLinterAvailability()`, `getLinterToolsForPreset()`, `LINTER_TOOLS`, `PRESET_LINTERS`, `parsePrettierOutput()`, `parseEslintOutput()`, `parseSpotlessOutput()`, `parseSqlfluffOutput()`, `filesToSpotlessRegex()` (v2.34.0) |
324
- | `analysis-engine.js` | Shared analysis logic | `buildFileData()`, `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `hasBlockingIssues()`, `hasAnyIssues()`, `displayResults()`, `displayIssueSummary()` (v2.13.0+) |
325
- | `diff-analysis-orchestrator.js` | Intelligent batch orchestration | `orchestrateBatches()`, `buildFileOverview()`, `detectDependencies()` (v2.20.0) |
326
- | `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `executeClaudeWithRetry()`, `extractJSON()` — spawn, retry, model override |
327
- | `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` — accepts `commonContext`, `batchRationale` |
328
- | `git-operations.js` | Git abstractions | `getStagedFiles()`, `getUnstagedFiles()`, `getAllTrackedFiles()`, `getDiff()`, `getRepoRoot()`, `getBranchPushStatus()`, `pushBranch()`, `createCommit()`, `fetchRemote()`, `branchExists()`, `getRemoteBranches()`, `resolveBaseBranch()`, `getChangedFilesBetweenRefs()`, `getDiffBetweenRefs()`, `getCommitsBetweenRefs()`, `checkoutBranch()`, `mergeBranch()`, `resetBranch()`, `forcePush()`, `deleteRemoteBranch()`, `getDivergence()`, `readFileFromRef()`, `getLatestTag()`, `isWorkingDirectoryClean()`, `getActiveBranch()`, `getCommitFiles()` |
329
- | `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
330
- | `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
331
- | `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
332
- | `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
333
- | `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
334
- | `coupling-detector.js` | Coupling algorithm | `buildFileIndex()`, `getSharedFiles()`, `detectCouplingGroups()` — Union-Find transitive grouping (v2.23.0) |
335
- | `github-api.js` | Octokit integration | `createPullRequest()`, `fetchPullRequest()`, `fetchPullRequestFiles()`, `createPullRequestReview()`, `parseGitHubPRUrl()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()`, `listOpenPullRequests()`, `listRepoTeams()`, `listTeamMembers()`, `getAuthenticatedUser()`, `checkOrgMembership()`, `getCollaboratorPermission()` |
336
- | `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` — config-based reviewer fallback |
337
- | `reviewer-selector.js` | Team-based reviewer selection | `selectReviewers()` — resolve team members via GitHub Teams API, exclude PR author, config fallback (v2.32.0) |
338
- | `authorization.js` | Role-based authorization | `authorizeCommand()`, `requiresAuthorization()`, `getRequiredRole()`, `AuthorizationError`, `PROTECTED_COMMANDS` — fail-closed guard for workflow commands (v2.23.0) |
339
- | `remote-config.js` | Remote config fetcher | `fetchRemoteConfig(fileName)`, `CONFIG_REPO_OWNER`, `CONFIG_REPO_NAME`, `_clearCache()` — cached JSON from `git-hooks-config` repo, graceful degradation (v2.31.0) |
340
- | `label-resolver.js` | Label resolution | `resolveLabels(context)` — 5-rule engine: preset, size, quality, strategy, defaults; remote config priority with local fallback (v2.31.0) |
341
- | `token-store.js` | Token persistence | `loadToken()`, `saveToken()`, `hasToken()`, `loadLocalSettings()` |
342
- | `linear-connector.js` | Linear integration | `loadLinearToken()`, `testConnection()`, `fetchTicket()`, `extractLinearTicketFromTitle()`, `parseLinearIdentifier()`, `LinearConnectorError` |
343
- | `pr-statistics.js` | PR statistics | `recordPRAnalysis()` — write-only JSONL at `.claude/statistics/pr/stats.jsonl` |
344
- | `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
345
- | `task-id.js` | Task ID extraction | `getOrPromptTaskId()`, `formatWithTaskId()` |
346
- | `logger.js` | Logging system | `info()`, `warning()`, `error()`, `debug()` |
347
- | `judge.js` | Auto-fix judge | `judgeAndFix()`, `applyFix()` — LLM verdict + search/replace fixes (v2.20.0) |
348
- | `resolution-prompt.js` | Issue resolution | `generateResolutionPrompt()` |
349
- | `interactive-ui.js` | CLI UI components | `showPRPreview()`, `promptConfirmation()`, `promptMenu()`, `promptToggleList()`, `promptEditField()`, `promptUserConfirmation()` |
350
- | `telemetry.js` | Local telemetry | `recordEvent()`, `displayStatistics()` |
351
-
352
- ### Design Patterns
353
-
354
- 1. **Command Registry**: `lib/cli-metadata.js` - single source of truth for CLI commands, flags, and descriptions. When adding CLI commands, add an entry to `lib/cli-metadata.js` — routing, completions, and help derive from it.
355
- 2. **Command Pattern**: `lib/commands/*.js` - each CLI command is a self-contained module
356
- 3. **Factory Pattern**: `preset-loader.js` loads configurations dynamically per tech-stack
357
- 4. **Template Method**: `prompt-builder.js` builds prompts from markdown templates
358
- 5. **Strategy Pattern**: `analysis-engine.js` selects between sequential (1–2 files) or orchestrated (3+ files) analysis based on file count threshold
359
- 6. **Orchestrator Pattern**: `diff-analysis-orchestrator.js` — Opus decides semantic grouping and model assignment; workers (analyzeCode per batch) execute in parallel
360
- 7. **Adapter Pattern**: `git-operations.js` abstracts git commands into JS functions
361
- 8. **Singleton Pattern**: `config.js` loads configuration once per execution
362
- 9. **Guard Pattern**: `authorization.js` — fail-closed gate in `bin/claude-hooks` before command dispatch; static `PROTECTED_COMMANDS` set avoids API calls for unprotected commands; permissions sourced from `mscope-S-L/git-hooks-config/permissions.json`
363
- 10. **Remote Config Pattern**: `remote-config.js` — fetches JSON from `mscope-S-L/git-hooks-config`, caches per-process (including nulls), graceful degradation (warn + return null); `label-resolver.js` and `linter-runner.js` — callers fetch remote config and decide fallback (`labels.json` for PR labels, `formatters.json` for preset-to-tools mapping)
364
- 11. **Pipeline Pattern**: `tool-runner.js` + `linter-runner.js` — generic tool execution infrastructure; formatters (Prettier) and linters (ESLint, Spotless, sqlfluff) share the same resolve → spawn → parse → fix → re-stage pipeline. Tool definitions are data objects, not classes. Preset-to-tools mapping fetched from `mscope-S-L/git-hooks-config/formatters.json` (remote config priority, local fallback).
365
-
366
- ### Key Data Flows
367
-
368
- **Flow 0: Pre-commit linting (v2.34.0)**
369
-
370
- ```
371
- git commit
372
- → hook reads staged files
373
- → filters by preset extensions + size
374
-
375
- → LINTING STEP (fast, deterministic)
376
- → getLinterToolsForPreset(presetName):
377
- 1. fetchRemoteConfig('formatters.json') → remote presetTools mapping
378
- 2. fallback to local PRESET_LINTERS if remote unavailable
379
- → for each tool (formatters first, then linters):
380
- isToolAvailable() → not found? warn + install hint → skip
381
- filterFilesByTool() → matching files
382
- runToolWithAutoFix() → check → auto-fix → re-stage → re-check
383
- → unfixable issues forwarded to judge
384
-
385
- → continues to Claude analysis
386
- ```
387
-
388
- **Flow 1: Pre-commit with blocking**
389
-
390
- ```
391
- git commit
392
- → hook reads staged files
393
- → filters by preset extensions
394
- → [linting step — see Flow 0]
395
- → builds prompt with diff
396
- → Claude analyzes → detects issues
397
- → judge evaluates ALL issues (any severity):
398
- → TRUE_ISSUE: applies search/replace fix + git add
399
- → FALSE_POSITIVE: dismissed
400
- → if ALL resolved: exit 0 → COMMIT PROCEEDS
401
- → if ANY unresolved: generates resolution prompt → exit 1 → COMMIT BLOCKED
402
- → if judge fails (timeout, parse error): user warned → exit 1 → COMMIT BLOCKED
403
- → judge disabled (config.judge.enabled: false):
404
- → falls back to original quality gate (blocks on CRITICAL/BLOCKER only)
405
- ```
406
-
407
- **Flow 1.5: Interactive analysis command (v2.13.0)**
408
-
409
- ```
410
- claude-hooks analyze
411
- → runs outside git hook context (stdin works)
412
- → getStagedFiles() → buildFilesData() → runAnalysis()
413
- → displayIssueSummary() → shows all severity levels (or "No issues found")
414
- → promptUserConfirmation() → interactive menu:
415
- → [y] Continue - executes createCommit('auto', { noVerify: true })
416
- → [n] Abort - generates resolution prompt
417
- → [v] View - shows detailed issues → returns to menu
418
- → on continue: git commit -m "auto" --no-verify
419
- → prepare-commit-msg hook generates message via Claude
420
- → commit created with auto-generated message
421
- ```
422
-
423
- **Flow 2: Commit with automatic message**
424
-
425
- ```
426
- git commit -m "auto"
427
- → hook extracts task-id from branch (feature/IX-123-add-auth)
428
- → Claude generates message → "[IX-123] feat: add user authentication"
429
- → writes to .git/COMMIT_EDITMSG
430
- → commit proceeds with generated message
431
- ```
432
-
433
- **Flow 3: Analyze diff (v2.14.0)**
434
-
435
- ```
436
- claude-hooks analyze-diff develop
437
- → lib/commands/analyze-diff.js (thin wrapper)
438
- → analyzeBranchForPR() (pr-metadata-engine.js)
439
- → fetchRemote() → resolveBaseBranch() (suggests similar branches if not found)
440
- → getChangedFilesBetweenRefs() → getCommitsBetweenRefs()
441
- → buildDiffPayload() → tiered reduction (context → proportional → stat-only)
442
- → executeClaudeWithRetry() → parses PRMetadata
443
- → formats metadata to console
444
- → saves to .claude/out/pr-analysis.json
445
- ```
446
-
447
- **Flow 3.5: Analyze PR from GitHub URL**
448
-
449
- ```
450
- claude-hooks analyze-pr https://github.com/owner/repo/pull/123
451
- → parseGitHubPRUrl() → { owner, repo, number }
452
- → fetchPullRequest() + fetchPullRequestFiles() via Octokit
453
- → extractLinearTicketFromTitle() → fetchTicket() (optional enrichment)
454
- → resolvePreset(): CLI flag → PR labels → ticket labels → auto-detect from extensions → 'default'
455
- → loadPrompt('ANALYZE_PR.md') with preset guidelines + category injection
456
- → executeClaudeWithRetry() → extractJSON() → normalizeCategory() with fuzzy aliases
457
- → interactive comment workflow: post all / select / skip
458
- → createPullRequestReview() with inline comments + review body
459
- → recordPRAnalysis() → .claude/statistics/pr/stats.jsonl
460
- ```
461
-
462
- **Flow 4: PR creation (with auto-push v2.11.0, engine v2.14.0, merge strategy v2.25.0, label resolver v2.31.0, team reviewers v2.32.0)**
463
-
464
- ```
465
- claude-hooks create-pr develop
466
- → checks branch push status (unpublished/unpushed commits)
467
- → shows commit preview → prompts for confirmation
468
- → pushes branch to remote (if needed)
469
- → selectReviewers({ org, teamSlug, prAuthor, configReviewers }):
470
- 1. listTeamMembers(org, teamSlug) → resolve team (default: 'automation')
471
- 2. filter out prAuthor
472
- 3. if team empty or API fails → fall back to config.github.pr.reviewers
473
- → detectMergeStrategy(sourceBranch, targetBranch):
474
- feature/* → squash | release-fix/* → squash
475
- release-candidate/* → merge-commit | hotfix/* → merge-commit
476
- any → main → merge-commit | unknown → user prompted to select
477
- → resolveLabels({ preset, fileCount, mergeStrategy, analysisResult, localLabelRules }):
478
- 1. preset labels (remote presetLabels or local fallback)
479
- 2. size labels: size:S (<10) | size:M (10-50) | size:L (50-100) | size:XL (>100)
480
- 3. quality labels: breaking-change, security, performance (from analysisResult)
481
- 4. strategy label: merge-strategy:squash or merge-strategy:merge-commit
482
- 5. default labels (from remote config only, e.g. needs-review)
483
- → prepends body reminder for merge-commit
484
- → analyzeBranchForPR() (pr-metadata-engine.js) → generates metadata
485
- → interactive preview + strategy info line → user confirms
486
- → Octokit creates PR on GitHub
487
- ```
488
-
489
- **Flow 5: Version bump with per-file editing (v2.16.0)**
490
-
491
- ```
492
- claude-hooks bump-version patch --interactive
493
- → validatePrerequisites() → clean working directory, valid branch, remote
494
- → discoverVersionFiles() → recursive scan (max 3 levels)
495
- → displayDiscoveryTable() → shows all discovered files with versions
496
- → promptFileSelection() → interactive menu:
497
- → [a] Update all files - all files get same newVersion
498
- → [s] Select files - toggle list to pick subset
499
- → [e] Edit per file - promptEditField() per file:
500
- → user enters custom version per file (Enter keeps calculated version)
501
- → validateVersionFormat() → rejects invalid entries
502
- → stores file.targetVersion on each descriptor
503
- → [c] Cancel
504
- → incrementVersion() / modifySuffix() → calculates newVersion (used for tag + commit)
505
- → updateVersionFiles() → uses file.targetVersion || newVersion per file
506
- → createTag(newVersion) → tag reflects primary release version
507
- → commit message uses newVersion
508
- ```
509
-
510
- **Per-file version design**: `VersionFileDescriptor` gains an optional `targetVersion` property at runtime, set only by option 'e'. When present, `updateVersionFiles()` writes `targetVersion` instead of the global `newVersion`. The tag and commit message always use the calculated `newVersion`.
511
-
512
- **Flow 6: Release candidate creation (v2.27.0)**
513
-
514
- ```
515
- claude-hooks create-release minor
516
- → parseArguments() → bumpType, noShadow, dryRun, skipPush, updateChangelog
517
- → validatePreconditions():
518
- checkGitRepo()
519
- isWorkingDirectoryClean() → abort if dirty
520
- getCurrentBranch() === 'develop' → abort if not on develop
521
- fetchRemote() → warn on failure, continue
522
- getDivergence('develop','origin/develop') → behind>0 → abort + git pull advice
523
- getDivergence('origin/develop','origin/main') → behind>0 → abort + back-merge advice
524
- getRemoteBranches().filter('release-candidate/*') → any found → abort + close-release advice
525
- verifyRemoteExists()
526
- → discoverVersionFiles()
527
- mismatch → abort with table + fix steps (non-interactive, cannot auto-resolve)
528
- → incrementVersion(currentVersion, bumpType) [no suffix in branch name]
529
- → rcBranch = 'release-candidate/V{nextVersion}'
530
- → [--dry-run] → preview table → return
531
- → promptConfirmation()
532
- → checkoutBranch(rcBranch, { create: true, startPoint: 'develop' })
533
- → updateVersionFiles()
534
- → [--update-changelog] → generateChangelogEntry() + updateChangelogFile()
535
- → stageFiles() + createCommit('chore(version): bump to {nextVersion}', { noVerify: true })
536
- → tagExists()? → warn + skip : createTag()
537
- → [unless --skip-push] pushBranch(rcBranch, { setUpstream: true })
538
- → [unless --no-shadow and not --skip-push] runShadow(['sync', rcBranch])
539
- shadow error → warn, don't abort
540
- → checkoutBranch(rcBranch) [safety: ensure we land on RC after shadow sync]
541
- → display summary (branch, version, tag, push status, shadow status)
542
- ```
543
-
544
- **create-release design decisions:**
545
-
546
- - Mismatch aborts non-interactively (automation context — ambiguity must be fixed by human)
547
- - Tag creation mirrors `bump-version`: created on RC branch; if tag already exists → warn + skip (no interactive prompt)
548
- - Shadow sync delegates to `runShadow(['sync', rcBranch])` (public API, reuses conflict-resolution logic from #93)
549
- - `--skip-push` implies no shadow (cannot sync unpushed branch)
550
- - Branch naming: `release-candidate/V{semver}` — capital V per team convention, no suffix in branch name
551
-
552
- **Flow 7: Feature revert in release-candidate (v2.28.0)**
553
-
554
- ```
555
- claude-hooks revert-feature AUT-3179
556
- → parseArgs() → taskId, updateShadow, dryRun
557
- → getCurrentBranch() — must start with 'release-candidate/'
558
- → isWorkingDirectoryClean() → abort if dirty
559
- → git log --grep="AUT-3179" --fixed-strings -i origin/main..HEAD
560
- 0 matches → abort
561
- 1 match → show commit details (hash, message, author, date, files)
562
- 2+ matches → promptMenu() → user selects one
563
- → getCommitFiles(targetHash) → target file set
564
- → _checkCoupling(): for each other RC commit (skip target, skip Revert commits):
565
- getCommitFiles(hash) → intersect with targetFiles → collect { taskId, sharedFiles }
566
- → [--dry-run] → preview table → return
567
- → _displayCommitDetails() + coupling warnings (informational, before single confirmation)
568
- → promptConfirmation('Proceed with revert?', false)
569
- → git revert --no-edit <hash> → stdio: inherit (shows git output)
570
- → git rev-parse HEAD → revertHash
571
- → pushBranch(rcBranch)
572
- → _appendRevertLog(): append { taskId, originalHash, revertHash, rcBranch, timestamp }
573
- to .claude/revert-log.json (array, consumed by back-merge #96)
574
- → [--update-shadow] runShadow(['sync', rcBranch]) → warn on failure, don't abort
575
- → display summary + revert-the-revert reminder:
576
- git revert <revertHash> # Restores AUT-3179 for next sprint
577
- ```
578
-
579
- **revert-feature design decisions:**
580
-
581
- - Coupling check is informational only — warnings shown before single confirmation (no second prompt)
582
- - `Revert "..."` commits are skipped in coupling scan (they intentionally share files)
583
- - `revert-log.json` schema: `[{ taskId, originalHash, revertHash, rcBranch, timestamp }]` — array to support multiple reverts; consumed by back-merge (#96)
584
- - `git revert` uses `stdio: 'inherit'` so the user sees git output (including conflict messages)
585
- - Push failure: revert commit is kept locally; user shown manual `git push` command
586
- - Shadow sync error: warns but does not abort (non-fatal, same pattern as `create-release`)
587
-
588
- **Flow 8: Release candidate closure (v2.29.0)**
589
-
590
- ```
591
- claude-hooks close-release "cashflow + auth"
592
- → _parseArgs() → description, autoDescribe, dryRun, noPr
593
- → detect RC branch: current branch or getActiveBranch('release-candidate') → offer checkout
594
- → _extractVersion(rcBranch) → semver string
595
- → isWorkingDirectoryClean() → abort if dirty
596
- → fetchRemote() → warn on failure
597
- → getDivergence(rc, origin/rc) → behind>0 → abort + git pull advice
598
- → _collectFeatureList(): getCommitsBetweenRefs('origin/main', 'HEAD', { format: '%s' })
599
- → _resolveDescription():
600
- CLI arg → use directly
601
- --auto-describe → executeClaudeWithRetry(haiku, 60s) → 1-line phrase
602
- default → show list + promptEditField() → TL accepts or overrides
603
- → [--dry-run] → preview table → return
604
- → promptConfirmation()
605
- → resetBranch('origin/main', { mode: 'soft' }) — all RC commits staged
606
- → execSync('git commit --no-verify -F -', { input: fullMessage }) — subject + body
607
- → forcePush(rcBranch, { lease: true })
608
- → [unless --no-pr] validateToken() + createPullRequest(head→main, label: merge-strategy:merge-commit)
609
- → display summary with PR URL + merge-commit reminder
610
- ```
611
-
612
- **close-release design decisions:**
613
-
614
- - `git commit --no-verify -F -` (stdin) is used instead of `createCommit()` to support multi-line body; `execSync` passes the full message via `input` option (no shell injection risk — fixed string)
615
- - `process.exit()` calls are placed _outside_ try/catch blocks so that mock-based tests can observe them; the catch only handles git errors
616
- - Description priority: CLI arg > `--auto-describe` (Claude haiku, 60s timeout) > `promptEditField` (shows list, TL edits)
617
- - Auto-describe fallback: if Claude fails or feature list is empty, falls back to interactive prompt without aborting
618
- - `close-release` was already in `PROTECTED_COMMANDS` — authorization.js unchanged
619
- - Force-push failure: exits 1 and shows manual push command (no retry — destructive op)
620
- - PR creation failure: warns + shows `gh pr create` alternative (non-fatal; commit and push already done)
621
- - `_extractVersion`, `_collectFeatureList`, `_resolveDescription` are exported for unit testing
622
-
623
- ## Code Conventions
624
-
625
- ### General Style
626
-
627
- - **Format**: Prettier (config in `.prettierrc.json`)
628
- - **Linting**: ESLint 8.57.0 (config in `.eslintrc.json`)
629
- - **Indentation**: 4 spaces
630
- - **Quotes**: Single quotes (strings), double quotes (HTML/imports)
631
- - **Semicolons**: Required
632
-
633
- ### Naming
634
-
635
- - **Files**: `kebab-case.js` (e.g., `claude-client.js`, `git-operations.js`)
636
- - **Functions**: `camelCase()` (e.g., `getStagedFiles()`, `buildAnalysisPrompt()`)
637
- - **Constants**: `UPPER_SNAKE_CASE` (e.g., `MAX_FILE_SIZE`, `ALLOWED_EXTENSIONS`)
638
- - **Classes**: Not applicable (no classes, only exported functions)
639
- - **Private variables**: `_` prefix (e.g., `_internalHelper()`)
640
-
641
- ### Exports
642
-
643
- **Preferred pattern - Named exports:**
644
-
645
- ```javascript
646
- // lib/utils/example.js
647
- export function doSomething() { ... }
648
- export function doOtherThing() { ... }
649
-
650
- // consumer
651
- import { doSomething, doOtherThing } from './utils/example.js';
652
- ```
653
-
654
- **Avoid default exports** (except in `bin/claude-hooks` per CLI convention).
655
-
656
- ### Error Handling
657
-
658
- **Standard pattern:**
659
-
660
- ```javascript
661
- import { error } from './utils/logger.js';
662
-
663
- try {
664
- const result = dangerousOperation();
665
- return result;
666
- } catch (err) {
667
- error(`Failed to perform operation: ${err.message}`);
668
- process.exit(1); // In hooks and CLI commands
669
- // or throw err; // In library functions
670
- }
671
- ```
672
-
673
- **Specific errors:**
674
-
675
- - Git operations → `execSync()` with try-catch, log and exit
676
- - Claude CLI → retry logic in `claude-client.js`, configurable timeout
677
- - GitHub API → catch `@octokit/request-error`, user-friendly formatting
678
-
679
- ### Logging
680
-
681
- ```javascript
682
- import { info, warning, error, debug } from './utils/logger.js';
683
-
684
- info('✅ Operation completed'); // User-facing messages
685
- warning('⚠️ Non-blocking issue detected'); // Warnings
686
- error('❌ Critical failure'); // Errors
687
- debug('Detailed diagnostic info'); // Only shown when debug=true
688
- ```
689
-
690
- **Debug mode**: Activate with `claude-hooks --debug true` or in `.claude/config.json`.
691
-
692
- ### Async/Await
693
-
694
- **Prefer async/await over callbacks:**
695
-
696
- ```javascript
697
- // ✅ GOOD
698
- async function analyzeCode(prompt) {
699
- const result = await spawnClaude(prompt);
700
- return result;
701
- }
702
-
703
- // ❌ BAD
704
- function analyzeCode(prompt, callback) {
705
- spawnClaude(prompt, callback);
706
- }
707
- ```
708
-
709
- ### Reusable Modules
710
-
711
- All modules in `lib/utils/` must:
712
-
713
- 1. Export pure functions (no global side effects)
714
- 2. Include inline JSDoc documentation
715
- 3. Accept configuration via parameters (no globals)
716
- 4. Return structured results (objects or arrays)
717
- 5. Log with `logger.js` (no direct `console.log`)
718
-
719
- ## Workflow
720
-
721
- ### Branching Strategy
722
-
723
- This repository follows **simplified GitHub Flow**:
724
-
725
- - **Main branch**: `main` (protected)
726
- - **Feature branches**: `feature/issue-description` or `feature/TASK-ID-description`
727
- - **Fix branches**: `fix/issue-description`
728
- - **Hotfix branches**: `hotfix/urgent-description`
729
-
730
- **Rules:**
731
-
732
- 1. All changes require PR to `main`
733
- 2. No direct commits to `main`
734
- 3. PRs require review (auto-merge NOT allowed)
735
-
736
- ### Development Process
737
-
738
- **1. Initial setup (first time)**
739
-
740
- ```bash
741
- git clone https://github.com/mscope-S-L/git-hooks.git
742
- cd git-hooks
743
- npm install
744
- npm link # Install globally as symlink for development
745
- ```
746
-
747
- **2. Create feature branch**
748
-
749
- ```bash
750
- git checkout -b feature/new-functionality
751
- # or with task-id: feature/IX-123-new-functionality
752
- ```
753
-
754
- **3. Iterative development**
755
-
756
- ```bash
757
- # Make changes...
758
- npm run lint # Check linting
759
- npm run test # Run tests
760
- git add .
761
- git commit -m "feat: add new functionality"
762
- # Hooks execute automatically
763
- ```
764
-
765
- **4. Local testing**
766
-
767
- Test changes in a test repository:
768
-
769
- ```bash
770
- # In test repo
771
- cd /path/to/test-repo
772
- claude-hooks install --force --skip-auth # Reinstall from symlink
773
- git commit -m "test" # Test hook
774
- ```
775
-
776
- **5. Create PR**
777
-
778
- ```bash
779
- git push -u origin feature/new-functionality
780
- claude-hooks analyze-diff main # Generate PR metadata
781
- # or directly:
782
- claude-hooks create-pr main # Create PR on GitHub (if MCP configured)
783
- ```
784
-
785
- ### CI/CD
786
-
787
- **Currently**: No automated CI/CD (GitHub Actions pending).
788
-
789
- **Manual verifications before merge:**
790
-
791
- 1. `npm run lint` → passes
792
- 2. `npm run test` → all tests pass
793
- 3. Manual testing in test repo
794
- 4. Peer code review
795
- 5. CHANGELOG.md updated
796
-
797
- ### Versioning
798
-
799
- We follow **Semantic Versioning** (MAJOR.MINOR.PATCH):
800
-
801
- - **MAJOR**: Breaking changes (e.g., 1.x → 2.0 with ES6 migration)
802
- - **MINOR**: New features without breaking changes (e.g., 2.2 → 2.3 with presets)
803
- - **PATCH**: Bug fixes (e.g., 2.6.0 → 2.6.1 with spawn ENOENT fix)
804
-
805
- **Update version:**
806
-
807
- ```bash
808
- npm version patch # 2.6.1 → 2.6.2
809
- npm version minor # 2.6.1 → 2.7.0
810
- npm version major # 2.6.1 → 3.0.0
811
- ```
812
-
813
- **Publish to NPM:**
814
-
815
- ```bash
816
- npm publish
817
- ```
818
-
819
- ### Conventional Commits
820
-
821
- Format: `<type>(<scope>): <subject>`
822
-
823
- **Types:**
824
-
825
- - `feat`: New functionality
826
- - `fix`: Bug fix
827
- - `docs`: Documentation only
828
- - `style`: Formatting (no logic change)
829
- - `refactor`: Refactoring without functional change
830
- - `test`: Add or modify tests
831
- - `chore`: Maintenance (deps, config)
832
- - `perf`: Performance improvements
833
-
834
- **Optional scopes:**
835
-
836
- - `hooks`: Changes in pre-commit or prepare-commit-msg
837
- - `presets`: Preset system
838
- - `github`: GitHub integration
839
- - `cli`: Main CLI commands
840
- - `config`: Configuration system
841
- - `windows`: Windows-specific fixes
842
-
843
- **Examples:**
844
-
845
- ```
846
- feat(presets): add database preset for SQL analysis
847
- fix(windows): resolve spawn ENOENT with .cmd files
848
- docs: update CLAUDE.md with architecture details
849
- chore(deps): upgrade @octokit/rest to v21
850
- ```
851
-
852
- ## Release Workflow Guide
853
-
854
- This section provides a practical guide for Tech Leads and Senior developers who use the workflow automation commands. These commands are **protected** — they require `senior` or `tech-lead` role (based on GitHub repo permissions).
855
-
856
- ### Quick Reference — Release Commands
857
-
858
- | Command | When to use | What it does |
859
- | ---------------- | --------------------------- | ------------------------------------------------------------------------ |
860
- | `check-coupling` | Before cutting a release | Scans open PRs for shared files — reveals which features are coupled |
861
- | `create-release` | Tuesday release prep | Creates RC branch from develop, bumps version, pushes, deploys to shadow |
862
- | `shadow` | Throughout QA cycle | Manages the shadow (QA) branch — analyze status, reset, or sync with RC |
863
- | `revert-feature` | During QA, feature fails | Finds and reverts a feature by task ID in the RC, with coupling warnings |
864
- | `close-release` | QA passed, ready for deploy | Squashes RC into one commit, force-pushes, creates PR to main |
865
- | `back-merge` | After production deploy | Tags release, resets shadow, merges main→develop, cleans up RC branch |
866
- | `create-pr` | Any PR creation | Creates GitHub PR with auto-detected merge strategy and labels |
867
- | `bump-version` | Manual version changes | Bumps version files, commits, tags — used outside the release cycle |
868
-
869
- ### Release Lifecycle — Step by Step
870
-
871
- The typical weekly release cycle follows this sequence:
872
-
873
- ```
874
- 1. TUESDAY — Prepare release
875
- claude-hooks check-coupling ← Are any open PRs coupled?
876
- claude-hooks create-release minor ← Create RC from develop, bump version, shadow deploy
877
-
878
- 2. TUESDAY–THURSDAY — QA cycle
879
- claude-hooks shadow sync release-candidate ← Re-sync shadow after fixes
880
- claude-hooks revert-feature AUT-XXXX ← Revert a failing feature if needed
881
- claude-hooks shadow analyze ← Check shadow divergence
882
-
883
- 3. THURSDAY 12:00 — Close release
884
- claude-hooks close-release "description" ← Squash RC, create PR to main
885
-
886
- 4. THURSDAY 13:00 — After deploy
887
- claude-hooks back-merge ← Tag, reset shadow, merge main→develop, cleanup
888
- ```
889
-
890
- ### Command Details
891
-
892
- #### check-coupling
893
-
894
- Scans open PRs targeting a base branch and detects which ones share modified files (are "coupled"). Coupled features must ship together or be pulled together — reverting one may break the other.
895
-
896
- ```bash
897
- claude-hooks check-coupling # PRs targeting develop (default)
898
- claude-hooks check-coupling --base main # PRs targeting main
899
- claude-hooks check-coupling --json # Machine-readable output for CI
900
- ```
901
-
902
- | Flag | Effect |
903
- | ----------------- | ------------------------------------------------------------------------------- |
904
- | `--base <branch>` | Base branch to scan PRs against (default: `develop`) |
905
- | `--json` | Output structured JSON with `coupledGroups`, `independentPRNumbers`, `warnings` |
906
-
907
- **Not protected** — any developer can run this. Uses GitHub API to fetch PR file lists. Transitive coupling is detected (if A shares files with B, and B with C, all three are grouped).
908
-
909
- ---
910
-
911
- #### create-release
912
-
913
- Creates a release-candidate branch from `develop`, bumps version files (package.json, pom.xml, etc.), commits, pushes, and deploys to shadow. Replaces 8 manual steps.
914
-
915
- ```bash
916
- claude-hooks create-release minor # Standard release (most common)
917
- claude-hooks create-release patch # Hotfix-style bump
918
- claude-hooks create-release major # Breaking change release
919
- ```
920
-
921
- | Flag | Effect |
922
- | -------------------- | -------------------------------------------------- |
923
- | `--dry-run` | Preview all planned actions without making changes |
924
- | `--no-shadow` | Skip shadow deployment after push |
925
- | `--skip-push` | Keep everything local (implies no shadow) |
926
- | `--update-changelog` | Generate and include CHANGELOG entry |
927
-
928
- **Preconditions** (all checked automatically):
929
-
930
- - Must be on `develop` branch
931
- - Working directory must be clean
932
- - `develop` must be up-to-date with remote
933
- - `develop` must not be behind `main` (run `back-merge` first if so)
934
- - No existing `release-candidate/*` branch on remote (run `close-release` first)
935
-
936
- **Branch naming**: `release-candidate/V{version}` (capital V, e.g., `release-candidate/V2.31.0`).
937
-
938
- ---
939
-
940
- #### shadow
941
-
942
- Manages the shadow branch — an ephemeral QA environment with its own pipeline. Three subcommands:
943
-
944
- **shadow analyze** — Show divergence status:
945
-
946
- ```bash
947
- claude-hooks shadow analyze
948
- ```
949
-
950
- Shows how shadow compares to `main` and the active RC (commits ahead/behind). Use this to check if shadow needs a sync.
951
-
952
- **shadow reset** — Recreate shadow from main:
953
-
954
- ```bash
955
- claude-hooks shadow reset # Destroys and recreates from main
956
- claude-hooks shadow reset --dry-run # Preview only
957
- ```
958
-
959
- Used at the start/end of a release cycle. Destroys the current shadow branch and creates a fresh copy from main. Prompts for confirmation.
960
-
961
- **shadow sync** — Merge a source branch into shadow:
962
-
963
- ```bash
964
- claude-hooks shadow sync release-candidate # Auto-detect latest RC
965
- claude-hooks shadow sync release-candidate/V2.31.0 # Explicit RC version
966
- claude-hooks shadow sync develop # Test develop in shadow
967
- claude-hooks shadow sync feature/AUT-XXXX # Test a specific feature
968
- claude-hooks shadow sync <source> --dry-run # Preview only
969
- ```
970
-
971
- | Flag | Effect |
972
- | ----------- | ------------------------------ |
973
- | `--dry-run` | Preview sync without executing |
974
-
975
- **Conflict resolution during sync**: version files → accept source; CHANGELOG → keep both (warn TL); other conflicts → abort merge and advise manual resolution.
976
-
977
- ---
978
-
979
- #### revert-feature
980
-
981
- Finds a squash-merged feature commit by its task ID in the current release-candidate, checks for coupling with other RC features, and reverts it.
982
-
983
- ```bash
984
- claude-hooks revert-feature AUT-3179 # Find and revert
985
- claude-hooks revert-feature AUT-3179 --update-shadow # Also re-deploy shadow
986
- claude-hooks revert-feature AUT-3179 --dry-run # Preview only
987
- ```
988
-
989
- | Flag | Effect |
990
- | ----------------- | -------------------------------------------------- |
991
- | `--dry-run` | Show what would be reverted without making changes |
992
- | `--update-shadow` | After reverting, sync shadow with the updated RC |
993
-
994
- **Preconditions**: Must be on a `release-candidate/*` branch. Working directory must be clean.
995
-
996
- **Search behavior**:
997
-
998
- - 0 matches → abort with "No commits found"
999
- - 1 match → show details, confirm with user
1000
- - 2+ matches → interactive menu to select which commit
1001
-
1002
- **Coupling detection**: Before reverting, checks if other RC commits modify the same files. Shows warnings like `"⚠️ [AUT-3200] also modifies EmailService.java"`. This is informational — you decide whether to proceed.
1003
-
1004
- **After revert**: Writes to `.claude/revert-log.json` for `back-merge` follow-up. Shows reminder to revert-the-revert in develop after the release deploys.
1005
-
1006
- ---
1007
-
1008
- #### close-release
1009
-
1010
- Finalizes the active release-candidate: squashes all RC commits into a single clean commit via `git reset --soft`, force-pushes, and creates a PR to main.
1011
-
1012
- ```bash
1013
- claude-hooks close-release # Auto-detect RC, prompt for description
1014
- claude-hooks close-release "cashflow + auth" # Explicit description
1015
- claude-hooks close-release --auto-describe # Claude generates 1-line summary
1016
- claude-hooks close-release --dry-run # Preview only
1017
- claude-hooks close-release --no-pr # Skip PR creation
1018
- ```
1019
-
1020
- | Flag | Effect |
1021
- | ----------------- | --------------------------------------------------------------------------- |
1022
- | `--dry-run` | Preview planned actions (reset, commit message, PR) without executing |
1023
- | `--auto-describe` | Use Claude (haiku, 60s timeout) to generate a description from feature list |
1024
- | `--no-pr` | Perform the reset + commit + force-push but skip GitHub PR creation |
1025
-
1026
- **Description priority**: CLI argument → `--auto-describe` → interactive prompt (shows feature list, TL accepts or edits).
1027
-
1028
- **Commit format**:
1029
-
1030
- ```
1031
- Release v2.31.0: cashflow improvements + auth fixes
1032
-
1033
- Includes:
1034
- - [AUT-3179] feat(cashflow): use Mailjet templates
1035
- - [AUT-3200] fix(cashflow): handle invalid template ID
1036
- ```
1037
-
1038
- **PR creation**: Labels with `merge-strategy:merge-commit` and adds body reminder. This PR **must** be merged with merge-commit (not squash) to preserve history for back-merge.
1039
-
1040
- ---
1041
-
1042
- #### back-merge
1043
-
1044
- Post-deploy synchronization: tags the release on main, resets shadow, merges main into develop (with auto-conflict resolution), and cleans up the RC branch.
1045
-
1046
- ```bash
1047
- claude-hooks back-merge # Full post-deploy (main → develop)
1048
- claude-hooks back-merge --from main --into develop # Explicit source/destination
1049
- claude-hooks back-merge --skip-tag # Already tagged manually
1050
- claude-hooks back-merge --skip-shadow # Skip shadow reset
1051
- claude-hooks back-merge --dry-run # Preview only
1052
- ```
1053
-
1054
- | Flag | Effect |
1055
- | ----------------- | --------------------------------------------- |
1056
- | `--dry-run` | Preview all planned actions without executing |
1057
- | `--from <branch>` | Source branch (default: `main`) |
1058
- | `--into <branch>` | Destination branch (default: `develop`) |
1059
- | `--skip-tag` | Skip tag creation (useful if already tagged) |
1060
- | `--skip-shadow` | Skip shadow reset step |
1061
-
1062
- **Auto-conflict resolution**:
1063
- | Conflict type | Resolution |
1064
- |---|---|
1065
- | Version files (pom.xml, package.json) | Accept source (released version) |
1066
- | CHANGELOG.md | Keep both — destination on top, source below |
1067
- | Other | Abort — TL resolves manually |
1068
-
1069
- **Revert follow-up**: If `.claude/revert-log.json` exists (from `revert-feature`), shows reverted features and offers to revert-the-revert in develop (restores them for next sprint).
1070
-
1071
- **Branch protection note**: `back-merge` pushes directly to `develop`. If branch protection requires PRs, you must temporarily disable it or have bypass permissions.
1072
-
1073
- ---
1074
-
1075
- #### create-pr (merge strategy awareness)
1076
-
1077
- When creating a PR, the merge strategy is auto-detected from branch naming and displayed in the preview:
1078
-
1079
- | PR direction | Strategy | Label |
1080
- | --------------------------------------- | --------------------------------- | ----------------------------- |
1081
- | `feature/*` → `develop` | Squash merge | `merge-strategy:squash` |
1082
- | `release-fix/*` → `release-candidate/*` | Squash merge | `merge-strategy:squash` |
1083
- | `release-candidate/*` → `main` | Merge commit | `merge-strategy:merge-commit` |
1084
- | `hotfix/*` → `main` | Merge commit | `merge-strategy:merge-commit` |
1085
- | Any branch → `main` | Merge commit | `merge-strategy:merge-commit` |
1086
- | Unknown pattern | Warning — user prompted to select | — |
1087
-
1088
- For merge-commit PRs, a reminder is added to the PR body: `"> ⚠️ This PR must be merged with **merge commit** (not squash)"`.
1089
-
1090
- ### Authorization
1091
-
1092
- All workflow commands (except `check-coupling` and `create-pr`) are protected by role-based authorization:
1093
-
1094
- - **Protected commands**: `create-release`, `close-release`, `back-merge`, `shadow`, `revert-feature`, `bump-version`
1095
- - **Role hierarchy**: `developer` < `senior` < `tech-lead`
1096
- - **Role mapping**: GitHub repo permission `push` → developer, `maintain` → senior, `admin` → tech-lead
1097
- - **Permissions source**: `git-hooks-config/permissions.json` repo (controlled by TL/Senior)
1098
- - **Fail-closed**: Any auth error (no token, invalid token, network failure, repo not governed) blocks the command
1099
-
1100
- If blocked: `"⛔ Command 'create-release' requires role 'senior'. Your role: developer. Contact your Tech Lead."`
1101
-
1102
- ## Useful Commands
1103
-
1104
- ### Development
1105
-
1106
- ```bash
1107
- # Installation and setup
1108
- npm install # Install dependencies
1109
- npm link # Install globally as symlink
1110
- npm unlink # Uninstall global symlink
1111
-
1112
- # Testing
1113
- npm test # Run all tests (Jest)
1114
- npm run test:watch # Tests in watch mode
1115
- npm run test:coverage # Coverage report
1116
-
1117
- # Linting and formatting
1118
- npm run lint # Check ESLint
1119
- npm run lint:fix # Auto-fix ESLint issues
1120
- npm run format # Format with Prettier
1121
-
1122
- # Versioning
1123
- npm version patch|minor|major # Bump version
1124
- npm publish # Publish to NPM
1125
- ```
1126
-
1127
- ### Package CLI
1128
-
1129
- ```bash
1130
- # Hook installation in a repo
1131
- claude-hooks install # Interactive installation
1132
- claude-hooks install --force # Reinstall without confirmation
1133
- claude-hooks install --skip-auth # Skip Claude verification
1134
-
1135
- # Hook management
1136
- claude-hooks status # View current status
1137
- claude-hooks enable [hook] # Enable all or specific hook
1138
- claude-hooks disable [hook] # Disable all or specific hook
1139
- claude-hooks uninstall # Completely uninstall
1140
-
1141
- # Presets
1142
- claude-hooks presets # List available presets
1143
- claude-hooks --set-preset backend # Change preset
1144
- claude-hooks preset current # View current preset
1145
-
1146
- # Linting
1147
- claude-hooks lint # Lint staged files
1148
- claude-hooks lint src/ # Lint all files in directory
1149
- claude-hooks lint src/ lib/utils/ # Multiple directories
1150
- claude-hooks lint file1.js file2.js # Specific files
1151
- claude-hooks lint src/ file.js lib/ # Mix of dirs and files
1152
-
1153
- # Analysis and PRs
1154
- claude-hooks analyze-diff [branch] # Analyze diff for PR
1155
- claude-hooks analyze-pr <pr-url> # Analyze GitHub PR with team guidelines
1156
- claude-hooks analyze-pr <url> --dry-run # Analyze without posting comments
1157
- claude-hooks check-coupling # Detect coupled PRs targeting develop
1158
- claude-hooks check-coupling --base main # Explicit base branch
1159
- claude-hooks check-coupling --json # Machine-readable JSON output
1160
- claude-hooks shadow analyze # Show shadow divergence vs main + active RC
1161
- claude-hooks shadow reset # Destroy shadow and recreate from main
1162
- claude-hooks shadow reset --dry-run # Preview reset without executing
1163
- claude-hooks shadow sync release-candidate # Merge latest RC into shadow (auto-detect)
1164
- claude-hooks shadow sync release-candidate/V2.7.0 # Merge explicit RC into shadow
1165
- claude-hooks shadow sync develop # Merge develop into shadow
1166
- claude-hooks shadow sync release-candidate --dry-run # Preview sync without executing
1167
- claude-hooks create-release minor # Create RC branch, bump, push, shadow
1168
- claude-hooks create-release minor --no-shadow # Skip shadow deployment
1169
- claude-hooks create-release minor --dry-run # Preview only
1170
- claude-hooks create-release minor --skip-push # Local only (skips shadow too)
1171
- claude-hooks create-release minor --update-changelog # Include CHANGELOG
1172
- claude-hooks revert-feature AUT-3179 # Find and revert by task ID
1173
- claude-hooks revert-feature AUT-3179 --update-shadow # Revert and re-deploy shadow
1174
- claude-hooks revert-feature AUT-3179 --dry-run # Preview only
1175
- claude-hooks close-release # Auto-detect RC, prompt for description
1176
- claude-hooks close-release "cashflow + auth" # Explicit description
1177
- claude-hooks close-release --auto-describe # Claude generates summary
1178
- claude-hooks close-release --dry-run # Preview only
1179
- claude-hooks close-release --no-pr # Skip PR creation
1180
- claude-hooks create-pr [branch] # Create PR on GitHub
1181
- claude-hooks setup-github # Configure GitHub token
1182
- claude-hooks setup-linear # Configure Linear token
1183
-
1184
- # Version management
1185
- claude-hooks bump-version patch # Bump version (commits, tags locally)
1186
- claude-hooks bump-version minor --suffix SNAPSHOT # With suffix
1187
- claude-hooks bump-version major --update-changelog # With CHANGELOG
1188
- claude-hooks bump-version patch --push # Push tag immediately
1189
- claude-hooks bump-version patch --no-commit # Manual workflow
1190
- claude-hooks bump-version patch --interactive # Force file selection menu
1191
- claude-hooks generate-changelog # Generate CHANGELOG only
1192
-
1193
- # Help and issue reporting
1194
- claude-hooks help "how do presets work?" # AI-powered help (uses CLAUDE.md)
1195
- claude-hooks help --report-issue # Interactive issue creation
1196
-
1197
- # Orchestration diagnostics
1198
- claude-hooks batch-info # Orchestration config + per-model speed telemetry
1199
-
1200
- # Debugging
1201
- claude-hooks --debug true # Enable debug mode
1202
- claude-hooks --debug false # Disable debug mode
1203
- claude-hooks --debug status # View debug status
1204
-
1205
- # Auto-update
1206
- claude-hooks update # Update to latest version
1207
- ```
1208
-
1209
- ### Git with Active Hooks
1210
-
1211
- ```bash
1212
- # Normal commit with analysis
1213
- git commit -m "feat: new functionality"
1214
-
1215
- # Commit with automatic message
1216
- git commit -m "auto" # Claude generates the message
1217
-
1218
- # Skip analysis (emergencies)
1219
- git commit --no-verify -m "hotfix: urgent"
1220
-
1221
- # Amend last commit
1222
- git commit --amend
1223
- ```
1224
-
1225
- ### Local Testing in Test Repo
1226
-
1227
- ```bash
1228
- # Initial setup
1229
- cd /path/to/test-repo
1230
- claude-hooks install --force --skip-auth
1231
-
1232
- # After changes in claude-hooks
1233
- # (npm link is already active, no need to reinstall)
1234
- cd /path/to/test-repo
1235
- git add test.txt
1236
- git commit -m "test: validate changes"
1237
- # Hooks use local version automatically
1238
- ```
1239
-
1240
- ## Testing
1241
-
1242
- ### Test Framework
1243
-
1244
- - **Framework**: Jest with ES6 modules (`--experimental-vm-modules`)
1245
- - **Location**: `test/unit/*.test.js`
1246
- - **Naming**: `{module-name}.test.js` matches `lib/utils/{module-name}.js`
1247
- - **Run**: `npm test` (all), `npm run test:watch` (watch mode)
1248
-
1249
- ### Test Patterns
1250
-
1251
- ```javascript
1252
- // test/unit/example.test.js
1253
- import { functionToTest } from '../../lib/utils/example.js';
1254
-
1255
- describe('functionToTest', () => {
1256
- it('should handle normal input', () => {
1257
- const result = functionToTest('input');
1258
- expect(result).toBe('expected');
1259
- });
1260
-
1261
- it('should throw on invalid input', () => {
1262
- expect(() => functionToTest(null)).toThrow('Invalid input');
1263
- });
1264
- });
1265
- ```
1266
-
1267
- ### Mocking
1268
-
1269
- ```javascript
1270
- // Mock child_process for git/claude commands
1271
- import { jest } from '@jest/globals';
1272
- import { execSync } from 'child_process';
1273
-
1274
- jest.mock('child_process');
1275
- execSync.mockReturnValue('mocked output');
1276
- ```
1277
-
1278
- ### Coverage
1279
-
1280
- No enforced threshold. Focus on critical paths:
1281
-
1282
- - `claude-client.js` - Claude CLI interaction
1283
- - `git-operations.js` - Git command abstraction
1284
- - `config.js` - Configuration merging
1285
-
1286
- ## Proven Implementation Patterns
1287
-
1288
- Recurring patterns validated across 15+ automation sessions. Apply these in new code to avoid known pitfalls.
1289
-
1290
- ### Testability
1291
-
1292
- - **`process.exit()` outside try/catch**: Mocking `process.exit` to throw requires the throw to propagate. Any surrounding `catch (e)` silently swallows it. Pattern: capture fallible values inside try/catch, check them and exit *outside* the catch block.
1293
- - **`jest.resetAllMocks()` over `clearAllMocks()`**: `clearAllMocks()` clears call history but does NOT drain `mockResolvedValueOnce` queues. Tests that leave unconsumed values silently corrupt subsequent tests. Use `resetAllMocks()` in `beforeEach` and re-apply all defaults explicitly.
1294
- - **`_privateHelper` export for unit testing**: Private-by-convention functions (`_` prefix) can be exported for direct unit testing without invoking the full command flow. Used in `close-release.js`, `revert-feature.js`, `shadow.js`.
1295
-
1296
- ### Git Operations
1297
-
1298
- - **`execSync` with `input` for multi-line commit messages**: `git commit -F -` with `execSync({ input: fullMessage })` avoids temp files, heredocs, and `\n` escaping. Used when `createCommit()` (which only supports `-m`) is insufficient.
1299
- - **`git diff-tree --no-commit-id -r --name-only <hash>`**: Canonical command to list files changed in a single commit. Preferable to parsing `git show --stat` (fragile) or `getChangedFilesBetweenRefs('<hash>^', '<hash>')` (breaks on first commit).
1300
- - **`git merge --no-verify --no-ff` via `execSync`**: `mergeBranch()` in `git-operations.js` does not support `--no-verify`. For hook-free merges (shadow sync, back-merge), use `execSync` directly with the full flags.
1301
-
1302
- ### Architecture
1303
-
1304
- - **`PROTECTED_COMMANDS` fast-path**: Static Set check before any API call — non-protected commands short-circuit with zero network overhead. Pattern for any guard that only applies to a subset of commands.
1305
- - **Graceful degradation for features, fail-closed for security**: Labels, shadow sync, CHANGELOG — warn and continue on failure. Authorization, permissions — block on any error. Two fetch patterns for the same config repo.
1306
- - **Inline Set intersection over Union-Find for single-target checks**: `coupling-detector.js` Union-Find is for N-PR transitive grouping. For checking one commit against others (revert-feature), a simple `Set` intersection per item is sufficient.
1307
-
1308
- ## Restrictions
1309
-
1310
- ### What Claude should NOT do in this repository
1311
-
1312
- 1. **DO NOT add interactive prompts to git hooks**
1313
- - Git hooks run with stdin redirected from `/dev/null`
1314
- - Readline-based prompts cannot read user input in hook context
1315
- - Works in IDE integrations (VSCode, IntelliJ) but breaks in terminal
1316
- - **Solution**: Use `claude-hooks analyze` command instead (runs outside hook context)
1317
- - **Rationale**: Discovered through manual testing in Issue #20
1318
-
1319
- 2. **DO NOT modify bash wrappers without corresponding Node.js changes**
1320
- - `templates/pre-commit` and `templates/prepare-commit-msg` are wrappers
1321
- - Real logic is in `lib/hooks/*.js`
1322
- - Changes to bash should be minimal (only Node.js invocation)
1323
-
1324
- 3. **DO NOT use `shell: true` in `spawn()` calls**
1325
- - Deprecated in Node.js 24 (DEP0190)
1326
- - Use `which-command.js` to resolve executable paths
1327
- - **Exception**: Windows with `.cmd`/`.bat` requires `cmd.exe /c` wrapper
1328
-
1329
- 4. **DO NOT modify config priority order**
1330
- - Maintain: defaults < user config < preset config
1331
- - Preset always wins (tech-stack specific has priority over user preferences)
1332
-
1333
- 5. **DO NOT create duplicate config files**
1334
- - Single `.claude/config.json` per repo
1335
- - Presets live in `.claude/presets/{name}/`
1336
- - Do not create `.env` or similar files for configuration
1337
-
1338
- 6. **DO NOT make breaking changes without MAJOR version bump**
1339
- - Changes in config.json structure → MAJOR
1340
- - Changes in CLI arguments → MAJOR
1341
- - Changes in template format → MINOR if backward compatible, otherwise MAJOR
1342
-
1343
- 7. **DO NOT hardcode absolute paths**
1344
- - Use `getRepoRoot()` from `git-operations.js`
1345
- - All paths relative to repo root
1346
- - Exception: paths in `PATH` resolved with `which-command.js`
1347
-
1348
- 8. **DO NOT use `console.log` directly**
1349
- - Always use `logger.js`: `info()`, `warning()`, `error()`, `debug()`
1350
- - Enables centralized output control and debug mode
1351
-
1352
- 9. **DO NOT execute Claude CLI without timeout**
1353
- - Always configure timeout (default: 150s analysis, 180s commit msg)
1354
- - Timeout configurable in `.claude/config.json`
1355
-
1356
- 10. **DO NOT ignore platform-specific issues**
1357
- - Test changes on Windows, Linux, and macOS
1358
- - Path separators: use `path.join()` (no hardcoded `/`)
1359
- - Line endings: templates use LF, convert on install if needed
1360
-
1361
- 11. **DO NOT commit secrets**
1362
- - `.claude/settings.local.json` is in `.gitignore`
1363
- - GitHub tokens should go in settings.local or env vars
1364
- - Never in `.claude/config.json` (tracked)
1365
-
1366
- 12. **DO NOT modify project `.gitignore` without consultation**
1367
- - `.claude/` is ignored by design (user-specific)
1368
- - `node_modules/` obvious
1369
- - New entries must be justified
1370
-
1371
- 13. **DO NOT add dependencies without validation**
1372
- - Only 1 runtime dependency currently: `@octokit/rest`
1373
- - New deps must be justified (no built-in alternative?)
1374
- - Dev deps are OK if they improve DX (testing, linting)
1375
-
1376
- 14. **DO NOT perform git operations add, commit, push unless asked to**
1377
- - Git flow associated with normal development must be left to human user
1378
-
1379
- ### Technical Limitations
1380
-
1381
- 1. **Node.js versions**: >=16.9.0 required (ES6 module features)
1382
- 2. **Claude CLI**: Must be installed and authenticated externally
1383
- 3. **Git**: Requires initialized repo (doesn't work outside git repos)
1384
- 4. **Parallel analysis**: Minimum 3 files to activate
1385
- 5. **File size**: Default max 1MB per file (configurable)
1386
- 6. **Max files**: Default 30 files per commit (configurable)
1387
- 7. **GitHub API**: Rate limits apply (5000 req/hour authenticated)
1388
-
1389
- ### Security Considerations
1390
-
1391
- 1. **Input sanitization**: Use `sanitize.js` for user inputs in shell commands
1392
- 2. **Token storage**: GitHub tokens in settings.local (gitignored) or env vars, NEVER tracked
1393
- 3. **Shell injection**: Avoid `shell: true`, use arrays in spawn()
1394
- 4. **Path traversal**: Validate relative paths before file operations
1395
- 5. **Secrets in diffs**: Hook should NOT prevent commits with secrets (user responsibility)
35
+ > See [`CLAUDE-MIGRATION.md`](CLAUDE-MIGRATION.md) for a mapping of where each original CLAUDE.md section moved.