@mariozechner/pi-coding-agent 0.23.3 → 0.23.5

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 (120) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +20 -13
  3. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  4. package/dist/core/custom-tools/loader.js +56 -4
  5. package/dist/core/custom-tools/loader.js.map +1 -1
  6. package/dist/core/custom-tools/types.d.ts +9 -1
  7. package/dist/core/custom-tools/types.d.ts.map +1 -1
  8. package/dist/core/custom-tools/types.js.map +1 -1
  9. package/dist/core/hooks/index.d.ts +2 -1
  10. package/dist/core/hooks/index.d.ts.map +1 -1
  11. package/dist/core/hooks/index.js +1 -0
  12. package/dist/core/hooks/index.js.map +1 -1
  13. package/dist/core/hooks/loader.d.ts.map +1 -1
  14. package/dist/core/hooks/loader.js +4 -2
  15. package/dist/core/hooks/loader.js.map +1 -1
  16. package/dist/core/hooks/runner.d.ts.map +1 -1
  17. package/dist/core/hooks/runner.js +44 -4
  18. package/dist/core/hooks/runner.js.map +1 -1
  19. package/dist/core/hooks/tool-wrapper.d.ts.map +1 -1
  20. package/dist/core/hooks/tool-wrapper.js +5 -9
  21. package/dist/core/hooks/tool-wrapper.js.map +1 -1
  22. package/dist/core/hooks/types.d.ts +73 -11
  23. package/dist/core/hooks/types.d.ts.map +1 -1
  24. package/dist/core/hooks/types.js +22 -1
  25. package/dist/core/hooks/types.js.map +1 -1
  26. package/dist/core/skills.d.ts +18 -5
  27. package/dist/core/skills.d.ts.map +1 -1
  28. package/dist/core/skills.js +183 -72
  29. package/dist/core/skills.js.map +1 -1
  30. package/dist/core/slash-commands.d.ts.map +1 -1
  31. package/dist/core/slash-commands.js +2 -2
  32. package/dist/core/slash-commands.js.map +1 -1
  33. package/dist/core/system-prompt.d.ts.map +1 -1
  34. package/dist/core/system-prompt.js +2 -2
  35. package/dist/core/system-prompt.js.map +1 -1
  36. package/dist/core/tools/bash.d.ts +5 -0
  37. package/dist/core/tools/bash.d.ts.map +1 -1
  38. package/dist/core/tools/bash.js.map +1 -1
  39. package/dist/core/tools/find.d.ts +5 -0
  40. package/dist/core/tools/find.d.ts.map +1 -1
  41. package/dist/core/tools/find.js.map +1 -1
  42. package/dist/core/tools/grep.d.ts +6 -0
  43. package/dist/core/tools/grep.d.ts.map +1 -1
  44. package/dist/core/tools/grep.js.map +1 -1
  45. package/dist/core/tools/index.d.ts +6 -5
  46. package/dist/core/tools/index.d.ts.map +1 -1
  47. package/dist/core/tools/index.js.map +1 -1
  48. package/dist/core/tools/ls.d.ts +5 -0
  49. package/dist/core/tools/ls.d.ts.map +1 -1
  50. package/dist/core/tools/ls.js.map +1 -1
  51. package/dist/core/tools/read.d.ts +4 -0
  52. package/dist/core/tools/read.d.ts.map +1 -1
  53. package/dist/core/tools/read.js.map +1 -1
  54. package/dist/index.d.ts +5 -3
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +5 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/main.d.ts.map +1 -1
  59. package/dist/main.js +4 -0
  60. package/dist/main.js.map +1 -1
  61. package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
  62. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  63. package/dist/modes/interactive/components/custom-editor.js +16 -7
  64. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  65. package/dist/modes/interactive/components/diff.d.ts +12 -0
  66. package/dist/modes/interactive/components/diff.d.ts.map +1 -0
  67. package/dist/modes/interactive/components/diff.js +133 -0
  68. package/dist/modes/interactive/components/diff.js.map +1 -0
  69. package/dist/modes/interactive/components/hook-input.d.ts.map +1 -1
  70. package/dist/modes/interactive/components/hook-input.js +2 -2
  71. package/dist/modes/interactive/components/hook-input.js.map +1 -1
  72. package/dist/modes/interactive/components/hook-selector.d.ts.map +1 -1
  73. package/dist/modes/interactive/components/hook-selector.js +2 -2
  74. package/dist/modes/interactive/components/hook-selector.js.map +1 -1
  75. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  76. package/dist/modes/interactive/components/model-selector.js +2 -2
  77. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  78. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  79. package/dist/modes/interactive/components/oauth-selector.js +2 -2
  80. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  81. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  82. package/dist/modes/interactive/components/session-selector.js +3 -3
  83. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  84. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  85. package/dist/modes/interactive/components/tool-execution.js +26 -20
  86. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  87. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  88. package/dist/modes/interactive/components/user-message-selector.js +3 -3
  89. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  90. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  91. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  92. package/dist/modes/interactive/interactive-mode.js +63 -1
  93. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  94. package/dist/modes/interactive/theme/dark.json +9 -9
  95. package/dist/modes/interactive/theme/light.json +9 -9
  96. package/dist/modes/interactive/theme/theme.d.ts +10 -0
  97. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  98. package/dist/modes/interactive/theme/theme.js +131 -3
  99. package/dist/modes/interactive/theme/theme.js.map +1 -1
  100. package/dist/modes/print-mode.d.ts.map +1 -1
  101. package/dist/modes/print-mode.js +10 -0
  102. package/dist/modes/print-mode.js.map +1 -1
  103. package/docs/custom-tools.md +43 -4
  104. package/docs/hooks.md +104 -5
  105. package/docs/skills.md +65 -24
  106. package/examples/custom-tools/README.md +18 -7
  107. package/examples/custom-tools/subagent/README.md +172 -0
  108. package/examples/custom-tools/subagent/agents/planner.md +37 -0
  109. package/examples/custom-tools/subagent/agents/reviewer.md +35 -0
  110. package/examples/custom-tools/subagent/agents/scout.md +50 -0
  111. package/examples/custom-tools/subagent/agents/worker.md +24 -0
  112. package/examples/custom-tools/subagent/agents.ts +157 -0
  113. package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
  114. package/examples/custom-tools/subagent/commands/implement.md +10 -0
  115. package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
  116. package/examples/custom-tools/subagent/index.ts +772 -0
  117. package/package.json +6 -6
  118. /package/examples/custom-tools/{hello.ts → hello/index.ts} +0 -0
  119. /package/examples/custom-tools/{question.ts → question/index.ts} +0 -0
  120. /package/examples/custom-tools/{todo.ts → todo/index.ts} +0 -0
