@oh-my-pi/pi-coding-agent 10.6.1 → 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
package/docs/theme.md CHANGED
@@ -8,7 +8,7 @@ Themes allow you to customize the colors used throughout the coding agent TUI.
8
8
 
9
9
  Every theme must define all color tokens. There are no optional colors.
10
10
 
11
- ### Core UI (10 colors)
11
+ ### Core UI (11 colors)
12
12
 
13
13
  | Token | Purpose | Examples |
14
14
  | -------------- | --------------------- | ------------------------------------ |
@@ -67,7 +67,7 @@ Note: Diff colors are specific to tool execution boxes and must work with tool b
67
67
 
68
68
  ### Syntax Highlighting (9 colors)
69
69
 
70
- Future-proofing for syntax highlighting support:
70
+ Used for native syntax highlighting in tool output and editors:
71
71
 
72
72
  | Token | Purpose |
73
73
  | ------------------- | -------------------------------- |
@@ -85,24 +85,44 @@ Future-proofing for syntax highlighting support:
85
85
 
86
86
  Editor border colors that indicate the current thinking/reasoning level:
87
87
 
88
- | Token | Purpose |
89
- | ----------------- | ----------------------------------------------------------------- |
90
- | `thinkingOff` | Border when thinking is off (most subtle) |
91
- | `thinkingMinimal` | Border for minimal thinking |
92
- | `thinkingLow` | Border for low thinking |
93
- | `thinkingMedium` | Border for medium thinking |
94
- | `thinkingHigh` | Border for high thinking |
95
- | `thinkingXhigh` | Border for xhigh thinking (most prominent, OpenAI codex-max only) |
88
+ | Token | Purpose |
89
+ | ----------------- | ------------------------------------------ |
90
+ | `thinkingOff` | Border when thinking is off (most subtle) |
91
+ | `thinkingMinimal` | Border for minimal thinking |
92
+ | `thinkingLow` | Border for low thinking |
93
+ | `thinkingMedium` | Border for medium thinking |
94
+ | `thinkingHigh` | Border for high thinking |
95
+ | `thinkingXhigh` | Border for xhigh thinking (most prominent) |
96
96
 
97
97
  These create a visual hierarchy: off → minimal → low → medium → high → xhigh
98
98
 
99
- ### Bash Mode (1 color)
100
-
101
- | Token | Purpose |
102
- | ---------- | ------------------------------------------------ |
103
- | `bashMode` | Editor border color when in bash mode (! prefix) |
104
-
105
- **Total: 50 color tokens** (all required)
99
+ ### Mode Borders (2 colors)
100
+
101
+ | Token | Purpose |
102
+ | ------------ | ------------------------------------------------ |
103
+ | `bashMode` | Editor border color when in bash mode (! prefix) |
104
+ | `pythonMode` | Editor border color when in python mode (>>>) |
105
+
106
+ ### Status Line (14 colors)
107
+
108
+ | Token | Purpose |
109
+ | --------------------- | --------------------------------------- |
110
+ | `statusLineBg` | Status line background |
111
+ | `statusLineSep` | Separators between status line segments |
112
+ | `statusLineModel` | Model segment text |
113
+ | `statusLinePath` | Working directory segment |
114
+ | `statusLineGitClean` | Git segment (clean) |
115
+ | `statusLineGitDirty` | Git segment (dirty) |
116
+ | `statusLineContext` | Context window usage segment |
117
+ | `statusLineSpend` | Token input/total segment |
118
+ | `statusLineStaged` | Git staged count |
119
+ | `statusLineDirty` | Git unstaged count |
120
+ | `statusLineUntracked` | Git untracked count |
121
+ | `statusLineOutput` | Token output/cache output segment |
122
+ | `statusLineCost` | Cost segment |
123
+ | `statusLineSubagents` | Subagent count segment |
124
+
125
+ **Total: 66 color tokens** (all required)
106
126
 
107
127
  ### HTML Export Colors (optional)
108
128
 
@@ -132,7 +152,7 @@ Themes are defined in JSON files with the following structure:
132
152
 
