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
@@ -22,32 +22,41 @@
22
22
 
23
23
  import { mkdirSync, readFileSync } from "node:fs";
24
24
  import os from "node:os";
25
- import { isAbsolute, join, resolve, sep } from "node:path";
25
+ import { join } from "node:path";
26
26
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
27
+ import { claudeMdLimitWarning, combinedLimitWarning, projectMemoryDir } from "../lib/memory.ts";
27
28
  import {
28
- claudeMdLimitWarning,
29
- combinedLimitWarning,
30
29
  INDEX_NEAR_LIMIT_REMINDER,
31
30
  INDEX_OVER_LIMIT_ERROR,
32
31
  indexLimitStatus,
33
- projectMemoryDir,
34
32
  stampFrontmatter,
35
- } from "../lib/memory.ts";
36
- import { claudeConfigDir, oneCodeStateDir } from "../lib/paths.ts";
33
+ } from "../lib/memory-content.ts";
34
+ import { type ConfigMode, CONFIG_MODE_KEY, claudeSourcesOn, configMode, configModeFromEnv, savedConfigMode } from "../lib/config-mode.ts";
35
+ import { isClaudeLocation } from "../lib/claude-context.ts";
36
+ import { oneCodeSettingsPath, readSettingsForWrite, writeSettings } from "../lib/one-code-settings.ts";
37
+ import { claudeConfigDir, comparablePath, isPathAtOrUnder, oneCodeStateDir, tildify, tryRealpath } from "../lib/paths.ts";
38
+ import { resolveThroughLinks } from "../auto-mode/paths.ts";
39
+ import { resolveToolPath } from "../lib/tool-path.ts";
37
40
  import { tryReadFile } from "../lib/plugins.ts";
38
41
  import { REMINDER_CHANNEL } from "../lib/reminders.ts";
39
42
  import { boundedDockHeight, safeThemeBold, safeThemePaint, truncateLine } from "../lib/tui-render.ts";
40
43
  import { buildMemoryEntries, entryName, type MemoryEntry } from "./entries.ts";
41
- import { openPath } from "./open-external.ts";
42
- import { applyMemoryKey, decodeMemoryKey, initialMemoryState, renderMemoryPanel } from "./panel.ts";
44
+ import { editorHint, memoryDisplayPath, openPath } from "./open-external.ts";
45
+ import { applyMemoryKey, type AutoMemoryView, decodeMemoryKey, initialMemoryState, type ModeView, renderMemoryPanel, settingChanges } from "./panel.ts";
46
+ import { AUTO_MEMORY_KEY, autoMemoryEnabled, autoMemoryFromEnv, savedAutoMemory, savedAutoMemorySource } from "../lib/auto-memory.ts";
43
47
  import { registerLocalCommand } from "../lib/local-command.ts";
48
+ import { canShowCustomUi } from "../lib/headless-output.ts";
49
+ import { panelUi } from "../lib/panel-focus.ts";
44
50
 
45
51
  const MEMORY_PANEL_MAX_HEIGHT = 20;
46
52
 
47
- function inputPath(input: unknown, cwd: string): string | undefined {
53
+ /** `root` is the memory directory's real path, resolved once per session. */
54
+ function inputPath(input: unknown, cwd: string, root: string): string | undefined {
48
55
  const raw = (input as { path?: unknown })?.path;
49
56
  if (typeof raw !== "string" || !raw.trim()) return undefined;
50
- return isAbsolute(raw) ? raw : resolve(cwd, raw);
57
+ // Match the file pi touches, including aliases and not-yet-created leaves.
58
+ const path = resolveThroughLinks(resolveToolPath(raw, cwd));
59
+ return path && isPathAtOrUnder(path, root) && comparablePath(path) !== comparablePath(root) ? path : undefined;
51
60
  }
52
61
 
53
62
  /** All the panel/warning entries for `cwd`, from One Code's real state dirs. */
@@ -57,15 +66,20 @@ function memoryEntriesFor(cwd: string): MemoryEntry[] {
57
66
  home: os.homedir(),
58
67
  homeClaudeDir: claudeConfigDir(),
59
68
  homeOneCodeDir: oneCodeStateDir(),
60
- memoryDir: projectMemoryDir(cwd),
69
+ memoryDir: autoMemoryEnabled(cwd) ? projectMemoryDir(cwd) : undefined,
61
70
  });
62
71
  }
63
72
 
