@oh-my-pi/pi-coding-agent 7.0.0 → 8.0.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 (501) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +1 -1
  3. package/docs/hooks.md +2 -2
  4. package/docs/sdk.md +1 -1
  5. package/package.json +10 -10
  6. package/scripts/format-prompts.ts +143 -0
  7. package/scripts/generate-template.ts +1 -1
  8. package/src/cli/args.ts +3 -3
  9. package/src/cli/config-cli.ts +4 -4
  10. package/src/cli/file-processor.ts +3 -3
  11. package/src/cli/list-models.ts +2 -2
  12. package/src/cli/plugin-cli.ts +3 -3
  13. package/src/cli/session-picker.ts +2 -2
  14. package/src/cli/setup-cli.ts +2 -2
  15. package/src/cli/stats-cli.ts +1 -1
  16. package/src/cli/update-cli.ts +2 -2
  17. package/src/{core → config}/keybindings.ts +1 -1
  18. package/src/{core → config}/model-registry.ts +1 -1
  19. package/src/{core → config}/model-resolver.ts +3 -3
  20. package/src/{core → config}/prompt-templates.ts +2 -2
  21. package/src/{core → config}/settings-manager.ts +6 -6
  22. package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
  23. package/src/discovery/agents-md.ts +4 -4
  24. package/src/discovery/builtin.ts +17 -17
  25. package/src/discovery/claude.ts +12 -12
  26. package/src/discovery/cline.ts +6 -6
  27. package/src/discovery/codex.ts +21 -21
  28. package/src/discovery/cursor.ts +9 -9
  29. package/src/discovery/gemini.ts +9 -9
  30. package/src/discovery/github.ts +6 -6
  31. package/src/discovery/helpers.ts +4 -4
  32. package/src/discovery/index.ts +16 -16
  33. package/src/discovery/mcp-json.ts +4 -4
  34. package/src/discovery/ssh.ts +4 -4
  35. package/src/discovery/vscode.ts +4 -4
  36. package/src/discovery/windsurf.ts +6 -6
  37. package/src/{core/tools/exa → exa}/company.ts +2 -3
  38. package/src/{core/tools/exa → exa}/index.ts +2 -2
  39. package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
  40. package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
  41. package/src/{core/tools/exa → exa}/render.ts +3 -3
  42. package/src/{core/tools/exa → exa}/researcher.ts +1 -1
  43. package/src/{core/tools/exa → exa}/search.ts +2 -10
  44. package/src/{core/tools/exa → exa}/websets.ts +1 -1
  45. package/src/{core → exec}/bash-executor.ts +22 -6
  46. package/src/{core → export}/custom-share.ts +1 -1
  47. package/src/{core/export-html → export/html}/index.ts +3 -3
  48. package/src/{core → export}/ttsr.ts +2 -2
  49. package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
  50. package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
  51. package/src/{core → extensibility}/custom-commands/types.ts +1 -1
  52. package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
  53. package/src/{core → extensibility}/custom-tools/types.ts +6 -6
  54. package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
  55. package/src/{core → extensibility}/extensions/loader.ts +8 -8
  56. package/src/{core → extensibility}/extensions/runner.ts +3 -3
  57. package/src/{core → extensibility}/extensions/types.ts +15 -15
  58. package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
  59. package/src/{core → extensibility}/hooks/index.ts +1 -1
  60. package/src/{core → extensibility}/hooks/loader.ts +7 -7
  61. package/src/{core → extensibility}/hooks/runner.ts +4 -4
  62. package/src/{core → extensibility}/hooks/types.ts +9 -9
  63. package/src/{core → extensibility}/plugins/doctor.ts +1 -1
  64. package/src/{core → extensibility}/plugins/installer.ts +2 -3
  65. package/src/{core → extensibility}/plugins/paths.ts +1 -1
  66. package/src/{core → extensibility}/skills.ts +8 -48
  67. package/src/{core → extensibility}/slash-commands.ts +6 -6
  68. package/src/index.ts +127 -128
  69. package/src/internal-urls/agent-protocol.ts +126 -0
  70. package/src/internal-urls/artifact-protocol.ts +93 -0
  71. package/src/internal-urls/index.ts +28 -0
  72. package/src/internal-urls/json-query.ts +126 -0
  73. package/src/internal-urls/router.ts +69 -0
  74. package/src/internal-urls/rule-protocol.ts +56 -0
  75. package/src/internal-urls/skill-protocol.ts +112 -0
  76. package/src/internal-urls/types.ts +48 -0
  77. package/src/{core/python-executor.ts → ipy/executor.ts} +51 -11
  78. package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +41 -325
  79. package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
  80. package/src/ipy/prelude.ts +3 -0
  81. package/src/{core/tools/lsp → lsp}/client.ts +7 -6
  82. package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
  83. package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
  84. package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
  85. package/src/{core/tools/lsp → lsp}/config.ts +1 -1
  86. package/src/{core/tools/lsp → lsp}/index.ts +16 -15
  87. package/src/{core/tools/lsp → lsp}/render.ts +2 -2
  88. package/src/{core/tools/lsp → lsp}/types.ts +14 -16
  89. package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
  90. package/src/main.ts +12 -12
  91. package/src/{core/mcp → mcp}/config.ts +8 -8
  92. package/src/{core/mcp → mcp}/loader.ts +5 -6
  93. package/src/{core/mcp → mcp}/manager.ts +2 -2
  94. package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
  95. package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
  96. package/src/{core/mcp → mcp}/transports/http.ts +7 -1
  97. package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
  98. package/src/{core/mcp → mcp}/types.ts +1 -1
  99. package/src/migrations.ts +2 -2
  100. package/src/modes/{interactive/components → components}/armin.ts +1 -1
  101. package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
  102. package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
  103. package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
  104. package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
  105. package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
  106. package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
  107. package/src/modes/{interactive/components → components}/diff.ts +1 -1
  108. package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
  109. package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
  110. package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
  111. package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
  112. package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
  113. package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
  114. package/src/modes/{interactive/components → components}/footer.ts +3 -3
  115. package/src/modes/{interactive/components → components}/history-search.ts +2 -2
  116. package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
  117. package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
  118. package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
  119. package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
  120. package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
  121. package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
  122. package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
  123. package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
  124. package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
  125. package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
  126. package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
  127. package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
  128. package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
  129. package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
  130. package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
  131. package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
  132. package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
  133. package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
  134. package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
  135. package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
  136. package/src/modes/{interactive/components → components}/status-line.ts +3 -3
  137. package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
  138. package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
  139. package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
  140. package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
  141. package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
  142. package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
  143. package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
  144. package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
  145. package/src/modes/{interactive/components → components}/user-message.ts +1 -1
  146. package/src/modes/{interactive/components → components}/welcome.ts +2 -2
  147. package/src/modes/{interactive/controllers → controllers}/command-controller.ts +32 -30
  148. package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
  149. package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
  150. package/src/modes/{interactive/controllers → controllers}/input-controller.ts +6 -6
  151. package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
  152. package/src/modes/index.ts +1 -1
  153. package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +14 -14
  154. package/src/modes/print-mode.ts +1 -1
  155. package/src/modes/rpc/rpc-client.ts +3 -3
  156. package/src/modes/rpc/rpc-mode.ts +3 -3
  157. package/src/modes/rpc/rpc-types.ts +3 -3
  158. package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
  159. package/src/modes/{interactive/types.ts → types.ts} +8 -9
  160. package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
  161. package/src/{core/tools/patch → patch}/applicator.ts +1 -1
  162. package/src/{core/tools/patch → patch}/diff.ts +1 -1
  163. package/src/{core/tools/patch → patch}/index.ts +31 -36
  164. package/src/{core/tools/patch → patch}/shared.ts +9 -6
  165. package/src/prompts/agents/explore.md +83 -46
  166. package/src/prompts/agents/init.md +9 -4
  167. package/src/prompts/agents/plan.md +8 -7
  168. package/src/prompts/agents/reviewer.md +36 -18
  169. package/src/prompts/agents/task.md +4 -4
  170. package/src/prompts/compaction/branch-summary-preamble.md +0 -1
  171. package/src/prompts/review-request.md +0 -1
  172. package/src/prompts/system/custom-system-prompt.md +2 -14
  173. package/src/prompts/system/file-operations.md +0 -2
  174. package/src/prompts/system/system-prompt.md +147 -138
  175. package/src/prompts/system/web-search.md +26 -0
  176. package/src/prompts/tools/ask.md +31 -24
  177. package/src/prompts/tools/bash.md +20 -17
  178. package/src/prompts/tools/calculator.md +9 -5
  179. package/src/prompts/tools/fetch.md +16 -0
  180. package/src/prompts/tools/find.md +15 -5
  181. package/src/prompts/tools/gemini-image.md +21 -6
  182. package/src/prompts/tools/grep.md +28 -12
  183. package/src/prompts/tools/lsp.md +35 -14
  184. package/src/prompts/tools/patch.md +39 -41
  185. package/src/prompts/tools/python.md +59 -76
  186. package/src/prompts/tools/read.md +23 -22
  187. package/src/prompts/tools/replace.md +19 -12
  188. package/src/prompts/tools/ssh.md +21 -28
  189. package/src/prompts/tools/task.md +54 -44
  190. package/src/prompts/tools/todo-write.md +52 -163
  191. package/src/prompts/tools/web-search.md +16 -9
  192. package/src/prompts/tools/write.md +13 -2
  193. package/src/{core/sdk.ts → sdk.ts} +65 -34
  194. package/src/{core → session}/agent-session.ts +45 -37
  195. package/src/{core → session}/agent-storage.ts +2 -2
  196. package/src/session/artifacts.ts +110 -0
  197. package/src/{core → session}/auth-storage.ts +1 -1
  198. package/src/{core → session}/compaction/branch-summarization.ts +5 -5
  199. package/src/{core → session}/compaction/compaction.ts +6 -6
  200. package/src/{core → session}/compaction/utils.ts +3 -3
  201. package/src/{core → session}/history-storage.ts +1 -1
  202. package/src/{core → session}/messages.ts +6 -8
  203. package/src/{core → session}/session-manager.ts +2 -2
  204. package/src/{core → session}/storage-migration.ts +2 -2
  205. package/src/session/streaming-output.ts +177 -0
  206. package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
  207. package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
  208. package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
  209. package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
  210. package/src/{core/tools/task → task}/agents.ts +8 -8
  211. package/src/{core/tools/task → task}/commands.ts +5 -6
  212. package/src/{core/tools/task → task}/discovery.ts +3 -3
  213. package/src/{core/tools/task → task}/executor.ts +34 -44
  214. package/src/{core/tools/task → task}/index.ts +206 -50
  215. package/src/{core/tools/task → task}/render.ts +80 -23
  216. package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
  217. package/src/task/template.ts +47 -0
  218. package/src/{core/tools/task → task}/types.ts +19 -27
  219. package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
  220. package/src/{core/tools/task → task}/worker.ts +34 -29
  221. package/src/task/worktree.ts +166 -0
  222. package/src/{core/tools → tools}/ask.ts +13 -21
  223. package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
  224. package/src/{core/tools → tools}/bash.ts +61 -63
  225. package/src/{core/tools → tools}/calculator.ts +4 -4
  226. package/src/{core/tools → tools}/complete.ts +1 -1
  227. package/src/{core/tools → tools}/context.ts +2 -2
  228. package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
  229. package/src/{core/tools → tools}/find.ts +80 -104
  230. package/src/{core/tools → tools}/gemini-image.ts +420 -29
  231. package/src/{core/tools → tools}/grep.ts +155 -164
  232. package/src/{core/tools → tools}/index.ts +63 -56
  233. package/src/tools/list-limit.ts +40 -0
  234. package/src/{core/tools → tools}/ls.ts +44 -35
  235. package/src/{core/tools → tools}/notebook.ts +3 -3
  236. package/src/tools/output-meta.ts +443 -0
  237. package/src/tools/output-utils.ts +63 -0
  238. package/src/{core/tools → tools}/python.ts +105 -89
  239. package/src/tools/read.ts +882 -0
  240. package/src/{core/tools → tools}/render-utils.ts +1 -1
  241. package/src/{core/tools → tools}/renderers.ts +8 -10
  242. package/src/{core/tools → tools}/review.ts +2 -2
  243. package/src/{core/tools → tools}/ssh.ts +56 -59
  244. package/src/{core/tools → tools}/todo-write.ts +12 -23
  245. package/src/tools/tool-errors.ts +95 -0
  246. package/src/tools/tool-result.ts +92 -0
  247. package/src/{core/tools → tools}/truncate.ts +2 -2
  248. package/src/{core/tools → tools}/write.ts +15 -13
  249. package/src/utils/changelog.ts +1 -1
  250. package/src/{core → utils}/file-mentions.ts +4 -4
  251. package/src/utils/image-convert.ts +1 -1
  252. package/src/utils/image-resize.ts +1 -1
  253. package/src/utils/shell.ts +1 -1
  254. package/src/{core → utils}/title-generator.ts +4 -4
  255. package/src/utils/tools-manager.ts +1 -1
  256. package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
  257. package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
  258. package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
  259. package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
  260. package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
  261. package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
  262. package/src/{core/tools/web-search → web/search}/index.ts +22 -71
  263. package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
  264. package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
  265. package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
  266. package/src/{core/tools/web-search → web/search}/render.ts +3 -3
  267. package/scripts/migrate-sessions.sh +0 -93
  268. package/src/core/index.ts +0 -56
  269. package/src/core/python-prelude.ts +0 -3
  270. package/src/core/ssh-executor.ts +0 -5
  271. package/src/core/streaming-output.ts +0 -115
  272. package/src/core/tools/output.ts +0 -519
  273. package/src/core/tools/read.ts +0 -717
  274. package/src/core/tools/task/template.ts +0 -37
  275. package/src/prompts/tools/output.md +0 -47
  276. package/src/prompts/tools/web-fetch.md +0 -9
  277. /package/src/{core/tools/exa → exa}/types.ts +0 -0
  278. /package/src/{core → exec}/exec.ts +0 -0
  279. /package/src/{core/export-html → export/html}/template.css +0 -0
  280. /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
  281. /package/src/{core/export-html → export/html}/template.html +0 -0
  282. /package/src/{core/export-html → export/html}/template.js +0 -0
  283. /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
  284. /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
  285. /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
  286. /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
  287. /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
  288. /package/src/{core → extensibility}/extensions/index.ts +0 -0
  289. /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
  290. /package/src/{core → extensibility}/plugins/index.ts +0 -0
  291. /package/src/{core → extensibility}/plugins/loader.ts +0 -0
  292. /package/src/{core → extensibility}/plugins/manager.ts +0 -0
  293. /package/src/{core → extensibility}/plugins/parser.ts +0 -0
  294. /package/src/{core → extensibility}/plugins/types.ts +0 -0
  295. /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
  296. /package/src/{core/python-prelude.py → ipy/prelude.py} +0 -0
  297. /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
  298. /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
  299. /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
  300. /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
  301. /package/src/{core/mcp → mcp}/client.ts +0 -0
  302. /package/src/{core/mcp → mcp}/index.ts +0 -0
  303. /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
  304. /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
  305. /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
  306. /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
  307. /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
  308. /package/src/modes/{interactive/components → components}/index.ts +0 -0
  309. /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
  310. /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
  311. /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
  312. /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
  313. /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
  314. /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
  315. /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
  316. /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
  317. /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
  318. /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
  319. /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
  320. /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
  321. /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
  322. /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
  323. /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
  324. /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
  325. /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
  326. /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
  327. /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
  328. /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
  329. /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
  330. /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
  331. /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
  332. /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
  333. /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
  334. /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
  335. /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
  336. /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
  337. /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
  338. /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
  339. /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
  340. /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
  341. /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
  342. /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
  343. /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
  344. /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
  345. /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
  346. /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
  347. /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
  348. /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
  349. /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
  350. /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
  351. /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
  352. /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
  353. /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
  354. /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
  355. /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
  356. /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  357. /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
  358. /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
  359. /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
  360. /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
  361. /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
  362. /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
  363. /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
  364. /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
  365. /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
  366. /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
  367. /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
  368. /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
  369. /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
  370. /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
  371. /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
  372. /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
  373. /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
  374. /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
  375. /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
  376. /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
  377. /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
  378. /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
  379. /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
  380. /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
  381. /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
  382. /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
  383. /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
  384. /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
  385. /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
  386. /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
  387. /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
  388. /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
  389. /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
  390. /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
  391. /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
  392. /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
  393. /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
  394. /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
  395. /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
  396. /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
  397. /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
  398. /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
  399. /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
  400. /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
  401. /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
  402. /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
  403. /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
  404. /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
  405. /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
  406. /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
  407. /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
  408. /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
  409. /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
  410. /package/src/modes/{interactive/theme → theme}/light.json +0 -0
  411. /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
  412. /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
  413. /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
  414. /package/src/{core/tools/patch → patch}/normative.ts +0 -0
  415. /package/src/{core/tools/patch → patch}/parser.ts +0 -0
  416. /package/src/{core/tools/patch → patch}/types.ts +0 -0
  417. /package/src/{core → session}/compaction/index.ts +0 -0
  418. /package/src/{core → session}/session-storage.ts +0 -0
  419. /package/src/{core/tools/task → task}/name-generator.ts +0 -0
  420. /package/src/{core/tools/task → task}/omp-command.ts +0 -0
  421. /package/src/{core/tools/task → task}/parallel.ts +0 -0
  422. /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
  423. /package/src/{core/tools → tools}/path-utils.ts +0 -0
  424. /package/src/{core → utils}/event-bus.ts +0 -0
  425. /package/src/{core → utils}/frontmatter.ts +0 -0
  426. /package/src/{core → utils}/terminal-notify.ts +0 -0
  427. /package/src/{core → utils}/timings.ts +0 -0
  428. /package/src/{core → utils}/utils.ts +0 -0
  429. /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
  430. /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
  431. /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
  432. /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
  433. /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
  434. /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
  435. /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
  436. /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
  437. /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
  438. /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
  439. /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
  440. /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
  441. /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
  442. /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
  443. /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
  444. /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
  445. /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
  446. /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
  447. /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
  448. /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
  449. /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
  450. /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
  451. /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
  452. /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
  453. /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
  454. /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
  455. /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
  456. /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
  457. /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
  458. /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
  459. /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
  460. /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
  461. /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
  462. /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
  463. /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
  464. /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
  465. /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
  466. /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
  467. /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
  468. /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
  469. /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
  470. /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
  471. /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
  472. /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
  473. /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
  474. /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
  475. /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
  476. /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
  477. /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
  478. /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
  479. /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
  480. /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
  481. /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
  482. /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
  483. /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
  484. /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
  485. /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
  486. /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
  487. /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
  488. /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
  489. /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
  490. /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
  491. /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
  492. /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
  493. /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
  494. /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
  495. /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
  496. /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
  497. /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
  498. /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
  499. /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
  500. /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
  501. /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
