@oh-my-pi/pi-coding-agent 3.21.0 → 3.25.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 +55 -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-commands/bundled/wt/index.ts +3 -0
- 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 +48 -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/complete.ts +131 -0
- 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 +188 -0
- package/src/core/tools/index.ts +106 -236
- package/src/core/tools/jtd-to-json-schema.ts +274 -0
- 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 +130 -31
- 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/artifacts.ts +6 -9
- package/src/core/tools/task/discovery.ts +0 -6
- package/src/core/tools/task/executor.ts +306 -257
- package/src/core/tools/task/index.ts +65 -235
- package/src/core/tools/task/name-generator.ts +247 -0
- package/src/core/tools/task/render.ts +158 -19
- package/src/core/tools/task/types.ts +13 -11
- package/src/core/tools/task/worker-protocol.ts +18 -0
- package/src/core/tools/task/worker.ts +270 -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 +14 -57
- package/src/prompts/tools/output.md +4 -3
- package/src/prompts/tools/task.md +70 -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,270 @@
|
|
|
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 completionInstruction =
|
|
127
|
+
"When finished, call the complete tool exactly once. Do not end with a plain-text final answer.";
|
|
128
|
+
|
|
129
|
+
const { session } = await createAgentSession({
|
|
130
|
+
cwd: payload.cwd,
|
|
131
|
+
authStorage,
|
|
132
|
+
modelRegistry,
|
|
133
|
+
model,
|
|
134
|
+
thinkingLevel,
|
|
135
|
+
toolNames: payload.toolNames,
|
|
136
|
+
outputSchema: payload.outputSchema,
|
|
137
|
+
requireCompleteTool: true,
|
|
138
|
+
// Append system prompt (equivalent to CLI's --append-system-prompt)
|
|
139
|
+
systemPrompt: (defaultPrompt) => `${defaultPrompt}\n\n${payload.systemPrompt}\n\n${completionInstruction}`,
|
|
140
|
+
sessionManager,
|
|
141
|
+
hasUI: false,
|
|
142
|
+
// Pass spawn restrictions to nested tasks
|
|
143
|
+
spawns: payload.spawnsEnv,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
activeSession = session;
|
|
147
|
+
|
|
148
|
+
// Initialize extensions (equivalent to CLI's extension initialization)
|
|
149
|
+
// Note: Does not support --extension CLI flag or extension CLI flags
|
|
150
|
+
const extensionRunner = session.extensionRunner;
|
|
151
|
+
if (extensionRunner) {
|
|
152
|
+
extensionRunner.initialize({
|
|
153
|
+
getModel: () => session.model,
|
|
154
|
+
sendMessageHandler: (message, options) => {
|
|
155
|
+
session.sendCustomMessage(message, options).catch((e) => {
|
|
156
|
+
console.error(`Extension sendMessage failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
appendEntryHandler: (customType, data) => {
|
|
160
|
+
session.sessionManager.appendCustomEntry(customType, data);
|
|
161
|
+
},
|
|
162
|
+
getActiveToolsHandler: () => session.getActiveToolNames(),
|
|
163
|
+
getAllToolsHandler: () => session.getAllToolNames(),
|
|
164
|
+
setActiveToolsHandler: (toolNamesList: string[]) => session.setActiveToolsByName(toolNamesList),
|
|
165
|
+
});
|
|
166
|
+
extensionRunner.onError((err) => {
|
|
167
|
+
console.error(`Extension error (${err.extensionPath}): ${err.error}`);
|
|
168
|
+
});
|
|
169
|
+
await extensionRunner.emit({ type: "session_start" });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Track complete tool calls
|
|
173
|
+
const MAX_COMPLETE_RETRIES = 3;
|
|
174
|
+
let completeCalled = false;
|
|
175
|
+
|
|
176
|
+
// Subscribe to events and forward to parent (equivalent to --mode json output)
|
|
177
|
+
session.subscribe((event: AgentSessionEvent) => {
|
|
178
|
+
if (isAgentEvent(event)) {
|
|
179
|
+
postMessageSafe({ type: "event", event });
|
|
180
|
+
// Track when complete tool is called
|
|
181
|
+
if (event.type === "tool_execution_end" && event.toolName === "complete") {
|
|
182
|
+
completeCalled = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Run the prompt (equivalent to --prompt flag)
|
|
188
|
+
await session.prompt(payload.task);
|
|
189
|
+
|
|
190
|
+
// Retry loop if complete was not called
|
|
191
|
+
let retryCount = 0;
|
|
192
|
+
while (!completeCalled && retryCount < MAX_COMPLETE_RETRIES && !abortRequested) {
|
|
193
|
+
retryCount++;
|
|
194
|
+
const reminder = `<system-reminder>
|
|
195
|
+
CRITICAL: You stopped without calling the complete tool. This is reminder ${retryCount} of ${MAX_COMPLETE_RETRIES}.
|
|
196
|
+
|
|
197
|
+
You MUST call the complete tool to finish your task. Options:
|
|
198
|
+
1. Call complete with your result data if you have completed the task
|
|
199
|
+
2. Call complete with status="aborted" and an error message if you cannot complete the task
|
|
200
|
+
|
|
201
|
+
Failure to call complete after ${MAX_COMPLETE_RETRIES} reminders will result in task failure.
|
|
202
|
+
</system-reminder>
|
|
203
|
+
|
|
204
|
+
Call complete now.`;
|
|
205
|
+
|
|
206
|
+
await session.prompt(reminder);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Check if aborted during execution
|
|
210
|
+
const lastMessage = session.state.messages[session.state.messages.length - 1];
|
|
211
|
+
if (lastMessage?.role === "assistant" && lastMessage.stopReason === "aborted") {
|
|
212
|
+
aborted = true;
|
|
213
|
+
exitCode = 1;
|
|
214
|
+
}
|
|
215
|
+
} catch (err) {
|
|
216
|
+
exitCode = 1;
|
|
217
|
+
error = err instanceof Error ? err.stack || err.message : String(err);
|
|
218
|
+
} finally {
|
|
219
|
+
// Handle abort requested during execution
|
|
220
|
+
if (abortRequested) {
|
|
221
|
+
aborted = true;
|
|
222
|
+
if (exitCode === 0) exitCode = 1;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Cleanup session
|
|
226
|
+
if (activeSession) {
|
|
227
|
+
try {
|
|
228
|
+
await activeSession.dispose();
|
|
229
|
+
} catch {
|
|
230
|
+
// Ignore cleanup errors
|
|
231
|
+
}
|
|
232
|
+
activeSession = null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Send completion message to parent
|
|
236
|
+
postMessageSafe({
|
|
237
|
+
type: "done",
|
|
238
|
+
exitCode,
|
|
239
|
+
durationMs: Date.now() - startTime,
|
|
240
|
+
error,
|
|
241
|
+
aborted,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Handle abort request from parent */
|
|
247
|
+
function handleAbort(): void {
|
|
248
|
+
abortRequested = true;
|
|
249
|
+
if (activeSession) {
|
|
250
|
+
void activeSession.abort();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Message handler - receives start/abort commands from parent
|
|
255
|
+
globalThis.addEventListener("message", (event: WorkerMessageEvent<SubagentWorkerRequest>) => {
|
|
256
|
+
const message = event.data;
|
|
257
|
+
if (!message) return;
|
|
258
|
+
|
|
259
|
+
if (message.type === "abort") {
|
|
260
|
+
handleAbort();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (message.type === "start") {
|
|
265
|
+
// Only allow one task per worker
|
|
266
|
+
if (running) return;
|
|
267
|
+
running = true;
|
|
268
|
+
void runTask(message.payload);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
@@ -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,57 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
- Analyzing multiple files to understand system architecture
|
|
16
|
-
- Investigating complex questions that require exploring many files
|
|
17
|
-
- Performing multi-step research and implementation tasks
|
|
18
|
-
|
|
19
|
-
Guidelines:
|
|
20
|
-
|
|
21
|
-
- Persist until the task is fully resolved end-to-end when feasible.
|
|
22
|
-
- Verify with tools; ask for clarification when required.
|
|
23
|
-
- For file searches: Use grep/glob when you need to search broadly. Use read when you know the specific file path.
|
|
24
|
-
- For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.
|
|
25
|
-
- Be thorough: Check multiple locations, consider different naming conventions, look for related files.
|
|
26
|
-
- When spawning subagents with the Task tool, include a short, user-facing `description` for each task (5-8 words) that summarizes the approach.
|
|
27
|
-
- NEVER create files unless absolutely necessary. ALWAYS prefer editing existing files.
|
|
28
|
-
- NEVER proactively create documentation files (\*.md) or README files unless explicitly requested.
|
|
29
|
-
- Any file paths in your response MUST be absolute. Do NOT use relative paths.
|
|
30
|
-
- Include relevant code snippets in your final response.
|
|
31
|
-
|
|
32
|
-
Output format when finished:
|
|
33
|
-
|
|
34
|
-
## Completed
|
|
35
|
-
|
|
36
|
-
What was done.
|
|
37
|
-
|
|
38
|
-
## Files Changed
|
|
39
|
-
|
|
40
|
-
- `/absolute/path/to/file.ts` - what changed
|
|
41
|
-
|
|
42
|
-
## Key Code
|
|
43
|
-
|
|
44
|
-
Relevant snippets or signatures touched:
|
|
45
|
-
|
|
46
|
-
```language
|
|
47
|
-
// actual code
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Notes (if any)
|
|
51
|
-
|
|
52
|
-
Anything the main agent should know.
|
|
53
|
-
|
|
54
|
-
If handing off to another agent (e.g. reviewer), include:
|
|
55
|
-
|
|
56
|
-
- Exact file paths changed
|
|
57
|
-
- Key functions/types touched (short list)
|
|
1
|
+
You are a worker agent for delegated tasks in an isolated context. Finish only the assigned work and return the minimum useful result.
|
|
2
|
+
|
|
3
|
+
Principles:
|
|
4
|
+
|
|
5
|
+
- Be concise. No filler, repetition, or tool transcripts.
|
|
6
|
+
- If blocked, ask a single focused question; otherwise proceed autonomously.
|
|
7
|
+
- Prefer narrow search (grep/glob) then read only needed ranges.
|
|
8
|
+
- Avoid full-file reads unless necessary.
|
|
9
|
+
- NEVER create files unless absolutely required. Prefer edits to existing files.
|
|
10
|
+
- NEVER create documentation files (\*.md) unless explicitly requested.
|
|
11
|
+
- Any file paths in your response MUST be absolute.
|
|
12
|
+
- When spawning subagents with the Task tool, include a 5-8 word user-facing description.
|
|
13
|
+
- Include the smallest relevant code snippet when discussing code or config.
|
|
14
|
+
- Follow the main agent's instructions.
|
|
@@ -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
|
|
|
@@ -21,7 +21,8 @@ Do NOT use when:
|
|
|
21
21
|
- `"raw"` (default): Full output with ANSI codes preserved
|
|
22
22
|
- `"json"`: Structured object with metadata
|
|
23
23
|
- `"stripped"`: Plain text with ANSI codes removed for parsing
|
|
24
|
+
- `query` (optional): jq-like query for JSON outputs (e.g., `.result.items[0].name`)
|
|
24
25
|
- `offset` (optional): Line number to start reading from (1-indexed)
|
|
25
26
|
- `limit` (optional): Maximum number of lines to read
|
|
26
27
|
|
|
27
|
-
Use offset/limit for line ranges to reduce context usage on large outputs.
|
|
28
|
+
Use offset/limit for line ranges to reduce context usage on large outputs. Use `query` for JSON outputs (for example, subagent `complete` results).
|