@melihmucuk/pi-crew 1.0.17 → 1.0.19

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 (35) hide show
  1. package/agents/code-reviewer.md +16 -11
  2. package/agents/quality-reviewer.md +8 -17
  3. package/extension/catalog.ts +543 -0
  4. package/extension/crew.ts +377 -0
  5. package/extension/index.ts +35 -18
  6. package/extension/subagent-session.ts +257 -0
  7. package/extension/tools.ts +323 -0
  8. package/extension/ui.ts +291 -0
  9. package/package.json +6 -6
  10. package/prompts/pi-crew-review.md +25 -16
  11. package/skills/pi-crew/SKILL.md +3 -1
  12. package/extension/agent-catalog.ts +0 -369
  13. package/extension/agent-config-fields.ts +0 -359
  14. package/extension/agent-discovery.ts +0 -123
  15. package/extension/bootstrap-session.ts +0 -131
  16. package/extension/integration/crew-tool-actions.ts +0 -306
  17. package/extension/integration/crew-tool-executor.ts +0 -109
  18. package/extension/integration/register-renderers.ts +0 -77
  19. package/extension/integration/register-tools.ts +0 -47
  20. package/extension/integration/tool-presentation.ts +0 -30
  21. package/extension/integration/tools/crew-abort.ts +0 -56
  22. package/extension/integration/tools/crew-done.ts +0 -27
  23. package/extension/integration/tools/crew-list.ts +0 -36
  24. package/extension/integration/tools/crew-respond.ts +0 -38
  25. package/extension/integration/tools/crew-spawn.ts +0 -46
  26. package/extension/message-delivery-policy.ts +0 -22
  27. package/extension/runtime/crew-runtime.ts +0 -263
  28. package/extension/runtime/overflow-recovery.ts +0 -211
  29. package/extension/runtime/owner-session-coordinator.ts +0 -138
  30. package/extension/runtime/subagent-lifecycle.ts +0 -203
  31. package/extension/runtime/subagent-registry.ts +0 -122
  32. package/extension/runtime/subagent-transitions.ts +0 -100
  33. package/extension/status-widget.ts +0 -107
  34. package/extension/subagent-messages.ts +0 -116
  35. package/extension/tool-registry.ts +0 -19