133
153
  ```json
134
154
  {
135
- "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/theme-schema.json",
155
+ "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json",
136
156
  "name": "my-theme",
137
157
  "vars": {
138
158
  "blue": "#0066cc",
@@ -151,7 +171,7 @@ Themes are defined in JSON files with the following structure:
151
171
 
152
172
  ## Symbols
153
173
 
154
- Themes can also customize specific UI symbols (icons, separators, bullets, etc.). Use `symbols.preset` to set a theme default (overridden by user settings), and `symbols.overrides` to override individual keys.
174
+ Themes can also customize specific UI symbols (icons, separators, bullets, etc.). Use `symbols.preset` (`unicode`, `nerd`, `ascii`) to set a theme default (overridden by the `symbolPreset` setting), and `symbols.overrides` to override individual keys.
155
175
 
156
176
  Example:
157
177
 
@@ -175,12 +195,14 @@ Symbol keys by category:
175
195
  - Tree: `tree.branch`, `tree.last`, `tree.vertical`, `tree.horizontal`, `tree.hook`
176
196
  - Boxes (rounded): `boxRound.topLeft`, `boxRound.topRight`, `boxRound.bottomLeft`, `boxRound.bottomRight`, `boxRound.horizontal`, `boxRound.vertical`
177
197
  - Boxes (sharp): `boxSharp.topLeft`, `boxSharp.topRight`, `boxSharp.bottomLeft`, `boxSharp.bottomRight`, `boxSharp.horizontal`, `boxSharp.vertical`, `boxSharp.cross`, `boxSharp.teeDown`, `boxSharp.teeUp`, `boxSharp.teeRight`, `boxSharp.teeLeft`
178
- - Separators: `sep.powerline`, `sep.powerlineThin`, `sep.powerlineLeft`, `sep.powerlineRight`, `sep.powerlineThinLeft`, `sep.powerlineThinRight`, `sep.dot`, `sep.slash`, `sep.pipe`
179
- - Icons: `icon.model`, `icon.folder`, `icon.file`, `icon.git`, `icon.branch`, `icon.tokens`, `icon.context`, `icon.cost`, `icon.time`, `icon.pi`, `icon.agents`, `icon.cache`, `icon.input`, `icon.output`, `icon.host`, `icon.session`, `icon.package`, `icon.warning`, `icon.rewind`, `icon.auto`, `icon.extensionSkill`, `icon.extensionTool`, `icon.extensionSlashCommand`, `icon.extensionMcp`, `icon.extensionRule`, `icon.extensionHook`, `icon.extensionPrompt`, `icon.extensionContextFile`, `icon.extensionInstruction`
198
+ - Separators: `sep.powerline`, `sep.powerlineThin`, `sep.powerlineLeft`, `sep.powerlineRight`, `sep.powerlineThinLeft`, `sep.powerlineThinRight`, `sep.block`, `sep.space`, `sep.asciiLeft`, `sep.asciiRight`, `sep.dot`, `sep.slash`, `sep.pipe`
199
+ - Icons: `icon.model`, `icon.plan`, `icon.folder`, `icon.file`, `icon.git`, `icon.branch`, `icon.tokens`, `icon.context`, `icon.cost`, `icon.time`, `icon.pi`, `icon.agents`, `icon.cache`, `icon.input`, `icon.output`, `icon.host`, `icon.session`, `icon.package`, `icon.warning`, `icon.rewind`, `icon.auto`, `icon.extensionSkill`, `icon.extensionTool`, `icon.extensionSlashCommand`, `icon.extensionMcp`, `icon.extensionRule`, `icon.extensionHook`, `icon.extensionPrompt`, `icon.extensionContextFile`, `icon.extensionInstruction`
180
200
  - Thinking: `thinking.minimal`, `thinking.low`, `thinking.medium`, `thinking.high`, `thinking.xhigh`
181
201
  - Checkboxes: `checkbox.checked`, `checkbox.unchecked`
182
- - Formatting: `format.bullet`, `format.dash`
202
+ - Formatting: `format.bullet`, `format.dash`, `format.bracketLeft`, `format.bracketRight`
183
203
  - Markdown: `md.quoteBorder`, `md.hrChar`, `md.bullet`
204
+ - Language icons: `lang.default`, `lang.typescript`, `lang.javascript`, `lang.python`, `lang.rust`, `lang.go`, `lang.java`, `lang.c`, `lang.cpp`, `lang.csharp`, `lang.ruby`, `lang.php`, `lang.swift`, `lang.kotlin`, `lang.shell`, `lang.html`, `lang.css`, `lang.json`, `lang.yaml`, `lang.markdown`, `lang.sql`, `lang.docker`, `lang.lua`, `lang.text`, `lang.env`, `lang.toml`, `lang.xml`, `lang.ini`, `lang.conf`, `lang.log`, `lang.csv`, `lang.tsv`, `lang.image`, `lang.pdf`, `lang.archive`, `lang.binary`
205
+ - Settings tabs: `tab.display`, `tab.agent`, `tab.input`, `tab.tools`, `tab.config`, `tab.services`, `tab.bash`, `tab.lsp`, `tab.ttsr`, `tab.status`
184
206
 
185
207
  ### Color Values
186
208
 
@@ -238,55 +260,47 @@ This is useful for:
238
260
 
239
261
  ## Built-in Themes
240
262
 
241
- OMP comes with two built-in themes:
242
-
243
- ### `dark` (default)
263
+ OMP ships with `dark` (default), `light`, and 90+ curated themes under `src/modes/theme/defaults/`. Examples include:
244
264
 
245
- Optimized for dark terminal backgrounds with bright, saturated colors.
246
-
247
- ### `light`
248
-
249
- Optimized for light terminal backgrounds with darker, muted colors.
265
+ - **Dark themes**: `dark-aurora`, `dark-gruvbox`, `dark-nord`, `dark-tokyo-night`, `dark-catppuccin`, `dark-dracula`, `dark-solarized`, `dark-github`, `dark-monokai`, `dark-synthwave`
266
+ - **Light themes**: `light-solarized`, `light-gruvbox`, `light-github`, `light-catppuccin`, `light-paper`, `light-dawn`, `light-frost`
267
+ - **Neutral/material**: `graphite`, `obsidian`, `onyx`, `titanium`, `marble`, `pearl`, `alabaster`, `anthracite`
250
268
 
251
269
  ## Selecting a Theme
252
270
 
253
- Themes are configured in the settings (accessible via `/settings`):
271
+ Themes are configured in the Settings UI (Display → Theme) or via the config CLI:
254
272
 
255
- ```json
256
- {
257
- "theme": "dark"
258
- }
273
+ ```bash
274
+ omp config set theme dark
259
275
  ```
