@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,257 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,177 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// SQLite database connection and schema management
|
|
2
|
-
import Database from 'better-sqlite3';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { getMemoryDir } from './memoryConfig.js';
|
|
5
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
6
|
-
class MemoryDatabase {
|
|
7
|
-
db = null;
|
|
8
|
-
dbPath;
|
|
9
|
-
constructor() {
|
|
10
|
-
const memoryDir = getMemoryDir();
|
|
11
|
-
// Ensure directory exists
|
|
12
|
-
if (!existsSync(memoryDir)) {
|
|
13
|
-
mkdirSync(memoryDir, { recursive: true });
|
|
14
|
-
}
|
|
15
|
-
this.dbPath = path.join(memoryDir, 'memories.db');
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Get database connection (lazy initialization)
|
|
19
|
-
*/
|
|
20
|
-
getConnection() {
|
|
21
|
-
if (!this.db) {
|
|
22
|
-
// Ensure directory exists before opening database
|
|
23
|
-
const memoryDir = getMemoryDir();
|
|
24
|
-
if (!existsSync(memoryDir)) {
|
|
25
|
-
mkdirSync(memoryDir, { recursive: true });
|
|
26
|
-
}
|
|
27
|
-
this.db = new Database(this.dbPath);
|
|
28
|
-
// Enable WAL mode for better concurrency
|
|
29
|
-
this.db.pragma('journal_mode = WAL');
|
|
30
|
-
// Initialize schema
|
|
31
|
-
this.initSchema();
|
|
32
|
-
}
|
|
33
|
-
return this.db;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Initialize database schema
|
|
37
|
-
*/
|
|
38
|
-
initSchema() {
|
|
39
|
-
const db = this.db;
|
|
40
|
-
// Memories table
|
|
41
|
-
db.exec(`
|
|
42
|
-
CREATE TABLE IF NOT EXISTS memories (
|
|
43
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
44
|
-
key TEXT UNIQUE NOT NULL,
|
|
45
|
-
value TEXT NOT NULL,
|
|
46
|
-
category TEXT NOT NULL DEFAULT 'general',
|
|
47
|
-
timestamp TEXT NOT NULL,
|
|
48
|
-
lastAccessed TEXT NOT NULL
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
CREATE INDEX IF NOT EXISTS idx_memories_key ON memories(key);
|
|
52
|
-
CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(category);
|
|
53
|
-
CREATE INDEX IF NOT EXISTS idx_memories_lastAccessed ON memories(lastAccessed);
|
|
54
|
-
`);
|
|
55
|
-
// Sessions table
|
|
56
|
-
db.exec(`
|
|
57
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
58
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
-
sessionId TEXT NOT NULL,
|
|
60
|
-
contextType TEXT NOT NULL,
|
|
61
|
-
summary TEXT NOT NULL,
|
|
62
|
-
urgency TEXT NOT NULL,
|
|
63
|
-
currentTask TEXT,
|
|
64
|
-
codeChanges TEXT,
|
|
65
|
-
decisions TEXT,
|
|
66
|
-
blockers TEXT,
|
|
67
|
-
nextSteps TEXT,
|
|
68
|
-
timestamp TEXT NOT NULL
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_sessionId ON sessions(sessionId);
|
|
72
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_timestamp ON sessions(timestamp);
|
|
73
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_urgency ON sessions(urgency);
|
|
74
|
-
`);
|
|
75
|
-
// Guides table (for coding guides)
|
|
76
|
-
db.exec(`
|
|
77
|
-
CREATE TABLE IF NOT EXISTS guides (
|
|
78
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
-
name TEXT UNIQUE NOT NULL,
|
|
80
|
-
category TEXT NOT NULL,
|
|
81
|
-
content TEXT NOT NULL,
|
|
82
|
-
timestamp TEXT NOT NULL
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
CREATE INDEX IF NOT EXISTS idx_guides_name ON guides(name);
|
|
86
|
-
CREATE INDEX IF NOT EXISTS idx_guides_category ON guides(category);
|
|
87
|
-
`);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Close database connection
|
|
91
|
-
*/
|
|
92
|
-
close() {
|
|
93
|
-
if (this.db) {
|
|
94
|
-
this.db.close();
|
|
95
|
-
this.db = null;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Run a checkpoint to merge WAL file
|
|
100
|
-
*/
|
|
101
|
-
checkpoint() {
|
|
102
|
-
if (this.db) {
|
|
103
|
-
this.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Singleton instance
|
|
108
|
-
export const memoryDB = new MemoryDatabase();
|
|
109
|
-
// Cleanup on process exit
|
|
110
|
-
process.on('exit', () => {
|
|
111
|
-
memoryDB.checkpoint();
|
|
112
|
-
memoryDB.close();
|
|
113
|
-
});
|
|
114
|
-
process.on('SIGINT', () => {
|
|
115
|
-
memoryDB.checkpoint();
|
|
116
|
-
memoryDB.close();
|
|
117
|
-
process.exit(0);
|
|
118
|
-
});
|
|
119
|
-
process.on('SIGTERM', () => {
|
|
120
|
-
memoryDB.checkpoint();
|
|
121
|
-
memoryDB.close();
|
|
122
|
-
process.exit(0);
|
|
123
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Memory management tool - completely independent
|
|
2
|
-
import { MemoryManager } from '../../lib/MemoryManager.js';
|
|
3
|
-
export const deleteMemoryDefinition = {
|
|
4
|
-
name: 'delete_memory',
|
|
5
|
-
description: '잊어|삭제해|지워|forget|delete|remove|erase - Delete specific memory',
|
|
6
|
-
inputSchema: {
|
|
7
|
-
type: 'object',
|
|
8
|
-
properties: {
|
|
9
|
-
key: { type: 'string', description: 'Memory key to delete' }
|
|
10
|
-
},
|
|
11
|
-
required: ['key']
|
|
12
|
-
},
|
|
13
|
-
annotations: {
|
|
14
|
-
title: 'Delete Memory',
|
|
15
|
-
audience: ['user', 'assistant']
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
export async function deleteMemory(args) {
|
|
19
|
-
const { key: deleteKey } = args;
|
|
20
|
-
try {
|
|
21
|
-
const mm = MemoryManager.getInstance();
|
|
22
|
-
const deleted = mm.delete(deleteKey);
|
|
23
|
-
if (deleted) {
|
|
24
|
-
return {
|
|
25
|
-
content: [{ type: 'text', text: `✓ Deleted memory: "${deleteKey}"` }]
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
return {
|
|
30
|
-
content: [{ type: 'text', text: `✗ Memory not found: "${deleteKey}"` }]
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
return {
|
|
36
|
-
content: [{ type: 'text', text: `✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}
|