one-code-extension 0.4.1 → 0.5.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 (419) 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 +32 -23
  7. package/docs/guide/doctor.md +20 -13
  8. package/docs/guide/hooks.md +18 -3
  9. package/docs/guide/installation.md +27 -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 +123 -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 +515 -0
  56. package/extensions/background/index.ts +514 -236
  57. package/extensions/background/loop-fire.ts +136 -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 +52 -0
  63. package/extensions/background/wakeup.ts +159 -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 +88 -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 +229 -71
  77. package/extensions/btw/panel.ts +228 -100
  78. package/extensions/btw/prompt.ts +41 -4
  79. package/extensions/claude-compat/index.ts +7 -11
  80. package/extensions/claude-context/index.ts +446 -58
  81. package/extensions/compaction/cut.ts +157 -0
  82. package/extensions/compaction/index.ts +216 -75
  83. package/extensions/compaction/kept-thinking.ts +62 -0
  84. package/extensions/compaction/prompt.ts +15 -12
  85. package/extensions/context-budget/index.ts +24 -5
  86. package/extensions/context-management/index.ts +25 -0
  87. package/extensions/doctor/build.ts +59 -8
  88. package/extensions/doctor/builtins.ts +84 -0
  89. package/extensions/doctor/cli.ts +7 -1
  90. package/extensions/doctor/compat.ts +88 -23
  91. package/extensions/doctor/dependencies.ts +41 -5
  92. package/extensions/doctor/fix-prompt.ts +9 -2
  93. package/extensions/doctor/index.ts +102 -58
  94. package/extensions/doctor/models.ts +102 -63
  95. package/extensions/doctor/presets.ts +42 -27
  96. package/extensions/doctor/report.ts +19 -2
  97. package/extensions/doctor/viewer.ts +49 -27
  98. package/extensions/effort/index.ts +72 -19
  99. package/extensions/effort/settings.ts +21 -0
  100. package/extensions/effort/slider.ts +24 -20
  101. package/extensions/exit/index.ts +32 -1
  102. package/extensions/file-tracker/index.ts +337 -26
  103. package/extensions/file-tracker/replay.ts +96 -10
  104. package/extensions/file-tracker/restore.ts +63 -0
  105. package/extensions/file-tracker/results.ts +66 -0
  106. package/extensions/file-tracker/shell-reads.ts +211 -0
  107. package/extensions/file-tracker/tracker.ts +32 -15
  108. package/extensions/footer/footer-line.ts +45 -24
  109. package/extensions/footer/index.ts +47 -2
  110. package/extensions/footer/pr.ts +34 -5
  111. package/extensions/hooks/compaction.ts +7 -0
  112. package/extensions/hooks/executor.ts +32 -30
  113. package/extensions/hooks/index.ts +198 -31
  114. package/extensions/hooks/matcher.ts +3 -0
  115. package/extensions/hooks/protocol.ts +41 -3
  116. package/extensions/hooks/settings.ts +50 -19
  117. package/extensions/hooks/subagent-bridge.ts +2 -0
  118. package/extensions/hooks/trust.ts +13 -5
  119. package/extensions/idle-compact/index.ts +119 -0
  120. package/extensions/idle-compact/policy.ts +143 -0
  121. package/extensions/init/index.ts +11 -10
  122. package/extensions/init/prompt.ts +25 -32
  123. package/extensions/interrupted/index.ts +13 -1
  124. package/extensions/kept-thinking/index.ts +27 -0
  125. package/extensions/lib/agent-cron.ts +130 -0
  126. package/extensions/lib/agent-loader.ts +15 -1
  127. package/extensions/lib/agent-view.ts +85 -0
  128. package/extensions/lib/anthropic-payload.ts +82 -1
  129. package/extensions/lib/anthropic-server-call.ts +230 -0
  130. package/extensions/lib/anthropic-server-tools.ts +485 -0
  131. package/extensions/lib/app-launch.mjs +94 -0
  132. package/extensions/lib/argument-hints.ts +190 -0
  133. package/extensions/lib/atomic-write.ts +6 -2
  134. package/extensions/lib/auto-memory.ts +75 -0
  135. package/extensions/lib/bash-parser.ts +84 -0
  136. package/extensions/lib/btw-fork.ts +93 -0
  137. package/extensions/lib/child-extensions.ts +42 -0
  138. package/extensions/lib/child-writes.ts +45 -0
  139. package/extensions/lib/claude-context.ts +515 -100
  140. package/extensions/lib/claude-external-includes.ts +68 -0
  141. package/extensions/lib/claude-id.ts +22 -0
  142. package/extensions/lib/claude-rules.ts +323 -0
  143. package/extensions/lib/claude-settings.ts +76 -4
  144. package/extensions/lib/compaction-boundary.ts +36 -0
  145. package/extensions/lib/compaction-keep.mjs +83 -0
  146. package/extensions/lib/config-mode.ts +112 -0
  147. package/extensions/lib/consent-dialogs.ts +63 -0
  148. package/extensions/lib/consent-preview.ts +5 -1
  149. package/extensions/lib/consent-stores.ts +48 -0
  150. package/extensions/lib/context-facts.ts +78 -0
  151. package/extensions/lib/context-stack.ts +81 -0
  152. package/extensions/lib/deferred.ts +219 -25
  153. package/extensions/lib/dock-rows.ts +60 -0
  154. package/extensions/lib/empty-tool-result.ts +27 -0
  155. package/extensions/lib/environment-block.ts +76 -0
  156. package/extensions/lib/fetch-timeout.ts +24 -6
  157. package/extensions/{system-prompt → lib}/git-status.ts +51 -13
  158. package/extensions/lib/git.ts +75 -13
  159. package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
  160. package/extensions/lib/grammars/README.md +2 -0
  161. package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
  162. package/extensions/lib/guide-docs.ts +45 -0
  163. package/extensions/lib/headless-output.ts +46 -0
  164. package/extensions/lib/hook-gate.ts +7 -7
  165. package/extensions/lib/interrupt.ts +20 -4
  166. package/extensions/lib/key-input.ts +455 -0
  167. package/extensions/lib/keys.ts +14 -1
  168. package/extensions/lib/local-command.ts +47 -13
  169. package/extensions/lib/mcp-overrides.ts +1 -1
  170. package/extensions/lib/mcp-share.ts +46 -0
  171. package/extensions/lib/memory-content.ts +146 -0
  172. package/extensions/lib/memory-index.ts +18 -0
  173. package/extensions/lib/memory.ts +134 -123
  174. package/extensions/lib/model-catalog/huggingface.json +174 -0
  175. package/extensions/lib/model-catalog/models-dev.json +18297 -0
  176. package/extensions/lib/model-catalog/openrouter.json +5252 -0
  177. package/extensions/lib/model-catalog-data.ts +513 -0
  178. package/extensions/lib/model-catalog.ts +462 -0
  179. package/extensions/lib/model-choice-warnings.ts +137 -0
  180. package/extensions/lib/model-pairings.ts +152 -0
  181. package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
  182. package/extensions/lib/model-policy.ts +112 -15
  183. package/extensions/lib/model-tier.ts +283 -355
  184. package/extensions/lib/model-unusable.ts +1 -1
  185. package/extensions/lib/newer-model.ts +100 -0
  186. package/extensions/lib/notices.ts +87 -0
  187. package/extensions/lib/notifications.ts +308 -64
  188. package/extensions/lib/one-code-settings.ts +74 -8
  189. package/extensions/lib/open-browser.ts +63 -0
  190. package/extensions/lib/openrouter-generation.ts +94 -0
  191. package/extensions/lib/operation-deadline.ts +30 -0
  192. package/extensions/lib/original-command.ts +4 -4
  193. package/extensions/lib/package-version.ts +1 -1
  194. package/extensions/lib/panel-focus.ts +134 -0
  195. package/extensions/lib/paths.ts +17 -2
  196. package/extensions/lib/permission-gate.ts +72 -6
  197. package/extensions/lib/persisted-output.ts +60 -8
  198. package/extensions/lib/pi-ai-estimate.ts +19 -3
  199. package/extensions/lib/pi-version.ts +21 -2
  200. package/extensions/lib/plan-mode-channels.ts +20 -5
  201. package/extensions/lib/plugins.ts +26 -17
  202. package/extensions/lib/powershell-parse-script.ts +208 -0
  203. package/extensions/lib/powershell-parser.ts +344 -0
  204. package/extensions/lib/process-tree.ts +131 -8
  205. package/extensions/lib/prompt-options.ts +82 -0
  206. package/extensions/lib/queued-delivery.ts +44 -0
  207. package/extensions/lib/reminders.ts +364 -58
  208. package/extensions/lib/replaced-builtins.mjs +199 -0
  209. package/extensions/lib/replay-call.ts +70 -0
  210. package/extensions/lib/request-replay.ts +439 -0
  211. package/extensions/lib/scratchpad.ts +81 -14
  212. package/extensions/lib/send-now-channel.ts +7 -0
  213. package/extensions/lib/session-model-tier.ts +51 -0
  214. package/extensions/lib/session-work.ts +64 -0
  215. package/extensions/lib/settings-channels.ts +4 -4
  216. package/extensions/lib/shell-exit-semantics.ts +170 -0
  217. package/extensions/lib/shell-quote.ts +11 -0
  218. package/extensions/lib/shell-result.ts +70 -0
  219. package/extensions/lib/shell-spawn.ts +70 -6
  220. package/extensions/lib/shell-tool.ts +75 -25
  221. package/extensions/lib/side-call-cache.ts +124 -0
  222. package/extensions/lib/side-call-run.ts +96 -0
  223. package/extensions/lib/side-call-usage.ts +38 -0
  224. package/extensions/lib/side-call.ts +13 -1
  225. package/extensions/lib/single-flight.ts +18 -0
  226. package/extensions/lib/skill-body.ts +42 -0
  227. package/extensions/lib/skill-scan.ts +63 -24
  228. package/extensions/lib/spill-file.ts +140 -0
  229. package/extensions/lib/subagent-handback.ts +113 -0
  230. package/extensions/lib/system-role.ts +156 -0
  231. package/extensions/lib/terminal-text.ts +114 -0
  232. package/extensions/lib/text-width.ts +47 -16
  233. package/extensions/lib/timer-ops.ts +20 -0
  234. package/extensions/lib/tool-additions.ts +149 -0
  235. package/extensions/lib/tool-call-corruption.ts +134 -0
  236. package/extensions/lib/tool-path.ts +66 -0
  237. package/extensions/lib/tool-variants.ts +69 -0
  238. package/extensions/lib/tui-render.ts +255 -27
  239. package/extensions/lib/turn-budget-layout.ts +143 -0
  240. package/extensions/lib/update-check.mjs +15 -2
  241. package/extensions/lib/usage-bus.ts +1 -1
  242. package/extensions/lib/user-denial.ts +38 -0
  243. package/extensions/lib/workspace-channel.ts +16 -0
  244. package/extensions/lib/worktree-channel.ts +31 -3
  245. package/extensions/lib/worktree-isolation.ts +40 -10
  246. package/extensions/lsp/client.ts +8 -9
  247. package/extensions/lsp/index.ts +70 -4
  248. package/extensions/lsp/keep-alive.ts +1 -1
  249. package/extensions/lsp/trust.ts +119 -0
  250. package/extensions/lsp/watcher.ts +1 -1
  251. package/extensions/mcp/announce.ts +87 -5
  252. package/extensions/mcp/client.ts +161 -48
  253. package/extensions/mcp/config.ts +132 -8
  254. package/extensions/mcp/discovery.ts +19 -0
  255. package/extensions/mcp/index.ts +209 -71
  256. package/extensions/mcp/oauth/browser.ts +38 -17
  257. package/extensions/mcp/oauth/flow.ts +25 -17
  258. package/extensions/mcp/oauth/provider.ts +14 -8
  259. package/extensions/mcp/oauth/store.ts +34 -16
  260. package/extensions/mcp/panel/keys.ts +12 -11
  261. package/extensions/mcp/panel/render.ts +9 -6
  262. package/extensions/mcp/schema.ts +104 -5
  263. package/extensions/mcp/trust.ts +151 -67
  264. package/extensions/memory/entries.ts +23 -14
  265. package/extensions/memory/index.ts +134 -28
  266. package/extensions/memory/open-external.ts +33 -18
  267. package/extensions/memory/panel.ts +135 -26
  268. package/extensions/newer-model/index.ts +31 -0
  269. package/extensions/notebook/description.ts +18 -0
  270. package/extensions/notebook/index.ts +6 -5
  271. package/extensions/notebook/notebook.ts +1 -1
  272. package/extensions/permissions/index.ts +1300 -381
  273. package/extensions/permissions/matcher.ts +425 -74
  274. package/extensions/permissions/modes.ts +6 -4
  275. package/extensions/permissions/panel/host.ts +130 -0
  276. package/extensions/permissions/panel/keys.ts +48 -0
  277. package/extensions/permissions/panel/render.ts +450 -0
  278. package/extensions/permissions/panel/state.ts +550 -0
  279. package/extensions/permissions/powershell-cmdlets.ts +198 -0
  280. package/extensions/permissions/powershell-paths.ts +197 -0
  281. package/extensions/permissions/powershell-rules.ts +207 -280
  282. package/extensions/permissions/powershell-tree.ts +557 -0
  283. package/extensions/permissions/project-trust.ts +27 -11
  284. package/extensions/permissions/protected-paths.ts +14 -2
  285. package/extensions/permissions/session-grant.ts +6 -0
  286. package/extensions/permissions/settings.ts +166 -31
  287. package/extensions/permissions/subagent-gate.ts +25 -0
  288. package/extensions/permissions/workspace.ts +78 -0
  289. package/extensions/plan-mode/index.ts +156 -69
  290. package/extensions/plan-mode/reminder.ts +89 -21
  291. package/extensions/plan-mode/texts.ts +177 -0
  292. package/extensions/plan-mode/viewer.ts +92 -27
  293. package/extensions/plugins/counts.ts +3 -3
  294. package/extensions/plugins/index.ts +61 -46
  295. package/extensions/plugins/install/install.ts +16 -0
  296. package/extensions/plugins/marketplace/sync.ts +9 -3
  297. package/extensions/plugins/marketplace/types.ts +3 -1
  298. package/extensions/plugins/panel/keys.ts +21 -24
  299. package/extensions/plugins/panel/render.ts +12 -6
  300. package/extensions/plugins/shell-expand.ts +107 -0
  301. package/extensions/plugins/template.ts +32 -15
  302. package/extensions/powershell/child.ts +7 -0
  303. package/extensions/powershell/description.ts +38 -6
  304. package/extensions/powershell/guards.ts +1 -1
  305. package/extensions/powershell/index.ts +12 -7
  306. package/extensions/recap/index.ts +65 -60
  307. package/extensions/recap/prompt.ts +2 -2
  308. package/extensions/recap/scheduler.ts +3 -5
  309. package/extensions/search-tools/grep-merge.ts +39 -0
  310. package/extensions/search-tools/index.ts +0 -0
  311. package/extensions/send-now/chord.ts +62 -0
  312. package/extensions/send-now/index.ts +133 -0
  313. package/extensions/session-title/index.ts +3 -0
  314. package/extensions/session-title/title.ts +4 -1
  315. package/extensions/skill/code-review.ts +97 -0
  316. package/extensions/skill/index.ts +421 -101
  317. package/extensions/skill/invoke.ts +128 -18
  318. package/extensions/skill/listing.ts +133 -9
  319. package/extensions/skill/panel/keys.ts +15 -16
  320. package/extensions/skill/panel/render.ts +5 -2
  321. package/extensions/skill/panel/state.ts +12 -1
  322. package/extensions/subagents/agent-args.ts +39 -0
  323. package/extensions/subagents/agent-description.ts +132 -0
  324. package/extensions/subagents/agents.ts +116 -23
  325. package/extensions/subagents/announce.ts +69 -0
  326. package/extensions/subagents/default-model.ts +2 -3
  327. package/extensions/subagents/delegation-steer.ts +4 -4
  328. package/extensions/subagents/fork-cache.ts +76 -0
  329. package/extensions/subagents/guide-agent.ts +189 -0
  330. package/extensions/subagents/hand-back-review.ts +3 -0
  331. package/extensions/subagents/index.ts +1493 -824
  332. package/extensions/subagents/live-runs.ts +26 -6
  333. package/extensions/subagents/model-select.ts +38 -25
  334. package/extensions/subagents/outcome.ts +2 -2
  335. package/extensions/subagents/panel-keys.ts +73 -31
  336. package/extensions/subagents/panel-render.ts +120 -96
  337. package/extensions/subagents/panel-widget.ts +60 -89
  338. package/extensions/subagents/runner.ts +108 -80
  339. package/extensions/subagents/runs.ts +39 -5
  340. package/extensions/subagents/send-to-main-tool.ts +1 -1
  341. package/extensions/subagents/session-turns.ts +32 -8
  342. package/extensions/subagents/shell-panel.ts +36 -178
  343. package/extensions/subagents/tasks-dialog.ts +170 -0
  344. package/extensions/subagents/viewport-mirror.ts +95 -0
  345. package/extensions/subagents/worktree.ts +57 -13
  346. package/extensions/system-prompt/environment.ts +19 -19
  347. package/extensions/system-prompt/idle-turn.ts +65 -0
  348. package/extensions/system-prompt/index.ts +131 -47
  349. package/extensions/system-prompt/template.ts +24 -50
  350. package/extensions/system-prompt/tiers/common.ts +23 -64
  351. package/extensions/system-prompt/tiers/frontier.ts +32 -11
  352. package/extensions/system-prompt/tiers/low.ts +67 -45
  353. package/extensions/system-prompt/tiers/mid.ts +96 -38
  354. package/extensions/system-reminder/index.ts +175 -13
  355. package/extensions/tasks/descriptions.ts +190 -0
  356. package/extensions/tasks/index.ts +120 -41
  357. package/extensions/tasks/store.ts +83 -16
  358. package/extensions/tool-call-corruption/child.ts +7 -0
  359. package/extensions/tool-call-corruption/index.ts +237 -0
  360. package/extensions/tool-search/announce.ts +16 -0
  361. package/extensions/tool-search/index.ts +189 -41
  362. package/extensions/tool-style/index.ts +68 -10
  363. package/extensions/tool-style/write-description.ts +40 -0
  364. package/extensions/tool-style/write-render.ts +237 -0
  365. package/extensions/turn-duration/index.ts +7 -3
  366. package/extensions/turn-duration/line.ts +47 -4
  367. package/extensions/web/backends.ts +6 -9
  368. package/extensions/web/description.ts +50 -0
  369. package/extensions/web/index.ts +118 -53
  370. package/extensions/web/native-call.ts +15 -0
  371. package/extensions/web/native-results.ts +41 -0
  372. package/extensions/web-fetch/description.ts +42 -0
  373. package/extensions/web-fetch/extract.ts +53 -6
  374. package/extensions/web-fetch/index.ts +125 -76
  375. package/extensions/web-fetch/page-content.ts +19 -0
  376. package/extensions/web-fetch/response.ts +49 -0
  377. package/extensions/web-fetch/summarize.ts +9 -10
  378. package/extensions/workflow/agent-session.ts +105 -18
  379. package/extensions/workflow/args.ts +24 -0
  380. package/extensions/workflow/description.ts +67 -0
  381. package/extensions/workflow/globals.ts +17 -3
  382. package/extensions/workflow/index.ts +145 -162
  383. package/extensions/workflow/records.ts +1 -0
  384. package/extensions/workflow/run-manager.ts +29 -6
  385. package/extensions/workflow/saved-workflows.ts +3 -2
  386. package/extensions/workflow/types.ts +22 -1
  387. package/extensions/workflow/viewer.ts +54 -24
  388. package/extensions/workflow/widget.ts +20 -0
  389. package/extensions/worktree/descriptions.ts +91 -0
  390. package/extensions/worktree/guards.ts +316 -54
  391. package/extensions/worktree/index.ts +121 -39
  392. package/extensions/worktree/powershell-guards.ts +167 -0
  393. package/extensions/worktree/rewrite.ts +14 -16
  394. package/package.json +18 -7
  395. package/skills/artifact-design/SKILL.md +85 -0
  396. package/skills/code-review/SKILL.md +20 -20
  397. package/skills/code-review/cells/arguments-and-flags.md +19 -0
  398. package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
  399. package/skills/code-review/cells/high-8-inline.md +122 -0
  400. package/skills/code-review/cells/low-1-pass-max4.md +27 -0
  401. package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
  402. package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
  403. package/skills/code-review/cells/medium-3x5-verify.md +132 -0
  404. package/skills/code-review/cells/minimal-prompt.md +32 -0
  405. package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
  406. package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
  407. package/skills/fewer-permission-prompts/SKILL.md +1 -1
  408. package/skills/loop/SKILL.md +11 -0
  409. package/skills/security-review/SKILL.md +2 -5
  410. package/skills/simplify/SKILL.md +7 -5
  411. package/skills/workflow-authoring/SKILL.md +143 -0
  412. package/themes/onecode-light.json +1 -1
  413. package/themes/onecode.json +1 -1
  414. package/types/app.d.ts +58 -0
  415. package/types/community-packages.d.ts +26 -1
  416. package/extensions/auto-mode/recoverability.ts +0 -229
  417. package/extensions/lib/capability-index.ts +0 -466
  418. package/extensions/lib/model-facts.generated.json +0 -2675
  419. package/extensions/lib/model-facts.ts +0 -162
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * One Code's own settings files — the writable half of "own state, borrowed
3
- * config" (docs/decisions/memory-state.md). `~/.claude` is Claude Code's
3
+ * config" (working-docs/decisions/memory-state.md). `~/.claude` is Claude Code's
4
4
  * directory and One Code only ever *reads* it; anything One Code persists that
