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