claude-git-hooks 2.18.1 → 2.20.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 (54) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/CLAUDE.md +85 -38
  3. package/README.md +52 -18
  4. package/bin/claude-hooks +75 -89
  5. package/lib/cli-metadata.js +306 -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 +56 -40
  9. package/lib/commands/create-pr.js +71 -34
  10. package/lib/commands/debug.js +4 -7
  11. package/lib/commands/diff-batch-info.js +105 -0
  12. package/lib/commands/generate-changelog.js +3 -2
  13. package/lib/commands/help.js +47 -27
  14. package/lib/commands/helpers.js +66 -43
  15. package/lib/commands/hooks.js +15 -13
  16. package/lib/commands/install.js +546 -49
  17. package/lib/commands/migrate-config.js +8 -11
  18. package/lib/commands/presets.js +6 -13
  19. package/lib/commands/setup-github.js +12 -3
  20. package/lib/commands/telemetry-cmd.js +8 -6
  21. package/lib/commands/update.js +1 -2
  22. package/lib/config.js +36 -52
  23. package/lib/hooks/pre-commit.js +70 -64
  24. package/lib/hooks/prepare-commit-msg.js +35 -75
  25. package/lib/utils/analysis-engine.js +77 -54
  26. package/lib/utils/changelog-generator.js +63 -37
  27. package/lib/utils/claude-client.js +447 -438
  28. package/lib/utils/claude-diagnostics.js +20 -10
  29. package/lib/utils/diff-analysis-orchestrator.js +332 -0
  30. package/lib/utils/file-operations.js +51 -79
  31. package/lib/utils/file-utils.js +6 -7
  32. package/lib/utils/git-operations.js +140 -123
  33. package/lib/utils/git-tag-manager.js +24 -23
  34. package/lib/utils/github-api.js +85 -61
  35. package/lib/utils/github-client.js +12 -14
  36. package/lib/utils/installation-diagnostics.js +4 -4
  37. package/lib/utils/interactive-ui.js +29 -17
  38. package/lib/utils/judge.js +195 -0
  39. package/lib/utils/logger.js +4 -1
  40. package/lib/utils/package-info.js +0 -11
  41. package/lib/utils/pr-metadata-engine.js +67 -33
  42. package/lib/utils/preset-loader.js +20 -62
  43. package/lib/utils/prompt-builder.js +57 -68
  44. package/lib/utils/resolution-prompt.js +34 -52
  45. package/lib/utils/sanitize.js +20 -19
  46. package/lib/utils/task-id.js +27 -40
  47. package/lib/utils/telemetry.js +73 -25
  48. package/lib/utils/version-manager.js +147 -70
  49. package/lib/utils/which-command.js +23 -12
  50. package/package.json +1 -1
  51. package/templates/CLAUDE_RESOLUTION_PROMPT.md +17 -9
  52. package/templates/DIFF_ANALYSIS_ORCHESTRATION_PROMPT.md +70 -0
  53. package/templates/config.advanced.example.json +15 -31
  54. package/templates/config.example.json +0 -11
