activo 0.4.3 → 0.5.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 (166) hide show
  1. package/README.md +203 -1
  2. package/data/2026-03-04_20-54.json +181 -0
  3. package/data/2026-03-04_20-56.json +181 -0
  4. package/data/apex-rulesets/egov.yaml +469 -0
  5. package/data/apex-rulesets/modernize.yaml +687 -0
  6. package/data/apex-rulesets/quality.yaml +1677 -0
  7. package/data/apex-rulesets/rule-schema.yaml +587 -0
  8. package/data/apex-rulesets/secure.yaml +1688 -0
  9. package/data/apex-rulesets/spring.yaml +455 -0
  10. package/data/apex-rulesets/sql-format.yaml +99 -0
  11. package/data/apex-rulesets/sql-oracle.yaml +281 -0
  12. package/data/apex-rulesets/sql.yaml +1660 -0
  13. package/dist/cli/headless.d.ts.map +1 -1
  14. package/dist/cli/headless.js +32 -10
  15. package/dist/cli/headless.js.map +1 -1
  16. package/dist/cli/index.js +31 -3
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/core/agent.d.ts +3 -3
  19. package/dist/core/agent.d.ts.map +1 -1
  20. package/dist/core/agent.js +255 -17
  21. package/dist/core/agent.js.map +1 -1
  22. package/dist/core/commands.d.ts +2 -1
  23. package/dist/core/commands.d.ts.map +1 -1
  24. package/dist/core/commands.js +61 -9
  25. package/dist/core/commands.js.map +1 -1
  26. package/dist/core/config.d.ts +14 -0
  27. package/dist/core/config.d.ts.map +1 -1
  28. package/dist/core/config.js +41 -4
  29. package/dist/core/config.js.map +1 -1
  30. package/dist/core/conversation.d.ts +2 -2
  31. package/dist/core/conversation.d.ts.map +1 -1
  32. package/dist/core/conversation.js.map +1 -1
  33. package/dist/core/intentRouter.d.ts +43 -0
  34. package/dist/core/intentRouter.d.ts.map +1 -0
  35. package/dist/core/intentRouter.js +804 -0
  36. package/dist/core/intentRouter.js.map +1 -0
  37. package/dist/core/llm/anthropic.d.ts +24 -0
  38. package/dist/core/llm/anthropic.d.ts.map +1 -0
  39. package/dist/core/llm/anthropic.js +226 -0
  40. package/dist/core/llm/anthropic.js.map +1 -0
  41. package/dist/core/llm/ollama.d.ts +5 -14
  42. package/dist/core/llm/ollama.d.ts.map +1 -1
  43. package/dist/core/llm/ollama.js +3 -0
  44. package/dist/core/llm/ollama.js.map +1 -1
  45. package/dist/core/llm/types.d.ts +22 -0
  46. package/dist/core/llm/types.d.ts.map +1 -0
  47. package/dist/core/llm/types.js +2 -0
  48. package/dist/core/llm/types.js.map +1 -0
  49. package/dist/core/mcp/client.d.ts +6 -0
  50. package/dist/core/mcp/client.d.ts.map +1 -1
  51. package/dist/core/mcp/client.js +16 -0
  52. package/dist/core/mcp/client.js.map +1 -1
  53. package/dist/core/mcp/init.d.ts +12 -0
  54. package/dist/core/mcp/init.d.ts.map +1 -0
  55. package/dist/core/mcp/init.js +55 -0
  56. package/dist/core/mcp/init.js.map +1 -0
  57. package/dist/core/mcp/logger.d.ts +14 -0
  58. package/dist/core/mcp/logger.d.ts.map +1 -0
  59. package/dist/core/mcp/logger.js +50 -0
  60. package/dist/core/mcp/logger.js.map +1 -0
  61. package/dist/core/tools/analyzeAll.d.ts.map +1 -1
  62. package/dist/core/tools/analyzeAll.js +16 -28
  63. package/dist/core/tools/analyzeAll.js.map +1 -1
  64. package/dist/core/tools/analyzePatterns.d.ts +3 -0
  65. package/dist/core/tools/analyzePatterns.d.ts.map +1 -0
  66. package/dist/core/tools/analyzePatterns.js +293 -0
  67. package/dist/core/tools/analyzePatterns.js.map +1 -0
  68. package/dist/core/tools/apexPaths.d.ts +14 -0
  69. package/dist/core/tools/apexPaths.d.ts.map +1 -0
  70. package/dist/core/tools/apexPaths.js +54 -0
  71. package/dist/core/tools/apexPaths.js.map +1 -0
  72. package/dist/core/tools/apexUtils.d.ts +36 -0
  73. package/dist/core/tools/apexUtils.d.ts.map +1 -0
  74. package/dist/core/tools/apexUtils.js +83 -0
  75. package/dist/core/tools/apexUtils.js.map +1 -0
  76. package/dist/core/tools/explainIssue.d.ts +3 -0
  77. package/dist/core/tools/explainIssue.d.ts.map +1 -0
  78. package/dist/core/tools/explainIssue.js +181 -0
  79. package/dist/core/tools/explainIssue.js.map +1 -0
  80. package/dist/core/tools/fixGen.d.ts +3 -0
  81. package/dist/core/tools/fixGen.d.ts.map +1 -0
  82. package/dist/core/tools/fixGen.js +338 -0
  83. package/dist/core/tools/fixGen.js.map +1 -0
  84. package/dist/core/tools/generateImprovements.d.ts +21 -0
  85. package/dist/core/tools/generateImprovements.d.ts.map +1 -0
  86. package/dist/core/tools/generateImprovements.js +602 -0
  87. package/dist/core/tools/generateImprovements.js.map +1 -0
  88. package/dist/core/tools/generateReport.d.ts +3 -0
  89. package/dist/core/tools/generateReport.d.ts.map +1 -0
  90. package/dist/core/tools/generateReport.js +315 -0
  91. package/dist/core/tools/generateReport.js.map +1 -0
  92. package/dist/core/tools/index.d.ts +7 -0
  93. package/dist/core/tools/index.d.ts.map +1 -1
  94. package/dist/core/tools/index.js +62 -23
  95. package/dist/core/tools/index.js.map +1 -1
  96. package/dist/core/tools/javaAst.d.ts.map +1 -1
  97. package/dist/core/tools/javaAst.js +191 -0
  98. package/dist/core/tools/javaAst.js.map +1 -1
  99. package/dist/core/tools/recommendProfile.d.ts +3 -0
  100. package/dist/core/tools/recommendProfile.d.ts.map +1 -0
  101. package/dist/core/tools/recommendProfile.js +334 -0
  102. package/dist/core/tools/recommendProfile.js.map +1 -0
  103. package/dist/core/tools/ruleGen.d.ts +3 -0
  104. package/dist/core/tools/ruleGen.d.ts.map +1 -0
  105. package/dist/core/tools/ruleGen.js +1103 -0
  106. package/dist/core/tools/ruleGen.js.map +1 -0
  107. package/dist/core/tools/standards.d.ts.map +1 -1
  108. package/dist/core/tools/standards.js +7 -3
  109. package/dist/core/tools/standards.js.map +1 -1
  110. package/dist/ui/App.d.ts.map +1 -1
  111. package/dist/ui/App.js +86 -35
  112. package/dist/ui/App.js.map +1 -1
  113. package/dist/ui/components/InputBox.d.ts +1 -3
  114. package/dist/ui/components/InputBox.d.ts.map +1 -1
  115. package/dist/ui/components/InputBox.js +146 -5
  116. package/dist/ui/components/InputBox.js.map +1 -1
  117. package/dist/ui/components/MessageList.d.ts +3 -1
  118. package/dist/ui/components/MessageList.d.ts.map +1 -1
  119. package/dist/ui/components/MessageList.js +13 -7
  120. package/dist/ui/components/MessageList.js.map +1 -1
  121. package/dist/ui/components/StatusBar.d.ts +1 -1
  122. package/dist/ui/components/StatusBar.d.ts.map +1 -1
  123. package/dist/ui/components/StatusBar.js +3 -2
  124. package/dist/ui/components/StatusBar.js.map +1 -1
  125. package/dist/ui/components/ToolStatus.d.ts +3 -1
  126. package/dist/ui/components/ToolStatus.d.ts.map +1 -1
  127. package/dist/ui/components/ToolStatus.js +19 -4
  128. package/dist/ui/components/ToolStatus.js.map +1 -1
  129. package/package.json +7 -1
  130. package/demo.gif +0 -0
  131. package/demo.tape +0 -53
  132. package/screenshot.png +0 -0
  133. package/src/cli/banner.ts +0 -38
  134. package/src/cli/headless.ts +0 -63
  135. package/src/cli/index.ts +0 -57
  136. package/src/core/agent.ts +0 -237
  137. package/src/core/commands.ts +0 -118
  138. package/src/core/config.ts +0 -98
  139. package/src/core/conversation.ts +0 -235
  140. package/src/core/llm/ollama.ts +0 -351
  141. package/src/core/mcp/client.ts +0 -143
  142. package/src/core/tools/analyzeAll.ts +0 -494
  143. package/src/core/tools/ast.ts +0 -826
  144. package/src/core/tools/builtIn.ts +0 -221
  145. package/src/core/tools/cache.ts +0 -570
  146. package/src/core/tools/cssAnalysis.ts +0 -324
  147. package/src/core/tools/dependencyAnalysis.ts +0 -363
  148. package/src/core/tools/embeddings.ts +0 -746
  149. package/src/core/tools/frontendAst.ts +0 -802
  150. package/src/core/tools/htmlAnalysis.ts +0 -466
  151. package/src/core/tools/index.ts +0 -160
  152. package/src/core/tools/javaAst.ts +0 -812
  153. package/src/core/tools/memory.ts +0 -655
  154. package/src/core/tools/mybatisAnalysis.ts +0 -322
  155. package/src/core/tools/openapiAnalysis.ts +0 -431
  156. package/src/core/tools/pythonAnalysis.ts +0 -477
  157. package/src/core/tools/sqlAnalysis.ts +0 -298
  158. package/src/core/tools/standards.test.ts +0 -186
  159. package/src/core/tools/standards.ts +0 -889
  160. package/src/core/tools/types.ts +0 -38
  161. package/src/ui/App.tsx +0 -334
  162. package/src/ui/components/InputBox.tsx +0 -37
  163. package/src/ui/components/MessageList.tsx +0 -80
  164. package/src/ui/components/StatusBar.tsx +0 -36
  165. package/src/ui/components/ToolStatus.tsx +0 -38
  166. package/tsconfig.json +0 -21
