one-code-extension 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/README.md +219 -219
  2. package/agents/general-purpose.md +2 -2
  3. package/docs/guide/README.md +2 -1
  4. package/docs/guide/bring-your-claude-code-setup.md +60 -9
  5. package/docs/guide/configuration.md +48 -14
  6. package/docs/guide/differences-from-claude-code.md +32 -23
  7. package/docs/guide/doctor.md +20 -13
  8. package/docs/guide/hooks.md +18 -3
  9. package/docs/guide/installation.md +27 -4
  10. package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
  11. package/docs/guide/providers-and-models.md +137 -54
  12. package/docs/guide/reference.md +27 -15
  13. package/docs/guide/sessions-and-context.md +69 -16
  14. package/docs/guide/skills-plugins-and-mcp.md +57 -20
  15. package/docs/guide/subagents-and-workflows.md +95 -29
  16. package/docs/guide/tasks-and-background-work.md +101 -26
  17. package/docs/guide/terminal-interface.md +24 -12
  18. package/docs/guide/tools.md +46 -5
  19. package/docs/guide/troubleshooting.md +13 -9
  20. package/docs/guide/windows.md +66 -34
  21. package/extensions/artifacts/gallery.ts +227 -0
  22. package/extensions/artifacts/index.ts +288 -0
  23. package/extensions/artifacts/store.ts +570 -0
  24. package/extensions/artifacts/viewer.ts +199 -0
  25. package/extensions/ask-user/description.ts +67 -0
  26. package/extensions/ask-user/dialogs.ts +80 -0
  27. package/extensions/ask-user/index.ts +37 -33
  28. package/extensions/ask-user/questions.ts +32 -7
  29. package/extensions/ask-user/widget.ts +45 -25
  30. package/extensions/auto-mode/actions.ts +6 -0
  31. package/extensions/auto-mode/cache.ts +88 -0
  32. package/extensions/auto-mode/classifier-prompt.ts +5 -4
  33. package/extensions/auto-mode/classifier.ts +290 -65
  34. package/extensions/auto-mode/config.ts +72 -20
  35. package/extensions/auto-mode/decision-log.ts +2 -0
  36. package/extensions/auto-mode/defaults.ts +1 -1
  37. package/extensions/auto-mode/denials.ts +123 -0
  38. package/extensions/auto-mode/git-checkout-programs.ts +187 -0
  39. package/extensions/auto-mode/git-status-meta.ts +159 -0
  40. package/extensions/auto-mode/history.ts +189 -0
  41. package/extensions/auto-mode/instructions.ts +58 -52
  42. package/extensions/auto-mode/model-select.ts +159 -134
  43. package/extensions/auto-mode/paths.ts +57 -5
  44. package/extensions/auto-mode/pause.ts +57 -2
  45. package/extensions/auto-mode/prompt.ts +37 -21
  46. package/extensions/auto-mode/read-only-options.ts +724 -0
  47. package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
  48. package/extensions/auto-mode/rules.ts +18 -1
  49. package/extensions/auto-mode/safety-floor.ts +361 -18
  50. package/extensions/auto-mode/setup-run.ts +20 -14
  51. package/extensions/auto-mode/shell-analysis.ts +764 -490
  52. package/extensions/auto-mode/shell-parse.ts +885 -0
  53. package/extensions/auto-mode/system-note.ts +33 -0
  54. package/extensions/auto-mode/transcript.ts +80 -51
  55. package/extensions/background/cron.ts +515 -0
  56. package/extensions/background/index.ts +514 -236
  57. package/extensions/background/loop-fire.ts +136 -0
  58. package/extensions/background/loop-preamble.ts +59 -0
  59. package/extensions/background/loop-skill.ts +163 -0
  60. package/extensions/background/monitor-batch.ts +53 -0
  61. package/extensions/background/noop-fold.ts +128 -0
  62. package/extensions/background/switch-guard.ts +52 -0
  63. package/extensions/background/wakeup.ts +159 -55
  64. package/extensions/bash/background.ts +53 -17
  65. package/extensions/bash/child.ts +7 -0
  66. package/extensions/bash/description.ts +188 -0
  67. package/extensions/bash/guards.ts +15 -67
  68. package/extensions/bash/index.ts +88 -35
  69. package/extensions/branding/compaction-keep.ts +63 -0
  70. package/extensions/branding/declined.ts +22 -0
  71. package/extensions/branding/index.ts +133 -9
  72. package/extensions/branding/prompt-editor.ts +53 -6
  73. package/extensions/branding/queued-edit.ts +31 -0
  74. package/extensions/branding/replaced-builtins.ts +68 -0
  75. package/extensions/branding/startup.ts +33 -71
  76. package/extensions/btw/index.ts +229 -71
  77. package/extensions/btw/panel.ts +228 -100
  78. package/extensions/btw/prompt.ts +41 -4
  79. package/extensions/claude-compat/index.ts +7 -11
  80. package/extensions/claude-context/index.ts +446 -58
  81. package/extensions/compaction/cut.ts +157 -0
  82. package/extensions/compaction/index.ts +216 -75
  83. package/extensions/compaction/kept-thinking.ts +62 -0
  84. package/extensions/compaction/prompt.ts +15 -12
  85. package/extensions/context-budget/index.ts +24 -5
  86. package/extensions/context-management/index.ts +25 -0
  87. package/extensions/doctor/build.ts +59 -8
  88. package/extensions/doctor/builtins.ts +84 -0
  89. package/extensions/doctor/cli.ts +7 -1
  90. package/extensions/doctor/compat.ts +88 -23
  91. package/extensions/doctor/dependencies.ts +41 -5
  92. package/extensions/doctor/fix-prompt.ts +9 -2
  93. package/extensions/doctor/index.ts +102 -58
  94. package/extensions/doctor/models.ts +102 -63
  95. package/extensions/doctor/presets.ts +42 -27
  96. package/extensions/doctor/report.ts +19 -2
  97. package/extensions/doctor/viewer.ts +49 -27
  98. package/extensions/effort/index.ts +72 -19
  99. package/extensions/effort/settings.ts +21 -0
  100. package/extensions/effort/slider.ts +24 -20
  101. package/extensions/exit/index.ts +32 -1
  102. package/extensions/file-tracker/index.ts +337 -26
  103. package/extensions/file-tracker/replay.ts +96 -10
  104. package/extensions/file-tracker/restore.ts +63 -0
  105. package/extensions/file-tracker/results.ts +66 -0
  106. package/extensions/file-tracker/shell-reads.ts +211 -0
  107. package/extensions/file-tracker/tracker.ts +32 -15
  108. package/extensions/footer/footer-line.ts +45 -24
  109. package/extensions/footer/index.ts +47 -2
  110. package/extensions/footer/pr.ts +34 -5
  111. package/extensions/hooks/compaction.ts +7 -0
  112. package/extensions/hooks/executor.ts +32 -30
  113. package/extensions/hooks/index.ts +198 -31
  114. package/extensions/hooks/matcher.ts +3 -0
  115. package/extensions/hooks/protocol.ts +41 -3
  116. package/extensions/hooks/settings.ts +50 -19
  117. package/extensions/hooks/subagent-bridge.ts +2 -0
  118. package/extensions/hooks/trust.ts +13 -5
  119. package/extensions/idle-compact/index.ts +119 -0
  120. package/extensions/idle-compact/policy.ts +143 -0
  121. package/extensions/init/index.ts +11 -10
  122. package/extensions/init/prompt.ts +25 -32
  123. package/extensions/interrupted/index.ts +13 -1
  124. package/extensions/kept-thinking/index.ts +27 -0
  125. package/extensions/lib/agent-cron.ts +130 -0
  126. package/extensions/lib/agent-loader.ts +15 -1
  127. package/extensions/lib/agent-view.ts +85 -0
  128. package/extensions/lib/anthropic-payload.ts +82 -1
  129. package/extensions/lib/anthropic-server-call.ts +230 -0
  130. package/extensions/lib/anthropic-server-tools.ts +485 -0
  131. package/extensions/lib/app-launch.mjs +94 -0
  132. package/extensions/lib/argument-hints.ts +190 -0
  133. package/extensions/lib/atomic-write.ts +6 -2
  134. package/extensions/lib/auto-memory.ts +75 -0
  135. package/extensions/lib/bash-parser.ts +84 -0
  136. package/extensions/lib/btw-fork.ts +93 -0
  137. package/extensions/lib/child-extensions.ts +42 -0
  138. package/extensions/lib/child-writes.ts +45 -0
  139. package/extensions/lib/claude-context.ts +515 -100
  140. package/extensions/lib/claude-external-includes.ts +68 -0
  141. package/extensions/lib/claude-id.ts +22 -0
  142. package/extensions/lib/claude-rules.ts +323 -0
  143. package/extensions/lib/claude-settings.ts +76 -4
  144. package/extensions/lib/compaction-boundary.ts +36 -0
  145. package/extensions/lib/compaction-keep.mjs +83 -0
  146. package/extensions/lib/config-mode.ts +112 -0
  147. package/extensions/lib/consent-dialogs.ts +63 -0
  148. package/extensions/lib/consent-preview.ts +5 -1
  149. package/extensions/lib/consent-stores.ts +48 -0
  150. package/extensions/lib/context-facts.ts +78 -0
  151. package/extensions/lib/context-stack.ts +81 -0
  152. package/extensions/lib/deferred.ts +219 -25
  153. package/extensions/lib/dock-rows.ts +60 -0
  154. package/extensions/lib/empty-tool-result.ts +27 -0
  155. package/extensions/lib/environment-block.ts +76 -0
  156. package/extensions/lib/fetch-timeout.ts +24 -6
  157. package/extensions/{system-prompt → lib}/git-status.ts +51 -13
  158. package/extensions/lib/git.ts +75 -13
  159. package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
  160. package/extensions/lib/grammars/README.md +2 -0
  161. package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
  162. package/extensions/lib/guide-docs.ts +45 -0
  163. package/extensions/lib/headless-output.ts +46 -0
  164. package/extensions/lib/hook-gate.ts +7 -7
  165. package/extensions/lib/interrupt.ts +20 -4
  166. package/extensions/lib/key-input.ts +455 -0
  167. package/extensions/lib/keys.ts +14 -1
  168. package/extensions/lib/local-command.ts +47 -13
  169. package/extensions/lib/mcp-overrides.ts +1 -1
  170. package/extensions/lib/mcp-share.ts +46 -0
  171. package/extensions/lib/memory-content.ts +146 -0
  172. package/extensions/lib/memory-index.ts +18 -0
  173. package/extensions/lib/memory.ts +134 -123
  174. package/extensions/lib/model-catalog/huggingface.json +174 -0
  175. package/extensions/lib/model-catalog/models-dev.json +18297 -0
  176. package/extensions/lib/model-catalog/openrouter.json +5252 -0
  177. package/extensions/lib/model-catalog-data.ts +513 -0
  178. package/extensions/lib/model-catalog.ts +462 -0
  179. package/extensions/lib/model-choice-warnings.ts +137 -0
  180. package/extensions/lib/model-pairings.ts +152 -0
  181. package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
  182. package/extensions/lib/model-policy.ts +112 -15
  183. package/extensions/lib/model-tier.ts +283 -355
  184. package/extensions/lib/model-unusable.ts +1 -1
  185. package/extensions/lib/newer-model.ts +100 -0
  186. package/extensions/lib/notices.ts +87 -0
  187. package/extensions/lib/notifications.ts +308 -64
  188. package/extensions/lib/one-code-settings.ts +74 -8
  189. package/extensions/lib/open-browser.ts +63 -0
  190. package/extensions/lib/openrouter-generation.ts +94 -0
  191. package/extensions/lib/operation-deadline.ts +30 -0
  192. package/extensions/lib/original-command.ts +4 -4
  193. package/extensions/lib/package-version.ts +1 -1
  194. package/extensions/lib/panel-focus.ts +134 -0
  195. package/extensions/lib/paths.ts +17 -2
  196. package/extensions/lib/permission-gate.ts +72 -6
  197. package/extensions/lib/persisted-output.ts +60 -8
  198. package/extensions/lib/pi-ai-estimate.ts +19 -3
  199. package/extensions/lib/pi-version.ts +21 -2
  200. package/extensions/lib/plan-mode-channels.ts +20 -5
  201. package/extensions/lib/plugins.ts +26 -17
  202. package/extensions/lib/powershell-parse-script.ts +208 -0
  203. package/extensions/lib/powershell-parser.ts +344 -0
  204. package/extensions/lib/process-tree.ts +131 -8
  205. package/extensions/lib/prompt-options.ts +82 -0
  206. package/extensions/lib/queued-delivery.ts +44 -0
  207. package/extensions/lib/reminders.ts +364 -58
  208. package/extensions/lib/replaced-builtins.mjs +199 -0
  209. package/extensions/lib/replay-call.ts +70 -0
  210. package/extensions/lib/request-replay.ts +439 -0
  211. package/extensions/lib/scratchpad.ts +81 -14
  212. package/extensions/lib/send-now-channel.ts +7 -0
  213. package/extensions/lib/session-model-tier.ts +51 -0
  214. package/extensions/lib/session-work.ts +64 -0
  215. package/extensions/lib/settings-channels.ts +4 -4
  216. package/extensions/lib/shell-exit-semantics.ts +170 -0
  217. package/extensions/lib/shell-quote.ts +11 -0
  218. package/extensions/lib/shell-result.ts +70 -0
  219. package/extensions/lib/shell-spawn.ts +70 -6
  220. package/extensions/lib/shell-tool.ts +75 -25
  221. package/extensions/lib/side-call-cache.ts +124 -0
  222. package/extensions/lib/side-call-run.ts +96 -0
  223. package/extensions/lib/side-call-usage.ts +38 -0
  224. package/extensions/lib/side-call.ts +13 -1
  225. package/extensions/lib/single-flight.ts +18 -0
  226. package/extensions/lib/skill-body.ts +42 -0
  227. package/extensions/lib/skill-scan.ts +63 -24
  228. package/extensions/lib/spill-file.ts +140 -0
  229. package/extensions/lib/subagent-handback.ts +113 -0
  230. package/extensions/lib/system-role.ts +156 -0
  231. package/extensions/lib/terminal-text.ts +114 -0
  232. package/extensions/lib/text-width.ts +47 -16
  233. package/extensions/lib/timer-ops.ts +20 -0
  234. package/extensions/lib/tool-additions.ts +149 -0
  235. package/extensions/lib/tool-call-corruption.ts +134 -0
  236. package/extensions/lib/tool-path.ts +66 -0
  237. package/extensions/lib/tool-variants.ts +69 -0
  238. package/extensions/lib/tui-render.ts +255 -27
  239. package/extensions/lib/turn-budget-layout.ts +143 -0
  240. package/extensions/lib/update-check.mjs +15 -2
  241. package/extensions/lib/usage-bus.ts +1 -1
  242. package/extensions/lib/user-denial.ts +38 -0
  243. package/extensions/lib/workspace-channel.ts +16 -0
  244. package/extensions/lib/worktree-channel.ts +31 -3
  245. package/extensions/lib/worktree-isolation.ts +40 -10
  246. package/extensions/lsp/client.ts +8 -9
  247. package/extensions/lsp/index.ts +70 -4
  248. package/extensions/lsp/keep-alive.ts +1 -1
  249. package/extensions/lsp/trust.ts +119 -0
  250. package/extensions/lsp/watcher.ts +1 -1
  251. package/extensions/mcp/announce.ts +87 -5
  252. package/extensions/mcp/client.ts +161 -48
  253. package/extensions/mcp/config.ts +132 -8
  254. package/extensions/mcp/discovery.ts +19 -0
  255. package/extensions/mcp/index.ts +209 -71
  256. package/extensions/mcp/oauth/browser.ts +38 -17
  257. package/extensions/mcp/oauth/flow.ts +25 -17
  258. package/extensions/mcp/oauth/provider.ts +14 -8
  259. package/extensions/mcp/oauth/store.ts +34 -16
  260. package/extensions/mcp/panel/keys.ts +12 -11
  261. package/extensions/mcp/panel/render.ts +9 -6
  262. package/extensions/mcp/schema.ts +104 -5
  263. package/extensions/mcp/trust.ts +151 -67
  264. package/extensions/memory/entries.ts +23 -14
  265. package/extensions/memory/index.ts +134 -28
  266. package/extensions/memory/open-external.ts +33 -18
  267. package/extensions/memory/panel.ts +135 -26
  268. package/extensions/newer-model/index.ts +31 -0
  269. package/extensions/notebook/description.ts +18 -0
  270. package/extensions/notebook/index.ts +6 -5
  271. package/extensions/notebook/notebook.ts +1 -1
  272. package/extensions/permissions/index.ts +1300 -381
  273. package/extensions/permissions/matcher.ts +425 -74
  274. package/extensions/permissions/modes.ts +6 -4
  275. package/extensions/permissions/panel/host.ts +130 -0
  276. package/extensions/permissions/panel/keys.ts +48 -0
  277. package/extensions/permissions/panel/render.ts +450 -0
  278. package/extensions/permissions/panel/state.ts +550 -0
  279. package/extensions/permissions/powershell-cmdlets.ts +198 -0
  280. package/extensions/permissions/powershell-paths.ts +197 -0
  281. package/extensions/permissions/powershell-rules.ts +207 -280
  282. package/extensions/permissions/powershell-tree.ts +557 -0
  283. package/extensions/permissions/project-trust.ts +27 -11
  284. package/extensions/permissions/protected-paths.ts +14 -2
  285. package/extensions/permissions/session-grant.ts +6 -0
  286. package/extensions/permissions/settings.ts +166 -31
  287. package/extensions/permissions/subagent-gate.ts +25 -0
  288. package/extensions/permissions/workspace.ts +78 -0
  289. package/extensions/plan-mode/index.ts +156 -69
  290. package/extensions/plan-mode/reminder.ts +89 -21
  291. package/extensions/plan-mode/texts.ts +177 -0
  292. package/extensions/plan-mode/viewer.ts +92 -27
  293. package/extensions/plugins/counts.ts +3 -3
  294. package/extensions/plugins/index.ts +61 -46
  295. package/extensions/plugins/install/install.ts +16 -0
  296. package/extensions/plugins/marketplace/sync.ts +9 -3
  297. package/extensions/plugins/marketplace/types.ts +3 -1
  298. package/extensions/plugins/panel/keys.ts +21 -24
  299. package/extensions/plugins/panel/render.ts +12 -6
  300. package/extensions/plugins/shell-expand.ts +107 -0
  301. package/extensions/plugins/template.ts +32 -15
  302. package/extensions/powershell/child.ts +7 -0
  303. package/extensions/powershell/description.ts +38 -6
  304. package/extensions/powershell/guards.ts +1 -1
  305. package/extensions/powershell/index.ts +12 -7
  306. package/extensions/recap/index.ts +65 -60
  307. package/extensions/recap/prompt.ts +2 -2
  308. package/extensions/recap/scheduler.ts +3 -5
  309. package/extensions/search-tools/grep-merge.ts +39 -0
  310. package/extensions/search-tools/index.ts +0 -0
  311. package/extensions/send-now/chord.ts +62 -0
  312. package/extensions/send-now/index.ts +133 -0
  313. package/extensions/session-title/index.ts +3 -0
  314. package/extensions/session-title/title.ts +4 -1
  315. package/extensions/skill/code-review.ts +97 -0
  316. package/extensions/skill/index.ts +421 -101
  317. package/extensions/skill/invoke.ts +128 -18
  318. package/extensions/skill/listing.ts +133 -9
  319. package/extensions/skill/panel/keys.ts +15 -16
  320. package/extensions/skill/panel/render.ts +5 -2
  321. package/extensions/skill/panel/state.ts +12 -1
  322. package/extensions/subagents/agent-args.ts +39 -0
  323. package/extensions/subagents/agent-description.ts +132 -0
  324. package/extensions/subagents/agents.ts +116 -23
  325. package/extensions/subagents/announce.ts +69 -0
  326. package/extensions/subagents/default-model.ts +2 -3
  327. package/extensions/subagents/delegation-steer.ts +4 -4
  328. package/extensions/subagents/fork-cache.ts +76 -0
  329. package/extensions/subagents/guide-agent.ts +189 -0
  330. package/extensions/subagents/hand-back-review.ts +3 -0
  331. package/extensions/subagents/index.ts +1493 -824
  332. package/extensions/subagents/live-runs.ts +26 -6
  333. package/extensions/subagents/model-select.ts +38 -25
  334. package/extensions/subagents/outcome.ts +2 -2
  335. package/extensions/subagents/panel-keys.ts +73 -31
  336. package/extensions/subagents/panel-render.ts +120 -96
  337. package/extensions/subagents/panel-widget.ts +60 -89
  338. package/extensions/subagents/runner.ts +108 -80
  339. package/extensions/subagents/runs.ts +39 -5
  340. package/extensions/subagents/send-to-main-tool.ts +1 -1
  341. package/extensions/subagents/session-turns.ts +32 -8
  342. package/extensions/subagents/shell-panel.ts +36 -178
  343. package/extensions/subagents/tasks-dialog.ts +170 -0
  344. package/extensions/subagents/viewport-mirror.ts +95 -0
  345. package/extensions/subagents/worktree.ts +57 -13
  346. package/extensions/system-prompt/environment.ts +19 -19
  347. package/extensions/system-prompt/idle-turn.ts +65 -0
  348. package/extensions/system-prompt/index.ts +131 -47
  349. package/extensions/system-prompt/template.ts +24 -50
  350. package/extensions/system-prompt/tiers/common.ts +23 -64
  351. package/extensions/system-prompt/tiers/frontier.ts +32 -11
  352. package/extensions/system-prompt/tiers/low.ts +67 -45
  353. package/extensions/system-prompt/tiers/mid.ts +96 -38
  354. package/extensions/system-reminder/index.ts +175 -13
  355. package/extensions/tasks/descriptions.ts +190 -0
  356. package/extensions/tasks/index.ts +120 -41
  357. package/extensions/tasks/store.ts +83 -16
  358. package/extensions/tool-call-corruption/child.ts +7 -0
  359. package/extensions/tool-call-corruption/index.ts +237 -0
  360. package/extensions/tool-search/announce.ts +16 -0
  361. package/extensions/tool-search/index.ts +189 -41
  362. package/extensions/tool-style/index.ts +68 -10
  363. package/extensions/tool-style/write-description.ts +40 -0
  364. package/extensions/tool-style/write-render.ts +237 -0
  365. package/extensions/turn-duration/index.ts +7 -3
  366. package/extensions/turn-duration/line.ts +47 -4
  367. package/extensions/web/backends.ts +6 -9
  368. package/extensions/web/description.ts +50 -0
  369. package/extensions/web/index.ts +118 -53
  370. package/extensions/web/native-call.ts +15 -0
  371. package/extensions/web/native-results.ts +41 -0
  372. package/extensions/web-fetch/description.ts +42 -0
  373. package/extensions/web-fetch/extract.ts +53 -6
  374. package/extensions/web-fetch/index.ts +125 -76
  375. package/extensions/web-fetch/page-content.ts +19 -0
  376. package/extensions/web-fetch/response.ts +49 -0
  377. package/extensions/web-fetch/summarize.ts +9 -10
  378. package/extensions/workflow/agent-session.ts +105 -18
  379. package/extensions/workflow/args.ts +24 -0
  380. package/extensions/workflow/description.ts +67 -0
  381. package/extensions/workflow/globals.ts +17 -3
  382. package/extensions/workflow/index.ts +145 -162
  383. package/extensions/workflow/records.ts +1 -0
  384. package/extensions/workflow/run-manager.ts +29 -6
  385. package/extensions/workflow/saved-workflows.ts +3 -2
  386. package/extensions/workflow/types.ts +22 -1
  387. package/extensions/workflow/viewer.ts +54 -24
  388. package/extensions/workflow/widget.ts +20 -0
  389. package/extensions/worktree/descriptions.ts +91 -0
  390. package/extensions/worktree/guards.ts +316 -54
  391. package/extensions/worktree/index.ts +121 -39
  392. package/extensions/worktree/powershell-guards.ts +167 -0
  393. package/extensions/worktree/rewrite.ts +14 -16
  394. package/package.json +19 -7
  395. package/skills/artifact-design/SKILL.md +85 -0
  396. package/skills/code-review/SKILL.md +20 -20
  397. package/skills/code-review/cells/arguments-and-flags.md +19 -0
  398. package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
  399. package/skills/code-review/cells/high-8-inline.md +122 -0
  400. package/skills/code-review/cells/low-1-pass-max4.md +27 -0
  401. package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
  402. package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
  403. package/skills/code-review/cells/medium-3x5-verify.md +132 -0
  404. package/skills/code-review/cells/minimal-prompt.md +32 -0
  405. package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
  406. package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
  407. package/skills/fewer-permission-prompts/SKILL.md +1 -1
  408. package/skills/loop/SKILL.md +11 -0
  409. package/skills/security-review/SKILL.md +2 -5
  410. package/skills/simplify/SKILL.md +7 -5
  411. package/skills/workflow-authoring/SKILL.md +143 -0
  412. package/themes/onecode-light.json +1 -1
  413. package/themes/onecode.json +1 -1
  414. package/types/app.d.ts +58 -0
  415. package/types/community-packages.d.ts +26 -1
  416. package/extensions/auto-mode/recoverability.ts +0 -229
  417. package/extensions/lib/capability-index.ts +0 -466
  418. package/extensions/lib/model-facts.generated.json +0 -2675
  419. package/extensions/lib/model-facts.ts +0 -162
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Claude Code's `/loop` sentinels (2.1.282). A loop
3
+ * with no prompt schedules a sentinel as its prompt; at fire time the
4
+ * sentinel expands to the full instructions on first delivery (and, for a
5
+ * loop.md tasks file, whenever the file changed) and to a short tick
6
+ * reminder after that, so the long text stays in the cached prefix
7
+ * (findings §21). The texts are Claude Code's, verbatim with our tool names;
8
+ * the "tell the user" addenda are dropped (no counterpart tool here).
9
+ *
10
+ * Pure apart from reading loop.md (`<cwd>/.claude/loop.md`, then `~/.claude/loop.md`;
11
+ * the corresponding `.onecode` directories in independent mode).
12
+ */
13
+
14
+ import { projectConfigDir, userConfigDir } from "../lib/config-mode.ts";
15
+ import { readFileSync } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ import { join } from "node:path";
18
+
19
+ const yl = "schedule_wakeup";
20
+ const il = "monitor";
21
+ const Um = "task_stop";
22
+ const jb = "task_output";
23
+
24
+ export const AUTONOMOUS_LOOP = "<<autonomous-loop>>";
25
+ export const AUTONOMOUS_LOOP_DYNAMIC = "<<autonomous-loop-dynamic>>";
26
+ export const LOOP_FILE_SENTINEL = "<<loop.md>>";
27
+ export const LOOP_FILE_DYNAMIC_SENTINEL = "<<loop.md-dynamic>>";
28
+ /** Claude Code's cap on loop.md, in characters (its text says bytes). */
29
+ export const LOOP_FILE_CAP = 25000;
30
+ /** Marks "the autonomous preamble went out because loop.md was absent". */
31
+ const PREAMBLE_MARK = "__autonomous_preamble__";
32
+
33
+ export interface LoopFile {
34
+ path: string;
35
+ content: string;
36
+ }
37
+
38
+ /** What went out already this session; compaction resets it (the first fire after it resends everything). */
39
+ export interface LoopDeliveryState {
40
+ autonomousPreambleDelivered: boolean;
41
+ lastLoopFileDelivered: string | null;
42
+ }
43
+
44
+ export function freshDeliveryState(): LoopDeliveryState {
45
+ return { autonomousPreambleDelivered: false, lastLoopFileDelivered: null };
46
+ }
47
+
48
+ export function isAutonomousSentinel(prompt: string): boolean {
49
+ return prompt === AUTONOMOUS_LOOP || prompt === AUTONOMOUS_LOOP_DYNAMIC;
50
+ }
51
+
52
+ export function isLoopFileSentinel(prompt: string): boolean {
53
+ return prompt === LOOP_FILE_SENTINEL || prompt === LOOP_FILE_DYNAMIC_SENTINEL;
54
+ }
55
+
56
+ function cronTick(): string {return`# Autonomous loop tick
57
+
58
+ Run the autonomous check using the loop instructions established earlier in this conversation. If you cannot find them, treat this as a no-op tick. The recurring cron will fire the next tick automatically \u2014 do not call ${yl} from this tick.`}
59
+ const DYNAMIC_MONITOR_NOTE=`
60
+
61
+ If a ${il} is armed (check ${jb}), keep \`delaySeconds\` at 1200\u20131800s \u2014 the ${il} is the wake signal and this is only the fallback heartbeat. If you were woken by a \`<task-notification>\`, handle the event before deciding whether to re-arm. To stop the loop, call ${yl} with \`stop: true\` and ${Um} the monitor (use ${jb} to find its task ID if no longer in context).`;
62
+ function dynamicTick(): string {return`# Autonomous loop tick (dynamic pacing)
63
+
64
+ Run the autonomous check using the loop instructions established earlier in this conversation. If you cannot find them, treat this as a no-op tick.
65
+
66
+ You scheduled this tick via the ${yl} tool (not a recurring cron). To keep the loop alive, call ${yl} again at the end of this turn with \`prompt\` set to the literal sentinel \`${AUTONOMOUS_LOOP_DYNAMIC}\` and \`noop\` set to \`true\` if this tick changed nothing (or \`false\` if it did) \u2014 otherwise the loop ends after this tick.${DYNAMIC_MONITOR_NOTE}`}
67
+ function resolveAutonomous(e: LoopDeliveryState, t: string, preamble: string): string | null {if(!isAutonomousSentinel(t))return null;let o=t===AUTONOMOUS_LOOP_DYNAMIC?dynamicTick():cronTick();if(e.autonomousPreambleDelivered||e.lastLoopFileDelivered!==null)return o;return e.autonomousPreambleDelivered=!0,`${preamble}
68
+
69
+ ---
70
+
71
+ ${o}`}
72
+ function loopFileCronTick(): string {return`# /loop tick \u2014 loop.md tasks
73
+
74
+ Work the tasks from the loop.md contents established earlier in this conversation. If you cannot find them, treat this as a no-op tick. The recurring cron will fire the next tick automatically \u2014 do not call ${yl} from this tick.`}
75
+ function loopFileDynamicTick(): string {return`# /loop tick \u2014 loop.md tasks (dynamic pacing)
76
+
77
+ Work the tasks from the loop.md contents established earlier in this conversation. If you cannot find them, treat this as a no-op tick.
78
+
79
+ You scheduled this tick via the ${yl} tool (not a recurring cron). To keep the loop alive, call ${yl} again at the end of this turn with \`prompt\` set to the literal sentinel \`${LOOP_FILE_DYNAMIC_SENTINEL}\` and \`noop\` set to \`true\` if this tick changed nothing (or \`false\` if it did) \u2014 otherwise the loop ends after this tick.${DYNAMIC_MONITOR_NOTE}`}
80
+ function loopFileAbsentDynamicTick(): string {return`# /loop tick \u2014 loop.md absent (dynamic pacing)
81
+
82
+ loop.md is not currently present. Run the autonomous check using the loop instructions established earlier in this conversation.
83
+
84
+ You scheduled this tick via the ${yl} tool (not a recurring cron). To keep the loop alive \u2014 and to pick up loop.md if it is recreated \u2014 call ${yl} again at the end of this turn with \`prompt\` set to the literal sentinel \`${LOOP_FILE_DYNAMIC_SENTINEL}\` and \`noop\` set to \`true\` if this tick changed nothing (or \`false\` if it did) \u2014 otherwise the loop ends after this tick.${DYNAMIC_MONITOR_NOTE}`}
85
+ function capLoopFile(e: string): string {if(e.length<=LOOP_FILE_CAP)return e;let t=e.lastIndexOf(`
86
+ `,LOOP_FILE_CAP);return`${e.slice(0,t>0?t:LOOP_FILE_CAP)}
87
+
88
+ > WARNING: loop.md was truncated to ${LOOP_FILE_CAP} bytes. Keep the task list concise.`}
89
+ /**
90
+ * One loop.md candidate, or null. Every read error counts as absent (Claude
91
+ * Code rethrows all but a missing file or a directory): this runs from the
92
+ * cron timer, where a throw would take the process down.
93
+ */
94
+ function readLoopFileAt(path: string): LoopFile | null {
95
+ let text: string;
96
+ try {
97
+ text = readFileSync(path, "utf-8");
98
+ } catch {
99
+ return null;
100
+ }
101
+ const trimmed = text.trim();
102
+ if (trimmed.length === 0) return null;
103
+ return { path, content: capLoopFile(trimmed) };
104
+ }
105
+
106
+ /**
107
+ * The loop-tasks file: the project's `.claude/loop.md` (`.onecode/loop.md` in
108
+ * independent mode, lib/config-mode.ts), else the user config directory's
109
+ * `loop.md` (Claude Code 2.1.296: `~/.claude/loop.md` or `CLAUDE_CONFIG_DIR`).
110
+ */
111
+ export function readLoopFile(cwd: string, home = homedir()): LoopFile | null {
112
+ return readLoopFileAt(join(projectConfigDir(cwd), "loop.md")) ?? readLoopFileAt(join(userConfigDir(home), "loop.md"));
113
+ }
114
+
115
+ function resolveLoopFile(e: LoopDeliveryState, t: string, o: LoopFile | null, preamble: string): string {let n=t===LOOP_FILE_DYNAMIC_SENTINEL;if(o){let s=n?loopFileDynamicTick():loopFileCronTick();if(e.lastLoopFileDelivered===o.content)return s;return e.lastLoopFileDelivered=o.content,`# /loop tick \u2014 tasks from ${o.path}
116
+
117
+ The user configured a loop-tasks file. Work through the tasks defined below; these are the instructions for this tick and every subsequent tick (the reminder on later fires refers back to this message).
118
+
119
+ ---
120
+
121
+ ${o.content}
122
+
123
+ ---
124
+
125
+ ${s}`}let r=n?loopFileAbsentDynamicTick():cronTick();if(e.lastLoopFileDelivered===PREAMBLE_MARK||e.autonomousPreambleDelivered)return r;return e.lastLoopFileDelivered=PREAMBLE_MARK,e.autonomousPreambleDelivered=!0,`${preamble}
126
+
127
+ ---
128
+
129
+ ${r}`}
130
+ /**
131
+ * The text a fired prompt delivers, as in Claude Code:
132
+ * a sentinel expands, updating `state`; any other prompt passes through.
133
+ */
134
+ export function resolveLoopFire(state: LoopDeliveryState, prompt: string, cwd: string, preamble: string, home?: string): string {
135
+ return resolveAutonomous(state, prompt, preamble) ?? (isLoopFileSentinel(prompt) ? resolveLoopFile(state, prompt, readLoopFile(cwd, home), preamble) : prompt);
136
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Claude Code's autonomous-loop instructions (2.1.282, verbatim): the text a
3
+ * bare `/loop` (or `/loop <interval>` with no prompt) runs on, when no
4
+ * loop.md tasks file exists. There are two; the
5
+ * persistent one is chosen by `CLAUDE_CODE_LOOP_PERSISTENT` (Claude Code also
6
+ * reads a remote flag, default off). Findings §21.
7
+ *
8
+ * Verbatim; never hand-edit the text.
9
+ */
10
+
11
+ export const AUTONOMOUS_PREAMBLE = `# Autonomous loop check
12
+
13
+ You're being invoked on a timer while the user is away or occupied. The point is to keep work moving forward without the user driving every step - finishing things they started, maintaining PRs they're building, catching problems before they come back to find them. You're a steward, not an initiator. The user set you loose on their work, and the value you provide comes from reliably advancing things they've already set in motion, not from finding new things to do.
14
+
15
+ The key tension to navigate: the user trusts you enough to run autonomously, but that trust is easily lost. Acting on what the conversation already established is safe and valuable. Inventing new work or making irreversible changes without clear authorization erodes trust fast. When you're unsure whether something falls into "continuing established work" or "inventing new work," lean toward the former only when the transcript provides clear evidence the user wanted it done. If you find yourself reaching for justifications about why a push is probably fine, that's a signal to wait.
16
+
17
+ ## What to act on
18
+
19
+ The current conversation is your highest-signal source - re-read the transcript above, since everything there is something the user was actively engaged with. The strongest signal is an in-progress PR you've been building together: review comments to address and resolve, failing CI checks to diagnose (and re-enqueue if they're flakes), merge conflicts to fix. The goal is to get the PR into a state where it's ready to merge pending only human review - the user shouldn't come back to find a PR blocked on things you could have handled. After that, look for unfinished implementation where the last exchange left something half-done, and explicit "I'll also..." or "next I'll..." commitments the conversation made and didn't honor. Weaker but still real: dangling questions you could now answer, verification steps that were skipped, edge cases that were mentioned but not handled, and natural continuations that don't require new decisions.
20
+
21
+ If you find anything in this category, act on it - actually do the work, don't describe what could be done. Run the tests, don't say "you could run the tests." The whole point of autonomous operation is that work gets done while the user is away.
22
+
23
+ When the conversation transcript has nothing left, the current branch's pull/merge request on the user's SCM is the next-best place to look. This is maintenance work - valuable, but lower priority than continuing the user's active work. Find the PR/MR for the current branch via the SCM's CLI, then check three things: CI status, unresolved review threads, and whether the branch has fallen behind the base. For failing CI, pull the failing job's logs and diagnose before acting - flaky-shaped failures (timeout, runner died, transient network) can be re-enqueued; real failures need a reproduction and a minimal fix. For unresolved review threads, fetch the comment, address the feedback, push, and resolve the thread via, for example, the GitHub GraphQL \`resolveReviewThread\` mutation (or the equivalent for whichever SCM the project uses). Before pushing anything, check whether someone else has pushed to the branch while you were working - if so, rebase (don't merge) to keep history clean.
24
+
25
+ When CI is green, threads are clear, and there's idle time, sweeping the branch for issues is a good use of that time - bug-hunt or simplification passes catch problems before reviewers do, saving everyone a round-trip.
26
+
27
+ If everything is genuinely quiet - no conversation work, no PR maintenance - say so in one sentence and stop. No summary of what you checked, no list of what you might do later. The user will see your message in the transcript when they come back; three consecutive "nothing to do" results means you should scale back to a quick CI check and stop, not narrate.
28
+
29
+ ## Repeated invocations
30
+
31
+ If you see earlier autonomous checks in this conversation, adjust your scope accordingly. If a previous check left a question the user hasn't answered, the cost of acting depends on reversibility: for reversible actions (local edits, running tests), make your best call and proceed; for irreversible ones (pushing, deleting, sending), keep waiting - the cost of acting wrongly on something irreversible is much higher than the cost of waiting one more cycle. If three or more consecutive checks have found nothing actionable, things are quiet - do one quick CI/threads check and stop in a single line. Repeated "nothing to do" messages clutter the transcript and waste the user's attention when they come back to review.
32
+
33
+ Read and analyze freely - understanding the state of things has no blast radius. Make edits and run tests when you're confident they continue established work. Commit and push only when you're clearly continuing something the user authorized, or when the work pattern makes the intent obvious - like fixing CI on a PR you've been building together.
34
+ `;
35
+
36
+ export const AUTONOMOUS_PREAMBLE_PERSISTENT = `# Autonomous loop check
37
+
38
+ You're being invoked on a timer while the user is away or occupied. The point is to keep work moving forward without the user driving every step - finishing things they started, maintaining PRs they're building, catching problems before they come back to find them, and following through on the *spirit* of the task they gave you, not just its literal scope. The user set you loose on their work, and the value you provide comes from reliably advancing things they've already set in motion.
39
+
40
+ The key tension to navigate: the user trusts you enough to run autonomously, but that trust is easily lost. Acting on what the conversation already established is safe and valuable. For irreversible actions (push, delete, send), require clear authorization in the transcript or use a reversible alternative (a draft, a local commit, a queued message). For reversible actions (edits, tests, drafts, exploration), bias toward acting - the cost of an unneeded local edit is near zero, and the cost of a stalled loop is high. When you're unsure whether something falls into "continuing established work" or "inventing new work," lean toward continuing whenever the transcript gives you any reasonable thread to pull on.
41
+
42
+ ## What to act on
43
+
44
+ The current conversation is your highest-signal source - re-read the transcript above, since everything there is something the user was actively engaged with. The strongest signal is an in-progress PR you've been building together: review comments to address and resolve, failing CI checks to diagnose (and re-enqueue if they're flakes), merge conflicts to fix. The goal is to get the PR into a state where it's ready to merge pending only human review - the user shouldn't come back to find a PR blocked on things you could have handled. After that, look for unfinished implementation where the last exchange left something half-done, and explicit "I'll also..." or "next I'll..." commitments the conversation made and didn't honor. Weaker but still real: dangling questions you could now answer, verification steps that were skipped, edge cases that were mentioned but not handled, and natural continuations that don't require new decisions.
45
+
46
+ If you find anything in this category, act on it - actually do the work, don't describe what could be done. Run the tests, don't say "you could run the tests." The whole point of autonomous operation is that work gets done while the user is away.
47
+
48
+ When the conversation transcript has nothing left, the current branch's pull/merge request on the user's SCM is the next-best place to look. This is maintenance work - valuable, but lower priority than continuing the user's active work. Find the PR/MR for the current branch via the SCM's CLI, then check three things: CI status, unresolved review threads, and whether the branch has fallen behind the base. For failing CI, pull the failing job's logs and diagnose before acting - flaky-shaped failures (timeout, runner died, transient network) can be re-enqueued; real failures need a reproduction and a minimal fix. For unresolved review threads, fetch the comment, address the feedback, push, and resolve the thread via, for example, the GitHub GraphQL \`resolveReviewThread\` mutation (or the equivalent for whichever SCM the project uses). Before pushing anything, check whether someone else has pushed to the branch while you were working - if so, rebase (don't merge) to keep history clean.
49
+
50
+ When CI is green, threads are clear, and there's idle time, sweeping the branch for issues is a good use of that time - bug-hunt or simplification passes catch problems before reviewers do, saving everyone a round-trip.
51
+
52
+ If everything is genuinely quiet - no conversation work, no PR maintenance - say so in one sentence and keep the loop alive. Before stopping, broaden once: re-read the original task framing, check whether earlier ticks deferred anything ("I'll wait for X"), and look at sibling PRs/branches the user owns. Persistence is the point of autonomous mode. Only stop if the original task is provably complete or the user said to stop. (Pacing - how long to wait before the next tick - is handled by the per-mode reminder appended to this preamble; don't try to manage delay from here.)
53
+
54
+ ## Repeated invocations
55
+
56
+ If you see earlier autonomous checks in this conversation, adjust your scope accordingly. If a previous check left a question the user hasn't answered, the cost of acting depends on reversibility: for reversible actions (local edits, running tests), make your best call and proceed; for irreversible ones (pushing, deleting, sending), keep waiting - the cost of acting wrongly on something irreversible is much higher than the cost of waiting one more cycle. If three or more consecutive checks have found nothing actionable, broaden scope once before considering stopping - re-read the original task, check sibling work, look for verification or polish steps that were skipped. A loop that quits the moment work goes quiet is less useful than one that waits.
57
+
58
+ Read and analyze freely - understanding the state of things has no blast radius. Make edits and run tests when you're confident they continue established work. Commit and push only when you're clearly continuing something the user authorized, or when the work pattern makes the intent obvious - like fixing CI on a PR you've been building together.
59
+ `;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Claude Code's `/loop` bundled skill (2.1.288): the
3
+ * prompt each form of `/loop` sends the model. The model then schedules the
4
+ * loop itself (cron_create, or schedule_wakeup for self-pacing) and runs the
5
+ * first iteration, as in Claude Code (findings §21).
6
+ *
7
+ * The templates are Claude Code's, verbatim; only the tool names are ours. Dropped branches: the cloud-schedule offer, the
8
+ * "tell the user" addenda and the confirm-before-arming order (Claude Code
9
+ * gates them on claude.ai features and a per-model flag One Code has no
10
+ * counterpart for), and `TaskList`, which becomes task_output
11
+ * (our task_list is the to-do list and shows no monitor; decisions/tools.md,
12
+ * "Session cron").
13
+ *
14
+ * Pure: no pi imports.
15
+ */
16
+
17
+ import { AUTONOMOUS_PREAMBLE, AUTONOMOUS_PREAMBLE_PERSISTENT } from "./loop-preamble.ts";
18
+ import { LOOP_FILE_DYNAMIC_SENTINEL, LOOP_FILE_SENTINEL, AUTONOMOUS_LOOP, AUTONOMOUS_LOOP_DYNAMIC, type LoopFile } from "./loop-fire.ts";
19
+ import { RECURRING_MAX_AGE_DAYS as Ofe } from "./cron.ts";
20
+ import { envFlag } from "./wakeup.ts";
21
+
22
+ const yl = "schedule_wakeup";
23
+ const ky = "cron_create";
24
+ const vA = "cron_delete";
25
+ const il = "monitor";
26
+ const Um = "task_stop";
27
+ const jb = "task_output";
28
+ const PVe = AUTONOMOUS_LOOP;
29
+ const gve = AUTONOMOUS_LOOP_DYNAMIC;
30
+ /** Claude Code's monitor-TTL branch is off here: our monitor has `persistent`. */
31
+ const b8 = (): boolean => false;
32
+ const gDe = (): number => 0;
33
+ const Ktn = (ms: number): string => `${ms}ms`;
34
+ /** The interval a bare `/loop` uses. */
35
+ const N = "10m";
36
+ /** The self-paced forms confirm after arming the wakeup, as visible text, and say how a stopped loop ended. */
37
+ const VISIBLE_UPDATE = "This must be ordinary visible response text \u2014 the user cannot see your thinking/reasoning, so an update written only there is invisible to them. Make it the last thing in the turn, then end the turn.";
38
+ const STOP_OUTCOME = "Then write the loop's outcome for the user as ordinary visible response text \u2014 a stopped loop has no next tick to surface it.";
39
+
40
+ export const LOOP_SKILL = {
41
+ name: "loop",
42
+ description: "Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace.",
43
+ whenToUse: 'When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.',
44
+ argumentHint: "[interval] [prompt]",
45
+ } as const;
46
+
47
+ /** The autonomous-loop instructions: the persistent variant with `CLAUDE_CODE_LOOP_PERSISTENT` on. */
48
+ export function autonomousPreamble(env: NodeJS.ProcessEnv = process.env): string {
49
+ return envFlag(env.CLAUDE_CODE_LOOP_PERSISTENT, false) ? AUTONOMOUS_PREAMBLE_PERSISTENT : AUTONOMOUS_PREAMBLE;
50
+ }
51
+
52
+ const INTERVAL_ONLY=/^\d+[smhd]$/;
53
+ const EVERY_ONLY=/^every\s+(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days)\s*$/i;
54
+ function normalizeEvery(e: RegExpMatchArray): string {let o=e[1],t=e[2].toLowerCase();if(t.startsWith("s"))return`${o}s`;if(t.startsWith("h"))return`${o}h`;if(t.startsWith("d"))return`${o}d`;return`${o}m`}
55
+ const I="| Interval pattern | Cron expression | Notes |\n|-----------------------|---------------------|------------------------------------------|\n| `Nm` where N \u2264 59 | `*/N * * * *` | every N minutes |\n| `Nm` where N \u2265 60 | `0 */H * * *` | round to hours (H = N/60, must divide 24)|\n| `Nh` where N \u2264 23 | `0 */N * * *` | every N hours |\n| `Nd` | `0 0 */N * *` | every N days at midnight local |\n| `Ns` | treat as `ceil(N/60)m` | cron minimum granularity is 1 minute |\n\n**If the interval doesn't cleanly divide its unit** (e.g. `7m` \u2192 `*/7 * * * *` gives uneven gaps at :56\u2192:00; `90m` \u2192 1.5h which cron can't express), pick the nearest clean interval and tell the user what you rounded to before scheduling.";
56
+
57
+ function armMonitor(): string {return b8()?`arm one now with \`timeout_ms: ${gDe()}\``:"arm one now with `persistent: true`"}
58
+ function rearmReminder(e: "iterations" | "ticks"): string {if(b8())return`A monitor expires after at most ${Ktn(gDe())} and tells you; on later ${e} call ${jb} first and re-arm only if no monitor for it is still running.`;return e==="iterations"?`Arm once; on later iterations call ${jb} first and skip this step if a monitor is already running.`:`Arm once; on later ticks call ${jb} first and skip if a monitor is already running.`}
59
+ function explicitPrompt(e: string): string {let o=`The user wants you to self-pace. Decide what makes the next iteration worth running \u2014 a passage of time, or an observable event.
60
+
61
+ 1. **Run the parsed prompt now.** If it's a slash command, invoke it via the skill tool; otherwise act on it directly.
62
+ 2. **If the next run is gated on an event** (CI finishing, a log line matching, a file changing, a PR comment) and no ${il} is already running for it: ${armMonitor()}. Its events arrive as \`<task-notification>\` messages and wake this loop immediately \u2014 you do not wait for the ${yl} deadline. ${rearmReminder("iterations")}
63
+ 3. **Decide whether the loop continues.** If the task needs another iteration, call ${yl} with:
64
+ - \`delaySeconds\`: with a ${il} armed this is the **fallback heartbeat** \u2014 how long to wait if no event fires (lean 1200\u20131800s; idle ticks more frequent than the task needs are pure overhead). Without a ${il} this is the cadence \u2014 pick based on what you observed. Read the tool's own description for cache-aware delay guidance.
65
+ - \`reason\`: one short sentence on why you picked that delay.
66
+ - \`prompt\`: the full original /loop input verbatim, prefixed with \`/loop \` so the next firing re-enters this skill and continues the loop. For example, if the user typed \`/loop check the deploy\`, pass \`/loop check the deploy\` as the prompt.
67
+ - \`noop\`: \`true\` if this tick changed nothing ("still waiting", "quiet hold"); \`false\` if it did something worth keeping. Consecutive \`noop: true\` ticks collapse in the terminal.
68
+ If it doesn't need another iteration, stop instead (step 6) \u2014 re-arming is a per-turn choice, not a default.
69
+ 4. **After the wakeup is armed, briefly confirm**: that you're self-pacing, whether a ${il} is the primary wake signal, that you ran the task now, and what fallback delay you picked. ${VISIBLE_UPDATE}
70
+ 5. **If you were woken by a \`<task-notification>\`** rather than this prompt: handle the event in the context of the loop task, then make the same decision. If the loop should continue, call ${yl} again with the same \`prompt\` and the same 1200\u20131800s \`delaySeconds\` from the schedule step above (the ${il} remains the wake signal; the new wakeup is only the fallback heartbeat), then write the same brief update as visible text. If the event means the work is finished, stop (step 6).
71
+ 6. **To stop the loop** \u2014 the task is complete, further iterations can't make progress, or the user asked you to stop \u2014 call ${yl} with \`stop: true\` (no other fields) and ${Um} any ${il} you armed (use ${jb} to find the task ID if it is no longer in context). ${STOP_OUTCOME} Stopping is the loop's normal ending \u2014 the user can restart it anytime with /loop.`;return`# /loop \u2014 schedule a recurring or self-paced prompt
72
+
73
+ Parse the input below into \`[interval] <prompt\u2026>\` and schedule it.
74
+
75
+ ## Parsing (in priority order)
76
+
77
+ 1. **Leading token**: if the first whitespace-delimited token matches \`^\\d+[smhd]$\` (e.g. \`5m\`, \`2h\`), that's the interval; the rest is the prompt.
78
+ 2. **Trailing "every" clause**: otherwise, if the input ends with \`every <N><unit>\` or \`every <N> <unit-word>\` (e.g. \`every 20m\`, \`every 5 minutes\`, \`every 2 hours\`), extract that as the interval and strip it from the prompt. Only match when what follows "every" is a time expression \u2014 \`check every PR\` has no interval.
79
+ 3. **No interval**: otherwise, the entire input is the prompt and you'll self-pace dynamically (see "Dynamic mode" below).
80
+
81
+ If the resulting prompt is empty, show usage \`/loop [interval] <prompt>\` and stop.
82
+
83
+ Examples:
84
+ - \`5m /babysit-prs\` \u2192 interval \`5m\`, prompt \`/babysit-prs\` (rule 1)
85
+ - \`check the deploy every 20m\` \u2192 interval \`20m\`, prompt \`check the deploy\` (rule 2)
86
+ - \`run tests every 5 minutes\` \u2192 interval \`5m\`, prompt \`run tests\` (rule 2)
87
+ - \`check the deploy\` \u2192 no interval \u2192 dynamic mode, prompt \`check the deploy\` (rule 3)
88
+ - \`check every PR\` \u2192 no interval \u2192 dynamic mode, prompt \`check every PR\` (rule 3 \u2014 "every" not followed by time)
89
+ - \`5m\` \u2192 empty prompt \u2192 show usage
90
+
91
+ ## Fixed-interval mode (rules 1 and 2)
92
+
93
+ Convert the interval to a cron expression:
94
+
95
+ ${I}
96
+
97
+ Then:
98
+ 1. Call ${ky} with: \`cron\` (the expression above), \`prompt\` (the parsed prompt verbatim), \`recurring: true\`.
99
+ 2. Briefly confirm: what's scheduled, the cron expression, the human-readable cadence, that recurring tasks auto-expire after ${Ofe} days, and that the user can cancel sooner with ${vA} (include the job ID).
100
+ 3. **Then immediately execute the parsed prompt now** \u2014 don't wait for the first cron fire. If it's a slash command, invoke it via the skill tool; otherwise act on it directly.
101
+
102
+ ## Dynamic mode (rule 3 \u2014 no interval)
103
+
104
+ ${o}
105
+
106
+ ## Input
107
+
108
+ ${e}`}
109
+ function noPromptBody(e: LoopFile | null, o: boolean, t: string): string {let s: string=e?`## Loop tasks (from ${e.path})`:"## Autonomous-loop instructions (for the immediate execution and every fire)";let r: string;if(e)r=e.content;else r=autonomousPreamble();let h=e?"the loop.md tasks":"the autonomous check";if(o){let c=e?LOOP_FILE_DYNAMIC_SENTINEL:gve,O=e?`# /loop \u2014 loop.md tasks with dynamic pacing
110
+
111
+ The user invoked \`/loop\` with no prompt and no interval and has a loop-tasks file at \`${e.path}\`. Run those tasks now, then self-pace the next iteration via ${yl} \u2014 no cron.`:`# /loop \u2014 autonomous default with dynamic pacing
112
+
113
+ The user invoked \`/loop\` with no prompt and no interval. Run the autonomous check now, then self-pace the next iteration via ${yl} \u2014 no cron.`,b=e?`that you're running tasks from \`${e.path}\` in dynamic-pacing mode, that you ran the first tick now`:"that this is the autonomous default in dynamic-pacing mode, that you ran the check now",_=`1. **Run ${h} now**, following the instructions inlined below.
114
+ 2. **If the next tick is gated on an event** (CI finishing, a PR comment, a log line) and no ${il} is already running for it: ${armMonitor()}. Its events wake this loop immediately \u2014 you do not wait for the ${yl} deadline. ${rearmReminder("ticks")}
115
+ 3. **Decide whether the loop continues.** If the next check is worth running, call ${yl} with:
116
+ - \`delaySeconds\`: with a ${il} armed this is the fallback heartbeat (lean 1200\u20131800s). Without one, pick based on what you observed this turn \u2014 quiet branch? wait longer. Lots in flight? wait shorter. Read the tool's own description for cache-aware delay guidance.
117
+ - \`reason\`: one short sentence on why you picked that delay.
118
+ - \`prompt\`: the literal string \`${c}\` \u2014 the dynamic-mode sentinel expands at fire time to the full instructions (first fire / first fire post-compact / loop.md edited) or a dynamic-pacing-specific short reminder (subsequent fires). Do not pass the full instructions; that is handled automatically.
119
+ - \`noop\`: \`true\` if this tick changed nothing ("still waiting", "quiet hold"); \`false\` if it did something worth keeping. Consecutive \`noop: true\` ticks collapse in the terminal.
120
+ If it isn't, stop instead (step 6) \u2014 re-arming is a per-turn choice, not a default.
121
+ 4. **After the wakeup is armed, briefly confirm**: ${b}, whether a ${il} is the primary wake signal, and what fallback delay you picked. ${VISIBLE_UPDATE}
122
+ 5. **If woken by a \`<task-notification>\`** rather than this prompt: handle the event, then make the same decision. If the loop should continue, call ${yl} again with \`${c}\` and the same 1200\u20131800s \`delaySeconds\` (the ${il} remains the wake signal; the new wakeup is only the fallback heartbeat), then write the same brief update as visible text. If the event means the work is finished, stop (step 6).
123
+ 6. **To stop the loop** \u2014 the task is complete, further iterations can't make progress, or the user asked you to stop \u2014 call ${yl} with \`stop: true\` (no other fields) and ${Um} any ${il} you armed (use ${jb} to find the task ID if it is no longer in context). ${STOP_OUTCOME} Stopping is the loop's normal ending \u2014 the user can restart it anytime with /loop.`;return`${O}
124
+
125
+ ## Action
126
+
127
+ ${_}
128
+
129
+ ${s}
130
+
131
+ ${r}`}let l=e?LOOP_FILE_SENTINEL:PVe,u=e?`# /loop \u2014 schedule loop.md tasks
132
+
133
+ The user invoked \`/loop\` with no prompt (input was empty or just the interval \`${t}\`) and has a loop-tasks file at \`${e.path}\`. Schedule a recurring cron that runs those tasks each tick, then run the first tick immediately.`:`# /loop \u2014 schedule the autonomous default
134
+
135
+ The user invoked \`/loop\` with no prompt (input was empty or just the interval \`${t}\`). Schedule the autonomous-loop default and then run the first autonomous check immediately.`,k=e?"it expands at fire time to the full loop.md contents on first delivery (and whenever loop.md has been edited since last fire), and to a short reminder on subsequent unchanged fires. The long instructions stay in the cached message-prefix.":"it expands at fire time to the full autonomous-loop instructions on first delivery, and to a short reminder on subsequent fires (the long instructions stay in the cached message-prefix).",v=e?`what's scheduled, the cron expression, the human-readable cadence, that it's running tasks from \`${e.path}\`, that recurring tasks auto-expire after ${Ofe} days, and that the user can cancel sooner with ${vA} (include the job ID).`:`what's scheduled, the cron expression, the human-readable cadence, that recurring tasks auto-expire after ${Ofe} days, and that they can cancel sooner with ${vA} (include the job ID). Mention this is the autonomous default and that the autonomous-loop instructions are baked in.`;return`${u}
136
+
137
+ ## Action
138
+
139
+ 1. Convert \`${t}\` to a 5-field cron expression. Supported suffixes: \`s\` \u2192 ceil to nearest minute, \`m\` (minutes), \`h\` (hours), \`d\` (days). Examples: \`5m\` \u2192 \`*/5 * * * *\`, \`1h\` \u2192 \`0 * * * *\`, \`1d\` \u2192 \`0 0 * * *\`. If the interval doesn't cleanly divide its unit, round to the nearest clean interval and tell the user what you rounded to.
140
+ 2. Call ${ky} with:
141
+ - \`cron\`: the expression from step 1
142
+ - \`prompt\`: the literal string \`${l}\` \u2014 ${k}
143
+ - \`recurring\`: \`true\`
144
+ 3. Briefly confirm: ${v}
145
+ 4. **Then immediately run ${h} now**, following the instructions inlined below. Don't wait for the first cron fire.
146
+
147
+ ${s}
148
+
149
+ ${r}`}
150
+
151
+ /**
152
+ * What `/loop <args>` sends the model, as in Claude Code:
153
+ * no prompt runs the autonomous default, self-paced when there is no
154
+ * interval either; anything else goes to the model to parse.
155
+ */
156
+ export function loopSkillPrompt(args: string, loopFile: LoopFile | null): string {
157
+ const t = args.trim();
158
+ const every = t.match(EVERY_ONLY);
159
+ if (!t || INTERVAL_ONLY.test(t) || every !== null) {
160
+ return noPromptBody(loopFile, !t, every ? normalizeEvery(every) : t || N);
161
+ }
162
+ return explicitPrompt(t);
163
+ }
@@ -58,3 +58,56 @@ export function formatMonitorEvents(id: string, batch: MonitorBatch): string {
58
58
  const more = hidden > 0 ? `\n… +${hidden} more line(s) not shown — task_output ${id} has the full stream` : "";
59
59
  return `${lines.join("\n")}${more}`;
60
60
  }
61
+
62
+ /** The longest line kept waiting for its newline; past it the text so far goes out as an event. */
63
+ export const MONITOR_MAX_LINE_CHARS = 16_000;
64
+
65
+ /**
66
+ * A carriage return overwrites the line, as a terminal shows it: keep the text
67
+ * after the last `\r` that has anything after it (a trailing `\r`, as in
68
+ * `\r\n`, is dropped first).
69
+ */
70
+ function overwriteLine(text: string): string {
71
+ const trimmed = text.replace(/\r+$/, "");
72
+ const at = trimmed.lastIndexOf("\r");
73
+ return at === -1 ? trimmed : trimmed.slice(at + 1);
74
+ }
75
+
76
+ /**
77
+ * Splits a monitor's stdout into event lines, bounded. Complete lines come out
78
+ * as they arrive; `end()` returns the last line when the stream stopped
79
+ * without a newline (a `curl` body, a `printf 'READY'`), which the monitor
80
+ * used to drop and then report "no output". A `\r` progress meter never grows
81
+ * the buffer past its latest segment, and a line longer than
82
+ * MONITOR_MAX_LINE_CHARS goes out at the cap instead of waiting forever.
83
+ */
84
+ export class MonitorLineSplitter {
85
+ private buffer = "";
86
+
87
+ push(text: string): string[] {
88
+ this.buffer += text;
89
+ const lines: string[] = [];
90
+ let idx: number;
91
+ while ((idx = this.buffer.indexOf("\n")) !== -1) {
92
+ const line = overwriteLine(this.buffer.slice(0, idx)).trimEnd();
93
+ this.buffer = this.buffer.slice(idx + 1);
94
+ if (line) lines.push(line);
95
+ }
96
+ // Only the latest `\r` segment can still show; a lone trailing `\r` may be half of `\r\n`.
97
+ const cr = this.buffer.lastIndexOf("\r", this.buffer.length - 2);
98
+ if (cr !== -1) this.buffer = this.buffer.slice(cr + 1);
99
+ if (this.buffer.length > MONITOR_MAX_LINE_CHARS) {
100
+ const line = overwriteLine(this.buffer).trimEnd();
101
+ this.buffer = "";
102
+ if (line) lines.push(line);
103
+ }
104
+ return lines;
105
+ }
106
+
107
+ /** The stream ended: the unterminated last line, if any. */
108
+ end(): string[] {
109
+ const line = overwriteLine(this.buffer).trimEnd();
110
+ this.buffer = "";
111
+ return line ? [line] : [];
112
+ }
113
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Claude Code's no-op folding for self-paced `/loop` (2.1.282, findings §21).
3
+ * When a wakeup fires and the tick since the previous wakeup changed nothing
4
+ * (the model called schedule_wakeup with `noop: true`, and nothing vetoes
5
+ * it), that tick is folded: its messages leave the model's context and the
6
+ * new wakeup carries the streak, shown as
7
+ * `Resuming /loop wakeup (<time>) · <N> no-op ticks since <time>` and told to
8
+ * the model as `[<N> prior /loop wakeups found nothing actionable; loop is
9
+ * healthy.]`.
10
+ *
11
+ * `decideFold` applies Claude Code's rules to the session branch, run at fire
12
+ * time; its answer is stored on the new wakeup message, so `applyNoopFolds`
13
+ * (the `context` handler) gives the same context on every request. pi cannot
14
+ * remove messages already drawn, so the terminal keeps the folded tick above
15
+ * the streak line.
16
+ *
17
+ * Pure: no pi imports.
18
+ */
19
+
20
+ /** The wakeup fire message's details that record a fold. */
21
+ export interface FoldDetails {
22
+ noOpStreak?: number;
23
+ streakStartedAt?: number;
24
+ }
25
+
26
+ /** The slice of a session entry the fold reads. */
27
+ export type FoldEntry =
28
+ | { kind: "wakeup"; timestamp: number; details?: FoldDetails }
29
+ | { kind: "fire" }
30
+ | { kind: "compaction" }
31
+ | { kind: "assistant"; toolCalls: Array<{ id: string; name: string; noop?: unknown }>; aborted: boolean }
32
+ | { kind: "toolResult"; toolCallId: string }
33
+ | { kind: "user" }
34
+ | { kind: "other" };
35
+
36
+ export type FoldDecision =
37
+ | { kind: "none" }
38
+ | { kind: "veto"; reason: string }
39
+ | { kind: "fold"; priorStreak: number; since: number };
40
+
41
+ /** Should the tick since the last wakeup fold into the one firing now? */
42
+ export function decideFold(entries: readonly FoldEntry[]): FoldDecision {
43
+ let anchorIdx = -1;
44
+ for (let i = entries.length - 1; i >= 0; i--) {
45
+ if (entries[i].kind === "wakeup") {
46
+ anchorIdx = i;
47
+ break;
48
+ }
49
+ }
50
+ if (anchorIdx === -1) return { kind: "none" };
51
+ const anchor = entries[anchorIdx] as Extract<FoldEntry, { kind: "wakeup" }>;
52
+ for (let i = anchorIdx - 1; i >= 0; i--) {
53
+ const entry = entries[i];
54
+ if (entry.kind === "wakeup") break;
55
+ if (entry.kind === "fire" || entry.kind === "compaction") return { kind: "veto", reason: "blocking_system_before_anchor" };
56
+ }
57
+ let noop: boolean | undefined;
58
+ const calls = new Set<string>();
59
+ for (let i = anchorIdx + 1; i < entries.length; i++) {
60
+ const entry = entries[i];
61
+ if (entry.kind === "wakeup" || entry.kind === "fire" || entry.kind === "compaction") return { kind: "veto", reason: "blocking_system_in_span" };
62
+ if (entry.kind === "assistant") {
63
+ if (entry.aborted) return { kind: "veto", reason: "tool_abort" };
64
+ for (const call of entry.toolCalls) {
65
+ calls.add(call.id);
66
+ if (call.name === "schedule_wakeup") noop = call.noop === true;
67
+ }
68
+ } else if (entry.kind === "toolResult") {
69
+ if (!calls.has(entry.toolCallId)) return { kind: "veto", reason: "split_tool_pair" };
70
+ } else if (entry.kind === "user") {
71
+ return { kind: "veto", reason: "foreign_user_input" };
72
+ }
73
+ }
74
+ if (noop !== true) return { kind: "veto", reason: "model_reported_work" };
75
+ return { kind: "fold", priorStreak: anchor.details?.noOpStreak ?? 0, since: anchor.details?.streakStartedAt ?? anchor.timestamp };
76
+ }
77
+
78
+ /** Claude Code's note to the model in place of the folded ticks. */
79
+ export function foldCompanion(streak: number): string {
80
+ return `[${streak} prior /loop ${streak === 1 ? "wakeup" : "wakeups"} found nothing actionable; loop is healthy.]`;
81
+ }
82
+
83
+ /** The streak part of the wakeup line, after `Resuming /loop wakeup (<time>)`. */
84
+ export function foldSuffix(streak: number, since: string): string {
85
+ return ` · ${streak} no-op ${streak === 1 ? "tick" : "ticks"} since ${since}`;
86
+ }
87
+
88
+ interface ContextMessage {
89
+ role: string;
90
+ customType?: string;
91
+ details?: unknown;
92
+ timestamp?: number;
93
+ }
94
+
95
+ const WAKEUP_TYPE = "wakeup";
96
+
97
+ function streakOf(message: ContextMessage): number {
98
+ if (message.role !== "custom" || message.customType !== WAKEUP_TYPE) return 0;
99
+ const streak = (message.details as FoldDetails | undefined)?.noOpStreak;
100
+ return typeof streak === "number" && streak > 0 ? streak : 0;
101
+ }
102
+
103
+ /**
104
+ * The model's context with every folded tick removed: before a wakeup that
105
+ * carries a streak, drop everything back to (and including) the previous
106
+ * wakeup and the note in front of it, and put the note for this streak in
107
+ * front of it. Unchanged when nothing folded.
108
+ */
109
+ export function applyNoopFolds<M extends ContextMessage>(messages: readonly M[], companion: (text: string, timestamp: number) => M): M[] | undefined {
110
+ if (!messages.some((m) => streakOf(m) > 0)) return undefined;
111
+ const out: M[] = [];
112
+ const companions = new Set<M>();
113
+ let lastWakeup = -1;
114
+ for (const message of messages) {
115
+ const streak = streakOf(message);
116
+ if (streak > 0 && lastWakeup !== -1) {
117
+ let cut = lastWakeup;
118
+ if (cut > 0 && companions.has(out[cut - 1])) cut--;
119
+ out.length = cut;
120
+ const note = companion(foldCompanion(streak), message.timestamp ?? Date.now());
121
+ companions.add(note);
122
+ out.push(note);
123
+ }
124
+ if (message.role === "custom" && message.customType === WAKEUP_TYPE) lastWakeup = out.length;
125
+ out.push(message);
126
+ }
127
+ return out;
128
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * What a session switch or quit would stop (pure). Running background tasks and
3
+ * scheduled jobs belong to the session: `/clear`, `/new`, `/resume` and a fork
4
+ * stop them, and so do `/reload` and quitting. pi lets an extension cancel a
5
+ * switch (`session_before_switch`, `session_before_fork`), so a switch asks
6
+ * first; reloading and quitting have no such hook, so the standing widget line
7
+ * says it up front.
8
+ */
9
+
10
+ /** A running task or scheduled job, as the dialog and widget name it. */
11
+ export interface SessionWork {
12
+ id: string;
13
+ label: string;
14
+ }
15
+
16
+ export const SWITCH_CANCEL = "Cancel";
17
+ export const SWITCH_STOP = "Stop them and continue";
18
+
19
+ /** At most this many items are named in the dialog; the rest are counted. */
20
+ const MAX_NAMED = 6;
21
+
22
+ const plural = (n: number, word: string) => `${n} ${word}${n === 1 ? "" : "s"}`;
23
+
24
+ function counts(tasks: number, jobs: number): string {
25
+ const parts = [tasks > 0 ? plural(tasks, "background task") : "", jobs > 0 ? plural(jobs, "scheduled job") : ""].filter(Boolean);
26
+ return parts.join(" and ");
27
+ }
28
+
29
+ /**
30
+ * The confirm dialog's title, or undefined when nothing would be stopped.
31
+ * `action` names the switch ("Starting a new session"): pi reports /clear and
32
+ * /new alike, so the dialog cannot name the command.
33
+ */
34
+ export function switchWarning(tasks: readonly SessionWork[], jobs: readonly SessionWork[], action: string): string | undefined {
35
+ if (tasks.length === 0 && jobs.length === 0) return undefined;
36
+ const items = [...tasks, ...jobs];
37
+ const named = items.slice(0, MAX_NAMED).map((item) => ` ${item.id} ${item.label}`);
38
+ if (items.length > MAX_NAMED) named.push(` … and ${items.length - MAX_NAMED} more`);
39
+ const one = items.length === 1;
40
+ const them = one ? "it" : "them";
41
+ return [
42
+ `${counts(tasks.length, jobs.length)} ${one ? "is" : "are"} still active. ${action} stops ${them}:`,
43
+ named.join("\n"),
44
+ tasks.length > 0 ? `Cancel to keep ${them} running; /tasks opens or stops a task.` : `Cancel to keep ${them}.`,
45
+ ].join("\n\n");
46
+ }
47
+
48
+ /** The standing widget line while anything runs, or undefined when nothing does. */
49
+ export function workWidgetLine(tasks: number, jobs: number): string | undefined {
50
+ if (tasks === 0 && jobs === 0) return undefined;
51
+ return ` ${counts(tasks, jobs)} · stopped by /clear, /reload or quitting${tasks > 0 ? " · /tasks to manage" : ""}`;
52
+ }