@oh-my-pi/pi-coding-agent 12.7.6 → 12.8.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 (56) hide show
  1. package/CHANGELOG.md +37 -37
  2. package/README.md +9 -1052
  3. package/package.json +7 -7
  4. package/src/cli/args.ts +1 -0
  5. package/src/cli/update-cli.ts +49 -35
  6. package/src/cli/web-search-cli.ts +3 -2
  7. package/src/commands/web-search.ts +1 -0
  8. package/src/config/model-registry.ts +6 -0
  9. package/src/config/settings-schema.ts +25 -3
  10. package/src/config/settings.ts +1 -0
  11. package/src/extensibility/extensions/wrapper.ts +20 -13
  12. package/src/extensibility/slash-commands.ts +12 -91
  13. package/src/lsp/client.ts +24 -27
  14. package/src/lsp/index.ts +92 -42
  15. package/src/mcp/config-writer.ts +33 -0
  16. package/src/mcp/config.ts +6 -1
  17. package/src/mcp/types.ts +1 -0
  18. package/src/modes/components/custom-editor.ts +8 -5
  19. package/src/modes/components/settings-defs.ts +2 -1
  20. package/src/modes/controllers/command-controller.ts +12 -6
  21. package/src/modes/controllers/input-controller.ts +21 -186
  22. package/src/modes/controllers/mcp-command-controller.ts +60 -3
  23. package/src/modes/interactive-mode.ts +2 -2
  24. package/src/modes/types.ts +1 -1
  25. package/src/sdk.ts +23 -1
  26. package/src/secrets/index.ts +116 -0
  27. package/src/secrets/obfuscator.ts +269 -0
  28. package/src/secrets/regex.ts +21 -0
  29. package/src/session/agent-session.ts +143 -21
  30. package/src/session/compaction/branch-summarization.ts +2 -2
  31. package/src/session/compaction/compaction.ts +10 -3
  32. package/src/session/compaction/utils.ts +25 -1
  33. package/src/slash-commands/builtin-registry.ts +419 -0
  34. package/src/web/scrapers/github.ts +50 -12
  35. package/src/web/search/index.ts +5 -5
  36. package/src/web/search/provider.ts +13 -2
  37. package/src/web/search/providers/brave.ts +165 -0
  38. package/src/web/search/types.ts +1 -1
  39. package/docs/compaction.md +0 -436
  40. package/docs/config-usage.md +0 -176
  41. package/docs/custom-tools.md +0 -585
  42. package/docs/environment-variables.md +0 -257
  43. package/docs/extension-loading.md +0 -106
  44. package/docs/extensions.md +0 -1342
  45. package/docs/fs-scan-cache-architecture.md +0 -50
  46. package/docs/hooks.md +0 -906
  47. package/docs/models.md +0 -234
  48. package/docs/python-repl.md +0 -110
  49. package/docs/rpc.md +0 -1173
  50. package/docs/sdk.md +0 -1039
  51. package/docs/session-tree-plan.md +0 -84
  52. package/docs/session.md +0 -368
  53. package/docs/skills.md +0 -254
  54. package/docs/theme.md +0 -696
  55. package/docs/tree.md +0 -206
  56. package/docs/tui.md +0 -487
