@quickchatai/quickreview 0.0.0-stage → 1.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/LICENSE +23 -0
- package/README.md +385 -2
- package/THIRD-PARTY-NOTICES.md +134 -0
- package/agents/pass-abstraction.md +64 -0
- package/agents/pass-ask-human.md +134 -0
- package/agents/pass-backcompat.md +67 -0
- package/agents/pass-baseline.md +61 -0
- package/agents/pass-behaviour-delta.md +64 -0
- package/agents/pass-bounds.md +36 -0
- package/agents/pass-claims.md +31 -0
- package/agents/pass-concurrency.md +168 -0
- package/agents/pass-consistency.md +57 -0
- package/agents/pass-contracts.md +30 -0
- package/agents/pass-data-safety.md +54 -0
- package/agents/pass-deferred.md +35 -0
- package/agents/pass-delivery.md +37 -0
- package/agents/pass-duplication.md +60 -0
- package/agents/pass-intent.md +63 -0
- package/agents/pass-lint-conformance.md +46 -0
- package/agents/pass-maintainability.md +67 -0
- package/agents/pass-placement.md +68 -0
- package/agents/pass-prod-risk.md +24 -0
- package/agents/pass-rollout.md +32 -0
- package/agents/pass-roundtrip.md +46 -0
- package/agents/pass-security.md +31 -0
- package/agents/pass-simplicity.md +68 -0
- package/agents/pass-statelessness.md +27 -0
- package/agents/pass-style.md +37 -0
- package/agents/pass-sweep.md +57 -0
- package/agents/pass-tests.md +58 -0
- package/agents/pass-ui.md +23 -0
- package/agents/pass-validate.md +50 -0
- package/agents/pass-widened.md +46 -0
- package/agents/scan/pass-scan.md +86 -0
- package/bin/qcr.js +11 -0
- package/config.example.toml +1067 -0
- package/deploy/ansible/README.md +356 -0
- package/deploy/ansible/group_vars/all.yml +212 -0
- package/deploy/ansible/inventory.example.ini +25 -0
- package/deploy/ansible/playbook.yml +360 -0
- package/deploy/collect-secrets.sh +289 -0
- package/deploy/collect-slack-secrets.mjs +1190 -0
- package/deploy/github-app-manifest.json +31 -0
- package/deploy/manifest.schema.json +75 -0
- package/deploy/qcr-update.sh +803 -0
- package/deploy/reviewer/AGENTS.override.md +12 -0
- package/dist/extensions/cli/bash-guard-ambient.js +42 -0
- package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
- package/dist/extensions/cli/bash-guard.js +5 -0
- package/dist/extensions/cli/bash-guard.js.map +1 -0
- package/dist/extensions/cli/companion-guard.js +5 -0
- package/dist/extensions/cli/companion-guard.js.map +1 -0
- package/dist/extensions/cli/debug-round.js +8 -0
- package/dist/extensions/cli/debug-round.js.map +1 -0
- package/dist/extensions/cli/inject-bridge.js +5 -0
- package/dist/extensions/cli/inject-bridge.js.map +1 -0
- package/dist/extensions/cli/ledger-pin.js +5 -0
- package/dist/extensions/cli/ledger-pin.js.map +1 -0
- package/dist/extensions/cli/lens.js +13 -0
- package/dist/extensions/cli/lens.js.map +1 -0
- package/dist/extensions/cli/muted-theme.js +5 -0
- package/dist/extensions/cli/muted-theme.js.map +1 -0
- package/dist/extensions/cli/onboarding.js +5 -0
- package/dist/extensions/cli/onboarding.js.map +1 -0
- package/dist/extensions/cli/setup.js +5 -0
- package/dist/extensions/cli/setup.js.map +1 -0
- package/dist/extensions/cli/tui-view.js +5 -0
- package/dist/extensions/cli/tui-view.js.map +1 -0
- package/dist/extensions/companion-guard.js +361 -0
- package/dist/extensions/companion-guard.js.map +1 -0
- package/dist/extensions/debug-round.js +204 -0
- package/dist/extensions/debug-round.js.map +1 -0
- package/dist/extensions/inject-bridge.js +603 -0
- package/dist/extensions/inject-bridge.js.map +1 -0
- package/dist/extensions/muted-theme.js +12 -0
- package/dist/extensions/muted-theme.js.map +1 -0
- package/dist/extensions/onboarding.js +175 -0
- package/dist/extensions/onboarding.js.map +1 -0
- package/dist/extensions/setup-replay.js +269 -0
- package/dist/extensions/setup-replay.js.map +1 -0
- package/dist/extensions/setup-scan.js +998 -0
- package/dist/extensions/setup-scan.js.map +1 -0
- package/dist/extensions/setup-triage.js +175 -0
- package/dist/extensions/setup-triage.js.map +1 -0
- package/dist/extensions/setup.js +1160 -0
- package/dist/extensions/setup.js.map +1 -0
- package/dist/extensions/tui-view.js +389 -0
- package/dist/extensions/tui-view.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/client.js +376 -0
- package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/config.js +189 -0
- package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/index.js +421 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
- package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
- package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
- package/dist/src/anomalies.js +483 -0
- package/dist/src/anomalies.js.map +1 -0
- package/dist/src/answerworker.js +180 -0
- package/dist/src/answerworker.js.map +1 -0
- package/dist/src/appstamp.js +68 -0
- package/dist/src/appstamp.js.map +1 -0
- package/dist/src/archive/admission.js +3 -0
- package/dist/src/archive/admission.js.map +1 -0
- package/dist/src/archive/archivedb.js +474 -0
- package/dist/src/archive/archivedb.js.map +1 -0
- package/dist/src/archive/archiver.js +1770 -0
- package/dist/src/archive/archiver.js.map +1 -0
- package/dist/src/archive/archivewrite.js +198 -0
- package/dist/src/archive/archivewrite.js.map +1 -0
- package/dist/src/archive/bundle.js +266 -0
- package/dist/src/archive/bundle.js.map +1 -0
- package/dist/src/archive/dbmove.js +638 -0
- package/dist/src/archive/dbmove.js.map +1 -0
- package/dist/src/archive/duplicatecounts.js +80 -0
- package/dist/src/archive/duplicatecounts.js.map +1 -0
- package/dist/src/archive/eligibility.js +113 -0
- package/dist/src/archive/eligibility.js.map +1 -0
- package/dist/src/archive/extract.js +288 -0
- package/dist/src/archive/extract.js.map +1 -0
- package/dist/src/archive/fsutil.js +330 -0
- package/dist/src/archive/fsutil.js.map +1 -0
- package/dist/src/archive/gzfile.js +277 -0
- package/dist/src/archive/gzfile.js.map +1 -0
- package/dist/src/archive/kinds.js +22 -0
- package/dist/src/archive/kinds.js.map +1 -0
- package/dist/src/archive/livecheckpoint.js +178 -0
- package/dist/src/archive/livecheckpoint.js.map +1 -0
- package/dist/src/archive/liveretention.js +145 -0
- package/dist/src/archive/liveretention.js.map +1 -0
- package/dist/src/archive/manifest.js +284 -0
- package/dist/src/archive/manifest.js.map +1 -0
- package/dist/src/archive/overseer.js +283 -0
- package/dist/src/archive/overseer.js.map +1 -0
- package/dist/src/archive/piglobal.js +129 -0
- package/dist/src/archive/piglobal.js.map +1 -0
- package/dist/src/archive/plan.js +398 -0
- package/dist/src/archive/plan.js.map +1 -0
- package/dist/src/archive/prarchive.js +831 -0
- package/dist/src/archive/prarchive.js.map +1 -0
- package/dist/src/archive/prfiles.js +303 -0
- package/dist/src/archive/prfiles.js.map +1 -0
- package/dist/src/archive/report.js +169 -0
- package/dist/src/archive/report.js.map +1 -0
- package/dist/src/archive/restore.js +276 -0
- package/dist/src/archive/restore.js.map +1 -0
- package/dist/src/archive/rows.js +106 -0
- package/dist/src/archive/rows.js.map +1 -0
- package/dist/src/archive/seal.js +480 -0
- package/dist/src/archive/seal.js.map +1 -0
- package/dist/src/archive/status.js +528 -0
- package/dist/src/archive/status.js.map +1 -0
- package/dist/src/archive/storepass.js +409 -0
- package/dist/src/archive/storepass.js.map +1 -0
- package/dist/src/backfill.js +217 -0
- package/dist/src/backfill.js.map +1 -0
- package/dist/src/bench/cost.js +87 -0
- package/dist/src/bench/exec.js +76 -0
- package/dist/src/bench/values.js +14 -0
- package/dist/src/botcontrol.js +127 -0
- package/dist/src/botcontrol.js.map +1 -0
- package/dist/src/botworker.js +805 -0
- package/dist/src/botworker.js.map +1 -0
- package/dist/src/capture.js +234 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cases.js +1491 -0
- package/dist/src/cases.js.map +1 -0
- package/dist/src/channelfs.js +27 -0
- package/dist/src/channelfs.js.map +1 -0
- package/dist/src/checklist.js +312 -0
- package/dist/src/checklist.js.map +1 -0
- package/dist/src/ciretry.js +91 -0
- package/dist/src/ciretry.js.map +1 -0
- package/dist/src/cli.js +1916 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/cliops.js +333 -0
- package/dist/src/cliops.js.map +1 -0
- package/dist/src/clonedir.js +15 -0
- package/dist/src/clonedir.js.map +1 -0
- package/dist/src/commandeffects.js +43 -0
- package/dist/src/commandeffects.js.map +1 -0
- package/dist/src/commands.js +1312 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/companioninbox.js +203 -0
- package/dist/src/companioninbox.js.map +1 -0
- package/dist/src/compare.js +673 -0
- package/dist/src/compare.js.map +1 -0
- package/dist/src/config.js +1027 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/configcheck.js +111 -0
- package/dist/src/configcheck.js.map +1 -0
- package/dist/src/coreext.js +43 -0
- package/dist/src/coreext.js.map +1 -0
- package/dist/src/daemon.js +929 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/debuground.js +731 -0
- package/dist/src/debuground.js.map +1 -0
- package/dist/src/debugroundpi.js +219 -0
- package/dist/src/debugroundpi.js.map +1 -0
- package/dist/src/diagnostics.js +14 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/digest.js +139 -0
- package/dist/src/digest.js.map +1 -0
- package/dist/src/dispatch.js +1726 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/engine/answerguard.js +18 -0
- package/dist/src/engine/answerguard.js.map +1 -0
- package/dist/src/engine/botsdk.js +262 -0
- package/dist/src/engine/botsdk.js.map +1 -0
- package/dist/src/engine/caserunner-sdk.js +332 -0
- package/dist/src/engine/caserunner-sdk.js.map +1 -0
- package/dist/src/engine/debugpi.js +194 -0
- package/dist/src/engine/debugpi.js.map +1 -0
- package/dist/src/eod.js +473 -0
- package/dist/src/eod.js.map +1 -0
- package/dist/src/eodpinwatch.js +391 -0
- package/dist/src/eodpinwatch.js.map +1 -0
- package/dist/src/escalation.js +1009 -0
- package/dist/src/escalation.js.map +1 -0
- package/dist/src/eventloop.js +779 -0
- package/dist/src/eventloop.js.map +1 -0
- package/dist/src/execboundary.js +430 -0
- package/dist/src/execboundary.js.map +1 -0
- package/dist/src/feedback.js +915 -0
- package/dist/src/feedback.js.map +1 -0
- package/dist/src/findingscheck.js +200 -0
- package/dist/src/findingscheck.js.map +1 -0
- package/dist/src/fsdenied.js +26 -0
- package/dist/src/fsdenied.js.map +1 -0
- package/dist/src/ghread.js +181 -0
- package/dist/src/ghread.js.map +1 -0
- package/dist/src/github.js +1063 -0
- package/dist/src/github.js.map +1 -0
- package/dist/src/homes.js +103 -0
- package/dist/src/homes.js.map +1 -0
- package/dist/src/hostroute.js +24 -0
- package/dist/src/hostroute.js.map +1 -0
- package/dist/src/improvements.js +625 -0
- package/dist/src/improvements.js.map +1 -0
- package/dist/src/install.js +946 -0
- package/dist/src/install.js.map +1 -0
- package/dist/src/installkind.js +40 -0
- package/dist/src/installkind.js.map +1 -0
- package/dist/src/ipc/capability.js +286 -0
- package/dist/src/ipc/capability.js.map +1 -0
- package/dist/src/ipc/client.js +226 -0
- package/dist/src/ipc/client.js.map +1 -0
- package/dist/src/ipc/control.js +70 -0
- package/dist/src/ipc/control.js.map +1 -0
- package/dist/src/ipc/ghreadshim.js +77 -0
- package/dist/src/ipc/ghreadshim.js.map +1 -0
- package/dist/src/ipc/launcher.js +65 -0
- package/dist/src/ipc/launcher.js.map +1 -0
- package/dist/src/ipc/permits.js +293 -0
- package/dist/src/ipc/permits.js.map +1 -0
- package/dist/src/ipc/postshim.js +137 -0
- package/dist/src/ipc/postshim.js.map +1 -0
- package/dist/src/ipc/protocol.js +239 -0
- package/dist/src/ipc/protocol.js.map +1 -0
- package/dist/src/ipc/server.js +312 -0
- package/dist/src/ipc/server.js.map +1 -0
- package/dist/src/ipc/workerspec.js +148 -0
- package/dist/src/ipc/workerspec.js.map +1 -0
- package/dist/src/learner/learner.js +1272 -0
- package/dist/src/learner/learner.js.map +1 -0
- package/dist/src/learner/miner.js +948 -0
- package/dist/src/learner/miner.js.map +1 -0
- package/dist/src/learner/packet.js +216 -0
- package/dist/src/learner/packet.js.map +1 -0
- package/dist/src/learner/receipt.js +135 -0
- package/dist/src/learner/receipt.js.map +1 -0
- package/dist/src/learner/status.js +38 -0
- package/dist/src/learner/status.js.map +1 -0
- package/dist/src/ledgercheck.js +120 -0
- package/dist/src/ledgercheck.js.map +1 -0
- package/dist/src/lessonledger.js +451 -0
- package/dist/src/lessonledger.js.map +1 -0
- package/dist/src/lessonselect.js +639 -0
- package/dist/src/lessonselect.js.map +1 -0
- package/dist/src/lessonshadow.js +540 -0
- package/dist/src/lessonshadow.js.map +1 -0
- package/dist/src/levers.js +302 -0
- package/dist/src/levers.js.map +1 -0
- package/dist/src/live/projection.js +450 -0
- package/dist/src/live/projection.js.map +1 -0
- package/dist/src/live/publish.js +83 -0
- package/dist/src/live/publish.js.map +1 -0
- package/dist/src/live/read.js +60 -0
- package/dist/src/live/read.js.map +1 -0
- package/dist/src/livecli.js +288 -0
- package/dist/src/livecli.js.map +1 -0
- package/dist/src/loginhint.js +42 -0
- package/dist/src/loginhint.js.map +1 -0
- package/dist/src/loginrefresh/atomic.js +66 -0
- package/dist/src/loginrefresh/atomic.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +93 -0
- package/dist/src/loginrefresh/lock.js.map +1 -0
- package/dist/src/loginrefresh/main.js +58 -0
- package/dist/src/loginrefresh/main.js.map +1 -0
- package/dist/src/loginrefresh/pirefresh.js +49 -0
- package/dist/src/loginrefresh/pirefresh.js.map +1 -0
- package/dist/src/loginrefresh/refresher.js +286 -0
- package/dist/src/loginrefresh/refresher.js.map +1 -0
- package/dist/src/loginrefresh/supervisor.js +253 -0
- package/dist/src/loginrefresh/supervisor.js.map +1 -0
- package/dist/src/loginrefresh/types.js +4 -0
- package/dist/src/loginrefresh/types.js.map +1 -0
- package/dist/src/memory/context.js +251 -0
- package/dist/src/memory/context.js.map +1 -0
- package/dist/src/memory/datafence.js +5 -0
- package/dist/src/memory/datafence.js.map +1 -0
- package/dist/src/memory/delivery.js +131 -0
- package/dist/src/memory/delivery.js.map +1 -0
- package/dist/src/memory/eventtags.js +81 -0
- package/dist/src/memory/eventtags.js.map +1 -0
- package/dist/src/memory/health.js +713 -0
- package/dist/src/memory/health.js.map +1 -0
- package/dist/src/memory/import.js +504 -0
- package/dist/src/memory/import.js.map +1 -0
- package/dist/src/memory/layout.js +252 -0
- package/dist/src/memory/layout.js.map +1 -0
- package/dist/src/memory/legacy-file.js +100 -0
- package/dist/src/memory/legacy-file.js.map +1 -0
- package/dist/src/memory/lessons.js +370 -0
- package/dist/src/memory/lessons.js.map +1 -0
- package/dist/src/memory/ovsession.js +449 -0
- package/dist/src/memory/ovsession.js.map +1 -0
- package/dist/src/memory/relayout.js +259 -0
- package/dist/src/memory/relayout.js.map +1 -0
- package/dist/src/memory/roundactivity.js +180 -0
- package/dist/src/memory/roundactivity.js.map +1 -0
- package/dist/src/memory/selection.js +690 -0
- package/dist/src/memory/selection.js.map +1 -0
- package/dist/src/memory/shadowstatus.js +69 -0
- package/dist/src/memory/shadowstatus.js.map +1 -0
- package/dist/src/memory/store.js +83 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/memory/textidentity.js +8 -0
- package/dist/src/memory/textidentity.js.map +1 -0
- package/dist/src/metrics.js +773 -0
- package/dist/src/metrics.js.map +1 -0
- package/dist/src/missingconfig.js +8 -0
- package/dist/src/missingconfig.js.map +1 -0
- package/dist/src/npmverbs.js +226 -0
- package/dist/src/npmverbs.js.map +1 -0
- package/dist/src/onboard/choices.js +191 -0
- package/dist/src/onboard/choices.js.map +1 -0
- package/dist/src/onboard/engine.js +344 -0
- package/dist/src/onboard/engine.js.map +1 -0
- package/dist/src/onboard/launch.js +83 -0
- package/dist/src/onboard/launch.js.map +1 -0
- package/dist/src/onboard/steps/appslug.js +211 -0
- package/dist/src/onboard/steps/appslug.js.map +1 -0
- package/dist/src/onboard/steps/bringup.js +408 -0
- package/dist/src/onboard/steps/bringup.js.map +1 -0
- package/dist/src/onboard/steps/compose.js +343 -0
- package/dist/src/onboard/steps/compose.js.map +1 -0
- package/dist/src/onboard/steps/configdelivery.js +50 -0
- package/dist/src/onboard/steps/configdelivery.js.map +1 -0
- package/dist/src/onboard/steps/configfill.js +1148 -0
- package/dist/src/onboard/steps/configfill.js.map +1 -0
- package/dist/src/onboard/steps/deploysh.js +80 -0
- package/dist/src/onboard/steps/deploysh.js.map +1 -0
- package/dist/src/onboard/steps/embeddings.js +472 -0
- package/dist/src/onboard/steps/embeddings.js.map +1 -0
- package/dist/src/onboard/steps/escalation.js +571 -0
- package/dist/src/onboard/steps/escalation.js.map +1 -0
- package/dist/src/onboard/steps/githubapp.js +1092 -0
- package/dist/src/onboard/steps/githubapp.js.map +1 -0
- package/dist/src/onboard/steps/golive.js +506 -0
- package/dist/src/onboard/steps/golive.js.map +1 -0
- package/dist/src/onboard/steps/index.js +67 -0
- package/dist/src/onboard/steps/index.js.map +1 -0
- package/dist/src/onboard/steps/ingress.js +762 -0
- package/dist/src/onboard/steps/ingress.js.map +1 -0
- package/dist/src/onboard/steps/inventory.js +420 -0
- package/dist/src/onboard/steps/inventory.js.map +1 -0
- package/dist/src/onboard/steps/llmauth.js +893 -0
- package/dist/src/onboard/steps/llmauth.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +1060 -0
- package/dist/src/onboard/steps/memory.js.map +1 -0
- package/dist/src/onboard/steps/memorykeys.js +43 -0
- package/dist/src/onboard/steps/memorykeys.js.map +1 -0
- package/dist/src/onboard/steps/preflight.js +172 -0
- package/dist/src/onboard/steps/preflight.js.map +1 -0
- package/dist/src/onboard/steps/proof.js +502 -0
- package/dist/src/onboard/steps/proof.js.map +1 -0
- package/dist/src/onboard/steps/ssh.js +300 -0
- package/dist/src/onboard/steps/ssh.js.map +1 -0
- package/dist/src/onboard/steps/topology.js +291 -0
- package/dist/src/onboard/steps/topology.js.map +1 -0
- package/dist/src/onboard/steps/transport.js +511 -0
- package/dist/src/onboard/steps/transport.js.map +1 -0
- package/dist/src/onboard/steps/verifyup.js +362 -0
- package/dist/src/onboard/steps/verifyup.js.map +1 -0
- package/dist/src/onboard/steps/vmdeploy.js +368 -0
- package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
- package/dist/src/onboard/toml.js +53 -0
- package/dist/src/onboard/toml.js.map +1 -0
- package/dist/src/opencli.js +508 -0
- package/dist/src/opencli.js.map +1 -0
- package/dist/src/openpr.js +457 -0
- package/dist/src/openpr.js.map +1 -0
- package/dist/src/openviking/client.js +821 -0
- package/dist/src/openviking/client.js.map +1 -0
- package/dist/src/openviking/context.js +218 -0
- package/dist/src/openviking/context.js.map +1 -0
- package/dist/src/openviking/embedder.js +457 -0
- package/dist/src/openviking/embedder.js.map +1 -0
- package/dist/src/openviking/gapsink.js +38 -0
- package/dist/src/openviking/gapsink.js.map +1 -0
- package/dist/src/openviking/health.js +69 -0
- package/dist/src/openviking/health.js.map +1 -0
- package/dist/src/openviking/index.js +24 -0
- package/dist/src/openviking/index.js.map +1 -0
- package/dist/src/openviking/lessonfind.js +54 -0
- package/dist/src/openviking/lessonfind.js.map +1 -0
- package/dist/src/openviking/probe.js +89 -0
- package/dist/src/openviking/probe.js.map +1 -0
- package/dist/src/openviking/proc.js +95 -0
- package/dist/src/openviking/proc.js.map +1 -0
- package/dist/src/openviking/provisionreport.js +144 -0
- package/dist/src/openviking/provisionreport.js.map +1 -0
- package/dist/src/openviking/reembed.js +317 -0
- package/dist/src/openviking/reembed.js.map +1 -0
- package/dist/src/openviking/relayout.js +153 -0
- package/dist/src/openviking/relayout.js.map +1 -0
- package/dist/src/openviking/sidecar-main.js +39 -0
- package/dist/src/openviking/sidecar-main.js.map +1 -0
- package/dist/src/openviking/sidecar.js +79 -0
- package/dist/src/openviking/sidecar.js.map +1 -0
- package/dist/src/openviking/store.js +1758 -0
- package/dist/src/openviking/store.js.map +1 -0
- package/dist/src/openviking/supervisor.js +422 -0
- package/dist/src/openviking/supervisor.js.map +1 -0
- package/dist/src/openviking/types.js +5 -0
- package/dist/src/openviking/types.js.map +1 -0
- package/dist/src/operator-recovery.js +172 -0
- package/dist/src/operator-recovery.js.map +1 -0
- package/dist/src/operatormessage.js +44 -0
- package/dist/src/operatormessage.js.map +1 -0
- package/dist/src/outcomes.js +1361 -0
- package/dist/src/outcomes.js.map +1 -0
- package/dist/src/piauthcopy.js +5 -0
- package/dist/src/piauthcopy.js.map +1 -0
- package/dist/src/picli.js +45 -0
- package/dist/src/picli.js.map +1 -0
- package/dist/src/picwd.js +25 -0
- package/dist/src/picwd.js.map +1 -0
- package/dist/src/pilogin.js +35 -0
- package/dist/src/pilogin.js.map +1 -0
- package/dist/src/pingpong.js +1028 -0
- package/dist/src/pingpong.js.map +1 -0
- package/dist/src/posting-journal.js +441 -0
- package/dist/src/posting-journal.js.map +1 -0
- package/dist/src/posting-outbox.js +662 -0
- package/dist/src/posting-outbox.js.map +1 -0
- package/dist/src/posting-receipt.js +203 -0
- package/dist/src/posting-receipt.js.map +1 -0
- package/dist/src/posting.js +348 -0
- package/dist/src/posting.js.map +1 -0
- package/dist/src/postops.js +637 -0
- package/dist/src/postops.js.map +1 -0
- package/dist/src/pr-activity.js +11 -0
- package/dist/src/pr-activity.js.map +1 -0
- package/dist/src/pr-author.js +5 -0
- package/dist/src/pr-author.js.map +1 -0
- package/dist/src/pr-conversation.js +355 -0
- package/dist/src/pr-conversation.js.map +1 -0
- package/dist/src/pr-session-lifecycle.js +542 -0
- package/dist/src/pr-session-lifecycle.js.map +1 -0
- package/dist/src/preflight.js +341 -0
- package/dist/src/preflight.js.map +1 -0
- package/dist/src/priorresolve.js +275 -0
- package/dist/src/priorresolve.js.map +1 -0
- package/dist/src/qcrbin.js +180 -0
- package/dist/src/qcrbin.js.map +1 -0
- package/dist/src/recheck.js +1398 -0
- package/dist/src/recheck.js.map +1 -0
- package/dist/src/reconcile.js +820 -0
- package/dist/src/reconcile.js.map +1 -0
- package/dist/src/recordedfindings.js +15 -0
- package/dist/src/recordedfindings.js.map +1 -0
- package/dist/src/regression.js +546 -0
- package/dist/src/regression.js.map +1 -0
- package/dist/src/replay.js +598 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/requiredpasses.js +113 -0
- package/dist/src/requiredpasses.js.map +1 -0
- package/dist/src/resumecheck.js +168 -0
- package/dist/src/resumecheck.js.map +1 -0
- package/dist/src/reviewcli.js +671 -0
- package/dist/src/reviewcli.js.map +1 -0
- package/dist/src/reviewcontext.js +22 -0
- package/dist/src/reviewcontext.js.map +1 -0
- package/dist/src/reviewdetach.js +574 -0
- package/dist/src/reviewdetach.js.map +1 -0
- package/dist/src/reviewhistory.js +284 -0
- package/dist/src/reviewhistory.js.map +1 -0
- package/dist/src/reviewhome.js +26 -0
- package/dist/src/reviewhome.js.map +1 -0
- package/dist/src/reviewmemory.js +110 -0
- package/dist/src/reviewmemory.js.map +1 -0
- package/dist/src/reviewrendercli.js +104 -0
- package/dist/src/reviewrendercli.js.map +1 -0
- package/dist/src/round-work.js +20 -0
- package/dist/src/round-work.js.map +1 -0
- package/dist/src/runhost.js +72 -0
- package/dist/src/runhost.js.map +1 -0
- package/dist/src/scanauth.js +25 -0
- package/dist/src/scanauth.js.map +1 -0
- package/dist/src/scanbox.js +846 -0
- package/dist/src/scanbox.js.map +1 -0
- package/dist/src/scancli.js +1074 -0
- package/dist/src/scancli.js.map +1 -0
- package/dist/src/scandriverlog.js +30 -0
- package/dist/src/scandriverlog.js.map +1 -0
- package/dist/src/session-bridge.js +75 -0
- package/dist/src/session-bridge.js.map +1 -0
- package/dist/src/session-command-payload.js +29 -0
- package/dist/src/session-command-payload.js.map +1 -0
- package/dist/src/session-command-supervisor.js +939 -0
- package/dist/src/session-command-supervisor.js.map +1 -0
- package/dist/src/sessionjsonl.js +85 -0
- package/dist/src/sessionjsonl.js.map +1 -0
- package/dist/src/sessions.js +541 -0
- package/dist/src/sessions.js.map +1 -0
- package/dist/src/setup/adapters.js +330 -0
- package/dist/src/setup/adapters.js.map +1 -0
- package/dist/src/setup/baselines.js +64 -0
- package/dist/src/setup/baselines.js.map +1 -0
- package/dist/src/setup/caserunner.js +554 -0
- package/dist/src/setup/caserunner.js.map +1 -0
- package/dist/src/setup/configcommit.js +358 -0
- package/dist/src/setup/configcommit.js.map +1 -0
- package/dist/src/setup/engine.js +672 -0
- package/dist/src/setup/engine.js.map +1 -0
- package/dist/src/setup/facts.js +593 -0
- package/dist/src/setup/facts.js.map +1 -0
- package/dist/src/setup/findings.js +948 -0
- package/dist/src/setup/findings.js.map +1 -0
- package/dist/src/setup/history.js +338 -0
- package/dist/src/setup/history.js.map +1 -0
- package/dist/src/setup/interview.js +185 -0
- package/dist/src/setup/interview.js.map +1 -0
- package/dist/src/setup/ledger.js +207 -0
- package/dist/src/setup/ledger.js.map +1 -0
- package/dist/src/setup/pins.js +43 -0
- package/dist/src/setup/pins.js.map +1 -0
- package/dist/src/setup/portrait.js +432 -0
- package/dist/src/setup/portrait.js.map +1 -0
- package/dist/src/setup/presentation.js +9 -0
- package/dist/src/setup/presentation.js.map +1 -0
- package/dist/src/setup/profile.js +417 -0
- package/dist/src/setup/profile.js.map +1 -0
- package/dist/src/setup/replay.js +240 -0
- package/dist/src/setup/replay.js.map +1 -0
- package/dist/src/setup/replaybuild.js +311 -0
- package/dist/src/setup/replaybuild.js.map +1 -0
- package/dist/src/setup/replayrun.js +806 -0
- package/dist/src/setup/replayrun.js.map +1 -0
- package/dist/src/setup/replaysource.js +169 -0
- package/dist/src/setup/replaysource.js.map +1 -0
- package/dist/src/setup/retune.js +333 -0
- package/dist/src/setup/retune.js.map +1 -0
- package/dist/src/setup/retunelog.js +51 -0
- package/dist/src/setup/retunelog.js.map +1 -0
- package/dist/src/setup/scan.js +725 -0
- package/dist/src/setup/scan.js.map +1 -0
- package/dist/src/setup/scanbuild.js +133 -0
- package/dist/src/setup/scanbuild.js.map +1 -0
- package/dist/src/setup/scanfacts.js +96 -0
- package/dist/src/setup/scanfacts.js.map +1 -0
- package/dist/src/setup/scanlease.js +251 -0
- package/dist/src/setup/scanlease.js.map +1 -0
- package/dist/src/setup/scanreport.js +289 -0
- package/dist/src/setup/scanreport.js.map +1 -0
- package/dist/src/setup/scanrun.js +1733 -0
- package/dist/src/setup/scanrun.js.map +1 -0
- package/dist/src/setup/scantree.js +257 -0
- package/dist/src/setup/scantree.js.map +1 -0
- package/dist/src/setup/stage2.js +617 -0
- package/dist/src/setup/stage2.js.map +1 -0
- package/dist/src/setup/stage2request.js +259 -0
- package/dist/src/setup/stage2request.js.map +1 -0
- package/dist/src/setup/survey.js +307 -0
- package/dist/src/setup/survey.js.map +1 -0
- package/dist/src/setup/surveyrun.js +549 -0
- package/dist/src/setup/surveyrun.js.map +1 -0
- package/dist/src/setup/textsniff.js +89 -0
- package/dist/src/setup/textsniff.js.map +1 -0
- package/dist/src/setup/trackersink.js +395 -0
- package/dist/src/setup/trackersink.js.map +1 -0
- package/dist/src/setup/triage.js +636 -0
- package/dist/src/setup/triage.js.map +1 -0
- package/dist/src/setup/tuning.js +585 -0
- package/dist/src/setup/tuning.js.map +1 -0
- package/dist/src/setup/unitcut.js +437 -0
- package/dist/src/setup/unitcut.js.map +1 -0
- package/dist/src/singleflight.js +72 -0
- package/dist/src/singleflight.js.map +1 -0
- package/dist/src/slack/announce.js +725 -0
- package/dist/src/slack/announce.js.map +1 -0
- package/dist/src/slack/api.js +227 -0
- package/dist/src/slack/api.js.map +1 -0
- package/dist/src/slack/messages.js +158 -0
- package/dist/src/slack/messages.js.map +1 -0
- package/dist/src/slack/origin.js +550 -0
- package/dist/src/slack/origin.js.map +1 -0
- package/dist/src/slack/policy.js +225 -0
- package/dist/src/slack/policy.js.map +1 -0
- package/dist/src/slack/reply.js +153 -0
- package/dist/src/slack/reply.js.map +1 -0
- package/dist/src/slack/router.js +189 -0
- package/dist/src/slack/router.js.map +1 -0
- package/dist/src/slack/socket.js +323 -0
- package/dist/src/slack/socket.js.map +1 -0
- package/dist/src/slack/types.js +11 -0
- package/dist/src/slack/types.js.map +1 -0
- package/dist/src/sqlitehint.js +30 -0
- package/dist/src/sqlitehint.js.map +1 -0
- package/dist/src/ssh-observation.js +25 -0
- package/dist/src/ssh-observation.js.map +1 -0
- package/dist/src/state.js +5869 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/statemachine.js +220 -0
- package/dist/src/statemachine.js.map +1 -0
- package/dist/src/stubs.js +52 -0
- package/dist/src/stubs.js.map +1 -0
- package/dist/src/takeover.js +506 -0
- package/dist/src/takeover.js.map +1 -0
- package/dist/src/takeoverpi.js +249 -0
- package/dist/src/takeoverpi.js.map +1 -0
- package/dist/src/terminal-failure.js +26 -0
- package/dist/src/terminal-failure.js.map +1 -0
- package/dist/src/thread-resolution-refusal.js +4 -0
- package/dist/src/thread-resolution-refusal.js.map +1 -0
- package/dist/src/tmux.js +126 -0
- package/dist/src/tmux.js.map +1 -0
- package/dist/src/trackerid.js +43 -0
- package/dist/src/trackerid.js.map +1 -0
- package/dist/src/transcriptindex.js +512 -0
- package/dist/src/transcriptindex.js.map +1 -0
- package/dist/src/tui/actions.js +404 -0
- package/dist/src/tui/actions.js.map +1 -0
- package/dist/src/tui/alerts.js +523 -0
- package/dist/src/tui/alerts.js.map +1 -0
- package/dist/src/tui/app.js +1835 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/archiveops.js +164 -0
- package/dist/src/tui/archiveops.js.map +1 -0
- package/dist/src/tui/autofile.js +191 -0
- package/dist/src/tui/autofile.js.map +1 -0
- package/dist/src/tui/board.js +1143 -0
- package/dist/src/tui/board.js.map +1 -0
- package/dist/src/tui/candidates.js +699 -0
- package/dist/src/tui/candidates.js.map +1 -0
- package/dist/src/tui/cellwidth.js +151 -0
- package/dist/src/tui/cellwidth.js.map +1 -0
- package/dist/src/tui/changelog-store.js +404 -0
- package/dist/src/tui/changelog-store.js.map +1 -0
- package/dist/src/tui/chord.js +26 -0
- package/dist/src/tui/chord.js.map +1 -0
- package/dist/src/tui/clipboard.js +81 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/cmdbar.js +253 -0
- package/dist/src/tui/cmdbar.js.map +1 -0
- package/dist/src/tui/companion.js +1275 -0
- package/dist/src/tui/companion.js.map +1 -0
- package/dist/src/tui/companionplan.js +200 -0
- package/dist/src/tui/companionplan.js.map +1 -0
- package/dist/src/tui/companiontab.js +61 -0
- package/dist/src/tui/companiontab.js.map +1 -0
- package/dist/src/tui/confighome.js +105 -0
- package/dist/src/tui/confighome.js.map +1 -0
- package/dist/src/tui/connect.js +44 -0
- package/dist/src/tui/connect.js.map +1 -0
- package/dist/src/tui/deploy-ceremony.js +75 -0
- package/dist/src/tui/deploy-ceremony.js.map +1 -0
- package/dist/src/tui/deploy-drain.js +93 -0
- package/dist/src/tui/deploy-drain.js.map +1 -0
- package/dist/src/tui/deploy-process.js +234 -0
- package/dist/src/tui/deploy-process.js.map +1 -0
- package/dist/src/tui/deploytab.js +1263 -0
- package/dist/src/tui/deploytab.js.map +1 -0
- package/dist/src/tui/design.js +111 -0
- package/dist/src/tui/design.js.map +1 -0
- package/dist/src/tui/digesttab.js +148 -0
- package/dist/src/tui/digesttab.js.map +1 -0
- package/dist/src/tui/emptystate.js +32 -0
- package/dist/src/tui/emptystate.js.map +1 -0
- package/dist/src/tui/evidence.js +790 -0
- package/dist/src/tui/evidence.js.map +1 -0
- package/dist/src/tui/feed.js +415 -0
- package/dist/src/tui/feed.js.map +1 -0
- package/dist/src/tui/feedbacktab.js +907 -0
- package/dist/src/tui/feedbacktab.js.map +1 -0
- package/dist/src/tui/filters.js +72 -0
- package/dist/src/tui/filters.js.map +1 -0
- package/dist/src/tui/ghread.js +272 -0
- package/dist/src/tui/ghread.js.map +1 -0
- package/dist/src/tui/historypull.js +61 -0
- package/dist/src/tui/historypull.js.map +1 -0
- package/dist/src/tui/inboxpull.js +197 -0
- package/dist/src/tui/inboxpull.js.map +1 -0
- package/dist/src/tui/index.js +1425 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/issuesink.js +206 -0
- package/dist/src/tui/issuesink.js.map +1 -0
- package/dist/src/tui/jsonfile.js +32 -0
- package/dist/src/tui/jsonfile.js.map +1 -0
- package/dist/src/tui/keys.js +97 -0
- package/dist/src/tui/keys.js.map +1 -0
- package/dist/src/tui/layout.js +70 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/learning.js +469 -0
- package/dist/src/tui/learning.js.map +1 -0
- package/dist/src/tui/lessonswitch.js +103 -0
- package/dist/src/tui/lessonswitch.js.map +1 -0
- package/dist/src/tui/livecontrol.js +167 -0
- package/dist/src/tui/livecontrol.js.map +1 -0
- package/dist/src/tui/memforward.js +310 -0
- package/dist/src/tui/memforward.js.map +1 -0
- package/dist/src/tui/memincident.js +287 -0
- package/dist/src/tui/memincident.js.map +1 -0
- package/dist/src/tui/memory.js +485 -0
- package/dist/src/tui/memory.js.map +1 -0
- package/dist/src/tui/memtab.js +513 -0
- package/dist/src/tui/memtab.js.map +1 -0
- package/dist/src/tui/metricstab.js +979 -0
- package/dist/src/tui/metricstab.js.map +1 -0
- package/dist/src/tui/model.js +104 -0
- package/dist/src/tui/model.js.map +1 -0
- package/dist/src/tui/mouse.js +83 -0
- package/dist/src/tui/mouse.js.map +1 -0
- package/dist/src/tui/msg.js +196 -0
- package/dist/src/tui/msg.js.map +1 -0
- package/dist/src/tui/nativecompanion.js +321 -0
- package/dist/src/tui/nativecompanion.js.map +1 -0
- package/dist/src/tui/openurl.js +17 -0
- package/dist/src/tui/openurl.js.map +1 -0
- package/dist/src/tui/paint.js +501 -0
- package/dist/src/tui/paint.js.map +1 -0
- package/dist/src/tui/panes.js +74 -0
- package/dist/src/tui/panes.js.map +1 -0
- package/dist/src/tui/pipane.js +166 -0
- package/dist/src/tui/pipane.js.map +1 -0
- package/dist/src/tui/poll-hooks.js +11 -0
- package/dist/src/tui/poll-hooks.js.map +1 -0
- package/dist/src/tui/poll.js +113 -0
- package/dist/src/tui/poll.js.map +1 -0
- package/dist/src/tui/proposals.js +715 -0
- package/dist/src/tui/proposals.js.map +1 -0
- package/dist/src/tui/prpause.js +132 -0
- package/dist/src/tui/prpause.js.map +1 -0
- package/dist/src/tui/reload.js +351 -0
- package/dist/src/tui/reload.js.map +1 -0
- package/dist/src/tui/remote.js +312 -0
- package/dist/src/tui/remote.js.map +1 -0
- package/dist/src/tui/rollover.js +767 -0
- package/dist/src/tui/rollover.js.map +1 -0
- package/dist/src/tui/scantab.js +1053 -0
- package/dist/src/tui/scantab.js.map +1 -0
- package/dist/src/tui/setupentry.js +81 -0
- package/dist/src/tui/setupentry.js.map +1 -0
- package/dist/src/tui/sidebar.js +602 -0
- package/dist/src/tui/sidebar.js.map +1 -0
- package/dist/src/tui/sshtriage.js +393 -0
- package/dist/src/tui/sshtriage.js.map +1 -0
- package/dist/src/tui/startup.js +172 -0
- package/dist/src/tui/startup.js.map +1 -0
- package/dist/src/tui/tabs.js +41 -0
- package/dist/src/tui/tabs.js.map +1 -0
- package/dist/src/tui/termmirror.js +217 -0
- package/dist/src/tui/termmirror.js.map +1 -0
- package/dist/src/tui/theme.js +95 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/ticketstab.js +224 -0
- package/dist/src/tui/ticketstab.js.map +1 -0
- package/dist/src/tui/tmuxsocket.js +101 -0
- package/dist/src/tui/tmuxsocket.js.map +1 -0
- package/dist/src/tui/trackertickets.js +77 -0
- package/dist/src/tui/trackertickets.js.map +1 -0
- package/dist/src/tui/unwedge.js +245 -0
- package/dist/src/tui/unwedge.js.map +1 -0
- package/dist/src/tui/viewer.js +479 -0
- package/dist/src/tui/viewer.js.map +1 -0
- package/dist/src/tui/viewerinput.js +125 -0
- package/dist/src/tui/viewerinput.js.map +1 -0
- package/dist/src/updatenotice.js +143 -0
- package/dist/src/updatenotice.js.map +1 -0
- package/dist/src/usage.js +1007 -0
- package/dist/src/usage.js.map +1 -0
- package/dist/src/verdicts.js +3848 -0
- package/dist/src/verdicts.js.map +1 -0
- package/dist/src/version.js +58 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/wakes.js +411 -0
- package/dist/src/wakes.js.map +1 -0
- package/dist/src/webhook.js +725 -0
- package/dist/src/webhook.js.map +1 -0
- package/dist/src/webhookheal.js +238 -0
- package/dist/src/webhookheal.js.map +1 -0
- package/dist/src/wire.js +1801 -0
- package/dist/src/wire.js.map +1 -0
- package/dist/src/worker-session-host.js +531 -0
- package/dist/src/worker-session-host.js.map +1 -0
- package/dist/src/workerbreaker.js +112 -0
- package/dist/src/workerbreaker.js.map +1 -0
- package/dist/src/workercontrol.js +49 -0
- package/dist/src/workercontrol.js.map +1 -0
- package/dist/src/workerlease.js +351 -0
- package/dist/src/workerlease.js.map +1 -0
- package/dist/src/workerliveness.js +84 -0
- package/dist/src/workerliveness.js.map +1 -0
- package/dist/src/workerlog.js +65 -0
- package/dist/src/workerlog.js.map +1 -0
- package/dist/src/workerruntime.js +10 -0
- package/dist/src/workerruntime.js.map +1 -0
- package/dist/src/workersupervisor.js +1206 -0
- package/dist/src/workersupervisor.js.map +1 -0
- package/dist/src/workerusage.js +23 -0
- package/dist/src/workerusage.js.map +1 -0
- package/dist/src/workerworld.js +332 -0
- package/dist/src/workerworld.js.map +1 -0
- package/dist/src/worktrees.js +984 -0
- package/dist/src/worktrees.js.map +1 -0
- package/docker/Dockerfile +284 -0
- package/docker/Dockerfile.dockerignore +35 -0
- package/docker/README.md +221 -0
- package/docker/companion/Dockerfile +139 -0
- package/docker/companion/agents/evidence.md +34 -0
- package/docker/companion/agents/explore.md +26 -0
- package/docker/companion/agents/pass-survey.md +49 -0
- package/docker/companion/agents/study.md +26 -0
- package/docker/companion/entrypoint.sh +343 -0
- package/docker/companion/memory-health.mjs +269 -0
- package/docker/entrypoint.sh +782 -0
- package/docker/memory/Dockerfile +191 -0
- package/docker/memory/Dockerfile.dockerignore +20 -0
- package/docker/memory/README.md +60 -0
- package/docker/memory/entrypoint.sh +163 -0
- package/docker/setid/qcr-setid.c +109 -0
- package/docker/tunnel/Dockerfile.cloudflared +51 -0
- package/docker/tunnel/Dockerfile.ngrok +40 -0
- package/docker/tunnel/sidecar.mjs +411 -0
- package/docker/vm/tmux.conf +26 -0
- package/docker-compose.yml +348 -0
- package/extensions/cli/bash-guard-ambient.ts +71 -0
- package/extensions/cli/bash-guard.ts +12 -0
- package/extensions/cli/companion-guard.ts +8 -0
- package/extensions/cli/debug-round.ts +9 -0
- package/extensions/cli/inject-bridge.ts +8 -0
- package/extensions/cli/ledger-pin.ts +13 -0
- package/extensions/cli/lens.ts +22 -0
- package/extensions/cli/muted-theme.ts +9 -0
- package/extensions/cli/onboarding.ts +8 -0
- package/extensions/cli/setup.ts +8 -0
- package/extensions/cli/tui-view.ts +8 -0
- package/extensions/companion-guard.ts +411 -0
- package/extensions/debug-round.ts +281 -0
- package/extensions/inject-bridge.ts +630 -0
- package/extensions/inline-bash.ts +102 -0
- package/extensions/muted-theme.json +68 -0
- package/extensions/muted-theme.ts +23 -0
- package/extensions/onboarding.ts +233 -0
- package/extensions/setup-replay.ts +336 -0
- package/extensions/setup-scan.ts +1236 -0
- package/extensions/setup-triage.ts +190 -0
- package/extensions/setup.ts +1428 -0
- package/extensions/tui-view.ts +489 -0
- package/extensions/vendor/openviking-pi/LICENSE +201 -0
- package/extensions/vendor/openviking-pi/NOTICE +68 -0
- package/extensions/vendor/openviking-pi/README.md +550 -0
- package/extensions/vendor/openviking-pi/client.ts +588 -0
- package/extensions/vendor/openviking-pi/config.json +28 -0
- package/extensions/vendor/openviking-pi/config.ts +271 -0
- package/extensions/vendor/openviking-pi/index.ts +523 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
- package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
- package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
- package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
- package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
- package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
- package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
- package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
- package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
- package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
- package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
- package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
- package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
- package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
- package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
- package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
- package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
- package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
- package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
- package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
- package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
- package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
- package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
- package/extensions/vendor/openviking-pi/recall.ts +149 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
- package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
- package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
- package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
- package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
- package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
- package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
- package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/extensions/vendor/openviking-pi/sync.ts +163 -0
- package/extensions/vendor/openviking-pi/takeover.ts +36 -0
- package/extensions/vendor/openviking-pi/tools.ts +479 -0
- package/extensions/vendor/openviking-pi/upstream.json +48 -0
- package/package.json +138 -4
- package/profiles/default/README.md +21 -0
- package/profiles/default/addenda/.gitkeep +0 -0
- package/profiles/default/profile.toml +17 -0
- package/profiles/example-django/README.md +21 -0
- package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
- package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
- package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
- package/profiles/example-django/addenda/40-orm-usage.md +19 -0
- package/profiles/example-django/addenda/50-declared-shape.md +10 -0
- package/profiles/example-django/addenda/60-config-placement.md +14 -0
- package/profiles/example-django/heuristics.toml +24 -0
- package/profiles/example-django/profile.toml +9 -0
- package/profiles/fast/README.md +24 -0
- package/profiles/fast/profile.toml +59 -0
- package/profiles/minimal/README.md +17 -0
- package/profiles/minimal/addenda/.gitkeep +0 -0
- package/profiles/minimal/profile.toml +8 -0
- package/profiles/snappy/README.md +24 -0
- package/profiles/snappy/addenda/10-race-as-question.md +6 -0
- package/profiles/snappy/profile.toml +46 -0
- package/prompts/companion-day-open.md +46 -0
- package/prompts/companion-first-run.md +104 -0
- package/prompts/companion.md +821 -0
- package/prompts/conversation-system.md +117 -0
- package/prompts/eod-sweep.md +120 -0
- package/prompts/onboarding.md +74 -0
- package/prompts/pass-common.md +52 -0
- package/prompts/pass-quality-common.md +22 -0
- package/prompts/round-first.md +874 -0
- package/prompts/round-follow-up.md +991 -0
- package/prompts/round-re-review.md +889 -0
- package/prompts/round-recheck.md +196 -0
- package/prompts/round-staleness.md +856 -0
- package/schemas/review-result.v1.json +391 -0
- package/schemas/scan-result.v1.json +421 -0
- package/schemas/version.v1.json +57 -0
- package/scripts/approve-pr.sh +16 -0
- package/scripts/build-stamp.mjs +67 -0
- package/scripts/commit-verdict.sh +155 -0
- package/scripts/companion-e2e.sh +250 -0
- package/scripts/companion-memory-forward.mjs +1966 -0
- package/scripts/config-fingerprint.sh +28 -0
- package/scripts/deploy-manifest.mjs +141 -0
- package/scripts/deploy.sh +1435 -0
- package/scripts/directory-lock.mjs +193 -0
- package/scripts/heartbeat-fresh.mjs +54 -0
- package/scripts/install-locator.sh +328 -0
- package/scripts/install-wrapper.sh +76 -0
- package/scripts/memory-search-check.mjs +98 -0
- package/scripts/mint-token.sh +212 -0
- package/scripts/openviking-embedder.py +392 -0
- package/scripts/openviking-ov-conf.mjs +161 -0
- package/scripts/openviking-provision.mjs +1122 -0
- package/scripts/openviking-reembed.mjs +1154 -0
- package/scripts/post-review.sh +16 -0
- package/scripts/pr-checks.sh +256 -0
- package/scripts/pr-context.py +174 -0
- package/scripts/pr-context.sh +63 -0
- package/scripts/pr-self.sh +144 -0
- package/scripts/pr-threads.sh +145 -0
- package/scripts/qcr +15 -0
- package/scripts/qcr-host.sh +769 -0
- package/scripts/render-review.sh +24 -0
- package/scripts/reply-thread.sh +16 -0
- package/scripts/required-passes.py +112 -0
- package/scripts/smoke-companion.sh +211 -0
- package/scripts/tui-deploy-runner.mjs +61 -0
- package/scripts/verify-dist.mjs +201 -0
- package/scripts/vm-ghcr-login.sh +188 -0
- package/seeds/agents/pass-baseline.md +61 -0
- package/seeds/agents/pass-contracts.md +30 -0
- package/seeds/agents/pass-data-safety.md +54 -0
- package/seeds/agents/pass-delivery.md +37 -0
- package/seeds/agents/pass-prod-risk.md +24 -0
- package/seeds/agents/pass-security.md +31 -0
- package/seeds/agents/pass-statelessness.md +27 -0
- package/seeds/agents/pass-ui.md +23 -0
- package/seeds/agents/pass-validate.md +50 -0
- package/seeds/prompts/conversation-system.md +117 -0
- package/seeds/prompts/eod-sweep.md +120 -0
- package/seeds/prompts/pass-common.md +52 -0
- package/skills/companion/adjudicate/SKILL.md +247 -0
- package/skills/companion/evidence/SKILL.md +150 -0
- package/skills/companion/land/SKILL.md +200 -0
- package/skills/companion/replay/SKILL.md +138 -0
- package/skills/companion/setup/SKILL.md +167 -0
- package/skills/companion/survey/SKILL.md +160 -0
- package/skills/selfimprove/README.md +34 -0
- package/skills/selfimprove/SKILL.md +127 -0
- package/skills/selfimprove/agents/openai.yaml +2 -0
- package/skills/selfimprove/commands/selfimprove.md +6 -0
- package/skills/selfimprove/references/fix-loop.md +49 -0
- package/skills/selfimprove/references/ledger.md +20 -0
- package/skills/selfimprove/references/setup.md +40 -0
- package/src/anomalies.ts +566 -0
- package/src/answerworker.ts +251 -0
- package/src/appstamp.ts +65 -0
- package/src/archive/admission.ts +3 -0
- package/src/archive/archivedb.ts +548 -0
- package/src/archive/archiver.ts +1963 -0
- package/src/archive/archivewrite.ts +279 -0
- package/src/archive/bundle.ts +333 -0
- package/src/archive/dbmove.ts +911 -0
- package/src/archive/duplicatecounts.ts +98 -0
- package/src/archive/eligibility.ts +181 -0
- package/src/archive/extract.ts +342 -0
- package/src/archive/fsutil.ts +415 -0
- package/src/archive/gzfile.ts +370 -0
- package/src/archive/kinds.ts +21 -0
- package/src/archive/livecheckpoint.ts +217 -0
- package/src/archive/liveretention.ts +180 -0
- package/src/archive/manifest.ts +519 -0
- package/src/archive/overseer.ts +336 -0
- package/src/archive/piglobal.ts +174 -0
- package/src/archive/plan.ts +612 -0
- package/src/archive/prarchive.ts +984 -0
- package/src/archive/prfiles.ts +406 -0
- package/src/archive/report.ts +252 -0
- package/src/archive/restore.ts +330 -0
- package/src/archive/rows.ts +187 -0
- package/src/archive/seal.ts +630 -0
- package/src/archive/status.ts +719 -0
- package/src/archive/storepass.ts +581 -0
- package/src/backfill.ts +362 -0
- package/src/bench/cost.ts +113 -0
- package/src/bench/exec.ts +90 -0
- package/src/bench/values.ts +13 -0
- package/src/botcontrol.ts +176 -0
- package/src/botworker.ts +953 -0
- package/src/capture.ts +321 -0
- package/src/cases.ts +1789 -0
- package/src/channelfs.ts +26 -0
- package/src/checklist.ts +506 -0
- package/src/ciretry.ts +141 -0
- package/src/cli.ts +2271 -0
- package/src/cliops.ts +384 -0
- package/src/clonedir.ts +15 -0
- package/src/commandeffects.ts +57 -0
- package/src/commands.ts +1678 -0
- package/src/companioninbox.ts +239 -0
- package/src/compare.ts +957 -0
- package/src/config.ts +2050 -0
- package/src/configcheck.ts +133 -0
- package/src/coreext.ts +47 -0
- package/src/daemon.ts +1302 -0
- package/src/debuground.ts +927 -0
- package/src/debugroundpi.ts +262 -0
- package/src/diagnostics.ts +14 -0
- package/src/digest.ts +183 -0
- package/src/dispatch.ts +2250 -0
- package/src/engine/answerguard.ts +27 -0
- package/src/engine/botsdk.ts +336 -0
- package/src/engine/caserunner-sdk.ts +423 -0
- package/src/engine/debugpi.ts +235 -0
- package/src/eod.ts +669 -0
- package/src/eodpinwatch.ts +556 -0
- package/src/escalation.ts +1253 -0
- package/src/eventloop.ts +1045 -0
- package/src/execboundary.ts +633 -0
- package/src/feedback.ts +1162 -0
- package/src/findingscheck.ts +298 -0
- package/src/fsdenied.ts +24 -0
- package/src/ghread.ts +242 -0
- package/src/github.ts +1497 -0
- package/src/homes.ts +175 -0
- package/src/hostroute.ts +53 -0
- package/src/improvements.ts +787 -0
- package/src/install.ts +1103 -0
- package/src/installkind.ts +50 -0
- package/src/ipc/capability.ts +333 -0
- package/src/ipc/client.ts +269 -0
- package/src/ipc/control.ts +90 -0
- package/src/ipc/ghreadshim.ts +94 -0
- package/src/ipc/launcher.ts +65 -0
- package/src/ipc/permits.ts +362 -0
- package/src/ipc/postshim.ts +164 -0
- package/src/ipc/protocol.ts +342 -0
- package/src/ipc/server.ts +371 -0
- package/src/ipc/workerspec.ts +218 -0
- package/src/learner/learner.ts +1655 -0
- package/src/learner/miner.ts +1290 -0
- package/src/learner/packet.ts +340 -0
- package/src/learner/receipt.ts +168 -0
- package/src/learner/status.ts +107 -0
- package/src/ledgercheck.ts +157 -0
- package/src/lessonledger.ts +703 -0
- package/src/lessonselect.ts +888 -0
- package/src/lessonshadow.ts +810 -0
- package/src/levers.ts +393 -0
- package/src/live/projection.ts +491 -0
- package/src/live/publish.ts +92 -0
- package/src/live/read.ts +86 -0
- package/src/livecli.ts +336 -0
- package/src/loginhint.ts +56 -0
- package/src/loginrefresh/atomic.ts +76 -0
- package/src/loginrefresh/lock.ts +104 -0
- package/src/loginrefresh/main.ts +59 -0
- package/src/loginrefresh/pirefresh.ts +57 -0
- package/src/loginrefresh/refresher.ts +326 -0
- package/src/loginrefresh/supervisor.ts +273 -0
- package/src/loginrefresh/types.ts +35 -0
- package/src/memory/context.ts +385 -0
- package/src/memory/datafence.ts +9 -0
- package/src/memory/delivery.ts +171 -0
- package/src/memory/eventtags.ts +80 -0
- package/src/memory/health.ts +953 -0
- package/src/memory/import.ts +664 -0
- package/src/memory/layout.ts +261 -0
- package/src/memory/legacy-file.ts +104 -0
- package/src/memory/lessons.ts +447 -0
- package/src/memory/ovsession.ts +601 -0
- package/src/memory/relayout.ts +335 -0
- package/src/memory/roundactivity.ts +209 -0
- package/src/memory/selection.ts +989 -0
- package/src/memory/shadowstatus.ts +125 -0
- package/src/memory/store.ts +275 -0
- package/src/memory/textidentity.ts +8 -0
- package/src/metrics.ts +1056 -0
- package/src/missingconfig.ts +7 -0
- package/src/npmverbs.ts +264 -0
- package/src/onboard/choices.ts +222 -0
- package/src/onboard/engine.ts +518 -0
- package/src/onboard/launch.ts +115 -0
- package/src/onboard/steps/appslug.ts +265 -0
- package/src/onboard/steps/bringup.ts +482 -0
- package/src/onboard/steps/compose.ts +459 -0
- package/src/onboard/steps/configdelivery.ts +58 -0
- package/src/onboard/steps/configfill.ts +1352 -0
- package/src/onboard/steps/deploysh.ts +105 -0
- package/src/onboard/steps/embeddings.ts +550 -0
- package/src/onboard/steps/escalation.ts +640 -0
- package/src/onboard/steps/githubapp.ts +1414 -0
- package/src/onboard/steps/golive.ts +577 -0
- package/src/onboard/steps/index.ts +71 -0
- package/src/onboard/steps/ingress.ts +907 -0
- package/src/onboard/steps/inventory.ts +526 -0
- package/src/onboard/steps/llmauth.ts +1095 -0
- package/src/onboard/steps/memory.ts +1290 -0
- package/src/onboard/steps/memorykeys.ts +85 -0
- package/src/onboard/steps/preflight.ts +191 -0
- package/src/onboard/steps/proof.ts +624 -0
- package/src/onboard/steps/ssh.ts +377 -0
- package/src/onboard/steps/topology.ts +337 -0
- package/src/onboard/steps/transport.ts +665 -0
- package/src/onboard/steps/verifyup.ts +448 -0
- package/src/onboard/steps/vmdeploy.ts +442 -0
- package/src/onboard/toml.ts +50 -0
- package/src/opencli.ts +591 -0
- package/src/openpr.ts +568 -0
- package/src/openviking/client.ts +1131 -0
- package/src/openviking/context.ts +263 -0
- package/src/openviking/embedder.ts +516 -0
- package/src/openviking/gapsink.ts +63 -0
- package/src/openviking/health.ts +83 -0
- package/src/openviking/index.ts +119 -0
- package/src/openviking/lessonfind.ts +92 -0
- package/src/openviking/probe.ts +129 -0
- package/src/openviking/proc.ts +110 -0
- package/src/openviking/provisionreport.ts +160 -0
- package/src/openviking/reembed.ts +412 -0
- package/src/openviking/relayout.ts +177 -0
- package/src/openviking/sidecar-main.ts +38 -0
- package/src/openviking/sidecar.ts +106 -0
- package/src/openviking/store.ts +2025 -0
- package/src/openviking/supervisor.ts +474 -0
- package/src/openviking/types.ts +174 -0
- package/src/operator-recovery.ts +237 -0
- package/src/operatormessage.ts +52 -0
- package/src/outcomes.ts +1670 -0
- package/src/piauthcopy.ts +12 -0
- package/src/picli.ts +49 -0
- package/src/picwd.ts +25 -0
- package/src/pilogin.ts +37 -0
- package/src/pingpong.ts +1339 -0
- package/src/posting-journal.ts +549 -0
- package/src/posting-outbox.ts +802 -0
- package/src/posting-receipt.ts +324 -0
- package/src/posting.ts +603 -0
- package/src/postops.ts +832 -0
- package/src/pr-activity.ts +20 -0
- package/src/pr-author.ts +6 -0
- package/src/pr-conversation.ts +432 -0
- package/src/pr-session-lifecycle.ts +631 -0
- package/src/preflight.ts +391 -0
- package/src/priorresolve.ts +343 -0
- package/src/qcrbin.ts +186 -0
- package/src/recheck.ts +1775 -0
- package/src/reconcile.ts +1030 -0
- package/src/recordedfindings.ts +27 -0
- package/src/regression.ts +833 -0
- package/src/replay.ts +816 -0
- package/src/requiredpasses.ts +148 -0
- package/src/resumecheck.ts +246 -0
- package/src/reviewcli.ts +787 -0
- package/src/reviewcontext.ts +35 -0
- package/src/reviewdetach.ts +734 -0
- package/src/reviewhistory.ts +427 -0
- package/src/reviewhome.ts +36 -0
- package/src/reviewmemory.ts +133 -0
- package/src/reviewrendercli.ts +139 -0
- package/src/round-work.ts +42 -0
- package/src/runhost.ts +108 -0
- package/src/scanauth.ts +36 -0
- package/src/scanbox.ts +1081 -0
- package/src/scancli.ts +1296 -0
- package/src/scandriverlog.ts +32 -0
- package/src/session-bridge.ts +139 -0
- package/src/session-command-payload.ts +35 -0
- package/src/session-command-supervisor.ts +1142 -0
- package/src/sessionjsonl.ts +88 -0
- package/src/sessions.ts +659 -0
- package/src/setup/adapters.ts +335 -0
- package/src/setup/baselines.ts +110 -0
- package/src/setup/caserunner.ts +611 -0
- package/src/setup/configcommit.ts +443 -0
- package/src/setup/engine.ts +1001 -0
- package/src/setup/facts.ts +627 -0
- package/src/setup/findings.ts +1264 -0
- package/src/setup/history.ts +443 -0
- package/src/setup/interview.ts +243 -0
- package/src/setup/ledger.ts +324 -0
- package/src/setup/pins.ts +66 -0
- package/src/setup/portrait.ts +635 -0
- package/src/setup/presentation.ts +18 -0
- package/src/setup/profile.ts +508 -0
- package/src/setup/replay.ts +370 -0
- package/src/setup/replaybuild.ts +392 -0
- package/src/setup/replayrun.ts +1096 -0
- package/src/setup/replaysource.ts +198 -0
- package/src/setup/retune.ts +415 -0
- package/src/setup/retunelog.ts +95 -0
- package/src/setup/scan.ts +1033 -0
- package/src/setup/scanbuild.ts +168 -0
- package/src/setup/scanfacts.ts +107 -0
- package/src/setup/scanlease.ts +297 -0
- package/src/setup/scanreport.ts +373 -0
- package/src/setup/scanrun.ts +2268 -0
- package/src/setup/scantree.ts +259 -0
- package/src/setup/stage2.ts +926 -0
- package/src/setup/stage2request.ts +347 -0
- package/src/setup/survey.ts +458 -0
- package/src/setup/surveyrun.ts +758 -0
- package/src/setup/textsniff.ts +95 -0
- package/src/setup/trackersink.ts +433 -0
- package/src/setup/triage.ts +768 -0
- package/src/setup/tuning.ts +766 -0
- package/src/setup/unitcut.ts +523 -0
- package/src/singleflight.ts +86 -0
- package/src/slack/announce.ts +911 -0
- package/src/slack/api.ts +303 -0
- package/src/slack/messages.ts +288 -0
- package/src/slack/origin.ts +599 -0
- package/src/slack/policy.ts +354 -0
- package/src/slack/reply.ts +201 -0
- package/src/slack/router.ts +285 -0
- package/src/slack/socket.ts +384 -0
- package/src/slack/types.ts +108 -0
- package/src/sqlitehint.ts +32 -0
- package/src/ssh-observation.ts +30 -0
- package/src/state.ts +8243 -0
- package/src/statemachine.ts +369 -0
- package/src/stubs.ts +68 -0
- package/src/takeover.ts +634 -0
- package/src/takeoverpi.ts +293 -0
- package/src/terminal-failure.ts +41 -0
- package/src/thread-resolution-refusal.ts +2 -0
- package/src/tmux.ts +153 -0
- package/src/trackerid.ts +41 -0
- package/src/transcriptindex.ts +665 -0
- package/src/tui/actions.ts +484 -0
- package/src/tui/alerts.ts +666 -0
- package/src/tui/app.ts +1979 -0
- package/src/tui/archiveops.ts +192 -0
- package/src/tui/autofile.ts +233 -0
- package/src/tui/board.ts +1432 -0
- package/src/tui/candidates.ts +889 -0
- package/src/tui/cellwidth.ts +148 -0
- package/src/tui/changelog-store.ts +503 -0
- package/src/tui/chord.ts +25 -0
- package/src/tui/clipboard.ts +92 -0
- package/src/tui/cmdbar.ts +296 -0
- package/src/tui/companion.ts +1427 -0
- package/src/tui/companionplan.ts +247 -0
- package/src/tui/companiontab.ts +83 -0
- package/src/tui/confighome.ts +155 -0
- package/src/tui/connect.ts +68 -0
- package/src/tui/deploy-ceremony.ts +104 -0
- package/src/tui/deploy-drain.ts +129 -0
- package/src/tui/deploy-process.ts +265 -0
- package/src/tui/deploytab.ts +1523 -0
- package/src/tui/design.ts +125 -0
- package/src/tui/digesttab.ts +214 -0
- package/src/tui/emptystate.ts +39 -0
- package/src/tui/evidence.ts +999 -0
- package/src/tui/feed.ts +604 -0
- package/src/tui/feedbacktab.ts +1077 -0
- package/src/tui/filters.ts +90 -0
- package/src/tui/ghread.ts +321 -0
- package/src/tui/historypull.ts +81 -0
- package/src/tui/inboxpull.ts +236 -0
- package/src/tui/index.ts +1565 -0
- package/src/tui/issuesink.ts +238 -0
- package/src/tui/jsonfile.ts +42 -0
- package/src/tui/keys.ts +109 -0
- package/src/tui/layout.ts +108 -0
- package/src/tui/learning.ts +627 -0
- package/src/tui/lessonswitch.ts +118 -0
- package/src/tui/livecontrol.ts +228 -0
- package/src/tui/memforward.ts +443 -0
- package/src/tui/memincident.ts +363 -0
- package/src/tui/memory.ts +620 -0
- package/src/tui/memtab.ts +606 -0
- package/src/tui/metricstab.ts +1177 -0
- package/src/tui/model.ts +131 -0
- package/src/tui/mouse.ts +98 -0
- package/src/tui/msg.ts +268 -0
- package/src/tui/nativecompanion.ts +417 -0
- package/src/tui/openurl.ts +21 -0
- package/src/tui/paint.ts +511 -0
- package/src/tui/panes.ts +98 -0
- package/src/tui/pipane.ts +219 -0
- package/src/tui/poll-hooks.ts +12 -0
- package/src/tui/poll.ts +147 -0
- package/src/tui/proposals.ts +838 -0
- package/src/tui/prpause.ts +150 -0
- package/src/tui/reload.ts +436 -0
- package/src/tui/remote.ts +413 -0
- package/src/tui/rollover.ts +969 -0
- package/src/tui/scantab.ts +1290 -0
- package/src/tui/setupentry.ts +93 -0
- package/src/tui/sidebar.ts +749 -0
- package/src/tui/sshtriage.ts +510 -0
- package/src/tui/startup.ts +231 -0
- package/src/tui/tabs.ts +45 -0
- package/src/tui/termmirror.ts +224 -0
- package/src/tui/theme.ts +95 -0
- package/src/tui/ticketstab.ts +310 -0
- package/src/tui/tmuxsocket.ts +129 -0
- package/src/tui/trackertickets.ts +84 -0
- package/src/tui/unwedge.ts +286 -0
- package/src/tui/viewer.ts +575 -0
- package/src/tui/viewerinput.ts +173 -0
- package/src/updatenotice.ts +178 -0
- package/src/usage.ts +1154 -0
- package/src/verdicts.ts +4689 -0
- package/src/version.ts +80 -0
- package/src/wakes.ts +476 -0
- package/src/webhook.ts +942 -0
- package/src/webhookheal.ts +276 -0
- package/src/wire.ts +2153 -0
- package/src/worker-session-host.ts +602 -0
- package/src/workerbreaker.ts +156 -0
- package/src/workercontrol.ts +104 -0
- package/src/workerlease.ts +441 -0
- package/src/workerliveness.ts +103 -0
- package/src/workerlog.ts +79 -0
- package/src/workerruntime.ts +30 -0
- package/src/workersupervisor.ts +1538 -0
- package/src/workerusage.ts +42 -0
- package/src/workerworld.ts +427 -0
- package/src/worktrees.ts +1138 -0
- package/tests/fixtures/regression-cases.json +795 -0
- package/tests/fixtures/seed-manifest.json +19 -0
package/src/tui/board.ts
ADDED
|
@@ -0,0 +1,1432 @@
|
|
|
1
|
+
// Board screen: semantic truecolor everywhere, rich PR rows (author, last-tick
|
|
2
|
+
// age, rounds, verdict chip), colored glyph legend, section titles with
|
|
3
|
+
// subtitles, clickable zones for full mouse navigation, OSC-8 PR links.
|
|
4
|
+
// Pure: (view, cols, rows) → Frame (+ zones/meta for the input layer).
|
|
5
|
+
import { archiveAttention, readArchiveStatus } from "../archive/status.js";
|
|
6
|
+
import type { WhyPayload } from "../cli.js";
|
|
7
|
+
import { readDaemonLoad, stallShortPhrase } from "../eventloop.js";
|
|
8
|
+
import { curationNeedsAttention, earlierPhrase } from "../learner/status.js";
|
|
9
|
+
import { fmtDuration, isAuthResult, MEMORY_OFF_NO_DOCKER } from "../memory/health.js";
|
|
10
|
+
import { cancelCauseLabel, isLoudCancelCause, type StatusSnapshot } from "../state.js";
|
|
11
|
+
import type { AlertBadge } from "./alerts.js";
|
|
12
|
+
import { barHeight, paintCmdBar, type BarButton, type LiveKeys } from "./cmdbar.js";
|
|
13
|
+
import { filterLabel, visiblePrs, type BoardFilter } from "./filters.js";
|
|
14
|
+
import { glyphFor, type EventLine, type SnapshotPr } from "./model.js";
|
|
15
|
+
import { windowStart } from "./panes.js";
|
|
16
|
+
import {
|
|
17
|
+
ACCENT,
|
|
18
|
+
ACCENT_BG,
|
|
19
|
+
HEADING,
|
|
20
|
+
MUTED,
|
|
21
|
+
RESET as DRESET,
|
|
22
|
+
STRONG,
|
|
23
|
+
WARN,
|
|
24
|
+
modeBadge,
|
|
25
|
+
sectionTitle,
|
|
26
|
+
selected as selectedLine,
|
|
27
|
+
} from "./design.js";
|
|
28
|
+
import {
|
|
29
|
+
Frame,
|
|
30
|
+
charWidth,
|
|
31
|
+
displayWidth,
|
|
32
|
+
fit,
|
|
33
|
+
rule,
|
|
34
|
+
RULE_SEGMENT_SEP,
|
|
35
|
+
sanitizeText,
|
|
36
|
+
stripAnsi,
|
|
37
|
+
truncate,
|
|
38
|
+
wrap,
|
|
39
|
+
} from "./paint.js";
|
|
40
|
+
import type { PiPaneView } from "./pipane.js";
|
|
41
|
+
import { paneSurface } from "./viewer.js";
|
|
42
|
+
import { viewerInputRow, type ViewerInputView } from "./viewerinput.js";
|
|
43
|
+
import type { Health } from "./poll.js";
|
|
44
|
+
import {
|
|
45
|
+
lensForAgent,
|
|
46
|
+
roundExecutionStage,
|
|
47
|
+
type LiveAgentProgress,
|
|
48
|
+
type RoundProgressSnapshot,
|
|
49
|
+
} from "@quickchatai/pi-quickreview/src/round-progress.js";
|
|
50
|
+
import type { ConnectionDiagnosis } from "./sshtriage.js";
|
|
51
|
+
import { FG, RESET, fmtAge, legendLine, osc8, prUrl, stateColor, verdictChip } from "./theme.js";
|
|
52
|
+
|
|
53
|
+
/** Every clickable thing the board declares. */
|
|
54
|
+
export type BoardAction =
|
|
55
|
+
| { t: "select-pr"; pr: number }
|
|
56
|
+
| { t: "open-github"; pr: number }
|
|
57
|
+
| { t: "event-jump"; pr: number }
|
|
58
|
+
| { t: "pi-focus" } // the live pi pane: clicking it types into the session in place
|
|
59
|
+
| { t: "viewer-lens"; lens: string; nth: number } // a progress row: its lens in the viewer
|
|
60
|
+
| { t: "companion" }; // the alert badge: hand the keyboard to the companion column
|
|
61
|
+
|
|
62
|
+
export interface BoardView {
|
|
63
|
+
health: Health;
|
|
64
|
+
/** `undefined` = not yet known. Only a heartbeat sets it: a rendering
|
|
65
|
+
* default here would paint a DRY-RUN badge on a live install. */
|
|
66
|
+
dryRun: boolean | undefined;
|
|
67
|
+
snapshot?: StatusSnapshot;
|
|
68
|
+
/** The triaged reason the daemon cannot be reached, with the exact
|
|
69
|
+
* remedy. Painted where the PR list would be, because that is where the
|
|
70
|
+
* operator is looking when the list is empty. */
|
|
71
|
+
connection?: ConnectionDiagnosis;
|
|
72
|
+
events: EventLine[]; // newest first
|
|
73
|
+
selected: number;
|
|
74
|
+
detail?: WhyPayload;
|
|
75
|
+
/** The EVENTS overlay is open (and owns j/k) — `e` toggles it. */
|
|
76
|
+
eventsFocus: boolean;
|
|
77
|
+
eventsScroll: number;
|
|
78
|
+
help: boolean;
|
|
79
|
+
/** First HELP_LINES entry shown by the overlay (j/k scroll it). */
|
|
80
|
+
helpScroll?: number;
|
|
81
|
+
/** The quit prompt is up. Painted by App over the composed frame,
|
|
82
|
+
* so it covers every tab without any renderer knowing about it. */
|
|
83
|
+
confirmQuit?: boolean;
|
|
84
|
+
/** Live mirror of the selected PR's pi session (board's lower region). */
|
|
85
|
+
pi?: PiPaneView;
|
|
86
|
+
/** Structured parent/lens activity for the selected running round. */
|
|
87
|
+
progress?: RoundProgressSnapshot;
|
|
88
|
+
/** The PR the pane is showing. Defaults to the selection; while the pane is
|
|
89
|
+
* focused it is locked to the session being typed into, so a poll that
|
|
90
|
+
* reorders the list cannot yank the keyboard to another PR. */
|
|
91
|
+
piPr?: number;
|
|
92
|
+
/** The pi pane owns the keyboard: every key but Ctrl-q goes to the session. */
|
|
93
|
+
piFocus?: boolean;
|
|
94
|
+
/** Extra line rendered on the pi pane header (quick-message state). */
|
|
95
|
+
noticeLine?: string;
|
|
96
|
+
/** The viewer's input line (the transcript viewer's pane only). */
|
|
97
|
+
viewerInput?: ViewerInputView;
|
|
98
|
+
/** Replaces the pi pane while the message box is open. */
|
|
99
|
+
inputBox?: { target: string; text: string };
|
|
100
|
+
/** Wall clock for age rendering; injected for testability. */
|
|
101
|
+
now?: Date;
|
|
102
|
+
/** owner/repo for GitHub links (OSC-8 + click). */
|
|
103
|
+
repo?: string;
|
|
104
|
+
/** Companion alert badge (count + latest, themed by tier). */
|
|
105
|
+
alerts?: AlertBadge;
|
|
106
|
+
/** A live `alert`-tier badge takes ⏎ until it is acknowledged (Esc) or
|
|
107
|
+
* followed into the companion tab. */
|
|
108
|
+
alertFocus?: boolean;
|
|
109
|
+
/** Active board filter (companion `board_filter` verb; F clears).
|
|
110
|
+
* Consumed through `visiblePrs` — never filter a sortPrs call directly. */
|
|
111
|
+
boardFilter?: BoardFilter | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---- layout ----------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/** "held by the operator" / "held by a session, lapses 12:30:00Z";
|
|
117
|
+
* an older daemon reports no holder, and that is said rather than guessed. */
|
|
118
|
+
export function leverHolderLabel(
|
|
119
|
+
by: "operator" | "session" | undefined,
|
|
120
|
+
until: string | undefined,
|
|
121
|
+
): string {
|
|
122
|
+
if (by === undefined) return "holder unknown (older daemon)";
|
|
123
|
+
const who = by === "operator" ? "held by the operator" : "held by a session";
|
|
124
|
+
return until ? `${who}, lapses ${until.slice(11, 19)}Z` : `${who} until lifted`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The drain bar's hold marker: ` · HOLD …` for the operator's no-auto-lift
|
|
128
|
+
* drain, ` · hold ?` for an operator drain from a daemon that reports no
|
|
129
|
+
* marker (honest unknown), nothing otherwise. */
|
|
130
|
+
export function drainHoldLabel(s: Pick<StatusSnapshot, "draining_by" | "draining_hold">): string {
|
|
131
|
+
if (s.draining_hold === true) return " · HOLD: no boot lifts it, drain off does";
|
|
132
|
+
if (s.draining_hold === undefined && s.draining_by === "operator") return " · hold ?";
|
|
133
|
+
return "";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The pause chip on a PR row: `⏸op` for the operator's hold, `⏸` + the lapse time for a session's. */
|
|
137
|
+
function pauseChip(pr: SnapshotPr): string {
|
|
138
|
+
if (!pr.paused) return "";
|
|
139
|
+
if (!pr.pause) return "⏸";
|
|
140
|
+
if (pr.pause.issuer === "operator") return "⏸op";
|
|
141
|
+
return pr.pause.expires_at ? `⏸→${pr.pause.expires_at.slice(11, 16)}Z` : "⏸";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** A PR row's verdict chip: a held review failure, else the last verdict. */
|
|
145
|
+
function rowChip(pr: SnapshotPr): string {
|
|
146
|
+
if (pr.review_failure) return `${FG.anomaly}FAILED_LOUD${RESET}`;
|
|
147
|
+
// The worker crash-loop breaker parked it: no worker starts until rerun-round / restart-session.
|
|
148
|
+
if (pr.worker_crash?.parked_at) return `${FG.anomaly}PARKED${RESET}`;
|
|
149
|
+
return verdictChip(pr.last_verdict);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** A PR title as one inert line — it is the author's text, and the row is
|
|
153
|
+
* written straight to the terminal. */
|
|
154
|
+
function rowTitle(pr: SnapshotPr): string {
|
|
155
|
+
return oneLine(pr.title);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** A PR row as `styledRow` paints it, without its styling — what the list
|
|
159
|
+
* is sized to. */
|
|
160
|
+
function rowPlain(pr: SnapshotPr, now: Date): string {
|
|
161
|
+
const rounds = pr.rounds ? `r${pr.rounds}` : "";
|
|
162
|
+
return [
|
|
163
|
+
glyphFor(pr),
|
|
164
|
+
`#${pr.number}`,
|
|
165
|
+
pr.author ?? "",
|
|
166
|
+
fmtAge(pr.updated_at, now),
|
|
167
|
+
rounds,
|
|
168
|
+
stripAnsi(rowChip(pr)),
|
|
169
|
+
pauseChip(pr),
|
|
170
|
+
rowTitle(pr),
|
|
171
|
+
]
|
|
172
|
+
.filter((s) => s !== "")
|
|
173
|
+
.join(" ");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Left column sized to the longest PR row so titles aren't cropped while
|
|
177
|
+
* the detail pane sits empty — clamped to 45% of the terminal. */
|
|
178
|
+
export function listWidth(prs: SnapshotPr[], cols: number, now: Date = new Date(0)): number {
|
|
179
|
+
const widest = prs.reduce(
|
|
180
|
+
(w, pr) => Math.max(w, displayWidth(rowPlain(pr, now))),
|
|
181
|
+
displayWidth(stripAnsi(legendLine())),
|
|
182
|
+
);
|
|
183
|
+
return Math.min(Math.max(24, widest + 3), Math.floor(cols * 0.45));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* What the board answers to on this frame (the bar is state-derived, so a
|
|
188
|
+
* button is painted exactly when its key would do something).
|
|
189
|
+
*
|
|
190
|
+
* The verbs that act on the selected PR appear only while there is one: with
|
|
191
|
+
* no snapshot, or an empty list, a `[u unwedge]` would be an action derived
|
|
192
|
+
* from no data — the second half of the honest-unknown rule. `g` also needs
|
|
193
|
+
* the repo slug the URL is built from. `e` toggles the events overlay
|
|
194
|
+
* regardless, and `I` (setup) is about the repo, not a PR. Inline: `a` is ⏎'s
|
|
195
|
+
* printable twin and `F` clears a filter the PRS subtitle is advertising.
|
|
196
|
+
*/
|
|
197
|
+
export function boardKeys(
|
|
198
|
+
view: Pick<BoardView, "snapshot" | "boardFilter" | "selected" | "repo">,
|
|
199
|
+
): LiveKeys {
|
|
200
|
+
const selected = visiblePrs(view)[view.selected] !== undefined;
|
|
201
|
+
const bar: BarButton[] = [];
|
|
202
|
+
if (selected) bar.push({ key: "\r", label: "⏎ pi pane" }, { key: "m", label: "m message" });
|
|
203
|
+
if (selected && view.repo) bar.push({ key: "g", label: "g github" });
|
|
204
|
+
bar.push({ key: "e", label: "e events" });
|
|
205
|
+
if (selected) bar.push({ key: "u", label: "u unwedge" });
|
|
206
|
+
// Setup is about the repo, not a PR — offered whatever is selected.
|
|
207
|
+
bar.push({ key: "I", label: "I setup" });
|
|
208
|
+
const inline: string[] = [];
|
|
209
|
+
if (selected) inline.push("a");
|
|
210
|
+
if (view.boardFilter != null) inline.push("F");
|
|
211
|
+
// A worker-hosted PR's pane is the transcript viewer: `[ ]` step its streams
|
|
212
|
+
// and `< >` its rounds. They are the pane header's keys (painted there), so
|
|
213
|
+
// they exist only while the selected PR's pane is the viewer.
|
|
214
|
+
const pr = visiblePrs(view)[view.selected];
|
|
215
|
+
if (pr !== undefined && paneSurface(pr.runtime, pr.number).kind === "viewer")
|
|
216
|
+
inline.push("[", "]", "<", ">");
|
|
217
|
+
return { bar, inline };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Rows the command bar takes on the board at this width — the one number
|
|
221
|
+
* `renderBoard` and the App's pi-viewport arithmetic must agree on. */
|
|
222
|
+
export function boardBarHeight(
|
|
223
|
+
view: Pick<BoardView, "snapshot" | "boardFilter" | "selected" | "repo">,
|
|
224
|
+
cols: number,
|
|
225
|
+
): number {
|
|
226
|
+
return barHeight("board", cols, boardKeys(view).bar);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The live pi mirror owns the bottom half of the board. Two rows are chrome
|
|
231
|
+
* (divider + header), the rest is session tail.
|
|
232
|
+
* Floors at 6 rows and never squeezes the list/detail body below 3.
|
|
233
|
+
*/
|
|
234
|
+
export function piRows(rows: number, barRows: number): number {
|
|
235
|
+
const usable = rows - 2 - barRows; // top bar + command bar + bottom border
|
|
236
|
+
return Math.max(0, Math.min(Math.max(6, Math.floor(usable * 0.5)), usable - 3));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The exact terminal geometry the mirrored pi session should have — the
|
|
241
|
+
* rectangle `renderBoard` paints its capture into, and nothing more.
|
|
242
|
+
*
|
|
243
|
+
* One source of truth on purpose, shared with the paint below: if these numbers
|
|
244
|
+
* drift, pi lays itself out for a terminal of a different size than the one its
|
|
245
|
+
* output is rendered in, which is the bug this exists to stop. Two of the pane's
|
|
246
|
+
* rows are chrome (divider + header); of the width, two cells are the board's
|
|
247
|
+
* borders and one is the leading space each mirror row is painted with.
|
|
248
|
+
*/
|
|
249
|
+
export function piPaneSize(
|
|
250
|
+
cols: number,
|
|
251
|
+
rows: number,
|
|
252
|
+
barRows: number,
|
|
253
|
+
): { cols: number; rows: number } | undefined {
|
|
254
|
+
const lines = piRows(rows, barRows) - 2;
|
|
255
|
+
const width = cols - 4;
|
|
256
|
+
if (lines <= 0 || width <= 0) return undefined;
|
|
257
|
+
return { cols: width, rows: lines };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Geometry of the floating EVENTS overlay: a centered band over the board,
|
|
261
|
+
* bordered top and bottom, ~60% of the height, never under the bar. */
|
|
262
|
+
export function eventsOverlayBand(rows: number, barRows: number): { top: number; height: number } {
|
|
263
|
+
const height = Math.max(5, Math.min(Math.floor(rows * 0.6), rows - 3 - barRows));
|
|
264
|
+
return { top: Math.max(1, Math.floor((rows - height) / 2)), height };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ---- pieces ----------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
/** The companion's alert badge as it sits in the top bar: tier color, today's
|
|
270
|
+
* count, the latest title, and how many repeats folded into counters. */
|
|
271
|
+
export function alertSegment(badge: AlertBadge, focused: boolean): string {
|
|
272
|
+
const latest = badge.latest;
|
|
273
|
+
if (!latest) return "";
|
|
274
|
+
const color = latest.tier === "alert" ? FG.anomaly : FG.accent;
|
|
275
|
+
const glyph = latest.tier === "alert" ? "▲" : "•";
|
|
276
|
+
const where = latest.pr !== null ? ` #${latest.pr}` : "";
|
|
277
|
+
const folds = badge.folds > 0 ? ` +${badge.folds}` : "";
|
|
278
|
+
const label = `${glyph} ${badge.count}${where} ${truncate(latest.title, 40)}${folds}${focused ? " ⏎ companion" : ""}`;
|
|
279
|
+
return focused ? selectedLine(`${color}${label}${RESET}`) : `${color}${label}${RESET}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Short top-bar label per triage class — the long form lives in the list. */
|
|
283
|
+
const CONNECTION_LABEL: Record<ConnectionDiagnosis["kind"], string> = {
|
|
284
|
+
ok: "ssh ok",
|
|
285
|
+
local: "local",
|
|
286
|
+
"no-agent-key": "no ssh key loaded",
|
|
287
|
+
"wedged-master": "ssh master wedged",
|
|
288
|
+
"host-unreachable": "host down",
|
|
289
|
+
unknown: "cause unknown",
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* What the board says where the PR list would be, when it has never had a
|
|
294
|
+
* snapshot.
|
|
295
|
+
*
|
|
296
|
+
* The count is `?`, never `0` (an empty count reads as "the data is gone" when
|
|
297
|
+
* the truth is "I could not ask"), the reason is named, and the remedy is a
|
|
298
|
+
* command the operator can paste into their own shell.
|
|
299
|
+
*/
|
|
300
|
+
export function unknownSourceLines(conn: ConnectionDiagnosis | undefined, width: number): string[] {
|
|
301
|
+
const lines = [
|
|
302
|
+
`${FG.anomaly}the daemon has not answered yet${RESET}`,
|
|
303
|
+
`${FG.dim}nothing below is a measurement — it is not known${RESET}`,
|
|
304
|
+
"",
|
|
305
|
+
];
|
|
306
|
+
if (conn && conn.kind !== "ok" && conn.kind !== "local") {
|
|
307
|
+
for (const l of wrap(stripAnsi(conn.cause), width)) lines.push(`${FG.dim}${l}${RESET}`);
|
|
308
|
+
if (conn.remedy !== "") {
|
|
309
|
+
lines.push("");
|
|
310
|
+
lines.push(`${STRONG}run this:${RESET}`);
|
|
311
|
+
for (const l of wrap(conn.remedy, width)) lines.push(`${FG.accent} ${l}${RESET}`);
|
|
312
|
+
// The remedy is a shell command, and the operator is inside the alt
|
|
313
|
+
// screen looking at it. For the one class `s` can clear in place — a key
|
|
314
|
+
// that is simply not loaded — say so here rather than leaving it to the
|
|
315
|
+
// `?` key map: otherwise the honest answer reads as "quit the TUI first".
|
|
316
|
+
if (conn.kind === "no-agent-key") {
|
|
317
|
+
for (const l of wrap("…or press s to be prompted for the key passphrase here", width))
|
|
318
|
+
lines.push(`${FG.dim}${l}${RESET}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} else if (!conn) {
|
|
322
|
+
lines.push(`${FG.dim}checking the connection…${RESET}`);
|
|
323
|
+
}
|
|
324
|
+
// Bring-up progress is not painted here: the companion column is on every
|
|
325
|
+
// frame and carries it (sidebar.ts `startup`).
|
|
326
|
+
lines.push("");
|
|
327
|
+
lines.push(`${FG.dim}s re-checks the connection · r refreshes${RESET}`);
|
|
328
|
+
return lines.map((l) => truncate(l, width));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** The detail pane's twin of the above: never "no PR selected" (which implies
|
|
332
|
+
* there were PRs to select from) when the list itself is unknown. */
|
|
333
|
+
export function unknownDetailLines(conn: ConnectionDiagnosis | undefined, width: number): string[] {
|
|
334
|
+
const lines = [
|
|
335
|
+
`${STRONG}no data${RESET}`,
|
|
336
|
+
`${FG.dim}the TUI has not been able to read status.json${RESET}`,
|
|
337
|
+
];
|
|
338
|
+
if (conn?.detail) {
|
|
339
|
+
lines.push("");
|
|
340
|
+
for (const l of wrap(`ssh said: ${stripAnsi(conn.detail)}`, width))
|
|
341
|
+
lines.push(`${FG.dim}${l}${RESET}`);
|
|
342
|
+
}
|
|
343
|
+
return lines.map((l) => truncate(l, width));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The board's reviewer-memory banner, from status.json `memory` and nothing
|
|
348
|
+
* else — never from OpenViking, which is the thing that may be down. The
|
|
349
|
+
* memory tab [2] only shows what the TUI's own store can reach, so this is
|
|
350
|
+
* where the daemon's view of memory health reaches a person.
|
|
351
|
+
*
|
|
352
|
+
* `loud` — down / refusing a key / degraded: painted right after the
|
|
353
|
+
* daemon health, where truncation reaches it last;
|
|
354
|
+
* `quiet` — not measured yet, not reported by this daemon, not monitored:
|
|
355
|
+
* a short dim mark at the end of the bar — honest, never healthy,
|
|
356
|
+
* and the first thing a narrow terminal drops;
|
|
357
|
+
* nothing — healthy, or no snapshot at all (the whole board already says
|
|
358
|
+
* it does not know).
|
|
359
|
+
*/
|
|
360
|
+
export function memorySegment(
|
|
361
|
+
snapshot: StatusSnapshot | undefined,
|
|
362
|
+
now: Date,
|
|
363
|
+
): { text: string; loud: boolean } | undefined {
|
|
364
|
+
if (!snapshot) return undefined;
|
|
365
|
+
const m = snapshot.memory;
|
|
366
|
+
if (!m) return { text: `${FG.dim}mem ?${RESET}`, loud: false };
|
|
367
|
+
if (!m.enabled)
|
|
368
|
+
return {
|
|
369
|
+
text: `${FG.dim}${m.disabled_reason === MEMORY_OFF_NO_DOCKER ? MEMORY_OFF_NO_DOCKER : "mem off"}${RESET}`,
|
|
370
|
+
loud: false,
|
|
371
|
+
};
|
|
372
|
+
const since = Date.parse(m.incident?.since ?? m.since);
|
|
373
|
+
const age = Number.isFinite(since) ? ` ${fmtDuration(now.getTime() - since)}` : "";
|
|
374
|
+
switch (m.state) {
|
|
375
|
+
case "healthy":
|
|
376
|
+
return undefined;
|
|
377
|
+
case "unknown":
|
|
378
|
+
return { text: `${FG.dim}mem ?${RESET}`, loud: false };
|
|
379
|
+
case "degraded":
|
|
380
|
+
return {
|
|
381
|
+
// Memory that answers but cannot search says so.
|
|
382
|
+
text: `${WARN}▲ MEMORY DEGRADED${age}${m.serving?.ok === false ? " · search not serving" : ""}${DRESET}`,
|
|
383
|
+
loud: true,
|
|
384
|
+
};
|
|
385
|
+
case "down":
|
|
386
|
+
return {
|
|
387
|
+
text: `${FG.anomaly}⛔ MEMORY DOWN${age} · ${m.down_cause === "not_serving" ? "search not serving" : "no authenticated answer"}${RESET}`,
|
|
388
|
+
loud: true,
|
|
389
|
+
};
|
|
390
|
+
case "auth_failed": {
|
|
391
|
+
const d = isAuthResult(m.credentials.daemon);
|
|
392
|
+
const sess = isAuthResult(m.credentials.sessions);
|
|
393
|
+
const which = d && sess ? "both keys" : d ? "daemon key" : sess ? "sessions' key" : "a key";
|
|
394
|
+
return {
|
|
395
|
+
text: `${FG.anomaly}⛔ MEMORY AUTH FAILED${age} · ${which} refused${RESET}`,
|
|
396
|
+
loud: true,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The board's EOD-curation banner, from status.json
|
|
404
|
+
* `memory.curation` alone: loud when the day's curation did not reach memory
|
|
405
|
+
* — no receipt (`missing`), or a lesson refused or not found
|
|
406
|
+
* (`not_persisted`). Both are also overseer wakes; this is the half a person
|
|
407
|
+
* sees, beside the memory banner. A block the daemon could not read (`null`)
|
|
408
|
+
* is a dim `curation ?` — honest-unknown: painting nothing would read the
|
|
409
|
+
* same as a quiet day, which `qcr status` and the companion's view both call
|
|
410
|
+
* unknown. Nothing for a daemon that reports no block (`undefined`: memory
|
|
411
|
+
* off, or an older daemon), and a quiet day is quiet.
|
|
412
|
+
*/
|
|
413
|
+
export function curationSegment(snapshot: StatusSnapshot | undefined): string | undefined {
|
|
414
|
+
const c = snapshot?.memory?.curation;
|
|
415
|
+
if (c === null && snapshot?.memory) return `${FG.dim}curation ?${RESET}`;
|
|
416
|
+
if (!curationNeedsAttention(c) || !c) return undefined;
|
|
417
|
+
return c.state === "missing"
|
|
418
|
+
? `${FG.anomaly}⛔ EOD CURATION MISSING ${c.day ?? "?"} · ${c.offered} candidate${c.offered === 1 ? "" : "s"}, no receipt${earlierPhrase(c)}${RESET}`
|
|
419
|
+
: `${FG.anomaly}⛔ LESSONS NOT PERSISTED ${c.day ?? "?"} · ${c.not_persisted}${earlierPhrase(c)}${RESET}`;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* The board's archive banner (docs/archiving.md §11), from
|
|
424
|
+
* status.json `archive` alone, through its total reader: loud only for what
|
|
425
|
+
* needs a person (`archiveAttention`) — held for disk, a restore that failed,
|
|
426
|
+
* a reopened PR no tick or birth will restore (mode not `on`: its archived
|
|
427
|
+
* receipts read unknown until `qcr archive restore`), or repeated verify
|
|
428
|
+
* failures. A block this build cannot read is a dim `archive ?`, never
|
|
429
|
+
* silence; nothing for a daemon that reports no block, and a quiet archiver
|
|
430
|
+
* is quiet.
|
|
431
|
+
*/
|
|
432
|
+
export function archiveSegment(snapshot: StatusSnapshot | undefined): string | undefined {
|
|
433
|
+
if (!snapshot) return undefined;
|
|
434
|
+
const r = readArchiveStatus(snapshot.archive);
|
|
435
|
+
if (r.kind === "absent") return undefined;
|
|
436
|
+
if (r.kind === "unreadable") return `${FG.dim}archive ?${RESET}`;
|
|
437
|
+
const parts = archiveAttention(r.view);
|
|
438
|
+
return parts.length > 0 ? `${FG.anomaly}⛔ ARCHIVE ${parts.join(" · ")}${RESET}` : undefined;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* The board's event-loop banner, from status.json `daemon` and nothing else:
|
|
443
|
+
* the daemon measuring its own loop, so a stalled loop (an unanswered /health)
|
|
444
|
+
* is visible on the board.
|
|
445
|
+
*
|
|
446
|
+
* `loud` — stalled: painted beside the memory banner, with the cause the
|
|
447
|
+
* evidence names (the loop itself blocked / CPU starved /
|
|
448
|
+
* cause unknown);
|
|
449
|
+
* `quiet` — not measured yet, not reported by this daemon (an older one),
|
|
450
|
+
* or a block this build cannot read whole (a newer daemon's state,
|
|
451
|
+
* a missing field): a short dim `loop ?` at the end of the bar —
|
|
452
|
+
* honest, never ok;
|
|
453
|
+
* nothing — ok, or no snapshot at all.
|
|
454
|
+
*
|
|
455
|
+
* The block is parsed JSON, read through `readDaemonLoad`: nothing in it can
|
|
456
|
+
* throw out of a paint.
|
|
457
|
+
*/
|
|
458
|
+
export function eventLoopSegment(
|
|
459
|
+
snapshot: StatusSnapshot | undefined,
|
|
460
|
+
): { text: string; loud: boolean } | undefined {
|
|
461
|
+
if (!snapshot) return undefined;
|
|
462
|
+
const r = readDaemonLoad(snapshot.daemon);
|
|
463
|
+
if (r.kind !== "read" || r.view.event_loop.state === "unknown")
|
|
464
|
+
return { text: `${FG.dim}loop ?${RESET}`, loud: false };
|
|
465
|
+
if (r.view.event_loop.state === "ok") return undefined;
|
|
466
|
+
return { text: `${FG.anomaly}⛔ ${stallShortPhrase(snapshot.daemon)}${RESET}`, loud: true };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** Top-bar segments, left to right — shared by the renderer and the hit-test
|
|
470
|
+
* geometry so the alert click zone can never drift from the text. */
|
|
471
|
+
function barSegments(view: BoardView, alerts: boolean): string[] {
|
|
472
|
+
const mode = modeBadge(view.dryRun);
|
|
473
|
+
let health: string;
|
|
474
|
+
switch (view.health.kind) {
|
|
475
|
+
case "ok":
|
|
476
|
+
health = `${FG.approve}●${RESET} daemon ok (hb ${view.health.heartbeatAgeS}s)`;
|
|
477
|
+
break;
|
|
478
|
+
case "pipeline_unhealthy":
|
|
479
|
+
health = `${FG.anomaly}⛔ daemon alive · PIPELINE FAILED · ${view.health.causes.length} (hb ${view.health.heartbeatAgeS}s)${RESET}`;
|
|
480
|
+
break;
|
|
481
|
+
case "stale":
|
|
482
|
+
health = `${WARN}▲ DATA STALE (${view.health.dataAgeS}s)${DRESET}`;
|
|
483
|
+
break;
|
|
484
|
+
case "unreachable":
|
|
485
|
+
health = `${FG.anomaly}✖ unreachable${view.connection ? ` · ${CONNECTION_LABEL[view.connection.kind]}` : ""}${RESET}`;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
const s = view.snapshot;
|
|
489
|
+
const rounds = s ? `rounds ${s.running_rounds}/${s.max_concurrent_rounds ?? "?"}` : "";
|
|
490
|
+
const spend = s?.today_spend_usd !== undefined ? `today $${s.today_spend_usd.toFixed(2)}` : "";
|
|
491
|
+
// Deploy-window drain: while the daemon drains, the bar says so loudly,
|
|
492
|
+
// with how many rounds still have to finish before a deploy is safe and how
|
|
493
|
+
// many webhook deliveries were dropped so far — and by whom, and when it
|
|
494
|
+
// lapses: an operator's drain holds until lifted, a session's always has an
|
|
495
|
+
// expiry. An older daemon that reports neither is shown as such rather than
|
|
496
|
+
// guessed at. The operator's HOLD says that no boot lifts it; an
|
|
497
|
+
// operator drain from a daemon that reports no hold marker says "hold ?".
|
|
498
|
+
const drainSeg = s?.draining
|
|
499
|
+
? `${FG.anomaly}⛔ DRAINING — ${s.running_rounds} round${s.running_rounds === 1 ? "" : "s"} finishing · ${s.drain_failed_loud ?? 0} failed loud · ${s.drained_deliveries ?? 0} dropped · ${leverHolderLabel(s.draining_by, s.draining_until)}${drainHoldLabel(s)}${s.draining_reason === "worker_failures" ? " · workers keep failing, drain off lifts it" : ""}${RESET}`
|
|
500
|
+
: "";
|
|
501
|
+
const td = s?.thread_decisions;
|
|
502
|
+
const breakerSeg =
|
|
503
|
+
td && (td.breaker_state !== "closed" || td.failed > 0)
|
|
504
|
+
? `${FG.anomaly}⛔ THREAD RETRY ${td.breaker_state.toUpperCase()}${td.open_until ? ` until ${td.open_until.slice(11, 19)}Z` : ""} · ${td.pending} pending · ${td.failed} failed${RESET}`
|
|
505
|
+
: "";
|
|
506
|
+
const badge = alerts && view.alerts ? alertSegment(view.alerts, view.alertFocus === true) : "";
|
|
507
|
+
const mem = memorySegment(s, view.now ?? new Date());
|
|
508
|
+
const curation = curationSegment(s) ?? "";
|
|
509
|
+
const archive = archiveSegment(s) ?? "";
|
|
510
|
+
const loop = eventLoopSegment(s);
|
|
511
|
+
return [
|
|
512
|
+
`${HEADING}quickreview${DRESET}`,
|
|
513
|
+
mode,
|
|
514
|
+
health,
|
|
515
|
+
mem?.loud ? mem.text : "",
|
|
516
|
+
loop?.loud ? loop.text : "",
|
|
517
|
+
curation,
|
|
518
|
+
archive,
|
|
519
|
+
drainSeg,
|
|
520
|
+
breakerSeg,
|
|
521
|
+
rounds,
|
|
522
|
+
spend,
|
|
523
|
+
badge,
|
|
524
|
+
// Only when `alerts` — the hit-test geometry measures the segments BEFORE
|
|
525
|
+
// the badge, and a quiet mark sits after it.
|
|
526
|
+
alerts && mem && !mem.loud ? mem.text : "",
|
|
527
|
+
alerts && loop && !loop.loud ? loop.text : "",
|
|
528
|
+
].filter((x) => x !== "");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export function topBar(view: BoardView, cols: number): string {
|
|
532
|
+
return rule(cols, "┌", "┐", barSegments(view, true));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* 1-based screen columns of the alert badge inside the top bar, or undefined
|
|
537
|
+
* when there is no badge. Derived from the same segment list `topBar` renders:
|
|
538
|
+
* `rule` lays out `border + " " + segments.join(" ── ") + " "`, so the badge
|
|
539
|
+
* starts one column past the border, the leading space, and every earlier
|
|
540
|
+
* segment.
|
|
541
|
+
*/
|
|
542
|
+
export function alertZone(view: BoardView, cols: number): { x1: number; x2: number } | undefined {
|
|
543
|
+
if (!view.alerts?.latest) return undefined;
|
|
544
|
+
const before = barSegments(view, false);
|
|
545
|
+
const prefix = before.length > 0 ? `${before.join(RULE_SEGMENT_SEP)}${RULE_SEGMENT_SEP}` : "";
|
|
546
|
+
const x1 = 3 + displayWidth(stripAnsi(prefix));
|
|
547
|
+
const width = displayWidth(stripAnsi(alertSegment(view.alerts, view.alertFocus === true)));
|
|
548
|
+
if (x1 >= cols) return undefined; // truncated away on a narrow terminal
|
|
549
|
+
return { x1, x2: Math.min(cols - 1, x1 + width - 1) };
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function styledRow(
|
|
553
|
+
pr: SnapshotPr,
|
|
554
|
+
selected: boolean,
|
|
555
|
+
width: number,
|
|
556
|
+
now: Date,
|
|
557
|
+
repo: string | undefined,
|
|
558
|
+
): string {
|
|
559
|
+
const color = stateColor(pr.stale_recheck ? "reviewing" : pr.state);
|
|
560
|
+
const chip = rowChip(pr);
|
|
561
|
+
const rounds = pr.rounds ? `${FG.dim}r${pr.rounds}${RESET}` : "";
|
|
562
|
+
const paused = pr.paused ? `${WARN}${pauseChip(pr)}${DRESET}` : "";
|
|
563
|
+
const head = `${color}${glyphFor(pr)} ${STRONG}#${pr.number}${RESET}`;
|
|
564
|
+
const meta = [
|
|
565
|
+
`${FG.author}${pr.author ?? ""}${RESET}`,
|
|
566
|
+
`${FG.dim}${fmtAge(pr.updated_at, now)}${RESET}`,
|
|
567
|
+
rounds,
|
|
568
|
+
chip,
|
|
569
|
+
paused,
|
|
570
|
+
]
|
|
571
|
+
.filter((s) => stripAnsi(s) !== "")
|
|
572
|
+
.join(" ");
|
|
573
|
+
const used = displayWidth(`${stripAnsi(head)} ${stripAnsi(meta)} `);
|
|
574
|
+
const title = truncate(rowTitle(pr), Math.max(4, width - used - 1));
|
|
575
|
+
const numLinked = repo
|
|
576
|
+
? `${color}${glyphFor(pr)} ${STRONG}${osc8(prUrl(repo, pr.number), `#${pr.number}`)}${RESET}`
|
|
577
|
+
: head;
|
|
578
|
+
const body = `${numLinked} ${meta} ${title}`;
|
|
579
|
+
const pad = " ".repeat(Math.max(0, width - displayWidth(body) - 1));
|
|
580
|
+
return selected ? selectedLine(` ${body}${pad}`) : ` ${body}${pad}`;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const VERDICT_ABBREV: Record<string, string> = {
|
|
584
|
+
REQUEST_CHANGES: "RC",
|
|
585
|
+
APPROVE: "APPROVE",
|
|
586
|
+
COMMENT: "COMMENT",
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
function section(title: string, subtitle: string): string {
|
|
590
|
+
return sectionTitle(title, subtitle);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function durationLabel(ms: number): string {
|
|
594
|
+
const seconds = Math.max(0, Math.floor(ms / 1_000));
|
|
595
|
+
if (seconds < 60) return `${seconds}s`;
|
|
596
|
+
const minutes = Math.floor(seconds / 60);
|
|
597
|
+
if (minutes < 60) return `${minutes}m`;
|
|
598
|
+
const hours = Math.floor(minutes / 60);
|
|
599
|
+
return `${hours}h${minutes % 60 > 0 ? ` ${minutes % 60}m` : ""}`;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function oneLine(value: string): string {
|
|
603
|
+
return sanitizeText(value).replace(/\s+/g, " ").trim();
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
type CurrentRound = Pick<
|
|
607
|
+
WhyPayload["rounds"][number],
|
|
608
|
+
"round_no" | "status" | "started_at" | "deadline_at" | "lenses" | "validate_tier"
|
|
609
|
+
> & {
|
|
610
|
+
command_id?: string;
|
|
611
|
+
command_sha256?: string;
|
|
612
|
+
head_sha?: string;
|
|
613
|
+
stage_deadlines?: {
|
|
614
|
+
first_pass_start: string;
|
|
615
|
+
passes_complete: string;
|
|
616
|
+
running_hard_bound: string;
|
|
617
|
+
};
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
/** A lens row of the progress panel, by its line in the detail: what a click on
|
|
621
|
+
* it selects in the viewer (the `nth` run of `lens` the round started). */
|
|
622
|
+
export interface ProgressRow {
|
|
623
|
+
line: number;
|
|
624
|
+
lens: string;
|
|
625
|
+
nth: number;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function currentProgressLines(
|
|
629
|
+
round: CurrentRound,
|
|
630
|
+
progress: RoundProgressSnapshot | undefined,
|
|
631
|
+
width: number,
|
|
632
|
+
now: Date,
|
|
633
|
+
rowsOut?: ProgressRow[],
|
|
634
|
+
firstLine = 0,
|
|
635
|
+
): string[] {
|
|
636
|
+
const lines: string[] = [];
|
|
637
|
+
const started = Date.parse(round.started_at);
|
|
638
|
+
const deadline = round.deadline_at ? Date.parse(round.deadline_at) : Number.NaN;
|
|
639
|
+
const nowMs = now.getTime();
|
|
640
|
+
const elapsed = Number.isFinite(started) ? durationLabel(nowMs - started) : "?";
|
|
641
|
+
const deadlineText = Number.isFinite(deadline)
|
|
642
|
+
? deadline >= nowMs
|
|
643
|
+
? `${durationLabel(deadline - nowMs)} left`
|
|
644
|
+
: `${durationLabel(nowMs - deadline)} OVERDUE`
|
|
645
|
+
: "deadline unavailable";
|
|
646
|
+
lines.push(
|
|
647
|
+
truncate(
|
|
648
|
+
section("CURRENT REVIEW", `round ${round.round_no} · ${elapsed} elapsed · ${deadlineText}`),
|
|
649
|
+
width,
|
|
650
|
+
),
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
// A sticky session may process neighboring wakes at nearly identical times.
|
|
654
|
+
// Only the exact durable delivery + prompt-derived round/head identity may
|
|
655
|
+
// paint this round; timestamp proximity is not identity.
|
|
656
|
+
const current =
|
|
657
|
+
progress?.version === 2 &&
|
|
658
|
+
round.command_id !== undefined &&
|
|
659
|
+
progress.command_id === round.command_id &&
|
|
660
|
+
round.command_sha256 !== undefined &&
|
|
661
|
+
progress.command_sha256 === round.command_sha256 &&
|
|
662
|
+
round.head_sha !== undefined &&
|
|
663
|
+
progress.head_sha === round.head_sha &&
|
|
664
|
+
progress.round_no === round.round_no
|
|
665
|
+
? progress
|
|
666
|
+
: undefined;
|
|
667
|
+
const planned = round.lenses
|
|
668
|
+
.split(",")
|
|
669
|
+
.map((x) => x.trim())
|
|
670
|
+
.filter(Boolean);
|
|
671
|
+
// The validator runs at its own tier, shown beside its row.
|
|
672
|
+
const tierOf = (name: string): string =>
|
|
673
|
+
name === "pass-validate" && round.validate_tier ? ` · ${round.validate_tier}` : "";
|
|
674
|
+
|
|
675
|
+
if (!current) {
|
|
676
|
+
lines.push(truncate(` ${FG.dim}? parent · live progress unavailable${RESET}`, width));
|
|
677
|
+
for (const name of planned) {
|
|
678
|
+
lines.push(truncate(` ${FG.dim}? ${name} · unknown${tierOf(name)}${RESET}`, width));
|
|
679
|
+
}
|
|
680
|
+
lines.push(truncate(` ${FG.dim}? final verdict · unknown${RESET}`, width));
|
|
681
|
+
return lines;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const signalAge = Math.max(0, nowMs - Date.parse(current.updated_at));
|
|
685
|
+
const signal =
|
|
686
|
+
signalAge >= 60_000
|
|
687
|
+
? `SILENT ${durationLabel(signalAge)}`
|
|
688
|
+
: `signal ${durationLabel(signalAge)} ago`;
|
|
689
|
+
const parentColor =
|
|
690
|
+
signalAge >= 120_000 ? FG.anomaly : current.parent_status === "active" ? FG.accent : FG.dim;
|
|
691
|
+
const active = current.active_tool ? ` · ${oneLine(current.active_tool)}` : "";
|
|
692
|
+
const stage = roundExecutionStage(current, planned);
|
|
693
|
+
const stageDeadline =
|
|
694
|
+
stage === "accepted" || stage === "preparing"
|
|
695
|
+
? round.stage_deadlines?.first_pass_start
|
|
696
|
+
: stage === "fanout-started"
|
|
697
|
+
? round.stage_deadlines?.passes_complete
|
|
698
|
+
: undefined;
|
|
699
|
+
const due = stageDeadline
|
|
700
|
+
? ` · stage ${Date.parse(stageDeadline) >= nowMs ? `due in ${durationLabel(Date.parse(stageDeadline) - nowMs)}` : `OVERDUE ${durationLabel(nowMs - Date.parse(stageDeadline))}`}`
|
|
701
|
+
: "";
|
|
702
|
+
lines.push(
|
|
703
|
+
truncate(
|
|
704
|
+
` ${parentColor}parent ${current.parent_status.toUpperCase()} · ${stage}${due} · ${signal}${active}${RESET}`,
|
|
705
|
+
width,
|
|
706
|
+
),
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
const agentLine = (name: string, agent: LiveAgentProgress | undefined): string => {
|
|
710
|
+
const tier = tierOf(name);
|
|
711
|
+
if (!agent) return ` ${FG.dim}○ ${name} · pending${tier}${RESET}`;
|
|
712
|
+
if (agent.status === "running") {
|
|
713
|
+
const tool = agent.current_tool ? ` · ${oneLine(agent.current_tool)}` : "";
|
|
714
|
+
return ` ${FG.accent}⟳ ${name} · running ${durationLabel(agent.duration_ms)}${tool}${tier}${RESET}`;
|
|
715
|
+
}
|
|
716
|
+
if (agent.status === "failed") {
|
|
717
|
+
const error = agent.error ? ` · ${oneLine(agent.error)}` : "";
|
|
718
|
+
return ` ${FG.anomaly}✗ ${name} · failed after ${durationLabel(agent.duration_ms)}${error}${tier}${RESET}`;
|
|
719
|
+
}
|
|
720
|
+
return ` ${FG.approve}✓ ${name} · done in ${durationLabel(agent.duration_ms)} · ${agent.tool_count} tools${tier}${RESET}`;
|
|
721
|
+
};
|
|
722
|
+
// Every execution of a planned lens is shown — a lens run twice (two task
|
|
723
|
+
// rows) never hides a running or failed sibling behind a finished one.
|
|
724
|
+
const runs = new Map<string, LiveAgentProgress[]>();
|
|
725
|
+
for (const agent of current.agents) {
|
|
726
|
+
const name = lensForAgent(agent.agent);
|
|
727
|
+
runs.set(name, [...(runs.get(name) ?? []), agent]);
|
|
728
|
+
}
|
|
729
|
+
for (const name of new Set(planned)) {
|
|
730
|
+
const executions = runs.get(name) ?? [];
|
|
731
|
+
const rows = Math.max(planned.filter((p) => p === name).length, executions.length);
|
|
732
|
+
for (let i = 0; i < rows; i++) {
|
|
733
|
+
rowsOut?.push({ line: firstLine + lines.length, lens: name, nth: i });
|
|
734
|
+
lines.push(truncate(agentLine(name, executions[i]), width));
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// The parent may justify and add a lens outside the dispatch plan. Those
|
|
738
|
+
// agents are real work too; omitting them would make "all agents" false.
|
|
739
|
+
const added = new Map<string, number>();
|
|
740
|
+
for (const agent of current.agents.filter(
|
|
741
|
+
(item) => !planned.includes(lensForAgent(item.agent)),
|
|
742
|
+
)) {
|
|
743
|
+
const lens = lensForAgent(agent.agent);
|
|
744
|
+
const nth = added.get(lens) ?? 0;
|
|
745
|
+
added.set(lens, nth + 1);
|
|
746
|
+
rowsOut?.push({ line: firstLine + lines.length, lens, nth });
|
|
747
|
+
const rendered = agentLine(agent.agent, agent).replace(`${RESET}`, ` · added${RESET}`);
|
|
748
|
+
lines.push(truncate(rendered, width));
|
|
749
|
+
}
|
|
750
|
+
lines.push(truncate(` ${FG.dim}○ final verdict · pending${RESET}`, width));
|
|
751
|
+
const activityAge = Math.max(0, nowMs - Date.parse(current.last_activity_at));
|
|
752
|
+
lines.push(
|
|
753
|
+
truncate(
|
|
754
|
+
` ${FG.dim}last activity ${durationLabel(activityAge)} ago · ${oneLine(current.last_activity)}${RESET}`,
|
|
755
|
+
width,
|
|
756
|
+
),
|
|
757
|
+
);
|
|
758
|
+
if (signalAge >= 120_000) {
|
|
759
|
+
lines.push(
|
|
760
|
+
truncate(
|
|
761
|
+
` ${FG.anomaly}⚠ no live signal for ${durationLabel(signalAge)} — inspect raw PI below${RESET}`,
|
|
762
|
+
width,
|
|
763
|
+
),
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
return lines;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/** Doubted findings shown before the "… N more" line. */
|
|
770
|
+
const DOUBTED_SHOWN = 8;
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* The DOUBTED section: findings the validator could not stand behind
|
|
774
|
+
* — kept in the findings record as "may not be valid", never posted on the PR,
|
|
775
|
+
* and put here for the operator to judge. Newest round first. Nothing renders
|
|
776
|
+
* when there are none, or when an older daemon did not report the field.
|
|
777
|
+
*/
|
|
778
|
+
export function doubtedLines(
|
|
779
|
+
doubted: WhyPayload["doubted"],
|
|
780
|
+
width: number,
|
|
781
|
+
title = "DOUBTED",
|
|
782
|
+
note = "may not be valid · kept, never posted",
|
|
783
|
+
): string[] {
|
|
784
|
+
if (!doubted || doubted.length === 0) return [];
|
|
785
|
+
const lines = [truncate(section(title, note), width)];
|
|
786
|
+
const newest = [...doubted].sort((a, b) => b.round_no - a.round_no);
|
|
787
|
+
for (const f of newest.slice(0, DOUBTED_SHOWN)) {
|
|
788
|
+
const at = f.path ? ` ${sanitizeText(f.path)}${f.line !== null ? `:${f.line}` : ""}` : "";
|
|
789
|
+
const sev = f.severity ? ` · ${sanitizeText(f.severity)}` : "";
|
|
790
|
+
const word = f.word ? ` · ${sanitizeText(f.word)}` : "";
|
|
791
|
+
const why = f.reason ? ` — ${oneLine(f.reason)}` : "";
|
|
792
|
+
lines.push(
|
|
793
|
+
truncate(
|
|
794
|
+
` ${WARN}? r${f.round_no} ${sanitizeText(f.lens)}${at}${sev}${word}${DRESET}${FG.dim}${why}${RESET}`,
|
|
795
|
+
width,
|
|
796
|
+
),
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
if (newest.length > DOUBTED_SHOWN)
|
|
800
|
+
lines.push(`${FG.dim} … ${newest.length - DOUBTED_SHOWN} more (qcr why <pr> --json)${RESET}`);
|
|
801
|
+
return lines;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
export function detailLines(
|
|
805
|
+
detail: WhyPayload | undefined,
|
|
806
|
+
pr: SnapshotPr | undefined,
|
|
807
|
+
width: number,
|
|
808
|
+
now: Date = new Date(0),
|
|
809
|
+
progress?: RoundProgressSnapshot,
|
|
810
|
+
progressRows?: ProgressRow[],
|
|
811
|
+
): string[] {
|
|
812
|
+
const lines: string[] = [];
|
|
813
|
+
if (!pr) return ["no PR selected"];
|
|
814
|
+
lines.push(
|
|
815
|
+
`${stateColor(pr.state)}${STRONG}#${pr.number}${RESET} ${STRONG}${truncate(sanitizeText(pr.title), width - 8 - String(pr.number).length)}${RESET}`,
|
|
816
|
+
);
|
|
817
|
+
const delivery = pr.current_round?.posting_delivery;
|
|
818
|
+
if (delivery) {
|
|
819
|
+
lines.push(
|
|
820
|
+
truncate(
|
|
821
|
+
`${FG.dim}POSTING PENDING · automatic retry · attempts ${delivery.attempts}${RESET}`,
|
|
822
|
+
width,
|
|
823
|
+
),
|
|
824
|
+
);
|
|
825
|
+
lines.push(
|
|
826
|
+
truncate(
|
|
827
|
+
`next ${sanitizeText(delivery.next_attempt_at)}${delivery.last_error ? ` · ${oneLine(delivery.last_error)}` : ""}`,
|
|
828
|
+
width,
|
|
829
|
+
),
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
if (!detail || detail.pr !== pr.number) {
|
|
833
|
+
lines.push(`${FG.dim}loading detail…${RESET}`);
|
|
834
|
+
return lines;
|
|
835
|
+
}
|
|
836
|
+
const round =
|
|
837
|
+
pr.current_round?.round_no ??
|
|
838
|
+
(detail.rounds.length > 0 ? detail.rounds[detail.rounds.length - 1]?.round_no : undefined);
|
|
839
|
+
const facts: string[] = [
|
|
840
|
+
`${stateColor(detail.state ?? pr.state)}${detail.state ?? pr.state}${RESET}`,
|
|
841
|
+
];
|
|
842
|
+
if (round !== undefined) facts.push(`round ${round}`);
|
|
843
|
+
if (detail.author) facts.push(`by ${FG.author}${detail.author}${RESET}`);
|
|
844
|
+
lines.push(truncate(facts.join(" · "), width));
|
|
845
|
+
const trig = detail.trigger_ts ? `trig ${fmtAge(detail.trigger_ts, now)} ago` : "trig —";
|
|
846
|
+
lines.push(
|
|
847
|
+
truncate(
|
|
848
|
+
`spend $${detail.spend_usd.toFixed(2)} · head ${(detail.head_sha ?? "").slice(0, 7)} · ${trig}`,
|
|
849
|
+
width,
|
|
850
|
+
),
|
|
851
|
+
);
|
|
852
|
+
if (detail.labels && detail.labels.length > 0) {
|
|
853
|
+
lines.push(
|
|
854
|
+
truncate(
|
|
855
|
+
detail.labels.map((l) => `${FG.accent}[${sanitizeText(l)}]${RESET}`).join(" "),
|
|
856
|
+
width,
|
|
857
|
+
),
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
if (pr.paused) {
|
|
861
|
+
// A held PR says who holds it and when the hold lapses.
|
|
862
|
+
const hold = pr.pause;
|
|
863
|
+
const why = hold?.reason ? ` · ${oneLine(sanitizeText(hold.reason))}` : "";
|
|
864
|
+
lines.push(
|
|
865
|
+
truncate(
|
|
866
|
+
`${WARN}${STRONG}PAUSED${DRESET}${WARN} · ${leverHolderLabel(hold?.issuer, hold?.expires_at ?? undefined)}${why}${DRESET}`,
|
|
867
|
+
width,
|
|
868
|
+
),
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
if (pr.review_failure) {
|
|
872
|
+
const command = pr.review_failure.command_id ? ` · ${pr.review_failure.command_id}` : "";
|
|
873
|
+
const round =
|
|
874
|
+
pr.review_failure.round_no !== null ? ` · round ${pr.review_failure.round_no}` : "";
|
|
875
|
+
lines.push(
|
|
876
|
+
truncate(
|
|
877
|
+
`${FG.anomaly}${STRONG}FAILED_LOUD${RESET}${FG.anomaly} · ${pr.review_failure.stage}${command}${round} · attempt ${pr.review_failure.attempts}/${pr.review_failure.retry_cap} · ${fmtAge(pr.review_failure.failed_at, now)} old${RESET}`,
|
|
878
|
+
width,
|
|
879
|
+
),
|
|
880
|
+
);
|
|
881
|
+
const retry =
|
|
882
|
+
pr.review_failure.next_action === "retry_wait" && pr.review_failure.next_retry_at
|
|
883
|
+
? `auto retry ${pr.review_failure.next_retry_at.slice(11, 16)}Z · epoch ${pr.review_failure.retry_epoch} · u now`
|
|
884
|
+
: "press u to recover";
|
|
885
|
+
lines.push(
|
|
886
|
+
truncate(`${FG.anomaly}${oneLine(pr.review_failure.detail)} · ${retry}${RESET}`, width),
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
lines.push(`${FG.dim}${"─".repeat(Math.max(0, width))}${RESET}`);
|
|
890
|
+
// status.json advances every poll and therefore owns current timing/plan;
|
|
891
|
+
// `why` is enriched and deliberately cached, so it is only the fallback for
|
|
892
|
+
// an older daemon that has not shipped current_round yet — and only while
|
|
893
|
+
// the snapshot says a round is under way: a cached `running` row outlives
|
|
894
|
+
// the round it describes until the next read lands.
|
|
895
|
+
const runningRound =
|
|
896
|
+
pr.current_round ??
|
|
897
|
+
(pr.state === "reviewing"
|
|
898
|
+
? [...detail.rounds].reverse().find((r) => r.status === "running")
|
|
899
|
+
: undefined);
|
|
900
|
+
if (runningRound)
|
|
901
|
+
lines.push(
|
|
902
|
+
...currentProgressLines(runningRound, progress, width, now, progressRows, lines.length),
|
|
903
|
+
);
|
|
904
|
+
const lastSummary = [...detail.rounds]
|
|
905
|
+
.reverse()
|
|
906
|
+
.find((r) => r.summary && r.summary.trim().length > 0)?.summary;
|
|
907
|
+
if (lastSummary) {
|
|
908
|
+
lines.push(truncate(section("NOW", "the session's own 3-4 sentence read"), width));
|
|
909
|
+
for (const l of wrap(sanitizeText(lastSummary).trim(), width)) lines.push(l);
|
|
910
|
+
}
|
|
911
|
+
if (detail.description !== undefined) {
|
|
912
|
+
lines.push(truncate(section("DESCRIPTION", "the PR body"), width));
|
|
913
|
+
const raw = sanitizeText(detail.description).trim();
|
|
914
|
+
const desc = raw.length > 0 ? wrap(raw, width) : ["(empty)"];
|
|
915
|
+
for (const l of desc.slice(0, 12)) lines.push(l);
|
|
916
|
+
if (desc.length > 12)
|
|
917
|
+
lines.push(`${FG.dim}… ${desc.length - 12} more lines (open on GitHub with g)${RESET}`);
|
|
918
|
+
}
|
|
919
|
+
lines.push(truncate(section("LEDGER", "findings the conversation tracks"), width));
|
|
920
|
+
if (detail.ledger) {
|
|
921
|
+
for (const l of sanitizeText(detail.ledger).split("\n")) {
|
|
922
|
+
const t = l.trim();
|
|
923
|
+
if (t.length > 0) for (const wl of wrap(t, width)) lines.push(wl);
|
|
924
|
+
}
|
|
925
|
+
} else {
|
|
926
|
+
lines.push(
|
|
927
|
+
truncate(`${FG.dim}none yet — the live ledger is pinned in the session${RESET}`, width),
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
// A cancelled round carries its own glyph, distinct from any other
|
|
931
|
+
// verdictless round, and the one loud cause — a round that stopped dead
|
|
932
|
+
// inside its deadline — is the only one that gets a color; a redeploy orphan
|
|
933
|
+
// stays dim on purpose.
|
|
934
|
+
const roundsBits = detail.rounds.map((r) => {
|
|
935
|
+
const cancelled = r.status === "cancelled";
|
|
936
|
+
const loud = cancelled && isLoudCancelCause(r.cancel_cause);
|
|
937
|
+
const v = r.verdict
|
|
938
|
+
? (VERDICT_ABBREV[r.verdict] ?? r.verdict)
|
|
939
|
+
: cancelled
|
|
940
|
+
? "✕"
|
|
941
|
+
: r.status === "running"
|
|
942
|
+
? "…"
|
|
943
|
+
: "—";
|
|
944
|
+
const color = loud
|
|
945
|
+
? FG.anomaly
|
|
946
|
+
: r.verdict === "APPROVE"
|
|
947
|
+
? FG.approve
|
|
948
|
+
: r.verdict === "REQUEST_CHANGES"
|
|
949
|
+
? FG.request_changes
|
|
950
|
+
: FG.dim;
|
|
951
|
+
return `r${r.round_no} ${color}${v}${RESET}`;
|
|
952
|
+
});
|
|
953
|
+
lines.push(
|
|
954
|
+
truncate(
|
|
955
|
+
`${section("ROUNDS", "review rounds + verdicts")} ${roundsBits.join(" · ") || "—"}`,
|
|
956
|
+
width,
|
|
957
|
+
),
|
|
958
|
+
);
|
|
959
|
+
for (const r of detail.rounds) {
|
|
960
|
+
if (r.status !== "cancelled") continue;
|
|
961
|
+
const loud = isLoudCancelCause(r.cancel_cause);
|
|
962
|
+
lines.push(
|
|
963
|
+
truncate(
|
|
964
|
+
` ${loud ? FG.anomaly : FG.dim}✕ r${r.round_no} cancelled — ${cancelCauseLabel(r.cancel_cause)}${RESET}`,
|
|
965
|
+
width,
|
|
966
|
+
),
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
lines.push(...doubtedLines(detail.doubted, width));
|
|
970
|
+
// The profile's `[posting]` policy: survivors kept off the PR, same grammar.
|
|
971
|
+
lines.push(
|
|
972
|
+
...doubtedLines(
|
|
973
|
+
detail.withheld,
|
|
974
|
+
width,
|
|
975
|
+
"WITHHELD",
|
|
976
|
+
"over the posting policy · kept, never posted",
|
|
977
|
+
),
|
|
978
|
+
);
|
|
979
|
+
lines.push(
|
|
980
|
+
truncate(`${section("THREADS", "bot review threads")} ${detail.threads.total} known`, width),
|
|
981
|
+
);
|
|
982
|
+
if (detail.comments && detail.comments.length > 0) {
|
|
983
|
+
lines.push(truncate(section("COMMENTS", "latest PR conversation"), width));
|
|
984
|
+
for (const c of detail.comments) {
|
|
985
|
+
lines.push(
|
|
986
|
+
truncate(
|
|
987
|
+
`${FG.author}${c.author}${RESET} ${FG.dim}${fmtAge(c.created_at, now)} ago${RESET}`,
|
|
988
|
+
width,
|
|
989
|
+
),
|
|
990
|
+
);
|
|
991
|
+
const body = sanitizeText(c.body)
|
|
992
|
+
.split("\n")
|
|
993
|
+
.map((t) => t.trim())
|
|
994
|
+
.filter((t) => t.length > 0)
|
|
995
|
+
.join(" ");
|
|
996
|
+
const bodyLines = wrap(body, Math.max(8, width - 2));
|
|
997
|
+
for (const bl of bodyLines.slice(0, 2)) lines.push(` ${bl}`);
|
|
998
|
+
if (bodyLines.length > 2) lines.push(` ${FG.dim}…${RESET}`);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return lines;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* The `?` overlay. Pinned against docs/tui.md (tests/tui-surfaces.test.ts):
|
|
1006
|
+
* every key named here is in some surface's vocabulary, and every surface
|
|
1007
|
+
* the doc lists is named here. The bar shows what the current frame answers
|
|
1008
|
+
* to; this is the whole map.
|
|
1009
|
+
*/
|
|
1010
|
+
export const HELP_LINES = [
|
|
1011
|
+
"1…8 the eight surfaces: board · memory · digest · tickets · deploy · metrics · feedback · scan",
|
|
1012
|
+
" the bar at the foot shows what THIS frame answers to; a button that is not there is a key that would do nothing",
|
|
1013
|
+
"r / s / ? / q refresh · reconnect VM ssh (the one ssh key-passphrase prompt) · this overlay · quit (asks first)",
|
|
1014
|
+
"Ctrl-c ×3 kill the TUI from anywhere (terminal restored) — the un-stuck hatch",
|
|
1015
|
+
" an alert in the top bar: click it or ⏎ to open the companion",
|
|
1016
|
+
"the companion column down the right edge is on every tab:",
|
|
1017
|
+
" type any key no tab claims (or /) — it is a terminal, every byte reaches pi; Ctrl-q releases",
|
|
1018
|
+
" wheel over the column (or PgUp/PgDn while focused) walks its scrollback · drag its left edge to resize",
|
|
1019
|
+
" the column is always there at full width — nothing in the TUI collapses it or goes full-screen",
|
|
1020
|
+
"[1] board — the verbs act on the SELECTED PR, so they exist only while one is selected, and only here:",
|
|
1021
|
+
"j/k ↓/↑ select PR (mouse: click a row; click again to focus its pi pane)",
|
|
1022
|
+
"⏎ / a focus the selected PR's pi pane (embedded, below the list; Ctrl-q back) — it is a live terminal, not a picture",
|
|
1023
|
+
"g open the PR on GitHub (PR numbers are Ctrl+clickable links)",
|
|
1024
|
+
"[ / ] a worker-hosted PR's pane is a transcript viewer, not a terminal: previous / next stream (main, then each lens); a click on a progress row picks its lens",
|
|
1025
|
+
"< / > past / later round in the viewer; PgUp / wheel parks it, End follows the tail again",
|
|
1026
|
+
" its input line (focused pane): ⏎ steers the running round, or sends a message to an idle PR; Alt-⏎ queues a follow-up",
|
|
1027
|
+
" Alt-p marks the next message may-post (shown, reset after send); Ctrl-c asks to abort the round (y confirms); [ ] < > are keys only while the line is empty",
|
|
1028
|
+
"m quick-message the selected PR's session",
|
|
1029
|
+
"u unwedge: cancel the running round and re-queue it (nothing posted, no GitHub write)",
|
|
1030
|
+
"e events overlay (j/k scroll, e/Esc close; click an event → its PR)",
|
|
1031
|
+
"F clear the board filter the PRS title is advertising",
|
|
1032
|
+
"I set up the repo: the companion runs its setup skill (survey, interview, the repo profile)",
|
|
1033
|
+
"[2] memory — the store, newest first, grouped by day:",
|
|
1034
|
+
"j/k · [ / ] select an entry · jump to the older / newer day",
|
|
1035
|
+
"e / p / x / n edit · pin or unpin · retire (x twice) · new #manual entry — painted beside the selected entry",
|
|
1036
|
+
"W / F open OpenViking Studio through the memory forward · clear the tag filter",
|
|
1037
|
+
"[3] digest — the EOD sweep's document: j/k scroll · [ / ] older / newer day",
|
|
1038
|
+
"[4] tickets — what adjudication filed on the tracker: j/k choose · [ / ] scroll the body · c actionable ⇄ all",
|
|
1039
|
+
"[5] deploy — V picks the version · D D deploys it (or click ▶ DEPLOY TO VM) · S S powers the VM app · x aborts a run / closes its log",
|
|
1040
|
+
"[6] metrics — w / m / a switch the 7d / 30d / all window · # <pr> ⏎ opens a PR timeline (Esc back)",
|
|
1041
|
+
"[7] feedback — the loop on one surface: signal → adjudication → proposal → change → revert",
|
|
1042
|
+
"t focus the next pane: inbox → proposed → changed",
|
|
1043
|
+
"a show / hide archived (acted-on) signals",
|
|
1044
|
+
"A / x accept / reject the focused rubric proposal (accept applies + auto-ratifies + commits locally)",
|
|
1045
|
+
"R then y arm and confirm a revert of the focused change — a local commit only, never pushed",
|
|
1046
|
+
"[8] scan — the whole-repo scan: units left, the unit's findings by validity tier (confirmed first; style grouped per rule) over the detail, the run on top:",
|
|
1047
|
+
"[ / ] · j/k previous / next unit · select a finding",
|
|
1048
|
+
"p / d promote (the companion files one ticket each; unconfirmed ones need an explicit yes) · dismiss with a reason (it becomes reviewer memory)",
|
|
1049
|
+
"f / F · t filter by state, lens, severity, tier, doubted · clear it · step the tier filter (confirmed → likely → doubtful → unknown)",
|
|
1050
|
+
"P the stage 2 plan: space toggles a row, ⏎ approves (hard stop = the kept total), Esc back",
|
|
1051
|
+
"x / D stop the running scan (the ledger resumes it) · ask the companion for a delta re-scan (it shows the price first)",
|
|
1052
|
+
"Ctrl-q release a focused pi pane or the companion column back to the board",
|
|
1053
|
+
"",
|
|
1054
|
+
"↑/↓ scroll when the terminal is short · any other key closes",
|
|
1055
|
+
];
|
|
1056
|
+
|
|
1057
|
+
// ---- live pi pane ----------------------------------------------------------
|
|
1058
|
+
|
|
1059
|
+
/** The viewer header after its name: where it is in the PR's logs, whether it
|
|
1060
|
+
* follows the tail, and the keys that move it. */
|
|
1061
|
+
function viewerHeaderTail(
|
|
1062
|
+
pane: PiPaneView,
|
|
1063
|
+
viewer: NonNullable<PiPaneView["viewer"]>,
|
|
1064
|
+
focused: boolean,
|
|
1065
|
+
): string {
|
|
1066
|
+
if (viewer.round === undefined) return `${FG.dim} · no round log yet${RESET}`;
|
|
1067
|
+
const round = `r${viewer.round}${viewer.rounds > 1 ? ` (${viewer.roundIndex}/${viewer.rounds})` : ""}${viewer.atLatest ? "" : " · past"}`;
|
|
1068
|
+
const stream = `${viewer.stream}${viewer.streams > 1 ? ` (${viewer.streamIndex}/${viewer.streams})` : ""}`;
|
|
1069
|
+
const state = pane.stale
|
|
1070
|
+
? "stale — refresh failing"
|
|
1071
|
+
: viewer.live === true
|
|
1072
|
+
? "running"
|
|
1073
|
+
: viewer.live === false
|
|
1074
|
+
? "settled"
|
|
1075
|
+
: "";
|
|
1076
|
+
const follow =
|
|
1077
|
+
pane.offset > 0
|
|
1078
|
+
? `${FG.accent} · ↑ ${pane.offset} rows back${RESET}${FG.dim} · End follows${RESET}`
|
|
1079
|
+
: `${FG.dim} · following${RESET}`;
|
|
1080
|
+
const keys = " · [ ] stream · < > round";
|
|
1081
|
+
const mode = focused ? " · Ctrl-q back to board" : "";
|
|
1082
|
+
return `${FG.dim} · ${round} · ${stream}${state ? ` · ${state}` : ""}${RESET}${follow}${FG.dim}${keys}${mode}${RESET}`;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Header of the pane: which session, whether it is live, whether it currently
|
|
1087
|
+
* owns the keyboard, and how far back in scrollback it is parked.
|
|
1088
|
+
*/
|
|
1089
|
+
export function piHeader(
|
|
1090
|
+
pr: number | undefined,
|
|
1091
|
+
pane: PiPaneView | undefined,
|
|
1092
|
+
focused = false,
|
|
1093
|
+
): string {
|
|
1094
|
+
if (pr === undefined) return `${FG.dim}PI — no PR selected${RESET}`;
|
|
1095
|
+
const mine = pane && pane.pr === pr ? pane : undefined;
|
|
1096
|
+
// A pi terminal (a debug round's or a takeover's) names its tmux session;
|
|
1097
|
+
// everything else is the PR's live transcript — there is no `pr-<n>` tmux
|
|
1098
|
+
// session to name.
|
|
1099
|
+
const session = mine?.viewer ? undefined : mine?.session;
|
|
1100
|
+
const viewer = mine?.viewer;
|
|
1101
|
+
const name =
|
|
1102
|
+
session === undefined
|
|
1103
|
+
? focused
|
|
1104
|
+
? `${ACCENT_BG}LIVE pr-${pr}${DRESET}`
|
|
1105
|
+
: `${MUTED}LIVE ${DRESET}${ACCENT}pr-${pr}${DRESET}`
|
|
1106
|
+
: focused
|
|
1107
|
+
? `${ACCENT_BG}PI ${session}${DRESET}`
|
|
1108
|
+
: `${MUTED}PI ${DRESET}${ACCENT}${session}${DRESET}`;
|
|
1109
|
+
if (!mine) return `${name}${FG.dim} · connecting…${RESET}`;
|
|
1110
|
+
if (viewer) return `${name}${viewerHeaderTail(mine, viewer, focused)}`;
|
|
1111
|
+
if (mine.missing) {
|
|
1112
|
+
return `${name}${FG.dim} · no pi session for #${pr}${mine.stale ? " · (stale)" : ""}${RESET}`;
|
|
1113
|
+
}
|
|
1114
|
+
const scroll =
|
|
1115
|
+
mine.offset > 0
|
|
1116
|
+
? `${FG.accent} · ↑ ${mine.offset} lines back${RESET}${FG.dim} · End live${RESET}`
|
|
1117
|
+
: "";
|
|
1118
|
+
const mode = focused
|
|
1119
|
+
? `${FG.accent} · focused${RESET}${FG.dim} · Ctrl-q back to board${RESET}`
|
|
1120
|
+
: `${FG.dim} · live mirror · click to type${RESET}`;
|
|
1121
|
+
return `${name}${mode}${scroll}`;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/** The window of the capture that fits the region, already width-clamped.
|
|
1125
|
+
* `pane.offset` parks the window that many lines above the live tail. */
|
|
1126
|
+
export function piBody(
|
|
1127
|
+
pr: number | undefined,
|
|
1128
|
+
pane: PiPaneView | undefined,
|
|
1129
|
+
width: number,
|
|
1130
|
+
height: number,
|
|
1131
|
+
): string[] {
|
|
1132
|
+
if (pr === undefined || height <= 0) return [];
|
|
1133
|
+
const mine = pane && pane.pr === pr ? pane : undefined;
|
|
1134
|
+
if (!mine) return [`${FG.dim}waiting for the first capture…${RESET}`];
|
|
1135
|
+
if (mine.lines.length === 0) return mine.missing ? [] : [`${FG.dim}(pane empty)${RESET}`];
|
|
1136
|
+
// Bottom-anchored, like the terminal it mirrors: new output lands on a
|
|
1137
|
+
// stable edge instead of shifting the whole pane down. A scrollback offset
|
|
1138
|
+
// moves that edge up through the captured history.
|
|
1139
|
+
const src =
|
|
1140
|
+
mine.offset > 0
|
|
1141
|
+
? mine.lines.slice(0, Math.max(0, mine.lines.length - mine.offset))
|
|
1142
|
+
: mine.lines;
|
|
1143
|
+
const tail = src.slice(-height).map((l) => truncate(l, width));
|
|
1144
|
+
return [...new Array<string>(Math.max(0, height - tail.length)).fill(""), ...tail];
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// ---- events overlay ---------------------------------------------------------
|
|
1148
|
+
|
|
1149
|
+
/** The demoted EVENTS strip: a floating, scrollable band over the board. */
|
|
1150
|
+
function renderEventsOverlay(
|
|
1151
|
+
frame: Frame,
|
|
1152
|
+
view: BoardView,
|
|
1153
|
+
cols: number,
|
|
1154
|
+
rows: number,
|
|
1155
|
+
barRows: number,
|
|
1156
|
+
): void {
|
|
1157
|
+
const { top, height } = eventsOverlayBand(rows, barRows);
|
|
1158
|
+
const visible = Math.max(0, height - 2);
|
|
1159
|
+
// The band is opaque: whatever it paints over must not stay clickable
|
|
1160
|
+
// underneath it (a hidden PR row, the pi pane's focus zone). Zones are
|
|
1161
|
+
// clipped to the rows the band leaves visible; only event rows below are
|
|
1162
|
+
// declared in its place.
|
|
1163
|
+
const bandY1 = top + 1; // frame row → terminal y
|
|
1164
|
+
const bandY2 = top + height;
|
|
1165
|
+
frame.zones = frame.zones.flatMap((z) => {
|
|
1166
|
+
if (z.y2 < bandY1 || z.y1 > bandY2) return [z];
|
|
1167
|
+
const kept: typeof frame.zones = [];
|
|
1168
|
+
if (z.y1 < bandY1) kept.push({ ...z, y2: bandY1 - 1 });
|
|
1169
|
+
if (z.y2 > bandY2) kept.push({ ...z, y1: bandY2 + 1 });
|
|
1170
|
+
return kept;
|
|
1171
|
+
});
|
|
1172
|
+
frame.set(
|
|
1173
|
+
top,
|
|
1174
|
+
rule(cols, "├", "┤", [
|
|
1175
|
+
sectionTitle("EVENTS"),
|
|
1176
|
+
`${MUTED}what the daemon just did${DRESET}`,
|
|
1177
|
+
`${MUTED}j/k scroll · e/Esc close${DRESET}`,
|
|
1178
|
+
]),
|
|
1179
|
+
);
|
|
1180
|
+
const slice = view.events.slice(view.eventsScroll, view.eventsScroll + visible);
|
|
1181
|
+
for (let i = 0; i < visible; i++) {
|
|
1182
|
+
const e = slice[i];
|
|
1183
|
+
const text = e
|
|
1184
|
+
? e.anomaly
|
|
1185
|
+
? `${FG.anomaly}${truncate(oneLine(e.text), cols - 4)}${RESET}`
|
|
1186
|
+
: truncate(oneLine(e.text), cols - 4)
|
|
1187
|
+
: "";
|
|
1188
|
+
frame.boxRow(top + 1 + i, [{ text: ` ${text}`, width: cols - 2 }]);
|
|
1189
|
+
const prMatch = e ? /#(\d+)/.exec(e.text) : null;
|
|
1190
|
+
if (prMatch) {
|
|
1191
|
+
frame.zones.push({
|
|
1192
|
+
x1: 2,
|
|
1193
|
+
y1: top + 1 + i + 1,
|
|
1194
|
+
x2: cols - 1,
|
|
1195
|
+
y2: top + 1 + i + 1,
|
|
1196
|
+
action: { t: "event-jump", pr: Number(prMatch[1]) } satisfies BoardAction,
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
frame.set(top + height - 1, rule(cols, "├", "┤", []));
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// ---- render ----------------------------------------------------------------
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* The draft as one paintable line of at most `width` cells: control bytes
|
|
1207
|
+
* stripped, each newline shown as `⏎`, and — when it overflows — its END kept
|
|
1208
|
+
* behind a `…`, because the end is where the operator types (the `▁` cursor).
|
|
1209
|
+
*/
|
|
1210
|
+
function draftLine(text: string, width: number): string {
|
|
1211
|
+
const full = `${sanitizeText(text).replace(/\n/g, "⏎")}▁`;
|
|
1212
|
+
if (displayWidth(full) <= width) return full;
|
|
1213
|
+
const chars = Array.from(full);
|
|
1214
|
+
let tail = "";
|
|
1215
|
+
let w = 0;
|
|
1216
|
+
for (let i = chars.length - 1; i >= 0; i--) {
|
|
1217
|
+
const cw = charWidth(chars[i]!.codePointAt(0) ?? 0);
|
|
1218
|
+
if (w + cw > width - 1) break;
|
|
1219
|
+
tail = chars[i] + tail;
|
|
1220
|
+
w += cw;
|
|
1221
|
+
}
|
|
1222
|
+
return `…${tail}`;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/** The input box's three rows: its target, the text being typed, its keys. */
|
|
1226
|
+
function inputBoxLines(box: NonNullable<BoardView["inputBox"]>, width: number): string[] {
|
|
1227
|
+
return [
|
|
1228
|
+
` ${HEADING}MSG → ${oneLine(box.target)}${DRESET}`,
|
|
1229
|
+
` > ${draftLine(box.text, Math.max(1, width - 3))}`,
|
|
1230
|
+
` ⏎ send · Esc cancel · Alt-⏎ newline`,
|
|
1231
|
+
];
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* The input box over the foot of a tab's own screen — the board paints it in
|
|
1236
|
+
* its pi region, but a prompt a tab opens (a memory edit, a rejection reason)
|
|
1237
|
+
* types into the same box and must be seen on that tab: a divider and the
|
|
1238
|
+
* box's three rows, just above the frame's bottom border.
|
|
1239
|
+
*/
|
|
1240
|
+
export function overlayInputBox(frame: Frame, box: NonNullable<BoardView["inputBox"]>): void {
|
|
1241
|
+
const top = frame.rows - 5;
|
|
1242
|
+
if (top < 1) return;
|
|
1243
|
+
frame.meta["inputBoxTop"] = top;
|
|
1244
|
+
frame.meta["inputBoxBottom"] = top + 3;
|
|
1245
|
+
frame.meta["inputBoxCols"] = frame.cols;
|
|
1246
|
+
frame.set(top, `├${"─".repeat(Math.max(0, frame.cols - 2))}┤`);
|
|
1247
|
+
inputBoxLines(box, frame.cols - 2).forEach((text, i) =>
|
|
1248
|
+
frame.boxRow(top + 1 + i, [{ text, width: frame.cols - 2 }]),
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
export function renderBoard(view: BoardView, cols: number, rows: number): Frame {
|
|
1253
|
+
const frame = new Frame(cols, rows);
|
|
1254
|
+
const now = view.now ?? new Date();
|
|
1255
|
+
frame.set(0, topBar(view, cols));
|
|
1256
|
+
const az = alertZone(view, cols);
|
|
1257
|
+
if (az)
|
|
1258
|
+
frame.zones.push({
|
|
1259
|
+
x1: az.x1,
|
|
1260
|
+
y1: 1,
|
|
1261
|
+
x2: az.x2,
|
|
1262
|
+
y2: 1,
|
|
1263
|
+
action: { t: "companion" } satisfies BoardAction,
|
|
1264
|
+
});
|
|
1265
|
+
const live = boardKeys(view);
|
|
1266
|
+
const barRows = barHeight("board", cols, live.bar);
|
|
1267
|
+
const pRows = piRows(rows, barRows);
|
|
1268
|
+
const bodyRows = rows - 2 - barRows - pRows; // top bar + command bar + bottom border
|
|
1269
|
+
const sorted = visiblePrs(view);
|
|
1270
|
+
const listW = listWidth(sorted, cols, now);
|
|
1271
|
+
const detailW = cols - 2 - listW - 1;
|
|
1272
|
+
const selectedPr = sorted[view.selected];
|
|
1273
|
+
frame.meta = { listW, bodyRows, piTop: 1 + bodyRows, piRows: pRows };
|
|
1274
|
+
|
|
1275
|
+
if (view.help) {
|
|
1276
|
+
frame.boxRow(1, [
|
|
1277
|
+
{ text: ` ${sectionTitle("KEYS", "everything the TUI answers to")}`, width: cols - 2 },
|
|
1278
|
+
]);
|
|
1279
|
+
const helpRows = Math.max(0, rows - 3);
|
|
1280
|
+
const helpMax = Math.max(0, HELP_LINES.length - helpRows);
|
|
1281
|
+
const helpTop = Math.min(Math.max(0, view.helpScroll ?? 0), helpMax);
|
|
1282
|
+
frame.meta["helpMax"] = helpMax;
|
|
1283
|
+
HELP_LINES.slice(helpTop, helpTop + helpRows).forEach((l, i) => {
|
|
1284
|
+
frame.boxRow(2 + i, [{ text: ` ${l}`, width: cols - 2 }]);
|
|
1285
|
+
});
|
|
1286
|
+
for (let r = 2 + Math.min(HELP_LINES.length - helpTop, helpRows); r < rows - 1; r++)
|
|
1287
|
+
frame.boxRow(r, [{ text: "", width: cols - 2 }]);
|
|
1288
|
+
frame.set(rows - 1, rule(cols, "└", "┘", []));
|
|
1289
|
+
return frame;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// Board body: list | detail.
|
|
1293
|
+
//
|
|
1294
|
+
// A snapshot that never arrived is unknown, not empty. The count
|
|
1295
|
+
// reads `?`, the rows are replaced by the reason and the remedy, and the
|
|
1296
|
+
// detail pane says "no data" rather than "no PR selected".
|
|
1297
|
+
const known = view.snapshot !== undefined;
|
|
1298
|
+
const flabel = filterLabel(view.boardFilter);
|
|
1299
|
+
const listLines: string[] = known
|
|
1300
|
+
? [section(`PRS (${sorted.length})`, flabel !== "" ? flabel : "the daemon's live set")]
|
|
1301
|
+
: [
|
|
1302
|
+
section("PRS (?)", "unknown — the daemon has not answered"),
|
|
1303
|
+
...unknownSourceLines(view.connection, listW - 1),
|
|
1304
|
+
];
|
|
1305
|
+
// The list scrolls with the selection: the verbs act on the selected PR, so
|
|
1306
|
+
// it must never sit off-screen (`windowStart`, the tabs' shared rule). The
|
|
1307
|
+
// header stays put; the last body row is the legend, painted over whatever
|
|
1308
|
+
// would sit there, so the PR slots are the rows between the two.
|
|
1309
|
+
const prSlots = Math.max(1, bodyRows - 1 - listLines.length);
|
|
1310
|
+
const start = windowStart(view.selected, sorted.length, prSlots);
|
|
1311
|
+
sorted.slice(start, start + prSlots).forEach((pr, offset) => {
|
|
1312
|
+
const i = start + offset;
|
|
1313
|
+
listLines.push(styledRow(pr, i === view.selected, listW - 1, now, view.repo));
|
|
1314
|
+
const y = 1 + listLines.length - 1 + 1; // frame row of this PR line (below top bar)
|
|
1315
|
+
// Only painted rows get zones: with a long list, off-screen rows would
|
|
1316
|
+
// otherwise leave phantom click targets over the pi region and the
|
|
1317
|
+
// bar-row gaps — a click there could select (or double-click into the
|
|
1318
|
+
// pi pane of) an invisible PR. The slice above holds only painted rows,
|
|
1319
|
+
// but a body too short for even one slot must still declare nothing.
|
|
1320
|
+
if (listLines.length > bodyRows - 1) return;
|
|
1321
|
+
frame.zones.push({
|
|
1322
|
+
x1: 2,
|
|
1323
|
+
y1: y,
|
|
1324
|
+
x2: listW,
|
|
1325
|
+
y2: y,
|
|
1326
|
+
action: { t: "select-pr", pr: pr.number } satisfies BoardAction,
|
|
1327
|
+
});
|
|
1328
|
+
frame.zones.push({
|
|
1329
|
+
x1: 4,
|
|
1330
|
+
y1: y,
|
|
1331
|
+
x2: 4 + String(pr.number).length + 1,
|
|
1332
|
+
y2: y,
|
|
1333
|
+
action: { t: "open-github", pr: pr.number } satisfies BoardAction,
|
|
1334
|
+
});
|
|
1335
|
+
});
|
|
1336
|
+
const progressRows: ProgressRow[] = [];
|
|
1337
|
+
const dLines = known
|
|
1338
|
+
? detailLines(
|
|
1339
|
+
view.detail,
|
|
1340
|
+
selectedPr,
|
|
1341
|
+
detailW - 2,
|
|
1342
|
+
now,
|
|
1343
|
+
view.progress?.pr === selectedPr?.number ? view.progress : undefined,
|
|
1344
|
+
progressRows,
|
|
1345
|
+
)
|
|
1346
|
+
: unknownDetailLines(view.connection, detailW - 2);
|
|
1347
|
+
// With the transcript viewer in the pane, a lens row of the progress panel is
|
|
1348
|
+
// a click target: it selects that lens's transcript. A painted row only (an
|
|
1349
|
+
// off-screen one would be a phantom target), and only while the viewer is up.
|
|
1350
|
+
if (view.pi?.viewer && view.pi.pr === selectedPr?.number) {
|
|
1351
|
+
for (const row of progressRows) {
|
|
1352
|
+
if (row.line >= bodyRows) continue;
|
|
1353
|
+
frame.zones.push({
|
|
1354
|
+
x1: listW + 3,
|
|
1355
|
+
y1: row.line + 2,
|
|
1356
|
+
x2: cols - 1,
|
|
1357
|
+
y2: row.line + 2,
|
|
1358
|
+
action: { t: "viewer-lens", lens: row.lens, nth: row.nth } satisfies BoardAction,
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
for (let r = 0; r < bodyRows; r++) {
|
|
1363
|
+
const isLegendRow = r === bodyRows - 1;
|
|
1364
|
+
frame.boxRow(1 + r, [
|
|
1365
|
+
{ text: isLegendRow ? ` ${legendLine()}` : ` ${listLines[r] ?? ""}`, width: listW },
|
|
1366
|
+
{ text: ` ${dLines[r] ?? ""}`, width: detailW },
|
|
1367
|
+
]);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// Live pi mirror (or the quick-message input box) — the lower region.
|
|
1371
|
+
const piTop = 1 + bodyRows;
|
|
1372
|
+
const piLines = Math.max(0, pRows - 2); // the region is divider + header + lines
|
|
1373
|
+
const panePr = view.piPr ?? selectedPr?.number;
|
|
1374
|
+
const piFocused = view.piFocus === true && panePr !== undefined;
|
|
1375
|
+
if (pRows > 0) {
|
|
1376
|
+
// Focused, the divider is drawn heavy and accented — the pane visibly
|
|
1377
|
+
// becomes its own window rather than a strip of the board.
|
|
1378
|
+
const bar = piFocused ? "━" : "─";
|
|
1379
|
+
const inner = `${fit(bar.repeat(listW), listW)}${piFocused ? bar : "┴"}${bar.repeat(detailW)}`;
|
|
1380
|
+
frame.set(piTop, `│${piFocused ? `${FG.accent}${inner}${RESET}` : inner}│`);
|
|
1381
|
+
}
|
|
1382
|
+
if (view.inputBox) {
|
|
1383
|
+
inputBoxLines(view.inputBox, cols - 2).forEach((text, i) =>
|
|
1384
|
+
frame.boxRow(piTop + 1 + i, [{ text, width: cols - 2 }]),
|
|
1385
|
+
);
|
|
1386
|
+
for (let i = 4; i < pRows; i++) frame.boxRow(piTop + i, [{ text: "", width: cols - 2 }]);
|
|
1387
|
+
} else if (pRows > 0) {
|
|
1388
|
+
frame.boxRow(piTop + 1, [
|
|
1389
|
+
{
|
|
1390
|
+
text: ` ${piHeader(panePr, view.pi, piFocused)}${view.noticeLine ? ` ${view.noticeLine}` : ""}`,
|
|
1391
|
+
width: cols - 2,
|
|
1392
|
+
},
|
|
1393
|
+
]);
|
|
1394
|
+
// Width comes from piPaneSize so the rectangle tmux is resized to and the
|
|
1395
|
+
// rectangle the capture is painted into cannot drift apart.
|
|
1396
|
+
const paneCols = piPaneSize(cols, rows, barRows)?.cols ?? 0;
|
|
1397
|
+
// The viewer keeps its last row for its input line (the PR-pane rule): the
|
|
1398
|
+
// transcript above it, so a mirror's rows are untouched.
|
|
1399
|
+
const line =
|
|
1400
|
+
piLines > 1 && view.pi?.viewer && view.pi.pr === panePr && view.viewerInput?.pr === panePr
|
|
1401
|
+
? view.viewerInput
|
|
1402
|
+
: undefined;
|
|
1403
|
+
const bodyLines = line ? piLines - 1 : piLines;
|
|
1404
|
+
const body = piBody(panePr, view.pi, paneCols, bodyLines);
|
|
1405
|
+
for (let i = 0; i < bodyLines; i++)
|
|
1406
|
+
frame.boxRow(piTop + 2 + i, [{ text: ` ${body[i] ?? ""}`, width: cols - 2 }]);
|
|
1407
|
+
if (line)
|
|
1408
|
+
frame.boxRow(piTop + 2 + bodyLines, [
|
|
1409
|
+
{ text: ` ${viewerInputRow(line, paneCols)}`, width: cols - 2 },
|
|
1410
|
+
]);
|
|
1411
|
+
// Clicking anywhere in the region (divider, header or body) gives the pane
|
|
1412
|
+
// the keyboard, in place — nothing on the board takes the whole terminal.
|
|
1413
|
+
if (panePr !== undefined) {
|
|
1414
|
+
frame.zones.push({
|
|
1415
|
+
x1: 2,
|
|
1416
|
+
y1: piTop + 1,
|
|
1417
|
+
x2: cols - 1,
|
|
1418
|
+
y2: piTop + pRows,
|
|
1419
|
+
action: { t: "pi-focus" } satisfies BoardAction,
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// EVENTS: a floating overlay on `e`.
|
|
1425
|
+
// The quick-message editor owns the keyboard while open, so it stays visible.
|
|
1426
|
+
if (view.eventsFocus && !view.inputBox) renderEventsOverlay(frame, view, cols, rows, barRows);
|
|
1427
|
+
|
|
1428
|
+
// The shared command bar: exactly the verbs this frame answers to.
|
|
1429
|
+
paintCmdBar(frame, "board", cols, rows, live.bar);
|
|
1430
|
+
frame.set(rows - 1, rule(cols, "└", "┘", []));
|
|
1431
|
+
return frame;
|
|
1432
|
+
}
|