opencode-ai-cli 1.17.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 (40) hide show
  1. package/GEMINI.md +11 -0
  2. package/cli.ts +17 -0
  3. package/commands/agent.ts +102 -0
  4. package/commands/chat.ts +518 -0
  5. package/commands/models.ts +10 -0
  6. package/commands/providers/index.ts +10 -0
  7. package/commands/providers/login.ts +30 -0
  8. package/commands/providers/logout.ts +13 -0
  9. package/commands/providers/setProvider.ts +9 -0
  10. package/package.json +22 -0
  11. package/src/core/auth-storage.ts +136 -0
  12. package/src/core/model-registry.ts +23 -0
  13. package/src/engine/agentLoop.ts +389 -0
  14. package/src/engine/messages.ts +110 -0
  15. package/src/engine/systemPrompt.ts +58 -0
  16. package/src/engine/type.ts +133 -0
  17. package/src/providers/gemini.ts +122 -0
  18. package/src/providers/openai.ts +60 -0
  19. package/src/subagent/README.md +177 -0
  20. package/src/subagent/agents/planner.md +37 -0
  21. package/src/subagent/agents/reviewer.md +35 -0
  22. package/src/subagent/agents/scout.md +49 -0
  23. package/src/subagent/agents/worker.md +29 -0
  24. package/src/subagent/agents.ts +89 -0
  25. package/src/subagent/index.ts +224 -0
  26. package/src/subagent/prompts/implement-and-review.md +10 -0
  27. package/src/subagent/prompts/implement.md +10 -0
  28. package/src/subagent/prompts/scout-and-plan.md +9 -0
  29. package/src/tools/bash-tool.ts +44 -0
  30. package/src/tools/edit-tool.ts +85 -0
  31. package/src/tools/find-tool.ts +81 -0
  32. package/src/tools/grep-tool.ts +100 -0
  33. package/src/tools/index.ts +37 -0
  34. package/src/tools/ls-tool.ts +93 -0
  35. package/src/tools/plan-tool.ts +35 -0
  36. package/src/tools/read-tool.ts +89 -0
  37. package/src/tools/truncate.ts +21 -0
  38. package/src/tools/weather-tool.ts +55 -0
  39. package/src/tools/write-tool.ts +53 -0
  40. package/src/types.ts +28 -0
