agileflow 2.74.0 → 2.76.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 (44) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/scripts/agileflow-configure.js +105 -27
  4. package/scripts/agileflow-welcome.js +95 -2
  5. package/scripts/auto-self-improve.js +301 -0
  6. package/scripts/ralph-loop.js +491 -0
  7. package/src/core/agents/accessibility.md +1 -1
  8. package/src/core/agents/adr-writer.md +6 -6
  9. package/src/core/agents/analytics.md +1 -1
  10. package/src/core/agents/api.md +130 -41
  11. package/src/core/agents/ci.md +3 -3
  12. package/src/core/agents/compliance.md +1 -1
  13. package/src/core/agents/database.md +121 -36
  14. package/src/core/agents/datamigration.md +1 -1
  15. package/src/core/agents/design.md +2 -2
  16. package/src/core/agents/devops.md +2 -2
  17. package/src/core/agents/documentation.md +2 -2
  18. package/src/core/agents/epic-planner.md +4 -4
  19. package/src/core/agents/integrations.md +4 -4
  20. package/src/core/agents/mentor.md +6 -6
  21. package/src/core/agents/mobile.md +2 -2
  22. package/src/core/agents/monitoring.md +2 -2
  23. package/src/core/agents/performance.md +2 -2
  24. package/src/core/agents/product.md +3 -3
  25. package/src/core/agents/qa.md +1 -1
  26. package/src/core/agents/refactor.md +1 -1
  27. package/src/core/agents/security.md +4 -4
  28. package/src/core/agents/testing.md +2 -2
  29. package/src/core/agents/ui.md +129 -44
  30. package/src/core/commands/babysit.md +210 -0
  31. package/src/core/commands/blockers.md +3 -3
  32. package/src/core/commands/configure.md +50 -7
  33. package/src/core/commands/context/export.md +99 -0
  34. package/src/core/commands/context/full.md +172 -0
  35. package/src/core/commands/context/note.md +128 -0
  36. package/src/core/commands/research/ask.md +453 -0
  37. package/src/core/commands/research/import.md +287 -0
  38. package/src/core/commands/research/list.md +93 -0
  39. package/src/core/commands/research/view.md +113 -0
  40. package/src/core/experts/documentation/expertise.yaml +4 -0
  41. package/src/core/experts/research/expertise.yaml +4 -4
  42. package/tools/cli/lib/docs-setup.js +1 -1
  43. package/src/core/commands/context.md +0 -417
  44. package/src/core/commands/research.md +0 -124
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/agileflow?color=brightgreen)](https://www.npmjs.com/package/agileflow)
6
- [![Commands](https://img.shields.io/badge/commands-53-blue)](docs/04-architecture/commands.md)
6
+ [![Commands](https://img.shields.io/badge/commands-58-blue)](docs/04-architecture/commands.md)
7
7
  [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-27-orange)](docs/04-architecture/subagents.md)
8
8
  [![Skills](https://img.shields.io/badge/skills-dynamic-purple)](docs/04-architecture/skills.md)
9
9
 
@@ -65,7 +65,7 @@ AgileFlow combines three proven methodologies:
65
65
 
66
66
  | Component | Count | Description |
67
67
  |-----------|-------|-------------|
68
- | [Commands](docs/04-architecture/commands.md) | 53 | Slash commands for agile workflows |
68
+ | [Commands](docs/04-architecture/commands.md) | 58 | Slash commands for agile workflows |
69
69
  | [Agents/Experts](docs/04-architecture/subagents.md) | 27 | Specialized agents with self-improving knowledge bases |
70
70
  | [Skills](docs/04-architecture/skills.md) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
71
71
 
@@ -76,7 +76,7 @@ AgileFlow combines three proven methodologies:
76
76
  Full documentation lives in [`docs/04-architecture/`](docs/04-architecture/):
77
77
 
78
78
  ### Reference
79
- - [Commands](docs/04-architecture/commands.md) - All 53 slash commands
79
+ - [Commands](docs/04-architecture/commands.md) - All 58 slash commands
80
80
  - [Agents/Experts](docs/04-architecture/subagents.md) - 27 specialized agents with self-improving knowledge
81
81
  - [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
82
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.74.0",
3
+ "version": "2.76.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -23,7 +23,7 @@
23
23
  * --detect Show current status
24
24
  * --help Show help
25
25
  *
26
- * Features: sessionstart, precompact, archival, statusline, autoupdate
26
+ * Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate
27
27
  */
28
28
 
29
29
  const fs = require('fs');
@@ -71,7 +71,8 @@ const VERSION = getVersion();
71
71
  const FEATURES = {
72
72
  sessionstart: { hook: 'SessionStart', script: 'agileflow-welcome.js', type: 'node' },
73
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)
74
+ ralphloop: { hook: 'Stop', script: 'ralph-loop.js', type: 'node' },
75
+ selfimprove: { hook: 'Stop', script: 'auto-self-improve.js', type: 'node' },
75
76
  archival: { script: 'archive-completed-stories.sh', requiresHook: 'sessionstart' },
76
77
  statusline: { script: 'agileflow-statusline.sh' },
77
78
  autoupdate: { metadataOnly: true }, // Stored in metadata.updates.autoUpdate
@@ -82,6 +83,8 @@ const ALL_SCRIPTS = {
82
83
  // Core feature scripts (linked to FEATURES)
83
84
  'agileflow-welcome.js': { feature: 'sessionstart', required: true },
84
85
  'precompact-context.sh': { feature: 'precompact', required: true },
86
+ 'ralph-loop.js': { feature: 'ralphloop', required: true },
87
+ 'auto-self-improve.js': { feature: 'selfimprove', required: true },
85
88
  'archive-completed-stories.sh': { feature: 'archival', required: true },
86
89
  'agileflow-statusline.sh': { feature: 'statusline', required: true },
87
90
 
@@ -119,25 +122,25 @@ const STATUSLINE_COMPONENTS = [
119
122
 
120
123
  const PROFILES = {
121
124
  full: {
122
- description: 'All features enabled',
123
- enable: ['sessionstart', 'precompact', 'archival', 'statusline'],
125
+ description: 'All features enabled (including experimental Stop hooks)',
126
+ enable: ['sessionstart', 'precompact', 'archival', 'statusline', 'ralphloop', 'selfimprove'],
124
127
  archivalDays: 30,
125
128
  },
126
129
  basic: {
127
130
  description: 'Essential hooks + archival (SessionStart + PreCompact + Archival)',
128
131
  enable: ['sessionstart', 'precompact', 'archival'],
129
- disable: ['statusline'],
132
+ disable: ['statusline', 'ralphloop', 'selfimprove'],
130
133
  archivalDays: 30,
131
134
  },
132
135
  minimal: {
133
136
  description: 'SessionStart + archival only',
134
137
  enable: ['sessionstart', 'archival'],
135
- disable: ['precompact', 'statusline'],
138
+ disable: ['precompact', 'statusline', 'ralphloop', 'selfimprove'],
136
139
  archivalDays: 30,
137
140
  },
138
141
  none: {
139
142
  description: 'Disable all AgileFlow features',
140
- disable: ['sessionstart', 'precompact', 'archival', 'statusline'],
143
+ disable: ['sessionstart', 'precompact', 'archival', 'statusline', 'ralphloop', 'selfimprove'],
141
144
  },
142
145
  };
143
146
 
@@ -208,6 +211,8 @@ function detectConfig() {
208
211
  features: {
209
212
  sessionstart: { enabled: false, valid: true, issues: [], version: null, outdated: false },
210
213
  precompact: { enabled: false, valid: true, issues: [], version: null, outdated: false },
214
+ ralphloop: { enabled: false, valid: true, issues: [], version: null, outdated: false },
215
+ selfimprove: { enabled: false, valid: true, issues: [], version: null, outdated: false },
211
216
  archival: { enabled: false, threshold: null, version: null, outdated: false },
212
217
  statusline: { enabled: false, valid: true, issues: [], version: null, outdated: false },
213
218
  },
@@ -276,7 +281,23 @@ function detectConfig() {
276
281
  }
277
282
  }
278
283
 
279
- // Note: Stop hook removed due to reliability issues
284
+ // Stop hooks (ralphloop and selfimprove)
285
+ if (settings.hooks.Stop) {
286
+ if (Array.isArray(settings.hooks.Stop) && settings.hooks.Stop.length > 0) {
287
+ const hook = settings.hooks.Stop[0];
288
+ if (hook.matcher !== undefined && hook.hooks) {
289
+ // Check for each Stop hook feature
290
+ for (const h of hook.hooks) {
291
+ if (h.command?.includes('ralph-loop')) {
292
+ status.features.ralphloop.enabled = true;
293
+ }
294
+ if (h.command?.includes('auto-self-improve')) {
295
+ status.features.selfimprove.enabled = true;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }
280
301
  }
281
302
 
282
303
  // StatusLine
@@ -370,6 +391,8 @@ function printStatus(status) {
370
391
 
371
392
  printFeature('sessionstart', 'SessionStart Hook');
372
393
  printFeature('precompact', 'PreCompact Hook');
394
+ printFeature('ralphloop', 'RalphLoop (Stop)');
395
+ printFeature('selfimprove', 'SelfImprove (Stop)');
373
396
 
374
397
  const arch = status.features.archival;
375
398
  log(
@@ -417,9 +440,9 @@ function migrateSettings() {
417
440
 
418
441
  let migrated = false;
419
442
 
420
- // Migrate hooks (Stop hook removed due to reliability issues)
443
+ // Migrate hooks to new format
421
444
  if (settings.hooks) {
422
- ['SessionStart', 'PreCompact', 'UserPromptSubmit'].forEach(hookName => {
445
+ ['SessionStart', 'PreCompact', 'UserPromptSubmit', 'Stop'].forEach(hookName => {
423
446
  const hook = settings.hooks[hookName];
424
447
  if (!hook) return;
425
448
 
@@ -556,16 +579,46 @@ function enableFeature(feature, options = {}) {
556
579
  return false;
557
580
  }
558
581
 
559
- // Configure hook
560
- const command = config.type === 'node' ? `node ${scriptPath}` : `bash ${scriptPath}`;
582
+ // Use absolute path so hooks work from any subdirectory
583
+ const absoluteScriptPath = path.join(process.cwd(), scriptPath);
584
+
585
+ // Stop hooks use error suppression to avoid blocking Claude
586
+ const isStoHook = config.hook === 'Stop';
587
+ const command = config.type === 'node'
588
+ ? `node ${absoluteScriptPath}${isStoHook ? ' 2>/dev/null || true' : ''}`
589
+ : `bash ${absoluteScriptPath}${isStoHook ? ' 2>/dev/null || true' : ''}`;
590
+
591
+ if (isStoHook) {
592
+ // Stop hooks stack - add to existing hooks instead of replacing
593
+ if (!settings.hooks.Stop) {
594
+ settings.hooks.Stop = [{ matcher: '', hooks: [] }];
595
+ } else if (!Array.isArray(settings.hooks.Stop) || settings.hooks.Stop.length === 0) {
596
+ settings.hooks.Stop = [{ matcher: '', hooks: [] }];
597
+ } else if (!settings.hooks.Stop[0].hooks) {
598
+ settings.hooks.Stop[0].hooks = [];
599
+ }
600
+
601
+ // Check if this script is already added
602
+ const hasHook = settings.hooks.Stop[0].hooks.some(h =>
603
+ h.command?.includes(config.script)
604
+ );
561
605
 
562
- settings.hooks[config.hook] = [
563
- {
564
- matcher: '',
565
- hooks: [{ type: 'command', command }],
566
- },
567
- ];
568
- success(`${config.hook} hook enabled (${config.script})`);
606
+ if (!hasHook) {
607
+ settings.hooks.Stop[0].hooks.push({ type: 'command', command });
608
+ success(`Stop hook added (${config.script})`);
609
+ } else {
610
+ info(`${feature} already enabled`);
611
+ }
612
+ } else {
613
+ // Other hooks (SessionStart, PreCompact) replace entirely
614
+ settings.hooks[config.hook] = [
615
+ {
616
+ matcher: '',
617
+ hooks: [{ type: 'command', command }],
618
+ },
619
+ ];
620
+ success(`${config.hook} hook enabled (${config.script})`);
621
+ }
569
622
  }
570
623
 
571
624
  // Handle archival
@@ -579,7 +632,8 @@ function enableFeature(feature, options = {}) {
579
632
  return false;
580
633
  }
581
634
 
582
- // Add to SessionStart hook
635
+ // Use absolute path so hooks work from any subdirectory
636
+ const absoluteScriptPath = path.join(process.cwd(), scriptPath);
583
637
  if (settings.hooks.SessionStart?.[0]?.hooks) {
584
638
  const hasArchival = settings.hooks.SessionStart[0].hooks.some(h =>
585
639
  h.command?.includes('archive-completed-stories')
@@ -587,7 +641,7 @@ function enableFeature(feature, options = {}) {
587
641
  if (!hasArchival) {
588
642
  settings.hooks.SessionStart[0].hooks.push({
589
643
  type: 'command',
590
- command: `bash ${scriptPath} --quiet`,
644
+ command: `bash ${absoluteScriptPath} --quiet`,
591
645
  });
592
646
  }
593
647
  }
@@ -607,9 +661,11 @@ function enableFeature(feature, options = {}) {
607
661
  return false;
608
662
  }
609
663
 
664
+ // Use absolute path so hooks work from any subdirectory
665
+ const absoluteScriptPath = path.join(process.cwd(), scriptPath);
610
666
  settings.statusLine = {
611
667
  type: 'command',
612
- command: `bash ${scriptPath}`,
668
+ command: `bash ${absoluteScriptPath}`,
613
669
  padding: 0,
614
670
  };
615
671
  success('Status line enabled');
@@ -656,8 +712,28 @@ function disableFeature(feature) {
656
712
 
657
713
  // Disable hook
658
714
  if (config.hook && settings.hooks?.[config.hook]) {
659
- delete settings.hooks[config.hook];
660
- success(`${config.hook} hook disabled`);
715
+ if (config.hook === 'Stop') {
716
+ // Stop hooks stack - remove only this script, not the entire hook
717
+ if (settings.hooks.Stop?.[0]?.hooks) {
718
+ const before = settings.hooks.Stop[0].hooks.length;
719
+ settings.hooks.Stop[0].hooks = settings.hooks.Stop[0].hooks.filter(
720
+ h => !h.command?.includes(config.script)
721
+ );
722
+ const after = settings.hooks.Stop[0].hooks.length;
723
+
724
+ if (before > after) {
725
+ success(`Stop hook removed (${config.script})`);
726
+ }
727
+
728
+ // If no more Stop hooks, remove the entire Stop hook
729
+ if (settings.hooks.Stop[0].hooks.length === 0) {
730
+ delete settings.hooks.Stop;
731
+ }
732
+ }
733
+ } else {
734
+ delete settings.hooks[config.hook];
735
+ success(`${config.hook} hook disabled`);
736
+ }
661
737
  }
662
738
 
663
739
  // Disable archival
@@ -1142,8 +1218,8 @@ ${c.cyan}Usage:${c.reset}
1142
1218
  node .agileflow/scripts/agileflow-configure.js [options]
1143
1219
 
1144
1220
  ${c.cyan}Profiles:${c.reset}
1145
- --profile=full All features (hooks, archival, statusline)
1146
- --profile=basic SessionStart + PreCompact + archival
1221
+ --profile=full All features (hooks, Stop hooks, archival, statusline)
1222
+ --profile=basic SessionStart + PreCompact + archival (no Stop hooks)
1147
1223
  --profile=minimal SessionStart + archival only
1148
1224
  --profile=none Disable all AgileFlow features
1149
1225
 
@@ -1151,7 +1227,9 @@ ${c.cyan}Feature Control:${c.reset}
1151
1227
  --enable=<list> Enable features (comma-separated)
1152
1228
  --disable=<list> Disable features (comma-separated)
1153
1229
 
1154
- Features: sessionstart, precompact, archival, statusline
1230
+ Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline
1231
+
1232
+ Stop hooks (ralphloop, selfimprove) run when Claude completes/pauses
1155
1233
 
1156
1234
  ${c.cyan}Statusline Components:${c.reset}
1157
1235
  --show=<list> Show statusline components (comma-separated)
@@ -433,6 +433,85 @@ async function runAutoUpdate(rootDir) {
433
433
  }
434
434
  }
435
435
 
436
+ function validateExpertise(rootDir) {
437
+ const result = { total: 0, passed: 0, warnings: 0, failed: 0, issues: [] };
438
+
439
+ // Find experts directory
440
+ let expertsDir = path.join(rootDir, '.agileflow', 'experts');
441
+ if (!fs.existsSync(expertsDir)) {
442
+ expertsDir = path.join(rootDir, 'packages', 'cli', 'src', 'core', 'experts');
443
+ }
444
+ if (!fs.existsSync(expertsDir)) {
445
+ return result; // No experts directory found
446
+ }
447
+
448
+ const STALE_DAYS = 30;
449
+ const MAX_LINES = 200;
450
+
451
+ try {
452
+ const domains = fs.readdirSync(expertsDir, { withFileTypes: true })
453
+ .filter(d => d.isDirectory() && d.name !== 'templates')
454
+ .map(d => d.name);
455
+
456
+ for (const domain of domains) {
457
+ const filePath = path.join(expertsDir, domain, 'expertise.yaml');
458
+ if (!fs.existsSync(filePath)) {
459
+ result.total++;
460
+ result.failed++;
461
+ result.issues.push(`${domain}: missing file`);
462
+ continue;
463
+ }
464
+
465
+ result.total++;
466
+ const content = fs.readFileSync(filePath, 'utf8');
467
+ const lines = content.split('\n');
468
+ let status = 'pass';
469
+ let issue = '';
470
+
471
+ // Check required fields (use multiline flag)
472
+ const hasVersion = /^version:/m.test(content);
473
+ const hasDomain = /^domain:/m.test(content);
474
+ const hasLastUpdated = /^last_updated:/m.test(content);
475
+
476
+ if (!hasVersion || !hasDomain || !hasLastUpdated) {
477
+ status = 'fail';
478
+ issue = 'missing required fields';
479
+ }
480
+
481
+ // Check staleness
482
+ const lastUpdatedMatch = content.match(/^last_updated:\s*['"]?(\d{4}-\d{2}-\d{2})/m);
483
+ if (lastUpdatedMatch && status !== 'fail') {
484
+ const lastDate = new Date(lastUpdatedMatch[1]);
485
+ const daysSince = Math.floor((Date.now() - lastDate.getTime()) / (1000 * 60 * 60 * 24));
486
+ if (daysSince > STALE_DAYS) {
487
+ status = 'warn';
488
+ issue = `stale (${daysSince}d)`;
489
+ }
490
+ }
491
+
492
+ // Check file size
493
+ if (lines.length > MAX_LINES && status === 'pass') {
494
+ status = 'warn';
495
+ issue = `large (${lines.length} lines)`;
496
+ }
497
+
498
+ if (status === 'pass') {
499
+ result.passed++;
500
+ } else if (status === 'warn') {
501
+ result.warnings++;
502
+ result.issues.push(`${domain}: ${issue}`);
503
+ } else {
504
+ result.failed++;
505
+ result.issues.push(`${domain}: ${issue}`);
506
+ }
507
+ }
508
+ } catch (e) {
509
+ // Silently fail
510
+ }
511
+
512
+ return result;
513
+ }
514
+
436
515
  function getFeatureVersions(rootDir) {
437
516
  const result = {
438
517
  hooks: { version: null, outdated: false },
@@ -505,7 +584,7 @@ function truncate(str, maxLen, suffix = '..') {
505
584
  return str.substring(0, cutIndex) + suffix;
506
585
  }
507
586
 
508
- function formatTable(info, archival, session, precompact, parallelSessions, updateInfo = {}) {
587
+ function formatTable(info, archival, session, precompact, parallelSessions, updateInfo = {}, expertise = {}) {
509
588
  const W = 58; // inner width
510
589
  const R = W - 24; // right column width (34 chars)
511
590
  const lines = [];
@@ -662,6 +741,19 @@ function formatTable(info, archival, session, precompact, parallelSessions, upda
662
741
  }
663
742
  }
664
743
 
744
+ // Agent expertise validation (only show if issues exist)
745
+ if (expertise && expertise.total > 0) {
746
+ if (expertise.failed > 0) {
747
+ const expertStr = `❌ ${expertise.failed} failed, ${expertise.warnings} warnings`;
748
+ lines.push(row('Expertise', expertStr, c.dim, c.red));
749
+ } else if (expertise.warnings > 0) {
750
+ const expertStr = `⚠️ ${expertise.warnings} warnings (${expertise.passed} ok)`;
751
+ lines.push(row('Expertise', expertStr, c.dim, c.yellow));
752
+ } else {
753
+ lines.push(row('Expertise', `✓ ${expertise.total} valid`, c.dim, c.green));
754
+ }
755
+ }
756
+
665
757
  lines.push(divider());
666
758
 
667
759
  // Current story (if any) - row() auto-truncates
@@ -694,6 +786,7 @@ async function main() {
694
786
  const session = clearActiveCommands(rootDir);
695
787
  const precompact = checkPreCompact(rootDir);
696
788
  const parallelSessions = checkParallelSessions(rootDir);
789
+ const expertise = validateExpertise(rootDir);
697
790
 
698
791
  // Check for updates (async, cached)
699
792
  let updateInfo = {};
@@ -717,7 +810,7 @@ async function main() {
717
810
  // Update check failed - continue without it
718
811
  }
719
812
 
720
- console.log(formatTable(info, archival, session, precompact, parallelSessions, updateInfo));
813
+ console.log(formatTable(info, archival, session, precompact, parallelSessions, updateInfo, expertise));
721
814
 
722
815
  // Show warning and tip if other sessions are active
723
816
  if (parallelSessions.otherActive > 0) {