wave-agent-sdk 0.17.5 → 0.17.7
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.
- package/dist/agent.d.ts +18 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +114 -1
- package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
- package/dist/managers/MemoryRuleManager.js +30 -13
- package/dist/managers/aiManager.d.ts +1 -0
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +20 -62
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +3 -1
- package/dist/managers/lspManager.d.ts.map +1 -1
- package/dist/managers/lspManager.js +12 -4
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +13 -6
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +0 -4
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +7 -2
- package/dist/managers/planManager.d.ts +3 -0
- package/dist/managers/planManager.d.ts.map +1 -1
- package/dist/managers/planManager.js +9 -0
- package/dist/managers/skillManager.d.ts +3 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +69 -54
- package/dist/managers/slashCommandManager.d.ts +0 -6
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -170
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +2 -5
- package/dist/prompts/planModeReminders.d.ts +0 -1
- package/dist/prompts/planModeReminders.d.ts.map +1 -1
- package/dist/prompts/planModeReminders.js +3 -12
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +12 -4
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +39 -5
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +30 -7
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/utils/customCommands.d.ts.map +1 -1
- package/dist/utils/customCommands.js +11 -9
- package/dist/utils/skillParser.d.ts.map +1 -1
- package/dist/utils/skillParser.js +3 -1
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +18 -7
- package/package.json +1 -1
- package/src/agent.ts +146 -1
- package/src/managers/MemoryRuleManager.ts +29 -14
- package/src/managers/aiManager.ts +28 -78
- package/src/managers/hookManager.ts +6 -1
- package/src/managers/lspManager.ts +23 -5
- package/src/managers/mcpManager.ts +24 -7
- package/src/managers/messageManager.ts +0 -5
- package/src/managers/permissionManager.ts +8 -1
- package/src/managers/planManager.ts +11 -0
- package/src/managers/skillManager.ts +90 -57
- package/src/managers/slashCommandManager.ts +0 -215
- package/src/managers/toolManager.ts +2 -9
- package/src/prompts/planModeReminders.ts +3 -15
- package/src/services/MarketplaceService.ts +22 -4
- package/src/services/memory.ts +43 -6
- package/src/services/pluginLoader.ts +35 -7
- package/src/types/skills.ts +1 -0
- package/src/utils/customCommands.ts +17 -12
- package/src/utils/skillParser.ts +3 -1
- package/src/utils/subagentParser.ts +22 -8
|
@@ -92,22 +92,40 @@ export class LspManager implements ILspManager {
|
|
|
92
92
|
try {
|
|
93
93
|
const env = { ...process.env, ...config.env };
|
|
94
94
|
|
|
95
|
-
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT}
|
|
95
|
+
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}
|
|
96
96
|
let command = config.command;
|
|
97
97
|
let args = config.args || [];
|
|
98
98
|
if (config.pluginRoot) {
|
|
99
99
|
env.WAVE_PLUGIN_ROOT = config.pluginRoot;
|
|
100
|
+
env.CLAUDE_PLUGIN_ROOT = config.pluginRoot;
|
|
100
101
|
command = command.replace(/\$\{WAVE_PLUGIN_ROOT\}/g, config.pluginRoot);
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
command = command.replace(
|
|
103
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
104
|
+
config.pluginRoot,
|
|
103
105
|
);
|
|
104
|
-
|
|
106
|
+
args = args.map((arg) => {
|
|
107
|
+
let result = arg.replace(
|
|
108
|
+
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
109
|
+
config.pluginRoot!,
|
|
110
|
+
);
|
|
111
|
+
result = result.replace(
|
|
112
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
113
|
+
config.pluginRoot!,
|
|
114
|
+
);
|
|
115
|
+
return result;
|
|
116
|
+
});
|
|
117
|
+
// Also expand plugin root in user-provided env values
|
|
105
118
|
if (config.env) {
|
|
106
119
|
for (const [key, value] of Object.entries(config.env)) {
|
|
107
|
-
|
|
120
|
+
let expanded = value.replace(
|
|
108
121
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
109
122
|
config.pluginRoot!,
|
|
110
123
|
);
|
|
124
|
+
expanded = expanded.replace(
|
|
125
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
126
|
+
config.pluginRoot!,
|
|
127
|
+
);
|
|
128
|
+
env[key] = expanded;
|
|
111
129
|
}
|
|
112
130
|
}
|
|
113
131
|
}
|
|
@@ -40,7 +40,7 @@ export interface McpManagerOptions {
|
|
|
40
40
|
* Expand environment variables in a string value.
|
|
41
41
|
* Supports ${VAR} and ${VAR:-default} patterns.
|
|
42
42
|
*/
|
|
43
|
-
const WAVE_TEMPLATE_VARS = ["WAVE_PLUGIN_ROOT"];
|
|
43
|
+
const WAVE_TEMPLATE_VARS = ["WAVE_PLUGIN_ROOT", "CLAUDE_PLUGIN_ROOT"];
|
|
44
44
|
|
|
45
45
|
export function expandEnvVars(value: string): string {
|
|
46
46
|
return value.replace(/\$\{([^}]+)\}/g, (_match, expr: string) => {
|
|
@@ -440,25 +440,42 @@ export class McpManager {
|
|
|
440
440
|
...(server.config.env || {}),
|
|
441
441
|
};
|
|
442
442
|
|
|
443
|
-
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT}
|
|
444
|
-
// (same pattern as Claude Code's substitutePluginVariables)
|
|
443
|
+
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}
|
|
445
444
|
let command = server.config.command;
|
|
446
445
|
let args = server.config.args || [];
|
|
447
446
|
if (server.config.pluginRoot) {
|
|
448
447
|
env.WAVE_PLUGIN_ROOT = server.config.pluginRoot;
|
|
448
|
+
env.CLAUDE_PLUGIN_ROOT = server.config.pluginRoot;
|
|
449
449
|
command = command.replace(
|
|
450
450
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
451
451
|
server.config.pluginRoot,
|
|
452
452
|
);
|
|
453
|
-
|
|
454
|
-
|
|
453
|
+
command = command.replace(
|
|
454
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
455
|
+
server.config.pluginRoot,
|
|
455
456
|
);
|
|
456
|
-
|
|
457
|
+
args = args.map((arg) => {
|
|
458
|
+
let result = arg.replace(
|
|
459
|
+
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
460
|
+
server.config.pluginRoot!,
|
|
461
|
+
);
|
|
462
|
+
result = result.replace(
|
|
463
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
464
|
+
server.config.pluginRoot!,
|
|
465
|
+
);
|
|
466
|
+
return result;
|
|
467
|
+
});
|
|
468
|
+
// Also expand plugin root in user-provided env values
|
|
457
469
|
for (const [key, value] of Object.entries(server.config.env || {})) {
|
|
458
|
-
|
|
470
|
+
let expanded = value.replace(
|
|
459
471
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
460
472
|
server.config.pluginRoot!,
|
|
461
473
|
);
|
|
474
|
+
expanded = expanded.replace(
|
|
475
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
476
|
+
server.config.pluginRoot!,
|
|
477
|
+
);
|
|
478
|
+
env[key] = expanded;
|
|
462
479
|
}
|
|
463
480
|
}
|
|
464
481
|
transport = new StdioClientTransport({
|
|
@@ -548,11 +548,6 @@ export class MessageManager {
|
|
|
548
548
|
this.setSessionId(generateSessionId());
|
|
549
549
|
this.parentSessionId = oldSessionId;
|
|
550
550
|
|
|
551
|
-
// Trigger task list update if this is the main session to ensure continuity
|
|
552
|
-
if (this.sessionType === "main") {
|
|
553
|
-
this.callbacks.onSessionIdChange?.(this.sessionId);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
551
|
// Set new message list
|
|
557
552
|
this.setMessages(newMessages);
|
|
558
553
|
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
EDIT_TOOL_NAME,
|
|
32
32
|
WRITE_TOOL_NAME,
|
|
33
33
|
READ_TOOL_NAME,
|
|
34
|
+
ASK_USER_QUESTION_TOOL_NAME,
|
|
34
35
|
} from "../constants/tools.js";
|
|
35
36
|
import { Container } from "../utils/container.js";
|
|
36
37
|
import { ConfigurationService } from "../services/configurationService.js";
|
|
@@ -440,8 +441,14 @@ export class PermissionManager {
|
|
|
440
441
|
}
|
|
441
442
|
|
|
442
443
|
// 1. If bypassPermissions mode, always allow
|
|
444
|
+
// Exception: tools that require user interaction (e.g. AskUserQuestion)
|
|
445
|
+
// must still prompt the user, matching Claude Code's requiresUserInteraction behavior.
|
|
443
446
|
if (context.permissionMode === "bypassPermissions") {
|
|
444
|
-
|
|
447
|
+
const requiresUserInteraction =
|
|
448
|
+
context.toolName === ASK_USER_QUESTION_TOOL_NAME;
|
|
449
|
+
if (!requiresUserInteraction) {
|
|
450
|
+
return { behavior: "allow" };
|
|
451
|
+
}
|
|
445
452
|
}
|
|
446
453
|
|
|
447
454
|
// 1.0 Check worktree safety for Write and Edit tools
|
|
@@ -16,6 +16,7 @@ import { logger } from "../utils/globalLogger.js";
|
|
|
16
16
|
export class PlanManager {
|
|
17
17
|
private planDir: string;
|
|
18
18
|
private currentPlanFilePath: string | null = null;
|
|
19
|
+
private planEntryReminderPending: boolean = false;
|
|
19
20
|
|
|
20
21
|
constructor(private container: Container) {
|
|
21
22
|
this.planDir = path.join(os.homedir(), ".wave", "plans");
|
|
@@ -80,6 +81,7 @@ export class PlanManager {
|
|
|
80
81
|
// Entering plan mode: clear any pending exit attachment
|
|
81
82
|
// (prevents sending both plan_mode and plan_mode_exit on rapid toggle)
|
|
82
83
|
permissionManager?.setNeedsPlanModeExitAttachment(false);
|
|
84
|
+
this.planEntryReminderPending = true;
|
|
83
85
|
|
|
84
86
|
this.getOrGeneratePlanFilePath(messageManager?.getRootSessionId())
|
|
85
87
|
.then(({ path }) => {
|
|
@@ -94,8 +96,17 @@ export class PlanManager {
|
|
|
94
96
|
permissionManager?.setHasExitedPlanMode(true);
|
|
95
97
|
permissionManager?.setNeedsPlanModeExitAttachment(true);
|
|
96
98
|
permissionManager?.setPlanFilePath(undefined);
|
|
99
|
+
this.planEntryReminderPending = false;
|
|
97
100
|
} else {
|
|
98
101
|
permissionManager?.setPlanFilePath(undefined);
|
|
99
102
|
}
|
|
100
103
|
}
|
|
104
|
+
|
|
105
|
+
public isPlanEntryReminderPending(): boolean {
|
|
106
|
+
return this.planEntryReminderPending;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public consumePlanEntryReminder(): void {
|
|
110
|
+
this.planEntryReminderPending = false;
|
|
111
|
+
}
|
|
101
112
|
}
|
|
@@ -29,6 +29,7 @@ import { logger } from "../utils/globalLogger.js";
|
|
|
29
29
|
*/
|
|
30
30
|
export class SkillManager extends EventEmitter {
|
|
31
31
|
private personalSkillsPath: string;
|
|
32
|
+
private personalClaudeSkillsPath: string;
|
|
32
33
|
private scanTimeout: number;
|
|
33
34
|
private workdir: string;
|
|
34
35
|
|
|
@@ -47,6 +48,8 @@ export class SkillManager extends EventEmitter {
|
|
|
47
48
|
super();
|
|
48
49
|
this.personalSkillsPath =
|
|
49
50
|
options.personalSkillsPath || join(homedir(), ".wave", "skills");
|
|
51
|
+
this.personalClaudeSkillsPath =
|
|
52
|
+
options.personalClaudeSkillsPath || join(homedir(), ".claude", "skills");
|
|
50
53
|
this.scanTimeout = options.scanTimeout || 5000;
|
|
51
54
|
this.workdir = options.workdir || process.cwd();
|
|
52
55
|
this.watchEnabled = options.watch ?? false;
|
|
@@ -127,7 +130,9 @@ export class SkillManager extends EventEmitter {
|
|
|
127
130
|
|
|
128
131
|
const pathsToWatch = [
|
|
129
132
|
this.personalSkillsPath,
|
|
133
|
+
this.personalClaudeSkillsPath,
|
|
130
134
|
join(this.workdir, ".wave", "skills"),
|
|
135
|
+
join(this.workdir, ".claude", "skills"),
|
|
131
136
|
];
|
|
132
137
|
|
|
133
138
|
logger?.debug(`Setting up skill watcher for: ${pathsToWatch.join(", ")}`);
|
|
@@ -220,6 +225,11 @@ export class SkillManager extends EventEmitter {
|
|
|
220
225
|
"builtin",
|
|
221
226
|
);
|
|
222
227
|
|
|
228
|
+
const personalClaudeCollection = await this.discoverSkillCollection(
|
|
229
|
+
this.personalClaudeSkillsPath,
|
|
230
|
+
"personal",
|
|
231
|
+
);
|
|
232
|
+
|
|
223
233
|
const personalCollection = await this.discoverSkillCollection(
|
|
224
234
|
this.personalSkillsPath,
|
|
225
235
|
"personal",
|
|
@@ -232,10 +242,14 @@ export class SkillManager extends EventEmitter {
|
|
|
232
242
|
|
|
233
243
|
return {
|
|
234
244
|
builtinSkills: builtinCollection.skills,
|
|
235
|
-
personalSkills:
|
|
245
|
+
personalSkills: new Map([
|
|
246
|
+
...personalClaudeCollection.skills,
|
|
247
|
+
...personalCollection.skills, // .wave overrides .claude
|
|
248
|
+
]),
|
|
236
249
|
projectSkills: projectCollection.skills,
|
|
237
250
|
errors: [
|
|
238
251
|
...builtinCollection.errors,
|
|
252
|
+
...personalClaudeCollection.errors,
|
|
239
253
|
...personalCollection.errors,
|
|
240
254
|
...projectCollection.errors,
|
|
241
255
|
],
|
|
@@ -256,77 +270,88 @@ export class SkillManager extends EventEmitter {
|
|
|
256
270
|
errors: [],
|
|
257
271
|
};
|
|
258
272
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
273
|
+
if (type === "project") {
|
|
274
|
+
// Scan .claude/skills first, then .wave/skills (wave overrides)
|
|
275
|
+
const claudePath = join(basePath, ".claude", "skills");
|
|
276
|
+
const wavePath = join(basePath, ".wave", "skills");
|
|
277
|
+
await this.scanSkillPath(claudePath, collection);
|
|
278
|
+
await this.scanSkillPath(wavePath, collection);
|
|
264
279
|
} else {
|
|
265
|
-
|
|
280
|
+
await this.scanSkillPath(basePath, collection);
|
|
266
281
|
}
|
|
267
282
|
|
|
283
|
+
return collection;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private async scanSkillPath(
|
|
287
|
+
skillsPath: string,
|
|
288
|
+
collection: SkillCollection,
|
|
289
|
+
): Promise<void> {
|
|
268
290
|
try {
|
|
269
291
|
const skillDirs = await this.findSkillDirectories(skillsPath);
|
|
270
292
|
logger?.debug(
|
|
271
293
|
`Found ${skillDirs.length} potential skill directories in ${skillsPath}`,
|
|
272
294
|
);
|
|
295
|
+
await this.processSkillDirs(skillDirs, collection);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
logger?.debug(
|
|
298
|
+
`Could not scan ${skillsPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
273
302
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
303
|
+
private async processSkillDirs(
|
|
304
|
+
skillDirs: string[],
|
|
305
|
+
collection: SkillCollection,
|
|
306
|
+
): Promise<void> {
|
|
307
|
+
for (const skillDir of skillDirs) {
|
|
308
|
+
try {
|
|
309
|
+
const skillFilePath = join(skillDir, "SKILL.md");
|
|
277
310
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const parsed = parseSkillFile(skillFilePath, {
|
|
286
|
-
basePath: skillDir,
|
|
287
|
-
validateMetadata: true,
|
|
288
|
-
});
|
|
311
|
+
// Check if SKILL.md exists
|
|
312
|
+
try {
|
|
313
|
+
await stat(skillFilePath);
|
|
314
|
+
} catch {
|
|
315
|
+
continue; // Skip directories without SKILL.md
|
|
316
|
+
}
|
|
289
317
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
} catch (error) {
|
|
318
|
+
const parsed = parseSkillFile(skillFilePath, {
|
|
319
|
+
basePath: skillDir,
|
|
320
|
+
validateMetadata: true,
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (parsed.isValid) {
|
|
324
|
+
// Override the skill type with the collection type
|
|
325
|
+
const skillMetadata: SkillMetadata = {
|
|
326
|
+
...parsed.skillMetadata,
|
|
327
|
+
type: collection.type,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Create full skill object with content
|
|
331
|
+
const skill: Skill = {
|
|
332
|
+
...skillMetadata,
|
|
333
|
+
content: parsed.content,
|
|
334
|
+
frontmatter: parsed.frontmatter,
|
|
335
|
+
isValid: parsed.isValid,
|
|
336
|
+
errors: parsed.validationErrors,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
collection.skills.set(skillMetadata.name, skillMetadata);
|
|
340
|
+
// Store the full skill content in the manager's skillContent map
|
|
341
|
+
this.skillContent.set(skillMetadata.name, skill);
|
|
342
|
+
} else {
|
|
316
343
|
collection.errors.push({
|
|
317
344
|
skillPath: skillDir,
|
|
318
|
-
message:
|
|
345
|
+
message: parsed.validationErrors.join("; "),
|
|
319
346
|
});
|
|
320
347
|
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
collection.errors.push({
|
|
350
|
+
skillPath: skillDir,
|
|
351
|
+
message: `Failed to process skill: ${error instanceof Error ? error.message : String(error)}`,
|
|
352
|
+
});
|
|
321
353
|
}
|
|
322
|
-
} catch (error) {
|
|
323
|
-
logger?.debug(
|
|
324
|
-
`Could not scan ${skillsPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
325
|
-
);
|
|
326
|
-
// Not an error - the directory might not exist yet
|
|
327
354
|
}
|
|
328
|
-
|
|
329
|
-
return collection;
|
|
330
355
|
}
|
|
331
356
|
|
|
332
357
|
/**
|
|
@@ -450,15 +475,23 @@ export class SkillManager extends EventEmitter {
|
|
|
450
475
|
// 1. Substitute parameters ($1, $ARGUMENTS, etc.)
|
|
451
476
|
mainContent = substituteCommandParameters(mainContent, argsString);
|
|
452
477
|
|
|
453
|
-
// 2. Substitute ${WAVE_SKILL_DIR}
|
|
478
|
+
// 2. Substitute ${WAVE_SKILL_DIR} and ${CLAUDE_SKILL_DIR}
|
|
454
479
|
mainContent = mainContent.replace(/\$\{WAVE_SKILL_DIR\}/g, skill.skillPath);
|
|
480
|
+
mainContent = mainContent.replace(
|
|
481
|
+
/\$\{CLAUDE_SKILL_DIR\}/g,
|
|
482
|
+
skill.skillPath,
|
|
483
|
+
);
|
|
455
484
|
|
|
456
|
-
// 3. Substitute ${WAVE_PLUGIN_ROOT}
|
|
485
|
+
// 3. Substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}
|
|
457
486
|
if (skill.pluginRoot) {
|
|
458
487
|
mainContent = mainContent.replace(
|
|
459
488
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
460
489
|
skill.pluginRoot,
|
|
461
490
|
);
|
|
491
|
+
mainContent = mainContent.replace(
|
|
492
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
493
|
+
skill.pluginRoot,
|
|
494
|
+
);
|
|
462
495
|
}
|
|
463
496
|
|
|
464
497
|
return skillPath + mainContent;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { MessageManager } from "./messageManager.js";
|
|
2
2
|
import type { AIManager } from "./aiManager.js";
|
|
3
|
-
import type { BackgroundTaskManager } from "./backgroundTaskManager.js";
|
|
4
|
-
import type { TaskManager } from "../services/taskManager.js";
|
|
5
3
|
import type { SlashCommand, CustomSlashCommand } from "../types/index.js";
|
|
6
4
|
import { loadCustomSlashCommands } from "../utils/customCommands.js";
|
|
7
5
|
|
|
@@ -23,10 +21,6 @@ import {
|
|
|
23
21
|
import type { SkillManager } from "./skillManager.js";
|
|
24
22
|
import type { SkillMetadata } from "../types/skills.js";
|
|
25
23
|
import type { SubagentManager } from "./subagentManager.js";
|
|
26
|
-
import type { MemoryService } from "../services/memory.js";
|
|
27
|
-
import type { HookManager } from "./hookManager.js";
|
|
28
|
-
import type { GoalManager } from "./goalManager.js";
|
|
29
|
-
|
|
30
24
|
import { logger } from "../utils/globalLogger.js";
|
|
31
25
|
|
|
32
26
|
export interface SlashCommandManagerOptions {
|
|
@@ -48,7 +42,6 @@ export class SlashCommandManager {
|
|
|
48
42
|
}
|
|
49
43
|
|
|
50
44
|
public initialize(): void {
|
|
51
|
-
this.initializeBuiltinCommands();
|
|
52
45
|
this.loadCustomCommands();
|
|
53
46
|
|
|
54
47
|
// Listen for skill refreshes and update skill commands
|
|
@@ -68,14 +61,6 @@ export class SlashCommandManager {
|
|
|
68
61
|
return this.container.get<AIManager>("AIManager")!;
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
private get backgroundTaskManager(): BackgroundTaskManager {
|
|
72
|
-
return this.container.get<BackgroundTaskManager>("BackgroundTaskManager")!;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private get taskManager(): TaskManager {
|
|
76
|
-
return this.container.get<TaskManager>("TaskManager")!;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
64
|
private get skillManager(): SkillManager {
|
|
80
65
|
return this.container.get<SkillManager>("SkillManager")!;
|
|
81
66
|
}
|
|
@@ -84,206 +69,6 @@ export class SlashCommandManager {
|
|
|
84
69
|
return this.container.get<SubagentManager>("SubagentManager")!;
|
|
85
70
|
}
|
|
86
71
|
|
|
87
|
-
private get memoryService(): MemoryService {
|
|
88
|
-
return this.container.get<MemoryService>("MemoryService")!;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private get hookManager(): HookManager | undefined {
|
|
92
|
-
return this.container.get<HookManager>("HookManager");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private get goalManager(): GoalManager | undefined {
|
|
96
|
-
return this.container.get<GoalManager>("GoalManager");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private initializeBuiltinCommands(): void {
|
|
100
|
-
// Register built-in clear command
|
|
101
|
-
this.registerCommand({
|
|
102
|
-
id: "clear",
|
|
103
|
-
name: "clear",
|
|
104
|
-
description: "Clear conversation history and reset session",
|
|
105
|
-
immediate: true,
|
|
106
|
-
handler: async () => {
|
|
107
|
-
this.aiManager.abortAIMessage();
|
|
108
|
-
|
|
109
|
-
// Clear any active goal
|
|
110
|
-
this.goalManager?.clearGoal();
|
|
111
|
-
|
|
112
|
-
// Capture old session info before clearing
|
|
113
|
-
const oldSessionId = this.messageManager.getSessionId();
|
|
114
|
-
const transcriptPath = this.messageManager.getTranscriptPath();
|
|
115
|
-
|
|
116
|
-
// Run SessionEnd hooks (cleanup before clear)
|
|
117
|
-
if (this.hookManager) {
|
|
118
|
-
try {
|
|
119
|
-
await this.hookManager.executeSessionEndHooks(
|
|
120
|
-
"clear",
|
|
121
|
-
oldSessionId,
|
|
122
|
-
transcriptPath,
|
|
123
|
-
);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
logger?.warn(
|
|
126
|
-
`SessionEnd hooks on clear failed: ${(error as Error).message}`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Clear messages and generate new session
|
|
132
|
-
this.messageManager.clearMessages();
|
|
133
|
-
this.memoryService.clearCache();
|
|
134
|
-
await this.taskManager.syncWithSession();
|
|
135
|
-
|
|
136
|
-
// Run SessionStart hooks (restore context for new session)
|
|
137
|
-
if (this.hookManager) {
|
|
138
|
-
try {
|
|
139
|
-
const newSessionId = this.messageManager.getSessionId();
|
|
140
|
-
const sessionStartResult =
|
|
141
|
-
await this.hookManager.executeSessionStartHooks(
|
|
142
|
-
"clear",
|
|
143
|
-
newSessionId,
|
|
144
|
-
this.messageManager.getTranscriptPath(),
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
// Inject additionalContext as a meta user message
|
|
148
|
-
if (sessionStartResult.additionalContext) {
|
|
149
|
-
this.messageManager.addUserMessage({
|
|
150
|
-
content: `<system-reminder>\nSessionStart hook additional context: ${sessionStartResult.additionalContext}\n</system-reminder>`,
|
|
151
|
-
isMeta: true,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Inject initialUserMessage as a meta user message
|
|
156
|
-
if (sessionStartResult.initialUserMessage) {
|
|
157
|
-
this.messageManager.addUserMessage({
|
|
158
|
-
content: sessionStartResult.initialUserMessage,
|
|
159
|
-
isMeta: true,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
} catch (error) {
|
|
163
|
-
logger?.warn(
|
|
164
|
-
`SessionStart hooks on clear failed: ${(error as Error).message}`,
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// Register built-in compact command
|
|
172
|
-
this.registerCommand({
|
|
173
|
-
id: "compact",
|
|
174
|
-
name: "compact",
|
|
175
|
-
description: "Compact conversation history to reduce context usage",
|
|
176
|
-
immediate: true,
|
|
177
|
-
handler: async (args?: string, signal?: AbortSignal) => {
|
|
178
|
-
this.aiManager.abortAIMessage();
|
|
179
|
-
|
|
180
|
-
const customInstructions = args?.trim() || undefined;
|
|
181
|
-
|
|
182
|
-
await this.aiManager.compactConversation({
|
|
183
|
-
customInstructions,
|
|
184
|
-
abortSignal: signal,
|
|
185
|
-
});
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// Register built-in goal command
|
|
190
|
-
this.registerCommand({
|
|
191
|
-
id: "goal",
|
|
192
|
-
name: "goal",
|
|
193
|
-
description: "Set, check, or clear an autonomous goal for the session",
|
|
194
|
-
immediate: (args?: string) => {
|
|
195
|
-
const trimmed = args?.trim() ?? "";
|
|
196
|
-
return (
|
|
197
|
-
!trimmed ||
|
|
198
|
-
["clear", "stop", "off", "reset", "none", "cancel"].includes(trimmed)
|
|
199
|
-
);
|
|
200
|
-
},
|
|
201
|
-
handler: async (args?: string) => {
|
|
202
|
-
const goalManager = this.goalManager;
|
|
203
|
-
if (!goalManager) {
|
|
204
|
-
this.messageManager.addUserMessage({
|
|
205
|
-
content: "Goal manager is not available",
|
|
206
|
-
isMeta: true,
|
|
207
|
-
});
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const trimmed = args?.trim() ?? "";
|
|
212
|
-
|
|
213
|
-
// Clear aliases
|
|
214
|
-
if (
|
|
215
|
-
["clear", "stop", "off", "reset", "none", "cancel"].includes(trimmed)
|
|
216
|
-
) {
|
|
217
|
-
if (goalManager.isGoalActive()) {
|
|
218
|
-
goalManager.clearGoal();
|
|
219
|
-
this.messageManager.addUserMessage({
|
|
220
|
-
content: "<system-reminder>Goal cleared.</system-reminder>",
|
|
221
|
-
isMeta: true,
|
|
222
|
-
});
|
|
223
|
-
} else {
|
|
224
|
-
this.messageManager.addUserMessage({
|
|
225
|
-
content:
|
|
226
|
-
"<system-reminder>No active goal to clear.</system-reminder>",
|
|
227
|
-
isMeta: true,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Show status
|
|
234
|
-
if (!trimmed) {
|
|
235
|
-
if (goalManager.isGoalActive()) {
|
|
236
|
-
this.messageManager.addUserMessage({
|
|
237
|
-
content: `<system-reminder>${goalManager.getStatusString()}</system-reminder>`,
|
|
238
|
-
isMeta: true,
|
|
239
|
-
});
|
|
240
|
-
} else {
|
|
241
|
-
this.messageManager.addUserMessage({
|
|
242
|
-
content:
|
|
243
|
-
"<system-reminder>No active goal. Use /goal <condition> to set one.</system-reminder>",
|
|
244
|
-
isMeta: true,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Check plan mode
|
|
251
|
-
const permissionMode = this.container.has("PermissionMode")
|
|
252
|
-
? this.container.get<
|
|
253
|
-
import("../types/permissions.js").PermissionMode
|
|
254
|
-
>("PermissionMode")
|
|
255
|
-
: undefined;
|
|
256
|
-
if (permissionMode === "plan") {
|
|
257
|
-
this.messageManager.addUserMessage({
|
|
258
|
-
content:
|
|
259
|
-
"<system-reminder>Cannot set a goal in plan mode. Exit plan mode first.</system-reminder>",
|
|
260
|
-
isMeta: true,
|
|
261
|
-
});
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Set goal
|
|
266
|
-
try {
|
|
267
|
-
goalManager.setGoal(trimmed);
|
|
268
|
-
this.messageManager.addUserMessage({
|
|
269
|
-
content: `<system-reminder>Goal set: ${trimmed}. The agent will work autonomously until this goal is achieved.</system-reminder>`,
|
|
270
|
-
isMeta: true,
|
|
271
|
-
});
|
|
272
|
-
// Add the goal as a user directive to start working
|
|
273
|
-
this.messageManager.addUserMessage({
|
|
274
|
-
content: trimmed,
|
|
275
|
-
});
|
|
276
|
-
this.aiManager.sendAIMessage();
|
|
277
|
-
} catch (error) {
|
|
278
|
-
this.messageManager.addUserMessage({
|
|
279
|
-
content: `<system-reminder>Failed to set goal: ${(error as Error).message}</system-reminder>`,
|
|
280
|
-
isMeta: true,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
72
|
/**
|
|
288
73
|
* Load custom commands from filesystem
|
|
289
74
|
*/
|
|
@@ -364,11 +364,7 @@ class ToolManager {
|
|
|
364
364
|
return false;
|
|
365
365
|
}
|
|
366
366
|
if (effectivePermissionMode === "bypassPermissions") {
|
|
367
|
-
if (
|
|
368
|
-
tool.name === "ExitPlanMode" ||
|
|
369
|
-
tool.name === "AskUserQuestion" ||
|
|
370
|
-
tool.name === "EnterPlanMode"
|
|
371
|
-
) {
|
|
367
|
+
if (tool.name === "ExitPlanMode") {
|
|
372
368
|
return false;
|
|
373
369
|
}
|
|
374
370
|
}
|
|
@@ -376,10 +372,7 @@ class ToolManager {
|
|
|
376
372
|
return effectivePermissionMode === "plan";
|
|
377
373
|
}
|
|
378
374
|
if (tool.name === "EnterPlanMode") {
|
|
379
|
-
return
|
|
380
|
-
effectivePermissionMode !== "plan" &&
|
|
381
|
-
effectivePermissionMode !== "bypassPermissions"
|
|
382
|
-
);
|
|
375
|
+
return effectivePermissionMode !== "plan";
|
|
383
376
|
}
|
|
384
377
|
return true;
|
|
385
378
|
})
|