claude-git-hooks 2.6.3 → 2.8.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 +173 -0
- package/README.md +77 -58
- package/bin/claude-hooks +322 -27
- package/lib/config.js +176 -83
- package/lib/hooks/pre-commit.js +11 -14
- package/lib/hooks/prepare-commit-msg.js +2 -3
- package/lib/utils/claude-client.js +199 -34
- package/lib/utils/claude-diagnostics.js +102 -11
- package/lib/utils/prompt-builder.js +2 -2
- package/package.json +1 -1
- package/templates/{CLAUDE_ANALYSIS_PROMPT_SONAR.md → CLAUDE_ANALYSIS_PROMPT.md} +2 -1
- package/templates/{CLAUDE_PRE_COMMIT_SONAR.md → CLAUDE_PRE_COMMIT.md} +10 -1
- package/templates/CUSTOMIZATION_GUIDE.md +3 -3
- package/templates/config.advanced.example.json +113 -0
- package/templates/config.example.json +53 -36
- package/templates/presets/ai/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/ai/config.json +5 -12
- package/templates/presets/backend/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/backend/config.json +5 -12
- package/templates/presets/database/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/database/config.json +5 -12
- package/templates/presets/default/config.json +5 -12
- package/templates/presets/frontend/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/frontend/config.json +5 -12
- package/templates/presets/fullstack/ANALYSIS_PROMPT.md +1 -1
- package/templates/presets/fullstack/config.json +5 -12
- package/templates/shared/ANALYSIS_PROMPT.md +1 -1
package/bin/claude-hooks
CHANGED
|
@@ -8,7 +8,7 @@ import readline from 'readline';
|
|
|
8
8
|
import https from 'https';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
import { dirname } from 'path';
|
|
11
|
-
import { executeClaude, extractJSON } from '../lib/utils/claude-client.js';
|
|
11
|
+
import { executeClaude, executeClaudeWithRetry, extractJSON, analyzeCode } from '../lib/utils/claude-client.js';
|
|
12
12
|
import { loadPrompt } from '../lib/utils/prompt-builder.js';
|
|
13
13
|
import { listPresets } from '../lib/utils/preset-loader.js';
|
|
14
14
|
import { getConfig } from '../lib/config.js';
|
|
@@ -384,28 +384,78 @@ async function install(args) {
|
|
|
384
384
|
success('.claude directory created');
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
//
|
|
387
|
+
// Remove old SONAR template files if they exist (migration from v2.6.x to v2.7.0+)
|
|
388
|
+
const oldSonarFiles = [
|
|
389
|
+
'CLAUDE_PRE_COMMIT_SONAR.md',
|
|
390
|
+
'CLAUDE_ANALYSIS_PROMPT_SONAR.md'
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
oldSonarFiles.forEach(oldFile => {
|
|
394
|
+
const oldPath = path.join(claudeDir, oldFile);
|
|
395
|
+
if (fs.existsSync(oldPath)) {
|
|
396
|
+
fs.unlinkSync(oldPath);
|
|
397
|
+
info(`Removed old template: ${oldFile}`);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Create .claude/prompts directory for markdown templates
|
|
402
|
+
const promptsDir = path.join(claudeDir, 'prompts');
|
|
403
|
+
if (!fs.existsSync(promptsDir)) {
|
|
404
|
+
fs.mkdirSync(promptsDir, { recursive: true });
|
|
405
|
+
success('.claude/prompts directory created');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Copy template files (.md and .json) to appropriate locations
|
|
388
409
|
const templateFiles = fs.readdirSync(templatesPath)
|
|
389
410
|
.filter(file => {
|
|
390
411
|
const filePath = path.join(templatesPath, file);
|
|
391
|
-
|
|
412
|
+
// Exclude example.json files and only include .md and .json files
|
|
413
|
+
return fs.statSync(filePath).isFile() &&
|
|
414
|
+
(file.endsWith('.md') || file.endsWith('.json')) &&
|
|
415
|
+
!file.includes('example.json');
|
|
392
416
|
});
|
|
393
417
|
|
|
394
418
|
templateFiles.forEach(file => {
|
|
395
419
|
const sourcePath = path.join(templatesPath, file);
|
|
396
|
-
|
|
420
|
+
let destPath;
|
|
421
|
+
let destLocation;
|
|
422
|
+
|
|
423
|
+
// .md files go to .claude/prompts/, .json files go to .claude/
|
|
424
|
+
if (file.endsWith('.md')) {
|
|
425
|
+
destPath = path.join(promptsDir, file);
|
|
426
|
+
destLocation = '.claude/prompts/';
|
|
427
|
+
} else {
|
|
428
|
+
destPath = path.join(claudeDir, file);
|
|
429
|
+
destLocation = '.claude/';
|
|
430
|
+
}
|
|
397
431
|
|
|
398
432
|
// In force mode or if it doesn't exist, copy the file
|
|
399
433
|
if (isForce || !fs.existsSync(destPath)) {
|
|
400
434
|
if (fs.existsSync(sourcePath)) {
|
|
401
435
|
fs.copyFileSync(sourcePath, destPath);
|
|
402
|
-
success(`${file} installed in
|
|
436
|
+
success(`${file} installed in ${destLocation}`);
|
|
403
437
|
}
|
|
404
438
|
} else {
|
|
405
439
|
info(`${file} already exists (skipped)`);
|
|
406
440
|
}
|
|
407
441
|
});
|
|
408
442
|
|
|
443
|
+
// Clean up old .md files from .claude/ root (v2.8.0 migration)
|
|
444
|
+
// .md files should now be in .claude/prompts/, not .claude/
|
|
445
|
+
const oldMdFiles = fs.readdirSync(claudeDir)
|
|
446
|
+
.filter(file => {
|
|
447
|
+
const filePath = path.join(claudeDir, file);
|
|
448
|
+
return fs.statSync(filePath).isFile() && file.endsWith('.md');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (oldMdFiles.length > 0) {
|
|
452
|
+
oldMdFiles.forEach(file => {
|
|
453
|
+
const oldPath = path.join(claudeDir, file);
|
|
454
|
+
fs.unlinkSync(oldPath);
|
|
455
|
+
info(`Removed old template from .claude/: ${file} (now in prompts/)`);
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
409
459
|
// Copy presets directory structure
|
|
410
460
|
const presetsSourcePath = path.join(templatesPath, 'presets');
|
|
411
461
|
const presetsDestPath = path.join(claudeDir, 'presets');
|
|
@@ -446,15 +496,74 @@ async function install(args) {
|
|
|
446
496
|
success(`${presetDirs.length} presets installed in .claude/presets/`);
|
|
447
497
|
}
|
|
448
498
|
|
|
449
|
-
// Special handling for config.json
|
|
450
|
-
const configExamplePath = path.join(claudeDir, 'config.example.json');
|
|
499
|
+
// Special handling for config.json (v2.8.0+): backup old, create new simplified
|
|
451
500
|
const configPath = path.join(claudeDir, 'config.json');
|
|
501
|
+
const configOldDir = path.join(claudeDir, 'config_old');
|
|
502
|
+
const configExampleDir = path.join(claudeDir, 'config_example');
|
|
503
|
+
|
|
504
|
+
// Create config_old directory if needed
|
|
505
|
+
if (!fs.existsSync(configOldDir)) {
|
|
506
|
+
fs.mkdirSync(configOldDir, { recursive: true });
|
|
507
|
+
}
|
|
452
508
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
509
|
+
// Create config_example directory
|
|
510
|
+
if (!fs.existsSync(configExampleDir)) {
|
|
511
|
+
fs.mkdirSync(configExampleDir, { recursive: true });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Copy example configs to config_example/ directly from templates/
|
|
515
|
+
const exampleConfigs = ['config.example.json', 'config.advanced.example.json'];
|
|
516
|
+
exampleConfigs.forEach(exampleFile => {
|
|
517
|
+
const sourcePath = path.join(templatesPath, exampleFile);
|
|
518
|
+
const destPath = path.join(configExampleDir, exampleFile);
|
|
519
|
+
if (fs.existsSync(sourcePath)) {
|
|
520
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
success('Example configs installed in .claude/config_example/');
|
|
524
|
+
|
|
525
|
+
// Backup existing config if it exists (legacy format migration)
|
|
526
|
+
let needsMigration = false;
|
|
527
|
+
if (fs.existsSync(configPath)) {
|
|
528
|
+
const backupPath = path.join(configOldDir, `config.json.${Date.now()}`);
|
|
529
|
+
fs.copyFileSync(configPath, backupPath);
|
|
530
|
+
info(`Existing config backed up: ${backupPath}`);
|
|
531
|
+
|
|
532
|
+
// Read old config to check if it's legacy format
|
|
533
|
+
const oldConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
534
|
+
if (!oldConfig.version || oldConfig.version !== '2.8.0') {
|
|
535
|
+
warning('Legacy config detected - will be replaced with v2.8.0 format');
|
|
536
|
+
needsMigration = true;
|
|
537
|
+
|
|
538
|
+
// Delete old config to force new format
|
|
539
|
+
fs.unlinkSync(configPath);
|
|
540
|
+
} else {
|
|
541
|
+
info('Config already in v2.8.0 format - keeping existing file');
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Create new config.json from minimal example if it doesn't exist
|
|
546
|
+
if (!fs.existsSync(configPath)) {
|
|
547
|
+
// Read example and extract minimal config
|
|
548
|
+
const examplePath = path.join(configExampleDir, 'config.example.json');
|
|
549
|
+
const exampleContent = fs.readFileSync(examplePath, 'utf8');
|
|
550
|
+
const exampleJson = JSON.parse(exampleContent);
|
|
551
|
+
|
|
552
|
+
// Create minimal config: just version and preset
|
|
553
|
+
const minimalConfig = {
|
|
554
|
+
version: exampleJson.version,
|
|
555
|
+
preset: exampleJson.preset
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
fs.writeFileSync(configPath, JSON.stringify(minimalConfig, null, 4));
|
|
559
|
+
success('config.json created with minimal v2.8.0 format');
|
|
560
|
+
info('📝 Customize: .claude/config.json (see config_example/ for examples)');
|
|
561
|
+
|
|
562
|
+
// Auto-run migration if needed to preserve settings
|
|
563
|
+
if (needsMigration) {
|
|
564
|
+
info('🔄 Auto-migrating settings from backup...');
|
|
565
|
+
await autoMigrateConfig(configPath, path.join(configOldDir, fs.readdirSync(configOldDir).sort().pop()));
|
|
566
|
+
}
|
|
458
567
|
}
|
|
459
568
|
|
|
460
569
|
// Create settings.local.json for sensitive data (gitignored)
|
|
@@ -479,21 +588,31 @@ async function install(args) {
|
|
|
479
588
|
console.log(' git commit -m "auto" # Generate message automatically');
|
|
480
589
|
console.log(' git commit -m "message" # Analyze code before commit');
|
|
481
590
|
console.log(' git commit --no-verify # Skip analysis completely');
|
|
482
|
-
console.log('\n💡 Configuration:');
|
|
591
|
+
console.log('\n💡 Configuration (v2.8.0):');
|
|
483
592
|
console.log(' 📁 All templates installed in .claude/');
|
|
484
|
-
console.log(' 📝 Edit .claude/config.json
|
|
485
|
-
console.log('
|
|
486
|
-
console.log('
|
|
487
|
-
console.log('
|
|
593
|
+
console.log(' 📝 Edit .claude/config.json (minimal by default)');
|
|
594
|
+
console.log(' 📂 Examples: .claude/config_example/');
|
|
595
|
+
console.log(' 📦 Backups: .claude/config_old/');
|
|
596
|
+
console.log(' 🎯 Presets: backend, frontend, fullstack, database, ai, default');
|
|
597
|
+
console.log(' 🚀 Parallel analysis enabled by default (hardcoded)');
|
|
598
|
+
console.log(' 🐛 Debug mode: claude-hooks --debug true');
|
|
488
599
|
console.log('\n🔗 GitHub PR Creation (v2.5.0+):');
|
|
489
|
-
console.log(' claude-hooks setup-github # Configure GitHub token
|
|
490
|
-
console.log(' claude-hooks create-pr main # Create PR with auto-
|
|
491
|
-
console.log('\n📖
|
|
600
|
+
console.log(' claude-hooks setup-github # Configure GitHub token');
|
|
601
|
+
console.log(' claude-hooks create-pr main # Create PR with auto-metadata');
|
|
602
|
+
console.log('\n📖 Minimal config.json (v2.8.0):');
|
|
603
|
+
console.log(' {');
|
|
604
|
+
console.log(' "version": "2.8.0",');
|
|
605
|
+
console.log(' "preset": "backend"');
|
|
606
|
+
console.log(' }');
|
|
607
|
+
console.log('\n📖 With GitHub customization:');
|
|
492
608
|
console.log(' {');
|
|
609
|
+
console.log(' "version": "2.8.0",');
|
|
493
610
|
console.log(' "preset": "backend",');
|
|
494
|
-
console.log(' "
|
|
495
|
-
console.log('
|
|
611
|
+
console.log(' "overrides": {');
|
|
612
|
+
console.log(' "github": { "pr": { "reviewers": ["your-username"] } }');
|
|
613
|
+
console.log(' }');
|
|
496
614
|
console.log(' }');
|
|
615
|
+
console.log('\n🔧 Advanced: see .claude/config_example/config.advanced.example.json');
|
|
497
616
|
console.log('\nFor more options: claude-hooks --help');
|
|
498
617
|
}
|
|
499
618
|
|
|
@@ -954,8 +1073,8 @@ async function analyzeDiff(args) {
|
|
|
954
1073
|
const startTime = Date.now();
|
|
955
1074
|
|
|
956
1075
|
try {
|
|
957
|
-
// Use cross-platform
|
|
958
|
-
const response = await
|
|
1076
|
+
// Use cross-platform executeClaudeWithRetry from claude-client.js
|
|
1077
|
+
const response = await executeClaudeWithRetry(prompt, { timeout: 180000 }); // 3 minutes for diff analysis
|
|
959
1078
|
|
|
960
1079
|
// Extract JSON from response using claude-client utility
|
|
961
1080
|
const result = extractJSON(response);
|
|
@@ -1217,7 +1336,7 @@ async function createPr(args) {
|
|
|
1217
1336
|
|
|
1218
1337
|
showInfo('Generating PR metadata with Claude...');
|
|
1219
1338
|
logger.debug('create-pr', 'Calling Claude with prompt', { promptLength: prompt.length });
|
|
1220
|
-
const response = await
|
|
1339
|
+
const response = await executeClaudeWithRetry(prompt, { timeout: 180000 });
|
|
1221
1340
|
logger.debug('create-pr', 'Claude response received', { responseLength: response.length });
|
|
1222
1341
|
|
|
1223
1342
|
const analysisResult = extractJSON(response);
|
|
@@ -1455,7 +1574,7 @@ function status() {
|
|
|
1455
1574
|
|
|
1456
1575
|
// Check guidelines files
|
|
1457
1576
|
console.log('\nGuidelines files:');
|
|
1458
|
-
const guidelines = ['
|
|
1577
|
+
const guidelines = ['CLAUDE_PRE_COMMIT.md'];
|
|
1459
1578
|
guidelines.forEach(guideline => {
|
|
1460
1579
|
const claudePath = path.join('.claude', guideline);
|
|
1461
1580
|
if (fs.existsSync(claudePath)) {
|
|
@@ -1683,7 +1802,7 @@ Customization:
|
|
|
1683
1802
|
Override prompts by copying to .claude/:
|
|
1684
1803
|
cp templates/COMMIT_MESSAGE.md .claude/
|
|
1685
1804
|
cp templates/ANALYZE_DIFF.md .claude/
|
|
1686
|
-
cp templates/
|
|
1805
|
+
cp templates/CLAUDE_PRE_COMMIT.md .claude/
|
|
1687
1806
|
# Edit as needed - system uses .claude/ version if exists
|
|
1688
1807
|
|
|
1689
1808
|
More information: https://github.com/pablorovito/claude-git-hooks
|
|
@@ -1839,6 +1958,179 @@ async function currentPreset() {
|
|
|
1839
1958
|
}
|
|
1840
1959
|
}
|
|
1841
1960
|
|
|
1961
|
+
// ============================================================================
|
|
1962
|
+
// DEPRECATED CODE SECTION - Will be removed in v3.0.0
|
|
1963
|
+
// ============================================================================
|
|
1964
|
+
// This section contains migration code for legacy configs (pre-v2.8.0)
|
|
1965
|
+
// Auto-executed during install when legacy config detected
|
|
1966
|
+
// Manual command: claude-hooks migrate-config
|
|
1967
|
+
// ============================================================================
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* Extracts allowed settings from legacy config format
|
|
1971
|
+
* Shared by both autoMigrateConfig and migrateConfig
|
|
1972
|
+
*
|
|
1973
|
+
* @param {Object} rawConfig - Legacy format config
|
|
1974
|
+
* @returns {Object} Allowed overrides only
|
|
1975
|
+
*/
|
|
1976
|
+
function extractLegacySettings(rawConfig) {
|
|
1977
|
+
const allowedOverrides = {};
|
|
1978
|
+
|
|
1979
|
+
// GitHub PR config (fully allowed)
|
|
1980
|
+
if (rawConfig.github?.pr) {
|
|
1981
|
+
allowedOverrides.github = { pr: {} };
|
|
1982
|
+
if (rawConfig.github.pr.defaultBase !== undefined) {
|
|
1983
|
+
allowedOverrides.github.pr.defaultBase = rawConfig.github.pr.defaultBase;
|
|
1984
|
+
}
|
|
1985
|
+
if (rawConfig.github.pr.reviewers !== undefined) {
|
|
1986
|
+
allowedOverrides.github.pr.reviewers = rawConfig.github.pr.reviewers;
|
|
1987
|
+
}
|
|
1988
|
+
if (rawConfig.github.pr.labelRules !== undefined) {
|
|
1989
|
+
allowedOverrides.github.pr.labelRules = rawConfig.github.pr.labelRules;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Subagent batchSize (allowed)
|
|
1994
|
+
if (rawConfig.subagents?.batchSize !== undefined) {
|
|
1995
|
+
allowedOverrides.subagents = { batchSize: rawConfig.subagents.batchSize };
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// Advanced params (preserved with warning in manual migration)
|
|
1999
|
+
if (rawConfig.analysis?.ignoreExtensions !== undefined) {
|
|
2000
|
+
if (!allowedOverrides.analysis) allowedOverrides.analysis = {};
|
|
2001
|
+
allowedOverrides.analysis.ignoreExtensions = rawConfig.analysis.ignoreExtensions;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
if (rawConfig.commitMessage?.taskIdPattern !== undefined) {
|
|
2005
|
+
if (!allowedOverrides.commitMessage) allowedOverrides.commitMessage = {};
|
|
2006
|
+
allowedOverrides.commitMessage.taskIdPattern = rawConfig.commitMessage.taskIdPattern;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
if (rawConfig.subagents?.model !== undefined) {
|
|
2010
|
+
if (!allowedOverrides.subagents) allowedOverrides.subagents = {};
|
|
2011
|
+
allowedOverrides.subagents.model = rawConfig.subagents.model;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
return allowedOverrides;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
/**
|
|
2018
|
+
* Auto-migrates legacy config during installation
|
|
2019
|
+
* Called automatically by installer when legacy format detected
|
|
2020
|
+
*
|
|
2021
|
+
* @param {string} newConfigPath - Path to new config.json
|
|
2022
|
+
* @param {string} backupConfigPath - Path to backup of old config
|
|
2023
|
+
*/
|
|
2024
|
+
async function autoMigrateConfig(newConfigPath, backupConfigPath) {
|
|
2025
|
+
try {
|
|
2026
|
+
const rawConfig = JSON.parse(fs.readFileSync(backupConfigPath, 'utf8'));
|
|
2027
|
+
const allowedOverrides = extractLegacySettings(rawConfig);
|
|
2028
|
+
|
|
2029
|
+
// Read the newly created minimal config
|
|
2030
|
+
const newConfig = JSON.parse(fs.readFileSync(newConfigPath, 'utf8'));
|
|
2031
|
+
|
|
2032
|
+
// Add overrides if any
|
|
2033
|
+
if (Object.keys(allowedOverrides).length > 0) {
|
|
2034
|
+
newConfig.overrides = allowedOverrides;
|
|
2035
|
+
fs.writeFileSync(newConfigPath, JSON.stringify(newConfig, null, 4));
|
|
2036
|
+
success('✅ Settings migrated from legacy config');
|
|
2037
|
+
info(`📋 Preserved ${Object.keys(allowedOverrides).length} custom settings`);
|
|
2038
|
+
}
|
|
2039
|
+
} catch (err) {
|
|
2040
|
+
warning(`⚠️ Could not auto-migrate settings: ${err.message}`);
|
|
2041
|
+
info('💡 Run "claude-hooks migrate-config" manually if needed');
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* Migrates legacy config.json to v2.8.0 format (Manual command)
|
|
2047
|
+
* Why: Simplifies configuration, reduces redundancy
|
|
2048
|
+
*
|
|
2049
|
+
* DEPRECATED: Will be removed in v3.0.0 (most users will have migrated by then)
|
|
2050
|
+
*/
|
|
2051
|
+
async function migrateConfig() {
|
|
2052
|
+
const claudeDir = '.claude';
|
|
2053
|
+
const configPath = path.join(claudeDir, 'config.json');
|
|
2054
|
+
|
|
2055
|
+
if (!fs.existsSync(configPath)) {
|
|
2056
|
+
info('ℹ️ No config file found. Nothing to migrate.');
|
|
2057
|
+
console.log('\n💡 To create a new config:');
|
|
2058
|
+
console.log(' 1. Run: claude-hooks install --force');
|
|
2059
|
+
console.log(' 2. Or copy from: .claude/config_example/config.example.json');
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
try {
|
|
2064
|
+
const rawConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
2065
|
+
|
|
2066
|
+
// Check if already in v2.8.0 format
|
|
2067
|
+
if (rawConfig.version === '2.8.0') {
|
|
2068
|
+
success('✅ Config is already in v2.8.0 format.');
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
info('📦 Starting config migration to v2.8.0...');
|
|
2073
|
+
|
|
2074
|
+
// Create backup in config_old/
|
|
2075
|
+
const configOldDir = path.join(claudeDir, 'config_old');
|
|
2076
|
+
if (!fs.existsSync(configOldDir)) {
|
|
2077
|
+
fs.mkdirSync(configOldDir, { recursive: true });
|
|
2078
|
+
}
|
|
2079
|
+
const backupPath = path.join(configOldDir, `config.json.${Date.now()}`);
|
|
2080
|
+
fs.copyFileSync(configPath, backupPath);
|
|
2081
|
+
success(`Backup created: ${backupPath}`);
|
|
2082
|
+
|
|
2083
|
+
// Extract only allowed parameters
|
|
2084
|
+
const allowedOverrides = extractLegacySettings(rawConfig);
|
|
2085
|
+
|
|
2086
|
+
// Check for advanced params
|
|
2087
|
+
const hasAdvancedParams = allowedOverrides.analysis?.ignoreExtensions ||
|
|
2088
|
+
allowedOverrides.commitMessage?.taskIdPattern ||
|
|
2089
|
+
allowedOverrides.subagents?.model;
|
|
2090
|
+
|
|
2091
|
+
// Build new config
|
|
2092
|
+
const newConfig = {
|
|
2093
|
+
version: '2.8.0',
|
|
2094
|
+
preset: rawConfig.preset || 'default'
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
// Only add overrides if there are any
|
|
2098
|
+
if (Object.keys(allowedOverrides).length > 0) {
|
|
2099
|
+
newConfig.overrides = allowedOverrides;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// Show diff
|
|
2103
|
+
console.log('\n📝 Migration preview:');
|
|
2104
|
+
console.log(` Old format: ${Object.keys(rawConfig).length} top-level keys`);
|
|
2105
|
+
console.log(` New format: ${Object.keys(newConfig).length} top-level keys`);
|
|
2106
|
+
if (Object.keys(allowedOverrides).length > 0) {
|
|
2107
|
+
console.log(` Preserved: ${Object.keys(allowedOverrides).length} override sections`);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// Write new config
|
|
2111
|
+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 4));
|
|
2112
|
+
success('✅ Config migrated to v2.8.0 successfully!');
|
|
2113
|
+
|
|
2114
|
+
if (hasAdvancedParams) {
|
|
2115
|
+
warning('⚠️ Advanced parameters detected and preserved');
|
|
2116
|
+
info('📖 See .claude/config.advanced.example.json for documentation');
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
console.log(`\n✨ New config:`);
|
|
2120
|
+
console.log(JSON.stringify(newConfig, null, 2));
|
|
2121
|
+
console.log(`\n💾 Old config backed up to: ${backupPath}`);
|
|
2122
|
+
console.log('\n💡 Many parameters are now hardcoded with sensible defaults');
|
|
2123
|
+
console.log(' See CHANGELOG.md for full list of changes');
|
|
2124
|
+
|
|
2125
|
+
} catch (error) {
|
|
2126
|
+
error(`Failed to migrate config: ${error.message}`);
|
|
2127
|
+
console.log('\n💡 Manual migration:');
|
|
2128
|
+
console.log(' 1. Backup your current config');
|
|
2129
|
+
console.log(' 2. See .claude/config.example.json for new format');
|
|
2130
|
+
console.log(' 3. Copy minimal example and customize');
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
|
|
1842
2134
|
/**
|
|
1843
2135
|
* Sets debug mode
|
|
1844
2136
|
* Why: Enables detailed logging for troubleshooting
|
|
@@ -1928,6 +2220,9 @@ async function main() {
|
|
|
1928
2220
|
error(`Unknown preset subcommand: ${args[1]}`);
|
|
1929
2221
|
}
|
|
1930
2222
|
break;
|
|
2223
|
+
case 'migrate-config':
|
|
2224
|
+
await migrateConfig();
|
|
2225
|
+
break;
|
|
1931
2226
|
case '--debug':
|
|
1932
2227
|
await setDebug(args[1]);
|
|
1933
2228
|
break;
|