wave-agent-sdk 0.0.1

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 (170) hide show
  1. package/README.md +32 -0
  2. package/dist/agent.d.ts +96 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +286 -0
  5. package/dist/hooks/executor.d.ts +56 -0
  6. package/dist/hooks/executor.d.ts.map +1 -0
  7. package/dist/hooks/executor.js +312 -0
  8. package/dist/hooks/index.d.ts +17 -0
  9. package/dist/hooks/index.d.ts.map +1 -0
  10. package/dist/hooks/index.js +14 -0
  11. package/dist/hooks/manager.d.ts +90 -0
  12. package/dist/hooks/manager.d.ts.map +1 -0
  13. package/dist/hooks/manager.js +395 -0
  14. package/dist/hooks/matcher.d.ts +49 -0
  15. package/dist/hooks/matcher.d.ts.map +1 -0
  16. package/dist/hooks/matcher.js +147 -0
  17. package/dist/hooks/settings.d.ts +46 -0
  18. package/dist/hooks/settings.d.ts.map +1 -0
  19. package/dist/hooks/settings.js +100 -0
  20. package/dist/hooks/types.d.ts +80 -0
  21. package/dist/hooks/types.d.ts.map +1 -0
  22. package/dist/hooks/types.js +59 -0
  23. package/dist/index.d.ts +16 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +20 -0
  26. package/dist/managers/aiManager.d.ts +61 -0
  27. package/dist/managers/aiManager.d.ts.map +1 -0
  28. package/dist/managers/aiManager.js +415 -0
  29. package/dist/managers/backgroundBashManager.d.ts +27 -0
  30. package/dist/managers/backgroundBashManager.d.ts.map +1 -0
  31. package/dist/managers/backgroundBashManager.js +166 -0
  32. package/dist/managers/bashManager.d.ts +20 -0
  33. package/dist/managers/bashManager.d.ts.map +1 -0
  34. package/dist/managers/bashManager.js +66 -0
  35. package/dist/managers/mcpManager.d.ts +63 -0
  36. package/dist/managers/mcpManager.d.ts.map +1 -0
  37. package/dist/managers/mcpManager.js +378 -0
  38. package/dist/managers/messageManager.d.ts +85 -0
  39. package/dist/managers/messageManager.d.ts.map +1 -0
  40. package/dist/managers/messageManager.js +265 -0
  41. package/dist/managers/skillManager.d.ts +59 -0
  42. package/dist/managers/skillManager.d.ts.map +1 -0
  43. package/dist/managers/skillManager.js +317 -0
  44. package/dist/managers/slashCommandManager.d.ts +77 -0
  45. package/dist/managers/slashCommandManager.d.ts.map +1 -0
  46. package/dist/managers/slashCommandManager.js +208 -0
  47. package/dist/managers/toolManager.d.ts +23 -0
  48. package/dist/managers/toolManager.d.ts.map +1 -0
  49. package/dist/managers/toolManager.js +79 -0
  50. package/dist/services/aiService.d.ts +28 -0
  51. package/dist/services/aiService.d.ts.map +1 -0
  52. package/dist/services/aiService.js +180 -0
  53. package/dist/services/memory.d.ts +8 -0
  54. package/dist/services/memory.d.ts.map +1 -0
  55. package/dist/services/memory.js +128 -0
  56. package/dist/services/session.d.ts +54 -0
  57. package/dist/services/session.d.ts.map +1 -0
  58. package/dist/services/session.js +196 -0
  59. package/dist/tools/bashTool.d.ts +14 -0
  60. package/dist/tools/bashTool.d.ts.map +1 -0
  61. package/dist/tools/bashTool.js +351 -0
  62. package/dist/tools/deleteFileTool.d.ts +6 -0
  63. package/dist/tools/deleteFileTool.d.ts.map +1 -0
  64. package/dist/tools/deleteFileTool.js +67 -0
  65. package/dist/tools/editTool.d.ts +6 -0
  66. package/dist/tools/editTool.d.ts.map +1 -0
  67. package/dist/tools/editTool.js +168 -0
  68. package/dist/tools/globTool.d.ts +6 -0
  69. package/dist/tools/globTool.d.ts.map +1 -0
  70. package/dist/tools/globTool.js +113 -0
  71. package/dist/tools/grepTool.d.ts +6 -0
  72. package/dist/tools/grepTool.d.ts.map +1 -0
  73. package/dist/tools/grepTool.js +268 -0
  74. package/dist/tools/lsTool.d.ts +6 -0
  75. package/dist/tools/lsTool.d.ts.map +1 -0
  76. package/dist/tools/lsTool.js +160 -0
  77. package/dist/tools/multiEditTool.d.ts +6 -0
  78. package/dist/tools/multiEditTool.d.ts.map +1 -0
  79. package/dist/tools/multiEditTool.js +222 -0
  80. package/dist/tools/readTool.d.ts +6 -0
  81. package/dist/tools/readTool.d.ts.map +1 -0
  82. package/dist/tools/readTool.js +136 -0
  83. package/dist/tools/types.d.ts +35 -0
  84. package/dist/tools/types.d.ts.map +1 -0
  85. package/dist/tools/types.js +4 -0
  86. package/dist/tools/writeTool.d.ts +6 -0
  87. package/dist/tools/writeTool.d.ts.map +1 -0
  88. package/dist/tools/writeTool.js +138 -0
  89. package/dist/types.d.ts +212 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +13 -0
  92. package/dist/utils/bashHistory.d.ts +46 -0
  93. package/dist/utils/bashHistory.d.ts.map +1 -0
  94. package/dist/utils/bashHistory.js +236 -0
  95. package/dist/utils/commandArgumentParser.d.ts +34 -0
  96. package/dist/utils/commandArgumentParser.d.ts.map +1 -0
  97. package/dist/utils/commandArgumentParser.js +123 -0
  98. package/dist/utils/constants.d.ts +27 -0
  99. package/dist/utils/constants.d.ts.map +1 -0
  100. package/dist/utils/constants.js +28 -0
  101. package/dist/utils/convertMessagesForAPI.d.ts +9 -0
  102. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -0
  103. package/dist/utils/convertMessagesForAPI.js +189 -0
  104. package/dist/utils/customCommands.d.ts +14 -0
  105. package/dist/utils/customCommands.d.ts.map +1 -0
  106. package/dist/utils/customCommands.js +71 -0
  107. package/dist/utils/fileFilter.d.ts +26 -0
  108. package/dist/utils/fileFilter.d.ts.map +1 -0
  109. package/dist/utils/fileFilter.js +177 -0
  110. package/dist/utils/markdownParser.d.ts +27 -0
  111. package/dist/utils/markdownParser.d.ts.map +1 -0
  112. package/dist/utils/markdownParser.js +109 -0
  113. package/dist/utils/mcpUtils.d.ts +24 -0
  114. package/dist/utils/mcpUtils.d.ts.map +1 -0
  115. package/dist/utils/mcpUtils.js +51 -0
  116. package/dist/utils/messageOperations.d.ts +118 -0
  117. package/dist/utils/messageOperations.d.ts.map +1 -0
  118. package/dist/utils/messageOperations.js +334 -0
  119. package/dist/utils/path.d.ts +25 -0
  120. package/dist/utils/path.d.ts.map +1 -0
  121. package/dist/utils/path.js +109 -0
  122. package/dist/utils/skillParser.d.ts +18 -0
  123. package/dist/utils/skillParser.d.ts.map +1 -0
  124. package/dist/utils/skillParser.js +147 -0
  125. package/dist/utils/stringUtils.d.ts +13 -0
  126. package/dist/utils/stringUtils.d.ts.map +1 -0
  127. package/dist/utils/stringUtils.js +44 -0
  128. package/package.json +51 -0
  129. package/src/agent.ts +405 -0
  130. package/src/hooks/executor.ts +440 -0
  131. package/src/hooks/index.ts +52 -0
  132. package/src/hooks/manager.ts +618 -0
  133. package/src/hooks/matcher.ts +187 -0
  134. package/src/hooks/settings.ts +129 -0
  135. package/src/hooks/types.ts +169 -0
  136. package/src/index.ts +24 -0
  137. package/src/managers/aiManager.ts +573 -0
  138. package/src/managers/backgroundBashManager.ts +203 -0
  139. package/src/managers/bashManager.ts +97 -0
  140. package/src/managers/mcpManager.ts +493 -0
  141. package/src/managers/messageManager.ts +415 -0
  142. package/src/managers/skillManager.ts +404 -0
  143. package/src/managers/slashCommandManager.ts +293 -0
  144. package/src/managers/toolManager.ts +106 -0
  145. package/src/services/aiService.ts +252 -0
  146. package/src/services/memory.ts +149 -0
  147. package/src/services/session.ts +265 -0
  148. package/src/tools/bashTool.ts +402 -0
  149. package/src/tools/deleteFileTool.ts +81 -0
  150. package/src/tools/editTool.ts +192 -0
  151. package/src/tools/globTool.ts +135 -0
  152. package/src/tools/grepTool.ts +326 -0
  153. package/src/tools/lsTool.ts +187 -0
  154. package/src/tools/multiEditTool.ts +268 -0
  155. package/src/tools/readTool.ts +165 -0
  156. package/src/tools/types.ts +47 -0
  157. package/src/tools/writeTool.ts +163 -0
  158. package/src/types.ts +260 -0
  159. package/src/utils/bashHistory.ts +303 -0
  160. package/src/utils/commandArgumentParser.ts +153 -0
  161. package/src/utils/constants.ts +37 -0
  162. package/src/utils/convertMessagesForAPI.ts +236 -0
  163. package/src/utils/customCommands.ts +85 -0
  164. package/src/utils/fileFilter.ts +202 -0
  165. package/src/utils/markdownParser.ts +156 -0
  166. package/src/utils/mcpUtils.ts +81 -0
  167. package/src/utils/messageOperations.ts +506 -0
  168. package/src/utils/path.ts +118 -0
  169. package/src/utils/skillParser.ts +188 -0
  170. package/src/utils/stringUtils.ts +50 -0
