musubi-sdd 5.0.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 +164 -145
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +247 -125
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +83 -80
- 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 +53 -44
- package/src/monitoring/incident-manager.js +123 -103
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +82 -59
- package/src/monitoring/quality-dashboard.js +51 -39
- package/src/monitoring/release-manager.js +70 -50
- 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,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUSUBI Complexity Analyzer (Enhanced)
|
|
3
|
+
*
|
|
4
|
+
* Advanced complexity detection including:
|
|
5
|
+
* - Giant function detection (100/500/1000+ lines)
|
|
6
|
+
* - Cyclomatic complexity with severity levels
|
|
7
|
+
* - Cognitive complexity calculation
|
|
8
|
+
* - Dependency complexity
|
|
9
|
+
* - Split recommendations
|
|
10
|
+
*
|
|
11
|
+
* Based on GCC analysis where 95 functions exceeded 1000 lines
|
|
12
|
+
*
|
|
13
|
+
* @version 5.5.0
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs-extra');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Thresholds
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
const THRESHOLDS = {
|
|
24
|
+
functionLines: {
|
|
25
|
+
ideal: 50,
|
|
26
|
+
warning: 100,
|
|
27
|
+
critical: 500,
|
|
28
|
+
extreme: 1000,
|
|
29
|
+
},
|
|
30
|
+
cyclomaticComplexity: {
|
|
31
|
+
ideal: 5,
|
|
32
|
+
warning: 10,
|
|
33
|
+
critical: 25,
|
|
34
|
+
extreme: 50,
|
|
35
|
+
},
|
|
36
|
+
cognitiveComplexity: {
|
|
37
|
+
ideal: 8,
|
|
38
|
+
warning: 15,
|
|
39
|
+
critical: 30,
|
|
40
|
+
extreme: 60,
|
|
41
|
+
},
|
|
42
|
+
dependencies: {
|
|
43
|
+
ideal: 5,
|
|
44
|
+
warning: 10,
|
|
45
|
+
critical: 30,
|
|
46
|
+
extreme: 100,
|
|
47
|
+
},
|
|
48
|
+
fileLines: {
|
|
49
|
+
ideal: 300,
|
|
50
|
+
warning: 500,
|
|
51
|
+
critical: 1000,
|
|
52
|
+
extreme: 2000,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Complexity Analyzer
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
class ComplexityAnalyzer {
|
|
61
|
+
constructor(options = {}) {
|
|
62
|
+
this.options = {
|
|
63
|
+
thresholds: { ...THRESHOLDS, ...options.thresholds },
|
|
64
|
+
includeRecommendations: true,
|
|
65
|
+
...options,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Analyze a file for complexity issues
|
|
71
|
+
*/
|
|
72
|
+
async analyzeFile(filePath) {
|
|
73
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
74
|
+
const lines = content.split('\n');
|
|
75
|
+
const language = this.detectLanguage(filePath);
|
|
76
|
+
|
|
77
|
+
const analysis = {
|
|
78
|
+
path: filePath,
|
|
79
|
+
language,
|
|
80
|
+
totalLines: lines.length,
|
|
81
|
+
codeLines: this.countCodeLines(lines),
|
|
82
|
+
functions: [],
|
|
83
|
+
issues: [],
|
|
84
|
+
metrics: {
|
|
85
|
+
cyclomaticComplexity: 0,
|
|
86
|
+
cognitiveComplexity: 0,
|
|
87
|
+
maintainabilityIndex: 0,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Extract and analyze functions
|
|
92
|
+
analysis.functions = await this.extractFunctions(content, language, lines);
|
|
93
|
+
|
|
94
|
+
// Calculate file-level metrics
|
|
95
|
+
analysis.metrics.cyclomaticComplexity = this.calculateCyclomaticComplexity(content);
|
|
96
|
+
analysis.metrics.cognitiveComplexity = this.calculateCognitiveComplexity(content, language);
|
|
97
|
+
analysis.metrics.maintainabilityIndex = this.calculateMaintainabilityIndex(
|
|
98
|
+
analysis.codeLines,
|
|
99
|
+
analysis.metrics.cyclomaticComplexity
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Detect issues
|
|
103
|
+
analysis.issues = this.detectIssues(analysis);
|
|
104
|
+
|
|
105
|
+
return analysis;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Detect language from file extension
|
|
110
|
+
*/
|
|
111
|
+
detectLanguage(filePath) {
|
|
112
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
113
|
+
const langMap = {
|
|
114
|
+
'.js': 'javascript',
|
|
115
|
+
'.ts': 'typescript',
|
|
116
|
+
'.jsx': 'javascript',
|
|
117
|
+
'.tsx': 'typescript',
|
|
118
|
+
'.c': 'c',
|
|
119
|
+
'.h': 'c',
|
|
120
|
+
'.cpp': 'cpp',
|
|
121
|
+
'.cc': 'cpp',
|
|
122
|
+
'.hpp': 'cpp',
|
|
123
|
+
'.py': 'python',
|
|
124
|
+
'.rs': 'rust',
|
|
125
|
+
'.go': 'go',
|
|
126
|
+
'.java': 'java',
|
|
127
|
+
};
|
|
128
|
+
return langMap[ext] || 'unknown';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Count non-empty, non-comment lines
|
|
133
|
+
*/
|
|
134
|
+
countCodeLines(lines) {
|
|
135
|
+
return lines.filter(line => {
|
|
136
|
+
const trimmed = line.trim();
|
|
137
|
+
return (
|
|
138
|
+
trimmed.length > 0 &&
|
|
139
|
+
!trimmed.startsWith('//') &&
|
|
140
|
+
!trimmed.startsWith('/*') &&
|
|
141
|
+
!trimmed.startsWith('*') &&
|
|
142
|
+
!trimmed.startsWith('#')
|
|
143
|
+
);
|
|
144
|
+
}).length;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Extract functions from code
|
|
149
|
+
*/
|
|
150
|
+
async extractFunctions(content, language, lines) {
|
|
151
|
+
const functions = [];
|
|
152
|
+
const patterns = this.getFunctionPatterns(language);
|
|
153
|
+
|
|
154
|
+
for (const pattern of patterns) {
|
|
155
|
+
let match;
|
|
156
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
157
|
+
const name = match[1] || match[2] || 'anonymous';
|
|
158
|
+
const startIndex = match.index;
|
|
159
|
+
const startLine = content.substring(0, startIndex).split('\n').length;
|
|
160
|
+
const endLine = this.findFunctionEnd(lines, startLine - 1, language);
|
|
161
|
+
const functionLines = endLine - startLine + 1;
|
|
162
|
+
|
|
163
|
+
const functionContent = lines.slice(startLine - 1, endLine).join('\n');
|
|
164
|
+
|
|
165
|
+
functions.push({
|
|
166
|
+
name,
|
|
167
|
+
startLine,
|
|
168
|
+
endLine,
|
|
169
|
+
lines: functionLines,
|
|
170
|
+
cyclomaticComplexity: this.calculateCyclomaticComplexity(functionContent),
|
|
171
|
+
cognitiveComplexity: this.calculateCognitiveComplexity(functionContent, language),
|
|
172
|
+
severity: this.getSeverity(functionLines, 'functionLines'),
|
|
173
|
+
issues: [],
|
|
174
|
+
recommendations: [],
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Add issues and recommendations to each function
|
|
180
|
+
for (const func of functions) {
|
|
181
|
+
func.issues = this.detectFunctionIssues(func);
|
|
182
|
+
if (this.options.includeRecommendations && func.issues.length > 0) {
|
|
183
|
+
func.recommendations = this.generateRecommendations(func);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return functions;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get function detection patterns for language
|
|
192
|
+
*/
|
|
193
|
+
getFunctionPatterns(language) {
|
|
194
|
+
const patterns = {
|
|
195
|
+
javascript: [
|
|
196
|
+
/function\s+(\w+)\s*\(/g,
|
|
197
|
+
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?function/g,
|
|
198
|
+
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g,
|
|
199
|
+
/(\w+)\s*:\s*(?:async\s+)?function/g,
|
|
200
|
+
],
|
|
201
|
+
typescript: [
|
|
202
|
+
/function\s+(\w+)\s*[<(]/g,
|
|
203
|
+
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?function/g,
|
|
204
|
+
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g,
|
|
205
|
+
/(\w+)\s*\([^)]*\)\s*:\s*\w+\s*\{/g,
|
|
206
|
+
],
|
|
207
|
+
c: [/(?:static\s+)?(?:inline\s+)?(?:\w+\s+)+(\w+)\s*\([^)]*\)\s*\{/g],
|
|
208
|
+
cpp: [
|
|
209
|
+
/(?:static\s+)?(?:inline\s+)?(?:virtual\s+)?(?:\w+\s+)+(\w+)\s*\([^)]*\)(?:\s*const)?\s*(?:override)?\s*\{/g,
|
|
210
|
+
/(\w+)::(\w+)\s*\([^)]*\)\s*\{/g,
|
|
211
|
+
],
|
|
212
|
+
python: [/def\s+(\w+)\s*\(/g, /async\s+def\s+(\w+)\s*\(/g],
|
|
213
|
+
rust: [/(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/g],
|
|
214
|
+
go: [/func\s+(?:\([^)]+\)\s+)?(\w+)/g],
|
|
215
|
+
java: [
|
|
216
|
+
/(?:public|private|protected)?\s*(?:static)?\s*(?:\w+)\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+[\w,\s]+)?\s*\{/g,
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
return patterns[language] || patterns.javascript;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Find end of function
|
|
224
|
+
*/
|
|
225
|
+
findFunctionEnd(lines, startLine, _language) {
|
|
226
|
+
if (_language === 'python') {
|
|
227
|
+
const startIndent = lines[startLine]?.match(/^\s*/)?.[0].length || 0;
|
|
228
|
+
for (let i = startLine + 1; i < lines.length; i++) {
|
|
229
|
+
const line = lines[i];
|
|
230
|
+
if (line.trim() && line.match(/^\s*/)[0].length <= startIndent) {
|
|
231
|
+
return i;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return lines.length;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let braceCount = 0;
|
|
238
|
+
let started = false;
|
|
239
|
+
|
|
240
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
241
|
+
const line = lines[i];
|
|
242
|
+
for (const char of line) {
|
|
243
|
+
if (char === '{') {
|
|
244
|
+
braceCount++;
|
|
245
|
+
started = true;
|
|
246
|
+
} else if (char === '}') {
|
|
247
|
+
braceCount--;
|
|
248
|
+
if (started && braceCount === 0) {
|
|
249
|
+
return i + 1;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return Math.min(startLine + 100, lines.length);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Calculate cyclomatic complexity
|
|
260
|
+
*/
|
|
261
|
+
calculateCyclomaticComplexity(code) {
|
|
262
|
+
let complexity = 1;
|
|
263
|
+
|
|
264
|
+
const patterns = [
|
|
265
|
+
/\bif\b/g,
|
|
266
|
+
/\belse\s+if\b/g,
|
|
267
|
+
/\bfor\b/g,
|
|
268
|
+
/\bwhile\b/g,
|
|
269
|
+
/\bcase\b/g,
|
|
270
|
+
/\bcatch\b/g,
|
|
271
|
+
/&&/g,
|
|
272
|
+
/\|\|/g,
|
|
273
|
+
/\?[^:]*:/g,
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
patterns.forEach(pattern => {
|
|
277
|
+
const matches = code.match(pattern);
|
|
278
|
+
if (matches) complexity += matches.length;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return complexity;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Calculate cognitive complexity (SonarSource method)
|
|
286
|
+
*/
|
|
287
|
+
calculateCognitiveComplexity(code, _language) {
|
|
288
|
+
let complexity = 0;
|
|
289
|
+
let nestingLevel = 0;
|
|
290
|
+
|
|
291
|
+
const lines = code.split('\n');
|
|
292
|
+
|
|
293
|
+
for (const line of lines) {
|
|
294
|
+
const trimmed = line.trim();
|
|
295
|
+
|
|
296
|
+
// Nesting increases
|
|
297
|
+
if (/\{$/.test(trimmed)) {
|
|
298
|
+
nestingLevel++;
|
|
299
|
+
}
|
|
300
|
+
if (/^\}/.test(trimmed)) {
|
|
301
|
+
nestingLevel = Math.max(0, nestingLevel - 1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Control flow structures add complexity + nesting penalty
|
|
305
|
+
if (/\b(if|else\s+if|elif)\b/.test(trimmed)) {
|
|
306
|
+
complexity += 1 + nestingLevel;
|
|
307
|
+
}
|
|
308
|
+
if (/\b(for|while|do)\b/.test(trimmed)) {
|
|
309
|
+
complexity += 1 + nestingLevel;
|
|
310
|
+
}
|
|
311
|
+
if (/\b(catch|except)\b/.test(trimmed)) {
|
|
312
|
+
complexity += 1 + nestingLevel;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Switch/match statements
|
|
316
|
+
if (/\b(switch|match)\b/.test(trimmed)) {
|
|
317
|
+
complexity += 1;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Logical operators
|
|
321
|
+
const andOr = (trimmed.match(/&&|\|\|/g) || []).length;
|
|
322
|
+
complexity += andOr;
|
|
323
|
+
|
|
324
|
+
// Recursion (function calls to self)
|
|
325
|
+
// This is simplified - would need function name context
|
|
326
|
+
|
|
327
|
+
// Break/continue with labels
|
|
328
|
+
if (/\b(break|continue)\s+\w+/.test(trimmed)) {
|
|
329
|
+
complexity += 1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Nested ternary
|
|
333
|
+
const ternaries = (trimmed.match(/\?[^:]*:/g) || []).length;
|
|
334
|
+
if (ternaries > 1) {
|
|
335
|
+
complexity += ternaries; // Nested ternaries are especially hard to read
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return complexity;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Calculate maintainability index
|
|
344
|
+
*/
|
|
345
|
+
calculateMaintainabilityIndex(codeLines, complexity) {
|
|
346
|
+
// Simplified Maintainability Index formula
|
|
347
|
+
// MI = 171 - 5.2 * ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code)
|
|
348
|
+
// We use a simplified version
|
|
349
|
+
|
|
350
|
+
const volumeScore = Math.max(0, 100 - Math.log2(codeLines + 1) * 10);
|
|
351
|
+
const complexityScore = Math.max(0, 100 - complexity * 2);
|
|
352
|
+
|
|
353
|
+
return Math.round(volumeScore * 0.5 + complexityScore * 0.5);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get severity level based on threshold
|
|
358
|
+
*/
|
|
359
|
+
getSeverity(value, metricType) {
|
|
360
|
+
const thresholds = this.options.thresholds[metricType];
|
|
361
|
+
if (!thresholds) return 'unknown';
|
|
362
|
+
|
|
363
|
+
if (value >= thresholds.extreme) return 'extreme';
|
|
364
|
+
if (value >= thresholds.critical) return 'critical';
|
|
365
|
+
if (value >= thresholds.warning) return 'warning';
|
|
366
|
+
if (value > thresholds.ideal) return 'minor';
|
|
367
|
+
return 'ok';
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Detect issues in function
|
|
372
|
+
*/
|
|
373
|
+
detectFunctionIssues(func) {
|
|
374
|
+
const issues = [];
|
|
375
|
+
|
|
376
|
+
// Function size
|
|
377
|
+
if (func.lines >= THRESHOLDS.functionLines.extreme) {
|
|
378
|
+
issues.push({
|
|
379
|
+
type: 'giant-function',
|
|
380
|
+
severity: 'extreme',
|
|
381
|
+
metric: 'lines',
|
|
382
|
+
value: func.lines,
|
|
383
|
+
threshold: THRESHOLDS.functionLines.extreme,
|
|
384
|
+
message: `Function "${func.name}" has ${func.lines} lines (extreme: >${THRESHOLDS.functionLines.extreme})`,
|
|
385
|
+
});
|
|
386
|
+
} else if (func.lines >= THRESHOLDS.functionLines.critical) {
|
|
387
|
+
issues.push({
|
|
388
|
+
type: 'very-large-function',
|
|
389
|
+
severity: 'critical',
|
|
390
|
+
metric: 'lines',
|
|
391
|
+
value: func.lines,
|
|
392
|
+
threshold: THRESHOLDS.functionLines.critical,
|
|
393
|
+
message: `Function "${func.name}" has ${func.lines} lines (critical: >${THRESHOLDS.functionLines.critical})`,
|
|
394
|
+
});
|
|
395
|
+
} else if (func.lines >= THRESHOLDS.functionLines.warning) {
|
|
396
|
+
issues.push({
|
|
397
|
+
type: 'large-function',
|
|
398
|
+
severity: 'warning',
|
|
399
|
+
metric: 'lines',
|
|
400
|
+
value: func.lines,
|
|
401
|
+
threshold: THRESHOLDS.functionLines.warning,
|
|
402
|
+
message: `Function "${func.name}" has ${func.lines} lines (warning: >${THRESHOLDS.functionLines.warning})`,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Cyclomatic complexity
|
|
407
|
+
if (func.cyclomaticComplexity >= THRESHOLDS.cyclomaticComplexity.extreme) {
|
|
408
|
+
issues.push({
|
|
409
|
+
type: 'extreme-complexity',
|
|
410
|
+
severity: 'extreme',
|
|
411
|
+
metric: 'cyclomatic',
|
|
412
|
+
value: func.cyclomaticComplexity,
|
|
413
|
+
threshold: THRESHOLDS.cyclomaticComplexity.extreme,
|
|
414
|
+
message: `Function "${func.name}" has cyclomatic complexity ${func.cyclomaticComplexity} (extreme: >${THRESHOLDS.cyclomaticComplexity.extreme})`,
|
|
415
|
+
});
|
|
416
|
+
} else if (func.cyclomaticComplexity >= THRESHOLDS.cyclomaticComplexity.critical) {
|
|
417
|
+
issues.push({
|
|
418
|
+
type: 'high-complexity',
|
|
419
|
+
severity: 'critical',
|
|
420
|
+
metric: 'cyclomatic',
|
|
421
|
+
value: func.cyclomaticComplexity,
|
|
422
|
+
threshold: THRESHOLDS.cyclomaticComplexity.critical,
|
|
423
|
+
message: `Function "${func.name}" has cyclomatic complexity ${func.cyclomaticComplexity} (critical: >${THRESHOLDS.cyclomaticComplexity.critical})`,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Cognitive complexity
|
|
428
|
+
if (func.cognitiveComplexity >= THRESHOLDS.cognitiveComplexity.extreme) {
|
|
429
|
+
issues.push({
|
|
430
|
+
type: 'extreme-cognitive-complexity',
|
|
431
|
+
severity: 'extreme',
|
|
432
|
+
metric: 'cognitive',
|
|
433
|
+
value: func.cognitiveComplexity,
|
|
434
|
+
threshold: THRESHOLDS.cognitiveComplexity.extreme,
|
|
435
|
+
message: `Function "${func.name}" has cognitive complexity ${func.cognitiveComplexity} (extreme: >${THRESHOLDS.cognitiveComplexity.extreme})`,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return issues;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Detect file-level issues
|
|
444
|
+
*/
|
|
445
|
+
detectIssues(analysis) {
|
|
446
|
+
const issues = [];
|
|
447
|
+
|
|
448
|
+
// File size
|
|
449
|
+
if (analysis.codeLines >= THRESHOLDS.fileLines.extreme) {
|
|
450
|
+
issues.push({
|
|
451
|
+
type: 'giant-file',
|
|
452
|
+
severity: 'extreme',
|
|
453
|
+
message: `File has ${analysis.codeLines} code lines (extreme: >${THRESHOLDS.fileLines.extreme})`,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Aggregate function issues
|
|
458
|
+
const giantFunctions = analysis.functions.filter(
|
|
459
|
+
f => f.lines >= THRESHOLDS.functionLines.extreme
|
|
460
|
+
);
|
|
461
|
+
if (giantFunctions.length > 0) {
|
|
462
|
+
issues.push({
|
|
463
|
+
type: 'contains-giant-functions',
|
|
464
|
+
severity: 'extreme',
|
|
465
|
+
message: `File contains ${giantFunctions.length} giant functions (>${THRESHOLDS.functionLines.extreme} lines)`,
|
|
466
|
+
functions: giantFunctions.map(f => f.name),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return issues;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Generate recommendations for a complex function
|
|
475
|
+
*/
|
|
476
|
+
generateRecommendations(func) {
|
|
477
|
+
const recommendations = [];
|
|
478
|
+
|
|
479
|
+
// Size-based recommendations
|
|
480
|
+
if (func.lines >= THRESHOLDS.functionLines.warning) {
|
|
481
|
+
const targetCount = Math.ceil(func.lines / 50);
|
|
482
|
+
recommendations.push({
|
|
483
|
+
type: 'split-function',
|
|
484
|
+
priority: func.lines >= THRESHOLDS.functionLines.extreme ? 'P0' : 'P1',
|
|
485
|
+
title: 'Split into smaller functions',
|
|
486
|
+
description: `Break "${func.name}" into approximately ${targetCount} smaller functions (~50 lines each)`,
|
|
487
|
+
actions: [
|
|
488
|
+
'Identify distinct logical sections within the function',
|
|
489
|
+
'Extract each section into a helper function',
|
|
490
|
+
'Use descriptive names that explain what each helper does',
|
|
491
|
+
'Consider using the "Extract Method" refactoring pattern',
|
|
492
|
+
],
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Complexity-based recommendations
|
|
497
|
+
if (func.cyclomaticComplexity >= THRESHOLDS.cyclomaticComplexity.warning) {
|
|
498
|
+
recommendations.push({
|
|
499
|
+
type: 'reduce-complexity',
|
|
500
|
+
priority:
|
|
501
|
+
func.cyclomaticComplexity >= THRESHOLDS.cyclomaticComplexity.extreme ? 'P0' : 'P1',
|
|
502
|
+
title: 'Reduce cyclomatic complexity',
|
|
503
|
+
description: `Current complexity: ${func.cyclomaticComplexity}, target: <${THRESHOLDS.cyclomaticComplexity.warning}`,
|
|
504
|
+
actions: [
|
|
505
|
+
'Replace nested conditionals with early returns',
|
|
506
|
+
'Use polymorphism instead of type-checking switches',
|
|
507
|
+
'Extract complex conditions into well-named boolean functions',
|
|
508
|
+
'Consider using the Strategy pattern for complex branching',
|
|
509
|
+
],
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Cognitive complexity recommendations
|
|
514
|
+
if (func.cognitiveComplexity >= THRESHOLDS.cognitiveComplexity.warning) {
|
|
515
|
+
recommendations.push({
|
|
516
|
+
type: 'improve-readability',
|
|
517
|
+
priority: 'P2',
|
|
518
|
+
title: 'Improve cognitive readability',
|
|
519
|
+
description: `Cognitive complexity: ${func.cognitiveComplexity}`,
|
|
520
|
+
actions: [
|
|
521
|
+
'Reduce nesting depth by inverting conditions',
|
|
522
|
+
'Avoid nested ternary operators',
|
|
523
|
+
'Break long chains of && and || into named variables',
|
|
524
|
+
'Consider using guard clauses instead of nested if-else',
|
|
525
|
+
],
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return recommendations;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
module.exports = {
|
|
534
|
+
ComplexityAnalyzer,
|
|
535
|
+
THRESHOLDS,
|
|
536
|
+
};
|