one-code-extension 0.4.0 → 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 +219 -219
  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 +66 -34
  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 +19 -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
@@ -28,6 +28,7 @@
28
28
  import {
29
29
  gitSubcommand,
30
30
  leadTokens,
31
+ LOOPS,
31
32
  parseCommand,
32
33
  resolvePayload,
33
34
  type Segment,
@@ -37,18 +38,14 @@ import {
37
38
  export const clip = (text: string, max = 200): string => (text.length > max ? `${text.slice(0, max)}…` : text);
38
39
 
39
40
  export function bashGuardReason(command: string, opts: { background: boolean }): string | undefined {
40
- // A heredoc/here-string body is data, but parseCommand tokenizes it as live
41
- // shell — every guard would misfire on body text (a literal `vim` or
42
- // `while … sleep` inside a document). Judge only the command that PRECEDES
43
- // the first `<<`: `sleep 600 <<< ""` is still a sleep (review T14), while a
44
- // `cat <<EOF` body is never read.
45
- const lead = command.split(/<</)[0];
46
- if (lead !== command && !lead.trim()) return undefined;
47
- const { segments, parseFailed } = parseCommand(lead);
48
- if (parseFailed || segments.length === 0) return undefined;
41
+ // A heredoc body is data to the grammar, so a literal `vim` or `while …
42
+ // sleep` inside a document never trips a guard (review T14).
43
+ const parsed = parseCommand(command);
44
+ if (parsed.parseFailed || parsed.segments.length === 0) return undefined;
45
+ const { segments } = parsed;
49
46
  const interactive = interactiveReason(segments);
50
47
  if (interactive || opts.background) return interactive;
51
- return waitReason(segments) ?? pollLoopReason(lead, segments) ?? orphanReason(lead, segments);
48
+ return waitReason(segments) ?? pollLoopReason(command, segments) ?? orphanReason(parsed.background, segments);
52
49
  }
53
50
 
54
51
  // ---------------------------------------------------------------------------
@@ -123,15 +120,13 @@ function waitReason(segments: Segment[]): string | undefined {
123
120
  // ---------------------------------------------------------------------------
124
121
  // Poll-loop guard
125
122
 
123
+ const inLoop = (seg: Segment) => seg.enclosing.some((construct) => LOOPS.has(construct));
124
+
126
125
  function pollLoopReason(command: string, segments: Segment[]): string | undefined {
127
- const first = leadTokens(segments[0])[0]?.value;
128
- if (first !== "while" && first !== "until" && first !== "for") return undefined;
129
- // `sleep` counts only in command position (segment lead, or right after a
130
- // `do`/`then`/`else` keyword) — `echo sleep` is data, not a wait.
131
- const sleeps = segments.some((seg) => {
132
- const { command: cmd } = resolvePayload(leadTokens(seg));
133
- return cmd === "sleep";
134
- });
126
+ // Wherever the loop sits in the line: `echo start; while true; do sleep 5;
127
+ // done` stalls the session as surely as the loop alone. `sleep` counts only
128
+ // as a command inside the loop: `echo sleep` is data.
129
+ const sleeps = segments.some((seg) => inLoop(seg) && resolvePayload(leadTokens(seg)).command === "sleep");
135
130
  if (!sleeps) return undefined;
136
131
  return (
137
132
  `Blocked: a foreground polling loop (\`${clip(command, 160)}\`). It occupies the whole session while it spins. ` +
@@ -143,54 +138,7 @@ function pollLoopReason(command: string, segments: Segment[]): string | undefine
143
138
  // ---------------------------------------------------------------------------
144
139
  // Orphan guard
145
140
 
146
- /** True when the command backgrounds something with a top-level unquoted `&`. */
147
- export function hasBackgroundAmp(command: string): boolean {
148
- let inSingle = false;
149
- let inDouble = false;
150
- let inAnsiC = false; // $'…' honors backslash-escaped quotes, unlike '…'
151
- let escape = false;
152
- for (let i = 0; i < command.length; i++) {
153
- const ch = command[i];
154
- if (escape) {
155
- escape = false;
156
- continue;
157
- }
158
- if (inAnsiC) {
159
- if (ch === "\\") escape = true;
160
- else if (ch === "'") inAnsiC = false;
161
- continue;
162
- }
163
- if (ch === "'" && !inSingle && !inDouble && command[i - 1] === "$") {
164
- inAnsiC = true;
165
- continue;
166
- }
167
- if (ch === "\\" && !inSingle) {
168
- escape = true;
169
- continue;
170
- }
171
- if (ch === "'" && !inDouble) {
172
- inSingle = !inSingle;
173
- continue;
174
- }
175
- if (ch === '"' && !inSingle) {
176
- inDouble = !inDouble;
177
- continue;
178
- }
179
- if (inSingle || inDouble || ch !== "&") continue;
180
- const prev = command[i - 1];
181
- const next = command[i + 1];
182
- if (next === "&") {
183
- i++; // logical &&
184
- continue;
185
- }
186
- if (next === ">") continue; // &> redirect
187
- if (prev === ">" || prev === "|") continue; // 2>&1 fd duplication, |& pipe
188
- return true;
189
- }
190
- return false;
191
- }
192
-
193
- function orphanReason(command: string, segments: Segment[]): string | undefined {
141
+ function orphanReason(background: boolean, segments: Segment[]): string | undefined {
194
142
  const message = (via: string) =>
195
143
  `Blocked: this command detaches a process with ${via}, leaving an orphan this session cannot manage — its output and exit status would be lost. ` +
196
144
  "Run it with run_in_background: true instead: it returns a task id immediately, completion arrives as a task notification, output stays readable with task_output, and it can be stopped with task_stop. " +
@@ -204,7 +152,7 @@ function orphanReason(command: string, segments: Segment[]): string | undefined
204
152
  // Only a `wait` that comes LAST reaps the children — `wait; job &` still
205
153
  // orphans the job it precedes.
206
154
  const lastCmd = resolvePayload(leadTokens(segments[segments.length - 1])).command;
207
- if (hasBackgroundAmp(command) && lastCmd !== "wait") return message("`&`");
155
+ if (background && lastCmd !== "wait") return message("`&`");
208
156
  return undefined;
209
157
  }
210
158
 
@@ -5,77 +5,130 @@
5
5
  * Registering a tool named `bash` overrides the built-in (findings §2), which
6
6
  * is how pi's official sandbox example does it too. The foreground path
7
7
  * delegates to pi's real executor (`createBashToolDefinition`) so upstream
8
- * bash behavior — timeout handling, truncation, PI_* env, spawn hooks — stays
9
- * exactly pi's; the shared body in lib/shell-tool.ts adds the background
8
+ * bash behavior — timeout handling, PI_* env, spawn hooks — stays exactly
9
+ * pi's (its tail cut of a long output is replaced by Claude Code's head-first
10
+ * one, lib/spill-file.ts); the shared body in lib/shell-tool.ts adds the background
10
11
  * branch (spool to `<sessionDir>/bash/<taskId>/output.log`, the shared
11
12
  * background registry, a steered completion notification), and this file only
12
13
  * supplies what is bash's: pi's definition, the bash the session resolved
13
14
  * (lib/shell-spawn.ts — Git Bash on Windows, `CLAUDE_CODE_GIT_BASH_PATH`
14
- * honoured), the CC-shaped description and the bash guards. The permission
15
+ * honoured), Claude Code's description (description.ts) and the bash guards. The permission
15
16
  * gate and auto-mode classifier run before execute like any bash call — a
16
17
  * background command is NOT auto-allowed, and the gate fires before anything
17
18
  * detaches.
18
19
  */
19
20
 
20
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
21
- import { createBashToolDefinition } from "@earendil-works/pi-coding-agent";
21
+ import { join } from "node:path";
22
+ import type { BashSpawnContext, ExtensionAPI } from "@earendil-works/pi-coding-agent";
23
+ import { createBashToolDefinition, createLocalBashOperations, getAgentDir } from "@earendil-works/pi-coding-agent";
22
24
  import { Type } from "typebox";
25
+ import { childProcessEnv, LAUNCHER_ENV_VAR } from "../lib/app-launch.mjs";
23
26
  import { perCwd } from "../lib/per-cwd.ts";
24
- import { bashSpawn } from "../lib/shell-spawn.ts";
27
+ import { bashSpawn, piShellEnv, withChildProcessEnv } from "../lib/shell-spawn.ts";
25
28
  import { registerShellTool } from "../lib/shell-tool.ts";
29
+ import { type DescriptionForm, followDescriptionForm } from "../lib/tool-variants.ts";
30
+ import { CONTEXT_BASELINE_CHANNEL, restoredContext } from "../lib/context-stack.ts";
31
+ import { PERMISSION_STATUS_CHANNEL, type PermissionStatus } from "../permissions/modes.ts";
32
+ import { BASH_PARAMS, bashDescription } from "./description.ts";
26
33
  import { bashGuardReason } from "./guards.ts";
27
34
 
28
35
  const BashParams = Type.Object({
29
- command: Type.String({ description: "Bash command to execute" }),
30
- timeout: Type.Optional(Type.Number({ description: "Optional timeout in milliseconds (max 600000)" })),
31
- run_in_background: Type.Optional(
32
- Type.Boolean({
33
- description:
34
- "Run detached and return a task id immediately instead of waiting. Completion arrives as a task notification; inspect with task_output, stop with task_stop. Use this instead of nohup/'&' — those leave an unmanaged orphan process",
35
- }),
36
- ),
37
- description: Type.Optional(
38
- Type.String({ description: "5-10 word description of what the command does (shown in notifications)" }),
39
- ),
36
+ command: Type.String({ description: BASH_PARAMS.command }),
37
+ timeout: Type.Optional(Type.Number({ description: BASH_PARAMS.timeout })),
38
+ description: Type.Optional(Type.String({ description: BASH_PARAMS.description })),
39
+ run_in_background: Type.Optional(Type.Boolean({ description: BASH_PARAMS.run_in_background })),
40
40
  });
41
41
 
42
42
  export default function bashExtension(pi: ExtensionAPI) {
43
+ bashTool(pi, { announce: true });
44
+ }
45
+
46
+ /**
47
+ * The tool itself. `announce: false` is the in-process child's shape
48
+ * (bash/child.ts): the same tool, but a child has no terminal of its own
49
+ * (stderr is the parent's TUI), so it prints nothing.
50
+ */
51
+ export function bashTool(pi: ExtensionAPI, { announce }: { announce: boolean }) {
43
52
  // The session's bash, resolved once (lib/shell-spawn.ts). An ignored
44
53
  // CLAUDE_CODE_GIT_BASH_PATH is reported once, at the first session start.
45
54
  const bash = bashSpawn();
46
55
  const shellPath = bash.spawn?.commandTransport === "stdin" ? undefined : bash.spawn?.shell;
47
56
  let warned = false;
48
- pi.on("session_start", (_event, ctx) => {
57
+ if (announce) pi.on("session_start", (_event, ctx) => {
49
58
  if (warned || !bash.warning) return;
50
59
  warned = true;
51
60
  if (ctx.hasUI) ctx.ui.notify(bash.warning, "warning");
52
61
  else process.stderr.write(`${bash.warning}\n`);
53
62
  });
54
63
 
55
- // pi's definition supplies the description and TUI renderers; the executor
56
- // is re-created per working directory because it closes over cwd (worktree
57
- // switches change ctx.cwd mid-session). The same bash drives pi's foreground
64
+ // pi's definition supplies the TUI renderers; the executor is re-created
65
+ // per working directory because it closes over cwd (worktree switches
66
+ // change ctx.cwd mid-session). The same bash drives pi's foreground
58
67
  // executor, so the override applies there too.
59
- const base = createBashToolDefinition(process.cwd(), { shellPath });
60
- // pi's base sentence is "Optionally provide a timeout in seconds." — but the
61
- // `timeout` parameter and the execute path both use milliseconds (Claude
62
- // Code's Bash unit; the executor divides by 1000). The two must not
63
- // contradict, or a model that trusts the description sends `timeout: 120`
64
- // and gets a 120 ms deadline (TOOL-FIDELITY-REVIEW-2026-09-07 H3). Swap in
65
- // CC's Bash sentence.
66
- const baseDescription = base.description.replace(
67
- "Optionally provide a timeout in seconds.",
68
- "`timeout` is in milliseconds: default 120000, max 600000.",
69
- );
68
+ // Every command the model runs gets the user's own environment back under
69
+ // the bundled app (lib/app-launch.mjs), so a `pi` it starts is the user's pi.
70
+ const spawnHook = (context: BashSpawnContext): BashSpawnContext => ({ ...context, env: childProcessEnv(context.env) });
71
+ const base = createBashToolDefinition(process.cwd(), { shellPath, spawnHook });
70
72
 
71
- registerShellTool(pi, {
73
+ // The tier follows the model; the permission-mode variant freezes when the
74
+ // first request starts. Re-registering on a mode switch changes both the
75
+ // description and pi's tool order, losing the entire cached prefix. Mode
76
+ // reminders and the live permission gate carry later changes instead.
77
+ let form: DescriptionForm = "short";
78
+ // Tiny's dedicated search tools replace the long form's shell-find advice.
79
+ let searchTools = false;
80
+ let autoMode = false;
81
+ let liveAutoMode = false;
82
+ let modeFrozen = false;
83
+ const restoredAutoMode = (): boolean | undefined => {
84
+ const baselines = restoredContext(pi.events)?.baselines;
85
+ // Older snapshots' last mode chose the last request's description.
86
+ const saved = baselines?.["bash-auto-mode"];
87
+ if (typeof saved === "boolean") return saved;
88
+ return typeof baselines?.["permission-mode"] === "string" ? baselines["permission-mode"] === "auto" : undefined;
89
+ };
90
+ const describe = () => bashDescription(form, autoMode, searchTools);
91
+ const setDescription = registerShellTool(pi, {
72
92
  name: "bash",
73
93
  ccLabel: "Bash",
74
- description: `${baseDescription} Pass run_in_background: true for long-running commands (builds, servers, watches): it returns a task id immediately so you can keep working, completion arrives as a task notification, and the output is retrievable with task_output / stoppable with task_stop (both deferred — load them with tool_search; in a one-shot print/json session the call runs to completion and returns the output directly). Foreground \`sleep\` is blocked; to wait on a condition use the monitor tool (deferred — load it with tool_search select:monitor) with an until-loop.`,
94
+ description: describe(),
75
95
  parameters: BashParams,
76
96
  base,
77
- foreground: perCwd((cwd: string) => createBashToolDefinition(cwd, { shellPath })),
97
+ foreground: perCwd((cwd: string) => createBashToolDefinition(cwd, { shellPath, spawnHook })),
78
98
  guard: bashGuardReason,
79
99
  backgroundShell: () => bash.spawn,
80
100
  });
101
+ followDescriptionForm(pi, (next, tier) => {
102
+ form = next;
103
+ searchTools = tier === "tiny";
104
+ setDescription(describe());
105
+ });
106
+ pi.events.on(PERMISSION_STATUS_CHANNEL, (data) => {
107
+ liveAutoMode = (data as PermissionStatus).mode === "auto";
108
+ if (modeFrozen) return;
109
+ // Permissions' startup broadcast precedes this extension's session_start.
110
+ // Honor a restored variant even here: a temporary re-registration would
111
+ // move the tool despite putting its original description back afterwards.
112
+ autoMode = restoredAutoMode() ?? liveAutoMode;
113
+ setDescription(describe());
114
+ });
115
+ pi.on("session_start", () => {
116
+ const saved = restoredAutoMode();
117
+ modeFrozen = saved !== undefined;
118
+ autoMode = saved ?? liveAutoMode;
119
+ setDescription(describe());
120
+ });
121
+ pi.on("turn_start", () => {
122
+ modeFrozen = true;
123
+ pi.events.emit(CONTEXT_BASELINE_CHANNEL, { key: "bash-auto-mode", value: autoMode });
124
+ });
125
+
126
+ // The user's own `!` commands, under the bundled app only: pi runs them with
127
+ // its full process environment, so they get the same restore. Outside the app
128
+ // pi's default path runs untouched. They run in the session's bash, the one
129
+ // the bash tool uses.
130
+ pi.on("user_bash", () => {
131
+ if (process.env[LAUNCHER_ENV_VAR] === undefined) return undefined;
132
+ return { operations: withChildProcessEnv(createLocalBashOperations({ shellPath }), () => piShellEnv(join(getAgentDir(), "bin"))) };
133
+ });
81
134
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * pi's compaction keep window, at startup (lib/compaction-keep.mjs has the
3
+ * rule and why).
4
+ *
5
+ * On the user's own pi, while neither the user's nor the project's settings set
6
+ * `compaction.keepRecentTokens`, One Code asks once whether to set it to 0 in
7
+ * the user's settings. "Yes" writes that one key (pi merges nested settings
8
+ * key by key when it saves its own, so the value survives); pi reads it at
9
+ * the next start. "No" is remembered in `~/.onecode/settings.json` and never
10
+ * asked again; Escape asks again next start. An explicit value is the user's
11
+ * and is never questioned; `/doctor` shows it. The bundled app sets 0 itself.
12
+ */
13
+
14
+ import os from "node:os";
15
+ import { join } from "node:path";
16
+ import { type ExtensionContext, getAgentDir, SettingsManager } from "@earendil-works/pi-coding-agent";
17
+ import { installKind } from "../doctor/builtins.ts";
18
+ import {
19
+ compactionKeepView,
20
+ KEEP_NO,
21
+ KEEP_YES,
22
+ keepWindowPrompt,
23
+ ONE_CODE_KEEP_RECENT_TOKENS,
24
+ withCompactionKeepBackfilled,
25
+ } from "../lib/compaction-keep.mjs";
26
+ import { readSettingsForWrite, writeSettings } from "../lib/one-code-settings.ts";
27
+ import { tildify } from "../lib/paths.ts";
28
+ import { declined, recordDeclined } from "./declined.ts";
29
+
30
+ /** `~/.onecode/settings.json` key: the user answered "No" to the question. */
31
+ export const KEEP_DECLINED_KEY = "compactionKeepDeclined";
32
+
33
+ /** Ask once, when nothing sets the keep window. Never throws. */
34
+ export async function handleCompactionKeep(ctx: ExtensionContext): Promise<void> {
35
+ try {
36
+ if (installKind() === "app" || !ctx.hasUI || process.env.CC_COMPACTION === "0") return;
37
+ if (declined(KEEP_DECLINED_KEY)) return;
38
+ const agentDir = getAgentDir();
39
+ const manager = SettingsManager.create(ctx.cwd, agentDir);
40
+ const modelKey = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
41
+ if (compactionKeepView(manager.getGlobalSettings(), manager.getProjectSettings(), modelKey).source !== "default") return;
42
+
43
+ const userPath = join(agentDir, "settings.json");
44
+ const choice = await ctx.ui.select(keepWindowPrompt(tildify(userPath, os.homedir())), [KEEP_YES, KEEP_NO]);
45
+ if (choice === KEEP_YES) {
46
+ const { settings, changed } = withCompactionKeepBackfilled(readSettingsForWrite(userPath));
47
+ if (changed) writeSettings(userPath, settings);
48
+ ctx.ui.notify(`Set compaction.keepRecentTokens to ${ONE_CODE_KEEP_RECENT_TOKENS}; /compact works on short sessions from the next start.`, "info");
49
+ } else if (choice === KEEP_NO) {
50
+ try {
51
+ recordDeclined(KEEP_DECLINED_KEY);
52
+ } catch (error) {
53
+ ctx.ui.notify(`Could not remember the answer in ~/.onecode/settings.json: ${(error as Error).message}. One Code asks again next start.`, "warning");
54
+ }
55
+ }
56
+ } catch (error) {
57
+ try {
58
+ ctx.ui.notify(`Could not set pi's compaction keep window: ${(error as Error).message}. Run /doctor for the settings change.`, "warning");
59
+ } catch {
60
+ // The session ended while the dialog was open: nothing is left to tell.
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The startup questions' remembered "No" (replaced-builtins.ts,
3
+ * compaction-keep.ts): a key set to true in `~/.onecode/settings.json`.
4
+ */
5
+
6
+ import os from "node:os";
7
+ import { oneCodeSettingsPath, readSettingsForWrite, writeSettings } from "../lib/one-code-settings.ts";
8
+
9
+ /** Whether the user answered "No" to the question kept under `key`; a malformed settings file reads as not declined. */
10
+ export function declined(key: string): boolean {
11
+ try {
12
+ return readSettingsForWrite(oneCodeSettingsPath(os.homedir()))[key] === true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ /** Remember a "No" to the question kept under `key`. */
19
+ export function recordDeclined(key: string): void {
20
+ const path = oneCodeSettingsPath(os.homedir());
21
+ writeSettings(path, { ...readSettingsForWrite(path), [key]: true });
22
+ }
@@ -14,6 +14,9 @@
14
14
  */
15
15
 
16
16
  import { existsSync, readFileSync } from "node:fs";
17
+ import { ARGUMENT_HINT_CHANNEL, type ArgumentHint, type CommandHint, frontmatterCommandHint, hintForInput, PI_BUILTIN_HINTS } from "../lib/argument-hints.ts";
18
+ import { parseFrontmatterLoosely } from "../lib/frontmatter.ts";
19
+ import { promptTemplateFiles } from "../lib/skill-scan.ts";
17
20
  import { modeCycleKey } from "../lib/keys.ts";
18
21
  import os from "node:os";
19
22
  import { fileURLToPath } from "node:url";
@@ -21,6 +24,8 @@ import { basename, dirname, join } from "node:path";
21
24
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
22
25
  import { getAgentDir, SettingsManager, VERSION as PI_VERSION } from "@earendil-works/pi-coding-agent";
23
26
  import { piVersionWarning } from "../lib/pi-version.ts";
27
+ import { handleCompactionKeep } from "./compaction-keep.ts";
28
+ import { handleReplacedBuiltins } from "./replaced-builtins.ts";
24
29
  import { extensionVersion } from "../lib/package-version.ts";
25
30
  import {
26
31
  formatModel,
@@ -38,9 +43,11 @@ import {
38
43
  shouldDefaultHideThinking,
39
44
  type StartupSection,
40
45
  } from "./startup.ts";
41
- import { linesComponent, safeThemePaint, truncateLine } from "../lib/tui-render.ts";
46
+ import { linesComponent, safeThemeInverse, safeThemePaint, truncateLine } from "../lib/tui-render.ts";
47
+ import { AGENT_VIEW_CHANNEL, type AgentViewAnnouncement, type AgentViewTarget } from "../lib/agent-view.ts";
42
48
  import { markAssistantMarkdown } from "./assistant-marker.ts";
43
49
  import { PROMPT_PADDING, PromptEditor } from "./prompt-editor.ts";
50
+ import { sanitizeTitle } from "../lib/terminal-text.ts";
44
51
 
45
52
  export { truncateLine };
46
53
 
@@ -138,6 +145,19 @@ export function sectionSummary(sections: StartupSection[], paint: (color: string
138
145
  .join(paint("muted", " · "));
139
146
  }
140
147
 
148
+ /** The mode line's words for each permission mode, so the banner never says "default" beside "manual mode on". */
149
+ const MODE_WORDS: Record<string, string> = {
150
+ default: "manual",
151
+ acceptEdits: "accept edits",
152
+ bypassPermissions: "bypass permissions",
153
+ dontAsk: "don't ask",
154
+ };
155
+
156
+ /** The banner's mode field: the permission display with the mode named as the mode line names it. */
157
+ export function bannerModeLabel(display: string): string {
158
+ return MODE_WORDS[display] ?? display;
159
+ }
160
+
141
161
  /**
142
162
  * Banner layout: the wordmark block, then a one-space-indented text column
143
163
  * (tagline + version, context, hints, sections). Kept pure so the layout is
@@ -213,7 +233,11 @@ interface BrandingEditorUI {
213
233
  * a pi-tui render change ever makes the marker misplace (cosmetic only — typing
214
234
  * is never affected; see prompt-marker.ts).
215
235
  */
216
- function installPromptMarker(ctx: { hasUI: boolean; mode: string; ui: BrandingEditorUI }): void {
236
+ function installPromptMarker(
237
+ ctx: { hasUI: boolean; mode: string; ui: BrandingEditorUI; isIdle(): boolean; hasPendingMessages(): boolean },
238
+ argumentHints: ReadonlyMap<string, CommandHint>,
239
+ agentView: { target?: AgentViewTarget },
240
+ ): void {
217
241
  if (process.env.CC_NO_INPUT_MARKER === "1") return;
218
242
  if (!ctx.hasUI || ctx.mode !== "tui") return;
219
243
  const ui = ctx.ui;
@@ -223,7 +247,26 @@ function installPromptMarker(ctx: { hasUI: boolean; mode: string; ui: BrandingEd
223
247
  // Bold makes the chevron read heavier than the input text beside it.
224
248
  return `\x1b[1m${safeThemePaint(ui.theme)("accent", PROMPT_GLYPH)}\x1b[0m${" ".repeat(PROMPT_PADDING - 1)}`;
225
249
  };
226
- ui.setEditorComponent((tui: unknown, theme: unknown, keybindings: unknown) => new PromptEditor(tui as never, theme as never, keybindings as never, renderMarker));
250
+ const renderHint = (text: string) => {
251
+ // While an agent is viewed the empty editor addresses it (lib/agent-view.ts).
252
+ const placeholder = text === "" && agentView.target ? agentView.target.placeholder : hintForInput(text, argumentHints);
253
+ return placeholder === undefined ? undefined : safeThemePaint(ui.theme)("dim", placeholder);
254
+ };
255
+ const badge = () => agentView.target?.badge;
256
+ const paintBadge = (label: string) => safeThemeInverse(ui.theme)(safeThemePaint(ui.theme)("accent", ` ${label} `));
257
+ // The editor can outlive this context across a session replacement; a stale
258
+ // context throws, and ↑ must still reach the editor then.
259
+ const queued = () => {
260
+ try {
261
+ return !ctx.isIdle() && ctx.hasPendingMessages();
262
+ } catch {
263
+ return false;
264
+ }
265
+ };
266
+ ui.setEditorComponent(
267
+ (tui: unknown, theme: unknown, keybindings: unknown) =>
268
+ new PromptEditor(tui as never, theme as never, keybindings as never, renderMarker, renderHint, badge, paintBadge, queued),
269
+ );
227
270
  }
228
271
 
229
272
  /**
@@ -268,6 +311,25 @@ function applyOutputPadDefault(): void {
268
311
  }
269
312
 
270
313
  export default function brandingExtension(pi: ExtensionAPI) {
314
+ /**
315
+ * pi's merged `quietStartup` (global settings with the project's
316
+ * `.pi/settings.json` over them), so the banner and pi agree on whether pi
317
+ * lists its own startup sections. `pi.getSettings()` is in pi 1.0.1; a pi
318
+ * near the 0.84.3 floor without it falls back to the global file alone.
319
+ */
320
+ const effectiveQuietStartup = (agentDir: string): unknown => {
321
+ try {
322
+ if (typeof (pi as { getSettings?: unknown }).getSettings === "function") return pi.getSettings().quietStartup;
323
+ } catch {
324
+ // The banner must still install: fall back to the global file.
325
+ }
326
+ try {
327
+ return (JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as { quietStartup?: unknown }).quietStartup;
328
+ } catch {
329
+ return undefined;
330
+ }
331
+ };
332
+
271
333
  applyThinkingDefault();
272
334
  applyOutputPadDefault();
273
335
  // Mark each block of assistant prose with Claude Code's "●" bullet. Only
@@ -296,7 +358,9 @@ export default function brandingExtension(pi: ExtensionAPI) {
296
358
  const retitle = (ctx: { hasUI: boolean; cwd: string; ui: { setTitle(title: string): void } }) => {
297
359
  if (!live || !ctx.hasUI) return;
298
360
  const name = pi.getSessionName?.()?.trim();
299
- const title = name ? `${NAME} - ${name} - ${basename(ctx.cwd)}` : NAME;
361
+ // pi writes the title inside its OSC 0 and strips only CR and LF; a BEL or
362
+ // ESC in the session name (any source) or the directory would end it early.
363
+ const title = sanitizeTitle(name ? `${NAME} - ${name} - ${basename(ctx.cwd)}` : NAME);
300
364
  if (title === lastTitle) return; // agent_start fires every turn; write only on change
301
365
  lastTitle = title;
302
366
  ctx.ui.setTitle(title);
@@ -307,18 +371,76 @@ export default function brandingExtension(pi: ExtensionAPI) {
307
371
  live = false;
308
372
  });
309
373
 
310
- pi.on("session_start", (_event, ctx) => {
374
+ // Commands' argument placeholders (lib/argument-hints.ts), drawn by the prompt editor.
375
+ // Declared before the CC_NO_BANNER return below: session_start reads it.
376
+ const argumentHints = new Map<string, CommandHint>(Object.entries(PI_BUILTIN_HINTS).map(([command, hint]) => [command, { hint }]));
377
+ // The agent the user is viewing, if any: the editor then targets it (lib/agent-view.ts).
378
+ const agentView: { target?: AgentViewTarget } = {};
379
+ pi.events.on(AGENT_VIEW_CHANNEL, (data) => {
380
+ agentView.target = (data as AgentViewAnnouncement | undefined)?.target;
381
+ });
382
+ pi.events.on(ARGUMENT_HINT_CHANNEL, (data) => {
383
+ const { command, ...hint } = data as ArgumentHint;
384
+ argumentHints.set(command, hint);
385
+ });
386
+ // pi's prompt templates and `/skill:` commands carry their file's
387
+ // `argument-hint` frontmatter. pi lists them only once a turn resolves them,
388
+ // so the template folders are scanned at session_start (the skill extension
389
+ // announces `/skill:` hints from its own scan) and pi's list read each turn.
390
+ // pi runs a built-in or extension command before a command file of the same
391
+ // name, so such a file's hint is never read.
392
+ const hintedFiles = new Set<string>();
393
+ const readFileHints = (files: { name: string; path: string }[], commands = pi.getCommands()) => {
394
+ const extensionCommands = new Set(commands.filter((c) => c.source === "extension").map((c) => c.name));
395
+ for (const { name, path } of files) {
396
+ if (hintedFiles.has(path)) continue;
397
+ if (Object.hasOwn(PI_BUILTIN_HINTS, name) || extensionCommands.has(name)) continue;
398
+ try {
399
+ const hint = frontmatterCommandHint(parseFrontmatterLoosely(readFileSync(path, "utf-8")).frontmatter);
400
+ if (hint) argumentHints.set(name, hint);
401
+ hintedFiles.add(path);
402
+ } catch {
403
+ // Unreadable (mid-replace, say): no hint now; a later scan retries it.
404
+ }
405
+ }
406
+ };
407
+ pi.on("before_agent_start", () => {
408
+ try {
409
+ const commands = pi.getCommands();
410
+ readFileHints(
411
+ commands.filter((c) => c.source !== "extension").map((c) => ({ name: c.name, path: c.sourceInfo.path })),
412
+ commands,
413
+ );
414
+ } catch {
415
+ // Not bound (a torn-down session): the next turn looks again.
416
+ }
417
+ });
418
+ pi.on("session_start", (event, ctx) => {
311
419
  // pi's own write lands after this handler, so only the deferred write
312
420
  // sticks; a new session may carry a name (resume), so forget the last.
313
421
  lastTitle = undefined;
314
422
  live = true;
315
423
  setTimeout(() => retitle(ctx), 0).unref?.();
316
424
  ctx.ui.setHiddenThinkingLabel(THINKING_LABEL);
317
- installPromptMarker(ctx as unknown as { hasUI: boolean; mode: string; ui: BrandingEditorUI });
425
+ installPromptMarker(ctx as unknown as Parameters<typeof installPromptMarker>[0], argumentHints, agentView);
426
+ try {
427
+ readFileHints(promptTemplateFiles(ctx.cwd, os.homedir(), getAgentDir()));
428
+ } catch {
429
+ // Commands not bound: nothing was marked read, so the next turn reads pi's list.
430
+ }
318
431
  // Soft drift guard: warn once at startup when the hosting pi is outside
319
432
  // the range this release was tested against (see lib/pi-version.ts).
320
433
  const versionWarning = piVersionWarning(PI_VERSION);
321
434
  if (versionWarning) ctx.ui.notify(versionWarning, "warning");
435
+ // pi 0.99 skips its built-in tool search and MCP for ours and warns at
436
+ // every start, advising the user to remove One Code: offer to turn them
437
+ // off once, else answer with the notice (replaced-builtins.ts). Only
438
+ // when pi resolves its built-ins again (startup, /reload), not on a
439
+ // /new, resume or fork, which re-run this handler too. Not awaited: a
440
+ // dialog must not hold up startup. The compaction keep-window question
441
+ // (compaction-keep.ts) follows it, so the two dialogs never overlap.
442
+ const reason = (event as { reason?: string }).reason ?? "startup";
443
+ if (reason === "startup" || reason === "reload") void handleReplacedBuiltins(ctx).then(() => handleCompactionKeep(ctx));
322
444
  });
323
445
 
324
446
  if (process.env.CC_NO_BANNER === "1") return;
@@ -363,9 +485,11 @@ export default function brandingExtension(pi: ExtensionAPI) {
363
485
  // instead (minus the internal [Extensions] noise).
364
486
  const home = os.homedir();
365
487
  // Resolve pi's live agent dir (honours PI_CODING_AGENT_DIR) so an
366
- // isolated one-code app reads its own settings/skills, never ~/.pi.
488
+ // isolated one-code app reads its own skills, never ~/.pi.
367
489
  const agentDir = getAgentDir();
368
- const sections = quietStartupEnabled(join(agentDir, "settings.json"))
490
+ // Use pi's effective settings, including project overrides, so the two
491
+ // listings agree on whether quiet startup is enabled.
492
+ const sections = quietStartupEnabled(effectiveQuietStartup(agentDir))
369
493
  ? collectStartupSections(ctx.cwd, home, join(dirname(fileURLToPath(import.meta.url)), "..", "..", "themes"), agentDir)
370
494
  : undefined;
371
495
 
@@ -387,7 +511,7 @@ export default function brandingExtension(pi: ExtensionAPI) {
387
511
  version,
388
512
  model: modelLabel ?? "none",
389
513
  cwd: ctx.cwd,
390
- mode: permissionModeDisplay(permissionStatus ?? { mode: "default", paused: false }),
514
+ mode: bannerModeLabel(permissionModeDisplay(permissionStatus ?? { mode: "default", paused: false })),
391
515
  subagents:
392
516
  modelLabel === undefined
393
517
  ? "none"