@@ -1,655 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { Tool, ToolResult } from "./types.js";
4
-
5
- // Memory directory
6
- const MEMORY_DIR = ".activo/memory";
7
-
8
- // Project context interface
9
- interface ProjectContext {
10
- name: string;
11
- rootPath: string;
12
- description?: string;
13
- techStack: string[];
14
- keyFiles: Array<{
15
- path: string;
16
- description: string;
17
- }>;
18
- conventions: string[];
19
- lastUpdated: string;
20
- }
21
-
22
- // Conversation summary interface
23
- interface ConversationSummary {
24
- id: string;
25
- date: string;
26
- topics: string[];
27
- keyFindings: string[];
28
- decisions: string[];
29
- todos: string[];
30
- }
31
-
32
- // Memory store interface
33
- interface MemoryStore {
34
- version: string;
35
- project: ProjectContext;
36
- conversations: ConversationSummary[];
37
- notes: Array<{
38
- id: string;
39
- title: string;
40
- content: string;
41
- tags: string[];
42
- createdAt: string;
43
- }>;
44
- facts: Array<{
45
- key: string;
46
- value: string;
47
- source: string;
48
- createdAt: string;
49
- }>;
50
- }
51
-
52
- // Get memory directory path
53
- function getMemoryDir(): string {
54
- return path.resolve(process.cwd(), MEMORY_DIR);
55
- }
56
-
57
- // Get memory file path
58
- function getMemoryPath(): string {
59
- return path.join(getMemoryDir(), "store.json");
60
- }
61
-
62
- // Ensure memory directory exists
63
- function ensureMemoryDir(): void {
64
- const dir = getMemoryDir();
65
- if (!fs.existsSync(dir)) {
66
- fs.mkdirSync(dir, { recursive: true });
67
- }
68
- }
69
-
70
- // Load memory store
71
- function loadMemory(): MemoryStore {
72
- const memoryPath = getMemoryPath();
73
- if (fs.existsSync(memoryPath)) {
74
- try {
75
- return JSON.parse(fs.readFileSync(memoryPath, "utf-8"));
76
- } catch {
77
- return createDefaultMemory();
78
- }
79
- }
80
- return createDefaultMemory();
81
- }
82
-
83
- // Create default memory
84
- function createDefaultMemory(): MemoryStore {
85
- return {
86
- version: "1.0",
87
- project: {
88
- name: path.basename(process.cwd()),
89
- rootPath: process.cwd(),
90
- techStack: [],
91
- keyFiles: [],
92
- conventions: [],
93
- lastUpdated: new Date().toISOString(),
94
- },
95
- conversations: [],
96
- notes: [],
97
- facts: [],
98
- };
99
- }
100
-
101
- // Save memory store
102
- function saveMemory(memory: MemoryStore): void {
103
- ensureMemoryDir();
104
- fs.writeFileSync(getMemoryPath(), JSON.stringify(memory, null, 2));
105
- }
106
-
107
- // Generate unique ID
108
- function generateId(): string {
109
- return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
110
- }
111
-
112
- // Initialize/Update Project Context Tool
113
- export const initProjectTool: Tool = {
114
- name: "init_project_memory",
115
- description: "Initialize or update project context in memory (프로젝트 컨텍스트 초기화). Stores project info like tech stack, key files. Use when user asks: 'remember project', 'init memory', '프로젝트 기억', '컨텍스트 저장'.",
116
- parameters: {
117
- type: "object",
118
- properties: {
119
- description: {
120
- type: "string",
121
- description: "Project description",
122
- },
123
- techStack: {
124
- type: "string",
125
- description: "Comma-separated tech stack (e.g., 'TypeScript, React, Node.js')",
126
- },
127
- conventions: {
128
- type: "string",
129
- description: "Comma-separated coding conventions",
130
- },
131
- },
132
- },
133
- handler: async (args): Promise<ToolResult> => {
134
- try {
135
- const memory = loadMemory();
136
-
137
- if (args.description) {
138
- memory.project.description = args.description as string;
139
- }
140
-
141
- if (args.techStack) {
142
- memory.project.techStack = (args.techStack as string)
143
- .split(",")
144
- .map((s) => s.trim())
145
- .filter((s) => s);
146
- }
147
-
148
- if (args.conventions) {
149
- memory.project.conventions = (args.conventions as string)
150
- .split(",")
151
- .map((s) => s.trim())
152
- .filter((s) => s);
153
- }
154
-
155
- memory.project.lastUpdated = new Date().toISOString();
156
- saveMemory(memory);
157
-
158
- const lines: string[] = [];
159
- lines.push("=== 프로젝트 컨텍스트 저장됨 ===");
160
- lines.push("");
161
- lines.push(`📁 프로젝트: ${memory.project.name}`);
162
- if (memory.project.description) {
163
- lines.push(`📝 설명: ${memory.project.description}`);
164
- }
165
- if (memory.project.techStack.length > 0) {
166
- lines.push(`🛠️ 기술 스택: ${memory.project.techStack.join(", ")}`);
167
- }
168
- if (memory.project.conventions.length > 0) {
169
- lines.push(`📋 컨벤션: ${memory.project.conventions.join(", ")}`);
170
- }
171
-
172
- return { success: true, content: lines.join("\n") };
173
- } catch (error) {
174
- return { success: false, content: "", error: String(error) };
175
- }
176
- },
177
- };
178
-
179
- // Add Key File Tool
180
- export const addKeyFileTool: Tool = {
181
- name: "add_key_file",
182
- description: "Mark a file as important with description (중요 파일 등록). Use when user asks: 'remember this file', 'mark as important', '이 파일 기억', '중요 파일'.",
183
- parameters: {
184
- type: "object",
185
- required: ["filepath", "description"],
186
- properties: {
187
- filepath: {
188
- type: "string",
189
- description: "Path to the file",
190
- },
191
- description: {
192
- type: "string",
193
- description: "What this file does / why it's important",
194
- },
195
- },
196
- },
197
- handler: async (args): Promise<ToolResult> => {
198
- try {
199
- const filepath = args.filepath as string;
200
- const description = args.description as string;
201
-
202
- const memory = loadMemory();
203
-
204
- // Remove if already exists
205
- memory.project.keyFiles = memory.project.keyFiles.filter(
206
- (f) => f.path !== filepath
207
- );
208
-
209
- // Add new
210
- memory.project.keyFiles.push({ path: filepath, description });
211
- memory.project.lastUpdated = new Date().toISOString();
212
- saveMemory(memory);
213
-
214
- return {
215
- success: true,
216
- content: `✅ 중요 파일 등록됨: ${filepath}\n 설명: ${description}`,
217
- };
218
- } catch (error) {
219
- return { success: false, content: "", error: String(error) };
220
- }
221
- },
222
- };
223
-
224
- // Add Note Tool
225
- export const addNoteTool: Tool = {
226
- name: "add_note",
227
- description: "Save a note/finding about the codebase (노트 저장). Use when user asks: 'remember this', 'save note', 'note that', '기억해', '메모해', '노트'.",
228
- parameters: {
229
- type: "object",
230
- required: ["title", "content"],
231
- properties: {
232
- title: {
233
- type: "string",
234
- description: "Note title",
235
- },
236
- content: {
237
- type: "string",
238
- description: "Note content",
239
- },
240
- tags: {
241
- type: "string",
242
- description: "Comma-separated tags",
243
- },
244
- },
245
- },
246
- handler: async (args): Promise<ToolResult> => {
247
- try {
248
- const title = args.title as string;
249
- const content = args.content as string;
250
- const tags = args.tags
251
- ? (args.tags as string).split(",").map((t) => t.trim())
252
- : [];
253
-
254
- const memory = loadMemory();
255
- memory.notes.push({
256
- id: generateId(),
257
- title,
258
- content,
259
- tags,
260
- createdAt: new Date().toISOString(),
261
- });
262
- saveMemory(memory);
263
-
264
- return {
265
- success: true,
266
- content: `✅ 노트 저장됨: "${title}"\n 태그: ${tags.length > 0 ? tags.join(", ") : "(없음)"}`,
267
- };
268
- } catch (error) {
269
- return { success: false, content: "", error: String(error) };
270
- }
271
- },
272
- };
273
-
274
- // Add Fact Tool
275
- export const addFactTool: Tool = {
276
- name: "add_fact",
277
- description: "Store a fact about the codebase (사실 저장). Key-value pairs like 'main entry point = src/index.ts'. Use when user asks: 'remember that X is Y', '기억해', 'store fact'.",
278
- parameters: {
279
- type: "object",
280
- required: ["key", "value"],
281
- properties: {
282
- key: {
283
- type: "string",
284
- description: "Fact key/name",
285
- },
286
- value: {
287
- type: "string",
288
- description: "Fact value",
289
- },
290
- source: {
291
- type: "string",
292
- description: "Where this fact came from (file, conversation, etc.)",
293
- },
294
- },
295
- },
296
- handler: async (args): Promise<ToolResult> => {
297
- try {
298
- const key = args.key as string;
299
- const value = args.value as string;
300
- const source = (args.source as string) || "user";
301
-
302
- const memory = loadMemory();
303
-
304
- // Update if exists, otherwise add
305
- const existingIdx = memory.facts.findIndex((f) => f.key === key);
306
- if (existingIdx >= 0) {
307
- memory.facts[existingIdx] = {
308
- key,
309
- value,
310
- source,
311
- createdAt: new Date().toISOString(),
312
- };
313
- } else {
314
- memory.facts.push({
315
- key,
316
- value,
317
- source,
318
- createdAt: new Date().toISOString(),
319
- });
320
- }
321
- saveMemory(memory);
322
-
323
- return {
324
- success: true,
325
- content: `✅ 사실 저장됨: ${key} = ${value}`,
326
- };
327
- } catch (error) {
328
- return { success: false, content: "", error: String(error) };
329
- }
330
- },
331
- };
332
-
333
- // Save Conversation Summary Tool
334
- export const saveConversationTool: Tool = {
335
- name: "save_conversation",
336
- description: "Save a summary of the current conversation (대화 요약 저장). Use at end of session or when switching topics. Use when user asks: 'save session', 'remember conversation', '대화 저장', '세션 저장'.",
337
- parameters: {
338
- type: "object",
339
- required: ["topics", "keyFindings"],
340
- properties: {
341
- topics: {
342
- type: "string",
343
- description: "Comma-separated topics discussed",
344
- },
345
- keyFindings: {
346
- type: "string",
347
- description: "Comma-separated key findings or insights",
348
- },
349
- decisions: {
350
- type: "string",
351
- description: "Comma-separated decisions made",
352
- },
353
- todos: {
354
- type: "string",
355
- description: "Comma-separated todo items",
356
- },
357
- },
358
- },
359
- handler: async (args): Promise<ToolResult> => {
360
- try {
361
- const topics = (args.topics as string).split(",").map((t) => t.trim());
362
- const keyFindings = (args.keyFindings as string).split(",").map((t) => t.trim());
363
- const decisions = args.decisions
364
- ? (args.decisions as string).split(",").map((t) => t.trim())
365
- : [];
366
- const todos = args.todos
367
- ? (args.todos as string).split(",").map((t) => t.trim())
368
- : [];
369
-
370
- const memory = loadMemory();
371
- memory.conversations.push({
372
- id: generateId(),
373
- date: new Date().toISOString(),
374
- topics,
375
- keyFindings,
376
- decisions,
377
- todos,
378
- });
379
-
380
- // Keep only last 20 conversations
381
- if (memory.conversations.length > 20) {
382
- memory.conversations = memory.conversations.slice(-20);
383
- }
384
-
385
- saveMemory(memory);
386
-
387
- return {
388
- success: true,
389
- content: `✅ 대화 요약 저장됨\n 주제: ${topics.join(", ")}\n 발견: ${keyFindings.length}개\n 결정: ${decisions.length}개\n TODO: ${todos.length}개`,
390
- };
391
- } catch (error) {
392
- return { success: false, content: "", error: String(error) };
393
- }
394
- },
395
- };
396
-
397
- // Get Project Context Tool
398
- export const getContextTool: Tool = {
399
- name: "get_project_context",
400
- description: "Get stored project context and memory (프로젝트 컨텍스트 조회). Returns project info, key files, notes, facts. Use when starting session or user asks: 'what do you remember', 'project context', '기억하는거', '컨텍스트'.",
401
- parameters: {
402
- type: "object",
403
- properties: {
404
- section: {
405
- type: "string",
406
- description: "Specific section: 'project', 'files', 'notes', 'facts', 'conversations', or 'all'",
407
- enum: ["project", "files", "notes", "facts", "conversations", "all"],
408
- },
409
- },
410
- },
411
- handler: async (args): Promise<ToolResult> => {
412
- try {
413
- const section = (args.section as string) || "all";
414
- const memory = loadMemory();
415
-
416
- const lines: string[] = [];
417
-
418
- if (section === "all" || section === "project") {
419
- lines.push("=== 프로젝트 컨텍스트 ===");
420
- lines.push(`📁 이름: ${memory.project.name}`);
421
- if (memory.project.description) {
422
- lines.push(`📝 설명: ${memory.project.description}`);
423
- }
424
- if (memory.project.techStack.length > 0) {
425
- lines.push(`🛠️ 기술: ${memory.project.techStack.join(", ")}`);
426
- }
427
- if (memory.project.conventions.length > 0) {
428
- lines.push(`📋 컨벤션: ${memory.project.conventions.join(", ")}`);
429
- }
430
- lines.push(`🕐 갱신: ${memory.project.lastUpdated.slice(0, 10)}`);
431
- lines.push("");
432
- }
433
-
434
- if (section === "all" || section === "files") {
435
- if (memory.project.keyFiles.length > 0) {
436
- lines.push("=== 중요 파일 ===");
437
- for (const file of memory.project.keyFiles) {
438
- lines.push(`📄 ${file.path}`);
439
- lines.push(` ${file.description}`);
440
- }
441
- lines.push("");
442
- }
443
- }
444
-
445
- if (section === "all" || section === "notes") {
446
- if (memory.notes.length > 0) {
447
- lines.push("=== 노트 ===");
448
- for (const note of memory.notes.slice(-5)) {
449
- lines.push(`📌 ${note.title}`);
450
- lines.push(` ${note.content.slice(0, 100)}${note.content.length > 100 ? "..." : ""}`);
451
- if (note.tags.length > 0) {
452
- lines.push(` 태그: ${note.tags.join(", ")}`);
453
- }
454
- }
455
- if (memory.notes.length > 5) {
456
- lines.push(` ... 외 ${memory.notes.length - 5}개`);
457
- }
458
- lines.push("");
459
- }
460
- }
461
-
462
- if (section === "all" || section === "facts") {
463
- if (memory.facts.length > 0) {
464
- lines.push("=== 저장된 사실 ===");
465
- for (const fact of memory.facts) {
466
- lines.push(`💡 ${fact.key}: ${fact.value}`);
467
- }
468
- lines.push("");
469
- }
470
- }
471
-
472
- if (section === "all" || section === "conversations") {
473
- if (memory.conversations.length > 0) {
474
- lines.push("=== 최근 대화 ===");
475
- for (const conv of memory.conversations.slice(-3)) {
476
- lines.push(`📅 ${conv.date.slice(0, 10)}`);
477
- lines.push(` 주제: ${conv.topics.join(", ")}`);
478
- if (conv.todos.length > 0) {
479
- lines.push(` TODO: ${conv.todos.join(", ")}`);
480
- }
481
- }
482
- lines.push("");
483
- }
484
- }
485
-
486
- if (lines.length === 0) {
487
- return { success: true, content: "저장된 메모리가 없습니다." };
488
- }
489
-
490
- return { success: true, content: lines.join("\n") };
491
- } catch (error) {
492
- return { success: false, content: "", error: String(error) };
493
- }
494
- },
495
- };
496
-
497
- // Search Memory Tool
498
- export const searchMemoryTool: Tool = {
499
- name: "search_memory",
500
- description: "Search through stored notes and facts (메모리 검색). Use when user asks: 'find in memory', 'what did we say about', '기억에서 찾아', '메모리 검색'.",
501
- parameters: {
502
- type: "object",
503
- required: ["query"],
504
- properties: {
505
- query: {
506
- type: "string",
507
- description: "Search query",
508
- },
509
- },
510
- },
511
- handler: async (args): Promise<ToolResult> => {
512
- try {
513
- const query = (args.query as string).toLowerCase();
514
- const memory = loadMemory();
515
-
516
- const results: string[] = [];
517
- results.push(`=== 메모리 검색: "${args.query}" ===`);
518
- results.push("");
519
-
520
- // Search notes
521
- const matchingNotes = memory.notes.filter(
522
- (n) =>
523
- n.title.toLowerCase().includes(query) ||
524
- n.content.toLowerCase().includes(query) ||
525
- n.tags.some((t) => t.toLowerCase().includes(query))
526
- );
527
- if (matchingNotes.length > 0) {
528
- results.push("📌 노트:");
529
- for (const note of matchingNotes) {
530
- results.push(` ${note.title}`);
531
- results.push(` ${note.content.slice(0, 80)}...`);
532
- }
533
- results.push("");
534
- }
535
-
536
- // Search facts
537
- const matchingFacts = memory.facts.filter(
538
- (f) =>
539
- f.key.toLowerCase().includes(query) ||
540
- f.value.toLowerCase().includes(query)
541
- );
542
- if (matchingFacts.length > 0) {
543
- results.push("💡 사실:");
544
- for (const fact of matchingFacts) {
545
- results.push(` ${fact.key}: ${fact.value}`);
546
- }
547
- results.push("");
548
- }
549
-
550
- // Search key files
551
- const matchingFiles = memory.project.keyFiles.filter(
552
- (f) =>
553
- f.path.toLowerCase().includes(query) ||
554
- f.description.toLowerCase().includes(query)
555
- );
556
- if (matchingFiles.length > 0) {
557
- results.push("📄 중요 파일:");
558
- for (const file of matchingFiles) {
559
- results.push(` ${file.path}: ${file.description}`);
560
- }
561
- results.push("");
562
- }
563
-
564
- // Search conversations
565
- const matchingConvs = memory.conversations.filter(
566
- (c) =>
567
- c.topics.some((t) => t.toLowerCase().includes(query)) ||
568
- c.keyFindings.some((f) => f.toLowerCase().includes(query))
569
- );
570
- if (matchingConvs.length > 0) {
571
- results.push("📅 대화:");
572
- for (const conv of matchingConvs) {
573
- results.push(` ${conv.date.slice(0, 10)}: ${conv.topics.join(", ")}`);
574
- }
575
- results.push("");
576
- }
577
-
578
- if (results.length === 3) {
579
- // Only header, no results
580
- return { success: true, content: `"${args.query}"에 대한 검색 결과가 없습니다.` };
581
- }
582
-
583
- return { success: true, content: results.join("\n") };
584
- } catch (error) {
585
- return { success: false, content: "", error: String(error) };
586
- }
587
- },
588
- };
589
-
590
- // Clear Memory Tool
591
- export const clearMemoryTool: Tool = {
592
- name: "clear_memory",
593
- description: "Clear stored memory (메모리 삭제). Use when user asks: 'forget everything', 'clear memory', '메모리 삭제', '기억 삭제'.",
594
- parameters: {
595
- type: "object",
596
- properties: {
597
- section: {
598
- type: "string",
599
- description: "Section to clear: 'notes', 'facts', 'conversations', or 'all'",
600
- enum: ["notes", "facts", "conversations", "all"],
601
- },
602
- },
603
- },
604
- handler: async (args): Promise<ToolResult> => {
605
- try {
606
- const section = (args.section as string) || "all";
607
- const memory = loadMemory();
608
-
609
- if (section === "all") {
610
- const dir = getMemoryDir();
611
- if (fs.existsSync(dir)) {
612
- fs.rmSync(dir, { recursive: true });
613
- }
614
- return { success: true, content: "✅ 전체 메모리가 삭제되었습니다." };
615
- }
616
-
617
- if (section === "notes") {
618
- const count = memory.notes.length;
619
- memory.notes = [];
620
- saveMemory(memory);
621
- return { success: true, content: `✅ ${count}개 노트가 삭제되었습니다.` };
622
- }
623
-
624
- if (section === "facts") {
625
- const count = memory.facts.length;
626
- memory.facts = [];
627
- saveMemory(memory);
628
- return { success: true, content: `✅ ${count}개 사실이 삭제되었습니다.` };
629
- }
630
-
631
- if (section === "conversations") {
632
- const count = memory.conversations.length;
633
- memory.conversations = [];
634
- saveMemory(memory);
635
- return { success: true, content: `✅ ${count}개 대화 기록이 삭제되었습니다.` };
636
- }
637
-
638
- return { success: false, content: "", error: `알 수 없는 섹션: ${section}` };
639
- } catch (error) {
640
- return { success: false, content: "", error: String(error) };
641
- }
642
- },
643
- };
644
-
645
- // Export all memory tools
646
- export const memoryTools: Tool[] = [
647
- initProjectTool,
648
- addKeyFileTool,
649
- addNoteTool,
650
- addFactTool,
651
- saveConversationTool,
652
- getContextTool,
653
- searchMemoryTool,
654
- clearMemoryTool,
655
- ];