@@ -3,7 +3,6 @@ import {
3
3
  existsSync,
4
4
  mkdirSync,
5
5
  openSync,
6
- readdirSync,
7
6
  readFileSync,
8
7
  renameSync,
9
8
  statSync,
@@ -13,18 +12,16 @@ import {
13
12
  } from "node:fs";
14
13
  import { createServer } from "node:net";
15
14
  import { delimiter, join } from "node:path";
16
- import { logger, postmortem } from "@oh-my-pi/pi-utils";
15
+ import { logger } from "@oh-my-pi/pi-utils";
17
16
  import type { Subprocess } from "bun";
18
- import { getAgentDir } from "../config";
19
- import { getShellConfig, killProcessTree } from "../utils/shell";
20
- import { getOrCreateSnapshot } from "../utils/shell-snapshot";
17
+ import { getAgentDir } from "$c/config";
18
+ import { getShellConfig, killProcessTree } from "$c/utils/shell";
19
+ import { getOrCreateSnapshot } from "$c/utils/shell-snapshot";
21
20
 
22
21
  const GATEWAY_DIR_NAME = "python-gateway";
23
22
  const GATEWAY_INFO_FILE = "gateway.json";
24
23
  const GATEWAY_LOCK_FILE = "gateway.lock";
25
- const GATEWAY_CLIENT_PREFIX = "client-";
26
24
  const GATEWAY_STARTUP_TIMEOUT_MS = 30000;
27
- const GATEWAY_IDLE_TIMEOUT_MS = 30000;
28
25
  const GATEWAY_LOCK_TIMEOUT_MS = GATEWAY_STARTUP_TIMEOUT_MS + 5000;
29
26
  const GATEWAY_LOCK_RETRY_MS = 50;
30
27
  const GATEWAY_LOCK_STALE_MS = GATEWAY_STARTUP_TIMEOUT_MS * 2;
@@ -140,8 +137,6 @@ export interface GatewayInfo {
140
137
  url: string;
141
138
  pid: number;
142
139
  startedAt: number;
143
- refCount: number;
144
- cwd: string;
145
140
  pythonPath?: string;
146
141
  venvPath?: string | null;
147
142
  }
@@ -158,61 +153,7 @@ interface AcquireResult {
158
153
 
159
154
  let localGatewayProcess: Subprocess | null = null;
160
155
  let localGatewayUrl: string | null = null;
161
- let idleShutdownTimer: ReturnType<typeof setTimeout> | null = null;
162
156
  let isCoordinatorInitialized = false;
163
- let localClientFile: string | null = null;
164
- let postmortemRegistered = false;
165
-
166
- /**
167
- * Register cleanup handler for process exit. Called lazily on first gateway acquisition.
168
- * Ensures the gateway process we spawned is killed when omp exits, preventing orphaned processes.
169
- */
170
- function ensurePostmortemCleanup(): void {
171
- if (postmortemRegistered) return;
172
- postmortemRegistered = true;
173
-
174
- postmortem.register("shared-gateway", async () => {
175
- cancelIdleShutdown();
176
-
177
- // Clean up our client file first so refcount is accurate
178
- if (localClientFile) {
179
- try {
180
- unlinkSync(localClientFile);
181
- } catch {
182
- // Ignore cleanup errors
183
- }
184
- localClientFile = null;
185
- }
186
-
187
- // If we spawned the gateway, kill it only if no other clients remain
188
- if (localGatewayProcess) {
189
- const clients = pruneStaleClientInfos(listClientInfos());
190
- const remainingRefs = clients.reduce((sum, c) => sum + c.info.refCount, 0);
191
-
192
- if (remainingRefs === 0) {
193
- logger.debug("Cleaning up shared gateway on process exit", { pid: localGatewayProcess.pid });
194
- try {
195
- await killProcessTree(localGatewayProcess.pid);
196
- } catch (err) {
197
- logger.warn("Failed to kill shared gateway on exit", {
198
- error: err instanceof Error ? err.message : String(err),
199
- });
200
- }
201
- clearGatewayInfo();
202
- } else {
203
- logger.debug("Leaving shared gateway running for other clients", {
204
- pid: localGatewayProcess.pid,
205
- remainingRefs,
206
- });
207
- }
208
-
209
- localGatewayProcess = null;
210
- localGatewayUrl = null;
211
- }
212
-
213
- isCoordinatorInitialized = false;
214
- });
215
- }
216
157
 
217
158
  function filterEnv(env: Record<string, string | undefined>): Record<string, string | undefined> {
218
159
  const filtered: Record<string, string | undefined> = {};
@@ -398,6 +339,7 @@ async function withGatewayLock<T>(handler: () => Promise<T>): Promise<T> {
398
339
  function readGatewayInfo(): GatewayInfo | null {
399
340
  const infoPath = getGatewayInfoPath();
400
341
  if (!existsSync(infoPath)) return null;
342
+
401
343
  try {
402
344
  const content = readFileSync(infoPath, "utf-8");
403
345
  const parsed = JSON.parse(content) as Partial<GatewayInfo>;
@@ -405,16 +347,10 @@ function readGatewayInfo(): GatewayInfo | null {
405
347
  if (typeof parsed.url !== "string" || typeof parsed.pid !== "number" || typeof parsed.startedAt !== "number") {
406
348
  return null;
407
349
  }
408
- if (typeof parsed.cwd !== "string") return null;
409
- const clients = pruneStaleClientInfos(listClientInfos());
410
- const totalRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
411
- const recoveredRefCount = clients.length > 0 ? totalRefCount : 0;
412
350
  return {
413
351
  url: parsed.url,
414
352
  pid: parsed.pid,
415
353
  startedAt: parsed.startedAt,
416
- refCount: recoveredRefCount,
417
- cwd: parsed.cwd,
418
354
  pythonPath: typeof parsed.pythonPath === "string" ? parsed.pythonPath : undefined,
419
355
  venvPath: typeof parsed.venvPath === "string" || parsed.venvPath === null ? parsed.venvPath : undefined,
420
356
  };
@@ -450,103 +386,6 @@ function isPidRunning(pid: number): boolean {
450
386
  }
451
387
  }
452
388
 
453
- interface GatewayClientInfo {
454
- pid: number;
455
- refCount: number;
456
- updatedAt?: number;
457
- }
458
-
459
- function getClientFilePath(pid: number): string {
460
- return join(getGatewayDir(), `${GATEWAY_CLIENT_PREFIX}${pid}.json`);
461
- }
462
-
463
- function readClientInfo(path: string): GatewayClientInfo | null {
464
- try {
465
- const raw = readFileSync(path, "utf-8");
466
- const parsed = JSON.parse(raw) as GatewayClientInfo;
467
- if (typeof parsed.pid !== "number" || typeof parsed.refCount !== "number") return null;
468
- return parsed;
469
- } catch {
470
- return null;
471
- }
472
- }
473
-
474
- function listClientInfos(): Array<{ path: string; info: GatewayClientInfo }> {
475
- const dir = getGatewayDir();
476
- if (!existsSync(dir)) return [];
477
- const entries = readdirSync(dir);
478
- const results: Array<{ path: string; info: GatewayClientInfo }> = [];
479
- for (const entry of entries) {
480
- if (!entry.startsWith(GATEWAY_CLIENT_PREFIX)) continue;
481
- const path = join(dir, entry);
482
- const info = readClientInfo(path);
483
- if (!info) continue;
484
- results.push({ path, info });
485
- }
486
- return results;
487
- }
488
-
489
- function pruneStaleClientInfos(
490
- clients: Array<{ path: string; info: GatewayClientInfo }>,
491
- ): Array<{ path: string; info: GatewayClientInfo }> {
492
- const active: Array<{ path: string; info: GatewayClientInfo }> = [];
493
- for (const client of clients) {
494
- if (!isPidRunning(client.info.pid)) {
495
- try {
496
- unlinkSync(client.path);
497
- } catch {
498
- // Ignore cleanup errors
499
- }
500
- continue;
501
- }
502
- active.push(client);
503
- }
504
- return active;
505
- }
506
-
507
- function updateLocalClientRefCount(delta: number): { totalRefCount: number; localRefCount: number } {
508
- ensureGatewayDir();
509
- const clients = pruneStaleClientInfos(listClientInfos());
510
- const localPath = localClientFile ?? getClientFilePath(process.pid);
511
- const localEntry = clients.find((client) => client.info.pid === process.pid);
512
- const baseCount = localEntry?.info.refCount ?? 0;
513
- const nextCount = Math.max(0, baseCount + delta);
514
- const otherClients = clients.filter((client) => client.info.pid !== process.pid);
515
-
516
- if (nextCount <= 0) {
517
- if (localEntry) {
518
- try {
519
- unlinkSync(localEntry.path);
520
- } catch {
521
- // Ignore cleanup errors
522
- }
523
- }
524
- if (localClientFile === localPath) {
525
- localClientFile = null;
526
- }
527
- } else {
528
- const payload: GatewayClientInfo = { pid: process.pid, refCount: nextCount, updatedAt: Date.now() };
529
- writeFileSync(localPath, JSON.stringify(payload, null, 2));
530
- localClientFile = localPath;
531
- }
532
-
533
- const totalRefCount =
534
- otherClients.reduce((sum, client) => sum + client.info.refCount, 0) + (nextCount > 0 ? nextCount : 0);
535
- return { totalRefCount, localRefCount: nextCount };
536
- }
537
-
538
- function clearClientFiles(): void {
539
- const clients = listClientInfos();
540
- for (const client of clients) {
541
- try {
542
- unlinkSync(client.path);
543
- } catch {
544
- // Ignore cleanup errors
545
- }
546
- }
547
- localClientFile = null;
548
- }
549
-
550
389
  async function isGatewayHealthy(url: string): Promise<boolean> {
551
390
  try {
552
391
  const controller = new AbortController();
@@ -585,11 +424,6 @@ async function startGatewayProcess(
585
424
  OMP_SHELL_SNAPSHOT: snapshotPath ?? undefined,
586
425
  };
587
426
 
588
- const pythonPathParts = [cwd, kernelEnv.PYTHONPATH].filter(Boolean).join(delimiter);
589
- if (pythonPathParts) {
590
- kernelEnv.PYTHONPATH = pythonPathParts;
591
- }
592
-
593
427
  const gatewayPort = await allocatePort();
594
428
  const gatewayUrl = `http://127.0.0.1:${gatewayPort}`;
595
429
 
@@ -622,7 +456,6 @@ async function startGatewayProcess(
622
456
  exited = true;
623
457
  });
624
458
 
625
- // Wait for gateway to become healthy
626
459
  const startTime = Date.now();
627
460
  while (Date.now() - startTime < GATEWAY_STARTUP_TIMEOUT_MS) {
628
461
  if (exited) {
@@ -645,70 +478,15 @@ async function startGatewayProcess(
645
478
  throw new Error("Gateway startup timeout");
646
479
  }
647
480
 
648
- function scheduleIdleShutdown(): void {
649
- if (idleShutdownTimer) {
650
- clearTimeout(idleShutdownTimer);
651
- }
652
- idleShutdownTimer = setTimeout(async () => {
653
- try {
654
- await withGatewayLock(async () => {
655
- const info = readGatewayInfo();
656
- if (!info) {
657
- clearClientFiles();
658
- return;
659
- }
660
- const clients = pruneStaleClientInfos(listClientInfos());
661
- const totalRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
662
- if (totalRefCount > 0) {
663
- if (info.refCount !== totalRefCount) {
664
- writeGatewayInfo({ ...info, refCount: totalRefCount });
665
- }
666
- return;
667
- }
668
- logger.debug("Shutting down idle shared gateway", { pid: info.pid });
669
- if (localGatewayProcess) {
670
- await shutdownLocalGateway();
671
- } else if (isPidRunning(info.pid)) {
672
- try {
673
- await killProcessTree(info.pid);
674
- } catch (err) {
675
- logger.warn("Failed to kill idle shared gateway", {
676
- error: err instanceof Error ? err.message : String(err),
677
- pid: info.pid,
678
- });
679
- }
680
- }
681
- clearGatewayInfo();
682
- clearClientFiles();
683
- });
684
- } catch (err) {
685
- logger.warn("Failed to shutdown idle shared gateway", {
686
- error: err instanceof Error ? err.message : String(err),
687
- });
688
- } finally {
689
- idleShutdownTimer = null;
690
- }
691
- }, GATEWAY_IDLE_TIMEOUT_MS);
692
- }
693
-
694
- function cancelIdleShutdown(): void {
695
- if (idleShutdownTimer) {
696
- clearTimeout(idleShutdownTimer);
697
- idleShutdownTimer = null;
698
- }
699
- }
700
-
701
- async function shutdownLocalGateway(): Promise<void> {
702
- if (localGatewayProcess) {
703
- try {
704
- await killProcessTree(localGatewayProcess.pid);
705
- } catch (err) {
706
- logger.warn("Failed to kill shared gateway process", {
707
- error: err instanceof Error ? err.message : String(err),
708
- });
709
- }
710
- localGatewayProcess = null;
711
- localGatewayUrl = null;
481
+ async function killGateway(pid: number, context: string): Promise<void> {
482
+ try {
483
+ await killProcessTree(pid);
484
+ } catch (err) {
485
+ logger.warn("Failed to kill shared gateway process", {
486
+ error: err instanceof Error ? err.message : String(err),
487
+ pid,
488
+ context,
489
+ });
712
490
  }
713
491
  }
714
492
 
@@ -717,72 +495,35 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
717
495
  return null;
718
496
  }
719
497
 
720
- ensurePostmortemCleanup();
721
-
722
498
  try {
723
499
  return await withGatewayLock(async () => {
724
500
  const existingInfo = readGatewayInfo();
725
- if (existingInfo && (await isGatewayAlive(existingInfo))) {
726
- const { env } = await getShellConfig();
727
- const filteredEnv = filterEnv(env);
728
- const runtime = await resolvePythonRuntime(cwd, filteredEnv);
729
- const existingVenv = existingInfo.venvPath ?? null;
730
- const runtimeVenv = runtime.venvPath ?? null;
731
- if (
732
- existingInfo.cwd !== cwd ||
733
- !existingInfo.pythonPath ||
734
- existingInfo.pythonPath !== runtime.pythonPath ||
735
- existingVenv !== runtimeVenv
736
- ) {
737
- logger.debug("Shared gateway metadata mismatch", {
738
- existingCwd: existingInfo.cwd,
739
- requestedCwd: cwd,
740
- existingPython: existingInfo.pythonPath,
741
- runtimePython: runtime.pythonPath,
742
- existingVenv,
743
- runtimeVenv,
744
- });
745
- return null;
501
+ if (existingInfo) {
502
+ if (await isGatewayAlive(existingInfo)) {
503
+ localGatewayUrl = existingInfo.url;
504
+ isCoordinatorInitialized = true;
505
+ logger.debug("Reusing global Python gateway", { url: existingInfo.url });
506
+ return { url: existingInfo.url, isShared: true };
746
507
  }
747
- const { totalRefCount } = updateLocalClientRefCount(1);
748
- const updatedInfo = { ...existingInfo, refCount: totalRefCount };
749
- writeGatewayInfo(updatedInfo);
750
- cancelIdleShutdown();
751
- logger.debug("Reusing shared gateway", { url: existingInfo.url, refCount: updatedInfo.refCount });
752
- isCoordinatorInitialized = true;
753
- return { url: existingInfo.url, isShared: true };
754
- }
755
508
 
756
- if (existingInfo) {
757
509
  logger.debug("Cleaning up stale gateway info", { pid: existingInfo.pid });
758
510
  if (isPidRunning(existingInfo.pid)) {
759
- try {
760
- await killProcessTree(existingInfo.pid);
761
- } catch (err) {
762
- logger.warn("Failed to kill stale shared gateway process", {
763
- error: err instanceof Error ? err.message : String(err),
764
- pid: existingInfo.pid,
765
- });
766
- }
511
+ await killGateway(existingInfo.pid, "stale");
767
512
  }
768
513
  clearGatewayInfo();
769
- clearClientFiles();
770
514
  }
771
515
 
772
516
  const { url, pid, pythonPath, venvPath } = await startGatewayProcess(cwd);
773
- const { totalRefCount } = updateLocalClientRefCount(1);
774
517
  const info: GatewayInfo = {
775
518
  url,
776
519
  pid,
777
520
  startedAt: Date.now(),
778
- refCount: totalRefCount,
779
- cwd,
780
521
  pythonPath,
781
522
  venvPath,
782
523
  };
783
524
  writeGatewayInfo(info);
784
525
  isCoordinatorInitialized = true;
785
- logger.debug("Started shared gateway", { url, pid });
526
+ logger.debug("Started global Python gateway", { url, pid });
786
527
  return { url, isShared: true };
787
528
  });
788
529
  } catch (err) {
@@ -795,48 +536,24 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
795
536
 
796
537
  export async function releaseSharedGateway(): Promise<void> {
797
538
  if (!isCoordinatorInitialized) return;
798
-
799
- try {
800
- await withGatewayLock(async () => {
801
- const { totalRefCount } = updateLocalClientRefCount(-1);
802
- const info = readGatewayInfo();
803
- if (!info) return;
804
-
805
- const newRefCount = Math.max(0, totalRefCount);
806
- if (newRefCount === 0) {
807
- const updatedInfo = { ...info, refCount: 0 };
808
- writeGatewayInfo(updatedInfo);
809
- scheduleIdleShutdown();
810
- logger.debug("Scheduled idle shutdown for shared gateway", { pid: info.pid });
811
- return;
812
- }
813
- const updatedInfo = { ...info, refCount: newRefCount };
814
- writeGatewayInfo(updatedInfo);
815
- logger.debug("Released shared gateway reference", { url: info.url, refCount: newRefCount });
816
- });
817
- } catch (err) {
818
- logger.warn("Failed to release shared gateway", {
819
- error: err instanceof Error ? err.message : String(err),
820
- });
821
- }
822
539
  }
823
540
 
824
541
  export function getSharedGatewayUrl(): string | null {
825
- return localGatewayUrl;
542
+ if (localGatewayUrl) return localGatewayUrl;
543
+ return readGatewayInfo()?.url ?? null;
826
544
  }
827
545
 
828
546
  export function isSharedGatewayActive(): boolean {
829
- return localGatewayProcess !== null && localGatewayUrl !== null;
547
+ return getGatewayStatus().active;
830
548
  }
831
549
 
832
550
  export interface GatewayStatus {
833
551
  active: boolean;
834
- shared: boolean;
835
552
  url: string | null;
836
553
  pid: number | null;
837
- refCount: number;
838
- cwd: string | null;
839
554
  uptime: number | null;
555
+ pythonPath: string | null;
556
+ venvPath: string | null;
840
557
  }
841
558
 
842
559
  export function getGatewayStatus(): GatewayStatus {
@@ -844,45 +561,44 @@ export function getGatewayStatus(): GatewayStatus {
844
561
  if (!info) {
845
562
  return {
846
563
  active: false,
847
- shared: false,
848
564
  url: null,
849
565
  pid: null,
850
- refCount: 0,
851
- cwd: null,
852
566
  uptime: null,
567
+ pythonPath: null,
568
+ venvPath: null,
853
569
  };
854
570
  }
855
571
  const active = isPidRunning(info.pid);
856
- const clients = pruneStaleClientInfos(listClientInfos());
857
- const clientRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
858
- const refCount = clientRefCount > 0 ? clientRefCount : info.refCount;
859
572
  return {
860
573
  active,
861
- shared: active && refCount > 1,
862
574
  url: info.url,
863
575
  pid: info.pid,
864
- refCount,
865
- cwd: info.cwd,
866
- uptime: Date.now() - info.startedAt,
576
+ uptime: active ? Date.now() - info.startedAt : null,
577
+ pythonPath: info.pythonPath ?? null,
578
+ venvPath: info.venvPath ?? null,
867
579
  };
868
580
  }
869
581
 
870
582
  export async function shutdownSharedGateway(): Promise<void> {
871
- cancelIdleShutdown();
872
583
  try {
873
584
  await withGatewayLock(async () => {
874
585
  const info = readGatewayInfo();
875
- if (info) {
876
- clearGatewayInfo();
586
+ if (!info) return;
587
+ if (isPidRunning(info.pid)) {
588
+ await killGateway(info.pid, "shutdown");
877
589
  }
878
- clearClientFiles();
590
+ clearGatewayInfo();
879
591
  });
880
592
  } catch (err) {
881
593
  logger.warn("Failed to shutdown shared gateway", {
882
594
  error: err instanceof Error ? err.message : String(err),
883
595
  });
884
596
  } finally {
885
- await shutdownLocalGateway();
597
+ if (localGatewayProcess) {
598
+ await killGateway(localGatewayProcess.pid, "shutdown-local");
599
+ }
600
+ localGatewayProcess = null;
601
+ localGatewayUrl = null;
886
602
  isCoordinatorInitialized = false;
887
603
  }
888
604
  }
@@ -3,12 +3,12 @@ import { delimiter, join } from "node:path";
3
3
  import { logger } from "@oh-my-pi/pi-utils";
4
4
  import { $, type Subprocess } from "bun";
5
5
  import { nanoid } from "nanoid";
6
- import { getShellConfig, killProcessTree } from "../utils/shell";
7
- import { getOrCreateSnapshot } from "../utils/shell-snapshot";
8
- import { acquireSharedGateway, releaseSharedGateway } from "./python-gateway-coordinator";
9
- import { loadPythonModules } from "./python-modules";
10
- import { PYTHON_PRELUDE } from "./python-prelude";
11
- import { htmlToBasicMarkdown } from "./tools/web-scrapers/types";
6
+ import { getShellConfig, killProcessTree } from "$c/utils/shell";
7
+ import { getOrCreateSnapshot } from "$c/utils/shell-snapshot";
8
+ import { htmlToBasicMarkdown } from "$c/web/scrapers/types";
9
+ import { acquireSharedGateway, releaseSharedGateway } from "./gateway-coordinator";
10
+ import { loadPythonModules } from "./modules";
11
+ import { PYTHON_PRELUDE } from "./prelude";
12
12
 
13
13
  const TEXT_ENCODER = new TextEncoder();
14
14
  const TEXT_DECODER = new TextDecoder();
@@ -511,7 +511,7 @@ export class PythonKernel {
511
511
 
512
512
  const externalConfig = getExternalGatewayConfig();
513
513
  if (externalConfig) {
514
- return PythonKernel.startWithExternalGateway(externalConfig, options.cwd);
514
+ return PythonKernel.startWithExternalGateway(externalConfig, options.cwd, options.env);
515
515
  }
516
516
 
517
517
  // Try shared gateway first (unless explicitly disabled)
@@ -519,7 +519,7 @@ export class PythonKernel {
519
519
  try {
520
520
  const sharedResult = await acquireSharedGateway(options.cwd);
521
521
  if (sharedResult) {
522
- return PythonKernel.startWithSharedGateway(sharedResult.url, options.cwd);
522
+ return PythonKernel.startWithSharedGateway(sharedResult.url, options.cwd, options.env);
523
523
  }
524
524
  } catch (err) {
525
525
  logger.warn("Failed to acquire shared gateway, falling back to local", {
@@ -531,7 +531,11 @@ export class PythonKernel {
531
531
  return PythonKernel.startWithLocalGateway(options);
532
532
  }
533
533
 
534
- private static async startWithExternalGateway(config: ExternalGatewayConfig, cwd: string): Promise<PythonKernel> {
534
+ private static async startWithExternalGateway(
535
+ config: ExternalGatewayConfig,
536
+ cwd: string,
537
+ env?: Record<string, string | undefined>,
538
+ ): Promise<PythonKernel> {
535
539
  const headers: Record<string, string> = { "Content-Type": "application/json" };
536
540
  if (config.token) {
537
541
  headers.Authorization = `token ${config.token}`;
@@ -554,6 +558,7 @@ export class PythonKernel {
554
558
 
555
559
  try {
556
560
  await kernel.connectWebSocket();
561
+ await kernel.initializeKernelEnvironment(cwd, env);
557
562
  kernel.startHeartbeat();
558
563
  const preludeResult = await kernel.execute(PYTHON_PRELUDE, { silent: true, storeHistory: false });
559
564
  if (preludeResult.cancelled || preludeResult.status === "error") {
@@ -567,7 +572,11 @@ export class PythonKernel {
567
572
  }
568
573
  }
569
574
 
570
- private static async startWithSharedGateway(gatewayUrl: string, cwd: string): Promise<PythonKernel> {
575
+ private static async startWithSharedGateway(
576
+ gatewayUrl: string,
577
+ cwd: string,
578
+ env?: Record<string, string | undefined>,
579
+ ): Promise<PythonKernel> {
571
580
  const createResponse = await fetch(`${gatewayUrl}/api/kernels`, {
572
581
  method: "POST",
573
582
  headers: { "Content-Type": "application/json" },
@@ -586,6 +595,7 @@ export class PythonKernel {
586
595
 
587
596
  try {
588
597
  await kernel.connectWebSocket();
598
+ await kernel.initializeKernelEnvironment(cwd, env);
589
599
  kernel.startHeartbeat();
590
600
  const preludeResult = await kernel.execute(PYTHON_PRELUDE, { silent: true, storeHistory: false });
591
601
  if (preludeResult.cancelled || preludeResult.status === "error") {
@@ -702,6 +712,7 @@ export class PythonKernel {
702
712
 
703
713
  try {
704
714
  await kernel.connectWebSocket();
715
+ await kernel.initializeKernelEnvironment(options.cwd, options.env);
705
716
  kernel.startHeartbeat();
706
717
  const preludeResult = await kernel.execute(PYTHON_PRELUDE, { silent: true, storeHistory: false });
707
718
  if (preludeResult.cancelled || preludeResult.status === "error") {
@@ -802,6 +813,23 @@ export class PythonKernel {
802
813
  return promise;
803
814
  }
804
815
 
816
+ private async initializeKernelEnvironment(cwd: string, env?: Record<string, string | undefined>): Promise<void> {
817
+ const envEntries = Object.entries(env ?? {}).filter(([, value]) => value !== undefined);
818
+ const envPayload = Object.fromEntries(envEntries);
819
+ const initScript = [
820
+ "import os, sys",
821
+ `__omp_cwd = ${JSON.stringify(cwd)}`,
822
+ "os.chdir(__omp_cwd)",
823
+ `__omp_env = ${JSON.stringify(envPayload)}`,
824
+ "for __omp_key, __omp_val in __omp_env.items():\n os.environ[__omp_key] = __omp_val",
825
+ "if __omp_cwd not in sys.path:\n sys.path.insert(0, __omp_cwd)",
826
+ ].join("\n");
827
+ const result = await this.execute(initScript, { silent: true, storeHistory: false });
828
+ if (result.cancelled || result.status === "error") {
829
+ throw new Error("Failed to initialize Python kernel environment");
830
+ }
831
+ }
832
+
805
833
  private abortPendingExecutions(reason: string): void {
806
834
  if (this.#pendingExecutions.size === 0) return;
807
835
  for (const cancel of this.#pendingExecutions.values()) {
@@ -0,0 +1,3 @@
1
+ import pythonPrelude from "./prelude.py" with { type: "text" };
2
+
3
+ export const PYTHON_PRELUDE = pythonPrelude;
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import { logger } from "@oh-my-pi/pi-utils";
3
+ import { ToolAbortError, throwIfAborted } from "$c/tools/tool-errors";
3
4
  import { applyWorkspaceEdit } from "./edits";
4
5
  import { getLspmuxCommand, isLspmuxSupported } from "./lspmux";
5
6
  import type {
@@ -547,7 +548,7 @@ export async function syncContent(
547
548
  ): Promise<void> {
548
549
  const uri = fileToUri(filePath);
549
550
  const lockKey = `${client.name}:${uri}`;
550
- signal?.throwIfAborted();
551
+ throwIfAborted(signal);
551
552
 
552
553
  const existingLock = fileOperationLocks.get(lockKey);
553
554
  if (existingLock) {
@@ -563,7 +564,7 @@ export async function syncContent(
563
564
  if (!info) {
564
565
  // Open file with provided content instead of reading from disk
565
566
  const languageId = detectLanguageId(filePath);
566
- signal?.throwIfAborted();
567
+ throwIfAborted(signal);
567
568
  await sendNotification(client, "textDocument/didOpen", {
568
569
  textDocument: {
569
570
  uri,
@@ -578,7 +579,7 @@ export async function syncContent(
578
579
  }
579
580
 
580
581
  const version = ++info.version;
581
- signal?.throwIfAborted();
582
+ throwIfAborted(signal);
582
583
  await sendNotification(client, "textDocument/didChange", {
583
584
  textDocument: { uri, version },
584
585
  contentChanges: [{ text: content }],
@@ -603,7 +604,7 @@ export async function notifySaved(client: LspClient, filePath: string, signal?:
603
604
  const info = client.openFiles.get(uri);
604
605
  if (!info) return; // File not open, nothing to notify
605
606
 
606
- signal?.throwIfAborted();
607
+ throwIfAborted(signal);
607
608
  await sendNotification(client, "textDocument/didSave", {
608
609
  textDocument: { uri },
609
610
  });
@@ -698,7 +699,7 @@ export async function sendRequest(
698
699
  // Atomically increment and capture request ID
699
700
  const id = ++client.requestId;
700
701
  if (signal?.aborted) {
701
- const reason = signal.reason instanceof Error ? signal.reason : new Error("Operation aborted");
702
+ const reason = signal.reason instanceof Error ? signal.reason : new ToolAbortError();
702
703
  return Promise.reject(reason);
703
704
  }
704
705
 
@@ -724,7 +725,7 @@ export async function sendRequest(
724
725
  }
725
726
  if (timeout) clearTimeout(timeout);
726
727
  cleanup();
727
- const reason = signal?.reason instanceof Error ? signal.reason : new Error("Operation aborted");
728
+ const reason = signal?.reason instanceof Error ? signal.reason : new ToolAbortError();
728
729
  reject(reason);
729
730
  };
730
731