5
- * looks like a settings key (its own `subagentModel` / `autoMode.classifierModel`,
6
- * the allow rules `/allow` records) lands here instead, so One Code never mutates
5
+ * looks like a settings key (its own `subagentModel` or the allow rules `/allow`
6
+ * records) lands here instead, so One Code never mutates
7
7
  * Claude Code's config and a value that means nothing to Claude Code (a model it
8
8
  * cannot run, say) never ends up in Claude Code's file.
9
9
  *
@@ -22,23 +22,43 @@
22
22
 
23
23
  import { existsSync, readFileSync } from "node:fs";
24
24
  import { join } from "node:path";
25
- import { writeJsonAtomic } from "./atomic-write.ts";
25
+ import { readJsonFile, writeJsonAtomic } from "./atomic-write.ts";
26
26
  import { findProjectRoot } from "./git.ts";
27
27
  import { projectSlug } from "./memory.ts";
28
- import { oneCodeStateDir } from "./paths.ts";
28
+ import { oneCodeStateDir, tryRealpath } from "./paths.ts";
29
29
 
30
30
  /** `~/.onecode/settings.json` — One Code's user-scope settings (writable). */
31
31
  export function oneCodeSettingsPath(home: string, env: NodeJS.ProcessEnv = process.env): string {
32
32
  return join(oneCodeStateDir(env, home), "settings.json");
33
33
  }