@@ -0,0 +1,306 @@
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: 'batch-info',
189
+ description: 'Show intelligent diff-analysis orchestration configuration and speed telemetry',
190
+ handler: async () => (await import('./commands/diff-batch-info.js')).runDiffBatchInfo
191
+ },
192
+ {
193
+ name: 'telemetry',
194
+ description: 'View or clear telemetry data',
195
+ handler: async () => null,
196
+ subcommands: ['show', 'clear']
197
+ },
198
+ {
199
+ name: '--debug',
200
+ description: 'Toggle debug mode',
201
+ handler: async () => (await import('./commands/debug.js')).runSetDebug,
202
+ args: {
203
+ name: 'value',
204
+ values: ['true', 'false', 'status']
205
+ }
206
+ },
207
+ {
208
+ name: 'version',
209
+ aliases: ['--version', '-v'],
210
+ description: 'Show current version',
211
+ handler: async () => (await import('./commands/help.js')).runShowVersion
212
+ },
213
+ {
214
+ name: 'help',
215
+ aliases: ['--help', '-h'],
216
+ description: 'Show help or ask AI a question',
217
+ handler: async () => (await import('./commands/help.js')).runShowHelp,
218
+ flags: {
219
+ '--report-issue': { description: 'Create GitHub issue interactively' }
220
+ }
221
+ }
222
+ ];
223
+
224
+ /**
225
+ * Build a lookup map from command name/alias to command entry
226
+ * @returns {Map<string, CommandEntry>}
227
+ */
228
+ export function buildCommandMap() {
229
+ const map = new Map();
230
+ for (const cmd of commands) {
231
+ map.set(cmd.name, cmd);
232
+ if (cmd.aliases) {
233
+ for (const alias of cmd.aliases) {
234
+ map.set(alias, cmd);
235
+ }
236
+ }
237
+ }
238
+ return map;
239
+ }
240
+
241
+ /**
242
+ * Generate flat completion data for shell script generators
243
+ *
244
+ * @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[]>> }}
245
+ */
246
+ export function generateCompletionData() {
247
+ const data = {
248
+ commands: [],
249
+ descriptions: {},
250
+ flags: {},
251
+ subcommands: {},
252
+ argValues: {},
253
+ argCompletions: {},
254
+ flagValues: {}
255
+ };
256
+
257
+ for (const cmd of commands) {
258
+ // Skip alias-only entries (--version, --help mapped via version/help)
259
+ if (cmd.name.startsWith('-')) {
260
+ // Still include --set-preset and --debug as they are primary names
261
+ if (cmd.name !== '--set-preset' && cmd.name !== '--debug') {
262
+ continue;
263
+ }
264
+ }
265
+
266
+ data.commands.push(cmd.name);
267
+ data.descriptions[cmd.name] = cmd.description;
268
+
269
+ if (cmd.flags) {
270
+ data.flags[cmd.name] = Object.keys(cmd.flags);
271
+
272
+ // Collect flag-level values (e.g., --suffix takes a value)
273
+ const flagVals = {};
274
+ for (const [flag, meta] of Object.entries(cmd.flags)) {
275
+ if (meta.values) {
276
+ flagVals[flag] = meta.values;
277
+ }
278
+ }
279
+ if (Object.keys(flagVals).length > 0) {
280
+ data.flagValues[cmd.name] = flagVals;
281
+ }
282
+ }
283
+
284
+ if (cmd.subcommands) {
285
+ data.subcommands[cmd.name] = cmd.subcommands;
286
+ }
287
+
288
+ if (cmd.args) {
289
+ if (cmd.args.values) {
290
+ data.argValues[cmd.name] = cmd.args.values;
291
+ }
292
+ if (cmd.args.completion) {
293
+ data.argCompletions[cmd.name] = cmd.args.completion;
294
+ }
295
+ }
296
+ }
297
+
298
+ // Add aliases that users type directly
299
+ data.commands.push('--version', '-v', '--help', '-h');
300
+ data.descriptions['--version'] = 'Show current version';
301
+ data.descriptions['-v'] = 'Show current version';
302
+ data.descriptions['--help'] = 'Show help';
303
+ data.descriptions['-h'] = 'Show help';
304
+
305
+ return data;
306
+ }
@@ -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
  }
@@ -19,7 +19,12 @@
19
19
  * claude-hooks analyze --all # Analyze all tracked files
20
20
  */
21
21
 
22
- import { getStagedFiles, getUnstagedFiles, getAllTrackedFiles, createCommit } from '../utils/git-operations.js';
22
+ import {
23
+ getStagedFiles,
24
+ getUnstagedFiles,
25
+ getAllTrackedFiles,
26
+ createCommit
27
+ } from '../utils/git-operations.js';
23
28
  import { filterFiles } from '../utils/file-operations.js';
24
29
  import {
25
30
  buildFilesData,
@@ -97,12 +102,12 @@ export const runAnalyze = async (options = {}) => {
97
102
  extensions: allowedExtensions
98
103
  });
99
104
 
100
- const validFiles = filteredFiles.filter(f => f.valid);
101
- const invalidFiles = filteredFiles.filter(f => !f.valid);
105
+ const validFiles = filteredFiles.filter((f) => f.valid);
106
+ const invalidFiles = filteredFiles.filter((f) => !f.valid);
102
107
 
103
108
  // Show warnings for skipped files
