one-code-extension 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +40 -28
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +26 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +124 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +642 -0
- package/extensions/background/index.ts +614 -248
- package/extensions/background/loop-fire.ts +173 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +56 -0
- package/extensions/background/wakeup.ts +178 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +66 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +250 -78
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/cache-marks/index.ts +42 -0
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +436 -63
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +219 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/budget.ts +16 -2
- package/extensions/context-budget/index.ts +35 -15
- package/extensions/context-management/index.ts +36 -28
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +141 -25
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +420 -42
- package/extensions/file-tracker/replay.ts +137 -24
- package/extensions/file-tracker/restore.ts +66 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +45 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +209 -58
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/prompt-context.ts +59 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +26 -12
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/ledger/index.ts +46 -0
- package/extensions/ledger/wire.ts +154 -0
- package/extensions/lib/agent-cron.ts +146 -0
- package/extensions/lib/agent-loader.ts +18 -3
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-auth.ts +98 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +52 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +97 -0
- package/extensions/lib/covered-messages.ts +23 -0
- package/extensions/lib/deferred.ts +359 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/ledger/fold-memo.ts +49 -0
- package/extensions/lib/ledger/fold.ts +265 -0
- package/extensions/lib/ledger/layout.ts +72 -0
- package/extensions/lib/ledger/plan.ts +102 -0
- package/extensions/lib/ledger/records.ts +276 -0
- package/extensions/lib/ledger/state.ts +47 -0
- package/extensions/lib/ledger/validate.ts +141 -0
- package/extensions/lib/ledger/wire.ts +632 -0
- package/extensions/lib/local-command.ts +71 -19
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +178 -0
- package/extensions/lib/model-catalog/models-dev.json +18533 -0
- package/extensions/lib/model-catalog/openrouter.json +5050 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +357 -73
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +38 -2
- package/extensions/lib/plan-mode-channels.ts +42 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/previous-mark.ts +101 -0
- package/extensions/lib/process-state.ts +48 -0
- package/extensions/lib/process-tree.ts +132 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +370 -358
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +547 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +24 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +112 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +129 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +159 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +76 -12
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +90 -43
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +220 -121
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +164 -73
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/model-default/index.ts +86 -19
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1426 -398
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +11 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +276 -92
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/run-state.ts +26 -0
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +64 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +14 -7
- package/extensions/recap/index.ts +73 -71
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/announce.ts +30 -0
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +469 -132
- package/extensions/skill/invoke.ts +118 -75
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +146 -31
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +82 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1594 -852
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +5 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/parent-mode.ts +50 -0
- package/extensions/subagents/pending-claim.ts +18 -1
- package/extensions/subagents/runner.ts +135 -80
- package/extensions/subagents/runs.ts +44 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +165 -47
- package/extensions/system-prompt/template.ts +34 -51
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +204 -70
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/index.ts +366 -144
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +108 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +157 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +33 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +162 -44
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +21 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- package/extensions/lib/model-facts.ts +0 -162
- package/extensions/tool-search/announce.ts +0 -42
|
@@ -8,30 +8,35 @@
|
|
|
8
8
|
* most one recap per user turn (firedSinceTurn), matching CC's
|
|
9
9
|
* hasSummarySinceLastUserTurn guard.
|
|
10
10
|
*
|
|
11
|
-
* Generation
|
|
12
|
-
*
|
|
13
|
-
* prompt,
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* Generation is Claude Code 2.1.261's: the session's last provider request
|
|
12
|
+
* replayed unchanged on the session model, the reply that answered it, then
|
|
13
|
+
* the verbatim prompt, so the call reads the cache the session wrote and
|
|
14
|
+
* writes the reply for the user's next turn (lib/request-replay.ts; the
|
|
15
|
+
* compaction extension publishes the capture). Without a capture for the
|
|
16
|
+
* session model, or on a provider API the replay does not cover, it falls back
|
|
17
|
+
* to a small standalone call on a cheap same-containment model
|
|
18
|
+
* (pickEconomicalContainedModel): the current branch's last 30 messages (read
|
|
19
|
+
* from the session when the recap runs) and name-only stubs of
|
|
20
|
+
* the active tools (some providers reject a history carrying tool_use blocks
|
|
21
|
+
* with no tools declared). The result is a display-only entry (appendEntry,
|
|
16
22
|
* not in LLM context). Best-effort throughout: any failure just shows nothing.
|
|
17
23
|
*
|
|
18
|
-
* Deviations from CC, logged in docs/decisions: the session-memory block is
|
|
19
|
-
* omitted (decoupling)
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the model answering in text rather than calling one. A failed recap does not
|
|
23
|
-
* retry until the next turn (one attempt per turn, success or not). CC_RECAP=0
|
|
24
|
-
* opts out; CC_RECAP_IDLE_MS overrides the 5-minute delay.
|
|
24
|
+
* Deviations from CC, logged in working-docs/decisions: the session-memory block is
|
|
25
|
+
* omitted (decoupling). A failed recap does not retry until the next turn (one
|
|
26
|
+
* attempt per turn, success or not). CC_RECAP=0 opts out; CC_RECAP_IDLE_MS
|
|
27
|
+
* overrides the 5-minute delay.
|
|
25
28
|
*/
|
|
26
29
|
|
|
27
30
|
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
28
31
|
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
29
|
-
import { completeSimple } from "@earendil-works/pi-ai/compat";
|
|
30
32
|
import { convertToLlm, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
31
|
-
import {
|
|
33
|
+
import { followLastExchange, replaySideCall } from "../lib/replay-call.ts";
|
|
32
34
|
import { recordUsage } from "../lib/usage-bus.ts";
|
|
33
35
|
import { pickEconomicalContainedModel } from "../lib/model-tier.ts";
|
|
34
|
-
import {
|
|
36
|
+
import { CONVERSATION_CACHE_PLACEMENT } from "../lib/side-call-cache.ts";
|
|
37
|
+
import { releaseSideCalls, runSideCall } from "../lib/side-call-run.ts";
|
|
38
|
+
import { answerText, branchMessages, stripImageBlocks, toolStubs, withoutSystemMessages } from "../lib/side-call.ts";
|
|
39
|
+
import { unrefTimers } from "../lib/timer-ops.ts";
|
|
35
40
|
import { dimMarkedLine } from "../lib/tui-render.ts";
|
|
36
41
|
import { RECAP_PROMPT, recapLine, recentForRecap, REFERENCE_MARK } from "./prompt.ts";
|
|
37
42
|
import { RecapScheduler } from "./scheduler.ts";
|
|
@@ -60,12 +65,8 @@ export default function recapExtension(pi: ExtensionAPI) {
|
|
|
60
65
|
return dimMarkedLine(theme, REFERENCE_MARK, recapLine(content));
|
|
61
66
|
});
|
|
62
67
|
|
|
63
|
-
// The
|
|
64
|
-
|
|
65
|
-
let capturedMessages: AgentMessage[] | undefined;
|
|
66
|
-
pi.on("context", (event) => {
|
|
67
|
-
capturedMessages = event.messages;
|
|
68
|
-
});
|
|
68
|
+
// The session's last request as it went on the wire, and the reply that answered it.
|
|
69
|
+
const exchange = followLastExchange(pi);
|
|
69
70
|
|
|
70
71
|
let lastCtx: ExtensionContext | undefined;
|
|
71
72
|
let inFlight: AbortController | undefined;
|
|
@@ -77,66 +78,67 @@ export default function recapExtension(pi: ExtensionAPI) {
|
|
|
77
78
|
};
|
|
78
79
|
|
|
79
80
|
const scheduler = new RecapScheduler(
|
|
80
|
-
|
|
81
|
-
set: (cb, ms) => {
|
|
82
|
-
const handle = setTimeout(cb, ms);
|
|
83
|
-
handle.unref?.();
|
|
84
|
-
return handle;
|
|
85
|
-
},
|
|
86
|
-
clear: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),
|
|
87
|
-
},
|
|
81
|
+
unrefTimers,
|
|
88
82
|
idleMs,
|
|
89
83
|
() => process.env.CC_RECAP !== "0",
|
|
90
84
|
() => void generate(),
|
|
91
85
|
);
|
|
92
86
|
|
|
87
|
+
/** The fallback: a small standalone call on a cheap same-containment model. */
|
|
88
|
+
async function standaloneRecap(ctx: ExtensionContext, model: Model<Api>, messages: AgentMessage[], signal: AbortSignal): Promise<string> {
|
|
89
|
+
const choice = pickEconomicalContainedModel(ctx.modelRegistry.getAvailable(), model);
|
|
90
|
+
if (!choice) return "";
|
|
91
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(choice.model);
|
|
92
|
+
if (!auth.ok) return "";
|
|
93
|
+
// Name-only stubs of the active tools, so a history carrying tool_use
|
|
94
|
+
// blocks stays valid on strict providers (see the header note) without
|
|
95
|
+
// shipping every full schema (~6k tokens) to a call that must answer
|
|
96
|
+
// in text.
|
|
97
|
+
const tools = toolStubs(pi.getActiveTools(), STUB_REASON);
|
|
98
|
+
|
|
99
|
+
const recent = recentForRecap(withoutSystemMessages(messages));
|
|
100
|
+
// The cheap reader may be a text-only model, and the recap does not need
|
|
101
|
+
// images — strip them so a pasted image or one a Read returned cannot break
|
|
102
|
+
// the call (working-docs/decisions/model-policy.md).
|
|
103
|
+
const recapMessages = [...stripImageBlocks(convertToLlm(recent)), { role: "user" as const, content: RECAP_PROMPT, timestamp: Date.now() }];
|
|
104
|
+
// Thinking off unless the model cannot disable it; withReasoningFallback
|
|
105
|
+
// sends a level up front for catalog-marked models and retries on the 400
|
|
106
|
+
// for the rest. Fires on a 5-min idle timer, so no cross-call memo.
|
|
107
|
+
const result = await runSideCall({
|
|
108
|
+
kind: "recap",
|
|
109
|
+
model: choice.model,
|
|
110
|
+
auth,
|
|
111
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
112
|
+
context: { systemPrompt: "", messages: recapMessages, tools },
|
|
113
|
+
signal,
|
|
114
|
+
timeoutMs: RECAP_TIMEOUT_MS,
|
|
115
|
+
maxTokens: RECAP_MAX_TOKENS,
|
|
116
|
+
cache: CONVERSATION_CACHE_PLACEMENT,
|
|
117
|
+
onUsage: (usage) => recordUsage(pi, "recap", usage),
|
|
118
|
+
});
|
|
119
|
+
return answerText(result.content);
|
|
120
|
+
}
|
|
121
|
+
|
|
93
122
|
async function generate() {
|
|
94
123
|
const ctx = lastCtx;
|
|
95
|
-
|
|
96
|
-
|
|
124
|
+
if (!ctx?.hasUI || scheduler.hasFiredSinceTurn) return;
|
|
125
|
+
// The current branch, read now: a /tree or a compaction since the last
|
|
126
|
+
// request changed it, and pi fires no `context` event for either.
|
|
127
|
+
const messages = branchMessages(ctx);
|
|
128
|
+
if (!messages.length) return;
|
|
97
129
|
abortInFlight();
|
|
98
130
|
const controller = new AbortController();
|
|
99
131
|
inFlight = controller;
|
|
100
132
|
try {
|
|
101
133
|
const model = ctx.model;
|
|
102
134
|
if (!model) return;
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Name-only stubs of the active tools, so a history carrying tool_use
|
|
110
|
-
// blocks stays valid on strict providers (see the header note) without
|
|
111
|
-
// shipping every full schema (~6k tokens) to a call that must answer
|
|
112
|
-
// in text.
|
|
113
|
-
const tools = toolStubs(pi.getActiveTools(), STUB_REASON);
|
|
114
|
-
|
|
115
|
-
const recent = recentForRecap(messages);
|
|
116
|
-
// The cheap reader may be a text-only model, and the recap does not need
|
|
117
|
-
// images — strip them so a pasted image or one a Read returned cannot break
|
|
118
|
-
// the call (docs/decisions/model-policy.md).
|
|
119
|
-
const recapMessages = [...stripImageBlocks(convertToLlm(recent)), { role: "user" as const, content: RECAP_PROMPT, timestamp: Date.now() }];
|
|
120
|
-
// Thinking off unless the model cannot disable it; withReasoningFallback
|
|
121
|
-
// sends a level up front for catalog-marked models and retries on the 400
|
|
122
|
-
// for the rest. Fires on a 5-min idle timer, so no cross-call memo.
|
|
123
|
-
const result = await withReasoningFallback(choice.model, (reasoning) => {
|
|
124
|
-
const timeout = AbortSignal.timeout(RECAP_TIMEOUT_MS);
|
|
125
|
-
return completeSimple(
|
|
126
|
-
baseUrl ? ({ ...choice.model, baseUrl } as Model<Api>) : choice.model,
|
|
127
|
-
{ systemPrompt: "", messages: recapMessages, tools },
|
|
128
|
-
{
|
|
129
|
-
apiKey: auth.apiKey,
|
|
130
|
-
headers: auth.headers,
|
|
131
|
-
env: auth.env,
|
|
132
|
-
signal: AbortSignal.any([controller.signal, timeout]),
|
|
133
|
-
maxTokens: RECAP_MAX_TOKENS,
|
|
134
|
-
...(reasoning ? { reasoning } : {}),
|
|
135
|
-
},
|
|
136
|
-
);
|
|
137
|
-
}, undefined, (usage) => recordUsage(pi, "recap", usage));
|
|
135
|
+
const replayed = await replaySideCall(ctx, model as Model<Api>, exchange, RECAP_PROMPT, {
|
|
136
|
+
signal: controller.signal,
|
|
137
|
+
timeoutMs: RECAP_TIMEOUT_MS,
|
|
138
|
+
onUsage: (usage) => recordUsage(pi, "recap", usage),
|
|
139
|
+
});
|
|
140
|
+
const content = replayed ?? (await standaloneRecap(ctx, model as Model<Api>, messages, controller.signal));
|
|
138
141
|
if (controller.signal.aborted) return;
|
|
139
|
-
const content = answerText(result.content);
|
|
140
142
|
if (!content) return;
|
|
141
143
|
scheduler.markFired();
|
|
142
144
|
pi.appendEntry<RecapData>(ENTRY_TYPE, { content });
|
|
@@ -155,11 +157,10 @@ export default function recapExtension(pi: ExtensionAPI) {
|
|
|
155
157
|
pi.on("session_start", (_event, ctx) => {
|
|
156
158
|
lastCtx = ctx;
|
|
157
159
|
// A new session (including after /clear): drop any timer armed by the
|
|
158
|
-
// previous session
|
|
159
|
-
//
|
|
160
|
+
// previous session, so a stale recap can never surface against the
|
|
161
|
+
// fresh conversation.
|
|
160
162
|
scheduler.reset();
|
|
161
163
|
abortInFlight();
|
|
162
|
-
capturedMessages = undefined;
|
|
163
164
|
if (!ctx.hasUI) return;
|
|
164
165
|
// Reset the idle clock on any keystroke — the closest signal to CC's
|
|
165
166
|
// terminal-blur trigger pi exposes. Registered ONCE (session_start fires
|
|
@@ -194,8 +195,9 @@ export default function recapExtension(pi: ExtensionAPI) {
|
|
|
194
195
|
scheduler.turnEnded();
|
|
195
196
|
});
|
|
196
197
|
|
|
197
|
-
pi.on("session_shutdown", () => {
|
|
198
|
+
pi.on("session_shutdown", (_event, ctx) => {
|
|
198
199
|
scheduler.reset();
|
|
199
200
|
abortInFlight();
|
|
201
|
+
releaseSideCalls(ctx.sessionManager.getSessionId());
|
|
200
202
|
});
|
|
201
203
|
}
|
|
@@ -18,9 +18,9 @@ export const REFERENCE_MARK = "※";
|
|
|
18
18
|
*/
|
|
19
19
|
export const RECENT_MESSAGE_WINDOW = 30;
|
|
20
20
|
|
|
21
|
-
/** The away-summary instruction, verbatim from
|
|
21
|
+
/** The away-summary instruction, verbatim from Claude Code 2.1.261's request (no memory block). */
|
|
22
22
|
export const RECAP_PROMPT =
|
|
23
|
-
"The user stepped away and is coming back.
|
|
23
|
+
"The user stepped away and is coming back. Recap in under 40 words, 1-2 plain sentences, no markdown. Lead with the overall goal and current task, then the one next action. Skip root-cause narrative, fix internals, secondary to-dos, and em-dash tangents.";
|
|
24
24
|
|
|
25
25
|
/** The transcript text after the ※ mark, matching CC's shipped "recap: …" line. */
|
|
26
26
|
export function recapLine(content: string): string {
|
|
@@ -14,11 +14,9 @@
|
|
|
14
14
|
* async model call it triggers stays in the extension.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
clear(handle: unknown): void;
|
|
21
|
-
}
|
|
17
|
+
import type { TimerOps } from "../lib/timer-ops.ts";
|
|
18
|
+
|
|
19
|
+
export type { TimerOps };
|
|
22
20
|
|
|
23
21
|
export class RecapScheduler {
|
|
24
22
|
private turnRunning = false;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi's grep tool prints one context window per match, so with `context` set,
|
|
3
|
+
* nearby matches repeat the lines their windows share (a line once as context,
|
|
4
|
+
* again as context, again as the next match). Merged here (pure): each file
|
|
5
|
+
* line appears once, in the order first seen, and a line that is a match in
|
|
6
|
+
* any window shows as the match. Rows are `path:N: text` (match) and
|
|
7
|
+
* `path-N- text` (context); two rows are the same line only when path, number
|
|
8
|
+
* and text all agree, so lines of different files are never merged. Anything
|
|
9
|
+
* else (pi's notices) passes through.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const MATCH_ROW = /^(.+?):(\d+): (.*)$/s;
|
|
13
|
+
const CONTEXT_ROW = /^(.+?)-(\d+)- (.*)$/s;
|
|
14
|
+
|
|
15
|
+
export function mergeGrepWindows(text: string): string {
|
|
16
|
+
const out: string[] = [];
|
|
17
|
+
/** Each line's context form → its index in `out`. */
|
|
18
|
+
const at = new Map<string, number>();
|
|
19
|
+
for (const line of text.split("\n")) {
|
|
20
|
+
// The separator that comes first decides: a row's text may itself hold
|
|
21
|
+
// `:N: ` or `-N- ` (`a.ts-12- k:3: v` is a context row).
|
|
22
|
+
const asMatch = MATCH_ROW.exec(line);
|
|
23
|
+
const asContext = CONTEXT_ROW.exec(line);
|
|
24
|
+
const match = asMatch && (!asContext || asMatch[1].length < asContext[1].length) ? asMatch : null;
|
|
25
|
+
const key = match ? `${match[1]}-${match[2]}- ${match[3]}` : asContext ? line : undefined;
|
|
26
|
+
if (key === undefined) {
|
|
27
|
+
out.push(line);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const index = at.get(key);
|
|
31
|
+
if (index === undefined) {
|
|
32
|
+
at.set(key, out.length);
|
|
33
|
+
out.push(line);
|
|
34
|
+
} else if (match) {
|
|
35
|
+
out[index] = line;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out.join("\n");
|
|
39
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code's "send now" chord, `ctrl+x ctrl+s` (its `chat:sendNow` action),
|
|
3
|
+
* read from raw terminal input. Pure: the extension feeds it every key.
|
|
4
|
+
*
|
|
5
|
+
* Keys arrive in any of three encodings: the legacy control byte (`\x18`,
|
|
6
|
+
* `\x13`), the kitty keyboard protocol's CSI u form, which pi-tui turns on
|
|
7
|
+
* with flags 7 (disambiguate, event types, alternate keys) where the terminal
|
|
8
|
+
* supports it, or xterm's modifyOtherKeys form (`\x1b[27;5;120~`), which
|
|
9
|
+
* pi-tui turns on otherwise and tmux sends with `extended-keys on`.
|
|
10
|
+
* `lib/key-input.ts` names all three alike. With event types on, a release
|
|
11
|
+
* follows every press, so releases never change the chord's state.
|
|
12
|
+
*
|
|
13
|
+
* pi binds `ctrl+x` alone to "copy the last assistant message". The chord
|
|
14
|
+
* takes a `ctrl+x` only while send now applies (a turn runs with something to
|
|
15
|
+
* send), where it is the chord's prefix as in Claude Code: one the chord does
|
|
16
|
+
* not complete (another key, or the extension's timeout) is dropped. At every
|
|
17
|
+
* other time `ctrl+x` reaches pi and copies.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { isKeyRelease, keyId } from "../lib/key-input.ts";
|
|
21
|
+
|
|
22
|
+
export type ChordKey = "ctrl+x" | "ctrl+s" | "release" | "other";
|
|
23
|
+
|
|
24
|
+
export function classifyKey(data: string): ChordKey {
|
|
25
|
+
if (isKeyRelease(data)) return "release";
|
|
26
|
+
const id = keyId(data);
|
|
27
|
+
if (id === "ctrl+x" || id === "ctrl+s") return id;
|
|
28
|
+
return "other";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What the extension does with a key: let it through, hold it (a `ctrl+x`
|
|
33
|
+
* that may start the chord, consumed), or send now (the `ctrl+s`, consumed).
|
|
34
|
+
*/
|
|
35
|
+
export type ChordAction = "pass" | "hold" | "send";
|
|
36
|
+
|
|
37
|
+
export class SendNowChord {
|
|
38
|
+
private armed = false;
|
|
39
|
+
|
|
40
|
+
/** Feed one key; `active` is whether send now applies (a turn is running with something to send). */
|
|
41
|
+
feed(data: string, active: boolean): ChordAction {
|
|
42
|
+
const key = classifyKey(data);
|
|
43
|
+
if (key === "release") return "pass";
|
|
44
|
+
const armed = this.armed;
|
|
45
|
+
this.armed = false;
|
|
46
|
+
// A second ctrl+x, or the kitty protocol's auto-repeat of a held one, keeps the chord armed.
|
|
47
|
+
if (key === "ctrl+x" && active) {
|
|
48
|
+
this.armed = true;
|
|
49
|
+
return "hold";
|
|
50
|
+
}
|
|
51
|
+
if (armed && key === "ctrl+s" && active) return "send";
|
|
52
|
+
return "pass";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The chord timed out: its `ctrl+x` is dropped. */
|
|
56
|
+
expire(): void {
|
|
57
|
+
this.armed = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The hint under the queued messages, Claude Code's wording. */
|
|
62
|
+
export const SEND_NOW_HINT = "ctrl+x ctrl+s to send now";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* send-now extension — Claude Code's "send now" for messages queued mid-turn.
|
|
3
|
+
*
|
|
4
|
+
* A message typed while a turn runs waits in pi's queue until the current
|
|
5
|
+
* response and its tool calls finish. `ctrl+x ctrl+s` (chord.ts) delivers it
|
|
6
|
+
* at once: the running turn is aborted, which makes pi move every queued
|
|
7
|
+
* message and the draft back into the editor (its abort handler), and once the
|
|
8
|
+
* session is idle that text is sent as the next prompt (a pasted image is its
|
|
9
|
+
* file path in the text, so nothing is lost). Running tools are cancelled, as in Claude Code before 2.1.283, which moves
|
|
10
|
+
* them to the background instead. A dim `ctrl+x ctrl+s to send now` line sits
|
|
11
|
+
* above the editor while anything is queued.
|
|
12
|
+
*
|
|
13
|
+
* pi binds `ctrl+x` alone to copy; while send now applies, `ctrl+x` is only the
|
|
14
|
+
* chord's prefix (chord.ts), and one the chord does not complete (another key,
|
|
15
|
+
* or CHORD_TIMEOUT_MS without one) is dropped. Only public extension API.
|
|
16
|
+
*
|
|
17
|
+
* pi exposes no read of its queue to extensions, only `hasPendingMessages()`,
|
|
18
|
+
* so the hint asks on every render rather than tracking queue events (a
|
|
19
|
+
* dequeue with alt+up fires none). TUI only.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
23
|
+
import { SEND_NOW_CHANNEL } from "../lib/send-now-channel.ts";
|
|
24
|
+
import { safeThemePaint, truncateLine } from "../lib/tui-render.ts";
|
|
25
|
+
import { SEND_NOW_HINT, SendNowChord } from "./chord.ts";
|
|
26
|
+
|
|
27
|
+
/** How long send now waits for the aborted turn to settle before giving up. */
|
|
28
|
+
const IDLE_WAIT_MS = 10_000;
|
|
29
|
+
/** How long a held `ctrl+x` waits for its `ctrl+s` before the chord is dropped. */
|
|
30
|
+
const CHORD_TIMEOUT_MS = 1_000;
|
|
31
|
+
|
|
32
|
+
export default function sendNowExtension(pi: ExtensionAPI) {
|
|
33
|
+
let unsubscribe: (() => void) | undefined;
|
|
34
|
+
// Bumped on every session start and shutdown, so a send now still waiting
|
|
35
|
+
// for the aborted turn never acts on a replaced or closed session.
|
|
36
|
+
let epoch = 0;
|
|
37
|
+
/** Resolves a send now waiting for the aborted turn to settle. */
|
|
38
|
+
let onSettled: (() => void) | undefined;
|
|
39
|
+
const wakeSettled = () => {
|
|
40
|
+
const wake = onSettled;
|
|
41
|
+
onSettled = undefined;
|
|
42
|
+
wake?.();
|
|
43
|
+
};
|
|
44
|
+
pi.on("agent_settled", wakeSettled);
|
|
45
|
+
|
|
46
|
+
pi.on("session_shutdown", () => {
|
|
47
|
+
epoch++;
|
|
48
|
+
wakeSettled();
|
|
49
|
+
unsubscribe?.();
|
|
50
|
+
unsubscribe = undefined;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
pi.on("session_start", (_event, ctx) => {
|
|
54
|
+
unsubscribe?.();
|
|
55
|
+
unsubscribe = undefined;
|
|
56
|
+
const session = ++epoch;
|
|
57
|
+
const current = () => epoch === session;
|
|
58
|
+
if (!ctx.hasUI || ctx.mode !== "tui") return;
|
|
59
|
+
|
|
60
|
+
const queued = () => !ctx.isIdle() && ctx.hasPendingMessages();
|
|
61
|
+
// Claude Code sends the draft too, so a draft alone is enough to send now.
|
|
62
|
+
const active = () => !ctx.isIdle() && (ctx.hasPendingMessages() || ctx.ui.getEditorText().trim() !== "");
|
|
63
|
+
|
|
64
|
+
let sending = false;
|
|
65
|
+
const sendNow = async (turnCtx: ExtensionContext) => {
|
|
66
|
+
if (sending) return;
|
|
67
|
+
sending = true;
|
|
68
|
+
try {
|
|
69
|
+
pi.events.emit(SEND_NOW_CHANNEL, {});
|
|
70
|
+
turnCtx.abort();
|
|
71
|
+
if (!turnCtx.isIdle()) {
|
|
72
|
+
await new Promise<void>((resolve) => {
|
|
73
|
+
const fallback = setTimeout(resolve, IDLE_WAIT_MS);
|
|
74
|
+
fallback.unref?.();
|
|
75
|
+
onSettled = () => {
|
|
76
|
+
clearTimeout(fallback);
|
|
77
|
+
resolve();
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!current() || !turnCtx.isIdle()) return;
|
|
82
|
+
const text = turnCtx.ui.getEditorText().trim();
|
|
83
|
+
if (!text) return;
|
|
84
|
+
turnCtx.ui.setEditorText("");
|
|
85
|
+
// followUp: an async UserPromptSubmit hook runs before pi's busy
|
|
86
|
+
// check, and on pi 1.2.0 a run starting meanwhile would reject a
|
|
87
|
+
// bare send after the editor was cleared.
|
|
88
|
+
pi.sendUserMessage(text, { deliverAs: "followUp" });
|
|
89
|
+
} finally {
|
|
90
|
+
sending = false;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const chord = new SendNowChord();
|
|
95
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
96
|
+
const stopUnsubscribe = ctx.ui.onTerminalInput((data) => {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
switch (chord.feed(data, active())) {
|
|
99
|
+
case "hold":
|
|
100
|
+
timer = setTimeout(() => chord.expire(), CHORD_TIMEOUT_MS);
|
|
101
|
+
timer.unref?.();
|
|
102
|
+
return { consume: true };
|
|
103
|
+
case "send":
|
|
104
|
+
void sendNow(ctx);
|
|
105
|
+
return { consume: true };
|
|
106
|
+
default:
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
unsubscribe = () => {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
stopUnsubscribe();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
ctx.ui.setWidget("send-now", (_tui, theme) => {
|
|
116
|
+
const paint = safeThemePaint(theme);
|
|
117
|
+
let cache: { key: string; lines: string[] } | undefined;
|
|
118
|
+
return {
|
|
119
|
+
render: (width: number) => {
|
|
120
|
+
const shown = queued();
|
|
121
|
+
const key = `${width}:${shown}`;
|
|
122
|
+
if (cache?.key === key) return cache.lines;
|
|
123
|
+
const lines = shown ? [truncateLine(paint("dim", ` ↳ ${SEND_NOW_HINT}`), width)] : [];
|
|
124
|
+
cache = { key, lines };
|
|
125
|
+
return lines;
|
|
126
|
+
},
|
|
127
|
+
invalidate: () => {
|
|
128
|
+
cache = undefined;
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
@@ -27,6 +27,7 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
|
|
|
27
27
|
import { withReasoningFallback } from "../lib/model-policy.ts";
|
|
28
28
|
import { pickEconomicalContainedModel } from "../lib/model-tier.ts";
|
|
29
29
|
import { sessionOutlivesTurn } from "../lib/notifications.ts";
|
|
30
|
+
import { isUserRun } from "../lib/prompt-options.ts";
|
|
30
31
|
import { recordUsage } from "../lib/usage-bus.ts";
|
|
31
32
|
import { firstUserText, isNameableText, parseTitle, SESSION_TITLE_PROMPT, sessionTitleInput } from "./title.ts";
|
|
32
33
|
|
|
@@ -61,6 +62,8 @@ export default function sessionTitleExtension(pi: ExtensionAPI) {
|
|
|
61
62
|
// message never changes, so it is examined once: a first message that is
|
|
62
63
|
// not prose (a breadcrumb, a slash command sent as text) leaves the
|
|
63
64
|
// session unnamed rather than re-scanning the entries every turn.
|
|
65
|
+
// A run an extension opened (pi 1.2.0) carries no prompt: wait for the user's.
|
|
66
|
+
if (!isUserRun(event)) return;
|
|
64
67
|
attempted = true;
|
|
65
68
|
const text = firstUserText(ctx.sessionManager.getEntries() as never) ?? event.prompt;
|
|
66
69
|
if (!isNameableText(text)) return;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { LOCAL_COMMAND_TAG_PREFIXES } from "../lib/local-command.ts";
|
|
16
16
|
import { customMessageText } from "../lib/tui-render.ts";
|
|
17
|
+
import { sanitizeTitle } from "../lib/terminal-text.ts";
|
|
17
18
|
|
|
18
19
|
/** CC's naming instruction (system prompt), verbatim from the 2.1.278 capture. */
|
|
19
20
|
export const SESSION_TITLE_PROMPT =
|
|
@@ -86,7 +87,9 @@ function titleFromJson(text: string): string | undefined {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
function normalizeTitle(raw: string): string | undefined {
|
|
89
|
-
|
|
90
|
+
// The title becomes the terminal title inside pi's own OSC 0: an embedded
|
|
91
|
+
// BEL or ESC would end that sequence and run the rest as terminal output.
|
|
92
|
+
const title = sanitizeTitle(raw);
|
|
90
93
|
if (!title || title.length > MAX_TITLE_LENGTH) return undefined;
|
|
91
94
|
// CC: "Capitalize the first letter of the title."
|
|
92
95
|
return title[0].toUpperCase() + title.slice(1);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to tell the model when the skill set changes (pure).
|
|
3
|
+
*
|
|
4
|
+
* The skills listing is a `first-prepend` reminder on message 1, so once a
|
|
5
|
+
* request has gone out it is the record every later request in the epoch
|
|
6
|
+
* replays (extensions/ledger); rewriting it would also break the binding of
|
|
7
|
+
* every earlier signed thinking block (findings §46). As in Claude Code, a
|
|
8
|
+
* skill that becomes listable later is announced on the request's tail under
|
|
9
|
+
* the listing's own header, and a skill that leaves the listing is not
|
|
10
|
+
* announced: the skill tool refuses it by name. A compaction opens an epoch
|
|
11
|
+
* and re-lists every skill.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { skillListingVisibility } from "../lib/skill-overrides.ts";
|
|
15
|
+
import type { ListedSkill } from "./listing.ts";
|
|
16
|
+
|
|
17
|
+
/** The header Claude Code puts over its skills listing, initial or later. */
|
|
18
|
+
export const SKILL_LISTING_HEADER = "The following skills are available for use with the Skill tool:";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The names the listing shows. `readable` is the read-the-file framing for a
|
|
22
|
+
* session without the skill tool, which also leaves out plugin commands and
|
|
23
|
+
* skills without a file (listing.ts `readableSkillListing`).
|
|
24
|
+
*/
|
|
25
|
+
export function listableSkillNames(skills: ReadonlyArray<ListedSkill & { path?: string; kind?: string }>, readable: boolean): string[] {
|
|
26
|
+
return skills
|
|
27
|
+
.filter((skill) => !skill.disableModelInvocation && skillListingVisibility(skill.state) !== "hidden")
|
|
28
|
+
.filter((skill) => !readable || (!skill.kind && skill.path))
|
|
29
|
+
.map((skill) => skill.name);
|
|
30
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which `/code-review` body a session gets (pure apart from reading the cell
|
|
3
|
+
* files). Claude Code no longer has one body: it picks a "cell" by the model
|
|
4
|
+
* and the effort level, from one table per model family. One Code keys the
|
|
5
|
+
* same choice by prompt tier:
|
|
6
|
+
*
|
|
7
|
+
* - frontier takes Claude Code's default table, the one its Opus 5.5,
|
|
8
|
+
* Sonnet 5.5 and Fable sessions use (minimal prompt at medium, inline
|
|
9
|
+
* angles at high and xhigh, a verified fan-out at max);
|
|
10
|
+
* - workhorse, cheap and tiny take its Sonnet 5 table: the long, explicit
|
|
11
|
+
* recipe (finder angles fanned out over the Agent tool, a verify vote)
|
|
12
|
+
* written for the one model below that line Claude Code tunes a table for.
|
|
13
|
+
* Claude Code's default table reaches its older models only because they
|
|
14
|
+
* have no table of their own; its medium cell leaves the whole review to
|
|
15
|
+
* the model's judgment, which the lower tiers' registers exist to avoid.
|
|
16
|
+
*
|
|
17
|
+
* The effort is the level the user typed first (`/code-review high 123`),
|
|
18
|
+
* else the session's (`/effort`). Claude Code runs Opus 5.5's medium cell as
|
|
19
|
+
* a background fork; here every cell runs in the session's own turn.
|
|
20
|
+
*
|
|
21
|
+
* The cells are Claude Code's text in `skills/code-review/cells/`; the minimal
|
|
22
|
+
* prompt reports through the JSON array the other cells use, since One Code
|
|
23
|
+
* has no findings tool. Every cell is followed by One Code's "Arguments and
|
|
24
|
+
* flags" section.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { readFileSync } from "node:fs";
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
import type { PromptTier } from "../lib/model-tier.ts";
|
|
30
|
+
|
|
31
|
+
export const REVIEW_EFFORTS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
32
|
+
export type ReviewEffort = (typeof REVIEW_EFFORTS)[number];
|
|
33
|
+
|
|
34
|
+
/** Claude Code's default table (`cells/<name>.md`). */
|
|
35
|
+
const DEFAULT_TABLE: Record<ReviewEffort, string> = {
|
|
36
|
+
low: "low-1-pass-max4",
|
|
37
|
+
medium: "minimal-prompt",
|
|
38
|
+
high: "high-8-inline",
|
|
39
|
+
xhigh: "xhigh-10-inline-sweep",
|
|
40
|
+
max: "max-5x5-verify-sweep",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Claude Code's Sonnet 5 table. */
|
|
44
|
+
const SONNET5_TABLE: Record<ReviewEffort, string> = {
|
|
45
|
+
low: "low-1-pass-min-files-4",
|
|
46
|
+
medium: "medium-3x5-verify",
|
|
47
|
+
high: "high-3x5-recall-verify",
|
|
48
|
+
xhigh: "xhigh-5x5-verify-sweep",
|
|
49
|
+
max: "max-5x5-verify-sweep",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const ARGUMENTS_AND_FLAGS = "arguments-and-flags";
|
|
53
|
+
|
|
54
|
+
export function codeReviewCell(tier: PromptTier, effort: ReviewEffort): string {
|
|
55
|
+
return (tier === "frontier" ? DEFAULT_TABLE : SONNET5_TABLE)[effort];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The review effort for a session thinking level: below low reads as low, unknown as medium (Claude Code's default). */
|
|
59
|
+
export function reviewEffortFor(thinkingLevel: string | undefined): ReviewEffort {
|
|
60
|
+
switch (thinkingLevel) {
|
|
61
|
+
case "off":
|
|
62
|
+
case "minimal":
|
|
63
|
+
case "low":
|
|
64
|
+
return "low";
|
|
65
|
+
case "high":
|
|
66
|
+
case "xhigh":
|
|
67
|
+
case "max":
|
|
68
|
+
return thinkingLevel;
|
|
69
|
+
default:
|
|
70
|
+
return "medium";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** A typed level as the first argument (`/code-review high 123`) and what follows it. */
|
|
75
|
+
export function parseCodeReviewArgs(args: string): { effort?: ReviewEffort; rest: string } {
|
|
76
|
+
const match = /^\s*(\S+)(?:\s+([\s\S]*))?$/.exec(args);
|
|
77
|
+
const first = match?.[1]?.toLowerCase();
|
|
78
|
+
if (!first || !(REVIEW_EFFORTS as readonly string[]).includes(first)) return { rest: args.trim() };
|
|
79
|
+
return { effort: first as ReviewEffort, rest: (match?.[2] ?? "").trim() };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The body for a session: the chosen cell, then the flags section. `args`
|
|
84
|
+
* comes back without the typed level, which the cell already reflects.
|
|
85
|
+
*/
|
|
86
|
+
export function codeReviewBody(
|
|
87
|
+
cellsDir: string,
|
|
88
|
+
tier: PromptTier,
|
|
89
|
+
thinkingLevel: string | undefined,
|
|
90
|
+
args: string,
|
|
91
|
+
): { body: string; args: string; cell: string } {
|
|
92
|
+
const { effort, rest } = parseCodeReviewArgs(args);
|
|
93
|
+
const cell = codeReviewCell(tier, effort ?? reviewEffortFor(thinkingLevel));
|
|
94
|
+
// A checkout that turned line endings into CRLF still sends Claude Code's bytes.
|
|
95
|
+
const read = (name: string) => readFileSync(join(cellsDir, `${name}.md`), "utf-8").replace(/\r\n/g, "\n").trimEnd();
|
|
96
|
+
return { body: `${read(cell)}\n\n${read(ARGUMENTS_AND_FLAGS)}`, args: rest, cell };
|
|
97
|
+
}
|