34
34
 
35
+ /**
36
+ * The project a per-repo setting belongs to: the git repository root when
37
+ * there is one (shared by worktrees and subdirectories), else the cwd, through
38
+ * symlinks so an aliased checkout is the same project.
39
+ */
40
+ export function oneCodeProjectRoot(cwd: string): string {
41
+ const root = findProjectRoot(cwd) ?? cwd;
42
+ return tryRealpath(root) ?? root;
43
+ }
44
+
35
45
  /**
36
46
  * `~/.onecode/projects/<slug>/settings.json` — One Code's per-repo settings
37
- * (writable). Keyed by the git repository root when there is one (shared by
38
- * worktrees and subdirectories), else the cwd — the same slug the memory dir uses.
47
+ * (writable), for `oneCodeProjectRoot`. The slug is the memory dir's
48
+ * (`projectSlug`), but of the resolved root, where the memory dir slugs the
49
+ * root as found. It is lossy (`my_app` and `my-app` share a file), so a setting
50
+ * that grants trust records the root it was given for (claude-external-includes.ts).
39
51
  */
40
52
  export function oneCodeProjectSettingsPath(cwd: string, home: string, env: NodeJS.ProcessEnv = process.env): string {
41
- return join(oneCodeStateDir(env, home), "projects", projectSlug(findProjectRoot(cwd) ?? cwd), "settings.json");
53
+ return oneCodeProjectSettingsPathFor(oneCodeProjectRoot(cwd), home, env);
54
+ }
55
+
56
+ /** Resolve a project settings path without moving existing permission or trust state. */
57
+ export function oneCodeProjectSettingsPathFor(root: string, home: string, env: NodeJS.ProcessEnv = process.env): string {
58
+ const projects = join(oneCodeStateDir(env, home), "projects");
59
+ const legacy = join(projects, root.replace(/[^A-Za-z0-9-]/g, "-"), "settings.json");
60
+ if (existsSync(legacy)) return legacy;
61
+ return join(projects, projectSlug(root), "settings.json");
42
62
  }
