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
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 ExoPro. Inspired by @jasonish/pi-prompt-history
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
|
|
6
|
+
interface SharedHistoryEntry {
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isSharedHistoryEntry(value: unknown): value is SharedHistoryEntry {
|
|
11
|
+
if (!value || typeof value !== "object") return false;
|
|
12
|
+
return typeof (value as { text?: unknown }).text === "string";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function toSharedHistoryValue(value: unknown): string | null {
|
|
16
|
+
if (typeof value === "string") return value.length > 0 ? value : null;
|
|
17
|
+
if (isSharedHistoryEntry(value))
|
|
18
|
+
return value.text.length > 0 ? value.text : null;
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isNonEmptyString(value: string | null): value is string {
|
|
23
|
+
return typeof value === "string" && value.length > 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function loadSharedHistory(historyFile: string): string[] {
|
|
27
|
+
if (!fs.existsSync(historyFile)) return [];
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const raw = fs.readFileSync(historyFile, "utf8");
|
|
31
|
+
const parsed: unknown = JSON.parse(raw);
|
|
32
|
+
|
|
33
|
+
if (!Array.isArray(parsed)) return [];
|
|
34
|
+
|
|
35
|
+
return parsed.map(toSharedHistoryValue).filter(isNonEmptyString);
|
|
36
|
+
} catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
export interface PromptRecord {
|
|
2
|
+
text: string;
|
|
3
|
+
searchText: string;
|
|
4
|
+
/**
|
|
5
|
+
* Provenance (spec C3): set on records built from PromptEntry inputs;
|
|
6
|
+
* ABSENT on records built from bare strings so the pinned deepEqual
|
|
7
|
+
* record shape ({text, searchText}) stays byte-compatible (design §I).
|
|
8
|
+
*/
|
|
9
|
+
source?: PromptSource;
|
|
10
|
+
/** Session records only: the resolved ms-epoch ordering timestamp. */
|
|
11
|
+
ts?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Provenance of a prompt record or merge-loader entry (spec C3). */
|
|
15
|
+
export type PromptSource = "editor" | "session";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Merge-loader currency (pre-dedup, spec C3): one editor-store or
|
|
19
|
+
* session-derived prompt. Session entries carry the resolved ms-epoch `ts`;
|
|
20
|
+
* editor entries do not (block ordering at the seam, proposal R8).
|
|
21
|
+
*/
|
|
22
|
+
export interface PromptEntry {
|
|
23
|
+
text: string;
|
|
24
|
+
source: PromptSource;
|
|
25
|
+
ts?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface VisibleRange {
|
|
29
|
+
start: number;
|
|
30
|
+
end: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface VisiblePromptRecord {
|
|
34
|
+
index: number;
|
|
35
|
+
record: PromptRecord;
|
|
36
|
+
isSelected: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PiHistoryGlobals {
|
|
40
|
+
__piHistoryExpand?: () => void;
|
|
41
|
+
__piHistoryTrim?: () => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildPromptRecords(
|
|
45
|
+
entries: ReadonlyArray<string | PromptEntry>,
|
|
46
|
+
): PromptRecord[] {
|
|
47
|
+
return entries.map((entry): PromptRecord => {
|
|
48
|
+
if (typeof entry === "string") {
|
|
49
|
+
// Bare string input keeps the EXACT Change 2 runtime shape — the
|
|
50
|
+
// pinned deepEqual records carry only {text, searchText}.
|
|
51
|
+
return { text: entry, searchText: entry.toLowerCase() };
|
|
52
|
+
}
|
|
53
|
+
const record: PromptRecord = {
|
|
54
|
+
text: entry.text,
|
|
55
|
+
searchText: entry.text.toLowerCase(),
|
|
56
|
+
source: entry.source,
|
|
57
|
+
};
|
|
58
|
+
if (entry.ts !== undefined) {
|
|
59
|
+
record.ts = entry.ts;
|
|
60
|
+
}
|
|
61
|
+
return record;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function clampSelectedIndex(
|
|
66
|
+
selectedIndex: number,
|
|
67
|
+
total: number,
|
|
68
|
+
): number {
|
|
69
|
+
return Math.max(0, Math.min(selectedIndex, Math.max(0, total - 1)));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function clampPreviewOffset(
|
|
73
|
+
offset: number,
|
|
74
|
+
totalLines: number,
|
|
75
|
+
viewportRows: number,
|
|
76
|
+
): number {
|
|
77
|
+
return Math.max(0, Math.min(offset, Math.max(0, totalLines - viewportRows)));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function computeVisibleRange(
|
|
81
|
+
selectedIndex: number,
|
|
82
|
+
total: number,
|
|
83
|
+
maxVisible: number,
|
|
84
|
+
): VisibleRange {
|
|
85
|
+
if (total <= 0 || maxVisible <= 0) return { start: 0, end: 0 };
|
|
86
|
+
if (total <= maxVisible) return { start: 0, end: total };
|
|
87
|
+
|
|
88
|
+
const half = Math.floor(maxVisible / 2);
|
|
89
|
+
const start = Math.max(0, Math.min(selectedIndex - half, total - maxVisible));
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
start,
|
|
93
|
+
end: Math.min(start + maxVisible, total),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function moveSelectedIndex(
|
|
98
|
+
selectedIndex: number,
|
|
99
|
+
total: number,
|
|
100
|
+
delta: number,
|
|
101
|
+
): number {
|
|
102
|
+
if (total === 0) return 0;
|
|
103
|
+
return (selectedIndex + delta + total) % total;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function pageSelectedIndex(
|
|
107
|
+
selectedIndex: number,
|
|
108
|
+
total: number,
|
|
109
|
+
pageSize: number,
|
|
110
|
+
): number {
|
|
111
|
+
if (total === 0) return 0;
|
|
112
|
+
return clampSelectedIndex(selectedIndex + pageSize, total);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Normalization key for read-time dedup (spec C3): byte-matches the
|
|
117
|
+
* APPLIED patch key in nav/patches/editor.cjs (:480-:586) — whitespace
|
|
118
|
+
* runs collapse, then trim, then a 120-char prefix slice, then lowercase.
|
|
119
|
+
* The literal is the single-backslash applied-patch form; the raw patch
|
|
120
|
+
* file stores \\s+ only because its code sits inside a template literal.
|
|
121
|
+
* Shared by contract (spec C4): hide-prompts tombstone keys and the
|
|
122
|
+
* merge-history session-half tombstone filter MUST byte-match this key.
|
|
123
|
+
*/
|
|
124
|
+
export function promptDedupKey(entry: string): string {
|
|
125
|
+
return entry.replace(/\s+/g, " ").trim().slice(0, 120).toLowerCase();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Read-time dedup pass (spec C3): keep-first over input order (file order
|
|
130
|
+
* is newest-first, mirroring the patch's keep-first dedup-on-save), and
|
|
131
|
+
* empty-key entries (empty or whitespace-only) are skipped — excluded from
|
|
132
|
+
* the output and never usable as collision keys. Removes ONLY duplicate-
|
|
133
|
+
* normalized entries; no snapshot cap (filterPrompts still caps output).
|
|
134
|
+
* Generic over string | PromptEntry (WU3): over the COMBINED merged input
|
|
135
|
+
* the keep-first rule makes the editor copy win at the seam — objects pass
|
|
136
|
+
* through with provenance intact; no new dedup logic exists anywhere.
|
|
137
|
+
*/
|
|
138
|
+
export function dedupePromptEntries<T extends string | PromptEntry>(
|
|
139
|
+
entries: readonly T[],
|
|
140
|
+
): T[] {
|
|
141
|
+
const seen = new Set<string>();
|
|
142
|
+
const deduped: T[] = [];
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
const key =
|
|
145
|
+
typeof entry === "string"
|
|
146
|
+
? promptDedupKey(entry)
|
|
147
|
+
: promptDedupKey(entry.text);
|
|
148
|
+
if (key !== "" && !seen.has(key)) {
|
|
149
|
+
seen.add(key);
|
|
150
|
+
deduped.push(entry);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return deduped;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* First-paint window size (spec C1, AC-L1-1): min(initialBatch, total),
|
|
158
|
+
* floored at 0 — small stores open fully loaded (exhausted at open),
|
|
159
|
+
* identical to today's behavior for R <= INITIAL_BATCH.
|
|
160
|
+
*/
|
|
161
|
+
export function initialLoadedCount(
|
|
162
|
+
total: number,
|
|
163
|
+
initialBatch: number,
|
|
164
|
+
): number {
|
|
165
|
+
return Math.max(0, Math.min(initialBatch, total));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Prefetch trigger (spec C2's normative expression, AC-L2-1): growth fires
|
|
170
|
+
* iff rows remain unloaded AND the 0-based cursor sits within the final
|
|
171
|
+
* preloadBuffer rows of the loaded window. Reads UNFILTERED counts only —
|
|
172
|
+
* filteredRecords.length appears in no trigger arithmetic (AC-L2-2).
|
|
173
|
+
*/
|
|
174
|
+
export function shouldGrowWindow(
|
|
175
|
+
selectedIndex: number,
|
|
176
|
+
loadedCount: number,
|
|
177
|
+
totalCount: number,
|
|
178
|
+
preloadBuffer: number,
|
|
179
|
+
): boolean {
|
|
180
|
+
return (
|
|
181
|
+
loadedCount < totalCount && selectedIndex + preloadBuffer >= loadedCount
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* One growth step (spec C2, AC-L2-1): min(L + max(1, batchSize), R). The
|
|
187
|
+
* max(1, ·) guard also keeps loadedCountForTarget's loop terminating on a
|
|
188
|
+
* degenerate batch size.
|
|
189
|
+
*/
|
|
190
|
+
export function nextLoadedCount(
|
|
191
|
+
loadedCount: number,
|
|
192
|
+
totalCount: number,
|
|
193
|
+
batchSize: number,
|
|
194
|
+
): number {
|
|
195
|
+
const step = Math.max(1, batchSize);
|
|
196
|
+
return Math.min(loadedCount + step, totalCount);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* PgDn catch-up (spec C1, AC-L1-5): the smallest whole-batch count that
|
|
201
|
+
* strictly covers targetIndex (a 0-based master row), clamped at totalCount.
|
|
202
|
+
* No-op when the target is already covered or the window is exhausted.
|
|
203
|
+
* Terminates by construction: each step adds ≥ 1, bounded by totalCount.
|
|
204
|
+
*/
|
|
205
|
+
export function loadedCountForTarget(
|
|
206
|
+
loadedCount: number,
|
|
207
|
+
totalCount: number,
|
|
208
|
+
targetIndex: number,
|
|
209
|
+
batchSize: number,
|
|
210
|
+
): number {
|
|
211
|
+
let next = loadedCount;
|
|
212
|
+
while (next <= targetIndex && next < totalCount) {
|
|
213
|
+
next = nextLoadedCount(next, totalCount, batchSize);
|
|
214
|
+
}
|
|
215
|
+
return next;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Delete backfill (spec C4's two steps verbatim, AC-L4-1..3): decrement the
|
|
220
|
+
* window against the splice-shrunk snapshot; while unloaded rows remain,
|
|
221
|
+
* backfill one row (clamped) so the next unloaded record slides into the
|
|
222
|
+
* deleted slot and the visible list length stays stable; at exhaustion the
|
|
223
|
+
* decrement is the genuine shrink. Written stepwise — NOT the algebraic
|
|
224
|
+
* min(L, T') shortcut — so the unit tests pin the contract, not an
|
|
225
|
+
* equivalence. Callers guarantee the deleted row sits inside the loaded
|
|
226
|
+
* prefix (idx < loadedCount by construction).
|
|
227
|
+
*/
|
|
228
|
+
export function loadedCountAfterDelete(
|
|
229
|
+
loadedCount: number,
|
|
230
|
+
totalCountAfterSplice: number,
|
|
231
|
+
): number {
|
|
232
|
+
const decrement = loadedCount - 1;
|
|
233
|
+
if (decrement < totalCountAfterSplice) {
|
|
234
|
+
return Math.min(decrement + 1, totalCountAfterSplice);
|
|
235
|
+
}
|
|
236
|
+
return decrement;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Pure delete-flow planner (spec C4, design §F): maps a record's provenance
|
|
241
|
+
* to the two delete actions. "editor" deletes from the editor store on disk
|
|
242
|
+
* AND writes the tombstone (twin suppression — the session copy of the same
|
|
243
|
+
* text would otherwise resurface next open); "session" plans NOTHING —
|
|
244
|
+
* session-derived rows are read-only (slice-05 D1): transcripts are
|
|
245
|
+
* immutable and owned by Pi core, so the extension never deletes from or
|
|
246
|
+
* writes to them, and deleteCurrent guards the source before the flow.
|
|
247
|
+
* Takes source as a plain parameter (no member reads — the T23 provenance
|
|
248
|
+
* pin keeps overlay consumers source-agnostic outside deleteCurrent); the
|
|
249
|
+
* only consumer is the delete flow in history/index.ts.
|
|
250
|
+
*/
|
|
251
|
+
export function deletionActionsFor(
|
|
252
|
+
source: PromptSource,
|
|
253
|
+
): { deleteFromEditorStore: boolean; writeTombstone: boolean } {
|
|
254
|
+
if (source === "editor") {
|
|
255
|
+
return { deleteFromEditorStore: true, writeTombstone: true };
|
|
256
|
+
}
|
|
257
|
+
return { deleteFromEditorStore: false, writeTombstone: false };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* One transition of the modal delete confirmation (PR #1393 follow-up,
|
|
262
|
+
* slice-05 D3). Disarmed, only the delete combo matters: it ARMS the
|
|
263
|
+
* confirm and executes nothing. While armed the confirm is MODAL: `y`/`Y`
|
|
264
|
+
* executes, `n`/`N`/Esc cancels, and every other key — including a second
|
|
265
|
+
* press of the combo — is swallowed with the confirm still armed (nothing
|
|
266
|
+
* reaches the dispatch table or the search input). The TUI keybinding
|
|
267
|
+
* matches (ctrl+shift+backspace, escape) are computed by the caller via
|
|
268
|
+
* matchesKey and passed as plain booleans so this router stays pure and
|
|
269
|
+
* testable without the TUI; the y/n semantics read the raw data here.
|
|
270
|
+
* ONE definition: the selector's handleInput routes every armed-state key
|
|
271
|
+
* through this function.
|
|
272
|
+
*/
|
|
273
|
+
export interface DeleteConfirmStep {
|
|
274
|
+
/** The armed state AFTER this transition. */
|
|
275
|
+
armed: boolean;
|
|
276
|
+
/** True only when `y`/`Y` confirms the armed delete — run the flow. */
|
|
277
|
+
execute: boolean;
|
|
278
|
+
/** True when `n`/`N`/Esc cancels — disarm and resume normal input. */
|
|
279
|
+
cancel: boolean;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function deleteConfirmStep(
|
|
283
|
+
armed: boolean,
|
|
284
|
+
isDeleteKey: boolean,
|
|
285
|
+
isEscapeKey: boolean,
|
|
286
|
+
data: string,
|
|
287
|
+
): DeleteConfirmStep {
|
|
288
|
+
if (!armed) {
|
|
289
|
+
return isDeleteKey
|
|
290
|
+
? { armed: true, execute: false, cancel: false }
|
|
291
|
+
: { armed: false, execute: false, cancel: false };
|
|
292
|
+
}
|
|
293
|
+
if (data === "y" || data === "Y") {
|
|
294
|
+
return { armed: false, execute: true, cancel: false };
|
|
295
|
+
}
|
|
296
|
+
if (data === "n" || data === "N" || isEscapeKey) {
|
|
297
|
+
return { armed: false, execute: false, cancel: true };
|
|
298
|
+
}
|
|
299
|
+
return { armed: true, execute: false, cancel: false };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Confirmation copy shown in the footer while a delete is armed (PR
|
|
304
|
+
* #1393): one line, one variant — a y/n question carrying the standing
|
|
305
|
+
* guarantee that the session log keeps the original either way.
|
|
306
|
+
*/
|
|
307
|
+
export function deleteConfirmFooterText(): string {
|
|
308
|
+
return "Delete this prompt from history (y/n)? Prompt stays in session log";
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Toast copy when the store delete THROWS (PR #1393): the flow aborts
|
|
313
|
+
* before any tombstone write, so nothing was removed — the store keeps the
|
|
314
|
+
* prompt and no tombstone is written.
|
|
315
|
+
*/
|
|
316
|
+
export const STORE_DELETE_FAILED_TEXT =
|
|
317
|
+
"Store delete failed; nothing was removed.";
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Toast copy when the tombstone write fails on the EDITOR path (PR
|
|
321
|
+
* #1393): the store row was already removed, so only the hide failed —
|
|
322
|
+
* the prompt may reappear from session transcripts.
|
|
323
|
+
*/
|
|
324
|
+
export const EDITOR_HIDE_FAILED_TEXT =
|
|
325
|
+
"Deleted from the store, but hiding failed — the prompt may reappear from session transcripts.";
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Toast copy when some store files could not be read or rewritten: copies
|
|
329
|
+
* may remain on disk, and only the tombstone keeps them out of the list.
|
|
330
|
+
*/
|
|
331
|
+
export const STORE_DELETE_PARTIAL_TEXT =
|
|
332
|
+
"Some history files could not be rewritten; the prompt is hidden, but copies may remain on disk.";
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Toast copy when some store files could not be rewritten AND the tombstone
|
|
336
|
+
* write failed: nothing hides the copies that may remain on disk.
|
|
337
|
+
*/
|
|
338
|
+
export const STORE_DELETE_PARTIAL_HIDE_FAILED_TEXT =
|
|
339
|
+
"Some history files could not be rewritten and hiding failed — the prompt may reappear from those files or from session transcripts.";
|
|
340
|
+
|
|
341
|
+
/** The counts a scope delete reports (structural twin of store's SweepResult). */
|
|
342
|
+
export interface StoreSweepCounts {
|
|
343
|
+
filesAffected: number;
|
|
344
|
+
removed: number;
|
|
345
|
+
failed: number;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* What the delete flow does after the store sweep: proceed to the
|
|
350
|
+
* tombstone when anything was removed OR any file failed (a failed file
|
|
351
|
+
* may still hold a copy the tombstone must hide), surfacing an error
|
|
352
|
+
* notice for failures; stop quietly when there was nothing to delete.
|
|
353
|
+
*/
|
|
354
|
+
export function storeDeleteFollowUp(
|
|
355
|
+
counts: StoreSweepCounts,
|
|
356
|
+
): { proceed: boolean; notice?: string } {
|
|
357
|
+
if (counts.failed > 0) {
|
|
358
|
+
return { proceed: true, notice: STORE_DELETE_PARTIAL_TEXT };
|
|
359
|
+
}
|
|
360
|
+
return { proceed: counts.removed > 0 };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* The one error notice of an editor-path delete, chosen AFTER the tombstone
|
|
365
|
+
* write so it never claims the prompt is hidden when hiding failed. No
|
|
366
|
+
* notice for a clean sweep with a written tombstone.
|
|
367
|
+
*/
|
|
368
|
+
export function storeDeleteNotice(
|
|
369
|
+
counts: StoreSweepCounts,
|
|
370
|
+
hideFailed: boolean,
|
|
371
|
+
): string | undefined {
|
|
372
|
+
if (counts.failed > 0) {
|
|
373
|
+
return hideFailed
|
|
374
|
+
? STORE_DELETE_PARTIAL_HIDE_FAILED_TEXT
|
|
375
|
+
: STORE_DELETE_PARTIAL_TEXT;
|
|
376
|
+
}
|
|
377
|
+
return hideFailed ? EDITOR_HIDE_FAILED_TEXT : undefined;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function getVisiblePromptRecords(
|
|
381
|
+
records: PromptRecord[],
|
|
382
|
+
selectedIndex: number,
|
|
383
|
+
maxVisible: number,
|
|
384
|
+
): VisiblePromptRecord[] {
|
|
385
|
+
const { start, end } = computeVisibleRange(
|
|
386
|
+
selectedIndex,
|
|
387
|
+
records.length,
|
|
388
|
+
maxVisible,
|
|
389
|
+
);
|
|
390
|
+
return records.slice(start, end).map((record, offset) => ({
|
|
391
|
+
index: start + offset,
|
|
392
|
+
record,
|
|
393
|
+
isSelected: start + offset === selectedIndex,
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export async function withExpandedHistoryGlobals<T>(
|
|
398
|
+
globals: PiHistoryGlobals,
|
|
399
|
+
run: () => Promise<T>,
|
|
400
|
+
): Promise<T> {
|
|
401
|
+
globals.__piHistoryExpand?.();
|
|
402
|
+
try {
|
|
403
|
+
return await run();
|
|
404
|
+
} finally {
|
|
405
|
+
globals.__piHistoryTrim?.();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Full-snapshot visibility for non-empty queries (AC-L2-3r, user-directed
|
|
411
|
+
* 2026-09-08): searching must see the whole deduped snapshot, not just the
|
|
412
|
+
* loaded prefix. One-shot and idempotent — returns the total, never an
|
|
413
|
+
* incremental batch — so per-keypress growth stays impossible. Empty or
|
|
414
|
+
* whitespace-only queries leave the lazy window untouched.
|
|
415
|
+
*/
|
|
416
|
+
export function loadedCountForQuery(
|
|
417
|
+
loadedCount: number,
|
|
418
|
+
totalCount: number,
|
|
419
|
+
query: string,
|
|
420
|
+
): number {
|
|
421
|
+
return query.trim().length > 0 ? totalCount : loadedCount;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const MAX_RESULTS = 10000;
|
|
425
|
+
|
|
426
|
+
export function filterPrompts(
|
|
427
|
+
records: PromptRecord[],
|
|
428
|
+
query: string,
|
|
429
|
+
): PromptRecord[] {
|
|
430
|
+
const trimmed = query.trim();
|
|
431
|
+
if (!trimmed) return records.slice(0, MAX_RESULTS);
|
|
432
|
+
|
|
433
|
+
const tokens = trimmed.toLowerCase().split(/\s+/).filter(Boolean);
|
|
434
|
+
const filtered = records.filter((record) => {
|
|
435
|
+
return tokens.every((token) => record.searchText.includes(token));
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
return filtered.slice(0, MAX_RESULTS);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Cross-extension fullscreen-sidebar state contract (gentle-shell): stored on
|
|
443
|
+
* the shared ProcessTerminal under a global-registry symbol so any extension
|
|
444
|
+
* can read it without importing gentle-shell. Shape per lib/shell-sidebar.ts:
|
|
445
|
+
* `{ active: boolean; ownsHost?: () => boolean; railColumns?: number; ... }`.
|
|
446
|
+
*/
|
|
447
|
+
const SIDEBAR_STATE_SYMBOL = Symbol.for("gentle-pi.experimental-sidebar.state");
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Visual breathing room between the picker and the sidebar rail, added on top
|
|
451
|
+
* of the rail reservation (user-directed: 1 column, 2026-09-21).
|
|
452
|
+
*/
|
|
453
|
+
export const SIDEBAR_OVERLAY_PADDING = 1;
|
|
454
|
+
|
|
455
|
+
interface SidebarStateShape {
|
|
456
|
+
active?: unknown;
|
|
457
|
+
ownsHost?: () => unknown;
|
|
458
|
+
railColumns?: unknown;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Overlay right margin for the current terminal: the rail reservation the
|
|
463
|
+
* gentle-shell sidebar publishes (`railColumns`: rail width plus gap) plus
|
|
464
|
+
* padding while the rail is painting, else 0 (native full-window overlay).
|
|
465
|
+
* pi-tui resolves overlay width "100%" and the bottom-center anchor inside
|
|
466
|
+
* `[0, columns - margin)`, which is then exactly the editor column. Reads the
|
|
467
|
+
* terminal-owned state contract defensively — any absent, malformed, or
|
|
468
|
+
* non-owning state degrades to 0 so the picker keeps opening. Purity note:
|
|
469
|
+
* this returns the CURRENT margin per call; live refresh while an overlay
|
|
470
|
+
* stays open is the caller's job (the picker wires visible() plus a getter
|
|
471
|
+
* margin — pi-tui re-reads both every render).
|
|
472
|
+
*/
|
|
473
|
+
export function editorOverlayMargin(terminal: unknown): number {
|
|
474
|
+
if (typeof terminal !== "object" || terminal === null) return 0;
|
|
475
|
+
const state = (terminal as Record<symbol, unknown>)[SIDEBAR_STATE_SYMBOL] as
|
|
476
|
+
| SidebarStateShape
|
|
477
|
+
| undefined;
|
|
478
|
+
if (typeof state !== "object" || state === null) return 0;
|
|
479
|
+
if (state.active !== true || typeof state.ownsHost !== "function") return 0;
|
|
480
|
+
const railColumns = state.railColumns;
|
|
481
|
+
if (
|
|
482
|
+
typeof railColumns !== "number" ||
|
|
483
|
+
!Number.isInteger(railColumns) ||
|
|
484
|
+
railColumns <= 0
|
|
485
|
+
) {
|
|
486
|
+
return 0;
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
return state.ownsHost() === true
|
|
490
|
+
? railColumns + SIDEBAR_OVERLAY_PADDING
|
|
491
|
+
: 0;
|
|
492
|
+
} catch {
|
|
493
|
+
return 0;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Responsive picker-header mode at the current render width. */
|
|
498
|
+
export type HeaderLayoutMode = "inline" | "stacked" | "compact";
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Fit-driven header plan (user-directed responsive header): "inline" keeps
|
|
502
|
+
* title + counts + right-flushed radio on one row; "stacked" (tablet) deletes
|
|
503
|
+
* the spacer — the radio wraps to its own row under the full counts line;
|
|
504
|
+
* "compact" (mobile) further splits the counts off and abbreviates the radio.
|
|
505
|
+
* Thresholds derive from the ACTUAL text widths, so any count size flips the
|
|
506
|
+
* mode at the exact column where the previous layout stops fitting.
|
|
507
|
+
*/
|
|
508
|
+
export function planHeaderLayout(
|
|
509
|
+
width: number,
|
|
510
|
+
leftWidth: number,
|
|
511
|
+
radioWidth: number,
|
|
512
|
+
minGap: number,
|
|
513
|
+
): HeaderLayoutMode {
|
|
514
|
+
if (width >= leftWidth + minGap + radioWidth) return "inline";
|
|
515
|
+
if (width >= leftWidth) return "stacked";
|
|
516
|
+
return "compact";
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** Full scope radio: both scope labels spelled out. */
|
|
520
|
+
export const SCOPE_RADIO_FULL_PROJECT = "◉ Current project | ○ All projects";
|
|
521
|
+
export const SCOPE_RADIO_FULL_GLOBAL = "○ Current project | ◉ All projects";
|
|
522
|
+
/** Abbreviated radio: the ACTIVE scope keeps its full label, the other shortens. */
|
|
523
|
+
export const SCOPE_RADIO_COMPACT_PROJECT = "◉ Current project | ○ All";
|
|
524
|
+
export const SCOPE_RADIO_COMPACT_GLOBAL = "○ Current | ◉ All projects";
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Scope radio text for the current width: abbreviated only when the full
|
|
528
|
+
* radio cannot fit the row it would occupy (compact widths).
|
|
529
|
+
*/
|
|
530
|
+
export function scopeRadioText(
|
|
531
|
+
scope: "project" | "global",
|
|
532
|
+
compact: boolean,
|
|
533
|
+
): string {
|
|
534
|
+
if (scope === "project") {
|
|
535
|
+
return compact ? SCOPE_RADIO_COMPACT_PROJECT : SCOPE_RADIO_FULL_PROJECT;
|
|
536
|
+
}
|
|
537
|
+
return compact ? SCOPE_RADIO_COMPACT_GLOBAL : SCOPE_RADIO_FULL_GLOBAL;
|
|
538
|
+
}
|