@quickchatai/quickreview 1.2.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 (322) hide show
  1. package/README.md +114 -50
  2. package/bin/qcr.js +1 -1
  3. package/config.example.toml +20 -6
  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/selfimprove.js +18 -0
  11. package/dist/extensions/cli/selfimprove.js.map +1 -0
  12. package/dist/extensions/home.js +350 -0
  13. package/dist/extensions/home.js.map +1 -0
  14. package/dist/extensions/login-route.js +1 -1
  15. package/dist/extensions/login-route.js.map +1 -1
  16. package/dist/extensions/onboarding.js +8 -7
  17. package/dist/extensions/onboarding.js.map +1 -1
  18. package/dist/extensions/selfimprove.js +49 -0
  19. package/dist/extensions/selfimprove.js.map +1 -0
  20. package/dist/extensions/setup-scan.js +23 -592
  21. package/dist/extensions/setup-scan.js.map +1 -1
  22. package/dist/extensions/setup.js +2 -47
  23. package/dist/extensions/setup.js.map +1 -1
  24. package/dist/src/archive/restore.js +18 -15
  25. package/dist/src/archive/restore.js.map +1 -1
  26. package/dist/src/botworker.js +13 -2
  27. package/dist/src/botworker.js.map +1 -1
  28. package/dist/src/channelfs.js +3 -26
  29. package/dist/src/channelfs.js.map +1 -1
  30. package/dist/src/cli.js +12 -50
  31. package/dist/src/cli.js.map +1 -1
  32. package/dist/src/config.js +16 -0
  33. package/dist/src/config.js.map +1 -1
  34. package/dist/src/daemon.js +26 -0
  35. package/dist/src/daemon.js.map +1 -1
  36. package/dist/src/debuground.js +1 -0
  37. package/dist/src/debuground.js.map +1 -1
  38. package/dist/src/debugroundpi.js +1 -0
  39. package/dist/src/debugroundpi.js.map +1 -1
  40. package/dist/src/engine/caserunner-sdk.js +15 -251
  41. package/dist/src/engine/caserunner-sdk.js.map +1 -1
  42. package/dist/src/home/adapters.js +349 -0
  43. package/dist/src/home/adapters.js.map +1 -0
  44. package/dist/src/home/advanced.js +318 -0
  45. package/dist/src/home/advanced.js.map +1 -0
  46. package/dist/src/home/advancedtools.js +57 -0
  47. package/dist/src/home/advancedtools.js.map +1 -0
  48. package/dist/src/home/autoreview.js +325 -0
  49. package/dist/src/home/autoreview.js.map +1 -0
  50. package/dist/src/home/daemonstatus.js +32 -0
  51. package/dist/src/home/daemonstatus.js.map +1 -0
  52. package/dist/src/home/devicelogin.js +61 -0
  53. package/dist/src/home/devicelogin.js.map +1 -0
  54. package/dist/src/home/launch.js +202 -0
  55. package/dist/src/home/launch.js.map +1 -0
  56. package/dist/src/home/loginowner.js +205 -0
  57. package/dist/src/home/loginowner.js.map +1 -0
  58. package/dist/src/home/memory.js +122 -0
  59. package/dist/src/home/memory.js.map +1 -0
  60. package/dist/src/home/model.js +811 -0
  61. package/dist/src/home/model.js.map +1 -0
  62. package/dist/src/home/panel.js +520 -0
  63. package/dist/src/home/panel.js.map +1 -0
  64. package/dist/src/home/ports.js +19 -0
  65. package/dist/src/home/ports.js.map +1 -0
  66. package/dist/src/home/review.js +163 -0
  67. package/dist/src/home/review.js.map +1 -0
  68. package/dist/src/home/reviews.js +478 -0
  69. package/dist/src/home/reviews.js.map +1 -0
  70. package/dist/src/home/setup.js +507 -0
  71. package/dist/src/home/setup.js.map +1 -0
  72. package/dist/src/home/setupadapters.js +214 -0
  73. package/dist/src/home/setupadapters.js.map +1 -0
  74. package/dist/src/home/setupcard.js +199 -0
  75. package/dist/src/home/setupcard.js.map +1 -0
  76. package/dist/src/home/setuptools.js +45 -0
  77. package/dist/src/home/setuptools.js.map +1 -0
  78. package/dist/src/home/spend.js +160 -0
  79. package/dist/src/home/spend.js.map +1 -0
  80. package/dist/src/home/switches.js +209 -0
  81. package/dist/src/home/switches.js.map +1 -0
  82. package/dist/src/home/switchon.js +439 -0
  83. package/dist/src/home/switchon.js.map +1 -0
  84. package/dist/src/home/toolnames.js +41 -0
  85. package/dist/src/home/toolnames.js.map +1 -0
  86. package/dist/src/home/tune.js +341 -0
  87. package/dist/src/home/tune.js.map +1 -0
  88. package/dist/src/home/words.js +7 -0
  89. package/dist/src/home/words.js.map +1 -0
  90. package/dist/src/homes.js +14 -0
  91. package/dist/src/homes.js.map +1 -1
  92. package/dist/src/install.js +1 -1
  93. package/dist/src/install.js.map +1 -1
  94. package/dist/src/ipc/workerspec.js +8 -0
  95. package/dist/src/ipc/workerspec.js.map +1 -1
  96. package/dist/src/live/publish.js +1 -1
  97. package/dist/src/live/publish.js.map +1 -1
  98. package/dist/src/livecli.js +1 -1
  99. package/dist/src/livecli.js.map +1 -1
  100. package/dist/src/loginrefresh/handover.js +121 -0
  101. package/dist/src/loginrefresh/handover.js.map +1 -0
  102. package/dist/src/loginrefresh/lock.js +54 -3
  103. package/dist/src/loginrefresh/lock.js.map +1 -1
  104. package/dist/src/loginrefresh/refresher.js +33 -5
  105. package/dist/src/loginrefresh/refresher.js.map +1 -1
  106. package/dist/src/loginrefresh/supervisor.js +18 -1
  107. package/dist/src/loginrefresh/supervisor.js.map +1 -1
  108. package/dist/src/npmverbs.js +42 -12
  109. package/dist/src/npmverbs.js.map +1 -1
  110. package/dist/src/onboard/choices.js +6 -3
  111. package/dist/src/onboard/choices.js.map +1 -1
  112. package/dist/src/onboard/engine.js +33 -292
  113. package/dist/src/onboard/engine.js.map +1 -1
  114. package/dist/src/onboard/steps/bringup.js +3 -2
  115. package/dist/src/onboard/steps/bringup.js.map +1 -1
  116. package/dist/src/onboard/steps/configfill.js +18 -7
  117. package/dist/src/onboard/steps/configfill.js.map +1 -1
  118. package/dist/src/onboard/steps/loginshare.js +213 -0
  119. package/dist/src/onboard/steps/loginshare.js.map +1 -0
  120. package/dist/src/onboard/steps/memory.js +14 -2
  121. package/dist/src/onboard/steps/memory.js.map +1 -1
  122. package/dist/src/onboard/steps/repoconfig.js +72 -0
  123. package/dist/src/onboard/steps/repoconfig.js.map +1 -0
  124. package/dist/src/onboard/toolnames.js +2 -2
  125. package/dist/src/onboard/toolnames.js.map +1 -1
  126. package/dist/src/piagentdir.js +13 -0
  127. package/dist/src/piagentdir.js.map +1 -0
  128. package/dist/src/pilogin.js +31 -16
  129. package/dist/src/pilogin.js.map +1 -1
  130. package/dist/src/qcrbin.js +16 -11
  131. package/dist/src/qcrbin.js.map +1 -1
  132. package/dist/src/reviewargs.js +122 -0
  133. package/dist/src/reviewargs.js.map +1 -0
  134. package/dist/src/reviewcli.js +7 -161
  135. package/dist/src/reviewcli.js.map +1 -1
  136. package/dist/src/reviewdetach.js +76 -11
  137. package/dist/src/reviewdetach.js.map +1 -1
  138. package/dist/src/scancli.js +39 -691
  139. package/dist/src/scancli.js.map +1 -1
  140. package/dist/src/selfimprove.js +66 -0
  141. package/dist/src/selfimprove.js.map +1 -0
  142. package/dist/src/setup/adapters.js +5 -223
  143. package/dist/src/setup/adapters.js.map +1 -1
  144. package/dist/src/setup/caserunner.js +15 -249
  145. package/dist/src/setup/caserunner.js.map +1 -1
  146. package/dist/src/setup/engine.js +32 -341
  147. package/dist/src/setup/engine.js.map +1 -1
  148. package/dist/src/setup/facts.js +3 -592
  149. package/dist/src/setup/facts.js.map +1 -1
  150. package/dist/src/setup/findings.js +3 -947
  151. package/dist/src/setup/findings.js.map +1 -1
  152. package/dist/src/setup/presentation.js +3 -8
  153. package/dist/src/setup/presentation.js.map +1 -1
  154. package/dist/src/setup/scan.js +3 -724
  155. package/dist/src/setup/scan.js.map +1 -1
  156. package/dist/src/setup/scanbuild.js +9 -92
  157. package/dist/src/setup/scanbuild.js.map +1 -1
  158. package/dist/src/setup/scanlease.js +3 -250
  159. package/dist/src/setup/scanlease.js.map +1 -1
  160. package/dist/src/setup/scanreport.js +3 -288
  161. package/dist/src/setup/scanreport.js.map +1 -1
  162. package/dist/src/setup/scanrun.js +3 -1732
  163. package/dist/src/setup/scanrun.js.map +1 -1
  164. package/dist/src/setup/scantree.js +3 -256
  165. package/dist/src/setup/scantree.js.map +1 -1
  166. package/dist/src/setup/stage2request.js +3 -258
  167. package/dist/src/setup/stage2request.js.map +1 -1
  168. package/dist/src/setup/unitcut.js +3 -436
  169. package/dist/src/setup/unitcut.js.map +1 -1
  170. package/dist/src/state.js +2 -0
  171. package/dist/src/state.js.map +1 -1
  172. package/dist/src/takeover.js +1 -0
  173. package/dist/src/takeover.js.map +1 -1
  174. package/dist/src/takeoverpi.js +2 -1
  175. package/dist/src/takeoverpi.js.map +1 -1
  176. package/dist/src/tui/board.js +1 -1
  177. package/dist/src/tui/board.js.map +1 -1
  178. package/dist/src/tui/companion.js +2 -1
  179. package/dist/src/tui/companion.js.map +1 -1
  180. package/dist/src/tui/deploytab.js +2 -2
  181. package/dist/src/tui/deploytab.js.map +1 -1
  182. package/dist/src/tui/index.js +9 -6
  183. package/dist/src/tui/index.js.map +1 -1
  184. package/dist/src/tui/memory.js +3 -16
  185. package/dist/src/tui/memory.js.map +1 -1
  186. package/dist/src/tui/memoryurl.js +22 -0
  187. package/dist/src/tui/memoryurl.js.map +1 -0
  188. package/dist/src/tui/remote.js +1 -1
  189. package/dist/src/tui/remote.js.map +1 -1
  190. package/dist/src/tui/route.js +2 -2
  191. package/dist/src/tui/route.js.map +1 -1
  192. package/dist/src/tui/setupstate.js +8 -5
  193. package/dist/src/tui/setupstate.js.map +1 -1
  194. package/dist/src/tui/sshtriage.js +1 -1
  195. package/dist/src/tui/sshtriage.js.map +1 -1
  196. package/dist/src/wire.js +6 -2
  197. package/dist/src/wire.js.map +1 -1
  198. package/dist/src/workerworld.js +4 -0
  199. package/dist/src/workerworld.js.map +1 -1
  200. package/docker/README.md +1 -1
  201. package/docker/entrypoint.sh +40 -6
  202. package/docker-compose.yml +16 -1
  203. package/extensions/cli/home.ts +14 -0
  204. package/extensions/cli/selfimprove.ts +18 -0
  205. package/extensions/home.ts +484 -0
  206. package/extensions/login-route.ts +1 -1
  207. package/extensions/onboarding.ts +8 -7
  208. package/extensions/selfimprove.ts +65 -0
  209. package/extensions/setup-scan.ts +79 -766
  210. package/extensions/setup.ts +14 -70
  211. package/package.json +6 -4
  212. package/prompts/onboarding.md +8 -7
  213. package/schemas/review-result.v1.json +60 -0
  214. package/scripts/deploy.sh +8 -6
  215. package/scripts/qcr-host.sh +28 -5
  216. package/scripts/vm-ghcr-login.sh +7 -4
  217. package/src/archive/restore.ts +18 -21
  218. package/src/botworker.ts +13 -2
  219. package/src/channelfs.ts +3 -26
  220. package/src/cli.ts +11 -54
  221. package/src/config.ts +38 -0
  222. package/src/daemon.ts +26 -0
  223. package/src/debuground.ts +3 -0
  224. package/src/debugroundpi.ts +1 -0
  225. package/src/engine/caserunner-sdk.ts +29 -314
  226. package/src/home/adapters.ts +418 -0
  227. package/src/home/advanced.ts +421 -0
  228. package/src/home/advancedtools.ts +96 -0
  229. package/src/home/autoreview.ts +385 -0
  230. package/src/home/daemonstatus.ts +55 -0
  231. package/src/home/devicelogin.ts +107 -0
  232. package/src/home/launch.ts +257 -0
  233. package/src/home/loginowner.ts +248 -0
  234. package/src/home/memory.ts +176 -0
  235. package/src/home/model.ts +965 -0
  236. package/src/home/panel.ts +666 -0
  237. package/src/home/ports.ts +338 -0
  238. package/src/home/review.ts +206 -0
  239. package/src/home/reviews.ts +565 -0
  240. package/src/home/setup.ts +689 -0
  241. package/src/home/setupadapters.ts +217 -0
  242. package/src/home/setupcard.ts +288 -0
  243. package/src/home/setuptools.ts +57 -0
  244. package/src/home/spend.ts +194 -0
  245. package/src/home/switches.ts +293 -0
  246. package/src/home/switchon.ts +554 -0
  247. package/src/home/toolnames.ts +46 -0
  248. package/src/home/tune.ts +405 -0
  249. package/src/home/words.ts +8 -0
  250. package/src/homes.ts +17 -0
  251. package/src/install.ts +1 -1
  252. package/src/ipc/workerspec.ts +15 -0
  253. package/src/live/publish.ts +4 -1
  254. package/src/livecli.ts +6 -1
  255. package/src/loginrefresh/handover.ts +138 -0
  256. package/src/loginrefresh/lock.ts +72 -3
  257. package/src/loginrefresh/refresher.ts +34 -5
  258. package/src/loginrefresh/supervisor.ts +25 -2
  259. package/src/npmverbs.ts +51 -11
  260. package/src/onboard/choices.ts +6 -3
  261. package/src/onboard/engine.ts +64 -434
  262. package/src/onboard/steps/bringup.ts +3 -2
  263. package/src/onboard/steps/configfill.ts +23 -7
  264. package/src/onboard/steps/loginshare.ts +258 -0
  265. package/src/onboard/steps/memory.ts +18 -2
  266. package/src/onboard/steps/repoconfig.ts +85 -0
  267. package/src/onboard/toolnames.ts +2 -2
  268. package/src/piagentdir.ts +12 -0
  269. package/src/pilogin.ts +32 -16
  270. package/src/qcrbin.ts +16 -11
  271. package/src/reviewargs.ts +119 -0
  272. package/src/reviewcli.ts +6 -167
  273. package/src/reviewdetach.ts +107 -13
  274. package/src/scancli.ts +75 -826
  275. package/src/selfimprove.ts +90 -0
  276. package/src/setup/adapters.ts +13 -221
  277. package/src/setup/caserunner.ts +34 -273
  278. package/src/setup/engine.ts +62 -496
  279. package/src/setup/facts.ts +3 -627
  280. package/src/setup/findings.ts +3 -1264
  281. package/src/setup/presentation.ts +3 -18
  282. package/src/setup/scan.ts +3 -1033
  283. package/src/setup/scanbuild.ts +20 -114
  284. package/src/setup/scanlease.ts +3 -297
  285. package/src/setup/scanreport.ts +3 -373
  286. package/src/setup/scanrun.ts +3 -2268
  287. package/src/setup/scantree.ts +3 -259
  288. package/src/setup/stage2request.ts +3 -347
  289. package/src/setup/unitcut.ts +3 -523
  290. package/src/state.ts +25 -0
  291. package/src/takeover.ts +5 -0
  292. package/src/takeoverpi.ts +3 -2
  293. package/src/tui/board.ts +1 -1
  294. package/src/tui/companion.ts +2 -1
  295. package/src/tui/deploytab.ts +2 -2
  296. package/src/tui/index.ts +9 -6
  297. package/src/tui/memory.ts +3 -15
  298. package/src/tui/memoryurl.ts +22 -0
  299. package/src/tui/remote.ts +1 -1
  300. package/src/tui/route.ts +2 -2
  301. package/src/tui/setupstate.ts +20 -16
  302. package/src/tui/sshtriage.ts +1 -1
  303. package/src/wire.ts +6 -2
  304. package/src/workerworld.ts +9 -1
  305. package/dist/src/live/projection.js +0 -450
  306. package/dist/src/live/projection.js.map +0 -1
  307. package/dist/src/live/read.js +0 -60
  308. package/dist/src/live/read.js.map +0 -1
  309. package/dist/src/onboard/launch.js +0 -75
  310. package/dist/src/onboard/launch.js.map +0 -1
  311. package/dist/src/setup/ledger.js +0 -207
  312. package/dist/src/setup/ledger.js.map +0 -1
  313. package/dist/src/setup/stage2.js +0 -617
  314. package/dist/src/setup/stage2.js.map +0 -1
  315. package/dist/src/setup/textsniff.js +0 -89
  316. package/dist/src/setup/textsniff.js.map +0 -1
  317. package/src/live/projection.ts +0 -491
  318. package/src/live/read.ts +0 -86
  319. package/src/onboard/launch.ts +0 -103
  320. package/src/setup/ledger.ts +0 -324
  321. package/src/setup/stage2.ts +0 -926
  322. package/src/setup/textsniff.ts +0 -95
