agileflow 2.72.0 → 2.74.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/package.json +1 -1
- package/scripts/agileflow-configure.js +434 -85
- package/src/core/agents/configuration/archival.md +10 -10
- package/src/core/commands/configure.md +155 -32
- package/tools/cli/installers/core/installer.js +3 -11
- package/src/core/templates/agileflow-configure.js +0 -1033
- package/src/core/templates/agileflow-statusline.sh +0 -355
- package/src/core/templates/agileflow-welcome.js +0 -731
- package/src/core/templates/clear-active-command.js +0 -42
- package/src/core/templates/init.sh +0 -76
- package/src/core/templates/precompact-context.sh +0 -123
- package/src/core/templates/resume-session.sh +0 -121
- package/src/core/templates/validate-tokens.sh +0 -73
- package/src/core/templates/worktree-create.sh +0 -111
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - RECONFIGURE: Change settings (archival days, etc.)
|
|
12
12
|
*
|
|
13
13
|
* Usage:
|
|
14
|
-
* node scripts/agileflow-configure.js [options]
|
|
14
|
+
* node .agileflow/scripts/agileflow-configure.js [options]
|
|
15
15
|
*
|
|
16
16
|
* Options:
|
|
17
17
|
* --profile=full|basic|minimal|none Apply a preset
|
|
@@ -77,6 +77,34 @@ const FEATURES = {
|
|
|
77
77
|
autoupdate: { metadataOnly: true }, // Stored in metadata.updates.autoUpdate
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
// Complete registry of all scripts that may need repair
|
|
81
|
+
const ALL_SCRIPTS = {
|
|
82
|
+
// Core feature scripts (linked to FEATURES)
|
|
83
|
+
'agileflow-welcome.js': { feature: 'sessionstart', required: true },
|
|
84
|
+
'precompact-context.sh': { feature: 'precompact', required: true },
|
|
85
|
+
'archive-completed-stories.sh': { feature: 'archival', required: true },
|
|
86
|
+
'agileflow-statusline.sh': { feature: 'statusline', required: true },
|
|
87
|
+
|
|
88
|
+
// Support scripts (used by commands/agents)
|
|
89
|
+
'obtain-context.js': { usedBy: ['/babysit', '/mentor', '/sprint'] },
|
|
90
|
+
'session-manager.js': { usedBy: ['/session:new', '/session:resume'] },
|
|
91
|
+
'check-update.js': { usedBy: ['SessionStart hook'] },
|
|
92
|
+
'get-env.js': { usedBy: ['SessionStart hook'] },
|
|
93
|
+
'clear-active-command.js': { usedBy: ['session commands'] },
|
|
94
|
+
|
|
95
|
+
// Utility scripts
|
|
96
|
+
'compress-status.sh': { usedBy: ['/compress'] },
|
|
97
|
+
'validate-expertise.sh': { usedBy: ['/validate-expertise'] },
|
|
98
|
+
'expertise-metrics.sh': { usedBy: ['agent experts'] },
|
|
99
|
+
'session-coordinator.sh': { usedBy: ['session management'] },
|
|
100
|
+
'validate-tokens.sh': { usedBy: ['token validation'] },
|
|
101
|
+
'worktree-create.sh': { usedBy: ['/session:new'] },
|
|
102
|
+
'resume-session.sh': { usedBy: ['/session:resume'] },
|
|
103
|
+
'init.sh': { usedBy: ['/session:init'] },
|
|
104
|
+
'agileflow-configure.js': { usedBy: ['/configure'] },
|
|
105
|
+
'generate-all.sh': { usedBy: ['content generation'] },
|
|
106
|
+
};
|
|
107
|
+
|
|
80
108
|
// Statusline component names
|
|
81
109
|
const STATUSLINE_COMPONENTS = [
|
|
82
110
|
'agileflow',
|
|
@@ -93,19 +121,19 @@ const PROFILES = {
|
|
|
93
121
|
full: {
|
|
94
122
|
description: 'All features enabled',
|
|
95
123
|
enable: ['sessionstart', 'precompact', 'archival', 'statusline'],
|
|
96
|
-
archivalDays:
|
|
124
|
+
archivalDays: 30,
|
|
97
125
|
},
|
|
98
126
|
basic: {
|
|
99
127
|
description: 'Essential hooks + archival (SessionStart + PreCompact + Archival)',
|
|
100
128
|
enable: ['sessionstart', 'precompact', 'archival'],
|
|
101
129
|
disable: ['statusline'],
|
|
102
|
-
archivalDays:
|
|
130
|
+
archivalDays: 30,
|
|
103
131
|
},
|
|
104
132
|
minimal: {
|
|
105
133
|
description: 'SessionStart + archival only',
|
|
106
134
|
enable: ['sessionstart', 'archival'],
|
|
107
135
|
disable: ['precompact', 'statusline'],
|
|
108
|
-
archivalDays:
|
|
136
|
+
archivalDays: 30,
|
|
109
137
|
},
|
|
110
138
|
none: {
|
|
111
139
|
description: 'Disable all AgileFlow features',
|
|
@@ -155,22 +183,16 @@ const writeJSON = (filePath, data) => {
|
|
|
155
183
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
156
184
|
};
|
|
157
185
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
fs.chmodSync(destPath, '755');
|
|
169
|
-
} catch {}
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return false;
|
|
186
|
+
// Scripts are located in .agileflow/scripts/ (installed by AgileFlow)
|
|
187
|
+
const SCRIPTS_DIR = path.join(process.cwd(), '.agileflow', 'scripts');
|
|
188
|
+
|
|
189
|
+
const scriptExists = scriptName => {
|
|
190
|
+
const scriptPath = path.join(SCRIPTS_DIR, scriptName);
|
|
191
|
+
return fs.existsSync(scriptPath);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const getScriptPath = scriptName => {
|
|
195
|
+
return `.agileflow/scripts/${scriptName}`;
|
|
174
196
|
};
|
|
175
197
|
|
|
176
198
|
// ============================================================================
|
|
@@ -184,12 +206,14 @@ function detectConfig() {
|
|
|
184
206
|
settingsValid: true,
|
|
185
207
|
settingsIssues: [],
|
|
186
208
|
features: {
|
|
187
|
-
sessionstart: { enabled: false, valid: true, issues: [] },
|
|
188
|
-
precompact: { enabled: false, valid: true, issues: [] },
|
|
189
|
-
archival: { enabled: false, threshold: null },
|
|
190
|
-
statusline: { enabled: false, valid: true, issues: [] },
|
|
209
|
+
sessionstart: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
210
|
+
precompact: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
211
|
+
archival: { enabled: false, threshold: null, version: null, outdated: false },
|
|
212
|
+
statusline: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
191
213
|
},
|
|
192
214
|
metadata: { exists: false, version: null },
|
|
215
|
+
currentVersion: VERSION,
|
|
216
|
+
hasOutdated: false,
|
|
193
217
|
};
|
|
194
218
|
|
|
195
219
|
// Git
|
|
@@ -280,6 +304,20 @@ function detectConfig() {
|
|
|
280
304
|
status.features.archival.enabled = true;
|
|
281
305
|
status.features.archival.threshold = meta.archival.threshold_days;
|
|
282
306
|
}
|
|
307
|
+
|
|
308
|
+
// Read feature versions from metadata and check if outdated
|
|
309
|
+
if (meta.features) {
|
|
310
|
+
Object.entries(meta.features).forEach(([feature, data]) => {
|
|
311
|
+
if (status.features[feature] && data.version) {
|
|
312
|
+
status.features[feature].version = data.version;
|
|
313
|
+
// Check if feature version differs from current VERSION
|
|
314
|
+
if (data.version !== VERSION && status.features[feature].enabled) {
|
|
315
|
+
status.features[feature].outdated = true;
|
|
316
|
+
status.hasOutdated = true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
283
321
|
}
|
|
284
322
|
}
|
|
285
323
|
|
|
@@ -317,6 +355,10 @@ function printStatus(status) {
|
|
|
317
355
|
statusIcon = '⚠️';
|
|
318
356
|
statusText = 'INVALID FORMAT';
|
|
319
357
|
color = c.yellow;
|
|
358
|
+
} else if (f.enabled && f.outdated) {
|
|
359
|
+
statusIcon = '🔄';
|
|
360
|
+
statusText = `outdated (v${f.version} → v${status.currentVersion})`;
|
|
361
|
+
color = c.yellow;
|
|
320
362
|
}
|
|
321
363
|
|
|
322
364
|
log(` ${statusIcon} ${label}: ${statusText}`, color);
|
|
@@ -348,7 +390,11 @@ function printStatus(status) {
|
|
|
348
390
|
log('\n⚠️ Format issues detected! Run with --migrate to fix.', c.yellow);
|
|
349
391
|
}
|
|
350
392
|
|
|
351
|
-
|
|
393
|
+
if (status.hasOutdated) {
|
|
394
|
+
log('\n🔄 Outdated scripts detected! Run with --upgrade to update.', c.yellow);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return { hasIssues, hasOutdated: status.hasOutdated };
|
|
352
398
|
}
|
|
353
399
|
|
|
354
400
|
// ============================================================================
|
|
@@ -454,6 +500,34 @@ function migrateSettings() {
|
|
|
454
500
|
return migrated;
|
|
455
501
|
}
|
|
456
502
|
|
|
503
|
+
// ============================================================================
|
|
504
|
+
// UPGRADE FEATURES
|
|
505
|
+
// ============================================================================
|
|
506
|
+
|
|
507
|
+
function upgradeFeatures(status) {
|
|
508
|
+
header('🔄 Upgrading Outdated Features...');
|
|
509
|
+
|
|
510
|
+
let upgraded = 0;
|
|
511
|
+
|
|
512
|
+
Object.entries(status.features).forEach(([feature, data]) => {
|
|
513
|
+
if (data.enabled && data.outdated) {
|
|
514
|
+
log(`\nUpgrading ${feature}...`, c.cyan);
|
|
515
|
+
// Re-enable the feature to deploy latest scripts
|
|
516
|
+
if (enableFeature(feature, { archivalDays: data.threshold || 30, isUpgrade: true })) {
|
|
517
|
+
upgraded++;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
if (upgraded === 0) {
|
|
523
|
+
info('No features needed upgrading');
|
|
524
|
+
} else {
|
|
525
|
+
success(`Upgraded ${upgraded} feature(s) to v${VERSION}`);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return upgraded > 0;
|
|
529
|
+
}
|
|
530
|
+
|
|
457
531
|
// ============================================================================
|
|
458
532
|
// ENABLE/DISABLE FEATURES
|
|
459
533
|
// ============================================================================
|
|
@@ -466,7 +540,6 @@ function enableFeature(feature, options = {}) {
|
|
|
466
540
|
}
|
|
467
541
|
|
|
468
542
|
ensureDir('.claude');
|
|
469
|
-
ensureDir('scripts');
|
|
470
543
|
|
|
471
544
|
const settings = readJSON('.claude/settings.json') || {};
|
|
472
545
|
settings.hooks = settings.hooks || {};
|
|
@@ -474,25 +547,13 @@ function enableFeature(feature, options = {}) {
|
|
|
474
547
|
|
|
475
548
|
// Handle hook-based features
|
|
476
549
|
if (config.hook) {
|
|
477
|
-
const scriptPath =
|
|
478
|
-
|
|
479
|
-
//
|
|
480
|
-
if (!
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
scriptPath,
|
|
485
|
-
`#!/usr/bin/env node\nconsole.log('AgileFlow v${VERSION} loaded');\n`
|
|
486
|
-
);
|
|
487
|
-
} else if (feature === 'precompact') {
|
|
488
|
-
fs.writeFileSync(scriptPath, '#!/bin/bash\necho "PreCompact: preserving context"\n');
|
|
489
|
-
}
|
|
490
|
-
try {
|
|
491
|
-
fs.chmodSync(scriptPath, '755');
|
|
492
|
-
} catch {}
|
|
493
|
-
warn(`Created minimal ${config.script}`);
|
|
494
|
-
} else {
|
|
495
|
-
success(`Deployed ${config.script}`);
|
|
550
|
+
const scriptPath = getScriptPath(config.script);
|
|
551
|
+
|
|
552
|
+
// Verify script exists
|
|
553
|
+
if (!scriptExists(config.script)) {
|
|
554
|
+
error(`Script not found: ${scriptPath}`);
|
|
555
|
+
info('Run "npx agileflow update" to reinstall scripts');
|
|
556
|
+
return false;
|
|
496
557
|
}
|
|
497
558
|
|
|
498
559
|
// Configure hook
|
|
@@ -504,18 +565,18 @@ function enableFeature(feature, options = {}) {
|
|
|
504
565
|
hooks: [{ type: 'command', command }],
|
|
505
566
|
},
|
|
506
567
|
];
|
|
507
|
-
success(`${config.hook} hook enabled`);
|
|
568
|
+
success(`${config.hook} hook enabled (${config.script})`);
|
|
508
569
|
}
|
|
509
570
|
|
|
510
571
|
// Handle archival
|
|
511
572
|
if (feature === 'archival') {
|
|
512
|
-
const days = options.archivalDays ||
|
|
513
|
-
const scriptPath = '
|
|
573
|
+
const days = options.archivalDays || 30;
|
|
574
|
+
const scriptPath = getScriptPath('archive-completed-stories.sh');
|
|
514
575
|
|
|
515
|
-
if (!
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
576
|
+
if (!scriptExists('archive-completed-stories.sh')) {
|
|
577
|
+
error(`Script not found: ${scriptPath}`);
|
|
578
|
+
info('Run "npx agileflow update" to reinstall scripts');
|
|
579
|
+
return false;
|
|
519
580
|
}
|
|
520
581
|
|
|
521
582
|
// Add to SessionStart hook
|
|
@@ -526,7 +587,7 @@ function enableFeature(feature, options = {}) {
|
|
|
526
587
|
if (!hasArchival) {
|
|
527
588
|
settings.hooks.SessionStart[0].hooks.push({
|
|
528
589
|
type: 'command',
|
|
529
|
-
command:
|
|
590
|
+
command: `bash ${scriptPath} --quiet`,
|
|
530
591
|
});
|
|
531
592
|
}
|
|
532
593
|
}
|
|
@@ -538,28 +599,17 @@ function enableFeature(feature, options = {}) {
|
|
|
538
599
|
|
|
539
600
|
// Handle statusLine
|
|
540
601
|
if (feature === 'statusline') {
|
|
541
|
-
const scriptPath = '
|
|
542
|
-
|
|
543
|
-
if (!
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
input=$(cat)
|
|
548
|
-
MODEL=$(echo "$input" | jq -r '.model.display_name // "Claude"')
|
|
549
|
-
echo "[$MODEL] AgileFlow"
|
|
550
|
-
`
|
|
551
|
-
);
|
|
552
|
-
try {
|
|
553
|
-
fs.chmodSync(scriptPath, '755');
|
|
554
|
-
} catch {}
|
|
555
|
-
warn('Created minimal statusline script');
|
|
556
|
-
} else {
|
|
557
|
-
success('Deployed agileflow-statusline.sh');
|
|
602
|
+
const scriptPath = getScriptPath('agileflow-statusline.sh');
|
|
603
|
+
|
|
604
|
+
if (!scriptExists('agileflow-statusline.sh')) {
|
|
605
|
+
error(`Script not found: ${scriptPath}`);
|
|
606
|
+
info('Run "npx agileflow update" to reinstall scripts');
|
|
607
|
+
return false;
|
|
558
608
|
}
|
|
559
609
|
|
|
560
610
|
settings.statusLine = {
|
|
561
611
|
type: 'command',
|
|
562
|
-
command:
|
|
612
|
+
command: `bash ${scriptPath}`,
|
|
563
613
|
padding: 0,
|
|
564
614
|
};
|
|
565
615
|
success('Status line enabled');
|
|
@@ -789,6 +839,240 @@ function listStatuslineComponents() {
|
|
|
789
839
|
log(`Components: ${STATUSLINE_COMPONENTS.join(', ')}`, c.dim);
|
|
790
840
|
}
|
|
791
841
|
|
|
842
|
+
// ============================================================================
|
|
843
|
+
// REPAIR & DIAGNOSTICS
|
|
844
|
+
// ============================================================================
|
|
845
|
+
|
|
846
|
+
const crypto = require('crypto');
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Calculate SHA256 hash of a file
|
|
850
|
+
*/
|
|
851
|
+
function sha256(data) {
|
|
852
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Get the source scripts directory (from npm package)
|
|
857
|
+
*/
|
|
858
|
+
function getSourceScriptsDir() {
|
|
859
|
+
// When running from installed package, __dirname is .agileflow/scripts
|
|
860
|
+
// The source is the same directory since it was copied during install
|
|
861
|
+
// But for repair, we need the npm package source
|
|
862
|
+
|
|
863
|
+
// Try to find the npm package (when run via npx or global install)
|
|
864
|
+
const possiblePaths = [
|
|
865
|
+
path.join(__dirname, '..', '..', 'scripts'), // npm package structure
|
|
866
|
+
path.join(__dirname), // Same directory (for development)
|
|
867
|
+
];
|
|
868
|
+
|
|
869
|
+
for (const p of possiblePaths) {
|
|
870
|
+
if (fs.existsSync(p) && fs.existsSync(path.join(p, 'agileflow-welcome.js'))) {
|
|
871
|
+
return p;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* List all scripts with their status (present/missing/modified)
|
|
880
|
+
*/
|
|
881
|
+
function listScripts() {
|
|
882
|
+
header('📋 Installed Scripts');
|
|
883
|
+
|
|
884
|
+
const scriptsDir = path.join(process.cwd(), '.agileflow', 'scripts');
|
|
885
|
+
const fileIndexPath = path.join(process.cwd(), '.agileflow', '_cfg', 'files.json');
|
|
886
|
+
const fileIndex = readJSON(fileIndexPath);
|
|
887
|
+
|
|
888
|
+
let missing = 0;
|
|
889
|
+
let modified = 0;
|
|
890
|
+
let present = 0;
|
|
891
|
+
|
|
892
|
+
Object.entries(ALL_SCRIPTS).forEach(([script, info]) => {
|
|
893
|
+
const scriptPath = path.join(scriptsDir, script);
|
|
894
|
+
const exists = fs.existsSync(scriptPath);
|
|
895
|
+
|
|
896
|
+
// Check if modified (compare to file index hash)
|
|
897
|
+
let isModified = false;
|
|
898
|
+
if (exists && fileIndex?.files?.[`scripts/${script}`]) {
|
|
899
|
+
try {
|
|
900
|
+
const currentHash = sha256(fs.readFileSync(scriptPath));
|
|
901
|
+
const indexHash = fileIndex.files[`scripts/${script}`].sha256;
|
|
902
|
+
isModified = currentHash !== indexHash;
|
|
903
|
+
} catch {}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Print status
|
|
907
|
+
if (!exists) {
|
|
908
|
+
log(` ❌ ${script}: MISSING`, c.red);
|
|
909
|
+
if (info.usedBy) log(` └─ Used by: ${info.usedBy.join(', ')}`, c.dim);
|
|
910
|
+
if (info.feature) log(` └─ Feature: ${info.feature}`, c.dim);
|
|
911
|
+
missing++;
|
|
912
|
+
} else if (isModified) {
|
|
913
|
+
log(` ⚠️ ${script}: modified (local changes)`, c.yellow);
|
|
914
|
+
modified++;
|
|
915
|
+
} else {
|
|
916
|
+
log(` ✅ ${script}: present`, c.green);
|
|
917
|
+
present++;
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
// Summary
|
|
922
|
+
log('');
|
|
923
|
+
log(`Summary: ${present} present, ${modified} modified, ${missing} missing`, c.dim);
|
|
924
|
+
|
|
925
|
+
if (missing > 0) {
|
|
926
|
+
log('\n💡 Run with --repair to restore missing scripts', c.yellow);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Show version information
|
|
932
|
+
*/
|
|
933
|
+
function showVersionInfo() {
|
|
934
|
+
header('📊 Version Information');
|
|
935
|
+
|
|
936
|
+
const meta = readJSON('docs/00-meta/agileflow-metadata.json') || {};
|
|
937
|
+
const manifest = readJSON('.agileflow/_cfg/manifest.yaml');
|
|
938
|
+
|
|
939
|
+
const installedVersion = meta.version || 'unknown';
|
|
940
|
+
|
|
941
|
+
log(`Installed: v${installedVersion}`);
|
|
942
|
+
log(`CLI: v${VERSION}`);
|
|
943
|
+
|
|
944
|
+
// Check npm for latest
|
|
945
|
+
let latestVersion = null;
|
|
946
|
+
try {
|
|
947
|
+
latestVersion = execSync('npm view agileflow version 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
948
|
+
log(`Latest: v${latestVersion}`);
|
|
949
|
+
|
|
950
|
+
if (installedVersion !== 'unknown' && latestVersion && installedVersion !== latestVersion) {
|
|
951
|
+
const installed = installedVersion.split('.').map(Number);
|
|
952
|
+
const latest = latestVersion.split('.').map(Number);
|
|
953
|
+
|
|
954
|
+
if (latest[0] > installed[0] ||
|
|
955
|
+
(latest[0] === installed[0] && latest[1] > installed[1]) ||
|
|
956
|
+
(latest[0] === installed[0] && latest[1] === installed[1] && latest[2] > installed[2])) {
|
|
957
|
+
log('\n🔄 Update available! Run: npx agileflow update', c.yellow);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
} catch {
|
|
961
|
+
log('Latest: (could not check npm)', c.dim);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Show per-feature versions
|
|
965
|
+
if (meta.features && Object.keys(meta.features).length > 0) {
|
|
966
|
+
header('Feature Versions:');
|
|
967
|
+
Object.entries(meta.features).forEach(([feature, data]) => {
|
|
968
|
+
if (!data) return;
|
|
969
|
+
const featureVersion = data.version || 'unknown';
|
|
970
|
+
const enabled = data.enabled !== false;
|
|
971
|
+
const outdated = featureVersion !== VERSION && enabled;
|
|
972
|
+
|
|
973
|
+
let icon = '❌';
|
|
974
|
+
let color = c.dim;
|
|
975
|
+
let statusText = `v${featureVersion}`;
|
|
976
|
+
|
|
977
|
+
if (!enabled) {
|
|
978
|
+
statusText = 'disabled';
|
|
979
|
+
} else if (outdated) {
|
|
980
|
+
icon = '🔄';
|
|
981
|
+
color = c.yellow;
|
|
982
|
+
statusText = `v${featureVersion} → v${VERSION}`;
|
|
983
|
+
} else {
|
|
984
|
+
icon = '✅';
|
|
985
|
+
color = c.green;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
log(` ${icon} ${feature}: ${statusText}`, color);
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Show installation metadata
|
|
993
|
+
if (meta.created || meta.updated) {
|
|
994
|
+
header('Installation:');
|
|
995
|
+
if (meta.created) log(` Created: ${new Date(meta.created).toLocaleDateString()}`, c.dim);
|
|
996
|
+
if (meta.updated) log(` Updated: ${new Date(meta.updated).toLocaleDateString()}`, c.dim);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Repair missing or corrupted scripts
|
|
1002
|
+
*/
|
|
1003
|
+
function repairScripts(targetFeature = null) {
|
|
1004
|
+
header('🔧 Repairing Scripts...');
|
|
1005
|
+
|
|
1006
|
+
const scriptsDir = path.join(process.cwd(), '.agileflow', 'scripts');
|
|
1007
|
+
const sourceDir = getSourceScriptsDir();
|
|
1008
|
+
|
|
1009
|
+
if (!sourceDir) {
|
|
1010
|
+
warn('Could not find source scripts directory');
|
|
1011
|
+
info('Try running: npx agileflow@latest update');
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
let repaired = 0;
|
|
1016
|
+
let errors = 0;
|
|
1017
|
+
let skipped = 0;
|
|
1018
|
+
|
|
1019
|
+
// Determine which scripts to check
|
|
1020
|
+
const scriptsToCheck = targetFeature
|
|
1021
|
+
? Object.entries(ALL_SCRIPTS).filter(([_, info]) => info.feature === targetFeature)
|
|
1022
|
+
: Object.entries(ALL_SCRIPTS);
|
|
1023
|
+
|
|
1024
|
+
if (scriptsToCheck.length === 0 && targetFeature) {
|
|
1025
|
+
error(`Unknown feature: ${targetFeature}`);
|
|
1026
|
+
log(`Available features: ${Object.keys(FEATURES).join(', ')}`, c.dim);
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Ensure scripts directory exists
|
|
1031
|
+
ensureDir(scriptsDir);
|
|
1032
|
+
|
|
1033
|
+
for (const [script, info] of scriptsToCheck) {
|
|
1034
|
+
const destPath = path.join(scriptsDir, script);
|
|
1035
|
+
const srcPath = path.join(sourceDir, script);
|
|
1036
|
+
|
|
1037
|
+
if (!fs.existsSync(destPath)) {
|
|
1038
|
+
// Script is missing - reinstall from source
|
|
1039
|
+
if (fs.existsSync(srcPath)) {
|
|
1040
|
+
try {
|
|
1041
|
+
fs.copyFileSync(srcPath, destPath);
|
|
1042
|
+
// Make executable
|
|
1043
|
+
try {
|
|
1044
|
+
fs.chmodSync(destPath, 0o755);
|
|
1045
|
+
} catch {}
|
|
1046
|
+
success(`Restored ${script}`);
|
|
1047
|
+
repaired++;
|
|
1048
|
+
} catch (err) {
|
|
1049
|
+
error(`Failed to restore ${script}: ${err.message}`);
|
|
1050
|
+
errors++;
|
|
1051
|
+
}
|
|
1052
|
+
} else {
|
|
1053
|
+
warn(`Source not found for ${script}`);
|
|
1054
|
+
errors++;
|
|
1055
|
+
}
|
|
1056
|
+
} else {
|
|
1057
|
+
skipped++;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// Summary
|
|
1062
|
+
log('');
|
|
1063
|
+
if (repaired === 0 && errors === 0) {
|
|
1064
|
+
info('All scripts present - nothing to repair');
|
|
1065
|
+
} else {
|
|
1066
|
+
log(`Repaired: ${repaired}, Errors: ${errors}, Skipped: ${skipped}`, c.dim);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
if (errors > 0) {
|
|
1070
|
+
log('\n💡 For comprehensive repair, run: npx agileflow update --force', c.yellow);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
return repaired > 0;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
792
1076
|
// ============================================================================
|
|
793
1077
|
// PROFILES
|
|
794
1078
|
// ============================================================================
|
|
@@ -855,7 +1139,7 @@ function printHelp() {
|
|
|
855
1139
|
${c.bold}AgileFlow Configure${c.reset} - Manage AgileFlow features
|
|
856
1140
|
|
|
857
1141
|
${c.cyan}Usage:${c.reset}
|
|
858
|
-
node scripts/agileflow-configure.js [options]
|
|
1142
|
+
node .agileflow/scripts/agileflow-configure.js [options]
|
|
859
1143
|
|
|
860
1144
|
${c.cyan}Profiles:${c.reset}
|
|
861
1145
|
--profile=full All features (hooks, archival, statusline)
|
|
@@ -877,37 +1161,59 @@ ${c.cyan}Statusline Components:${c.reset}
|
|
|
877
1161
|
Components: agileflow, model, story, epic, wip, context, cost, git
|
|
878
1162
|
|
|
879
1163
|
${c.cyan}Settings:${c.reset}
|
|
880
|
-
--archival-days=N Set archival threshold (default:
|
|
1164
|
+
--archival-days=N Set archival threshold (default: 30)
|
|
881
1165
|
|
|
882
1166
|
${c.cyan}Maintenance:${c.reset}
|
|
883
1167
|
--migrate Fix old/invalid formats
|
|
1168
|
+
--upgrade Re-deploy all enabled features with latest scripts
|
|
884
1169
|
--validate Check for issues (same as --detect)
|
|
885
1170
|
--detect Show current configuration
|
|
886
1171
|
|
|
1172
|
+
${c.cyan}Repair & Diagnostics:${c.reset}
|
|
1173
|
+
--repair Check for and restore missing scripts
|
|
1174
|
+
--repair=<feature> Repair scripts for a specific feature (e.g., statusline)
|
|
1175
|
+
--version Show installed vs latest version info
|
|
1176
|
+
--list-scripts List all scripts with their status (missing/present/modified)
|
|
1177
|
+
|
|
887
1178
|
${c.cyan}Examples:${c.reset}
|
|
888
1179
|
# Quick setup with all features
|
|
889
|
-
node scripts/agileflow-configure.js --profile=full
|
|
1180
|
+
node .agileflow/scripts/agileflow-configure.js --profile=full
|
|
890
1181
|
|
|
891
1182
|
# Enable specific features
|
|
892
|
-
node scripts/agileflow-configure.js --enable=sessionstart,precompact,archival
|
|
1183
|
+
node .agileflow/scripts/agileflow-configure.js --enable=sessionstart,precompact,archival
|
|
893
1184
|
|
|
894
1185
|
# Disable a feature
|
|
895
|
-
node scripts/agileflow-configure.js --disable=statusline
|
|
1186
|
+
node .agileflow/scripts/agileflow-configure.js --disable=statusline
|
|
896
1187
|
|
|
897
1188
|
# Show only agileflow branding and context in statusline
|
|
898
|
-
node scripts/agileflow-configure.js --hide=model,story,epic,wip,cost,git
|
|
1189
|
+
node .agileflow/scripts/agileflow-configure.js --hide=model,story,epic,wip,cost,git
|
|
899
1190
|
|
|
900
1191
|
# Re-enable git branch in statusline
|
|
901
|
-
node scripts/agileflow-configure.js --show=git
|
|
1192
|
+
node .agileflow/scripts/agileflow-configure.js --show=git
|
|
902
1193
|
|
|
903
1194
|
# List component status
|
|
904
|
-
node scripts/agileflow-configure.js --components
|
|
1195
|
+
node .agileflow/scripts/agileflow-configure.js --components
|
|
905
1196
|
|
|
906
1197
|
# Fix format issues
|
|
907
|
-
node scripts/agileflow-configure.js --migrate
|
|
1198
|
+
node .agileflow/scripts/agileflow-configure.js --migrate
|
|
908
1199
|
|
|
909
1200
|
# Check current status
|
|
910
|
-
node scripts/agileflow-configure.js --detect
|
|
1201
|
+
node .agileflow/scripts/agileflow-configure.js --detect
|
|
1202
|
+
|
|
1203
|
+
# Upgrade outdated scripts to latest version
|
|
1204
|
+
node .agileflow/scripts/agileflow-configure.js --upgrade
|
|
1205
|
+
|
|
1206
|
+
# List all scripts with status
|
|
1207
|
+
node .agileflow/scripts/agileflow-configure.js --list-scripts
|
|
1208
|
+
|
|
1209
|
+
# Show version information
|
|
1210
|
+
node .agileflow/scripts/agileflow-configure.js --version
|
|
1211
|
+
|
|
1212
|
+
# Repair missing scripts
|
|
1213
|
+
node .agileflow/scripts/agileflow-configure.js --repair
|
|
1214
|
+
|
|
1215
|
+
# Repair scripts for a specific feature
|
|
1216
|
+
node .agileflow/scripts/agileflow-configure.js --repair=statusline
|
|
911
1217
|
`);
|
|
912
1218
|
}
|
|
913
1219
|
|
|
@@ -924,11 +1230,16 @@ function main() {
|
|
|
924
1230
|
let disable = [];
|
|
925
1231
|
let show = [];
|
|
926
1232
|
let hide = [];
|
|
927
|
-
let archivalDays =
|
|
1233
|
+
let archivalDays = 30;
|
|
928
1234
|
let migrate = false;
|
|
929
1235
|
let detect = false;
|
|
1236
|
+
let upgrade = false;
|
|
930
1237
|
let components = false;
|
|
931
1238
|
let help = false;
|
|
1239
|
+
let repair = false;
|
|
1240
|
+
let repairFeature = null;
|
|
1241
|
+
let showVersion = false;
|
|
1242
|
+
let listScriptsMode = false;
|
|
932
1243
|
|
|
933
1244
|
args.forEach(arg => {
|
|
934
1245
|
if (arg.startsWith('--profile=')) profile = arg.split('=')[1];
|
|
@@ -952,11 +1263,19 @@ function main() {
|
|
|
952
1263
|
.split('=')[1]
|
|
953
1264
|
.split(',')
|
|
954
1265
|
.map(s => s.trim().toLowerCase());
|
|
955
|
-
else if (arg.startsWith('--archival-days=')) archivalDays = parseInt(arg.split('=')[1]) ||
|
|
1266
|
+
else if (arg.startsWith('--archival-days=')) archivalDays = parseInt(arg.split('=')[1]) || 30;
|
|
956
1267
|
else if (arg === '--migrate') migrate = true;
|
|
957
1268
|
else if (arg === '--detect' || arg === '--validate') detect = true;
|
|
1269
|
+
else if (arg === '--upgrade') upgrade = true;
|
|
958
1270
|
else if (arg === '--components') components = true;
|
|
959
1271
|
else if (arg === '--help' || arg === '-h') help = true;
|
|
1272
|
+
else if (arg === '--repair') repair = true;
|
|
1273
|
+
else if (arg.startsWith('--repair=')) {
|
|
1274
|
+
repair = true;
|
|
1275
|
+
repairFeature = arg.split('=')[1].trim().toLowerCase();
|
|
1276
|
+
}
|
|
1277
|
+
else if (arg === '--version' || arg === '-v') showVersion = true;
|
|
1278
|
+
else if (arg === '--list-scripts' || arg === '--scripts') listScriptsMode = true;
|
|
960
1279
|
});
|
|
961
1280
|
|
|
962
1281
|
if (help) {
|
|
@@ -964,6 +1283,30 @@ function main() {
|
|
|
964
1283
|
return;
|
|
965
1284
|
}
|
|
966
1285
|
|
|
1286
|
+
// List scripts mode (standalone, doesn't need detection)
|
|
1287
|
+
if (listScriptsMode) {
|
|
1288
|
+
listScripts();
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// Version info mode (standalone, doesn't need detection)
|
|
1293
|
+
if (showVersion) {
|
|
1294
|
+
showVersionInfo();
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// Repair mode (standalone, doesn't need detection)
|
|
1299
|
+
if (repair) {
|
|
1300
|
+
const needsRestart = repairScripts(repairFeature);
|
|
1301
|
+
if (needsRestart) {
|
|
1302
|
+
log('\n' + '═'.repeat(55), c.red);
|
|
1303
|
+
log('🔴 RESTART CLAUDE CODE NOW!', c.red + c.bold);
|
|
1304
|
+
log(' Quit completely, wait 5 seconds, restart', c.red);
|
|
1305
|
+
log('═'.repeat(55), c.red);
|
|
1306
|
+
}
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
967
1310
|
// Components list mode
|
|
968
1311
|
if (components && show.length === 0 && hide.length === 0) {
|
|
969
1312
|
listStatuslineComponents();
|
|
@@ -979,10 +1322,16 @@ function main() {
|
|
|
979
1322
|
|
|
980
1323
|
// Always detect first
|
|
981
1324
|
const status = detectConfig();
|
|
982
|
-
const hasIssues = printStatus(status);
|
|
1325
|
+
const { hasIssues, hasOutdated } = printStatus(status);
|
|
983
1326
|
|
|
984
1327
|
// Detect only mode
|
|
985
|
-
if (detect && !migrate && !profile && enable.length === 0 && disable.length === 0) {
|
|
1328
|
+
if (detect && !migrate && !upgrade && !profile && enable.length === 0 && disable.length === 0) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// Upgrade mode
|
|
1333
|
+
if (upgrade) {
|
|
1334
|
+
upgradeFeatures(status);
|
|
986
1335
|
return;
|
|
987
1336
|
}
|
|
988
1337
|
|