one-code-extension 0.4.1 → 0.6.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.
- package/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +40 -28
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +26 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +124 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +642 -0
- package/extensions/background/index.ts +614 -248
- package/extensions/background/loop-fire.ts +173 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +56 -0
- package/extensions/background/wakeup.ts +178 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +66 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +250 -78
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/cache-marks/index.ts +42 -0
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +436 -63
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +219 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/budget.ts +16 -2
- package/extensions/context-budget/index.ts +35 -15
- package/extensions/context-management/index.ts +36 -28
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +141 -25
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +420 -42
- package/extensions/file-tracker/replay.ts +137 -24
- package/extensions/file-tracker/restore.ts +66 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +45 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +209 -58
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/prompt-context.ts +59 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +26 -12
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/ledger/index.ts +46 -0
- package/extensions/ledger/wire.ts +154 -0
- package/extensions/lib/agent-cron.ts +146 -0
- package/extensions/lib/agent-loader.ts +18 -3
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-auth.ts +98 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +52 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +97 -0
- package/extensions/lib/covered-messages.ts +23 -0
- package/extensions/lib/deferred.ts +359 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/ledger/fold-memo.ts +49 -0
- package/extensions/lib/ledger/fold.ts +265 -0
- package/extensions/lib/ledger/layout.ts +72 -0
- package/extensions/lib/ledger/plan.ts +102 -0
- package/extensions/lib/ledger/records.ts +276 -0
- package/extensions/lib/ledger/state.ts +47 -0
- package/extensions/lib/ledger/validate.ts +141 -0
- package/extensions/lib/ledger/wire.ts +632 -0
- package/extensions/lib/local-command.ts +71 -19
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +178 -0
- package/extensions/lib/model-catalog/models-dev.json +18533 -0
- package/extensions/lib/model-catalog/openrouter.json +5050 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +357 -73
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +38 -2
- package/extensions/lib/plan-mode-channels.ts +42 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/previous-mark.ts +101 -0
- package/extensions/lib/process-state.ts +48 -0
- package/extensions/lib/process-tree.ts +132 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +370 -358
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +547 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +24 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +112 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +129 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +159 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +76 -12
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +90 -43
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +220 -121
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +164 -73
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/model-default/index.ts +86 -19
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1426 -398
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +11 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +276 -92
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/run-state.ts +26 -0
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +64 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +14 -7
- package/extensions/recap/index.ts +73 -71
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/announce.ts +30 -0
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +469 -132
- package/extensions/skill/invoke.ts +118 -75
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +146 -31
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +82 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1594 -852
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +5 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/parent-mode.ts +50 -0
- package/extensions/subagents/pending-claim.ts +18 -1
- package/extensions/subagents/runner.ts +135 -80
- package/extensions/subagents/runs.ts +44 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +165 -47
- package/extensions/system-prompt/template.ts +34 -51
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +204 -70
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/index.ts +366 -144
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +108 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +157 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +33 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +162 -44
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +21 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- package/extensions/lib/model-facts.ts +0 -162
- package/extensions/tool-search/announce.ts +0 -42
|
@@ -13,31 +13,64 @@
|
|
|
13
13
|
* turn puts the block on the queue before the turn's next request — until
|
|
14
14
|
* 2026-09-05 the mid-turn requests ran with no plan reminder while every edit
|
|
15
15
|
* was denied "see the plan-mode reminder" (STEERING-REVIEW-2026-09-05 M3) —
|
|
16
|
-
* and on
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* allocation.
|
|
16
|
+
* and on session_start, after restoring a previous path and its baseline.
|
|
17
|
+
* A session that STARTS in plan mode must install the block before the first
|
|
18
|
+
* prompt is stamped: before_agent_start is already too late to anchor it to
|
|
19
|
+
* that prompt. before_agent_start also re-announces the existing state.
|
|
21
20
|
*/
|
|
22
21
|
|
|
23
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
-
import type
|
|
22
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
25
|
+
import { registerFormTool } from "../lib/tool-variants.ts";
|
|
26
26
|
import { Type } from "typebox";
|
|
27
27
|
import { DEFER_CHANNEL } from "../lib/deferred.ts";
|
|
28
28
|
import { oneCodeStateDir } from "../lib/paths.ts";
|
|
29
29
|
import { REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
30
|
+
import { canShowCustomUi } from "../lib/headless-output.ts";
|
|
30
31
|
import { ccToolRenderers, safeThemePaint } from "../lib/tui-render.ts";
|
|
32
|
+
import { userDenialText } from "../lib/user-denial.ts";
|
|
31
33
|
import type { PermissionMode } from "../permissions/matcher.ts";
|
|
32
34
|
import { PERMISSION_STATUS_CHANNEL, type PermissionStatus } from "../permissions/modes.ts";
|
|
33
35
|
import { buildPlanModeReminder } from "./reminder.ts";
|
|
34
36
|
import { randomSlug } from "./slug.ts";
|
|
35
|
-
import {
|
|
37
|
+
import { readState, recordState } from "../lib/ledger/state.ts";
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
/** A plan file larger than this is named after a compaction, not inlined: the model reads it when it needs it. */
|
|
40
|
+
const PLAN_REFERENCE_CHARS = 40_000;
|
|
41
|
+
|
|
42
|
+
/** The plan the summary replaced, re-attached after a compaction. */
|
|
43
|
+
function planFileReferenceText(path: string, content: string): string {
|
|
44
|
+
if (content.length > PLAN_REFERENCE_CHARS) return `A plan file exists from plan mode at: ${path}\n\nIt is too large to include here (${content.length.toLocaleString("en-US")} characters); read it before continuing.\n\nIf this plan is relevant to the current work and not already complete, continue working on it.`;
|
|
45
|
+
return `A plan file exists from plan mode at: ${path}\n\nPlan contents:\n\n${content}\n\nIf this plan is relevant to the current work and not already complete, continue working on it.`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
import { PLAN_ENTRY_STATE, parsePlanRun, type PlanRun } from "./run-state.ts";
|
|
49
|
+
import {
|
|
50
|
+
approvedPlanText,
|
|
51
|
+
ENTER_PLAN_MODE_DESCRIPTION,
|
|
52
|
+
enteredPlanModeText,
|
|
53
|
+
enterPlanModeDescription,
|
|
54
|
+
EXIT_PLAN_MODE_DESCRIPTION,
|
|
55
|
+
exitedPlanModeText,
|
|
56
|
+
} from "./texts.ts";
|
|
57
|
+
import { createMarkdownProse } from "../subagents/prose.ts";
|
|
58
|
+
import type { ProseRenderer } from "../subagents/panel-render.ts";
|
|
59
|
+
import {
|
|
60
|
+
clampOffset,
|
|
61
|
+
decodeViewerKey,
|
|
62
|
+
initialPlanChoice,
|
|
63
|
+
KEEP_PLANNING_QUESTION,
|
|
64
|
+
type PlanChoice,
|
|
65
|
+
planLines,
|
|
66
|
+
renderPlanViewer,
|
|
67
|
+
runPlanApproval,
|
|
68
|
+
selectPlanChoice,
|
|
69
|
+
} from "./viewer.ts";
|
|
70
|
+
|
|
71
|
+
import { MODE_CHANNEL, PLAN_FILE_CHANNEL, PLAN_FILE_ENTRY, planFileOnBranch, planFileRecords, type PlanFileRecord } from "../lib/plan-mode-channels.ts";
|
|
72
|
+
import { panelUi } from "../lib/panel-focus.ts";
|
|
38
73
|
export { MODE_CHANNEL, PLAN_FILE_CHANNEL };
|
|
39
|
-
/** Session entry type persisting the allocated path across resume/branch. */
|
|
40
|
-
const PLAN_FILE_ENTRY = "one-code:plan-mode-file";
|
|
41
74
|
|
|
42
75
|
const PLANS_DIR = () => join(oneCodeStateDir(), "plans");
|
|
43
76
|
|
|
@@ -55,48 +88,126 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
55
88
|
/** The last mode broadcast by permissions; undefined until the first status arrives. */
|
|
56
89
|
let currentMode: string | undefined;
|
|
57
90
|
/**
|
|
58
|
-
* The mode the
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* (PERMISSIONS-REVIEW-2026-09-05 M4).
|
|
91
|
+
* The live mode the last switch into plan mode came from. It is recorded on
|
|
92
|
+
* the branch only when that switch begins a run (`refresh` finds no run
|
|
93
|
+
* recorded for the branch's plan file). A switch that merely restores a run,
|
|
94
|
+
* permissions following a /tree to a branch in plan mode, keeps the branch's
|
|
95
|
+
* own value, since the live mode before it belongs to the abandoned branch.
|
|
64
96
|
*/
|
|
65
|
-
let
|
|
66
|
-
|
|
97
|
+
let enteredFrom: PermissionMode | undefined;
|
|
98
|
+
/** Whether the session can enter bypassPermissions, as permissions last said: the exit offers it back only then. */
|
|
99
|
+
let bypassAvailable = false;
|
|
67
100
|
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
/**
|
|
102
|
+
* The current run as the branch recorded it when plan mode came on: its plan
|
|
103
|
+
* file, whether that file existed then (which picks Claude Code's "Plan File
|
|
104
|
+
* Info" line, fixed for the run so the sticky block stays byte-identical turn
|
|
105
|
+
* after turn, plan-mode/reminder.ts), and the mode it came from, offered
|
|
106
|
+
* first on exit (Claude Code's ExitPlanMode restores `prePlanMode`). Read
|
|
107
|
+
* from the branch every time, never kept here, so a /reload, a resume and a
|
|
108
|
+
* /tree each see the run of the branch they land on. Until 2026-09-05 the
|
|
109
|
+
* exit could only escalate: a default-mode session that planned was offered
|
|
110
|
+
* auto (PERMISSIONS-REVIEW-2026-09-05 M4).
|
|
111
|
+
*/
|
|
112
|
+
const runOnBranch = (ctx: ExtensionContext, path: string): PlanRun | undefined => parsePlanRun(readState(ctx, PLAN_ENTRY_STATE), path);
|
|
113
|
+
/** Record the current run on the branch, for `runOnBranch` to read back. */
|
|
114
|
+
const recordRun = (path: string, run: PlanRun) => recordState(pi, PLAN_ENTRY_STATE, { path, ...run });
|
|
78
115
|
|
|
116
|
+
const freshPlanPath = (): string => {
|
|
79
117
|
let path = join(PLANS_DIR(), `${randomSlug()}.md`);
|
|
80
118
|
for (let attempt = 0; attempt < 5 && existsSync(path); attempt++) {
|
|
81
119
|
path = join(PLANS_DIR(), `${randomSlug()}.md`);
|
|
82
120
|
}
|
|
83
|
-
planFilePath = path;
|
|
84
|
-
pi.appendEntry(PLAN_FILE_ENTRY, { path });
|
|
85
121
|
return path;
|
|
86
122
|
};
|
|
87
123
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
124
|
+
/**
|
|
125
|
+
* A fork's own copy of the plan another session recorded, as Claude Code
|
|
126
|
+
* gives a forked session, so the two sessions never overwrite each other's plan
|
|
127
|
+
* (decisions/modes.md). The copy this session already made of it, on any
|
|
128
|
+
* of its branches, is reused. The run moves to the copy with the mode it
|
|
129
|
+
* came from, and says whether the copy exists (the parent's run may have
|
|
130
|
+
* begun before its plan did).
|
|
131
|
+
*/
|
|
132
|
+
const forkPlanFile = (ctx: ExtensionContext, parent: PlanFileRecord, sessionId: string): string => {
|
|
133
|
+
const made = planFileRecords(ctx.sessionManager.getEntries()).find((r) => r.sessionId === sessionId && r.copiedFrom === parent.path);
|
|
134
|
+
const path = made?.path ?? freshPlanPath();
|
|
135
|
+
if (!made && existsSync(parent.path)) {
|
|
136
|
+
try {
|
|
137
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
138
|
+
copyFileSync(parent.path, path);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (ctx.hasUI) ctx.ui.notify(`Could not copy the plan at ${parent.path} for this fork (${(error as Error).message}); its plan starts empty at ${path}.`, "warning");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const run = runOnBranch(ctx, parent.path);
|
|
144
|
+
pi.appendEntry(PLAN_FILE_ENTRY, { path, sessionId, copiedFrom: parent.path } satisfies PlanFileRecord);
|
|
145
|
+
// The standing block names the new path anyway, so it also says whether the copy exists.
|
|
146
|
+
if (run) recordRun(path, { ...run, existed: existsSync(path) });
|
|
147
|
+
return path;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The branch's plan file, else a fresh slug recorded on the branch. A
|
|
152
|
+
* record another session made (a fork copies its parent's entries) gets
|
|
153
|
+
* this session's own copy, and so does a record that names no session (an
|
|
154
|
+
* older session's): only a record this session made is its own.
|
|
155
|
+
* Never cached: on /tree, permissions' session_tree switches the mode (and
|
|
156
|
+
* this extension refreshes) before this extension's own session_tree runs,
|
|
157
|
+
* so a cached path would be the abandoned branch's.
|
|
158
|
+
*/
|
|
159
|
+
const ensurePlanFile = (ctx: ExtensionContext, onBranch = planFileRecords(ctx.sessionManager.getBranch()).at(-1)): string => {
|
|
160
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
161
|
+
if (onBranch?.sessionId === sessionId) return onBranch.path;
|
|
162
|
+
if (onBranch) return forkPlanFile(ctx, onBranch, sessionId);
|
|
163
|
+
|
|
164
|
+
const path = freshPlanPath();
|
|
165
|
+
pi.appendEntry(PLAN_FILE_ENTRY, { path, sessionId } satisfies PlanFileRecord);
|
|
166
|
+
return path;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** A branch that names a plan file owns it before anything reads it: a fork copies its parent's now. */
|
|
170
|
+
const ownPlanFile = (ctx: ExtensionContext) => {
|
|
171
|
+
const onBranch = planFileRecords(ctx.sessionManager.getBranch()).at(-1);
|
|
172
|
+
if (onBranch?.path) ensurePlanFile(ctx, onBranch);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/** Re-announce path + reminder; a re-emit replaces the sticky state by key. */
|
|
176
|
+
const refresh = (ctx: ExtensionContext, toolCallId?: string) => {
|
|
90
177
|
const path = ensurePlanFile(ctx);
|
|
178
|
+
let run = runOnBranch(ctx, path);
|
|
179
|
+
if (!run) {
|
|
180
|
+
// A run begins: record what it began with.
|
|
181
|
+
run = { existed: existsSync(path), ...(enteredFrom ? { modeBeforePlan: enteredFrom } : {}) };
|
|
182
|
+
recordRun(path, run);
|
|
183
|
+
}
|
|
91
184
|
pi.events.emit(PLAN_FILE_CHANNEL, { path });
|
|
92
185
|
pi.events.emit(REMINDER_CHANNEL, {
|
|
93
|
-
text: buildPlanModeReminder(path),
|
|
94
|
-
scope: "every-turn",
|
|
186
|
+
text: buildPlanModeReminder(path, run.existed),
|
|
95
187
|
key: "permission-mode",
|
|
96
188
|
placement: "sticky-append",
|
|
189
|
+
toolCallId,
|
|
97
190
|
});
|
|
98
191
|
};
|
|
99
192
|
|
|
193
|
+
/**
|
|
194
|
+
* pi-tui's Markdown renderer for the approval dialog, loaded on first use.
|
|
195
|
+
* A failed load is not cached (the theme may not be ready yet); the dialog
|
|
196
|
+
* then wraps the plan as plain text.
|
|
197
|
+
*/
|
|
198
|
+
let markdownLoad: Promise<ProseRenderer | undefined> | undefined;
|
|
199
|
+
const planMarkdown = (): Promise<ProseRenderer | undefined> =>
|
|
200
|
+
(markdownLoad ??= createMarkdownProse().catch(() => {
|
|
201
|
+
markdownLoad = undefined;
|
|
202
|
+
return undefined;
|
|
203
|
+
}));
|
|
204
|
+
|
|
205
|
+
/** Plan approvals the user dismissed without words: the turn stops once pi records their result. */
|
|
206
|
+
const abortAfterResult = new Set<string>();
|
|
207
|
+
pi.on("tool_execution_end", (event, ctx) => {
|
|
208
|
+
if (abortAfterResult.delete(event.toolCallId)) ctx.abort();
|
|
209
|
+
});
|
|
210
|
+
|
|
100
211
|
/** The last context seen, for a refresh triggered off the bus (no ctx of its own). */
|
|
101
212
|
let lastCtx: ExtensionContext | undefined;
|
|
102
213
|
|
|
@@ -105,18 +216,32 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
105
216
|
if (typeof status?.mode !== "string") return;
|
|
106
217
|
const previous = currentMode;
|
|
107
218
|
currentMode = status.mode;
|
|
108
|
-
|
|
109
|
-
//
|
|
219
|
+
bypassAvailable = status.bypassAvailable === true;
|
|
220
|
+
// Unknown for a session that STARTED in plan mode (the first status seen
|
|
221
|
+
// is plan itself): a run it begins offers the fixed list on exit.
|
|
222
|
+
if (currentMode === "plan" && previous !== "plan") enteredFrom = previous as PermissionMode | undefined;
|
|
223
|
+
// The run ended: the next entry decides afresh.
|
|
224
|
+
if (previous === "plan" && currentMode !== "plan") recordState(pi, PLAN_ENTRY_STATE, null);
|
|
225
|
+
// Entering plan mode mid-turn: permissions' setMode has just closed the
|
|
110
226
|
// shared "permission-mode" key (before broadcasting this status, so this
|
|
111
227
|
// re-add is not undone) and will announce the change on the next tool
|
|
112
228
|
// result; the standing block has to be back on the queue for that same
|
|
113
229
|
// request. The path is announced over PLAN_FILE_CHANNEL inside refresh,
|
|
114
230
|
// synchronously, so setMode's announcement can name it.
|
|
115
|
-
if (currentMode === "plan" && previous !== "plan" && lastCtx) refresh(lastCtx);
|
|
231
|
+
if (currentMode === "plan" && previous !== "plan" && lastCtx) refresh(lastCtx, status.toolCallId);
|
|
116
232
|
});
|
|
117
233
|
|
|
118
234
|
pi.on("session_start", (_event, ctx) => {
|
|
119
235
|
lastCtx = ctx;
|
|
236
|
+
ownPlanFile(ctx);
|
|
237
|
+
if (currentMode === "plan") refresh(ctx);
|
|
238
|
+
});
|
|
239
|
+
// The plan file and its run follow the branch (both are read from it): a
|
|
240
|
+
// /tree switch takes the selected branch's, or allocates one there.
|
|
241
|
+
pi.on("session_tree", (_event, ctx) => {
|
|
242
|
+
lastCtx = ctx;
|
|
243
|
+
ownPlanFile(ctx);
|
|
244
|
+
if (currentMode === "plan") refresh(ctx);
|
|
120
245
|
});
|
|
121
246
|
|
|
122
247
|
pi.on("before_agent_start", (_event, ctx) => {
|
|
@@ -124,42 +249,69 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
124
249
|
if (currentMode === "plan") refresh(ctx);
|
|
125
250
|
});
|
|
126
251
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
details: { planFilePath: path },
|
|
150
|
-
};
|
|
151
|
-
},
|
|
252
|
+
// A compaction summarizes the plan away and opens an epoch, where nothing
|
|
253
|
+
// above has to stay byte-stable (R3-M20). The plan file is re-attached
|
|
254
|
+
// whenever one exists, plan mode or not, as Claude Code does; in plan mode
|
|
255
|
+
// the run is recorded again, so the new epoch's block says whether the file
|
|
256
|
+
// exists now.
|
|
257
|
+
pi.on("session_compact", (_event, ctx) => {
|
|
258
|
+
lastCtx = ctx;
|
|
259
|
+
const path = planFileOnBranch(ctx.sessionManager.getBranch());
|
|
260
|
+
if (!path) return;
|
|
261
|
+
if (currentMode === "plan") {
|
|
262
|
+
recordRun(path, { ...runOnBranch(ctx, path), existed: existsSync(path) });
|
|
263
|
+
refresh(ctx);
|
|
264
|
+
}
|
|
265
|
+
let content = "";
|
|
266
|
+
try {
|
|
267
|
+
content = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
268
|
+
} catch {
|
|
269
|
+
// The re-attach is best effort, like the other post-compaction restores:
|
|
270
|
+
// an unreadable path (a directory, no permission) attaches nothing.
|
|
271
|
+
}
|
|
272
|
+
if (!content.trim()) return;
|
|
273
|
+
pi.events.emit(REMINDER_CHANNEL, { text: planFileReferenceText(path, content), placement: "user-prepend", orTail: true, untilCompaction: true });
|
|
152
274
|
});
|
|
153
275
|
|
|
276
|
+
/**
|
|
277
|
+
* enter_plan_mode's description follows the model's tier: Claude Code's,
|
|
278
|
+
* which prefers plan mode, on frontier and workhorse, and One Code's, which
|
|
279
|
+
* says most tasks do not need it, on cheap and tiny (`texts.ts`, through the
|
|
280
|
+
* shared lib/tool-variants.ts). A re-registration keeps the tool's active
|
|
281
|
+
* state, so the deferred tool stays deferred, and it sets no prompt snippet,
|
|
282
|
+
* so the system prompt never moves with it.
|
|
283
|
+
*/
|
|
284
|
+
registerFormTool(
|
|
285
|
+
pi,
|
|
286
|
+
defineTool({
|
|
287
|
+
name: "enter_plan_mode",
|
|
288
|
+
label: "Enter plan mode",
|
|
289
|
+
...ccToolRenderers("Enter plan mode"),
|
|
290
|
+
description: ENTER_PLAN_MODE_DESCRIPTION,
|
|
291
|
+
parameters: Type.Object({}),
|
|
292
|
+
async execute(toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
293
|
+
pi.events.emit(MODE_CHANNEL, { mode: "plan", toolCallId });
|
|
294
|
+
// setMode broadcasts synchronously over the status channel, so the
|
|
295
|
+
// listener above has already installed the block and announced the
|
|
296
|
+
// path (or the mode was plan already and both stand); name the file in
|
|
297
|
+
// the tool result too so the model can start writing this same turn.
|
|
298
|
+
const path = ensurePlanFile(ctx);
|
|
299
|
+
return {
|
|
300
|
+
content: [{ type: "text", text: enteredPlanModeText(path) }],
|
|
301
|
+
details: { planFilePath: path },
|
|
302
|
+
};
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
enterPlanModeDescription,
|
|
306
|
+
);
|
|
307
|
+
|
|
154
308
|
pi.registerTool({
|
|
155
309
|
name: "exit_plan_mode",
|
|
156
310
|
label: "Exit plan mode",
|
|
157
311
|
...ccToolRenderers("Exit plan mode"),
|
|
158
|
-
description:
|
|
159
|
-
"Signal that planning is complete and ask the user to approve the plan.\n" +
|
|
160
|
-
"Takes no parameters: the plan is read from the plan file named in the plan-mode reminder, which you must have written before calling this. The user reviews that file's contents.",
|
|
312
|
+
description: EXIT_PLAN_MODE_DESCRIPTION,
|
|
161
313
|
parameters: Type.Object({}),
|
|
162
|
-
async execute(
|
|
314
|
+
async execute(toolCallId, _params, signal, _onUpdate, ctx) {
|
|
163
315
|
// Guard the out-of-sequence call: without this, a cold exit_plan_mode
|
|
164
316
|
// (never entered plan mode) allocates a fresh empty plan file and blames
|
|
165
317
|
// "the plan file is empty" — a plausible but wrong cause a weak model
|
|
@@ -213,11 +365,15 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
213
365
|
// The mode the session was in before planning leads the list (Claude
|
|
214
366
|
// Code restores it), then auto mode when a classifier model is reachable —
|
|
215
367
|
// the same gate the mode cycle uses (permissions/index.ts autoInCycle) —
|
|
216
|
-
// and the fixed choices.
|
|
217
|
-
//
|
|
218
|
-
//
|
|
368
|
+
// and the fixed choices. A bypass before planning is offered back only
|
|
369
|
+
// when this session can grant it (a resume without the bypass flag
|
|
370
|
+
// cannot, and the request would land in default). Each approving
|
|
371
|
+
// choice carries the mode it switches to; the final "Keep planning"
|
|
372
|
+
// choice has none and leaves the session in plan mode.
|
|
219
373
|
const autoAvailable = ctx.modelRegistry.getAvailable().length > 0;
|
|
220
|
-
const
|
|
374
|
+
const modeBeforePlan = runOnBranch(ctx, path)?.modeBeforePlan;
|
|
375
|
+
const offerable = (mode: PermissionMode) => (mode !== "auto" || autoAvailable) && (mode !== "bypassPermissions" || bypassAvailable);
|
|
376
|
+
const restore = modeBeforePlan && offerable(modeBeforePlan) ? modeBeforePlan : undefined;
|
|
221
377
|
const fixedModes: PermissionMode[] = [...(autoAvailable ? (["auto"] as const) : []), "acceptEdits", "default"];
|
|
222
378
|
const options: { label: string; mode?: PermissionMode; restore?: boolean }[] = [
|
|
223
379
|
...(restore ? [{ label: `Approve — back to ${MODE_NAMES[restore]} (the mode before planning)`, mode: restore, restore: true }] : []),
|
|
@@ -226,11 +382,30 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
226
382
|
];
|
|
227
383
|
const choices = options.map((o) => o.label);
|
|
228
384
|
|
|
229
|
-
|
|
385
|
+
// The plan renders as Markdown, like assistant text in the transcript.
|
|
386
|
+
const prose = canShowCustomUi(ctx) ? await planMarkdown() : undefined;
|
|
387
|
+
const planRef = {};
|
|
388
|
+
const markdown = prose ? (text: string, width: number) => prose(planRef, text, width) : undefined;
|
|
389
|
+
|
|
390
|
+
// The scroll position and the highlighted choice outlive one showing: a
|
|
391
|
+
// blank keep-planning answer reopens the dialog where the user left it.
|
|
392
|
+
const maxVisible = 12;
|
|
393
|
+
let offset = 0;
|
|
394
|
+
let selected: PlanChoice = initialPlanChoice(options);
|
|
395
|
+
const showDialog = (): Promise<PlanChoice | null> => !canShowCustomUi(ctx) ? selectPlanChoice(ctx.ui, plan, path, choices, signal) : panelUi(pi.events, ctx.ui).custom<PlanChoice | null>((tui, theme, _keybindings, close) => {
|
|
396
|
+
// An aborted turn closes the dialog as a cancel; a choice or a cancel
|
|
397
|
+
// drops the abort listener. Either way the dialog settles once.
|
|
398
|
+
let settled = false;
|
|
399
|
+
const onAbort = () => done(null);
|
|
400
|
+
const done = (choice: PlanChoice | null) => {
|
|
401
|
+
if (settled) return;
|
|
402
|
+
settled = true;
|
|
403
|
+
signal?.removeEventListener("abort", onAbort);
|
|
404
|
+
close(choice);
|
|
405
|
+
};
|
|
406
|
+
if (signal?.aborted) queueMicrotask(onAbort);
|
|
407
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
230
408
|
const paint = safeThemePaint(theme);
|
|
231
|
-
const maxVisible = 12;
|
|
232
|
-
let offset = 0;
|
|
233
|
-
let selected: PlanChoice = initialPlanChoice(options);
|
|
234
409
|
let lineCount = 0;
|
|
235
410
|
// The dialog stays open while exit_plan_mode runs, so pi's working
|
|
236
411
|
// spinner drives ~8 frames a second — and re-wrapping the whole plan
|
|
@@ -243,7 +418,7 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
243
418
|
let wrapped: { width: number; lines: string[] } | undefined;
|
|
244
419
|
let frame: { key: string; lines: string[] } | undefined;
|
|
245
420
|
const wrapAt = (wrapWidth: number): string[] => {
|
|
246
|
-
if (wrapped?.width !== wrapWidth) wrapped = { width: wrapWidth, lines:
|
|
421
|
+
if (wrapped?.width !== wrapWidth) wrapped = { width: wrapWidth, lines: planLines(plan, wrapWidth, markdown) };
|
|
247
422
|
return wrapped.lines;
|
|
248
423
|
};
|
|
249
424
|
return {
|
|
@@ -274,29 +449,38 @@ export default function planModeExtension(pi: ExtensionAPI) {
|
|
|
274
449
|
},
|
|
275
450
|
};
|
|
276
451
|
});
|
|
452
|
+
const decision = await runPlanApproval(
|
|
453
|
+
options,
|
|
454
|
+
showDialog,
|
|
455
|
+
() => ctx.ui.input(KEEP_PLANNING_QUESTION, undefined, { signal }),
|
|
456
|
+
signal,
|
|
457
|
+
);
|
|
277
458
|
|
|
278
|
-
const picked =
|
|
459
|
+
const picked = decision.kind === "approve" ? options[decision.index] : undefined;
|
|
279
460
|
if (picked?.mode) {
|
|
280
|
-
pi.events.emit(MODE_CHANNEL, { mode: picked.mode });
|
|
461
|
+
pi.events.emit(MODE_CHANNEL, { mode: picked.mode, toolCallId });
|
|
462
|
+
// Claude Code's exit reminder, queued from execute so it is written
|
|
463
|
+
// into this result after the mode notice.
|
|
464
|
+
pi.events.emit(REMINDER_CHANNEL, { text: exitedPlanModeText(path), toolCallId });
|
|
281
465
|
return {
|
|
282
|
-
content: [
|
|
283
|
-
{
|
|
284
|
-
type: "text",
|
|
285
|
-
text: `Plan approved by the user. You may now implement it. The approved plan stays at ${path} for reference.`,
|
|
286
|
-
},
|
|
287
|
-
],
|
|
466
|
+
content: [{ type: "text", text: approvedPlanText(path, plan) }],
|
|
288
467
|
details: { plan, approved: true },
|
|
289
468
|
};
|
|
290
469
|
}
|
|
291
470
|
|
|
471
|
+
// Rejected: Claude Code's rejection text. With the user's words the model
|
|
472
|
+
// acts on them and keeps planning; without (Esc) the turn stops, as a
|
|
473
|
+
// reasonless permission rejection does (permissions/index.ts): `terminate`
|
|
474
|
+
// ends pi's loop after the batch, and the abort waits for the recorded
|
|
475
|
+
// result so the run ends as the user's interrupt.
|
|
476
|
+
const feedback = decision.kind === "reject" ? decision.feedback : undefined;
|
|
477
|
+
const stop = !feedback;
|
|
478
|
+
if (stop) abortAfterResult.add(toolCallId);
|
|
292
479
|
return {
|
|
293
|
-
content: [
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
},
|
|
298
|
-
],
|
|
299
|
-
details: { plan, approved: false },
|
|
480
|
+
content: [{ type: "text", text: userDenialText(feedback) }],
|
|
481
|
+
details: { plan, approved: false, ...(feedback ? { feedback } : {}) },
|
|
482
|
+
isError: true,
|
|
483
|
+
...(stop ? { terminate: true } : {}),
|
|
300
484
|
};
|
|
301
485
|
},
|
|
302
486
|
});
|
|
@@ -1,30 +1,98 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plan-mode reminder text (pure)
|
|
3
|
-
*
|
|
4
|
-
* carried by every user message since plan mode came
|
|
5
|
-
* that anchor only while the re-emitted text is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* Plan-mode reminder text (pure): Claude Code's plan-mode instructions, the
|
|
3
|
+
* five-phase workflow, with One Code's tool and agent names. The block is a
|
|
4
|
+
* sticky-append reminder carried by every user message since plan mode came
|
|
5
|
+
* on, and the queue keeps that anchor only while the re-emitted text is
|
|
6
|
+
* unchanged, so the text must not flip while the mode lasts: a text that
|
|
7
|
+
* flipped when the file appeared re-anchored the block and re-cached every
|
|
8
|
+
* message between entering plan mode and the first write
|
|
9
|
+
* (CACHE-REVIEW-2026-09-04 M2). Claude Code's own "Plan File Info" line
|
|
10
|
+
* depends on whether the plan file exists; the caller decides that once, when
|
|
11
|
+
* plan mode comes on (`planExists`), and keeps it until the mode ends.
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* parameter. Claude Code's own text does vary with the file's existence; ours
|
|
13
|
-
* folds both states into one sentence so the cached prefix survives.
|
|
13
|
+
* Claude Code puts this text in a system message on the models that take one
|
|
14
|
+
* and in a reminder elsewhere; One Code delivers it through the reminder
|
|
15
|
+
* queue's plan-mode placement either way.
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/** Claude Code's "Plan File Info" line, for a plan file that exists or not yet. */
|
|
19
|
+
export function planFileInfo(filePath: string, planExists: boolean): string {
|
|
20
|
+
return planExists
|
|
21
|
+
? `A plan file already exists at ${filePath}. You can read it and make incremental edits using the edit tool.`
|
|
22
|
+
: `No plan file exists yet. You should create your plan at ${filePath} using the write tool.`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function buildPlanModeReminder(filePath: string, planExists = false): string {
|
|
26
|
+
const fileInfo = planFileInfo(filePath, planExists);
|
|
18
27
|
return [
|
|
19
|
-
|
|
28
|
+
"Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits (with the exception of the plan file mentioned below), run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received.",
|
|
29
|
+
"",
|
|
30
|
+
"## Plan File Info:",
|
|
31
|
+
fileInfo,
|
|
32
|
+
"You should build your plan incrementally by writing to or editing this file. NOTE that this is the only file you are allowed to edit - other than this you are only allowed to take READ-ONLY actions.",
|
|
33
|
+
"",
|
|
34
|
+
"## Plan Workflow",
|
|
35
|
+
"",
|
|
36
|
+
"### Phase 1: Initial Understanding",
|
|
37
|
+
"Goal: Gain a comprehensive understanding of the user's request by reading through code and asking them questions. Critical: In this phase you should only use the explore subagent type.",
|
|
38
|
+
"",
|
|
39
|
+
"1. Focus on understanding the user's request and the code associated with their request. Actively search for existing functions, utilities, and patterns that can be reused — avoid proposing new code when suitable implementations already exist.",
|
|
40
|
+
"",
|
|
41
|
+
"2. **Launch up to 3 explore agents IN PARALLEL** (single message, multiple tool calls) to efficiently explore the codebase.",
|
|
42
|
+
" - Use 1 agent when the task is isolated to known files, the user provided specific file paths, or you're making a small targeted change.",
|
|
43
|
+
" - Use multiple agents when: the scope is uncertain, multiple areas of the codebase are involved, or you need to understand existing patterns before planning.",
|
|
44
|
+
" - Quality over quantity - 3 agents maximum, but you should try to use the minimum number of agents necessary (usually just 1)",
|
|
45
|
+
" - If using multiple agents: Provide each agent with a specific search focus or area to explore. Example: One agent searches for existing implementations, another explores related components, a third investigating testing patterns",
|
|
46
|
+
"",
|
|
47
|
+
"### Phase 2: Design",
|
|
48
|
+
"Goal: Design an implementation approach.",
|
|
49
|
+
"",
|
|
50
|
+
"Launch plan agent(s) to design the implementation based on the user's intent and your exploration results from Phase 1.",
|
|
51
|
+
"",
|
|
52
|
+
"You can launch up to 3 agent(s) in parallel.",
|
|
53
|
+
"",
|
|
54
|
+
"**Guidelines:**",
|
|
55
|
+
"- **Default**: Launch at least 1 plan agent for most tasks - it helps validate your understanding and consider alternatives",
|
|
56
|
+
"- **Skip agents**: Only for truly trivial tasks (typo fixes, single-line changes, simple renames)",
|
|
57
|
+
"- **Multiple agents**: Use up to 3 agents for complex tasks that benefit from different perspectives",
|
|
58
|
+
"",
|
|
59
|
+
"Examples of when to use multiple agents:",
|
|
60
|
+
"- The task touches multiple parts of the codebase",
|
|
61
|
+
"- It's a large refactor or architectural change",
|
|
62
|
+
"- There are many edge cases to consider",
|
|
63
|
+
"- You'd benefit from exploring different approaches",
|
|
64
|
+
"",
|
|
65
|
+
"Example perspectives by task type:",
|
|
66
|
+
"- New feature: simplicity vs performance vs maintainability",
|
|
67
|
+
"- Bug fix: root cause vs workaround vs prevention",
|
|
68
|
+
"- Refactoring: minimal change vs clean architecture",
|
|
69
|
+
"",
|
|
70
|
+
"In the agent prompt:",
|
|
71
|
+
"- Provide comprehensive background context from Phase 1 exploration including filenames and code path traces",
|
|
72
|
+
"- Describe requirements and constraints",
|
|
73
|
+
"- Request a detailed implementation plan",
|
|
74
|
+
"",
|
|
75
|
+
"### Phase 3: Review",
|
|
76
|
+
"Goal: Review the plan(s) from Phase 2 and ensure alignment with the user's intentions.",
|
|
77
|
+
"1. Read the critical files you identified during exploration to deepen your understanding",
|
|
78
|
+
"2. Ensure that the plans align with the user's original request",
|
|
79
|
+
"3. Use ask_user_question to clarify any remaining questions with the user",
|
|
80
|
+
"",
|
|
81
|
+
"### Phase 4: Final Plan",
|
|
82
|
+
"Goal: Write your final plan to the plan file (the only file you can edit).",
|
|
83
|
+
"- Begin with a **Context** section: explain why this change is being made — the problem or need it addresses, what prompted it, and the intended outcome",
|
|
84
|
+
"- Include only your recommended approach, not all alternatives",
|
|
85
|
+
"- Ensure that the plan file is concise enough to scan quickly, but detailed enough to execute effectively",
|
|
86
|
+
"- Name the critical files to be modified. For changes that repeat a pattern across many files, describe the pattern once and list a few representative paths — do not enumerate every file or line number",
|
|
87
|
+
"- Reference existing functions and utilities you found that should be reused, with their file paths",
|
|
88
|
+
"- Include a verification section describing how to test the changes end-to-end (run the code, use MCP tools, run tests)",
|
|
89
|
+
"",
|
|
90
|
+
"### Phase 5: Call exit_plan_mode",
|
|
91
|
+
"At the very end of your turn, once you have asked the user questions and are happy with your final plan file - you should always call exit_plan_mode to indicate to the user that you are done planning.",
|
|
92
|
+
"This is critical - your turn should only end with either using the ask_user_question tool OR calling exit_plan_mode. Do not stop unless it's for these 2 reasons",
|
|
20
93
|
"",
|
|
21
|
-
"
|
|
22
|
-
'1. Explore: delegate broad reconnaissance to the `Agent` tool with `subagent_type: "explore"` (up to 3 in parallel) rather than reading everything yourself.',
|
|
23
|
-
'2. Design: delegate the implementation strategy to the `Agent` tool with `subagent_type: "plan"` once exploration has mapped the ground.',
|
|
24
|
-
"3. Review the critical files yourself, and call `ask_user_question` for any decision you cannot make on the user's behalf.",
|
|
25
|
-
`4. Write the final plan to ${filePath}: a Context section explaining why, your recommended approach (not several options), the critical files named, existing utilities to reuse, and a verification section.`,
|
|
26
|
-
"5. Call `exit_plan_mode` to ask the user to approve the plan. It reads the plan file directly — you do not pass the plan as a parameter. If `exit_plan_mode` is not in your active tools, load it first with tool_search (`select:exit_plan_mode`).",
|
|
94
|
+
"**Important:** Use ask_user_question ONLY to clarify requirements or choose between approaches. Use exit_plan_mode to request plan approval. Do NOT ask about plan approval in any other way - no text questions, no ask_user_question. Phrases like \"Is this plan okay?\", \"Should I proceed?\", \"How does this plan look?\", \"Any changes before we start?\", or similar MUST use exit_plan_mode.",
|
|
27
95
|
"",
|
|
28
|
-
"
|
|
96
|
+
"NOTE: At any point in time through this workflow you should feel free to ask the user questions or clarifications using the ask_user_question tool. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.",
|
|
29
97
|
].join("\n");
|
|
30
98
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A plan-mode run as the branch records it (pure, no pi imports): its plan
|
|
3
|
+
* file's path, whether that file existed when the run began, and the mode the
|
|
4
|
+
* run came from. plan-mode/index.ts reads and writes the record.
|
|
5
|
+
*/
|
|
6
|
+
import type { PermissionMode } from "../permissions/matcher.ts";
|
|
7
|
+
import { normalizePermissionMode } from "../permissions/settings.ts";
|
|
8
|
+
|
|
9
|
+
/** The `one-code:state` key of the current plan-mode run. */
|
|
10
|
+
export const PLAN_ENTRY_STATE = "plan-mode:entry";
|
|
11
|
+
|
|
12
|
+
/** A plan-mode run as the branch records it, beside its plan file's path. */
|
|
13
|
+
export type PlanRun = { existed: boolean; modeBeforePlan?: PermissionMode };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The run `stored` records for the plan file at `path`, or undefined when it
|
|
17
|
+
* records another file or is malformed. A mode it came from that is unknown,
|
|
18
|
+
* or plan itself, is dropped: the exit then offers its fixed list.
|
|
19
|
+
*/
|
|
20
|
+
export function parsePlanRun(stored: unknown, path: string): PlanRun | undefined {
|
|
21
|
+
const record = stored as { path?: unknown; existed?: unknown; modeBeforePlan?: unknown } | null | undefined;
|
|
22
|
+
if (record?.path !== path || typeof record.existed !== "boolean") return undefined;
|
|
23
|
+
const mode = normalizePermissionMode(record.modeBeforePlan);
|
|
24
|
+
const before = mode !== "plan" ? mode : undefined;
|
|
25
|
+
return { existed: record.existed, ...(before ? { modeBeforePlan: before } : {}) };
|
|
26
|
+
}
|