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.
- package/CHANGELOG.md +38 -0
- package/CLAUDE.md +12 -8
- package/README.md +2 -1
- package/bin/claude-hooks +75 -89
- package/lib/cli-metadata.js +301 -0
- package/lib/commands/analyze-diff.js +12 -10
- package/lib/commands/analyze.js +9 -5
- package/lib/commands/bump-version.js +66 -43
- package/lib/commands/create-pr.js +71 -34
- package/lib/commands/debug.js +4 -7
- package/lib/commands/generate-changelog.js +11 -4
- 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 -39
- 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 -31
- package/lib/hooks/pre-commit.js +34 -54
- package/lib/hooks/prepare-commit-msg.js +39 -58
- package/lib/utils/analysis-engine.js +28 -21
- package/lib/utils/changelog-generator.js +162 -34
- package/lib/utils/claude-client.js +438 -377
- package/lib/utils/claude-diagnostics.js +20 -10
- package/lib/utils/file-operations.js +51 -79
- package/lib/utils/file-utils.js +46 -9
- 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/logger.js +4 -1
- package/lib/utils/pr-metadata-engine.js +67 -33
- package/lib/utils/preset-loader.js +20 -62
- package/lib/utils/prompt-builder.js +50 -55
- package/lib/utils/resolution-prompt.js +33 -44
- package/lib/utils/sanitize.js +20 -19
- package/lib/utils/task-id.js +27 -40
- package/lib/utils/telemetry.js +29 -17
- package/lib/utils/version-manager.js +173 -126
- package/lib/utils/which-command.js +23 -12
- package/package.json +69 -69
|
@@ -7,12 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import path from 'path';
|
|
10
|
-
import {
|
|
11
|
-
error,
|
|
12
|
-
success,
|
|
13
|
-
info,
|
|
14
|
-
warning
|
|
15
|
-
} from './helpers.js';
|
|
10
|
+
import { error, success, info, warning } from './helpers.js';
|
|
16
11
|
import { extractLegacySettings } from './install.js';
|
|
17
12
|
|
|
18
13
|
/**
|
|
@@ -43,7 +38,9 @@ export async function runMigrateConfig() {
|
|
|
43
38
|
|
|
44
39
|
// If top-level preset doesn't exist or differs, use the one from overrides
|
|
45
40
|
if (!rawConfig.preset || rawConfig.preset !== rawConfig.overrides.preset) {
|
|
46
|
-
info(
|
|
41
|
+
info(
|
|
42
|
+
` Moving preset '${rawConfig.overrides.preset}' from overrides to top level`
|
|
43
|
+
);
|
|
47
44
|
rawConfig.preset = rawConfig.overrides.preset;
|
|
48
45
|
}
|
|
49
46
|
|
|
@@ -77,9 +74,10 @@ export async function runMigrateConfig() {
|
|
|
77
74
|
const allowedOverrides = extractLegacySettings(rawConfig);
|
|
78
75
|
|
|
79
76
|
// Check for advanced params
|
|
80
|
-
const hasAdvancedParams =
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
const hasAdvancedParams =
|
|
78
|
+
allowedOverrides.analysis?.ignoreExtensions ||
|
|
79
|
+
allowedOverrides.commitMessage?.taskIdPattern ||
|
|
80
|
+
allowedOverrides.subagents?.model;
|
|
83
81
|
|
|
84
82
|
// Build new config
|
|
85
83
|
const newConfig = {
|
|
@@ -114,7 +112,6 @@ export async function runMigrateConfig() {
|
|
|
114
112
|
console.log(`\nš¾ Old config backed up to: ${backupPath}`);
|
|
115
113
|
console.log('\nš” Many parameters are now hardcoded with sensible defaults');
|
|
116
114
|
console.log(' See CHANGELOG.md for full list of changes');
|
|
117
|
-
|
|
118
115
|
} catch (err) {
|
|
119
116
|
error(`Failed to migrate config: ${err.message}`);
|
|
120
117
|
console.log('\nš” Manual migration:');
|
package/lib/commands/presets.js
CHANGED
|
@@ -5,14 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { listPresets } from '../utils/preset-loader.js';
|
|
7
7
|
import { getConfig } from '../config.js';
|
|
8
|
-
import {
|
|
9
|
-
colors,
|
|
10
|
-
error,
|
|
11
|
-
success,
|
|
12
|
-
info,
|
|
13
|
-
warning,
|
|
14
|
-
updateConfig
|
|
15
|
-
} from './helpers.js';
|
|
8
|
+
import { colors, error, success, info, warning, updateConfig } from './helpers.js';
|
|
16
9
|
|
|
17
10
|
/**
|
|
18
11
|
* Shows all available presets
|
|
@@ -30,7 +23,7 @@ export async function runShowPresets() {
|
|
|
30
23
|
info('Available presets:');
|
|
31
24
|
console.log('');
|
|
32
25
|
|
|
33
|
-
presets.forEach(preset => {
|
|
26
|
+
presets.forEach((preset) => {
|
|
34
27
|
console.log(` ${colors.green}${preset.name}${colors.reset}`);
|
|
35
28
|
console.log(` ${preset.displayName}`);
|
|
36
29
|
console.log(` ${colors.blue}${preset.description}${colors.reset}`);
|
|
@@ -59,18 +52,18 @@ export async function runSetPreset(presetName) {
|
|
|
59
52
|
await updateConfig('preset', presetName, {
|
|
60
53
|
validator: async (name) => {
|
|
61
54
|
const presets = await listPresets();
|
|
62
|
-
const preset = presets.find(p => p.name === name);
|
|
55
|
+
const preset = presets.find((p) => p.name === name);
|
|
63
56
|
if (!preset) {
|
|
64
57
|
error(`Preset "${name}" not found`);
|
|
65
58
|
info('Available presets:');
|
|
66
|
-
presets.forEach(p => console.log(` - ${p.name}`));
|
|
59
|
+
presets.forEach((p) => console.log(` - ${p.name}`));
|
|
67
60
|
throw new Error(`Invalid preset: ${name}`);
|
|
68
61
|
}
|
|
69
62
|
return preset;
|
|
70
63
|
},
|
|
71
64
|
successMessage: async (name) => {
|
|
72
65
|
const presets = await listPresets();
|
|
73
|
-
const preset = presets.find(p => p.name === name);
|
|
66
|
+
const preset = presets.find((p) => p.name === name);
|
|
74
67
|
return `Preset '${preset.displayName}' activated`;
|
|
75
68
|
}
|
|
76
69
|
});
|
|
@@ -85,7 +78,7 @@ export async function runCurrentPreset() {
|
|
|
85
78
|
const presetName = config.preset || 'default';
|
|
86
79
|
|
|
87
80
|
const presets = await listPresets();
|
|
88
|
-
const preset = presets.find(p => p.name === presetName);
|
|
81
|
+
const preset = presets.find((p) => p.name === presetName);
|
|
89
82
|
|
|
90
83
|
if (preset) {
|
|
91
84
|
console.log('');
|
|
@@ -10,7 +10,14 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { validateToken, saveGitHubToken, validateTokenFormat } from '../utils/github-api.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
promptConfirmation,
|
|
15
|
+
promptEditField,
|
|
16
|
+
showSuccess,
|
|
17
|
+
showError,
|
|
18
|
+
showInfo,
|
|
19
|
+
showWarning
|
|
20
|
+
} from '../utils/interactive-ui.js';
|
|
14
21
|
import logger from '../utils/logger.js';
|
|
15
22
|
|
|
16
23
|
/**
|
|
@@ -39,7 +46,9 @@ export async function runSetupGitHub() {
|
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
} catch (e) {
|
|
42
|
-
logger.debug('setup-github', 'No existing token found or validation failed', {
|
|
49
|
+
logger.debug('setup-github', 'No existing token found or validation failed', {
|
|
50
|
+
error: e.message
|
|
51
|
+
});
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
// Show instructions
|
|
@@ -71,7 +80,7 @@ export async function runSetupGitHub() {
|
|
|
71
80
|
// Save token
|
|
72
81
|
const saveResult = saveGitHubToken(trimmedToken);
|
|
73
82
|
if (!saveResult.success) {
|
|
74
|
-
showError(`Failed to save token: ${
|
|
83
|
+
showError(`Failed to save token: ${saveResult.error}`);
|
|
75
84
|
return;
|
|
76
85
|
}
|
|
77
86
|
showSuccess(`Token saved to ${saveResult.path}`);
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getConfig } from '../config.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
displayStatistics as showTelemetryStats,
|
|
9
|
+
clearTelemetry as clearTelemetryData
|
|
10
|
+
} from '../utils/telemetry.js';
|
|
8
11
|
import { promptConfirmation } from '../utils/interactive-ui.js';
|
|
9
12
|
import logger from '../utils/logger.js';
|
|
10
|
-
import {
|
|
11
|
-
success,
|
|
12
|
-
info
|
|
13
|
-
} from './helpers.js';
|
|
13
|
+
import { success, info } from './helpers.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Show telemetry statistics
|
|
@@ -38,7 +38,9 @@ export async function runClearTelemetry() {
|
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const confirmed = await promptConfirmation(
|
|
41
|
+
const confirmed = await promptConfirmation(
|
|
42
|
+
'Are you sure you want to clear all telemetry data?'
|
|
43
|
+
);
|
|
42
44
|
if (confirmed) {
|
|
43
45
|
await clearTelemetryData();
|
|
44
46
|
success('Telemetry data cleared successfully');
|
package/lib/commands/update.js
CHANGED
|
@@ -49,7 +49,6 @@ export async function runUpdate() {
|
|
|
49
49
|
// Reinstall hooks with the new version
|
|
50
50
|
info('Reinstalling hooks with the new version...');
|
|
51
51
|
await runInstall(['--force']);
|
|
52
|
-
|
|
53
52
|
} catch (updateError) {
|
|
54
53
|
error('Error updating. Try running: npm install -g claude-git-hooks@latest');
|
|
55
54
|
}
|
|
@@ -61,7 +60,7 @@ export async function runUpdate() {
|
|
|
61
60
|
success('Update completed');
|
|
62
61
|
await runInstall(['--force']);
|
|
63
62
|
} catch (updateError) {
|
|
64
|
-
error(`Error updating: ${
|
|
63
|
+
error(`Error updating: ${updateError.message}`);
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
}
|
package/lib/config.js
CHANGED
|
@@ -34,21 +34,21 @@ import logger from './utils/logger.js';
|
|
|
34
34
|
*/
|
|
35
35
|
const HARDCODED = {
|
|
36
36
|
analysis: {
|
|
37
|
-
maxFileSize: 1000000,
|
|
38
|
-
maxFiles: 20,
|
|
39
|
-
timeout: 300000,
|
|
40
|
-
contextLines: 3,
|
|
41
|
-
ignoreExtensions: []
|
|
37
|
+
maxFileSize: 1000000, // 1MB - sufficient for most files
|
|
38
|
+
maxFiles: 20, // Reasonable limit per commit
|
|
39
|
+
timeout: 300000, // 5 minutes - adequate for Claude API
|
|
40
|
+
contextLines: 3, // Git default
|
|
41
|
+
ignoreExtensions: [] // Can be set in advanced config only
|
|
42
42
|
},
|
|
43
43
|
commitMessage: {
|
|
44
|
-
autoKeyword: 'auto',
|
|
45
|
-
timeout: 300000,
|
|
46
|
-
taskIdPattern: '([A-Z]{1,3}[-\\s]\\d{3,5})'
|
|
44
|
+
autoKeyword: 'auto', // Standard keyword
|
|
45
|
+
timeout: 300000, // Use same timeout as analysis
|
|
46
|
+
taskIdPattern: '([A-Z]{1,3}[-\\s]\\d{3,5})' // Jira/GitHub/Linear pattern
|
|
47
47
|
},
|
|
48
48
|
subagents: {
|
|
49
|
-
enabled: true,
|
|
50
|
-
model: 'haiku',
|
|
51
|
-
batchSize: 3
|
|
49
|
+
enabled: true, // Enable by default (faster analysis)
|
|
50
|
+
model: 'haiku', // Fast and cost-effective
|
|
51
|
+
batchSize: 3 // Reasonable parallelization
|
|
52
52
|
},
|
|
53
53
|
templates: {
|
|
54
54
|
baseDir: '.claude/prompts',
|
|
@@ -58,25 +58,25 @@ const HARDCODED = {
|
|
|
58
58
|
analyzeDiff: 'ANALYZE_DIFF.md',
|
|
59
59
|
resolution: 'CLAUDE_RESOLUTION_PROMPT.md',
|
|
60
60
|
subagentInstruction: 'SUBAGENT_INSTRUCTION.md',
|
|
61
|
-
createGithubPR: 'CREATE_GITHUB_PR.md'
|
|
61
|
+
createGithubPR: 'CREATE_GITHUB_PR.md'
|
|
62
62
|
},
|
|
63
63
|
output: {
|
|
64
64
|
outputDir: '.claude/out',
|
|
65
65
|
debugFile: '.claude/out/debug-claude-response.json',
|
|
66
66
|
resolutionFile: '.claude/out/claude_resolution_prompt.md',
|
|
67
|
-
prAnalysisFile: '.claude/out/pr-analysis.json'
|
|
67
|
+
prAnalysisFile: '.claude/out/pr-analysis.json'
|
|
68
68
|
},
|
|
69
69
|
system: {
|
|
70
|
-
debug: false,
|
|
71
|
-
telemetry: true,
|
|
72
|
-
wslCheckTimeout: 15000
|
|
70
|
+
debug: false, // Controlled by --debug flag
|
|
71
|
+
telemetry: true, // Opt-out telemetry for debugging (local only, set to false to disable)
|
|
72
|
+
wslCheckTimeout: 15000 // System behavior
|
|
73
73
|
},
|
|
74
74
|
git: {
|
|
75
|
-
diffFilter: 'ACM'
|
|
75
|
+
diffFilter: 'ACM' // Standard: Added, Copied, Modified
|
|
76
76
|
},
|
|
77
77
|
github: {
|
|
78
|
-
enabled: true
|
|
79
|
-
}
|
|
78
|
+
enabled: true // Always enabled
|
|
79
|
+
}
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -87,9 +87,10 @@ const defaults = {
|
|
|
87
87
|
// GitHub PR configuration (user-specific)
|
|
88
88
|
github: {
|
|
89
89
|
pr: {
|
|
90
|
-
defaultBase: 'develop',
|
|
91
|
-
reviewers: [],
|
|
92
|
-
labelRules: {
|
|
90
|
+
defaultBase: 'develop', // Project default branch
|
|
91
|
+
reviewers: [], // Project reviewers
|
|
92
|
+
labelRules: {
|
|
93
|
+
// Labels by preset
|
|
93
94
|
backend: ['backend', 'java'],
|
|
94
95
|
frontend: ['frontend', 'react'],
|
|
95
96
|
fullstack: ['fullstack'],
|
|
@@ -98,17 +99,17 @@ const defaults = {
|
|
|
98
99
|
default: []
|
|
99
100
|
},
|
|
100
101
|
// Auto-push configuration (v2.11.0)
|
|
101
|
-
autoPush: true,
|
|
102
|
-
pushConfirm: true,
|
|
103
|
-
verifyRemote: true,
|
|
104
|
-
showCommits: true
|
|
105
|
-
}
|
|
102
|
+
autoPush: true, // Auto-push unpublished branches
|
|
103
|
+
pushConfirm: true, // Prompt for confirmation before push
|
|
104
|
+
verifyRemote: true, // Verify remote exists before push
|
|
105
|
+
showCommits: true // Show commit preview before push
|
|
106
|
+
}
|
|
106
107
|
},
|
|
107
108
|
|
|
108
109
|
// Subagent configuration (preset can override)
|
|
109
110
|
subagents: {
|
|
110
|
-
batchSize: 3
|
|
111
|
-
}
|
|
111
|
+
batchSize: 3 // Files per parallel batch
|
|
112
|
+
}
|
|
112
113
|
};
|
|
113
114
|
|
|
114
115
|
/**
|
|
@@ -137,10 +138,14 @@ const loadUserConfig = async (baseDir = process.cwd()) => {
|
|
|
137
138
|
if (rawConfig.version === '2.8.0') {
|
|
138
139
|
configFormat = 'v3';
|
|
139
140
|
} else if (rawConfig.version) {
|
|
140
|
-
throw new Error(
|
|
141
|
+
throw new Error(
|
|
142
|
+
`Unsupported config version: ${rawConfig.version}. Please run 'claude-hooks migrate-config'`
|
|
143
|
+
);
|
|
141
144
|
} else if (Object.keys(rawConfig).length > 0 && rawConfig.preset) {
|
|
142
145
|
configFormat = 'legacy';
|
|
143
|
-
logger.warning(
|
|
146
|
+
logger.warning(
|
|
147
|
+
'ā ļø Legacy config format detected. Run "claude-hooks migrate-config" to update to v2.8.0'
|
|
148
|
+
);
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
} catch (error) {
|
package/lib/hooks/pre-commit.js
CHANGED
|
@@ -19,22 +19,10 @@
|
|
|
19
19
|
* - resolution-prompt: Issue resolution generation
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from '../utils/
|
|
26
|
-
import {
|
|
27
|
-
filterFiles,
|
|
28
|
-
} from '../utils/file-operations.js';
|
|
29
|
-
import {
|
|
30
|
-
buildFilesData,
|
|
31
|
-
runAnalysis,
|
|
32
|
-
displayResults
|
|
33
|
-
} from '../utils/analysis-engine.js';
|
|
34
|
-
import {
|
|
35
|
-
generateResolutionPrompt,
|
|
36
|
-
shouldGeneratePrompt
|
|
37
|
-
} from '../utils/resolution-prompt.js';
|
|
22
|
+
import { getStagedFiles, getRepoRoot } from '../utils/git-operations.js';
|
|
23
|
+
import { filterFiles } from '../utils/file-operations.js';
|
|
24
|
+
import { buildFilesData, runAnalysis, displayResults } from '../utils/analysis-engine.js';
|
|
25
|
+
import { generateResolutionPrompt, shouldGeneratePrompt } from '../utils/resolution-prompt.js';
|
|
38
26
|
import { loadPreset } from '../utils/preset-loader.js';
|
|
39
27
|
import { getVersion, calculateBatches } from '../utils/package-info.js';
|
|
40
28
|
import logger from '../utils/logger.js';
|
|
@@ -81,38 +69,26 @@ const main = async () => {
|
|
|
81
69
|
const cwdNormalized = normalizePath(process.cwd());
|
|
82
70
|
const repoRootNormalized = normalizePath(repoRoot);
|
|
83
71
|
|
|
84
|
-
logger.debug(
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
'repo root (normalized)': repoRootNormalized,
|
|
92
|
-
'match': cwdNormalized === repoRootNormalized
|
|
93
|
-
}
|
|
94
|
-
);
|
|
72
|
+
logger.debug('pre-commit - main', 'Working directory info', {
|
|
73
|
+
'process.cwd()': process.cwd(),
|
|
74
|
+
'repo root': repoRoot,
|
|
75
|
+
'cwd (normalized)': cwdNormalized,
|
|
76
|
+
'repo root (normalized)': repoRootNormalized,
|
|
77
|
+
match: cwdNormalized === repoRootNormalized
|
|
78
|
+
});
|
|
95
79
|
|
|
96
|
-
logger.debug(
|
|
97
|
-
'pre-commit - main',
|
|
98
|
-
'Configuration',
|
|
99
|
-
{ ...config }
|
|
100
|
-
);
|
|
80
|
+
logger.debug('pre-commit - main', 'Configuration', { ...config });
|
|
101
81
|
|
|
102
82
|
// Load active preset
|
|
103
83
|
const presetName = config.preset || 'default';
|
|
104
84
|
const { metadata } = await loadPreset(presetName);
|
|
105
85
|
|
|
106
86
|
logger.info(`šÆ Analyzing with '${metadata.displayName}' preset`);
|
|
107
|
-
logger.debug(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
fileExtensions: metadata.fileExtensions,
|
|
113
|
-
techStack: metadata.techStack
|
|
114
|
-
}
|
|
115
|
-
);
|
|
87
|
+
logger.debug('pre-commit - main', 'Preset loaded', {
|
|
88
|
+
preset: presetName,
|
|
89
|
+
fileExtensions: metadata.fileExtensions,
|
|
90
|
+
techStack: metadata.techStack
|
|
91
|
+
});
|
|
116
92
|
|
|
117
93
|
// Use preset's file extensions
|
|
118
94
|
const allowedExtensions = metadata.fileExtensions;
|
|
@@ -129,11 +105,10 @@ const main = async () => {
|
|
|
129
105
|
}
|
|
130
106
|
|
|
131
107
|
logger.info(`Files to review: ${stagedFiles.length}`);
|
|
132
|
-
logger.debug(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
);
|
|
108
|
+
logger.debug('pre-commit - main', 'Files matched by preset', {
|
|
109
|
+
count: stagedFiles.length,
|
|
110
|
+
extensions: allowedExtensions
|
|
111
|
+
});
|
|
137
112
|
|
|
138
113
|
// Step 2: Filter files by size
|
|
139
114
|
logger.debug('pre-commit - main', 'Filtering files by size');
|
|
@@ -142,12 +117,12 @@ const main = async () => {
|
|
|
142
117
|
extensions: allowedExtensions
|
|
143
118
|
});
|
|
144
119
|
|
|
145
|
-
const validFiles = filteredFiles.filter(f => f.valid);
|
|
146
|
-
const invalidFiles = filteredFiles.filter(f => !f.valid);
|
|
120
|
+
const validFiles = filteredFiles.filter((f) => f.valid);
|
|
121
|
+
const invalidFiles = filteredFiles.filter((f) => !f.valid);
|
|
147
122
|
|
|
148
123
|
// Show user-facing warnings for rejected files
|
|
149
124
|
if (invalidFiles.length > 0) {
|
|
150
|
-
invalidFiles.forEach(file => {
|
|
125
|
+
invalidFiles.forEach((file) => {
|
|
151
126
|
logger.warning(`Skipping ${file.path}: ${file.reason}`);
|
|
152
127
|
});
|
|
153
128
|
}
|
|
@@ -177,9 +152,13 @@ const main = async () => {
|
|
|
177
152
|
|
|
178
153
|
if (subagentsEnabled && filesData.length >= 3) {
|
|
179
154
|
const { numBatches, shouldShowBatches } = calculateBatches(filesData.length, batchSize);
|
|
180
|
-
console.log(
|
|
155
|
+
console.log(
|
|
156
|
+
`ā” Batch optimization: ${subagentModel} model, ${batchSize} files per batch`
|
|
157
|
+
);
|
|
181
158
|
if (shouldShowBatches) {
|
|
182
|
-
console.log(
|
|
159
|
+
console.log(
|
|
160
|
+
`š Analyzing ${filesData.length} files in ${numBatches} batch${numBatches > 1 ? 'es' : ''}`
|
|
161
|
+
);
|
|
183
162
|
}
|
|
184
163
|
}
|
|
185
164
|
|
|
@@ -205,7 +184,7 @@ const main = async () => {
|
|
|
205
184
|
// Show blocking issues
|
|
206
185
|
if (Array.isArray(result.blockingIssues) && result.blockingIssues.length > 0) {
|
|
207
186
|
console.log('=== CRITICAL ISSUES ===');
|
|
208
|
-
result.blockingIssues.forEach(issue => {
|
|
187
|
+
result.blockingIssues.forEach((issue) => {
|
|
209
188
|
console.log(`- ${issue.description || 'Unknown issue'}`);
|
|
210
189
|
});
|
|
211
190
|
console.log();
|
|
@@ -219,7 +198,9 @@ const main = async () => {
|
|
|
219
198
|
|
|
220
199
|
console.log('āāā AI RESOLUTION PROMPT GENERATED āāā');
|
|
221
200
|
logger.info(`An AI-friendly prompt has been generated at: ${resolutionPath}`);
|
|
222
|
-
logger.warning(
|
|
201
|
+
logger.warning(
|
|
202
|
+
'Copy this file to a new Claude instance to resolve problems automatically.'
|
|
203
|
+
);
|
|
223
204
|
console.log();
|
|
224
205
|
}
|
|
225
206
|
|
|
@@ -232,7 +213,6 @@ const main = async () => {
|
|
|
232
213
|
logger.success('Code analysis completed. Quality gate passed.');
|
|
233
214
|
|
|
234
215
|
process.exit(0);
|
|
235
|
-
|
|
236
216
|
} catch (error) {
|
|
237
217
|
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
238
218
|
console.log(`\nā±ļø Analysis time: ${duration}s`);
|
|
@@ -35,11 +35,9 @@ import { getOrPromptTaskId, formatWithTaskId } from '../utils/task-id.js';
|
|
|
35
35
|
* @returns {Promise<string>} Complete prompt
|
|
36
36
|
*/
|
|
37
37
|
const buildCommitPrompt = async (filesData, stats) => {
|
|
38
|
-
logger.debug(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{ fileCount: filesData.length }
|
|
42
|
-
);
|
|
38
|
+
logger.debug('prepare-commit-msg - buildCommitPrompt', 'Building commit message prompt', {
|
|
39
|
+
fileCount: filesData.length
|
|
40
|
+
});
|
|
43
41
|
|
|
44
42
|
// Build file list
|
|
45
43
|
const fileList = filesData.map(({ path }) => path).join('\n');
|
|
@@ -81,11 +79,11 @@ const buildCommitPrompt = async (filesData, stats) => {
|
|
|
81
79
|
* }
|
|
82
80
|
*/
|
|
83
81
|
const formatCommitMessage = (json) => {
|
|
84
|
-
logger.debug(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
);
|
|
82
|
+
logger.debug('prepare-commit-msg - formatCommitMessage', 'Formatting commit message', {
|
|
83
|
+
type: json.type,
|
|
84
|
+
hasScope: !!json.scope,
|
|
85
|
+
hasBody: !!json.body
|
|
86
|
+
});
|
|
89
87
|
|
|
90
88
|
const type = json.type || 'feat';
|
|
91
89
|
const scope = json.scope && json.scope !== 'null' ? json.scope : null;
|
|
@@ -131,19 +129,12 @@ const main = async () => {
|
|
|
131
129
|
const commitMsgFile = args[0];
|
|
132
130
|
const commitSource = args[1];
|
|
133
131
|
|
|
134
|
-
logger.debug(
|
|
135
|
-
'prepare-commit-msg - main',
|
|
136
|
-
'Hook started',
|
|
137
|
-
{ commitMsgFile, commitSource }
|
|
138
|
-
);
|
|
132
|
+
logger.debug('prepare-commit-msg - main', 'Hook started', { commitMsgFile, commitSource });
|
|
139
133
|
|
|
140
134
|
// Only process normal commits
|
|
141
135
|
// Why: Don't interfere with merge commits, amend, squash, etc.
|
|
142
136
|
if (commitSource && commitSource !== 'message') {
|
|
143
|
-
logger.debug(
|
|
144
|
-
'prepare-commit-msg - main',
|
|
145
|
-
`Skipping: commit source is ${commitSource}`
|
|
146
|
-
);
|
|
137
|
+
logger.debug('prepare-commit-msg - main', `Skipping: commit source is ${commitSource}`);
|
|
147
138
|
process.exit(0);
|
|
148
139
|
}
|
|
149
140
|
|
|
@@ -151,18 +142,11 @@ const main = async () => {
|
|
|
151
142
|
const currentMsg = await fs.readFile(commitMsgFile, 'utf8');
|
|
152
143
|
const firstLine = currentMsg.split('\n')[0].trim();
|
|
153
144
|
|
|
154
|
-
logger.debug(
|
|
155
|
-
'prepare-commit-msg - main',
|
|
156
|
-
'Current commit message',
|
|
157
|
-
{ firstLine }
|
|
158
|
-
);
|
|
145
|
+
logger.debug('prepare-commit-msg - main', 'Current commit message', { firstLine });
|
|
159
146
|
|
|
160
147
|
// Check if message is "auto"
|
|
161
148
|
if (firstLine !== config.commitMessage.autoKeyword) {
|
|
162
|
-
logger.debug(
|
|
163
|
-
'prepare-commit-msg - main',
|
|
164
|
-
'Not generating: message is not "auto"'
|
|
165
|
-
);
|
|
149
|
+
logger.debug('prepare-commit-msg - main', 'Not generating: message is not "auto"');
|
|
166
150
|
process.exit(0);
|
|
167
151
|
}
|
|
168
152
|
|
|
@@ -182,14 +166,17 @@ const main = async () => {
|
|
|
182
166
|
// Step 1: Auto-detect task ID from branch (no prompt)
|
|
183
167
|
logger.debug('prepare-commit-msg - main', 'Detecting task ID from branch');
|
|
184
168
|
const taskId = await getOrPromptTaskId({
|
|
185
|
-
prompt: false,
|
|
169
|
+
prompt: false, // Don't prompt - just detect from branch
|
|
186
170
|
required: false,
|
|
187
|
-
config
|
|
171
|
+
config // Pass config for custom pattern
|
|
188
172
|
});
|
|
189
173
|
|
|
190
174
|
// Note: getOrPromptTaskId() already logs the task ID if found
|
|
191
175
|
if (!taskId) {
|
|
192
|
-
logger.debug(
|
|
176
|
+
logger.debug(
|
|
177
|
+
'prepare-commit-msg - main',
|
|
178
|
+
'No task ID found in branch, continuing without it'
|
|
179
|
+
);
|
|
193
180
|
}
|
|
194
181
|
|
|
195
182
|
// Step 2: Get staged files
|
|
@@ -204,11 +191,10 @@ const main = async () => {
|
|
|
204
191
|
// Get statistics
|
|
205
192
|
const stats = getStagedStats();
|
|
206
193
|
|
|
207
|
-
logger.debug(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
);
|
|
194
|
+
logger.debug('prepare-commit-msg - main', 'Staged files', {
|
|
195
|
+
fileCount: stagedFiles.length,
|
|
196
|
+
stats
|
|
197
|
+
});
|
|
212
198
|
|
|
213
199
|
// Build file data with diffs
|
|
214
200
|
const filesData = await Promise.all(
|
|
@@ -228,7 +214,6 @@ const main = async () => {
|
|
|
228
214
|
diff,
|
|
229
215
|
size: fileStats.size
|
|
230
216
|
};
|
|
231
|
-
|
|
232
217
|
} catch (error) {
|
|
233
218
|
logger.error(
|
|
234
219
|
'prepare-commit-msg - main',
|
|
@@ -248,17 +233,15 @@ const main = async () => {
|
|
|
248
233
|
// Build prompt
|
|
249
234
|
const prompt = await buildCommitPrompt(filesData, stats);
|
|
250
235
|
|
|
251
|
-
logger.debug(
|
|
252
|
-
'prepare-commit-msg - main',
|
|
253
|
-
'Prompt built',
|
|
254
|
-
{ promptLength: prompt.length }
|
|
255
|
-
);
|
|
236
|
+
logger.debug('prepare-commit-msg - main', 'Prompt built', { promptLength: prompt.length });
|
|
256
237
|
|
|
257
238
|
// Calculate batches if subagents enabled and applicable
|
|
258
239
|
if (subagentsEnabled && filesData.length >= 3) {
|
|
259
240
|
const { numBatches, shouldShowBatches } = calculateBatches(filesData.length, batchSize);
|
|
260
241
|
if (shouldShowBatches) {
|
|
261
|
-
console.log(
|
|
242
|
+
console.log(
|
|
243
|
+
`š Analyzing ${filesData.length} files in ${numBatches} batch${numBatches > 1 ? 'es' : ''}`
|
|
244
|
+
);
|
|
262
245
|
}
|
|
263
246
|
}
|
|
264
247
|
|
|
@@ -269,9 +252,10 @@ const main = async () => {
|
|
|
269
252
|
const telemetryContext = {
|
|
270
253
|
fileCount: filesData.length,
|
|
271
254
|
batchSize: config.subagents?.batchSize || 3,
|
|
272
|
-
totalBatches:
|
|
273
|
-
|
|
274
|
-
|
|
255
|
+
totalBatches:
|
|
256
|
+
subagentsEnabled && filesData.length >= 3
|
|
257
|
+
? Math.ceil(filesData.length / (config.subagents?.batchSize || 3))
|
|
258
|
+
: 1,
|
|
275
259
|
model: subagentModel,
|
|
276
260
|
hook: 'prepare-commit-msg'
|
|
277
261
|
};
|
|
@@ -282,11 +266,10 @@ const main = async () => {
|
|
|
282
266
|
telemetryContext
|
|
283
267
|
});
|
|
284
268
|
|
|
285
|
-
logger.debug(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
);
|
|
269
|
+
logger.debug('prepare-commit-msg - main', 'Response received', {
|
|
270
|
+
hasType: !!response.type,
|
|
271
|
+
hasTitle: !!response.title
|
|
272
|
+
});
|
|
290
273
|
|
|
291
274
|
// Format message
|
|
292
275
|
let message = formatCommitMessage(response);
|
|
@@ -294,15 +277,14 @@ const main = async () => {
|
|
|
294
277
|
// Add task ID prefix if available
|
|
295
278
|
if (taskId) {
|
|
296
279
|
message = formatWithTaskId(message, taskId);
|
|
297
|
-
logger.debug(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
);
|
|
280
|
+
logger.debug('prepare-commit-msg - main', 'Task ID added to message', {
|
|
281
|
+
taskId,
|
|
282
|
+
message: message.split('\n')[0]
|
|
283
|
+
});
|
|
302
284
|
}
|
|
303
285
|
|
|
304
286
|
// Write to commit message file
|
|
305
|
-
await fs.writeFile(commitMsgFile, `${message
|
|
287
|
+
await fs.writeFile(commitMsgFile, `${message}\n`, 'utf8');
|
|
306
288
|
|
|
307
289
|
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
308
290
|
console.error(`ā±ļø Message generation completed in ${duration}s`);
|
|
@@ -310,7 +292,6 @@ const main = async () => {
|
|
|
310
292
|
logger.success(`Message generated: ${message.split('\n')[0]}`);
|
|
311
293
|
|
|
312
294
|
process.exit(0);
|
|
313
|
-
|
|
314
295
|
} catch (error) {
|
|
315
296
|
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
316
297
|
console.error(`ā±ļø Message generation failed after ${duration}s`);
|