260
276
 
261
- Or use the `/theme` command interactively.
262
-
263
- On first run, OMP detects your terminal's background and sets a sensible default (`dark` or `light`).
277
+ On first run, OMP uses the terminal background reported by `COLORFGBG` and falls back to `dark` if unavailable.
264
278
 
265
279
  ## Custom Themes
266
280
 
267
281
  ### Theme Locations
268
282
 
269
- Custom themes are loaded from `~/.omp/agent/themes/*.json`.
283
+ Custom themes are loaded from `~/.omp/agent/themes/*.json` by default, or from `$PI_CODING_AGENT_DIR/themes` if that environment variable is set.
270
284
 
271
285
  ### Creating a Custom Theme
272
286
 
273
287
  1. **Create theme directory:**
274
288
 
275
289
  ```bash
276
- mkdir -p ~/.omp/agent/themes
290
+ mkdir -p "${PI_CODING_AGENT_DIR:-~/.omp/agent}/themes"
277
291
  ```
278
292
 
279
293
  2. **Create theme file:**
280
294
 
281
295
  ```bash
282
- vim ~/.omp/agent/themes/my-theme.json
296
+ vim "${PI_CODING_AGENT_DIR:-~/.omp/agent}/themes/my-theme.json"
283
297
  ```
284
298
 
285
- 3. **Define all colors:**
299
+ 3. **Define all colors (see the schema for the full list; snippet below shows structure):**
286
300
 