@@ -57,7 +57,12 @@ import {
57
57
  } from "@quickchatai/pi-quickreview/extensions/pi-tool.js";
58
58
  import { fileURLToPath } from "node:url";
59
59
  import { configHome, stateHome } from "../src/install.js";
60
- import { SetupEngine, type CutStatus, type CutView } from "../src/setup/engine.js";
60
+ import { SetupEngine } from "../src/setup/engine.js";
61
+ import {
62
+ renderCut,
63
+ renderDraft,
64
+ renderStatus,
65
+ } from "@quickchatai/pi-quickreview/src/scan/cutrender.js";
61
66
  import type { HistoryTheme, HistoryThemesFile } from "../src/setup/history.js";
62
67
  import {
63
68
  HISTORY_SECTIONS,
@@ -154,75 +159,14 @@ export function renderFacts(facts: RepoFacts, head: string | null): string {
154
159
  ].join("\n\n");
155
160
  }
156
161
 
157
- export function renderCut(title: string, v: CutView): string {
158
- const rows = v.units.map((u) => {
159
- const split =
160
- u.cases === null
161
- ? " — case coverage UNKNOWN"
162
- : u.cases.length > 1
163
- ? ` — split into ${u.cases.length} cases (${u.cases.map((c) => `${c.id}: ${c.files}f/${c.lines}l`).join(", ")})`
164
- : "";
165
- const over = u.cases?.some((c) => c.over_budget)
166
- ? " [a file alone exceeds one case — a scan reads it in line ranges]"
167
- : "";
168
- return ` - ${u.name} [${u.kind}] ${u.roots.map(displayRoot).join(", ")}: ${u.files ?? "UNKNOWN"} files, ${u.reviewable ?? "UNKNOWN"} reviewable, ${u.lines ?? "UNKNOWN"} lines${split}${over}${u.note ? ` — ${u.note}` : ""}`;
169
- });
170
- const lines = [`${title}:`, ...rows];
171
- if (v.ignored.length > 0)
172
- lines.push(` ignored: ${v.ignored.join(", ")} (${v.ignored_files ?? "UNKNOWN"} files)`);
173
- for (const e of v.errors) lines.push(` ✗ ${e}`);
174
- for (const b of v.blocking_save) lines.push(` ! ${b}`);
175
- return lines.join("\n");
176
- }
177
-
178
- /** How a rendering names the save that makes a draft the cut — the tool, or
179
- * the `qcr scan units` verb. */
180
- export interface CutVerbs {
181
- save: string;
182
- }
183
- export const CUT_TOOL_VERBS: CutVerbs = { save: "setup_unit_cut_save" };
184
- export const CUT_CLI_VERBS: CutVerbs = { save: "qcr scan units save" };
185
-
186
- /** A stored or adjusted draft, and that it is not in use until saved. */
187
- export function renderDraft(
188
- title: string,
189
- draft: CutView,
190
- verbs: CutVerbs = CUT_TOOL_VERBS,
191
- ): string {
192
- return `${renderCut(title, draft)}\n\nNot in use until ${verbs.save}.`;
193
- }
194
-
195
- export function renderStatus(s: CutStatus): string {
196
- const out: string[] = [];
197
- for (const p of s.problems) out.push(`✗ ${p}`);
198
- if (s.saved) {
199
- const stale =
200
- s.saved.blocking_save.length > 0 ? " — STALE against the repo now, see the ! lines" : "";
201
- out.push(
202
- renderCut(
203
- `Saved unit cut (saved ${s.saved.saved_at}${s.saved.head ? ` at ${s.saved.head.slice(0, 12)}` : ""})${stale}`,
204
- s.saved,
205
- ),
206
- );
207
- } else if (s.cut_file_unreadable) {
208
- out.push(
209
- "Saved unit cut: UNKNOWN — the cut file could not be read (see above); nothing will overwrite it.",
210
- );
211
- } else {
212
- out.push("No saved unit cut yet.");
213
- }
214
- if (s.draft)
215
- out.push(
216
- renderCut(
217
- `Draft (${s.draft.source}, updated ${s.draft.updated_at}) — not in use until saved`,
218
- s.draft,
219
- ),
220
- );
221
- if (s.has_previous)
222
- out.push("The cut before the saved one is kept as `previous` in the state home.");
223
- return out.join("\n\n");
224
- }
225
-
162
+ export {
163
+ CUT_CLI_VERBS,
164
+ CUT_TOOL_VERBS,
165
+ renderCut,
166
+ renderDraft,
167
+ renderStatus,
168
+ type CutVerbs,
169
+ } from "@quickchatai/pi-quickreview/src/scan/cutrender.js";
226
170
  export function renderThemes(themes: readonly HistoryTheme[]): string {
227
171
  return themes.length === 0
228
172
  ? " (none — an empty history has no themes)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quickchatai/quickreview",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "private": false,
5
5
  "//private": "The root is the published `qcr` CLI: the whole app (TUI, daemon, companion, every verb), installed with npm, pnpm or bun. It depends on the pi plugin (packages/pi-plugin) at its exact version and on pi at the exact pins below. Publishing is a release step of its own, never part of a build.",
6
6
  "description": "A 24/7 AI code reviewer for GitHub: a deterministic daemon driving pi review sessions, with an operator TUI and a local qcr review",
@@ -94,11 +94,10 @@
94
94
  "prepack": "npm run build",
95
95
  "watch": "node scripts/tsc-watch.mjs -b --watch --preserveWatchOutput",
96
96
  "dev": "bash scripts/dev-tui.sh",
97
- "onboard": "bash scripts/qcr onboard",
98
97
  "test": "vitest run",
99
98
  "//canary:pi": "The offline pi canaries (tests/pi-canary/): the real pi SDK at the pinned version on pi-ai's faux provider, so no network and no cost. They also run inside `npm test`; this is the named entry point CI requires whenever the pi dependencies change (a bump is gated by it).",
100
99
  "canary:pi": "vitest run tests/pi-canary",
101
- "//canary:pi:range": "The same canaries on each pi version the plugin's peer range (packages/pi-plugin/package.json) should hold, refusing one outside it: no argument runs the installed pi; `-- --install 1.4.x` fetches another first (the registry: CI's pi-range matrix runs it in a fresh checkout). Needs the build.",
100
+ "//canary:pi:range": "The same canaries on each pi version the plugin's peer range (packages/pi-plugin/package.json) should hold, refusing one outside it: no argument runs the installed pi; `-- --install 1.4.x` fetches another first (the registry). Run it by hand before a version joins the peer range; CI runs only the installed pi's canaries. Needs the build.",
102
101
  "canary:pi:range": "node scripts/pi-canary-range.mjs",
103
102
  "//lint": "Deliberately NOT chained into `test` or `build`: scripts/dev-tui.sh blocks on the build before the TUI paints (ruling 5), and an ESLint pass there is seconds of blank terminal. CI runs it as its own step.",
104
103
  "lint": "node --max-old-space-size=4096 node_modules/eslint/bin/eslint.js .",
@@ -116,7 +115,7 @@
116
115
  },
