@ryan_nookpi/pi-extension-subagent 0.5.2 → 0.5.4
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/agents.ts +14 -1
- package/claude-args.ts +2 -1
- package/claude-sdk-runner.ts +8 -4
- package/commands.ts +21 -4
- package/package.json +1 -1
- package/runner.ts +32 -5
- package/tool-execute.ts +36 -7
- package/utils/agent-utils.ts +29 -11
- package/utils/short-label.ts +1 -1
package/agents.ts
CHANGED
|
@@ -21,6 +21,10 @@ export interface AgentConfig {
|
|
|
21
21
|
name: string;
|
|
22
22
|
description: string;
|
|
23
23
|
tools?: string[];
|
|
24
|
+
/** Whether the tools field was explicitly present. Explicit [] disables all tools. */
|
|
25
|
+
toolsConfigured?: boolean;
|
|
26
|
+
/** Discovery-time validation failure. Invalid higher-priority definitions still mask lower-priority agents. */
|
|
27
|
+
configurationError?: string;
|
|
24
28
|
model?: string;
|
|
25
29
|
thinking?: AgentThinkingLevel;
|
|
26
30
|
systemPrompt: string;
|
|
@@ -145,7 +149,14 @@ function loadAgentsFromDir(dir: string, source: "user" | "project", options: Loa
|
|
|
145
149
|
if (typeof frontmatter.name !== "string" || typeof frontmatter.description !== "string") continue;
|
|
146
150
|
if (!frontmatter.name || !frontmatter.description) continue;
|
|
147
151
|
|
|
148
|
-
const
|
|
152
|
+
const toolsConfigured = Object.hasOwn(frontmatter, "tools");
|
|
153
|
+
let tools: string[] | undefined;
|
|
154
|
+
let configurationError: string | undefined;
|
|
155
|
+
try {
|
|
156
|
+
tools = toolsConfigured ? normalizeTools(frontmatter.tools, format) : undefined;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
configurationError = `${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
159
|
+
}
|
|
149
160
|
const model = normalizeModel(typeof frontmatter.model === "string" ? frontmatter.model : undefined, format);
|
|
150
161
|
const thinking = normalizeThinkingLevel(
|
|
151
162
|
typeof frontmatter.thinking === "string" ? frontmatter.thinking : undefined,
|
|
@@ -156,6 +167,8 @@ function loadAgentsFromDir(dir: string, source: "user" | "project", options: Loa
|
|
|
156
167
|
name: frontmatter.name,
|
|
157
168
|
description: frontmatter.description,
|
|
158
169
|
tools,
|
|
170
|
+
toolsConfigured,
|
|
171
|
+
configurationError,
|
|
159
172
|
model,
|
|
160
173
|
thinking,
|
|
161
174
|
systemPrompt: attachCommonSubagentRule(body, runtime),
|
package/claude-args.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { mapPiToolsToClaude } from "./utils/agent-utils.js";
|
|
|
5
5
|
export interface ClaudeArgsConfig {
|
|
6
6
|
prompt: string;
|
|
7
7
|
tools: string[];
|
|
8
|
+
toolsConfigured?: boolean;
|
|
8
9
|
model?: string;
|
|
9
10
|
thinking?: string;
|
|
10
11
|
resumeSessionId?: string;
|
|
@@ -63,7 +64,7 @@ export function buildClaudeArgs(config: ClaudeArgsConfig): string[] {
|
|
|
63
64
|
|
|
64
65
|
const claudeTools = mapPiToolsToClaude(config.tools);
|
|
65
66
|
|
|
66
|
-
if (claudeTools.length > 0) {
|
|
67
|
+
if (config.toolsConfigured || claudeTools.length > 0) {
|
|
67
68
|
args.push("--tools", claudeTools.join(","));
|
|
68
69
|
args.push("--allowedTools", claudeTools.join(","));
|
|
69
70
|
}
|
package/claude-sdk-runner.ts
CHANGED
|
@@ -31,8 +31,12 @@ function normalizeTaskForSubagentPrompt(task: string): string {
|
|
|
31
31
|
return task;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function resolveClaudeSdkBuiltInTools(
|
|
35
|
-
|
|
34
|
+
function resolveClaudeSdkBuiltInTools(
|
|
35
|
+
requestedTools: string[] | undefined,
|
|
36
|
+
toolsConfigured: boolean,
|
|
37
|
+
): string[] | undefined {
|
|
38
|
+
if (!toolsConfigured) return undefined;
|
|
39
|
+
if (!requestedTools || requestedTools.length === 0) return [];
|
|
36
40
|
return mapPiToolsToClaude(requestedTools);
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -74,7 +78,7 @@ export async function runClaudeAgentViaSdk(
|
|
|
74
78
|
|
|
75
79
|
let enabledTools: string[] | undefined;
|
|
76
80
|
try {
|
|
77
|
-
enabledTools = resolveClaudeSdkBuiltInTools(agent.tools);
|
|
81
|
+
enabledTools = resolveClaudeSdkBuiltInTools(agent.tools, agent.toolsConfigured ?? agent.tools !== undefined);
|
|
78
82
|
} catch (error: any) {
|
|
79
83
|
return {
|
|
80
84
|
agent: agent.name,
|
|
@@ -140,7 +144,7 @@ export async function runClaudeAgentViaSdk(
|
|
|
140
144
|
settingSources: [],
|
|
141
145
|
includePartialMessages: true,
|
|
142
146
|
persistSession: true,
|
|
143
|
-
...(enabledTools ? { tools: enabledTools, allowedTools: enabledTools } : {}),
|
|
147
|
+
...(enabledTools !== undefined ? { tools: enabledTools, allowedTools: enabledTools } : {}),
|
|
144
148
|
resume: resumeSessionId,
|
|
145
149
|
stderr: (data) => {
|
|
146
150
|
stderrBuf += data;
|
package/commands.ts
CHANGED
|
@@ -969,9 +969,15 @@ export function registerAll(pi: ExtensionAPI, store: SubagentStore): SubagentReg
|
|
|
969
969
|
const lines = agents.map((agent) => {
|
|
970
970
|
const model = agent.model ?? "(inherit current model)";
|
|
971
971
|
const thinking = agent.thinking ?? "(inherit current thinking)";
|
|
972
|
-
const tools =
|
|
972
|
+
const tools =
|
|
973
|
+
(agent.toolsConfigured ?? agent.tools !== undefined)
|
|
974
|
+
? agent.tools?.length
|
|
975
|
+
? agent.tools.join(",")
|
|
976
|
+
: "none"
|
|
977
|
+
: "default";
|
|
973
978
|
const description = agent.description ? ` · ${agent.description}` : "";
|
|
974
|
-
|
|
979
|
+
const invalid = agent.configurationError ? ` · INVALID: ${agent.configurationError}` : "";
|
|
980
|
+
return `${agent.name} [${agent.source}] · model: ${model} · thinking: ${thinking} · tools: ${tools}${description}${invalid}`;
|
|
975
981
|
});
|
|
976
982
|
|
|
977
983
|
return {
|
|
@@ -1230,6 +1236,15 @@ export function registerAll(pi: ExtensionAPI, store: SubagentStore): SubagentReg
|
|
|
1230
1236
|
taskForAgent = taskForDisplay;
|
|
1231
1237
|
}
|
|
1232
1238
|
|
|
1239
|
+
const selectedConfig = agents.find((agent) => agent.name === selectedAgent);
|
|
1240
|
+
if (selectedConfig?.configurationError) {
|
|
1241
|
+
ctx.ui.notify(
|
|
1242
|
+
`Invalid agent configuration for "${selectedAgent}": ${selectedConfig.configurationError}`,
|
|
1243
|
+
"error",
|
|
1244
|
+
);
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1233
1248
|
let runId: number;
|
|
1234
1249
|
let runState: CommandRunState;
|
|
1235
1250
|
|
|
@@ -1671,14 +1686,16 @@ export function registerAll(pi: ExtensionAPI, store: SubagentStore): SubagentReg
|
|
|
1671
1686
|
if (starterPackNotice) ctx.ui.notify(starterPackNotice, "info");
|
|
1672
1687
|
|
|
1673
1688
|
const lines = agents.map((a) => {
|
|
1674
|
-
const tools =
|
|
1689
|
+
const tools =
|
|
1690
|
+
(a.toolsConfigured ?? a.tools !== undefined) ? (a.tools?.length ? a.tools.join(",") : "none") : "default";
|
|
1675
1691
|
const model = a.model ?? "(inherit current model)";
|
|
1676
1692
|
const thinking = a.thinking ?? "(inherit current thinking)";
|
|
1677
1693
|
const description = a.description ? ` · ${a.description}` : "";
|
|
1694
|
+
const invalid = a.configurationError ? ` · INVALID: ${a.configurationError}` : "";
|
|
1678
1695
|
const colorCode = AGENT_NAME_PALETTE[agentBgIndex(a.name)];
|
|
1679
1696
|
const coloredName = `\x1b[38;5;${colorCode}m${a.name}\x1b[39m`;
|
|
1680
1697
|
return truncateText(
|
|
1681
|
-
`${coloredName} [${a.source}] · model: ${model} · thinking: ${thinking} · tools: ${tools}${description}`,
|
|
1698
|
+
`${coloredName} [${a.source}] · model: ${model} · thinking: ${thinking} · tools: ${tools}${description}${invalid}`,
|
|
1682
1699
|
220,
|
|
1683
1700
|
);
|
|
1684
1701
|
});
|
package/package.json
CHANGED
package/runner.ts
CHANGED
|
@@ -82,6 +82,12 @@ function appendStderrDiagnostic(result: SingleResult, message: string): void {
|
|
|
82
82
|
result.stderr = result.stderr ? `${result.stderr.trimEnd()}\n${line}\n` : `${line}\n`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function normalizeChildExitCode(code: number | null, signal: NodeJS.Signals | null, fallbackCode = 0): number {
|
|
86
|
+
if (code !== null) return code;
|
|
87
|
+
if (signal !== null) return 1;
|
|
88
|
+
return fallbackCode;
|
|
89
|
+
}
|
|
90
|
+
|
|
85
91
|
/**
|
|
86
92
|
* Prevent tasks starting with `/` from being treated as slash commands
|
|
87
93
|
* inside the spawned pi process.
|
|
@@ -225,6 +231,19 @@ export async function runSingleAgent(
|
|
|
225
231
|
};
|
|
226
232
|
}
|
|
227
233
|
|
|
234
|
+
if (agent.configurationError) {
|
|
235
|
+
return {
|
|
236
|
+
agent: agent.name,
|
|
237
|
+
agentSource: agent.source,
|
|
238
|
+
task,
|
|
239
|
+
exitCode: 1,
|
|
240
|
+
messages: [],
|
|
241
|
+
stderr: `Invalid agent configuration: ${agent.configurationError}`,
|
|
242
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
243
|
+
step,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
228
247
|
if (agent.runtime === "claude") {
|
|
229
248
|
if (resolveClaudeRuntimeMode(defaultCwd) === "sdk") {
|
|
230
249
|
const { runClaudeAgentViaSdk } = await import("./claude-sdk-runner.js");
|
|
@@ -364,6 +383,7 @@ async function runClaudeAgent(
|
|
|
364
383
|
const args = buildClaudeArgs({
|
|
365
384
|
prompt: normalizeTaskForSubagentPrompt(task),
|
|
366
385
|
tools: agent.tools ?? [],
|
|
386
|
+
toolsConfigured: agent.toolsConfigured ?? agent.tools !== undefined,
|
|
367
387
|
model: agent.model,
|
|
368
388
|
thinking: agent.thinking,
|
|
369
389
|
resumeSessionId: resumeId,
|
|
@@ -545,7 +565,7 @@ async function runClaudeAgent(
|
|
|
545
565
|
|
|
546
566
|
proc.on("exit", (code, terminationSignal) => {
|
|
547
567
|
procExited = true;
|
|
548
|
-
lastExitCode = code
|
|
568
|
+
lastExitCode = normalizeChildExitCode(code, terminationSignal, lastExitCode);
|
|
549
569
|
lastProcessSignal = terminationSignal;
|
|
550
570
|
diagnose({ event: "exit", childPid: proc.pid, code, signal: terminationSignal });
|
|
551
571
|
if (streamState.resultReceived) {
|
|
@@ -553,6 +573,9 @@ async function runClaudeAgent(
|
|
|
553
573
|
resolveOnce(0);
|
|
554
574
|
return;
|
|
555
575
|
}
|
|
576
|
+
if (terminationSignal && !settled && !wasAborted) {
|
|
577
|
+
stderrBuf += `[runner] process terminated by signal ${terminationSignal}\n`;
|
|
578
|
+
}
|
|
556
579
|
exitFallbackTimer = setTimeout(() => {
|
|
557
580
|
settleReason = "exit_fallback_timeout";
|
|
558
581
|
resolveOnce(lastExitCode);
|
|
@@ -565,7 +588,7 @@ async function runClaudeAgent(
|
|
|
565
588
|
diagnose({ event: "close", childPid: proc.pid, code, signal: terminationSignal });
|
|
566
589
|
if (!settled) {
|
|
567
590
|
settleReason = "close";
|
|
568
|
-
resolveOnce(streamState.resultReceived ? 0 : (code
|
|
591
|
+
resolveOnce(streamState.resultReceived ? 0 : normalizeChildExitCode(code, lastProcessSignal, lastExitCode));
|
|
569
592
|
}
|
|
570
593
|
});
|
|
571
594
|
|
|
@@ -640,7 +663,8 @@ async function runPiAgent(
|
|
|
640
663
|
else args.push("--no-session");
|
|
641
664
|
if (agent.model) args.push("--model", agent.model);
|
|
642
665
|
if (agent.thinking) args.push("--thinking", agent.thinking);
|
|
643
|
-
if (agent.tools && agent.tools
|
|
666
|
+
if ((agent.toolsConfigured ?? agent.tools !== undefined) && agent.tools?.length === 0) args.push("--no-tools");
|
|
667
|
+
else if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
644
668
|
|
|
645
669
|
let tmpPromptDir: string | null = null;
|
|
646
670
|
let tmpPromptPath: string | null = null;
|
|
@@ -1066,9 +1090,12 @@ async function runPiAgent(
|
|
|
1066
1090
|
|
|
1067
1091
|
proc.on("exit", (code, terminationSignal) => {
|
|
1068
1092
|
procExited = true;
|
|
1069
|
-
lastExitCode = code
|
|
1093
|
+
lastExitCode = normalizeChildExitCode(code, terminationSignal, lastExitCode);
|
|
1070
1094
|
lastProcessSignal = terminationSignal;
|
|
1071
1095
|
diagnose({ event: "exit", childPid: proc.pid, code, signal: terminationSignal });
|
|
1096
|
+
if (terminationSignal && !settled && !wasAborted) {
|
|
1097
|
+
appendStderrDiagnostic(currentResult, `process terminated by signal ${terminationSignal}`);
|
|
1098
|
+
}
|
|
1072
1099
|
// In rare cases stdout/stderr pipes may stay open after process exit.
|
|
1073
1100
|
// Use a short fallback so runs cannot stay "running" forever.
|
|
1074
1101
|
exitFallbackTimer = setTimeout(() => {
|
|
@@ -1082,7 +1109,7 @@ async function runPiAgent(
|
|
|
1082
1109
|
lastProcessSignal = terminationSignal ?? lastProcessSignal;
|
|
1083
1110
|
diagnose({ event: "close", childPid: proc.pid, code, signal: terminationSignal });
|
|
1084
1111
|
settleReason = "close";
|
|
1085
|
-
resolveOnce(code
|
|
1112
|
+
resolveOnce(normalizeChildExitCode(code, lastProcessSignal, lastExitCode));
|
|
1086
1113
|
});
|
|
1087
1114
|
|
|
1088
1115
|
proc.on("error", (error) => {
|
package/tool-execute.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { createRunActivityRecorder } from "./activity.js";
|
|
12
|
-
import { discoverAgents } from "./agents.js";
|
|
12
|
+
import { type AgentConfig, discoverAgents } from "./agents.js";
|
|
13
13
|
import { parseSubagentToolCommand, SUBAGENT_CLI_HELP_TEXT } from "./cli.js";
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_TURN_COUNT,
|
|
@@ -313,8 +313,15 @@ export function diagnoseResultFailure(result: SingleResult): ResultFailureDiagno
|
|
|
313
313
|
};
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
|
-
if (result.exitCode !== 0)
|
|
317
|
-
|
|
316
|
+
if (result.exitCode !== 0) {
|
|
317
|
+
const detail = (result.errorMessage || result.stderr || "").trim();
|
|
318
|
+
const detailPreview = detail ? truncateLines(detail, 4).slice(0, STATUS_OUTPUT_PREVIEW_MAX_CHARS).trimEnd() : "";
|
|
319
|
+
return {
|
|
320
|
+
failed: true,
|
|
321
|
+
errorClass,
|
|
322
|
+
reason: `Subagent process exited with code ${result.exitCode}.${detailPreview ? ` Cause: ${detailPreview}` : ""}`,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
318
325
|
if (result.stopReason === "error")
|
|
319
326
|
return { failed: true, errorClass, reason: result.errorMessage || "Subagent reported stopReason=error." };
|
|
320
327
|
if (result.stopReason === "aborted")
|
|
@@ -810,9 +817,15 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
810
817
|
const lines = agents.map((agent) => {
|
|
811
818
|
const model = agent.model ?? "(inherit current model)";
|
|
812
819
|
const thinking = agent.thinking ?? "(inherit current thinking)";
|
|
813
|
-
const tools =
|
|
820
|
+
const tools =
|
|
821
|
+
(agent.toolsConfigured ?? agent.tools !== undefined)
|
|
822
|
+
? agent.tools?.length
|
|
823
|
+
? agent.tools.join(",")
|
|
824
|
+
: "none"
|
|
825
|
+
: "default";
|
|
814
826
|
const description = agent.description ? ` · ${agent.description}` : "";
|
|
815
|
-
|
|
827
|
+
const invalid = agent.configurationError ? ` · INVALID: ${agent.configurationError}` : "";
|
|
828
|
+
return `${agent.name} [${agent.source}] · model: ${model} · thinking: ${thinking} · tools: ${tools}${description}${invalid}`;
|
|
816
829
|
});
|
|
817
830
|
|
|
818
831
|
return {
|
|
@@ -902,6 +915,24 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
902
915
|
isError: true,
|
|
903
916
|
};
|
|
904
917
|
}
|
|
918
|
+
|
|
919
|
+
const invalidAgents = [...new Set(requestedNames)]
|
|
920
|
+
.map((name) => agents.find((agent) => agent.name === name))
|
|
921
|
+
.filter((agent): agent is AgentConfig => Boolean(agent?.configurationError));
|
|
922
|
+
if (invalidAgents.length > 0) {
|
|
923
|
+
return {
|
|
924
|
+
content: [
|
|
925
|
+
{
|
|
926
|
+
type: "text",
|
|
927
|
+
text: `❌ Invalid agent configuration:\n${invalidAgents
|
|
928
|
+
.map((agent) => `- ${agent.name}: ${agent.configurationError}`)
|
|
929
|
+
.join("\n")}`,
|
|
930
|
+
},
|
|
931
|
+
],
|
|
932
|
+
details: makeDetails(),
|
|
933
|
+
isError: true,
|
|
934
|
+
};
|
|
935
|
+
}
|
|
905
936
|
}
|
|
906
937
|
|
|
907
938
|
if (asyncAction === "list") {
|
|
@@ -1442,7 +1473,6 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
1442
1473
|
existingRunState: continueFromRun,
|
|
1443
1474
|
});
|
|
1444
1475
|
const launchSummary = toLaunchSummary(runState, continueFromRun ? "continue" : "run");
|
|
1445
|
-
const startedState = continueFromRun ? "resumed" : "started";
|
|
1446
1476
|
|
|
1447
1477
|
if (!shouldRunAsync) {
|
|
1448
1478
|
let finalized: FinalizedRun;
|
|
@@ -1475,7 +1505,6 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
1475
1505
|
};
|
|
1476
1506
|
}
|
|
1477
1507
|
|
|
1478
|
-
pi.sendMessage(buildRunStartMessage(runState, startedState), { deliverAs: "followUp", triggerTurn: false });
|
|
1479
1508
|
ctx.ui?.notify?.(
|
|
1480
1509
|
`${continueFromRun ? `Resumed subagent #${runState.id}` : `Started subagent #${runState.id}`}: ${resolvedAgent}`,
|
|
1481
1510
|
"info",
|
package/utils/agent-utils.ts
CHANGED
|
@@ -89,20 +89,38 @@ export const CLAUDE_MODEL_ALIAS_MAP: Record<string, string> = {
|
|
|
89
89
|
* For "claude" format, maps Claude tool names to pi equivalents.
|
|
90
90
|
*/
|
|
91
91
|
export function normalizeTools(rawTools: unknown, format: "pi" | "claude"): string[] | undefined {
|
|
92
|
-
|
|
93
|
-
.filter((tool): tool is string => typeof tool === "string")
|
|
94
|
-
.map((tool) => tool.trim())
|
|
95
|
-
.filter(Boolean);
|
|
92
|
+
if (rawTools === undefined) return undefined;
|
|
96
93
|
|
|
97
|
-
|
|
98
|
-
if (
|
|
94
|
+
let parsed: string[];
|
|
95
|
+
if (Array.isArray(rawTools)) {
|
|
96
|
+
if (rawTools.some((tool) => typeof tool !== "string" || tool.trim().length === 0)) {
|
|
97
|
+
throw new Error("Explicit tools must contain only non-empty strings.");
|
|
98
|
+
}
|
|
99
|
+
parsed = rawTools.map((tool) => (tool as string).trim());
|
|
100
|
+
} else if (typeof rawTools === "string") {
|
|
101
|
+
parsed = rawTools
|
|
102
|
+
.split(",")
|
|
103
|
+
.map((tool) => tool.trim())
|
|
104
|
+
.filter(Boolean);
|
|
105
|
+
if (parsed.length === 0) throw new Error("Explicit tools must not be an empty string.");
|
|
106
|
+
} else {
|
|
107
|
+
throw new Error("Explicit tools must be a comma-separated string or an array of strings.");
|
|
108
|
+
}
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
.map((tool) => CLAUDE_TOOL_MAP[tool.toLowerCase()] ?? undefined)
|
|
102
|
-
.filter((tool): tool is string => Boolean(tool));
|
|
110
|
+
if (format === "pi") return Array.from(new Set(parsed));
|
|
103
111
|
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
const unsupported = parsed.filter((tool) => !CLAUDE_TOOL_MAP[tool.toLowerCase()]);
|
|
113
|
+
if (unsupported.length > 0) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Unsupported Claude tool${unsupported.length > 1 ? "s" : ""}: ${unsupported.join(", ")}. Supported tools: ${Object.keys(
|
|
116
|
+
CLAUDE_TOOL_MAP,
|
|
117
|
+
)
|
|
118
|
+
.filter((tool) => CLAUDE_TOOL_MAP[tool])
|
|
119
|
+
.join(", ")}`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return Array.from(new Set(parsed.map((tool) => CLAUDE_TOOL_MAP[tool.toLowerCase()] as string)));
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
/**
|
package/utils/short-label.ts
CHANGED