@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
@@ -7,24 +7,23 @@
7
7
  *
8
8
  * The mode is determined by the `edit.patchMode` setting.
9
9
  */
10
-
11
- import { mkdir } from "node:fs/promises";
10
+ import * as fs from "node:fs/promises";
12
11
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
13
12
  import { StringEnum } from "@oh-my-pi/pi-ai";
14
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
13
+ import { Type } from "@sinclair/typebox";
14
+ import { renderPromptTemplate } from "../config/prompt-templates";
15
15
  import {
16
16
  createLspWritethrough,
17
17
  type FileDiagnosticsResult,
18
18
  flushLspWritethroughBatch,
19
19
  type WritethroughCallback,
20
20
  writethroughNoop,
21
- } from "@oh-my-pi/pi-coding-agent/lsp/index";
22
- import patchDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/patch.md" with { type: "text" };
23
- import replaceDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/replace.md" with { type: "text" };
24
- import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools/index";
25
- import { outputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
26
- import { resolveToCwd } from "@oh-my-pi/pi-coding-agent/tools/path-utils";
27
- import { Type } from "@sinclair/typebox";
21
+ } from "../lsp";
22
+ import patchDescription from "../prompts/tools/patch.md" with { type: "text" };
23
+ import replaceDescription from "../prompts/tools/replace.md" with { type: "text" };
24
+ import type { ToolSession } from "../tools";
25
+ import { outputMeta } from "../tools/output-meta";
26
+ import { resolveToCwd } from "../tools/path-utils";
28
27
  import { applyPatch } from "./applicator";
29
28
  import { generateDiffString, generateUnifiedDiffString, replaceText } from "./diff";
30
29
  import { DEFAULT_FUZZY_THRESHOLD, findMatch } from "./fuzzy";