43
63
 
44
64
  /**
@@ -61,3 +81,49 @@ export function readSettingsForWrite(path: string): Record<string, unknown> {
61
81
  export function writeSettings(path: string, file: Record<string, unknown>): void {
62
82
  writeJsonAtomic(path, file);
63
83
  }
84
+
85
+ /** User-only upgrade advice, enabled unless explicitly disabled in One Code's settings. */
86
+ export function readSuggestNewerModels(home: string, env: NodeJS.ProcessEnv = process.env): boolean {
87
+ const file = readJsonFile<{ suggestNewerModels?: unknown }>(oneCodeSettingsPath(home, env));
88
+ return file?.suggestNewerModels !== false;
89
+ }
90
+
91
+ /** Claude Code's `workflowSizeGuideline` values: under 5, 10 or 50 agents, or no guideline. */
92
+ export type WorkflowSizeGuideline = "small" | "medium" | "large" | "unrestricted";
93
+
94
+ const WORKFLOW_SIZES: readonly WorkflowSizeGuideline[] = ["small", "medium", "large", "unrestricted"];
95
+
96
+ export interface WorkflowSettings {
97
+ /** Whether the workflow tool is offered at all. */
98
+ enabled: boolean;
99
+ sizeGuideline: WorkflowSizeGuideline;
100
+ /** True when a settings file set the guideline (the description says "configured"). */
101
+ sizeConfigured: boolean;
102
+ }
103
+
104
+ interface WorkflowSettingsFile {
105
+ enableWorkflows?: unknown;
106
+ disableWorkflows?: unknown;
107
+ workflowSizeGuideline?: unknown;
108
+ }
109
+
110
+ /**
111
+ * The workflow settings Claude Code reads, from One Code's own files (user,
112
+ * then the project file, which wins): `enableWorkflows` (default true),
113
+ * `disableWorkflows` (true turns workflows off whatever `enableWorkflows`
114
+ * says, as in Claude Code) and `workflowSizeGuideline` (default "medium"; an
115
+ * unknown value is ignored). Read leniently: a malformed file is skipped.
116
+ */
117
+ export function readWorkflowSettings(cwd: string, home: string, env: NodeJS.ProcessEnv = process.env): WorkflowSettings {
118
+ const files = [oneCodeSettingsPath(home, env), oneCodeProjectSettingsPath(cwd, home, env)].map((path) => readJsonFile<WorkflowSettingsFile>(path));
119
+ let enable = true;
120
+ let disable = false;
121
+ let size: WorkflowSizeGuideline | undefined;
122
+ for (const file of files) {
123
+ if (!file || typeof file !== "object") continue;
124
+ if (typeof file.enableWorkflows === "boolean") enable = file.enableWorkflows;
125
+ if (file.disableWorkflows === true) disable = true;
126
+ if (WORKFLOW_SIZES.includes(file.workflowSizeGuideline as WorkflowSizeGuideline)) size = file.workflowSizeGuideline as WorkflowSizeGuideline;
127
+ }
128
+ return { enabled: enable && !disable, sizeGuideline: size ?? "medium", sizeConfigured: size !== undefined };
129
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Hand a URL to the desktop's default handler (no dependency, a platform
3
+ * command). Shared by the MCP sign-in flow (`mcp/oauth/browser.ts`, which also
4
+ * screens the URL it is given) and the artifact viewer (`extensions/artifacts`,
5
+ * which only ever opens `file:` URLs it built itself).
6
+ *
7
+ * The child is detached and unref'd once it starts, so the caller never
8
+ * blocks on the browser process. A launch failure resolves to false: a box
9
+ * with no browser falls back to printing the target. On Windows the URL never passes
10
+ * through `cmd.exe`: `cmd /c start "" <url>` cut a URL at its first `&` and
11
+ * handed the rest to cmd's parser as further commands. `rundll32
12
+ * url.dll,FileProtocolHandler` takes the URL as one argument, by its System32
13
+ * path so no PATH entry can stand in for it.
14
+ */
15
+
16
+ import { spawn } from "node:child_process";
17
+ import { system32Path } from "./paths.ts";
18
+
19
+ export interface OpenerPlan {
20
+ command: string;
21
+ args: string[];
22
+ }
23
+
24
+ /** The command and argv that open `url` on `platform`. Pure. */
25
+ export function openerPlan(url: string, platform: NodeJS.Platform = process.platform, env: Record<string, string | undefined> = process.env): OpenerPlan {
26
+ if (platform === "darwin") return { command: "open", args: [url] };
27
+ if (platform === "win32") return { command: system32Path("rundll32.exe", env), args: ["url.dll,FileProtocolHandler", url] };
28
+ return { command: "xdg-open", args: [url] };
29
+ }
30
+
31
+ /**
32
+ * Why a browser opened from here would not reach the user, or undefined when
33
+ * it would. Over SSH the opener runs on the remote machine's desktop, and a
34
+ * Linux or BSD box with neither X nor Wayland has no desktop at all. Pure.
35
+ */
36
+ export function noDisplayReason(platform: NodeJS.Platform = process.platform, env: Record<string, string | undefined> = process.env): string | undefined {
37
+ if (env.SSH_CONNECTION || env.SSH_TTY) return "this session runs over SSH";
38
+ if (platform !== "darwin" && platform !== "win32" && !env.DISPLAY && !env.WAYLAND_DISPLAY) return "no graphical display is available";
39
+ return undefined;
40
+ }
41
+
42
+ /**
43
+ * Start `plan` detached. Resolves true once the opener has started, false when
44
+ * it could not: a missing `xdg-open` is reported by an `error` event after
45
+ * `spawn` returns, so success waits for the `spawn` event. The child stays
46
+ * ref'd until then, so a one-shot run cannot exit mid-launch.
47
+ */
48
+ export function launchOpener(plan: OpenerPlan): Promise<boolean> {
49
+ return new Promise((resolve) => {
50
+ let child: ReturnType<typeof spawn>;
51
+ try {
52
+ child = spawn(plan.command, plan.args, { stdio: "ignore", detached: true });
53
+ } catch {
54
+ resolve(false);
55
+ return;
56
+ }
57
+ child.once("error", () => resolve(false));
58
+ child.once("spawn", () => {
59
+ child.unref();
60
+ resolve(true);
61
+ });
62
+ });
63
+ }
@@ -0,0 +1,94 @@
1
+ /** A best-effort attribution lookup, bounded across auth, retries, and response-body reads. */
2
+ export async function openRouterProviderName(
3
+ generationId: string | undefined,
4
+ getApiKey: () => Promise<string | undefined>,
5
+ signal: AbortSignal,
6
+ ): Promise<string | undefined> {
7
+ if (!generationId || signal.aborted) return;
8
+ const controller = new AbortController();
9
+ const cancel = () => controller.abort();
10
+ signal.addEventListener("abort", cancel, { once: true });
11
+ const timeout = setTimeout(cancel, 8_000);
12
+ let retryTimer: ReturnType<typeof setTimeout> | undefined;
13
+ let releaseRetry: (() => void) | undefined;
14
+ const stopped = new Promise<undefined>((resolve) => {
15
+ controller.signal.addEventListener("abort", () => {
16
+ clearTimeout(retryTimer);
17
+ releaseRetry?.();
18
+ resolve(undefined);
19
+ }, { once: true });
20
+ });
21
+ const lookup = async (): Promise<string | undefined> => {
22
+ let apiKey: string | undefined;
23
+ try {
24
+ apiKey = await getApiKey();
25
+ } catch {
26
+ return;
27
+ }
28
+ if (!apiKey || controller.signal.aborted) return;
29
+ while (!controller.signal.aborted) {
30
+ try {
31
+ const response = await fetch(`https://openrouter.ai/api/v1/generation?id=${encodeURIComponent(generationId)}`, {
32
+ headers: { Authorization: `Bearer ${apiKey}` },
33
+ signal: controller.signal,
34
+ });
35
+ if (controller.signal.aborted) return;
36
+ if (response.ok) {
37
+ const body = await response.json() as { data?: { provider_name?: unknown } } | null;
38
+ const provider = body?.data?.provider_name;
39
+ if (typeof provider === "string" && provider.trim()) return provider.trim();
40
+ } else {
41
+ await response.body?.cancel();
42
+ if (response.status === 401 || response.status === 403) return;
43
+ }
44
+ } catch {
45
+ // Generation metadata can lag or fail independently of the completed model call.
46
+ }
47
+ if (controller.signal.aborted) return;
48
+ await new Promise<void>((resolve) => {
49
+ releaseRetry = resolve;
50
+ retryTimer = setTimeout(resolve, 1_000);
51
+ });
52
+ }
53
+ };
54
+ try {
55
+ return await Promise.race([lookup(), stopped]);
56
+ } finally {
57
+ clearTimeout(timeout);
58
+ signal.removeEventListener("abort", cancel);
59
+ controller.abort();
60
+ }
61
+ }
62
+
63
+ /** Per model object: the stream detector asks on every chunk of every provider. */
64
+ const openRouterByModel = new WeakMap<object, boolean>();
65
+
66
+ export function isOpenRouter(model: { provider: string; baseUrl?: string } | undefined): boolean {
67
+ if (!model) return false;
68
+ if (model.provider === "openrouter") return true;
69
+ const known = openRouterByModel.get(model);
70
+ if (known !== undefined) return known;
71
+ let result = false;
72
+ try {
73
+ const hostname = new URL(model.baseUrl ?? "").hostname;
74
+ result = hostname === "openrouter.ai" || hostname.endsWith(".openrouter.ai");
75
+ } catch {
76
+ // No or malformed base URL: not OpenRouter.
77
+ }
78
+ openRouterByModel.set(model, result);
79
+ return result;
80
+ }
81
+
82
+ /** How the upstream provider failed: a blocked call, a call cut off mid-stream, or a stream gone silent. */
83
+ export type UpstreamFailure = "blocked-call" | "corrupt-stream" | { stalledSeconds: number };
84
+
85
+ export function toolCallCorruptionNotice(modelId: string, providerName: string | undefined, failure: UpstreamFailure = "blocked-call"): string {
86
+ const stalled = typeof failure === "object";
87
+ const what = stalled ? `stopped streaming for ${modelId} (no data for ${failure.stalledSeconds} s)` : `is corrupting tool calls for ${modelId}`;
88
+ const provider = providerName
89
+ ? `OpenRouter upstream provider ${providerName} ${what}.`
90
+ : `An OpenRouter upstream provider ${what}; the provider could not be identified.`;
91
+ const outcome = failure === "blocked-call" ? "The call was blocked and the turn stopped." : stalled ? "The turn was stopped." : "The reply was cut off and the turn stopped.";
92
+ const ignore = JSON.stringify([providerName ?? "PROVIDER_NAME"]);
93
+ return `${provider} ${outcome} Add the provider to compat.openRouterRouting.ignore for this model in pi's models.json ("ignore": ${ignore}), or switch models.`;
94
+ }
@@ -0,0 +1,30 @@
1
+ /** Bound an awaited operation, including dependencies that do not accept an abort signal. */
2
+ export async function runWithDeadline<T>(
3
+ call: (signal: AbortSignal) => Promise<T>,
4
+ options: { signal?: AbortSignal; timeoutMs: number; message: string },
5
+ ): Promise<T> {
6
+ options.signal?.throwIfAborted();
7
+ const controller = new AbortController();
8
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
9
+ // A one-shot process must live until the awaited operation settles.
10
+ const timer = setTimeout(() => controller.abort(new Error(options.message)), options.timeoutMs);
11
+ let rejectAbort: () => void = () => {};
12
+ const aborted = new Promise<never>((_, reject) => {
13
+ rejectAbort = () => reject(signal.reason);
14
+ signal.addEventListener("abort", rejectAbort, { once: true });
15
+ });
16
+ try {
17
+ const result = await Promise.race([
18
+ Promise.resolve().then(() => {
19
+ signal.throwIfAborted();
20
+ return call(signal);
21
+ }),
22
+ aborted,
23
+ ]);
24
+ signal.throwIfAborted();
25
+ return result;
26
+ } finally {
27
+ clearTimeout(timer);
28
+ signal.removeEventListener("abort", rejectAbort);
29
+ }
30
+ }
@@ -22,10 +22,10 @@ export interface OriginalCommandRecord {
22
22
  toolCallId: string;
23
23
  command: string;
24
24
  /**
25
- * The directory the wrapper `cd`s into (the worktree). The pre-gate and the
26
- * recoverability judge run on the original command against THIS cwd, so a
27
- * worktree session keeps the containment fast path and the prompt shows the
28
- * command the model wrote, not `cd '…' && (…)` (PERMISSIONS-REVIEW-2026-09-05 L3).
25
+ * The directory the wrapper `cd`s into (the worktree). The pre-gate runs on
26
+ * the original command against THIS cwd, so a worktree session keeps the
27
+ * containment fast path and the prompt shows the command the model wrote,
28
+ * not `cd '…' && (…)` (PERMISSIONS-REVIEW-2026-09-05 L3).
29
29
  */
30
30
  cwd?: string;
31
31
  }