64
73
  export default function memoryExtension(pi: ExtensionAPI) {
65
74
  let dir: string | undefined;
75
+ let dirReal = "";
66
76
 
67
77
  pi.on("session_start", (_event, ctx) => {
78
+ dir = undefined;
68
79
  const candidate = projectMemoryDir(ctx.cwd);
80
+ // An independent state directory can contain links into Claude's tree.
81
+ // Resolve missing leaves through their parents before creating anything.
82
+ if (!claudeSourcesOn() && isClaudeLocation(resolveThroughLinks(candidate) ?? candidate, claudeConfigDir())) return;
69
83
 
70
84
  // Claude Code's startup warning: an instruction file over the char limit
71
85
  // bloats every turn's context. Warn once, pointing at /memory to trim it.
@@ -92,6 +106,9 @@ export default function memoryExtension(pi: ExtensionAPI) {
92
106
  }
93
107
  }
94
108
 
109
+ // Auto-memory off (lib/auto-memory.ts): no folder, no stamping.
110
+ if (!autoMemoryEnabled(ctx.cwd)) return;
111
+
95
112
  // Like Claude Code: one memory dir per git repo (shared by worktrees and
96
113
  // subdirectories); outside a repo, per cwd.
97
114
  try {
@@ -102,6 +119,7 @@ export default function memoryExtension(pi: ExtensionAPI) {
102
119
  return;
103
120
  }
104
121
  dir = candidate;
122
+ dirReal = tryRealpath(candidate) ?? candidate;
105
123
 
106
124
  // The MEMORY.md index is injected by the claude-context extension, folded
107
125
  // into the `# claudeMd` block exactly as Claude Code does — not as a
@@ -111,8 +129,8 @@ export default function memoryExtension(pi: ExtensionAPI) {
111
129
 
112
130
  pi.on("tool_call", (event, ctx) => {
113
131
  if (event.toolName !== "write" || !dir) return undefined;
114
- const path = inputPath(event.input, ctx.cwd);
115
- if (!path?.startsWith(dir + sep)) return undefined;
132
+ const path = inputPath(event.input, ctx.cwd, dirReal);
133
+ if (!path) return undefined;
116
134
  const input = event.input as { content?: unknown };
117
135
  if (typeof input.content !== "string") return undefined;
118
136
  input.content = stampFrontmatter(input.content, ctx.sessionManager.getSessionId(), new Date().toISOString());
@@ -122,7 +140,9 @@ export default function memoryExtension(pi: ExtensionAPI) {
122
140
  pi.on("tool_result", (event, ctx) => {
123
141
  if (event.isError || !dir) return undefined;
124
142
  if (event.toolName !== "write" && event.toolName !== "edit") return undefined;
125
- if (inputPath(event.input, ctx.cwd) !== join(dir, "MEMORY.md")) return undefined;
143
+ const path = inputPath(event.input, ctx.cwd, dirReal);
144
+ const indexPath = join(dir, "MEMORY.md");
145
+ if (!path || comparablePath(path) !== comparablePath(tryRealpath(indexPath) ?? indexPath)) return undefined;
126
146
 
127
147
  let index: string;
128
148
  try {
@@ -144,30 +164,69 @@ export default function memoryExtension(pi: ExtensionAPI) {
144
164
 
145
165
  // ---- /memory: Claude Code's Memory picker --------------------------------
146
166
  // Pick a memory-related file (or the auto-memory folder) and open it in the
147
- // external editor, matching CC. Pure panel logic lives in ./panel.ts.
167
+ // external editor, matching CC. Pure panel logic lives in ./panel.ts. The
168
+ // returned text is the model's `<local-command-stdout>`, worded as CC's.
148
169
  registerLocalCommand(pi, "memory", {
149
170
  description: "View or edit CLAUDE.md and memory files",
150
- handler: async (args, ctx) => {
171
+ reportsResult: true,
172
+ handler: async (_args, ctx): Promise<string | undefined> => {
151
173
  const entries = memoryEntriesFor(ctx.cwd);
152
- if (!ctx.hasUI) {
174
+ if (!canShowCustomUi(ctx)) {
175
+ const limitation = ctx.mode === "rpc" ? "/memory editing is unavailable in RPC; use the TUI or open a listed path in your editor.\n\n" : "";
153
176
  const lines = entries.map(
154
177
  (e, i) => `${i + 1}. ${e.title}${e.description ? ` — ${e.description}` : ""} [${e.path}]`,
155
178
  );
156
- ctx.ui.notify(`Memory / CLAUDE.md files:\n${lines.join("\n")}`, "info");
157
- return;
179
+ const stdout = `${limitation}Memory / CLAUDE.md files:\n${lines.join("\n")}`;
180
+ ctx.ui.notify(stdout, "info");
181
+ return ctx.mode === "rpc" ? limitation.trim() : undefined;
182
+ }
183
+ const mode: ModeView = { running: configMode(), saved: savedConfigMode(), fromEnv: configModeFromEnv() !== undefined };
184
+ const autoMemory: AutoMemoryView = {
185
+ running: autoMemoryEnabled(ctx.cwd),
186
+ saved: savedAutoMemory(ctx.cwd),
187
+ fromEnv: autoMemoryFromEnv() !== undefined,
188
+ };
189
+ const before = { mode: mode.saved, autoMemory: autoMemory.saved };
190
+ const chosen = await openMemoryPanel(pi.events, ctx, entries, mode, autoMemory);
191
+ // A toggle's line leads CC's result when it changed something.
192
+ const changes = settingChanges(before, mode, autoMemory);
193
+ const modeLine = changes ? `${changes}\n\n` : "";
194
+ if (!chosen) {
195
+ // Closing after a change reports the change, not a cancel.
196
+ ctx.ui.notify(changes || CANCELLED, "info");
197
+ return changes || CANCELLED;
158
198
  }
159
- const chosen = await openMemoryPanel(ctx, entries);
160
- if (!chosen) return;
161
- const result = await openPath(chosen.path, chosen.kind);
162
- ctx.ui.notify(result.message, result.ok ? "info" : "error");
163
- if (result.ok && result.hint) ctx.ui.notify(result.hint, "info");
199
+ const shown = memoryDisplayPath(chosen.path, ctx.cwd, os.homedir());
200
+ const result = await openPath(chosen.path, "file");
201
+ const stdout = result.ok ? `Opened ${shown}\n${editorHint()}` : `Couldn't open ${shown}: ${result.error}`;
202
+ ctx.ui.notify(stdout, result.ok ? "info" : "error");
203
+ return `${modeLine}${stdout}`;
164
204
  },
165
205
  });
166
206
  }
167
207
 
168
- /** Show the Memory panel as a focused overlay; resolve to the chosen entry or null. */
169
- function openMemoryPanel(ctx: ExtensionContext, entries: MemoryEntry[]): Promise<MemoryEntry | null> {
170
- return ctx.ui.custom<MemoryEntry | null>((tui, theme, _keybindings, done) => {
208
+ /** Claude Code's `/memory` result when the panel closes without a pick. */
209
+ const CANCELLED = "Cancelled memory editing";
210
+
211
+ /** Save one key to One Code's user settings; the running process keeps its value. */
212
+ function saveUserSetting(key: string, value: unknown): void {
213
+ const path = oneCodeSettingsPath(os.homedir());
214
+ writeSettings(path, { ...readSettingsForWrite(path), [key]: value });
215
+ }
216
+
217
+ /**
218
+ * Show the Memory panel as a focused overlay; resolve to the chosen entry or
219
+ * null. The toggle rows save into `autoMemory.saved` and `mode.saved` (the
220
+ * config-sources row after a confirming second Enter) and keep the panel up.
221
+ */
222
+ function openMemoryPanel(
223
+ events: ExtensionAPI["events"],
224
+ ctx: ExtensionContext,
225
+ entries: MemoryEntry[],
226
+ mode: ModeView,
227
+ autoMemory: AutoMemoryView,
228
+ ): Promise<MemoryEntry | null> {
229
+ return panelUi(events, ctx.ui).custom<MemoryEntry | null>((tui, theme, _keybindings, done) => {
171
230
  const paint = { fg: safeThemePaint(theme), bold: safeThemeBold(theme) };
172
231
  const state = initialMemoryState();
173
232
  let cache: { width: number; lines: string[] } | undefined;
@@ -180,7 +239,7 @@ function openMemoryPanel(ctx: ExtensionContext, entries: MemoryEntry[]): Promise
180
239
  if (cache?.width === width) return cache.lines;
181
240
  const termRows = (tui as { terminal: { rows: number } }).terminal.rows;
182
241
  const height = boundedDockHeight(termRows, MEMORY_PANEL_MAX_HEIGHT);
183
- const lines = renderMemoryPanel({ state, entries, width, height }, paint).map((line) =>
242
+ const lines = renderMemoryPanel({ state, entries, width, height, mode, autoMemory }, paint).map((line) =>
184
243
  truncateLine(line, width),
185
244
  );
186
245
  cache = { width, lines };
@@ -190,10 +249,57 @@ function openMemoryPanel(ctx: ExtensionContext, entries: MemoryEntry[]): Promise
190
249
  const key = decodeMemoryKey(data);
191
250
  if (!key) return;
192
251
  const effect = applyMemoryKey(state, key, entries);
252
+ if (effect?.kind === "confirm-mode" && mode.fromEnv) {
253
+ // The variable outranks the saved setting, so a write here would never apply.
254
+ state.confirmingMode = false;
255
+ ctx.ui.notify("Config sources are set by ONECODE_CONFIG_MODE. Unset it to switch here.", "info");
256
+ repaint();
257
+ return;
258
+ }
259
+ if (effect?.kind === "toggle-auto-memory") {
260
+ if (autoMemory.fromEnv) {
261
+ ctx.ui.notify("Auto-memory is set by CLAUDE_CODE_DISABLE_AUTO_MEMORY. Unset it to switch here.", "info");
262
+ repaint();
263
+ return;
264
+ }
265
+ try {
266
+ const wanted = !autoMemory.saved;
267
+ saveUserSetting(AUTO_MEMORY_KEY, wanted);
268
+ // Re-read the ladder: a project or managed file can still outrank the user file.
269
+ const decided = savedAutoMemorySource(ctx.cwd);
270
+ autoMemory.saved = decided.enabled;
271
+ if (decided.enabled !== wanted && decided.path) {
272
+ ctx.ui.notify(`${tildify(decided.path, os.homedir())} sets ${AUTO_MEMORY_KEY}: ${decided.enabled}, which outranks your user setting. Change it there.`, "info");
273
+ }
274
+ } catch (error) {
275
+ ctx.ui.notify(`Couldn't save the auto-memory setting: ${error instanceof Error ? error.message : error}`, "error");
276
+ }
277
+ repaint();
278
+ return;
279
+ }
280
+ if (effect?.kind === "toggle-mode") {
281
+ const next: ConfigMode = mode.saved === "independent" ? "claude-compatible" : "independent";
282
+ try {
283
+ saveUserSetting(CONFIG_MODE_KEY, next);
284
+ mode.saved = next;
285
+ } catch (error) {
286
+ ctx.ui.notify(`Couldn't save the config sources setting: ${error instanceof Error ? error.message : error}`, "error");
287
+ }
288
+ repaint();
289
+ return;
290
+ }
193
291
  if (effect?.kind === "close") {
194
292
  done(null);
195
293
  return;
196
294
  }
295
+ if (effect?.kind === "open" && effect.entry.kind === "folder") {
296
+ // CC opens the folder and keeps the picker up; only a file or Esc ends it.
297
+ const { path } = effect.entry;
298
+ void openPath(path, "folder").then((result) => {
299
+ if (!result.ok) ctx.ui.notify(`Couldn't open ${memoryDisplayPath(path, ctx.cwd, os.homedir())}: ${result.error}`, "error");
300
+ });
301
+ return;
302
+ }
197
303
  if (effect?.kind === "open") {
198
304
  done(effect.entry);
199
305
  return;
@@ -2,8 +2,8 @@
2
2
  * Open a memory file or folder in the user's editor / OS opener, matching Claude
3
3
  * Code's `/memory`: a file goes to `$VISUAL`/`$EDITOR` (or the OS default), a
4
4
  * folder to the OS file manager. The child is detached and unref'd so the TUI is
5
- * never blocked (as the MCP OAuth browser open does). Returns the status line and
6
- * the editor hint to print, matching CC's "Opened <path>" + the $EDITOR nudge.
5
+ * never blocked (as the MCP OAuth browser open does). The caller words the
6
+ * result (`memoryDisplayPath`, `editorHint`), matching CC's `/memory`.
7
7
  *
8
8
  * Limitation: a terminal editor (vim/nano) needs the controlling tty, which the
9
9
  * pi TUI owns, so it cannot run detached — this suits GUI editors (code, subl)
@@ -14,7 +14,10 @@
14
14
  */
15
15
 
16
16
  import { spawn } from "node:child_process";
17
+ import { relative } from "node:path";
18
+ import { childProcessEnv } from "../lib/app-launch.mjs";
17
19
  import { commandLaunch } from "../lib/command-launch.ts";
20
+ import { forwardSlashes, isRelativeInside, tildify } from "../lib/paths.ts";
18
21
 
19
22
  export interface OpenPlan {
20
23
  command: string;
@@ -52,14 +55,28 @@ export function resolveOpen(
52
55
  return { command: "xdg-open", args: [path] };
53
56
  }
54
57
 
55
- export interface OpenResult {
56
- message: string;
57
- hint?: string;
58
- ok: boolean;
58
+ export type OpenResult = { ok: true } | { ok: false; error: string };
59
+
60
+ /**
61
+ * CC's display path for `/memory`'s result: `~/…` under the home directory,
62
+ * `./…` under the working directory, the shorter when both apply, else as is.
63
+ */
64
+ export function memoryDisplayPath(path: string, cwd: string, home: string): string {
65
+ const tilde = tildify(path, home);
66
+ const underHome = tilde !== path;
67
+ const rel = relative(cwd, path);
68
+ const dot = isRelativeInside(rel) ? `./${forwardSlashes(rel)}` : undefined;
69
+ if (underHome && dot) return tilde.length <= dot.length ? tilde : dot;
70
+ return underHome ? tilde : (dot ?? path);
59
71
  }
60
72
 
61
- /** The editor hint CC prints under an opened file. */
62
- export const EDITOR_HINT = "To use a different editor, set the $EDITOR or $VISUAL environment variable.";
73
+ /** CC's editor hint under an opened file, naming the variable it used. */
74
+ export function editorHint(env: Record<string, string | undefined> = process.env): string {
75
+ const used = env.VISUAL ? `Using $VISUAL="${env.VISUAL}".` : env.EDITOR ? `Using $EDITOR="${env.EDITOR}".` : "";
76
+ return used
77
+ ? `> ${used} To change editor, set $EDITOR or $VISUAL environment variable.`
78
+ : "> To use a different editor, set the $EDITOR or $VISUAL environment variable.";
79
+ }
63
80
 
64
81
  /**
65
82
  * Spawn the opener/editor detached, resolving once the child either spawns
@@ -80,21 +97,19 @@ export function openPath(path: string, kind: "file" | "folder"): Promise<OpenRes
80
97
  let child: ReturnType<typeof spawn>;
81
98
  try {
82
99
  // `$EDITOR=code` is VS Code's `code.cmd` shim on Windows, which a bare spawn cannot start.
83
- const launch = commandLaunch(command, args, process.env);
84
- child = spawn(launch.command, launch.args, { stdio: "ignore", detached: true, windowsVerbatimArguments: launch.windowsVerbatimArguments });
100
+ // The user's own environment under the bundled app (lib/app-launch.mjs): an
101
+ // editor with a terminal must not hand the app's pi state to a `pi` run there.
102
+ const env = childProcessEnv(process.env);
103
+ const launch = commandLaunch(command, args, env);
104
+ child = spawn(launch.command, launch.args, { env, stdio: "ignore", detached: true, windowsVerbatimArguments: launch.windowsVerbatimArguments });
85
105
  } catch (error) {
86
- finish({ message: `Could not open ${path}: ${error instanceof Error ? error.message : error}`, ok: false });
106
+ finish({ ok: false, error: error instanceof Error ? error.message : String(error) });
87
107
  return;
88
108
  }
89
- child.on("error", (error) => finish({ message: `Could not open ${path}: ${error.message}`, ok: false }));
109
+ child.on("error", (error) => finish({ ok: false, error: error.message }));
90
110
  child.on("spawn", () => {
91
111
  child.unref();
92
- finish({
93
- message: `Opened ${path}`,
94
- // CC shows the hint on file opens (even when $EDITOR handled it); folders don't.
95
- hint: kind === "file" ? EDITOR_HINT : undefined,
96
- ok: true,
97
- });
112
+ finish({ ok: true });
98
113
  });
99
114
  });
100
115
  }
@@ -3,66 +3,114 @@
3
3
  * Memory picker: a titled list of the CLAUDE.md-family / AGENTS.md / ONECODE.md
4
4
  * files plus "Open auto-memory folder", with an Auto-memory status line and a
5
5
  * learn-more link. Enter opens the selected entry (in `$EDITOR`); Esc closes.
6
+ * Above the list, where CC keeps its toggle rows (↑ from the first file
7
+ * reaches them): CC's "Auto-memory" toggle (lib/auto-memory.ts), then One
8
+ * Code's "Config sources" row, which switches between the Claude-compatible
9
+ * and independent modes (lib/config-mode.ts) after a confirmation, since it
10
+ * changes every place configuration is read from.
6
11
  *
7
12
  * Kept free of pi imports so the layout and navigation are unit-tested; the
8
13
  * extension owns repaint, the overlay, and the actual open.
9
14
  */
10
15
 
11
16
  import type { MemoryEntry } from "./entries.ts";
17
+ import { type ConfigMode, MODE_LABELS } from "../lib/config-mode.ts";
18
+ import { keyId } from "../lib/key-input.ts";
19
+ import { wrapHints, wrapProse } from "../lib/tui-render.ts";
12
20
 
13
21
  export const MEMORY_DOCS_URL = "https://github.com/IsuruMaduranga/one-code";
14
22
 
23
+ /** Cursor rows above the list. */
24
+ export const AUTO_MEMORY_ROW = -2;
25
+ export const CONFIG_SOURCES_ROW = -1;
26
+
15
27
  export interface MemoryPanelState {
28
+ /** The selected entry's index; the two toggle rows above the list are negative. */
16
29
  cursor: number;
30
+ /** Set while the config-sources switch waits for a second Enter. */
31
+ confirmingMode?: boolean;
17
32
  }
18
33
 
19
34
  export function initialMemoryState(): MemoryPanelState {
20
35
  return { cursor: 0 };
21
36
  }
22
37
 
38
+ /** A setting's value in this process, the value saved for the next start, and whether an env variable sets it. */
39
+ export interface SettingView<T> {
40
+ running: T;
41
+ saved: T;
42
+ fromEnv?: boolean;
43
+ }
44
+
45
+ /** Auto-memory; `CLAUDE_CODE_DISABLE_AUTO_MEMORY` can set it. */
46
+ export type AutoMemoryView = SettingView<boolean>;
47
+
48
+ /** The config-sources mode; `ONECODE_CONFIG_MODE` can set it. */
49
+ export type ModeView = SettingView<ConfigMode>;
50
+
51
+ /** "; applies from the next start" while the saved value differs from the running one. */
52
+ const pendingSuffix = (view: SettingView<unknown>): string => (view.saved === view.running ? "" : "; applies from the next start");
53
+
54
+ const MODE_DESCRIPTIONS: Record<ConfigMode, string> = {
55
+ "claude-compatible": "~/.claude, CLAUDE.md, .agents and pi's dirs",
56
+ independent: "~/.onecode, ONECODE.md, .agents and pi's dirs",
57
+ };
58
+
23
59
  export type MemoryKey = { kind: "up" | "down" | "enter" | "close" };
24
60
 
25
61
  export function decodeMemoryKey(data: string): MemoryKey | undefined {
26
- switch (data) {
27
- case "\x1b[A":
28
- case "\x1bOA":
29
- case "\x10": // ctrl+p
62
+ switch (keyId(data)) {
63
+ case "up":
64
+ case "ctrl+p":
30
65
  return { kind: "up" };
31
- case "\x1b[B":
32
- case "\x1bOB":
33
- case "\x0e": // ctrl+n
66
+ case "down":
67
+ case "ctrl+n":
34
68
  return { kind: "down" };
35
- case "\r":
36
- case "\n":
69
+ case "enter":
37
70
  return { kind: "enter" };
38
- case "\x1b":
39
- case "\x03": // ctrl+c
71
+ case "escape":
72
+ case "ctrl+c":
40
73
  return { kind: "close" };
41
74
  default:
42
75
  return undefined;
43
76
  }
44
77
  }
45
78
 
46
- export type MemoryEffect = { kind: "open"; entry: MemoryEntry } | { kind: "close" };
79
+ export type MemoryEffect =
80
+ | { kind: "open"; entry: MemoryEntry }
81
+ | { kind: "confirm-mode" }
82
+ | { kind: "toggle-mode" }
83
+ | { kind: "toggle-auto-memory" }
84
+ | { kind: "close" };
47
85
 
48
- export function applyMemoryKey(
49
- state: MemoryPanelState,
50
- key: MemoryKey,
51
- entries: readonly MemoryEntry[],
52
- ): MemoryEffect | undefined {
86
+ /**
87
+ * Enter on "Config sources" asks first (`confirm-mode`); a second Enter
88
+ * switches (`toggle-mode`), and Esc or a move keeps the mode and the panel.
89
+ * "Auto-memory" toggles at once, as in Claude Code.
90
+ */
91
+ export function applyMemoryKey(state: MemoryPanelState, key: MemoryKey, entries: readonly MemoryEntry[]): MemoryEffect | undefined {
92
+ const confirming = state.confirmingMode === true;
93
+ state.confirmingMode = false;
53
94
  switch (key.kind) {
54
95
  case "up":
55
- state.cursor = Math.max(0, state.cursor - 1);
96
+ state.cursor = Math.max(AUTO_MEMORY_ROW, state.cursor - 1);
56
97
  return undefined;
57
98
  case "down":
58
99
  state.cursor = Math.min(entries.length - 1, state.cursor + 1);
59
100
  return undefined;
60
101
  case "enter": {
102
+ if (state.cursor === AUTO_MEMORY_ROW) return { kind: "toggle-auto-memory" };
103
+ if (state.cursor === CONFIG_SOURCES_ROW) {
104
+ if (confirming) return { kind: "toggle-mode" };
105
+ state.confirmingMode = true;
106
+ return { kind: "confirm-mode" };
107
+ }
61
108
  const entry = entries[state.cursor];
62
109
  return entry ? { kind: "open", entry } : undefined;
63
110
  }
64
111
  case "close":
65
- return { kind: "close" };
112
+ // Esc on the confirmation keeps the mode and the panel.
113
+ return confirming ? undefined : { kind: "close" };
66
114
  }
67
115
  }
68
116
 
@@ -80,21 +128,36 @@ const DESC_COL = 34;
80
128
  * the cursor stays visible.
81
129
  */
82
130
  export function renderMemoryPanel(
83
- input: { state: MemoryPanelState; entries: readonly MemoryEntry[]; width: number; height: number },
131
+ input: {
132
+ state: MemoryPanelState;
133
+ entries: readonly MemoryEntry[];
134
+ width: number;
135
+ height: number;
136
+ mode: ModeView;
137
+ autoMemory?: AutoMemoryView;
138
+ },
84
139
  paint: PanelPaint,
85
140
  ): string[] {
86
- const { state, entries, height } = input;
141
+ const { state, entries, height, mode, width } = input;
142
+ const autoMemory = input.autoMemory ?? { running: true, saved: true };
87
143
 
88
- const header = [paint.bold("Memory"), "", ` ${paint.fg("muted", "Auto-memory: on")}`, ""];
89
- const footer = [
144
+ const header = [
145
+ paint.bold("Memory"),
90
146
  "",
91
- paint.fg("muted", `Learn more: ${MEMORY_DOCS_URL}`),
147
+ renderAutoMemoryRow(autoMemory, state.cursor === AUTO_MEMORY_ROW, paint),
148
+ renderModeRow(mode, state.cursor === CONFIG_SOURCES_ROW, paint),
92
149
  "",
93
- paint.fg("muted", "Enter to open · Esc to close"),
94
150
  ];
151
+ const hints = state.confirmingMode
152
+ ? [
153
+ ...wrapProse(modeConfirmQuestion(mode), Math.max(20, width)).map((line) => paint.fg("warning", line)),
154
+ ...wrapHints(`Enter to switch · Esc to keep ${MODE_LABELS[mode.saved]}`, width).map((line) => paint.fg("muted", line)),
155
+ ]
156
+ : wrapHints("Enter to open or toggle · ↑ for auto-memory and config sources · Esc to close", width).map((line) => paint.fg("muted", line));
157
+ const footer = ["", paint.fg("muted", `Learn more: ${MEMORY_DOCS_URL}`), "", ...hints];
95
158
 
96
159
  const listCapacity = Math.max(1, height - header.length - footer.length);
97
- const start = scrollStart(state.cursor, entries.length, listCapacity);
160
+ const start = scrollStart(Math.max(0, state.cursor), entries.length, listCapacity);
98
161
  const rows: string[] = [];
99
162
  for (let i = start; i < Math.min(entries.length, start + listCapacity); i++) {
100
163
  rows.push(renderRow(entries[i], i, i === state.cursor, paint));
@@ -103,6 +166,52 @@ export function renderMemoryPanel(
103
166
  return [...header, ...rows, ...footer];
104
167
  }
105
168
 
169
+ /**
170
+ * One line per toggle the panel changed (the result `/memory` reports), empty
171
+ * when nothing changed.
172
+ */
173
+ export function settingChanges(before: { mode: ConfigMode; autoMemory: boolean }, mode: ModeView, autoMemory: AutoMemoryView): string {
174
+ const lines: string[] = [];
175
+ if (autoMemory.saved !== before.autoMemory) {
176
+ lines.push(`Turned auto-memory ${autoMemory.saved ? "on" : "off"}${pendingSuffix(autoMemory)}`);
177
+ }
178
+ if (mode.saved !== before.mode) {
179
+ lines.push(`Set config sources to ${MODE_LABELS[mode.saved]}${pendingSuffix(mode)}`);
180
+ }
181
+ return lines.join("\n");
182
+ }
183
+
184
+ /** The question the config-sources confirmation asks. */
185
+ export function modeConfirmQuestion(mode: ModeView): string {
186
+ const next: ConfigMode = mode.saved === "independent" ? "claude-compatible" : "independent";
187
+ return `Switch config sources to ${MODE_LABELS[next]}? One Code will read ${MODE_DESCRIPTIONS[next]} instead, from the next start.`;
188
+ }
189
+
190
+ /**
191
+ * A toggle row's plain text, "<label>: <saved>", with "(from next start)" while
192
+ * the saved value differs from the running one; when `envName` sets it, the
193
+ * running value and that it is set there. Also returns the value shown.
194
+ */
195
+ function settingRow<T>(label: string, view: SettingView<T>, valueText: (value: T) => string, envName: string, selected: boolean): { left: string; shown: T } {
196
+ const shown = view.fromEnv ? view.running : view.saved;
197
+ const note = view.fromEnv ? ` (set by ${envName})` : view.saved !== view.running ? " (from next start)" : "";
198
+ return { left: `${selected ? "❯ " : " "}${label}: ${valueText(shown)}${note}`, shown };
199
+ }
200
+
201
+ /** "Auto-memory: on|off". */
202
+ function renderAutoMemoryRow(view: AutoMemoryView, selected: boolean, paint: PanelPaint): string {
203
+ const { left } = settingRow("Auto-memory", view, (on) => (on ? "on" : "off"), "CLAUDE_CODE_DISABLE_AUTO_MEMORY", selected);
204
+ return selected ? paint.fg("accent", left) : left;
205
+ }
206
+
207
+ /** "Config sources: <mode>" and the mode's description. */
208
+ function renderModeRow(mode: ModeView, selected: boolean, paint: PanelPaint): string {
209
+ const { left, shown } = settingRow("Config sources", mode, (value) => MODE_LABELS[value], "ONECODE_CONFIG_MODE", selected);
210
+ const pad = " ".repeat(Math.max(2, DESC_COL + 12 - left.length));
211
+ const description = MODE_DESCRIPTIONS[shown];
212
+ return selected ? `${paint.fg("accent", left)}${pad}${paint.fg("accent", description)}` : `${left}${pad}${paint.fg("muted", description)}`;
213
+ }
214
+
106
215
  /** First index to show so `cursor` is within a `capacity`-tall window. */
107
216
  function scrollStart(cursor: number, total: number, capacity: number): number {
108
217
  if (total <= capacity) return 0;
@@ -0,0 +1,31 @@
1
+ /** User-only model-upgrade advice. No reminders, transcript entries, or request hooks. */
2
+ import { homedir } from "node:os";
3
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
+ import { modelSpec } from "../lib/model-policy.ts";
5
+ import { MODEL_UNUSABLE_CHANNEL, type ModelUnusableEvent, withoutUnusable } from "../lib/model-unusable.ts";
6
+ import { newerModelSuggestion } from "../lib/newer-model.ts";
7
+ import { queueNotice } from "../lib/notices.ts";
8
+ import { sessionOutlivesTurn } from "../lib/notifications.ts";
9
+ import { readSuggestNewerModels } from "../lib/one-code-settings.ts";
10
+
11
+ export default function newerModelExtension(pi: ExtensionAPI) {
12
+ const shown = new Set<string>();
13
+ const unusable = new Set<string>();
14
+ pi.events.on(MODEL_UNUSABLE_CHANNEL, (data) => unusable.add((data as ModelUnusableEvent).model));
15
+
16
+ const suggest = (ctx: ExtensionContext, model = ctx.model) => {
17
+ if (!sessionOutlivesTurn(ctx.mode) || !ctx.hasUI || !model || !readSuggestNewerModels(homedir())) return;
18
+ const key = JSON.stringify([ctx.sessionManager.getSessionId(), modelSpec(model)]);
19
+ if (shown.has(key)) return;
20
+ const suggestion = newerModelSuggestion(withoutUnusable(ctx.modelRegistry.getAvailable(), unusable), model);
21
+ if (!suggestion) return;
22
+ // Joined with the other extensions' startup and model-switch notices (lib/notices.ts).
23
+ queueNotice(pi.events, ctx, "info", `${suggestion.text} ${suggestion.fix}`);
24
+ shown.add(key);
25
+ };
26
+
27
+ // Local catalog/cache reads only. Never await a network refresh at startup.
28
+ // Session ids also handle RPC's duplicate session_start for a new session.
29
+ pi.on("session_start", (_event, ctx) => suggest(ctx));
30
+ pi.on("model_select", (event, ctx) => suggest(ctx, event.model));
31
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * notebook_edit's model-facing description: Claude Code's NotebookEdit text
3
+ * with One Code's extras (pure).
4
+ *
5
+ * Claude Code addresses a cell by the id its Read tool prints; pi's read
6
+ * returns the notebook's JSON, so a cell is named by its own `id` or by its
7
+ * position (`cell-0`). The path may be relative and is `path` or
8
+ * `notebook_path`; `append` and the cleared outputs are One Code's.
9
+ */
10
+
11
+ export const NOTEBOOK_EDIT_DESCRIPTION = `Replaces, inserts, or deletes a single cell in a Jupyter notebook (.ipynb file).
12
+
13
+ Usage:
14
+ - You must use the read tool on the notebook in this conversation before editing — this tool will fail otherwise.
15
+ - \`path\` (or \`notebook_path\`) is the notebook, absolute or relative to the working directory.
16
+ - \`cell_id\` is the cell's own \`id\` or, for a notebook whose cells have none, its position: \`cell-0\` is the first cell. It is required for \`replace\` and \`delete\`.
17
+ - \`edit_mode\` defaults to \`replace\`. Use \`insert\` to add a new cell after the cell with the given \`cell_id\` (or at the beginning of the notebook if \`cell_id\` is omitted) — \`cell_type\` is required when inserting. Use \`delete\` to remove the cell. Use \`append\` to add a cell at the end without naming one.
18
+ - Editing a code cell clears its outputs.`;