@mariozechner/pi-coding-agent 0.34.2 → 0.36.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 +210 -0
- package/README.md +246 -107
- package/dist/cli/args.d.ts +3 -4
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -18
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +39 -50
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +166 -197
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +6 -5
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/index.d.ts +10 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/index.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +21 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +400 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +88 -0
- package/dist/core/extensions/runner.d.ts.map +1 -0
- package/dist/core/{hooks → extensions}/runner.js +52 -141
- package/dist/core/extensions/runner.js.map +1 -0
- package/dist/core/extensions/types.d.ts +461 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/{hooks → extensions}/types.js +7 -4
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts +25 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -0
- package/dist/core/{hooks/tool-wrapper.js → extensions/wrapper.js} +39 -24
- package/dist/core/extensions/wrapper.js.map +1 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +7 -7
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +4 -4
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +40 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/{slash-commands.js → prompt-templates.js} +31 -31
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/sdk.d.ts +29 -52
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +111 -211
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -17
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +25 -10
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -6
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -11
- 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 +4 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +36 -33
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +7 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +93 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +4 -4
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +18 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-message.js → custom-message.js} +3 -3
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +2 -2
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +2 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/{hook-editor.d.ts → extension-editor.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-editor.js → extension-editor.js} +4 -4
- package/dist/modes/interactive/components/extension-editor.js.map +1 -0
- package/dist/modes/interactive/components/{hook-input.d.ts → extension-input.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-input.js → extension-input.js} +3 -3
- package/dist/modes/interactive/components/extension-input.js.map +1 -0
- package/dist/modes/interactive/components/{hook-selector.d.ts → extension-selector.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-selector.js → extension-selector.js} +3 -3
- package/dist/modes/interactive/components/extension-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +3 -3
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -3
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +9 -9
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +37 -44
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +143 -189
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +10 -33
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +3 -3
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +3 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +33 -57
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +16 -16
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/extensions.md +1053 -0
- package/docs/rpc.md +4 -4
- package/docs/sdk.md +62 -93
- package/docs/session.md +22 -19
- package/docs/skills.md +1 -1
- package/docs/tui.md +1 -1
- package/examples/README.md +9 -15
- package/examples/extensions/README.md +141 -0
- package/examples/{hooks → extensions}/auto-commit-on-exit.ts +3 -3
- package/examples/extensions/chalk-logger.ts +26 -0
- package/examples/{hooks → extensions}/confirm-destructive.ts +3 -3
- package/examples/{hooks → extensions}/custom-compaction.ts +6 -6
- package/examples/{hooks → extensions}/dirty-repo-guard.ts +8 -4
- package/examples/{hooks → extensions}/file-trigger.ts +3 -3
- package/examples/{hooks → extensions}/git-checkpoint.ts +3 -3
- package/examples/{hooks → extensions}/handoff.ts +3 -3
- package/examples/extensions/hello.ts +25 -0
- package/examples/{hooks → extensions}/permission-gate.ts +3 -3
- package/examples/{hooks → extensions}/pirate.ts +5 -5
- package/examples/{hooks → extensions}/plan-mode.ts +6 -6
- package/examples/{hooks → extensions}/protected-paths.ts +3 -3
- package/examples/{hooks → extensions}/qna.ts +3 -3
- package/examples/{custom-tools/question/index.ts → extensions/question.ts} +13 -17
- package/examples/{hooks → extensions}/snake.ts +3 -3
- package/examples/{hooks → extensions}/status-line.ts +3 -3
- package/examples/{custom-tools → extensions}/subagent/README.md +15 -15
- package/examples/{custom-tools → extensions}/subagent/index.ts +22 -43
- package/examples/{custom-tools/todo/index.ts → extensions/todo.ts} +122 -39
- package/examples/{hooks → extensions}/tools.ts +5 -5
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +16 -0
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/05-tools.ts +7 -41
- package/examples/sdk/06-extensions.ts +81 -0
- package/examples/sdk/08-prompt-templates.ts +42 -0
- package/examples/sdk/12-full-control.ts +10 -29
- package/examples/sdk/README.md +5 -5
- package/package.json +4 -4
- package/dist/core/custom-tools/index.d.ts +0 -7
- package/dist/core/custom-tools/index.d.ts.map +0 -1
- package/dist/core/custom-tools/index.js +0 -6
- package/dist/core/custom-tools/index.js.map +0 -1
- package/dist/core/custom-tools/loader.d.ts +0 -30
- package/dist/core/custom-tools/loader.d.ts.map +0 -1
- package/dist/core/custom-tools/loader.js +0 -276
- package/dist/core/custom-tools/loader.js.map +0 -1
- package/dist/core/custom-tools/types.d.ts +0 -144
- package/dist/core/custom-tools/types.d.ts.map +0 -1
- package/dist/core/custom-tools/types.js +0 -8
- package/dist/core/custom-tools/types.js.map +0 -1
- package/dist/core/custom-tools/wrapper.d.ts +0 -15
- package/dist/core/custom-tools/wrapper.d.ts.map +0 -1
- package/dist/core/custom-tools/wrapper.js +0 -23
- package/dist/core/custom-tools/wrapper.js.map +0 -1
- package/dist/core/hooks/index.d.ts +0 -6
- package/dist/core/hooks/index.d.ts.map +0 -1
- package/dist/core/hooks/index.js +0 -6
- package/dist/core/hooks/index.js.map +0 -1
- package/dist/core/hooks/loader.d.ts +0 -146
- package/dist/core/hooks/loader.d.ts.map +0 -1
- package/dist/core/hooks/loader.js +0 -275
- package/dist/core/hooks/loader.js.map +0 -1
- package/dist/core/hooks/runner.d.ts +0 -173
- package/dist/core/hooks/runner.d.ts.map +0 -1
- package/dist/core/hooks/runner.js.map +0 -1
- package/dist/core/hooks/tool-wrapper.d.ts +0 -17
- package/dist/core/hooks/tool-wrapper.d.ts.map +0 -1
- package/dist/core/hooks/tool-wrapper.js.map +0 -1
- package/dist/core/hooks/types.d.ts +0 -767
- package/dist/core/hooks/types.d.ts.map +0 -1
- package/dist/core/hooks/types.js.map +0 -1
- package/dist/core/slash-commands.d.ts +0 -40
- package/dist/core/slash-commands.d.ts.map +0 -1
- package/dist/core/slash-commands.js.map +0 -1
- package/dist/modes/interactive/components/hook-editor.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-editor.js.map +0 -1
- package/dist/modes/interactive/components/hook-input.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-input.js.map +0 -1
- package/dist/modes/interactive/components/hook-message.d.ts +0 -18
- package/dist/modes/interactive/components/hook-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-message.js.map +0 -1
- package/dist/modes/interactive/components/hook-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-selector.js.map +0 -1
- package/docs/custom-tools.md +0 -514
- package/docs/extension-loading.md +0 -1004
- package/docs/hooks.md +0 -979
- package/docs/session-tree-plan.md +0 -441
- package/examples/custom-tools/README.md +0 -114
- package/examples/custom-tools/hello/index.ts +0 -21
- package/examples/hooks/README.md +0 -60
- package/examples/hooks/todo/index.ts +0 -134
- package/examples/sdk/06-hooks.ts +0 -61
- package/examples/sdk/08-slash-commands.ts +0 -42
- /package/examples/{custom-tools → extensions}/subagent/agents/planner.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/reviewer.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/scout.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/worker.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents.ts +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement-and-review.md +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement.md +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/scout-and-plan.md +0 -0
package/docs/rpc.md
CHANGED
|
@@ -52,9 +52,9 @@ With images:
|
|
|
52
52
|
|
|
53
53
|
If the agent is streaming and no `streamingBehavior` is specified, the command returns an error.
|
|
54
54
|
|
|
55
|
-
**
|
|
55
|
+
**Extension commands**: If the message is a hook command (e.g., `/mycommand`), it executes immediately even during streaming. Extension commands manage their own LLM interaction via `pi.sendMessage()`.
|
|
56
56
|
|
|
57
|
-
**
|
|
57
|
+
**Prompt templates**: File-based prompt templates (from `.md` files) are expanded before sending/queueing.
|
|
58
58
|
|
|
59
59
|
Response:
|
|
60
60
|
```json
|
|
@@ -65,7 +65,7 @@ The `images` field is optional. Each image uses `ImageContent` format with base6
|
|
|
65
65
|
|
|
66
66
|
#### steer
|
|
67
67
|
|
|
68
|
-
Queue a steering message to interrupt the agent mid-run. Delivered after current tool execution, remaining tools are skipped. File-based
|
|
68
|
+
Queue a steering message to interrupt the agent mid-run. Delivered after current tool execution, remaining tools are skipped. File-based prompt templates are expanded. Extension commands are not allowed (use `prompt` instead).
|
|
69
69
|
|
|
70
70
|
```json
|
|
71
71
|
{"type": "steer", "message": "Stop and do this instead"}
|
|
@@ -80,7 +80,7 @@ See [set_steering_mode](#set_steering_mode) for controlling how steering message
|
|
|
80
80
|
|
|
81
81
|
#### follow_up
|
|
82
82
|
|
|
83
|
-
Queue a follow-up message to be processed after the agent finishes. Delivered only when agent has no more tool calls or steering messages. File-based
|
|
83
|
+
Queue a follow-up message to be processed after the agent finishes. Delivered only when agent has no more tool calls or steering messages. File-based prompt templates are expanded. Extension commands are not allowed (use `prompt` instead).
|
|
84
84
|
|
|
85
85
|
```json
|
|
86
86
|
{"type": "follow_up", "message": "After you're done, also do this"}
|
package/docs/sdk.md
CHANGED
|
@@ -129,7 +129,7 @@ interface AgentSession {
|
|
|
129
129
|
|
|
130
130
|
### Prompting and Message Queueing
|
|
131
131
|
|
|
132
|
-
The `prompt()` method handles
|
|
132
|
+
The `prompt()` method handles prompt templates, extension commands, and message sending:
|
|
133
133
|
|
|
134
134
|
```typescript
|
|
135
135
|
// Basic prompt (when not streaming)
|
|
@@ -146,8 +146,8 @@ await session.prompt("After you're done, also check X", { streamingBehavior: "fo
|
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
**Behavior:**
|
|
149
|
-
- **
|
|
150
|
-
- **File-based
|
|
149
|
+
- **Extension commands** (e.g., `/mycommand`): Execute immediately, even during streaming. They manage their own LLM interaction via `pi.sendMessage()`.
|
|
150
|
+
- **File-based prompt templates** (from `.md` files): Expanded to their content before sending/queueing.
|
|
151
151
|
- **During streaming without `streamingBehavior`**: Throws an error. Use `steer()` or `followUp()` directly, or specify the option.
|
|
152
152
|
|
|
153
153
|
For explicit queueing during streaming:
|
|
@@ -160,7 +160,7 @@ await session.steer("New instruction");
|
|
|
160
160
|
await session.followUp("After you're done, also do this");
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
Both `steer()` and `followUp()` expand file-based
|
|
163
|
+
Both `steer()` and `followUp()` expand file-based prompt templates but error on extension commands (extension commands cannot be queued).
|
|
164
164
|
|
|
165
165
|
### Agent and AgentState
|
|
166
166
|
|
|
@@ -260,18 +260,16 @@ const { session } = await createAgentSession({
|
|
|
260
260
|
```
|
|
261
261
|
|
|
262
262
|
`cwd` is used for:
|
|
263
|
-
- Project
|
|
264
|
-
- Project tools (`.pi/tools/`)
|
|
263
|
+
- Project extensions (`.pi/extensions/`)
|
|
265
264
|
- Project skills (`.pi/skills/`)
|
|
266
|
-
- Project
|
|
265
|
+
- Project prompts (`.pi/prompts/`)
|
|
267
266
|
- Context files (`AGENTS.md` walking up from cwd)
|
|
268
267
|
- Session directory naming
|
|
269
268
|
|
|
270
269
|
`agentDir` is used for:
|
|
271
|
-
- Global
|
|
272
|
-
- Global tools (`tools/`)
|
|
270
|
+
- Global extensions (`extensions/`)
|
|
273
271
|
- Global skills (`skills/`)
|
|
274
|
-
- Global
|
|
272
|
+
- Global prompts (`prompts/`)
|
|
275
273
|
- Global context file (`AGENTS.md`)
|
|
276
274
|
- Settings (`settings.json`)
|
|
277
275
|
- Custom models (`models.json`)
|
|
@@ -442,113 +440,79 @@ const { session } = await createAgentSession({
|
|
|
442
440
|
|
|
443
441
|
```typescript
|
|
444
442
|
import { Type } from "@sinclair/typebox";
|
|
445
|
-
import { createAgentSession,
|
|
443
|
+
import { createAgentSession, type ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
446
444
|
|
|
447
445
|
// Inline custom tool
|
|
448
|
-
const myTool:
|
|
446
|
+
const myTool: ToolDefinition = {
|
|
449
447
|
name: "my_tool",
|
|
450
448
|
label: "My Tool",
|
|
451
449
|
description: "Does something useful",
|
|
452
450
|
parameters: Type.Object({
|
|
453
451
|
input: Type.String({ description: "Input value" }),
|
|
454
452
|
}),
|
|
455
|
-
execute: async (toolCallId, params) => ({
|
|
453
|
+
execute: async (toolCallId, params, onUpdate, ctx, signal) => ({
|
|
456
454
|
content: [{ type: "text", text: `Result: ${params.input}` }],
|
|
457
455
|
details: {},
|
|
458
456
|
}),
|
|
459
457
|
};
|
|
460
458
|
|
|
461
|
-
//
|
|
459
|
+
// Pass custom tools directly
|
|
462
460
|
const { session } = await createAgentSession({
|
|
463
|
-
customTools: [
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
// Merge with discovered tools
|
|
467
|
-
const discovered = await discoverCustomTools();
|
|
468
|
-
const { session } = await createAgentSession({
|
|
469
|
-
customTools: [...discovered, { tool: myTool }],
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// Add paths without replacing discovery
|
|
473
|
-
const { session } = await createAgentSession({
|
|
474
|
-
additionalCustomToolPaths: ["/extra/tools"],
|
|
461
|
+
customTools: [myTool],
|
|
475
462
|
});
|
|
476
463
|
```
|
|
477
464
|
|
|
465
|
+
Custom tools passed via `customTools` are combined with extension-registered tools. Extensions discovered from `~/.pi/agent/extensions/` and `.pi/extensions/` can also register tools via `pi.registerTool()`.
|
|
466
|
+
|
|
478
467
|
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
|
479
468
|
|
|
480
|
-
###
|
|
469
|
+
### Extensions
|
|
470
|
+
|
|
471
|
+
Extensions are discovered from `~/.pi/agent/extensions/` and `.pi/extensions/`. You can also pass inline extensions or additional paths:
|
|
481
472
|
|
|
482
473
|
```typescript
|
|
483
|
-
import { createAgentSession,
|
|
474
|
+
import { createAgentSession, type ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
484
475
|
|
|
485
|
-
// Inline
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
api.on("tool_call", async (event) => {
|
|
476
|
+
// Inline extension
|
|
477
|
+
const myExtension: ExtensionFactory = (pi) => {
|
|
478
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
489
479
|
console.log(`Tool: ${event.toolName}`);
|
|
490
|
-
return undefined; // Don't block
|
|
491
480
|
});
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
return undefined;
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
// Register custom slash command
|
|
502
|
-
api.registerCommand("stats", {
|
|
503
|
-
description: "Show session stats",
|
|
504
|
-
handler: async (ctx) => {
|
|
505
|
-
const entries = ctx.sessionManager.getEntries();
|
|
506
|
-
ctx.ui.notify(`${entries.length} entries`, "info");
|
|
481
|
+
|
|
482
|
+
pi.registerCommand("hello", {
|
|
483
|
+
description: "Say hello",
|
|
484
|
+
handler: async (args, ctx) => {
|
|
485
|
+
ctx.ui.notify("Hello!", "info");
|
|
507
486
|
},
|
|
508
487
|
});
|
|
509
|
-
|
|
510
|
-
// Inject messages
|
|
511
|
-
api.sendMessage({
|
|
512
|
-
customType: "my-hook",
|
|
513
|
-
content: "Hook initialized",
|
|
514
|
-
display: false, // Hidden from TUI
|
|
515
|
-
}, false); // Don't trigger agent turn
|
|
516
|
-
|
|
517
|
-
// Persist hook state
|
|
518
|
-
api.appendEntry("my-hook", { initialized: true });
|
|
519
488
|
};
|
|
520
489
|
|
|
521
|
-
//
|
|
490
|
+
// Pass inline extensions (merged with discovery)
|
|
522
491
|
const { session } = await createAgentSession({
|
|
523
|
-
|
|
492
|
+
extensions: [myExtension],
|
|
524
493
|
});
|
|
525
494
|
|
|
526
|
-
//
|
|
495
|
+
// Or add paths to load (merged with discovery)
|
|
527
496
|
const { session } = await createAgentSession({
|
|
528
|
-
|
|
497
|
+
additionalExtensionPaths: ["/path/to/my-extension.ts"],
|
|
529
498
|
});
|
|
499
|
+
```
|
|
530
500
|
|
|
531
|
-
|
|
532
|
-
const discovered = await discoverHooks();
|
|
533
|
-
const { session } = await createAgentSession({
|
|
534
|
-
hooks: [...discovered, { factory: loggingHook }],
|
|
535
|
-
});
|
|
501
|
+
Extensions can register tools, subscribe to events, add commands, and more. See [extensions.md](extensions.md) for the full API.
|
|
536
502
|
|
|
537
|
-
|
|
538
|
-
const { session } = await createAgentSession({
|
|
539
|
-
additionalHookPaths: ["/extra/hooks"],
|
|
540
|
-
});
|
|
541
|
-
```
|
|
503
|
+
**Event Bus:** Extensions can communicate via `pi.events`. Pass a shared `eventBus` to `createAgentSession()` if you need to emit/listen from outside:
|
|
542
504
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
- `api.sendMessage(message, triggerTurn?)` - Inject message (creates `CustomMessageEntry`)
|
|
546
|
-
- `api.appendEntry(customType, data?)` - Persist hook state (not in LLM context)
|
|
547
|
-
- `api.registerCommand(name, options)` - Register custom slash command
|
|
548
|
-
- `api.registerMessageRenderer(customType, renderer)` - Custom TUI rendering
|
|
549
|
-
- `api.exec(command, args, options?)` - Execute shell commands
|
|
505
|
+
```typescript
|
|
506
|
+
import { createAgentSession, createEventBus } from "@mariozechner/pi-coding-agent";
|
|
550
507
|
|
|
551
|
-
|
|
508
|
+
const eventBus = createEventBus();
|
|
509
|
+
const { session } = await createAgentSession({ eventBus });
|
|
510
|
+
|
|
511
|
+
// Listen for events from extensions
|
|
512
|
+
eventBus.on("my-extension:status", (data) => console.log(data));
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
> See [examples/sdk/06-extensions.ts](../examples/sdk/06-extensions.ts) and [docs/extensions.md](extensions.md)
|
|
552
516
|
|
|
553
517
|
### Skills
|
|
554
518
|
|
|
@@ -616,11 +580,11 @@ const { session } = await createAgentSession({
|
|
|
616
580
|
### Slash Commands
|
|
617
581
|
|
|
618
582
|
```typescript
|
|
619
|
-
import { createAgentSession,
|
|
583
|
+
import { createAgentSession, discoverPromptTemplates, type PromptTemplate } from "@mariozechner/pi-coding-agent";
|
|
620
584
|
|
|
621
|
-
const discovered =
|
|
585
|
+
const discovered = discoverPromptTemplates();
|
|
622
586
|
|
|
623
|
-
const customCommand:
|
|
587
|
+
const customCommand: PromptTemplate = {
|
|
624
588
|
name: "deploy",
|
|
625
589
|
description: "Deploy the application",
|
|
626
590
|
source: "(custom)",
|
|
@@ -628,11 +592,11 @@ const customCommand: FileSlashCommand = {
|
|
|
628
592
|
};
|
|
629
593
|
|
|
630
594
|
const { session } = await createAgentSession({
|
|
631
|
-
|
|
595
|
+
promptTemplates: [...discovered, customCommand],
|
|
632
596
|
});
|
|
633
597
|
```
|
|
634
598
|
|
|
635
|
-
> See [examples/sdk/08-
|
|
599
|
+
> See [examples/sdk/08-prompt-templates.ts](../examples/sdk/08-prompt-templates.ts)
|
|
636
600
|
|
|
637
601
|
### Session Management
|
|
638
602
|
|
|
@@ -761,7 +725,7 @@ import {
|
|
|
761
725
|
discoverHooks,
|
|
762
726
|
discoverCustomTools,
|
|
763
727
|
discoverContextFiles,
|
|
764
|
-
|
|
728
|
+
discoverPromptTemplates,
|
|
765
729
|
loadSettings,
|
|
766
730
|
buildSystemPrompt,
|
|
767
731
|
} from "@mariozechner/pi-coding-agent";
|
|
@@ -778,16 +742,18 @@ const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only
|
|
|
778
742
|
const skills = discoverSkills(cwd, agentDir, skillsSettings);
|
|
779
743
|
|
|
780
744
|
// Hooks (async - loads TypeScript)
|
|
781
|
-
|
|
745
|
+
// Pass eventBus to share pi.events across hooks/tools
|
|
746
|
+
const eventBus = createEventBus();
|
|
747
|
+
const hooks = await discoverHooks(eventBus, cwd, agentDir);
|
|
782
748
|
|
|
783
749
|
// Custom tools (async - loads TypeScript)
|
|
784
|
-
const tools = await discoverCustomTools(cwd, agentDir);
|
|
750
|
+
const tools = await discoverCustomTools(eventBus, cwd, agentDir);
|
|
785
751
|
|
|
786
752
|
// Context files
|
|
787
753
|
const contextFiles = discoverContextFiles(cwd, agentDir);
|
|
788
754
|
|
|
789
|
-
//
|
|
790
|
-
const commands =
|
|
755
|
+
// Prompt templates
|
|
756
|
+
const commands = discoverPromptTemplates(cwd, agentDir);
|
|
791
757
|
|
|
792
758
|
// Settings (global + project merged)
|
|
793
759
|
const settings = loadSettings(cwd, agentDir);
|
|
@@ -894,7 +860,7 @@ const { session } = await createAgentSession({
|
|
|
894
860
|
hooks: [{ factory: auditHook }],
|
|
895
861
|
skills: [],
|
|
896
862
|
contextFiles: [],
|
|
897
|
-
|
|
863
|
+
promptTemplates: [],
|
|
898
864
|
|
|
899
865
|
sessionManager: SessionManager.inMemory(),
|
|
900
866
|
settingsManager,
|
|
@@ -949,7 +915,10 @@ discoverSkills
|
|
|
949
915
|
discoverHooks
|
|
950
916
|
discoverCustomTools
|
|
951
917
|
discoverContextFiles
|
|
952
|
-
|
|
918
|
+
discoverPromptTemplates
|
|
919
|
+
|
|
920
|
+
// Event Bus (for shared hook/tool communication)
|
|
921
|
+
createEventBus
|
|
953
922
|
|
|
954
923
|
// Helpers
|
|
955
924
|
loadSettings
|
|
@@ -977,7 +946,7 @@ type CreateAgentSessionResult
|
|
|
977
946
|
type CustomTool
|
|
978
947
|
type HookFactory
|
|
979
948
|
type Skill
|
|
980
|
-
type
|
|
949
|
+
type PromptTemplate
|
|
981
950
|
type Settings
|
|
982
951
|
type SkillsSettings
|
|
983
952
|
type Tool
|
package/docs/session.md
CHANGED
|
@@ -16,14 +16,15 @@ Sessions have a version field in the header:
|
|
|
16
16
|
|
|
17
17
|
- **Version 1**: Linear entry sequence (legacy, auto-migrated on load)
|
|
18
18
|
- **Version 2**: Tree structure with `id`/`parentId` linking
|
|
19
|
+
- **Version 3**: Renamed `hookMessage` role to `custom` (extensions unification)
|
|
19
20
|
|
|
20
|
-
Existing
|
|
21
|
+
Existing sessions are automatically migrated to the current version (v3) when loaded.
|
|
21
22
|
|
|
22
23
|
## Type Definitions
|
|
23
24
|
|
|
24
25
|
- [`src/core/session-manager.ts`](../src/core/session-manager.ts) - Session entry types
|
|
25
|
-
- [`packages/agent/src/types.ts`](../../agent/src/types.ts) - `AgentMessage
|
|
26
|
-
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `UserMessage`, `AssistantMessage`, `ToolResultMessage`, `Usage`, `ToolCall`
|
|
26
|
+
- [`packages/agent-core/src/types.ts`](../../agent-core/src/types.ts) - `AgentMessage`
|
|
27
|
+
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `UserMessage`, `AssistantMessage`, `ToolResultMessage`, `Usage`, `ToolCall`, `ImageContent`, `TextContent`
|
|
27
28
|
|
|
28
29
|
## Entry Base
|
|
29
30
|
|
|
@@ -45,13 +46,13 @@ interface SessionEntryBase {
|
|
|
45
46
|
First line of the file. Metadata only, not part of the tree (no `id`/`parentId`).
|
|
46
47
|
|
|
47
48
|
```json
|
|
48
|
-
{"type":"session","version":
|
|
49
|
+
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}
|
|
49
50
|
```
|
|
50
51
|
|
|
51
52
|
For sessions with a parent (created via `/branch` or `newSession({ parentSession })`):
|
|
52
53
|
|
|
53
54
|
```json
|
|
54
|
-
{"type":"session","version":
|
|
55
|
+
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
### SessionMessageEntry
|
|
@@ -89,8 +90,8 @@ Created when context is compacted. Stores a summary of earlier messages.
|
|
|
89
90
|
```
|
|
90
91
|
|
|
91
92
|
Optional fields:
|
|
92
|
-
- `details`: Compaction-implementation specific data (e.g., file operations for default implementation, or custom data for
|
|
93
|
-
- `fromHook`: `true` if generated by
|
|
93
|
+
- `details`: Compaction-implementation specific data (e.g., file operations for default implementation, or custom data for extension implementations)
|
|
94
|
+
- `fromHook`: `true` if generated by an extension, `false`/`undefined` if pi-generated
|
|
94
95
|
|
|
95
96
|
### BranchSummaryEntry
|
|
96
97
|
|
|
@@ -102,30 +103,30 @@ Created when switching branches via `/tree` with an LLM generated summary of the
|
|
|
102
103
|
|
|
103
104
|
Optional fields:
|
|
104
105
|
- `details`: File tracking data (`{ readFiles: string[], modifiedFiles: string[] }`) for default implementation, arbitrary for custom implementation
|
|
105
|
-
- `fromHook`: `true` if generated by
|
|
106
|
+
- `fromHook`: `true` if generated by an extension
|
|
106
107
|
|
|
107
108
|
### CustomEntry
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
Extension state persistence. Does NOT participate in LLM context.
|
|
110
111
|
|
|
111
112
|
```json
|
|
112
|
-
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-
|
|
113
|
+
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-extension","data":{"count":42}}
|
|
113
114
|
```
|
|
114
115
|
|
|
115
|
-
Use `customType` to identify your
|
|
116
|
+
Use `customType` to identify your extension's entries on reload.
|
|
116
117
|
|
|
117
118
|
### CustomMessageEntry
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
Extension-injected messages that DO participate in LLM context.
|
|
120
121
|
|
|
121
122
|
```json
|
|
122
|
-
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-
|
|
123
|
+
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-extension","content":"Injected context...","display":true}
|
|
123
124
|
```
|
|
124
125
|
|
|
125
126
|
Fields:
|
|
126
127
|
- `content`: String or `(TextContent | ImageContent)[]` (same as UserMessage)
|
|
127
|
-
- `display`: `true` = show in TUI with
|
|
128
|
-
- `details`: Optional
|
|
128
|
+
- `display`: `true` = show in TUI with distinct styling, `false` = hidden
|
|
129
|
+
- `details`: Optional extension-specific metadata (not sent to LLM)
|
|
129
130
|
|
|
130
131
|
### LabelEntry
|
|
131
132
|
|
|
@@ -190,7 +191,7 @@ for (const line of lines) {
|
|
|
190
191
|
console.log(`[${entry.id}] Custom (${entry.customType}): ${JSON.stringify(entry.data)}`);
|
|
191
192
|
break;
|
|
192
193
|
case "custom_message":
|
|
193
|
-
console.log(`[${entry.id}]
|
|
194
|
+
console.log(`[${entry.id}] Extension message (${entry.customType}): ${entry.content}`);
|
|
194
195
|
break;
|
|
195
196
|
case "label":
|
|
196
197
|
console.log(`[${entry.id}] Label "${entry.label}" on ${entry.targetId}`);
|
|
@@ -220,18 +221,20 @@ Key methods for working with sessions programmatically:
|
|
|
220
221
|
- `appendThinkingLevelChange(level)` - Record thinking change
|
|
221
222
|
- `appendModelChange(provider, modelId)` - Record model change
|
|
222
223
|
- `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)` - Add compaction
|
|
223
|
-
- `appendCustomEntry(customType, data?)` -
|
|
224
|
-
- `appendCustomMessageEntry(customType, content, display, details?)` -
|
|
224
|
+
- `appendCustomEntry(customType, data?)` - Extension state (not in context)
|
|
225
|
+
- `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
|
|
225
226
|
- `appendLabelChange(targetId, label)` - Set/clear label
|
|
226
227
|
|
|
227
228
|
### Tree Navigation
|
|
228
229
|
- `getLeafId()` - Current position
|
|
230
|
+
- `getLeafEntry()` - Get current leaf entry
|
|
229
231
|
- `getEntry(id)` - Get entry by ID
|
|
230
|
-
- `
|
|
232
|
+
- `getBranch(fromId?)` - Walk from entry to root
|
|
231
233
|
- `getTree()` - Get full tree structure
|
|
232
234
|
- `getChildren(parentId)` - Get direct children
|
|
233
235
|
- `getLabel(id)` - Get label for entry
|
|
234
236
|
- `branch(entryId)` - Move leaf to earlier entry
|
|
237
|
+
- `resetLeaf()` - Reset leaf to null (before any entries)
|
|
235
238
|
- `branchWithSummary(entryId, summary, details?, fromHook?)` - Branch with context summary
|
|
236
239
|
|
|
237
240
|
### Context
|
package/docs/skills.md
CHANGED
|
@@ -37,7 +37,7 @@ Skills are loaded when:
|
|
|
37
37
|
|
|
38
38
|
**Not a good fit for skills:**
|
|
39
39
|
- "Always use TypeScript strict mode" → put in AGENTS.md
|
|
40
|
-
- "Review my code" → make a
|
|
40
|
+
- "Review my code" → make a prompt template
|
|
41
41
|
- Need user confirmation dialogs or custom TUI rendering → make a custom tool
|
|
42
42
|
|
|
43
43
|
## Skill Structure
|
package/docs/tui.md
CHANGED
|
@@ -343,4 +343,4 @@ Call `invalidate()` when state changes, then `handle.requestRender()` to trigger
|
|
|
343
343
|
## Examples
|
|
344
344
|
|
|
345
345
|
- **Snake game**: [examples/hooks/snake.ts](../examples/hooks/snake.ts) - Full game with keyboard input, game loop, state persistence
|
|
346
|
-
- **Custom tool rendering**: [examples/
|
|
346
|
+
- **Custom tool rendering**: [examples/extensions/todo.ts](../examples/extensions/todo.ts) - Custom `renderCall` and `renderResult`
|
package/examples/README.md
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
# Examples
|
|
2
2
|
|
|
3
|
-
Example code for pi-coding-agent SDK
|
|
3
|
+
Example code for pi-coding-agent SDK and extensions.
|
|
4
4
|
|
|
5
5
|
## Directories
|
|
6
6
|
|
|
7
7
|
### [sdk/](sdk/)
|
|
8
|
-
Programmatic usage via `createAgentSession()`. Shows how to customize models, prompts, tools,
|
|
8
|
+
Programmatic usage via `createAgentSession()`. Shows how to customize models, prompts, tools, extensions, and session management.
|
|
9
9
|
|
|
10
|
-
### [
|
|
11
|
-
Example
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## Tool + Hook Combinations
|
|
17
|
-
|
|
18
|
-
Some examples are designed to work together:
|
|
19
|
-
|
|
20
|
-
- **todo/** - The [custom tool](custom-tools/todo/) lets the LLM manage a todo list, while the [hook](hooks/todo/) adds a `/todos` command for users to view todos at any time.
|
|
10
|
+
### [extensions/](extensions/)
|
|
11
|
+
Example extensions demonstrating:
|
|
12
|
+
- Lifecycle event handlers (tool interception, safety gates, context modifications)
|
|
13
|
+
- Custom tools (todo lists, subagents)
|
|
14
|
+
- Commands and keyboard shortcuts
|
|
15
|
+
- External integrations (git, file watchers)
|
|
21
16
|
|
|
22
17
|
## Documentation
|
|
23
18
|
|
|
24
19
|
- [SDK Reference](sdk/README.md)
|
|
25
|
-
- [
|
|
26
|
-
- [Custom Tools Documentation](../docs/custom-tools.md)
|
|
20
|
+
- [Extensions Documentation](../docs/extensions.md)
|
|
27
21
|
- [Skills Documentation](../docs/skills.md)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Extension Examples
|
|
2
|
+
|
|
3
|
+
Example extensions for pi-coding-agent.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Load an extension with --extension flag
|
|
9
|
+
pi --extension examples/extensions/permission-gate.ts
|
|
10
|
+
|
|
11
|
+
# Or copy to extensions directory for auto-discovery
|
|
12
|
+
cp permission-gate.ts ~/.pi/agent/extensions/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
### Lifecycle & Safety
|
|
18
|
+
|
|
19
|
+
| Extension | Description |
|
|
20
|
+
|-----------|-------------|
|
|
21
|
+
| `permission-gate.ts` | Prompts for confirmation before dangerous bash commands (rm -rf, sudo, etc.) |
|
|
22
|
+
| `protected-paths.ts` | Blocks writes to protected paths (.env, .git/, node_modules/) |
|
|
23
|
+
| `confirm-destructive.ts` | Confirms before destructive session actions (clear, switch, branch) |
|
|
24
|
+
| `dirty-repo-guard.ts` | Prevents session changes with uncommitted git changes |
|
|
25
|
+
|
|
26
|
+
### Custom Tools
|
|
27
|
+
|
|
28
|
+
| Extension | Description |
|
|
29
|
+
|-----------|-------------|
|
|
30
|
+
| `todo.ts` | Todo list tool + `/todos` command with custom rendering and state persistence |
|
|
31
|
+
| `hello.ts` | Minimal custom tool example |
|
|
32
|
+
| `question.ts` | Demonstrates `ctx.ui.select()` for asking the user questions |
|
|
33
|
+
| `subagent/` | Delegate tasks to specialized subagents with isolated context windows |
|
|
34
|
+
|
|
35
|
+
### Commands & UI
|
|
36
|
+
|
|
37
|
+
| Extension | Description |
|
|
38
|
+
|-----------|-------------|
|
|
39
|
+
| `plan-mode.ts` | Claude Code-style plan mode for read-only exploration with `/plan` command |
|
|
40
|
+
| `tools.ts` | Interactive `/tools` command to enable/disable tools with session persistence |
|
|
41
|
+
| `handoff.ts` | Transfer context to a new focused session via `/handoff <goal>` |
|
|
42
|
+
| `qna.ts` | Extracts questions from last response into editor via `ctx.ui.setEditorText()` |
|
|
43
|
+
| `status-line.ts` | Shows turn progress in footer via `ctx.ui.setStatus()` with themed colors |
|
|
44
|
+
| `snake.ts` | Snake game with custom UI, keyboard handling, and session persistence |
|
|
45
|
+
|
|
46
|
+
### Git Integration
|
|
47
|
+
|
|
48
|
+
| Extension | Description |
|
|
49
|
+
|-----------|-------------|
|
|
50
|
+
| `git-checkpoint.ts` | Creates git stash checkpoints at each turn for code restoration on branch |
|
|
51
|
+
| `auto-commit-on-exit.ts` | Auto-commits on exit using last assistant message for commit message |
|
|
52
|
+
|
|
53
|
+
### System Prompt & Compaction
|
|
54
|
+
|
|
55
|
+
| Extension | Description |
|
|
56
|
+
|-----------|-------------|
|
|
57
|
+
| `pirate.ts` | Demonstrates `systemPromptAppend` to dynamically modify system prompt |
|
|
58
|
+
| `custom-compaction.ts` | Custom compaction that summarizes entire conversation |
|
|
59
|
+
|
|
60
|
+
### External Dependencies
|
|
61
|
+
|
|
62
|
+
| Extension | Description |
|
|
63
|
+
|-----------|-------------|
|
|
64
|
+
| `chalk-logger.ts` | Uses chalk from parent node_modules (demonstrates jiti module resolution) |
|
|
65
|
+
| `with-deps/` | Extension with its own package.json and dependencies |
|
|
66
|
+
| `file-trigger.ts` | Watches a trigger file and injects contents into conversation |
|
|
67
|
+
|
|
68
|
+
## Writing Extensions
|
|
69
|
+
|
|
70
|
+
See [docs/extensions.md](../../docs/extensions.md) for full documentation.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
74
|
+
import { Type } from "@sinclair/typebox";
|
|
75
|
+
|
|
76
|
+
export default function (pi: ExtensionAPI) {
|
|
77
|
+
// Subscribe to lifecycle events
|
|
78
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
79
|
+
if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
|
|
80
|
+
const ok = await ctx.ui.confirm("Dangerous!", "Allow rm -rf?");
|
|
81
|
+
if (!ok) return { block: true, reason: "Blocked by user" };
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Register custom tools
|
|
86
|
+
pi.registerTool({
|
|
87
|
+
name: "greet",
|
|
88
|
+
label: "Greeting",
|
|
89
|
+
description: "Generate a greeting",
|
|
90
|
+
parameters: Type.Object({
|
|
91
|
+
name: Type.String({ description: "Name to greet" }),
|
|
92
|
+
}),
|
|
93
|
+
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: "text", text: `Hello, ${params.name}!` }],
|
|
96
|
+
details: {},
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Register commands
|
|
102
|
+
pi.registerCommand("hello", {
|
|
103
|
+
description: "Say hello",
|
|
104
|
+
handler: async (args, ctx) => {
|
|
105
|
+
ctx.ui.notify("Hello!", "info");
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Key Patterns
|
|
112
|
+
|
|
113
|
+
**Use StringEnum for string parameters** (required for Google API compatibility):
|
|
114
|
+
```typescript
|
|
115
|
+
import { StringEnum } from "@mariozechner/pi-ai";
|
|
116
|
+
|
|
117
|
+
// Good
|
|
118
|
+
action: StringEnum(["list", "add"] as const)
|
|
119
|
+
|
|
120
|
+
// Bad - doesn't work with Google
|
|
121
|
+
action: Type.Union([Type.Literal("list"), Type.Literal("add")])
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**State persistence via details:**
|
|
125
|
+
```typescript
|
|
126
|
+
// Store state in tool result details for proper branching support
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: "text", text: "Done" }],
|
|
129
|
+
details: { todos: [...todos], nextId }, // Persisted in session
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Reconstruct on session events
|
|
133
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
134
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
135
|
+
if (entry.type === "message" && entry.message.toolName === "my_tool") {
|
|
136
|
+
const details = entry.message.details;
|
|
137
|
+
// Reconstruct state from details
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auto-Commit on Exit
|
|
2
|
+
* Auto-Commit on Exit Extension
|
|
3
3
|
*
|
|
4
4
|
* Automatically commits changes when the agent exits.
|
|
5
5
|
* Uses the last assistant message to generate a commit message.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
9
|
|
|
10
|
-
export default function (pi:
|
|
10
|
+
export default function (pi: ExtensionAPI) {
|
|
11
11
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
12
12
|
// Check for uncommitted changes
|
|
13
13
|
const { stdout: status, code } = await pi.exec("git", ["status", "--porcelain"]);
|