@@ -3,7 +3,7 @@
3
3
  * banner never hardcodes (and drifts from) the released version.
4
4
  *
5
5
  * The bundled app sets CC_VERSION to the app package version, which is kept in
6
- * lockstep with this package (docs/decisions/distribution.md). When the
6
+ * lockstep with this package (working-docs/decisions/distribution.md). When the
7
7
  * extension is loaded directly via plain `pi` there is no CC_VERSION, so this
8
8
  * reads the extension's own package.json instead of falling back to a stale
9
9
  * literal.
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Which One Code panels hold the keyboard, over the event bus (jiti modules
3
+ * cannot share state), so a permission prompt waits for a panel to close
4
+ * instead of taking its place. Pure: no pi imports.
5
+ *
6
+ * pi shows a dialog (`ctx.ui.select`, `input`, `confirm`) in the slot a
7
+ * `ctx.ui.custom` panel occupies: the dialog replaces the panel and takes its
8
+ * focus, the panel never comes back, and an Enter typed for the panel answers
9
+ * the dialog. Claude Code holds a permission dialog while a command's panel
10
+ * is open. So every panel goes through `panelFactory`, which reports when its
11
+ * component gains and loses focus, and the permissions extension queues its
12
+ * prompts until no panel is focused (`PanelFocus.whenClear`).
13
+ *
14
+ * Focus is read from pi-tui's own `focused` flag, which `setFocus` sets on a
15
+ * component that has the property: a panel that something else displaced
16
+ * reports itself gone at once, so a prompt can never wait on a panel that is
17
+ * no longer on screen.
18
+ */
19
+
20
+ export const PANEL_FOCUS_CHANNEL = "one-code:panel-focus";
21
+
22
+ /** One panel's focus change; `id` is an object unique to the panel. */
23
+ export interface PanelFocusEvent {
24
+ id: object;
25
+ focused: boolean;
26
+ }
27
+
28
+ interface Emitter {
29
+ emit(channel: string, data: unknown): void;
30
+ }
31
+
32
+ function findDescriptor(target: object, key: string): PropertyDescriptor | undefined {
33
+ for (let at: object | null = target; at; at = Object.getPrototypeOf(at)) {
34
+ const found = Object.getOwnPropertyDescriptor(at, key);
35
+ if (found) return found;
36
+ }
37
+ return undefined;
38
+ }
39
+
40
+ /** Report every write to the component's `focused` flag, keeping whatever the component does with it. */
41
+ function watchFocus(component: object, onChange: (focused: boolean) => void): void {
42
+ const own = findDescriptor(component, "focused");
43
+ let value: unknown = own && "value" in own ? own.value : undefined;
44
+ Object.defineProperty(component, "focused", {
45
+ configurable: true,
46
+ enumerable: true,
47
+ get: () => (own?.get ? own.get.call(component) : value),
48
+ set: (next: unknown) => {
49
+ if (own?.set) own.set.call(component, next);
50
+ else value = next;
51
+ onChange(Boolean(next));
52
+ },
53
+ });
54
+ }
55
+
56
+ /**
57
+ * A `ctx.ui.custom` factory that reports its panel's focus on
58
+ * `PANEL_FOCUS_CHANNEL`: `ctx.ui.custom(panelFactory(pi.events, (tui, theme,
59
+ * keybindings, done) => …))`. The panel counts as gone when it loses focus or
60
+ * calls `done`.
61
+ */
62
+ export function panelFactory<A extends unknown[], C>(events: Emitter, factory: (...args: A) => C): (...args: A) => C {
63
+ return (...args: A): C => {
64
+ const id = {};
65
+ const report = (focused: boolean) => events.emit(PANEL_FOCUS_CHANNEL, { id, focused } satisfies PanelFocusEvent);
66
+ const done = args[3];
67
+ if (typeof done === "function") {
68
+ args[3] = ((...result: unknown[]) => {
69
+ report(false);
70
+ return done(...result);
71
+ }) as A[3];
72
+ }
73
+ const watch = (component: unknown) => {
74
+ if (component && typeof component === "object") watchFocus(component, report);
75
+ return component;
76
+ };
77
+ const made = factory(...args);
78
+ return (made instanceof Promise ? made.then(watch) : watch(made)) as C;
79
+ };
80
+ }
81
+
82
+ /**
83
+ * `ctx.ui` with a `custom` whose panels report their focus:
84
+ * `panelUi(pi.events, ctx.ui).custom<T>((tui, theme, keybindings, done) => …)`.
85
+ */
86
+ export function panelUi<U extends { custom: (...args: any[]) => any }>(events: Emitter, ui: U): Pick<U, "custom"> {
87
+ const custom = (factory: (...args: unknown[]) => unknown, ...rest: unknown[]) => ui.custom(panelFactory(events, factory), ...rest);
88
+ return { custom: custom as U["custom"] };
89
+ }
90
+
91
+ /** The focused panels, fed from `PANEL_FOCUS_CHANNEL`. */
92
+ export class PanelFocus {
93
+ private readonly focused = new Set<object>();
94
+ private waiters = new Set<() => void>();
95
+
96
+ note(data: unknown): void {
97
+ const event = data as Partial<PanelFocusEvent> | undefined;
98
+ if (!event || typeof event.id !== "object" || event.id === null) return;
99
+ if (event.focused) this.focused.add(event.id);
100
+ else this.focused.delete(event.id);
101
+ if (this.focused.size === 0) this.release();
102
+ }
103
+
104
+ /** True while a panel holds the keyboard. */
105
+ get busy(): boolean {
106
+ return this.focused.size > 0;
107
+ }
108
+
109
+ /** Resolves once no panel is focused, or when `signal` aborts. */
110
+ whenClear(signal?: AbortSignal): Promise<void> {
111
+ if (!this.busy || signal?.aborted) return Promise.resolve();
112
+ return new Promise((resolve) => {
113
+ const finish = () => {
114
+ this.waiters.delete(finish);
115
+ signal?.removeEventListener("abort", finish);
116
+ resolve();
117
+ };
118
+ this.waiters.add(finish);
119
+ signal?.addEventListener("abort", finish, { once: true });
120
+ });
121
+ }
122
+
123
+ /** Forget every panel (a new session) and let waiting prompts through. */
124
+ reset(): void {
125
+ this.focused.clear();
126
+ this.release();
127
+ }
128
+
129
+ private release(): void {
130
+ const waiting = this.waiters;
131
+ this.waiters = new Set();
132
+ for (const finish of waiting) finish();
133
+ }
134
+ }
@@ -6,7 +6,7 @@
6
6
  * (settings, skills, agents, plugins, CLAUDE.md); everything One Code
