@su-record/vibe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +448 -0
- package/agents/backend-python-expert.md +453 -0
- package/agents/database-postgres-expert.md +538 -0
- package/agents/frontend-flutter-expert.md +487 -0
- package/agents/frontend-react-expert.md +424 -0
- package/agents/quality-reviewer.md +542 -0
- package/agents/specification-agent.md +505 -0
- package/bin/sutory +332 -0
- package/bin/vibe +338 -0
- package/mcp/dist/__tests__/complexity.test.js +126 -0
- package/mcp/dist/__tests__/memory.test.js +120 -0
- package/mcp/dist/__tests__/python-dart-complexity.test.js +146 -0
- package/mcp/dist/index.js +230 -0
- package/mcp/dist/lib/ContextCompressor.js +305 -0
- package/mcp/dist/lib/MemoryManager.js +334 -0
- package/mcp/dist/lib/ProjectCache.js +126 -0
- package/mcp/dist/lib/PythonParser.js +241 -0
- package/mcp/dist/tools/browser/browserPool.js +76 -0
- package/mcp/dist/tools/browser/browserUtils.js +135 -0
- package/mcp/dist/tools/browser/inspectNetworkRequests.js +140 -0
- package/mcp/dist/tools/browser/monitorConsoleLogs.js +97 -0
- package/mcp/dist/tools/convention/analyzeComplexity.js +248 -0
- package/mcp/dist/tools/convention/applyQualityRules.js +102 -0
- package/mcp/dist/tools/convention/checkCouplingCohesion.js +233 -0
- package/mcp/dist/tools/convention/complexityMetrics.js +133 -0
- package/mcp/dist/tools/convention/dartComplexity.js +117 -0
- package/mcp/dist/tools/convention/getCodingGuide.js +64 -0
- package/mcp/dist/tools/convention/languageDetector.js +50 -0
- package/mcp/dist/tools/convention/pythonComplexity.js +109 -0
- package/mcp/dist/tools/convention/suggestImprovements.js +257 -0
- package/mcp/dist/tools/convention/validateCodeQuality.js +177 -0
- package/mcp/dist/tools/memory/autoSaveContext.js +79 -0
- package/mcp/dist/tools/memory/database.js +123 -0
- package/mcp/dist/tools/memory/deleteMemory.js +39 -0
- package/mcp/dist/tools/memory/listMemories.js +38 -0
- package/mcp/dist/tools/memory/memoryConfig.js +27 -0
- package/mcp/dist/tools/memory/memorySQLite.js +138 -0
- package/mcp/dist/tools/memory/memoryUtils.js +34 -0
- package/mcp/dist/tools/memory/migrate.js +113 -0
- package/mcp/dist/tools/memory/prioritizeMemory.js +109 -0
- package/mcp/dist/tools/memory/recallMemory.js +40 -0
- package/mcp/dist/tools/memory/restoreSessionContext.js +69 -0
- package/mcp/dist/tools/memory/saveMemory.js +34 -0
- package/mcp/dist/tools/memory/searchMemories.js +37 -0
- package/mcp/dist/tools/memory/startSession.js +100 -0
- package/mcp/dist/tools/memory/updateMemory.js +46 -0
- package/mcp/dist/tools/planning/analyzeRequirements.js +166 -0
- package/mcp/dist/tools/planning/createUserStories.js +119 -0
- package/mcp/dist/tools/planning/featureRoadmap.js +202 -0
- package/mcp/dist/tools/planning/generatePrd.js +156 -0
- package/mcp/dist/tools/prompt/analyzePrompt.js +145 -0
- package/mcp/dist/tools/prompt/enhancePrompt.js +105 -0
- package/mcp/dist/tools/semantic/findReferences.js +195 -0
- package/mcp/dist/tools/semantic/findSymbol.js +200 -0
- package/mcp/dist/tools/thinking/analyzeProblem.js +50 -0
- package/mcp/dist/tools/thinking/breakDownProblem.js +140 -0
- package/mcp/dist/tools/thinking/createThinkingChain.js +39 -0
- package/mcp/dist/tools/thinking/formatAsPlan.js +73 -0
- package/mcp/dist/tools/thinking/stepByStepAnalysis.js +58 -0
- package/mcp/dist/tools/thinking/thinkAloudProcess.js +75 -0
- package/mcp/dist/tools/time/getCurrentTime.js +61 -0
- package/mcp/dist/tools/ui/previewUiAscii.js +232 -0
- package/mcp/dist/types/tool.js +2 -0
- package/mcp/package.json +53 -0
- package/package.json +49 -0
- package/scripts/install-mcp.js +48 -0
- package/scripts/install.sh +70 -0
- package/skills/core/communication-guide.md +104 -0
- package/skills/core/development-philosophy.md +53 -0
- package/skills/core/quick-start.md +121 -0
- package/skills/languages/dart-flutter.md +509 -0
- package/skills/languages/python-fastapi.md +386 -0
- package/skills/languages/typescript-nextjs.md +441 -0
- package/skills/languages/typescript-react-native.md +446 -0
- package/skills/languages/typescript-react.md +525 -0
- package/skills/quality/checklist.md +276 -0
- package/skills/quality/testing-strategy.md +437 -0
- package/skills/standards/anti-patterns.md +369 -0
- package/skills/standards/code-structure.md +291 -0
- package/skills/standards/complexity-metrics.md +312 -0
- package/skills/standards/naming-conventions.md +198 -0
- package/skills/tools/mcp-hi-ai-guide.md +665 -0
- package/skills/tools/mcp-workflow.md +51 -0
- package/templates/constitution-template.md +193 -0
- package/templates/plan-template.md +237 -0
- package/templates/spec-template.md +142 -0
- package/templates/tasks-template.md +132 -0
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// Convention management tool - completely independent
|
|
2
|
+
export const suggestImprovementsDefinition = {
|
|
3
|
+
name: 'suggest_improvements',
|
|
4
|
+
description: '개선|더 좋게|리팩토링|improve|make better|refactor|optimize|enhance code - Suggest improvements',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
code: { type: 'string', description: 'Code to analyze' },
|
|
9
|
+
focus: { type: 'string', description: 'Focus area', enum: ['performance', 'readability', 'maintainability', 'accessibility', 'type-safety'] },
|
|
10
|
+
priority: { type: 'string', description: 'Priority level', enum: ['critical', 'high', 'medium', 'low'] }
|
|
11
|
+
},
|
|
12
|
+
required: ['code']
|
|
13
|
+
},
|
|
14
|
+
annotations: {
|
|
15
|
+
title: 'Suggest Improvements',
|
|
16
|
+
audience: ['user', 'assistant']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export async function suggestImprovements(args) {
|
|
20
|
+
const { code, focus = 'maintainability', priority = 'medium' } = args;
|
|
21
|
+
const suggestions = [];
|
|
22
|
+
const codeLines = code.split('\n');
|
|
23
|
+
const codeLength = codeLines.length;
|
|
24
|
+
// Performance improvements
|
|
25
|
+
if (focus === 'performance' || focus === 'all') {
|
|
26
|
+
// Check for inefficient loops
|
|
27
|
+
if (code.includes('for') && code.includes('length')) {
|
|
28
|
+
suggestions.push({
|
|
29
|
+
category: 'performance',
|
|
30
|
+
priority: 'high',
|
|
31
|
+
issue: 'Potential inefficient loop accessing .length property',
|
|
32
|
+
suggestion: 'Cache array length in a variable before the loop',
|
|
33
|
+
example: 'const len = array.length; for (let i = 0; i < len; i++)'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Check for React performance issues
|
|
37
|
+
if (code.includes('React') || code.includes('jsx') || code.includes('tsx')) {
|
|
38
|
+
if (!code.includes('memo') && !code.includes('useMemo') && !code.includes('useCallback')) {
|
|
39
|
+
suggestions.push({
|
|
40
|
+
category: 'performance',
|
|
41
|
+
priority: 'medium',
|
|
42
|
+
issue: 'Missing React performance optimizations',
|
|
43
|
+
suggestion: 'Consider using React.memo, useMemo, or useCallback',
|
|
44
|
+
example: 'const Component = React.memo(() => { ... })'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (code.includes('map') && code.includes('return')) {
|
|
48
|
+
suggestions.push({
|
|
49
|
+
category: 'performance',
|
|
50
|
+
priority: 'medium',
|
|
51
|
+
issue: 'Ensure unique keys in map functions',
|
|
52
|
+
suggestion: 'Use unique and stable keys for list items',
|
|
53
|
+
example: 'items.map(item => <div key={item.id}>{item.name}</div>)'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Check for expensive operations
|
|
58
|
+
if (code.includes('JSON.parse') || code.includes('JSON.stringify')) {
|
|
59
|
+
suggestions.push({
|
|
60
|
+
category: 'performance',
|
|
61
|
+
priority: 'medium',
|
|
62
|
+
issue: 'JSON operations can be expensive',
|
|
63
|
+
suggestion: 'Consider memoizing JSON operations or using alternatives',
|
|
64
|
+
example: 'const memoizedParse = useMemo(() => JSON.parse(data), [data])'
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Readability improvements
|
|
69
|
+
if (focus === 'readability' || focus === 'all') {
|
|
70
|
+
// Check for long functions
|
|
71
|
+
if (codeLength > 20) {
|
|
72
|
+
suggestions.push({
|
|
73
|
+
category: 'readability',
|
|
74
|
+
priority: 'high',
|
|
75
|
+
issue: `Function is too long (${codeLength} lines)`,
|
|
76
|
+
suggestion: 'Break down into smaller, focused functions',
|
|
77
|
+
example: 'Extract logical groups into separate functions'
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Check for deep nesting
|
|
81
|
+
let maxNesting = 0;
|
|
82
|
+
let currentNesting = 0;
|
|
83
|
+
for (const line of codeLines) {
|
|
84
|
+
const braceCount = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
85
|
+
currentNesting += braceCount;
|
|
86
|
+
maxNesting = Math.max(maxNesting, currentNesting);
|
|
87
|
+
}
|
|
88
|
+
if (maxNesting > 3) {
|
|
89
|
+
suggestions.push({
|
|
90
|
+
category: 'readability',
|
|
91
|
+
priority: 'high',
|
|
92
|
+
issue: `Deep nesting detected (${maxNesting} levels)`,
|
|
93
|
+
suggestion: 'Use early returns or guard clauses to reduce nesting',
|
|
94
|
+
example: 'if (!condition) return; // instead of wrapping in else'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Check for magic numbers
|
|
98
|
+
const magicNumbers = code.match(/\b\d{2,}\b/g) || [];
|
|
99
|
+
if (magicNumbers.length > 0) {
|
|
100
|
+
suggestions.push({
|
|
101
|
+
category: 'readability',
|
|
102
|
+
priority: 'medium',
|
|
103
|
+
issue: 'Magic numbers found in code',
|
|
104
|
+
suggestion: 'Extract numbers into named constants',
|
|
105
|
+
example: 'const MAX_RETRY_COUNT = 3; // instead of using 3 directly'
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Check for unclear variable names
|
|
109
|
+
const shortVars = code.match(/\b[a-z]\b/g) || [];
|
|
110
|
+
if (shortVars.length > 2) {
|
|
111
|
+
suggestions.push({
|
|
112
|
+
category: 'readability',
|
|
113
|
+
priority: 'medium',
|
|
114
|
+
issue: 'Single letter variable names detected',
|
|
115
|
+
suggestion: 'Use descriptive variable names',
|
|
116
|
+
example: 'const userIndex = 0; // instead of i = 0'
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Maintainability improvements
|
|
121
|
+
if (focus === 'maintainability' || focus === 'all') {
|
|
122
|
+
// Check for error handling
|
|
123
|
+
const hasAsyncCode = code.includes('async') || code.includes('await') || code.includes('Promise');
|
|
124
|
+
const hasErrorHandling = code.includes('try') || code.includes('catch') || code.includes('throw');
|
|
125
|
+
if (hasAsyncCode && !hasErrorHandling) {
|
|
126
|
+
suggestions.push({
|
|
127
|
+
category: 'maintainability',
|
|
128
|
+
priority: 'high',
|
|
129
|
+
issue: 'Async code without error handling',
|
|
130
|
+
suggestion: 'Add try-catch blocks for async operations',
|
|
131
|
+
example: 'try { await asyncOperation(); } catch (error) { handleError(error); }'
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Check for code duplication
|
|
135
|
+
const lines = codeLines.map(line => line.trim()).filter(line => line.length > 5);
|
|
136
|
+
const duplicateLines = lines.filter((line, index) => lines.indexOf(line) !== index);
|
|
137
|
+
if (duplicateLines.length > 0) {
|
|
138
|
+
suggestions.push({
|
|
139
|
+
category: 'maintainability',
|
|
140
|
+
priority: 'medium',
|
|
141
|
+
issue: 'Potential code duplication detected',
|
|
142
|
+
suggestion: 'Extract common code into reusable functions',
|
|
143
|
+
example: 'Create utility functions for repeated logic'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// Check for comments
|
|
147
|
+
const commentLines = code.match(/\/\*[\s\S]*?\*\/|\/\/.*$/gm) || [];
|
|
148
|
+
const commentRatio = commentLines.length / codeLines.length;
|
|
149
|
+
if (commentRatio < 0.1 && codeLength > 10) {
|
|
150
|
+
suggestions.push({
|
|
151
|
+
category: 'maintainability',
|
|
152
|
+
priority: 'low',
|
|
153
|
+
issue: 'Low comment-to-code ratio',
|
|
154
|
+
suggestion: 'Add comments explaining complex logic',
|
|
155
|
+
example: '// Validate user input before processing'
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Type safety improvements
|
|
160
|
+
if (focus === 'type-safety' || focus === 'all') {
|
|
161
|
+
// Check for any types
|
|
162
|
+
if (code.includes('any')) {
|
|
163
|
+
suggestions.push({
|
|
164
|
+
category: 'type-safety',
|
|
165
|
+
priority: 'high',
|
|
166
|
+
issue: 'Using "any" type reduces type safety',
|
|
167
|
+
suggestion: 'Use specific types or interfaces',
|
|
168
|
+
example: 'interface User { id: string; name: string; }'
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
// Check for loose equality
|
|
172
|
+
if (code.includes('== ') || code.includes('!= ')) {
|
|
173
|
+
suggestions.push({
|
|
174
|
+
category: 'type-safety',
|
|
175
|
+
priority: 'medium',
|
|
176
|
+
issue: 'Loose equality operators found',
|
|
177
|
+
suggestion: 'Use strict equality operators',
|
|
178
|
+
example: 'Use === and !== instead of == and !='
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// Check for missing return types
|
|
182
|
+
const functions = code.match(/function\s+\w+\([^)]*\)/g) || [];
|
|
183
|
+
const functionsWithReturnType = code.match(/function\s+\w+\([^)]*\):\s*\w+/g) || [];
|
|
184
|
+
if (functions.length > functionsWithReturnType.length) {
|
|
185
|
+
suggestions.push({
|
|
186
|
+
category: 'type-safety',
|
|
187
|
+
priority: 'medium',
|
|
188
|
+
issue: 'Functions missing explicit return types',
|
|
189
|
+
suggestion: 'Add return type annotations',
|
|
190
|
+
example: 'function getName(): string { return "name"; }'
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Accessibility improvements
|
|
195
|
+
if (focus === 'accessibility' || focus === 'all') {
|
|
196
|
+
// Check for missing alt text
|
|
197
|
+
if (code.includes('<img') && !code.includes('alt=')) {
|
|
198
|
+
suggestions.push({
|
|
199
|
+
category: 'accessibility',
|
|
200
|
+
priority: 'high',
|
|
201
|
+
issue: 'Images without alt text',
|
|
202
|
+
suggestion: 'Add descriptive alt text to images',
|
|
203
|
+
example: '<img src="..." alt="Description of image" />'
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// Check for missing ARIA labels
|
|
207
|
+
if (code.includes('button') && !code.includes('aria-label') && !code.includes('aria-describedby')) {
|
|
208
|
+
suggestions.push({
|
|
209
|
+
category: 'accessibility',
|
|
210
|
+
priority: 'medium',
|
|
211
|
+
issue: 'Interactive elements may need ARIA labels',
|
|
212
|
+
suggestion: 'Add ARIA labels for screen readers',
|
|
213
|
+
example: '<button aria-label="Close dialog">×</button>'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Check for form labels
|
|
217
|
+
if (code.includes('<input') && !code.includes('label')) {
|
|
218
|
+
suggestions.push({
|
|
219
|
+
category: 'accessibility',
|
|
220
|
+
priority: 'high',
|
|
221
|
+
issue: 'Form inputs without labels',
|
|
222
|
+
suggestion: 'Associate labels with form controls',
|
|
223
|
+
example: '<label htmlFor="name">Name:</label><input id="name" />'
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Filter suggestions by priority if specified
|
|
228
|
+
const filteredSuggestions = priority === 'all' ? suggestions :
|
|
229
|
+
suggestions.filter(s => s.priority === priority || s.priority === 'critical');
|
|
230
|
+
// Sort by priority
|
|
231
|
+
const priorityOrder = { 'critical': 4, 'high': 3, 'medium': 2, 'low': 1 };
|
|
232
|
+
filteredSuggestions.sort((a, b) => (priorityOrder[b.priority] || 0) - (priorityOrder[a.priority] || 0));
|
|
233
|
+
const improvementResult = {
|
|
234
|
+
action: 'suggest_improvements',
|
|
235
|
+
focus,
|
|
236
|
+
priority,
|
|
237
|
+
codeStats: {
|
|
238
|
+
lines: codeLength,
|
|
239
|
+
functions: (code.match(/function\s+\w+|\w+\s*=\s*\(/g) || []).length,
|
|
240
|
+
comments: (code.match(/\/\*[\s\S]*?\*\/|\/\/.*$/gm) || []).length
|
|
241
|
+
},
|
|
242
|
+
suggestions: filteredSuggestions,
|
|
243
|
+
summary: {
|
|
244
|
+
total: filteredSuggestions.length,
|
|
245
|
+
critical: filteredSuggestions.filter(s => s.priority === 'critical').length,
|
|
246
|
+
high: filteredSuggestions.filter(s => s.priority === 'high').length,
|
|
247
|
+
medium: filteredSuggestions.filter(s => s.priority === 'medium').length,
|
|
248
|
+
low: filteredSuggestions.filter(s => s.priority === 'low').length
|
|
249
|
+
},
|
|
250
|
+
overallScore: Math.max(0, 100 - (filteredSuggestions.length * 10)),
|
|
251
|
+
status: 'success'
|
|
252
|
+
};
|
|
253
|
+
const topSuggestions = filteredSuggestions.slice(0, 8);
|
|
254
|
+
return {
|
|
255
|
+
content: [{ type: 'text', text: `Focus: ${focus}\nScore: ${improvementResult.overallScore}/100\nSuggestions: ${improvementResult.summary.total} (${improvementResult.summary.critical}C ${improvementResult.summary.high}H ${improvementResult.summary.medium}M ${improvementResult.summary.low}L)\n\n${topSuggestions.map(s => `[${s.priority.toUpperCase()}] ${s.category}\n Issue: ${s.issue}\n Fix: ${s.suggestion}`).join('\n\n')}${filteredSuggestions.length > 8 ? `\n\n... ${filteredSuggestions.length - 8} more suggestions` : ''}` }]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Convention management tool - completely independent
|
|
2
|
+
// Enhanced Software Engineering Metrics
|
|
3
|
+
const CODE_QUALITY_METRICS = {
|
|
4
|
+
COMPLEXITY: {
|
|
5
|
+
maxCyclomaticComplexity: 10,
|
|
6
|
+
maxCognitiveComplexity: 15,
|
|
7
|
+
maxFunctionLines: 20,
|
|
8
|
+
maxNestingDepth: 3,
|
|
9
|
+
maxParameters: 5
|
|
10
|
+
},
|
|
11
|
+
COUPLING: {
|
|
12
|
+
maxDependencies: 7,
|
|
13
|
+
maxFanOut: 5,
|
|
14
|
+
preventCircularDeps: true
|
|
15
|
+
},
|
|
16
|
+
COHESION: {
|
|
17
|
+
singleResponsibility: true,
|
|
18
|
+
relatedFunctionsOnly: true
|
|
19
|
+
},
|
|
20
|
+
MAINTAINABILITY: {
|
|
21
|
+
noMagicNumbers: true,
|
|
22
|
+
consistentNaming: true,
|
|
23
|
+
properErrorHandling: true,
|
|
24
|
+
typesSafety: true
|
|
25
|
+
},
|
|
26
|
+
PERFORMANCE: {
|
|
27
|
+
memoizeExpensiveCalc: true,
|
|
28
|
+
lazyLoading: true,
|
|
29
|
+
batchOperations: true
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export const validateCodeQualityDefinition = {
|
|
33
|
+
name: 'validate_code_quality',
|
|
34
|
+
description: '품질|리뷰|검사|quality|review code|check quality|validate|코드 리뷰 - Validate code quality',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
code: { type: 'string', description: 'Code to validate' },
|
|
39
|
+
type: { type: 'string', description: 'Code type', enum: ['component', 'function', 'hook', 'utility', 'general'] },
|
|
40
|
+
strict: { type: 'boolean', description: 'Apply strict validation rules' },
|
|
41
|
+
metrics: { type: 'string', description: 'Specific metrics to check', enum: ['complexity', 'coupling', 'cohesion', 'maintainability', 'performance', 'all'] }
|
|
42
|
+
},
|
|
43
|
+
required: ['code']
|
|
44
|
+
},
|
|
45
|
+
annotations: {
|
|
46
|
+
title: 'Validate Code Quality',
|
|
47
|
+
audience: ['user', 'assistant']
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export async function validateCodeQuality(args) {
|
|
51
|
+
const { code: validateCode, type: validateType = 'general', strict = false, metrics = 'all' } = args;
|
|
52
|
+
const qualityIssues = [];
|
|
53
|
+
const qualityScore = { total: 100, deductions: [] };
|
|
54
|
+
// Basic complexity checks
|
|
55
|
+
const lines = validateCode.split('\n');
|
|
56
|
+
const functionLineCount = lines.length;
|
|
57
|
+
if (functionLineCount > CODE_QUALITY_METRICS.COMPLEXITY.maxFunctionLines) {
|
|
58
|
+
qualityIssues.push({
|
|
59
|
+
type: 'complexity',
|
|
60
|
+
severity: 'high',
|
|
61
|
+
message: `Function exceeds maximum lines (${functionLineCount}/${CODE_QUALITY_METRICS.COMPLEXITY.maxFunctionLines})`
|
|
62
|
+
});
|
|
63
|
+
qualityScore.deductions.push({ reason: 'Function too long', points: 15 });
|
|
64
|
+
}
|
|
65
|
+
// Nesting depth check
|
|
66
|
+
let maxNesting = 0;
|
|
67
|
+
let currentNesting = 0;
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
const braceCount = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
70
|
+
currentNesting += braceCount;
|
|
71
|
+
maxNesting = Math.max(maxNesting, currentNesting);
|
|
72
|
+
}
|
|
73
|
+
if (maxNesting > CODE_QUALITY_METRICS.COMPLEXITY.maxNestingDepth) {
|
|
74
|
+
qualityIssues.push({
|
|
75
|
+
type: 'complexity',
|
|
76
|
+
severity: 'medium',
|
|
77
|
+
message: `Nesting depth exceeds maximum (${maxNesting}/${CODE_QUALITY_METRICS.COMPLEXITY.maxNestingDepth})`
|
|
78
|
+
});
|
|
79
|
+
qualityScore.deductions.push({ reason: 'Deep nesting', points: 10 });
|
|
80
|
+
}
|
|
81
|
+
// Cyclomatic complexity estimation
|
|
82
|
+
const cyclomaticComplexity = (validateCode.match(/\bif\b|\bfor\b|\bwhile\b|\bcase\b|\b&&\b|\b\|\|\b/g) || []).length + 1;
|
|
83
|
+
if (cyclomaticComplexity > CODE_QUALITY_METRICS.COMPLEXITY.maxCyclomaticComplexity) {
|
|
84
|
+
qualityIssues.push({
|
|
85
|
+
type: 'complexity',
|
|
86
|
+
severity: 'high',
|
|
87
|
+
message: `Cyclomatic complexity too high (${cyclomaticComplexity}/${CODE_QUALITY_METRICS.COMPLEXITY.maxCyclomaticComplexity})`
|
|
88
|
+
});
|
|
89
|
+
qualityScore.deductions.push({ reason: 'High cyclomatic complexity', points: 20 });
|
|
90
|
+
}
|
|
91
|
+
// Anti-pattern checks
|
|
92
|
+
if (validateCode.includes('any')) {
|
|
93
|
+
qualityIssues.push({
|
|
94
|
+
type: 'type-safety',
|
|
95
|
+
severity: 'medium',
|
|
96
|
+
message: 'Using "any" type - consider more specific types'
|
|
97
|
+
});
|
|
98
|
+
qualityScore.deductions.push({ reason: 'Any type usage', points: 10 });
|
|
99
|
+
}
|
|
100
|
+
if (validateCode.includes('== ')) {
|
|
101
|
+
qualityIssues.push({
|
|
102
|
+
type: 'best-practices',
|
|
103
|
+
severity: 'low',
|
|
104
|
+
message: 'Use strict equality (===) instead of loose equality (==)'
|
|
105
|
+
});
|
|
106
|
+
qualityScore.deductions.push({ reason: 'Loose equality', points: 5 });
|
|
107
|
+
}
|
|
108
|
+
if (validateCode.includes('var ')) {
|
|
109
|
+
qualityIssues.push({
|
|
110
|
+
type: 'best-practices',
|
|
111
|
+
severity: 'medium',
|
|
112
|
+
message: 'Use const/let instead of var'
|
|
113
|
+
});
|
|
114
|
+
qualityScore.deductions.push({ reason: 'Var usage', points: 8 });
|
|
115
|
+
}
|
|
116
|
+
// Magic numbers check
|
|
117
|
+
const magicNumbers = validateCode.match(/\b\d{2,}\b/g) || [];
|
|
118
|
+
if (magicNumbers.length > 0) {
|
|
119
|
+
qualityIssues.push({
|
|
120
|
+
type: 'maintainability',
|
|
121
|
+
severity: 'low',
|
|
122
|
+
message: `Found potential magic numbers: ${magicNumbers.join(', ')}`
|
|
123
|
+
});
|
|
124
|
+
qualityScore.deductions.push({ reason: 'Magic numbers', points: 5 });
|
|
125
|
+
}
|
|
126
|
+
// Error handling check
|
|
127
|
+
const hasErrorHandling = validateCode.includes('try') || validateCode.includes('catch') || validateCode.includes('throw');
|
|
128
|
+
if (!hasErrorHandling && validateCode.includes('async')) {
|
|
129
|
+
qualityIssues.push({
|
|
130
|
+
type: 'error-handling',
|
|
131
|
+
severity: 'medium',
|
|
132
|
+
message: 'Async functions should include error handling'
|
|
133
|
+
});
|
|
134
|
+
qualityScore.deductions.push({ reason: 'Missing error handling', points: 10 });
|
|
135
|
+
}
|
|
136
|
+
// Performance checks for React components
|
|
137
|
+
if (validateType === 'component' && validateCode.includes('React')) {
|
|
138
|
+
if (!validateCode.includes('memo') && !validateCode.includes('useMemo') && !validateCode.includes('useCallback')) {
|
|
139
|
+
qualityIssues.push({
|
|
140
|
+
type: 'performance',
|
|
141
|
+
severity: 'low',
|
|
142
|
+
message: 'Consider using React.memo, useMemo, or useCallback for performance optimization'
|
|
143
|
+
});
|
|
144
|
+
qualityScore.deductions.push({ reason: 'Missing performance optimization', points: 5 });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const finalScore = Math.max(0, qualityScore.total - qualityScore.deductions.reduce((sum, d) => sum + d.points, 0));
|
|
148
|
+
const validationResult = {
|
|
149
|
+
action: 'validate_code_quality',
|
|
150
|
+
type: validateType,
|
|
151
|
+
strict,
|
|
152
|
+
metricsRequested: metrics,
|
|
153
|
+
score: finalScore,
|
|
154
|
+
grade: finalScore >= 90 ? 'A' : finalScore >= 80 ? 'B' : finalScore >= 70 ? 'C' : finalScore >= 60 ? 'D' : 'F',
|
|
155
|
+
issues: qualityIssues,
|
|
156
|
+
deductions: qualityScore.deductions,
|
|
157
|
+
recommendations: qualityIssues.length > 0 ? [
|
|
158
|
+
'Consider breaking down complex functions',
|
|
159
|
+
'Reduce nesting depth with early returns',
|
|
160
|
+
'Use more specific types instead of "any"',
|
|
161
|
+
'Apply consistent coding standards',
|
|
162
|
+
'Add proper error handling',
|
|
163
|
+
'Consider performance optimizations'
|
|
164
|
+
] : ['Code quality is excellent!'],
|
|
165
|
+
metrics: {
|
|
166
|
+
complexity: cyclomaticComplexity,
|
|
167
|
+
lines: functionLineCount,
|
|
168
|
+
nesting: maxNesting,
|
|
169
|
+
issues: qualityIssues.length
|
|
170
|
+
},
|
|
171
|
+
status: 'success'
|
|
172
|
+
};
|
|
173
|
+
const topIssues = qualityIssues.slice(0, 8);
|
|
174
|
+
return {
|
|
175
|
+
content: [{ type: 'text', text: `Type: ${validateType}\nScore: ${finalScore}/100 (Grade: ${validationResult.grade})\nMetrics: Lines=${validationResult.metrics.lines}, Complexity=${validationResult.metrics.complexity}, Nesting=${validationResult.metrics.nesting}\n\nIssues (${qualityIssues.length}):\n${topIssues.map(i => `[${i.severity.toUpperCase()}] ${i.type}: ${i.message}`).join('\n')}${qualityIssues.length > 8 ? `\n... ${qualityIssues.length - 8} more issues` : ''}\n\nDeductions: -${qualityScore.deductions.reduce((sum, d) => sum + d.points, 0)} pts (${qualityScore.deductions.map(d => `${d.reason}: -${d.points}`).join(', ')})` }]
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Memory management tool - SQLite based with context compression (v1.3)
|
|
2
|
+
import { MemoryManager } from '../../lib/MemoryManager.js';
|
|
3
|
+
import { ContextCompressor } from '../../lib/ContextCompressor.js';
|
|
4
|
+
export const autoSaveContextDefinition = {
|
|
5
|
+
name: 'auto_save_context',
|
|
6
|
+
description: 'commit|save|checkpoint|backup|커밋|저장|compress - Auto-save and compress context',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
urgency: { type: 'string', description: 'Urgency level', enum: ['low', 'medium', 'high', 'critical'] },
|
|
11
|
+
contextType: { type: 'string', description: 'Type of context to save', enum: ['progress', 'decisions', 'code-snippets', 'debugging', 'planning'] },
|
|
12
|
+
sessionId: { type: 'string', description: 'Current session identifier' },
|
|
13
|
+
summary: { type: 'string', description: 'Brief summary of current context' },
|
|
14
|
+
fullContext: { type: 'string', description: 'Full context to compress and save' },
|
|
15
|
+
compress: { type: 'boolean', description: 'Enable smart compression (default: true)' }
|
|
16
|
+
},
|
|
17
|
+
required: ['urgency', 'contextType']
|
|
18
|
+
},
|
|
19
|
+
annotations: {
|
|
20
|
+
title: 'Auto-Save Context',
|
|
21
|
+
audience: ['user', 'assistant']
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export async function autoSaveContext(args) {
|
|
25
|
+
const { urgency, contextType, sessionId, summary, fullContext, compress = true } = args;
|
|
26
|
+
try {
|
|
27
|
+
const memoryManager = MemoryManager.getInstance();
|
|
28
|
+
let contextToSave = summary || '';
|
|
29
|
+
let compressionStats = null;
|
|
30
|
+
// Apply smart compression if full context provided and compression enabled
|
|
31
|
+
if (fullContext && compress) {
|
|
32
|
+
const targetTokens = urgency === 'critical' ? 6000 : urgency === 'high' ? 4000 : 2000;
|
|
33
|
+
const compressionResult = ContextCompressor.compress(fullContext, targetTokens);
|
|
34
|
+
contextToSave = compressionResult.compressed;
|
|
35
|
+
compressionStats = {
|
|
36
|
+
originalTokens: ContextCompressor.estimateTokens(fullContext),
|
|
37
|
+
compressedTokens: ContextCompressor.estimateTokens(compressionResult.compressed),
|
|
38
|
+
ratio: Math.round(compressionResult.compressionRatio * 100),
|
|
39
|
+
removed: compressionResult.removedSections.length
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
else if (fullContext) {
|
|
43
|
+
contextToSave = fullContext;
|
|
44
|
+
}
|
|
45
|
+
const contextData = {
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
urgency,
|
|
48
|
+
contextType,
|
|
49
|
+
sessionId,
|
|
50
|
+
summary,
|
|
51
|
+
context: contextToSave,
|
|
52
|
+
compressed: compress && !!fullContext,
|
|
53
|
+
compressionStats
|
|
54
|
+
};
|
|
55
|
+
const priority = urgency === 'high' || urgency === 'critical' ? 2 : urgency === 'medium' ? 1 : 0;
|
|
56
|
+
const contextKey = sessionId ? `context:session_${sessionId}_${Date.now()}` : `context:${Date.now()}`;
|
|
57
|
+
memoryManager.save(contextKey, JSON.stringify(contextData), 'context', priority);
|
|
58
|
+
let resultText = `✓ Context saved: ${contextType} (${urgency})`;
|
|
59
|
+
if (sessionId)
|
|
60
|
+
resultText += `\nSession: ${sessionId}`;
|
|
61
|
+
if (compressionStats) {
|
|
62
|
+
resultText += `\nCompressed: ${compressionStats.originalTokens} → ${compressionStats.compressedTokens} tokens (${compressionStats.ratio}%)`;
|
|
63
|
+
resultText += `\nRemoved: ${compressionStats.removed} low-priority sections`;
|
|
64
|
+
}
|
|
65
|
+
if (summary)
|
|
66
|
+
resultText += `\n${summary}`;
|
|
67
|
+
return {
|
|
68
|
+
content: [{
|
|
69
|
+
type: 'text',
|
|
70
|
+
text: resultText
|
|
71
|
+
}]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: 'text', text: `✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|