agileflow 2.50.0 → 2.55.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.
Files changed (87) hide show
  1. package/README.md +82 -460
  2. package/package.json +18 -3
  3. package/scripts/agileflow-configure.js +134 -63
  4. package/scripts/agileflow-welcome.js +161 -31
  5. package/scripts/generators/agent-registry.js +2 -2
  6. package/scripts/generators/command-registry.js +6 -6
  7. package/scripts/generators/index.js +2 -6
  8. package/scripts/generators/inject-babysit.js +9 -2
  9. package/scripts/generators/inject-help.js +3 -1
  10. package/scripts/generators/inject-readme.js +7 -3
  11. package/scripts/generators/skill-registry.js +5 -5
  12. package/scripts/get-env.js +13 -12
  13. package/scripts/obtain-context.js +396 -185
  14. package/scripts/session-coordinator.sh +232 -0
  15. package/scripts/session-manager.js +512 -0
  16. package/src/core/agents/orchestrator.md +275 -0
  17. package/src/core/commands/adr.md +38 -16
  18. package/src/core/commands/agent.md +39 -22
  19. package/src/core/commands/assign.md +17 -0
  20. package/src/core/commands/auto.md +60 -46
  21. package/src/core/commands/babysit.md +302 -637
  22. package/src/core/commands/baseline.md +20 -0
  23. package/src/core/commands/blockers.md +33 -48
  24. package/src/core/commands/board.md +19 -0
  25. package/src/core/commands/changelog.md +20 -0
  26. package/src/core/commands/ci.md +17 -0
  27. package/src/core/commands/context.md +43 -40
  28. package/src/core/commands/debt.md +76 -45
  29. package/src/core/commands/deploy.md +20 -0
  30. package/src/core/commands/deps.md +40 -46
  31. package/src/core/commands/diagnose.md +24 -18
  32. package/src/core/commands/docs.md +18 -0
  33. package/src/core/commands/epic.md +31 -0
  34. package/src/core/commands/feedback.md +33 -21
  35. package/src/core/commands/handoff.md +29 -0
  36. package/src/core/commands/help.md +16 -7
  37. package/src/core/commands/impact.md +31 -61
  38. package/src/core/commands/metrics.md +17 -35
  39. package/src/core/commands/packages.md +21 -0
  40. package/src/core/commands/pr.md +15 -0
  41. package/src/core/commands/readme-sync.md +42 -9
  42. package/src/core/commands/research.md +58 -11
  43. package/src/core/commands/retro.md +42 -50
  44. package/src/core/commands/review.md +22 -27
  45. package/src/core/commands/session/end.md +53 -297
  46. package/src/core/commands/session/history.md +38 -257
  47. package/src/core/commands/session/init.md +44 -446
  48. package/src/core/commands/session/new.md +152 -0
  49. package/src/core/commands/session/resume.md +51 -447
  50. package/src/core/commands/session/status.md +32 -244
  51. package/src/core/commands/sprint.md +33 -0
  52. package/src/core/commands/status.md +18 -0
  53. package/src/core/commands/story-validate.md +32 -0
  54. package/src/core/commands/story.md +21 -6
  55. package/src/core/commands/template.md +18 -0
  56. package/src/core/commands/tests.md +22 -0
  57. package/src/core/commands/update.md +72 -58
  58. package/src/core/commands/validate-expertise.md +25 -37
  59. package/src/core/commands/velocity.md +33 -74
  60. package/src/core/commands/verify.md +16 -0
  61. package/src/core/experts/documentation/expertise.yaml +16 -2
  62. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +57 -219
  63. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +86 -0
  64. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +79 -0
  65. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +142 -0
  66. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +83 -0
  67. package/src/core/skills/writing-skills/SKILL.md +352 -0
  68. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +232 -0
  69. package/tools/cli/agileflow-cli.js +4 -2
  70. package/tools/cli/commands/config.js +20 -13
  71. package/tools/cli/commands/doctor.js +25 -9
  72. package/tools/cli/commands/list.js +10 -6
  73. package/tools/cli/commands/setup.js +54 -3
  74. package/tools/cli/commands/status.js +6 -8
  75. package/tools/cli/commands/uninstall.js +5 -5
  76. package/tools/cli/commands/update.js +51 -7
  77. package/tools/cli/installers/core/installer.js +8 -4
  78. package/tools/cli/installers/ide/_base-ide.js +3 -1
  79. package/tools/cli/installers/ide/claude-code.js +3 -7
  80. package/tools/cli/installers/ide/codex.js +440 -0
  81. package/tools/cli/installers/ide/manager.js +2 -6
  82. package/tools/cli/lib/content-injector.js +3 -3
  83. package/tools/cli/lib/docs-setup.js +3 -2
  84. package/tools/cli/lib/npm-utils.js +3 -3
  85. package/tools/cli/lib/ui.js +7 -7
  86. package/tools/cli/lib/version-checker.js +3 -3
  87. package/tools/postinstall.js +2 -3
