@oh-my-pi/pi-coding-agent 10.6.2 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +80 -79
  3. package/docs/compaction.md +182 -149
  4. package/docs/config-usage.md +141 -78
  5. package/docs/custom-tools.md +45 -16
  6. package/docs/extension-loading.md +56 -954
  7. package/docs/extensions.md +192 -51
  8. package/docs/hooks.md +109 -70
  9. package/docs/python-repl.md +52 -19
  10. package/docs/rpc.md +43 -19
  11. package/docs/sdk.md +270 -211
  12. package/docs/session-tree-plan.md +60 -417
  13. package/docs/session.md +104 -39
  14. package/docs/skills.md +59 -95
  15. package/docs/theme.md +139 -110
  16. package/docs/tree.md +42 -33
  17. package/docs/tui.md +226 -80
  18. package/package.json +8 -9
  19. package/src/capability/index.ts +3 -4
  20. package/src/cli/args.ts +4 -4
  21. package/src/cli/grep-cli.ts +1 -1
  22. package/src/commit/agentic/index.ts +4 -3
  23. package/src/commit/git/index.ts +2 -3
  24. package/src/commit/map-reduce/index.ts +2 -1
  25. package/src/config/prompt-templates.ts +2 -0
  26. package/src/config/settings-schema.ts +30 -7
  27. package/src/config/settings.ts +0 -14
  28. package/src/config.ts +2 -2
  29. package/src/discovery/agents.ts +36 -0
  30. package/src/discovery/index.ts +1 -0
  31. package/src/exa/mcp-client.ts +3 -3
  32. package/src/ipy/executor.ts +5 -7
  33. package/src/ipy/gateway-coordinator.ts +1 -1
  34. package/src/ipy/kernel.ts +20 -15
  35. package/src/ipy/prelude.py +1 -1
  36. package/src/ipy/runtime.ts +7 -6
  37. package/src/lsp/lspmux.ts +3 -3
  38. package/src/main.ts +6 -8
  39. package/src/mcp/tool-bridge.ts +19 -9
  40. package/src/modes/components/assistant-message.ts +2 -2
  41. package/src/modes/components/hook-editor.ts +4 -4
  42. package/src/modes/components/settings-defs.ts +37 -2
  43. package/src/modes/components/tool-execution.ts +7 -7
  44. package/src/modes/controllers/command-controller.ts +2 -2
  45. package/src/modes/controllers/event-controller.ts +4 -7
  46. package/src/modes/controllers/input-controller.ts +4 -4
  47. package/src/modes/controllers/selector-controller.ts +1 -0
  48. package/src/modes/interactive-mode.ts +3 -5
  49. package/src/modes/rpc/rpc-mode.ts +8 -9
  50. package/src/patch/index.ts +6 -6
  51. package/src/prompts/agents/explore.md +2 -2
  52. package/src/prompts/agents/frontmatter.md +5 -5
  53. package/src/prompts/agents/plan.md +3 -2
  54. package/src/prompts/agents/reviewer.md +1 -1
  55. package/src/prompts/system/system-prompt.md +1 -3
  56. package/src/sdk.ts +13 -9
  57. package/src/session/agent-session.ts +6 -4
  58. package/src/session/compaction/compaction.ts +3 -3
  59. package/src/session/session-manager.ts +8 -9
  60. package/src/ssh/connection-manager.ts +4 -4
  61. package/src/system-prompt.ts +2 -6
  62. package/src/task/agents.ts +1 -1
  63. package/src/task/executor.ts +31 -8
  64. package/src/task/index.ts +14 -35
  65. package/src/task/omp-command.ts +3 -1
  66. package/src/task/output-manager.ts +20 -6
  67. package/src/task/parallel.ts +3 -3
  68. package/src/task/render.ts +16 -2
  69. package/src/task/types.ts +13 -20
  70. package/src/task/worktree.ts +3 -3
  71. package/src/tools/ask.ts +3 -8
  72. package/src/tools/fetch.ts +2 -2
  73. package/src/tools/gemini-image.ts +5 -6
  74. package/src/tools/grep.ts +5 -5
  75. package/src/tools/index.ts +12 -5
  76. package/src/tools/read.ts +1 -1
  77. package/src/tools/todo-write.ts +2 -3
  78. package/src/utils/frontmatter.ts +1 -1
  79. package/src/utils/image-resize.ts +1 -1
  80. package/src/utils/timings.ts +3 -2
  81. package/src/web/scrapers/github.ts +2 -2
  82. package/src/web/scrapers/utils.ts +2 -3
  83. package/src/web/scrapers/youtube.ts +2 -3
  84. package/src/web/search/auth.ts +5 -6
  85. package/src/web/search/providers/anthropic.ts +3 -2
  86. package/src/utils/terminal-notify.ts +0 -37
