@mariozechner/pi-coding-agent 0.30.1 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +251 -2
- package/README.md +105 -84
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +5 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts +3 -3
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +7 -10
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +73 -34
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +464 -210
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -7
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -52
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -2
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -2
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +84 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +233 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/{compaction.d.ts → compaction/compaction.d.ts} +38 -19
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +558 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +35 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +138 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/custom-tools/index.d.ts +2 -1
- package/dist/core/custom-tools/index.d.ts.map +1 -1
- package/dist/core/custom-tools/index.js +1 -0
- package/dist/core/custom-tools/index.js.map +1 -1
- package/dist/core/custom-tools/loader.d.ts.map +1 -1
- package/dist/core/custom-tools/loader.js +13 -80
- package/dist/core/custom-tools/loader.js.map +1 -1
- package/dist/core/custom-tools/types.d.ts +84 -59
- package/dist/core/custom-tools/types.d.ts.map +1 -1
- package/dist/core/custom-tools/types.js.map +1 -1
- package/dist/core/custom-tools/wrapper.d.ts +15 -0
- package/dist/core/custom-tools/wrapper.d.ts.map +1 -0
- package/dist/core/custom-tools/wrapper.js +23 -0
- package/dist/core/custom-tools/wrapper.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +71 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/export-html/index.d.ts +17 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +171 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +781 -0
- package/dist/core/export-html/template.html +54 -0
- package/dist/core/export-html/template.js +1185 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +6 -0
- package/dist/core/hooks/index.d.ts +4 -4
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +3 -3
- package/dist/core/hooks/index.js.map +1 -1
- package/dist/core/hooks/loader.d.ts +40 -5
- package/dist/core/hooks/loader.d.ts.map +1 -1
- package/dist/core/hooks/loader.js +43 -10
- package/dist/core/hooks/loader.js.map +1 -1
- package/dist/core/hooks/runner.d.ts +94 -18
- package/dist/core/hooks/runner.d.ts.map +1 -1
- package/dist/core/hooks/runner.js +199 -120
- package/dist/core/hooks/runner.js.map +1 -1
- package/dist/core/hooks/tool-wrapper.d.ts +1 -1
- package/dist/core/hooks/tool-wrapper.d.ts.map +1 -1
- package/dist/core/hooks/tool-wrapper.js +36 -19
- package/dist/core/hooks/tool-wrapper.js.map +1 -1
- package/dist/core/hooks/types.d.ts +407 -96
- package/dist/core/hooks/types.d.ts.map +1 -1
- package/dist/core/hooks/types.js.map +1 -1
- package/dist/core/index.d.ts +4 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +44 -12
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +82 -34
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -5
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +7 -7
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +7 -7
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +45 -14
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/sdk.d.ts +7 -10
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +88 -32
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +202 -36
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +565 -133
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +9 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +13 -12
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +6 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +33 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +171 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +7 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -95
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -25
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +28 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +125 -0
- package/dist/migrations.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +3 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +6 -2
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +12 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +30 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +14 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +35 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +14 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +36 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +5 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +5 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +12 -6
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +57 -25
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/hook-editor.d.ts +15 -0
- package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-editor.js +95 -0
- package/dist/modes/interactive/components/hook-editor.js.map +1 -0
- package/dist/modes/interactive/components/hook-message.d.ts +18 -0
- package/dist/modes/interactive/components/hook-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-message.js +80 -0
- package/dist/modes/interactive/components/hook-message.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +3 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +15 -2
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +70 -21
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +52 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +745 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +3 -3
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +1 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -5
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +29 -12
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +589 -208
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +13 -1
- package/dist/modes/interactive/theme/light.json +13 -1
- package/dist/modes/interactive/theme/theme-schema.json +34 -0
- package/dist/modes/interactive/theme/theme.d.ts +20 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +135 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +3 -3
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +26 -20
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +13 -10
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +11 -10
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +88 -35
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +30 -11
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/shell.d.ts +4 -2
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +36 -7
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +2 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +388 -0
- package/docs/custom-tools.md +146 -43
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +562 -596
- package/docs/rpc.md +33 -19
- package/docs/sdk.md +93 -21
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +172 -21
- package/docs/skills.md +2 -0
- package/docs/theme.md +31 -2
- package/docs/tree.md +197 -0
- package/docs/tui.md +343 -0
- package/examples/README.md +1 -9
- package/examples/custom-tools/hello/index.ts +4 -3
- package/examples/custom-tools/question/index.ts +4 -4
- package/examples/custom-tools/subagent/index.ts +7 -6
- package/examples/custom-tools/todo/index.ts +11 -5
- package/examples/hooks/README.md +29 -71
- package/examples/hooks/auto-commit-on-exit.ts +8 -9
- package/examples/hooks/confirm-destructive.ts +29 -30
- package/examples/hooks/custom-compaction.ts +20 -21
- package/examples/hooks/dirty-repo-guard.ts +41 -40
- package/examples/hooks/file-trigger.ts +10 -5
- package/examples/hooks/git-checkpoint.ts +16 -12
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +1 -1
- package/examples/hooks/protected-paths.ts +1 -1
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/05-tools.ts +4 -4
- package/examples/sdk/06-hooks.ts +1 -1
- package/examples/sdk/07-context-files.ts +1 -1
- package/examples/sdk/08-slash-commands.ts +6 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
- package/examples/sdk/10-settings.ts +1 -1
- package/examples/sdk/11-sessions.ts +1 -1
- package/examples/sdk/12-full-control.ts +4 -7
- package/package.json +6 -6
- package/dist/core/compaction.d.ts.map +0 -1
- package/dist/core/compaction.js +0 -412
- package/dist/core/compaction.js.map +0 -1
- package/dist/core/export-html.d.ts +0 -23
- package/dist/core/export-html.d.ts.map +0 -1
- package/dist/core/export-html.js +0 -1185
- package/dist/core/export-html.js.map +0 -1
- package/dist/modes/interactive/components/compaction.d.ts +0 -15
- package/dist/modes/interactive/components/compaction.d.ts.map +0 -1
- package/dist/modes/interactive/components/compaction.js +0 -41
- package/dist/modes/interactive/components/compaction.js.map +0 -1
- package/docs/hooks-v2.md +0 -385
- package/docs/session-tree.md +0 -452
package/docs/custom-tools.md
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
> pi can create custom tools. Ask it to build one for your use case.
|
|
2
|
+
|
|
1
3
|
# Custom Tools
|
|
2
4
|
|
|
3
5
|
Custom tools are additional tools that the LLM can call directly, just like the built-in `read`, `write`, `edit`, and `bash` tools. They are TypeScript modules that define callable functions with parameters, return values, and optional TUI rendering.
|
|
4
6
|
|
|
7
|
+
**Key capabilities:**
|
|
8
|
+
- **User interaction** - Prompt users via `pi.ui` (select, confirm, input dialogs)
|
|
9
|
+
- **Custom rendering** - Control how tool calls and results appear via `renderCall`/`renderResult`
|
|
10
|
+
- **TUI components** - Render custom components with `pi.ui.custom()` (see [tui.md](tui.md))
|
|
11
|
+
- **State management** - Persist state in tool result `details` for proper branching support
|
|
12
|
+
- **Streaming results** - Send partial updates via `onUpdate` callback
|
|
13
|
+
|
|
5
14
|
**Example use cases:**
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- Tools that need user confirmation before proceeding
|
|
15
|
+
- Interactive dialogs (questions with selectable options)
|
|
16
|
+
- Stateful tools (todo lists, connection pools)
|
|
17
|
+
- Rich output rendering (progress indicators, structured views)
|
|
18
|
+
- External service integrations with confirmation flows
|
|
11
19
|
|
|
12
20
|
**When to use custom tools vs. alternatives:**
|
|
13
21
|
|
|
@@ -36,10 +44,11 @@ const factory: CustomToolFactory = (pi) => ({
|
|
|
36
44
|
name: Type.String({ description: "Name to greet" }),
|
|
37
45
|
}),
|
|
38
46
|
|
|
39
|
-
async execute(toolCallId, params) {
|
|
47
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
48
|
+
const { name } = params as { name: string };
|
|
40
49
|
return {
|
|
41
|
-
content: [{ type: "text", text: `Hello, ${
|
|
42
|
-
details: { greeted:
|
|
50
|
+
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
51
|
+
details: { greeted: name },
|
|
43
52
|
};
|
|
44
53
|
},
|
|
45
54
|
});
|
|
@@ -82,7 +91,7 @@ Custom tools can import from these packages (automatically resolved by pi):
|
|
|
82
91
|
| Package | Purpose |
|
|
83
92
|
|---------|---------|
|
|
84
93
|
| `@sinclair/typebox` | Schema definitions (`Type.Object`, `Type.String`, etc.) |
|
|
85
|
-
| `@mariozechner/pi-coding-agent` | Types (`CustomToolFactory`, `
|
|
94
|
+
| `@mariozechner/pi-coding-agent` | Types (`CustomToolFactory`, `CustomTool`, `CustomToolContext`, etc.) |
|
|
86
95
|
| `@mariozechner/pi-ai` | AI utilities (`StringEnum` for Google-compatible enums) |
|
|
87
96
|
| `@mariozechner/pi-tui` | TUI components (`Text`, `Box`, etc. for custom rendering) |
|
|
88
97
|
|
|
@@ -94,7 +103,12 @@ Node.js built-in modules (`node:fs`, `node:path`, etc.) are also available.
|
|
|
94
103
|
import { Type } from "@sinclair/typebox";
|
|
95
104
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
96
105
|
import { Text } from "@mariozechner/pi-tui";
|
|
97
|
-
import type {
|
|
106
|
+
import type {
|
|
107
|
+
CustomTool,
|
|
108
|
+
CustomToolContext,
|
|
109
|
+
CustomToolFactory,
|
|
110
|
+
CustomToolSessionEvent,
|
|
111
|
+
} from "@mariozechner/pi-coding-agent";
|
|
98
112
|
|
|
99
113
|
const factory: CustomToolFactory = (pi) => ({
|
|
100
114
|
name: "my_tool",
|
|
@@ -106,9 +120,10 @@ const factory: CustomToolFactory = (pi) => ({
|
|
|
106
120
|
text: Type.Optional(Type.String()),
|
|
107
121
|
}),
|
|
108
122
|
|
|
109
|
-
async execute(toolCallId, params,
|
|
123
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
110
124
|
// signal - AbortSignal for cancellation
|
|
111
125
|
// onUpdate - Callback for streaming partial results
|
|
126
|
+
// ctx - CustomToolContext with sessionManager, modelRegistry, model
|
|
112
127
|
return {
|
|
113
128
|
content: [{ type: "text", text: "Result for LLM" }],
|
|
114
129
|
details: { /* structured data for rendering */ },
|
|
@@ -116,14 +131,17 @@ const factory: CustomToolFactory = (pi) => ({
|
|
|
116
131
|
},
|
|
117
132
|
|
|
118
133
|
// Optional: Session lifecycle callback
|
|
119
|
-
onSession(event) {
|
|
134
|
+
onSession(event, ctx) {
|
|
135
|
+
if (event.reason === "shutdown") {
|
|
136
|
+
// Cleanup resources (close connections, save state, etc.)
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Reconstruct state from ctx.sessionManager.getBranch()
|
|
140
|
+
},
|
|
120
141
|
|
|
121
142
|
// Optional: Custom rendering
|
|
122
143
|
renderCall(args, theme) { /* return Component */ },
|
|
123
144
|
renderResult(result, options, theme) { /* return Component */ },
|
|
124
|
-
|
|
125
|
-
// Optional: Cleanup on session end
|
|
126
|
-
dispose() { /* save state, close connections */ },
|
|
127
145
|
});
|
|
128
146
|
|
|
129
147
|
export default factory;
|
|
@@ -131,23 +149,26 @@ export default factory;
|
|
|
131
149
|
|
|
132
150
|
**Important:** Use `StringEnum` from `@mariozechner/pi-ai` instead of `Type.Union`/`Type.Literal` for string enums. The latter doesn't work with Google's API.
|
|
133
151
|
|
|
134
|
-
##
|
|
152
|
+
## CustomToolAPI Object
|
|
135
153
|
|
|
136
|
-
The factory receives a `
|
|
154
|
+
The factory receives a `CustomToolAPI` object (named `pi` by convention):
|
|
137
155
|
|
|
138
156
|
```typescript
|
|
139
|
-
interface
|
|
157
|
+
interface CustomToolAPI {
|
|
140
158
|
cwd: string; // Current working directory
|
|
141
159
|
exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
142
|
-
ui:
|
|
143
|
-
select(title: string, options: string[]): Promise<string | null>;
|
|
144
|
-
confirm(title: string, message: string): Promise<boolean>;
|
|
145
|
-
input(title: string, placeholder?: string): Promise<string | null>;
|
|
146
|
-
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
147
|
-
};
|
|
160
|
+
ui: ToolUIContext;
|
|
148
161
|
hasUI: boolean; // false in --print or --mode rpc
|
|
149
162
|
}
|
|
150
163
|
|
|
164
|
+
interface ToolUIContext {
|
|
165
|
+
select(title: string, options: string[]): Promise<string | undefined>;
|
|
166
|
+
confirm(title: string, message: string): Promise<boolean>;
|
|
167
|
+
input(title: string, placeholder?: string): Promise<string | undefined>;
|
|
168
|
+
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
169
|
+
custom(component: Component & { dispose?(): void }): { close: () => void; requestRender: () => void };
|
|
170
|
+
}
|
|
171
|
+
|
|
151
172
|
interface ExecOptions {
|
|
152
173
|
signal?: AbortSignal; // Cancel the process
|
|
153
174
|
timeout?: number; // Timeout in milliseconds
|
|
@@ -168,7 +189,7 @@ Always check `pi.hasUI` before using UI methods.
|
|
|
168
189
|
Pass the `signal` from `execute` to `pi.exec` to support cancellation:
|
|
169
190
|
|
|
170
191
|
```typescript
|
|
171
|
-
async execute(toolCallId, params, signal) {
|
|
192
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
172
193
|
const result = await pi.exec("long-running-command", ["arg"], { signal });
|
|
173
194
|
if (result.killed) {
|
|
174
195
|
return { content: [{ type: "text", text: "Cancelled" }] };
|
|
@@ -177,24 +198,101 @@ async execute(toolCallId, params, signal) {
|
|
|
177
198
|
}
|
|
178
199
|
```
|
|
179
200
|
|
|
201
|
+
### Error Handling
|
|
202
|
+
|
|
203
|
+
**Throw an error** when the tool fails. Do not return an error message as content.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
207
|
+
const { path } = params as { path: string };
|
|
208
|
+
|
|
209
|
+
// Throw on error - pi will catch it and report to the LLM
|
|
210
|
+
if (!fs.existsSync(path)) {
|
|
211
|
+
throw new Error(`File not found: ${path}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Return content only on success
|
|
215
|
+
return { content: [{ type: "text", text: "Success" }] };
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Thrown errors are:
|
|
220
|
+
- Reported to the LLM as tool errors (with `isError: true`)
|
|
221
|
+
- Emitted to hooks via `tool_result` event (hooks can inspect `event.isError`)
|
|
222
|
+
- Displayed in the TUI with error styling
|
|
223
|
+
|
|
224
|
+
## CustomToolContext
|
|
225
|
+
|
|
226
|
+
The `execute` and `onSession` callbacks receive a `CustomToolContext`:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
interface CustomToolContext {
|
|
230
|
+
sessionManager: ReadonlySessionManager; // Read-only access to session
|
|
231
|
+
modelRegistry: ModelRegistry; // For API key resolution
|
|
232
|
+
model: Model | undefined; // Current model (may be undefined)
|
|
233
|
+
isIdle(): boolean; // Whether agent is streaming
|
|
234
|
+
hasQueuedMessages(): boolean; // Whether user has queued messages
|
|
235
|
+
abort(): void; // Abort current operation (fire-and-forget)
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Use `ctx.sessionManager.getBranch()` to get entries on the current branch for state reconstruction.
|
|
240
|
+
|
|
241
|
+
### Checking Queue State
|
|
242
|
+
|
|
243
|
+
Interactive tools can skip prompts when the user has already queued a message:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
247
|
+
// If user already queued a message, skip the interactive prompt
|
|
248
|
+
if (ctx.hasQueuedMessages()) {
|
|
249
|
+
return {
|
|
250
|
+
content: [{ type: "text", text: "Skipped - user has queued input" }],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Otherwise, prompt for input
|
|
255
|
+
const answer = await pi.ui.input("What would you like to do?");
|
|
256
|
+
// ...
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Multi-line Editor
|
|
261
|
+
|
|
262
|
+
For longer text editing, use `pi.ui.editor()` which supports Ctrl+G for external editor:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
266
|
+
const text = await pi.ui.editor("Edit your response:", "prefilled text");
|
|
267
|
+
// Returns edited text or undefined if cancelled (Escape)
|
|
268
|
+
// Ctrl+Enter to submit, Ctrl+G to open $VISUAL or $EDITOR
|
|
269
|
+
|
|
270
|
+
if (!text) {
|
|
271
|
+
return { content: [{ type: "text", text: "Cancelled" }] };
|
|
272
|
+
}
|
|
273
|
+
// ...
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
180
277
|
## Session Lifecycle
|
|
181
278
|
|
|
182
279
|
Tools can implement `onSession` to react to session changes:
|
|
183
280
|
|
|
184
281
|
```typescript
|
|
185
|
-
interface
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
previousSessionFile: string | null; // Previous session file
|
|
189
|
-
reason: "start" | "switch" | "branch" | "new";
|
|
282
|
+
interface CustomToolSessionEvent {
|
|
283
|
+
reason: "start" | "switch" | "branch" | "tree" | "shutdown";
|
|
284
|
+
previousSessionFile: string | undefined;
|
|
190
285
|
}
|
|
191
286
|
```
|
|
192
287
|
|
|
193
288
|
**Reasons:**
|
|
194
289
|
- `start`: Initial session load on startup
|
|
195
|
-
- `switch`: User switched to a different session (`/resume`)
|
|
290
|
+
- `switch`: User started a new session (`/new`) or switched to a different session (`/resume`)
|
|
196
291
|
- `branch`: User branched from a previous message (`/branch`)
|
|
197
|
-
- `
|
|
292
|
+
- `tree`: User navigated to a different point in the session tree (`/tree`)
|
|
293
|
+
- `shutdown`: Process is exiting (Ctrl+C, Ctrl+D, or SIGTERM) - use to cleanup resources
|
|
294
|
+
|
|
295
|
+
To check if a session is fresh (no messages), use `ctx.sessionManager.getEntries().length === 0`.
|
|
198
296
|
|
|
199
297
|
### State Management Pattern
|
|
200
298
|
|
|
@@ -210,9 +308,11 @@ const factory: CustomToolFactory = (pi) => {
|
|
|
210
308
|
let items: string[] = [];
|
|
211
309
|
|
|
212
310
|
// Reconstruct state from session entries
|
|
213
|
-
const reconstructState = (event:
|
|
311
|
+
const reconstructState = (event: CustomToolSessionEvent, ctx: CustomToolContext) => {
|
|
312
|
+
if (event.reason === "shutdown") return;
|
|
313
|
+
|
|
214
314
|
items = [];
|
|
215
|
-
for (const entry of
|
|
315
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
216
316
|
if (entry.type !== "message") continue;
|
|
217
317
|
const msg = entry.message;
|
|
218
318
|
if (msg.role !== "toolResult") continue;
|
|
@@ -233,7 +333,7 @@ const factory: CustomToolFactory = (pi) => {
|
|
|
233
333
|
|
|
234
334
|
onSession: reconstructState,
|
|
235
335
|
|
|
236
|
-
async execute(toolCallId, params) {
|
|
336
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
237
337
|
// Modify items...
|
|
238
338
|
items.push("new item");
|
|
239
339
|
|
|
@@ -254,7 +354,7 @@ This pattern ensures:
|
|
|
254
354
|
|
|
255
355
|
## Custom Rendering
|
|
256
356
|
|
|
257
|
-
Custom tools can provide `renderCall` and `renderResult` methods to control how they appear in the TUI. Both are optional.
|
|
357
|
+
Custom tools can provide `renderCall` and `renderResult` methods to control how they appear in the TUI. Both are optional. See [tui.md](tui.md) for the full component API.
|
|
258
358
|
|
|
259
359
|
### How It Works
|
|
260
360
|
|
|
@@ -355,7 +455,7 @@ If `renderCall` or `renderResult` is not defined or throws an error:
|
|
|
355
455
|
## Execute Function
|
|
356
456
|
|
|
357
457
|
```typescript
|
|
358
|
-
async execute(toolCallId, args,
|
|
458
|
+
async execute(toolCallId, args, onUpdate, ctx, signal) {
|
|
359
459
|
// Type assertion for params (TypeBox schema doesn't flow through)
|
|
360
460
|
const params = args as { action: "list" | "add"; text?: string };
|
|
361
461
|
|
|
@@ -387,13 +487,16 @@ const factory: CustomToolFactory = (pi) => {
|
|
|
387
487
|
// Shared state
|
|
388
488
|
let connection = null;
|
|
389
489
|
|
|
490
|
+
const handleSession = (event: CustomToolSessionEvent, ctx: CustomToolContext) => {
|
|
491
|
+
if (event.reason === "shutdown") {
|
|
492
|
+
connection?.close();
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
390
496
|
return [
|
|
391
|
-
{ name: "db_connect", ... },
|
|
392
|
-
{ name: "db_query", ... },
|
|
393
|
-
{
|
|
394
|
-
name: "db_close",
|
|
395
|
-
dispose() { connection?.close(); }
|
|
396
|
-
},
|
|
497
|
+
{ name: "db_connect", onSession: handleSession, ... },
|
|
498
|
+
{ name: "db_query", onSession: handleSession, ... },
|
|
499
|
+
{ name: "db_close", onSession: handleSession, ... },
|
|
397
500
|
];
|
|
398
501
|
};
|
|
399
502
|
```
|