7
7
  * *generates* — plan files, and over time the rest of its state — lands in
8
8
  * One Code's own `~/.onecode`, so neither product's artifacts mingle with the
9
- * other's. See "Own state, borrowed config" in docs/decisions.md.
9
+ * other's. See "Own state, borrowed config" in working-docs/decisions.md.
10
10
  */
11
11
 
12
12
  import { realpathSync } from "node:fs";
@@ -21,6 +21,19 @@ export function claudeConfigDir(env: Record<string, string | undefined> = proces
21
21
  return env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
22
22
  }
23
23
 
24
+ /**
25
+ * Claude Code's organization-managed root: `managed-settings.json`
26
+ * (lib/claude-settings.ts) and the managed CLAUDE.md and rules all live here.
27
+ * On Windows it is `C:\Program Files\ClaudeCode`, the only Windows path the
28
+ * Claude Code binary names (2.1.292); `C:\ProgramData\ClaudeCode` is not read.
29
+ * The Windows policy registry keys are a separate source, not read here.
30
+ */
31
+ export function claudeManagedDir(platform: NodeJS.Platform = process.platform): string {
32
+ if (platform === "darwin") return "/Library/Application Support/ClaudeCode";
33
+ if (platform === "win32") return "C:\\Program Files\\ClaudeCode";
34
+ return "/etc/claude-code";
35
+ }
36
+
24
37
  /**
25
38
  * The user-scope Claude Code dir for a given `home` — `claudeConfigDir` for
26
39
  * callers that thread a `home` (tests, `~`-expansion). `CLAUDE_CONFIG_DIR`
@@ -158,7 +171,9 @@ export function foldsCase(): boolean {
158
171
  * the safety floor — so "under this directory" means one thing everywhere.
159
172
  */
