musubi-sdd 5.1.0 → 5.6.1

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 (232) hide show
  1. package/README.ja.md +106 -48
  2. package/README.md +110 -32
  3. package/bin/musubi-analyze.js +74 -67
  4. package/bin/musubi-browser.js +27 -26
  5. package/bin/musubi-change.js +48 -47
  6. package/bin/musubi-checkpoint.js +10 -7
  7. package/bin/musubi-convert.js +25 -25
  8. package/bin/musubi-costs.js +27 -10
  9. package/bin/musubi-gui.js +52 -46
  10. package/bin/musubi-init.js +1952 -10
  11. package/bin/musubi-orchestrate.js +327 -239
  12. package/bin/musubi-remember.js +69 -56
  13. package/bin/musubi-resolve.js +53 -45
  14. package/bin/musubi-trace.js +51 -22
  15. package/bin/musubi-validate.js +39 -30
  16. package/bin/musubi-workflow.js +33 -34
  17. package/bin/musubi.js +39 -2
  18. package/package.json +1 -1
  19. package/src/agents/agent-loop.js +94 -95
  20. package/src/agents/agentic/code-generator.js +119 -109
  21. package/src/agents/agentic/code-reviewer.js +105 -108
  22. package/src/agents/agentic/index.js +4 -4
  23. package/src/agents/browser/action-executor.js +13 -13
  24. package/src/agents/browser/ai-comparator.js +11 -10
  25. package/src/agents/browser/context-manager.js +6 -6
  26. package/src/agents/browser/index.js +5 -5
  27. package/src/agents/browser/nl-parser.js +31 -46
  28. package/src/agents/browser/screenshot.js +2 -2
  29. package/src/agents/browser/test-generator.js +6 -4
  30. package/src/agents/function-tool.js +71 -65
  31. package/src/agents/index.js +7 -7
  32. package/src/agents/schema-generator.js +98 -94
  33. package/src/analyzers/ast-extractor.js +158 -146
  34. package/src/analyzers/codegraph-auto-update.js +858 -0
  35. package/src/analyzers/complexity-analyzer.js +536 -0
  36. package/src/analyzers/context-optimizer.js +241 -126
  37. package/src/analyzers/impact-analyzer.js +1 -1
  38. package/src/analyzers/large-project-analyzer.js +766 -0
  39. package/src/analyzers/repository-map.js +77 -81
  40. package/src/analyzers/security-analyzer.js +19 -11
  41. package/src/analyzers/stuck-detector.js +19 -17
  42. package/src/converters/index.js +78 -57
  43. package/src/converters/ir/types.js +12 -12
  44. package/src/converters/parsers/musubi-parser.js +134 -126
  45. package/src/converters/parsers/openapi-parser.js +70 -53
  46. package/src/converters/parsers/speckit-parser.js +239 -175
  47. package/src/converters/writers/musubi-writer.js +123 -118
  48. package/src/converters/writers/speckit-writer.js +124 -113
  49. package/src/generators/rust-migration-generator.js +512 -0
  50. package/src/gui/public/index.html +1365 -1211
  51. package/src/gui/server.js +41 -40
  52. package/src/gui/services/file-watcher.js +23 -8
  53. package/src/gui/services/project-scanner.js +26 -20
  54. package/src/gui/services/replanning-service.js +27 -23
  55. package/src/gui/services/traceability-service.js +8 -8
  56. package/src/gui/services/workflow-service.js +14 -7
  57. package/src/index.js +151 -0
  58. package/src/integrations/cicd.js +90 -104
  59. package/src/integrations/codegraph-mcp.js +643 -0
  60. package/src/integrations/documentation.js +142 -103
  61. package/src/integrations/examples.js +95 -80
  62. package/src/integrations/github-client.js +17 -17
  63. package/src/integrations/index.js +5 -5
  64. package/src/integrations/mcp/index.js +21 -21
  65. package/src/integrations/mcp/mcp-context-provider.js +76 -78
  66. package/src/integrations/mcp/mcp-discovery.js +74 -72
  67. package/src/integrations/mcp/mcp-tool-registry.js +99 -94
  68. package/src/integrations/mcp-connector.js +70 -66
  69. package/src/integrations/platforms.js +50 -49
  70. package/src/integrations/tool-discovery.js +37 -31
  71. package/src/llm-providers/anthropic-provider.js +11 -11
  72. package/src/llm-providers/base-provider.js +16 -18
  73. package/src/llm-providers/copilot-provider.js +22 -19
  74. package/src/llm-providers/index.js +26 -25
  75. package/src/llm-providers/ollama-provider.js +11 -11
  76. package/src/llm-providers/openai-provider.js +12 -12
  77. package/src/managers/agent-memory.js +36 -24
  78. package/src/managers/checkpoint-manager.js +4 -8
  79. package/src/managers/delta-spec.js +19 -19
  80. package/src/managers/index.js +13 -4
  81. package/src/managers/memory-condenser.js +35 -45
  82. package/src/managers/repo-skill-manager.js +57 -31
  83. package/src/managers/skill-loader.js +25 -22
  84. package/src/managers/skill-tools.js +36 -72
  85. package/src/managers/workflow.js +30 -22
  86. package/src/monitoring/cost-tracker.js +48 -46
  87. package/src/monitoring/incident-manager.js +116 -106
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +75 -62
  90. package/src/monitoring/quality-dashboard.js +45 -41
  91. package/src/monitoring/release-manager.js +63 -53
  92. package/src/orchestration/agent-skill-binding.js +39 -47
  93. package/src/orchestration/error-handler.js +65 -107
  94. package/src/orchestration/guardrails/base-guardrail.js +26 -24
  95. package/src/orchestration/guardrails/guardrail-rules.js +50 -64
  96. package/src/orchestration/guardrails/index.js +5 -5
  97. package/src/orchestration/guardrails/input-guardrail.js +58 -45
  98. package/src/orchestration/guardrails/output-guardrail.js +104 -81
  99. package/src/orchestration/guardrails/safety-check.js +79 -79
  100. package/src/orchestration/index.js +38 -55
  101. package/src/orchestration/mcp-tool-adapters.js +96 -99
  102. package/src/orchestration/orchestration-engine.js +21 -21
  103. package/src/orchestration/pattern-registry.js +60 -45
  104. package/src/orchestration/patterns/auto.js +34 -47
  105. package/src/orchestration/patterns/group-chat.js +59 -65
  106. package/src/orchestration/patterns/handoff.js +67 -65
  107. package/src/orchestration/patterns/human-in-loop.js +51 -72
  108. package/src/orchestration/patterns/nested.js +25 -40
  109. package/src/orchestration/patterns/sequential.js +35 -34
  110. package/src/orchestration/patterns/swarm.js +63 -56
  111. package/src/orchestration/patterns/triage.js +150 -109
  112. package/src/orchestration/reasoning/index.js +9 -9
  113. package/src/orchestration/reasoning/planning-engine.js +143 -140
  114. package/src/orchestration/reasoning/reasoning-engine.js +206 -144
  115. package/src/orchestration/reasoning/self-correction.js +121 -128
  116. package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
  117. package/src/orchestration/replanning/alternative-generator.js +37 -42
  118. package/src/orchestration/replanning/config.js +63 -59
  119. package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
  120. package/src/orchestration/replanning/index.js +24 -20
  121. package/src/orchestration/replanning/plan-evaluator.js +49 -50
  122. package/src/orchestration/replanning/plan-monitor.js +32 -28
  123. package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
  124. package/src/orchestration/replanning/replan-history.js +33 -26
  125. package/src/orchestration/replanning/replanning-engine.js +106 -108
  126. package/src/orchestration/skill-executor.js +107 -109
  127. package/src/orchestration/skill-registry.js +85 -89
  128. package/src/orchestration/workflow-examples.js +228 -231
  129. package/src/orchestration/workflow-executor.js +65 -68
  130. package/src/orchestration/workflow-orchestrator.js +72 -73
  131. package/src/phase4-integration.js +47 -40
  132. package/src/phase5-integration.js +89 -30
  133. package/src/reporters/coverage-report.js +82 -30
  134. package/src/reporters/hierarchical-reporter.js +498 -0
  135. package/src/reporters/traceability-matrix-report.js +29 -20
  136. package/src/resolvers/issue-resolver.js +43 -31
  137. package/src/steering/advanced-validation.js +133 -124
  138. package/src/steering/auto-updater.js +60 -73
  139. package/src/steering/index.js +6 -6
  140. package/src/steering/quality-metrics.js +41 -35
  141. package/src/steering/steering-auto-update.js +83 -86
  142. package/src/steering/steering-validator.js +98 -106
  143. package/src/steering/template-constraints.js +53 -54
  144. package/src/templates/agents/claude-code/CLAUDE.md +32 -32
  145. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
  146. package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
  147. package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
  148. package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
  149. package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
  150. package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
  151. package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
  152. package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
  153. package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
  154. package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
  155. package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
  156. package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
  157. package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
  158. package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
  159. package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
  160. package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
  161. package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
  162. package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
  163. package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
  164. package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
  165. package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
  166. package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
  167. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
  168. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
  169. package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
  170. package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
  171. package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
  172. package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
  173. package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
  174. package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
  175. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
  176. package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
  177. package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
  178. package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
  179. package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
  180. package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
  181. package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
  182. package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
  183. package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
  184. package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
  185. package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
  186. package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
  187. package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
  188. package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
  189. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
  190. package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
  191. package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
  192. package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
  193. package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
  194. package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
  195. package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
  196. package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
  197. package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
  198. package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
  199. package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
  200. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
  201. package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
  202. package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
  203. package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
  204. package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
  205. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
  206. package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
  207. package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
  208. package/src/templates/agents/codex/AGENTS.md +74 -42
  209. package/src/templates/agents/cursor/AGENTS.md +74 -42
  210. package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
  211. package/src/templates/agents/github-copilot/AGENTS.md +83 -51
  212. package/src/templates/agents/qwen-code/QWEN.md +74 -42
  213. package/src/templates/agents/windsurf/AGENTS.md +74 -42
  214. package/src/templates/architectures/README.md +41 -0
  215. package/src/templates/architectures/clean-architecture/README.md +113 -0
  216. package/src/templates/architectures/event-driven/README.md +162 -0
  217. package/src/templates/architectures/hexagonal/README.md +130 -0
  218. package/src/templates/index.js +6 -1
  219. package/src/templates/locale-manager.js +16 -16
  220. package/src/templates/shared/delta-spec-template.md +20 -13
  221. package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
  222. package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
  223. package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
  224. package/src/templates/shared/steering/structure.md +95 -0
  225. package/src/templates/skills/browser-agent.md +21 -16
  226. package/src/templates/skills/web-gui.md +8 -0
  227. package/src/templates/template-constraints.js +50 -53
  228. package/src/validators/advanced-validation.js +30 -36
  229. package/src/validators/constitutional-validator.js +77 -73
  230. package/src/validators/critic-system.js +49 -59
  231. package/src/validators/delta-format.js +59 -55
  232. package/src/validators/traceability-validator.js +7 -11