@@ -0,0 +1,106 @@
1
+ import type { ToolContext, ToolPlugin, ToolResult } from "../tools/types.js";
2
+ import { bashTool, bashOutputTool, killBashTool } from "../tools/bashTool.js";
3
+ import { deleteFileTool } from "../tools/deleteFileTool.js";
4
+ import { editTool } from "../tools/editTool.js";
5
+ import { multiEditTool } from "../tools/multiEditTool.js";
6
+ import { writeTool } from "../tools/writeTool.js";
7
+ // New tools
8
+ import { globTool } from "../tools/globTool.js";
9
+ import { grepTool } from "../tools/grepTool.js";
10
+ import { lsTool } from "../tools/lsTool.js";
11
+ import { readTool } from "../tools/readTool.js";
12
+ import { SkillManager } from "./skillManager.js";
13
+ import { McpManager } from "./mcpManager.js";
14
+ import { ChatCompletionFunctionTool } from "openai/resources.js";
15
+ import type { Logger } from "../types.js";
16
+
17
+ export interface ToolManagerOptions {
18
+ mcpManager: McpManager;
19
+ logger?: Logger;
20
+ }
21
+
22
+ /**
23
+ * Tool Manager
24
+ */
25
+ class ToolManager {
26
+ private tools = new Map<string, ToolPlugin>();
27
+ private mcpManager: McpManager;
28
+ private logger?: Logger;
29
+
30
+ constructor(options: ToolManagerOptions) {
31
+ this.mcpManager = options.mcpManager;
32
+ this.logger = options.logger;
33
+
34
+ // Initialize built-in tools
35
+ this.initializeBuiltInTools();
36
+ }
37
+
38
+ private initializeBuiltInTools(): void {
39
+ const builtInTools = [
40
+ bashTool,
41
+ bashOutputTool,
42
+ killBashTool,
43
+ deleteFileTool,
44
+ editTool,
45
+ multiEditTool,
46
+ writeTool,
47
+ globTool,
48
+ grepTool,
49
+ lsTool,
50
+ readTool,
51
+ new SkillManager({ logger: this.logger }).createTool(),
52
+ ];
53
+
54
+ for (const tool of builtInTools) {
55
+ this.tools.set(tool.name, tool);
56
+ }
57
+ }
58
+
59
+ async execute(
60
+ name: string,
61
+ args: Record<string, unknown>,
62
+ context: ToolContext,
63
+ ): Promise<ToolResult> {
64
+ // Check if it's an MCP tool first
65
+ if (this.mcpManager.isMcpTool(name)) {
66
+ return this.mcpManager.executeMcpToolByRegistry(name, args, context);
67
+ }
68
+
69
+ // Check built-in tools
70
+ const plugin = this.tools.get(name);
71
+ if (plugin) {
72
+ try {
73
+ return await plugin.execute(args, context);
74
+ } catch (error) {
75
+ return {
76
+ success: false,
77
+ content: "",
78
+ error: error instanceof Error ? error.message : String(error),
79
+ };
80
+ }
81
+ }
82
+
83
+ return {
84
+ success: false,
85
+ content: "",
86
+ error: `Tool '${name}' not found`,
87
+ };
88
+ }
89
+
90
+ list(): ToolPlugin[] {
91
+ const builtInTools = Array.from(this.tools.values());
92
+ const mcpTools = this.mcpManager.getMcpToolPlugins();
93
+ return [...builtInTools, ...mcpTools];
94
+ }
95
+
96
+ getToolsConfig(): ChatCompletionFunctionTool[] {
97
+ const builtInToolsConfig = Array.from(this.tools.values()).map(
98
+ (tool) => tool.config,
99
+ );
100
+ const mcpToolsConfig = this.mcpManager.getMcpToolsConfig();
101
+ return [...builtInToolsConfig, ...mcpToolsConfig];
102
+ }
103
+ }
104
+
105
+ // Export tool registry class and types
106
+ export { ToolManager };
@@ -0,0 +1,252 @@
1
+ import OpenAI from "openai";
2
+ import { ChatCompletionMessageToolCall } from "openai/resources";
3
+ import {
4
+ ChatCompletionCreateParamsNonStreaming,
5
+ ChatCompletionMessageParam,
6
+ ChatCompletionFunctionTool,
7
+ } from "openai/resources.js";
8
+ import { FAST_MODEL_ID, AGENT_MODEL_ID } from "@/utils/constants.js";
9
+
10
+ /**
11
+ * Model configuration type, based on OpenAI parameters but excluding messages
12
+ */
13
+ type ModelConfig = Omit<ChatCompletionCreateParamsNonStreaming, "messages">;
14
+
15
+ /**
16
+ * Get specific configuration parameters based on model name
17
+ * @param modelName Model name
18
+ * @param baseConfig Base configuration
19
+ * @returns Configured model parameters
20
+ */
21
+ function getModelConfig(
22
+ modelName: string,
23
+ baseConfig: Partial<ModelConfig> = {},
24
+ ): ModelConfig {
25
+ const config: ModelConfig = {
26
+ model: modelName,
27
+ stream: false,
28
+ ...baseConfig,
29
+ };
30
+
31
+ // Configuration rules for specific models
32
+ if (modelName.includes("gpt-5-codex")) {
33
+ // gpt-5-codex model sets temperature to undefined
34
+ config.temperature = undefined;
35
+ }
36
+
37
+ return config;
38
+ }
39
+
40
+ // Initialize OpenAI client with environment variables
41
+ const openai = new OpenAI({
42
+ apiKey: process.env.AIGW_TOKEN,
43
+ baseURL: process.env.AIGW_URL,
44
+ });
45
+
46
+ export interface CallAgentOptions {
47
+ messages: ChatCompletionMessageParam[];
48
+ sessionId?: string;
49
+ abortSignal?: AbortSignal;
50
+ memory?: string; // Memory content parameter, content read from WAVE.md
51
+ workdir: string; // Current working directory
52
+ tools?: ChatCompletionFunctionTool[]; // Tool configuration
53
+ model?: string; // Custom model
54
+ systemPrompt?: string; // Custom system prompt
55
+ }
56
+
57
+ export interface CallAgentResult {
58
+ content?: string;
59
+ tool_calls?: ChatCompletionMessageToolCall[];
60
+ usage?: {
61
+ prompt_tokens: number;
62
+ completion_tokens: number;
63
+ total_tokens: number;
64
+ };
65
+ }
66
+
67
+ export async function callAgent(
68
+ options: CallAgentOptions,
69
+ ): Promise<CallAgentResult> {
70
+ const { messages, abortSignal, memory, workdir, tools, model, systemPrompt } =
71
+ options;
72
+
73
+ try {
74
+ // Build system prompt content
75
+ let systemContent: string;
76
+
77
+ if (systemPrompt) {
78
+ // Use custom system prompt if provided
79
+ systemContent = systemPrompt;
80
+ } else {
81
+ // Use default system prompt
82
+ systemContent = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
83
+
84
+ ## Current Working Directory
85
+ ${workdir}
86
+ `;
87
+
88
+ // If there is memory content, add it to the system prompt
89
+ if (memory && memory.trim()) {
90
+ systemContent += `\n\n## Memory Context\n\nThe following is important context and memory from previous interactions:\n\n${memory}`;
91
+ }
92
+ }
93
+
94
+ // Add system prompt
95
+ const systemMessage: OpenAI.Chat.Completions.ChatCompletionMessageParam = {
96
+ role: "system",
97
+ content: systemContent,
98
+ };
99
+
100
+ // ChatCompletionMessageParam[] is already in OpenAI format, add system prompt to the beginning
101
+ const openaiMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] =
102
+ [systemMessage, ...messages];
103
+
104
+ // Get model configuration
105
+ const modelConfig = getModelConfig(model || AGENT_MODEL_ID, {
106
+ temperature: 0,
107
+ max_completion_tokens: 32768,
108
+ });
109
+
110
+ // Prepare API call parameters
111
+ const createParams: ChatCompletionCreateParamsNonStreaming = {
112
+ ...modelConfig,
113
+ messages: openaiMessages,
114
+ };
115
+
116
+ // Only add tools if they exist
117
+ if (tools && tools.length > 0) {
118
+ createParams.tools = tools;
119
+ }
120
+
121
+ // Call OpenAI API (non-streaming)
122
+ const response = await openai.chat.completions.create(createParams, {
123
+ signal: abortSignal,
124
+ });
125
+
126
+ const finalMessage = response.choices[0]?.message;
127
+ const totalUsage = response.usage
128
+ ? {
129
+ prompt_tokens: response.usage.prompt_tokens,
130
+ completion_tokens: response.usage.completion_tokens,
131
+ total_tokens: response.usage.total_tokens,
132
+ }
133
+ : undefined;
134
+
135
+ const result: CallAgentResult = {};
136
+
137
+ // Return content
138
+ if (finalMessage?.content) {
139
+ result.content = finalMessage.content;
140
+ }
141
+
142
+ // Return tool call
143
+ if (finalMessage?.tool_calls && finalMessage.tool_calls.length > 0) {
144
+ result.tool_calls = finalMessage.tool_calls;
145
+ }
146
+
147
+ // Return token usage information
148
+ if (totalUsage) {
149
+ result.usage = totalUsage;
150
+ }
151
+
152
+ return result;
153
+ } catch (error) {
154
+ if ((error as Error).name === "AbortError") {
155
+ throw new Error("Request was aborted");
156
+ }
157
+ // // logger.error("Failed to call OpenAI:", error);
158
+ throw error;
159
+ }
160
+ }
161
+
162
+ export interface CompressMessagesOptions {
163
+ messages: ChatCompletionMessageParam[];
164
+ abortSignal?: AbortSignal;
165
+ }
166
+
167
+ export async function compressMessages(
168
+ options: CompressMessagesOptions,
169
+ ): Promise<string> {
170
+ const { messages, abortSignal } = options;
171
+
172
+ // Get model configuration
173
+ const modelConfig = getModelConfig(FAST_MODEL_ID, {
174
+ temperature: 0.1,
175
+ max_tokens: 1500,
176
+ });
177
+
178
+ try {
179
+ const response = await openai.chat.completions.create(
180
+ {
181
+ ...modelConfig,
182
+ messages: [
183
+ {
184
+ role: "system",
185
+ content: `You are an expert conversation history compression specialist. Your task is to create comprehensive yet concise summaries that preserve critical development context.
186
+
187
+ ## Primary Request and Intent
188
+ Compress conversation history while maintaining all essential technical and procedural information.
189
+
190
+ ## Key Technical Concepts
191
+ - Code modifications and file operations
192
+ - Tool executions and their results
193
+ - Error handling and debugging processes
194
+ - User requirements and assistant solutions
195
+ - Technical discussions and decisions
196
+
197
+ ## Compression Strategy
198
+ 1. **Preserve Critical Information**:
199
+ - All file paths, function names, and code examples
200
+ - Tool execution results and outcomes
201
+ - Error messages and resolution steps
202
+ - User requirements and implementation approaches
203
+ - Technical decisions and their reasoning
204
+
205
+ 2. **Structure Organization**:
206
+ - Group related actions and discussions
207
+ - Maintain chronological flow for complex operations
208
+ - Separate different technical topics clearly
209
+
210
+ 3. **Context Preservation**:
211
+ - Keep enough detail for future reference
212
+ - Maintain relationships between requests and solutions
213
+ - Preserve debugging context and error resolution paths
214
+
215
+ ## Output Requirements:
216
+ - Use third-person narrative format
217
+ - Target 300-800 words (scale based on complexity)
218
+ - Maintain the original conversation language
219
+ - Structure with clear sections for multi-topic conversations
220
+ - Focus on actionable information and outcomes
221
+
222
+ ## Format Template:
223
+ For technical conversations, structure as:
224
+ - **User Requests**: Key requirements and goals
225
+ - **Technical Implementation**: Code changes, file operations, tool usage
226
+ - **Problem Resolution**: Errors encountered and solutions applied
227
+ - **Outcomes**: Final results and current state`,
228
+ },
229
+ ...messages,
230
+ {
231
+ role: "user",
232
+ content: `Please compress this conversation following the structured approach. Focus on preserving all technical details, file operations, and problem-solving context while creating a concise summary.`,
233
+ },
234
+ ],
235
+ },
236
+ {
237
+ signal: abortSignal,
238
+ },
239
+ );
240
+
241
+ return (
242
+ response.choices[0]?.message?.content?.trim() ||
243
+ "Failed to compress conversation history"
244
+ );
245
+ } catch (error) {
246
+ if ((error as Error).name === "AbortError") {
247
+ throw new Error("Compression request was aborted");
248
+ }
249
+ // // logger.error("Failed to compress messages:", error);
250
+ return "Failed to compress conversation history";
251
+ }
252
+ }
@@ -0,0 +1,149 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import { USER_MEMORY_FILE, DATA_DIRECTORY } from "../utils/constants.js";
4
+
5
+ // Project memory related methods
6
+ export const isMemoryMessage = (message: string): boolean => {
7
+ return message.trim().startsWith("#");
8
+ };
9
+
10
+ export const addMemory = async (
11
+ message: string,
12
+ workdir: string,
13
+ ): Promise<void> => {
14
+ if (!isMemoryMessage(message)) {
15
+ return;
16
+ }
17
+
18
+ try {
19
+ const memoryFilePath = path.join(workdir, "WAVE.md");
20
+
21
+ // Format memory entry, starting with -, no timestamp
22
+ const memoryEntry = `- ${message.substring(1).trim()}\n`;
23
+
24
+ // Check if file exists
25
+ let existingContent = "";
26
+ try {
27
+ existingContent = await fs.readFile(memoryFilePath, "utf-8");
28
+ } catch (error) {
29
+ // File does not exist, create new file
30
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
31
+ existingContent =
32
+ "# Memory\n\nThis is the AI assistant's memory file, recording important information and context.\n\n";
33
+ } else {
34
+ throw error;
35
+ }
36
+ }
37
+
38
+ // Append new memory entry to the end of the file
39
+ const updatedContent = existingContent + memoryEntry;
40
+
41
+ // Write file
42
+ await fs.writeFile(memoryFilePath, updatedContent, "utf-8");
43
+
44
+ // logger.info(`Memory added to ${memoryFilePath}:`, message);
45
+ } catch (error) {
46
+ // logger.error("Failed to add memory:", error);
47
+ throw new Error(`Failed to add memory: ${(error as Error).message}`);
48
+ }
49
+ };
50
+
51
+ // User memory related methods
52
+ export const ensureUserMemoryFile = async (): Promise<void> => {
53
+ try {
54
+ // Ensure data directory exists
55
+ await fs.mkdir(DATA_DIRECTORY, { recursive: true });
56
+
57
+ // Check if user memory file exists
58
+ try {
59
+ await fs.access(USER_MEMORY_FILE);
60
+ } catch (error) {
61
+ // File does not exist, create new file
62
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
63
+ const initialContent =
64
+ "# User Memory\n\nThis is the user-level memory file, recording important information and context across projects.\n\n";
65
+ await fs.writeFile(USER_MEMORY_FILE, initialContent, "utf-8");
66
+ // logger.info(`Created user memory file: ${USER_MEMORY_FILE}`);
67
+ } else {
68
+ throw error;
69
+ }
70
+ }
71
+ } catch (error) {
72
+ // logger.error("Failed to ensure user memory file:", error);
73
+ throw new Error(
74
+ `Failed to ensure user memory file: ${(error as Error).message}`,
75
+ );
76
+ }
77
+ };
78
+
79
+ export const addUserMemory = async (message: string): Promise<void> => {
80
+ try {
81
+ // Ensure user memory file exists
82
+ await ensureUserMemoryFile();
83
+
84
+ // Format memory entry, starting with -
85
+ const memoryEntry = `- ${message.substring(1).trim()}\n`;
86
+
87
+ // Read existing content
88
+ const existingContent = await fs.readFile(USER_MEMORY_FILE, "utf-8");
89
+
90
+ // Append new memory entry to the end of the file
91
+ const updatedContent = existingContent + memoryEntry;
92
+
93
+ // Write file
94
+ await fs.writeFile(USER_MEMORY_FILE, updatedContent, "utf-8");
95
+
96
+ // logger.info(`User memory added to ${USER_MEMORY_FILE}:`, message);
97
+ } catch (error) {
98
+ // logger.error("Failed to add user memory:", error);
99
+ throw new Error(`Failed to add user memory: ${(error as Error).message}`);
100
+ }
101
+ };
102
+
103
+ export const getUserMemoryContent = async (): Promise<string> => {
104
+ try {
105
+ await ensureUserMemoryFile();
106
+ return await fs.readFile(USER_MEMORY_FILE, "utf-8");
107
+ } catch {
108
+ // logger.error("Failed to read user memory:", error);
109
+ return "";
110
+ }
111
+ };
112
+
113
+ // Read project memory file content
114
+ export const readMemoryFile = async (workdir: string): Promise<string> => {
115
+ try {
116
+ const memoryFilePath = path.join(workdir, "WAVE.md");
117
+ return await fs.readFile(memoryFilePath, "utf-8");
118
+ } catch (error) {
119
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
120
+ return "";
121
+ }
122
+ throw error;
123
+ }
124
+ };
125
+
126
+ // Get merged memory content (project memory + user memory)
127
+ export const getCombinedMemoryContent = async (
128
+ workdir: string,
129
+ ): Promise<string> => {
130
+ // Read memory file content
131
+ const memoryContent = await readMemoryFile(workdir);
132
+
133
+ // Read user-level memory content
134
+ const userMemoryContent = await getUserMemoryContent();
135
+
136
+ // Merge project memory and user memory
137
+ let combinedMemory = "";
138
+ if (memoryContent.trim()) {
139
+ combinedMemory += memoryContent;
140
+ }
141
+ if (userMemoryContent.trim()) {
142
+ if (combinedMemory) {
143
+ combinedMemory += "\n\n";
144
+ }
145
+ combinedMemory += userMemoryContent;
146
+ }
147
+
148
+ return combinedMemory;
149
+ };