looply-cli 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (793) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -3
  3. package/dist/.src-hash +1 -0
  4. package/dist/acceptance-observe.d.ts +108 -0
  5. package/dist/acceptance-observe.d.ts.map +1 -0
  6. package/dist/acceptance-observe.js +152 -0
  7. package/dist/acceptance-observe.js.map +1 -0
  8. package/dist/agent-cmd.d.ts +131 -0
  9. package/dist/agent-cmd.d.ts.map +1 -0
  10. package/dist/agent-cmd.js +154 -0
  11. package/dist/agent-cmd.js.map +1 -0
  12. package/dist/agent-contained.d.ts +114 -0
  13. package/dist/agent-contained.d.ts.map +1 -0
  14. package/dist/agent-contained.js +292 -0
  15. package/dist/agent-contained.js.map +1 -0
  16. package/dist/agent-driver.d.ts +130 -0
  17. package/dist/agent-driver.d.ts.map +1 -0
  18. package/dist/agent-driver.js +170 -0
  19. package/dist/agent-driver.js.map +1 -0
  20. package/dist/agent-envelope.d.ts +94 -0
  21. package/dist/agent-envelope.d.ts.map +1 -0
  22. package/dist/agent-envelope.js +214 -0
  23. package/dist/agent-envelope.js.map +1 -0
  24. package/dist/agent-exec.d.ts +182 -0
  25. package/dist/agent-exec.d.ts.map +1 -0
  26. package/dist/agent-exec.js +125 -0
  27. package/dist/agent-exec.js.map +1 -0
  28. package/dist/agent-runtime.d.ts +23 -0
  29. package/dist/agent-runtime.d.ts.map +1 -0
  30. package/dist/agent-runtime.js +143 -0
  31. package/dist/agent-runtime.js.map +1 -0
  32. package/dist/approval-gate-read.d.ts +66 -0
  33. package/dist/approval-gate-read.d.ts.map +1 -0
  34. package/dist/approval-gate-read.js +138 -0
  35. package/dist/approval-gate-read.js.map +1 -0
  36. package/dist/approval-resolution.d.ts +87 -0
  37. package/dist/approval-resolution.d.ts.map +1 -0
  38. package/dist/approval-resolution.js +100 -0
  39. package/dist/approval-resolution.js.map +1 -0
  40. package/dist/approval-resumes.d.ts +102 -0
  41. package/dist/approval-resumes.d.ts.map +1 -0
  42. package/dist/approval-resumes.js +167 -0
  43. package/dist/approval-resumes.js.map +1 -0
  44. package/dist/ask-brain.d.ts +167 -0
  45. package/dist/ask-brain.d.ts.map +1 -0
  46. package/dist/ask-brain.js +355 -0
  47. package/dist/ask-brain.js.map +1 -0
  48. package/dist/autonomy-authority.d.ts +80 -0
  49. package/dist/autonomy-authority.d.ts.map +1 -0
  50. package/dist/autonomy-authority.js +82 -0
  51. package/dist/autonomy-authority.js.map +1 -0
  52. package/dist/autonomy-controller.d.ts +321 -0
  53. package/dist/autonomy-controller.d.ts.map +1 -0
  54. package/dist/autonomy-controller.js +517 -0
  55. package/dist/autonomy-controller.js.map +1 -0
  56. package/dist/autonomy-store.d.ts +327 -0
  57. package/dist/autonomy-store.d.ts.map +1 -0
  58. package/dist/autonomy-store.js +581 -0
  59. package/dist/autonomy-store.js.map +1 -0
  60. package/dist/autonomy-tick.d.ts +257 -0
  61. package/dist/autonomy-tick.d.ts.map +1 -0
  62. package/dist/autonomy-tick.js +616 -0
  63. package/dist/autonomy-tick.js.map +1 -0
  64. package/dist/bin.d.ts +3 -0
  65. package/dist/bin.d.ts.map +1 -0
  66. package/dist/bin.js +2727 -0
  67. package/dist/bin.js.map +1 -0
  68. package/dist/brain-advice.d.ts +21 -0
  69. package/dist/brain-advice.d.ts.map +1 -0
  70. package/dist/brain-advice.js +51 -0
  71. package/dist/brain-advice.js.map +1 -0
  72. package/dist/candidate-replay-runtime.d.ts +125 -0
  73. package/dist/candidate-replay-runtime.d.ts.map +1 -0
  74. package/dist/candidate-replay-runtime.js +188 -0
  75. package/dist/candidate-replay-runtime.js.map +1 -0
  76. package/dist/checkout-carry.d.ts +76 -0
  77. package/dist/checkout-carry.d.ts.map +1 -0
  78. package/dist/checkout-carry.js +125 -0
  79. package/dist/checkout-carry.js.map +1 -0
  80. package/dist/citation-readers.d.ts +26 -0
  81. package/dist/citation-readers.d.ts.map +1 -0
  82. package/dist/citation-readers.js +102 -0
  83. package/dist/citation-readers.js.map +1 -0
  84. package/dist/command-receipts.d.ts +79 -0
  85. package/dist/command-receipts.d.ts.map +1 -0
  86. package/dist/command-receipts.js +143 -0
  87. package/dist/command-receipts.js.map +1 -0
  88. package/dist/command-runner.d.ts +32 -0
  89. package/dist/command-runner.d.ts.map +1 -0
  90. package/dist/command-runner.js +108 -0
  91. package/dist/command-runner.js.map +1 -0
  92. package/dist/compatible-sets.d.ts +67 -0
  93. package/dist/compatible-sets.d.ts.map +1 -0
  94. package/dist/compatible-sets.js +81 -0
  95. package/dist/compatible-sets.js.map +1 -0
  96. package/dist/compile-check.d.ts +31 -0
  97. package/dist/compile-check.d.ts.map +1 -0
  98. package/dist/compile-check.js +34 -0
  99. package/dist/compile-check.js.map +1 -0
  100. package/dist/compile-store.d.ts +16 -0
  101. package/dist/compile-store.d.ts.map +1 -0
  102. package/dist/compile-store.js +21 -0
  103. package/dist/compile-store.js.map +1 -0
  104. package/dist/completion.d.ts +106 -0
  105. package/dist/completion.d.ts.map +1 -0
  106. package/dist/completion.js +131 -0
  107. package/dist/completion.js.map +1 -0
  108. package/dist/config.d.ts +549 -0
  109. package/dist/config.d.ts.map +1 -0
  110. package/dist/config.js +763 -0
  111. package/dist/config.js.map +1 -0
  112. package/dist/control-file.d.ts +142 -0
  113. package/dist/control-file.d.ts.map +1 -0
  114. package/dist/control-file.js +144 -0
  115. package/dist/control-file.js.map +1 -0
  116. package/dist/coordinator-state.d.ts +131 -0
  117. package/dist/coordinator-state.d.ts.map +1 -0
  118. package/dist/coordinator-state.js +380 -0
  119. package/dist/coordinator-state.js.map +1 -0
  120. package/dist/coordinator.d.ts +95 -0
  121. package/dist/coordinator.d.ts.map +1 -0
  122. package/dist/coordinator.js +97 -0
  123. package/dist/coordinator.js.map +1 -0
  124. package/dist/cursor-store.d.ts +53 -0
  125. package/dist/cursor-store.d.ts.map +1 -0
  126. package/dist/cursor-store.js +98 -0
  127. package/dist/cursor-store.js.map +1 -0
  128. package/dist/daemon-cmd.d.ts +239 -0
  129. package/dist/daemon-cmd.d.ts.map +1 -0
  130. package/dist/daemon-cmd.js +1751 -0
  131. package/dist/daemon-cmd.js.map +1 -0
  132. package/dist/daemon-lock.d.ts +140 -0
  133. package/dist/daemon-lock.d.ts.map +1 -0
  134. package/dist/daemon-lock.js +352 -0
  135. package/dist/daemon-lock.js.map +1 -0
  136. package/dist/daemon-recovery.d.ts +78 -0
  137. package/dist/daemon-recovery.d.ts.map +1 -0
  138. package/dist/daemon-recovery.js +161 -0
  139. package/dist/daemon-recovery.js.map +1 -0
  140. package/dist/daemon.d.ts +368 -0
  141. package/dist/daemon.d.ts.map +1 -0
  142. package/dist/daemon.js +601 -0
  143. package/dist/daemon.js.map +1 -0
  144. package/dist/declared-response-shape.d.ts +93 -0
  145. package/dist/declared-response-shape.d.ts.map +1 -0
  146. package/dist/declared-response-shape.js +200 -0
  147. package/dist/declared-response-shape.js.map +1 -0
  148. package/dist/declared-surfaces.d.ts +16 -0
  149. package/dist/declared-surfaces.d.ts.map +1 -0
  150. package/dist/declared-surfaces.js +19 -0
  151. package/dist/declared-surfaces.js.map +1 -0
  152. package/dist/depends-review.d.ts +113 -0
  153. package/dist/depends-review.d.ts.map +1 -0
  154. package/dist/depends-review.js +110 -0
  155. package/dist/depends-review.js.map +1 -0
  156. package/dist/doctor.d.ts +91 -0
  157. package/dist/doctor.d.ts.map +1 -0
  158. package/dist/doctor.js +71 -0
  159. package/dist/doctor.js.map +1 -0
  160. package/dist/edge-approval.d.ts +105 -0
  161. package/dist/edge-approval.d.ts.map +1 -0
  162. package/dist/edge-approval.js +100 -0
  163. package/dist/edge-approval.js.map +1 -0
  164. package/dist/evidence-cache.d.ts +45 -0
  165. package/dist/evidence-cache.d.ts.map +1 -0
  166. package/dist/evidence-cache.js +90 -0
  167. package/dist/evidence-cache.js.map +1 -0
  168. package/dist/evidence-cmd.d.ts +133 -0
  169. package/dist/evidence-cmd.d.ts.map +1 -0
  170. package/dist/evidence-cmd.js +347 -0
  171. package/dist/evidence-cmd.js.map +1 -0
  172. package/dist/evidence-facts.d.ts +40 -0
  173. package/dist/evidence-facts.d.ts.map +1 -0
  174. package/dist/evidence-facts.js +114 -0
  175. package/dist/evidence-facts.js.map +1 -0
  176. package/dist/exclusive-lock.d.ts +52 -0
  177. package/dist/exclusive-lock.d.ts.map +1 -0
  178. package/dist/exclusive-lock.js +150 -0
  179. package/dist/exclusive-lock.js.map +1 -0
  180. package/dist/exec-domains.d.ts +140 -0
  181. package/dist/exec-domains.d.ts.map +1 -0
  182. package/dist/exec-domains.js +287 -0
  183. package/dist/exec-domains.js.map +1 -0
  184. package/dist/exemption-store.d.ts +197 -0
  185. package/dist/exemption-store.d.ts.map +1 -0
  186. package/dist/exemption-store.js +266 -0
  187. package/dist/exemption-store.js.map +1 -0
  188. package/dist/explicit-start-gate.d.ts +96 -0
  189. package/dist/explicit-start-gate.d.ts.map +1 -0
  190. package/dist/explicit-start-gate.js +103 -0
  191. package/dist/explicit-start-gate.js.map +1 -0
  192. package/dist/fs-trouble.d.ts +70 -0
  193. package/dist/fs-trouble.d.ts.map +1 -0
  194. package/dist/fs-trouble.js +98 -0
  195. package/dist/fs-trouble.js.map +1 -0
  196. package/dist/gate.d.ts +278 -0
  197. package/dist/gate.d.ts.map +1 -0
  198. package/dist/gate.js +278 -0
  199. package/dist/gate.js.map +1 -0
  200. package/dist/git-driver.d.ts +519 -0
  201. package/dist/git-driver.d.ts.map +1 -0
  202. package/dist/git-driver.js +900 -0
  203. package/dist/git-driver.js.map +1 -0
  204. package/dist/git-push-executor.d.ts +41 -0
  205. package/dist/git-push-executor.d.ts.map +1 -0
  206. package/dist/git-push-executor.js +142 -0
  207. package/dist/git-push-executor.js.map +1 -0
  208. package/dist/glob.d.ts +26 -0
  209. package/dist/glob.d.ts.map +1 -0
  210. package/dist/glob.js +93 -0
  211. package/dist/glob.js.map +1 -0
  212. package/dist/goal-adaptive.d.ts +295 -0
  213. package/dist/goal-adaptive.d.ts.map +1 -0
  214. package/dist/goal-adaptive.js +393 -0
  215. package/dist/goal-adaptive.js.map +1 -0
  216. package/dist/goal-compile.d.ts +342 -0
  217. package/dist/goal-compile.d.ts.map +1 -0
  218. package/dist/goal-compile.js +444 -0
  219. package/dist/goal-compile.js.map +1 -0
  220. package/dist/goal-depends-executor.d.ts +55 -0
  221. package/dist/goal-depends-executor.d.ts.map +1 -0
  222. package/dist/goal-depends-executor.js +202 -0
  223. package/dist/goal-depends-executor.js.map +1 -0
  224. package/dist/goal-execution-evidence.d.ts +135 -0
  225. package/dist/goal-execution-evidence.d.ts.map +1 -0
  226. package/dist/goal-execution-evidence.js +159 -0
  227. package/dist/goal-execution-evidence.js.map +1 -0
  228. package/dist/goal-gap.d.ts +140 -0
  229. package/dist/goal-gap.d.ts.map +1 -0
  230. package/dist/goal-gap.js +197 -0
  231. package/dist/goal-gap.js.map +1 -0
  232. package/dist/goal-met-target.d.ts +98 -0
  233. package/dist/goal-met-target.d.ts.map +1 -0
  234. package/dist/goal-met-target.js +92 -0
  235. package/dist/goal-met-target.js.map +1 -0
  236. package/dist/goal-ownership.d.ts +416 -0
  237. package/dist/goal-ownership.d.ts.map +1 -0
  238. package/dist/goal-ownership.js +576 -0
  239. package/dist/goal-ownership.js.map +1 -0
  240. package/dist/goal-runs.d.ts +82 -0
  241. package/dist/goal-runs.d.ts.map +1 -0
  242. package/dist/goal-runs.js +120 -0
  243. package/dist/goal-runs.js.map +1 -0
  244. package/dist/goal-store.d.ts +146 -0
  245. package/dist/goal-store.d.ts.map +1 -0
  246. package/dist/goal-store.js +294 -0
  247. package/dist/goal-store.js.map +1 -0
  248. package/dist/goal-view.d.ts +156 -0
  249. package/dist/goal-view.d.ts.map +1 -0
  250. package/dist/goal-view.js +499 -0
  251. package/dist/goal-view.js.map +1 -0
  252. package/dist/governed-approvals.d.ts +152 -0
  253. package/dist/governed-approvals.d.ts.map +1 -0
  254. package/dist/governed-approvals.js +246 -0
  255. package/dist/governed-approvals.js.map +1 -0
  256. package/dist/governed-broker.d.ts +169 -0
  257. package/dist/governed-broker.d.ts.map +1 -0
  258. package/dist/governed-broker.js +196 -0
  259. package/dist/governed-broker.js.map +1 -0
  260. package/dist/governed-run.d.ts +220 -0
  261. package/dist/governed-run.d.ts.map +1 -0
  262. package/dist/governed-run.js +406 -0
  263. package/dist/governed-run.js.map +1 -0
  264. package/dist/grant-reconcile.d.ts +63 -0
  265. package/dist/grant-reconcile.d.ts.map +1 -0
  266. package/dist/grant-reconcile.js +121 -0
  267. package/dist/grant-reconcile.js.map +1 -0
  268. package/dist/index-for-head.d.ts +56 -0
  269. package/dist/index-for-head.d.ts.map +1 -0
  270. package/dist/index-for-head.js +103 -0
  271. package/dist/index-for-head.js.map +1 -0
  272. package/dist/index.d.ts +39 -0
  273. package/dist/index.d.ts.map +1 -0
  274. package/dist/index.js +55 -0
  275. package/dist/index.js.map +1 -0
  276. package/dist/init-detect.d.ts +39 -0
  277. package/dist/init-detect.d.ts.map +1 -0
  278. package/dist/init-detect.js +188 -0
  279. package/dist/init-detect.js.map +1 -0
  280. package/dist/integration-deps.d.ts +62 -0
  281. package/dist/integration-deps.d.ts.map +1 -0
  282. package/dist/integration-deps.js +251 -0
  283. package/dist/integration-deps.js.map +1 -0
  284. package/dist/integration-runtime.d.ts +458 -0
  285. package/dist/integration-runtime.d.ts.map +1 -0
  286. package/dist/integration-runtime.js +681 -0
  287. package/dist/integration-runtime.js.map +1 -0
  288. package/dist/integration.d.ts +45 -0
  289. package/dist/integration.d.ts.map +1 -0
  290. package/dist/integration.js +87 -0
  291. package/dist/integration.js.map +1 -0
  292. package/dist/integrator.d.ts +84 -0
  293. package/dist/integrator.d.ts.map +1 -0
  294. package/dist/integrator.js +193 -0
  295. package/dist/integrator.js.map +1 -0
  296. package/dist/lane-claims.d.ts +174 -0
  297. package/dist/lane-claims.d.ts.map +1 -0
  298. package/dist/lane-claims.js +218 -0
  299. package/dist/lane-claims.js.map +1 -0
  300. package/dist/lane-coverage.d.ts +87 -0
  301. package/dist/lane-coverage.d.ts.map +1 -0
  302. package/dist/lane-coverage.js +129 -0
  303. package/dist/lane-coverage.js.map +1 -0
  304. package/dist/ledger-replay.d.ts +115 -0
  305. package/dist/ledger-replay.d.ts.map +1 -0
  306. package/dist/ledger-replay.js +155 -0
  307. package/dist/ledger-replay.js.map +1 -0
  308. package/dist/ledger-store.d.ts +68 -0
  309. package/dist/ledger-store.d.ts.map +1 -0
  310. package/dist/ledger-store.js +111 -0
  311. package/dist/ledger-store.js.map +1 -0
  312. package/dist/loop-safety-observer.d.ts +132 -0
  313. package/dist/loop-safety-observer.d.ts.map +1 -0
  314. package/dist/loop-safety-observer.js +76 -0
  315. package/dist/loop-safety-observer.js.map +1 -0
  316. package/dist/loop-safety-store.d.ts +339 -0
  317. package/dist/loop-safety-store.d.ts.map +1 -0
  318. package/dist/loop-safety-store.js +800 -0
  319. package/dist/loop-safety-store.js.map +1 -0
  320. package/dist/machine-store.d.ts +54 -0
  321. package/dist/machine-store.d.ts.map +1 -0
  322. package/dist/machine-store.js +149 -0
  323. package/dist/machine-store.js.map +1 -0
  324. package/dist/mcp-main.d.ts +2 -0
  325. package/dist/mcp-main.d.ts.map +1 -0
  326. package/dist/mcp-main.js +116 -0
  327. package/dist/mcp-main.js.map +1 -0
  328. package/dist/mcp-server.d.ts +72 -0
  329. package/dist/mcp-server.d.ts.map +1 -0
  330. package/dist/mcp-server.js +259 -0
  331. package/dist/mcp-server.js.map +1 -0
  332. package/dist/model-routing.d.ts +75 -0
  333. package/dist/model-routing.d.ts.map +1 -0
  334. package/dist/model-routing.js +108 -0
  335. package/dist/model-routing.js.map +1 -0
  336. package/dist/model-transport.d.ts +188 -0
  337. package/dist/model-transport.d.ts.map +1 -0
  338. package/dist/model-transport.js +625 -0
  339. package/dist/model-transport.js.map +1 -0
  340. package/dist/notify-cadence.d.ts +55 -0
  341. package/dist/notify-cadence.d.ts.map +1 -0
  342. package/dist/notify-cadence.js +68 -0
  343. package/dist/notify-cadence.js.map +1 -0
  344. package/dist/notify-channel.d.ts +69 -0
  345. package/dist/notify-channel.d.ts.map +1 -0
  346. package/dist/notify-channel.js +151 -0
  347. package/dist/notify-channel.js.map +1 -0
  348. package/dist/notify.d.ts +71 -0
  349. package/dist/notify.d.ts.map +1 -0
  350. package/dist/notify.js +125 -0
  351. package/dist/notify.js.map +1 -0
  352. package/dist/observation-inventory.d.ts +65 -0
  353. package/dist/observation-inventory.d.ts.map +1 -0
  354. package/dist/observation-inventory.js +139 -0
  355. package/dist/observation-inventory.js.map +1 -0
  356. package/dist/owned-children.d.ts +45 -0
  357. package/dist/owned-children.d.ts.map +1 -0
  358. package/dist/owned-children.js +64 -0
  359. package/dist/owned-children.js.map +1 -0
  360. package/dist/packet-for-goal.d.ts +19 -0
  361. package/dist/packet-for-goal.d.ts.map +1 -0
  362. package/dist/packet-for-goal.js +32 -0
  363. package/dist/packet-for-goal.js.map +1 -0
  364. package/dist/pair-cmd.d.ts +85 -0
  365. package/dist/pair-cmd.d.ts.map +1 -0
  366. package/dist/pair-cmd.js +205 -0
  367. package/dist/pair-cmd.js.map +1 -0
  368. package/dist/partial-asker.d.ts +96 -0
  369. package/dist/partial-asker.d.ts.map +1 -0
  370. package/dist/partial-asker.js +117 -0
  371. package/dist/partial-asker.js.map +1 -0
  372. package/dist/pdf-text-lines.d.ts +17 -0
  373. package/dist/pdf-text-lines.d.ts.map +1 -0
  374. package/dist/pdf-text-lines.js +70 -0
  375. package/dist/pdf-text-lines.js.map +1 -0
  376. package/dist/pdf-text-worker.d.ts +2 -0
  377. package/dist/pdf-text-worker.d.ts.map +1 -0
  378. package/dist/pdf-text-worker.js +81 -0
  379. package/dist/pdf-text-worker.js.map +1 -0
  380. package/dist/pdf-text.d.ts +56 -0
  381. package/dist/pdf-text.d.ts.map +1 -0
  382. package/dist/pdf-text.js +64 -0
  383. package/dist/pdf-text.js.map +1 -0
  384. package/dist/pending-action.d.ts +154 -0
  385. package/dist/pending-action.d.ts.map +1 -0
  386. package/dist/pending-action.js +148 -0
  387. package/dist/pending-action.js.map +1 -0
  388. package/dist/preemptor.d.ts +40 -0
  389. package/dist/preemptor.d.ts.map +1 -0
  390. package/dist/preemptor.js +49 -0
  391. package/dist/preemptor.js.map +1 -0
  392. package/dist/programme-mode.d.ts +29 -0
  393. package/dist/programme-mode.d.ts.map +1 -0
  394. package/dist/programme-mode.js +50 -0
  395. package/dist/programme-mode.js.map +1 -0
  396. package/dist/progress-view.d.ts +14 -0
  397. package/dist/progress-view.d.ts.map +1 -0
  398. package/dist/progress-view.js +114 -0
  399. package/dist/progress-view.js.map +1 -0
  400. package/dist/project-graph-builder.d.ts +81 -0
  401. package/dist/project-graph-builder.d.ts.map +1 -0
  402. package/dist/project-graph-builder.js +246 -0
  403. package/dist/project-graph-builder.js.map +1 -0
  404. package/dist/project-graph-runtime.d.ts +86 -0
  405. package/dist/project-graph-runtime.d.ts.map +1 -0
  406. package/dist/project-graph-runtime.js +112 -0
  407. package/dist/project-graph-runtime.js.map +1 -0
  408. package/dist/project-index-builder.d.ts +96 -0
  409. package/dist/project-index-builder.d.ts.map +1 -0
  410. package/dist/project-index-builder.js +298 -0
  411. package/dist/project-index-builder.js.map +1 -0
  412. package/dist/project-index-facts.d.ts +51 -0
  413. package/dist/project-index-facts.d.ts.map +1 -0
  414. package/dist/project-index-facts.js +117 -0
  415. package/dist/project-index-facts.js.map +1 -0
  416. package/dist/project-index-store.d.ts +97 -0
  417. package/dist/project-index-store.d.ts.map +1 -0
  418. package/dist/project-index-store.js +140 -0
  419. package/dist/project-index-store.js.map +1 -0
  420. package/dist/project-memory-store.d.ts +127 -0
  421. package/dist/project-memory-store.d.ts.map +1 -0
  422. package/dist/project-memory-store.js +302 -0
  423. package/dist/project-memory-store.js.map +1 -0
  424. package/dist/proposal-store.d.ts +14 -0
  425. package/dist/proposal-store.d.ts.map +1 -0
  426. package/dist/proposal-store.js +19 -0
  427. package/dist/proposal-store.js.map +1 -0
  428. package/dist/prover.d.ts +80 -0
  429. package/dist/prover.d.ts.map +1 -0
  430. package/dist/prover.js +137 -0
  431. package/dist/prover.js.map +1 -0
  432. package/dist/provider-identity-store.d.ts +34 -0
  433. package/dist/provider-identity-store.d.ts.map +1 -0
  434. package/dist/provider-identity-store.js +72 -0
  435. package/dist/provider-identity-store.js.map +1 -0
  436. package/dist/provider-identity.d.ts +104 -0
  437. package/dist/provider-identity.d.ts.map +1 -0
  438. package/dist/provider-identity.js +167 -0
  439. package/dist/provider-identity.js.map +1 -0
  440. package/dist/provider-limit.d.ts +79 -0
  441. package/dist/provider-limit.d.ts.map +1 -0
  442. package/dist/provider-limit.js +148 -0
  443. package/dist/provider-limit.js.map +1 -0
  444. package/dist/provider-preflight.d.ts +171 -0
  445. package/dist/provider-preflight.d.ts.map +1 -0
  446. package/dist/provider-preflight.js +395 -0
  447. package/dist/provider-preflight.js.map +1 -0
  448. package/dist/provider-state.d.ts +120 -0
  449. package/dist/provider-state.d.ts.map +1 -0
  450. package/dist/provider-state.js +182 -0
  451. package/dist/provider-state.js.map +1 -0
  452. package/dist/providers.d.ts +132 -0
  453. package/dist/providers.d.ts.map +1 -0
  454. package/dist/providers.js +161 -0
  455. package/dist/providers.js.map +1 -0
  456. package/dist/publication-recorder.d.ts +117 -0
  457. package/dist/publication-recorder.d.ts.map +1 -0
  458. package/dist/publication-recorder.js +124 -0
  459. package/dist/publication-recorder.js.map +1 -0
  460. package/dist/pursuit-delivery.d.ts +24 -0
  461. package/dist/pursuit-delivery.d.ts.map +1 -0
  462. package/dist/pursuit-delivery.js +38 -0
  463. package/dist/pursuit-delivery.js.map +1 -0
  464. package/dist/queue-store.d.ts +132 -0
  465. package/dist/queue-store.d.ts.map +1 -0
  466. package/dist/queue-store.js +185 -0
  467. package/dist/queue-store.js.map +1 -0
  468. package/dist/queue-summary.d.ts +14 -0
  469. package/dist/queue-summary.d.ts.map +1 -0
  470. package/dist/queue-summary.js +82 -0
  471. package/dist/queue-summary.js.map +1 -0
  472. package/dist/queue-view.d.ts +20 -0
  473. package/dist/queue-view.d.ts.map +1 -0
  474. package/dist/queue-view.js +163 -0
  475. package/dist/queue-view.js.map +1 -0
  476. package/dist/queue-write-lease.d.ts +106 -0
  477. package/dist/queue-write-lease.d.ts.map +1 -0
  478. package/dist/queue-write-lease.js +282 -0
  479. package/dist/queue-write-lease.js.map +1 -0
  480. package/dist/rails-home.d.ts +162 -0
  481. package/dist/rails-home.d.ts.map +1 -0
  482. package/dist/rails-home.js +452 -0
  483. package/dist/rails-home.js.map +1 -0
  484. package/dist/rails-migrations.d.ts +75 -0
  485. package/dist/rails-migrations.d.ts.map +1 -0
  486. package/dist/rails-migrations.js +199 -0
  487. package/dist/rails-migrations.js.map +1 -0
  488. package/dist/rails-stores.d.ts +49 -0
  489. package/dist/rails-stores.d.ts.map +1 -0
  490. package/dist/rails-stores.js +100 -0
  491. package/dist/rails-stores.js.map +1 -0
  492. package/dist/reaper.d.ts +69 -0
  493. package/dist/reaper.d.ts.map +1 -0
  494. package/dist/reaper.js +152 -0
  495. package/dist/reaper.js.map +1 -0
  496. package/dist/record-store.d.ts +36 -0
  497. package/dist/record-store.d.ts.map +1 -0
  498. package/dist/record-store.js +78 -0
  499. package/dist/record-store.js.map +1 -0
  500. package/dist/recovery-io.d.ts +50 -0
  501. package/dist/recovery-io.d.ts.map +1 -0
  502. package/dist/recovery-io.js +122 -0
  503. package/dist/recovery-io.js.map +1 -0
  504. package/dist/recovery.d.ts +146 -0
  505. package/dist/recovery.d.ts.map +1 -0
  506. package/dist/recovery.js +110 -0
  507. package/dist/recovery.js.map +1 -0
  508. package/dist/redraw.d.ts +34 -0
  509. package/dist/redraw.d.ts.map +1 -0
  510. package/dist/redraw.js +62 -0
  511. package/dist/redraw.js.map +1 -0
  512. package/dist/rejected-evidence.d.ts +255 -0
  513. package/dist/rejected-evidence.d.ts.map +1 -0
  514. package/dist/rejected-evidence.js +324 -0
  515. package/dist/rejected-evidence.js.map +1 -0
  516. package/dist/report-cmd.d.ts +32 -0
  517. package/dist/report-cmd.d.ts.map +1 -0
  518. package/dist/report-cmd.js +201 -0
  519. package/dist/report-cmd.js.map +1 -0
  520. package/dist/repos-cmd.d.ts +80 -0
  521. package/dist/repos-cmd.d.ts.map +1 -0
  522. package/dist/repos-cmd.js +240 -0
  523. package/dist/repos-cmd.js.map +1 -0
  524. package/dist/request-body-shape.d.ts +93 -0
  525. package/dist/request-body-shape.d.ts.map +1 -0
  526. package/dist/request-body-shape.js +407 -0
  527. package/dist/request-body-shape.js.map +1 -0
  528. package/dist/requirement-claims.d.ts +89 -0
  529. package/dist/requirement-claims.d.ts.map +1 -0
  530. package/dist/requirement-claims.js +92 -0
  531. package/dist/requirement-claims.js.map +1 -0
  532. package/dist/retention-apply-runtime.d.ts +105 -0
  533. package/dist/retention-apply-runtime.d.ts.map +1 -0
  534. package/dist/retention-apply-runtime.js +269 -0
  535. package/dist/retention-apply-runtime.js.map +1 -0
  536. package/dist/reverify.d.ts +41 -0
  537. package/dist/reverify.d.ts.map +1 -0
  538. package/dist/reverify.js +69 -0
  539. package/dist/reverify.js.map +1 -0
  540. package/dist/round-records.d.ts +65 -0
  541. package/dist/round-records.d.ts.map +1 -0
  542. package/dist/round-records.js +61 -0
  543. package/dist/round-records.js.map +1 -0
  544. package/dist/round-runner.d.ts +924 -0
  545. package/dist/round-runner.d.ts.map +1 -0
  546. package/dist/round-runner.js +1693 -0
  547. package/dist/round-runner.js.map +1 -0
  548. package/dist/run-approval.d.ts +180 -0
  549. package/dist/run-approval.d.ts.map +1 -0
  550. package/dist/run-approval.js +199 -0
  551. package/dist/run-approval.js.map +1 -0
  552. package/dist/run-cmd.d.ts +359 -0
  553. package/dist/run-cmd.d.ts.map +1 -0
  554. package/dist/run-cmd.js +4418 -0
  555. package/dist/run-cmd.js.map +1 -0
  556. package/dist/run-delivery.d.ts +71 -0
  557. package/dist/run-delivery.d.ts.map +1 -0
  558. package/dist/run-delivery.js +104 -0
  559. package/dist/run-delivery.js.map +1 -0
  560. package/dist/run-ended.d.ts +75 -0
  561. package/dist/run-ended.d.ts.map +1 -0
  562. package/dist/run-ended.js +27 -0
  563. package/dist/run-ended.js.map +1 -0
  564. package/dist/run-ending-locator.d.ts +125 -0
  565. package/dist/run-ending-locator.d.ts.map +1 -0
  566. package/dist/run-ending-locator.js +133 -0
  567. package/dist/run-ending-locator.js.map +1 -0
  568. package/dist/run-launcher.d.ts +459 -0
  569. package/dist/run-launcher.d.ts.map +1 -0
  570. package/dist/run-launcher.js +652 -0
  571. package/dist/run-launcher.js.map +1 -0
  572. package/dist/run-lease.d.ts +208 -0
  573. package/dist/run-lease.d.ts.map +1 -0
  574. package/dist/run-lease.js +301 -0
  575. package/dist/run-lease.js.map +1 -0
  576. package/dist/run-refusal-marks.d.ts +189 -0
  577. package/dist/run-refusal-marks.d.ts.map +1 -0
  578. package/dist/run-refusal-marks.js +249 -0
  579. package/dist/run-refusal-marks.js.map +1 -0
  580. package/dist/run-start-marks.d.ts +153 -0
  581. package/dist/run-start-marks.d.ts.map +1 -0
  582. package/dist/run-start-marks.js +269 -0
  583. package/dist/run-start-marks.js.map +1 -0
  584. package/dist/scan-cmd.d.ts +14 -0
  585. package/dist/scan-cmd.d.ts.map +1 -0
  586. package/dist/scan-cmd.js +270 -0
  587. package/dist/scan-cmd.js.map +1 -0
  588. package/dist/scan-events.d.ts +50 -0
  589. package/dist/scan-events.d.ts.map +1 -0
  590. package/dist/scan-events.js +91 -0
  591. package/dist/scan-events.js.map +1 -0
  592. package/dist/scan.d.ts +191 -0
  593. package/dist/scan.d.ts.map +1 -0
  594. package/dist/scan.js +417 -0
  595. package/dist/scan.js.map +1 -0
  596. package/dist/scope-rule.d.ts +148 -0
  597. package/dist/scope-rule.d.ts.map +1 -0
  598. package/dist/scope-rule.js +121 -0
  599. package/dist/scope-rule.js.map +1 -0
  600. package/dist/self-repo-id.d.ts +18 -0
  601. package/dist/self-repo-id.d.ts.map +1 -0
  602. package/dist/self-repo-id.js +70 -0
  603. package/dist/self-repo-id.js.map +1 -0
  604. package/dist/serve-cmd.d.ts +115 -0
  605. package/dist/serve-cmd.d.ts.map +1 -0
  606. package/dist/serve-cmd.js +505 -0
  607. package/dist/serve-cmd.js.map +1 -0
  608. package/dist/serve.d.ts +167 -0
  609. package/dist/serve.d.ts.map +1 -0
  610. package/dist/serve.js +336 -0
  611. package/dist/serve.js.map +1 -0
  612. package/dist/server-client.d.ts +766 -0
  613. package/dist/server-client.d.ts.map +1 -0
  614. package/dist/server-client.js +475 -0
  615. package/dist/server-client.js.map +1 -0
  616. package/dist/session-adapter-runtime.d.ts +195 -0
  617. package/dist/session-adapter-runtime.d.ts.map +1 -0
  618. package/dist/session-adapter-runtime.js +373 -0
  619. package/dist/session-adapter-runtime.js.map +1 -0
  620. package/dist/session-checkpoint-runtime.d.ts +62 -0
  621. package/dist/session-checkpoint-runtime.d.ts.map +1 -0
  622. package/dist/session-checkpoint-runtime.js +268 -0
  623. package/dist/session-checkpoint-runtime.js.map +1 -0
  624. package/dist/session-control-transport.d.ts +106 -0
  625. package/dist/session-control-transport.d.ts.map +1 -0
  626. package/dist/session-control-transport.js +232 -0
  627. package/dist/session-control-transport.js.map +1 -0
  628. package/dist/session-observation-store.d.ts +98 -0
  629. package/dist/session-observation-store.d.ts.map +1 -0
  630. package/dist/session-observation-store.js +155 -0
  631. package/dist/session-observation-store.js.map +1 -0
  632. package/dist/session-observe-runtime.d.ts +123 -0
  633. package/dist/session-observe-runtime.d.ts.map +1 -0
  634. package/dist/session-observe-runtime.js +317 -0
  635. package/dist/session-observe-runtime.js.map +1 -0
  636. package/dist/session-store.d.ts +98 -0
  637. package/dist/session-store.d.ts.map +1 -0
  638. package/dist/session-store.js +116 -0
  639. package/dist/session-store.js.map +1 -0
  640. package/dist/sessions-cmd.d.ts +202 -0
  641. package/dist/sessions-cmd.d.ts.map +1 -0
  642. package/dist/sessions-cmd.js +609 -0
  643. package/dist/sessions-cmd.js.map +1 -0
  644. package/dist/settings-overlay.d.ts +53 -0
  645. package/dist/settings-overlay.d.ts.map +1 -0
  646. package/dist/settings-overlay.js +175 -0
  647. package/dist/settings-overlay.js.map +1 -0
  648. package/dist/setup-prompt.d.ts +34 -0
  649. package/dist/setup-prompt.d.ts.map +1 -0
  650. package/dist/setup-prompt.js +92 -0
  651. package/dist/setup-prompt.js.map +1 -0
  652. package/dist/setup.d.ts +103 -0
  653. package/dist/setup.d.ts.map +1 -0
  654. package/dist/setup.js +283 -0
  655. package/dist/setup.js.map +1 -0
  656. package/dist/shared-grants.d.ts +164 -0
  657. package/dist/shared-grants.d.ts.map +1 -0
  658. package/dist/shared-grants.js +242 -0
  659. package/dist/shared-grants.js.map +1 -0
  660. package/dist/signals.d.ts +55 -0
  661. package/dist/signals.d.ts.map +1 -0
  662. package/dist/signals.js +85 -0
  663. package/dist/signals.js.map +1 -0
  664. package/dist/source-cmd.d.ts +201 -0
  665. package/dist/source-cmd.d.ts.map +1 -0
  666. package/dist/source-cmd.js +684 -0
  667. package/dist/source-cmd.js.map +1 -0
  668. package/dist/source-verdict.d.ts +99 -0
  669. package/dist/source-verdict.d.ts.map +1 -0
  670. package/dist/source-verdict.js +182 -0
  671. package/dist/source-verdict.js.map +1 -0
  672. package/dist/spend-ledger.d.ts +350 -0
  673. package/dist/spend-ledger.d.ts.map +1 -0
  674. package/dist/spend-ledger.js +546 -0
  675. package/dist/spend-ledger.js.map +1 -0
  676. package/dist/stale-run-sweep.d.ts +79 -0
  677. package/dist/stale-run-sweep.d.ts.map +1 -0
  678. package/dist/stale-run-sweep.js +116 -0
  679. package/dist/stale-run-sweep.js.map +1 -0
  680. package/dist/start-attempts.d.ts +385 -0
  681. package/dist/start-attempts.d.ts.map +1 -0
  682. package/dist/start-attempts.js +613 -0
  683. package/dist/start-attempts.js.map +1 -0
  684. package/dist/status-cmd.d.ts +28 -0
  685. package/dist/status-cmd.d.ts.map +1 -0
  686. package/dist/status-cmd.js +116 -0
  687. package/dist/status-cmd.js.map +1 -0
  688. package/dist/stop-follow-up.d.ts +107 -0
  689. package/dist/stop-follow-up.d.ts.map +1 -0
  690. package/dist/stop-follow-up.js +172 -0
  691. package/dist/stop-follow-up.js.map +1 -0
  692. package/dist/surface-lease.d.ts +118 -0
  693. package/dist/surface-lease.d.ts.map +1 -0
  694. package/dist/surface-lease.js +485 -0
  695. package/dist/surface-lease.js.map +1 -0
  696. package/dist/sync.d.ts +54 -0
  697. package/dist/sync.d.ts.map +1 -0
  698. package/dist/sync.js +93 -0
  699. package/dist/sync.js.map +1 -0
  700. package/dist/syncing-ledger.d.ts +45 -0
  701. package/dist/syncing-ledger.d.ts.map +1 -0
  702. package/dist/syncing-ledger.js +53 -0
  703. package/dist/syncing-ledger.js.map +1 -0
  704. package/dist/temp-fixtures.d.ts +68 -0
  705. package/dist/temp-fixtures.d.ts.map +1 -0
  706. package/dist/temp-fixtures.js +38 -0
  707. package/dist/temp-fixtures.js.map +1 -0
  708. package/dist/token-window.d.ts +84 -0
  709. package/dist/token-window.d.ts.map +1 -0
  710. package/dist/token-window.js +143 -0
  711. package/dist/token-window.js.map +1 -0
  712. package/dist/tool-bridge.d.ts +177 -0
  713. package/dist/tool-bridge.d.ts.map +1 -0
  714. package/dist/tool-bridge.js +329 -0
  715. package/dist/tool-bridge.js.map +1 -0
  716. package/dist/tool-call-record.d.ts +197 -0
  717. package/dist/tool-call-record.d.ts.map +1 -0
  718. package/dist/tool-call-record.js +276 -0
  719. package/dist/tool-call-record.js.map +1 -0
  720. package/dist/tool-protocol.d.ts +135 -0
  721. package/dist/tool-protocol.d.ts.map +1 -0
  722. package/dist/tool-protocol.js +104 -0
  723. package/dist/tool-protocol.js.map +1 -0
  724. package/dist/tool-worker.d.ts +3 -0
  725. package/dist/tool-worker.d.ts.map +1 -0
  726. package/dist/tool-worker.js +478 -0
  727. package/dist/tool-worker.js.map +1 -0
  728. package/dist/toolchain-mirror.d.ts +86 -0
  729. package/dist/toolchain-mirror.d.ts.map +1 -0
  730. package/dist/toolchain-mirror.js +307 -0
  731. package/dist/toolchain-mirror.js.map +1 -0
  732. package/dist/trusted-git.d.ts +243 -0
  733. package/dist/trusted-git.d.ts.map +1 -0
  734. package/dist/trusted-git.js +920 -0
  735. package/dist/trusted-git.js.map +1 -0
  736. package/dist/ui.d.ts +76 -0
  737. package/dist/ui.d.ts.map +1 -0
  738. package/dist/ui.js +118 -0
  739. package/dist/ui.js.map +1 -0
  740. package/dist/untrusted-exec.d.ts +275 -0
  741. package/dist/untrusted-exec.d.ts.map +1 -0
  742. package/dist/untrusted-exec.js +479 -0
  743. package/dist/untrusted-exec.js.map +1 -0
  744. package/dist/untrusted-gate.d.ts +73 -0
  745. package/dist/untrusted-gate.d.ts.map +1 -0
  746. package/dist/untrusted-gate.js +226 -0
  747. package/dist/untrusted-gate.js.map +1 -0
  748. package/dist/uploader.d.ts +153 -0
  749. package/dist/uploader.d.ts.map +1 -0
  750. package/dist/uploader.js +204 -0
  751. package/dist/uploader.js.map +1 -0
  752. package/dist/verifier.d.ts +66 -0
  753. package/dist/verifier.d.ts.map +1 -0
  754. package/dist/verifier.js +170 -0
  755. package/dist/verifier.js.map +1 -0
  756. package/dist/verify-apply.d.ts +26 -0
  757. package/dist/verify-apply.d.ts.map +1 -0
  758. package/dist/verify-apply.js +36 -0
  759. package/dist/verify-apply.js.map +1 -0
  760. package/dist/verify-cmd.d.ts +44 -0
  761. package/dist/verify-cmd.d.ts.map +1 -0
  762. package/dist/verify-cmd.js +125 -0
  763. package/dist/verify-cmd.js.map +1 -0
  764. package/dist/verify-pass.d.ts +73 -0
  765. package/dist/verify-pass.d.ts.map +1 -0
  766. package/dist/verify-pass.js +103 -0
  767. package/dist/verify-pass.js.map +1 -0
  768. package/dist/welcome.d.ts +43 -0
  769. package/dist/welcome.d.ts.map +1 -0
  770. package/dist/welcome.js +116 -0
  771. package/dist/welcome.js.map +1 -0
  772. package/dist/workspace-provisioner.d.ts +65 -0
  773. package/dist/workspace-provisioner.d.ts.map +1 -0
  774. package/dist/workspace-provisioner.js +66 -0
  775. package/dist/workspace-provisioner.js.map +1 -0
  776. package/dist/workspace-quarantine.d.ts +64 -0
  777. package/dist/workspace-quarantine.d.ts.map +1 -0
  778. package/dist/workspace-quarantine.js +156 -0
  779. package/dist/workspace-quarantine.js.map +1 -0
  780. package/dist/workspace-residue-sweep.d.ts +113 -0
  781. package/dist/workspace-residue-sweep.d.ts.map +1 -0
  782. package/dist/workspace-residue-sweep.js +158 -0
  783. package/dist/workspace-residue-sweep.js.map +1 -0
  784. package/dist/workspace.d.ts +639 -0
  785. package/dist/workspace.d.ts.map +1 -0
  786. package/dist/workspace.js +2026 -0
  787. package/dist/workspace.js.map +1 -0
  788. package/package.json +52 -4
  789. package/win/CredProbe.cs +64 -0
  790. package/win/FakeClaude.cs +56 -0
  791. package/win/HandleProbe.cs +116 -0
  792. package/win/TokenProbe.cs +53 -0
  793. package/win/appcontainer.ps1 +500 -0
