@oh-my-pi/pi-coding-agent 8.0.20 → 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 (421) hide show
  1. package/CHANGELOG.md +125 -0
  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 +54 -23
  38. package/scripts/format-prompts.ts +0 -1
  39. package/src/capability/context-file.ts +3 -4
  40. package/src/capability/extension-module.ts +3 -4
  41. package/src/capability/extension.ts +3 -4
  42. package/src/capability/fs.ts +20 -21
  43. package/src/capability/hook.ts +3 -4
  44. package/src/capability/index.ts +15 -16
  45. package/src/capability/instruction.ts +3 -4
  46. package/src/capability/mcp.ts +3 -4
  47. package/src/capability/prompt.ts +3 -4
  48. package/src/capability/rule.ts +3 -4
  49. package/src/capability/settings.ts +2 -3
  50. package/src/capability/skill.ts +3 -4
  51. package/src/capability/slash-command.ts +3 -4
  52. package/src/capability/ssh.ts +3 -4
  53. package/src/capability/system-prompt.ts +3 -4
  54. package/src/capability/tool.ts +3 -4
  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 -25
  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 +307 -0
  66. package/src/commit/agentic/fallback.ts +96 -0
  67. package/src/commit/agentic/index.ts +351 -0
  68. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  69. package/src/commit/agentic/prompts/session-user.md +26 -0
  70. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  71. package/src/commit/agentic/prompts/system.md +40 -0
  72. package/src/commit/agentic/state.ts +69 -0
  73. package/src/commit/agentic/tools/analyze-file.ts +131 -0
  74. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  75. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  76. package/src/commit/agentic/tools/git-overview.ts +84 -0
  77. package/src/commit/agentic/tools/index.ts +56 -0
  78. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  79. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  80. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  81. package/src/commit/agentic/tools/split-commit.ts +280 -0
  82. package/src/commit/agentic/topo-sort.ts +44 -0
  83. package/src/commit/agentic/trivial.ts +51 -0
  84. package/src/commit/agentic/validation.ts +200 -0
  85. package/src/commit/analysis/conventional.ts +165 -0
  86. package/src/commit/analysis/index.ts +4 -0
  87. package/src/commit/analysis/scope.ts +242 -0
  88. package/src/commit/analysis/summary.ts +112 -0
  89. package/src/commit/analysis/validation.ts +66 -0
  90. package/src/commit/changelog/detect.ts +36 -0
  91. package/src/commit/changelog/generate.ts +110 -0
  92. package/src/commit/changelog/index.ts +233 -0
  93. package/src/commit/changelog/parse.ts +44 -0
  94. package/src/commit/cli.ts +93 -0
  95. package/src/commit/git/diff.ts +148 -0
  96. package/src/commit/git/errors.ts +11 -0
  97. package/src/commit/git/index.ts +212 -0
  98. package/src/commit/git/operations.ts +53 -0
  99. package/src/commit/index.ts +5 -0
  100. package/src/commit/map-reduce/index.ts +63 -0
  101. package/src/commit/map-reduce/map-phase.ts +178 -0
  102. package/src/commit/map-reduce/reduce-phase.ts +145 -0
  103. package/src/commit/map-reduce/utils.ts +9 -0
  104. package/src/commit/message.ts +11 -0
  105. package/src/commit/model-selection.ts +80 -0
  106. package/src/commit/pipeline.ts +240 -0
  107. package/src/commit/prompts/analysis-system.md +155 -0
  108. package/src/commit/prompts/analysis-user.md +41 -0
  109. package/src/commit/prompts/changelog-system.md +56 -0
  110. package/src/commit/prompts/changelog-user.md +19 -0
  111. package/src/commit/prompts/file-observer-system.md +26 -0
  112. package/src/commit/prompts/file-observer-user.md +9 -0
  113. package/src/commit/prompts/reduce-system.md +60 -0
  114. package/src/commit/prompts/reduce-user.md +17 -0
  115. package/src/commit/prompts/summary-retry.md +4 -0
  116. package/src/commit/prompts/summary-system.md +52 -0
  117. package/src/commit/prompts/summary-user.md +13 -0
  118. package/src/commit/prompts/types-description.md +2 -0
  119. package/src/commit/types.ts +109 -0
  120. package/src/commit/utils/exclusions.ts +42 -0
  121. package/src/config/file-lock.ts +121 -0
  122. package/src/config/keybindings.ts +6 -8
  123. package/src/config/model-registry.ts +65 -38
  124. package/src/config/model-resolver.ts +18 -19
  125. package/src/config/prompt-templates.ts +11 -11
  126. package/src/config/settings-manager.ts +141 -50
  127. package/src/config.ts +64 -66
  128. package/src/cursor.ts +11 -9
  129. package/src/discovery/agents-md.ts +11 -12
  130. package/src/discovery/builtin.ts +68 -73
  131. package/src/discovery/claude.ts +41 -42
  132. package/src/discovery/cline.ts +11 -12
  133. package/src/discovery/codex.ts +52 -53
  134. package/src/discovery/cursor.ts +9 -10
  135. package/src/discovery/gemini.ts +17 -22
  136. package/src/discovery/github.ts +13 -14
  137. package/src/discovery/helpers.ts +35 -34
  138. package/src/discovery/index.ts +22 -24
  139. package/src/discovery/mcp-json.ts +8 -9
  140. package/src/discovery/ssh.ts +8 -9
  141. package/src/discovery/vscode.ts +4 -5
  142. package/src/discovery/windsurf.ts +6 -7
  143. package/src/exa/company.ts +1 -2
  144. package/src/exa/index.ts +2 -3
  145. package/src/exa/linkedin.ts +1 -2
  146. package/src/exa/mcp-client.ts +14 -16
  147. package/src/exa/render.ts +10 -11
  148. package/src/exa/researcher.ts +1 -2
  149. package/src/exa/search.ts +1 -2
  150. package/src/exa/types.ts +0 -1
  151. package/src/exa/websets.ts +1 -2
  152. package/src/exec/bash-executor.ts +3 -4
  153. package/src/exec/exec.ts +0 -1
  154. package/src/export/custom-share.ts +5 -6
  155. package/src/export/html/index.ts +24 -21
  156. package/src/export/ttsr.ts +2 -3
  157. package/src/extensibility/custom-commands/bundled/review/index.ts +7 -8
  158. package/src/extensibility/custom-commands/loader.ts +18 -15
  159. package/src/extensibility/custom-commands/types.ts +2 -3
  160. package/src/extensibility/custom-tools/loader.ts +11 -12
  161. package/src/extensibility/custom-tools/types.ts +7 -8
  162. package/src/extensibility/custom-tools/wrapper.ts +2 -3
  163. package/src/extensibility/extensions/loader.ts +76 -54
  164. package/src/extensibility/extensions/runner.ts +11 -12
  165. package/src/extensibility/extensions/types.ts +20 -27
  166. package/src/extensibility/extensions/wrapper.ts +3 -4
  167. package/src/extensibility/hooks/index.ts +1 -1
  168. package/src/extensibility/hooks/loader.ts +9 -10
  169. package/src/extensibility/hooks/runner.ts +7 -8
  170. package/src/extensibility/hooks/tool-wrapper.ts +0 -1
  171. package/src/extensibility/hooks/types.ts +11 -18
  172. package/src/extensibility/plugins/doctor.ts +3 -3
  173. package/src/extensibility/plugins/installer.ts +27 -27
  174. package/src/extensibility/plugins/loader.ts +59 -56
  175. package/src/extensibility/plugins/manager.ts +211 -171
  176. package/src/extensibility/plugins/parser.ts +1 -1
  177. package/src/extensibility/plugins/paths.ts +8 -8
  178. package/src/extensibility/skills.ts +63 -60
  179. package/src/extensibility/slash-commands.ts +10 -10
  180. package/src/index.ts +54 -54
  181. package/src/internal-urls/agent-protocol.ts +21 -11
  182. package/src/internal-urls/artifact-protocol.ts +17 -13
  183. package/src/internal-urls/router.ts +1 -2
  184. package/src/internal-urls/rule-protocol.ts +3 -4
  185. package/src/internal-urls/skill-protocol.ts +3 -4
  186. package/src/ipy/executor.ts +109 -9
  187. package/src/ipy/gateway-coordinator.ts +79 -90
  188. package/src/ipy/kernel.ts +32 -30
  189. package/src/ipy/modules.ts +13 -13
  190. package/src/lsp/client.ts +21 -10
  191. package/src/lsp/clients/biome-client.ts +1 -2
  192. package/src/lsp/clients/index.ts +3 -3
  193. package/src/lsp/clients/lsp-linter-client.ts +4 -5
  194. package/src/lsp/config.ts +15 -15
  195. package/src/lsp/edits.ts +4 -5
  196. package/src/lsp/index.ts +43 -44
  197. package/src/lsp/lspmux.ts +8 -8
  198. package/src/lsp/render.ts +99 -61
  199. package/src/lsp/utils.ts +3 -3
  200. package/src/main.ts +71 -37
  201. package/src/mcp/client.ts +2 -3
  202. package/src/mcp/config.ts +5 -6
  203. package/src/mcp/json-rpc.ts +0 -1
  204. package/src/mcp/loader.ts +6 -7
  205. package/src/mcp/manager.ts +17 -18
  206. package/src/mcp/tool-bridge.ts +4 -9
  207. package/src/mcp/tool-cache.ts +2 -3
  208. package/src/mcp/transports/http.ts +2 -4
  209. package/src/mcp/transports/stdio.ts +1 -2
  210. package/src/migrations.ts +63 -52
  211. package/src/modes/components/armin.ts +4 -5
  212. package/src/modes/components/assistant-message.ts +33 -5
  213. package/src/modes/components/bash-execution.ts +7 -8
  214. package/src/modes/components/bordered-loader.ts +3 -3
  215. package/src/modes/components/branch-summary-message.ts +3 -3
  216. package/src/modes/components/compaction-summary-message.ts +3 -3
  217. package/src/modes/components/countdown-timer.ts +0 -1
  218. package/src/modes/components/custom-message.ts +5 -5
  219. package/src/modes/components/diff.ts +1 -1
  220. package/src/modes/components/dynamic-border.ts +2 -2
  221. package/src/modes/components/extensions/extension-dashboard.ts +6 -7
  222. package/src/modes/components/extensions/extension-list.ts +2 -3
  223. package/src/modes/components/extensions/inspector-panel.ts +3 -4
  224. package/src/modes/components/extensions/state-manager.ts +25 -26
  225. package/src/modes/components/extensions/types.ts +1 -2
  226. package/src/modes/components/footer.ts +47 -43
  227. package/src/modes/components/history-search.ts +2 -2
  228. package/src/modes/components/hook-editor.ts +3 -4
  229. package/src/modes/components/hook-input.ts +2 -3
  230. package/src/modes/components/hook-message.ts +5 -5
  231. package/src/modes/components/hook-selector.ts +2 -3
  232. package/src/modes/components/keybinding-hints.ts +2 -3
  233. package/src/modes/components/login-dialog.ts +2 -2
  234. package/src/modes/components/model-selector.ts +12 -12
  235. package/src/modes/components/oauth-selector.ts +2 -2
  236. package/src/modes/components/plugin-settings.ts +20 -20
  237. package/src/modes/components/python-execution.ts +7 -8
  238. package/src/modes/components/queue-mode-selector.ts +3 -3
  239. package/src/modes/components/read-tool-group.ts +2 -2
  240. package/src/modes/components/session-selector.ts +4 -4
  241. package/src/modes/components/settings-defs.ts +77 -69
  242. package/src/modes/components/settings-selector.ts +16 -16
  243. package/src/modes/components/show-images-selector.ts +2 -2
  244. package/src/modes/components/status-line/segments.ts +4 -4
  245. package/src/modes/components/status-line/separators.ts +1 -1
  246. package/src/modes/components/status-line/types.ts +2 -2
  247. package/src/modes/components/status-line-segment-editor.ts +7 -8
  248. package/src/modes/components/status-line.ts +12 -12
  249. package/src/modes/components/theme-selector.ts +8 -7
  250. package/src/modes/components/thinking-selector.ts +4 -4
  251. package/src/modes/components/todo-display.ts +2 -2
  252. package/src/modes/components/todo-reminder.ts +4 -4
  253. package/src/modes/components/tool-execution.ts +16 -19
  254. package/src/modes/components/tree-selector.ts +12 -12
  255. package/src/modes/components/ttsr-notification.ts +5 -5
  256. package/src/modes/components/user-message-selector.ts +1 -1
  257. package/src/modes/components/user-message.ts +1 -1
  258. package/src/modes/components/visual-truncate.ts +0 -1
  259. package/src/modes/components/welcome.ts +4 -4
  260. package/src/modes/controllers/command-controller.ts +46 -47
  261. package/src/modes/controllers/event-controller.ts +16 -20
  262. package/src/modes/controllers/extension-ui-controller.ts +40 -46
  263. package/src/modes/controllers/input-controller.ts +17 -18
  264. package/src/modes/controllers/selector-controller.ts +103 -91
  265. package/src/modes/index.ts +3 -3
  266. package/src/modes/interactive-mode.ts +31 -31
  267. package/src/modes/print-mode.ts +12 -13
  268. package/src/modes/rpc/rpc-client.ts +7 -8
  269. package/src/modes/rpc/rpc-mode.ts +24 -28
  270. package/src/modes/rpc/rpc-types.ts +3 -4
  271. package/src/modes/theme/mermaid-cache.ts +89 -0
  272. package/src/modes/theme/theme.ts +130 -53
  273. package/src/modes/types.ts +10 -10
  274. package/src/modes/utils/ui-helpers.ts +17 -17
  275. package/src/patch/applicator.ts +18 -19
  276. package/src/patch/diff.ts +1 -2
  277. package/src/patch/fuzzy.ts +1 -2
  278. package/src/patch/index.ts +11 -18
  279. package/src/patch/normalize.ts +4 -4
  280. package/src/patch/normative.ts +1 -2
  281. package/src/patch/parser.ts +8 -9
  282. package/src/patch/shared.ts +43 -16
  283. package/src/prompts/tools/task.md +2 -0
  284. package/src/sdk.ts +100 -65
  285. package/src/session/agent-session.ts +84 -85
  286. package/src/session/agent-storage.ts +43 -39
  287. package/src/session/artifacts.ts +32 -10
  288. package/src/session/auth-storage.ts +50 -39
  289. package/src/session/compaction/branch-summarization.ts +7 -10
  290. package/src/session/compaction/compaction.ts +8 -19
  291. package/src/session/compaction/utils.ts +6 -9
  292. package/src/session/history-storage.ts +10 -10
  293. package/src/session/messages.ts +4 -5
  294. package/src/session/session-manager.ts +76 -65
  295. package/src/session/session-storage.ts +57 -69
  296. package/src/session/storage-migration.ts +14 -56
  297. package/src/session/streaming-output.ts +2 -2
  298. package/src/ssh/connection-manager.ts +43 -50
  299. package/src/ssh/ssh-executor.ts +2 -2
  300. package/src/ssh/sshfs-mount.ts +11 -18
  301. package/src/system-prompt.ts +28 -35
  302. package/src/task/agents.ts +45 -30
  303. package/src/task/commands.ts +6 -7
  304. package/src/task/discovery.ts +39 -76
  305. package/src/task/executor.ts +14 -15
  306. package/src/task/index.ts +40 -34
  307. package/src/task/output-manager.ts +93 -0
  308. package/src/task/parallel.ts +0 -1
  309. package/src/task/render.ts +24 -30
  310. package/src/task/subprocess-tool-registry.ts +1 -2
  311. package/src/task/worker-protocol.ts +3 -3
  312. package/src/task/worker.ts +33 -39
  313. package/src/task/worktree.ts +19 -19
  314. package/src/tools/ask.ts +41 -20
  315. package/src/tools/bash-interceptor.ts +1 -5
  316. package/src/tools/bash.ts +91 -97
  317. package/src/tools/calculator.ts +49 -47
  318. package/src/tools/complete.ts +4 -5
  319. package/src/tools/context.ts +2 -2
  320. package/src/tools/fetch.ts +84 -124
  321. package/src/tools/find.ts +94 -98
  322. package/src/tools/gemini-image.ts +14 -14
  323. package/src/tools/grep.ts +100 -116
  324. package/src/tools/index.ts +80 -55
  325. package/src/tools/list-limit.ts +1 -1
  326. package/src/tools/ls.ts +44 -70
  327. package/src/tools/notebook.ts +51 -67
  328. package/src/tools/output-meta.ts +3 -4
  329. package/src/tools/output-utils.ts +2 -2
  330. package/src/tools/path-utils.ts +5 -5
  331. package/src/tools/python.ts +104 -217
  332. package/src/tools/read.ts +92 -33
  333. package/src/tools/render-utils.ts +8 -23
  334. package/src/tools/renderers.ts +6 -7
  335. package/src/tools/review.ts +8 -11
  336. package/src/tools/ssh.ts +69 -49
  337. package/src/tools/todo-write.ts +37 -25
  338. package/src/tools/tool-errors.ts +3 -3
  339. package/src/tools/tool-result.ts +3 -8
  340. package/src/tools/write.ts +99 -75
  341. package/src/tui/code-cell.ts +109 -0
  342. package/src/tui/file-list.ts +47 -0
  343. package/src/tui/index.ts +11 -0
  344. package/src/tui/output-block.ts +72 -0
  345. package/src/tui/status-line.ts +39 -0
  346. package/src/tui/tree-list.ts +55 -0
  347. package/src/tui/types.ts +16 -0
  348. package/src/tui/utils.ts +48 -0
  349. package/src/utils/changelog.ts +9 -10
  350. package/src/utils/clipboard.ts +11 -11
  351. package/src/utils/file-mentions.ts +4 -10
  352. package/src/utils/frontmatter.ts +6 -3
  353. package/src/utils/fuzzy.ts +2 -2
  354. package/src/utils/image-convert.ts +1 -1
  355. package/src/utils/image-resize.ts +1 -1
  356. package/src/utils/mime.ts +2 -2
  357. package/src/utils/shell-snapshot.ts +11 -13
  358. package/src/utils/shell.ts +4 -5
  359. package/src/utils/title-generator.ts +8 -9
  360. package/src/utils/tools-manager.ts +23 -23
  361. package/src/vendor/photon/index.js +1099 -1059
  362. package/src/vendor/photon/photon_rs_bg.wasm +0 -0
  363. package/src/web/scrapers/artifacthub.ts +1 -1
  364. package/src/web/scrapers/arxiv.ts +2 -2
  365. package/src/web/scrapers/bluesky.ts +2 -2
  366. package/src/web/scrapers/cheatsh.ts +1 -1
  367. package/src/web/scrapers/chocolatey.ts +2 -2
  368. package/src/web/scrapers/choosealicense.ts +5 -5
  369. package/src/web/scrapers/cisa-kev.ts +1 -1
  370. package/src/web/scrapers/crossref.ts +2 -2
  371. package/src/web/scrapers/devto.ts +3 -3
  372. package/src/web/scrapers/discogs.ts +3 -4
  373. package/src/web/scrapers/discourse.ts +1 -1
  374. package/src/web/scrapers/dockerhub.ts +1 -1
  375. package/src/web/scrapers/fdroid.ts +2 -2
  376. package/src/web/scrapers/firefox-addons.ts +3 -3
  377. package/src/web/scrapers/flathub.ts +1 -1
  378. package/src/web/scrapers/github.ts +3 -3
  379. package/src/web/scrapers/gitlab.ts +4 -4
  380. package/src/web/scrapers/hackernews.ts +2 -2
  381. package/src/web/scrapers/huggingface.ts +1 -1
  382. package/src/web/scrapers/iacr.ts +2 -2
  383. package/src/web/scrapers/index.ts +0 -1
  384. package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
  385. package/src/web/scrapers/lemmy.ts +2 -2
  386. package/src/web/scrapers/maven.ts +2 -2
  387. package/src/web/scrapers/mdn.ts +2 -4
  388. package/src/web/scrapers/metacpan.ts +2 -2
  389. package/src/web/scrapers/musicbrainz.ts +1 -2
  390. package/src/web/scrapers/npm.ts +1 -1
  391. package/src/web/scrapers/nuget.ts +2 -2
  392. package/src/web/scrapers/nvd.ts +3 -3
  393. package/src/web/scrapers/ollama.ts +7 -9
  394. package/src/web/scrapers/opencorporates.ts +2 -2
  395. package/src/web/scrapers/openlibrary.ts +6 -6
  396. package/src/web/scrapers/orcid.ts +0 -1
  397. package/src/web/scrapers/osv.ts +2 -2
  398. package/src/web/scrapers/packagist.ts +1 -1
  399. package/src/web/scrapers/pubmed.ts +1 -2
  400. package/src/web/scrapers/rawg.ts +2 -2
  401. package/src/web/scrapers/readthedocs.ts +1 -2
  402. package/src/web/scrapers/repology.ts +2 -2
  403. package/src/web/scrapers/rfc.ts +1 -1
  404. package/src/web/scrapers/searchcode.ts +2 -2
  405. package/src/web/scrapers/semantic-scholar.ts +1 -1
  406. package/src/web/scrapers/snapcraft.ts +2 -2
  407. package/src/web/scrapers/sourcegraph.ts +1 -1
  408. package/src/web/scrapers/spdx.ts +3 -3
  409. package/src/web/scrapers/spotify.ts +0 -1
  410. package/src/web/scrapers/twitter.ts +1 -1
  411. package/src/web/scrapers/types.ts +1 -2
  412. package/src/web/scrapers/utils.ts +5 -5
  413. package/src/web/scrapers/wikidata.ts +3 -3
  414. package/src/web/scrapers/youtube.ts +9 -14
  415. package/src/web/search/auth.ts +5 -10
  416. package/src/web/search/index.ts +11 -21
  417. package/src/web/search/providers/anthropic.ts +3 -9
  418. package/src/web/search/providers/exa.ts +6 -10
  419. package/src/web/search/providers/perplexity.ts +5 -5
  420. package/src/web/search/render.ts +129 -175
  421. package/tsconfig.json +0 -42
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Hierarchical tree list rendering helper.
3
+ */
4
+ import type { Theme } from "../modes/theme/theme";
5
+ import { formatMoreItems } from "../tools/render-utils";
6
+ import type { TreeContext } from "./types";
7
+ import { getTreeBranch, getTreeContinuePrefix } from "./utils";
8
+
9
+ export interface TreeListOptions<T> {
10
+ items: T[];
11
+ expanded?: boolean;
12
+ maxCollapsed?: number;
13
+ itemType?: string;
14
+ renderItem: (item: T, context: TreeContext) => string | string[];
15
+ }
16
+
17
+ export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): string[] {
18
+ const { items, expanded = false, maxCollapsed = 8, itemType = "item", renderItem } = options;
19
+ const lines: string[] = [];
20
+ const maxItems = expanded ? items.length : Math.min(items.length, maxCollapsed);
21
+
22
+ for (let i = 0; i < maxItems; i++) {
23
+ const isLast = i === maxItems - 1 && (expanded || items.length <= maxCollapsed);
24
+ const branch = getTreeBranch(isLast, theme);
25
+ const prefix = `${theme.fg("dim", branch)} `;
26
+ const continuePrefix = `${theme.fg("dim", getTreeContinuePrefix(isLast, theme))}`;
27
+ const context: TreeContext = {
28
+ index: i,
29
+ isLast,
30
+ depth: 0,
31
+ theme,
32
+ prefix,
33
+ continuePrefix,
34
+ };
35
+ const rendered = renderItem(items[i], context);
36
+ if (Array.isArray(rendered)) {
37
+ if (rendered.length === 0) continue;
38
+ lines.push(`${prefix}${rendered[0]}`);
39
+ for (let j = 1; j < rendered.length; j++) {
40
+ lines.push(`${continuePrefix}${rendered[j]}`);
41
+ }
42
+ } else {
43
+ lines.push(`${prefix}${rendered}`);
44
+ }
45
+ }
46
+
47
+ if (!expanded && items.length > maxItems) {
48
+ const remaining = items.length - maxItems;
49
+ lines.push(
50
+ `${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", formatMoreItems(remaining, itemType, theme))}`,
51
+ );
52
+ }
53
+
54
+ return lines;
55
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared types for TUI rendering components.
3
+ */
4
+ import type { Theme } from "../modes/theme/theme";
5
+
6
+ export type State = "pending" | "running" | "success" | "error" | "warning";
7
+ export type IconType = "success" | "error" | "running" | "pending" | "warning" | "info";
8
+
9
+ export interface TreeContext {
10
+ index: number;
11
+ isLast: boolean;
12
+ depth: number;
13
+ theme: Theme;
14
+ prefix: string;
15
+ continuePrefix: string;
16
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shared helpers for tool-rendered UI components.
3
+ */
4
+ import { truncateToWidth as truncateToWidthBase, visibleWidth } from "@oh-my-pi/pi-tui";
5
+ import type { Theme, ThemeBg } from "../modes/theme/theme";
6
+ import type { IconType, State } from "./types";
7
+
8
+ export function buildTreePrefix(ancestors: boolean[], theme: Theme): string {
9
+ return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
10
+ }
11
+
12
+ export function getTreeBranch(isLast: boolean, theme: Theme): string {
13
+ return isLast ? theme.tree.last : theme.tree.branch;
14
+ }
15
+
16
+ export function getTreeContinuePrefix(isLast: boolean, theme: Theme): string {
17
+ return isLast ? " " : `${theme.tree.vertical} `;
18
+ }
19
+
20
+ export function truncateToWidth(text: string, width: number, ellipsis: string): string {
21
+ return truncateToWidthBase(text, width, ellipsis);
22
+ }
23
+
24
+ export function padToWidth(text: string, width: number, bgFn?: (s: string) => string): string {
25
+ if (width <= 0) return bgFn ? bgFn(text) : text;
26
+ const paddingNeeded = Math.max(0, width - visibleWidth(text));
27
+ const padded = paddingNeeded > 0 ? text + " ".repeat(paddingNeeded) : text;
28
+ return bgFn ? bgFn(padded) : padded;
29
+ }
30
+
31
+ export function getStateBgColor(state: State): ThemeBg {
32
+ if (state === "success") return "toolSuccessBg";
33
+ if (state === "error") return "toolErrorBg";
34
+ return "toolPendingBg";
35
+ }
36
+
37
+ export function getStateIcon(icon: IconType, theme: Theme, spinnerFrame?: number): string {
38
+ if (icon === "success") return theme.styledSymbol("status.success", "success");
39
+ if (icon === "error") return theme.styledSymbol("status.error", "error");
40
+ if (icon === "warning") return theme.styledSymbol("status.warning", "warning");
41
+ if (icon === "info") return theme.styledSymbol("status.info", "accent");
42
+ if (icon === "pending") return theme.styledSymbol("status.pending", "accent");
43
+ if (spinnerFrame !== undefined) {
44
+ const frames = theme.spinnerFrames;
45
+ return frames[spinnerFrame % frames.length];
46
+ }
47
+ return theme.styledSymbol("status.running", "accent");
48
+ }
@@ -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);
@@ -5,11 +5,9 @@
5
5
  * .bashrc/.zshrc, which can be sourced before each command to provide a familiar
6
6
  * shell experience.
7
7
  */
8
-
9
- import { unlinkSync } from "node:fs";
10
- import { mkdir } from "node:fs/promises";
11
- import { homedir, tmpdir } from "node:os";
12
- import { join } from "node:path";
8
+ import * as fs from "node:fs";
9
+ import * as os from "node:os";
10
+ import * as path from "node:path";
13
11
  import { postmortem } from "@oh-my-pi/pi-utils";
14
12
  import { $ } from "bun";
15
13
 
@@ -19,10 +17,10 @@ let cachedSnapshotPath: string | null = null;
19
17
  * Get the user's shell config file path.
20
18
  */
21
19
  function getShellConfigFile(shell: string): string {
22
- const home = homedir();
23
- if (shell.includes("zsh")) return join(home, ".zshrc");
24
- if (shell.includes("bash")) return join(home, ".bashrc");
25
- return join(home, ".profile");
20
+ const home = os.homedir();
21
+ if (shell.includes("zsh")) return path.join(home, ".zshrc");
22
+ if (shell.includes("bash")) return path.join(home, ".bashrc");
23
+ return path.join(home, ".profile");
26
24
  }
27
25
 
28
26
  /**
@@ -131,12 +129,12 @@ export async function getOrCreateSnapshot(
131
129
  const rcFile = getShellConfigFile(shell);
132
130
 
133
131
  // Create snapshot directory
134
- const snapshotDir = join(tmpdir(), "omp-shell-snapshots");
135
- await mkdir(snapshotDir, { recursive: true });
132
+ const snapshotDir = path.join(os.tmpdir(), "omp-shell-snapshots");
133
+ await fs.promises.mkdir(snapshotDir, { recursive: true });
136
134
 
137
135
  // Generate unique snapshot path
138
136
  const shellName = shell.includes("zsh") ? "zsh" : shell.includes("bash") ? "bash" : "sh";
139
- const snapshotPath = join(snapshotDir, `snapshot-${shellName}-${crypto.randomUUID()}.sh`);
137
+ const snapshotPath = path.join(snapshotDir, `snapshot-${shellName}-${crypto.randomUUID()}.sh`);
140
138
 
141
139
  // Generate and execute snapshot script
142
140
  const script = await generateSnapshotScript(shell, snapshotPath, rcFile);
@@ -167,6 +165,6 @@ export function getSnapshotSourceCommand(snapshotPath: string | null): string {
167
165
 
168
166
  postmortem.register("shell-snapshot", () => {
169
167
  if (cachedSnapshotPath) {
170
- unlinkSync(cachedSnapshotPath);
168
+ fs.unlinkSync(cachedSnapshotPath);
171
169
  }
172
170
  });
@@ -1,7 +1,6 @@
1
- import { constants } from "node:fs";
2
- import { access } from "node:fs/promises";
3
- import { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
1
+ import * as fs from "node:fs";
4
2
  import { $ } from "bun";
3
+ import { SettingsManager } from "../config/settings-manager";
5
4
 
6
5
  export interface ShellConfig {
7
6
  shell: string;
@@ -17,7 +16,7 @@ let cachedShellConfig: ShellConfig | null = null;
17
16
  */
18
17
  async function isExecutable(path: string): Promise<boolean> {
19
18
  try {
20
- await access(path, constants.X_OK);
19
+ await fs.promises.access(path, fs.constants.X_OK);
21
20
  return true;
22
21
  } catch {
23
22
  return false;
@@ -138,7 +137,7 @@ export async function getShellConfig(): Promise<ShellConfig> {
138
137
  ` 1. Install Git for Windows: https://git-scm.com/download/win\n` +
139
138
  ` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\n` +
140
139
  ` 3. Set shellPath in ~/.omp/agent/settings.json\n\n` +
141
- `Searched Git Bash in:\n${paths.map((p) => ` ${p}`).join("\n")}`,
140
+ `Searched Git Bash in:\n${paths.map(p => ` ${p}`).join("\n")}`,
142
141
  );
143
142
  }
144
143
 
@@ -1,14 +1,13 @@
1
1
  /**
2
2
  * Generate session titles using a smol, fast model.
3
3
  */
4
-
5
4
  import type { Api, Model } from "@oh-my-pi/pi-ai";
6
5
  import { completeSimple } from "@oh-my-pi/pi-ai";
7
- import type { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
8
- import { parseModelString, SMOL_MODEL_PRIORITY } from "@oh-my-pi/pi-coding-agent/config/model-resolver";
9
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
10
- import titleSystemPrompt from "@oh-my-pi/pi-coding-agent/prompts/system/title-system.md" with { type: "text" };
11
6
  import { logger } from "@oh-my-pi/pi-utils";
7
+ import type { ModelRegistry } from "../config/model-registry";
8
+ import { parseModelString, SMOL_MODEL_PRIORITY } from "../config/model-resolver";
9
+ import { renderPromptTemplate } from "../config/prompt-templates";
10
+ import titleSystemPrompt from "../prompts/system/title-system.md" with { type: "text" };
12
11
 
13
12
  const TITLE_SYSTEM_PROMPT = renderPromptTemplate(titleSystemPrompt);
14
13
 
@@ -21,7 +20,7 @@ function getTitleModelCandidates(registry: ModelRegistry, savedSmolModel?: strin
21
20
  const candidates: Model<Api>[] = [];
22
21
  const addCandidate = (model?: Model<Api>): void => {
23
22
  if (!model) return;
24
- const exists = candidates.some((candidate) => candidate.provider === model.provider && candidate.id === model.id);
23
+ const exists = candidates.some(candidate => candidate.provider === model.provider && candidate.id === model.id);
25
24
  if (!exists) {
26
25
  candidates.push(model);
27
26
  }
@@ -30,17 +29,17 @@ function getTitleModelCandidates(registry: ModelRegistry, savedSmolModel?: strin
30
29
  if (savedSmolModel) {
31
30
  const parsed = parseModelString(savedSmolModel);
32
31
  if (parsed) {
33
- const match = availableModels.find((model) => model.provider === parsed.provider && model.id === parsed.id);
32
+ const match = availableModels.find(model => model.provider === parsed.provider && model.id === parsed.id);
34
33
  addCandidate(match);
35
34
  }
36
35
  }
37
36
 
38
37
  for (const pattern of SMOL_MODEL_PRIORITY) {
39
38
  const needle = pattern.toLowerCase();
40
- const exactMatch = availableModels.find((model) => model.id.toLowerCase() === needle);
39
+ const exactMatch = availableModels.find(model => model.id.toLowerCase() === needle);
41
40
  addCandidate(exactMatch);
42
41
 
43
- const fuzzyMatch = availableModels.find((model) => model.id.toLowerCase().includes(needle));
42
+ const fuzzyMatch = availableModels.find(model => model.id.toLowerCase().includes(needle));
44
43
  addCandidate(fuzzyMatch);
45
44
  }
46
45
 
@@ -1,9 +1,9 @@
1
- import { chmod, mkdir, rename, rm } from "node:fs/promises";
2
- import { arch, platform } from "node:os";
3
- import { join } from "node:path";
4
- import { APP_NAME, getBinDir } from "@oh-my-pi/pi-coding-agent/config";
5
- import { createTempDir, logger } from "@oh-my-pi/pi-utils";
1
+ import * as fs from "node:fs/promises";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { logger, TempDir } from "@oh-my-pi/pi-utils";
6
5
  import { $ } from "bun";
6
+ import { APP_NAME, getBinDir } from "../config";
7
7
 
8
8
  const TOOLS_DIR = getBinDir();
9
9
 
@@ -148,7 +148,7 @@ export async function getToolPath(tool: ToolName): Promise<string | null> {
148
148
  if (!config) return null;
149
149
 
150
150
  // Check our tools directory first
151
- const localPath = join(TOOLS_DIR, config.binaryName + (platform() === "win32" ? ".exe" : ""));
151
+ const localPath = path.join(TOOLS_DIR, config.binaryName + (os.platform() === "win32" ? ".exe" : ""));
152
152
  if (await Bun.file(localPath).exists()) {
153
153
  return localPath;
154
154
  }
@@ -187,8 +187,8 @@ async function downloadTool(tool: ToolName): Promise<string> {
187
187
  const config = TOOLS[tool];
188
188
  if (!config) throw new Error(`Unknown tool: ${tool}`);
189
189
 
190
- const plat = platform();
191
- const architecture = arch();
190
+ const plat = os.platform();
191
+ const architecture = os.arch();
192
192
 
193
193
  // Get latest version
194
194
  const version = await getLatestVersion(config.repo);
@@ -200,64 +200,64 @@ async function downloadTool(tool: ToolName): Promise<string> {
200
200
  }
201
201
 
202
202
  // Create tools directory
203
- await mkdir(TOOLS_DIR, { recursive: true });
203
+ await fs.mkdir(TOOLS_DIR, { recursive: true });
204
204
 
205
205
  const downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;
206
206
  const binaryExt = plat === "win32" ? ".exe" : "";
207
- const binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);
207
+ const binaryPath = path.join(TOOLS_DIR, config.binaryName + binaryExt);
208
208
 
209
209
  // Handle direct binary downloads (no archive extraction needed)
210
210
  if (config.isDirectBinary) {
211
211
  await downloadFile(downloadUrl, binaryPath);
212
212
  if (plat !== "win32") {
213
- await chmod(binaryPath, 0o755);
213
+ await fs.chmod(binaryPath, 0o755);
214
214
  }
215
215
  return binaryPath;
216
216
  }
217
217
 
218
218
  // Download archive
219
- const archivePath = join(TOOLS_DIR, assetName);
219
+ const archivePath = path.join(TOOLS_DIR, assetName);
220
220
  await downloadFile(downloadUrl, archivePath);
221
221
 
222
222
  // Extract
223
- const tmp = await createTempDir("@omp-tools-extract-");
223
+ const tmp = await TempDir.create("@omp-tools-extract-");
224
224
 
225
225
  try {
226
226
  if (assetName.endsWith(".tar.gz")) {
227
227
  const archive = new Bun.Archive(await Bun.file(archivePath).arrayBuffer());
228
228
  const files = await archive.files();
229
- for (const [path, file] of files) {
230
- await Bun.write(join(tmp.path, path), file);
229
+ for (const [filePath, file] of files) {
230
+ await Bun.write(path.join(tmp.path(), filePath), file);
231
231
  }
232
232
  } else if (assetName.endsWith(".zip")) {
233
- await mkdir(tmp.path, { recursive: true });
234
- await $`unzip -o ${archivePath} -d ${tmp.path}`.quiet().nothrow();
233
+ await fs.mkdir(tmp.path(), { recursive: true });
234
+ await $`unzip -o ${archivePath} -d ${tmp.path()}`.quiet().nothrow();
235
235
  }
236
236
 
237
237
  // Find the binary in extracted files
238
238
  // ast-grep releases the binary directly in the zip, not in a subdirectory
239
239
  let extractedBinary: string;
240
240
  if (tool === "sg") {
241
- extractedBinary = join(tmp.path, config.binaryName + binaryExt);
241
+ extractedBinary = path.join(tmp.path(), config.binaryName + binaryExt);
242
242
  } else {
243
- const extractedDir = join(tmp.path, assetName.replace(/\.(tar\.gz|zip)$/, ""));
244
- extractedBinary = join(extractedDir, config.binaryName + binaryExt);
243
+ const extractedDir = path.join(tmp.path(), assetName.replace(/\.(tar\.gz|zip)$/, ""));
244
+ extractedBinary = path.join(extractedDir, config.binaryName + binaryExt);
245
245
  }
246
246
 
247
247
  if (await Bun.file(extractedBinary).exists()) {
248
- await rename(extractedBinary, binaryPath);
248
+ await fs.rename(extractedBinary, binaryPath);
249
249
  } else {
250
250
  throw new Error(`Binary not found in archive: ${extractedBinary}`);
251
251
  }
252
252
 
253
253
  // Make executable (Unix only)
254
254
  if (plat !== "win32") {
255
- await chmod(binaryPath, 0o755);
255
+ await fs.chmod(binaryPath, 0o755);
256
256
  }
257
257
  } finally {
258
258
  // Cleanup
259
259
  await tmp.remove();
260
- await rm(archivePath, { force: true });
260
+ await fs.rm(archivePath, { force: true });
261
261
  }
262
262
 
263
263
  return binaryPath;