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
@@ -2,7 +2,8 @@
2
2
  * worktree extension — Claude Code's EnterWorktree/ExitWorktree.
3
3
  *
4
4
  * enter_worktree creates (or switches into) a git worktree under
5
- * `.claude/worktrees/` and "moves" the session there. pi fixes a session's cwd
5
+ * `.claude/worktrees/` (`.onecode/worktrees/` in independent mode) and "moves"
6
+ * the session there. pi fixes a session's cwd
6
7
  * at creation, so the move is enforced on the tool_call hook: bash commands
7
8
  * are prefixed with `cd`, relative paths resolve against the worktree (see
8
9
  * rewrite.ts), and an every-turn reminder keeps the model oriented. State
@@ -12,19 +13,27 @@
12
13
  import { execFile } from "node:child_process";
13
14
  import { randomBytes } from "node:crypto";
14
15
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
15
- import { join } from "node:path";
16
+ import { homedir } from "node:os";
17
+ import { join, resolve } from "node:path";
16
18
  import { promisify } from "node:util";
17
19
  import { StringEnum } from "@earendil-works/pi-ai";
18
20
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
19
21
  import { Type } from "typebox";
20
22
  import { restoreLatestDetails } from "../lib/branch-restore.ts";
23
+ import { checkoutGitRunsProgram } from "../auto-mode/git-checkout-programs.ts";
21
24
  import { DEFER_CHANNEL } from "../lib/deferred.ts";
22
25
  import { REMINDER_CHANNEL } from "../lib/reminders.ts";
26
+ import { absoluteFrom, comparablePath, tryRealpath } from "../lib/paths.ts";
23
27
  import { WORKTREE_CHANNEL, type WorktreeLocation } from "../lib/worktree-channel.ts";
24
- import { worktreeBashGuardReason } from "./guards.ts";
28
+ import { namesGit, unlistedWorktreesReason, worktreeBashGuardReason } from "./guards.ts";
29
+ import { worktreePowershellGuardReason } from "./powershell-guards.ts";
30
+ import { bashParserReady } from "../lib/bash-parser.ts";
25
31
  import { ORIGINAL_COMMAND_CHANNEL, type OriginalCommandRecord } from "../lib/original-command.ts";
32
+ import { enterWorktreeDescription, ENTER_WORKTREE_PARAMS, EXIT_WORKTREE_DESCRIPTION, EXIT_WORKTREE_PARAMS } from "./descriptions.ts";
26
33
  import { rewriteToolInput, validateWorktreeName } from "./rewrite.ts";
27
34
  import { ccToolRenderers } from "../lib/tui-render.ts";
35
+ import { findProjectRoot, HARNESS_GIT_CONFIG, HARNESS_GIT_NO_HOOKS, repositoryWorktrees } from "../lib/git.ts";
36
+ import { projectConfigDir, projectConfigDirName } from "../lib/config-mode.ts";
28
37
 
29
38
  const run = promisify(execFile);
30
39
  const REMINDER_KEY = "cc-worktree-session";
@@ -36,7 +45,7 @@ interface WorktreeState {
36
45
  baseCommit?: string;
37
46
  createdByUs: boolean;
38
47
  originalCwd: string;
39
- /** Root of the shared checkout (`git rev-parse --show-toplevel` at entry). */
48
+ /** Root of the main checkout, even when the session started in a linked worktree. */
40
49
  sharedRoot: string;
41
50
  }
42
51
 
@@ -46,7 +55,7 @@ interface WorktreeDetails {
46
55
  }
47
56
 
48
57
  async function git(args: string[], cwd: string): Promise<string> {
49
- const { stdout } = await run("git", args, { cwd, maxBuffer: 10 * 1024 * 1024 });
58
+ const { stdout } = await run("git", [...HARNESS_GIT_CONFIG, ...HARNESS_GIT_NO_HOOKS, ...args], { cwd, maxBuffer: 10 * 1024 * 1024 });
50
59
  return stdout.trim();
51
60
  }
52
61
 
@@ -58,6 +67,20 @@ async function listWorktreePaths(cwd: string): Promise<string[]> {
58
67
  .map((line) => line.slice("worktree ".length));
59
68
  }
60
69
 
