one-code-extension 0.4.1 → 0.6.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 (439) hide show
  1. package/README.md +16 -5
  2. package/agents/general-purpose.md +2 -2
  3. package/docs/guide/README.md +2 -1
  4. package/docs/guide/bring-your-claude-code-setup.md +60 -9
  5. package/docs/guide/configuration.md +48 -14
  6. package/docs/guide/differences-from-claude-code.md +40 -28
  7. package/docs/guide/doctor.md +20 -13
  8. package/docs/guide/hooks.md +18 -3
  9. package/docs/guide/installation.md +26 -4
  10. package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
  11. package/docs/guide/providers-and-models.md +137 -54
  12. package/docs/guide/reference.md +27 -15
  13. package/docs/guide/sessions-and-context.md +69 -16
  14. package/docs/guide/skills-plugins-and-mcp.md +57 -20
  15. package/docs/guide/subagents-and-workflows.md +95 -29
  16. package/docs/guide/tasks-and-background-work.md +101 -26
  17. package/docs/guide/terminal-interface.md +24 -12
  18. package/docs/guide/tools.md +46 -5
  19. package/docs/guide/troubleshooting.md +13 -9
  20. package/docs/guide/windows.md +58 -31
  21. package/extensions/artifacts/gallery.ts +227 -0
  22. package/extensions/artifacts/index.ts +288 -0
  23. package/extensions/artifacts/store.ts +570 -0
  24. package/extensions/artifacts/viewer.ts +199 -0
  25. package/extensions/ask-user/description.ts +67 -0
  26. package/extensions/ask-user/dialogs.ts +80 -0
  27. package/extensions/ask-user/index.ts +37 -33
  28. package/extensions/ask-user/questions.ts +32 -7
  29. package/extensions/ask-user/widget.ts +45 -25
  30. package/extensions/auto-mode/actions.ts +6 -0
  31. package/extensions/auto-mode/cache.ts +88 -0
  32. package/extensions/auto-mode/classifier-prompt.ts +5 -4
  33. package/extensions/auto-mode/classifier.ts +290 -65
  34. package/extensions/auto-mode/config.ts +72 -20
  35. package/extensions/auto-mode/decision-log.ts +2 -0
  36. package/extensions/auto-mode/defaults.ts +1 -1
  37. package/extensions/auto-mode/denials.ts +124 -0
  38. package/extensions/auto-mode/git-checkout-programs.ts +187 -0
  39. package/extensions/auto-mode/git-status-meta.ts +159 -0
  40. package/extensions/auto-mode/history.ts +189 -0
  41. package/extensions/auto-mode/instructions.ts +58 -52
  42. package/extensions/auto-mode/model-select.ts +159 -134
  43. package/extensions/auto-mode/paths.ts +57 -5
  44. package/extensions/auto-mode/pause.ts +57 -2
  45. package/extensions/auto-mode/prompt.ts +37 -21
  46. package/extensions/auto-mode/read-only-options.ts +724 -0
  47. package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
  48. package/extensions/auto-mode/rules.ts +18 -1
  49. package/extensions/auto-mode/safety-floor.ts +361 -18
  50. package/extensions/auto-mode/setup-run.ts +20 -14
  51. package/extensions/auto-mode/shell-analysis.ts +764 -490
  52. package/extensions/auto-mode/shell-parse.ts +885 -0
  53. package/extensions/auto-mode/system-note.ts +33 -0
  54. package/extensions/auto-mode/transcript.ts +80 -51
  55. package/extensions/background/cron.ts +642 -0
  56. package/extensions/background/index.ts +614 -248
  57. package/extensions/background/loop-fire.ts +173 -0
  58. package/extensions/background/loop-preamble.ts +59 -0
  59. package/extensions/background/loop-skill.ts +163 -0
  60. package/extensions/background/monitor-batch.ts +53 -0
  61. package/extensions/background/noop-fold.ts +128 -0
  62. package/extensions/background/switch-guard.ts +56 -0
  63. package/extensions/background/wakeup.ts +178 -55
  64. package/extensions/bash/background.ts +53 -17
  65. package/extensions/bash/child.ts +7 -0
  66. package/extensions/bash/description.ts +188 -0
  67. package/extensions/bash/guards.ts +15 -67
  68. package/extensions/bash/index.ts +66 -35
  69. package/extensions/branding/compaction-keep.ts +63 -0
  70. package/extensions/branding/declined.ts +22 -0
  71. package/extensions/branding/index.ts +133 -9
  72. package/extensions/branding/prompt-editor.ts +53 -6
  73. package/extensions/branding/queued-edit.ts +31 -0
  74. package/extensions/branding/replaced-builtins.ts +68 -0
  75. package/extensions/branding/startup.ts +33 -71
  76. package/extensions/btw/index.ts +250 -78
  77. package/extensions/btw/panel.ts +228 -100
  78. package/extensions/btw/prompt.ts +41 -4
  79. package/extensions/cache-marks/index.ts +42 -0
  80. package/extensions/claude-compat/index.ts +7 -11
  81. package/extensions/claude-context/index.ts +436 -63
  82. package/extensions/compaction/cut.ts +157 -0
  83. package/extensions/compaction/index.ts +219 -75
  84. package/extensions/compaction/kept-thinking.ts +62 -0
  85. package/extensions/compaction/prompt.ts +15 -12
  86. package/extensions/context-budget/budget.ts +16 -2
  87. package/extensions/context-budget/index.ts +35 -15
  88. package/extensions/context-management/index.ts +36 -28
  89. package/extensions/doctor/build.ts +59 -8
  90. package/extensions/doctor/builtins.ts +84 -0
  91. package/extensions/doctor/cli.ts +7 -1
  92. package/extensions/doctor/compat.ts +88 -23
  93. package/extensions/doctor/dependencies.ts +41 -5
  94. package/extensions/doctor/fix-prompt.ts +9 -2
  95. package/extensions/doctor/index.ts +102 -58
  96. package/extensions/doctor/models.ts +102 -63
  97. package/extensions/doctor/presets.ts +42 -27
  98. package/extensions/doctor/report.ts +19 -2
  99. package/extensions/doctor/viewer.ts +49 -27
  100. package/extensions/effort/index.ts +141 -25
  101. package/extensions/effort/settings.ts +21 -0
  102. package/extensions/effort/slider.ts +24 -20
  103. package/extensions/exit/index.ts +32 -1
  104. package/extensions/file-tracker/index.ts +420 -42
  105. package/extensions/file-tracker/replay.ts +137 -24
  106. package/extensions/file-tracker/restore.ts +66 -0
  107. package/extensions/file-tracker/results.ts +66 -0
  108. package/extensions/file-tracker/shell-reads.ts +211 -0
  109. package/extensions/file-tracker/tracker.ts +45 -15
  110. package/extensions/footer/footer-line.ts +45 -24
  111. package/extensions/footer/index.ts +47 -2
  112. package/extensions/footer/pr.ts +34 -5
  113. package/extensions/hooks/compaction.ts +7 -0
  114. package/extensions/hooks/executor.ts +32 -30
  115. package/extensions/hooks/index.ts +209 -58
  116. package/extensions/hooks/matcher.ts +3 -0
  117. package/extensions/hooks/prompt-context.ts +59 -0
  118. package/extensions/hooks/protocol.ts +41 -3
  119. package/extensions/hooks/settings.ts +50 -19
  120. package/extensions/hooks/subagent-bridge.ts +2 -0
  121. package/extensions/hooks/trust.ts +26 -12
  122. package/extensions/idle-compact/index.ts +119 -0
  123. package/extensions/idle-compact/policy.ts +143 -0
  124. package/extensions/init/index.ts +11 -10
  125. package/extensions/init/prompt.ts +25 -32
  126. package/extensions/interrupted/index.ts +13 -1
  127. package/extensions/kept-thinking/index.ts +27 -0
  128. package/extensions/ledger/index.ts +46 -0
  129. package/extensions/ledger/wire.ts +154 -0
  130. package/extensions/lib/agent-cron.ts +146 -0
  131. package/extensions/lib/agent-loader.ts +18 -3
  132. package/extensions/lib/agent-view.ts +85 -0
  133. package/extensions/lib/anthropic-auth.ts +98 -0
  134. package/extensions/lib/anthropic-payload.ts +82 -1
  135. package/extensions/lib/anthropic-server-call.ts +230 -0
  136. package/extensions/lib/anthropic-server-tools.ts +485 -0
  137. package/extensions/lib/app-launch.mjs +94 -0
  138. package/extensions/lib/argument-hints.ts +190 -0
  139. package/extensions/lib/atomic-write.ts +6 -2
  140. package/extensions/lib/auto-memory.ts +75 -0
  141. package/extensions/lib/bash-parser.ts +84 -0
  142. package/extensions/lib/btw-fork.ts +93 -0
  143. package/extensions/lib/child-extensions.ts +52 -0
  144. package/extensions/lib/child-writes.ts +45 -0
  145. package/extensions/lib/claude-context.ts +515 -100
  146. package/extensions/lib/claude-external-includes.ts +68 -0
  147. package/extensions/lib/claude-id.ts +22 -0
  148. package/extensions/lib/claude-rules.ts +323 -0
  149. package/extensions/lib/claude-settings.ts +76 -4
  150. package/extensions/lib/compaction-boundary.ts +36 -0
  151. package/extensions/lib/compaction-keep.mjs +83 -0
  152. package/extensions/lib/config-mode.ts +112 -0
  153. package/extensions/lib/consent-dialogs.ts +63 -0
  154. package/extensions/lib/consent-preview.ts +5 -1
  155. package/extensions/lib/consent-stores.ts +48 -0
  156. package/extensions/lib/context-facts.ts +97 -0
  157. package/extensions/lib/covered-messages.ts +23 -0
  158. package/extensions/lib/deferred.ts +359 -25
  159. package/extensions/lib/dock-rows.ts +60 -0
  160. package/extensions/lib/empty-tool-result.ts +27 -0
  161. package/extensions/lib/environment-block.ts +76 -0
  162. package/extensions/lib/fetch-timeout.ts +24 -6
  163. package/extensions/{system-prompt → lib}/git-status.ts +51 -13
  164. package/extensions/lib/git.ts +75 -13
  165. package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
  166. package/extensions/lib/grammars/README.md +2 -0
  167. package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
  168. package/extensions/lib/guide-docs.ts +45 -0
  169. package/extensions/lib/headless-output.ts +46 -0
  170. package/extensions/lib/hook-gate.ts +7 -7
  171. package/extensions/lib/interrupt.ts +20 -4
  172. package/extensions/lib/key-input.ts +455 -0
  173. package/extensions/lib/keys.ts +14 -1
  174. package/extensions/lib/ledger/fold-memo.ts +49 -0
  175. package/extensions/lib/ledger/fold.ts +265 -0
  176. package/extensions/lib/ledger/layout.ts +72 -0
  177. package/extensions/lib/ledger/plan.ts +102 -0
  178. package/extensions/lib/ledger/records.ts +276 -0
  179. package/extensions/lib/ledger/state.ts +47 -0
  180. package/extensions/lib/ledger/validate.ts +141 -0
  181. package/extensions/lib/ledger/wire.ts +632 -0
  182. package/extensions/lib/local-command.ts +71 -19
  183. package/extensions/lib/mcp-overrides.ts +1 -1
  184. package/extensions/lib/mcp-share.ts +46 -0
  185. package/extensions/lib/memory-content.ts +146 -0
  186. package/extensions/lib/memory-index.ts +18 -0
  187. package/extensions/lib/memory.ts +134 -123
  188. package/extensions/lib/model-catalog/huggingface.json +178 -0
  189. package/extensions/lib/model-catalog/models-dev.json +18533 -0
  190. package/extensions/lib/model-catalog/openrouter.json +5050 -0
  191. package/extensions/lib/model-catalog-data.ts +513 -0
  192. package/extensions/lib/model-catalog.ts +462 -0
  193. package/extensions/lib/model-choice-warnings.ts +137 -0
  194. package/extensions/lib/model-pairings.ts +152 -0
  195. package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
  196. package/extensions/lib/model-policy.ts +112 -15
  197. package/extensions/lib/model-tier.ts +283 -355
  198. package/extensions/lib/model-unusable.ts +1 -1
  199. package/extensions/lib/newer-model.ts +100 -0
  200. package/extensions/lib/notices.ts +87 -0
  201. package/extensions/lib/notifications.ts +357 -73
  202. package/extensions/lib/one-code-settings.ts +74 -8
  203. package/extensions/lib/open-browser.ts +63 -0
  204. package/extensions/lib/openrouter-generation.ts +94 -0
  205. package/extensions/lib/operation-deadline.ts +30 -0
  206. package/extensions/lib/original-command.ts +4 -4
  207. package/extensions/lib/package-version.ts +1 -1
  208. package/extensions/lib/panel-focus.ts +134 -0
  209. package/extensions/lib/paths.ts +17 -2
  210. package/extensions/lib/permission-gate.ts +72 -6
  211. package/extensions/lib/persisted-output.ts +60 -8
  212. package/extensions/lib/pi-ai-estimate.ts +19 -3
  213. package/extensions/lib/pi-version.ts +38 -2
  214. package/extensions/lib/plan-mode-channels.ts +42 -5
  215. package/extensions/lib/plugins.ts +26 -17
  216. package/extensions/lib/powershell-parse-script.ts +208 -0
  217. package/extensions/lib/powershell-parser.ts +344 -0
  218. package/extensions/lib/previous-mark.ts +101 -0
  219. package/extensions/lib/process-state.ts +48 -0
  220. package/extensions/lib/process-tree.ts +132 -8
  221. package/extensions/lib/prompt-options.ts +82 -0
  222. package/extensions/lib/queued-delivery.ts +44 -0
  223. package/extensions/lib/reminders.ts +370 -358
  224. package/extensions/lib/replaced-builtins.mjs +199 -0
  225. package/extensions/lib/replay-call.ts +70 -0
  226. package/extensions/lib/request-replay.ts +547 -0
  227. package/extensions/lib/scratchpad.ts +81 -14
  228. package/extensions/lib/send-now-channel.ts +7 -0
  229. package/extensions/lib/session-model-tier.ts +51 -0
  230. package/extensions/lib/session-work.ts +64 -0
  231. package/extensions/lib/settings-channels.ts +4 -4
  232. package/extensions/lib/shell-exit-semantics.ts +170 -0
  233. package/extensions/lib/shell-quote.ts +11 -0
  234. package/extensions/lib/shell-result.ts +70 -0
  235. package/extensions/lib/shell-spawn.ts +70 -6
  236. package/extensions/lib/shell-tool.ts +75 -25
  237. package/extensions/lib/side-call-cache.ts +124 -0
  238. package/extensions/lib/side-call-run.ts +96 -0
  239. package/extensions/lib/side-call-usage.ts +38 -0
  240. package/extensions/lib/side-call.ts +24 -1
  241. package/extensions/lib/single-flight.ts +18 -0
  242. package/extensions/lib/skill-body.ts +42 -0
  243. package/extensions/lib/skill-scan.ts +63 -24
  244. package/extensions/lib/spill-file.ts +140 -0
  245. package/extensions/lib/subagent-handback.ts +112 -0
  246. package/extensions/lib/system-role.ts +156 -0
  247. package/extensions/lib/terminal-text.ts +114 -0
  248. package/extensions/lib/text-width.ts +47 -16
  249. package/extensions/lib/timer-ops.ts +20 -0
  250. package/extensions/lib/tool-additions.ts +129 -0
  251. package/extensions/lib/tool-call-corruption.ts +134 -0
  252. package/extensions/lib/tool-path.ts +66 -0
  253. package/extensions/lib/tool-variants.ts +69 -0
  254. package/extensions/lib/tui-render.ts +255 -27
  255. package/extensions/lib/turn-budget-layout.ts +159 -0
  256. package/extensions/lib/update-check.mjs +15 -2
  257. package/extensions/lib/usage-bus.ts +1 -1
  258. package/extensions/lib/user-denial.ts +38 -0
  259. package/extensions/lib/workspace-channel.ts +16 -0
  260. package/extensions/lib/worktree-channel.ts +31 -3
  261. package/extensions/lib/worktree-isolation.ts +40 -10
  262. package/extensions/lsp/client.ts +8 -9
  263. package/extensions/lsp/index.ts +76 -12
  264. package/extensions/lsp/keep-alive.ts +1 -1
  265. package/extensions/lsp/trust.ts +119 -0
  266. package/extensions/lsp/watcher.ts +1 -1
  267. package/extensions/mcp/announce.ts +90 -43
  268. package/extensions/mcp/client.ts +161 -48
  269. package/extensions/mcp/config.ts +132 -8
  270. package/extensions/mcp/discovery.ts +19 -0
  271. package/extensions/mcp/index.ts +220 -121
  272. package/extensions/mcp/oauth/browser.ts +38 -17
  273. package/extensions/mcp/oauth/flow.ts +25 -17
  274. package/extensions/mcp/oauth/provider.ts +14 -8
  275. package/extensions/mcp/oauth/store.ts +34 -16
  276. package/extensions/mcp/panel/keys.ts +12 -11
  277. package/extensions/mcp/panel/render.ts +9 -6
  278. package/extensions/mcp/schema.ts +104 -5
  279. package/extensions/mcp/trust.ts +164 -73
  280. package/extensions/memory/entries.ts +23 -14
  281. package/extensions/memory/index.ts +134 -28
  282. package/extensions/memory/open-external.ts +33 -18
  283. package/extensions/memory/panel.ts +135 -26
  284. package/extensions/model-default/index.ts +86 -19
  285. package/extensions/newer-model/index.ts +31 -0
  286. package/extensions/notebook/description.ts +18 -0
  287. package/extensions/notebook/index.ts +6 -5
  288. package/extensions/notebook/notebook.ts +1 -1
  289. package/extensions/permissions/index.ts +1426 -398
  290. package/extensions/permissions/matcher.ts +425 -74
  291. package/extensions/permissions/modes.ts +11 -4
  292. package/extensions/permissions/panel/host.ts +130 -0
  293. package/extensions/permissions/panel/keys.ts +48 -0
  294. package/extensions/permissions/panel/render.ts +450 -0
  295. package/extensions/permissions/panel/state.ts +550 -0
  296. package/extensions/permissions/powershell-cmdlets.ts +198 -0
  297. package/extensions/permissions/powershell-paths.ts +197 -0
  298. package/extensions/permissions/powershell-rules.ts +207 -280
  299. package/extensions/permissions/powershell-tree.ts +557 -0
  300. package/extensions/permissions/project-trust.ts +27 -11
  301. package/extensions/permissions/protected-paths.ts +14 -2
  302. package/extensions/permissions/session-grant.ts +6 -0
  303. package/extensions/permissions/settings.ts +166 -31
  304. package/extensions/permissions/subagent-gate.ts +25 -0
  305. package/extensions/permissions/workspace.ts +78 -0
  306. package/extensions/plan-mode/index.ts +276 -92
  307. package/extensions/plan-mode/reminder.ts +89 -21
  308. package/extensions/plan-mode/run-state.ts +26 -0
  309. package/extensions/plan-mode/texts.ts +177 -0
  310. package/extensions/plan-mode/viewer.ts +92 -27
  311. package/extensions/plugins/counts.ts +3 -3
  312. package/extensions/plugins/index.ts +64 -46
  313. package/extensions/plugins/install/install.ts +16 -0
  314. package/extensions/plugins/marketplace/sync.ts +9 -3
  315. package/extensions/plugins/marketplace/types.ts +3 -1
  316. package/extensions/plugins/panel/keys.ts +21 -24
  317. package/extensions/plugins/panel/render.ts +12 -6
  318. package/extensions/plugins/shell-expand.ts +107 -0
  319. package/extensions/plugins/template.ts +32 -15
  320. package/extensions/powershell/child.ts +7 -0
  321. package/extensions/powershell/description.ts +38 -6
  322. package/extensions/powershell/guards.ts +1 -1
  323. package/extensions/powershell/index.ts +14 -7
  324. package/extensions/recap/index.ts +73 -71
  325. package/extensions/recap/prompt.ts +2 -2
  326. package/extensions/recap/scheduler.ts +3 -5
  327. package/extensions/search-tools/grep-merge.ts +39 -0
  328. package/extensions/search-tools/index.ts +0 -0
  329. package/extensions/send-now/chord.ts +62 -0
  330. package/extensions/send-now/index.ts +133 -0
  331. package/extensions/session-title/index.ts +3 -0
  332. package/extensions/session-title/title.ts +4 -1
  333. package/extensions/skill/announce.ts +30 -0
  334. package/extensions/skill/code-review.ts +97 -0
  335. package/extensions/skill/index.ts +469 -132
  336. package/extensions/skill/invoke.ts +118 -75
  337. package/extensions/skill/listing.ts +133 -9
  338. package/extensions/skill/panel/keys.ts +15 -16
  339. package/extensions/skill/panel/render.ts +5 -2
  340. package/extensions/skill/panel/state.ts +12 -1
  341. package/extensions/subagents/agent-args.ts +39 -0
  342. package/extensions/subagents/agent-description.ts +132 -0
  343. package/extensions/subagents/agents.ts +146 -31
  344. package/extensions/subagents/default-model.ts +2 -3
  345. package/extensions/subagents/delegation-steer.ts +4 -4
  346. package/extensions/subagents/fork-cache.ts +82 -0
  347. package/extensions/subagents/guide-agent.ts +189 -0
  348. package/extensions/subagents/hand-back-review.ts +3 -0
  349. package/extensions/subagents/index.ts +1594 -852
  350. package/extensions/subagents/live-runs.ts +26 -6
  351. package/extensions/subagents/model-select.ts +38 -25
  352. package/extensions/subagents/outcome.ts +5 -2
  353. package/extensions/subagents/panel-keys.ts +73 -31
  354. package/extensions/subagents/panel-render.ts +120 -96
  355. package/extensions/subagents/panel-widget.ts +60 -89
  356. package/extensions/subagents/parent-mode.ts +50 -0
  357. package/extensions/subagents/pending-claim.ts +18 -1
  358. package/extensions/subagents/runner.ts +135 -80
  359. package/extensions/subagents/runs.ts +44 -5
  360. package/extensions/subagents/send-to-main-tool.ts +1 -1
  361. package/extensions/subagents/session-turns.ts +32 -8
  362. package/extensions/subagents/shell-panel.ts +36 -178
  363. package/extensions/subagents/tasks-dialog.ts +170 -0
  364. package/extensions/subagents/viewport-mirror.ts +95 -0
  365. package/extensions/subagents/worktree.ts +57 -13
  366. package/extensions/system-prompt/environment.ts +19 -19
  367. package/extensions/system-prompt/idle-turn.ts +65 -0
  368. package/extensions/system-prompt/index.ts +165 -47
  369. package/extensions/system-prompt/template.ts +34 -51
  370. package/extensions/system-prompt/tiers/common.ts +23 -64
  371. package/extensions/system-prompt/tiers/frontier.ts +32 -11
  372. package/extensions/system-prompt/tiers/low.ts +67 -45
  373. package/extensions/system-prompt/tiers/mid.ts +96 -38
  374. package/extensions/system-reminder/index.ts +204 -70
  375. package/extensions/tasks/descriptions.ts +190 -0
  376. package/extensions/tasks/index.ts +120 -41
  377. package/extensions/tasks/store.ts +83 -16
  378. package/extensions/tool-call-corruption/child.ts +7 -0
  379. package/extensions/tool-call-corruption/index.ts +237 -0
  380. package/extensions/tool-search/index.ts +366 -144
  381. package/extensions/tool-style/index.ts +68 -10
  382. package/extensions/tool-style/write-description.ts +40 -0
  383. package/extensions/tool-style/write-render.ts +237 -0
  384. package/extensions/turn-duration/index.ts +7 -3
  385. package/extensions/turn-duration/line.ts +47 -4
  386. package/extensions/web/backends.ts +6 -9
  387. package/extensions/web/description.ts +50 -0
  388. package/extensions/web/index.ts +118 -53
  389. package/extensions/web/native-call.ts +15 -0
  390. package/extensions/web/native-results.ts +41 -0
  391. package/extensions/web-fetch/description.ts +42 -0
  392. package/extensions/web-fetch/extract.ts +53 -6
  393. package/extensions/web-fetch/index.ts +125 -76
  394. package/extensions/web-fetch/page-content.ts +19 -0
  395. package/extensions/web-fetch/response.ts +49 -0
  396. package/extensions/web-fetch/summarize.ts +9 -10
  397. package/extensions/workflow/agent-session.ts +108 -18
  398. package/extensions/workflow/args.ts +24 -0
  399. package/extensions/workflow/description.ts +67 -0
  400. package/extensions/workflow/globals.ts +17 -3
  401. package/extensions/workflow/index.ts +157 -162
  402. package/extensions/workflow/records.ts +1 -0
  403. package/extensions/workflow/run-manager.ts +33 -6
  404. package/extensions/workflow/saved-workflows.ts +3 -2
  405. package/extensions/workflow/types.ts +22 -1
  406. package/extensions/workflow/viewer.ts +54 -24
  407. package/extensions/workflow/widget.ts +20 -0
  408. package/extensions/worktree/descriptions.ts +91 -0
  409. package/extensions/worktree/guards.ts +316 -54
  410. package/extensions/worktree/index.ts +162 -44
  411. package/extensions/worktree/powershell-guards.ts +167 -0
  412. package/extensions/worktree/rewrite.ts +14 -16
  413. package/package.json +21 -7
  414. package/skills/artifact-design/SKILL.md +85 -0
  415. package/skills/code-review/SKILL.md +20 -20
  416. package/skills/code-review/cells/arguments-and-flags.md +19 -0
  417. package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
  418. package/skills/code-review/cells/high-8-inline.md +122 -0
  419. package/skills/code-review/cells/low-1-pass-max4.md +27 -0
  420. package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
  421. package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
  422. package/skills/code-review/cells/medium-3x5-verify.md +132 -0
  423. package/skills/code-review/cells/minimal-prompt.md +32 -0
  424. package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
  425. package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
  426. package/skills/fewer-permission-prompts/SKILL.md +1 -1
  427. package/skills/loop/SKILL.md +11 -0
  428. package/skills/security-review/SKILL.md +2 -5
  429. package/skills/simplify/SKILL.md +7 -5
  430. package/skills/workflow-authoring/SKILL.md +143 -0
  431. package/themes/onecode-light.json +1 -1
  432. package/themes/onecode.json +1 -1
  433. package/types/app.d.ts +58 -0
  434. package/types/community-packages.d.ts +26 -1
  435. package/extensions/auto-mode/recoverability.ts +0 -229
  436. package/extensions/lib/capability-index.ts +0 -466
  437. package/extensions/lib/model-facts.generated.json +0 -2675
  438. package/extensions/lib/model-facts.ts +0 -162
  439. package/extensions/tool-search/announce.ts +0 -42
