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
|
@@ -8,33 +8,46 @@
|
|
|
8
8
|
* and the result arrives later as a follow-up message. Scripts and per-call
|
|
9
9
|
* journals persist under the session dir, so `resumeFromRunId` replays the
|
|
10
10
|
* unchanged prefix of an edited or interrupted run. Saved workflows live in
|
|
11
|
-
* `.claude/workflows/` (project) and `~/.claude/workflows/` (user)
|
|
11
|
+
* `.claude/workflows/` (project) and `~/.claude/workflows/` (user), or their
|
|
12
|
+
* `.onecode` twins in independent mode (lib/config-mode.ts).
|
|
13
|
+
*
|
|
14
|
+
* The description is Claude Code's (description.ts); the authoring reference
|
|
15
|
+
* it points to is the bundled `workflow-authoring` skill. `enableWorkflows`,
|
|
16
|
+
* `disableWorkflows` and `workflowSizeGuideline` come from One Code's settings.
|
|
12
17
|
*
|
|
13
18
|
* Orchestration is opt-in, like Claude Code: the tool description gates it,
|
|
14
19
|
* and the literal keyword "ultracode" in a user message arms the turn via a
|
|
15
20
|
* system reminder (skipped while `/effort ultracode` has the standing block
|
|
16
|
-
* on — see `effort/`). The keyword is read by pi's `input` event
|
|
17
|
-
*
|
|
18
|
-
*
|
|
21
|
+
* on — see `effort/`). The keyword is read by pi's `input` event; on pi 0.86+
|
|
22
|
+
* that includes a message queued mid-turn, whose reminder is held until pi
|
|
23
|
+
* delivers the message. On older pi a queued message does not arm.
|
|
19
24
|
*/
|
|
20
25
|
|
|
26
|
+
import { prepareWorkflowArguments } from "./args.ts";
|
|
27
|
+
import { CHILD_WROTE_CHANNEL, type ChildWrote } from "../lib/child-writes.ts";
|
|
21
28
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22
29
|
import os from "node:os";
|
|
23
30
|
import { join } from "node:path";
|
|
24
|
-
import
|
|
31
|
+
import { contentText } from "@earendil-works/pi-ai";
|
|
32
|
+
import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
25
33
|
import { Type } from "typebox";
|
|
26
34
|
import { recordUsage } from "../lib/usage-bus.ts";
|
|
35
|
+
import { readWorkflowSettings } from "../lib/one-code-settings.ts";
|
|
36
|
+
import { registerVariantTool } from "../lib/tool-variants.ts";
|
|
37
|
+
import { workflowDescription } from "./description.ts";
|
|
27
38
|
import { MODEL_UNUSABLE_CHANNEL, type ModelUnusableEvent } from "../lib/model-unusable.ts";
|
|
28
39
|
import { whenAborted } from "../lib/abort.ts";
|
|
29
40
|
import { createTaskNotifier, oneShotNote, sessionOutlivesTurn, taskNotification, taskStatusOf, workflowSummary } from "../lib/notifications.ts";
|
|
41
|
+
import { QueuedDelivery } from "../lib/queued-delivery.ts";
|
|
30
42
|
import { REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
31
43
|
import { ULTRACODE_MODE_CHANNEL } from "../effort/slider.ts";
|
|
32
44
|
import { PERMISSION_STATUS_CHANNEL } from "../permissions/modes.ts";
|
|
33
45
|
import { watchPermissionBridge } from "../permissions/subagent-gate.ts";
|
|
34
46
|
import { watchHookBridge } from "../hooks/subagent-bridge.ts";
|
|
47
|
+
import { watchMcpTools } from "../lib/mcp-share.ts";
|
|
35
48
|
import { applicableSubagentDefault, loadSubagentDefault } from "../subagents/default-model.ts";
|
|
36
49
|
import { discoverSavedWorkflows, findSavedWorkflow, workflowDirs } from "./saved-workflows.ts";
|
|
37
|
-
import { buildRunReport, WorkflowRunManager } from "./run-manager.ts";
|
|
50
|
+
import { buildRunReport, type RunHandle, WorkflowRunManager } from "./run-manager.ts";
|
|
38
51
|
import {
|
|
39
52
|
ccToolRenderers,
|
|
40
53
|
customMessageText,
|
|
@@ -51,10 +64,19 @@ import {
|
|
|
51
64
|
initialViewerState,
|
|
52
65
|
planSave,
|
|
53
66
|
renderViewer,
|
|
67
|
+
viewerHeight,
|
|
54
68
|
type ViewerRunSnapshot,
|
|
55
69
|
} from "./viewer.ts";
|
|
56
70
|
import { WorkflowWidget } from "./widget.ts";
|
|
57
71
|
import { registerLocalCommand } from "../lib/local-command.ts";
|
|
72
|
+
import { canShowCustomUi } from "../lib/headless-output.ts";
|
|
73
|
+
import { sessionWorkCwd } from "../lib/worktree-channel.ts";
|
|
74
|
+
import { followEnteredWorktree } from "../lib/worktree-isolation.ts";
|
|
75
|
+
import { isKeyRelease, keyId } from "../lib/key-input.ts";
|
|
76
|
+
import { projectConfigDirName } from "../lib/config-mode.ts";
|
|
77
|
+
import { panelUi } from "../lib/panel-focus.ts";
|
|
78
|
+
import { dockRowsReporter, trackDockRows } from "../lib/dock-rows.ts";
|
|
79
|
+
import { queryDeferred } from "../lib/deferred.ts";
|
|
58
80
|
|
|
59
81
|
/**
|
|
60
82
|
* Claude Code's own arming reminder, verbatim in intent: the keyword is a
|
|
@@ -64,132 +86,35 @@ import { registerLocalCommand } from "../lib/local-command.ts";
|
|
|
64
86
|
const ULTRACODE_REMINDER =
|
|
65
87
|
'The user included the keyword "ultracode", opting this turn into multi-agent orchestration — use the workflow tool to fulfill the request.';
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* (runId + follow-up message instead of a task id + task-notification,
|
|
70
|
-
* `/workflows stop` instead of TaskStop, `Agent` as the single-agent
|
|
71
|
-
* alternative). The authoring guidance is the substantive part: it is what
|
|
72
|
-
* makes the model reach for pipeline() over barriers and pick sane fan-out
|
|
73
|
-
* sizes, so it is kept rather than trimmed.
|
|
74
|
-
*/
|
|
75
|
-
const WORKFLOW_TOOL_DESCRIPTION = `Execute a workflow script that orchestrates multiple subagents deterministically. Workflows run in the background — this tool returns immediately with a runId, and a follow-up message arrives when the workflow completes. Use /workflows to watch live progress.
|
|
76
|
-
|
|
77
|
-
A workflow structures work across many agents — to be comprehensive (decompose and cover in parallel), to be confident (independent perspectives and adversarial checks before committing), or to take on scale one context can't hold (migrations, audits, broad sweeps). The script is where you encode that structure: what fans out, what verifies, what synthesizes.
|
|
78
|
-
|
|
79
|
-
ONLY call this tool when the user has explicitly opted into multi-agent orchestration. Workflows can spawn dozens of agents and consume a large amount of tokens; the user must request that scale, not have it inferred. Explicit opt-in means one of:
|
|
80
|
-
- The user included the keyword "ultracode" in their prompt (you'll see a system-reminder confirming it).
|
|
81
|
-
- Ultracode is on for the session (a system-reminder confirms it), which makes the opt-in standing for every turn until it is turned off.
|
|
82
|
-
- The user directly asked you to run a workflow or use multi-agent orchestration in their own words ("use a workflow", "run a workflow", "fan out agents", "orchestrate this with subagents"). The ask must be in the user's words — a task that would merely benefit from a workflow does not count.
|
|
83
|
-
- The user invoked a skill or slash command whose instructions tell you to call Workflow.
|
|
84
|
-
- The user asked you to run a specific named or saved workflow.
|
|
85
|
-
|
|
86
|
-
For any other task — even one that would clearly benefit from parallelism — do NOT call this tool. Use the \`Agent\` tool for individual subagents, or briefly describe what a multi-agent workflow could do and how much it would roughly cost, and ask the user whether to run it.
|
|
87
|
-
|
|
88
|
-
When you do call it, the right move is often **hybrid**: scout inline first (list the files, find the targets, scope the diff) to discover the work-list, then call workflow to pipeline over it. You don't need to know the shape before the *task* — only before the *orchestration step*.
|
|
89
|
-
|
|
90
|
-
Common single-phase workflows you can chain across turns:
|
|
91
|
-
- **Understand** — parallel readers over relevant subsystems → structured map
|
|
92
|
-
- **Design** — judge panel of N independent approaches → scored synthesis
|
|
93
|
-
- **Review** — dimensions → find → adversarially verify (example below)
|
|
94
|
-
- **Research** — multi-modal sweep → deep-read → synthesize
|
|
95
|
-
- **Migrate** — discover sites → transform each (worktree isolation) → verify
|
|
96
|
-
|
|
97
|
-
Pass the script inline via \`script\`. Every invocation persists its script under the run directory and returns the path in the tool result. To iterate on a workflow, edit that file and re-invoke with \`{scriptPath, resumeFromRunId}\` instead of resending the full script.
|
|
98
|
-
|
|
99
|
-
Every script must begin with \`export const meta = {...}\`:
|
|
100
|
-
export const meta = {
|
|
101
|
-
name: 'find-flaky-tests',
|
|
102
|
-
description: 'Find flaky tests and propose fixes', // one-line summary
|
|
103
|
-
phases: [ // one entry per phase() call
|
|
104
|
-
{ title: 'Scan', detail: 'grep test logs for retries' },
|
|
105
|
-
{ title: 'Fix', detail: 'one agent per flaky test' },
|
|
106
|
-
],
|
|
107
|
-
}
|
|
108
|
-
// script body starts here — use agent()/parallel()/pipeline()/phase()/log()
|
|
109
|
-
phase('Scan')
|
|
110
|
-
const flaky = await agent('grep CI logs for retry markers', {schema: FLAKY_SCHEMA})
|
|
111
|
-
...
|
|
112
|
-
|
|
113
|
-
The \`meta\` object must be a PURE LITERAL — no variables, function calls, spreads, or template interpolation. Required fields: \`name\`, \`description\`. Optional: \`whenToUse\`, \`phases\`. Use the SAME phase titles in meta.phases as in phase() calls.
|
|
114
|
-
|
|
115
|
-
Script body hooks:
|
|
116
|
-
- agent(prompt: string, opts?: {label?, phase?, schema?, model?, allowExpensive?, effort?, isolation?: 'worktree', agentType?}): Promise<any> — spawn a subagent. Without schema, returns its final text as a string. With schema (a JSON Schema object), the subagent is forced to call a structured_output tool and agent() returns the validated object — no parsing needed. Returns null if the subagent fails (filter with .filter(Boolean)). opts.label overrides the display label. opts.phase explicitly assigns this agent to a progress group (use this inside pipeline()/parallel() stages to avoid races on the global phase() state). opts.model overrides the model — default to omitting it, the agent uses the effective /subagent default (configured, automatic smaller profile, then session fallback); a model costing more than the session model is rejected unless opts.allowExpensive is true (set it only when the user explicitly asked for that model). opts.effort overrides reasoning effort ('off' | 'minimal' | 'low' | 'medium' | 'high'). opts.isolation: 'worktree' runs the agent in a fresh git worktree — use ONLY when agents mutate files in parallel and would otherwise conflict. opts.agentType uses a named agent from .claude/agents/.
|
|
117
|
-
- parallel(thunks: Array<() => Promise<any>>): Promise<any[]> — run tasks concurrently. This is a BARRIER: awaits all thunks before returning. A thunk that throws resolves to \`null\` — the call itself never rejects, so \`.filter(Boolean)\` before using the results. Use ONLY when you genuinely need all results together.
|
|
118
|
-
- pipeline(items, stage1, stage2, ...): Promise<any[]> — run each item through all stages independently, NO barrier between stages. Item A can be in stage 3 while item B is still in stage 1. This is the DEFAULT for multi-stage work. Wall-clock = slowest single-item chain, not sum-of-slowest-per-stage. Every stage callback receives (prevResult, originalItem, index). A stage that throws drops that item to \`null\` and skips its remaining stages.
|
|
119
|
-
- log(message: string): void — emit a progress message to the user.
|
|
120
|
-
- phase(title: string): void — start a new phase; subsequent agent() calls are grouped under it.
|
|
121
|
-
- args: any — the value passed as this tool's \`args\` input, verbatim. Pass arrays/objects as actual JSON values, NOT as a JSON-encoded string.
|
|
122
|
-
- budget: {total: number|null, spent(): number, remaining(): number} — the run's output-token target from \`tokenBudget\`, shared with nested workflows. \`total\` is null if none was set. Once spent() reaches total, new and queued agent() calls throw before starting. Already-running agents may finish above the target; this is not a hard cap on provider usage. Use for dynamic loops: \`while (budget.total && budget.remaining() > 50_000) { ... }\`.
|
|
123
|
-
- workflow(nameOrRef: string | {scriptPath: string}, args?: any): Promise<any> — run another workflow inline as a sub-step. Nesting is one level only.
|
|
124
|
-
|
|
125
|
-
DEFAULT TO pipeline(). Only reach for a barrier (parallel between stages) when you genuinely need ALL prior-stage results together — dedup/merge across the full result set, early-exit on a zero count, or a prompt that references "the other findings". A barrier is NOT justified by "I need to flatten/map/filter first" (do it inside a pipeline stage) or "the stages are conceptually separate".
|
|
126
|
-
|
|
127
|
-
Smell test: if you wrote
|
|
128
|
-
const a = await parallel(...)
|
|
129
|
-
const b = transform(a) // flatten, map, filter — no cross-item dependency
|
|
130
|
-
const c = await parallel(b.map(...))
|
|
131
|
-
that middle transform doesn't need the barrier. Rewrite as a pipeline with the transform inside a stage. When in doubt: pipeline.
|
|
132
|
-
|
|
133
|
-
Concurrent agent() calls are capped at min(16, cpu cores - 2) per workflow — excess calls queue and run as slots free up. Total agent count across a run is capped at 1000. A single parallel()/pipeline() call accepts at most 4096 items.
|
|
134
|
-
|
|
135
|
-
The canonical multi-stage pattern — pipeline by default, each dimension verifies as soon as its review completes:
|
|
136
|
-
export const meta = {
|
|
137
|
-
name: 'review-changes',
|
|
138
|
-
description: 'Review changed files across dimensions, verify each finding',
|
|
139
|
-
phases: [{ title: 'Review' }, { title: 'Verify' }],
|
|
140
|
-
}
|
|
141
|
-
const DIMENSIONS = [{key: 'bugs', prompt: '...'}, {key: 'perf', prompt: '...'}]
|
|
142
|
-
const results = await pipeline(
|
|
143
|
-
DIMENSIONS,
|
|
144
|
-
d => agent(d.prompt, {label: \`review:\${d.key}\`, phase: 'Review', schema: FINDINGS_SCHEMA}),
|
|
145
|
-
review => parallel(review.findings.map(f => () =>
|
|
146
|
-
agent(\`Adversarially verify: \${f.title}\`, {label: \`verify:\${f.file}\`, phase: 'Verify', schema: VERDICT_SCHEMA})
|
|
147
|
-
.then(v => ({...f, verdict: v}))
|
|
148
|
-
))
|
|
149
|
-
)
|
|
150
|
-
return { confirmed: results.flat().filter(Boolean).filter(f => f.verdict?.isReal) }
|
|
151
|
-
|
|
152
|
-
Quality patterns — common shapes; pick by task and compose freely:
|
|
153
|
-
- Adversarial verify: spawn N independent skeptics per finding, each prompted to REFUTE. Kill if ≥majority refute. Prevents plausible-but-wrong findings from surviving.
|
|
154
|
-
- Perspective-diverse verify: when a finding can fail in more than one way, give each verifier a distinct lens (correctness, security, perf, does-it-reproduce) instead of N identical refuters.
|
|
155
|
-
- Judge panel: generate N independent attempts from different angles, score with parallel judges, synthesize from the winner while grafting the best ideas from runners-up.
|
|
156
|
-
- Loop-until-dry: for unknown-size discovery, keep spawning finders until K consecutive rounds return nothing new. Dedup against everything seen, not just what was confirmed, or it never converges.
|
|
157
|
-
- Multi-modal sweep: parallel agents each searching a different way (by-container, by-content, by-entity, by-time).
|
|
158
|
-
- Completeness critic: a final agent that asks "what's missing — modality not run, claim unverified, source unread?"
|
|
159
|
-
- No silent caps: if a workflow bounds coverage (top-N, no-retry, sampling), log() what was dropped.
|
|
160
|
-
|
|
161
|
-
Scale to what the user asked for. "find any bugs" → a few finders, single-vote verify. "thoroughly audit this" → larger finder pool, 3–5 vote adversarial pass, synthesis stage. Keep workflows under 15 agents unless the user's prompt calls for a different scale.
|
|
162
|
-
|
|
163
|
-
Scripts are plain JavaScript, NOT TypeScript — type annotations, interfaces, and generics fail to parse. The script body runs in an async context — use await directly, and a bare top-level \`return\` sets the workflow's result. Standard JS built-ins are available — EXCEPT \`Date.now()\`/\`Math.random()\`/argless \`new Date()\`, which throw (they would break resume); pass timestamps in via \`args\`, and for randomness vary the agent prompt/label by index. No filesystem or shell access from the script itself — only via spawned agents.
|
|
164
|
-
|
|
165
|
-
Resume: to continue after a stop, kill, or script edit, relaunch with \`{scriptPath, resumeFromRunId}\` — the longest unchanged prefix of agent() calls returns cached results instantly; the first edited/new call and everything after it runs live. Same script + same args → 100% cache hit. Stop a live run first (\`/workflows stop <runId>\`) before resuming it. Background runs do not survive a session switch or /reload — resume them instead.
|
|
166
|
-
|
|
167
|
-
Run a saved workflow from \`.claude/workflows/\` (project) or \`~/.claude/workflows/\` (personal) by passing its \`name\`.`;
|
|
168
|
-
|
|
89
|
+
// Claude Code's parameter texts, with One Code's tool names; `name` also
|
|
90
|
+
// names the personal directory, and a run is stopped with /workflows stop.
|
|
169
91
|
const WorkflowParams = Type.Object({
|
|
170
92
|
script: Type.Optional(
|
|
171
93
|
Type.String({
|
|
172
94
|
description:
|
|
173
|
-
"Self-contained workflow script. Must begin with `export const meta = { name, description, phases
|
|
95
|
+
"Self-contained workflow script. Must begin with `export const meta = { name, description, phases }` (pure literal, no computed values) followed by the script body using agent()/parallel()/pipeline()/phase().",
|
|
96
|
+
}),
|
|
97
|
+
),
|
|
98
|
+
name: Type.Optional(
|
|
99
|
+
Type.String({ description: `Name of a saved workflow from ${projectConfigDirName()}/workflows/ or ~/${projectConfigDirName()}/workflows/. Resolves to a self-contained script.` }),
|
|
100
|
+
),
|
|
101
|
+
args: Type.Optional(
|
|
102
|
+
Type.Any({
|
|
103
|
+
description:
|
|
104
|
+
"Optional input value exposed to the script as the global `args`, verbatim. Pass arrays/objects as actual JSON values, NOT as a JSON-encoded string — a stringified list breaks `args.filter`/`args.map` in the script. Use for parameterized named workflows (e.g. a research question).",
|
|
174
105
|
}),
|
|
175
106
|
),
|
|
176
107
|
scriptPath: Type.Optional(
|
|
177
108
|
Type.String({
|
|
178
109
|
description:
|
|
179
|
-
"Path to a workflow script file on disk. Every
|
|
110
|
+
"Path to a workflow script file on disk. Every workflow invocation persists its script under the session directory and returns the path in the tool result. To iterate, edit that file with write/edit and re-invoke workflow with the same `scriptPath` instead of re-sending the full script. Takes precedence over `script` and `name`.",
|
|
180
111
|
}),
|
|
181
112
|
),
|
|
182
|
-
name: Type.Optional(
|
|
183
|
-
Type.String({ description: "Name of a saved workflow from .claude/workflows/ or ~/.claude/workflows/" }),
|
|
184
|
-
),
|
|
185
|
-
args: Type.Optional(
|
|
186
|
-
Type.Any({ description: "Value exposed to the script as the global `args`, verbatim. Pass real JSON values, not stringified JSON" }),
|
|
187
|
-
),
|
|
188
113
|
resumeFromRunId: Type.Optional(
|
|
189
114
|
Type.String({
|
|
190
115
|
pattern: "^wf_[a-z0-9-]{6,}$",
|
|
191
116
|
description:
|
|
192
|
-
"Run ID of a prior workflow invocation to resume from. Completed agent() calls with unchanged (prompt, opts) return their cached results instantly; only edited or new calls re-run. Stop the prior run first (/workflows stop) before resuming",
|
|
117
|
+
"Run ID of a prior workflow invocation to resume from. Completed agent() calls with unchanged (prompt, opts) return their cached results instantly; only edited or new calls re-run. Same-session only. Stop the prior run first (/workflows stop) before resuming.",
|
|
193
118
|
}),
|
|
194
119
|
),
|
|
195
120
|
tokenBudget: Type.Optional(
|
|
@@ -211,37 +136,40 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
211
136
|
unusableModels.add((data as ModelUnusableEvent).model);
|
|
212
137
|
});
|
|
213
138
|
const notifyTask = createTaskNotifier(pi);
|
|
139
|
+
// After `enter_worktree`, workflow agents run in the worktree (and are guarded there).
|
|
140
|
+
const enteredWorktree = followEnteredWorktree(pi.events);
|
|
214
141
|
const manager = new WorkflowRunManager();
|
|
215
142
|
let lastCtx: ExtensionContext | undefined;
|
|
216
|
-
const widget = new WorkflowWidget(manager, () => lastCtx);
|
|
217
|
-
|
|
143
|
+
const widget = new WorkflowWidget(manager, () => lastCtx, dockRowsReporter(pi.events, "workflow"));
|
|
144
|
+
// The other strips below the editor (the subagent panel), so the viewer leaves their rows free.
|
|
145
|
+
const dock = trackDockRows(pi.events);
|
|
218
146
|
let viewerOpen = false;
|
|
219
147
|
|
|
220
148
|
// Parent permission bridge for workflow agents' gates — same bridge the
|
|
221
149
|
// subagent runner uses; see AgentRunnerOptions.getPermissionBridge.
|
|
222
150
|
const getPermissionBridge = watchPermissionBridge(pi);
|
|
223
151
|
const getHookBridge = watchHookBridge(pi);
|
|
152
|
+
// The parent's MCP tools, shared into workflow agents as they are into subagents.
|
|
153
|
+
const getMcpTools = watchMcpTools(pi.events);
|
|
224
154
|
|
|
225
155
|
const openViewer = async (ctx: ExtensionContext, opts?: { height?: "full" | "half"; runIndex?: number }) => {
|
|
226
156
|
if (viewerOpen) return;
|
|
227
157
|
viewerOpen = true;
|
|
228
158
|
try {
|
|
229
|
-
await openWorkflowViewer(ctx, manager, opts);
|
|
159
|
+
await openWorkflowViewer(pi.events, ctx, manager, () => widget.paintedRowCount() + dock.others("workflow"), opts);
|
|
230
160
|
} finally {
|
|
231
161
|
viewerOpen = false;
|
|
232
162
|
}
|
|
233
163
|
};
|
|
234
164
|
|
|
235
|
-
/**
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (!handle || deliveredRuns.has(runId)) return;
|
|
244
|
-
deliveredRuns.add(runId);
|
|
165
|
+
/**
|
|
166
|
+
* A background run's result notification, sent once when that run finishes.
|
|
167
|
+
* Keyed by the handle, not the run id: a `resumeFromRunId` run reuses its
|
|
168
|
+
* original id, and a set of delivered ids swallowed the resumed run's result
|
|
169
|
+
* (SUBAGENTS-WORKFLOWS-REVIEW-2026-09-26 H4). `toolUseId` is the call that
|
|
170
|
+
* started this run, the notification's `<tool-use-id>`.
|
|
171
|
+
*/
|
|
172
|
+
const deliverResult = (handle: RunHandle, toolUseId: string) => {
|
|
245
173
|
// CC's kind=workflow task notification; the run report (result or the
|
|
246
174
|
// failure and how to resume) is its `<result>`. Summary literal: see
|
|
247
175
|
// workflowSummary — unverified against CC.
|
|
@@ -264,7 +192,10 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
264
192
|
notificationComponent(theme, customMessageText(message.content), expanded),
|
|
265
193
|
);
|
|
266
194
|
|
|
267
|
-
|
|
195
|
+
// The description carries the session's size guideline (description.ts);
|
|
196
|
+
// it is read from One Code's settings at session start and the tool is
|
|
197
|
+
// registered again only when the line changes.
|
|
198
|
+
const workflowTool = defineTool({
|
|
268
199
|
name: "workflow",
|
|
269
200
|
label: "Workflow",
|
|
270
201
|
...ccToolRenderers<
|
|
@@ -279,19 +210,29 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
279
210
|
? `Running in background · /workflows to monitor and save · ${result.details.runId ?? ""}`.trimEnd()
|
|
280
211
|
: undefined,
|
|
281
212
|
}),
|
|
282
|
-
description:
|
|
213
|
+
description: workflowDescription("medium", false),
|
|
214
|
+
prepareArguments: prepareWorkflowArguments as never,
|
|
283
215
|
promptSnippet: "Run a script that orchestrates many subagents (opt-in ultracode mode)",
|
|
284
216
|
parameters: WorkflowParams,
|
|
285
217
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
286
218
|
lastCtx = ctx;
|
|
287
219
|
const sessionDir = ctx.sessionManager.getSessionDir();
|
|
288
220
|
|
|
289
|
-
let script: string | undefined
|
|
221
|
+
let script: string | undefined;
|
|
290
222
|
try {
|
|
291
|
-
|
|
223
|
+
// Settings that turn workflows off also refuse a call that reaches
|
|
224
|
+
// the tool anyway (a later setActiveTools can bring it back).
|
|
225
|
+
if (!workflowsEnabled) {
|
|
226
|
+
throw new WorkflowScriptError(
|
|
227
|
+
"Workflows are turned off: enableWorkflows is false or disableWorkflows is true in One Code's user or project settings. Turn them back on there and start a new session.",
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
// `scriptPath` takes precedence over `script` and `name`, as in Claude Code.
|
|
231
|
+
if (params.scriptPath) {
|
|
292
232
|
if (!existsSync(params.scriptPath)) throw new WorkflowScriptError(`scriptPath ${params.scriptPath} does not exist`);
|
|
293
233
|
script = readFileSync(params.scriptPath, "utf8");
|
|
294
234
|
}
|
|
235
|
+
script ??= params.script;
|
|
295
236
|
if (!script && params.name) {
|
|
296
237
|
const saved = findSavedWorkflow(ctx.cwd, os.homedir(), params.name);
|
|
297
238
|
if (!saved) {
|
|
@@ -319,13 +260,16 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
319
260
|
args: params.args,
|
|
320
261
|
tokenBudget: params.tokenBudget ?? null,
|
|
321
262
|
resumeFromRunId: params.resumeFromRunId,
|
|
322
|
-
|
|
263
|
+
// Where the session works: the entered worktree, if any (pi keeps ctx.cwd at the original checkout).
|
|
264
|
+
cwd: sessionWorkCwd(enteredWorktree(), ctx.cwd),
|
|
323
265
|
sessionDir,
|
|
324
266
|
defaultModel: ctx.model,
|
|
325
267
|
configuredDefault,
|
|
326
268
|
defaultEffort: ctx.thinkingLevel,
|
|
327
269
|
getPermissionBridge,
|
|
328
270
|
getHookBridge,
|
|
271
|
+
getMcpTools,
|
|
272
|
+
getParentDeferred: () => queryDeferred(pi.events),
|
|
329
273
|
// Workflow agents run in their own sessions; their spend reaches the footer only through the bus.
|
|
330
274
|
onUsage: (cost) => recordUsage(pi, "subagent", { cost: { total: cost } }),
|
|
331
275
|
unusableModels: () => unusableModels,
|
|
@@ -334,6 +278,7 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
334
278
|
unusableModels.add(model);
|
|
335
279
|
pi.events.emit(MODEL_UNUSABLE_CHANNEL, { model, reason } satisfies ModelUnusableEvent);
|
|
336
280
|
},
|
|
281
|
+
onChildWrite: (path, agent) => pi.events.emit(CHILD_WROTE_CHANNEL, { path, ...(agent ? { agent } : {}) } satisfies ChildWrote),
|
|
337
282
|
});
|
|
338
283
|
widget.attach(handle);
|
|
339
284
|
|
|
@@ -358,17 +303,16 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
358
303
|
unhookAbort();
|
|
359
304
|
handle.removeListener("progress", onProgress);
|
|
360
305
|
}
|
|
361
|
-
deliveredRuns.add(handle.runId); // sync result goes in the tool result, not a followUp
|
|
362
306
|
const oneShotPrefix = forcedSync ? `${oneShotNote("workflow")}\n\n` : "";
|
|
363
307
|
return {
|
|
364
|
-
|
|
308
|
+
// The script's path, as the background result gives it: a failed run is fixed by editing it and re-running with scriptPath.
|
|
309
|
+
content: [{ type: "text", text: `${oneShotPrefix}${buildRunReport(handle)}\nscript: ${handle.scriptPath}` }],
|
|
365
310
|
details: { runId: handle.runId, status: handle.status, scriptPath: handle.scriptPath },
|
|
366
311
|
isError: handle.status !== "completed",
|
|
367
312
|
};
|
|
368
313
|
}
|
|
369
314
|
|
|
370
|
-
|
|
371
|
-
void handle.finished.then(() => deliverResult(handle.runId));
|
|
315
|
+
void handle.finished.then(() => deliverResult(handle, toolCallId));
|
|
372
316
|
return {
|
|
373
317
|
content: [
|
|
374
318
|
{
|
|
@@ -392,9 +336,38 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
392
336
|
}
|
|
393
337
|
},
|
|
394
338
|
});
|
|
339
|
+
const setWorkflowDescription = registerVariantTool<string>(pi, workflowTool.description, (description) => ({ ...workflowTool, description }));
|
|
340
|
+
|
|
341
|
+
// `enableWorkflows`/`disableWorkflows` and `workflowSizeGuideline` from One
|
|
342
|
+
// Code's settings, read once per session so the description stays
|
|
343
|
+
// byte-stable within it. Turned off, the tool leaves the active set (and
|
|
344
|
+
// comes back if a later session turns it on) and the keyword arms nothing.
|
|
345
|
+
let workflowsEnabled = true;
|
|
346
|
+
let workflowWithheld = false;
|
|
347
|
+
/** Take the tool out of the active set while workflows are off; true when it was active. */
|
|
348
|
+
const withholdIfOff = (): boolean => {
|
|
349
|
+
const active = pi.getActiveTools();
|
|
350
|
+
if (workflowsEnabled || !active.includes("workflow")) return false;
|
|
351
|
+
pi.setActiveTools(active.filter((name) => name !== "workflow"));
|
|
352
|
+
return true;
|
|
353
|
+
};
|
|
354
|
+
const applyWorkflowSettings = (cwd: string) => {
|
|
355
|
+
const settings = readWorkflowSettings(cwd, os.homedir());
|
|
356
|
+
workflowsEnabled = settings.enabled;
|
|
357
|
+
setWorkflowDescription(workflowDescription(settings.sizeGuideline, settings.sizeConfigured));
|
|
358
|
+
if (withholdIfOff()) {
|
|
359
|
+
workflowWithheld = true;
|
|
360
|
+
} else if (settings.enabled && workflowWithheld) {
|
|
361
|
+
const active = pi.getActiveTools();
|
|
362
|
+
if (!active.includes("workflow")) pi.setActiveTools([...active, "workflow"]);
|
|
363
|
+
workflowWithheld = false;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
395
366
|
|
|
396
367
|
registerLocalCommand(pi, "workflows", {
|
|
368
|
+
reportsResult: true,
|
|
397
369
|
description: "Open the workflow viewer; list, stop, or inspect runs",
|
|
370
|
+
argumentHint: "[list|log <run-id>|stop <run-id>]",
|
|
398
371
|
getArgumentCompletions: (prefix) => {
|
|
399
372
|
const items = ["stop ", "log ", "list"].filter((c) => c.startsWith(prefix));
|
|
400
373
|
return items.length ? items.map((c) => ({ value: c, label: c.trim() })) : null;
|
|
@@ -402,23 +375,25 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
402
375
|
handler: async (args, ctx) => {
|
|
403
376
|
lastCtx = ctx;
|
|
404
377
|
const [action, runId] = args.trim().split(/\s+/);
|
|
405
|
-
if (!action && ctx
|
|
378
|
+
if (!action && canShowCustomUi(ctx)) {
|
|
406
379
|
await openViewer(ctx, { height: "full" });
|
|
407
|
-
return;
|
|
380
|
+
return "Dynamic workflows dialog dismissed";
|
|
408
381
|
}
|
|
409
382
|
if (action === "stop" && runId) {
|
|
410
383
|
const stopped = manager.abort(runId, "stopped via /workflows");
|
|
411
|
-
|
|
412
|
-
|
|
384
|
+
const text = stopped ? `Stopping ${runId}…` : `No running workflow ${runId}`;
|
|
385
|
+
ctx.ui.notify(text, stopped ? "info" : "error");
|
|
386
|
+
return text;
|
|
413
387
|
}
|
|
414
388
|
if (action === "log" && runId) {
|
|
415
389
|
const handle = manager.get(runId);
|
|
416
390
|
if (!handle) {
|
|
417
391
|
ctx.ui.notify(`No workflow run ${runId} in this session`, "error");
|
|
418
|
-
return
|
|
392
|
+
return `No workflow run ${runId} in this session`;
|
|
419
393
|
}
|
|
420
|
-
|
|
421
|
-
|
|
394
|
+
const log = `${handle.meta.name} (${handle.status})\n${handle.recentEvents.join("\n") || "(no events)"}`;
|
|
395
|
+
ctx.ui.notify(log, "info");
|
|
396
|
+
return log;
|
|
422
397
|
}
|
|
423
398
|
|
|
424
399
|
const lines: string[] = [];
|
|
@@ -437,27 +412,37 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
437
412
|
lines.push("Saved workflows:");
|
|
438
413
|
for (const w of saved) lines.push(` ${w.name} (${w.source}) — ${w.meta?.description ?? w.path}`);
|
|
439
414
|
}
|
|
440
|
-
if (!lines.length) lines.push(
|
|
415
|
+
if (!lines.length) lines.push(`No workflow runs yet and no saved workflows found (${projectConfigDirName()}/workflows/).`);
|
|
416
|
+
if (!action && ctx.mode === "rpc") lines.unshift("The interactive workflow viewer requires TUI mode; listing workflows instead.");
|
|
441
417
|
lines.push("Usage: /workflows [stop <runId> | log <runId>]");
|
|
442
418
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
419
|
+
return lines.join("\n");
|
|
443
420
|
},
|
|
444
421
|
});
|
|
445
422
|
|
|
446
423
|
// While ultracode MODE is on (/effort ultracode) the standing block already
|
|
447
424
|
// carries the opt-in, so the keyword's single-turn one-shot is skipped: one
|
|
448
|
-
// instruction for one fact. `input` fires for
|
|
449
|
-
//
|
|
450
|
-
//
|
|
425
|
+
// instruction for one fact. Since pi 0.86 `input` also fires for a message
|
|
426
|
+
// queued mid-turn; its one-shot waits until pi delivers that message
|
|
427
|
+
// (lib/queued-delivery.ts), or it would ride the running tool's result, before
|
|
428
|
+
// the message. On older pi a queued message never reaches `input` and the
|
|
429
|
+
// keyword arms nothing (the mode covers it).
|
|
451
430
|
let ultracodeMode = false;
|
|
452
431
|
pi.events.on(ULTRACODE_MODE_CHANNEL, (data) => {
|
|
453
432
|
ultracodeMode = (data as { active?: boolean })?.active === true;
|
|
454
433
|
});
|
|
434
|
+
const queuedKeyword = new QueuedDelivery<true>();
|
|
455
435
|
pi.on("input", (event) => {
|
|
456
|
-
if (!ultracodeMode
|
|
457
|
-
|
|
458
|
-
}
|
|
436
|
+
if (!workflowsEnabled || ultracodeMode || !/\bultracode\b/i.test(event.text)) return undefined;
|
|
437
|
+
if (event.streamingBehavior) queuedKeyword.hold(event.text, true);
|
|
438
|
+
else pi.events.emit(REMINDER_CHANNEL, { text: ULTRACODE_REMINDER });
|
|
459
439
|
return undefined;
|
|
460
440
|
});
|
|
441
|
+
pi.on("message_start", (event) => {
|
|
442
|
+
if (queuedKeyword.isEmpty || event.message.role !== "user") return;
|
|
443
|
+
if (queuedKeyword.release(contentText(event.message.content, ""))) pi.events.emit(REMINDER_CHANNEL, { text: ULTRACODE_REMINDER, placement: "last-append" });
|
|
444
|
+
});
|
|
445
|
+
pi.on("agent_settled", () => queuedKeyword.clear());
|
|
461
446
|
|
|
462
447
|
// Down-arrow soft focus for the below-editor status strip (Claude Code's
|
|
463
448
|
// bottom workflow entry): with the editor focused and empty, ↓ highlights
|
|
@@ -467,13 +452,15 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
467
452
|
// consume is guarded by "the core editor really has focus" (identity
|
|
468
453
|
// against the captured baseline) to never steal keys from dialogs.
|
|
469
454
|
let inputHookRegistered = false;
|
|
470
|
-
const DOWN_KEYS = new Set(["\x1b[B", "\x1bOB"]);
|
|
471
455
|
const registerInputHook = (registerCtx: ExtensionContext) => {
|
|
472
456
|
if (inputHookRegistered || !registerCtx.hasUI) return;
|
|
473
457
|
inputHookRegistered = true;
|
|
474
458
|
const leave = () => widget.setFocus(undefined);
|
|
475
459
|
try {
|
|
476
460
|
registerCtx.ui.onTerminalInput((data) => {
|
|
461
|
+
// pi-tui calls input listeners before it filters key releases, and a
|
|
462
|
+
// kitty terminal sends one after every press: never a key here.
|
|
463
|
+
if (isKeyRelease(data)) return undefined;
|
|
477
464
|
// Session switches replace the ExtensionContext; the hook registers
|
|
478
465
|
// once, so it must act through the freshest ctx, not its closure.
|
|
479
466
|
const ctx = lastCtx ?? registerCtx;
|
|
@@ -482,7 +469,7 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
482
469
|
return undefined;
|
|
483
470
|
}
|
|
484
471
|
if (widget.focusIndex === undefined) {
|
|
485
|
-
if (
|
|
472
|
+
if (keyId(data) !== "down" || !widget.editorFocusedAndIdle(ctx)) return undefined;
|
|
486
473
|
widget.setFocus(0);
|
|
487
474
|
return { consume: true };
|
|
488
475
|
}
|
|
@@ -530,8 +517,14 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
530
517
|
|
|
531
518
|
pi.on("session_start", (_event, ctx) => {
|
|
532
519
|
lastCtx = ctx;
|
|
520
|
+
queuedKeyword.clear();
|
|
521
|
+
applyWorkflowSettings(ctx.cwd);
|
|
533
522
|
registerInputHook(ctx);
|
|
534
523
|
});
|
|
524
|
+
// pi restores the selected branch's tool loadout at a /tree switch: turned off, the tool stays out.
|
|
525
|
+
pi.on("session_tree", () => {
|
|
526
|
+
withholdIfOff();
|
|
527
|
+
});
|
|
535
528
|
|
|
536
529
|
// The permission-mode badge is a below-editor widget too, re-set on every
|
|
537
530
|
// mode change; setWidget order is last-write order, so re-set the strip
|
|
@@ -558,11 +551,13 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
558
551
|
* lookup (findings §15).
|
|
559
552
|
*/
|
|
560
553
|
async function openWorkflowViewer(
|
|
554
|
+
events: ExtensionAPI["events"],
|
|
561
555
|
ctx: ExtensionContext,
|
|
562
556
|
manager: WorkflowRunManager,
|
|
557
|
+
stripRows: () => number,
|
|
563
558
|
opts?: { height?: "full" | "half"; runIndex?: number },
|
|
564
559
|
): Promise<void> {
|
|
565
|
-
await ctx.ui.custom<null>((tui, theme, _keybindings, done) => {
|
|
560
|
+
await panelUi(events, ctx.ui).custom<null>((tui, theme, _keybindings, done) => {
|
|
566
561
|
const paint = { fg: safeThemePaint(theme), bold: safeThemeBold(theme) };
|
|
567
562
|
|
|
568
563
|
// Newest run first, so the viewer opens on the latest.
|
|
@@ -570,11 +565,10 @@ async function openWorkflowViewer(
|
|
|
570
565
|
|
|
571
566
|
// Full (the /workflows tab) fills the terminal; half (opened from the
|
|
572
567
|
// status strip) leaves the transcript visible above, like Claude Code.
|
|
573
|
-
const
|
|
574
|
-
opts?.height === "half" ? Math.max(12, Math.min(Math.floor(rows / 2), 30)) : Math.max(12, rows - 2);
|
|
568
|
+
const heightMode = opts?.height === "half" ? "half" : "full";
|
|
575
569
|
|
|
576
570
|
const state = initialViewerState(opts?.runIndex ?? 0);
|
|
577
|
-
let cache: { width: number; lines: string[] } | undefined;
|
|
571
|
+
let cache: { width: number; height: number; lines: string[] } | undefined;
|
|
578
572
|
const repaint = () => {
|
|
579
573
|
cache = undefined;
|
|
580
574
|
tui.requestRender();
|
|
@@ -610,12 +604,13 @@ async function openWorkflowViewer(
|
|
|
610
604
|
|
|
611
605
|
return {
|
|
612
606
|
render: (width: number) => {
|
|
613
|
-
|
|
614
|
-
const height = viewerHeight(tui.terminal.rows);
|
|
607
|
+
// Keyed by height too: a resize or a strip that grows changes the room.
|
|
608
|
+
const height = viewerHeight(tui.terminal.rows, heightMode, stripRows());
|
|
609
|
+
if (cache?.width === width && cache.height === height) return cache.lines;
|
|
615
610
|
const lines = renderViewer({ runs: snapshots(), state, width, height, now: Date.now() }, paint).map(
|
|
616
611
|
(line) => truncateLine(line, width),
|
|
617
612
|
);
|
|
618
|
-
cache = { width, lines };
|
|
613
|
+
cache = { width, height, lines };
|
|
619
614
|
return lines;
|
|
620
615
|
},
|
|
621
616
|
handleInput: (data: string) => {
|
|
@@ -78,6 +78,7 @@ export class AgentRecordStore {
|
|
|
78
78
|
if (event.tokens) record.tokens = event.tokens;
|
|
79
79
|
if (event.cost !== undefined) record.cost = event.cost;
|
|
80
80
|
if (event.preview !== undefined) record.outcome = event.preview;
|
|
81
|
+
if (event.worktree) record.worktree = event.worktree;
|
|
81
82
|
if (event.replayed) {
|
|
82
83
|
record.status = "replayed";
|
|
83
84
|
} else if (event.text) {
|