70
+ /**
71
+ * The worktree git-isolation guard, said when the session enters: before it,
72
+ * GPT-6 Sol and Astra both ran a read-only `git -C <main checkout> status`
73
+ * and met the refusal with no warning (2026-10-04 self-test).
74
+ */
75
+ const ISOLATION_NOTE = "Git commands aimed at the main checkout or another worktree of this repository are refused until you exit.";
76
+
77
+ /**
78
+ * Sessions already told their restored worktree is gone, as `<session id>\0<path>`.
79
+ * Module scope on purpose: jiti keeps this module across the factory re-run
80
+ * that `/reload` makes, and a reload restores the same stale state again.
81
+ */
82
+ const reportedMissingWorktrees = new Set<string>();
83
+
61
84
  export default function worktreeExtension(pi: ExtensionAPI) {
62
85
  let state: WorktreeState | undefined;
63
86
 
@@ -66,9 +89,9 @@ export default function worktreeExtension(pi: ExtensionAPI) {
66
89
  `${s.branch ? ` (branch ${s.branch})` : ""}, not in ${s.originalCwd}. ` +
67
90
  "Relative paths and bash commands already run there. Use exit_worktree to leave when the user asks.";
68
91
 
69
- const applyState = (next: WorktreeState | undefined) => {
92
+ const applyState = (next: WorktreeState | undefined, toolCallId?: string) => {
70
93
  state = next;
71
- const location: WorktreeLocation | null = next ? { path: next.path, branch: next.branch } : null;
94
+ const location: WorktreeLocation | null = next ? { path: next.path, branch: next.branch, sharedRoot: next.sharedRoot } : null;
72
95
  pi.events.emit(WORKTREE_CHANNEL, location);
73
96
  if (next) {
74
97
  pi.events.emit(REMINDER_CHANNEL, {
@@ -76,6 +99,7 @@ export default function worktreeExtension(pi: ExtensionAPI) {
76
99
  scope: "every-turn",
77
100
  key: REMINDER_KEY,
78
101
  placement: "sticky-append",
102
+ toolCallId,
79
103
  });
80
104
  } else {
81
105
  pi.events.emit(REMINDER_CHANNEL, { key: REMINDER_KEY, remove: true });
@@ -85,7 +109,18 @@ export default function worktreeExtension(pi: ExtensionAPI) {
85
109
  const reconstructState = (ctx: ExtensionContext) => {
86
110
  const details = restoreLatestDetails<WorktreeDetails>(ctx.sessionManager.getBranch(), WORKTREE_TOOLS, (d) => d?.worktreeState !== undefined);
87
111
  let restored = details?.worktreeState ?? undefined;
88
- if (restored && !existsSync(restored.path)) restored = undefined;
112
+ if (restored && !existsSync(restored.path)) {
113
+ // Nothing records that the session left, so every later restore finds
114
+ // the same stale state; tell the model once per session.
115
+ const key = `${ctx.sessionManager.getSessionId()}\0${restored.path}`;
116
+ if (!reportedMissingWorktrees.has(key)) {
117
+ reportedMissingWorktrees.add(key);
118
+ pi.events.emit(REMINDER_CHANNEL, {
119
+ text: `Left worktree session; back in ${restored.originalCwd}. The worktree at ${restored.path} no longer exists.`,
120
+ });
121
+ }
122
+ restored = undefined;
123
+ }
89
124
  // Sessions persisted before sharedRoot existed: originalCwd is the best guess.
90
125
  if (restored && !restored.sharedRoot) restored = { ...restored, sharedRoot: restored.originalCwd };
91
126
  applyState(restored);
@@ -94,16 +129,38 @@ export default function worktreeExtension(pi: ExtensionAPI) {
94
129
  pi.on("session_start", (_event, ctx) => reconstructState(ctx));
95
130
  pi.on("session_tree", (_event, ctx) => reconstructState(ctx));
96
131
 
97
- pi.on("tool_call", (event) => {
132
+ /** The repository's worktrees for a git command's target check; undefined when git cannot list them. */
133
+ const otherWorktrees = async (command: string, cwd: string): Promise<string[] | undefined> =>
134
+ namesGit(command) ? repositoryWorktrees(cwd) : [];
135
+
136
+ pi.on("tool_call", async (event) => {
98
137
  if (!state) return;
99
- if (["enter_worktree", "exit_worktree", "Agent", "SendMessage", "workflow"].includes(event.toolName)) return;
100
- if (event.toolName === "bash") {
138
+ if (WORKTREE_TOOLS.has(event.toolName)) return;
139
+ if (!existsSync(state.path)) {
140
+ return { block: true, reason: `The active worktree at ${state.path} no longer exists. Use exit_worktree with action: "keep" to return to ${state.originalCwd}, or enter_worktree with path to switch to an existing worktree. No tool has run in the original checkout.` };
141
+ }
142
+ if (["Agent", "SendMessage", "workflow"].includes(event.toolName)) return;
143
+ if (event.toolName === "bash" || event.toolName === "monitor") {
101
144
  // Guard before rewriting (and before the permission prompt — this
102
145
  // extension loads ahead of permissions): git must verifiably target
103
146
  // this worktree, and shared-stash footguns are refused with the recipe.
147
+ // A monitor's command runs through the same bash, so it is judged the same way.
148
+ const command = (event.input as Record<string, unknown>).command;
149
+ if (typeof command === "string") {
150
+ await bashParserReady();
151
+ const others = await otherWorktrees(command, state.path);
152
+ if (others === undefined) return { block: true, reason: unlistedWorktreesReason(state.path) };
153
+ const reason = worktreeBashGuardReason({ command, worktreePath: state.path, sharedRoot: state.sharedRoot, otherWorktrees: others });
154
+ if (reason) return { block: true, reason };
155
+ }
156
+ }
157
+ if (event.toolName === "powershell") {
158
+ // The same invariants for PowerShell, the primary shell on Windows.
104
159
  const command = (event.input as Record<string, unknown>).command;
105
160
  if (typeof command === "string") {
106
- const reason = worktreeBashGuardReason({ command, worktreePath: state.path, sharedRoot: state.sharedRoot });
161
+ const others = await otherWorktrees(command, state.path);
162
+ if (others === undefined) return { block: true, reason: unlistedWorktreesReason(state.path) };
163
+ const reason = worktreePowershellGuardReason({ command, worktreePath: state.path, sharedRoot: state.sharedRoot, otherWorktrees: others });
107
164
  if (reason) return { block: true, reason };
108
165
  }
109
166
  }
@@ -118,15 +175,12 @@ export default function worktreeExtension(pi: ExtensionAPI) {
118
175
  name: "enter_worktree",
119
176
  label: "Enter Worktree",
120
177
  ...ccToolRenderers("Enter Worktree"),
121
- description:
122
- "Create an isolated git worktree under .claude/worktrees/ (branched from the current HEAD) and switch this session into it — subsequent commands and relative paths run there. Use ONLY when the user or project instructions explicitly ask for a worktree. Pass `name` to name the new worktree, or `path` to switch into an existing worktree instead (mutually exclusive). Leave with exit_worktree.",
178
+ description: enterWorktreeDescription(projectConfigDirName()),
123
179
  parameters: Type.Object({
124
- name: Type.Optional(
125
- Type.String({ description: "Name for a new worktree (segments of letters/digits/._-, max 64 chars). Random if omitted" }),
126
- ),
127
- path: Type.Optional(Type.String({ description: "Existing worktree (from `git worktree list`) to switch into instead of creating one" })),
180
+ name: Type.Optional(Type.String({ description: ENTER_WORKTREE_PARAMS.name })),
181
+ path: Type.Optional(Type.String({ description: ENTER_WORKTREE_PARAMS.path })),
128
182
  }),
129
- async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
183
+ async execute(toolCallId, params, _signal, _onUpdate, ctx) {
130
184
  const fail = (text: string) => ({ content: [{ type: "text" as const, text }], details: {} as WorktreeDetails, isError: true });
131
185
 
132
186
  if (params.name && params.path) return fail("Pass either `name` or `path`, not both.");
@@ -139,15 +193,22 @@ export default function worktreeExtension(pi: ExtensionAPI) {
139
193
 
140
194
  let repoRoot: string;
141
195
  try {
142
- repoRoot = await git(["rev-parse", "--show-toplevel"], ctx.cwd);
196
+ // git prints `C:/…` on Windows; resolve gives the native form every path built from it shares.
197
+ repoRoot = resolve(await git(["rev-parse", "--show-toplevel"], ctx.cwd));
143
198
  } catch {
144
199
  return fail("enter_worktree needs a git repository; this directory is not one.");
145
200
  }
146
201
 
202
+ const sharedRoot = findProjectRoot(repoRoot) ?? repoRoot;
147
203
  if (params.path) {
148
204
  const known = await listWorktreePaths(ctx.cwd);
149
- const target = known.find((p) => p === params.path);
205
+ // git lists `C:/…` on Windows, while the model passes the `C:\…` we showed it.
206
+ const requested = absoluteFrom(ctx.cwd, params.path);
207
+ const wanted = comparablePath(tryRealpath(requested) ?? requested);
208
+ const listed = known.find((p) => comparablePath(tryRealpath(p) ?? p) === wanted);
209
+ const target = listed === undefined ? undefined : resolve(listed);
150
210
  if (!target) return fail(`${params.path} is not a worktree of this repository. Known worktrees:\n${known.join("\n")}`);
211
+ if (!existsSync(target)) return fail(`Worktree ${target} no longer exists. Choose an existing worktree from \`git worktree list\`.`);
151
212
  // The branch is display-only here (footer, reminder): a detached HEAD
152
213
  // leaves it unset silently, a failing git is reported in the result text.
153
214
  let branch: string | undefined;
@@ -158,10 +219,10 @@ export default function worktreeExtension(pi: ExtensionAPI) {
158
219
  } catch (error) {
159
220
  branchNote = ` Its branch could not be read (${(error as Error).message.split("\n")[0]}).`;
160
221
  }
161
- const next: WorktreeState = { path: target, branch, createdByUs: false, originalCwd: ctx.cwd, sharedRoot: repoRoot };
162
- applyState(next);
222
+ const next: WorktreeState = { path: target, branch, createdByUs: false, originalCwd: ctx.cwd, sharedRoot };
223
+ applyState(next, toolCallId);
163
224
  return {
164
- content: [{ type: "text", text: `Switched into existing worktree ${target}${branch ? ` (branch ${branch})` : ""}.${branchNote} All work now happens there; exit_worktree returns to ${ctx.cwd}.` }],
225
+ content: [{ type: "text", text: `Switched into existing worktree ${target}${branch ? ` (branch ${branch})` : ""}.${branchNote} All work now happens there; exit_worktree returns to ${ctx.cwd}. ${ISOLATION_NOTE}` }],
165
226
  details: { worktreeState: next } satisfies WorktreeDetails,
166
227
  };
167
228
  }
@@ -177,7 +238,13 @@ export default function worktreeExtension(pi: ExtensionAPI) {
177
238
  const nameError = validateWorktreeName(name);
178
239
  if (nameError) return fail(`Invalid worktree name "${name}": ${nameError}`);
179
240
 
180
- const worktreesDir = join(repoRoot, ".claude", "worktrees");
241
+ // Hooks are disabled by the git wrapper; checkout filters must go through
242
+ // the normal shell permission gate rather than execute implicitly here.
243
+ const program = checkoutGitRunsProgram(repoRoot, homedir(), []);
244
+ if (program) return fail(`Could not create worktree safely: ${program}. Create it with a permission-reviewed \`git worktree add\` command, then enter it with \`path\`.`);
245
+
246
+ // `.onecode/worktrees` in independent mode (lib/config-mode.ts).
247
+ const worktreesDir = join(projectConfigDir(repoRoot), "worktrees");
181
248
  mkdirSync(worktreesDir, { recursive: true });
182
249
  // Self-ignoring directory: worktrees never show up as untracked files.
183
250
  const ignorePath = join(worktreesDir, ".gitignore");
@@ -195,13 +262,13 @@ export default function worktreeExtension(pi: ExtensionAPI) {
195
262
  return fail(`Could not create worktree: ${(error as Error).message}`);
196
263
  }
197
264
 
198
- const next: WorktreeState = { path, branch, baseCommit, createdByUs: true, originalCwd: ctx.cwd, sharedRoot: repoRoot };
199
- applyState(next);
265
+ const next: WorktreeState = { path, branch, baseCommit, createdByUs: true, originalCwd: ctx.cwd, sharedRoot };
266
+ applyState(next, toolCallId);
200
267
  return {
201
268
  content: [
202
269
  {
203
270
  type: "text",
204
- text: `Created worktree ${path} on branch ${branch} (from HEAD ${baseCommit.slice(0, 8)}). All commands and relative paths now run there; exit_worktree returns to ${ctx.cwd}.`,
271
+ text: `Created worktree ${path} on branch ${branch} (from HEAD ${baseCommit.slice(0, 8)}). All commands and relative paths now run there; exit_worktree returns to ${ctx.cwd}. ${ISOLATION_NOTE}`,
205
272
  },
206
273
  ],
207
274
  details: { worktreeState: next } satisfies WorktreeDetails,
@@ -213,11 +280,10 @@ export default function worktreeExtension(pi: ExtensionAPI) {
213
280
  name: "exit_worktree",
214
281
  label: "Exit Worktree",
215
282
  ...ccToolRenderers("Exit Worktree"),
216
- description:
217
- 'End the worktree session started by enter_worktree and return to the original directory. action: "keep" leaves the worktree and branch on disk; "remove" deletes both — refused (listing the changes) if there are uncommitted files or commits not on the original branch, unless discard_changes is true. No-op when no worktree session is active.',
283
+ description: EXIT_WORKTREE_DESCRIPTION,
218
284
  parameters: Type.Object({
219
- action: StringEnum(["keep", "remove"] as const, { description: '"keep" preserves the worktree on disk; "remove" deletes it and its branch' }),
220
- discard_changes: Type.Optional(Type.Boolean({ description: "Required true to remove a worktree with uncommitted files or unmerged commits" })),
285
+ action: StringEnum(["keep", "remove"] as const, { description: EXIT_WORKTREE_PARAMS.action }),
286
+ discard_changes: Type.Optional(Type.Boolean({ description: EXIT_WORKTREE_PARAMS.discard_changes })),
221
287
  }),
222
288
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
223
289
  if (!state) {
@@ -234,7 +300,9 @@ export default function worktreeExtension(pi: ExtensionAPI) {
234
300
  content: [
235
301
  {
236
302
  type: "text",
237
- text: `Left worktree session; back in ${current.originalCwd}. The worktree remains at ${current.path}${current.branch ? ` (branch ${current.branch})` : ""} — re-enter it with enter_worktree {path}.`,
303
+ text: `Left worktree session; back in ${current.originalCwd}. ` + (existsSync(current.path)
304
+ ? `The worktree remains at ${current.path}${current.branch ? ` (branch ${current.branch})` : ""} — re-enter it with enter_worktree (path: "${current.path}").`
305
+ : `The worktree at ${current.path} no longer exists. No branch was removed.`),
238
306
  },
239
307
  ],
240
308
  details: { worktreeState: null } satisfies WorktreeDetails,
@@ -254,11 +322,13 @@ export default function worktreeExtension(pi: ExtensionAPI) {
254
322
  if (!params.discard_changes) {
255
323
  const blockers: string[] = [];
256
324
  try {
257
- const dirty = await git(["status", "--porcelain"], current.path);
258
- if (dirty) blockers.push(`Uncommitted changes:\n${dirty}`);
325
+ const dirty = await git(["status", "--porcelain", "--untracked-files=all"], current.path);
326
+ if (dirty) blockers.push(`Uncommitted files:\n${dirty}`);
259
327
  if (current.baseCommit && current.branch) {
260
- const ahead = await git(["rev-list", "--count", `${current.baseCommit}..${current.branch}`], current.path);
261
- if (ahead !== "0") blockers.push(`${ahead} commit(s) on ${current.branch} not on the original branch.`);
328
+ const ahead = await git(["rev-list", "--count", "HEAD", `refs/heads/${current.branch}`, `^${current.baseCommit}`], current.path);
329
+ if (ahead !== "0") blockers.push(`${ahead} commit(s) on HEAD or ${current.branch} not on the original branch.`);
330
+ } else {
331
+ blockers.push("Could not verify commits without the original branch and base commit.");
262
332
  }
263
333
  } catch {
264
334
  blockers.push("Could not verify the worktree is clean.");
@@ -278,8 +348,7 @@ export default function worktreeExtension(pi: ExtensionAPI) {
278
348
  }
279
349
 
280
350
  try {
281
- await git(["worktree", "remove", "--force", current.path], current.originalCwd);
282
- if (current.branch) await git(["branch", "-D", current.branch], current.originalCwd);
351
+ await git(["worktree", "remove", ...(params.discard_changes ? ["--force"] : []), current.path], current.originalCwd);
283
352
  } catch (error) {
284
353
  return {
285
354
  content: [{ type: "text", text: `Could not remove the worktree: ${(error as Error).message}` }],
@@ -288,6 +357,19 @@ export default function worktreeExtension(pi: ExtensionAPI) {
288
357
  };
289
358
  }
290
359
  applyState(undefined);
360
+ if (current.branch) {
361
+ try {
362
+ // The commit check above (or discard consent) already cleared the branch;
363
+ // -d would also refuse a tip the original checkout's current HEAD lacks.
364
+ await git(["branch", "-D", current.branch], current.originalCwd);
365
+ } catch (error) {
366
+ return {
367
+ content: [{ type: "text", text: `Removed worktree ${current.path}; back in ${current.originalCwd}, but could not delete branch ${current.branch}: ${(error as Error).message}. Inspect the branch before removing it manually.` }],
368
+ details: { worktreeState: null } satisfies WorktreeDetails,
369
+ isError: true,
370
+ };
371
+ }
372
+ }
291
373
  return {
292
374
  content: [{ type: "text", text: `Removed worktree ${current.path}${current.branch ? ` and branch ${current.branch}` : ""}; back in ${current.originalCwd}.` }],
293
375
  details: { worktreeState: null } satisfies WorktreeDetails,
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Worktree-session guards for the `powershell` tool (pure): the same two
3
+ * invariants guards.ts enforces for bash (git must verifiably target this
4
+ * session's worktree; shared-stash forms that collide with a concurrent
5
+ * session are refused), judged on PowerShell's statement split
6
+ * (permissions/powershell-rules.ts). On Windows PowerShell is the primary
7
+ * shell, so without this the invariant did not hold where it matters most.
8
+ *
9
+ * Conservative in the one direction that matters: git in a line the split
10
+ * cannot follow (a script block, `$(…)`, a call operator, `Invoke-Expression`,
11
+ * a nested shell, backtick escapes), after a directory change it cannot
12
+ * resolve, or next to a GIT_DIR / GIT_WORK_TREE / GIT_COMMON_DIR setting is
13
+ * refused, naming the plain form that passes. A line without git passes.
14
+ * Steering, not security: the permission gate still judges every command.
15
+ */
16
+
17
+ import { homedir } from "node:os";
18
+ import { isAbsolute, resolve } from "node:path";
19
+ import { toAbsolute } from "../auto-mode/paths.ts";
20
+ import { gitSubcommand } from "../auto-mode/shell-analysis.ts";
21
+ import { powershellInjectionSyntax, powershellStatements, statementCommand } from "../permissions/powershell-rules.ts";
22
+ import { containsPath, isolated, sharedRepositoryRoots, stashReason, type WorktreeGuardContext } from "./guards.ts";
23
+
24
+ /** git as a word (`git`, `GIT`, `git.exe`); PowerShell resolves commands case-insensitively. */
25
+ const mentionsGit = (text: string) => /\bgit(?:\.exe)?\b/i.test(text);
26
+
27
+ /** The repository variables, in any spelling (`$env:GIT_DIR = …`, `Set-Item env:GIT_WORK_TREE`, `[Environment]::SetEnvironmentVariable('GIT_DIR', …)`). */
28
+ const REPOSITORY_ENV = /\bGIT_(?:DIR|WORK_TREE|COMMON_DIR)\b/i;
29
+
30
+ /** The words of one statement, quotes removed (`'…'` literal, `"…"` with doubled quotes). */
31
+ function statementWords(statement: string): string[] {
32
+ const words: string[] = [];
33
+ const pattern = /'((?:[^']|'')*)'|"((?:[^"]|"")*)"|([^\s'"]+)/g;
34
+ let end = 0;
35
+ for (const match of statement.matchAll(pattern)) {
36
+ const value = match[1] !== undefined ? match[1].replace(/''/g, "'") : match[2] !== undefined ? match[2].replace(/""/g, '"') : match[3];
37
+ // Quotes can start mid-word: --git-dir="C:\\my repo\\.git" is one argument.
38
+ if (words.length > 0 && match.index === end) words[words.length - 1] += value;
39
+ else words.push(value);
40
+ end = match.index + match[0].length;
41
+ }
42
+ return words;
43
+ }
44
+
45
+ /** A PowerShell path word resolved against `dir`: `~` and `~\` expand, a drive or UNC path stands alone. */
46
+ function resolvePowershellPath(dir: string, word: string): string {
47
+ const home = homedir();
48
+ const tilde = word === "~" || word.startsWith("~/") || word.startsWith("~\\") ? `~/${word.slice(2)}` : word;
49
+ if (/^[A-Za-z]:[\\/]/.test(tilde) || tilde.startsWith("\\\\") || isAbsolute(tilde)) return resolve(tilde);
50
+ return toAbsolute(dir, tilde === "~/" ? "~" : tilde, home);
51
+ }
52
+
53
+ /** Values PowerShell expands at runtime (a variable, a subexpression) or a wildcard `-Path` matches. */
54
+ const dynamicPath = (word: string) => /[$`*?[]/.test(word);
55
+
56
+ /** Location parameters that take a value, besides the path itself. */
57
+ const LOCATION_VALUE_PARAMETERS = /^-(?:erroraction|ea|warningaction|wa|informationaction|infa|stackname|errorvariable|ev|warningvariable|wv|informationvariable|iv|outvariable|ov)$/i;
58
+ const PATH_PARAMETER = /^-(?:literalpath|path|lp|pspath)(?::(.+))?$/i;
59
+
60
+ /** The directory a `Set-Location` / `Push-Location` statement moves to, as written; undefined when none is named. */
61
+ function locationTarget(args: string[]): string | undefined {
62
+ let positional: string | undefined;
63
+ for (let j = 0; j < args.length; j++) {
64
+ const word = args[j];
65
+ const named = PATH_PARAMETER.exec(word);
66
+ if (named) return named[1] ?? args[j + 1] ?? "";
67
+ if (LOCATION_VALUE_PARAMETERS.test(word)) j++;
68
+ else if (!word.startsWith("-") || word === "-") positional ??= word;
69
+ }
70
+ return positional;
71
+ }
72
+
73
+ export function worktreePowershellGuardReason(context: WorktreeGuardContext): string | undefined {
74
+ const { command, worktreePath } = context;
75
+ const sharedRoots = sharedRepositoryRoots(context);
76
+ if (!mentionsGit(command)) return undefined;
77
+ const plainGit = `Run git as its own plain statement, with literal paths inside ${worktreePath} (no script blocks, call operators or nested shells).`;
78
+
79
+ const statements = powershellStatements(command);
80
+ if (statements === undefined) {
81
+ return isolated(worktreePath, "this command's quoting could not be followed, so the repository its git commands target cannot be verified", plainGit);
82
+ }
83
+ const syntax = powershellInjectionSyntax(command);
84
+ if (syntax) {
85
+ return isolated(worktreePath, `this command runs git next to ${syntax}, so the repository it targets cannot be verified`, plainGit);
86
+ }
87
+ if (REPOSITORY_ENV.test(command)) {
88
+ return isolated(
89
+ worktreePath,
90
+ "this command sets GIT_DIR, GIT_WORK_TREE or GIT_COMMON_DIR, which can point git at another repository",
91
+ `Drop the variable and run git from ${worktreePath}.`,
92
+ );
93
+ }
94
+
95
+ /** Directory the current statement runs in; undefined = not statically known. */
96
+ let dir: string | undefined = worktreePath;
97
+ for (const statement of statements) {
98
+ const cmd = statementCommand(statement);
99
+ const [, ...args] = statementWords(statement);
100
+
101
+ if (cmd === "set-location" || cmd === "push-location") {
102
+ const target = locationTarget(args);
103
+ const absolute = target !== undefined && (/^[A-Za-z]:[\\/]/.test(target) || target.startsWith("\\\\") || isAbsolute(target));
104
+ // No argument: pwsh 7 goes home. `-`/`+` walk the location history, which is not known here.
105
+ if (target === undefined) dir = args.length === 0 ? homedir() : undefined;
106
+ else if (!target || target === "-" || target === "+" || dynamicPath(target)) dir = undefined;
107
+ // A relative move from an unknown directory stays unknown; an absolute one re-anchors.
108
+ else if (dir !== undefined || absolute) dir = resolvePowershellPath(dir ?? worktreePath, target);
109
+ continue;
110
+ }
111
+ if (cmd === "pop-location") {
112
+ dir = undefined;
113
+ continue;
114
+ }
115
+ if (cmd !== "git") continue;
116
+
117
+ if (dir === undefined) {
118
+ return isolated(worktreePath, "a preceding directory change makes the repository this git command targets unverifiable", `Use literal paths inside ${worktreePath} instead.`);
119
+ }
120
+
121
+ let effective = dir;
122
+ const extraTargets: string[] = [];
123
+ let i = 0;
124
+ while (i < args.length && args[i].startsWith("-")) {
125
+ const word = args[i];
126
+ const lower = word.toLowerCase();
127
+ const pathFlag = word === "-C" || lower === "--git-dir" || lower === "--work-tree";
128
+ const inlined = lower.startsWith("--git-dir=") || lower.startsWith("--work-tree=");
129
+ const configWorktree = word === "-c" && /^core\.worktree=/i.test(args[i + 1] ?? "");
130
+ if (lower === "--config-env" || lower.startsWith("--config-env=")) {
131
+ return isolated(worktreePath, "`git --config-env` reads its configuration at runtime, so the repository it targets cannot be verified", `Use literal paths inside ${worktreePath} instead.`);
132
+ }
133
+ if (pathFlag || inlined || configWorktree) {
134
+ const raw = inlined ? word.slice(word.indexOf("=") + 1) : configWorktree ? args[i + 1].slice(args[i + 1].indexOf("=") + 1) : args[i + 1];
135
+ if (!raw || dynamicPath(raw)) {
136
+ return isolated(worktreePath, `\`git ${word}\` computes its repository target at runtime, so it cannot be verified`, `Use literal paths inside ${worktreePath} instead.`);
137
+ }
138
+ const target = resolvePowershellPath(effective, raw);
139
+ if (word === "-C") effective = target;
140
+ else extraTargets.push(target);
141
+ i += inlined ? 1 : 2;
142
+ continue;
143
+ }
144
+ i += word === "-c" || lower === "--namespace" || lower === "--exec-path" ? 2 : 1;
145
+ }
146
+
147
+ const targets = [effective, ...extraTargets];
148
+ for (const target of targets) {
149
+ if (containsPath(worktreePath, target)) continue;
150
+ if (sharedRoots.some((root) => containsPath(root, target))) {
151
+ return isolated(
152
+ worktreePath,
153
+ `this git command targets ${target}, which is the shared checkout or another worktree of the same repository`,
154
+ `Run the equivalent against ${worktreePath}.`,
155
+ );
156
+ }
157
+ }
158
+ if (targets.some((target) => containsPath(worktreePath, target))) {
159
+ const { sub, rest } = gitSubcommand(args.map((value) => ({ value })));
160
+ if (sub === "stash") {
161
+ const reason = stashReason(rest);
162
+ if (reason) return reason;
163
+ }
164
+ }
165
+ }
166
+ return undefined;
167
+ }
@@ -10,31 +10,23 @@
10
10
  */
11
11
 
12
12
  import { isAbsolute, resolve } from "node:path";
13
+ import { powershellQuote, shellQuote } from "../lib/shell-quote.ts";
14
+ import { normalizeToolPath } from "../lib/tool-path.ts";
13
15
 
14
16
  /** Tools whose `path` argument is relative to the session cwd. */
15
17
  const PATH_TOOLS = new Set(["read", "edit", "write", "notebook_edit", "grep", "find", "ls", "lsp_diagnostics"]);
16
18
  /** Of those, the ones where a missing path means "the cwd itself". */
17
19
  const DEFAULTS_TO_CWD = new Set(["grep", "find", "ls"]);
18
20
 
19
- export function shellQuote(path: string): string {
20
- return `'${path.replace(/'/g, "'\\''")}'`;
21
- }
22
-
23
- /** PowerShell single-quoted literal: only `'` needs escaping, as `''`. */
24
- export function powershellQuote(path: string): string {
25
- return `'${path.replace(/'/g, "''")}'`;
26
- }
27
-
28
-
29
21
  /**
30
- * Mutates `input` in place so the call runs inside the worktree. For bash,
31
- * returns the model's ORIGINAL command (pre-`cd`-wrapper) so the caller can
22
+ * Mutates `input` in place so the call runs inside the worktree. For bash
23
+ * (and `monitor`, which runs a bash command), returns the model's ORIGINAL command (pre-`cd`-wrapper) so the caller can
32
24
  * publish it over `ORIGINAL_COMMAND_CHANNEL` keyed by the call id: the
33
25
  * permission matcher evaluates rules against what the model asked for, not
34
26
  * the wrapper (which starts with `cd` and matches no Bash rule), while the
35
27
  * classifier and safety floor keep reading the wrapped `input.command`, whose
36
28
  * `cd` makes containment resolve inside the worktree —
37
- * docs/decisions/code-review-remediation.md ("Worktree vs. permission rules").
29
+ * working-docs/decisions/code-review-remediation.md ("Worktree vs. permission rules").
38
30
  * The original is deliberately NOT stored in `input`: a key there is
39
31
  * model-writable (lib/original-command.ts).
40
32
  */
@@ -43,7 +35,10 @@ export function rewriteToolInput(
43
35
  input: Record<string, unknown>,
44
36
  worktreePath: string,
45
37
  ): { originalCommand?: string } {
46
- if (toolName === "bash") {
38
+ // `monitor` runs its command through the same bash as the `bash` tool
39
+ // (background/index.ts), so it gets the same `cd` wrapper; a `ws` monitor
40
+ // has no command and nothing to move.
41
+ if (toolName === "bash" || toolName === "monitor") {
47
42
  if (typeof input.command === "string") {
48
43
  const originalCommand = input.command;
49
44
  input.command = `cd ${shellQuote(worktreePath)} && (${originalCommand}\n)`;
@@ -73,8 +68,11 @@ export function rewriteToolInput(
73
68
  (field) => typeof input[field] === "string" && (input[field] as string).length > 0,
74
69
  );
75
70
  if (pathField) {
76
- const value = input[pathField] as string;
77
- if (!isAbsolute(value)) input[pathField] = resolve(worktreePath, value);
71
+ // Read the value the way pi's tools will (`~`, `@`, `file://`): judged
72
+ // raw, `~/x` and `@src/x` looked relative and became literal `~` and `@src`
73
+ // directories inside the worktree.
74
+ const normalized = normalizeToolPath(input[pathField] as string);
75
+ if (!isAbsolute(normalized)) input[pathField] = resolve(worktreePath, normalized);
78
76
  } else if (DEFAULTS_TO_CWD.has(toolName)) {
79
77
  input.path = worktreePath;
80
78
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one-code-extension",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "One Code as a pi package — the Claude Code experience on your own pi install: ported system prompt, Claude-Code-format permissions, .claude/ compatibility, ToolSearch-style deferred tools, and system-reminder steering. For the bundled app, install `@one-ai/one-code` instead.",
5
5
  "keywords": [
6
6
  "pi",
@@ -28,9 +28,11 @@
28
28
  "extensions": [
29
29
  "extensions/branding/index.ts",
30
30
  "extensions/claude-compat/index.ts",
31
+ "extensions/kept-thinking/index.ts",
31
32
  "extensions/context-budget/index.ts",
32
33
  "extensions/system-reminder/index.ts",
33
34
  "extensions/claude-context/index.ts",
35
+ "extensions/tool-call-corruption/index.ts",
34
36
  "extensions/hooks/index.ts",
35
37
  "extensions/tool-search/index.ts",
36
38
  "extensions/search-tools/index.ts",
@@ -50,9 +52,11 @@
50
52
  "extensions/effort/index.ts",
51
53
  "extensions/spinner/index.ts",
52
54
  "extensions/model-default/index.ts",
55
+ "extensions/newer-model/index.ts",
53
56
  "extensions/footer/index.ts",
54
57
  "extensions/turn-duration/index.ts",
55
58
  "extensions/interrupted/index.ts",
59
+ "extensions/send-now/index.ts",
56
60
  "extensions/recap/index.ts",
57
61
  "extensions/btw/index.ts",
58
62
  "extensions/session-title/index.ts",
@@ -62,6 +66,7 @@
62
66
  "extensions/tasks/index.ts",
63
67
  "extensions/plan-mode/index.ts",
64
68
  "extensions/notebook/index.ts",
69
+ "extensions/artifacts/index.ts",
65
70
  "extensions/web/index.ts",
66
71
  "extensions/web-fetch/index.ts",
67
72
  "extensions/lsp/index.ts",
@@ -69,6 +74,7 @@
69
74
  "extensions/doctor/index.ts",
70
75
  "extensions/context-management/index.ts",
71
76
  "extensions/compaction/index.ts",
77
+ "extensions/idle-compact/index.ts",
72
78
  "extensions/system-prompt/index.ts"
73
79
  ],
74
80
  "themes": [
@@ -88,28 +94,33 @@
88
94
  "scripts": {
89
95
  "typecheck": "tsc --noEmit",
90
96
  "test": "vitest run",
91
- "gen:model-facts": "node scripts/gen-model-facts.mjs",
97
+ "gen:model-catalog": "node scripts/gen-model-catalog.mjs",
92
98
  "sync:app-readme": "node scripts/app-readme.mjs"
93
99
  },
94
100
  "dependencies": {
95
101
  "@modelcontextprotocol/sdk": "^1.30.0",
96
102
  "@mozilla/readability": "^0.6.0",
97
103
  "acorn": "^8.18.0",
104
+ "ignore": "^7.0.8",
98
105
  "linkedom": "^0.18.13",
106
+ "marked": "^18.0.11",
99
107
  "pi-web-search": "^1.5.0",
100
108
  "turndown": "^7.2.4",
101
- "typebox": "^1.3.7"
109
+ "web-tree-sitter": "^0.27.0",
110
+ "yaml": "^2.9.0"
102
111
  },
103
112
  "devDependencies": {
104
- "@earendil-works/pi-agent-core": "^0.86.1",
105
- "@earendil-works/pi-ai": "^0.86.1",
106
- "@earendil-works/pi-coding-agent": "^0.86.1",
113
+ "@earendil-works/pi-agent-core": "^1.0.0",
114
+ "@earendil-works/pi-ai": "^1.0.0",
115
+ "@earendil-works/pi-coding-agent": "^1.0.0",
107
116
  "@types/node": "^26.1.2",
108
117
  "@types/turndown": "^5.0.6",
118
+ "typebox": "^1.3.7",
109
119
  "typescript": "^5.6.0",
110
120
  "vitest": "^2.1.0"
111
121
  },
112
122
  "peerDependencies": {
113
- "@earendil-works/pi-coding-agent": ">=0.83.0"
123
+ "@earendil-works/pi-coding-agent": ">=0.84.3",
124
+ "typebox": "*"
114
125
  }
115
126
  }