@oh-my-pi/pi-coding-agent 3.21.0 → 3.24.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.
- package/CHANGELOG.md +40 -1
- package/docs/sdk.md +47 -50
- package/examples/custom-tools/README.md +0 -15
- package/examples/hooks/custom-compaction.ts +1 -3
- package/examples/sdk/README.md +6 -10
- package/package.json +5 -5
- package/src/cli/args.ts +9 -6
- package/src/core/agent-session.ts +3 -3
- package/src/core/custom-tools/wrapper.ts +0 -1
- package/src/core/extensions/index.ts +1 -6
- package/src/core/extensions/wrapper.ts +0 -7
- package/src/core/file-mentions.ts +5 -8
- package/src/core/sdk.ts +41 -111
- package/src/core/session-manager.ts +7 -0
- package/src/core/system-prompt.ts +22 -33
- package/src/core/tools/ask.ts +14 -7
- package/src/core/tools/bash-interceptor.ts +4 -4
- package/src/core/tools/bash.ts +19 -9
- package/src/core/tools/context.ts +7 -0
- package/src/core/tools/edit.ts +8 -15
- package/src/core/tools/exa/render.ts +4 -16
- package/src/core/tools/find.ts +7 -18
- package/src/core/tools/git.ts +13 -3
- package/src/core/tools/grep.ts +7 -18
- package/src/core/tools/index.test.ts +180 -0
- package/src/core/tools/index.ts +94 -237
- package/src/core/tools/ls.ts +4 -9
- package/src/core/tools/lsp/index.ts +32 -29
- package/src/core/tools/lsp/render.ts +7 -28
- package/src/core/tools/notebook.ts +3 -5
- package/src/core/tools/output.ts +5 -17
- package/src/core/tools/read.ts +8 -19
- package/src/core/tools/review.ts +0 -18
- package/src/core/tools/rulebook.ts +8 -2
- package/src/core/tools/task/agents.ts +28 -7
- package/src/core/tools/task/discovery.ts +0 -6
- package/src/core/tools/task/executor.ts +264 -254
- package/src/core/tools/task/index.ts +45 -220
- package/src/core/tools/task/render.ts +21 -11
- package/src/core/tools/task/types.ts +6 -11
- package/src/core/tools/task/worker-protocol.ts +17 -0
- package/src/core/tools/task/worker.ts +238 -0
- package/src/core/tools/web-fetch.ts +4 -36
- package/src/core/tools/web-search/index.ts +2 -1
- package/src/core/tools/web-search/render.ts +1 -4
- package/src/core/tools/write.ts +7 -15
- package/src/discovery/helpers.test.ts +1 -1
- package/src/index.ts +5 -16
- package/src/main.ts +4 -4
- package/src/modes/interactive/theme/theme.ts +4 -4
- package/src/prompts/task.md +0 -7
- package/src/prompts/tools/output.md +2 -2
- package/src/prompts/tools/task.md +68 -0
- package/examples/custom-tools/question/index.ts +0 -84
- package/examples/custom-tools/subagent/README.md +0 -172
- package/examples/custom-tools/subagent/agents/planner.md +0 -37
- package/examples/custom-tools/subagent/agents/scout.md +0 -50
- package/examples/custom-tools/subagent/agents/worker.md +0 -24
- package/examples/custom-tools/subagent/agents.ts +0 -156
- package/examples/custom-tools/subagent/commands/implement-and-review.md +0 -10
- package/examples/custom-tools/subagent/commands/implement.md +0 -10
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +0 -9
- package/examples/custom-tools/subagent/index.ts +0 -1002
- package/examples/sdk/05-tools.ts +0 -94
- package/examples/sdk/12-full-control.ts +0 -95
- package/src/prompts/browser.md +0 -71
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker thread for subagent execution.
|
|
3
|
+
*
|
|
4
|
+
* This worker runs in a separate thread via Bun's Worker API. It creates a minimal
|
|
5
|
+
* AgentSession and forwards events back to the parent thread.
|
|
6
|
+
*
|
|
7
|
+
* ## Event Flow
|
|
8
|
+
*
|
|
9
|
+
* 1. Parent sends { type: "start", payload } with task config
|
|
10
|
+
* 2. Worker creates AgentSession and subscribes to events
|
|
11
|
+
* 3. Worker forwards AgentEvent messages via postMessage
|
|
12
|
+
* 4. Worker sends { type: "done", exitCode, ... } on completion
|
|
13
|
+
* 5. Parent can send { type: "abort" } to request cancellation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
17
|
+
import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
18
|
+
import type { AgentSessionEvent } from "../../agent-session";
|
|
19
|
+
import { parseModelPattern, parseModelString } from "../../model-resolver";
|
|
20
|
+
import { createAgentSession, discoverAuthStorage, discoverModels } from "../../sdk";
|
|
21
|
+
import { SessionManager } from "../../session-manager";
|
|
22
|
+
import type { SubagentWorkerRequest, SubagentWorkerResponse, SubagentWorkerStartPayload } from "./worker-protocol";
|
|
23
|
+
|
|
24
|
+
type PostMessageFn = (message: SubagentWorkerResponse) => void;
|
|
25
|
+
|
|
26
|
+
const postMessageSafe: PostMessageFn = (message) => {
|
|
27
|
+
(globalThis as typeof globalThis & { postMessage: PostMessageFn }).postMessage(message);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
interface WorkerMessageEvent<T> {
|
|
31
|
+
data: T;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Agent event types to forward to parent (excludes session-only events like compaction) */
|
|
35
|
+
const agentEventTypes = new Set<AgentEvent["type"]>([
|
|
36
|
+
"agent_start",
|
|
37
|
+
"agent_end",
|
|
38
|
+
"turn_start",
|
|
39
|
+
"turn_end",
|
|
40
|
+
"message_start",
|
|
41
|
+
"message_update",
|
|
42
|
+
"message_end",
|
|
43
|
+
"tool_execution_start",
|
|
44
|
+
"tool_execution_update",
|
|
45
|
+
"tool_execution_end",
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
const isAgentEvent = (event: AgentSessionEvent): event is AgentEvent => {
|
|
49
|
+
return agentEventTypes.has(event.type as AgentEvent["type"]);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let running = false;
|
|
53
|
+
let abortRequested = false;
|
|
54
|
+
let activeSession: { abort: () => Promise<void>; dispose: () => Promise<void> } | null = null;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolve model string to Model object with optional thinking level.
|
|
58
|
+
* Supports both exact "provider/id" format and fuzzy matching ("sonnet", "opus").
|
|
59
|
+
*/
|
|
60
|
+
function resolveModelOverride(
|
|
61
|
+
override: string | undefined,
|
|
62
|
+
modelRegistry: { getAvailable: () => Model<Api>[]; find: (provider: string, id: string) => Model<Api> | undefined },
|
|
63
|
+
): { model?: Model<Api>; thinkingLevel?: ThinkingLevel } {
|
|
64
|
+
if (!override) return {};
|
|
65
|
+
|
|
66
|
+
// Try exact "provider/id" format first
|
|
67
|
+
const parsed = parseModelString(override);
|
|
68
|
+
if (parsed) {
|
|
69
|
+
return { model: modelRegistry.find(parsed.provider, parsed.id) };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Fall back to fuzzy pattern matching
|
|
73
|
+
const result = parseModelPattern(override, modelRegistry.getAvailable());
|
|
74
|
+
return {
|
|
75
|
+
model: result.model,
|
|
76
|
+
thinkingLevel: result.thinkingLevel !== "off" ? result.thinkingLevel : undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Main task execution function.
|
|
82
|
+
*
|
|
83
|
+
* Equivalent to CLI flow:
|
|
84
|
+
* 1. omp --mode json --non-interactive
|
|
85
|
+
* 2. --append-system-prompt <agent.systemPrompt>
|
|
86
|
+
* 3. --tools <toolNames> (if specified)
|
|
87
|
+
* 4. --model <model> (if specified)
|
|
88
|
+
* 5. --session <sessionFile> OR --no-session
|
|
89
|
+
* 6. --prompt <task>
|
|
90
|
+
*
|
|
91
|
+
* Environment equivalent:
|
|
92
|
+
* - OMP_BLOCKED_AGENT: payload.blockedAgent (prevents same-agent recursion)
|
|
93
|
+
* - OMP_SPAWNS: payload.spawnsEnv (controls nested spawn permissions)
|
|
94
|
+
*/
|
|
95
|
+
async function runTask(payload: SubagentWorkerStartPayload): Promise<void> {
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
let exitCode = 0;
|
|
98
|
+
let error: string | undefined;
|
|
99
|
+
let aborted = false;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Check for pre-start abort
|
|
103
|
+
if (abortRequested) {
|
|
104
|
+
aborted = true;
|
|
105
|
+
exitCode = 1;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Set working directory (CLI does this implicitly)
|
|
110
|
+
process.chdir(payload.cwd);
|
|
111
|
+
|
|
112
|
+
// Discover auth and models (equivalent to CLI's discoverAuthStorage/discoverModels)
|
|
113
|
+
const authStorage = await discoverAuthStorage();
|
|
114
|
+
const modelRegistry = await discoverModels(authStorage);
|
|
115
|
+
|
|
116
|
+
// Resolve model override (equivalent to CLI's parseModelPattern with --model)
|
|
117
|
+
const { model, thinkingLevel } = resolveModelOverride(payload.model, modelRegistry);
|
|
118
|
+
|
|
119
|
+
// Create session manager (equivalent to CLI's --session or --no-session)
|
|
120
|
+
const sessionManager = payload.sessionFile
|
|
121
|
+
? await SessionManager.open(payload.sessionFile)
|
|
122
|
+
: SessionManager.inMemory(payload.cwd);
|
|
123
|
+
|
|
124
|
+
// Create agent session (equivalent to CLI's createAgentSession)
|
|
125
|
+
// Note: hasUI: false disables interactive features
|
|
126
|
+
const { session } = await createAgentSession({
|
|
127
|
+
cwd: payload.cwd,
|
|
128
|
+
authStorage,
|
|
129
|
+
modelRegistry,
|
|
130
|
+
model,
|
|
131
|
+
thinkingLevel,
|
|
132
|
+
toolNames: payload.toolNames,
|
|
133
|
+
// Append system prompt (equivalent to CLI's --append-system-prompt)
|
|
134
|
+
systemPrompt: (defaultPrompt) => `${defaultPrompt}\n\n${payload.systemPrompt}`,
|
|
135
|
+
sessionManager,
|
|
136
|
+
hasUI: false,
|
|
137
|
+
// Pass spawn restrictions to nested tasks
|
|
138
|
+
spawns: payload.spawnsEnv,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
activeSession = session;
|
|
142
|
+
|
|
143
|
+
// Initialize extensions (equivalent to CLI's extension initialization)
|
|
144
|
+
// Note: Does not support --extension CLI flag or extension CLI flags
|
|
145
|
+
const extensionRunner = session.extensionRunner;
|
|
146
|
+
if (extensionRunner) {
|
|
147
|
+
extensionRunner.initialize({
|
|
148
|
+
getModel: () => session.model,
|
|
149
|
+
sendMessageHandler: (message, options) => {
|
|
150
|
+
session.sendCustomMessage(message, options).catch((e) => {
|
|
151
|
+
console.error(`Extension sendMessage failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
appendEntryHandler: (customType, data) => {
|
|
155
|
+
session.sessionManager.appendCustomEntry(customType, data);
|
|
156
|
+
},
|
|
157
|
+
getActiveToolsHandler: () => session.getActiveToolNames(),
|
|
158
|
+
getAllToolsHandler: () => session.getAllToolNames(),
|
|
159
|
+
setActiveToolsHandler: (toolNamesList: string[]) => session.setActiveToolsByName(toolNamesList),
|
|
160
|
+
});
|
|
161
|
+
extensionRunner.onError((err) => {
|
|
162
|
+
console.error(`Extension error (${err.extensionPath}): ${err.error}`);
|
|
163
|
+
});
|
|
164
|
+
await extensionRunner.emit({ type: "session_start" });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Subscribe to events and forward to parent (equivalent to --mode json output)
|
|
168
|
+
session.subscribe((event: AgentSessionEvent) => {
|
|
169
|
+
if (isAgentEvent(event)) {
|
|
170
|
+
postMessageSafe({ type: "event", event });
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Run the prompt (equivalent to --prompt flag)
|
|
175
|
+
await session.prompt(payload.task);
|
|
176
|
+
|
|
177
|
+
// Check if aborted during execution
|
|
178
|
+
const lastMessage = session.state.messages[session.state.messages.length - 1];
|
|
179
|
+
if (lastMessage?.role === "assistant" && lastMessage.stopReason === "aborted") {
|
|
180
|
+
aborted = true;
|
|
181
|
+
exitCode = 1;
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
exitCode = 1;
|
|
185
|
+
error = err instanceof Error ? err.stack || err.message : String(err);
|
|
186
|
+
} finally {
|
|
187
|
+
// Handle abort requested during execution
|
|
188
|
+
if (abortRequested) {
|
|
189
|
+
aborted = true;
|
|
190
|
+
if (exitCode === 0) exitCode = 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Cleanup session
|
|
194
|
+
if (activeSession) {
|
|
195
|
+
try {
|
|
196
|
+
await activeSession.dispose();
|
|
197
|
+
} catch {
|
|
198
|
+
// Ignore cleanup errors
|
|
199
|
+
}
|
|
200
|
+
activeSession = null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Send completion message to parent
|
|
204
|
+
postMessageSafe({
|
|
205
|
+
type: "done",
|
|
206
|
+
exitCode,
|
|
207
|
+
durationMs: Date.now() - startTime,
|
|
208
|
+
error,
|
|
209
|
+
aborted,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Handle abort request from parent */
|
|
215
|
+
function handleAbort(): void {
|
|
216
|
+
abortRequested = true;
|
|
217
|
+
if (activeSession) {
|
|
218
|
+
void activeSession.abort();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Message handler - receives start/abort commands from parent
|
|
223
|
+
globalThis.addEventListener("message", (event: WorkerMessageEvent<SubagentWorkerRequest>) => {
|
|
224
|
+
const message = event.data;
|
|
225
|
+
if (!message) return;
|
|
226
|
+
|
|
227
|
+
if (message.type === "abort") {
|
|
228
|
+
handleAbort();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (message.type === "start") {
|
|
233
|
+
// Only allow one task per worker
|
|
234
|
+
if (running) return;
|
|
235
|
+
running = true;
|
|
236
|
+
void runTask(message.payload);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
@@ -5,6 +5,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
5
5
|
import { parse as parseHtml } from "node-html-parser";
|
|
6
6
|
import webFetchDescription from "../../prompts/tools/web-fetch.md" with { type: "text" };
|
|
7
7
|
import { logger } from "../logger";
|
|
8
|
+
import type { ToolSession } from "./index";
|
|
8
9
|
|
|
9
10
|
// =============================================================================
|
|
10
11
|
// Types and Constants
|
|
@@ -1239,9 +1240,7 @@ async function handleStackOverflow(url: string, timeout: number): Promise<Render
|
|
|
1239
1240
|
md += `**Score:** ${question.score} · **Answers:** ${question.answer_count}`;
|
|
1240
1241
|
md += question.is_answered ? " (Answered)" : "";
|
|
1241
1242
|
md += `\n**Tags:** ${question.tags.join(", ")}\n`;
|
|
1242
|
-
md += `**Asked by:** ${question.owner.display_name} · ${
|
|
1243
|
-
new Date(question.creation_date * 1000).toISOString().split("T")[0]
|
|
1244
|
-
}\n\n`;
|
|
1243
|
+
md += `**Asked by:** ${question.owner.display_name} · ${new Date(question.creation_date * 1000).toISOString().split("T")[0]}\n\n`;
|
|
1245
1244
|
md += `---\n\n## Question\n\n${htmlToBasicMarkdown(question.body)}\n\n`;
|
|
1246
1245
|
|
|
1247
1246
|
// Fetch answers
|
|
@@ -2270,7 +2269,7 @@ export interface WebFetchToolDetails {
|
|
|
2270
2269
|
notes: string[];
|
|
2271
2270
|
}
|
|
2272
2271
|
|
|
2273
|
-
export function createWebFetchTool(
|
|
2272
|
+
export function createWebFetchTool(_session: ToolSession): AgentTool<typeof webFetchSchema> {
|
|
2274
2273
|
return {
|
|
2275
2274
|
name: "web_fetch",
|
|
2276
2275
|
label: "Web Fetch",
|
|
@@ -2316,9 +2315,6 @@ export function createWebFetchTool(_cwd: string): AgentTool<typeof webFetchSchem
|
|
|
2316
2315
|
};
|
|
2317
2316
|
}
|
|
2318
2317
|
|
|
2319
|
-
/** Default web fetch tool using process.cwd() - for backwards compatibility */
|
|
2320
|
-
export const webFetchTool = createWebFetchTool(process.cwd());
|
|
2321
|
-
|
|
2322
2318
|
// =============================================================================
|
|
2323
2319
|
// TUI Rendering
|
|
2324
2320
|
// =============================================================================
|
|
@@ -2326,7 +2322,7 @@ export const webFetchTool = createWebFetchTool(process.cwd());
|
|
|
2326
2322
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
2327
2323
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
2328
2324
|
import { type Theme, theme } from "../../modes/interactive/theme/theme";
|
|
2329
|
-
import type {
|
|
2325
|
+
import type { RenderResultOptions } from "../custom-tools/types";
|
|
2330
2326
|
|
|
2331
2327
|
/** Truncate text to max length with ellipsis */
|
|
2332
2328
|
function truncate(text: string, maxLen: number, ellipsis: string): string {
|
|
@@ -2490,31 +2486,3 @@ export const webFetchToolRenderer = {
|
|
|
2490
2486
|
renderCall: renderWebFetchCall,
|
|
2491
2487
|
renderResult: renderWebFetchResult,
|
|
2492
2488
|
};
|
|
2493
|
-
|
|
2494
|
-
type WebFetchParams = { url: string; timeout?: number; raw?: boolean };
|
|
2495
|
-
|
|
2496
|
-
/** Web fetch tool as CustomTool (for TUI rendering support) */
|
|
2497
|
-
export const webFetchCustomTool: CustomTool<typeof webFetchSchema, WebFetchToolDetails> = {
|
|
2498
|
-
name: "web_fetch",
|
|
2499
|
-
label: "Web Fetch",
|
|
2500
|
-
description: webFetchDescription,
|
|
2501
|
-
parameters: webFetchSchema,
|
|
2502
|
-
|
|
2503
|
-
async execute(
|
|
2504
|
-
toolCallId: string,
|
|
2505
|
-
params: WebFetchParams,
|
|
2506
|
-
_onUpdate,
|
|
2507
|
-
_ctx: CustomToolContext,
|
|
2508
|
-
_signal?: AbortSignal,
|
|
2509
|
-
) {
|
|
2510
|
-
return webFetchTool.execute(toolCallId, params);
|
|
2511
|
-
},
|
|
2512
|
-
|
|
2513
|
-
renderCall(args: WebFetchParams, uiTheme: Theme) {
|
|
2514
|
-
return renderWebFetchCall(args, uiTheme);
|
|
2515
|
-
},
|
|
2516
|
-
|
|
2517
|
-
renderResult(result, options: RenderResultOptions, uiTheme: Theme) {
|
|
2518
|
-
return renderWebFetchResult(result, options, uiTheme);
|
|
2519
|
-
},
|
|
2520
|
-
};
|
|
@@ -20,6 +20,7 @@ import type { CustomTool, CustomToolContext, RenderResultOptions } from "../../c
|
|
|
20
20
|
import { callExaTool, findApiKey as findExaKey, formatSearchResults, isSearchResponse } from "../exa/mcp-client";
|
|
21
21
|
import { renderExaCall, renderExaResult } from "../exa/render";
|
|
22
22
|
import type { ExaRenderDetails } from "../exa/types";
|
|
23
|
+
import type { ToolSession } from "../index";
|
|
23
24
|
import { formatAge } from "../render-utils";
|
|
24
25
|
import { findAnthropicAuth } from "./auth";
|
|
25
26
|
import { searchAnthropic } from "./providers/anthropic";
|
|
@@ -365,7 +366,7 @@ export const webSearchCustomTool: CustomTool<typeof webSearchSchema, WebSearchRe
|
|
|
365
366
|
};
|
|
366
367
|
|
|
367
368
|
/** Factory function for backward compatibility */
|
|
368
|
-
export function createWebSearchTool(
|
|
369
|
+
export function createWebSearchTool(_session: ToolSession): AgentTool<typeof webSearchSchema> {
|
|
369
370
|
return webSearchTool;
|
|
370
371
|
}
|
|
371
372
|
|
|
@@ -257,10 +257,7 @@ export function renderWebSearchResult(
|
|
|
257
257
|
}
|
|
258
258
|
if (response.requestId) {
|
|
259
259
|
metaLines.push(
|
|
260
|
-
`${theme.fg("muted", "Request:")} ${theme.fg(
|
|
261
|
-
"text",
|
|
262
|
-
truncate(response.requestId, MAX_REQUEST_ID_LEN, theme.format.ellipsis),
|
|
263
|
-
)}`,
|
|
260
|
+
`${theme.fg("muted", "Request:")} ${theme.fg("text", truncate(response.requestId, MAX_REQUEST_ID_LEN, theme.format.ellipsis))}`,
|
|
264
261
|
);
|
|
265
262
|
}
|
|
266
263
|
if (searchQueries.length > 0) {
|
package/src/core/tools/write.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { Type } from "@sinclair/typebox";
|
|
|
5
5
|
import { getLanguageFromPath, highlightCode, type Theme } from "../../modes/interactive/theme/theme";
|
|
6
6
|
import writeDescription from "../../prompts/tools/write.md" with { type: "text" };
|
|
7
7
|
import type { RenderResultOptions } from "../custom-tools/types";
|
|
8
|
-
import
|
|
8
|
+
import type { ToolSession } from "../sdk";
|
|
9
|
+
import { createLspWritethrough, type FileDiagnosticsResult } from "./lsp/index";
|
|
9
10
|
import { resolveToCwd } from "./path-utils";
|
|
10
11
|
import { formatDiagnostics, replaceTabs, shortenPath } from "./render-utils";
|
|
11
12
|
|
|
@@ -14,21 +15,15 @@ const writeSchema = Type.Object({
|
|
|
14
15
|
content: Type.String({ description: "Content to write to the file" }),
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
/** Options for creating the write tool */
|
|
18
|
-
export interface WriteToolOptions {
|
|
19
|
-
writethrough?: WritethroughCallback;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
18
|
/** Details returned by the write tool for TUI rendering */
|
|
23
19
|
export interface WriteToolDetails {
|
|
24
20
|
diagnostics?: FileDiagnosticsResult;
|
|
25
21
|
}
|
|
26
22
|
|
|
27
|
-
export function createWriteTool(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const writethrough = options.writethrough ?? writethroughNoop;
|
|
23
|
+
export function createWriteTool(session: ToolSession): AgentTool<typeof writeSchema, WriteToolDetails> {
|
|
24
|
+
const enableFormat = session.settings?.getLspFormatOnWrite() ?? true;
|
|
25
|
+
const enableDiagnostics = session.settings?.getLspDiagnosticsOnWrite() ?? true;
|
|
26
|
+
const writethrough = createLspWritethrough(session.cwd, { enableFormat, enableDiagnostics });
|
|
32
27
|
return {
|
|
33
28
|
name: "write",
|
|
34
29
|
label: "Write",
|
|
@@ -39,7 +34,7 @@ export function createWriteTool(
|
|
|
39
34
|
{ path, content }: { path: string; content: string },
|
|
40
35
|
signal?: AbortSignal,
|
|
41
36
|
) => {
|
|
42
|
-
const absolutePath = resolveToCwd(path, cwd);
|
|
37
|
+
const absolutePath = resolveToCwd(path, session.cwd);
|
|
43
38
|
|
|
44
39
|
const diagnostics = await writethrough(absolutePath, content, signal);
|
|
45
40
|
|
|
@@ -64,9 +59,6 @@ export function createWriteTool(
|
|
|
64
59
|
};
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
/** Default write tool using process.cwd() - for backwards compatibility */
|
|
68
|
-
export const writeTool = createWriteTool(process.cwd());
|
|
69
|
-
|
|
70
62
|
// =============================================================================
|
|
71
63
|
// TUI Renderer
|
|
72
64
|
// =============================================================================
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,8 @@ export { ModelRegistry } from "./core/model-registry";
|
|
|
81
81
|
export type { PromptTemplate } from "./core/prompt-templates";
|
|
82
82
|
// SDK for programmatic usage
|
|
83
83
|
export {
|
|
84
|
+
// Tool factories
|
|
85
|
+
BUILTIN_TOOLS,
|
|
84
86
|
type BuildSystemPromptOptions,
|
|
85
87
|
buildSystemPrompt,
|
|
86
88
|
type CreateAgentSessionOptions,
|
|
@@ -88,14 +90,12 @@ export {
|
|
|
88
90
|
// Factory
|
|
89
91
|
createAgentSession,
|
|
90
92
|
createBashTool,
|
|
91
|
-
// Tool factories (for custom cwd)
|
|
92
|
-
createCodingTools,
|
|
93
93
|
createEditTool,
|
|
94
94
|
createFindTool,
|
|
95
95
|
createGrepTool,
|
|
96
96
|
createLsTool,
|
|
97
|
-
createReadOnlyTools,
|
|
98
97
|
createReadTool,
|
|
98
|
+
createTools,
|
|
99
99
|
createWriteTool,
|
|
100
100
|
// Discovery
|
|
101
101
|
discoverAuthStorage,
|
|
@@ -107,8 +107,7 @@ export {
|
|
|
107
107
|
discoverPromptTemplates,
|
|
108
108
|
discoverSkills,
|
|
109
109
|
loadSettings,
|
|
110
|
-
|
|
111
|
-
readOnlyTools,
|
|
110
|
+
type ToolSession,
|
|
112
111
|
} from "./core/sdk";
|
|
113
112
|
export {
|
|
114
113
|
type BranchSummaryEntry,
|
|
@@ -154,27 +153,17 @@ export {
|
|
|
154
153
|
} from "./core/skills";
|
|
155
154
|
// Slash commands
|
|
156
155
|
export { type FileSlashCommand, loadSlashCommands as discoverSlashCommands } from "./core/slash-commands";
|
|
157
|
-
// Tools
|
|
156
|
+
// Tools (detail types only - factories exported from sdk)
|
|
158
157
|
export {
|
|
159
158
|
type BashToolDetails,
|
|
160
|
-
bashTool,
|
|
161
|
-
type CodingToolsOptions,
|
|
162
|
-
codingTools,
|
|
163
|
-
editTool,
|
|
164
159
|
type FindToolDetails,
|
|
165
|
-
findTool,
|
|
166
160
|
type GitToolDetails,
|
|
167
161
|
type GrepToolDetails,
|
|
168
162
|
gitTool,
|
|
169
|
-
grepTool,
|
|
170
163
|
type LsToolDetails,
|
|
171
|
-
lsTool,
|
|
172
164
|
type ReadToolDetails,
|
|
173
|
-
readTool,
|
|
174
165
|
type TruncationResult,
|
|
175
166
|
type WriteToolDetails,
|
|
176
|
-
type WriteToolOptions,
|
|
177
|
-
writeTool,
|
|
178
167
|
} from "./core/tools/index";
|
|
179
168
|
export type { FileDiagnosticsResult } from "./core/tools/lsp/index";
|
|
180
169
|
// Main entry point
|
package/src/main.ts
CHANGED
|
@@ -24,7 +24,6 @@ import { SessionManager } from "./core/session-manager";
|
|
|
24
24
|
import { SettingsManager } from "./core/settings-manager";
|
|
25
25
|
import { resolvePromptInput } from "./core/system-prompt";
|
|
26
26
|
import { printTimings, time } from "./core/timings";
|
|
27
|
-
import { allTools } from "./core/tools/index";
|
|
28
27
|
import { runMigrations, showDeprecationWarnings } from "./migrations";
|
|
29
28
|
import { InteractiveMode, installTerminalCrashHandlers, runPrintMode, runRpcMode } from "./modes/index";
|
|
30
29
|
import { initTheme, stopThemeWatcher } from "./modes/interactive/theme/theme";
|
|
@@ -210,7 +209,9 @@ async function buildSessionOptions(
|
|
|
210
209
|
modelRegistry: ModelRegistry,
|
|
211
210
|
settingsManager: SettingsManager,
|
|
212
211
|
): Promise<CreateAgentSessionOptions> {
|
|
213
|
-
const options: CreateAgentSessionOptions = {
|
|
212
|
+
const options: CreateAgentSessionOptions = {
|
|
213
|
+
cwd: parsed.cwd ?? process.cwd(),
|
|
214
|
+
};
|
|
214
215
|
|
|
215
216
|
// Auto-discover SYSTEM.md if no CLI system prompt provided
|
|
216
217
|
const systemPromptSource = parsed.systemPrompt ?? discoverSystemPromptFile();
|
|
@@ -263,8 +264,7 @@ async function buildSessionOptions(
|
|
|
263
264
|
|
|
264
265
|
// Tools
|
|
265
266
|
if (parsed.tools) {
|
|
266
|
-
options.
|
|
267
|
-
options.explicitTools = parsed.tools;
|
|
267
|
+
options.toolNames = parsed.tools;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
// Skills
|
|
@@ -1367,9 +1367,9 @@ export class Theme {
|
|
|
1367
1367
|
return (str: string) => this.fg("bashMode", str);
|
|
1368
1368
|
}
|
|
1369
1369
|
|
|
1370
|
-
//
|
|
1370
|
+
// ============================================================================
|
|
1371
1371
|
// Symbol Methods
|
|
1372
|
-
//
|
|
1372
|
+
// ============================================================================
|
|
1373
1373
|
|
|
1374
1374
|
/**
|
|
1375
1375
|
* Get a symbol by key.
|
|
@@ -1392,9 +1392,9 @@ export class Theme {
|
|
|
1392
1392
|
return this.symbolPreset;
|
|
1393
1393
|
}
|
|
1394
1394
|
|
|
1395
|
-
//
|
|
1395
|
+
// ============================================================================
|
|
1396
1396
|
// Symbol Category Accessors
|
|
1397
|
-
//
|
|
1397
|
+
// ============================================================================
|
|
1398
1398
|
|
|
1399
1399
|
get status() {
|
|
1400
1400
|
return {
|
package/src/prompts/task.md
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: task
|
|
3
|
-
description: General-purpose subagent with full capabilities for delegated multi-step tasks
|
|
4
|
-
spawns: explore
|
|
5
|
-
model: default
|
|
6
|
-
---
|
|
7
|
-
|
|
8
1
|
You are a worker agent for delegated tasks. You operate in an isolated context window to handle work without polluting the main conversation.
|
|
9
2
|
|
|
10
3
|
Do what has been asked; nothing more, nothing less. Work autonomously using all available tools.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# TaskOutput
|
|
2
|
-
|
|
3
1
|
Retrieves complete output from background tasks spawned with the Task tool.
|
|
4
2
|
|
|
5
3
|
## When to Use
|
|
6
4
|
|
|
7
5
|
Use TaskOutput when:
|
|
6
|
+
|
|
8
7
|
- Task tool returns truncated preview with "Output truncated" message
|
|
9
8
|
- You need full output to debug errors or analyze detailed results
|
|
10
9
|
- Task tool's summary shows substantial line/character counts but preview is incomplete
|
|
11
10
|
- You're analyzing multi-step task output requiring full context
|
|
12
11
|
|
|
13
12
|
Do NOT use when:
|
|
13
|
+
|
|
14
14
|
- Task preview already shows complete output (no truncation indicator)
|
|
15
15
|
- Summary alone answers your question
|
|
16
16
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
2
|
+
|
|
3
|
+
The Task tool launches specialized agents (workers) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
|
|
4
|
+
|
|
5
|
+
## Available Agents
|
|
6
|
+
|
|
7
|
+
{{AGENTS_LIST}}
|
|
8
|
+
|
|
9
|
+
## When NOT to Use
|
|
10
|
+
|
|
11
|
+
- Reading a specific file path → Use Read or Glob tool instead
|
|
12
|
+
- Searching for a specific class/function definition → Use Glob tool instead
|
|
13
|
+
- Searching code within 2-3 specific files → Use Read tool instead
|
|
14
|
+
- Tasks unrelated to the agent descriptions above
|
|
15
|
+
|
|
16
|
+
## Usage Notes
|
|
17
|
+
|
|
18
|
+
- Always include a short description of the task in the task parameter
|
|
19
|
+
- **Plan-then-execute**: Put shared constraints in `context`, keep each task focused, specify output format and acceptance criteria
|
|
20
|
+
- **Minimize tool chatter**: Avoid repeating large context; use Output tool with output ids for full logs
|
|
21
|
+
- **Parallelize**: Launch multiple agents concurrently whenever possible
|
|
22
|
+
- **Results are intermediate data**: Agent findings provide context for YOU to perform actual work. Do not treat agent reports as "task complete" signals.
|
|
23
|
+
- **Stateless invocations**: Each agent runs autonomously and returns a single final message. Include all necessary context and specify exactly what information to return.
|
|
24
|
+
- **Trust outputs**: Agent results should generally be trusted
|
|
25
|
+
- **Clarify intent**: Tell the agent whether you expect code changes or just research (search, file reads, web fetches)
|
|
26
|
+
- **Proactive use**: If an agent description says to use it proactively, do so without waiting for explicit user request
|
|
27
|
+
|
|
28
|
+
## Parameters
|
|
29
|
+
|
|
30
|
+
- `tasks`: Array of `{agent, task, description?, model?}` - tasks to run in parallel (max {{MAX_PARALLEL_TASKS}}, {{MAX_CONCURRENCY}} concurrent)
|
|
31
|
+
- `model`: (optional) Override the agent's default model with fuzzy matching (e.g., "sonnet", "codex", "5.2"). Supports comma-separated fallbacks: "gpt, opus" tries gpt first, then opus. Use "default" for omp's default model
|
|
32
|
+
- `context`: (optional) Shared context string prepended to all task prompts - use this to avoid repeating instructions
|
|
33
|
+
|
|
34
|
+
## Examples
|
|
35
|
+
|
|
36
|
+
<example>
|
|
37
|
+
user: "Please write a function that checks if a number is prime"
|
|
38
|
+
assistant: Sure let me write a function that checks if a number is prime
|
|
39
|
+
assistant: I'm going to use the Write tool to write the following code:
|
|
40
|
+
<code>
|
|
41
|
+
function isPrime(n) {
|
|
42
|
+
if (n <= 1) return false
|
|
43
|
+
for (let i = 2; i * i <= n; i++) {
|
|
44
|
+
if (n % i === 0) return false
|
|
45
|
+
}
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
</code>
|
|
49
|
+
<commentary>
|
|
50
|
+
Since a significant piece of code was written and the task was completed, now use the code-reviewer agent to review the code
|
|
51
|
+
</commentary>
|
|
52
|
+
assistant: Now let me use the code-reviewer agent to review the code
|
|
53
|
+
assistant: Uses the Task tool: { tasks: [{ agent: "code-reviewer", task: "Review the isPrime function" }] }
|
|
54
|
+
</example>
|
|
55
|
+
|
|
56
|
+
<example>
|
|
57
|
+
user: "Find all TODO comments in the codebase"
|
|
58
|
+
assistant: I'll use multiple explore agents to search different directories in parallel
|
|
59
|
+
assistant: Uses the Task tool:
|
|
60
|
+
{
|
|
61
|
+
"context": "Find all TODO comments. Return file:line:content format.",
|
|
62
|
+
"tasks": [
|
|
63
|
+
{ "agent": "explore", "task": "Search in src/" },
|
|
64
|
+
{ "agent": "explore", "task": "Search in lib/" },
|
|
65
|
+
{ "agent": "explore", "task": "Search in tests/" }
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
</example>
|