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.
- package/CHANGELOG.md +52 -0
- package/CLAUDE.md +85 -38
- package/README.md +52 -18
- package/bin/claude-hooks +75 -89
- package/lib/cli-metadata.js +306 -0
- package/lib/commands/analyze-diff.js +12 -10
- package/lib/commands/analyze.js +9 -5
- package/lib/commands/bump-version.js +56 -40
- package/lib/commands/create-pr.js +71 -34
- package/lib/commands/debug.js +4 -7
- package/lib/commands/diff-batch-info.js +105 -0
- package/lib/commands/generate-changelog.js +3 -2
- package/lib/commands/help.js +47 -27
- package/lib/commands/helpers.js +66 -43
- package/lib/commands/hooks.js +15 -13
- package/lib/commands/install.js +546 -49
- package/lib/commands/migrate-config.js +8 -11
- package/lib/commands/presets.js +6 -13
- package/lib/commands/setup-github.js +12 -3
- package/lib/commands/telemetry-cmd.js +8 -6
- package/lib/commands/update.js +1 -2
- package/lib/config.js +36 -52
- package/lib/hooks/pre-commit.js +70 -64
- package/lib/hooks/prepare-commit-msg.js +35 -75
- package/lib/utils/analysis-engine.js +77 -54
- package/lib/utils/changelog-generator.js +63 -37
- package/lib/utils/claude-client.js +447 -438
- package/lib/utils/claude-diagnostics.js +20 -10
- package/lib/utils/diff-analysis-orchestrator.js +332 -0
- package/lib/utils/file-operations.js +51 -79
- package/lib/utils/file-utils.js +6 -7
- package/lib/utils/git-operations.js +140 -123
- package/lib/utils/git-tag-manager.js +24 -23
- package/lib/utils/github-api.js +85 -61
- package/lib/utils/github-client.js +12 -14
- package/lib/utils/installation-diagnostics.js +4 -4
- package/lib/utils/interactive-ui.js +29 -17
- package/lib/utils/judge.js +195 -0
- package/lib/utils/logger.js +4 -1
- package/lib/utils/package-info.js +0 -11
- package/lib/utils/pr-metadata-engine.js +67 -33
- package/lib/utils/preset-loader.js +20 -62
- package/lib/utils/prompt-builder.js +57 -68
- package/lib/utils/resolution-prompt.js +34 -52
- package/lib/utils/sanitize.js +20 -19
- package/lib/utils/task-id.js +27 -40
- package/lib/utils/telemetry.js +73 -25
- package/lib/utils/version-manager.js +147 -70
- package/lib/utils/which-command.js +23 -12
- package/package.json +1 -1
- package/templates/CLAUDE_RESOLUTION_PROMPT.md +17 -9
- package/templates/DIFF_ANALYSIS_ORCHESTRATION_PROMPT.md +70 -0
- package/templates/config.advanced.example.json +15 -31
- 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(
|
|
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 {
|
|
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(
|
|
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
|
}
|
package/lib/commands/analyze.js
CHANGED
|
@@ -19,7 +19,12 @@
|
|
|
19
19
|
* claude-hooks analyze --all # Analyze all tracked files
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {
|
|
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(
|
|
208
|
-
|
|
209
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
762
|
-
|
|
763
|
-
|
|
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(
|
|
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(
|
|
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}`);
|