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 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();
@@ -204,10 +204,11 @@ async function analyzeDirectoryStructure() {
204
204
  };
205
205
 
206
206
  // Glob for main directories (exclude node_modules, .git, etc.)
207
- const dirs = await glob('*/', {
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 srcDirs = await glob('src/*/', { ignore: ['node_modules/**'] });
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 files = await glob('**/*', {
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.4.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",