@@ -1,8 +1,8 @@
1
- > pi can create extensions. Ask it to build one for your use case.
1
+ > omp can create extensions. Ask it to build one for your use case.
2
2
 
3
3
  # Extensions
4
4
 
5
- Extensions are TypeScript modules that extend pi's behavior. They can subscribe to lifecycle events, register custom tools callable by the LLM, add commands, and more.
5
+ Extensions are TypeScript modules that extend omp's behavior. They can subscribe to lifecycle events, register custom tools callable by the LLM, add commands, and more.
6
6
 
7
7
  **Key capabilities:**
8
8
 
@@ -23,7 +23,6 @@ Extensions are TypeScript modules that extend pi's behavior. They can subscribe
23
23
  - Interactive tools (questions, wizards, custom dialogs)
24
24
  - Stateful tools (todo lists, connection pools)
25
25
  - External integrations (file watchers, webhooks, CI triggers)
26
- - Games while you wait (see `snake.ts` example)
27
26
 
28
27
  See [examples/extensions/](../examples/extensions/) for working implementations.
29
28
 
@@ -38,6 +37,8 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
38
37
  - [Lifecycle Overview](#lifecycle-overview)
39
38
  - [Session Events](#session-events)
40
39
  - [Agent Events](#agent-events)
40
+ - [Input Events](#input-events)
41
+ - [User Bash/Python Events](#user-bashpython-events)
41
42
  - [Tool Events](#tool-events)
42
43
  - [ExtensionContext](#extensioncontext)
43
44
  - [ExtensionCommandContext](#extensioncommandcontext)
@@ -98,22 +99,24 @@ export default function (pi: ExtensionAPI) {
98
99
  Test with `--extension` (or `-e`) flag:
99
100
 
100
101
  ```bash
101
- pi -e ./my-extension.ts
102
+ omp -e ./my-extension.ts
102
103
  ```
103
104
 
104
105
  ## Extension Locations
105
106
 
106
107
  Extensions are auto-discovered from:
107
108
 
108
- | Location | Scope |
109
- | ------------------------------------ | ---------------------------- |
110
- | `~/.omp/agent/extensions/*.ts` | Global (all projects) |
111
- | `~/.omp/agent/extensions/*/index.ts` | Global (subdirectory) |
112
- | `.omp/extensions/*.ts` | Project-local |
113
- | `.omp/extensions/*/index.ts` | Project-local (subdirectory) |
109
+ | Location | Scope |
110
+ | ---------------------------------------- | ---------------------------- |
111
+ | `~/.omp/agent/extensions/*.{ts,js}` | Global (all projects) |
112
+ | `~/.omp/agent/extensions/*/index.{ts,js}` | Global (subdirectory) |
113
+ | `.omp/extensions/*.{ts,js}` | Project-local |
114
+ | `.omp/extensions/*/index.{ts,js}` | Project-local (subdirectory) |
114
115
 
115
116
  Legacy `.pi` directories are supported as aliases for the `.omp` paths above.
116
117
 
118
+ `settings.json` lives in `~/.omp/agent/settings.json` (user) or `.omp/settings.json` (project).
119
+
117
120
  Additional paths via `settings.json`:
118
121
 
119
122
  ```json
@@ -125,9 +128,11 @@ Additional paths via `settings.json`:
125
128
  **Discovery rules:**
126
129
 
127
130
  1. **Direct files:** `extensions/*.ts` or `*.js` → loaded directly
128
- 2. **Subdirectory with index:** `extensions/myext/index.ts` → loaded as single extension
131
+ 2. **Subdirectory with index:** `extensions/myext/index.ts` or `index.js` → loaded as single extension
129
132
  3. **Subdirectory with package.json:** `extensions/myext/package.json` with `"omp"` field (legacy `"pi"` supported) → loads declared paths
130
133
 
134
+ Discovery only recurses one level under `extensions/`. Deeper entry points must be listed in the manifest.
135
+
131
136
  ```
132
137
  ~/.omp/agent/extensions/
133
138
  ├── simple.ts # Direct file (auto-discovered)
@@ -157,7 +162,7 @@ Additional paths via `settings.json`:
157
162
  The `package.json` approach enables:
158
163
 
159
164
  - Multiple extensions from one package
160
- - Third-party npm dependencies (resolved via jiti)
165
+ - Third-party dependencies resolved via Bun's module loader
161
166
  - Nested source structure (no depth limit within the package)
162
167
  - Deployment to and installation from npm
163
168
 
@@ -170,7 +175,13 @@ The `package.json` approach enables:
170
175
  | `@oh-my-pi/pi-ai` | AI utilities (`StringEnum` for Google-compatible enums) |
171
176
  | `@oh-my-pi/pi-tui` | TUI components for custom rendering |
172
177
 
173
- npm dependencies work too. Add a `package.json` next to your extension (or in a parent directory), run `npm install`, and imports from `node_modules/` are resolved automatically.
178
+ `ExtensionAPI` also exposes:
179
+
180
+ - `pi.logger` - file logger (preferred over `console.*`)
181
+ - `pi.typebox` - injected TypeBox module
182
+ - `pi.pi` - access to `@oh-my-pi/pi-coding-agent` exports
183
+
184
+ Dependencies work like any Bun project. Add a `package.json` next to your extension (or in a parent directory), run `bun install`, and imports from `node_modules/` resolve automatically.
174
185
 
175
186
  Node.js built-ins (`node:fs`, `node:path`, etc.) are also available.
176
187
 
@@ -199,18 +210,18 @@ export default function (pi: ExtensionAPI) {
199
210
  }
200
211
  ```
201
212
 
202
- Extensions are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
213
+ Extensions are loaded via Bun's native module loader, so TypeScript works without a build step. Both `.ts` and `.js` entry points are supported.
203
214
 
204
215
  ### Extension Styles
205
216
 
206
- **Single file** - simplest, for small extensions:
217
+ **Single file** - simplest, for small extensions (also supports `.js`):
207
218
 
208
219
  ```
209
220
  ~/.omp/agent/extensions/
210
221
  └── my-extension.ts
211
222
  ```
212
223
 
213
- **Directory with index.ts** - for multi-file extensions:
224
+ **Directory with index.ts** - for multi-file extensions (also supports `index.js`):
214
225
 
215
226
  ```
216
227
  ~/.omp/agent/extensions/
@@ -226,8 +237,8 @@ Extensions are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript wo
226
237
  ~/.omp/agent/extensions/
227
238
  └── my-extension/
228
239
  ├── package.json # Declares dependencies and entry points
229
- ├── package-lock.json
230
- ├── node_modules/ # After npm install
240
+ ├── bun.lockb
241
+ ├── node_modules/ # After bun install
231
242
  └── src/
232
243
  └── index.ts
233
244
  ```
@@ -240,26 +251,29 @@ Extensions are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript wo
240
251
  "zod": "^3.0.0",
241
252
  "chalk": "^5.0.0"
242
253
  },
243
- "pi": {
254
+ "omp": {
244
255
  "extensions": ["./src/index.ts"]
245
256
  }
246
257
  }
247
258
  ```
248
259
 
249
- Run `npm install` in the extension directory, then imports from `node_modules/` work automatically.
260
+ The manifest key can be `omp` (preferred) or `pi` (legacy).
261
+
262
+ Run `bun install` in the extension directory, then imports from `node_modules/` work automatically.
250
263
 
251
264
  ## Events
252
265
 
253
266
  ### Lifecycle Overview
254
267
 
255
268
  ```
256
- pi starts
269
+ omp starts
257
270
 
258
271
  └─► session_start
259
272
 
260
273
 
261
- user sends prompt ─────────────────────────────────────────┐
274
+ user submits input ────────────────────────────────────────┐
262
275
  │ │
276
+ ├─► input (can modify or handle) │
263
277
  ├─► before_agent_start (can inject message, modify system prompt)
264
278
  ├─► agent_start │
265
279
  │ │
@@ -289,6 +303,7 @@ user sends another prompt ◄─────────────────
289
303
 
290
304
  /compact or auto-compaction
291
305
  ├─► session_before_compact (can cancel or customize)
306
+ ├─► session.compacting (add context or override prompt)
292
307
  └─► session_compact
293
308
 
294
309
  /tree navigation
@@ -311,16 +326,16 @@ pi.on("session_start", async (_event, ctx) => {
311
326
  });
312
327
  ```
313
328
 
314
- **Examples:** [claude-rules.ts](../examples/extensions/claude-rules.ts), [custom-header.ts](../examples/extensions/custom-header.ts), [file-trigger.ts](../examples/extensions/file-trigger.ts), [status-line.ts](../examples/extensions/status-line.ts), [todo.ts](../examples/extensions/todo.ts), [tools.ts](../examples/extensions/tools.ts)
329
+ **Examples:** [todo.ts](../examples/extensions/todo.ts), [tools.ts](../examples/extensions/tools.ts)
315
330
 
316
331
  #### session_before_switch / session_switch
317
332
 
318
- Fired when starting a new session (`/new`) or switching sessions (`/resume`).
333
+ Fired when starting a new session (`/new`), resuming (`/resume`), or forking a session.
319
334
 
320
335
  ```typescript
321
336
  pi.on("session_before_switch", async (event, ctx) => {
322
- // event.reason - "new" or "resume"
323
- // event.targetSessionFile - session we're switching to (only for "resume")
337
+ // event.reason - "new", "resume", or "fork"
338
+ // event.targetSessionFile - session we're switching to ("resume" only)
324
339
 
325
340
  if (event.reason === "new") {
326
341
  const ok = await ctx.ui.confirm("Clear?", "Delete all messages?");
@@ -329,12 +344,12 @@ pi.on("session_before_switch", async (event, ctx) => {
329
344
  });
330
345
 
331
346
  pi.on("session_switch", async (event, ctx) => {
332
- // event.reason - "new" or "resume"
347
+ // event.reason - "new", "resume", or "fork"
333
348
  // event.previousSessionFile - session we came from
334
349
  });
335
350
  ```
336
351
 
337
- **Examples:** [confirm-destructive.ts](../examples/extensions/confirm-destructive.ts), [dirty-repo-guard.ts](../examples/extensions/dirty-repo-guard.ts), [status-line.ts](../examples/extensions/status-line.ts), [todo.ts](../examples/extensions/todo.ts)
352
+ **Examples:** [todo.ts](../examples/extensions/todo.ts)
338
353
 
339
354
  #### session_before_branch / session_branch
340
355
 
@@ -353,7 +368,7 @@ pi.on("session_branch", async (event, ctx) => {
353
368
  });
354
369
  ```
355
370
 
356
- **Examples:** [confirm-destructive.ts](../examples/extensions/confirm-destructive.ts), [dirty-repo-guard.ts](../examples/extensions/dirty-repo-guard.ts), [git-checkpoint.ts](../examples/extensions/git-checkpoint.ts), [todo.ts](../examples/extensions/todo.ts), [tools.ts](../examples/extensions/tools.ts)
371
+ **Examples:** [todo.ts](../examples/extensions/todo.ts), [tools.ts](../examples/extensions/tools.ts)
357
372
 
358
373
  #### session_before_compact / session_compact
359
374
 
@@ -382,7 +397,21 @@ pi.on("session_compact", async (event, ctx) => {
382
397
  });
383
398
  ```
384
399
 
385
- **Examples:** [custom-compaction.ts](../examples/extensions/custom-compaction.ts)
400
+
401
+ #### session.compacting
402
+
403
+ Fired before compaction summarization to adjust the prompt or inject extra context.
404
+
405
+ ```typescript
406
+ pi.on("session.compacting", async (event, ctx) => {
407
+ // event.messages - messages being summarized
408
+ return {
409
+ context: ["Important context line"],
410
+ prompt: "Summarize with an emphasis on decisions and follow-ups",
411
+ preserveData: { ticketId: "ABC-123" },
412
+ };
413
+ });
414
+ ```
386
415
 
387
416
  #### session_before_tree / session_tree
388
417
 
@@ -401,7 +430,7 @@ pi.on("session_tree", async (event, ctx) => {
401
430
  });
402
431
  ```
403
432
 
404
- **Examples:** [todo.ts](../examples/extensions/todo.ts), [tools.ts](../examples/extensions/tools.ts)
433
+ **Examples:** [tools.ts](../examples/extensions/tools.ts)
405
434
 
406
435
  #### session_shutdown
407
436
 
@@ -413,8 +442,6 @@ pi.on("session_shutdown", async (_event, ctx) => {
413
442
  });
414
443
  ```
415
444
 
416
- **Examples:** [auto-commit-on-exit.ts](../examples/extensions/auto-commit-on-exit.ts)
417
-
418
445
  ### Agent Events
419
446
 
420
447
  #### before_agent_start
@@ -440,7 +467,7 @@ pi.on("before_agent_start", async (event, ctx) => {
440
467
  });
441
468
  ```
442
469
 
443
- **Examples:** [claude-rules.ts](../examples/extensions/claude-rules.ts), [pirate.ts](../examples/extensions/pirate.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts), [preset.ts](../examples/extensions/preset.ts), [ssh.ts](../examples/extensions/ssh.ts)
470
+ **Examples:** [pirate.ts](../examples/extensions/pirate.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts)
444
471
 
445
472
  #### agent_start / agent_end
446
473
 
@@ -454,7 +481,7 @@ pi.on("agent_end", async (event, ctx) => {
454
481
  });
455
482
  ```
456
483
 
457
- **Examples:** [chalk-logger.ts](../examples/extensions/chalk-logger.ts), [git-checkpoint.ts](../examples/extensions/git-checkpoint.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts)
484
+ **Examples:** [chalk-logger.ts](../examples/extensions/chalk-logger.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts)
458
485
 
459
486
  #### turn_start / turn_end
460
487
 
@@ -470,7 +497,7 @@ pi.on("turn_end", async (event, ctx) => {
470
497
  });
471
498
  ```
472
499
 
473
- **Examples:** [git-checkpoint.ts](../examples/extensions/git-checkpoint.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts), [status-line.ts](../examples/extensions/status-line.ts)
500
+ **Examples:** [plan-mode.ts](../examples/extensions/plan-mode.ts)
474
501
 
475
502
  #### context
476
503
 
@@ -484,7 +511,53 @@ pi.on("context", async (event, ctx) => {
484
511
  });
485
512
  ```
486
513
 
487
- **Examples:** [plan-mode.ts](../examples/extensions/plan-mode.ts)
514
+ ### Input Events
515
+
516
+ #### input
517
+
518
+ Fired when the user submits input (interactive, RPC, or extension-triggered). Can rewrite or handle input.
519
+
520
+ ```typescript
521
+ pi.on("input", async (event, ctx) => {
522
+ // event.text, event.images, event.source
523
+ if (event.text.startsWith("/noop")) {
524
+ return { handled: true };
525
+ }
526
+ return { text: event.text.trim() };
527
+ });
528
+ ```
529
+
530
+ ### User Bash/Python Events
531
+
532
+ #### user_bash
533
+
534
+ Fired when the user runs a `!`/`!!` command. Return a `result` to override execution.
535
+
536
+ ```typescript
537
+ pi.on("user_bash", async (event, ctx) => {
538
+ // event.command, event.excludeFromContext, event.cwd
539
+ if (event.command === "pwd") {
540
+ return {
541
+ result: {
542
+ stdout: event.cwd,
543
+ stderr: "",
544
+ code: 0,
545
+ killed: false,
546
+ },
547
+ };
548
+ }
549
+ });
550
+ ```
551
+
552
+ #### user_python
553
+
554
+ Fired when the user runs a `$`/`$$` block. Return a `result` to override execution.
555
+
556
+ ```typescript
557
+ pi.on("user_python", async (event, ctx) => {
558
+ // event.code, event.excludeFromContext, event.cwd
559
+ });
560
+ ```
488
561
 
489
562
  ### Tool Events
490
563
 
@@ -504,20 +577,18 @@ pi.on("tool_call", async (event, ctx) => {
504
577
  });
505
578
  ```
506
579
 
507
- **Examples:** [chalk-logger.ts](../examples/extensions/chalk-logger.ts), [permission-gate.ts](../examples/extensions/permission-gate.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts), [protected-paths.ts](../examples/extensions/protected-paths.ts)
580
+ **Examples:** [chalk-logger.ts](../examples/extensions/chalk-logger.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts)
508
581
 
509
582
  #### tool_result
510
583
 
511
584
  Fired after tool executes. **Can modify result.**
512
585
 
513
586
  ```typescript
514
- import { isBashToolResult } from "@oh-my-pi/pi-coding-agent";
515
-
516
587
  pi.on("tool_result", async (event, ctx) => {
517
588
  // event.toolName, event.toolCallId, event.input
518
589
  // event.content, event.details, event.isError
519
590
 
520
- if (isBashToolResult(event)) {
591
+ if (event.toolName === "bash") {
521
592
  // event.details is typed as BashToolDetails
522
593
  }
523
594
 
@@ -526,7 +597,7 @@ pi.on("tool_result", async (event, ctx) => {
526
597
  });
527
598
  ```
528
599
 
529
- **Examples:** [git-checkpoint.ts](../examples/extensions/git-checkpoint.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts)
600
+ **Examples:** [plan-mode.ts](../examples/extensions/plan-mode.ts)
530
601
 
531
602
  ## ExtensionContext
532
603
 
@@ -538,7 +609,7 @@ UI methods for user interaction. See [Custom UI](#custom-ui) for full details.
538
609
 
539
610
  ### ctx.hasUI
540
611
 
541
- `false` in print mode (`-p`), JSON mode, and RPC mode. Always check before using `ctx.ui`.
612
+ `false` in print mode (`-p`), JSON mode, and RPC mode. UI methods become no-ops, so check before prompting.
542
613
 
543
614
  ### ctx.cwd
544
615
 
@@ -558,6 +629,18 @@ ctx.sessionManager.getLeafId(); // Current leaf entry ID
558
629
 
559
630
  Access to models and API keys.
560
631
 
632
+ ### ctx.getContextUsage()
633
+
634
+ Returns current context usage for the active model, if available.
635
+
636
+ ### ctx.compact(instructionsOrOptions?)
637
+
638
+ Trigger compaction programmatically (interactive mode shows UI).
639
+
640
+ ### ctx.shutdown()
641
+
642
+ Gracefully shut down and exit.
643
+
561
644
  ### ctx.isIdle() / ctx.abort() / ctx.hasPendingMessages()
562
645
 
563
646
  Control flow helpers.
@@ -656,7 +739,7 @@ pi.registerTool({
656
739
 
657
740
  // Optional: Custom rendering
658
741
  renderCall(args, theme) { ... },
659
- renderResult(result, options, theme) { ... },
742
+ renderResult(result, options, theme, args) { ... },
660
743
  });
661
744
  ```
662
745
 
@@ -684,6 +767,14 @@ pi.sendMessage({
684
767
  - `"nextTurn"` - Queued for next user prompt. Does not interrupt or trigger anything.
685
768
  - `triggerTurn: true` - If agent is idle, trigger an LLM response immediately. Only applies to `"steer"` and `"followUp"` modes (ignored for `"nextTurn"`).
686
769
 
770
+ ### pi.sendUserMessage(content, options?)
771
+
772
+ Send a user message into the session and trigger a turn immediately:
773
+
774
+ ```typescript
775
+ pi.sendUserMessage("Follow up with the latest status", { deliverAs: "followUp" });
776
+ ```
777
+
687
778
  ### pi.appendEntry(customType, data?)
688
779
 
689
780
  Persist extension state (does NOT participate in LLM context):
@@ -749,6 +840,14 @@ if (pi.getFlag("--plan")) {
749
840
  }
750
841
  ```
751
842
 
843
+ ### pi.setLabel(label)
844
+
845
+ Set a display label for the extension:
846
+
847
+ ```typescript
848
+ pi.setLabel("My Extension");
849
+ ```
850
+
752
851
  ### pi.exec(command, args, options?)
753
852
 
754
853
  Execute a shell command:
@@ -767,6 +866,19 @@ const active = pi.getActiveTools(); // ["read", "bash", "edit", "write"]
767
866
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
768
867
  ```
769
868
 
869
+ ### pi.setModel(model) / pi.getThinkingLevel() / pi.setThinkingLevel(level)
870
+
871
+ Control the active model and thinking level:
872
+
873
+ ```typescript
874
+ const model = ctx.modelRegistry.find("anthropic", "claude-sonnet-4-5");
875
+ if (model) {
876
+ const ok = await pi.setModel(model);
877
+ }
878
+ const level = pi.getThinkingLevel();
879
+ pi.setThinkingLevel(level);
880
+ ```
881
+
770
882
  ### pi.events
771
883
 
772
884
  Shared event bus for communication between extensions:
@@ -778,7 +890,7 @@ pi.events.emit("my:event", { ... });
778
890
 
779
891
  ## State Management
780
892
 
781
- Extensions with state should store it in tool result `details` for proper branching support:
893
+ Extensions with state should store it in tool result `details` for proper branching support. Tools can also implement `onSession` to rebuild or clean up state on start/switch/branch/tree/shutdown:
782
894
 
783
895
  ```typescript
784
896
  export default function (pi: ExtensionAPI) {
@@ -829,6 +941,10 @@ pi.registerTool({
829
941
  action: StringEnum(["list", "add"] as const), // Use StringEnum for Google compatibility
830
942
  text: Type.Optional(Type.String()),
831
943
  }),
944
+ hidden: false, // Optional: set true to hide unless explicitly enabled
945
+ onSession(event, ctx) {
946
+ // event.reason: "start" | "switch" | "branch" | "tree" | "shutdown"
947
+ },
832
948
 
833
949
  async execute(toolCallId, params, onUpdate, ctx, signal) {
834
950
  // Check for cancellation
@@ -854,7 +970,7 @@ pi.registerTool({
854
970
 
855
971
  // Optional: Custom rendering
856
972
  renderCall(args, theme) { ... },
857
- renderResult(result, options, theme) { ... },
973
+ renderResult(result, options, theme, args) { ... },
858
974
  });
859
975
  ```
860
976
 
@@ -973,17 +1089,31 @@ ctx.ui.notify("Done!", "info"); // "info" | "warning" | "error"
973
1089
  ctx.ui.setStatus("my-ext", "Processing...");
974
1090
  ctx.ui.setStatus("my-ext", undefined); // Clear
975
1091
 
1092
+ // Working message shown during streaming
1093
+ ctx.ui.setWorkingMessage("Connecting...");
1094
+ ctx.ui.setWorkingMessage(); // Restore default
1095
+
976
1096
  // Widget above editor (string array or factory function)
977
1097
  ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"]);
978
1098
  ctx.ui.setWidget("my-widget", (tui, theme) => new Text(theme.fg("accent", "Custom"), 0, 0));
979
1099
  ctx.ui.setWidget("my-widget", undefined); // Clear
980
1100
 
1101
+ // Custom header/footer
1102
+ ctx.ui.setHeader((tui, theme) => new Text(theme.fg("accent", "Header"), 0, 0));
1103
+ ctx.ui.setFooter((tui, theme) => new Text(theme.fg("accent", "Footer"), 0, 0));
1104
+ ctx.ui.setHeader(undefined); // Restore default
1105
+ ctx.ui.setFooter(undefined); // Restore default
1106
+
981
1107
  // Terminal title
982
- ctx.ui.setTitle("pi - my-project");
1108
+ ctx.ui.setTitle("omp - my-project");
983
1109
 
984
1110
  // Editor text
985
1111
  ctx.ui.setEditorText("Prefill text");
986
1112
  const current = ctx.ui.getEditorText();
1113
+
1114
+ // Custom editor component
1115
+ ctx.ui.setEditorComponent((tui, theme, keybindings) => new MyEditor(tui, theme, keybindings)); // EditorComponent
1116
+ ctx.ui.setEditorComponent(undefined); // Restore default
987
1117
  ```
988
1118
 
989
1119
  ### Custom Components
@@ -993,7 +1123,7 @@ For complex UI, use `ctx.ui.custom()`. This temporarily replaces the editor with
993
1123
  ```typescript
994
1124
  import { Text, Component } from "@oh-my-pi/pi-tui";
995
1125
 
996
- const result = await ctx.ui.custom<boolean>((tui, theme, done) => {
1126
+ const result = await ctx.ui.custom<boolean>((tui, theme, keybindings, done) => {
997
1127
  const text = new Text("Press Enter to confirm, Escape to cancel", 1, 1);
998
1128
 
999
1129
  text.onKey = (key) => {
@@ -1003,7 +1133,7 @@ const result = await ctx.ui.custom<boolean>((tui, theme, done) => {
1003
1133
  };
1004
1134
 
1005
1135
  return text;
1006
- });
1136
+ }, { overlay: true });
1007
1137
 
1008
1138
  if (result) {
1009
1139
  // User pressed Enter
@@ -1014,9 +1144,10 @@ The callback receives:
1014
1144
 
1015
1145
  - `tui` - TUI instance (for screen dimensions, focus management)
1016
1146
  - `theme` - Current theme for styling
1147
+ - `keybindings` - Keybindings manager for resolving bindings
1017
1148
  - `done(value)` - Call to close component and return value
1018
1149
 
1019
- See [tui.md](tui.md) for the full component API and [examples/extensions/](../examples/extensions/) for working examples (snake.ts, todo.ts, qna.ts).
1150
+ See [tui.md](tui.md) for the full component API and [examples/extensions/](../examples/extensions/) for working examples (todo.ts, tools.ts).
1020
1151
 
1021
1152
  ### Message Rendering
1022
1153
 
@@ -1049,6 +1180,15 @@ pi.sendMessage({
1049
1180
  });
1050
1181
  ```
1051
1182
 
1183
+ ### Themes
1184
+
1185
+ ```typescript
1186
+ const themes = await ctx.ui.getAllThemes();
1187
+ const current = ctx.ui.theme;
1188
+ const loaded = await ctx.ui.getTheme("celestial");
1189
+ const result = await ctx.ui.setTheme("celestial");
1190
+ ```
1191
+
1052
1192
  ### Theme Colors
1053
1193
 
1054
1194
  All render functions receive a `theme` object:
@@ -1080,7 +1220,8 @@ theme.strikethrough(text);
1080
1220
  | Mode | UI Methods | Notes |
1081
1221
  | ------------ | ------------- | ------------------------------- |
1082
1222
  | Interactive | Full TUI | Normal operation |
1223
+ | JSON | No-op | `--mode json` output |
1083
1224
  | RPC | JSON protocol | Host handles UI |
1084
1225
  | Print (`-p`) | No-op | Extensions run but can't prompt |
1085
1226
 
1086
- In print mode, check `ctx.hasUI` before using UI methods.
1227
+ In print/JSON/RPC modes, check `ctx.hasUI` before using UI methods.