@@ -0,0 +1,900 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitDriver = exports.STATE_DIR = exports.GitError = void 0;
4
+ exports.executionStateDir = executionStateDir;
5
+ exports.projectStateDir = projectStateDir;
6
+ exports.nodeGitExec = nodeGitExec;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ const trusted_git_1 = require("./trusted-git");
11
+ const node_child_process_1 = require("node:child_process");
12
+ const core_1 = require("@looply/core");
13
+ class GitError extends Error {
14
+ }
15
+ exports.GitError = GitError;
16
+ /** The runner's own files; git must never stage them and never clean them. */
17
+ exports.STATE_DIR = '.looply';
18
+ /**
19
+ * THE EXECUTION'S OWN STATE — one directory per checkout, one writer.
20
+ *
21
+ * Everything whose correctness rests on there being a single writer lives here:
22
+ * the run lease, `queue.json`'s whole-file rewrite, `status.json`'s single-run
23
+ * shape, `events.jsonl`'s in-memory `seq` counter. Those are correct BECAUSE
24
+ * one run owns one checkout, and they stay correct when a second run happens in
25
+ * a different one.
26
+ */
27
+ function executionStateDir(cwd) {
28
+ return (0, node_path_1.join)(cwd, exports.STATE_DIR, 'state');
29
+ }
30
+ /**
31
+ * THE PROJECT'S STATE — one directory per repository, however many worktrees.
32
+ *
33
+ * 🔴 A LINKED WORKTREE IS THE SAME PROJECT, and the state whose invariant says
34
+ * "per project" must not follow the checkout. Otherwise isolating executions
35
+ * silently un-does safety rather than adding it:
36
+ *
37
+ * loop-safety history keyed by the state directory and the pursuit it
38
+ * watches, and the path half exists only as where the
39
+ * file sits — so a second worktree is a FRESH
40
+ * history. Both detectors
41
+ * need three observations and each run contributes
42
+ * one, so with a worktree per run they can never
43
+ * fire at all.
44
+ * exemption spend ledger single-use holds only because there is one
45
+ * `exemptions.json` per checkout. N checkouts make
46
+ * one granted row spendable N times — a rails bypass,
47
+ * not a bookkeeping detail.
48
+ * control.json the ONE channel the daemon, the run and the
49
+ * controller share about whether a person said stop.
50
+ * Split it and the stop button reaches one run.
51
+ * goals the daemon is the sole writer; a run reads.
52
+ *
53
+ * ⚠️ IDENTICAL TO `executionStateDir` IN THE ORDINARY CASE. In a normal checkout
54
+ * the main worktree IS the cwd, so both answer the same path and Simple Mode is
55
+ * byte-identical. They diverge only inside a linked worktree.
56
+ *
57
+ * ⚠️ FALLS BACK TO THE EXECUTION DIR when there is no repository or its metadata
58
+ * cannot be read — the pre-worktree behaviour, unchanged. `repoConfigTroubleAt`
59
+ * is the check that refuses on unreadable metadata; this is not that check.
60
+ *
61
+ * 🔴 AND THAT FALLBACK USED TO SWALLOW A CASE IT SHOULD NOT HAVE. A repository
62
+ * can have several worktrees and no main one — `clone --bare` plus two
63
+ * `worktree add`s — and there `mainWorktreeRoot` is null while the sharing is
64
+ * still required. The old code read null as "nothing to share" and put the
65
+ * project's state inside each checkout: a fresh loop-safety history per
66
+ * checkout, and one single-use exemption spendable once per checkout, with
67
+ * nothing said about it.
68
+ *
69
+ * For those, the shared state goes under `~/.looply/projects/<key>`, keyed by
70
+ * the common dir — the one identity every worktree of that repository agrees
71
+ * on. `~/.looply` is where this codebase already puts state that must survive
72
+ * the repository, and a bare repository has no tree to put it in anyway.
73
+ *
74
+ * ⚠️ AN ORDINARY CHECKOUT IS UNAFFECTED, and that is load-bearing for
75
+ * compatibility: `linkedRepoCommonDir` answers null unless the checkout is
76
+ * LINKED, so a single-worktree repository — bare or not — keeps the exact path
77
+ * it has always used and no existing state is orphaned.
78
+ */
79
+ function projectStateDir(cwd) {
80
+ /**
81
+ * ⚠️ A SUBDIRECTORY ANSWERS ITS REPOSITORY'S STATE, and a narrowing that made
82
+ * it answer its own was reverted — see `repoIdentityPath` for the measurement.
83
+ *
84
+ * In short: `looply stop` and `looply runs` are the only commands a person
85
+ * types from inside a tree, they are not guarded by the repository-root check,
86
+ * and a subdirectory that answers `<subdir>/.looply/state` makes the brake
87
+ * write a file no run will ever read while reporting "no run is going".
88
+ */
89
+ const root = (0, trusted_git_1.mainWorktreeRoot)(cwd);
90
+ if (root !== null)
91
+ return (0, node_path_1.join)(root, exports.STATE_DIR, 'state');
92
+ const shared = (0, trusted_git_1.linkedRepoCommonDir)(cwd);
93
+ if (shared !== null) {
94
+ const key = (0, node_crypto_1.createHash)('sha256').update(shared.toLowerCase(), 'utf8').digest('hex').slice(0, 16);
95
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), '.looply', 'projects', key, 'state');
96
+ }
97
+ return executionStateDir(cwd);
98
+ }
99
+ function isStatePath(path) {
100
+ return path === exports.STATE_DIR || path.startsWith(`${exports.STATE_DIR}/`);
101
+ }
102
+ /**
103
+ * The loop's own writes: the ledger, the queue, the projection, the control
104
+ * file. These churn constantly while a run is going and are nobody's edit.
105
+ *
106
+ * Deliberately narrower than {@link isStatePath}. Everything else under
107
+ * `.looply/` — above all `config.yaml`, which is tracked and holds the rails —
108
+ * is a real change that the interlock and the dirty-tree check must both see.
109
+ */
110
+ const RUNTIME_STATE = `${exports.STATE_DIR}/state`;
111
+ function isRuntimeState(path) {
112
+ return path === RUNTIME_STATE || path.startsWith(`${RUNTIME_STATE}/`);
113
+ }
114
+ class GitDriver {
115
+ exec;
116
+ constructor(exec) {
117
+ this.exec = exec;
118
+ }
119
+ async run(args) {
120
+ const r = await this.exec(args);
121
+ if (r.code !== 0) {
122
+ throw new GitError(`git ${args.join(' ')} exited ${r.code}: ${r.stderr.trim()}`);
123
+ }
124
+ return r.stdout;
125
+ }
126
+ /**
127
+ * The pathspec that keeps `.looply` out of a stage — WHEN NAMING IT IS LEGAL.
128
+ *
129
+ * 🔴 `:(exclude).looply` IS A REFUSAL WHEN `.looply` IS ALREADY IGNORED, and
130
+ * that broke evidence gathering on every round of any project that gitignores
131
+ * it. Git rejects a pathspec that EXPLICITLY names an ignored path, and an
132
+ * exclusion names it:
133
+ *
134
+ * .looply not in .gitignore git add -N -- . :(exclude).looply exit 0
135
+ * .looply in .gitignore same command exit 1
136
+ * "The following paths are ignored by one of your .gitignore files"
137
+ *
138
+ * `advice.addIgnoredFile=false` only hides the hint; the exit code stays 1.
139
+ *
140
+ * ⚠️ AND IT IS A CATCH-22 WITHOUT THIS. Leave `.looply/` untracked and the run
141
+ * refuses to start on a dirty tree; gitignore it — the obvious fix, and the
142
+ * one a person reaches for — and every round's evidence fails instead.
143
+ * Measured live: a round whose gate was green and whose proof said `proven`
144
+ * recorded `evidence could not be gathered`.
145
+ *
146
+ * When the path is already ignored the exclusion is not merely illegal, it is
147
+ * REDUNDANT: git skips ignored paths under a `.` pathspec anyway. So the
148
+ * answer is to ask, and to drop it exactly when git is already doing the job.
149
+ *
150
+ * ⚠️ ASKED EVERY TIME, NOT MEMOISED. A round may edit `.gitignore`, and the
151
+ * direction that goes wrong is the one a cache would get wrong: believing
152
+ * `.looply` is ignored when it is no longer ignored stages the state
153
+ * directory into somebody's commit. One `check-ignore` is a few milliseconds.
154
+ *
155
+ * `check-ignore` only reads `.gitignore` and prints; it is in the same
156
+ * READ-ONLY family as `status` and `rev-parse`, and none of the forbidden
157
+ * verbs.
158
+ */
159
+ async keepStateOut() {
160
+ // exit 0 = ignored (git already excludes it, and naming it would refuse)
161
+ // exit 1 = not ignored, including absent (the exclusion is needed)
162
+ const asked = await this.exec(['check-ignore', '-q', '--', exports.STATE_DIR]);
163
+ return asked.code === 0 ? [] : [`:(exclude)${exports.STATE_DIR}`];
164
+ }
165
+ /** Create and switch to `name`. An existing branch of that name is a refusal. */
166
+ async createBranch(name, from) {
167
+ const probe = await this.exec(['rev-parse', '--verify', '--quiet', `refs/heads/${name}`]);
168
+ if (probe.code === 0) {
169
+ // Reusing a branch would land this round's commits on top of whatever a
170
+ // previous (possibly aborted) run left there (DESIGN §12 case 2).
171
+ throw new GitError(`branch already exists: ${name} — refusing to reuse it`);
172
+ }
173
+ /**
174
+ * ⚠️ `from` IS PHASE 5's ANSWER TO STACKING, AND ABSENT KEEPS THE OLD SHAPE.
175
+ *
176
+ * Run N+1 used to be cut from wherever HEAD was — which, after run N, is run
177
+ * N's branch. One ever-deepening chain with no shared base. With integration
178
+ * on, the caller names the integration head instead. With it off nothing is
179
+ * passed and this is byte-identical to what it has always done, which is the
180
+ * whole compatibility guarantee of the phase.
181
+ */
182
+ await this.run(from === undefined ? ['switch', '-c', name] : ['switch', '-c', name, from]);
183
+ }
184
+ /** The sha of HEAD — recorded as the run's base before any round writes. */
185
+ async headSha() {
186
+ return (await this.run(['rev-parse', 'HEAD'])).trim();
187
+ }
188
+ /**
189
+ * THE FIRST CONVERGENCE VERBS THIS DRIVER HAS EVER ISSUED.
190
+ *
191
+ * Until Phase 5 the complete list was add, cat-file, clean, commit, ls-tree,
192
+ * push, reset, rev-list, rev-parse, status, switch, worktree — no fetch, no
193
+ * merge, no rebase, no cherry-pick, no revert, no stash, no checkout, no tag,
194
+ * no remote, no branch -d, no notes, no gc, no bisect, no submodule. That was
195
+ * not an oversight; it was the whole safety argument, and it is why the three
196
+ * added here are the three that only ever READ or FAST-FORWARD.
197
+ *
198
+ * ⚠️ THERE IS STILL NO `merge`, NO `rebase` AND NO `cherry-pick`. Integration
199
+ * is a fast-forward or it is a refusal. A merge that had to combine two
200
+ * histories is the thing that can silently drop somebody's line, and
201
+ * "auto-resolving a merge conflict" is listed under **Never**.
202
+ */
203
+ async mergeBase(a, b) {
204
+ try {
205
+ return (await this.run(['merge-base', a, b])).trim() || null;
206
+ }
207
+ catch {
208
+ // No common ancestor is a real answer, not an error: two histories that
209
+ // share nothing cannot be reconciled by any verb this driver may issue.
210
+ return null;
211
+ }
212
+ }
213
+ /**
214
+ * How far apart two refs are, counted rather than assumed.
215
+ *
216
+ * `--left-right --count` answers both directions in one call, so the two
217
+ * numbers are read at the SAME moment — asking twice would let a ref move in
218
+ * between and produce a pair that never simultaneously held.
219
+ */
220
+ async divergence(mine, theirs) {
221
+ const out = (await this.run(['rev-list', '--left-right', '--count', `${theirs}...${mine}`])).trim();
222
+ const [behind, ahead] = out.split(/\s+/).map((n) => Number.parseInt(n, 10));
223
+ /**
224
+ * 🔴 UNPARSEABLE OUTPUT IS NOT ZERO DIVERGENCE.
225
+ *
226
+ * `Number.isInteger(x) ? x : 0` reads as a tidy default and is a fail-open:
227
+ * the consumer is `baseFreshness`, and `{ahead: 0, behind: 0}` is its word
228
+ * for "the base is current". So a git that answered anything this could not
229
+ * parse — a warning line, a locale-translated message, an empty string from
230
+ * a broken invocation — told the run its ground had not moved.
231
+ *
232
+ * There is no number here that can mean "I could not tell", so this throws
233
+ * and lets the caller decide. `baseFreshness` refuses on it, which is the
234
+ * direction a staleness check has to fail in.
235
+ */
236
+ if (!Number.isInteger(ahead) || !Number.isInteger(behind)) {
237
+ throw new Error(`git could not say how ${mine} and ${theirs} diverge — rev-list answered ${JSON.stringify(out)}`);
238
+ }
239
+ return { ahead: ahead, behind: behind };
240
+ }
241
+ /**
242
+ * CREATE OR ADVANCE ONE OF LOOPLY'S OWN REFS — AND ONLY BY COMPARE-AND-SWAP.
243
+ *
244
+ * 🔴 THE FOURTH CONVERGENCE VERB, AND THE ONLY ONE THAT WRITES. Phase 5's
245
+ * integration queue has to move `looply/integration` forward, and until now
246
+ * this driver could not move a ref at all. What it must NOT become is a
247
+ * general `update-ref`: that is one typo away from moving `main`.
248
+ *
249
+ * Three narrowings, all of them enforced here rather than by convention:
250
+ *
251
+ * 1. THE NAME MUST BE LOOPLY'S. Anything not under `refs/heads/looply/` is
252
+ * refused before git is asked. A person's branch is theirs, and `main`
253
+ * is nobody's to move from here.
254
+ * 2. IT IS A COMPARE-AND-SWAP, ALWAYS. `git update-ref <ref> <new> <old>`
255
+ * fails if the ref is not still `old`, so a base that moved between
256
+ * validation and publication cannot be overwritten — git itself refuses,
257
+ * atomically, without this code having to re-read and hope. Creating a
258
+ * ref passes the all-zero oid as `old`, which is git's own way of saying
259
+ * "and it must not exist yet".
260
+ * 3. FAST-FORWARD ONLY. The caller must have established that `newSha` has
261
+ * `oldSha` as an ancestor; there is still no `merge`, `rebase` or
262
+ * `cherry-pick` in this file, so nothing here can combine two histories.
263
+ *
264
+ * ⚠️ GIT'S REFERENCE API, NOT A FILE WRITE. Writing `.git/refs/...` by hand
265
+ * would miss packed refs, the reflog and the lock — and would be a second,
266
+ * worse implementation of the compare-and-swap that is the whole point.
267
+ *
268
+ * Answers `false` when the swap was refused (the ref moved, or already
269
+ * existed); throws only when git could not be asked at all.
270
+ */
271
+ async compareAndSetRef(ref, newSha, oldSha) {
272
+ if (!/^looply\/[A-Za-z0-9._\-\/]+$/.test(ref)) {
273
+ throw new GitError(`refusing to move ${ref}: this driver may only create or advance looply's own refs`);
274
+ }
275
+ if (!/^[0-9a-f]{7,40}$/.test(newSha)) {
276
+ throw new GitError(`refusing to move ${ref}: ${newSha} is not a commit id`);
277
+ }
278
+ if (oldSha !== null && !/^[0-9a-f]{7,40}$/.test(oldSha)) {
279
+ throw new GitError(`refusing to move ${ref}: ${oldSha} is not a commit id`);
280
+ }
281
+ // The all-zero oid is git's own "it must not exist yet".
282
+ const expected = oldSha ?? '0000000000000000000000000000000000000000';
283
+ const out = await this.exec(['update-ref', `refs/heads/${ref}`, newSha, expected]);
284
+ return out.code === 0;
285
+ }
286
+ /** Local branches under a prefix, with their tips — the finished runs the lane may be offered (M5-1). */
287
+ async listBranches(prefix) {
288
+ const out = await this.run(['for-each-ref', '--format=%(refname:short) %(objectname)', `refs/heads/${prefix}*`]);
289
+ const found = [];
290
+ for (const line of out.split(/\r?\n/)) {
291
+ const [name, sha] = line.trim().split(' ');
292
+ if (name !== undefined && sha !== undefined && name.startsWith(prefix) && /^[0-9a-f]{40}$/.test(sha))
293
+ found.push({ name, sha });
294
+ }
295
+ return found;
296
+ }
297
+ /** Whether a ref exists at all. Integration may not have been cut yet. */
298
+ async refExists(ref) {
299
+ try {
300
+ await this.run(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]);
301
+ return true;
302
+ }
303
+ catch {
304
+ return false;
305
+ }
306
+ }
307
+ /**
308
+ * WHAT THE REPOSITORY CONTAINS, as distinct from which commit produced it.
309
+ *
310
+ * ─────────────────────────────────────────────────────────────────────────
311
+ * A COMMIT SHA IS THE WRONG IDENTITY FOR "IS THIS THE SAME STATE AGAIN"
312
+ *
313
+ * A commit id covers the parent, the author, the message and the timestamp,
314
+ * so it differs on every commit even when the content is byte-identical.
315
+ * Loop safety needs to notice a pursuit moving A → B → A → B, and with commit
316
+ * ids that reads as four distinct states and no cycle is ever detectable.
317
+ *
318
+ * A tree id is the content. The same files reached twice by two different
319
+ * commits are ONE state, which is exactly the equality the detector needs.
320
+ *
321
+ * ⚠️ IT DESCRIBES THE COMMITTED TREE, NOT THE WORKING ONE. Uncommitted edits
322
+ * do not move it — the same limit `headSha` has. That is the honest reading
323
+ * for a Round that commits its work; a caller comparing dirty trees would
324
+ * need something else, and inventing one here would mean writing to the index
325
+ * (see `diff()`, which is not read-only despite its name).
326
+ *
327
+ * READ-ONLY. `rev-parse` resolves; it changes nothing.
328
+ */
329
+ async headTreeSha() {
330
+ return (await this.run(['rev-parse', 'HEAD^{tree}'])).trim();
331
+ }
332
+ /**
333
+ * The tree a ref or commit points at — `headTreeSha` for any ref (F25: the lane
334
+ * judges coverage on the integration tip's tree). READ-ONLY, the same `rev-parse`.
335
+ */
336
+ async treeOf(ref) {
337
+ return (await this.run(['rev-parse', `${ref}^{tree}`])).trim();
338
+ }
339
+ /**
340
+ * Whether `ancestor` is in `of`'s history (a commit is its own ancestor) — F31. READ-ONLY:
341
+ * `merge-base --is-ancestor` answers by exit code, 0 yes and 1 no; anything else is a fault.
342
+ */
343
+ async isAncestor(ancestor, of) {
344
+ const r = await this.exec(['merge-base', '--is-ancestor', ancestor, of]);
345
+ if (r.code === 0)
346
+ return true;
347
+ if (r.code === 1)
348
+ return false;
349
+ throw new GitError(`merge-base --is-ancestor ${ancestor} ${of} failed: ${r.stderr.trim()}`);
350
+ }
351
+ /** The commit a ref points at (F27). READ-ONLY, the same `rev-parse`. */
352
+ async commitOf(ref) {
353
+ return (await this.run(['rev-parse', '--verify', `${ref}^{commit}`])).trim();
354
+ }
355
+ /**
356
+ * Put HEAD on `sha`, detached — the tree a goal-met Start must judge (F27).
357
+ *
358
+ * ⚠️ `switch`, the verb this driver already uses, and never forced: `switch --detach`
359
+ * refuses when local changes would be lost, and the caller has already found the tree clean.
360
+ */
361
+ async switchDetached(sha) {
362
+ // F33: `--no-overwrite-ignore` — git overwrites IGNORED files by default, and the caller's
363
+ // clean-tree check cannot see them.
364
+ await this.run(['switch', '--detach', '--no-overwrite-ignore', sha]);
365
+ }
366
+ /**
367
+ * The branch HEAD is on, or `(detached)` when it is not on one.
368
+ *
369
+ * Recorded in the run lease as the branch the run was cut from. Nothing
370
+ * returns to it — run branches stack — so knowing where a run started is the
371
+ * only way a later process can tell an ordinary stacked branch from a tree
372
+ * somebody has since moved somewhere else entirely.
373
+ */
374
+ /**
375
+ * Every tracked file at a revision, with its blob sha and size.
376
+ *
377
+ * 🔴 ENUMERATED FROM THE GIT INDEX, NEVER FROM THE FILESYSTEM, AND THAT ONE
378
+ * DECISION SETTLES FOUR SEPARATE PROBLEMS THE AUDIT FOUND.
379
+ *
380
+ * ignore rules git has already applied `.gitignore`. Nothing in this
381
+ * repository reads that file, and the one production skip
382
+ * list is three hardcoded basenames that would happily walk
383
+ * `dist/` and `coverage/` — build output that is by
384
+ * construction different on every machine.
385
+ * symlink escape the existing physical-path defence is a NAMED-PATH
386
+ * defence: it says nothing about an enumeration, so a
387
+ * directory walk inherits none of it. The git index cannot
388
+ * contain an entry outside the repository, so the escape is
389
+ * impossible here rather than guarded against.
390
+ * ordering `ls-tree` output is deterministic, unlike `readdir`.
391
+ * portability paths are already project-relative.
392
+ *
393
+ * `-r` recurses into subtrees; `-z` because a path may contain anything a
394
+ * newline could, and `-l` adds the size git already knows so nothing has to
395
+ * stat the working tree.
396
+ */
397
+ async listTree(treeIsh) {
398
+ const out = await this.run(['ls-tree', '-r', '-l', '-z', treeIsh]);
399
+ const entries = [];
400
+ for (const record of out.split('\0')) {
401
+ if (record === '')
402
+ continue;
403
+ // `<mode> SP <type> SP <sha> SP* <size> TAB <path>`
404
+ const tab = record.indexOf('\t');
405
+ if (tab < 0)
406
+ continue;
407
+ const meta = record.slice(0, tab).split(/\s+/);
408
+ const path = record.slice(tab + 1);
409
+ if (meta.length < 4)
410
+ continue;
411
+ const [, type, blobSha, size] = meta;
412
+ // Only blobs. A submodule is a `commit` entry and has no content here.
413
+ if (type !== 'blob' || blobSha === undefined || path === '')
414
+ continue;
415
+ const bytes = Number(size);
416
+ entries.push({ path, blobSha, bytes: Number.isFinite(bytes) ? bytes : 0 });
417
+ }
418
+ return entries;
419
+ }
420
+ /**
421
+ * The COMMITTED content of one blob.
422
+ *
423
+ * ⚠️ THE COMMITTED CONTENT, NOT THE WORKING TREE'S. An index describes a
424
+ * revision; reading the file from disk would describe whatever somebody has
425
+ * half-edited, and two machines would then disagree about a revision they
426
+ * both have.
427
+ */
428
+ async readBlob(blobSha) {
429
+ return this.run(['cat-file', 'blob', blobSha]);
430
+ }
431
+ async currentBranch() {
432
+ const r = await this.exec(['rev-parse', '--abbrev-ref', 'HEAD']);
433
+ if (r.code !== 0)
434
+ return '(unknown)';
435
+ const name = r.stdout.trim();
436
+ return name === 'HEAD' || name === '' ? '(detached)' : name;
437
+ }
438
+ /**
439
+ * Working-tree paths that differ from HEAD, with `.looply/**` filtered out —
440
+ * the runner's own state writes must never read as "the agent changed
441
+ * something", or the no-op branch becomes unreachable (DESIGN §2.4).
442
+ */
443
+ async changedPaths() {
444
+ return (await this.changedEntries()).map((e) => e.path);
445
+ }
446
+ /**
447
+ * Every changed path WITH what happened to it, as `git status` reports it.
448
+ *
449
+ * The manifest a rejected candidate is retained with. `changedPaths` is the
450
+ * same list with the status dropped, and the two share one parse so they
451
+ * cannot disagree about which paths a round touched. `status` is the
452
+ * two-character porcelain code (`??` new, ` M` modified, `R ` renamed…).
453
+ */
454
+ async changedEntries() {
455
+ // -z: NUL separators, no quoting — a path with a space or a non-ASCII byte
456
+ // arrives literal instead of C-escaped.
457
+ // -uall lists every untracked file. The default collapses a new directory
458
+ // to `newdir/`, so a railed file created inside one was invisible to the
459
+ // interlock — it never saw a path it could match against.
460
+ const out = await this.run(['status', '--porcelain=v1', '-z', '-uall']);
461
+ const tokens = out.split('\0').filter((t) => t.length > 0);
462
+ const entries = [];
463
+ for (let i = 0; i < tokens.length; i++) {
464
+ const entry = tokens[i];
465
+ if (entry === undefined || entry.length < 4)
466
+ continue;
467
+ const status = entry.slice(0, 2);
468
+ entries.push({ path: entry.slice(3), status });
469
+ if (entry.startsWith('R') || entry.startsWith('C')) {
470
+ // Rename/copy entries carry the source as the next token. Both ends
471
+ // changed as far as `when:` filters and rails are concerned.
472
+ i += 1;
473
+ const source = tokens[i];
474
+ if (source !== undefined)
475
+ entries.push({ path: source, status });
476
+ }
477
+ }
478
+ // Only the loop's own runtime writes are filtered. This used to drop
479
+ // everything under `.looply/`, and that list is the interlock's only input
480
+ // — so a rail naming `.looply/` could never fire, and a round was free to
481
+ // edit the config that constrains it: weaken forbidden_paths, drop a
482
+ // `required: true`, commit its legitimate work alongside, and the next run
483
+ // re-reads the weakened file. Nothing recorded that it happened.
484
+ return entries.filter((e) => !isRuntimeState(e.path));
485
+ }
486
+ /**
487
+ * Commits in `fromSha..toSha` that touch any of `paths` — the input to the
488
+ * re-verification trigger (DESIGN §12 case 19). Zero paths short-circuits to
489
+ * 0 without calling git: `rev-list --count` with an empty pathspec counts
490
+ * *every* commit, which would read as "everything touched the evidence".
491
+ */
492
+ async countCommitsTouching(fromSha, toSha, paths) {
493
+ if (paths.length === 0)
494
+ return 0;
495
+ const out = await this.run(['rev-list', '--count', `${fromSha}..${toSha}`, '--', ...paths]);
496
+ const n = Number.parseInt(out.trim(), 10);
497
+ if (Number.isNaN(n)) {
498
+ throw new GitError(`rev-list --count returned non-numeric output: ${JSON.stringify(out.trim())}`);
499
+ }
500
+ return n;
501
+ }
502
+ /** `add -A`, with the state directory excluded by pathspec. */
503
+ /**
504
+ * The round's changes as a unified diff, for the prover.
505
+ *
506
+ * Staged and unstaged both, because the round is inspected before anything is
507
+ * committed. State and run logs are excluded by the same pathspec the commit
508
+ * uses — a prover reading its own run's ledger would be grading the story.
509
+ */
510
+ /**
511
+ * Create a linked worktree at `path`, on a NEW branch cut from `baseSha`.
512
+ *
513
+ * ⚠️ THROUGH THE SAME `exec` AS EVERY OTHER VERB, deliberately. A worktree
514
+ * provisioner with its own git runner would be a second "safe enough" path
515
+ * around the config check, the repository-root check, the rebuilt `GIT_`
516
+ * namespace and the absolute binary resolution — all four of which exist
517
+ * because the repository is untrusted input.
518
+ *
519
+ * ⚠️ A NEW BRANCH, NEVER AN EXISTING ONE. `-b` fails if the branch is already
520
+ * there, which is the same refusal `createBranch` makes and for the same
521
+ * reason: reusing a branch would stack this execution's commits on whatever a
522
+ * previous one left behind. Linked worktrees SHARE the ref namespace, so this
523
+ * is the check that makes two simultaneous executions safe to start.
524
+ */
525
+ async addWorktree(path, branch, baseSha) {
526
+ await this.run(['worktree', 'add', '-b', branch, path, baseSha]);
527
+ }
528
+ /**
529
+ * Every worktree of this repository: its path, its HEAD, its branch.
530
+ *
531
+ * `--porcelain` because the human-readable form is not parseable — a path may
532
+ * contain spaces, and the columns are aligned rather than delimited.
533
+ */
534
+ async listWorktrees() {
535
+ const out = await this.run(['worktree', 'list', '--porcelain']);
536
+ const found = [];
537
+ let cur = null;
538
+ for (const line of out.split(/\r?\n/)) {
539
+ if (line.startsWith('worktree ')) {
540
+ if (cur !== null)
541
+ found.push(cur);
542
+ cur = { path: line.slice('worktree '.length), head: '', branch: null };
543
+ continue;
544
+ }
545
+ if (cur === null)
546
+ continue;
547
+ if (line.startsWith('HEAD '))
548
+ cur.head = line.slice('HEAD '.length);
549
+ else if (line.startsWith('branch '))
550
+ cur.branch = line.slice('branch '.length);
551
+ }
552
+ if (cur !== null)
553
+ found.push(cur);
554
+ return found;
555
+ }
556
+ /**
557
+ * Remove a linked worktree.
558
+ *
559
+ * 🔴 NEVER `--force`, AND THIS IS NOT A STYLE CHOICE. `git worktree remove
560
+ * --force` deletes IGNORED files — `node_modules`, `.env` — which `discard()`
561
+ * one method below deliberately preserves by omitting `-x` from `clean`. It
562
+ * also fires exactly when git has already refused the safe form, which is to
563
+ * say exactly when something is there that git thought worth refusing over.
564
+ *
565
+ * So git's own refusal IS the check. A caller that cannot remove a worktree
566
+ * should leave it and say so, not insist.
567
+ */
568
+ async removeWorktree(path) {
569
+ await this.run(['worktree', 'remove', path]);
570
+ }
571
+ /**
572
+ * Drop the administrative records of worktrees whose directories are gone.
573
+ *
574
+ * Touches no working tree — it only reconciles `.git/worktrees/*` with what is
575
+ * actually on disk, which is what makes a directory that a person deleted by
576
+ * hand stop counting against the ref namespace.
577
+ */
578
+ async pruneWorktrees() {
579
+ await this.run(['worktree', 'prune']);
580
+ }
581
+ /**
582
+ * Everything THIS checkout holds that a pristine one would not — INCLUDING
583
+ * ignored files.
584
+ *
585
+ * ⚠️ `--ignored` IS THE WHOLE POINT. Without it this asks exactly the question
586
+ * `git worktree remove` already answers by refusing, and learns nothing new.
587
+ * With it, it sees the `node_modules` and the `.env` that `remove` deletes
588
+ * silently.
589
+ *
590
+ * 🔴 AND IT ANSWERS ABOUT *THIS* DRIVER'S CHECKOUT — the first version took a
591
+ * `workspace` and passed `-C <workspace>`, which does nothing here.
592
+ * `trustedGitEnv` pins GIT_DIR and GIT_WORK_TREE from the cwd the driver was
593
+ * built with, and a pinned work tree beats `-C`. Measured: it reported the
594
+ * MAIN checkout's residue while claiming to describe a workspace — so a dirty
595
+ * main checkout would have blocked every reap, and a clean one would have
596
+ * waved through a workspace full of files. To ask about another checkout,
597
+ * build a driver for it.
598
+ */
599
+ async statusWithIgnored() {
600
+ /**
601
+ * ⚠️ `-uall` AND `--ignored=matching`, SO EVERY PATH IS NAMED INDIVIDUALLY.
602
+ *
603
+ * 🔴 Plain `--porcelain` COLLAPSES an entirely-untracked directory to one
604
+ * entry: `?? .looply/`, never `?? .looply/state/creating.json`. Any caller
605
+ * that wants to reason about WHICH files are there — to ignore looply's own
606
+ * state while still seeing a person's — cannot, because the person's file
607
+ * and looply's are hidden behind the same single line. Measured: the reaper's
608
+ * residue filter matched nothing at all because of it.
609
+ */
610
+ return this.run(['status', '--porcelain', '-uall', '--ignored=matching']);
611
+ }
612
+ async diff(maxBytes = 200_000) {
613
+ const p = await this.patch(maxBytes);
614
+ if (!p.truncated)
615
+ return p.text;
616
+ // Truncation is announced rather than silent: a prover judging a diff it
617
+ // only partly received must know that is what happened.
618
+ return `${p.text}\n\n[diff truncated at ${maxBytes} bytes]\n`;
619
+ }
620
+ /**
621
+ * The working tree's change against HEAD as one applicable patch — the
622
+ * artifact a rejected candidate is retained as, before `discard()` resets it.
623
+ *
624
+ * `diff()` is this with the prover's own truncation notice; the two share the
625
+ * implementation so the patch a repair restores and the diff a prover judged
626
+ * are the same text. `truncated` is a FLAG here rather than a notice in the
627
+ * text, because a truncated patch must never be applied: `git apply` would
628
+ * either refuse it or, worse, apply the part it could read.
629
+ */
630
+ async patch(maxBytes) {
631
+ // Intent-to-add first, so a file the round CREATED appears in the diff.
632
+ // `git diff HEAD` is tracked-only, and stageAll does not run until after
633
+ // the prover has answered — so an additive round handed the prover an
634
+ // empty string under the instruction "if a required behaviour is not
635
+ // visible in the diff, it does not count". It rejected work it was never
636
+ // shown, and since "add a spec"/"add a guard" is the commonest shape a
637
+ // scan produces, that was the default path for most overnight runs.
638
+ //
639
+ // -N records intent only: no content is staged, and both discard (which
640
+ // resets) and commit (which stages properly) are unaffected.
641
+ await this.run(['add', '-N', '--', '.', ...(await this.keepStateOut())]);
642
+ const out = await this.run([
643
+ 'diff',
644
+ 'HEAD',
645
+ '--',
646
+ '.',
647
+ `:!${exports.STATE_DIR}`,
648
+ ]);
649
+ if (out.length <= maxBytes)
650
+ return { text: out, bytes: out.length, truncated: false };
651
+ return { text: out.slice(0, maxBytes), bytes: out.length, truncated: true };
652
+ }
653
+ /**
654
+ * The change one commit range makes, as one applicable patch — the same
655
+ * artifact `patch()` produces for a working tree, asked about history
656
+ * instead.
657
+ *
658
+ * 🔴 THIS IS WHAT A REPLAY CARRIES, and it is deliberately a PATCH rather
659
+ * than a range of commits. Carrying commits would mean replaying their
660
+ * identities onto a new base, which is the rewrite `integration.spec.ts`
661
+ * forbids; carrying the change means a NEW commit that must earn its own
662
+ * proof. The distinction is the whole reason a replay is not a rebase.
663
+ *
664
+ * ⚠️ `truncated` is a flag, never a notice in the text: a truncated patch
665
+ * must never be applied, because `git apply` would either refuse it or,
666
+ * worse, apply the part it could read.
667
+ */
668
+ async patchBetween(fromSha, toSha, maxBytes) {
669
+ const out = await this.run(['diff', fromSha, toSha, '--', '.', `:!${exports.STATE_DIR}`]);
670
+ if (out.length <= maxBytes)
671
+ return { text: out, bytes: out.length, truncated: false };
672
+ return { text: out.slice(0, maxBytes), bytes: out.length, truncated: true };
673
+ }
674
+ /**
675
+ * The paths a commit range touched — what the replay fold is asked about.
676
+ *
677
+ * ⚠️ The state directory is excluded for the same reason it is excluded from
678
+ * the patch: it is looply's own bookkeeping, not the candidate's work, and a
679
+ * replay that carried it would carry one run's ledger onto another's base.
680
+ */
681
+ async changedPathsBetween(fromSha, toSha) {
682
+ // F31 (soun-5/corr-8): `--no-renames`, so a rename names BOTH paths. With rename detection a
683
+ // moved file reported only its new path, and a replay that overlapped the old one read as disjoint.
684
+ const out = await this.run(['diff', '--name-only', '--no-renames', fromSha, toSha, '--', '.', `:!${exports.STATE_DIR}`]);
685
+ return out
686
+ .split('\n')
687
+ .map((l) => l.trim())
688
+ .filter((l) => l !== '');
689
+ }
690
+ /**
691
+ * Restore a retained candidate into the working tree from its patch file.
692
+ *
693
+ * ⚠️ CHECKED FIRST, THEN APPLIED, AND NEVER PARTIALLY. `--check` asks git
694
+ * whether the whole patch fits the tree as it stands; only then is it applied
695
+ * — and `git apply` without `--reject` is itself all-or-nothing, so a hunk
696
+ * that no longer fits leaves the tree exactly as it was. The runner decides
697
+ * WHETHER to restore (base matches, patch complete, paths permitted); this
698
+ * only does the restoring, and answers a refusal as a value rather than a
699
+ * throw because "the patch no longer applies" is a finding, not a fault.
700
+ *
701
+ * `patchFile` is a path on this machine, never patch text on a command line.
702
+ */
703
+ async applyPatch(patchFile) {
704
+ const check = await this.exec(['apply', '--check', '--', patchFile]);
705
+ if (check.code !== 0) {
706
+ return { ok: false, because: `the patch does not apply to this tree: ${check.stderr.trim()}` };
707
+ }
708
+ const applied = await this.exec(['apply', '--', patchFile]);
709
+ if (applied.code !== 0) {
710
+ return { ok: false, because: `git apply exited ${applied.code}: ${applied.stderr.trim()}` };
711
+ }
712
+ return { ok: true };
713
+ }
714
+ async stageAll() {
715
+ await this.run(['add', '-A', '--', '.', ...(await this.keepStateOut())]);
716
+ }
717
+ /**
718
+ * Is there anything staged to commit?
719
+ *
720
+ * 🔴 BECAUSE "ALREADY DONE" WAS ENDING WHOLE RUNS. `git commit` exits 1 on an
721
+ * empty tree, the driver turns a non-zero exit into a `GitError`, and the
722
+ * runner's crash path turns that into `run-aborted` — severity critical, the
723
+ * run over, every remaining item dropped.
724
+ *
725
+ * Measured live: a round whose gate was GREEN in 2,148 ms and whose prover
726
+ * answered `proven` at 0.83 killed its run, because the previous round's
727
+ * implementer had already written the tests this round's item asked for. The
728
+ * work was right, the checks agreed it was right, and being right is what
729
+ * ended the run.
730
+ *
731
+ * `--quiet` implies `--exit-code`: 0 means no staged difference against HEAD,
732
+ * 1 means there is one. Anything else is a real git failure and is raised
733
+ * rather than read as "nothing staged" — answering "empty" to a broken
734
+ * repository would silently skip a commit that should have happened.
735
+ */
736
+ async hasStagedChanges() {
737
+ const r = await this.exec(['diff', '--cached', '--quiet']);
738
+ if (r.code === 0)
739
+ return false;
740
+ if (r.code === 1)
741
+ return true;
742
+ throw new GitError(`git diff --cached --quiet exited ${r.code}: ${r.stderr.trim()}`);
743
+ }
744
+ /**
745
+ * Commit the staged tree. The trailer is generated here from the executed step
746
+ * list — the driver takes `TrailerInput`, not a finished string, so no caller
747
+ * can hand-write a trailer that claims a check it never ran.
748
+ */
749
+ async commit(subject, trailer) {
750
+ await this.run(['commit', '-m', `${subject}\n\n${(0, core_1.buildTrailer)(trailer)}`]);
751
+ return (await this.run(['rev-parse', 'HEAD'])).trim();
752
+ }
753
+ /**
754
+ * The full message of a commit, or null when the ref does not resolve.
755
+ *
756
+ * Read-only, and used by recovery for one narrow question: whether the commit
757
+ * at HEAD is the one a crashed round was about to record. A round commits
758
+ * with the item's title as the subject, and only ever after the commit permit
759
+ * passed — so finding it is evidence the round finished, not a substitute for
760
+ * the checks that let it.
761
+ */
762
+ async messageOf(ref) {
763
+ const r = await this.exec(['show', '-s', '--format=%B', ref]);
764
+ return r.code === 0 ? r.stdout.trim() : null;
765
+ }
766
+ /**
767
+ * Drop everything the round did: `reset --hard`, then `clean -fd` with the
768
+ * state directory explicitly spared. `-fd` (never `-fdx`) plus `-e` means
769
+ * `.looply/**` survives even in a repo whose `.gitignore` forgot it.
770
+ */
771
+ async discard() {
772
+ await this.run(['reset', '--hard']);
773
+ await this.run(['clean', '-fd', '-e', exports.STATE_DIR]);
774
+ }
775
+ /* ------------------------------------------------ the only outward verbs */
776
+ /**
777
+ * WHAT THE REMOTE SAYS THIS REF IS, OR NULL.
778
+ *
779
+ * The reconciliation read, and the reason a push is the right first real
780
+ * action: the question "did it happen" has an exact answer that can be asked
781
+ * afterwards, from a different process, with nothing carried over but the
782
+ * approved sha.
783
+ *
784
+ * A ref the remote does not have is `null`, which is not an error — a branch
785
+ * that has never existed is the ordinary case for a first push.
786
+ */
787
+ async remoteRef(remoteName, ref) {
788
+ refuseOption('remote', remoteName);
789
+ refuseOption('ref', ref);
790
+ const r = await this.exec(['ls-remote', '--exit-code', remoteName, ref]);
791
+ // 2 is `--exit-code`'s "no matching ref", which is an answer and not a
792
+ // failure. Anything else genuinely went wrong and must not read as absence.
793
+ if (r.code === 2)
794
+ return null;
795
+ if (r.code !== 0) {
796
+ throw new GitError(`git ls-remote exited ${r.code}: ${r.stderr.trim()}`);
797
+ }
798
+ const first = r.stdout.split(/\r?\n/).find((l) => l.trim().length > 0);
799
+ const sha = first?.split(/\s+/)[0]?.trim();
800
+ return sha !== undefined && /^[0-9a-f]{40}$/.test(sha) ? sha : null;
801
+ }
802
+ /**
803
+ * THE ONE OUTWARD ACT.
804
+ *
805
+ * `<sha>:<ref>` rather than a branch name, so what is pushed is the exact
806
+ * commit a person approved and not whatever a local ref points at by the time
807
+ * this runs. There is no `--force` and no `+` in the refspec: this method
808
+ * cannot express a forced push, which is a stronger statement than declining
809
+ * to pass a flag.
810
+ *
811
+ * Everything else about safety it inherits: `nodeGitExec` is the one trusted
812
+ * construction point, so this call is refused outright if the repository's
813
+ * config names a program for git to run, and carries the hooks override and
814
+ * `GIT_TERMINAL_PROMPT=0` like every other verb.
815
+ */
816
+ async push(remoteName, sourceSha, destinationRef) {
817
+ refuseOption('remote', remoteName);
818
+ refuseOption('ref', destinationRef);
819
+ if (!/^[0-9a-f]{40}$/.test(sourceSha)) {
820
+ throw new GitError('a push needs a full commit id, not a ref');
821
+ }
822
+ if (!destinationRef.startsWith('refs/')) {
823
+ throw new GitError('a push needs a full destination ref');
824
+ }
825
+ await this.run(['push', remoteName, `${sourceSha}:${destinationRef}`]);
826
+ }
827
+ }
828
+ exports.GitDriver = GitDriver;
829
+ /**
830
+ * A leading dash is an OPTION to git, not a name.
831
+ *
832
+ * There is no shell here — argv goes straight to the process — so this is not
833
+ * about quoting. It is that `git push --receive-pack=... origin` is a perfectly
834
+ * well-formed argv in which the "remote" names a program to run on the far end,
835
+ * and `--` cannot help because git's push grammar takes the repository
836
+ * positionally before any separator would apply.
837
+ *
838
+ * The action parser refuses these shapes too. This is the second place, because
839
+ * the driver is reachable from callers the parser never saw.
840
+ */
841
+ function refuseOption(what, value) {
842
+ if (value.length === 0)
843
+ throw new GitError(`a push needs a ${what}`);
844
+ if (value.startsWith('-'))
845
+ throw new GitError(`that ${what} is an option, not a name`);
846
+ }
847
+ /**
848
+ * Real exec: argv straight to git, no shell, so message bytes arrive verbatim.
849
+ *
850
+ * THE ONE TRUSTED CONSTRUCTION POINT. Every production git call in the tree —
851
+ * the daemon's, the run's, the verifier's — is built here, which is why the
852
+ * repository-config defence can live in one place and cover all of them.
853
+ */
854
+ function nodeGitExec(cwd) {
855
+ return (args) => new Promise((resolve, reject) => {
856
+ // BEFORE GIT RUNS AT ALL. A filter is named by the repository and can be
857
+ // called anything, so there is no `-c` override that covers the family;
858
+ // the only sound answer is to decline the operation. Read as a plain
859
+ // file, deliberately — asking git would already be running git.
860
+ const trouble = (0, trusted_git_1.repoConfigTroubleAt)(cwd);
861
+ if (trouble !== null) {
862
+ reject(new GitError(trouble));
863
+ return;
864
+ }
865
+ // AND THAT THE TREE IS THE ONE THE CALLER NAMED. Every verb below is
866
+ // scoped to a repository, not to `cwd`: run from a subdirectory and
867
+ // `reset --hard` still resets everything above. Checked here for the same
868
+ // reason as the config — this is the one construction point, so a caller
869
+ // cannot forget it.
870
+ const misplaced = (0, trusted_git_1.repoRootTroubleAt)(cwd);
871
+ if (misplaced !== null) {
872
+ reject(new GitError(misplaced));
873
+ return;
874
+ }
875
+ /**
876
+ * AN ABSOLUTE PATH, NEVER THE BARE NAME. On Windows the cwd is searched
877
+ * before PATH, and the cwd here is a directory the contained agent may
878
+ * write — so `spawn('git', …)` let the repository choose the program.
879
+ */
880
+ const bin = (0, trusted_git_1.gitBinary)();
881
+ if (bin === null) {
882
+ reject(new GitError('git was not found on PATH, so no repository operation can be performed'));
883
+ return;
884
+ }
885
+ const child = (0, node_child_process_1.spawn)(bin, [...(0, trusted_git_1.trustedGitArgs)(), ...args], {
886
+ cwd,
887
+ env: (0, trusted_git_1.trustedGitEnv)(process.env, cwd),
888
+ stdio: ['ignore', 'pipe', 'pipe'],
889
+ });
890
+ const out = [];
891
+ const err = [];
892
+ child.stdout.on('data', (d) => out.push(d.toString('utf8')));
893
+ child.stderr.on('data', (d) => err.push(d.toString('utf8')));
894
+ child.on('error', reject);
895
+ child.on('close', (code) => {
896
+ resolve({ code: code ?? 1, stdout: out.join(''), stderr: err.join('') });
897
+ });
898
+ });
899
+ }
900
+ //# sourceMappingURL=git-driver.js.map