287
301
  ```json
288
302
  {
289
- "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/theme-schema.json",
303
+ "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json",
290
304
  "name": "my-theme",
291
305
  "vars": {
292
306
  "primary": "#00aaff",
@@ -309,7 +323,9 @@ Custom themes are loaded from `~/.omp/agent/themes/*.json`.
309
323
  "toolPendingBg": "#1e1e2e",
310
324
  "toolSuccessBg": "#1e2e1e",
311
325
  "toolErrorBg": "#2e1e1e",
312
- "toolText": "",
326
+ "toolTitle": "",
327
+ "toolOutput": "",
328
+ // ...
313
329
 
314
330
  "mdHeading": "#ffaa00",
315
331
  "mdLink": "primary",
@@ -339,14 +355,16 @@ Custom themes are loaded from `~/.omp/agent/themes/*.json`.
339
355
  "thinkingMinimal": "primary",
340
356
  "thinkingLow": "#00aaff",
341
357
  "thinkingMedium": "#00ffff",
342
- "thinkingHigh": "#ff00ff"
358
+ "thinkingHigh": "#ff00ff",
359
+ "thinkingXhigh": "#ff88ff"
360
+ // ... plus bashMode, pythonMode, statusLine* colors
343
361
  }
344
362
  }
345
363
  ```
346
364
 
347
365
  4. **Select your theme:**
348
- - Use `/settings` command and set `"theme": "my-theme"`
349
- - Or use `/theme` command interactively
366
+ - Use the Settings UI (Display Theme)
367
+ - Or run `omp config set theme my-theme`
350
368
 
351
369
  ## Tips
352
370
 
@@ -367,7 +385,7 @@ Custom themes are loaded from `~/.omp/agent/themes/*.json`.
367
385
  ### Color Harmony
368
386
 
369
387
  - Start with a base palette (e.g., Nord, Gruvbox, Tokyo Night)
370
- - Define your palette in `defs`
388
+ - Define your palette in `vars`
371
389
  - Reference colors consistently
372
390
 
373
391
  ### Testing
@@ -444,51 +462,56 @@ Example usage:
444
462
 
445
463
  ### Terminal Compatibility
446
464
 
447
- OMP uses 24-bit RGB colors (`\x1b[38;2;R;G;Bm`). Most modern terminals support this:
465
+ OMP prefers 24-bit RGB colors (`\x1b[38;2;R;G;Bm`) and assumes truecolor on modern terminals.
448
466
 
449
- - iTerm2, Alacritty, Kitty, WezTerm
450
- - ✅ Windows Terminal
451
- - ✅ VS Code integrated terminal
452
- - ✅ Modern GNOME Terminal, Konsole
467
+ Color mode detection:
453
468
 
454
- For older terminals with only 256-color support, OMP automatically falls back to the nearest 256-color approximation.
469
+ - `COLORTERM=truecolor|24bit` or `WT_SESSION` truecolor
470
+ - `TERM=dumb`, `TERM=linux`, or empty `TERM` → 256-color fallback
471
+ - Otherwise → truecolor
455
472
 
456
- To check if your terminal supports truecolor:
473
+ If you need to confirm terminal hints:
457
474
 
458
475
  ```bash
459
- echo $COLORTERM # Should output "truecolor" or "24bit"
476
+ echo $COLORTERM
460
477
  ```
461
478
 
462
479
  ## Example Themes
463
480
 
464
481
  See the built-in themes for complete examples:
465
482
 
466
- - [Dark theme](../src/themes/dark.json)
467
- - [Light theme](../src/themes/light.json)
483
+ - [Dark theme](../src/modes/theme/dark.json)
484
+ - [Light theme](../src/modes/theme/light.json)
485
+ - [Defaults library](../src/modes/theme/defaults)
468
486
 
469
487
  ## Schema Validation
470
488
 
471
- Themes are validated on load using [TypeBox](https://github.com/sinclairzx81/typebox) + [Ajv](https://ajv.js.org/).
489
+ Themes are validated on load using [TypeBox](https://github.com/sinclairzx81/typebox) and the TypeBox compiler.
472
490
 
473
491
  Invalid themes will show an error with details about what's wrong:
474
492
 
475
493
  ```
476
- Error loading theme 'my-theme':
477
- - colors.accent: must be string or number
478
- - colors.mdHeading: required property missing
494
+ Invalid theme "my-theme":
495
+
496
+ Missing required color tokens:
497
+ - mdHeading
498
+ - mdLink
499
+
500
+ Other errors:
501
+ - /colors/accent: Expected union value
479
502
  ```
480
503
 
481
504
  For editor support, the JSON schema is available at:
482
505
 
483
506
  ```
484
- https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/theme-schema.json
507
+ https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json
485
508
  ```
486
509
 
487
510
  Add to your theme file for auto-completion and validation:
488
511
 
489
512
  ```json
490
513
  {
491
- "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/theme-schema.json",
514
+ "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json",
492
515
  ...
493
516
  }
494
517
  ```
@@ -511,6 +534,30 @@ class Theme {
511
534
  bold(text: string): string;
512
535
  italic(text: string): string;
513
536
  underline(text: string): string;
537
+ strikethrough(text: string): string;
538
+ inverse(text: string): string;
539
+
540
+ // Raw ANSI codes (for composing with other formatters)
541
+ getFgAnsi(color: ThemeColor): string;
542
+ getBgAnsi(color: ThemeBg): string;
543
+
544
+ // Symbol access
545
+ symbol(key: SymbolKey): string;
546
+ styledSymbol(key: SymbolKey, color: ThemeColor): string;
547
+ getSymbolPreset(): SymbolPreset;
548
+
549
+ // Category accessors (return grouped symbol objects)
550
+ get status(): { success, error, warning, ... };
551
+ get nav(): { cursor, selected, expand, collapse, back };
552
+ get icon(): { model, folder, file, git, ... };
553
+ get boxRound(): { topLeft, topRight, ... };
554
+ get boxSharp(): { topLeft, topRight, ... };
555
+ get sep(): { powerline, dot, slash, pipe, ... };
556
+ get thinking(): { minimal, low, medium, high, xhigh };
557
+ get spinnerFrames(): string[];
558
+
559
+ // Language icon lookup
560
+ getLangIcon(lang: string | undefined): string;
514
561
  }
515
562
  ```
516
563
 
@@ -522,9 +569,13 @@ The active theme is available as a global singleton in `coding-agent`:
522
569
  // theme.ts
523
570
  export let theme: Theme;
524
571
 
525
- export function setTheme(name: string) {
526
- theme = loadTheme(name);
527
- }
572
+ export async function initTheme(
573
+ themeName?: string,
574
+ enableWatcher?: boolean,
575
+ symbolPreset?: SymbolPreset,
576
+ colorBlindMode?: boolean
577
+ ): Promise<void>;
578
+ export async function setTheme(name: string, enableWatcher?: boolean): Promise<{ success: boolean; error?: string }>;
528
579
 
529
580
  // Usage throughout coding-agent
530
581
  import { theme } from "./theme.js";
@@ -554,51 +605,46 @@ export interface MarkdownTheme {
554
605
  italic: (text: string) => string;
555
606
  strikethrough: (text: string) => string;
556
607
  underline: (text: string) => string;
608
+ highlightCode?: (code: string, lang?: string) => string[];
609
+ getMermaidImage?: (sourceHash: string) => MermaidImage | null;
610
+ symbols: SymbolTheme;
557
611
  }
558
612
  ```
559
613
 
560
- The `coding-agent` provides themed functions when creating components:
614
+ The `coding-agent` bridges the theme to TUI components via exported helpers:
561
615
 
562
616
  ```typescript
563
- // In coding-agent
564
- import { theme } from "./theme.js";
565
- import { Markdown } from "@oh-my-pi/pi-tui";
566
-
567
- // Helper to create markdown theme functions
568
- function getMarkdownTheme(): MarkdownTheme {
617
+ // Exported helper in theme.ts
618
+ export function getMarkdownTheme(): MarkdownTheme {
569
619
  return {
570
620
  heading: (text) => theme.fg("mdHeading", text),
571
621
  link: (text) => theme.fg("mdLink", text),
572
- linkUrl: (text) => theme.fg("mdLinkUrl", text),
573
- code: (text) => theme.fg("mdCode", text),
574
- codeBlock: (text) => theme.fg("mdCodeBlock", text),
575
- codeBlockBorder: (text) => theme.fg("mdCodeBlockBorder", text),
576
- quote: (text) => theme.fg("mdQuote", text),
577
- quoteBorder: (text) => theme.fg("mdQuoteBorder", text),
578
- hr: (text) => theme.fg("mdHr", text),
579
- listBullet: (text) => theme.fg("mdListBullet", text),
622
+ // ... all color mappings ...
580
623
  bold: (text) => theme.bold(text),
581
624
  italic: (text) => theme.italic(text),
582
625
  underline: (text) => theme.underline(text),
583
626
  strikethrough: (text) => chalk.strikethrough(text),
627
+ symbols: getSymbolTheme(),
628
+ getMermaidImage,
629
+ highlightCode: (code, lang) => {
630
+ /* uses native syntax highlighter */
631
+ },
584
632
  };
