musubi-sdd 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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();
@@ -208,7 +208,8 @@ async function analyzeCodebase() {
208
208
  nodir: true,
209
209
  });
210
210
 
211
- const extensions = new Set(files.map(f => path.extname(f)).filter(Boolean));
211
+ const fileArray = Array.isArray(files) ? files : [];
212
+ const extensions = new Set(fileArray.map(f => path.extname(f)).filter(Boolean));
212
213
  const langMap = {
213
214
  '.js': 'javascript',
214
215
  '.ts': 'typescript',
@@ -229,7 +230,8 @@ async function analyzeCodebase() {
229
230
  const dirs = await glob('*/', {
230
231
  ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**', '.vscode/**'],
231
232
  });
232
- state.directories = dirs.map(d => d.replace(/\/$/, '')).slice(0, 10);
233
+ const dirArray = Array.isArray(dirs) ? dirs : [];
234
+ state.directories = dirArray.map(d => d.replace(/\/$/, '')).slice(0, 10);
233
235
 
234
236
  return state;
235
237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musubi-sdd",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
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",