raga-cli 0.0.0-stage → 0.8.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 (478) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/LICENSE +21 -0
  3. package/README.md +139 -2
  4. package/bundled-skills/AGENTS.md +5 -0
  5. package/bundled-skills/README.md +25 -0
  6. package/bundled-skills/agents/comment-sicko.json +9 -0
  7. package/bundled-skills/agents/comment-sicko.md +28 -0
  8. package/bundled-skills/agents/model-roles.json +41 -0
  9. package/bundled-skills/agents/poteto-agent.json +9 -0
  10. package/bundled-skills/agents/poteto-agent.md +4 -0
  11. package/bundled-skills/agents/worker.json +8 -0
  12. package/bundled-skills/agents/worker.md +1 -0
  13. package/bundled-skills/bundle.json +1448 -0
  14. package/bundled-skills/dependencies.json +40 -0
  15. package/bundled-skills/docs/guide/01-setup.md +3 -0
  16. package/bundled-skills/docs/guide/02-peak.md +135 -0
  17. package/bundled-skills/docs/guide/03-understand.md +79 -0
  18. package/bundled-skills/docs/guide/04-design.md +133 -0
  19. package/bundled-skills/docs/guide/05-build-and-clean.md +83 -0
  20. package/bundled-skills/docs/guide/06-verify-and-ship.md +166 -0
  21. package/bundled-skills/docs/guide/07-overnight.md +118 -0
  22. package/bundled-skills/docs/guide/08-principles.md +72 -0
  23. package/bundled-skills/docs/guide/09-make-it-yours.md +96 -0
  24. package/bundled-skills/docs/guide/10-recipes-and-pitfalls.md +180 -0
  25. package/bundled-skills/docs/guide/README.md +38 -0
  26. package/bundled-skills/docs/guide/images/design.jpg +0 -0
  27. package/bundled-skills/docs/guide/images/overnight.jpg +0 -0
  28. package/bundled-skills/docs/guide/images/recipes.jpg +0 -0
  29. package/bundled-skills/docs/guide/images/router.jpg +0 -0
  30. package/bundled-skills/docs/guide/images/understanding.jpg +0 -0
  31. package/bundled-skills/docs/guide/images/verification.jpg +0 -0
  32. package/bundled-skills/licenses/commander +22 -0
  33. package/bundled-skills/licenses/cursor-team-kit +21 -0
  34. package/bundled-skills/licenses/no-mistakes +21 -0
  35. package/bundled-skills/licenses/pstack +21 -0
  36. package/bundled-skills/skills/peak/SKILL.md +184 -0
  37. package/bundled-skills/skills/peak/playbooks/authoring-a-skill.md +12 -0
  38. package/bundled-skills/skills/peak/playbooks/autonomous-run.md +13 -0
  39. package/bundled-skills/skills/peak/playbooks/autopilot-full.md +25 -0
  40. package/bundled-skills/skills/peak/playbooks/autopilot-stack.md +12 -0
  41. package/bundled-skills/skills/peak/playbooks/babysit.md +18 -0
  42. package/bundled-skills/skills/peak/playbooks/bug-fix.md +15 -0
  43. package/bundled-skills/skills/peak/playbooks/eval.md +25 -0
  44. package/bundled-skills/skills/peak/playbooks/feature.md +21 -0
  45. package/bundled-skills/skills/peak/playbooks/hillclimb.md +21 -0
  46. package/bundled-skills/skills/peak/playbooks/investigation.md +14 -0
  47. package/bundled-skills/skills/peak/playbooks/multi-phase-plan.md +155 -0
  48. package/bundled-skills/skills/peak/playbooks/opening-a-pr.md +34 -0
  49. package/bundled-skills/skills/peak/playbooks/orchestrate.md +112 -0
  50. package/bundled-skills/skills/peak/playbooks/pause-safely.md +10 -0
  51. package/bundled-skills/skills/peak/playbooks/perf-issue.md +25 -0
  52. package/bundled-skills/skills/peak/playbooks/prototype.md +14 -0
  53. package/bundled-skills/skills/peak/playbooks/refactoring.md +16 -0
  54. package/bundled-skills/skills/peak/playbooks/runtime-forensics.md +11 -0
  55. package/bundled-skills/skills/peak/playbooks/session-pickup.md +11 -0
  56. package/bundled-skills/skills/peak/playbooks/shipping.md +12 -0
  57. package/bundled-skills/skills/peak/playbooks/trace-forensics.md +14 -0
  58. package/bundled-skills/skills/peak/playbooks/visual-parity.md +11 -0
  59. package/bundled-skills/skills/peak/playbooks/worktree-cleanup.md +6 -0
  60. package/bundled-skills/skills/peak/references/architect/SKILL.md +81 -0
  61. package/bundled-skills/skills/peak/references/architect/references/design-red-flags.md +57 -0
  62. package/bundled-skills/skills/peak/references/architect/references/rationale-template.md +35 -0
  63. package/bundled-skills/skills/peak/references/architect/references/runner-prompt.md +20 -0
  64. package/bundled-skills/skills/peak/references/arena/SKILL.md +50 -0
  65. package/bundled-skills/skills/peak/references/automate-me/SKILL.md +106 -0
  66. package/bundled-skills/skills/peak/references/benchmark-checklist/SKILL.md +41 -0
  67. package/bundled-skills/skills/peak/references/blast-radius/SKILL.md +52 -0
  68. package/bundled-skills/skills/peak/references/bro/SKILL.md +9 -0
  69. package/bundled-skills/skills/peak/references/control-cli/SKILL.md +111 -0
  70. package/bundled-skills/skills/peak/references/control-ui/SKILL.md +111 -0
  71. package/bundled-skills/skills/peak/references/correct/SKILL.md +34 -0
  72. package/bundled-skills/skills/peak/references/create-formal-verification-skill/SKILL.md +43 -0
  73. package/bundled-skills/skills/peak/references/create-skill/SKILL.md +14 -0
  74. package/bundled-skills/skills/peak/references/create-verification-skill/SKILL.md +46 -0
  75. package/bundled-skills/skills/peak/references/create-verification-skill/references/feature-map-example/README.md +47 -0
  76. package/bundled-skills/skills/peak/references/create-verification-skill/references/feature-map-example/create-note.md +39 -0
  77. package/bundled-skills/skills/peak/references/create-verification-skill/references/feature-map-example/search.md +45 -0
  78. package/bundled-skills/skills/peak/references/deslop/SKILL.md +24 -0
  79. package/bundled-skills/skills/peak/references/figure-it-out/SKILL.md +54 -0
  80. package/bundled-skills/skills/peak/references/how/SKILL.md +38 -0
  81. package/bundled-skills/skills/peak/references/how/references/explainer-prompt.md +55 -0
  82. package/bundled-skills/skills/peak/references/how/references/explorer-prompt.md +35 -0
  83. package/bundled-skills/skills/peak/references/in-your-own-words/SKILL.md +9 -0
  84. package/bundled-skills/skills/peak/references/interrogate/SKILL.md +76 -0
  85. package/bundled-skills/skills/peak/references/interrogate/references/code-quality-review.md +47 -0
  86. package/bundled-skills/skills/peak/references/interrogate/references/lead-judgment.md +39 -0
  87. package/bundled-skills/skills/peak/references/interrogate/references/reviewer-prompt.md +62 -0
  88. package/bundled-skills/skills/peak/references/interrogate/references/rubric.md +77 -0
  89. package/bundled-skills/skills/peak/references/ketchup/SKILL.md +45 -0
  90. package/bundled-skills/skills/peak/references/maintain-formal-verification-skill/SKILL.md +60 -0
  91. package/bundled-skills/skills/peak/references/maintain-verification-skill/SKILL.md +41 -0
  92. package/bundled-skills/skills/peak/references/no-comments/SKILL.md +26 -0
  93. package/bundled-skills/skills/peak/references/no-mistakes/SKILL.md +20 -0
  94. package/bundled-skills/skills/peak/references/no-mistakes/upstream/SKILL.md +394 -0
  95. package/bundled-skills/skills/peak/references/peak-setup/SKILL.md +18 -0
  96. package/bundled-skills/skills/peak/references/poteto-help/SKILL.md +16 -0
  97. package/bundled-skills/skills/peak/references/poteto-help/references/prompting.md +51 -0
  98. package/bundled-skills/skills/peak/references/poteto-help/references/recipes.md +51 -0
  99. package/bundled-skills/skills/peak/references/principle-attack-the-premise/SKILL.md +23 -0
  100. package/bundled-skills/skills/peak/references/principle-boundary-discipline/SKILL.md +36 -0
  101. package/bundled-skills/skills/peak/references/principle-build-the-lever/SKILL.md +25 -0
  102. package/bundled-skills/skills/peak/references/principle-encode-lessons-in-structure/SKILL.md +13 -0
  103. package/bundled-skills/skills/peak/references/principle-exhaust-the-design-space/SKILL.md +11 -0
  104. package/bundled-skills/skills/peak/references/principle-experience-first/SKILL.md +21 -0
  105. package/bundled-skills/skills/peak/references/principle-explain-the-number/SKILL.md +19 -0
  106. package/bundled-skills/skills/peak/references/principle-fix-root-causes/SKILL.md +23 -0
  107. package/bundled-skills/skills/peak/references/principle-foundational-thinking/SKILL.md +23 -0
  108. package/bundled-skills/skills/peak/references/principle-guard-the-context-window/SKILL.md +14 -0
  109. package/bundled-skills/skills/peak/references/principle-laziness-protocol/SKILL.md +12 -0
  110. package/bundled-skills/skills/peak/references/principle-make-operations-idempotent/SKILL.md +24 -0
  111. package/bundled-skills/skills/peak/references/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +16 -0
  112. package/bundled-skills/skills/peak/references/principle-minimize-reader-load/SKILL.md +20 -0
  113. package/bundled-skills/skills/peak/references/principle-model-the-domain/SKILL.md +26 -0
  114. package/bundled-skills/skills/peak/references/principle-never-block-on-the-human/SKILL.md +17 -0
  115. package/bundled-skills/skills/peak/references/principle-outcome-oriented-execution/SKILL.md +23 -0
  116. package/bundled-skills/skills/peak/references/principle-prove-it-works/SKILL.md +24 -0
  117. package/bundled-skills/skills/peak/references/principle-redesign-from-first-principles/SKILL.md +18 -0
  118. package/bundled-skills/skills/peak/references/principle-separate-before-serializing-shared-state/SKILL.md +18 -0
  119. package/bundled-skills/skills/peak/references/principle-sequence-verifiable-units/SKILL.md +19 -0
  120. package/bundled-skills/skills/peak/references/principle-subtract-before-you-add/SKILL.md +20 -0
  121. package/bundled-skills/skills/peak/references/principle-test-behavior-not-implementation/SKILL.md +22 -0
  122. package/bundled-skills/skills/peak/references/principle-type-system-discipline/SKILL.md +15 -0
  123. package/bundled-skills/skills/peak/references/raga-runtime/SKILL.md +37 -0
  124. package/bundled-skills/skills/peak/references/raga-runtime/managed-tools.mjs +34 -0
  125. package/bundled-skills/skills/peak/references/raga-runtime/raga_runtime.py +44 -0
  126. package/bundled-skills/skills/peak/references/recall/SKILL.md +37 -0
  127. package/bundled-skills/skills/peak/references/reflect/SKILL.md +63 -0
  128. package/bundled-skills/skills/peak/references/reflect/references/divergent-reviewer.md +38 -0
  129. package/bundled-skills/skills/peak/references/reflect/references/judgment-reviewer.md +37 -0
  130. package/bundled-skills/skills/peak/references/reflect/references/synthesizer.md +56 -0
  131. package/bundled-skills/skills/peak/references/reflect/references/tooling-reviewer.md +48 -0
  132. package/bundled-skills/skills/peak/references/reviewer-triage.md +142 -0
  133. package/bundled-skills/skills/peak/references/sharpen/SKILL.md +18 -0
  134. package/bundled-skills/skills/peak/references/show-me-your-work/SKILL.md +84 -0
  135. package/bundled-skills/skills/peak/references/show-me-your-work/references/decision-log-template.tsv +1 -0
  136. package/bundled-skills/skills/peak/references/show-me-your-work/scripts/log.sh +42 -0
  137. package/bundled-skills/skills/peak/references/statechart/SKILL.md +38 -0
  138. package/bundled-skills/skills/peak/references/statechart/scripts/chart-to-tla.mjs +236 -0
  139. package/bundled-skills/skills/peak/references/statechart/scripts/compare-charts.mjs +104 -0
  140. package/bundled-skills/skills/peak/references/statechart/scripts/lib.mjs +725 -0
  141. package/bundled-skills/skills/peak/references/statechart/scripts/model/OutputFolder.cfg +6 -0
  142. package/bundled-skills/skills/peak/references/statechart/scripts/model/OutputFolder.map.mjs +106 -0
  143. package/bundled-skills/skills/peak/references/statechart/scripts/model/OutputFolder.tla +176 -0
  144. package/bundled-skills/skills/peak/references/statechart/scripts/statechart.mjs +749 -0
  145. package/bundled-skills/skills/peak/references/statechart/scripts/test/statechart.test.mjs +2790 -0
  146. package/bundled-skills/skills/peak/references/statechart/scripts/test/sync.mmd +19 -0
  147. package/bundled-skills/skills/peak/references/statechart/scripts/test/upload.mmd +16 -0
  148. package/bundled-skills/skills/peak/references/statechart/scripts/tlc-to-chart.mjs +1347 -0
  149. package/bundled-skills/skills/peak/references/swarm/SKILL.md +43 -0
  150. package/bundled-skills/skills/peak/references/tdd/SKILL.md +44 -0
  151. package/bundled-skills/skills/peak/references/teach/SKILL.md +23 -0
  152. package/bundled-skills/skills/peak/references/technical-writing/SKILL.md +108 -0
  153. package/bundled-skills/skills/peak/references/typescript-best-practices/SKILL.md +33 -0
  154. package/bundled-skills/skills/peak/references/typescript-best-practices/references/patterns.md +328 -0
  155. package/bundled-skills/skills/peak/references/unslop/SKILL.md +64 -0
  156. package/bundled-skills/skills/peak/references/verify-this/SKILL.md +76 -0
  157. package/bundled-skills/skills/peak/references/why/SKILL.md +100 -0
  158. package/bundled-skills/skills/peak/references/why/references/epistemics.md +71 -0
  159. package/bundled-skills/skills/peak/references/why/references/investigator-prompt.md +84 -0
  160. package/bundled-skills/skills/peak/references/why/references/source-playbook.md +17 -0
  161. package/bundled-skills/skills/peak/references/why/references/sources/code-archaeology.md +51 -0
  162. package/bundled-skills/skills/peak/references/why/references/sources/databricks.md +70 -0
  163. package/bundled-skills/skills/peak/references/why/references/sources/datadog.md +97 -0
  164. package/bundled-skills/skills/peak/references/why/references/sources/incident-postmortem.md +5 -0
  165. package/bundled-skills/skills/peak/references/why/references/sources/linear.md +48 -0
  166. package/bundled-skills/skills/peak/references/why/references/sources/notion.md +55 -0
  167. package/bundled-skills/skills/peak/references/why/references/sources/sentry.md +66 -0
  168. package/bundled-skills/skills/peak/references/why/references/sources/slack.md +17 -0
  169. package/bundled-skills/skills/peak/references/why/references/synthesizer-prompt.md +129 -0
  170. package/bundled-skills/skills/peak/scripts/check-plan.mjs +205 -0
  171. package/bundled-skills/skills/peak/scripts/check-plan.test.ts +96 -0
  172. package/bundled-skills/skills/peak/scripts/orch/orch.test.ts +761 -0
  173. package/bundled-skills/skills/peak/scripts/orch/orch.ts +588 -0
  174. package/bundled-skills/skills/peak/scripts/orch/store.ts +1676 -0
  175. package/bundled-skills/skills/peak/scripts/vendor/commander/LICENSE +22 -0
  176. package/bundled-skills/skills/peak/scripts/vendor/commander/esm.mjs +16 -0
  177. package/bundled-skills/skills/peak/scripts/vendor/commander/index.js +24 -0
  178. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/argument.js +149 -0
  179. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/command.js +2778 -0
  180. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/error.js +39 -0
  181. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/help.js +747 -0
  182. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/option.js +379 -0
  183. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/suggestSimilar.js +101 -0
  184. package/bundled-skills/skills/peak/scripts/vendor/commander/package.json +1 -0
  185. package/bundled-skills/skills/peak/scripts/vendor/commander/typings/esm.d.mts +3 -0
  186. package/bundled-skills/skills/peak/scripts/vendor/commander/typings/index.d.ts +1113 -0
  187. package/bundled-skills/skills/peak/scripts/watch-pr/cli.test.ts +224 -0
  188. package/bundled-skills/skills/peak/scripts/watch-pr/cli.ts +223 -0
  189. package/bundled-skills/skills/peak/scripts/watch-pr/fakes.test-helper.ts +129 -0
  190. package/bundled-skills/skills/peak/scripts/watch-pr/github.test.ts +436 -0
  191. package/bundled-skills/skills/peak/scripts/watch-pr/github.ts +718 -0
  192. package/bundled-skills/skills/peak/scripts/watch-pr/policy.test.ts +420 -0
  193. package/bundled-skills/skills/peak/scripts/watch-pr/policy.ts +836 -0
  194. package/bundled-skills/skills/peak/scripts/watch-pr/render.ts +169 -0
  195. package/bundled-skills/skills/peak/scripts/watch-pr/tsconfig.json +13 -0
  196. package/bundled-skills/skills/peak/scripts/watch-pr/types.compile.ts +93 -0
  197. package/bundled-skills/skills/peak/scripts/watch-pr/types.ts +413 -0
  198. package/bundled-skills/skills/peak/scripts/watch-pr/watch-pr +4 -0
  199. package/bundled-skills/skills/peak/scripts/worktree-audit.sh +4 -0
  200. package/dist/bundle/chunks/anthropic-messages-BMDYDGHW.js +6 -0
  201. package/dist/bundle/chunks/anthropic.js +75 -0
  202. package/dist/bundle/chunks/azure-openai-responses-Q7EZTEHK.js +2 -0
  203. package/dist/bundle/chunks/bedrock-converse-stream.js +86 -0
  204. package/dist/bundle/chunks/chunk-3EGDWOKT.js +2 -0
  205. package/dist/bundle/chunks/chunk-3V45WARH.js +7 -0
  206. package/dist/bundle/chunks/chunk-5AGIVGSV.js +6 -0
  207. package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
  208. package/dist/bundle/chunks/chunk-7CBJINYJ.js +20 -0
  209. package/dist/bundle/chunks/chunk-7DDKXS2Q.js +2 -0
  210. package/dist/bundle/chunks/chunk-7ZC4UXSL.js +2 -0
  211. package/dist/bundle/chunks/chunk-AI2NMHE7.js +1567 -0
  212. package/dist/bundle/chunks/chunk-C4IJNE7U.js +2 -0
  213. package/dist/bundle/chunks/chunk-CF5GWZHW.js +31 -0
  214. package/dist/bundle/chunks/chunk-G54UBZTF.js +44 -0
  215. package/dist/bundle/chunks/chunk-H76KZMOI.js +2 -0
  216. package/dist/bundle/chunks/chunk-IOPE2TJ4.js +131 -0
  217. package/dist/bundle/chunks/chunk-IXDQLPKS.js +11 -0
  218. package/dist/bundle/chunks/chunk-J22MF7YW.js +64 -0
  219. package/dist/bundle/chunks/chunk-JLV25LHE.js +2 -0
  220. package/dist/bundle/chunks/chunk-JXWJOIH4.js +2 -0
  221. package/dist/bundle/chunks/chunk-KNY3PXQI.js +2 -0
  222. package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
  223. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  224. package/dist/bundle/chunks/chunk-LVVZS2NO.js +12 -0
  225. package/dist/bundle/chunks/chunk-LX24DVQ5.js +2 -0
  226. package/dist/bundle/chunks/chunk-P2QCWBFM.js +9 -0
  227. package/dist/bundle/chunks/chunk-P7FPUEYA.js +2 -0
  228. package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
  229. package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
  230. package/dist/bundle/chunks/chunk-PMRZO46Y.js +2 -0
  231. package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
  232. package/dist/bundle/chunks/chunk-Q4U25BSY.js +2 -0
  233. package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
  234. package/dist/bundle/chunks/chunk-RFPFZTK2.js +7 -0
  235. package/dist/bundle/chunks/chunk-RP2WKNT7.js +4 -0
  236. package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
  237. package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
  238. package/dist/bundle/chunks/chunk-UDT647VB.js +82 -0
  239. package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
  240. package/dist/bundle/chunks/chunk-USJZMAIR.js +2 -0
  241. package/dist/bundle/chunks/chunk-UTB4DTYQ.js +2 -0
  242. package/dist/bundle/chunks/chunk-WJGLPWUE.js +16 -0
  243. package/dist/bundle/chunks/chunk-YMZUNB25.js +2 -0
  244. package/dist/bundle/chunks/cli-4P6FVVLT.js +51 -0
  245. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-S6CT2FXS.js +2 -0
  246. package/dist/bundle/chunks/codemode-worker.js +478 -0
  247. package/dist/bundle/chunks/easter-egg-3d-65VIC4G2.js +2 -0
  248. package/dist/bundle/chunks/execute-3H5WKMA3.js +505 -0
  249. package/dist/bundle/chunks/github-copilot.js +2 -0
  250. package/dist/bundle/chunks/google-generative-ai-35NZKG6S.js +2 -0
  251. package/dist/bundle/chunks/google-vertex-P33AHYYX.js +2 -0
  252. package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
  253. package/dist/bundle/chunks/image-resize-worker.js +2 -0
  254. package/dist/bundle/chunks/jiti-loader-GL4HJFAD.js +21 -0
  255. package/dist/bundle/chunks/jiti-static-loader-2RCDI6B4.js +2 -0
  256. package/dist/bundle/chunks/kimi-coding.js +2 -0
  257. package/dist/bundle/chunks/lib-4AVRLBHM.js +9 -0
  258. package/dist/bundle/chunks/llama-cpp-classify-U4EH6TAM.js +22 -0
  259. package/dist/bundle/chunks/meta.js +2 -0
  260. package/dist/bundle/chunks/mistral-conversations-J62DPS2P.js +5 -0
  261. package/dist/bundle/chunks/multipart-parser-FMGQ7342.js +3 -0
  262. package/dist/bundle/chunks/node-AUOGOBCN.js +2 -0
  263. package/dist/bundle/chunks/node-KCMANYC2.js +16 -0
  264. package/dist/bundle/chunks/openai-chatgpt.js +75 -0
  265. package/dist/bundle/chunks/openai-codex-responses-4HXATL2M.js +10 -0
  266. package/dist/bundle/chunks/openai-codex.js +75 -0
  267. package/dist/bundle/chunks/openai-completions-75YYY35N.js +7 -0
  268. package/dist/bundle/chunks/openai-decisions-HEBEVGA2.js +5 -0
  269. package/dist/bundle/chunks/openai-responses-UWNAKTBV.js +4 -0
  270. package/dist/bundle/chunks/openrouter-images-HVOPXKHS.js +2 -0
  271. package/dist/bundle/chunks/openrouter.js +75 -0
  272. package/dist/bundle/chunks/pi-messages-DG7CALOV.js +8 -0
  273. package/dist/bundle/chunks/radius.js +75 -0
  274. package/dist/bundle/chunks/runtime-435R6IKY.js +2 -0
  275. package/dist/bundle/chunks/src-QKGKDFSU.js +4 -0
  276. package/dist/bundle/chunks/typesafe-system-one-24HEGHNN.js +2 -0
  277. package/dist/bundle/chunks/virtual-modules-U3JBUDP7.js +2 -0
  278. package/dist/bundle/chunks/xai.js +2 -0
  279. package/dist/bundle/cli-runtime.js +3 -0
  280. package/dist/bundle/cli.js +5 -0
  281. package/dist/bundle/index.js +2 -0
  282. package/dist/bundle/raga-runtime.js +3 -0
  283. package/dist/bundle/raga.js +5 -0
  284. package/dist/bundle/rpc-entry.js +3 -0
  285. package/dist/core/export-html/template.css +1085 -0
  286. package/dist/core/export-html/template.html +56 -0
  287. package/dist/core/export-html/template.js +2044 -0
  288. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  289. package/dist/core/export-html/vendor/marked-cjk-friendly.min.js +6 -0
  290. package/dist/core/export-html/vendor/marked.min.js +79 -0
  291. package/dist/modes/interactive/assets/clankolas.png +0 -0
  292. package/dist/modes/interactive/theme/dark.json +78 -0
  293. package/dist/modes/interactive/theme/light.json +78 -0
  294. package/docs/cli-integration.md +106 -0
  295. package/docs/cli.md +337 -0
  296. package/docs/codemode.md +230 -0
  297. package/docs/compaction.md +463 -0
  298. package/docs/configuration.md +62 -0
  299. package/docs/containerization.md +183 -0
  300. package/docs/custom-provider.md +227 -0
  301. package/docs/docs.json +220 -0
  302. package/docs/environment-variables.md +101 -0
  303. package/docs/extensions.md +400 -0
  304. package/docs/how-pi-works.md +49 -0
  305. package/docs/images/doom-extension.png +0 -0
  306. package/docs/images/exy.png +0 -0
  307. package/docs/images/interactive-mode.png +0 -0
  308. package/docs/images/tree-view.png +0 -0
  309. package/docs/index.md +39 -0
  310. package/docs/json.md +226 -0
  311. package/docs/keybindings.md +195 -0
  312. package/docs/llama-cpp.md +121 -0
  313. package/docs/mcp.md +272 -0
  314. package/docs/message-types.md +279 -0
  315. package/docs/models-configuration.md +196 -0
  316. package/docs/models.md +225 -0
  317. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  318. package/docs/packages.md +129 -0
  319. package/docs/prompt-templates.md +59 -0
  320. package/docs/providers.md +247 -0
  321. package/docs/quickstart.md +136 -0
  322. package/docs/rpc-commands.md +860 -0
  323. package/docs/rpc-extension-ui.md +200 -0
  324. package/docs/rpc.md +193 -0
  325. package/docs/sdk.md +148 -0
  326. package/docs/security.md +99 -0
  327. package/docs/session-format.md +293 -0
  328. package/docs/sessions.md +66 -0
  329. package/docs/settings.md +184 -0
  330. package/docs/shell-aliases.md +93 -0
  331. package/docs/skills.md +93 -0
  332. package/docs/slash-commands.md +61 -0
  333. package/docs/terminal-setup.md +233 -0
  334. package/docs/termux.md +117 -0
  335. package/docs/themes.md +138 -0
  336. package/docs/tmux.md +55 -0
  337. package/docs/tui.md +195 -0
  338. package/docs/usage.md +94 -0
  339. package/docs/virtual-models.md +114 -0
  340. package/docs/windows.md +81 -0
  341. package/examples/README.md +36 -0
  342. package/examples/extensions/README.md +214 -0
  343. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  344. package/examples/extensions/bash-spawn-hook.ts +30 -0
  345. package/examples/extensions/bookmark.ts +50 -0
  346. package/examples/extensions/border-status-editor.ts +150 -0
  347. package/examples/extensions/built-in-tool-renderer.ts +225 -0
  348. package/examples/extensions/claude-rules.ts +86 -0
  349. package/examples/extensions/commands.ts +72 -0
  350. package/examples/extensions/confirm-destructive.ts +59 -0
  351. package/examples/extensions/custom-compaction.ts +117 -0
  352. package/examples/extensions/custom-footer.ts +64 -0
  353. package/examples/extensions/custom-header.ts +73 -0
  354. package/examples/extensions/custom-provider-anthropic/index.ts +617 -0
  355. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  356. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  357. package/examples/extensions/custom-provider-gitlab-duo/index.ts +405 -0
  358. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  359. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  360. package/examples/extensions/debug-provider.ts +90 -0
  361. package/examples/extensions/dirty-repo-guard.ts +56 -0
  362. package/examples/extensions/doom-overlay/README.md +46 -0
  363. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  364. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  365. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  366. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  367. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  368. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  369. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  370. package/examples/extensions/doom-overlay/index.ts +74 -0
  371. package/examples/extensions/doom-overlay/wad-finder.ts +55 -0
  372. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  373. package/examples/extensions/dynamic-resources/dynamic.json +81 -0
  374. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  375. package/examples/extensions/dynamic-resources/index.ts +15 -0
  376. package/examples/extensions/dynamic-tools.ts +74 -0
  377. package/examples/extensions/entry-renderer.ts +41 -0
  378. package/examples/extensions/event-bus.ts +43 -0
  379. package/examples/extensions/file-trigger.ts +41 -0
  380. package/examples/extensions/git-checkpoint.ts +53 -0
  381. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  382. package/examples/extensions/github-issue-autocomplete.ts +185 -0
  383. package/examples/extensions/gondolin/index.ts +531 -0
  384. package/examples/extensions/gondolin/package-lock.json +175 -0
  385. package/examples/extensions/gondolin/package.json +19 -0
  386. package/examples/extensions/handoff.ts +190 -0
  387. package/examples/extensions/hello.ts +26 -0
  388. package/examples/extensions/hidden-thinking-label.ts +53 -0
  389. package/examples/extensions/inline-bash.ts +94 -0
  390. package/examples/extensions/input-transform-streaming.ts +39 -0
  391. package/examples/extensions/input-transform.ts +43 -0
  392. package/examples/extensions/interactive-shell.ts +196 -0
  393. package/examples/extensions/jev-router.ts +113 -0
  394. package/examples/extensions/mac-system-theme.ts +47 -0
  395. package/examples/extensions/message-renderer.ts +59 -0
  396. package/examples/extensions/minimal-mode.ts +353 -0
  397. package/examples/extensions/modal-editor.ts +85 -0
  398. package/examples/extensions/model-status.ts +31 -0
  399. package/examples/extensions/notify.ts +57 -0
  400. package/examples/extensions/overlay-qa-tests.ts +1450 -0
  401. package/examples/extensions/overlay-test.ts +153 -0
  402. package/examples/extensions/permission-gate.ts +34 -0
  403. package/examples/extensions/pirate.ts +47 -0
  404. package/examples/extensions/plan-mode/README.md +66 -0
  405. package/examples/extensions/plan-mode/index.ts +394 -0
  406. package/examples/extensions/plan-mode/utils.ts +168 -0
  407. package/examples/extensions/preset.ts +436 -0
  408. package/examples/extensions/project-trust.ts +64 -0
  409. package/examples/extensions/prompt-customizer.ts +49 -0
  410. package/examples/extensions/protected-paths.ts +30 -0
  411. package/examples/extensions/provider-payload.ts +18 -0
  412. package/examples/extensions/qna.ts +118 -0
  413. package/examples/extensions/question.ts +286 -0
  414. package/examples/extensions/questionnaire.ts +448 -0
  415. package/examples/extensions/rainbow-editor.ts +88 -0
  416. package/examples/extensions/reload-runtime.ts +37 -0
  417. package/examples/extensions/rpc-demo.ts +118 -0
  418. package/examples/extensions/sandbox/index.ts +326 -0
  419. package/examples/extensions/sandbox/package-lock.json +92 -0
  420. package/examples/extensions/sandbox/package.json +19 -0
  421. package/examples/extensions/send-user-message.ts +97 -0
  422. package/examples/extensions/session-name.ts +27 -0
  423. package/examples/extensions/shutdown-command.ts +63 -0
  424. package/examples/extensions/snake.ts +343 -0
  425. package/examples/extensions/space-invaders.ts +560 -0
  426. package/examples/extensions/ssh.ts +220 -0
  427. package/examples/extensions/status-line.ts +32 -0
  428. package/examples/extensions/structured-output.ts +65 -0
  429. package/examples/extensions/subagent/README.md +177 -0
  430. package/examples/extensions/subagent/agents/planner.md +37 -0
  431. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  432. package/examples/extensions/subagent/agents/scout.md +50 -0
  433. package/examples/extensions/subagent/agents/worker.md +24 -0
  434. package/examples/extensions/subagent/agents.ts +157 -0
  435. package/examples/extensions/subagent/index.ts +1038 -0
  436. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  437. package/examples/extensions/subagent/prompts/implement.md +10 -0
  438. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  439. package/examples/extensions/summarize.ts +199 -0
  440. package/examples/extensions/system-prompt-header.ts +17 -0
  441. package/examples/extensions/tic-tac-toe.ts +1008 -0
  442. package/examples/extensions/timed-confirm.ts +70 -0
  443. package/examples/extensions/titlebar-spinner.ts +58 -0
  444. package/examples/extensions/todo.ts +297 -0
  445. package/examples/extensions/tool-override.ts +144 -0
  446. package/examples/extensions/tools.ts +146 -0
  447. package/examples/extensions/trigger-compact.ts +50 -0
  448. package/examples/extensions/truncated-tool.ts +195 -0
  449. package/examples/extensions/widget-placement.ts +9 -0
  450. package/examples/extensions/with-deps/index.ts +32 -0
  451. package/examples/extensions/with-deps/package-lock.json +31 -0
  452. package/examples/extensions/with-deps/package.json +22 -0
  453. package/examples/extensions/working-indicator.ts +123 -0
  454. package/examples/extensions/working-message-test.ts +25 -0
  455. package/examples/rpc-client.ts +35 -0
  456. package/examples/rpc-extension-ui.ts +661 -0
  457. package/examples/sdk/01-minimal.ts +26 -0
  458. package/examples/sdk/02-custom-model.ts +49 -0
  459. package/examples/sdk/03-custom-prompt.ts +75 -0
  460. package/examples/sdk/04-skills.ts +55 -0
  461. package/examples/sdk/05-tools.ts +48 -0
  462. package/examples/sdk/06-extensions.ts +99 -0
  463. package/examples/sdk/07-context-files.ts +47 -0
  464. package/examples/sdk/08-prompt-templates.ts +51 -0
  465. package/examples/sdk/09-api-keys-and-oauth.ts +34 -0
  466. package/examples/sdk/10-settings.ts +53 -0
  467. package/examples/sdk/11-sessions.ts +52 -0
  468. package/examples/sdk/12-full-control.ts +74 -0
  469. package/examples/sdk/13-session-runtime.ts +67 -0
  470. package/examples/sdk/14-codemode-mcp.ts +61 -0
  471. package/examples/sdk/README.md +141 -0
  472. package/package.json +21 -5
  473. package/python/raga_rlm/__init__.py +336 -0
  474. package/python/raga_rlm/repl.py +920 -0
  475. package/schemas/keybindings.schema.json +393 -0
  476. package/schemas/models.schema.json +1211 -0
  477. package/schemas/settings.schema.json +815 -0
  478. package/schemas/theme.schema.json +365 -0