@@ -41,34 +41,43 @@ const FEATURES = {
41
41
  precompact: { hook: 'PreCompact', script: 'precompact-context.sh', type: 'bash' },
42
42
  stop: { hook: 'Stop', script: 'agileflow-stop.sh', type: 'bash' },
43
43
  archival: { script: 'archive-completed-stories.sh', requiresHook: 'sessionstart' },
44
- statusline: { script: 'agileflow-statusline.sh' }
44
+ statusline: { script: 'agileflow-statusline.sh' },
45
45
  };
46
46
 
47
47
  // Statusline component names
48
- const STATUSLINE_COMPONENTS = ['agileflow', 'model', 'story', 'epic', 'wip', 'context', 'cost', 'git'];
48
+ const STATUSLINE_COMPONENTS = [
49
+ 'agileflow',
50
+ 'model',
51
+ 'story',
52
+ 'epic',
53
+ 'wip',
54
+ 'context',
55
+ 'cost',
56
+ 'git',
57
+ ];
49
58
 
50
59
  const PROFILES = {
51
60
  full: {
52
61
  description: 'All features enabled',
53
62
  enable: ['sessionstart', 'precompact', 'stop', 'archival', 'statusline'],
54
- archivalDays: 7
63
+ archivalDays: 7,
55
64
  },
56
65
  basic: {
57
66
  description: 'Essential hooks + archival (SessionStart + PreCompact + Archival)',
58
67
  enable: ['sessionstart', 'precompact', 'archival'],
59
68
  disable: ['stop', 'statusline'],
60
- archivalDays: 7
69
+ archivalDays: 7,
61
70
  },
62
71
  minimal: {
63
72
  description: 'SessionStart + archival only',
64
73
  enable: ['sessionstart', 'archival'],
65
74
  disable: ['precompact', 'stop', 'statusline'],
66
- archivalDays: 7
75
+ archivalDays: 7,
67
76
  },
68
77
  none: {
69
78
  description: 'Disable all AgileFlow features',
70
- disable: ['sessionstart', 'precompact', 'stop', 'archival', 'statusline']
71
- }
79
+ disable: ['sessionstart', 'precompact', 'stop', 'archival', 'statusline'],
80
+ },
72
81
  };
73
82
 
74
83
  // ============================================================================
@@ -76,8 +85,13 @@ const PROFILES = {
76
85
  // ============================================================================
77
86
 
78
87
  const c = {
79
- reset: '\x1b[0m', dim: '\x1b[2m', bold: '\x1b[1m',
80
- green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m'
88
+ reset: '\x1b[0m',
89
+ dim: '\x1b[2m',
90
+ bold: '\x1b[1m',
91
+ green: '\x1b[32m',
92
+ yellow: '\x1b[33m',
93
+ red: '\x1b[31m',
94
+ cyan: '\x1b[36m',
81
95
  };
82
96
 
83
97
  const log = (msg, color = '') => console.log(`${color}${msg}${c.reset}`);
@@ -91,11 +105,16 @@ const header = msg => log(`\n${msg}`, c.bold + c.cyan);
91
105
  // FILE UTILITIES
92
106
  // ============================================================================
93
107
 
94
- const ensureDir = dir => { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); };
108
+ const ensureDir = dir => {
109
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
110
+ };
95
111
 
