@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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +448 -0
  3. package/agents/backend-python-expert.md +453 -0
  4. package/agents/database-postgres-expert.md +538 -0
  5. package/agents/frontend-flutter-expert.md +487 -0
  6. package/agents/frontend-react-expert.md +424 -0
  7. package/agents/quality-reviewer.md +542 -0
  8. package/agents/specification-agent.md +505 -0
  9. package/bin/sutory +332 -0
  10. package/bin/vibe +338 -0
  11. package/mcp/dist/__tests__/complexity.test.js +126 -0
  12. package/mcp/dist/__tests__/memory.test.js +120 -0
  13. package/mcp/dist/__tests__/python-dart-complexity.test.js +146 -0
  14. package/mcp/dist/index.js +230 -0
  15. package/mcp/dist/lib/ContextCompressor.js +305 -0
  16. package/mcp/dist/lib/MemoryManager.js +334 -0
  17. package/mcp/dist/lib/ProjectCache.js +126 -0
  18. package/mcp/dist/lib/PythonParser.js +241 -0
  19. package/mcp/dist/tools/browser/browserPool.js +76 -0
  20. package/mcp/dist/tools/browser/browserUtils.js +135 -0
  21. package/mcp/dist/tools/browser/inspectNetworkRequests.js +140 -0
  22. package/mcp/dist/tools/browser/monitorConsoleLogs.js +97 -0
  23. package/mcp/dist/tools/convention/analyzeComplexity.js +248 -0
  24. package/mcp/dist/tools/convention/applyQualityRules.js +102 -0
  25. package/mcp/dist/tools/convention/checkCouplingCohesion.js +233 -0
  26. package/mcp/dist/tools/convention/complexityMetrics.js +133 -0
  27. package/mcp/dist/tools/convention/dartComplexity.js +117 -0
  28. package/mcp/dist/tools/convention/getCodingGuide.js +64 -0
  29. package/mcp/dist/tools/convention/languageDetector.js +50 -0
  30. package/mcp/dist/tools/convention/pythonComplexity.js +109 -0
  31. package/mcp/dist/tools/convention/suggestImprovements.js +257 -0
  32. package/mcp/dist/tools/convention/validateCodeQuality.js +177 -0
  33. package/mcp/dist/tools/memory/autoSaveContext.js +79 -0
  34. package/mcp/dist/tools/memory/database.js +123 -0
  35. package/mcp/dist/tools/memory/deleteMemory.js +39 -0
  36. package/mcp/dist/tools/memory/listMemories.js +38 -0
  37. package/mcp/dist/tools/memory/memoryConfig.js +27 -0
  38. package/mcp/dist/tools/memory/memorySQLite.js +138 -0
  39. package/mcp/dist/tools/memory/memoryUtils.js +34 -0
  40. package/mcp/dist/tools/memory/migrate.js +113 -0
  41. package/mcp/dist/tools/memory/prioritizeMemory.js +109 -0
  42. package/mcp/dist/tools/memory/recallMemory.js +40 -0
  43. package/mcp/dist/tools/memory/restoreSessionContext.js +69 -0
  44. package/mcp/dist/tools/memory/saveMemory.js +34 -0
  45. package/mcp/dist/tools/memory/searchMemories.js +37 -0
  46. package/mcp/dist/tools/memory/startSession.js +100 -0
  47. package/mcp/dist/tools/memory/updateMemory.js +46 -0
  48. package/mcp/dist/tools/planning/analyzeRequirements.js +166 -0
  49. package/mcp/dist/tools/planning/createUserStories.js +119 -0
  50. package/mcp/dist/tools/planning/featureRoadmap.js +202 -0
  51. package/mcp/dist/tools/planning/generatePrd.js +156 -0
  52. package/mcp/dist/tools/prompt/analyzePrompt.js +145 -0
  53. package/mcp/dist/tools/prompt/enhancePrompt.js +105 -0
  54. package/mcp/dist/tools/semantic/findReferences.js +195 -0
  55. package/mcp/dist/tools/semantic/findSymbol.js +200 -0
  56. package/mcp/dist/tools/thinking/analyzeProblem.js +50 -0
  57. package/mcp/dist/tools/thinking/breakDownProblem.js +140 -0
  58. package/mcp/dist/tools/thinking/createThinkingChain.js +39 -0
  59. package/mcp/dist/tools/thinking/formatAsPlan.js +73 -0
  60. package/mcp/dist/tools/thinking/stepByStepAnalysis.js +58 -0
  61. package/mcp/dist/tools/thinking/thinkAloudProcess.js +75 -0
  62. package/mcp/dist/tools/time/getCurrentTime.js +61 -0
  63. package/mcp/dist/tools/ui/previewUiAscii.js +232 -0
  64. package/mcp/dist/types/tool.js +2 -0
  65. package/mcp/package.json +53 -0
  66. package/package.json +49 -0
  67. package/scripts/install-mcp.js +48 -0
  68. package/scripts/install.sh +70 -0
  69. package/skills/core/communication-guide.md +104 -0
  70. package/skills/core/development-philosophy.md +53 -0
  71. package/skills/core/quick-start.md +121 -0
  72. package/skills/languages/dart-flutter.md +509 -0
  73. package/skills/languages/python-fastapi.md +386 -0
  74. package/skills/languages/typescript-nextjs.md +441 -0
  75. package/skills/languages/typescript-react-native.md +446 -0
  76. package/skills/languages/typescript-react.md +525 -0
  77. package/skills/quality/checklist.md +276 -0
  78. package/skills/quality/testing-strategy.md +437 -0
  79. package/skills/standards/anti-patterns.md +369 -0
  80. package/skills/standards/code-structure.md +291 -0
  81. package/skills/standards/complexity-metrics.md +312 -0
  82. package/skills/standards/naming-conventions.md +198 -0
  83. package/skills/tools/mcp-hi-ai-guide.md +665 -0
  84. package/skills/tools/mcp-workflow.md +51 -0
  85. package/templates/constitution-template.md +193 -0
  86. package/templates/plan-template.md +237 -0
  87. package/templates/spec-template.md +142 -0
  88. 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
+ }