@@ -90,5 +90,15 @@ export async function runPrintMode(session, mode, messages, initialMessage, init
90
90
  }
91
91
  }
92
92
  }
93
+ // Ensure stdout is fully flushed before returning
94
+ // This prevents race conditions where the process exits before all output is written
95
+ await new Promise((resolve, reject) => {
96
+ process.stdout.write("", (err) => {
97
+ if (err)
98
+ reject(err);
99
+ else
100
+ resolve();
101
+ });
102
+ });
93
103
  }
94
104
  //# sourceMappingURL=print-mode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { Attachment } from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport type { AgentSession } from \"../core/agent-session.js\";\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n *\n * @param session The agent session\n * @param mode Output mode: \"text\" for final response only, \"json\" for all events\n * @param messages Array of prompts to send\n * @param initialMessage Optional first message (may contain @file content)\n * @param initialAttachments Optional attachments for the initial message\n */\nexport async function runPrintMode(\n\tsession: AgentSession,\n\tmode: \"text\" | \"json\",\n\tmessages: string[],\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n): Promise<void> {\n\t// Load entries once for session start events\n\tconst entries = session.sessionManager.loadEntries();\n\n\t// Hook runner already has no-op UI context by default (set in main.ts)\n\t// Set up hooks for print mode (no UI)\n\tconst hookRunner = session.hookRunner;\n\tif (hookRunner) {\n\t\t// Use actual session file if configured (via --session), otherwise null\n\t\thookRunner.setSessionFile(session.sessionFile);\n\t\thookRunner.onError((err) => {\n\t\t\tconsole.error(`Hook error (${err.hookPath}): ${err.error}`);\n\t\t});\n\t\t// No-op send handler for print mode (single-shot, no async messages)\n\t\thookRunner.setSendHandler(() => {\n\t\t\tconsole.error(\"Warning: pi.send() is not supported in print mode\");\n\t\t});\n\t\t// Emit session event\n\t\tawait hookRunner.emit({\n\t\t\ttype: \"session\",\n\t\t\tentries,\n\t\t\tsessionFile: session.sessionFile,\n\t\t\tpreviousSessionFile: null,\n\t\t\treason: \"start\",\n\t\t});\n\t}\n\n\t// Emit session start event to custom tools (no UI in print mode)\n\tfor (const { tool } of session.customTools) {\n\t\tif (tool.onSession) {\n\t\t\ttry {\n\t\t\t\tawait tool.onSession({\n\t\t\t\t\tentries,\n\t\t\t\t\tsessionFile: session.sessionFile,\n\t\t\t\t\tpreviousSessionFile: null,\n\t\t\t\t\treason: \"start\",\n\t\t\t\t});\n\t\t\t} catch (_err) {\n\t\t\t\t// Silently ignore tool errors\n\t\t\t}\n\t\t}\n\t}\n\n\t// Always subscribe to enable session persistence via _handleAgentEvent\n\tsession.subscribe((event) => {\n\t\t// In JSON mode, output all events\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(JSON.stringify(event));\n\t\t}\n\t});\n\n\t// Send initial message with attachments\n\tif (initialMessage) {\n\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t}\n\n\t// Send remaining messages\n\tfor (const message of messages) {\n\t\tawait session.prompt(message);\n\t}\n\n\t// In text mode, output final response\n\tif (mode === \"text\") {\n\t\tconst state = session.state;\n\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\n\t\t\t// Check for error/aborted\n\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Output text content\n\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\tconsole.log(content.text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { Attachment } from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport type { AgentSession } from \"../core/agent-session.js\";\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n *\n * @param session The agent session\n * @param mode Output mode: \"text\" for final response only, \"json\" for all events\n * @param messages Array of prompts to send\n * @param initialMessage Optional first message (may contain @file content)\n * @param initialAttachments Optional attachments for the initial message\n */\nexport async function runPrintMode(\n\tsession: AgentSession,\n\tmode: \"text\" | \"json\",\n\tmessages: string[],\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n): Promise<void> {\n\t// Load entries once for session start events\n\tconst entries = session.sessionManager.loadEntries();\n\n\t// Hook runner already has no-op UI context by default (set in main.ts)\n\t// Set up hooks for print mode (no UI)\n\tconst hookRunner = session.hookRunner;\n\tif (hookRunner) {\n\t\t// Use actual session file if configured (via --session), otherwise null\n\t\thookRunner.setSessionFile(session.sessionFile);\n\t\thookRunner.onError((err) => {\n\t\t\tconsole.error(`Hook error (${err.hookPath}): ${err.error}`);\n\t\t});\n\t\t// No-op send handler for print mode (single-shot, no async messages)\n\t\thookRunner.setSendHandler(() => {\n\t\t\tconsole.error(\"Warning: pi.send() is not supported in print mode\");\n\t\t});\n\t\t// Emit session event\n\t\tawait hookRunner.emit({\n\t\t\ttype: \"session\",\n\t\t\tentries,\n\t\t\tsessionFile: session.sessionFile,\n\t\t\tpreviousSessionFile: null,\n\t\t\treason: \"start\",\n\t\t});\n\t}\n\n\t// Emit session start event to custom tools (no UI in print mode)\n\tfor (const { tool } of session.customTools) {\n\t\tif (tool.onSession) {\n\t\t\ttry {\n\t\t\t\tawait tool.onSession({\n\t\t\t\t\tentries,\n\t\t\t\t\tsessionFile: session.sessionFile,\n\t\t\t\t\tpreviousSessionFile: null,\n\t\t\t\t\treason: \"start\",\n\t\t\t\t});\n\t\t\t} catch (_err) {\n\t\t\t\t// Silently ignore tool errors\n\t\t\t}\n\t\t}\n\t}\n\n\t// Always subscribe to enable session persistence via _handleAgentEvent\n\tsession.subscribe((event) => {\n\t\t// In JSON mode, output all events\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(JSON.stringify(event));\n\t\t}\n\t});\n\n\t// Send initial message with attachments\n\tif (initialMessage) {\n\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t}\n\n\t// Send remaining messages\n\tfor (const message of messages) {\n\t\tawait session.prompt(message);\n\t}\n\n\t// In text mode, output final response\n\tif (mode === \"text\") {\n\t\tconst state = session.state;\n\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\n\t\t\t// Check for error/aborted\n\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Output text content\n\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\tconsole.log(content.text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Ensure stdout is fully flushed before returning\n\t// This prevents race conditions where the process exits before all output is written\n\tawait new Promise<void>((resolve, reject) => {\n\t\tprocess.stdout.write(\"\", (err) => {\n\t\t\tif (err) reject(err);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