117
116
  "dependencies": {
118
117
  "@earendil-works/pi-ai": "1.4.0",
119
- "@quickchatai/pi-quickreview": "1.2.0",
118
+ "@quickchatai/pi-quickreview": "1.3.1",
120
119
  "@earendil-works/pi-coding-agent": "1.4.0",
121
120
  "better-sqlite3": "13.0.3",
122
121
  "smol-toml": "1.9.1"
@@ -132,6 +131,9 @@
132
131
  "vitest": "5.0.5",
133
132
  "yaml": "2.9.1"
134
133
  },
134
+ "overrides": {
135
+ "undici": "8.12.2"
136
+ },
135
137
  "qcrPi": {
136
138
  "//": "Named qcrPi, never `pi`: pi reads a package.json `pi` key as a package manifest, and this root is not a pi package (the plugin is packages/pi-plugin). pi and pi-ai are exact dependencies above (types for the extensions, and the VM boot's plain `npm ci` installs them beside the global CLI the image carries). This block is the mirror a bare checkout without config.toml reads: package and version must equal the dependencies, the lockfile, config.toml [pi], ansible group_vars and every Dockerfile ARG (tests/fences.test.ts [T12], tests/docker-daemon.test.ts). Lens sessions run through our own lens tools (src/engine/lens/), not a pi subagent extension, so no extra extension pin exists.",
137
139
  "package": "@earendil-works/pi-coding-agent",
@@ -94,20 +94,21 @@ the own-account route: it is skipped there.
94
94
  `onboard_go_live` are marked "after setup": setup finishes without them. Do
95
95
  not run them here unless the person asks. Say plainly that the installation
96
96
  stays in dry-run (every intended GitHub write is recorded, nothing is
97
- posted), and that going live is their decision later: re-run `qcr onboard`
98
- and ask for it once a review on the board has shown them what it says.
97
+ posted), and that going live is their decision later, from the home
98
+ (Advanced → "Dry-run or live"), once a review has shown them what it says.
99
99
 
