claude-git-hooks 2.18.0 → 2.19.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/CLAUDE.md +12 -8
  3. package/README.md +2 -1
  4. package/bin/claude-hooks +75 -89
  5. package/lib/cli-metadata.js +301 -0
  6. package/lib/commands/analyze-diff.js +12 -10
  7. package/lib/commands/analyze.js +9 -5
  8. package/lib/commands/bump-version.js +66 -43
  9. package/lib/commands/create-pr.js +71 -34
  10. package/lib/commands/debug.js +4 -7
  11. package/lib/commands/generate-changelog.js +11 -4
  12. package/lib/commands/help.js +47 -27
  13. package/lib/commands/helpers.js +66 -43
  14. package/lib/commands/hooks.js +15 -13
  15. package/lib/commands/install.js +546 -39
  16. package/lib/commands/migrate-config.js +8 -11
  17. package/lib/commands/presets.js +6 -13
  18. package/lib/commands/setup-github.js +12 -3
  19. package/lib/commands/telemetry-cmd.js +8 -6
  20. package/lib/commands/update.js +1 -2
  21. package/lib/config.js +36 -31
  22. package/lib/hooks/pre-commit.js +34 -54
  23. package/lib/hooks/prepare-commit-msg.js +39 -58
  24. package/lib/utils/analysis-engine.js +28 -21
  25. package/lib/utils/changelog-generator.js +162 -34
  26. package/lib/utils/claude-client.js +438 -377
  27. package/lib/utils/claude-diagnostics.js +20 -10
  28. package/lib/utils/file-operations.js +51 -79
  29. package/lib/utils/file-utils.js +46 -9
  30. package/lib/utils/git-operations.js +140 -123
  31. package/lib/utils/git-tag-manager.js +24 -23
  32. package/lib/utils/github-api.js +85 -61
  33. package/lib/utils/github-client.js +12 -14
  34. package/lib/utils/installation-diagnostics.js +4 -4
  35. package/lib/utils/interactive-ui.js +29 -17
  36. package/lib/utils/logger.js +4 -1
  37. package/lib/utils/pr-metadata-engine.js +67 -33
  38. package/lib/utils/preset-loader.js +20 -62
  39. package/lib/utils/prompt-builder.js +50 -55
  40. package/lib/utils/resolution-prompt.js +33 -44
  41. package/lib/utils/sanitize.js +20 -19
  42. package/lib/utils/task-id.js +27 -40
  43. package/lib/utils/telemetry.js +29 -17
  44. package/lib/utils/version-manager.js +173 -126
  45. package/lib/utils/which-command.js +23 -12
  46. package/package.json +69 -69
package/CHANGELOG.md CHANGED
@@ -5,6 +5,44 @@ Todos los cambios notables en este proyecto se documentarán en este archivo.
5
5
  El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.19.0] - 2026-03-06