package/docs/theme.md DELETED
@@ -1,696 +0,0 @@
1
- > omp can create themes. Ask it to build one for your use case.
2
-
3
- # OMP Coding Agent Themes
4
-
5
- Themes allow you to customize the colors used throughout the coding agent TUI.
6
-
7
- ## Color Tokens
8
-
9
- Every theme must define all color tokens. There are no optional colors.
10
-
11
- ### Core UI (11 colors)
12
-
13
- | Token | Purpose | Examples |
14
- | -------------- | --------------------- | ------------------------------------ |
15
- | `accent` | Primary accent color | Logo, selected items, cursor (›) |
16
- | `border` | Normal borders | Selector borders, horizontal lines |
17
- | `borderAccent` | Highlighted borders | Changelog borders, special panels |
18
- | `borderMuted` | Subtle borders | Editor borders, secondary separators |
19
- | `success` | Success states | Success messages, diff additions |
20
- | `error` | Error states | Error messages, diff deletions |
21
- | `warning` | Warning states | Warning messages |
22
- | `muted` | Secondary/dimmed text | Metadata, descriptions, output |
23
- | `dim` | Very dimmed text | Less important info, placeholders |
24
- | `text` | Default text color | Main content (usually `""`) |
25
- | `thinkingText` | Thinking block text | Assistant reasoning traces |
26
-
27
- ### Backgrounds & Content Text (11 colors)
28
-
29
- | Token | Purpose |
30
- | -------------------- | ----------------------------------------------------------------- |
31
- | `selectedBg` | Selected/active line background (e.g., tree selector) |
32
- | `userMessageBg` | User message background |
33
- | `userMessageText` | User message text color |
34
- | `customMessageBg` | Hook custom message background |
35
- | `customMessageText` | Hook custom message text color |
36
- | `customMessageLabel` | Hook custom message label/type text |
37
- | `toolPendingBg` | Tool execution box (pending state) |
38
- | `toolSuccessBg` | Tool execution box (success state) |
39
- | `toolErrorBg` | Tool execution box (error state) |
40
- | `toolTitle` | Tool execution title/heading (e.g., `$ command`, `read file.txt`) |
41
- | `toolOutput` | Tool execution output text |
42
-
43
- ### Markdown (10 colors)
44
-
45
- | Token | Purpose |
46
- | ------------------- | ----------------------------- |
47
- | `mdHeading` | Heading text (`#`, `##`, etc) |
48
- | `mdLink` | Link text |
49
- | `mdLinkUrl` | Link URL (in parentheses) |
50
- | `mdCode` | Inline code (backticks) |
51
- | `mdCodeBlock` | Code block content |
52
- | `mdCodeBlockBorder` | Code block fences (```) |
53
- | `mdQuote` | Blockquote text |
54
- | `mdQuoteBorder` | Blockquote border (`│`) |
55
- | `mdHr` | Horizontal rule (`---`) |
56
- | `mdListBullet` | List bullets/numbers |
57
-
58
- ### Tool Diffs (3 colors)
59
-
60
- | Token | Purpose |
61
- | ----------------- | --------------------------- |
62
- | `toolDiffAdded` | Added lines in tool diffs |
63
- | `toolDiffRemoved` | Removed lines in tool diffs |
64
- | `toolDiffContext` | Context lines in tool diffs |
65
-
66
- Note: Diff colors are specific to tool execution boxes and must work with tool background colors.
67
-
68
- ### Syntax Highlighting (9 colors)
69
-
70
- Used for native syntax highlighting in tool output and editors:
71
-
72
- | Token | Purpose |
73
- | ------------------- | -------------------------------- |
74
- | `syntaxComment` | Comments |
75
- | `syntaxKeyword` | Keywords (`if`, `function`, etc) |
76
- | `syntaxFunction` | Function names |
77
- | `syntaxVariable` | Variable names |
78
- | `syntaxString` | String literals |
79
- | `syntaxNumber` | Number literals |
80
- | `syntaxType` | Type names |
81
- | `syntaxOperator` | Operators (`+`, `-`, etc) |
82
- | `syntaxPunctuation` | Punctuation (`;`, `,`, etc) |
83
-
84
- ### Thinking Level Borders (6 colors)
85
-
86
- Editor border colors that indicate the current thinking/reasoning level:
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) |
96
-
97
- These create a visual hierarchy: off → minimal → low → medium → high → xhigh
98
-
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)
126
-
127
- ### HTML Export Colors (optional)
128
-
129
- The `export` section is optional and controls colors used when exporting sessions to HTML via `/export`. If not specified, these colors are automatically derived from `userMessageBg` based on luminance detection.
130
-
131
- | Token | Purpose |
132
- | -------- | ------------------------------------------------------------- |
133
- | `pageBg` | Page background color |
134
- | `cardBg` | Card/container background (headers, stats boxes) |
135
- | `infoBg` | Info sections background (system prompt, notices, compaction) |
136
-
137
- Example:
138
-
139
- ```json
140
- {
141
- "export": {
142
- "pageBg": "#18181e",
143
- "cardBg": "#1e1e24",
144
- "infoBg": "#3c3728"
145
- }
146
- }
147
- ```
148
-
149
- ## Theme Format
150
-
151
- Themes are defined in JSON files with the following structure:
152
-
153
- ```json
154
- {
155
- "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json",
156
- "name": "my-theme",
157
- "vars": {
158
- "blue": "#0066cc",
159
- "gray": 242,
160
- "brightCyan": 51
161
- },
162
- "colors": {
163
- "accent": "blue",
164
- "muted": "gray",
165
- "thinkingText": "gray",
166
- "text": "",
167
- ...
168
- }
169
- }
170
- ```
171
-
172
- ## Symbols
173
-
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.
175
-
176
- Example:
177
-
178
- ```json
179
- {
180
- "symbols": {
181
- "preset": "ascii",
182
- "overrides": {
183
- "icon.model": "[M]",
184
- "sep.powerlineLeft": ">",
185
- "sep.powerlineRight": "<"
186
- }
187
- }
188
- }
189
- ```
190
-
191
- Symbol keys by category:
192
-
193
- - Status: `status.success`, `status.error`, `status.warning`, `status.info`, `status.pending`, `status.disabled`, `status.enabled`, `status.running`, `status.shadowed`, `status.aborted`
194
- - Navigation: `nav.cursor`, `nav.selected`, `nav.expand`, `nav.collapse`, `nav.back`
195
- - Tree: `tree.branch`, `tree.last`, `tree.vertical`, `tree.horizontal`, `tree.hook`
196
- - Boxes (rounded): `boxRound.topLeft`, `boxRound.topRight`, `boxRound.bottomLeft`, `boxRound.bottomRight`, `boxRound.horizontal`, `boxRound.vertical`
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`
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`
200
- - Thinking: `thinking.minimal`, `thinking.low`, `thinking.medium`, `thinking.high`, `thinking.xhigh`
201
- - Checkboxes: `checkbox.checked`, `checkbox.unchecked`
202
- - Formatting: `format.bullet`, `format.dash`, `format.bracketLeft`, `format.bracketRight`
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`
206
-
207
- ### Color Values
208
-
209
- Four formats are supported:
210
-
211
- 1. **Hex colors**: `"#ff0000"` (6-digit hex RGB)
212
- 2. **256-color palette**: `39` (number 0-255, xterm 256-color palette)
213
- 3. **Color references**: `"blue"` (must be defined in `vars`)
214
- 4. **Terminal default**: `""` (empty string, uses terminal's default color)
215
-
216
- ### The `vars` Section
217
-
218
- The optional `vars` section allows you to define reusable colors:
219
-
220
- ```json
221
- {
222
- "vars": {
223
- "nord0": "#2E3440",
224
- "nord1": "#3B4252",
225
- "nord8": "#88C0D0",
226
- "brightBlue": 39
227
- },
228
- "colors": {
229
- "accent": "nord8",
230
- "muted": "nord1",
231
- "mdLink": "brightBlue"
232
- }
233
- }
234
- ```
235
-
236
- Benefits:
237
-
238
- - Reuse colors across multiple tokens
239
- - Easier to maintain theme consistency
240
- - Can reference standard color palettes
241
-
242
- Variables can be hex colors (`"#ff0000"`), 256-color indices (`42`), or references to other variables.
243
-
244
- ### Terminal Default (empty string)
245
-
246
- Use `""` (empty string) to inherit the terminal's default foreground/background color:
247
-
248
- ```json
249
- {
250
- "colors": {
251
- "text": "" // Uses terminal's default text color
252
- }
253
- }
254
- ```
255
-
256
- This is useful for:
257
-
258
- - Main text color (adapts to user's terminal theme)
259
- - Creating themes that blend with terminal appearance
260
-
261
- ## Built-in Themes
262
-
263
- OMP ships with `dark` (default), `light`, and 90+ curated themes under `src/modes/theme/defaults/`. Examples include:
264
-
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`
268
-
269
- ## Selecting a Theme
270
-
271
- Themes are configured in the Settings UI (Display → Theme) or via the config CLI:
272
-
273
- ```bash
274
- omp config set theme dark
275
- ```
276
-
277
- On first run, OMP uses the terminal background reported by `COLORFGBG` and falls back to `dark` if unavailable.
278
-
279
- ## Custom Themes
280
-
281
- ### Theme Locations
282
-
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.
284
-
285
- ### Creating a Custom Theme
286
-
287
- 1. **Create theme directory:**
288
-
289
- ```bash
290
- mkdir -p "${PI_CODING_AGENT_DIR:-~/.omp/agent}/themes"
291
- ```
292
-
293
- 2. **Create theme file:**
294
-
295
- ```bash
296
- vim "${PI_CODING_AGENT_DIR:-~/.omp/agent}/themes/my-theme.json"
297
- ```
298
-
299
- 3. **Define all colors (see the schema for the full list; snippet below shows structure):**
300
-
301
- ```json
302
- {
303
- "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json",
304
- "name": "my-theme",
305
- "vars": {
306
- "primary": "#00aaff",
307
- "secondary": 242,
308
- "brightGreen": 46
309
- },
310
- "colors": {
311
- "accent": "primary",
312
- "border": "primary",
313
- "borderAccent": "#00ffff",
314
- "borderMuted": "secondary",
315
- "success": "brightGreen",
316
- "error": "#ff0000",
317
- "warning": "#ffff00",
318
- "muted": "secondary",
319
- "text": "",
320
-
321
- "userMessageBg": "#2d2d30",
322
- "userMessageText": "",
323
- "toolPendingBg": "#1e1e2e",
324
- "toolSuccessBg": "#1e2e1e",
325
- "toolErrorBg": "#2e1e1e",
326
- "toolTitle": "",
327
- "toolOutput": "",
328
- // ...
329
-
330
- "mdHeading": "#ffaa00",
331
- "mdLink": "primary",
332
- "mdCode": "#00ffff",
333
- "mdCodeBlock": "#00ff00",
334
- "mdCodeBlockBorder": "secondary",
335
- "mdQuote": "secondary",
336
- "mdQuoteBorder": "secondary",
337
- "mdHr": "secondary",
338
- "mdListBullet": "#00ffff",
339
-
340
- "toolDiffAdded": "#00ff00",
341
- "toolDiffRemoved": "#ff0000",
342
- "toolDiffContext": "secondary",
343
-
344
- "syntaxComment": "secondary",
345
- "syntaxKeyword": "primary",
346
- "syntaxFunction": "#00aaff",
347
- "syntaxVariable": "#ffaa00",
348
- "syntaxString": "#00ff00",
349
- "syntaxNumber": "#ff00ff",
350
- "syntaxType": "#00aaff",
351
- "syntaxOperator": "primary",
352
- "syntaxPunctuation": "secondary",
353
-
354
- "thinkingOff": "secondary",
355
- "thinkingMinimal": "primary",
356
- "thinkingLow": "#00aaff",
357
- "thinkingMedium": "#00ffff",
358
- "thinkingHigh": "#ff00ff",
359
- "thinkingXhigh": "#ff88ff"
360
- // ... plus bashMode, pythonMode, statusLine* colors
361
- }
362
- }
363
- ```
364
-
365
- 4. **Select your theme:**
366
- - Use the Settings UI (Display → Theme)
367
- - Or run `omp config set theme my-theme`
368
-
369
- ## Tips
370
-
371
- ### Light vs Dark Themes
372
-
373
- **For dark terminals:**
374
-
375
- - Use bright, saturated colors
376
- - Higher contrast
377
- - Example: `#00ffff` (bright cyan)
378
-
379
- **For light terminals:**
380
-
381
- - Use darker, muted colors
382
- - Lower contrast to avoid eye strain
383
- - Example: `#008888` (dark cyan)
384
-
385
- ### Color Harmony
386
-
387
- - Start with a base palette (e.g., Nord, Gruvbox, Tokyo Night)
388
- - Define your palette in `vars`
389
- - Reference colors consistently
390
-
391
- ### Testing
392
-
393
- Test your theme with:
394
-
395
- - Different message types (user, assistant, errors)
396
- - Tool executions (success and error states)
397
- - Markdown content (headings, code, lists, etc)
398
- - Long text that wraps
399
-
400
- ## Color Format Reference
401
-
402
- ### Hex Colors
403
-
404
- Standard 6-digit hex format:
405
-
406
- - `"#ff0000"` - Red
407
- - `"#00ff00"` - Green
408
- - `"#0000ff"` - Blue
409
- - `"#808080"` - Gray
410
- - `"#ffffff"` - White
411
- - `"#000000"` - Black
412
-
413
- RGB values: `#RRGGBB` where each component is `00-ff` (0-255)
414
-
415
- ### 256-Color Palette
416
-
417
- Use numeric indices (0-255) to reference the xterm 256-color palette:
418
-
419
- **Colors 0-15:** Basic ANSI colors (terminal-dependent, may be themed)
420
-
421
- - `0` - Black
422
- - `1` - Red
423
- - `2` - Green
424
- - `3` - Yellow
425
- - `4` - Blue
426
- - `5` - Magenta
427
- - `6` - Cyan
428
- - `7` - White
429
- - `8-15` - Bright variants
430
-
431
- **Colors 16-231:** 6×6×6 RGB cube (standardized)
432
-
433
- - Formula: `16 + 36×R + 6×G + B` where R, G, B are 0-5
434
- - Example: `39` = bright cyan, `196` = bright red
435
-
436
- **Colors 232-255:** Grayscale ramp (standardized)
437
-
438
- - `232` - Darkest gray
439
- - `255` - Near white
440
-
441
- Example usage:
442
-
443
- ```json
444
- {
445
- "vars": {
446
- "gray": 242,
447
- "brightCyan": 51,
448
- "darkBlue": 18
449
- },
450
- "colors": {
451
- "muted": "gray",
452
- "accent": "brightCyan"
453
- }
454
- }
455
- ```
456
-
457
- **Benefits:**
458
-
459
- - Works everywhere (`TERM=xterm-256color`)
460
- - No truecolor detection needed
461
- - Standardized RGB cube (16-231) looks the same on all terminals
462
-
463
- ### Terminal Compatibility
464
-
465
- OMP prefers 24-bit RGB colors (`\x1b[38;2;R;G;Bm`) and assumes truecolor on modern terminals.
466
-
467
- Color mode detection:
468
-
469
- - `COLORTERM=truecolor|24bit` or `WT_SESSION` → truecolor
470
- - `TERM=dumb`, `TERM=linux`, or empty `TERM` → 256-color fallback
471
- - Otherwise → truecolor
472
-
473
- If you need to confirm terminal hints:
474
-
475
- ```bash
476
- echo $COLORTERM
477
- ```
478
-
479
- ## Example Themes
480
-
481
- See the built-in themes for complete examples:
482
-
483
- - [Dark theme](../src/modes/theme/dark.json)
484
- - [Light theme](../src/modes/theme/light.json)
485
- - [Defaults library](../src/modes/theme/defaults)
486
-
487
- ## Schema Validation
488
-
489
- Themes are validated on load using [TypeBox](https://github.com/sinclairzx81/typebox) and the TypeBox compiler.
490
-
491
- Invalid themes will show an error with details about what's wrong:
492
-
493
- ```
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
502
- ```
503
-
504
- For editor support, the JSON schema is available at:
505
-
506
- ```
507
- https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json
508
- ```
509
-
510
- Add to your theme file for auto-completion and validation:
511
-
512
- ```json
513
- {
514
- "$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/modes/theme/theme-schema.json",
515
- ...
516
- }
517
- ```
518
-
519
- ## Implementation
520
-
521
- ### Theme Class
522
-
523
- Themes are loaded and converted to a `Theme` class that provides type-safe color methods:
524
-
525
- ```typescript
526
- class Theme {
527
- // Apply foreground color
528
- fg(color: ThemeColor, text: string): string;
529
-
530
- // Apply background color
531
- bg(color: ThemeBg, text: string): string;
532
-
533
- // Text attributes (preserve current colors)
534
- bold(text: string): string;
535
- italic(text: string): string;
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;
561
- }
562
- ```
563
-
564
- ### Global Theme Instance
565
-
566
- The active theme is available as a global singleton in `coding-agent`:
567
-
568
- ```typescript
569
- // theme.ts
570
- export let theme: Theme;
571
-
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 }>;
579
-
580
- // Usage throughout coding-agent
581
- import { theme } from "./theme.js";
582
-
583
- theme.fg("accent", "Selected");
584
- theme.bg("userMessageBg", content);
585
- ```
586
-
587
- ### TUI Component Theming
588
-
589
- TUI components (like `Markdown`, `SelectList`, `Editor`) are in the `@oh-my-pi/pi-tui` package and don't have direct access to the theme. Instead, they define interfaces for the colors they need:
590
-
591
- ```typescript
592
- // In @oh-my-pi/pi-tui
593
- export interface MarkdownTheme {
594
- heading: (text: string) => string;
595
- link: (text: string) => string;
596
- linkUrl: (text: string) => string;
597
- code: (text: string) => string;
598
- codeBlock: (text: string) => string;
599
- codeBlockBorder: (text: string) => string;
600
- quote: (text: string) => string;
601
- quoteBorder: (text: string) => string;
602
- hr: (text: string) => string;
603
- listBullet: (text: string) => string;
604
- bold: (text: string) => string;
605
- italic: (text: string) => string;
606
- strikethrough: (text: string) => string;
607
- underline: (text: string) => string;
608
- highlightCode?: (code: string, lang?: string) => string[];
609
- getMermaidImage?: (sourceHash: string) => MermaidImage | null;
610
- symbols: SymbolTheme;
611
- }
612
- ```
613
-
614
- The `coding-agent` bridges the theme to TUI components via exported helpers:
615
-
616
- ```typescript
617
- // Exported helper in theme.ts
618
- export function getMarkdownTheme(): MarkdownTheme {
619
- return {
620
- heading: (text) => theme.fg("mdHeading", text),
621
- link: (text) => theme.fg("mdLink", text),
622
- // ... all color mappings ...
623
- bold: (text) => theme.bold(text),
624
- italic: (text) => theme.italic(text),
625
- underline: (text) => theme.underline(text),
626
- strikethrough: (text) => chalk.strikethrough(text),
627
- symbols: getSymbolTheme(),
628
- getMermaidImage,
629
- highlightCode: (code, lang) => {
630
- /* uses native syntax highlighter */
631
- },
632
- };
633
- }
634
- ```
635
-
636
- This approach:
637
-
638
- - Keeps TUI components theme-agnostic (reusable in other projects)
639
- - Maintains type safety via interfaces
640
- - Centralizes theme access in `coding-agent`
641
-
642
- Similar helpers exist for other TUI components: `getSelectListTheme()`, `getEditorTheme()`, `getSettingsListTheme()`, `getSymbolTheme()`.
643
-
644
- **Example usage:**
645
-
646
- ```typescript
647
- await initTheme("dark");
648
-
649
- // Apply foreground colors
650
- theme.fg("accent", "Selected");
651
- theme.fg("success", "✓ Done");
652
- theme.fg("error", "Failed");
653
-
654
- // Apply background colors
655
- theme.bg("userMessageBg", content);
656
- theme.bg("toolSuccessBg", output);
657
-
658
- // Combine styles
659
- theme.bold(theme.fg("accent", "Title"));
660
- theme.italic(theme.fg("muted", "metadata"));
661
-
662
- // Nested foreground + background
663
- const userMsg = theme.bg("userMessageBg", theme.fg("userMessageText", "Hello"));
664
- ```
665
-
666
- **Color resolution:**
667
-
668
- 1. **Detect terminal capabilities:**
669
- - `COLORTERM=truecolor|24bit` or `WT_SESSION` → truecolor
670
- - `TERM=dumb`, `TERM=linux`, or empty `TERM` → 256-color
671
- - Otherwise → truecolor
672
-
673
- 2. **Load JSON theme file**
674
-
675
- 3. **Resolve `vars` references recursively:**
676
-
677
- ```json
678
- {
679
- "vars": {
680
- "primary": "#0066cc",
681
- "accent": "primary"
682
- },
683
- "colors": {
684
- "accent": "accent" // → "primary" → "#0066cc"
685
- }
686
- }
687
- ```
688
-
689
- 4. **Convert colors to ANSI codes based on terminal capability:**
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")`
693
-
694
- 5. **Cache as `Theme` instance**
695
-
696
- This ensures themes work correctly regardless of terminal capabilities, with graceful degradation from truecolor to 256-color.