100
100
  ## Finishing
101
101
 
102
102
  When every setup step reports done (the "after setup" ones need not be), say
103
103
  that `finish_and_exit` will re-verify the whole checklist one last time and,
104
- only if all green, close this session and open the TUI on the board: there they
105
- select a pull request and press `R` to review it now. If it returns red checks, that is the answer: explain them, fix
104
+ only if all green, close this session: the home then lists the repository's
105
+ pull requests, and they select one and press `alt+r` to review it now. If it
106
+ returns red checks, that is the answer: explain them, fix
106
107
  what can be fixed through the step tools, and try again. Never present
107
108
  onboarding as finished while anything is red, and never tell the operator to
108
109
  quit around a red check.
109
110
 
110
- Before `finish_and_exit`, pass on what comes next: the first review is `R` on
111
- a pull request on the board; then set up the repo with `qcr setup` (or `I` on
112
- the TUI board). Until then the reviewer knows nothing about this codebase. It is a
111
+ Before `finish_and_exit`, pass on what comes next: the first review is `alt+r`
112
+ on a pull request in the home; then tune the reviewer to the repository with
113
+ the home's "Tune to this repo" (`t` on the panel). Until then the reviewer knows nothing about this codebase. It is a
113
114
  recommendation, once — never start it from here.
