agileflow 2.51.0 → 2.56.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/README.md +80 -460
- package/package.json +18 -3
- package/scripts/agileflow-configure.js +134 -63
- package/scripts/agileflow-welcome.js +161 -31
- package/scripts/generators/agent-registry.js +45 -57
- package/scripts/generators/command-registry.js +48 -32
- package/scripts/generators/index.js +2 -6
- package/scripts/generators/inject-babysit.js +9 -2
- package/scripts/generators/inject-help.js +3 -1
- package/scripts/generators/inject-readme.js +7 -3
- package/scripts/generators/skill-registry.js +60 -33
- package/scripts/get-env.js +13 -12
- package/scripts/lib/frontmatter-parser.js +82 -0
- package/scripts/obtain-context.js +79 -26
- package/scripts/session-coordinator.sh +232 -0
- package/scripts/session-manager.js +512 -0
- package/src/core/agents/orchestrator.md +275 -0
- package/src/core/commands/adr.md +38 -16
- package/src/core/commands/agent.md +39 -22
- package/src/core/commands/assign.md +17 -0
- package/src/core/commands/auto.md +60 -46
- package/src/core/commands/babysit.md +302 -637
- package/src/core/commands/baseline.md +20 -0
- package/src/core/commands/blockers.md +33 -48
- package/src/core/commands/board.md +19 -0
- package/src/core/commands/changelog.md +20 -0
- package/src/core/commands/ci.md +17 -0
- package/src/core/commands/context.md +43 -40
- package/src/core/commands/debt.md +76 -45
- package/src/core/commands/deploy.md +20 -0
- package/src/core/commands/deps.md +40 -46
- package/src/core/commands/diagnose.md +24 -18
- package/src/core/commands/docs.md +18 -0
- package/src/core/commands/epic.md +31 -0
- package/src/core/commands/feedback.md +33 -21
- package/src/core/commands/handoff.md +29 -0
- package/src/core/commands/help.md +16 -7
- package/src/core/commands/impact.md +31 -61
- package/src/core/commands/metrics.md +17 -35
- package/src/core/commands/packages.md +21 -0
- package/src/core/commands/pr.md +15 -0
- package/src/core/commands/readme-sync.md +42 -9
- package/src/core/commands/research.md +58 -11
- package/src/core/commands/retro.md +42 -50
- package/src/core/commands/review.md +22 -27
- package/src/core/commands/session/end.md +53 -297
- package/src/core/commands/session/history.md +38 -257
- package/src/core/commands/session/init.md +44 -446
- package/src/core/commands/session/new.md +152 -0
- package/src/core/commands/session/resume.md +51 -447
- package/src/core/commands/session/status.md +32 -244
- package/src/core/commands/sprint.md +33 -0
- package/src/core/commands/status.md +18 -0
- package/src/core/commands/story-validate.md +32 -0
- package/src/core/commands/story.md +21 -6
- package/src/core/commands/template.md +18 -0
- package/src/core/commands/tests.md +22 -0
- package/src/core/commands/update.md +72 -58
- package/src/core/commands/validate-expertise.md +25 -37
- package/src/core/commands/velocity.md +33 -74
- package/src/core/commands/verify.md +16 -0
- package/src/core/experts/documentation/expertise.yaml +16 -2
- package/src/core/skills/agileflow-retro-facilitator/SKILL.md +57 -219
- package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +86 -0
- package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +79 -0
- package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +142 -0
- package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +83 -0
- package/src/core/skills/writing-skills/SKILL.md +352 -0
- package/src/core/skills/writing-skills/testing-skills-with-subagents.md +232 -0
- package/tools/cli/agileflow-cli.js +4 -2
- package/tools/cli/commands/config.js +20 -13
- package/tools/cli/commands/doctor.js +25 -9
- package/tools/cli/commands/list.js +10 -6
- package/tools/cli/commands/setup.js +54 -3
- package/tools/cli/commands/status.js +6 -8
- package/tools/cli/commands/uninstall.js +5 -5
- package/tools/cli/commands/update.js +51 -7
- package/tools/cli/installers/core/installer.js +8 -4
- package/tools/cli/installers/ide/_base-ide.js +58 -1
- package/tools/cli/installers/ide/claude-code.js +3 -61
- package/tools/cli/installers/ide/codex.js +440 -0
- package/tools/cli/installers/ide/cursor.js +21 -51
- package/tools/cli/installers/ide/manager.js +2 -6
- package/tools/cli/installers/ide/windsurf.js +20 -50
- package/tools/cli/lib/content-injector.js +26 -49
- package/tools/cli/lib/docs-setup.js +3 -2
- package/tools/cli/lib/npm-utils.js +39 -12
- package/tools/cli/lib/ui.js +31 -10
- package/tools/cli/lib/version-checker.js +3 -3
- package/tools/postinstall.js +2 -3
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
|
-
const { execSync } = require('child_process');
|
|
16
|
+
const { execSync, spawnSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
// Session manager path (relative to script location)
|
|
19
|
+
const SESSION_MANAGER_PATH = path.join(__dirname, 'session-manager.js');
|
|
17
20
|
|
|
18
21
|
// ANSI color codes
|
|
19
22
|
const c = {
|
|
@@ -39,9 +42,16 @@ const c = {
|
|
|
39
42
|
|
|
40
43
|
// Box drawing characters
|
|
41
44
|
const box = {
|
|
42
|
-
tl: '╭',
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
tl: '╭',
|
|
46
|
+
tr: '╮',
|
|
47
|
+
bl: '╰',
|
|
48
|
+
br: '╯',
|
|
49
|
+
h: '─',
|
|
50
|
+
v: '│',
|
|
51
|
+
lT: '├',
|
|
52
|
+
rT: '┤',
|
|
53
|
+
tT: '┬',
|
|
54
|
+
bT: '┴',
|
|
45
55
|
cross: '┼',
|
|
46
56
|
};
|
|
47
57
|
|
|
@@ -70,7 +80,9 @@ function getProjectInfo(rootDir) {
|
|
|
70
80
|
|
|
71
81
|
// Get package info
|
|
72
82
|
try {
|
|
73
|
-
const pkg = JSON.parse(
|
|
83
|
+
const pkg = JSON.parse(
|
|
84
|
+
fs.readFileSync(path.join(rootDir, 'packages/cli/package.json'), 'utf8')
|
|
85
|
+
);
|
|
74
86
|
info.version = pkg.version || info.version;
|
|
75
87
|
} catch (e) {
|
|
76
88
|
try {
|
|
@@ -83,7 +95,10 @@ function getProjectInfo(rootDir) {
|
|
|
83
95
|
try {
|
|
84
96
|
info.branch = execSync('git branch --show-current', { cwd: rootDir, encoding: 'utf8' }).trim();
|
|
85
97
|
info.commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf8' }).trim();
|
|
86
|
-
info.lastCommit = execSync('git log -1 --format="%s"', {
|
|
98
|
+
info.lastCommit = execSync('git log -1 --format="%s"', {
|
|
99
|
+
cwd: rootDir,
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
}).trim();
|
|
87
102
|
} catch (e) {}
|
|
88
103
|
|
|
89
104
|
// Get status info
|
|
@@ -155,7 +170,7 @@ function runArchival(rootDir) {
|
|
|
155
170
|
execSync('bash scripts/archive-completed-stories.sh', {
|
|
156
171
|
cwd: rootDir,
|
|
157
172
|
encoding: 'utf8',
|
|
158
|
-
stdio: 'pipe'
|
|
173
|
+
stdio: 'pipe',
|
|
159
174
|
});
|
|
160
175
|
result.archived = toArchiveCount;
|
|
161
176
|
result.remaining -= toArchiveCount;
|
|
@@ -201,6 +216,60 @@ function clearActiveCommands(rootDir) {
|
|
|
201
216
|
return result;
|
|
202
217
|
}
|
|
203
218
|
|
|
219
|
+
function checkParallelSessions(rootDir) {
|
|
220
|
+
const result = {
|
|
221
|
+
available: false,
|
|
222
|
+
registered: false,
|
|
223
|
+
otherActive: 0,
|
|
224
|
+
currentId: null,
|
|
225
|
+
cleaned: 0,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
// Check if session manager exists
|
|
230
|
+
const managerPath = path.join(rootDir, '.agileflow', 'scripts', 'session-manager.js');
|
|
231
|
+
if (!fs.existsSync(managerPath) && !fs.existsSync(SESSION_MANAGER_PATH)) {
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
result.available = true;
|
|
236
|
+
|
|
237
|
+
// Try to register current session and get status
|
|
238
|
+
const scriptPath = fs.existsSync(managerPath) ? managerPath : SESSION_MANAGER_PATH;
|
|
239
|
+
|
|
240
|
+
// Register this session
|
|
241
|
+
try {
|
|
242
|
+
const registerOutput = execSync(`node "${scriptPath}" register`, {
|
|
243
|
+
cwd: rootDir,
|
|
244
|
+
encoding: 'utf8',
|
|
245
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
246
|
+
});
|
|
247
|
+
const registerData = JSON.parse(registerOutput);
|
|
248
|
+
result.registered = true;
|
|
249
|
+
result.currentId = registerData.id;
|
|
250
|
+
} catch (e) {
|
|
251
|
+
// Registration failed, continue anyway
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Get count of other active sessions
|
|
255
|
+
try {
|
|
256
|
+
const countOutput = execSync(`node "${scriptPath}" count`, {
|
|
257
|
+
cwd: rootDir,
|
|
258
|
+
encoding: 'utf8',
|
|
259
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
260
|
+
});
|
|
261
|
+
const countData = JSON.parse(countOutput);
|
|
262
|
+
result.otherActive = countData.count || 0;
|
|
263
|
+
} catch (e) {
|
|
264
|
+
// Count failed
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
// Session system not available
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
204
273
|
function checkPreCompact(rootDir) {
|
|
205
274
|
const result = { configured: false, scriptExists: false, version: null, outdated: false };
|
|
206
275
|
|
|
@@ -258,7 +327,7 @@ function getFeatureVersions(rootDir) {
|
|
|
258
327
|
hooks: { version: null, outdated: false },
|
|
259
328
|
archival: { version: null, outdated: false },
|
|
260
329
|
statusline: { version: null, outdated: false },
|
|
261
|
-
precompact: { version: null, outdated: false }
|
|
330
|
+
precompact: { version: null, outdated: false },
|
|
262
331
|
};
|
|
263
332
|
|
|
264
333
|
// Minimum compatible versions for each feature
|
|
@@ -266,7 +335,7 @@ function getFeatureVersions(rootDir) {
|
|
|
266
335
|
hooks: '2.35.0',
|
|
267
336
|
archival: '2.35.0',
|
|
268
337
|
statusline: '2.35.0',
|
|
269
|
-
precompact: '2.40.0'
|
|
338
|
+
precompact: '2.40.0', // Multi-command support
|
|
270
339
|
};
|
|
271
340
|
|
|
272
341
|
try {
|
|
@@ -277,7 +346,8 @@ function getFeatureVersions(rootDir) {
|
|
|
277
346
|
for (const feature of Object.keys(result)) {
|
|
278
347
|
if (metadata.features?.[feature]?.configured_version) {
|
|
279
348
|
result[feature].version = metadata.features[feature].configured_version;
|
|
280
|
-
result[feature].outdated =
|
|
349
|
+
result[feature].outdated =
|
|
350
|
+
compareVersions(result[feature].version, minVersions[feature]) < 0;
|
|
281
351
|
}
|
|
282
352
|
}
|
|
283
353
|
}
|
|
@@ -291,7 +361,8 @@ function pad(str, len, align = 'left') {
|
|
|
291
361
|
const diff = len - stripped.length;
|
|
292
362
|
if (diff <= 0) return str;
|
|
293
363
|
if (align === 'right') return ' '.repeat(diff) + str;
|
|
294
|
-
if (align === 'center')
|
|
364
|
+
if (align === 'center')
|
|
365
|
+
return ' '.repeat(Math.floor(diff / 2)) + str + ' '.repeat(Math.ceil(diff / 2));
|
|
295
366
|
return str + ' '.repeat(diff);
|
|
296
367
|
}
|
|
297
368
|
|
|
@@ -323,7 +394,7 @@ function truncate(str, maxLen, suffix = '..') {
|
|
|
323
394
|
return str.substring(0, cutIndex) + suffix;
|
|
324
395
|
}
|
|
325
396
|
|
|
326
|
-
function formatTable(info, archival, session, precompact) {
|
|
397
|
+
function formatTable(info, archival, session, precompact, parallelSessions) {
|
|
327
398
|
const W = 58; // inner width
|
|
328
399
|
const R = W - 24; // right column width (34 chars)
|
|
329
400
|
const lines = [];
|
|
@@ -336,17 +407,20 @@ function formatTable(info, archival, session, precompact) {
|
|
|
336
407
|
return `${c.dim}${box.v}${c.reset} ${pad(leftStr, 20)} ${c.dim}${box.v}${c.reset} ${pad(rightStr, R)} ${c.dim}${box.v}${c.reset}`;
|
|
337
408
|
};
|
|
338
409
|
|
|
339
|
-
const divider = () =>
|
|
410
|
+
const divider = () =>
|
|
411
|
+
`${c.dim}${box.lT}${box.h.repeat(22)}${box.cross}${box.h.repeat(W - 22)}${box.rT}${c.reset}`;
|
|
340
412
|
const topBorder = `${c.dim}${box.tl}${box.h.repeat(22)}${box.tT}${box.h.repeat(W - 22)}${box.tr}${c.reset}`;
|
|
341
413
|
const bottomBorder = `${c.dim}${box.bl}${box.h.repeat(22)}${box.bT}${box.h.repeat(W - 22)}${box.br}${c.reset}`;
|
|
342
414
|
|
|
343
415
|
// Header (truncate branch name if too long)
|
|
344
|
-
const branchColor =
|
|
416
|
+
const branchColor =
|
|
417
|
+
info.branch === 'main' ? c.green : info.branch.startsWith('fix') ? c.red : c.cyan;
|
|
345
418
|
// Fixed parts: "agileflow " (10) + "v" (1) + version + " " (2) + " (" (2) + commit (7) + ")" (1) = 23 + version.length
|
|
346
|
-
const maxBranchLen =
|
|
347
|
-
const branchDisplay =
|
|
348
|
-
|
|
349
|
-
|
|
419
|
+
const maxBranchLen = W - 1 - 23 - info.version.length;
|
|
420
|
+
const branchDisplay =
|
|
421
|
+
info.branch.length > maxBranchLen
|
|
422
|
+
? info.branch.substring(0, maxBranchLen - 2) + '..'
|
|
423
|
+
: info.branch;
|
|
350
424
|
const header = `${c.brand}${c.bold}agileflow${c.reset} ${c.dim}v${info.version}${c.reset} ${branchColor}${branchDisplay}${c.reset} ${c.dim}(${info.commit})${c.reset}`;
|
|
351
425
|
const headerLine = `${c.dim}${box.v}${c.reset} ${pad(header, W - 1)} ${c.dim}${box.v}${c.reset}`;
|
|
352
426
|
|
|
@@ -355,10 +429,38 @@ function formatTable(info, archival, session, precompact) {
|
|
|
355
429
|
lines.push(divider());
|
|
356
430
|
|
|
357
431
|
// Stories section
|
|
358
|
-
lines.push(
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
432
|
+
lines.push(
|
|
433
|
+
row(
|
|
434
|
+
'In Progress',
|
|
435
|
+
info.wipCount > 0 ? `${info.wipCount}` : '0',
|
|
436
|
+
c.dim,
|
|
437
|
+
info.wipCount > 0 ? c.yellow : c.dim
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
lines.push(
|
|
441
|
+
row(
|
|
442
|
+
'Blocked',
|
|
443
|
+
info.blockedCount > 0 ? `${info.blockedCount}` : '0',
|
|
444
|
+
c.dim,
|
|
445
|
+
info.blockedCount > 0 ? c.red : c.dim
|
|
446
|
+
)
|
|
447
|
+
);
|
|
448
|
+
lines.push(
|
|
449
|
+
row(
|
|
450
|
+
'Ready',
|
|
451
|
+
info.readyCount > 0 ? `${info.readyCount}` : '0',
|
|
452
|
+
c.dim,
|
|
453
|
+
info.readyCount > 0 ? c.cyan : c.dim
|
|
454
|
+
)
|
|
455
|
+
);
|
|
456
|
+
lines.push(
|
|
457
|
+
row(
|
|
458
|
+
'Completed',
|
|
459
|
+
info.completedCount > 0 ? `${info.completedCount}` : '0',
|
|
460
|
+
c.dim,
|
|
461
|
+
info.completedCount > 0 ? c.green : c.dim
|
|
462
|
+
)
|
|
463
|
+
);
|
|
362
464
|
|
|
363
465
|
lines.push(divider());
|
|
364
466
|
|
|
@@ -366,16 +468,15 @@ function formatTable(info, archival, session, precompact) {
|
|
|
366
468
|
if (archival.disabled) {
|
|
367
469
|
lines.push(row('Auto-archival', 'disabled', c.dim, c.dim));
|
|
368
470
|
} else {
|
|
369
|
-
const archivalStatus =
|
|
370
|
-
? `archived ${archival.archived} stories`
|
|
371
|
-
|
|
372
|
-
|
|
471
|
+
const archivalStatus =
|
|
472
|
+
archival.archived > 0 ? `archived ${archival.archived} stories` : `nothing to archive`;
|
|
473
|
+
lines.push(
|
|
474
|
+
row('Auto-archival', archivalStatus, c.dim, archival.archived > 0 ? c.green : c.dim)
|
|
475
|
+
);
|
|
373
476
|
}
|
|
374
477
|
|
|
375
478
|
// Session cleanup
|
|
376
|
-
const sessionStatus = session.cleared > 0
|
|
377
|
-
? `cleared ${session.cleared} command(s)`
|
|
378
|
-
: `clean`;
|
|
479
|
+
const sessionStatus = session.cleared > 0 ? `cleared ${session.cleared} command(s)` : `clean`;
|
|
379
480
|
lines.push(row('Session state', sessionStatus, c.dim, session.cleared > 0 ? c.green : c.dim));
|
|
380
481
|
|
|
381
482
|
// PreCompact status with version check
|
|
@@ -396,11 +497,31 @@ function formatTable(info, archival, session, precompact) {
|
|
|
396
497
|
lines.push(row('Context preserve', 'not configured', c.dim, c.dim));
|
|
397
498
|
}
|
|
398
499
|
|
|
500
|
+
// Parallel sessions status
|
|
501
|
+
if (parallelSessions && parallelSessions.available) {
|
|
502
|
+
if (parallelSessions.otherActive > 0) {
|
|
503
|
+
const sessionStr = `⚠️ ${parallelSessions.otherActive} other active`;
|
|
504
|
+
lines.push(row('Sessions', sessionStr, c.dim, c.yellow));
|
|
505
|
+
} else {
|
|
506
|
+
const sessionStr = parallelSessions.currentId
|
|
507
|
+
? `✓ Session ${parallelSessions.currentId} (only)`
|
|
508
|
+
: '✓ Only session';
|
|
509
|
+
lines.push(row('Sessions', sessionStr, c.dim, c.green));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
399
513
|
lines.push(divider());
|
|
400
514
|
|
|
401
515
|
// Current story (if any) - row() auto-truncates
|
|
402
516
|
if (info.currentStory) {
|
|
403
|
-
lines.push(
|
|
517
|
+
lines.push(
|
|
518
|
+
row(
|
|
519
|
+
'Current',
|
|
520
|
+
`${c.blue}${info.currentStory.id}${c.reset}: ${info.currentStory.title}`,
|
|
521
|
+
c.dim,
|
|
522
|
+
''
|
|
523
|
+
)
|
|
524
|
+
);
|
|
404
525
|
} else {
|
|
405
526
|
lines.push(row('Current', 'No active story', c.dim, c.dim));
|
|
406
527
|
}
|
|
@@ -420,8 +541,17 @@ function main() {
|
|
|
420
541
|
const archival = runArchival(rootDir);
|
|
421
542
|
const session = clearActiveCommands(rootDir);
|
|
422
543
|
const precompact = checkPreCompact(rootDir);
|
|
544
|
+
const parallelSessions = checkParallelSessions(rootDir);
|
|
545
|
+
|
|
546
|
+
console.log(formatTable(info, archival, session, precompact, parallelSessions));
|
|
423
547
|
|
|
424
|
-
|
|
548
|
+
// Show warning and tip if other sessions are active
|
|
549
|
+
if (parallelSessions.otherActive > 0) {
|
|
550
|
+
console.log('');
|
|
551
|
+
console.log(`${c.yellow}⚠️ Other Claude session(s) active in this repo.${c.reset}`);
|
|
552
|
+
console.log(`${c.dim} Run /agileflow:session:status to see all sessions.${c.reset}`);
|
|
553
|
+
console.log(`${c.dim} Run /agileflow:session:new to create isolated workspace.${c.reset}`);
|
|
554
|
+
}
|
|
425
555
|
}
|
|
426
556
|
|
|
427
557
|
main();
|
|
@@ -5,58 +5,25 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Scans agents/ directory and extracts metadata from frontmatter.
|
|
7
7
|
* Returns structured agent registry for use in generators.
|
|
8
|
+
*
|
|
9
|
+
* Set DEBUG_REGISTRY=1 for verbose logging of skipped files.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
const fs = require('fs');
|
|
11
13
|
const path = require('path');
|
|
14
|
+
const { extractFrontmatter, normalizeTools } = require('../lib/frontmatter-parser');
|
|
15
|
+
|
|
16
|
+
// Debug mode: set DEBUG_REGISTRY=1 to see why files are skipped
|
|
17
|
+
const DEBUG = process.env.DEBUG_REGISTRY === '1';
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @param {string} filePath - Path to markdown file
|
|
17
|
-
* @returns {object} Frontmatter object
|
|
20
|
+
* Log debug messages when DEBUG_REGISTRY=1
|
|
21
|
+
* @param {string} message - Message to log
|
|
18
22
|
*/
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!frontmatterMatch) {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const frontmatter = {};
|
|
28
|
-
const lines = frontmatterMatch[1].split('\n');
|
|
29
|
-
let currentKey = null;
|
|
30
|
-
let currentArray = null;
|
|
31
|
-
|
|
32
|
-
for (const line of lines) {
|
|
33
|
-
// Handle array items (lines starting with -)
|
|
34
|
-
if (line.trim().startsWith('-')) {
|
|
35
|
-
if (currentArray) {
|
|
36
|
-
currentArray.push(line.trim().substring(1).trim());
|
|
37
|
-
}
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Handle key-value pairs
|
|
42
|
-
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
43
|
-
if (match) {
|
|
44
|
-
const [, key, value] = match;
|
|
45
|
-
currentKey = key;
|
|
46
|
-
|
|
47
|
-
// If value is empty, it's likely an array
|
|
48
|
-
if (!value) {
|
|
49
|
-
currentArray = [];
|
|
50
|
-
frontmatter[key] = currentArray;
|
|
51
|
-
} else {
|
|
52
|
-
// Remove quotes if present
|
|
53
|
-
frontmatter[key] = value.replace(/^["']|["']$/g, '');
|
|
54
|
-
currentArray = null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
23
|
+
function debugLog(message) {
|
|
24
|
+
if (DEBUG) {
|
|
25
|
+
console.error(`[agent-registry] ${message}`);
|
|
57
26
|
}
|
|
58
|
-
|
|
59
|
-
return frontmatter;
|
|
60
27
|
}
|
|
61
28
|
|
|
62
29
|
/**
|
|
@@ -74,7 +41,7 @@ function categorizeAgent(name, description) {
|
|
|
74
41
|
'Maintenance & Optimization': ['refactor', 'performance', 'monitoring'],
|
|
75
42
|
'Documentation & Knowledge': ['documentation', 'readme-updater', 'research'],
|
|
76
43
|
'Compliance & Governance': ['compliance', 'analytics'],
|
|
77
|
-
|
|
44
|
+
Mentorship: ['mentor'],
|
|
78
45
|
};
|
|
79
46
|
|
|
80
47
|
for (const [category, keywords] of Object.entries(categories)) {
|
|
@@ -93,25 +60,40 @@ function categorizeAgent(name, description) {
|
|
|
93
60
|
*/
|
|
94
61
|
function scanAgents(agentsDir) {
|
|
95
62
|
const agents = [];
|
|
96
|
-
const
|
|
63
|
+
const skipped = [];
|
|
64
|
+
|
|
65
|
+
let files;
|
|
66
|
+
try {
|
|
67
|
+
files = fs.readdirSync(agentsDir);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
debugLog(`Failed to read directory: ${err.message}`);
|
|
70
|
+
return agents;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
debugLog(`Scanning ${files.length} files in ${agentsDir}`);
|
|
97
74
|
|
|
98
75
|
for (const file of files) {
|
|
99
|
-
if (!file.endsWith('.md'))
|
|
76
|
+
if (!file.endsWith('.md')) {
|
|
77
|
+
debugLog(`Skipping non-md file: ${file}`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
100
80
|
|
|
101
81
|
const filePath = path.join(agentsDir, file);
|
|
102
82
|
const frontmatter = extractFrontmatter(filePath);
|
|
103
83
|
const name = file.replace('.md', '');
|
|
104
84
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} else if (typeof frontmatter.tools === 'string') {
|
|
111
|
-
tools = frontmatter.tools.split(',').map(t => t.trim());
|
|
112
|
-
}
|
|
85
|
+
// Check if frontmatter was extracted successfully
|
|
86
|
+
if (Object.keys(frontmatter).length === 0) {
|
|
87
|
+
skipped.push({ file, reason: 'no frontmatter or parse error' });
|
|
88
|
+
debugLog(`Skipping ${file}: no frontmatter found`);
|
|
89
|
+
continue;
|
|
113
90
|
}
|
|
114
91
|
|
|
92
|
+
// Normalize tools field (handles array or comma-separated string)
|
|
93
|
+
const tools = normalizeTools(frontmatter.tools);
|
|
94
|
+
|
|
95
|
+
debugLog(`Loaded ${file}: name="${frontmatter.name || name}", tools=${tools.length}`);
|
|
96
|
+
|
|
115
97
|
agents.push({
|
|
116
98
|
name,
|
|
117
99
|
file,
|
|
@@ -121,7 +103,7 @@ function scanAgents(agentsDir) {
|
|
|
121
103
|
tools,
|
|
122
104
|
model: frontmatter.model || 'haiku',
|
|
123
105
|
color: frontmatter.color || 'blue',
|
|
124
|
-
category: categorizeAgent(name, frontmatter.description || '')
|
|
106
|
+
category: categorizeAgent(name, frontmatter.description || ''),
|
|
125
107
|
});
|
|
126
108
|
}
|
|
127
109
|
|
|
@@ -133,6 +115,12 @@ function scanAgents(agentsDir) {
|
|
|
133
115
|
return a.name.localeCompare(b.name);
|
|
134
116
|
});
|
|
135
117
|
|
|
118
|
+
if (skipped.length > 0) {
|
|
119
|
+
debugLog(`Skipped ${skipped.length} files: ${skipped.map(s => s.file).join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
debugLog(`Found ${agents.length} agents`);
|
|
123
|
+
|
|
136
124
|
return agents;
|
|
137
125
|
}
|
|
138
126
|
|
|
@@ -159,7 +147,7 @@ function main() {
|
|
|
159
147
|
}
|
|
160
148
|
|
|
161
149
|
// Export for use in other scripts
|
|
162
|
-
module.exports = { scanAgents,
|
|
150
|
+
module.exports = { scanAgents, categorizeAgent };
|
|
163
151
|
|
|
164
152
|
// Run if called directly
|
|
165
153
|
if (require.main === module) {
|
|
@@ -5,37 +5,25 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Scans commands/ directory and extracts metadata from frontmatter.
|
|
7
7
|
* Returns structured command registry for use in generators.
|
|
8
|
+
*
|
|
9
|
+
* Set DEBUG_REGISTRY=1 for verbose logging of skipped files.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
const fs = require('fs');
|
|
11
13
|
const path = require('path');
|
|
14
|
+
const { extractFrontmatter } = require('../lib/frontmatter-parser');
|
|
15
|
+
|
|
16
|
+
// Debug mode: set DEBUG_REGISTRY=1 to see why files are skipped
|
|
17
|
+
const DEBUG = process.env.DEBUG_REGISTRY === '1';
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @param {string}
|
|
16
|
-
* @returns {object} Frontmatter object
|
|
20
|
+
* Log debug messages when DEBUG_REGISTRY=1
|
|
21
|
+
* @param {string} message - Message to log
|
|
17
22
|
*/
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!frontmatterMatch) {
|
|
23
|
-
return {};
|
|
23
|
+
function debugLog(message) {
|
|
24
|
+
if (DEBUG) {
|
|
25
|
+
console.error(`[command-registry] ${message}`);
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
const frontmatter = {};
|
|
27
|
-
const lines = frontmatterMatch[1].split('\n');
|
|
28
|
-
|
|
29
|
-
for (const line of lines) {
|
|
30
|
-
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
31
|
-
if (match) {
|
|
32
|
-
const [, key, value] = match;
|
|
33
|
-
// Remove quotes if present
|
|
34
|
-
frontmatter[key] = value.replace(/^["']|["']$/g, '');
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return frontmatter;
|
|
39
27
|
}
|
|
40
28
|
|
|
41
29
|
/**
|
|
@@ -47,15 +35,15 @@ function extractFrontmatter(filePath) {
|
|
|
47
35
|
function categorizeCommand(name, description) {
|
|
48
36
|
const categories = {
|
|
49
37
|
'Story Management': ['story', 'epic', 'assign', 'status'],
|
|
50
|
-
|
|
38
|
+
Development: ['verify', 'baseline', 'resume', 'session-init', 'babysit'],
|
|
51
39
|
'Quality & Testing': ['tests', 'review', 'ci'],
|
|
52
|
-
|
|
40
|
+
Documentation: ['docs', 'adr', 'readme-sync'],
|
|
53
41
|
'Planning & Metrics': ['sprint', 'velocity', 'metrics', 'board', 'deps'],
|
|
54
42
|
'Research & Strategy': ['research', 'product'],
|
|
55
43
|
'Deployment & Operations': ['deploy', 'packages'],
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
Collaboration: ['update', 'handoff', 'feedback', 'retro'],
|
|
45
|
+
Maintenance: ['debt', 'compress', 'template'],
|
|
46
|
+
System: ['setup', 'help', 'diagnose', 'auto', 'agent'],
|
|
59
47
|
};
|
|
60
48
|
|
|
61
49
|
for (const [category, keywords] of Object.entries(categories)) {
|
|
@@ -74,22 +62,44 @@ function categorizeCommand(name, description) {
|
|
|
74
62
|
*/
|
|
75
63
|
function scanCommands(commandsDir) {
|
|
76
64
|
const commands = [];
|
|
77
|
-
const
|
|
65
|
+
const skipped = [];
|
|
66
|
+
|
|
67
|
+
let files;
|
|
68
|
+
try {
|
|
69
|
+
files = fs.readdirSync(commandsDir);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
debugLog(`Failed to read directory: ${err.message}`);
|
|
72
|
+
return commands;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
debugLog(`Scanning ${files.length} files in ${commandsDir}`);
|
|
78
76
|
|
|
79
77
|
for (const file of files) {
|
|
80
|
-
if (!file.endsWith('.md'))
|
|
78
|
+
if (!file.endsWith('.md')) {
|
|
79
|
+
debugLog(`Skipping non-md file: ${file}`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
81
82
|
|
|
82
83
|
const filePath = path.join(commandsDir, file);
|
|
83
84
|
const frontmatter = extractFrontmatter(filePath);
|
|
84
85
|
const name = file.replace('.md', '');
|
|
85
86
|
|
|
87
|
+
// Check if frontmatter was extracted successfully
|
|
88
|
+
if (Object.keys(frontmatter).length === 0) {
|
|
89
|
+
skipped.push({ file, reason: 'no frontmatter or parse error' });
|
|
90
|
+
debugLog(`Skipping ${file}: no frontmatter found`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
debugLog(`Loaded ${file}: description="${frontmatter.description || '(none)'}"`);
|
|
95
|
+
|
|
86
96
|
commands.push({
|
|
87
97
|
name,
|
|
88
98
|
file,
|
|
89
99
|
path: filePath,
|
|
90
100
|
description: frontmatter.description || '',
|
|
91
101
|
argumentHint: frontmatter['argument-hint'] || '',
|
|
92
|
-
category: categorizeCommand(name, frontmatter.description || '')
|
|
102
|
+
category: categorizeCommand(name, frontmatter.description || ''),
|
|
93
103
|
});
|
|
94
104
|
}
|
|
95
105
|
|
|
@@ -101,6 +111,12 @@ function scanCommands(commandsDir) {
|
|
|
101
111
|
return a.name.localeCompare(b.name);
|
|
102
112
|
});
|
|
103
113
|
|
|
114
|
+
if (skipped.length > 0) {
|
|
115
|
+
debugLog(`Skipped ${skipped.length} files: ${skipped.map(s => s.file).join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
debugLog(`Found ${commands.length} commands`);
|
|
119
|
+
|
|
104
120
|
return commands;
|
|
105
121
|
}
|
|
106
122
|
|
|
@@ -127,7 +143,7 @@ function main() {
|
|
|
127
143
|
}
|
|
128
144
|
|
|
129
145
|
// Export for use in other scripts
|
|
130
|
-
module.exports = { scanCommands,
|
|
146
|
+
module.exports = { scanCommands, categorizeCommand };
|
|
131
147
|
|
|
132
148
|
// Run if called directly
|
|
133
149
|
if (require.main === module) {
|
|
@@ -25,7 +25,7 @@ function runGenerator(scriptName) {
|
|
|
25
25
|
try {
|
|
26
26
|
execSync(`node "${scriptPath}"`, {
|
|
27
27
|
cwd: __dirname,
|
|
28
|
-
stdio: 'inherit'
|
|
28
|
+
stdio: 'inherit',
|
|
29
29
|
});
|
|
30
30
|
console.log(`✅ ${scriptName} completed successfully`);
|
|
31
31
|
return true;
|
|
@@ -42,11 +42,7 @@ function main() {
|
|
|
42
42
|
console.log('🚀 AgileFlow Content Generation System');
|
|
43
43
|
console.log('Generating content from metadata...\n');
|
|
44
44
|
|
|
45
|
-
const generators = [
|
|
46
|
-
'inject-help.js',
|
|
47
|
-
'inject-babysit.js',
|
|
48
|
-
'inject-readme.js'
|
|
49
|
-
];
|
|
45
|
+
const generators = ['inject-help.js', 'inject-babysit.js', 'inject-readme.js'];
|
|
50
46
|
|
|
51
47
|
const results = [];
|
|
52
48
|
|
|
@@ -83,7 +83,9 @@ function injectContentByMarker(content, markerName, generated) {
|
|
|
83
83
|
const timestamp = new Date().toISOString().split('T')[0];
|
|
84
84
|
const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
|
|
85
85
|
|
|
86
|
-
return
|
|
86
|
+
return (
|
|
87
|
+
content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length)
|
|
88
|
+
);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
/**
|
|
@@ -159,7 +161,12 @@ function main() {
|
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
// Export for use in orchestrator
|
|
162
|
-
module.exports = {
|
|
164
|
+
module.exports = {
|
|
165
|
+
generateAgentList,
|
|
166
|
+
generateCommandReference,
|
|
167
|
+
injectContentByMarker,
|
|
168
|
+
addMarkersIfMissing,
|
|
169
|
+
};
|
|
163
170
|
|
|
164
171
|
// Run if called directly
|
|
165
172
|
if (require.main === module) {
|
|
@@ -62,7 +62,9 @@ function injectContent(content, generated) {
|
|
|
62
62
|
const timestamp = new Date().toISOString().split('T')[0];
|
|
63
63
|
const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
|
|
64
64
|
|
|
65
|
-
return
|
|
65
|
+
return (
|
|
66
|
+
content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length)
|
|
67
|
+
);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
/**
|