claude-git-hooks 2.21.0 → 2.30.2

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
@@ -11,7 +11,12 @@
11
11
  3. **Automatic messages**: Write `git commit -m "auto"` and Claude generates the message in Conventional Commits format with task-id extracted from branch
12
12
  4. **PR analysis**: `claude-hooks analyze-diff [branch]` generates title, description, and test plan for PRs
13
13
  5. **PR review**: `claude-hooks analyze-pr <url>` analyzes a GitHub PR with preset guidelines, Linear ticket enrichment, and posts review comments
14
- 6. **PR creation**: `claude-hooks create-pr [branch]` creates the PR on GitHub with automatic metadata (reviewers from CODEOWNERS, labels by preset)
14
+ 6. **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)
15
+ 7. **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
16
+ 8. **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)
17
+ 9. **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
18
+ 10. **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
19
+ 11. **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
15
20
 
16
21
  ## Architecture
17
22
 
@@ -28,7 +33,7 @@
28
33
 
29
34
  **Modular, decoupled, reusable code.** Each component has a single responsibility:
30
35
 
31
- - **bin/claude-hooks**: Thin CLI router - only argument parsing and command dispatch
36
+ - **bin/claude-hooks**: Thin CLI router - argument parsing, command dispatch, and authorization guard
32
37
  - **lib/commands/**: Command modules - one file per CLI command, self-contained logic
33
38
  - **lib/hooks/**: Git hook logic - Node.js implementations invoked by bash wrappers
34
39
  - **lib/utils/**: Reusable utilities - shared across commands, no CLI dependencies
@@ -56,6 +61,11 @@ claude-git-hooks/
56
61
  │ │ ├── hooks.js # Hook management - enable, disable, status, uninstall
57
62
  │ │ ├── analyze-diff.js # Diff analysis - generate PR metadata from git diff
58
63
  │ │ ├── analyze-pr.js # PR analysis - analyze GitHub PR with team guidelines
64
+ │ │ ├── check-coupling.js # Coupling detection - detect coupled PRs before release (v2.23.0)
65
+ │ │ ├── shadow.js # Shadow management - analyze/reset/sync shadow branch lifecycle (v2.24.0)
66
+ │ │ ├── create-release.js # Release creation - RC branch from develop, version bump, shadow deploy (v2.27.0)
67
+ │ │ ├── revert-feature.js # Feature revert - find by task-id, coupling check, git revert, push, shadow (v2.28.0)
68
+ │ │ ├── close-release.js # Release closure - soft-reset, single commit, force-push, PR to main (v2.29.0)
59
69
  │ │ ├── create-pr.js # PR creation - full Octokit workflow
60
70
  │ │ ├── setup-github.js # Token setup - interactive GitHub configuration
61
71
  │ │ ├── setup-linear.js # Token setup - interactive Linear configuration
@@ -76,15 +86,18 @@ claude-git-hooks/
76
86
  │ ├── diff-analysis-orchestrator.js # Intelligent batch orchestration via Opus (v2.20.0)
77
87
  │ ├── claude-client.js # Claude CLI wrapper - spawn, retry, model override
78
88
  │ ├── prompt-builder.js # Prompt construction - load templates, replace vars
79
- │ ├── git-operations.js # Git abstractions - staged files, diff, repo root, push, commit
89
+ │ ├── git-operations.js # Git abstractions - staged files, diff, repo root, push, commit, checkout, merge, reset, force-push, delete-remote-branch, divergence
80
90
  │ ├── file-utils.js # File operations - repo-relative paths
81
91
  │ ├── logger.js # Logging system - centralized output, debug mode
82
92
  │ ├── preset-loader.js # Preset system - load tech-stack configurations
83
93
  │ ├── pr-metadata-engine.js # PR metadata generation - branch context, diff reduction, metadata (v2.14.0)
84
94
  │ ├── github-api.js # Octokit integration - PR creation, PR analysis, token validation
85
95
  │ ├── github-client.js # GitHub helpers - CODEOWNERS parsing, reviewers
96
+ │ ├── authorization.js # Role-based authorization - PROTECTED_COMMANDS, authorizeCommand(), fail-closed (v2.23.0)
97
+ │ ├── authorization.js # Role-based authorization - PROTECTED_COMMANDS, authorizeCommand(), fail-closed (v2.23.0)
86
98
  │ ├── token-store.js # Token persistence - centralized settings.local.json read/write
87
99
  │ ├── linear-connector.js # Linear API - ticket context fetching with retry
100
+ │ ├── coupling-detector.js # Coupling algorithm - Union-Find transitive grouping (v2.23.0)
88
101
  │ ├── pr-statistics.js # PR statistics - write-only JSONL analytics
89
102
  │ ├── task-id.js # Task ID extraction - Jira, GitHub, Linear patterns
90
103
  │ ├── interactive-ui.js # CLI UI components - previews, prompts, spinners
@@ -176,27 +189,27 @@ preset config (.claude/presets/{name}/config.json) ← HIGHEST PRIORITY
176
189
  "showCommits": true, // Show commit preview (v2.11.0)
177
190
  "verifyRemote": true // Verify remote exists (v2.11.0)
178
191
  }
179
- },
192
+ }
180
193
  }
181
194
  }
182
195
  ```
183
196
 
184
197
  **Hardcoded defaults (v2.8.0+):**
185
198
 
186
- | Parameter | Value | Notes |
187
- | ----------------------- | ------- | -------------------------------------------------- |
188
- | Max file size | 1MB | Files larger are skipped |
189
- | Max files per commit | 30 | Excess files trigger warning |
190
- | Orchestrator threshold | 3 files | Commits with ≥3 files use Opus orchestration |
191
- | Orchestrator model | opus | Internal constant — not user-configurable |
192
- | Orchestrator timeout | 60s | Lightweight call (file overview only) |
193
- | Analysis timeout | 300s | Per-batch timeout |
194
- | Commit msg timeout | 300s | Message generation timeout |
195
- | PR metadata timeout | 180s | Engine default (reads config) |
196
- | Judge model | sonnet | Default, configurable via `config.judge.model` |
197
- | Judge timeout | 120s | Per-judge call timeout |
198
- | PR analysis model | sonnet | Default, configurable via `config.prAnalysis.model` |
199
- | PR analysis timeout | 300s | Per-analysis Claude call |
199
+ | Parameter | Value | Notes |
200
+ | ---------------------- | ------- | --------------------------------------------------- |
201
+ | Max file size | 1MB | Files larger are skipped |
202
+ | Max files per commit | 30 | Excess files trigger warning |
203
+ | Orchestrator threshold | 3 files | Commits with ≥3 files use Opus orchestration |
204
+ | Orchestrator model | opus | Internal constant — not user-configurable |
205
+ | Orchestrator timeout | 60s | Lightweight call (file overview only) |
206
+ | Analysis timeout | 300s | Per-batch timeout |
207
+ | Commit msg timeout | 300s | Message generation timeout |
208
+ | PR metadata timeout | 180s | Engine default (reads config) |
209
+ | Judge model | sonnet | Default, configurable via `config.judge.model` |
210
+ | Judge timeout | 120s | Per-judge call timeout |
211
+ | PR analysis model | sonnet | Default, configurable via `config.prAnalysis.model` |
212
+ | PR analysis timeout | 300s | Per-analysis Claude call |
200
213
 
201
214
  **Judge behavior (v2.20.0):**
202
215
 
@@ -223,9 +236,9 @@ preset config (.claude/presets/{name}/config.json) ← HIGHEST PRIORITY
223
236
 
224
237
  Three-tier strategy based on file count:
225
238
 
226
- | Files | Strategy |
227
- |-------|----------|
228
- | 1–2 | Sequential — single Claude call |
239
+ | Files | Strategy |
240
+ | ------ | ----------------------------------------------------------------------------------------------------- |
241
+ | 1–2 | Sequential — single Claude call |
229
242
  | **3+** | **Intelligent orchestration** — Opus orchestrator, semantic grouping, per-batch model, shared context |
230
243
 
231
244
  **Orchestration flow (3+ files):**
@@ -254,55 +267,62 @@ consolidateResults()
254
267
 
255
268
  **Command Modules (`lib/commands/`):**
256
269
 
257
- | Module | Purpose | Key Exports |
258
- | ----------------------- | ------------------------- | ----------------------------------------------------------------------------- |
259
- | `helpers.js` | Shared CLI utilities | `colors`, `error()`, `success()`, `info()`, `checkGitRepo()`, `getGitHooksPath()`, `Entertainment` |
260
- | `install.js` | Installation logic | `runInstall()`, `extractLegacySettings()` |
261
- | `hooks.js` | Hook management | `runEnable()`, `runDisable()`, `runStatus()`, `runUninstall()` |
262
- | `analyze.js` | Interactive code analysis | `runAnalyze()` |
263
- | `analyze-diff.js` | Diff analysis | `runAnalyzeDiff()` |
264
- | `analyze-pr.js` | PR analysis from URL | `runAnalyzePr()`, `normalizeCategory()` |
265
- | `create-pr.js` | PR creation | `runCreatePr()` |
266
- | `bump-version.js` | Version management | `runBumpVersion()` |
267
- | `generate-changelog.js` | CHANGELOG generation | `runGenerateChangelog()` |
268
- | `setup-github.js` | Token setup (GitHub) | `runSetupGitHub()` |
269
- | `setup-linear.js` | Token setup (Linear) | `runSetupLinear()` |
270
- | `presets.js` | Preset management | `runShowPresets()`, `runSetPreset()`, `runCurrentPreset()` |
271
- | `update.js` | Self-update | `runUpdate()` |
272
- | `migrate-config.js` | Config migration | `runMigrateConfig()` |
273
- | `debug.js` | Debug toggle | `runSetDebug()` |
274
- | `telemetry-cmd.js` | Telemetry commands | `runShowTelemetry()`, `runClearTelemetry()` |
275
- | `diff-batch-info.js` | Batch info display | `runDiffBatchInfo()` |
276
- | `help.js` | Help, AI help, report-issue | `runShowHelp()`, `showStaticHelp()`, `runShowVersion()` |
270
+ | Module | Purpose | Key Exports |
271
+ | ----------------------- | --------------------------- | -------------------------------------------------------------------------------------------------- |
272
+ | `helpers.js` | Shared CLI utilities | `colors`, `error()`, `success()`, `info()`, `checkGitRepo()`, `getGitHooksPath()`, `Entertainment` |
273
+ | `install.js` | Installation logic | `runInstall()`, `extractLegacySettings()` |
274
+ | `hooks.js` | Hook management | `runEnable()`, `runDisable()`, `runStatus()`, `runUninstall()` |
275
+ | `analyze.js` | Interactive code analysis | `runAnalyze()` |
276
+ | `analyze-diff.js` | Diff analysis | `runAnalyzeDiff()` |
277
+ | `analyze-pr.js` | PR analysis from URL | `runAnalyzePr()`, `normalizeCategory()` |
278
+ | `check-coupling.js` | Coupling detection | `runCheckCoupling()` |
279
+ | `shadow.js` | Shadow branch management | `runShadow()` — routes to analyze/reset/sync subcommands (v2.24.0) |
280
+ | `create-release.js` | Release candidate creation | `runCreateRelease()` — RC branch from develop, version bump, push, shadow deploy (v2.27.0) |
281
+ | `revert-feature.js` | Feature revert in RC | `runRevertFeature()` — find by task-id, coupling check, revert, push, shadow sync (v2.28.0) |
282
+ | `close-release.js` | Release candidate closure | `runCloseRelease()` — soft-reset, single commit, force-push, PR to main (v2.29.0) |
283
+ | `create-pr.js` | PR creation | `runCreatePr()`, `detectMergeStrategy()` (private) |
284
+ | `bump-version.js` | Version management | `runBumpVersion()` |
285
+ | `generate-changelog.js` | CHANGELOG generation | `runGenerateChangelog()` |
286
+ | `setup-github.js` | Token setup (GitHub) | `runSetupGitHub()` |
287
+ | `setup-linear.js` | Token setup (Linear) | `runSetupLinear()` |
288
+ | `presets.js` | Preset management | `runShowPresets()`, `runSetPreset()`, `runCurrentPreset()` |
289
+ | `update.js` | Self-update | `runUpdate()` |
290
+ | `migrate-config.js` | Config migration | `runMigrateConfig()` |
291
+ | `debug.js` | Debug toggle | `runSetDebug()` |
292
+ | `telemetry-cmd.js` | Telemetry commands | `runShowTelemetry()`, `runClearTelemetry()` |
293
+ | `diff-batch-info.js` | Batch info display | `runDiffBatchInfo()` |
294
+ | `help.js` | Help, AI help, report-issue | `runShowHelp()`, `showStaticHelp()`, `runShowVersion()` |
277
295
 
278
296
  **Utility Modules (`lib/utils/`):**
279
297
 
280
- | Module | Purpose | Key Exports |
281
- | ------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
282
- | `lib/cli-metadata.js` | Command registry | `commands`, `buildCommandMap()`, `generateCompletionData()`, `PRESET_NAMES`, `HOOK_NAMES`, `BUMP_TYPES` |
283
- | `lib/config.js` | Config system | `getConfig()` |
284
- | `analysis-engine.js` | Shared analysis logic | `buildFileData()`, `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `hasBlockingIssues()`, `hasAnyIssues()`, `displayResults()`, `displayIssueSummary()` (v2.13.0+) |
285
- | `diff-analysis-orchestrator.js` | Intelligent batch orchestration | `orchestrateBatches()`, `buildFileOverview()`, `detectDependencies()` (v2.20.0) |
286
- | `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `executeClaudeWithRetry()`, `extractJSON()` — spawn, retry, model override |
287
- | `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` — accepts `commonContext`, `batchRationale` |
288
- | `git-operations.js` | Git abstractions | `getStagedFiles()`, `getUnstagedFiles()`, `getAllTrackedFiles()`, `getDiff()`, `getRepoRoot()`, `getBranchPushStatus()`, `pushBranch()`, `createCommit()`, `fetchRemote()`, `branchExists()`, `getRemoteBranches()`, `resolveBaseBranch()`, `getChangedFilesBetweenRefs()`, `getDiffBetweenRefs()`, `getCommitsBetweenRefs()` |
289
- | `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
290
- | `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
291
- | `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
292
- | `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
293
- | `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
294
- | `github-api.js` | Octokit integration | `createPullRequest()`, `fetchPullRequest()`, `fetchPullRequestFiles()`, `createPullRequestReview()`, `parseGitHubPRUrl()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()` |
295
- | `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` |
296
- | `token-store.js` | Token persistence | `loadToken()`, `saveToken()`, `hasToken()`, `loadLocalSettings()` |
297
- | `linear-connector.js` | Linear integration | `loadLinearToken()`, `testConnection()`, `fetchTicket()`, `extractLinearTicketFromTitle()`, `parseLinearIdentifier()`, `LinearConnectorError` |
298
- | `pr-statistics.js` | PR statistics | `recordPRAnalysis()` write-only JSONL at `.claude/statistics/pr/stats.jsonl` |
299
- | `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
300
- | `task-id.js` | Task ID extraction | `getOrPromptTaskId()`, `formatWithTaskId()` |
301
- | `logger.js` | Logging system | `info()`, `warning()`, `error()`, `debug()` |
302
- | `judge.js` | Auto-fix judge | `judgeAndFix()`, `applyFix()` — LLM verdict + search/replace fixes (v2.20.0) |
303
- | `resolution-prompt.js` | Issue resolution | `generateResolutionPrompt()` |
304
- | `interactive-ui.js` | CLI UI components | `showPRPreview()`, `promptConfirmation()`, `promptMenu()`, `promptToggleList()`, `promptEditField()`, `promptUserConfirmation()` |
305
- | `telemetry.js` | Local telemetry | `recordEvent()`, `displayStatistics()` |
298
+ | Module | Purpose | Key Exports |
299
+ | ------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
300
+ | `lib/cli-metadata.js` | Command registry | `commands`, `buildCommandMap()`, `generateCompletionData()`, `PRESET_NAMES`, `HOOK_NAMES`, `BUMP_TYPES` |
301
+ | `lib/config.js` | Config system | `getConfig()` |
302
+ | `analysis-engine.js` | Shared analysis logic | `buildFileData()`, `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `hasBlockingIssues()`, `hasAnyIssues()`, `displayResults()`, `displayIssueSummary()` (v2.13.0+) |
303
+ | `diff-analysis-orchestrator.js` | Intelligent batch orchestration | `orchestrateBatches()`, `buildFileOverview()`, `detectDependencies()` (v2.20.0) |
304
+ | `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `executeClaudeWithRetry()`, `extractJSON()` — spawn, retry, model override |
305
+ | `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` — accepts `commonContext`, `batchRationale` |
306
+ | `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()` |
307
+ | `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
308
+ | `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
309
+ | `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
310
+ | `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
311
+ | `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
312
+ | `coupling-detector.js` | Coupling algorithm | `buildFileIndex()`, `getSharedFiles()`, `detectCouplingGroups()` Union-Find transitive grouping (v2.23.0) |
313
+ | `github-api.js` | Octokit integration | `createPullRequest()`, `fetchPullRequest()`, `fetchPullRequestFiles()`, `createPullRequestReview()`, `parseGitHubPRUrl()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()`, `listOpenPullRequests()`, `getAuthenticatedUser()`, `checkOrgMembership()`, `getCollaboratorPermission()` |
314
+ | `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` |
315
+ | `authorization.js` | Role-based authorization | `authorizeCommand()`, `requiresAuthorization()`, `getRequiredRole()`, `AuthorizationError`, `PROTECTED_COMMANDS` — fail-closed guard for workflow commands (v2.23.0) |
316
+ | `token-store.js` | Token persistence | `loadToken()`, `saveToken()`, `hasToken()`, `loadLocalSettings()` |
317
+ | `linear-connector.js` | Linear integration | `loadLinearToken()`, `testConnection()`, `fetchTicket()`, `extractLinearTicketFromTitle()`, `parseLinearIdentifier()`, `LinearConnectorError` |
318
+ | `pr-statistics.js` | PR statistics | `recordPRAnalysis()` — write-only JSONL at `.claude/statistics/pr/stats.jsonl` |
319
+ | `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
320
+ | `task-id.js` | Task ID extraction | `getOrPromptTaskId()`, `formatWithTaskId()` |
321
+ | `logger.js` | Logging system | `info()`, `warning()`, `error()`, `debug()` |
322
+ | `judge.js` | Auto-fix judge | `judgeAndFix()`, `applyFix()` LLM verdict + search/replace fixes (v2.20.0) |
323
+ | `resolution-prompt.js` | Issue resolution | `generateResolutionPrompt()` |
324
+ | `interactive-ui.js` | CLI UI components | `showPRPreview()`, `promptConfirmation()`, `promptMenu()`, `promptToggleList()`, `promptEditField()`, `promptUserConfirmation()` |
325
+ | `telemetry.js` | Local telemetry | `recordEvent()`, `displayStatistics()` |
306
326
 
307
327
  ### Design Patterns
308
328
 
@@ -314,6 +334,8 @@ consolidateResults()
314
334
  6. **Orchestrator Pattern**: `diff-analysis-orchestrator.js` — Opus decides semantic grouping and model assignment; workers (analyzeCode per batch) execute in parallel
315
335
  7. **Adapter Pattern**: `git-operations.js` abstracts git commands into JS functions
316
336
  8. **Singleton Pattern**: `config.js` loads configuration once per execution
337
+ 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/claude-hooks-permissions/permissions.json`
338
+ 10. **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/claude-hooks-permissions/permissions.json`
317
339
 
318
340
  ### Key Data Flows
319
341
 
@@ -390,7 +412,7 @@ claude-hooks analyze-pr https://github.com/owner/repo/pull/123
390
412
  → recordPRAnalysis() → .claude/statistics/pr/stats.jsonl
391
413
  ```
392
414
 
393
- **Flow 4: PR creation (with auto-push v2.11.0, engine v2.14.0)**
415
+ **Flow 4: PR creation (with auto-push v2.11.0, engine v2.14.0, merge strategy v2.25.0)**
394
416
 
395
417
  ```
396
418
  claude-hooks create-pr develop
@@ -399,8 +421,13 @@ claude-hooks create-pr develop
399
421
  → pushes branch to remote (if needed)
400
422
  → reads CODEOWNERS → detects reviewers
401
423
  → reads config → applies label rules per preset
424
+ → detectMergeStrategy(sourceBranch, targetBranch):
425
+ feature/* → squash | release-fix/* → squash
426
+ release-candidate/* → merge-commit | hotfix/* → merge-commit
427
+ any → main → merge-commit | unknown → user prompted to select
428
+ → adds merge-strategy:<strategy> label; prepends body reminder for merge-commit
402
429
  → analyzeBranchForPR() (pr-metadata-engine.js) → generates metadata
403
- → interactive preview → user confirms
430
+ → interactive preview + strategy info line → user confirms
404
431
  → Octokit creates PR on GitHub
405
432
  ```
406
433
 
@@ -427,6 +454,114 @@ claude-hooks bump-version patch --interactive
427
454
 
428
455
  **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`.
429
456
 
457
+ **Flow 6: Release candidate creation (v2.27.0)**
458
+
459
+ ```
460
+ claude-hooks create-release minor
461
+ → parseArguments() → bumpType, noShadow, dryRun, skipPush, updateChangelog
462
+ → validatePreconditions():
463
+ checkGitRepo()
464
+ isWorkingDirectoryClean() → abort if dirty
465
+ getCurrentBranch() === 'develop' → abort if not on develop
466
+ fetchRemote() → warn on failure, continue
467
+ getDivergence('develop','origin/develop') → behind>0 → abort + git pull advice
468
+ getDivergence('origin/develop','origin/main') → behind>0 → abort + back-merge advice
469
+ getRemoteBranches().filter('release-candidate/*') → any found → abort + close-release advice
470
+ verifyRemoteExists()
471
+ → discoverVersionFiles()
472
+ mismatch → abort with table + fix steps (non-interactive, cannot auto-resolve)
473
+ → incrementVersion(currentVersion, bumpType) [no suffix in branch name]
474
+ → rcBranch = 'release-candidate/V{nextVersion}'
475
+ → [--dry-run] → preview table → return
476
+ → promptConfirmation()
477
+ → checkoutBranch(rcBranch, { create: true, startPoint: 'develop' })
478
+ → updateVersionFiles()
479
+ → [--update-changelog] → generateChangelogEntry() + updateChangelogFile()
480
+ → stageFiles() + createCommit('chore(version): bump to {nextVersion}', { noVerify: true })
481
+ → tagExists()? → warn + skip : createTag()
482
+ → [unless --skip-push] pushBranch(rcBranch, { setUpstream: true })
483
+ → [unless --no-shadow and not --skip-push] runShadow(['sync', rcBranch])
484
+ shadow error → warn, don't abort
485
+ → checkoutBranch(rcBranch) [safety: ensure we land on RC after shadow sync]
486
+ → display summary (branch, version, tag, push status, shadow status)
487
+ ```
488
+
489
+ **create-release design decisions:**
490
+ - Mismatch aborts non-interactively (automation context — ambiguity must be fixed by human)
491
+ - Tag creation mirrors `bump-version`: created on RC branch; if tag already exists → warn + skip (no interactive prompt)
492
+ - Shadow sync delegates to `runShadow(['sync', rcBranch])` (public API, reuses conflict-resolution logic from #93)
493
+ - `--skip-push` implies no shadow (cannot sync unpushed branch)
494
+ - Branch naming: `release-candidate/V{semver}` — capital V per team convention, no suffix in branch name
495
+
496
+ **Flow 7: Feature revert in release-candidate (v2.28.0)**
497
+
498
+ ```
499
+ claude-hooks revert-feature AUT-3179
500
+ → parseArgs() → taskId, updateShadow, dryRun
501
+ → getCurrentBranch() — must start with 'release-candidate/'
502
+ → isWorkingDirectoryClean() → abort if dirty
503
+ → git log --grep="AUT-3179" --fixed-strings -i origin/main..HEAD
504
+ 0 matches → abort
505
+ 1 match → show commit details (hash, message, author, date, files)
506
+ 2+ matches → promptMenu() → user selects one
507
+ → getCommitFiles(targetHash) → target file set
508
+ → _checkCoupling(): for each other RC commit (skip target, skip Revert commits):
509
+ getCommitFiles(hash) → intersect with targetFiles → collect { taskId, sharedFiles }
510
+ → [--dry-run] → preview table → return
511
+ → _displayCommitDetails() + coupling warnings (informational, before single confirmation)
512
+ → promptConfirmation('Proceed with revert?', false)
513
+ → git revert --no-edit <hash> → stdio: inherit (shows git output)
514
+ → git rev-parse HEAD → revertHash
515
+ → pushBranch(rcBranch)
516
+ → _appendRevertLog(): append { taskId, originalHash, revertHash, rcBranch, timestamp }
517
+ to .claude/revert-log.json (array, consumed by back-merge #96)
518
+ → [--update-shadow] runShadow(['sync', rcBranch]) → warn on failure, don't abort
519
+ → display summary + revert-the-revert reminder:
520
+ git revert <revertHash> # Restores AUT-3179 for next sprint
521
+ ```
522
+
523
+ **revert-feature design decisions:**
524
+ - Coupling check is informational only — warnings shown before single confirmation (no second prompt)
525
+ - `Revert "..."` commits are skipped in coupling scan (they intentionally share files)
526
+ - `revert-log.json` schema: `[{ taskId, originalHash, revertHash, rcBranch, timestamp }]` — array to support multiple reverts; consumed by back-merge (#96)
527
+ - `git revert` uses `stdio: 'inherit'` so the user sees git output (including conflict messages)
528
+ - Push failure: revert commit is kept locally; user shown manual `git push` command
529
+ - Shadow sync error: warns but does not abort (non-fatal, same pattern as `create-release`)
530
+
531
+ **Flow 8: Release candidate closure (v2.29.0)**
532
+
533
+ ```
534
+ claude-hooks close-release "cashflow + auth"
535
+ → _parseArgs() → description, autoDescribe, dryRun, noPr
536
+ → detect RC branch: current branch or getActiveBranch('release-candidate') → offer checkout
537
+ → _extractVersion(rcBranch) → semver string
538
+ → isWorkingDirectoryClean() → abort if dirty
539
+ → fetchRemote() → warn on failure
540
+ → getDivergence(rc, origin/rc) → behind>0 → abort + git pull advice
541
+ → _collectFeatureList(): getCommitsBetweenRefs('origin/main', 'HEAD', { format: '%s' })
542
+ → _resolveDescription():
543
+ CLI arg → use directly
544
+ --auto-describe → executeClaudeWithRetry(haiku, 60s) → 1-line phrase
545
+ default → show list + promptEditField() → TL accepts or overrides
546
+ → [--dry-run] → preview table → return
547
+ → promptConfirmation()
548
+ → resetBranch('origin/main', { mode: 'soft' }) — all RC commits staged
549
+ → execSync('git commit --no-verify -F -', { input: fullMessage }) — subject + body
550
+ → forcePush(rcBranch, { lease: true })
551
+ → [unless --no-pr] validateToken() + createPullRequest(head→main, label: merge-strategy:merge-commit)
552
+ → display summary with PR URL + merge-commit reminder
553
+ ```
554
+
555
+ **close-release design decisions:**
556
+ - `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)
557
+ - `process.exit()` calls are placed *outside* try/catch blocks so that mock-based tests can observe them; the catch only handles git errors
558
+ - Description priority: CLI arg > `--auto-describe` (Claude haiku, 60s timeout) > `promptEditField` (shows list, TL edits)
559
+ - Auto-describe fallback: if Claude fails or feature list is empty, falls back to interactive prompt without aborting
560
+ - `close-release` was already in `PROTECTED_COMMANDS` — authorization.js unchanged
561
+ - Force-push failure: exits 1 and shows manual push command (no retry — destructive op)
562
+ - PR creation failure: warns + shows `gh pr create` alternative (non-fatal; commit and push already done)
563
+ - `_extractVersion`, `_collectFeatureList`, `_resolveDescription` are exported for unit testing
564
+
430
565
  ## Code Conventions
431
566
 
432
567
  ### General Style
@@ -656,6 +791,248 @@ docs: update CLAUDE.md with architecture details
656
791
  chore(deps): upgrade @octokit/rest to v21
657
792
  ```
658
793
 
794
+ ## Release Workflow Guide
795
+
796
+ 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).
797
+
798
+ ### Quick Reference — Release Commands
799
+
800
+ | Command | When to use | What it does |
801
+ |---|---|---|
802
+ | `check-coupling` | Before cutting a release | Scans open PRs for shared files — reveals which features are coupled |
803
+ | `create-release` | Tuesday release prep | Creates RC branch from develop, bumps version, pushes, deploys to shadow |
804
+ | `shadow` | Throughout QA cycle | Manages the shadow (QA) branch — analyze status, reset, or sync with RC |
805
+ | `revert-feature` | During QA, feature fails | Finds and reverts a feature by task ID in the RC, with coupling warnings |
806
+ | `close-release` | QA passed, ready for deploy | Squashes RC into one commit, force-pushes, creates PR to main |
807
+ | `back-merge` | After production deploy | Tags release, resets shadow, merges main→develop, cleans up RC branch |
808
+ | `create-pr` | Any PR creation | Creates GitHub PR with auto-detected merge strategy and labels |
809
+ | `bump-version` | Manual version changes | Bumps version files, commits, tags — used outside the release cycle |
810
+
811
+ ### Release Lifecycle — Step by Step
812
+
813
+ The typical weekly release cycle follows this sequence:
814
+
815
+ ```
816
+ 1. TUESDAY — Prepare release
817
+ claude-hooks check-coupling ← Are any open PRs coupled?
818
+ claude-hooks create-release minor ← Create RC from develop, bump version, shadow deploy
819
+
820
+ 2. TUESDAY–THURSDAY — QA cycle
821
+ claude-hooks shadow sync release-candidate ← Re-sync shadow after fixes
822
+ claude-hooks revert-feature AUT-XXXX ← Revert a failing feature if needed
823
+ claude-hooks shadow analyze ← Check shadow divergence
824
+
825
+ 3. THURSDAY 12:00 — Close release
826
+ claude-hooks close-release "description" ← Squash RC, create PR to main
827
+
828
+ 4. THURSDAY 13:00 — After deploy
829
+ claude-hooks back-merge ← Tag, reset shadow, merge main→develop, cleanup
830
+ ```
831
+
832
+ ### Command Details
833
+
834
+ #### check-coupling
835
+
836
+ 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.
837
+
838
+ ```bash
839
+ claude-hooks check-coupling # PRs targeting develop (default)
840
+ claude-hooks check-coupling --base main # PRs targeting main
841
+ claude-hooks check-coupling --json # Machine-readable output for CI
842
+ ```
843
+
844
+ | Flag | Effect |
845
+ |---|---|
846
+ | `--base <branch>` | Base branch to scan PRs against (default: `develop`) |
847
+ | `--json` | Output structured JSON with `coupledGroups`, `independentPRNumbers`, `warnings` |
848
+
849
+ **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).
850
+
851
+ ---
852
+
853
+ #### create-release
854
+
855
+ 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.
856
+
857
+ ```bash
858
+ claude-hooks create-release minor # Standard release (most common)
859
+ claude-hooks create-release patch # Hotfix-style bump
860
+ claude-hooks create-release major # Breaking change release
861
+ ```
862
+
863
+ | Flag | Effect |
864
+ |---|---|
865
+ | `--dry-run` | Preview all planned actions without making changes |
866
+ | `--no-shadow` | Skip shadow deployment after push |
867
+ | `--skip-push` | Keep everything local (implies no shadow) |
868
+ | `--update-changelog` | Generate and include CHANGELOG entry |
869
+
870
+ **Preconditions** (all checked automatically):
871
+ - Must be on `develop` branch
872
+ - Working directory must be clean
873
+ - `develop` must be up-to-date with remote
874
+ - `develop` must not be behind `main` (run `back-merge` first if so)
875
+ - No existing `release-candidate/*` branch on remote (run `close-release` first)
876
+
877
+ **Branch naming**: `release-candidate/V{version}` (capital V, e.g., `release-candidate/V2.31.0`).
878
+
879
+ ---
880
+
881
+ #### shadow
882
+
883
+ Manages the shadow branch — an ephemeral QA environment with its own pipeline. Three subcommands:
884
+
885
+ **shadow analyze** — Show divergence status:
886
+ ```bash
887
+ claude-hooks shadow analyze
888
+ ```
889
+ Shows how shadow compares to `main` and the active RC (commits ahead/behind). Use this to check if shadow needs a sync.
890
+
891
+ **shadow reset** — Recreate shadow from main:
892
+ ```bash
893
+ claude-hooks shadow reset # Destroys and recreates from main
894
+ claude-hooks shadow reset --dry-run # Preview only
895
+ ```
896
+ Used at the start/end of a release cycle. Destroys the current shadow branch and creates a fresh copy from main. Prompts for confirmation.
897
+
898
+ **shadow sync** — Merge a source branch into shadow:
899
+ ```bash
900
+ claude-hooks shadow sync release-candidate # Auto-detect latest RC
901
+ claude-hooks shadow sync release-candidate/V2.31.0 # Explicit RC version
902
+ claude-hooks shadow sync develop # Test develop in shadow
903
+ claude-hooks shadow sync feature/AUT-XXXX # Test a specific feature
904
+ claude-hooks shadow sync <source> --dry-run # Preview only
905
+ ```
906
+
907
+ | Flag | Effect |
908
+ |---|---|
909
+ | `--dry-run` | Preview sync without executing |
910
+
911
+ **Conflict resolution during sync**: version files → accept source; CHANGELOG → keep both (warn TL); other conflicts → abort merge and advise manual resolution.
912
+
913
+ ---
914
+
915
+ #### revert-feature
916
+
917
+ 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.
918
+
919
+ ```bash
920
+ claude-hooks revert-feature AUT-3179 # Find and revert
921
+ claude-hooks revert-feature AUT-3179 --update-shadow # Also re-deploy shadow
922
+ claude-hooks revert-feature AUT-3179 --dry-run # Preview only
923
+ ```
924
+
925
+ | Flag | Effect |
926
+ |---|---|
927
+ | `--dry-run` | Show what would be reverted without making changes |
928
+ | `--update-shadow` | After reverting, sync shadow with the updated RC |
929
+
930
+ **Preconditions**: Must be on a `release-candidate/*` branch. Working directory must be clean.
931
+
932
+ **Search behavior**:
933
+ - 0 matches → abort with "No commits found"
934
+ - 1 match → show details, confirm with user
935
+ - 2+ matches → interactive menu to select which commit
936
+
937
+ **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.
938
+
939
+ **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.
940
+
941
+ ---
942
+
943
+ #### close-release
944
+
945
+ 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.
946
+
947
+ ```bash
948
+ claude-hooks close-release # Auto-detect RC, prompt for description
949
+ claude-hooks close-release "cashflow + auth" # Explicit description
950
+ claude-hooks close-release --auto-describe # Claude generates 1-line summary
951
+ claude-hooks close-release --dry-run # Preview only
952
+ claude-hooks close-release --no-pr # Skip PR creation
953
+ ```
954
+
955
+ | Flag | Effect |
956
+ |---|---|
957
+ | `--dry-run` | Preview planned actions (reset, commit message, PR) without executing |
958
+ | `--auto-describe` | Use Claude (haiku, 60s timeout) to generate a description from feature list |
959
+ | `--no-pr` | Perform the reset + commit + force-push but skip GitHub PR creation |
960
+
961
+ **Description priority**: CLI argument → `--auto-describe` → interactive prompt (shows feature list, TL accepts or edits).
962
+
963
+ **Commit format**:
964
+ ```
965
+ Release v2.31.0: cashflow improvements + auth fixes
966
+
967
+ Includes:
968
+ - [AUT-3179] feat(cashflow): use Mailjet templates
969
+ - [AUT-3200] fix(cashflow): handle invalid template ID
970
+ ```
971
+
972
+ **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.
973
+
974
+ ---
975
+
976
+ #### back-merge
977
+
978
+ Post-deploy synchronization: tags the release on main, resets shadow, merges main into develop (with auto-conflict resolution), and cleans up the RC branch.
979
+
980
+ ```bash
981
+ claude-hooks back-merge # Full post-deploy (main → develop)
982
+ claude-hooks back-merge --from main --into develop # Explicit source/destination
983
+ claude-hooks back-merge --skip-tag # Already tagged manually
984
+ claude-hooks back-merge --skip-shadow # Skip shadow reset
985
+ claude-hooks back-merge --dry-run # Preview only
986
+ ```
987
+
988
+ | Flag | Effect |
989
+ |---|---|
990
+ | `--dry-run` | Preview all planned actions without executing |
991
+ | `--from <branch>` | Source branch (default: `main`) |
992
+ | `--into <branch>` | Destination branch (default: `develop`) |
993
+ | `--skip-tag` | Skip tag creation (useful if already tagged) |
994
+ | `--skip-shadow` | Skip shadow reset step |
995
+
996
+ **Auto-conflict resolution**:
997
+ | Conflict type | Resolution |
998
+ |---|---|
999
+ | Version files (pom.xml, package.json) | Accept source (released version) |
1000
+ | CHANGELOG.md | Keep both — destination on top, source below |
1001
+ | Other | Abort — TL resolves manually |
1002
+
1003
+ **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).
1004
+
1005
+ **Branch protection note**: `back-merge` pushes directly to `develop`. If branch protection requires PRs, you must temporarily disable it or have bypass permissions.
1006
+
1007
+ ---
1008
+
1009
+ #### create-pr (merge strategy awareness)
1010
+
1011
+ When creating a PR, the merge strategy is auto-detected from branch naming and displayed in the preview:
1012
+
1013
+ | PR direction | Strategy | Label |
1014
+ |---|---|---|
1015
+ | `feature/*` → `develop` | Squash merge | `merge-strategy:squash` |
1016
+ | `release-fix/*` → `release-candidate/*` | Squash merge | `merge-strategy:squash` |
1017
+ | `release-candidate/*` → `main` | Merge commit | `merge-strategy:merge-commit` |
1018
+ | `hotfix/*` → `main` | Merge commit | `merge-strategy:merge-commit` |
1019
+ | Any branch → `main` | Merge commit | `merge-strategy:merge-commit` |
1020
+ | Unknown pattern | Warning — user prompted to select | — |
1021
+
1022
+ For merge-commit PRs, a reminder is added to the PR body: `"> ⚠️ This PR must be merged with **merge commit** (not squash)"`.
1023
+
1024
+ ### Authorization
1025
+
1026
+ All workflow commands (except `check-coupling` and `create-pr`) are protected by role-based authorization:
1027
+
1028
+ - **Protected commands**: `create-release`, `close-release`, `back-merge`, `shadow`, `revert-feature`, `bump-version`
1029
+ - **Role hierarchy**: `developer` < `senior` < `tech-lead`
1030
+ - **Role mapping**: GitHub repo permission `push` → developer, `maintain` → senior, `admin` → tech-lead
1031
+ - **Permissions source**: `claude-hooks-permissions/permissions.json` repo (controlled by TL/Senior)
1032
+ - **Fail-closed**: Any auth error (no token, invalid token, network failure, repo not governed) blocks the command
1033
+
1034
+ If blocked: `"⛔ Command 'create-release' requires role 'senior'. Your role: developer. Contact your Tech Lead."`
1035
+
659
1036
  ## Useful Commands
660
1037
 
661
1038
  ### Development
@@ -704,6 +1081,29 @@ claude-hooks preset current # View current preset
704
1081
  claude-hooks analyze-diff [branch] # Analyze diff for PR
705
1082
  claude-hooks analyze-pr <pr-url> # Analyze GitHub PR with team guidelines
706
1083
  claude-hooks analyze-pr <url> --dry-run # Analyze without posting comments
1084
+ claude-hooks check-coupling # Detect coupled PRs targeting develop
1085
+ claude-hooks check-coupling --base main # Explicit base branch
1086
+ claude-hooks check-coupling --json # Machine-readable JSON output
1087
+ claude-hooks shadow analyze # Show shadow divergence vs main + active RC
1088
+ claude-hooks shadow reset # Destroy shadow and recreate from main
1089
+ claude-hooks shadow reset --dry-run # Preview reset without executing
1090
+ claude-hooks shadow sync release-candidate # Merge latest RC into shadow (auto-detect)
1091
+ claude-hooks shadow sync release-candidate/V2.7.0 # Merge explicit RC into shadow
1092
+ claude-hooks shadow sync develop # Merge develop into shadow
1093
+ claude-hooks shadow sync release-candidate --dry-run # Preview sync without executing
1094
+ claude-hooks create-release minor # Create RC branch, bump, push, shadow
1095
+ claude-hooks create-release minor --no-shadow # Skip shadow deployment
1096
+ claude-hooks create-release minor --dry-run # Preview only
1097
+ claude-hooks create-release minor --skip-push # Local only (skips shadow too)
1098
+ claude-hooks create-release minor --update-changelog # Include CHANGELOG
1099
+ claude-hooks revert-feature AUT-3179 # Find and revert by task ID
1100
+ claude-hooks revert-feature AUT-3179 --update-shadow # Revert and re-deploy shadow
1101
+ claude-hooks revert-feature AUT-3179 --dry-run # Preview only
1102
+ claude-hooks close-release # Auto-detect RC, prompt for description
1103
+ claude-hooks close-release "cashflow + auth" # Explicit description
1104
+ claude-hooks close-release --auto-describe # Claude generates summary
1105
+ claude-hooks close-release --dry-run # Preview only
1106
+ claude-hooks close-release --no-pr # Skip PR creation
707
1107
  claude-hooks create-pr [branch] # Create PR on GitHub
708
1108
  claude-hooks setup-github # Configure GitHub token
709
1109
  claude-hooks setup-linear # Configure Linear token