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
@@ -4,23 +4,30 @@
4
4
  * The doctor does not re-decide anything: it calls the SAME resolvers the
5
5
  * session uses — `resolveSubagentModel` for delegated work, `classifierCandidates`
6
6
  * for auto mode's screener, `pickEconomicalContainedModel` for the web-fetch /
7
- * recap reader, `resolveModelTier` for the prompt register — and reports their
8
- * answers next to their sources. A user who asks "why did my subagent run on
9
- * Haiku?" gets the resolver's own notice, not a paraphrase.
7
+ * recap reader — and reports their answers next to their sources. The prompt
8
+ * register comes from the session's frozen tier, or `resolveModelTier` in the
9
+ * CLI. A user who asks "why did my subagent run on Haiku?" gets the resolver's
10
+ * own notice, not a paraphrase.
10
11
  */
11
12
 
12
13
  import type { Api, Model } from "@earendil-works/pi-ai";
13
14
  import { modeCycleKey } from "../lib/keys.ts";
14
15
  import { loadAutoModeConfig } from "../auto-mode/config.ts";
15
16
  import { classifierCandidates, describeCandidate, type ClassifierNotice } from "../auto-mode/model-select.ts";
16
- import { ATTRIBUTION, capabilityFloor, configuredCapabilityKey, type FloorRole, type FloorVerdict, KEY_ADVICE, snapshotAgeMs } from "../lib/capability-index.ts";
17
- import { modelSpec, pricedInput } from "../lib/model-policy.ts";
17
+ import { chosenModelWarnings, type ChosenModelWarning } from "../lib/model-choice-warnings.ts";
18
+ import { loadCatalogSources, readCatalogRefreshEnabled } from "../lib/model-catalog-data.ts";
19
+ import { autoSelectSkipReason, catalogModelFor } from "../lib/model-catalog.ts";
20
+ import { DAY_MS, modelsContainedToSession, modelSpec, pricedInput, supportsImageInput } from "../lib/model-policy.ts";
21
+ import { newerModelSuggestion, type NewerModelSuggestion } from "../lib/newer-model.ts";
22
+ import { oneCodeSettingsPath, readSuggestNewerModels } from "../lib/one-code-settings.ts";
23
+ import { oneCodeStateDir } from "../lib/paths.ts";
18
24
  import {
19
- currentCapabilitySnapshot,
25
+ classifyModelTier,
20
26
  intrinsicTier,
21
27
  pickEconomicalContainedModel,
22
28
  type PromptTier,
23
29
  resolveModelTier,
30
+ servedCatalogIds,
24
31
  tierOverride,
25
32
  } from "../lib/model-tier.ts";
26
33
  import { applicableSubagentDefault, loadSubagentDefault, type SubagentDefault } from "../subagents/default-model.ts";
