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
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Spec Kit Writer
3
- *
3
+ *
4
4
  * Writes Intermediate Representation (IR) to Spec Kit project structure
5
- *
5
+ *
6
6
  * Spec Kit structure:
7
7
  * .specify/
8
8
  * ├── memory/
@@ -36,22 +36,22 @@ async function writeSpeckitProject(ir, outputPath, options = {}) {
36
36
  const { dryRun = false, force = false, preserveRaw = false, verbose = false } = options;
37
37
  const warnings = [];
38
38
  let filesWritten = 0;
39
-
39
+
40
40
  const specifyPath = path.join(outputPath, '.specify');
41
-
41
+
42
42
  // Create base directories
43
43
  const dirs = [
44
44
  path.join(specifyPath, 'memory'),
45
45
  path.join(specifyPath, 'specs'),
46
46
  path.join(specifyPath, 'templates'),
47
47
  ];
48
-
48
+
49
49
  if (!dryRun) {
50
50
  for (const dir of dirs) {
51
51
  await fs.ensureDir(dir);
52
52
  }
53
53
  }
54
-
54
+
55
55
  // Write constitution
56
56
  const constitutionPath = path.join(specifyPath, 'memory', 'constitution.md');
57
57
  const constitution = generateConstitution(ir.constitution, preserveRaw);
@@ -60,17 +60,22 @@ async function writeSpeckitProject(ir, outputPath, options = {}) {
60
60
  filesWritten++;
61
61
  }
62
62
  if (verbose) console.log(` Writing: ${constitutionPath}`);
63
-
63
+
64
64
  // Write features
65
65
  for (let i = 0; i < ir.features.length; i++) {
66
66
  const feature = ir.features[i];
67
67
  const featureId = formatFeatureId(i + 1, feature.id);
68
68
  const featurePath = path.join(specifyPath, 'specs', featureId);
69
- const result = await writeFeature(feature, featurePath, { dryRun, force, preserveRaw, verbose });
69
+ const result = await writeFeature(feature, featurePath, {
70
+ dryRun,
71
+ force,
72
+ preserveRaw,
73
+ verbose,
74
+ });
70
75
  filesWritten += result.filesWritten;
71
76
  warnings.push(...result.warnings);
72
77
  }
73
-
78
+
74
79
  // Write templates
75
80
  for (const template of ir.templates) {
76
81
  const templatePath = path.join(specifyPath, 'templates', `${template.name}.md`);
@@ -80,18 +85,18 @@ async function writeSpeckitProject(ir, outputPath, options = {}) {
80
85
  }
81
86
  if (verbose) console.log(` Writing: ${templatePath}`);
82
87
  }
83
-
88
+
84
89
  return { filesWritten, warnings };
85
90
  }
86
91
 
87
92
  /**
88
93
  * Write file with optional force overwrite
89
- * @param {string} filePath
90
- * @param {string} content
91
- * @param {boolean} force
94
+ * @param {string} filePath
95
+ * @param {string} content
96
+ * @param {boolean} force
92
97
  */