@@ -0,0 +1,227 @@
1
+ /**
2
+ * The artifact gallery page, `~/.onecode/artifacts/index.html` (pure, no fs).
3
+ * It lists every artifact with links to open it and each earlier version, and
4
+ * a Download button.
5
+ *
6
+ * Download goes through a script payload rather than `<a download>` on the page
7
+ * file: a browser ignores the `download` attribute on a cross-origin link, and
8
+ * Firefox treats every `file:` URL as its own origin. A classic `<script src>`
9
+ * loads across `file:` URLs in every browser, so the button loads
10
+ * `<id>/download.js`, which hands the page's HTML back to the gallery; the
11
+ * gallery saves it through a same-origin `blob:` link, where `download` always
12
+ * works. When the script cannot load, the button falls back to opening the page.
13
+ */
14
+
15
+ import { homedir } from "node:os";
16
+ import { tildify } from "../lib/paths.ts";
17
+ import type { ArtifactMeta } from "./store.ts";
18
+
19
+ /** The light and dark palettes, as tokens; shared with the viewer (viewer.ts). */
20
+ export const THEME_TOKENS = `:root {
21
+ color-scheme: light;
22
+ --bg: #f5f6f8;
23
+ --surface: #ffffff;
24
+ --text: #1b2230;
25
+ --muted: #5b6577;
26
+ --line: #dde1e8;
27
+ --accent: #2a4fb8;
28
+ --accent-text: #ffffff;
29
+ --focus: #2a4fb8;
30
+ --hover: #edf0f5;
31
+ }
32
+ @media (prefers-color-scheme: dark) {
33
+ :root:not([data-theme="light"]) {
34
+ color-scheme: dark;
35
+ --bg: #13161c;
36
+ --surface: #1b1f27;
37
+ --text: #e4e8f0;
38
+ --muted: #97a1b3;
39
+ --line: #2c3340;
40
+ --accent: #8fb0ff;
41
+ --accent-text: #0f1420;
42
+ --focus: #8fb0ff;
43
+ --hover: #252b36;
44
+ }
45
+ }
46
+ :root[data-theme="dark"] {
47
+ color-scheme: dark;
48
+ --bg: #13161c;
49
+ --surface: #1b1f27;
50
+ --text: #e4e8f0;
51
+ --muted: #97a1b3;
52
+ --line: #2c3340;
53
+ --accent: #8fb0ff;
54
+ --accent-text: #0f1420;
55
+ --focus: #8fb0ff;
56
+ --hover: #252b36;
57
+ }`;
58
+
59
+ /** The global the gallery defines and every `download.js` calls. */
60
+ export const DOWNLOAD_GLOBAL = "oneCodeArtifactDownload";
61
+
62
+ export function escapeHtml(text: string): string {
63
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
64
+ }
65
+
66
+ /**
67
+ * Script both the gallery and the viewer run: the download global every
68
+ * payload calls, saving through a `blob:` link, and each `<time datetime>`
69
+ * rewritten in the viewer's local time.
70
+ */
71
+ export const CLIENT_RUNTIME = ` window.${DOWNLOAD_GLOBAL} = function (payload) {
72
+ var url = URL.createObjectURL(new Blob([payload.html], { type: "text/html" }));
73
+ var link = document.createElement("a");
74
+ link.href = url;
75
+ link.download = payload.filename;
76
+ document.body.appendChild(link);
77
+ link.click();
78
+ link.remove();
79
+ setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
80
+ };
81
+ document.querySelectorAll("time[datetime]").forEach(function (node) {
82
+ var date = new Date(node.getAttribute("datetime"));
83
+ if (!isNaN(date)) node.textContent = date.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" });
84
+ });`;
85
+
86
+ /** `<id>/download.js`: the page's HTML as a call into the gallery or viewer. */
87
+ export function renderDownloadPayload(filename: string, html: string): string {
88
+ return `window.${DOWNLOAD_GLOBAL} && window.${DOWNLOAD_GLOBAL}(${JSON.stringify({ filename, html })});\n`;
89
+ }
90
+
91
+ /** `2026-09-27 14:05 UTC`: the at-rest text a script replaces with local time. */
92
+ export function utcStamp(iso: string): string {
93
+ return `${iso.slice(0, 10)} ${iso.slice(11, 16)} UTC`;
94
+ }
95
+
96
+ function renderEntry(meta: ArtifactMeta, home: string): string {
97
+ const id = escapeHtml(meta.id);
98
+ const versions: string[] = [];
99
+ for (let n = meta.version - 1; n >= 1; n--) versions.push(`<li><a href="${id}/view.html#v${n}">Version ${n}</a></li>`);
100
+ const facts = [
101
+ meta.project ? `<span class="project">${escapeHtml(tildify(meta.project, home))}</span>` : "",
102
+ `<time datetime="${escapeHtml(meta.updatedAt)}">${escapeHtml(utcStamp(meta.updatedAt))}</time>`,
103
+ `<span>v${meta.version}</span>`,
104
+ ].filter(Boolean);
105
+ const search = escapeHtml([meta.title, meta.description ?? "", meta.project ?? "", meta.id].join(" ").toLowerCase());
106
+ return `<li class="entry" data-search="${search}">
107
+ <div class="main">
108
+ <h2><a href="${id}/view.html">${escapeHtml(meta.title)}</a></h2>
109
+ ${meta.description ? `<p class="desc">${escapeHtml(meta.description)}</p>` : ""}
110
+ <p class="facts">${facts.join('<span class="sep" aria-hidden="true">·</span>')}</p>
111
+ ${
112
+ versions.length
113
+ ? `<details><summary>${versions.length} earlier version${versions.length === 1 ? "" : "s"}</summary><ul class="versions">${versions.join("")}</ul></details>`
114
+ : ""
115
+ }
116
+ </div>
117
+ <div class="actions">
118
+ <a class="button" href="${id}/view.html">Open</a>
119
+ <button type="button" class="button" data-download="${id}/download.js" data-fallback="${id}/index.html">Download</button>
120
+ </div>
121
+ <p class="id"><code>${id}</code></p>
122
+ </li>`;
123
+ }
124
+
125
+ export function renderGallery(artifacts: ArtifactMeta[], home: string = homedir()): string {
126
+ const count = artifacts.length;
127
+ const body = count
128
+ ? `<ul class="list" id="list">\n${artifacts.map((meta) => renderEntry(meta, home)).join("\n")}\n</ul>
129
+ <p class="empty" id="no-match" hidden>No artifact matches that filter.</p>`
130
+ : `<p class="empty">No artifacts yet. Ask One Code for a page, a report or a small tool, and it lands here.</p>`;
131
+ return `<!doctype html>
132
+ <html lang="en">
133
+ <head>
134
+ <meta charset="utf-8">
135
+ <meta name="viewport" content="width=device-width, initial-scale=1">
136
+ <title>One Code Artifacts</title>
137
+ <style>
138
+ ${THEME_TOKENS}
139
+ * { box-sizing: border-box; }
140
+ html, body { margin: 0; }
141
+ body {
142
+ background: var(--bg);
143
+ color: var(--text);
144
+ font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
145
+ padding: 0 16px;
146
+ }
147
+ .wrap { max-width: 880px; margin: 0 auto; padding-block: 40px 64px; display: flex; flex-direction: column; gap: 24px; }
148
+ header { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 16px; }
149
+ h1 { margin: 0; font-size: 26px; line-height: 1.2; letter-spacing: -0.01em; text-wrap: balance; }
150
+ .count { margin: 4px 0 0; color: var(--muted); font-size: 13px; }
151
+ input[type="search"] {
152
+ font: inherit; color: var(--text); background: var(--surface);
153
+ border: 1px solid var(--line); border-radius: 6px; padding: 7px 10px; width: min(280px, 100%);
154
+ }
155
+ .list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border-top: 1px solid var(--line); }
156
+ .entry {
157
+ display: grid; grid-template-columns: 1fr auto; gap: 4px 16px; align-items: start;
158
+ padding: 16px 0; border-bottom: 1px solid var(--line);
159
+ }
160
+ .entry[hidden] { display: none; }
161
+ .main { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
162
+ h2 { margin: 0; font-size: 17px; line-height: 1.3; text-wrap: balance; }
163
+ h2 a { color: var(--text); text-decoration: none; }
164
+ h2 a:hover { color: var(--accent); text-decoration: underline; }
165
+ .desc { margin: 0; max-width: 65ch; }
166
+ .facts { margin: 0; color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; display: flex; flex-wrap: wrap; gap: 0 8px; }
167
+ .project { overflow-wrap: anywhere; }
168
+ .id { grid-column: 1 / -1; margin: 0; }
169
+ code { font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--muted); }
170
+ details { font-size: 13px; color: var(--muted); }
171
+ summary { cursor: pointer; width: fit-content; }
172
+ .versions { margin: 6px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }
173
+ .versions a { color: var(--accent); }
174
+ .actions { display: flex; gap: 8px; }
175
+ .button {
176
+ font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; text-decoration: none;
177
+ border-radius: 6px; padding: 6px 12px; border: 1px solid var(--line);
178
+ background: var(--surface); color: var(--text);
179
+ }
180
+ .actions a.button { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
181
+ a:focus-visible, button:focus-visible, input:focus-visible, summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
182
+ .empty { margin: 0; color: var(--muted); }
183
+ @media (max-width: 480px) {
184
+ .entry { grid-template-columns: 1fr; }
185
+ }
186
+ </style>
187
+ </head>
188
+ <body>
189
+ <div class="wrap">
190
+ <header>
191
+ <div>
192
+ <h1>One Code Artifacts</h1>
193
+ <p class="count">${count} ${count === 1 ? "artifact" : "artifacts"}, newest first. Stored in this folder.</p>
194
+ </div>
195
+ ${count ? '<input type="search" id="filter" placeholder="Filter by title or project" aria-label="Filter artifacts">' : ""}
196
+ </header>
197
+ ${body}
198
+ </div>
199
+ <script>
200
+ (function () {
201
+ ${CLIENT_RUNTIME}
202
+ document.addEventListener("click", function (event) {
203
+ var button = event.target.closest && event.target.closest("[data-download]");
204
+ if (!button) return;
205
+ var script = document.createElement("script");
206
+ script.src = button.getAttribute("data-download");
207
+ script.onload = function () { script.remove(); };
208
+ script.onerror = function () { script.remove(); location.href = button.getAttribute("data-fallback"); };
209
+ document.head.appendChild(script);
210
+ });
211
+ var filter = document.getElementById("filter");
212
+ if (filter) filter.addEventListener("input", function () {
213
+ var query = filter.value.trim().toLowerCase();
214
+ var shown = 0;
215
+ document.querySelectorAll(".entry").forEach(function (entry) {
216
+ var match = !query || entry.getAttribute("data-search").indexOf(query) !== -1;
217
+ entry.hidden = !match;
218
+ if (match) shown++;
219
+ });
220
+ document.getElementById("no-match").hidden = shown !== 0;
221
+ });
222
+ })();
223
+ </script>
224
+ </body>
225
+ </html>
226
+ `;
227
+ }
@@ -0,0 +1,288 @@
1
+ /**
2
+ * artifacts extension — Claude Code's Artifact tool, kept on this machine: the
3
+ * model writes a self-contained HTML page, `artifact` stores it under
4
+ * `~/.onecode/artifacts/` with every earlier version kept (store.ts) and opens
5
+ * it in the user's browser. `/artifacts` browses, opens, exports and deletes
6
+ * them. Deferred behind tool_search; the page rules are the bundled
7
+ * `artifact-design` skill. Decision: `working-docs/decisions/tools.md`
8
+ * ("Artifacts").
9
+ */
10
+
11
+ import { randomBytes } from "node:crypto";
12
+ import { readFileSync, statSync } from "node:fs";
13
+ import { homedir } from "node:os";
14
+ import { basename } from "node:path";
15
+ import { StringEnum } from "@earendil-works/pi-ai";
16
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
17
+ import { Type } from "typebox";
18
+ import { readJsonFile } from "../lib/atomic-write.ts";
19
+ import { DEFER_CHANNEL } from "../lib/deferred.ts";
20
+ import { findProjectRoot } from "../lib/git.ts";
21
+ import { registerLocalCommand } from "../lib/local-command.ts";
22
+ import { oneCodeSettingsPath } from "../lib/one-code-settings.ts";
23
+ import { launchOpener, noDisplayReason, openerPlan } from "../lib/open-browser.ts";
24
+ import { tildify } from "../lib/paths.ts";
25
+ import { sanitizeDisplayText, terminalLink } from "../lib/terminal-text.ts";
26
+ import { resolveToolPath } from "../lib/tool-path.ts";
27
+ import { ccToolRenderers } from "../lib/tui-render.ts";
28
+ import {
29
+ type ArtifactMeta,
30
+ artifactsRoot,
31
+ deleteArtifact,
32
+ exportArtifact,
33
+ fileUrl,
34
+ galleryPath,
35
+ getArtifact,
36
+ listArtifacts,
37
+ listLine,
38
+ pagePath,
39
+ publishArtifact,
40
+ publishProblem,
41
+ resolveArtifactRef,
42
+ viewerPath,
43
+ writeGallery,
44
+ } from "./store.ts";
45
+
46
+ interface ArtifactArgs {
47
+ action?: string;
48
+ file_path?: string;
49
+ id?: string;
50
+ url?: string;
51
+ }
52
+
53
+ /** A publish result's details: what the renderer shows. */
54
+ interface PublishDetails {
55
+ id: string;
56
+ version: number;
57
+ /** The viewer page. */
58
+ path: string;
59
+ created: boolean;
60
+ }
61
+
62
+ const DESCRIPTION = `Publish a self-contained HTML page as an artifact: a page saved on this machine under ~/.onecode/artifacts/ and opened in the user's default browser. Use it when a page would be clearer than terminal text (a report, a visual explainer, a comparison, a diagram), or when the user will use the page rather than only read it (a dashboard, a small tool, a game). Advice the user will act on right away in the code they are working on belongs in your reply, not in an artifact.
63
+
64
+ Before writing the page, load the \`artifact-design\` skill with the skill tool; it holds the page rules (title, both themes, phone-width layout, libraries). Write the page to one .html file (your scratchpad when there is one), then publish it with \`file_path\`. Keep it self-contained: inline the CSS and JS, load a library only as a pinned script from cdnjs.cloudflare.com or cdn.jsdelivr.net, fonts only from Google Fonts, and embed images as data: URIs. Only the one file is stored, so a relative link to another local file breaks.
65
+
66
+ Actions (\`action\`, publish when omitted):
67
+ - publish: stores \`file_path\` as a new artifact and opens it in the browser. Publishing the same file again updates that artifact in place as a new version; earlier versions are kept. To update an artifact from an earlier session, pass its \`id\` (from list). \`description\` is one sentence for the gallery; \`title\` is the fallback for a page with no <title>.
68
+ - list: the saved artifacts, newest first, with ids and links.
69
+ - open: shows an existing artifact (\`id\`) in the browser again, or the gallery of all artifacts when no id is given.
70
+ - delete: removes an artifact and all its versions, only when the user asks; the user confirms it.
71
+
72
+ The browser shows the page under a bar with Download, Copy file path, the earlier versions and a link to the gallery, so the page needs no download or navigation controls of its own. An update does not reopen the browser: tell the user to refresh the tab. When no browser can be opened (an SSH session, no display), give the user the link. The page is opened from a file: URL, so browser storage may be shared with other local pages or unavailable: wrap every localStorage read and write in try/catch and make the page work without it. There is no runtime API: the page cannot call Claude or keep shared state.
73
+
74
+ Never publish a page that impersonates a real person or organization, fabricated records presented as genuine, or a form that collects credentials or payment details.`;
75
+
76
+ /** `artifacts.autoOpen` in ~/.onecode/settings.json; true unless set to false. */
77
+ function autoOpenEnabled(): boolean {
78
+ const settings = readJsonFile<{ artifacts?: { autoOpen?: unknown } }>(oneCodeSettingsPath(homedir()));
79
+ return settings?.artifacts?.autoOpen !== false;
80
+ }
81
+
82
+ /** Open a page the store built: why it was not opened, or undefined when it was. */
83
+ async function showInBrowser(path: string): Promise<string | undefined> {
84
+ const reason = noDisplayReason();
85
+ if (reason) return reason;
86
+ return (await launchOpener(openerPlan(fileUrl(path)))) ? undefined : "the browser could not be started";
87
+ }
88
+
89
+ /** The tool result's line about the browser. */
90
+ async function browserNote(path: string): Promise<string> {
91
+ const failed = await showInBrowser(path);
92
+ return failed ? `No browser was opened: ${failed}. Give the user the link.` : "Opened it in the user's default browser.";
93
+ }
94
+
95
+ /** The /artifacts status line after opening `path`. */
96
+ async function notifyOpened(ctx: ExtensionContext, path: string, name: string): Promise<void> {
97
+ const failed = await showInBrowser(path);
98
+ if (failed) ctx.ui.notify(`No browser was opened (${failed}): ${path}`, "warning");
99
+ else ctx.ui.notify(`Opened ${name}`, "info");
100
+ }
101
+
102
+ function textResult(text: string, details: object = {}, isError = false) {
103
+ return { content: [{ type: "text" as const, text }], details, ...(isError ? { isError: true } : {}) };
104
+ }
105
+
106
+ /**
107
+ * The title for a terminal menu or notice. Titles are sanitized when stored;
108
+ * this also guards a meta.json edited by hand.
109
+ */
110
+ function uiTitle(meta: ArtifactMeta): string {
111
+ return sanitizeDisplayText(meta.title);
112
+ }
113
+
114
+ /** Ask the user before deleting `meta`; the tool and /artifacts share the wording. */
115
+ async function confirmDelete(ctx: ExtensionContext, meta: ArtifactMeta, signal?: AbortSignal): Promise<boolean> {
116
+ const versions = meta.version === 1 ? "its only version" : `all ${meta.version} versions`;
117
+ const approved = await ctx.ui.confirm(`Delete the artifact "${uiTitle(meta)}"?`, `This permanently removes ${meta.id} and ${versions}.`, { signal });
118
+ return approved === true && !signal?.aborted;
119
+ }
120
+
121
+ export default function artifactsExtension(pi: ExtensionAPI) {
122
+ pi.registerTool({
123
+ name: "artifact",
124
+ label: "Artifact",
125
+ // Claude Code's shape: `Artifact(page.html)` over `⎿ Published <link>`.
126
+ ...ccToolRenderers<ArtifactArgs, PublishDetails>("Artifact", {
127
+ title: (args) => {
128
+ const action = args?.action ?? "publish";
129
+ if (action === "publish") return args?.file_path ? basename(args.file_path) : action;
130
+ const target = args?.id ?? args?.url;
131
+ return target ? `${action} ${target}` : action;
132
+ },
133
+ result: (result, _args, isError) => {
134
+ const details = result.details;
135
+ if (isError || !details?.path) return undefined;
136
+ return details.created ? "Published" : `Updated to version ${details.version}`;
137
+ },
138
+ // The short path, clickable where the terminal renders OSC 8 links.
139
+ link: (result, isError) => {
140
+ const path = result.details?.path;
141
+ if (isError || !path) return undefined;
142
+ return terminalLink(tildify(path, homedir()), fileUrl(path));
143
+ },
144
+ }),
145
+ description: DESCRIPTION,
146
+ parameters: Type.Object({
147
+ action: Type.Optional(StringEnum(["publish", "list", "open", "delete"] as const, { description: "What to do. Defaults to publish." })),
148
+ file_path: Type.Optional(Type.String({ description: "publish: the .html file to store." })),
149
+ title: Type.Optional(Type.String({ description: "publish: the fallback title for a page with no <title>." })),
150
+ description: Type.Optional(Type.String({ description: "publish: one sentence shown under the title in the gallery." })),
151
+ id: Type.Optional(Type.String({ description: "The artifact to update (publish), open or delete: its id, or the file URL publish returned." })),
152
+ url: Type.Optional(Type.String({ description: "Alias of `id` (Claude Code's name for it)." })),
153
+ limit: Type.Optional(Type.Number({ description: "list: the maximum number of artifacts to return (default 25)." })),
154
+ }),
155
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
156
+ const root = artifactsRoot();
157
+ const action = params.action ?? "publish";
158
+ const ref = params.id ?? params.url;
159
+ const resolved = ref === undefined ? undefined : resolveArtifactRef(root, ref);
160
+ if (ref !== undefined && !resolved) {
161
+ return textResult(`No artifact matches "${ref}". Run the artifact tool with action "list" to see the ids.`, {}, true);
162
+ }
163
+
164
+ if (action === "list") {
165
+ const all = listArtifacts(root);
166
+ if (all.length === 0) return textResult("No artifacts yet.");
167
+ const limit = Math.max(1, Math.floor(params.limit ?? 25));
168
+ const shown = all.slice(0, limit);
169
+ const more = all.length > shown.length ? `\n(${all.length - shown.length} more not shown; raise \`limit\` to see them.)` : "";
170
+ return textResult(`${all.length} artifact${all.length === 1 ? "" : "s"}, newest first:\n${shown.map((meta) => listLine(root, meta, homedir())).join("\n")}${more}\nGallery: ${fileUrl(galleryPath(root))}`);
171
+ }
172
+
173
+ if (action === "open") {
174
+ if (!resolved) {
175
+ writeGallery(root);
176
+ return textResult(`Gallery: ${fileUrl(galleryPath(root))}\n${await browserNote(galleryPath(root))}`);
177
+ }
178
+ return textResult(`${fileUrl(viewerPath(root, resolved))}\n${await browserNote(viewerPath(root, resolved))}`);
179
+ }
180
+
181
+ if (action === "delete") {
182
+ if (!resolved) return textResult("Pass the `id` of the artifact to delete.", {}, true);
183
+ const meta = getArtifact(root, resolved);
184
+ if (!meta) return textResult(`No artifact has the id "${resolved}".`, {}, true);
185
+ if (!ctx.hasUI) {
186
+ return textResult("Deleting an artifact needs the user's confirmation, and this session cannot ask. Tell the user to delete it with /artifacts.", {}, true);
187
+ }
188
+ const approved = await confirmDelete(ctx, meta, signal);
189
+ if (signal?.aborted) return textResult("The operation was aborted; the artifact was not deleted.", {}, true);
190
+ if (!approved) return textResult("The user declined; the artifact was not deleted.", {}, true);
191
+ deleteArtifact(root, resolved);
192
+ return textResult(`Deleted the artifact ${meta.id} ("${meta.title}").`);
193
+ }
194
+
195
+ if (!params.file_path) return textResult("`file_path` is required: the .html file to publish.", {}, true);
196
+ const sourcePath = resolveToolPath(params.file_path, ctx.cwd);
197
+ let bytes: number;
198
+ try {
199
+ const stat = statSync(sourcePath);
200
+ if (!stat.isFile()) return textResult(`${sourcePath} is not a file. Pass the path of the .html page to publish.`, {}, true);
201
+ bytes = stat.size;
202
+ } catch {
203
+ return textResult(`${sourcePath} does not exist. Write the page to that path first, then publish it.`, {}, true);
204
+ }
205
+ const problem = publishProblem(root, sourcePath, bytes);
206
+ if (problem) return textResult(problem, {}, true);
207
+
208
+ let result: ReturnType<typeof publishArtifact>;
209
+ try {
210
+ result = publishArtifact(root, {
211
+ sourcePath,
212
+ html: readFileSync(sourcePath, "utf-8"),
213
+ title: params.title,
214
+ description: params.description,
215
+ project: findProjectRoot(ctx.cwd) ?? ctx.cwd,
216
+ id: resolved,
217
+ now: new Date(),
218
+ suffix: () => randomBytes(3).toString("hex"),
219
+ });
220
+ } catch (error) {
221
+ return textResult(`Publishing failed: ${(error as Error).message}`, {}, true);
222
+ }
223
+ const { meta, created } = result;
224
+ const viewer = viewerPath(root, meta.id);
225
+ const url = fileUrl(viewer);
226
+ const lines = [
227
+ created
228
+ ? `Published "${meta.title}" as the artifact ${meta.id}.`
229
+ : `Updated the artifact ${meta.id} ("${meta.title}") to version ${meta.version}; earlier versions are kept.`,
230
+ `Link: ${url}`,
231
+ `File: ${pagePath(root, meta.id)}`,
232
+ ];
233
+ if (!created) lines.push("The browser was not reopened: tell the user to refresh the tab that shows it.");
234
+ else if (!autoOpenEnabled()) lines.push(`Opening the browser is off (artifacts.autoOpen is false in ${tildify(oneCodeSettingsPath(homedir()), homedir())}). Give the user the link.`);
235
+ else lines.push(await browserNote(viewer));
236
+ lines.push(`Gallery of all artifacts: ${fileUrl(galleryPath(root))}`);
237
+ const details: PublishDetails = { id: meta.id, version: meta.version, path: viewer, created };
238
+ return textResult(lines.join("\n"), details);
239
+ },
240
+ });
241
+
242
+ pi.events.emit(DEFER_CHANNEL, { name: "artifact", keywords: ["artifact", "html", "page", "browser", "report", "dashboard", "visualize", "publish"] });
243
+
244
+ registerLocalCommand(pi, "artifacts", {
245
+ description: "Browse, open, export or delete saved artifacts",
246
+ handler: async (_args: string, ctx: ExtensionContext) => {
247
+ const root = artifactsRoot();
248
+ const all = listArtifacts(root);
249
+ if (!ctx.hasUI) {
250
+ ctx.ui.notify(all.length ? all.map((meta) => listLine(root, meta, homedir())).join("\n") : "No artifacts yet.", "info");
251
+ return;
252
+ }
253
+ const GALLERY = "Open the gallery in the browser";
254
+ const labels = new Map<string, ArtifactMeta>();
255
+ for (const meta of all) labels.set(`${uiTitle(meta)} (v${meta.version}, ${meta.id})`, meta);
256
+ const picked = await ctx.ui.select(all.length ? `${all.length} artifact${all.length === 1 ? "" : "s"}` : "No artifacts yet", [GALLERY, ...labels.keys()]);
257
+ if (!picked) return;
258
+ if (picked === GALLERY) {
259
+ writeGallery(root);
260
+ await notifyOpened(ctx, galleryPath(root), "the artifact gallery");
261
+ return;
262
+ }
263
+ const meta = labels.get(picked);
264
+ if (!meta) return;
265
+ const OPEN = "Open in the browser";
266
+ const EXPORT = "Save a copy to Downloads";
267
+ const PATH = "Show the file path";
268
+ const DELETE = "Delete";
269
+ const choice = await ctx.ui.select(uiTitle(meta), [OPEN, EXPORT, PATH, DELETE]);
270
+ if (choice === OPEN) {
271
+ await notifyOpened(ctx, viewerPath(root, meta.id), uiTitle(meta));
272
+ } else if (choice === EXPORT) {
273
+ try {
274
+ ctx.ui.notify(`Saved ${tildify(exportArtifact(root, meta, homedir()), homedir())}`, "info");
275
+ } catch (error) {
276
+ ctx.ui.notify(`Could not save a copy: ${(error as Error).message}`, "error");
277
+ }
278
+ } else if (choice === PATH) {
279
+ ctx.ui.notify(pagePath(root, meta.id), "info");
280
+ } else if (choice === DELETE) {
281
+ if (await confirmDelete(ctx, meta)) {
282
+ deleteArtifact(root, meta.id);
283
+ ctx.ui.notify(`Deleted ${uiTitle(meta)}`, "info");
284
+ }
285
+ }
286
+ },
287
+ });
288
+ }