musubi-sdd 5.1.0 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +106 -48
- package/README.md +110 -32
- package/bin/musubi-analyze.js +74 -67
- package/bin/musubi-browser.js +27 -26
- package/bin/musubi-change.js +48 -47
- package/bin/musubi-checkpoint.js +10 -7
- package/bin/musubi-convert.js +25 -25
- package/bin/musubi-costs.js +27 -10
- package/bin/musubi-gui.js +52 -46
- package/bin/musubi-init.js +1952 -10
- package/bin/musubi-orchestrate.js +327 -239
- package/bin/musubi-remember.js +69 -56
- package/bin/musubi-resolve.js +53 -45
- package/bin/musubi-trace.js +51 -22
- package/bin/musubi-validate.js +39 -30
- package/bin/musubi-workflow.js +33 -34
- package/bin/musubi.js +39 -2
- package/package.json +1 -1
- package/src/agents/agent-loop.js +94 -95
- package/src/agents/agentic/code-generator.js +119 -109
- package/src/agents/agentic/code-reviewer.js +105 -108
- package/src/agents/agentic/index.js +4 -4
- package/src/agents/browser/action-executor.js +13 -13
- package/src/agents/browser/ai-comparator.js +11 -10
- package/src/agents/browser/context-manager.js +6 -6
- package/src/agents/browser/index.js +5 -5
- package/src/agents/browser/nl-parser.js +31 -46
- package/src/agents/browser/screenshot.js +2 -2
- package/src/agents/browser/test-generator.js +6 -4
- package/src/agents/function-tool.js +71 -65
- package/src/agents/index.js +7 -7
- package/src/agents/schema-generator.js +98 -94
- package/src/analyzers/ast-extractor.js +158 -146
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +241 -126
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +77 -81
- package/src/analyzers/security-analyzer.js +19 -11
- package/src/analyzers/stuck-detector.js +19 -17
- package/src/converters/index.js +78 -57
- package/src/converters/ir/types.js +12 -12
- package/src/converters/parsers/musubi-parser.js +134 -126
- package/src/converters/parsers/openapi-parser.js +70 -53
- package/src/converters/parsers/speckit-parser.js +239 -175
- package/src/converters/writers/musubi-writer.js +123 -118
- package/src/converters/writers/speckit-writer.js +124 -113
- package/src/generators/rust-migration-generator.js +512 -0
- package/src/gui/public/index.html +1365 -1211
- package/src/gui/server.js +41 -40
- package/src/gui/services/file-watcher.js +23 -8
- package/src/gui/services/project-scanner.js +26 -20
- package/src/gui/services/replanning-service.js +27 -23
- package/src/gui/services/traceability-service.js +8 -8
- package/src/gui/services/workflow-service.js +14 -7
- package/src/index.js +151 -0
- package/src/integrations/cicd.js +90 -104
- package/src/integrations/codegraph-mcp.js +643 -0
- package/src/integrations/documentation.js +142 -103
- package/src/integrations/examples.js +95 -80
- package/src/integrations/github-client.js +17 -17
- package/src/integrations/index.js +5 -5
- package/src/integrations/mcp/index.js +21 -21
- package/src/integrations/mcp/mcp-context-provider.js +76 -78
- package/src/integrations/mcp/mcp-discovery.js +74 -72
- package/src/integrations/mcp/mcp-tool-registry.js +99 -94
- package/src/integrations/mcp-connector.js +70 -66
- package/src/integrations/platforms.js +50 -49
- package/src/integrations/tool-discovery.js +37 -31
- package/src/llm-providers/anthropic-provider.js +11 -11
- package/src/llm-providers/base-provider.js +16 -18
- package/src/llm-providers/copilot-provider.js +22 -19
- package/src/llm-providers/index.js +26 -25
- package/src/llm-providers/ollama-provider.js +11 -11
- package/src/llm-providers/openai-provider.js +12 -12
- package/src/managers/agent-memory.js +36 -24
- package/src/managers/checkpoint-manager.js +4 -8
- package/src/managers/delta-spec.js +19 -19
- package/src/managers/index.js +13 -4
- package/src/managers/memory-condenser.js +35 -45
- package/src/managers/repo-skill-manager.js +57 -31
- package/src/managers/skill-loader.js +25 -22
- package/src/managers/skill-tools.js +36 -72
- package/src/managers/workflow.js +30 -22
- package/src/monitoring/cost-tracker.js +48 -46
- package/src/monitoring/incident-manager.js +116 -106
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +75 -62
- package/src/monitoring/quality-dashboard.js +45 -41
- package/src/monitoring/release-manager.js +63 -53
- package/src/orchestration/agent-skill-binding.js +39 -47
- package/src/orchestration/error-handler.js +65 -107
- package/src/orchestration/guardrails/base-guardrail.js +26 -24
- package/src/orchestration/guardrails/guardrail-rules.js +50 -64
- package/src/orchestration/guardrails/index.js +5 -5
- package/src/orchestration/guardrails/input-guardrail.js +58 -45
- package/src/orchestration/guardrails/output-guardrail.js +104 -81
- package/src/orchestration/guardrails/safety-check.js +79 -79
- package/src/orchestration/index.js +38 -55
- package/src/orchestration/mcp-tool-adapters.js +96 -99
- package/src/orchestration/orchestration-engine.js +21 -21
- package/src/orchestration/pattern-registry.js +60 -45
- package/src/orchestration/patterns/auto.js +34 -47
- package/src/orchestration/patterns/group-chat.js +59 -65
- package/src/orchestration/patterns/handoff.js +67 -65
- package/src/orchestration/patterns/human-in-loop.js +51 -72
- package/src/orchestration/patterns/nested.js +25 -40
- package/src/orchestration/patterns/sequential.js +35 -34
- package/src/orchestration/patterns/swarm.js +63 -56
- package/src/orchestration/patterns/triage.js +150 -109
- package/src/orchestration/reasoning/index.js +9 -9
- package/src/orchestration/reasoning/planning-engine.js +143 -140
- package/src/orchestration/reasoning/reasoning-engine.js +206 -144
- package/src/orchestration/reasoning/self-correction.js +121 -128
- package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
- package/src/orchestration/replanning/alternative-generator.js +37 -42
- package/src/orchestration/replanning/config.js +63 -59
- package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
- package/src/orchestration/replanning/index.js +24 -20
- package/src/orchestration/replanning/plan-evaluator.js +49 -50
- package/src/orchestration/replanning/plan-monitor.js +32 -28
- package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
- package/src/orchestration/replanning/replan-history.js +33 -26
- package/src/orchestration/replanning/replanning-engine.js +106 -108
- package/src/orchestration/skill-executor.js +107 -109
- package/src/orchestration/skill-registry.js +85 -89
- package/src/orchestration/workflow-examples.js +228 -231
- package/src/orchestration/workflow-executor.js +65 -68
- package/src/orchestration/workflow-orchestrator.js +72 -73
- package/src/phase4-integration.js +47 -40
- package/src/phase5-integration.js +89 -30
- package/src/reporters/coverage-report.js +82 -30
- package/src/reporters/hierarchical-reporter.js +498 -0
- package/src/reporters/traceability-matrix-report.js +29 -20
- package/src/resolvers/issue-resolver.js +43 -31
- package/src/steering/advanced-validation.js +133 -124
- package/src/steering/auto-updater.js +60 -73
- package/src/steering/index.js +6 -6
- package/src/steering/quality-metrics.js +41 -35
- package/src/steering/steering-auto-update.js +83 -86
- package/src/steering/steering-validator.js +98 -106
- package/src/steering/template-constraints.js +53 -54
- package/src/templates/agents/claude-code/CLAUDE.md +32 -32
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
- package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
- package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
- package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
- package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
- package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
- package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
- package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
- package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
- package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
- package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
- package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
- package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
- package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
- package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
- package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
- package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
- package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
- package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
- package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
- package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
- package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
- package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
- package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
- package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
- package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
- package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
- package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
- package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
- package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
- package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
- package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
- package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
- package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
- package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
- package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
- package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
- package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
- package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
- package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
- package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
- package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
- package/src/templates/agents/codex/AGENTS.md +74 -42
- package/src/templates/agents/cursor/AGENTS.md +74 -42
- package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
- package/src/templates/agents/github-copilot/AGENTS.md +83 -51
- package/src/templates/agents/qwen-code/QWEN.md +74 -42
- package/src/templates/agents/windsurf/AGENTS.md +74 -42
- package/src/templates/architectures/README.md +41 -0
- package/src/templates/architectures/clean-architecture/README.md +113 -0
- package/src/templates/architectures/event-driven/README.md +162 -0
- package/src/templates/architectures/hexagonal/README.md +130 -0
- package/src/templates/index.js +6 -1
- package/src/templates/locale-manager.js +16 -16
- package/src/templates/shared/delta-spec-template.md +20 -13
- package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
- package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
- package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
- package/src/templates/shared/steering/structure.md +95 -0
- package/src/templates/skills/browser-agent.md +21 -16
- package/src/templates/skills/web-gui.md +8 -0
- package/src/templates/template-constraints.js +50 -53
- package/src/validators/advanced-validation.js +30 -36
- package/src/validators/constitutional-validator.js +77 -73
- package/src/validators/critic-system.js +49 -59
- package/src/validators/delta-format.js +59 -55
- package/src/validators/traceability-validator.js +7 -11
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUSUBI Hierarchical Reporter
|
|
3
|
+
*
|
|
4
|
+
* Generates hierarchical, drilldown-capable reports for large projects:
|
|
5
|
+
* - Module/directory-based grouping
|
|
6
|
+
* - Hotspot identification
|
|
7
|
+
* - Interactive drill-down support
|
|
8
|
+
* - Multiple output formats
|
|
9
|
+
*
|
|
10
|
+
* Designed for projects with 10,000+ files (like GCC with 109,073 files)
|
|
11
|
+
*
|
|
12
|
+
* @version 5.5.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs-extra');
|
|
16
|
+
// const path = require('path');
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Hierarchical Reporter
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
class HierarchicalReporter {
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.options = {
|
|
25
|
+
maxDepth: 4,
|
|
26
|
+
hotspotThreshold: 25,
|
|
27
|
+
groupingDepth: 3,
|
|
28
|
+
outputFormat: 'markdown',
|
|
29
|
+
...options,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generate hierarchical report from analysis results
|
|
35
|
+
*/
|
|
36
|
+
generateReport(analysis, options = {}) {
|
|
37
|
+
const mergedOptions = { ...this.options, ...options };
|
|
38
|
+
|
|
39
|
+
const report = {
|
|
40
|
+
generatedAt: new Date().toISOString(),
|
|
41
|
+
projectPath: analysis.projectPath || process.cwd(),
|
|
42
|
+
summary: this.generateSummary(analysis),
|
|
43
|
+
hierarchy: this.buildHierarchy(
|
|
44
|
+
analysis.files || analysis.results?.files || [],
|
|
45
|
+
mergedOptions.groupingDepth
|
|
46
|
+
),
|
|
47
|
+
hotspots: this.identifyHotspots(analysis, mergedOptions.hotspotThreshold),
|
|
48
|
+
trends: this.analyzeTrends(analysis),
|
|
49
|
+
recommendations: this.generateRecommendations(analysis),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return report;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate executive summary
|
|
57
|
+
*/
|
|
58
|
+
generateSummary(analysis) {
|
|
59
|
+
const files = analysis.files || analysis.results?.files || [];
|
|
60
|
+
const summary = analysis.summary || analysis.results?.summary || {};
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
totalFiles: files.length,
|
|
64
|
+
totalLines: summary.totalLines || files.reduce((sum, f) => sum + (f.lines || 0), 0),
|
|
65
|
+
averageComplexity: summary.averageComplexity || this.calculateAverage(files, 'complexity'),
|
|
66
|
+
averageMaintainability:
|
|
67
|
+
summary.averageMaintainability || this.calculateAverage(files, 'maintainability'),
|
|
68
|
+
languageDistribution:
|
|
69
|
+
summary.languageDistribution || this.calculateLanguageDistribution(files),
|
|
70
|
+
issueCount: this.countIssues(files),
|
|
71
|
+
healthScore: this.calculateHealthScore(files),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build hierarchical tree from file list
|
|
77
|
+
*/
|
|
78
|
+
buildHierarchy(files, depth = 3) {
|
|
79
|
+
const tree = {
|
|
80
|
+
name: 'root',
|
|
81
|
+
path: '',
|
|
82
|
+
stats: {
|
|
83
|
+
files: 0,
|
|
84
|
+
lines: 0,
|
|
85
|
+
complexity: 0,
|
|
86
|
+
issues: 0,
|
|
87
|
+
},
|
|
88
|
+
children: {},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
const filePath = file.path || file.absolutePath || '';
|
|
93
|
+
const parts = filePath.split('/').filter(p => p);
|
|
94
|
+
const relevantParts = parts.slice(0, depth);
|
|
95
|
+
|
|
96
|
+
let current = tree;
|
|
97
|
+
let currentPath = '';
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < relevantParts.length; i++) {
|
|
100
|
+
const part = relevantParts[i];
|
|
101
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
102
|
+
|
|
103
|
+
if (!current.children[part]) {
|
|
104
|
+
current.children[part] = {
|
|
105
|
+
name: part,
|
|
106
|
+
path: currentPath,
|
|
107
|
+
stats: {
|
|
108
|
+
files: 0,
|
|
109
|
+
lines: 0,
|
|
110
|
+
complexity: 0,
|
|
111
|
+
issues: 0,
|
|
112
|
+
},
|
|
113
|
+
children: {},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
current = current.children[part];
|
|
118
|
+
|
|
119
|
+
// Add stats to each level
|
|
120
|
+
current.stats.files++;
|
|
121
|
+
current.stats.lines += file.lines || 0;
|
|
122
|
+
current.stats.complexity += file.complexity || 0;
|
|
123
|
+
current.stats.issues += file.issueCount || file.issues?.length || 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add to root
|
|
127
|
+
tree.stats.files++;
|
|
128
|
+
tree.stats.lines += file.lines || 0;
|
|
129
|
+
tree.stats.complexity += file.complexity || 0;
|
|
130
|
+
tree.stats.issues += file.issueCount || file.issues?.length || 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Calculate averages for each node
|
|
134
|
+
this.calculateNodeAverages(tree);
|
|
135
|
+
|
|
136
|
+
return tree;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Calculate averages for a node and its children recursively
|
|
141
|
+
*/
|
|
142
|
+
calculateNodeAverages(node) {
|
|
143
|
+
if (node.stats.files > 0) {
|
|
144
|
+
node.stats.averageComplexity = Math.round(node.stats.complexity / node.stats.files);
|
|
145
|
+
node.stats.issuesPerFile = Math.round((node.stats.issues / node.stats.files) * 100) / 100;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const child of Object.values(node.children)) {
|
|
149
|
+
this.calculateNodeAverages(child);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Identify hotspots (high-complexity, high-issue areas)
|
|
155
|
+
*/
|
|
156
|
+
identifyHotspots(analysis, threshold = 25) {
|
|
157
|
+
const files = analysis.files || analysis.results?.files || [];
|
|
158
|
+
const hotspots = [];
|
|
159
|
+
|
|
160
|
+
// File-level hotspots
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
const complexity = file.complexity || 0;
|
|
163
|
+
const issues = file.issueCount || file.issues?.length || 0;
|
|
164
|
+
|
|
165
|
+
if (complexity >= threshold || issues >= 3) {
|
|
166
|
+
hotspots.push({
|
|
167
|
+
type: 'file',
|
|
168
|
+
path: file.path,
|
|
169
|
+
complexity,
|
|
170
|
+
issues,
|
|
171
|
+
reason: complexity >= threshold ? 'high-complexity' : 'many-issues',
|
|
172
|
+
severity: complexity >= 50 || issues >= 5 ? 'critical' : 'warning',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Function-level hotspots (from giant functions)
|
|
178
|
+
const giantFunctions = analysis.results?.giantFunctions || [];
|
|
179
|
+
for (const func of giantFunctions) {
|
|
180
|
+
hotspots.push({
|
|
181
|
+
type: 'function',
|
|
182
|
+
path: `${func.file}:${func.name}`,
|
|
183
|
+
lines: func.lines,
|
|
184
|
+
reason: 'giant-function',
|
|
185
|
+
severity: func.lines >= 1000 ? 'critical' : 'warning',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Directory-level hotspots
|
|
190
|
+
const hierarchy = this.buildHierarchy(files, 2);
|
|
191
|
+
for (const [name, node] of Object.entries(hierarchy.children)) {
|
|
192
|
+
if (node.stats.averageComplexity >= threshold || node.stats.issuesPerFile >= 2) {
|
|
193
|
+
hotspots.push({
|
|
194
|
+
type: 'directory',
|
|
195
|
+
path: name,
|
|
196
|
+
files: node.stats.files,
|
|
197
|
+
averageComplexity: node.stats.averageComplexity,
|
|
198
|
+
issuesPerFile: node.stats.issuesPerFile,
|
|
199
|
+
reason: 'concentrated-issues',
|
|
200
|
+
severity: node.stats.averageComplexity >= 50 ? 'critical' : 'warning',
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Sort by severity and complexity
|
|
206
|
+
return hotspots.sort((a, b) => {
|
|
207
|
+
if (a.severity === 'critical' && b.severity !== 'critical') return -1;
|
|
208
|
+
if (a.severity !== 'critical' && b.severity === 'critical') return 1;
|
|
209
|
+
return (b.complexity || b.lines || 0) - (a.complexity || a.lines || 0);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Analyze trends (for comparison with previous analyses)
|
|
215
|
+
*/
|
|
216
|
+
analyzeTrends(analysis) {
|
|
217
|
+
// This would compare with historical data if available
|
|
218
|
+
const files = analysis.files || analysis.results?.files || [];
|
|
219
|
+
|
|
220
|
+
const complexityDistribution = {
|
|
221
|
+
low: 0,
|
|
222
|
+
medium: 0,
|
|
223
|
+
high: 0,
|
|
224
|
+
extreme: 0,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
for (const file of files) {
|
|
228
|
+
const c = file.complexity || 0;
|
|
229
|
+
if (c < 10) complexityDistribution.low++;
|
|
230
|
+
else if (c < 25) complexityDistribution.medium++;
|
|
231
|
+
else if (c < 50) complexityDistribution.high++;
|
|
232
|
+
else complexityDistribution.extreme++;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
complexityDistribution,
|
|
237
|
+
timestamp: new Date().toISOString(),
|
|
238
|
+
// Future: compare with previous analysis
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Generate recommendations based on analysis
|
|
244
|
+
*/
|
|
245
|
+
generateRecommendations(analysis) {
|
|
246
|
+
const recommendations = [];
|
|
247
|
+
const files = analysis.files || analysis.results?.files || [];
|
|
248
|
+
const summary = analysis.summary || analysis.results?.summary || {};
|
|
249
|
+
const hotspots = this.identifyHotspots(analysis);
|
|
250
|
+
|
|
251
|
+
// Giant functions
|
|
252
|
+
const giantFunctions = analysis.results?.giantFunctions || [];
|
|
253
|
+
if (giantFunctions.length > 0) {
|
|
254
|
+
recommendations.push({
|
|
255
|
+
priority: 'P0',
|
|
256
|
+
category: 'refactoring',
|
|
257
|
+
title: 'Refactor Giant Functions',
|
|
258
|
+
description: `${giantFunctions.length} functions exceed 1000 lines`,
|
|
259
|
+
impact: 'Significantly improves maintainability and testability',
|
|
260
|
+
effort: 'High',
|
|
261
|
+
items: giantFunctions.slice(0, 5).map(f => `${f.file}:${f.name} (${f.lines} lines)`),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Critical hotspots
|
|
266
|
+
const criticalHotspots = hotspots.filter(h => h.severity === 'critical');
|
|
267
|
+
if (criticalHotspots.length > 0) {
|
|
268
|
+
recommendations.push({
|
|
269
|
+
priority: 'P1',
|
|
270
|
+
category: 'quality',
|
|
271
|
+
title: 'Address Critical Hotspots',
|
|
272
|
+
description: `${criticalHotspots.length} critical areas need attention`,
|
|
273
|
+
impact: 'Reduces bug risk and improves code health',
|
|
274
|
+
effort: 'Medium',
|
|
275
|
+
items: criticalHotspots.slice(0, 5).map(h => `${h.path} (${h.reason})`),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Low maintainability
|
|
280
|
+
const avgMaintainability =
|
|
281
|
+
summary.averageMaintainability || this.calculateAverage(files, 'maintainability');
|
|
282
|
+
if (avgMaintainability < 40) {
|
|
283
|
+
recommendations.push({
|
|
284
|
+
priority: 'P2',
|
|
285
|
+
category: 'documentation',
|
|
286
|
+
title: 'Improve Code Documentation',
|
|
287
|
+
description: `Average maintainability index is ${avgMaintainability} (target: >60)`,
|
|
288
|
+
impact: 'Easier onboarding and reduced knowledge silos',
|
|
289
|
+
effort: 'Low',
|
|
290
|
+
items: [
|
|
291
|
+
'Add JSDoc/docstring comments to public functions',
|
|
292
|
+
'Document complex algorithms inline',
|
|
293
|
+
'Create architecture documentation',
|
|
294
|
+
],
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Language diversity
|
|
299
|
+
const languages = Object.keys(
|
|
300
|
+
summary.languageDistribution || this.calculateLanguageDistribution(files)
|
|
301
|
+
);
|
|
302
|
+
if (languages.length > 5) {
|
|
303
|
+
recommendations.push({
|
|
304
|
+
priority: 'P3',
|
|
305
|
+
category: 'architecture',
|
|
306
|
+
title: 'Consider Language Consolidation',
|
|
307
|
+
description: `${languages.length} different languages detected`,
|
|
308
|
+
impact: 'Simplified tooling and reduced context switching',
|
|
309
|
+
effort: 'High',
|
|
310
|
+
items: languages,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return recommendations;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Calculate average of a property across files
|
|
319
|
+
*/
|
|
320
|
+
calculateAverage(files, property) {
|
|
321
|
+
if (files.length === 0) return 0;
|
|
322
|
+
const sum = files.reduce((acc, f) => acc + (f[property] || 0), 0);
|
|
323
|
+
return Math.round(sum / files.length);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Calculate language distribution
|
|
328
|
+
*/
|
|
329
|
+
calculateLanguageDistribution(files) {
|
|
330
|
+
const distribution = {};
|
|
331
|
+
for (const file of files) {
|
|
332
|
+
const lang = file.language || 'unknown';
|
|
333
|
+
distribution[lang] = (distribution[lang] || 0) + 1;
|
|
334
|
+
}
|
|
335
|
+
return distribution;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Count total issues across files
|
|
340
|
+
*/
|
|
341
|
+
countIssues(files) {
|
|
342
|
+
return files.reduce((sum, f) => sum + (f.issueCount || f.issues?.length || 0), 0);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Calculate overall health score (0-100)
|
|
347
|
+
*/
|
|
348
|
+
calculateHealthScore(files) {
|
|
349
|
+
if (files.length === 0) return 100;
|
|
350
|
+
|
|
351
|
+
const avgComplexity = this.calculateAverage(files, 'complexity');
|
|
352
|
+
const avgMaintainability = this.calculateAverage(files, 'maintainability');
|
|
353
|
+
const issueRatio = this.countIssues(files) / files.length;
|
|
354
|
+
|
|
355
|
+
// Weighted score
|
|
356
|
+
const complexityScore = Math.max(0, 100 - avgComplexity * 2);
|
|
357
|
+
const maintainabilityScore = avgMaintainability;
|
|
358
|
+
const issueScore = Math.max(0, 100 - issueRatio * 20);
|
|
359
|
+
|
|
360
|
+
return Math.round(complexityScore * 0.3 + maintainabilityScore * 0.4 + issueScore * 0.3);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get drill-down data for a specific path
|
|
365
|
+
*/
|
|
366
|
+
drillDown(report, targetPath) {
|
|
367
|
+
const pathParts = targetPath.split('/').filter(p => p);
|
|
368
|
+
let current = report.hierarchy;
|
|
369
|
+
|
|
370
|
+
for (const part of pathParts) {
|
|
371
|
+
if (current.children && current.children[part]) {
|
|
372
|
+
current = current.children[part];
|
|
373
|
+
} else {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
path: targetPath,
|
|
380
|
+
stats: current.stats,
|
|
381
|
+
children: Object.entries(current.children).map(([name, node]) => ({
|
|
382
|
+
name,
|
|
383
|
+
path: node.path,
|
|
384
|
+
stats: node.stats,
|
|
385
|
+
hasChildren: Object.keys(node.children).length > 0,
|
|
386
|
+
})),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Format report as Markdown
|
|
392
|
+
*/
|
|
393
|
+
formatAsMarkdown(report) {
|
|
394
|
+
let md = '# Hierarchical Code Analysis Report\n\n';
|
|
395
|
+
md += `**Generated**: ${report.generatedAt}\n`;
|
|
396
|
+
md += `**Project**: ${report.projectPath}\n\n`;
|
|
397
|
+
|
|
398
|
+
// Summary
|
|
399
|
+
md += '## Executive Summary\n\n';
|
|
400
|
+
md += `| Metric | Value |\n|--------|-------|\n`;
|
|
401
|
+
md += `| Total Files | ${report.summary.totalFiles.toLocaleString()} |\n`;
|
|
402
|
+
md += `| Total Lines | ${report.summary.totalLines.toLocaleString()} |\n`;
|
|
403
|
+
md += `| Average Complexity | ${report.summary.averageComplexity} |\n`;
|
|
404
|
+
md += `| Average Maintainability | ${report.summary.averageMaintainability} |\n`;
|
|
405
|
+
md += `| Total Issues | ${report.summary.issueCount} |\n`;
|
|
406
|
+
md += `| Health Score | ${report.summary.healthScore}/100 |\n\n`;
|
|
407
|
+
|
|
408
|
+
// Language distribution
|
|
409
|
+
md += '### Language Distribution\n\n';
|
|
410
|
+
md += '| Language | Files |\n|----------|-------|\n';
|
|
411
|
+
for (const [lang, count] of Object.entries(report.summary.languageDistribution)) {
|
|
412
|
+
md += `| ${lang} | ${count} |\n`;
|
|
413
|
+
}
|
|
414
|
+
md += '\n';
|
|
415
|
+
|
|
416
|
+
// Hierarchy (top level)
|
|
417
|
+
md += '## Project Structure\n\n';
|
|
418
|
+
md += '| Directory | Files | Lines | Avg Complexity | Issues |\n';
|
|
419
|
+
md += '|-----------|-------|-------|----------------|--------|\n';
|
|
420
|
+
for (const [name, node] of Object.entries(report.hierarchy.children)) {
|
|
421
|
+
md += `| ${name}/ | ${node.stats.files} | ${node.stats.lines.toLocaleString()} | ${node.stats.averageComplexity} | ${node.stats.issues} |\n`;
|
|
422
|
+
}
|
|
423
|
+
md += '\n';
|
|
424
|
+
|
|
425
|
+
// Hotspots
|
|
426
|
+
if (report.hotspots.length > 0) {
|
|
427
|
+
md += '## Hotspots\n\n';
|
|
428
|
+
md += '| Type | Path | Severity | Reason |\n';
|
|
429
|
+
md += '|------|------|----------|--------|\n';
|
|
430
|
+
for (const hotspot of report.hotspots.slice(0, 20)) {
|
|
431
|
+
md += `| ${hotspot.type} | ${hotspot.path} | ${hotspot.severity} | ${hotspot.reason} |\n`;
|
|
432
|
+
}
|
|
433
|
+
md += '\n';
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Recommendations
|
|
437
|
+
if (report.recommendations.length > 0) {
|
|
438
|
+
md += '## Recommendations\n\n';
|
|
439
|
+
for (const rec of report.recommendations) {
|
|
440
|
+
md += `### ${rec.priority}: ${rec.title}\n\n`;
|
|
441
|
+
md += `**Category**: ${rec.category}\n`;
|
|
442
|
+
md += `**Impact**: ${rec.impact}\n`;
|
|
443
|
+
md += `**Effort**: ${rec.effort}\n\n`;
|
|
444
|
+
md += `${rec.description}\n\n`;
|
|
445
|
+
if (rec.items && rec.items.length > 0) {
|
|
446
|
+
md += 'Items:\n';
|
|
447
|
+
for (const item of rec.items) {
|
|
448
|
+
md += `- ${item}\n`;
|
|
449
|
+
}
|
|
450
|
+
md += '\n';
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Complexity distribution
|
|
456
|
+
md += '## Complexity Distribution\n\n';
|
|
457
|
+
md += '| Level | File Count |\n|-------|------------|\n';
|
|
458
|
+
for (const [level, count] of Object.entries(report.trends.complexityDistribution)) {
|
|
459
|
+
md += `| ${level} | ${count} |\n`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return md;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Format report as JSON
|
|
467
|
+
*/
|
|
468
|
+
formatAsJson(report) {
|
|
469
|
+
return JSON.stringify(report, null, 2);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Save report to file
|
|
474
|
+
*/
|
|
475
|
+
async saveReport(report, outputPath, format = 'markdown') {
|
|
476
|
+
let content;
|
|
477
|
+
let extension;
|
|
478
|
+
|
|
479
|
+
switch (format) {
|
|
480
|
+
case 'json':
|
|
481
|
+
content = this.formatAsJson(report);
|
|
482
|
+
extension = '.json';
|
|
483
|
+
break;
|
|
484
|
+
case 'markdown':
|
|
485
|
+
default:
|
|
486
|
+
content = this.formatAsMarkdown(report);
|
|
487
|
+
extension = '.md';
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const finalPath = outputPath.endsWith(extension) ? outputPath : outputPath + extension;
|
|
492
|
+
await fs.writeFile(finalPath, content, 'utf8');
|
|
493
|
+
|
|
494
|
+
return finalPath;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
module.exports = { HierarchicalReporter };
|
|
@@ -415,11 +415,12 @@ class TraceabilityMatrixReport {
|
|
|
415
415
|
return '<tr><td colspan="6" style="text-align: center;">No requirements found</td></tr>';
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
-
return forward
|
|
419
|
-
|
|
420
|
-
|
|
418
|
+
return forward
|
|
419
|
+
.map(item => {
|
|
420
|
+
const req = item.requirement;
|
|
421
|
+
const reqId = req?.id || req?.file || 'Unknown';
|
|
421
422
|
|
|
422
|
-
|
|
423
|
+
return `
|
|
423
424
|
<tr data-complete="${item.complete}">
|
|
424
425
|
<td><code>${this.escapeHtml(reqId)}</code></td>
|
|
425
426
|
<td>${this.formatLinks(item.design, 'design')}</td>
|
|
@@ -433,7 +434,8 @@ class TraceabilityMatrixReport {
|
|
|
433
434
|
</td>
|
|
434
435
|
</tr>
|
|
435
436
|
`;
|
|
436
|
-
|
|
437
|
+
})
|
|
438
|
+
.join('');
|
|
437
439
|
}
|
|
438
440
|
|
|
439
441
|
/**
|
|
@@ -444,11 +446,12 @@ class TraceabilityMatrixReport {
|
|
|
444
446
|
return '<tr><td colspan="6" style="text-align: center;">No tests found</td></tr>';
|
|
445
447
|
}
|
|
446
448
|
|
|
447
|
-
return backward
|
|
448
|
-
|
|
449
|
-
|
|
449
|
+
return backward
|
|
450
|
+
.map(item => {
|
|
451
|
+
const test = item.test;
|
|
452
|
+
const testId = test?.file || test?.id || 'Unknown';
|
|
450
453
|
|
|
451
|
-
|
|
454
|
+
return `
|
|
452
455
|
<tr data-complete="${item.complete}">
|
|
453
456
|
<td><code>${this.escapeHtml(path.basename(testId))}</code></td>
|
|
454
457
|
<td>${this.formatLinks(item.code, 'code')}</td>
|
|
@@ -462,7 +465,8 @@ class TraceabilityMatrixReport {
|
|
|
462
465
|
</td>
|
|
463
466
|
</tr>
|
|
464
467
|
`;
|
|
465
|
-
|
|
468
|
+
})
|
|
469
|
+
.join('');
|
|
466
470
|
}
|
|
467
471
|
|
|
468
472
|
/**
|
|
@@ -473,11 +477,13 @@ class TraceabilityMatrixReport {
|
|
|
473
477
|
return `<span class="badge missing">None</span>`;
|
|
474
478
|
}
|
|
475
479
|
|
|
476
|
-
return items
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
480
|
+
return items
|
|
481
|
+
.map(item => {
|
|
482
|
+
const id = item?.id || item?.file || 'Unknown';
|
|
483
|
+
const displayId = path.basename(id);
|
|
484
|
+
return `<span class="badge link chain-node ${type}">${this.escapeHtml(displayId)}</span>`;
|
|
485
|
+
})
|
|
486
|
+
.join(' ');
|
|
481
487
|
}
|
|
482
488
|
|
|
483
489
|
/**
|
|
@@ -518,10 +524,12 @@ class TraceabilityMatrixReport {
|
|
|
518
524
|
<h3 class="collapsible" onclick="toggleCollapsible(this)">${category} (${items.length})</h3>
|
|
519
525
|
<div class="collapsible-content">
|
|
520
526
|
<ul>
|
|
521
|
-
${items
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
527
|
+
${items
|
|
528
|
+
.map(item => {
|
|
529
|
+
const id = item?.id || item?.file || 'Unknown';
|
|
530
|
+
return `<li><code>${this.escapeHtml(id)}</code></li>`;
|
|
531
|
+
})
|
|
532
|
+
.join('')}
|
|
525
533
|
</ul>
|
|
526
534
|
</div>
|
|
527
535
|
</div>
|
|
@@ -669,7 +677,8 @@ Generated: ${data.timestamp}
|
|
|
669
677
|
const outputDir = path.join(this.workspaceRoot, this.options.outputDir);
|
|
670
678
|
await fs.ensureDir(outputDir);
|
|
671
679
|
|
|
672
|
-
const extension =
|
|
680
|
+
const extension =
|
|
681
|
+
format === ReportFormat.HTML ? '.html' : format === ReportFormat.MARKDOWN ? '.md' : '.json';
|
|
673
682
|
const fullPath = path.join(outputDir, filename + extension);
|
|
674
683
|
|
|
675
684
|
await fs.writeFile(fullPath, report, 'utf8');
|