585
633
  }
586
-
587
- // Create markdown with theme
588
- const md = new Markdown(text, 1, 1, getMarkdownTheme(), { bgColor: theme.bg("userMessageBg") });
589
634
  ```
590
635
 
591
636
  This approach:
592
637
 
593
638
  - Keeps TUI components theme-agnostic (reusable in other projects)
594
639
  - Maintains type safety via interfaces
595
- - Allows components to have sensible defaults if no theme provided
596
640
  - Centralizes theme access in `coding-agent`
597
641
 
642
+ Similar helpers exist for other TUI components: `getSelectListTheme()`, `getEditorTheme()`, `getSettingsListTheme()`, `getSymbolTheme()`.
643
+
598
644
  **Example usage:**
599
645
 
600
646
  ```typescript
601
- const theme = loadTheme("dark");
647
+ await initTheme("dark");
602
648
 
603
649
  // Apply foreground colors
604
650
  theme.fg("accent", "Selected");
@@ -620,9 +666,9 @@ const userMsg = theme.bg("userMessageBg", theme.fg("userMessageText", "Hello"));
620
666
  **Color resolution:**
621
667
 
622
668
  1. **Detect terminal capabilities:**
623
- - Check `$COLORTERM` env var (`truecolor` or `24bit` → truecolor support)
624
- - Check `$TERM` env var (`*-256color` → 256-color support)
625
- - Fallback to 256-color mode if detection fails
669
+ - `COLORTERM=truecolor|24bit` or `WT_SESSION` → truecolor
670
+ - `TERM=dumb`, `TERM=linux`, or empty `TERM` → 256-color
671
+ - Otherwise truecolor
626
672
 
