@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,233 @@
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 checkCouplingCohesionDefinition = {
33
+ name: 'check_coupling_cohesion',
34
+ description: '결합도|응집도|coupling|cohesion|dependencies check|module structure - Check coupling and cohesion',
35
+ inputSchema: {
36
+ type: 'object',
37
+ properties: {
38
+ code: { type: 'string', description: 'Code to analyze' },
39
+ type: { type: 'string', description: 'Code type', enum: ['class', 'module', 'function', 'component'] },
40
+ checkDependencies: { type: 'boolean', description: 'Analyze dependencies' }
41
+ },
42
+ required: ['code']
43
+ },
44
+ annotations: {
45
+ title: 'Check Coupling & Cohesion',
46
+ audience: ['user', 'assistant']
47
+ }
48
+ };
49
+ import { Project, ScriptKind } from "ts-morph";
50
+ import * as ts from "typescript";
51
+ const AST_PROJECT = new Project({
52
+ useInMemoryFileSystem: true,
53
+ compilerOptions: { allowJs: true, skipLibCheck: true }
54
+ });
55
+ export async function checkCouplingCohesion(args) {
56
+ const { code: couplingCode, type: couplingType = 'function', checkDependencies = false } = args;
57
+ const couplingAnalysis = {
58
+ action: 'check_coupling_cohesion',
59
+ type: couplingType,
60
+ checkDependencies,
61
+ results: {},
62
+ overallScore: 0,
63
+ issues: [],
64
+ recommendations: [],
65
+ status: 'pending'
66
+ };
67
+ // AST 기반 의존성/구조 분석
68
+ try {
69
+ const sourceFile = AST_PROJECT.createSourceFile('temp.ts', couplingCode, {
70
+ overwrite: true,
71
+ scriptKind: ScriptKind.TS
72
+ });
73
+ // Import/Require 분석
74
+ const importDecls = sourceFile.getImportDeclarations();
75
+ const requireCalls = sourceFile.getDescendantsOfKind(ts.SyntaxKind.CallExpression).filter(call => call.getExpression().getText() === 'require');
76
+ // 클래스/함수/모듈 구조 분석
77
+ const classDecls = sourceFile.getClasses();
78
+ const funcDecls = sourceFile.getFunctions();
79
+ const exportDecls = sourceFile.getExportDeclarations();
80
+ couplingAnalysis.results.ast = {
81
+ importCount: importDecls.length,
82
+ requireCount: requireCalls.length,
83
+ classCount: classDecls.length,
84
+ functionCount: funcDecls.length,
85
+ exportCount: exportDecls.length,
86
+ importModules: importDecls.map(d => d.getModuleSpecifierValue()),
87
+ exportedNames: exportDecls.map(d => d.getNamedExports().map(e => e.getName()))
88
+ };
89
+ }
90
+ catch (e) {
91
+ couplingAnalysis.results.ast = {
92
+ error: 'AST 분석 실패: ' + (e instanceof Error ? e.message : String(e))
93
+ };
94
+ }
95
+ // Dependency analysis (Coupling)
96
+ const imports = (couplingCode.match(/import\s+.*?\s+from\s+['"](.*?)['"]/g) || []).length;
97
+ const requires = (couplingCode.match(/require\s*\(\s*['"](.*?)['"]\s*\)/g) || []).length;
98
+ const totalDependencies = imports + requires;
99
+ // External dependencies
100
+ const externalDeps = (couplingCode.match(/import\s+.*?\s+from\s+['"](?!\.)(.*?)['"]/g) || []).length;
101
+ const internalDeps = totalDependencies - externalDeps;
102
+ // Function calls (fan-out)
103
+ const functionCalls = (couplingCode.match(/\w+\s*\(/g) || []).length;
104
+ const uniqueFunctionCalls = new Set((couplingCode.match(/\w+\s*\(/g) || []).map(call => call.replace(/\s*\(/, ''))).size;
105
+ couplingAnalysis.results.coupling = {
106
+ totalDependencies: totalDependencies,
107
+ externalDependencies: externalDeps,
108
+ internalDependencies: internalDeps,
109
+ functionCalls: functionCalls,
110
+ uniqueFunctionCalls: uniqueFunctionCalls,
111
+ threshold: CODE_QUALITY_METRICS.COUPLING.maxDependencies,
112
+ status: totalDependencies <= CODE_QUALITY_METRICS.COUPLING.maxDependencies ? 'pass' : 'fail',
113
+ fanOut: uniqueFunctionCalls,
114
+ fanOutStatus: uniqueFunctionCalls <= CODE_QUALITY_METRICS.COUPLING.maxFanOut ? 'pass' : 'fail'
115
+ };
116
+ // Cohesion analysis
117
+ let cohesionScore = 0;
118
+ let cohesionLevel = 'low';
119
+ if (couplingType === 'class') {
120
+ // Class cohesion: methods using same data
121
+ const methods = (couplingCode.match(/\w+\s*\([^)]*\)\s*\{/g) || []).length;
122
+ const properties = (couplingCode.match(/this\.\w+/g) || []).length;
123
+ const uniqueProperties = new Set((couplingCode.match(/this\.(\w+)/g) || []).map(prop => prop.replace('this.', ''))).size;
124
+ // LCOM (Lack of Cohesion in Methods) - simplified calculation
125
+ const propertyUsage = methods > 0 ? uniqueProperties / methods : 0;
126
+ cohesionScore = propertyUsage;
127
+ couplingAnalysis.results.cohesion = {
128
+ type: 'class',
129
+ methods: methods,
130
+ properties: uniqueProperties,
131
+ propertyUsageRatio: Math.round(propertyUsage * 100) / 100,
132
+ score: cohesionScore,
133
+ level: cohesionScore > 0.7 ? 'high' : cohesionScore > 0.4 ? 'medium' : 'low',
134
+ status: cohesionScore > 0.4 ? 'pass' : 'fail'
135
+ };
136
+ }
137
+ else if (couplingType === 'module') {
138
+ // Module cohesion: related functions and exports
139
+ const functions = (couplingCode.match(/function\s+\w+|const\s+\w+\s*=\s*\(/g) || []).length;
140
+ const exports = (couplingCode.match(/export\s+/g) || []).length;
141
+ const variables = (couplingCode.match(/(?:const|let|var)\s+\w+/g) || []).length;
142
+ // Functional cohesion: ratio of related functions
143
+ const cohesionRatio = functions > 0 ? exports / functions : 0;
144
+ cohesionScore = cohesionRatio;
145
+ couplingAnalysis.results.cohesion = {
146
+ type: 'module',
147
+ functions: functions,
148
+ exports: exports,
149
+ variables: variables,
150
+ cohesionRatio: Math.round(cohesionRatio * 100) / 100,
151
+ score: cohesionScore,
152
+ level: cohesionScore > 0.6 ? 'high' : cohesionScore > 0.3 ? 'medium' : 'low',
153
+ status: cohesionScore > 0.3 ? 'pass' : 'fail'
154
+ };
155
+ }
156
+ else if (couplingType === 'function') {
157
+ // Function cohesion: single responsibility principle
158
+ const statements = (couplingCode.match(/;/g) || []).length;
159
+ const returns = (couplingCode.match(/\breturn\b/g) || []).length;
160
+ const variables = (couplingCode.match(/(?:const|let|var)\s+\w+/g) || []).length;
161
+ // Sequential cohesion: operations flow in sequence
162
+ const lines = couplingCode.split('\n').filter(line => line.trim().length > 0).length;
163
+ const complexityIndicators = (couplingCode.match(/\bif\b|\bfor\b|\bwhile\b|\bswitch\b/g) || []).length;
164
+ cohesionScore = lines > 0 ? 1 - (complexityIndicators / lines) : 0;
165
+ couplingAnalysis.results.cohesion = {
166
+ type: 'function',
167
+ statements: statements,
168
+ returns: returns,
169
+ variables: variables,
170
+ lines: lines,
171
+ complexityIndicators: complexityIndicators,
172
+ score: Math.round(cohesionScore * 100) / 100,
173
+ level: cohesionScore > 0.7 ? 'high' : cohesionScore > 0.4 ? 'medium' : 'low',
174
+ status: cohesionScore > 0.4 ? 'pass' : 'fail'
175
+ };
176
+ }
177
+ else if (couplingType === 'component') {
178
+ // React component cohesion: props and state usage
179
+ const props = (couplingCode.match(/props\.\w+/g) || []).length;
180
+ const state = (couplingCode.match(/useState|useReducer/g) || []).length;
181
+ const effects = (couplingCode.match(/useEffect/g) || []).length;
182
+ const hooks = (couplingCode.match(/use\w+/g) || []).length;
183
+ // Component cohesion: how well props, state, and effects are related
184
+ const totalElements = props + state + effects;
185
+ const cohesionRatio = totalElements > 0 ? hooks / totalElements : 0;
186
+ cohesionScore = Math.min(1, cohesionRatio);
187
+ couplingAnalysis.results.cohesion = {
188
+ type: 'component',
189
+ props: props,
190
+ state: state,
191
+ effects: effects,
192
+ hooks: hooks,
193
+ cohesionRatio: Math.round(cohesionRatio * 100) / 100,
194
+ score: Math.round(cohesionScore * 100) / 100,
195
+ level: cohesionScore > 0.6 ? 'high' : cohesionScore > 0.3 ? 'medium' : 'low',
196
+ status: cohesionScore > 0.3 ? 'pass' : 'fail'
197
+ };
198
+ }
199
+ // Overall assessment
200
+ const issues = [];
201
+ let overallScore = 100;
202
+ if (couplingAnalysis.results.coupling.status === 'fail') {
203
+ issues.push('High coupling detected - too many dependencies');
204
+ overallScore -= 30;
205
+ }
206
+ if (couplingAnalysis.results.coupling.fanOutStatus === 'fail') {
207
+ issues.push('High fan-out detected - too many function calls');
208
+ overallScore -= 20;
209
+ }
210
+ if (couplingAnalysis.results.cohesion.status === 'fail') {
211
+ issues.push('Low cohesion detected - poor single responsibility');
212
+ overallScore -= 25;
213
+ }
214
+ couplingAnalysis.overallScore = Math.max(0, overallScore);
215
+ couplingAnalysis.issues = issues;
216
+ couplingAnalysis.recommendations = [];
217
+ if (couplingAnalysis.results.coupling.status === 'fail') {
218
+ couplingAnalysis.recommendations.push('Reduce dependencies by using dependency injection');
219
+ couplingAnalysis.recommendations.push('Consider using interfaces to abstract dependencies');
220
+ }
221
+ if (couplingAnalysis.results.cohesion.status === 'fail') {
222
+ couplingAnalysis.recommendations.push('Ensure single responsibility principle');
223
+ couplingAnalysis.recommendations.push('Group related functionality together');
224
+ couplingAnalysis.recommendations.push('Extract unrelated code into separate modules');
225
+ }
226
+ if (couplingAnalysis.recommendations.length === 0) {
227
+ couplingAnalysis.recommendations.push('Coupling and cohesion are within acceptable ranges');
228
+ }
229
+ couplingAnalysis.status = 'success';
230
+ return {
231
+ content: [{ type: 'text', text: `Type: ${couplingType}\nScore: ${couplingAnalysis.overallScore}/100\n\nCoupling: ${couplingAnalysis.results.coupling.totalDependencies} deps (${couplingAnalysis.results.coupling.status}) | Fan-out: ${couplingAnalysis.results.coupling.fanOut} (${couplingAnalysis.results.coupling.fanOutStatus})\nCohesion: ${couplingAnalysis.results.cohesion.score} (${couplingAnalysis.results.cohesion.level}, ${couplingAnalysis.results.cohesion.status})\n\nIssues (${couplingAnalysis.issues.length}):\n${couplingAnalysis.issues.map(i => `- ${i}`).join('\n')}\n\nRecommendations:\n${couplingAnalysis.recommendations.map(r => `- ${r}`).join('\n')}` }]
232
+ };
233
+ }
@@ -0,0 +1,133 @@
1
+ // Complexity metrics calculation - extracted from analyzeComplexity
2
+ import { Project, ScriptKind } from "ts-morph";
3
+ const AST_PROJECT = new Project({
4
+ useInMemoryFileSystem: true,
5
+ compilerOptions: { allowJs: true, skipLibCheck: true }
6
+ });
7
+ const COMPLEXITY_NODES = new Set([
8
+ 'IfStatement', 'ForStatement', 'ForOfStatement', 'ForInStatement',
9
+ 'WhileStatement', 'CaseClause', 'ConditionalExpression',
10
+ 'DoStatement', 'CatchClause', 'BinaryExpression'
11
+ ]);
12
+ export const THRESHOLDS = {
13
+ maxCyclomatic: 10,
14
+ maxCognitive: 15,
15
+ maxHalsteadDifficulty: 10
16
+ };
17
+ /**
18
+ * Calculate AST-based cyclomatic complexity
19
+ */
20
+ export function calculateASTCyclomatic(code) {
21
+ try {
22
+ const sourceFile = AST_PROJECT.createSourceFile('temp.ts', code, {
23
+ overwrite: true,
24
+ scriptKind: ScriptKind.TS
25
+ });
26
+ let complexity = 1;
27
+ sourceFile.forEachDescendant((node) => {
28
+ if (COMPLEXITY_NODES.has(node.getKindName())) {
29
+ complexity++;
30
+ }
31
+ });
32
+ return {
33
+ value: complexity,
34
+ threshold: THRESHOLDS.maxCyclomatic,
35
+ status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
36
+ description: 'AST-based branch/decision point count'
37
+ };
38
+ }
39
+ catch (error) {
40
+ return {
41
+ value: 0,
42
+ status: 'error',
43
+ description: `AST analysis failed: ${error instanceof Error ? error.message : String(error)}`
44
+ };
45
+ }
46
+ }
47
+ /**
48
+ * Calculate regex-based cyclomatic complexity (fallback)
49
+ */
50
+ export function calculateRegexCyclomatic(code) {
51
+ const patterns = /\bif\b|\bfor\b|\bwhile\b|\bcase\b|\b&&\b|\b\|\|\b/g;
52
+ const matches = code.match(patterns) || [];
53
+ const complexity = matches.length + 1;
54
+ return {
55
+ value: complexity,
56
+ threshold: THRESHOLDS.maxCyclomatic,
57
+ status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
58
+ description: 'Number of linearly independent paths'
59
+ };
60
+ }
61
+ /**
62
+ * Calculate cognitive complexity with nesting awareness
63
+ */
64
+ export function calculateCognitiveComplexity(code) {
65
+ const lines = code.split('\n');
66
+ let complexity = 0;
67
+ let nestingLevel = 0;
68
+ for (const line of lines) {
69
+ const trimmed = line.trim();
70
+ // Control structures add complexity based on nesting
71
+ if (/(if|for|while|catch|switch)\s*\(/.test(trimmed)) {
72
+ complexity += 1 + nestingLevel;
73
+ }
74
+ // Track nesting level
75
+ const openBraces = (line.match(/\{/g) || []).length;
76
+ const closeBraces = (line.match(/\}/g) || []).length;
77
+ nestingLevel = Math.max(0, nestingLevel + openBraces - closeBraces);
78
+ }
79
+ return {
80
+ value: complexity,
81
+ threshold: THRESHOLDS.maxCognitive,
82
+ status: complexity <= THRESHOLDS.maxCognitive ? 'pass' : 'fail',
83
+ description: 'Difficulty to understand the code'
84
+ };
85
+ }
86
+ /**
87
+ * Calculate Halstead complexity metrics
88
+ */
89
+ export function calculateHalsteadMetrics(code) {
90
+ const operators = code.match(/[+\-*/=<>!&|%^~?:]/g) || [];
91
+ const operands = code.match(/\b[a-zA-Z_]\w*\b/g) || [];
92
+ const uniqueOperators = new Set(operators).size;
93
+ const uniqueOperands = new Set(operands).size;
94
+ const vocabulary = uniqueOperators + uniqueOperands;
95
+ const length = operators.length + operands.length;
96
+ const calculatedLength = vocabulary > 0
97
+ ? uniqueOperators * Math.log2(uniqueOperators || 1) + uniqueOperands * Math.log2(uniqueOperands || 1)
98
+ : 0;
99
+ const volume = length * Math.log2(vocabulary || 1);
100
+ const difficulty = vocabulary > 0
101
+ ? (uniqueOperators / 2) * (operands.length / (uniqueOperands || 1))
102
+ : 0;
103
+ const effort = difficulty * volume;
104
+ return {
105
+ vocabulary,
106
+ length,
107
+ calculatedLength: Math.round(calculatedLength),
108
+ volume: Math.round(volume),
109
+ difficulty: Math.round(difficulty * 100) / 100,
110
+ effort: Math.round(effort),
111
+ timeToProgram: Math.round(effort / 18),
112
+ bugsDelivered: Math.round(volume / 3000 * 100) / 100,
113
+ description: 'Software science metrics measuring program complexity'
114
+ };
115
+ }
116
+ /**
117
+ * Calculate additional code metrics
118
+ */
119
+ export function calculateAdditionalMetrics(code) {
120
+ const lines = code.split('\n');
121
+ const nonEmptyLines = lines.filter(line => line.trim().length > 0).length;
122
+ const comments = (code.match(/\/\*[\s\S]*?\*\/|\/\/.*$/gm) || []).length;
123
+ const functions = (code.match(/function\s+\w+|\w+\s*=\s*\(/g) || []).length;
124
+ const classes = (code.match(/class\s+\w+/g) || []).length;
125
+ return {
126
+ linesOfCode: nonEmptyLines,
127
+ comments,
128
+ commentRatio: nonEmptyLines > 0 ? Math.round((comments / nonEmptyLines) * 100) / 100 : 0,
129
+ functions,
130
+ classes,
131
+ averageFunctionLength: functions > 0 ? Math.round(nonEmptyLines / functions) : 0
132
+ };
133
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Dart/Flutter-specific complexity analysis
3
+ */
4
+ import { THRESHOLDS } from './complexityMetrics.js';
5
+ /**
6
+ * Calculate cyclomatic complexity for Dart code
7
+ */
8
+ export function calculateDartComplexity(code) {
9
+ // Dart control flow keywords
10
+ const patterns = [
11
+ /\bif\b/g, // if statements
12
+ /\belse\s+if\b/g, // else if
13
+ /\bfor\b/g, // for loops
14
+ /\bwhile\b/g, // while loops
15
+ /\bdo\b/g, // do-while
16
+ /\bswitch\b/g, // switch statements
17
+ /\bcase\b/g, // case clauses
18
+ /\btry\b/g, // try blocks
19
+ /\bcatch\b/g, // catch blocks
20
+ /&&/g, // logical and
21
+ /\|\|/g, // logical or
22
+ /\?\?/g // null-coalescing
23
+ ];
24
+ let complexity = 1; // Base complexity
25
+ for (const pattern of patterns) {
26
+ const matches = code.match(pattern);
27
+ if (matches) {
28
+ complexity += matches.length;
29
+ }
30
+ }
31
+ return {
32
+ value: complexity,
33
+ threshold: THRESHOLDS.maxCyclomatic,
34
+ status: complexity <= THRESHOLDS.maxCyclomatic ? 'pass' : 'fail',
35
+ description: 'Dart cyclomatic complexity (control flow branches)'
36
+ };
37
+ }
38
+ /**
39
+ * Calculate cognitive complexity for Dart
40
+ */
41
+ export function calculateDartCognitiveComplexity(code) {
42
+ let complexity = 0;
43
+ // Nested control structures
44
+ let nestingLevel = 0;
45
+ const lines = code.split('\n');
46
+ for (const line of lines) {
47
+ const trimmed = line.trim();
48
+ // Track nesting level
49
+ const openBraces = (line.match(/{/g) || []).length;
50
+ const closeBraces = (line.match(/}/g) || []).length;
51
+ // Control flow keywords
52
+ if (/\b(if|else|for|while|switch|try|catch)\b/.test(trimmed)) {
53
+ complexity += 1 + nestingLevel;
54
+ }
55
+ // Ternary operators
56
+ const ternaryCount = (trimmed.match(/\?.*:/g) || []).length;
57
+ complexity += ternaryCount;
58
+ // Null-aware operators (Flutter specific)
59
+ const nullAwareCount = (trimmed.match(/\?\?|!\.|\/\?/g) || []).length;
60
+ complexity += nullAwareCount * 0.5; // Half weight
61
+ // Update nesting
62
+ nestingLevel += openBraces - closeBraces;
63
+ if (nestingLevel < 0)
64
+ nestingLevel = 0;
65
+ }
66
+ return {
67
+ value: Math.round(complexity),
68
+ threshold: THRESHOLDS.maxCognitive,
69
+ status: complexity <= THRESHOLDS.maxCognitive ? 'pass' : 'fail',
70
+ description: 'Dart cognitive complexity (mental load to understand)'
71
+ };
72
+ }
73
+ /**
74
+ * Analyze Flutter/Dart code quality
75
+ */
76
+ export function analyzeDartQuality(code) {
77
+ const issues = [];
78
+ const recommendations = [];
79
+ // Flutter-specific anti-patterns
80
+ if (/setState\(\(\)\s*{\s*[\s\S]*?}\s*\)/.test(code)) {
81
+ const setStateCount = (code.match(/setState/g) || []).length;
82
+ if (setStateCount > 5) {
83
+ issues.push('Too many setState calls - consider state management solution');
84
+ recommendations.push('Use Provider, Riverpod, or Bloc for complex state');
85
+ }
86
+ }
87
+ // Deep widget nesting
88
+ const widgetNesting = (code.match(/child:\s*\w+\(/g) || []).length;
89
+ if (widgetNesting > 5) {
90
+ issues.push('Deep widget nesting detected');
91
+ recommendations.push('Extract nested widgets into separate methods or classes');
92
+ }
93
+ // Missing const constructors
94
+ const widgetConstructors = (code.match(/\w+\(/g) || []).length;
95
+ const constConstructors = (code.match(/const\s+\w+\(/g) || []).length;
96
+ if (widgetConstructors > 10 && constConstructors < widgetConstructors * 0.3) {
97
+ issues.push('Many widgets without const constructors');
98
+ recommendations.push('Use const constructors for immutable widgets to improve performance');
99
+ }
100
+ // Missing key in lists
101
+ if (/ListView\.builder|GridView\.builder/.test(code) && !/key:/.test(code)) {
102
+ issues.push('ListView/GridView without keys');
103
+ recommendations.push('Add keys to list items for better performance');
104
+ }
105
+ // Missing null safety
106
+ if (!/\w+\?|\w+!/.test(code) && code.includes('null')) {
107
+ issues.push('Possible null safety issues');
108
+ recommendations.push('Enable null safety and use ? and ! operators appropriately');
109
+ }
110
+ // Large build methods
111
+ const buildMethodMatch = code.match(/Widget build\(BuildContext context\)\s*{([\s\S]*?)^ }/m);
112
+ if (buildMethodMatch && buildMethodMatch[1].split('\n').length > 50) {
113
+ issues.push('Build method is too large (> 50 lines)');
114
+ recommendations.push('Extract widgets into separate methods or classes');
115
+ }
116
+ return { issues, recommendations };
117
+ }
@@ -0,0 +1,64 @@
1
+ // Convention management tool - completely independent
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ const GUIDES_DIR = path.join(process.cwd(), 'guides');
5
+ const GUIDES_FILE = path.join(GUIDES_DIR, 'coding_guides.json');
6
+ async function ensureGuidesDir() {
7
+ try {
8
+ await fs.access(GUIDES_DIR);
9
+ }
10
+ catch {
11
+ await fs.mkdir(GUIDES_DIR, { recursive: true });
12
+ }
13
+ }
14
+ async function loadGuides() {
15
+ try {
16
+ await ensureGuidesDir();
17
+ const data = await fs.readFile(GUIDES_FILE, 'utf-8');
18
+ return JSON.parse(data);
19
+ }
20
+ catch {
21
+ return [];
22
+ }
23
+ }
24
+ async function findGuide(name) {
25
+ const guides = await loadGuides();
26
+ return guides.find(g => g.name === name);
27
+ }
28
+ export const getCodingGuideDefinition = {
29
+ name: 'get_coding_guide',
30
+ description: '가이드|규칙|컨벤션|guide|rules|convention|standards|best practices - Get coding guide',
31
+ inputSchema: {
32
+ type: 'object',
33
+ properties: {
34
+ name: { type: 'string', description: 'Guide name to retrieve' },
35
+ category: { type: 'string', description: 'Guide category' }
36
+ },
37
+ required: ['name']
38
+ },
39
+ annotations: {
40
+ title: 'Get Coding Guide',
41
+ audience: ['user', 'assistant']
42
+ }
43
+ };
44
+ export async function getCodingGuide(args) {
45
+ const { name: guideName, category: guideCategory } = args;
46
+ try {
47
+ const guide = await findGuide(guideName);
48
+ if (guide) {
49
+ return {
50
+ content: [{ type: 'text', text: `Guide: ${guide.name}\nCategory: ${guide.category}\n\n${guide.content}\n\nTags: ${guide.tags.join(', ')} | Updated: ${guide.lastUpdated}` }]
51
+ };
52
+ }
53
+ else {
54
+ return {
55
+ content: [{ type: 'text', text: `Guide not found: "${guideName}". Use list_coding_guides to see available guides.` }]
56
+ };
57
+ }
58
+ }
59
+ catch (error) {
60
+ return {
61
+ content: [{ type: 'text', text: `Error retrieving guide: ${error instanceof Error ? error.message : 'Unknown error'}` }]
62
+ };
63
+ }
64
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Language detection utility for code quality tools
3
+ * Supports TypeScript, JavaScript, Python, and Dart/Flutter
4
+ */
5
+ export function detectLanguage(code) {
6
+ // Dart/Flutter indicators (check first - most specific)
7
+ if (/\bWidget\b/.test(code) ||
8
+ /\bStatelessWidget\b/.test(code) ||
9
+ /\bStatefulWidget\b/.test(code) ||
10
+ /\bBuildContext\b/.test(code) ||
11
+ /Widget build\(/.test(code) ||
12
+ /extends\s+(StatelessWidget|StatefulWidget|State)/.test(code) ||
13
+ (/@override/i.test(code) && /Widget|BuildContext/.test(code))) {
14
+ return 'dart';
15
+ }
16
+ // Python indicators
17
+ if (/^(def|async def)\s/m.test(code) ||
18
+ /^from\s+\w+\s+import/.test(code) ||
19
+ /^\s+def\s/m.test(code) || // Indented function definitions
20
+ /\belif\b/.test(code) ||
21
+ /\b__init__\b/.test(code) ||
22
+ /\bprint\(/m.test(code) ||
23
+ (/:\s*$/m.test(code) && !/;/.test(code)) // Python colon without semicolon
24
+ ) {
25
+ return 'python';
26
+ }
27
+ // TypeScript indicators
28
+ if (/:\s*(string|number|boolean|any|void|unknown|never)\b/.test(code) ||
29
+ /interface\s+\w+/.test(code) ||
30
+ /type\s+\w+\s*=/.test(code) ||
31
+ /<[A-Z]\w*>/.test(code) // generics with capital letters
32
+ ) {
33
+ return 'typescript';
34
+ }
35
+ // JavaScript (default if none match)
36
+ if (/\b(const|let|var|function|class|async|await|import|export)\b/.test(code)) {
37
+ return 'javascript';
38
+ }
39
+ return 'unknown';
40
+ }
41
+ export function getLanguageName(lang) {
42
+ const names = {
43
+ typescript: 'TypeScript',
44
+ javascript: 'JavaScript',
45
+ python: 'Python',
46
+ dart: 'Dart/Flutter',
47
+ unknown: 'Unknown'
48
+ };
49
+ return names[lang];
50
+ }