musubi-sdd 5.0.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 +164 -145
  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 +247 -125
  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 +83 -80
  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 +53 -44
  87. package/src/monitoring/incident-manager.js +123 -103
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +82 -59
  90. package/src/monitoring/quality-dashboard.js +51 -39
  91. package/src/monitoring/release-manager.js +70 -50
  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
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * MUSUBI Stuck Detector
3
- *
3
+ *
4
4
  * AIエージェントのスタック状態(無限ループ、繰り返しエラー)を検出
5
- *
5
+ *
6
6
  * @module src/analyzers/stuck-detector
7
7
  * @see REQ-P0-B001
8
8
  * @inspired-by OpenHands openhands/controller/stuck.py
@@ -52,7 +52,7 @@ const Severity = {
52
52
 
53
53
  /**
54
54
  * イベントのハッシュを生成
55
- * @param {Object} event
55
+ * @param {Object} event
56
56
  * @returns {string}
57
57
  */
58
58
  function hashEvent(event) {
@@ -131,7 +131,7 @@ class StuckDetector {
131
131
  this.maxMonologueSteps = options.maxMonologueSteps || 10;
132
132
  this.maxContextErrors = options.maxContextErrors || 3;
133
133
  this.maxStageOscillations = options.maxStageOscillations || 3;
134
-
134
+
135
135
  this.history = [];
136
136
  this.stuckAnalysis = null;
137
137
  }
@@ -252,11 +252,11 @@ class StuckDetector {
252
252
 
253
253
  const lastN = this.history.slice(-this.maxRepeatErrors);
254
254
  const allErrors = lastN.every(e => e.type === EventType.ERROR);
255
-
255
+
256
256
  if (allErrors) {
257
257
  const firstHash = lastN[0].hash;
258
258
  const sameError = lastN.every(e => e.hash === firstHash);
259
-
259
+
260
260
  if (sameError) {
261
261
  return new StuckAnalysis({
262
262
  loopType: LoopType.ERROR_LOOP,
@@ -282,10 +282,11 @@ class StuckDetector {
282
282
  }
283
283
 
284
284
  const lastN = this.history.slice(-this.maxMonologueSteps);
285
- const allMessages = lastN.every(e =>
286
- e.type === EventType.MESSAGE &&
287
- !e.content.includes('```') && // コードブロックなし
288
- e.content.length < 500 // 短いメッセージ
285
+ const allMessages = lastN.every(
286
+ e =>
287
+ e.type === EventType.MESSAGE &&
288
+ !e.content.includes('```') && // コードブロックなし
289
+ e.content.length < 500 // 短いメッセージ
289
290
  );
290
291
 
291
292
  if (allMessages) {
@@ -320,11 +321,12 @@ class StuckDetector {
320
321
  ];
321
322
 
322
323
  const lastN = this.history.slice(-this.maxContextErrors);
323
- const allContextErrors = lastN.every(e =>
324
- e.type === EventType.ERROR &&
325
- contextErrorPatterns.some(pattern =>
326
- e.content.toLowerCase().includes(pattern.toLowerCase())
327
- )
324
+ const allContextErrors = lastN.every(
325
+ e =>
326
+ e.type === EventType.ERROR &&
327
+ contextErrorPatterns.some(pattern =>
328
+ e.content.toLowerCase().includes(pattern.toLowerCase())
329
+ )
328
330
  );
329
331
 
330
332
  if (allContextErrors) {
@@ -353,7 +355,7 @@ class StuckDetector {
353
355
 
354
356
  const lastN = this.history.slice(-minEvents);
355
357
  const stages = lastN.map(e => e.stage);
356
-
358
+
357
359
  // 2つのステージ間を往復しているかチェック
358
360
  const uniqueStages = [...new Set(stages)];
359
361
  if (uniqueStages.length !== 2) {
@@ -383,7 +385,7 @@ class StuckDetector {
383
385
 
384
386
  /**
385
387
  * 代替アプローチを提案
386
- * @param {string} loopType
388
+ * @param {string} loopType
387
389
  * @returns {string[]}
388
390
  */
389
391
  _suggestAlternatives(loopType) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Converters Module
3
- *
3
+ *
4
4
  * Cross-format conversion between MUSUBI and Spec Kit
5
5
  */
6
6
 
@@ -20,21 +20,27 @@ const irTypes = require('./ir/types');
20
20
  * @returns {Promise<{filesConverted: number, warnings: string[], outputPath: string}>}
21
21
  */
22
22
  async function convertFromSpeckit(sourcePath, options = {}) {
23
- const { output = '.', dryRun = false, force = false, verbose = false, preserveRaw = false } = options;
24
-
23
+ const {
24
+ output = '.',
25
+ dryRun = false,
26
+ force = false,
27
+ verbose = false,
28
+ preserveRaw = false,
29
+ } = options;
30
+
25
31
  if (verbose) console.log(`Converting Spec Kit project from: ${sourcePath}`);
26
-
32
+
27
33
  // Parse Spec Kit project to IR
28
34
  const ir = await parseSpeckitProject(sourcePath);
29
-
35
+
30
36
  if (verbose) {
31
37
  console.log(` Found ${ir.features.length} features`);
32
38
  console.log(` Found ${ir.constitution.articles.length} constitution articles`);
33
39
  }
34
-
40
+
35
41
  // Write to MUSUBI format
36
42
  const result = await writeMusubiProject(ir, output, { dryRun, force, preserveRaw, verbose });
37
-
43
+
38
44
  return {
39
45
  filesConverted: result.filesWritten,
40
46
  warnings: result.warnings,
@@ -48,28 +54,33 @@ async function convertFromSpeckit(sourcePath, options = {}) {
48
54
  * @returns {Promise<{filesConverted: number, warnings: string[], outputPath: string}>}
49
55
  */
50
56
  async function convertToSpeckit(options = {}) {
51
- const {
52
- source = '.',
53
- output = './.specify',
54
- dryRun = false,
55
- force = false,
56
- verbose = false,
57
- preserveRaw = false
57
+ const {
58
+ source = '.',
59
+ output = './.specify',
60
+ dryRun = false,
61
+ force = false,
62
+ verbose = false,
63
+ preserveRaw = false,
58
64
  } = options;
59
-
65
+
60
66
  if (verbose) console.log(`Converting MUSUBI project to Spec Kit format`);
61
-
67
+
62
68
  // Parse MUSUBI project to IR
63
69
  const ir = await parseMusubiProject(source);
64
-
70
+
65
71
  if (verbose) {
66
72
  console.log(` Found ${ir.features.length} features`);
67
73
  console.log(` Found ${ir.constitution.articles.length} constitution articles`);
68
74
  }
69
-
75
+
70
76
  // Write to Spec Kit format
71
- const result = await writeSpeckitProject(ir, output.replace('/.specify', ''), { dryRun, force, preserveRaw, verbose });
72
-
77
+ const result = await writeSpeckitProject(ir, output.replace('/.specify', ''), {
78
+ dryRun,
79
+ force,
80
+ preserveRaw,
81
+ verbose,
82
+ });
83
+
73
84
  return {
74
85
  filesConverted: result.filesWritten,
75
86
  warnings: result.warnings,
@@ -86,7 +97,7 @@ async function convertToSpeckit(options = {}) {
86
97
  async function validateFormat(format, projectPath) {
87
98
  const errors = [];
88
99
  const warnings = [];
89
-
100
+
90
101
  try {
91
102
  if (format === 'speckit') {
92
103
  await parseSpeckitProject(projectPath);
@@ -98,7 +109,7 @@ async function validateFormat(format, projectPath) {
98
109
  } catch (error) {
99
110
  errors.push(error.message);
100
111
  }
101
-
112
+
102
113
  return {
103
114
  valid: errors.length === 0,
104
115
  errors,
@@ -115,36 +126,38 @@ async function validateFormat(format, projectPath) {
115
126
  async function testRoundtrip(projectPath, options = {}) {
116
127
  const { verbose = false } = options;
117
128
  const differences = [];
118
-
129
+
119
130
  try {
120
131
  // Detect format
121
132
  const fs = require('fs-extra');
122
133
  const path = require('path');
123
-
134
+
124
135
  const isSpeckit = await fs.pathExists(path.join(projectPath, '.specify'));
125
136
  const isMusubi = await fs.pathExists(path.join(projectPath, 'steering'));
126
-
137
+
127
138
  if (!isSpeckit && !isMusubi) {
128
139
  return {
129
140
  passed: false,
130
141
  similarity: 0,
131
- differences: ['Could not detect project format (neither .specify nor steering directory found)'],
142
+ differences: [
143
+ 'Could not detect project format (neither .specify nor steering directory found)',
144
+ ],
132
145
  };
133
146
  }
134
-
147
+
135
148
  if (verbose) {
136
149
  console.log(`Detected format: ${isSpeckit ? 'Spec Kit' : 'MUSUBI'}`);
137
150
  }
138
-
151
+
139
152
  // Parse original
140
- const originalIR = isSpeckit
153
+ const originalIR = isSpeckit
141
154
  ? await parseSpeckitProject(projectPath)
142
155
  : await parseMusubiProject(projectPath);
143
-
156
+
144
157
  // Convert to other format (in memory)
145
158
  const tempDir = path.join(projectPath, '.roundtrip-temp');
146
159
  await fs.ensureDir(tempDir);
147
-
160
+
148
161
  try {
149
162
  // Write to other format
150
163
  if (isSpeckit) {
@@ -152,34 +165,34 @@ async function testRoundtrip(projectPath, options = {}) {
152
165
  } else {
153
166
  await writeSpeckitProject(originalIR, tempDir, { force: true });
154
167
  }
155
-
168
+
156
169
  // Parse converted
157
170
  const convertedIR = isSpeckit
158
171
  ? await parseMusubiProject(tempDir)
159
172
  : await parseSpeckitProject(tempDir);
160
-
173
+
161
174
  // Write back to original format
162
175
  const tempDir2 = path.join(projectPath, '.roundtrip-temp2');
163
176
  await fs.ensureDir(tempDir2);
164
-
177
+
165
178
  if (isSpeckit) {
166
179
  await writeSpeckitProject(convertedIR, tempDir2, { force: true });
167
180
  } else {
168
181
  await writeMusubiProject(convertedIR, tempDir2, { force: true });
169
182
  }
170
-
183
+
171
184
  // Parse roundtrip result
172
185
  const roundtripIR = isSpeckit
173
186
  ? await parseSpeckitProject(tempDir2)
174
187
  : await parseMusubiProject(tempDir2);
175
-
188
+
176
189
  // Compare
177
190
  const similarity = compareIR(originalIR, roundtripIR, differences);
178
-
191
+
179
192
  // Cleanup
180
193
  await fs.remove(tempDir);
181
194
  await fs.remove(tempDir2);
182
-
195
+
183
196
  return {
184
197
  passed: similarity >= 90,
185
198
  similarity,
@@ -206,27 +219,33 @@ async function testRoundtrip(projectPath, options = {}) {
206
219
  * @returns {Promise<{featuresCreated: number, requirementsCreated: number, warnings: string[], outputPath: string}>}
207
220
  */
208
221
  async function convertFromOpenAPI(specPath, options = {}) {
209
- const { output = '.', dryRun = false, force = false, verbose = false, featureName } = options;
210
-
222
+ const {
223
+ output = '.',
224
+ dryRun = false,
225
+ force = false,
226
+ verbose = false,
227
+ featureName: _featureName,
228
+ } = options;
229
+
211
230
  if (verbose) console.log(`Converting OpenAPI spec from: ${specPath}`);
212
-
231
+
213
232
  // Parse OpenAPI spec to IR
214
233
  const ir = await parseOpenAPISpec(specPath);
215
-
234
+
216
235
  // Count requirements
217
236
  let requirementsCreated = 0;
218
237
  for (const feature of ir.features) {
219
238
  requirementsCreated += feature.requirements?.length || 0;
220
239
  }
221
-
240
+
222
241
  if (verbose) {
223
242
  console.log(` Found ${ir.features.length} features`);
224
243
  console.log(` Found ${requirementsCreated} requirements`);
225
244
  }
226
-
245
+
227
246
  // Write to MUSUBI format
228
247
  const result = await writeMusubiProject(ir, output, { dryRun, force, verbose });
229
-
248
+
230
249
  return {
231
250
  featuresCreated: ir.features.length,
232
251
  requirementsCreated,
@@ -237,15 +256,15 @@ async function convertFromOpenAPI(specPath, options = {}) {
237
256
 
238
257
  /**
239
258
  * Compare two IR structures and return similarity percentage
240
- * @param {import('./ir/types').ProjectIR} original
241
- * @param {import('./ir/types').ProjectIR} roundtrip
242
- * @param {string[]} differences
259
+ * @param {import('./ir/types').ProjectIR} original
260
+ * @param {import('./ir/types').ProjectIR} roundtrip
261
+ * @param {string[]} differences
243
262
  * @returns {number} Similarity percentage (0-100)
244
263
  */
245
264
  function compareIR(original, roundtrip, differences) {
246
265
  let matches = 0;
247
266
  let total = 0;
248
-
267
+
249
268
  // Compare metadata
250
269
  total++;
251
270
  if (original.metadata.name === roundtrip.metadata.name) {
@@ -253,20 +272,22 @@ function compareIR(original, roundtrip, differences) {
253
272
  } else {
254
273
  differences.push(`Name mismatch: "${original.metadata.name}" vs "${roundtrip.metadata.name}"`);
255
274
  }
256
-
275
+
257
276
  // Compare features count
258
277
  total++;
259
278
  if (original.features.length === roundtrip.features.length) {
260
279
  matches++;
261
280
  } else {
262
- differences.push(`Feature count mismatch: ${original.features.length} vs ${roundtrip.features.length}`);
281
+ differences.push(
282
+ `Feature count mismatch: ${original.features.length} vs ${roundtrip.features.length}`
283
+ );
263
284
  }
264
-
285
+
265
286
  // Compare each feature
266
287
  for (let i = 0; i < Math.min(original.features.length, roundtrip.features.length); i++) {
267
288
  const origFeature = original.features[i];
268
289
  const rtFeature = roundtrip.features[i];
269
-
290
+
270
291
  // Compare feature name
271
292
  total++;
272
293
  if (origFeature.name === rtFeature.name) {
@@ -274,7 +295,7 @@ function compareIR(original, roundtrip, differences) {
274
295
  } else {
275
296
  differences.push(`Feature ${i} name mismatch: "${origFeature.name}" vs "${rtFeature.name}"`);
276
297
  }
277
-
298
+
278
299
  // Compare requirements count
279
300
  total++;
280
301
  const origReqs = origFeature.specification?.requirements?.length || 0;
@@ -284,7 +305,7 @@ function compareIR(original, roundtrip, differences) {
284
305
  } else {
285
306
  differences.push(`Feature ${i} requirements count mismatch: ${origReqs} vs ${rtReqs}`);
286
307
  }
287
-
308
+
288
309
  // Compare tasks count
289
310
  total++;
290
311
  const origTasks = origFeature.tasks?.length || 0;
@@ -295,7 +316,7 @@ function compareIR(original, roundtrip, differences) {
295
316
  differences.push(`Feature ${i} tasks count mismatch: ${origTasks} vs ${rtTasks}`);
296
317
  }
297
318
  }
298
-
319
+
299
320
  // Compare constitution articles
300
321
  total++;
301
322
  const origArticles = original.constitution?.articles?.length || 0;
@@ -305,7 +326,7 @@ function compareIR(original, roundtrip, differences) {
305
326
  } else {
306
327
  differences.push(`Constitution articles count mismatch: ${origArticles} vs ${rtArticles}`);
307
328
  }
308
-
329
+
309
330
  return Math.round((matches / total) * 100);
310
331
  }
311
332
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Intermediate Representation (IR) Types for Cross-Format Conversion
3
- *
3
+ *
4
4
  * Enables bidirectional conversion between MUSUBI and Spec Kit formats
5
5
  * with minimal information loss.
6
6
  */
@@ -369,7 +369,7 @@ function createEmptyFeatureIR(id, name) {
369
369
  function createRequirementFromEARS(id, statement) {
370
370
  const pattern = detectEARSPattern(statement);
371
371
  const parsed = parseEARSStatement(statement, pattern);
372
-
372
+
373
373
  return {
374
374
  id,
375
375
  title: '',
@@ -390,7 +390,7 @@ function createRequirementFromEARS(id, statement) {
390
390
  */
391
391
  function detectEARSPattern(statement) {
392
392
  const upper = statement.toUpperCase();
393
-
393
+
394
394
  if (upper.includes('WHILE') && upper.includes('WHEN')) {
395
395
  return 'complex';
396
396
  }
@@ -412,33 +412,33 @@ function detectEARSPattern(statement) {
412
412
  * @param {EARSPattern} pattern - EARS pattern
413
413
  * @returns {{trigger?: string, condition?: string, action: string}}
414
414
  */
415
- function parseEARSStatement(statement, pattern) {
415
+ function parseEARSStatement(statement, _pattern) {
416
416
  const result = { action: '' };
417
-
417
+
418
418
  // Extract SHALL clause
419
419
  const shallMatch = statement.match(/SHALL\s+(.+?)(?:\.|$)/i);
420
420
  if (shallMatch) {
421
421
  result.action = shallMatch[1].trim();
422
422
  }
423
-
423
+
424
424
  // Extract WHEN clause
425
425
  const whenMatch = statement.match(/WHEN\s+(.+?),/i);
426
426
  if (whenMatch) {
427
427
  result.trigger = whenMatch[1].trim();
428
428
  }
429
-
429
+
430
430
  // Extract WHILE clause
431
431
  const whileMatch = statement.match(/WHILE\s+(.+?),/i);
432
432
  if (whileMatch) {
433
433
  result.condition = whileMatch[1].trim();
434
434
  }
435
-
435
+
436
436
  // Extract WHERE clause
437
437
  const whereMatch = statement.match(/WHERE\s+(.+?),/i);
438
438
  if (whereMatch) {
439
439
  result.condition = whereMatch[1].trim();
440
440
  }
441
-
441
+
442
442
  return result;
443
443
  }
444
444
 
@@ -452,7 +452,7 @@ function userScenarioToRequirement(userScenario, reqId) {
452
452
  // Convert "As a [actor], I want [action] so that [benefit]"
453
453
  // to "WHEN the [actor] [action], the system SHALL [provide benefit]"
454
454
  const statement = `WHEN the ${userScenario.actor} ${userScenario.action}, the system SHALL ${userScenario.benefit}.`;
455
-
455
+
456
456
  return {
457
457
  id: reqId,
458
458
  title: userScenario.title,
@@ -477,7 +477,7 @@ function requirementToUserScenario(requirement, storyId) {
477
477
  let actor = 'user';
478
478
  let action = requirement.trigger || 'performs an action';
479
479
  let benefit = requirement.action;
480
-
480
+
481
481
  // Try to extract actor from trigger
482
482
  if (requirement.trigger) {
483
483
  const actorMatch = requirement.trigger.match(/the\s+(\w+)/i);
@@ -485,7 +485,7 @@ function requirementToUserScenario(requirement, storyId) {
485
485
  actor = actorMatch[1];
486
486
  }
487
487
  }
488
-
488
+
489
489
  return {
490
490
  id: storyId,
491
491
  title: requirement.title || `Story for ${requirement.id}`,