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
|
@@ -12,25 +12,48 @@
|
|
|
12
12
|
* The model then reads or greps the file for the parts it actually needs.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
16
|
-
import {
|
|
15
|
+
import { closeSync, mkdirSync, openSync, readSync, statSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { StringDecoder } from "node:string_decoder";
|
|
17
17
|
import { join } from "node:path";
|
|
18
|
+
import { oneCodeStateDir } from "./paths.ts";
|
|
19
|
+
import { privateSessionTempDir } from "./scratchpad.ts";
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Where a session's oversized outputs are persisted — the session dir,
|
|
21
23
|
* matching Claude Code's `<session-dir>/tool-results/<id>.txt`. A session-less
|
|
22
|
-
* run (`--no-session
|
|
23
|
-
*
|
|
24
|
-
*
|
|
24
|
+
* run (`--no-session`, every workflow agent, a subagent of a session-less
|
|
25
|
+
* parent) has no such dir, so the session's own private temp dir serves
|
|
26
|
+
* (`<tmp>/onecode-<uid>/<project-slug>/<session-id>`, 0700, beside its
|
|
27
|
+
* scratchpad). Until 2026-09-27 that fallback was one shared
|
|
28
|
+
* `<tmp>/one-code` with no uid or session in its name, which the gate then
|
|
29
|
+
* treated as working space: every session-less run on the machine, any
|
|
30
|
+
* project and any user, read the others' outputs with no prompt. When the
|
|
31
|
+
* temp dir cannot be made private, `~/.onecode/tmp/<session-id>` serves. The
|
|
32
|
+
* model gets the path either way. Shared by every producer that would
|
|
33
|
+
* otherwise truncate: cutting text loses context, a file keeps it one `read`
|
|
34
|
+
* away.
|
|
25
35
|
*/
|
|
26
|
-
export function sessionResultsDir(
|
|
36
|
+
export function sessionResultsDir(
|
|
37
|
+
ctx: { cwd?: string; sessionManager?: { getSessionDir?: () => string | undefined; getSessionId?: () => string } } | undefined,
|
|
38
|
+
): string {
|
|
39
|
+
let sessionId: string | undefined;
|
|
27
40
|
try {
|
|
28
41
|
const dir = ctx?.sessionManager?.getSessionDir?.();
|
|
29
42
|
if (dir) return dir;
|
|
43
|
+
sessionId = ctx?.sessionManager?.getSessionId?.();
|
|
30
44
|
} catch {
|
|
31
|
-
// fall through to the temp folder
|
|
45
|
+
// fall through to the private temp folder
|
|
32
46
|
}
|
|
33
|
-
|
|
47
|
+
const id = (sessionId || `process-${process.pid}`).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
48
|
+
const temp = privateSessionTempDir(ctx?.cwd ?? process.cwd(), id);
|
|
49
|
+
if (temp) return temp;
|
|
50
|
+
const fallback = join(oneCodeStateDir(), "tmp", id);
|
|
51
|
+
try {
|
|
52
|
+
mkdirSync(fallback, { recursive: true, mode: 0o700 });
|
|
53
|
+
} catch {
|
|
54
|
+
// persistIfLarge names the failed write in its own result.
|
|
55
|
+
}
|
|
56
|
+
return fallback;
|
|
34
57
|
}
|
|
35
58
|
|
|
36
59
|
/** Claude Code persists around this size; pi's bash truncation uses 50KB too. */
|
|
@@ -76,6 +99,35 @@ export function persistIfLarge(text: string, options: PersistOptions): string {
|
|
|
76
99
|
saved = `Output too large (${formatSize(size)}) and could not be saved to ${file}: ${(error as Error).message}. Only this preview survives — re-run with a narrower query if more is needed.`;
|
|
77
100
|
}
|
|
78
101
|
|
|
102
|
+
return persistedBlock(saved, preview, previewBytes);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The block for output already whole in a file (a monitor's spool): its path,
|
|
107
|
+
* its size and a preview, the same shape `persistIfLarge` returns, without
|
|
108
|
+
* reading or copying the file.
|
|
109
|
+
*/
|
|
110
|
+
export function persistedFileBlock(file: string, size: number, preview: string, previewBytes: number = PREVIEW_BYTES): string {
|
|
111
|
+
return persistedBlock(`Output too large (${formatSize(size)}). Full output saved to: ${file}`, preview, previewBytes);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Preview an existing spool without reading or copying the whole output. */
|
|
115
|
+
export function persistedFilePreview(path: string): string | undefined {
|
|
116
|
+
let fd: number | undefined;
|
|
117
|
+
try {
|
|
118
|
+
const { size } = statSync(path);
|
|
119
|
+
fd = openSync(path, "r");
|
|
120
|
+
const head = Buffer.alloc(Math.min(size, PREVIEW_BYTES));
|
|
121
|
+
const read = readSync(fd, head, 0, head.length, 0);
|
|
122
|
+
return persistedFileBlock(path, size, new StringDecoder("utf8").write(head.subarray(0, read)));
|
|
123
|
+
} catch {
|
|
124
|
+
return undefined;
|
|
125
|
+
} finally {
|
|
126
|
+
if (fd !== undefined) closeSync(fd);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function persistedBlock(saved: string, preview: string, previewBytes: number): string {
|
|
79
131
|
return [
|
|
80
132
|
"<persisted-output>",
|
|
81
133
|
saved,
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* (which pi-ai re-exports from its entry point, so it needs no vendoring — unlike
|
|
21
21
|
* the estimate/clamp helpers under `/utils/estimate` and `/api/simple-options`,
|
|
22
22
|
* which have no bare re-export). The two exported helpers
|
|
23
|
-
* are byte-identical to upstream
|
|
23
|
+
* are byte-identical to upstream (pi-ai 1.1.0 changed only `CHARS_PER_TOKEN`,
|
|
24
|
+
* which `charsPerToken` picks for the running pi); the one adaptation is the internal
|
|
24
25
|
* `estimateContextTokens`, narrowed to accept only `Context` (the sole shape we
|
|
25
26
|
* call it with) rather than upstream's `Context | Message[]` overload — the
|
|
26
27
|
* Message[] branch would be dead code here. `test/unit/pi-ai-estimate.test.ts`
|
|
@@ -48,8 +49,22 @@
|
|
|
48
49
|
*/
|
|
49
50
|
|
|
50
51
|
import { getSystemMessageText, type Api, type Context, type ImageContent, type Message, type Model, type TextContent, type Usage } from "@earendil-works/pi-ai";
|
|
52
|
+
import { VERSION as PI_VERSION } from "@earendil-works/pi-coding-agent";
|
|
53
|
+
import { compareVersions } from "./pi-version.ts";
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
/**
|
|
56
|
+
* pi-ai's characters per token: 4 through pi-ai 1.0.x, 3.5 from pi-ai 1.1.0.
|
|
57
|
+
* pi-ai's entry point looks the same on both, so the running pi's version
|
|
58
|
+
* stands in: pi 0.x keeps pi-ai 0.x and pi 1.1.0 requires pi-ai ^1.1.0, but
|
|
59
|
+
* pi 1.0.x can float onto either (pi ships no shrinkwrap since 1.0.1). There
|
|
60
|
+
* 3.5 wins, as it predicts less output room than pi gives, never more.
|
|
61
|
+
*/
|
|
62
|
+
export function charsPerToken(piVersion: string | undefined): number {
|
|
63
|
+
const order = piVersion === undefined ? undefined : compareVersions(piVersion, "1.0.0");
|
|
64
|
+
return order !== undefined && order < 0 ? 4 : 3.5;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const CHARS_PER_TOKEN = charsPerToken(PI_VERSION);
|
|
53
68
|
const ESTIMATED_IMAGE_CHARS = 4800;
|
|
54
69
|
const CONTEXT_SAFETY_TOKENS = 4096;
|
|
55
70
|
const MIN_MAX_TOKENS = 1;
|
|
@@ -73,7 +88,8 @@ function estimateTextAndImageContentChars(content: string | Array<TextContent |
|
|
|
73
88
|
return chars;
|
|
74
89
|
}
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
/** pi-ai's chars-per-token estimate for plain text. */
|
|
92
|
+
export function estimateTextTokens(text: string): number {
|
|
77
93
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
78
94
|
}
|
|
79
95
|
|
|
@@ -8,11 +8,17 @@
|
|
|
8
8
|
* version to the range this release was verified against and warns — softly,
|
|
9
9
|
* never blocking — when outside it. The bundled `one-code` app pins pi inside
|
|
10
10
|
* the range, so it never warns.
|
|
11
|
+
*
|
|
12
|
+
* The minimum is 1.0.0: the record-once request path (extensions/ledger)
|
|
13
|
+
* builds on pi 1.0's session entries, transcript system messages and request
|
|
14
|
+
* pipeline (decisions/distribution.md). The peer range in package.json and the
|
|
15
|
+
* guide name the same floor (pi-version.test.ts), and CI runs
|
|
16
|
+
* `test/e2e/pi-floor-smoke.mjs` against it.
|
|
11
17
|
*/
|
|
12
18
|
|
|
13
19
|
/** Inclusive minimum and exclusive maximum pi version this release is tested against. */
|
|
14
|
-
export const TESTED_PI_MIN = "0.
|
|
15
|
-
export const TESTED_PI_MAX_EXCLUSIVE = "
|
|
20
|
+
export const TESTED_PI_MIN = "1.0.0";
|
|
21
|
+
export const TESTED_PI_MAX_EXCLUSIVE = "1.5.0";
|
|
16
22
|
|
|
17
23
|
/** Dotted-numeric parse; undefined for anything that is not plain x.y.z numbers. */
|
|
18
24
|
export function parseVersion(version: string): number[] | undefined {
|
|
@@ -52,3 +58,33 @@ export function piVersionWarning(runningPiVersion: string | undefined): string |
|
|
|
52
58
|
`(or pin pi inside the tested range).`
|
|
53
59
|
);
|
|
54
60
|
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether the running pi fires `before_agent_start` for a run an extension
|
|
64
|
+
* opens from idle with `sendMessage(…, { triggerTurn: true })`. pi 1.2.0
|
|
65
|
+
* added it (its changelog, "before_agent_start for extension-triggered
|
|
66
|
+
* runs"); before, only `prompt()` fired the event. A version check, not a
|
|
67
|
+
* feature test: nothing tells the two apart before such a run starts, and an
|
|
68
|
+
* unparseable version reads as no, the older and safer behavior.
|
|
69
|
+
*/
|
|
70
|
+
export function piFiresBeforeAgentStartForCustomRuns(runningPiVersion: string | undefined): boolean {
|
|
71
|
+
return piAtLeast(runningPiVersion, "1.2.0");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Whether the running pi ships built-in extensions that can replace ours'
|
|
76
|
+
* names (`builtin:tool-search`, `builtin:mcp`; lib/replaced-builtins.mjs).
|
|
77
|
+
* A version check, not a feature test: nothing an extension can see tells a
|
|
78
|
+
* pi with the built-ins from one without, and an unparseable version reads
|
|
79
|
+
* as no.
|
|
80
|
+
*/
|
|
81
|
+
export function piHasBuiltinExtensions(runningPiVersion: string | undefined, firstVersion: string): boolean {
|
|
82
|
+
return piAtLeast(runningPiVersion, firstVersion);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Whether the running pi is `min` or later; a missing or unparseable version reads as no. */
|
|
86
|
+
function piAtLeast(runningPiVersion: string | undefined, min: string): boolean {
|
|
87
|
+
if (!runningPiVersion) return false;
|
|
88
|
+
const order = compareVersions(runningPiVersion, min);
|
|
89
|
+
return order !== undefined && order >= 0;
|
|
90
|
+
}
|
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bus channels between plan-mode and permissions
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Bus channels between plan-mode and permissions, and the session entry that
|
|
3
|
+
* records the plan file (also read by the post-compaction file restore). They
|
|
4
|
+
* live here, not in either extension, because permissions cannot import
|
|
5
|
+
* plan-mode (plan-mode already imports `permissions/modes.ts`) and a string
|
|
6
|
+
* literal typed in two places is the one way a rename silently disconnects a
|
|
7
|
+
* reader.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
/** Request a permission-mode change (`{ mode }`); permissions applies it. */
|
|
10
|
+
/** Request a permission-mode change (`{ mode, toolCallId? }`); permissions applies it and anchors a tool-driven switch to its result. */
|
|
9
11
|
export const MODE_CHANNEL = "one-code:set-permission-mode";
|
|
10
12
|
/** Announces plan mode's one writable file (`{ path }`); the permissions matcher consumes it. */
|
|
11
13
|
export const PLAN_FILE_CHANNEL = "one-code:plan-file-path";
|
|
14
|
+
/**
|
|
15
|
+
* Session entry type persisting plan mode's allocated path across resume/branch
|
|
16
|
+
* (`{ path, sessionId?, copiedFrom? }`): the session that allocated it, and the
|
|
17
|
+
* plan a fork's copy was taken from.
|
|
18
|
+
*/
|
|
19
|
+
export const PLAN_FILE_ENTRY = "one-code:plan-mode-file";
|
|
20
|
+
|
|
21
|
+
export interface PlanFileRecord {
|
|
22
|
+
path: string;
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
copiedFrom?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type EntryLike = { type: string; customType?: string; data?: unknown };
|
|
28
|
+
|
|
29
|
+
/** Every plan-file record in `entries`, oldest first. */
|
|
30
|
+
export function planFileRecords(entries: readonly EntryLike[]): PlanFileRecord[] {
|
|
31
|
+
const records: PlanFileRecord[] = [];
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (entry.type !== "custom" || entry.customType !== PLAN_FILE_ENTRY) continue;
|
|
34
|
+
const data = entry.data as { path?: unknown; sessionId?: unknown; copiedFrom?: unknown } | undefined;
|
|
35
|
+
if (typeof data?.path !== "string") continue;
|
|
36
|
+
records.push({
|
|
37
|
+
path: data.path,
|
|
38
|
+
...(typeof data.sessionId === "string" ? { sessionId: data.sessionId } : {}),
|
|
39
|
+
...(typeof data.copiedFrom === "string" ? { copiedFrom: data.copiedFrom } : {}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return records;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The plan file the branch's latest plan-file entry records, if any. */
|
|
46
|
+
export function planFileOnBranch(entries: readonly EntryLike[]): string | undefined {
|
|
47
|
+
return planFileRecords(entries).at(-1)?.path;
|
|
48
|
+
}
|
|
@@ -36,6 +36,7 @@ import { readEnabledPlugins } from "./claude-settings.ts";
|
|
|
36
36
|
import { readOverrides } from "./plugin-overrides.ts";
|
|
37
37
|
import { pluginRoot } from "./plugin-root.ts";
|
|
38
38
|
import { isSkillEnabled, readSkillStates, skillOverrideKey } from "./skill-overrides.ts";
|
|
39
|
+
import { claudeSourcesOn } from "./config-mode.ts";
|
|
39
40
|
import { claudeUserDir } from "./paths.ts";
|
|
40
41
|
|
|
41
42
|
export interface PluginManifest {
|
|
@@ -144,7 +145,12 @@ export function loadInstalledPlugins(pluginsDir: string, cwd?: string): Installe
|
|
|
144
145
|
const plugins: InstalledPlugin[] = [];
|
|
145
146
|
for (const [key, entries] of Object.entries(registry.plugins)) {
|
|
146
147
|
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
147
|
-
const entry = entries.find((e) =>
|
|
148
|
+
const entry = entries.find((e) =>
|
|
149
|
+
e && typeof e === "object" && !Array.isArray(e) &&
|
|
150
|
+
typeof e.installPath === "string" && e.installPath.length > 0 &&
|
|
151
|
+
(e.projectPath === undefined || typeof e.projectPath === "string") &&
|
|
152
|
+
existsSync(e.installPath) && entryAppliesToCwd(e, cwd),
|
|
153
|
+
);
|
|
148
154
|
if (!entry?.installPath) continue;
|
|
149
155
|
|
|
150
156
|
const { name, marketplace } = splitPluginKey(key);
|
|
@@ -158,7 +164,7 @@ export function loadInstalledPlugins(pluginsDir: string, cwd?: string): Installe
|
|
|
158
164
|
name: validPluginName(manifest?.name) ?? name,
|
|
159
165
|
marketplace,
|
|
160
166
|
path: entry.installPath,
|
|
161
|
-
version: entry.version,
|
|
167
|
+
version: typeof entry.version === "string" ? entry.version : undefined,
|
|
162
168
|
description: typeof manifest?.description === "string" ? manifest.description : undefined,
|
|
163
169
|
rawEnabled: typeof entry.enabled === "boolean" ? entry.enabled : undefined,
|
|
164
170
|
});
|
|
@@ -166,12 +172,12 @@ export function loadInstalledPlugins(pluginsDir: string, cwd?: string): Installe
|
|
|
166
172
|
return plugins.sort((a, b) => a.name.localeCompare(b.name));
|
|
167
173
|
}
|
|
168
174
|
|
|
169
|
-
/** A manifest `name` usable as a namespace
|
|
175
|
+
/** A manifest `name` usable as a namespace and plugin identifier. */
|
|
170
176
|
export function validPluginName(value: unknown): string | undefined {
|
|
171
177
|
if (typeof value !== "string") return undefined;
|
|
172
178
|
const trimmed = value.trim();
|
|
173
|
-
if (trimmed.length === 0) return undefined;
|
|
174
|
-
if (/[
|
|
179
|
+
if (trimmed.length === 0 || trimmed === "." || trimmed.includes("..")) return undefined;
|
|
180
|
+
if (/[/\\:@\s\p{Cc}\p{Cf}]/u.test(trimmed)) return undefined;
|
|
175
181
|
return trimmed;
|
|
176
182
|
}
|
|
177
183
|
|
|
@@ -244,8 +250,8 @@ export function findPluginCommands(plugin: Pick<Plugin, "name">, commandsDir: st
|
|
|
244
250
|
|
|
245
251
|
/** The two roots + settings locations discovery reads from. */
|
|
246
252
|
export interface DiscoverRoots {
|
|
247
|
-
/** Claude Code's plugins dir (`~/.claude/plugins`) — read-only. */
|
|
248
|
-
claudePluginsDir
|
|
253
|
+
/** Claude Code's plugins dir (`~/.claude/plugins`) — read-only; unset in independent mode (lib/config-mode.ts). */
|
|
254
|
+
claudePluginsDir?: string;
|
|
249
255
|
/** One Code's plugin root (`<agentDir>/plugins`) — ours to write. */
|
|
250
256
|
oneCodeRoot: string;
|
|
251
257
|
cwd: string;
|
|
@@ -255,7 +261,7 @@ export interface DiscoverRoots {
|
|
|
255
261
|
/** The standard roots; wiring passes `getAgentDir()` (pure modules can't). */
|
|
256
262
|
export function defaultDiscoverRoots(agentDir: string, cwd: string = process.cwd(), home: string = os.homedir()): DiscoverRoots {
|
|
257
263
|
return {
|
|
258
|
-
claudePluginsDir: join(claudeUserDir(home), "plugins"),
|
|
264
|
+
claudePluginsDir: claudeSourcesOn() ? join(claudeUserDir(home), "plugins") : undefined,
|
|
259
265
|
oneCodeRoot: pluginRoot(agentDir),
|
|
260
266
|
cwd,
|
|
261
267
|
home,
|
|
@@ -330,20 +336,23 @@ export function invalidatePluginsCache(): void {
|
|
|
330
336
|
}
|
|
331
337
|
|
|
332
338
|
export function discoverPlugins(roots: DiscoverRoots): DiscoveredPlugins {
|
|
333
|
-
const
|
|
339
|
+
const claudeDir = roots.claudePluginsDir;
|
|
340
|
+
const key = [claudeDir ?? "", roots.oneCodeRoot, roots.cwd, roots.home].join("\n");
|
|
334
341
|
if (box.current?.key === key) return box.current.result;
|
|
335
342
|
|
|
336
|
-
const ccEnabled = readEnabledPlugins(roots.cwd, roots.home);
|
|
343
|
+
const ccEnabled = claudeDir ? readEnabledPlugins(roots.cwd, roots.home) : {};
|
|
337
344
|
const overrides = readOverrides(roots.oneCodeRoot);
|
|
338
345
|
const skillOverrides = readSkillStates(roots.oneCodeRoot);
|
|
339
346
|
|
|
340
|
-
const claudePlugins: Plugin[] =
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
+
const claudePlugins: Plugin[] = claudeDir
|
|
348
|
+
? loadInstalledPlugins(claudeDir, roots.cwd).map((p) => ({
|
|
349
|
+
...p,
|
|
350
|
+
originRoot: "claude" as const,
|
|
351
|
+
enabled: claudePluginEnabled(p.id, ccEnabled, overrides),
|
|
352
|
+
overridden: p.id in overrides,
|
|
353
|
+
dataRoot: join(claudeDir, "data"),
|
|
354
|
+
}))
|
|
355
|
+
: [];
|
|
347
356
|
|
|
348
357
|
const oneCodePlugins: Plugin[] = loadInstalledPlugins(roots.oneCodeRoot, roots.cwd).map((p) => ({
|
|
349
358
|
...p,
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PowerShell side of the parse server (lib/powershell-parser.ts): a short
|
|
3
|
+
* bootstrap passed on the command line, and the walker script it reads from
|
|
4
|
+
* stdin and runs.
|
|
5
|
+
*
|
|
6
|
+
* The walker calls PowerShell's own `Parser::ParseInput` on each request and
|
|
7
|
+
* writes back every syntax-tree node, so the gate judges the line exactly as
|
|
8
|
+
* the PowerShell that runs it reads it (decisions/windows.md "The PowerShell
|
|
9
|
+
* pre-gate reads PowerShell's own parse"). All judgement stays in TypeScript;
|
|
10
|
+
* the walker only reports.
|
|
11
|
+
*
|
|
12
|
+
* Wire protocol, one line each way, ASCII only because every payload is
|
|
13
|
+
* base64: Windows PowerShell 5.1 reads and writes its console in the OEM code
|
|
14
|
+
* page, which would garble exactly the Unicode dashes and quotes a gate must
|
|
15
|
+
* see.
|
|
16
|
+
*
|
|
17
|
+
* - The first stdin line is the walker script (base64 UTF-8). The walker
|
|
18
|
+
* answers with a ready record, id 0: `{ id, ready, edition, version, language }`.
|
|
19
|
+
* - Each later stdin line is `<id> <base64 UTF-8 command>`. The walker answers
|
|
20
|
+
* `{ id, nodes, errors }`, or `{ id, failure }` when the walk itself threw.
|
|
21
|
+
* - Each stdout line is base64 UTF-8 JSON. EOF on stdin ends the walker.
|
|
22
|
+
*
|
|
23
|
+
* `nodes` is the tree in pre-order, root first. Each node carries its .NET
|
|
24
|
+
* type name, its parent's index (-1 for the root), its extent as UTF-16
|
|
25
|
+
* offsets into the command (the same units as a JS string index), and a few
|
|
26
|
+
* fields for the types a gate reads (see `nodeFields`). `errors` lists each
|
|
27
|
+
* parse error's ErrorId; PowerShell runs nothing from a line that has one.
|
|
28
|
+
*
|
|
29
|
+
* Pure: two string constants, no pi imports, shared with the BI fork.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Passed after `-Command`. It reads the first stdin line and runs it as the
|
|
34
|
+
* walker. It holds no double quote, so no platform's argument quoting can
|
|
35
|
+
* change it.
|
|
36
|
+
*/
|
|
37
|
+
export const POWERSHELL_PARSE_BOOTSTRAP =
|
|
38
|
+
"$s=[Console]::In.ReadLine();if($null -ne $s){. ([ScriptBlock]::Create([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s))))}";
|
|
39
|
+
|
|
40
|
+
/** The walker, sent as the first stdin line. Plain ASCII; no backtick, so it survives a template literal unchanged. */
|
|
41
|
+
export const POWERSHELL_PARSE_WALKER = String.raw`
|
|
42
|
+
$ErrorActionPreference = 'Stop'
|
|
43
|
+
$utf8 = New-Object System.Text.UTF8Encoding $false
|
|
44
|
+
$out = [Console]::Out
|
|
45
|
+
|
|
46
|
+
# Command lookup must never load or run a module: autoloading is off, and the
|
|
47
|
+
# built-in modules the gate's cmdlets live in are imported from $PSHOME by
|
|
48
|
+
# path, so a same-named module earlier on PSModulePath cannot stand in. The
|
|
49
|
+
# walker only reports what a name resolves to (type, module, path); the gate
|
|
50
|
+
# enforces which of those it trusts (powershell-tree.ts).
|
|
51
|
+
$PSModuleAutoLoadingPreference = 'None'
|
|
52
|
+
foreach ($module in 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security') {
|
|
53
|
+
$manifest = "$PSHOME/Modules/$module"
|
|
54
|
+
if ([System.IO.Directory]::Exists($manifest)) { try { Import-Module -Name $manifest } catch {} }
|
|
55
|
+
}
|
|
56
|
+
$binderType = 'System.Management.Automation.Language.StaticParameterBinder' -as [type]
|
|
57
|
+
|
|
58
|
+
function Send-Record($record) {
|
|
59
|
+
$json = ConvertTo-Json -InputObject $record -Depth 5 -Compress
|
|
60
|
+
$out.WriteLine([Convert]::ToBase64String($utf8.GetBytes($json)))
|
|
61
|
+
$out.Flush()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function Get-NodeFields($node) {
|
|
65
|
+
$f = @{}
|
|
66
|
+
switch ($node.GetType().Name) {
|
|
67
|
+
'CommandAst' { $f.operator = [string]$node.InvocationOperator; $f.name = $node.GetCommandName() }
|
|
68
|
+
'CommandParameterAst' { $f.name = $node.ParameterName }
|
|
69
|
+
'StringConstantExpressionAst' { $f.value = $node.Value; $f.kind = [string]$node.StringConstantType }
|
|
70
|
+
'ExpandableStringExpressionAst' { $f.value = $node.Value; $f.kind = [string]$node.StringConstantType }
|
|
71
|
+
'ConstantExpressionAst' { $f.value = [string]$node.Value }
|
|
72
|
+
'VariableExpressionAst' { $f.name = $node.VariablePath.UserPath; $f.splatted = $node.Splatted }
|
|
73
|
+
'FileRedirectionAst' { $f.from = [string]$node.FromStream; $f.append = $node.Append }
|
|
74
|
+
'MergingRedirectionAst' { $f.from = [string]$node.FromStream; $f.to = [string]$node.ToStream }
|
|
75
|
+
'BinaryExpressionAst' { $f.operator = [string]$node.Operator }
|
|
76
|
+
'UnaryExpressionAst' { $f.operator = [string]$node.TokenKind }
|
|
77
|
+
'AssignmentStatementAst' { $f.operator = [string]$node.Operator }
|
|
78
|
+
'PipelineChainAst' { $f.operator = [string]$node.Operator; $f.background = $node.Background }
|
|
79
|
+
'PipelineAst' { if ($node.PSObject.Properties['Background']) { $f.background = $node.Background } }
|
|
80
|
+
'TypeExpressionAst' { $f.name = $node.TypeName.FullName }
|
|
81
|
+
'TypeConstraintAst' { $f.name = $node.TypeName.FullName }
|
|
82
|
+
'MemberExpressionAst' { $f.static = $node.Static }
|
|
83
|
+
'InvokeMemberExpressionAst' { $f.static = $node.Static }
|
|
84
|
+
}
|
|
85
|
+
$f
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Send-Record @{
|
|
89
|
+
id = 0
|
|
90
|
+
ready = $true
|
|
91
|
+
edition = [string]$PSVersionTable.PSEdition
|
|
92
|
+
version = [string]$PSVersionTable.PSVersion
|
|
93
|
+
language = [string]$ExecutionContext.SessionState.LanguageMode
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
while ($null -ne ($line = [Console]::In.ReadLine())) {
|
|
97
|
+
$space = $line.IndexOf(' ')
|
|
98
|
+
if ($space -lt 1) { continue }
|
|
99
|
+
$id = [int]$line.Substring(0, $space)
|
|
100
|
+
try {
|
|
101
|
+
$command = $utf8.GetString([Convert]::FromBase64String($line.Substring($space + 1)))
|
|
102
|
+
$tokens = $null
|
|
103
|
+
$parseErrors = $null
|
|
104
|
+
$ast = [System.Management.Automation.Language.Parser]::ParseInput($command, [ref]$tokens, [ref]$parseErrors)
|
|
105
|
+
$all = @($ast.FindAll({ $true }, $true))
|
|
106
|
+
$index = New-Object 'System.Collections.Generic.Dictionary[System.Object,int]'
|
|
107
|
+
$nodes = New-Object System.Collections.ArrayList
|
|
108
|
+
$asts = New-Object System.Collections.ArrayList
|
|
109
|
+
foreach ($node in $all) {
|
|
110
|
+
if ($index.ContainsKey($node)) { continue }
|
|
111
|
+
$parent = -1
|
|
112
|
+
if ($null -ne $node.Parent -and $index.ContainsKey($node.Parent)) { $parent = $index[$node.Parent] }
|
|
113
|
+
$record = Get-NodeFields $node
|
|
114
|
+
$record.type = $node.GetType().Name
|
|
115
|
+
$record.parent = $parent
|
|
116
|
+
$record.start = $node.Extent.StartOffset
|
|
117
|
+
$record.end = $node.Extent.EndOffset
|
|
118
|
+
$index[$node] = $nodes.Count
|
|
119
|
+
[void]$nodes.Add($record)
|
|
120
|
+
[void]$asts.Add($node)
|
|
121
|
+
}
|
|
122
|
+
# Second pass, once every node has its index: what each static command
|
|
123
|
+
# name resolves to in this PowerShell, and for a cmdlet how PowerShell's
|
|
124
|
+
# own binder assigns its arguments to parameters.
|
|
125
|
+
for ($i = 0; $i -lt $nodes.Count; $i++) {
|
|
126
|
+
if ($nodes[$i].type -ne 'CommandAst' -or $null -eq $nodes[$i].name) { continue }
|
|
127
|
+
$command = $asts[$i]
|
|
128
|
+
$info = $null
|
|
129
|
+
try { $info = $ExecutionContext.InvokeCommand.GetCommand($nodes[$i].name, 'All') } catch {}
|
|
130
|
+
if ($null -eq $info) { continue }
|
|
131
|
+
if ($info -is [System.Management.Automation.AliasInfo]) {
|
|
132
|
+
$nodes[$i].alias = $true
|
|
133
|
+
$info = $info.ResolvedCommand
|
|
134
|
+
if ($null -eq $info) { continue }
|
|
135
|
+
}
|
|
136
|
+
$nodes[$i].commandType = [string]$info.CommandType
|
|
137
|
+
$nodes[$i].resolvedName = $info.Name
|
|
138
|
+
if ($info -is [System.Management.Automation.ApplicationInfo]) { $nodes[$i].resolvedName = $info.Path }
|
|
139
|
+
$nodes[$i].module = [string]$info.ModuleName
|
|
140
|
+
if ($info -isnot [System.Management.Automation.CmdletInfo]) { continue }
|
|
141
|
+
if ($null -eq $binderType) { $nodes[$i].bindingUnavailable = $true; continue }
|
|
142
|
+
try {
|
|
143
|
+
# An aliased command is bound as a copy spelled with the cmdlet's name,
|
|
144
|
+
# so the binding never depends on how an edition's binder treats an
|
|
145
|
+
# alias; each bound value is mapped back by extent.
|
|
146
|
+
$bindAst = $command
|
|
147
|
+
$origin = 0
|
|
148
|
+
$nameEnd = 0
|
|
149
|
+
$delta = 0
|
|
150
|
+
if ($nodes[$i].alias) {
|
|
151
|
+
$nameExtent = $command.CommandElements[0].Extent
|
|
152
|
+
$relative = $nameExtent.StartOffset - $command.Extent.StartOffset
|
|
153
|
+
$text = $command.Extent.Text
|
|
154
|
+
$copy = $text.Substring(0, $relative) + $info.Name + $text.Substring($relative + $nameExtent.Text.Length)
|
|
155
|
+
$bindAst = [System.Management.Automation.Language.Parser]::ParseInput($copy, [ref]$null, [ref]$null).Find({ param($n) $n -is [System.Management.Automation.Language.CommandAst] }, $false)
|
|
156
|
+
$origin = $command.Extent.StartOffset
|
|
157
|
+
$nameEnd = $relative + $info.Name.Length
|
|
158
|
+
$delta = $info.Name.Length - $nameExtent.Text.Length
|
|
159
|
+
}
|
|
160
|
+
# The node of the original command a bound value stands for, or -1:
|
|
161
|
+
# the value itself, or the node with the same (mapped) extent and type.
|
|
162
|
+
# The binder builds values of its own for a remaining-arguments
|
|
163
|
+
# parameter (Write-Output a b: an array of copies).
|
|
164
|
+
$original = {
|
|
165
|
+
param($ast)
|
|
166
|
+
if ([object]::ReferenceEquals($bindAst, $command) -and $index.ContainsKey($ast)) { return $index[$ast] }
|
|
167
|
+
$start = $ast.Extent.StartOffset
|
|
168
|
+
$end = $ast.Extent.EndOffset
|
|
169
|
+
if (-not [object]::ReferenceEquals($bindAst, $command)) {
|
|
170
|
+
if ($start -ge $nameEnd) { $start -= $delta }
|
|
171
|
+
if ($end -ge $nameEnd) { $end -= $delta }
|
|
172
|
+
$start += $origin
|
|
173
|
+
$end += $origin
|
|
174
|
+
}
|
|
175
|
+
$type = $ast.GetType().Name
|
|
176
|
+
for ($j = $i + 1; $j -lt $nodes.Count -and $nodes[$j].start -lt $command.Extent.EndOffset; $j++) {
|
|
177
|
+
if ($nodes[$j].start -eq $start -and $nodes[$j].end -eq $end -and $nodes[$j].type -eq $type) { return $j }
|
|
178
|
+
}
|
|
179
|
+
return -1
|
|
180
|
+
}
|
|
181
|
+
$bound = $binderType::BindCommand($bindAst, $true)
|
|
182
|
+
$nodes[$i].bindings = @(foreach ($key in $bound.BoundParameters.Keys) {
|
|
183
|
+
$result = $bound.BoundParameters[$key]
|
|
184
|
+
$record = @{ parameter = [string]$key; value = -1 }
|
|
185
|
+
if ($null -ne $result.Value) {
|
|
186
|
+
$record.value = & $original $result.Value
|
|
187
|
+
# -2 marks a value the binder built: its elements, if an array,
|
|
188
|
+
# are mapped one by one.
|
|
189
|
+
if ($record.value -lt 0) {
|
|
190
|
+
$record.value = -2
|
|
191
|
+
if ($result.Value -is [System.Management.Automation.Language.ArrayLiteralAst]) {
|
|
192
|
+
$record.elements = @(foreach ($element in $result.Value.Elements) { & $original $element })
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
$record
|
|
197
|
+
})
|
|
198
|
+
$nodes[$i].bindingErrors = @(foreach ($key in $bound.BindingExceptions.Keys) { [string]$key })
|
|
199
|
+
} catch {
|
|
200
|
+
$nodes[$i].bindingUnavailable = $true
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
Send-Record @{ id = $id; nodes = @($nodes); errors = @($parseErrors | ForEach-Object { $_.ErrorId }) }
|
|
204
|
+
} catch {
|
|
205
|
+
Send-Record @{ id = $id; failure = $_.Exception.Message }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
`;
|