627
673
  2. **Load JSON theme file**
628
674
 
@@ -641,26 +687,9 @@ const userMsg = theme.bg("userMessageBg", theme.fg("userMessageText", "Hello"));
641
687
  ```
642
688
 
643
689
  4. **Convert colors to ANSI codes based on terminal capability:**
644
-
645
- **Truecolor mode (24-bit):**
646
- - Hex (`"#ff0000"`) `\x1b[38;2;255;0;0m`
647
- - 256-color (`42`) → `\x1b[38;5;42m` (keep as-is)
648
- - Empty string (`""`) → `\x1b[39m`
649
-
650
- **256-color mode:**
651
- - Hex (`"#ff0000"`) → convert to nearest RGB cube color → `\x1b[38;5;196m`
652
- - 256-color (`42`) → `\x1b[38;5;42m` (keep as-is)
653
- - Empty string (`""`) → `\x1b[39m`
654
-
655
- **Hex to 256-color conversion:**
656
-
657
- ```typescript
658
- // Convert RGB to 6x6x6 cube (colors 16-231)
659
- r_index = Math.round((r / 255) * 5);
660
- g_index = Math.round((g / 255) * 5);
661
- b_index = Math.round((b / 255) * 5);
662
- color_index = 16 + 36 * r_index + 6 * g_index + b_index;
663
- ```
690
+ - Empty string (`""`) → terminal default (foreground/background reset)
691
+ - 256-color (`42`) → `\x1b[38;5;42m` / `\x1b[48;5;42m`
692
+ - Hex or resolved vars → `Bun.color(value, "ansi-16m" | "ansi-256")`
664
693
 
665
694
  5. **Cache as `Theme` instance**
666
695
 
package/docs/tree.md CHANGED
@@ -20,10 +20,9 @@ Sessions are stored as trees where each entry has an `id` and `parentId`. The "l
20
20
  ```
21
21
  ├─ user: "Hello, can you help..."
22
22
  │ └─ assistant: "Of course! I can..."
23
- │ ├─ user: "Let's try approach A..."
24
- │ │ └─ assistant: "For approach A..."
25
- │ │ └─ [compaction: 12k tokens]
26
- │ │ └─ user: "That worked..." ← active
23
+ │ ├─ user: "Let's try approach A..."
24
+ │ │ └─ assistant: "For approach A..."
25
+ │ │ └─ [label-name] user: "That worked..."
27
26
  │ └─ user: "Actually, approach B..."
28
27
  │ └─ assistant: "For approach B..."
29
28
  ```
@@ -32,18 +31,25 @@ Sessions are stored as trees where each entry has an `id` and `parentId`. The "l
32
31
 
33
32
  | Key | Action |
34
33
  |-----|--------|
35
- | ↑/↓ | Navigate (depth-first order) |
34
+ | ↑/↓ | Move selection |
35
+ | ←/→ | Page up/down |
36
36
  | Enter | Select node |
37
- | Escape/Ctrl+C | Cancel |
38
- | Ctrl+U | Toggle: user messages only |
39
- | Ctrl+O | Toggle: show all (including custom/label entries) |
37
+ | Escape | Clear search (if active) or cancel |
38
+ | Ctrl+C | Cancel |
39
+ | Ctrl+O / Shift+Ctrl+O | Cycle filter forward/back |
40
+ | Alt+D/T/U/L/A | Set filter: default / no-tools / user-only / labeled-only / all |
41
+ | Shift+L | Edit label for selected entry |
42
+ | Type | Search (space-separated tokens) |
43
+ | Backspace | Remove last search character |
40
44
 
41
45
  ### Display
42
46
 
43
- - Height: half terminal height
44
- - Current leaf marked with `← active`
45
- - Labels shown inline: `[label-name]`
46
- - Default filter hides `label` and `custom` entries (shown in Ctrl+O mode)
47
+ - Tree list height: `max(5, floor(terminalHeight / 2))` lines
48
+ - Active path marked with a bullet (`•`) before each entry (current leaf is last node on the path)
49
+ - Labels shown inline: `[label-name]` before the entry text
50
+ - Default filter hides `label`, `custom`, `model_change`, and `thinking_level_change` entries
51
+ - Assistant messages with only tool calls are hidden unless they contain errors/aborts (current leaf is always shown)
52
+ - `no-tools` filter hides tool result messages
47
53
  - Children sorted by timestamp (oldest first)
48
54
 
49
55
  ## Selection Behavior
@@ -66,7 +72,11 @@ If user selects the very first message (has no parent):
66
72
 
67
73
  ## Branch Summarization
68
74
 
69
- When switching, user is prompted: "Summarize the branch you're leaving?"
75
+ If branch summaries are enabled (`branchSummary.enabled`), the user is prompted:
76
+
77
+ - No summary
78
+ - Summarize
79
+ - Summarize with custom prompt (passed as `customInstructions`)
70
80
 
71
81
  ### What Gets Summarized
72
82
 
@@ -79,9 +89,8 @@ A → B → C → D → E → F ← old leaf
79
89
 
80
90
  Abandoned path: D → E → F (summarized)
81
91
 
82
- Summarization stops at:
83
- 1. Common ancestor (always)
84
- 2. Compaction node (if encountered first)
92
+ Summarization stops at the common ancestor only.
93
+ Compaction and branch summary entries are included; tool results are ignored.
85
94
 
86
95
  ### Summary Storage
87
96
 
@@ -91,11 +100,12 @@ Stored as `BranchSummaryEntry`:
91
100
  interface BranchSummaryEntry {
92
101
  type: "branch_summary";
93
102
  id: string;
94
- parentId: string; // New leaf position
103
+ parentId: string | null; // New leaf position (null when navigating to root)
95
104
  timestamp: string;
96
- fromId: string; // Old leaf we abandoned
105
+ fromId: string; // Entry the summary is attached to ("root" if null)
97
106
  summary: string; // LLM-generated summary
98
107
  details?: unknown; // Optional hook data
108
+ fromExtension?: boolean;
99
109
  }
100
110
  ```
101
111
 
@@ -107,34 +117,33 @@ interface BranchSummaryEntry {
107
117
  async navigateTree(
108
118
  targetId: string,
109
119
  options?: { summarize?: boolean; customInstructions?: string }
110
- ): Promise<{ editorText?: string; cancelled: boolean }>
120
+ ): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean; summaryEntry?: BranchSummaryEntry }>
111
121
  ```
112
122
 
113
123
  Flow:
114
124
  1. Validate target, check no-op (target === current leaf)
115
125
  2. Find common ancestor between old leaf and target
116
- 3. Collect entries to summarize (if requested)
126
+ 3. Collect entries to summarize (if requested, includes compaction entries)
117
127
  4. Fire `session_before_tree` event (hook can cancel or provide summary)
118
- 5. Run default summarizer if needed
128
+ 5. Run default summarizer if needed (respects `customInstructions`)
119
129
  6. Switch leaf via `branch()` or `branchWithSummary()`
120
130
  7. Update agent: `agent.replaceMessages(sessionManager.buildSessionContext().messages)`
121
- 8. Fire `session_tree` event
122
- 9. Notify custom tools via session event
123
- 10. Return result with `editorText` if user message was selected
131
+ 8. Fire `session_tree` event (includes `summaryEntry`/`fromExtension` when applicable)
132
+ 9. Return result with `editorText` if user message was selected
124
133
 
125
134
  ### SessionManager
126
135
 
127
- - `getLeafUuid(): string | null` - Current leaf (null if empty)
136
+ - `getLeafId(): string | null` - Current leaf (null if empty)
128
137
  - `resetLeaf(): void` - Set leaf to null (for root user message navigation)
129
138
  - `getTree(): SessionTreeNode[]` - Full tree with children sorted by timestamp
130
139
  - `branch(id)` - Change leaf pointer
131
- - `branchWithSummary(id, summary)` - Change leaf and create summary entry
140
+ - `branchWithSummary(id: string | null, summary, details?, fromExtension?)` - Change leaf and create summary entry
132
141
 
133
142
  ### InteractiveMode
134
143
 
135
144
  `/tree` command shows `TreeSelectorComponent`, then:
136
- 1. Prompt for summarization
137
- 2. Call `session.navigateTree()`
145
+ 1. If `branchSummary.enabled`, prompt for summary type (including custom prompt)
146
+ 2. Call `session.navigateTree()` with `summarize`/`customInstructions`
138
147
  3. Clear and re-render chat
139
148
  4. Set editor text if applicable
140
149
 
@@ -154,7 +163,6 @@ interface TreePreparation {
154
163
  interface SessionBeforeTreeEvent {
155
164
  type: "session_before_tree";
156
165
  preparation: TreePreparation;
157
- model: Model;
158
166
  signal: AbortSignal;
159
167
  }
160
168
 
@@ -172,7 +180,7 @@ interface SessionTreeEvent {
172
180
  newLeafId: string | null;
173
181
  oldLeafId: string | null;
174
182
  summaryEntry?: BranchSummaryEntry;
175
- fromHook?: boolean;
183
+ fromExtension?: boolean;
176
184
  }
177
185
  ```
178
186
 
@@ -192,6 +200,7 @@ export default function(pi: HookAPI) {
192
200
 
193
201
  ## Error Handling
194
202
 
195
- - Summarization failure: cancels navigation, shows error
196
- - User abort (Escape): cancels navigation
197
- - Hook returns `cancel: true`: cancels navigation silently
203
+ - Summarization failure: navigation is cancelled and the caller shows the error
204
+ - Escape during summarization: returns `{ cancelled: true, aborted: true }` and the selector reopens
205
+ - Hook returns `cancel: true`: navigation is cancelled (caller decides UI)
206
+ - Escape in the tree selector clears search first, then cancels if empty