@oh-my-pi/pi-coding-agent 8.1.0 → 8.2.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 (402) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/docs/session.md +111 -46
  3. package/examples/custom-tools/hello/index.ts +1 -1
  4. package/examples/custom-tools/todo/index.ts +3 -4
  5. package/examples/extensions/api-demo.ts +0 -1
  6. package/examples/extensions/chalk-logger.ts +2 -3
  7. package/examples/extensions/hello.ts +0 -1
  8. package/examples/extensions/pirate.ts +0 -1
  9. package/examples/extensions/plan-mode.ts +15 -16
  10. package/examples/extensions/todo.ts +3 -4
  11. package/examples/extensions/tools.ts +1 -2
  12. package/examples/extensions/with-deps/index.ts +0 -1
  13. package/examples/hooks/auto-commit-on-exit.ts +1 -2
  14. package/examples/hooks/confirm-destructive.ts +0 -1
  15. package/examples/hooks/custom-compaction.ts +1 -2
  16. package/examples/hooks/dirty-repo-guard.ts +0 -1
  17. package/examples/hooks/file-trigger.ts +3 -4
  18. package/examples/hooks/git-checkpoint.ts +0 -1
  19. package/examples/hooks/handoff.ts +3 -4
  20. package/examples/hooks/permission-gate.ts +1 -2
  21. package/examples/hooks/protected-paths.ts +1 -2
  22. package/examples/hooks/qna.ts +2 -3
  23. package/examples/hooks/snake.ts +4 -5
  24. package/examples/hooks/status-line.ts +0 -1
  25. package/examples/sdk/01-minimal.ts +2 -3
  26. package/examples/sdk/02-custom-model.ts +2 -3
  27. package/examples/sdk/03-custom-prompt.ts +3 -4
  28. package/examples/sdk/04-skills.ts +2 -3
  29. package/examples/sdk/06-extensions.ts +1 -2
  30. package/examples/sdk/06-hooks.ts +6 -7
  31. package/examples/sdk/07-context-files.ts +0 -1
  32. package/examples/sdk/08-prompt-templates.ts +0 -1
  33. package/examples/sdk/08-slash-commands.ts +0 -1
  34. package/examples/sdk/09-api-keys-and-oauth.ts +0 -1
  35. package/examples/sdk/10-settings.ts +0 -1
  36. package/examples/sdk/11-sessions.ts +0 -1
  37. package/package.json +51 -23
  38. package/scripts/format-prompts.ts +0 -1
  39. package/src/capability/context-file.ts +2 -3
  40. package/src/capability/extension-module.ts +2 -3
  41. package/src/capability/extension.ts +2 -3
  42. package/src/capability/fs.ts +20 -21
  43. package/src/capability/hook.ts +2 -3
  44. package/src/capability/index.ts +15 -16
  45. package/src/capability/instruction.ts +2 -3
  46. package/src/capability/mcp.ts +2 -3
  47. package/src/capability/prompt.ts +2 -3
  48. package/src/capability/rule.ts +2 -3
  49. package/src/capability/settings.ts +1 -2
  50. package/src/capability/skill.ts +2 -3
  51. package/src/capability/slash-command.ts +2 -3
  52. package/src/capability/ssh.ts +2 -3
  53. package/src/capability/system-prompt.ts +2 -3
  54. package/src/capability/tool.ts +2 -3
  55. package/src/cli/args.ts +5 -6
  56. package/src/cli/config-cli.ts +6 -7
  57. package/src/cli/file-processor.ts +19 -17
  58. package/src/cli/jupyter-cli.ts +105 -0
  59. package/src/cli/list-models.ts +10 -11
  60. package/src/cli/plugin-cli.ts +20 -21
  61. package/src/cli/session-picker.ts +2 -3
  62. package/src/cli/setup-cli.ts +2 -3
  63. package/src/cli/stats-cli.ts +2 -3
  64. package/src/cli/update-cli.ts +25 -22
  65. package/src/commit/agentic/agent.ts +21 -23
  66. package/src/commit/agentic/fallback.ts +9 -9
  67. package/src/commit/agentic/index.ts +30 -38
  68. package/src/commit/agentic/state.ts +1 -6
  69. package/src/commit/agentic/tools/analyze-file.ts +15 -15
  70. package/src/commit/agentic/tools/git-file-diff.ts +3 -3
  71. package/src/commit/agentic/tools/git-hunk.ts +7 -7
  72. package/src/commit/agentic/tools/git-overview.ts +5 -5
  73. package/src/commit/agentic/tools/index.ts +14 -14
  74. package/src/commit/agentic/tools/propose-changelog.ts +6 -6
  75. package/src/commit/agentic/tools/propose-commit.ts +8 -8
  76. package/src/commit/agentic/tools/recent-commits.ts +2 -2
  77. package/src/commit/agentic/tools/split-commit.ts +19 -23
  78. package/src/commit/agentic/topo-sort.ts +1 -1
  79. package/src/commit/agentic/trivial.ts +3 -3
  80. package/src/commit/agentic/validation.ts +12 -12
  81. package/src/commit/analysis/conventional.ts +7 -11
  82. package/src/commit/analysis/index.ts +4 -4
  83. package/src/commit/analysis/scope.ts +4 -4
  84. package/src/commit/analysis/summary.ts +7 -9
  85. package/src/commit/analysis/validation.ts +1 -1
  86. package/src/commit/changelog/detect.ts +6 -6
  87. package/src/commit/changelog/generate.ts +7 -9
  88. package/src/commit/changelog/index.ts +13 -13
  89. package/src/commit/changelog/parse.ts +2 -2
  90. package/src/commit/cli.ts +1 -1
  91. package/src/commit/git/diff.ts +3 -3
  92. package/src/commit/git/index.ts +19 -24
  93. package/src/commit/index.ts +1 -1
  94. package/src/commit/map-reduce/index.ts +9 -9
  95. package/src/commit/map-reduce/map-phase.ts +19 -34
  96. package/src/commit/map-reduce/reduce-phase.ts +9 -11
  97. package/src/commit/message.ts +2 -2
  98. package/src/commit/model-selection.ts +3 -7
  99. package/src/commit/pipeline.ts +20 -22
  100. package/src/commit/utils/exclusions.ts +3 -3
  101. package/src/config/file-lock.ts +17 -7
  102. package/src/config/keybindings.ts +6 -8
  103. package/src/config/model-registry.ts +55 -37
  104. package/src/config/model-resolver.ts +18 -19
  105. package/src/config/prompt-templates.ts +11 -11
  106. package/src/config/settings-manager.ts +50 -34
  107. package/src/config.ts +60 -62
  108. package/src/cursor.ts +11 -9
  109. package/src/discovery/agents-md.ts +11 -12
  110. package/src/discovery/builtin.ts +68 -73
  111. package/src/discovery/claude.ts +41 -42
  112. package/src/discovery/cline.ts +11 -12
  113. package/src/discovery/codex.ts +52 -53
  114. package/src/discovery/cursor.ts +9 -10
  115. package/src/discovery/gemini.ts +17 -22
  116. package/src/discovery/github.ts +13 -14
  117. package/src/discovery/helpers.ts +35 -34
  118. package/src/discovery/index.ts +16 -18
  119. package/src/discovery/mcp-json.ts +8 -9
  120. package/src/discovery/ssh.ts +8 -9
  121. package/src/discovery/vscode.ts +4 -5
  122. package/src/discovery/windsurf.ts +6 -7
  123. package/src/exa/company.ts +1 -2
  124. package/src/exa/index.ts +2 -3
  125. package/src/exa/linkedin.ts +1 -2
  126. package/src/exa/mcp-client.ts +14 -16
  127. package/src/exa/render.ts +10 -11
  128. package/src/exa/researcher.ts +1 -2
  129. package/src/exa/search.ts +1 -2
  130. package/src/exa/types.ts +0 -1
  131. package/src/exa/websets.ts +1 -2
  132. package/src/exec/bash-executor.ts +3 -4
  133. package/src/exec/exec.ts +0 -1
  134. package/src/export/custom-share.ts +5 -6
  135. package/src/export/html/index.ts +24 -21
  136. package/src/export/ttsr.ts +2 -3
  137. package/src/extensibility/custom-commands/bundled/review/index.ts +7 -8
  138. package/src/extensibility/custom-commands/loader.ts +17 -14
  139. package/src/extensibility/custom-commands/types.ts +1 -2
  140. package/src/extensibility/custom-tools/loader.ts +10 -11
  141. package/src/extensibility/custom-tools/types.ts +6 -7
  142. package/src/extensibility/custom-tools/wrapper.ts +2 -3
  143. package/src/extensibility/extensions/loader.ts +75 -53
  144. package/src/extensibility/extensions/runner.ts +11 -12
  145. package/src/extensibility/extensions/types.ts +19 -26
  146. package/src/extensibility/extensions/wrapper.ts +3 -4
  147. package/src/extensibility/hooks/index.ts +1 -1
  148. package/src/extensibility/hooks/loader.ts +8 -9
  149. package/src/extensibility/hooks/runner.ts +7 -8
  150. package/src/extensibility/hooks/tool-wrapper.ts +0 -1
  151. package/src/extensibility/hooks/types.ts +10 -17
  152. package/src/extensibility/plugins/doctor.ts +3 -3
  153. package/src/extensibility/plugins/installer.ts +27 -27
  154. package/src/extensibility/plugins/loader.ts +59 -56
  155. package/src/extensibility/plugins/manager.ts +211 -171
  156. package/src/extensibility/plugins/parser.ts +1 -1
  157. package/src/extensibility/plugins/paths.ts +8 -8
  158. package/src/extensibility/skills.ts +63 -60
  159. package/src/extensibility/slash-commands.ts +10 -10
  160. package/src/index.ts +46 -46
  161. package/src/internal-urls/agent-protocol.ts +21 -11
  162. package/src/internal-urls/artifact-protocol.ts +17 -13
  163. package/src/internal-urls/router.ts +1 -2
  164. package/src/internal-urls/rule-protocol.ts +3 -4
  165. package/src/internal-urls/skill-protocol.ts +3 -4
  166. package/src/ipy/executor.ts +14 -10
  167. package/src/ipy/gateway-coordinator.ts +79 -90
  168. package/src/ipy/kernel.ts +32 -30
  169. package/src/ipy/modules.ts +13 -13
  170. package/src/lsp/client.ts +21 -10
  171. package/src/lsp/clients/biome-client.ts +1 -2
  172. package/src/lsp/clients/index.ts +3 -3
  173. package/src/lsp/clients/lsp-linter-client.ts +4 -5
  174. package/src/lsp/config.ts +15 -15
  175. package/src/lsp/edits.ts +4 -5
  176. package/src/lsp/index.ts +43 -44
  177. package/src/lsp/lspmux.ts +8 -8
  178. package/src/lsp/render.ts +10 -16
  179. package/src/lsp/utils.ts +3 -3
  180. package/src/main.ts +55 -34
  181. package/src/mcp/client.ts +2 -3
  182. package/src/mcp/config.ts +5 -6
  183. package/src/mcp/json-rpc.ts +0 -1
  184. package/src/mcp/loader.ts +3 -4
  185. package/src/mcp/manager.ts +17 -18
  186. package/src/mcp/tool-bridge.ts +4 -9
  187. package/src/mcp/tool-cache.ts +2 -3
  188. package/src/mcp/transports/http.ts +2 -4
  189. package/src/mcp/transports/stdio.ts +1 -2
  190. package/src/migrations.ts +60 -49
  191. package/src/modes/components/armin.ts +4 -5
  192. package/src/modes/components/assistant-message.ts +6 -6
  193. package/src/modes/components/bash-execution.ts +7 -8
  194. package/src/modes/components/bordered-loader.ts +3 -3
  195. package/src/modes/components/branch-summary-message.ts +3 -3
  196. package/src/modes/components/compaction-summary-message.ts +3 -3
  197. package/src/modes/components/countdown-timer.ts +0 -1
  198. package/src/modes/components/custom-message.ts +5 -5
  199. package/src/modes/components/diff.ts +1 -1
  200. package/src/modes/components/dynamic-border.ts +2 -2
  201. package/src/modes/components/extensions/extension-dashboard.ts +6 -7
  202. package/src/modes/components/extensions/extension-list.ts +2 -3
  203. package/src/modes/components/extensions/inspector-panel.ts +3 -4
  204. package/src/modes/components/extensions/state-manager.ts +25 -26
  205. package/src/modes/components/extensions/types.ts +1 -2
  206. package/src/modes/components/footer.ts +47 -43
  207. package/src/modes/components/history-search.ts +2 -2
  208. package/src/modes/components/hook-editor.ts +3 -4
  209. package/src/modes/components/hook-input.ts +2 -3
  210. package/src/modes/components/hook-message.ts +5 -5
  211. package/src/modes/components/hook-selector.ts +2 -3
  212. package/src/modes/components/keybinding-hints.ts +2 -3
  213. package/src/modes/components/login-dialog.ts +2 -2
  214. package/src/modes/components/model-selector.ts +12 -12
  215. package/src/modes/components/oauth-selector.ts +2 -2
  216. package/src/modes/components/plugin-settings.ts +20 -20
  217. package/src/modes/components/python-execution.ts +7 -8
  218. package/src/modes/components/queue-mode-selector.ts +3 -3
  219. package/src/modes/components/read-tool-group.ts +2 -2
  220. package/src/modes/components/session-selector.ts +4 -4
  221. package/src/modes/components/settings-defs.ts +77 -69
  222. package/src/modes/components/settings-selector.ts +16 -16
  223. package/src/modes/components/show-images-selector.ts +2 -2
  224. package/src/modes/components/status-line/segments.ts +4 -4
  225. package/src/modes/components/status-line/separators.ts +1 -1
  226. package/src/modes/components/status-line/types.ts +2 -2
  227. package/src/modes/components/status-line-segment-editor.ts +7 -8
  228. package/src/modes/components/status-line.ts +12 -12
  229. package/src/modes/components/theme-selector.ts +8 -7
  230. package/src/modes/components/thinking-selector.ts +4 -4
  231. package/src/modes/components/todo-display.ts +2 -2
  232. package/src/modes/components/todo-reminder.ts +4 -4
  233. package/src/modes/components/tool-execution.ts +11 -16
  234. package/src/modes/components/tree-selector.ts +11 -11
  235. package/src/modes/components/ttsr-notification.ts +5 -5
  236. package/src/modes/components/user-message-selector.ts +1 -1
  237. package/src/modes/components/user-message.ts +1 -1
  238. package/src/modes/components/visual-truncate.ts +0 -1
  239. package/src/modes/components/welcome.ts +4 -4
  240. package/src/modes/controllers/command-controller.ts +46 -47
  241. package/src/modes/controllers/event-controller.ts +16 -20
  242. package/src/modes/controllers/extension-ui-controller.ts +40 -46
  243. package/src/modes/controllers/input-controller.ts +17 -18
  244. package/src/modes/controllers/selector-controller.ts +103 -91
  245. package/src/modes/index.ts +3 -3
  246. package/src/modes/interactive-mode.ts +27 -29
  247. package/src/modes/print-mode.ts +12 -13
  248. package/src/modes/rpc/rpc-client.ts +7 -8
  249. package/src/modes/rpc/rpc-mode.ts +24 -25
  250. package/src/modes/rpc/rpc-types.ts +3 -4
  251. package/src/modes/theme/mermaid-cache.ts +2 -2
  252. package/src/modes/theme/theme.ts +128 -53
  253. package/src/modes/types.ts +10 -10
  254. package/src/modes/utils/ui-helpers.ts +17 -17
  255. package/src/patch/applicator.ts +18 -19
  256. package/src/patch/diff.ts +1 -2
  257. package/src/patch/fuzzy.ts +1 -2
  258. package/src/patch/index.ts +10 -11
  259. package/src/patch/normalize.ts +4 -4
  260. package/src/patch/normative.ts +1 -2
  261. package/src/patch/parser.ts +8 -9
  262. package/src/patch/shared.ts +12 -13
  263. package/src/sdk.ts +60 -63
  264. package/src/session/agent-session.ts +83 -84
  265. package/src/session/agent-storage.ts +11 -11
  266. package/src/session/artifacts.ts +8 -9
  267. package/src/session/auth-storage.ts +25 -29
  268. package/src/session/compaction/branch-summarization.ts +7 -10
  269. package/src/session/compaction/compaction.ts +8 -19
  270. package/src/session/compaction/utils.ts +6 -9
  271. package/src/session/history-storage.ts +10 -10
  272. package/src/session/messages.ts +4 -5
  273. package/src/session/session-manager.ts +76 -65
  274. package/src/session/session-storage.ts +57 -69
  275. package/src/session/storage-migration.ts +2 -3
  276. package/src/session/streaming-output.ts +2 -2
  277. package/src/ssh/connection-manager.ts +43 -50
  278. package/src/ssh/ssh-executor.ts +2 -2
  279. package/src/ssh/sshfs-mount.ts +11 -18
  280. package/src/system-prompt.ts +27 -34
  281. package/src/task/agents.ts +45 -30
  282. package/src/task/commands.ts +6 -7
  283. package/src/task/discovery.ts +39 -76
  284. package/src/task/executor.ts +14 -15
  285. package/src/task/index.ts +33 -36
  286. package/src/task/output-manager.ts +3 -4
  287. package/src/task/parallel.ts +0 -1
  288. package/src/task/render.ts +19 -20
  289. package/src/task/subprocess-tool-registry.ts +1 -2
  290. package/src/task/worker-protocol.ts +3 -3
  291. package/src/task/worker.ts +32 -38
  292. package/src/task/worktree.ts +19 -19
  293. package/src/tools/ask.ts +8 -9
  294. package/src/tools/bash-interceptor.ts +1 -5
  295. package/src/tools/bash.ts +19 -18
  296. package/src/tools/calculator.ts +12 -12
  297. package/src/tools/complete.ts +3 -4
  298. package/src/tools/context.ts +2 -2
  299. package/src/tools/fetch.ts +23 -26
  300. package/src/tools/find.ts +15 -16
  301. package/src/tools/gemini-image.ts +14 -14
  302. package/src/tools/grep.ts +27 -27
  303. package/src/tools/index.ts +78 -56
  304. package/src/tools/list-limit.ts +1 -1
  305. package/src/tools/ls.ts +7 -7
  306. package/src/tools/notebook.ts +5 -5
  307. package/src/tools/output-meta.ts +3 -4
  308. package/src/tools/output-utils.ts +1 -1
  309. package/src/tools/path-utils.ts +5 -5
  310. package/src/tools/python.ts +36 -37
  311. package/src/tools/read.ts +23 -23
  312. package/src/tools/render-utils.ts +8 -9
  313. package/src/tools/renderers.ts +6 -7
  314. package/src/tools/review.ts +8 -11
  315. package/src/tools/ssh.ts +31 -30
  316. package/src/tools/todo-write.ts +13 -13
  317. package/src/tools/tool-errors.ts +3 -3
  318. package/src/tools/tool-result.ts +3 -8
  319. package/src/tools/write.ts +11 -16
  320. package/src/tui/code-cell.ts +3 -9
  321. package/src/tui/file-list.ts +3 -4
  322. package/src/tui/output-block.ts +1 -2
  323. package/src/tui/status-line.ts +2 -3
  324. package/src/tui/tree-list.ts +2 -3
  325. package/src/tui/types.ts +1 -2
  326. package/src/tui/utils.ts +2 -3
  327. package/src/utils/changelog.ts +9 -10
  328. package/src/utils/clipboard.ts +11 -11
  329. package/src/utils/file-mentions.ts +4 -10
  330. package/src/utils/frontmatter.ts +6 -3
  331. package/src/utils/fuzzy.ts +2 -2
  332. package/src/utils/image-convert.ts +1 -1
  333. package/src/utils/image-resize.ts +1 -1
  334. package/src/utils/mime.ts +2 -2
  335. package/src/utils/shell-snapshot.ts +11 -13
  336. package/src/utils/shell.ts +4 -5
  337. package/src/utils/title-generator.ts +8 -9
  338. package/src/utils/tools-manager.ts +23 -23
  339. package/src/vendor/photon/index.js +1099 -1059
  340. package/src/vendor/photon/photon_rs_bg.wasm +0 -0
  341. package/src/web/scrapers/artifacthub.ts +1 -1
  342. package/src/web/scrapers/arxiv.ts +2 -2
  343. package/src/web/scrapers/bluesky.ts +2 -2
  344. package/src/web/scrapers/cheatsh.ts +1 -1
  345. package/src/web/scrapers/chocolatey.ts +2 -2
  346. package/src/web/scrapers/choosealicense.ts +5 -5
  347. package/src/web/scrapers/cisa-kev.ts +1 -1
  348. package/src/web/scrapers/crossref.ts +2 -2
  349. package/src/web/scrapers/devto.ts +3 -3
  350. package/src/web/scrapers/discogs.ts +3 -4
  351. package/src/web/scrapers/discourse.ts +1 -1
  352. package/src/web/scrapers/dockerhub.ts +1 -1
  353. package/src/web/scrapers/fdroid.ts +2 -2
  354. package/src/web/scrapers/firefox-addons.ts +3 -3
  355. package/src/web/scrapers/flathub.ts +1 -1
  356. package/src/web/scrapers/github.ts +3 -3
  357. package/src/web/scrapers/gitlab.ts +4 -4
  358. package/src/web/scrapers/hackernews.ts +2 -2
  359. package/src/web/scrapers/huggingface.ts +1 -1
  360. package/src/web/scrapers/iacr.ts +2 -2
  361. package/src/web/scrapers/index.ts +0 -1
  362. package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
  363. package/src/web/scrapers/lemmy.ts +2 -2
  364. package/src/web/scrapers/maven.ts +2 -2
  365. package/src/web/scrapers/mdn.ts +2 -4
  366. package/src/web/scrapers/metacpan.ts +2 -2
  367. package/src/web/scrapers/musicbrainz.ts +1 -2
  368. package/src/web/scrapers/npm.ts +1 -1
  369. package/src/web/scrapers/nuget.ts +2 -2
  370. package/src/web/scrapers/nvd.ts +3 -3
  371. package/src/web/scrapers/ollama.ts +7 -9
  372. package/src/web/scrapers/opencorporates.ts +2 -2
  373. package/src/web/scrapers/openlibrary.ts +6 -6
  374. package/src/web/scrapers/orcid.ts +0 -1
  375. package/src/web/scrapers/osv.ts +2 -2
  376. package/src/web/scrapers/packagist.ts +1 -1
  377. package/src/web/scrapers/pubmed.ts +1 -2
  378. package/src/web/scrapers/rawg.ts +2 -2
  379. package/src/web/scrapers/readthedocs.ts +1 -2
  380. package/src/web/scrapers/repology.ts +2 -2
  381. package/src/web/scrapers/rfc.ts +1 -1
  382. package/src/web/scrapers/searchcode.ts +2 -2
  383. package/src/web/scrapers/semantic-scholar.ts +1 -1
  384. package/src/web/scrapers/snapcraft.ts +2 -2
  385. package/src/web/scrapers/sourcegraph.ts +1 -1
  386. package/src/web/scrapers/spdx.ts +3 -3
  387. package/src/web/scrapers/spotify.ts +0 -1
  388. package/src/web/scrapers/twitter.ts +1 -1
  389. package/src/web/scrapers/types.ts +1 -2
  390. package/src/web/scrapers/utils.ts +5 -5
  391. package/src/web/scrapers/wikidata.ts +3 -3
  392. package/src/web/scrapers/youtube.ts +9 -14
  393. package/src/web/search/auth.ts +4 -9
  394. package/src/web/search/index.ts +11 -21
  395. package/src/web/search/providers/anthropic.ts +3 -9
  396. package/src/web/search/providers/exa.ts +6 -10
  397. package/src/web/search/providers/perplexity.ts +5 -5
  398. package/src/web/search/render.ts +16 -18
  399. package/scripts/generate-wasm-b64.ts +0 -24
  400. package/src/commit/map-reduce/.map-phase.ts.kate-swp +0 -0
  401. package/src/task/.executor.ts.kate-swp +0 -0
  402. package/src/vendor/photon/photon_rs_bg.wasm.b64.js +0 -1
