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
@@ -26,12 +26,874 @@ const TEMPLATE_DIR = path.join(__dirname, '..', 'src', 'templates');
26
26
  const SHARED_TEMPLATE_DIR = path.join(TEMPLATE_DIR, 'shared');
27
27
  const AGENTS_TEMPLATE_DIR = path.join(TEMPLATE_DIR, 'agents');
28
28
 
29
+ /**
30
+ * External specification reference handler
31
+ * Supports: URL (http/https), local file path, Git repository
32
+ * @param {string} specSource - Specification source (URL, file path, or git URL)
33
+ * @returns {object} Parsed specification with metadata
34
+ */
35
+ async function fetchExternalSpec(specSource) {
36
+ const result = {
37
+ source: specSource,
38
+ type: 'unknown',
39
+ content: null,
40
+ metadata: {},
41
+ error: null,
42
+ };
43
+
44
+ try {
45
+ // Determine source type
46
+ if (specSource.startsWith('http://') || specSource.startsWith('https://')) {
47
+ result.type = 'url';
48
+ const https = require('https');
49
+ const http = require('http');
50
+ const protocol = specSource.startsWith('https://') ? https : http;
51
+
52
+ result.content = await new Promise((resolve, reject) => {
53
+ protocol
54
+ .get(specSource, res => {
55
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
56
+ // Handle redirect
57
+ fetchExternalSpec(res.headers.location).then(r => resolve(r.content));
58
+ return;
59
+ }
60
+ if (res.statusCode !== 200) {
61
+ reject(new Error(`HTTP ${res.statusCode}`));
62
+ return;
63
+ }
64
+ let data = '';
65
+ res.on('data', chunk => (data += chunk));
66
+ res.on('end', () => resolve(data));
67
+ })
68
+ .on('error', reject);
69
+ });
70
+
71
+ // Extract metadata from URL
72
+ result.metadata.url = specSource;
73
+ result.metadata.fetchedAt = new Date().toISOString();
74
+ } else if (specSource.startsWith('git://') || specSource.includes('.git')) {
75
+ result.type = 'git';
76
+ result.metadata.repository = specSource;
77
+ // For Git repos, we'll store the reference for later cloning
78
+ result.content = `# External Specification Reference\n\nRepository: ${specSource}\n\n> Clone this repository to access the full specification.\n`;
79
+ } else if (fs.existsSync(specSource)) {
80
+ result.type = 'file';
81
+ result.content = await fs.readFile(specSource, 'utf8');
82
+ result.metadata.path = path.resolve(specSource);
83
+ result.metadata.readAt = new Date().toISOString();
84
+ } else {
85
+ result.error = `Specification source not found: ${specSource}`;
86
+ }
87
+
88
+ // Try to parse specification format
89
+ if (result.content) {
90
+ result.metadata.format = detectSpecFormat(result.content, specSource);
91
+ result.metadata.summary = extractSpecSummary(result.content);
92
+ }
93
+ } catch (err) {
94
+ result.error = err.message;
95
+ }
96
+
97
+ return result;
98
+ }
99
+
100
+ /**
101
+ * Parse GitHub repository reference
102
+ * Supports formats:
103
+ * - owner/repo
104
+ * - https://github.com/owner/repo
105
+ * - git@github.com:owner/repo.git
106
+ * @param {string} repoRef - Repository reference string
107
+ * @returns {object} Parsed repository info
108
+ */
109
+ function parseGitHubRepo(repoRef) {
110
+ let owner = '';
111
+ let repo = '';
112
+ let branch = 'main';
113
+ let path = '';
114
+
115
+ // Handle owner/repo format
116
+ const simpleMatch = repoRef.match(/^([^/]+)\/([^/@#]+)(?:@([^#]+))?(?:#(.+))?$/);
117
+ if (simpleMatch) {
118
+ owner = simpleMatch[1];
119
+ repo = simpleMatch[2];
120
+ branch = simpleMatch[3] || 'main';
121
+ path = simpleMatch[4] || '';
122
+ return { owner, repo, branch, path, url: `https://github.com/${owner}/${repo}` };
123
+ }
124
+
125
+ // Handle https://github.com/owner/repo format
126
+ const httpsMatch = repoRef.match(
127
+ /github\.com\/([^/]+)\/([^/@#\s]+?)(?:\.git)?(?:@([^#]+))?(?:#(.+))?$/
128
+ );
129
+ if (httpsMatch) {
130
+ owner = httpsMatch[1];
131
+ repo = httpsMatch[2];
132
+ branch = httpsMatch[3] || 'main';
133
+ path = httpsMatch[4] || '';
134
+ return { owner, repo, branch, path, url: `https://github.com/${owner}/${repo}` };
135
+ }
136
+
137
+ // Handle git@github.com:owner/repo.git format
138
+ const sshMatch = repoRef.match(
139
+ /git@github\.com:([^/]+)\/([^/.]+)(?:\.git)?(?:@([^#]+))?(?:#(.+))?$/
140
+ );
141
+ if (sshMatch) {
142
+ owner = sshMatch[1];
143
+ repo = sshMatch[2];
144
+ branch = sshMatch[3] || 'main';
145
+ path = sshMatch[4] || '';
146
+ return { owner, repo, branch, path, url: `https://github.com/${owner}/${repo}` };
147
+ }
148
+
149
+ return { error: `Invalid GitHub repository format: ${repoRef}` };
150
+ }
151
+
152
+ /**
153
+ * Fetch GitHub repository metadata and key files
154
+ * @param {string} repoRef - Repository reference (owner/repo, URL, etc.)
155
+ * @returns {object} Repository data with structure and key files
156
+ */
157
+ async function fetchGitHubRepo(repoRef) {
158
+ const parsed = parseGitHubRepo(repoRef);
159
+ if (parsed.error) {
160
+ return { source: repoRef, error: parsed.error };
161
+ }
162
+
163
+ const { owner, repo, branch, path: subPath } = parsed;
164
+ const https = require('https');
165
+
166
+ const result = {
167
+ source: repoRef,
168
+ owner,
169
+ repo,
170
+ branch,
171
+ url: parsed.url,
172
+ metadata: {},
173
+ files: {},
174
+ structure: [],
175
+ improvements: [],
176
+ error: null,
177
+ };
178
+
179
+ // Helper to fetch from GitHub API
180
+ const fetchGitHubAPI = endpoint =>
181
+ new Promise((resolve, reject) => {
182
+ const options = {
183
+ hostname: 'api.github.com',
184
+ path: endpoint,
185
+ headers: {
186
+ 'User-Agent': 'MUSUBI-SDD',
187
+ Accept: 'application/vnd.github.v3+json',
188
+ },
189
+ };
190
+
191
+ // Add GitHub token if available
192
+ if (process.env.GITHUB_TOKEN) {
193
+ options.headers['Authorization'] = `token ${process.env.GITHUB_TOKEN}`;
194
+ }
195
+
196
+ https
197
+ .get(options, res => {
198
+ let data = '';
199
+ res.on('data', chunk => (data += chunk));
200
+ res.on('end', () => {
201
+ if (res.statusCode === 200) {
202
+ try {
203
+ resolve(JSON.parse(data));
204
+ } catch {
205
+ reject(new Error('Invalid JSON response'));
206
+ }
207
+ } else if (res.statusCode === 404) {
208
+ reject(new Error(`Repository not found: ${owner}/${repo}`));
209
+ } else if (res.statusCode === 403) {
210
+ reject(
211
+ new Error('GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable.')
212
+ );
213
+ } else {
214
+ reject(new Error(`GitHub API error: ${res.statusCode}`));
215
+ }
216
+ });
217
+ })
218
+ .on('error', reject);
219
+ });
220
+
221
+ // Fetch raw file content
222
+ const fetchRawFile = filePath =>
223
+ new Promise((resolve, reject) => {
224
+ const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`;
225
+ https
226
+ .get(rawUrl, res => {
227
+ if (res.statusCode === 302 || res.statusCode === 301) {
228
+ https
229
+ .get(res.headers.location, res2 => {
230
+ let data = '';
231
+ res2.on('data', chunk => (data += chunk));
232
+ res2.on('end', () => resolve(data));
233
+ })
234
+ .on('error', reject);
235
+ return;
236
+ }
237
+ if (res.statusCode !== 200) {
238
+ resolve(null); // File not found is OK
239
+ return;
240
+ }
241
+ let data = '';
242
+ res.on('data', chunk => (data += chunk));
243
+ res.on('end', () => resolve(data));
244
+ })
245
+ .on('error', reject);
246
+ });
247
+
248
+ try {
249
+ // Fetch repository metadata
250
+ const repoData = await fetchGitHubAPI(`/repos/${owner}/${repo}`);
251
+ result.metadata = {
252
+ name: repoData.name,
253
+ description: repoData.description,
254
+ language: repoData.language,
255
+ stars: repoData.stargazers_count,
256
+ topics: repoData.topics || [],
257
+ license: repoData.license?.spdx_id,
258
+ defaultBranch: repoData.default_branch,
259
+ updatedAt: repoData.updated_at,
260
+ };
261
+
262
+ // Fetch directory structure (root level)
263
+ const treePath = subPath
264
+ ? `/repos/${owner}/${repo}/contents/${subPath}`
265
+ : `/repos/${owner}/${repo}/contents`;
266
+ try {
267
+ const contents = await fetchGitHubAPI(treePath);
268
+ if (Array.isArray(contents)) {
269
+ result.structure = contents.map(item => ({
270
+ name: item.name,
271
+ type: item.type,
272
+ path: item.path,
273
+ }));
274
+ }
275
+ } catch {
276
+ // Ignore structure fetch errors
277
+ }
278
+
279
+ // Fetch key files for analysis
280
+ const keyFiles = [
281
+ 'README.md',
282
+ 'package.json',
283
+ 'Cargo.toml',
284
+ 'pyproject.toml',
285
+ 'go.mod',
286
+ 'pom.xml',
287
+ '.github/CODEOWNERS',
288
+ 'ARCHITECTURE.md',
289
+ 'CONTRIBUTING.md',
290
+ 'docs/architecture.md',
291
+ 'src/lib.rs',
292
+ 'src/index.ts',
293
+ 'src/main.ts',
294
+ ];
295
+
296
+ for (const file of keyFiles) {
297
+ const filePath = subPath ? `${subPath}/${file}` : file;
298
+ try {
299
+ const content = await fetchRawFile(filePath);
300
+ if (content) {
301
+ result.files[file] = content.slice(0, 10000); // Limit content size
302
+ }
303
+ } catch {
304
+ // Ignore individual file fetch errors
305
+ }
306
+ }
307
+ } catch (err) {
308
+ result.error = err.message;
309
+ }
310
+
311
+ return result;
312
+ }
313
+
314
+ /**
315
+ * Fetch multiple GitHub repositories
316
+ * @param {string[]} repos - Array of repository references
317
+ * @returns {object[]} Array of repository data
318
+ */
319
+ async function fetchGitHubRepos(repos) {
320
+ const results = [];
321
+
322
+ for (const repoRef of repos) {
323
+ console.log(chalk.cyan(` 📦 Fetching ${repoRef}...`));
324
+ const repoData = await fetchGitHubRepo(repoRef);
325
+
326
+ if (repoData.error) {
327
+ console.log(chalk.yellow(` ⚠️ ${repoData.error}`));
328
+ } else {
329
+ console.log(
330
+ chalk.green(
331
+ ` ✓ ${repoData.metadata.name || repoData.repo} (${repoData.metadata.language || 'unknown'})`
332
+ )
333
+ );
334
+ if (repoData.metadata.description) {
335
+ console.log(chalk.gray(` ${repoData.metadata.description.slice(0, 80)}`));
336
+ }
337
+ }
338
+
339
+ results.push(repoData);
340
+ }
341
+
342
+ return results;
343
+ }
344
+
345
+ /**
346
+ * Analyze repositories for improvement suggestions
347
+ * @param {object[]} repos - Array of fetched repository data
348
+ * @returns {object} Analysis results with patterns and suggestions
349
+ */
350
+ function analyzeReposForImprovements(repos) {
351
+ const analysis = {
352
+ patterns: [],
353
+ architectures: [],
354
+ technologies: [],
355
+ configurations: [],
356
+ suggestions: [],
357
+ };
358
+
359
+ for (const repo of repos) {
360
+ if (repo.error) continue;
361
+
362
+ // Detect architecture patterns from structure
363
+ const dirs = repo.structure.filter(s => s.type === 'dir').map(s => s.name);
364
+ const files = repo.structure.filter(s => s.type === 'file').map(s => s.name);
365
+
366
+ // Check for Clean Architecture
367
+ if (dirs.some(d => ['domain', 'application', 'infrastructure', 'interface'].includes(d))) {
368
+ analysis.architectures.push({
369
+ repo: repo.repo,
370
+ pattern: 'clean-architecture',
371
+ evidence: dirs.filter(d =>
372
+ ['domain', 'application', 'infrastructure', 'interface'].includes(d)
373
+ ),
374
+ });
375
+ }
376
+
377
+ // Check for Hexagonal Architecture
378
+ if (dirs.some(d => ['adapters', 'ports', 'core', 'hexagon'].includes(d))) {
379
+ analysis.architectures.push({
380
+ repo: repo.repo,
381
+ pattern: 'hexagonal',
382
+ evidence: dirs.filter(d => ['adapters', 'ports', 'core', 'hexagon'].includes(d)),
383
+ });
384
+ }
385
+
386
+ // Check for DDD patterns
387
+ if (
388
+ dirs.some(d =>
389
+ ['aggregates', 'entities', 'valueobjects', 'repositories', 'services'].includes(
390
+ d.toLowerCase()
391
+ )
392
+ )
393
+ ) {
394
+ analysis.patterns.push({
395
+ repo: repo.repo,
396
+ pattern: 'domain-driven-design',
397
+ evidence: dirs,
398
+ });
399
+ }
400
+
401
+ // Check for monorepo patterns
402
+ if (
403
+ dirs.includes('packages') ||
404
+ dirs.includes('apps') ||
405
+ files.includes('pnpm-workspace.yaml')
406
+ ) {
407
+ analysis.patterns.push({
408
+ repo: repo.repo,
409
+ pattern: 'monorepo',
410
+ evidence: dirs.filter(d => ['packages', 'apps', 'libs'].includes(d)),
411
+ });
412
+ }
413
+
414
+ // Analyze package.json for technologies
415
+ if (repo.files['package.json']) {
416
+ try {
417
+ const pkg = JSON.parse(repo.files['package.json']);
418
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
419
+
420
+ // Detect frameworks
421
+ if (deps['react']) analysis.technologies.push({ repo: repo.repo, tech: 'react' });
422
+ if (deps['vue']) analysis.technologies.push({ repo: repo.repo, tech: 'vue' });
423
+ if (deps['@angular/core']) analysis.technologies.push({ repo: repo.repo, tech: 'angular' });
424
+ if (deps['express']) analysis.technologies.push({ repo: repo.repo, tech: 'express' });
425
+ if (deps['fastify']) analysis.technologies.push({ repo: repo.repo, tech: 'fastify' });
426
+ if (deps['next']) analysis.technologies.push({ repo: repo.repo, tech: 'nextjs' });
427
+ if (deps['typescript']) analysis.technologies.push({ repo: repo.repo, tech: 'typescript' });
428
+
429
+ // Detect testing frameworks
430
+ if (deps['jest']) analysis.configurations.push({ repo: repo.repo, config: 'jest' });
431
+ if (deps['vitest']) analysis.configurations.push({ repo: repo.repo, config: 'vitest' });
432
+ if (deps['mocha']) analysis.configurations.push({ repo: repo.repo, config: 'mocha' });
433
+
434
+ // Detect linting/formatting
435
+ if (deps['eslint']) analysis.configurations.push({ repo: repo.repo, config: 'eslint' });
436
+ if (deps['prettier']) analysis.configurations.push({ repo: repo.repo, config: 'prettier' });
437
+ if (deps['biome']) analysis.configurations.push({ repo: repo.repo, config: 'biome' });
438
+ } catch {
439
+ // Ignore JSON parse errors
440
+ }
441
+ }
442
+
443
+ // Analyze Cargo.toml for Rust patterns
444
+ if (repo.files['Cargo.toml']) {
445
+ const cargo = repo.files['Cargo.toml'];
446
+ if (cargo.includes('[workspace]')) {
447
+ analysis.patterns.push({ repo: repo.repo, pattern: 'rust-workspace' });
448
+ }
449
+ if (cargo.includes('tokio')) analysis.technologies.push({ repo: repo.repo, tech: 'tokio' });
450
+ if (cargo.includes('actix')) analysis.technologies.push({ repo: repo.repo, tech: 'actix' });
451
+ if (cargo.includes('axum')) analysis.technologies.push({ repo: repo.repo, tech: 'axum' });
452
+ }
453
+
454
+ // Analyze pyproject.toml for Python patterns
455
+ if (repo.files['pyproject.toml']) {
456
+ const pyproj = repo.files['pyproject.toml'];
457
+ if (pyproj.includes('fastapi'))
458
+ analysis.technologies.push({ repo: repo.repo, tech: 'fastapi' });
459
+ if (pyproj.includes('django'))
460
+ analysis.technologies.push({ repo: repo.repo, tech: 'django' });
461
+ if (pyproj.includes('flask')) analysis.technologies.push({ repo: repo.repo, tech: 'flask' });
462
+ if (pyproj.includes('pytest'))
463
+ analysis.configurations.push({ repo: repo.repo, config: 'pytest' });
464
+ }
465
+
466
+ // Extract README insights
467
+ if (repo.files['README.md']) {
468
+ const readme = repo.files['README.md'];
469
+
470
+ // Check for badges that indicate good practices
471
+ if (readme.includes('coverage')) {
472
+ analysis.suggestions.push({
473
+ repo: repo.repo,
474
+ suggestion: 'code-coverage',
475
+ description: 'Implements code coverage tracking',
476
+ });
477
+ }
478
+ if (readme.includes('CI/CD') || readme.includes('Actions')) {
479
+ analysis.suggestions.push({
480
+ repo: repo.repo,
481
+ suggestion: 'ci-cd',
482
+ description: 'Has CI/CD pipeline configured',
483
+ });
484
+ }
485
+ }
486
+ }
487
+
488
+ // Generate improvement suggestions based on analysis
489
+ if (analysis.architectures.length > 0) {
490
+ const archCounts = {};
491
+ for (const arch of analysis.architectures) {
492
+ archCounts[arch.pattern] = (archCounts[arch.pattern] || 0) + 1;
493
+ }
494
+ const mostCommon = Object.entries(archCounts).sort((a, b) => b[1] - a[1])[0];
495
+ if (mostCommon) {
496
+ analysis.suggestions.push({
497
+ type: 'architecture',
498
+ suggestion: `Consider using ${mostCommon[0]} pattern`,
499
+ count: mostCommon[1],
500
+ repos: analysis.architectures.filter(a => a.pattern === mostCommon[0]).map(a => a.repo),
501
+ });
502
+ }
503
+ }
504
+
505
+ if (analysis.technologies.length > 0) {
506
+ const techCounts = {};
507
+ for (const tech of analysis.technologies) {
508
+ techCounts[tech.tech] = (techCounts[tech.tech] || 0) + 1;
509
+ }
510
+ const popular = Object.entries(techCounts)
511
+ .sort((a, b) => b[1] - a[1])
512
+ .slice(0, 3);
513
+ for (const [tech, count] of popular) {
514
+ analysis.suggestions.push({
515
+ type: 'technology',
516
+ suggestion: `Consider using ${tech}`,
517
+ count,
518
+ repos: analysis.technologies.filter(t => t.tech === tech).map(t => t.repo),
519
+ });
520
+ }
521
+ }
522
+
523
+ return analysis;
524
+ }
525
+
526
+ /**
527
+ * Save reference repositories analysis to steering/references/
528
+ * @param {object[]} repos - Fetched repository data
529
+ * @param {object} analysis - Analysis results
530
+ * @param {string} projectPath - Target project path
531
+ * @returns {string} Created file path
532
+ */
533
+ async function saveReferenceRepos(repos, analysis, projectPath) {
534
+ const refsDir = path.join(projectPath, 'steering', 'references');
535
+ await fs.ensureDir(refsDir);
536
+
537
+ const timestamp = new Date().toISOString().split('T')[0];
538
+ const filename = `github-references-${timestamp}.md`;
539
+
540
+ // Build markdown content
541
+ let content = `# GitHub Reference Repositories
542
+
543
+ > Analyzed on ${new Date().toISOString()}
544
+
545
+ ## Referenced Repositories
546
+
547
+ `;
548
+
549
+ for (const repo of repos) {
550
+ if (repo.error) {
551
+ content += `### ❌ ${repo.source}\n\n`;
552
+ content += `Error: ${repo.error}\n\n`;
553
+ continue;
554
+ }
555
+
556
+ content += `### ${repo.metadata.name || repo.repo}\n\n`;
557
+ content += `- **URL**: ${repo.url}\n`;
558
+ content += `- **Language**: ${repo.metadata.language || 'Unknown'}\n`;
559
+ content += `- **Stars**: ${repo.metadata.stars || 0}\n`;
560
+ if (repo.metadata.description) {
561
+ content += `- **Description**: ${repo.metadata.description}\n`;
562
+ }
563
+ if (repo.metadata.topics && repo.metadata.topics.length > 0) {
564
+ content += `- **Topics**: ${repo.metadata.topics.join(', ')}\n`;
565
+ }
566
+ if (repo.metadata.license) {
567
+ content += `- **License**: ${repo.metadata.license}\n`;
568
+ }
569
+ content += '\n';
570
+
571
+ // Structure
572
+ if (repo.structure.length > 0) {
573
+ content += '**Directory Structure:**\n\n';
574
+ content += '```\n';
575
+ for (const item of repo.structure.slice(0, 20)) {
576
+ content += `${item.type === 'dir' ? '📁' : '📄'} ${item.name}\n`;
577
+ }
578
+ if (repo.structure.length > 20) {
579
+ content += `... and ${repo.structure.length - 20} more items\n`;
580
+ }
581
+ content += '```\n\n';
582
+ }
583
+ }
584
+
585
+ // Analysis section
586
+ content += `## Analysis Results
587
+
588
+ ### Architecture Patterns Detected
589
+
590
+ `;
591
+
592
+ if (analysis.architectures.length > 0) {
593
+ for (const arch of analysis.architectures) {
594
+ content += `- **${arch.pattern}** in \`${arch.repo}\`\n`;
595
+ content += ` - Evidence: ${arch.evidence.join(', ')}\n`;
596
+ }
597
+ } else {
598
+ content += '_No specific architecture patterns detected_\n';
599
+ }
600
+
601
+ content += `\n### Design Patterns
602
+
603
+ `;
604
+
605
+ if (analysis.patterns.length > 0) {
606
+ for (const pattern of analysis.patterns) {
607
+ content += `- **${pattern.pattern}** in \`${pattern.repo}\`\n`;
608
+ }
609
+ } else {
610
+ content += '_No specific design patterns detected_\n';
611
+ }
612
+
613
+ content += `\n### Technologies Used
614
+
615
+ `;
616
+
617
+ if (analysis.technologies.length > 0) {
618
+ const techByRepo = {};
619
+ for (const tech of analysis.technologies) {
620
+ if (!techByRepo[tech.repo]) techByRepo[tech.repo] = [];
621
+ techByRepo[tech.repo].push(tech.tech);
622
+ }
623
+ for (const [repo, techs] of Object.entries(techByRepo)) {
624
+ content += `- **${repo}**: ${techs.join(', ')}\n`;
625
+ }
626
+ } else {
627
+ content += '_No specific technologies detected_\n';
628
+ }
629
+
630
+ content += `\n### Configurations
631
+
632
+ `;
633
+
634
+ if (analysis.configurations.length > 0) {
635
+ const configByRepo = {};
636
+ for (const config of analysis.configurations) {
637
+ if (!configByRepo[config.repo]) configByRepo[config.repo] = [];
638
+ configByRepo[config.repo].push(config.config);
639
+ }
640
+ for (const [repo, configs] of Object.entries(configByRepo)) {
641
+ content += `- **${repo}**: ${configs.join(', ')}\n`;
642
+ }
643
+ } else {
644
+ content += '_No specific configurations detected_\n';
645
+ }
646
+
647
+ content += `\n## Improvement Suggestions
648
+
649
+ Based on the referenced repositories, consider the following improvements:
650
+
651
+ `;
652
+
653
+ if (analysis.suggestions.length > 0) {
654
+ let i = 1;
655
+ for (const suggestion of analysis.suggestions) {
656
+ if (suggestion.type === 'architecture') {
657
+ content += `${i}. **Architecture**: ${suggestion.suggestion}\n`;
658
+ content += ` - Found in ${suggestion.count} repository(ies): ${suggestion.repos.join(', ')}\n\n`;
659
+ } else if (suggestion.type === 'technology') {
660
+ content += `${i}. **Technology**: ${suggestion.suggestion}\n`;
661
+ content += ` - Used by ${suggestion.count} repository(ies): ${suggestion.repos.join(', ')}\n\n`;
662
+ } else {
663
+ content += `${i}. **${suggestion.suggestion}**: ${suggestion.description}\n`;
664
+ content += ` - Found in: ${suggestion.repo}\n\n`;
665
+ }
666
+ i++;
667
+ }
668
+ } else {
669
+ content += '_No specific suggestions generated_\n';
670
+ }
671
+
672
+ content += `
673
+ ---
674
+ *Generated by MUSUBI SDD - GitHub Reference Analysis*
675
+ `;
676
+
677
+ await fs.writeFile(path.join(refsDir, filename), content);
678
+ return filename;
679
+ }
680
+
681
+ /**
682
+ * Detect specification format from content and filename
683
+ */
684
+ function detectSpecFormat(content, source) {
685
+ const ext = path.extname(source).toLowerCase();
686
+ if (ext === '.json') return 'json';
687
+ if (ext === '.yaml' || ext === '.yml') return 'yaml';
688
+ if (ext === '.md') return 'markdown';
689
+ if (ext === '.rst') return 'rst';
690
+ if (ext === '.html') return 'html';
691
+
692
+ // Try to detect from content
693
+ if (content.trim().startsWith('{')) return 'json';
694
+ if (content.includes('openapi:') || content.includes('swagger:')) return 'openapi';
695
+ if (content.includes('asyncapi:')) return 'asyncapi';
696
+ if (content.includes('# ')) return 'markdown';
697
+
698
+ return 'text';
699
+ }
700
+
701
+ /**
702
+ * Extract summary from specification content
703
+ */
704
+ function extractSpecSummary(content) {
705
+ // Extract first heading and description
706
+ const lines = content.split('\n').slice(0, 50);
707
+ let title = '';
708
+ let description = '';
709
+
710
+ for (const line of lines) {
711
+ if (!title && line.startsWith('# ')) {
712
+ title = line.replace('# ', '').trim();
713
+ } else if (title && !description && line.trim() && !line.startsWith('#')) {
714
+ description = line.trim().slice(0, 200);
715
+ break;
716
+ }
717
+ }
718
+
719
+ return { title, description };
720
+ }
721
+
722
+ /**
723
+ * Save external specification reference to steering/specs/
724
+ */
725
+ async function saveSpecReference(specResult, projectPath) {
726
+ const specsDir = path.join(projectPath, 'steering', 'specs');
727
+ await fs.ensureDir(specsDir);
728
+
729
+ // Create spec reference file
730
+ const timestamp = new Date().toISOString().split('T')[0];
731
+ const safeName = specResult.metadata.summary?.title
732
+ ? specResult.metadata.summary.title
733
+ .toLowerCase()
734
+ .replace(/[^a-z0-9]+/g, '-')
735
+ .slice(0, 50)
736
+ : 'external-spec';
737
+ const filename = `${safeName}-${timestamp}.md`;
738
+
739
+ const refContent = `# External Specification Reference
740
+
741
+ ## Source Information
742
+
743
+ - **Type**: ${specResult.type}
744
+ - **Source**: ${specResult.source}
745
+ - **Format**: ${specResult.metadata.format || 'unknown'}
746
+ - **Fetched**: ${specResult.metadata.fetchedAt || specResult.metadata.readAt || 'N/A'}
747
+
748
+ ## Summary
749
+
750
+ ${specResult.metadata.summary?.title ? `**Title**: ${specResult.metadata.summary.title}` : ''}
751
+ ${specResult.metadata.summary?.description ? `\n**Description**: ${specResult.metadata.summary.description}` : ''}
752
+
753
+ ## Integration Notes
754
+
755
+ This specification is used as a reference for:
756
+ - Requirements analysis
757
+ - Architecture design
758
+ - API design
759
+ - Compliance validation
760
+
761
+ ## Original Content
762
+
763
+ \`\`\`${specResult.metadata.format || 'text'}
764
+ ${specResult.content?.slice(0, 5000) || 'Content not available'}${specResult.content?.length > 5000 ? '\n\n... (truncated, see original source)' : ''}
765
+ \`\`\`
766
+
767
+ ---
768
+ *Generated by MUSUBI SDD - External Specification Reference*
769
+ `;
770
+
771
+ await fs.writeFile(path.join(specsDir, filename), refContent);
772
+ return filename;
773
+ }
774
+
775
+ /**
776
+ * Language recommendation engine
777
+ * @param {object} requirements - User's answers about app types, performance, expertise
778
+ * @returns {Array} Recommended languages with reasons
779
+ */
780
+ function recommendLanguages(requirements) {
781
+ const { appTypes, performanceNeeds, teamExpertise } = requirements;
782
+ const scores = {};
783
+ const reasons = {};
784
+
785
+ // Initialize scores
786
+ const allLangs = [
787
+ 'javascript',
788
+ 'python',
789
+ 'rust',
790
+ 'go',
791
+ 'java',
792
+ 'csharp',
793
+ 'cpp',
794
+ 'swift',
795
+ 'ruby',
796
+ 'php',
797
+ ];
798
+ for (const lang of allLangs) {
799
+ scores[lang] = 0;
800
+ reasons[lang] = [];
801
+ }
802
+
803
+ // Score by application type
804
+ const appTypeScores = {
805
+ 'web-frontend': { javascript: 10, reason: 'Best ecosystem for web frontend' },
806
+ 'web-backend': {
807
+ javascript: 6,
808
+ python: 7,
809
+ go: 8,
810
+ rust: 7,
811
+ java: 7,
812
+ csharp: 6,
813
+ ruby: 5,
814
+ php: 5,
815
+ reason: 'Strong backend frameworks',
816
+ },
817
+ cli: { rust: 9, go: 9, python: 6, reason: 'Fast startup, single binary' },
818
+ desktop: { rust: 7, csharp: 8, cpp: 7, swift: 6, java: 6, reason: 'Native GUI support' },
819
+ mobile: { swift: 9, java: 8, javascript: 6, reason: 'Mobile platform support' },
820
+ data: { python: 10, rust: 6, reason: 'Rich data science ecosystem' },
821
+ ml: { python: 10, rust: 5, cpp: 5, reason: 'ML/AI libraries and frameworks' },
822
+ embedded: { rust: 10, cpp: 9, reason: 'Memory safety, no runtime' },
823
+ game: { cpp: 9, csharp: 8, rust: 6, reason: 'Game engine support' },
824
+ systems: { rust: 10, go: 8, cpp: 9, reason: 'Systems programming' },
825
+ };
826
+
827
+ for (const appType of appTypes || []) {
828
+ const typeScores = appTypeScores[appType];
829
+ if (typeScores) {
830
+ for (const [lang, score] of Object.entries(typeScores)) {
831
+ if (typeof score === 'number') {
832
+ scores[lang] += score;
833
+ if (!reasons[lang].includes(typeScores.reason)) {
834
+ reasons[lang].push(typeScores.reason);
835
+ }
836
+ }
837
+ }
838
+ }
839
+ }
840
+
841
+ // Score by performance needs
842
+ if (performanceNeeds === 'high') {
843
+ scores.rust += 8;
844
+ scores.go += 6;
845
+ scores.cpp += 7;
846
+ reasons.rust.push('High performance, zero-cost abstractions');
847
+ reasons.go.push('Fast compilation, efficient runtime');
848
+ } else if (performanceNeeds === 'rapid') {
849
+ scores.python += 5;
850
+ scores.javascript += 5;
851
+ scores.ruby += 4;
852
+ reasons.python.push('Rapid development, extensive libraries');
853
+ reasons.javascript.push('Fast iteration, universal runtime');
854
+ }
855
+
856
+ // Boost by team expertise
857
+ for (const lang of teamExpertise || []) {
858
+ scores[lang] += 5;
859
+ reasons[lang].push('Team has expertise');
860
+ }
861
+
862
+ // Sort and return top recommendations
863
+ const sorted = Object.entries(scores)
864
+ .filter(([, score]) => score > 0)
865
+ .sort((a, b) => b[1] - a[1])
866
+ .slice(0, 3);
867
+
868
+ const langInfo = {
869
+ javascript: { name: 'JavaScript/TypeScript', emoji: '🟨' },
870
+ python: { name: 'Python', emoji: '🐍' },
871
+ rust: { name: 'Rust', emoji: '🦀' },
872
+ go: { name: 'Go', emoji: '🐹' },
873
+ java: { name: 'Java/Kotlin', emoji: '☕' },
874
+ csharp: { name: 'C#/.NET', emoji: '💜' },
875
+ cpp: { name: 'C/C++', emoji: '⚙️' },
876
+ swift: { name: 'Swift', emoji: '🍎' },
877
+ ruby: { name: 'Ruby', emoji: '💎' },
878
+ php: { name: 'PHP', emoji: '🐘' },
879
+ };
880
+
881
+ return sorted.map(([lang]) => ({
882
+ value: lang,
883
+ name: langInfo[lang].name,
884
+ emoji: langInfo[lang].emoji,
885
+ reason: reasons[lang].slice(0, 2).join('; ') || 'General purpose',
886
+ score: scores[lang],
887
+ }));
888
+ }
889
+
29
890
  /**
30
891
  * Main initialization function
31
892
  * @param {object} agent - Agent definition from registry
32
893
  * @param {string} agentKey - Agent key (e.g., 'claude-code', 'cursor')
894
+ * @param {object} options - Command options (spec, workspace, etc.)
33
895
  */
34
- async function main(agent, agentKey) {
896
+ async function main(agent, agentKey, options = {}) {
35
897
  // Dynamic import for inquirer (ESM module)
36
898
  const inquirer = await import('inquirer');
37
899
 
@@ -45,6 +907,60 @@ async function main(agent, agentKey) {
45
907
  console.log(chalk.blue.bold('\n🎯 MUSUBI - Ultimate Specification Driven Development\n'));
46
908
  console.log(chalk.white(`Initializing for: ${chalk.bold(agent.label)}\n`));
47
909
 
910
+ // Handle external specification reference
911
+ let externalSpec = null;
912
+ if (options.spec) {
913
+ console.log(chalk.cyan('📄 Fetching external specification...'));
914
+ externalSpec = await fetchExternalSpec(options.spec);
915
+ if (externalSpec.error) {
916
+ console.log(chalk.yellow(`⚠️ Warning: ${externalSpec.error}`));
917
+ } else {
918
+ console.log(
919
+ chalk.green(
920
+ `✓ Loaded specification: ${externalSpec.metadata.summary?.title || externalSpec.source}`
921
+ )
922
+ );
923
+ console.log(
924
+ chalk.gray(` Format: ${externalSpec.metadata.format}, Type: ${externalSpec.type}\n`)
925
+ );
926
+ }
927
+ }
928
+
929
+ // Handle GitHub repository references
930
+ let referenceRepos = null;
931
+ let repoAnalysis = null;
932
+ if (options.references && options.references.length > 0) {
933
+ console.log(chalk.cyan(`\n📚 Fetching ${options.references.length} GitHub reference(s)...`));
934
+ referenceRepos = await fetchGitHubRepos(options.references);
935
+
936
+ // Analyze repositories for improvements
937
+ const validRepos = referenceRepos.filter(r => !r.error);
938
+ if (validRepos.length > 0) {
939
+ console.log(chalk.cyan('\n🔍 Analyzing repositories for patterns and improvements...'));
940
+ repoAnalysis = analyzeReposForImprovements(validRepos);
941
+
942
+ if (repoAnalysis.suggestions.length > 0) {
943
+ console.log(
944
+ chalk.green(`\n💡 Found ${repoAnalysis.suggestions.length} improvement suggestion(s):`)
945
+ );
946
+ for (const suggestion of repoAnalysis.suggestions.slice(0, 5)) {
947
+ if (suggestion.type === 'architecture') {
948
+ console.log(
949
+ chalk.white(` • ${suggestion.suggestion} (from ${suggestion.repos.join(', ')})`)
950
+ );
951
+ } else if (suggestion.type === 'technology') {
952
+ console.log(
953
+ chalk.white(` • ${suggestion.suggestion} (used by ${suggestion.count} repo(s))`)
954
+ );
955
+ } else {
956
+ console.log(chalk.white(` • ${suggestion.suggestion}`));
957
+ }
958
+ }
959
+ }
960
+ console.log('');
961
+ }
962
+ }
963
+
48
964
  // Check if already initialized for this agent
49
965
  const agentDir = agent.layout.agentDir;
50
966
  if (fs.existsSync(agentDir)) {
@@ -92,17 +1008,179 @@ async function main(agent, agentKey) {
92
1008
  ],
93
1009
  default: 'en',
94
1010
  },
1011
+ {
1012
+ type: 'list',
1013
+ name: 'projectStructure',
1014
+ message: 'Project structure:',
1015
+ choices: [
1016
+ { name: 'Single package', value: 'single' },
1017
+ { name: 'Workspace / Monorepo', value: 'workspace' },
1018
+ { name: 'Microservices', value: 'microservices' },
1019
+ ],
1020
+ default: options.workspace ? 'workspace' : 'single',
1021
+ },
95
1022
  {
96
1023
  type: 'list',
97
1024
  name: 'projectType',
98
1025
  message: 'Project type:',
99
1026
  choices: ['Greenfield (0→1)', 'Brownfield (1→n)', 'Both'],
100
1027
  },
1028
+ {
1029
+ type: 'list',
1030
+ name: 'techStackApproach',
1031
+ message: 'Technology stack approach:',
1032
+ choices: [
1033
+ { name: 'Single language', value: 'single' },
1034
+ { name: 'Multiple languages', value: 'multiple' },
1035
+ { name: 'Undecided (decide later)', value: 'undecided' },
1036
+ { name: 'Help me decide (recommend based on requirements)', value: 'recommend' },
1037
+ ],
1038
+ default: 'single',
1039
+ },
1040
+ ];
1041
+
1042
+ // Template selection if project structure is workspace or microservices
1043
+ const templatePrompts = [
1044
+ {
1045
+ type: 'list',
1046
+ name: 'archTemplate',
1047
+ message: 'Select architecture template:',
1048
+ choices: answers => {
1049
+ if (answers.projectStructure === 'workspace') {
1050
+ return [
1051
+ { name: 'Basic Workspace (packages/)', value: 'workspace-basic' },
1052
+ { name: 'Layered (core/, api/, web/)', value: 'workspace-layered' },
1053
+ { name: 'Domain-Driven (domains/, shared/)', value: 'workspace-ddd' },
1054
+ { name: 'Full Stack (frontend/, backend/, shared/)', value: 'workspace-fullstack' },
1055
+ ];
1056
+ } else if (answers.projectStructure === 'microservices') {
1057
+ return [
1058
+ { name: 'Basic Services (services/)', value: 'microservices-basic' },
1059
+ { name: 'Gateway + Services', value: 'microservices-gateway' },
1060
+ { name: 'Event-Driven (services/, events/)', value: 'microservices-event' },
1061
+ ];
1062
+ }
1063
+ return [{ name: 'Standard', value: 'standard' }];
1064
+ },
1065
+ when: answers =>
1066
+ answers.projectStructure === 'workspace' || answers.projectStructure === 'microservices',
1067
+ },
1068
+ ];
1069
+
1070
+ // Language selection based on approach
1071
+ const languageChoices = [
1072
+ { name: 'JavaScript/TypeScript', value: 'javascript' },
1073
+ { name: 'Python', value: 'python' },
1074
+ { name: 'Rust', value: 'rust' },
1075
+ { name: 'Go', value: 'go' },
1076
+ { name: 'Java/Kotlin', value: 'java' },
1077
+ { name: 'C#/.NET', value: 'csharp' },
1078
+ { name: 'C/C++', value: 'cpp' },
1079
+ { name: 'Swift', value: 'swift' },
1080
+ { name: 'Ruby', value: 'ruby' },
1081
+ { name: 'PHP', value: 'php' },
1082
+ { name: 'Other', value: 'other' },
1083
+ ];
1084
+
1085
+ // Recommendation questions for 'Help me decide' mode
1086
+ const recommendationPrompts = [
1087
+ {
1088
+ type: 'checkbox',
1089
+ name: 'appTypes',
1090
+ message: 'What type of application(s) are you building?',
1091
+ choices: [
1092
+ { name: 'Web Frontend (SPA, SSR)', value: 'web-frontend' },
1093
+ { name: 'Web Backend / API', value: 'web-backend' },
1094
+ { name: 'CLI Tool', value: 'cli' },
1095
+ { name: 'Desktop Application', value: 'desktop' },
1096
+ { name: 'Mobile App', value: 'mobile' },
1097
+ { name: 'Data Pipeline / ETL', value: 'data' },
1098
+ { name: 'AI/ML Application', value: 'ml' },
1099
+ { name: 'Embedded / IoT', value: 'embedded' },
1100
+ { name: 'Game Development', value: 'game' },
1101
+ { name: 'Systems / Infrastructure', value: 'systems' },
1102
+ ],
1103
+ },
1104
+ {
1105
+ type: 'list',
1106
+ name: 'performanceNeeds',
1107
+ message: 'Performance requirements:',
1108
+ choices: [
1109
+ { name: 'High performance / Low latency critical', value: 'high' },
1110
+ { name: 'Moderate (typical web app)', value: 'moderate' },
1111
+ { name: 'Rapid development prioritized', value: 'rapid' },
1112
+ ],
1113
+ },
1114
+ {
1115
+ type: 'checkbox',
1116
+ name: 'teamExpertise',
1117
+ message: 'Team expertise (select all that apply):',
1118
+ choices: languageChoices.filter(c => c.value !== 'other'),
1119
+ },
101
1120
  ];
102
1121
 
103
- // Skills selection is only for Claude Code (Skills API exclusive)
1122
+ // Get initial answers to determine language prompts
1123
+ const initialAnswers = await inquirer.default.prompt(prompts);
1124
+ let answers = { ...initialAnswers };
1125
+
1126
+ // Handle tech stack approach
1127
+ if (answers.techStackApproach === 'single') {
1128
+ const langAnswer = await inquirer.default.prompt([
1129
+ {
1130
+ type: 'list',
1131
+ name: 'primaryLanguage',
1132
+ message: 'Select primary language:',
1133
+ choices: languageChoices,
1134
+ },
1135
+ ]);
1136
+ answers.languages = [langAnswer.primaryLanguage];
1137
+ } else if (answers.techStackApproach === 'multiple') {
1138
+ const langAnswer = await inquirer.default.prompt([
1139
+ {
1140
+ type: 'checkbox',
1141
+ name: 'languages',
1142
+ message: 'Select languages (check all that apply):',
1143
+ choices: languageChoices,
1144
+ validate: input => (input.length > 0 ? true : 'Select at least one language'),
1145
+ },
1146
+ ]);
1147
+ answers.languages = langAnswer.languages;
1148
+ } else if (answers.techStackApproach === 'recommend') {
1149
+ // Ask recommendation questions
1150
+ const recAnswers = await inquirer.default.prompt(recommendationPrompts);
1151
+ const recommended = recommendLanguages(recAnswers);
1152
+
1153
+ console.log(chalk.cyan('\n📊 Recommended languages based on your requirements:\n'));
1154
+ for (const rec of recommended) {
1155
+ console.log(chalk.white(` ${rec.emoji} ${chalk.bold(rec.name)}: ${rec.reason}`));
1156
+ }
1157
+ console.log('');
1158
+
1159
+ const confirmAnswer = await inquirer.default.prompt([
1160
+ {
1161
+ type: 'checkbox',
1162
+ name: 'languages',
1163
+ message: 'Confirm languages to use:',
1164
+ choices: recommended.map(r => ({ name: r.name, value: r.value, checked: true })),
1165
+ },
1166
+ ]);
1167
+ answers.languages = confirmAnswer.languages;
1168
+ answers.recommendationContext = recAnswers;
1169
+ } else {
1170
+ // undecided
1171
+ answers.languages = ['undecided'];
1172
+ }
1173
+
1174
+ // Ask template questions if workspace or microservices
1175
+ if (answers.projectStructure === 'workspace' || answers.projectStructure === 'microservices') {
1176
+ const templateAnswer = await inquirer.default.prompt(templatePrompts);
1177
+ answers = { ...answers, ...templateAnswer };
1178
+ }
1179
+
1180
+ // Continue with remaining prompts
1181
+ const remainingPrompts = [];
104
1182
  if (agentKey === 'claude-code' && agent.layout.skillsDir) {
105
- prompts.push({
1183
+ remainingPrompts.push({
106
1184
  type: 'checkbox',
107
1185
  name: 'skills',
108
1186
  message: 'Select skills to install (all recommended):',
@@ -147,7 +1225,7 @@ async function main(agent, agentKey) {
147
1225
  });
148
1226
  }
149
1227
 
150
- prompts.push(
1228
+ remainingPrompts.push(
151
1229
  {
152
1230
  type: 'confirm',
153
1231
  name: 'createSteering',
@@ -162,7 +1240,8 @@ async function main(agent, agentKey) {
162
1240
  }
163
1241
  );
164
1242
 
165
- const answers = await inquirer.default.prompt(prompts);
1243
+ const finalAnswers = await inquirer.default.prompt(remainingPrompts);
1244
+ answers = { ...answers, ...finalAnswers };
166
1245
 
167
1246
  console.log(chalk.green('\n✨ Initializing MUSUBI...\n'));
168
1247
 
@@ -253,16 +1332,44 @@ async function main(agent, agentKey) {
253
1332
 
254
1333
  // Generate steering context
255
1334
  if (answers.createSteering) {
256
- await generateSteering(answers);
1335
+ await generateSteering(answers, externalSpec);
257
1336
  console.log(chalk.green(' Generated steering context'));
258
1337
  }
259
1338
 
1339
+ // Save external specification reference
1340
+ if (externalSpec && !externalSpec.error) {
1341
+ const specFilename = await saveSpecReference(externalSpec, process.cwd());
1342
+ console.log(chalk.green(` Saved specification reference: steering/specs/${specFilename}`));
1343
+ }
1344
+
1345
+ // Save GitHub repository references and analysis
1346
+ if (referenceRepos && referenceRepos.length > 0 && repoAnalysis) {
1347
+ const refFilename = await saveReferenceRepos(referenceRepos, repoAnalysis, process.cwd());
1348
+ console.log(chalk.green(` Saved GitHub references: steering/references/${refFilename}`));
1349
+ }
1350
+
260
1351
  // Create constitution
261
1352
  if (answers.createConstitution) {
262
1353
  await createConstitution();
263
1354
  console.log(chalk.green(' Created constitutional governance'));
264
1355
  }
265
1356
 
1357
+ // Generate language-specific dependency files (for single-package projects)
1358
+ if (answers.projectStructure !== 'workspace' && answers.projectStructure !== 'microservices') {
1359
+ const primaryLang =
1360
+ answers.languages && answers.languages[0] !== 'undecided' ? answers.languages[0] : null;
1361
+ if (primaryLang) {
1362
+ await generateDependencyFiles(primaryLang, answers);
1363
+ console.log(chalk.green(` Generated ${primaryLang} project files`));
1364
+ }
1365
+ }
1366
+
1367
+ // Generate reference architecture template if specified
1368
+ if (options.template) {
1369
+ await generateArchitectureTemplate(options.template, answers);
1370
+ console.log(chalk.green(` Applied ${options.template} architecture template`));
1371
+ }
1372
+
266
1373
  // Create README
267
1374
  await createReadme(answers, agent, agentKey);
268
1375
  console.log(chalk.green(` Created ${agent.layout.docFile || 'MUSUBI.md'} guide`));
@@ -338,12 +1445,13 @@ async function copyAgentsFile(agent) {
338
1445
  }
339
1446
  }
340
1447
 
341
- async function generateSteering(answers) {
1448
+ async function generateSteering(answers, externalSpec = null) {
342
1449
  const steeringTemplates = path.join(SHARED_TEMPLATE_DIR, 'steering');
343
1450
  const locale = answers.locale || 'en';
1451
+ const languages = answers.languages || ['undecided'];
344
1452
 
345
1453
  // Copy and customize steering files
346
- const files = ['structure.md', 'tech.md', 'product.md'];
1454
+ const files = ['structure.md', 'product.md'];
347
1455
  for (const file of files) {
348
1456
  // Try locale-specific file first (e.g., structure.ja.md)
349
1457
  let templatePath = path.join(steeringTemplates, file.replace('.md', `.${locale}.md`));
@@ -371,15 +1479,849 @@ async function generateSteering(answers) {
371
1479
  await fs.writeFile(path.join('steering', outputFile), content);
372
1480
  }
373
1481
 
374
- // Create project.yml with locale setting
1482
+ // Generate tech.md based on selected languages
1483
+ const techContent = generateTechMd(languages, answers, locale);
1484
+ const techFile = locale !== 'en' ? `tech.${locale}.md` : 'tech.md';
1485
+ await fs.writeFile(path.join('steering', techFile), techContent);
1486
+
1487
+ // Build external specification section for project.yml
1488
+ let externalSpecYml = '';
1489
+ if (externalSpec && !externalSpec.error) {
1490
+ externalSpecYml = `
1491
+ # External Specification Reference
1492
+ external_specs:
1493
+ - source: "${externalSpec.source}"
1494
+ type: ${externalSpec.type}
1495
+ format: ${externalSpec.metadata.format || 'unknown'}
1496
+ title: "${externalSpec.metadata.summary?.title || 'External Specification'}"
1497
+ fetched_at: ${externalSpec.metadata.fetchedAt || externalSpec.metadata.readAt || 'N/A'}
1498
+ `;
1499
+ }
1500
+
1501
+ // Create project.yml with locale, language settings, and external spec
375
1502
  const projectYml = `# MUSUBI Project Configuration
376
1503
  name: ${answers.projectName}
377
1504
  description: ${answers.description}
378
1505
  locale: ${locale}
379
1506
  version: "0.1.0"
380
1507
  created: ${new Date().toISOString().split('T')[0]}
381
- `;
1508
+
1509
+ # Technology Stack
1510
+ tech_stack:
1511
+ approach: ${answers.techStackApproach}
1512
+ languages:
1513
+ ${languages[0] === 'undecided' ? ' - undecided # To be determined' : languages.map(l => ` - ${l}`).join('\n')}
1514
+ ${externalSpecYml}`;
382
1515
  await fs.writeFile(path.join('steering', 'project.yml'), projectYml);
1516
+
1517
+ // Generate workspace structure if applicable
1518
+ if (answers.projectStructure === 'workspace' || answers.projectStructure === 'microservices') {
1519
+ await generateWorkspaceStructure(answers);
1520
+ }
1521
+ }
1522
+
1523
+ /**
1524
+ * Generate workspace/monorepo structure based on template
1525
+ */
1526
+ async function generateWorkspaceStructure(answers) {
1527
+ const template = answers.archTemplate || 'workspace-basic';
1528
+ const languages = answers.languages || ['javascript'];
1529
+ const primaryLang = languages[0];
1530
+
1531
+ const structures = {
1532
+ 'workspace-basic': {
1533
+ dirs: ['packages/', 'packages/core/', 'packages/cli/', 'packages/web/'],
1534
+ files: {
1535
+ 'packages/README.md': '# Packages\n\nThis workspace contains multiple packages.\n',
1536
+ },
1537
+ },
1538
+ 'workspace-layered': {
1539
+ dirs: ['core/', 'api/', 'web/', 'shared/', 'tools/'],
1540
+ files: {
1541
+ 'core/README.md': '# Core\n\nBusiness logic and domain models.\n',
1542
+ 'api/README.md': '# API\n\nREST/GraphQL API layer.\n',
1543
+ 'web/README.md': '# Web\n\nFrontend application.\n',
1544
+ 'shared/README.md': '# Shared\n\nShared utilities and types.\n',
1545
+ },
1546
+ },
1547
+ 'workspace-ddd': {
1548
+ dirs: [
1549
+ 'domains/',
1550
+ 'domains/identity/',
1551
+ 'domains/catalog/',
1552
+ 'shared/',
1553
+ 'shared/kernel/',
1554
+ 'infrastructure/',
1555
+ ],
1556
+ files: {
1557
+ 'domains/README.md': '# Domains\n\nDomain-driven design bounded contexts.\n',
1558
+ 'shared/kernel/README.md': '# Shared Kernel\n\nCore abstractions shared across domains.\n',
1559
+ 'infrastructure/README.md': '# Infrastructure\n\nCross-cutting infrastructure concerns.\n',
1560
+ },
1561
+ },
1562
+ 'workspace-fullstack': {
1563
+ dirs: ['frontend/', 'backend/', 'shared/', 'e2e/', 'docs/'],
1564
+ files: {
1565
+ 'frontend/README.md': '# Frontend\n\nClient-side application.\n',
1566
+ 'backend/README.md': '# Backend\n\nServer-side application.\n',
1567
+ 'shared/README.md': '# Shared\n\nShared types and utilities.\n',
1568
+ 'e2e/README.md': '# E2E Tests\n\nEnd-to-end test suite.\n',
1569
+ },
1570
+ },
1571
+ 'microservices-basic': {
1572
+ dirs: ['services/', 'services/auth/', 'services/api/', 'services/worker/', 'libs/'],
1573
+ files: {
1574
+ 'services/README.md': '# Services\n\nMicroservices directory.\n',
1575
+ 'libs/README.md': '# Libraries\n\nShared libraries across services.\n',
1576
+ },
1577
+ },
1578
+ 'microservices-gateway': {
1579
+ dirs: ['gateway/', 'services/', 'services/users/', 'services/products/', 'shared/'],
1580
+ files: {
1581
+ 'gateway/README.md': '# API Gateway\n\nEntry point for all API requests.\n',
1582
+ 'services/README.md': '# Services\n\nBackend microservices.\n',
1583
+ },
1584
+ },
1585
+ 'microservices-event': {
1586
+ dirs: [
1587
+ 'services/',
1588
+ 'services/order/',
1589
+ 'services/inventory/',
1590
+ 'events/',
1591
+ 'events/schemas/',
1592
+ 'infrastructure/',
1593
+ ],
1594
+ files: {
1595
+ 'services/README.md': '# Services\n\nEvent-driven microservices.\n',
1596
+ 'events/README.md': '# Events\n\nEvent schemas and contracts.\n',
1597
+ 'events/schemas/README.md': '# Event Schemas\n\nAsyncAPI/CloudEvents schemas.\n',
1598
+ },
1599
+ },
1600
+ };
1601
+
1602
+ const structure = structures[template] || structures['workspace-basic'];
1603
+
1604
+ // Create directories
1605
+ for (const dir of structure.dirs) {
1606
+ await fs.ensureDir(dir);
1607
+ }
1608
+
1609
+ // Create files
1610
+ for (const [file, content] of Object.entries(structure.files)) {
1611
+ await fs.writeFile(file, content);
1612
+ }
1613
+
1614
+ // Generate language-specific workspace config
1615
+ await generateWorkspaceConfig(primaryLang, template, answers);
1616
+ }
1617
+
1618
+ /**
1619
+ * Generate language-specific workspace configuration files
1620
+ */
1621
+ async function generateWorkspaceConfig(primaryLang, template, answers) {
1622
+ const projectName = answers.projectName || 'my-project';
1623
+
1624
+ if (primaryLang === 'javascript') {
1625
+ // Generate pnpm-workspace.yaml or npm workspaces in package.json
1626
+ const workspaceConfig =
1627
+ template.startsWith('workspace') || template.startsWith('microservices')
1628
+ ? `packages:
1629
+ - 'packages/*'
1630
+ - 'services/*'
1631
+ - 'shared'
1632
+ - 'libs/*'
1633
+ `
1634
+ : '';
1635
+ if (workspaceConfig) {
1636
+ await fs.writeFile('pnpm-workspace.yaml', workspaceConfig);
1637
+ }
1638
+
1639
+ // Root package.json with workspaces
1640
+ const rootPackageJson = {
1641
+ name: projectName,
1642
+ version: '0.0.0',
1643
+ private: true,
1644
+ workspaces: ['packages/*', 'services/*', 'shared', 'libs/*'],
1645
+ scripts: {
1646
+ build: 'pnpm -r build',
1647
+ test: 'pnpm -r test',
1648
+ lint: 'pnpm -r lint',
1649
+ },
1650
+ devDependencies: {
1651
+ typescript: '^5.0.0',
1652
+ },
1653
+ };
1654
+ await fs.writeFile('package.json', JSON.stringify(rootPackageJson, null, 2) + '\n');
1655
+ } else if (primaryLang === 'rust') {
1656
+ // Generate Cargo workspace
1657
+ const members =
1658
+ template === 'workspace-basic'
1659
+ ? ['packages/*']
1660
+ : template === 'workspace-layered'
1661
+ ? ['core', 'api', 'shared']
1662
+ : template === 'microservices-basic'
1663
+ ? ['services/*', 'libs/*']
1664
+ : ['crates/*'];
1665
+
1666
+ const cargoToml = `[workspace]
1667
+ resolver = "2"
1668
+ members = [
1669
+ ${members.map(m => ` "${m}"`).join(',\n')}
1670
+ ]
1671
+
1672
+ [workspace.package]
1673
+ version = "0.1.0"
1674
+ edition = "2021"
1675
+ authors = ["${projectName} Team"]
1676
+ license = "MIT"
1677
+
1678
+ [workspace.dependencies]
1679
+ # Add shared dependencies here
1680
+ tokio = { version = "1", features = ["full"] }
1681
+ serde = { version = "1", features = ["derive"] }
1682
+ `;
1683
+ await fs.writeFile('Cargo.toml', cargoToml);
1684
+ } else if (primaryLang === 'python') {
1685
+ // Generate pyproject.toml for monorepo
1686
+ const pyprojectToml = `[project]
1687
+ name = "${projectName}"
1688
+ version = "0.1.0"
1689
+ description = "${answers.description || ''}"
1690
+ requires-python = ">=3.11"
1691
+
1692
+ [tool.ruff]
1693
+ line-length = 100
1694
+
1695
+ [tool.pytest.ini_options]
1696
+ testpaths = ["tests"]
1697
+ `;
1698
+ await fs.writeFile('pyproject.toml', pyprojectToml);
1699
+ } else if (primaryLang === 'go') {
1700
+ // Generate go.work for Go workspaces
1701
+ const goWork = `go 1.21
1702
+
1703
+ use (
1704
+ ./cmd
1705
+ ./internal
1706
+ ./pkg
1707
+ )
1708
+ `;
1709
+ await fs.writeFile('go.work', goWork);
1710
+ }
1711
+ }
1712
+
1713
+ /**
1714
+ * Generate reference architecture template structure
1715
+ */
1716
+ async function generateArchitectureTemplate(templateName, answers) {
1717
+ const ARCH_TEMPLATE_DIR = path.join(__dirname, '..', 'src', 'templates', 'architectures');
1718
+ const languages = answers.languages || ['javascript'];
1719
+ const primaryLang = languages[0];
1720
+
1721
+ const architectures = {
1722
+ 'clean-architecture': {
1723
+ dirs: [
1724
+ 'src/domain/entities/',
1725
+ 'src/domain/value-objects/',
1726
+ 'src/domain/services/',
1727
+ 'src/domain/errors/',
1728
+ 'src/application/use-cases/',
1729
+ 'src/application/ports/input/',
1730
+ 'src/application/ports/output/',
1731
+ 'src/application/dtos/',
1732
+ 'src/infrastructure/persistence/repositories/',
1733
+ 'src/infrastructure/persistence/mappers/',
1734
+ 'src/infrastructure/external/',
1735
+ 'src/interface/controllers/',
1736
+ 'src/interface/presenters/',
1737
+ 'src/interface/cli/',
1738
+ ],
1739
+ readme: 'clean-architecture/README.md',
1740
+ },
1741
+ hexagonal: {
1742
+ dirs: [
1743
+ 'src/core/domain/models/',
1744
+ 'src/core/domain/events/',
1745
+ 'src/core/domain/services/',
1746
+ 'src/core/ports/inbound/',
1747
+ 'src/core/ports/outbound/',
1748
+ 'src/core/application/commands/',
1749
+ 'src/core/application/queries/',
1750
+ 'src/adapters/inbound/http/',
1751
+ 'src/adapters/inbound/cli/',
1752
+ 'src/adapters/outbound/persistence/',
1753
+ 'src/adapters/outbound/messaging/',
1754
+ ],
1755
+ readme: 'hexagonal/README.md',
1756
+ },
1757
+ 'event-driven': {
1758
+ dirs: [
1759
+ 'src/domain/events/',
1760
+ 'src/domain/aggregates/',
1761
+ 'src/domain/commands/',
1762
+ 'src/application/command-handlers/',
1763
+ 'src/application/event-handlers/',
1764
+ 'src/application/sagas/',
1765
+ 'src/application/projections/',
1766
+ 'src/infrastructure/messaging/',
1767
+ 'src/infrastructure/event-store/',
1768
+ 'src/interface/api/',
1769
+ 'src/interface/consumers/',
1770
+ 'src/interface/publishers/',
1771
+ ],
1772
+ readme: 'event-driven/README.md',
1773
+ },
1774
+ layered: {
1775
+ dirs: [
1776
+ 'src/presentation/controllers/',
1777
+ 'src/presentation/views/',
1778
+ 'src/business/services/',
1779
+ 'src/business/models/',
1780
+ 'src/data/repositories/',
1781
+ 'src/data/entities/',
1782
+ ],
1783
+ readme: null,
1784
+ },
1785
+ 'modular-monolith': {
1786
+ dirs: [
1787
+ 'src/modules/users/',
1788
+ 'src/modules/users/domain/',
1789
+ 'src/modules/users/application/',
1790
+ 'src/modules/users/infrastructure/',
1791
+ 'src/modules/orders/',
1792
+ 'src/modules/orders/domain/',
1793
+ 'src/modules/orders/application/',
1794
+ 'src/modules/orders/infrastructure/',
1795
+ 'src/shared/kernel/',
1796
+ 'src/shared/infrastructure/',
1797
+ ],
1798
+ readme: null,
1799
+ },
1800
+ };
1801
+
1802
+ const arch = architectures[templateName];
1803
+ if (!arch) {
1804
+ console.log(chalk.yellow(` Unknown architecture template: ${templateName}`));
1805
+ return;
1806
+ }
1807
+
1808
+ // Create directories
1809
+ for (const dir of arch.dirs) {
1810
+ await fs.ensureDir(dir);
1811
+ // Create .gitkeep to preserve empty directories
1812
+ await fs.writeFile(path.join(dir, '.gitkeep'), '');
1813
+ }
1814
+
1815
+ // Copy architecture README if available
1816
+ if (arch.readme) {
1817
+ const readmePath = path.join(ARCH_TEMPLATE_DIR, arch.readme);
1818
+ if (await fs.pathExists(readmePath)) {
1819
+ const destPath = path.join('docs', 'architecture', 'README.md');
1820
+ await fs.ensureDir(path.dirname(destPath));
1821
+ await fs.copy(readmePath, destPath);
1822
+ }
1823
+ }
1824
+
1825
+ // Generate language-specific entry files
1826
+ await generateArchitectureEntryFiles(templateName, primaryLang, answers);
1827
+ }
1828
+
1829
+ /**
1830
+ * Generate entry files for architecture template
1831
+ */
1832
+ async function generateArchitectureEntryFiles(templateName, primaryLang, answers) {
1833
+ const projectName = answers.projectName || 'my-project';
1834
+
1835
+ // Create a basic entry file based on language
1836
+ if (primaryLang === 'javascript' || primaryLang === 'typescript') {
1837
+ const entryFile = `// ${projectName} - ${templateName}
1838
+ // Entry point for the application
1839
+
1840
+ export function main(): void {
1841
+ console.log('Hello from ${projectName}!');
1842
+ }
1843
+
1844
+ main();
1845
+ `;
1846
+ await fs.ensureDir('src');
1847
+ await fs.writeFile('src/index.ts', entryFile);
1848
+ } else if (primaryLang === 'rust') {
1849
+ const mainRs = `//! ${projectName} - ${templateName}
1850
+ //!
1851
+ //! Entry point for the application
1852
+
1853
+ fn main() {
1854
+ println!("Hello from ${projectName}!");
1855
+ }
1856
+ `;
1857
+ await fs.ensureDir('src');
1858
+ await fs.writeFile('src/main.rs', mainRs);
1859
+ } else if (primaryLang === 'python') {
1860
+ const safeName = projectName.toLowerCase().replace(/[^a-z0-9_]/g, '_');
1861
+ const mainPy = `"""${projectName} - ${templateName}
1862
+
1863
+ Entry point for the application
1864
+ """
1865
+
1866
+
1867
+ def main() -> None:
1868
+ print(f"Hello from ${projectName}!")
1869
+
1870
+
1871
+ if __name__ == "__main__":
1872
+ main()
1873
+ `;
1874
+ const srcDir = `src/${safeName}`;
1875
+ await fs.ensureDir(srcDir);
1876
+ await fs.writeFile(`${srcDir}/__main__.py`, mainPy);
1877
+ }
1878
+ }
1879
+
1880
+ /**
1881
+ * Generate language-specific dependency files for single-package projects
1882
+ */
1883
+ async function generateDependencyFiles(primaryLang, answers) {
1884
+ const projectName = answers.projectName || 'my-project';
1885
+ const safeName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
1886
+
1887
+ if (primaryLang === 'javascript') {
1888
+ // Check if package.json already exists
1889
+ if (!(await fs.pathExists('package.json'))) {
1890
+ const packageJson = {
1891
+ name: safeName,
1892
+ version: '0.1.0',
1893
+ description: answers.description || '',
1894
+ type: 'module',
1895
+ main: 'dist/index.js',
1896
+ types: 'dist/index.d.ts',
1897
+ scripts: {
1898
+ build: 'tsc',
1899
+ test: 'jest',
1900
+ lint: 'eslint src/',
1901
+ format: 'prettier --write .',
1902
+ },
1903
+ devDependencies: {
1904
+ typescript: '^5.0.0',
1905
+ '@types/node': '^20.0.0',
1906
+ jest: '^29.0.0',
1907
+ '@types/jest': '^29.0.0',
1908
+ eslint: '^9.0.0',
1909
+ prettier: '^3.0.0',
1910
+ },
1911
+ };
1912
+ await fs.writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n');
1913
+ }
1914
+
1915
+ // Generate tsconfig.json
1916
+ if (!(await fs.pathExists('tsconfig.json'))) {
1917
+ const tsconfig = {
1918
+ compilerOptions: {
1919
+ target: 'ES2022',
1920
+ module: 'NodeNext',
1921
+ moduleResolution: 'NodeNext',
1922
+ declaration: true,
1923
+ outDir: './dist',
1924
+ rootDir: './src',
1925
+ strict: true,
1926
+ esModuleInterop: true,
1927
+ skipLibCheck: true,
1928
+ forceConsistentCasingInFileNames: true,
1929
+ },
1930
+ include: ['src/**/*'],
1931
+ exclude: ['node_modules', 'dist'],
1932
+ };
1933
+ await fs.writeFile('tsconfig.json', JSON.stringify(tsconfig, null, 2) + '\n');
1934
+ }
1935
+ } else if (primaryLang === 'rust') {
1936
+ // Check if Cargo.toml already exists
1937
+ if (!(await fs.pathExists('Cargo.toml'))) {
1938
+ const cargoToml = `[package]
1939
+ name = "${safeName}"
1940
+ version = "0.1.0"
1941
+ edition = "2021"
1942
+ description = "${answers.description || ''}"
1943
+ license = "MIT"
1944
+
1945
+ [dependencies]
1946
+ tokio = { version = "1", features = ["full"] }
1947
+ serde = { version = "1", features = ["derive"] }
1948
+ serde_json = "1"
1949
+ thiserror = "1"
1950
+ tracing = "0.1"
1951
+
1952
+ [dev-dependencies]
1953
+ tokio-test = "0.4"
1954
+ `;
1955
+ await fs.writeFile('Cargo.toml', cargoToml);
1956
+
1957
+ // Create src/main.rs or src/lib.rs
1958
+ await fs.ensureDir('src');
1959
+ if (!(await fs.pathExists('src/main.rs')) && !(await fs.pathExists('src/lib.rs'))) {
1960
+ const mainRs = `//! ${answers.description || projectName}
1961
+
1962
+ fn main() {
1963
+ println!("Hello from ${projectName}!");
1964
+ }
1965
+ `;
1966
+ await fs.writeFile('src/main.rs', mainRs);
1967
+ }
1968
+ }
1969
+ } else if (primaryLang === 'python') {
1970
+ // Check if pyproject.toml already exists
1971
+ if (!(await fs.pathExists('pyproject.toml'))) {
1972
+ const pyprojectToml = `[project]
1973
+ name = "${safeName}"
1974
+ version = "0.1.0"
1975
+ description = "${answers.description || ''}"
1976
+ requires-python = ">=3.11"
1977
+ dependencies = []
1978
+
1979
+ [project.optional-dependencies]
1980
+ dev = [
1981
+ "pytest>=7.0",
1982
+ "ruff>=0.1",
1983
+ "mypy>=1.0",
1984
+ ]
1985
+
1986
+ [tool.ruff]
1987
+ line-length = 100
1988
+ target-version = "py311"
1989
+
1990
+ [tool.ruff.lint]
1991
+ select = ["E", "F", "I", "N", "W"]
1992
+
1993
+ [tool.mypy]
1994
+ python_version = "3.11"
1995
+ strict = true
1996
+
1997
+ [tool.pytest.ini_options]
1998
+ testpaths = ["tests"]
1999
+ `;
2000
+ await fs.writeFile('pyproject.toml', pyprojectToml);
2001
+
2002
+ // Create src directory and __init__.py
2003
+ const srcDir = `src/${safeName.replace(/-/g, '_')}`;
2004
+ await fs.ensureDir(srcDir);
2005
+ if (!(await fs.pathExists(`${srcDir}/__init__.py`))) {
2006
+ await fs.writeFile(
2007
+ `${srcDir}/__init__.py`,
2008
+ `"""${answers.description || projectName}"""\n\n__version__ = "0.1.0"\n`
2009
+ );
2010
+ }
2011
+ }
2012
+ } else if (primaryLang === 'go') {
2013
+ // Check if go.mod already exists
2014
+ if (!(await fs.pathExists('go.mod'))) {
2015
+ const goMod = `module github.com/${safeName}
2016
+
2017
+ go 1.21
2018
+
2019
+ require (
2020
+ // Add dependencies here
2021
+ )
2022
+ `;
2023
+ await fs.writeFile('go.mod', goMod);
2024
+
2025
+ // Create main.go
2026
+ await fs.ensureDir('cmd');
2027
+ if (!(await fs.pathExists('cmd/main.go'))) {
2028
+ const mainGo = `package main
2029
+
2030
+ import "fmt"
2031
+
2032
+ func main() {
2033
+ fmt.Println("Hello from ${projectName}!")
2034
+ }
2035
+ `;
2036
+ await fs.writeFile('cmd/main.go', mainGo);
2037
+ }
2038
+ }
2039
+ } else if (primaryLang === 'java') {
2040
+ // Generate pom.xml for Maven
2041
+ if (!(await fs.pathExists('pom.xml'))) {
2042
+ const pomXml = `<?xml version="1.0" encoding="UTF-8"?>
2043
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
2044
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2045
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2046
+ <modelVersion>4.0.0</modelVersion>
2047
+
2048
+ <groupId>com.example</groupId>
2049
+ <artifactId>${safeName}</artifactId>
2050
+ <version>0.1.0</version>
2051
+ <packaging>jar</packaging>
2052
+
2053
+ <name>${projectName}</name>
2054
+ <description>${answers.description || ''}</description>
2055
+
2056
+ <properties>
2057
+ <maven.compiler.source>21</maven.compiler.source>
2058
+ <maven.compiler.target>21</maven.compiler.target>
2059
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2060
+ </properties>
2061
+
2062
+ <dependencies>
2063
+ <dependency>
2064
+ <groupId>org.junit.jupiter</groupId>
2065
+ <artifactId>junit-jupiter</artifactId>
2066
+ <version>5.10.0</version>
2067
+ <scope>test</scope>
2068
+ </dependency>
2069
+ </dependencies>
2070
+ </project>
2071
+ `;
2072
+ await fs.writeFile('pom.xml', pomXml);
2073
+ }
2074
+ } else if (primaryLang === 'csharp') {
2075
+ // Generate .csproj file
2076
+ const csprojPath = `${projectName}.csproj`;
2077
+ if (!(await fs.pathExists(csprojPath))) {
2078
+ const csproj = `<Project Sdk="Microsoft.NET.Sdk">
2079
+
2080
+ <PropertyGroup>
2081
+ <OutputType>Exe</OutputType>
2082
+ <TargetFramework>net8.0</TargetFramework>
2083
+ <ImplicitUsings>enable</ImplicitUsings>
2084
+ <Nullable>enable</Nullable>
2085
+ </PropertyGroup>
2086
+
2087
+ <ItemGroup>
2088
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
2089
+ </ItemGroup>
2090
+
2091
+ </Project>
2092
+ `;
2093
+ await fs.writeFile(csprojPath, csproj);
2094
+ }
2095
+ }
2096
+ }
2097
+
2098
+ /**
2099
+ * Generate language-specific tech.md content
2100
+ */
2101
+ function generateTechMd(languages, answers, _locale) {
2102
+ const langInfo = {
2103
+ javascript: {
2104
+ name: 'JavaScript/TypeScript',
2105
+ version: 'ES2022+ / TypeScript 5.0+',
2106
+ runtime: 'Node.js 20+ LTS, Bun, Deno',
2107
+ packageManager: 'npm, pnpm, yarn',
2108
+ frameworks: 'React, Vue, Next.js, Express, Fastify',
2109
+ testing: 'Jest, Vitest, Playwright',
2110
+ },
2111
+ python: {
2112
+ name: 'Python',
2113
+ version: '3.11+',
2114
+ runtime: 'CPython, PyPy',
2115
+ packageManager: 'pip, poetry, uv',
2116
+ frameworks: 'FastAPI, Django, Flask',
2117
+ testing: 'pytest, unittest',
2118
+ },
2119
+ rust: {
2120
+ name: 'Rust',
2121
+ version: '1.75+ stable',
2122
+ runtime: 'Native binary',
2123
+ packageManager: 'Cargo',
2124
+ frameworks: 'Axum, Actix-web, Tokio',
2125
+ testing: 'cargo test, criterion',
2126
+ },
2127
+ go: {
2128
+ name: 'Go',
2129
+ version: '1.21+',
2130
+ runtime: 'Native binary',
2131
+ packageManager: 'Go modules',
2132
+ frameworks: 'Gin, Echo, Chi',
2133
+ testing: 'go test, testify',
2134
+ },
2135
+ java: {
2136
+ name: 'Java/Kotlin',
2137
+ version: 'Java 21 LTS / Kotlin 1.9+',
2138
+ runtime: 'JVM, GraalVM',
2139
+ packageManager: 'Maven, Gradle',
2140
+ frameworks: 'Spring Boot, Quarkus, Ktor',
2141
+ testing: 'JUnit 5, Kotest',
2142
+ },
2143
+ csharp: {
2144
+ name: 'C#/.NET',
2145
+ version: '.NET 8+',
2146
+ runtime: '.NET Runtime',
2147
+ packageManager: 'NuGet',
2148
+ frameworks: 'ASP.NET Core, MAUI',
2149
+ testing: 'xUnit, NUnit',
2150
+ },
2151
+ cpp: {
2152
+ name: 'C/C++',
2153
+ version: 'C++20',
2154
+ runtime: 'Native binary',
2155
+ packageManager: 'vcpkg, Conan',
2156
+ frameworks: 'Qt, Boost',
2157
+ testing: 'GoogleTest, Catch2',
2158
+ },
2159
+ swift: {
2160
+ name: 'Swift',
2161
+ version: '5.9+',
2162
+ runtime: 'Native binary',
2163
+ packageManager: 'Swift Package Manager',
2164
+ frameworks: 'SwiftUI, Vapor',
2165
+ testing: 'XCTest',
2166
+ },
2167
+ ruby: {
2168
+ name: 'Ruby',
2169
+ version: '3.2+',
2170
+ runtime: 'CRuby, JRuby',
2171
+ packageManager: 'Bundler, RubyGems',
2172
+ frameworks: 'Rails, Sinatra',
2173
+ testing: 'RSpec, Minitest',
2174
+ },
2175
+ php: {
2176
+ name: 'PHP',
2177
+ version: '8.2+',
2178
+ runtime: 'PHP-FPM, Swoole',
2179
+ packageManager: 'Composer',
2180
+ frameworks: 'Laravel, Symfony',
2181
+ testing: 'PHPUnit, Pest',
2182
+ },
2183
+ };
2184
+
2185
+ const isUndecided = languages[0] === 'undecided';
2186
+ const date = new Date().toISOString().split('T')[0];
2187
+
2188
+ if (isUndecided) {
2189
+ return `# Technology Stack
2190
+
2191
+ **Project**: ${answers.projectName}
2192
+ **Last Updated**: ${date}
2193
+ **Status**: Technology stack to be determined
2194
+
2195
+ ---
2196
+
2197
+ ## Overview
2198
+
2199
+ The technology stack for this project has not yet been decided. This document will be updated once the technical decisions are made.
2200
+
2201
+ ## Decision Criteria
2202
+
2203
+ When selecting technologies, consider:
2204
+
2205
+ 1. **Application Type**: What type of application is being built?
2206
+ 2. **Performance Requirements**: What are the performance constraints?
2207
+ 3. **Team Expertise**: What technologies is the team familiar with?
2208
+ 4. **Ecosystem**: What libraries and tools are available?
2209
+ 5. **Long-term Maintainability**: How well-supported is the technology?
2210
+
2211
+ ## Candidates Under Consideration
2212
+
2213
+ | Aspect | Options | Decision |
2214
+ |--------|---------|----------|
2215
+ | Primary Language | TBD | ⏳ Pending |
2216
+ | Web Framework | TBD | ⏳ Pending |
2217
+ | Database | TBD | ⏳ Pending |
2218
+ | Hosting | TBD | ⏳ Pending |
2219
+
2220
+ ## Next Steps
2221
+
2222
+ 1. [ ] Define functional requirements
2223
+ 2. [ ] Identify performance constraints
2224
+ 3. [ ] Evaluate team skills
2225
+ 4. [ ] Create proof-of-concept
2226
+ 5. [ ] Make final decision and update this document
2227
+
2228
+ ---
2229
+
2230
+ *Run \`musubi steering\` to update this document after decisions are made.*
2231
+ `;
2232
+ }
2233
+
2234
+ // Generate tech.md for selected languages
2235
+ const primaryLang = languages[0];
2236
+ const primary = langInfo[primaryLang] || { name: primaryLang, version: 'Latest' };
2237
+
2238
+ let languageTable = `### Programming Languages
2239
+
2240
+ | Language | Version | Role | Notes |
2241
+ |----------|---------|------|-------|
2242
+ `;
2243
+
2244
+ for (let i = 0; i < languages.length; i++) {
2245
+ const lang = languages[i];
2246
+ const info = langInfo[lang] || { name: lang, version: 'Latest' };
2247
+ const role = i === 0 ? 'Primary' : 'Secondary';
2248
+ languageTable += `| ${info.name} | ${info.version} | ${role} | ${info.runtime || ''} |\n`;
2249
+ }
2250
+
2251
+ let frameworksSection = '';
2252
+ for (const lang of languages) {
2253
+ const info = langInfo[lang];
2254
+ if (info && info.frameworks) {
2255
+ frameworksSection += `
2256
+ ### ${info.name} Ecosystem
2257
+
2258
+ - **Package Manager**: ${info.packageManager}
2259
+ - **Frameworks**: ${info.frameworks}
2260
+ - **Testing**: ${info.testing}
2261
+ `;
2262
+ }
2263
+ }
2264
+
2265
+ return `# Technology Stack
2266
+
2267
+ **Project**: ${answers.projectName}
2268
+ **Last Updated**: ${date}
2269
+ **Version**: 0.1.0
2270
+
2271
+ ---
2272
+
2273
+ ## Overview
2274
+
2275
+ ${answers.description}
2276
+
2277
+ ---
2278
+
2279
+ ## Primary Technologies
2280
+
2281
+ ${languageTable}
2282
+ ${frameworksSection}
2283
+
2284
+ ---
2285
+
2286
+ ## Development Environment
2287
+
2288
+ ### Required Tools
2289
+
2290
+ - Primary language runtime (see above)
2291
+ - Git 2.40+
2292
+ - IDE: VS Code / JetBrains / Neovim
2293
+
2294
+ ### Recommended Extensions
2295
+
2296
+ - Language-specific LSP
2297
+ - Linter/Formatter integration
2298
+ - Test runner integration
2299
+
2300
+ ---
2301
+
2302
+ ## Architecture Decisions
2303
+
2304
+ | Decision | Choice | Rationale |
2305
+ |----------|--------|-----------|
2306
+ | Primary Language | ${primary.name} | Selected during project initialization |
2307
+ | Package Manager | ${primary.packageManager || 'TBD'} | Standard for ${primary.name} |
2308
+
2309
+ ---
2310
+
2311
+ ## Dependencies
2312
+
2313
+ ### Production Dependencies
2314
+
2315
+ *To be documented as dependencies are added.*
2316
+
2317
+ ### Development Dependencies
2318
+
2319
+ *To be documented as dependencies are added.*
2320
+
2321
+ ---
2322
+
2323
+ *Generated by MUSUBI SDD - Update with \`musubi steering\`*
2324
+ `;
383
2325
  }
384
2326
 
385
2327
  async function createConstitution() {