agileflow 2.89.1 → 2.89.3
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 +10 -0
- package/lib/content-sanitizer.js +463 -0
- package/lib/error-codes.js +544 -0
- package/lib/errors.js +336 -5
- package/lib/feedback.js +561 -0
- package/lib/path-resolver.js +396 -0
- package/lib/session-registry.js +461 -0
- package/lib/smart-json-file.js +449 -0
- package/lib/validate.js +165 -11
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +40 -1440
- package/scripts/agileflow-welcome.js +2 -1
- package/scripts/lib/configure-detect.js +383 -0
- package/scripts/lib/configure-features.js +811 -0
- package/scripts/lib/configure-repair.js +314 -0
- package/scripts/lib/configure-utils.js +115 -0
- package/scripts/lib/frontmatter-parser.js +3 -3
- package/scripts/obtain-context.js +417 -113
- package/scripts/ralph-loop.js +1 -1
- package/tools/cli/commands/config.js +3 -3
- package/tools/cli/commands/doctor.js +30 -2
- package/tools/cli/commands/list.js +2 -2
- package/tools/cli/commands/uninstall.js +3 -3
- package/tools/cli/installers/core/installer.js +62 -12
- package/tools/cli/installers/ide/_interface.js +238 -0
- package/tools/cli/installers/ide/codex.js +2 -2
- package/tools/cli/installers/ide/manager.js +15 -0
- package/tools/cli/lib/content-injector.js +69 -16
- package/tools/cli/lib/ide-errors.js +163 -29
|
@@ -298,7 +298,8 @@ function clearActiveCommands(rootDir, cache = null) {
|
|
|
298
298
|
const now = Date.now();
|
|
299
299
|
const secondsSincePrecompact = (now - precompactTime) / 1000;
|
|
300
300
|
|
|
301
|
-
if (secondsSincePrecompact <
|
|
301
|
+
if (secondsSincePrecompact < 600) {
|
|
302
|
+
// 10 minutes - compacts can take a while with background tasks
|
|
302
303
|
// This is a post-compact session start - preserve active commands
|
|
303
304
|
result.preserved = true;
|
|
304
305
|
// Capture command names for display (but don't clear)
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* configure-detect.js - Detection and validation for agileflow-configure
|
|
3
|
+
*
|
|
4
|
+
* Extracted from agileflow-configure.js (US-0094)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
const { c, log, header, readJSON } = require('./configure-utils');
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// DETECTION
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Detect current AgileFlow configuration status
|
|
18
|
+
* @param {string} version - Current VERSION string
|
|
19
|
+
* @returns {object} Configuration status object
|
|
20
|
+
*/
|
|
21
|
+
function detectConfig(version) {
|
|
22
|
+
const status = {
|
|
23
|
+
git: { initialized: false, remote: null },
|
|
24
|
+
settingsExists: false,
|
|
25
|
+
settingsValid: true,
|
|
26
|
+
settingsIssues: [],
|
|
27
|
+
features: {
|
|
28
|
+
sessionstart: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
29
|
+
precompact: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
30
|
+
ralphloop: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
31
|
+
selfimprove: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
32
|
+
archival: { enabled: false, threshold: null, version: null, outdated: false },
|
|
33
|
+
statusline: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
34
|
+
damagecontrol: {
|
|
35
|
+
enabled: false,
|
|
36
|
+
valid: true,
|
|
37
|
+
issues: [],
|
|
38
|
+
version: null,
|
|
39
|
+
outdated: false,
|
|
40
|
+
level: null,
|
|
41
|
+
patternCount: 0,
|
|
42
|
+
},
|
|
43
|
+
askuserquestion: {
|
|
44
|
+
enabled: false,
|
|
45
|
+
valid: true,
|
|
46
|
+
issues: [],
|
|
47
|
+
version: null,
|
|
48
|
+
outdated: false,
|
|
49
|
+
mode: null,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
metadata: { exists: false, version: null },
|
|
53
|
+
currentVersion: version,
|
|
54
|
+
hasOutdated: false,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Git detection
|
|
58
|
+
if (fs.existsSync('.git')) {
|
|
59
|
+
status.git.initialized = true;
|
|
60
|
+
try {
|
|
61
|
+
status.git.remote = execSync('git remote get-url origin 2>/dev/null', {
|
|
62
|
+
encoding: 'utf8',
|
|
63
|
+
}).trim();
|
|
64
|
+
} catch {}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Settings file detection
|
|
68
|
+
if (fs.existsSync('.claude/settings.json')) {
|
|
69
|
+
status.settingsExists = true;
|
|
70
|
+
const settings = readJSON('.claude/settings.json');
|
|
71
|
+
|
|
72
|
+
if (!settings) {
|
|
73
|
+
status.settingsValid = false;
|
|
74
|
+
status.settingsIssues.push('Invalid JSON in settings.json');
|
|
75
|
+
} else {
|
|
76
|
+
detectHooks(settings, status);
|
|
77
|
+
detectStatusLine(settings, status);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Metadata detection
|
|
82
|
+
detectMetadata(status, version);
|
|
83
|
+
|
|
84
|
+
return status;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Detect hook configurations in settings
|
|
89
|
+
*/
|
|
90
|
+
function detectHooks(settings, status) {
|
|
91
|
+
if (!settings.hooks) return;
|
|
92
|
+
|
|
93
|
+
// SessionStart detection
|
|
94
|
+
if (settings.hooks.SessionStart) {
|
|
95
|
+
detectSessionStartHook(settings.hooks.SessionStart, status);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// PreCompact detection
|
|
99
|
+
if (settings.hooks.PreCompact) {
|
|
100
|
+
detectPreCompactHook(settings.hooks.PreCompact, status);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Stop hooks detection (ralphloop and selfimprove)
|
|
104
|
+
if (settings.hooks.Stop) {
|
|
105
|
+
detectStopHooks(settings.hooks.Stop, status);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// PreToolUse hooks detection (damage control)
|
|
109
|
+
if (settings.hooks.PreToolUse) {
|
|
110
|
+
detectPreToolUseHooks(settings.hooks.PreToolUse, status);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Detect SessionStart hook configuration
|
|
116
|
+
*/
|
|
117
|
+
function detectSessionStartHook(hook, status) {
|
|
118
|
+
if (Array.isArray(hook) && hook.length > 0) {
|
|
119
|
+
const first = hook[0];
|
|
120
|
+
if (first.matcher !== undefined && first.hooks) {
|
|
121
|
+
status.features.sessionstart.enabled = true;
|
|
122
|
+
} else {
|
|
123
|
+
status.features.sessionstart.enabled = true;
|
|
124
|
+
status.features.sessionstart.valid = false;
|
|
125
|
+
status.features.sessionstart.issues.push('Old format - needs migration');
|
|
126
|
+
}
|
|
127
|
+
} else if (typeof hook === 'string') {
|
|
128
|
+
status.features.sessionstart.enabled = true;
|
|
129
|
+
status.features.sessionstart.valid = false;
|
|
130
|
+
status.features.sessionstart.issues.push('String format - needs migration');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Detect PreCompact hook configuration
|
|
136
|
+
*/
|
|
137
|
+
function detectPreCompactHook(hook, status) {
|
|
138
|
+
if (Array.isArray(hook) && hook.length > 0) {
|
|
139
|
+
const first = hook[0];
|
|
140
|
+
if (first.matcher !== undefined && first.hooks) {
|
|
141
|
+
status.features.precompact.enabled = true;
|
|
142
|
+
} else {
|
|
143
|
+
status.features.precompact.enabled = true;
|
|
144
|
+
status.features.precompact.valid = false;
|
|
145
|
+
status.features.precompact.issues.push('Old format - needs migration');
|
|
146
|
+
}
|
|
147
|
+
} else if (typeof hook === 'string') {
|
|
148
|
+
status.features.precompact.enabled = true;
|
|
149
|
+
status.features.precompact.valid = false;
|
|
150
|
+
status.features.precompact.issues.push('String format - needs migration');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Detect Stop hook configuration (ralphloop, selfimprove)
|
|
156
|
+
*/
|
|
157
|
+
function detectStopHooks(hook, status) {
|
|
158
|
+
if (Array.isArray(hook) && hook.length > 0) {
|
|
159
|
+
const first = hook[0];
|
|
160
|
+
if (first.matcher !== undefined && first.hooks) {
|
|
161
|
+
for (const h of first.hooks) {
|
|
162
|
+
if (h.command?.includes('ralph-loop')) {
|
|
163
|
+
status.features.ralphloop.enabled = true;
|
|
164
|
+
}
|
|
165
|
+
if (h.command?.includes('auto-self-improve')) {
|
|
166
|
+
status.features.selfimprove.enabled = true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Detect PreToolUse hooks (damage control)
|
|
175
|
+
*/
|
|
176
|
+
function detectPreToolUseHooks(hooks, status) {
|
|
177
|
+
if (!Array.isArray(hooks) || hooks.length === 0) return;
|
|
178
|
+
|
|
179
|
+
const hasBashHook = hooks.some(
|
|
180
|
+
h => h.matcher === 'Bash' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
|
|
181
|
+
);
|
|
182
|
+
const hasEditHook = hooks.some(
|
|
183
|
+
h => h.matcher === 'Edit' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
|
|
184
|
+
);
|
|
185
|
+
const hasWriteHook = hooks.some(
|
|
186
|
+
h => h.matcher === 'Write' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (hasBashHook || hasEditHook || hasWriteHook) {
|
|
190
|
+
status.features.damagecontrol.enabled = true;
|
|
191
|
+
const hookCount = [hasBashHook, hasEditHook, hasWriteHook].filter(Boolean).length;
|
|
192
|
+
if (hookCount < 3) {
|
|
193
|
+
status.features.damagecontrol.valid = false;
|
|
194
|
+
status.features.damagecontrol.issues.push(`Only ${hookCount}/3 hooks configured`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Detect statusLine configuration
|
|
201
|
+
*/
|
|
202
|
+
function detectStatusLine(settings, status) {
|
|
203
|
+
if (!settings.statusLine) return;
|
|
204
|
+
|
|
205
|
+
status.features.statusline.enabled = true;
|
|
206
|
+
if (typeof settings.statusLine === 'string') {
|
|
207
|
+
status.features.statusline.valid = false;
|
|
208
|
+
status.features.statusline.issues.push('String format - needs type:command');
|
|
209
|
+
} else if (!settings.statusLine.type) {
|
|
210
|
+
status.features.statusline.valid = false;
|
|
211
|
+
status.features.statusline.issues.push('Missing type:command');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Detect metadata file configuration
|
|
217
|
+
*/
|
|
218
|
+
function detectMetadata(status, version) {
|
|
219
|
+
const metaPath = 'docs/00-meta/agileflow-metadata.json';
|
|
220
|
+
if (!fs.existsSync(metaPath)) return;
|
|
221
|
+
|
|
222
|
+
status.metadata.exists = true;
|
|
223
|
+
const meta = readJSON(metaPath);
|
|
224
|
+
if (!meta) return;
|
|
225
|
+
|
|
226
|
+
status.metadata.version = meta.version;
|
|
227
|
+
|
|
228
|
+
// Archival settings
|
|
229
|
+
if (meta.archival?.enabled) {
|
|
230
|
+
status.features.archival.enabled = true;
|
|
231
|
+
status.features.archival.threshold = meta.archival.threshold_days;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Damage control metadata
|
|
235
|
+
if (meta.features?.damagecontrol?.enabled) {
|
|
236
|
+
status.features.damagecontrol.level = meta.features.damagecontrol.protectionLevel || 'standard';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// AskUserQuestion metadata
|
|
240
|
+
if (meta.features?.askUserQuestion?.enabled) {
|
|
241
|
+
status.features.askuserquestion.enabled = true;
|
|
242
|
+
status.features.askuserquestion.mode = meta.features.askUserQuestion.mode || 'all';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Read feature versions and check if outdated
|
|
246
|
+
if (meta.features) {
|
|
247
|
+
const featureKeyMap = { askUserQuestion: 'askuserquestion' };
|
|
248
|
+
Object.entries(meta.features).forEach(([feature, data]) => {
|
|
249
|
+
const statusKey = featureKeyMap[feature] || feature.toLowerCase();
|
|
250
|
+
if (status.features[statusKey] && data.version) {
|
|
251
|
+
status.features[statusKey].version = data.version;
|
|
252
|
+
if (data.version !== version && status.features[statusKey].enabled) {
|
|
253
|
+
status.features[statusKey].outdated = true;
|
|
254
|
+
status.hasOutdated = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// STATUS PRINTING
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Print configuration status to console
|
|
267
|
+
* @param {object} status - Status object from detectConfig
|
|
268
|
+
* @returns {{ hasIssues: boolean, hasOutdated: boolean }}
|
|
269
|
+
*/
|
|
270
|
+
function printStatus(status) {
|
|
271
|
+
header('Current Configuration');
|
|
272
|
+
|
|
273
|
+
// Git status
|
|
274
|
+
log(
|
|
275
|
+
`Git: ${status.git.initialized ? '' : ''} ${status.git.initialized ? 'initialized' : 'not initialized'}${status.git.remote ? ` (${status.git.remote})` : ''}`,
|
|
276
|
+
status.git.initialized ? c.green : c.dim
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// Settings status
|
|
280
|
+
if (!status.settingsExists) {
|
|
281
|
+
log('Settings: .claude/settings.json not found', c.dim);
|
|
282
|
+
} else if (!status.settingsValid) {
|
|
283
|
+
log('Settings: Invalid JSON', c.red);
|
|
284
|
+
} else {
|
|
285
|
+
log('Settings: .claude/settings.json exists', c.green);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Features status
|
|
289
|
+
header('Features:');
|
|
290
|
+
|
|
291
|
+
const printFeature = (name, label) => {
|
|
292
|
+
const f = status.features[name];
|
|
293
|
+
let statusIcon = f.enabled ? '' : '';
|
|
294
|
+
let statusText = f.enabled ? 'enabled' : 'disabled';
|
|
295
|
+
let color = f.enabled ? c.green : c.dim;
|
|
296
|
+
|
|
297
|
+
if (f.enabled && !f.valid) {
|
|
298
|
+
statusIcon = '';
|
|
299
|
+
statusText = 'INVALID FORMAT';
|
|
300
|
+
color = c.yellow;
|
|
301
|
+
} else if (f.enabled && f.outdated) {
|
|
302
|
+
statusIcon = '';
|
|
303
|
+
statusText = `outdated (v${f.version} -> v${status.currentVersion})`;
|
|
304
|
+
color = c.yellow;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
log(` ${statusIcon} ${label}: ${statusText}`, color);
|
|
308
|
+
|
|
309
|
+
if (f.issues?.length > 0) {
|
|
310
|
+
f.issues.forEach(issue => log(` - ${issue}`, c.yellow));
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
printFeature('sessionstart', 'SessionStart Hook');
|
|
315
|
+
printFeature('precompact', 'PreCompact Hook');
|
|
316
|
+
printFeature('ralphloop', 'RalphLoop (Stop)');
|
|
317
|
+
printFeature('selfimprove', 'SelfImprove (Stop)');
|
|
318
|
+
|
|
319
|
+
// Archival (special display)
|
|
320
|
+
const arch = status.features.archival;
|
|
321
|
+
log(
|
|
322
|
+
` ${arch.enabled ? '' : ''} Archival: ${arch.enabled ? `${arch.threshold} days` : 'disabled'}`,
|
|
323
|
+
arch.enabled ? c.green : c.dim
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
printFeature('statusline', 'Status Line');
|
|
327
|
+
|
|
328
|
+
// Damage Control (special display)
|
|
329
|
+
const dc = status.features.damagecontrol;
|
|
330
|
+
if (dc.enabled) {
|
|
331
|
+
let dcStatusText = 'enabled';
|
|
332
|
+
if (dc.level) dcStatusText += ` (${dc.level})`;
|
|
333
|
+
if (!dc.valid) dcStatusText = 'INCOMPLETE';
|
|
334
|
+
const dcIcon = dc.enabled && dc.valid ? '' : '';
|
|
335
|
+
const dcColor = dc.enabled && dc.valid ? c.green : c.yellow;
|
|
336
|
+
log(` ${dcIcon} Damage Control: ${dcStatusText}`, dcColor);
|
|
337
|
+
if (dc.issues?.length > 0) {
|
|
338
|
+
dc.issues.forEach(issue => log(` - ${issue}`, c.yellow));
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
log(` Damage Control: disabled`, c.dim);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// AskUserQuestion
|
|
345
|
+
const auq = status.features.askuserquestion;
|
|
346
|
+
if (auq.enabled) {
|
|
347
|
+
let auqStatusText = 'enabled';
|
|
348
|
+
if (auq.mode) auqStatusText += ` (mode: ${auq.mode})`;
|
|
349
|
+
log(` AskUserQuestion: ${auqStatusText}`, c.green);
|
|
350
|
+
} else {
|
|
351
|
+
log(` AskUserQuestion: disabled`, c.dim);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Metadata version
|
|
355
|
+
if (status.metadata.exists) {
|
|
356
|
+
log(`\nMetadata: v${status.metadata.version}`, c.dim);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Issues summary
|
|
360
|
+
const hasIssues = Object.values(status.features).some(f => f.issues?.length > 0);
|
|
361
|
+
if (hasIssues) {
|
|
362
|
+
log('\n Format issues detected! Run with --migrate to fix.', c.yellow);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (status.hasOutdated) {
|
|
366
|
+
log('\n Outdated scripts detected! Run with --upgrade to update.', c.yellow);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return { hasIssues, hasOutdated: status.hasOutdated };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
module.exports = {
|
|
373
|
+
detectConfig,
|
|
374
|
+
printStatus,
|
|
375
|
+
// Export helper functions for testing
|
|
376
|
+
detectHooks,
|
|
377
|
+
detectSessionStartHook,
|
|
378
|
+
detectPreCompactHook,
|
|
379
|
+
detectStopHooks,
|
|
380
|
+
detectPreToolUseHooks,
|
|
381
|
+
detectStatusLine,
|
|
382
|
+
detectMetadata,
|
|
383
|
+
};
|