9
+
10
+ ### ✨ Added
11
+ - Shell autocompletion for all CLI commands, flags, and arguments — Bash, Zsh, Fish, and PowerShell (#78)
12
+ - Command registry (`lib/cli-metadata.js`) — single source of truth for commands, flags, descriptions, and completion metadata
13
+ - Four shell completion generators with per-command flag/arg awareness and dynamic branch completion for `analyze-diff` and `create-pr`
14
+ - Completions auto-install during `claude-hooks install` and auto-remove during `claude-hooks uninstall`
15
+ - Self-healing RC file updates — reinstall detects and replaces stale source lines (e.g., Windows backslash paths) automatically
16
+
17
+ ### 🔧 Changed
18
+ - Refactored `bin/claude-hooks` — replaced 130-line switch/case with declarative command registry lookup via `buildCommandMap()`
19
+ - Shell source lines now use `$HOME`-relative paths for cross-platform compatibility (MINGW64, WSL, native Linux/macOS)
20
+ - Completion source lines written to both `~/.bashrc` and `~/.bash_profile` (MINGW64 reads `.bash_profile` first)
21
+ - PowerShell profile detection now queries `$PROFILE` directly via `powershell.exe`/`pwsh`, handling OneDrive-redirected and locale-specific Documents folders
22
+ - PowerShell completion script uses fully qualified .NET type names (`System.Management.Automation.CompletionResult`) for compatibility with dot-sourced scripts
23
+ - Updated CLAUDE.md with cli-metadata.js documentation, exports table, and Command Registry design pattern
24
+
25
+ ### 🐛 Fixed
26
+ - Bash completions failing on MINGW64/Git Bash due to Windows backslash paths in source lines
27
+ - PowerShell completions failing due to hardcoded Unix profile paths on Windows
28
+ - PowerShell 5.1 `-Native` completers not firing for npm `.ps1` shims — added proxy function wrapper that re-exposes the command as `Function` type
29
+ - PowerShell `[CompletionResult]` type accelerator not resolving in dot-sourced profile scripts
30
+
31
+
32
+ ## [2.18.1] - 2026-03-04
33
+
34
+ ### ✨ Added
35
+ - Monorepo support for CHANGELOG discovery - discovers all CHANGELOG.md files and prompts to select when multiple are found (#77)
36
+ - AI-powered help and issue creation system (#79)
37
+
38
+ ### 🔧 Changed
39
+ - Enhanced `walkDirectoryTree` utility function in file-utils.js for reusable directory traversal
40
+ - Updated documentation to reflect monorepo CHANGELOG discovery capabilities
41
+
42
+ ### 🐛 Fixed
43
+ - Cross-platform path handling in git hooks directory resolution (normalized path separators)
44
+
45
+
8
46
  ## [2.18.0] - 2026-03-03
9
47
 
10
48
  ### ✨ Added
package/CLAUDE.md CHANGED
@@ -45,8 +45,9 @@ This separation enables:
45
45
  ```
46
46
  claude-git-hooks/
47
47
  ├── bin/
48
- │ └── claude-hooks # Thin CLI router - argument parsing, command dispatch
48
+ │ └── claude-hooks # Thin CLI router - uses cli-metadata.js registry for dispatch
49
49
  ├── lib/
50
+ │ ├── cli-metadata.js # Command registry - single source of truth for CLI commands, flags, descriptions
50
51
  │ ├── config.js # Config system - load/merge with priority
51
52
  │ ├── commands/ # Command modules - one file per CLI command
52
53
  │ │ ├── helpers.js # Shared CLI utilities - colors, output, platform
@@ -235,15 +236,17 @@ Wait for both → consolidate results
235
236
 
236
237
  | Module | Purpose | Key Exports |
237
238
  | ------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
239
+ | `lib/cli-metadata.js` | Command registry | `commands`, `buildCommandMap()`, `generateCompletionData()`, `PRESET_NAMES`, `HOOK_NAMES`, `BUMP_TYPES` |
238
240
  | `lib/config.js` | Config system | `getConfig()` |
239
241
  | `analysis-engine.js` | Shared analysis logic | `buildFileData()`, `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `hasBlockingIssues()`, `hasAnyIssues()`, `displayResults()`, `displayIssueSummary()` (v2.13.0) |
240
242
  | `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `analyzeCodeParallel()`, `executeClaudeWithRetry()` |
241
243
  | `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` |
242
244
  | `git-operations.js` | Git abstractions | `getStagedFiles()`, `getUnstagedFiles()`, `getAllTrackedFiles()`, `getDiff()`, `getRepoRoot()`, `getBranchPushStatus()`, `pushBranch()`, `createCommit()`, `fetchRemote()`, `branchExists()`, `getRemoteBranches()`, `resolveBaseBranch()`, `getChangedFilesBetweenRefs()`, `getDiffBetweenRefs()`, `getCommitsBetweenRefs()` |
245
+ | `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
243
246
  | `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
244
247
  | `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
245
248
  | `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
246
- | `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()` (v2.12.0) |
249
+ | `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
247
250
  | `github-api.js` | Octokit integration | `createPullRequest()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()` |
248
251
  | `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` |
249
252
  | `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
@@ -255,12 +258,13 @@ Wait for both → consolidate results
255
258
 
256
259
  ### Design Patterns
257
260
 
258
- 1. **Command Pattern**: `lib/commands/*.js` - each CLI command is a self-contained module
259
- 2. **Factory Pattern**: `preset-loader.js` loads configurations dynamically per tech-stack
260
- 3. **Template Method**: `prompt-builder.js` builds prompts from markdown templates
261
- 4. **Strategy Pattern**: `claude-client.js` selects between sequential or parallel analysis
262
- 5. **Adapter Pattern**: `git-operations.js` abstracts git commands into JS functions
263
- 6. **Singleton Pattern**: `config.js` loads configuration once per execution
261
+ 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.
262
+ 2. **Command Pattern**: `lib/commands/*.js` - each CLI command is a self-contained module
263
+ 3. **Factory Pattern**: `preset-loader.js` loads configurations dynamically per tech-stack
264
+ 4. **Template Method**: `prompt-builder.js` builds prompts from markdown templates
265
+ 5. **Strategy Pattern**: `claude-client.js` selects between sequential or parallel analysis
266
+ 6. **Adapter Pattern**: `git-operations.js` abstracts git commands into JS functions
267
+ 7. **Singleton Pattern**: `config.js` loads configuration once per execution
264
268
 
265
269
  ### Key Data Flows
266
270
 
package/README.md CHANGED
@@ -157,7 +157,8 @@ claude-hooks generate-changelog --base-branch develop
157
157
  - Analyzes commits since last tag using Claude
158
158
  - Categorizes by Conventional Commits types (feat, fix, refactor, etc.)
159
159
  - Generates Keep a Changelog format entries
160
- - Updates CHANGELOG.md automatically
160
+ - Discovers all CHANGELOG.md files (monorepo aware); prompts to select when multiple found
161
+ - Updates the selected CHANGELOG.md automatically
161
162
  - Useful when `bump-version --update-changelog` fails
162
163
 
163
164
  ### Disable/Enable Hooks
package/bin/claude-hooks CHANGED
@@ -4,27 +4,12 @@
4
4
  * File: claude-hooks
5
5
  * Purpose: Main CLI entry point - thin router to command modules
6
6
  *
7
+ * Routes commands via cli-metadata.js registry.
7
8
  * All command implementations are in lib/commands/
8
- * This file only handles argument parsing and routing.
9
9
  */
10
10
 
11
11
  import { error } from '../lib/commands/helpers.js';
12
-
13
- // Import commands
14
- import { runInstall } from '../lib/commands/install.js';
15
- import { runEnable, runDisable, runStatus, runUninstall } from '../lib/commands/hooks.js';
16
- import { runAnalyze } from '../lib/commands/analyze.js';
17
- import { runAnalyzeDiff } from '../lib/commands/analyze-diff.js';
18
- import { runCreatePr } from '../lib/commands/create-pr.js';
19
- import { runSetupGitHub } from '../lib/commands/setup-github.js';
20
- import { runShowPresets, runSetPreset, runCurrentPreset } from '../lib/commands/presets.js';
21
- import { runUpdate } from '../lib/commands/update.js';
22
- import { runMigrateConfig } from '../lib/commands/migrate-config.js';
23
- import { runSetDebug } from '../lib/commands/debug.js';
24
- import { runShowTelemetry, runClearTelemetry } from '../lib/commands/telemetry-cmd.js';
25
- import { runShowHelp, runShowVersion } from '../lib/commands/help.js';
26
- import { runBumpVersion } from '../lib/commands/bump-version.js';
27
- import { runGenerateChangelog } from '../lib/commands/generate-changelog.js';
12
+ import { buildCommandMap } from '../lib/cli-metadata.js';
28
13
 
29
14
  /**
30
15
  * Main CLI router
@@ -33,97 +18,98 @@ async function main() {
33
18
  const args = process.argv.slice(2);
34
19
  const command = args[0];
35
20
 
36
- switch (command) {
37
- case 'install':
38
- await runInstall(args.slice(1));
39
- break;
40
- case 'update':
41
- await runUpdate();
42
- break;
43
- case 'uninstall':
44
- runUninstall();
45
- break;
46
- case 'enable':
47
- runEnable(args[1]);
48
- break;
49
- case 'disable':
50
- runDisable(args[1]);
51
- break;
52
- case 'status':
53
- runStatus();
54
- break;
55
- case 'analyze':
56
- await runAnalyze({
21
+ const commandMap = buildCommandMap();
22
+ const entry = commandMap.get(command);
23
+
24
+ // help / --help / -h / no command
25
+ if (command === undefined || command === '--help' || command === '-h') {
26
+ const { runShowHelp } = await import('../lib/commands/help.js');
27
+ await runShowHelp();
28
+ return;
29
+ }
30
+
31
+ if (!entry) {
32
+ error(`Unknown command: ${command}`);
33
+ const { runShowHelp } = await import('../lib/commands/help.js');
34
+ runShowHelp();
35
+ return;
36
+ }
37
+
38
+ // --- Commands with special argument handling ---
39
+
40
+ // analyze: translate flags to options object
41
+ if (entry.name === 'analyze') {
42
+ const handler = await entry.handler();
43
+ await handler({
57
44
  staged: !args.includes('--unstaged') && !args.includes('--all'),
58
45
  unstaged: args.includes('--unstaged'),
59
46
  all: args.includes('--all')
60
47
  });
61
- break;
62
- case 'analyze-diff':
63
- await runAnalyzeDiff(args.slice(1));
64
- break;
65
- case 'create-pr':
66
- await runCreatePr(args.slice(1));
67
- break;
68
- case 'setup-github':
69
- await runSetupGitHub();
70
- break;
71
- case 'presets':
72
- await runShowPresets();
73
- break;
74
- case '--set-preset':
75
- await runSetPreset(args[1]);
76
- break;
77
- case 'preset':
78
- // Handle subcommands: preset current
48
+ return;
49
+ }
50
+
51
+ // preset: subcommand routing
52
+ if (entry.name === 'preset') {
79
53
  if (args[1] === 'current') {
54
+ const { runCurrentPreset } = await import('../lib/commands/presets.js');
80
55
  await runCurrentPreset();
81
56
  } else {
82
57
  error(`Unknown preset subcommand: ${args[1]}`);
83
58
  }
84
- break;
85
- case 'migrate-config':
86
- await runMigrateConfig();
87
- break;
88
- case 'bump-version':
89
- await runBumpVersion(args.slice(1));
90
- break;
91
- case 'generate-changelog':
92
- await runGenerateChangelog(args.slice(1));
93
- break;
94
- case 'telemetry':
95
- // Handle subcommands: telemetry show, telemetry clear
59
+ return;
60
+ }
61
+
62
+ // telemetry: subcommand routing
63
+ if (entry.name === 'telemetry') {
96
64
  if (args[1] === 'show' || args[1] === undefined) {
65
+ const { runShowTelemetry } = await import('../lib/commands/telemetry-cmd.js');
97
66
  await runShowTelemetry();
98
67
  } else if (args[1] === 'clear') {
68
+ const { runClearTelemetry } = await import('../lib/commands/telemetry-cmd.js');
99
69
  await runClearTelemetry();
100
70
  } else {
101
71
  error(`Unknown telemetry subcommand: ${args[1]}`);
102
72
  }
103
- break;
104
- case '--debug':
105
- await runSetDebug(args[1]);
106
- break;
107
- case '--version':
108
- case '-v':
109
- case 'version':
110
- runShowVersion();
111
- break;
112
- case 'help':
113
- await runShowHelp(args.slice(1));
114
- break;
115
- case '--help':
116
- case '-h':
117
- case undefined:
118
- runShowHelp();
119
- break;
120
- default:
121
- error(`Unknown command: ${command}`);
122
- runShowHelp();
73
+ return;
74
+ }
75
+
76
+ // --- Commands that take args[1] as single argument ---
77
+ // enable/disable take optional hook name
78
+ if (entry.name === 'enable' || entry.name === 'disable') {
79
+ const handler = await entry.handler();
80
+ handler(args[1]);
81
+ return;
123
82
  }
83
+
84
+ // --set-preset takes preset name, --debug takes value
85
+ if (entry.name === '--set-preset' || entry.name === '--debug') {
86
+ const handler = await entry.handler();
87
+ await handler(args[1]);
88
+ return;
89
+ }
90
+
91
+ // --- Commands that take args.slice(1) ---
92
+ if (
93
+ [
94
+ 'install',
95
+ 'analyze-diff',
96
+ 'create-pr',
97
+ 'bump-version',
98
+ 'generate-changelog',
99
+ 'help'
100
+ ].includes(entry.name)
101
+ ) {
102
+ const handler = await entry.handler();
103
+ await handler(args.slice(1));
104
+ return;
105
+ }
106
+
107
+ // --- Commands with no arguments ---
108
+ const handler = await entry.handler();
109
+ await handler();
124
110
  }
125
111
 
126
112
  // Execute main
127
- main().catch(err => {
113
+ main().catch((err) => {
128
114
  error(`Unexpected error: ${err.message}`);
129
115
  });
@@ -0,0 +1,301 @@
1
+ /**
2
+ * File: cli-metadata.js
3
+ * Purpose: Single source of truth for CLI commands, flags, and descriptions
4
+ *
5
+ * Three consumers read from this registry:
6
+ * 1. bin/claude-hooks — routing (replaces switch/case)
7
+ * 2. installCompletions() — generates shell completion scripts at install time
8
+ * 3. showStaticHelp() — can derive help text from metadata
9
+ *
10
+ * Adding a new command = adding one entry here.
11
+ * Completions auto-update for every user at next install.
12
+ */
13
+
14
+ /**
15
+ * Available preset names for --set-preset completion
16
+ * @type {string[]}
17
+ */
18
+ export const PRESET_NAMES = ['ai', 'backend', 'database', 'default', 'frontend', 'fullstack'];
19
+
20
+ /**
21
+ * Hook names for enable/disable completion
22
+ * @type {string[]}
23
+ */
24
+ export const HOOK_NAMES = ['pre-commit', 'prepare-commit-msg'];
25
+
26
+ /**
27
+ * Bump types for bump-version completion
28
+ * @type {string[]}
29
+ */
30
+ export const BUMP_TYPES = ['major', 'minor', 'patch'];
31
+
32
+ /**
33
+ * @typedef {Object} CommandFlag
34
+ * @property {string} description - Flag description for completions
35
+ * @property {string[]} [values] - Completable values for the flag
36
+ * @property {boolean} [takesValue] - Whether the flag expects a value argument
37
+ */
38
+
39
+ /**
40
+ * @typedef {Object} CommandEntry
41
+ * @property {string} name - Primary command name
42
+ * @property {string[]} [aliases] - Alternative names (e.g., --version, -v)
43
+ * @property {string} description - Short description for completions and help
44
+ * @property {function(): Promise<function>} handler - Dynamic import returning handler function
45
+ * @property {Object<string, CommandFlag>} [flags] - Supported flags
46
+ * @property {string[]} [subcommands] - Available subcommands
47
+ * @property {Object} [args] - Positional argument info
48
+ * @property {string} [args.name] - Argument name
49
+ * @property {string[]} [args.values] - Completable values
50
+ * @property {string} [args.completion] - Shell command for dynamic completion
51
+ */
52
+
53
+ /**
54
+ * Command registry — all CLI commands defined declaratively
55
+ * @type {CommandEntry[]}
56
+ */
57
+ export const commands = [
58
+ {
59
+ name: 'install',
60
+ description: 'Install hooks in current repository',
61
+ handler: async () => (await import('./commands/install.js')).runInstall,
62
+ flags: {
63
+ '--force': { description: 'Reinstall even if already exist' },
64
+ '--skip-auth': { description: 'Skip Claude authentication check' }
65
+ }
66
+ },
67
+ {
68
+ name: 'update',
69
+ description: 'Update to latest version',
70
+ handler: async () => (await import('./commands/update.js')).runUpdate
71
+ },
72
+ {
73
+ name: 'uninstall',
74
+ description: 'Remove hooks from repository',
75
+ handler: async () => (await import('./commands/hooks.js')).runUninstall
76
+ },
77
+ {
78
+ name: 'enable',
79
+ description: 'Enable hooks (all or specific)',
80
+ handler: async () => (await import('./commands/hooks.js')).runEnable,
81
+ args: {
82
+ name: 'hook',
83
+ values: HOOK_NAMES
84
+ }
85
+ },
86
+ {
87
+ name: 'disable',
88
+ description: 'Disable hooks (all or specific)',
89
+ handler: async () => (await import('./commands/hooks.js')).runDisable,
90
+ args: {
91
+ name: 'hook',
92
+ values: HOOK_NAMES
93
+ }
94
+ },
95
+ {
96
+ name: 'status',
97
+ description: 'Show hook status',
98
+ handler: async () => (await import('./commands/hooks.js')).runStatus
99
+ },
100
+ {
101
+ name: 'analyze',
102
+ description: 'Analyze code interactively before committing',
103
+ handler: async () => (await import('./commands/analyze.js')).runAnalyze,
104
+ flags: {
105
+ '--unstaged': { description: 'Analyze unstaged changes' },
106
+ '--all': { description: 'Analyze all tracked files' }
107
+ }
108
+ },
109
+ {
110
+ name: 'analyze-diff',
111
+ description: 'Analyze diff and generate PR metadata',
112
+ handler: async () => (await import('./commands/analyze-diff.js')).runAnalyzeDiff,
113
+ args: {
114
+ name: 'base-branch',
115
+ completion: "git branch --format='%(refname:short)'"
116
+ }
117
+ },
118
+ {
119
+ name: 'create-pr',
120
+ description: 'Create PR with auto-generated metadata',
121
+ handler: async () => (await import('./commands/create-pr.js')).runCreatePr,
122
+ args: {
123
+ name: 'base-branch',
124
+ completion: "git branch --format='%(refname:short)'"
125
+ }
126
+ },
127
+ {
128
+ name: 'setup-github',
129
+ description: 'Configure GitHub token for PR creation',
130
+ handler: async () => (await import('./commands/setup-github.js')).runSetupGitHub
131
+ },
132
+ {
133
+ name: 'presets',
134
+ description: 'List all available presets',
135
+ handler: async () => (await import('./commands/presets.js')).runShowPresets
136
+ },
137
+ {
138
+ name: '--set-preset',
139
+ description: 'Set the active preset',
140
+ handler: async () => (await import('./commands/presets.js')).runSetPreset,
141
+ args: {
142
+ name: 'preset',
143
+ values: PRESET_NAMES
144
+ }
145
+ },
146
+ {
147
+ name: 'preset',
148
+ description: 'Preset management',
149
+ handler: async () => null,
150
+ subcommands: ['current']
151
+ },
152
+ {
153
+ name: 'migrate-config',
154
+ description: 'Migrate legacy config to v2.8.0 format',
155
+ handler: async () => (await import('./commands/migrate-config.js')).runMigrateConfig
156
+ },
157
+ {
158
+ name: 'bump-version',
159
+ description: 'Bump version with CHANGELOG and Git tag',
160
+ handler: async () => (await import('./commands/bump-version.js')).runBumpVersion,
161
+ args: {
162
+ name: 'type',
163
+ values: BUMP_TYPES
164
+ },
165
+ flags: {
166
+ '--dry-run': { description: 'Preview changes without applying' },
167
+ '--interactive': { description: 'Force file selection menu' },
168
+ '--no-commit': { description: 'Skip automatic commit' },
169
+ '--no-tag': { description: 'Skip Git tag creation' },
170
+ '--push': { description: 'Push tag to remote' },
171
+ '--remove-suffix': { description: 'Remove version suffix' },
172
+ '--set-suffix': { description: 'Set/replace version suffix', takesValue: true },
173
+ '--suffix': { description: 'Add version suffix (e.g., SNAPSHOT)', takesValue: true },
174
+ '--update-changelog': { description: 'Generate CHANGELOG entry' }
175
+ }
176
+ },
177
+ {
178
+ name: 'generate-changelog',
179
+ description: 'Generate CHANGELOG entry independently',
180
+ handler: async () =>
181
+ (await import('./commands/generate-changelog.js')).runGenerateChangelog,
182
+ flags: {
183
+ '--base-branch': { description: 'Compare against branch', takesValue: true },
184
+ '--release': { description: 'Mark as released' }
185
+ }
186
+ },
187
+ {
188
+ name: 'telemetry',
189
+ description: 'View or clear telemetry data',
190
+ handler: async () => null,
191
+ subcommands: ['show', 'clear']
192
+ },
193
+ {
194
+ name: '--debug',
195
+ description: 'Toggle debug mode',
196
+ handler: async () => (await import('./commands/debug.js')).runSetDebug,
197
+ args: {
198
+ name: 'value',
199
+ values: ['true', 'false', 'status']
200
+ }
201
+ },
202
+ {
203
+ name: 'version',
204
+ aliases: ['--version', '-v'],
205
+ description: 'Show current version',
206
+ handler: async () => (await import('./commands/help.js')).runShowVersion
207
+ },
208
+ {
209
+ name: 'help',
210
+ aliases: ['--help', '-h'],
211
+ description: 'Show help or ask AI a question',
212
+ handler: async () => (await import('./commands/help.js')).runShowHelp,
213
+ flags: {
214
+ '--report-issue': { description: 'Create GitHub issue interactively' }
215
+ }
216
+ }
217
+ ];
218
+
219
+ /**
220
+ * Build a lookup map from command name/alias to command entry
221
+ * @returns {Map<string, CommandEntry>}
222
+ */
223
+ export function buildCommandMap() {
224
+ const map = new Map();
225
+ for (const cmd of commands) {
226
+ map.set(cmd.name, cmd);
227
+ if (cmd.aliases) {
228
+ for (const alias of cmd.aliases) {
229
+ map.set(alias, cmd);
230
+ }
231
+ }
232
+ }
233
+ return map;
234
+ }
235
+
236
+ /**
237
+ * Generate flat completion data for shell script generators
238
+ *
239
+ * @returns {{ commands: string[], descriptions: Object<string, string>, flags: Object<string, string[]>, subcommands: Object<string, string[]>, argValues: Object<string, string[]>, argCompletions: Object<string, string>, flagValues: Object<string, Object<string, string[]>> }}
240
+ */
241
+ export function generateCompletionData() {
242
+ const data = {
243
+ commands: [],
244
+ descriptions: {},
245
+ flags: {},
246
+ subcommands: {},
247
+ argValues: {},
248
+ argCompletions: {},
249
+ flagValues: {}
250
+ };
251
+
252
+ for (const cmd of commands) {
253
+ // Skip alias-only entries (--version, --help mapped via version/help)
254
+ if (cmd.name.startsWith('-')) {
255
+ // Still include --set-preset and --debug as they are primary names
256
+ if (cmd.name !== '--set-preset' && cmd.name !== '--debug') {
257
+ continue;
258
+ }
259
+ }
260
+
261
+ data.commands.push(cmd.name);
262
+ data.descriptions[cmd.name] = cmd.description;
263
+
264
+ if (cmd.flags) {
265
+ data.flags[cmd.name] = Object.keys(cmd.flags);
266
+
267
+ // Collect flag-level values (e.g., --suffix takes a value)
268
+ const flagVals = {};
269
+ for (const [flag, meta] of Object.entries(cmd.flags)) {
270
+ if (meta.values) {
271
+ flagVals[flag] = meta.values;
272
+ }
273
+ }
274
+ if (Object.keys(flagVals).length > 0) {
275
+ data.flagValues[cmd.name] = flagVals;
276
+ }
277
+ }
278
+
279
+ if (cmd.subcommands) {
280
+ data.subcommands[cmd.name] = cmd.subcommands;
281
+ }
282
+
283
+ if (cmd.args) {
284
+ if (cmd.args.values) {
285
+ data.argValues[cmd.name] = cmd.args.values;
286
+ }
287
+ if (cmd.args.completion) {
288
+ data.argCompletions[cmd.name] = cmd.args.completion;
289
+ }
290
+ }
291
+ }
292
+
293
+ // Add aliases that users type directly
294
+ data.commands.push('--version', '-v', '--help', '-h');
295
+ data.descriptions['--version'] = 'Show current version';
296
+ data.descriptions['-v'] = 'Show current version';
297
+ data.descriptions['--help'] = 'Show help';
298
+ data.descriptions['-h'] = 'Show help';
299
+
300
+ return data;
301
+ }
@@ -7,12 +7,7 @@ import fs from 'fs';
7
7
  import { analyzeBranchForPR } from '../utils/pr-metadata-engine.js';
8
8
  import { getConfig } from '../config.js';
9
9
  import logger from '../utils/logger.js';
10
- import {
11
- colors,
12
- error,
13
- info,
14
- checkGitRepo
15
- } from './helpers.js';
10
+ import { colors, error, info, checkGitRepo } from './helpers.js';
16
11
 
17
12
  /**
18
13
  * Analyze-diff command
@@ -33,12 +28,18 @@ export async function runAnalyzeDiff(args) {
33
28
  // Parse target branch from arguments
34
29
  const targetBranch = args[0];
35
30
 
36
- info(targetBranch ? `Analyzing differences with ${targetBranch}...` : 'Analyzing differences...');
31
+ info(
32
+ targetBranch ? `Analyzing differences with ${targetBranch}...` : 'Analyzing differences...'
33
+ );
37
34
  const startTime = Date.now();
38
35
 
39
36
  try {
40
37
  // Call PR metadata engine
41
- const { success: engineSuccess, result, error: engineError } = await analyzeBranchForPR(targetBranch, {
38
+ const {
39
+ success: engineSuccess,
40
+ result,
41
+ error: engineError
42
+ } = await analyzeBranchForPR(targetBranch, {
42
43
  hook: 'analyze-diff'
43
44
  });
44
45
 
@@ -132,8 +133,9 @@ export async function runAnalyzeDiff(args) {
132
133
  console.log(` git branch -m ${result.suggestedBranchName}`);
133
134
  }
134
135
 
135
- console.log(`💡 ${colors.yellow}Tip:${colors.reset} Use this information to create your PR on GitHub.`);
136
-
136
+ console.log(
137
+ `💡 ${colors.yellow}Tip:${colors.reset} Use this information to create your PR on GitHub.`
138
+ );
137
139
  } catch (e) {
138
140
  error(`Error analyzing diff: ${e.message}`);
139
141
  }