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,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code's argument hint in the prompt: once the input is a slash command
|
|
3
|
+
* that takes an argument plus one space (`/btw `), a dim placeholder for it
|
|
4
|
+
* (`[question]`) follows the cursor until the user types the argument
|
|
5
|
+
* (findings §34). A command file with an `arguments` list and no
|
|
6
|
+
* `argument-hint` gets a progressive hint instead: the names not typed yet.
|
|
7
|
+
*
|
|
8
|
+
* pi shows an extension command's hint nowhere (its `registerCommand` has no
|
|
9
|
+
* hint field), so a command extension announces its hint on
|
|
10
|
+
* ARGUMENT_HINT_CHANNEL (`announceArgumentHint`, or `registerLocalCommand`'s
|
|
11
|
+
* `argumentHint` option) and the branding extension's prompt editor draws it.
|
|
12
|
+
* pi's own built-ins get theirs from PI_BUILTIN_HINTS.
|
|
13
|
+
*
|
|
14
|
+
* Every command that takes an argument has a hint: Claude Code's text where
|
|
15
|
+
* Claude Code has the same command, else our own syntax in its bracket style
|
|
16
|
+
* (`[optional]`, `<required>`). A command with no argument has none, as in
|
|
17
|
+
* Claude Code.
|
|
18
|
+
*
|
|
19
|
+
* Pure: no pi imports.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { visibleWidth } from "./tui-render.ts";
|
|
23
|
+
|
|
24
|
+
export const ARGUMENT_HINT_CHANNEL = "one-code:argument-hint";
|
|
25
|
+
|
|
26
|
+
export interface ArgumentHint extends CommandHint {
|
|
27
|
+
/** The command name, without the slash. */
|
|
28
|
+
command: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** What the prompt knows about one command's argument. */
|
|
32
|
+
export interface CommandHint {
|
|
33
|
+
/** The placeholder shown after `/<command> `, e.g. `[question]`. */
|
|
34
|
+
hint?: string;
|
|
35
|
+
/** A command file's `arguments` names, for the progressive hint. */
|
|
36
|
+
argNames?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The slice of pi's ExtensionAPI an announcement needs. */
|
|
40
|
+
export interface HintEmitter {
|
|
41
|
+
events: { emit(channel: string, data: unknown): unknown };
|
|
42
|
+
on?(event: "session_start", handler: () => void): unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Announce `/command`'s hint now and again at every `session_start`, so it
|
|
47
|
+
* reaches the prompt editor whether the command registers at load or during
|
|
48
|
+
* `session_start`. Repeats are harmless: the editor keeps one hint per command.
|
|
49
|
+
*/
|
|
50
|
+
export function announceArgumentHint(pi: HintEmitter, command: string, hint: string | CommandHint): void {
|
|
51
|
+
const payload: ArgumentHint = { command, ...(typeof hint === "string" ? { hint } : hint) };
|
|
52
|
+
const emit = () => pi.events.emit(ARGUMENT_HINT_CHANNEL, payload);
|
|
53
|
+
emit();
|
|
54
|
+
pi.on?.("session_start", emit);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* pi's built-in commands that take an argument. Claude Code's text where it has
|
|
59
|
+
* the same command (`/model`, `/compact`, `/export`, and `/rename` for pi's
|
|
60
|
+
* `/name`); pi's own `argumentHint` otherwise.
|
|
61
|
+
*/
|
|
62
|
+
export const PI_BUILTIN_HINTS: Readonly<Record<string, string>> = {
|
|
63
|
+
model: "[model]",
|
|
64
|
+
compact: "<optional custom summarization instructions>",
|
|
65
|
+
export: "[filename]",
|
|
66
|
+
name: "[name]",
|
|
67
|
+
thinking: "<level>",
|
|
68
|
+
login: "<provider>",
|
|
69
|
+
bug: "<description>",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A command file's hint (a SKILL.md, a plugin or template command): its
|
|
74
|
+
* `argument-hint` string, and its `arguments` names (a space-separated string
|
|
75
|
+
* or a list; numeric names are dropped, as they clash with `$0`, `$1`).
|
|
76
|
+
* Undefined when it has neither.
|
|
77
|
+
*/
|
|
78
|
+
export function frontmatterCommandHint(frontmatter: Record<string, unknown> | undefined): CommandHint | undefined {
|
|
79
|
+
const raw = frontmatter?.["argument-hint"];
|
|
80
|
+
const hint = typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
|
|
81
|
+
const names = frontmatter?.arguments;
|
|
82
|
+
const list = typeof names === "string" ? names.split(/\s+/) : Array.isArray(names) ? names : [];
|
|
83
|
+
const argNames = list.filter((n): n is string => typeof n === "string" && n.trim() !== "" && !/^\d+$/.test(n));
|
|
84
|
+
if (!hint && argNames.length === 0) return undefined;
|
|
85
|
+
return { ...(hint && { hint }), ...(argNames.length > 0 && { argNames }) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The words typed so far, quotes grouping (Claude Code's `parseArguments`, minus shell expansion). */
|
|
89
|
+
export function typedArguments(text: string): string[] {
|
|
90
|
+
return [...text.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g)].map((m) => m[1] ?? m[2] ?? m[3]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The placeholder to draw after the cursor for the editor's `text`, or
|
|
95
|
+
* undefined. Claude Code's rule: the input is `/<command> ` and nothing more
|
|
96
|
+
* shows the command's hint; failing that, a command with `arguments` names
|
|
97
|
+
* shows the names not typed yet whenever the input ends in a space.
|
|
98
|
+
*/
|
|
99
|
+
export function hintForInput(text: string, commands: ReadonlyMap<string, CommandHint>): string | undefined {
|
|
100
|
+
if (!text.startsWith("/")) return undefined;
|
|
101
|
+
const space = text.indexOf(" ");
|
|
102
|
+
if (space === -1) return undefined;
|
|
103
|
+
const command = commands.get(text.slice(1, space));
|
|
104
|
+
if (!command) return undefined;
|
|
105
|
+
if (command.hint && text.length === space + 1) return command.hint;
|
|
106
|
+
if (command.argNames?.length && text.endsWith(" ")) {
|
|
107
|
+
const remaining = command.argNames.slice(typedArguments(text.slice(space + 1)).length);
|
|
108
|
+
return remaining.length > 0 ? remaining.map((name) => `[${name}]`).join(" ") : undefined;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The fake end-of-text cursor pi-tui's editor draws: an inverse space. */
|
|
114
|
+
const END_CURSOR = "\x1b[7m \x1b[0m";
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The editor's rendered lines with `placeholder` (already painted; its visible
|
|
118
|
+
* width is measured) drawn into the blank padding after the end-of-text cursor
|
|
119
|
+
* on the first content line. The line keeps its width. No-op when there is no
|
|
120
|
+
* end cursor on that line or too little padding after it, so an unexpected
|
|
121
|
+
* render shape shows no hint rather than a corrupted line.
|
|
122
|
+
*/
|
|
123
|
+
export function applyArgumentHint(lines: string[], placeholder: string): string[] {
|
|
124
|
+
const line = lines[1];
|
|
125
|
+
if (line === undefined) return lines;
|
|
126
|
+
const at = line.lastIndexOf(END_CURSOR);
|
|
127
|
+
if (at === -1) return lines;
|
|
128
|
+
const tail = line.slice(at + END_CURSOR.length);
|
|
129
|
+
const width = visibleWidth(placeholder);
|
|
130
|
+
if (!/^ *$/.test(tail.slice(0, width)) || tail.length < width) return lines;
|
|
131
|
+
const out = lines.slice();
|
|
132
|
+
out[1] = line.slice(0, at + END_CURSOR.length) + placeholder + tail.slice(width);
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** One argument completion, as pi's `getArgumentCompletions` returns them. */
|
|
137
|
+
export interface ArgumentCompletion {
|
|
138
|
+
value: string;
|
|
139
|
+
label: string;
|
|
140
|
+
description?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The completions that extend what was typed, or null to close the menu. pi's
|
|
145
|
+
* editor answers Enter on an open argument menu by applying the selection,
|
|
146
|
+
* not by submitting, so offering the value already typed in full (`/doctor
|
|
147
|
+
* report`) took a second Enter to run the command; an exact match is left
|
|
148
|
+
* out, and with nothing longer the menu stays shut and Enter submits.
|
|
149
|
+
*/
|
|
150
|
+
export function extendingCompletions(prefix: string, options: readonly ArgumentCompletion[]): ArgumentCompletion[] | null {
|
|
151
|
+
const typed = prefix.trim().toLowerCase();
|
|
152
|
+
return dropExactMatch(prefix, options.filter((option) => option.value.startsWith(typed)));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* When an item's value is exactly what was typed (case-blind), only the items
|
|
157
|
+
* that extend it, or null when none does: Enter then submits the typed
|
|
158
|
+
* argument instead of applying some other item a completer returned without
|
|
159
|
+
* filtering (`/auto-mode`). A value is compared as given, so a completion that
|
|
160
|
+
* carries the separator to a required next argument (`"stop "`) still extends
|
|
161
|
+
* a typed `stop`. With no exact item, `items` pass unchanged.
|
|
162
|
+
*/
|
|
163
|
+
export function dropExactMatch<T extends { value: string }>(prefix: string | undefined, items: readonly T[]): T[] | null {
|
|
164
|
+
const typed = (prefix ?? "").trim().toLowerCase();
|
|
165
|
+
if (!items.some((item) => item.value.toLowerCase() === typed)) return items.length ? [...items] : null;
|
|
166
|
+
const longer = items.filter((item) => {
|
|
167
|
+
const value = item.value.toLowerCase();
|
|
168
|
+
return value !== typed && value.startsWith(typed);
|
|
169
|
+
});
|
|
170
|
+
return longer.length ? longer : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type Completer<T extends { value: string }> = (prefix: string) => T[] | null | Promise<T[] | null>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A command's completer with the exact match left out (see
|
|
177
|
+
* `extendingCompletions`), so a fully typed argument runs on the first Enter.
|
|
178
|
+
* `registerLocalCommand` applies it to every local command.
|
|
179
|
+
*/
|
|
180
|
+
export function withoutExactMatch<T extends { value: string }>(complete: Completer<T>): Completer<T> {
|
|
181
|
+
const settle = (prefix: string, items: T[] | null): T[] | null => (items ? dropExactMatch(prefix, items) : items);
|
|
182
|
+
return (prefix) => {
|
|
183
|
+
const items = complete(prefix);
|
|
184
|
+
// Any thenable, not only a native Promise.
|
|
185
|
+
if (items && typeof (items as PromiseLike<T[] | null>).then === "function") {
|
|
186
|
+
return Promise.resolve(items).then((resolved) => settle(prefix, resolved));
|
|
187
|
+
}
|
|
188
|
+
return settle(prefix, items as T[] | null);
|
|
189
|
+
};
|
|
190
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Atomic JSON file writes (pure fs, no pi imports).
|
|
2
|
+
* Atomic JSON and text file writes (pure fs, no pi imports).
|
|
3
3
|
*
|
|
4
4
|
* Write to a temp sibling, then rename over the target — a reader never sees a
|
|
5
5
|
* partially written file, and a crash mid-write leaves the target untouched.
|
|
@@ -14,9 +14,13 @@ import { dirname } from "node:path";
|
|
|
14
14
|
let sequence = 0;
|
|
15
15
|
|
|
16
16
|
export function writeJsonAtomic(path: string, data: unknown, options: { mode?: number } = {}): void {
|
|
17
|
+
writeTextAtomic(path, `${JSON.stringify(data, null, 2)}\n`, options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function writeTextAtomic(path: string, text: string, options: { mode?: number } = {}): void {
|
|
17
21
|
mkdirSync(dirname(path), { recursive: true });
|
|
18
22
|
const tmp = `${path}.tmp-${process.pid}-${++sequence}`;
|
|
19
|
-
writeFileSync(tmp,
|
|
23
|
+
writeFileSync(tmp, text, options.mode !== undefined ? { mode: options.mode } : undefined);
|
|
20
24
|
renameSync(tmp, path);
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether auto-memory is on: Claude Code's `autoMemoryEnabled` setting and its
|
|
3
|
+
* `CLAUDE_CODE_DISABLE_AUTO_MEMORY` override (pure fs, no pi imports).
|
|
4
|
+
*
|
|
5
|
+
* Off means the system prompt has no memory section, the MEMORY.md index is
|
|
6
|
+
* not loaded, and the memory folder is neither created nor offered by
|
|
7
|
+
* `/memory`. The `/memory` panel's "Auto-memory" row toggles the setting in
|
|
8
|
+
* One Code's user settings. The value is read once and pinned for the
|
|
9
|
+
* process, like the config mode: the system prompt must stay byte-stable, so
|
|
10
|
+
* a change applies from the next start.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
import { readSettingsFile, settingsSources } from "./claude-settings.ts";
|
|
15
|
+
|
|
16
|
+
/** The settings key, as Claude Code spells it. */
|
|
17
|
+
export const AUTO_MEMORY_KEY = "autoMemoryEnabled";
|
|
18
|
+
export const AUTO_MEMORY_ENV = "CLAUDE_CODE_DISABLE_AUTO_MEMORY";
|
|
19
|
+
|
|
20
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
21
|
+
const FALSY = new Set(["0", "false", "no", "off"]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* What `CLAUDE_CODE_DISABLE_AUTO_MEMORY` decides: a truthy value turns
|
|
25
|
+
* auto-memory off, a defined falsy one forces it on, anything else leaves it
|
|
26
|
+
* to the settings.
|
|
27
|
+
*/
|
|
28
|
+
export function autoMemoryFromEnv(env: Record<string, string | undefined> = process.env): boolean | undefined {
|
|
29
|
+
const raw = env[AUTO_MEMORY_ENV]?.trim().toLowerCase();
|
|
30
|
+
if (raw === undefined || raw === "") return undefined;
|
|
31
|
+
if (TRUTHY.has(raw)) return false;
|
|
32
|
+
if (FALSY.has(raw)) return true;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* `autoMemoryEnabled` from the settings ladder the mode reads (the last file
|
|
38
|
+
* that sets it wins, managed policy last), ignoring the env override.
|
|
39
|
+
* Default on.
|
|
40
|
+
*/
|
|
41
|
+
export function savedAutoMemory(cwd: string, home: string = os.homedir()): boolean {
|
|
42
|
+
return savedAutoMemorySource(cwd, home).enabled;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** `savedAutoMemory` and the settings file that decides it (undefined: the default). */
|
|
46
|
+
export function savedAutoMemorySource(cwd: string, home: string = os.homedir()): { enabled: boolean; path?: string } {
|
|
47
|
+
let decided: { enabled: boolean; path?: string } = { enabled: true };
|
|
48
|
+
for (const [, path] of settingsSources(cwd, home)) {
|
|
49
|
+
const value = readSettingsFile(path)?.[AUTO_MEMORY_KEY];
|
|
50
|
+
if (typeof value === "boolean") decided = { enabled: value, path };
|
|
51
|
+
}
|
|
52
|
+
return decided;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function readAutoMemory(cwd: string, home: string = os.homedir(), env: Record<string, string | undefined> = process.env): boolean {
|
|
56
|
+
return autoMemoryFromEnv(env) ?? savedAutoMemory(cwd, home);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const AUTO_MEMORY_SLOT = Symbol.for("one-code:auto-memory");
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether auto-memory is on for this process: read once (for the first
|
|
63
|
+
* session's cwd), then pinned for every extension. Tests that need the other
|
|
64
|
+
* value call `resetAutoMemoryForTest` first.
|
|
65
|
+
*/
|
|
66
|
+
export function autoMemoryEnabled(cwd: string = process.cwd()): boolean {
|
|
67
|
+
const slot = globalThis as { [AUTO_MEMORY_SLOT]?: boolean };
|
|
68
|
+
return (slot[AUTO_MEMORY_SLOT] ??= readAutoMemory(cwd));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function resetAutoMemoryForTest(enabled?: boolean): void {
|
|
72
|
+
const slot = globalThis as { [AUTO_MEMORY_SLOT]?: boolean };
|
|
73
|
+
if (enabled === undefined) delete slot[AUTO_MEMORY_SLOT];
|
|
74
|
+
else slot[AUTO_MEMORY_SLOT] = enabled;
|
|
75
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tree-sitter bash grammar, loaded once per process (pure apart from
|
|
3
|
+
* loading the WASM files).
|
|
4
|
+
*
|
|
5
|
+
* Loading is asynchronous (about 5 ms: the web-tree-sitter runtime, then the
|
|
6
|
+
* grammar); parsing after that is synchronous, so the permission gate and the
|
|
7
|
+
* guards stay synchronous. Loading starts when this module is first imported.
|
|
8
|
+
* A hook that parses awaits {@link bashParserReady} first; until the promise
|
|
9
|
+
* settles, and forever if loading failed, {@link parseBash} returns undefined
|
|
10
|
+
* and every caller treats the command as unparseable (findings §30).
|
|
11
|
+
*
|
|
12
|
+
* The loaded parser lives on `globalThis`, not in module state: jiti gives
|
|
13
|
+
* each extension its own instance of this module (findings §3), and five
|
|
14
|
+
* extensions parse bash. It is a cache of a pure function, so sharing it
|
|
15
|
+
* changes no behaviour, only how often the grammar is loaded.
|
|
16
|
+
*
|
|
17
|
+
* The grammar file is tree-sitter-bash's release WASM, vendored under
|
|
18
|
+
* `grammars/` (MIT, `grammars/LICENSE-tree-sitter-bash`): the npm package
|
|
19
|
+
* builds a native addon on install, which this package never uses.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { createRequire } from "node:module";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { Language, Parser, type Tree } from "web-tree-sitter";
|
|
26
|
+
|
|
27
|
+
interface BashParserState {
|
|
28
|
+
ready: Promise<void>;
|
|
29
|
+
parser?: Parser;
|
|
30
|
+
/** Why loading failed, once it has. */
|
|
31
|
+
error?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const STATE_KEY = Symbol.for("one-code.bash-parser");
|
|
35
|
+
|
|
36
|
+
const GRAMMAR_PATH = join(dirname(fileURLToPath(import.meta.url)), "grammars", "tree-sitter-bash.wasm");
|
|
37
|
+
|
|
38
|
+
function state(): BashParserState {
|
|
39
|
+
const store = globalThis as { [STATE_KEY]?: BashParserState };
|
|
40
|
+
let current = store[STATE_KEY];
|
|
41
|
+
if (!current) {
|
|
42
|
+
const created: BashParserState = { ready: Promise.resolve() };
|
|
43
|
+
created.ready = load(created);
|
|
44
|
+
store[STATE_KEY] = created;
|
|
45
|
+
current = created;
|
|
46
|
+
}
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function load(target: BashParserState): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
const runtimeWasm = createRequire(import.meta.url).resolve("web-tree-sitter/web-tree-sitter.wasm");
|
|
53
|
+
await Parser.init({ locateFile: () => runtimeWasm });
|
|
54
|
+
const parser = new Parser();
|
|
55
|
+
parser.setLanguage(await Language.load(GRAMMAR_PATH));
|
|
56
|
+
target.parser = parser;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
target.error = `the bash grammar failed to load (${error instanceof Error ? error.message : String(error)})`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Start loading on import, so the grammar is ready long before the first
|
|
63
|
+
// tool call; hooks still await it.
|
|
64
|
+
state();
|
|
65
|
+
|
|
66
|
+
/** Settles once the grammar has loaded or failed to load. Never rejects. */
|
|
67
|
+
export function bashParserReady(): Promise<void> {
|
|
68
|
+
return state().ready;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Why the grammar is unavailable: still loading, or the load error. Undefined once it is ready. */
|
|
72
|
+
export function bashParserUnavailable(): string | undefined {
|
|
73
|
+
const current = state();
|
|
74
|
+
if (current.parser) return undefined;
|
|
75
|
+
return current.error ?? "the bash grammar is still loading";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse `command` into a syntax tree, or undefined when the grammar is not
|
|
80
|
+
* loaded. The caller must `delete()` the tree: it lives in WASM memory.
|
|
81
|
+
*/
|
|
82
|
+
export function parseBash(command: string): Tree | undefined {
|
|
83
|
+
return state().parser?.parse(command) ?? undefined;
|
|
84
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/btw` panel's `f to fork` request, sent to the subagents extension over
|
|
3
|
+
* pi.events (extensions share no module state). Claude Code's key spawns a
|
|
4
|
+
* fork subagent: it inherits the conversation, then the side question and its
|
|
5
|
+
* answer, takes the question as its task, runs in the background and reports
|
|
6
|
+
* back as a task notification.
|
|
7
|
+
*
|
|
8
|
+
* The subagents extension sets `handled` synchronously inside its listener, so
|
|
9
|
+
* the emitter can tell "nobody listens" (the extension is not loaded) from a
|
|
10
|
+
* fork still starting; `respond` settles the request once.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
import type { Message } from "@earendil-works/pi-ai";
|
|
15
|
+
import { commandInputBlock, commandStdoutBlock } from "./local-command.ts";
|
|
16
|
+
import { cutPlainText } from "./tui-render.ts";
|
|
17
|
+
|
|
18
|
+
export const BTW_FORK_CHANNEL = "one-code:btw-fork";
|
|
19
|
+
|
|
20
|
+
/** The started fork, or why it could not start. */
|
|
21
|
+
export type BtwForkResult = { name: string; taskId: string } | { error: string };
|
|
22
|
+
|
|
23
|
+
export interface BtwForkRequest {
|
|
24
|
+
/** The command context of the open panel (the listener reads cwd, session and model from it). */
|
|
25
|
+
ctx: unknown;
|
|
26
|
+
/** The side question: the fork's task. */
|
|
27
|
+
question: string;
|
|
28
|
+
/** The question and answer, appended to the fork's inherited conversation. */
|
|
29
|
+
messages: Message[];
|
|
30
|
+
/** Set by the listener before it returns. */
|
|
31
|
+
handled?: boolean;
|
|
32
|
+
respond: (result: BtwForkResult) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Emit a fork request and wait for its answer; an error result when no listener took it. */
|
|
36
|
+
export function requestBtwFork(
|
|
37
|
+
events: { emit(channel: string, data: unknown): void },
|
|
38
|
+
request: Omit<BtwForkRequest, "respond" | "handled">,
|
|
39
|
+
): Promise<BtwForkResult> {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const payload: BtwForkRequest = { ...request, respond: resolve };
|
|
42
|
+
events.emit(BTW_FORK_CHANNEL, payload);
|
|
43
|
+
if (!payload.handled) resolve({ error: "Forking needs the subagents extension, which is not loaded." });
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The fork's base name, Claude Code's rule: the question's first three words,
|
|
49
|
+
* hyphen-joined, lowercased, cut to `[a-z0-9-]` and 24 characters; `fork`
|
|
50
|
+
* when nothing is left. "does reading outside project…" becomes
|
|
51
|
+
* `does-reading-outside`. A taken name is suffixed by the caller.
|
|
52
|
+
*/
|
|
53
|
+
export function btwForkName(question: string): string {
|
|
54
|
+
const slug = question
|
|
55
|
+
.trim()
|
|
56
|
+
.split(/\s+/)
|
|
57
|
+
.slice(0, 3)
|
|
58
|
+
.join("-")
|
|
59
|
+
.toLowerCase()
|
|
60
|
+
.replace(/[^a-z0-9-]/g, "")
|
|
61
|
+
.replace(/-+/g, "-")
|
|
62
|
+
.replace(/^-|-$/g, "")
|
|
63
|
+
.slice(0, 24);
|
|
64
|
+
return slug || "fork";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Claude Code's transcript line for a started fork: `⑂ forked <name> (<last 4 of the id>)`. */
|
|
68
|
+
export function btwForkedLine(name: string, taskId: string): string {
|
|
69
|
+
return `⑂ forked ${name} (${taskId.slice(-4)})`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A fork's task description, Claude Code's rule (findings §29): the question
|
|
74
|
+
* with whitespace collapsed, cut to 49 characters plus `…` past 50.
|
|
75
|
+
*/
|
|
76
|
+
export function btwForkDescription(question: string): string {
|
|
77
|
+
return cutPlainText(question.trim().replace(/\s+/g, " "), 50);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* What the main conversation is told when a fork starts: Claude Code's
|
|
82
|
+
* local-command breadcrumb for `/btw <question>` and its stdout, the forked
|
|
83
|
+
* line (findings §40), as two bare text blocks on the next user message. No
|
|
84
|
+
* caveat block precedes them, unlike other local commands.
|
|
85
|
+
*/
|
|
86
|
+
export function btwForkRecord(name: string, taskId: string, question: string): string[] {
|
|
87
|
+
return [commandInputBlock("btw", question.trim()), commandStdoutBlock(btwForkedLine(name, taskId))];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A fork's task id, Claude Code's shape: `a<name>-<16 hex>` (`awhat-word-is-477c2d9e80467f14`). */
|
|
91
|
+
export function btwForkTaskId(name: string): string {
|
|
92
|
+
return `a${name}-${randomBytes(8).toString("hex")}`;
|
|
93
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The curated extensions every in-process child session loads: a subagent
|
|
3
|
+
* (subagents/runner.ts) and a workflow agent (workflow/agent-session.ts) alike.
|
|
4
|
+
* They go in through `additionalExtensionPaths`, which the loader loads even
|
|
5
|
+
* under `noExtensions`. Broadly Claude Code's model: a child gets project
|
|
6
|
+
* context, file freshness and a working toolset (search, skills, web, notebook),
|
|
7
|
+
* but NOT the frontier chrome (banner, spinner, recap) or the orchestration
|
|
8
|
+
* extensions. Nested spawning is an injected tool instead (subagents/index.ts).
|
|
9
|
+
*
|
|
10
|
+
* `lsp` is deliberately NOT here (matching Claude Code, findings §17.3): a child
|
|
11
|
+
* session is torn down with the raw `AgentSession.dispose()`, which never fires
|
|
12
|
+
* `session_shutdown`, so lsp's cleanup would never run and any language server
|
|
13
|
+
* it started would leak for the life of the parent session. MCP is not listed
|
|
14
|
+
* either: its tools are shared in from the parent as customTools
|
|
15
|
+
* (lib/mcp-share.ts `watchMcpTools`), not reconnected.
|
|
16
|
+
*
|
|
17
|
+
* Order mirrors the package's load order: kept-thinking first (the first
|
|
18
|
+
* `context` hook, before anything reads the messages), the ledger's request
|
|
19
|
+
* side next (it cuts the request to its tail before anything injects), then
|
|
20
|
+
* the reminder and deferral sinks (system-reminder, tool-search), before
|
|
21
|
+
* anything emitting on their channels (the bus does not replay), then
|
|
22
|
+
* context-management and compaction as in the main session (a child's requests
|
|
23
|
+
* carry the same thinking edit, and a fork that crosses its threshold is
|
|
24
|
+
* summarized by replaying its own last request, not by pi's uncached default
|
|
25
|
+
* summarizer), then the ledger's payload side, after every hook that shapes
|
|
26
|
+
* the tail and before cache-marks. A child has its own event bus (pi's
|
|
27
|
+
* resource loader makes one per loader), so its captures never reach the
|
|
28
|
+
* parent's side calls. A fork's file is a copy of the parent's, records included, so
|
|
29
|
+
* its first request extends the parent's last one and reads its cache. A caller that loads these must set the
|
|
30
|
+
* loader's `noContextFiles`, because claude-context injects `# claudeMd` itself.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { dirname, join } from "node:path";
|
|
34
|
+
import { fileURLToPath } from "node:url";
|
|
35
|
+
|
|
36
|
+
const EXTENSIONS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
37
|
+
|
|
38
|
+
// A name with a slash is a child-specific entry file: `tool-call-corruption/child`
|
|
39
|
+
// blocks damaged OpenRouter calls like the main session's, before the hook and
|
|
40
|
+
// permission gates (which load after these paths), but prints nothing. The
|
|
41
|
+
// shell tools are One Code's, as in the main session: without them a child ran
|
|
42
|
+
// pi's built-in bash (timeout in seconds, no default) while a fork advertised
|
|
43
|
+
// ours (milliseconds), and had no PowerShell tool. A child binds without a UI
|
|
44
|
+
// (mode "print"), so `run_in_background` runs blocking there and nothing a
|
|
45
|
+
// child starts outlives it (Claude Code kills an agent's shells as it exits).
|
|
46
|
+
export const CHILD_EXTENSIONS = [
|
|
47
|
+
"kept-thinking", "ledger", "system-reminder", "tool-search", "context-management", "compaction", "ledger/wire", "cache-marks", "claude-context", "tool-call-corruption/child",
|
|
48
|
+
"file-tracker", "search-tools", "skill", "bash/child", "powershell/child", "web", "web-fetch", "notebook",
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
export const CHILD_EXTENSION_PATHS = CHILD_EXTENSIONS.map((name) =>
|
|
52
|
+
name.includes("/") ? join(EXTENSIONS_DIR, `${name}.ts`) : join(EXTENSIONS_DIR, name, "index.ts"));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A subagent's or workflow agent's file edits, announced to the parent session.
|
|
3
|
+
*
|
|
4
|
+
* An in-process child keeps its own file tracker, so a file the parent read and
|
|
5
|
+
* a child then edited looks to the parent like a change "on disk" by someone
|
|
6
|
+
* else, and the parent's notice said so (the user, a formatter, a command).
|
|
7
|
+
* The child runners publish each successful edit, write or notebook edit on
|
|
8
|
+
* the parent's bus; the parent's file-tracker names the child in its notice
|
|
9
|
+
* instead. The file stays stale either way, so the parent still re-reads
|
|
10
|
+
* before editing it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { isWritingTool, pathArgument } from "../auto-mode/paths.ts";
|
|
14
|
+
import { resolveToolPath } from "./tool-path.ts";
|
|
15
|
+
|
|
16
|
+
export const CHILD_WROTE_CHANNEL = "one-code:child-wrote";
|
|
17
|
+
|
|
18
|
+
export interface ChildWrote {
|
|
19
|
+
/** Absolute path of the file the child changed. */
|
|
20
|
+
path: string;
|
|
21
|
+
/** The child's name or description, for the notice; undefined when unknown. */
|
|
22
|
+
agent?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tracks a child session's tool calls and returns the changed file when one of
|
|
27
|
+
* its file-writing calls (edit, write, notebook_edit) ends without an error,
|
|
28
|
+
* resolved as the child's tools resolved it (lib/tool-path.ts), which is how
|
|
29
|
+
* the parent's file-tracker keys it.
|
|
30
|
+
*/
|
|
31
|
+
export function childWriteWatcher(cwd: string) {
|
|
32
|
+
const started = new Map<string, string>();
|
|
33
|
+
return (event: { type?: string; toolName?: string; toolCallId?: string; args?: unknown; isError?: boolean }): string | undefined => {
|
|
34
|
+
if (!event.toolCallId || !event.toolName || !isWritingTool(event.toolName)) return undefined;
|
|
35
|
+
if (event.type === "tool_execution_start") {
|
|
36
|
+
const path = pathArgument(event.args as Record<string, unknown> | undefined);
|
|
37
|
+
if (path) started.set(event.toolCallId, resolveToolPath(path, cwd));
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (event.type !== "tool_execution_end") return undefined;
|
|
41
|
+
const path = started.get(event.toolCallId);
|
|
42
|
+
started.delete(event.toolCallId);
|
|
43
|
+
return event.isError ? undefined : path;
|
|
44
|
+
};
|
|
45
|
+
}
|