@@ -137,6 +137,10 @@
137
137
  "post": {
138
138
  "$ref": "#/$defs/post"
139
139
  },
140
+ "post_ready": {
141
+ "type": "string",
142
+ "description": "A finished PR review that was not posted: the file it is kept in, which a host that asks the person later posts through the gate exactly as `--to pr` would have."
143
+ },
140
144
  "run_id": {
141
145
  "type": "string",
142
146
  "description": "A detached run's id (start, status, stop)."
@@ -149,6 +153,14 @@
149
153
  "type": "string",
150
154
  "description": "What a detached run reviews."
151
155
  },
156
+ "started_at": {
157
+ "type": "number",
158
+ "description": "When a detached run was started, ms since the epoch."
159
+ },
160
+ "ended_at": {
161
+ "type": "number",
162
+ "description": "When a detached run ended (its exit or its stop), ms since the epoch; absent while it runs."
163
+ },
152
164
  "elapsed_s": {
153
165
  "type": "integer",
154
166
  "minimum": 0
@@ -165,6 +177,50 @@
165
177
  "items": {
166
178
  "type": "string"
167
179
  }
180
+ },
181
+ "live": {
182
+ "type": "object",
183
+ "description": "The running round's live log, projected: what the parent and each lens are doing now.",
184
+ "properties": {
185
+ "round": { "type": "integer", "minimum": 1 },
186
+ "parent_status": { "enum": ["active", "settled"] },
187
+ "last_activity": { "type": "string" },
188
+ "updated_at": { "type": "string" },
189
+ "active_tool": { "type": "string" },
190
+ "agents": {
191
+ "type": "array",
192
+ "items": {
193
+ "type": "object",
194
+ "required": ["agent", "status", "duration_ms", "tool_count"],
195
+ "properties": {
196
+ "agent": { "type": "string" },
197
+ "status": { "enum": ["running", "completed", "failed"] },
198
+ "duration_ms": {
199
+ "type": "number",
200
+ "minimum": 0,
201
+ "description": "The lens's time so far; a running lens's counts from its start between updates."
202
+ },
203
+ "tool_count": {
204
+ "type": "number",
205
+ "minimum": 0,
206
+ "description": "Tool calls the lens has started; a finished lens keeps its final count."
207
+ },
208
+ "current_tool": {
209
+ "type": "string",
210
+ "description": "The running (or last) tool, as `<tool>: <args>` or the bare name."
211
+ },
212
+ "current_target": {
213
+ "type": "string",
214
+ "description": "While the lens runs: the path its current tool works on, when it names one."
215
+ },
216
+ "line": {
217
+ "type": "string",
218
+ "description": "The lens as one line a person reads: `security · read src/x.ts · 7 tools · 0:41`."
219
+ }
220
+ }
221
+ }
222
+ }
223
+ }
168
224
  }
169
225
  }
170
226
  },
@@ -234,6 +290,10 @@
234
290
  "kind": {
235
291
  "enum": ["pr", "branch", "uncommitted", "diff"],
236
292
  "description": "What is reviewed: a pull request, the current branch against its base, the uncommitted working tree, or a unified diff handed in (`--diff`). Only a pr target can be posted."
293
+ },
294
+ "uncommitted": {
295
+ "const": true,
296
+ "description": "A branch target reviewed together with the working tree's uncommitted edits, as one review. Absent otherwise."
237
297
  }
238
298
  }
239
299
  },
package/scripts/deploy.sh CHANGED
@@ -46,9 +46,10 @@
46
46
  # cloudflared when the inventory enables it), the
47
47
  # containers, the image stamp, the pi settings and the
48
48
  # reviewer stub, and the state/status.json heartbeat the
49
- # status collector reads. It also warns 14 days before the
50
- # VM's ghcr pull token expires (the expiry that
51
- # scripts/vm-ghcr-login.sh recorded).
49
+ # status collector reads. It also warns 14 days before a
50
+ # private mirror's ghcr pull token expires (the expiry
51
+ # that scripts/vm-ghcr-login.sh recorded; the public
52
+ # release images need no login, so no record).
52
53
  #
53
54
  # `sync`, `dev` and `rebuild` are gone (the on-VM rsync and build are deleted):
