@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
package/src/tools/ssh.ts CHANGED
@@ -1,23 +1,23 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
- import type { SSHHost } from "@oh-my-pi/pi-coding-agent/capability/ssh";
3
- import { sshCapability } from "@oh-my-pi/pi-coding-agent/capability/ssh";
4
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
5
- import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
6
- import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
7
- import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
8
- import sshDescriptionBase from "@oh-my-pi/pi-coding-agent/prompts/tools/ssh.md" with { type: "text" };
9
- import type { SSHHostInfo } from "@oh-my-pi/pi-coding-agent/ssh/connection-manager";
10
- import { ensureHostInfo, getHostInfoForHost } from "@oh-my-pi/pi-coding-agent/ssh/connection-manager";
11
- import { executeSSH } from "@oh-my-pi/pi-coding-agent/ssh/ssh-executor";
12
- import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
13
- import { ToolError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
14
- import { renderOutputBlock, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
15
2
  import type { Component } from "@oh-my-pi/pi-tui";
16
3
  import { Text } from "@oh-my-pi/pi-tui";
17
4
  import { Type } from "@sinclair/typebox";
5
+ import type { SSHHost } from "../capability/ssh";
6
+ import { sshCapability } from "../capability/ssh";
7
+ import { renderPromptTemplate } from "../config/prompt-templates";
8
+ import { loadCapability } from "../discovery";
9
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
10
+ import type { Theme } from "../modes/theme/theme";
11
+ import sshDescriptionBase from "../prompts/tools/ssh.md" with { type: "text" };
12
+ import type { SSHHostInfo } from "../ssh/connection-manager";
13
+ import { ensureHostInfo, getHostInfoForHost } from "../ssh/connection-manager";
14
+ import { executeSSH } from "../ssh/ssh-executor";
15
+ import { renderOutputBlock, renderStatusLine } from "../tui";
18
16
  import type { ToolSession } from ".";
17
+ import type { OutputMeta } from "./output-meta";
19
18
  import { allocateOutputArtifact, createTailBuffer } from "./output-utils";
20
19
  import { formatBytes, wrapBrackets } from "./render-utils";
20
+ import { ToolError } from "./tool-errors";
21
21
  import { toolResult } from "./tool-result";
22
22
  import { DEFAULT_MAX_BYTES } from "./truncate";
23
23
 
@@ -32,8 +32,8 @@ export interface SSHToolDetails {
32
32
  meta?: OutputMeta;
33
33
  }
34
34
 
35
- function formatHostEntry(host: SSHHost): string {
36
- const info = getHostInfoForHost(host);
35
+ async function formatHostEntry(host: SSHHost): Promise<string> {
36
+ const info = await getHostInfoForHost(host);
37
37
 
38
38
  let shell: string;
39
39
  if (!info) {
@@ -58,12 +58,12 @@ function formatHostEntry(host: SSHHost): string {
58
58
  return `- ${host.name} (${host.host}) | ${shell}`;
59
59
  }
60
60
 
61
- function formatDescription(hosts: SSHHost[]): string {
61
+ async function formatDescription(hosts: SSHHost[]): Promise<string> {
62
62
  const baseDescription = renderPromptTemplate(sshDescriptionBase);
63
63
  if (hosts.length === 0) {
64
64
  return baseDescription;
65
65
  }
66
- const hostList = hosts.map(formatHostEntry).join("\n");
66
+ const hostList = (await Promise.all(hosts.map(formatHostEntry))).join("\n");
67
67
  return `${baseDescription}\n\nAvailable hosts:\n${hostList}`;
68
68
  }
69
69
 
@@ -135,17 +135,12 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
135
135
  private readonly hostsByName: Map<string, SSHHost>;
136
136
  private readonly hostNames: string[];
137
137
 
138
- constructor(session: ToolSession, hostNames: string[], hostsByName: Map<string, SSHHost>) {
138
+ constructor(session: ToolSession, hostNames: string[], hostsByName: Map<string, SSHHost>, description: string) {
139
139
  this.session = session;
140
140
  this.hostNames = hostNames;
141
141
  this.hostsByName = hostsByName;
142
142
  this.allowedHosts = new Set(hostNames);
143
-
144
- const descriptionHosts = hostNames
145
- .map((name) => hostsByName.get(name))
146
- .filter((host): host is SSHHost => host !== undefined);
147
-
148
- this.description = formatDescription(descriptionHosts);
143
+ this.description = description;
149
144
  }
150
145
 
151
146
  public async execute(
@@ -182,7 +177,7 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
182
177
  compatEnabled: hostInfo.compatEnabled,
183
178
  artifactPath,
184
179
  artifactId,
185
- onChunk: (chunk) => {
180
+ onChunk: chunk => {
186
181
  tailBuffer.append(chunk);
187
182
  if (onUpdate) {
188
183
  onUpdate({
@@ -214,7 +209,13 @@ export async function loadSshTool(session: ToolSession): Promise<SshTool | null>
214
209
  if (hostNames.length === 0) {
215
210
  return null;
216
211
  }
217
- return new SshTool(session, hostNames, hostsByName);
212
+
213
+ const descriptionHosts = hostNames
214
+ .map(name => hostsByName.get(name))
215
+ .filter((host): host is SSHHost => host !== undefined);
216
+ const description = await formatDescription(descriptionHosts);
217
+
218
+ return new SshTool(session, hostNames, hostsByName, description);
218
219
  }
219
220
 
220
221
  // =============================================================================
@@ -263,12 +264,12 @@ export const sshToolRenderer = {
263
264
  );
264
265
  const outputLines: string[] = [];
265
266
 
266
- const textContent = result.content?.find((c) => c.type === "text")?.text ?? "";
267
+ const textContent = result.content?.find(c => c.type === "text")?.text ?? "";
267
268
  const output = textContent.trimEnd();
268
269
 
269
270
  if (output) {
270
271
  if (expanded) {
271
- outputLines.push(...output.split("\n").map((line) => uiTheme.fg("toolOutput", line)));
272
+ outputLines.push(...output.split("\n").map(line => uiTheme.fg("toolOutput", line)));
272
273
  } else if (renderContext?.visualLines) {
273
274
  const { visualLines, skippedCount = 0, totalVisualLines = visualLines.length } = renderContext;
274
275
  if (skippedCount > 0) {
@@ -279,7 +280,7 @@ export const sshToolRenderer = {
279
280
  ),
280
281
  );
281
282
  }
282
- const styledVisual = visualLines.map((line) =>
283
+ const styledVisual = visualLines.map(line =>
283
284
  line.includes("\x1b[") ? line : uiTheme.fg("toolOutput", line),
284
285
  );
285
286
  outputLines.push(...styledVisual);
@@ -288,7 +289,7 @@ export const sshToolRenderer = {
288
289
  const maxLines = 5;
289
290
  const displayLines = outputLinesRaw.slice(0, maxLines);
290
291
  const remaining = outputLinesRaw.length - maxLines;
291
- outputLines.push(...displayLines.map((line) => uiTheme.fg("toolOutput", line)));
292
+ outputLines.push(...displayLines.map(line => uiTheme.fg("toolOutput", line)));
292
293
  if (remaining > 0) {
293
294
  outputLines.push(
294
295
  uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${remaining} more lines) (ctrl+o to expand)`),
@@ -2,17 +2,17 @@ import { randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
4
4
  import { StringEnum } from "@oh-my-pi/pi-ai";
5
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
6
- import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
7
- import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
8
- import todoWriteDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/todo-write.md" with { type: "text" };
9
- import type { ToolSession } from "@oh-my-pi/pi-coding-agent/sdk";
10
- import { renderStatusLine, renderTreeList } from "@oh-my-pi/pi-coding-agent/tui";
11
5
  import type { Component } from "@oh-my-pi/pi-tui";
12
6
  import { Text } from "@oh-my-pi/pi-tui";
13
7
  import { logger } from "@oh-my-pi/pi-utils";
14
8
  import { Type } from "@sinclair/typebox";
15
9
  import chalk from "chalk";
10
+ import { renderPromptTemplate } from "../config/prompt-templates";
11
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
12
+ import type { Theme } from "../modes/theme/theme";
13
+ import todoWriteDescription from "../prompts/tools/todo-write.md" with { type: "text" };
14
+ import type { ToolSession } from "../sdk";
15
+ import { renderStatusLine, renderTreeList } from "../tui";
16
16
  import { PREVIEW_LIMITS } from "./render-utils";
17
17
 
18
18
  const todoWriteSchema = Type.Object({
@@ -63,7 +63,7 @@ function normalizeTodoStatus(status?: string): TodoStatus {
63
63
  }
64
64
 
65
65
  function normalizeTodos(items: Array<{ id?: string; content?: string; status?: string }>): TodoItem[] {
66
- return items.map((item) => {
66
+ return items.map(item => {
67
67
  if (!item.content) {
68
68
  throw new Error("Todo content is required.");
69
69
  }
@@ -82,7 +82,7 @@ function normalizeTodos(items: Array<{ id?: string; content?: string; status?: s
82
82
  function validateSequentialTodos(todos: TodoItem[]): { valid: boolean; error?: string } {
83
83
  if (todos.length === 0) return { valid: true };
84
84
 
85
- const firstIncompleteIndex = todos.findIndex((todo) => todo.status !== "completed");
85
+ const firstIncompleteIndex = todos.findIndex(todo => todo.status !== "completed");
86
86
  if (firstIncompleteIndex >= 0) {
87
87
  for (let i = firstIncompleteIndex + 1; i < todos.length; i++) {
88
88
  if (todos[i].status === "completed") {
@@ -128,9 +128,9 @@ async function loadTodoFile(filePath: string): Promise<TodoFile | null> {
128
128
 
129
129
  function formatTodoSummary(todos: TodoItem[]): string {
130
130
  if (todos.length === 0) return "Todo list cleared.";
131
- const completed = todos.filter((t) => t.status === "completed").length;
132
- const inProgress = todos.filter((t) => t.status === "in_progress").length;
133
- const pending = todos.filter((t) => t.status === "pending").length;
131
+ const completed = todos.filter(t => t.status === "completed").length;
132
+ const inProgress = todos.filter(t => t.status === "in_progress").length;
133
+ const pending = todos.filter(t => t.status === "pending").length;
134
134
  return `Saved ${todos.length} todos (${pending} pending, ${inProgress} in progress, ${completed} completed).`;
135
135
  }
136
136
 
@@ -237,7 +237,7 @@ export const todoWriteToolRenderer = {
237
237
  uiTheme,
238
238
  );
239
239
  if (todos.length === 0) {
240
- const fallback = result.content?.find((c) => c.type === "text")?.text ?? "No todos";
240
+ const fallback = result.content?.find(c => c.type === "text")?.text ?? "No todos";
241
241
  return new Text([header, uiTheme.fg("dim", fallback)].join("\n"), 0, 0);
242
242
  }
243
243
  const lines = renderTreeList(
@@ -246,7 +246,7 @@ export const todoWriteToolRenderer = {
246
246
  expanded,
247
247
  maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS,
248
248
  itemType: "todo",
249
- renderItem: (todo) => formatTodoLine(todo, uiTheme, ""),
249
+ renderItem: todo => formatTodoLine(todo, uiTheme, ""),
250
250
  },
251
251
  uiTheme,
252
252
  );
@@ -39,7 +39,7 @@ export class MultiError extends ToolError {
39
39
  readonly errors: ErrorEntry[];
40
40
 
41
41
  constructor(errors: ErrorEntry[]) {
42
- super(errors.map((e) => e.message).join("; "));
42
+ super(errors.map(e => e.message).join("; "));
43
43
  this.name = "MultiError";
44
44
  this.errors = errors;
45
45
  }
@@ -49,11 +49,11 @@ export class MultiError extends ToolError {
49
49
  const e = this.errors[0];
50
50
  return e.context ? `${e.context}: ${e.message}` : e.message;
51
51
  }
52
- return this.errors.map((e) => (e.context ? `${e.context}: ${e.message}` : e.message)).join("\n");
52
+ return this.errors.map(e => (e.context ? `${e.context}: ${e.message}` : e.message)).join("\n");
53
53
  }
54
54
 
55
55
  static from(errors: Array<string | ErrorEntry>): MultiError {
56
- return new MultiError(errors.map((e) => (typeof e === "string" ? { message: e } : e)));
56
+ return new MultiError(errors.map(e => (typeof e === "string" ? { message: e } : e)));
57
57
  }
58
58
  }
59
59
 
@@ -1,13 +1,8 @@
1
1
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
2
  import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
3
- import type { OutputSummary } from "@oh-my-pi/pi-coding-agent/session/streaming-output";
4
- import type {
5
- OutputMeta,
6
- TruncationOptions,
7
- TruncationSummaryOptions,
8
- TruncationTextOptions,
9
- } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
10
- import { outputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
3
+ import type { OutputSummary } from "../session/streaming-output";
4
+ import type { OutputMeta, TruncationOptions, TruncationSummaryOptions, TruncationTextOptions } from "./output-meta";
5
+ import { outputMeta } from "./output-meta";
11
6
  import type { TruncationResult } from "./truncate";
12
7
 
13
8
  type ToolContent = Array<TextContent | ImageContent>;
@@ -5,23 +5,18 @@ import type {
5
5
  AgentToolUpdateCallback,
6
6
  ToolCallContext,
7
7
  } from "@oh-my-pi/pi-agent-core";
8
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
9
- import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
10
- import {
11
- createLspWritethrough,
12
- type FileDiagnosticsResult,
13
- type WritethroughCallback,
14
- writethroughNoop,
15
- } from "@oh-my-pi/pi-coding-agent/lsp";
16
- import { getLanguageFromPath, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
17
- import writeDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/write.md" with { type: "text" };
18
- import type { ToolSession } from "@oh-my-pi/pi-coding-agent/sdk";
19
- import { type OutputMeta, outputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
20
- import { renderCodeCell, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
21
8
  import type { Component } from "@oh-my-pi/pi-tui";
22
9
  import { Text } from "@oh-my-pi/pi-tui";
23
10
  import { untilAborted } from "@oh-my-pi/pi-utils";
24
11
  import { Type } from "@sinclair/typebox";
12
+ import { renderPromptTemplate } from "../config/prompt-templates";
13
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
14
+ import { createLspWritethrough, type FileDiagnosticsResult, type WritethroughCallback, writethroughNoop } from "../lsp";
15
+ import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
16
+ import writeDescription from "../prompts/tools/write.md" with { type: "text" };
17
+ import type { ToolSession } from "../sdk";
18
+ import { renderCodeCell, renderStatusLine } from "../tui";
19
+ import { type OutputMeta, outputMeta } from "./output-meta";
25
20
  import { resolveToCwd } from "./path-utils";
26
21
  import { formatDiagnostics, shortenPath } from "./render-utils";
27
22
  import type { RenderCallOptions } from "./renderers";
@@ -49,7 +44,7 @@ function getLspBatchRequest(toolCall: ToolCallContext | undefined): { id: string
49
44
  if (!hasOtherWrites) {
50
45
  return undefined;
51
46
  }
52
- const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some((call) => LSP_BATCH_TOOLS.has(call.name));
47
+ const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some(call => LSP_BATCH_TOOLS.has(call.name));
53
48
  return { id: toolCall.batchId, flush: !hasLaterWrites };
54
49
  }
55
50
 
@@ -231,12 +226,12 @@ export const writeToolRenderer = {
231
226
  }
232
227
 
233
228
  if (result.details?.diagnostics) {
234
- const diagText = formatDiagnostics(result.details.diagnostics, expanded, uiTheme, (fp) =>
229
+ const diagText = formatDiagnostics(result.details.diagnostics, expanded, uiTheme, fp =>
235
230
  uiTheme.getLangIcon(getLanguageFromPath(fp)),
236
231
  );
237
232
  if (diagText.trim()) {
238
233
  const diagLines = diagText.split("\n");
239
- const firstNonEmpty = diagLines.findIndex((line) => line.trim());
234
+ const firstNonEmpty = diagLines.findIndex(line => line.trim());
240
235
  outputLines.push(...(firstNonEmpty >= 0 ? diagLines.slice(firstNonEmpty) : []));
241
236
  }
242
237
  }
@@ -1,14 +1,8 @@
1
1
  /**
2
2
  * Render a code cell with optional output section.
3
3
  */
4
-
5
- import { highlightCode, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
6
- import {
7
- formatDuration,
8
- formatExpandHint,
9
- formatMoreItems,
10
- replaceTabs,
11
- } from "@oh-my-pi/pi-coding-agent/tools/render-utils";
4
+ import { highlightCode, type Theme } from "../modes/theme/theme";
5
+ import { formatDuration, formatExpandHint, formatMoreItems, replaceTabs } from "../tools/render-utils";
12
6
  import { renderOutputBlock } from "./output-block";
13
7
  import type { State } from "./types";
14
8
  import { getStateIcon } from "./utils";
@@ -96,7 +90,7 @@ export function renderCodeCell(options: CodeCellOptions, theme: Theme): string[]
96
90
  const maxLines = expanded ? rawLines.length : Math.min(rawLines.length, outputMaxLines);
97
91
  const displayLines = rawLines
98
92
  .slice(0, maxLines)
99
- .map((line) => (line.includes("\x1b[") ? line : theme.fg("toolOutput", line)));
93
+ .map(line => (line.includes("\x1b[") ? line : theme.fg("toolOutput", line)));
100
94
  outputLines.push(...displayLines);
101
95
  const remaining = rawLines.length - maxLines;
102
96
  if (remaining > 0) {
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Render file listings with optional icons and metadata.
3
3
  */
4
-
5
- import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
6
- import { getLanguageFromPath } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
4
+ import type { Theme } from "../modes/theme/theme";
5
+ import { getLanguageFromPath } from "../modes/theme/theme";
7
6
  import { renderTreeList } from "./tree-list";
8
7
 
9
8
  export interface FileEntry {
@@ -28,7 +27,7 @@ export function renderFileList(options: FileListOptions, theme: Theme): string[]
28
27
  expanded,
29
28
  maxCollapsed,
30
29
  itemType: "file",
31
- renderItem: (entry) => {
30
+ renderItem: entry => {
32
31
  const isDirectory = entry.isDirectory ?? entry.path.endsWith("/");
33
32
  const displayPath = isDirectory && entry.path.endsWith("/") ? entry.path : entry.path;
34
33
  const lang = isDirectory ? undefined : getLanguageFromPath(displayPath);
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Bordered output container with optional header and sections.
3
3
  */
4
-
5
- import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
6
4
  import { visibleWidth } from "@oh-my-pi/pi-tui";
5
+ import type { Theme } from "../modes/theme/theme";
7
6
  import type { State } from "./types";
8
7
  import { getStateBgColor, padToWidth, truncateToWidth } from "./utils";
9
8
 
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Standardized status header rendering for tool output.
3
3
  */
4
-
5
- import type { Theme, ThemeColor } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
4
+ import type { Theme, ThemeColor } from "../modes/theme/theme";
6
5
  import type { IconType } from "./types";
7
6
  import { getStateIcon } from "./utils";
8
7
 
@@ -31,7 +30,7 @@ export function renderStatusLine(options: StatusLineOptions, theme: Theme): stri
31
30
  line += ` ${theme.fg(color, `${theme.format.bracketLeft}${label}${theme.format.bracketRight}`)}`;
32
31
  }
33
32
 
34
- const meta = options.meta?.filter((value) => value.trim().length > 0) ?? [];
33
+ const meta = options.meta?.filter(value => value.trim().length > 0) ?? [];
35
34
  if (meta.length > 0) {
36
35
  line += ` ${theme.fg("dim", meta.join(theme.sep.dot))}`;
37
36
  }
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Hierarchical tree list rendering helper.
3
3
  */
4
-
5
- import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
6
- import { formatMoreItems } from "@oh-my-pi/pi-coding-agent/tools/render-utils";
4
+ import type { Theme } from "../modes/theme/theme";
5
+ import { formatMoreItems } from "../tools/render-utils";
7
6
  import type { TreeContext } from "./types";
8
7
  import { getTreeBranch, getTreeContinuePrefix } from "./utils";
9
8
 
package/src/tui/types.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Shared types for TUI rendering components.
3
3
  */
4
-
5
- import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
4
+ import type { Theme } from "../modes/theme/theme";
6
5
 
7
6
  export type State = "pending" | "running" | "success" | "error" | "warning";
8
7
  export type IconType = "success" | "error" | "running" | "pending" | "warning" | "info";
package/src/tui/utils.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * Shared helpers for tool-rendered UI components.
3
3
  */
4
-
5
- import type { Theme, ThemeBg } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
6
4
  import { truncateToWidth as truncateToWidthBase, visibleWidth } from "@oh-my-pi/pi-tui";
5
+ import type { Theme, ThemeBg } from "../modes/theme/theme";
7
6
  import type { IconType, State } from "./types";
8
7
 
9
8
  export function buildTreePrefix(ancestors: boolean[], theme: Theme): string {
10
- return ancestors.map((hasNext) => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
9
+ return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
11
10
  }
12
11
 
13
12
  export function getTreeBranch(isLast: boolean, theme: Theme): string {
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from "node:fs";
1
+ import { isEnoent, logger } from "@oh-my-pi/pi-utils";
2
2
 
3
3
  export interface ChangelogEntry {
4
4
  major: number;
@@ -11,13 +11,9 @@ export interface ChangelogEntry {
11
11
  * Parse changelog entries from CHANGELOG.md
12
12
  * Scans for ## lines and collects content until next ## or EOF
13
13
  */
14
- export function parseChangelog(changelogPath: string): ChangelogEntry[] {
15
- if (!existsSync(changelogPath)) {
16
- return [];
17
- }
18
-
14
+ export async function parseChangelog(changelogPath: string): Promise<ChangelogEntry[]> {
19
15
  try {
20
- const content = readFileSync(changelogPath, "utf-8");
16
+ const content = await Bun.file(changelogPath).text();
21
17
  const lines = content.split("\n");
22
18
  const entries: ChangelogEntry[] = [];
23
19
 
@@ -65,7 +61,10 @@ export function parseChangelog(changelogPath: string): ChangelogEntry[] {
65
61
 
66
62
  return entries;
67
63
  } catch (error) {
68
- console.error(`Warning: Could not parse changelog: ${error}`);
64
+ if (isEnoent(error)) {
65
+ return [];
66
+ }
67
+ logger.error(`Warning: Could not parse changelog: ${error}`);
69
68
  return [];
70
69
  }
71
70
  }
@@ -92,8 +91,8 @@ export function getNewEntries(entries: ChangelogEntry[], lastVersion: string): C
92
91
  content: "",
93
92
  };
94
93
 
95
- return entries.filter((entry) => compareVersions(entry, last) > 0);
94
+ return entries.filter(entry => compareVersions(entry, last) > 0);
96
95
  }
97
96
 
98
97
  // Re-export getChangelogPath from paths.ts for convenience
99
- export { getChangelogPath } from "@oh-my-pi/pi-coding-agent/config";
98
+ export { getChangelogPath } from "../config";
@@ -1,5 +1,5 @@
1
- import { unlink } from "node:fs/promises";
2
- import { platform } from "node:os";
1
+ import * as fs from "node:fs/promises";
2
+ import * as os from "node:os";
3
3
  import { $ } from "bun";
4
4
  import { nanoid } from "nanoid";
5
5
 
@@ -16,23 +16,23 @@ function baseMimeType(mimeType: string): string {
16
16
 
17
17
  function selectPreferredImageMimeType(mimeTypes: string[]): string | null {
18
18
  const normalized = mimeTypes
19
- .map((t) => t.trim())
19
+ .map(t => t.trim())
20
20
  .filter(Boolean)
21
- .map((t) => ({ raw: t, base: baseMimeType(t) }));
21
+ .map(t => ({ raw: t, base: baseMimeType(t) }));
22
22
 
23
23
  for (const preferred of PREFERRED_IMAGE_MIME_TYPES) {
24
- const match = normalized.find((t) => t.base === preferred);
24
+ const match = normalized.find(t => t.base === preferred);
25
25
  if (match) {
26
26
  return match.raw;
27
27
  }
28
28
  }
29
29
 
30
- const anyImage = normalized.find((t) => t.base.startsWith("image/"));
30
+ const anyImage = normalized.find(t => t.base.startsWith("image/"));
31
31
  return anyImage?.raw ?? null;
32
32
  }
33
33
 
34
34
  export async function copyToClipboard(text: string): Promise<void> {
35
- const p = platform();
35
+ const p = os.platform();
36
36
  const timeout = 5000;
37
37
 
38
38
  try {
@@ -83,7 +83,7 @@ export interface ClipboardImage {
83
83
  * - Windows: uses PowerShell
84
84
  */
85
85
  export async function readImageFromClipboard(): Promise<ClipboardImage | null> {
86
- const p = platform();
86
+ const p = os.platform();
87
87
  const timeout = 3000;
88
88
  let promise: Promise<ClipboardImage | null>;
89
89
  switch (p) {
@@ -125,7 +125,7 @@ async function readImageWayland(): Promise<ClipboardReadResult> {
125
125
 
126
126
  const typeList = types
127
127
  .split(/\r?\n/)
128
- .map((t) => t.trim())
128
+ .map(t => t.trim())
129
129
  .filter(Boolean);
130
130
 
131
131
  const selectedType = selectPreferredImageMimeType(typeList);
@@ -149,7 +149,7 @@ async function readImageX11(): Promise<ClipboardReadResult> {
149
149
 
150
150
  const candidateTypes = targets
151
151
  .split(/\r?\n/)
152
- .map((t) => t.trim())
152
+ .map(t => t.trim())
153
153
  .filter(Boolean);
154
154
 
155
155
  const selectedType = selectPreferredImageMimeType(candidateTypes);
@@ -207,7 +207,7 @@ async function readImageMacOS(): Promise<ClipboardImage | null> {
207
207
  const file = Bun.file(tempFile);
208
208
  if (await file.exists()) {
209
209
  const buffer = await file.bytes();
210
- await unlink(tempFile).catch(() => {});
210
+ await fs.unlink(tempFile).catch(() => {});
211
211
 
212
212
  if (buffer.length > 0) {
213
213
  return {
@@ -5,18 +5,12 @@
5
5
  * we automatically inject the file contents as a FileMentionMessage
6
6
  * so the agent doesn't need to read them manually.
7
7
  */
8
-
9
8
  import path from "node:path";
10
9
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
11
- import type { FileMentionMessage } from "@oh-my-pi/pi-coding-agent/session/messages";
12
- import { resolveReadPath } from "@oh-my-pi/pi-coding-agent/tools/path-utils";
13
- import { formatAge } from "@oh-my-pi/pi-coding-agent/tools/render-utils";
14
- import {
15
- DEFAULT_MAX_BYTES,
16
- formatSize,
17
- truncateHead,
18
- truncateStringToBytesFromStart,
19
- } from "@oh-my-pi/pi-coding-agent/tools/truncate";
10
+ import type { FileMentionMessage } from "../session/messages";
11
+ import { resolveReadPath } from "../tools/path-utils";
12
+ import { formatAge } from "../tools/render-utils";
13
+ import { DEFAULT_MAX_BYTES, formatSize, truncateHead, truncateStringToBytesFromStart } from "../tools/truncate";
20
14
 
21
15
  /** Regex to match @filepath patterns in text */
22
16
  const FILE_MENTION_REGEX = /@([^\s@]+)/g;
@@ -38,7 +38,9 @@ export class FrontmatterError extends Error {
38
38
  }
39
39
 
40
40
  export interface FrontmatterOptions {
41
- /** Source of the content */
41
+ /** Source of the content (alias: source) */
42
+ location?: unknown;
43
+ /** Source of the content (alias for location) */
42
44
  source?: unknown;
43
45
  /** Fallback frontmatter values */
44
46
  fallback?: Record<string, unknown>;
@@ -56,7 +58,8 @@ export function parseFrontmatter(
56
58
  content: string,
57
59
  options?: FrontmatterOptions,
58
60
  ): { frontmatter: Record<string, unknown>; body: string } {
59
- const { source, fallback, normalize = true, level = "warn" } = options ?? {};
61
+ const { location, source, fallback, normalize = true, level = "warn" } = options ?? {};
62
+ const loc = location ?? source;
60
63
  const frontmatter: Record<string, unknown> = Object.assign({}, fallback);
61
64
 
62
65
  const normalized = normalize ? stripHtmlComments(content.replace(/\r\n/g, "\n").replace(/\r/g, "\n")) : content;
@@ -77,7 +80,7 @@ export function parseFrontmatter(
77
80
  const loaded = YAML.parse(metadata.replaceAll("\t", " ")) as Record<string, unknown> | null;
78
81
  return { frontmatter: Object.assign(frontmatter, loaded), body: body };
79
82
  } catch (error) {
80
- const err = new FrontmatterError(toError(error), source ?? `Inline '${truncate(content, 64)}'`);
83
+ const err = new FrontmatterError(toError(error), loc ?? `Inline '${truncate(content, 64)}'`);
81
84
  if (level === "warn" || level === "fatal") {
82
85
  logger.warn("Failed to parse YAML frontmatter", { err: err.toString() });
83
86
  }
@@ -71,7 +71,7 @@ export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) =>
71
71
  const tokens = query
72
72
  .trim()
73
73
  .split(/\s+/)
74
- .filter((t) => t.length > 0);
74
+ .filter(t => t.length > 0);
75
75
 
76
76
  if (tokens.length === 0) {
77
77
  return items;
@@ -104,5 +104,5 @@ export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) =>
104
104
  // Sort by score (asc, lower is better)
105
105
  results.sort((a, b) => a.totalScore - b.totalScore);
106
106
 
107
- return results.map((r) => r.item);
107
+ return results.map(r => r.item);
108
108
  }
@@ -1,4 +1,4 @@
1
- import * as photon from "@oh-my-pi/pi-coding-agent/vendor/photon";
1
+ import * as photon from "../vendor/photon";
2
2
 
3
3
  /**
4
4
  * Convert image to PNG format for terminal display.
@@ -1,5 +1,5 @@
1
1
  import type { ImageContent } from "@oh-my-pi/pi-ai";
2
- import * as photon from "@oh-my-pi/pi-coding-agent/vendor/photon";
2
+ import * as photon from "../vendor/photon";
3
3
 
4
4
  export interface ImageResizeOptions {
5
5
  maxWidth?: number; // Default: 2000
package/src/utils/mime.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { open } from "node:fs/promises";
1
+ import * as fs from "node:fs/promises";
2
2
  import { fileTypeFromBuffer } from "file-type";
3
3
 
4
4
  const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
@@ -6,7 +6,7 @@ const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image
6
6
  const FILE_TYPE_SNIFF_BYTES = 4100;
7
7
 
8
8
  export async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {
9
- const fileHandle = await open(filePath, "r");
9
+ const fileHandle = await fs.open(filePath, "r");
10
10
  try {
11
11
  const buffer = Buffer.alloc(FILE_TYPE_SNIFF_BYTES);
12
12
  const { bytesRead } = await fileHandle.read(buffer, 0, FILE_TYPE_SNIFF_BYTES, 0);