@su-record/vibe 0.1.2 → 0.1.4
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 +13 -6
- package/bin/vibe +20 -2
- package/package.json +5 -6
- package/scripts/install-mcp.js +31 -5
- package/mcp/dist/__tests__/complexity.test.js +0 -126
- package/mcp/dist/__tests__/memory.test.js +0 -120
- package/mcp/dist/__tests__/python-dart-complexity.test.js +0 -146
- package/mcp/dist/index.js +0 -230
- package/mcp/dist/lib/ContextCompressor.js +0 -305
- package/mcp/dist/lib/MemoryManager.js +0 -334
- package/mcp/dist/lib/ProjectCache.js +0 -126
- package/mcp/dist/lib/PythonParser.js +0 -241
- package/mcp/dist/tools/browser/browserPool.js +0 -76
- package/mcp/dist/tools/browser/browserUtils.js +0 -135
- package/mcp/dist/tools/browser/inspectNetworkRequests.js +0 -140
- package/mcp/dist/tools/browser/monitorConsoleLogs.js +0 -97
- package/mcp/dist/tools/convention/analyzeComplexity.js +0 -248
- package/mcp/dist/tools/convention/applyQualityRules.js +0 -102
- package/mcp/dist/tools/convention/checkCouplingCohesion.js +0 -233
- package/mcp/dist/tools/convention/complexityMetrics.js +0 -133
- package/mcp/dist/tools/convention/dartComplexity.js +0 -117
- package/mcp/dist/tools/convention/getCodingGuide.js +0 -64
- package/mcp/dist/tools/convention/languageDetector.js +0 -50
- package/mcp/dist/tools/convention/pythonComplexity.js +0 -109
- package/mcp/dist/tools/convention/suggestImprovements.js +0 -257
- package/mcp/dist/tools/convention/validateCodeQuality.js +0 -177
- package/mcp/dist/tools/memory/autoSaveContext.js +0 -79
- package/mcp/dist/tools/memory/database.js +0 -123
- package/mcp/dist/tools/memory/deleteMemory.js +0 -39
- package/mcp/dist/tools/memory/listMemories.js +0 -38
- package/mcp/dist/tools/memory/memoryConfig.js +0 -27
- package/mcp/dist/tools/memory/memorySQLite.js +0 -138
- package/mcp/dist/tools/memory/memoryUtils.js +0 -34
- package/mcp/dist/tools/memory/migrate.js +0 -113
- package/mcp/dist/tools/memory/prioritizeMemory.js +0 -109
- package/mcp/dist/tools/memory/recallMemory.js +0 -40
- package/mcp/dist/tools/memory/restoreSessionContext.js +0 -69
- package/mcp/dist/tools/memory/saveMemory.js +0 -34
- package/mcp/dist/tools/memory/searchMemories.js +0 -37
- package/mcp/dist/tools/memory/startSession.js +0 -100
- package/mcp/dist/tools/memory/updateMemory.js +0 -46
- package/mcp/dist/tools/planning/analyzeRequirements.js +0 -166
- package/mcp/dist/tools/planning/createUserStories.js +0 -119
- package/mcp/dist/tools/planning/featureRoadmap.js +0 -202
- package/mcp/dist/tools/planning/generatePrd.js +0 -156
- package/mcp/dist/tools/prompt/analyzePrompt.js +0 -145
- package/mcp/dist/tools/prompt/enhancePrompt.js +0 -105
- package/mcp/dist/tools/semantic/findReferences.js +0 -195
- package/mcp/dist/tools/semantic/findSymbol.js +0 -200
- package/mcp/dist/tools/thinking/analyzeProblem.js +0 -50
- package/mcp/dist/tools/thinking/breakDownProblem.js +0 -140
- package/mcp/dist/tools/thinking/createThinkingChain.js +0 -39
- package/mcp/dist/tools/thinking/formatAsPlan.js +0 -73
- package/mcp/dist/tools/thinking/stepByStepAnalysis.js +0 -58
- package/mcp/dist/tools/thinking/thinkAloudProcess.js +0 -75
- package/mcp/dist/tools/time/getCurrentTime.js +0 -61
- package/mcp/dist/tools/ui/previewUiAscii.js +0 -232
- package/mcp/dist/types/tool.js +0 -2
- package/mcp/package.json +0 -53
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
// Complexity metrics calculation - extracted from analyzeComplexity
|
|
2
|
-
import { Project, ScriptKind } from "ts-morph";
|
|
3
|
-
const AST_PROJECT = new Project({
|
|
4
|
-
useInMemoryFileSystem: true,
|
|
5
|
-
compilerOptions: { allowJs: true, skipLibCheck: true }
|
|
6
|
-
});
|
|
7
|
-
const COMPLEXITY_NODES = new Set([
|
|
8
|
-
'IfStatement', 'ForStatement', 'ForOfStatement', 'ForInStatement',
|
|
9
|
-
'WhileStatement', 'CaseClause', 'ConditionalExpression',
|
|
10
|
-
'DoStatement', 'CatchClause', 'BinaryExpression'
|
|
11
|
-
]);
|
|
12
|
-
export const THRESHOLDS = {
|
|
13
|
-
maxCyclomatic: 10,
|
|
14
|
-
maxCognitive: 15,
|
|
15
|
-
maxHalsteadDifficulty: 10
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* Calculate AST-based cyclomatic complexity
|
|
19
|
-
*/
|
|
20
|
-
export function calculateASTCyclomatic(code) {
|
|
21
|
-
try {
|
|
22
|
-
const sourceFile = AST_PROJECT.createSourceFile('temp.ts', code, {
|
|
23
|
-
overwrite: true,
|
|
24
|
-
scriptKind: ScriptKind.TS
|
|
25
|
-
});
|
|
26
|
-
let complexity = 1;
|
|
27
|
-
sourceFile.forEachDescendant((node) => {
|
|
28
|
-
if (COMPLEXITY_NODES.has(node.getKindName())) {
|
|
29
|
-
complexity++;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return {
|
|
33
|
-
value: complexity,
|
|
34
|
-
threshold: THRESHOLDS.maxCyclomatic,
|
|
35
|
-
status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
|
|
36
|
-
description: 'AST-based branch/decision point count'
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
return {
|
|
41
|
-
value: 0,
|
|
42
|
-
status: 'error',
|
|
43
|
-
description: `AST analysis failed: ${error instanceof Error ? error.message : String(error)}`
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Calculate regex-based cyclomatic complexity (fallback)
|
|
49
|
-
*/
|
|
50
|
-
export function calculateRegexCyclomatic(code) {
|
|
51
|
-
const patterns = /\bif\b|\bfor\b|\bwhile\b|\bcase\b|\b&&\b|\b\|\|\b/g;
|
|
52
|
-
const matches = code.match(patterns) || [];
|
|
53
|
-
const complexity = matches.length + 1;
|
|
54
|
-
return {
|
|
55
|
-
value: complexity,
|
|
56
|
-
threshold: THRESHOLDS.maxCyclomatic,
|
|
57
|
-
status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
|
|
58
|
-
description: 'Number of linearly independent paths'
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Calculate cognitive complexity with nesting awareness
|
|
63
|
-
*/
|
|
64
|
-
export function calculateCognitiveComplexity(code) {
|
|
65
|
-
const lines = code.split('\n');
|
|
66
|
-
let complexity = 0;
|
|
67
|
-
let nestingLevel = 0;
|
|
68
|
-
for (const line of lines) {
|
|
69
|
-
const trimmed = line.trim();
|
|
70
|
-
// Control structures add complexity based on nesting
|
|
71
|
-
if (/(if|for|while|catch|switch)\s*\(/.test(trimmed)) {
|
|
72
|
-
complexity += 1 + nestingLevel;
|
|
73
|
-
}
|
|
74
|
-
// Track nesting level
|
|
75
|
-
const openBraces = (line.match(/\{/g) || []).length;
|
|
76
|
-
const closeBraces = (line.match(/\}/g) || []).length;
|
|
77
|
-
nestingLevel = Math.max(0, nestingLevel + openBraces - closeBraces);
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
value: complexity,
|
|
81
|
-
threshold: THRESHOLDS.maxCognitive,
|
|
82
|
-
status: complexity <= THRESHOLDS.maxCognitive ? 'pass' : 'fail',
|
|
83
|
-
description: 'Difficulty to understand the code'
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Calculate Halstead complexity metrics
|
|
88
|
-
*/
|
|
89
|
-
export function calculateHalsteadMetrics(code) {
|
|
90
|
-
const operators = code.match(/[+\-*/=<>!&|%^~?:]/g) || [];
|
|
91
|
-
const operands = code.match(/\b[a-zA-Z_]\w*\b/g) || [];
|
|
92
|
-
const uniqueOperators = new Set(operators).size;
|
|
93
|
-
const uniqueOperands = new Set(operands).size;
|
|
94
|
-
const vocabulary = uniqueOperators + uniqueOperands;
|
|
95
|
-
const length = operators.length + operands.length;
|
|
96
|
-
const calculatedLength = vocabulary > 0
|
|
97
|
-
? uniqueOperators * Math.log2(uniqueOperators || 1) + uniqueOperands * Math.log2(uniqueOperands || 1)
|
|
98
|
-
: 0;
|
|
99
|
-
const volume = length * Math.log2(vocabulary || 1);
|
|
100
|
-
const difficulty = vocabulary > 0
|
|
101
|
-
? (uniqueOperators / 2) * (operands.length / (uniqueOperands || 1))
|
|
102
|
-
: 0;
|
|
103
|
-
const effort = difficulty * volume;
|
|
104
|
-
return {
|
|
105
|
-
vocabulary,
|
|
106
|
-
length,
|
|
107
|
-
calculatedLength: Math.round(calculatedLength),
|
|
108
|
-
volume: Math.round(volume),
|
|
109
|
-
difficulty: Math.round(difficulty * 100) / 100,
|
|
110
|
-
effort: Math.round(effort),
|
|
111
|
-
timeToProgram: Math.round(effort / 18),
|
|
112
|
-
bugsDelivered: Math.round(volume / 3000 * 100) / 100,
|
|
113
|
-
description: 'Software science metrics measuring program complexity'
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Calculate additional code metrics
|
|
118
|
-
*/
|
|
119
|
-
export function calculateAdditionalMetrics(code) {
|
|
120
|
-
const lines = code.split('\n');
|
|
121
|
-
const nonEmptyLines = lines.filter(line => line.trim().length > 0).length;
|
|
122
|
-
const comments = (code.match(/\/\*[\s\S]*?\*\/|\/\/.*$/gm) || []).length;
|
|
123
|
-
const functions = (code.match(/function\s+\w+|\w+\s*=\s*\(/g) || []).length;
|
|
124
|
-
const classes = (code.match(/class\s+\w+/g) || []).length;
|
|
125
|
-
return {
|
|
126
|
-
linesOfCode: nonEmptyLines,
|
|
127
|
-
comments,
|
|
128
|
-
commentRatio: nonEmptyLines > 0 ? Math.round((comments / nonEmptyLines) * 100) / 100 : 0,
|
|
129
|
-
functions,
|
|
130
|
-
classes,
|
|
131
|
-
averageFunctionLength: functions > 0 ? Math.round(nonEmptyLines / functions) : 0
|
|
132
|
-
};
|
|
133
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dart/Flutter-specific complexity analysis
|
|
3
|
-
*/
|
|
4
|
-
import { THRESHOLDS } from './complexityMetrics.js';
|
|
5
|
-
/**
|
|
6
|
-
* Calculate cyclomatic complexity for Dart code
|
|
7
|
-
*/
|
|
8
|
-
export function calculateDartComplexity(code) {
|
|
9
|
-
// Dart control flow keywords
|
|
10
|
-
const patterns = [
|
|
11
|
-
/\bif\b/g, // if statements
|
|
12
|
-
/\belse\s+if\b/g, // else if
|
|
13
|
-
/\bfor\b/g, // for loops
|
|
14
|
-
/\bwhile\b/g, // while loops
|
|
15
|
-
/\bdo\b/g, // do-while
|
|
16
|
-
/\bswitch\b/g, // switch statements
|
|
17
|
-
/\bcase\b/g, // case clauses
|
|
18
|
-
/\btry\b/g, // try blocks
|
|
19
|
-
/\bcatch\b/g, // catch blocks
|
|
20
|
-
/&&/g, // logical and
|
|
21
|
-
/\|\|/g, // logical or
|
|
22
|
-
/\?\?/g // null-coalescing
|
|
23
|
-
];
|
|
24
|
-
let complexity = 1; // Base complexity
|
|
25
|
-
for (const pattern of patterns) {
|
|
26
|
-
const matches = code.match(pattern);
|
|
27
|
-
if (matches) {
|
|
28
|
-
complexity += matches.length;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
value: complexity,
|
|
33
|
-
threshold: THRESHOLDS.maxCyclomatic,
|
|
34
|
-
status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
|
|
35
|
-
description: 'Dart cyclomatic complexity (control flow branches)'
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Calculate cognitive complexity for Dart
|
|
40
|
-
*/
|
|
41
|
-
export function calculateDartCognitiveComplexity(code) {
|
|
42
|
-
let complexity = 0;
|
|
43
|
-
// Nested control structures
|
|
44
|
-
let nestingLevel = 0;
|
|
45
|
-
const lines = code.split('\n');
|
|
46
|
-
for (const line of lines) {
|
|
47
|
-
const trimmed = line.trim();
|
|
48
|
-
// Track nesting level
|
|
49
|
-
const openBraces = (line.match(/{/g) || []).length;
|
|
50
|
-
const closeBraces = (line.match(/}/g) || []).length;
|
|
51
|
-
// Control flow keywords
|
|
52
|
-
if (/\b(if|else|for|while|switch|try|catch)\b/.test(trimmed)) {
|
|
53
|
-
complexity += 1 + nestingLevel;
|
|
54
|
-
}
|
|
55
|
-
// Ternary operators
|
|
56
|
-
const ternaryCount = (trimmed.match(/\?.*:/g) || []).length;
|
|
57
|
-
complexity += ternaryCount;
|
|
58
|
-
// Null-aware operators (Flutter specific)
|
|
59
|
-
const nullAwareCount = (trimmed.match(/\?\?|!\.|\/\?/g) || []).length;
|
|
60
|
-
complexity += nullAwareCount * 0.5; // Half weight
|
|
61
|
-
// Update nesting
|
|
62
|
-
nestingLevel += openBraces - closeBraces;
|
|
63
|
-
if (nestingLevel < 0)
|
|
64
|
-
nestingLevel = 0;
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
value: Math.round(complexity),
|
|
68
|
-
threshold: THRESHOLDS.maxCognitive,
|
|
69
|
-
status: complexity <= THRESHOLDS.maxCognitive ? 'pass' : 'fail',
|
|
70
|
-
description: 'Dart cognitive complexity (mental load to understand)'
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Analyze Flutter/Dart code quality
|
|
75
|
-
*/
|
|
76
|
-
export function analyzeDartQuality(code) {
|
|
77
|
-
const issues = [];
|
|
78
|
-
const recommendations = [];
|
|
79
|
-
// Flutter-specific anti-patterns
|
|
80
|
-
if (/setState\(\(\)\s*{\s*[\s\S]*?}\s*\)/.test(code)) {
|
|
81
|
-
const setStateCount = (code.match(/setState/g) || []).length;
|
|
82
|
-
if (setStateCount > 5) {
|
|
83
|
-
issues.push('Too many setState calls - consider state management solution');
|
|
84
|
-
recommendations.push('Use Provider, Riverpod, or Bloc for complex state');
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Deep widget nesting
|
|
88
|
-
const widgetNesting = (code.match(/child:\s*\w+\(/g) || []).length;
|
|
89
|
-
if (widgetNesting > 5) {
|
|
90
|
-
issues.push('Deep widget nesting detected');
|
|
91
|
-
recommendations.push('Extract nested widgets into separate methods or classes');
|
|
92
|
-
}
|
|
93
|
-
// Missing const constructors
|
|
94
|
-
const widgetConstructors = (code.match(/\w+\(/g) || []).length;
|
|
95
|
-
const constConstructors = (code.match(/const\s+\w+\(/g) || []).length;
|
|
96
|
-
if (widgetConstructors > 10 && constConstructors < widgetConstructors * 0.3) {
|
|
97
|
-
issues.push('Many widgets without const constructors');
|
|
98
|
-
recommendations.push('Use const constructors for immutable widgets to improve performance');
|
|
99
|
-
}
|
|
100
|
-
// Missing key in lists
|
|
101
|
-
if (/ListView\.builder|GridView\.builder/.test(code) && !/key:/.test(code)) {
|
|
102
|
-
issues.push('ListView/GridView without keys');
|
|
103
|
-
recommendations.push('Add keys to list items for better performance');
|
|
104
|
-
}
|
|
105
|
-
// Missing null safety
|
|
106
|
-
if (!/\w+\?|\w+!/.test(code) && code.includes('null')) {
|
|
107
|
-
issues.push('Possible null safety issues');
|
|
108
|
-
recommendations.push('Enable null safety and use ? and ! operators appropriately');
|
|
109
|
-
}
|
|
110
|
-
// Large build methods
|
|
111
|
-
const buildMethodMatch = code.match(/Widget build\(BuildContext context\)\s*{([\s\S]*?)^ }/m);
|
|
112
|
-
if (buildMethodMatch && buildMethodMatch[1].split('\n').length > 50) {
|
|
113
|
-
issues.push('Build method is too large (> 50 lines)');
|
|
114
|
-
recommendations.push('Extract widgets into separate methods or classes');
|
|
115
|
-
}
|
|
116
|
-
return { issues, recommendations };
|
|
117
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Convention management tool - completely independent
|
|
2
|
-
import { promises as fs } from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
const GUIDES_DIR = path.join(process.cwd(), 'guides');
|
|
5
|
-
const GUIDES_FILE = path.join(GUIDES_DIR, 'coding_guides.json');
|
|
6
|
-
async function ensureGuidesDir() {
|
|
7
|
-
try {
|
|
8
|
-
await fs.access(GUIDES_DIR);
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
await fs.mkdir(GUIDES_DIR, { recursive: true });
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
async function loadGuides() {
|
|
15
|
-
try {
|
|
16
|
-
await ensureGuidesDir();
|
|
17
|
-
const data = await fs.readFile(GUIDES_FILE, 'utf-8');
|
|
18
|
-
return JSON.parse(data);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async function findGuide(name) {
|
|
25
|
-
const guides = await loadGuides();
|
|
26
|
-
return guides.find(g => g.name === name);
|
|
27
|
-
}
|
|
28
|
-
export const getCodingGuideDefinition = {
|
|
29
|
-
name: 'get_coding_guide',
|
|
30
|
-
description: '가이드|규칙|컨벤션|guide|rules|convention|standards|best practices - Get coding guide',
|
|
31
|
-
inputSchema: {
|
|
32
|
-
type: 'object',
|
|
33
|
-
properties: {
|
|
34
|
-
name: { type: 'string', description: 'Guide name to retrieve' },
|
|
35
|
-
category: { type: 'string', description: 'Guide category' }
|
|
36
|
-
},
|
|
37
|
-
required: ['name']
|
|
38
|
-
},
|
|
39
|
-
annotations: {
|
|
40
|
-
title: 'Get Coding Guide',
|
|
41
|
-
audience: ['user', 'assistant']
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
export async function getCodingGuide(args) {
|
|
45
|
-
const { name: guideName, category: guideCategory } = args;
|
|
46
|
-
try {
|
|
47
|
-
const guide = await findGuide(guideName);
|
|
48
|
-
if (guide) {
|
|
49
|
-
return {
|
|
50
|
-
content: [{ type: 'text', text: `Guide: ${guide.name}\nCategory: ${guide.category}\n\n${guide.content}\n\nTags: ${guide.tags.join(', ')} | Updated: ${guide.lastUpdated}` }]
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
return {
|
|
55
|
-
content: [{ type: 'text', text: `Guide not found: "${guideName}". Use list_coding_guides to see available guides.` }]
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
return {
|
|
61
|
-
content: [{ type: 'text', text: `Error retrieving guide: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Language detection utility for code quality tools
|
|
3
|
-
* Supports TypeScript, JavaScript, Python, and Dart/Flutter
|
|
4
|
-
*/
|
|
5
|
-
export function detectLanguage(code) {
|
|
6
|
-
// Dart/Flutter indicators (check first - most specific)
|
|
7
|
-
if (/\bWidget\b/.test(code) ||
|
|
8
|
-
/\bStatelessWidget\b/.test(code) ||
|
|
9
|
-
/\bStatefulWidget\b/.test(code) ||
|
|
10
|
-
/\bBuildContext\b/.test(code) ||
|
|
11
|
-
/Widget build\(/.test(code) ||
|
|
12
|
-
/extends\s+(StatelessWidget|StatefulWidget|State)/.test(code) ||
|
|
13
|
-
(/@override/i.test(code) && /Widget|BuildContext/.test(code))) {
|
|
14
|
-
return 'dart';
|
|
15
|
-
}
|
|
16
|
-
// Python indicators
|
|
17
|
-
if (/^(def|async def)\s/m.test(code) ||
|
|
18
|
-
/^from\s+\w+\s+import/.test(code) ||
|
|
19
|
-
/^\s+def\s/m.test(code) || // Indented function definitions
|
|
20
|
-
/\belif\b/.test(code) ||
|
|
21
|
-
/\b__init__\b/.test(code) ||
|
|
22
|
-
/\bprint\(/m.test(code) ||
|
|
23
|
-
(/:\s*$/m.test(code) && !/;/.test(code)) // Python colon without semicolon
|
|
24
|
-
) {
|
|
25
|
-
return 'python';
|
|
26
|
-
}
|
|
27
|
-
// TypeScript indicators
|
|
28
|
-
if (/:\s*(string|number|boolean|any|void|unknown|never)\b/.test(code) ||
|
|
29
|
-
/interface\s+\w+/.test(code) ||
|
|
30
|
-
/type\s+\w+\s*=/.test(code) ||
|
|
31
|
-
/<[A-Z]\w*>/.test(code) // generics with capital letters
|
|
32
|
-
) {
|
|
33
|
-
return 'typescript';
|
|
34
|
-
}
|
|
35
|
-
// JavaScript (default if none match)
|
|
36
|
-
if (/\b(const|let|var|function|class|async|await|import|export)\b/.test(code)) {
|
|
37
|
-
return 'javascript';
|
|
38
|
-
}
|
|
39
|
-
return 'unknown';
|
|
40
|
-
}
|
|
41
|
-
export function getLanguageName(lang) {
|
|
42
|
-
const names = {
|
|
43
|
-
typescript: 'TypeScript',
|
|
44
|
-
javascript: 'JavaScript',
|
|
45
|
-
python: 'Python',
|
|
46
|
-
dart: 'Dart/Flutter',
|
|
47
|
-
unknown: 'Unknown'
|
|
48
|
-
};
|
|
49
|
-
return names[lang];
|
|
50
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Python-specific complexity analysis
|
|
3
|
-
*/
|
|
4
|
-
import { THRESHOLDS } from './complexityMetrics.js';
|
|
5
|
-
/**
|
|
6
|
-
* Calculate cyclomatic complexity for Python code
|
|
7
|
-
*/
|
|
8
|
-
export function calculatePythonComplexity(code) {
|
|
9
|
-
// Python control flow keywords
|
|
10
|
-
const patterns = [
|
|
11
|
-
/\bif\b/g, // if statements
|
|
12
|
-
/\belif\b/g, // elif statements
|
|
13
|
-
/\bfor\b/g, // for loops
|
|
14
|
-
/\bwhile\b/g, // while loops
|
|
15
|
-
/\btry\b/g, // try blocks
|
|
16
|
-
/\bexcept\b/g, // except blocks
|
|
17
|
-
/\band\b/g, // logical and
|
|
18
|
-
/\bor\b/g, // logical or
|
|
19
|
-
/\bwith\b/g, // with statements
|
|
20
|
-
/\blambda\b/g // lambda expressions
|
|
21
|
-
];
|
|
22
|
-
let complexity = 1; // Base complexity
|
|
23
|
-
for (const pattern of patterns) {
|
|
24
|
-
const matches = code.match(pattern);
|
|
25
|
-
if (matches) {
|
|
26
|
-
complexity += matches.length;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return {
|
|
30
|
-
value: complexity,
|
|
31
|
-
threshold: THRESHOLDS.maxCyclomatic,
|
|
32
|
-
status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
|
|
33
|
-
description: 'Python cyclomatic complexity (control flow branches)'
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Calculate cognitive complexity for Python
|
|
38
|
-
*/
|
|
39
|
-
export function calculatePythonCognitiveComplexity(code) {
|
|
40
|
-
let complexity = 0;
|
|
41
|
-
let nestingLevel = 0;
|
|
42
|
-
const lines = code.split('\n');
|
|
43
|
-
for (const line of lines) {
|
|
44
|
-
const trimmed = line.trim();
|
|
45
|
-
// Detect indentation level (Python uses indentation for nesting)
|
|
46
|
-
const indent = line.match(/^(\s*)/)?.[1].length || 0;
|
|
47
|
-
const currentNesting = Math.floor(indent / 4); // Assuming 4-space indents
|
|
48
|
-
// Control flow that increases cognitive load
|
|
49
|
-
if (/\b(if|elif|for|while|try|except|with)\b/.test(trimmed)) {
|
|
50
|
-
complexity += 1 + currentNesting;
|
|
51
|
-
}
|
|
52
|
-
// Logical operators increase complexity
|
|
53
|
-
const logicalOps = trimmed.match(/\b(and|or)\b/g);
|
|
54
|
-
if (logicalOps) {
|
|
55
|
-
complexity += logicalOps.length;
|
|
56
|
-
}
|
|
57
|
-
// List comprehensions add cognitive load
|
|
58
|
-
if (/\[.*for.*in.*\]/.test(trimmed)) {
|
|
59
|
-
complexity += 1;
|
|
60
|
-
}
|
|
61
|
-
// Nested functions
|
|
62
|
-
if (/^\s+def\s+/.test(line)) {
|
|
63
|
-
complexity += currentNesting;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
value: complexity,
|
|
68
|
-
threshold: THRESHOLDS.maxCognitive,
|
|
69
|
-
status: complexity <= THRESHOLDS.maxCognitive ? 'pass' : 'fail',
|
|
70
|
-
description: 'Python cognitive complexity (mental load to understand)'
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Analyze Python code quality
|
|
75
|
-
*/
|
|
76
|
-
export function analyzePythonQuality(code) {
|
|
77
|
-
const issues = [];
|
|
78
|
-
const recommendations = [];
|
|
79
|
-
// Check for common Python anti-patterns
|
|
80
|
-
if (code.includes('eval(') || code.includes('exec(')) {
|
|
81
|
-
issues.push('Use of eval() or exec() detected - security risk');
|
|
82
|
-
recommendations.push('Avoid eval() and exec(), use safer alternatives');
|
|
83
|
-
}
|
|
84
|
-
if (/except:\s*$/.test(code)) {
|
|
85
|
-
issues.push('Bare except clause detected');
|
|
86
|
-
recommendations.push('Specify exception types: except ValueError:');
|
|
87
|
-
}
|
|
88
|
-
if (/import \*/.test(code)) {
|
|
89
|
-
issues.push('Wildcard import detected');
|
|
90
|
-
recommendations.push('Import specific names instead of using wildcard');
|
|
91
|
-
}
|
|
92
|
-
// Check for long functions
|
|
93
|
-
const functionMatches = code.match(/def\s+\w+\([^)]*\):/g) || [];
|
|
94
|
-
if (functionMatches.length > 0) {
|
|
95
|
-
const avgFunctionLength = code.split('\n').length / functionMatches.length;
|
|
96
|
-
if (avgFunctionLength > 50) {
|
|
97
|
-
issues.push('Functions are too long (avg > 50 lines)');
|
|
98
|
-
recommendations.push('Break down large functions into smaller ones');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Check for PEP 8 violations (basic)
|
|
102
|
-
const lines = code.split('\n');
|
|
103
|
-
const longLines = lines.filter(line => line.length > 79);
|
|
104
|
-
if (longLines.length > lines.length * 0.2) {
|
|
105
|
-
issues.push('Many lines exceed 79 characters (PEP 8)');
|
|
106
|
-
recommendations.push('Keep lines under 79 characters for better readability');
|
|
107
|
-
}
|
|
108
|
-
return { issues, recommendations };
|
|
109
|
-
}
|