@@ -0,0 +1,110 @@
1
+ import { ProviderName } from "../core/auth-storage";
2
+ import { AgentMessage, AssistantMessage, ToolResultMessage } from "./type";
3
+
4
+ export function convertToLlm(
5
+ messages: AgentMessage[],
6
+ provider: ProviderName,
7
+ ) {
8
+ if (provider === "openai") {
9
+ return convertToOpenAi(messages);
10
+ } else if (provider === "gemini") {
11
+ return messages;
12
+ }
13
+
14
+ throw new Error(`Provider ${provider} not supported yet`);
15
+ }
16
+
17
+ export function convertToOpenAi(messages: AgentMessage[]): any[] {
18
+ return messages.map((msg) => {
19
+ switch (msg.role) {
20
+ case "system":
21
+ case "user":
22
+ return { role: msg.role, content: msg.content };
23
+
24
+ case "assistant": {
25
+ const textContent = msg.content
26
+ .filter((part) => part.type === "text")
27
+ .map((part: any) => part.text)
28
+ .join("\n");
29
+
30
+ const toolCalls = msg.content
31
+ .filter((part) => part.type === "toolCall")
32
+ .map((part: any) => part.toolCall);
33
+
34
+ return {
35
+ role: "assistant",
36
+ content: textContent || null,
37
+ tool_calls:
38
+ toolCalls.length > 0
39
+ ? toolCalls.map((tc: any) => ({
40
+ id: tc.id,
41
+ type: "function",
42
+ function: {
43
+ name: tc.name,
44
+ arguments: JSON.stringify(tc.arguments),
45
+ },
46
+ }))
47
+ : undefined,
48
+ };
49
+ }
50
+
51
+ case "tool":
52
+ return {
53
+ role: "tool",
54
+ tool_call_id: msg.toolCallId,
55
+ content: msg.content,
56
+ };
57
+
58
+ default:
59
+ const _exhaustiveCheck: never = msg;
60
+ return _exhaustiveCheck;
61
+ }
62
+ });
63
+ }
64
+
65
+ export function convertToGeminiAi(messages: AgentMessage[]): any[] {
66
+ return messages.map((msg) => {
67
+ switch (msg.role) {
68
+ case "system":
69
+ return msg;
70
+
71
+ case "user":
72
+ return { role: "user", parts: [{ text: msg.content }] };
73
+
74
+ case "assistant": {
75
+ const parts = msg.content.map((part) => {
76
+ if (part.type === "text") {
77
+ return { text: part.text };
78
+ } else if (part.type === "toolCall") {
79
+ return {
80
+ functionCall: {
81
+ name: part.toolCall.name,
82
+ args: part.toolCall.arguments,
83
+ },
84
+ };
85
+ } else if (part.type === "thinking") {
86
+ return { text: `Thinking: ${part.thinking}` };
87
+ }
88
+ return { text: "" };
89
+ });
90
+ return { role: "model", parts };
91
+ }
92
+
93
+ case "tool":
94
+ return {
95
+ role: "user", // Gemini uses 'user' role for function responses in some SDKs, or 'function'
96
+ parts: [
97
+ {
98
+ functionResponse: {
99
+ name: msg.name,
100
+ response: { content: msg.content },
101
+ },
102
+ },
103
+ ],
104
+ };
105
+
106
+ default:
107
+ return { role: "user", parts: [{ text: "" }] };
108
+ }
109
+ });
110
+ }
@@ -0,0 +1,58 @@
1
+ export function buildSystemPrompt(): string {
2
+ const cwd = process.cwd();
3
+ const date = new Date().toISOString().split("T")[0];
4
+
5
+ return `
6
+ You are a senior software engineering agent operating inside a user's terminal.
7
+
8
+ Current Date: ${date}
9
+ Current Working Directory: ${cwd}
10
+
11
+ # CRITICAL RESEARCH MANDATE
12
+
13
+ You are strictly forbidden from guessing, assuming, or hallucinating information about the user's project, files, or code.
14
+
15
+ # THE SCOUT AND PLAN PIPELINE (MANDATORY)
16
+
17
+ You are the Orchestrator. When the user asks a question about their project, repository, architecture, or asks you to implement a feature, fix a bug, or execute a change, you MUST follow this strict 2-step pipeline:
18
+
19
+ 1. THE SCOUT PHASE:
20
+ You MUST immediately call the 'subagent' tool using the PARALLEL mode by passing an array of 'tasks'.
21
+ Spawn 2 or 3 'scout' agents concurrently to map different parts of the project at the exact same time. This drastically speeds up research.
22
+ Example:
23
+ tasks: [
24
+ { agent: "scout", task: "Read package.json, configs, and find the main entry points." },
25
+ { agent: "scout", task: "Analyze the core logic in the src directory." },
26
+ { agent: "scout", task: "Investigate any specific bugs or files mentioned in the user's prompt: <user prompt>" }
27
+ ]
28
+ DO NOT use 'ls', 'read', 'grep', or 'find' yourself. You must delegate research to the scouts.
29
+ DO NOT guess the project structure. DO NOT call 'propose_plan' yet.
30
+ YOU MAY ONLY CALL THE SUBAGENT TOOL ONCE. Launch all your scouts in that single call.
31
+
32
+ 2. THE PLAN PHASE:
33
+ Only AFTER you receive the Markdown report from the scout subagent, you may call 'propose_plan'. Use the "Start Here" and "Architecture" sections of the scout report to formulate your step-by-step plan.
34
+ The plan must separate read-only investigation from mutating work. Ask for approval before any edit, write, delete, or command that can change project state.
35
+
36
+ 3. THE IMPLEMENTATION PHASE:
37
+ Once the user approves your plan, you are encouraged to use 'read_file', 'grep', 'edit_file', and 'bash' DIRECTLY to execute the steps. You do not need to call subagents for simple file reads or edits during this phase.
38
+
39
+ Calling 'propose_plan' or trying to change files before invoking the scout is strictly forbidden.
40
+
41
+ You operate under an "Evidence-First" policy.
42
+
43
+ Use tools only when they are needed to answer the user's request. For casual
44
+ conversation, greetings, thanks, or simple chat answer directly without using tools.
45
+
46
+ Rules:
47
+ - Never claim a file exists unless your scout has verified it.
48
+ - Never claim code behaves a certain way unless your scout has inspected it.
49
+ - Never invent dependencies, configurations, scripts, or APIs.
50
+ - MANDATORY: When using mutating tools (\`write_file\`, \`edit_file\`, \`bash\`), you MUST explicitly describe your action.
51
+ - For \`edit_file\`, clearly state what is being removed and what is being added, and identify the affected lines.
52
+ - For \`write_file\`, state that you are creating/overwriting a file at a specific path.
53
+ - For \`bash\`, state exactly what command you are running and its purpose.
54
+
55
+ If the answer cannot be determined from available evidence, respond:
56
+ "I searched the repository using the scout but could not find enough evidence."
57
+ `;
58
+ }
@@ -0,0 +1,133 @@
1
+ import { AgentTool, AgentToolResult } from "../types";
2
+
3
+ export type MessagePart =
4
+ | { type: "text"; text: string }
5
+ | { type: "toolCall"; toolCall: AgentToolCall }
6
+ | { type: "thinking"; thinking: string };
7
+
8
+ export interface AgentToolCall {
9
+ id: string;
10
+ name: string;
11
+ arguments: any;
12
+ metadata?: any;
13
+ }
14
+
15
+ export interface UserMessage {
16
+ role: "user";
17
+ content: string;
18
+ }
19
+
20
+ export interface SystemMessage {
21
+ role: "system";
22
+ content: string;
23
+ }
24
+
25
+ export interface AssistantMessage {
26
+ role: "assistant";
27
+ content: MessagePart[];
28
+ toolCalls?: AgentToolCall[];
29
+ stopReason?: "stop" | "tool_calls" | "error" | "aborted";
30
+ metadata?: any;
31
+ usage?: {
32
+ promptTokens: number;
33
+ completionTokens: number;
34
+ totalTokens: number;
35
+ };
36
+ }
37
+
38
+ export interface ToolResultMessage {
39
+ role: "tool";
40
+ toolCallId: string;
41
+ name: string;
42
+ content: string;
43
+ isError?: boolean;
44
+ terminate?: boolean;
45
+ metadata?: any;
46
+ usage?: {
47
+ promptTokens: number;
48
+ completionTokens: number;
49
+ totalTokens: number;
50
+ };
51
+ }
52
+
53
+ export type AgentMessage =
54
+ | SystemMessage
55
+ | UserMessage
56
+ | AssistantMessage
57
+ | ToolResultMessage;
58
+
59
+ export interface AgentContext {
60
+ cwd: string;
61
+ messages: AgentMessage[];
62
+ activeToolNames: string[];
63
+ systemPrompt?: string;
64
+ tools?: AgentTool[];
65
+ }
66
+
67
+ export interface BeforeToolCallContext {
68
+ assistantMessage: AssistantMessage;
69
+ toolCall: AgentToolCall;
70
+ context: AgentContext;
71
+ }
72
+
73
+ export interface AfterToolCallContext {
74
+ assistantMessage: AssistantMessage;
75
+ toolCall: AgentToolCall;
76
+ result: AgentToolResult;
77
+ isError: boolean;
78
+ context: AgentContext;
79
+ }
80
+
81
+ export interface ShouldStopAfterTurnContext {
82
+ message: AssistantMessage;
83
+ toolResults: ToolResultMessage[];
84
+ context: AgentContext;
85
+ newMessages: AgentMessage[];
86
+ }
87
+
88
+ import { ProviderName } from "../core/auth-storage";
89
+
90
+ export interface AgentLoopConfig {
91
+ provider: ProviderName;
92
+ apiKey: string;
93
+ model?: string;
94
+ maxTurns?: number;
95
+ toolExecution?: "sequential" | "parallel";
96
+ beforeToolCall?: (
97
+ ctx: BeforeToolCallContext,
98
+ ) => Promise<{ block?: boolean; reason?: string } | void>;
99
+ shouldStopAfterTurn?: (ctx: ShouldStopAfterTurnContext) => Promise<boolean>;
100
+ }
101
+
102
+ export type AgentEvent =
103
+ | { type: "agent_start" }
104
+ | { type: "agent_end"; message: AgentMessage[] }
105
+ | { type: "turn_start" }
106
+ | {
107
+ type: "turn_end";
108
+ message: AssistantMessage;
109
+ toolResults: ToolResultMessage[];
110
+ }
111
+ | { type: "message_start"; message: AgentMessage }
112
+ | { type: "message_update"; message: AssistantMessage }
113
+ | { type: "message_end"; message: AgentMessage }
114
+ | {
115
+ type: "tool_execution_start";
116
+ toolCallId: string;
117
+ toolName: string;
118
+ args: any;
119
+ }
120
+ | {
121
+ type: "tool_execution_end";
122
+ toolCallId: string;
123
+ toolName: string;
124
+ result: AgentToolResult;
125
+ isError: boolean;
126
+ };
127
+
128
+ export type AgentEventSink = (event: AgentEvent) => Promise<void> | void;
129
+
130
+ export type ExecutedToolCallBatch = {
131
+ messages: ToolResultMessage[];
132
+ terminate: boolean;
133
+ };
@@ -0,0 +1,122 @@
1
+ import { ChatRequest, ChatResponse } from "../core/model-registry";
2
+
3
+ function toGeminiContents(history: any[]) {
4
+ return history
5
+ .filter((message) => message.role !== "system")
6
+ .map((message) => {
7
+ if (message.role === "assistant") {
8
+ if (message.metadata?.geminiParts) {
9
+ return {
10
+ role: "model",
11
+ parts: message.metadata.geminiParts,
12
+ };
13
+ }
14
+
15
+ const parts = message.content.map((part: any) => {
16
+ if (part.type === "text") {
17
+ return { text: part.text };
18
+ }
19
+ if (part.type === "toolCall") {
20
+ return {
21
+ functionCall: {
22
+ name: part.toolCall.name,
23
+ args: part.toolCall.arguments,
24
+ ...part.toolCall.metadata,
25
+ },
26
+ };
27
+ }
28
+ return { text: "" };
29
+ });
30
+ return {
31
+ role: "model",
32
+ parts: parts,
33
+ };
34
+ }
35
+
36
+ if (message.role === "tool") {
37
+ return {
38
+ role: "user",
39
+ parts: [
40
+ {
41
+ functionResponse: {
42
+ name: message.name,
43
+ response: { result: message.content },
44
+ },
45
+ },
46
+ ],
47
+ };
48
+ }
49
+
50
+ return {
51
+ role: "user",
52
+ parts: [{ text: message.content ?? "" }],
53
+ };
54
+ });
55
+ }
56
+
57
+ export async function executeGemini(req: ChatRequest): Promise<ChatResponse> {
58
+ const systemMessage = req.history.find(
59
+ (message) => message.role === "system",
60
+ );
61
+ const tools = req.tools.map((tool) => tool.schema.function);
62
+
63
+ const modelToUse = req.model || "gemini-3-flash-preview";
64
+
65
+ const response = await fetch(
66
+ `https://generativelanguage.googleapis.com/v1beta/models/${modelToUse}:generateContent`,
67
+ {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ "x-goog-api-key": req.apiKey,
72
+ },
73
+ body: JSON.stringify({
74
+ systemInstruction: systemMessage
75
+ ? { parts: [{ text: systemMessage.content }] }
76
+ : undefined,
77
+ contents: toGeminiContents(req.history),
78
+ tools: tools.length > 0 ? [{ functionDeclarations: tools }] : undefined,
79
+ }),
80
+ signal: req.signal,
81
+ },
82
+ );
83
+
84
+ if (!response.ok) {
85
+ const errorData = await response.json().catch(() => null);
86
+ throw new Error(`Gemini API Error: ${response.statusText}\nDetails: ${JSON.stringify(errorData, null, 2)}`);
87
+ }
88
+
89
+ const data = await response.json();
90
+ const parts = data.candidates?.[0]?.content?.parts ?? [];
91
+ const text = parts
92
+ .filter((part: any) => typeof part.text === "string")
93
+ .map((part: any) => part.text)
94
+ .join("");
95
+ const functionCalls = parts.filter((part: any) => part.functionCall);
96
+
97
+ return {
98
+ text: text || null,
99
+ toolCalls:
100
+ functionCalls.length > 0
101
+ ? functionCalls.map((part: any, index: number) => {
102
+ const { name, args, ...rest } = part.functionCall;
103
+ return {
104
+ id: `gemini-tool-${index}`,
105
+ name: name,
106
+ arguments: args ?? {},
107
+ metadata: rest,
108
+ };
109
+ })
110
+ : null,
111
+ rawMessage: {
112
+ role: "assistant",
113
+ content: text || null,
114
+ geminiParts: parts,
115
+ },
116
+ usage: data.usageMetadata ? {
117
+ promptTokens: data.usageMetadata.promptTokenCount,
118
+ completionTokens: data.usageMetadata.candidatesTokenCount,
119
+ totalTokens: data.usageMetadata.totalTokenCount,
120
+ } : undefined,
121
+ };
122
+ }
@@ -0,0 +1,60 @@
1
+ import { ChatRequest, ChatResponse } from "../core/model-registry";
2
+
3
+ export async function executeOpenAI(req: ChatRequest): Promise<ChatResponse> {
4
+ const openaiTools = req.tools.map((t) => t.schema);
5
+
6
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
7
+ method: "POST",
8
+ headers: {
9
+ "Content-Type": "application/json",
10
+ Authorization: `Bearer ${req.apiKey}`,
11
+ },
12
+ body: JSON.stringify({
13
+ model: req.model || "gpt-4o-mini",
14
+ messages: req.history,
15
+ tools: openaiTools.length > 0 ? openaiTools : undefined,
16
+ tool_choice: openaiTools.length > 0 ? "auto" : undefined,
17
+ }),
18
+ signal: req.signal,
19
+ });
20
+
21
+ if (!response.ok) {
22
+ const errorData = await response.json();
23
+ throw new Error(`OpenAI API Error: ${response.statusText}\nDetails: ${JSON.stringify(errorData, null, 2)}`);
24
+ }
25
+
26
+ const data = await response.json();
27
+ const assistantMessage = data.choices[0].message;
28
+
29
+ let toolCalls = null;
30
+
31
+ if (assistantMessage.tool_calls) {
32
+ toolCalls = assistantMessage.tool_calls.map((tc: any) => {
33
+ try {
34
+ return {
35
+ id: tc.id,
36
+ name: tc.function.name,
37
+ arguments: JSON.parse(tc.function.arguments),
38
+ };
39
+ } catch (e) {
40
+ console.error("Failed to parse tool arguments:", tc.function.arguments);
41
+ return {
42
+ id: tc.id,
43
+ name: tc.function.name,
44
+ arguments: {}, // Fallback to empty object
45
+ };
46
+ }
47
+ });
48
+ }
49
+
50
+ return {
51
+ text: assistantMessage.content || null,
52
+ toolCalls: toolCalls,
53
+ rawMessage: assistantMessage,
54
+ usage: data.usage ? {
55
+ promptTokens: data.usage.prompt_tokens,
56
+ completionTokens: data.usage.completion_tokens,
57
+ totalTokens: data.usage.total_tokens,
58
+ } : undefined,
59
+ };
60
+ }
@@ -0,0 +1,177 @@
1
+ # Subagent Example
2
+
3
+ Delegate tasks to specialized subagents with isolated context windows.
4
+
5
+ ## Features
6
+
7
+ - **Isolated context**: Each subagent runs in a separate `pi` process
8
+ - **Streaming output**: See tool calls and progress as they happen
9
+ - **Parallel streaming**: All parallel tasks stream updates simultaneously
10
+ - **Markdown rendering**: Final output rendered with proper formatting (expanded view)
11
+ - **Usage tracking**: Shows turns, tokens, cost, and context usage per agent
12
+ - **Abort support**: Ctrl+C propagates to kill subagent processes
13
+
14
+ ## Structure
15
+
16
+ ```
17
+ subagent/
18
+ ├── README.md # This file
19
+ ├── index.ts # The extension (entry point)
20
+ ├── agents.ts # Agent discovery logic
21
+ ├── agents/ # Sample agent definitions
22
+ │ ├──
23
+
24
+ # Fast recon, returns compressed context
25
+ │ ├── planner.md # Creates implementation plans
26
+ │ ├── reviewer.md # Code review
27
+ │ └── worker.md # General-purpose (full capabilities)
28
+ └── prompts/ # Workflow presets (prompt templates)
29
+ ├── implement.md # scout -> planner -> worker
30
+ ├── scout-and-plan.md # scout -> planner (no implementation)
31
+ └── implement-and-review.md # worker -> reviewer -> worker
32
+ ```
33
+
34
+ ## Installation
35
+
36
+ From the repository root, symlink the files:
37
+
38
+ ```bash
39
+ # Symlink the extension (must be in a subdirectory with index.ts)
40
+ mkdir -p ~/.pi/agent/extensions/subagent
41
+ ln -sf "$(pwd)/packages/coding-agent/examples/extensions/subagent/index.ts" ~/.pi/agent/extensions/subagent/index.ts
42
+ ln -sf "$(pwd)/packages/coding-agent/examples/extensions/subagent/agents.ts" ~/.pi/agent/extensions/subagent/agents.ts
43
+
44
+ # Symlink agents
45
+ mkdir -p ~/.pi/agent/agents
46
+ for f in packages/coding-agent/examples/extensions/subagent/agents/*.md; do
47
+ ln -sf "$(pwd)/$f" ~/.pi/agent/agents/$(basename "$f")
48
+ done
49
+
50
+ # Symlink workflow prompts
51
+ mkdir -p ~/.pi/agent/prompts
52
+ for f in packages/coding-agent/examples/extensions/subagent/prompts/*.md; do
53
+ ln -sf "$(pwd)/$f" ~/.pi/agent/prompts/$(basename "$f")
54
+ done
55
+ ```
56
+
57
+ ## Security Model
58
+
59
+ This tool executes a separate `pi` subprocess with a delegated system prompt and tool/model configuration.
60
+
61
+ **Project-local agents** (`.pi/agents/*.md`) are repo-controlled prompts that can instruct the model to read files, run bash commands, etc.
62
+
63
+ **Default behavior:** Only loads **user-level agents** from `~/.pi/agent/agents`.
64
+
65
+ To enable project-local agents, pass `agentScope: "both"` (or `"project"`). Only do this for repositories you trust.
66
+
67
+ When running interactively, the tool prompts for confirmation before running project-local agents. Set `confirmProjectAgents: false` to disable.
68
+
69
+ ## Usage
70
+
71
+ ### Single agent
72
+ ```
73
+ Use scout to find all authentication code
74
+ ```
75
+
76
+ ### Parallel execution
77
+ ```
78
+ Run 2 scouts in parallel: one to find models, one to find providers
79
+ ```
80
+
81
+ ### Chained workflow
82
+ ```
83
+ Use a chain: first have scout find the read tool, then have planner suggest improvements
84
+ ```
85
+
86
+ ### Workflow prompts
87
+ ```
88
+ /implement add Redis caching to the session store
89
+ /scout-and-plan refactor auth to support OAuth
90
+ /implement-and-review add input validation to API endpoints
91
+ ```
92
+
93
+ ## Tool Modes
94
+
95
+ | Mode | Parameter | Description |
96
+ |------|-----------|-------------|
97
+ | Single | `{ agent, task }` | One agent, one task |
98
+ | Parallel | `{ tasks: [...] }` | Multiple agents run concurrently (max 8, 4 concurrent) |
99
+ | Chain | `{ chain: [...] }` | Sequential with `{previous}` placeholder |
100
+
101
+ ## Output Display
102
+
103
+ **Collapsed view** (default):
104
+ - Status icon (✓/✗/⏳) and agent name
105
+ - Last 5-10 items (tool calls and text)
106
+ - Usage stats: `3 turns ↑input ↓output RcacheRead WcacheWrite $cost ctx:contextTokens model`
107
+
108
+ **Expanded view** (Ctrl+O):
109
+ - Full task text
110
+ - All tool calls with formatted arguments
111
+ - Final output rendered as Markdown
112
+ - Per-task usage (for chain/parallel)
113
+
114
+ **Parallel mode streaming**:
115
+ - Shows all tasks with live status (⏳ running, ✓ done, ✗ failed)
116
+ - Updates as each task makes progress
117
+ - Shows "2/3 done, 1 running" status
118
+ - Returns each completed task's final output to the parent model, capped at 50 KB per task
119
+ - Returns failure diagnostics from stderr/error messages when a child exits before producing output
120
+
121
+ **Tool call formatting** (mimics built-in tools):
122
+ - `$ command` for bash
123
+ - `read ~/path:1-10` for read
124
+ - `grep /pattern/ in ~/path` for grep
125
+ - etc.
126
+
127
+ ## Agent Definitions
128
+
129
+ Agents are markdown files with YAML frontmatter:
130
+
131
+ ```markdown
132
+ ---
133
+ name: my-agent
134
+ description: What this agent does
135
+ tools: read, grep, find, ls
136
+ model: claude-haiku-4-5
137
+ ---
138
+
139
+ System prompt for the agent goes here.
140
+ ```
141
+
142
+ **Locations:**
143
+ - `~/.pi/agent/agents/*.md` - User-level (always loaded)
144
+ - `.pi/agents/*.md` - Project-level (only with `agentScope: "project"` or `"both"`)
145
+
146
+ Project agents override user agents with the same name when `agentScope: "both"`.
147
+
148
+ ## Sample Agents
149
+
150
+ | Agent | Purpose | Model | Tools |
151
+ |-------|---------|-------|-------|
152
+ | `scout` | Fast codebase recon | Haiku | read, grep, find, ls, bash |
153
+ | `planner` | Implementation plans | Sonnet | read, grep, find, ls |
154
+ | `reviewer` | Code review | Sonnet | read, grep, find, ls, bash |
155
+ | `worker` | General-purpose | Sonnet | (all default) |
156
+
157
+ ## Workflow Prompts
158
+
159
+ | Prompt | Flow |
160
+ |--------|------|
161
+ | `/implement <query>` | scout → planner → worker |
162
+ | `/scout-and-plan <query>` | scout → planner |
163
+ | `/implement-and-review <query>` | worker → reviewer → worker |
164
+
165
+ ## Error Handling
166
+
167
+ - **Exit code != 0**: Tool returns error with stderr/output
168
+ - **stopReason "error"**: LLM error propagated with error message
169
+ - **stopReason "aborted"**: User abort (Ctrl+C) kills subprocess, throws error
170
+ - **Chain mode**: Stops at first failing step, reports which step failed
171
+
172
+ ## Limitations
173
+
174
+ - Output truncated to last 10 items in collapsed view (expand to see all)
175
+ - Parallel model-visible output is capped at 50 KB per task; full results remain in tool details
176
+ - Agents discovered fresh on each invocation (allows editing mid-session)
177
+ - Parallel mode limited to 8 tasks, 4 concurrent
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: planner
3
+ description: Creates implementation plans from context and requirements
4
+ tools: read, grep, find, ls
5
+ model: claude-sonnet-4-5
6
+ ---
7
+
8
+ You are a planning specialist. You receive context (from a scout) and requirements, then produce a clear implementation plan.
9
+
10
+ You must NOT make any changes. Only read, analyze, and plan.
11
+
12
+ Input format you'll receive:
13
+ - Context/findings from a scout agent
14
+ - Original query or requirements
15
+
16
+ Output format:
17
+
18
+ ## Goal
19
+ One sentence summary of what needs to be done.
20
+
21
+ ## Plan
22
+ Numbered steps, each small and actionable:
23
+ 1. Step one - specific file/function to modify
24
+ 2. Step two - what to add/change
25
+ 3. ...
26
+
27
+ ## Files to Modify
28
+ - `path/to/file.ts` - what changes
29
+ - `path/to/other.ts` - what changes
30
+
31
+ ## New Files (if any)
32
+ - `path/to/new.ts` - purpose
33
+
34
+ ## Risks
35
+ Anything to watch out for.
36
+
37
+ Keep the plan concrete. The worker agent will execute it verbatim.