@@ -48,13 +47,7 @@ export { computeEditDiff, computePatchDiff, generateDiffString, generateUnifiedD
48
47
  export { DEFAULT_FUZZY_THRESHOLD, findContextLine, findMatch as findEditMatch, findMatch, seekSequence } from "./fuzzy";
49
48
 
50
49
  // Normalization
51
- export {
52
- adjustIndentation,
53
- detectLineEnding,
54
- normalizeToLF,
55
- restoreLineEndings,
56
- stripBom,
57
- } from "./normalize";
50
+ export { adjustIndentation, detectLineEnding, normalizeToLF, restoreLineEndings, stripBom } from "./normalize";
58
51
 
59
52
  // Parsing
60
53
  export { normalizeCreateContent, normalizeDiff, parseHunks as parseDiffHunks } from "./parser";
@@ -166,7 +159,7 @@ class LspFileSystem implements FileSystem {
166
159
  }
167
160
 
168
161
  async mkdir(path: string): Promise<void> {
169
- await mkdir(path, { recursive: true });
162
+ await fs.mkdir(path, { recursive: true });
170
163
  }
171
164
 
172
165
  getDiagnostics(): FileDiagnosticsResult | undefined {
@@ -189,7 +189,7 @@ export function convertLeadingTabsToSpaces(text: string, spacesPerTab: number):
189
189
  if (spacesPerTab <= 0) return text;
190
190
  return text
191
191
  .split("\n")
192
- .map((line) => {
192
+ .map(line => {
193
193
  const trimmed = line.trimStart();
194
194
  if (trimmed.length === 0) return line;
195
195
  const leading = getLeadingWhitespace(line);
@@ -212,7 +212,7 @@ export function normalizeUnicode(s: string): string {
212
212
  return s
213
213
  .trim()
214
214
  .split("")
215
- .map((c) => {
215
+ .map(c => {
216
216
  const code = c.charCodeAt(0);
217
217
 
218
218
  // Various dash/hyphen code-points → ASCII '-'
@@ -367,7 +367,7 @@ export function adjustIndentation(oldText: string, actualText: string, newText:
367
367
  }
368
368
 
369
369
  const delta = deltas[0];
370
- if (!deltas.every((value) => value === delta)) {
370
+ if (!deltas.every(value => value === delta)) {
371
371
  return newText;
372
372
  }
373
373
 
@@ -380,7 +380,7 @@ export function adjustIndentation(oldText: string, actualText: string, newText:
380
380
  }
381
381
 
382
382
  const indentChar = actualProfile.char ?? oldProfile.char ?? detectIndentChar(actualText);
383
- const adjusted = newText.split("\n").map((line) => {
383
+ const adjusted = newText.split("\n").map(line => {
384
384
  if (line.trim().length === 0) {
385
385
  return line;
386
386
  }
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Normalize applied patch output into a canonical edit tool payload.
3
3
  */
4
-
5
4
  import { generateUnifiedDiffString } from "./diff";
6
5
  import { normalizeToLF, stripBom } from "./normalize";
7
6
  import { parseHunks } from "./parser";
@@ -37,7 +36,7 @@ function applyAnchors(diff: string, anchors: Array<string | undefined> | undefin
37
36
 
38
37
  function deriveAnchors(diff: string): Array<string | undefined> {
39
38
  const hunks = parseHunks(diff);
40
- return hunks.map((hunk) => {
39
+ return hunks.map(hunk => {
41
40
  if (hunk.oldLines.length === 0 || hunk.newLines.length === 0) {
42
41
  return undefined;
43
42
  }
@@ -6,7 +6,6 @@
6
6
  * - Unified diff format (@@ -X,Y +A,B @@)
7
7
  * - Codex-style wrapped patches (*** Begin Patch / *** End Patch)
8
8
  */
9
-
10
9
  import type { DiffHunk } from "./types";
11
10
  import { ApplyPatchError, ParseError } from "./types";
12
11
 
@@ -90,7 +89,7 @@ export function normalizeDiff(diff: string): string {
90
89
  // Layer 2: Strip Codex-style file operation markers and unified diff metadata
91
90
  // NOTE: Do NOT strip "*** End of File" - that's a valid marker within hunks, not a wrapper
92
91
  // IMPORTANT: Only strip actual metadata lines, NOT diff content lines (starting with space, +, or -)
93
- lines = lines.filter((line) => {
92
+ lines = lines.filter(line => {
94
93
  // Preserve diff content lines even if their content looks like metadata
95
94
  // Note: `--- ` and `+++ ` are metadata, not content lines
96
95
  if (isDiffContentLine(line)) {
@@ -130,12 +129,12 @@ export function normalizeDiff(diff: string): string {
130
129
  */
131
130
  export function normalizeCreateContent(content: string): string {
132
131
  const lines = content.split("\n");
133
- const nonEmptyLines = lines.filter((l) => l.length > 0);
132
+ const nonEmptyLines = lines.filter(l => l.length > 0);
134
133
 
135
134
  // Check if all non-empty lines start with "+ " or "+"
136
- if (nonEmptyLines.length > 0 && nonEmptyLines.every((l) => l.startsWith("+ ") || l.startsWith("+"))) {
135
+ if (nonEmptyLines.length > 0 && nonEmptyLines.every(l => l.startsWith("+ ") || l.startsWith("+"))) {
137
136
  return lines
138
- .map((l) => {
137
+ .map(l => {
139
138
  if (l.startsWith("+ ")) return l.slice(2);
140
139
  if (l.startsWith("+")) return l.slice(1);
141
140
  return l;
@@ -390,18 +389,18 @@ function parseOneHunk(lines: string[], lineNumber: number, allowMissingContext:
390
389
  }
391
390
 
392
391
  function stripLineNumberPrefixes(hunk: DiffHunk): void {
393
- const allLines = [...hunk.oldLines, ...hunk.newLines].filter((line) => line.trim().length > 0);
392
+ const allLines = [...hunk.oldLines, ...hunk.newLines].filter(line => line.trim().length > 0);
394
393
  if (allLines.length < 2) return;
395
394
 
396
395
  const numberMatches = allLines
397
- .map((line) => line.match(/^\s*(\d{1,6})\s+(.+)$/u))
396
+ .map(line => line.match(/^\s*(\d{1,6})\s+(.+)$/u))
398
397
  .filter((match): match is RegExpMatchArray => match !== null);
399
398
 
400
399
  if (numberMatches.length < Math.max(2, Math.ceil(allLines.length * 0.6))) {
401
400
  return;
402
401
  }
403
402
 
404
- const numbers = numberMatches.map((match) => Number(match[1]));
403
+ const numbers = numberMatches.map(match => Number(match[1]));
405
404
  let sequential = 0;
406
405
  for (let i = 1; i < numbers.length; i++) {
407
406
  if (numbers[i] === numbers[i - 1] + 1) {
@@ -515,7 +514,7 @@ export function parseHunks(diff: string): DiffHunk[] {
515
514
  continue;
516
515
  }
517
516
 
518
- if (trimmed.startsWith("@@") && lines.slice(i + 1).every((l) => l.trim() === "")) {
517
+ if (trimmed.startsWith("@@") && lines.slice(i + 1).every(l => l.trim() === "")) {
519
518
  break;
520
519
  }
521
520
 
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Shared utilities for edit tool TUI rendering.
3
3
  */
4
-
5
4
  import type { ToolCallContext } from "@oh-my-pi/pi-agent-core";
6
- import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
7
- import type { FileDiagnosticsResult } from "@oh-my-pi/pi-coding-agent/lsp/index";
8
- import { renderDiff as renderDiffColored } from "@oh-my-pi/pi-coding-agent/modes/components/diff";
9
- import { getLanguageFromPath, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
10
- import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
5
+ import type { Component } from "@oh-my-pi/pi-tui";
6
+ import { Text } from "@oh-my-pi/pi-tui";
7
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
8
+ import type { FileDiagnosticsResult } from "../lsp";
9
+ import { renderDiff as renderDiffColored } from "../modes/components/diff";
10
+ import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
11
+ import type { OutputMeta } from "../tools/output-meta";
11
12
  import {
12
13
  formatExpandHint,
13
14
  formatStatusIcon,
@@ -15,10 +16,9 @@ import {
15
16
  shortenPath,
16
17
  ToolUIKit,
17
18
  truncateDiffByHunk,
18
- } from "@oh-my-pi/pi-coding-agent/tools/render-utils";
19
- import type { RenderCallOptions } from "@oh-my-pi/pi-coding-agent/tools/renderers";
20
- import type { Component } from "@oh-my-pi/pi-tui";
21
- import { Text } from "@oh-my-pi/pi-tui";
19
+ } from "../tools/render-utils";
20
+ import type { RenderCallOptions } from "../tools/renderers";
21
+ import { renderStatusLine } from "../tui";
22
22
  import type { DiffError, DiffResult, Operation } from "./types";
23
23
 
24
24
  // ═══════════════════════════════════════════════════════════════════════════
@@ -37,7 +37,7 @@ export function getLspBatchRequest(toolCall: ToolCallContext | undefined): { id:
37
37
  if (!hasOtherWrites) {
38
38
  return undefined;
39
39
  }
40
- const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some((call) => LSP_BATCH_TOOLS.has(call.name));
40
+ const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some(call => LSP_BATCH_TOOLS.has(call.name));
41
41
  return { id: toolCall.batchId, flush: !hasLaterWrites };
42
42
  }
43
43
 
@@ -178,9 +178,28 @@ export const editToolRenderer = {
178
178
  let text = `${ui.title(opTitle)} ${spinner ? `${spinner} ` : ""}${editIcon} ${pathDisplay}`;
179
179
 
180
180
  // Show streaming preview of diff/content
181
- const streamingContent = args.diff ?? args.newText ?? args.patch;
182
- if (streamingContent) {
183
- text += formatStreamingDiff(streamingContent, rawPath, uiTheme);
181
+ if (args.diff && args.op) {
182
+ text += formatStreamingDiff(args.diff, rawPath, uiTheme);
183
+ } else if (args.diff) {
184
+ const previewLines = args.diff.split("\n");
185
+ const maxLines = 6;
186
+ text += "\n\n";
187
+ for (const line of previewLines.slice(0, maxLines)) {
188
+ text += `${uiTheme.fg("toolOutput", ui.truncate(line, 80))}\n`;
189
+ }
190
+ if (previewLines.length > maxLines) {
191
+ text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} ${previewLines.length - maxLines} more lines`);
192
+ }
193
+ } else if (args.newText || args.patch) {
194
+ const previewLines = (args.newText ?? args.patch ?? "").split("\n");
195
+ const maxLines = 6;
196
+ text += "\n\n";
197
+ for (const line of previewLines.slice(0, maxLines)) {
198
+ text += `${uiTheme.fg("toolOutput", ui.truncate(line, 80))}\n`;
199
+ }
200
+ if (previewLines.length > maxLines) {
201
+ text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} ${previewLines.length - maxLines} more lines`);
202
+ }
184
203
  }
185
204
 
186
205
  return new Text(text, 0, 0);
@@ -221,7 +240,15 @@ export const editToolRenderer = {
221
240
 
222
241
  // Show operation type for patch mode
223
242
  const opTitle = op === "create" ? "Create" : op === "delete" ? "Delete" : "Edit";
224
- let text = `${uiTheme.fg("toolTitle", uiTheme.bold(opTitle))} ${editIcon} ${pathDisplay}`;
243
+ const header = renderStatusLine(
244
+ {
245
+ icon: result.isError ? "error" : "success",
246
+ title: opTitle,
247
+ description: `${editIcon} ${pathDisplay}`,
248
+ },
249
+ uiTheme,
250
+ );
251
+ let text = header;
225
252
 
226
253
  // Skip metadata line for delete operations
227
254
  if (op !== "delete") {
@@ -231,7 +258,7 @@ export const editToolRenderer = {
231
258
 
232
259
  if (result.isError) {
233
260
  // Show error from result
234
- const errorText = result.content?.find((c) => c.type === "text")?.text ?? "";
261
+ const errorText = result.content?.find(c => c.type === "text")?.text ?? "";
235
262
  if (errorText) {
236
263
  text += `\n\n${uiTheme.fg("error", errorText)}`;
237
264
  }
@@ -59,6 +59,8 @@ Results are keyed by task `id` (e.g., "AuthProvider", "AuthApi").
59
59
  If you discussed requirements, plans, schemas, or decisions with the user, you MUST include that information in `context`. Subagents cannot see prior messages—they start fresh with only what you explicitly pass them.
60
60
 
61
61
  **Never call Task multiple times in parallel.** Use a single Task call with multiple entries in the `tasks` array. Parallel Task calls waste resources and bypass coordination.
62
+
63
+ **For code changes, subagents write files directly.** Never ask an agent to "return the changes" for you to apply—they have Edit and Write tools. Their context window holds the work; asking them to report back wastes it.
62
64
  </critical>
63
65
 
64
66
  <example>
package/src/sdk.ts CHANGED
@@ -25,32 +25,30 @@
25
25
  * });
26
26
  * ```
27
27
  */
28
-
29
- import { join } from "node:path";
28
+ import * as fs from "node:fs";
29
+ import * as path from "node:path";
30
30
  import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
31
31
  import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
32
- import { loadCapability } from "@oh-my-pi/pi-coding-agent/capability/index";
33
- import { type Rule, ruleCapability } from "@oh-my-pi/pi-coding-agent/capability/rule";
34
- import { getAgentDir, getConfigDirPaths } from "@oh-my-pi/pi-coding-agent/config";
35
32
  import type { Component } from "@oh-my-pi/pi-tui";
36
- // Import discovery to register all providers on startup
37
33
  import { logger, postmortem } from "@oh-my-pi/pi-utils";
34
+ import { YAML } from "bun";
38
35
  import chalk from "chalk";
39
- import "./discovery";
40
- import { CursorExecHandlers } from "@oh-my-pi/pi-coding-agent/cursor";
41
- import { initializeWithSettings } from "@oh-my-pi/pi-coding-agent/discovery";
42
- import { TtsrManager } from "@oh-my-pi/pi-coding-agent/export/ttsr";
43
- import { disposeAllKernelSessions } from "@oh-my-pi/pi-coding-agent/ipy/executor";
44
- import { closeAllConnections } from "@oh-my-pi/pi-coding-agent/ssh/connection-manager";
45
- import { unmountAll } from "@oh-my-pi/pi-coding-agent/ssh/sshfs-mount";
36
+ // Import discovery to register all providers on startup
37
+ import { loadCapability } from "./capability";
38
+ import { type Rule, ruleCapability } from "./capability/rule";
39
+ import { getAgentDir, getConfigDirPaths } from "./config";
46
40
  import { ModelRegistry } from "./config/model-registry";
47
41
  import { formatModelString, parseModelString } from "./config/model-resolver";
48
42
  import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
49
43
  import { type Settings, SettingsManager, type SkillsSettings } from "./config/settings-manager";
44
+ import { CursorExecHandlers } from "./cursor";
45
+ import "./discovery";
46
+ import { initializeWithSettings } from "./discovery";
47
+ import { TtsrManager } from "./export/ttsr";
50
48
  import {
51
49
  type CustomCommandsLoadResult,
52
50
  loadCustomCommands as loadCustomCommandsInternal,
53
- } from "./extensibility/custom-commands/index";
51
+ } from "./extensibility/custom-commands";
54
52
  import type { CustomTool, CustomToolContext, CustomToolSessionEvent } from "./extensibility/custom-tools/types";
55
53
  import {
56
54
  discoverAndLoadExtensions,
@@ -64,7 +62,7 @@ import {
64
62
  loadExtensions,
65
63
  type ToolDefinition,
66
64
  wrapRegisteredTools,
67
- } from "./extensibility/extensions/index";
65
+ } from "./extensibility/extensions";
68
66
  import { loadSkills as loadSkillsInternal, type Skill, type SkillWarning } from "./extensibility/skills";
69
67
  import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./extensibility/slash-commands";
70
68
  import {
@@ -74,18 +72,20 @@ import {
74
72
  RuleProtocolHandler,
75
73
  SkillProtocolHandler,
76
74
  } from "./internal-urls";
77
- import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index";
75
+ import { disposeAllKernelSessions } from "./ipy/executor";
76
+ import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp";
78
77
  import { AgentSession } from "./session/agent-session";
79
78
  import { AuthStorage } from "./session/auth-storage";
80
79
  import { convertToLlm } from "./session/messages";
81
80
  import { SessionManager } from "./session/session-manager";
82
81
  import { migrateJsonStorage } from "./session/storage-migration";
82
+ import { closeAllConnections } from "./ssh/connection-manager";
83
+ import { unmountAll } from "./ssh/sshfs-mount";
83
84
  import {
84
85
  buildSystemPrompt as buildSystemPromptInternal,
85
86
  loadProjectContextFiles as loadContextFilesInternal,
86
87
  } from "./system-prompt";
87
- import { ToolContextStore } from "./tools/context";
88
- import { getGeminiImageTools } from "./tools/gemini-image";
88
+ import { AgentOutputManager } from "./task/output-manager";
89
89
  import {
90
90
  BashTool,
91
91
  BUILTIN_TOOLS,
@@ -104,7 +104,9 @@ import {
104
104
  type ToolSession,
105
105
  WriteTool,
106
106
  warmupLspServers,
107
- } from "./tools/index";
107
+ } from "./tools";
108
+ import { ToolContextStore } from "./tools/context";
109
+ import { getGeminiImageTools } from "./tools/gemini-image";
108
110
  import { wrapToolsWithMetaNotice } from "./tools/output-meta";
109
111
  import { EventBus } from "./utils/event-bus";
110
112
  import { time } from "./utils/timings";
@@ -201,24 +203,21 @@ export interface CreateAgentSessionResult {
201
203
 
202
204
  // Re-exports
203
205
 
204
- export type { PromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
205
- export type { Settings, SkillsSettings } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
206
- export type {
207
- CustomCommand,
208
- CustomCommandFactory,
209
- } from "@oh-my-pi/pi-coding-agent/extensibility/custom-commands/types";
210
- export type { CustomTool, CustomToolFactory } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
206
+ export type { PromptTemplate } from "./config/prompt-templates";
207
+ export type { Settings, SkillsSettings } from "./config/settings-manager";
208
+ export type { CustomCommand, CustomCommandFactory } from "./extensibility/custom-commands/types";
209
+ export type { CustomTool, CustomToolFactory } from "./extensibility/custom-tools/types";
211
210
  export type {
212
211
  ExtensionAPI,
213
212
  ExtensionCommandContext,
214
213
  ExtensionContext,
215
214
  ExtensionFactory,
216
215
  ToolDefinition,
217
- } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/index";
218
- export type { Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
219
- export type { FileSlashCommand } from "@oh-my-pi/pi-coding-agent/extensibility/slash-commands";
220
- export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp/index";
221
- export type { Tool } from "./tools/index";
216
+ } from "./extensibility/extensions";
217
+ export type { Skill } from "./extensibility/skills";
218
+ export type { FileSlashCommand } from "./extensibility/slash-commands";
219
+ export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp";
220
+ export type { Tool } from "./tools";
222
221
 
223
222
  export {
224
223
  // Individual tool classes (for custom usage)
@@ -250,43 +249,80 @@ function getDefaultAgentDir(): string {
250
249
  * Reads from primary path first, then falls back to legacy paths (.pi, .claude).
251
250
  */
252
251
  export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
253
- const primaryPath = join(agentDir, "auth.json");
252
+ const primaryPath = path.join(agentDir, "auth.json");
254
253
  // Get all auth.json paths (user-level only), excluding the primary
255
254
  const allPaths = getConfigDirPaths("auth.json", { project: false });
256
- const fallbackPaths = allPaths.filter((p) => p !== primaryPath);
255
+ const fallbackPaths = allPaths.filter(p => p !== primaryPath);
257
256
 
258
257
  logger.debug("discoverAuthStorage", { agentDir, primaryPath, allPaths, fallbackPaths });
259
258
 
260
259
  // Migrate legacy JSON files (settings.json, auth.json) to SQLite before loading
261
260
  await migrateJsonStorage({
262
261
  agentDir,
263
- settingsPath: join(agentDir, "settings.json"),
262
+ settingsPath: path.join(agentDir, "settings.json"),
264
263
  authPaths: [primaryPath, ...fallbackPaths],
265
264
  });
266
265
 
267
- const storage = new AuthStorage(primaryPath, fallbackPaths);
266
+ const storage = await AuthStorage.create(primaryPath, fallbackPaths);
268
267
  await storage.reload();
269
268
  return storage;
270
269
  }
271
270
 
272
271
  /**
273
272
  * Create a ModelRegistry with fallback support.
274
- * Reads from primary path first, then falls back to legacy paths (.pi, .claude).
273
+ * Prefers models.yml over models.json. Reads from primary path first,
274
+ * then falls back to legacy paths (.pi, .claude).
275
275
  */
276
- export async function discoverModels(
277
- authStorage: AuthStorage,
278
- agentDir: string = getDefaultAgentDir(),
279
- ): Promise<ModelRegistry> {
280
- const primaryPath = join(agentDir, "models.json");
281
- // Get all models.json paths (user-level only), excluding the primary
282
- const allPaths = getConfigDirPaths("models.json", { project: false });
283
- const fallbackPaths = allPaths.filter((p) => p !== primaryPath);
276
+ export function discoverModels(authStorage: AuthStorage, agentDir: string = getDefaultAgentDir()): ModelRegistry {
277
+ const yamlPath = path.join(agentDir, "models.yml");
278
+ const jsonPath = path.join(agentDir, "models.json");
279
+
280
+ // Check existence of yaml and json files
281
+ let yamlExists = fs.existsSync(yamlPath);
282
+ let jsonExists = fs.existsSync(jsonPath);
283
+
284
+ // Migrate models.json to models.yml if yaml doesn't exist but json does
285
+ if (!yamlExists && jsonExists) {
286
+ migrateModelsJsonToYaml(jsonPath, yamlPath);
287
+ yamlExists = fs.existsSync(yamlPath);
288
+ jsonExists = fs.existsSync(jsonPath);
289
+ }
290
+
291
+ // Prefer models.yml, fall back to models.json
292
+ const primaryPath = yamlExists ? yamlPath : jsonPath;
293
+
294
+ // Get all models config paths (user-level only), excluding the primary
295
+ const yamlPaths = getConfigDirPaths("models.yml", { project: false });
296
+ const jsonPaths = getConfigDirPaths("models.json", { project: false });
297
+ const allPaths = [...yamlPaths, ...jsonPaths];
298
+ const existenceResults = allPaths.map(p => {
299
+ return { p, exists: fs.existsSync(p) };
300
+ });
301
+ const fallbackPaths = existenceResults.filter(({ p, exists }) => p !== primaryPath && exists).map(({ p }) => p);
284
302
 
285
303
  logger.debug("discoverModels", { primaryPath, fallbackPaths });
304
+ return new ModelRegistry(authStorage, primaryPath, fallbackPaths);
305
+ }
286
306
 
287
- const registry = new ModelRegistry(authStorage, primaryPath, fallbackPaths);
288
- await registry.refresh();
289
- return registry;
307
+ /**
308
+ * Migrate models.json to models.yml.
309
+ * Creates models.yml from models.json and renames the json file to .bak.
310
+ */
311
+ function migrateModelsJsonToYaml(jsonPath: string, yamlPath: string): void {
312
+ try {
313
+ const content = fs.readFileSync(jsonPath, "utf-8");
314
+ const parsed = JSON.parse(content);
315
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
316
+ logger.warn("migrateModelsJsonToYaml: invalid models.json structure", { path: jsonPath });
317
+ return;
318
+ }
319
+ fs.mkdirSync(path.dirname(yamlPath), { recursive: true });
320
+ fs.writeFileSync(yamlPath, YAML.stringify(parsed, null, 2));
321
+ fs.renameSync(jsonPath, `${jsonPath}.bak`);
322
+ logger.debug("migrateModelsJsonToYaml: migrated models.json to models.yml", { from: jsonPath, to: yamlPath });
323
+ } catch (error) {
324
+ logger.warn("migrateModelsJsonToYaml: migration failed", { error: String(error) });
325
+ }
290
326
  }
291
327
 
292
328
  /**
@@ -487,7 +523,7 @@ function customToolToDefinition(tool: CustomTool): ToolDefinition {
487
523
  }
488
524
 
489
525
  function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
490
- return (api) => {
526
+ return api => {
491
527
  for (const tool of tools) {
492
528
  api.registerTool(customToolToDefinition(tool));
493
529
  }
@@ -564,7 +600,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
564
600
 
565
601
  // Use provided or create AuthStorage and ModelRegistry
566
602
  const authStorage = options.authStorage ?? (await discoverAuthStorage(agentDir));
567
- const modelRegistry = options.modelRegistry ?? (await discoverModels(authStorage, agentDir));
603
+ const modelRegistry = options.modelRegistry ?? discoverModels(authStorage, agentDir);
568
604
  time("discoverModels");
569
605
 
570
606
  const settingsManager = options.settingsManager ?? (await SettingsManager.create(cwd, agentDir));
@@ -622,9 +658,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
622
658
  if (!model) {
623
659
  const allModels = modelRegistry.getAll();
624
660
  const keyResults = await Promise.all(
625
- allModels.map(async (m) => ({ model: m, hasKey: !!(await modelRegistry.getApiKey(m, sessionId)) })),
661
+ allModels.map(async m => ({ model: m, hasKey: !!(await modelRegistry.getApiKey(m, sessionId)) })),
626
662
  );
627
- model = keyResults.find((r) => r.hasKey)?.model;
663
+ model = keyResults.find(r => r.hasKey)?.model;
628
664
  time("findAvailableModel");
629
665
  if (model) {
630
666
  if (modelFallbackMessage) {
@@ -734,6 +770,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
734
770
  );
735
771
  toolSession.internalRouter = internalRouter;
736
772
  toolSession.getArtifactsDir = getArtifactsDir;
773
+ toolSession.agentOutputManager = new AgentOutputManager(getArtifactsDir);
737
774
 
738
775
  // Create and wrap tools with meta notice formatting
739
776
  const rawBuiltinTools = await createTools(toolSession, options.toolNames);
@@ -746,7 +783,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
746
783
  const customTools: CustomTool[] = [];
747
784
  if (enableMCP) {
748
785
  const mcpResult = await discoverAndLoadMCPTools(cwd, {
749
- onConnecting: (serverNames) => {
786
+ onConnecting: serverNames => {
750
787
  if (options.hasUI && serverNames.length > 0) {
751
788
  process.stderr.write(
752
789
  chalk.gray(`Connecting to MCP servers: ${serverNames.join(", ")}...
@@ -775,7 +812,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
775
812
 
776
813
  if (mcpResult.tools.length > 0) {
777
814
  // MCP tools are LoadedCustomTool, extract the tool property
778
- customTools.push(...mcpResult.tools.map((loaded) => loaded.tool));
815
+ customTools.push(...mcpResult.tools.map(loaded => loaded.tool));
779
816
  }
780
817
  }
781
818
 
@@ -794,7 +831,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
794
831
  enableCompany: exaSettings.enableCompany,
795
832
  });
796
833
  // Filter out the base web_search (already in built-in tools), add specialized Exa tools
797
- const specializedTools = exaWebSearchTools.filter((t) => t.name !== "web_search");
834
+ const specializedTools = exaWebSearchTools.filter(t => t.name !== "web_search");
798
835
  if (specializedTools.length > 0) {
799
836
  customTools.push(...specializedTools);
800
837
  }
@@ -884,7 +921,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
884
921
  const registeredTools = extensionRunner?.getAllRegisteredTools() ?? [];
885
922
  const allCustomTools = [
886
923
  ...registeredTools,
887
- ...(options.customTools?.map((tool) => {
924
+ ...(options.customTools?.map(tool => {
888
925
  const definition = isCustomTool(tool) ? customToolToDefinition(tool) : tool;
889
926
  return { definition, extensionPath: "<sdk>" };
890
927
  }) ?? []),
@@ -914,7 +951,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
914
951
  cwd,
915
952
  tools: toolRegistry,
916
953
  getToolContext: () => toolContextStore.getContext(),
917
- emitEvent: (event) => cursorEventEmitter?.(event),
954
+ emitEvent: event => cursorEventEmitter?.(event),
918
955
  });
919
956
 
920
957
  const rebuildSystemPrompt = async (toolNames: string[], tools: Map<string, AgentTool>): Promise<string> => {
@@ -964,16 +1001,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
964
1001
  return converted;
965
1002
  }
966
1003
  // Filter out ImageContent from all messages, replacing with text placeholder
967
- return converted.map((msg) => {
1004
+ return converted.map(msg => {
968
1005
  if (msg.role === "user" || msg.role === "toolResult") {
969
1006
  const content = msg.content;
970
1007
  if (Array.isArray(content)) {
971
- const hasImages = content.some((c) => c.type === "image");
1008
+ const hasImages = content.some(c => c.type === "image");
972
1009
  if (hasImages) {
973
1010
  const filteredContent = content
974
- .map((c) =>
975
- c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c,
976
- )
1011
+ .map(c => (c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c))
977
1012
  .filter(
978
1013
  (c, i, arr) =>
979
1014
  // Dedupe consecutive "Image reading is disabled." texts
@@ -1007,7 +1042,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1007
1042
  convertToLlm: convertToLlmWithBlockImages,
1008
1043
  sessionId: sessionManager.getSessionId(),
1009
1044
  transformContext: extensionRunner
1010
- ? async (messages) => {
1045
+ ? async messages => {
1011
1046
  return extensionRunner.emitContext(messages);
1012
1047
  }
1013
1048
  : undefined,
@@ -1015,7 +1050,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1015
1050
  followUpMode: settingsManager.getFollowUpMode(),
1016
1051
  interruptMode: settingsManager.getInterruptMode(),
1017
1052
  thinkingBudgets: settingsManager.getThinkingBudgets(),
1018
- getToolContext: (tc) => toolContextStore.getContext(tc),
1053
+ getToolContext: tc => toolContextStore.getContext(tc),
1019
1054
  getApiKey: async () => {
1020
1055
  const currentModel = agent.state.model;
1021
1056
  if (!currentModel) {
@@ -1029,7 +1064,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1029
1064
  },
1030
1065
  cursorExecHandlers,
1031
1066
  });
1032
- cursorEventEmitter = (event) => agent.emitExternalEvent(event);
1067
+ cursorEventEmitter = event => agent.emitExternalEvent(event);
1033
1068
  time("createAgent");
1034
1069
 
1035
1070
  // Restore messages if session has existing data
@@ -1067,7 +1102,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1067
1102
  if (enableLsp && settingsManager.getLspDiagnosticsOnWrite()) {
1068
1103
  try {
1069
1104
  const result = await warmupLspServers(cwd, {
1070
- onConnecting: (serverNames) => {
1105
+ onConnecting: serverNames => {
1071
1106
  if (options.hasUI && serverNames.length > 0) {
1072
1107
  process.stderr.write(chalk.gray(`Starting LSP servers: ${serverNames.join(", ")}...\n`));
1073
1108
  }