@oh-my-pi/pi-coding-agent 6.9.69 → 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 (502) hide show
  1. package/CHANGELOG.md +219 -51
  2. package/README.md +1 -1
  3. package/docs/hooks.md +2 -2
  4. package/docs/sdk.md +1 -1
  5. package/examples/sdk/04-skills.ts +1 -1
  6. package/package.json +10 -10
  7. package/scripts/format-prompts.ts +143 -0
  8. package/scripts/generate-template.ts +1 -1
  9. package/src/cli/args.ts +3 -3
  10. package/src/cli/config-cli.ts +4 -4
  11. package/src/cli/file-processor.ts +3 -3
  12. package/src/cli/list-models.ts +2 -2
  13. package/src/cli/plugin-cli.ts +3 -3
  14. package/src/cli/session-picker.ts +2 -2
  15. package/src/cli/setup-cli.ts +2 -2
  16. package/src/cli/stats-cli.ts +1 -1
  17. package/src/cli/update-cli.ts +2 -2
  18. package/src/{core → config}/keybindings.ts +1 -1
  19. package/src/{core → config}/model-registry.ts +8 -1
  20. package/src/{core → config}/model-resolver.ts +3 -3
  21. package/src/{core → config}/prompt-templates.ts +2 -2
  22. package/src/{core → config}/settings-manager.ts +6 -6
  23. package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
  24. package/src/discovery/agents-md.ts +4 -4
  25. package/src/discovery/builtin.ts +17 -17
  26. package/src/discovery/claude.ts +12 -12
  27. package/src/discovery/cline.ts +6 -6
  28. package/src/discovery/codex.ts +21 -21
  29. package/src/discovery/cursor.ts +9 -9
  30. package/src/discovery/gemini.ts +9 -9
  31. package/src/discovery/github.ts +6 -6
  32. package/src/discovery/helpers.ts +4 -4
  33. package/src/discovery/index.ts +16 -16
  34. package/src/discovery/mcp-json.ts +4 -4
  35. package/src/discovery/ssh.ts +4 -4
  36. package/src/discovery/vscode.ts +4 -4
  37. package/src/discovery/windsurf.ts +6 -6
  38. package/src/{core/tools/exa → exa}/company.ts +2 -3
  39. package/src/{core/tools/exa → exa}/index.ts +2 -2
  40. package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
  41. package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
  42. package/src/{core/tools/exa → exa}/render.ts +3 -3
  43. package/src/{core/tools/exa → exa}/researcher.ts +1 -1
  44. package/src/{core/tools/exa → exa}/search.ts +2 -10
  45. package/src/{core/tools/exa → exa}/websets.ts +1 -1
  46. package/src/{core → exec}/bash-executor.ts +23 -7
  47. package/src/{core → export}/custom-share.ts +1 -1
  48. package/src/{core/export-html → export/html}/index.ts +3 -3
  49. package/src/{core → export}/ttsr.ts +2 -2
  50. package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
  51. package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
  52. package/src/{core → extensibility}/custom-commands/types.ts +1 -1
  53. package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
  54. package/src/{core → extensibility}/custom-tools/types.ts +6 -6
  55. package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
  56. package/src/{core → extensibility}/extensions/loader.ts +8 -8
  57. package/src/{core → extensibility}/extensions/runner.ts +3 -3
  58. package/src/{core → extensibility}/extensions/types.ts +15 -15
  59. package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
  60. package/src/{core → extensibility}/hooks/index.ts +1 -1
  61. package/src/{core → extensibility}/hooks/loader.ts +7 -7
  62. package/src/{core → extensibility}/hooks/runner.ts +4 -4
  63. package/src/{core → extensibility}/hooks/types.ts +9 -9
  64. package/src/{core → extensibility}/plugins/doctor.ts +1 -1
  65. package/src/{core → extensibility}/plugins/installer.ts +2 -3
  66. package/src/{core → extensibility}/plugins/paths.ts +1 -1
  67. package/src/{core → extensibility}/skills.ts +8 -48
  68. package/src/{core → extensibility}/slash-commands.ts +6 -6
  69. package/src/index.ts +127 -128
  70. package/src/internal-urls/agent-protocol.ts +126 -0
  71. package/src/internal-urls/artifact-protocol.ts +93 -0
  72. package/src/internal-urls/index.ts +28 -0
  73. package/src/internal-urls/json-query.ts +126 -0
  74. package/src/internal-urls/router.ts +69 -0
  75. package/src/internal-urls/rule-protocol.ts +56 -0
  76. package/src/internal-urls/skill-protocol.ts +112 -0
  77. package/src/internal-urls/types.ts +48 -0
  78. package/src/{core/python-executor.ts → ipy/executor.ts} +74 -13
  79. package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +40 -270
  80. package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
  81. package/src/{core/python-prelude.py → ipy/prelude.py} +201 -8
  82. package/src/ipy/prelude.ts +3 -0
  83. package/src/{core/tools/lsp → lsp}/client.ts +7 -6
  84. package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
  85. package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
  86. package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
  87. package/src/{core/tools/lsp → lsp}/config.ts +1 -1
  88. package/src/{core/tools/lsp → lsp}/index.ts +29 -17
  89. package/src/{core/tools/lsp → lsp}/render.ts +2 -2
  90. package/src/{core/tools/lsp → lsp}/types.ts +14 -16
  91. package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
  92. package/src/main.ts +12 -12
  93. package/src/{core/mcp → mcp}/config.ts +8 -8
  94. package/src/{core/mcp → mcp}/loader.ts +5 -6
  95. package/src/{core/mcp → mcp}/manager.ts +2 -2
  96. package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
  97. package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
  98. package/src/{core/mcp → mcp}/transports/http.ts +7 -1
  99. package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
  100. package/src/{core/mcp → mcp}/types.ts +1 -1
  101. package/src/migrations.ts +2 -2
  102. package/src/modes/{interactive/components → components}/armin.ts +1 -1
  103. package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
  104. package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
  105. package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
  106. package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
  107. package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
  108. package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
  109. package/src/modes/{interactive/components → components}/diff.ts +1 -1
  110. package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
  111. package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
  112. package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
  113. package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
  114. package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
  115. package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
  116. package/src/modes/{interactive/components → components}/footer.ts +3 -3
  117. package/src/modes/{interactive/components → components}/history-search.ts +2 -2
  118. package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
  119. package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
  120. package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
  121. package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
  122. package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
  123. package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
  124. package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
  125. package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
  126. package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
  127. package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
  128. package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
  129. package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
  130. package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
  131. package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
  132. package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
  133. package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
  134. package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
  135. package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
  136. package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
  137. package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
  138. package/src/modes/{interactive/components → components}/status-line.ts +3 -3
  139. package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
  140. package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
  141. package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
  142. package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
  143. package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
  144. package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
  145. package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
  146. package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
  147. package/src/modes/{interactive/components → components}/user-message.ts +1 -1
  148. package/src/modes/{interactive/components → components}/welcome.ts +2 -2
  149. package/src/modes/{interactive/controllers → controllers}/command-controller.ts +381 -30
  150. package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
  151. package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
  152. package/src/modes/{interactive/controllers → controllers}/input-controller.ts +61 -13
  153. package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
  154. package/src/modes/index.ts +1 -1
  155. package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +20 -15
  156. package/src/modes/print-mode.ts +1 -1
  157. package/src/modes/rpc/rpc-client.ts +3 -3
  158. package/src/modes/rpc/rpc-mode.ts +3 -3
  159. package/src/modes/rpc/rpc-types.ts +3 -3
  160. package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
  161. package/src/modes/{interactive/types.ts → types.ts} +10 -10
  162. package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
  163. package/src/{core/tools/patch → patch}/applicator.ts +1 -1
  164. package/src/{core/tools/patch → patch}/diff.ts +1 -1
  165. package/src/{core/tools/patch → patch}/index.ts +31 -36
  166. package/src/{core/tools/patch → patch}/shared.ts +9 -6
  167. package/src/prompts/agents/explore.md +83 -46
  168. package/src/prompts/agents/init.md +9 -4
  169. package/src/prompts/agents/plan.md +8 -7
  170. package/src/prompts/agents/reviewer.md +36 -18
  171. package/src/prompts/agents/task.md +4 -4
  172. package/src/prompts/compaction/branch-summary-preamble.md +0 -1
  173. package/src/prompts/review-request.md +0 -1
  174. package/src/prompts/system/custom-system-prompt.md +2 -14
  175. package/src/prompts/system/file-operations.md +0 -2
  176. package/src/prompts/system/system-prompt.md +182 -171
  177. package/src/prompts/system/web-search.md +26 -0
  178. package/src/prompts/tools/ask.md +31 -24
  179. package/src/prompts/tools/bash.md +20 -17
  180. package/src/prompts/tools/calculator.md +9 -5
  181. package/src/prompts/tools/fetch.md +16 -0
  182. package/src/prompts/tools/find.md +15 -5
  183. package/src/prompts/tools/gemini-image.md +21 -6
  184. package/src/prompts/tools/grep.md +28 -12
  185. package/src/prompts/tools/lsp.md +35 -14
  186. package/src/prompts/tools/patch.md +39 -41
  187. package/src/prompts/tools/python.md +59 -77
  188. package/src/prompts/tools/read.md +23 -22
  189. package/src/prompts/tools/replace.md +19 -12
  190. package/src/prompts/tools/ssh.md +21 -28
  191. package/src/prompts/tools/task.md +54 -44
  192. package/src/prompts/tools/todo-write.md +52 -163
  193. package/src/prompts/tools/web-search.md +16 -9
  194. package/src/prompts/tools/write.md +13 -2
  195. package/src/{core/sdk.ts → sdk.ts} +65 -34
  196. package/src/{core → session}/agent-session.ts +157 -41
  197. package/src/{core → session}/agent-storage.ts +2 -2
  198. package/src/session/artifacts.ts +110 -0
  199. package/src/{core → session}/auth-storage.ts +525 -203
  200. package/src/{core → session}/compaction/branch-summarization.ts +5 -5
  201. package/src/{core → session}/compaction/compaction.ts +6 -6
  202. package/src/{core → session}/compaction/utils.ts +3 -3
  203. package/src/{core → session}/history-storage.ts +1 -1
  204. package/src/{core → session}/messages.ts +6 -8
  205. package/src/{core → session}/session-manager.ts +2 -2
  206. package/src/{core → session}/storage-migration.ts +2 -2
  207. package/src/session/streaming-output.ts +177 -0
  208. package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
  209. package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
  210. package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
  211. package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
  212. package/src/{core/tools/task → task}/agents.ts +8 -8
  213. package/src/{core/tools/task → task}/commands.ts +5 -6
  214. package/src/{core/tools/task → task}/discovery.ts +3 -3
  215. package/src/{core/tools/task → task}/executor.ts +34 -44
  216. package/src/{core/tools/task → task}/index.ts +206 -50
  217. package/src/{core/tools/task → task}/render.ts +80 -23
  218. package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
  219. package/src/task/template.ts +47 -0
  220. package/src/{core/tools/task → task}/types.ts +19 -27
  221. package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
  222. package/src/{core/tools/task → task}/worker.ts +34 -29
  223. package/src/task/worktree.ts +166 -0
  224. package/src/{core/tools → tools}/ask.ts +13 -21
  225. package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
  226. package/src/{core/tools → tools}/bash.ts +61 -63
  227. package/src/{core/tools → tools}/calculator.ts +4 -4
  228. package/src/{core/tools → tools}/complete.ts +1 -1
  229. package/src/{core/tools → tools}/context.ts +2 -2
  230. package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
  231. package/src/{core/tools → tools}/find.ts +96 -107
  232. package/src/{core/tools → tools}/gemini-image.ts +420 -29
  233. package/src/{core/tools → tools}/grep.ts +155 -164
  234. package/src/{core/tools → tools}/index.ts +63 -56
  235. package/src/tools/list-limit.ts +40 -0
  236. package/src/{core/tools → tools}/ls.ts +44 -35
  237. package/src/{core/tools → tools}/notebook.ts +3 -3
  238. package/src/tools/output-meta.ts +443 -0
  239. package/src/tools/output-utils.ts +63 -0
  240. package/src/{core/tools → tools}/python.ts +106 -89
  241. package/src/tools/read.ts +882 -0
  242. package/src/{core/tools → tools}/render-utils.ts +1 -1
  243. package/src/{core/tools → tools}/renderers.ts +8 -10
  244. package/src/{core/tools → tools}/review.ts +2 -2
  245. package/src/{core/tools → tools}/ssh.ts +56 -59
  246. package/src/{core/tools → tools}/todo-write.ts +12 -23
  247. package/src/tools/tool-errors.ts +95 -0
  248. package/src/tools/tool-result.ts +92 -0
  249. package/src/{core/tools → tools}/truncate.ts +2 -2
  250. package/src/{core/tools → tools}/write.ts +15 -13
  251. package/src/utils/changelog.ts +1 -1
  252. package/src/{core → utils}/file-mentions.ts +4 -4
  253. package/src/utils/image-convert.ts +1 -1
  254. package/src/utils/image-resize.ts +1 -1
  255. package/src/utils/shell.ts +1 -1
  256. package/src/{core → utils}/title-generator.ts +4 -4
  257. package/src/utils/tools-manager.ts +1 -1
  258. package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
  259. package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
  260. package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
  261. package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
  262. package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
  263. package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
  264. package/src/{core/tools/web-search → web/search}/index.ts +22 -71
  265. package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
  266. package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
  267. package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
  268. package/src/{core/tools/web-search → web/search}/render.ts +3 -3
  269. package/scripts/migrate-sessions.sh +0 -93
  270. package/src/core/index.ts +0 -56
  271. package/src/core/python-prelude.ts +0 -3
  272. package/src/core/ssh-executor.ts +0 -5
  273. package/src/core/streaming-output.ts +0 -115
  274. package/src/core/tools/output.ts +0 -519
  275. package/src/core/tools/read.ts +0 -717
  276. package/src/core/tools/task/template.ts +0 -37
  277. package/src/prompts/tools/output.md +0 -47
  278. package/src/prompts/tools/web-fetch.md +0 -9
  279. /package/src/{core/tools/exa → exa}/types.ts +0 -0
  280. /package/src/{core → exec}/exec.ts +0 -0
  281. /package/src/{core/export-html → export/html}/template.css +0 -0
  282. /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
  283. /package/src/{core/export-html → export/html}/template.html +0 -0
  284. /package/src/{core/export-html → export/html}/template.js +0 -0
  285. /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
  286. /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
  287. /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
  288. /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
  289. /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
  290. /package/src/{core → extensibility}/extensions/index.ts +0 -0
  291. /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
  292. /package/src/{core → extensibility}/plugins/index.ts +0 -0
  293. /package/src/{core → extensibility}/plugins/loader.ts +0 -0
  294. /package/src/{core → extensibility}/plugins/manager.ts +0 -0
  295. /package/src/{core → extensibility}/plugins/parser.ts +0 -0
  296. /package/src/{core → extensibility}/plugins/types.ts +0 -0
  297. /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
  298. /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
  299. /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
  300. /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
  301. /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
  302. /package/src/{core/mcp → mcp}/client.ts +0 -0
  303. /package/src/{core/mcp → mcp}/index.ts +0 -0
  304. /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
  305. /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
  306. /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
  307. /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
  308. /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
  309. /package/src/modes/{interactive/components → components}/index.ts +0 -0
  310. /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
  311. /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
  312. /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
  313. /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
  314. /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
  315. /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
  316. /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
  317. /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
  318. /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
  319. /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
  320. /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
  321. /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
  322. /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
  323. /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
  324. /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
  325. /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
  326. /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
  327. /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
  328. /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
  329. /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
  330. /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
  331. /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
  332. /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
  333. /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
  334. /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
  335. /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
  336. /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
  337. /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
  338. /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
  339. /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
  340. /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
  341. /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
  342. /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
  343. /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
  344. /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
  345. /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
  346. /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
  347. /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
  348. /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
  349. /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
  350. /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
  351. /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
  352. /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
  353. /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
  354. /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
  355. /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
  356. /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
  357. /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  358. /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
  359. /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
  360. /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
  361. /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
  362. /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
  363. /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
  364. /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
  365. /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
  366. /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
  367. /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
  368. /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
  369. /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
  370. /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
  371. /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
  372. /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
  373. /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
  374. /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
  375. /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
  376. /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
  377. /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
  378. /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
  379. /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
  380. /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
  381. /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
  382. /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
  383. /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
  384. /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
  385. /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
  386. /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
  387. /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
  388. /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
  389. /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
  390. /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
  391. /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
  392. /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
  393. /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
  394. /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
  395. /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
  396. /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
  397. /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
  398. /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
  399. /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
  400. /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
  401. /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
  402. /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
  403. /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
  404. /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
  405. /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
  406. /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
  407. /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
  408. /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
  409. /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
  410. /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
  411. /package/src/modes/{interactive/theme → theme}/light.json +0 -0
  412. /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
  413. /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
  414. /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
  415. /package/src/{core/tools/patch → patch}/normative.ts +0 -0
  416. /package/src/{core/tools/patch → patch}/parser.ts +0 -0
  417. /package/src/{core/tools/patch → patch}/types.ts +0 -0
  418. /package/src/{core → session}/compaction/index.ts +0 -0
  419. /package/src/{core → session}/session-storage.ts +0 -0
  420. /package/src/{core/tools/task → task}/name-generator.ts +0 -0
  421. /package/src/{core/tools/task → task}/omp-command.ts +0 -0
  422. /package/src/{core/tools/task → task}/parallel.ts +0 -0
  423. /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
  424. /package/src/{core/tools → tools}/path-utils.ts +0 -0
  425. /package/src/{core → utils}/event-bus.ts +0 -0
  426. /package/src/{core → utils}/frontmatter.ts +0 -0
  427. /package/src/{core → utils}/terminal-notify.ts +0 -0
  428. /package/src/{core → utils}/timings.ts +0 -0
  429. /package/src/{core → utils}/utils.ts +0 -0
  430. /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
  431. /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
  432. /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
  433. /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
  434. /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
  435. /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
  436. /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
  437. /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
  438. /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
  439. /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
  440. /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
  441. /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
  442. /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
  443. /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
  444. /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
  445. /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
  446. /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
  447. /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
  448. /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
  449. /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
  450. /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
  451. /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
  452. /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
  453. /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
  454. /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
  455. /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
  456. /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
  457. /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
  458. /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
  459. /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
  460. /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
  461. /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
  462. /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
  463. /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
  464. /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
  465. /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
  466. /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
  467. /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
  468. /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
  469. /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
  470. /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
  471. /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
  472. /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
  473. /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
  474. /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
  475. /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
  476. /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
  477. /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
  478. /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
  479. /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
  480. /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
  481. /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
  482. /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
  483. /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
  484. /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
  485. /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
  486. /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
  487. /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
  488. /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
  489. /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
  490. /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
  491. /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
  492. /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
  493. /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
  494. /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
  495. /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
  496. /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
  497. /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
  498. /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
  499. /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
  500. /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
  501. /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
  502. /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Protocol handler for artifact:// URLs.
