@oh-my-pi/pi-coding-agent 3.15.0 → 3.20.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 +61 -1
- package/docs/extensions.md +1055 -0
- package/docs/rpc.md +69 -13
- package/docs/session-tree-plan.md +1 -1
- package/examples/extensions/README.md +141 -0
- package/examples/extensions/api-demo.ts +87 -0
- package/examples/extensions/chalk-logger.ts +26 -0
- package/examples/extensions/hello.ts +33 -0
- package/examples/extensions/pirate.ts +44 -0
- package/examples/extensions/plan-mode.ts +551 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/todo.ts +299 -0
- package/examples/extensions/tools.ts +145 -0
- 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/02-custom-model.ts +3 -3
- package/examples/sdk/05-tools.ts +7 -3
- package/examples/sdk/06-extensions.ts +81 -0
- package/examples/sdk/06-hooks.ts +14 -13
- package/examples/sdk/08-prompt-templates.ts +42 -0
- package/examples/sdk/08-slash-commands.ts +17 -12
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/examples/sdk/12-full-control.ts +6 -6
- package/package.json +11 -7
- package/src/capability/extension-module.ts +34 -0
- package/src/cli/args.ts +22 -7
- package/src/cli/file-processor.ts +38 -67
- package/src/cli/list-models.ts +1 -1
- package/src/config.ts +25 -14
- package/src/core/agent-session.ts +505 -242
- package/src/core/auth-storage.ts +33 -21
- package/src/core/compaction/branch-summarization.ts +4 -4
- package/src/core/compaction/compaction.ts +3 -3
- package/src/core/custom-commands/bundled/wt/index.ts +430 -0
- package/src/core/custom-commands/loader.ts +9 -0
- package/src/core/custom-tools/wrapper.ts +5 -0
- package/src/core/event-bus.ts +59 -0
- package/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/src/core/export-html/vendor/marked.min.js +6 -0
- package/src/core/extensions/index.ts +100 -0
- package/src/core/extensions/loader.ts +501 -0
- package/src/core/extensions/runner.ts +477 -0
- package/src/core/extensions/types.ts +712 -0
- package/src/core/extensions/wrapper.ts +147 -0
- package/src/core/hooks/types.ts +2 -2
- package/src/core/index.ts +10 -21
- package/src/core/keybindings.ts +199 -0
- package/src/core/messages.ts +26 -7
- package/src/core/model-registry.ts +123 -46
- package/src/core/model-resolver.ts +7 -5
- package/src/core/prompt-templates.ts +242 -0
- package/src/core/sdk.ts +378 -295
- package/src/core/session-manager.ts +72 -58
- package/src/core/settings-manager.ts +118 -22
- package/src/core/system-prompt.ts +24 -1
- package/src/core/terminal-notify.ts +37 -0
- package/src/core/tools/context.ts +4 -4
- package/src/core/tools/exa/mcp-client.ts +5 -4
- package/src/core/tools/exa/render.ts +176 -131
- package/src/core/tools/gemini-image.ts +361 -0
- package/src/core/tools/git.ts +216 -0
- package/src/core/tools/index.ts +28 -15
- package/src/core/tools/lsp/config.ts +5 -4
- package/src/core/tools/lsp/index.ts +17 -12
- package/src/core/tools/lsp/render.ts +39 -47
- package/src/core/tools/read.ts +66 -29
- package/src/core/tools/render-utils.ts +268 -0
- package/src/core/tools/renderers.ts +243 -225
- package/src/core/tools/task/discovery.ts +2 -2
- package/src/core/tools/task/executor.ts +66 -58
- package/src/core/tools/task/index.ts +29 -10
- package/src/core/tools/task/model-resolver.ts +8 -13
- package/src/core/tools/task/omp-command.ts +24 -0
- package/src/core/tools/task/render.ts +35 -60
- package/src/core/tools/task/types.ts +3 -0
- package/src/core/tools/web-fetch.ts +29 -28
- package/src/core/tools/web-search/index.ts +6 -5
- package/src/core/tools/web-search/providers/exa.ts +6 -5
- package/src/core/tools/web-search/render.ts +66 -111
- package/src/core/voice-controller.ts +135 -0
- package/src/core/voice-supervisor.ts +1003 -0
- package/src/core/voice.ts +308 -0
- package/src/discovery/builtin.ts +75 -1
- package/src/discovery/claude.ts +47 -1
- package/src/discovery/codex.ts +54 -2
- package/src/discovery/gemini.ts +55 -2
- package/src/discovery/helpers.ts +100 -1
- package/src/discovery/index.ts +2 -0
- package/src/index.ts +14 -9
- package/src/lib/worktree/collapse.ts +179 -0
- package/src/lib/worktree/constants.ts +14 -0
- package/src/lib/worktree/errors.ts +23 -0
- package/src/lib/worktree/git.ts +110 -0
- package/src/lib/worktree/index.ts +23 -0
- package/src/lib/worktree/operations.ts +216 -0
- package/src/lib/worktree/session.ts +114 -0
- package/src/lib/worktree/stats.ts +67 -0
- package/src/main.ts +61 -37
- package/src/migrations.ts +37 -7
- package/src/modes/interactive/components/bash-execution.ts +6 -4
- package/src/modes/interactive/components/custom-editor.ts +55 -0
- package/src/modes/interactive/components/custom-message.ts +95 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +5 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +18 -12
- package/src/modes/interactive/components/extensions/state-manager.ts +12 -0
- package/src/modes/interactive/components/extensions/types.ts +1 -0
- package/src/modes/interactive/components/footer.ts +324 -0
- package/src/modes/interactive/components/hook-editor.ts +1 -0
- package/src/modes/interactive/components/hook-selector.ts +3 -3
- package/src/modes/interactive/components/model-selector.ts +7 -6
- package/src/modes/interactive/components/oauth-selector.ts +3 -3
- package/src/modes/interactive/components/settings-defs.ts +55 -6
- package/src/modes/interactive/components/status-line/separators.ts +4 -4
- package/src/modes/interactive/components/status-line.ts +45 -35
- package/src/modes/interactive/components/tool-execution.ts +95 -23
- package/src/modes/interactive/interactive-mode.ts +644 -113
- package/src/modes/interactive/theme/defaults/alabaster.json +99 -0
- package/src/modes/interactive/theme/defaults/amethyst.json +103 -0
- package/src/modes/interactive/theme/defaults/anthracite.json +100 -0
- package/src/modes/interactive/theme/defaults/basalt.json +90 -0
- package/src/modes/interactive/theme/defaults/birch.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-abyss.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-aurora.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-cavern.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-copper.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-cosmos.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-eclipse.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-ember.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-equinox.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-lavender.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-lunar.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-midnight.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-nebula.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-rainforest.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-reef.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-sakura.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-slate.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-solstice.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-starfall.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-swamp.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-taiga.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-terminal.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-tundra.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-twilight.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-volcanic.json +97 -0
- package/src/modes/interactive/theme/defaults/graphite.json +99 -0
- package/src/modes/interactive/theme/defaults/index.ts +128 -0
- package/src/modes/interactive/theme/defaults/light-aurora-day.json +97 -0
- package/src/modes/interactive/theme/defaults/light-canyon.json +97 -0
- package/src/modes/interactive/theme/defaults/light-cirrus.json +96 -0
- package/src/modes/interactive/theme/defaults/light-coral.json +94 -0
- package/src/modes/interactive/theme/defaults/light-dawn.json +96 -0
- package/src/modes/interactive/theme/defaults/light-dunes.json +97 -0
- package/src/modes/interactive/theme/defaults/light-eucalyptus.json +94 -0
- package/src/modes/interactive/theme/defaults/light-frost.json +94 -0
- package/src/modes/interactive/theme/defaults/light-glacier.json +97 -0
- package/src/modes/interactive/theme/defaults/light-haze.json +96 -0
- package/src/modes/interactive/theme/defaults/light-honeycomb.json +94 -0
- package/src/modes/interactive/theme/defaults/light-lagoon.json +97 -0
- package/src/modes/interactive/theme/defaults/light-lavender.json +94 -0
- package/src/modes/interactive/theme/defaults/light-meadow.json +97 -0
- package/src/modes/interactive/theme/defaults/light-mint.json +94 -0
- package/src/modes/interactive/theme/defaults/light-opal.json +97 -0
- package/src/modes/interactive/theme/defaults/light-orchard.json +97 -0
- package/src/modes/interactive/theme/defaults/light-paper.json +94 -0
- package/src/modes/interactive/theme/defaults/light-prism.json +96 -0
- package/src/modes/interactive/theme/defaults/light-sand.json +94 -0
- package/src/modes/interactive/theme/defaults/light-savanna.json +97 -0
- package/src/modes/interactive/theme/defaults/light-soleil.json +96 -0
- package/src/modes/interactive/theme/defaults/light-wetland.json +97 -0
- package/src/modes/interactive/theme/defaults/light-zenith.json +95 -0
- package/src/modes/interactive/theme/defaults/limestone.json +100 -0
- package/src/modes/interactive/theme/defaults/mahogany.json +104 -0
- package/src/modes/interactive/theme/defaults/marble.json +99 -0
- package/src/modes/interactive/theme/defaults/obsidian.json +90 -0
- package/src/modes/interactive/theme/defaults/onyx.json +90 -0
- package/src/modes/interactive/theme/defaults/pearl.json +99 -0
- package/src/modes/interactive/theme/defaults/porcelain.json +90 -0
- package/src/modes/interactive/theme/defaults/quartz.json +102 -0
- package/src/modes/interactive/theme/defaults/sandstone.json +101 -0
- package/src/modes/interactive/theme/defaults/titanium.json +89 -0
- package/src/modes/print-mode.ts +14 -72
- package/src/modes/rpc/rpc-client.ts +23 -9
- package/src/modes/rpc/rpc-mode.ts +137 -125
- package/src/modes/rpc/rpc-types.ts +46 -24
- package/src/prompts/task.md +1 -0
- package/src/prompts/tools/gemini-image.md +4 -0
- package/src/prompts/tools/git.md +9 -0
- package/src/prompts/voice-summary.md +12 -0
- package/src/utils/image-convert.ts +26 -0
- package/src/utils/image-resize.ts +215 -0
- package/src/utils/shell-snapshot.ts +22 -20
package/docs/rpc.md
CHANGED
|
@@ -54,22 +54,38 @@ Response:
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
The `images` field is optional. Each image uses `ImageContent` format with base64 or URL source.
|
|
57
|
+
When prompting during streaming, set `"streamingBehavior": "steer"` or `"followUp"` to queue the message.
|
|
57
58
|
|
|
58
|
-
####
|
|
59
|
+
#### steer
|
|
59
60
|
|
|
60
|
-
Queue a message to
|
|
61
|
+
Queue a steering message to interrupt the agent mid-run. Useful for injecting corrections while streaming.
|
|
61
62
|
|
|
62
63
|
```json
|
|
63
|
-
{ "type": "
|
|
64
|
+
{ "type": "steer", "message": "Additional context" }
|
|
64
65
|
```
|
|
65
66
|
|
|
66
67
|
Response:
|
|
67
68
|
|
|
68
69
|
```json
|
|
69
|
-
{ "type": "response", "command": "
|
|
70
|
+
{ "type": "response", "command": "steer", "success": true }
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
#### follow_up
|
|
74
|
+
|
|
75
|
+
Queue a follow-up message to be processed after the current run completes.
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{ "type": "follow_up", "message": "Additional context" }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Response:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{ "type": "response", "command": "follow_up", "success": true }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
See [set_steering_mode](#set_steering_mode), [set_follow_up_mode](#set_follow_up_mode), and
|
|
88
|
+
[set_interrupt_mode](#set_interrupt_mode) for controlling queued message handling.
|
|
73
89
|
|
|
74
90
|
#### abort
|
|
75
91
|
|
|
@@ -133,7 +149,9 @@ Response:
|
|
|
133
149
|
"thinkingLevel": "medium",
|
|
134
150
|
"isStreaming": false,
|
|
135
151
|
"isCompacting": false,
|
|
136
|
-
"
|
|
152
|
+
"steeringMode": "all",
|
|
153
|
+
"followUpMode": "one-at-a-time",
|
|
154
|
+
"interruptMode": "immediate",
|
|
137
155
|
"sessionFile": "/path/to/session.jsonl",
|
|
138
156
|
"sessionId": "abc123",
|
|
139
157
|
"autoCompactionEnabled": true,
|
|
@@ -272,25 +290,63 @@ Response:
|
|
|
272
290
|
}
|
|
273
291
|
```
|
|
274
292
|
|
|
275
|
-
### Queue
|
|
293
|
+
### Queue Modes
|
|
294
|
+
|
|
295
|
+
#### set_steering_mode
|
|
296
|
+
|
|
297
|
+
Control how steering messages are injected into the conversation.
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{ "type": "set_steering_mode", "mode": "one-at-a-time" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Modes:
|
|
304
|
+
|
|
305
|
+
- `"all"`: Inject all steering messages at the next turn
|
|
306
|
+
- `"one-at-a-time"`: Inject one steering message per turn (default)
|
|
307
|
+
|
|
308
|
+
Response:
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{ "type": "response", "command": "set_steering_mode", "success": true }
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### set_follow_up_mode
|
|
315
|
+
|
|
316
|
+
Control how follow-up messages are injected into the conversation.
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{ "type": "set_follow_up_mode", "mode": "one-at-a-time" }
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Modes:
|
|
323
|
+
|
|
324
|
+
- `"all"`: Inject all follow-up messages at the next turn
|
|
325
|
+
- `"one-at-a-time"`: Inject one follow-up message per turn (default)
|
|
326
|
+
|
|
327
|
+
Response:
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{ "type": "response", "command": "set_follow_up_mode", "success": true }
|
|
331
|
+
```
|
|
276
332
|
|
|
277
|
-
####
|
|
333
|
+
#### set_interrupt_mode
|
|
278
334
|
|
|
279
|
-
Control how
|
|
335
|
+
Control how the agent handles incoming steering messages while streaming.
|
|
280
336
|
|
|
281
337
|
```json
|
|
282
|
-
{ "type": "
|
|
338
|
+
{ "type": "set_interrupt_mode", "mode": "wait" }
|
|
283
339
|
```
|
|
284
340
|
|
|
285
341
|
Modes:
|
|
286
342
|
|
|
287
|
-
- `"
|
|
288
|
-
- `"
|
|
343
|
+
- `"immediate"`: Interrupt immediately when steering arrives
|
|
344
|
+
- `"wait"`: Wait to apply steering until current tool call completes
|
|
289
345
|
|
|
290
346
|
Response:
|
|
291
347
|
|
|
292
348
|
```json
|
|
293
|
-
{ "type": "response", "command": "
|
|
349
|
+
{ "type": "response", "command": "set_interrupt_mode", "success": true }
|
|
294
350
|
```
|
|
295
351
|
|
|
296
352
|
### Compaction
|
|
@@ -150,7 +150,7 @@ Implementation:
|
|
|
150
150
|
- Uses agent's queue mechanism with `_hookData` marker on AppMessage
|
|
151
151
|
- `message_end` handler routes based on marker presence
|
|
152
152
|
- `AgentSession.sendHookMessage()` handles three cases:
|
|
153
|
-
|
|
153
|
+
- Streaming: queues via `agent.steer()` or `agent.followUp()`, loop processes and emits `message_end`
|
|
154
154
|
- Not streaming + triggerTurn: direct append + `agent.continue()`
|
|
155
155
|
- Not streaming + no trigger: direct append only
|
|
156
156
|
- TUI updates via event (streaming) or explicit rebuild (non-streaming)
|
|
@@ -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 ~/.omp/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 "@oh-my-pi/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 "@oh-my-pi/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
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Demo Extension
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates using ExtensionAPI's logger, typebox, and pi module access.
|
|
5
|
+
* These features are now exposed directly on the ExtensionAPI, matching
|
|
6
|
+
* the CustomToolAPI interface.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
10
|
+
|
|
11
|
+
export default function (pi: ExtensionAPI) {
|
|
12
|
+
// 1. Access TypeBox directly from pi.typebox (no separate import needed)
|
|
13
|
+
const { Type } = pi.typebox;
|
|
14
|
+
|
|
15
|
+
// 2. Access the logger for debugging
|
|
16
|
+
pi.logger.debug("API demo extension loaded");
|
|
17
|
+
|
|
18
|
+
// 3. Register a tool that uses all three API features
|
|
19
|
+
pi.registerTool({
|
|
20
|
+
name: "api_demo",
|
|
21
|
+
label: "API Demo",
|
|
22
|
+
description: "Demonstrates ExtensionAPI capabilities: logger, typebox, and pi module access",
|
|
23
|
+
parameters: Type.Object({
|
|
24
|
+
message: Type.String({ description: "Test message" }),
|
|
25
|
+
logLevel: Type.Optional(
|
|
26
|
+
Type.Union([Type.Literal("error"), Type.Literal("warn"), Type.Literal("debug")], {
|
|
27
|
+
description: "Log level to use",
|
|
28
|
+
default: "debug",
|
|
29
|
+
}),
|
|
30
|
+
),
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
async execute(_toolCallId, params, _onUpdate, ctx, _signal) {
|
|
34
|
+
const { message, logLevel = "debug" } = params as { message: string; logLevel?: "error" | "warn" | "debug" };
|
|
35
|
+
|
|
36
|
+
// Use logger at specified level
|
|
37
|
+
pi.logger[logLevel]("API demo tool executed", { message, logLevel });
|
|
38
|
+
|
|
39
|
+
// Access pi module utilities
|
|
40
|
+
const { logger: piLogger } = pi.pi;
|
|
41
|
+
piLogger.debug("Accessed pi module from extension", { sessionFile: ctx.sessionManager.getSessionFile() });
|
|
42
|
+
|
|
43
|
+
// Get session information
|
|
44
|
+
const sessionInfo = `Session: ${ctx.sessionManager.getSessionFile()}`;
|
|
45
|
+
const modelInfo = ctx.model ? `Model: ${ctx.model.id}` : "Model: none";
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: [
|
|
52
|
+
`API Demo Tool executed successfully!`,
|
|
53
|
+
``,
|
|
54
|
+
`Message: ${message}`,
|
|
55
|
+
`Log Level: ${logLevel}`,
|
|
56
|
+
``,
|
|
57
|
+
`Features demonstrated:`,
|
|
58
|
+
`1. ✓ Logger access via pi.logger`,
|
|
59
|
+
`2. ✓ TypeBox access via pi.typebox`,
|
|
60
|
+
`3. ✓ Pi module access via pi.pi`,
|
|
61
|
+
``,
|
|
62
|
+
`Context:`,
|
|
63
|
+
`- ${sessionInfo}`,
|
|
64
|
+
`- ${modelInfo}`,
|
|
65
|
+
`- CWD: ${ctx.cwd}`,
|
|
66
|
+
].join("\n"),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
details: {
|
|
70
|
+
message,
|
|
71
|
+
logLevel,
|
|
72
|
+
sessionFile: ctx.sessionManager.getSessionFile(),
|
|
73
|
+
modelId: ctx.model?.id,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Demonstrate event handling with logger
|
|
80
|
+
pi.on("session_start", async () => {
|
|
81
|
+
pi.logger.debug("Session started", { extension: "api-demo" });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
pi.on("agent_start", async () => {
|
|
85
|
+
pi.logger.debug("Agent started", { extension: "api-demo" });
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example extension that uses a 3rd party dependency (chalk).
|
|
3
|
+
* Tests that jiti can resolve npm modules correctly.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
|
|
9
|
+
export default function (pi: ExtensionAPI) {
|
|
10
|
+
// Log with colors using chalk
|
|
11
|
+
console.log(`${chalk.green("✓")} ${chalk.bold("chalk-logger extension loaded")}`);
|
|
12
|
+
|
|
13
|
+
pi.on("agent_start", async () => {
|
|
14
|
+
console.log(`${chalk.blue("[chalk-logger]")} Agent starting`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
pi.on("tool_call", async (event) => {
|
|
18
|
+
console.log(`${chalk.yellow("[chalk-logger]")} Tool: ${chalk.cyan(event.toolName)}`);
|
|
19
|
+
return undefined;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
pi.on("agent_end", async (event) => {
|
|
23
|
+
const count = event.messages.length;
|
|
24
|
+
console.log(`${chalk.green("[chalk-logger]")} Done with ${chalk.bold(String(count))} messages`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hello Tool - Minimal custom tool example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates using ExtensionAPI's logger, typebox, and pi module access.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
8
|
+
|
|
9
|
+
export default function (pi: ExtensionAPI) {
|
|
10
|
+
// Access TypeBox via pi.typebox (no need to import separately)
|
|
11
|
+
const { Type } = pi.typebox;
|
|
12
|
+
|
|
13
|
+
pi.registerTool({
|
|
14
|
+
name: "hello",
|
|
15
|
+
label: "Hello",
|
|
16
|
+
description: "A simple greeting tool",
|
|
17
|
+
parameters: Type.Object({
|
|
18
|
+
name: Type.String({ description: "Name to greet" }),
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
|
|
22
|
+
const { name } = params as { name: string };
|
|
23
|
+
|
|
24
|
+
// Use logger for debugging
|
|
25
|
+
pi.logger.debug("Hello tool executed", { name });
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
29
|
+
details: { greeted: name },
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pirate Extension
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates using systemPromptAppend in before_agent_start to dynamically
|
|
5
|
+
* modify the system prompt based on extension state.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* 1. Copy this file to ~/.omp/agent/extensions/ (legacy: ~/.pi/agent/extensions/) or your project's .omp/extensions/
|
|
9
|
+
* 2. Use /pirate to toggle pirate mode
|
|
10
|
+
* 3. When enabled, the agent will respond like a pirate
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
14
|
+
|
|
15
|
+
export default function pirateExtension(pi: ExtensionAPI) {
|
|
16
|
+
let pirateMode = false;
|
|
17
|
+
|
|
18
|
+
// Register /pirate command to toggle pirate mode
|
|
19
|
+
pi.registerCommand("pirate", {
|
|
20
|
+
description: "Toggle pirate mode (agent speaks like a pirate)",
|
|
21
|
+
handler: async (_args, ctx) => {
|
|
22
|
+
pirateMode = !pirateMode;
|
|
23
|
+
ctx.ui.notify(pirateMode ? "Arrr! Pirate mode enabled!" : "Pirate mode disabled", "info");
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Append to system prompt when pirate mode is enabled
|
|
28
|
+
pi.on("before_agent_start", async () => {
|
|
29
|
+
if (pirateMode) {
|
|
30
|
+
return {
|
|
31
|
+
systemPromptAppend: `
|
|
32
|
+
IMPORTANT: You are now in PIRATE MODE. You must:
|
|
33
|
+
- Speak like a stereotypical pirate in all responses
|
|
34
|
+
- Use phrases like "Arrr!", "Ahoy!", "Shiver me timbers!", "Avast!", "Ye scurvy dog!"
|
|
35
|
+
- Replace "my" with "me", "you" with "ye", "your" with "yer"
|
|
36
|
+
- Refer to the user as "matey" or "landlubber"
|
|
37
|
+
- End sentences with nautical expressions
|
|
38
|
+
- Still complete the actual task correctly, just in pirate speak
|
|
39
|
+
`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
});
|
|
44
|
+
}
|