160
173
  export function comparablePath(path: string): string {
161
- const normalized = forwardSlashes(resolve(path)).replace(/\/+$/, "");
174
+ const absolute = resolve(path);
175
+ // Backslashes separate Windows paths, but name literal sibling files on POSIX.
176
+ const normalized = (process.platform === "win32" ? forwardSlashes(absolute) : absolute).replace(/\/+$/, "");
162
177
  return foldsCase() ? normalized.toLowerCase() : normalized;
163
178
  }
164
179
 
@@ -30,11 +30,20 @@
30
30
  */
31
31
 
32
32
  import { getAgentDir, type InlineExtension } from "@earendil-works/pi-coding-agent";
33
+ import { withKeepAlive } from "../lsp/keep-alive.ts";
34
+ import { bashParserReady } from "./bash-parser.ts";
33
35
  import { findProjectRoot } from "./git.ts";
36
+ import { SUBAGENT_HANDBACK } from "./subagent-handback.ts";
37
+ import { autoMemoryEnabled } from "./auto-memory.ts";
34
38
  import { memoryDir } from "./memory.ts";
35
- import { claudeConfigDir, oneCodeStateDir } from "./paths.ts";
39
+ import type { PowerShellParse } from "./powershell-parser.ts";
40
+ import { usesClaudeCodeFastPaths } from "./model-tier.ts";
41
+ import { homedir } from "node:os";
42
+ import { join } from "node:path";
43
+ import { claudeConfigDir, claudeJsonPath, oneCodeStateDir } from "./paths.ts";
36
44
  import { sessionResultsDir } from "./persisted-output.ts";
37
- import { sessionScratchpadDir } from "./scratchpad.ts";
45
+ import { powerShellParser } from "./shell-spawn.ts";
46
+ import { isPrivateScratchpad, sessionScratchpadDir } from "./scratchpad.ts";
38
47
  import { decide, extractSubject, isPathSubjectTool, normalizeToolName, type PermissionMode, parseRules } from "../permissions/matcher.ts";
39
48
  import { projectAllowApproved } from "../permissions/project-trust.ts";
40
49
  import { loadPermissionSettings, normalizePermissionMode } from "../permissions/settings.ts";
@@ -64,8 +73,33 @@ export function runtimeProtectedDirs(): string[] {
64
73
  return [...new Set(roots.flatMap((dir) => [dir, resolvedOrSelf(dir)]))];
65
74
  }