54
55
  # they refuse with a pointer to `release`. A pre-release fix is a release.
@@ -1083,8 +1084,9 @@ release_problem() {
1083
1084
  fi
1084
1085
  }
1085
1086
 
1086
- # The VM's ghcr pull token expires (1 year, by ruling), and a lapsed one fails
1087
- # the updater's pull. scripts/vm-ghcr-login.sh records the expiry on this
1087
+ # The release images are public and pull with no login. A private mirror's
1088
+ # ghcr pull token expires (1 year, by ruling), and a lapsed one fails the
1089
+ # updater's pull. scripts/vm-ghcr-login.sh records the expiry on this
1088
1090
  # controller; release and verify read it back. Within GHCR_WARN_DAYS of it
1089
1091
  # (or past it) the plan line starts with WARNING. Warn only, never a refusal.
1090
1092
  GHCR_WARN_DAYS=14
@@ -1095,7 +1097,7 @@ ghcr_expiry_status() {
1095
1097
  local file iso epoch now days
1096
1098
  file="$(qcr_ghcr_expiry_file 2>/dev/null)" || file=""
1097
1099
  if [[ -z "$file" || ! -s "$file" ]]; then
1098
- GHCR_EXPIRY_LINE="no recorded expiry for the VM's ghcr pull token (scripts/vm-ghcr-login.sh records it when the owner logs the VM in)"
1100
+ GHCR_EXPIRY_LINE="no ghcr login recorded: the release images are public and pull without one (a private mirror logs the VM in with scripts/vm-ghcr-login.sh, which records its expiry)"
1099
1101
  return 0
1100
1102
  fi
1101
1103
  iso="$(head -1 "$file" | tr -d '[:space:]')"
@@ -12,7 +12,10 @@
12
12
  # `stop` find it by label. `stop` sends `docker stop -t
13
13
  # <grace>`, then `docker kill`: the container is the
14
14
  # hard-kill backstop for a driver whose loop is frozen.
15
- # qcr a bare qcr opens the operator TUI (tui mode)
15
+ # qcr a bare qcr (or `qcr home`) opens the home: the image's
16
+ # pi with the plugin and its panel (home mode)
17
+ # qcr tui the operator TUI (tui mode), the one verb that starts
18
+ # the companion
16
19
  # qcr login [provider] pi's own /login, stored in the qcr-login volume
17
20
  # qcr update [version] pull a newer image and repin this file
18
21
  #
@@ -21,8 +24,8 @@
21
24
  # a worktree's common git dir at its own path read-only
22
25
  # the qcr-state volume /opt/qcr/state read-write
23
26
  # the qcr-login volume /qcr-login read-only
24
- # (only `login` mounts it read-write,
25
- # at pi's agent dir; its files are in
27
+ # (only `login` and the home mount it
28
+ # read-write, at pi's agent dir; its files are in
26
29
  # group 4747, which every call joins,
27
30
  # so a uid change never loses the login)
28
31
  # ~/.config/qcr/profiles /qcr-config/profiles read-only
@@ -219,11 +222,20 @@ split=1
219
222
  drop=0
220
223
  gh=1
221
224
  case "$verb" in
225
+ # The home is the person's own pi, as `qcr` from npm opens it: pi's agent
226
+ # dir on the login volume, read-write (its device login lands there), its
227
+ # config and sessions on the state volume, and the GitHub token for its `gh`.
222
228
  '')
223
- mode=tui
229
+ mode=home
224
230
  tty=1
225
231
  split=0
226
232
  ;;
233
+ home)
234
+ mode=home
235
+ tty=1
236
+ split=0
237
+ drop=1
238
+ ;;
227
239
  tui)
228
240
  mode=tui
229
241
  tty=1
@@ -555,7 +567,8 @@ fi
555
567
  # ---- the GitHub token (GITHUB_TOKEN, GH_TOKEN, gh; no App on a laptop) -----
556
568
 
557
569
  # The claim call only reads git: it needs no token, and a token file is read once.
558
- if [ "$split" = 1 ] && [ "$gh" = 1 ] && [ "$phase" != claim ]; then
570
+ # The home is handed it too: its `gh` lists and opens the pull requests.
571
+ if { [ "$split" = 1 ] || [ "$mode" = home ]; } && [ "$gh" = 1 ] && [ "$phase" != claim ]; then
559
572
  token="${GITHUB_TOKEN:-}"
560
573
  [ -n "$token" ] || token="${GH_TOKEN:-}"
561
574
  if [ -z "$token" ] && command -v gh >/dev/null 2>&1; then
@@ -603,6 +616,16 @@ if [ "$mode" = login ]; then
603
616
  # pi's own /login, written to the login volume: the only read-write mount of
604
617
  # it. A login needs no repo, no state and no profiles.
605
618
  set -- "$@" --mount "type=volume,src=qcr-login,dst=/home/qcr/.pi/agent"
619
+ elif [ "$mode" = home ]; then
620
+ # The home works on a GitHub repository, not on the directory you stand in:
621
+ # no repo mount. pi's agent dir is the login volume, read-write, as login's
622
+ # is. Its state home, sessions and config.toml (the repository it picks,
623
+ # which it writes) live on the state volume, so they persist and stay
624
+ # writable; the install home is never mounted.
625
+ set -- "$@" --mount "type=volume,src=qcr-login,dst=/home/qcr/.pi/agent"
626
+ set -- "$@" --mount "type=volume,src=qcr-state,dst=/opt/qcr/state"
627
+ set -- "$@" -e QCR_STATE_HOME=/opt/qcr/state/home
628
+ set -- "$@" -e QCR_CONFIG=/opt/qcr/state/home/config.toml
606
629
  else
607
630
  # The repo, read-only; a worktree's common git dir at its own path.
608
631
  set -- "$@" --mount "type=bind,src=$root,dst=/repo,readonly" -w "/repo$rel"
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env bash
2
- # vm-ghcr-login.sh — log the review VM in to ghcr.io, so it can pull the
3
- # release images. Run by the owner, on the controller. Idempotent: running it
4
- # again replaces the login and the recorded expiry.
2
+ # vm-ghcr-login.sh — log the review VM in to ghcr.io, so it can pull images
3
+ # that are not public: a private mirror named by QC_RELEASE_IMAGE and
4
+ # QC_RELEASE_MEMORY_IMAGE. The release images are public and pull with no
5
+ # login, so a VM on them needs none of this. Run by the owner, on the
6
+ # controller. Idempotent: running it again replaces the login and the recorded
7
+ # expiry.
5
8
  #