@@ -1,123 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
- import {
6
- AgentCatalog,
7
- type AgentCatalogSource,
8
- type AgentConfigFile,
9
- type AgentDefinitionFile,
10
- type AgentDefinitionSourceGroup,
11
- type AgentDiscoveryResult,
12
- type AgentDiscoveryWarning,
13
- } from "./agent-catalog.js";
14
-
15
- export type {
16
- AgentConfig,
17
- AgentDiscoveryResult,
18
- AgentDiscoveryWarning,
19
- } from "./agent-catalog.js";
20
-
21
- function createDiscoveryWarning(filePath: string, message: string): AgentDiscoveryWarning {
22
- return { filePath, message };
23
- }
24
-
25
- function loadAgentFile(filePath: string): AgentDefinitionFile {
26
- try {
27
- return {
28
- filePath,
29
- content: fs.readFileSync(filePath, "utf-8"),
30
- };
31
- } catch (error) {
32
- const reason = error instanceof Error ? error.message : String(error);
33
- return {
34
- filePath,
35
- content: null,
36
- warnings: [
37
- createDiscoveryWarning(
38
- filePath,
39
- `Ignored subagent definition. File could not be read: ${reason}`,
40
- ),
41
- ],
42
- };
43
- }
44
- }
45
-
46
- function loadAgentDefinitionGroup(agentsDir: string): AgentDefinitionSourceGroup | null {
47
- if (!fs.existsSync(agentsDir)) return null;
48
-
49
- let entries: fs.Dirent[];
50
- try {
51
- entries = fs.readdirSync(agentsDir, { withFileTypes: true });
52
- } catch (error) {
53
- const reason = error instanceof Error ? error.message : String(error);
54
- return {
55
- agentsDir,
56
- files: [],
57
- warnings: [
58
- createDiscoveryWarning(
59
- agentsDir,
60
- `Subagent directory could not be read: ${reason}`,
61
- ),
62
- ],
63
- };
64
- }
65
-
66
- return {
67
- agentsDir,
68
- files: entries
69
- .filter((entry) => entry.name.endsWith(".md"))
70
- .filter((entry) => entry.isFile() || entry.isSymbolicLink())
71
- .map((entry) => loadAgentFile(path.join(agentsDir, entry.name))),
72
- };
73
- }
74
-
75
- function loadConfigFile(filePath: string): AgentConfigFile | null {
76
- if (!fs.existsSync(filePath)) return null;
77
-
78
- try {
79
- return {
80
- filePath,
81
- content: fs.readFileSync(filePath, "utf-8"),
82
- };
83
- } catch (error) {
84
- const reason = error instanceof Error ? error.message : String(error);
85
- return {
86
- filePath,
87
- content: null,
88
- warnings: [
89
- createDiscoveryWarning(
90
- filePath,
91
- `Ignored pi-crew config. File could not be read: ${reason}`,
92
- ),
93
- ],
94
- };
95
- }
96
- }
97
-
98
- const bundledAgentsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "agents");
99
-
100
- class FilesystemAgentCatalogSource implements AgentCatalogSource {
101
- loadAgentDefinitionGroups(cwd: string): AgentDefinitionSourceGroup[] {
102
- return [
103
- path.join(cwd, ".pi", "agents"),
104
- path.join(getAgentDir(), "agents"),
105
- bundledAgentsDir,
106
- ]
107
- .map(loadAgentDefinitionGroup)
108
- .filter((group): group is AgentDefinitionSourceGroup => group !== null);
109
- }
110
-
111
- loadConfigFiles(cwd: string): AgentConfigFile[] {
112
- return [
113
- path.join(getAgentDir(), "pi-crew.json"),
114
- path.join(cwd, ".pi", "pi-crew.json"),
115
- ]
116
- .map(loadConfigFile)
117
- .filter((file): file is AgentConfigFile => file !== null);
118
- }
119
- }
120
-
121
- export function discoverAgents(cwd: string = process.cwd()): AgentDiscoveryResult {
122
- return new AgentCatalog(new FilesystemAgentCatalogSource()).discover(cwd);
123
- }
@@ -1,131 +0,0 @@
1
- import {
2
- type AgentSession,
3
- createAgentSession,
4
- DefaultResourceLoader,
5
- type ModelRegistry,
6
- SessionManager,
7
- SettingsManager,
8
- } from "@earendil-works/pi-coding-agent";
9
- import type { Api, Model } from "@earendil-works/pi-ai";
10
- import type { AgentConfig } from "./agent-discovery.js";
11
- import { SUPPORTED_TOOL_NAMES, type SupportedToolName } from "./tool-registry.js";
12
-
13
- function resolveTools(agentConfig: AgentConfig): SupportedToolName[] {
14
- return [...(agentConfig.tools ?? SUPPORTED_TOOL_NAMES)];
15
- }
16
-
17
- function resolveModel(agentConfig: AgentConfig, ctx: BootstrapContext): { model: Model<Api> | undefined; warnings: string[] } {
18
- const warnings: string[] = [];
19
- const model = ctx.model;
20
- if (!agentConfig.parsedModel) return { model, warnings };
21
-
22
- const found = ctx.modelRegistry.find(
23
- agentConfig.parsedModel.provider,
24
- agentConfig.parsedModel.modelId,
25
- );
26
- if (found) return { model: found, warnings };
27
-
28
- warnings.push(
29
- `Model "${agentConfig.model}" not found, using current session model`,
30
- );
31
- return { model, warnings };
32
- }
33
-
34
- function getSkillWarnings(
35
- agentConfig: AgentConfig,
36
- resourceLoader: DefaultResourceLoader,
37
- ): string[] {
38
- const warnings: string[] = [];
39
- if (!agentConfig.skills) return warnings;
40
-
41
- const availableSkillNames = new Set(
42
- resourceLoader.getSkills().skills.map((skill) => skill.name),
43
- );
44
- for (const skillName of agentConfig.skills) {
45
- if (!availableSkillNames.has(skillName)) {
46
- warnings.push(
47
- `Unknown skill "${skillName}" in subagent config, skipping`,
48
- );
49
- }
50
- }
51
- return warnings;
52
- }
53
-
54
- export interface BootstrapContext {
55
- model: Model<Api> | undefined;
56
- modelRegistry: ModelRegistry;
57
- agentDir: string;
58
- parentSessionFile?: string;
59
- }
60
-
61
- interface BootstrapOptions {
62
- agentConfig: AgentConfig;
63
- cwd: string;
64
- ctx: BootstrapContext;
65
- extensionResolvedPath: string;
66
- }
67
-
68
- export interface BootstrapResult {
69
- session: AgentSession;
70
- warnings: string[];
71
- }
72
-
73
- export async function bootstrapSession(
74
- opts: BootstrapOptions,
75
- ): Promise<BootstrapResult> {
76
- const warnings: string[] = [];
77
- const { agentConfig, cwd, ctx, extensionResolvedPath } = opts;
78
-
79
- const authStorage = ctx.modelRegistry.authStorage;
80
- const modelRegistry = ctx.modelRegistry;
81
- const { model, warnings: modelWarnings } = resolveModel(agentConfig, ctx);
82
- warnings.push(...modelWarnings);
83
- const tools = resolveTools(agentConfig);
84
-
85
- const resourceLoader = new DefaultResourceLoader({
86
- cwd,
87
- agentDir: ctx.agentDir,
88
- extensionsOverride: (base) => ({
89
- ...base,
90
- extensions: base.extensions.filter(
91
- (ext) => !ext.resolvedPath.startsWith(extensionResolvedPath),
92
- ),
93
- }),
94
- skillsOverride: agentConfig.skills
95
- ? (base) => ({
96
- skills: base.skills.filter((skill) =>
97
- agentConfig.skills!.includes(skill.name),
98
- ),
99
- diagnostics: base.diagnostics,
100
- })
101
- : undefined,
102
- appendSystemPromptOverride: (base) =>
103
- agentConfig.systemPrompt.trim()
104
- ? [...base, agentConfig.systemPrompt]
105
- : base,
106
- });
107
- await resourceLoader.reload();
108
- warnings.push(...getSkillWarnings(agentConfig, resourceLoader));
109
-
110
- const settingsManager = SettingsManager.inMemory({
111
- compaction: { enabled: agentConfig.compaction ?? true },
112
- });
113
-
114
- const sessionManager = SessionManager.create(cwd);
115
- sessionManager.newSession({ parentSession: ctx.parentSessionFile });
116
-
117
- const result = await createAgentSession({
118
- cwd,
119
- agentDir: ctx.agentDir,
120
- model,
121
- thinkingLevel: agentConfig.thinking,
122
- tools,
123
- resourceLoader,
124
- sessionManager,
125
- settingsManager,
126
- authStorage,
127
- modelRegistry,
128
- });
129
-
130
- return { session: result.session, warnings };
131
- }
@@ -1,306 +0,0 @@
1
- import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
- import type { Api, Model } from "@earendil-works/pi-ai";
3
- import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
4
- import type {
5
- AgentConfig,
6
- AgentDiscoveryResult,
7
- AgentDiscoveryWarning,
8
- } from "../agent-discovery.js";
9
- import type {
10
- AbortOwnedResult,
11
- ActiveAgentSummary,
12
- CrewRuntime,
13
- } from "../runtime/crew-runtime.js";
14
- import { STATUS_ICON } from "../subagent-messages.js";
15
-
16
- export type CrewToolResult = AgentToolResult<unknown> & {
17
- isError?: boolean;
18
- terminate?: boolean;
19
- };
20
-
21
- export type CrewToolActionSideEffect =
22
- | { type: "discovery-warnings"; warnings: AgentDiscoveryWarning[] }
23
- | { type: "active-list-warning" };
24
-
25
- export interface CrewToolActionResponse {
26
- result: CrewToolResult;
27
- sideEffects: CrewToolActionSideEffect[];
28
- }
29
-
30
- export interface CrewToolActionContext {
31
- cwd: string;
32
- callerSessionId: string;
33
- }
34
-
35
- export interface CrewSpawnActionContext extends CrewToolActionContext {
36
- model: Model<Api> | undefined;
37
- modelRegistry: ModelRegistry;
38
- agentDir: string;
39
- parentSessionFile?: string;
40
- onWarning?: (message: string) => void;
41
- }
42
-
43
- interface CrewToolRuntime {
44
- spawn: CrewRuntime["spawn"];
45
- abortAllOwned: CrewRuntime["abortAllOwned"];
46
- abortOwned: CrewRuntime["abortOwned"];
47
- respond: CrewRuntime["respond"];
48
- done: CrewRuntime["done"];
49
- getActiveSummariesForOwner: CrewRuntime["getActiveSummariesForOwner"];
50
- }
51
-
52
- interface CreateCrewToolActionsDeps {
53
- crew: CrewToolRuntime;
54
- discoverAgents: (cwd: string) => AgentDiscoveryResult;
55
- extensionDir: string;
56
- }
57
-
58
- function toolError(text: string): CrewToolResult {
59
- return {
60
- content: [{ type: "text", text }],
61
- isError: true,
62
- details: { error: true },
63
- };
64
- }
65
-
66
- function toolSuccess(
67
- text: string,
68
- details: Record<string, unknown> = {},
69
- options: { terminate?: boolean } = {},
70
- ): CrewToolResult {
71
- return {
72
- content: [{ type: "text", text }],
73
- details,
74
- ...(options.terminate ? { terminate: true } : {}),
75
- };
76
- }
77
-
78
- function response(
79
- result: CrewToolResult,
80
- sideEffects: CrewToolActionSideEffect[] = [],
81
- ): CrewToolActionResponse {
82
- return { result, sideEffects };
83
- }
84
-
85
- function discoveryWarningSideEffect(
86
- warnings: AgentDiscoveryWarning[],
87
- ): CrewToolActionSideEffect[] {
88
- return warnings.length > 0 ? [{ type: "discovery-warnings", warnings }] : [];
89
- }
90
-
91
- function formatAbortToolMessage(result: AbortOwnedResult): string {
92
- const parts: string[] = [];
93
-
94
- if (result.abortedIds.length > 0) {
95
- parts.push(`Aborted ${result.abortedIds.length} subagent(s): ${result.abortedIds.join(", ")}`);
96
- }
97
- if (result.missingIds.length > 0) {
98
- parts.push(`Not found or already finished: ${result.missingIds.join(", ")}`);
99
- }
100
- if (result.foreignIds.length > 0) {
101
- parts.push(`Belong to a different session: ${result.foreignIds.join(", ")}`);
102
- }
103
-
104
- return parts.join("\n");
105
- }
106
-
107
- function formatAvailableAgents(agents: AgentConfig[]): string[] {
108
- if (agents.length === 0) {
109
- return [
110
- "No valid subagent definitions found. Add `.md` files to `<cwd>/.pi/agents/` or `~/.pi/agent/agents/`.",
111
- ];
112
- }
113
-
114
- return agents.flatMap((agent) => [
115
- "",
116
- `name: ${agent.name}`,
117
- `description: ${agent.description}`,
118
- `interactive: ${agent.interactive ? "true" : "false"}`,
119
- ]);
120
- }
121
-
122
- function formatWarnings(warnings: AgentDiscoveryWarning[]): string[] {
123
- if (warnings.length === 0) return [];
124
-
125
- return [
126
- "",
127
- "## Ignored subagent definitions",
128
- ...warnings.map((warning) => `- ${warning.message} (${warning.filePath})`),
129
- ];
130
- }
131
-
132
- function formatActiveAgents(running: ActiveAgentSummary[]): string[] {
133
- if (running.length === 0) return ["No subagents currently active."];
134
-
135
- return running.flatMap((agent) => {
136
- const icon = STATUS_ICON[agent.status] ?? "❓";
137
- return [
138
- "",
139
- `id: ${agent.id}`,
140
- `name: ${agent.agentName}`,
141
- `status: ${icon} ${agent.status}`,
142
- ];
143
- });
144
- }
145
-
146
- export type CrewToolActions = ReturnType<typeof createCrewToolActions>;
147
-
148
- export function createCrewToolActions({
149
- crew,
150
- discoverAgents,
151
- extensionDir,
152
- }: CreateCrewToolActionsDeps) {
153
- return {
154
- list(ctx: CrewToolActionContext): CrewToolActionResponse {
155
- const { agents, warnings } = discoverAgents(ctx.cwd);
156
- const running = crew.getActiveSummariesForOwner(ctx.callerSessionId);
157
- const lines = [
158
- "## Available Subagents",
159
- ...formatAvailableAgents(agents),
160
- ...formatWarnings(warnings),
161
- "",
162
- "## Active Subagents",
163
- ...formatActiveAgents(running),
164
- ];
165
-
166
- return response(
167
- { content: [{ type: "text", text: lines.join("\n") }], details: {} },
168
- [
169
- ...discoveryWarningSideEffect(warnings),
170
- ...(running.length > 0 ? [{ type: "active-list-warning" } as const] : []),
171
- ],
172
- );
173
- },
174
-
175
- spawn(
176
- params: { subagent: string; task: string },
177
- ctx: CrewSpawnActionContext,
178
- ): CrewToolActionResponse {
179
- const { agents, warnings } = discoverAgents(ctx.cwd);
180
- const sideEffects = discoveryWarningSideEffect(warnings);
181
- const subagent = agents.find(
182
- (candidate) => candidate.name === params.subagent,
183
- );
184
-
185
- if (!subagent) {
186
- const available =
187
- agents.map((candidate) => candidate.name).join(", ") || "none";
188
- return response(
189
- toolError(
190
- `Unknown subagent: "${params.subagent}". Available: ${available}`,
191
- ),
192
- sideEffects,
193
- );
194
- }
195
-
196
- const id = crew.spawn(
197
- subagent,
198
- params.task,
199
- ctx.cwd,
200
- ctx.callerSessionId,
201
- {
202
- model: ctx.model,
203
- modelRegistry: ctx.modelRegistry,
204
- agentDir: ctx.agentDir,
205
- parentSessionFile: ctx.parentSessionFile,
206
- onWarning: ctx.onWarning,
207
- },
208
- extensionDir,
209
- );
210
-
211
- return response(
212
- toolSuccess(
213
- `Subagent '${subagent.name}' spawned as ${id}. Result will be delivered as a steering message when done.`,
214
- { id, agentName: subagent.name, task: params.task },
215
- ),
216
- sideEffects,
217
- );
218
- },
219
-
220
- abort(
221
- params: {
222
- subagent_id?: string;
223
- subagent_ids?: string[];
224
- all?: boolean;
225
- },
226
- ctx: CrewToolActionContext,
227
- ): CrewToolActionResponse {
228
- const modeCount = Number(Boolean(params.subagent_id))
229
- + Number(Boolean(params.subagent_ids?.length))
230
- + Number(params.all === true);
231
-
232
- if (modeCount !== 1) {
233
- return response(toolError(
234
- "Provide exactly one of: subagent_id, subagent_ids, or all=true.",
235
- ));
236
- }
237
-
238
- if (params.all) {
239
- const abortedIds = crew.abortAllOwned(ctx.callerSessionId, {
240
- reason: "Aborted by tool request",
241
- });
242
- if (abortedIds.length === 0) {
243
- return response(toolError("No active subagents in the current session."));
244
- }
245
-
246
- return response(toolSuccess(
247
- `Aborted ${abortedIds.length} subagent(s): ${abortedIds.join(", ")}`,
248
- { ids: abortedIds },
249
- { terminate: true },
250
- ));
251
- }
252
-
253
- const ids = params.subagent_id
254
- ? [params.subagent_id]
255
- : (params.subagent_ids ?? []);
256
- const result = crew.abortOwned(ids, ctx.callerSessionId, {
257
- reason: "Aborted by tool request",
258
- });
259
- const message = formatAbortToolMessage(result);
260
-
261
- if (result.abortedIds.length === 0) {
262
- return response(toolError(message || "No subagents were aborted."));
263
- }
264
-
265
- return response(toolSuccess(
266
- message,
267
- {
268
- ids: result.abortedIds,
269
- missing_ids: result.missingIds,
270
- foreign_ids: result.foreignIds,
271
- },
272
- { terminate: true },
273
- ));
274
- },
275
-
276
- respond(
277
- params: { subagent_id: string; message: string },
278
- ctx: CrewToolActionContext,
279
- ): CrewToolActionResponse {
280
- const { error } = crew.respond(
281
- params.subagent_id,
282
- params.message,
283
- ctx.callerSessionId,
284
- );
285
- if (error) return response(toolError(error));
286
-
287
- return response(toolSuccess(
288
- `Message sent to subagent ${params.subagent_id}. Response will be delivered as a steering message.`,
289
- { id: params.subagent_id, message: params.message },
290
- ));
291
- },
292
-
293
- done(
294
- params: { subagent_id: string },
295
- ctx: CrewToolActionContext,
296
- ): CrewToolActionResponse {
297
- const { error } = crew.done(params.subagent_id, ctx.callerSessionId);
298
- if (error) return response(toolError(error));
299
-
300
- return response(toolSuccess(
301
- `Subagent ${params.subagent_id} closed.`,
302
- { id: params.subagent_id },
303
- ));
304
- },
305
- };
306
- }
@@ -1,109 +0,0 @@
1
- import type {
2
- ExtensionAPI,
3
- ExtensionContext,
4
- } from "@earendil-works/pi-coding-agent";
5
- import type { AgentDiscoveryWarning } from "../agent-discovery.js";
6
- import { sendCrewListActiveWarning } from "../subagent-messages.js";
7
- import { renderCrewResult } from "./tool-presentation.js";
8
- import type {
9
- CrewToolActionContext,
10
- CrewToolActionResponse,
11
- CrewToolActionSideEffect,
12
- CrewToolActions,
13
- CrewToolResult,
14
- } from "./crew-tool-actions.js";
15
-
16
- interface CrewToolExecutorDeps {
17
- pi: ExtensionAPI;
18
- notifyDiscoveryWarnings: (
19
- ctx: ExtensionContext,
20
- warnings: AgentDiscoveryWarning[],
21
- ) => void;
22
- }
23
-
24
- export interface CrewToolDeps {
25
- pi: ExtensionAPI;
26
- actions: CrewToolActions;
27
- executor: CrewToolExecutor;
28
- }
29
-
30
- function getBaseActionContext(ctx: ExtensionContext): CrewToolActionContext {
31
- return {
32
- cwd: ctx.cwd,
33
- callerSessionId: ctx.sessionManager.getSessionId(),
34
- };
35
- }
36
-
37
- function runSideEffects(
38
- deps: CrewToolExecutorDeps,
39
- ctx: ExtensionContext,
40
- sideEffects: CrewToolActionSideEffect[],
41
- ): void {
42
- for (const sideEffect of sideEffects) {
43
- switch (sideEffect.type) {
44
- case "discovery-warnings":
45
- deps.notifyDiscoveryWarnings(ctx, sideEffect.warnings);
46
- break;
47
- case "active-list-warning":
48
- Promise.resolve().then(() => {
49
- sendCrewListActiveWarning(deps.pi.sendMessage.bind(deps.pi), {
50
- isIdle: ctx.isIdle(),
51
- triggerTurn: true,
52
- });
53
- });
54
- break;
55
- }
56
- }
57
- }
58
-
59
- export class CrewToolExecutor {
60
- constructor(private readonly deps: CrewToolExecutorDeps) {}
61
-
62
- execute(
63
- ctx: ExtensionContext,
64
- runAction: (actionCtx: CrewToolActionContext) => CrewToolActionResponse,
65
- ): CrewToolResult {
66
- const response = runAction(getBaseActionContext(ctx));
67
- runSideEffects(this.deps, ctx, response.sideEffects);
68
- return response.result;
69
- }
70
- }
71
-
72
- type RegisteredTool = Parameters<ExtensionAPI["registerTool"]>[0];
73
- type ToolRenderCall = Exclude<RegisteredTool["renderCall"], undefined>;
74
-
75
- export function registerCrewActionTool<Params extends object>(
76
- { pi, executor }: CrewToolDeps,
77
- options: Omit<RegisteredTool, "execute" | "renderResult" | "renderCall"> & {
78
- action: (
79
- params: Params,
80
- actionCtx: CrewToolActionContext,
81
- ctx: ExtensionContext,
82
- ) => CrewToolActionResponse;
83
- renderCall?: (
84
- args: Partial<Params>,
85
- theme: Parameters<ToolRenderCall>[1],
86
- context: Parameters<ToolRenderCall>[2],
87
- ) => ReturnType<ToolRenderCall>;
88
- },
89
- ): void {
90
- const { action, renderCall, ...tool } = options;
91
-
92
- pi.registerTool({
93
- ...tool,
94
- ...(renderCall
95
- ? {
96
- renderCall: (args, theme, context) =>
97
- renderCall(args as Partial<Params>, theme, context),
98
- }
99
- : {}),
100
- async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
101
- return executor.execute(ctx, (actionCtx) =>
102
- action(params as Params, actionCtx, ctx),
103
- );
104
- },
105
- renderResult(result, _options, theme, _context) {
106
- return renderCrewResult(result, theme);
107
- },
108
- });
109
- }