@@ -22,7 +22,7 @@ See [examples/custom-tools/](../examples/custom-tools/) for working examples.
22
22
 
23
23
  ## Quick Start
24
24
 
25
- Create a file `~/.pi/agent/tools/hello.ts`:
25
+ Create a file `~/.pi/agent/tools/hello/index.ts`:
26
26
 
27
27
  ```typescript
28
28
  import { Type } from "@sinclair/typebox";
@@ -51,13 +51,26 @@ The tool is automatically discovered and available in your next pi session.
51
51
 
52
52
  ## Tool Locations
53
53
 
54
+ Tools must be in a subdirectory with an `index.ts` entry point:
55
+
54
56
  | Location | Scope | Auto-discovered |
55
57
  |----------|-------|-----------------|
56
- | `~/.pi/agent/tools/*.ts` | Global (all projects) | Yes |
57
- | `.pi/tools/*.ts` | Project-local | Yes |
58
+ | `~/.pi/agent/tools/*/index.ts` | Global (all projects) | Yes |
59
+ | `.pi/tools/*/index.ts` | Project-local | Yes |
58
60
  | `settings.json` `customTools` array | Configured paths | Yes |
59
61
  | `--tool <path>` CLI flag | One-off/debugging | No |
60
62
 
63
+ **Example structure:**
64
+ ```
65
+ ~/.pi/agent/tools/
66
+ ├── hello/
67
+ │ └── index.ts # Entry point (auto-discovered)
68
+ └── complex-tool/
69
+ ├── index.ts # Entry point (auto-discovered)
70
+ ├── helpers.ts # Helper module (not loaded directly)
71
+ └── types.ts # Type definitions (not loaded directly)
72
+ ```
73
+
61
74
  **Priority:** Later sources win on name conflicts. CLI `--tool` takes highest priority.
62
75
 
63
76
  **Reserved names:** Custom tools cannot use built-in tool names (`read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`).
@@ -125,7 +138,7 @@ The factory receives a `ToolAPI` object (named `pi` by convention):
125
138
  ```typescript
126
139
  interface ToolAPI {
127
140
  cwd: string; // Current working directory
128
- exec(command: string, args: string[]): Promise<ExecResult>;
141
+ exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
129
142
  ui: {
130
143
  select(title: string, options: string[]): Promise<string | null>;
131
144
  confirm(title: string, message: string): Promise<boolean>;
@@ -134,10 +147,36 @@ interface ToolAPI {
134
147
  };
135
148
  hasUI: boolean; // false in --print or --mode rpc
136
149
  }
150
+
151
+ interface ExecOptions {
152
+ signal?: AbortSignal; // Cancel the process
153
+ timeout?: number; // Timeout in milliseconds
154
+ }
155
+
156
+ interface ExecResult {
157
+ stdout: string;
158
+ stderr: string;
159
+ code: number;
160
+ killed?: boolean; // True if process was killed by signal/timeout
161
+ }
137
162
  ```
138
163
 
139
164
  Always check `pi.hasUI` before using UI methods.
140
165
 
166
+ ### Cancellation Example
167
+
168
+ Pass the `signal` from `execute` to `pi.exec` to support cancellation:
169
+
170
+ ```typescript
171
+ async execute(toolCallId, params, signal) {
172
+ const result = await pi.exec("long-running-command", ["arg"], { signal });
173
+ if (result.killed) {
174
+ return { content: [{ type: "text", text: "Cancelled" }] };
175
+ }
176
+ return { content: [{ type: "text", text: result.stdout }] };
177
+ }
178
+ ```
179
+
141
180
  ## Session Lifecycle
142
181
 
143
182
  Tools can implement `onSession` to react to session changes:
package/docs/hooks.md CHANGED
@@ -222,13 +222,104 @@ Fired after tool executes. **Can modify result.**
222
222
 
223
223
  ```typescript
224
224
  pi.on("tool_result", async (event, ctx) => {
225
- // event.toolName, event.toolCallId, event.input
226
- // event.result: string
225
+ // event.toolName: string
226
+ // event.toolCallId: string
227
+ // event.input: Record<string, unknown>
228
+ // event.content: (TextContent | ImageContent)[]
229
+ // event.details: tool-specific (see below)
227
230
  // event.isError: boolean
228
- return { result: "modified" }; // or undefined to keep original
231
+
232
+ // Return modified content/details, or undefined to keep original
233
+ return { content: [...], details: {...} };
229
234
  });
230
235
  ```
231
236
 
237
+ The event type is a discriminated union based on `toolName`. Use the provided type guards to narrow `details` to the correct type:
238
+
239
+ ```typescript
240
+ import { isBashToolResult, type HookAPI } from "@mariozechner/pi-coding-agent/hooks";
241
+
242
+ export default function (pi: HookAPI) {
243
+ pi.on("tool_result", async (event, ctx) => {
244
+ if (isBashToolResult(event)) {
245
+ // event.details is BashToolDetails | undefined
246
+ if (event.details?.truncation?.truncated) {
247
+ // Access full output from temp file
248
+ const fullPath = event.details.fullOutputPath;
249
+ }
250
+ }
251
+ });
252
+ }
253
+ ```
254
+
255
+ Available type guards: `isBashToolResult`, `isReadToolResult`, `isEditToolResult`, `isWriteToolResult`, `isGrepToolResult`, `isFindToolResult`, `isLsToolResult`.
256
+
257
+ #### Tool Details Types
258
+
259
+ Each built-in tool has a typed `details` field. Types are exported from `@mariozechner/pi-coding-agent`:
260
+
261
+ | Tool | Details Type | Source |
262
+ |------|-------------|--------|
263
+ | `bash` | `BashToolDetails` | `src/core/tools/bash.ts` |
264
+ | `read` | `ReadToolDetails` | `src/core/tools/read.ts` |
265
+ | `edit` | `undefined` | - |
266
+ | `write` | `undefined` | - |
267
+ | `grep` | `GrepToolDetails` | `src/core/tools/grep.ts` |
268
+ | `find` | `FindToolDetails` | `src/core/tools/find.ts` |
269
+ | `ls` | `LsToolDetails` | `src/core/tools/ls.ts` |
270
+
271
+ Common fields in details:
272
+ - `truncation?: TruncationResult` - present when output was truncated
273
+ - `fullOutputPath?: string` - path to temp file with full output (bash only)
274
+
275
+ `TruncationResult` contains:
276
+ - `truncated: boolean` - whether truncation occurred
277
+ - `truncatedBy: "lines" | "bytes" | null` - which limit was hit
278
+ - `totalLines`, `totalBytes` - original size
279
+ - `outputLines`, `outputBytes` - truncated size
280
+
281
+ Custom tools use `CustomToolResultEvent` with `details: unknown`. Create your own type guard to get full type safety:
282
+
283
+ ```typescript
284
+ import {
285
+ isBashToolResult,
286
+ type CustomToolResultEvent,
287
+ type HookAPI,
288
+ type ToolResultEvent,
289
+ } from "@mariozechner/pi-coding-agent/hooks";
290
+
291
+ interface MyCustomToolDetails {
292
+ someField: string;
293
+ }
294
+
295
+ // Type guard that narrows both toolName and details
296
+ function isMyCustomToolResult(e: ToolResultEvent): e is CustomToolResultEvent & {
297
+ toolName: "my-custom-tool";
298
+ details: MyCustomToolDetails;
299
+ } {
300
+ return e.toolName === "my-custom-tool";
301
+ }
302
+
303
+ export default function (pi: HookAPI) {
304
+ pi.on("tool_result", async (event, ctx) => {
305
+ // Built-in tool: use provided type guard
306
+ if (isBashToolResult(event)) {
307
+ if (event.details?.fullOutputPath) {
308
+ console.log(`Full output at: ${event.details.fullOutputPath}`);
309
+ }
310
+ }
311
+
312
+ // Custom tool: use your own type guard
313
+ if (isMyCustomToolResult(event)) {
314
+ // event.details is now MyCustomToolDetails
315
+ console.log(event.details.someField);
316
+ }
317
+ });
318
+ }
319
+ ```
320
+
321
+ **Note:** If you modify `content`, you should also update `details` accordingly. The TUI uses `details` (e.g., truncation info) for rendering, so inconsistent values will cause display issues.
322
+
232
323
  ## Context API
233
324
 
234
325
  Every event handler receives a context object with these methods:
@@ -272,15 +363,23 @@ ctx.ui.notify("Operation complete", "info");
272
363
  ctx.ui.notify("Something went wrong", "error");
273
364
  ```
274
365
 
275
- ### ctx.exec(command, args)
366
+ ### ctx.exec(command, args, options?)
276
367
 
277
- Execute a command and get the result.
368
+ Execute a command and get the result. Supports cancellation via `AbortSignal` and timeout.
278
369
 
279
370
  ```typescript
280
371
  const result = await ctx.exec("git", ["status"]);
281
372
  // result.stdout: string
282
373
  // result.stderr: string
283
374
  // result.code: number
375
+ // result.killed?: boolean // True if killed by signal/timeout
376
+
377
+ // With timeout (5 seconds)
378
+ const result = await ctx.exec("slow-command", [], { timeout: 5000 });
379
+
380
+ // With abort signal
381
+ const controller = new AbortController();
382
+ const result = await ctx.exec("long-command", [], { signal: controller.signal });
284
383
  ```
285
384
 
286
385
  ### ctx.cwd
package/docs/skills.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Skills are self-contained capability packages that the agent loads on-demand. A skill provides specialized workflows, setup instructions, helper scripts, and reference documentation for specific tasks.
4
4
 
5
+ Pi implements the [Agent Skills standard](https://agentskills.io/specification).
6
+
5
7
  **Example use cases:**
6
8
  - Web search and content extraction (Brave Search API)
7
9
  - Browser automation via Chrome DevTools Protocol
@@ -65,14 +67,13 @@ description: What this skill does and when to use it. Be specific.
65
67
 
66
68
  Run once before first use:
67
69
  \`\`\`bash
68
- cd {baseDir}
69
- npm install
70
+ cd /path/to/skill && npm install
70
71
  \`\`\`
71
72
 
72
73
  ## Usage
73
74
 
74
75
  \`\`\`bash
75
- {baseDir}/scripts/process.sh <input>
76
+ ./scripts/process.sh <input>
76
77
  \`\`\`
77
78
 
78
79
  ## Workflow
@@ -84,20 +85,54 @@ npm install
84
85
 
85
86
  ### Frontmatter Fields
86
87
 
87
- | Field | Required | Description |
88
+ Per the [Agent Skills specification](https://agentskills.io/specification#frontmatter-required):
89
+
90
+ | Field | Required | Constraints |
88
91
  |-------|----------|-------------|
89
- | `description` | Yes | What the skill does and when to use it |
90
- | `name` | No | Override skill name (defaults to directory name) |
92
+ | `name` | Yes | Max 64 chars. Lowercase a-z, 0-9, hyphens only. Must match parent directory name. |
93
+ | `description` | Yes | Max 1024 chars. What the skill does and when to use it. |
94
+ | `license` | No | License name or reference to bundled license file. |
95
+ | `compatibility` | No | Max 500 chars. Environment requirements (system packages, network access, etc.). |
96
+ | `metadata` | No | Arbitrary key-value mapping for additional metadata. |
97
+ | `allowed-tools` | No | Space-delimited list of pre-approved tools (experimental). |
98
+
99
+ #### Name Validation
100
+
101
+ The `name` field must:
102
+ - Be 1-64 characters
103
+ - Contain only lowercase letters (a-z), numbers (0-9), and hyphens
104
+ - Not start or end with a hyphen
105
+ - Not contain consecutive hyphens (--)
106
+ - Match the parent directory name exactly
107
+
108
+ Valid: `pdf-processing`, `data-analysis`, `code-review`
109
+ Invalid: `PDF-Processing`, `-pdf`, `pdf--processing`
91
110
 
92
- The `description` is critical. It's shown in the system prompt and determines when the agent loads the skill. Be specific about both what it does and when to use it.
111
+ #### Description Best Practices
112
+
113
+ The `description` is critical. It determines when the agent loads the skill. Be specific about both what it does and when to use it.
114
+
115
+ Good:
116
+ ```yaml
117
+ description: Extracts text and tables from PDF files, fills PDF forms, and merges multiple PDFs. Use when working with PDF documents or when the user mentions PDFs, forms, or document extraction.
118
+ ```
119
+
120
+ Poor:
121
+ ```yaml
122
+ description: Helps with PDFs.
123
+ ```
93
124
 
94
- ### The `{baseDir}` Placeholder
125
+ ### File References
95
126
 
96
- Use `{baseDir}` to reference files in the skill's directory. The agent sees each skill's base directory and substitutes it when following instructions:
127
+ Use relative paths from the skill directory:
97
128
 
98
129
  ```markdown
99
- Helper scripts: {baseDir}/scripts/
100
- Config template: {baseDir}/assets/config.json
130
+ See [the reference guide](references/REFERENCE.md) for details.
131
+
132
+ Run the extraction script:
133
+ \`\`\`bash
134
+ ./scripts/extract.py input.pdf
135
+ \`\`\`
101
136
  ```
102
137
 
103
138
  ## Skill Locations
@@ -110,21 +145,28 @@ Skills are discovered from these locations (later wins on name collision):
110
145
  4. `~/.pi/agent/skills/**/SKILL.md` (Pi user, recursive)
111
146
  5. `<cwd>/.pi/skills/**/SKILL.md` (Pi project, recursive)
112
147
 
113
- ### Subdirectory Naming
114
-
115
- Pi skills in subdirectories use colon-separated names:
116
- - `~/.pi/agent/skills/db/migrate/SKILL.md` → `db:migrate`
117
- - `<cwd>/.pi/skills/aws/s3/upload/SKILL.md` → `aws:s3:upload`
118
-
119
148
  ## How Skills Work
120
149
 
121
150
  1. At startup, pi scans skill locations and extracts names + descriptions
122
- 2. The system prompt includes a list of available skills with their descriptions
151
+ 2. The system prompt includes available skills in XML format
123
152
  3. When a task matches, the agent uses `read` to load the full SKILL.md
124
- 4. The agent follows the instructions, using `{baseDir}` to reference scripts/assets
153
+ 4. The agent follows the instructions, using relative paths to reference scripts/assets
125
154
 
126
155
  This is progressive disclosure: only descriptions are always in context, full instructions load on-demand.
127
156
 
157
+ ## Validation Warnings
158
+
159
+ Pi validates skills against the Agent Skills standard and warns (but still loads) non-compliant skills:
160
+
161
+ - Name doesn't match parent directory
162
+ - Name exceeds 64 characters
163
+ - Name contains invalid characters
164
+ - Name starts/ends with hyphen or has consecutive hyphens
165
+ - Description missing or exceeds 1024 characters
166
+ - Unknown frontmatter fields
167
+
168
+ Name collisions (same name from different locations) warn and keep the first skill found.
169
+
128
170
  ## Example: Web Search Skill
129
171
 
130
172
  ```
