claude-git-hooks 2.30.2 → 2.33.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/CHANGELOG.md +80 -9
- package/CLAUDE.md +139 -87
- package/README.md +117 -93
- package/lib/commands/close-release.js +7 -7
- package/lib/commands/create-pr.js +47 -21
- package/lib/commands/diff-batch-info.js +0 -9
- package/lib/commands/telemetry-cmd.js +0 -15
- package/lib/config.js +0 -1
- package/lib/hooks/pre-commit.js +43 -0
- package/lib/hooks/prepare-commit-msg.js +15 -0
- package/lib/utils/analysis-engine.js +10 -0
- package/lib/utils/authorization.js +6 -7
- package/lib/utils/github-api.js +92 -60
- package/lib/utils/github-client.js +5 -105
- package/lib/utils/judge.js +12 -5
- package/lib/utils/label-resolver.js +232 -0
- package/lib/utils/metrics.js +185 -0
- package/lib/utils/pr-statistics.js +15 -0
- package/lib/utils/remote-config.js +102 -0
- package/lib/utils/reviewer-selector.js +154 -0
- package/lib/utils/telemetry.js +12 -39
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -91,10 +91,12 @@ claude-git-hooks/
|
|
|
91
91
|
│ ├── logger.js # Logging system - centralized output, debug mode
|
|
92
92
|
│ ├── preset-loader.js # Preset system - load tech-stack configurations
|
|
93
93
|
│ ├── pr-metadata-engine.js # PR metadata generation - branch context, diff reduction, metadata (v2.14.0)
|
|
94
|
-
│ ├── github-api.js # Octokit integration - PR creation, PR analysis, token validation
|
|
95
|
-
│ ├── github-client.js # GitHub helpers -
|
|
96
|
-
│ ├──
|
|
94
|
+
│ ├── github-api.js # Octokit integration - PR creation, PR analysis, token validation, Teams API
|
|
95
|
+
│ ├── github-client.js # GitHub helpers - repo parsing, config-based reviewers (fallback)
|
|
96
|
+
│ ├── reviewer-selector.js # Team-based reviewer selection - resolve team members, exclude author (v2.32.0)
|
|
97
97
|
│ ├── authorization.js # Role-based authorization - PROTECTED_COMMANDS, authorizeCommand(), fail-closed (v2.23.0)
|
|
98
|
+
│ ├── remote-config.js # Remote config fetcher - cached JSON from git-hooks-config repo (v2.31.0)
|
|
99
|
+
│ ├── label-resolver.js # Label resolution - 5-rule engine with remote + local fallback (v2.31.0)
|
|
98
100
|
│ ├── token-store.js # Token persistence - centralized settings.local.json read/write
|
|
99
101
|
│ ├── linear-connector.js # Linear API - ticket context fetching with retry
|
|
100
102
|
│ ├── coupling-detector.js # Coupling algorithm - Union-Find transitive grouping (v2.23.0)
|
|
@@ -173,6 +175,12 @@ preset config (.claude/presets/{name}/config.json) ← HIGHEST PRIORITY
|
|
|
173
175
|
|
|
174
176
|
**Rationale**: User configures general preferences, preset provides tech-stack-specific overrides.
|
|
175
177
|
|
|
178
|
+
**Team-wide remote config** ([`mscope-S-L/git-hooks-config`](https://github.com/mscope-S-L/git-hooks-config)):
|
|
179
|
+
|
|
180
|
+
- `labels.json` — PR label rules (fetched by `remote-config.js`, consumed by `label-resolver.js`)
|
|
181
|
+
- `permissions.json` — role-based authorization (fetched directly by `authorization.js`, fail-closed)
|
|
182
|
+
- Changes take effect immediately across all governed repos — no tool update needed
|
|
183
|
+
|
|
176
184
|
**Config format (v2.8.0):**
|
|
177
185
|
|
|
178
186
|
```json
|
|
@@ -276,10 +284,10 @@ consolidateResults()
|
|
|
276
284
|
| `analyze-diff.js` | Diff analysis | `runAnalyzeDiff()` |
|
|
277
285
|
| `analyze-pr.js` | PR analysis from URL | `runAnalyzePr()`, `normalizeCategory()` |
|
|
278
286
|
| `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)
|
|
287
|
+
| `shadow.js` | Shadow branch management | `runShadow()` — routes to analyze/reset/sync subcommands (v2.24.0) |
|
|
288
|
+
| `create-release.js` | Release candidate creation | `runCreateRelease()` — RC branch from develop, version bump, push, shadow deploy (v2.27.0) |
|
|
289
|
+
| `revert-feature.js` | Feature revert in RC | `runRevertFeature()` — find by task-id, coupling check, revert, push, shadow sync (v2.28.0) |
|
|
290
|
+
| `close-release.js` | Release candidate closure | `runCloseRelease()` — soft-reset, single commit, force-push, PR to main (v2.29.0) |
|
|
283
291
|
| `create-pr.js` | PR creation | `runCreatePr()`, `detectMergeStrategy()` (private) |
|
|
284
292
|
| `bump-version.js` | Version management | `runBumpVersion()` |
|
|
285
293
|
| `generate-changelog.js` | CHANGELOG generation | `runGenerateChangelog()` |
|
|
@@ -295,34 +303,37 @@ consolidateResults()
|
|
|
295
303
|
|
|
296
304
|
**Utility Modules (`lib/utils/`):**
|
|
297
305
|
|
|
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
|
+
| Module | Purpose | Key Exports |
|
|
307
|
+
| ------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
308
|
+
| `lib/cli-metadata.js` | Command registry | `commands`, `buildCommandMap()`, `generateCompletionData()`, `PRESET_NAMES`, `HOOK_NAMES`, `BUMP_TYPES` |
|
|
309
|
+
| `lib/config.js` | Config system | `getConfig()` |
|
|
310
|
+
| `analysis-engine.js` | Shared analysis logic | `buildFileData()`, `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `hasBlockingIssues()`, `hasAnyIssues()`, `displayResults()`, `displayIssueSummary()` (v2.13.0+) |
|
|
311
|
+
| `diff-analysis-orchestrator.js` | Intelligent batch orchestration | `orchestrateBatches()`, `buildFileOverview()`, `detectDependencies()` (v2.20.0) |
|
|
312
|
+
| `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `executeClaudeWithRetry()`, `extractJSON()` — spawn, retry, model override |
|
|
313
|
+
| `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` — accepts `commonContext`, `batchRationale` |
|
|
306
314
|
| `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
|
-
| `
|
|
316
|
-
| `
|
|
317
|
-
| `
|
|
318
|
-
| `
|
|
319
|
-
| `
|
|
320
|
-
| `
|
|
321
|
-
| `
|
|
322
|
-
| `
|
|
323
|
-
| `
|
|
324
|
-
| `
|
|
325
|
-
| `
|
|
315
|
+
| `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
|
|
316
|
+
| `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
|
|
317
|
+
| `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
|
|
318
|
+
| `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
|
|
319
|
+
| `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
|
|
320
|
+
| `coupling-detector.js` | Coupling algorithm | `buildFileIndex()`, `getSharedFiles()`, `detectCouplingGroups()` — Union-Find transitive grouping (v2.23.0) |
|
|
321
|
+
| `github-api.js` | Octokit integration | `createPullRequest()`, `fetchPullRequest()`, `fetchPullRequestFiles()`, `createPullRequestReview()`, `parseGitHubPRUrl()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()`, `listOpenPullRequests()`, `listRepoTeams()`, `listTeamMembers()`, `getAuthenticatedUser()`, `checkOrgMembership()`, `getCollaboratorPermission()` |
|
|
322
|
+
| `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` — config-based reviewer fallback |
|
|
323
|
+
| `reviewer-selector.js` | Team-based reviewer selection | `selectReviewers()` — resolve team members via GitHub Teams API, exclude PR author, config fallback (v2.32.0) |
|
|
324
|
+
| `authorization.js` | Role-based authorization | `authorizeCommand()`, `requiresAuthorization()`, `getRequiredRole()`, `AuthorizationError`, `PROTECTED_COMMANDS` — fail-closed guard for workflow commands (v2.23.0) |
|
|
325
|
+
| `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) |
|
|
326
|
+
| `label-resolver.js` | Label resolution | `resolveLabels(context)` — 5-rule engine: preset, size, quality, strategy, defaults; remote config priority with local fallback (v2.31.0) |
|
|
327
|
+
| `token-store.js` | Token persistence | `loadToken()`, `saveToken()`, `hasToken()`, `loadLocalSettings()` |
|
|
328
|
+
| `linear-connector.js` | Linear integration | `loadLinearToken()`, `testConnection()`, `fetchTicket()`, `extractLinearTicketFromTitle()`, `parseLinearIdentifier()`, `LinearConnectorError` |
|
|
329
|
+
| `pr-statistics.js` | PR statistics | `recordPRAnalysis()` — write-only JSONL at `.claude/statistics/pr/stats.jsonl` |
|
|
330
|
+
| `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
|
|
331
|
+
| `task-id.js` | Task ID extraction | `getOrPromptTaskId()`, `formatWithTaskId()` |
|
|
332
|
+
| `logger.js` | Logging system | `info()`, `warning()`, `error()`, `debug()` |
|
|
333
|
+
| `judge.js` | Auto-fix judge | `judgeAndFix()`, `applyFix()` — LLM verdict + search/replace fixes (v2.20.0) |
|
|
334
|
+
| `resolution-prompt.js` | Issue resolution | `generateResolutionPrompt()` |
|
|
335
|
+
| `interactive-ui.js` | CLI UI components | `showPRPreview()`, `promptConfirmation()`, `promptMenu()`, `promptToggleList()`, `promptEditField()`, `promptUserConfirmation()` |
|
|
336
|
+
| `telemetry.js` | Local telemetry | `recordEvent()`, `displayStatistics()` |
|
|
326
337
|
|
|
327
338
|
### Design Patterns
|
|
328
339
|
|
|
@@ -334,8 +345,8 @@ consolidateResults()
|
|
|
334
345
|
6. **Orchestrator Pattern**: `diff-analysis-orchestrator.js` — Opus decides semantic grouping and model assignment; workers (analyzeCode per batch) execute in parallel
|
|
335
346
|
7. **Adapter Pattern**: `git-operations.js` abstracts git commands into JS functions
|
|
336
347
|
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/
|
|
338
|
-
10. **
|
|
348
|
+
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`
|
|
349
|
+
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` — callers receive config via dependency injection and decide fallback
|
|
339
350
|
|
|
340
351
|
### Key Data Flows
|
|
341
352
|
|
|
@@ -412,20 +423,28 @@ claude-hooks analyze-pr https://github.com/owner/repo/pull/123
|
|
|
412
423
|
→ recordPRAnalysis() → .claude/statistics/pr/stats.jsonl
|
|
413
424
|
```
|
|
414
425
|
|
|
415
|
-
**Flow 4: PR creation (with auto-push v2.11.0, engine v2.14.0, merge strategy v2.25.0)**
|
|
426
|
+
**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)**
|
|
416
427
|
|
|
417
428
|
```
|
|
418
429
|
claude-hooks create-pr develop
|
|
419
430
|
→ checks branch push status (unpublished/unpushed commits)
|
|
420
431
|
→ shows commit preview → prompts for confirmation
|
|
421
432
|
→ pushes branch to remote (if needed)
|
|
422
|
-
→
|
|
423
|
-
|
|
433
|
+
→ selectReviewers({ org, teamSlug, prAuthor, configReviewers }):
|
|
434
|
+
1. listTeamMembers(org, teamSlug) → resolve team (default: 'automation')
|
|
435
|
+
2. filter out prAuthor
|
|
436
|
+
3. if team empty or API fails → fall back to config.github.pr.reviewers
|
|
424
437
|
→ detectMergeStrategy(sourceBranch, targetBranch):
|
|
425
438
|
feature/* → squash | release-fix/* → squash
|
|
426
439
|
release-candidate/* → merge-commit | hotfix/* → merge-commit
|
|
427
440
|
any → main → merge-commit | unknown → user prompted to select
|
|
428
|
-
→
|
|
441
|
+
→ resolveLabels({ preset, fileCount, mergeStrategy, analysisResult, localLabelRules }):
|
|
442
|
+
1. preset labels (remote presetLabels or local fallback)
|
|
443
|
+
2. size labels: size:S (<10) | size:M (10-50) | size:L (50-100) | size:XL (>100)
|
|
444
|
+
3. quality labels: breaking-change, security, performance (from analysisResult)
|
|
445
|
+
4. strategy label: merge-strategy:squash or merge-strategy:merge-commit
|
|
446
|
+
5. default labels (from remote config only, e.g. needs-review)
|
|
447
|
+
→ prepends body reminder for merge-commit
|
|
429
448
|
→ analyzeBranchForPR() (pr-metadata-engine.js) → generates metadata
|
|
430
449
|
→ interactive preview + strategy info line → user confirms
|
|
431
450
|
→ Octokit creates PR on GitHub
|
|
@@ -487,6 +506,7 @@ claude-hooks create-release minor
|
|
|
487
506
|
```
|
|
488
507
|
|
|
489
508
|
**create-release design decisions:**
|
|
509
|
+
|
|
490
510
|
- Mismatch aborts non-interactively (automation context — ambiguity must be fixed by human)
|
|
491
511
|
- Tag creation mirrors `bump-version`: created on RC branch; if tag already exists → warn + skip (no interactive prompt)
|
|
492
512
|
- Shadow sync delegates to `runShadow(['sync', rcBranch])` (public API, reuses conflict-resolution logic from #93)
|
|
@@ -521,6 +541,7 @@ claude-hooks revert-feature AUT-3179
|
|
|
521
541
|
```
|
|
522
542
|
|
|
523
543
|
**revert-feature design decisions:**
|
|
544
|
+
|
|
524
545
|
- Coupling check is informational only — warnings shown before single confirmation (no second prompt)
|
|
525
546
|
- `Revert "..."` commits are skipped in coupling scan (they intentionally share files)
|
|
526
547
|
- `revert-log.json` schema: `[{ taskId, originalHash, revertHash, rcBranch, timestamp }]` — array to support multiple reverts; consumed by back-merge (#96)
|
|
@@ -553,8 +574,9 @@ claude-hooks close-release "cashflow + auth"
|
|
|
553
574
|
```
|
|
554
575
|
|
|
555
576
|
**close-release design decisions:**
|
|
577
|
+
|
|
556
578
|
- `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
|
|
579
|
+
- `process.exit()` calls are placed _outside_ try/catch blocks so that mock-based tests can observe them; the catch only handles git errors
|
|
558
580
|
- Description priority: CLI arg > `--auto-describe` (Claude haiku, 60s timeout) > `promptEditField` (shows list, TL edits)
|
|
559
581
|
- Auto-describe fallback: if Claude fails or feature list is empty, falls back to interactive prompt without aborting
|
|
560
582
|
- `close-release` was already in `PROTECTED_COMMANDS` — authorization.js unchanged
|
|
@@ -797,16 +819,16 @@ This section provides a practical guide for Tech Leads and Senior developers who
|
|
|
797
819
|
|
|
798
820
|
### Quick Reference — Release Commands
|
|
799
821
|
|
|
800
|
-
| Command
|
|
801
|
-
|
|
802
|
-
| `check-coupling` | Before cutting a release
|
|
803
|
-
| `create-release` | Tuesday release prep
|
|
804
|
-
| `shadow`
|
|
805
|
-
| `revert-feature` | During QA, feature fails
|
|
806
|
-
| `close-release`
|
|
807
|
-
| `back-merge`
|
|
808
|
-
| `create-pr`
|
|
809
|
-
| `bump-version`
|
|
822
|
+
| Command | When to use | What it does |
|
|
823
|
+
| ---------------- | --------------------------- | ------------------------------------------------------------------------ |
|
|
824
|
+
| `check-coupling` | Before cutting a release | Scans open PRs for shared files — reveals which features are coupled |
|
|
825
|
+
| `create-release` | Tuesday release prep | Creates RC branch from develop, bumps version, pushes, deploys to shadow |
|
|
826
|
+
| `shadow` | Throughout QA cycle | Manages the shadow (QA) branch — analyze status, reset, or sync with RC |
|
|
827
|
+
| `revert-feature` | During QA, feature fails | Finds and reverts a feature by task ID in the RC, with coupling warnings |
|
|
828
|
+
| `close-release` | QA passed, ready for deploy | Squashes RC into one commit, force-pushes, creates PR to main |
|
|
829
|
+
| `back-merge` | After production deploy | Tags release, resets shadow, merges main→develop, cleans up RC branch |
|
|
830
|
+
| `create-pr` | Any PR creation | Creates GitHub PR with auto-detected merge strategy and labels |
|
|
831
|
+
| `bump-version` | Manual version changes | Bumps version files, commits, tags — used outside the release cycle |
|
|
810
832
|
|
|
811
833
|
### Release Lifecycle — Step by Step
|
|
812
834
|
|
|
@@ -841,10 +863,10 @@ claude-hooks check-coupling --base main # PRs targeting main
|
|
|
841
863
|
claude-hooks check-coupling --json # Machine-readable output for CI
|
|
842
864
|
```
|
|
843
865
|
|
|
844
|
-
| Flag
|
|
845
|
-
|
|
846
|
-
| `--base <branch>` | Base branch to scan PRs against (default: `develop`)
|
|
847
|
-
| `--json`
|
|
866
|
+
| Flag | Effect |
|
|
867
|
+
| ----------------- | ------------------------------------------------------------------------------- |
|
|
868
|
+
| `--base <branch>` | Base branch to scan PRs against (default: `develop`) |
|
|
869
|
+
| `--json` | Output structured JSON with `coupledGroups`, `independentPRNumbers`, `warnings` |
|
|
848
870
|
|
|
849
871
|
**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
872
|
|
|
@@ -860,14 +882,15 @@ claude-hooks create-release patch # Hotfix-style bump
|
|
|
860
882
|
claude-hooks create-release major # Breaking change release
|
|
861
883
|
```
|
|
862
884
|
|
|
863
|
-
| Flag
|
|
864
|
-
|
|
865
|
-
| `--dry-run`
|
|
866
|
-
| `--no-shadow`
|
|
867
|
-
| `--skip-push`
|
|
868
|
-
| `--update-changelog` | Generate and include CHANGELOG entry
|
|
885
|
+
| Flag | Effect |
|
|
886
|
+
| -------------------- | -------------------------------------------------- |
|
|
887
|
+
| `--dry-run` | Preview all planned actions without making changes |
|
|
888
|
+
| `--no-shadow` | Skip shadow deployment after push |
|
|
889
|
+
| `--skip-push` | Keep everything local (implies no shadow) |
|
|
890
|
+
| `--update-changelog` | Generate and include CHANGELOG entry |
|
|
869
891
|
|
|
870
892
|
**Preconditions** (all checked automatically):
|
|
893
|
+
|
|
871
894
|
- Must be on `develop` branch
|
|
872
895
|
- Working directory must be clean
|
|
873
896
|
- `develop` must be up-to-date with remote
|
|
@@ -883,19 +906,24 @@ claude-hooks create-release major # Breaking change release
|
|
|
883
906
|
Manages the shadow branch — an ephemeral QA environment with its own pipeline. Three subcommands:
|
|
884
907
|
|
|
885
908
|
**shadow analyze** — Show divergence status:
|
|
909
|
+
|
|
886
910
|
```bash
|
|
887
911
|
claude-hooks shadow analyze
|
|
888
912
|
```
|
|
913
|
+
|
|
889
914
|
Shows how shadow compares to `main` and the active RC (commits ahead/behind). Use this to check if shadow needs a sync.
|
|
890
915
|
|
|
891
916
|
**shadow reset** — Recreate shadow from main:
|
|
917
|
+
|
|
892
918
|
```bash
|
|
893
919
|
claude-hooks shadow reset # Destroys and recreates from main
|
|
894
920
|
claude-hooks shadow reset --dry-run # Preview only
|
|
895
921
|
```
|
|
922
|
+
|
|
896
923
|
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
924
|
|
|
898
925
|
**shadow sync** — Merge a source branch into shadow:
|
|
926
|
+
|
|
899
927
|
```bash
|
|
900
928
|
claude-hooks shadow sync release-candidate # Auto-detect latest RC
|
|
901
929
|
claude-hooks shadow sync release-candidate/V2.31.0 # Explicit RC version
|
|
@@ -904,8 +932,8 @@ claude-hooks shadow sync feature/AUT-XXXX # Test a specific feature
|
|
|
904
932
|
claude-hooks shadow sync <source> --dry-run # Preview only
|
|
905
933
|
```
|
|
906
934
|
|
|
907
|
-
| Flag
|
|
908
|
-
|
|
935
|
+
| Flag | Effect |
|
|
936
|
+
| ----------- | ------------------------------ |
|
|
909
937
|
| `--dry-run` | Preview sync without executing |
|
|
910
938
|
|
|
911
939
|
**Conflict resolution during sync**: version files → accept source; CHANGELOG → keep both (warn TL); other conflicts → abort merge and advise manual resolution.
|
|
@@ -922,14 +950,15 @@ claude-hooks revert-feature AUT-3179 --update-shadow # Also re-deploy shadow
|
|
|
922
950
|
claude-hooks revert-feature AUT-3179 --dry-run # Preview only
|
|
923
951
|
```
|
|
924
952
|
|
|
925
|
-
| Flag
|
|
926
|
-
|
|
927
|
-
| `--dry-run`
|
|
928
|
-
| `--update-shadow` | After reverting, sync shadow with the updated RC
|
|
953
|
+
| Flag | Effect |
|
|
954
|
+
| ----------------- | -------------------------------------------------- |
|
|
955
|
+
| `--dry-run` | Show what would be reverted without making changes |
|
|
956
|
+
| `--update-shadow` | After reverting, sync shadow with the updated RC |
|
|
929
957
|
|
|
930
958
|
**Preconditions**: Must be on a `release-candidate/*` branch. Working directory must be clean.
|
|
931
959
|
|
|
932
960
|
**Search behavior**:
|
|
961
|
+
|
|
933
962
|
- 0 matches → abort with "No commits found"
|
|
934
963
|
- 1 match → show details, confirm with user
|
|
935
964
|
- 2+ matches → interactive menu to select which commit
|
|
@@ -952,15 +981,16 @@ claude-hooks close-release --dry-run # Preview only
|
|
|
952
981
|
claude-hooks close-release --no-pr # Skip PR creation
|
|
953
982
|
```
|
|
954
983
|
|
|
955
|
-
| Flag
|
|
956
|
-
|
|
957
|
-
| `--dry-run`
|
|
984
|
+
| Flag | Effect |
|
|
985
|
+
| ----------------- | --------------------------------------------------------------------------- |
|
|
986
|
+
| `--dry-run` | Preview planned actions (reset, commit message, PR) without executing |
|
|
958
987
|
| `--auto-describe` | Use Claude (haiku, 60s timeout) to generate a description from feature list |
|
|
959
|
-
| `--no-pr`
|
|
988
|
+
| `--no-pr` | Perform the reset + commit + force-push but skip GitHub PR creation |
|
|
960
989
|
|
|
961
990
|
**Description priority**: CLI argument → `--auto-describe` → interactive prompt (shows feature list, TL accepts or edits).
|
|
962
991
|
|
|
963
992
|
**Commit format**:
|
|
993
|
+
|
|
964
994
|
```
|
|
965
995
|
Release v2.31.0: cashflow improvements + auth fixes
|
|
966
996
|
|
|
@@ -985,13 +1015,13 @@ claude-hooks back-merge --skip-shadow # Skip shadow reset
|
|
|
985
1015
|
claude-hooks back-merge --dry-run # Preview only
|
|
986
1016
|
```
|
|
987
1017
|
|
|
988
|
-
| Flag
|
|
989
|
-
|
|
990
|
-
| `--dry-run`
|
|
991
|
-
| `--from <branch>` | Source branch (default: `main`)
|
|
992
|
-
| `--into <branch>` | Destination branch (default: `develop`)
|
|
993
|
-
| `--skip-tag`
|
|
994
|
-
| `--skip-shadow`
|
|
1018
|
+
| Flag | Effect |
|
|
1019
|
+
| ----------------- | --------------------------------------------- |
|
|
1020
|
+
| `--dry-run` | Preview all planned actions without executing |
|
|
1021
|
+
| `--from <branch>` | Source branch (default: `main`) |
|
|
1022
|
+
| `--into <branch>` | Destination branch (default: `develop`) |
|
|
1023
|
+
| `--skip-tag` | Skip tag creation (useful if already tagged) |
|
|
1024
|
+
| `--skip-shadow` | Skip shadow reset step |
|
|
995
1025
|
|
|
996
1026
|
**Auto-conflict resolution**:
|
|
997
1027
|
| Conflict type | Resolution |
|
|
@@ -1010,14 +1040,14 @@ claude-hooks back-merge --dry-run # Preview only
|
|
|
1010
1040
|
|
|
1011
1041
|
When creating a PR, the merge strategy is auto-detected from branch naming and displayed in the preview:
|
|
1012
1042
|
|
|
1013
|
-
| PR direction
|
|
1014
|
-
|
|
1015
|
-
| `feature/*` → `develop`
|
|
1016
|
-
| `release-fix/*` → `release-candidate/*` | Squash merge
|
|
1017
|
-
| `release-candidate/*` → `main`
|
|
1018
|
-
| `hotfix/*` → `main`
|
|
1019
|
-
| Any branch → `main`
|
|
1020
|
-
| Unknown pattern
|
|
1043
|
+
| PR direction | Strategy | Label |
|
|
1044
|
+
| --------------------------------------- | --------------------------------- | ----------------------------- |
|
|
1045
|
+
| `feature/*` → `develop` | Squash merge | `merge-strategy:squash` |
|
|
1046
|
+
| `release-fix/*` → `release-candidate/*` | Squash merge | `merge-strategy:squash` |
|
|
1047
|
+
| `release-candidate/*` → `main` | Merge commit | `merge-strategy:merge-commit` |
|
|
1048
|
+
| `hotfix/*` → `main` | Merge commit | `merge-strategy:merge-commit` |
|
|
1049
|
+
| Any branch → `main` | Merge commit | `merge-strategy:merge-commit` |
|
|
1050
|
+
| Unknown pattern | Warning — user prompted to select | — |
|
|
1021
1051
|
|
|
1022
1052
|
For merge-commit PRs, a reminder is added to the PR body: `"> ⚠️ This PR must be merged with **merge commit** (not squash)"`.
|
|
1023
1053
|
|
|
@@ -1028,7 +1058,7 @@ All workflow commands (except `check-coupling` and `create-pr`) are protected by
|
|
|
1028
1058
|
- **Protected commands**: `create-release`, `close-release`, `back-merge`, `shadow`, `revert-feature`, `bump-version`
|
|
1029
1059
|
- **Role hierarchy**: `developer` < `senior` < `tech-lead`
|
|
1030
1060
|
- **Role mapping**: GitHub repo permission `push` → developer, `maintain` → senior, `admin` → tech-lead
|
|
1031
|
-
- **Permissions source**: `
|
|
1061
|
+
- **Permissions source**: `git-hooks-config/permissions.json` repo (controlled by TL/Senior)
|
|
1032
1062
|
- **Fail-closed**: Any auth error (no token, invalid token, network failure, repo not governed) blocks the command
|
|
1033
1063
|
|
|
1034
1064
|
If blocked: `"⛔ Command 'create-release' requires role 'senior'. Your role: developer. Contact your Tech Lead."`
|
|
@@ -1210,6 +1240,28 @@ No enforced threshold. Focus on critical paths:
|
|
|
1210
1240
|
- `git-operations.js` - Git command abstraction
|
|
1211
1241
|
- `config.js` - Configuration merging
|
|
1212
1242
|
|
|
1243
|
+
## Proven Implementation Patterns
|
|
1244
|
+
|
|
1245
|
+
Recurring patterns validated across 15+ automation sessions. Apply these in new code to avoid known pitfalls.
|
|
1246
|
+
|
|
1247
|
+
### Testability
|
|
1248
|
+
|
|
1249
|
+
- **`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.
|
|
1250
|
+
- **`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.
|
|
1251
|
+
- **`_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`.
|
|
1252
|
+
|
|
1253
|
+
### Git Operations
|
|
1254
|
+
|
|
1255
|
+
- **`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.
|
|
1256
|
+
- **`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).
|
|
1257
|
+
- **`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.
|
|
1258
|
+
|
|
1259
|
+
### Architecture
|
|
1260
|
+
|
|
1261
|
+
- **`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.
|
|
1262
|
+
- **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.
|
|
1263
|
+
- **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.
|
|
1264
|
+
|
|
1213
1265
|
## Restrictions
|
|
1214
1266
|
|
|
1215
1267
|
### What Claude should NOT do in this repository
|