@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.
Files changed (66) hide show
  1. package/CHANGELOG.md +40 -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-tools/wrapper.ts +0 -1
  10. package/src/core/extensions/index.ts +1 -6
  11. package/src/core/extensions/wrapper.ts +0 -7
  12. package/src/core/file-mentions.ts +5 -8
  13. package/src/core/sdk.ts +41 -111
  14. package/src/core/session-manager.ts +7 -0
  15. package/src/core/system-prompt.ts +22 -33
  16. package/src/core/tools/ask.ts +14 -7
  17. package/src/core/tools/bash-interceptor.ts +4 -4
  18. package/src/core/tools/bash.ts +19 -9
  19. package/src/core/tools/context.ts +7 -0
  20. package/src/core/tools/edit.ts +8 -15
  21. package/src/core/tools/exa/render.ts +4 -16
  22. package/src/core/tools/find.ts +7 -18
  23. package/src/core/tools/git.ts +13 -3
  24. package/src/core/tools/grep.ts +7 -18
  25. package/src/core/tools/index.test.ts +180 -0
  26. package/src/core/tools/index.ts +94 -237
  27. package/src/core/tools/ls.ts +4 -9
  28. package/src/core/tools/lsp/index.ts +32 -29
  29. package/src/core/tools/lsp/render.ts +7 -28
  30. package/src/core/tools/notebook.ts +3 -5
  31. package/src/core/tools/output.ts +5 -17
  32. package/src/core/tools/read.ts +8 -19
  33. package/src/core/tools/review.ts +0 -18
  34. package/src/core/tools/rulebook.ts +8 -2
  35. package/src/core/tools/task/agents.ts +28 -7
  36. package/src/core/tools/task/discovery.ts +0 -6
  37. package/src/core/tools/task/executor.ts +264 -254
  38. package/src/core/tools/task/index.ts +45 -220
  39. package/src/core/tools/task/render.ts +21 -11
  40. package/src/core/tools/task/types.ts +6 -11
  41. package/src/core/tools/task/worker-protocol.ts +17 -0
  42. package/src/core/tools/task/worker.ts +238 -0
  43. package/src/core/tools/web-fetch.ts +4 -36
  44. package/src/core/tools/web-search/index.ts +2 -1
  45. package/src/core/tools/web-search/render.ts +1 -4
  46. package/src/core/tools/write.ts +7 -15
  47. package/src/discovery/helpers.test.ts +1 -1
  48. package/src/index.ts +5 -16
  49. package/src/main.ts +4 -4
  50. package/src/modes/interactive/theme/theme.ts +4 -4
  51. package/src/prompts/task.md +0 -7
  52. package/src/prompts/tools/output.md +2 -2
  53. package/src/prompts/tools/task.md +68 -0
  54. package/examples/custom-tools/question/index.ts +0 -84
  55. package/examples/custom-tools/subagent/README.md +0 -172
  56. package/examples/custom-tools/subagent/agents/planner.md +0 -37
  57. package/examples/custom-tools/subagent/agents/scout.md +0 -50
  58. package/examples/custom-tools/subagent/agents/worker.md +0 -24
  59. package/examples/custom-tools/subagent/agents.ts +0 -156
  60. package/examples/custom-tools/subagent/commands/implement-and-review.md +0 -10
  61. package/examples/custom-tools/subagent/commands/implement.md +0 -10
  62. package/examples/custom-tools/subagent/commands/scout-and-plan.md +0 -9
  63. package/examples/custom-tools/subagent/index.ts +0 -1002
  64. package/examples/sdk/05-tools.ts +0 -94
  65. package/examples/sdk/12-full-control.ts +0 -95
  66. 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(_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,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>