93
98
  async function writeFile(filePath, content, force = false) {
94
- if (await fs.pathExists(filePath) && !force) {
99
+ if ((await fs.pathExists(filePath)) && !force) {
95
100
  throw new Error(`File exists: ${filePath} (use --force to overwrite)`);
96
101
  }
97
102
  await fs.writeFile(filePath, content, 'utf-8');
@@ -99,8 +104,8 @@ async function writeFile(filePath, content, force = false) {
99
104
 
100
105
  /**
101
106
  * Format feature ID with leading zeros (e.g., "001-photo-albums")
102
- * @param {number} index
103
- * @param {string} originalId
107
+ * @param {number} index
108
+ * @param {string} originalId
104
109
  * @returns {string}
105
110
  */
106
111
  function formatFeatureId(index, originalId) {
@@ -108,45 +113,45 @@ function formatFeatureId(index, originalId) {
108
113
  if (/^\d{3}-/.test(originalId)) {
109
114
  return originalId;
110
115
  }
111
-
116
+
112
117
  // Convert to kebab-case
113
118
  const kebabName = originalId
114
119
  .toLowerCase()
115
120
  .replace(/[^a-z0-9]+/g, '-')
116
121
  .replace(/^-|-$/g, '');
117
-
122
+
118
123
  return `${String(index).padStart(3, '0')}-${kebabName}`;
119
124
  }
120
125
 
121
126
  /**
122
127
  * Generate constitution.md content (Spec Kit format)
123
- * @param {import('../ir/types').ConstitutionIR} constitution
124
- * @param {boolean} preserveRaw
128
+ * @param {import('../ir/types').ConstitutionIR} constitution
129
+ * @param {boolean} preserveRaw
125
130
  * @returns {string}
126
131
  */
127
132
  function generateConstitution(constitution, preserveRaw = false) {
128
133
  const lines = [];
129
-
134
+
130
135
  lines.push('# Project Constitution');
131
136
  lines.push('');
132
137
  lines.push('The core principles and guidelines that govern this project.');
133
138
  lines.push('');
134
-
139
+
135
140
  // Write Core Principles (converted from MUSUBI articles)
136
141
  lines.push('## Core Principles');
137
142
  lines.push('');
138
-
143
+
139
144
  // Map articles to principles
140
145
  if (constitution.articles && constitution.articles.length > 0) {
141
146
  for (const article of constitution.articles) {
142
147
  lines.push(`### ${article.name}`);
143
148
  lines.push('');
144
-
149
+
145
150
  if (article.description) {
146
151
  lines.push(article.description);
147
152
  lines.push('');
148
153
  }
149
-
154
+
150
155
  if (article.rules && article.rules.length > 0) {
151
156
  for (const rule of article.rules) {
152
157
  lines.push(`- ${rule}`);
@@ -163,14 +168,14 @@ function generateConstitution(constitution, preserveRaw = false) {
163
168
  lines.push('');
164
169
  }
165
170
  }
166
-
171
+
167
172
  // Write Governance
168
173
  if (constitution.governance) {
169
174
  lines.push('## Governance');
170
175
  lines.push('');
171
176
  lines.push(`**Version**: ${constitution.governance.version}`);
172
177
  lines.push('');
173
-
178
+
174
179
  if (constitution.governance.rules && constitution.governance.rules.length > 0) {
175
180
  for (const rule of constitution.governance.rules) {
176
181
  lines.push(`- ${rule}`);
@@ -178,7 +183,7 @@ function generateConstitution(constitution, preserveRaw = false) {
178
183
  lines.push('');
179
184
  }
180
185
  }
181
-
186
+
182
187
  // Preserve raw content if requested
183
188
  if (preserveRaw && constitution.rawContent) {
184
189
  lines.push('---');
@@ -189,26 +194,26 @@ function generateConstitution(constitution, preserveRaw = false) {
189
194
  lines.push(constitution.rawContent);
190
195
  lines.push('```');
191
196
  }
192
-
197
+
193
198
  return lines.join('\n');
194
199
  }
195
200
 
196
201
  /**
197
202
  * Write a feature to Spec Kit format
198
- * @param {import('../ir/types').FeatureIR} feature
199
- * @param {string} featurePath
200
- * @param {Object} options
203
+ * @param {import('../ir/types').FeatureIR} feature
204
+ * @param {string} featurePath
205
+ * @param {Object} options
201
206
  * @returns {Promise<{filesWritten: number, warnings: string[]}>}
202
207
  */
203
208
  async function writeFeature(feature, featurePath, options = {}) {
204
209
  const { dryRun = false, force = false, preserveRaw = false, verbose = false } = options;
205
210
  const warnings = [];
206
211
  let filesWritten = 0;
207
-
212
+
208
213
  if (!dryRun) {
209
214
  await fs.ensureDir(featurePath);
210
215
  }
211
-
216
+
212
217
  // Write spec.md
213
218
  const specPath = path.join(featurePath, 'spec.md');
214
219
  const specContent = generateSpec(feature, preserveRaw);
@@ -217,7 +222,7 @@ async function writeFeature(feature, featurePath, options = {}) {
217
222
  filesWritten++;
218
223
  }
219
224
  if (verbose) console.log(` Writing: ${specPath}`);
220
-
225
+
221
226
  // Write plan.md
222
227
  if (feature.plan) {
223
228
  const planPath = path.join(featurePath, 'plan.md');
@@ -228,7 +233,7 @@ async function writeFeature(feature, featurePath, options = {}) {
228
233
  }
229
234
  if (verbose) console.log(` Writing: ${planPath}`);
230
235
  }
231
-
236
+
232
237
  // Write tasks.md
233
238
  if (feature.tasks && feature.tasks.length > 0) {
234
239
  const tasksPath = path.join(featurePath, 'tasks.md');
@@ -239,7 +244,7 @@ async function writeFeature(feature, featurePath, options = {}) {
239
244
  }
240
245
  if (verbose) console.log(` Writing: ${tasksPath}`);
241
246
  }
242
-
247
+
243
248
  // Write research.md
244
249
  if (feature.research) {
245
250
  const researchPath = path.join(featurePath, 'research.md');
@@ -250,7 +255,7 @@ async function writeFeature(feature, featurePath, options = {}) {
250
255
  }
251
256
  if (verbose) console.log(` Writing: ${researchPath}`);
252
257
  }
253
-
258
+
254
259
  // Write data-model.md
255
260
  if (feature.dataModel) {
256
261
  const dataModelPath = path.join(featurePath, 'data-model.md');
@@ -261,14 +266,14 @@ async function writeFeature(feature, featurePath, options = {}) {
261
266
  }
262
267
  if (verbose) console.log(` Writing: ${dataModelPath}`);
263
268
  }
264
-
269
+
265
270
  // Write contracts
266
271
  if (feature.contracts && feature.contracts.length > 0) {
267
272
  const contractsPath = path.join(featurePath, 'contracts');
268
273
  if (!dryRun) {
269
274
  await fs.ensureDir(contractsPath);
270
275
  }
271
-
276
+
272
277
  for (const contract of feature.contracts) {
273
278
  const contractFile = path.join(contractsPath, `${contract.name}.md`);
274
279
  const contractContent = contract.rawContent || generateContract(contract);
@@ -279,7 +284,7 @@ async function writeFeature(feature, featurePath, options = {}) {
279
284
  if (verbose) console.log(` Writing: ${contractFile}`);
280
285
  }
281
286
  }
282
-
287
+
283
288
  // Write quickstart.md
284
289
  if (feature.quickstart) {
285
290
  const quickstartPath = path.join(featurePath, 'quickstart.md');
@@ -290,47 +295,49 @@ async function writeFeature(feature, featurePath, options = {}) {
290
295
  }
291
296
  if (verbose) console.log(` Writing: ${quickstartPath}`);
292
297
  }
293
-
298
+
294
299
  return { filesWritten, warnings };
295
300
  }
296
301
 
297
302
  /**
298
303
  * Generate spec.md content (Spec Kit format with User Scenarios)
299
- * @param {import('../ir/types').FeatureIR} feature
300
- * @param {boolean} preserveRaw
304
+ * @param {import('../ir/types').FeatureIR} feature
305
+ * @param {boolean} preserveRaw
301
306
  * @returns {string}
302
307
  */
303
308
  function generateSpec(feature, preserveRaw = false) {
304
309
  const lines = [];
305
310
  const spec = feature.specification;
306
-
311
+
307
312
  lines.push(`# ${spec.title || feature.name}`);
308
313
  lines.push('');
309
-
314
+
310
315
  if (spec.description) {
311
316
  lines.push(spec.description);
312
317
  lines.push('');
313
318
  }
314
-
319
+
315
320
  // Convert EARS requirements to User Scenarios
316
321
  if (spec.requirements && spec.requirements.length > 0) {
317
322
  lines.push('## User Scenarios');
318
323
  lines.push('');
319
-
324
+
320
325
  let storyIndex = 1;
321
326
  for (const req of spec.requirements) {
322
327
  // Convert requirement to user scenario if needed
323
328
  const storyId = `US${storyIndex++}`;
324
- const scenario = spec.userScenarios?.find(s => s.id === storyId)
325
- || requirementToUserScenario(req, storyId);
326
-
329
+ const scenario =
330
+ spec.userScenarios?.find(s => s.id === storyId) || requirementToUserScenario(req, storyId);
331
+
327
332
  lines.push(`### ${scenario.title || req.title || `User Story ${storyIndex}`}`);
328
333
  lines.push('');
329
- lines.push(`As a ${scenario.actor}, I want to ${scenario.action} so that ${scenario.benefit}.`);
334
+ lines.push(
335
+ `As a ${scenario.actor}, I want to ${scenario.action} so that ${scenario.benefit}.`
336
+ );
330
337
  lines.push('');
331
338
  lines.push(`**Priority**: ${scenario.priority || req.priority}`);
332
339
  lines.push('');
333
-
340
+
334
341
  if (scenario.acceptanceCriteria && scenario.acceptanceCriteria.length > 0) {
335
342
  lines.push('**Acceptance Criteria**:');
336
343
  for (const ac of scenario.acceptanceCriteria) {
@@ -338,7 +345,7 @@ function generateSpec(feature, preserveRaw = false) {
338
345
  }
339
346
  lines.push('');
340
347
  }
341
-
348
+
342
349
  // Add original EARS statement as note
343
350
  if (req.statement && !req.mappedFromUserStory) {
344
351
  lines.push(`> Original EARS: ${req.statement}`);
@@ -349,15 +356,17 @@ function generateSpec(feature, preserveRaw = false) {
349
356
  // Use original user scenarios
350
357
  lines.push('## User Scenarios');
351
358
  lines.push('');
352
-
359
+
353
360
  for (const scenario of spec.userScenarios) {
354
361
  lines.push(`### ${scenario.title}`);
355
362
  lines.push('');
356
- lines.push(`As a ${scenario.actor}, I want to ${scenario.action} so that ${scenario.benefit}.`);
363
+ lines.push(
364
+ `As a ${scenario.actor}, I want to ${scenario.action} so that ${scenario.benefit}.`
365
+ );
357
366
  lines.push('');
358
367
  lines.push(`**Priority**: ${scenario.priority}`);
359
368
  lines.push('');
360
-
369
+
361
370
  if (scenario.acceptanceCriteria && scenario.acceptanceCriteria.length > 0) {
362
371
  lines.push('**Acceptance Criteria**:');
363
372
  for (const ac of scenario.acceptanceCriteria) {
@@ -367,7 +376,7 @@ function generateSpec(feature, preserveRaw = false) {
367
376
  }
368
377
  }
369
378
  }
370
-
379
+
371
380
  // Write success criteria
372
381
  if (spec.successCriteria && spec.successCriteria.length > 0) {
373
382
  lines.push('## Success Criteria');
@@ -377,7 +386,7 @@ function generateSpec(feature, preserveRaw = false) {
377
386
  }
378
387
  lines.push('');
379
388
  }
380
-
389
+
381
390
  // Preserve raw content if requested
382
391
  if (preserveRaw && spec.rawContent) {
383
392
  lines.push('---');
@@ -388,33 +397,33 @@ function generateSpec(feature, preserveRaw = false) {
388
397
  lines.push(spec.rawContent);
389
398
  lines.push('```');
390
399
  }
391
-
400
+
392
401
  return lines.join('\n');
393
402
  }
394
403
 
395
404
  /**
396
405
  * Generate plan.md content
397
- * @param {import('../ir/types').PlanIR} plan
398
- * @param {boolean} preserveRaw
406
+ * @param {import('../ir/types').PlanIR} plan
407
+ * @param {boolean} preserveRaw
399
408
  * @returns {string}
400
409
  */
401
410
  function generatePlan(plan, preserveRaw = false) {
402
411
  const lines = [];
403
-
412
+
404
413
  lines.push('# Implementation Plan');
405
414
  lines.push('');
406
-
415
+
407
416
  if (plan.summary) {
408
417
  lines.push(plan.summary);
409
418
  lines.push('');
410
419
  }
411
-
420
+
412
421
  // Technical Stack
413
422
  const tech = plan.technicalContext;
414
423
  if (tech && (tech.language || tech.framework)) {
415
424
  lines.push('## Technical Stack');
416
425
  lines.push('');
417
-
426
+
418
427
  if (tech.language) {
419
428
  lines.push(`- **Language**: ${tech.language}${tech.version ? ` ${tech.version}` : ''}`);
420
429
  }
@@ -429,21 +438,21 @@ function generatePlan(plan, preserveRaw = false) {
429
438
  }
430
439
  lines.push('');
431
440
  }
432
-
441
+
433
442
  // Implementation Phases
434
443
  if (plan.phases && plan.phases.length > 0) {
435
444
  lines.push('## Implementation Phases');
436
445
  lines.push('');
437
-
446
+
438
447
  for (const phase of plan.phases) {
439
448
  lines.push(`### Phase ${phase.number}: ${phase.name}`);
440
449
  lines.push('');
441
-
450
+
442
451
  if (phase.purpose) {
443
452
  lines.push(phase.purpose);
444
453
  lines.push('');
445
454
  }
446
-
455
+
447
456
  if (phase.outputs && phase.outputs.length > 0) {
448
457
  lines.push('**Outputs**:');
449
458
  for (const output of phase.outputs) {
@@ -453,7 +462,7 @@ function generatePlan(plan, preserveRaw = false) {
453
462
  }
454
463
  }
455
464
  }
456
-
465
+
457
466
  // Preserve raw content if requested
458
467
  if (preserveRaw && plan.rawContent) {
459
468
  lines.push('---');
@@ -464,21 +473,21 @@ function generatePlan(plan, preserveRaw = false) {
464
473
  lines.push(plan.rawContent);
465
474
  lines.push('```');
466
475
  }
467
-
476
+
468
477
  return lines.join('\n');
469
478
  }
470
479
 
471
480
  /**
472
481
  * Generate tasks.md content (Spec Kit format)
473
- * @param {import('../ir/types').TaskIR[]} tasks
482
+ * @param {import('../ir/types').TaskIR[]} tasks
474
483
  * @returns {string}
475
484
  */
476
485
  function generateTasks(tasks) {
477
486
  const lines = [];
478
-
487
+
479
488
  lines.push('# Tasks');
480
489
  lines.push('');
481
-
490
+
482
491
  // Group by phase
483
492
  const phases = {};
484
493
  for (const task of tasks) {
@@ -488,43 +497,45 @@ function generateTasks(tasks) {
488
497
  }
489
498
  phases[phase].push(task);
490
499
  }
491
-
500
+
492
501
  for (const [phaseNum, phaseTasks] of Object.entries(phases)) {
493
502
  lines.push(`## Phase ${phaseNum}`);
494
503
  lines.push('');
495
-
504
+
496
505
  for (const task of phaseTasks) {
497
506
  const checkbox = task.completed ? '[x]' : '[ ]';
498
507
  const parallelMarker = task.parallel ? '[P] ' : '';
499
508
  const storyMarker = task.userStory ? `[${task.userStory}] ` : '';
500
509
  const pathSuffix = task.filePath ? ` at ${task.filePath}` : '';
501
-
510
+
502
511
  // Spec Kit format: - [ ] T001 [P] [US1] Description at path/
503
- lines.push(`- ${checkbox} ${task.id} ${parallelMarker}${storyMarker}${task.description}${pathSuffix}`);
512
+ lines.push(
513
+ `- ${checkbox} ${task.id} ${parallelMarker}${storyMarker}${task.description}${pathSuffix}`
514
+ );
504
515
  }
505
516
  lines.push('');
506
517
  }
507
-
518
+
508
519
  return lines.join('\n');
509
520
  }
510
521
 
511
522
  /**
512
523
  * Generate research.md content
513
- * @param {import('../ir/types').ResearchIR} research
514
- * @param {boolean} preserveRaw
524
+ * @param {import('../ir/types').ResearchIR} research
525
+ * @param {boolean} preserveRaw
515
526
  * @returns {string}
516
527
  */
517
528
  function generateResearch(research, preserveRaw = false) {
518
529
  const lines = [];
519
-
530
+
520
531
  lines.push('# Research');
521
532
  lines.push('');
522
-
533
+
523
534
  // Decisions
524
535
  if (research.decisions && research.decisions.length > 0) {
525
536
  lines.push('## Decisions');
526
537
  lines.push('');
527
-
538
+
528
539
  for (const decision of research.decisions) {
529
540
  lines.push(`### ${decision.topic}`);
530
541
  lines.push('');
@@ -535,16 +546,16 @@ function generateResearch(research, preserveRaw = false) {
535
546
  lines.push('');
536
547
  }
537
548
  }
538
-
549
+
539
550
  // Alternatives Considered
540
551
  if (research.alternatives && research.alternatives.length > 0) {
541
552
  lines.push('## Alternatives Considered');
542
553
  lines.push('');
543
-
554
+
544
555
  for (const alt of research.alternatives) {
545
556
  lines.push(`### ${alt.name}`);
546
557
  lines.push('');
547
-
558
+
548
559
  if (alt.pros && alt.pros.length > 0) {
549
560
  lines.push('**Pros**:');
550
561
  for (const pro of alt.pros) {
@@ -552,7 +563,7 @@ function generateResearch(research, preserveRaw = false) {
552
563
  }
553
564
  lines.push('');
554
565
  }
555
-
566
+
556
567
  if (alt.cons && alt.cons.length > 0) {
557
568
  lines.push('**Cons**:');
558
569
  for (const con of alt.cons) {
@@ -560,7 +571,7 @@ function generateResearch(research, preserveRaw = false) {
560
571
  }
561
572
  lines.push('');
562
573
  }
563
-
574
+
564
575
  if (alt.rejected) {
565
576
  lines.push(`**Status**: Rejected`);
566
577
  if (alt.reason) {
@@ -570,7 +581,7 @@ function generateResearch(research, preserveRaw = false) {
570
581
  }
571
582
  }
572
583
  }
573
-
584
+
574
585
  // Preserve raw content if requested
575
586
  if (preserveRaw && research.rawContent) {
576
587
  lines.push('---');
@@ -581,36 +592,36 @@ function generateResearch(research, preserveRaw = false) {
581
592
  lines.push(research.rawContent);
582
593
  lines.push('```');
583
594
  }
584
-
595
+
585
596
  return lines.join('\n');
586
597
  }
587
598
 
588
599
  /**
589
600
  * Generate data-model.md content
590
- * @param {import('../ir/types').DataModelIR} dataModel
591
- * @param {boolean} preserveRaw
601
+ * @param {import('../ir/types').DataModelIR} dataModel
602
+ * @param {boolean} preserveRaw
592
603
  * @returns {string}
593
604
  */
594
605
  function generateDataModel(dataModel, preserveRaw = false) {
595
606
  const lines = [];
596
-
607
+
597
608
  lines.push('# Data Model');
598
609
  lines.push('');
599
-
610
+
600
611
  // Entities
601
612
  if (dataModel.entities && dataModel.entities.length > 0) {
602
613
  lines.push('## Entities');
603
614
  lines.push('');
604
-
615
+
605
616
  for (const entity of dataModel.entities) {
606
617
  lines.push(`### Entity: ${entity.name}`);
607
618
  lines.push('');
608
-
619
+
609
620
  if (entity.description) {
610
621
  lines.push(entity.description);
611
622
  lines.push('');
612
623
  }
613
-
624
+
614
625
  if (entity.fields && entity.fields.length > 0) {
615
626
  for (const field of entity.fields) {
616
627
  let fieldLine = `- ${field.name}: ${field.type}`;
@@ -621,19 +632,19 @@ function generateDataModel(dataModel, preserveRaw = false) {
621
632
  }
622
633
  }
623
634
  }
624
-
635
+
625
636
  // Relationships
626
637
  if (dataModel.relationships && dataModel.relationships.length > 0) {
627
638
  lines.push('## Relationships');
628
639
  lines.push('');
629
-
640
+
630
641
  for (const rel of dataModel.relationships) {
631
642
  const relType = rel.type === 'one-to-many' ? 'has many' : 'has one';
632
643
  lines.push(`- ${rel.from} ${relType} ${rel.to}`);
633
644
  }
634
645
  lines.push('');
635
646
  }
636
-
647
+
637
648
  // Preserve raw content if requested
638
649
  if (preserveRaw && dataModel.rawContent) {
639
650
  lines.push('---');
@@ -644,44 +655,44 @@ function generateDataModel(dataModel, preserveRaw = false) {
644
655
  lines.push(dataModel.rawContent);
645
656
  lines.push('```');
646
657
  }
647
-
658
+
648
659
  return lines.join('\n');
649
660
  }
650
661
 
651
662
  /**
652
663
  * Generate contract content
653
- * @param {import('../ir/types').ContractIR} contract
664
+ * @param {import('../ir/types').ContractIR} contract
654
665
  * @returns {string}
655
666
  */
656
667
  function generateContract(contract) {
657
668
  const lines = [];
658
-
669
+
659
670
  lines.push(`# ${contract.name}`);
660
671
  lines.push('');
661
672
  lines.push(`**Type**: ${contract.type.toUpperCase()}`);
662
673
  lines.push('');
663
-
674
+
664
675
  if (contract.rawContent) {
665
676
  lines.push(contract.rawContent);
666
677
  } else {
667
678
  lines.push('> Contract details to be defined.');
668
679
  }
669
-
680
+
670
681
  return lines.join('\n');
671
682
  }
672
683
 
673
684
  /**
674
685
  * Generate quickstart.md content
675
- * @param {import('../ir/types').QuickstartIR} quickstart
676
- * @param {boolean} preserveRaw
686
+ * @param {import('../ir/types').QuickstartIR} quickstart
687
+ * @param {boolean} preserveRaw
677
688
  * @returns {string}
678
689
  */
679
690
  function generateQuickstart(quickstart, preserveRaw = false) {
680
691
  const lines = [];
681
-
692
+
682
693
  lines.push('# Quickstart');
683
694
  lines.push('');
684
-
695
+
685
696
  if (quickstart.steps && quickstart.steps.length > 0) {
686
697
  for (let i = 0; i < quickstart.steps.length; i++) {
687
698
  const step = quickstart.steps[i];
@@ -689,7 +700,7 @@ function generateQuickstart(quickstart, preserveRaw = false) {
689
700
  }
690
701
  lines.push('');
691
702
  }
692
-
703
+
693
704
  // Preserve raw content if requested
694
705
  if (preserveRaw && quickstart.rawContent) {
695
706
  lines.push('---');
@@ -700,7 +711,7 @@ function generateQuickstart(quickstart, preserveRaw = false) {
700
711
  lines.push(quickstart.rawContent);
701
712
  lines.push('```');
702
713
  }
703
-
714
+
704
715
  return lines.join('\n');
705
716
  }
706
717