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
|
@@ -6,20 +6,28 @@
|
|
|
6
6
|
* same mechanism the bash extension uses). Every model-facing field —
|
|
7
7
|
* description, promptSnippet, promptGuidelines, parameters — is passed through
|
|
8
8
|
* from pi's own definition byte-identical, so the system prompt and tool
|
|
9
|
-
* schemas are unchanged
|
|
9
|
+
* schemas are unchanged, with one exception: write carries Claude Code's
|
|
10
|
+
* description, short or long by the model's tier (write-description.ts,
|
|
11
|
+
* lib/tool-variants.ts), and is registered again when the form changes.
|
|
12
|
+
* Execution delegates to a per-cwd instance of pi's
|
|
10
13
|
* real definition (they close over cwd, and enter_worktree moves the session
|
|
11
14
|
* mid-run). Only the TUI renderers differ: a `●` call line, and the base
|
|
12
15
|
* result component indented under a `⎿` elbow so upstream streaming,
|
|
13
16
|
* truncation, and ctrl+o expansion behavior is preserved. Edit keeps its own
|
|
14
17
|
* call component (the diff preview a permission prompt relies on) with just
|
|
15
|
-
* the header line restyled.
|
|
18
|
+
* the header line restyled. Write renders as Claude Code's Write
|
|
19
|
+
* (write-render.ts): a preview of the content or the diff while it waits, then
|
|
20
|
+
* "Wrote N lines" or "Added N lines, removed M lines"; to show an overwrite's
|
|
21
|
+
* diff afterwards, its execution reads the old file first and records the diff
|
|
22
|
+
* on the result's details, which never reach the model.
|
|
16
23
|
*
|
|
17
24
|
* grep/find/ls stay tier-gated: registering them does not bypass the
|
|
18
25
|
* search-tools extension, which reconciles the active-tool list on
|
|
19
26
|
* session_start/model_select and removes them for frontier models.
|
|
20
27
|
*/
|
|
21
28
|
|
|
22
|
-
import
|
|
29
|
+
import { readFile, stat } from "node:fs/promises";
|
|
30
|
+
import type { ExtensionAPI, ExtensionToolContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
23
31
|
import {
|
|
24
32
|
createEditToolDefinition,
|
|
25
33
|
createFindToolDefinition,
|
|
@@ -27,9 +35,14 @@ import {
|
|
|
27
35
|
createLsToolDefinition,
|
|
28
36
|
createReadToolDefinition,
|
|
29
37
|
createWriteToolDefinition,
|
|
38
|
+
generateDiffString,
|
|
30
39
|
} from "@earendil-works/pi-coding-agent";
|
|
31
40
|
import { perCwd } from "../lib/per-cwd.ts";
|
|
32
|
-
import {
|
|
41
|
+
import { type DescriptionForm, registerFormTool } from "../lib/tool-variants.ts";
|
|
42
|
+
import { resolveToolPath } from "../lib/tool-path.ts";
|
|
43
|
+
import { ccWrapBuiltinRenderers, type RejectedFileOp } from "../lib/tui-render.ts";
|
|
44
|
+
import { editDescription, writeDescription } from "./write-description.ts";
|
|
45
|
+
import { createWriteRenderers, WRITE_DIFF_MAX_BYTES, type WriteRenderDeps, writeDetails } from "./write-render.ts";
|
|
33
46
|
|
|
34
47
|
// The concrete definitions are each strongly typed to their own schema; this
|
|
35
48
|
// extension treats them uniformly, so the spec is deliberately loose. Every
|
|
@@ -41,6 +54,12 @@ interface BuiltinSpec {
|
|
|
41
54
|
create: (cwd: string) => BuiltinDefinition;
|
|
42
55
|
title: (args: Record<string, unknown> | undefined) => string | undefined;
|
|
43
56
|
keepBaseCall?: boolean;
|
|
57
|
+
/** A file tool: a rejection without a reason reads "User rejected <op> to <path>". */
|
|
58
|
+
rejectedAs?: RejectedFileOp;
|
|
59
|
+
/** Write's own Claude Code renderers and result details. */
|
|
60
|
+
write?: boolean;
|
|
61
|
+
/** A description in place of pi's, by the session's description form; `base` is pi's own. */
|
|
62
|
+
description?: (form: DescriptionForm, base: string) => string;
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
const asSpecCreate = (create: (cwd: string) => unknown): BuiltinSpec["create"] =>
|
|
@@ -48,24 +67,51 @@ const asSpecCreate = (create: (cwd: string) => unknown): BuiltinSpec["create"] =
|
|
|
48
67
|
|
|
49
68
|
const BUILTINS: BuiltinSpec[] = [
|
|
50
69
|
{ label: "Read", create: asSpecCreate(createReadToolDefinition), title: (a) => a?.path as string | undefined },
|
|
51
|
-
{
|
|
70
|
+
{
|
|
71
|
+
label: "Write",
|
|
72
|
+
create: asSpecCreate(createWriteToolDefinition),
|
|
73
|
+
title: (a) => a?.path as string | undefined,
|
|
74
|
+
rejectedAs: "write",
|
|
75
|
+
write: true,
|
|
76
|
+
description: writeDescription,
|
|
77
|
+
},
|
|
52
78
|
{
|
|
53
79
|
label: "Update",
|
|
54
80
|
create: asSpecCreate(createEditToolDefinition),
|
|
55
81
|
title: (a) => a?.path as string | undefined,
|
|
56
82
|
keepBaseCall: true,
|
|
83
|
+
rejectedAs: "update",
|
|
84
|
+
description: (_form, base) => editDescription(base),
|
|
57
85
|
},
|
|
58
86
|
{ label: "Grep", create: asSpecCreate(createGrepToolDefinition), title: (a) => a?.pattern as string | undefined },
|
|
59
87
|
{ label: "Find", create: asSpecCreate(createFindToolDefinition), title: (a) => a?.pattern as string | undefined },
|
|
60
88
|
{ label: "LS", create: asSpecCreate(createLsToolDefinition), title: (a) => (a?.path as string | undefined) ?? "." },
|
|
61
89
|
];
|
|
62
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The file a write is about to replace, for its diff: undefined when there is
|
|
93
|
+
* none, null when it is not a readable file or too big to diff.
|
|
94
|
+
*/
|
|
95
|
+
const readBefore: WriteRenderDeps["readBefore"] = async (raw, cwd) => {
|
|
96
|
+
const path = resolveToolPath(raw, cwd);
|
|
97
|
+
try {
|
|
98
|
+
const info = await stat(path);
|
|
99
|
+
if (!info.isFile() || info.size > WRITE_DIFF_MAX_BYTES) return null;
|
|
100
|
+
return await readFile(path, "utf8");
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return (error as NodeJS.ErrnoException).code === "ENOENT" ? undefined : null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const writeDeps: WriteRenderDeps = { readBefore, diff: (before, after) => generateDiffString(before, after).diff };
|
|
107
|
+
|
|
63
108
|
export default function toolStyleExtension(pi: ExtensionAPI) {
|
|
64
109
|
for (const spec of BUILTINS) {
|
|
65
110
|
const base = spec.create(process.cwd());
|
|
66
111
|
const forCwd = perCwd(spec.create);
|
|
112
|
+
const renderers = ccWrapBuiltinRenderers(spec.label, base, { title: spec.title, keepBaseCall: spec.keepBaseCall, rejectedAs: spec.rejectedAs });
|
|
67
113
|
|
|
68
|
-
|
|
114
|
+
const definition = {
|
|
69
115
|
name: base.name,
|
|
70
116
|
label: base.label,
|
|
71
117
|
description: base.description,
|
|
@@ -75,10 +121,22 @@ export default function toolStyleExtension(pi: ExtensionAPI) {
|
|
|
75
121
|
executionMode: base.executionMode,
|
|
76
122
|
prepareArguments: base.prepareArguments,
|
|
77
123
|
constrainedSampling: base.constrainedSampling,
|
|
78
|
-
...
|
|
79
|
-
async execute(toolCallId, params, signal, onUpdate, ctx:
|
|
80
|
-
|
|
124
|
+
...(spec.write ? createWriteRenderers(writeDeps, renderers.renderResult) : renderers),
|
|
125
|
+
async execute(toolCallId, params, signal, onUpdate, ctx: ExtensionToolContext) {
|
|
126
|
+
const run = () => forCwd(ctx.cwd).execute(toolCallId, params as never, signal, onUpdate, ctx);
|
|
127
|
+
if (!spec.write) return run();
|
|
128
|
+
const { path, content } = params as { path?: unknown; content?: unknown };
|
|
129
|
+
if (typeof path !== "string" || typeof content !== "string") return run();
|
|
130
|
+
const before = await readBefore(path, ctx.cwd);
|
|
131
|
+
const result = await run();
|
|
132
|
+
return { ...result, details: writeDetails(before, content, writeDeps.diff) };
|
|
81
133
|
},
|
|
82
|
-
} as ToolDefinition
|
|
134
|
+
} as ToolDefinition;
|
|
135
|
+
const describe = spec.description;
|
|
136
|
+
if (!describe) {
|
|
137
|
+
pi.registerTool(definition);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
registerFormTool(pi, definition, (form) => describe(form, base.description));
|
|
83
141
|
}
|
|
84
142
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The write tool's model-facing description: Claude Code's Write text (pure).
|
|
3
|
+
*
|
|
4
|
+
* pi's own write tool runs underneath (tool-style/index.ts); only the
|
|
5
|
+
* description is Claude Code's, short on frontier and workhorse, long on cheap
|
|
6
|
+
* and tiny (lib/tool-variants.ts), with One Code's tool names. "Overwriting an
|
|
7
|
+
* existing file you haven't read will fail" holds here: the file tracker
|
|
8
|
+
* refuses it (file-tracker/tracker.ts). Read and edit keep pi's descriptions,
|
|
9
|
+
* because Claude Code's describe a different call shape (`file_path`,
|
|
10
|
+
* numbered lines, `old_string`); edit's gets the read rule appended
|
|
11
|
+
* (`editDescription`).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { DescriptionForm } from "../lib/tool-variants.ts";
|
|
15
|
+
|
|
16
|
+
export const WRITE_SHORT_DESCRIPTION = `Writes a file to the local filesystem, overwriting if one exists.
|
|
17
|
+
|
|
18
|
+
When to use: creating a new file, or fully replacing one you've already read. Overwriting an existing file you haven't read will fail. For partial changes, use edit instead.`;
|
|
19
|
+
|
|
20
|
+
export const WRITE_LONG_DESCRIPTION = `Writes a file to the local filesystem.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
- This tool will overwrite the existing file if there is one at the provided path.
|
|
24
|
+
- If this is an existing file, you MUST use the read tool first to read the file's contents. This tool will fail if you did not read the file first.
|
|
25
|
+
- Prefer the edit tool for modifying existing files — it only sends the diff. Only use this tool to create new files or for complete rewrites.
|
|
26
|
+
- NEVER create documentation files (*.md) or README files unless explicitly requested by the User.
|
|
27
|
+
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`;
|
|
28
|
+
|
|
29
|
+
export function writeDescription(form: DescriptionForm): string {
|
|
30
|
+
return form === "long" ? WRITE_LONG_DESCRIPTION : WRITE_SHORT_DESCRIPTION;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* pi's edit description plus the rule the file tracker enforces, which pi's
|
|
35
|
+
* text does not state (write's does). Without it, GPT-6 models read with `cat`
|
|
36
|
+
* and met the refusal first (2026-10-04 Codex self-test: 16 refused edits).
|
|
37
|
+
*/
|
|
38
|
+
export function editDescription(base: string): string {
|
|
39
|
+
return `${base} The file must have been read in this conversation, with the read tool or a shell command that printed the whole file as it is on disk (such as cat), or the edit is refused; if it changed on disk since, read it again first.`;
|
|
40
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The write tool's transcript, as Claude Code shows a Write (pure: no pi
|
|
3
|
+
* imports; tool-style/index.ts wires it over pi's write tool).
|
|
4
|
+
*
|
|
5
|
+
* - While the call waits to run (a permission prompt sits under it), the call
|
|
6
|
+
* line carries a preview of what it will write: the new file's first lines,
|
|
7
|
+
* or the diff against the file it overwrites. The prompt's decision depends
|
|
8
|
+
* on it, the way edit's diff preview serves an edit prompt.
|
|
9
|
+
* - Once it ran: `⎿ Wrote N lines to <path>` and the file's first lines for a
|
|
10
|
+
* new file, `⎿ Added N lines, removed M lines` and the diff for an
|
|
11
|
+
* overwrite.
|
|
12
|
+
* - Rejected without a reason: `⎿ User rejected write to <path>` and the
|
|
13
|
+
* first lines it would have written.
|
|
14
|
+
*
|
|
15
|
+
* The content preview stops after WRITE_PREVIEW_LINES lines with a
|
|
16
|
+
* `… +N lines (ctrl+o to expand)` trailer (ctrl+o works inside the prompt
|
|
17
|
+
* too); a diff shows whole, as edit's does. Every line is wrapped to the
|
|
18
|
+
* width, never cut, so nothing in an overwide line hides past the edge.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { hardWrapColumns } from "../lib/text-width.ts";
|
|
22
|
+
import { sanitizeDisplayText } from "../lib/terminal-text.ts";
|
|
23
|
+
import {
|
|
24
|
+
callLine,
|
|
25
|
+
countNoun,
|
|
26
|
+
displayToolPath,
|
|
27
|
+
EXPAND_HINT,
|
|
28
|
+
elbowIndent,
|
|
29
|
+
linesComponent,
|
|
30
|
+
rejectedFileHead,
|
|
31
|
+
spinnerBulletColor,
|
|
32
|
+
stripReminderBlocks,
|
|
33
|
+
summarizeArgs,
|
|
34
|
+
textContent,
|
|
35
|
+
type ThemeLike,
|
|
36
|
+
type TuiComponent,
|
|
37
|
+
} from "../lib/tui-render.ts";
|
|
38
|
+
import { userDenialKind } from "../lib/user-denial.ts";
|
|
39
|
+
|
|
40
|
+
/** Content lines shown before the `… +N lines` trailer (Claude Code's). */
|
|
41
|
+
export const WRITE_PREVIEW_LINES = 10;
|
|
42
|
+
|
|
43
|
+
/** An overwrite's diff is computed only when both sides are at most this big; past it the result reads as a create. */
|
|
44
|
+
export const WRITE_DIFF_MAX_BYTES = 512 * 1024;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* What a finished write did, on its result's `details` (display-only: tool
|
|
48
|
+
* result details never reach the model). `diff` is pi's display diff
|
|
49
|
+
* (`+12 line`, `-12 line`, ` 12 line`), absent when it was too big to compute.
|
|
50
|
+
*/
|
|
51
|
+
export type WriteDetails = { kind: "create" } | { kind: "update"; diff?: string };
|
|
52
|
+
|
|
53
|
+
/** Lines in the content, a trailing newline ending the last line rather than starting another (Claude Code's count). */
|
|
54
|
+
export function countLines(content: string): number {
|
|
55
|
+
const parts = content.split("\n");
|
|
56
|
+
return content.endsWith("\n") ? parts.length - 1 : parts.length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The details a finished write carries: `before` is the file's old content, undefined when it did not exist, null when it was not read. */
|
|
60
|
+
export function writeDetails(before: string | null | undefined, content: string, diff: (before: string, after: string) => string): WriteDetails {
|
|
61
|
+
if (before === undefined) return { kind: "create" };
|
|
62
|
+
if (before === null || before.length > WRITE_DIFF_MAX_BYTES || content.length > WRITE_DIFF_MAX_BYTES) return { kind: "update" };
|
|
63
|
+
return { kind: "update", diff: diff(before, content) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** One source line made safe and wrapped: escapes and control characters removed, tabs as spaces. */
|
|
67
|
+
function wrapLine(line: string, width: number): string[] {
|
|
68
|
+
const safe = sanitizeDisplayText(line.replace(/\t/g, " "));
|
|
69
|
+
return safe === "" ? [""] : hardWrapColumns(safe, Math.max(1, width));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The content's first lines (all when expanded), wrapped to `width`, with the `… +N lines` trailer. */
|
|
73
|
+
export function contentPreviewLines(theme: ThemeLike, content: string, width: number, expanded: boolean): string[] {
|
|
74
|
+
const total = countLines(content);
|
|
75
|
+
const source = (content || "(No content)").split("\n");
|
|
76
|
+
if (content.endsWith("\n")) source.pop();
|
|
77
|
+
const shown = expanded ? source : source.slice(0, WRITE_PREVIEW_LINES);
|
|
78
|
+
const out = shown.flatMap((line) => wrapLine(line, width).map((part) => theme.fg("toolOutput", part)));
|
|
79
|
+
const hidden = total - shown.length;
|
|
80
|
+
if (!expanded && hidden > 0) out.push(theme.fg("dim", `… +${countNoun(hidden, "line")} (${EXPAND_HINT})`));
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const DIFF_COLOR: Record<string, string> = { "+": "toolDiffAdded", "-": "toolDiffRemoved" };
|
|
85
|
+
|
|
86
|
+
/** pi's display diff, each line coloured by its sign and wrapped to `width`. */
|
|
87
|
+
export function diffPreviewLines(theme: ThemeLike, diff: string, width: number): string[] {
|
|
88
|
+
if (diff === "") return [];
|
|
89
|
+
return diff.split("\n").flatMap((line) => {
|
|
90
|
+
const color = DIFF_COLOR[line[0]] ?? "toolDiffContext";
|
|
91
|
+
return wrapLine(line, width).map((part) => theme.fg(color, part));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Added and removed lines in pi's display diff. */
|
|
96
|
+
export function diffCounts(diff: string): { added: number; removed: number } {
|
|
97
|
+
let added = 0;
|
|
98
|
+
let removed = 0;
|
|
99
|
+
for (const line of diff.split("\n")) {
|
|
100
|
+
if (line.startsWith("+")) added++;
|
|
101
|
+
else if (line.startsWith("-")) removed++;
|
|
102
|
+
}
|
|
103
|
+
return { added, removed };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Claude Code's "Added N lines, removed M lines" (either half alone when the other is zero). */
|
|
107
|
+
export function updateSummary(theme: ThemeLike, diff: string): string {
|
|
108
|
+
const { added, removed } = diffCounts(diff);
|
|
109
|
+
const plural = (n: number) => (n > 1 ? "lines" : "line");
|
|
110
|
+
const parts: string[] = [];
|
|
111
|
+
if (added > 0) parts.push(`Added ${theme.bold(String(added))} ${plural(added)}`);
|
|
112
|
+
if (removed > 0) parts.push(`${added > 0 ? "r" : "R"}emoved ${theme.bold(String(removed))} ${plural(removed)}`);
|
|
113
|
+
return parts.length > 0 ? parts.join(", ") : "No changes";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The waiting call's preview body, wrapped to `width`: the diff for an overwrite it could diff, the new content otherwise. */
|
|
117
|
+
export function writePreviewLines(theme: ThemeLike, preview: WriteDetails, content: string, width: number, expanded: boolean): string[] {
|
|
118
|
+
return preview.kind === "update" && preview.diff !== undefined
|
|
119
|
+
? diffPreviewLines(theme, preview.diff, width)
|
|
120
|
+
: contentPreviewLines(theme, content, width, expanded);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A finished write's result body, before the `⎿` elbow is added: the summary
|
|
125
|
+
* line, then the content preview or the diff, wrapped to `width`. `path` is
|
|
126
|
+
* the display path.
|
|
127
|
+
*/
|
|
128
|
+
export function writeResultLines(theme: ThemeLike, details: WriteDetails | undefined, path: string, content: string, width: number, expanded: boolean): string[] {
|
|
129
|
+
if (details?.kind === "update" && details.diff !== undefined) {
|
|
130
|
+
return [updateSummary(theme, details.diff), ...diffPreviewLines(theme, details.diff, width)];
|
|
131
|
+
}
|
|
132
|
+
const head = `Wrote ${theme.bold(String(countLines(content)))} lines to ${theme.bold(sanitizeDisplayText(path))}`;
|
|
133
|
+
return [head, ...contentPreviewLines(theme, content, width, expanded)];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** A write rejected without a reason: "User rejected write to <path>" and the first lines it would have written. */
|
|
137
|
+
export function rejectedWriteLines(theme: ThemeLike, path: string, content: string, width: number, expanded: boolean): string[] {
|
|
138
|
+
return [rejectedFileHead(theme, "write", path), ...contentPreviewLines(theme, content, width, expanded)];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The I/O the renderers need, injected so this module stays pure. */
|
|
142
|
+
export interface WriteRenderDeps {
|
|
143
|
+
/** The file's content: undefined when it does not exist, null when it cannot or should not be read (too big, a directory). */
|
|
144
|
+
readBefore: (path: string, cwd: string) => Promise<string | null | undefined>;
|
|
145
|
+
/** pi's display diff of two contents. */
|
|
146
|
+
diff: (before: string, after: string) => string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface WriteArgs {
|
|
150
|
+
path?: unknown;
|
|
151
|
+
file_path?: unknown;
|
|
152
|
+
content?: unknown;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The waiting call's preview, kept on the row's `context.state` across renders. */
|
|
156
|
+
interface PreviewState {
|
|
157
|
+
path: string;
|
|
158
|
+
content: string;
|
|
159
|
+
preview?: WriteDetails;
|
|
160
|
+
lines?: { width: number; expanded: boolean; lines: string[] };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const argPath = (args: WriteArgs | undefined): string | undefined => {
|
|
164
|
+
const raw = args?.path ?? args?.file_path;
|
|
165
|
+
return typeof raw === "string" && raw !== "" ? raw : undefined;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
type ResultRenderer = (result: any, options: { expanded: boolean; isPartial: boolean }, theme: any, context: any) => TuiComponent;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* renderCall/renderResult for the write tool. `fallbackResult` renders what
|
|
172
|
+
* this view does not own (an error, a rejection with a reason): the generic
|
|
173
|
+
* wrapper around pi's own result component.
|
|
174
|
+
*/
|
|
175
|
+
export function createWriteRenderers(
|
|
176
|
+
deps: WriteRenderDeps,
|
|
177
|
+
fallbackResult: ResultRenderer,
|
|
178
|
+
): {
|
|
179
|
+
renderShell: "self";
|
|
180
|
+
renderCall: (args: WriteArgs, theme: any, context: any) => TuiComponent;
|
|
181
|
+
renderResult: ResultRenderer;
|
|
182
|
+
} {
|
|
183
|
+
/** The preview for these args, starting the read of the target the first time. */
|
|
184
|
+
const previewFor = (path: string, content: string, context: any): PreviewState => {
|
|
185
|
+
const state = context.state as { ccWritePreview?: PreviewState };
|
|
186
|
+
const current = state.ccWritePreview;
|
|
187
|
+
if (current && current.path === path && current.content === content) return current;
|
|
188
|
+
const next: PreviewState = { path, content };
|
|
189
|
+
state.ccWritePreview = next;
|
|
190
|
+
const cwd = typeof context.cwd === "string" ? context.cwd : process.cwd();
|
|
191
|
+
const settle = (preview: WriteDetails) => {
|
|
192
|
+
if (state.ccWritePreview !== next) return;
|
|
193
|
+
next.preview = preview;
|
|
194
|
+
context.invalidate?.();
|
|
195
|
+
};
|
|
196
|
+
deps.readBefore(path, cwd).then(
|
|
197
|
+
(before) => settle(writeDetails(before, content, deps.diff)),
|
|
198
|
+
() => settle({ kind: "create" }),
|
|
199
|
+
);
|
|
200
|
+
return next;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
renderShell: "self",
|
|
205
|
+
renderCall(args, theme: ThemeLike, context) {
|
|
206
|
+
const path = argPath(args);
|
|
207
|
+
const header = callLine(theme, "Write", path ? summarizeArgs(path) : "", context.isPartial, context.isError, spinnerBulletColor(context));
|
|
208
|
+
const content = args?.content;
|
|
209
|
+
// The preview belongs to the call that has not run yet: once its result
|
|
210
|
+
// is in, the result carries the content (or the diff) instead.
|
|
211
|
+
const waiting = context.isPartial && context.argsComplete && path !== undefined && typeof content === "string";
|
|
212
|
+
const preview = waiting ? previewFor(path, content, context) : undefined;
|
|
213
|
+
const expanded = context.expanded === true;
|
|
214
|
+
return linesComponent((width) => {
|
|
215
|
+
if (!preview?.preview) return [header];
|
|
216
|
+
const cached = preview.lines;
|
|
217
|
+
if (cached && cached.width === width && cached.expanded === expanded) return [header, ...cached.lines];
|
|
218
|
+
const body = writePreviewLines(theme, preview.preview, preview.content, width - 2, expanded).map((line) => ` ${line}`);
|
|
219
|
+
preview.lines = { width, expanded, lines: body };
|
|
220
|
+
return [header, ...body];
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
renderResult(result, options, theme: ThemeLike, context) {
|
|
224
|
+
const args = context.args as WriteArgs | undefined;
|
|
225
|
+
const path = argPath(args);
|
|
226
|
+
const content = args?.content;
|
|
227
|
+
if (path === undefined || typeof content !== "string") return fallbackResult(result, options, theme, context);
|
|
228
|
+
const shownPath = displayToolPath(path, context.cwd);
|
|
229
|
+
if (context.isError) {
|
|
230
|
+
if (userDenialKind(textContent(stripReminderBlocks(result))) !== "stop") return fallbackResult(result, options, theme, context);
|
|
231
|
+
return linesComponent((width) => elbowIndent(rejectedWriteLines(theme, shownPath, content, width - 5, options.expanded)));
|
|
232
|
+
}
|
|
233
|
+
const details = result?.details as WriteDetails | undefined;
|
|
234
|
+
return linesComponent((width) => elbowIndent(writeResultLines(theme, details, shownPath, content, width - 5, options.expanded)));
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
* the model never sees its own timing line), rendered dim and led by the ✻ mark
|
|
9
9
|
* to match CC's TurnDurationMessage. CC shows it after every response with no
|
|
10
10
|
* threshold and defaults it on; CC_TURN_DURATION=0 opts out. The completion verb
|
|
11
|
-
* is sampled once per turn from CC's 8-verb list.
|
|
11
|
+
* is sampled once per turn from CC's 8-verb list. The line ends with CC's
|
|
12
|
+
* `· done <time>` (the entry's timestamp, line.ts formatDoneAt) and, when
|
|
13
|
+
* background shells outlive the turn, `· N shells still running`.
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
17
|
import { trackShellTasks } from "../lib/shell-tasks.ts";
|
|
16
18
|
import { dimMarkedLine } from "../lib/tui-render.ts";
|
|
17
|
-
import { TURN_MARK, turnDurationText } from "./line.ts";
|
|
19
|
+
import { formatDoneAt, TURN_MARK, turnDurationText } from "./line.ts";
|
|
18
20
|
import { TurnSpan } from "./span.ts";
|
|
19
21
|
import { pickCompletionVerb } from "./verbs.ts";
|
|
20
22
|
|
|
@@ -33,7 +35,9 @@ export default function turnDurationExtension(pi: ExtensionAPI) {
|
|
|
33
35
|
pi.registerEntryRenderer<TurnDurationData>(ENTRY_TYPE, (entry, _options, theme) => {
|
|
34
36
|
const data = entry.data;
|
|
35
37
|
if (!data) return undefined;
|
|
36
|
-
|
|
38
|
+
// The entry's own timestamp is when the turn settled: Claude Code's `· done 2:33 PM`.
|
|
39
|
+
const doneAt = formatDoneAt(new Date(entry.timestamp));
|
|
40
|
+
return dimMarkedLine(theme, TURN_MARK, turnDurationText(data.verb, data.durationMs, data.runningShells ?? 0, doneAt));
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
const span = new TurnSpan();
|
|
@@ -13,10 +13,53 @@ export const TURN_MARK = "✻";
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* `<Verb> for <duration>` — duration formatted like the spinner's elapsed
|
|
16
|
-
* clock —
|
|
17
|
-
* running`) when shells
|
|
16
|
+
* clock — then Claude Code's ` · done <time>` when the finish time is known,
|
|
17
|
+
* then its background-shell tail (`· 2 shells still running`) when shells
|
|
18
|
+
* outlive the turn.
|
|
18
19
|
*/
|
|
19
|
-
export function turnDurationText(verb: string, durationMs: number, runningShells = 0): string {
|
|
20
|
+
export function turnDurationText(verb: string, durationMs: number, runningShells = 0, doneAt?: string): string {
|
|
21
|
+
const done = doneAt ? ` · done ${doneAt}` : "";
|
|
20
22
|
const shells = runningShells > 0 ? ` · ${countNoun(runningShells, "shell")} still running` : "";
|
|
21
|
-
return `${verb} for ${formatDuration(0, durationMs)}${shells}`;
|
|
23
|
+
return `${verb} for ${formatDuration(0, durationMs)}${done}${shells}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The locale Claude Code formats times in: `LC_ALL`, then `LC_TIME`, then
|
|
28
|
+
* `LANG`, with the encoding and modifier dropped (`en_US.UTF-8` → `en-US`);
|
|
29
|
+
* undefined for `C`, `POSIX`, unset or a tag Intl rejects, which leaves the
|
|
30
|
+
* runtime's default.
|
|
31
|
+
*/
|
|
32
|
+
export function timeLocale(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
33
|
+
const raw = env.LC_ALL || env.LC_TIME || env.LANG || "";
|
|
34
|
+
if (!raw || raw === "C" || raw === "POSIX") return undefined;
|
|
35
|
+
const tag = raw.split(".")[0]?.split("@")[0]?.replaceAll("_", "-");
|
|
36
|
+
if (!tag) return undefined;
|
|
37
|
+
try {
|
|
38
|
+
new Intl.DateTimeFormat(tag);
|
|
39
|
+
return tag;
|
|
40
|
+
} catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DAY_MS = 86_400_000;
|
|
46
|
+
|
|
47
|
+
/** Midnight of `date`'s calendar day, in the runtime's local time zone. */
|
|
48
|
+
function localDay(date: Date): number {
|
|
49
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Claude Code's "done" time (its `auto` time format): the time alone for a
|
|
54
|
+
* turn that finished today (`2:33 PM`), the weekday too within the last week
|
|
55
|
+
* (`Friday 2:33 PM`), and the date as well before that (`Friday, Sep 19, 2:33
|
|
56
|
+
* PM`), in the locale's own hour cycle. Empty for an invalid date.
|
|
57
|
+
*/
|
|
58
|
+
export function formatDoneAt(at: Date, now: Date = new Date(), locale: string | undefined = timeLocale()): string {
|
|
59
|
+
if (Number.isNaN(at.getTime())) return "";
|
|
60
|
+
const days = Math.round((localDay(now) - localDay(at)) / DAY_MS);
|
|
61
|
+
const time: Intl.DateTimeFormatOptions = { hour: "numeric", minute: "2-digit" };
|
|
62
|
+
const options: Intl.DateTimeFormatOptions =
|
|
63
|
+
days === 0 ? time : days > 0 && days < 7 ? { weekday: "long", ...time } : { weekday: "long", month: "short", day: "numeric", ...time };
|
|
64
|
+
return new Intl.DateTimeFormat(locale, options).format(at);
|
|
22
65
|
}
|
|
@@ -73,12 +73,12 @@ const KEYLESS_ADVICE =
|
|
|
73
73
|
* these results are best-effort and what the user can configure instead.
|
|
74
74
|
*/
|
|
75
75
|
export const KEYLESS_NOTE =
|
|
76
|
-
"Note: these results came from Exa's free keyless endpoint (rate-limited, best-effort)
|
|
76
|
+
"Note: these results came from Exa's free keyless endpoint (rate-limited, best-effort). " +
|
|
77
77
|
`For reliable search the user can ${KEYLESS_ADVICE}`;
|
|
78
78
|
|
|
79
79
|
/** User-facing one-time notice (TUI) the first time a session searches keyless. */
|
|
80
80
|
export const KEYLESS_USER_NOTICE =
|
|
81
|
-
"web_search used Exa's free keyless endpoint (rate-limited, best-effort)
|
|
81
|
+
"web_search used Exa's free keyless endpoint (rate-limited, best-effort). " +
|
|
82
82
|
`For reliable results ${KEYLESS_ADVICE}`;
|
|
83
83
|
|
|
84
84
|
// ---------------------------------------------------------------------------
|
|
@@ -140,19 +140,16 @@ export function resolveChain(
|
|
|
140
140
|
|
|
141
141
|
function hostOf(url: string): string | undefined {
|
|
142
142
|
try {
|
|
143
|
-
return new URL(url).hostname.toLowerCase();
|
|
143
|
+
return new URL(url).hostname.toLowerCase().replace(/\.+$/, "");
|
|
144
144
|
} catch {
|
|
145
145
|
return undefined;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
function normalizeDomain(domain: string): string {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
.replace(/^https?:\/\//, "")
|
|
154
|
-
.replace(/^www\./, "")
|
|
155
|
-
.replace(/\/.*$/, "");
|
|
150
|
+
const value = domain.trim().toLowerCase();
|
|
151
|
+
const host = hostOf(/^https?:\/\//.test(value) ? value : `https://${value}`);
|
|
152
|
+
return (host ?? value).replace(/^www\./, "");
|
|
156
153
|
}
|
|
157
154
|
|
|
158
155
|
function hostMatches(host: string, domain: string): boolean {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web_search's model-facing description (pure).
|
|
3
|
+
*
|
|
4
|
+
* The short form, on frontier and workhorse models, is One Code's own: it
|
|
5
|
+
* names the backends that answer and how the domain filters apply to each.
|
|
6
|
+
* The long form, on cheap and tiny models (lib/tool-variants.ts), is the text
|
|
7
|
+
* Claude Code writes for its weakest models, addressed to the model rather
|
|
8
|
+
* than to Claude, with the backends line in place of "Web search is only
|
|
9
|
+
* available in the US".
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { DescriptionForm } from "../lib/tool-variants.ts";
|
|
13
|
+
|
|
14
|
+
export const WEB_SEARCH_SHORT_DESCRIPTION =
|
|
15
|
+
"Search the web. Returns result blocks with titles and URLs.\n\n" +
|
|
16
|
+
"- `allowed_domains` / `blocked_domains` filter results (enforced on Brave/Tavily/Exa; with provider-native search they become `site:` operators in the query, best-effort).\n" +
|
|
17
|
+
'- After answering from results, end with a "Sources:" list of the URLs you used as markdown links.\n' +
|
|
18
|
+
"- Uses the model provider's own search when it has one; otherwise a configured search API (Brave, Tavily) or, with no key, a free rate-limited endpoint — the result names which.";
|
|
19
|
+
|
|
20
|
+
export const WEB_SEARCH_LONG_DESCRIPTION = `
|
|
21
|
+
- Allows you to search the web and use the results to inform responses
|
|
22
|
+
- Provides up-to-date information for current events and recent data
|
|
23
|
+
- Returns search result information formatted as search result blocks, including links as markdown hyperlinks
|
|
24
|
+
- Use this tool for accessing information beyond your knowledge cutoff
|
|
25
|
+
- Searches are performed automatically within a single API call
|
|
26
|
+
|
|
27
|
+
CRITICAL REQUIREMENT - You MUST follow this:
|
|
28
|
+
- After answering the user's question, you MUST include a "Sources:" section at the end of your response
|
|
29
|
+
- In the Sources section, list all relevant URLs from the search results as markdown hyperlinks: [Title](URL)
|
|
30
|
+
- This is MANDATORY - never skip including sources in your response
|
|
31
|
+
- Example format:
|
|
32
|
+
|
|
33
|
+
[Your answer here]
|
|
34
|
+
|
|
35
|
+
Sources:
|
|
36
|
+
- [Source Title 1](https://example.com/1)
|
|
37
|
+
- [Source Title 2](https://example.com/2)
|
|
38
|
+
|
|
39
|
+
Usage notes:
|
|
40
|
+
- Domain filtering is supported to include or block specific websites
|
|
41
|
+
- Uses the model provider's own search when it has one; otherwise a configured search API (Brave, Tavily) or, with no key, a free rate-limited endpoint — the result names which
|
|
42
|
+
|
|
43
|
+
IMPORTANT - Use the correct year in search queries:
|
|
44
|
+
- The current month is (provided in the conversation below). You MUST use this year when searching for recent information, documentation, or current events.
|
|
45
|
+
- Example: If the user asks for "latest React docs", search for "React documentation" with the current year, NOT last year
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export function webSearchDescription(form: DescriptionForm): string {
|
|
49
|
+
return form === "long" ? WEB_SEARCH_LONG_DESCRIPTION : WEB_SEARCH_SHORT_DESCRIPTION;
|
|
50
|
+
}
|