activo 0.2.1 → 0.3.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 (73) hide show
  1. package/README.md +79 -3
  2. package/dist/core/commands.d.ts +11 -0
  3. package/dist/core/commands.d.ts.map +1 -0
  4. package/dist/core/commands.js +90 -0
  5. package/dist/core/commands.js.map +1 -0
  6. package/dist/core/llm/ollama.d.ts +2 -0
  7. package/dist/core/llm/ollama.d.ts.map +1 -1
  8. package/dist/core/llm/ollama.js +26 -0
  9. package/dist/core/llm/ollama.js.map +1 -1
  10. package/dist/core/tools/ast.d.ts +81 -0
  11. package/dist/core/tools/ast.d.ts.map +1 -0
  12. package/dist/core/tools/ast.js +700 -0
  13. package/dist/core/tools/ast.js.map +1 -0
  14. package/dist/core/tools/cache.d.ts +19 -0
  15. package/dist/core/tools/cache.d.ts.map +1 -0
  16. package/dist/core/tools/cache.js +497 -0
  17. package/dist/core/tools/cache.js.map +1 -0
  18. package/dist/core/tools/cssAnalysis.d.ts +3 -0
  19. package/dist/core/tools/cssAnalysis.d.ts.map +1 -0
  20. package/dist/core/tools/cssAnalysis.js +270 -0
  21. package/dist/core/tools/cssAnalysis.js.map +1 -0
  22. package/dist/core/tools/embeddings.d.ts +8 -0
  23. package/dist/core/tools/embeddings.d.ts.map +1 -0
  24. package/dist/core/tools/embeddings.js +631 -0
  25. package/dist/core/tools/embeddings.js.map +1 -0
  26. package/dist/core/tools/frontendAst.d.ts +6 -0
  27. package/dist/core/tools/frontendAst.d.ts.map +1 -0
  28. package/dist/core/tools/frontendAst.js +680 -0
  29. package/dist/core/tools/frontendAst.js.map +1 -0
  30. package/dist/core/tools/htmlAnalysis.d.ts +3 -0
  31. package/dist/core/tools/htmlAnalysis.d.ts.map +1 -0
  32. package/dist/core/tools/htmlAnalysis.js +398 -0
  33. package/dist/core/tools/htmlAnalysis.js.map +1 -0
  34. package/dist/core/tools/index.d.ts +10 -0
  35. package/dist/core/tools/index.d.ts.map +1 -1
  36. package/dist/core/tools/index.js +21 -1
  37. package/dist/core/tools/index.js.map +1 -1
  38. package/dist/core/tools/javaAst.d.ts +6 -0
  39. package/dist/core/tools/javaAst.d.ts.map +1 -0
  40. package/dist/core/tools/javaAst.js +678 -0
  41. package/dist/core/tools/javaAst.js.map +1 -0
  42. package/dist/core/tools/memory.d.ts +11 -0
  43. package/dist/core/tools/memory.d.ts.map +1 -0
  44. package/dist/core/tools/memory.js +551 -0
  45. package/dist/core/tools/memory.js.map +1 -0
  46. package/dist/core/tools/mybatisAnalysis.d.ts +3 -0
  47. package/dist/core/tools/mybatisAnalysis.d.ts.map +1 -0
  48. package/dist/core/tools/mybatisAnalysis.js +251 -0
  49. package/dist/core/tools/mybatisAnalysis.js.map +1 -0
  50. package/dist/core/tools/sqlAnalysis.d.ts +3 -0
  51. package/dist/core/tools/sqlAnalysis.d.ts.map +1 -0
  52. package/dist/core/tools/sqlAnalysis.js +250 -0
  53. package/dist/core/tools/sqlAnalysis.js.map +1 -0
  54. package/dist/ui/App.d.ts.map +1 -1
  55. package/dist/ui/App.js +31 -2
  56. package/dist/ui/App.js.map +1 -1
  57. package/package.json +2 -1
  58. package/src/core/commands.ts +118 -0
  59. package/src/core/llm/ollama.ts +30 -0
  60. package/src/core/tools/ast.ts +826 -0
  61. package/src/core/tools/cache.ts +570 -0
  62. package/src/core/tools/cssAnalysis.ts +324 -0
  63. package/src/core/tools/embeddings.ts +746 -0
  64. package/src/core/tools/frontendAst.ts +802 -0
  65. package/src/core/tools/htmlAnalysis.ts +466 -0
  66. package/src/core/tools/index.ts +21 -1
  67. package/src/core/tools/javaAst.ts +812 -0
  68. package/src/core/tools/memory.ts +655 -0
  69. package/src/core/tools/mybatisAnalysis.ts +322 -0
  70. package/src/core/tools/sqlAnalysis.ts +298 -0
  71. package/src/ui/App.tsx +38 -2
  72. package/FINAL_SIMPLIFIED_SPEC.md +0 -456
  73. package/TODO.md +0 -193
