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