@quickchatai/quickreview 1.1.0 → 1.3.1

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 (471) hide show
  1. package/README.md +137 -57
  2. package/bin/qcr.js +1 -1
  3. package/config.example.toml +60 -14
  4. package/deploy/ansible/README.md +20 -12
  5. package/deploy/ansible/inventory.example.ini +1 -1
  6. package/deploy/collect-secrets.sh +1 -1
  7. package/deploy/qcr-update.sh +1 -1
  8. package/dist/extensions/cli/home.js +14 -0
  9. package/dist/extensions/cli/home.js.map +1 -0
  10. package/dist/extensions/cli/login-route.js +5 -0
  11. package/dist/extensions/cli/login-route.js.map +1 -0
  12. package/dist/extensions/cli/selfimprove.js +18 -0
  13. package/dist/extensions/cli/selfimprove.js.map +1 -0
  14. package/dist/extensions/home.js +350 -0
  15. package/dist/extensions/home.js.map +1 -0
  16. package/dist/extensions/login-route.js +180 -0
  17. package/dist/extensions/login-route.js.map +1 -0
  18. package/dist/extensions/onboarding.js +35 -11
  19. package/dist/extensions/onboarding.js.map +1 -1
  20. package/dist/extensions/selfimprove.js +49 -0
  21. package/dist/extensions/selfimprove.js.map +1 -0
  22. package/dist/extensions/setup-scan.js +23 -592
  23. package/dist/extensions/setup-scan.js.map +1 -1
  24. package/dist/extensions/setup.js +2 -47
  25. package/dist/extensions/setup.js.map +1 -1
  26. package/dist/src/archive/restore.js +18 -15
  27. package/dist/src/archive/restore.js.map +1 -1
  28. package/dist/src/botworker.js +13 -2
  29. package/dist/src/botworker.js.map +1 -1
  30. package/dist/src/channelfs.js +3 -26
  31. package/dist/src/channelfs.js.map +1 -1
  32. package/dist/src/ciretry.js +2 -1
  33. package/dist/src/ciretry.js.map +1 -1
  34. package/dist/src/cli.js +54 -61
  35. package/dist/src/cli.js.map +1 -1
  36. package/dist/src/cliops.js +3 -2
  37. package/dist/src/cliops.js.map +1 -1
  38. package/dist/src/commands.js +25 -2
  39. package/dist/src/commands.js.map +1 -1
  40. package/dist/src/config.js +32 -2
  41. package/dist/src/config.js.map +1 -1
  42. package/dist/src/daemon.js +50 -8
  43. package/dist/src/daemon.js.map +1 -1
  44. package/dist/src/debuground.js +1 -0
  45. package/dist/src/debuground.js.map +1 -1
  46. package/dist/src/debugroundpi.js +6 -1
  47. package/dist/src/debugroundpi.js.map +1 -1
  48. package/dist/src/dispatch.js +5 -4
  49. package/dist/src/dispatch.js.map +1 -1
  50. package/dist/src/engine/caserunner-sdk.js +15 -251
  51. package/dist/src/engine/caserunner-sdk.js.map +1 -1
  52. package/dist/src/engine/debugpi.js +17 -5
  53. package/dist/src/engine/debugpi.js.map +1 -1
  54. package/dist/src/execboundary.js +10 -3
  55. package/dist/src/execboundary.js.map +1 -1
  56. package/dist/src/github.js +51 -16
  57. package/dist/src/github.js.map +1 -1
  58. package/dist/src/githubtoken.js +346 -0
  59. package/dist/src/githubtoken.js.map +1 -0
  60. package/dist/src/home/adapters.js +349 -0
  61. package/dist/src/home/adapters.js.map +1 -0
  62. package/dist/src/home/advanced.js +318 -0
  63. package/dist/src/home/advanced.js.map +1 -0
  64. package/dist/src/home/advancedtools.js +57 -0
  65. package/dist/src/home/advancedtools.js.map +1 -0
  66. package/dist/src/home/autoreview.js +325 -0
  67. package/dist/src/home/autoreview.js.map +1 -0
  68. package/dist/src/home/daemonstatus.js +32 -0
  69. package/dist/src/home/daemonstatus.js.map +1 -0
  70. package/dist/src/home/devicelogin.js +61 -0
  71. package/dist/src/home/devicelogin.js.map +1 -0
  72. package/dist/src/home/launch.js +202 -0
  73. package/dist/src/home/launch.js.map +1 -0
  74. package/dist/src/home/loginowner.js +205 -0
  75. package/dist/src/home/loginowner.js.map +1 -0
  76. package/dist/src/home/memory.js +122 -0
  77. package/dist/src/home/memory.js.map +1 -0
  78. package/dist/src/home/model.js +811 -0
  79. package/dist/src/home/model.js.map +1 -0
  80. package/dist/src/home/panel.js +520 -0
  81. package/dist/src/home/panel.js.map +1 -0
  82. package/dist/src/home/ports.js +19 -0
  83. package/dist/src/home/ports.js.map +1 -0
  84. package/dist/src/home/review.js +163 -0
  85. package/dist/src/home/review.js.map +1 -0
  86. package/dist/src/home/reviews.js +478 -0
  87. package/dist/src/home/reviews.js.map +1 -0
  88. package/dist/src/home/setup.js +507 -0
  89. package/dist/src/home/setup.js.map +1 -0
  90. package/dist/src/home/setupadapters.js +214 -0
  91. package/dist/src/home/setupadapters.js.map +1 -0
  92. package/dist/src/home/setupcard.js +199 -0
  93. package/dist/src/home/setupcard.js.map +1 -0
  94. package/dist/src/home/setuptools.js +45 -0
  95. package/dist/src/home/setuptools.js.map +1 -0
  96. package/dist/src/home/spend.js +160 -0
  97. package/dist/src/home/spend.js.map +1 -0
  98. package/dist/src/home/switches.js +209 -0
  99. package/dist/src/home/switches.js.map +1 -0
  100. package/dist/src/home/switchon.js +439 -0
  101. package/dist/src/home/switchon.js.map +1 -0
  102. package/dist/src/home/toolnames.js +41 -0
  103. package/dist/src/home/toolnames.js.map +1 -0
  104. package/dist/src/home/tune.js +341 -0
  105. package/dist/src/home/tune.js.map +1 -0
  106. package/dist/src/home/words.js +7 -0
  107. package/dist/src/home/words.js.map +1 -0
  108. package/dist/src/homes.js +14 -0
  109. package/dist/src/homes.js.map +1 -1
  110. package/dist/src/install.js +1 -1
  111. package/dist/src/install.js.map +1 -1
  112. package/dist/src/ipc/workerspec.js +8 -0
  113. package/dist/src/ipc/workerspec.js.map +1 -1
  114. package/dist/src/live/publish.js +1 -1
  115. package/dist/src/live/publish.js.map +1 -1
  116. package/dist/src/livecli.js +1 -1
  117. package/dist/src/livecli.js.map +1 -1
  118. package/dist/src/loginhint.js +8 -3
  119. package/dist/src/loginhint.js.map +1 -1
  120. package/dist/src/loginrefresh/handover.js +121 -0
  121. package/dist/src/loginrefresh/handover.js.map +1 -0
  122. package/dist/src/loginrefresh/lock.js +54 -3
  123. package/dist/src/loginrefresh/lock.js.map +1 -1
  124. package/dist/src/loginrefresh/pirefresh.js +3 -0
  125. package/dist/src/loginrefresh/pirefresh.js.map +1 -1
  126. package/dist/src/loginrefresh/refresher.js +33 -5
  127. package/dist/src/loginrefresh/refresher.js.map +1 -1
  128. package/dist/src/loginrefresh/supervisor.js +18 -1
  129. package/dist/src/loginrefresh/supervisor.js.map +1 -1
  130. package/dist/src/npmverbs.js +46 -14
  131. package/dist/src/npmverbs.js.map +1 -1
  132. package/dist/src/onboard/choices.js +35 -4
  133. package/dist/src/onboard/choices.js.map +1 -1
  134. package/dist/src/onboard/engine.js +32 -306
  135. package/dist/src/onboard/engine.js.map +1 -1
  136. package/dist/src/onboard/pipin.js +22 -0
  137. package/dist/src/onboard/pipin.js.map +1 -0
  138. package/dist/src/onboard/stepforward.js +41 -0
  139. package/dist/src/onboard/stepforward.js.map +1 -0
  140. package/dist/src/onboard/steps/appslug.js +4 -1
  141. package/dist/src/onboard/steps/appslug.js.map +1 -1
  142. package/dist/src/onboard/steps/bringup.js +3 -2
  143. package/dist/src/onboard/steps/bringup.js.map +1 -1
  144. package/dist/src/onboard/steps/compose.js +29 -0
  145. package/dist/src/onboard/steps/compose.js.map +1 -1
  146. package/dist/src/onboard/steps/configfill.js +120 -39
  147. package/dist/src/onboard/steps/configfill.js.map +1 -1
  148. package/dist/src/onboard/steps/githubaccount.js +595 -0
  149. package/dist/src/onboard/steps/githubaccount.js.map +1 -0
  150. package/dist/src/onboard/steps/githubapp.js +65 -7
  151. package/dist/src/onboard/steps/githubapp.js.map +1 -1
  152. package/dist/src/onboard/steps/golive.js +2 -0
  153. package/dist/src/onboard/steps/golive.js.map +1 -1
  154. package/dist/src/onboard/steps/index.js +11 -0
  155. package/dist/src/onboard/steps/index.js.map +1 -1
  156. package/dist/src/onboard/steps/ingress.js +80 -25
  157. package/dist/src/onboard/steps/ingress.js.map +1 -1
  158. package/dist/src/onboard/steps/llmauth.js +103 -30
  159. package/dist/src/onboard/steps/llmauth.js.map +1 -1
  160. package/dist/src/onboard/steps/loginshare.js +213 -0
  161. package/dist/src/onboard/steps/loginshare.js.map +1 -0
  162. package/dist/src/onboard/steps/memory.js +67 -3
  163. package/dist/src/onboard/steps/memory.js.map +1 -1
  164. package/dist/src/onboard/steps/proof.js +366 -56
  165. package/dist/src/onboard/steps/proof.js.map +1 -1
  166. package/dist/src/onboard/steps/repoconfig.js +72 -0
  167. package/dist/src/onboard/steps/repoconfig.js.map +1 -0
  168. package/dist/src/onboard/steps/topology.js +6 -6
  169. package/dist/src/onboard/steps/topology.js.map +1 -1
  170. package/dist/src/onboard/steps/transport.js +9 -1
  171. package/dist/src/onboard/steps/transport.js.map +1 -1
  172. package/dist/src/onboard/steps/tuiroute.js +102 -0
  173. package/dist/src/onboard/steps/tuiroute.js.map +1 -0
  174. package/dist/src/onboard/steps/verifyup.js +17 -1
  175. package/dist/src/onboard/steps/verifyup.js.map +1 -1
  176. package/dist/src/onboard/toolnames.js +30 -0
  177. package/dist/src/onboard/toolnames.js.map +1 -0
  178. package/dist/src/operatorwrite.js +23 -0
  179. package/dist/src/operatorwrite.js.map +1 -0
  180. package/dist/src/outcomes.js +4 -1
  181. package/dist/src/outcomes.js.map +1 -1
  182. package/dist/src/piagentdir.js +13 -0
  183. package/dist/src/piagentdir.js.map +1 -0
  184. package/dist/src/pilogin.js +31 -16
  185. package/dist/src/pilogin.js.map +1 -1
  186. package/dist/src/posting-journal.js +2 -1
  187. package/dist/src/posting-journal.js.map +1 -1
  188. package/dist/src/posting-outbox.js +6 -2
  189. package/dist/src/posting-outbox.js.map +1 -1
  190. package/dist/src/posting-receipt.js +14 -1
  191. package/dist/src/posting-receipt.js.map +1 -1
  192. package/dist/src/qcrbin.js +16 -11
  193. package/dist/src/qcrbin.js.map +1 -1
  194. package/dist/src/recheck.js +2 -1
  195. package/dist/src/recheck.js.map +1 -1
  196. package/dist/src/reconcile.js +22 -2
  197. package/dist/src/reconcile.js.map +1 -1
  198. package/dist/src/reviewargs.js +122 -0
  199. package/dist/src/reviewargs.js.map +1 -0
  200. package/dist/src/reviewcli.js +7 -161
  201. package/dist/src/reviewcli.js.map +1 -1
  202. package/dist/src/reviewdetach.js +76 -11
  203. package/dist/src/reviewdetach.js.map +1 -1
  204. package/dist/src/reviewhistory.js +32 -6
  205. package/dist/src/reviewhistory.js.map +1 -1
  206. package/dist/src/reviewnow.js +45 -0
  207. package/dist/src/reviewnow.js.map +1 -0
  208. package/dist/src/scancli.js +39 -691
  209. package/dist/src/scancli.js.map +1 -1
  210. package/dist/src/selfimprove.js +66 -0
  211. package/dist/src/selfimprove.js.map +1 -0
  212. package/dist/src/setup/adapters.js +5 -223
  213. package/dist/src/setup/adapters.js.map +1 -1
  214. package/dist/src/setup/caserunner.js +15 -249
  215. package/dist/src/setup/caserunner.js.map +1 -1
  216. package/dist/src/setup/engine.js +32 -341
  217. package/dist/src/setup/engine.js.map +1 -1
  218. package/dist/src/setup/facts.js +3 -592
  219. package/dist/src/setup/facts.js.map +1 -1
  220. package/dist/src/setup/findings.js +3 -947
  221. package/dist/src/setup/findings.js.map +1 -1
  222. package/dist/src/setup/presentation.js +3 -8
  223. package/dist/src/setup/presentation.js.map +1 -1
  224. package/dist/src/setup/scan.js +3 -724
  225. package/dist/src/setup/scan.js.map +1 -1
  226. package/dist/src/setup/scanbuild.js +9 -92
  227. package/dist/src/setup/scanbuild.js.map +1 -1
  228. package/dist/src/setup/scanlease.js +3 -250
  229. package/dist/src/setup/scanlease.js.map +1 -1
  230. package/dist/src/setup/scanreport.js +3 -288
  231. package/dist/src/setup/scanreport.js.map +1 -1
  232. package/dist/src/setup/scanrun.js +3 -1732
  233. package/dist/src/setup/scanrun.js.map +1 -1
  234. package/dist/src/setup/scantree.js +3 -256
  235. package/dist/src/setup/scantree.js.map +1 -1
  236. package/dist/src/setup/stage2request.js +3 -258
  237. package/dist/src/setup/stage2request.js.map +1 -1
  238. package/dist/src/setup/unitcut.js +3 -436
  239. package/dist/src/setup/unitcut.js.map +1 -1
  240. package/dist/src/state.js +2 -0
  241. package/dist/src/state.js.map +1 -1
  242. package/dist/src/statemachine.js +4 -1
  243. package/dist/src/statemachine.js.map +1 -1
  244. package/dist/src/takeover.js +1 -0
  245. package/dist/src/takeover.js.map +1 -1
  246. package/dist/src/takeoverpi.js +15 -1
  247. package/dist/src/takeoverpi.js.map +1 -1
  248. package/dist/src/tui/app.js +49 -0
  249. package/dist/src/tui/app.js.map +1 -1
  250. package/dist/src/tui/board.js +88 -12
  251. package/dist/src/tui/board.js.map +1 -1
  252. package/dist/src/tui/cmdbar.js +4 -1
  253. package/dist/src/tui/cmdbar.js.map +1 -1
  254. package/dist/src/tui/companion.js +76 -11
  255. package/dist/src/tui/companion.js.map +1 -1
  256. package/dist/src/tui/connect.js +7 -1
  257. package/dist/src/tui/connect.js.map +1 -1
  258. package/dist/src/tui/deploytab.js +2 -2
  259. package/dist/src/tui/deploytab.js.map +1 -1
  260. package/dist/src/tui/filters.js +5 -2
  261. package/dist/src/tui/filters.js.map +1 -1
  262. package/dist/src/tui/ghread.js +61 -5
  263. package/dist/src/tui/ghread.js.map +1 -1
  264. package/dist/src/tui/index.js +109 -20
  265. package/dist/src/tui/index.js.map +1 -1
  266. package/dist/src/tui/memforward.js +39 -2
  267. package/dist/src/tui/memforward.js.map +1 -1
  268. package/dist/src/tui/memory.js +3 -16
  269. package/dist/src/tui/memory.js.map +1 -1
  270. package/dist/src/tui/memoryurl.js +22 -0
  271. package/dist/src/tui/memoryurl.js.map +1 -0
  272. package/dist/src/tui/model.js +25 -0
  273. package/dist/src/tui/model.js.map +1 -1
  274. package/dist/src/tui/prpause.js +7 -0
  275. package/dist/src/tui/prpause.js.map +1 -1
  276. package/dist/src/tui/remote.js +7 -1
  277. package/dist/src/tui/remote.js.map +1 -1
  278. package/dist/src/tui/route.js +143 -0
  279. package/dist/src/tui/route.js.map +1 -0
  280. package/dist/src/tui/setupstate.js +32 -0
  281. package/dist/src/tui/setupstate.js.map +1 -0
  282. package/dist/src/tui/sshtriage.js +102 -5
  283. package/dist/src/tui/sshtriage.js.map +1 -1
  284. package/dist/src/tui/theme.js +2 -1
  285. package/dist/src/tui/theme.js.map +1 -1
  286. package/dist/src/tui/unwedge.js +2 -12
  287. package/dist/src/tui/unwedge.js.map +1 -1
  288. package/dist/src/verdicts.js +17 -5
  289. package/dist/src/verdicts.js.map +1 -1
  290. package/dist/src/version.js +1 -1
  291. package/dist/src/version.js.map +1 -1
  292. package/dist/src/webhook.js +12 -1
  293. package/dist/src/webhook.js.map +1 -1
  294. package/dist/src/wire.js +34 -8
  295. package/dist/src/wire.js.map +1 -1
  296. package/dist/src/workerworld.js +11 -1
  297. package/dist/src/workerworld.js.map +1 -1
  298. package/docker/README.md +1 -1
  299. package/docker/companion/entrypoint.sh +20 -1
  300. package/docker/entrypoint.sh +57 -6
  301. package/docker-compose.yml +16 -1
  302. package/extensions/cli/home.ts +14 -0
  303. package/extensions/cli/login-route.ts +10 -0
  304. package/extensions/cli/selfimprove.ts +18 -0
  305. package/extensions/home.ts +484 -0
  306. package/extensions/login-route.ts +220 -0
  307. package/extensions/onboarding.ts +41 -11
  308. package/extensions/selfimprove.ts +65 -0
  309. package/extensions/setup-scan.ts +79 -766
  310. package/extensions/setup.ts +14 -70
  311. package/package.json +6 -4
  312. package/prompts/onboarding.md +58 -18
  313. package/schemas/review-result.v1.json +60 -0
  314. package/scripts/deploy.sh +8 -6
  315. package/scripts/mint-token.sh +25 -4
  316. package/scripts/qcr-host.sh +28 -5
  317. package/scripts/vm-ghcr-login.sh +7 -4
  318. package/src/archive/restore.ts +18 -21
  319. package/src/botworker.ts +13 -2
  320. package/src/channelfs.ts +3 -26
  321. package/src/ciretry.ts +2 -1
  322. package/src/cli.ts +59 -67
  323. package/src/cliops.ts +3 -2
  324. package/src/commands.ts +28 -2
  325. package/src/config.ts +62 -4
  326. package/src/daemon.ts +58 -7
  327. package/src/debuground.ts +3 -0
  328. package/src/debugroundpi.ts +12 -1
  329. package/src/dispatch.ts +5 -4
  330. package/src/engine/caserunner-sdk.ts +29 -314
  331. package/src/engine/debugpi.ts +17 -5
  332. package/src/execboundary.ts +16 -4
  333. package/src/github.ts +57 -15
  334. package/src/githubtoken.ts +435 -0
  335. package/src/home/adapters.ts +418 -0
  336. package/src/home/advanced.ts +421 -0
  337. package/src/home/advancedtools.ts +96 -0
  338. package/src/home/autoreview.ts +385 -0
  339. package/src/home/daemonstatus.ts +55 -0
  340. package/src/home/devicelogin.ts +107 -0
  341. package/src/home/launch.ts +257 -0
  342. package/src/home/loginowner.ts +248 -0
  343. package/src/home/memory.ts +176 -0
  344. package/src/home/model.ts +965 -0
  345. package/src/home/panel.ts +666 -0
  346. package/src/home/ports.ts +338 -0
  347. package/src/home/review.ts +206 -0
  348. package/src/home/reviews.ts +565 -0
  349. package/src/home/setup.ts +689 -0
  350. package/src/home/setupadapters.ts +217 -0
  351. package/src/home/setupcard.ts +288 -0
  352. package/src/home/setuptools.ts +57 -0
  353. package/src/home/spend.ts +194 -0
  354. package/src/home/switches.ts +293 -0
  355. package/src/home/switchon.ts +554 -0
  356. package/src/home/toolnames.ts +46 -0
  357. package/src/home/tune.ts +405 -0
  358. package/src/home/words.ts +8 -0
  359. package/src/homes.ts +17 -0
  360. package/src/install.ts +1 -1
  361. package/src/ipc/workerspec.ts +15 -0
  362. package/src/live/publish.ts +4 -1
  363. package/src/livecli.ts +6 -1
  364. package/src/loginhint.ts +8 -3
  365. package/src/loginrefresh/handover.ts +138 -0
  366. package/src/loginrefresh/lock.ts +72 -3
  367. package/src/loginrefresh/pirefresh.ts +3 -0
  368. package/src/loginrefresh/refresher.ts +34 -5
  369. package/src/loginrefresh/supervisor.ts +25 -2
  370. package/src/npmverbs.ts +55 -13
  371. package/src/onboard/choices.ts +40 -4
  372. package/src/onboard/engine.ts +70 -421
  373. package/src/onboard/pipin.ts +31 -0
  374. package/src/onboard/stepforward.ts +61 -0
  375. package/src/onboard/steps/appslug.ts +4 -1
  376. package/src/onboard/steps/bringup.ts +3 -2
  377. package/src/onboard/steps/compose.ts +31 -0
  378. package/src/onboard/steps/configfill.ts +144 -40
  379. package/src/onboard/steps/githubaccount.ts +716 -0
  380. package/src/onboard/steps/githubapp.ts +92 -8
  381. package/src/onboard/steps/golive.ts +2 -0
  382. package/src/onboard/steps/index.ts +11 -0
  383. package/src/onboard/steps/ingress.ts +98 -31
  384. package/src/onboard/steps/llmauth.ts +114 -29
  385. package/src/onboard/steps/loginshare.ts +258 -0
  386. package/src/onboard/steps/memory.ts +76 -3
  387. package/src/onboard/steps/proof.ts +473 -55
  388. package/src/onboard/steps/repoconfig.ts +85 -0
  389. package/src/onboard/steps/topology.ts +7 -6
  390. package/src/onboard/steps/transport.ts +15 -0
  391. package/src/onboard/steps/tuiroute.ts +119 -0
  392. package/src/onboard/steps/verifyup.ts +20 -1
  393. package/src/onboard/toolnames.ts +34 -0
  394. package/src/operatorwrite.ts +25 -0
  395. package/src/outcomes.ts +8 -1
  396. package/src/piagentdir.ts +12 -0
  397. package/src/pilogin.ts +32 -16
  398. package/src/posting-journal.ts +6 -1
  399. package/src/posting-outbox.ts +10 -2
  400. package/src/posting-receipt.ts +17 -0
  401. package/src/qcrbin.ts +16 -11
  402. package/src/recheck.ts +2 -1
  403. package/src/reconcile.ts +29 -3
  404. package/src/reviewargs.ts +119 -0
  405. package/src/reviewcli.ts +6 -167
  406. package/src/reviewdetach.ts +107 -13
  407. package/src/reviewhistory.ts +20 -6
  408. package/src/reviewnow.ts +76 -0
  409. package/src/scancli.ts +75 -826
  410. package/src/selfimprove.ts +90 -0
  411. package/src/setup/adapters.ts +13 -221
  412. package/src/setup/caserunner.ts +34 -273
  413. package/src/setup/engine.ts +62 -496
  414. package/src/setup/facts.ts +3 -627
  415. package/src/setup/findings.ts +3 -1264
  416. package/src/setup/presentation.ts +3 -18
  417. package/src/setup/scan.ts +3 -1033
  418. package/src/setup/scanbuild.ts +20 -114
  419. package/src/setup/scanlease.ts +3 -297
  420. package/src/setup/scanreport.ts +3 -373
  421. package/src/setup/scanrun.ts +3 -2268
  422. package/src/setup/scantree.ts +3 -259
  423. package/src/setup/stage2request.ts +3 -347
  424. package/src/setup/unitcut.ts +3 -523
  425. package/src/state.ts +43 -0
  426. package/src/statemachine.ts +4 -1
  427. package/src/takeover.ts +5 -0
  428. package/src/takeoverpi.ts +16 -2
  429. package/src/tui/app.ts +47 -0
  430. package/src/tui/board.ts +111 -14
  431. package/src/tui/cmdbar.ts +4 -1
  432. package/src/tui/companion.ts +80 -11
  433. package/src/tui/connect.ts +7 -1
  434. package/src/tui/deploytab.ts +2 -2
  435. package/src/tui/filters.ts +7 -3
  436. package/src/tui/ghread.ts +74 -7
  437. package/src/tui/index.ts +117 -23
  438. package/src/tui/memforward.ts +52 -3
  439. package/src/tui/memory.ts +3 -15
  440. package/src/tui/memoryurl.ts +22 -0
  441. package/src/tui/model.ts +36 -2
  442. package/src/tui/prpause.ts +11 -2
  443. package/src/tui/remote.ts +9 -1
  444. package/src/tui/route.ts +173 -0
  445. package/src/tui/setupstate.ts +56 -0
  446. package/src/tui/sshtriage.ts +127 -6
  447. package/src/tui/theme.ts +2 -1
  448. package/src/tui/unwedge.ts +2 -14
  449. package/src/verdicts.ts +23 -5
  450. package/src/version.ts +1 -1
  451. package/src/webhook.ts +10 -1
  452. package/src/wire.ts +42 -9
  453. package/src/workerworld.ts +16 -2
  454. package/dist/src/live/projection.js +0 -450
  455. package/dist/src/live/projection.js.map +0 -1
  456. package/dist/src/live/read.js +0 -60
  457. package/dist/src/live/read.js.map +0 -1
  458. package/dist/src/onboard/launch.js +0 -83
  459. package/dist/src/onboard/launch.js.map +0 -1
  460. package/dist/src/setup/ledger.js +0 -207
  461. package/dist/src/setup/ledger.js.map +0 -1
  462. package/dist/src/setup/stage2.js +0 -617
  463. package/dist/src/setup/stage2.js.map +0 -1
  464. package/dist/src/setup/textsniff.js +0 -89
  465. package/dist/src/setup/textsniff.js.map +0 -1
  466. package/src/live/projection.ts +0 -491
  467. package/src/live/read.ts +0 -86
  468. package/src/onboard/launch.ts +0 -115
  469. package/src/setup/ledger.ts +0 -324
  470. package/src/setup/stage2.ts +0 -926
  471. package/src/setup/textsniff.ts +0 -95