package/src/gui/server.js CHANGED
@@ -60,17 +60,17 @@ class GUIServer {
60
60
  */
61
61
  setupReplanningEvents() {
62
62
  // Forward replanning events to WebSocket clients
63
- this.replanningService.on('state:updated', (state) => {
64
- this.broadcast({
65
- type: 'replanning:state',
66
- data: state
63
+ this.replanningService.on('state:updated', state => {
64
+ this.broadcast({
65
+ type: 'replanning:state',
66
+ data: state,
67
67
  });
68
68
  });
69
69
 
70
- this.replanningService.on('replan:recorded', (event) => {
71
- this.broadcast({
72
- type: 'replanning:event',
73
- data: event
70
+ this.replanningService.on('replan:recorded', event => {
71
+ this.broadcast({
72
+ type: 'replanning:event',
73
+ data: event,
74
74
  });
75
75
  });
76
76
  }
@@ -286,9 +286,9 @@ class GUIServer {
286
286
  }
287
287
  try {
288
288
  const { spawn } = require('child_process');
289
- const child = spawn('npx', ['musubi-requirements', 'create'], {
289
+ const _child = spawn('npx', ['musubi-requirements', 'create'], {
290
290
  cwd: this.projectPath,
291
- shell: true
291
+ shell: true,
292
292
  });
293
293
  res.json({ success: true, message: 'Requirements wizard started' });
294
294
  } catch (error) {
@@ -303,20 +303,20 @@ class GUIServer {
303
303
  }
304
304
  try {
305
305
  const { id, title, type, priority, description, feature } = req.body;
306
-
306
+
307
307
  if (!id || !title || !description || !feature) {
308
308
  return res.status(400).json({ error: 'Missing required fields' });
309
309
  }
310
310
 
311
311
  const fs = require('fs').promises;
312
312
  const specsDir = path.join(this.projectPath, 'storage', 'specs');
313
-
313
+
314
314
  // Ensure directory exists
315
315
  await fs.mkdir(specsDir, { recursive: true });
316
-
316
+
317
317
  const filename = `${feature}-requirements.md`;
318
318
  const filepath = path.join(specsDir, filename);
319
-
319
+
320
320
  // Check if file exists
321
321
  let content = '';
322
322
  try {
@@ -346,9 +346,9 @@ ${description}
346
346
 
347
347
  `;
348
348
  content += reqContent;
349
-
349
+
350
350
  await fs.writeFile(filepath, content, 'utf-8');
351
-
351
+
352
352
  res.json({ success: true, file: filepath });
353
353
  } catch (error) {
354
354
  res.status(500).json({ error: error.message });
@@ -359,26 +359,26 @@ ${description}
359
359
  try {
360
360
  const { spawn } = require('child_process');
361
361
  const result = { stdout: '', stderr: '' };
362
-
362
+
363
363
  const child = spawn('node', [path.join(__dirname, '../../bin/musubi-validate.js'), 'all'], {
364
364
  cwd: this.projectPath,
365
365
  shell: false,
366
- env: { ...process.env, FORCE_COLOR: '0' }
366
+ env: { ...process.env, FORCE_COLOR: '0' },
367
367
  });
368
-
369
- child.stdout.on('data', (data) => result.stdout += data.toString());
370
- child.stderr.on('data', (data) => result.stderr += data.toString());
371
-
372
- child.on('error', (error) => {
368
+
369
+ child.stdout.on('data', data => (result.stdout += data.toString()));
370
+ child.stderr.on('data', data => (result.stderr += data.toString()));
371
+
372
+ child.on('error', error => {
373
373
  res.status(500).json({ success: false, error: error.message });
374
374
  });
375
-
376
- child.on('close', (code) => {
377
- res.json({
378
- success: code === 0,
379
- output: result.stdout || 'Validation completed',
375
+
376
+ child.on('close', code => {
377
+ res.json({
378
+ success: code === 0,
379
+ output: result.stdout || 'Validation completed',
380
380
  errors: result.stderr,
381
- exitCode: code
381
+ exitCode: code,
382
382
  });
383
383
  });
384
384
  } catch (error) {
@@ -412,20 +412,20 @@ ${description}
412
412
  setupWebSocket() {
413
413
  this.wss = new WebSocketServer({ server: this.httpServer });
414
414
 
415
- this.wss.on('connection', (ws) => {
415
+ this.wss.on('connection', ws => {
416
416
  this.clients.add(ws);
417
417
 
418
418
  ws.on('close', () => {
419
419
  this.clients.delete(ws);
420
420
  });
421
421
 
422
- ws.on('error', (error) => {
422
+ ws.on('error', error => {
423
423
  console.error('WebSocket error:', error);
424
424
  this.clients.delete(ws);
425
425
  });
426
426
 
427
427
  // Send initial project state
428
- this.projectScanner.scan().then((project) => {
428
+ this.projectScanner.scan().then(project => {
429
429
  ws.send(JSON.stringify({ type: 'project:init', data: project }));
430
430
  });
431
431
  });
@@ -438,7 +438,8 @@ ${description}
438
438
  broadcast(message) {
439
439
  const data = JSON.stringify(message);
440
440
  for (const client of this.clients) {
441
- if (client.readyState === 1) { // WebSocket.OPEN
441
+ if (client.readyState === 1) {
442
+ // WebSocket.OPEN
442
443
  client.send(data);
443
444
  }
444
445
  }
@@ -449,10 +450,10 @@ ${description}
449
450
  */
450
451
  setupFileWatcher() {
451
452
  this.fileWatcher = new FileWatcher(this.projectPath, {
452
- ignored: /(^|[\/\\])\.|node_modules/,
453
+ ignored: /(^|[/\\])\.|node_modules/,
453
454
  });
454
455
 
455
- this.fileWatcher.on('change', async (filePath) => {
456
+ this.fileWatcher.on('change', async filePath => {
456
457
  try {
457
458
  const project = await this.projectScanner.scan();
458
459
  this.broadcast({ type: 'file:changed', data: { path: filePath, project } });
@@ -461,7 +462,7 @@ ${description}
461
462
  }
462
463
  });
463
464
 
464
- this.fileWatcher.on('add', async (filePath) => {
465
+ this.fileWatcher.on('add', async filePath => {
465
466
  try {
466
467
  const project = await this.projectScanner.scan();
467
468
  this.broadcast({ type: 'file:added', data: { path: filePath, project } });
@@ -470,7 +471,7 @@ ${description}
470
471
  }
471
472
  });
472
473
 
473
- this.fileWatcher.on('unlink', async (filePath) => {
474
+ this.fileWatcher.on('unlink', async filePath => {
474
475
  try {
475
476
  const project = await this.projectScanner.scan();
476
477
  this.broadcast({ type: 'file:removed', data: { path: filePath, project } });
@@ -487,11 +488,11 @@ ${description}
487
488
  async start() {
488
489
  return new Promise((resolve, reject) => {
489
490
  this.httpServer = http.createServer(this.app);
490
-
491
+
491
492
  this.setupWebSocket();
492
493
  this.setupFileWatcher();
493
494
 
494
- this.httpServer.listen(this.port, (err) => {
495
+ this.httpServer.listen(this.port, err => {
495
496
  if (err) {
496
497
  reject(err);
497
498
  return;
@@ -537,7 +538,7 @@ ${description}
537
538
  }
538
539
 
539
540
  if (this.httpServer) {
540
- return new Promise((resolve) => {
541
+ return new Promise(resolve => {
541
542
  this.httpServer.close(resolve);
542
543
  });
543
544
  }
@@ -6,6 +6,7 @@
6
6
  const chokidar = require('chokidar');
7
7
  const EventEmitter = require('events');
8
8
  const path = require('path');
9
+ const fs = require('fs');
9
10
 
10
11
  /**
11
12
  * File Watcher - Watches for file changes in the project
@@ -18,10 +19,10 @@ class FileWatcher extends EventEmitter {
18
19
  */
19
20
  constructor(projectPath, options = {}) {
20
21
  super();
21
-
22
+
22
23
  this.projectPath = projectPath;
23
24
  this.options = {
24
- ignored: options.ignored || /(^|[\/\\])\.|node_modules/,
25
+ ignored: options.ignored || /(^|[/\\])\.|node_modules/,
25
26
  persistent: true,
26
27
  ignoreInitial: true,
27
28
  awaitWriteFinish: {
@@ -42,26 +43,40 @@ class FileWatcher extends EventEmitter {
42
43
  * Start watching
43
44
  */
44
45
  start() {
45
- const watchPaths = [
46
+ const potentialPaths = [
46
47
  path.join(this.projectPath, 'steering'),
47
48
  path.join(this.projectPath, 'storage'),
48
49
  ];
49
50
 
51
+ // Only watch paths that exist to avoid ENOENT errors
52
+ const watchPaths = potentialPaths.filter(p => {
53
+ try {
54
+ return fs.existsSync(p);
55
+ } catch {
56
+ return false;
57
+ }
58
+ });
59
+
60
+ // If no paths exist, watch the project path itself
61
+ if (watchPaths.length === 0) {
62
+ watchPaths.push(this.projectPath);
63
+ }
64
+
50
65
  this.watcher = chokidar.watch(watchPaths, this.options);
51
66
 
52
- this.watcher.on('change', (filePath) => {
67
+ this.watcher.on('change', filePath => {
53
68
  this.debounce('change', filePath);
54
69
  });
55
70
 
56
- this.watcher.on('add', (filePath) => {
71
+ this.watcher.on('add', filePath => {
57
72
  this.debounce('add', filePath);
58
73
  });
59
74
 
60
- this.watcher.on('unlink', (filePath) => {
75
+ this.watcher.on('unlink', filePath => {
61
76
  this.debounce('unlink', filePath);
62
77
  });
63
78
 
64
- this.watcher.on('error', (error) => {
79
+ this.watcher.on('error', error => {
65
80
  this.emit('error', error);
66
81
  });
67
82
 
@@ -77,7 +92,7 @@ class FileWatcher extends EventEmitter {
77
92
  */
78
93
  debounce(event, filePath) {
79
94
  const key = `${event}:${filePath}`;
80
-
95
+
81
96
  if (this.debounceTimers.has(key)) {
82
97
  clearTimeout(this.debounceTimers.get(key));
83
98
  }
@@ -83,8 +83,8 @@ class ProjectScanner {
83
83
  */
84
84
  async getSteering() {
85
85
  const steeringPath = path.join(this.projectPath, 'steering');
86
-
87
- if (!await fs.pathExists(steeringPath)) {
86
+
87
+ if (!(await fs.pathExists(steeringPath))) {
88
88
  return null;
89
89
  }
90
90
 
@@ -104,8 +104,8 @@ class ProjectScanner {
104
104
  */
105
105
  async getConstitution() {
106
106
  const constitutionPath = path.join(this.projectPath, 'steering', 'rules', 'constitution.md');
107
-
108
- if (!await fs.pathExists(constitutionPath)) {
107
+
108
+ if (!(await fs.pathExists(constitutionPath))) {
109
109
  return null;
110
110
  }
111
111
 
@@ -146,8 +146,8 @@ class ProjectScanner {
146
146
  */
147
147
  async getSpecs() {
148
148
  const specsPath = path.join(this.projectPath, 'storage', 'specs');
149
-
150
- if (!await fs.pathExists(specsPath)) {
149
+
150
+ if (!(await fs.pathExists(specsPath))) {
151
151
  return [];
152
152
  }
153
153
 
@@ -174,6 +174,11 @@ class ProjectScanner {
174
174
  */
175
175
  async getSpec(id) {
176
176
  const specsPath = path.join(this.projectPath, 'storage', 'specs');
177
+
178
+ if (!(await fs.pathExists(specsPath))) {
179
+ return null;
180
+ }
181
+
177
182
  const files = await fs.readdir(specsPath);
178
183
 
179
184
  for (const file of files) {
@@ -181,7 +186,7 @@ class ProjectScanner {
181
186
 
182
187
  const filePath = path.join(specsPath, file);
183
188
  const spec = await this.parseSpec(filePath);
184
-
189
+
185
190
  if (spec && spec.id === id) {
186
191
  return spec;
187
192
  }
@@ -232,16 +237,17 @@ class ProjectScanner {
232
237
  */
233
238
  parseRequirements(content) {
234
239
  const requirements = [];
235
- const reqRegex = /#{2,3}\s+(REQ[-\w]+)[:\s]*(.+?)(?=\n#{2,3}\s+REQ|\n#{1,2}\s+[^#]|\n#{1,2}$|$)/gs;
240
+ const reqRegex =
241
+ /#{2,3}\s+(REQ[-\w]+)[:\s]*(.+?)(?=\n#{2,3}\s+REQ|\n#{1,2}\s+[^#]|\n#{1,2}$|$)/gs;
236
242
 
237
243
  let match;
238
244
  while ((match = reqRegex.exec(content)) !== null) {
239
245
  const id = match[1];
240
246
  const body = match[2].trim();
241
-
247
+
242
248
  // Try to extract EARS pattern
243
249
  const pattern = this.detectEARSPattern(body);
244
-
250
+
245
251
  requirements.push({
246
252
  id,
247
253
  body,
@@ -387,8 +393,8 @@ class ProjectScanner {
387
393
  */
388
394
  async getWorkflow() {
389
395
  const workflowPath = path.join(this.projectPath, 'steering', 'rules', 'workflow.md');
390
-
391
- if (!await fs.pathExists(workflowPath)) {
396
+
397
+ if (!(await fs.pathExists(workflowPath))) {
392
398
  // Return default workflow stages
393
399
  return {
394
400
  stages: [
@@ -427,7 +433,7 @@ class ProjectScanner {
427
433
  name: match[2].trim(),
428
434
  status,
429
435
  });
430
-
436
+
431
437
  if (status === 'active') {
432
438
  currentStage = parseInt(match[1], 10);
433
439
  }
@@ -448,7 +454,7 @@ class ProjectScanner {
448
454
  };
449
455
 
450
456
  // Check for steering directory
451
- if (!await this.hasSteering()) {
457
+ if (!(await this.hasSteering())) {
452
458
  results.errors.push({
453
459
  type: 'missing-steering',
454
460
  message: 'steering/ directory not found',
@@ -495,13 +501,13 @@ class ProjectScanner {
495
501
  */
496
502
  async readMarkdownFile(filePath) {
497
503
  try {
498
- if (!await fs.pathExists(filePath)) {
504
+ if (!(await fs.pathExists(filePath))) {
499
505
  return null;
500
506
  }
501
-
507
+
502
508
  const content = await fs.readFile(filePath, 'utf-8');
503
509
  const { data, content: body } = matter(content);
504
-
510
+
505
511
  return {
506
512
  path: filePath,
507
513
  metadata: data,
@@ -519,13 +525,13 @@ class ProjectScanner {
519
525
  */
520
526
  async readYamlFile(filePath) {
521
527
  try {
522
- if (!await fs.pathExists(filePath)) {
528
+ if (!(await fs.pathExists(filePath))) {
523
529
  return null;
524
530
  }
525
-
531
+
526
532
  const yaml = require('js-yaml');
527
533
  const content = await fs.readFile(filePath, 'utf-8');
528
-
534
+
529
535
  return {
530
536
  path: filePath,
531
537
  data: yaml.load(content),
@@ -35,7 +35,7 @@ class ReplanningService extends EventEmitter {
35
35
  super();
36
36
  this.projectPath = projectPath;
37
37
  this.storagePath = path.join(projectPath, 'storage', 'replanning');
38
-
38
+
39
39
  // In-memory state (will be populated from storage or engine)
40
40
  this.state = {
41
41
  status: 'idle',
@@ -49,8 +49,8 @@ class ReplanningService extends EventEmitter {
49
49
  successfulReplans: 0,
50
50
  averageConfidence: 0,
51
51
  goalCompletionRate: 0,
52
- optimizationsApplied: 0
53
- }
52
+ optimizationsApplied: 0,
53
+ },
54
54
  };
55
55
  }
56
56
 
@@ -106,10 +106,13 @@ class ReplanningService extends EventEmitter {
106
106
  try {
107
107
  const historyDir = path.join(this.storagePath, 'history');
108
108
  await fs.mkdir(historyDir, { recursive: true });
109
-
109
+
110
110
  const files = await fs.readdir(historyDir);
111
- const jsonFiles = files.filter(f => f.endsWith('.json')).sort().reverse();
112
-
111
+ const jsonFiles = files
112
+ .filter(f => f.endsWith('.json'))
113
+ .sort()
114
+ .reverse();
115
+
113
116
  this.state.history = [];
114
117
  for (const file of jsonFiles.slice(0, 50)) {
115
118
  try {
@@ -148,7 +151,7 @@ class ReplanningService extends EventEmitter {
148
151
  overallProgress: 0,
149
152
  activeGoals: 0,
150
153
  completedGoals: 0,
151
- milestones: []
154
+ milestones: [],
152
155
  };
153
156
  }
154
157
  }
@@ -168,7 +171,7 @@ class ReplanningService extends EventEmitter {
168
171
  currentPath: null,
169
172
  optimizedPath: null,
170
173
  potentialSavings: 0,
171
- suggestions: []
174
+ suggestions: [],
172
175
  };
173
176
  }
174
177
  }
@@ -214,26 +217,26 @@ class ReplanningService extends EventEmitter {
214
217
  async recordReplan(event) {
215
218
  const historyDir = path.join(this.storagePath, 'history');
216
219
  await fs.mkdir(historyDir, { recursive: true });
217
-
220
+
218
221
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
219
222
  const filename = `replan-${timestamp}.json`;
220
-
223
+
221
224
  const entry = {
222
225
  id: `replan-${Date.now()}`,
223
226
  timestamp: new Date().toISOString(),
224
- ...event
227
+ ...event,
225
228
  };
226
-
229
+
227
230
  await fs.writeFile(path.join(historyDir, filename), JSON.stringify(entry, null, 2));
228
-
231
+
229
232
  this.state.history.unshift(entry);
230
233
  this.state.lastReplan = entry;
231
234
  this.state.metrics.totalReplans++;
232
-
235
+
233
236
  if (event.success) {
234
237
  this.state.metrics.successfulReplans++;
235
238
  }
236
-
239
+
237
240
  await this.saveState();
238
241
  this.emit('replan:recorded', entry);
239
242
  }
@@ -247,7 +250,7 @@ class ReplanningService extends EventEmitter {
247
250
  this.getState(),
248
251
  this.getGoalProgress(),
249
252
  this.getPathOptimization(),
250
- this.getMetrics()
253
+ this.getMetrics(),
251
254
  ]);
252
255
 
253
256
  return {
@@ -255,20 +258,21 @@ class ReplanningService extends EventEmitter {
255
258
  goalProgress: {
256
259
  overall: goalProgress.overallProgress || 0,
257
260
  active: goalProgress.activeGoals || 0,
258
- completed: goalProgress.completedGoals || 0
261
+ completed: goalProgress.completedGoals || 0,
259
262
  },
260
263
  optimization: {
261
264
  status: pathOptimization.status,
262
- suggestions: pathOptimization.suggestions?.length || 0
265
+ suggestions: pathOptimization.suggestions?.length || 0,
263
266
  },
264
267
  metrics: {
265
268
  totalReplans: metrics.totalReplans || 0,
266
- successRate: metrics.totalReplans > 0
267
- ? Math.round((metrics.successfulReplans / metrics.totalReplans) * 100)
268
- : 100,
269
- optimizationsApplied: metrics.optimizationsApplied || 0
269
+ successRate:
270
+ metrics.totalReplans > 0
271
+ ? Math.round((metrics.successfulReplans / metrics.totalReplans) * 100)
272
+ : 100,
273
+ optimizationsApplied: metrics.optimizationsApplied || 0,
270
274
  },
271
- recentHistory: state.history.slice(0, 5)
275
+ recentHistory: state.history.slice(0, 5),
272
276
  };
273
277
  }
274
278
  }
@@ -132,7 +132,7 @@ class TraceabilityService {
132
132
 
133
133
  try {
134
134
  const files = await fs.readdir(specsPath);
135
- const reqFiles = files.filter((f) => f.endsWith('-requirements.md'));
135
+ const reqFiles = files.filter(f => f.endsWith('-requirements.md'));
136
136
 
137
137
  for (const file of reqFiles) {
138
138
  const filePath = path.join(specsPath, file);
@@ -156,7 +156,7 @@ class TraceabilityService {
156
156
  // Also check frontmatter for requirements list
157
157
  if (frontmatter.requirements) {
158
158
  for (const req of frontmatter.requirements) {
159
- if (!requirements.find((r) => r.id === req.id)) {
159
+ if (!requirements.find(r => r.id === req.id)) {
160
160
  requirements.push({
161
161
  id: req.id,
162
162
  title: req.title || req.id,
@@ -185,7 +185,7 @@ class TraceabilityService {
185
185
 
186
186
  try {
187
187
  const files = await fs.readdir(specsPath);
188
- const designFiles = files.filter((f) => f.endsWith('-design.md'));
188
+ const designFiles = files.filter(f => f.endsWith('-design.md'));
189
189
 
190
190
  for (const file of designFiles) {
191
191
  const filePath = path.join(specsPath, file);
@@ -216,7 +216,7 @@ class TraceabilityService {
216
216
 
217
217
  try {
218
218
  const files = await fs.readdir(specsPath);
219
- const taskFiles = files.filter((f) => f.endsWith('-tasks.md'));
219
+ const taskFiles = files.filter(f => f.endsWith('-tasks.md'));
220
220
 
221
221
  for (const file of taskFiles) {
222
222
  const filePath = path.join(specsPath, file);
@@ -255,10 +255,10 @@ class TraceabilityService {
255
255
  const implementations = [];
256
256
 
257
257
  try {
258
- await this.walkDirectory(srcPath, async (filePath) => {
258
+ await this.walkDirectory(srcPath, async filePath => {
259
259
  if (filePath.endsWith('.js') || filePath.endsWith('.ts')) {
260
260
  const content = await fs.readFile(filePath, 'utf-8');
261
-
261
+
262
262
  // Look for @implements or @requirement tags
263
263
  const tagRegex = /@(?:implements|requirement)\s+(REQ-\d+[^\s]*)/g;
264
264
  let match;
@@ -292,10 +292,10 @@ class TraceabilityService {
292
292
  async walkDirectory(dir, callback) {
293
293
  try {
294
294
  const entries = await fs.readdir(dir, { withFileTypes: true });
295
-
295
+
296
296
  for (const entry of entries) {
297
297
  const fullPath = path.join(dir, entry.name);
298
-
298
+
299
299
  if (entry.isDirectory()) {
300
300
  if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
301
301
  await this.walkDirectory(fullPath, callback);
@@ -46,7 +46,7 @@ class WorkflowService {
46
46
  // Check steering
47
47
  const steeringPath = path.join(this.projectPath, 'steering');
48
48
  const constitutionPath = path.join(steeringPath, 'rules', 'constitution.md');
49
-
49
+
50
50
  try {
51
51
  await fs.access(constitutionPath);
52
52
  state.completedStages.push('steering');
@@ -61,7 +61,12 @@ class WorkflowService {
61
61
  }
62
62
 
63
63
  // Check requirements
64
- const requirementsPath = path.join(this.projectPath, 'storage', 'specs', `${featureId}-requirements.md`);
64
+ const requirementsPath = path.join(
65
+ this.projectPath,
66
+ 'storage',
67
+ 'specs',
68
+ `${featureId}-requirements.md`
69
+ );
65
70
  if (await this.fileExists(requirementsPath)) {
66
71
  state.completedStages.push('requirements');
67
72
  state.currentStage = 'design';
@@ -100,7 +105,7 @@ class WorkflowService {
100
105
 
101
106
  try {
102
107
  const files = await fs.readdir(specsPath);
103
-
108
+
104
109
  for (const file of files) {
105
110
  const match = file.match(/^(.+)-(requirements|design|tasks)\.md$/);
106
111
  if (match) {
@@ -133,7 +138,9 @@ class WorkflowService {
133
138
  }
134
139
 
135
140
  if (targetIndex > currentIndex + 1) {
136
- throw new Error(`Cannot skip stages. Current: ${currentState.currentStage}, Target: ${targetStage}`);
141
+ throw new Error(
142
+ `Cannot skip stages. Current: ${currentState.currentStage}, Target: ${targetStage}`
143
+ );
137
144
  }
138
145
 
139
146
  // The actual transition is done by creating the artifact
@@ -190,7 +197,7 @@ class WorkflowService {
190
197
 
191
198
  return {
192
199
  featureId,
193
- valid: issues.filter((i) => i.type === 'error').length === 0,
200
+ valid: issues.filter(i => i.type === 'error').length === 0,
194
201
  issues,
195
202
  state,
196
203
  };
@@ -219,11 +226,11 @@ class WorkflowService {
219
226
  try {
220
227
  const content = await fs.readFile(filePath, 'utf-8');
221
228
  const { data: frontmatter, content: body } = matter(content);
222
-
229
+
223
230
  const sections = [];
224
231
  const sectionRegex = /^##\s+(.+)$/gm;
225
232
  let match;
226
-
233
+
227
234
  while ((match = sectionRegex.exec(body)) !== null) {
228
235
  sections.push(match[1]);
229
236
  }