66
75
 
76
+ /**
77
+ * The harness's own secret stores, files and directories, which a read tool
78
+ * outside the working space must not read unclassified: One Code's settings
79
+ * (web search API keys, the model tiers) and MCP OAuth token store, pi's
80
+ * `models.json` (custom-provider `apiKey` values) and `auth.json`, Claude
81
+ * Code's user settings (`env` keys, `apiKeyHelper`) and `.claude.json`. The
82
+ * basename denylist (`auto-mode/sensitive.ts`) cannot name these without
83
+ * flagging every project's `settings.json`. Literal and resolved spelling, as
84
+ * for `runtimeProtectedDirs`; `DecideInput.secretPaths` is the consumer.
85
+ */
86
+ export function runtimeSecretPaths(home: string = homedir()): string[] {
87
+ const stateDir = oneCodeStateDir(process.env, home);
88
+ const agentDir = getAgentDir();
89
+ const paths = [
90
+ join(stateDir, "settings.json"),
91
+ join(stateDir, "mcp-auth"),
92
+ join(agentDir, "models.json"),
93
+ join(agentDir, "auth.json"),
94
+ join(claudeConfigDir(), "settings.json"),
95
+ join(claudeConfigDir(), ".credentials.json"),
96
+ claudeJsonPath(home),
97
+ ];
98
+ return [...new Set(paths.flatMap((path) => [path, resolvedOrSelf(path)]))];
99
+ }
100
+
67
101
  /** Tools the runtime itself injects; never gate them. */
68
- const DEFAULT_INTERNAL_TOOLS = new Set(["structured_output"]);
102
+ const DEFAULT_INTERNAL_TOOLS = new Set(["structured_output", SUBAGENT_HANDBACK]);
69
103
 
70
104
  /** The parent permissions extension publishes its live mode here (in-process children read it). */
71
105
  export const MODE_ENV = "CC_PERMISSION_MODE";
@@ -78,6 +112,24 @@ export function localGateMode(envValue: string | undefined, defaultMode: Permiss
78
112
  return normalizePermissionMode(envValue) ?? defaultMode ?? "acceptEdits";
79
113
  }
80
114
 
115
+ /**
116
+ * PowerShell's own parse of a `powershell` call's command line, for decide(),
117
+ * from the process-wide parse server on the tool's own executable
118
+ * (lib/powershell-parser.ts). Undefined for another tool, in
119
+ * bypassPermissions (nothing is judged), or when the parser cannot answer (no
120
+ * PowerShell, a timeout, a crash, Constrained Language Mode): the gate then
121
+ * clears nothing and the line is classified, prompted or denied. The server's
122
+ * child is unref'd, so the await holds the event loop open itself (a one-shot
123
+ * run would otherwise exit mid-parse).
124
+ */
125
+ export async function parsePowerShell(tool: string, command: string | undefined, mode: PermissionMode): Promise<PowerShellParse | undefined> {
126
+ if (tool !== "powershell" || !command || mode === "bypassPermissions") return undefined;
127
+ const parser = powerShellParser();
128
+ if (!parser) return undefined;
129
+ const outcome = await withKeepAlive(() => parser.parse(command));
130
+ return outcome.ok ? outcome.parse : undefined;
131
+ }
132
+
81
133
  export function permissionGateFactory(
82
134
  cwd: string,
83
135
  home: string,
@@ -105,9 +157,11 @@ export function permissionGateFactory(
105
157
  );
106
158
  // Memory writes work inside agent sessions too — otherwise the protected
107
159
  // `.claude` check turns them into "needs interactive approval" and the
108
- // harness blocks its own feature (same rationale as in decide()).
109
- const memoryDirPath = memoryDir(home, projectRoot);
160
+ // harness blocks its own feature (same rationale as in decide()). With
161
+ // auto-memory off the folder gets no grant, as in the parent's gate.
162
+ const memoryDirPath = autoMemoryEnabled(projectRoot) ? memoryDir(home, projectRoot) : undefined;
110
163
  const protectedDirs = runtimeProtectedDirs();
164
+ const secretPaths = runtimeSecretPaths(home);
111
165
 
112
166
  return {
113
167
  name: "agent-permission-gate",
@@ -117,6 +171,7 @@ export function permissionGateFactory(
117
171
  // until the session runs — derived on first tool call, then pinned.
118
172
  let scratchpadDirPath: string | undefined;
119
173
  pi.on("tool_call", async (event, ctx) => {
174
+ await bashParserReady();
120
175
  if (neverGate.has(event.toolName)) return undefined;
121
176
  const runCwd = ctx?.cwd ?? cwd;
122
177
  const sessionId = ctx?.sessionManager?.getSessionId?.();
@@ -137,13 +192,17 @@ export function permissionGateFactory(
137
192
  cwd: runCwd,
138
193
  signal: ctx?.signal,
139
194
  sessionId,
195
+ model: ctx?.model,
140
196
  });
141
197
  }
142
198
  } catch (error) {
143
199
  return { block: true, reason: `Permission bridge failed (${(error as Error).message}); denied to fail safe.` };
144
200
  }
145
201
 
146
- if (!scratchpadDirPath && sessionId) scratchpadDirPath = sessionScratchpadDir(runCwd, sessionId);
202
+ if (!scratchpadDirPath && sessionId) {
203
+ const candidate = sessionScratchpadDir(runCwd, sessionId);
204
+ if (isPrivateScratchpad(candidate)) scratchpadDirPath = candidate;
205
+ }
147
206
  const tool = normalizeToolName(event.toolName);
148
207
  const subject = extractSubject(tool, event.input as Record<string, unknown>);
149
208
  // Path tools (writers and the read tier) are judged by where the path
@@ -156,6 +215,9 @@ export function permissionGateFactory(
156
215
  // No classifier is reachable without the bridge, so auto mode is judged
157
216
  // as acceptEdits — which decide() confines to the working directory.
158
217
  const mode = liveMode === "auto" ? "acceptEdits" : liveMode;
218
+ // PowerShell is judged from its own parse, as in the parent's gate;
219
+ // without one it clears nothing and the call is denied here.
220
+ const powershellParse = await parsePowerShell(tool, subject, mode);
159
221
  const result = decide({
160
222
  toolName: event.toolName,
161
223
  subject,
@@ -173,6 +235,10 @@ export function permissionGateFactory(
173
235
  // the subject (the tmpdir fallback sits under a symlinked /var on macOS).
174
236
  resultsDirPath: resolvedOrSelf(sessionResultsDir(ctx)),
175
237
  protectedDirs,
238
+ secretPaths,
239
+ claudeCodeFastPaths: usesClaudeCodeFastPaths(ctx?.model),
240
+ blockReadsOutsideWorkingDirectories: settings.blockReadsOutsideWorkingDirectories,
241
+ powershellParse,
176
242
  });
177
243
  if (result.decision === "allow") return undefined;
178
244
  const ruleNote = result.rule ? ` (rule: ${result.rule.raw})` : "";