6
9
  # The credential is a CLASSIC personal access token, scoped read:packages and
7
10
  # nothing else (the one carve-out from the App-only credential rule, for the
@@ -26,7 +29,7 @@
26
29
  # scripts/vm-ghcr-login.sh --version X.Y.Z [--vm HOST] [--login USER] [--token-file FILE]
27
30
  # pbpaste | scripts/vm-ghcr-login.sh --version X.Y.Z # token on stdin
28
31
  #
29
- # --version a published release version; both images must exist at it
32
+ # --version a published version; both images must exist at it
30
33
  # --vm ssh target (default: $QC_VM_HOST, else the inventory's first host)
31
34
  # --login the GitHub login to store (default: the token's own; if given
32
35
  # it must be the token's)
@@ -32,13 +32,7 @@ import { readdir } from "node:fs/promises";
32
32
  import { join, relative } from "node:path";
33
33
  import type { StateStore } from "../state.js";
34
34
  import { yieldToLoop } from "@quickchatai/pi-quickreview/src/yieldloop.js";
35
- import {
36
- archiveStamp,
37
- bundlePassOf,
38
- freshPassId,
39
- identityFields,
40
- sameRecorded,
41
- } from "./archivewrite.js";
35
+ import { archiveStamp, bundlePassOf, freshPassId, identityFields } from "./archivewrite.js";
42
36
  import { readBundleRecords } from "./bundle.js";
43
37
  import { sameIdentity } from "./fsutil.js";
44
38
  import {
@@ -182,20 +176,23 @@ export async function restorePr(
182
176
  const steady = sameIdentity(live, after);
183
177
  if (steady && d.bytes === r.bytes && d.sha256 === r.sha256) {
184
178
  out.present += 1;
185
- // Its identity is recorded unless it is the one the archive record
186
- // already holds (a source that was never unlinked). A file an
187
- // earlier restore pass put back — one that died before its entry
188
- // was written — has a NEW inode: unrecorded, a re-archive after a
189
- // re-close would not know it and would compress the same bytes
190
- // again under `.v2`, instead of reusing the archived copy.
191
- if (!sameRecorded(r, live))
192
- restored.push({
193
- source: r.source,
194
- archive: r.archive,
195
- sha256: r.sha256,
196
- ...identityFields(live),
197
- present: true,
198
- });
179
+ // Its identity is recorded, always. A file an earlier restore pass
180
+ // put back — one that died before its entry was written — is
181
+ // unknown to the manifest otherwise, and a re-archive after a
182
+ // re-close would compress the same bytes again under `.v2` instead
183
+ // of reusing the archived copy. Its identity is no sign of whether
184
+ // it was ever unlinked: the filesystem hands a freed inode number
185
+ // back, and the restore gave the file its original size and mtime,
186
+ // so a file put back can carry exactly the identity the archive
187
+ // record holds. Recording a source that was never unlinked costs
188
+ // one `reused` entry at the re-archive, nothing more.
189
+ restored.push({
190
+ source: r.source,
191
+ archive: r.archive,
192
+ sha256: r.sha256,
193
+ ...identityFields(live),
194
+ present: true,
195
+ });
199
196
  } else out.liveDiffers.push(r.source);
200
197
  continue;
201
198
  }
package/src/botworker.ts CHANGED
@@ -439,7 +439,14 @@ export async function runBotWorker(args: WorkerArgs, deps: WorkerDeps): Promise<
439
439
  // The login: borrowed, blanked, and checked against this worker's own cap.
440
440
  let auth: EngineAuth;
441
441
  try {
442
- auth = engineCredentials({ srcDir: spec.agent_dir, needMs: spec.wall_clock_ms, now });
442
+ auth = engineCredentials({
443
+ srcDir: spec.login_dir ?? spec.agent_dir,
444
+ needMs: spec.wall_clock_ms,
445
+ now,
446
+ // a shared pi home holds the person's other logins too: only the
447
+ // review's providers are admitted
448
+ ...(spec.providers !== undefined ? { providers: spec.providers } : {}),
449
+ });
443
450
  } catch (e) {
444
451
  if (e instanceof AuthError) throw new Stop(EXIT.auth, e.message);
445
452
  throw e;
@@ -461,7 +468,11 @@ export async function runBotWorker(args: WorkerArgs, deps: WorkerDeps): Promise<
461
468
  ownsProcess: true,
462
469
  homeDir: homedir(),
463
470
  ...(ids !== null && !("refused" in ids) ? { toolUid: ids } : {}),
464
- fenceDirs: [...loginSourceDirs(env), ...credentialPaths(env, spec.env)],
471
+ fenceDirs: [
472
+ ...loginSourceDirs(env),
473
+ ...(spec.login_dir ? [spec.login_dir] : []),
474
+ ...credentialPaths(env, spec.env),
475
+ ],
465
476
  credentials: auth.credentials,
466
477
  settings: agentDirSettings(spec.agent_dir),
467
478
  });
package/src/channelfs.ts CHANGED
@@ -1,26 +1,3 @@
1
- // The file I/O of the scan's TUI↔companion records and channel files (the
2
- // run record, the findings store, the stop and stage-2 start requests and
3
- // their answers). A dependency-free leaf, so the two setup leaves the TUI
4
- // imports (scanreport.ts, stage2request.ts) share it without reaching each
5
- // other at runtime.
6
- import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
7
- import { dirname } from "node:path";
8
-
9
- /** A file's text, or null when it is ABSENT — any other failure throws, so a
10
- * reader can tell "not written yet" from "cannot be read". */
11
- export function readTextIfPresent(path: string): string | null {
12
- try {
13
- return readFileSync(path, "utf8");
14
- } catch (err) {
15
- if ((err as NodeJS.ErrnoException).code === "ENOENT") return null;
16
- throw err;
17
- }
18
- }
19
-
20
- /** tmp+rename: a reader never sees a half-written file. */
21
- export function writeAtomic(path: string, text: string): void {
22
- mkdirSync(dirname(path), { recursive: true });
23
- const tmp = `${path}.${process.pid}.tmp`;
24
- writeFileSync(tmp, text);
25
- renameSync(tmp, path);
26
- }
1
+ // Moved to the plugin (packages/pi-plugin/src/scan/channelfs.ts): this name
2
+ // stays for the app's importers.
3
+ export * from "@quickchatai/pi-quickreview/src/scan/channelfs.js";
package/src/cli.ts CHANGED
@@ -1857,55 +1857,6 @@ export async function cmdSetup(rest: string[]): Promise<number> {
1857
1857
  return 0;
1858
1858
  }
1859
1859
 