@@ -0,0 +1,678 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { parse, createVisitor } from "java-ast";
4
+ // Calculate cyclomatic complexity from method body text
5
+ function calculateComplexity(methodText) {
6
+ let complexity = 1;
7
+ // Count decision points
8
+ const patterns = [
9
+ /\bif\s*\(/g,
10
+ /\belse\s+if\s*\(/g,
11
+ /\bfor\s*\(/g,
12
+ /\bwhile\s*\(/g,
13
+ /\bcase\s+/g,
14
+ /\bcatch\s*\(/g,
15
+ /\b\?\s*[^:]/g, // ternary operator
16
+ /\&\&/g,
17
+ /\|\|/g,
18
+ ];
19
+ for (const pattern of patterns) {
20
+ const matches = methodText.match(pattern);
21
+ if (matches) {
22
+ complexity += matches.length;
23
+ }
24
+ }
25
+ return complexity;
26
+ }
27
+ // Analyze Java file
28
+ function analyzeJavaFile(content, filepath) {
29
+ const analysis = {
30
+ filepath,
31
+ package: "",
32
+ imports: [],
33
+ classes: [],
34
+ complexity: { total: 0, average: 0, highest: { name: "", value: 0 } },
35
+ };
36
+ try {
37
+ const tree = parse(content);
38
+ const lines = content.split("\n");
39
+ // Get line number from context
40
+ const getLine = (ctx) => {
41
+ if (ctx && ctx.start) {
42
+ return ctx.start.line;
43
+ }
44
+ return 0;
45
+ };
46
+ // Get text from context
47
+ const getText = (ctx) => {
48
+ if (ctx && ctx.start && ctx.stop) {
49
+ const startIdx = ctx.start.startIndex;
50
+ const stopIdx = ctx.stop.stopIndex;
51
+ return content.substring(startIdx, stopIdx + 1);
52
+ }
53
+ return ctx?.text || "";
54
+ };
55
+ // Get modifiers
56
+ const getModifiers = (modifierCtxs) => {
57
+ if (!modifierCtxs)
58
+ return [];
59
+ return modifierCtxs.map((m) => m.text).filter((m) => m);
60
+ };
61
+ // Get annotations
62
+ const getAnnotations = (modifierCtxs) => {
63
+ if (!modifierCtxs)
64
+ return [];
65
+ return modifierCtxs
66
+ .filter((m) => m.annotation)
67
+ .map((m) => m.annotation()?.qualifiedName()?.text || m.text)
68
+ .filter((a) => a);
69
+ };
70
+ // Parse using visitor pattern
71
+ const visitor = createVisitor({
72
+ defaultResult: () => null,
73
+ aggregateResult: (a, b) => b || a,
74
+ visitPackageDeclaration: (ctx) => {
75
+ analysis.package = ctx.qualifiedName()?.text || "";
76
+ return null;
77
+ },
78
+ visitImportDeclaration: (ctx) => {
79
+ const importText = ctx.qualifiedName()?.text || "";
80
+ if (importText) {
81
+ const isStatic = ctx.STATIC() ? "static " : "";
82
+ const isWildcard = ctx.MUL() ? ".*" : "";
83
+ analysis.imports.push(isStatic + importText + isWildcard);
84
+ }
85
+ return null;
86
+ },
87
+ visitClassDeclaration: (ctx) => {
88
+ const classInfo = {
89
+ name: ctx.identifier()?.text || "Unknown",
90
+ type: "class",
91
+ modifiers: [],
92
+ line: getLine(ctx),
93
+ implements: [],
94
+ annotations: [],
95
+ methods: [],
96
+ fields: [],
97
+ innerClasses: [],
98
+ };
99
+ // Get parent modifiers
100
+ const parent = ctx.parent;
101
+ if (parent && parent.classOrInterfaceModifier) {
102
+ const mods = parent.classOrInterfaceModifier();
103
+ classInfo.modifiers = getModifiers(mods);
104
+ classInfo.annotations = getAnnotations(mods);
105
+ }
106
+ // Extends
107
+ const extendsType = ctx.typeType();
108
+ if (extendsType) {
109
+ classInfo.extends = extendsType.text;
110
+ }
111
+ // Implements
112
+ const typeList = ctx.typeList();
113
+ if (typeList && typeList.length > 0) {
114
+ for (const tl of typeList) {
115
+ const types = tl.typeType();
116
+ if (types) {
117
+ classInfo.implements.push(...types.map((t) => t.text));
118
+ }
119
+ }
120
+ }
121
+ // Parse class body
122
+ const classBody = ctx.classBody();
123
+ if (classBody) {
124
+ const bodyDecls = classBody.classBodyDeclaration();
125
+ for (const bodyDecl of bodyDecls) {
126
+ const memberDecl = bodyDecl.memberDeclaration?.();
127
+ if (!memberDecl)
128
+ continue;
129
+ // Method
130
+ const methodDecl = memberDecl.methodDeclaration?.();
131
+ if (methodDecl) {
132
+ const methodInfo = {
133
+ name: methodDecl.identifier()?.text || "unknown",
134
+ returnType: methodDecl.typeTypeOrVoid()?.text || "void",
135
+ params: [],
136
+ modifiers: [],
137
+ line: getLine(methodDecl),
138
+ annotations: [],
139
+ throws: [],
140
+ complexity: 1,
141
+ };
142
+ // Get modifiers from parent
143
+ const modCtxs = bodyDecl.modifier?.();
144
+ if (modCtxs) {
145
+ methodInfo.modifiers = getModifiers(modCtxs);
146
+ methodInfo.annotations = getAnnotations(modCtxs);
147
+ }
148
+ // Parameters
149
+ const formalParams = methodDecl.formalParameters()?.formalParameterList?.();
150
+ if (formalParams) {
151
+ const params = formalParams.formalParameter?.();
152
+ if (params) {
153
+ for (const param of params) {
154
+ methodInfo.params.push({
155
+ name: param.variableDeclaratorId()?.text || "",
156
+ type: param.typeType()?.text || "",
157
+ });
158
+ }
159
+ }
160
+ }
161
+ // Throws
162
+ const throwsClause = methodDecl.THROWS?.();
163
+ if (throwsClause) {
164
+ const qualNames = methodDecl.qualifiedNameList()?.qualifiedName?.();
165
+ if (qualNames) {
166
+ methodInfo.throws = qualNames.map((q) => q.text);
167
+ }
168
+ }
169
+ // Complexity
170
+ const methodBody = methodDecl.methodBody();
171
+ if (methodBody) {
172
+ methodInfo.complexity = calculateComplexity(getText(methodBody));
173
+ }
174
+ classInfo.methods.push(methodInfo);
175
+ }
176
+ // Constructor
177
+ const ctorDecl = memberDecl.constructorDeclaration?.();
178
+ if (ctorDecl) {
179
+ const ctorInfo = {
180
+ name: ctorDecl.identifier()?.text || classInfo.name,
181
+ returnType: classInfo.name,
182
+ params: [],
183
+ modifiers: [],
184
+ line: getLine(ctorDecl),
185
+ annotations: [],
186
+ throws: [],
187
+ complexity: 1,
188
+ };
189
+ const modCtxs = bodyDecl.modifier?.();
190
+ if (modCtxs) {
191
+ ctorInfo.modifiers = getModifiers(modCtxs);
192
+ ctorInfo.annotations = getAnnotations(modCtxs);
193
+ }
194
+ const formalParams = ctorDecl.formalParameters()?.formalParameterList?.();
195
+ if (formalParams) {
196
+ const params = formalParams.formalParameter?.();
197
+ if (params) {
198
+ for (const param of params) {
199
+ ctorInfo.params.push({
200
+ name: param.variableDeclaratorId()?.text || "",
201
+ type: param.typeType()?.text || "",
202
+ });
203
+ }
204
+ }
205
+ }
206
+ const ctorBody = ctorDecl.block();
207
+ if (ctorBody) {
208
+ ctorInfo.complexity = calculateComplexity(getText(ctorBody));
209
+ }
210
+ classInfo.methods.push(ctorInfo);
211
+ }
212
+ // Field
213
+ const fieldDecl = memberDecl.fieldDeclaration?.();
214
+ if (fieldDecl) {
215
+ const varDecls = fieldDecl.variableDeclarators()?.variableDeclarator?.();
216
+ if (varDecls) {
217
+ for (const varDecl of varDecls) {
218
+ const fieldInfo = {
219
+ name: varDecl.variableDeclaratorId()?.text || "",
220
+ type: fieldDecl.typeType()?.text || "",
221
+ modifiers: [],
222
+ line: getLine(fieldDecl),
223
+ annotations: [],
224
+ };
225
+ const modCtxs = bodyDecl.modifier?.();
226
+ if (modCtxs) {
227
+ fieldInfo.modifiers = getModifiers(modCtxs);
228
+ fieldInfo.annotations = getAnnotations(modCtxs);
229
+ }
230
+ classInfo.fields.push(fieldInfo);
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ analysis.classes.push(classInfo);
237
+ return null;
238
+ },
239
+ visitInterfaceDeclaration: (ctx) => {
240
+ const interfaceInfo = {
241
+ name: ctx.identifier()?.text || "Unknown",
242
+ type: "interface",
243
+ modifiers: [],
244
+ line: getLine(ctx),
245
+ implements: [],
246
+ annotations: [],
247
+ methods: [],
248
+ fields: [],
249
+ innerClasses: [],
250
+ };
251
+ // Extends (interfaces extend other interfaces)
252
+ const typeList = ctx.typeList();
253
+ if (typeList && typeList.length > 0) {
254
+ for (const tl of typeList) {
255
+ const types = tl.typeType?.();
256
+ if (types) {
257
+ interfaceInfo.implements.push(...types.map((t) => t.text));
258
+ }
259
+ }
260
+ }
261
+ // Parse interface body
262
+ const interfaceBody = ctx.interfaceBody();
263
+ if (interfaceBody) {
264
+ const bodyDecls = interfaceBody.interfaceBodyDeclaration();
265
+ for (const bodyDecl of bodyDecls) {
266
+ const memberDecl = bodyDecl.interfaceMemberDeclaration?.();
267
+ if (!memberDecl)
268
+ continue;
269
+ const methodDecl = memberDecl.interfaceMethodDeclaration?.();
270
+ if (methodDecl) {
271
+ const commonBody = methodDecl.interfaceCommonBodyDeclaration?.();
272
+ if (commonBody) {
273
+ const methodInfo = {
274
+ name: commonBody.identifier()?.text || "unknown",
275
+ returnType: methodDecl.interfaceMethodModifier?.()?.map((m) => m.text).join(" ") || "void",
276
+ params: [],
277
+ modifiers: [],
278
+ line: getLine(methodDecl),
279
+ annotations: [],
280
+ throws: [],
281
+ complexity: 1,
282
+ };
283
+ interfaceInfo.methods.push(methodInfo);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ analysis.classes.push(interfaceInfo);
289
+ return null;
290
+ },
291
+ visitEnumDeclaration: (ctx) => {
292
+ const enumInfo = {
293
+ name: ctx.identifier()?.text || "Unknown",
294
+ type: "enum",
295
+ modifiers: [],
296
+ line: getLine(ctx),
297
+ implements: [],
298
+ annotations: [],
299
+ methods: [],
300
+ fields: [],
301
+ innerClasses: [],
302
+ };
303
+ // Enum constants as fields
304
+ const enumConstants = ctx.enumConstants()?.enumConstant?.();
305
+ if (enumConstants) {
306
+ for (const ec of enumConstants) {
307
+ enumInfo.fields.push({
308
+ name: ec.identifier()?.text || "",
309
+ type: enumInfo.name,
310
+ modifiers: ["public", "static", "final"],
311
+ line: getLine(ec),
312
+ annotations: [],
313
+ });
314
+ }
315
+ }
316
+ analysis.classes.push(enumInfo);
317
+ return null;
318
+ },
319
+ });
320
+ visitor.visit(tree);
321
+ // Calculate complexity stats
322
+ const allMethods = analysis.classes.flatMap((c) => c.methods);
323
+ if (allMethods.length > 0) {
324
+ analysis.complexity.total = allMethods.reduce((sum, m) => sum + m.complexity, 0);
325
+ analysis.complexity.average = Math.round(analysis.complexity.total / allMethods.length * 10) / 10;
326
+ analysis.complexity.highest = allMethods.reduce((max, m) => (m.complexity > max.value ? { name: m.name, value: m.complexity } : max), { name: "", value: 0 });
327
+ }
328
+ }
329
+ catch (error) {
330
+ // If parsing fails, try basic regex extraction
331
+ const classMatch = content.match(/(?:public\s+)?(?:abstract\s+)?(?:class|interface|enum)\s+(\w+)/);
332
+ if (classMatch) {
333
+ analysis.classes.push({
334
+ name: classMatch[1],
335
+ type: content.includes("interface ") ? "interface" : content.includes("enum ") ? "enum" : "class",
336
+ modifiers: [],
337
+ line: 1,
338
+ implements: [],
339
+ annotations: [],
340
+ methods: [],
341
+ fields: [],
342
+ innerClasses: [],
343
+ });
344
+ }
345
+ }
346
+ return analysis;
347
+ }
348
+ // Format analysis as text
349
+ function formatJavaAnalysis(analysis) {
350
+ const lines = [];
351
+ lines.push(`=== ${path.basename(analysis.filepath)} ===`);
352
+ lines.push("");
353
+ if (analysis.package) {
354
+ lines.push(`📦 Package: ${analysis.package}`);
355
+ lines.push("");
356
+ }
357
+ if (analysis.imports.length > 0) {
358
+ lines.push("📥 Imports:");
359
+ // Group by prefix
360
+ const grouped = {};
361
+ for (const imp of analysis.imports) {
362
+ const prefix = imp.split(".").slice(0, 2).join(".");
363
+ if (!grouped[prefix])
364
+ grouped[prefix] = [];
365
+ grouped[prefix].push(imp);
366
+ }
367
+ for (const [prefix, imps] of Object.entries(grouped)) {
368
+ lines.push(` ${prefix}.* (${imps.length})`);
369
+ }
370
+ lines.push("");
371
+ }
372
+ for (const cls of analysis.classes) {
373
+ const icon = cls.type === "interface" ? "📋" : cls.type === "enum" ? "🔢" : "🏛️";
374
+ const mods = cls.modifiers.length > 0 ? cls.modifiers.join(" ") + " " : "";
375
+ const ext = cls.extends ? ` extends ${cls.extends}` : "";
376
+ const impl = cls.implements.length > 0 ? ` implements ${cls.implements.join(", ")}` : "";
377
+ const annots = cls.annotations.length > 0 ? cls.annotations.map((a) => `@${a}`).join(" ") + " " : "";
378
+ lines.push(`${icon} L${cls.line}: ${annots}${mods}${cls.type} ${cls.name}${ext}${impl}`);
379
+ // Fields
380
+ if (cls.fields.length > 0) {
381
+ lines.push(" 필드:");
382
+ for (const field of cls.fields) {
383
+ const fMods = field.modifiers.join(" ");
384
+ const fAnnots = field.annotations.length > 0 ? field.annotations.map((a) => `@${a}`).join(" ") + " " : "";
385
+ lines.push(` ${fAnnots}${fMods} ${field.type} ${field.name}`);
386
+ }
387
+ }
388
+ // Methods
389
+ if (cls.methods.length > 0) {
390
+ lines.push(" 메서드:");
391
+ for (const method of cls.methods) {
392
+ const mMods = method.modifiers.join(" ");
393
+ const mAnnots = method.annotations.length > 0 ? method.annotations.map((a) => `@${a}`).join(" ") + " " : "";
394
+ const params = method.params.map((p) => `${p.type} ${p.name}`).join(", ");
395
+ const throws = method.throws.length > 0 ? ` throws ${method.throws.join(", ")}` : "";
396
+ lines.push(` L${method.line}: ${mAnnots}${mMods} ${method.returnType} ${method.name}(${params})${throws}`);
397
+ lines.push(` 복잡도: ${method.complexity}`);
398
+ }
399
+ }
400
+ lines.push("");
401
+ }
402
+ lines.push("📊 복잡도:");
403
+ lines.push(` 총합: ${analysis.complexity.total} | 평균: ${analysis.complexity.average}`);
404
+ if (analysis.complexity.highest.name) {
405
+ lines.push(` 최고: ${analysis.complexity.highest.name} (${analysis.complexity.highest.value})`);
406
+ }
407
+ return lines.join("\n");
408
+ }
409
+ // Java Analyze Tool
410
+ export const javaAnalyzeTool = {
411
+ name: "java_analyze",
412
+ description: "Analyze Java source file using AST parser (Java 분석). Returns classes, methods, fields, annotations, complexity. Use when user asks: 'analyze java', 'java 분석', 'Spring 분석'.",
413
+ parameters: {
414
+ type: "object",
415
+ required: ["filepath"],
416
+ properties: {
417
+ filepath: {
418
+ type: "string",
419
+ description: "Path to Java file (.java)",
420
+ },
421
+ format: {
422
+ type: "string",
423
+ description: "Output format: 'text' or 'json'",
424
+ enum: ["text", "json"],
425
+ },
426
+ },
427
+ },
428
+ handler: async (args) => {
429
+ try {
430
+ const filepath = path.resolve(args.filepath);
431
+ const format = args.format || "text";
432
+ if (!fs.existsSync(filepath)) {
433
+ return { success: false, content: "", error: `File not found: ${filepath}` };
434
+ }
435
+ if (!filepath.endsWith(".java")) {
436
+ return { success: false, content: "", error: "Not a Java file (.java required)" };
437
+ }
438
+ const content = fs.readFileSync(filepath, "utf-8");
439
+ const analysis = analyzeJavaFile(content, filepath);
440
+ if (format === "json") {
441
+ return { success: true, content: JSON.stringify(analysis, null, 2) };
442
+ }
443
+ return { success: true, content: formatJavaAnalysis(analysis) };
444
+ }
445
+ catch (error) {
446
+ return { success: false, content: "", error: String(error) };
447
+ }
448
+ },
449
+ };
450
+ // Java Complexity Report Tool
451
+ export const javaComplexityTool = {
452
+ name: "java_complexity",
453
+ description: "Calculate complexity for Java files (Java 복잡도 리포트). Use when user asks: 'java complexity', 'java 복잡도'.",
454
+ parameters: {
455
+ type: "object",
456
+ required: ["pattern"],
457
+ properties: {
458
+ pattern: {
459
+ type: "string",
460
+ description: "Glob pattern (e.g., src/**/*.java)",
461
+ },
462
+ threshold: {
463
+ type: "number",
464
+ description: "Only show methods with complexity >= threshold (default: 5)",
465
+ },
466
+ },
467
+ },
468
+ handler: async (args) => {
469
+ try {
470
+ const { glob } = await import("glob");
471
+ const pattern = args.pattern;
472
+ const threshold = args.threshold || 5;
473
+ const files = await glob(pattern, {
474
+ ignore: ["**/node_modules/**", "**/target/**", "**/build/**"],
475
+ });
476
+ const allMethods = [];
477
+ for (const file of files) {
478
+ if (!file.endsWith(".java"))
479
+ continue;
480
+ try {
481
+ const content = fs.readFileSync(file, "utf-8");
482
+ const analysis = analyzeJavaFile(content, file);
483
+ for (const cls of analysis.classes) {
484
+ for (const method of cls.methods) {
485
+ allMethods.push({
486
+ file: path.relative(process.cwd(), file),
487
+ class: cls.name,
488
+ method: method.name,
489
+ complexity: method.complexity,
490
+ line: method.line,
491
+ });
492
+ }
493
+ }
494
+ }
495
+ catch {
496
+ // Skip unparseable files
497
+ }
498
+ }
499
+ // Sort by complexity
500
+ allMethods.sort((a, b) => b.complexity - a.complexity);
501
+ const high = allMethods.filter((m) => m.complexity >= threshold);
502
+ const total = allMethods.reduce((sum, m) => sum + m.complexity, 0);
503
+ const avg = allMethods.length > 0 ? Math.round(total / allMethods.length * 10) / 10 : 0;
504
+ const lines = [];
505
+ lines.push("=== Java 복잡도 리포트 ===");
506
+ lines.push("");
507
+ lines.push("📊 통계:");
508
+ lines.push(` 총 메서드: ${allMethods.length}개`);
509
+ lines.push(` 평균 복잡도: ${avg}`);
510
+ lines.push(` 높은 복잡도 (>= ${threshold}): ${high.length}개`);
511
+ lines.push("");
512
+ if (high.length > 0) {
513
+ lines.push(`⚠️ 복잡도 높은 메서드 (>= ${threshold}):`);
514
+ for (const m of high.slice(0, 20)) {
515
+ const bar = "█".repeat(Math.min(m.complexity, 20));
516
+ lines.push(` ${m.complexity.toString().padStart(2)} ${bar} ${m.class}.${m.method}()`);
517
+ lines.push(` ${m.file}:${m.line}`);
518
+ }
519
+ if (high.length > 20) {
520
+ lines.push(` ... 외 ${high.length - 20}개`);
521
+ }
522
+ }
523
+ else {
524
+ lines.push(`✅ 복잡도 ${threshold} 이상인 메서드가 없습니다.`);
525
+ }
526
+ return { success: true, content: lines.join("\n") };
527
+ }
528
+ catch (error) {
529
+ return { success: false, content: "", error: String(error) };
530
+ }
531
+ },
532
+ };
533
+ // Spring Pattern Check Tool
534
+ export const springCheckTool = {
535
+ name: "spring_check",
536
+ description: "Check Spring framework patterns (Spring 패턴 검사). Finds controllers, services, repositories, configuration. Use when user asks: 'spring check', 'spring 분석', 'Spring 패턴'.",
537
+ parameters: {
538
+ type: "object",
539
+ required: ["pattern"],
540
+ properties: {
541
+ pattern: {
542
+ type: "string",
543
+ description: "Glob pattern (e.g., src/**/*.java)",
544
+ },
545
+ },
546
+ },
547
+ handler: async (args) => {
548
+ try {
549
+ const { glob } = await import("glob");
550
+ const pattern = args.pattern;
551
+ const files = await glob(pattern, {
552
+ ignore: ["**/node_modules/**", "**/target/**", "**/build/**"],
553
+ });
554
+ const springComponents = {
555
+ controllers: [],
556
+ services: [],
557
+ repositories: [],
558
+ components: [],
559
+ configurations: [],
560
+ entities: [],
561
+ };
562
+ for (const file of files) {
563
+ if (!file.endsWith(".java"))
564
+ continue;
565
+ try {
566
+ const content = fs.readFileSync(file, "utf-8");
567
+ const analysis = analyzeJavaFile(content, file);
568
+ const relativePath = path.relative(process.cwd(), file);
569
+ for (const cls of analysis.classes) {
570
+ const annotations = cls.annotations.map((a) => a.toLowerCase());
571
+ // Controller
572
+ if (annotations.some((a) => a.includes("controller") || a.includes("restcontroller"))) {
573
+ const mappings = [];
574
+ // Find request mappings in methods
575
+ for (const method of cls.methods) {
576
+ const methodAnnots = method.annotations.join(" ").toLowerCase();
577
+ if (methodAnnots.includes("mapping")) {
578
+ mappings.push(`${method.name}()`);
579
+ }
580
+ }
581
+ springComponents.controllers.push({ file: relativePath, class: cls.name, mappings });
582
+ }
583
+ // Service
584
+ if (annotations.some((a) => a.includes("service"))) {
585
+ springComponents.services.push({ file: relativePath, class: cls.name });
586
+ }
587
+ // Repository
588
+ if (annotations.some((a) => a.includes("repository"))) {
589
+ springComponents.repositories.push({ file: relativePath, class: cls.name });
590
+ }
591
+ // Component
592
+ if (annotations.some((a) => a === "component")) {
593
+ springComponents.components.push({ file: relativePath, class: cls.name });
594
+ }
595
+ // Configuration
596
+ if (annotations.some((a) => a.includes("configuration"))) {
597
+ springComponents.configurations.push({ file: relativePath, class: cls.name });
598
+ }
599
+ // Entity
600
+ if (annotations.some((a) => a.includes("entity") || a.includes("table"))) {
601
+ springComponents.entities.push({ file: relativePath, class: cls.name });
602
+ }
603
+ }
604
+ }
605
+ catch {
606
+ // Skip unparseable files
607
+ }
608
+ }
609
+ const lines = [];
610
+ lines.push("=== Spring 패턴 분석 ===");
611
+ lines.push("");
612
+ if (springComponents.controllers.length > 0) {
613
+ lines.push(`🎮 Controllers (${springComponents.controllers.length}):`);
614
+ for (const c of springComponents.controllers) {
615
+ lines.push(` ${c.class}`);
616
+ lines.push(` ${c.file}`);
617
+ if (c.mappings.length > 0) {
618
+ lines.push(` 엔드포인트: ${c.mappings.join(", ")}`);
619
+ }
620
+ }
621
+ lines.push("");
622
+ }
623
+ if (springComponents.services.length > 0) {
624
+ lines.push(`⚙️ Services (${springComponents.services.length}):`);
625
+ for (const s of springComponents.services) {
626
+ lines.push(` ${s.class} - ${s.file}`);
627
+ }
628
+ lines.push("");
629
+ }
630
+ if (springComponents.repositories.length > 0) {
631
+ lines.push(`🗄️ Repositories (${springComponents.repositories.length}):`);
632
+ for (const r of springComponents.repositories) {
633
+ lines.push(` ${r.class} - ${r.file}`);
634
+ }
635
+ lines.push("");
636
+ }
637
+ if (springComponents.entities.length > 0) {
638
+ lines.push(`📋 Entities (${springComponents.entities.length}):`);
639
+ for (const e of springComponents.entities) {
640
+ lines.push(` ${e.class} - ${e.file}`);
641
+ }
642
+ lines.push("");
643
+ }
644
+ if (springComponents.configurations.length > 0) {
645
+ lines.push(`🔧 Configurations (${springComponents.configurations.length}):`);
646
+ for (const c of springComponents.configurations) {
647
+ lines.push(` ${c.class} - ${c.file}`);
648
+ }
649
+ lines.push("");
650
+ }
651
+ if (springComponents.components.length > 0) {
652
+ lines.push(`📦 Components (${springComponents.components.length}):`);
653
+ for (const c of springComponents.components) {
654
+ lines.push(` ${c.class} - ${c.file}`);
655
+ }
656
+ lines.push("");
657
+ }
658
+ const total = Object.values(springComponents).reduce((sum, arr) => sum + arr.length, 0);
659
+ if (total === 0) {
660
+ lines.push("Spring 컴포넌트를 찾지 못했습니다.");
661
+ }
662
+ else {
663
+ lines.push(`📊 총 ${total}개 Spring 컴포넌트 발견`);
664
+ }
665
+ return { success: true, content: lines.join("\n") };
666
+ }
667
+ catch (error) {
668
+ return { success: false, content: "", error: String(error) };
669
+ }
670
+ },
671
+ };
672
+ // Export all Java tools
673
+ export const javaTools = [
674
+ javaAnalyzeTool,
675
+ javaComplexityTool,
676
+ springCheckTool,
677
+ ];
678
+ //# sourceMappingURL=javaAst.js.map