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,466 @@
1
+ import { Tool, ToolResult } from "./types.js";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+
5
+ interface HtmlElement {
6
+ tag: string;
7
+ line: number;
8
+ issues: string[];
9
+ attributes?: string[];
10
+ }
11
+
12
+ interface HtmlAnalysisResult {
13
+ file: string;
14
+ type: "html" | "jsp" | "vue" | "template";
15
+ elements: HtmlElement[];
16
+ meta: {
17
+ title: boolean;
18
+ description: boolean;
19
+ viewport: boolean;
20
+ charset: boolean;
21
+ lang: boolean;
22
+ };
23
+ accessibility: {
24
+ missingAlt: number;
25
+ missingLabel: number;
26
+ missingAriaLabel: number;
27
+ emptyLinks: number;
28
+ emptyButtons: number;
29
+ };
30
+ semantic: {
31
+ hasHeader: boolean;
32
+ hasNav: boolean;
33
+ hasMain: boolean;
34
+ hasFooter: boolean;
35
+ hasArticle: boolean;
36
+ divCount: number;
37
+ };
38
+ issues: string[];
39
+ summary: {
40
+ totalElements: number;
41
+ elementsWithIssues: number;
42
+ a11yScore: number;
43
+ seoScore: number;
44
+ };
45
+ }
46
+
47
+ // HTML/JSP/Vue 분석
48
+ function analyzeHtmlFile(filePath: string): HtmlAnalysisResult {
49
+ const content = fs.readFileSync(filePath, "utf-8");
50
+ const lines = content.split("\n");
51
+ const ext = path.extname(filePath).toLowerCase();
52
+
53
+ let type: "html" | "jsp" | "vue" | "template";
54
+ if (ext === ".jsp") type = "jsp";
55
+ else if (ext === ".vue") type = "vue";
56
+ else if (ext === ".ejs" || ext === ".hbs" || ext === ".pug") type = "template";
57
+ else type = "html";
58
+
59
+ const elements: HtmlElement[] = [];
60
+ const issues: string[] = [];
61
+
62
+ // Meta 태그 검사
63
+ const meta = {
64
+ title: /<title[^>]*>[^<]+<\/title>/i.test(content),
65
+ description: /<meta[^>]*name=["']description["'][^>]*>/i.test(content),
66
+ viewport: /<meta[^>]*name=["']viewport["'][^>]*>/i.test(content),
67
+ charset: /<meta[^>]*charset=/i.test(content) || /charset=/i.test(content),
68
+ lang: /<html[^>]*lang=/i.test(content),
69
+ };
70
+
71
+ // 접근성 검사
72
+ const accessibility = {
73
+ missingAlt: 0,
74
+ missingLabel: 0,
75
+ missingAriaLabel: 0,
76
+ emptyLinks: 0,
77
+ emptyButtons: 0,
78
+ };
79
+
80
+ // 시맨틱 태그 검사
81
+ const semantic = {
82
+ hasHeader: /<header[\s>]/i.test(content),
83
+ hasNav: /<nav[\s>]/i.test(content),
84
+ hasMain: /<main[\s>]/i.test(content),
85
+ hasFooter: /<footer[\s>]/i.test(content),
86
+ hasArticle: /<article[\s>]/i.test(content),
87
+ divCount: (content.match(/<div[\s>]/gi) || []).length,
88
+ };
89
+
90
+ // Deprecated 태그 목록
91
+ const deprecatedTags = [
92
+ "font", "center", "marquee", "blink", "strike", "big", "tt",
93
+ "frame", "frameset", "noframes", "applet", "basefont", "dir", "isindex",
94
+ ];
95
+
96
+ // 인라인 스타일/이벤트 핸들러
97
+ const inlineStyles = (content.match(/style=["'][^"']+["']/gi) || []).length;
98
+ const inlineEvents = (content.match(/on\w+=["'][^"']+["']/gi) || []).length;
99
+
100
+ // 라인 번호 찾기 함수
101
+ const findLineNumber = (index: number): number => {
102
+ let lineNum = 1;
103
+ for (let i = 0; i < index && i < content.length; i++) {
104
+ if (content[i] === "\n") lineNum++;
105
+ }
106
+ return lineNum;
107
+ };
108
+
109
+ // img 태그 검사
110
+ const imgRegex = /<img([^>]*)>/gi;
111
+ let match;
112
+ while ((match = imgRegex.exec(content)) !== null) {
113
+ const attrs = match[1];
114
+ const elementIssues: string[] = [];
115
+
116
+ if (!/alt=/i.test(attrs)) {
117
+ elementIssues.push("alt 속성 누락");
118
+ accessibility.missingAlt++;
119
+ } else if (/alt=["']\s*["']/i.test(attrs)) {
120
+ elementIssues.push("alt 속성 비어있음");
121
+ }
122
+
123
+ if (!/loading=/i.test(attrs)) {
124
+ elementIssues.push("loading 속성 권장 (lazy)");
125
+ }
126
+
127
+ if (elementIssues.length > 0) {
128
+ elements.push({
129
+ tag: "img",
130
+ line: findLineNumber(match.index),
131
+ issues: elementIssues,
132
+ });
133
+ }
134
+ }
135
+
136
+ // a 태그 검사
137
+ const aRegex = /<a([^>]*)>([^<]*)<\/a>/gi;
138
+ while ((match = aRegex.exec(content)) !== null) {
139
+ const attrs = match[1];
140
+ const text = match[2].trim();
141
+ const elementIssues: string[] = [];
142
+
143
+ if (!text && !/aria-label=/i.test(attrs)) {
144
+ elementIssues.push("빈 링크 텍스트");
145
+ accessibility.emptyLinks++;
146
+ }
147
+
148
+ if (/target=["']_blank["']/i.test(attrs) && !/rel=["'][^"']*noopener/i.test(attrs)) {
149
+ elementIssues.push("target=_blank에 rel=noopener 권장");
150
+ }
151
+
152
+ if (/href=["']#["']/i.test(attrs)) {
153
+ elementIssues.push("href='#' - 실제 링크 또는 button 사용 권장");
154
+ }
155
+
156
+ if (elementIssues.length > 0) {
157
+ elements.push({
158
+ tag: "a",
159
+ line: findLineNumber(match.index),
160
+ issues: elementIssues,
161
+ });
162
+ }
163
+ }
164
+
165
+ // button 태그 검사
166
+ const buttonRegex = /<button([^>]*)>([^<]*)<\/button>/gi;
167
+ while ((match = buttonRegex.exec(content)) !== null) {
168
+ const attrs = match[1];
169
+ const text = match[2].trim();
170
+ const elementIssues: string[] = [];
171
+
172
+ if (!text && !/aria-label=/i.test(attrs)) {
173
+ elementIssues.push("빈 버튼 텍스트");
174
+ accessibility.emptyButtons++;
175
+ }
176
+
177
+ if (!/type=/i.test(attrs)) {
178
+ elementIssues.push("type 속성 권장 (button/submit)");
179
+ }
180
+
181
+ if (elementIssues.length > 0) {
182
+ elements.push({
183
+ tag: "button",
184
+ line: findLineNumber(match.index),
185
+ issues: elementIssues,
186
+ });
187
+ }
188
+ }
189
+
190
+ // input 태그 검사
191
+ const inputRegex = /<input([^>]*)>/gi;
192
+ while ((match = inputRegex.exec(content)) !== null) {
193
+ const attrs = match[1];
194
+ const elementIssues: string[] = [];
195
+
196
+ // label 연결 검사 (id가 있어야 label for 가능)
197
+ const idMatch = attrs.match(/id=["']([^"']+)["']/i);
198
+ if (idMatch) {
199
+ const inputId = idMatch[1];
200
+ if (!new RegExp(`<label[^>]*for=["']${inputId}["']`, "i").test(content)) {
201
+ if (!/aria-label=/i.test(attrs) && !/placeholder=/i.test(attrs)) {
202
+ elementIssues.push("label 또는 aria-label 권장");
203
+ accessibility.missingLabel++;
204
+ }
205
+ }
206
+ } else if (!/aria-label=/i.test(attrs)) {
207
+ // id 없으면 aria-label 필요
208
+ if (!/type=["'](?:hidden|submit|button|reset)["']/i.test(attrs)) {
209
+ elementIssues.push("id 또는 aria-label 권장");
210
+ accessibility.missingLabel++;
211
+ }
212
+ }
213
+
214
+ // autocomplete 검사
215
+ if (/type=["'](?:password|email|tel|text)["']/i.test(attrs) && !/autocomplete=/i.test(attrs)) {
216
+ elementIssues.push("autocomplete 속성 권장");
217
+ }
218
+
219
+ if (elementIssues.length > 0) {
220
+ elements.push({
221
+ tag: "input",
222
+ line: findLineNumber(match.index),
223
+ issues: elementIssues,
224
+ });
225
+ }
226
+ }
227
+
228
+ // form 태그 검사
229
+ const formRegex = /<form([^>]*)>/gi;
230
+ while ((match = formRegex.exec(content)) !== null) {
231
+ const attrs = match[1];
232
+ const elementIssues: string[] = [];
233
+
234
+ if (!/action=/i.test(attrs)) {
235
+ elementIssues.push("action 속성 누락");
236
+ }
237
+
238
+ if (!/method=/i.test(attrs)) {
239
+ elementIssues.push("method 속성 권장");
240
+ }
241
+
242
+ if (elementIssues.length > 0) {
243
+ elements.push({
244
+ tag: "form",
245
+ line: findLineNumber(match.index),
246
+ issues: elementIssues,
247
+ });
248
+ }
249
+ }
250
+
251
+ // table 태그 검사
252
+ const tableRegex = /<table([^>]*)>/gi;
253
+ while ((match = tableRegex.exec(content)) !== null) {
254
+ const tableStart = match.index;
255
+ const tableEnd = content.indexOf("</table>", tableStart);
256
+ const tableContent = content.substring(tableStart, tableEnd);
257
+ const elementIssues: string[] = [];
258
+
259
+ if (!/<caption/i.test(tableContent)) {
260
+ elementIssues.push("caption 권장 (테이블 설명)");
261
+ }
262
+
263
+ if (!/<th[\s>]/i.test(tableContent)) {
264
+ elementIssues.push("th 요소 권장 (헤더 셀)");
265
+ }
266
+
267
+ if (elementIssues.length > 0) {
268
+ elements.push({
269
+ tag: "table",
270
+ line: findLineNumber(match.index),
271
+ issues: elementIssues,
272
+ });
273
+ }
274
+ }
275
+
276
+ // deprecated 태그 검사
277
+ deprecatedTags.forEach((tag) => {
278
+ const tagRegex = new RegExp(`<${tag}[\\s>]`, "gi");
279
+ let tagMatch;
280
+ while ((tagMatch = tagRegex.exec(content)) !== null) {
281
+ elements.push({
282
+ tag,
283
+ line: findLineNumber(tagMatch.index),
284
+ issues: [`deprecated 태그 - 사용 금지`],
285
+ });
286
+ }
287
+ });
288
+
289
+ // iframe 검사
290
+ const iframeRegex = /<iframe([^>]*)>/gi;
291
+ while ((match = iframeRegex.exec(content)) !== null) {
292
+ const attrs = match[1];
293
+ const elementIssues: string[] = [];
294
+
295
+ if (!/title=/i.test(attrs)) {
296
+ elementIssues.push("title 속성 권장 (접근성)");
297
+ }
298
+
299
+ if (!/sandbox=/i.test(attrs)) {
300
+ elementIssues.push("sandbox 속성 권장 (보안)");
301
+ }
302
+
303
+ if (elementIssues.length > 0) {
304
+ elements.push({
305
+ tag: "iframe",
306
+ line: findLineNumber(match.index),
307
+ issues: elementIssues,
308
+ });
309
+ }
310
+ }
311
+
312
+ // 전체 이슈 수집
313
+ if (!meta.title) issues.push("title 태그 누락 (SEO)");
314
+ if (!meta.description) issues.push("meta description 누락 (SEO)");
315
+ if (!meta.viewport) issues.push("viewport 설정 누락 (반응형)");
316
+ if (!meta.charset) issues.push("charset 설정 누락");
317
+ if (!meta.lang) issues.push("html lang 속성 누락 (접근성)");
318
+
319
+ if (!semantic.hasMain) issues.push("main 요소 없음 (시맨틱)");
320
+ if (semantic.divCount > 30) issues.push(`div 요소 ${semantic.divCount}개 - 시맨틱 태그 권장`);
321
+
322
+ if (inlineStyles > 5) issues.push(`인라인 스타일 ${inlineStyles}개 - CSS 클래스 권장`);
323
+ if (inlineEvents > 3) issues.push(`인라인 이벤트 ${inlineEvents}개 - JS 분리 권장`);
324
+
325
+ // 점수 계산
326
+ const a11yMaxPoints = 5;
327
+ let a11yPoints = a11yMaxPoints;
328
+ if (accessibility.missingAlt > 0) a11yPoints -= 1;
329
+ if (accessibility.missingLabel > 0) a11yPoints -= 1;
330
+ if (accessibility.emptyLinks > 0) a11yPoints -= 1;
331
+ if (accessibility.emptyButtons > 0) a11yPoints -= 1;
332
+ if (!meta.lang) a11yPoints -= 1;
333
+ const a11yScore = Math.max(0, Math.round((a11yPoints / a11yMaxPoints) * 100));
334
+
335
+ const seoMaxPoints = 5;
336
+ let seoPoints = seoMaxPoints;
337
+ if (!meta.title) seoPoints -= 1;
338
+ if (!meta.description) seoPoints -= 1;
339
+ if (!meta.viewport) seoPoints -= 1;
340
+ if (!semantic.hasMain) seoPoints -= 1;
341
+ if (!semantic.hasHeader && !semantic.hasNav) seoPoints -= 1;
342
+ const seoScore = Math.max(0, Math.round((seoPoints / seoMaxPoints) * 100));
343
+
344
+ return {
345
+ file: filePath,
346
+ type,
347
+ elements,
348
+ meta,
349
+ accessibility,
350
+ semantic,
351
+ issues,
352
+ summary: {
353
+ totalElements: elements.length,
354
+ elementsWithIssues: elements.filter((e) => e.issues.length > 0).length,
355
+ a11yScore,
356
+ seoScore,
357
+ },
358
+ };
359
+ }
360
+
361
+ // 도구 정의
362
+ export const htmlTools: Tool[] = [
363
+ {
364
+ name: "html_check",
365
+ description:
366
+ "HTML/JSP/Vue 파일을 분석합니다. 접근성(a11y), SEO 메타태그, 시맨틱 태그, deprecated 태그, 폼 요소 등을 검사합니다.",
367
+ parameters: {
368
+ type: "object",
369
+ properties: {
370
+ path: {
371
+ type: "string",
372
+ description: "분석할 HTML/JSP/Vue 파일 또는 디렉토리 경로",
373
+ },
374
+ recursive: {
375
+ type: "boolean",
376
+ description: "디렉토리인 경우 하위 폴더 포함 여부 (기본: true)",
377
+ },
378
+ },
379
+ required: ["path"],
380
+ },
381
+ handler: async (args: Record<string, unknown>): Promise<ToolResult> => {
382
+ const targetPath = args.path as string;
383
+ const recursive = args.recursive !== false;
384
+
385
+ if (!fs.existsSync(targetPath)) {
386
+ return {
387
+ success: false,
388
+ content: "",
389
+ error: `경로를 찾을 수 없습니다: ${targetPath}`,
390
+ };
391
+ }
392
+
393
+ const results: HtmlAnalysisResult[] = [];
394
+ const stats = fs.statSync(targetPath);
395
+ const htmlExtensions = [".html", ".htm", ".jsp", ".vue", ".ejs", ".hbs"];
396
+
397
+ if (stats.isFile()) {
398
+ const ext = path.extname(targetPath).toLowerCase();
399
+ if (htmlExtensions.includes(ext)) {
400
+ results.push(analyzeHtmlFile(targetPath));
401
+ }
402
+ } else if (stats.isDirectory()) {
403
+ const walkDir = (dir: string) => {
404
+ const files = fs.readdirSync(dir);
405
+ for (const file of files) {
406
+ const filePath = path.join(dir, file);
407
+ const fileStat = fs.statSync(filePath);
408
+ if (fileStat.isDirectory() && recursive) {
409
+ if (!file.startsWith(".") && file !== "node_modules" && file !== "target" && file !== "build") {
410
+ walkDir(filePath);
411
+ }
412
+ } else {
413
+ const ext = path.extname(file).toLowerCase();
414
+ if (htmlExtensions.includes(ext)) {
415
+ results.push(analyzeHtmlFile(filePath));
416
+ }
417
+ }
418
+ }
419
+ };
420
+ walkDir(targetPath);
421
+ }
422
+
423
+ // 전체 통계
424
+ const avgA11y = results.length > 0
425
+ ? Math.round(results.reduce((sum, r) => sum + r.summary.a11yScore, 0) / results.length)
426
+ : 0;
427
+ const avgSeo = results.length > 0
428
+ ? Math.round(results.reduce((sum, r) => sum + r.summary.seoScore, 0) / results.length)
429
+ : 0;
430
+
431
+ const totalA11y = {
432
+ missingAlt: results.reduce((sum, r) => sum + r.accessibility.missingAlt, 0),
433
+ missingLabel: results.reduce((sum, r) => sum + r.accessibility.missingLabel, 0),
434
+ emptyLinks: results.reduce((sum, r) => sum + r.accessibility.emptyLinks, 0),
435
+ emptyButtons: results.reduce((sum, r) => sum + r.accessibility.emptyButtons, 0),
436
+ };
437
+
438
+ const output = {
439
+ analyzedFiles: results.length,
440
+ averageA11yScore: avgA11y,
441
+ averageSeoScore: avgSeo,
442
+ totalAccessibilityIssues: totalA11y,
443
+ files: results.map((r) => ({
444
+ file: r.file,
445
+ type: r.type,
446
+ a11yScore: r.summary.a11yScore,
447
+ seoScore: r.summary.seoScore,
448
+ meta: r.meta,
449
+ semantic: r.semantic,
450
+ accessibility: r.accessibility,
451
+ pageIssues: r.issues,
452
+ elementIssues: r.elements.map((e) => ({
453
+ tag: e.tag,
454
+ line: e.line,
455
+ issues: e.issues,
456
+ })),
457
+ })),
458
+ };
459
+
460
+ return {
461
+ success: true,
462
+ content: JSON.stringify(output, null, 2),
463
+ };
464
+ },
465
+ },
466
+ ];
@@ -1,14 +1,34 @@
1
1
  import { Tool, ToolCall, ToolResult } from "./types.js";
2
2
  import { builtInTools } from "./builtIn.js";
3
3
  import { standardsTools } from "./standards.js";
4
+ import { cacheTools } from "./cache.js";
5
+ import { astTools } from "./ast.js";
6
+ import { embeddingTools } from "./embeddings.js";
7
+ import { memoryTools } from "./memory.js";
8
+ import { javaTools } from "./javaAst.js";
9
+ import { frontendTools } from "./frontendAst.js";
10
+ import { sqlTools } from "./sqlAnalysis.js";
11
+ import { mybatisTools } from "./mybatisAnalysis.js";
12
+ import { cssTools } from "./cssAnalysis.js";
13
+ import { htmlTools } from "./htmlAnalysis.js";
4
14
 
5
15
  export * from "./types.js";
6
16
  export * from "./builtIn.js";
7
17
  export * from "./standards.js";
18
+ export * from "./cache.js";
19
+ export * from "./ast.js";
20
+ export * from "./embeddings.js";
21
+ export * from "./memory.js";
22
+ export * from "./javaAst.js";
23
+ export * from "./frontendAst.js";
24
+ export * from "./sqlAnalysis.js";
25
+ export * from "./mybatisAnalysis.js";
26
+ export * from "./cssAnalysis.js";
27
+ export * from "./htmlAnalysis.js";
8
28
 
9
29
  // All available tools
10
30
  export function getAllTools(): Tool[] {
11
- return [...builtInTools, ...standardsTools];
31
+ return [...builtInTools, ...standardsTools, ...cacheTools, ...astTools, ...embeddingTools, ...memoryTools, ...javaTools, ...frontendTools, ...sqlTools, ...mybatisTools, ...cssTools, ...htmlTools];
12
32
  }
13
33
 
14
34
  // Get tool by name