1860
- /** `qcr onboard` — the guided setup wizard: pi at the pinned
1861
- * version via npx (works from a bare clone; pi stays a non-dependency), the
1862
- * onboarding prompt + extension + a restricted tool set, then the TUI handoff
1863
- * when the checklist verifies complete. Resumable: run it again after a crash
1864
- * and completed steps are re-verified, not repeated. */
1865
- export async function cmdOnboard(): Promise<number> {
1866
- const { piPinFromPackageJson, onboardingSessionDir, runOnboard } =
1867
- await import("./onboard/launch.js");
1868
- const { checklistComplete } = await import("./onboard/engine.js");
1869
- const { spawn } = await import("node:child_process");
1870
- const root = sandboxRoot();
1871
- // Pin source: config.toml [pi] when it loads; package.json's "qcrPi" block on a
1872
- // bare clone (config.toml is written later, BY onboarding). Same pinned
1873
- // version by the rule-7 mirror discipline.
1874
- let pin: { package: string; version: string } | null = null;
1875
- try {
1876
- const { package: pkg, version } = loadConfig().pi;
1877
- pin = { package: pkg, version };
1878
- } catch {
1879
- pin = piPinFromPackageJson(root);
1880
- }
1881
- if (!pin)
1882
- throw new Error(
1883
- 'onboard: pi pin not found (need config.toml [pi] or the package.json "qcrPi" block)',
1884
- );
1885
- mkdirSync(onboardingSessionDir(root), { recursive: true });
1886
- const run = (cmd: string, args: string[]): Promise<number> =>
1887
- new Promise((resolvePromise) => {
1888
- const child = spawn(cmd, args, {
1889
- stdio: "inherit",
1890
- cwd: root,
1891
- env: { ...process.env, QCR_ROOT: root },
1892
- });
1893
- child.on("error", (err) => {
1894
- console.error(`onboard: ${cmd} failed to start: ${err.message}`);
1895
- resolvePromise(1);
1896
- });
1897
- child.on("close", (code) => resolvePromise(code ?? 1));
1898
- });
1899
- return runOnboard({
1900
- pin,
1901
- root,
1902
- spawn: run,
1903
- isComplete: () => checklistComplete(join(root, "state")),
1904
- log: (line) => console.log(line),
1905
- cliPath: join(root, "dist", "src", "cli.js"),
1906
- });
1907
- }
1908
-
1909
1860
  export const ARCHIVE_USAGE =
1910
1861
  "usage: qcr archive status [--plan] [--json] | qcr archive run | qcr archive restore <pr> | " +
1911
1862
  "qcr archive extract <pr> --to <dir>";
@@ -2154,6 +2105,16 @@ export async function main(rawArgv: string[]): Promise<void> {
2154
2105
  const { runTui } = await import("./tui/index.js");
2155
2106
  return runTui(rest);
2156
2107
  }
2108
+ case "home": {
2109
+ // the home (src/home/launch.ts): the bin routes a bare `qcr` here too
2110
+ // (src/qcrbin.ts); the image's home mode reaches it through this verb
2111
+ if (rest.length > 0) throw new Error("usage: qcr home");
2112
+ const { homeRunDeps, runHome } = await import("./home/launch.js");
2113
+ // the token taken at startup, when the wrapper handed one over
2114
+ const githubToken = takeHandoffToken();
2115
+ process.exitCode = runHome({ ...homeRunDeps(sandboxRoot()), githubToken: () => githubToken });
2116
+ return;
2117
+ }
2157
2118
  case "companion":
2158
2119
  if (rest.length !== 1) throw new Error("usage: qcr companion <up|down|status|rebuild|plan>");
2159
2120
  return cmdCompanion(rest[0]);
@@ -2161,10 +2122,6 @@ export async function main(rawArgv: string[]): Promise<void> {
2161
2122
  process.exitCode = await cmdMemory(rest);
2162
2123
  return;
2163
2124
  }
2164
- case "onboard": {
2165
- process.exitCode = await cmdOnboard();
2166
- return;
2167
- }
2168
2125
  case "setup": {
2169
2126
  process.exitCode = await cmdSetup(rest);
2170
2127
  return;
@@ -2273,7 +2230,7 @@ export async function main(rawArgv: string[]): Promise<void> {
2273
2230
  }
2274
2231
  default:
2275
2232
  console.log(
2276
- "usage: qcr <version|config check [--config-home <dir>]|onboard|setup [validate [<profile>]|commit]|status|spend|today|why <n>|metrics [--pr <n>]|events|takeover <n>|debug-round <n>|release <n>|tui [pr|overseer]|drain <on [--hold]|off>|pause-pr <pr> [--reason <text>]|resume-pr <pr>|review-now <pr>|github-token sync [--gh <path>] [--secrets-dir <dir>]|message <pr> [--may-post] <text...>|archive <status [--plan]|run|restore <pr>|extract <pr> --to <dir>>|install <where|migrate>|" +
2233
+ "usage: qcr <version|config check [--config-home <dir>]|home|setup [validate [<profile>]|commit]|status|spend|today|why <n>|metrics [--pr <n>]|events|takeover <n>|debug-round <n>|release <n>|tui [pr|overseer]|drain <on [--hold]|off>|pause-pr <pr> [--reason <text>]|resume-pr <pr>|review-now <pr>|github-token sync [--gh <path>] [--secrets-dir <dir>]|message <pr> [--may-post] <text...>|archive <status [--plan]|run|restore <pr>|extract <pr> --to <dir>>|install <where|migrate>|" +
2277
2234
  "companion <up|down|status|rebuild>|review [start|status|stop|branch|--uncommitted|--diff FILE|-|<PR number|URL>] [--repo OWNER/NAME] [--base REF] [--preview] [--key K] [--limit USD] [--json]|live [RUN_ID|DIR] [--lens ID] [--round N] [--follow] [--json]|open [RUN_ID|DIR|FILE.jsonl|scan[:RUN]|pr N [--round R]] [--lens ID] [--write] [--print]|scan <preview|start|status|stop|findings|matches|match|suggestions|stage2 propose|stage2 start|dismiss|units show|units propose|units adjust|units save|login|doctor|rebuild>|memory reembed [--user <name>]|memory relayout [--plan|--apply]|backfill [--dry-run] [--limit <n>]|history <digest|pull>|outcomes [--days <n>] [--capture [--read-github]]|lessons [mine|shadow-report [--days <n>]|off|on|suppress <id>|unsuppress <id>]|checklist|compare <pr...>|" +
2278
2235
  "regression [--set <path>] [--clone <path>] [--change <name>] [--limit <n>] [--out <path>]>",
2279
2236
  );