@@ -146,21 +188,20 @@ description: Web search and content extraction via Brave Search API. Use for sea
146
188
  ## Setup
147
189
 
148
190
  \`\`\`bash
149
- cd {baseDir}
150
- npm install
191
+ cd /path/to/brave-search && npm install
151
192
  \`\`\`
152
193
 
153
194
  ## Search
154
195
 
155
196
  \`\`\`bash
156
- {baseDir}/search.js "query" # Basic search
157
- {baseDir}/search.js "query" --content # Include page content
197
+ ./search.js "query" # Basic search
198
+ ./search.js "query" --content # Include page content
158
199
  \`\`\`
159
200
 
160
201
  ## Extract Page Content
161
202
 
162
203
  \`\`\`bash
163
- {baseDir}/content.js https://example.com
204
+ ./content.js https://example.com
164
205
  \`\`\`
165
206
  ```
166
207
 
@@ -4,27 +4,38 @@ Example custom tools for pi-coding-agent.
4
4
 
5
5
  ## Examples
6
6
 
7
- ### hello.ts
7
+ Each example uses the `subdirectory/index.ts` structure required for tool discovery.
8
+
9
+ ### hello/
8
10
  Minimal example showing the basic structure of a custom tool.
9
11
 
10
- ### question.ts
12
+ ### question/
11
13
  Demonstrates `pi.ui.select()` for asking the user questions with options.
