musubi-sdd 0.4.0 ā 0.5.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.md +16 -0
- package/bin/musubi-analyze.js +448 -0
- package/bin/musubi-onboard.js +6 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ MUSUBI is a comprehensive SDD (Specification Driven Development) framework that
|
|
|
22
22
|
- š§ **Auto-Updating Project Memory** - Steering system maintains architecture, tech stack, and product context
|
|
23
23
|
- š **Automatic Onboarding** - `musubi-onboard` analyzes existing projects and generates steering docs (2-5 minutes)
|
|
24
24
|
- š **Auto-Sync** - `musubi-sync` detects codebase changes and keeps steering docs current
|
|
25
|
+
- š **Intelligent Code Analysis** - `musubi-analyze` provides quality metrics, complexity analysis, and technical debt detection (v0.5.0)
|
|
25
26
|
- ā
**Complete Traceability** - Requirements ā Design ā Code ā Tests mapping
|
|
26
27
|
- š **Bilingual Documentation** - All agent-generated documents created in both English and Japanese
|
|
27
28
|
|
|
@@ -86,6 +87,13 @@ musubi-onboard
|
|
|
86
87
|
musubi-sync
|
|
87
88
|
musubi-sync --dry-run # Preview changes
|
|
88
89
|
musubi-sync --auto-approve # Auto-apply (CI/CD)
|
|
90
|
+
|
|
91
|
+
# Analyze code quality (v0.5.0)
|
|
92
|
+
musubi-analyze # Full analysis
|
|
93
|
+
musubi-analyze --type=quality # Quality metrics only
|
|
94
|
+
musubi-analyze --type=dependencies # Dependencies only
|
|
95
|
+
musubi-analyze --type=security # Security audit
|
|
96
|
+
musubi-analyze --output=report.md # Save report
|
|
89
97
|
```
|
|
90
98
|
|
|
91
99
|
### Project Types
|
|
@@ -269,6 +277,14 @@ musubi-onboard --skip-memories # Skip memory initialization
|
|
|
269
277
|
musubi-sync # Interactive mode
|
|
270
278
|
musubi-sync --dry-run # Preview changes only
|
|
271
279
|
musubi-sync --auto-approve # Auto-apply (CI/CD)
|
|
280
|
+
|
|
281
|
+
# Analyze code quality (v0.5.0+)
|
|
282
|
+
musubi-analyze # Full analysis (quality + dependencies + security)
|
|
283
|
+
musubi-analyze --type=quality # Code quality metrics only
|
|
284
|
+
musubi-analyze --type=dependencies # Dependency analysis
|
|
285
|
+
musubi-analyze --type=security # Security vulnerabilities
|
|
286
|
+
musubi-analyze --output=report.md # Save report to file
|
|
287
|
+
musubi-analyze --json # JSON output
|
|
272
288
|
```
|
|
273
289
|
|
|
274
290
|
#### musubi-onboard
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MUSUBI Analyze - Intelligent Code Quality Analysis
|
|
5
|
+
*
|
|
6
|
+
* Analyzes codebase for:
|
|
7
|
+
* - Code quality metrics (complexity, maintainability)
|
|
8
|
+
* - Technical debt detection
|
|
9
|
+
* - Dependency analysis
|
|
10
|
+
* - Security vulnerabilities
|
|
11
|
+
*
|
|
12
|
+
* @version 0.5.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs-extra');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const chalk = require('chalk');
|
|
18
|
+
const { program } = require('commander');
|
|
19
|
+
const glob = require('glob');
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Configuration
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
const CONFIG = {
|
|
26
|
+
excludePatterns: [
|
|
27
|
+
'node_modules/**',
|
|
28
|
+
'dist/**',
|
|
29
|
+
'build/**',
|
|
30
|
+
'.git/**',
|
|
31
|
+
'coverage/**',
|
|
32
|
+
'*.min.js',
|
|
33
|
+
'*.bundle.js'
|
|
34
|
+
],
|
|
35
|
+
thresholds: {
|
|
36
|
+
complexity: {
|
|
37
|
+
low: 5,
|
|
38
|
+
medium: 10,
|
|
39
|
+
high: 20
|
|
40
|
+
},
|
|
41
|
+
maintainability: {
|
|
42
|
+
excellent: 80,
|
|
43
|
+
good: 60,
|
|
44
|
+
moderate: 40,
|
|
45
|
+
poor: 20
|
|
46
|
+
},
|
|
47
|
+
fileSize: {
|
|
48
|
+
small: 200,
|
|
49
|
+
medium: 500,
|
|
50
|
+
large: 1000
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// CLI Setup
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
program
|
|
60
|
+
.name('musubi-analyze')
|
|
61
|
+
.description('Analyze codebase for quality, complexity, and technical debt')
|
|
62
|
+
.version('0.5.0')
|
|
63
|
+
.option('-t, --type <type>', 'Analysis type: quality, dependencies, security, all', 'all')
|
|
64
|
+
.option('-o, --output <file>', 'Output file for analysis report')
|
|
65
|
+
.option('--json', 'Output in JSON format')
|
|
66
|
+
.option('--threshold <level>', 'Quality threshold: low, medium, high', 'medium')
|
|
67
|
+
.option('-v, --verbose', 'Verbose output')
|
|
68
|
+
.parse(process.argv);
|
|
69
|
+
|
|
70
|
+
const options = program.opts();
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Utility Functions
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
function log(message, type = 'info') {
|
|
77
|
+
const icons = {
|
|
78
|
+
info: 'ā¹ļø',
|
|
79
|
+
success: 'ā
',
|
|
80
|
+
warning: 'ā ļø',
|
|
81
|
+
error: 'ā',
|
|
82
|
+
analyze: 'š'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const colors = {
|
|
86
|
+
info: chalk.blue,
|
|
87
|
+
success: chalk.green,
|
|
88
|
+
warning: chalk.yellow,
|
|
89
|
+
error: chalk.red,
|
|
90
|
+
analyze: chalk.cyan
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const color = colors[type] || chalk.white;
|
|
94
|
+
console.log(`${icons[type] || 'ā¢'} ${color(message)}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function calculateComplexity(code) {
|
|
98
|
+
// Simplified cyclomatic complexity calculation
|
|
99
|
+
let complexity = 1; // Base complexity
|
|
100
|
+
|
|
101
|
+
// Count decision points
|
|
102
|
+
const patterns = [
|
|
103
|
+
/\bif\b/g,
|
|
104
|
+
/\belse\s+if\b/g,
|
|
105
|
+
/\bfor\b/g,
|
|
106
|
+
/\bwhile\b/g,
|
|
107
|
+
/\bcase\b/g,
|
|
108
|
+
/\bcatch\b/g,
|
|
109
|
+
/\&\&/g,
|
|
110
|
+
/\|\|/g,
|
|
111
|
+
/\?/g // Ternary operator
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
patterns.forEach(pattern => {
|
|
115
|
+
const matches = code.match(pattern);
|
|
116
|
+
if (matches) complexity += matches.length;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return complexity;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function calculateMaintainability(code, complexity) {
|
|
123
|
+
// Simplified maintainability index
|
|
124
|
+
// Based on: lines of code, complexity, comment ratio
|
|
125
|
+
|
|
126
|
+
const lines = code.split('\n');
|
|
127
|
+
const codeLines = lines.filter(line => {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
return trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('/*');
|
|
130
|
+
}).length;
|
|
131
|
+
|
|
132
|
+
const commentLines = lines.filter(line => {
|
|
133
|
+
const trimmed = line.trim();
|
|
134
|
+
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
|
|
135
|
+
}).length;
|
|
136
|
+
|
|
137
|
+
const commentRatio = codeLines > 0 ? commentLines / codeLines : 0;
|
|
138
|
+
|
|
139
|
+
// Maintainability Index formula (simplified)
|
|
140
|
+
const volumeScore = Math.max(0, 100 - Math.log2(codeLines + 1) * 5);
|
|
141
|
+
const complexityScore = Math.max(0, 100 - complexity * 2);
|
|
142
|
+
const commentScore = Math.min(100, commentRatio * 100);
|
|
143
|
+
|
|
144
|
+
const maintainability = (volumeScore * 0.4) + (complexityScore * 0.4) + (commentScore * 0.2);
|
|
145
|
+
|
|
146
|
+
return Math.round(maintainability);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function analyzeFile(filePath) {
|
|
150
|
+
try {
|
|
151
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
152
|
+
const lines = content.split('\n');
|
|
153
|
+
const complexity = calculateComplexity(content);
|
|
154
|
+
const maintainability = calculateMaintainability(content, complexity);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
path: filePath,
|
|
158
|
+
lines: lines.length,
|
|
159
|
+
complexity,
|
|
160
|
+
maintainability,
|
|
161
|
+
size: fs.statSync(filePath).size
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (options.verbose) {
|
|
165
|
+
log(`Error analyzing ${filePath}: ${error.message}`, 'error');
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getComplexityLevel(complexity) {
|
|
172
|
+
const { low, medium, high } = CONFIG.thresholds.complexity;
|
|
173
|
+
if (complexity <= low) return { level: 'Low', color: chalk.green };
|
|
174
|
+
if (complexity <= medium) return { level: 'Medium', color: chalk.yellow };
|
|
175
|
+
if (complexity <= high) return { level: 'High', color: chalk.red };
|
|
176
|
+
return { level: 'Very High', color: chalk.red.bold };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getMaintainabilityLevel(score) {
|
|
180
|
+
const { excellent, good, moderate, poor } = CONFIG.thresholds.maintainability;
|
|
181
|
+
if (score >= excellent) return { level: 'Excellent', color: chalk.green.bold };
|
|
182
|
+
if (score >= good) return { level: 'Good', color: chalk.green };
|
|
183
|
+
if (score >= moderate) return { level: 'Moderate', color: chalk.yellow };
|
|
184
|
+
if (score >= poor) return { level: 'Poor', color: chalk.red };
|
|
185
|
+
return { level: 'Critical', color: chalk.red.bold };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ============================================================================
|
|
189
|
+
// Analysis Functions
|
|
190
|
+
// ============================================================================
|
|
191
|
+
|
|
192
|
+
async function analyzeQuality() {
|
|
193
|
+
log('Code Quality Analysis', 'analyze');
|
|
194
|
+
console.log();
|
|
195
|
+
|
|
196
|
+
// Find all JavaScript/TypeScript files
|
|
197
|
+
const patterns = ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx'];
|
|
198
|
+
let allFiles = [];
|
|
199
|
+
|
|
200
|
+
for (const pattern of patterns) {
|
|
201
|
+
const files = await glob(pattern, {
|
|
202
|
+
ignore: CONFIG.excludePatterns,
|
|
203
|
+
nodir: true
|
|
204
|
+
});
|
|
205
|
+
allFiles = allFiles.concat(Array.isArray(files) ? files : []);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (allFiles.length === 0) {
|
|
209
|
+
log('No JavaScript/TypeScript files found', 'warning');
|
|
210
|
+
return { files: [], summary: null };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
log(`Analyzing ${allFiles.length} files...`, 'info');
|
|
214
|
+
console.log();
|
|
215
|
+
|
|
216
|
+
const results = [];
|
|
217
|
+
let totalComplexity = 0;
|
|
218
|
+
let totalMaintainability = 0;
|
|
219
|
+
|
|
220
|
+
for (const file of allFiles) {
|
|
221
|
+
const analysis = analyzeFile(file);
|
|
222
|
+
if (analysis) {
|
|
223
|
+
results.push(analysis);
|
|
224
|
+
totalComplexity += analysis.complexity;
|
|
225
|
+
totalMaintainability += analysis.maintainability;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Sort by complexity (highest first)
|
|
230
|
+
results.sort((a, b) => b.complexity - a.complexity);
|
|
231
|
+
|
|
232
|
+
// Summary
|
|
233
|
+
const summary = {
|
|
234
|
+
totalFiles: results.length,
|
|
235
|
+
averageComplexity: Math.round(totalComplexity / results.length),
|
|
236
|
+
averageMaintainability: Math.round(totalMaintainability / results.length),
|
|
237
|
+
highComplexityFiles: results.filter(r => r.complexity > CONFIG.thresholds.complexity.high).length,
|
|
238
|
+
lowMaintainabilityFiles: results.filter(r => r.maintainability < CONFIG.thresholds.maintainability.moderate).length
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Display top 10 most complex files
|
|
242
|
+
console.log(chalk.bold('š Top 10 Most Complex Files:'));
|
|
243
|
+
console.log();
|
|
244
|
+
|
|
245
|
+
results.slice(0, 10).forEach((file, index) => {
|
|
246
|
+
const complexityInfo = getComplexityLevel(file.complexity);
|
|
247
|
+
const maintInfo = getMaintainabilityLevel(file.maintainability);
|
|
248
|
+
|
|
249
|
+
console.log(`${index + 1}. ${chalk.cyan(file.path)}`);
|
|
250
|
+
console.log(` Lines: ${file.lines} | Complexity: ${complexityInfo.color(file.complexity)} (${complexityInfo.level}) | Maintainability: ${maintInfo.color(file.maintainability)} (${maintInfo.level})`);
|
|
251
|
+
console.log();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Display summary
|
|
255
|
+
console.log(chalk.bold('š Summary:'));
|
|
256
|
+
console.log();
|
|
257
|
+
console.log(`Total Files Analyzed: ${summary.totalFiles}`);
|
|
258
|
+
console.log(`Average Complexity: ${summary.averageComplexity}`);
|
|
259
|
+
console.log(`Average Maintainability: ${summary.averageMaintainability}`);
|
|
260
|
+
console.log(`High Complexity Files: ${chalk.red(summary.highComplexityFiles)}`);
|
|
261
|
+
console.log(`Low Maintainability Files: ${chalk.red(summary.lowMaintainabilityFiles)}`);
|
|
262
|
+
console.log();
|
|
263
|
+
|
|
264
|
+
return { files: results, summary };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function analyzeDependencies() {
|
|
268
|
+
log('Dependency Analysis', 'analyze');
|
|
269
|
+
console.log();
|
|
270
|
+
|
|
271
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
272
|
+
|
|
273
|
+
if (!fs.existsSync(packagePath)) {
|
|
274
|
+
log('No package.json found', 'warning');
|
|
275
|
+
return { dependencies: [], devDependencies: [] };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
279
|
+
const deps = pkg.dependencies || {};
|
|
280
|
+
const devDeps = pkg.devDependencies || {};
|
|
281
|
+
|
|
282
|
+
console.log(chalk.bold('š¦ Production Dependencies:'));
|
|
283
|
+
console.log();
|
|
284
|
+
Object.entries(deps).forEach(([name, version]) => {
|
|
285
|
+
console.log(` ${chalk.green('ā¢')} ${chalk.cyan(name)}: ${version}`);
|
|
286
|
+
});
|
|
287
|
+
console.log();
|
|
288
|
+
|
|
289
|
+
console.log(chalk.bold('š ļø Development Dependencies:'));
|
|
290
|
+
console.log();
|
|
291
|
+
Object.entries(devDeps).forEach(([name, version]) => {
|
|
292
|
+
console.log(` ${chalk.yellow('ā¢')} ${chalk.cyan(name)}: ${version}`);
|
|
293
|
+
});
|
|
294
|
+
console.log();
|
|
295
|
+
|
|
296
|
+
console.log(chalk.bold('š Summary:'));
|
|
297
|
+
console.log();
|
|
298
|
+
console.log(`Total Dependencies: ${Object.keys(deps).length}`);
|
|
299
|
+
console.log(`Total DevDependencies: ${Object.keys(devDeps).length}`);
|
|
300
|
+
console.log();
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
dependencies: Object.entries(deps).map(([name, version]) => ({ name, version })),
|
|
304
|
+
devDependencies: Object.entries(devDeps).map(([name, version]) => ({ name, version }))
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function analyzeSecurity() {
|
|
309
|
+
log('Security Analysis', 'analyze');
|
|
310
|
+
console.log();
|
|
311
|
+
|
|
312
|
+
log('Running npm audit...', 'info');
|
|
313
|
+
console.log();
|
|
314
|
+
|
|
315
|
+
const { execSync } = require('child_process');
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const output = execSync('npm audit --json', { encoding: 'utf8' });
|
|
319
|
+
const auditResult = JSON.parse(output);
|
|
320
|
+
|
|
321
|
+
const vulnerabilities = auditResult.metadata?.vulnerabilities || {};
|
|
322
|
+
const total = Object.values(vulnerabilities).reduce((sum, count) => sum + count, 0);
|
|
323
|
+
|
|
324
|
+
console.log(chalk.bold('š Security Vulnerabilities:'));
|
|
325
|
+
console.log();
|
|
326
|
+
console.log(`Critical: ${chalk.red.bold(vulnerabilities.critical || 0)}`);
|
|
327
|
+
console.log(`High: ${chalk.red(vulnerabilities.high || 0)}`);
|
|
328
|
+
console.log(`Moderate: ${chalk.yellow(vulnerabilities.moderate || 0)}`);
|
|
329
|
+
console.log(`Low: ${chalk.green(vulnerabilities.low || 0)}`);
|
|
330
|
+
console.log();
|
|
331
|
+
console.log(`Total: ${total === 0 ? chalk.green('No vulnerabilities found! āØ') : chalk.red(total)}`);
|
|
332
|
+
console.log();
|
|
333
|
+
|
|
334
|
+
if (total > 0) {
|
|
335
|
+
log('Run "npm audit fix" to fix vulnerabilities', 'warning');
|
|
336
|
+
console.log();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return vulnerabilities;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
log('npm audit failed. Make sure npm is installed.', 'error');
|
|
342
|
+
return {};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function generateReport(analysisData) {
|
|
347
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
348
|
+
const reportPath = options.output || `steering/memories/code_quality_report_${timestamp}.md`;
|
|
349
|
+
|
|
350
|
+
let report = `# Code Quality Analysis Report\n\n`;
|
|
351
|
+
report += `**Generated**: ${new Date().toISOString()}\n`;
|
|
352
|
+
report += `**Version**: MUSUBI v0.5.0\n\n`;
|
|
353
|
+
|
|
354
|
+
if (analysisData.quality && analysisData.quality.summary) {
|
|
355
|
+
const { summary } = analysisData.quality;
|
|
356
|
+
report += `## Quality Metrics\n\n`;
|
|
357
|
+
report += `- Total Files: ${summary.totalFiles}\n`;
|
|
358
|
+
report += `- Average Complexity: ${summary.averageComplexity}\n`;
|
|
359
|
+
report += `- Average Maintainability: ${summary.averageMaintainability}\n`;
|
|
360
|
+
report += `- High Complexity Files: ${summary.highComplexityFiles}\n`;
|
|
361
|
+
report += `- Low Maintainability Files: ${summary.lowMaintainabilityFiles}\n\n`;
|
|
362
|
+
|
|
363
|
+
report += `### Recommendations\n\n`;
|
|
364
|
+
if (summary.highComplexityFiles > 0) {
|
|
365
|
+
report += `- ā ļø ${summary.highComplexityFiles} files have high complexity. Consider refactoring.\n`;
|
|
366
|
+
}
|
|
367
|
+
if (summary.lowMaintainabilityFiles > 0) {
|
|
368
|
+
report += `- ā ļø ${summary.lowMaintainabilityFiles} files have low maintainability. Add comments and simplify logic.\n`;
|
|
369
|
+
}
|
|
370
|
+
if (summary.highComplexityFiles === 0 && summary.lowMaintainabilityFiles === 0) {
|
|
371
|
+
report += `- ā
Code quality is excellent!\n`;
|
|
372
|
+
}
|
|
373
|
+
report += `\n`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (analysisData.dependencies) {
|
|
377
|
+
report += `## Dependencies\n\n`;
|
|
378
|
+
report += `- Production: ${analysisData.dependencies.dependencies.length}\n`;
|
|
379
|
+
report += `- Development: ${analysisData.dependencies.devDependencies.length}\n\n`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (analysisData.security) {
|
|
383
|
+
const total = Object.values(analysisData.security).reduce((sum, count) => sum + count, 0);
|
|
384
|
+
report += `## Security\n\n`;
|
|
385
|
+
report += `- Total Vulnerabilities: ${total}\n`;
|
|
386
|
+
report += `- Critical: ${analysisData.security.critical || 0}\n`;
|
|
387
|
+
report += `- High: ${analysisData.security.high || 0}\n`;
|
|
388
|
+
report += `- Moderate: ${analysisData.security.moderate || 0}\n`;
|
|
389
|
+
report += `- Low: ${analysisData.security.low || 0}\n\n`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
report += `---\n\n`;
|
|
393
|
+
report += `*Generated by MUSUBI v0.5.0*\n`;
|
|
394
|
+
|
|
395
|
+
fs.ensureDirSync(path.dirname(reportPath));
|
|
396
|
+
fs.writeFileSync(reportPath, report);
|
|
397
|
+
|
|
398
|
+
log(`Report saved to: ${reportPath}`, 'success');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ============================================================================
|
|
402
|
+
// Main Function
|
|
403
|
+
// ============================================================================
|
|
404
|
+
|
|
405
|
+
async function main() {
|
|
406
|
+
console.log(chalk.bold.cyan('\nš MUSUBI Code Analysis\n'));
|
|
407
|
+
|
|
408
|
+
const analysisData = {};
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
if (options.type === 'quality' || options.type === 'all') {
|
|
412
|
+
analysisData.quality = await analyzeQuality();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (options.type === 'dependencies' || options.type === 'all') {
|
|
416
|
+
analysisData.dependencies = await analyzeDependencies();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (options.type === 'security' || options.type === 'all') {
|
|
420
|
+
analysisData.security = await analyzeSecurity();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Generate report if output specified
|
|
424
|
+
if (options.output || options.type === 'all') {
|
|
425
|
+
await generateReport(analysisData);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// JSON output
|
|
429
|
+
if (options.json) {
|
|
430
|
+
console.log(JSON.stringify(analysisData, null, 2));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
log('Analysis complete!', 'success');
|
|
434
|
+
|
|
435
|
+
} catch (error) {
|
|
436
|
+
log(`Analysis failed: ${error.message}`, 'error');
|
|
437
|
+
if (options.verbose) {
|
|
438
|
+
console.error(error);
|
|
439
|
+
}
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// ============================================================================
|
|
445
|
+
// Execute
|
|
446
|
+
// ============================================================================
|
|
447
|
+
|
|
448
|
+
main();
|
package/bin/musubi-onboard.js
CHANGED
|
@@ -204,10 +204,11 @@ async function analyzeDirectoryStructure() {
|
|
|
204
204
|
};
|
|
205
205
|
|
|
206
206
|
// Glob for main directories (exclude node_modules, .git, etc.)
|
|
207
|
-
const
|
|
207
|
+
const dirsResult = await glob('*/', {
|
|
208
208
|
ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**', '.vscode/**', '.idea/**'],
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
const dirs = Array.isArray(dirsResult) ? dirsResult : [];
|
|
211
212
|
analysis.directories = dirs.map(d => d.replace(/\/$/, ''));
|
|
212
213
|
|
|
213
214
|
// Detect architecture patterns
|
|
@@ -215,7 +216,8 @@ async function analyzeDirectoryStructure() {
|
|
|
215
216
|
analysis.patterns.push('src-based');
|
|
216
217
|
|
|
217
218
|
// Check sub-patterns within src
|
|
218
|
-
const
|
|
219
|
+
const srcDirsResult = await glob('src/*/', { ignore: ['node_modules/**'] });
|
|
220
|
+
const srcDirs = Array.isArray(srcDirsResult) ? srcDirsResult : [];
|
|
219
221
|
|
|
220
222
|
if (srcDirs.includes('src/features/')) analysis.architecture = 'feature-first';
|
|
221
223
|
else if (srcDirs.includes('src/components/')) analysis.architecture = 'component-based';
|
|
@@ -245,11 +247,12 @@ async function detectTechnologyStack(projectConfig) {
|
|
|
245
247
|
};
|
|
246
248
|
|
|
247
249
|
// Detect languages from file extensions
|
|
248
|
-
const
|
|
250
|
+
const filesResult = await glob('**/*', {
|
|
249
251
|
ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'],
|
|
250
252
|
nodir: true,
|
|
251
253
|
});
|
|
252
254
|
|
|
255
|
+
const files = Array.isArray(filesResult) ? filesResult : [];
|
|
253
256
|
const extensions = new Set(files.map(f => path.extname(f)).filter(Boolean));
|
|
254
257
|
|
|
255
258
|
if (extensions.has('.js')) stack.languages.push('javascript');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musubi-sdd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Ultimate Specification Driven Development Tool with 25 Agents for 7 AI Coding Platforms (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Windsurf, Codex, Qwen Code)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"musubi": "bin/musubi.js",
|
|
9
9
|
"musubi-init": "bin/musubi-init.js",
|
|
10
10
|
"musubi-onboard": "bin/musubi-onboard.js",
|
|
11
|
-
"musubi-sync": "bin/musubi-sync.js"
|
|
11
|
+
"musubi-sync": "bin/musubi-sync.js",
|
|
12
|
+
"musubi-analyze": "bin/musubi-analyze.js"
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"test": "jest",
|