one-code-extension 0.4.0 → 0.5.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 +219 -219
- 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 +32 -23
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +27 -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 +66 -34
- 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 +123 -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 +515 -0
- package/extensions/background/index.ts +514 -236
- package/extensions/background/loop-fire.ts +136 -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 +52 -0
- package/extensions/background/wakeup.ts +159 -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 +88 -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 +229 -71
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +446 -58
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +216 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/index.ts +24 -5
- package/extensions/context-management/index.ts +25 -0
- 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 +72 -19
- 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 +337 -26
- package/extensions/file-tracker/replay.ts +96 -10
- package/extensions/file-tracker/restore.ts +63 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +32 -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 +198 -31
- package/extensions/hooks/matcher.ts +3 -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 +13 -5
- 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/lib/agent-cron.ts +130 -0
- package/extensions/lib/agent-loader.ts +15 -1
- package/extensions/lib/agent-view.ts +85 -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 +42 -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 +78 -0
- package/extensions/lib/context-stack.ts +81 -0
- package/extensions/lib/deferred.ts +219 -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/local-command.ts +47 -13
- 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 +174 -0
- package/extensions/lib/model-catalog/models-dev.json +18297 -0
- package/extensions/lib/model-catalog/openrouter.json +5252 -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 +308 -64
- 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 +21 -2
- package/extensions/lib/plan-mode-channels.ts +20 -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/process-tree.ts +131 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +364 -58
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +439 -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 +13 -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 +113 -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 +149 -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 +143 -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 +70 -4
- 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 +87 -5
- 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 +209 -71
- 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 +151 -67
- 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/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 +1300 -381
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +6 -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 +156 -69
- package/extensions/plan-mode/reminder.ts +89 -21
- 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 +61 -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 +12 -7
- package/extensions/recap/index.ts +65 -60
- 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/code-review.ts +97 -0
- package/extensions/skill/index.ts +421 -101
- package/extensions/skill/invoke.ts +128 -18
- 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 +116 -23
- package/extensions/subagents/announce.ts +69 -0
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +76 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1493 -824
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +2 -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/runner.ts +108 -80
- package/extensions/subagents/runs.ts +39 -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 +131 -47
- package/extensions/system-prompt/template.ts +24 -50
- 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 +175 -13
- 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/announce.ts +16 -0
- package/extensions/tool-search/index.ts +189 -41
- 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 +105 -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 +145 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +29 -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 +121 -39
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +19 -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
|
@@ -11,51 +11,134 @@
|
|
|
11
11
|
*
|
|
12
12
|
* The widget mirrors Claude Code's pinned task list (summary line + ✔/◼/◻
|
|
13
13
|
* rows). Claude Code toggles it with ctrl+t, but pi reserves that key for
|
|
14
|
-
* thinking blocks
|
|
14
|
+
* thinking blocks, so alt+t (ctrl+\ on macOS; `taskToggleKey`) and `/tasks
|
|
15
|
+
* hide` / `/tasks show` toggle it here; the key's hint is always on screen.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
18
19
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
19
20
|
import { Type } from "typebox";
|
|
20
21
|
import { restoreLatestDetails } from "../lib/branch-restore.ts";
|
|
21
|
-
import { DEFER_CHANNEL } from "../lib/deferred.ts";
|
|
22
|
+
import { DEFER_CHANNEL, WITHHOLD_CHANNEL } from "../lib/deferred.ts";
|
|
23
|
+
import { taskToolsEnabled } from "../lib/model-tier.ts";
|
|
24
|
+
import { sessionModelTier } from "../lib/session-model-tier.ts";
|
|
22
25
|
import { ccToolRenderers, linesComponent, safeThemeBold, safeThemePaint, strike } from "../lib/tui-render.ts";
|
|
23
26
|
import { REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
24
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
TASK_CREATE_DESCRIPTION,
|
|
29
|
+
TASK_CREATE_PARAMS,
|
|
30
|
+
TASK_GET_DESCRIPTION,
|
|
31
|
+
TASK_GET_PARAMS,
|
|
32
|
+
TASK_LIST_DESCRIPTION,
|
|
33
|
+
TASK_UPDATE_DESCRIPTION,
|
|
34
|
+
TASK_UPDATE_PARAMS,
|
|
35
|
+
} from "./descriptions.ts";
|
|
36
|
+
import {
|
|
37
|
+
FINISHED_LIST_CLEAR_MS,
|
|
38
|
+
formatHiddenTaskWidget,
|
|
39
|
+
formatTaskDetails,
|
|
40
|
+
formatTaskList,
|
|
41
|
+
formatTaskWidget,
|
|
42
|
+
normalizeTaskId,
|
|
43
|
+
nudgeMessage,
|
|
44
|
+
type TaskSnapshot,
|
|
45
|
+
TaskStore,
|
|
46
|
+
} from "./store.ts";
|
|
25
47
|
import { registerLocalCommand } from "../lib/local-command.ts";
|
|
48
|
+
import { canShowCustomUi } from "../lib/headless-output.ts";
|
|
49
|
+
import { taskToggleKey } from "../lib/keys.ts";
|
|
26
50
|
|
|
27
51
|
interface TaskDetails {
|
|
28
52
|
taskSnapshot: TaskSnapshot;
|
|
29
53
|
}
|
|
30
54
|
|
|
31
55
|
const TASK_TOOLS = new Set(["task_create", "task_get", "task_list", "task_update"]);
|
|
56
|
+
const TASK_KEYWORDS = ["task", "todo", "plan", "progress", "dependencies", "tracking"];
|
|
32
57
|
|
|
33
58
|
export default function tasksExtension(pi: ExtensionAPI) {
|
|
59
|
+
const requestTier = sessionModelTier(pi);
|
|
34
60
|
const store = new TaskStore();
|
|
61
|
+
const toggleKey = taskToggleKey();
|
|
35
62
|
let widgetHidden = false;
|
|
36
63
|
|
|
37
64
|
const updateWidget = (ctx: ExtensionContext) => {
|
|
38
65
|
if (!ctx.hasUI) return;
|
|
39
|
-
if (
|
|
66
|
+
if (store.list().length === 0) {
|
|
40
67
|
ctx.ui.setWidget("cc-tasks", undefined);
|
|
41
68
|
return;
|
|
42
69
|
}
|
|
70
|
+
// RPC forwards line arrays only, and has no terminal shortcut to hide it.
|
|
71
|
+
if (!canShowCustomUi(ctx)) {
|
|
72
|
+
ctx.ui.setWidget("cc-tasks", formatTaskWidget(store));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
43
75
|
// Component form so every line passes truncateLine — subjects are
|
|
44
76
|
// unbounded model text, and pi-tui crashes on an over-wide line.
|
|
45
77
|
ctx.ui.setWidget("cc-tasks", (_tui, theme) => {
|
|
46
78
|
const style = { paint: safeThemePaint(theme), bold: safeThemeBold(theme), strike };
|
|
47
|
-
return linesComponent(() =>
|
|
79
|
+
return linesComponent(() =>
|
|
80
|
+
widgetHidden
|
|
81
|
+
? formatHiddenTaskWidget(store, `${toggleKey} to show`, style)
|
|
82
|
+
: formatTaskWidget(store, 12, style, `${toggleKey} to hide`),
|
|
83
|
+
);
|
|
48
84
|
});
|
|
49
85
|
};
|
|
86
|
+
const setWidgetHidden = (hidden: boolean, ctx: ExtensionContext) => {
|
|
87
|
+
widgetHidden = hidden;
|
|
88
|
+
updateWidget(ctx);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Claude Code clears a finished list (every task completed) 5 s after it
|
|
92
|
+
// gets there, and the panel goes with it; reopening or adding a task first
|
|
93
|
+
// cancels the clear. Only where there is a widget: a one-shot run keeps its
|
|
94
|
+
// list. The timer is unref'd and cancelled on shutdown, so it never holds
|
|
95
|
+
// the process or fires into a disposed session.
|
|
96
|
+
let clearTimer: ReturnType<typeof setTimeout> | undefined;
|
|
97
|
+
const cancelClear = () => {
|
|
98
|
+
if (clearTimer) clearTimeout(clearTimer);
|
|
99
|
+
clearTimer = undefined;
|
|
100
|
+
};
|
|
101
|
+
const scheduleClear = (ctx: ExtensionContext) => {
|
|
102
|
+
if (!ctx.hasUI || !store.isFinished()) return cancelClear();
|
|
103
|
+
if (clearTimer) return;
|
|
104
|
+
clearTimer = setTimeout(() => {
|
|
105
|
+
clearTimer = undefined;
|
|
106
|
+
if (!store.isFinished()) return;
|
|
107
|
+
store.clear();
|
|
108
|
+
updateWidget(ctx);
|
|
109
|
+
}, FINISHED_LIST_CLEAR_MS);
|
|
110
|
+
clearTimer.unref?.();
|
|
111
|
+
};
|
|
50
112
|
|
|
51
113
|
const reconstructState = (ctx: ExtensionContext) => {
|
|
114
|
+
cancelClear();
|
|
52
115
|
const details = restoreLatestDetails<TaskDetails>(ctx.sessionManager.getBranch(), TASK_TOOLS, (d) => Boolean(d?.taskSnapshot));
|
|
53
116
|
store.restore(details?.taskSnapshot);
|
|
54
117
|
updateWidget(ctx);
|
|
55
118
|
};
|
|
56
119
|
|
|
57
|
-
|
|
120
|
+
// Claude Code's model gate: frontier models and Sonnet 5+ run without the
|
|
121
|
+
// task tools unless CLAUDE_CODE_ENABLE_TODO_TOOLS is set (lib/model-tier.ts
|
|
122
|
+
// taskToolsEnabled). Re-applied on a model change, so switching to a model
|
|
123
|
+
// that has them brings them back through the deferred listing.
|
|
124
|
+
let withheld = false;
|
|
125
|
+
const applyModelGate = (model: Parameters<typeof taskToolsEnabled>[0]) => {
|
|
126
|
+
const enabled = taskToolsEnabled(model, process.env, requestTier());
|
|
127
|
+
if (enabled === !withheld) return;
|
|
128
|
+
withheld = !enabled;
|
|
129
|
+
for (const name of TASK_TOOLS) {
|
|
130
|
+
if (withheld) pi.events.emit(WITHHOLD_CHANNEL, { name });
|
|
131
|
+
else pi.events.emit(DEFER_CHANNEL, { name, keywords: TASK_KEYWORDS });
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
pi.on("session_start", (_event, ctx) => {
|
|
136
|
+
reconstructState(ctx);
|
|
137
|
+
applyModelGate(ctx.model);
|
|
138
|
+
});
|
|
58
139
|
pi.on("session_tree", (_event, ctx) => reconstructState(ctx));
|
|
140
|
+
pi.on("model_select", (event) => applyModelGate(event.model));
|
|
141
|
+
pi.on("session_shutdown", () => cancelClear());
|
|
59
142
|
|
|
60
143
|
// Claude Code's periodic task_reminder (TODO_REMINDER_CONFIG): a gentle
|
|
61
144
|
// nudge once the task tools have gone unused for TURNS_SINCE_WRITE turns,
|
|
@@ -71,6 +154,8 @@ export default function tasksExtension(pi: ExtensionAPI) {
|
|
|
71
154
|
let turnsSinceReminder = TURNS_BETWEEN_REMINDERS;
|
|
72
155
|
|
|
73
156
|
pi.on("turn_end", () => {
|
|
157
|
+
// Claude Code's reminder needs TaskUpdate in the tool list.
|
|
158
|
+
if (withheld) return;
|
|
74
159
|
turnsSinceTaskUse++;
|
|
75
160
|
turnsSinceReminder++;
|
|
76
161
|
if (turnsSinceTaskUse < TURNS_SINCE_WRITE || turnsSinceReminder < TURNS_BETWEEN_REMINDERS) return;
|
|
@@ -86,6 +171,7 @@ export default function tasksExtension(pi: ExtensionAPI) {
|
|
|
86
171
|
|
|
87
172
|
const result = (text: string, ctx: ExtensionContext, isError = false) => {
|
|
88
173
|
updateWidget(ctx);
|
|
174
|
+
scheduleClear(ctx);
|
|
89
175
|
return {
|
|
90
176
|
content: [{ type: "text" as const, text }],
|
|
91
177
|
details: { taskSnapshot: store.snapshot() } satisfies TaskDetails,
|
|
@@ -97,18 +183,17 @@ export default function tasksExtension(pi: ExtensionAPI) {
|
|
|
97
183
|
name: "task_create",
|
|
98
184
|
label: "Create Task",
|
|
99
185
|
...ccToolRenderers("Create Task"),
|
|
100
|
-
description:
|
|
101
|
-
"Add a task to the session's structured task list. Tasks are addressable by id and can carry owners, metadata, and dependencies — use task_update to change status or link tasks, task_list/task_get to read them. Create tasks for multi-step work (3+ steps) so progress is visible, and mark exactly one in_progress while working on it.",
|
|
186
|
+
description: TASK_CREATE_DESCRIPTION,
|
|
102
187
|
parameters: Type.Object({
|
|
103
|
-
subject: Type.String({ description:
|
|
104
|
-
description: Type.String({ description:
|
|
105
|
-
activeForm: Type.Optional(Type.String({ description:
|
|
106
|
-
metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description:
|
|
188
|
+
subject: Type.String({ description: TASK_CREATE_PARAMS.subject }),
|
|
189
|
+
description: Type.String({ description: TASK_CREATE_PARAMS.description }),
|
|
190
|
+
activeForm: Type.Optional(Type.String({ description: TASK_CREATE_PARAMS.activeForm })),
|
|
191
|
+
metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: TASK_CREATE_PARAMS.metadata })),
|
|
107
192
|
}),
|
|
108
193
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
109
194
|
markUsed();
|
|
110
195
|
const task = store.create(params);
|
|
111
|
-
return result(`
|
|
196
|
+
return result(`Task #${task.id} created successfully: ${task.subject}`, ctx);
|
|
112
197
|
},
|
|
113
198
|
});
|
|
114
199
|
|
|
@@ -116,9 +201,9 @@ export default function tasksExtension(pi: ExtensionAPI) {
|
|
|
116
201
|
name: "task_get",
|
|
117
202
|
label: "Get Task",
|
|
118
203
|
...ccToolRenderers("Get Task"),
|
|
119
|
-
description:
|
|
204
|
+
description: TASK_GET_DESCRIPTION,
|
|
120
205
|
parameters: Type.Object({
|
|
121
|
-
taskId: Type.String({ description:
|
|
206
|
+
taskId: Type.String({ description: TASK_GET_PARAMS.taskId }),
|
|
122
207
|
}),
|
|
123
208
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
124
209
|
const task = store.get(params.taskId);
|
|
@@ -131,7 +216,7 @@ export default function tasksExtension(pi: ExtensionAPI) {
|
|
|
131
216
|
name: "task_list",
|
|
132
217
|
label: "List Tasks",
|
|
133
218
|
...ccToolRenderers("List Tasks", { maxCollapsedLines: 12 }),
|
|
134
|
-
description:
|
|
219
|
+
description: TASK_LIST_DESCRIPTION,
|
|
135
220
|
parameters: Type.Object({}),
|
|
136
221
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
137
222
|
return result(formatTaskList(store), ctx);
|
|
@@ -142,49 +227,43 @@ export default function tasksExtension(pi: ExtensionAPI) {
|
|
|
142
227
|
name: "task_update",
|
|
143
228
|
label: "Update Task",
|
|
144
229
|
...ccToolRenderers("Update Task"),
|
|
145
|
-
description:
|
|
146
|
-
"Update a task: status (pending/in_progress/completed, or deleted to remove it), subject, description, owner, metadata (merge; null deletes a key), and dependencies via addBlocks/addBlockedBy. Mark a task in_progress before starting it and completed only when fully done.",
|
|
230
|
+
description: TASK_UPDATE_DESCRIPTION,
|
|
147
231
|
parameters: Type.Object({
|
|
148
|
-
taskId: Type.String({ description:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
232
|
+
taskId: Type.String({ description: TASK_UPDATE_PARAMS.taskId }),
|
|
233
|
+
subject: Type.Optional(Type.String({ description: TASK_UPDATE_PARAMS.subject })),
|
|
234
|
+
description: Type.Optional(Type.String({ description: TASK_UPDATE_PARAMS.description })),
|
|
235
|
+
activeForm: Type.Optional(Type.String({ description: TASK_UPDATE_PARAMS.activeForm })),
|
|
236
|
+
status: Type.Optional(StringEnum(["pending", "in_progress", "completed", "deleted"] as const, { description: TASK_UPDATE_PARAMS.status })),
|
|
237
|
+
addBlocks: Type.Optional(Type.Array(Type.String(), { description: TASK_UPDATE_PARAMS.addBlocks })),
|
|
238
|
+
addBlockedBy: Type.Optional(Type.Array(Type.String(), { description: TASK_UPDATE_PARAMS.addBlockedBy })),
|
|
239
|
+
owner: Type.Optional(Type.String({ description: TASK_UPDATE_PARAMS.owner })),
|
|
240
|
+
metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: TASK_UPDATE_PARAMS.metadata })),
|
|
157
241
|
}),
|
|
158
242
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
159
243
|
const { taskId, ...input } = params;
|
|
160
244
|
const outcome = store.update(taskId, input as Parameters<TaskStore["update"]>[1]);
|
|
161
245
|
if (outcome.deleted) {
|
|
162
246
|
markUsed();
|
|
163
|
-
return result(`
|
|
247
|
+
return result(`Updated task #${normalizeTaskId(taskId)} deleted`, ctx);
|
|
164
248
|
}
|
|
165
249
|
// A failed update (unknown id) is not "using" the tracker — a model
|
|
166
250
|
// looping on a hallucinated id must not suppress the nudge.
|
|
167
251
|
if (outcome.error && !outcome.task) return result(outcome.error, ctx, true);
|
|
168
252
|
markUsed();
|
|
169
|
-
|
|
253
|
+
// Claude Code's result names the fields that changed.
|
|
254
|
+
const line = `Updated task #${outcome.task!.id} ${(outcome.updatedFields ?? []).join(", ")}`;
|
|
170
255
|
return result(outcome.error ? `${line}\n${outcome.error}` : line, ctx, Boolean(outcome.error));
|
|
171
256
|
},
|
|
172
257
|
});
|
|
173
258
|
|
|
174
259
|
for (const name of TASK_TOOLS) {
|
|
175
|
-
pi.events.emit(DEFER_CHANNEL, { name, keywords:
|
|
260
|
+
pi.events.emit(DEFER_CHANNEL, { name, keywords: TASK_KEYWORDS });
|
|
176
261
|
}
|
|
177
262
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
widgetHidden = arg === "hide";
|
|
184
|
-
updateWidget(ctx);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
ctx.ui.notify(formatTaskList(store), "info");
|
|
188
|
-
},
|
|
263
|
+
// `/tasks` is Claude Code's Background dialog (the subagents extension, findings
|
|
264
|
+
// §40); the task list toggles with its key only, as Claude Code's does.
|
|
265
|
+
pi.registerShortcut(toggleKey, {
|
|
266
|
+
description: "Show or hide the task list",
|
|
267
|
+
handler: (ctx) => setWidgetHidden(!widgetHidden, ctx),
|
|
189
268
|
});
|
|
190
269
|
}
|
|
@@ -38,6 +38,11 @@ export interface TaskUpdateInput {
|
|
|
38
38
|
addBlockedBy?: string[];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** A task id as the model may write it: tasks show as `#3`, so a leading `#` names task 3. */
|
|
42
|
+
export function normalizeTaskId(id: string): string {
|
|
43
|
+
return id.startsWith("#") ? id.slice(1) : id;
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
export class TaskStore {
|
|
42
47
|
private tasks = new Map<string, TaskItem>();
|
|
43
48
|
private nextId = 1;
|
|
@@ -58,7 +63,7 @@ export class TaskStore {
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
get(id: string): TaskItem | undefined {
|
|
61
|
-
return this.tasks.get(id);
|
|
66
|
+
return this.tasks.get(normalizeTaskId(id));
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
list(): TaskItem[] {
|
|
@@ -73,9 +78,14 @@ export class TaskStore {
|
|
|
73
78
|
});
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Apply an update. `updatedFields` names what actually changed, in Claude
|
|
83
|
+
* Code's order (its TaskUpdate result lists them).
|
|
84
|
+
*/
|
|
85
|
+
update(rawId: string, input: TaskUpdateInput): { task?: TaskItem; deleted?: boolean; error?: string; updatedFields?: string[] } {
|
|
86
|
+
const id = normalizeTaskId(rawId);
|
|
77
87
|
const task = this.tasks.get(id);
|
|
78
|
-
if (!task) return { error: `No task with id "${
|
|
88
|
+
if (!task) return { error: `No task with id "${rawId}". Use task_list to see ids.` };
|
|
79
89
|
|
|
80
90
|
if (input.status === "deleted") {
|
|
81
91
|
this.tasks.delete(id);
|
|
@@ -86,17 +96,33 @@ export class TaskStore {
|
|
|
86
96
|
return { deleted: true };
|
|
87
97
|
}
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
if (input.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
const updatedFields: string[] = [];
|
|
100
|
+
if (input.subject !== undefined && input.subject !== task.subject) {
|
|
101
|
+
task.subject = input.subject;
|
|
102
|
+
updatedFields.push("subject");
|
|
103
|
+
}
|
|
104
|
+
if (input.description !== undefined && input.description !== task.description) {
|
|
105
|
+
task.description = input.description;
|
|
106
|
+
updatedFields.push("description");
|
|
107
|
+
}
|
|
108
|
+
if (input.activeForm !== undefined && input.activeForm !== task.activeForm) {
|
|
109
|
+
task.activeForm = input.activeForm;
|
|
110
|
+
updatedFields.push("activeForm");
|
|
111
|
+
}
|
|
112
|
+
if (input.owner !== undefined && (input.owner || undefined) !== task.owner) {
|
|
113
|
+
task.owner = input.owner || undefined;
|
|
114
|
+
updatedFields.push("owner");
|
|
115
|
+
}
|
|
95
116
|
if (input.metadata) {
|
|
96
117
|
for (const [key, value] of Object.entries(input.metadata)) {
|
|
97
118
|
if (value === null) delete task.metadata[key];
|
|
98
119
|
else task.metadata[key] = value;
|
|
99
120
|
}
|
|
121
|
+
updatedFields.push("metadata");
|
|
122
|
+
}
|
|
123
|
+
if (input.status !== undefined && input.status !== task.status) {
|
|
124
|
+
task.status = input.status;
|
|
125
|
+
updatedFields.push("status");
|
|
100
126
|
}
|
|
101
127
|
|
|
102
128
|
// Separate a self-reference from a genuinely unknown id: link() rejects
|
|
@@ -104,19 +130,23 @@ export class TaskStore {
|
|
|
104
130
|
// task id" — sending the model to hunt a typo that isn't there.
|
|
105
131
|
const unknown: string[] = [];
|
|
106
132
|
let selfRef = false;
|
|
107
|
-
|
|
133
|
+
const blocksBefore = task.blocks.length;
|
|
134
|
+
for (const other of (input.addBlocks ?? []).map(normalizeTaskId)) {
|
|
108
135
|
if (other === id) selfRef = true;
|
|
109
136
|
else if (!this.link(id, other)) unknown.push(other);
|
|
110
137
|
}
|
|
111
|
-
|
|
138
|
+
if (task.blocks.length > blocksBefore) updatedFields.push("blocks");
|
|
139
|
+
const blockedByBefore = task.blockedBy.length;
|
|
140
|
+
for (const other of (input.addBlockedBy ?? []).map(normalizeTaskId)) {
|
|
112
141
|
if (other === id) selfRef = true;
|
|
113
142
|
else if (!this.link(other, id)) unknown.push(other);
|
|
114
143
|
}
|
|
144
|
+
if (task.blockedBy.length > blockedByBefore) updatedFields.push("blockedBy");
|
|
115
145
|
const errors: string[] = [];
|
|
116
146
|
if (selfRef) errors.push(`Task #${id} cannot block or be blocked by itself`);
|
|
117
147
|
if (unknown.length > 0) errors.push(`Unknown task id(s) in dependency list: ${unknown.join(", ")}`);
|
|
118
|
-
if (errors.length > 0) return { task, error: errors.join(". ") };
|
|
119
|
-
return { task };
|
|
148
|
+
if (errors.length > 0) return { task, error: errors.join(". "), updatedFields };
|
|
149
|
+
return { task, updatedFields };
|
|
120
150
|
}
|
|
121
151
|
|
|
122
152
|
/** Record "blocker blocks blocked" on both sides. False if either id is unknown. */
|
|
@@ -133,15 +163,42 @@ export class TaskStore {
|
|
|
133
163
|
return { nextId: this.nextId, tasks: this.list().map((t) => ({ ...t, metadata: { ...t.metadata }, blocks: [...t.blocks], blockedBy: [...t.blockedBy] })) };
|
|
134
164
|
}
|
|
135
165
|
|
|
166
|
+
/**
|
|
167
|
+
* A non-empty list with every task completed: the state Claude Code clears
|
|
168
|
+
* itself from, 5 s after it is reached (`FINISHED_LIST_CLEAR_MS`).
|
|
169
|
+
*/
|
|
170
|
+
isFinished(): boolean {
|
|
171
|
+
const tasks = this.list();
|
|
172
|
+
return tasks.length > 0 && tasks.every((t) => t.status === "completed");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Drop every task and keep the id counter, as Claude Code's reset does (it
|
|
177
|
+
* records the high-water mark before deleting), so a task created after a
|
|
178
|
+
* clear never reuses an id the transcript already mentions.
|
|
179
|
+
*/
|
|
180
|
+
clear(): void {
|
|
181
|
+
this.tasks.clear();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A finished snapshot restores as empty: the clear runs on a timer and never
|
|
186
|
+
* lands in a tool result, so without this rule `/resume` or a `/tree` switch
|
|
187
|
+
* would bring back a list Claude Code would already have cleared.
|
|
188
|
+
*/
|
|
136
189
|
restore(snapshot: TaskSnapshot | undefined): void {
|
|
137
190
|
this.tasks.clear();
|
|
138
191
|
this.nextId = snapshot?.nextId ?? 1;
|
|
139
192
|
for (const task of snapshot?.tasks ?? []) {
|
|
140
193
|
this.tasks.set(task.id, { ...task, metadata: { ...task.metadata }, blocks: [...task.blocks], blockedBy: [...task.blockedBy] });
|
|
141
194
|
}
|
|
195
|
+
if (this.isFinished()) this.clear();
|
|
142
196
|
}
|
|
143
197
|
}
|
|
144
198
|
|
|
199
|
+
/** How long a finished list stays on screen before it clears (Claude Code's 5000 ms). */
|
|
200
|
+
export const FINISHED_LIST_CLEAR_MS = 5_000;
|
|
201
|
+
|
|
145
202
|
const STATUS_MARK: Record<TaskStatus, string> = { pending: " ", in_progress: "▸", completed: "x" };
|
|
146
203
|
|
|
147
204
|
export function formatTaskLine(store: TaskStore, task: TaskItem): string {
|
|
@@ -193,7 +250,7 @@ function taskRow(task: TaskItem, { paint, bold, strike }: WidgetStyle): string {
|
|
|
193
250
|
* past `maxTasks` so the widget cannot swallow the screen (Claude Code's
|
|
194
251
|
* cutoff is unverified).
|
|
195
252
|
*/
|
|
196
|
-
export function formatTaskWidget(store: TaskStore, maxTasks = 12, style: WidgetStyle = PLAIN_STYLE): string[] {
|
|
253
|
+
export function formatTaskWidget(store: TaskStore, maxTasks = 12, style: WidgetStyle = PLAIN_STYLE, hint?: string): string[] {
|
|
197
254
|
const { paint, bold } = style;
|
|
198
255
|
const tasks = store.list();
|
|
199
256
|
if (tasks.length === 0) return [];
|
|
@@ -203,13 +260,23 @@ export function formatTaskWidget(store: TaskStore, maxTasks = 12, style: WidgetS
|
|
|
203
260
|
const dim = (text: string) => paint("dim", text);
|
|
204
261
|
const count = (n: number) => bold(dim(String(n)));
|
|
205
262
|
const lines = [
|
|
206
|
-
` ${count(tasks.length)}${dim(` ${tasks.length === 1 ? "task" : "tasks"} (`)}${count(done)}${dim(" done, ")}${count(inProgress)}${dim(" in progress, ")}${count(open)}${dim(" open)")}`,
|
|
263
|
+
` ${count(tasks.length)}${dim(` ${tasks.length === 1 ? "task" : "tasks"} (`)}${count(done)}${dim(" done, ")}${count(inProgress)}${dim(" in progress, ")}${count(open)}${dim(" open)")}${hint ? dim(` · ${hint}`) : ""}`,
|
|
207
264
|
];
|
|
208
265
|
for (const t of tasks.slice(0, maxTasks)) lines.push(` ${taskRow(t, style)}`);
|
|
209
266
|
if (tasks.length > maxTasks) lines.push(` ${dim(`… +${tasks.length - maxTasks} more`)}`);
|
|
210
267
|
return lines;
|
|
211
268
|
}
|
|
212
269
|
|
|
270
|
+
/**
|
|
271
|
+
* The one line left when the user hides the list, so the key that brings it
|
|
272
|
+
* back stays on screen (Claude Code shows "ctrl+t to show tasks" in its footer).
|
|
273
|
+
*/
|
|
274
|
+
export function formatHiddenTaskWidget(store: TaskStore, hint: string, style: WidgetStyle = PLAIN_STYLE): string[] {
|
|
275
|
+
const n = store.list().length;
|
|
276
|
+
if (n === 0) return [];
|
|
277
|
+
return [` ${style.paint("dim", `${n} ${n === 1 ? "task" : "tasks"} hidden · ${hint}`)}`];
|
|
278
|
+
}
|
|
279
|
+
|
|
213
280
|
/**
|
|
214
281
|
* The periodic task-tool reminder (Claude Code's `task_reminder`): a single
|
|
215
282
|
* gentle nudge fired when the task tools have gone unused for a while, with the
|
|
@@ -217,7 +284,7 @@ export function formatTaskWidget(store: TaskStore, maxTasks = 12, style: WidgetS
|
|
|
217
284
|
* when the user pivots mid-session and the plan no longer matches the work.
|
|
218
285
|
*
|
|
219
286
|
* Byte-for-byte Claude Code's text (messages.ts) except the tool names, which
|
|
220
|
-
* stay our snake_case (docs/decisions/tools.md). We do NOT branch on list state
|
|
287
|
+
* stay our snake_case (working-docs/decisions/tools.md). We do NOT branch on list state
|
|
221
288
|
* the way CC's earlier prompt did — one reminder, always the same, and the
|
|
222
289
|
* appended list is what the model reasons over.
|
|
223
290
|
*/
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The OpenRouter stream guard for an in-process child session (lib/child-extensions.ts): blocks and stops, prints nothing. */
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { toolCallCorruptionGuard } from "./index.ts";
|
|
4
|
+
|
|
5
|
+
export default function childToolCallCorruptionExtension(pi: ExtensionAPI) {
|
|
6
|
+
toolCallCorruptionGuard(pi, { announce: false });
|
|
7
|
+
}
|