musubi-sdd 6.2.2 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +3 -3
- package/README.md +3 -3
- package/bin/musubi-dashboard.js +22 -13
- package/bin/musubi-design.js +3 -3
- package/bin/musubi-gaps.js +9 -9
- package/bin/musubi-init.js +14 -1310
- package/bin/musubi-requirements.js +1 -1
- package/bin/musubi-tasks.js +5 -5
- package/bin/musubi-trace.js +23 -23
- package/bin/musubi-upgrade.js +7 -2
- package/bin/musubi.js +1 -1
- package/package.json +2 -2
- package/src/analyzers/gap-detector.js +3 -3
- package/src/analyzers/traceability.js +17 -17
- package/src/cli/dashboard-cli.js +54 -60
- package/src/cli/init-generators.js +464 -0
- package/src/cli/init-helpers.js +884 -0
- package/src/constitutional/checker.js +67 -65
- package/src/constitutional/ci-reporter.js +50 -43
- package/src/constitutional/index.js +2 -2
- package/src/constitutional/phase-minus-one.js +22 -25
- package/src/constitutional/steering-sync.js +28 -39
- package/src/dashboard/index.js +2 -2
- package/src/dashboard/sprint-planner.js +17 -19
- package/src/dashboard/sprint-reporter.js +46 -37
- package/src/dashboard/transition-recorder.js +12 -18
- package/src/dashboard/workflow-dashboard.js +27 -38
- package/src/enterprise/error-recovery.js +109 -49
- package/src/enterprise/experiment-report.js +62 -36
- package/src/enterprise/index.js +5 -5
- package/src/enterprise/rollback-manager.js +28 -29
- package/src/enterprise/tech-article.js +41 -35
- package/src/generators/design.js +3 -3
- package/src/generators/requirements.js +5 -3
- package/src/generators/tasks.js +2 -2
- package/src/integrations/platforms.js +1 -1
- package/src/templates/agents/claude-code/CLAUDE.md +1 -1
- package/src/templates/agents/claude-code/skills/design-reviewer/SKILL.md +132 -113
- package/src/templates/agents/claude-code/skills/requirements-reviewer/SKILL.md +85 -56
- package/src/templates/agents/codex/AGENTS.md +2 -2
- package/src/templates/agents/cursor/AGENTS.md +2 -2
- package/src/templates/agents/gemini-cli/GEMINI.md +2 -2
- package/src/templates/agents/github-copilot/AGENTS.md +2 -2
- package/src/templates/agents/github-copilot/commands/sdd-requirements.prompt.md +23 -4
- package/src/templates/agents/qwen-code/QWEN.md +2 -2
- package/src/templates/agents/shared/AGENTS.md +1 -1
- package/src/templates/agents/windsurf/AGENTS.md +2 -2
- package/src/templates/skills/browser-agent.md +1 -1
- package/src/traceability/extractor.js +21 -20
- package/src/traceability/gap-detector.js +19 -17
- package/src/traceability/index.js +2 -2
- package/src/traceability/matrix-storage.js +20 -22
- package/src/validators/constitution.js +5 -2
- package/src/validators/critic-system.js +6 -6
- package/src/validators/traceability-validator.js +3 -3
package/bin/musubi-init.js
CHANGED
|
@@ -22,871 +22,22 @@ const fs = require('fs-extra');
|
|
|
22
22
|
const path = require('path');
|
|
23
23
|
const chalk = require('chalk');
|
|
24
24
|
|
|
25
|
+
// Import helpers from separate modules to reduce file size
|
|
26
|
+
const {
|
|
27
|
+
fetchExternalSpec,
|
|
28
|
+
fetchGitHubRepos,
|
|
29
|
+
analyzeReposForImprovements,
|
|
30
|
+
saveReferenceRepos,
|
|
31
|
+
saveSpecReference,
|
|
32
|
+
recommendLanguages,
|
|
33
|
+
} = require('../src/cli/init-helpers');
|
|
34
|
+
|
|
35
|
+
const { generateDependencyFiles, generateTechMd } = require('../src/cli/init-generators');
|
|
36
|
+
|
|
25
37
|
const TEMPLATE_DIR = path.join(__dirname, '..', 'src', 'templates');
|
|
26
38
|
const SHARED_TEMPLATE_DIR = path.join(TEMPLATE_DIR, 'shared');
|
|
27
39
|
const AGENTS_TEMPLATE_DIR = path.join(TEMPLATE_DIR, 'agents');
|
|
28
40
|
|
|
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
|
-
|
|
890
41
|
/**
|
|
891
42
|
* Main initialization function
|
|
892
43
|
* @param {object} agent - Agent definition from registry
|
|
@@ -1389,7 +540,7 @@ async function main(agent, agentKey, options = {}) {
|
|
|
1389
540
|
|
|
1390
541
|
const cmdExample = agent.commands.requirements.replace(' <feature>', ' authentication');
|
|
1391
542
|
console.log(chalk.gray(` 4. Try commands: ${cmdExample}\n`));
|
|
1392
|
-
console.log(chalk.cyan('Learn more: https://github.com/
|
|
543
|
+
console.log(chalk.cyan('Learn more: https://github.com/nahisaho/MUSUBI\n'));
|
|
1393
544
|
}
|
|
1394
545
|
|
|
1395
546
|
async function copySkill(skillName, agent) {
|
|
@@ -1878,453 +1029,6 @@ if __name__ == "__main__":
|
|
|
1878
1029
|
}
|
|
1879
1030
|
}
|
|
1880
1031
|
|
|
1881
|
-
/**
|
|
1882
|
-
* Generate language-specific dependency files for single-package projects
|
|
1883
|
-
*/
|
|
1884
|
-
async function generateDependencyFiles(primaryLang, answers) {
|
|
1885
|
-
const projectName = answers.projectName || 'my-project';
|
|
1886
|
-
const safeName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
1887
|
-
|
|
1888
|
-
if (primaryLang === 'javascript') {
|
|
1889
|
-
// Check if package.json already exists
|
|
1890
|
-
if (!(await fs.pathExists('package.json'))) {
|
|
1891
|
-
const packageJson = {
|
|
1892
|
-
name: safeName,
|
|
1893
|
-
version: '0.1.0',
|
|
1894
|
-
description: answers.description || '',
|
|
1895
|
-
type: 'module',
|
|
1896
|
-
main: 'dist/index.js',
|
|
1897
|
-
types: 'dist/index.d.ts',
|
|
1898
|
-
scripts: {
|
|
1899
|
-
build: 'tsc',
|
|
1900
|
-
test: 'jest',
|
|
1901
|
-
lint: 'eslint src/',
|
|
1902
|
-
format: 'prettier --write .',
|
|
1903
|
-
},
|
|
1904
|
-
devDependencies: {
|
|
1905
|
-
typescript: '^5.0.0',
|
|
1906
|
-
'@types/node': '^20.0.0',
|
|
1907
|
-
jest: '^29.0.0',
|
|
1908
|
-
'@types/jest': '^29.0.0',
|
|
1909
|
-
eslint: '^9.0.0',
|
|
1910
|
-
prettier: '^3.0.0',
|
|
1911
|
-
},
|
|
1912
|
-
};
|
|
1913
|
-
await fs.writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n');
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
// Generate tsconfig.json
|
|
1917
|
-
if (!(await fs.pathExists('tsconfig.json'))) {
|
|
1918
|
-
const tsconfig = {
|
|
1919
|
-
compilerOptions: {
|
|
1920
|
-
target: 'ES2022',
|
|
1921
|
-
module: 'NodeNext',
|
|
1922
|
-
moduleResolution: 'NodeNext',
|
|
1923
|
-
declaration: true,
|
|
1924
|
-
outDir: './dist',
|
|
1925
|
-
rootDir: './src',
|
|
1926
|
-
strict: true,
|
|
1927
|
-
esModuleInterop: true,
|
|
1928
|
-
skipLibCheck: true,
|
|
1929
|
-
forceConsistentCasingInFileNames: true,
|
|
1930
|
-
},
|
|
1931
|
-
include: ['src/**/*'],
|
|
1932
|
-
exclude: ['node_modules', 'dist'],
|
|
1933
|
-
};
|
|
1934
|
-
await fs.writeFile('tsconfig.json', JSON.stringify(tsconfig, null, 2) + '\n');
|
|
1935
|
-
}
|
|
1936
|
-
} else if (primaryLang === 'rust') {
|
|
1937
|
-
// Check if Cargo.toml already exists
|
|
1938
|
-
if (!(await fs.pathExists('Cargo.toml'))) {
|
|
1939
|
-
const cargoToml = `[package]
|
|
1940
|
-
name = "${safeName}"
|
|
1941
|
-
version = "0.1.0"
|
|
1942
|
-
edition = "2021"
|
|
1943
|
-
description = "${answers.description || ''}"
|
|
1944
|
-
license = "MIT"
|
|
1945
|
-
|
|
1946
|
-
[dependencies]
|
|
1947
|
-
tokio = { version = "1", features = ["full"] }
|
|
1948
|
-
serde = { version = "1", features = ["derive"] }
|
|
1949
|
-
serde_json = "1"
|
|
1950
|
-
thiserror = "1"
|
|
1951
|
-
tracing = "0.1"
|
|
1952
|
-
|
|
1953
|
-
[dev-dependencies]
|
|
1954
|
-
tokio-test = "0.4"
|
|
1955
|
-
`;
|
|
1956
|
-
await fs.writeFile('Cargo.toml', cargoToml);
|
|
1957
|
-
|
|
1958
|
-
// Create src/main.rs or src/lib.rs
|
|
1959
|
-
await fs.ensureDir('src');
|
|
1960
|
-
if (!(await fs.pathExists('src/main.rs')) && !(await fs.pathExists('src/lib.rs'))) {
|
|
1961
|
-
const mainRs = `//! ${answers.description || projectName}
|
|
1962
|
-
|
|
1963
|
-
fn main() {
|
|
1964
|
-
println!("Hello from ${projectName}!");
|
|
1965
|
-
}
|
|
1966
|
-
`;
|
|
1967
|
-
await fs.writeFile('src/main.rs', mainRs);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
} else if (primaryLang === 'python') {
|
|
1971
|
-
// Check if pyproject.toml already exists
|
|
1972
|
-
if (!(await fs.pathExists('pyproject.toml'))) {
|
|
1973
|
-
const pyprojectToml = `[project]
|
|
1974
|
-
name = "${safeName}"
|
|
1975
|
-
version = "0.1.0"
|
|
1976
|
-
description = "${answers.description || ''}"
|
|
1977
|
-
requires-python = ">=3.11"
|
|
1978
|
-
dependencies = []
|
|
1979
|
-
|
|
1980
|
-
[project.optional-dependencies]
|
|
1981
|
-
dev = [
|
|
1982
|
-
"pytest>=7.0",
|
|
1983
|
-
"ruff>=0.1",
|
|
1984
|
-
"mypy>=1.0",
|
|
1985
|
-
]
|
|
1986
|
-
|
|
1987
|
-
[tool.ruff]
|
|
1988
|
-
line-length = 100
|
|
1989
|
-
target-version = "py311"
|
|
1990
|
-
|
|
1991
|
-
[tool.ruff.lint]
|
|
1992
|
-
select = ["E", "F", "I", "N", "W"]
|
|
1993
|
-
|
|
1994
|
-
[tool.mypy]
|
|
1995
|
-
python_version = "3.11"
|
|
1996
|
-
strict = true
|
|
1997
|
-
|
|
1998
|
-
[tool.pytest.ini_options]
|
|
1999
|
-
testpaths = ["tests"]
|
|
2000
|
-
`;
|
|
2001
|
-
await fs.writeFile('pyproject.toml', pyprojectToml);
|
|
2002
|
-
|
|
2003
|
-
// Create src directory and __init__.py
|
|
2004
|
-
const srcDir = `src/${safeName.replace(/-/g, '_')}`;
|
|
2005
|
-
await fs.ensureDir(srcDir);
|
|
2006
|
-
if (!(await fs.pathExists(`${srcDir}/__init__.py`))) {
|
|
2007
|
-
await fs.writeFile(
|
|
2008
|
-
`${srcDir}/__init__.py`,
|
|
2009
|
-
`"""${answers.description || projectName}"""\n\n__version__ = "0.1.0"\n`
|
|
2010
|
-
);
|
|
2011
|
-
}
|
|
2012
|
-
}
|
|
2013
|
-
} else if (primaryLang === 'go') {
|
|
2014
|
-
// Check if go.mod already exists
|
|
2015
|
-
if (!(await fs.pathExists('go.mod'))) {
|
|
2016
|
-
const goMod = `module github.com/${safeName}
|
|
2017
|
-
|
|
2018
|
-
go 1.21
|
|
2019
|
-
|
|
2020
|
-
require (
|
|
2021
|
-
// Add dependencies here
|
|
2022
|
-
)
|
|
2023
|
-
`;
|
|
2024
|
-
await fs.writeFile('go.mod', goMod);
|
|
2025
|
-
|
|
2026
|
-
// Create main.go
|
|
2027
|
-
await fs.ensureDir('cmd');
|
|
2028
|
-
if (!(await fs.pathExists('cmd/main.go'))) {
|
|
2029
|
-
const mainGo = `package main
|
|
2030
|
-
|
|
2031
|
-
import "fmt"
|
|
2032
|
-
|
|
2033
|
-
func main() {
|
|
2034
|
-
fmt.Println("Hello from ${projectName}!")
|
|
2035
|
-
}
|
|
2036
|
-
`;
|
|
2037
|
-
await fs.writeFile('cmd/main.go', mainGo);
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2040
|
-
} else if (primaryLang === 'java') {
|
|
2041
|
-
// Generate pom.xml for Maven
|
|
2042
|
-
if (!(await fs.pathExists('pom.xml'))) {
|
|
2043
|
-
const pomXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
2044
|
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
2045
|
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
2046
|
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
2047
|
-
<modelVersion>4.0.0</modelVersion>
|
|
2048
|
-
|
|
2049
|
-
<groupId>com.example</groupId>
|
|
2050
|
-
<artifactId>${safeName}</artifactId>
|
|
2051
|
-
<version>0.1.0</version>
|
|
2052
|
-
<packaging>jar</packaging>
|
|
2053
|
-
|
|
2054
|
-
<name>${projectName}</name>
|
|
2055
|
-
<description>${answers.description || ''}</description>
|
|
2056
|
-
|
|
2057
|
-
<properties>
|
|
2058
|
-
<maven.compiler.source>21</maven.compiler.source>
|
|
2059
|
-
<maven.compiler.target>21</maven.compiler.target>
|
|
2060
|
-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
2061
|
-
</properties>
|
|
2062
|
-
|
|
2063
|
-
<dependencies>
|
|
2064
|
-
<dependency>
|
|
2065
|
-
<groupId>org.junit.jupiter</groupId>
|
|
2066
|
-
<artifactId>junit-jupiter</artifactId>
|
|
2067
|
-
<version>5.10.0</version>
|
|
2068
|
-
<scope>test</scope>
|
|
2069
|
-
</dependency>
|
|
2070
|
-
</dependencies>
|
|
2071
|
-
</project>
|
|
2072
|
-
`;
|
|
2073
|
-
await fs.writeFile('pom.xml', pomXml);
|
|
2074
|
-
}
|
|
2075
|
-
} else if (primaryLang === 'csharp') {
|
|
2076
|
-
// Generate .csproj file
|
|
2077
|
-
const csprojPath = `${projectName}.csproj`;
|
|
2078
|
-
if (!(await fs.pathExists(csprojPath))) {
|
|
2079
|
-
const csproj = `<Project Sdk="Microsoft.NET.Sdk">
|
|
2080
|
-
|
|
2081
|
-
<PropertyGroup>
|
|
2082
|
-
<OutputType>Exe</OutputType>
|
|
2083
|
-
<TargetFramework>net8.0</TargetFramework>
|
|
2084
|
-
<ImplicitUsings>enable</ImplicitUsings>
|
|
2085
|
-
<Nullable>enable</Nullable>
|
|
2086
|
-
</PropertyGroup>
|
|
2087
|
-
|
|
2088
|
-
<ItemGroup>
|
|
2089
|
-
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
|
2090
|
-
</ItemGroup>
|
|
2091
|
-
|
|
2092
|
-
</Project>
|
|
2093
|
-
`;
|
|
2094
|
-
await fs.writeFile(csprojPath, csproj);
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
/**
|
|
2100
|
-
* Generate language-specific tech.md content
|
|
2101
|
-
*/
|
|
2102
|
-
function generateTechMd(languages, answers, _locale) {
|
|
2103
|
-
const langInfo = {
|
|
2104
|
-
javascript: {
|
|
2105
|
-
name: 'JavaScript/TypeScript',
|
|
2106
|
-
version: 'ES2022+ / TypeScript 5.0+',
|
|
2107
|
-
runtime: 'Node.js 20+ LTS, Bun, Deno',
|
|
2108
|
-
packageManager: 'npm, pnpm, yarn',
|
|
2109
|
-
frameworks: 'React, Vue, Next.js, Express, Fastify',
|
|
2110
|
-
testing: 'Jest, Vitest, Playwright',
|
|
2111
|
-
},
|
|
2112
|
-
python: {
|
|
2113
|
-
name: 'Python',
|
|
2114
|
-
version: '3.11+',
|
|
2115
|
-
runtime: 'CPython, PyPy',
|
|
2116
|
-
packageManager: 'pip, poetry, uv',
|
|
2117
|
-
frameworks: 'FastAPI, Django, Flask',
|
|
2118
|
-
testing: 'pytest, unittest',
|
|
2119
|
-
},
|
|
2120
|
-
rust: {
|
|
2121
|
-
name: 'Rust',
|
|
2122
|
-
version: '1.75+ stable',
|
|
2123
|
-
runtime: 'Native binary',
|
|
2124
|
-
packageManager: 'Cargo',
|
|
2125
|
-
frameworks: 'Axum, Actix-web, Tokio',
|
|
2126
|
-
testing: 'cargo test, criterion',
|
|
2127
|
-
},
|
|
2128
|
-
go: {
|
|
2129
|
-
name: 'Go',
|
|
2130
|
-
version: '1.21+',
|
|
2131
|
-
runtime: 'Native binary',
|
|
2132
|
-
packageManager: 'Go modules',
|
|
2133
|
-
frameworks: 'Gin, Echo, Chi',
|
|
2134
|
-
testing: 'go test, testify',
|
|
2135
|
-
},
|
|
2136
|
-
java: {
|
|
2137
|
-
name: 'Java/Kotlin',
|
|
2138
|
-
version: 'Java 21 LTS / Kotlin 1.9+',
|
|
2139
|
-
runtime: 'JVM, GraalVM',
|
|
2140
|
-
packageManager: 'Maven, Gradle',
|
|
2141
|
-
frameworks: 'Spring Boot, Quarkus, Ktor',
|
|
2142
|
-
testing: 'JUnit 5, Kotest',
|
|
2143
|
-
},
|
|
2144
|
-
csharp: {
|
|
2145
|
-
name: 'C#/.NET',
|
|
2146
|
-
version: '.NET 8+',
|
|
2147
|
-
runtime: '.NET Runtime',
|
|
2148
|
-
packageManager: 'NuGet',
|
|
2149
|
-
frameworks: 'ASP.NET Core, MAUI',
|
|
2150
|
-
testing: 'xUnit, NUnit',
|
|
2151
|
-
},
|
|
2152
|
-
cpp: {
|
|
2153
|
-
name: 'C/C++',
|
|
2154
|
-
version: 'C++20',
|
|
2155
|
-
runtime: 'Native binary',
|
|
2156
|
-
packageManager: 'vcpkg, Conan',
|
|
2157
|
-
frameworks: 'Qt, Boost',
|
|
2158
|
-
testing: 'GoogleTest, Catch2',
|
|
2159
|
-
},
|
|
2160
|
-
swift: {
|
|
2161
|
-
name: 'Swift',
|
|
2162
|
-
version: '5.9+',
|
|
2163
|
-
runtime: 'Native binary',
|
|
2164
|
-
packageManager: 'Swift Package Manager',
|
|
2165
|
-
frameworks: 'SwiftUI, Vapor',
|
|
2166
|
-
testing: 'XCTest',
|
|
2167
|
-
},
|
|
2168
|
-
ruby: {
|
|
2169
|
-
name: 'Ruby',
|
|
2170
|
-
version: '3.2+',
|
|
2171
|
-
runtime: 'CRuby, JRuby',
|
|
2172
|
-
packageManager: 'Bundler, RubyGems',
|
|
2173
|
-
frameworks: 'Rails, Sinatra',
|
|
2174
|
-
testing: 'RSpec, Minitest',
|
|
2175
|
-
},
|
|
2176
|
-
php: {
|
|
2177
|
-
name: 'PHP',
|
|
2178
|
-
version: '8.2+',
|
|
2179
|
-
runtime: 'PHP-FPM, Swoole',
|
|
2180
|
-
packageManager: 'Composer',
|
|
2181
|
-
frameworks: 'Laravel, Symfony',
|
|
2182
|
-
testing: 'PHPUnit, Pest',
|
|
2183
|
-
},
|
|
2184
|
-
};
|
|
2185
|
-
|
|
2186
|
-
const isUndecided = languages[0] === 'undecided';
|
|
2187
|
-
const date = new Date().toISOString().split('T')[0];
|
|
2188
|
-
|
|
2189
|
-
if (isUndecided) {
|
|
2190
|
-
return `# Technology Stack
|
|
2191
|
-
|
|
2192
|
-
**Project**: ${answers.projectName}
|
|
2193
|
-
**Last Updated**: ${date}
|
|
2194
|
-
**Status**: Technology stack to be determined
|
|
2195
|
-
|
|
2196
|
-
---
|
|
2197
|
-
|
|
2198
|
-
## Overview
|
|
2199
|
-
|
|
2200
|
-
The technology stack for this project has not yet been decided. This document will be updated once the technical decisions are made.
|
|
2201
|
-
|
|
2202
|
-
## Decision Criteria
|
|
2203
|
-
|
|
2204
|
-
When selecting technologies, consider:
|
|
2205
|
-
|
|
2206
|
-
1. **Application Type**: What type of application is being built?
|
|
2207
|
-
2. **Performance Requirements**: What are the performance constraints?
|
|
2208
|
-
3. **Team Expertise**: What technologies is the team familiar with?
|
|
2209
|
-
4. **Ecosystem**: What libraries and tools are available?
|
|
2210
|
-
5. **Long-term Maintainability**: How well-supported is the technology?
|
|
2211
|
-
|
|
2212
|
-
## Candidates Under Consideration
|
|
2213
|
-
|
|
2214
|
-
| Aspect | Options | Decision |
|
|
2215
|
-
|--------|---------|----------|
|
|
2216
|
-
| Primary Language | TBD | ⏳ Pending |
|
|
2217
|
-
| Web Framework | TBD | ⏳ Pending |
|
|
2218
|
-
| Database | TBD | ⏳ Pending |
|
|
2219
|
-
| Hosting | TBD | ⏳ Pending |
|
|
2220
|
-
|
|
2221
|
-
## Next Steps
|
|
2222
|
-
|
|
2223
|
-
1. [ ] Define functional requirements
|
|
2224
|
-
2. [ ] Identify performance constraints
|
|
2225
|
-
3. [ ] Evaluate team skills
|
|
2226
|
-
4. [ ] Create proof-of-concept
|
|
2227
|
-
5. [ ] Make final decision and update this document
|
|
2228
|
-
|
|
2229
|
-
---
|
|
2230
|
-
|
|
2231
|
-
*Run \`musubi steering\` to update this document after decisions are made.*
|
|
2232
|
-
`;
|
|
2233
|
-
}
|
|
2234
|
-
|
|
2235
|
-
// Generate tech.md for selected languages
|
|
2236
|
-
const primaryLang = languages[0];
|
|
2237
|
-
const primary = langInfo[primaryLang] || { name: primaryLang, version: 'Latest' };
|
|
2238
|
-
|
|
2239
|
-
let languageTable = `### Programming Languages
|
|
2240
|
-
|
|
2241
|
-
| Language | Version | Role | Notes |
|
|
2242
|
-
|----------|---------|------|-------|
|
|
2243
|
-
`;
|
|
2244
|
-
|
|
2245
|
-
for (let i = 0; i < languages.length; i++) {
|
|
2246
|
-
const lang = languages[i];
|
|
2247
|
-
const info = langInfo[lang] || { name: lang, version: 'Latest' };
|
|
2248
|
-
const role = i === 0 ? 'Primary' : 'Secondary';
|
|
2249
|
-
languageTable += `| ${info.name} | ${info.version} | ${role} | ${info.runtime || ''} |\n`;
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
let frameworksSection = '';
|
|
2253
|
-
for (const lang of languages) {
|
|
2254
|
-
const info = langInfo[lang];
|
|
2255
|
-
if (info && info.frameworks) {
|
|
2256
|
-
frameworksSection += `
|
|
2257
|
-
### ${info.name} Ecosystem
|
|
2258
|
-
|
|
2259
|
-
- **Package Manager**: ${info.packageManager}
|
|
2260
|
-
- **Frameworks**: ${info.frameworks}
|
|
2261
|
-
- **Testing**: ${info.testing}
|
|
2262
|
-
`;
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
return `# Technology Stack
|
|
2267
|
-
|
|
2268
|
-
**Project**: ${answers.projectName}
|
|
2269
|
-
**Last Updated**: ${date}
|
|
2270
|
-
**Version**: 0.1.0
|
|
2271
|
-
|
|
2272
|
-
---
|
|
2273
|
-
|
|
2274
|
-
## Overview
|
|
2275
|
-
|
|
2276
|
-
${answers.description}
|
|
2277
|
-
|
|
2278
|
-
---
|
|
2279
|
-
|
|
2280
|
-
## Primary Technologies
|
|
2281
|
-
|
|
2282
|
-
${languageTable}
|
|
2283
|
-
${frameworksSection}
|
|
2284
|
-
|
|
2285
|
-
---
|
|
2286
|
-
|
|
2287
|
-
## Development Environment
|
|
2288
|
-
|
|
2289
|
-
### Required Tools
|
|
2290
|
-
|
|
2291
|
-
- Primary language runtime (see above)
|
|
2292
|
-
- Git 2.40+
|
|
2293
|
-
- IDE: VS Code / JetBrains / Neovim
|
|
2294
|
-
|
|
2295
|
-
### Recommended Extensions
|
|
2296
|
-
|
|
2297
|
-
- Language-specific LSP
|
|
2298
|
-
- Linter/Formatter integration
|
|
2299
|
-
- Test runner integration
|
|
2300
|
-
|
|
2301
|
-
---
|
|
2302
|
-
|
|
2303
|
-
## Architecture Decisions
|
|
2304
|
-
|
|
2305
|
-
| Decision | Choice | Rationale |
|
|
2306
|
-
|----------|--------|-----------|
|
|
2307
|
-
| Primary Language | ${primary.name} | Selected during project initialization |
|
|
2308
|
-
| Package Manager | ${primary.packageManager || 'TBD'} | Standard for ${primary.name} |
|
|
2309
|
-
|
|
2310
|
-
---
|
|
2311
|
-
|
|
2312
|
-
## Dependencies
|
|
2313
|
-
|
|
2314
|
-
### Production Dependencies
|
|
2315
|
-
|
|
2316
|
-
*To be documented as dependencies are added.*
|
|
2317
|
-
|
|
2318
|
-
### Development Dependencies
|
|
2319
|
-
|
|
2320
|
-
*To be documented as dependencies are added.*
|
|
2321
|
-
|
|
2322
|
-
---
|
|
2323
|
-
|
|
2324
|
-
*Generated by MUSUBI SDD - Update with \`musubi steering\`*
|
|
2325
|
-
`;
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
1032
|
async function createConstitution() {
|
|
2329
1033
|
const constitutionTemplate = path.join(SHARED_TEMPLATE_DIR, 'constitution', 'constitution.md');
|
|
2330
1034
|
await fs.copy(constitutionTemplate, 'steering/rules/constitution.md');
|
|
@@ -2376,7 +1080,7 @@ ${skillsSection}
|
|
|
2376
1080
|
|
|
2377
1081
|
### Learn More
|
|
2378
1082
|
|
|
2379
|
-
- [MUSUBI Documentation](https://github.com/
|
|
1083
|
+
- [MUSUBI Documentation](https://github.com/nahisaho/MUSUBI)
|
|
2380
1084
|
- [Constitutional Governance](steering/rules/constitution.md)
|
|
2381
1085
|
- [8-Stage SDD Workflow](steering/rules/workflow.md)
|
|
2382
1086
|
|