@@ -30,6 +37,7 @@ import { contextLabel, type Finding, priceLabel, type ReportLine, type ReportSec
30
37
  export interface ModelFacts {
31
38
  session?: Model<Api>;
32
39
  sessionTier?: PromptTier;
40
+ newerModel?: NewerModelSuggestion;
33
41
  /** The register the system prompt is built in — the tier, or a CC_PROMPT_TIER override. */
34
42
  promptTier: PromptTier;
35
43
  promptTierForced: boolean;
@@ -37,20 +45,18 @@ export interface ModelFacts {
37
45
  subagentConfigured?: SubagentDefault;
38
46
  /** The configured default exists but does not apply to this session (Claude Code's env var on a non-Claude model). */
39
47
  subagentConfiguredInapplicable: boolean;
48
+ /** What the configured subagent default costs (newer model, far weaker than the session), when it is in effect. */
49
+ subagentWarnings: ChosenModelWarning[];
50
+ /** `configured`: the user's `autoMode.classifierModel`, as written. */
40
51
  classifier: { model?: Model<Api>; description?: string; notices: ClassifierNotice[]; configured?: string };
41
52
  reader?: { model: Model<Api>; via: "tier" | "session" };
42
- /**
43
- * The optional Artificial Analysis snapshot behind the measured capability
44
- * floor (lib/capability-index.ts): whether a key is configured, the snapshot's
45
- * age, and the verdict each automatic pick was judged on.
46
- */
47
- capability: {
48
- keyConfigured: boolean;
49
- snapshot?: { fetchedAt: string; rows: number };
50
- subagent?: FloorVerdict;
51
- classifier?: FloorVerdict;
52
- reader?: FloorVerdict;
53
- };
53
+ /** The session model's own tier and the rule that decided it (`classifyModelTier`, no CC_PROMPT_TIER). */
54
+ tierReason?: string;
55
+ /** Whether the public model catalogs know the session model, and why automatic picks skip it, if they do. */
56
+ inCatalog: boolean;
57
+ skipReason?: string;
58
+ /** The model catalogs in use (lib/model-catalog-data.ts). */
59
+ catalog: { fetchedAt: string; refreshed: boolean; refreshEnabled: boolean };
54
60
  }
55
61
 
56
62
  export const TIER_LABEL: Record<PromptTier, string> = {
@@ -64,35 +70,42 @@ export function collectModelFacts(available: Model<Api>[], session: SessionView,
64
70
  const sessionModel = session.model;
65
71
  const configuredAll = loadSubagentDefault(home, env);
66
72
  const configured = applicableSubagentDefault(configuredAll, sessionModel);
67
- const subagent = resolveSubagentModel({ configuredDefault: configured, sessionModel, available });
73
+ // The live resolver's image gate (subagents/index.ts), so the preview names what a spawn gets.
74
+ const subagent = resolveSubagentModel({ configuredDefault: configured, sessionModel, available, requireImageInput: supportsImageInput(sessionModel) });
75
+ const suggestNewer = readSuggestNewerModels(home, env);
68
76
  const autoConfig = loadAutoModeConfig(home);
69
77
  const chain = classifierCandidates({
70
78
  available,
71
79
  sessionModel,
72
80
  configured: autoConfig.classifierModel,
73
81
  configuredSetForContainment: autoConfig.classifierModelSetFor,
82
+ suggestNewer,
74
83
  });
75
84
  const first = chain.candidates[0];
76
85
  const reader = pickEconomicalContainedModel(available, sessionModel);
77
- const snapshot = currentCapabilitySnapshot();
78
- const verdict = (pick: Model<Api> | undefined, role: FloorRole): FloorVerdict | undefined =>
79
- snapshot && sessionModel && pick && modelSpec(pick) !== modelSpec(sessionModel) ? capabilityFloor(snapshot, pick, sessionModel, role) : undefined;
80
- const capability: ModelFacts["capability"] = {
81
- keyConfigured: configuredCapabilityKey(home, env) !== undefined,
82
- snapshot: snapshot ? { fetchedAt: snapshot.fetchedAt, rows: snapshot.rows.length } : undefined,
83
- subagent: verdict(subagent.source === "automatic" ? subagent.model : undefined, "subagent"),
84
- classifier: verdict(first?.source === "economical" ? first.model : undefined, "classifier"),
85
- reader: verdict(reader?.via === "tier" ? reader.model : undefined, "reader"),
86
- };
86
+ const sources = loadCatalogSources(oneCodeStateDir(env, home));
87
+ const entry = sessionModel ? catalogModelFor(sessionModel) : undefined;
87
88
  return {
88
- capability,
89
+ catalog: {
90
+ fetchedAt: sources.modelsDev.fetchedAt,
91
+ refreshed: sources.refreshed === true,
92
+ refreshEnabled: readCatalogRefreshEnabled(oneCodeSettingsPath(home, env), env),
93
+ },
94
+ tierReason: sessionModel ? classifyModelTier(sessionModel, {}, "capability").reason : undefined,
95
+ inCatalog: entry !== undefined,
96
+ skipReason: entry && sessionModel ? autoSelectSkipReason(entry, servedCatalogIds(modelsContainedToSession(available, sessionModel))) : undefined,
97
+ newerModel: suggestNewer ? newerModelSuggestion(available, sessionModel) : undefined,
89
98
  session: sessionModel,
90
99
  sessionTier: sessionModel ? intrinsicTier(sessionModel) : undefined,
91
- promptTier: resolveModelTier(sessionModel, env),
100
+ promptTier: session.promptTier ?? resolveModelTier(sessionModel, env),
92
101
  promptTierForced: tierOverride(env) !== undefined,
93
102
  subagent,
94
103
  subagentConfigured: configuredAll,
95
104
  subagentConfiguredInapplicable: configuredAll !== undefined && configured === undefined,
105
+ subagentWarnings:
106
+ subagent.source === "default" && subagent.model
107
+ ? chosenModelWarnings({ available, sessionModel, chosen: subagent.model, role: "subagent", suggestNewer })
108
+ : [],
96
109
  classifier: {
97
110
  model: first?.model,
98
111
  description: first ? describeCandidate(first) : undefined,
@@ -107,8 +120,8 @@ const SUBAGENT_SOURCE: Record<SubagentModelResolution["source"], string> = {
107
120
  call: "named per call",
108
121
  agent: "from the agent file",
109
122
  default: "the configured default",
110
- automatic: "automatic: cheapest capable model on this provider, cheaper than the main model",
111
- session: "the main model — nothing cheaper and capable on this provider",
123
+ automatic: "automatic: cheapest model on this provider in the main model's tier or above, cheaper than it",
124
+ session: "the main model — nothing cheaper on this provider in its tier or above",
112
125
  };
113
126
 
114
127
  export function modelsSection(facts: ModelFacts, session: SessionView, findings: Finding[]): ReportSection {
@@ -133,11 +146,25 @@ export function modelsSection(facts: ModelFacts, session: SessionView, findings:
133
146
  : "";
134
147
  const details = [priceLabel(main), contextLabel(main.contextWindow), sourceNote].filter(Boolean).join(" · ");
135
148
  lines.push({ text: `Main: ${modelSpec(main)} — ${details}`, level: "ok" });
149
+ if (facts.newerModel) {
150
+ findings.push({ level: "warn", text: facts.newerModel.text, fix: facts.newerModel.fix });
151
+ }
136
152
  lines.push({
137
153
  text: `Prompt register: ${TIER_LABEL[facts.promptTier]}${facts.promptTierForced ? " (forced by CC_PROMPT_TIER)" : ""}`,
138
154
  indent: 1,
139
155
  level: "dim",
140
156
  });
157
+ if (facts.sessionTier && facts.tierReason) {
158
+ lines.push({ text: `Tier: ${facts.sessionTier} — ${facts.tierReason}`, indent: 1, level: "dim" });
159
+ }
160
+ if (facts.skipReason) lines.push({ text: `Automatic picks skip this model: ${facts.skipReason}`, indent: 1, level: "dim" });
161
+ if (!facts.inCatalog && facts.tierReason !== "modelTiers setting" && facts.sessionTier !== "frontier") {
162
+ findings.push({
163
+ level: "warn",
164
+ text: `${modelSpec(main)} is not in the public model catalogs (models.dev, OpenRouter), so its tier is a guess (${facts.sessionTier}) and automatic picks never choose it.`,
165
+ fix: `Set its tier in ~/.onecode/settings.json: { "modelTiers": { "${modelSpec(main)}": "workhorse" } } (frontier, workhorse, cheap or tiny).`,
166
+ });
167
+ }
141
168
  if (session.thinkingLevel) lines.push({ text: `Effort: ${session.thinkingLevel} (/effort or shift+tab to change)`, indent: 1, level: "dim" });
142
169
 
143
170
  const sub = facts.subagent;
@@ -150,11 +177,23 @@ export function modelsSection(facts: ModelFacts, session: SessionView, findings:
150
177
  indent: 1,
151
178
  level: facts.subagentConfiguredInapplicable ? "warn" : "dim",
152
179
  });
180
+ // A warning line is a finding too, so the summary never says "No issues found" beside it.
181
+ if (facts.subagentConfiguredInapplicable) {
182
+ findings.push({
183
+ level: "warn",
184
+ text: `${knob} sets "${facts.subagentConfigured.spec}" for subagents, but it applies only when the main model is a Claude model, so it is not used here.`,
185
+ fix: "Choose the subagent model with /subagent (it writes subagentModel in ~/.onecode/settings.json), or unset the variable.",
186
+ });
187
+ }
153
188
  }
154
189
  for (const notice of sub.notices) {
155
190
  lines.push({ text: notice, indent: 1, level: "warn" });
156
191
  findings.push({ level: "warn", text: `Subagent model: ${notice}`, fix: "Re-set the default with /subagent on this session, or /subagent clear." });
157
192
  }
193
+ for (const warning of facts.subagentWarnings) {
194
+ lines.push({ text: warning.text, indent: 1, level: "warn" });
195
+ findings.push({ level: "warn", text: warning.text, fix: warning.fix });
196
+ }
158
197
  } else {
159
198
  lines.push({ text: "Subagents: no model resolves", level: "error" });
160
199
  }
@@ -164,50 +203,50 @@ export function modelsSection(facts: ModelFacts, session: SessionView, findings:
164
203
  const live = session.permission?.classifier;
165
204
  const pinned = session.permission?.pinned && live ? ` — screening this session on ${live}` : "";
166
205
  lines.push({ text: `Auto-mode classifier: ${classifier.description ?? modelSpec(classifier.model)}${pinned}`, level: "ok" });
206
+ if (classifier.configured) {
207
+ lines.push({ text: `Setting: "${classifier.configured}" via autoMode.classifierModel in ~/.onecode/settings.json (/auto-mode model clear returns to automatic)`, indent: 1, level: "dim" });
208
+ }
209
+ lines.push({
210
+ text: "Classifier policy: for the models One Code's pairing table names, the table's pick on this provider (or this session's model), unless it costs more than this session's model or its window is smaller; otherwise the cheapest model on this provider in this session's tier or above, strictly cheaper than it, with a context window at least as large, not an experimental build and not a moving -latest alias, else this session's model; /auto-mode model chooses one by hand.",
211
+ indent: 1,
212
+ level: "dim",
213
+ });
167
214
  } else {
168
215
  lines.push({ text: "Auto-mode classifier: none — auto mode stays out of the mode cycle until a model is available", level: "warn" });
216
+ findings.push({
217
+ level: "warn",
218
+ text: "No auto-mode classifier model resolves, so auto mode stays out of the mode cycle.",
219
+ fix: "Choose one with /auto-mode model, or connect a provider with /login.",
220
+ });
169
221
  }
170
222
  for (const notice of classifier.notices) {
171
223
  lines.push({ text: notice.text, indent: 1, level: notice.level === "warning" ? "warn" : "dim" });
172
224
  if (notice.level === "warning") {
173
- findings.push({ level: "warn", text: `Classifier: ${notice.text}`, fix: "Pick a model with /auto-mode model, or /auto-mode model clear for the automatic choice." });
225
+ findings.push({
226
+ level: "warn",
227
+ text: `Classifier: ${notice.text}`,
228
+ fix: classifier.configured
229
+ ? "Change it with /auto-mode model, or /auto-mode model clear for the automatic choice."
230
+ : "Check provider authentication and model availability, or choose one with /auto-mode model.",
231
+ });
174
232
  }
175
233
  }
176
234
 
177
235
  if (facts.reader) {
178
236
  lines.push({
179
- text: `Web-fetch and recap reader: ${modelSpec(facts.reader.model)} — ${facts.reader.via === "tier" ? "cheapest capable model on this provider" : "the main model"}`,
237
+ text: `Web-fetch and recap reader: ${modelSpec(facts.reader.model)} — ${facts.reader.via === "tier" ? "the cheapest cheap-tier model on this provider that costs no more than the main model (a workhorse or frontier one when there is none)" : "the main model"}`,
180
238
  level: "dim",
181
239
  });
182
240
  }
183
241
 
184
- // The measured capability floor: what each automatic pick was judged on, or
185
- // how to switch it on. Scores shown carry the attribution the free API requires.
186
- const cap = facts.capability;
187
- const verdictLine = (label: string, v: FloorVerdict | undefined): ReportLine | undefined => {
188
- if (!v) return undefined;
189
- if (v.verdict === "unscored") return { text: `${label}: not measured — ${v.reason ?? "no confirmed score"}; the name-class rule decided`, indent: 1, level: "dim" };
190
- const basis = v.candidate?.variant === "non-reasoning" ? "thinking-off" : "default-effort";
191
- return {
192
- text: `${label}: coding index ${v.candidate?.coding} vs floor ${v.floor?.toFixed(1)} (${basis}; session ${v.session?.coding}, Sonnet 5 ${v.reference?.coding}) — ${v.verdict === "pass" ? "measured capable" : "below the floor"}`,
193
- indent: 1,
194
- level: "dim",
195
- };
196
- };
197
- if (cap.snapshot) {
198
- const ageH = Math.round(snapshotAgeMs({ fetchedAt: cap.snapshot.fetchedAt, source: "", rows: [] }) / 3_600_000);
199
- lines.push({ text: `Capability scores: Artificial Analysis snapshot, ${cap.snapshot.rows} models, ${ageH} h old — measured picks for subagents and the classifier`, level: "ok" });
200
- for (const line of [verdictLine("Subagent pick", cap.subagent), verdictLine("Classifier pick", cap.classifier), verdictLine("Reader pick", cap.reader)]) {
201
- if (line) lines.push(line);
202
- }
203
- lines.push({ text: ATTRIBUTION, indent: 1, level: "dim" });
204
- } else if (cap.keyConfigured) {
205
- lines.push({ text: "Capability scores: key configured, snapshot not fetched yet — it downloads in the background on the next interactive start", level: "dim" });
206
- } else {
207
- lines.push({ text: "Capability scores: none — automatic picks use model names and generations only", level: "warn" });
208
- findings.push({ level: "warn", text: "No Artificial Analysis key: subagent and classifier picks cannot be judged by measured coding ability.", fix: KEY_ADVICE });
209
- }
210
-
242
+ // The catalogs every tier below frontier comes from, and how fresh they are.
243
+ const cat = facts.catalog;
244
+ const ageDays = Math.floor((Date.now() - Date.parse(cat.fetchedAt)) / DAY_MS);
245
+ const age = ageDays <= 0 ? "today" : ageDays === 1 ? "1 day ago" : `${ageDays} days ago`;
246
+ lines.push({
247
+ text: `Model catalogs: models.dev, OpenRouter and Hugging Face, fetched ${age} (${cat.refreshed ? "refreshed by One Code" : "bundled with this release"})${cat.refreshEnabled ? "; refreshed daily from interactive sessions" : "; daily refresh off (refreshModelCatalog: false, or PI_OFFLINE)"}`,
248
+ level: "dim",
249
+ });
211
250
  if (session.permission) {
212
251
  const mode = session.permission.mode;
213
252
  const from = session.permission.source ? ` (${session.permission.source})` : "";
@@ -217,7 +256,7 @@ export function modelsSection(facts: ModelFacts, session: SessionView, findings:
217
256
  if (facts.sessionTier === "tiny") {
218
257
  findings.push({
219
258
  level: "warn",
220
- text: `${modelSpec(main)} is classed as a tiny-tier model: One Code adds weak-model scaffolding and search tools, and never auto-selects a tiny model for subagents or the classifier.`,
259
+ text: `${modelSpec(main)} is classed as a tiny-tier model (a small model); automatic alternatives exclude tiny models, but the session model remains the classifier fallback.`,
221
260
  fix: "For coding work pick a cheap- or workhorse-tier model with /model; see /doctor presets.",
222
261
  });
223
262
  }
@@ -6,30 +6,22 @@
6
6
  *
7
7
  * Presets never leave the current provider family: every automatic model choice
8
8
  * in One Code is contained to the session's provider (a subagent inherits the
9
- * transcript, the classifier reads the prompts — docs/decisions/model-policy.md),
9
+ * transcript, the classifier reads the prompts — working-docs/decisions/model-policy.md),
10
10
  * and a preset is a bundle of those same choices. A user who wants another
11
11
  * provider switches with /model first and reruns /doctor presets there.
12
12
  *
13
- * Each preset pins ONE thing — the main model — and sets the two secondary
14
- * knobs to the shape that matches its intent:
15
- * economical main = cheapest capable (cheap tier, never tiny); subagents inherit it;
16
- * classifier automatic (lands on the same model)
17
- * balanced main = cheapest workhorse-tier model; subagents automatic (the
18
- * cheapest model at the main's floor — often the main itself,
19
- * since subagents share the classifier's workhorse floor);
20
- * classifier automatic (workhorse floor)
21
- * maximum quality main = the strongest (frontier, else priciest workhorse);
22
- * subagents inherit it; classifier automatic
23
- * so applying a preset is three settings writes the user can each undo by hand
24
- * (/model, /subagent, /auto-mode model). The rows display what the resolvers
25
- * would pick for that main model, computed with the resolvers themselves.
13
+ * Each preset pins the main model, sets the subagent default to the shape that
14
+ * matches its intent, and returns the classifier to the automatic choice
15
+ * (clearing `autoMode.classifierModel`); the rows display what its resolver
16
+ * would pick for that main model.
26
17
  */
27
18
 
28
19
  import type { Api, Model } from "@earendil-works/pi-ai";
29
20
  import { classifierCandidates } from "../auto-mode/model-select.ts";
30
- import { isPriorGeneration, lacksToolCalls } from "../lib/model-facts.ts";
31
- import { isDatedDuplicate, modelsContainedToSession, modelSpec, pricedInput } from "../lib/model-policy.ts";
32
- import { intrinsicTier, type PromptTier } from "../lib/model-tier.ts";
21
+ import { autoSelectable, catalogModelFor } from "../lib/model-catalog.ts";
22
+ import { isDatedDuplicate, isExperimentalBuild, modelsContainedToSession, modelSpec, pricedInput, supportsImageInput } from "../lib/model-policy.ts";
23
+ import { intrinsicTier, type PromptTier, servedCatalogIds } from "../lib/model-tier.ts";
24
+ import { newerModelSuggestion } from "../lib/newer-model.ts";
33
25
  import { resolveSubagentModel } from "../subagents/model-select.ts";
34
26
  import type { ReportLine, ReportSection } from "./report.ts";
35
27
 
@@ -44,7 +36,7 @@ export const PRESET_LABEL: Record<PresetName, string> = {
44
36
  };
45
37
 
46
38
  export const PRESET_INTENT: Record<PresetName, string> = {
47
- economical: "lowest cost — one cheap model for everything",
39
+ economical: "lowest-cost main model, with subagents inheriting it",
48
40
  balanced: "a capable main model, delegated work on the cheapest model at its floor",
49
41
  quality: "the strongest model for the main session and its subagents",
50
42
  };
@@ -71,10 +63,26 @@ export type PresetsUnavailable = "no-model" | "no-priced-models";
71
63
  * to `claude-haiku-4-5`). Tiny-tier rows stay in the pool only as a last resort.
72
64
  */
73
65
  export function presetPool(available: Model<Api>[], sessionModel: Model<Api>): Model<Api>[] {
74
- const contained = modelsContainedToSession(available, sessionModel).filter(
75
- // A preset recommends a MAIN model: never one a generation behind its family
76
- // or one models.dev says cannot call tools (model-facts.ts), whatever its tier.
77
- (m) => pricedInput(m) !== undefined && !isPriorGeneration(m) && !lacksToolCalls(m),
66
+ const all = modelsContainedToSession(available, sessionModel);
67
+ const served = servedCatalogIds(all);
68
+ // The newer-model check flags a main model with a newer model of its line at
69
+ // no more than 1.1× its price; supersession's band is 0.6–1.5×, so GPT-5.6
70
+ // Sol survives it against the half-price GPT-6.1 Sol. A preset never
71
+ // recommends a model /doctor would then call outdated, nor an alias of one
72
+ // (`gpt-daybreak-blue-latest` serves gpt-5.6-sol today).
73
+ const identity = (m: Model<Api>): string => catalogModelFor(m)?.id ?? modelSpec(m);
74
+ const outdated = new Set(all.filter((m) => newerModelSuggestion(available, m)).map(identity));
75
+ const contained = all.filter(
76
+ // A preset recommends a MAIN model: never one automatic selection skips
77
+ // (superseded by a model this provider serves, legacy, deprecated, unable
78
+ // to call tools, not a text model: model-catalog.ts), whatever its tier.
79
+ // A model no catalog knows stays, judged by its tier alone.
80
+ (m) => {
81
+ // An experimental build is out whether or not a catalog knows it.
82
+ if (pricedInput(m) === undefined || outdated.has(identity(m)) || isExperimentalBuild(m.id)) return false;
83
+ const entry = catalogModelFor(m);
84
+ return !entry || autoSelectable(entry, served);
85
+ },
78
86
  );
79
87
  return contained.filter((m) => !isDatedDuplicate(m, contained));
80
88
  }
@@ -97,7 +105,10 @@ function pickMain(name: PresetName, pool: Model<Api>[]): { model: Model<Api>; no
97
105
  return tiny ? { model: tiny, note: "only tiny-tier models on this provider — expect weaker results" } : undefined;
98
106
  }
99
107
  case "balanced": {
100
- const pick = cheapest(workhorse) ?? cheapest(frontier) ?? priciest(cheap);
108
+ // The cheapest workhorse-or-better model: a frontier model cheaper than
109
+ // every workhorse one (GPT-6.1 Sol against the GPT-5.5 Pro SKU) is the
110
+ // better buy, not a reason to pay for the premium SKU.
111
+ const pick = cheapest([...workhorse, ...frontier]) ?? priciest(cheap);
101
112
  if (pick) return { model: pick, note: workhorse.length === 0 ? "no workhorse-tier model on this provider" : undefined };
102
113
  const any = priciest(pool);
103
114
  return any ? { model: any, note: "only tiny-tier models on this provider — expect weaker results" } : undefined;
@@ -139,7 +150,7 @@ export function computePresets(
139
150
  // what applying the preset produces.
140
151
  const subagentModel = inherit
141
152
  ? main.model
142
- : (resolveSubagentModel({ sessionModel: main.model, available }).model ?? main.model);
153
+ : (resolveSubagentModel({ sessionModel: main.model, available, requireImageInput: supportsImageInput(main.model) }).model ?? main.model);
143
154
  const classifier = classifierCandidates({ available, sessionModel: main.model }).candidates[0]?.model;
144
155
  presets.push({
145
156
  name,
@@ -173,6 +184,10 @@ export function presetsSection(result: ReturnType<typeof computePresets>, sessio
173
184
  lines.push({ text: "No priced models on this provider, so tiers cannot be told apart and no preset is offered.", level: "dim" });
174
185
  return { title: "Presets", lines };
175
186
  }
187
+ lines.push({
188
+ text: "Classifier previews use each preset's main model: the cheapest model in its tier or above that is strictly cheaper than the main model, else the main model itself.",
189
+ level: "dim",
190
+ });
176
191
  const family = sessionModel ? sessionModel.provider : "";
177
192
  for (const preset of result.presets) {
178
193
  const sub = preset.subagents.setting === "inherit" ? "same" : shortId(preset.subagents.model);
@@ -193,9 +208,9 @@ export function presetsSection(result: ReturnType<typeof computePresets>, sessio
193
208
  }
194
209
 
195
210
  /**
196
- * The three settings writes a preset amounts to, one line each with its undo —
197
- * the text `/doctor preset` reports after applying. `mainSwitched` false means
198
- * the session already ran the preset's main model, so that line says so.
211
+ * The three settings changes a preset makes, each with its undo — the text
212
+ * `/doctor preset` reports after applying. `mainSwitched` false means the
213
+ * session already ran the preset's main model, so that line says so.
199
214
  */
200
215
  export function describePresetChanges(preset: PresetPlan, mainSwitched = true): string[] {
201
216
  return [
@@ -9,17 +9,22 @@
9
9
  * Claude Code never has to explain: which provider is ready, which model each
10
10
  * role (main, subagents, classifier, reader) resolves to and why, what of the
11
11
  * imported Claude Code configuration is honoured, and which external programs
12
- * are missing. Rationale: docs/decisions/doctor.md.
12
+ * are missing. Rationale: working-docs/decisions/doctor.md.
13
13
  *
14
14
  * No pi imports. The wiring (index.ts, cli.ts) gathers the live inputs and hands
15
15
  * them to `buildDoctorReport`; everything below is deterministic given them.
16
16
  */
17
17
 
18
18
  import type { Api, Model } from "@earendil-works/pi-ai";
19
+ import type { PromptTier } from "../lib/model-tier.ts";
19
20
  import { tildify } from "../lib/paths.ts";
20
21
  import type { McpStatusEvent } from "../lib/mcp-status.ts";
21
- import { wrapProse } from "../subagents/panel-render.ts";
22
+ import type { ReplacedBuiltinsView } from "./builtins.ts";
23
+ import type { CompactionKeepView } from "../lib/compaction-keep.mjs";
24
+ import type { RegisteredServer } from "../mcp/config.ts";
25
+ import { wrapProse } from "../lib/tui-render.ts";
22
26
  import { countNoun } from "../lib/tui-render.ts";
27
+ import type { ConfigMode } from "../lib/config-mode.ts";
23
28
  export { countNoun };
24
29
 
25
30
  export type LineLevel = "ok" | "info" | "warn" | "error" | "dim";
@@ -63,6 +68,8 @@ export interface DoctorEnvironment {
63
68
  agentDir: string;
64
69
  /** One Code's own state dir (~/.onecode or ONECODE_STATE_DIR). */
65
70
  stateDir: string;
71
+ /** Where configuration is read from (lib/config-mode.ts). */
72
+ configMode: ConfigMode;
66
73
  env: NodeJS.ProcessEnv;
67
74
  platform: string;
68
75
  arch: string;
@@ -71,6 +78,14 @@ export interface DoctorEnvironment {
71
78
  /** How One Code was launched: the bundled `onecode` app or the extension on the user's own pi. */
72
79
  install: "app" | "pi-package";
73
80
  piVersion?: string;
81
+ /** pi's built-ins One Code replaces and whether pi still loads them; undefined before pi 0.99 (doctor/builtins.ts). */
82
+ replacedBuiltins?: ReplacedBuiltinsView;
83
+ /** pi's compaction keep window and where it is set (lib/compaction-keep.mjs); undefined when the settings could not be read. */
84
+ compactionKeep?: CompactionKeepView & { paths: { user: string; project: string } };
85
+ /** pi's `CONFIG_DIR_NAME`, the project folder its own `mcp.json` sits in (`.pi` when absent). */
86
+ piConfigDirName?: string;
87
+ /** MCP servers other extensions registered in this session (`pi.getMcpServers()`); the CLI has none. */
88
+ registeredMcpServers?: RegisteredServer[];
74
89
  /** Result of the registry lookup for the newest release, when one was attempted. */
75
90
  latest?: { status: "current" | "behind" | "unknown" | "skipped"; version?: string; reason?: string };
76
91
  }
@@ -88,6 +103,8 @@ export interface RegistryView {
88
103
  /** Live session facts; the CLI fills what it can from settings and leaves the rest undefined. */
89
104
  export interface SessionView {
90
105
  model?: Model<Api>;
106
+ /** The running session's frozen register; absent in the standalone CLI. */
107
+ promptTier?: PromptTier;
91
108
  /** Where `model` came from: the running session, pi's saved default, or nowhere. */
92
109
  modelSource: "session" | "default-setting" | "first-available" | "none";
93
110
  thinkingLevel?: string;
@@ -4,10 +4,13 @@
4
4
  * an undiscoverable key). The wiring owns repaint and the fix/close effects.
5
5
  */
6
6
 
7
- import { panelTopRule } from "../lib/tui-render.ts";
7
+ import { panelTopRule, wrapHints } from "../lib/tui-render.ts";
8
+ import { keyId, keyText } from "../lib/key-input.ts";
8
9
 
9
10
  export interface DoctorViewerState {
10
11
  offset: number;
12
+ /** Body rows the last render showed (the hint row can wrap); the keys page by it. */
13
+ visibleRows?: number;
11
14
  }
12
15
 
13
16
  export type DoctorViewerKey =
@@ -21,38 +24,43 @@ export type DoctorViewerKey =
21
24
  | { kind: "close" };
22
25
 
23
26
  export function decodeDoctorKey(data: string): DoctorViewerKey | undefined {
24
- switch (data) {
25
- case "\x1b[A":
26
- case "\x1bOA":
27
+ switch (keyId(data)) {
28
+ case "up":
29
+ return { kind: "up" };
30
+ case "down":
31
+ return { kind: "down" };
32
+ case "pageUp":
33
+ case "ctrl+b":
34
+ return { kind: "pageUp" };
35
+ case "pageDown":
36
+ case "ctrl+f":
37
+ case "space":
38
+ return { kind: "pageDown" };
39
+ case "home":
40
+ return { kind: "home" };
41
+ case "end":
42
+ return { kind: "end" };
43
+ case "escape":
44
+ case "ctrl+c":
45
+ case "enter":
46
+ return { kind: "close" };
47
+ default:
48
+ break;
49
+ }
50
+ switch (keyText(data)) {
27
51
  case "k":
28
52
  return { kind: "up" };
29
- case "\x1b[B":
30
- case "\x1bOB":
31
53
  case "j":
32
54
  return { kind: "down" };
33
- case "\x1b[5~":
34
- case "\x02": // ctrl+b
35
55
  case "b":
36
56
  return { kind: "pageUp" };
37
- case "\x1b[6~":
38
- case "\x06": // ctrl+f
39
- case " ":
40
- return { kind: "pageDown" };
41
- case "\x1b[H":
42
- case "\x1b[1~":
43
57
  case "g":
44
58
  return { kind: "home" };
45
- case "\x1b[F":
46
- case "\x1b[4~":
47
59
  case "G":
48
60
  return { kind: "end" };
49
61
  case "f":
50
62
  return { kind: "fix" };
51
- case "\x1b":
52
- case "\x03": // ctrl+c
53
63
  case "q":
54
- case "\r":
55
- case "\n":
56
64
  return { kind: "close" };
57
65
  default:
58
66
  return undefined;
@@ -102,7 +110,7 @@ export interface DoctorViewerInput {
102
110
  canFix: boolean;
103
111
  }
104
112
 
105
- /** Rows taken by the top rule and the footer hint. */
113
+ /** Rows taken by the top rule, the blank above the hints and a one-line hint row. */
106
114
  export const VIEWER_CHROME_ROWS = 3;
107
115
 
108
116
  export function visibleBodyRows(height: number): number {
@@ -111,14 +119,28 @@ export function visibleBodyRows(height: number): number {
111
119
 
112
120
  export function renderDoctorViewer(input: DoctorViewerInput, paint: (color: string, text: string) => string): string[] {
113
121
  const { lines, state, width, height } = input;
114
- const visible = visibleBodyRows(height);
122
+ // PageUp/PageDown are pi-tui's own scrollback keys in fullscreen mode and never
123
+ // reach a component, so the hint names keys that do. On a narrow terminal the
124
+ // hints wrap at their separators (so "esc close" is never the part cut off)
125
+ // and the body gives up the rows; the position text depends on the body
126
+ // height, so the layout settles in a second pass.
127
+ const hintLines = (visible: number): string[] => {
128
+ const max = Math.max(0, lines.length - visible);
129
+ const offset = Math.min(state.offset, max);
130
+ const position = lines.length > visible ? ` · ${offset + 1}-${Math.min(lines.length, offset + visible)} of ${lines.length}` : "";
131
+ const hints = [`↑/↓ scroll · space/b page · g/G top/end${position}`, ...(input.canFix ? ["f run the checkup (the model fixes the findings)"] : []), "esc close"].join(" · ");
132
+ return wrapHints(hints, Math.max(1, width - 1));
133
+ };
134
+ let visible = visibleBodyRows(height);
135
+ let footer = hintLines(visible);
136
+ if (footer.length > 1) {
137
+ visible = Math.max(1, visible - (footer.length - 1));
138
+ footer = hintLines(visible);
139
+ }
140
+ state.visibleRows = visible;
115
141
  const max = Math.max(0, lines.length - visible);
116
142
  if (state.offset > max) state.offset = max;
117
143
  const window = lines.slice(state.offset, state.offset + visible);
118
144
  while (window.length < visible) window.push("");
119
- const position = lines.length > visible ? ` · ${state.offset + 1}-${Math.min(lines.length, state.offset + visible)} of ${lines.length}` : "";
120
- // PageUp/PageDown are pi-tui's own scrollback keys in fullscreen mode and never
121
- // reach a component, so the hint names keys that do.
122
- const hints = [`↑/↓ scroll · space/b page · g/G top/end${position}`, ...(input.canFix ? ["f run the checkup (the model fixes the findings)"] : []), "esc close"].join(" · ");
123
- return [panelTopRule(paint, width), ...window, "", paint("dim", ` ${hints}`)];
145
+ return [panelTopRule(paint, width), ...window, "", ...footer.map((line) => paint("dim", ` ${line}`))];
124
146
  }