104
109
  if (invalidFiles.length > 0) {
105
- invalidFiles.forEach(file => {
110
+ invalidFiles.forEach((file) => {
106
111
  logger.warning(`Skipping ${file.path}: ${file.reason}`);
107
112
  });
108
113
  }
@@ -208,7 +213,6 @@ export const runAnalyze = async (options = {}) => {
208
213
  process.exit(1);
209
214
  }
210
215
  }
211
-
212
216
  } catch (err) {
213
217
  logger.error('analyze', 'Analysis failed', err);
214
218
  error(`Analysis failed: ${err.message}`);
@@ -27,12 +27,7 @@ import {
27
27
  updateVersionFiles,
28
28
  validateVersionFormat
29
29
  } from '../utils/version-manager.js';
30
- import {
31
- createTag,
32
- pushTags,
33
- tagExists,
34
- formatTagName
35
- } from '../utils/git-tag-manager.js';
30
+ import { createTag, pushTags, tagExists, formatTagName } from '../utils/git-tag-manager.js';
36
31
  import {
37
32
  generateChangelogEntry,
38
33
  updateChangelogFile,
@@ -58,11 +53,7 @@ import {
58
53
  promptEditField
59
54
  } from '../utils/interactive-ui.js';
60
55
  import logger from '../utils/logger.js';
61
- import {
62
- colors,
63
- error,
64
- checkGitRepo
65
- } from './helpers.js';
56
+ import { colors, error, checkGitRepo } from './helpers.js';
66
57
 
67
58
  /**
68
59
  * Validates prerequisites before version bump
@@ -111,7 +102,6 @@ function validatePrerequisites() {
111
102
  });
112
103
 
113
104
  return { valid, errors };
114
-
115
105
  } catch (err) {
116
106
  logger.error('bump-version - validatePrerequisites', 'Validation failed', err);
117
107
  return {
@@ -204,9 +194,15 @@ function parseArguments(args) {
204
194
  */
205
195
  function displayDiscoveryTable(discovery) {
206
196
  console.log('');
207
- console.log(`${colors.blue}═══════════════════════════════════════════════════════════════${colors.reset}`);
208
- console.log(`${colors.blue} Version Files Discovery ${colors.reset}`);
209
- console.log(`${colors.blue}═══════════════════════════════════════════════════════════════${colors.reset}`);
197
+ console.log(
198
+ `${colors.blue}═══════════════════════════════════════════════════════════════${colors.reset}`
199
+ );
200
+ console.log(
201
+ `${colors.blue} Version Files Discovery ${colors.reset}`
202
+ );
203
+ console.log(
204
+ `${colors.blue}═══════════════════════════════════════════════════════════════${colors.reset}`
205
+ );
210
206
  console.log('');
211
207
 
212
208
  if (discovery.files.length === 0) {
@@ -216,7 +212,9 @@ function displayDiscoveryTable(discovery) {
216
212
  }
217
213
 
218
214
  // Table header
219
- console.log(` ${'#'.padEnd(3)} ${'File'.padEnd(35)} ${'Type'.padEnd(15)} ${'Version'.padEnd(15)}`);
215
+ console.log(
216
+ ` ${'#'.padEnd(3)} ${'File'.padEnd(35)} ${'Type'.padEnd(15)} ${'Version'.padEnd(15)}`
217
+ );
220
218
  console.log(` ${'-'.repeat(3)} ${'-'.repeat(35)} ${'-'.repeat(15)} ${'-'.repeat(15)}`);
221
219
 
222
220
  // Table rows
@@ -225,7 +223,8 @@ function displayDiscoveryTable(discovery) {
225
223
  const filePath = file.relativePath.padEnd(35);
226
224
  const fileType = file.projectLabel.padEnd(15);
227
225
  const version = file.version ? file.version.padEnd(15) : 'N/A'.padEnd(15);
228
- const mismatchMarker = discovery.mismatch && file.version !== discovery.resolvedVersion ? ' ⚠️ ' : '';
226
+ const mismatchMarker =
227
+ discovery.mismatch && file.version !== discovery.resolvedVersion ? ' ⚠️ ' : '';
229
228
 
230
229
  console.log(` ${num} ${filePath} ${fileType} ${version}${mismatchMarker}`);
231
230
  });
@@ -249,7 +248,9 @@ function displayDiscoveryTable(discovery) {
249
248
  function showDryRunPreview(info) {
250
249
  console.log('');
251
250
  console.log(`${colors.yellow}═════════════════════════════════════════════════${colors.reset}`);
252
- console.log(`${colors.yellow} DRY RUN - No changes will be made ${colors.reset}`);
251
+ console.log(
252
+ `${colors.yellow} DRY RUN - No changes will be made ${colors.reset}`
253
+ );
253
254
  console.log(`${colors.yellow}═════════════════════════════════════════════════${colors.reset}`);
254
255
  console.log('');
255
256
 
@@ -263,7 +264,7 @@ function showDryRunPreview(info) {
263
264
 
264
265
  if (info.selectedFiles.length > 0) {
265
266
  console.log(`${colors.blue}Files to update:${colors.reset}`);
266
- info.selectedFiles.forEach(file => {
267
+ info.selectedFiles.forEach((file) => {
267
268
  const targetVer = file.targetVersion || info.newVersion;
268
269
  console.log(` - ${file.relativePath} (${file.version} → ${targetVer})`);
269
270
  });
@@ -397,7 +398,9 @@ export async function runBumpVersion(args) {
397
398
  console.log(' --remove-suffix Remove version suffix (2.15.5-SNAPSHOT → 2.15.5)');
398
399
  console.log(' --set-suffix <value> Set/replace suffix (2.15.5 → 2.15.5-SNAPSHOT)');
399
400
  console.log(' --interactive Force interactive file selection menu');
400
- console.log(' --update-changelog [branch] Generate CHANGELOG entry (default branch: main)');
401
+ console.log(
402
+ ' --update-changelog [branch] Generate CHANGELOG entry (default branch: main)'
403
+ );
401
404
  console.log(' --dry-run Preview changes without applying');
402
405
  console.log(' --no-tag Skip Git tag creation');
403
406
  console.log(' --push Push tag to remote (default: tags stay local)');
@@ -426,7 +429,7 @@ export async function runBumpVersion(args) {
426
429
  if (!validation.valid) {
427
430
  showError('Prerequisites validation failed:');
428
431
  console.log('');
429
- validation.errors.forEach(err => console.log(` ❌ ${err}`));
432
+ validation.errors.forEach((err) => console.log(` ❌ ${err}`));
430
433
  console.log('');
431
434
  console.log('Please fix these issues:');
432
435
  console.log(' - Commit or stash uncommitted changes: git stash');
@@ -472,7 +475,7 @@ export async function runBumpVersion(args) {
472
475
  showInfo(`Current version: ${currentVersion}`);
473
476
 
474
477
  // Step 3: File selection (if mismatch or interactive mode)
475
- let selectedFiles = discovery.files.filter(f => f.selected);
478
+ let selectedFiles = discovery.files.filter((f) => f.selected);
476
479
 
477
480
  if (discovery.mismatch || options.interactive) {
478
481
  selectedFiles = await promptFileSelection(discovery);
@@ -500,7 +503,11 @@ export async function runBumpVersion(args) {
500
503
  operation = `Set suffix to ${options.setSuffix}`;
501
504
  }
502
505
  } else {
503
- newVersion = incrementVersion(currentVersion, options.bumpType, options.suffix || options.setSuffix);
506
+ newVersion = incrementVersion(
507
+ currentVersion,
508
+ options.bumpType,
509
+ options.suffix || options.setSuffix
510
+ );
504
511
  operation = `Bump ${options.bumpType}`;
505
512
  }
506
513
 
@@ -524,10 +531,7 @@ export async function runBumpVersion(args) {
524
531
  }
525
532
 
526
533
  // Step 6: Confirm with user
527
- const shouldContinue = await promptConfirmation(
528
- `Update version to ${newVersion}?`,
529
- true
530
- );
534
+ const shouldContinue = await promptConfirmation(`Update version to ${newVersion}?`, true);
531
535
 
532
536
  if (!shouldContinue) {
533
537
  showInfo('Version bump cancelled');
@@ -545,7 +549,7 @@ export async function runBumpVersion(args) {
545
549
  // Step 7: Snapshot files for rollback
546
550
  logger.debug('bump-version', 'Step 7: Creating snapshot for rollback');
547
551
  const snapshot = new Map();
548
- selectedFiles.forEach(file => {
552
+ selectedFiles.forEach((file) => {
549
553
  try {
550
554
  const content = fs.readFileSync(file.path, 'utf8');
551
555
  snapshot.set(file.path, content);
@@ -568,7 +572,7 @@ export async function runBumpVersion(args) {
568
572
  try {
569
573
  updateVersionFiles(selectedFiles, newVersion);
570
574
 
571
- selectedFiles.forEach(file => {
575
+ selectedFiles.forEach((file) => {
572
576
  const targetVer = file.targetVersion || newVersion;
573
577
  showSuccess(`✓ Updated ${file.relativePath} → ${targetVer}`);
574
578
  });
@@ -625,11 +629,12 @@ export async function runBumpVersion(args) {
625
629
  showInfo('Staging and committing changes...');
626
630
 
627
631
  // Collect files to stage
628
- const filesToStage = selectedFiles.map(f => f.path);
632
+ const filesToStage = selectedFiles.map((f) => f.path);
629
633
 
630
634
  // Add CHANGELOG if it was updated (use selected path from Step 9, or default root)
631
635
  if (options.updateChangelog) {
632
- const changelogPath = selectedChangelogPath || path.join(getRepoRoot(), 'CHANGELOG.md');
636
+ const changelogPath =
637
+ selectedChangelogPath || path.join(getRepoRoot(), 'CHANGELOG.md');
633
638
  if (fs.existsSync(changelogPath)) {
634
639
  filesToStage.push(changelogPath);
635
640
  }
@@ -642,7 +647,7 @@ export async function runBumpVersion(args) {
642
647
  showError(`Failed to stage files: ${stageResult.error}`);
643
648
  console.log('');
644
649
  console.log('Files that should be staged:');
645
- filesToStage.forEach(f => console.log(` - ${path.relative(getRepoRoot(), f)}`));
650
+ filesToStage.forEach((f) => console.log(` - ${path.relative(getRepoRoot(), f)}`));
646
651
  console.log('');
647
652
 
648
653
  // Rollback version changes
@@ -694,7 +699,9 @@ export async function runBumpVersion(args) {
694
699
 
695
700
  // Prevent tag creation if changes not committed
696
701
  if (options.noCommit) {
697
- showWarning('Tag creation skipped: --no-commit requires manual commit before tagging');
702
+ showWarning(
703
+ 'Tag creation skipped: --no-commit requires manual commit before tagging'
704
+ );
698
705
  console.log('');
699
706
  console.log('After committing, you can create the tag:');
700
707
  console.log(` git tag -a ${tagName} -m "Release version ${newVersion}"`);
@@ -758,16 +765,24 @@ export async function runBumpVersion(args) {
758
765
 
759
766
  // Success summary
760
767
  console.log('');
761
- console.log(`${colors.green}════════════════════════════════════════════════${colors.reset}`);
762
- console.log(`${colors.green} Version Bump Complete ✅ ${colors.reset}`);
763
- console.log(`${colors.green}════════════════════════════════════════════════${colors.reset}`);
768
+ console.log(
769
+ `${colors.green}════════════════════════════════════════════════${colors.reset}`
770
+ );
771
+ console.log(
772
+ `${colors.green} Version Bump Complete ✅ ${colors.reset}`
773
+ );
774
+ console.log(
775
+ `${colors.green}════════════════════════════════════════════════${colors.reset}`
776
+ );
764
777
  console.log('');
765
778
 
766
779
  // Show updated files table
767
780
  console.log(`${colors.blue}Updated files:${colors.reset}`);
768
- selectedFiles.forEach(file => {
781
+ selectedFiles.forEach((file) => {
769
782
  const targetVer = file.targetVersion || newVersion;
770
- console.log(` ✓ ${file.relativePath} (${file.projectLabel}) - ${file.version} → ${targetVer}`);
783
+ console.log(
784
+ ` ✓ ${file.relativePath} (${file.projectLabel}) - ${file.version} → ${targetVer}`
785
+ );
771
786
  });
772
787
  console.log('');
773
788
 
@@ -785,7 +800,9 @@ export async function runBumpVersion(args) {
785
800
  console.log(' git add CHANGELOG.md');
786
801
  }
787
802
  console.log(` 3. Commit: git commit -m "chore(version): bump to ${newVersion}"`);
788
- console.log(` 4. Create tag: git tag -a ${tagName} -m "Release version ${newVersion}"`);
803
+ console.log(
804
+ ` 4. Create tag: git tag -a ${tagName} -m "Release version ${newVersion}"`
805
+ );
789
806
  console.log(` 5. Push: git push origin $(git branch --show-current) ${tagName}`);
790
807
  console.log('');
791
808
  } else if (commitCreated && !options.noTag && options.push) {
@@ -802,7 +819,6 @@ export async function runBumpVersion(args) {
802
819
  }
803
820
  console.log('');
804
821
  }
805
-
806
822
  } catch (err) {
807
823
  logger.error('bump-version', 'Version bump failed', err);
808
824
  showError(`Version bump failed: ${err.message}`);