gentle-pi 3.7.0 → 4.1.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 +155 -10
- package/assets/agents/gentle-ai-explore.md +13 -6
- package/assets/agents/gentle-ai-verify.md +27 -6
- package/assets/agents/gentle-ai-worker.md +13 -15
- package/assets/install-wizard/index.html +47 -0
- package/assets/install-wizard/wizard.css +1279 -0
- package/assets/install-wizard/wizard.js +951 -0
- package/assets/orchestrator-delegation.md +75 -169
- package/assets/orchestrator-memory.md +12 -26
- package/assets/orchestrator-prompts.md +48 -0
- package/assets/orchestrator-skills.md +1 -1
- package/assets/orchestrator-tracking.md +53 -0
- package/assets/orchestrator-verification.md +28 -0
- package/assets/orchestrator-writer.md +38 -0
- package/assets/orchestrator.md +30 -38
- package/assets/sounds/LICENSE.md +52 -0
- package/assets/sounds/attention.wav +0 -0
- package/assets/sounds/error.wav +0 -0
- package/assets/sounds/success.wav +0 -0
- package/assets/support/strict-tdd-verify.md +4 -266
- package/assets/support/strict-tdd.md +8 -360
- package/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +956 -50
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/bundled-install.md +397 -0
- package/docs/delegated-verification.md +27 -4
- package/docs/gentle-agents-activity.md +537 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +165 -51
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +3 -3
- package/docs/permission-modes.md +82 -0
- package/docs/prompt-history.md +280 -0
- package/docs/readme-reference.md +364 -231
- package/docs/render-cache-performance.md +28 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/telemetry.md +1 -1
- package/docs/yolo-mode.md +86 -0
- package/extensions/child-context.ts +35 -0
- package/extensions/child-safety.ts +25 -0
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +1093 -326
- package/extensions/gentle-ai.ts +1636 -890
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +1619 -136
- package/extensions/gentle-stats.ts +101 -0
- package/extensions/gentle-todo.ts +126 -20
- package/extensions/history/atomic-write.ts +38 -0
- package/extensions/history/hide-prompts.ts +183 -0
- package/extensions/history/index.ts +1424 -0
- package/extensions/history/load-shared-history.ts +39 -0
- package/extensions/history/selector-helpers.ts +538 -0
- package/extensions/history/session-scan.ts +233 -0
- package/extensions/history/store.ts +1119 -0
- package/extensions/nan-provider.ts +6 -0
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +254 -92
- package/extensions/resume-hint.ts +60 -0
- package/extensions/skill-registry.ts +191 -58
- package/extensions/startup-banner.ts +71 -33
- package/lib/agent-assets.ts +604 -0
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profile-pin.ts +12 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-message-delivery.ts +181 -0
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +99 -0
- package/lib/agents-runner.ts +180 -107
- package/lib/agents-view.ts +26 -3
- package/lib/agents-widget.ts +16 -9
- package/lib/append-system-prompt.ts +21 -0
- package/lib/auto-permission-classifier.ts +280 -0
- package/lib/auto-permission-runtime.ts +268 -0
- package/lib/background-jobs-tools.ts +202 -0
- package/lib/background-jobs.ts +253 -0
- package/lib/background-monitor.ts +154 -0
- package/lib/bounded-writer-admission.ts +220 -0
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/card-style-policy.ts +60 -0
- package/lib/child-context-files.ts +166 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +195 -0
- package/lib/command-palette-catalog.ts +5 -10
- package/lib/command-palette.ts +25 -14
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +144 -0
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-ai-elapsed-store.ts +87 -0
- package/lib/gentle-ai-renderer.ts +196 -39
- package/lib/gentle-shell-bundled.ts +517 -0
- package/lib/gentle-shell-launcher.ts +89 -27
- package/lib/gentle-shell-resume-hint.ts +176 -0
- package/lib/gentle-shell-startup-flow.ts +54 -0
- package/lib/gentle-shell-startup-update.ts +92 -0
- package/lib/gentle-shell-uninstall.ts +840 -0
- package/lib/gentle-shell-update-panel.ts +231 -0
- package/lib/herdr-activity.ts +183 -0
- package/lib/history-capture-policy.ts +95 -0
- package/lib/inprocess-reviewer.ts +3 -0
- package/lib/jobs-sidebar-state.ts +20 -0
- package/lib/jobs-view.ts +161 -0
- package/lib/model-price-ratio.ts +83 -0
- package/lib/model-routing-authority.ts +5 -1
- package/lib/nan-provider.ts +249 -0
- package/lib/native-review-cli.ts +242 -180
- package/lib/notification-audio-native.ts +162 -0
- package/lib/notification-audio-windows.ts +399 -0
- package/lib/notification-audio.ts +332 -0
- package/lib/notification-customize.ts +241 -0
- package/lib/notification-events.ts +113 -0
- package/lib/notification-policy.ts +148 -0
- package/lib/notification-pulse-client.ts +367 -0
- package/lib/notification-pulse-protocol.ts +305 -0
- package/lib/notification-pulse-stream.ts +213 -0
- package/lib/notification-pulse-worker.ts +66 -0
- package/lib/notification-scheduler.ts +173 -0
- package/lib/notification-service.ts +202 -0
- package/lib/notification-ui.ts +87 -0
- package/lib/odd-phase-inference.ts +231 -0
- package/lib/odd-phase.ts +141 -0
- package/lib/orchestrator-alias.ts +20 -0
- package/lib/orchestrator-catalog.ts +79 -0
- package/lib/orchestrator-consultation.ts +61 -0
- package/lib/orchestrator-discovery.ts +49 -0
- package/lib/orchestrator-helper-consent.ts +122 -0
- package/lib/orchestrator-helper.ts +167 -0
- package/lib/orchestrator-message-card.ts +105 -0
- package/lib/orchestrator-presence.ts +115 -14
- package/lib/orchestrator-scope.ts +80 -0
- package/lib/orchestrator-state.ts +107 -0
- package/lib/orchestrator-tool-card.ts +280 -0
- package/lib/orchestrator-work-search.ts +218 -0
- package/lib/orchestrator-work.ts +79 -0
- package/lib/overlay-repaint.ts +26 -0
- package/lib/permission-mode-policy.ts +59 -0
- package/lib/pi-tui-keys.ts +53 -0
- package/lib/review-candidate-view-owner.ts +73 -21
- package/lib/review-candidate-view.ts +115 -27
- package/lib/review-compact-contract.ts +2 -2
- package/lib/review-host-relay.ts +7 -2
- package/lib/review-integration-v2.ts +100 -22
- package/lib/review-publication-gate.ts +1 -1
- package/lib/review-reminder-receipt.ts +48 -8
- package/lib/review-risk-assessment.ts +227 -56
- package/lib/review-sidebar-state.ts +224 -0
- package/lib/review-transaction.ts +72 -3
- package/lib/runtime-metrics-children.ts +1 -1
- package/lib/runtime-metrics-native.ts +3 -2
- package/lib/selection-engine.ts +527 -0
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-messaging-grants.ts +135 -0
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +266 -0
- package/lib/session-worktree-registry.ts +87 -9
- package/lib/shell-bar.ts +215 -30
- package/lib/shell-card.ts +287 -18
- package/lib/shell-changes-view.ts +42 -14
- package/lib/shell-changes.ts +86 -71
- package/lib/shell-prompt.ts +102 -6
- package/lib/shell-sidebar-layout.ts +83 -15
- package/lib/shell-sidebar.ts +15 -1
- package/lib/shell-todo.ts +84 -31
- package/lib/shell-usage-view.ts +9 -4
- package/lib/shell-usage.ts +70 -13
- package/lib/stats-collector.ts +381 -0
- package/lib/stats-view.ts +431 -0
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/theme-customization.ts +52 -0
- package/lib/vim-editor-adapter.ts +383 -0
- package/lib/vim-normal-engine.ts +154 -0
- package/lib/vim-operator-engine.ts +416 -0
- package/lib/vim-policy.ts +49 -0
- package/lib/vim-visual-engine.ts +107 -0
- package/lib/visual-customization-policy.ts +119 -0
- package/lib/visual-customize-view.ts +481 -0
- package/lib/visual-profiles.ts +228 -0
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +317 -0
- package/package.json +20 -8
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +518 -0
- package/runtime/gentle-shell-launcher.mjs +87 -25
- package/runtime/gentle-shell-resume-hint.mjs +177 -0
- package/runtime/gentle-shell-startup-flow.mjs +55 -0
- package/runtime/gentle-shell-startup-update.mjs +93 -0
- package/runtime/gentle-shell-uninstall.mjs +841 -0
- package/runtime/gentle-shell-update-panel.mjs +232 -0
- package/runtime/native-review-cli.mjs +239 -177
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +225 -54
- package/runtime/status-timing-diagnostics.mjs +202 -0
- package/runtime/telemetry-trigger.mjs +3 -1
- package/scripts/bootstrap.cmd +255 -0
- package/scripts/bootstrap.sh +168 -0
- package/scripts/build-distribution.mjs +157 -0
- package/scripts/build-installer-bundles.mjs +136 -0
- package/scripts/build-runtime-modules.mjs +8 -0
- package/scripts/bundled-install.mjs +1308 -0
- package/scripts/bundled-wizard.mjs +399 -0
- package/scripts/check-types.mjs +4 -6
- package/scripts/gentle-ai-installer.mjs +83 -35
- package/scripts/install-gentle-ai.mjs +48 -1
- package/scripts/install-tui-mode-setting.mjs +10 -1
- package/scripts/install-wizard-preview.mjs +154 -0
- package/scripts/installer-downloads.mjs +648 -0
- package/scripts/installer-preflight.mjs +387 -0
- package/scripts/installer-probes.mjs +564 -0
- package/scripts/installer-runner.mjs +1102 -0
- package/scripts/installer-server.mjs +881 -0
- package/scripts/installer-windows-artifacts.json +15 -0
- package/scripts/installer-windows.mjs +752 -0
- package/scripts/main-channel.mjs +452 -0
- package/scripts/mirror-odd-routing.mjs +2 -2
- package/scripts/npm-pack-result.mjs +31 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +90 -0
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +54 -19
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +91 -38
- package/skills/branch-pr/SKILL.md +24 -52
- package/skills/chained-pr/SKILL.md +31 -15
- package/skills/chained-pr/references/chaining-details.md +31 -20
- package/skills/gentle-ai/SKILL.md +16 -21
- package/skills/issue-creation/SKILL.md +8 -2
- package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
- package/skills/work-unit-commits/SKILL.md +4 -3
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +30 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-fake-child.ts +2 -2
- package/tests/agents-message-delivery.test.ts +106 -0
- package/tests/agents-protocol.test.ts +97 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +467 -93
- package/tests/agents-view-thread-identity.test.ts +169 -0
- package/tests/agents-view.test.ts +8 -2
- package/tests/agents-widget.test.ts +154 -15
- package/tests/append-system-prompt-route.test.ts +160 -0
- package/tests/append-system-prompt.test.ts +46 -0
- package/tests/artifact-language.test.ts +19 -213
- package/tests/ask-user-question.test.ts +44 -1
- package/tests/asset-installation-runtime.test.ts +5 -16
- package/tests/auto-mode-invariants.test.ts +258 -0
- package/tests/auto-mode-sdk.test.ts +57 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-classifier.test.ts +283 -0
- package/tests/auto-permission-runtime.test.ts +196 -0
- package/tests/autonomous-guard.test.ts +69 -1
- package/tests/background-cache-warming.test.ts +2 -1
- package/tests/background-jobs.test.ts +227 -0
- package/tests/background-monitor.test.ts +195 -0
- package/tests/background-subagents-default-mode.test.ts +28 -5
- package/tests/bounded-stop-contract.test.ts +85 -0
- package/tests/bounded-writer-admission.test.ts +146 -0
- package/tests/branch-pr-skill.test.ts +43 -0
- package/tests/bridge-wake-lifecycle.test.ts +129 -0
- package/tests/bridge-wake-rendering.test.ts +56 -0
- package/tests/build-distribution.test.ts +269 -0
- package/tests/builtin-codemode-optout.test.ts +242 -0
- package/tests/bundled-install.test.ts +1319 -0
- package/tests/bundled-wizard.test.ts +505 -0
- package/tests/card-content-policy.test.ts +68 -0
- package/tests/card-style-policy.test.ts +87 -0
- package/tests/chained-pr-skill.test.ts +124 -0
- package/tests/child-codegraph-bootstrap.test.ts +74 -0
- package/tests/child-context-files.test.ts +255 -0
- package/tests/child-package-entrypoints.test.ts +65 -0
- package/tests/child-package-injection.test.ts +74 -0
- package/tests/child-safety.test.ts +94 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +596 -0
- package/tests/command-palette.test.ts +43 -7
- package/tests/context-threshold.test.ts +31 -0
- package/tests/delegate-for-reason-contract.test.ts +327 -0
- package/tests/delegated-key-learnings-contract.test.ts +3 -78
- package/tests/destructive-command-guard.test.ts +84 -0
- package/tests/devbinary/native-review-parity.devtest.ts +171 -3
- package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +288 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
- package/tests/generic-agent-tools.test.ts +69 -0
- package/tests/gentle-agents.test.ts +2804 -346
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +21 -4
- package/tests/gentle-ai-child-guards.test.ts +202 -0
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
- package/tests/gentle-ai-elapsed-store.test.ts +68 -0
- package/tests/gentle-ai-installer.test.ts +566 -62
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +493 -14
- package/tests/gentle-ai.test.ts +1427 -236
- package/tests/gentle-card-text.ts +2 -1
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +896 -115
- package/tests/gentle-shell-bundled.test.ts +1002 -0
- package/tests/gentle-shell-launcher.test.ts +333 -65
- package/tests/gentle-shell-resume-hint.test.ts +270 -0
- package/tests/gentle-shell-startup-flow.test.ts +75 -0
- package/tests/gentle-shell-startup-update.test.ts +85 -0
- package/tests/gentle-shell-uninstall.test.ts +978 -0
- package/tests/gentle-shell-update-panel.test.ts +158 -0
- package/tests/gentle-shell.test.ts +4216 -119
- package/tests/gentle-stats.test.ts +152 -0
- package/tests/gentle-theme.test.ts +4 -1
- package/tests/gentle-todo.test.ts +333 -10
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-atomic-write.test.ts +57 -0
- package/tests/history-capture-policy.test.ts +102 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/history-command-registration.test.ts +164 -0
- package/tests/history-dedupe-entries.test.ts +123 -0
- package/tests/history-delete-backfill.test.ts +190 -0
- package/tests/history-delete-confirm.test.ts +460 -0
- package/tests/history-dispatch.test.ts +180 -0
- package/tests/history-drain-hidden.test.ts +110 -0
- package/tests/history-drain-order.test.ts +98 -0
- package/tests/history-expanded-globals.test.ts +62 -0
- package/tests/history-gc.test.ts +832 -0
- package/tests/history-header-layout.test.ts +265 -0
- package/tests/history-hide-prompts.test.ts +275 -0
- package/tests/history-lazy-windowing.test.ts +508 -0
- package/tests/history-legacy-migrate-v2.test.ts +297 -0
- package/tests/history-load-shared-history.test.ts +53 -0
- package/tests/history-max-results-cap.test.ts +76 -0
- package/tests/history-multi-reader.test.ts +203 -0
- package/tests/history-off-path.test.ts +170 -0
- package/tests/history-openflow-integration.test.ts +173 -0
- package/tests/history-overlay-margin.test.ts +326 -0
- package/tests/history-preview-layout.test.ts +93 -0
- package/tests/history-registry.test.ts +143 -0
- package/tests/history-scope-delete.test.ts +411 -0
- package/tests/history-search-caret-keys.test.ts +142 -0
- package/tests/history-seed-bootstrap.test.ts +170 -0
- package/tests/history-seed-regen.test.ts +129 -0
- package/tests/history-selector-windowing.test.ts +94 -0
- package/tests/history-session-scan-directory.test.ts +87 -0
- package/tests/history-session-scan-extract.test.ts +583 -0
- package/tests/history-session-writer.test.ts +351 -0
- package/tests/history-store-paths.test.ts +79 -0
- package/tests/history-tombstone-exact.test.ts +139 -0
- package/tests/history-wheel-mouse.test.ts +242 -0
- package/tests/inprocess-reviewer.test.ts +49 -19
- package/tests/install-gentle-ai-failure.test.ts +69 -0
- package/tests/install-tui-mode-setting.test.ts +15 -0
- package/tests/install-wizard.test.ts +1223 -0
- package/tests/installer-bundles.test.ts +122 -0
- package/tests/installer-posix-bootstrap.test.ts +1201 -0
- package/tests/installer-preflight.test.ts +750 -0
- package/tests/installer-probes.test.ts +1106 -0
- package/tests/installer-runner.test.ts +2158 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/issue-creation-skill.test.ts +61 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +389 -0
- package/tests/main-channel.test.ts +344 -0
- package/tests/maintainer/provider-relay.maintest.ts +13 -12
- package/tests/model-price-ratio.test.ts +116 -0
- package/tests/model-profiles-preservation.test.ts +153 -0
- package/tests/model-routing-authority.test.ts +16 -0
- package/tests/model-routing-package.test.ts +10 -0
- package/tests/nan-provider-runtime.test.ts +156 -0
- package/tests/nan-provider.test.ts +481 -0
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +13 -1
- package/tests/native-review-cli.test.ts +239 -118
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +101 -4
- package/tests/native-review-parity.test.ts +12 -48
- package/tests/native-risk-evidence-parity.test.ts +21 -0
- package/tests/no-self-pause-contract.test.ts +87 -0
- package/tests/notification-audio-native.test.ts +281 -0
- package/tests/notification-audio-windows.test.ts +581 -0
- package/tests/notification-audio.test.ts +548 -0
- package/tests/notification-customize.test.ts +655 -0
- package/tests/notification-events.test.ts +115 -0
- package/tests/notification-policy.test.ts +201 -0
- package/tests/notification-pulse-client.test.ts +356 -0
- package/tests/notification-pulse-protocol.test.ts +213 -0
- package/tests/notification-pulse-stream.test.ts +320 -0
- package/tests/notification-scheduler.test.ts +258 -0
- package/tests/notification-ui.test.ts +157 -0
- package/tests/npm-pack-result.test.ts +41 -0
- package/tests/odd-integration.test.ts +76 -0
- package/tests/odd-phase-inference.test.ts +213 -0
- package/tests/odd-phase-loader.test.ts +253 -0
- package/tests/odd-phase.test.ts +307 -0
- package/tests/odd-routing-canonical-ratchet.test.ts +60 -14
- package/tests/odd-routing-contract.test.ts +158 -50
- package/tests/orchestrator-budget.test.ts +43 -45
- package/tests/orchestrator-catalog.test.ts +146 -0
- package/tests/orchestrator-consultation-sdk.test.ts +614 -0
- package/tests/orchestrator-consultation.test.ts +111 -0
- package/tests/orchestrator-discovery.test.ts +241 -0
- package/tests/orchestrator-helper-consent.test.ts +171 -0
- package/tests/orchestrator-helper.test.ts +297 -0
- package/tests/orchestrator-message-card.test.ts +190 -0
- package/tests/orchestrator-presence.test.ts +23 -0
- package/tests/orchestrator-rdd-ownership.test.ts +8 -7
- package/tests/orchestrator-scope.test.ts +107 -0
- package/tests/orchestrator-state.test.ts +114 -0
- package/tests/orchestrator-status-publication-contract.test.ts +34 -0
- package/tests/orchestrator-tool-card.test.ts +398 -0
- package/tests/orchestrator-work-search.test.ts +318 -0
- package/tests/orchestrator-work.test.ts +147 -0
- package/tests/overlay-repaint.test.ts +74 -0
- package/tests/package-manifest.test.ts +344 -123
- package/tests/packed-runner-owned-path.test.ts +80 -0
- package/tests/parallel-reason-contract.test.ts +21 -0
- package/tests/parallel-writer-admission.test.ts +210 -0
- package/tests/permission-mode-policy.test.ts +194 -0
- package/tests/persona-single-channel.test.ts +10 -18
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/provider-defect-handoff.test.ts +7 -14
- package/tests/quiet-bash-runtime.test.ts +76 -0
- package/tests/quiet-tool-rendering.test.ts +569 -187
- package/tests/rdd-aware-verification-contract.test.ts +103 -40
- package/tests/rdd-status-line.test.ts +9 -4
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/resume-hint-extension.test.ts +122 -0
- package/tests/review-agent-end-preflight.test.ts +237 -21
- package/tests/review-candidate-owner-retry.test.ts +22 -1
- package/tests/review-candidate-view.test.ts +399 -12
- package/tests/review-contract-prompt.test.ts +138 -44
- package/tests/review-controller-lock-status.test.ts +0 -1
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +866 -6
- package/tests/review-controller-workspace-root.test.ts +206 -6
- package/tests/review-controller.test.ts +2 -2
- package/tests/review-correction-lifecycle.test.ts +2 -1
- package/tests/review-gate.test.ts +7 -4
- package/tests/review-graph-schema.test.ts +2 -2
- package/tests/review-host-relay-routing.test.ts +342 -4
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +221 -1
- package/tests/review-json-arguments.test.ts +167 -0
- package/tests/review-last-event-closure.test.ts +157 -8
- package/tests/review-ledger-contract.test.ts +14 -37
- package/tests/review-policy-judgment-day.test.ts +1 -1
- package/tests/review-policy-ordinary.test.ts +2 -2
- package/tests/review-relay-transport-agent.test.ts +66 -1
- package/tests/review-reminder-receipt.test.ts +47 -1
- package/tests/review-risk-assessment.test.ts +594 -26
- package/tests/review-session-standing-permission-controller.test.ts +16 -17
- package/tests/review-session-standing-permission-ipc.test.ts +6 -5
- package/tests/review-sidebar-state.test.ts +402 -0
- package/tests/review-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +202 -0
- package/tests/runtime-harness.mjs +186 -800
- package/tests/runtime-metrics-children.test.ts +16 -23
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +451 -0
- package/tests/session-change-capture.test.ts +10 -10
- package/tests/session-changes-shell.test.ts +242 -48
- package/tests/session-changes.test.ts +17 -24
- package/tests/session-messaging-grants.test.ts +255 -0
- package/tests/session-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +381 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +134 -2
- package/tests/shell-bar.test.ts +422 -1
- package/tests/shell-card.test.ts +353 -1
- package/tests/shell-changes-view.test.ts +135 -32
- package/tests/shell-changes.test.ts +61 -62
- package/tests/shell-prompt.test.ts +94 -2
- package/tests/shell-render-cache-runtime.test.ts +69 -0
- package/tests/shell-render-cache.test.ts +129 -0
- package/tests/shell-sidebar-layout.test.ts +351 -22
- package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
- package/tests/shell-todo.test.ts +207 -3
- package/tests/shell-usage-view.test.ts +28 -1
- package/tests/shell-usage.test.ts +108 -0
- package/tests/short-check-routing-contract.test.ts +49 -0
- package/tests/skill-registry-watcher.test.ts +119 -0
- package/tests/skill-registry.test.ts +357 -2
- package/tests/startup-banner.test.ts +246 -4
- package/tests/startup-home-repo.test.ts +170 -0
- package/tests/stats-collector.test.ts +195 -0
- package/tests/stats-view.test.ts +202 -0
- package/tests/status-timing-diagnostics.test.ts +283 -0
- package/tests/support/orchestrator-modules.ts +19 -0
- package/tests/task-size-routing-contract.test.ts +241 -0
- package/tests/telemetry-trigger.test.ts +107 -20
- package/tests/test-strength-contract.test.ts +124 -0
- package/tests/theme-customization.test.ts +72 -0
- package/tests/verify-agent-contract.test.ts +90 -0
- package/tests/verify-first-launch-contract.test.ts +139 -0
- package/tests/verify-package-files.test.ts +56 -0
- package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
- package/tests/vim-editor-adapter.test.ts +804 -0
- package/tests/vim-normal-engine.test.ts +101 -0
- package/tests/vim-operator-engine.test.ts +215 -0
- package/tests/vim-policy.test.ts +19 -0
- package/tests/vim-visual-engine.test.ts +52 -0
- package/tests/visual-customization-policy.test.ts +139 -0
- package/tests/visual-customize-view.test.ts +696 -0
- package/tests/visual-profiles.test.ts +87 -0
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +271 -0
- package/tests/yolo-mode-runtime.test.ts +188 -0
- package/tests/yolo-mode.test.ts +289 -0
- package/tests/yolo-session-policy.test.ts +59 -0
- package/themes/Gentle.json +2 -1
- package/themes/Gentleman-Cute.json +2 -1
- package/themes/Gentleman-Sexy.json +2 -1
- package/assets/agents/sdd-apply.md +0 -159
- package/assets/agents/sdd-archive.md +0 -228
- package/assets/agents/sdd-design.md +0 -49
- package/assets/agents/sdd-explore.md +0 -48
- package/assets/agents/sdd-init.md +0 -56
- package/assets/agents/sdd-onboard.md +0 -52
- package/assets/agents/sdd-proposal.md +0 -64
- package/assets/agents/sdd-remediate.md +0 -37
- package/assets/agents/sdd-research.md +0 -49
- package/assets/agents/sdd-spec.md +0 -192
- package/assets/agents/sdd-status.md +0 -54
- package/assets/agents/sdd-tasks.md +0 -108
- package/assets/agents/sdd-verify.md +0 -124
- package/assets/chains/sdd-full.chain.md +0 -83
- package/assets/chains/sdd-plan.chain.md +0 -56
- package/assets/chains/sdd-verify.chain.md +0 -43
- package/assets/sdd-orchestrator-workflow.md +0 -319
- package/assets/support/sdd-status-contract.md +0 -77
- package/docs/assets/diagrams/sdd-cycle.svg +0 -14
- package/extensions/sdd-init.ts +0 -816
- package/lib/openspec-deltas.ts +0 -156
- package/lib/sdd-preflight.ts +0 -1066
- package/lib/sdd-research-capabilities.ts +0 -94
- package/lib/sdd-status.ts +0 -26
- package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
- package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
- package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
- package/tests/openspec-deltas.test.ts +0 -209
- package/tests/sdd-agent-tools.test.ts +0 -156
- package/tests/sdd-archive-replay.test.ts +0 -82
- package/tests/sdd-classical-continuation.test.ts +0 -74
- package/tests/sdd-execution-routing-contract.test.ts +0 -44
- package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
- package/tests/sdd-native-managed-uptake.test.ts +0 -243
- package/tests/sdd-no-attempts-contract.test.ts +0 -15
- package/tests/sdd-odd-integration.test.ts +0 -33
- package/tests/sdd-optional-research.test.ts +0 -124
- package/tests/sdd-planning-routing-contract.test.ts +0 -45
- package/tests/sdd-preflight-rpc-input.test.ts +0 -125
- package/tests/sdd-preflight.test.ts +0 -541
- package/tests/sdd-research-capabilities.test.ts +0 -114
- package/tests/sdd-research-live.test.ts +0 -241
- package/tests/sdd-selection-transport.test.ts +0 -653
- package/tests/sdd-status.test.ts +0 -9
- package/tests/sdd-task-truth.test.ts +0 -43
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
package/lib/shell-todo.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
|
-
import { CARD_TONE,
|
|
1
|
+
import { truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
|
+
import { CARD_TONE, panelHeaderRow, panelInnerWidth, renderCard, type CardTheme, type CardTone } from "./shell-card.ts";
|
|
3
3
|
import { sanitizeTerminalText } from "./terminal-theme.ts";
|
|
4
4
|
import { paintHoverable } from "./shell-hover.ts";
|
|
5
5
|
|
|
@@ -11,7 +11,11 @@ import { paintHoverable } from "./shell-hover.ts";
|
|
|
11
11
|
export const TODO_STATUS = {
|
|
12
12
|
PENDING: "pending",
|
|
13
13
|
IN_PROGRESS: "in_progress",
|
|
14
|
+
/** Open, but waiting on something outside the list; always carries a note naming it. */
|
|
15
|
+
BLOCKED: "blocked",
|
|
14
16
|
DONE: "done",
|
|
17
|
+
/** Will not be done, on purpose: terminal, and never counts as open. */
|
|
18
|
+
DROPPED: "dropped",
|
|
15
19
|
} as const;
|
|
16
20
|
|
|
17
21
|
export type TodoStatus = (typeof TODO_STATUS)[keyof typeof TODO_STATUS];
|
|
@@ -86,10 +90,20 @@ export interface TodoRenderOptions {
|
|
|
86
90
|
export const TODO_DETAILS_KEY = "gentleTodo";
|
|
87
91
|
export const TODO_TOOL_NAME = "todo";
|
|
88
92
|
export const TODO_GLYPH = "❀";
|
|
89
|
-
const STATUS_ALIASES: Record<string, TodoStatus> = {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
const STATUS_ALIASES: Record<string, TodoStatus> = {
|
|
94
|
+
completed: TODO_STATUS.DONE,
|
|
95
|
+
complete: TODO_STATUS.DONE,
|
|
96
|
+
doing: TODO_STATUS.IN_PROGRESS,
|
|
97
|
+
todo: TODO_STATUS.PENDING,
|
|
98
|
+
cancelled: TODO_STATUS.DROPPED,
|
|
99
|
+
canceled: TODO_STATUS.DROPPED,
|
|
100
|
+
abandoned: TODO_STATUS.DROPPED,
|
|
101
|
+
};
|
|
102
|
+
const STATUS_LIST = "pending, in_progress, blocked, done, or dropped";
|
|
103
|
+
const BLOCKED_NEEDS_NOTE = "blocked needs a note naming what it waits for";
|
|
104
|
+
const STATUS_GLYPH: Record<TodoStatus, string> = { [TODO_STATUS.PENDING]: "○", [TODO_STATUS.IN_PROGRESS]: "◐", [TODO_STATUS.BLOCKED]: "⊘", [TODO_STATUS.DONE]: "✓", [TODO_STATUS.DROPPED]: "✕" };
|
|
105
|
+
const STATUS_ROLE: Record<TodoStatus, string> = { [TODO_STATUS.PENDING]: "text", [TODO_STATUS.IN_PROGRESS]: "accent", [TODO_STATUS.BLOCKED]: "text", [TODO_STATUS.DONE]: "dim", [TODO_STATUS.DROPPED]: "dim" };
|
|
106
|
+
const GLYPH_ROLE: Record<TodoStatus, string> = { [TODO_STATUS.PENDING]: "muted", [TODO_STATUS.IN_PROGRESS]: "accent", [TODO_STATUS.BLOCKED]: "warning", [TODO_STATUS.DONE]: "success", [TODO_STATUS.DROPPED]: "muted" };
|
|
93
107
|
const NOTE_ROLE = "muted";
|
|
94
108
|
const STALE_AFTER_TURNS = 2;
|
|
95
109
|
/** Above this many rows the finished tasks fold into one line and the rest is capped. */
|
|
@@ -111,20 +125,30 @@ function parseStatus(value: unknown): TodoStatus | undefined {
|
|
|
111
125
|
return STATUS_ALIASES[normalized];
|
|
112
126
|
}
|
|
113
127
|
|
|
128
|
+
function countStatus(state: TodoState, status: TodoStatus): number {
|
|
129
|
+
return state.tasks.filter((task) => task.status === status).length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Dropped tasks are neither done nor open. */
|
|
114
133
|
export function todoSummary(state: TodoState): TodoSummary {
|
|
115
|
-
const done = state
|
|
116
|
-
return { done, total: state.tasks.length, open: state.tasks.length - done };
|
|
134
|
+
const done = countStatus(state, TODO_STATUS.DONE);
|
|
135
|
+
return { done, total: state.tasks.length, open: state.tasks.length - done - countStatus(state, TODO_STATUS.DROPPED) };
|
|
117
136
|
}
|
|
118
137
|
|
|
138
|
+
// Only work the model can pick up goes stale: a list whose open tasks all
|
|
139
|
+
// wait on something outside it has nothing to bring up to date.
|
|
119
140
|
export function staleTurns(state: TodoState, currentTurn: number): number {
|
|
120
|
-
|
|
141
|
+
const actionable = state.tasks.some((task) => task.status === TODO_STATUS.PENDING || task.status === TODO_STATUS.IN_PROGRESS);
|
|
142
|
+
if (state.updatedTurn === null || !actionable) return 0;
|
|
121
143
|
return Math.max(0, currentTurn - state.updatedTurn);
|
|
122
144
|
}
|
|
123
145
|
|
|
124
146
|
function summaryText(state: TodoState): string {
|
|
125
147
|
const { done, total } = todoSummary(state);
|
|
126
|
-
const inProgress = state
|
|
127
|
-
|
|
148
|
+
const inProgress = countStatus(state, TODO_STATUS.IN_PROGRESS);
|
|
149
|
+
const blocked = countStatus(state, TODO_STATUS.BLOCKED);
|
|
150
|
+
const dropped = countStatus(state, TODO_STATUS.DROPPED);
|
|
151
|
+
return `${total} ${total === 1 ? "task" : "tasks"} · ${done} done · ${inProgress} in progress${blocked ? ` · ${blocked} blocked` : ""}${dropped ? ` · ${dropped} dropped` : ""}`;
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
function taskLine(task: TodoTask): string {
|
|
@@ -135,8 +159,9 @@ function buildTask(input: TodoTaskInput, id: number): TodoTask | string {
|
|
|
135
159
|
const title = cleanText(input.title);
|
|
136
160
|
if (title.length === 0) return "title is required";
|
|
137
161
|
const status = parseStatus(input.status);
|
|
138
|
-
if (status === undefined) return `unknown status "${input.status}" (use
|
|
162
|
+
if (status === undefined) return `unknown status "${input.status}" (use ${STATUS_LIST})`;
|
|
139
163
|
const note = cleanText(input.note);
|
|
164
|
+
if (status === TODO_STATUS.BLOCKED && note.length === 0) return BLOCKED_NEEDS_NOTE;
|
|
140
165
|
return { id, title, status, ...(note ? { note } : {}) };
|
|
141
166
|
}
|
|
142
167
|
|
|
@@ -240,64 +265,92 @@ export function todoPromptBlock(state: TodoState, stale: number): string | undef
|
|
|
240
265
|
return [
|
|
241
266
|
"## Todo list",
|
|
242
267
|
"Keep it current with the `todo` tool: mark a task in_progress before starting it, done right after finishing it, and rewrite the whole list with `write` whenever the plan changes. Update it before you end the turn.",
|
|
268
|
+
"A blocked task waits on something outside the list: do not work on it until its condition holds. When the plan changes, mark the tasks it made obsolete dropped instead of leaving them pending or marking them done.",
|
|
243
269
|
...lines,
|
|
244
270
|
].join("\n") + staleLine;
|
|
245
271
|
}
|
|
246
272
|
|
|
247
|
-
function taskRow(task: TodoTask, theme: TodoTheme,
|
|
273
|
+
function taskRow(task: TodoTask, theme: TodoTheme, inner: number): string {
|
|
248
274
|
if (task.status === TODO_STATUS.DONE) {
|
|
249
275
|
// Wrap before applying SGR 9: every physical title closes its own style
|
|
250
276
|
// before renderCard adds padding and rails.
|
|
251
|
-
return wrapTextWithAnsi(task.title, Math.max(1,
|
|
277
|
+
return wrapTextWithAnsi(task.title, Math.max(1, inner - 2))
|
|
252
278
|
.map((line, index) => `${index === 0 ? theme.fg(GLYPH_ROLE[task.status], STATUS_GLYPH[task.status]) : " "} ${theme.fg(STATUS_ROLE[task.status], theme.strikethrough(line))}`)
|
|
253
279
|
.join("\n");
|
|
254
280
|
}
|
|
255
281
|
const title = task.title;
|
|
256
|
-
const
|
|
282
|
+
const showsNote = task.status === TODO_STATUS.IN_PROGRESS || task.status === TODO_STATUS.BLOCKED;
|
|
283
|
+
const note = showsNote && task.note ? ` ${theme.fg(NOTE_ROLE, "·")} ${theme.fg(NOTE_ROLE, task.note)}` : "";
|
|
257
284
|
return `${theme.fg(GLYPH_ROLE[task.status], STATUS_GLYPH[task.status])} ${theme.fg(STATUS_ROLE[task.status], title)}${note}`;
|
|
258
285
|
}
|
|
259
286
|
|
|
260
|
-
// Long lists keep the card short: done tasks fold into "✓ N done"
|
|
261
|
-
// open ones fill the remaining rows, with a
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const open = state.tasks.filter((task) => task.status !== TODO_STATUS.DONE);
|
|
287
|
+
// Long lists keep the card short: done and dropped tasks fold into "✓ N done"
|
|
288
|
+
// and "✕ N dropped", and the open ones fill the remaining rows, with a
|
|
289
|
+
// trailing count for the rest.
|
|
290
|
+
function bodyRows(state: TodoState, theme: TodoTheme, inner: number): string[] {
|
|
291
|
+
if (state.tasks.length <= ROW_CAP) return state.tasks.map((task) => taskRow(task, theme, inner));
|
|
292
|
+
const open = state.tasks.filter((task) => task.status !== TODO_STATUS.DONE && task.status !== TODO_STATUS.DROPPED);
|
|
266
293
|
const rows: string[] = [];
|
|
267
|
-
|
|
294
|
+
for (const [status, label] of [[TODO_STATUS.DONE, "done"], [TODO_STATUS.DROPPED, "dropped"]] as const) {
|
|
295
|
+
const count = countStatus(state, status);
|
|
296
|
+
if (count > 0) rows.push(`${theme.fg(GLYPH_ROLE[status], STATUS_GLYPH[status])} ${theme.fg(NOTE_ROLE, `${count} ${label}`)}`);
|
|
297
|
+
}
|
|
268
298
|
const room = ROW_CAP - rows.length - (open.length > ROW_CAP - rows.length ? 1 : 0);
|
|
269
|
-
for (const task of open.slice(0, room)) rows.push(taskRow(task, theme,
|
|
299
|
+
for (const task of open.slice(0, room)) rows.push(taskRow(task, theme, inner));
|
|
270
300
|
if (open.length > room) rows.push(theme.fg(NOTE_ROLE, `… ${open.length - room} more`));
|
|
271
301
|
return rows;
|
|
272
302
|
}
|
|
273
303
|
|
|
274
|
-
function collapsedRow(state: TodoState, theme: TodoTheme,
|
|
304
|
+
function collapsedRow(state: TodoState, theme: TodoTheme, inner: number): string {
|
|
275
305
|
const active = state.tasks.find((task) => task.status === TODO_STATUS.IN_PROGRESS);
|
|
276
|
-
if (active) return taskRow(active, theme,
|
|
306
|
+
if (active) return taskRow(active, theme, inner);
|
|
277
307
|
const pending = state.tasks.find((task) => task.status === TODO_STATUS.PENDING);
|
|
278
|
-
if (pending) return taskRow(pending, theme,
|
|
308
|
+
if (pending) return taskRow(pending, theme, inner);
|
|
309
|
+
const blocked = state.tasks.find((task) => task.status === TODO_STATUS.BLOCKED);
|
|
310
|
+
if (blocked) return taskRow(blocked, theme, inner);
|
|
279
311
|
const { open } = todoSummary(state);
|
|
280
312
|
return `${theme.fg(GLYPH_ROLE[TODO_STATUS.PENDING], STATUS_GLYPH[TODO_STATUS.PENDING])} ${theme.fg(NOTE_ROLE, `${open} open`)}`;
|
|
281
313
|
}
|
|
282
314
|
|
|
315
|
+
/** The tone the Todos card renders in: warning once the list went stale, info otherwise. */
|
|
316
|
+
export function todoCardTone(staleTurns: number): CardTone {
|
|
317
|
+
return staleTurns >= STALE_AFTER_TURNS ? CARD_TONE.WARNING : CARD_TONE.INFO;
|
|
318
|
+
}
|
|
319
|
+
|
|
283
320
|
export function renderTodoCard(state: TodoState, theme: TodoTheme, width: number, options: TodoRenderOptions): string[] {
|
|
284
321
|
if (state.tasks.length === 0) return [];
|
|
285
322
|
const { done, total } = todoSummary(state);
|
|
323
|
+
// Dropped work is not part of the plan anymore, so it leaves the denominator.
|
|
324
|
+
const planned = total - countStatus(state, TODO_STATUS.DROPPED);
|
|
286
325
|
const stale = options.staleTurns >= STALE_AFTER_TURNS;
|
|
287
326
|
const action = options.collapsed ? "expand" : "collapse";
|
|
288
327
|
const actionLabel = action[0]!.toUpperCase() + action.slice(1);
|
|
289
328
|
const icon = options.collapsed ? "▸" : "▾";
|
|
290
329
|
const control = `${icon} ${width >= 28 ? actionLabel : ""}`.trimEnd();
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
330
|
+
const tone = todoCardTone(options.staleTurns);
|
|
331
|
+
const hint = options.collapseKey
|
|
332
|
+
? panelHeaderRow(theme, width, tone) ? options.collapseKey : `${options.collapseKey} ${action}`
|
|
333
|
+
: undefined;
|
|
334
|
+
// Content columns of the card body in the active style (float is narrower).
|
|
335
|
+
const inner = panelInnerWidth(theme, width, tone);
|
|
336
|
+
const rows = options.collapsed ? [collapsedRow(state, theme, inner)] : options.scrollable ? state.tasks.map((task) => taskRow(task, theme, inner)) : bodyRows(state, theme, inner);
|
|
337
|
+
const staleLabel = theme.fg(NOTE_ROLE, `stale · ${options.staleTurns} turns`);
|
|
338
|
+
let body = options.collapsed
|
|
339
|
+
? [truncateToWidth(`${stale ? `${staleLabel} · ` : ""}${rows[0]}`, inner, "…")]
|
|
340
|
+
: stale ? [staleLabel, ...rows] : rows;
|
|
341
|
+
if (!options.collapsed && !options.scrollable) {
|
|
342
|
+
// Budget physical lines after wrapping: one long task can exceed the
|
|
343
|
+
// old task-count cap by itself. Scrollable hosts retain every line.
|
|
344
|
+
body = body.flatMap((row) => wrapTextWithAnsi(row, Math.max(1, inner)));
|
|
345
|
+
if (body.length > ROW_CAP) body = [...body.slice(0, ROW_CAP - 1), theme.fg(NOTE_ROLE, "… more")];
|
|
346
|
+
}
|
|
294
347
|
// The clickable control paints the shared hover role while hovered --
|
|
295
348
|
// same treatment every other clickable surface uses -- instead of its
|
|
296
349
|
// ordinary accent role.
|
|
297
350
|
return renderCard(
|
|
298
|
-
{ title: `Todos ${paintHoverable(theme, control, options.hovered, "accent")}`, subtitle: `${done} of ${
|
|
351
|
+
{ title: `Todos ${paintHoverable(theme, control, options.hovered, "accent")}`, subtitle: `${done} of ${planned}`, body, tone, glyph: TODO_GLYPH },
|
|
299
352
|
theme,
|
|
300
353
|
width,
|
|
301
|
-
{ expanded: true, hint },
|
|
354
|
+
{ expanded: true, hint, panel: true },
|
|
302
355
|
);
|
|
303
356
|
}
|
package/lib/shell-usage-view.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Key, matchesKey, truncateToWidth, visibleWidth, type TuiMouseEvent, type TuiMouseEventResult } from "@earendil-works/pi-tui";
|
|
2
|
-
import { renderUsagePanel, type ActiveProvider, type UsageSourceRegistry, type UsageStore, type UsageTheme } from "./shell-usage.ts";
|
|
2
|
+
import { renderUsagePanel, type ActiveProvider, type UsagePanelScope, type UsageSourceRegistry, type UsageStore, type UsageTheme } from "./shell-usage.ts";
|
|
3
3
|
import { paintHoverable } from "./shell-hover.ts";
|
|
4
4
|
|
|
5
5
|
// Gentle Shell subscriptions overlay: a framed panel over the usage store.
|
|
@@ -12,6 +12,10 @@ export interface UsageViewDeps {
|
|
|
12
12
|
// Optional: lets the panel resolve the pending note of a provider whose
|
|
13
13
|
// usage source was registered at runtime instead of built in.
|
|
14
14
|
registry?(): UsageSourceRegistry | undefined;
|
|
15
|
+
// Optional: the targeted routing scope. Without it the panel draws every
|
|
16
|
+
// provider the store holds; with it, exactly the targeted providers, in
|
|
17
|
+
// scope order, explaining the ones without data.
|
|
18
|
+
scope?(): UsagePanelScope | undefined;
|
|
15
19
|
onRefresh(): Promise<void>;
|
|
16
20
|
onClose(): void;
|
|
17
21
|
requestRender(): void;
|
|
@@ -80,8 +84,9 @@ export class UsageView {
|
|
|
80
84
|
// A failing usage fetch is the store's problem to report (its rows already
|
|
81
85
|
// carry the last error); the panel only clears its "refreshing" state. The
|
|
82
86
|
// rejection must never leave this method: an unhandled rejection is fatal
|
|
83
|
-
// to the whole shell on current Node.
|
|
84
|
-
|
|
87
|
+
// to the whole shell on current Node. Public so the overlay can dispatch
|
|
88
|
+
// its opening refresh instead of making the open wait for it.
|
|
89
|
+
refresh(): void {
|
|
85
90
|
if (this.refreshing) return;
|
|
86
91
|
this.refreshing = true;
|
|
87
92
|
this.deps.requestRender();
|
|
@@ -101,7 +106,7 @@ export class UsageView {
|
|
|
101
106
|
const inner = width - 2;
|
|
102
107
|
const title = this.refreshing ? REFRESHING : TITLE;
|
|
103
108
|
const top = theme.fg(FRAME_ROLE, "╭─ ") + theme.fg(TITLE_ROLE, title) + theme.fg(FRAME_ROLE, ` ${rule(inner - visibleWidth(title) - 3)}╮`);
|
|
104
|
-
const body = renderUsagePanel(this.store.all(), theme, inner - 2, this.deps.now(), this.deps.active(), this.deps.registry?.()).map(
|
|
109
|
+
const body = renderUsagePanel(this.store.all(), theme, inner - 2, this.deps.now(), this.deps.active(), this.deps.registry?.(), this.deps.scope?.()).map(
|
|
105
110
|
(line) => `${theme.fg(FRAME_ROLE, "│")} ${fit(line, inner - 2)} ${theme.fg(FRAME_ROLE, "│")}`,
|
|
106
111
|
);
|
|
107
112
|
const hints = KEYS.map(([key, label]) => ({ key, label, text: `${key} ${label}`, action: (key === "r" ? "refresh" : "close") as HintAction }));
|
package/lib/shell-usage.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { parseModelRef } from "./agents-config.ts";
|
|
2
3
|
import { paintGauge } from "./shell-gauge.ts";
|
|
3
4
|
|
|
4
5
|
// Gentle Shell subscription usage: the rate-limit windows each connected
|
|
@@ -125,6 +126,10 @@ const PENDING_NOTE: Record<string, string> = {
|
|
|
125
126
|
};
|
|
126
127
|
const UNSUPPORTED_NOTE = "no subscription usage for this provider";
|
|
127
128
|
const ACTIVE_MARK = "✿";
|
|
129
|
+
// What a targeted provider with no snapshot says after a refresh actually ran
|
|
130
|
+
// and answered nothing. Generic on purpose: it names no endpoint, key, or
|
|
131
|
+
// account detail, exactly like the other notes here.
|
|
132
|
+
export const USAGE_FETCH_FAILED_NOTE = "fetch failed · r to retry";
|
|
128
133
|
|
|
129
134
|
export interface ActiveProvider {
|
|
130
135
|
provider: string;
|
|
@@ -264,7 +269,7 @@ export function formatReset(resetAt: number | null, now: number): string {
|
|
|
264
269
|
}
|
|
265
270
|
|
|
266
271
|
function parseWindow(raw: RawWindow | null | undefined, now: number): UsageWindow | undefined {
|
|
267
|
-
if (!raw ||
|
|
272
|
+
if (!raw || !isFiniteNumber(raw.used_percent) || !isFiniteNumber(raw.limit_window_seconds) || raw.limit_window_seconds <= 0) return undefined;
|
|
268
273
|
const resetAt =
|
|
269
274
|
typeof raw.reset_at === "number" ? raw.reset_at * 1000 : typeof raw.reset_after_seconds === "number" ? now + raw.reset_after_seconds * 1000 : null;
|
|
270
275
|
return { label: windowLabel(raw.limit_window_seconds), usedPercent: raw.used_percent, windowSeconds: raw.limit_window_seconds, resetAt };
|
|
@@ -289,17 +294,18 @@ export function parseCodexUsage(payload: unknown, now: number): ProviderUsage {
|
|
|
289
294
|
return { provider: CODEX_PROVIDER, plan: typeof raw.plan_type === "string" ? raw.plan_type : undefined, limits, fetchedAt: now };
|
|
290
295
|
}
|
|
291
296
|
|
|
292
|
-
function headerWindow(headers: Record<string, string>, kind: "primary" | "secondary"
|
|
297
|
+
function headerWindow(headers: Record<string, string>, kind: "primary" | "secondary"): UsageWindow | undefined {
|
|
293
298
|
const used = Number.parseFloat(headers[`${HEADER_PREFIX}${kind}-used-percent`] ?? "");
|
|
294
299
|
if (!Number.isFinite(used)) return undefined;
|
|
295
|
-
|
|
300
|
+
// Missing or invalid duration is not a zero-minute quota or an assumed 5h window.
|
|
301
|
+
const seconds = Number(headers[`${HEADER_PREFIX}${kind}-window-minutes`]) * MINUTE;
|
|
302
|
+
if (!Number.isFinite(seconds) || seconds <= 0) return undefined;
|
|
296
303
|
const resetAt = Number.parseInt(headers[`${HEADER_PREFIX}${kind}-reset-at`] ?? "", 10);
|
|
297
|
-
const seconds = Number.isFinite(minutes) ? minutes * MINUTE : 0;
|
|
298
304
|
return { label: windowLabel(seconds), usedPercent: used, windowSeconds: seconds, resetAt: Number.isFinite(resetAt) ? resetAt * 1000 : null };
|
|
299
305
|
}
|
|
300
306
|
|
|
301
307
|
export function parseCodexHeaders(headers: Record<string, string>, now: number): ProviderUsage | undefined {
|
|
302
|
-
const windows = [headerWindow(headers, "primary"
|
|
308
|
+
const windows = [headerWindow(headers, "primary"), headerWindow(headers, "secondary")].filter((window): window is UsageWindow => window !== undefined);
|
|
303
309
|
if (windows.length === 0) return undefined;
|
|
304
310
|
const reached = headers[`${HEADER_PREFIX}rate-limit-reached-type`];
|
|
305
311
|
return { provider: CODEX_PROVIDER, plan: undefined, limits: [{ name: CODEX_MAIN_LIMIT, windows, limitReached: Boolean(reached) }], fetchedAt: now };
|
|
@@ -527,18 +533,66 @@ function updatedAgo(fetchedAt: number, now: number): string {
|
|
|
527
533
|
return minutes < 1 ? "updated just now" : `updated ${minutes}m ago`;
|
|
528
534
|
}
|
|
529
535
|
|
|
536
|
+
// The providers one refresh targets: the session's own provider first, then
|
|
537
|
+
// every provider the active profile's subagent routing names, deduplicated in
|
|
538
|
+
// first-seen order. A qualified ref ("provider/id") resolves directly; a bare
|
|
539
|
+
// model id resolves through the registry callback only when that callback can
|
|
540
|
+
// name exactly one provider, so an ambiguous or unknown ref never guesses one
|
|
541
|
+
// and is dropped instead.
|
|
542
|
+
export function usageScopeProviders(
|
|
543
|
+
mainProvider: string | undefined,
|
|
544
|
+
routingModels: ReadonlyArray<string | undefined> | undefined,
|
|
545
|
+
resolveBareModel?: (modelId: string) => string | undefined,
|
|
546
|
+
): string[] {
|
|
547
|
+
const providers = new Set<string>();
|
|
548
|
+
if (mainProvider) providers.add(mainProvider);
|
|
549
|
+
for (const model of routingModels ?? []) {
|
|
550
|
+
const ref = parseModelRef(model);
|
|
551
|
+
if (!ref) continue;
|
|
552
|
+
const provider = ref.provider ?? resolveBareModel?.(ref.id);
|
|
553
|
+
if (provider) providers.add(provider);
|
|
554
|
+
}
|
|
555
|
+
return [...providers];
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// The current routing scope the panel draws: which providers are targeted, and
|
|
559
|
+
// which of them failed their latest settled refresh. A provider recorded
|
|
560
|
+
// earlier but no longer targeted is simply absent — it is not current scope.
|
|
561
|
+
export interface UsagePanelScope {
|
|
562
|
+
providers: readonly string[];
|
|
563
|
+
// Actual failures only: a refresh that settled without a snapshot. A
|
|
564
|
+
// provider still fetching is never in here, so an in-flight refresh cannot
|
|
565
|
+
// read as failed, and a successful refresh removes the provider again.
|
|
566
|
+
failed: ReadonlySet<string>;
|
|
567
|
+
}
|
|
568
|
+
|
|
530
569
|
// The active provider comes first, marked with the petal, and explains
|
|
531
570
|
// itself when it has no data yet. Other providers seen this session follow.
|
|
532
|
-
|
|
571
|
+
// With a scope, the panel draws exactly the targeted providers in scope order:
|
|
572
|
+
// one with no snapshot explains itself (its latest refresh failed, or its
|
|
573
|
+
// pending note otherwise), one whose latest refresh failed after a good
|
|
574
|
+
// snapshot keeps that snapshot and says so beside it, and a provider recorded
|
|
575
|
+
// under a previous profile's routing is never presented as current scope after
|
|
576
|
+
// a profile switch.
|
|
577
|
+
export function renderUsagePanel(usages: ProviderUsage[], theme: UsageTheme, width: number, now: number, active?: ActiveProvider, registry?: UsageSourceRegistry, scope?: UsagePanelScope): string[] {
|
|
533
578
|
const activeUsage = active ? usages.find((usage) => usage.provider === active.provider) : undefined;
|
|
534
|
-
const
|
|
535
|
-
|
|
579
|
+
const byProvider = new Map(usages.map((usage) => [usage.provider, usage] as const));
|
|
580
|
+
const rows = scope
|
|
581
|
+
? scope.providers.map((provider) => ({ provider, usage: byProvider.get(provider) }))
|
|
582
|
+
: [...(active ? [{ provider: active.provider, usage: activeUsage }] : []), ...usages.filter((usage) => usage !== activeUsage).map((usage) => ({ provider: usage.provider, usage }))];
|
|
583
|
+
if (rows.length === 0) return [truncateToWidth(USAGE_EMPTY_MESSAGE, width, "…")];
|
|
536
584
|
const lines: string[] = [];
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
585
|
+
for (const row of rows) {
|
|
586
|
+
const mark = active?.provider === row.provider ? `${theme.fg(ROLE.LIMIT, ACTIVE_MARK)} ` : "";
|
|
587
|
+
if (!row.usage) {
|
|
588
|
+
// A settled refresh that answered nothing is a failure; a provider
|
|
589
|
+
// whose usage arrives with responses (Anthropic) or that has not
|
|
590
|
+
// failed keeps its pending note.
|
|
591
|
+
const note = scope?.failed.has(row.provider) ? USAGE_FETCH_FAILED_NOTE : providerNote(row.provider, registry);
|
|
592
|
+
lines.push(`${mark}${theme.fg(ROLE.PROVIDER, row.provider)} ${theme.fg(ROLE.SEPARATOR, "·")} ${theme.fg(ROLE.RESET, note)}`);
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
const usage = row.usage;
|
|
542
596
|
const plan = usage.plan ? ` ${theme.fg(ROLE.SEPARATOR, "·")} ${theme.fg(ROLE.PLAN, usage.plan)}` : "";
|
|
543
597
|
lines.push(`${mark}${theme.fg(ROLE.PROVIDER, usage.provider)}${plan} ${theme.fg(ROLE.SEPARATOR, "·")} ${theme.fg(ROLE.RESET, updatedAgo(usage.fetchedAt, now))}`);
|
|
544
598
|
// One row per window: the limit name, its meter, its percentage and the reset
|
|
@@ -555,6 +609,9 @@ export function renderUsagePanel(usages: ProviderUsage[], theme: UsageTheme, wid
|
|
|
555
609
|
const tail = reset.length > 0 ? ` ${theme.fg(ROLE.SEPARATOR, "·")} ${theme.fg(ROLE.RESET, reset)}` : "";
|
|
556
610
|
lines.push(` ${theme.fg(ROLE.LABEL, row.name.padEnd(nameWidth))} ${paintMeter(row.window.usedPercent, PANEL_METER_CELLS, theme)} ${theme.fg(ROLE.PERCENT, percent)}${tail}`);
|
|
557
611
|
}
|
|
612
|
+
// A failure after a good snapshot never hides either fact: the last good
|
|
613
|
+
// snapshot stays on its rows and the failed refresh says so beside it.
|
|
614
|
+
if (scope?.failed.has(row.provider)) lines.push(` ${theme.fg(ROLE.RESET, USAGE_FETCH_FAILED_NOTE)}`);
|
|
558
615
|
}
|
|
559
616
|
return lines.map((line) => truncateToWidth(line, width, "…"));
|
|
560
617
|
}
|