12
14
 
13
- ### todo.ts
15
+ ### todo/
14
16
  Full-featured example demonstrating:
15
17
  - `onSession` for state reconstruction from session history
16
18
  - Custom `renderCall` and `renderResult`
17
19
  - Proper branching support via details storage
18
20
  - State management without external files
19
21
 
22
+ ### subagent/
23
+ Delegate tasks to specialized subagents with isolated context windows. Includes:
24
+ - `index.ts` - The custom tool (single, parallel, and chain modes)
25
+ - `agents.ts` - Agent discovery helper
26
+ - `agents/` - Sample agent definitions (scout, planner, reviewer, worker)
27
+ - `commands/` - Workflow presets (/implement, /scout-and-plan, /implement-and-review)
28
+
29
+ See [subagent/README.md](subagent/README.md) for full documentation.
30
+
20
31
  ## Usage
21
32
 
22
33
  ```bash
23
- # Test directly
24
- pi --tool examples/custom-tools/todo.ts
34
+ # Test directly (can point to any .ts file)
35
+ pi --tool examples/custom-tools/todo/index.ts
25
36
 
26
- # Or copy to tools directory for persistent use
27
- cp todo.ts ~/.pi/agent/tools/
37
+ # Or copy entire folder to tools directory for persistent use
38
+ cp -r todo ~/.pi/agent/tools/
28
39
  ```