@@ -10,12 +10,11 @@
10
10
  * - Events: AgentSessionEvent objects streamed as they occur
11
11
  * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response
12
12
  */
13
-
14
- import type { ExtensionUIContext, ExtensionUIDialogOptions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
15
- import { type Theme, theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
16
- import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
17
13
  import { readLines } from "@oh-my-pi/pi-utils";
18
14
  import { nanoid } from "nanoid";
15
+ import type { ExtensionUIContext, ExtensionUIDialogOptions } from "../../extensibility/extensions";
16
+ import { type Theme, theme } from "../../modes/theme/theme";
17
+ import type { AgentSession } from "../../session/agent-session";
19
18
  import type {
20
19
  RpcCommand,
21
20
  RpcExtensionUIRequest,
@@ -125,7 +124,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
125
124
  dialogOptions,
126
125
  undefined,
127
126
  { method: "select", title, options, timeout: dialogOptions?.timeout },
128
- (response) =>
127
+ response =>
129
128
  "cancelled" in response && response.cancelled
130
129
  ? undefined
131
130
  : "value" in response
@@ -139,7 +138,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
139
138
  dialogOptions,
140
139
  false,
141
140
  { method: "confirm", title, message, timeout: dialogOptions?.timeout },
142
- (response) =>
141
+ response =>
143
142
  "cancelled" in response && response.cancelled
144
143
  ? false
145
144
  : "confirmed" in response
@@ -157,7 +156,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
157
156
  dialogOptions,
158
157
  undefined,
159
158
  { method: "input", title, placeholder, timeout: dialogOptions?.timeout },
160
- (response) =>
159
+ response =>
161
160
  "cancelled" in response && response.cancelled
162
161
  ? undefined
163
162
  : "value" in response
@@ -271,17 +270,17 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
271
270
  return theme;
272
271
  }
273
272
 
274
- getAllThemes(): { name: string; path: string | undefined }[] {
275
- return [];
273
+ getAllThemes(): Promise<{ name: string; path: string | undefined }[]> {
274
+ return Promise.resolve([]);
276
275
  }
277
276
 
278
- getTheme(_name: string): Theme | undefined {
279
- return undefined;
277
+ getTheme(_name: string): Promise<Theme | undefined> {
278
+ return Promise.resolve(undefined);
280
279
  }
281
280
 
282
- setTheme(_theme: string | Theme): { success: boolean; error?: string } {
281
+ setTheme(_theme: string | Theme): Promise<{ success: boolean; error?: string }> {
283
282
  // Theme switching not supported in RPC mode
284
- return { success: false, error: "Theme switching not supported in RPC mode" };
283
+ return Promise.resolve({ success: false, error: "Theme switching not supported in RPC mode" });
285
284
  }
286
285
 
287
286
  setEditorComponent(): void {
@@ -296,12 +295,12 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
296
295
  // ExtensionActions
297
296
  {
298
297
  sendMessage: (message, options) => {
299
- session.sendCustomMessage(message, options).catch((e) => {
298
+ session.sendCustomMessage(message, options).catch(e => {
300
299
  output(error(undefined, "extension_send", e.message));
301
300
  });
302
301
  },
303
302
  sendUserMessage: (content, options) => {
304
- session.sendUserMessage(content, options).catch((e) => {
303
+ session.sendUserMessage(content, options).catch(e => {
305
304
  output(error(undefined, "extension_send_user", e.message));
306
305
  });
307
306
  },
@@ -314,14 +313,14 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
314
313
  getActiveTools: () => session.getActiveToolNames(),
315
314
  getAllTools: () => session.getAllToolNames(),
316
315
  setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames),
317
- setModel: async (model) => {
316
+ setModel: async model => {
318
317
  const key = await session.modelRegistry.getApiKey(model);
319
318
  if (!key) return false;
320
319
  await session.setModel(model);
321
320
  return true;
322
321
  },
323
322
  getThinkingLevel: () => session.thinkingLevel,
324
- setThinkingLevel: (level) => session.setThinkingLevel(level),
323
+ setThinkingLevel: level => session.setThinkingLevel(level),
325
324
  },
326
325
  // ExtensionContextActions
327
326
  {
@@ -333,7 +332,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
333
332
  shutdownState.requested = true;
334
333
  },
335
334
  getContextUsage: () => session.getContextUsage(),
336
- compact: async (instructionsOrOptions) => {
335
+ compact: async instructionsOrOptions => {
337
336
  const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
338
337
  const options =
339
338
  instructionsOrOptions && typeof instructionsOrOptions === "object"
@@ -346,7 +345,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
346
345
  {
347
346
  getContextUsage: () => session.getContextUsage(),
348
347
  waitForIdle: () => session.agent.waitForIdle(),
349
- newSession: async (options) => {
348
+ newSession: async options => {
350
349
  const success = await session.newSession({ parentSession: options?.parentSession });
351
350
  // Note: setup callback runs but no UI feedback in RPC mode
352
351
  if (success && options?.setup) {
@@ -354,7 +353,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
354
353
  }
355
354
  return { cancelled: !success };
356
355
  },
357
- branch: async (entryId) => {
356
+ branch: async entryId => {
358
357
  const result = await session.branch(entryId);
359
358
  return { cancelled: result.cancelled };
360
359
  },
@@ -362,7 +361,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
362
361
  const result = await session.navigateTree(targetId, { summarize: options?.summarize });
363
362
  return { cancelled: result.cancelled };
364
363
  },
365
- compact: async (instructionsOrOptions) => {
364
+ compact: async instructionsOrOptions => {
366
365
  const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
367
366
  const options =
368
367
  instructionsOrOptions && typeof instructionsOrOptions === "object"
@@ -373,7 +372,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
373
372
  },
374
373
  new RpcExtensionUIContext(pendingExtensionRequests, output),
375
374
  );
376
- extensionRunner.onError((err) => {
375
+ extensionRunner.onError(err => {
377
376
  output({ type: "extension_error", extensionPath: err.extensionPath, event: err.event, error: err.error });
378
377
  });
379
378
  // Emit session_start event
@@ -383,7 +382,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
383
382
  }
384
383
 
385
384
  // Output all agent events as JSON
386
- session.subscribe((event) => {
385
+ session.subscribe(event => {
387
386
  output(event);
388
387
  });
389
388
 
@@ -405,7 +404,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
405
404
  images: command.images,
406
405
  streamingBehavior: command.streamingBehavior,
407
406
  })
408
- .catch((e) => output(error(id, "prompt", e.message)));
407
+ .catch(e => output(error(id, "prompt", e.message)));
409
408
  return success(id, "prompt");
410
409
  }
411
410
 
@@ -458,7 +457,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
458
457
 
459
458
  case "set_model": {
460
459
  const models = session.getAvailableModels();
461
- const model = models.find((m) => m.provider === command.provider && m.id === command.modelId);
460
+ const model = models.find(m => m.provider === command.provider && m.id === command.modelId);
462
461
  if (!model) {
463
462
  return error(id, "set_model", `Model not found: ${command.provider}/${command.modelId}`);
464
463
  }
@@ -4,12 +4,11 @@
4
4
  * Commands are sent as JSON lines on stdin.
5
5
  * Responses and events are emitted as JSON lines on stdout.
6
6
  */
7
-
8
7
  import type { AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
9
8
  import type { ImageContent, Model } from "@oh-my-pi/pi-ai";
10
- import type { BashResult } from "@oh-my-pi/pi-coding-agent/exec/bash-executor";
11
- import type { SessionStats } from "@oh-my-pi/pi-coding-agent/session/agent-session";
12
- import type { CompactionResult } from "@oh-my-pi/pi-coding-agent/session/compaction";
9
+ import type { BashResult } from "../../exec/bash-executor";
10
+ import type { SessionStats } from "../../session/agent-session";
11
+ import type { CompactionResult } from "../../session/compaction";
13
12
 
14
13
  // ============================================================================
15
14
  // RPC Commands (stdin)
@@ -54,7 +54,7 @@ export async function prerenderMermaid(
54
54
  }
55
55
 
56
56
  promises.push(
57
- promise.then((image) => {
57
+ promise.then(image => {
58
58
  pending.delete(hash);
59
59
  if (image) {
60
60
  cache.set(hash, image);
@@ -66,7 +66,7 @@ export async function prerenderMermaid(
66
66
  }
67
67
 
68
68
  const results = await Promise.all(promises);
69
- const newImages = results.some((added) => added);
69
+ const newImages = results.some(added => added);
70
70
 
71
71
  if (newImages && onRenderNeeded) {
72
72
  onRenderNeeded();
@@ -1,12 +1,12 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { getCustomThemesDir } from "@oh-my-pi/pi-coding-agent/config";
4
3
  import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
5
- import { logger } from "@oh-my-pi/pi-utils";
4
+ import { adjustHsv, isEnoent, logger } from "@oh-my-pi/pi-utils";
6
5
  import { type Static, Type } from "@sinclair/typebox";
7
6
  import { TypeCompiler } from "@sinclair/typebox/compiler";
8
7
  import chalk from "chalk";
9
8
  import { highlight, supportsLanguage } from "cli-highlight";
9
+ import { getCustomThemesDir } from "../../config";
10
10
  // Embed theme JSON files at build time
11
11
  import darkThemeJson from "./dark.json" with { type: "json" };
12
12
  import { defaultThemes } from "./defaults";
@@ -1600,16 +1600,18 @@ function getBuiltinThemes(): Record<string, ThemeJson> {
1600
1600
  return BUILTIN_THEMES;
1601
1601
  }
1602
1602
 
1603
- export function getAvailableThemes(): string[] {
1603
+ export async function getAvailableThemes(): Promise<string[]> {
1604
1604
  const themes = new Set<string>(Object.keys(getBuiltinThemes()));
1605
1605
  const customThemesDir = getCustomThemesDir();
1606
- if (fs.existsSync(customThemesDir)) {
1607
- const files = fs.readdirSync(customThemesDir);
1606
+ try {
1607
+ const files = await fs.promises.readdir(customThemesDir);
1608
1608
  for (const file of files) {
1609
1609
  if (file.endsWith(".json")) {
1610
1610
  themes.add(file.slice(0, -5));
1611
1611
  }
1612
1612
  }
1613
+ } catch {
1614
+ // Directory doesn't exist or isn't readable
1613
1615
  }
1614
1616
  return Array.from(themes).sort();
1615
1617
  }
@@ -1619,7 +1621,7 @@ export interface ThemeInfo {
1619
1621
  path: string | undefined;
1620
1622
  }
1621
1623
 
1622
- export function getAvailableThemesWithPaths(): ThemeInfo[] {
1624
+ export async function getAvailableThemesWithPaths(): Promise<ThemeInfo[]> {
1623
1625
  const result: ThemeInfo[] = [];
1624
1626
 
1625
1627
  // Built-in themes (embedded, no file path)
@@ -1629,31 +1631,37 @@ export function getAvailableThemesWithPaths(): ThemeInfo[] {
1629
1631
 
1630
1632
  // Custom themes
1631
1633
  const customThemesDir = getCustomThemesDir();
1632
- if (fs.existsSync(customThemesDir)) {
1633
- for (const file of fs.readdirSync(customThemesDir)) {
1634
+ try {
1635
+ const files = await fs.promises.readdir(customThemesDir);
1636
+ for (const file of files) {
1634
1637
  if (file.endsWith(".json")) {
1635
1638
  const name = file.slice(0, -5);
1636
- if (!result.some((themeInfo) => themeInfo.name === name)) {
1639
+ if (!result.some(themeInfo => themeInfo.name === name)) {
1637
1640
  result.push({ name, path: path.join(customThemesDir, file) });
1638
1641
  }
1639
1642
  }
1640
1643
  }
1644
+ } catch {
1645
+ // Directory doesn't exist or isn't readable
1641
1646
  }
1642
1647
 
1643
1648
  return result.sort((a, b) => a.name.localeCompare(b.name));
1644
1649
  }
1645
1650
 
1646
- function loadThemeJson(name: string): ThemeJson {
1651
+ async function loadThemeJson(name: string): Promise<ThemeJson> {
1647
1652
  const builtinThemes = getBuiltinThemes();
1648
1653
  if (name in builtinThemes) {
1649
1654
  return builtinThemes[name];
1650
1655
  }
1651
1656
  const customThemesDir = getCustomThemesDir();
1652
1657
  const themePath = path.join(customThemesDir, `${name}.json`);
1653
- if (!fs.existsSync(themePath)) {
1654
- throw new Error(`Theme not found: ${name}`);
1658
+ let content: string;
1659
+ try {
1660
+ content = await Bun.file(themePath).text();
1661
+ } catch (err) {
1662
+ if (isEnoent(err)) throw new Error(`Theme not found: ${name}`);
1663
+ throw err;
1655
1664
  }
1656
- const content = fs.readFileSync(themePath, "utf-8");
1657
1665
  let json: unknown;
1658
1666
  try {
1659
1667
  json = JSON.parse(content);
@@ -1678,7 +1686,7 @@ function loadThemeJson(name: string): ThemeJson {
1678
1686
  let errorMessage = `Invalid theme "${name}":\n`;
1679
1687
  if (missingColors.length > 0) {
1680
1688
  errorMessage += `\nMissing required color tokens:\n`;
1681
- errorMessage += missingColors.map((c) => ` - ${c}`).join("\n");
1689
+ errorMessage += missingColors.map(c => ` - ${c}`).join("\n");
1682
1690
  errorMessage += `\n\nPlease add these colors to your theme's "colors" object.`;
1683
1691
  errorMessage += `\nSee the built-in themes (dark.json, light.json) for reference values.`;
1684
1692
  }
@@ -1691,9 +1699,27 @@ function loadThemeJson(name: string): ThemeJson {
1691
1699
  return json as ThemeJson;
1692
1700
  }
1693
1701
 
1694
- function createTheme(themeJson: ThemeJson, mode?: ColorMode, symbolPresetOverride?: SymbolPreset): Theme {
1702
+ interface CreateThemeOptions {
1703
+ mode?: ColorMode;
1704
+ symbolPresetOverride?: SymbolPreset;
1705
+ colorBlindMode?: boolean;
1706
+ }
1707
+
1708
+ /** HSV adjustment to shift green toward blue for colorblind mode (red-green colorblindness) */
1709
+ const COLORBLIND_ADJUSTMENT = { h: 60, s: 0.71 };
1710
+
1711
+ function createTheme(themeJson: ThemeJson, options: CreateThemeOptions = {}): Theme {
1712
+ const { mode, symbolPresetOverride, colorBlindMode } = options;
1695
1713
  const colorMode = mode ?? detectColorMode();
1696
1714
  const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
1715
+
1716
+ if (colorBlindMode) {
1717
+ const added = resolvedColors.toolDiffAdded;
1718
+ if (typeof added === "string" && added.startsWith("#")) {
1719
+ resolvedColors.toolDiffAdded = adjustHsv(added, COLORBLIND_ADJUSTMENT);
1720
+ }
1721
+ }
1722
+
1697
1723
  const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
1698
1724
  const bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;
1699
1725
  const bgColorKeys: Set<string> = new Set([
@@ -1718,14 +1744,14 @@ function createTheme(themeJson: ThemeJson, mode?: ColorMode, symbolPresetOverrid
1718
1744
  return new Theme(fgColors, bgColors, colorMode, symbolPreset, symbolOverrides);
1719
1745
  }
1720
1746
 
1721
- function loadTheme(name: string, mode?: ColorMode, symbolPresetOverride?: SymbolPreset): Theme {
1722
- const themeJson = loadThemeJson(name);
1723
- return createTheme(themeJson, mode, symbolPresetOverride);
1747
+ async function loadTheme(name: string, options: CreateThemeOptions = {}): Promise<Theme> {
1748
+ const themeJson = await loadThemeJson(name);
1749
+ return createTheme(themeJson, options);
1724
1750
  }
1725
1751
 
1726
- export function getThemeByName(name: string): Theme | undefined {
1752
+ export async function getThemeByName(name: string): Promise<Theme | undefined> {
1727
1753
  try {
1728
- return loadTheme(name);
1754
+ return await loadTheme(name);
1729
1755
  } catch {
1730
1756
  return undefined;
1731
1757
  }
@@ -1757,32 +1783,49 @@ function getDefaultTheme(): string {
1757
1783
  export let theme: Theme;
1758
1784
  let currentThemeName: string | undefined;
1759
1785
  let currentSymbolPresetOverride: SymbolPreset | undefined;
1786
+ let currentColorBlindMode: boolean = false;
1760
1787
  let themeWatcher: fs.FSWatcher | undefined;
1761
1788
  let onThemeChangeCallback: (() => void) | undefined;
1762
1789
 
1763
- export function initTheme(themeName?: string, enableWatcher: boolean = false, symbolPreset?: SymbolPreset): void {
1790
+ function getCurrentThemeOptions(): CreateThemeOptions {
1791
+ return {
1792
+ symbolPresetOverride: currentSymbolPresetOverride,
1793
+ colorBlindMode: currentColorBlindMode,
1794
+ };
1795
+ }
1796
+
1797
+ export async function initTheme(
1798
+ themeName?: string,
1799
+ enableWatcher: boolean = false,
1800
+ symbolPreset?: SymbolPreset,
1801
+ colorBlindMode?: boolean,
1802
+ ): Promise<void> {
1764
1803
  const name = themeName ?? getDefaultTheme();
1765
1804
  currentThemeName = name;
1766
1805
  currentSymbolPresetOverride = symbolPreset;
1806
+ currentColorBlindMode = colorBlindMode ?? false;
1767
1807
  try {
1768
- theme = loadTheme(name, undefined, symbolPreset);
1808
+ theme = await loadTheme(name, getCurrentThemeOptions());
1769
1809
  if (enableWatcher) {
1770
- startThemeWatcher();
1810
+ await startThemeWatcher();
1771
1811
  }
1772
1812
  } catch (err) {
1773
1813
  logger.debug("Theme loading failed, falling back to dark theme", { error: String(err) });
1774
1814
  currentThemeName = "dark";
1775
- theme = loadTheme("dark", undefined, symbolPreset);
1815
+ theme = await loadTheme("dark", getCurrentThemeOptions());
1776
1816
  // Don't start watcher for fallback theme
1777
1817
  }
1778
1818
  }
1779
1819
 
1780
- export function setTheme(name: string, enableWatcher: boolean = false): { success: boolean; error?: string } {
1820
+ export async function setTheme(
1821
+ name: string,
1822
+ enableWatcher: boolean = false,
1823
+ ): Promise<{ success: boolean; error?: string }> {
1781
1824
  currentThemeName = name;
1782
1825
  try {
1783
- theme = loadTheme(name, undefined, currentSymbolPresetOverride);
1826
+ theme = await loadTheme(name, getCurrentThemeOptions());
1784
1827
  if (enableWatcher) {
1785
- startThemeWatcher();
1828
+ await startThemeWatcher();
1786
1829
  }
1787
1830
  if (onThemeChangeCallback) {
1788
1831
  onThemeChangeCallback();
@@ -1791,7 +1834,7 @@ export function setTheme(name: string, enableWatcher: boolean = false): { succes
1791
1834
  } catch (error) {
1792
1835
  // Theme is invalid - fall back to dark theme
1793
1836
  currentThemeName = "dark";
1794
- theme = loadTheme("dark", undefined, currentSymbolPresetOverride);
1837
+ theme = await loadTheme("dark", getCurrentThemeOptions());
1795
1838
  // Don't start watcher for fallback theme
1796
1839
  return {
1797
1840
  success: false,
@@ -1812,14 +1855,14 @@ export function setThemeInstance(themeInstance: Theme): void {
1812
1855
  /**
1813
1856
  * Set the symbol preset override, recreating the theme with the new preset.
1814
1857
  */
1815
- export function setSymbolPreset(preset: SymbolPreset): void {
1858
+ export async function setSymbolPreset(preset: SymbolPreset): Promise<void> {
1816
1859
  currentSymbolPresetOverride = preset;
1817
1860
  if (currentThemeName) {
1818
1861
  try {
1819
- theme = loadTheme(currentThemeName, undefined, preset);
1862
+ theme = await loadTheme(currentThemeName, getCurrentThemeOptions());
1820
1863
  } catch {
1821
1864
  // Fall back to dark theme with new preset
1822
- theme = loadTheme("dark", undefined, preset);
1865
+ theme = await loadTheme("dark", getCurrentThemeOptions());
1823
1866
  }
1824
1867
  if (onThemeChangeCallback) {
1825
1868
  onThemeChangeCallback();
@@ -1834,6 +1877,32 @@ export function getSymbolPresetOverride(): SymbolPreset | undefined {
1834
1877
  return currentSymbolPresetOverride;
1835
1878
  }
1836
1879
 
1880
+ /**
1881
+ * Set color blind mode, recreating the theme with the new setting.
1882
+ * When enabled, uses blue instead of green for diff additions.
1883
+ */
1884
+ export async function setColorBlindMode(enabled: boolean): Promise<void> {
1885
+ currentColorBlindMode = enabled;
1886
+ if (currentThemeName) {
1887
+ try {
1888
+ theme = await loadTheme(currentThemeName, getCurrentThemeOptions());
1889
+ } catch {
1890
+ // Fall back to dark theme
1891
+ theme = await loadTheme("dark", getCurrentThemeOptions());
1892
+ }
1893
+ if (onThemeChangeCallback) {
1894
+ onThemeChangeCallback();
1895
+ }
1896
+ }
1897
+ }
1898
+
1899
+ /**
1900
+ * Get the current color blind mode setting.
1901
+ */
1902
+ export function getColorBlindMode(): boolean {
1903
+ return currentColorBlindMode;
1904
+ }
1905
+
1837
1906
  export function onThemeChange(callback: () => void): void {
1838
1907
  onThemeChangeCallback = callback;
1839
1908
  }
@@ -1852,7 +1921,7 @@ export function isValidSymbolPreset(preset: string): preset is SymbolPreset {
1852
1921
  return preset === "unicode" || preset === "nerd" || preset === "ascii";
1853
1922
  }
1854
1923
 
1855
- function startThemeWatcher(): void {
1924
+ async function startThemeWatcher(): Promise<void> {
1856
1925
  // Stop existing watcher if any
1857
1926
  if (themeWatcher) {
1858
1927
  themeWatcher.close();
@@ -1873,34 +1942,40 @@ function startThemeWatcher(): void {
1873
1942
  }
1874
1943
 
1875
1944
  try {
1876
- themeWatcher = fs.watch(themeFile, (eventType) => {
1945
+ themeWatcher = fs.watch(themeFile, eventType => {
1877
1946
  if (eventType === "change") {
1878
1947
  // Debounce rapid changes
1879
1948
  setTimeout(() => {
1880
- try {
1881
- // Reload the theme with current symbol preset override
1882
- theme = loadTheme(currentThemeName!, undefined, currentSymbolPresetOverride);
1883
- // Notify callback (to invalidate UI)
1884
- if (onThemeChangeCallback) {
1885
- onThemeChangeCallback();
1886
- }
1887
- } catch (err) {
1888
- logger.debug("Theme reload error during file change", { error: String(err) });
1889
- }
1949
+ loadTheme(currentThemeName!, getCurrentThemeOptions())
1950
+ .then(loadedTheme => {
1951
+ theme = loadedTheme;
1952
+ if (onThemeChangeCallback) {
1953
+ onThemeChangeCallback();
1954
+ }
1955
+ })
1956
+ .catch(err => {
1957
+ logger.debug("Theme reload error during file change", { error: String(err) });
1958
+ });
1890
1959
  }, 100);
1891
1960
  } else if (eventType === "rename") {
1892
1961
  // File was deleted or renamed - fall back to default theme
1893
1962
  setTimeout(() => {
1894
1963
  if (!fs.existsSync(themeFile)) {
1895
1964
  currentThemeName = "dark";
1896
- theme = loadTheme("dark");
1965
+ loadTheme("dark", getCurrentThemeOptions())
1966
+ .then(loadedTheme => {
1967
+ theme = loadedTheme;
1968
+ if (onThemeChangeCallback) {
1969
+ onThemeChangeCallback();
1970
+ }
1971
+ })
1972
+ .catch(err => {
1973
+ logger.debug("Theme reload error during rename fallback", { error: String(err) });
1974
+ });
1897
1975
  if (themeWatcher) {
1898
1976
  themeWatcher.close();
1899
1977
  themeWatcher = undefined;
1900
1978
  }
1901
- if (onThemeChangeCallback) {
1902
- onThemeChangeCallback();
1903
- }
1904
1979
  }
1905
1980
  }, 100);
1906
1981
  }
@@ -1971,10 +2046,10 @@ function ansi256ToHex(index: number): string {
1971
2046
  * Get resolved theme colors as CSS-compatible hex strings.
1972
2047
  * Used by HTML export to generate CSS custom properties.
1973
2048
  */
1974
- export function getResolvedThemeColors(themeName?: string): Record<string, string> {
2049
+ export async function getResolvedThemeColors(themeName?: string): Promise<Record<string, string>> {
1975
2050
  const name = themeName ?? getDefaultTheme();
1976
2051
  const isLight = name === "light";
1977
- const themeJson = loadThemeJson(name);
2052
+ const themeJson = await loadThemeJson(name);
1978
2053
  const resolved = resolveThemeColors(themeJson.colors, themeJson.vars);
1979
2054
 
1980
2055
  // Default text color for empty values (terminal uses default fg color)
@@ -2006,14 +2081,14 @@ export function isLightTheme(themeName?: string): boolean {
2006
2081
  * Get explicit export colors from theme JSON, if specified.
2007
2082
  * Returns undefined for each color that isn't explicitly set.
2008
2083
  */
2009
- export function getThemeExportColors(themeName?: string): {
2084
+ export async function getThemeExportColors(themeName?: string): Promise<{
2010
2085
  pageBg?: string;
2011
2086
  cardBg?: string;
2012
2087
  infoBg?: string;
2013
- } {
2088
+ }> {
2014
2089
  const name = themeName ?? getDefaultTheme();
2015
2090
  try {
2016
- const themeJson = loadThemeJson(name);
2091
+ const themeJson = await loadThemeJson(name);
2017
2092
  const exportSection = themeJson.export;
2018
2093
  if (!exportSection) return {};
2019
2094
 
@@ -2237,7 +2312,7 @@ export function getMarkdownTheme(): MarkdownTheme {
2237
2312
  try {
2238
2313
  return highlight(code, opts).split("\n");
2239
2314
  } catch {
2240
- return code.split("\n").map((line) => theme.fg("mdCodeBlock", line));
2315
+ return code.split("\n").map(line => theme.fg("mdCodeBlock", line));
2241
2316
  }
2242
2317
  },
2243
2318
  };
@@ -1,14 +1,14 @@
1
1
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
2
  import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
3
- import type { KeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings";
4
- import type { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
5
- import type { ExtensionUIContext } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
6
- import type { CompactOptions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/types";
7
- import type { MCPManager } from "@oh-my-pi/pi-coding-agent/mcp";
8
- import type { AgentSession, AgentSessionEvent } from "@oh-my-pi/pi-coding-agent/session/agent-session";
9
- import type { HistoryStorage } from "@oh-my-pi/pi-coding-agent/session/history-storage";
10
- import type { SessionContext, SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
11
3
  import type { Component, Container, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
4
+ import type { KeybindingsManager } from "../config/keybindings";
5
+ import type { SettingsManager } from "../config/settings-manager";
6
+ import type { ExtensionUIContext } from "../extensibility/extensions";
7
+ import type { CompactOptions } from "../extensibility/extensions/types";
8
+ import type { MCPManager } from "../mcp";
9
+ import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
10
+ import type { HistoryStorage } from "../session/history-storage";
11
+ import type { SessionContext, SessionManager } from "../session/session-manager";
12
12
  import type { AssistantMessageComponent } from "./components/assistant-message";
13
13
  import type { BashExecutionComponent } from "./components/bash-execution";
14
14
  import type { CustomEditor } from "./components/custom-editor";
@@ -132,9 +132,9 @@ export interface InteractiveModeContext {
132
132
  handleExportCommand(text: string): Promise<void>;
133
133
  handleShareCommand(): Promise<void>;
134
134
  handleCopyCommand(): Promise<void>;
135
- handleSessionCommand(): void;
135
+ handleSessionCommand(): Promise<void>;
136
136
  handleUsageCommand(reports?: UsageReport[] | null): Promise<void>;
137
- handleChangelogCommand(): void;
137
+ handleChangelogCommand(): Promise<void>;
138
138
  handleHotkeysCommand(): void;
139
139
  handleDumpCommand(): Promise<void>;
140
140
  handleClearCommand(): Promise<void>;
@@ -1,20 +1,20 @@
1
1
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
2
  import type { AssistantMessage, Message } from "@oh-my-pi/pi-ai";
3
- import { AssistantMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/assistant-message";
4
- import { BashExecutionComponent } from "@oh-my-pi/pi-coding-agent/modes/components/bash-execution";
5
- import { BranchSummaryMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/branch-summary-message";
6
- import { CompactionSummaryMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/compaction-summary-message";
7
- import { CustomMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/custom-message";
8
- import { DynamicBorder } from "@oh-my-pi/pi-coding-agent/modes/components/dynamic-border";
9
- import { PythonExecutionComponent } from "@oh-my-pi/pi-coding-agent/modes/components/python-execution";
10
- import { ReadToolGroupComponent } from "@oh-my-pi/pi-coding-agent/modes/components/read-tool-group";
11
- import { ToolExecutionComponent } from "@oh-my-pi/pi-coding-agent/modes/components/tool-execution";
12
- import { UserMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/user-message";
13
- import { theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
14
- import type { CompactionQueuedMessage, InteractiveModeContext } from "@oh-my-pi/pi-coding-agent/modes/types";
15
- import type { CustomMessage } from "@oh-my-pi/pi-coding-agent/session/messages";
16
- import type { SessionContext } from "@oh-my-pi/pi-coding-agent/session/session-manager";
17
3
  import { Spacer, Text, TruncatedText } from "@oh-my-pi/pi-tui";
4
+ import { AssistantMessageComponent } from "../../modes/components/assistant-message";
5
+ import { BashExecutionComponent } from "../../modes/components/bash-execution";
6
+ import { BranchSummaryMessageComponent } from "../../modes/components/branch-summary-message";
7
+ import { CompactionSummaryMessageComponent } from "../../modes/components/compaction-summary-message";
8
+ import { CustomMessageComponent } from "../../modes/components/custom-message";
9
+ import { DynamicBorder } from "../../modes/components/dynamic-border";
10
+ import { PythonExecutionComponent } from "../../modes/components/python-execution";
11
+ import { ReadToolGroupComponent } from "../../modes/components/read-tool-group";
12
+ import { ToolExecutionComponent } from "../../modes/components/tool-execution";
13
+ import { UserMessageComponent } from "../../modes/components/user-message";
14
+ import { theme } from "../../modes/theme/theme";
15
+ import type { CompactionQueuedMessage, InteractiveModeContext } from "../../modes/types";
16
+ import type { CustomMessage } from "../../session/messages";
17
+ import type { SessionContext } from "../../session/session-manager";
18
18
 
19
19
  type TextBlock = { type: "text"; text: string };
20
20
 
@@ -33,7 +33,7 @@ export class UiHelpers {
33
33
  typeof message.content === "string"
34
34
  ? [{ type: "text", text: message.content }]
35
35
  : message.content.filter((content): content is TextBlock => content.type === "text");
36
- return textBlocks.map((block) => block.text).join("");
36
+ return textBlocks.map(block => block.text).join("");
37
37
  }
38
38
 
39
39
  /**
@@ -306,7 +306,7 @@ export class UiHelpers {
306
306
 
307
307
  showNewVersionNotification(newVersion: string): void {
308
308
  this.ctx.chatContainer.addChild(new Spacer(1));
309
- this.ctx.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
309
+ this.ctx.chatContainer.addChild(new DynamicBorder(text => theme.fg("warning", text)));
310
310
  this.ctx.chatContainer.addChild(
311
311
  new Text(
312
312
  theme.bold(theme.fg("warning", "Update Available")) +
@@ -317,7 +317,7 @@ export class UiHelpers {
317
317
  0,
318
318
  ),
319
319
  );
320
- this.ctx.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
320
+ this.ctx.chatContainer.addChild(new DynamicBorder(text => theme.fg("warning", text)));
321
321
  this.ctx.ui.requestRender();
322
322
  }
323
323