3
+ *
4
+ * Resolves artifact IDs to files in the session artifacts directory.
5
+ * Unlike agent://, artifacts are raw text with no JSON extraction.
6
+ *
7
+ * URL form:
8
+ * - artifact://<id> - Full artifact content
9
+ *
10
+ * Pagination is handled by the read tool via offset/limit parameters.
11
+ */
12
+
13
+ import * as fs from "node:fs";
14
+ import * as path from "node:path";
15
+ import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
16
+
17
+ export interface ArtifactProtocolOptions {
18
+ /**
19
+ * Returns the artifacts directory path, or null if no session.
20
+ */
21
+ getArtifactsDir: () => string | null;
22
+ }
23
+
24
+ /**
25
+ * List available artifact IDs in the directory.
26
+ */
27
+ function listAvailableArtifacts(artifactsDir: string): string[] {
28
+ try {
29
+ const files = fs.readdirSync(artifactsDir);
30
+ return files
31
+ .filter((f) => /^\d+\./.test(f))
32
+ .map((f) => f.split(".")[0])
33
+ .sort((a, b) => Number(a) - Number(b));
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Handler for artifact:// URLs.
41
+ *
42
+ * Resolves numeric artifact IDs to their text content.
43
+ * Artifacts are created by tools when output is truncated.
44
+ */
45
+ export class ArtifactProtocolHandler implements ProtocolHandler {
46
+ readonly scheme = "artifact";
47
+
48
+ constructor(private readonly options: ArtifactProtocolOptions) {}
49
+
50
+ async resolve(url: InternalUrl): Promise<InternalResource> {
51
+ const artifactsDir = this.options.getArtifactsDir();
52
+ if (!artifactsDir) {
53
+ throw new Error("No session - artifacts unavailable");
54
+ }
55
+
56
+ // Extract artifact ID from host
57
+ const id = url.rawHost || url.hostname;
58
+ if (!id) {
59
+ throw new Error("artifact:// URL requires a numeric ID: artifact://0");
60
+ }
61
+
62
+ // Validate ID is numeric
63
+ if (!/^\d+$/.test(id)) {
64
+ throw new Error(`artifact:// ID must be numeric, got: ${id}`);
65
+ }
66
+
67
+ // Check directory exists
68
+ if (!fs.existsSync(artifactsDir)) {
69
+ throw new Error("No artifacts directory found");
70
+ }
71
+
72
+ // Find file matching ID prefix
73
+ const files = fs.readdirSync(artifactsDir);
74
+ const match = files.find((f) => f.startsWith(`${id}.`));
75
+
76
+ if (!match) {
77
+ const available = listAvailableArtifacts(artifactsDir);
78
+ const availableStr = available.length > 0 ? available.join(", ") : "none";
79
+ throw new Error(`Artifact ${id} not found. Available: ${availableStr}`);
80
+ }
81
+
82
+ const filePath = path.join(artifactsDir, match);
83
+ const content = await Bun.file(filePath).text();
84
+
85
+ return {
86
+ url: url.href,
87
+ content,
88
+ contentType: "text/plain",
89
+ size: Buffer.byteLength(content, "utf-8"),
90
+ sourcePath: filePath,
91
+ };
92
+ }
93
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Internal URL routing system for agent:// and skill:// URLs.
3
+ *
4
+ * This module provides a unified way to resolve internal URLs without
5
+ * exposing filesystem paths to the agent.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { InternalUrlRouter, AgentProtocolHandler, SkillProtocolHandler } from './internal-urls';
10
+ *
11
+ * const router = new InternalUrlRouter();
12
+ * router.register(new AgentProtocolHandler({ getArtifactsDir: () => sessionDir }));
13
+ * router.register(new SkillProtocolHandler({ getSkills: () => skills }));
14
+ *
15
+ * if (router.canHandle('agent://reviewer_0')) {
16
+ * const resource = await router.resolve('agent://reviewer_0');
17
+ * console.log(resource.content);
18
+ * }
19
+ * ```
20
+ */
21
+
22
+ export { AgentProtocolHandler, type AgentProtocolOptions } from "./agent-protocol";
23
+ export { ArtifactProtocolHandler, type ArtifactProtocolOptions } from "./artifact-protocol";
24
+ export { applyQuery, parseQuery, pathToQuery } from "./json-query";
25
+ export { InternalUrlRouter } from "./router";
26
+ export { RuleProtocolHandler, type RuleProtocolOptions } from "./rule-protocol";
27
+ export { SkillProtocolHandler, type SkillProtocolOptions } from "./skill-protocol";
28
+ export type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
@@ -0,0 +1,126 @@
1
+ /**
2
+ * JSON query parser and executor for agent:// URL extraction.
3
+ *
4
+ * Supports jq-like syntax: .foo, [0], .foo.bar[0].baz, ["special-key"]
5
+ * Also supports path form: /foo/bar/0 -> .foo.bar[0]
6
+ */
7
+
8
+ /**
9
+ * Parse a jq-like query string into tokens.
10
+ *
11
+ * @example
12
+ * parseQuery(".foo.bar[0]") // ["foo", "bar", 0]
13
+ * parseQuery(".foo['special-key']") // ["foo", "special-key"]
14
+ */
15
+ export function parseQuery(query: string): Array<string | number> {
16
+ let input = query.trim();
17
+ if (!input) return [];
18
+ if (input.startsWith(".")) input = input.slice(1);
19
+ if (!input) return [];
20
+
21
+ const tokens: Array<string | number> = [];
22
+ let i = 0;
23
+
24
+ const isIdentChar = (ch: string) => /[A-Za-z0-9_-]/.test(ch);
25
+
26
+ while (i < input.length) {
27
+ const ch = input[i];
28
+ if (ch === ".") {
29
+ i++;
30
+ continue;
31
+ }
32
+ if (ch === "[") {
33
+ const closeIndex = input.indexOf("]", i + 1);
34
+ if (closeIndex === -1) {
35
+ throw new Error(`Invalid query: missing ] in ${query}`);
36
+ }
37
+ const raw = input.slice(i + 1, closeIndex).trim();
38
+ if (!raw) {
39
+ throw new Error(`Invalid query: empty [] in ${query}`);
40
+ }
41
+ const quote = raw[0];
42
+ if ((quote === '"' || quote === "'") && raw.endsWith(quote)) {
43
+ let inner = raw.slice(1, -1);
44
+ inner = inner.replace(/\\(["'\\])/g, "$1");
45
+ tokens.push(inner);
46
+ } else if (/^\d+$/.test(raw)) {
47
+ tokens.push(Number(raw));
48
+ } else {
49
+ tokens.push(raw);
50
+ }
51
+ i = closeIndex + 1;
52
+ continue;
53
+ }
54
+
55
+ const start = i;
56
+ while (i < input.length && isIdentChar(input[i])) {
57
+ i++;
58
+ }
59
+ if (start === i) {
60
+ throw new Error(`Invalid query: unexpected token '${input[i]}' in ${query}`);
61
+ }
62
+ const ident = input.slice(start, i);
63
+ tokens.push(ident);
64
+ }
65
+
66
+ return tokens;
67
+ }
68
+
69
+ /**
70
+ * Apply a parsed query to a JSON value.
71
+ *
72
+ * @example
73
+ * applyQuery({ foo: { bar: [1, 2, 3] } }, ".foo.bar[0]") // 1
74
+ */
75
+ export function applyQuery(data: unknown, query: string): unknown {
76
+ const tokens = parseQuery(query);
77
+ let current: unknown = data;
78
+ for (const token of tokens) {
79
+ if (current === null || current === undefined) return undefined;
80
+ if (typeof token === "number") {
81
+ if (!Array.isArray(current)) return undefined;
82
+ current = current[token];
83
+ continue;
84
+ }
85
+ if (typeof current !== "object") return undefined;
86
+ const record = current as Record<string, unknown>;
87
+ current = record[token];
88
+ }
89
+ return current;
90
+ }
91
+
92
+ /**
93
+ * Convert a URL path form to a query string.
94
+ *
95
+ * Path form: /foo/bar/0 -> .foo.bar[0]
96
+ * Trailing slash is normalized (ignored).
97
+ *
98
+ * Segments that are not valid identifiers use bracket notation: ['segment']
99
+ */
100
+ export function pathToQuery(urlPath: string): string {
101
+ if (!urlPath || urlPath === "/") return "";
102
+
103
+ const segments = urlPath.split("/").filter(Boolean);
104
+ if (segments.length === 0) return "";
105
+
106
+ const parts: string[] = [];
107
+ for (const segment of segments) {
108
+ let decoded = segment;
109
+ try {
110
+ decoded = decodeURIComponent(segment);
111
+ } catch {
112
+ decoded = segment;
113
+ }
114
+ const isIdentifier = /^[A-Za-z0-9_-]+$/.test(decoded);
115
+ if (/^\d+$/.test(decoded)) {
116
+ parts.push(`[${decoded}]`);
117
+ } else if (isIdentifier) {
118
+ parts.push(`.${decoded}`);
119
+ } else {
120
+ const escaped = decoded.replace(/\\/g, String.raw`\\`).replace(/'/g, String.raw`\'`);
121
+ parts.push(`['${escaped}']`);
122
+ }
123
+ }
124
+
125
+ return parts.join("");
126
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Internal URL router for resolving agent:// and skill:// URLs.
3
+ */
4
+
5
+ import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
6
+
7
+ /**
8
+ * Router for internal URL schemes.
9
+ *
10
+ * Dispatches URLs like `agent://output_id` or `skill://skill-name` to
11
+ * registered protocol handlers.
12
+ */
13
+ export class InternalUrlRouter {
14
+ private handlers = new Map<string, ProtocolHandler>();
15
+
16
+ /**
17
+ * Register a protocol handler.
18
+ * @param handler Handler to register (uses handler.scheme as key)
19
+ */
20
+ register(handler: ProtocolHandler): void {
21
+ this.handlers.set(handler.scheme, handler);
22
+ }
23
+
24
+ /**
25
+ * Check if the router can handle a URL.
26
+ * @param input URL string to check
27
+ */
28
+ canHandle(input: string): boolean {
29
+ const match = input.match(/^([a-z][a-z0-9+.-]*):\/\//i);
30
+ if (!match) return false;
31
+ const scheme = match[1].toLowerCase();
32
+ return this.handlers.has(scheme);
33
+ }
34
+
35
+ /**
36
+ * Resolve an internal URL to its content.
37
+ * @param input URL string (e.g., "agent://reviewer_0", "skill://notion-pages")
38
+ * @throws Error if scheme is not registered or resolution fails
39
+ */
40
+ async resolve(input: string): Promise<InternalResource> {
41
+ let parsed: URL;
42
+ try {
43
+ parsed = new URL(input);
44
+ } catch {
45
+ throw new Error(`Invalid URL: ${input}`);
46
+ }
47
+
48
+ const hostMatch = input.match(/^([a-z][a-z0-9+.-]*):\/\/([^/?#]*)/i);
49
+ let rawHost = hostMatch ? hostMatch[2] : parsed.hostname;
50
+ try {
51
+ rawHost = decodeURIComponent(rawHost);
52
+ } catch {
53
+ // Leave rawHost as-is if decoding fails.
54
+ }
55
+ (parsed as InternalUrl).rawHost = rawHost;
56
+
57
+ const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
58
+ const handler = this.handlers.get(scheme);
59
+
60
+ if (!handler) {
61
+ const available = Array.from(this.handlers.keys())
62
+ .map((s) => `${s}://`)
63
+ .join(", ");
64
+ throw new Error(`Unknown protocol: ${scheme}://\nSupported: ${available || "none"}`);
65
+ }
66
+
67
+ return handler.resolve(parsed as InternalUrl);
68
+ }
69
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Protocol handler for rule:// URLs.
3
+ *
4
+ * Resolves rule names to their content files.
5
+ *
6
+ * URL forms:
7
+ * - rule://<name> - Reads rule content
8
+ */
9
+
10
+ import type { Rule } from "$c/capability/rule";
11
+ import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
12
+
13
+ export interface RuleProtocolOptions {
14
+ /**
15
+ * Returns the currently loaded rules.
16
+ */
17
+ getRules: () => readonly Rule[];
18
+ }
19
+
20
+ /**
21
+ * Handler for rule:// URLs.
22
+ *
23
+ * Resolves rule names to their content.
24
+ */
25
+ export class RuleProtocolHandler implements ProtocolHandler {
26
+ readonly scheme = "rule";
27
+
28
+ constructor(private readonly options: RuleProtocolOptions) {}
29
+
30
+ async resolve(url: InternalUrl): Promise<InternalResource> {
31
+ const rules = this.options.getRules();
32
+
33
+ // Extract rule name from host
34
+ const ruleName = url.rawHost || url.hostname;
35
+ if (!ruleName) {
36
+ throw new Error("rule:// URL requires a rule name: rule://<name>");
37
+ }
38
+
39
+ // Find the rule
40
+ const rule = rules.find((r) => r.name === ruleName);
41
+ if (!rule) {
42
+ const available = rules.map((r) => r.name);
43
+ const availableStr = available.length > 0 ? available.join(", ") : "none";
44
+ throw new Error(`Unknown rule: ${ruleName}\nAvailable: ${availableStr}`);
45
+ }
46
+
47
+ return {
48
+ url: url.href,
49
+ content: rule.content,
50
+ contentType: "text/markdown",
51
+ size: Buffer.byteLength(rule.content, "utf-8"),
52
+ sourcePath: rule.path,
53
+ notes: [],
54
+ };
55
+ }
56
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Protocol handler for skill:// URLs.
3
+ *
4
+ * Resolves skill names to their SKILL.md files or relative paths within skill directories.
5
+ *
6
+ * URL forms:
7
+ * - skill://<name> - Reads SKILL.md
8
+ * - skill://<name>/<path> - Reads relative path within skill's baseDir
9
+ */
10
+
11
+ import * as path from "node:path";
12
+ import type { Skill } from "$c/extensibility/skills";
13
+ import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
14
+
15
+ export interface SkillProtocolOptions {
16
+ /**
17
+ * Returns the currently loaded skills.
18
+ */
19
+ getSkills: () => readonly Skill[];
20
+ }
21
+
22
+ /**
23
+ * Get content type based on file extension.
24
+ */
25
+ function getContentType(filePath: string): InternalResource["contentType"] {
26
+ const ext = path.extname(filePath).toLowerCase();
27
+ if (ext === ".md") return "text/markdown";
28
+ return "text/plain";
29
+ }
30
+
31
+ /**
32
+ * Validate that a path is safe (no traversal, no absolute paths).
33
+ */
34
+ function validateRelativePath(relativePath: string): void {
35
+ if (path.isAbsolute(relativePath)) {
36
+ throw new Error("Absolute paths are not allowed in skill:// URLs");
37
+ }
38
+
39
+ const normalized = path.normalize(relativePath);
40
+ if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("/..")) {
41
+ throw new Error("Path traversal (..) is not allowed in skill:// URLs");
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Handler for skill:// URLs.
47
+ *
48
+ * Resolves skill names to their content files.
49
+ */
50
+ export class SkillProtocolHandler implements ProtocolHandler {
51
+ readonly scheme = "skill";
52
+
53
+ constructor(private readonly options: SkillProtocolOptions) {}
54
+
55
+ async resolve(url: InternalUrl): Promise<InternalResource> {
56
+ const skills = this.options.getSkills();
57
+
58
+ // Extract skill name from host
59
+ const skillName = url.rawHost || url.hostname;
60
+ if (!skillName) {
61
+ throw new Error("skill:// URL requires a skill name: skill://<name>");
62
+ }
63
+
64
+ // Find the skill
65
+ const skill = skills.find((s) => s.name === skillName);
66
+ if (!skill) {
67
+ const available = skills.map((s) => s.name);
68
+ const availableStr = available.length > 0 ? available.join(", ") : "none";
69
+ throw new Error(`Unknown skill: ${skillName}\nAvailable: ${availableStr}`);
70
+ }
71
+
72
+ // Determine the file to read
73
+ let targetPath: string;
74
+ const urlPath = url.pathname;
75
+ const hasRelativePath = urlPath && urlPath !== "/" && urlPath !== "";
76
+
77
+ if (hasRelativePath) {
78
+ // Read relative path within skill's baseDir
79
+ const relativePath = decodeURIComponent(urlPath.slice(1)); // Remove leading /
80
+ validateRelativePath(relativePath);
81
+ targetPath = path.join(skill.baseDir, relativePath);
82
+
83
+ // Verify the resolved path is still within baseDir
84
+ const resolvedPath = path.resolve(targetPath);
85
+ const resolvedBaseDir = path.resolve(skill.baseDir);
86
+ if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
87
+ throw new Error("Path traversal is not allowed");
88
+ }
89
+ } else {
90
+ // Read SKILL.md
91
+ targetPath = skill.filePath;
92
+ }
93
+
94
+ // Read the file
95
+ const file = Bun.file(targetPath);
96
+ if (!(await file.exists())) {
97
+ throw new Error(`File not found: ${targetPath}`);
98
+ }
99
+
100
+ const content = await file.text();
101
+ const contentType = getContentType(targetPath);
102
+
103
+ return {
104
+ url: url.href,
105
+ content,
106
+ contentType,
107
+ size: Buffer.byteLength(content, "utf-8"),
108
+ sourcePath: targetPath,
109
+ notes: [],
110
+ };
111
+ }
112
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Types for the internal URL routing system.
3
+ *
4
+ * Internal URLs (agent://, skill://) are resolved by tools like fetch and read,
5
+ * providing access to agent outputs and skill files without exposing filesystem paths.
6
+ */
7
+
8
+ /**
9
+ * Resolved internal resource returned by protocol handlers.
10
+ */
11
+ export interface InternalResource {
12
+ /** Canonical URL that was resolved */
13
+ url: string;
14
+ /** Resolved text content */
15
+ content: string;
16
+ /** MIME type: text/markdown, application/json, or text/plain */
17
+ contentType: "text/markdown" | "application/json" | "text/plain";
18
+ /** Content size in bytes */
19
+ size?: number;
20
+ /** Underlying filesystem path (for debugging, not exposed to agent) */
21
+ sourcePath?: string;
22
+ /** Additional notes about resolution */
23
+ notes?: string[];
24
+ }
25
+
26
+ /**
27
+ * Parsed internal URL with preserved host casing.
28
+ */
29
+ export interface InternalUrl extends URL {
30
+ /**
31
+ * Raw host segment extracted from input, preserving case.
32
+ */
33
+ rawHost: string;
34
+ }
35
+
36
+ /**
37
+ * Handler for a specific internal URL scheme (e.g., agent://, skill://).
38
+ */
39
+ export interface ProtocolHandler {
40
+ /** The scheme this handler processes (without trailing ://) */
41
+ readonly scheme: string;
42
+ /**
43
+ * Resolve an internal URL to its content.
44
+ * @param url Parsed URL object
45
+ * @throws Error with user-friendly message if resolution fails
46
+ */
47
+ resolve(url: InternalUrl): Promise<InternalResource>;
48
+ }