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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One request-surface tier per session/model boundary. The first consumer owns
|
|
3
|
+
* the lifecycle handlers; every consumer reads that owner's value over the
|
|
4
|
+
* event bus. No module state is shared (each extension has its own jiti copy),
|
|
5
|
+
* and an extension loaded in isolation still installs its own owner.
|
|
6
|
+
*
|
|
7
|
+
* Only session_start and model_select classify. A catalog refresh can affect
|
|
8
|
+
* automatic model selection immediately, but prompt text, tool descriptions,
|
|
9
|
+
* and tier-dependent steering keep the same tier until the next boundary.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { type PromptTier, resolveModelTier } from "./model-tier.ts";
|
|
14
|
+
import { invalidateCatalogCache } from "./model-catalog-data.ts";
|
|
15
|
+
|
|
16
|
+
const SESSION_TIER_REQUEST_CHANNEL = "one-code:session-tier-request";
|
|
17
|
+
|
|
18
|
+
interface TierRequest {
|
|
19
|
+
owned?: boolean;
|
|
20
|
+
tier?: PromptTier;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Install the owner once on this bus, and return a reader of its frozen tier. */
|
|
24
|
+
export function sessionModelTier(pi: Pick<ExtensionAPI, "events" | "on">): () => PromptTier {
|
|
25
|
+
const request = (): TierRequest => {
|
|
26
|
+
const query: TierRequest = {};
|
|
27
|
+
pi.events.emit(SESSION_TIER_REQUEST_CHANNEL, query);
|
|
28
|
+
return query;
|
|
29
|
+
};
|
|
30
|
+
if (!request().owned) {
|
|
31
|
+
let tier: PromptTier | undefined;
|
|
32
|
+
pi.events.on(SESSION_TIER_REQUEST_CHANNEL, (data) => {
|
|
33
|
+
Object.assign(data as TierRequest, { owned: true, tier });
|
|
34
|
+
});
|
|
35
|
+
const resolve = (model: Parameters<typeof resolveModelTier>[0]) => {
|
|
36
|
+
// Doctor's jiti copy cannot clear our memo. Do not freeze a stale
|
|
37
|
+
// catalog if the boundary lands inside the disk cache's stat throttle.
|
|
38
|
+
invalidateCatalogCache();
|
|
39
|
+
tier = resolveModelTier(model);
|
|
40
|
+
};
|
|
41
|
+
// Registered before the first consumer's hooks. Later consumers preserve
|
|
42
|
+
// their own lifecycle ordering (for example, restored bash mode variants).
|
|
43
|
+
pi.on("session_start", (_event, ctx) => resolve(ctx.model));
|
|
44
|
+
pi.on("model_select", (event) => resolve(event.model));
|
|
45
|
+
}
|
|
46
|
+
return () => {
|
|
47
|
+
const tier = request().tier;
|
|
48
|
+
if (tier === undefined) throw new Error("Request tier is unavailable: emit session_start before building the request surface.");
|
|
49
|
+
return tier;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What will wake the session later, for Claude Code's Stop hook payload: its
|
|
3
|
+
* `session_crons` (cron_create jobs, /loop, schedule_wakeup) and
|
|
4
|
+
* `background_tasks` (in-flight background work) fields, findings §21.
|
|
5
|
+
*
|
|
6
|
+
* The hooks extension emits a `SessionWorkQuery` on SESSION_WORK_CHANNEL and
|
|
7
|
+
* reads it back once the synchronous emit returns; the background extension,
|
|
8
|
+
* which owns both lists, fills it (jiti isolation: no shared module state).
|
|
9
|
+
*
|
|
10
|
+
* Pure: no pi imports.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const SESSION_WORK_CHANNEL = "one-code:session-work";
|
|
14
|
+
|
|
15
|
+
/** Claude Code's `session_crons` item. */
|
|
16
|
+
export interface SessionCron {
|
|
17
|
+
id: string;
|
|
18
|
+
/** The cron expression. */
|
|
19
|
+
schedule: string;
|
|
20
|
+
/** False for one-shots, a wakeup's single fire time included. */
|
|
21
|
+
recurring: boolean;
|
|
22
|
+
prompt: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Claude Code's `background_tasks` item. */
|
|
26
|
+
export interface SessionBackgroundTask {
|
|
27
|
+
id: string;
|
|
28
|
+
/** Friendly type label: shell, subagent, monitor, workflow; else the raw kind. */
|
|
29
|
+
type: string;
|
|
30
|
+
status: string;
|
|
31
|
+
description: string;
|
|
32
|
+
/** Only for shell tasks. */
|
|
33
|
+
command?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SessionWorkQuery {
|
|
37
|
+
crons: SessionCron[];
|
|
38
|
+
tasks: SessionBackgroundTask[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The hook payload's field cap (Claude Code: 1000 characters, then an in-string marker). */
|
|
42
|
+
const FIELD_CAP = 1000;
|
|
43
|
+
|
|
44
|
+
/** Claude Code's clip for these fields: the first 1000 characters, then `… [+N chars]`. */
|
|
45
|
+
export function capField(text: string): string {
|
|
46
|
+
return text.length <= FIELD_CAP ? text : `${text.slice(0, FIELD_CAP)}… [+${text.length - FIELD_CAP} chars]`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const TYPE_LABELS: Record<string, string> = { bash: "shell", shell: "shell", agent: "subagent", subagent: "subagent", monitor: "monitor", workflow: "workflow" };
|
|
50
|
+
|
|
51
|
+
export function sessionCron(job: { id: string; cron: string; recurring: boolean; prompt: string }): SessionCron {
|
|
52
|
+
return { id: job.id, schedule: job.cron, recurring: job.recurring, prompt: capField(job.prompt) };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function sessionBackgroundTask(task: { id: string; kind: string; status: string; description: string; command?: string }): SessionBackgroundTask {
|
|
56
|
+
const type = TYPE_LABELS[task.kind] ?? task.kind;
|
|
57
|
+
return {
|
|
58
|
+
id: task.id,
|
|
59
|
+
type,
|
|
60
|
+
status: task.status,
|
|
61
|
+
description: capField(task.description),
|
|
62
|
+
...(type === "shell" && task.command !== undefined && { command: capField(task.command) }),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-extension "a setting you own just changed on disk" channels (pure
|
|
3
3
|
* constants). jiti isolates module state, so when one extension writes another
|
|
4
|
-
* extension's setting — `/doctor preset` writing the subagent default and
|
|
5
|
-
* auto-mode classifier model — it announces the write here and the
|
|
6
|
-
* re-reads its file and republishes its live status. No payload: the
|
|
7
|
-
* own loader is the source of truth.
|
|
4
|
+
* extension's setting — `/doctor preset` writing the subagent default and
|
|
5
|
+
* clearing the auto-mode classifier model — it announces the write here and the
|
|
6
|
+
* owner re-reads its file and republishes its live status. No payload: the
|
|
7
|
+
* owner's own loader is the source of truth.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/** `subagentModel` in ~/.onecode/settings.json changed; the subagents extension re-resolves and re-emits its status. */
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code's semantic exit codes (pure): a nonzero exit that is not an
|
|
3
|
+
* error, such as grep's "no matches" or diff's "files differ", which Claude
|
|
4
|
+
* Code reports as plain output with no `Exit code N` line. Bash and
|
|
5
|
+
* PowerShell keep separate tables and separate last-command rules.
|
|
6
|
+
*/
|
|
7
|
+
import { parseBash } from "./bash-parser.ts";
|
|
8
|
+
|
|
9
|
+
type BashNode = NonNullable<ReturnType<typeof parseBash>>["rootNode"];
|
|
10
|
+
const CONTAINERS = new Set(["program", "list", "pipeline"]);
|
|
11
|
+
const OPERATORS = new Set(["&&", "||", "|", ";", "&", "|&", "\n"]);
|
|
12
|
+
const EXIT_ONE_SUCCESSES = new Set(["grep", "rg", "egrep", "fgrep", "find", "diff", "test", "["]);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The rightmost command's text, or undefined when an `&&` precedes it: a failed
|
|
16
|
+
* earlier step can explain the exit, not the final search. Redirects and
|
|
17
|
+
* comments are skipped, as Claude Code's last-command walk does.
|
|
18
|
+
*/
|
|
19
|
+
function lastCommand(root: BashNode): string | undefined {
|
|
20
|
+
let node: BashNode | undefined = root;
|
|
21
|
+
while (node && (CONTAINERS.has(node.type) || node.type === "redirected_statement")) {
|
|
22
|
+
const children: BashNode[] = node.children;
|
|
23
|
+
const last = children.findLastIndex((child) => !OPERATORS.has(child.type) && child.type !== "comment" && !child.type.endsWith("_redirect"));
|
|
24
|
+
if (node.type === "list" && children[last - 1]?.type === "&&") return undefined;
|
|
25
|
+
node = children[last];
|
|
26
|
+
}
|
|
27
|
+
return node?.text;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** git's global options that take their value as the next word (`--git-dir=x` is one word). */
|
|
31
|
+
const GIT_VALUE_OPTIONS = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--super-prefix", "--config-env", "--attr-source"]);
|
|
32
|
+
|
|
33
|
+
/** git's subcommand among the words after `git`: the first that is no option or an option's value. */
|
|
34
|
+
function gitSubcommandOf(args: readonly string[]): string | undefined {
|
|
35
|
+
for (let i = 0; i < args.length; i++) {
|
|
36
|
+
if (!args[i].startsWith("-")) return args[i];
|
|
37
|
+
if (GIT_VALUE_OPTIONS.has(args[i])) i++;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Bash: exit 1 from a final grep, rg, egrep, fgrep, find, diff, test, `[` or git grep/diff. */
|
|
43
|
+
export function bashExitOneIsSuccess(command: string): boolean {
|
|
44
|
+
if (command.length > 10_000) return false;
|
|
45
|
+
const tree = parseBash(command);
|
|
46
|
+
if (!tree) return false;
|
|
47
|
+
try {
|
|
48
|
+
if (tree.rootNode.type !== "program") return false;
|
|
49
|
+
const last = lastCommand(tree.rootNode);
|
|
50
|
+
if (last === undefined) return false;
|
|
51
|
+
// Claude Code matches the literal first word: no unwrapping of env, assignments or quotes.
|
|
52
|
+
const [head, ...args] = last.trim().split(/\s+/);
|
|
53
|
+
if (head !== "git") return EXIT_ONE_SUCCESSES.has(head);
|
|
54
|
+
const sub = gitSubcommandOf(args);
|
|
55
|
+
return sub === "grep" || sub === "diff";
|
|
56
|
+
} finally {
|
|
57
|
+
tree.delete();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// PowerShell. The last segment splits on `;`, `|`, newlines, `&&` and a
|
|
62
|
+
// spaced `&`, outside quotes and comments; `&&` before it means an earlier
|
|
63
|
+
// step can explain the exit.
|
|
64
|
+
type Segment = { text: string; andGated: boolean };
|
|
65
|
+
|
|
66
|
+
function lastSegment(command: string): Segment {
|
|
67
|
+
const segments: Segment[] = [];
|
|
68
|
+
let start = 0;
|
|
69
|
+
let andGated = false;
|
|
70
|
+
let inSingle = false;
|
|
71
|
+
let inDouble = false;
|
|
72
|
+
for (let i = 0; i < command.length; i++) {
|
|
73
|
+
const c = command[i];
|
|
74
|
+
if (inSingle) {
|
|
75
|
+
if (c === "'") inSingle = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (inDouble) {
|
|
79
|
+
if (c === "`") i++;
|
|
80
|
+
else if (c === '"') inDouble = false;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (c === "#" && (i === 0 || " \t\n\r".includes(command[i - 1]))) {
|
|
84
|
+
segments.push({ text: command.slice(start, i), andGated });
|
|
85
|
+
while (i + 1 < command.length && command[i + 1] !== "\n" && command[i + 1] !== "\r") i++;
|
|
86
|
+
start = i + 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (c === "'") {
|
|
90
|
+
inSingle = true;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (c === '"') {
|
|
94
|
+
inDouble = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (c === ";" || c === "|" || c === "\n" || c === "\r") {
|
|
98
|
+
segments.push({ text: command.slice(start, i), andGated });
|
|
99
|
+
start = i + 1;
|
|
100
|
+
if (c !== "|") andGated = false;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (c === "&") {
|
|
104
|
+
if (command[i + 1] === "&") {
|
|
105
|
+
segments.push({ text: command.slice(start, i), andGated });
|
|
106
|
+
i++;
|
|
107
|
+
start = i + 1;
|
|
108
|
+
andGated = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const before = command[i - 1];
|
|
112
|
+
if ((before === " " || before === "\t") && command.slice(start, i).trim() !== "") {
|
|
113
|
+
segments.push({ text: command.slice(start, i), andGated });
|
|
114
|
+
start = i + 1;
|
|
115
|
+
andGated = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
segments.push({ text: command.slice(start), andGated });
|
|
120
|
+
return segments.findLast((segment) => segment.text.trim()) ?? { text: command, andGated: false };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** A segment's first word, unquoted, and the text after it; a leading `&` or `.` call operator is dropped. */
|
|
124
|
+
function splitFirstWord(segment: string): { word: string; rest: string } {
|
|
125
|
+
const line = segment.trim().replace(/^[&.]\s+/, "");
|
|
126
|
+
const quoted = /^"([^"]*)"|^'([^']*)'/.exec(line);
|
|
127
|
+
if (quoted) return { word: quoted[1] ?? quoted[2], rest: line.slice(quoted[0].length) };
|
|
128
|
+
const raw = line.split(/\s+/)[0] ?? "";
|
|
129
|
+
return { word: raw.replace(/^["']|["']$/g, ""), rest: line.slice(raw.length) };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The command's file name, lowercased, without a native .exe/.cmd/.bat extension. */
|
|
133
|
+
function baseCommand(word: string): { base: string; nativeExt: string | undefined } {
|
|
134
|
+
const file = (word.split(/[\\/]/).pop() || word).toLowerCase();
|
|
135
|
+
const nativeExt = ["exe", "cmd", "bat"].find((ext) => file.endsWith(`.${ext}`));
|
|
136
|
+
return { base: nativeExt ? file.slice(0, -(nativeExt.length + 1)) : file, nativeExt };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const exitOneOk = (code: number) => code === 0 || code === 1;
|
|
140
|
+
const POWERSHELL_SEMANTICS = new Map<string, (code: number) => boolean>([
|
|
141
|
+
["grep", exitOneOk],
|
|
142
|
+
["rg", exitOneOk],
|
|
143
|
+
["egrep", exitOneOk],
|
|
144
|
+
["fgrep", exitOneOk],
|
|
145
|
+
["findstr", exitOneOk],
|
|
146
|
+
["robocopy", (code) => code >= 0 && code < 8],
|
|
147
|
+
]);
|
|
148
|
+
// Only as `<name>.exe` with output: bare `where` is Where-Object, bare `fc` Format-Custom.
|
|
149
|
+
const POWERSHELL_EXE_WITH_OUTPUT = new Map<string, (code: number) => boolean>([
|
|
150
|
+
["where", exitOneOk],
|
|
151
|
+
["fc", exitOneOk],
|
|
152
|
+
["diff", exitOneOk],
|
|
153
|
+
]);
|
|
154
|
+
const NOT_RECOGNIZED = /is not recognized as (a name of a cmdlet|the name of a cmdlet|an? internal)/i;
|
|
155
|
+
|
|
156
|
+
/** PowerShell: a nonzero exit the final command's table calls success. `output` is stdout and stderr merged. */
|
|
157
|
+
export function powershellExitIsSuccess(command: string, exitCode: number, output: string): boolean {
|
|
158
|
+
if (exitCode === 0) return true;
|
|
159
|
+
const { text, andGated } = lastSegment(command);
|
|
160
|
+
const { word, rest } = splitFirstWord(text);
|
|
161
|
+
const { base, nativeExt } = baseCommand(word);
|
|
162
|
+
const sub = base === "git" ? gitSubcommandOf(rest.trim().split(/\s+/).filter(Boolean)) : undefined;
|
|
163
|
+
const ok =
|
|
164
|
+
sub === "grep" || sub === "diff"
|
|
165
|
+
? exitOneOk
|
|
166
|
+
: ((nativeExt === "exe" && output.trim() !== "" ? POWERSHELL_EXE_WITH_OUTPUT.get(base) : undefined) ?? POWERSHELL_SEMANTICS.get(base));
|
|
167
|
+
if (!ok?.(exitCode)) return false;
|
|
168
|
+
// A command that never ran, or findstr's own error, is a failure after all.
|
|
169
|
+
return !(andGated || NOT_RECOGNIZED.test(output) || /CommandNotFoundException/.test(output) || /^FINDSTR: /m.test(output));
|
|
170
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Quoting one word for a shell command line. Pure. */
|
|
2
|
+
|
|
3
|
+
/** A POSIX shell single-quoted word: the text exactly, whatever it holds. */
|
|
4
|
+
export function shellQuote(word: string): string {
|
|
5
|
+
return `'${word.replace(/'/g, "'\\''")}'`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** PowerShell single-quoted literal: only `'` needs escaping, as `''`. */
|
|
9
|
+
export function powershellQuote(word: string): string {
|
|
10
|
+
return `'${word.replace(/'/g, "''")}'`;
|
|
11
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code's shell result text, made from pi's (pure). pi ends a failing
|
|
3
|
+
* command's output with `\n\nCommand exited with code N` and keeps the
|
|
4
|
+
* output's trailing newline; Claude Code starts a failure with `Exit code N`
|
|
5
|
+
* on its own line and trims the output's leading blank lines and trailing
|
|
6
|
+
* whitespace, and an empty success reads `(<tool> completed with no output)`.
|
|
7
|
+
* A nonzero exit Claude Code's semantics call success (grep's no match, a
|
|
8
|
+
* diff that found differences) reads as a success. pi's truncation notes
|
|
9
|
+
* stay where they are, at the end of the output.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { emptyToolResultText } from "./empty-tool-result.ts";
|
|
13
|
+
import { bashExitOneIsSuccess, powershellExitIsSuccess } from "./shell-exit-semantics.ts";
|
|
14
|
+
|
|
15
|
+
type Block = { type: string; text?: string };
|
|
16
|
+
type ShellResult = { content?: Block[]; structuredContent?: unknown; isError?: boolean };
|
|
17
|
+
|
|
18
|
+
/** pi's text for an empty output. */
|
|
19
|
+
const PI_NO_OUTPUT = "(no output)";
|
|
20
|
+
|
|
21
|
+
function successText(output: string, toolName: string): string {
|
|
22
|
+
if (output === PI_NO_OUTPUT) return emptyToolResultText(toolName);
|
|
23
|
+
return output.replace(/^(\s*\n)+/, "").trimEnd() || emptyToolResultText(toolName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The output before pi's `Command exited with code N` trailer, or undefined when the text is not pi's failure shape. */
|
|
27
|
+
function failureOutput(text: string, exitCode: number): string | undefined {
|
|
28
|
+
const status = `Command exited with code ${exitCode}`;
|
|
29
|
+
if (text !== status && !text.endsWith(`\n\n${status}`)) return undefined;
|
|
30
|
+
return text.slice(0, Math.max(0, text.length - status.length - 2));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Claude Code's failure text: `Exit code N`, then the output on its own lines. */
|
|
34
|
+
function failureText(output: string, exitCode: number): string {
|
|
35
|
+
return (output === PI_NO_OUTPUT || output === "" ? `Exit code ${exitCode}` : `Exit code ${exitCode}\n${output}`).trimEnd();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Claude Code's text for one foreground result, or undefined to keep pi's. */
|
|
39
|
+
export function claudeCodeShellText(text: string, exitCode: unknown, toolName: string): string | undefined {
|
|
40
|
+
if (typeof exitCode !== "number") return undefined;
|
|
41
|
+
if (exitCode === 0) return successText(text, toolName);
|
|
42
|
+
const output = failureOutput(text, exitCode);
|
|
43
|
+
return output === undefined ? undefined : failureText(output, exitCode);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function semanticSuccess(toolName: string, command: string, exitCode: number, output: string): boolean {
|
|
47
|
+
if (toolName === "bash") return exitCode === 1 && bashExitOneIsSuccess(command);
|
|
48
|
+
if (toolName === "powershell") return powershellExitIsSuccess(command, exitCode, output === PI_NO_OUTPUT ? "" : output);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A foreground result with its first text block in Claude Code's words; other results pass through. */
|
|
53
|
+
export function withClaudeCodeShellText<R extends ShellResult>(result: R, toolName: string, command: string): R {
|
|
54
|
+
const first = result?.content?.[0];
|
|
55
|
+
if (first?.type !== "text" || typeof first.text !== "string") return result;
|
|
56
|
+
const exitCode = (result.structuredContent as { exit_code?: unknown } | undefined)?.exit_code;
|
|
57
|
+
const rest = (result.content ?? []).slice(1);
|
|
58
|
+
let text: string | undefined;
|
|
59
|
+
if (typeof exitCode === "number" && exitCode !== 0) {
|
|
60
|
+
const output = failureOutput(first.text, exitCode);
|
|
61
|
+
if (output !== undefined && semanticSuccess(toolName, command, exitCode, output)) {
|
|
62
|
+
return { ...result, content: [{ ...first, text: successText(output, toolName) }, ...rest], isError: false };
|
|
63
|
+
}
|
|
64
|
+
text = output === undefined ? undefined : failureText(output, exitCode);
|
|
65
|
+
} else {
|
|
66
|
+
text = claudeCodeShellText(first.text, exitCode, toolName);
|
|
67
|
+
}
|
|
68
|
+
if (text === undefined || text === first.text) return result;
|
|
69
|
+
return { ...result, content: [{ ...first, text }, ...rest] };
|
|
70
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Which shell One Code spawns, and how — the one place that knows about Git
|
|
3
3
|
* Bash on Windows and about PowerShell.
|
|
4
4
|
*
|
|
5
|
-
* Claude Code's shape (findings §22, docs/decisions/windows.md): Git for
|
|
5
|
+
* Claude Code's shape (findings §22, working-docs/decisions/windows.md): Git for
|
|
6
6
|
* Windows is optional. When a bash is available it drives the `bash` tool,
|
|
7
7
|
* hooks and background shells; `CLAUDE_CODE_GIT_BASH_PATH` (process env, or
|
|
8
8
|
* Claude Code's user-settings `env` block) names a custom bash and is ignored
|
|
@@ -29,9 +29,11 @@ import { type ChildProcess, spawn, type SpawnOptions } from "node:child_process"
|
|
|
29
29
|
import { constants, existsSync } from "node:fs";
|
|
30
30
|
import { access } from "node:fs/promises";
|
|
31
31
|
import { homedir } from "node:os";
|
|
32
|
-
import { basename } from "node:path";
|
|
33
|
-
import { getPowerShellConfig, getShellConfig } from "@earendil-works/pi-coding-agent";
|
|
32
|
+
import { basename, delimiter, join } from "node:path";
|
|
33
|
+
import { getAgentDir, getPowerShellConfig, getShellConfig } from "@earendil-works/pi-coding-agent";
|
|
34
|
+
import { childProcessEnv } from "./app-launch.mjs";
|
|
34
35
|
import { readSettingsEnv } from "./claude-settings.ts";
|
|
36
|
+
import { PowerShellParser, sharedPowerShellParser } from "./powershell-parser.ts";
|
|
35
37
|
import { detachedSpawnOptions, killProcessTree, waitForChildExit } from "./process-tree.ts";
|
|
36
38
|
import { whichOnPath } from "./which.ts";
|
|
37
39
|
|
|
@@ -52,6 +54,20 @@ export const POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-ExecutionPoli
|
|
|
52
54
|
/** pi's UTF-8 output prefix for PowerShell (core/tools/powershell.ts), so Windows PowerShell 5.1 does not emit UTF-16. */
|
|
53
55
|
export const POWERSHELL_UTF8_PREFIX = "try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}\n";
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* What the PowerShell tool prepends to every command, foreground and
|
|
59
|
+
* background: pi's UTF-8 line, then UTF-8 for `Out-File` and `$OutputEncoding`
|
|
60
|
+
* and plain-text rendering, as Claude Code's PowerShell tool runs commands.
|
|
61
|
+
* `$PSStyle.OutputRendering = 'PlainText'` keeps pwsh 7's formatter from
|
|
62
|
+
* colouring table headers, which otherwise reached the model as raw
|
|
63
|
+
* `\x1b[32;1m` escapes.
|
|
64
|
+
*/
|
|
65
|
+
export const POWERSHELL_TOOL_PREFIX =
|
|
66
|
+
// One line, so an error's "At line:N" stays one off the command's own line, as with pi's prefix alone.
|
|
67
|
+
`${POWERSHELL_UTF8_PREFIX.trimEnd()}; try { $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8' } catch {}; ` +
|
|
68
|
+
"if ($ExecutionContext.SessionState.LanguageMode -eq 'FullLanguage') { try { $OutputEncoding = [System.Text.UTF8Encoding]::new() } catch {}; " +
|
|
69
|
+
"if ($null -ne $PSStyle) { try { $PSStyle.OutputRendering = 'PlainText' } catch {} } };\n";
|
|
70
|
+
|
|
55
71
|
export const GIT_BASH_PATH_VAR = "CLAUDE_CODE_GIT_BASH_PATH";
|
|
56
72
|
|
|
57
73
|
// ---------------------------------------------------------------------------
|
|
@@ -187,6 +203,27 @@ export function powerShellSpawn(): ShellSpawn | undefined {
|
|
|
187
203
|
return cachedPowerShell.spawn;
|
|
188
204
|
}
|
|
189
205
|
|
|
206
|
+
/**
|
|
207
|
+
* PowerShell's own parser, served by the same executable and arguments the
|
|
208
|
+
* PowerShell tool runs (lib/powershell-parser.ts), one server per process.
|
|
209
|
+
* Undefined when no PowerShell resolves. It resolves command names on the
|
|
210
|
+
* foreground tool's PATH, pi's managed-tools directory first (piShellEnv):
|
|
211
|
+
* a `findstr.exe` there shadows Windows' own where the command runs. The
|
|
212
|
+
* background spawn's PATH lacks that directory, so a name resolved to
|
|
213
|
+
* System32 here resolves there too.
|
|
214
|
+
*/
|
|
215
|
+
export function powerShellParser(spec: ShellSpawn | undefined = powerShellSpawn()): PowerShellParser | undefined {
|
|
216
|
+
if (!spec) return undefined;
|
|
217
|
+
return sharedPowerShellParser(
|
|
218
|
+
spec.shell,
|
|
219
|
+
() =>
|
|
220
|
+
new PowerShellParser({
|
|
221
|
+
spawnServer: (bootstrap) =>
|
|
222
|
+
spawnShellCommand(spec, bootstrap, { env: piShellEnv(join(getAgentDir(), "bin")), stdio: ["pipe", "pipe", "pipe"] }),
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
190
227
|
/** Whether the resolved PowerShell is Windows PowerShell 5.1 (`powershell.exe`) or PowerShell 7+ (`pwsh`). */
|
|
191
228
|
export function powerShellEdition(spawnSpec: ShellSpawn | undefined): "core" | "desktop" | "unknown" {
|
|
192
229
|
if (!spawnSpec) return "unknown";
|
|
@@ -204,16 +241,20 @@ export function powerShellEdition(spawnSpec: ShellSpawn | undefined): "core" | "
|
|
|
204
241
|
* shell's arguments (`bash -c <cmd>`, `pwsh … -Command <cmd>`); stdin transport
|
|
205
242
|
* writes it to the child's stdin (pi's legacy-WSL bash), which needs stdin
|
|
206
243
|
* piped — callers that must feed their own stdin (hooks) reject that form.
|
|
244
|
+
* The child's environment is `options.env` (default `process.env`) with the
|
|
245
|
+
* bundled app's launcher variables put back to the user's own values
|
|
246
|
+
* (app-launch.mjs childProcessEnv), so a `pi` run from a command is the user's pi.
|
|
207
247
|
*/
|
|
208
248
|
export function spawnShellCommand(spec: ShellSpawn, command: string, options: SpawnOptions): ChildProcess {
|
|
249
|
+
const env = childProcessEnv(options.env ?? process.env);
|
|
209
250
|
if (spec.commandTransport === "stdin") {
|
|
210
251
|
const stdio = Array.isArray(options.stdio) ? ["pipe", ...options.stdio.slice(1)] : ["pipe", "pipe", "pipe"];
|
|
211
|
-
const child = spawn(spec.shell, spec.args, { ...options, stdio: stdio as SpawnOptions["stdio"], windowsHide: true });
|
|
252
|
+
const child = spawn(spec.shell, spec.args, { ...options, env, stdio: stdio as SpawnOptions["stdio"], windowsHide: true });
|
|
212
253
|
child.stdin?.on("error", () => {});
|
|
213
254
|
child.stdin?.end(command);
|
|
214
255
|
return child;
|
|
215
256
|
}
|
|
216
|
-
return spawn(spec.shell, [...spec.args, command], { ...options, windowsHide: true });
|
|
257
|
+
return spawn(spec.shell, [...spec.args, command], { ...options, env, windowsHide: true });
|
|
217
258
|
}
|
|
218
259
|
|
|
219
260
|
// ---------------------------------------------------------------------------
|
|
@@ -228,6 +269,29 @@ export interface ShellOperations {
|
|
|
228
269
|
) => Promise<{ exitCode: number | null }>;
|
|
229
270
|
}
|
|
230
271
|
|
|
272
|
+
/**
|
|
273
|
+
* pi's shell environment (utils/shell.ts `getShellEnv`, not exported): `env`
|
|
274
|
+
* with pi's managed-tools directory (`<agentDir>/bin`) first on PATH, the PATH
|
|
275
|
+
* key found in any case.
|
|
276
|
+
*/
|
|
277
|
+
export function piShellEnv(binDir: string, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
278
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
279
|
+
const current = env[pathKey] ?? "";
|
|
280
|
+
if (current.split(delimiter).includes(binDir)) return { ...env };
|
|
281
|
+
return { ...env, [pathKey]: [binDir, current].filter(Boolean).join(delimiter) };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* `operations` with every command's environment passed through
|
|
286
|
+
* childProcessEnv; `defaultEnv` stands in when the caller passes none (pi's
|
|
287
|
+
* user `!` commands never do, and its local operations then use its shell env).
|
|
288
|
+
*/
|
|
289
|
+
export function withChildProcessEnv(operations: ShellOperations, defaultEnv: () => NodeJS.ProcessEnv): ShellOperations {
|
|
290
|
+
return {
|
|
291
|
+
exec: (command, cwd, options) => operations.exec(command, cwd, { ...options, env: childProcessEnv(options.env ?? defaultEnv()) }),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
231
295
|
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
232
296
|
|
|
233
297
|
/** pi's timeout validation (seconds → ms), same messages. */
|
|
@@ -263,7 +327,7 @@ export function createPowerShellOperations(resolve: () => ShellSpawn | undefined
|
|
|
263
327
|
} catch {
|
|
264
328
|
throw new Error(`Working directory does not exist: ${cwd}\nCannot execute PowerShell commands.`);
|
|
265
329
|
}
|
|
266
|
-
const child = spawnShellCommand(spec, `${
|
|
330
|
+
const child = spawnShellCommand(spec, `${POWERSHELL_TOOL_PREFIX}${command}`, {
|
|
267
331
|
cwd,
|
|
268
332
|
...detachedSpawnOptions(),
|
|
269
333
|
env: env ?? process.env,
|