agileflow 2.72.0 → 2.73.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 +119 -85
- package/src/core/agents/configuration/archival.md +10 -10
- package/src/core/commands/configure.md +68 -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
|
@@ -1,1033 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* agileflow-configure.js - Comprehensive configuration management
|
|
4
|
-
*
|
|
5
|
-
* Features:
|
|
6
|
-
* - DETECT: Show current configuration status
|
|
7
|
-
* - MIGRATE: Fix old/invalid formats automatically
|
|
8
|
-
* - ENABLE: Turn on features
|
|
9
|
-
* - DISABLE: Turn off features
|
|
10
|
-
* - PROFILES: Quick presets (full, basic, minimal, none)
|
|
11
|
-
* - RECONFIGURE: Change settings (archival days, etc.)
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* node scripts/agileflow-configure.js [options]
|
|
15
|
-
*
|
|
16
|
-
* Options:
|
|
17
|
-
* --profile=full|basic|minimal|none Apply a preset
|
|
18
|
-
* --enable=<features> Enable specific features
|
|
19
|
-
* --disable=<features> Disable specific features
|
|
20
|
-
* --archival-days=<N> Set archival threshold
|
|
21
|
-
* --migrate Fix old formats without changing features
|
|
22
|
-
* --validate Check for issues
|
|
23
|
-
* --detect Show current status
|
|
24
|
-
* --help Show help
|
|
25
|
-
*
|
|
26
|
-
* Features: sessionstart, precompact, archival, statusline, autoupdate
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
const fs = require('fs');
|
|
30
|
-
const path = require('path');
|
|
31
|
-
const { execSync } = require('child_process');
|
|
32
|
-
|
|
33
|
-
// ============================================================================
|
|
34
|
-
// CONFIGURATION
|
|
35
|
-
// ============================================================================
|
|
36
|
-
|
|
37
|
-
// Get version dynamically from metadata or package.json
|
|
38
|
-
function getVersion() {
|
|
39
|
-
// Try agileflow-metadata.json first (user's installed version)
|
|
40
|
-
try {
|
|
41
|
-
const metaPath = path.join(process.cwd(), 'docs/00-meta/agileflow-metadata.json');
|
|
42
|
-
if (fs.existsSync(metaPath)) {
|
|
43
|
-
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
44
|
-
if (meta.version) return meta.version;
|
|
45
|
-
}
|
|
46
|
-
} catch {}
|
|
47
|
-
|
|
48
|
-
// Try .agileflow/package.json
|
|
49
|
-
try {
|
|
50
|
-
const pkgPath = path.join(process.cwd(), '.agileflow/package.json');
|
|
51
|
-
if (fs.existsSync(pkgPath)) {
|
|
52
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
53
|
-
if (pkg.version) return pkg.version;
|
|
54
|
-
}
|
|
55
|
-
} catch {}
|
|
56
|
-
|
|
57
|
-
// Fallback to script's own package.json (when running from npm package)
|
|
58
|
-
try {
|
|
59
|
-
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
60
|
-
if (fs.existsSync(pkgPath)) {
|
|
61
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
62
|
-
if (pkg.version) return pkg.version;
|
|
63
|
-
}
|
|
64
|
-
} catch {}
|
|
65
|
-
|
|
66
|
-
return 'unknown';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const VERSION = getVersion();
|
|
70
|
-
|
|
71
|
-
const FEATURES = {
|
|
72
|
-
sessionstart: { hook: 'SessionStart', script: 'agileflow-welcome.js', type: 'node' },
|
|
73
|
-
precompact: { hook: 'PreCompact', script: 'precompact-context.sh', type: 'bash' },
|
|
74
|
-
// Note: Stop hook removed due to Claude Code reliability issues (see GitHub issues #6974, #11544)
|
|
75
|
-
archival: { script: 'archive-completed-stories.sh', requiresHook: 'sessionstart' },
|
|
76
|
-
statusline: { script: 'agileflow-statusline.sh' },
|
|
77
|
-
autoupdate: { metadataOnly: true }, // Stored in metadata.updates.autoUpdate
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Statusline component names
|
|
81
|
-
const STATUSLINE_COMPONENTS = [
|
|
82
|
-
'agileflow',
|
|
83
|
-
'model',
|
|
84
|
-
'story',
|
|
85
|
-
'epic',
|
|
86
|
-
'wip',
|
|
87
|
-
'context',
|
|
88
|
-
'cost',
|
|
89
|
-
'git',
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
const PROFILES = {
|
|
93
|
-
full: {
|
|
94
|
-
description: 'All features enabled',
|
|
95
|
-
enable: ['sessionstart', 'precompact', 'archival', 'statusline'],
|
|
96
|
-
archivalDays: 7,
|
|
97
|
-
},
|
|
98
|
-
basic: {
|
|
99
|
-
description: 'Essential hooks + archival (SessionStart + PreCompact + Archival)',
|
|
100
|
-
enable: ['sessionstart', 'precompact', 'archival'],
|
|
101
|
-
disable: ['statusline'],
|
|
102
|
-
archivalDays: 7,
|
|
103
|
-
},
|
|
104
|
-
minimal: {
|
|
105
|
-
description: 'SessionStart + archival only',
|
|
106
|
-
enable: ['sessionstart', 'archival'],
|
|
107
|
-
disable: ['precompact', 'statusline'],
|
|
108
|
-
archivalDays: 7,
|
|
109
|
-
},
|
|
110
|
-
none: {
|
|
111
|
-
description: 'Disable all AgileFlow features',
|
|
112
|
-
disable: ['sessionstart', 'precompact', 'archival', 'statusline'],
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// ============================================================================
|
|
117
|
-
// COLORS & LOGGING
|
|
118
|
-
// ============================================================================
|
|
119
|
-
|
|
120
|
-
const c = {
|
|
121
|
-
reset: '\x1b[0m',
|
|
122
|
-
dim: '\x1b[2m',
|
|
123
|
-
bold: '\x1b[1m',
|
|
124
|
-
green: '\x1b[32m',
|
|
125
|
-
yellow: '\x1b[33m',
|
|
126
|
-
red: '\x1b[31m',
|
|
127
|
-
cyan: '\x1b[36m',
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const log = (msg, color = '') => console.log(`${color}${msg}${c.reset}`);
|
|
131
|
-
const success = msg => log(`✅ ${msg}`, c.green);
|
|
132
|
-
const warn = msg => log(`⚠️ ${msg}`, c.yellow);
|
|
133
|
-
const error = msg => log(`❌ ${msg}`, c.red);
|
|
134
|
-
const info = msg => log(`ℹ️ ${msg}`, c.dim);
|
|
135
|
-
const header = msg => log(`\n${msg}`, c.bold + c.cyan);
|
|
136
|
-
|
|
137
|
-
// ============================================================================
|
|
138
|
-
// FILE UTILITIES
|
|
139
|
-
// ============================================================================
|
|
140
|
-
|
|
141
|
-
const ensureDir = dir => {
|
|
142
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const readJSON = filePath => {
|
|
146
|
-
try {
|
|
147
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
148
|
-
} catch {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const writeJSON = (filePath, data) => {
|
|
154
|
-
ensureDir(path.dirname(filePath));
|
|
155
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const copyTemplate = (templateName, destPath) => {
|
|
159
|
-
const sources = [
|
|
160
|
-
path.join(process.cwd(), '.agileflow', 'templates', templateName),
|
|
161
|
-
path.join(__dirname, templateName),
|
|
162
|
-
path.join(__dirname, '..', 'templates', templateName),
|
|
163
|
-
];
|
|
164
|
-
for (const src of sources) {
|
|
165
|
-
if (fs.existsSync(src)) {
|
|
166
|
-
fs.copyFileSync(src, destPath);
|
|
167
|
-
try {
|
|
168
|
-
fs.chmodSync(destPath, '755');
|
|
169
|
-
} catch {}
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return false;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// ============================================================================
|
|
177
|
-
// DETECTION & VALIDATION
|
|
178
|
-
// ============================================================================
|
|
179
|
-
|
|
180
|
-
function detectConfig() {
|
|
181
|
-
const status = {
|
|
182
|
-
git: { initialized: false, remote: null },
|
|
183
|
-
settingsExists: false,
|
|
184
|
-
settingsValid: true,
|
|
185
|
-
settingsIssues: [],
|
|
186
|
-
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: [] },
|
|
191
|
-
},
|
|
192
|
-
metadata: { exists: false, version: null },
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
// Git
|
|
196
|
-
if (fs.existsSync('.git')) {
|
|
197
|
-
status.git.initialized = true;
|
|
198
|
-
try {
|
|
199
|
-
status.git.remote = execSync('git remote get-url origin 2>/dev/null', {
|
|
200
|
-
encoding: 'utf8',
|
|
201
|
-
}).trim();
|
|
202
|
-
} catch {}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Settings file
|
|
206
|
-
if (fs.existsSync('.claude/settings.json')) {
|
|
207
|
-
status.settingsExists = true;
|
|
208
|
-
const settings = readJSON('.claude/settings.json');
|
|
209
|
-
|
|
210
|
-
if (!settings) {
|
|
211
|
-
status.settingsValid = false;
|
|
212
|
-
status.settingsIssues.push('Invalid JSON in settings.json');
|
|
213
|
-
} else {
|
|
214
|
-
// Check hooks
|
|
215
|
-
if (settings.hooks) {
|
|
216
|
-
// SessionStart
|
|
217
|
-
if (settings.hooks.SessionStart) {
|
|
218
|
-
if (
|
|
219
|
-
Array.isArray(settings.hooks.SessionStart) &&
|
|
220
|
-
settings.hooks.SessionStart.length > 0
|
|
221
|
-
) {
|
|
222
|
-
const hook = settings.hooks.SessionStart[0];
|
|
223
|
-
if (hook.matcher !== undefined && hook.hooks) {
|
|
224
|
-
status.features.sessionstart.enabled = true;
|
|
225
|
-
} else {
|
|
226
|
-
status.features.sessionstart.enabled = true;
|
|
227
|
-
status.features.sessionstart.valid = false;
|
|
228
|
-
status.features.sessionstart.issues.push('Old format - needs migration');
|
|
229
|
-
}
|
|
230
|
-
} else if (typeof settings.hooks.SessionStart === 'string') {
|
|
231
|
-
status.features.sessionstart.enabled = true;
|
|
232
|
-
status.features.sessionstart.valid = false;
|
|
233
|
-
status.features.sessionstart.issues.push('String format - needs migration');
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// PreCompact
|
|
238
|
-
if (settings.hooks.PreCompact) {
|
|
239
|
-
if (Array.isArray(settings.hooks.PreCompact) && settings.hooks.PreCompact.length > 0) {
|
|
240
|
-
const hook = settings.hooks.PreCompact[0];
|
|
241
|
-
if (hook.matcher !== undefined && hook.hooks) {
|
|
242
|
-
status.features.precompact.enabled = true;
|
|
243
|
-
} else {
|
|
244
|
-
status.features.precompact.enabled = true;
|
|
245
|
-
status.features.precompact.valid = false;
|
|
246
|
-
status.features.precompact.issues.push('Old format - needs migration');
|
|
247
|
-
}
|
|
248
|
-
} else if (typeof settings.hooks.PreCompact === 'string') {
|
|
249
|
-
status.features.precompact.enabled = true;
|
|
250
|
-
status.features.precompact.valid = false;
|
|
251
|
-
status.features.precompact.issues.push('String format - needs migration');
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Note: Stop hook removed due to reliability issues
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// StatusLine
|
|
259
|
-
if (settings.statusLine) {
|
|
260
|
-
status.features.statusline.enabled = true;
|
|
261
|
-
if (typeof settings.statusLine === 'string') {
|
|
262
|
-
status.features.statusline.valid = false;
|
|
263
|
-
status.features.statusline.issues.push('String format - needs type:command');
|
|
264
|
-
} else if (!settings.statusLine.type) {
|
|
265
|
-
status.features.statusline.valid = false;
|
|
266
|
-
status.features.statusline.issues.push('Missing type:command');
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Metadata
|
|
273
|
-
const metaPath = 'docs/00-meta/agileflow-metadata.json';
|
|
274
|
-
if (fs.existsSync(metaPath)) {
|
|
275
|
-
status.metadata.exists = true;
|
|
276
|
-
const meta = readJSON(metaPath);
|
|
277
|
-
if (meta) {
|
|
278
|
-
status.metadata.version = meta.version;
|
|
279
|
-
if (meta.archival?.enabled) {
|
|
280
|
-
status.features.archival.enabled = true;
|
|
281
|
-
status.features.archival.threshold = meta.archival.threshold_days;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return status;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function printStatus(status) {
|
|
290
|
-
header('📊 Current Configuration');
|
|
291
|
-
|
|
292
|
-
// Git
|
|
293
|
-
log(
|
|
294
|
-
`Git: ${status.git.initialized ? '✅' : '❌'} ${status.git.initialized ? 'initialized' : 'not initialized'}${status.git.remote ? ` (${status.git.remote})` : ''}`,
|
|
295
|
-
status.git.initialized ? c.green : c.dim
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
// Settings
|
|
299
|
-
if (!status.settingsExists) {
|
|
300
|
-
log('Settings: ❌ .claude/settings.json not found', c.dim);
|
|
301
|
-
} else if (!status.settingsValid) {
|
|
302
|
-
log('Settings: ❌ Invalid JSON', c.red);
|
|
303
|
-
} else {
|
|
304
|
-
log('Settings: ✅ .claude/settings.json exists', c.green);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Features
|
|
308
|
-
header('Features:');
|
|
309
|
-
|
|
310
|
-
const printFeature = (name, label) => {
|
|
311
|
-
const f = status.features[name];
|
|
312
|
-
let statusIcon = f.enabled ? '✅' : '❌';
|
|
313
|
-
let statusText = f.enabled ? 'enabled' : 'disabled';
|
|
314
|
-
let color = f.enabled ? c.green : c.dim;
|
|
315
|
-
|
|
316
|
-
if (f.enabled && !f.valid) {
|
|
317
|
-
statusIcon = '⚠️';
|
|
318
|
-
statusText = 'INVALID FORMAT';
|
|
319
|
-
color = c.yellow;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
log(` ${statusIcon} ${label}: ${statusText}`, color);
|
|
323
|
-
|
|
324
|
-
if (f.issues?.length > 0) {
|
|
325
|
-
f.issues.forEach(issue => log(` └─ ${issue}`, c.yellow));
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
printFeature('sessionstart', 'SessionStart Hook');
|
|
330
|
-
printFeature('precompact', 'PreCompact Hook');
|
|
331
|
-
|
|
332
|
-
const arch = status.features.archival;
|
|
333
|
-
log(
|
|
334
|
-
` ${arch.enabled ? '✅' : '❌'} Archival: ${arch.enabled ? `${arch.threshold} days` : 'disabled'}`,
|
|
335
|
-
arch.enabled ? c.green : c.dim
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
printFeature('statusline', 'Status Line');
|
|
339
|
-
|
|
340
|
-
// Metadata
|
|
341
|
-
if (status.metadata.exists) {
|
|
342
|
-
log(`\nMetadata: v${status.metadata.version}`, c.dim);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Issues summary
|
|
346
|
-
const hasIssues = Object.values(status.features).some(f => f.issues?.length > 0);
|
|
347
|
-
if (hasIssues) {
|
|
348
|
-
log('\n⚠️ Format issues detected! Run with --migrate to fix.', c.yellow);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return hasIssues;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// ============================================================================
|
|
355
|
-
// MIGRATION
|
|
356
|
-
// ============================================================================
|
|
357
|
-
|
|
358
|
-
function migrateSettings() {
|
|
359
|
-
header('🔧 Migrating Settings...');
|
|
360
|
-
|
|
361
|
-
if (!fs.existsSync('.claude/settings.json')) {
|
|
362
|
-
warn('No settings.json to migrate');
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const settings = readJSON('.claude/settings.json');
|
|
367
|
-
if (!settings) {
|
|
368
|
-
error('Cannot parse settings.json');
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
let migrated = false;
|
|
373
|
-
|
|
374
|
-
// Migrate hooks (Stop hook removed due to reliability issues)
|
|
375
|
-
if (settings.hooks) {
|
|
376
|
-
['SessionStart', 'PreCompact', 'UserPromptSubmit'].forEach(hookName => {
|
|
377
|
-
const hook = settings.hooks[hookName];
|
|
378
|
-
if (!hook) return;
|
|
379
|
-
|
|
380
|
-
// String format → array format
|
|
381
|
-
if (typeof hook === 'string') {
|
|
382
|
-
const isNode = hook.includes('node ') || hook.endsWith('.js');
|
|
383
|
-
settings.hooks[hookName] = [
|
|
384
|
-
{
|
|
385
|
-
matcher: '',
|
|
386
|
-
hooks: [{ type: 'command', command: isNode ? hook : `bash ${hook}` }],
|
|
387
|
-
},
|
|
388
|
-
];
|
|
389
|
-
success(`Migrated ${hookName} from string format`);
|
|
390
|
-
migrated = true;
|
|
391
|
-
}
|
|
392
|
-
// Old object format → new format
|
|
393
|
-
else if (Array.isArray(hook) && hook.length > 0) {
|
|
394
|
-
const first = hook[0];
|
|
395
|
-
if (first.enabled !== undefined || first.command !== undefined) {
|
|
396
|
-
// Old format with enabled/command
|
|
397
|
-
if (first.command) {
|
|
398
|
-
settings.hooks[hookName] = [
|
|
399
|
-
{
|
|
400
|
-
matcher: '',
|
|
401
|
-
hooks: [{ type: 'command', command: first.command }],
|
|
402
|
-
},
|
|
403
|
-
];
|
|
404
|
-
success(`Migrated ${hookName} from old object format`);
|
|
405
|
-
migrated = true;
|
|
406
|
-
}
|
|
407
|
-
} else if (first.matcher === undefined) {
|
|
408
|
-
// Missing matcher
|
|
409
|
-
settings.hooks[hookName] = [
|
|
410
|
-
{
|
|
411
|
-
matcher: '',
|
|
412
|
-
hooks: first.hooks || [{ type: 'command', command: 'echo "hook"' }],
|
|
413
|
-
},
|
|
414
|
-
];
|
|
415
|
-
success(`Migrated ${hookName} - added matcher`);
|
|
416
|
-
migrated = true;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Migrate statusLine
|
|
423
|
-
if (settings.statusLine) {
|
|
424
|
-
if (typeof settings.statusLine === 'string') {
|
|
425
|
-
settings.statusLine = {
|
|
426
|
-
type: 'command',
|
|
427
|
-
command: settings.statusLine,
|
|
428
|
-
padding: 0,
|
|
429
|
-
};
|
|
430
|
-
success('Migrated statusLine from string format');
|
|
431
|
-
migrated = true;
|
|
432
|
-
} else if (!settings.statusLine.type) {
|
|
433
|
-
settings.statusLine.type = 'command';
|
|
434
|
-
if (settings.statusLine.refreshInterval) {
|
|
435
|
-
delete settings.statusLine.refreshInterval;
|
|
436
|
-
settings.statusLine.padding = 0;
|
|
437
|
-
}
|
|
438
|
-
success('Migrated statusLine - added type:command');
|
|
439
|
-
migrated = true;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (migrated) {
|
|
444
|
-
// Backup original
|
|
445
|
-
fs.copyFileSync('.claude/settings.json', '.claude/settings.json.backup');
|
|
446
|
-
info('Backed up to .claude/settings.json.backup');
|
|
447
|
-
|
|
448
|
-
writeJSON('.claude/settings.json', settings);
|
|
449
|
-
success('Settings migrated successfully!');
|
|
450
|
-
} else {
|
|
451
|
-
info('No migration needed - formats are correct');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
return migrated;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// ============================================================================
|
|
458
|
-
// ENABLE/DISABLE FEATURES
|
|
459
|
-
// ============================================================================
|
|
460
|
-
|
|
461
|
-
function enableFeature(feature, options = {}) {
|
|
462
|
-
const config = FEATURES[feature];
|
|
463
|
-
if (!config) {
|
|
464
|
-
error(`Unknown feature: ${feature}`);
|
|
465
|
-
return false;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
ensureDir('.claude');
|
|
469
|
-
ensureDir('scripts');
|
|
470
|
-
|
|
471
|
-
const settings = readJSON('.claude/settings.json') || {};
|
|
472
|
-
settings.hooks = settings.hooks || {};
|
|
473
|
-
settings.permissions = settings.permissions || { allow: [], deny: [], ask: [] };
|
|
474
|
-
|
|
475
|
-
// Handle hook-based features
|
|
476
|
-
if (config.hook) {
|
|
477
|
-
const scriptPath = `scripts/${config.script}`;
|
|
478
|
-
|
|
479
|
-
// Deploy script
|
|
480
|
-
if (!copyTemplate(config.script, scriptPath)) {
|
|
481
|
-
// Create minimal version
|
|
482
|
-
if (feature === 'sessionstart') {
|
|
483
|
-
fs.writeFileSync(
|
|
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}`);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Configure hook
|
|
499
|
-
const command = config.type === 'node' ? `node ${scriptPath}` : `bash ${scriptPath}`;
|
|
500
|
-
|
|
501
|
-
settings.hooks[config.hook] = [
|
|
502
|
-
{
|
|
503
|
-
matcher: '',
|
|
504
|
-
hooks: [{ type: 'command', command }],
|
|
505
|
-
},
|
|
506
|
-
];
|
|
507
|
-
success(`${config.hook} hook enabled`);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Handle archival
|
|
511
|
-
if (feature === 'archival') {
|
|
512
|
-
const days = options.archivalDays || 7;
|
|
513
|
-
const scriptPath = 'scripts/archive-completed-stories.sh';
|
|
514
|
-
|
|
515
|
-
if (!copyTemplate('archive-completed-stories.sh', scriptPath)) {
|
|
516
|
-
warn('Archival script template not found');
|
|
517
|
-
} else {
|
|
518
|
-
success('Deployed archive-completed-stories.sh');
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Add to SessionStart hook
|
|
522
|
-
if (settings.hooks.SessionStart?.[0]?.hooks) {
|
|
523
|
-
const hasArchival = settings.hooks.SessionStart[0].hooks.some(h =>
|
|
524
|
-
h.command?.includes('archive-completed-stories')
|
|
525
|
-
);
|
|
526
|
-
if (!hasArchival) {
|
|
527
|
-
settings.hooks.SessionStart[0].hooks.push({
|
|
528
|
-
type: 'command',
|
|
529
|
-
command: 'bash scripts/archive-completed-stories.sh --quiet',
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Update metadata
|
|
535
|
-
updateMetadata({ archival: { enabled: true, threshold_days: days } });
|
|
536
|
-
success(`Archival enabled (${days} days)`);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Handle statusLine
|
|
540
|
-
if (feature === 'statusline') {
|
|
541
|
-
const scriptPath = 'scripts/agileflow-statusline.sh';
|
|
542
|
-
|
|
543
|
-
if (!copyTemplate('agileflow-statusline.sh', scriptPath)) {
|
|
544
|
-
fs.writeFileSync(
|
|
545
|
-
scriptPath,
|
|
546
|
-
`#!/bin/bash
|
|
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');
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
settings.statusLine = {
|
|
561
|
-
type: 'command',
|
|
562
|
-
command: 'bash scripts/agileflow-statusline.sh',
|
|
563
|
-
padding: 0,
|
|
564
|
-
};
|
|
565
|
-
success('Status line enabled');
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// Handle autoupdate (metadata only, no hooks needed)
|
|
569
|
-
if (feature === 'autoupdate') {
|
|
570
|
-
const frequency = options.checkFrequency || 'daily';
|
|
571
|
-
updateMetadata({
|
|
572
|
-
updates: {
|
|
573
|
-
autoUpdate: true,
|
|
574
|
-
checkFrequency: frequency,
|
|
575
|
-
showChangelog: true,
|
|
576
|
-
},
|
|
577
|
-
});
|
|
578
|
-
success(`Auto-update enabled (check frequency: ${frequency})`);
|
|
579
|
-
info('AgileFlow will automatically update on session start');
|
|
580
|
-
return true; // Skip settings.json write for this feature
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
writeJSON('.claude/settings.json', settings);
|
|
584
|
-
updateMetadata({
|
|
585
|
-
features: { [feature]: { enabled: true, version: VERSION, at: new Date().toISOString() } },
|
|
586
|
-
});
|
|
587
|
-
updateGitignore();
|
|
588
|
-
|
|
589
|
-
return true;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function disableFeature(feature) {
|
|
593
|
-
const config = FEATURES[feature];
|
|
594
|
-
if (!config) {
|
|
595
|
-
error(`Unknown feature: ${feature}`);
|
|
596
|
-
return false;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
if (!fs.existsSync('.claude/settings.json')) {
|
|
600
|
-
info(`${feature} already disabled (no settings file)`);
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
const settings = readJSON('.claude/settings.json');
|
|
605
|
-
if (!settings) return false;
|
|
606
|
-
|
|
607
|
-
// Disable hook
|
|
608
|
-
if (config.hook && settings.hooks?.[config.hook]) {
|
|
609
|
-
delete settings.hooks[config.hook];
|
|
610
|
-
success(`${config.hook} hook disabled`);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Disable archival
|
|
614
|
-
if (feature === 'archival') {
|
|
615
|
-
// Remove from SessionStart
|
|
616
|
-
if (settings.hooks?.SessionStart?.[0]?.hooks) {
|
|
617
|
-
settings.hooks.SessionStart[0].hooks = settings.hooks.SessionStart[0].hooks.filter(
|
|
618
|
-
h => !h.command?.includes('archive-completed-stories')
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
updateMetadata({ archival: { enabled: false } });
|
|
622
|
-
success('Archival disabled');
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// Disable statusLine
|
|
626
|
-
if (feature === 'statusline' && settings.statusLine) {
|
|
627
|
-
delete settings.statusLine;
|
|
628
|
-
success('Status line disabled');
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Disable autoupdate
|
|
632
|
-
if (feature === 'autoupdate') {
|
|
633
|
-
updateMetadata({
|
|
634
|
-
updates: {
|
|
635
|
-
autoUpdate: false,
|
|
636
|
-
},
|
|
637
|
-
});
|
|
638
|
-
success('Auto-update disabled');
|
|
639
|
-
return true; // Skip settings.json write for this feature
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
writeJSON('.claude/settings.json', settings);
|
|
643
|
-
updateMetadata({
|
|
644
|
-
features: { [feature]: { enabled: false, version: VERSION, at: new Date().toISOString() } },
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
return true;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// ============================================================================
|
|
651
|
-
// METADATA
|
|
652
|
-
// ============================================================================
|
|
653
|
-
|
|
654
|
-
function updateMetadata(updates) {
|
|
655
|
-
const metaPath = 'docs/00-meta/agileflow-metadata.json';
|
|
656
|
-
|
|
657
|
-
if (!fs.existsSync(metaPath)) {
|
|
658
|
-
ensureDir('docs/00-meta');
|
|
659
|
-
writeJSON(metaPath, { version: VERSION, created: new Date().toISOString() });
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const meta = readJSON(metaPath) || {};
|
|
663
|
-
|
|
664
|
-
// Deep merge
|
|
665
|
-
if (updates.archival) {
|
|
666
|
-
meta.archival = { ...meta.archival, ...updates.archival };
|
|
667
|
-
}
|
|
668
|
-
if (updates.features) {
|
|
669
|
-
meta.features = meta.features || {};
|
|
670
|
-
Object.entries(updates.features).forEach(([key, value]) => {
|
|
671
|
-
meta.features[key] = { ...meta.features[key], ...value };
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
if (updates.updates) {
|
|
675
|
-
meta.updates = { ...meta.updates, ...updates.updates };
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
meta.version = VERSION;
|
|
679
|
-
meta.updated = new Date().toISOString();
|
|
680
|
-
|
|
681
|
-
writeJSON(metaPath, meta);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
function updateGitignore() {
|
|
685
|
-
const entries = [
|
|
686
|
-
'.claude/settings.local.json',
|
|
687
|
-
'.claude/activity.log',
|
|
688
|
-
'.claude/context.log',
|
|
689
|
-
'.claude/hook.log',
|
|
690
|
-
'.claude/prompt-log.txt',
|
|
691
|
-
'.claude/session.log',
|
|
692
|
-
];
|
|
693
|
-
|
|
694
|
-
let content = fs.existsSync('.gitignore') ? fs.readFileSync('.gitignore', 'utf8') : '';
|
|
695
|
-
let added = false;
|
|
696
|
-
|
|
697
|
-
entries.forEach(entry => {
|
|
698
|
-
if (!content.includes(entry)) {
|
|
699
|
-
content += `\n${entry}`;
|
|
700
|
-
added = true;
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
if (added) {
|
|
705
|
-
fs.writeFileSync('.gitignore', content.trimEnd() + '\n');
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// ============================================================================
|
|
710
|
-
// STATUSLINE COMPONENTS
|
|
711
|
-
// ============================================================================
|
|
712
|
-
|
|
713
|
-
function setStatuslineComponents(enableComponents = [], disableComponents = []) {
|
|
714
|
-
const metaPath = 'docs/00-meta/agileflow-metadata.json';
|
|
715
|
-
|
|
716
|
-
if (!fs.existsSync(metaPath)) {
|
|
717
|
-
warn('No metadata file found - run with --enable=statusline first');
|
|
718
|
-
return false;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
const meta = readJSON(metaPath);
|
|
722
|
-
if (!meta) {
|
|
723
|
-
error('Cannot parse metadata file');
|
|
724
|
-
return false;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Ensure statusline.components structure exists
|
|
728
|
-
meta.features = meta.features || {};
|
|
729
|
-
meta.features.statusline = meta.features.statusline || {};
|
|
730
|
-
meta.features.statusline.components = meta.features.statusline.components || {};
|
|
731
|
-
|
|
732
|
-
// Set defaults for any missing components
|
|
733
|
-
STATUSLINE_COMPONENTS.forEach(comp => {
|
|
734
|
-
if (meta.features.statusline.components[comp] === undefined) {
|
|
735
|
-
meta.features.statusline.components[comp] = true;
|
|
736
|
-
}
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
// Enable specified components
|
|
740
|
-
enableComponents.forEach(comp => {
|
|
741
|
-
if (STATUSLINE_COMPONENTS.includes(comp)) {
|
|
742
|
-
meta.features.statusline.components[comp] = true;
|
|
743
|
-
success(`Statusline component enabled: ${comp}`);
|
|
744
|
-
} else {
|
|
745
|
-
warn(`Unknown component: ${comp} (available: ${STATUSLINE_COMPONENTS.join(', ')})`);
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
// Disable specified components
|
|
750
|
-
disableComponents.forEach(comp => {
|
|
751
|
-
if (STATUSLINE_COMPONENTS.includes(comp)) {
|
|
752
|
-
meta.features.statusline.components[comp] = false;
|
|
753
|
-
success(`Statusline component disabled: ${comp}`);
|
|
754
|
-
} else {
|
|
755
|
-
warn(`Unknown component: ${comp} (available: ${STATUSLINE_COMPONENTS.join(', ')})`);
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
meta.updated = new Date().toISOString();
|
|
760
|
-
writeJSON(metaPath, meta);
|
|
761
|
-
|
|
762
|
-
return true;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function listStatuslineComponents() {
|
|
766
|
-
const metaPath = 'docs/00-meta/agileflow-metadata.json';
|
|
767
|
-
|
|
768
|
-
header('📊 Statusline Components');
|
|
769
|
-
|
|
770
|
-
if (!fs.existsSync(metaPath)) {
|
|
771
|
-
log(' No configuration found (defaults: all enabled)', c.dim);
|
|
772
|
-
STATUSLINE_COMPONENTS.forEach(comp => {
|
|
773
|
-
log(` ✅ ${comp}: enabled (default)`, c.green);
|
|
774
|
-
});
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
const meta = readJSON(metaPath);
|
|
779
|
-
const components = meta?.features?.statusline?.components || {};
|
|
780
|
-
|
|
781
|
-
STATUSLINE_COMPONENTS.forEach(comp => {
|
|
782
|
-
const enabled = components[comp] !== false; // default true
|
|
783
|
-
const icon = enabled ? '✅' : '❌';
|
|
784
|
-
const color = enabled ? c.green : c.dim;
|
|
785
|
-
log(` ${icon} ${comp}: ${enabled ? 'enabled' : 'disabled'}`, color);
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
log('\nTo toggle: --show=<component> or --hide=<component>', c.dim);
|
|
789
|
-
log(`Components: ${STATUSLINE_COMPONENTS.join(', ')}`, c.dim);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// ============================================================================
|
|
793
|
-
// PROFILES
|
|
794
|
-
// ============================================================================
|
|
795
|
-
|
|
796
|
-
function applyProfile(profileName, options = {}) {
|
|
797
|
-
const profile = PROFILES[profileName];
|
|
798
|
-
if (!profile) {
|
|
799
|
-
error(`Unknown profile: ${profileName}`);
|
|
800
|
-
log('Available: ' + Object.keys(PROFILES).join(', '));
|
|
801
|
-
return false;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
header(`🚀 Applying "${profileName}" profile`);
|
|
805
|
-
log(profile.description, c.dim);
|
|
806
|
-
|
|
807
|
-
// Enable features
|
|
808
|
-
if (profile.enable) {
|
|
809
|
-
profile.enable.forEach(f =>
|
|
810
|
-
enableFeature(f, { archivalDays: profile.archivalDays || options.archivalDays })
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Disable features
|
|
815
|
-
if (profile.disable) {
|
|
816
|
-
profile.disable.forEach(f => disableFeature(f));
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
return true;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// ============================================================================
|
|
823
|
-
// SUMMARY
|
|
824
|
-
// ============================================================================
|
|
825
|
-
|
|
826
|
-
function printSummary(actions) {
|
|
827
|
-
header('✅ Configuration Complete!');
|
|
828
|
-
|
|
829
|
-
if (actions.enabled?.length > 0) {
|
|
830
|
-
log('\nEnabled:', c.green);
|
|
831
|
-
actions.enabled.forEach(f => log(` ✅ ${f}`, c.green));
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
if (actions.disabled?.length > 0) {
|
|
835
|
-
log('\nDisabled:', c.dim);
|
|
836
|
-
actions.disabled.forEach(f => log(` ❌ ${f}`, c.dim));
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (actions.migrated) {
|
|
840
|
-
log('\nMigrated: Fixed format issues', c.yellow);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
log('\n' + '═'.repeat(55), c.red);
|
|
844
|
-
log('🔴 RESTART CLAUDE CODE NOW!', c.red + c.bold);
|
|
845
|
-
log(' Quit completely, wait 5 seconds, restart', c.red);
|
|
846
|
-
log('═'.repeat(55), c.red);
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// ============================================================================
|
|
850
|
-
// HELP
|
|
851
|
-
// ============================================================================
|
|
852
|
-
|
|
853
|
-
function printHelp() {
|
|
854
|
-
log(`
|
|
855
|
-
${c.bold}AgileFlow Configure${c.reset} - Manage AgileFlow features
|
|
856
|
-
|
|
857
|
-
${c.cyan}Usage:${c.reset}
|
|
858
|
-
node scripts/agileflow-configure.js [options]
|
|
859
|
-
|
|
860
|
-
${c.cyan}Profiles:${c.reset}
|
|
861
|
-
--profile=full All features (hooks, archival, statusline)
|
|
862
|
-
--profile=basic SessionStart + PreCompact + archival
|
|
863
|
-
--profile=minimal SessionStart + archival only
|
|
864
|
-
--profile=none Disable all AgileFlow features
|
|
865
|
-
|
|
866
|
-
${c.cyan}Feature Control:${c.reset}
|
|
867
|
-
--enable=<list> Enable features (comma-separated)
|
|
868
|
-
--disable=<list> Disable features (comma-separated)
|
|
869
|
-
|
|
870
|
-
Features: sessionstart, precompact, archival, statusline
|
|
871
|
-
|
|
872
|
-
${c.cyan}Statusline Components:${c.reset}
|
|
873
|
-
--show=<list> Show statusline components (comma-separated)
|
|
874
|
-
--hide=<list> Hide statusline components (comma-separated)
|
|
875
|
-
--components List statusline component status
|
|
876
|
-
|
|
877
|
-
Components: agileflow, model, story, epic, wip, context, cost, git
|
|
878
|
-
|
|
879
|
-
${c.cyan}Settings:${c.reset}
|
|
880
|
-
--archival-days=N Set archival threshold (default: 7)
|
|
881
|
-
|
|
882
|
-
${c.cyan}Maintenance:${c.reset}
|
|
883
|
-
--migrate Fix old/invalid formats
|
|
884
|
-
--validate Check for issues (same as --detect)
|
|
885
|
-
--detect Show current configuration
|
|
886
|
-
|
|
887
|
-
${c.cyan}Examples:${c.reset}
|
|
888
|
-
# Quick setup with all features
|
|
889
|
-
node scripts/agileflow-configure.js --profile=full
|
|
890
|
-
|
|
891
|
-
# Enable specific features
|
|
892
|
-
node scripts/agileflow-configure.js --enable=sessionstart,precompact,archival
|
|
893
|
-
|
|
894
|
-
# Disable a feature
|
|
895
|
-
node scripts/agileflow-configure.js --disable=statusline
|
|
896
|
-
|
|
897
|
-
# Show only agileflow branding and context in statusline
|
|
898
|
-
node scripts/agileflow-configure.js --hide=model,story,epic,wip,cost,git
|
|
899
|
-
|
|
900
|
-
# Re-enable git branch in statusline
|
|
901
|
-
node scripts/agileflow-configure.js --show=git
|
|
902
|
-
|
|
903
|
-
# List component status
|
|
904
|
-
node scripts/agileflow-configure.js --components
|
|
905
|
-
|
|
906
|
-
# Fix format issues
|
|
907
|
-
node scripts/agileflow-configure.js --migrate
|
|
908
|
-
|
|
909
|
-
# Check current status
|
|
910
|
-
node scripts/agileflow-configure.js --detect
|
|
911
|
-
`);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
// ============================================================================
|
|
915
|
-
// MAIN
|
|
916
|
-
// ============================================================================
|
|
917
|
-
|
|
918
|
-
function main() {
|
|
919
|
-
const args = process.argv.slice(2);
|
|
920
|
-
|
|
921
|
-
// Parse arguments
|
|
922
|
-
let profile = null;
|
|
923
|
-
let enable = [];
|
|
924
|
-
let disable = [];
|
|
925
|
-
let show = [];
|
|
926
|
-
let hide = [];
|
|
927
|
-
let archivalDays = 7;
|
|
928
|
-
let migrate = false;
|
|
929
|
-
let detect = false;
|
|
930
|
-
let components = false;
|
|
931
|
-
let help = false;
|
|
932
|
-
|
|
933
|
-
args.forEach(arg => {
|
|
934
|
-
if (arg.startsWith('--profile=')) profile = arg.split('=')[1];
|
|
935
|
-
else if (arg.startsWith('--enable='))
|
|
936
|
-
enable = arg
|
|
937
|
-
.split('=')[1]
|
|
938
|
-
.split(',')
|
|
939
|
-
.map(s => s.trim().toLowerCase());
|
|
940
|
-
else if (arg.startsWith('--disable='))
|
|
941
|
-
disable = arg
|
|
942
|
-
.split('=')[1]
|
|
943
|
-
.split(',')
|
|
944
|
-
.map(s => s.trim().toLowerCase());
|
|
945
|
-
else if (arg.startsWith('--show='))
|
|
946
|
-
show = arg
|
|
947
|
-
.split('=')[1]
|
|
948
|
-
.split(',')
|
|
949
|
-
.map(s => s.trim().toLowerCase());
|
|
950
|
-
else if (arg.startsWith('--hide='))
|
|
951
|
-
hide = arg
|
|
952
|
-
.split('=')[1]
|
|
953
|
-
.split(',')
|
|
954
|
-
.map(s => s.trim().toLowerCase());
|
|
955
|
-
else if (arg.startsWith('--archival-days=')) archivalDays = parseInt(arg.split('=')[1]) || 7;
|
|
956
|
-
else if (arg === '--migrate') migrate = true;
|
|
957
|
-
else if (arg === '--detect' || arg === '--validate') detect = true;
|
|
958
|
-
else if (arg === '--components') components = true;
|
|
959
|
-
else if (arg === '--help' || arg === '-h') help = true;
|
|
960
|
-
});
|
|
961
|
-
|
|
962
|
-
if (help) {
|
|
963
|
-
printHelp();
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// Components list mode
|
|
968
|
-
if (components && show.length === 0 && hide.length === 0) {
|
|
969
|
-
listStatuslineComponents();
|
|
970
|
-
return;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// Component toggle mode
|
|
974
|
-
if (show.length > 0 || hide.length > 0) {
|
|
975
|
-
setStatuslineComponents(show, hide);
|
|
976
|
-
listStatuslineComponents();
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// Always detect first
|
|
981
|
-
const status = detectConfig();
|
|
982
|
-
const hasIssues = printStatus(status);
|
|
983
|
-
|
|
984
|
-
// Detect only mode
|
|
985
|
-
if (detect && !migrate && !profile && enable.length === 0 && disable.length === 0) {
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// Migrate mode
|
|
990
|
-
if (migrate) {
|
|
991
|
-
migrateSettings();
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
// Auto-migrate if issues detected
|
|
996
|
-
if (hasIssues && (profile || enable.length > 0)) {
|
|
997
|
-
log('\n⚠️ Auto-migrating invalid formats...', c.yellow);
|
|
998
|
-
migrateSettings();
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
const actions = { enabled: [], disabled: [], migrated: hasIssues };
|
|
1002
|
-
|
|
1003
|
-
// Apply profile
|
|
1004
|
-
if (profile) {
|
|
1005
|
-
applyProfile(profile, { archivalDays });
|
|
1006
|
-
const p = PROFILES[profile];
|
|
1007
|
-
actions.enabled = p.enable || [];
|
|
1008
|
-
actions.disabled = p.disable || [];
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// Enable specific features
|
|
1012
|
-
enable.forEach(f => {
|
|
1013
|
-
if (enableFeature(f, { archivalDays })) {
|
|
1014
|
-
actions.enabled.push(f);
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
|
-
|
|
1018
|
-
// Disable specific features
|
|
1019
|
-
disable.forEach(f => {
|
|
1020
|
-
if (disableFeature(f)) {
|
|
1021
|
-
actions.disabled.push(f);
|
|
1022
|
-
}
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
// Print summary if anything changed
|
|
1026
|
-
if (actions.enabled.length > 0 || actions.disabled.length > 0) {
|
|
1027
|
-
printSummary(actions);
|
|
1028
|
-
} else if (!detect) {
|
|
1029
|
-
log('\nNo changes made. Use --help for usage.', c.dim);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
main();
|