wave-agent-sdk 0.4.0 → 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 (105) hide show
  1. package/dist/agent.d.ts +28 -5
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +59 -37
  4. package/dist/constants/tools.d.ts +2 -2
  5. package/dist/constants/tools.js +2 -2
  6. package/dist/managers/MemoryRuleManager.js +1 -1
  7. package/dist/managers/aiManager.d.ts +8 -3
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +35 -9
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +1 -0
  12. package/dist/managers/backgroundTaskManager.d.ts +35 -0
  13. package/dist/managers/backgroundTaskManager.d.ts.map +1 -0
  14. package/dist/managers/backgroundTaskManager.js +249 -0
  15. package/dist/managers/foregroundTaskManager.d.ts +9 -0
  16. package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
  17. package/dist/managers/foregroundTaskManager.js +20 -0
  18. package/dist/managers/liveConfigManager.d.ts +1 -1
  19. package/dist/managers/lspManager.d.ts.map +1 -1
  20. package/dist/managers/lspManager.js +3 -1
  21. package/dist/managers/messageManager.d.ts +12 -2
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +36 -2
  24. package/dist/managers/permissionManager.d.ts.map +1 -1
  25. package/dist/managers/permissionManager.js +1 -7
  26. package/dist/managers/pluginManager.d.ts.map +1 -1
  27. package/dist/managers/pluginManager.js +3 -2
  28. package/dist/managers/slashCommandManager.d.ts +3 -0
  29. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  30. package/dist/managers/slashCommandManager.js +1 -0
  31. package/dist/managers/subagentManager.d.ts +11 -2
  32. package/dist/managers/subagentManager.d.ts.map +1 -1
  33. package/dist/managers/subagentManager.js +141 -35
  34. package/dist/managers/toolManager.d.ts +7 -1
  35. package/dist/managers/toolManager.d.ts.map +1 -1
  36. package/dist/managers/toolManager.js +9 -3
  37. package/dist/services/GitService.d.ts.map +1 -1
  38. package/dist/services/GitService.js +6 -2
  39. package/dist/services/MarketplaceService.d.ts +2 -2
  40. package/dist/services/MarketplaceService.d.ts.map +1 -1
  41. package/dist/services/MarketplaceService.js +18 -11
  42. package/dist/services/MemoryRuleService.d.ts +1 -1
  43. package/dist/services/MemoryRuleService.d.ts.map +1 -1
  44. package/dist/services/MemoryRuleService.js +13 -2
  45. package/dist/services/memory.js +1 -1
  46. package/dist/tools/bashTool.d.ts +0 -8
  47. package/dist/tools/bashTool.d.ts.map +1 -1
  48. package/dist/tools/bashTool.js +52 -174
  49. package/dist/tools/editTool.d.ts.map +1 -1
  50. package/dist/tools/editTool.js +6 -5
  51. package/dist/tools/multiEditTool.d.ts.map +1 -1
  52. package/dist/tools/multiEditTool.js +7 -6
  53. package/dist/tools/taskOutputTool.d.ts +3 -0
  54. package/dist/tools/taskOutputTool.d.ts.map +1 -0
  55. package/dist/tools/taskOutputTool.js +149 -0
  56. package/dist/tools/taskStopTool.d.ts +3 -0
  57. package/dist/tools/taskStopTool.d.ts.map +1 -0
  58. package/dist/tools/taskStopTool.js +65 -0
  59. package/dist/tools/taskTool.d.ts.map +1 -1
  60. package/dist/tools/taskTool.js +105 -63
  61. package/dist/tools/types.d.ts +3 -0
  62. package/dist/tools/types.d.ts.map +1 -1
  63. package/dist/types/marketplace.d.ts +1 -0
  64. package/dist/types/marketplace.d.ts.map +1 -1
  65. package/dist/types/messaging.d.ts +1 -0
  66. package/dist/types/messaging.d.ts.map +1 -1
  67. package/dist/types/processes.d.ts +24 -4
  68. package/dist/types/processes.d.ts.map +1 -1
  69. package/dist/utils/editUtils.d.ts +2 -11
  70. package/dist/utils/editUtils.d.ts.map +1 -1
  71. package/dist/utils/editUtils.js +52 -79
  72. package/dist/utils/messageOperations.d.ts +3 -1
  73. package/dist/utils/messageOperations.d.ts.map +1 -1
  74. package/dist/utils/messageOperations.js +5 -1
  75. package/package.json +5 -5
  76. package/src/agent.ts +79 -45
  77. package/src/constants/tools.ts +2 -2
  78. package/src/managers/MemoryRuleManager.ts +1 -1
  79. package/src/managers/aiManager.ts +50 -17
  80. package/src/managers/backgroundBashManager.ts +1 -0
  81. package/src/managers/backgroundTaskManager.ts +306 -0
  82. package/src/managers/foregroundTaskManager.ts +26 -0
  83. package/src/managers/lspManager.ts +3 -1
  84. package/src/managers/messageManager.ts +48 -2
  85. package/src/managers/permissionManager.ts +1 -7
  86. package/src/managers/pluginManager.ts +4 -3
  87. package/src/managers/slashCommandManager.ts +4 -0
  88. package/src/managers/subagentManager.ts +171 -31
  89. package/src/managers/toolManager.ts +16 -4
  90. package/src/services/GitService.ts +6 -2
  91. package/src/services/MarketplaceService.ts +30 -12
  92. package/src/services/MemoryRuleService.ts +18 -6
  93. package/src/services/memory.ts +1 -1
  94. package/src/tools/bashTool.ts +73 -200
  95. package/src/tools/editTool.ts +6 -17
  96. package/src/tools/multiEditTool.ts +7 -18
  97. package/src/tools/taskOutputTool.ts +174 -0
  98. package/src/tools/taskStopTool.ts +72 -0
  99. package/src/tools/taskTool.ts +130 -74
  100. package/src/tools/types.ts +3 -0
  101. package/src/types/marketplace.ts +1 -0
  102. package/src/types/messaging.ts +1 -0
  103. package/src/types/processes.ts +33 -4
  104. package/src/utils/editUtils.ts +65 -103
  105. package/src/utils/messageOperations.ts +7 -0