96
112
  const readJSON = filePath => {
97
- try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
98
- catch { return null; }
113
+ try {
114
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
115
+ } catch {
116
+ return null;
117
+ }
99
118
  };
100
119
 
101
120
  const writeJSON = (filePath, data) => {
@@ -107,12 +126,14 @@ const copyTemplate = (templateName, destPath) => {
107
126
  const sources = [
108
127
  path.join(process.cwd(), '.agileflow', 'templates', templateName),
109
128
  path.join(__dirname, templateName),
110
- path.join(__dirname, '..', 'templates', templateName)
129
+ path.join(__dirname, '..', 'templates', templateName),
111
130
  ];
112
131
  for (const src of sources) {
113
132
  if (fs.existsSync(src)) {
114
133
  fs.copyFileSync(src, destPath);
115
- try { fs.chmodSync(destPath, '755'); } catch {}
134
+ try {
135
+ fs.chmodSync(destPath, '755');
136
+ } catch {}
116
137
  return true;
117
138
  }
118
139
  }
@@ -134,16 +155,18 @@ function detectConfig() {
134
155
  precompact: { enabled: false, valid: true, issues: [] },
135
156
  stop: { enabled: false, valid: true, issues: [] },
136
157
  archival: { enabled: false, threshold: null },
137
- statusline: { enabled: false, valid: true, issues: [] }
158
+ statusline: { enabled: false, valid: true, issues: [] },
138
159
  },
139
- metadata: { exists: false, version: null }
160
+ metadata: { exists: false, version: null },
140
161
  };
141
162
 
142
163
  // Git
143
164
  if (fs.existsSync('.git')) {
144
165
  status.git.initialized = true;
145
166
  try {
146
- status.git.remote = execSync('git remote get-url origin 2>/dev/null', { encoding: 'utf8' }).trim();
167
+ status.git.remote = execSync('git remote get-url origin 2>/dev/null', {
168
+ encoding: 'utf8',
169
+ }).trim();
147
170
  } catch {}
148
171
  }
149
172
 
@@ -160,7 +183,10 @@ function detectConfig() {
160
183
  if (settings.hooks) {
161
184
  // SessionStart
162
185
  if (settings.hooks.SessionStart) {
163
- if (Array.isArray(settings.hooks.SessionStart) && settings.hooks.SessionStart.length > 0) {
186
+ if (
187
+ Array.isArray(settings.hooks.SessionStart) &&
188
+ settings.hooks.SessionStart.length > 0
189
+ ) {
164
190
  const hook = settings.hooks.SessionStart[0];
165
191
  if (hook.matcher !== undefined && hook.hooks) {
166
192
  status.features.sessionstart.enabled = true;
@@ -248,8 +274,10 @@ function printStatus(status) {
248
274
  header('📊 Current Configuration');
249
275
 
250
276
  // Git
251
- log(`Git: ${status.git.initialized ? '✅' : '❌'} ${status.git.initialized ? 'initialized' : 'not initialized'}${status.git.remote ? ` (${status.git.remote})` : ''}`,
252
- status.git.initialized ? c.green : c.dim);
277
+ log(
278
+ `Git: ${status.git.initialized ? '✅' : '❌'} ${status.git.initialized ? 'initialized' : 'not initialized'}${status.git.remote ? ` (${status.git.remote})` : ''}`,
279
+ status.git.initialized ? c.green : c.dim
280
+ );
253
281
 
254
282
  // Settings
255
283
  if (!status.settingsExists) {
@@ -287,8 +315,10 @@ function printStatus(status) {
287
315
  printFeature('stop', 'Stop Hook');
288
316
 
289
317
  const arch = status.features.archival;
290
- log(` ${arch.enabled ? '✅' : '❌'} Archival: ${arch.enabled ? `${arch.threshold} days` : 'disabled'}`,
291
- arch.enabled ? c.green : c.dim);
318
+ log(
319
+ ` ${arch.enabled ? '✅' : '❌'} Archival: ${arch.enabled ? `${arch.threshold} days` : 'disabled'}`,
320
+ arch.enabled ? c.green : c.dim
321
+ );
292
322
 
293
323
  printFeature('statusline', 'Status Line');
294
324
 
@@ -335,10 +365,12 @@ function migrateSettings() {
335
365
  // String format → array format
336
366
  if (typeof hook === 'string') {
337
367
  const isNode = hook.includes('node ') || hook.endsWith('.js');
338
- settings.hooks[hookName] = [{
339
- matcher: '',
340
- hooks: [{ type: 'command', command: isNode ? hook : `bash ${hook}` }]
341
- }];
368
+ settings.hooks[hookName] = [
369
+ {
370
+ matcher: '',
371
+ hooks: [{ type: 'command', command: isNode ? hook : `bash ${hook}` }],
372
+ },
373
+ ];
342
374
  success(`Migrated ${hookName} from string format`);
343
375
  migrated = true;
344
376
  }
@@ -348,19 +380,23 @@ function migrateSettings() {
348
380
  if (first.enabled !== undefined || first.command !== undefined) {
349
381
  // Old format with enabled/command
350
382
  if (first.command) {
351
- settings.hooks[hookName] = [{
352
- matcher: '',
353
- hooks: [{ type: 'command', command: first.command }]
354
- }];
383
+ settings.hooks[hookName] = [
384
+ {
385
+ matcher: '',
386
+ hooks: [{ type: 'command', command: first.command }],
387
+ },
388
+ ];
355
389
  success(`Migrated ${hookName} from old object format`);
356
390
  migrated = true;
357
391
  }
358
392
  } else if (first.matcher === undefined) {
359
393
  // Missing matcher
360
- settings.hooks[hookName] = [{
361
- matcher: '',
362
- hooks: first.hooks || [{ type: 'command', command: 'echo "hook"' }]
363
- }];
394
+ settings.hooks[hookName] = [
395
+ {
396
+ matcher: '',
397
+ hooks: first.hooks || [{ type: 'command', command: 'echo "hook"' }],
398
+ },
399
+ ];
364
400
  success(`Migrated ${hookName} - added matcher`);
365
401
  migrated = true;
366
402
  }
@@ -374,7 +410,7 @@ function migrateSettings() {
374
410
  settings.statusLine = {
375
411
  type: 'command',
376
412
  command: settings.statusLine,
377
- padding: 0
413
+ padding: 0,
378
414
  };
379
415
  success('Migrated statusLine from string format');
380
416
  migrated = true;
@@ -417,7 +453,7 @@ function enableFeature(feature, options = {}) {
417
453
  ensureDir('.claude');
418
454
  ensureDir('scripts');
419
455
 
420
- let settings = readJSON('.claude/settings.json') || {};
456
+ const settings = readJSON('.claude/settings.json') || {};
421
457
  settings.hooks = settings.hooks || {};
422
458
  settings.permissions = settings.permissions || { allow: [], deny: [], ask: [] };
423
459
 
@@ -429,31 +465,39 @@ function enableFeature(feature, options = {}) {
429
465
  if (!copyTemplate(config.script, scriptPath)) {
430
466
  // Create minimal version
431
467
  if (feature === 'sessionstart') {
432
- fs.writeFileSync(scriptPath, `#!/usr/bin/env node\nconsole.log('AgileFlow v${VERSION} loaded');\n`);
468
+ fs.writeFileSync(
469
+ scriptPath,
470
+ `#!/usr/bin/env node\nconsole.log('AgileFlow v${VERSION} loaded');\n`
471
+ );
433
472
  } else if (feature === 'precompact') {
434
473
  fs.writeFileSync(scriptPath, '#!/bin/bash\necho "PreCompact: preserving context"\n');
435
474
  } else if (feature === 'stop') {
436
- fs.writeFileSync(scriptPath, `#!/bin/bash
475
+ fs.writeFileSync(
476
+ scriptPath,
477
+ `#!/bin/bash
437
478
  git rev-parse --git-dir > /dev/null 2>&1 || exit 0
438
479
  CHANGES=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
439
480
  [ "$CHANGES" -gt 0 ] && echo -e "\\n\\033[33m$CHANGES uncommitted change(s)\\033[0m"
440
- `);
481
+ `
482
+ );
441
483
  }
442
- try { fs.chmodSync(scriptPath, '755'); } catch {}
484
+ try {
485
+ fs.chmodSync(scriptPath, '755');
486
+ } catch {}
443
487
  warn(`Created minimal ${config.script}`);
444
488
  } else {
445
489
  success(`Deployed ${config.script}`);
446
490
  }
447
491
 
448
492
  // Configure hook
449
- const command = config.type === 'node'
450
- ? `node ${scriptPath}`
451
- : `bash ${scriptPath}`;
452
-
453
- settings.hooks[config.hook] = [{
454
- matcher: '',
455
- hooks: [{ type: 'command', command }]
456
- }];
493
+ const command = config.type === 'node' ? `node ${scriptPath}` : `bash ${scriptPath}`;
494
+
495
+ settings.hooks[config.hook] = [
496
+ {
497
+ matcher: '',
498
+ hooks: [{ type: 'command', command }],
499
+ },
500
+ ];
457
501
  success(`${config.hook} hook enabled`);
458
502
  }
459
503
 
@@ -470,13 +514,13 @@ CHANGES=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
470
514
 
471
515
  // Add to SessionStart hook
472
516
  if (settings.hooks.SessionStart?.[0]?.hooks) {
473
- const hasArchival = settings.hooks.SessionStart[0].hooks.some(
474
- h => h.command?.includes('archive-completed-stories')
517
+ const hasArchival = settings.hooks.SessionStart[0].hooks.some(h =>
518
+ h.command?.includes('archive-completed-stories')
475
519
  );
476
520
  if (!hasArchival) {
477
521
  settings.hooks.SessionStart[0].hooks.push({
478
522
  type: 'command',
479
- command: 'bash scripts/archive-completed-stories.sh --quiet'
523
+ command: 'bash scripts/archive-completed-stories.sh --quiet',
480
524
  });
481
525
  }
482
526
  }
@@ -491,12 +535,17 @@ CHANGES=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
491
535
  const scriptPath = 'scripts/agileflow-statusline.sh';
492
536
 
493
537
  if (!copyTemplate('agileflow-statusline.sh', scriptPath)) {
494
- fs.writeFileSync(scriptPath, `#!/bin/bash
538
+ fs.writeFileSync(
539
+ scriptPath,
540
+ `#!/bin/bash
495
541
  input=$(cat)
496
542
  MODEL=$(echo "$input" | jq -r '.model.display_name // "Claude"')
497
543
  echo "[$MODEL] AgileFlow"
498
- `);
499
- try { fs.chmodSync(scriptPath, '755'); } catch {}
544
+ `
545
+ );
546
+ try {
547
+ fs.chmodSync(scriptPath, '755');
548
+ } catch {}
500
549
  warn('Created minimal statusline script');
501
550
  } else {
502
551
  success('Deployed agileflow-statusline.sh');
@@ -505,13 +554,15 @@ echo "[$MODEL] AgileFlow"
505
554
  settings.statusLine = {
506
555
  type: 'command',
507
556
  command: 'bash scripts/agileflow-statusline.sh',
508
- padding: 0
557
+ padding: 0,
509
558
  };
510
559
  success('Status line enabled');
511
560
  }
512
561
 
513
562
  writeJSON('.claude/settings.json', settings);
514
- updateMetadata({ features: { [feature]: { enabled: true, version: VERSION, at: new Date().toISOString() } } });
563
+ updateMetadata({
564
+ features: { [feature]: { enabled: true, version: VERSION, at: new Date().toISOString() } },
565
+ });
515
566
  updateGitignore();
516
567
 
517
568
  return true;
@@ -557,7 +608,9 @@ function disableFeature(feature) {
557
608
  }
558
609
 
559
610
  writeJSON('.claude/settings.json', settings);
560
- updateMetadata({ features: { [feature]: { enabled: false, version: VERSION, at: new Date().toISOString() } } });
611
+ updateMetadata({
612
+ features: { [feature]: { enabled: false, version: VERSION, at: new Date().toISOString() } },
613
+ });
561
614
 
562
615
  return true;
563
616
  }
@@ -600,7 +653,7 @@ function updateGitignore() {
600
653
  '.claude/context.log',
601
654
  '.claude/hook.log',
602
655
  '.claude/prompt-log.txt',
603
- '.claude/session.log'
656
+ '.claude/session.log',
604
657
  ];
605
658
 
606
659
  let content = fs.existsSync('.gitignore') ? fs.readFileSync('.gitignore', 'utf8') : '';
@@ -718,7 +771,9 @@ function applyProfile(profileName, options = {}) {
718
771
 
719
772
  // Enable features
720
773
  if (profile.enable) {
721
- profile.enable.forEach(f => enableFeature(f, { archivalDays: profile.archivalDays || options.archivalDays }));
774
+ profile.enable.forEach(f =>
775
+ enableFeature(f, { archivalDays: profile.archivalDays || options.archivalDays })
776
+ );
722
777
  }
723
778
 
724
779
  // Disable features
@@ -842,10 +897,26 @@ function main() {
842
897
 
843
898
  args.forEach(arg => {
844
899
  if (arg.startsWith('--profile=')) profile = arg.split('=')[1];
845
- else if (arg.startsWith('--enable=')) enable = arg.split('=')[1].split(',').map(s => s.trim().toLowerCase());
846
- else if (arg.startsWith('--disable=')) disable = arg.split('=')[1].split(',').map(s => s.trim().toLowerCase());
847
- else if (arg.startsWith('--show=')) show = arg.split('=')[1].split(',').map(s => s.trim().toLowerCase());
848
- else if (arg.startsWith('--hide=')) hide = arg.split('=')[1].split(',').map(s => s.trim().toLowerCase());
900
+ else if (arg.startsWith('--enable='))
901
+ enable = arg
902
+ .split('=')[1]
903
+ .split(',')
904
+ .map(s => s.trim().toLowerCase());
905
+ else if (arg.startsWith('--disable='))
906
+ disable = arg
907
+ .split('=')[1]
908
+ .split(',')
909
+ .map(s => s.trim().toLowerCase());
910
+ else if (arg.startsWith('--show='))
911
+ show = arg
912
+ .split('=')[1]
913
+ .split(',')
914
+ .map(s => s.trim().toLowerCase());
915
+ else if (arg.startsWith('--hide='))
916
+ hide = arg
917
+ .split('=')[1]
918
+ .split(',')
919
+ .map(s => s.trim().toLowerCase());
849
920
  else if (arg.startsWith('--archival-days=')) archivalDays = parseInt(arg.split('=')[1]) || 7;
850
921
  else if (arg === '--migrate') migrate = true;
851
922
  else if (arg === '--detect' || arg === '--validate') detect = true;