@@ -0,0 +1,1676 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import type { Dirent } from "node:fs";
4
+ import {
5
+ access,
6
+ mkdir,
7
+ open,
8
+ readFile,
9
+ readdir,
10
+ rename,
11
+ rm,
12
+ unlink,
13
+ writeFile,
14
+ } from "node:fs/promises";
15
+ import { basename, dirname, join, resolve } from "node:path";
16
+
17
+ const UNIT_HEADER = "id\ttrack\tstate\tbranch\tpr\tsha\tbrief";
18
+ const LEDGER_HEADER = "pr\tsha\tverdict\tevidence\tverifier\tts";
19
+ const LOCK_FILE = ".orch.lock";
20
+
21
+ export type Verdict =
22
+ | "live-ui-verified"
23
+ | "unit-test-verified"
24
+ | "type-check-only"
25
+ | "verifier-blocked"
26
+ | "verifier-failed";
27
+
28
+ export interface Unit {
29
+ readonly id: string;
30
+ readonly track: string;
31
+ readonly state: string;
32
+ readonly branch: string;
33
+ readonly pr: string;
34
+ readonly sha: string;
35
+ readonly brief: string;
36
+ }
37
+
38
+ export interface LedgerEntry {
39
+ readonly pr: string;
40
+ readonly sha: string;
41
+ readonly verdict: Verdict;
42
+ readonly evidence: string;
43
+ readonly verifier: string;
44
+ readonly ts: string;
45
+ }
46
+
47
+ export interface InboxPointer {
48
+ readonly ts: string;
49
+ readonly agent: string;
50
+ readonly unit: string;
51
+ readonly status: string;
52
+ readonly report: string;
53
+ }
54
+
55
+ export interface InboxPushResult {
56
+ readonly pointer: InboxPointer;
57
+ readonly filename: string;
58
+ }
59
+
60
+ export interface OpenGate {
61
+ readonly kind: "open";
62
+ readonly id: string;
63
+ readonly question: string;
64
+ readonly options: string;
65
+ readonly defaultAnswer: string;
66
+ }
67
+
68
+ export interface ResolvedGate {
69
+ readonly kind: "resolved";
70
+ readonly id: string;
71
+ readonly question: string;
72
+ readonly options: string;
73
+ readonly defaultAnswer: string;
74
+ readonly answer: string;
75
+ }
76
+
77
+ export type Gate = OpenGate | ResolvedGate;
78
+
79
+ export type FrontierPrState = "OPEN" | "MERGED" | "CLOSED";
80
+
81
+ export interface FrontierPr {
82
+ readonly pr: number;
83
+ readonly branches: string;
84
+ readonly sha: string;
85
+ readonly state: FrontierPrState;
86
+ }
87
+
88
+ export interface Frontier {
89
+ readonly generation: number;
90
+ readonly prs: readonly FrontierPr[];
91
+ readonly lowestUnmerged: number | null;
92
+ }
93
+
94
+ export interface StandingLine {
95
+ readonly number: number;
96
+ readonly line: string;
97
+ }
98
+
99
+ export type Counts = Readonly<Record<string, number>>;
100
+
101
+ export interface StatusSummary {
102
+ readonly unitStates: Counts;
103
+ readonly ledgerVerdicts: Counts;
104
+ readonly frontierGeneration: number;
105
+ readonly openGateIds: readonly string[];
106
+ }
107
+
108
+ export interface StatusReport {
109
+ readonly units: readonly Unit[];
110
+ readonly ledger: readonly LedgerEntry[];
111
+ readonly frontier: Frontier;
112
+ readonly gates: readonly Gate[];
113
+ readonly summary: StatusSummary;
114
+ readonly changed: string;
115
+ }
116
+
117
+ export interface AddUnitParams {
118
+ readonly id: string;
119
+ readonly track: string;
120
+ readonly brief?: string;
121
+ }
122
+
123
+ export interface SetUnitParams {
124
+ readonly id: string;
125
+ readonly state: string;
126
+ readonly branch?: string;
127
+ readonly pr?: number;
128
+ readonly sha?: string;
129
+ }
130
+
131
+ export interface ListUnitsParams {
132
+ readonly state?: string;
133
+ readonly track?: string;
134
+ }
135
+
136
+ export interface RecordLedgerParams {
137
+ readonly pr: number;
138
+ readonly sha: string;
139
+ readonly verdict: Verdict;
140
+ readonly evidence: string;
141
+ readonly verifier?: string;
142
+ }
143
+
144
+ export interface CheckLedgerParams {
145
+ readonly pr: number;
146
+ readonly sha: string;
147
+ }
148
+
149
+ export interface PushInboxParams {
150
+ readonly agent: string;
151
+ readonly unit: string;
152
+ readonly status: string;
153
+ readonly report?: string;
154
+ }
155
+
156
+ export interface ParkGateParams {
157
+ readonly id: string;
158
+ readonly question: string;
159
+ readonly options: string;
160
+ readonly defaultAnswer: string;
161
+ }
162
+
163
+ export interface ResolveGateParams {
164
+ readonly id: string;
165
+ readonly answer: string;
166
+ }
167
+
168
+ export interface SetFrontierParams {
169
+ readonly repo: string;
170
+ readonly forge?: Forge;
171
+ readonly prs?: readonly number[];
172
+ }
173
+
174
+ export interface AddStandingParams {
175
+ readonly line: string;
176
+ }
177
+
178
+ export interface OpenStoreOptions {
179
+ readonly force?: boolean;
180
+ readonly onLockStolen?: (holder: string) => void;
181
+ readonly onStaleLock?: (holder: string) => void;
182
+ }
183
+
184
+ export interface Store {
185
+ readonly units: {
186
+ readonly add: (params: AddUnitParams) => Promise<Unit>;
187
+ readonly set: (params: SetUnitParams) => Promise<Unit>;
188
+ readonly get: (id: string) => Promise<Unit>;
189
+ readonly list: (params?: ListUnitsParams) => Promise<readonly Unit[]>;
190
+ readonly counts: () => Promise<Counts>;
191
+ };
192
+ readonly ledger: {
193
+ readonly record: (params: RecordLedgerParams) => Promise<LedgerEntry>;
194
+ readonly check: (params: CheckLedgerParams) => Promise<LedgerEntry>;
195
+ readonly summary: () => Promise<Counts>;
196
+ };
197
+ readonly inbox: {
198
+ readonly push: (params: PushInboxParams) => Promise<InboxPushResult>;
199
+ readonly drain: () => Promise<readonly InboxPointer[]>;
200
+ readonly peek: () => Promise<readonly InboxPointer[]>;
201
+ readonly count: () => Promise<number>;
202
+ };
203
+ readonly gates: {
204
+ readonly park: (params: ParkGateParams) => Promise<OpenGate>;
205
+ readonly list: () => Promise<readonly OpenGate[]>;
206
+ readonly resolve: (params: ResolveGateParams) => Promise<ResolvedGate>;
207
+ };
208
+ readonly frontier: {
209
+ readonly set: (params: SetFrontierParams) => Promise<Frontier>;
210
+ readonly show: () => Promise<Frontier>;
211
+ };
212
+ readonly standing: {
213
+ readonly show: () => Promise<readonly StandingLine[]>;
214
+ readonly add: (params: AddStandingParams) => Promise<StandingLine>;
215
+ };
216
+ readonly status: {
217
+ readonly render: () => Promise<StatusReport>;
218
+ };
219
+ readonly init: () => Promise<{ readonly store: string }>;
220
+ readonly close: () => Promise<void>;
221
+ }
222
+
223
+ export interface NotFoundOutput {
224
+ readonly compact: string;
225
+ readonly json: unknown;
226
+ }
227
+
228
+ export class UserError extends Error {}
229
+ export class UsageError extends UserError {}
230
+ export class NotFoundError extends UserError {
231
+ public readonly output?: NotFoundOutput;
232
+ public constructor(message: string, output?: NotFoundOutput) {
233
+ super(message);
234
+ this.output = output;
235
+ }
236
+ }
237
+
238
+ function errorCode(error: unknown): string | null {
239
+ if (
240
+ error !== null &&
241
+ typeof error === "object" &&
242
+ "code" in error &&
243
+ typeof error.code === "string"
244
+ ) {
245
+ return error.code;
246
+ }
247
+ return null;
248
+ }
249
+
250
+ function errorMessage(error: unknown): string {
251
+ return error instanceof Error ? error.message : String(error);
252
+ }
253
+
254
+ function isRecord(value: unknown): value is Record<string, unknown> {
255
+ return value !== null && typeof value === "object" && !Array.isArray(value);
256
+ }
257
+
258
+ function isUnknownArray(value: unknown): value is readonly unknown[] {
259
+ return Array.isArray(value);
260
+ }
261
+
262
+ function verdictOrNull(value: string): Verdict | null {
263
+ switch (value) {
264
+ case "live-ui-verified":
265
+ case "unit-test-verified":
266
+ case "type-check-only":
267
+ case "verifier-blocked":
268
+ case "verifier-failed":
269
+ return value;
270
+ default:
271
+ return null;
272
+ }
273
+ }
274
+
275
+ function frontierPrStateOrNull(value: unknown): FrontierPrState | null {
276
+ switch (value) {
277
+ case "OPEN":
278
+ case "MERGED":
279
+ case "CLOSED":
280
+ return value;
281
+ default:
282
+ return null;
283
+ }
284
+ }
285
+
286
+ export function parseVerdict(value: string): Verdict {
287
+ const verdict = verdictOrNull(value);
288
+ if (verdict === null) {
289
+ throw new UserError(
290
+ "verdict must be live-ui-verified, unit-test-verified, type-check-only, verifier-blocked, or verifier-failed"
291
+ );
292
+ }
293
+ return verdict;
294
+ }
295
+
296
+ function cleanCell(value: string): string {
297
+ const cleaned = value.replace(/[\t\n\r]/g, " ");
298
+ return /^[=+\-@]/.test(cleaned) ? `'${cleaned}` : cleaned;
299
+ }
300
+
301
+ function requiredCell(value: string, label: string): string {
302
+ const cleaned = cleanCell(value);
303
+ if (cleaned.trim().length === 0) {
304
+ throw new UserError(`${label} must not be empty`);
305
+ }
306
+ return cleaned;
307
+ }
308
+
309
+ function requiredLine(value: string, label: string): string {
310
+ const cleaned = value.replace(/[\n\r]/g, " ").trim();
311
+ if (cleaned.length === 0) {
312
+ throw new UserError(`${label} must not be empty`);
313
+ }
314
+ return cleaned;
315
+ }
316
+
317
+ function positiveInteger(value: number, label: string): number {
318
+ if (!Number.isSafeInteger(value) || value < 1) {
319
+ throw new UserError(`${label} must be a positive integer`);
320
+ }
321
+ return value;
322
+ }
323
+
324
+ async function exists(path: string): Promise<boolean> {
325
+ try {
326
+ await access(path);
327
+ return true;
328
+ } catch (error) {
329
+ if (errorCode(error) === "ENOENT") {
330
+ return false;
331
+ }
332
+ throw error;
333
+ }
334
+ }
335
+
336
+ async function atomicWrite(path: string, contents: string): Promise<void> {
337
+ const temporary = join(
338
+ dirname(path),
339
+ `.${basename(path)}.${process.pid}.${randomUUID()}.tmp`
340
+ );
341
+ try {
342
+ await writeFile(temporary, contents, { flag: "wx" });
343
+ await rename(temporary, path);
344
+ } finally {
345
+ await rm(temporary, { force: true });
346
+ }
347
+ }
348
+
349
+ async function writeIfMissing(path: string, contents: string): Promise<void> {
350
+ if (!(await exists(path))) {
351
+ await atomicWrite(path, contents);
352
+ }
353
+ }
354
+
355
+ async function requiredFile(path: string): Promise<string> {
356
+ try {
357
+ return await readFile(path, "utf8");
358
+ } catch (error) {
359
+ if (errorCode(error) === "ENOENT") {
360
+ throw new UserError(
361
+ `store is not initialized at ${dirname(path)}; run orch init`
362
+ );
363
+ }
364
+ throw error;
365
+ }
366
+ }
367
+
368
+ function holderIsDead(holder: string): boolean {
369
+ const pid = Number.parseInt(holder, 10);
370
+ if (!Number.isSafeInteger(pid) || pid <= 0 || String(pid) !== holder) {
371
+ return false;
372
+ }
373
+ try {
374
+ process.kill(pid, 0);
375
+ return false;
376
+ } catch (error) {
377
+ return errorCode(error) === "ESRCH";
378
+ }
379
+ }
380
+
381
+ async function acquireLock(
382
+ store: string,
383
+ options: OpenStoreOptions
384
+ ): Promise<() => Promise<void>> {
385
+ const path = join(store, LOCK_FILE);
386
+ const pid = String(process.pid);
387
+ const create = async (): Promise<void> => {
388
+ const handle = await open(path, "wx");
389
+ await handle.writeFile(`${pid}\n`);
390
+ await handle.close();
391
+ };
392
+
393
+ const takeOver = async (): Promise<void> => {
394
+ await unlink(path);
395
+ try {
396
+ await create();
397
+ } catch (retryError) {
398
+ if (errorCode(retryError) === "EEXIST") {
399
+ const retryHolder =
400
+ (await readFile(path, "utf8")).trim() || "unknown";
401
+ throw new UserError(`store lock held by pid ${retryHolder}`);
402
+ }
403
+ throw retryError;
404
+ }
405
+ };
406
+
407
+ try {
408
+ await create();
409
+ } catch (error) {
410
+ if (errorCode(error) !== "EEXIST") {
411
+ throw error;
412
+ }
413
+ let holder = "unknown";
414
+ try {
415
+ holder = (await readFile(path, "utf8")).trim() || "unknown";
416
+ } catch {
417
+ holder = "unknown";
418
+ }
419
+ if (holderIsDead(holder)) {
420
+ options.onStaleLock?.(holder);
421
+ await takeOver();
422
+ } else if (options.force) {
423
+ options.onLockStolen?.(holder);
424
+ await takeOver();
425
+ } else {
426
+ throw new UserError(`store lock held by pid ${holder}`);
427
+ }
428
+ }
429
+
430
+ return async (): Promise<void> => {
431
+ try {
432
+ if ((await readFile(path, "utf8")).trim() === pid) {
433
+ await unlink(path);
434
+ }
435
+ } catch (error) {
436
+ if (errorCode(error) !== "ENOENT") {
437
+ throw error;
438
+ }
439
+ }
440
+ };
441
+ }
442
+
443
+ async function readTsv(
444
+ path: string,
445
+ header: string,
446
+ width: number
447
+ ): Promise<readonly (readonly string[])[]> {
448
+ const lines = (await requiredFile(path)).replace(/\r/g, "").split("\n");
449
+ if (lines.shift() !== header) {
450
+ throw new UserError(`${basename(path)} has an invalid header`);
451
+ }
452
+ return lines
453
+ .filter((value) => value.length > 0)
454
+ .map((value) => {
455
+ const cells = value.split("\t");
456
+ if (cells.length !== width) {
457
+ throw new UserError(`${basename(path)} has a malformed row`);
458
+ }
459
+ return cells;
460
+ });
461
+ }
462
+
463
+ async function writeTsv(
464
+ path: string,
465
+ header: string,
466
+ rows: readonly (readonly string[])[]
467
+ ): Promise<void> {
468
+ const body = rows.map((row) => row.map(cleanCell).join("\t")).join("\n");
469
+ await atomicWrite(path, `${header}\n${body}${body.length > 0 ? "\n" : ""}`);
470
+ }
471
+
472
+ async function readUnits(store: string): Promise<readonly Unit[]> {
473
+ return (await readTsv(join(store, "units.tsv"), UNIT_HEADER, 7)).map(
474
+ (row) => ({
475
+ id: row[0] ?? "",
476
+ track: row[1] ?? "",
477
+ state: row[2] ?? "",
478
+ branch: row[3] ?? "",
479
+ pr: row[4] ?? "",
480
+ sha: row[5] ?? "",
481
+ brief: row[6] ?? "",
482
+ })
483
+ );
484
+ }
485
+
486
+ function unitCells(unit: Unit): readonly string[] {
487
+ return [
488
+ unit.id,
489
+ unit.track,
490
+ unit.state,
491
+ unit.branch,
492
+ unit.pr,
493
+ unit.sha,
494
+ unit.brief,
495
+ ];
496
+ }
497
+
498
+ async function saveUnits(store: string, rows: readonly Unit[]): Promise<void> {
499
+ await writeTsv(
500
+ join(store, "units.tsv"),
501
+ UNIT_HEADER,
502
+ rows.map(unitCells)
503
+ );
504
+ }
505
+
506
+ async function readLedger(store: string): Promise<readonly LedgerEntry[]> {
507
+ return (await readTsv(join(store, "ledger.tsv"), LEDGER_HEADER, 6)).map(
508
+ (row) => {
509
+ const rawVerdict = row[2] ?? "";
510
+ const verdict = verdictOrNull(rawVerdict);
511
+ if (verdict === null) {
512
+ throw new UserError(`ledger.tsv has invalid verdict ${rawVerdict}`);
513
+ }
514
+ return {
515
+ pr: row[0] ?? "",
516
+ sha: row[1] ?? "",
517
+ verdict,
518
+ evidence: row[3] ?? "",
519
+ verifier: row[4] ?? "",
520
+ ts: row[5] ?? "",
521
+ };
522
+ }
523
+ );
524
+ }
525
+
526
+ function ledgerCells(row: LedgerEntry): readonly string[] {
527
+ return [
528
+ row.pr,
529
+ row.sha,
530
+ row.verdict,
531
+ row.evidence,
532
+ row.verifier,
533
+ row.ts,
534
+ ];
535
+ }
536
+
537
+ async function saveLedger(
538
+ store: string,
539
+ rows: readonly LedgerEntry[]
540
+ ): Promise<void> {
541
+ await writeTsv(
542
+ join(store, "ledger.tsv"),
543
+ LEDGER_HEADER,
544
+ rows.map(ledgerCells)
545
+ );
546
+ }
547
+
548
+ function pointerCells(pointer: InboxPointer): readonly string[] {
549
+ return [
550
+ pointer.ts,
551
+ pointer.agent,
552
+ pointer.unit,
553
+ pointer.status,
554
+ pointer.report,
555
+ ];
556
+ }
557
+
558
+ async function readPointers(
559
+ directory: string
560
+ ): Promise<readonly InboxPointer[]> {
561
+ let entries: Dirent[];
562
+ try {
563
+ entries = await readdir(directory, { withFileTypes: true });
564
+ } catch (error) {
565
+ if (errorCode(error) === "ENOENT") {
566
+ throw new UserError(
567
+ `store is not initialized at ${dirname(directory)}; run orch init`
568
+ );
569
+ }
570
+ throw error;
571
+ }
572
+ const result: InboxPointer[] = [];
573
+ const files = entries
574
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".tsv"))
575
+ .sort((left, right) => left.name.localeCompare(right.name));
576
+ for (const entry of files) {
577
+ const raw = (await readFile(join(directory, entry.name), "utf8")).replace(
578
+ /\r?\n$/,
579
+ ""
580
+ );
581
+ const row = raw.split("\t");
582
+ if (/[\r\n]/.test(raw) || row.length !== 5) {
583
+ throw new UserError(`inbox pointer ${entry.name} is malformed`);
584
+ }
585
+ result.push({
586
+ ts: row[0] ?? "",
587
+ agent: row[1] ?? "",
588
+ unit: row[2] ?? "",
589
+ status: row[3] ?? "",
590
+ report: row[4] ?? "",
591
+ });
592
+ }
593
+ return result;
594
+ }
595
+
596
+ function renderGates(rows: readonly Gate[]): string {
597
+ if (rows.length === 0) {
598
+ return "";
599
+ }
600
+ const blocks = rows.map((gate) => {
601
+ const answer =
602
+ gate.kind === "resolved" ? `\n- Answer: ${gate.answer}` : "";
603
+ return `## ${gate.id}
604
+
605
+ - Status: ${gate.kind}
606
+ - Question: ${gate.question}
607
+ - Options: ${gate.options}
608
+ - Default: ${gate.defaultAnswer}${answer}`;
609
+ });
610
+ return `# Gates\n\n${blocks.join("\n\n")}\n`;
611
+ }
612
+
613
+ async function readGates(store: string): Promise<readonly Gate[]> {
614
+ const raw = (await requiredFile(join(store, "gates.md")))
615
+ .replace(/\r/g, "")
616
+ .trim();
617
+ if (raw.length === 0) {
618
+ return [];
619
+ }
620
+ const prefix = "# Gates\n\n## ";
621
+ if (!raw.startsWith(prefix)) {
622
+ throw new UserError("gates.md has an invalid heading");
623
+ }
624
+ const result: Gate[] = [];
625
+ for (const block of raw.slice(prefix.length).split("\n\n## ")) {
626
+ const lines = block.split("\n").filter((value) => value.length > 0);
627
+ const id = lines.shift() ?? "";
628
+ const fields = new Map<string, string>();
629
+ for (const value of lines) {
630
+ const match = /^- ([^:]+): (.*)$/.exec(value);
631
+ if (match === null) {
632
+ throw new UserError(`gates.md has a malformed gate ${id}`);
633
+ }
634
+ fields.set(match[1] ?? "", match[2] ?? "");
635
+ }
636
+ const status = fields.get("Status");
637
+ const question = fields.get("Question");
638
+ const options = fields.get("Options");
639
+ const defaultAnswer = fields.get("Default");
640
+ if (
641
+ id.length === 0 ||
642
+ question === undefined ||
643
+ options === undefined ||
644
+ defaultAnswer === undefined
645
+ ) {
646
+ throw new UserError(`gates.md has a malformed gate ${id}`);
647
+ }
648
+ if (status === "open") {
649
+ result.push({ kind: "open", id, question, options, defaultAnswer });
650
+ } else if (status === "resolved" && fields.has("Answer")) {
651
+ result.push({
652
+ kind: "resolved",
653
+ id,
654
+ question,
655
+ options,
656
+ defaultAnswer,
657
+ answer: fields.get("Answer") ?? "",
658
+ });
659
+ } else {
660
+ throw new UserError(`gates.md has invalid status ${status ?? ""}`);
661
+ }
662
+ }
663
+ if (new Set(result.map((gate) => gate.id)).size !== result.length) {
664
+ throw new UserError("gates.md has duplicate gate ids");
665
+ }
666
+ return result;
667
+ }
668
+
669
+ function parseFrontier(raw: string): Frontier {
670
+ let value: unknown;
671
+ try {
672
+ value = JSON.parse(raw);
673
+ } catch {
674
+ throw new UserError("frontier.json is not valid JSON");
675
+ }
676
+ if (!isRecord(value)) {
677
+ throw new UserError("frontier.json must contain an object");
678
+ }
679
+ if (Object.keys(value).length === 0) {
680
+ return { generation: 0, prs: [], lowestUnmerged: null };
681
+ }
682
+ if (
683
+ typeof value.generation !== "number" ||
684
+ !Number.isSafeInteger(value.generation) ||
685
+ value.generation < 0 ||
686
+ !isUnknownArray(value.prs) ||
687
+ !(
688
+ value.lowestUnmerged === null ||
689
+ (typeof value.lowestUnmerged === "number" &&
690
+ Number.isSafeInteger(value.lowestUnmerged))
691
+ )
692
+ ) {
693
+ throw new UserError("frontier.json has an invalid shape");
694
+ }
695
+ const prs: FrontierPr[] = [];
696
+ for (const row of value.prs) {
697
+ const state = isRecord(row)
698
+ ? frontierPrStateOrNull(row.state)
699
+ : null;
700
+ if (
701
+ !isRecord(row) ||
702
+ typeof row.pr !== "number" ||
703
+ !Number.isSafeInteger(row.pr) ||
704
+ row.pr < 1 ||
705
+ typeof row.branches !== "string" ||
706
+ row.branches.length === 0 ||
707
+ typeof row.sha !== "string" ||
708
+ state === null
709
+ ) {
710
+ throw new UserError("frontier.json has an invalid PR row");
711
+ }
712
+ prs.push({
713
+ pr: row.pr,
714
+ branches: row.branches,
715
+ sha: row.sha,
716
+ state,
717
+ });
718
+ }
719
+ return {
720
+ generation: value.generation,
721
+ prs,
722
+ lowestUnmerged: value.lowestUnmerged,
723
+ };
724
+ }
725
+
726
+ async function readFrontier(store: string): Promise<Frontier> {
727
+ return parseFrontier(await requiredFile(join(store, "frontier.json")));
728
+ }
729
+
730
+ async function readStanding(
731
+ store: string
732
+ ): Promise<readonly StandingLine[]> {
733
+ const raw = (await requiredFile(join(store, "preferences.md"))).replace(
734
+ /\r/g,
735
+ ""
736
+ );
737
+ if (raw.trim().length === 0) {
738
+ return [];
739
+ }
740
+ const result: StandingLine[] = [];
741
+ for (const value of raw.split("\n").filter((item) => item.length > 0)) {
742
+ const match = /^([1-9]\d*)\. (.+)$/.exec(value);
743
+ const number = Number(match?.[1] ?? 0);
744
+ if (match === null || number !== result.length + 1) {
745
+ throw new UserError("preferences.md has malformed numbering");
746
+ }
747
+ result.push({ number, line: match[2] ?? "" });
748
+ }
749
+ return result;
750
+ }
751
+
752
+ function countValues(values: readonly string[]): Counts {
753
+ const result: Record<string, number> = {};
754
+ for (const value of values) {
755
+ result[value] = (result[value] ?? 0) + 1;
756
+ }
757
+ return Object.fromEntries(
758
+ Object.entries(result).sort(([left], [right]) =>
759
+ left.localeCompare(right)
760
+ )
761
+ );
762
+ }
763
+
764
+ function summarize(
765
+ unitRows: readonly Unit[],
766
+ ledgerRows: readonly LedgerEntry[],
767
+ currentFrontier: Frontier,
768
+ gateRows: readonly Gate[]
769
+ ): StatusSummary {
770
+ return {
771
+ unitStates: countValues(unitRows.map((unit) => unit.state)),
772
+ ledgerVerdicts: countValues(ledgerRows.map((row) => row.verdict)),
773
+ frontierGeneration: currentFrontier.generation,
774
+ openGateIds: gateRows
775
+ .filter((gate): gate is OpenGate => gate.kind === "open")
776
+ .map((gate) => gate.id)
777
+ .sort(),
778
+ };
779
+ }
780
+
781
+ function countRecord(value: unknown): Record<string, number> | null {
782
+ if (!isRecord(value)) {
783
+ return null;
784
+ }
785
+ const result: Record<string, number> = {};
786
+ for (const [name, count] of Object.entries(value)) {
787
+ if (
788
+ typeof count !== "number" ||
789
+ !Number.isSafeInteger(count) ||
790
+ count < 0
791
+ ) {
792
+ return null;
793
+ }
794
+ result[name] = count;
795
+ }
796
+ return result;
797
+ }
798
+
799
+ function previousSummary(raw: string): StatusSummary | null {
800
+ const match = /<!-- orch-summary (.+) -->/.exec(raw);
801
+ if (match === null) {
802
+ return null;
803
+ }
804
+ let value: unknown;
805
+ try {
806
+ value = JSON.parse(match[1] ?? "");
807
+ } catch {
808
+ return null;
809
+ }
810
+ if (
811
+ !isRecord(value) ||
812
+ typeof value.frontierGeneration !== "number" ||
813
+ !isUnknownArray(value.openGateIds)
814
+ ) {
815
+ return null;
816
+ }
817
+ const unitStates = countRecord(value.unitStates);
818
+ const ledgerVerdicts = countRecord(value.ledgerVerdicts);
819
+ const openGateIds = value.openGateIds.filter(
820
+ (item): item is string => typeof item === "string"
821
+ );
822
+ if (
823
+ unitStates === null ||
824
+ ledgerVerdicts === null ||
825
+ openGateIds.length !== value.openGateIds.length
826
+ ) {
827
+ return null;
828
+ }
829
+ return {
830
+ unitStates,
831
+ ledgerVerdicts,
832
+ frontierGeneration: value.frontierGeneration,
833
+ openGateIds,
834
+ };
835
+ }
836
+
837
+ function changed(before: StatusSummary | null, after: StatusSummary): string {
838
+ if (before === null) {
839
+ return "first render";
840
+ }
841
+ const result: string[] = [];
842
+ const groups: readonly {
843
+ readonly label: string;
844
+ readonly oldCounts: Counts;
845
+ readonly newCounts: Counts;
846
+ }[] = [
847
+ {
848
+ label: "units",
849
+ oldCounts: before.unitStates,
850
+ newCounts: after.unitStates,
851
+ },
852
+ {
853
+ label: "ledger",
854
+ oldCounts: before.ledgerVerdicts,
855
+ newCounts: after.ledgerVerdicts,
856
+ },
857
+ ];
858
+ for (const { label, oldCounts, newCounts } of groups) {
859
+ const names = [
860
+ ...new Set([...Object.keys(oldCounts), ...Object.keys(newCounts)]),
861
+ ].sort();
862
+ for (const name of names) {
863
+ const oldCount = oldCounts[name] ?? 0;
864
+ const newCount = newCounts[name] ?? 0;
865
+ if (oldCount !== newCount) {
866
+ result.push(`${label} ${name} ${oldCount}->${newCount}`);
867
+ }
868
+ }
869
+ }
870
+ if (before.frontierGeneration !== after.frontierGeneration) {
871
+ result.push(
872
+ `frontier generation ${before.frontierGeneration}->${after.frontierGeneration}`
873
+ );
874
+ }
875
+ if (before.openGateIds.join("\0") !== after.openGateIds.join("\0")) {
876
+ result.push(
877
+ `open gates ${before.openGateIds.length}->${after.openGateIds.length}`
878
+ );
879
+ }
880
+ return result.length === 0 ? "no derived changes" : result.join("; ");
881
+ }
882
+
883
+ function markdown(value: string): string {
884
+ return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
885
+ }
886
+
887
+ function table(
888
+ headers: readonly string[],
889
+ rows: readonly (readonly string[])[]
890
+ ): string {
891
+ if (rows.length === 0) {
892
+ return "(none)";
893
+ }
894
+ return [
895
+ `| ${headers.join(" | ")} |`,
896
+ `| ${headers.map(() => "---").join(" | ")} |`,
897
+ ...rows.map((row) => `| ${row.map(markdown).join(" | ")} |`),
898
+ ].join("\n");
899
+ }
900
+
901
+ function statusMarkdown(
902
+ unitRows: readonly Unit[],
903
+ ledgerRows: readonly LedgerEntry[],
904
+ currentFrontier: Frontier,
905
+ gateRows: readonly Gate[],
906
+ currentSummary: StatusSummary
907
+ ): string {
908
+ return `# Orchestrate status
909
+
910
+ Generated: ${new Date().toISOString()}
911
+
912
+ ## Units
913
+
914
+ States: ${countLine(currentSummary.unitStates)}
915
+
916
+ ${table(
917
+ ["ID", "Track", "State", "Branch", "PR", "SHA", "Brief"],
918
+ unitRows.map(unitCells)
919
+ )}
920
+
921
+ ## Verification ledger
922
+
923
+ Verdicts: ${countLine(currentSummary.ledgerVerdicts)}
924
+
925
+ ${table(
926
+ ["PR", "SHA", "Verdict", "Evidence", "Verifier", "Timestamp"],
927
+ ledgerRows.map(ledgerCells)
928
+ )}
929
+
930
+ ## Frontier
931
+
932
+ Generation: ${currentFrontier.generation}
933
+ Lowest unmerged: ${currentFrontier.lowestUnmerged ?? "none"}
934
+
935
+ ${table(
936
+ ["Branch", "PR", "SHA", "State"],
937
+ currentFrontier.prs.map((row) => [
938
+ row.branches,
939
+ String(row.pr),
940
+ row.sha,
941
+ row.state,
942
+ ])
943
+ )}
944
+
945
+ ## Gates
946
+
947
+ ${table(
948
+ ["ID", "Status", "Question", "Options", "Default", "Answer"],
949
+ gateRows.map((gate) => [
950
+ gate.id,
951
+ gate.kind,
952
+ gate.question,
953
+ gate.options,
954
+ gate.defaultAnswer,
955
+ gate.kind === "resolved" ? gate.answer : "",
956
+ ])
957
+ )}
958
+
959
+ <!-- orch-summary ${JSON.stringify(currentSummary)} -->
960
+ `;
961
+ }
962
+
963
+ function countLine(value: Counts): string {
964
+ const entries = Object.entries(value);
965
+ return entries.length === 0
966
+ ? "none"
967
+ : entries.map(([name, count]) => `${name}=${count}`).join(", ");
968
+ }
969
+
970
+ export type Forge = "gh" | "origin";
971
+
972
+ interface ForgePullRequestQuery {
973
+ readonly stateField: string;
974
+ readonly headField: string;
975
+ readonly baseField: string;
976
+ readonly states: ReadonlyMap<string, FrontierPrState>;
977
+ }
978
+
979
+ const FORGE_PULL_REQUEST_QUERIES: Readonly<
980
+ Record<Forge, ForgePullRequestQuery>
981
+ > = {
982
+ gh: {
983
+ stateField: "state",
984
+ headField: "headRefName",
985
+ baseField: "baseRefName",
986
+ states: new Map<string, FrontierPrState>([
987
+ ["OPEN", "OPEN"],
988
+ ["MERGED", "MERGED"],
989
+ ["CLOSED", "CLOSED"],
990
+ ]),
991
+ },
992
+ origin: {
993
+ stateField: "status",
994
+ headField: "headRef",
995
+ baseField: "baseRef",
996
+ states: new Map<string, FrontierPrState>([
997
+ ["open", "OPEN"],
998
+ ["draft", "OPEN"],
999
+ ["merged", "MERGED"],
1000
+ ["closed", "CLOSED"],
1001
+ ]),
1002
+ },
1003
+ };
1004
+
1005
+ export function parseForge(value: string): Forge {
1006
+ if (value === "gh" || value === "origin") {
1007
+ return value;
1008
+ }
1009
+ throw new UserError("forge must be gh or origin");
1010
+ }
1011
+
1012
+ interface ForgePullRequest {
1013
+ readonly pr: number;
1014
+ readonly state: FrontierPrState;
1015
+ readonly branch: string;
1016
+ readonly base: string;
1017
+ }
1018
+
1019
+ function runTool({
1020
+ command,
1021
+ args,
1022
+ repo,
1023
+ }: {
1024
+ command: string;
1025
+ args: readonly string[];
1026
+ repo: string;
1027
+ }): string {
1028
+ try {
1029
+ return execFileSync(command, args, {
1030
+ cwd: repo,
1031
+ encoding: "utf8",
1032
+ env: process.env,
1033
+ stdio: ["ignore", "pipe", "pipe"],
1034
+ });
1035
+ } catch (error) {
1036
+ throw new UserError(
1037
+ `${command} ${args.join(" ")} failed: ${errorMessage(error)}`
1038
+ );
1039
+ }
1040
+ }
1041
+
1042
+ function listPullRequests({
1043
+ filter,
1044
+ branch,
1045
+ forge,
1046
+ repo,
1047
+ }: {
1048
+ filter: "--head" | "--base";
1049
+ branch: string;
1050
+ forge: Forge;
1051
+ repo: string;
1052
+ }): readonly ForgePullRequest[] {
1053
+ const query = FORGE_PULL_REQUEST_QUERIES[forge];
1054
+ const command = `${forge} pr list ${filter} ${branch}`;
1055
+ // Every state is fetched because a merged or closed PR still anchors the
1056
+ // chain, and a draft counts as OPEN.
1057
+ const raw = runTool({
1058
+ command: forge,
1059
+ args: [
1060
+ "pr",
1061
+ "list",
1062
+ filter,
1063
+ branch,
1064
+ "--state",
1065
+ "all",
1066
+ "--json",
1067
+ ["number", query.stateField, query.headField, query.baseField].join(
1068
+ ","
1069
+ ),
1070
+ ],
1071
+ repo,
1072
+ });
1073
+ let value: unknown;
1074
+ try {
1075
+ value = JSON.parse(raw);
1076
+ } catch {
1077
+ throw new UserError(`${command} output is not valid JSON`);
1078
+ }
1079
+ if (!isUnknownArray(value)) {
1080
+ throw new UserError(`${command} output is not a JSON array`);
1081
+ }
1082
+ return value.map((row) => {
1083
+ const status = isRecord(row) ? row[query.stateField] : undefined;
1084
+ const state =
1085
+ typeof status === "string" ? (query.states.get(status) ?? null) : null;
1086
+ const head = isRecord(row) ? row[query.headField] : undefined;
1087
+ const base = isRecord(row) ? row[query.baseField] : undefined;
1088
+ if (
1089
+ !isRecord(row) ||
1090
+ typeof row.number !== "number" ||
1091
+ !Number.isSafeInteger(row.number) ||
1092
+ row.number < 1 ||
1093
+ state === null ||
1094
+ typeof head !== "string" ||
1095
+ head.length === 0 ||
1096
+ typeof base !== "string" ||
1097
+ base.length === 0
1098
+ ) {
1099
+ throw new UserError(`${command} output has an invalid PR row`);
1100
+ }
1101
+ return { pr: row.number, state, branch: head, base };
1102
+ });
1103
+ }
1104
+
1105
+ function branchPullRequest({
1106
+ branch,
1107
+ forge,
1108
+ repo,
1109
+ }: {
1110
+ branch: string;
1111
+ forge: Forge;
1112
+ repo: string;
1113
+ }): ForgePullRequest {
1114
+ const rows = listPullRequests({ filter: "--head", branch, forge, repo });
1115
+ const row = rows[0];
1116
+ if (row === undefined) {
1117
+ throw new UserError(
1118
+ `branch ${branch} has no pull request; open one with ${forge} pr create, or resolve the frontier from a branch whose chain is fully submitted`
1119
+ );
1120
+ }
1121
+ if (rows.length > 1) {
1122
+ throw new UserError(
1123
+ `branch ${branch} has multiple pull requests (${rows.map((item) => `#${item.pr}`).join(", ")}); close the stale ones first`
1124
+ );
1125
+ }
1126
+ return row;
1127
+ }
1128
+
1129
+ function openChildren({
1130
+ branch,
1131
+ forge,
1132
+ repo,
1133
+ }: {
1134
+ branch: string;
1135
+ forge: Forge;
1136
+ repo: string;
1137
+ }): readonly ForgePullRequest[] {
1138
+ return listPullRequests({ filter: "--base", branch, forge, repo }).filter(
1139
+ (row) => row.state === "OPEN"
1140
+ );
1141
+ }
1142
+
1143
+ function runGit({
1144
+ args,
1145
+ repo,
1146
+ }: {
1147
+ args: readonly string[];
1148
+ repo: string;
1149
+ }): string {
1150
+ return runTool({ command: "git", args, repo }).trim();
1151
+ }
1152
+
1153
+ function branchSha({
1154
+ branch,
1155
+ repo,
1156
+ }: {
1157
+ branch: string;
1158
+ repo: string;
1159
+ }): string {
1160
+ const sha = runGit({ args: ["rev-parse", branch], repo });
1161
+ if (!/^[0-9a-f]{40,64}$/i.test(sha)) {
1162
+ throw new UserError(`git rev-parse ${branch} returned an invalid SHA`);
1163
+ }
1164
+ return sha;
1165
+ }
1166
+
1167
+ function currentBranch(repo: string): string {
1168
+ return runGit({ args: ["symbolic-ref", "--short", "HEAD"], repo });
1169
+ }
1170
+
1171
+ function trunkBranch(repo: string): string {
1172
+ try {
1173
+ return runGit({
1174
+ args: ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
1175
+ repo,
1176
+ }).replace(/^origin\//, "");
1177
+ } catch {
1178
+ // Init-plus-fetch checkouts and local fixtures lack origin/HEAD.
1179
+ return "main";
1180
+ }
1181
+ }
1182
+
1183
+ function resolveFrontier(repo: string, forge: Forge): readonly FrontierPr[] {
1184
+ const trunk = trunkBranch(repo);
1185
+ const current = currentBranch(repo);
1186
+ if (current === trunk) {
1187
+ return [];
1188
+ }
1189
+ const seen = new Set([current]);
1190
+ let bottom = branchPullRequest({ branch: current, forge, repo });
1191
+ const chain = [bottom];
1192
+ while (bottom.base !== trunk) {
1193
+ if (seen.has(bottom.base)) {
1194
+ throw new UserError(
1195
+ `pull request base chain loops at branch ${bottom.base}`
1196
+ );
1197
+ }
1198
+ seen.add(bottom.base);
1199
+ bottom = branchPullRequest({ branch: bottom.base, forge, repo });
1200
+ chain.unshift(bottom);
1201
+ }
1202
+ let tip = current;
1203
+ for (;;) {
1204
+ const children = openChildren({ branch: tip, forge, repo });
1205
+ const child = children[0];
1206
+ if (child === undefined) {
1207
+ break;
1208
+ }
1209
+ if (children.length > 1) {
1210
+ throw new UserError(
1211
+ `stack is not linear: branches ${children.map((row) => row.branch).join(", ")} all target ${tip}`
1212
+ );
1213
+ }
1214
+ if (seen.has(child.branch)) {
1215
+ throw new UserError(
1216
+ `pull request base chain loops at branch ${child.branch}`
1217
+ );
1218
+ }
1219
+ seen.add(child.branch);
1220
+ chain.push(child);
1221
+ tip = child.branch;
1222
+ }
1223
+ if (new Set(chain.map((row) => row.pr)).size !== chain.length) {
1224
+ throw new UserError(
1225
+ "pull request base chain contains duplicate pull requests"
1226
+ );
1227
+ }
1228
+ return chain.map((row) => ({
1229
+ pr: row.pr,
1230
+ branches: row.branch,
1231
+ sha: branchSha({ branch: row.branch, repo }),
1232
+ state: row.state,
1233
+ }));
1234
+ }
1235
+
1236
+ function validateFrontierPin({
1237
+ actual,
1238
+ expected,
1239
+ }: {
1240
+ actual: readonly number[];
1241
+ expected: readonly number[];
1242
+ }): void {
1243
+ if (
1244
+ actual.length === expected.length &&
1245
+ actual.every((pr, index) => pr === expected[index])
1246
+ ) {
1247
+ return;
1248
+ }
1249
+ const actualSet = new Set(actual);
1250
+ const expectedSet = new Set(expected);
1251
+ const missing = expected.filter((pr) => !actualSet.has(pr));
1252
+ const extra = actual.filter((pr) => !expectedSet.has(pr));
1253
+ const drift: string[] = [];
1254
+ if (missing.length > 0) {
1255
+ drift.push(`missing from resolved chain: ${missing.join(",")}`);
1256
+ }
1257
+ if (extra.length > 0) {
1258
+ drift.push(`extra in resolved chain: ${extra.join(",")}`);
1259
+ }
1260
+ if (missing.length === 0 && extra.length === 0) {
1261
+ drift.push(
1262
+ `order differs: expected ${expected.join(",")}; resolved ${actual.join(",")}`
1263
+ );
1264
+ }
1265
+ throw new UserError(`frontier pin mismatch: ${drift.join("; ")}`);
1266
+ }
1267
+
1268
+ export function openStore(
1269
+ directory: string,
1270
+ options: OpenStoreOptions = {}
1271
+ ): Store {
1272
+ const store = resolve(directory);
1273
+ let closed = false;
1274
+ let releaseLock: (() => Promise<void>) | null = null;
1275
+ let lockRequest: Promise<void> | null = null;
1276
+
1277
+ const ensureOpen = (): void => {
1278
+ if (closed) {
1279
+ throw new UserError("store is closed");
1280
+ }
1281
+ };
1282
+
1283
+ const ensureLock = async (): Promise<void> => {
1284
+ ensureOpen();
1285
+ if (releaseLock !== null) {
1286
+ return;
1287
+ }
1288
+ if (lockRequest === null) {
1289
+ lockRequest = acquireLock(store, options).then((release) => {
1290
+ releaseLock = release;
1291
+ });
1292
+ }
1293
+ try {
1294
+ await lockRequest;
1295
+ } catch (error) {
1296
+ lockRequest = null;
1297
+ throw error;
1298
+ }
1299
+ };
1300
+
1301
+ const beginWrite = async (): Promise<void> => {
1302
+ ensureOpen();
1303
+ if (!(await exists(store))) {
1304
+ throw new UserError(
1305
+ `store is not initialized at ${store}; run orch init`
1306
+ );
1307
+ }
1308
+ await ensureLock();
1309
+ };
1310
+
1311
+ return {
1312
+ units: {
1313
+ add: async (params) => {
1314
+ await beginWrite();
1315
+ const row: Unit = {
1316
+ id: requiredCell(params.id, "unit id"),
1317
+ track: requiredCell(params.track, "track"),
1318
+ state: "pending",
1319
+ branch: "",
1320
+ pr: "",
1321
+ sha: "",
1322
+ brief:
1323
+ params.brief === undefined
1324
+ ? ""
1325
+ : requiredCell(params.brief, "brief"),
1326
+ };
1327
+ const rows = [...(await readUnits(store))];
1328
+ if (rows.some((unit) => unit.id === row.id)) {
1329
+ throw new UserError(`unit ${row.id} already exists`);
1330
+ }
1331
+ rows.push(row);
1332
+ await saveUnits(store, rows);
1333
+ return row;
1334
+ },
1335
+ set: async (params) => {
1336
+ await beginWrite();
1337
+ const id = requiredCell(params.id, "unit id");
1338
+ const state = requiredCell(params.state, "state");
1339
+ const rows = [...(await readUnits(store))];
1340
+ const index = rows.findIndex((unit) => unit.id === id);
1341
+ const old = rows[index];
1342
+ if (index < 0 || old === undefined) {
1343
+ throw new NotFoundError(`unit ${id} not found`);
1344
+ }
1345
+ const row: Unit = {
1346
+ ...old,
1347
+ state,
1348
+ branch:
1349
+ params.branch === undefined
1350
+ ? old.branch
1351
+ : requiredCell(params.branch, "branch"),
1352
+ pr:
1353
+ params.pr === undefined
1354
+ ? old.pr
1355
+ : String(positiveInteger(params.pr, "PR")),
1356
+ sha:
1357
+ params.sha === undefined
1358
+ ? old.sha
1359
+ : requiredCell(params.sha, "SHA"),
1360
+ };
1361
+ rows[index] = row;
1362
+ await saveUnits(store, rows);
1363
+ return row;
1364
+ },
1365
+ get: async (id) => {
1366
+ ensureOpen();
1367
+ const cleanId = requiredCell(id, "unit id");
1368
+ const row = (await readUnits(store)).find(
1369
+ (unit) => unit.id === cleanId
1370
+ );
1371
+ if (row === undefined) {
1372
+ throw new NotFoundError(`unit ${cleanId} not found`);
1373
+ }
1374
+ return row;
1375
+ },
1376
+ list: async (params = {}) => {
1377
+ ensureOpen();
1378
+ const state =
1379
+ params.state === undefined
1380
+ ? undefined
1381
+ : requiredCell(params.state, "state");
1382
+ const track =
1383
+ params.track === undefined
1384
+ ? undefined
1385
+ : requiredCell(params.track, "track");
1386
+ return (await readUnits(store)).filter(
1387
+ (unit) =>
1388
+ (state === undefined || unit.state === state) &&
1389
+ (track === undefined || unit.track === track)
1390
+ );
1391
+ },
1392
+ counts: async () => {
1393
+ ensureOpen();
1394
+ return countValues(
1395
+ (await readUnits(store)).map((unit) => unit.state)
1396
+ );
1397
+ },
1398
+ },
1399
+ ledger: {
1400
+ record: async (params) => {
1401
+ await beginWrite();
1402
+ const verdict = parseVerdict(params.verdict);
1403
+ const row: LedgerEntry = {
1404
+ pr: String(positiveInteger(params.pr, "PR")),
1405
+ sha: requiredCell(params.sha, "SHA"),
1406
+ verdict,
1407
+ evidence: requiredCell(params.evidence, "evidence"),
1408
+ verifier:
1409
+ params.verifier === undefined
1410
+ ? ""
1411
+ : requiredCell(params.verifier, "verifier"),
1412
+ ts: new Date().toISOString(),
1413
+ };
1414
+ const rows = [...(await readLedger(store))];
1415
+ const index = rows.findIndex(
1416
+ (old) => old.pr === row.pr && old.sha === row.sha
1417
+ );
1418
+ if (index < 0) {
1419
+ rows.push(row);
1420
+ } else {
1421
+ rows[index] = row;
1422
+ }
1423
+ await saveLedger(store, rows);
1424
+ return row;
1425
+ },
1426
+ check: async (params) => {
1427
+ ensureOpen();
1428
+ const pr = String(positiveInteger(params.pr, "PR"));
1429
+ const sha = requiredCell(params.sha, "SHA");
1430
+ const row = (await readLedger(store)).find(
1431
+ (value) => value.pr === pr && value.sha === sha
1432
+ );
1433
+ if (row === undefined) {
1434
+ throw new NotFoundError("NOT-VERIFIED", {
1435
+ compact: "NOT-VERIFIED",
1436
+ json: { pr, sha, verdict: "NOT-VERIFIED" },
1437
+ });
1438
+ }
1439
+ return row;
1440
+ },
1441
+ summary: async () => {
1442
+ ensureOpen();
1443
+ return countValues(
1444
+ (await readLedger(store)).map((row) => row.verdict)
1445
+ );
1446
+ },
1447
+ },
1448
+ inbox: {
1449
+ push: async (params) => {
1450
+ await beginWrite();
1451
+ const pointer: InboxPointer = {
1452
+ ts: new Date().toISOString(),
1453
+ agent: requiredCell(params.agent, "agent"),
1454
+ unit: requiredCell(params.unit, "unit"),
1455
+ status: requiredCell(params.status, "status"),
1456
+ report:
1457
+ params.report === undefined
1458
+ ? ""
1459
+ : requiredCell(params.report, "report"),
1460
+ };
1461
+ const inbox = join(store, "inbox");
1462
+ if (!(await exists(inbox))) {
1463
+ throw new UserError(
1464
+ `store is not initialized at ${store}; run orch init`
1465
+ );
1466
+ }
1467
+ const timestamp = pointer.ts.replace(/[:.]/g, "-");
1468
+ const filename = `${timestamp}-${process.pid}-${randomUUID()}.tsv`;
1469
+ const contents = `${pointerCells(pointer).map(cleanCell).join("\t")}\n`;
1470
+ await atomicWrite(join(inbox, filename), contents);
1471
+ return { pointer, filename };
1472
+ },
1473
+ drain: async () => {
1474
+ await beginWrite();
1475
+ const inbox = join(store, "inbox");
1476
+ const rows = await readPointers(inbox);
1477
+ const drained = join(
1478
+ store,
1479
+ `.inbox-drain-${process.pid}-${randomUUID()}`
1480
+ );
1481
+ await rename(inbox, drained);
1482
+ try {
1483
+ await mkdir(inbox);
1484
+ } catch (error) {
1485
+ await rename(drained, inbox);
1486
+ throw error;
1487
+ }
1488
+ await rm(drained, { recursive: true, force: true });
1489
+ return rows;
1490
+ },
1491
+ peek: async () => {
1492
+ ensureOpen();
1493
+ return readPointers(join(store, "inbox"));
1494
+ },
1495
+ count: async () => {
1496
+ ensureOpen();
1497
+ return (await readPointers(join(store, "inbox"))).length;
1498
+ },
1499
+ },
1500
+ gates: {
1501
+ park: async (params) => {
1502
+ await beginWrite();
1503
+ const gate: OpenGate = {
1504
+ kind: "open",
1505
+ id: requiredLine(params.id, "gate id"),
1506
+ question: requiredLine(params.question, "question"),
1507
+ options: requiredLine(params.options, "options"),
1508
+ defaultAnswer: requiredLine(
1509
+ params.defaultAnswer,
1510
+ "default"
1511
+ ),
1512
+ };
1513
+ const rows = [...(await readGates(store))];
1514
+ const index = rows.findIndex((old) => old.id === gate.id);
1515
+ if (index < 0) {
1516
+ rows.push(gate);
1517
+ } else {
1518
+ rows[index] = gate;
1519
+ }
1520
+ await atomicWrite(join(store, "gates.md"), renderGates(rows));
1521
+ return gate;
1522
+ },
1523
+ list: async () => {
1524
+ ensureOpen();
1525
+ return (await readGates(store)).filter(
1526
+ (gate): gate is OpenGate => gate.kind === "open"
1527
+ );
1528
+ },
1529
+ resolve: async (params) => {
1530
+ await beginWrite();
1531
+ const id = requiredLine(params.id, "gate id");
1532
+ const rows = [...(await readGates(store))];
1533
+ const index = rows.findIndex((gate) => gate.id === id);
1534
+ const old = rows[index];
1535
+ if (index < 0 || old === undefined) {
1536
+ throw new NotFoundError(`gate ${id} not found`);
1537
+ }
1538
+ const gate: ResolvedGate = {
1539
+ kind: "resolved",
1540
+ id: old.id,
1541
+ question: old.question,
1542
+ options: old.options,
1543
+ defaultAnswer: old.defaultAnswer,
1544
+ answer: requiredLine(params.answer, "answer"),
1545
+ };
1546
+ rows[index] = gate;
1547
+ await atomicWrite(join(store, "gates.md"), renderGates(rows));
1548
+ return gate;
1549
+ },
1550
+ },
1551
+ frontier: {
1552
+ set: async (params) => {
1553
+ await beginWrite();
1554
+ const repo = resolve(requiredLine(params.repo, "repo directory"));
1555
+ const forge = parseForge(params.forge ?? "gh");
1556
+ const pin =
1557
+ params.prs === undefined
1558
+ ? undefined
1559
+ : params.prs.map((pr) => positiveInteger(pr, "PR"));
1560
+ if (pin !== undefined && new Set(pin).size !== pin.length) {
1561
+ throw new UserError("--prs must not contain duplicates");
1562
+ }
1563
+ const old = await readFrontier(store);
1564
+ const prs = resolveFrontier(repo, forge);
1565
+ if (pin !== undefined) {
1566
+ validateFrontierPin({
1567
+ actual: prs.map((row) => row.pr),
1568
+ expected: pin,
1569
+ });
1570
+ }
1571
+ const value: Frontier = {
1572
+ generation: old.generation + 1,
1573
+ prs,
1574
+ lowestUnmerged: prs.find((row) => row.state === "OPEN")?.pr ?? null,
1575
+ };
1576
+ await atomicWrite(
1577
+ join(store, "frontier.json"),
1578
+ `${JSON.stringify(value, null, 2)}\n`
1579
+ );
1580
+ return value;
1581
+ },
1582
+ show: async () => {
1583
+ ensureOpen();
1584
+ return readFrontier(store);
1585
+ },
1586
+ },
1587
+ standing: {
1588
+ show: async () => {
1589
+ ensureOpen();
1590
+ return readStanding(store);
1591
+ },
1592
+ add: async (params) => {
1593
+ await beginWrite();
1594
+ const rows = [...(await readStanding(store))];
1595
+ const item: StandingLine = {
1596
+ number: rows.length + 1,
1597
+ line: requiredLine(params.line, "standing order"),
1598
+ };
1599
+ rows.push(item);
1600
+ await atomicWrite(
1601
+ join(store, "preferences.md"),
1602
+ `${rows.map((row) => `${row.number}. ${row.line}`).join("\n")}\n`
1603
+ );
1604
+ return item;
1605
+ },
1606
+ },
1607
+ status: {
1608
+ render: async () => {
1609
+ await beginWrite();
1610
+ const unitRows = await readUnits(store);
1611
+ const ledgerRows = await readLedger(store);
1612
+ const currentFrontier = await readFrontier(store);
1613
+ const gateRows = await readGates(store);
1614
+ const currentSummary = summarize(
1615
+ unitRows,
1616
+ ledgerRows,
1617
+ currentFrontier,
1618
+ gateRows
1619
+ );
1620
+ const path = join(store, "status.md");
1621
+ const before = (await exists(path))
1622
+ ? previousSummary(await readFile(path, "utf8"))
1623
+ : null;
1624
+ const change = changed(before, currentSummary);
1625
+ await atomicWrite(
1626
+ path,
1627
+ statusMarkdown(
1628
+ unitRows,
1629
+ ledgerRows,
1630
+ currentFrontier,
1631
+ gateRows,
1632
+ currentSummary
1633
+ )
1634
+ );
1635
+ return {
1636
+ units: unitRows,
1637
+ ledger: ledgerRows,
1638
+ frontier: currentFrontier,
1639
+ gates: gateRows,
1640
+ summary: currentSummary,
1641
+ changed: change,
1642
+ };
1643
+ },
1644
+ },
1645
+ init: async () => {
1646
+ ensureOpen();
1647
+ await mkdir(store, { recursive: true });
1648
+ await ensureLock();
1649
+ await writeIfMissing(join(store, "units.tsv"), `${UNIT_HEADER}\n`);
1650
+ await writeIfMissing(join(store, "ledger.tsv"), `${LEDGER_HEADER}\n`);
1651
+ await mkdir(join(store, "inbox"), { recursive: true });
1652
+ await writeIfMissing(join(store, "gates.md"), "");
1653
+ await writeIfMissing(join(store, "preferences.md"), "");
1654
+ await writeIfMissing(join(store, "frontier.json"), "{}\n");
1655
+ return { store };
1656
+ },
1657
+ close: async () => {
1658
+ if (closed) {
1659
+ return;
1660
+ }
1661
+ if (lockRequest !== null) {
1662
+ try {
1663
+ await lockRequest;
1664
+ } catch {
1665
+ // A failed acquisition has no lock to release.
1666
+ }
1667
+ }
1668
+ const release = releaseLock;
1669
+ releaseLock = null;
1670
+ closed = true;
1671
+ if (release !== null) {
1672
+ await release();
1673
+ }
1674
+ },
1675
+ };
1676
+ }