@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
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Migrates legacy JSON storage (settings.json, auth.json) to SQLite-based agent.db.
3
- * Settings migrate only when the DB has no settings; auth merges per-provider when missing.
2
+ * Migrates legacy auth.json to SQLite-based agent.db.
3
+ * Auth credentials merge per-provider when missing.
4
4
  * Original JSON files are backed up to .bak and removed after successful migration.
5
+ *
6
+ * NOTE: Settings migration is now handled by SettingsManager.migrateToYaml(),
7
+ * which migrates from both settings.json and agent.db to config.yaml.
5
8
  */
6
-
7
- import { getAgentDbPath } from "@oh-my-pi/pi-coding-agent/config";
8
- import type { Settings } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
9
9
  import { logger } from "@oh-my-pi/pi-utils";
10
+ import { getAgentDbPath } from "../config";
10
11
  import { AgentStorage } from "./agent-storage";
11
12
  import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "./auth-storage";
12
13
 
@@ -14,7 +15,7 @@ import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "./aut
14
15
  type MigrationPaths = {
15
16
  /** Directory containing agent.db */
16
17
  agentDir: string;
17
- /** Path to legacy settings.json file */
18
+ /** Path to legacy settings.json file (kept for API compatibility, no longer used) */
18
19
  settingsPath: string;
19
20
  /** Candidate paths to search for auth.json (checked in order) */
20
21
  authPaths: string[];
@@ -22,7 +23,7 @@ type MigrationPaths = {
22
23
 
23
24
  /** Result of the JSON-to-SQLite storage migration. */
24
25
  export interface StorageMigrationResult {
25
- /** Whether settings.json was migrated to agent.db */
26
+ /** Whether settings.json was migrated (always false - settings migration is handled elsewhere) */
26
27
  migratedSettings: boolean;
27
28
  /** Whether auth.json was migrated to agent.db */
28
29
  migratedAuth: boolean;
@@ -39,20 +40,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
39
40
  return !!value && typeof value === "object" && !Array.isArray(value);
40
41
  }
41
42
 
42
- /**
43
- * Transforms legacy settings to current schema (e.g., queueMode -> steeringMode).
44
- * @param settings - Settings object potentially containing deprecated keys
45
- * @returns Settings with deprecated keys renamed to current equivalents
46
- */
47
- function migrateLegacySettings(settings: Settings): Settings {
48
- const migrated = { ...settings } as Record<string, unknown>;
49
- if ("queueMode" in migrated && !("steeringMode" in migrated)) {
50
- migrated.steeringMode = migrated.queueMode;
51
- delete migrated.queueMode;
52
- }
53
- return migrated as Settings;
54
- }
55
-
56
43
  /**
57
44
  * Normalizes credential entries to array format (legacy stored single credentials).
58
45
  * @param entry - Single credential or array of credentials
@@ -99,32 +86,6 @@ async function backupJson(path: string): Promise<void> {
99
86
  }
100
87
  }
101
88
 
102
- /**
103
- * Migrates settings.json to SQLite storage if DB is empty.
104
- * @param storage - AgentStorage instance to migrate into
105
- * @param settingsPath - Path to legacy settings.json
106
- * @param warnings - Array to collect non-fatal warnings
107
- * @returns True if migration was performed
108
- */
109
- async function migrateSettings(storage: AgentStorage, settingsPath: string, warnings: string[]): Promise<boolean> {
110
- const settingsFile = Bun.file(settingsPath);
111
- const settingsExists = await settingsFile.exists();
112
- const hasDbSettings = storage.getSettings() !== null;
113
-
114
- if (!settingsExists) return false;
115
- if (hasDbSettings) {
116
- warnings.push(`settings.json exists but agent.db is authoritative: ${settingsPath}`);
117
- return false;
118
- }
119
-
120
- const settingsJson = await readJsonFile<Settings>(settingsPath);
121
- if (!settingsJson) return false;
122
-
123
- storage.saveSettings(migrateLegacySettings(settingsJson));
124
- await backupJson(settingsPath);
125
- return true;
126
- }
127
-
128
89
  /**
129
90
  * Finds the first valid auth.json from candidate paths (checked in priority order).
130
91
  * @param authPaths - Candidate paths to search (e.g., project-local before global)
@@ -166,7 +127,7 @@ async function migrateAuth(storage: AgentStorage, authPaths: string[], warnings:
166
127
  for (const [provider, entry] of Object.entries(authJson.data)) {
167
128
  const credentials = normalizeCredentialEntry(entry)
168
129
  .filter(isValidCredential)
169
- .map((credential) => credential);
130
+ .map(credential => credential);
170
131
 
171
132
  if (credentials.length === 0) continue;
172
133
  sawValid = true;
@@ -191,19 +152,16 @@ async function migrateAuth(storage: AgentStorage, authPaths: string[], warnings:
191
152
  }
192
153
 
193
154
  /**
194
- * Migrates legacy JSON files (settings.json, auth.json) to SQLite-based agent.db.
195
- * Settings migrate only when the DB has no settings; auth merges per-provider when missing.
155
+ * Migrates legacy auth.json to SQLite-based agent.db.
156
+ * Settings migration is handled separately by SettingsManager.migrateToYaml().
196
157
  * @param paths - Configuration specifying locations of legacy files and target DB
197
158
  * @returns Result indicating what was migrated and any warnings encountered
198
159
  */
199
160
  export async function migrateJsonStorage(paths: MigrationPaths): Promise<StorageMigrationResult> {
200
- const storage = AgentStorage.open(getAgentDbPath(paths.agentDir));
161
+ const storage = await AgentStorage.open(getAgentDbPath(paths.agentDir));
201
162
  const warnings: string[] = [];
202
163
 
203
- const [migratedSettings, migratedAuth] = await Promise.all([
204
- migrateSettings(storage, paths.settingsPath, warnings),
205
- migrateAuth(storage, paths.authPaths, warnings),
206
- ]);
164
+ const migratedAuth = await migrateAuth(storage, paths.authPaths, warnings);
207
165
 
208
166
  if (warnings.length > 0) {
209
167
  for (const warning of warnings) {
@@ -211,5 +169,5 @@ export async function migrateJsonStorage(paths: MigrationPaths): Promise<Storage
211
169
  }
212
170
  }
213
171
 
214
- return { migratedSettings, migratedAuth, warnings };
172
+ return { migratedSettings: false, migratedAuth, warnings };
215
173
  }
@@ -1,5 +1,5 @@
1
- import { DEFAULT_MAX_BYTES } from "@oh-my-pi/pi-coding-agent/tools/truncate";
2
1
  import { sanitizeText } from "@oh-my-pi/pi-utils";
2
+ import { DEFAULT_MAX_BYTES } from "../tools/truncate";
3
3
 
4
4
  export interface OutputSummary {
5
5
  output: string;
@@ -141,7 +141,7 @@ export class OutputSink {
141
141
  };
142
142
 
143
143
  return new WritableStream<Uint8Array | string>({
144
- write: async (chunk) => {
144
+ write: async chunk => {
145
145
  if (typeof chunk === "string") {
146
146
  await this.push(chunk);
147
147
  } else {
@@ -1,9 +1,9 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import { CONFIG_DIR_NAME } from "@oh-my-pi/pi-coding-agent/config";
5
- import { 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 { isEnoent, logger } from "@oh-my-pi/pi-utils";
6
5
  import { $ } from "bun";
6
+ import { CONFIG_DIR_NAME } from "../config";
7
7
 
8
8
  export interface SSHConnectionTarget {
9
9
  name: string;
@@ -25,52 +25,44 @@ export interface SSHHostInfo {
25
25
  compatEnabled: boolean;
26
26
  }
27
27
 
28
- const CONTROL_DIR = join(homedir(), CONFIG_DIR_NAME, "ssh-control");
29
- const CONTROL_PATH = join(CONTROL_DIR, "%h.sock");
30
- const HOST_INFO_DIR = join(homedir(), CONFIG_DIR_NAME, "remote-host");
28
+ const CONTROL_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "ssh-control");
29
+ const CONTROL_PATH = path.join(CONTROL_DIR, "%h.sock");
30
+ const HOST_INFO_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "remote-host");
31
31
  const HOST_INFO_VERSION = 2;
32
32
 
33
33
  const activeHosts = new Map<string, SSHConnectionTarget>();
34
34
  const pendingConnections = new Map<string, Promise<void>>();
35
35
  const hostInfoCache = new Map<string, SSHHostInfo>();
36
36
 
37
- function ensureControlDir(): void {
38
- if (!existsSync(CONTROL_DIR)) {
39
- mkdirSync(CONTROL_DIR, { recursive: true, mode: 0o700 });
40
- }
37
+ async function ensureControlDir(): Promise<void> {
38
+ await fs.mkdir(CONTROL_DIR, { recursive: true, mode: 0o700 });
41
39
  try {
42
- chmodSync(CONTROL_DIR, 0o700);
40
+ await fs.chmod(CONTROL_DIR, 0o700);
43
41
  } catch (err) {
44
42
  logger.debug("SSH control dir chmod failed", { path: CONTROL_DIR, error: String(err) });
45
43
  }
46
44
  }
47
45
 
48
- function ensureHostInfoDir(): void {
49
- if (!existsSync(HOST_INFO_DIR)) {
50
- mkdirSync(HOST_INFO_DIR, { recursive: true, mode: 0o700 });
51
- }
52
- try {
53
- chmodSync(HOST_INFO_DIR, 0o700);
54
- } catch (err) {
55
- logger.debug("SSH host info dir chmod failed", { path: HOST_INFO_DIR, error: String(err) });
56
- }
57
- }
58
-
59
46
  function sanitizeHostName(name: string): string {
60
47
  const sanitized = name.replace(/[^a-zA-Z0-9._-]+/g, "_");
61
48
  return sanitized.length > 0 ? sanitized : "host";
62
49
  }
63
50
 
64
51
  function getHostInfoPath(name: string): string {
65
- return join(HOST_INFO_DIR, `${sanitizeHostName(name)}.json`);
52
+ return path.join(HOST_INFO_DIR, `${sanitizeHostName(name)}.json`);
66
53
  }
67
54
 
68
- function validateKeyPermissions(keyPath?: string): void {
55
+ async function validateKeyPermissions(keyPath?: string): Promise<void> {
69
56
  if (!keyPath) return;
70
- if (!existsSync(keyPath)) {
71
- throw new Error(`SSH key not found: ${keyPath}`);
57
+ let stats: Awaited<ReturnType<typeof fs.stat>>;
58
+ try {
59
+ stats = await fs.stat(keyPath);
60
+ } catch (err) {
61
+ if (isEnoent(err)) {
62
+ throw new Error(`SSH key not found: ${keyPath}`);
63
+ }
64
+ throw err;
72
65
  }
73
- const stats = statSync(keyPath);
74
66
  if (!stats.isFile()) {
75
67
  throw new Error(`SSH key is not a file: ${keyPath}`);
76
68
  }
@@ -208,31 +200,31 @@ function shouldRefreshHostInfo(host: SSHConnectionTarget, info: SSHHostInfo): bo
208
200
  return false;
209
201
  }
210
202
 
211
- function loadHostInfoFromDisk(host: SSHConnectionTarget): SSHHostInfo | undefined {
203
+ async function loadHostInfoFromDisk(host: SSHConnectionTarget): Promise<SSHHostInfo | undefined> {
212
204
  const path = getHostInfoPath(host.name);
213
- if (!existsSync(path)) return undefined;
214
205
  try {
215
- const raw = readFileSync(path, "utf-8");
206
+ const raw = await fs.readFile(path, "utf-8");
216
207
  const parsed = parseHostInfo(JSON.parse(raw));
217
208
  if (!parsed) return undefined;
218
209
  const resolved = applyCompatOverride(host, parsed);
219
210
  hostInfoCache.set(host.name, resolved);
220
211
  return resolved;
221
212
  } catch (err) {
213
+ if (isEnoent(err)) return undefined;
222
214
  logger.warn("Failed to load SSH host info", { host: host.name, error: String(err) });
223
215
  return undefined;
224
216
  }
225
217
  }
226
218
 
227
- function loadHostInfoFromDiskByName(hostName: string): SSHHostInfo | undefined {
219
+ async function loadHostInfoFromDiskByName(hostName: string): Promise<SSHHostInfo | undefined> {
228
220
  const path = getHostInfoPath(hostName);
229
- if (!existsSync(path)) return undefined;
230
221
  try {
231
- const raw = readFileSync(path, "utf-8");
222
+ const raw = await fs.readFile(path, "utf-8");
232
223
  const parsed = parseHostInfo(JSON.parse(raw));
233
224
  if (!parsed) return undefined;
234
225
  return parsed;
235
226
  } catch (err) {
227
+ if (isEnoent(err)) return undefined;
236
228
  logger.warn("Failed to load SSH host info", { host: hostName, error: String(err) });
237
229
  return undefined;
238
230
  }
@@ -240,7 +232,6 @@ function loadHostInfoFromDiskByName(hostName: string): SSHHostInfo | undefined {
240
232
 
241
233
  async function persistHostInfo(host: SSHConnectionTarget, info: SSHHostInfo): Promise<void> {
242
234
  try {
243
- ensureHostInfoDir();
244
235
  const path = getHostInfoPath(host.name);
245
236
  const payload = { ...info, version: HOST_INFO_VERSION };
246
237
  hostInfoCache.set(host.name, payload);
@@ -252,7 +243,7 @@ async function persistHostInfo(host: SSHConnectionTarget, info: SSHHostInfo): Pr
252
243
 
253
244
  async function probeHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
254
245
  const command = 'echo "$OSTYPE|$SHELL|$BASH_VERSION" 2>/dev/null || echo "%OS%|%COMSPEC%|"';
255
- const result = await runSshCaptureSync(buildRemoteCommand(host, command));
246
+ const result = await runSshCaptureSync(await buildRemoteCommand(host, command));
256
247
  if (result.exitCode !== 0 && !result.stdout) {
257
248
  logger.debug("SSH host probe failed", { host: host.name, error: result.stderr });
258
249
  const fallback: SSHHostInfo = {
@@ -315,11 +306,11 @@ async function probeHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
315
306
  const hasBash = !unexpandedPosixVars && (Boolean(bashVersion) || shell === "bash");
316
307
  let compatShell: SSHHostInfo["compatShell"];
317
308
  if (os === "windows" && host.compat !== false) {
318
- const bashProbe = await runSshCaptureSync(buildRemoteCommand(host, 'bash -lc "echo OMP_BASH_OK"'));
309
+ const bashProbe = await runSshCaptureSync(await buildRemoteCommand(host, 'bash -lc "echo OMP_BASH_OK"'));
319
310
  if (bashProbe.exitCode === 0 && bashProbe.stdout.includes("OMP_BASH_OK")) {
320
311
  compatShell = "bash";
321
312
  } else {
322
- const shProbe = await runSshCaptureSync(buildRemoteCommand(host, 'sh -lc "echo OMP_SH_OK"'));
313
+ const shProbe = await runSshCaptureSync(await buildRemoteCommand(host, 'sh -lc "echo OMP_SH_OK"'));
323
314
  if (shProbe.exitCode === 0 && shProbe.stdout.includes("OMP_SH_OK")) {
324
315
  compatShell = "sh";
325
316
  }
@@ -344,18 +335,20 @@ async function probeHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
344
335
  return info;
345
336
  }
346
337
 
347
- export function getHostInfo(hostName: string): SSHHostInfo | undefined {
348
- return hostInfoCache.get(hostName) ?? loadHostInfoFromDiskByName(hostName);
338
+ export async function getHostInfo(hostName: string): Promise<SSHHostInfo | undefined> {
339
+ const cached = hostInfoCache.get(hostName);
340
+ if (cached) return cached;
341
+ return loadHostInfoFromDiskByName(hostName);
349
342
  }
350
343
 
351
- export function getHostInfoForHost(host: SSHConnectionTarget): SSHHostInfo | undefined {
344
+ export async function getHostInfoForHost(host: SSHConnectionTarget): Promise<SSHHostInfo | undefined> {
352
345
  const cached = hostInfoCache.get(host.name);
353
346
  if (cached) {
354
347
  const resolved = applyCompatOverride(host, cached);
355
348
  if (resolved !== cached) hostInfoCache.set(host.name, resolved);
356
349
  return resolved;
357
350
  }
358
- return loadHostInfoFromDisk(host);
351
+ return await loadHostInfoFromDisk(host);
359
352
  }
360
353
 
361
354
  export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
@@ -365,7 +358,7 @@ export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHost
365
358
  hostInfoCache.set(host.name, resolved);
366
359
  if (!shouldRefreshHostInfo(host, resolved)) return resolved;
367
360
  }
368
- const fromDisk = loadHostInfoFromDisk(host);
361
+ const fromDisk = await loadHostInfoFromDisk(host);
369
362
  if (fromDisk && !shouldRefreshHostInfo(host, fromDisk)) return fromDisk;
370
363
  await ensureConnection(host);
371
364
  const current = hostInfoCache.get(host.name);
@@ -373,8 +366,8 @@ export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHost
373
366
  return probeHostInfo(host);
374
367
  }
375
368
 
376
- export function buildRemoteCommand(host: SSHConnectionTarget, command: string): string[] {
377
- validateKeyPermissions(host.keyPath);
369
+ export async function buildRemoteCommand(host: SSHConnectionTarget, command: string): Promise<string[]> {
370
+ await validateKeyPermissions(host.keyPath);
378
371
  return [...buildCommonArgs(host), buildSshTarget(host), command];
379
372
  }
380
373
 
@@ -388,14 +381,14 @@ export async function ensureConnection(host: SSHConnectionTarget): Promise<void>
388
381
 
389
382
  const promise = (async () => {
390
383
  ensureSshBinary();
391
- ensureControlDir();
392
- validateKeyPermissions(host.keyPath);
384
+ await ensureControlDir();
385
+ await validateKeyPermissions(host.keyPath);
393
386
 
394
387
  const target = buildSshTarget(host);
395
388
  const check = await runSshSync(["-O", "check", ...buildCommonArgs(host), target]);
396
389
  if (check.exitCode === 0) {
397
390
  activeHosts.set(key, host);
398
- if (!hostInfoCache.has(key) && !loadHostInfoFromDisk(host)) {
391
+ if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
399
392
  await probeHostInfo(host);
400
393
  }
401
394
  return;
@@ -408,7 +401,7 @@ export async function ensureConnection(host: SSHConnectionTarget): Promise<void>
408
401
  }
409
402
 
410
403
  activeHosts.set(key, host);
411
- if (!hostInfoCache.has(key) && !loadHostInfoFromDisk(host)) {
404
+ if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
412
405
  await probeHostInfo(host);
413
406
  }
414
407
  })();
@@ -1,5 +1,5 @@
1
- import { OutputSink } from "@oh-my-pi/pi-coding-agent/session/streaming-output";
2
1
  import { cspawn, logger, ptree } from "@oh-my-pi/pi-utils";
2
+ import { OutputSink } from "../session/streaming-output";
3
3
  import { buildRemoteCommand, ensureConnection, ensureHostInfo, type SSHConnectionTarget } from "./connection-manager";
4
4
  import { hasSshfs, mountRemote } from "./sshfs-mount";
5
5
 
@@ -76,7 +76,7 @@ export async function executeSSH(
76
76
  }
77
77
  }
78
78
 
79
- const child = cspawn(["ssh", ...buildRemoteCommand(host, resolvedCommand)], {
79
+ const child = cspawn(["ssh", ...(await buildRemoteCommand(host, resolvedCommand))], {
80
80
  signal: options?.signal,
81
81
  timeout: options?.timeout,
82
82
  });
@@ -1,25 +1,21 @@
1
- import { chmodSync, existsSync, mkdirSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import { CONFIG_DIR_NAME } from "@oh-my-pi/pi-coding-agent/config";
5
- import { logger } from "@oh-my-pi/pi-utils";
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
6
4
  import { $ } from "bun";
5
+ import { CONFIG_DIR_NAME } from "../config";
7
6
  import { getControlDir, getControlPathTemplate, type SSHConnectionTarget } from "./connection-manager";
8
7
 
9
- const REMOTE_DIR = join(homedir(), CONFIG_DIR_NAME, "remote");
8
+ const REMOTE_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "remote");
10
9
  const CONTROL_DIR = getControlDir();
11
10
  const CONTROL_PATH = getControlPathTemplate();
12
11
 
13
12
  const mountedPaths = new Set<string>();
14
13
 
15
- function ensureDir(path: string, mode = 0o700): void {
16
- if (!existsSync(path)) {
17
- mkdirSync(path, { recursive: true, mode });
18
- }
14
+ async function ensureDir(path: string, mode = 0o700): Promise<void> {
19
15
  try {
20
- chmodSync(path, mode);
21
- } catch (err) {
22
- logger.debug("SSHFS dir chmod failed", { path, error: String(err) });
16
+ await fs.promises.mkdir(path, { recursive: true, mode });
17
+ } catch {
18
+ await fs.promises.chmod(path, mode).catch(e => void e);
23
19
  }
24
20
  }
25
21
 
@@ -30,7 +26,7 @@ function getMountName(host: SSHConnectionTarget): string {
30
26
  }
31
27
 
32
28
  function getMountPath(host: SSHConnectionTarget): string {
33
- return join(REMOTE_DIR, getMountName(host));
29
+ return path.join(REMOTE_DIR, getMountName(host));
34
30
  }
35
31
 
36
32
  function buildSshTarget(host: SSHConnectionTarget): string {
@@ -95,11 +91,8 @@ export async function isMounted(path: string): Promise<boolean> {
95
91
  export async function mountRemote(host: SSHConnectionTarget, remotePath = "/"): Promise<string | undefined> {
96
92
  if (!hasSshfs()) return undefined;
97
93
 
98
- ensureDir(REMOTE_DIR);
99
- ensureDir(CONTROL_DIR);
100
-
101
94
  const mountPath = getMountPath(host);
102
- ensureDir(mountPath);
95
+ await Promise.all([ensureDir(REMOTE_DIR), ensureDir(CONTROL_DIR), ensureDir(mountPath)]);
103
96
 
104
97
  if (await isMounted(mountPath)) {
105
98
  mountedPaths.add(mountPath);
@@ -1,27 +1,19 @@
1
1
  /**
2
2
  * System prompt construction and project context loading
3
3
  */
4
-
5
- import { existsSync } from "node:fs";
6
- import { homedir } from "node:os";
7
- import { join } from "node:path";
8
- import { contextFileCapability } from "@oh-my-pi/pi-coding-agent/capability/context-file";
9
- import { systemPromptCapability } from "@oh-my-pi/pi-coding-agent/capability/system-prompt";
10
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
11
- import type { SkillsSettings } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
12
- import {
13
- type ContextFile,
14
- loadCapability,
15
- type SystemPrompt as SystemPromptFile,
16
- } from "@oh-my-pi/pi-coding-agent/discovery/index";
17
- import { loadSkills, type Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
18
- import customSystemPromptTemplate from "@oh-my-pi/pi-coding-agent/prompts/system/custom-system-prompt.md" with {
19
- type: "text",
20
- };
21
- import systemPromptTemplate from "@oh-my-pi/pi-coding-agent/prompts/system/system-prompt.md" with { type: "text" };
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
22
6
  import { $ } from "bun";
23
7
  import chalk from "chalk";
24
- import type { ToolName } from "./tools/index";
8
+ import { contextFileCapability } from "./capability/context-file";
9
+ import { systemPromptCapability } from "./capability/system-prompt";
10
+ import { renderPromptTemplate } from "./config/prompt-templates";
11
+ import type { SkillsSettings } from "./config/settings-manager";
12
+ import { type ContextFile, loadCapability, type SystemPrompt as SystemPromptFile } from "./discovery";
13
+ import { loadSkills, type Skill } from "./extensibility/skills";
14
+ import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md" with { type: "text" };
15
+ import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
16
+ import type { ToolName } from "./tools";
25
17
 
26
18
  interface GitContext {
27
19
  isRepo: boolean;
@@ -42,7 +34,7 @@ export async function loadGitContext(cwd: string): Promise<GitContext | null> {
42
34
  .quiet()
43
35
  .text()
44
36
  .catch(() => null)
45
- .then((text) => text?.trim() ?? null);
37
+ .then(text => text?.trim() ?? null);
46
38
 
47
39
  // Check if inside a git repo
48
40
  const isGitRepo = await git("rev-parse", "--is-inside-work-tree");
@@ -86,7 +78,7 @@ function firstNonEmptyLine(value: string | null): string | null {
86
78
  if (!value) return null;
87
79
  const line = value
88
80
  .split("\n")
89
- .map((entry) => entry.trim())
81
+ .map(entry => entry.trim())
90
82
  .filter(Boolean)[0];
91
83
  return line ?? null;
92
84
  }
@@ -94,9 +86,9 @@ function firstNonEmptyLine(value: string | null): string | null {
94
86
  function parseWmicTable(output: string, header: string): string | null {
95
87
  const lines = output
96
88
  .split("\n")
97
- .map((line) => line.trim())
89
+ .map(line => line.trim())
98
90
  .filter(Boolean);
99
- const filtered = lines.filter((line) => line.toLowerCase() !== header.toLowerCase());
91
+ const filtered = lines.filter(line => line.toLowerCase() !== header.toLowerCase());
100
92
  return filtered[0] ?? null;
101
93
  }
102
94
 
@@ -137,8 +129,8 @@ function listAgentsMdFiles(root: string, limit: number): string[] {
137
129
  new Bun.Glob(AGENTS_MD_PATTERN).scanSync({ cwd: root, onlyFiles: true, dot: false, absolute: false }),
138
130
  );
139
131
  const normalized = entries
140
- .map((entry) => normalizePath(entry))
141
- .filter((entry) => entry.length > 0 && !entry.includes("node_modules"))
132
+ .map(entry => normalizePath(entry))
133
+ .filter(entry => entry.length > 0 && !entry.includes("node_modules"))
142
134
  .sort();
143
135
  return normalized.length > limit ? normalized.slice(0, limit) : normalized;
144
136
  } catch {
@@ -274,8 +266,8 @@ async function getCpuModel(): Promise<string | null> {
274
266
  if (lscpu) {
275
267
  const match = lscpu
276
268
  .split("\n")
277
- .map((line) => line.trim())
278
- .find((line) => line.toLowerCase().startsWith("model name:"));
269
+ .map(line => line.trim())
270
+ .find(line => line.toLowerCase().startsWith("model name:"));
279
271
  if (match) return match.split(":").slice(1).join(":").trim();
280
272
  }
281
273
  const cpuInfo = await Bun.file("/proc/cpuinfo")
@@ -370,7 +362,7 @@ function normalizeDesktopValue(value: string): string {
370
362
  if (!trimmed) return "unknown";
371
363
  const parts = trimmed
372
364
  .split(":")
373
- .map((part) => part.trim())
365
+ .map(part => part.trim())
374
366
  .filter(Boolean);
375
367
  return parts[0] ?? trimmed;
376
368
  }
@@ -436,14 +428,15 @@ interface SystemInfoCache {
436
428
  }
437
429
 
438
430
  function getSystemInfoCachePath(): string {
439
- return join(homedir(), ".omp", "system_info.json");
431
+ return path.join(os.homedir(), ".omp", "system_info.json");
440
432
  }
441
433
 
442
434
  async function loadSystemInfoCache(): Promise<SystemInfoCache | null> {
443
435
  try {
444
436
  const cachePath = getSystemInfoCachePath();
445
- if (!existsSync(cachePath)) return null;
446
- const content = await Bun.file(cachePath).json();
437
+ const file = Bun.file(cachePath);
438
+ if (!(await file.exists())) return null;
439
+ const content = await file.json();
447
440
  return content as SystemInfoCache;
448
441
  } catch {
449
442
  return null;
@@ -494,7 +487,7 @@ async function getDiskInfo(): Promise<string | null> {
494
487
  .text()
495
488
  .catch(() => null);
496
489
  if (!output) return null;
497
- const lines = output.split("\n").filter((l) => l.trim() && !l.startsWith("Node"));
490
+ const lines = output.split("\n").filter(l => l.trim() && !l.startsWith("Node"));
498
491
  const disks: string[] = [];
499
492
  for (const line of lines) {
500
493
  const parts = line.split(",");
@@ -590,7 +583,7 @@ export async function loadProjectContextFiles(
590
583
  const result = await loadCapability(contextFileCapability.id, { cwd: resolvedCwd });
591
584
 
592
585
  // Convert ContextFile items and preserve depth info
593
- const files = result.items.map((item) => {
586
+ const files = result.items.map(item => {
594
587
  const contextFile = item as ContextFile;
595
588
  return {
596
589
  path: contextFile.path,
@@ -622,8 +615,8 @@ export async function loadSystemPromptFiles(options: LoadContextFilesOptions = {
622
615
  if (result.items.length === 0) return null;
623
616
 
624
617
  // Combine all SYSTEM.md contents (user-level first, then project-level)
625
- const userLevel = result.items.filter((item) => item.level === "user");
626
- const projectLevel = result.items.filter((item) => item.level === "project");
618
+ const userLevel = result.items.filter(item => item.level === "user");
619
+ const projectLevel = result.items.filter(item => item.level === "project");
627
620
 
628
621
  const parts: string[] = [];
629
622
  for (const item of [...userLevel, ...projectLevel]) {