@@ -0,0 +1,306 @@
1
+ import { spawn, type ChildProcess } from "child_process";
2
+ import { BackgroundTask, BackgroundShell } from "../types/processes.js";
3
+ import { stripAnsiColors } from "../utils/stringUtils.js";
4
+ import { logger } from "../utils/globalLogger.js";
5
+
6
+ export interface BackgroundTaskManagerCallbacks {
7
+ onTasksChange?: (tasks: BackgroundTask[]) => void;
8
+ }
9
+
10
+ export interface BackgroundTaskManagerOptions {
11
+ callbacks?: BackgroundTaskManagerCallbacks;
12
+ workdir: string;
13
+ }
14
+
15
+ export class BackgroundTaskManager {
16
+ private tasks = new Map<string, BackgroundTask>();
17
+ private nextId = 1;
18
+ private callbacks: BackgroundTaskManagerCallbacks;
19
+ private workdir: string;
20
+
21
+ constructor(options: BackgroundTaskManagerOptions) {
22
+ this.callbacks = options.callbacks || {};
23
+ this.workdir = options.workdir;
24
+ }
25
+
26
+ private notifyTasksChange(): void {
27
+ this.callbacks.onTasksChange?.(Array.from(this.tasks.values()));
28
+ }
29
+
30
+ public generateId(): string {
31
+ return `task_${this.nextId++}`;
32
+ }
33
+
34
+ public addTask(task: BackgroundTask): void {
35
+ this.tasks.set(task.id, task);
36
+ this.notifyTasksChange();
37
+ }
38
+
39
+ public getTask(id: string): BackgroundTask | undefined {
40
+ return this.tasks.get(id);
41
+ }
42
+
43
+ public getAllTasks(): BackgroundTask[] {
44
+ return Array.from(this.tasks.values());
45
+ }
46
+
47
+ public startShell(
48
+ command: string,
49
+ timeout?: number,
50
+ ): { id: string; child: ChildProcess; detach: () => void } {
51
+ const id = this.generateId();
52
+ const startTime = Date.now();
53
+
54
+ const child = spawn(command, {
55
+ shell: true,
56
+ stdio: "pipe",
57
+ cwd: this.workdir,
58
+ env: {
59
+ ...process.env,
60
+ },
61
+ });
62
+
63
+ const shell: BackgroundShell = {
64
+ id,
65
+ type: "shell",
66
+ process: child,
67
+ command,
68
+ startTime,
69
+ status: "running",
70
+ stdout: "",
71
+ stderr: "",
72
+ };
73
+
74
+ this.tasks.set(id, shell);
75
+ this.notifyTasksChange();
76
+
77
+ // Set up timeout if specified
78
+ let timeoutHandle: NodeJS.Timeout | undefined;
79
+ if (timeout && timeout > 0) {
80
+ timeoutHandle = setTimeout(() => {
81
+ if (shell.status === "running") {
82
+ this.stopTask(id);
83
+ }
84
+ }, timeout);
85
+ }
86
+
87
+ const onStdout = (data: Buffer | string) => {
88
+ shell.stdout += stripAnsiColors(data.toString());
89
+ this.notifyTasksChange();
90
+ };
91
+
92
+ const onStderr = (data: Buffer | string) => {
93
+ shell.stderr += stripAnsiColors(data.toString());
94
+ this.notifyTasksChange();
95
+ };
96
+
97
+ const onExit = (code: number | null) => {
98
+ if (timeoutHandle) {
99
+ clearTimeout(timeoutHandle);
100
+ }
101
+ shell.status = code === 0 ? "completed" : "failed";
102
+ shell.exitCode = code ?? 0;
103
+ shell.endTime = Date.now();
104
+ shell.runtime = shell.endTime - startTime;
105
+ this.notifyTasksChange();
106
+ };
107
+
108
+ const onError = (error: Error) => {
109
+ if (timeoutHandle) {
110
+ clearTimeout(timeoutHandle);
111
+ }
112
+ shell.status = "failed";
113
+ shell.stderr += `\nProcess error: ${stripAnsiColors(error.message)}`;
114
+ shell.exitCode = 1;
115
+ shell.endTime = Date.now();
116
+ shell.runtime = shell.endTime - startTime;
117
+ this.notifyTasksChange();
118
+ };
119
+
120
+ child.stdout?.on("data", onStdout);
121
+ child.stderr?.on("data", onStderr);
122
+ child.on("exit", onExit);
123
+ child.on("error", onError);
124
+
125
+ const detach = () => {
126
+ child.stdout?.off("data", onStdout);
127
+ child.stderr?.off("data", onStderr);
128
+ child.off("exit", onExit);
129
+ child.off("error", onError);
130
+ if (timeoutHandle) {
131
+ clearTimeout(timeoutHandle);
132
+ }
133
+ this.tasks.delete(id);
134
+ this.notifyTasksChange();
135
+ };
136
+
137
+ return { id, child, detach };
138
+ }
139
+
140
+ public adoptProcess(
141
+ child: ChildProcess,
142
+ command: string,
143
+ initialStdout: string = "",
144
+ initialStderr: string = "",
145
+ ): string {
146
+ const id = this.generateId();
147
+ const startTime = Date.now();
148
+
149
+ const shell: BackgroundShell = {
150
+ id,
151
+ type: "shell",
152
+ process: child,
153
+ command,
154
+ startTime,
155
+ status: "running",
156
+ stdout: initialStdout,
157
+ stderr: initialStderr,
158
+ };
159
+
160
+ this.tasks.set(id, shell);
161
+ this.notifyTasksChange();
162
+
163
+ child.stdout?.on("data", (data) => {
164
+ shell.stdout += stripAnsiColors(data.toString());
165
+ this.notifyTasksChange();
166
+ });
167
+
168
+ child.stderr?.on("data", (data) => {
169
+ shell.stderr += stripAnsiColors(data.toString());
170
+ this.notifyTasksChange();
171
+ });
172
+
173
+ child.on("exit", (code) => {
174
+ shell.status = code === 0 ? "completed" : "failed";
175
+ shell.exitCode = code ?? 0;
176
+ shell.endTime = Date.now();
177
+ shell.runtime = shell.endTime - startTime;
178
+ this.notifyTasksChange();
179
+ });
180
+
181
+ child.on("error", (error) => {
182
+ shell.status = "failed";
183
+ shell.stderr += `\nProcess error: ${stripAnsiColors(error.message)}`;
184
+ shell.exitCode = 1;
185
+ shell.endTime = Date.now();
186
+ shell.runtime = shell.endTime - startTime;
187
+ this.notifyTasksChange();
188
+ });
189
+
190
+ return id;
191
+ }
192
+
193
+ public getOutput(
194
+ id: string,
195
+ filter?: string,
196
+ ): { stdout: string; stderr: string; status: string } | null {
197
+ const task = this.tasks.get(id);
198
+ if (!task) {
199
+ return null;
200
+ }
201
+
202
+ let stdout = task.stdout;
203
+ let stderr = task.stderr;
204
+
205
+ // Apply regex filter if provided
206
+ if (filter) {
207
+ try {
208
+ const regex = new RegExp(filter);
209
+ stdout = stdout
210
+ .split("\n")
211
+ .filter((line) => regex.test(line))
212
+ .join("\n");
213
+ stderr = stderr
214
+ .split("\n")
215
+ .filter((line) => regex.test(line))
216
+ .join("\n");
217
+ } catch (error) {
218
+ logger.warn(`Invalid filter regex: ${filter}`, error);
219
+ }
220
+ }
221
+
222
+ return {
223
+ stdout,
224
+ stderr,
225
+ status: task.status,
226
+ };
227
+ }
228
+
229
+ public stopTask(id: string): boolean {
230
+ const task = this.tasks.get(id);
231
+ if (!task || task.status !== "running") {
232
+ return false;
233
+ }
234
+
235
+ if (task.type === "shell") {
236
+ const shell = task as BackgroundShell;
237
+ try {
238
+ // Try to kill process group first
239
+ if (shell.process.pid) {
240
+ process.kill(-shell.process.pid, "SIGTERM");
241
+
242
+ // Force kill after timeout
243
+ setTimeout(() => {
244
+ if (
245
+ shell.status === "running" &&
246
+ shell.process.pid &&
247
+ !shell.process.killed
248
+ ) {
249
+ try {
250
+ process.kill(-shell.process.pid, "SIGKILL");
251
+ } catch (error) {
252
+ logger.error("Failed to force kill process:", error);
253
+ }
254
+ }
255
+ }, 1000);
256
+ }
257
+
258
+ shell.status = "killed";
259
+ shell.endTime = Date.now();
260
+ shell.runtime = shell.endTime - shell.startTime;
261
+ this.notifyTasksChange();
262
+ return true;
263
+ } catch {
264
+ // Fallback to direct process kill
265
+ try {
266
+ shell.process.kill("SIGTERM");
267
+ setTimeout(() => {
268
+ if (!shell.process.killed) {
269
+ shell.process.kill("SIGKILL");
270
+ }
271
+ }, 1000);
272
+ shell.status = "killed";
273
+ shell.endTime = Date.now();
274
+ shell.runtime = shell.endTime - shell.startTime;
275
+ this.notifyTasksChange();
276
+ return true;
277
+ } catch (directKillError) {
278
+ logger.error("Failed to kill child process:", directKillError);
279
+ return false;
280
+ }
281
+ }
282
+ } else if (task.type === "subagent") {
283
+ // Subagent termination logic will be handled by aborting the AI loop
284
+ // which is already managed by the SubagentManager and AIManager.
285
+ // Here we just update the status.
286
+ task.status = "killed";
287
+ task.endTime = Date.now();
288
+ task.runtime = task.endTime - task.startTime;
289
+ this.notifyTasksChange();
290
+ return true;
291
+ }
292
+
293
+ return false;
294
+ }
295
+
296
+ public cleanup(): void {
297
+ // Kill all running tasks
298
+ for (const [id, task] of this.tasks) {
299
+ if (task.status === "running") {
300
+ this.stopTask(id);
301
+ }
302
+ }
303
+ this.tasks.clear();
304
+ this.notifyTasksChange();
305
+ }
306
+ }
@@ -0,0 +1,26 @@
1
+ import { ForegroundTask, IForegroundTaskManager } from "../types/processes.js";
2
+
3
+ export class ForegroundTaskManager implements IForegroundTaskManager {
4
+ private activeForegroundTasks: ForegroundTask[] = [];
5
+
6
+ public registerForegroundTask(task: ForegroundTask): void {
7
+ this.activeForegroundTasks.push(task);
8
+ }
9
+
10
+ public unregisterForegroundTask(id: string): void {
11
+ this.activeForegroundTasks = this.activeForegroundTasks.filter(
12
+ (t) => t.id !== id,
13
+ );
14
+ }
15
+
16
+ public async backgroundCurrentTask(): Promise<void> {
17
+ const task = this.activeForegroundTasks.pop();
18
+ if (task) {
19
+ await task.backgroundHandler();
20
+ }
21
+ }
22
+
23
+ public hasActiveTasks(): boolean {
24
+ return this.activeForegroundTasks.length > 0;
25
+ }
26
+ }
@@ -418,7 +418,9 @@ export class LspManager implements ILspManager {
418
418
  await this.sendRequest(lspProc, "shutdown", {}, timeout);
419
419
  await this.sendNotification(lspProc, "exit", {});
420
420
  // Give it a moment to exit
421
- await new Promise((resolve) => setTimeout(resolve, 100));
421
+ if (timeout > 100) {
422
+ await new Promise((resolve) => setTimeout(resolve, 100));
423
+ }
422
424
  } catch (error) {
423
425
  this.logger?.debug(
424
426
  `Failed to gracefully shutdown LSP for ${language}: ${error}`,
@@ -26,6 +26,7 @@ import {
26
26
  SESSION_DIR,
27
27
  } from "../services/session.js";
28
28
  import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
29
+ import type { MemoryRuleManager } from "./MemoryRuleManager.js";
29
30
  import { pathEncoder } from "../utils/pathEncoder.js";
30
31
 
31
32
  export interface MessageManagerCallbacks {
@@ -57,6 +58,7 @@ export interface MessageManagerCallbacks {
57
58
  onUpdateCommandOutputMessage?: (command: string, output: string) => void;
58
59
  onCompleteCommandMessage?: (command: string, exitCode: number) => void;
59
60
  onSlashCommandsChange?: (commands: SlashCommand[]) => void;
61
+ onInfoBlockAdded?: (content: string) => void;
60
62
  // Rewind callbacks
61
63
  onShowRewind?: () => void;
62
64
  // Subagent callbacks
@@ -80,6 +82,7 @@ export interface MessageManagerOptions {
80
82
  logger?: Logger;
81
83
  sessionType?: "main" | "subagent";
82
84
  subagentType?: string;
85
+ memoryRuleManager?: MemoryRuleManager;
83
86
  }
84
87
 
85
88
  export class MessageManager {
@@ -95,6 +98,7 @@ export class MessageManager {
95
98
  private transcriptPath: string; // Cached transcript path
96
99
  private savedMessageCount: number; // Track how many messages have been saved to prevent duplication
97
100
  private filesInContext: Set<string> = new Set(); // Track files mentioned in the conversation
101
+ private memoryRuleManager?: MemoryRuleManager;
98
102
  private sessionType: "main" | "subagent";
99
103
  private subagentType?: string;
100
104
 
@@ -110,6 +114,7 @@ export class MessageManager {
110
114
  this.savedMessageCount = 0; // Initialize saved message count tracker
111
115
  this.sessionType = options.sessionType || "main";
112
116
  this.subagentType = options.subagentType;
117
+ this.memoryRuleManager = options.memoryRuleManager;
113
118
 
114
119
  // Compute and cache the transcript path
115
120
  this.transcriptPath = this.computeTranscriptPath();
@@ -151,6 +156,30 @@ export class MessageManager {
151
156
  return this.transcriptPath;
152
157
  }
153
158
 
159
+ /**
160
+ * Get combined memory content (project memory + user memory + modular rules)
161
+ */
162
+ public async getCombinedMemory(): Promise<string> {
163
+ const memory = await import("../services/memory.js");
164
+ let combined = await memory.getCombinedMemoryContent(this.workdir);
165
+
166
+ if (this.memoryRuleManager) {
167
+ const filesInContext = this.getFilesInContext();
168
+ const activeRules = this.memoryRuleManager.getActiveRules(filesInContext);
169
+ if (activeRules.length > 0) {
170
+ this.logger?.debug(
171
+ `Active modular rules (${activeRules.length}): ${activeRules.map((r) => r.id).join(", ")}`,
172
+ );
173
+ if (combined) {
174
+ combined += "\n\n";
175
+ }
176
+ combined += activeRules.map((r) => r.content).join("\n\n");
177
+ }
178
+ }
179
+
180
+ return combined;
181
+ }
182
+
154
183
  /**
155
184
  * Compute the transcript path using cached encoded workdir
156
185
  * Called during construction and when sessionId changes
@@ -400,6 +429,18 @@ export class MessageManager {
400
429
  this.callbacks.onErrorBlockAdded?.(error);
401
430
  }
402
431
 
432
+ public addInfoBlock(content: string): void {
433
+ const lastMessage = this.messages[this.messages.length - 1];
434
+ if (lastMessage && lastMessage.role === "assistant") {
435
+ lastMessage.blocks.push({
436
+ type: "info",
437
+ content,
438
+ } as unknown as import("../types/index.js").MessageBlock);
439
+ this.setMessages([...this.messages]);
440
+ this.callbacks.onInfoBlockAdded?.(content);
441
+ }
442
+ }
443
+
403
444
  /**
404
445
  * Compress messages and update session, delete compressed messages, only keep compressed messages and subsequent messages
405
446
  */
@@ -494,12 +535,13 @@ export class MessageManager {
494
535
  subagentName: string,
495
536
  sessionId: string,
496
537
  configuration: SubagentConfiguration,
497
- status: "active" | "completed" | "error" = "active",
538
+ status: "active" | "completed" | "error" | "aborted" = "active",
498
539
  parameters: {
499
540
  description: string;
500
541
  prompt: string;
501
542
  subagent_type: string;
502
543
  },
544
+ runInBackground?: boolean,
503
545
  ): void {
504
546
  const params: AddSubagentBlockParams = {
505
547
  messages: this.messages,
@@ -508,6 +550,7 @@ export class MessageManager {
508
550
  sessionId,
509
551
  status,
510
552
  configuration,
553
+ runInBackground,
511
554
  };
512
555
  const updatedMessages = addSubagentBlockToMessage(params);
513
556
  this.setMessages(updatedMessages);
@@ -519,6 +562,7 @@ export class MessageManager {
519
562
  updates: Partial<{
520
563
  status: "active" | "completed" | "error" | "aborted";
521
564
  sessionId: string;
565
+ runInBackground: boolean;
522
566
  }>,
523
567
  ): void {
524
568
  const updatedMessages = updateSubagentBlockInMessage(
@@ -532,7 +576,9 @@ export class MessageManager {
532
576
  subagentId,
533
577
  status: updates.status || "active",
534
578
  };
535
- this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
579
+ if (updates.status) {
580
+ this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
581
+ }
536
582
  }
537
583
 
538
584
  /**
@@ -237,13 +237,7 @@ export class PermissionManager {
237
237
  * Get the current effective permission mode for tool execution context
238
238
  */
239
239
  getCurrentEffectiveMode(cliPermissionMode?: PermissionMode): PermissionMode {
240
- const mode = this.resolveEffectivePermissionMode(cliPermissionMode);
241
- this.logger?.debug("getCurrentEffectiveMode", {
242
- cliPermissionMode,
243
- configuredDefaultMode: this.configuredDefaultMode,
244
- resolvedMode: mode,
245
- });
246
- return mode;
240
+ return this.resolveEffectivePermissionMode(cliPermissionMode);
247
241
  }
248
242
 
249
243
  /**
@@ -149,9 +149,7 @@ export class PluginManager {
149
149
  * @param configs Array of plugin configurations
150
150
  */
151
151
  async loadPlugins(configs: PluginConfig[]): Promise<void> {
152
- // Load installed plugins from marketplace first
153
- await this.loadInstalledPlugins();
154
-
152
+ // Load plugins from configuration (e.g. --plugin-dir) first to give them higher priority
155
153
  for (const config of configs) {
156
154
  if (config.type !== "local") {
157
155
  this.logger?.warn(`Unsupported plugin type: ${config.type}`);
@@ -164,6 +162,9 @@ export class PluginManager {
164
162
 
165
163
  await this.loadSinglePlugin(absolutePath);
166
164
  }
165
+
166
+ // Load installed plugins from marketplace
167
+ await this.loadInstalledPlugins();
167
168
  }
168
169
 
169
170
  /**
@@ -1,5 +1,6 @@
1
1
  import type { MessageManager } from "./messageManager.js";
2
2
  import type { AIManager } from "./aiManager.js";
3
+ import type { BackgroundTaskManager } from "./backgroundTaskManager.js";
3
4
  import type {
4
5
  SlashCommand,
5
6
  CustomSlashCommand,
@@ -26,6 +27,7 @@ const execAsync = promisify(exec);
26
27
  export interface SlashCommandManagerOptions {
27
28
  messageManager: MessageManager;
28
29
  aiManager: AIManager;
30
+ backgroundTaskManager: BackgroundTaskManager;
29
31
  workdir: string;
30
32
  logger?: Logger;
31
33
  }
@@ -35,12 +37,14 @@ export class SlashCommandManager {
35
37
  private customCommands = new Map<string, CustomSlashCommand>();
36
38
  private messageManager: MessageManager;
37
39
  private aiManager: AIManager;
40
+ private backgroundTaskManager: BackgroundTaskManager;
38
41
  private workdir: string;
39
42
  private logger?: Logger;
40
43
 
41
44
  constructor(options: SlashCommandManagerOptions) {
42
45
  this.messageManager = options.messageManager;
43
46
  this.aiManager = options.aiManager;
47
+ this.backgroundTaskManager = options.backgroundTaskManager;
44
48
  this.workdir = options.workdir;
45
49
  this.logger = options.logger;
46
50