29
40
 
30
41
  Then in pi:
@@ -0,0 +1,172 @@
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
+ ├── subagent.ts # The custom tool (entry point)
20
+ ├── agents.ts # Agent discovery logic
21
+ ├── agents/ # Sample agent definitions
22
+ │ ├── scout.md # Fast recon, returns compressed context
23
+ │ ├── planner.md # Creates implementation plans
24
+ │ ├── reviewer.md # Code review
25
+ │ └── worker.md # General-purpose (full capabilities)
26
+ └── commands/ # Workflow presets
27
+ ├── implement.md # scout -> planner -> worker
28
+ ├── scout-and-plan.md # scout -> planner (no implementation)
29
+ └── implement-and-review.md # worker -> reviewer -> worker
30
+ ```
31
+
32
+ ## Installation
33
+
34
+ From the repository root, symlink the files:
35
+
36
+ ```bash
37
+ # Symlink the tool (must be in a subdirectory with index.ts)
38
+ mkdir -p ~/.pi/agent/tools/subagent
39
+ ln -sf "$(pwd)/packages/coding-agent/examples/custom-tools/subagent/subagent.ts" ~/.pi/agent/tools/subagent/index.ts
40
+ ln -sf "$(pwd)/packages/coding-agent/examples/custom-tools/subagent/agents.ts" ~/.pi/agent/tools/subagent/agents.ts
41
+
42
+ # Symlink agents
43
+ mkdir -p ~/.pi/agent/agents
44
+ for f in packages/coding-agent/examples/custom-tools/subagent/agents/*.md; do
45
+ ln -sf "$(pwd)/$f" ~/.pi/agent/agents/$(basename "$f")
46
+ done
47
+
48
+ # Symlink workflow commands
49
+ mkdir -p ~/.pi/agent/commands
50
+ for f in packages/coding-agent/examples/custom-tools/subagent/commands/*.md; do
51
+ ln -sf "$(pwd)/$f" ~/.pi/agent/commands/$(basename "$f")
52
+ done
53
+ ```
54
+
55
+ ## Security Model
56
+
57
+ This tool executes a separate `pi` subprocess with a delegated system prompt and tool/model configuration.
58
+
59
+ **Project-local agents** (`.pi/agents/*.md`) are repo-controlled prompts that can instruct the model to read files, run bash commands, etc.
60
+
61
+ **Default behavior:** Only loads **user-level agents** from `~/.pi/agent/agents`.
62
+
63
+ To enable project-local agents, pass `agentScope: "both"` (or `"project"`). Only do this for repositories you trust.
64
+
65
+ When running interactively, the tool prompts for confirmation before running project-local agents. Set `confirmProjectAgents: false` to disable.
66
+
67
+ ## Usage
68
+
69
+ ### Single agent
70
+ ```
71
+ Use scout to find all authentication code
72
+ ```
73
+
74
+ ### Parallel execution
75
+ ```
76
+ Run 2 scouts in parallel: one to find models, one to find providers
77
+ ```
78
+
79
+ ### Chained workflow
80
+ ```
81
+ Use a chain: first have scout find the read tool, then have planner suggest improvements
82
+ ```
83
+
84
+ ### Workflow commands
85
+ ```
86
+ /implement add Redis caching to the session store
87
+ /scout-and-plan refactor auth to support OAuth
88
+ /implement-and-review add input validation to API endpoints
89
+ ```
90
+
91
+ ## Tool Modes
92
+
93
+ | Mode | Parameter | Description |
94
+ |------|-----------|-------------|
95
+ | Single | `{ agent, task }` | One agent, one task |
96
+ | Parallel | `{ tasks: [...] }` | Multiple agents run concurrently (max 8, 4 concurrent) |
97
+ | Chain | `{ chain: [...] }` | Sequential with `{previous}` placeholder |
98
+
99
+ ## Output Display
100
+
101
+ **Collapsed view** (default):
102
+ - Status icon (✓/✗/⏳) and agent name
103
+ - Last 5-10 items (tool calls and text)
104
+ - Usage stats: `3 turns ↑input ↓output RcacheRead WcacheWrite $cost ctx:contextTokens model`
105
+
106
+ **Expanded view** (Ctrl+O):
107
+ - Full task text
108
+ - All tool calls with formatted arguments
109
+ - Final output rendered as Markdown
110
+ - Per-task usage (for chain/parallel)
111
+
112
+ **Parallel mode streaming**:
113
+ - Shows all tasks with live status (⏳ running, ✓ done, ✗ failed)
114
+ - Updates as each task makes progress
115
+ - Shows "2/3 done, 1 running" status
116
+
117
+ **Tool call formatting** (mimics built-in tools):
118
+ - `$ command` for bash
119
+ - `read ~/path:1-10` for read
120
+ - `grep /pattern/ in ~/path` for grep
121
+ - etc.
122
+
123
+ ## Agent Definitions
124
+
125
+ Agents are markdown files with YAML frontmatter:
126
+
127
+ ```markdown
128
+ ---
129
+ name: my-agent
130
+ description: What this agent does
131
+ tools: read, grep, find, ls
132
+ model: claude-haiku-4-5
133
+ ---
134
+
135
+ System prompt for the agent goes here.
136
+ ```
137
+
138
+ **Locations:**
139
+ - `~/.pi/agent/agents/*.md` - User-level (always loaded)
140
+ - `.pi/agents/*.md` - Project-level (only with `agentScope: "project"` or `"both"`)
141
+
142
+ Project agents override user agents with the same name when `agentScope: "both"`.
143
+
144
+ ## Sample Agents
145
+
146
+ | Agent | Purpose | Model | Tools |
147
+ |-------|---------|-------|-------|
148
+ | `scout` | Fast codebase recon | Haiku | read, grep, find, ls, bash |
149
+ | `planner` | Implementation plans | Sonnet | read, grep, find, ls |
150
+ | `reviewer` | Code review | Sonnet | read, grep, find, ls, bash |
151
+ | `worker` | General-purpose | Sonnet | (all default) |
152
+
153
+ ## Workflow Commands
154
+
155
+ | Command | Flow |
156
+ |---------|------|
157
+ | `/implement <query>` | scout → planner → worker |
158
+ | `/scout-and-plan <query>` | scout → planner |
159
+ | `/implement-and-review <query>` | worker → reviewer → worker |
160
+
161
+ ## Error Handling
162
+
163
+ - **Exit code != 0**: Tool returns error with stderr/output
164
+ - **stopReason "error"**: LLM error propagated with error message
165
+ - **stopReason "aborted"**: User abort (Ctrl+C) kills subprocess, throws error
166
+ - **Chain mode**: Stops at first failing step, reports which step failed
167
+
168
+ ## Limitations
169
+
170
+ - Output truncated to last 10 items in collapsed view (expand to see all)
171
+ - Agents discovered fresh on each invocation (allows editing mid-session)
172
+ - 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.