@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.
Files changed (71) hide show
  1. package/CHANGELOG.md +55 -1
  2. package/docs/sdk.md +47 -50
  3. package/examples/custom-tools/README.md +0 -15
  4. package/examples/hooks/custom-compaction.ts +1 -3
  5. package/examples/sdk/README.md +6 -10
  6. package/package.json +5 -5
  7. package/src/cli/args.ts +9 -6
  8. package/src/core/agent-session.ts +3 -3
  9. package/src/core/custom-commands/bundled/wt/index.ts +3 -0
  10. package/src/core/custom-tools/wrapper.ts +0 -1
  11. package/src/core/extensions/index.ts +1 -6
  12. package/src/core/extensions/wrapper.ts +0 -7
  13. package/src/core/file-mentions.ts +5 -8
  14. package/src/core/sdk.ts +48 -111
  15. package/src/core/session-manager.ts +7 -0
  16. package/src/core/system-prompt.ts +22 -33
  17. package/src/core/tools/ask.ts +14 -7
  18. package/src/core/tools/bash-interceptor.ts +4 -4
  19. package/src/core/tools/bash.ts +19 -9
  20. package/src/core/tools/complete.ts +131 -0
  21. package/src/core/tools/context.ts +7 -0
  22. package/src/core/tools/edit.ts +8 -15
  23. package/src/core/tools/exa/render.ts +4 -16
  24. package/src/core/tools/find.ts +7 -18
  25. package/src/core/tools/git.ts +13 -3
  26. package/src/core/tools/grep.ts +7 -18
  27. package/src/core/tools/index.test.ts +188 -0
  28. package/src/core/tools/index.ts +106 -236
  29. package/src/core/tools/jtd-to-json-schema.ts +274 -0
  30. package/src/core/tools/ls.ts +4 -9
  31. package/src/core/tools/lsp/index.ts +32 -29
  32. package/src/core/tools/lsp/render.ts +7 -28
  33. package/src/core/tools/notebook.ts +3 -5
  34. package/src/core/tools/output.ts +130 -31
  35. package/src/core/tools/read.ts +8 -19
  36. package/src/core/tools/review.ts +0 -18
  37. package/src/core/tools/rulebook.ts +8 -2
  38. package/src/core/tools/task/agents.ts +28 -7
  39. package/src/core/tools/task/artifacts.ts +6 -9
  40. package/src/core/tools/task/discovery.ts +0 -6
  41. package/src/core/tools/task/executor.ts +306 -257
  42. package/src/core/tools/task/index.ts +65 -235
  43. package/src/core/tools/task/name-generator.ts +247 -0
  44. package/src/core/tools/task/render.ts +158 -19
  45. package/src/core/tools/task/types.ts +13 -11
  46. package/src/core/tools/task/worker-protocol.ts +18 -0
  47. package/src/core/tools/task/worker.ts +270 -0
  48. package/src/core/tools/web-fetch.ts +4 -36
  49. package/src/core/tools/web-search/index.ts +2 -1
  50. package/src/core/tools/web-search/render.ts +1 -4
  51. package/src/core/tools/write.ts +7 -15
  52. package/src/discovery/helpers.test.ts +1 -1
  53. package/src/index.ts +5 -16
  54. package/src/main.ts +4 -4
  55. package/src/modes/interactive/theme/theme.ts +4 -4
  56. package/src/prompts/task.md +14 -57
  57. package/src/prompts/tools/output.md +4 -3
  58. package/src/prompts/tools/task.md +70 -0
  59. package/examples/custom-tools/question/index.ts +0 -84
  60. package/examples/custom-tools/subagent/README.md +0 -172
  61. package/examples/custom-tools/subagent/agents/planner.md +0 -37
  62. package/examples/custom-tools/subagent/agents/scout.md +0 -50
  63. package/examples/custom-tools/subagent/agents/worker.md +0 -24
  64. package/examples/custom-tools/subagent/agents.ts +0 -156
  65. package/examples/custom-tools/subagent/commands/implement-and-review.md +0 -10
  66. package/examples/custom-tools/subagent/commands/implement.md +0 -10
  67. package/examples/custom-tools/subagent/commands/scout-and-plan.md +0 -9
  68. package/examples/custom-tools/subagent/index.ts +0 -1002
  69. package/examples/sdk/05-tools.ts +0 -94
  70. package/examples/sdk/12-full-control.ts +0 -95
  71. 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(_cwd: string): AgentTool<typeof webFetchSchema> {
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 { CustomTool, CustomToolContext, RenderResultOptions } from "../custom-tools/types";
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(_cwd: string): AgentTool<typeof webSearchSchema> {
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) {
@@ -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 { type FileDiagnosticsResult, type WritethroughCallback, writethroughNoop } from "./lsp/index";
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
- cwd: string,
29
- options: WriteToolOptions = {},
30
- ): AgentTool<typeof writeSchema, WriteToolDetails> {
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
  // =============================================================================
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { parseFrontmatter } from "./helpers";
3
3
 
4
4
  describe("parseFrontmatter", () => {
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
- // Pre-built tools (use process.cwd())
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.tools = parsed.tools.map((name) => allTools[name]);
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 {
@@ -1,57 +1,14 @@
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
- You are a worker agent for delegated tasks. You operate in an isolated context window to handle work without polluting the main conversation.
9
-
10
- Do what has been asked; nothing more, nothing less. Work autonomously using all available tools.
11
-
12
- Your strengths:
13
-
14
- - Searching for code, configurations, and patterns across large codebases
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).