@@ -1,617 +0,0 @@
1
- import { globMatcher } from "@quickchatai/pi-quickreview/src/glob.js";
2
- import { isSweepSkipped, isTestFile } from "@quickchatai/pi-quickreview/src/heuristics.js";
3
- import { lensEnabled, flooredTier, } from "@quickchatai/pi-quickreview/src/lenstuning.js";
4
- import { PASS_TIER_ORDER, tiersAtOrAbove, } from "@quickchatai/pi-quickreview/src/types.js";
5
- import { caseEstimate, coverKey, cutRanges, focusLine, planKey, rangeNote, TREE_PATHS_NOTE, rangesCover, SCAN_DEEP_LENSES, stage1Exclusion, SCAN_PASS, } from "./scan.js";
6
- import { depthOf, measuredRate, rateWhere } from "./ledger.js";
7
- import { assign, splitUnit } from "./unitcut.js";
8
- /** At most this many rows the companion asks for in one proposal (the deep
9
- * floor is not counted: it is bounded by the tree). */
10
- export const MAX_STAGE2_ROWS = 100;
11
- /** At most this many tests paired with one code file (the nearest win). */
12
- export const MAX_PAIRED_PER_FILE = 3;
13
- /** A row's reason is one line. */
14
- export const ROW_REASON_MAX = 300;
15
- /** The unit a file no unit of the cut owns is filed under. */
16
- export const NO_UNIT = "(no unit)";
17
- /** Better bases first: what `priceStage2Row` prefers at the row's own depth. */
18
- const BASIS_RANK = {
19
- measured: 0,
20
- borrowed: 1,
21
- learned: 2,
22
- extrapolated: 3,
23
- };
24
- /** Stage 1 measured at BOTH validator settings at one tier and effort —
25
- * `[scan]`'s own when it has both, else the first other pair (sorted) that
26
- * does — or null: the skeptic's share is then unmeasured. */
27
- function stage1Pair(entries, cfg, validator) {
28
- const at = (tier, effort) => {
29
- const row = measuredRate(entries, { lens: SCAN_PASS, tier, effort, validator });
30
- const other = measuredRate(entries, { lens: SCAN_PASS, tier, effort, validator: !validator });
31
- return row && other ? { row, other } : null;
32
- };
33
- const own = at(cfg.tier, cfg.effort);
34
- if (own)
35
- return own;
36
- const keys = [
37
- ...new Set(entries.filter((e) => e.lens === SCAN_PASS).map((e) => `${e.tier}\0${e.effort}`)),
38
- ].sort();
39
- for (const k of keys) {
40
- const [tier, effort] = k.split("\0");
41
- const p = at(tier, effort);
42
- if (p)
43
- return p;
44
- }
45
- return null;
46
- }
47
- /**
48
- * A stage-2 row's price at `lens`/`tier`/`effort` from the ledger (header
49
- * "PRICED FROM THE LEDGER"), or null when stage 1 has measured nothing. With
50
- * a `depth`, the price stands on readings at that depth when they price the
51
- * row at least as well as every reading does; otherwise it is the price over
52
- * every reading, marked `across_depth`.
53
- */
54
- export function priceStage2Row(entries, cfg, lens, tier, effort, depth) {
55
- const any = priceFrom(entries, cfg, lens, tier, effort);
56
- if (depth === undefined || any === null)
57
- return any;
58
- const same = priceFrom(entries.filter((e) => depthOf(e) === depth), cfg, lens, tier, effort);
59
- if (same !== null && BASIS_RANK[same.basis] <= BASIS_RANK[any.basis])
60
- return same;
61
- return { ...any, across_depth: true };
62
- }
63
- function priceFrom(entries, cfg, lens, tier, effort) {
64
- const validator = cfg.validator;
65
- const own = measuredRate(entries, { lens, tier, effort, validator });
66
- if (own)
67
- return { rate: own, basis: "measured", from: lens };
68
- // Borrowed only from another DEEP lens: pass-scan is the cheap wide pass,
69
- // and its rate at a matching tier/effort (a profile giving a deep lens
70
- // `effort = "low"` at `[scan] tier`) would price a deep row at stage 1's
71
- // cost unscaled — stage 1's rate reaches a row only through a factor.
72
- const deepAt = (v, same) => (e) => (same ? e.lens === lens : e.lens !== lens && e.lens !== SCAN_PASS) &&
73
- e.tier === tier &&
74
- e.effort === effort &&
75
- e.validator === v;
76
- const lensesOf = (keep) => [...new Set(entries.filter((e) => keep(e) && e.cost_usd !== null).map((e) => e.lens))]
77
- .sort()
78
- .join(", ");
79
- const borrowed = rateWhere(entries, deepAt(validator, false));
80
- if (borrowed)
81
- return { rate: borrowed, basis: "borrowed", from: lensesOf(deepAt(validator, false)) };
82
- // Stage 1 at a validator setting: at `[scan]`'s tier and effort, else at
83
- // any (the extrapolation's fallback too).
84
- const stage1At = (v) => measuredRate(entries, { lens: SCAN_PASS, tier: cfg.tier, effort: cfg.effort, validator: v }) ??
85
- rateWhere(entries, (e) => e.lens === SCAN_PASS && e.validator === v);
86
- // Learned: a stage-2 measurement at the same tier and effort with the
87
- // validator the other way, carried across by the skeptic's measured share —
88
- // stage 1's rate at the row's setting over its rate at the other. Both
89
- // stage-1 rates must come from ONE tier and effort (`[scan]`'s first, else
90
- // the first other pair measured both ways): a ratio across tiers would be
91
- // the tier's price, not the skeptic's. The share is bounded by direction —
92
- // the skeptic adds work, so it can raise a price learned from a skeptic-off
93
- // measurement and lower one learned from a skeptic-on one, never the
94
- // reverse (two noisy stage-1 samples must not price a validated row UNDER
95
- // what the unvalidated lens was measured at).
96
- const other = !validator;
97
- const s1Row = stage1At(validator);
98
- const pair = stage1Pair(entries, cfg, validator);
99
- if (pair && pair.other.usd_per_kline > 0) {
100
- const raw = pair.row.usd_per_kline / pair.other.usd_per_kline;
101
- const skeptic = validator ? Math.max(raw, 1) : Math.min(raw, 1);
102
- for (const same of [true, false]) {
103
- const s2 = rateWhere(entries, deepAt(other, same));
104
- if (!s2)
105
- continue;
106
- return {
107
- rate: { ...s2, usd_per_kline: s2.usd_per_kline * skeptic },
108
- basis: "learned",
109
- from: lensesOf(deepAt(other, same)),
110
- learned: { validator: other, factor: s2.usd_per_kline / pair.other.usd_per_kline, skeptic },
111
- };
112
- }
113
- }
114
- const stage1 = s1Row ?? rateWhere(entries, (e) => e.lens === SCAN_PASS);
115
- if (!stage1)
116
- return null;
117
- return {
118
- rate: { ...stage1, usd_per_kline: stage1.usd_per_kline * cfg.unmeasured_rate_factor },
119
- basis: "extrapolated",
120
- from: SCAN_PASS,
121
- factor: cfg.unmeasured_rate_factor,
122
- };
123
- }
124
- /** A row's effort as the ledger keys it: the lens's profile `effort`, or
125
- * `tier` (the tier's own reasoning level). */
126
- export function lensEffort(lenses, lens) {
127
- return lenses?.[lens]?.effort ?? "tier";
128
- }
129
- /** A risk pass runs at `sol-high` alone; any other lens, null. */
130
- function riskTier(riskPasses, lens) {
131
- return riskPasses.includes(lens) ? "sol-high" : null;
132
- }
133
- /** The tiers `lens` may run at in a scan: the ones its build variants exist
134
- * at, at or above its profile floor. */
135
- export function lensTiers(lenses, lens, riskPasses) {
136
- const risk = riskTier(riskPasses, lens);
137
- if (risk !== null)
138
- return [risk];
139
- const floor = flooredTier(lenses, lens, PASS_TIER_ORDER[0]);
140
- return tiersAtOrAbove(floor);
141
- }
142
- /** The deep lenses stage 2 may run on this repo: stage 1's suggestible set,
143
- * less every lens the profile switched off. */
144
- export function stage2Lenses(lenses) {
145
- return SCAN_DEEP_LENSES.filter((l) => lensEnabled(lenses, l, true));
146
- }
147
- // ---- pairing tests -------------------------------------------------------------
148
- const TEST_DIRS = new Set(["test", "tests", "__tests__", "spec", "specs", "testing", "main"]);
149
- const FAMILY = {
150
- ts: "js",
151
- tsx: "js",
152
- js: "js",
153
- jsx: "js",
154
- mjs: "js",
155
- cjs: "js",
156
- mts: "js",
157
- cts: "js",
158
- };
159
- function ext(path) {
160
- const base = path.slice(path.lastIndexOf("/") + 1);
161
- const i = base.lastIndexOf(".");
162
- return i <= 0 ? "" : base.slice(i + 1).toLowerCase();
163
- }
164
- function family(path) {
165
- const e = ext(path);
166
- return FAMILY[e] ?? e;
167
- }
168
- function dirSegs(path) {
169
- const i = path.lastIndexOf("/");
170
- return i < 0 ? [] : path.slice(0, i).split("/");
171
- }
172
- /** A code file's subject: its basename without the extension. */
173
- function codeStem(path) {
174
- const base = path.slice(path.lastIndexOf("/") + 1);
175
- const i = base.lastIndexOf(".");
176
- return (i <= 0 ? base : base.slice(0, i)).toLowerCase();
177
- }
178
- /** A test file's subject: its basename with the extension and every test
179
- * marker (`test_`, `_test`, `.test`, `.spec`, `Test`, …) stripped. */
180
- function testStem(path) {
181
- let s = path.slice(path.lastIndexOf("/") + 1);
182
- const i = s.lastIndexOf(".");
183
- if (i > 0)
184
- s = s.slice(0, i);
185
- s = s
186
- .replace(/[._-](test|spec)s?$/i, "")
187
- .replace(/(?<=[a-z0-9])Tests?$/, "")
188
- .replace(/^tests?[_-]/i, "");
189
- return s.toLowerCase();
190
- }
191
- const endsWithSegs = (a, b) => b.length <= a.length && b.every((s, i) => a[a.length - b.length + i] === s);
192
- /**
193
- * The tests in `tree` that pair with `code`: same subject, same language
194
- * family, and a directory that mirrors the code's once the test directories
195
- * (`tests/`, `__tests__/`, `src/test` vs `src/main`, …) are set aside — the
196
- * code's own directory, a `tests/` beside or under it, or a top-level tests
197
- * tree mirroring its package. The nearest (most shared leading directories)
198
- * win, at most `MAX_PAIRED_PER_FILE` of those `eligible` says a lens may read
199
- * — so a test it may not never takes a nearer one's place. A heuristic,
200
- * deliberately: a pair is reading context for the lens, never a claim about
201
- * coverage.
202
- */
203
- export function pairTests(code, tree, eligible = () => true) {
204
- const stem = codeStem(code);
205
- if (stem === "" || isTestFile(code))
206
- return [];
207
- const fam = family(code);
208
- const cDir = dirSegs(code);
209
- const cNorm = cDir.filter((s) => !TEST_DIRS.has(s.toLowerCase()));
210
- const scored = [];
211
- for (const t of tree) {
212
- if (t === code || !isTestFile(t) || family(t) !== fam || testStem(t) !== stem)
213
- continue;
214
- if (!eligible(t))
215
- continue;
216
- const tDir = dirSegs(t);
217
- const tNorm = tDir.filter((s) => !TEST_DIRS.has(s.toLowerCase()));
218
- if (!(endsWithSegs(cNorm, tNorm) || endsWithSegs(tNorm, cNorm)))
219
- continue;
220
- let shared = 0;
221
- while (shared < cDir.length && shared < tDir.length && cDir[shared] === tDir[shared])
222
- shared++;
223
- scored.push({ path: t, score: shared * 10 - Math.abs(tNorm.length - cNorm.length) });
224
- }
225
- scored.sort((a, b) => b.score - a.score || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
226
- return scored.slice(0, MAX_PAIRED_PER_FILE).map((s) => s.path);
227
- }
228
- // ---- the plan ------------------------------------------------------------------
229
- const cleanPath = (p) => p
230
- .trim()
231
- .replace(/^`+|`+$/g, "")
232
- .replace(/^\.?\/+/, "");
233
- /** A row's tier when none is named: the lens's floor over `[scan] tier`
234
- * (`sol-high` for a risk pass). */
235
- function defaultRowTier(input, lens) {
236
- return riskTier(input.riskPasses, lens) ?? flooredTier(input.lenses, lens, input.defaultTier);
237
- }
238
- /**
239
- * Plan a proposal: every row checked (all-or-nothing — one bad row refuses
240
- * the proposal, each problem named), tests paired, units found, each row
241
- * priced, covered rows marked; then, for a deep proposal, the floor rows
242
- * after them; then the runnable rows fitted to the budget in that order.
243
- * Deterministic.
244
- */
245
- export function planStage2(input) {
246
- const errors = [];
247
- if (input.rows.length === 0 && input.floor === undefined)
248
- return { ok: false, errors: ["a stage 2 proposal needs at least one row"] };
249
- if (input.rows.length > MAX_STAGE2_ROWS) {
250
- return {
251
- ok: false,
252
- errors: [`${input.rows.length} rows — at most ${MAX_STAGE2_ROWS} in one proposal`],
253
- };
254
- }
255
- const excluded = globMatcher(input.exclude);
256
- const blobs = new Map(input.files.map((f) => [f.path, f.blob]));
257
- const treePaths = [...blobs.keys()].sort();
258
- const unitOf = unitOwner(input.cut, treePaths);
259
- const allowed = stage2Lenses(input.lenses);
260
- const seen = new Set();
261
- const rows = [];
262
- /** One file too big for one case alone: read in line ranges. */
263
- const oversized = (sorted, lines) => sorted.length === 1 && lines.get(sorted[0]) > input.budget.maxLines;
264
- /** One row over `sorted` (checked files; `lines` holds their counts), its
265
- * tests paired when `pair` — or why it cannot be priced. */
266
- const build = (spec) => {
267
- const { lens, sorted, lines } = spec;
268
- let rowLines = sorted.reduce((n, p) => n + lines.get(p), 0);
269
- const oversize = oversized(sorted, lines);
270
- // Pair the tests, nearest first, while the case budget holds. A ranged
271
- // row's tests ride in EVERY range case, so they get at most half a case.
272
- const testFiles = !spec.pair
273
- ? 0
274
- : oversize
275
- ? input.budget.maxFiles - 1
276
- : input.budget.maxFiles - sorted.length;
277
- const testLines = oversize
278
- ? Math.floor(input.budget.maxLines / 2)
279
- : input.budget.maxLines - rowLines;
280
- let pairedLines = 0;
281
- const tests = [];
282
- const unpaired = [];
283
- const taken = new Set(sorted);
284
- // A paired test obeys the named files' rules: never an unreadable kind
285
- // (generated, vendored, a lockfile), never excluded, readable as text.
286
- const pairable = (t) => !isSweepSkipped(t) && !excluded(t) && input.linesOf(t) !== null;
287
- for (const code of spec.pair ? sorted : []) {
288
- for (const t of pairTests(code, treePaths, pairable)) {
289
- if (taken.has(t))
290
- continue;
291
- taken.add(t);
292
- const n = input.linesOf(t);
293
- if (tests.length + 1 > testFiles || pairedLines + n > testLines) {
294
- unpaired.push(t);
295
- continue;
296
- }
297
- tests.push({ path: t, blob: blobs.get(t), lines: n });
298
- pairedLines += n;
299
- }
300
- }
301
- let ranges;
302
- if (oversize) {
303
- const p = sorted[0];
304
- const done = input.partial?.(lens).get(coverKey(p, blobs.get(p))) ?? [];
305
- ranges = cutRanges(lines.get(p), input.budget.maxLines - pairedLines, input.textOf?.(p) ?? null).filter((r) => !rangesCover(done, r.start, r.end));
306
- rowLines = ranges.reduce((n, r) => n + rangeCaseLines(r, pairedLines), 0);
307
- }
308
- else {
309
- rowLines += pairedLines;
310
- }
311
- const effort = lensEffort(input.lenses, lens);
312
- const price = input.price(lens, spec.tier, effort);
313
- if (price === null) {
314
- return `no measured price for any scan lens yet — run stage 1 (its calibration run prices the scan) before proposing stage 2`;
315
- }
316
- const files = sorted.map((p) => ({ path: p, blob: blobs.get(p), lines: lines.get(p) }));
317
- const cover = input.covered(lens);
318
- const all = [...files, ...tests];
319
- const isCovered = all.every((f) => cover.has(coverKey(f.path, f.blob))) ||
320
- (ranges !== undefined && ranges.length === 0);
321
- return {
322
- id: spec.id,
323
- lens,
324
- tier: spec.tier,
325
- effort,
326
- reason: spec.reason,
327
- units: [...new Set(all.map((f) => unitOf(f.path)))].sort(),
328
- files,
329
- tests,
330
- unpaired,
331
- ...(ranges !== undefined ? { ranges } : {}),
332
- lines: rowLines,
333
- estimate_usd: isCovered
334
- ? 0
335
- : ranges !== undefined
336
- ? ranges.reduce((n, r) => n + caseEstimate(rangeCaseLines(r, pairedLines), price.rate), 0)
337
- : caseEstimate(rowLines, price.rate),
338
- price,
339
- status: isCovered ? "covered" : "run",
340
- ...(spec.floor ? { floor: true } : {}),
341
- };
342
- };
343
- input.rows.forEach((req, i) => {
344
- const id = `row-${i + 1}`;
345
- const bad = (why) => void errors.push(`${id}: ${why}`);
346
- const lens = typeof req?.lens === "string" ? req.lens.trim() : "";
347
- if (lens === SCAN_PASS)
348
- return bad(`${SCAN_PASS} is stage 1's lens — a stage 2 row names a deep lens`);
349
- if (!allowed.includes(lens)) {
350
- return bad(SCAN_DEEP_LENSES.includes(lens)
351
- ? `${JSON.stringify(lens)} is switched off by this repo's profile`
352
- : `${JSON.stringify(lens)} is not a deep lens a scan can run (one of: ${allowed.join(", ")})`);
353
- }
354
- const reason = typeof req.reason === "string" ? req.reason.trim().replace(/\s+/g, " ") : "";
355
- if (reason === "")
356
- return bad("no reason — say in one line what the lens should chase");
357
- if (reason.length > ROW_REASON_MAX)
358
- return bad(`the reason is ${reason.length} characters — one line, at most ${ROW_REASON_MAX}`);
359
- const tiers = lensTiers(input.lenses, lens, input.riskPasses);
360
- let tier;
361
- if (req.tier === undefined || req.tier === null || req.tier === "") {
362
- tier = defaultRowTier(input, lens);
363
- }
364
- else if (tiers.includes(req.tier)) {
365
- tier = req.tier;
366
- }
367
- else {
368
- return bad(`${lens} cannot run at ${JSON.stringify(req.tier)} here — one of: ${tiers.join(", ")}`);
369
- }
370
- const named = [
371
- ...new Set((Array.isArray(req.files) ? req.files : [])
372
- .filter((p) => typeof p === "string")
373
- .map(cleanPath)),
374
- ].filter((p) => p !== "");
375
- if (named.length === 0)
376
- return bad("no files");
377
- const problems = [];
378
- const lines = new Map();
379
- for (const p of named) {
380
- if (!blobs.has(p))
381
- problems.push(`${p} is not in the tree at the scan sha`);
382
- else if (isTestFile(p))
383
- problems.push(`${p} is a test — tests enter stage 2 only paired with the code they test; name that code`);
384
- else if (isSweepSkipped(p))
385
- problems.push(`${p} is not code a lens reads (lockfile, generated, binary or prose)`);
386
- else if (excluded(p))
387
- problems.push(`${p} is excluded from scans by [scan] exclude`);
388
- else {
389
- const n = input.linesOf(p);
390
- if (n === null) {
391
- const why = input.whyUnreadable?.(p) ?? null;
392
- problems.push(`${p} cannot be read as text${why === "binary" ? " (skipped: binary)" : why === "too-big" ? " (skipped: over the size cap)" : ""}`);
393
- }
394
- else
395
- lines.set(p, n);
396
- }
397
- }
398
- if (problems.length > 0)
399
- return bad(problems.join("; "));
400
- const sorted = [...named].sort();
401
- const dedupe = `${lens}\0${sorted.join("\0")}`;
402
- if (seen.has(dedupe))
403
- return bad(`the same ${lens} row as an earlier one`);
404
- seen.add(dedupe);
405
- const rowLines = sorted.reduce((n, p) => n + lines.get(p), 0);
406
- // One file too big for one case is cut into line ranges; anywhere else a
407
- // row over one case is refused.
408
- const oversize = oversized(sorted, lines);
409
- if (!oversize && (sorted.length > input.budget.maxFiles || rowLines > input.budget.maxLines)) {
410
- const big = sorted.filter((p) => lines.get(p) > input.budget.maxLines);
411
- return bad(`${sorted.length} files, ${rowLines} lines — over one case (${input.budget.maxFiles} files, ${input.budget.maxLines} lines); split it into narrower rows` +
412
- (big.length > 0
413
- ? ` (${big.join(", ")} ${big.length === 1 ? "is" : "are"} over one case alone — name ${big.length === 1 ? "it" : "each"} alone in a row and it is cut into line ranges)`
414
- : ""));
415
- }
416
- const row = build({ id, lens, tier, reason, sorted, lines, pair: true, floor: false });
417
- if (typeof row === "string")
418
- return bad(row);
419
- rows.push(row);
420
- });
421
- if (errors.length > 0)
422
- return { ok: false, errors };
423
- let floor = null;
424
- if (input.floor !== undefined) {
425
- floor = {
426
- lenses: input.floor.filter((l) => allowed.includes(l)),
427
- skipped: input.floor.filter((l) => !allowed.includes(l)),
428
- covered: 0,
429
- named: 0,
430
- };
431
- // Stage 1's files: readable, not a test, a migration, an excluded glob
432
- // or an unreadable kind — and owned by a unit of the cut.
433
- const lines = new Map();
434
- for (const p of treePaths) {
435
- if (stage1Exclusion(p, excluded) !== null)
436
- continue;
437
- const n = input.linesOf(p);
438
- if (n !== null)
439
- lines.set(p, n);
440
- }
441
- const owned = assign(input.cut, [...lines.keys()]).byUnit;
442
- let n = 0;
443
- for (const lens of floor.lenses) {
444
- const named = new Set(rows.filter((r) => !r.floor && r.lens === lens).flatMap((r) => r.files.map((f) => f.path)));
445
- const cover = input.covered(lens);
446
- const tier = defaultRowTier(input, lens);
447
- for (const u of input.cut.units) {
448
- const files = [];
449
- for (const p of owned.get(u.name) ?? []) {
450
- if (named.has(p))
451
- floor.named++;
452
- else if (cover.has(coverKey(p, blobs.get(p))))
453
- floor.covered++;
454
- else
455
- files.push(p);
456
- }
457
- if (files.length === 0)
458
- continue;
459
- for (const chunk of splitUnit(u.name, files, (p) => lines.get(p), input.budget)) {
460
- const row = build({
461
- id: `floor-${++n}`,
462
- lens,
463
- tier,
464
- reason: `deep floor: ${lens} over ${u.name}`,
465
- sorted: [...chunk.files].sort(),
466
- lines,
467
- pair: false,
468
- floor: true,
469
- });
470
- if (typeof row === "string")
471
- return { ok: false, errors: [`floor-${n}: ${row}`] };
472
- rows.push(row);
473
- }
474
- }
475
- }
476
- if (rows.length === 0) {
477
- return {
478
- ok: false,
479
- errors: [
480
- floor.lenses.length === 0
481
- ? `no row and no floor: every [scan] deep_lenses lens (${input.floor.join(", ")}) is switched off by this repo's profile`
482
- : floor.covered > 0
483
- ? "nothing left for the floor: the ledger already holds a deep reading of every file by every floor lens at this content"
484
- : "no row and no floor: stage 1 reads no file a unit of the cut owns",
485
- ],
486
- };
487
- }
488
- }
489
- let total = 0;
490
- let over = 0;
491
- for (const r of rows) {
492
- if (r.status !== "run")
493
- continue;
494
- if (input.budgetUsd !== null && total + r.estimate_usd > input.budgetUsd + 1e-9) {
495
- r.status = "over-budget";
496
- over += r.estimate_usd;
497
- continue;
498
- }
499
- total += r.estimate_usd;
500
- }
501
- const runnable = rows.filter((r) => r.status === "run").length;
502
- return { ok: true, rows, runnable, total_usd: total, over_budget_usd: over, floor };
503
- }
504
- /** The unit owning each tree path under `cut` (NO_UNIT for none). */
505
- export function unitOwner(cut, paths) {
506
- const owner = new Map();
507
- for (const [unit, ps] of assign(cut, paths).byUnit)
508
- for (const p of ps)
509
- owner.set(p, unit);
510
- return (p) => owner.get(p) ?? NO_UNIT;
511
- }
512
- // ---- the prompt ----------------------------------------------------------------
513
- /** The cases one row runs as: the row itself, or — a row over a file too
514
- * big for one case — one per range, `<row>#<i>`, each the ranged file plus
515
- * the paired tests. */
516
- export function stage2Cases(row) {
517
- if (row.ranges === undefined)
518
- return [
519
- { id: row.id, part: 1, parts: 1, files: [...row.files, ...row.tests], lines: row.lines },
520
- ];
521
- const code = row.files[0];
522
- const tests = row.tests.reduce((n, t) => n + t.lines, 0);
523
- return row.ranges.map((r, i) => ({
524
- id: `${row.id}#${i + 1}`,
525
- part: i + 1,
526
- parts: row.ranges.length,
527
- files: [{ ...code, lines: rangeCaseLines(r, 0), range: r }, ...row.tests],
528
- lines: rangeCaseLines(r, tests),
529
- }));
530
- }
531
- /** Stage-1 leads a stage-2 case carries at most. */
532
- export const STAGE2_MAX_LEADS = 8;
533
- /** A lead's reason is cut to this many characters. */
534
- const LEAD_MAX_CHARS = 300;
535
- /**
536
- * The stage-1 suggestions a stage-2 row's cases carry: those naming the
537
- * row's lens and at least one of its code files, newest first (by `at`), one
538
- * per reason, at most `STAGE2_MAX_LEADS`. Each keeps only the row's files it
539
- * names; its reason is flattened to one line and capped. The companion's row reason is one line; what stage 1 saw in these
540
- * files is otherwise never in front of the lens. Pure.
541
- */
542
- export function stage2Leads(row, suggestions) {
543
- const mine = new Set(row.files.map((f) => f.path));
544
- const seen = new Set();
545
- const out = [];
546
- const newest = [...suggestions].sort((a, b) => (b.at ?? "").localeCompare(a.at ?? ""));
547
- for (const s of newest) {
548
- if (s.lens !== row.lens)
549
- continue;
550
- const files = s.files.filter((f) => mine.has(f));
551
- // Model-written: one line, capped, so a lead can never pass for a
552
- // header or a findings block in the task string.
553
- const flat = s.reason.replace(/\s+/g, " ").trim();
554
- const reason = flat.length > LEAD_MAX_CHARS ? `${flat.slice(0, LEAD_MAX_CHARS - 1)}…` : flat;
555
- if (files.length === 0 || reason === "" || seen.has(reason))
556
- continue;
557
- seen.add(reason);
558
- out.push({ files, reason });
559
- if (out.length >= STAGE2_MAX_LEADS)
560
- break;
561
- }
562
- return out;
563
- }
564
- /** The stage-2 task string for one row — or, for a ranged row, for the one
565
- * case whose `files` (from `stage2Cases`) are given. `leads` are what stage 1
566
- * pointed this lens at in the row's files (`stage2Leads`). */
567
- export function stage2CasePrompt(row, ctx, caseFiles, leads = []) {
568
- const list = (fs) => fs.map((f) => focusLine(f, ctx.root)).join("\n");
569
- const code = caseFiles ? caseFiles.slice(0, row.files.length) : row.files;
570
- return [
571
- `SCAN CASE ${ctx.caseId} — stage 2, ${row.id}: ${row.lens}${row.units.length > 1 ? ` across units ${row.units.join(", ")}` : ` in unit ${row.units[0]}`}.`,
572
- `Scan sha: ${ctx.sha}`,
573
- `Tree root (the repository at the scan sha, read-only): ${ctx.root}`,
574
- TREE_PATHS_NOTE,
575
- "",
576
- `Why this case: ${row.reason}`,
577
- ...(row.floor
578
- ? [
579
- leads.length > 0
580
- ? "This row is the deep scan's floor: every file is read with this lens whether or not a first look pointed here."
581
- : "This row is the deep scan's floor: every file is read with this lens whether or not a first look pointed here, so nobody names a shape for you — find it in the focus files yourself.",
582
- ]
583
- : []),
584
- ...(leads.length > 0
585
- ? [
586
- "",
587
- `What the first look pointed this lens at in these files (${leads.length} — leads to check first, not findings, and not the limit of your reading):`,
588
- ...leads.map((l) => `- ${l.files.map((f) => `\`${f}\``).join(", ")}: ${l.reason}`),
589
- ]
590
- : []),
591
- "",
592
- `Focus files (${code.length} — read each in full; they are one case because the problem may live between them):`,
593
- list(code),
594
- ...rangeNote(code),
595
- ...(row.tests.length > 0
596
- ? [
597
- "",
598
- `Their tests (${row.tests.length} — read them for the contract the code is held to; a missing test is not a finding):`,
599
- list(row.tests),
600
- ]
601
- : []),
602
- "",
603
- `Answer with the findings block headed \`FINDINGS ${row.lens} SCAN ${ctx.caseId} @${ctx.sha}\` (or NONE).`,
604
- ].join("\n");
605
- }
606
- /** The lines one range case reads: the range, and the paired tests that
607
- * ride in every range case — the row's total, its price and its cases all
608
- * count it this one way. */
609
- function rangeCaseLines(r, pairedLines) {
610
- return r.end - r.start + 1 + pairedLines;
611
- }
612
- /** The key a stage-2 start must quote: the runnable rows, the sha, the
613
- * validator setting and the cap. */
614
- export function stage2Key(parts) {
615
- return planKey({ stage: 2, parts });
616
- }
617
- //# sourceMappingURL=stage2.js.map