@maycombhq/atticus 0.0.0-stage → 2.0.0-beta.3

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 (475) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +17 -2
  3. package/THIRD-PARTY-LICENSES.txt +2607 -0
  4. package/dist/bin.js +47 -0
  5. package/dist/chunk-23W7VWKR.js +2609 -0
  6. package/dist/chunk-2C5B464C.js +106 -0
  7. package/dist/chunk-2LLFKP7C.js +53 -0
  8. package/dist/chunk-2MD7TXSG.js +166 -0
  9. package/dist/chunk-2UIQBDJO.js +131 -0
  10. package/dist/chunk-32MPK524.js +378 -0
  11. package/dist/chunk-3ALD2W2A.js +95 -0
  12. package/dist/chunk-3G4YI4ZU.js +16 -0
  13. package/dist/chunk-4AK7IBVP.js +915 -0
  14. package/dist/chunk-4BMDGTED.js +69 -0
  15. package/dist/chunk-4NDAAFQT.js +103 -0
  16. package/dist/chunk-4Q3NN7VF.js +32 -0
  17. package/dist/chunk-4RS5KZQD.js +25 -0
  18. package/dist/chunk-4SKZDI62.js +22 -0
  19. package/dist/chunk-55YODD3N.js +385 -0
  20. package/dist/chunk-56YQ6ROQ.js +3479 -0
  21. package/dist/chunk-5A5L3T2U.js +583 -0
  22. package/dist/chunk-5FEOD73Z.js +374 -0
  23. package/dist/chunk-5GDRCDQB.js +32 -0
  24. package/dist/chunk-5WS2YOGD.js +95 -0
  25. package/dist/chunk-65NYPGRC.js +766 -0
  26. package/dist/chunk-6BVONWJ7.js +50 -0
  27. package/dist/chunk-6D4FRTFF.js +53 -0
  28. package/dist/chunk-6GPJOFV4.js +149 -0
  29. package/dist/chunk-6JCPI55B.js +92 -0
  30. package/dist/chunk-6JUBZBMW.js +56 -0
  31. package/dist/chunk-6SCVQZX5.js +2522 -0
  32. package/dist/chunk-6TACQN3N.js +41 -0
  33. package/dist/chunk-72TXNLOC.js +496 -0
  34. package/dist/chunk-76WKMA4J.js +14 -0
  35. package/dist/chunk-7EFILTCI.js +67 -0
  36. package/dist/chunk-7EKXJZ2Y.js +2902 -0
  37. package/dist/chunk-7EOBHUMC.js +133 -0
  38. package/dist/chunk-7JUKDRSZ.js +1726 -0
  39. package/dist/chunk-7NN5VWDC.js +43 -0
  40. package/dist/chunk-7UFQZXZA.js +5949 -0
  41. package/dist/chunk-A3KPZJ5M.js +131 -0
  42. package/dist/chunk-A6AC72A5.js +48 -0
  43. package/dist/chunk-A7KZKIZX.js +166 -0
  44. package/dist/chunk-ABWHYWNI.js +101 -0
  45. package/dist/chunk-AFFIK6JI.js +46 -0
  46. package/dist/chunk-AHOQZ4OK.js +37 -0
  47. package/dist/chunk-AVJDSJ2F.js +18 -0
  48. package/dist/chunk-AXXCPAP6.js +203 -0
  49. package/dist/chunk-AY3QXYMB.js +41 -0
  50. package/dist/chunk-B2OKWJIU.js +143 -0
  51. package/dist/chunk-B632FH3I.js +251 -0
  52. package/dist/chunk-BLW4SXCU.js +38 -0
  53. package/dist/chunk-BVS2K73A.js +33 -0
  54. package/dist/chunk-BWZ77RQT.js +25 -0
  55. package/dist/chunk-BYBUDX4T.js +163 -0
  56. package/dist/chunk-BYZCY6SP.js +230 -0
  57. package/dist/chunk-C6EKFH7J.js +169 -0
  58. package/dist/chunk-C6MZFGN7.js +271 -0
  59. package/dist/chunk-CAQZUNOH.js +1150 -0
  60. package/dist/chunk-CD4XHYGY.js +856 -0
  61. package/dist/chunk-CEZQOLEO.js +1217 -0
  62. package/dist/chunk-CHWTETW7.js +128 -0
  63. package/dist/chunk-CMV7TGU6.js +31 -0
  64. package/dist/chunk-CNUKCS2T.js +2741 -0
  65. package/dist/chunk-CP3L3FH7.js +549 -0
  66. package/dist/chunk-CPGCA6GZ.js +41 -0
  67. package/dist/chunk-CWM6KM5G.js +315 -0
  68. package/dist/chunk-CXDSTRUJ.js +873 -0
  69. package/dist/chunk-D3PO4DOZ.js +261 -0
  70. package/dist/chunk-DFBCSHVT.js +69 -0
  71. package/dist/chunk-DHPACDG7.js +104 -0
  72. package/dist/chunk-DPC5Y5DL.js +3898 -0
  73. package/dist/chunk-DPUYDDQG.js +164 -0
  74. package/dist/chunk-DQN7TF5D.js +325 -0
  75. package/dist/chunk-EADP3PEN.js +450 -0
  76. package/dist/chunk-ED2LWWIP.js +878 -0
  77. package/dist/chunk-EHVTK3UE.js +116 -0
  78. package/dist/chunk-EPTBZ3GP.js +101 -0
  79. package/dist/chunk-EXIMEJ2X.js +545 -0
  80. package/dist/chunk-EYWIDJFU.js +130 -0
  81. package/dist/chunk-F44OMYIN.js +54 -0
  82. package/dist/chunk-F7HYOUFF.js +287 -0
  83. package/dist/chunk-FALXWAMD.js +182 -0
  84. package/dist/chunk-FBNPE7HS.js +49 -0
  85. package/dist/chunk-FSWUD5QB.js +108 -0
  86. package/dist/chunk-FVQISWPW.js +296 -0
  87. package/dist/chunk-G2B4ZQGH.js +240 -0
  88. package/dist/chunk-GK6XK5SP.js +1227 -0
  89. package/dist/chunk-GLEBK5L3.js +34 -0
  90. package/dist/chunk-GNB5QDDI.js +25 -0
  91. package/dist/chunk-GQ4N4WKF.js +1142 -0
  92. package/dist/chunk-GRMR5TRQ.js +119 -0
  93. package/dist/chunk-HC6XKREB.js +334 -0
  94. package/dist/chunk-HFQCFSJR.js +60 -0
  95. package/dist/chunk-HJDICQA4.js +102 -0
  96. package/dist/chunk-HLJAXMM4.js +233 -0
  97. package/dist/chunk-HVIMMDVB.js +162 -0
  98. package/dist/chunk-HZVLAPN5.js +32 -0
  99. package/dist/chunk-I3E7CX4H.js +155 -0
  100. package/dist/chunk-IQFTZ37C.js +2919 -0
  101. package/dist/chunk-ITY3VJRX.js +285 -0
  102. package/dist/chunk-JBTK5OP2.js +55 -0
  103. package/dist/chunk-JPGCERE2.js +26 -0
  104. package/dist/chunk-JQDNHIBC.js +38 -0
  105. package/dist/chunk-JRBCWLOR.js +307 -0
  106. package/dist/chunk-JRZQA2JM.js +212 -0
  107. package/dist/chunk-KCBJCA66.js +45 -0
  108. package/dist/chunk-KJPMEAI4.js +705 -0
  109. package/dist/chunk-KQDZXGNR.js +14 -0
  110. package/dist/chunk-KUUQR4HA.js +264 -0
  111. package/dist/chunk-L24C55VN.js +7923 -0
  112. package/dist/chunk-L3BX5ZIB.js +400 -0
  113. package/dist/chunk-L6SGQIR3.js +471 -0
  114. package/dist/chunk-LBENYT3H.js +66 -0
  115. package/dist/chunk-LNAJW6FB.js +47 -0
  116. package/dist/chunk-MC3H7736.js +19910 -0
  117. package/dist/chunk-MMOSZPWJ.js +273 -0
  118. package/dist/chunk-MXCHWULV.js +285 -0
  119. package/dist/chunk-N3E56YPN.js +344 -0
  120. package/dist/chunk-NHFQMOVK.js +180 -0
  121. package/dist/chunk-NQU6KNQF.js +27 -0
  122. package/dist/chunk-OIK5YPTN.js +391 -0
  123. package/dist/chunk-OLWFG75W.js +4330 -0
  124. package/dist/chunk-ONPUIE7Y.js +29 -0
  125. package/dist/chunk-OW7D2PJL.js +376 -0
  126. package/dist/chunk-P63VL4HE.js +2357 -0
  127. package/dist/chunk-PA4YNUMU.js +45 -0
  128. package/dist/chunk-PAMPPUR6.js +4500 -0
  129. package/dist/chunk-PBS3OFNN.js +113 -0
  130. package/dist/chunk-PFT3VTS4.js +214 -0
  131. package/dist/chunk-PFTA6BZF.js +49 -0
  132. package/dist/chunk-PNN5KLAY.js +119 -0
  133. package/dist/chunk-PRPPGYZG.js +104 -0
  134. package/dist/chunk-PUKEZ37F.js +666 -0
  135. package/dist/chunk-Q3HOFXZJ.js +76 -0
  136. package/dist/chunk-Q5AMFEWD.js +119 -0
  137. package/dist/chunk-QKRNMBJO.js +80 -0
  138. package/dist/chunk-QSU47IQ5.js +36 -0
  139. package/dist/chunk-QSXK2RGH.js +688 -0
  140. package/dist/chunk-QXNWYUYI.js +52 -0
  141. package/dist/chunk-R6RY4Q5T.js +51 -0
  142. package/dist/chunk-RCU3MPY2.js +1496 -0
  143. package/dist/chunk-RLIB4JUN.js +174 -0
  144. package/dist/chunk-RYSCLMU3.js +1991 -0
  145. package/dist/chunk-RZUZ4RJJ.js +415 -0
  146. package/dist/chunk-S2A7YC3Q.js +41433 -0
  147. package/dist/chunk-S2FBBX7D.js +540 -0
  148. package/dist/chunk-S34B4QPI.js +430 -0
  149. package/dist/chunk-SETWVXOJ.js +2078 -0
  150. package/dist/chunk-SMA4N4AC.js +211 -0
  151. package/dist/chunk-SP2I63OO.js +252 -0
  152. package/dist/chunk-STHSY74Q.js +242 -0
  153. package/dist/chunk-STUPFGXY.js +3206 -0
  154. package/dist/chunk-T5I4S2W3.js +34 -0
  155. package/dist/chunk-T6AIP7H5.js +3851 -0
  156. package/dist/chunk-TANFV3MZ.js +531 -0
  157. package/dist/chunk-TAVDMUV3.js +189 -0
  158. package/dist/chunk-TBSQJ6IV.js +324 -0
  159. package/dist/chunk-TIZHP7KE.js +408 -0
  160. package/dist/chunk-TV45EUGM.js +51 -0
  161. package/dist/chunk-TW2KPPUC.js +4087 -0
  162. package/dist/chunk-V2YTAIQO.js +1075 -0
  163. package/dist/chunk-V5PKHHQQ.js +1963 -0
  164. package/dist/chunk-VF4IOZZO.js +184 -0
  165. package/dist/chunk-VJTINHZI.js +48 -0
  166. package/dist/chunk-VKLILA2D.js +205 -0
  167. package/dist/chunk-VOGCSYOI.js +16 -0
  168. package/dist/chunk-VPTTCYOC.js +16101 -0
  169. package/dist/chunk-WY6PP6AA.js +53 -0
  170. package/dist/chunk-WYFIXP54.js +174 -0
  171. package/dist/chunk-WZ2BXIGG.js +34 -0
  172. package/dist/chunk-XJNDSYGO.js +147 -0
  173. package/dist/chunk-XPCM366L.js +70 -0
  174. package/dist/chunk-XSQKGVYA.js +69 -0
  175. package/dist/chunk-XUPVB7TL.js +207 -0
  176. package/dist/chunk-XZRON6IV.js +131 -0
  177. package/dist/chunk-Y4NG2FP5.js +94 -0
  178. package/dist/chunk-YEDDOYSC.js +27809 -0
  179. package/dist/chunk-YGMCXNTY.js +41 -0
  180. package/dist/chunk-YJKPTX6J.js +228 -0
  181. package/dist/chunk-YMZ3TNYB.js +18 -0
  182. package/dist/chunk-Z6LF7HQN.js +22 -0
  183. package/dist/chunk-Z7SG32ZY.js +55 -0
  184. package/dist/chunk-ZDPAS56H.js +76 -0
  185. package/dist/chunk-ZJSP24X4.js +1247 -0
  186. package/dist/chunk-ZMAINWBW.js +18 -0
  187. package/dist/chunk-ZMFPW66A.js +22 -0
  188. package/dist/chunk-ZUIAOKXI.js +1611 -0
  189. package/dist/chunk-ZVM6AKKM.js +47 -0
  190. package/dist/files.json +1888 -0
  191. package/dist/fixtures/forge/README.md +19 -0
  192. package/dist/fixtures/forge/expected/discrepancies.yaml +54 -0
  193. package/dist/fixtures/forge/expected/inputs.yaml +38 -0
  194. package/dist/fixtures/forge/expected/scan.yaml +342 -0
  195. package/dist/fixtures/forge/inputs/diagrams/forge-context.drawio +23 -0
  196. package/dist/fixtures/forge/inputs/partner-specs/northwind-pay-openapi.yaml +11 -0
  197. package/dist/fixtures/forge/inputs/tickets/ado-stories.json +4 -0
  198. package/dist/fixtures/forge/inputs/tickets/closed-service-requests.json +5 -0
  199. package/dist/fixtures/forge/inputs/wiki/integration-patterns.md +11 -0
  200. package/dist/fixtures/forge/inputs/wiki/ledger-solution-spec.md +9 -0
  201. package/dist/fixtures/forge/inputs/wiki/snow-firewall-request.md +4 -0
  202. package/dist/fixtures/forge/metarepo/judgments/reaches/84af5b6af24f7ca8c23013f3.yaml +17 -0
  203. package/dist/fixtures/forge/metarepo/judgments/reaches/c3917555edebddce8cecb647.yaml +17 -0
  204. package/dist/fixtures/forge/metarepo/judgments/reaches/c5fafd6601f8ca9e34e0bbd0.yaml +17 -0
  205. package/dist/fixtures/forge/metarepo/org-pack/agents/architecture-critic.yaml +9 -0
  206. package/dist/fixtures/forge/metarepo/org-pack/agents/story-writer.yaml +10 -0
  207. package/dist/fixtures/forge/metarepo/org-pack/checklists/architecture.yaml +115 -0
  208. package/dist/fixtures/forge/metarepo/org-pack/dialects/forge.yaml +111 -0
  209. package/dist/fixtures/forge/metarepo/org-pack/gates.yaml +22 -0
  210. package/dist/fixtures/forge/metarepo/org-pack/pack.yaml +11 -0
  211. package/dist/fixtures/forge/metarepo/org-pack/patterns/A.EB.PUB.yaml +12 -0
  212. package/dist/fixtures/forge/metarepo/org-pack/patterns/D.JDBC.yaml +12 -0
  213. package/dist/fixtures/forge/metarepo/org-pack/patterns/S.DIR.BFF.yaml +12 -0
  214. package/dist/fixtures/forge/metarepo/org-pack/patterns/S.GW.SOR.yaml +13 -0
  215. package/dist/fixtures/forge/metarepo/org-pack/patterns/S.GW.VND.yaml +14 -0
  216. package/dist/fixtures/forge/metarepo/org-pack/process/change-templates/feature.md +37 -0
  217. package/dist/fixtures/forge/metarepo/org-pack/process/skills/add-forge-endpoint/SKILL.md +17 -0
  218. package/dist/fixtures/forge/metarepo/org-pack/process/snow/firewall-rule.yaml +14 -0
  219. package/dist/fixtures/forge/metarepo/org-pack/process/snow/gateway-route.yaml +13 -0
  220. package/dist/fixtures/forge/metarepo/org-pack/profiles/forge/profile.yaml +12 -0
  221. package/dist/fixtures/forge/metarepo/org-pack/profiles/forge/repos.manifest.yaml +44 -0
  222. package/dist/fixtures/forge/metarepo/org-pack/roles.yaml +14 -0
  223. package/dist/fixtures/forge/metarepo/org-pack/runtime.yaml +14 -0
  224. package/dist/fixtures/forge/metarepo/org-pack/sources.yaml +3 -0
  225. package/dist/fixtures/forge/repos/forge-events/topics/payments.initiated.yaml +4 -0
  226. package/dist/fixtures/forge/repos/forge-events/topics/transfers.completed.yaml +4 -0
  227. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/ledger-v1.xml +3 -0
  228. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/policies/SpikeArrest.xml +3 -0
  229. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/policies/VerifyOAuth.xml +4 -0
  230. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/proxies/default.xml +14 -0
  231. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/targets/ledger.xml +5 -0
  232. package/dist/fixtures/forge/repos/forge-infra/alerts/ledger-api.yaml +5 -0
  233. package/dist/fixtures/forge/repos/forge-infra/k8s/ledger-api.yaml +15 -0
  234. package/dist/fixtures/forge/repos/forge-infra/k8s/portal-bff.yaml +7 -0
  235. package/dist/fixtures/forge/repos/forge-infra/terraform/ledger-db.tf +9 -0
  236. package/dist/fixtures/forge/repos/forge-ledger/api/ledger-openapi.yaml +31 -0
  237. package/dist/fixtures/forge/repos/forge-ledger/forge.yaml +4 -0
  238. package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/LedgerEndpoints.java +13 -0
  239. package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/LedgerEvents.java +11 -0
  240. package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/NorthwindClient.java +10 -0
  241. package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/application.yml +3 -0
  242. package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/db/migration/V1__create_account.sql +5 -0
  243. package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/db/migration/V2__create_entry.sql +7 -0
  244. package/dist/fixtures/forge/repos/forge-portal-bff/pom.xml +1 -0
  245. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/AuditPublisher.java +12 -0
  246. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/BalanceController.java +16 -0
  247. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/LedgerClient.java +13 -0
  248. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/PaymentEvents.java +13 -0
  249. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/resources/application.yml +8 -0
  250. package/dist/fixtures/forge/repos/forge-portal-ui/package.json +1 -0
  251. package/dist/fixtures/forge/repos/forge-portal-ui/src/api/balance.ts +5 -0
  252. package/dist/fixtures/forge/repos/forge-reports-job/src/main/java/forge/reports/DailyEntriesJob.java +19 -0
  253. package/dist/fixtures/forge/repos/forge-reports-job/src/main/resources/application.yml +3 -0
  254. package/dist/fixtures/forge-change/README.md +41 -0
  255. package/dist/fixtures/forge-change/expected/findings.yaml +301 -0
  256. package/dist/fixtures/forge-change/expected/stories.yaml +101 -0
  257. package/dist/fixtures/forge-change/judgments/personal-data/0b5f3dbf5257a22275d8c74b.yaml +18 -0
  258. package/dist/fixtures/forge-change/judgments/personal-data/2759a66770134c417440bf2d.yaml +18 -0
  259. package/dist/fixtures/forge-change/judgments/personal-data/31b1ea2f14d0737f1aca79fd.yaml +18 -0
  260. package/dist/fixtures/forge-change/judgments/personal-data/49afd70c08228e65ad6ca1ed.yaml +18 -0
  261. package/dist/fixtures/forge-change/judgments/personal-data/761cabcb8a39f5e5866d2dd6.yaml +18 -0
  262. package/dist/fixtures/forge-change/judgments/personal-data/adae902b90cc8ffa0c765145.yaml +18 -0
  263. package/dist/fixtures/forge-change/judgments/personal-data/ba11bb933105ec9493e065dd.yaml +18 -0
  264. package/dist/fixtures/forge-change/judgments/personal-data/bdbcd1cdac3021c68063c3e7.yaml +18 -0
  265. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/brief.md +35 -0
  266. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/change.yaml +120 -0
  267. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/data.md +18 -0
  268. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/design.md +29 -0
  269. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/interfaces.md +17 -0
  270. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/mappings.md +26 -0
  271. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/proposal.md +35 -0
  272. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/snow.md +12 -0
  273. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/specs/transfer-limits/spec.md +19 -0
  274. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s1-refuse-over-limit.md +17 -0
  275. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s2-show-remaining.md +16 -0
  276. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s3-audit-refusals.md +18 -0
  277. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s4-velocity-check.md +16 -0
  278. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories.md +8 -0
  279. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/tasks.md +12 -0
  280. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/ux.md +7 -0
  281. package/dist/fixtures/forge-change/org-pack/agents/code-and-test-critic.yaml +13 -0
  282. package/dist/fixtures/forge-change/org-pack/agents/complete-critic.yaml +12 -0
  283. package/dist/fixtures/forge-change/org-pack/agents/safe-critic.yaml +12 -0
  284. package/dist/fixtures/forge-change/org-pack/agents/ship-critic.yaml +11 -0
  285. package/dist/fixtures/forge-change/org-pack/agents/true-critic.yaml +11 -0
  286. package/dist/fixtures/forge-change/org-pack/checklists/review.yaml +54 -0
  287. package/dist/fixtures/forge-change/org-pack/gates.yaml +25 -0
  288. package/dist/fixtures/forge-change/org-pack/teams/team-ledger/checklists/ledger-team.yaml +227 -0
  289. package/dist/fixtures/forge-change/recordings/complete-critic/2ad84f84bf39c1d5412697e69fe23034d6983ec45a00314bf0975e627589a3c5.json +12 -0
  290. package/dist/fixtures/forge-change/recordings/complete-critic/417c98d8e54a4387d0fd93441c49f44c2868958739070d855c1dce0c41e24db3.json +12 -0
  291. package/dist/fixtures/forge-change/recordings/complete-critic/50203ee22e9cb3ef047295edb6538fe7d5695e1e4b139f3c929013dc6ce47021.json +12 -0
  292. package/dist/fixtures/forge-change/recordings/complete-critic/89f18d67f5d9c325bf35e2a9e020f9ddb6ee13b8523315037f0905cc57c040b1.json +12 -0
  293. package/dist/fixtures/forge-change/recordings/complete-critic/b4097867d26ae8eadc8b9efa8cdd86897013358035f0b40086c299a59c4ba121.json +12 -0
  294. package/dist/fixtures/forge-change/recordings/safe-critic/302d06f49664dd67ad2e21ab9cf74c1b0f7d3f750709c6ed8eff2b70983274e2.json +12 -0
  295. package/dist/fixtures/forge-change/recordings/safe-critic/32f1049c9a5ab4a759543290db4644ec6e221cc45fdfdcf06193837c6a8ce0b9.json +12 -0
  296. package/dist/fixtures/forge-change/recordings/safe-critic/4e4a84bfc04b9f26ca4250319a879901ff7a1c081c71e46a117675fee05026b0.json +12 -0
  297. package/dist/fixtures/forge-change/recordings/safe-critic/604af0fcdd6df6b76208fa88305fdc15c37606c2a4d1ae816477e46ac8c2dd04.json +12 -0
  298. package/dist/fixtures/forge-change/recordings/safe-critic/f0bf50af11f14c50d07904f0dffe1c8c20fb0cd89770645a8824c1d30a443955.json +12 -0
  299. package/dist/fixtures/forge-change/recordings/ship-critic/350954b58833b2c04880c67db45a1b328b7fd541d9f0d943b63905b8875488a6.json +12 -0
  300. package/dist/fixtures/forge-change/recordings/ship-critic/4af95c6c96a206777cec9de1f9c2d8c55a36ddc9b8db2f81d6eb83217b835efa.json +12 -0
  301. package/dist/fixtures/forge-change/recordings/ship-critic/76c34522a49cf7587a4e5689a5030d384603fa780b8aba97d27815cbc829683e.json +12 -0
  302. package/dist/fixtures/forge-change/recordings/ship-critic/a3b2cd1ace48587e5994333db03b34c3a0f9dec4f1932f1730ddfb687f6918c8.json +12 -0
  303. package/dist/fixtures/forge-change/recordings/ship-critic/de288a4fa64d0fc20890f9a787080ce6eac5203b7b9b72d884df11e8cf111835.json +12 -0
  304. package/dist/fixtures/forge-change/recordings/story-writer/1ddb00eac2aaf91a5ec5d9dfd220116be9f75aec6546a4a3a3b050ed89af2864.json +12 -0
  305. package/dist/packages/assistant-kit/frameworks-kit/README.md +86 -0
  306. package/dist/packages/assistant-kit/frameworks-kit/checklist.md +11 -0
  307. package/dist/packages/assistant-kit/frameworks-kit/examples/tide-lantern.yaml +56 -0
  308. package/dist/packages/assistant-kit/frameworks-kit/examples/tide-platform.yaml +43 -0
  309. package/dist/packages/assistant-kit/frameworks-kit/prompts/write-framework-model.md +12 -0
  310. package/dist/packages/assistant-kit/kit/README.md +98 -0
  311. package/dist/packages/assistant-kit/kit/c2-reference.md +334 -0
  312. package/dist/packages/assistant-kit/kit/checklist.md +39 -0
  313. package/dist/packages/assistant-kit/kit/examples/house-http-client.yaml +102 -0
  314. package/dist/packages/assistant-kit/kit/examples/tide-apigee.yaml +56 -0
  315. package/dist/packages/assistant-kit/kit/examples/tide-apim.yaml +32 -0
  316. package/dist/packages/assistant-kit/kit/examples/tide-kong.yaml +44 -0
  317. package/dist/packages/assistant-kit/kit/prompts/write-dialect.md +34 -0
  318. package/dist/packages/assistant-kit/kit/worked-example.md +198 -0
  319. package/dist/packages/conformance/sample/README.md +4 -0
  320. package/dist/packages/conformance/sample/inputs/notes.md +3 -0
  321. package/dist/packages/conformance/sample/inputs/systems.csv +3 -0
  322. package/dist/packages/conformance/sample/repo/api/greeter.proto +6 -0
  323. package/dist/packages/conformance/sample/repo/api/openapi.yaml +10 -0
  324. package/dist/packages/conformance/sample/repo/api/schema.graphql +3 -0
  325. package/dist/packages/conformance/sample/repo/db/V1__create_greeting.sql +4 -0
  326. package/dist/packages/conformance/sample/repo/package.json +7 -0
  327. package/dist/packages/conformance/sample/repo/src/main/java/sample/HelloController.java +10 -0
  328. package/dist/packages/conformance/sample/repo/web/app.ts +4 -0
  329. package/dist/packages/evals/datasets/critic-lenses.yaml +62 -0
  330. package/dist/packages/evals/datasets/dialect-writer.yaml +14 -0
  331. package/dist/packages/evals/datasets/feature-namer.yaml +34 -0
  332. package/dist/packages/evals/datasets/glossary-definer.yaml +25 -0
  333. package/dist/packages/evals/datasets/story-writer.yaml +13 -0
  334. package/dist/packages/evals/datasets/template-writer.yaml +16 -0
  335. package/dist/packages/evals/datasets/test-writer.yaml +18 -0
  336. package/dist/packages/executors/reference/reference-executor.mjs +233 -0
  337. package/dist/packages/extractors/dialects/argocd.yaml +45 -0
  338. package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/AccountResource.java +47 -0
  339. package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/AuditClient.java +12 -0
  340. package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/LedgerClient.java +17 -0
  341. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/LedgerClient.java +14 -0
  342. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/RatesClient.java +11 -0
  343. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/StatementController.java +36 -0
  344. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/resources/application.yml +7 -0
  345. package/dist/packages/extractors/dialects/fixtures/quarkus-reactive-messaging/fraud-service/src/main/java/forge/fraud/FraudScorer.java +27 -0
  346. package/dist/packages/extractors/dialects/fixtures/quarkus-reactive-messaging/fraud-service/src/main/resources/application.properties +4 -0
  347. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/api.tf +38 -0
  348. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/main.tf +95 -0
  349. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/terraform.tfvars +1 -0
  350. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/variables.tf +4 -0
  351. package/dist/packages/extractors/dialects/fixtures/terraform-azure/forge-azure-infra/main.tf +102 -0
  352. package/dist/packages/extractors/dialects/fixtures/terraform-gcp/forge-gcp-infra/main.tf +51 -0
  353. package/dist/packages/extractors/dialects/harness.yaml +101 -0
  354. package/dist/packages/extractors/dialects/jaxrs.yaml +155 -0
  355. package/dist/packages/extractors/dialects/micronaut.yaml +207 -0
  356. package/dist/packages/extractors/dialects/quarkus-reactive-messaging.yaml +158 -0
  357. package/dist/packages/extractors/dialects/tekton.yaml +71 -0
  358. package/dist/packages/extractors/dialects/terraform-aws.yaml +267 -0
  359. package/dist/packages/extractors/dialects/terraform-azure.yaml +212 -0
  360. package/dist/packages/extractors/dialects/terraform-gcp.yaml +109 -0
  361. package/dist/packages/extractors/grammars/README.md +11 -0
  362. package/dist/packages/extractors/grammars/tree-sitter-groovy.LICENSE +21 -0
  363. package/dist/packages/extractors/grammars/tree-sitter-groovy.wasm +0 -0
  364. package/dist/packages/extractors/grammars/vendored.json +11 -0
  365. package/dist/packages/learn/lessons/architect-diagrams-and-code.yaml +69 -0
  366. package/dist/packages/learn/lessons/business-analyst-story-bar.yaml +94 -0
  367. package/dist/packages/learn/lessons/delivery-lead-release-readiness.yaml +44 -0
  368. package/dist/packages/learn/lessons/designer-features-in-user-words.yaml +57 -0
  369. package/dist/packages/learn/lessons/developer-find-your-way.yaml +60 -0
  370. package/dist/packages/learn/lessons/everyone-first-hour.yaml +74 -0
  371. package/dist/packages/learn/lessons/platform-lead-coverage-and-defences.yaml +58 -0
  372. package/dist/packages/learn/lessons/product-owner-first-change.yaml +167 -0
  373. package/dist/packages/learn/lessons/qa-tests-and-trace.yaml +53 -0
  374. package/dist/packages/learn/lessons/team-lead-initiative.yaml +146 -0
  375. package/dist/packages/naysayer/constitution/constitution.yaml +286 -0
  376. package/dist/packages/naysayer/starter/review.yaml +256 -0
  377. package/dist/packages/sdk/npm/index.js +8892 -0
  378. package/dist/packages/speckit/rules/drift.yaml +78 -0
  379. package/dist/packages/taskrun/rules/conform.yaml +38 -0
  380. package/dist/packages/testgen/rules/trace.yaml +49 -0
  381. package/dist/packages/ui/dist/assets/elk-api-tma0MCuh.js +2 -0
  382. package/dist/packages/ui/dist/assets/elk-worker.min--dAJroGl.js +6401 -0
  383. package/dist/packages/ui/dist/assets/elk-worker.min-DdYmS5kP.js +2 -0
  384. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
  385. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
  386. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
  387. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
  388. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
  389. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
  390. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
  391. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
  392. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
  393. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
  394. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
  395. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
  396. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
  397. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
  398. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
  399. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
  400. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
  401. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
  402. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  403. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  404. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  405. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  406. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  407. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  408. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
  409. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
  410. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
  411. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
  412. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
  413. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
  414. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
  415. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
  416. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
  417. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
  418. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
  419. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
  420. package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +29 -0
  421. package/dist/packages/ui/dist/assets/index-DD_hufQz.css +1 -0
  422. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  423. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  424. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  425. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  426. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  427. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  428. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  429. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  430. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  431. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  432. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  433. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  434. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  435. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  436. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  437. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  438. package/dist/packages/ui/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  439. package/dist/packages/ui/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  440. package/dist/packages/ui/dist/assets/rajdhani-devanagari-600-normal-BtY48IV_.woff +0 -0
  441. package/dist/packages/ui/dist/assets/rajdhani-devanagari-600-normal-DhS7ScYx.woff2 +0 -0
  442. package/dist/packages/ui/dist/assets/rajdhani-devanagari-700-normal-BQQOj9BB.woff2 +0 -0
  443. package/dist/packages/ui/dist/assets/rajdhani-devanagari-700-normal-DBRwWuxA.woff +0 -0
  444. package/dist/packages/ui/dist/assets/rajdhani-latin-600-normal-CXCVEoA9.woff2 +0 -0
  445. package/dist/packages/ui/dist/assets/rajdhani-latin-600-normal-Dx02qI2_.woff +0 -0
  446. package/dist/packages/ui/dist/assets/rajdhani-latin-700-normal-BPDUZo87.woff2 +0 -0
  447. package/dist/packages/ui/dist/assets/rajdhani-latin-700-normal-e0Uj8ZlN.woff +0 -0
  448. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-600-normal-BleIgkPp.woff +0 -0
  449. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-600-normal-C9ZjJQAt.woff2 +0 -0
  450. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-700-normal-BPaQ7tlD.woff +0 -0
  451. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-700-normal-S6aYESQK.woff2 +0 -0
  452. package/dist/packages/ui/dist/favicon.svg +1 -0
  453. package/dist/packages/ui/dist/index.html +15 -0
  454. package/dist/third-party.json +575 -0
  455. package/dist/vendor/@fission-ai/openspec/bin/openspec.js +85986 -0
  456. package/dist/vendor/@fission-ai/openspec/package.json +82 -0
  457. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/schema.yaml +248 -0
  458. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/design.md +21 -0
  459. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/proposal.md +34 -0
  460. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/spec.md +13 -0
  461. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/tasks.md +11 -0
  462. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
  463. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
  464. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
  465. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
  466. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
  467. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
  468. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
  469. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
  470. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
  471. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
  472. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
  473. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
  474. package/dist/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
  475. package/package.json +36 -3
@@ -0,0 +1,4087 @@
1
+ import { createRequire as __atticusCreateRequire } from "node:module";
2
+ import { fileURLToPath as __atticusFileURLToPath } from "node:url";
3
+ var require = __atticusCreateRequire(import.meta.url);
4
+ var __atticusUrl = (p) => new URL("./" + p, import.meta.url).href;
5
+ var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
6
+ import {
7
+ listRuns,
8
+ runTranscript
9
+ } from "./chunk-DPUYDDQG.js";
10
+ import {
11
+ historyEntry,
12
+ listHistory
13
+ } from "./chunk-G2B4ZQGH.js";
14
+ import {
15
+ estimateOf
16
+ } from "./chunk-CHWTETW7.js";
17
+ import {
18
+ changeArchive,
19
+ changeHandoff,
20
+ changeNew,
21
+ changeSync
22
+ } from "./chunk-EXIMEJ2X.js";
23
+ import {
24
+ HttpError,
25
+ badRequest,
26
+ conflict,
27
+ notFound,
28
+ startServer,
29
+ strParam,
30
+ uiCiteRoutes
31
+ } from "./chunk-GK6XK5SP.js";
32
+ import "./chunk-B632FH3I.js";
33
+ import {
34
+ isHandoffStep
35
+ } from "./chunk-4Q3NN7VF.js";
36
+ import {
37
+ conformanceCommand
38
+ } from "./chunk-V2YTAIQO.js";
39
+ import {
40
+ outcomeList,
41
+ outcomeRecord,
42
+ releaseEvidenceCommand,
43
+ releaseForecastCommand,
44
+ releaseNotesCommand,
45
+ releasePlanCommand,
46
+ releaseSecurityCommand
47
+ } from "./chunk-STHSY74Q.js";
48
+ import {
49
+ changeMapBody,
50
+ changeMapOf
51
+ } from "./chunk-GRMR5TRQ.js";
52
+ import {
53
+ isSandbox,
54
+ learnCheck,
55
+ learnStart,
56
+ lessonsFor,
57
+ readProgress,
58
+ sandboxCreate
59
+ } from "./chunk-SETWVXOJ.js";
60
+ import {
61
+ defencesScan,
62
+ evalRun
63
+ } from "./chunk-MXCHWULV.js";
64
+ import {
65
+ listReports,
66
+ readQuality
67
+ } from "./chunk-STUPFGXY.js";
68
+ import {
69
+ readServiceRequests
70
+ } from "./chunk-RYSCLMU3.js";
71
+ import {
72
+ initiativeStatus,
73
+ readView
74
+ } from "./chunk-ZUIAOKXI.js";
75
+ import {
76
+ taskRun
77
+ } from "./chunk-JRBCWLOR.js";
78
+ import "./chunk-CEZQOLEO.js";
79
+ import "./chunk-2UIQBDJO.js";
80
+ import {
81
+ executorsCheck,
82
+ todoDone,
83
+ todoRun,
84
+ todoShow
85
+ } from "./chunk-HLJAXMM4.js";
86
+ import {
87
+ inputsSha256,
88
+ loadExecutors
89
+ } from "./chunk-QSXK2RGH.js";
90
+ import {
91
+ uiOnboardingRoutes
92
+ } from "./chunk-2MD7TXSG.js";
93
+ import {
94
+ readCorrections
95
+ } from "./chunk-XUPVB7TL.js";
96
+ import "./chunk-CMV7TGU6.js";
97
+ import {
98
+ readMe,
99
+ writeMe
100
+ } from "./chunk-JBTK5OP2.js";
101
+ import {
102
+ digestCommand
103
+ } from "./chunk-B2OKWJIU.js";
104
+ import {
105
+ reconcileCommand
106
+ } from "./chunk-6GPJOFV4.js";
107
+ import {
108
+ sourcesAdd,
109
+ sourcesSync
110
+ } from "./chunk-S34B4QPI.js";
111
+ import {
112
+ INPUTS_SOURCE_ID
113
+ } from "./chunk-ABWHYWNI.js";
114
+ import {
115
+ baselineCommand,
116
+ setupSteps
117
+ } from "./chunk-AXXCPAP6.js";
118
+ import {
119
+ catalogRecords,
120
+ openChanges,
121
+ systemGuide
122
+ } from "./chunk-5FEOD73Z.js";
123
+ import {
124
+ doctorChecks
125
+ } from "./chunk-S2FBBX7D.js";
126
+ import "./chunk-3G4YI4ZU.js";
127
+ import "./chunk-72TXNLOC.js";
128
+ import "./chunk-IQFTZ37C.js";
129
+ import {
130
+ handoffDone,
131
+ workspaceOpen,
132
+ workspaceSync
133
+ } from "./chunk-N3E56YPN.js";
134
+ import "./chunk-Z7SG32ZY.js";
135
+ import "./chunk-DPC5Y5DL.js";
136
+ import {
137
+ critic,
138
+ criticStats,
139
+ findingAction
140
+ } from "./chunk-RZUZ4RJJ.js";
141
+ import "./chunk-L3BX5ZIB.js";
142
+ import {
143
+ planStories,
144
+ snowDraft,
145
+ snowSubmit
146
+ } from "./chunk-OW7D2PJL.js";
147
+ import "./chunk-BWZ77RQT.js";
148
+ import {
149
+ archiveGate,
150
+ ciArchiveGate
151
+ } from "./chunk-ZJSP24X4.js";
152
+ import {
153
+ traceMatrix
154
+ } from "./chunk-6SCVQZX5.js";
155
+ import "./chunk-65NYPGRC.js";
156
+ import "./chunk-VOGCSYOI.js";
157
+ import {
158
+ scanCommand
159
+ } from "./chunk-TAVDMUV3.js";
160
+ import "./chunk-4BMDGTED.js";
161
+ import {
162
+ readChangeTasks,
163
+ readStory
164
+ } from "./chunk-RCU3MPY2.js";
165
+ import {
166
+ ASSISTANT_FORMATS,
167
+ readProbes,
168
+ whatRunsWhere
169
+ } from "./chunk-V5PKHHQQ.js";
170
+ import {
171
+ NEEDS_FILE,
172
+ PlannerError,
173
+ SYNC_FILE,
174
+ TARGET_DIR,
175
+ codeFactRecords,
176
+ commentOnFinding,
177
+ conflictTouches,
178
+ markNeed,
179
+ mergeDelta,
180
+ plainWhy,
181
+ readDeltas,
182
+ readKnowledgeApplies,
183
+ readNeeds,
184
+ readStageLoop,
185
+ setKnowledgeStatus,
186
+ stageComments,
187
+ stageGate
188
+ } from "./chunk-VPTTCYOC.js";
189
+ import {
190
+ STAGE_PHASE,
191
+ fieldNames,
192
+ rerunOf
193
+ } from "./chunk-L6SGQIR3.js";
194
+ import {
195
+ layerRules,
196
+ loadConstitution,
197
+ overrideStats,
198
+ readChangeFiles,
199
+ readPackFiles,
200
+ readProposals,
201
+ readRound,
202
+ readSnowDrafts,
203
+ readStoryIndex,
204
+ reviewedGates,
205
+ roundsOf
206
+ } from "./chunk-7UFQZXZA.js";
207
+ import {
208
+ artifactFiles,
209
+ artifactPhase,
210
+ changeFolder,
211
+ changeReport,
212
+ changeSize,
213
+ changeStore,
214
+ detectConflicts,
215
+ liveHandoff,
216
+ loadChangeContext,
217
+ requiredArtifacts
218
+ } from "./chunk-56YQ6ROQ.js";
219
+ import "./chunk-4AK7IBVP.js";
220
+ import {
221
+ fileStepStore,
222
+ manualAdapter,
223
+ stepOfProvisional
224
+ } from "./chunk-7JUKDRSZ.js";
225
+ import {
226
+ forgetCatalogs,
227
+ keepCatalogsInMemory,
228
+ readCatalog
229
+ } from "./chunk-OLWFG75W.js";
230
+ import {
231
+ OrgPackProposal,
232
+ SNAPSHOT_FILE,
233
+ SnapshotManifest,
234
+ acceptOrgPackDraft,
235
+ correctableFields,
236
+ draftOrgPack,
237
+ filterItems,
238
+ itemTitle,
239
+ keepCandidate,
240
+ readConflicts,
241
+ readFind,
242
+ readFinds,
243
+ readKnowledge,
244
+ readReviews,
245
+ readSnapshot,
246
+ readSourceSettings,
247
+ reviewItem,
248
+ scopeWords,
249
+ searchPages,
250
+ snapshotDates,
251
+ snapshotSources,
252
+ writeReview
253
+ } from "./chunk-L24C55VN.js";
254
+ import {
255
+ NoSuchNode,
256
+ applyCorrections,
257
+ buildEstate,
258
+ correctedFact,
259
+ correctionFor,
260
+ focusView,
261
+ frameworkFacts,
262
+ frameworkGaps,
263
+ nodeView,
264
+ viewKey
265
+ } from "./chunk-PAMPPUR6.js";
266
+ import {
267
+ readServiceRequestExports,
268
+ readWorkItemExports
269
+ } from "./chunk-23W7VWKR.js";
270
+ import "./chunk-7EKXJZ2Y.js";
271
+ import "./chunk-MC3H7736.js";
272
+ import "./chunk-T6AIP7H5.js";
273
+ import "./chunk-JPGCERE2.js";
274
+ import {
275
+ baseClonePath,
276
+ loadRepos,
277
+ readChangeRepos
278
+ } from "./chunk-KJPMEAI4.js";
279
+ import "./chunk-GQ4N4WKF.js";
280
+ import {
281
+ COMMON_SETTINGS,
282
+ PROVIDER_SETTINGS,
283
+ defaultKeyStore,
284
+ looksLikeKey
285
+ } from "./chunk-YEDDOYSC.js";
286
+ import {
287
+ providerFactoryFor
288
+ } from "./chunk-CNUKCS2T.js";
289
+ import {
290
+ ASSISTANTS,
291
+ ChangeArtifact,
292
+ DEFAULT_PORT,
293
+ DEFAULT_RUNTIME,
294
+ DiagramLayout,
295
+ FACT_FIXED_FIELDS,
296
+ FactCorrection,
297
+ KNOWLEDGE_KINDS,
298
+ KNOWLEDGE_RECORD_KIND,
299
+ LAYOUT_SCHEMA_VERSION,
300
+ MANUAL_SYSTEMS,
301
+ QuestionError,
302
+ QuestionState,
303
+ RUN_SCHEMA_VERSION,
304
+ RecordId,
305
+ Runtime,
306
+ SOURCE_KIND_SUGGESTIONS,
307
+ ViewKey,
308
+ answerQuestion,
309
+ api_exports,
310
+ catalogDirOf,
311
+ changesDirOf,
312
+ changesRelOf,
313
+ correctionLine,
314
+ external_exports,
315
+ factsDirOf,
316
+ factsFileOf,
317
+ factsRelOf,
318
+ gateRoundLimit,
319
+ inputsDirOf,
320
+ isAdopted,
321
+ knowledgeDirOf,
322
+ knowledgeKindOf,
323
+ knowledgeRelOf,
324
+ layoutPath,
325
+ listQuestions,
326
+ loadOrgPack,
327
+ orgPackDirOf,
328
+ pageStanding,
329
+ parseFacts,
330
+ parseFactsLines,
331
+ proposalsDirOf,
332
+ proposalsRelOf,
333
+ readEstateJoins,
334
+ readQuestion,
335
+ readRepoSpec,
336
+ require_dist,
337
+ runsDirOf,
338
+ runsRelOf,
339
+ specRelOf,
340
+ specRepos
341
+ } from "./chunk-S2A7YC3Q.js";
342
+ import {
343
+ createLog,
344
+ writeFileAtomic
345
+ } from "./chunk-D3PO4DOZ.js";
346
+ import {
347
+ __toESM
348
+ } from "./chunk-TV45EUGM.js";
349
+
350
+ // packages/cli/dist/ui-commands.js
351
+ import { spawn } from "node:child_process";
352
+ import { isAbsolute, join as join12, relative as relative3, sep as sep3 } from "node:path";
353
+
354
+ // packages/ui/dist-lib/index.js
355
+ import { dirname, join } from "node:path";
356
+ import { fileURLToPath } from "node:url";
357
+ var __atticus_import_meta_url = __atticusUrl("packages/ui/dist-lib/index.js");
358
+ function uiDistDir() {
359
+ return join(dirname(fileURLToPath(__atticus_import_meta_url)), "..", "dist");
360
+ }
361
+
362
+ // packages/cli/dist/ui-api.js
363
+ import { createHash } from "node:crypto";
364
+ import { readdir, readFile, writeFile } from "node:fs/promises";
365
+ import { join as join2 } from "node:path";
366
+ async function run(f) {
367
+ const out = [];
368
+ const err = [];
369
+ const code = await f({ out: (l) => out.push(l), err: (l) => err.push(l) });
370
+ return { code, out, err };
371
+ }
372
+ function refused(r) {
373
+ const detail = (r.err.length ? r.err : r.out).join("\n") || "The command refused.";
374
+ return r.code === 2 ? new HttpError(400, "The request isn't valid", detail) : new HttpError(422, "Atticus can't do that yet", detail, { lines: [...r.out, ...r.err] });
375
+ }
376
+ function parseBody(schema, body) {
377
+ const r = schema.safeParse(body ?? {});
378
+ if (!r.success)
379
+ throw badRequest("Some fields aren't right.", {
380
+ errors: r.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
381
+ });
382
+ return r.data;
383
+ }
384
+ var sha = (s) => createHash("sha256").update(s).digest("hex");
385
+ var MAX_ATTACH = 15 * 1024 * 1024;
386
+ var NewChange = api_exports.NewChangeRequest;
387
+ async function latestFindings(root, change) {
388
+ const out = [];
389
+ for (const gate of await reviewedGates(root, change)) {
390
+ const rounds = await roundsOf(root, change, gate);
391
+ if (rounds.length)
392
+ out.push(...(await readRound(root, change, gate, rounds.at(-1))).findings.map((f) => f.finding));
393
+ }
394
+ return out;
395
+ }
396
+ async function readChange(root, id) {
397
+ try {
398
+ return await changeStore(root).read(id);
399
+ } catch {
400
+ throw notFound(`There is no change ${id}.`);
401
+ }
402
+ }
403
+ async function changeDetail(root, id) {
404
+ const c = await readChange(root, id);
405
+ const report = await changeReport(root, id);
406
+ const findings = await latestFindings(root, id);
407
+ const open = { blocking: 0, gap: 0, risk: 0, idea: 0 };
408
+ for (const f of findings)
409
+ if (f.state === "open")
410
+ open[f.severity] += 1;
411
+ const dir = join2(changesDirOf(root), id);
412
+ const phases = report.type.phases;
413
+ const current = report.phase ? phases.indexOf(report.phase) : phases.length;
414
+ const artifacts = [];
415
+ const blockedArtifacts = new Set(report.blockers.map((b) => b.artifact).filter(Boolean));
416
+ const cctx = await loadChangeContext(root);
417
+ const onStages = (await Promise.all(cctx.policy.stages.map((s) => readFile(join2(dir, ...s.loop.split("/"))).then(() => true, () => false)))).some(Boolean);
418
+ const type = report.type.artifacts;
419
+ const ofStages = cctx.policy.stages.flatMap((s) => s.artifacts).filter((a, i, all) => type.includes(a) && all.indexOf(a) === i);
420
+ const due = /* @__PURE__ */ new Set();
421
+ for (const p of phases)
422
+ for (const a of requiredArtifacts(cctx, report.type, p, report.footprint))
423
+ due.add(a);
424
+ const order = onStages ? [...ofStages, ...type.filter((a) => !ofStages.includes(a))] : type;
425
+ for (const a of order) {
426
+ const files = await artifactFiles(dir, a);
427
+ if (onStages && !files.length && !(ofStages.includes(a) && due.has(a)))
428
+ continue;
429
+ const at = phases.indexOf(artifactPhase(cctx, report.type, a));
430
+ const done = files.length > 0 && !blockedArtifacts.has(a);
431
+ artifacts.push({
432
+ artifact: a,
433
+ path: `${changesRelOf(root)}/${id}/${files[0] ?? `${a}.md`}`,
434
+ status: done ? "done" : at <= current ? "ready" : "blocked",
435
+ openFindings: findings.filter((f) => f.state === "open" && f.artifact.startsWith(a)).length
436
+ });
437
+ }
438
+ const h = liveHandoff(c) ?? c.handoffs.at(-1);
439
+ const fast = (await changeSize(cctx, c)).fast;
440
+ return {
441
+ id: c.id,
442
+ title: c.title,
443
+ type: c.type,
444
+ state: c.state,
445
+ repos: c.touches.repos,
446
+ openFindings: open,
447
+ updatedAt: c.history.at(-1)?.at ?? c.createdAt,
448
+ ...fast ? { fastPath: true } : {},
449
+ artifacts,
450
+ ...h ? {
451
+ handoff: {
452
+ phase: h.phase,
453
+ ...h.pr?.url ? { pr: h.pr.url } : {},
454
+ state: h.state === "merged" ? "merged" : h.state === "closed" ? "closed" : "open"
455
+ }
456
+ } : {}
457
+ };
458
+ }
459
+ async function runView(root, change, runId) {
460
+ const dir = join2(runsDirOf(root), change, runId);
461
+ const text2 = await readFile(join2(dir, "run.json"), "utf8").catch(() => void 0);
462
+ if (!text2)
463
+ return void 0;
464
+ const r = JSON.parse(text2);
465
+ const manifest = await readFile(join2(dir, "context-manifest.json"), "utf8").catch(() => "");
466
+ return {
467
+ schemaVersion: RUN_SCHEMA_VERSION,
468
+ runId: r.runId,
469
+ change: r.request.change,
470
+ // A run records the whole agent definition; the view names it.
471
+ agent: typeof r.request.agent === "string" ? r.request.agent : r.request.agent.id,
472
+ runtime: r.runtime,
473
+ state: r.state,
474
+ startedAt: r.startedAt,
475
+ ...r.finishedAt ? { finishedAt: r.finishedAt } : {},
476
+ ...r.request.outputPath ? { output: r.request.outputPath } : {},
477
+ contextManifestSha256: sha(manifest),
478
+ errors: r.errors ?? [],
479
+ attempt: r.attempt ?? 1,
480
+ ...r.stepClass ? { stepClass: r.stepClass } : {},
481
+ ...r.model ? { model: r.model } : {},
482
+ promptPath: `${runsRelOf(root)}/${change}/${runId}/prompt.md`
483
+ };
484
+ }
485
+ async function findRun(root, runId) {
486
+ if (!/^run-[0-9a-z-]+$/.test(runId))
487
+ throw notFound(`There is no run ${runId}.`);
488
+ for (const change of await readdir(runsDirOf(root)).catch(() => [])) {
489
+ const v = await runView(root, change, runId);
490
+ if (v)
491
+ return v;
492
+ }
493
+ throw notFound(`There is no run ${runId}.`);
494
+ }
495
+ async function artifactPath(root, id, artifact) {
496
+ const a = ChangeArtifact.safeParse(artifact);
497
+ if (!a.success)
498
+ throw badRequest(`${artifact} is not an artifact. Use one of ${ChangeArtifact.options.join(", ")}.`);
499
+ const dir = join2(changesDirOf(root), id);
500
+ const files = await artifactFiles(dir, a.data);
501
+ if (files.length === 0)
502
+ throw notFound(`Change ${id} has no ${artifact} yet.`);
503
+ return files[0];
504
+ }
505
+ function uiRoutes(r, ctx, deps = {}) {
506
+ const { root, user, jobs } = ctx;
507
+ const changeDeps = {
508
+ ...deps.gitHost ? { gitHost: deps.gitHost } : {},
509
+ ...deps.now ? { now: deps.now } : {}
510
+ };
511
+ return r.add("POST", "/api/changes", async ({ body }) => {
512
+ const b = parseBody(NewChange, body);
513
+ const owners = Object.entries(b.owners ?? {}).map(([role, who]) => `${role}=${who.join(",")}`);
514
+ const res = await run((io) => changeNew(root, {
515
+ id: b.id,
516
+ title: b.title,
517
+ type: b.type,
518
+ by: user,
519
+ owners,
520
+ ...b.systems ? { systems: b.systems } : {},
521
+ ...b.template ? { template: b.template } : {},
522
+ ...b.like ? { like: b.like } : {}
523
+ }, io, changeDeps));
524
+ if (res.code !== 0) {
525
+ if (res.err.some((l) => /already exists/i.test(l)))
526
+ throw conflict(res.err.join("\n"));
527
+ throw refused(res);
528
+ }
529
+ return { status: 201, body: await changeDetail(root, b.id) };
530
+ }).add("GET", "/api/changes/{changeId}", async ({ params }) => ({
531
+ status: 200,
532
+ body: await changeDetail(root, params.changeId)
533
+ })).add("GET", "/api/changes/{changeId}/status", async ({ params }) => {
534
+ await readChange(root, params.changeId);
535
+ const rep = await changeReport(root, params.changeId);
536
+ return {
537
+ status: 200,
538
+ body: {
539
+ state: rep.state,
540
+ ...rep.phase ? { phase: rep.phase } : {},
541
+ ...rep.next ? { next: rep.next } : {},
542
+ ...rep.progress ? { progress: rep.progress } : {},
543
+ nextStep: rep.nextStep,
544
+ risk: rep.risk,
545
+ tests: rep.tests,
546
+ ...rep.handoff ? {
547
+ handoff: {
548
+ pr: rep.handoff.pr,
549
+ state: rep.handoff.state,
550
+ phase: rep.handoff.phase
551
+ }
552
+ } : {},
553
+ blockers: rep.blockers,
554
+ reasons: rep.reasons
555
+ }
556
+ };
557
+ }).add("GET", "/api/changes/{changeId}/artifacts/{artifact}", async ({ params }) => {
558
+ await readChange(root, params.changeId);
559
+ const rel = await artifactPath(root, params.changeId, params.artifact);
560
+ const content = await readFile(join2(changesDirOf(root), params.changeId, rel), "utf8");
561
+ return {
562
+ status: 200,
563
+ body: {
564
+ artifact: params.artifact,
565
+ path: `${changesRelOf(root)}/${params.changeId}/${rel}`,
566
+ content,
567
+ sha256: sha(content)
568
+ }
569
+ };
570
+ }).add("PUT", "/api/changes/{changeId}/artifacts/{artifact}", async ({ params, body }) => {
571
+ await readChange(root, params.changeId);
572
+ const b = parseBody(api_exports.SaveArtifactRequest, body);
573
+ const rel = await artifactPath(root, params.changeId, params.artifact);
574
+ const file = join2(changesDirOf(root), params.changeId, rel);
575
+ const now = await readFile(file, "utf8");
576
+ if (sha(now) !== b.baseSha256)
577
+ throw conflict(`${rel} changed since you opened it. Reload it, then make your edit again.`);
578
+ await writeFile(file, b.content, "utf8");
579
+ ctx.events.publish({
580
+ type: "file.changed",
581
+ path: `${changesRelOf(root)}/${params.changeId}/${rel}`
582
+ });
583
+ return {
584
+ status: 200,
585
+ body: {
586
+ artifact: params.artifact,
587
+ path: `${changesRelOf(root)}/${params.changeId}/${rel}`,
588
+ content: b.content,
589
+ sha256: sha(b.content)
590
+ }
591
+ };
592
+ }).add("GET", "/api/changes/{changeId}/tasks", async ({ params }) => {
593
+ await readChange(root, params.changeId);
594
+ const files = await readChangeFiles(join2(changesDirOf(root), params.changeId));
595
+ return {
596
+ status: 200,
597
+ body: readChangeTasks(files).map((t) => ({
598
+ id: t.id,
599
+ repo: t.repo,
600
+ description: t.title,
601
+ done: t.done,
602
+ ...t.stories[0] ? { story: t.stories[0] } : {},
603
+ branch: `task/${params.changeId}/${t.id}`
604
+ }))
605
+ };
606
+ }).add("POST", "/api/changes/{changeId}/workspace", async ({ params, body }) => {
607
+ await readChange(root, params.changeId);
608
+ const b = parseBody(api_exports.OpenWorkspaceRequest, body);
609
+ const res = await run((io) => workspaceOpen(root, params.changeId, b.repos, io));
610
+ if (res.code !== 0)
611
+ throw refused(res);
612
+ const repos = await readFile(join2(changesDirOf(root), params.changeId, "repos.yaml"), "utf8").catch(() => "");
613
+ return { status: 200, body: { lines: res.out, reposYaml: repos } };
614
+ }).add("POST", "/api/changes/{changeId}/handoff", async ({ params, body }) => {
615
+ await readChange(root, params.changeId);
616
+ const b = parseBody(api_exports.HandoffRequest, body);
617
+ const res = await run((io) => changeHandoff(root, params.changeId, { by: user, approve: true, ...b.repo ? { repo: b.repo } : {} }, io, changeDeps));
618
+ if (res.code !== 0)
619
+ throw refused(res);
620
+ return { status: 201, body: await changeDetail(root, params.changeId) };
621
+ }).add("POST", "/api/changes/{changeId}/sync", async ({ params, body }) => {
622
+ await readChange(root, params.changeId);
623
+ const b = parseBody(api_exports.SyncRequest, body);
624
+ const res = await run((io) => changeSync(root, params.changeId, { by: user, approve: true, ...b.repo ? { repo: b.repo } : {} }, io, changeDeps));
625
+ if (res.code === 2)
626
+ throw refused(res);
627
+ return {
628
+ status: 200,
629
+ body: {
630
+ ...await changeDetail(root, params.changeId),
631
+ lines: [...res.out, ...res.err]
632
+ }
633
+ };
634
+ }).add("GET", "/api/changes/{changeId}/inputs", async ({ params }) => {
635
+ const c = await readChange(root, params.changeId);
636
+ const { readChangeInputs } = await import("./chunk-6JCPI55B.js");
637
+ return {
638
+ status: 200,
639
+ body: api_exports.ChangeInputList.parse(await readChangeInputs(join2(changesDirOf(root), c.id)))
640
+ };
641
+ }).add("GET", "/api/changes/{changeId}/prototypes", async ({ params }) => {
642
+ const c = await readChange(root, params.changeId);
643
+ const { changePrototypes } = await import("./chunk-KUUQR4HA.js");
644
+ return {
645
+ status: 200,
646
+ body: api_exports.ChangePrototypeList.parse(await changePrototypes(root, c.id))
647
+ };
648
+ }).add("POST", "/api/changes/{changeId}/inputs", async ({ params, body }) => {
649
+ const c = await readChange(root, params.changeId);
650
+ const b = parseBody(api_exports.AttachFileRequest, body);
651
+ const bytes = new Uint8Array(Buffer.from(b.content, "base64"));
652
+ if (bytes.length > MAX_ATTACH)
653
+ throw new HttpError(413, "The file is too large", "Attach a file of 15 MB or less.");
654
+ const { attachFiles } = await import("./chunk-BYZCY6SP.js");
655
+ const r2 = await attachFiles(root, c.id, [{ name: b.name, bytes }], user);
656
+ if (!r2.added.length)
657
+ throw new HttpError(422, "Atticus can't attach that", r2.refused[0]?.why ?? "", {
658
+ lines: r2.refused.map((x) => `${x.name}: ${x.why}`)
659
+ });
660
+ return { status: 201, body: api_exports.AttachResult.parse(r2) };
661
+ }, { maxBody: Math.ceil(MAX_ATTACH * 4 / 3) + 64 * 1024 }).add("GET", "/api/changes/{changeId}/runs", async ({ params }) => {
662
+ await readChange(root, params.changeId);
663
+ const dir = join2(runsDirOf(root), params.changeId);
664
+ const out = [];
665
+ for (const id of (await readdir(dir).catch(() => [])).sort()) {
666
+ const v = await runView(root, params.changeId, id);
667
+ if (v)
668
+ out.push(v);
669
+ }
670
+ return { status: 200, body: out };
671
+ }).add("POST", "/api/changes/{changeId}/runs", async ({ params, body }) => {
672
+ await readChange(root, params.changeId);
673
+ const b = parseBody(api_exports.StartRunRequest, body);
674
+ const before = new Set(await readdir(join2(runsDirOf(root), params.changeId)).catch(() => []));
675
+ let res;
676
+ if (b.task)
677
+ res = await run((io) => taskRun(root, params.changeId, b.task, {
678
+ mode: b.runtime,
679
+ by: user,
680
+ ...b.autonomy ? {
681
+ level: b.autonomy === "draft-and-pr" ? "pr" : b.autonomy === "plan-only" ? "plan" : "draft"
682
+ } : {}
683
+ }, io));
684
+ else if (b.agent === "story-writer")
685
+ res = await run((io) => planStories(root, params.changeId, { mode: b.runtime }, io));
686
+ else if (b.agent.endsWith("-critic"))
687
+ res = await run((io) => critic(root, params.changeId, { mode: b.runtime }, io));
688
+ else
689
+ throw badRequest(`Start ${b.agent} from its screen; this call starts task runs, the story writer and the critic.`);
690
+ if (res.code === 2)
691
+ throw refused(res);
692
+ const after = (await readdir(join2(runsDirOf(root), params.changeId)).catch(() => [])).filter((x) => !before.has(x)).sort();
693
+ if (after.length === 0)
694
+ throw refused(res);
695
+ const v = await runView(root, params.changeId, after.at(-1));
696
+ for (const id of after)
697
+ ctx.events.publish({
698
+ type: "run.changed",
699
+ runId: id,
700
+ state: (await runView(root, params.changeId, id))?.state ?? "prepared"
701
+ });
702
+ return { status: 201, body: v };
703
+ }).add("POST", "/api/changes/{changeId}/critic", async ({ params, body }) => {
704
+ await readChange(root, params.changeId);
705
+ const b = parseBody(api_exports.RunCriticRequest, body);
706
+ const op = await loadOrgPack(orgPackDirOf(root));
707
+ const mode = op.runtime.ai === "off" ? void 0 : "hand-off";
708
+ const once = (gate) => run((io) => critic(root, params.changeId, { ...gate ? { gate } : {}, ...mode ? { mode } : {}, json: true }, io));
709
+ let res = await once(b.gate);
710
+ if (!b.gate && res.code === 2 && res.err.some((l) => /has no gate/.test(l))) {
711
+ const next = (await changeReport(root, params.changeId)).next;
712
+ if (next)
713
+ res = await once(next);
714
+ }
715
+ if (res.code === 2)
716
+ throw refused(res);
717
+ ctx.events.publish({
718
+ type: "file.changed",
719
+ path: `${changesRelOf(root)}/${params.changeId}/review`
720
+ });
721
+ let round;
722
+ try {
723
+ round = JSON.parse(res.out.join("\n"));
724
+ } catch {
725
+ throw refused(res);
726
+ }
727
+ return { status: 200, body: { ai: op.runtime.ai, round } };
728
+ }).add("GET", "/api/runs/{runId}", async ({ params }) => ({
729
+ status: 200,
730
+ body: await findRun(root, params.runId)
731
+ })).add("POST", "/api/runs/{runId}/done", async ({ params }) => {
732
+ const before = await findRun(root, params.runId);
733
+ if (before.state === "validated" || before.state === "cancelled")
734
+ throw conflict(`Run ${params.runId} is already ${before.state}.`);
735
+ const res = await run((io) => handoffDone(root, params.runId, io));
736
+ const after = await findRun(root, params.runId);
737
+ ctx.events.publish({ type: "run.changed", runId: after.runId, state: after.state });
738
+ return { status: 200, body: { ...after, lines: [...res.out, ...res.err] } };
739
+ }).add("POST", "/api/runs/{runId}/retry", async ({ params }) => {
740
+ const v = await findRun(root, params.runId);
741
+ if (v.state !== "failed")
742
+ throw conflict(`Only a failed run can be retried; ${v.runId} is ${v.state}.`);
743
+ const file = join2(runsDirOf(root), v.change, v.runId, "run.json");
744
+ const rec = JSON.parse(await readFile(file, "utf8"));
745
+ rec.state = "prepared";
746
+ rec.attempt = (rec.attempt ?? 1) + 1;
747
+ rec.errors = [];
748
+ await writeFile(file, JSON.stringify(rec, null, 2) + "\n");
749
+ const after = await findRun(root, params.runId);
750
+ ctx.events.publish({ type: "run.changed", runId: after.runId, state: after.state });
751
+ return { status: 200, body: after };
752
+ }).add("POST", "/api/runs/{runId}/cancel", async ({ params }) => {
753
+ const v = await findRun(root, params.runId);
754
+ if (["validated", "cancelled"].includes(v.state))
755
+ throw conflict(`Run ${v.runId} is already ${v.state}.`);
756
+ const file = join2(runsDirOf(root), v.change, v.runId, "run.json");
757
+ const rec = JSON.parse(await readFile(file, "utf8"));
758
+ rec.state = "cancelled";
759
+ rec.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
760
+ await writeFile(file, JSON.stringify(rec, null, 2) + "\n");
761
+ const after = await findRun(root, params.runId);
762
+ ctx.events.publish({ type: "run.changed", runId: after.runId, state: after.state });
763
+ return { status: 200, body: after };
764
+ }).add("POST", "/api/findings/{changeId}/{findingId}/action", async ({ params, body }) => {
765
+ await readChange(root, params.changeId);
766
+ const b = parseBody(api_exports.FindingActionRequest, body);
767
+ const res = await run((io) => findingAction(root, params.changeId, params.findingId, b.action, {
768
+ by: user,
769
+ ...b.reason ? { reason: b.reason } : {},
770
+ ...b.askedOf ? { askedOf: b.askedOf } : {},
771
+ ...b.patch ? { edit: b.patch } : {},
772
+ ...b.gate ? { gate: b.gate } : {}
773
+ }, io));
774
+ if (res.code !== 0) {
775
+ if (res.err.some((l) => /no finding|not found/i.test(l)))
776
+ throw notFound(res.err.join("\n"));
777
+ if (res.err.some((l) => /can't be waived|not allowed|needs/i.test(l)))
778
+ throw new HttpError(403, "Not allowed", res.err.join("\n"));
779
+ throw refused(res);
780
+ }
781
+ const f = (await latestFindings(root, params.changeId)).find((x) => x.id === params.findingId);
782
+ return { status: 200, body: f };
783
+ }).add("POST", "/api/scan", ({ body }) => {
784
+ const b = parseBody(api_exports.ScanRequest, body);
785
+ if (jobs.runningOf("scan"))
786
+ throw conflict("A scan is already running.");
787
+ const job = jobs.start("scan", async (c) => {
788
+ c.progress(0, 1, "Scanning");
789
+ const res = await run((io) => scanCommand(root, {
790
+ ...b.repos ? { repos: b.repos } : {},
791
+ full: b.full,
792
+ json: true,
793
+ // ADR 0123: another branch, scanned beside the baseline.
794
+ ...b.ref ? { ref: b.ref } : {}
795
+ }, io));
796
+ c.progress(1, 1, "Scanned");
797
+ const parsed = (() => {
798
+ try {
799
+ return JSON.parse(res.out.join("\n"));
800
+ } catch {
801
+ return {};
802
+ }
803
+ })();
804
+ const problems = parsed.problems?.length ?? 0;
805
+ ctx.events.publish({
806
+ type: "index.rebuilt",
807
+ records: Number(parsed.stats?.records ?? 0)
808
+ });
809
+ return {
810
+ result: { stats: parsed.stats, problems },
811
+ errors: res.code === 2 ? res.err : []
812
+ };
813
+ });
814
+ return { status: 202, body: { jobId: job.id } };
815
+ }).add("POST", "/api/workspace/sync", ({ body }) => {
816
+ const b = parseBody(api_exports.ScanRequest.pick({ repos: true }), body);
817
+ const job = jobs.start("sync", async (c) => {
818
+ c.progress(0, 1, "Syncing base clones");
819
+ const res = await run((io) => workspaceSync(root, b.repos, io));
820
+ c.progress(1, 1, "Synced");
821
+ return { result: { lines: res.out }, errors: res.code === 0 ? [] : res.err };
822
+ });
823
+ return { status: 202, body: { jobId: job.id } };
824
+ }).add("POST", "/api/reconcile", ({ body }) => {
825
+ const b = parseBody(api_exports.ReconcileRequest, body);
826
+ const job = jobs.start("reconcile", async (c) => {
827
+ c.progress(0, 1, `Reconciling ${b.diagram}`);
828
+ const res = await run((io) => reconcileCommand(root, [b.diagram], { json: true }, io));
829
+ c.progress(1, 1, "Reconciled");
830
+ let result;
831
+ try {
832
+ result = JSON.parse(res.out.join("\n"));
833
+ } catch {
834
+ result = { lines: res.out };
835
+ }
836
+ return { result, errors: res.code === 2 ? res.err : [] };
837
+ });
838
+ return { status: 202, body: { jobId: job.id } };
839
+ });
840
+ }
841
+
842
+ // packages/cli/dist/ui-api-run.js
843
+ var import_yaml = __toESM(require_dist(), 1);
844
+ import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
845
+ import { join as join3, resolve } from "node:path";
846
+ async function run2(f) {
847
+ const out = [];
848
+ const err = [];
849
+ const code = await f({ out: (l) => out.push(l), err: (l) => err.push(l) });
850
+ return { code, out, err };
851
+ }
852
+ function refused2(r) {
853
+ const detail = (r.err.length ? r.err : r.out).join("\n") || "The command refused.";
854
+ return r.code === 2 ? new HttpError(400, "The request isn't valid", detail) : new HttpError(422, "Atticus can't do that yet", detail, { lines: [...r.out, ...r.err] });
855
+ }
856
+ function parseBody2(schema, body) {
857
+ const r = schema.safeParse(body ?? {});
858
+ if (!r.success)
859
+ throw badRequest("Some fields aren't right.", {
860
+ errors: r.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
861
+ });
862
+ return r.data;
863
+ }
864
+ function jsonOf(r) {
865
+ const text2 = r.out.join("\n").trim();
866
+ if (!text2.startsWith("{") && !text2.startsWith("["))
867
+ return void 0;
868
+ try {
869
+ return JSON.parse(text2);
870
+ } catch {
871
+ return void 0;
872
+ }
873
+ }
874
+ function viewOf(r) {
875
+ const out = r.code === 0 ? jsonOf(r) : void 0;
876
+ if (out !== void 0)
877
+ return out;
878
+ if (r.err.some((l) => /^There is no change/.test(l)))
879
+ throw notFound(r.err.join("\n"));
880
+ throw refused2(r.code === 0 ? { ...r, code: 1 } : r);
881
+ }
882
+ async function readChange2(root, id) {
883
+ try {
884
+ return await changeStore(root).read(id);
885
+ } catch {
886
+ throw notFound(`There is no change ${id}.`);
887
+ }
888
+ }
889
+ async function openChanges2(root) {
890
+ return (await changeStore(root).list()).filter((c) => c.state !== "archived").sort((a, b) => a.id.localeCompare(b.id));
891
+ }
892
+ async function latestFindings2(root, change) {
893
+ const out = [];
894
+ for (const gate of await reviewedGates(root, change)) {
895
+ const rounds = await roundsOf(root, change, gate);
896
+ if (rounds.length)
897
+ out.push(...(await readRound(root, change, gate, rounds.at(-1))).findings.map((f) => f.finding));
898
+ }
899
+ return out;
900
+ }
901
+ async function filesUnder(dir, prefix = "") {
902
+ const out = [];
903
+ let entries;
904
+ try {
905
+ entries = await readdir2(dir, { withFileTypes: true });
906
+ } catch {
907
+ return out;
908
+ }
909
+ for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
910
+ const rel = prefix ? `${prefix}/${e.name}` : e.name;
911
+ if (e.isDirectory())
912
+ out.push(...await filesUnder(join3(dir, e.name), rel));
913
+ else
914
+ out.push({ path: rel, bytes: (await stat(join3(dir, e.name))).size });
915
+ }
916
+ return out;
917
+ }
918
+ var RUNTIME_KIND = {
919
+ "work-tracker": "workTracker",
920
+ "service-desk": "serviceDesk",
921
+ "git-host": "gitHost",
922
+ wiki: "wiki",
923
+ notify: "notify",
924
+ design: "design"
925
+ };
926
+ async function executorsOf(root, deps) {
927
+ return deps.todo?.executors ? { executors: deps.todo.executors, problems: [] } : await loadExecutors(root);
928
+ }
929
+ var stepRef = (id, change) => change ? `${change}/${id}` : id;
930
+ function stepView(s, all, pack, configured) {
931
+ const proc = s.procedure ? pack.procedures.find((p) => p.id === s.procedure) : void 0;
932
+ return {
933
+ id: s.id,
934
+ ...s.change ? { change: s.change } : {},
935
+ system: s.system,
936
+ action: s.action,
937
+ state: s.state,
938
+ title: s.title,
939
+ where: s.where,
940
+ ...s.procedure ? { procedure: s.procedure } : {},
941
+ ...s.procedureStep ? { procedureStep: s.procedureStep } : {},
942
+ fields: s.fields.map((f) => ({
943
+ name: f.name,
944
+ value: f.value,
945
+ ...f.help ? { help: f.help } : {}
946
+ })),
947
+ instructions: s.instructions,
948
+ result: s.result,
949
+ waitsFor: s.dependsOn.filter((d) => all.find((x) => x.id === d)?.state === "open"),
950
+ ...s.reason ? { reason: s.reason } : {},
951
+ ...s.recorded ? { recorded: s.recorded } : {},
952
+ runs: s.runs.map((r) => ({
953
+ runId: r.runId,
954
+ executor: r.executor,
955
+ at: r.at,
956
+ status: r.status,
957
+ mode: r.mode,
958
+ by: r.approval.by,
959
+ ...r.message ? { message: r.message } : {}
960
+ })),
961
+ /** Executors that may do this step: the procedure allows them and this person has them. */
962
+ executors: (proc?.executors.allowed ?? []).filter((n) => configured.has(n)),
963
+ createdAt: s.createdAt,
964
+ createdBy: s.createdBy
965
+ };
966
+ }
967
+ async function findStep(root, id, change) {
968
+ const all = await fileStepStore(root).list(change ? { change } : {});
969
+ const s = all.find((x) => x.id === id);
970
+ if (!s)
971
+ throw notFound(`There is no step ${stepRef(id, change)}.`);
972
+ return s;
973
+ }
974
+ var SyncEntries = external_exports.object({
975
+ requests: external_exports.array(external_exports.object({ key: external_exports.string(), id: external_exports.string() })).default([])
976
+ });
977
+ async function changeRequests(root, change, templates, steps) {
978
+ const dir = changeFolder(root, change);
979
+ const drafts = readSnowDrafts(await readChangeFiles(dir));
980
+ if (drafts.length === 0)
981
+ return [];
982
+ let synced = [];
983
+ try {
984
+ synced = SyncEntries.parse((0, import_yaml.parse)(await readFile2(join3(dir, SYNC_FILE), "utf8"))).requests;
985
+ } catch {
986
+ }
987
+ return drafts.map((d) => {
988
+ const h = d.heading.toLowerCase();
989
+ const t = templates.find((x) => [x.requestType, x.name, x.id].some((n) => h.includes(n.toLowerCase())));
990
+ const record = /:\s*(\S+)\s*$/.exec(d.heading)?.[1] ?? "";
991
+ const fields = t ? t.fields.map((f) => ({
992
+ name: f.name,
993
+ value: d.fields.get(f.name.toLowerCase())?.value ?? "",
994
+ required: f.required
995
+ })) : [...d.fields].map(([name, v2]) => ({ name, value: v2.value, required: false }));
996
+ const base = {
997
+ change,
998
+ heading: d.heading,
999
+ ...t ? { template: t.id, leadTimeDays: t.leadTimeDays } : {},
1000
+ requestType: t?.requestType ?? d.heading,
1001
+ record,
1002
+ approvers: t?.approvers ?? [],
1003
+ fields,
1004
+ missing: fields.filter((f) => f.required && !f.value.trim()).map((f) => f.name)
1005
+ };
1006
+ const entry = t ? synced.find((e) => e.key === `${t.id}|${record}`) : void 0;
1007
+ if (!entry)
1008
+ return { ...base, state: "to-file" };
1009
+ const stepId = stepOfProvisional(entry.id);
1010
+ if (!stepId)
1011
+ return { ...base, state: "filed", number: entry.id };
1012
+ const s = steps.find((x) => x.id === stepId);
1013
+ const v = s?.state === "done" ? s.recorded?.values : void 0;
1014
+ return typeof v?.requestNumber === "string" ? {
1015
+ ...base,
1016
+ state: "filed",
1017
+ step: stepId,
1018
+ number: v.requestNumber,
1019
+ ...typeof v.url === "string" ? { url: v.url } : {}
1020
+ } : { ...base, state: "waiting", step: stepId };
1021
+ });
1022
+ }
1023
+ function questionOf(check) {
1024
+ if (check.type === "answer")
1025
+ return check.question;
1026
+ if (check.type === "all") {
1027
+ for (const c of check.checks)
1028
+ if (c.type === "answer")
1029
+ return c.question;
1030
+ }
1031
+ return void 0;
1032
+ }
1033
+ var commandLine = (argv) => `atticus ${argv.map((a) => /[\s"']/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a).join(" ")}`;
1034
+ function stepOfLesson(l, id) {
1035
+ const s = l.steps.find((x) => x.id === id);
1036
+ if (!s)
1037
+ return void 0;
1038
+ const q = questionOf(s.check);
1039
+ return {
1040
+ id: s.id,
1041
+ n: l.steps.indexOf(s) + 1,
1042
+ title: s.title,
1043
+ body: s.body,
1044
+ commands: s.do.map(commandLine),
1045
+ ...q ? { question: q } : {},
1046
+ hint: s.hint
1047
+ };
1048
+ }
1049
+ var ALL = /* @__PURE__ */ new Set(["1", "true", "yes"]);
1050
+ function uiRunRoutes(r, ctx, deps = {}) {
1051
+ const { root, user } = ctx;
1052
+ const nowDate = deps.now ? () => new Date(deps.now) : void 0;
1053
+ const pack = () => loadOrgPack(orgPackDirOf(root));
1054
+ const rel = deps.now ? { now: () => new Date(deps.now) } : {};
1055
+ return r.add("GET", "/api/todo", async ({ query }) => {
1056
+ const change = strParam(query, "change");
1057
+ const op = await pack();
1058
+ let steps;
1059
+ try {
1060
+ steps = await fileStepStore(root).list(change ? { change } : {});
1061
+ } catch (e) {
1062
+ throw new HttpError(422, "A step file can't be read", e.message);
1063
+ }
1064
+ const all = ALL.has(strParam(query, "all") ?? "");
1065
+ const ex = await executorsOf(root, deps);
1066
+ const configured = new Set(ex.executors.map((e) => e.name));
1067
+ const inputs = { root, inputsDir: inputsDirOf(root) };
1068
+ const [items, requests] = await Promise.all([
1069
+ readWorkItemExports(inputs),
1070
+ readServiceRequestExports(inputs)
1071
+ ]);
1072
+ return {
1073
+ status: 200,
1074
+ body: {
1075
+ steps: steps.filter((s) => all || s.state === "open").map((s) => stepView(s, steps, op, configured)),
1076
+ sources: MANUAL_SYSTEMS.map((system) => ({
1077
+ system,
1078
+ configured: op.runtime.adapters[RUNTIME_KIND[system]]?.type ?? null,
1079
+ open: steps.filter((s) => s.system === system && s.state === "open").length
1080
+ })),
1081
+ exports: {
1082
+ workItems: items.items.length,
1083
+ requests: requests.requests.length,
1084
+ files: [.../* @__PURE__ */ new Set([...items.files, ...requests.files])].sort()
1085
+ },
1086
+ procedures: op.procedures.map((p) => {
1087
+ const runs = steps.filter((s) => s.procedure === p.id).flatMap((s) => s.runs).sort((a, b) => a.at < b.at ? -1 : 1);
1088
+ const last = runs.at(-1);
1089
+ return {
1090
+ id: p.id,
1091
+ name: p.name,
1092
+ system: p.system,
1093
+ status: p.status,
1094
+ steps: p.steps.length,
1095
+ writes: p.steps.filter((s) => s.writes).length,
1096
+ allowed: p.executors.allowed,
1097
+ configured: p.executors.allowed.filter((n) => configured.has(n)),
1098
+ runs: runs.length,
1099
+ ...last ? { lastRun: { at: last.at, status: last.status, executor: last.executor } } : {}
1100
+ };
1101
+ }),
1102
+ executors: { configured: [...configured].sort(), problems: ex.problems }
1103
+ }
1104
+ };
1105
+ }).add("GET", "/api/todo/{stepId}/export", async ({ params, query }) => {
1106
+ const format = strParam(query, "format") ?? "text";
1107
+ const res = await run2((io) => todoShow(root, stepRef(params.stepId, strParam(query, "change")), { export: format }, io));
1108
+ if (res.code === 0)
1109
+ return { status: 200, body: { format, text: res.out.join("\n") } };
1110
+ if (res.err.some((l) => /^There is no step/.test(l)))
1111
+ throw notFound(res.err.join("\n"));
1112
+ throw refused2(res);
1113
+ }).add("POST", "/api/todo/{stepId}/done", async ({ params, body }) => {
1114
+ const b = parseBody2(api_exports.StepDoneRequest, body);
1115
+ const ref = stepRef(params.stepId, b.change);
1116
+ const res = await run2((io) => todoDone(root, ref, {
1117
+ by: user,
1118
+ result: b.values,
1119
+ ...b.skip ? { skip: true } : {},
1120
+ ...b.note ? { note: b.note } : {}
1121
+ }, io, deps.todo ?? {}));
1122
+ if (res.code !== 0) {
1123
+ if (res.err.some((l) => /^There is no step/.test(l)))
1124
+ throw notFound(res.err.join("\n"));
1125
+ throw refused2(res);
1126
+ }
1127
+ ctx.events.publish({ type: "file.changed", path: `steps/${params.stepId}` });
1128
+ const s = await findStep(root, params.stepId, b.change);
1129
+ return { status: 200, body: { ...s, lines: res.out } };
1130
+ }).add("GET", "/api/todo/{stepId}/run", async ({ params, query }) => {
1131
+ const s = await findStep(root, params.stepId, strParam(query, "change"));
1132
+ const op = await pack();
1133
+ const proc = s.procedure ? op.procedures.find((p) => p.id === s.procedure) : void 0;
1134
+ const configured = new Set((await executorsOf(root, deps)).executors.map((e) => e.name));
1135
+ const allowed = (proc?.executors.allowed ?? []).filter((n) => configured.has(n));
1136
+ const want = strParam(query, "with");
1137
+ const step = proc?.steps.find((x) => x.id === s.procedureStep);
1138
+ return {
1139
+ status: 200,
1140
+ body: {
1141
+ step: s.id,
1142
+ inputs: s.inputs,
1143
+ inputsSha256: inputsSha256(s.inputs),
1144
+ executors: allowed,
1145
+ ...want ? { allowed: allowed.includes(want) } : {},
1146
+ writes: step?.writes ?? false,
1147
+ ...proc ? { procedure: proc.id } : {}
1148
+ }
1149
+ };
1150
+ }).add("POST", "/api/todo/{stepId}/run", async ({ params, body }) => {
1151
+ const b = parseBody2(api_exports.StepRunRequest, body);
1152
+ const res = await run2((io) => todoRun(root, stepRef(params.stepId, b.change), {
1153
+ with: b.with,
1154
+ approve: user,
1155
+ inputs: b.inputsSha256,
1156
+ ...b.fillOnly ? { fillOnly: true } : {}
1157
+ }, io, deps.todo ?? {}));
1158
+ if (res.code !== 0) {
1159
+ if (res.err.some((l) => /^There is no step/.test(l)))
1160
+ throw notFound(res.err.join("\n"));
1161
+ throw refused2(res);
1162
+ }
1163
+ ctx.events.publish({ type: "file.changed", path: `steps/${params.stepId}` });
1164
+ return {
1165
+ status: 200,
1166
+ body: { step: await findStep(root, params.stepId, b.change), lines: res.out }
1167
+ };
1168
+ }).add("GET", "/api/service-desk", async ({ query }) => {
1169
+ const only = strParam(query, "change");
1170
+ const op = await pack();
1171
+ const steps = await fileStepStore(root).list({ system: "service-desk" }).catch(() => []);
1172
+ const requests = [];
1173
+ for (const c of await openChanges2(root))
1174
+ if (!only || c.id === only)
1175
+ requests.push(...await changeRequests(root, c.id, op.serviceRequests, steps));
1176
+ const exported = await readServiceRequestExports({ root, inputsDir: inputsDirOf(root) });
1177
+ const ex = await executorsOf(root, deps);
1178
+ const configured = new Set(ex.executors.map((e) => e.name));
1179
+ return {
1180
+ status: 200,
1181
+ body: {
1182
+ adapter: { configured: op.runtime.adapters.serviceDesk?.type ?? null },
1183
+ exports: { requests: exported.requests.length, files: exported.files },
1184
+ templates: op.serviceRequests.map((t) => ({
1185
+ id: t.id,
1186
+ name: t.name,
1187
+ requestType: t.requestType,
1188
+ leadTimeDays: t.leadTimeDays
1189
+ })),
1190
+ executors: op.procedures.filter((p) => p.system === "service-desk").flatMap((p) => p.executors.allowed.filter((n) => configured.has(n))).filter((n, i, a) => a.indexOf(n) === i),
1191
+ requests
1192
+ }
1193
+ };
1194
+ }).add("POST", "/api/service-desk/{changeId}/draft", async ({ params, body }) => {
1195
+ await readChange2(root, params.changeId);
1196
+ const b = parseBody2(api_exports.DraftRequestsRequest, body);
1197
+ const res = await run2((io) => snowDraft(root, params.changeId, b.force ? { force: true } : {}, io));
1198
+ if (res.code !== 0 && (res.code !== 1 || res.err.length))
1199
+ throw refused2(res);
1200
+ ctx.events.publish({ type: "file.changed", path: `${params.changeId}/snow.md` });
1201
+ return { status: 200, body: { lines: res.out, toFill: res.code === 1 } };
1202
+ }).add("POST", "/api/service-desk/{changeId}/submit", async ({ params }) => {
1203
+ await readChange2(root, params.changeId);
1204
+ const op = await pack();
1205
+ const desk = deps.serviceDesk ?? manualAdapter("serviceDesk", {
1206
+ store: fileStepStore(root),
1207
+ root,
1208
+ change: params.changeId,
1209
+ procedures: op.procedures,
1210
+ ...deps.now ? { now: () => deps.now } : {}
1211
+ }, op.runtime.adapters.serviceDesk?.settings ?? {});
1212
+ const res = await run2((io) => snowSubmit(root, params.changeId, { approve: user }, io, {
1213
+ serviceDesk: desk,
1214
+ ...nowDate ? { now: nowDate } : {}
1215
+ }));
1216
+ if (res.code !== 0)
1217
+ throw refused2(res);
1218
+ ctx.events.publish({ type: "file.changed", path: `${params.changeId}/sync.yaml` });
1219
+ return { status: 200, body: { lines: res.out } };
1220
+ }).add("GET", "/api/release", async () => {
1221
+ const at = /* @__PURE__ */ new Set(["release-ready", "released", "post-release-review"]);
1222
+ const list = (await openChanges2(root)).map((c) => ({
1223
+ id: c.id,
1224
+ title: c.title,
1225
+ type: c.type,
1226
+ state: c.state,
1227
+ atRelease: at.has(c.state)
1228
+ }));
1229
+ list.sort((a, b) => Number(b.atRelease) - Number(a.atRelease));
1230
+ return { status: 200, body: list };
1231
+ }).add("GET", "/api/release/{changeId}", async ({ params }) => {
1232
+ const id = params.changeId;
1233
+ const c = await readChange2(root, id);
1234
+ const op = await pack();
1235
+ const rep = await changeReport(root, id);
1236
+ const findings = await latestFindings2(root, id);
1237
+ const open = { blocking: 0, gap: 0, risk: 0, idea: 0 };
1238
+ for (const f of findings)
1239
+ if (f.state === "open")
1240
+ open[f.severity] += 1;
1241
+ const steps = await fileStepStore(root).list({ change: id }).catch(() => []);
1242
+ const requests = await changeRequests(root, id, op.serviceRequests, steps);
1243
+ const design = (await readChangeFiles(changeFolder(root, id))).docs.get("design.md");
1244
+ const sec = design?.sections.find((s) => /rollout|rollback|release|deploy/i.test(s.heading));
1245
+ const repos = (await readChangeRepos(root, id).catch(() => void 0))?.repos ?? [];
1246
+ const conflicts = (await detectConflicts(root).catch(() => [])).filter((p) => p.changes.includes(id)).map((p) => ({ with: p.changes.find((x) => x !== id), overlap: p.overlap }));
1247
+ const archive = await archiveGate(root, id).catch((e) => ({
1248
+ pass: false,
1249
+ blockers: [{ code: "error", message: e.message }],
1250
+ notes: []
1251
+ }));
1252
+ const handoff = c.handoffs.filter((h) => h.phase === "release").at(-1);
1253
+ return {
1254
+ status: 200,
1255
+ body: {
1256
+ change: { id: c.id, title: c.title, type: c.type, state: c.state, owners: c.owners },
1257
+ status: {
1258
+ state: rep.state,
1259
+ ...rep.phase ? { phase: rep.phase } : {},
1260
+ ...rep.next ? { next: rep.next } : {},
1261
+ nextStep: rep.nextStep,
1262
+ risk: rep.risk,
1263
+ blockers: rep.blockers,
1264
+ reasons: rep.reasons
1265
+ },
1266
+ // Go / no-go is the release hand-off PR: merging it is the go (ADR 0073).
1267
+ ...handoff ? {
1268
+ handoff: {
1269
+ state: handoff.state,
1270
+ pr: handoff.pr.url,
1271
+ approvals: handoff.approvals,
1272
+ reviewers: handoff.reviewers
1273
+ }
1274
+ } : {},
1275
+ openFindings: open,
1276
+ requests: {
1277
+ total: requests.length,
1278
+ filed: requests.filter((x) => x.state !== "to-file").length,
1279
+ toFile: requests.filter((x) => x.state === "to-file").length,
1280
+ list: requests
1281
+ },
1282
+ repos: repos.map((x) => ({
1283
+ id: x.id,
1284
+ branch: x.branch,
1285
+ ...x.pr ? { pr: x.pr } : {},
1286
+ build: x.build
1287
+ })),
1288
+ ...sec && design ? {
1289
+ rollout: {
1290
+ heading: sec.heading,
1291
+ text: design.lines.slice(sec.line, sec.end).join("\n").trim()
1292
+ }
1293
+ } : {},
1294
+ conflicts,
1295
+ archive: { pass: archive.pass, blockers: archive.blockers, notes: archive.notes },
1296
+ evidence: await filesUnder(changeFolder(root, id))
1297
+ }
1298
+ };
1299
+ }).add("POST", "/api/release/{changeId}/archive", async ({ params, body }) => {
1300
+ const id = params.changeId;
1301
+ await readChange2(root, id);
1302
+ const b = parseBody2(api_exports.ArchiveRequest, body);
1303
+ const gate = await run2((io) => ciArchiveGate(root, id, {}, io));
1304
+ if (gate.code !== 0)
1305
+ throw refused2(gate);
1306
+ const res = await run2((io) => changeArchive(root, id, { by: user, ...b.rescan === false ? { rescan: false } : {} }, io));
1307
+ if (res.code !== 0)
1308
+ throw refused2(res);
1309
+ ctx.events.publish({ type: "file.changed", path: id });
1310
+ return { status: 200, body: { lines: [...gate.out, ...res.out] } };
1311
+ }).add("GET", "/api/release/{changeId}/plan", async ({ params }) => ({
1312
+ status: 200,
1313
+ body: viewOf(await run2((io) => releasePlanCommand(root, params.changeId, { json: true }, io, rel)))
1314
+ })).add("GET", "/api/release/{changeId}/forecast", async ({ params }) => ({
1315
+ status: 200,
1316
+ body: viewOf(await run2((io) => releaseForecastCommand(root, params.changeId, { json: true }, io, rel)))
1317
+ })).add("GET", "/api/release/{changeId}/evidence", async ({ params }) => ({
1318
+ status: 200,
1319
+ body: viewOf(await run2((io) => releaseEvidenceCommand(root, params.changeId, { json: true }, io, rel)))
1320
+ })).add("GET", "/api/release/{changeId}/notes", async ({ params, query }) => {
1321
+ const audience = strParam(query, "audience") ?? "business";
1322
+ return {
1323
+ status: 200,
1324
+ body: viewOf(await run2((io) => releaseNotesCommand(root, params.changeId, { audience, json: true }, io, rel)))
1325
+ };
1326
+ }).add("GET", "/api/release/{changeId}/security", async ({ params }) => ({
1327
+ status: 200,
1328
+ body: viewOf(await run2((io) => releaseSecurityCommand(root, params.changeId, { json: true }, io, rel)))
1329
+ })).add("GET", "/api/outcomes", async ({ query }) => {
1330
+ const change = strParam(query, "change");
1331
+ return {
1332
+ status: 200,
1333
+ body: viewOf(await run2((io) => outcomeList(root, { ...change ? { change } : {}, json: true }, io, rel)))
1334
+ };
1335
+ }).add("POST", "/api/release/{changeId}/outcomes", async ({ params, body }) => {
1336
+ const id = params.changeId;
1337
+ await readChange2(root, id);
1338
+ const b = parseBody2(api_exports.RecordOutcomeRequest, body);
1339
+ const res = await run2((io) => outcomeRecord(root, id, b.measure, { value: b.value, by: user, ...b.note ? { note: b.note } : {}, json: true }, io, rel));
1340
+ if (res.code !== 0)
1341
+ throw refused2(res);
1342
+ ctx.events.publish({ type: "file.changed", path: `${id}/review/outcomes.yaml` });
1343
+ return { status: 200, body: jsonOf(res) ?? { lines: res.out } };
1344
+ }).add("GET", "/api/governance", async () => {
1345
+ const conf = jsonOf(await run2((io) => conformanceCommand(orgPackDirOf(root), { kind: "org-pack", json: true }, io)));
1346
+ const constitution = await loadConstitution();
1347
+ const org = await readPackFiles(root, join3(orgPackDirOf(root), "checklists"));
1348
+ const team = await readPackFiles(root, join3(orgPackDirOf(root), "teams"));
1349
+ const layered = layerRules([
1350
+ { pack: constitution, file: "built-in", origin: "constitution" },
1351
+ ...org.packs.map((p) => ({ ...p, origin: "org" })),
1352
+ ...team.packs.map((p) => ({ ...p, origin: "team" }))
1353
+ ]);
1354
+ const changes = await openChanges2(root);
1355
+ const waivers = [];
1356
+ for (const c of changes) {
1357
+ for (const f of await latestFindings2(root, c.id))
1358
+ if (f.state === "waived")
1359
+ waivers.push({
1360
+ change: c.id,
1361
+ gate: f.gate,
1362
+ rule: f.rule,
1363
+ summary: f.summary,
1364
+ ...f.resolution ? { by: f.resolution.by, at: f.resolution.at, reason: f.resolution.reason } : {}
1365
+ });
1366
+ }
1367
+ const stats = jsonOf(await run2((io) => criticStats(root, { json: true }, io)));
1368
+ const count = (packs) => packs.reduce((a, p) => a + p.pack.rules.length, 0);
1369
+ return {
1370
+ status: 200,
1371
+ body: {
1372
+ conformance: conf ? { ...conf, target: "org-pack" } : {
1373
+ kind: "org-pack",
1374
+ passed: false,
1375
+ results: [],
1376
+ summary: { pass: 0, fail: 0, warn: 0 }
1377
+ },
1378
+ rules: {
1379
+ constitution: constitution.rules.length,
1380
+ org: count(org.packs),
1381
+ team: count(team.packs),
1382
+ inForce: layered.rules.length,
1383
+ waivable: layered.rules.filter((x) => x.waivable).length,
1384
+ refused: layered.refused,
1385
+ problems: [...org.problems, ...team.problems, ...layered.problems]
1386
+ },
1387
+ waivers: waivers.filter((w, i, a) => a.findIndex((x) => x.change === w.change && x.rule === w.rule && x.gate === w.gate) === i),
1388
+ overrides: stats ?? { changes: 0, rules: [], suggestions: [] }
1389
+ }
1390
+ };
1391
+ }).add("GET", "/api/evals", async () => {
1392
+ const op = await pack();
1393
+ const stats = await overrideStats(root);
1394
+ const t = { total: 0, applied: 0, dismissed: 0, waived: 0, asked: 0, open: 0 };
1395
+ for (const x of stats.rules) {
1396
+ t.total += x.total;
1397
+ t.applied += x.applied;
1398
+ t.dismissed += x.dismissed;
1399
+ t.waived += x.waived;
1400
+ t.asked += x.asked;
1401
+ t.open += x.open;
1402
+ }
1403
+ return {
1404
+ status: 200,
1405
+ body: {
1406
+ quality: await readQuality(root) ?? null,
1407
+ runs: (await listReports(root)).map((x) => ({
1408
+ runId: x.runId,
1409
+ mode: x.mode,
1410
+ startedAt: x.startedAt,
1411
+ finishedAt: x.finishedAt,
1412
+ runs: x.runs,
1413
+ agents: x.agents,
1414
+ failed: x.results.filter((c) => !c.pass).length,
1415
+ skipped: x.skipped.length,
1416
+ ...x.liveBar ? { liveBar: x.liveBar.pass } : {}
1417
+ })),
1418
+ findings: t,
1419
+ modes: op.runtime.modes,
1420
+ models: op.runtime.models.allowed
1421
+ }
1422
+ };
1423
+ }).add("POST", "/api/evals/run", async ({ body }) => {
1424
+ const b = parseBody2(api_exports.EvalRunRequest, body);
1425
+ if (b.mode === "live")
1426
+ throw badRequest("Live evals call a real model, so the app doesn't start them. Run atticus eval run --mode live in a terminal.");
1427
+ const res = await run2((io) => evalRun(root, {
1428
+ mode: b.mode,
1429
+ json: true,
1430
+ ...b.agents ? { agent: b.agents } : {},
1431
+ ...b.runs ? { runs: b.runs } : {}
1432
+ }, io, nowDate ? { now: nowDate } : {}));
1433
+ const out = jsonOf(res);
1434
+ if (res.code === 2 || !out)
1435
+ throw refused2(res.code === 2 ? res : { ...res, code: 1 });
1436
+ ctx.events.publish({ type: "file.changed", path: ".evals/quality.json" });
1437
+ return { status: 200, body: { ...out, problems: res.err } };
1438
+ }).add("GET", "/api/evals/defences", async () => {
1439
+ const res = await run2((io) => defencesScan(root, void 0, { json: true }, io));
1440
+ const scan = jsonOf(res) ?? {
1441
+ files: [],
1442
+ scanned: 0,
1443
+ skipped: 0
1444
+ };
1445
+ const items = [];
1446
+ for (const c of await openChanges2(root)) {
1447
+ const dir = join3(changeFolder(root, c.id), "review", "defences");
1448
+ for (const f of (await readdir2(dir).catch(() => [])).sort()) {
1449
+ if (!f.endsWith(".json"))
1450
+ continue;
1451
+ try {
1452
+ const list = JSON.parse(await readFile2(join3(dir, f), "utf8"));
1453
+ for (const q of list)
1454
+ items.push({
1455
+ change: c.id,
1456
+ file: `review/defences/${f}`,
1457
+ source: q.source,
1458
+ origin: q.origin,
1459
+ summary: q.summary,
1460
+ fix: q.fix,
1461
+ question: q.question,
1462
+ signals: q.signals.length
1463
+ });
1464
+ } catch {
1465
+ }
1466
+ }
1467
+ }
1468
+ return { status: 200, body: { ...scan, items, problems: res.err } };
1469
+ }).add("GET", "/api/learning", async () => {
1470
+ const { lessons, problems } = await lessonsFor(root);
1471
+ const sandbox = await isSandbox(root);
1472
+ const progress = sandbox ? await readProgress(root) : void 0;
1473
+ const view2 = lessons.map((l) => {
1474
+ const p = progress?.lessons[l.id];
1475
+ const done = p ? l.steps.filter((s) => p.steps[s.id]).length : 0;
1476
+ return {
1477
+ id: l.id,
1478
+ role: l.role,
1479
+ title: l.title,
1480
+ summary: l.summary,
1481
+ minutes: l.minutes,
1482
+ steps: l.steps.length,
1483
+ done,
1484
+ state: !p ? "not-started" : p.completedAt ? "done" : "in-progress"
1485
+ };
1486
+ });
1487
+ const roles = [...new Set(lessons.map((l) => l.role))].map((role) => ({
1488
+ role,
1489
+ lessons: view2.filter((v) => v.role === role).length,
1490
+ done: view2.filter((v) => v.role === role && v.state === "done").length
1491
+ }));
1492
+ const cur = progress?.current ? lessons.find((l) => l.id === progress.current) : void 0;
1493
+ const lp = cur ? progress.lessons[cur.id] : void 0;
1494
+ const next = cur?.steps.find((s) => !lp?.steps[s.id]);
1495
+ return {
1496
+ status: 200,
1497
+ body: {
1498
+ sandbox,
1499
+ roles,
1500
+ lessons: view2,
1501
+ ...cur ? {
1502
+ current: {
1503
+ lesson: cur.id,
1504
+ title: cur.title,
1505
+ intro: cur.intro,
1506
+ steps: cur.steps.length,
1507
+ completed: !next,
1508
+ ...next ? { step: stepOfLesson(cur, next.id) } : { outro: cur.outro }
1509
+ }
1510
+ } : {},
1511
+ problems
1512
+ }
1513
+ };
1514
+ }).add("POST", "/api/learning/start", async ({ body }) => {
1515
+ const b = parseBody2(api_exports.StartLessonRequest, body);
1516
+ const res = await run2((io) => learnStart(root, b.lesson, { json: true }, io, nowDate ? { now: nowDate() } : {}));
1517
+ const out = jsonOf(res);
1518
+ if (res.code !== 0 || !out)
1519
+ throw refused2(res);
1520
+ return { status: 200, body: out };
1521
+ }).add("POST", "/api/learning/check", async ({ body }) => {
1522
+ const b = parseBody2(api_exports.CheckLessonRequest, body);
1523
+ const res = await run2((io) => learnCheck(root, {
1524
+ json: true,
1525
+ ...b.answer !== void 0 && b.answer !== "" ? { answer: b.answer } : {},
1526
+ ...b.lesson ? { lesson: b.lesson } : {},
1527
+ ...b.step ? { step: b.step } : {}
1528
+ }, io, nowDate ? { now: nowDate() } : {}));
1529
+ const out = jsonOf(res);
1530
+ if (!out)
1531
+ throw refused2(res.code === 0 ? { ...res, code: 1 } : res);
1532
+ let hint;
1533
+ if (!out.pass) {
1534
+ const l = (await lessonsFor(root)).lessons.find((x) => x.id === out.lesson);
1535
+ hint = l?.steps.find((s) => s.id === out.step)?.hint;
1536
+ }
1537
+ return { status: 200, body: { ...out, ...hint ? { hint } : {} } };
1538
+ }).add("POST", "/api/learning/sandbox", async ({ body }) => {
1539
+ const b = parseBody2(api_exports.SandboxRequest, body);
1540
+ const res = await run2((io) => sandboxCreate(resolve(b.dir), { json: true }, io, nowDate ? { now: nowDate() } : {}));
1541
+ const out = jsonOf(res);
1542
+ if (res.code !== 0 || !out)
1543
+ throw refused2(res);
1544
+ return { status: 201, body: out };
1545
+ }).add("GET", "/api/settings", async () => {
1546
+ const op = await pack();
1547
+ const rt = op.runtime;
1548
+ const ex = await executorsOf(root, deps);
1549
+ const names = new Set(ex.executors.map((e) => e.name));
1550
+ const kinds = [
1551
+ "workTracker",
1552
+ "serviceDesk",
1553
+ "gitHost",
1554
+ "wiki",
1555
+ "notify",
1556
+ "design",
1557
+ "model"
1558
+ ];
1559
+ return {
1560
+ status: 200,
1561
+ body: {
1562
+ runtime: {
1563
+ assistants: rt.assistants,
1564
+ editors: rt.editors,
1565
+ modes: rt.modes,
1566
+ models: rt.models,
1567
+ autonomy: rt.autonomy,
1568
+ budgets: rt.budgets,
1569
+ retention: rt.retention,
1570
+ telemetry: rt.telemetry,
1571
+ buildEngine: rt.buildEngine ?? { default: "speckit", perRepo: {} }
1572
+ },
1573
+ adapters: kinds.map((kind) => {
1574
+ const cfg = rt.adapters[kind];
1575
+ return {
1576
+ kind,
1577
+ type: cfg?.type ?? null,
1578
+ mode: !cfg ? "not-set" : cfg.type === "manual" ? "manual" : cfg.type === "mock" ? "mock" : "live"
1579
+ };
1580
+ }),
1581
+ executors: {
1582
+ configured: ex.executors.map((e) => ({
1583
+ name: e.name,
1584
+ transport: e.transport,
1585
+ where: e.transport === "http" ? e.url : [e.command, ...e.args].join(" "),
1586
+ fillOnly: e.fillOnly,
1587
+ covers: [...e.procedures, ...e.stepKinds],
1588
+ allowedFor: op.procedures.filter((p) => p.executors.allowed.includes(e.name)).map((p) => p.id)
1589
+ })),
1590
+ missing: op.procedures.flatMap((p) => p.executors.allowed.filter((n) => !names.has(n)).map((n) => ({ procedure: p.id, executor: n }))),
1591
+ problems: ex.problems
1592
+ },
1593
+ orgPack: {
1594
+ platforms: op.profiles.map((p) => p.platform),
1595
+ ...op.pack ? { pack: op.pack } : {},
1596
+ counts: {
1597
+ patterns: op.patterns.length,
1598
+ dialects: op.dialects.length,
1599
+ checklists: op.checklists.length,
1600
+ agents: op.agents.length,
1601
+ procedures: op.procedures.length,
1602
+ serviceRequests: op.serviceRequests.length,
1603
+ profiles: op.profiles.length,
1604
+ skills: op.skills.length
1605
+ },
1606
+ defaulted: op.defaulted,
1607
+ problems: op.problems
1608
+ },
1609
+ gates: op.gates.gates.map((g) => ({
1610
+ id: g.id,
1611
+ phase: g.phase,
1612
+ checklists: g.checklists,
1613
+ checks: g.checks.map((c) => c.check),
1614
+ // ADR 0087: null when the gate has no round limit.
1615
+ maxAuthorRounds: gateRoundLimit(op.gates, g.id) ?? null
1616
+ }))
1617
+ }
1618
+ };
1619
+ }).add("GET", "/api/settings/executors/check", async () => {
1620
+ const res = await run2((io) => executorsCheck(root, { json: true }, io, deps.todo ?? {}));
1621
+ const out = jsonOf(res);
1622
+ if (!out)
1623
+ throw refused2({ ...res, code: 1 });
1624
+ return { status: 200, body: out };
1625
+ });
1626
+ }
1627
+
1628
+ // packages/cli/dist/ui-api-me.js
1629
+ async function meView(ctx) {
1630
+ const op = await loadOrgPack(orgPackDirOf(ctx.root));
1631
+ const me = await readMe(ctx.root);
1632
+ const p = ctx.person;
1633
+ return {
1634
+ user: ctx.user,
1635
+ ...p?.name ? { name: p.name } : {},
1636
+ ...p?.email ? { email: p.email } : {},
1637
+ source: p?.source ?? "given",
1638
+ ...p?.warning ? { warning: p.warning } : {},
1639
+ ai: op.runtime.ai,
1640
+ notify: {
1641
+ digest: me.notify?.digest ?? "off",
1642
+ ...me.notify?.channel ? { channel: me.notify.channel } : {}
1643
+ },
1644
+ // WP7.5: the changes the person follows.
1645
+ watching: me.watch
1646
+ };
1647
+ }
1648
+ function uiMeRoutes(r, ctx, deps = {}) {
1649
+ return r.add("GET", "/api/me", async () => ({ status: 200, body: await meView(ctx) })).add("PUT", "/api/me/notify", async ({ body }) => {
1650
+ const b = api_exports.SaveNotifyRequest.safeParse(body ?? {});
1651
+ if (!b.success)
1652
+ throw badRequest("Say how often: { digest: off | daily | weekly, channel?: <channel> }.");
1653
+ const me = await readMe(ctx.root);
1654
+ await writeMe(ctx.root, {
1655
+ ...me,
1656
+ notify: { digest: b.data.digest, ...b.data.channel ? { channel: b.data.channel } : {} }
1657
+ });
1658
+ return { status: 200, body: await meView(ctx) };
1659
+ }).add("PUT", "/api/me/watch", async ({ body }) => {
1660
+ const b = api_exports.WatchRequest.safeParse(body ?? {});
1661
+ if (!b.success)
1662
+ throw badRequest("Say which change and whether: { change, watch: true }.");
1663
+ const me = await readMe(ctx.root);
1664
+ const watch = b.data.watch ? [...me.watch, b.data.change] : me.watch.filter((c) => c !== b.data.change);
1665
+ await writeMe(ctx.root, { ...me, watch });
1666
+ return { status: 200, body: await meView(ctx) };
1667
+ }).add("POST", "/api/me/digest", async ({ body }) => {
1668
+ const b = api_exports.SendDigestRequest.safeParse(body ?? {});
1669
+ if (!b.success)
1670
+ throw badRequest("Send { send: true } to post it, or {} to see it.");
1671
+ const me = await readMe(ctx.root);
1672
+ const out = [];
1673
+ const err = [];
1674
+ const code = await digestCommand(ctx.root, {
1675
+ as: ctx.user,
1676
+ ...me.notify?.channel ? { channel: me.notify.channel } : {},
1677
+ send: b.data.send,
1678
+ json: true
1679
+ }, { out: (l) => out.push(l), err: (l) => err.push(l) }, deps);
1680
+ if (code === 2)
1681
+ throw new HttpError(400, "The request isn't valid", err.join("\n"));
1682
+ return { status: 200, body: JSON.parse(out.join("\n")) };
1683
+ });
1684
+ }
1685
+
1686
+ // packages/cli/dist/ui-api-know.js
1687
+ var ID = /^[a-z0-9][a-z0-9._-]*$/;
1688
+ var VIEWS = {
1689
+ coverage: () => ["coverage"],
1690
+ questions: () => ["questions"],
1691
+ intake: () => ["intake", "list"],
1692
+ triage: () => ["triage"],
1693
+ features: (id) => id ? ["feature", "show", id] : ["feature", "list"],
1694
+ glossary: () => ["glossary", "list"],
1695
+ "critic-stats": () => ["critic", "stats"],
1696
+ conflicts: () => ["change", "conflicts"],
1697
+ findings: (id) => id ? ["findings", id] : void 0,
1698
+ drift: (id) => id ? ["drift", id] : void 0,
1699
+ trace: (id) => id ? ["trace", id, "--no-write"] : void 0,
1700
+ "story-bar": (id) => id ? ["plan", "bar", id] : void 0,
1701
+ sources: () => ["sources", "sync"],
1702
+ initiatives: (id) => id ? ["initiative", "status", id] : void 0,
1703
+ reconcile: () => ["reconcile"]
1704
+ };
1705
+ var DIAGRAM_VIEWS = ["context", "container", "data", "event", "sequence"];
1706
+ var scanFirst = (words) => /catalog is empty|scan first/i.test(words);
1707
+ function uiKnowRoutes(r, ctx, o = {}) {
1708
+ const doctor = o.doctor ?? ((root) => doctorChecks(root, { portFree: async () => true }));
1709
+ let checked;
1710
+ const machine = (fresh = false) => {
1711
+ if (fresh || !checked || Date.now() - checked.at > 3e5) {
1712
+ const run3 = { at: Date.now(), checks: doctor(ctx.root) };
1713
+ checked = run3;
1714
+ run3.checks.catch(() => {
1715
+ if (checked === run3)
1716
+ checked = void 0;
1717
+ });
1718
+ }
1719
+ return checked.checks;
1720
+ };
1721
+ if (o.warm)
1722
+ void machine().catch(() => void 0);
1723
+ const view2 = async (name, arg) => {
1724
+ const build = VIEWS[name];
1725
+ if (!build)
1726
+ throw notFound(`There is no view ${name}.`);
1727
+ if (arg !== void 0 && !ID.test(arg))
1728
+ throw badRequest(`${arg} isn't an id.`);
1729
+ const args = build(arg);
1730
+ if (!args)
1731
+ throw badRequest(`The ${name} view needs an id: /api/view/${name}/<id>.`);
1732
+ const { main } = await import("./chunk-P63VL4HE.js");
1733
+ const out = [];
1734
+ const err = [];
1735
+ const code = await main([...args, "--json", "--root", ctx.root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
1736
+ const text3 = out.join("\n").trim();
1737
+ if (code === 2) {
1738
+ const words = err.join("\n") || "The command refused the request.";
1739
+ if (scanFirst(words))
1740
+ throw new HttpError(422, "Atticus can't show that yet", words, { notYet: true });
1741
+ throw badRequest(words);
1742
+ }
1743
+ let body;
1744
+ try {
1745
+ body = text3 ? JSON.parse(text3) : null;
1746
+ } catch {
1747
+ body = void 0;
1748
+ }
1749
+ if (body === void 0) {
1750
+ const words = [...err, ...out].join("\n") || "No output.";
1751
+ throw new HttpError(422, "Atticus can't show that yet", words, scanFirst(words) ? { notYet: true } : void 0);
1752
+ }
1753
+ return { status: 200, body: { code, data: body, problems: err } };
1754
+ };
1755
+ const text2 = async (args) => {
1756
+ const { main } = await import("./chunk-P63VL4HE.js");
1757
+ const out = [];
1758
+ const err = [];
1759
+ const code = await main([...args, "--root", ctx.root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
1760
+ if (code === 2) {
1761
+ const words = err.join("\n") || "The command refused the request.";
1762
+ if (scanFirst(words))
1763
+ throw new HttpError(422, "Atticus can't show that yet", words, { notYet: true });
1764
+ throw badRequest(words);
1765
+ }
1766
+ return { code, text: out.join("\n"), problems: err };
1767
+ };
1768
+ return r.add("POST", "/api/triage/{itemId}/decide", async ({ params, body }) => {
1769
+ const b = api_exports.TriageDecideRequest.safeParse(body ?? {});
1770
+ if (!b.success)
1771
+ throw badRequest("Some fields aren't right.", {
1772
+ errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
1773
+ });
1774
+ const { triageDecide } = await import("./chunk-WZ2BXIGG.js");
1775
+ const out = [];
1776
+ const err = [];
1777
+ const code = await triageDecide(ctx.root, params.itemId, b.data.option, { by: ctx.user, ...b.data.reason ? { reason: b.data.reason } : {} }, { out: (l) => out.push(l), err: (l) => err.push(l) });
1778
+ if (code !== 0) {
1779
+ const detail = [...err, ...out].join("\n") || "Atticus refused.";
1780
+ if (/There is no open triage item/.test(detail))
1781
+ throw notFound(detail);
1782
+ throw code === 2 ? badRequest(detail) : new HttpError(422, "Atticus refused", detail);
1783
+ }
1784
+ ctx.events.publish({ type: "file.changed", path: "catalog/curated" });
1785
+ return { status: 200, body: { item: params.itemId, option: b.data.option, lines: out } };
1786
+ }).add("GET", "/api/setup", async () => ({
1787
+ status: 200,
1788
+ body: await setupSteps(ctx.root, { doctor: machine() })
1789
+ })).add("GET", "/api/doctor", async ({ query }) => {
1790
+ const checks = await machine(query.get("fresh") === "1");
1791
+ return { status: 200, body: { checks, ok: !checks.some((c) => c.status === "fail") } };
1792
+ }).add("POST", "/api/baseline", async ({ body }) => {
1793
+ const b = api_exports.BaselineRequest.safeParse(body ?? {});
1794
+ if (!b.success)
1795
+ throw badRequest("Some fields aren't right.");
1796
+ const out = [];
1797
+ const err = [];
1798
+ const code = await baselineCommand(ctx.root, { by: ctx.user, ...b.data.note ? { note: b.data.note } : {} }, { out: (l) => out.push(l), err: (l) => err.push(l) });
1799
+ if (code !== 0)
1800
+ throw new HttpError(422, "Atticus refused", [...err, ...out].join("\n"));
1801
+ ctx.events.publish({ type: "file.changed", path: "catalog/baseline.yaml" });
1802
+ return {
1803
+ status: 200,
1804
+ body: { lines: out, ...await setupSteps(ctx.root, { doctor: await machine() }) }
1805
+ };
1806
+ }).add("GET", "/api/guide/{system}", async ({ params }) => {
1807
+ const id = params.system;
1808
+ if (!ID.test(id))
1809
+ throw badRequest(`${id} isn't an id.`);
1810
+ try {
1811
+ return {
1812
+ status: 200,
1813
+ body: systemGuide(await catalogRecords(ctx.root), id, {
1814
+ changes: await openChanges(ctx.root)
1815
+ })
1816
+ };
1817
+ } catch (e) {
1818
+ throw notFound(e.message);
1819
+ }
1820
+ }).add("GET", "/api/initiatives", async () => {
1821
+ const v = await readView(ctx.root);
1822
+ const body = [];
1823
+ for (const i of v.all("Initiative").sort((a, b) => a.id.localeCompare(b.id)))
1824
+ body.push(await initiativeStatus(ctx.root, i.id));
1825
+ return { status: 200, body };
1826
+ }).add("GET", "/api/diagram/{view}", async ({ params, query }) => {
1827
+ const name = params.view;
1828
+ if (!DIAGRAM_VIEWS.includes(name))
1829
+ throw badRequest(`${name} isn't a view. Views: ${DIAGRAM_VIEWS.join(", ")}.`);
1830
+ const focus = query.get("focus") ?? void 0;
1831
+ if (focus !== void 0 && !ID.test(focus))
1832
+ throw badRequest(`${focus} isn't an id.`);
1833
+ return {
1834
+ status: 200,
1835
+ body: await text2(["diagram", "render", name, ...focus ? ["--focus", focus] : []])
1836
+ };
1837
+ }).add("GET", "/api/diagram/{view}/graph", async ({ params, query }) => {
1838
+ const name = params.view;
1839
+ if (!DIAGRAM_VIEWS.includes(name) || name === "sequence")
1840
+ throw badRequest(`${name} has no canvas. Views: ${DIAGRAM_VIEWS.filter((v) => v !== "sequence").join(", ")}.`);
1841
+ const focus = query.get("focus") ?? void 0;
1842
+ if (focus !== void 0 && !ID.test(focus))
1843
+ throw badRequest(`${focus} isn't an id.`);
1844
+ const r2 = await text2([
1845
+ "diagram",
1846
+ "render",
1847
+ name,
1848
+ "--format",
1849
+ "json",
1850
+ ...focus ? ["--focus", focus] : []
1851
+ ]);
1852
+ const out = r2.text.trim();
1853
+ if (!out.startsWith("{")) {
1854
+ const words = r2.problems.join("\n") || "Nothing to draw.";
1855
+ throw new HttpError(422, "Atticus can't show that yet", words, scanFirst(words) ? { notYet: true } : void 0);
1856
+ }
1857
+ return { status: 200, body: { view: JSON.parse(out) } };
1858
+ }).add("GET", "/api/view/{name}", ({ params }) => view2(params.name)).add("GET", "/api/view/{name}/{id}", ({ params }) => view2(params.name, params.id));
1859
+ }
1860
+
1861
+ // packages/cli/dist/ui-api-questions.js
1862
+ function uiQuestionRoutes(r, ctx, deps = {}) {
1863
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
1864
+ return r.add("GET", "/api/questions", async ({ query }) => {
1865
+ const state = strParam(query, "state");
1866
+ if (state !== void 0 && !QuestionState.safeParse(state).success)
1867
+ throw badRequest("state is open, answered or retired.");
1868
+ const body = await listQuestions(ctx.root, state ? { state } : {});
1869
+ return { status: 200, body };
1870
+ }).add("GET", "/api/questions/{id}", async ({ params }) => {
1871
+ const q = await readQuestion(ctx.root, params.id).catch(() => {
1872
+ throw new HttpError(422, "Atticus can't read that question", `questions/${params.id}.yaml isn't a valid question.`);
1873
+ });
1874
+ if (!q)
1875
+ throw notFound(`There is no question ${params.id}.`);
1876
+ return { status: 200, body: q };
1877
+ }).add("GET", "/api/questions/{id}/evidence", async ({ params }) => {
1878
+ const q = await readQuestion(ctx.root, params.id).catch(() => void 0);
1879
+ if (!q)
1880
+ throw notFound(`There is no question ${params.id}.`);
1881
+ const { evidenceExcerpts } = await import("./chunk-6JUBZBMW.js");
1882
+ return {
1883
+ status: 200,
1884
+ body: {
1885
+ excerpts: await evidenceExcerpts(ctx.root, { repo: q.repo, evidence: q.evidence })
1886
+ }
1887
+ };
1888
+ }).add("POST", "/api/questions/{id}/answer", async ({ params, body }) => {
1889
+ const b = api_exports.AnswerQuestionRequest.safeParse(body ?? {});
1890
+ if (!b.success)
1891
+ throw badRequest('Answer with { option: <n> } or { text: "your own words" }.', {
1892
+ errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
1893
+ });
1894
+ try {
1895
+ const r2 = await answerQuestion(ctx.root, params.id, {
1896
+ ...b.data,
1897
+ by: ctx.user,
1898
+ at: now().toISOString()
1899
+ });
1900
+ return {
1901
+ status: 200,
1902
+ body: {
1903
+ question: r2.question,
1904
+ rerun: r2.question.rerun,
1905
+ ...r2.skill ? { skill: r2.skill } : {}
1906
+ }
1907
+ };
1908
+ } catch (e) {
1909
+ if (!(e instanceof QuestionError))
1910
+ throw e;
1911
+ if (e.code === "not-found")
1912
+ throw notFound(e.message);
1913
+ if (e.code === "bad-answer")
1914
+ throw badRequest(e.message);
1915
+ throw new HttpError(422, "Atticus can't do that yet", e.message);
1916
+ }
1917
+ });
1918
+ }
1919
+
1920
+ // packages/cli/dist/ui-api-across.js
1921
+ var ACROSS_VIEWS = [
1922
+ "findings",
1923
+ "tasks",
1924
+ "stories",
1925
+ "tests",
1926
+ "requests",
1927
+ "releases"
1928
+ ];
1929
+ async function openChanges3(root) {
1930
+ return (await changeStore(root).list()).filter((c) => c.state !== "archived").sort((a, b) => a.id.localeCompare(b.id));
1931
+ }
1932
+ async function latestFindings3(root, change) {
1933
+ const out = [];
1934
+ for (const gate of await reviewedGates(root, change)) {
1935
+ const rounds = await roundsOf(root, change, gate);
1936
+ if (rounds.length)
1937
+ out.push(...(await readRound(root, change, gate, rounds.at(-1))).findings.map((f) => f.finding));
1938
+ }
1939
+ return out;
1940
+ }
1941
+ var SEVERITY_ORDER = ["blocking", "gap", "risk", "idea"];
1942
+ var AT_RELEASE = /* @__PURE__ */ new Set(["release-ready", "released", "post-release-review"]);
1943
+ async function acrossRows(root, view2) {
1944
+ const changes = await openChanges3(root);
1945
+ const rows = [];
1946
+ const op = view2 === "requests" ? await loadOrgPack(orgPackDirOf(root)) : void 0;
1947
+ const steps = view2 === "requests" ? await fileStepStore(root).list({ system: "service-desk" }).catch(() => []) : [];
1948
+ for (const c of changes) {
1949
+ const base = { change: c.id, changeTitle: c.title, changeState: c.state };
1950
+ const dir = changeFolder(root, c.id);
1951
+ switch (view2) {
1952
+ case "findings":
1953
+ for (const f of await latestFindings3(root, c.id))
1954
+ rows.push({
1955
+ ...base,
1956
+ id: f.id,
1957
+ gate: f.gate,
1958
+ lens: f.lens,
1959
+ severity: f.severity,
1960
+ state: f.state,
1961
+ summary: f.summary,
1962
+ artifact: f.artifact
1963
+ });
1964
+ break;
1965
+ case "tasks":
1966
+ for (const t of readChangeTasks(await readChangeFiles(dir)))
1967
+ rows.push({
1968
+ ...base,
1969
+ id: t.id,
1970
+ repo: t.repo,
1971
+ description: t.title,
1972
+ done: t.done,
1973
+ ...t.stories[0] ? { story: t.stories[0] } : {}
1974
+ });
1975
+ break;
1976
+ case "stories": {
1977
+ const files = await readChangeFiles(dir);
1978
+ for (const s of readStoryIndex(files)) {
1979
+ const story = readStory(files, s.id);
1980
+ rows.push({
1981
+ ...base,
1982
+ id: s.id,
1983
+ title: s.title,
1984
+ repos: s.repos,
1985
+ dependsOn: s.dependsOn,
1986
+ written: !!story,
1987
+ criteria: story?.criteria.length ?? 0
1988
+ });
1989
+ }
1990
+ break;
1991
+ }
1992
+ case "tests": {
1993
+ const m = await traceMatrix(root, c.id).catch(() => void 0);
1994
+ if (!m)
1995
+ break;
1996
+ const reqRows = m.rows.filter((r) => r.requirement);
1997
+ rows.push({
1998
+ ...base,
1999
+ requirements: m.requirements.length,
2000
+ tested: reqRows.filter((r) => r.tests.length > 0).length,
2001
+ rows: reqRows.length,
2002
+ stories: m.stories.length,
2003
+ gaps: m.gaps.length,
2004
+ topGaps: m.gaps.slice(0, 3).map((g) => g.summary)
2005
+ });
2006
+ break;
2007
+ }
2008
+ case "requests":
2009
+ for (const r of await readServiceRequests(dir, await readChangeFiles(dir), op.serviceRequests, steps.filter((s) => s.change === c.id), []))
2010
+ rows.push({
2011
+ ...base,
2012
+ heading: r.heading,
2013
+ requestType: r.requestType,
2014
+ record: r.record,
2015
+ state: r.state,
2016
+ missing: r.missing,
2017
+ ...r.leadTimeDays !== void 0 ? { leadTimeDays: r.leadTimeDays } : {},
2018
+ ...r.number ? { number: r.number } : {}
2019
+ });
2020
+ break;
2021
+ case "releases": {
2022
+ const rep = await changeReport(root, c.id);
2023
+ const h = liveHandoff(c);
2024
+ rows.push({
2025
+ ...base,
2026
+ atRelease: AT_RELEASE.has(c.state),
2027
+ ...rep.phase ? { phase: rep.phase } : {},
2028
+ ...rep.next ? { next: rep.next } : {},
2029
+ nextStep: rep.nextStep,
2030
+ risk: rep.risk.level,
2031
+ blockers: rep.blockers.length,
2032
+ ...h ? { handoff: { phase: h.phase, pr: h.pr.url, state: h.state } } : {}
2033
+ });
2034
+ break;
2035
+ }
2036
+ }
2037
+ }
2038
+ if (view2 === "findings")
2039
+ rows.sort((a, b) => Number(a.state !== "open") - Number(b.state !== "open") || SEVERITY_ORDER.indexOf(String(a.severity)) - SEVERITY_ORDER.indexOf(String(b.severity)) || a.change.localeCompare(b.change));
2040
+ if (view2 === "releases")
2041
+ rows.sort((a, b) => Number(b.atRelease) - Number(a.atRelease));
2042
+ return rows;
2043
+ }
2044
+ var pathKey = (s) => s.toLowerCase().replace(/\{[^}]*\}|:[a-z0-9_]+/g, "{}").replace(/\/+$/, "").trim();
2045
+ function hopKey(heading) {
2046
+ const m = /^\s*(get|post|put|patch|delete|head|options)\s+(\S+)/i.exec(heading);
2047
+ return m ? { method: m[1].toUpperCase(), path: pathKey(m[2]) } : { path: pathKey(heading) };
2048
+ }
2049
+ async function mappingGrid(root, change, records) {
2050
+ const files = await readChangeFiles(changeFolder(root, change));
2051
+ const doc = files.docs.get("mappings.md");
2052
+ const all = records ?? await catalogRecords(root);
2053
+ const mappings = all.filter((r) => r.kind === "FieldMapping");
2054
+ const interfaces = all.filter((r) => r.kind === "Interface");
2055
+ const hops = [];
2056
+ for (const s of doc?.sections.filter((x) => x.level === 2) ?? []) {
2057
+ const body = doc.lines.slice(s.line, s.end).join("\n");
2058
+ const table = doc.tables.find((t) => t.line > s.line && t.line <= s.end);
2059
+ const cell = (r, ...names) => names.map((n) => r[n]).find((v) => v !== void 0) ?? "";
2060
+ const rows = (table?.rows ?? []).map((r) => ({
2061
+ line: r.line,
2062
+ source: cell(r.cells, "source field", "source"),
2063
+ target: cell(r.cells, "target field", "target"),
2064
+ rule: cell(r.cells, "rule or transformation", "transform", "rule"),
2065
+ whenMissing: cell(r.cells, "when missing or invalid", "when missing")
2066
+ }));
2067
+ const k = hopKey(s.heading);
2068
+ const ifIds = new Set(interfaces.filter((i) => pathKey(i.normalizedPath) === k.path && (!k.method || !i.method || i.method.toUpperCase() === k.method)).map((i) => i.id));
2069
+ const noChange = /no mapping change/i.test(body);
2070
+ const note = noChange ? body.replace(/\s+/g, " ").replace(/^.*?no mapping change\.?\s*/i, "").trim() : void 0;
2071
+ hops.push({
2072
+ heading: s.heading,
2073
+ line: s.line,
2074
+ noChange,
2075
+ ...note ? { note } : {},
2076
+ rows,
2077
+ asIs: asIsFor(mappings, ifIds),
2078
+ from: "mappings.md"
2079
+ });
2080
+ }
2081
+ if (!hops.length)
2082
+ hops.push(...await hopsFromTargetFlows(changeFolder(root, change), mappings, interfaces));
2083
+ return { change, hops, catalogMappings: mappings.length };
2084
+ }
2085
+ var asIsFor = (mappings, ifIds) => mappings.filter((m) => ifIds.has(m.interface)).map((m) => ({
2086
+ id: m.id,
2087
+ interface: m.interface,
2088
+ source: m.sourceField,
2089
+ target: m.targetField,
2090
+ ...m.transform ? { transform: m.transform } : {}
2091
+ }));
2092
+ async function hopsFromTargetFlows(dir, mappings, interfaces) {
2093
+ const { deltas } = await readDeltas(dir);
2094
+ const hops = [];
2095
+ for (const d of deltas) {
2096
+ let at = 0;
2097
+ for (const s of d.steps) {
2098
+ at += 1;
2099
+ const heading = s.to ? s.to.entry : s.store ? s.store.table : void 0;
2100
+ if (!heading)
2101
+ continue;
2102
+ const as = s.fields?.as ?? {};
2103
+ const derived = s.fields?.derived ?? {};
2104
+ const names = fieldNames(s.fields);
2105
+ if (!names.length)
2106
+ continue;
2107
+ const k = hopKey(heading);
2108
+ const ifIds = new Set(interfaces.filter((i) => pathKey(i.normalizedPath) === k.path && (!k.method || !i.method || i.method.toUpperCase() === k.method)).map((i) => i.id));
2109
+ hops.push({
2110
+ heading,
2111
+ line: at,
2112
+ noChange: false,
2113
+ rows: names.map((name, i) => ({
2114
+ line: i + 1,
2115
+ source: name,
2116
+ target: as[name] ?? name,
2117
+ rule: derived[name] ?? "",
2118
+ whenMissing: ""
2119
+ })),
2120
+ asIs: asIsFor(mappings, ifIds),
2121
+ from: `target-flows/${d.flow}.yaml`
2122
+ });
2123
+ }
2124
+ }
2125
+ return hops;
2126
+ }
2127
+ function uiAcrossRoutes(r, ctx) {
2128
+ const { root } = ctx;
2129
+ return r.add("GET", "/api/across", async ({ query }) => {
2130
+ const view2 = strParam(query, "view") ?? "";
2131
+ if (!ACROSS_VIEWS.includes(view2))
2132
+ throw badRequest(`Say which view: ?view= one of ${ACROSS_VIEWS.join(", ")}.`);
2133
+ const rows = await acrossRows(root, view2);
2134
+ return {
2135
+ status: 200,
2136
+ body: { view: view2, changes: (await openChanges3(root)).length, rows }
2137
+ };
2138
+ }).add("GET", "/api/changes/{changeId}/mappings", async ({ params }) => {
2139
+ try {
2140
+ await changeStore(root).read(params.changeId);
2141
+ } catch {
2142
+ throw notFound(`There is no change ${params.changeId}.`);
2143
+ }
2144
+ return { status: 200, body: await mappingGrid(root, params.changeId) };
2145
+ });
2146
+ }
2147
+
2148
+ // packages/cli/dist/ui-api-graph.js
2149
+ var import_yaml2 = __toESM(require_dist(), 1);
2150
+ import { readFile as readFile3 } from "node:fs/promises";
2151
+ import { join as join4 } from "node:path";
2152
+ var lensOf = (v, fallback) => {
2153
+ if (v === null)
2154
+ return fallback;
2155
+ const l = api_exports.GraphLens.safeParse(v);
2156
+ if (!l.success)
2157
+ throw badRequest(`${v} isn't a lens. Lenses: ${api_exports.GRAPH_LENSES.join(", ")}.`);
2158
+ return l.data;
2159
+ };
2160
+ var idOf = (id) => {
2161
+ if (!api_exports.HierId.safeParse(id).success)
2162
+ throw badRequest(`${id} isn't a node id. Use estate, or <kind>:<record id>, e.g. system:ledger.`);
2163
+ return id;
2164
+ };
2165
+ var keyOf = (k) => {
2166
+ if (!ViewKey.safeParse(k).success)
2167
+ throw badRequest(`${k} isn't a view key. Use <lens>.<node>, e.g. architecture.estate.`);
2168
+ return k;
2169
+ };
2170
+ function uiGraphRoutes(r, ctx) {
2171
+ const estate = async () => {
2172
+ const records = await catalogRecords(ctx.root);
2173
+ if (!records.length)
2174
+ throw new HttpError(422, "Atticus can't show that yet", "The catalog is empty: read the repos first (atticus map <repo>, then atticus verify <repo> --write).", { notYet: true });
2175
+ return buildEstate(records);
2176
+ };
2177
+ const guard = (f) => {
2178
+ try {
2179
+ return f();
2180
+ } catch (e) {
2181
+ if (e instanceof NoSuchNode)
2182
+ throw notFound(e.message);
2183
+ throw e;
2184
+ }
2185
+ };
2186
+ const readLayout = async (view2) => {
2187
+ try {
2188
+ const f = DiagramLayout.safeParse((0, import_yaml2.parse)(await readFile3(join4(ctx.root, layoutPath(view2)), "utf8")));
2189
+ if (!f.success)
2190
+ throw new HttpError(422, "Atticus refused", `${layoutPath(view2)} isn't a layout file.`);
2191
+ return f.data;
2192
+ } catch (e) {
2193
+ if (e.code === "ENOENT")
2194
+ return void 0;
2195
+ throw e;
2196
+ }
2197
+ };
2198
+ return r.add("GET", "/api/graph/node/{id}", async ({ params, query }) => {
2199
+ const id = idOf(params.id);
2200
+ const lens = lensOf(query.get("lens"), "architecture");
2201
+ const d = query.get("depth");
2202
+ if (d !== null && !/^[1-3]$/.test(d))
2203
+ throw badRequest("depth is 1, 2 or 3.");
2204
+ const est = await estate();
2205
+ return {
2206
+ status: 200,
2207
+ body: guard(() => nodeView(est, id, { lens, depth: d ? Number(d) : 1, all: query.get("all") === "1" }))
2208
+ };
2209
+ }).add("GET", "/api/graph/focus/{id}", async ({ params, query }) => {
2210
+ const id = idOf(params.id);
2211
+ const lens = lensOf(query.get("lens"), "data");
2212
+ const est = await estate();
2213
+ return { status: 200, body: guard(() => focusView(est, id, { lens })) };
2214
+ }).add("GET", "/api/graph/layout/{viewKey}", async ({ params }) => {
2215
+ const view2 = keyOf(params.viewKey);
2216
+ const f = await readLayout(view2);
2217
+ return {
2218
+ status: 200,
2219
+ body: { view: view2, saved: !!f, path: layoutPath(view2), positions: f?.positions ?? {} }
2220
+ };
2221
+ }).add("PUT", "/api/graph/layout/{viewKey}", async ({ params, body }) => {
2222
+ const view2 = keyOf(params.viewKey);
2223
+ const b = api_exports.PutLayoutRequest.safeParse(body ?? {});
2224
+ if (!b.success)
2225
+ throw badRequest("Some fields aren't right.", {
2226
+ errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
2227
+ });
2228
+ const { lens, node, positions } = b.data;
2229
+ if (view2 !== viewKey(lens, node) && view2 !== viewKey(lens, node, "focus"))
2230
+ throw badRequest(`${view2} isn't the view key of ${node} in the ${lens} lens.`);
2231
+ if (Object.keys(positions).length > 5e3)
2232
+ throw badRequest("At most 5000 boxes per view.");
2233
+ const sorted = Object.fromEntries(Object.entries(positions).sort(([a], [b2]) => a < b2 ? -1 : 1).map(([k, p]) => [k, { x: Math.round(p.x), y: Math.round(p.y) }]));
2234
+ const file = DiagramLayout.parse({
2235
+ schemaVersion: LAYOUT_SCHEMA_VERSION,
2236
+ view: view2,
2237
+ lens,
2238
+ node,
2239
+ by: ctx.user,
2240
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2241
+ positions: sorted
2242
+ });
2243
+ await writeFileAtomic(join4(ctx.root, layoutPath(view2)), (0, import_yaml2.stringify)(file));
2244
+ ctx.events.publish({ type: "file.changed", path: layoutPath(view2) });
2245
+ return {
2246
+ status: 200,
2247
+ body: { view: view2, saved: true, path: layoutPath(view2), positions: file.positions }
2248
+ };
2249
+ });
2250
+ }
2251
+
2252
+ // packages/cli/dist/ui-api-specs.js
2253
+ import { stat as stat2 } from "node:fs/promises";
2254
+ import { join as join5 } from "node:path";
2255
+ var idOf2 = (v, what) => {
2256
+ if (!RecordId.safeParse(v).success)
2257
+ throw badRequest(`${v} isn't a ${what} id. Ids are lowercase: a-z, 0-9, '.', '_' and '-'.`);
2258
+ return v;
2259
+ };
2260
+ var confirmed = (f) => f.steps.filter((s) => s.confirmedBy).length;
2261
+ function uiSpecRoutes(r, ctx) {
2262
+ const root = ctx.root;
2263
+ const specOf = async (repo) => {
2264
+ if (!(await specRepos(root)).includes(repo))
2265
+ throw notFound(`${repo} has no spec. Draft one with atticus spec draft ${repo}.`);
2266
+ return readRepoSpec(root, repo, { cacheRoot: root });
2267
+ };
2268
+ const namesIn = async (repos) => {
2269
+ const names = /* @__PURE__ */ new Map();
2270
+ const have = new Set(await specRepos(root));
2271
+ for (const repo of [...new Set(repos)].filter((x) => have.has(x)))
2272
+ for (const f of (await readRepoSpec(root, repo, { cacheRoot: root })).flows)
2273
+ names.set(f.id, f.name);
2274
+ return names;
2275
+ };
2276
+ const linksOf = (joins, flow) => ({
2277
+ out: (joins?.joins ?? []).filter((j) => j.from.flow === flow),
2278
+ in: (joins?.joins ?? []).filter((j) => j.to.flow === flow),
2279
+ unmatched: (joins?.unmatched ?? []).filter((u) => u.from.flow === flow)
2280
+ });
2281
+ r.add("GET", "/api/specs", async () => {
2282
+ const repos = [];
2283
+ for (const repo of await specRepos(root))
2284
+ repos.push({
2285
+ repo,
2286
+ flows: (await readRepoSpec(root, repo, { cacheRoot: root })).flows.length
2287
+ });
2288
+ return { status: 200, body: { repos } };
2289
+ });
2290
+ r.add("GET", "/api/specs/{repo}", async ({ params }) => {
2291
+ const repo = idOf2(params.repo, "repo");
2292
+ const spec = await specOf(repo);
2293
+ const joins = await readEstateJoins(root);
2294
+ const body = {
2295
+ repo,
2296
+ path: specRelOf(root, repo),
2297
+ ...spec.header?.commit ? { commit: spec.header.commit } : {},
2298
+ ...spec.header?.template ? { template: spec.header.template } : {},
2299
+ flows: spec.flows.map((f) => {
2300
+ const l = linksOf(joins, f.id);
2301
+ return {
2302
+ id: f.id,
2303
+ name: f.name,
2304
+ component: f.component,
2305
+ trigger: { kind: f.trigger.kind, what: f.trigger.what },
2306
+ steps: f.steps.length,
2307
+ confirmed: confirmed(f),
2308
+ errors: f.errors.length,
2309
+ calls: l.out.length,
2310
+ calledBy: l.in.length
2311
+ };
2312
+ }),
2313
+ problems: spec.problems
2314
+ };
2315
+ return { status: 200, body };
2316
+ });
2317
+ r.add("GET", "/api/specs/{repo}/flows/{flowId}", async ({ params }) => {
2318
+ const repo = idOf2(params.repo, "repo");
2319
+ const id = idOf2(params.flowId, "flow");
2320
+ const flow = (await specOf(repo)).flows.find((f) => f.id === id);
2321
+ if (!flow)
2322
+ throw notFound(`${repo}'s spec has no flow ${id}.`);
2323
+ const joins = await readEstateJoins(root);
2324
+ const l = linksOf(joins, id);
2325
+ const names = await namesIn([...l.out.map((j) => j.to.repo), ...l.in.map((j) => j.from.repo)]);
2326
+ const named = (flowId) => {
2327
+ const n = names.get(flowId);
2328
+ return n ? { name: n } : {};
2329
+ };
2330
+ const body = {
2331
+ repo,
2332
+ file: `${specRelOf(root, repo)}/${id}.yaml`,
2333
+ flow,
2334
+ joined: joins !== void 0,
2335
+ calls: l.out.map((j) => ({
2336
+ step: j.from.step,
2337
+ repo: j.to.repo,
2338
+ flow: j.to.flow,
2339
+ ...named(j.to.flow),
2340
+ how: j.how
2341
+ })),
2342
+ calledBy: l.in.map((j) => ({
2343
+ step: j.from.step,
2344
+ repo: j.from.repo,
2345
+ flow: j.from.flow,
2346
+ ...named(j.from.flow),
2347
+ how: j.how
2348
+ })),
2349
+ unmatched: l.unmatched.map((u) => ({
2350
+ step: u.from.step,
2351
+ entry: u.entry,
2352
+ reason: u.reason,
2353
+ message: u.message
2354
+ }))
2355
+ };
2356
+ return { status: 200, body };
2357
+ });
2358
+ const deltasOf = async (change) => {
2359
+ const dir = join5(changesDirOf(root), change);
2360
+ if (!await stat2(join5(dir, "change.yaml")).catch(() => void 0))
2361
+ throw notFound(`There is no change ${change}.`);
2362
+ return readDeltas(dir);
2363
+ };
2364
+ const todayOf = async (repo, flow) => (await specRepos(root)).includes(repo) ? (await readRepoSpec(root, repo, { cacheRoot: root })).flows.find((f) => f.id === flow) : void 0;
2365
+ r.add("GET", "/api/changes/{changeId}/target-flows", async ({ params }) => {
2366
+ const change = idOf2(params.changeId, "change");
2367
+ const { deltas, problems } = await deltasOf(change);
2368
+ const flows = [];
2369
+ for (const d of deltas) {
2370
+ const m = mergeDelta(d, await todayOf(d.repo, d.flow));
2371
+ flows.push({
2372
+ flow: d.flow,
2373
+ repo: d.repo,
2374
+ change: d.change,
2375
+ name: m.name,
2376
+ why: d.why,
2377
+ ...m.counts
2378
+ });
2379
+ }
2380
+ return { status: 200, body: { change, flows, problems } };
2381
+ });
2382
+ r.add("GET", "/api/changes/{changeId}/conformance", async ({ params }) => {
2383
+ const change = idOf2(params.changeId, "change");
2384
+ const { conformanceOf } = await import("./chunk-Y4NG2FP5.js");
2385
+ const c = await conformanceOf(root, change);
2386
+ if (!c)
2387
+ throw notFound(`There is no change ${change}.`);
2388
+ return { status: 200, body: c };
2389
+ });
2390
+ r.add("GET", "/api/changes/{changeId}/estimate", async ({ params }) => {
2391
+ const change = idOf2(params.changeId, "change");
2392
+ const e = await estimateOf(root, change);
2393
+ if (!e)
2394
+ throw notFound(`There is no change ${change}.`);
2395
+ return { status: 200, body: e };
2396
+ });
2397
+ r.add("GET", "/api/changes/{changeId}/map", async ({ params }) => {
2398
+ const change = idOf2(params.changeId, "change");
2399
+ const m = await changeMapOf(root, change);
2400
+ if (!m)
2401
+ throw notFound(`There is no change ${change}.`);
2402
+ return { status: 200, body: changeMapBody(change, m) };
2403
+ });
2404
+ r.add("GET", "/api/changes/{changeId}/target-flows/{flowId}", async ({ params }) => {
2405
+ const change = idOf2(params.changeId, "change");
2406
+ const flow = idOf2(params.flowId, "flow");
2407
+ const d = (await deltasOf(change)).deltas.find((x) => x.flow === flow);
2408
+ if (!d)
2409
+ throw notFound(`Change ${change} has no target flow ${flow}.`);
2410
+ const { flow: _f, repo: _r, change: delta, ...m } = mergeDelta(d, await todayOf(d.repo, flow));
2411
+ const body = {
2412
+ change,
2413
+ file: `${changesRelOf(root)}/${change}/${TARGET_DIR}/${flow}.yaml`,
2414
+ flow,
2415
+ repo: d.repo,
2416
+ delta,
2417
+ ...m
2418
+ };
2419
+ return { status: 200, body };
2420
+ });
2421
+ return r;
2422
+ }
2423
+
2424
+ // packages/cli/dist/ui-api-knowledge.js
2425
+ var import_yaml3 = __toESM(require_dist(), 1);
2426
+ import { readFile as readFile4, stat as stat3 } from "node:fs/promises";
2427
+ import { join as join6 } from "node:path";
2428
+ var STALE_DAYS = 14;
2429
+ var SLUG = /^[a-z0-9][a-z0-9-]*$/;
2430
+ var DAY = 864e5;
2431
+ var stamp = (d) => d.toISOString().replace(/\.\d{3}Z$/, "Z");
2432
+ var idOf3 = (v, what) => {
2433
+ if (!RecordId.safeParse(v).success)
2434
+ throw badRequest(`${v} isn't a ${what} id. Ids are lowercase: a-z, 0-9, '.', '_' and '-'.`);
2435
+ return v;
2436
+ };
2437
+ var invalid = (what, e) => badRequest(what, {
2438
+ errors: e.issues.map((i) => ({ path: i.path.map(String).join("."), message: i.message }))
2439
+ });
2440
+ function asSet(set) {
2441
+ if (!set)
2442
+ return void 0;
2443
+ const out = {};
2444
+ for (const [k, v] of Object.entries(set))
2445
+ out[k] = typeof v === "string" ? v : v.map((x) => typeof x === "string" ? x : x.label?.trim() ? `${x.label.trim()}=${x.url}` : x.url);
2446
+ return out;
2447
+ }
2448
+ var keepAs = (recordKind) => KNOWLEDGE_KINDS.filter((k) => KNOWLEDGE_RECORD_KIND[k] === recordKind);
2449
+ function conflictView(v) {
2450
+ const c = v.conflict;
2451
+ return {
2452
+ key: v.key,
2453
+ about: c.about,
2454
+ ...c.summary ? { summary: c.summary } : {},
2455
+ pages: c.pages.map((p, k) => ({ source: p.source, page: p.page, says: c.says[k] ?? "" })),
2456
+ applies: v.applies.map((p) => ({ source: p.source, page: p.page })),
2457
+ ...v.decided ? { decided: v.decided } : {},
2458
+ ...v.suggested ? {
2459
+ suggested: {
2460
+ source: v.suggested.source,
2461
+ page: v.suggested.page,
2462
+ signal: v.suggested.signal,
2463
+ detail: v.suggested.detail
2464
+ }
2465
+ } : {},
2466
+ ...v.question ? { question: v.question } : {}
2467
+ };
2468
+ }
2469
+ function itemView(r, review, conflicts = []) {
2470
+ const mine = conflicts.filter((v) => v.conflict.about.includes(r.id));
2471
+ return {
2472
+ id: r.id,
2473
+ kind: knowledgeKindOf(r) ?? keepAs(r.kind)[0],
2474
+ recordKind: r.kind,
2475
+ title: itemTitle(r),
2476
+ record: r,
2477
+ ...review ? { review } : {},
2478
+ fields: correctableFields(r),
2479
+ conflict: !!r.flags?.some((f) => f.startsWith("conflict:")) || mine.length > 0,
2480
+ ...mine.length ? { conflicts: mine.map(conflictView) } : {}
2481
+ };
2482
+ }
2483
+ function candidateView(c) {
2484
+ const r = c.record;
2485
+ return {
2486
+ id: r.id,
2487
+ kind: knowledgeKindOf(r) ?? keepAs(r.kind)[0],
2488
+ recordKind: r.kind,
2489
+ title: itemTitle(r),
2490
+ record: r,
2491
+ ...c.known ? { known: c.known } : {},
2492
+ ...c.kept ? { kept: c.kept } : {},
2493
+ keepAs: keepAs(r.kind)
2494
+ };
2495
+ }
2496
+ var exists = (p) => stat3(p).then(() => true, () => false);
2497
+ function capture() {
2498
+ const lines = [];
2499
+ const problems = [];
2500
+ return {
2501
+ io: { out: (l) => lines.push(l), err: (l) => problems.push(l) },
2502
+ lines,
2503
+ problems
2504
+ };
2505
+ }
2506
+ function uiKnowledgeRoutes(r, ctx, deps = {}) {
2507
+ const root = ctx.root;
2508
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
2509
+ const changed = (path) => ctx.events.publish({ type: "file.changed", path });
2510
+ const appliesView = async (change) => {
2511
+ const file = `${changesRelOf(root)}/${change}/knowledge.yaml`;
2512
+ const present = await exists(join6(changeFolder(root, change), "knowledge.yaml"));
2513
+ const doc = present ? await readKnowledgeApplies(root, change) : void 0;
2514
+ if (present && !doc)
2515
+ throw new HttpError(422, "Atticus can't read that file", `${file} isn't a valid knowledge list.`);
2516
+ const titles = /* @__PURE__ */ new Map();
2517
+ if (doc?.items.some((i) => !i.title) || doc?.nearMisses.some((n) => !n.title))
2518
+ for (const r2 of await readKnowledge(root, { cacheRoot: root }).then((k) => [
2519
+ ...k.items,
2520
+ ...k.rejected
2521
+ ]))
2522
+ titles.set(r2.id, itemTitle(r2));
2523
+ const titled = (x) => {
2524
+ const title = x.title ?? titles.get(x.id);
2525
+ return title ? { ...x, title } : x;
2526
+ };
2527
+ const used = new Set((doc?.items ?? []).filter((i) => i.status !== "excluded").map((i) => i.id));
2528
+ const conflicts = used.size ? (await readConflicts(root, { records: await codeFactRecords(root) }).catch(() => [])).filter((v) => conflictTouches(v, used)) : [];
2529
+ const settings = readSourceSettings(root);
2530
+ const quoted = /* @__PURE__ */ new Map();
2531
+ if (settings.size && doc?.items.length) {
2532
+ for (const r2 of (await readKnowledge(root, { cacheRoot: root })).items)
2533
+ if (r2.quote)
2534
+ quoted.set(r2.id, { source: r2.quote.source, page: r2.quote.page });
2535
+ }
2536
+ const sourceOf = (id) => {
2537
+ const q = quoted.get(id);
2538
+ if (q)
2539
+ return q;
2540
+ const hash = id.indexOf("#");
2541
+ const slash = id.indexOf("/");
2542
+ return hash > 0 && slash > 0 && slash < hash ? { source: id.slice(0, slash), page: id.slice(slash + 1, hash) } : void 0;
2543
+ };
2544
+ return {
2545
+ change,
2546
+ file,
2547
+ present,
2548
+ items: (doc?.items ?? []).map((i) => {
2549
+ const reason = plainWhy(i);
2550
+ const at = sourceOf(i.id);
2551
+ const w = at ? settings.get(at.source) : void 0;
2552
+ const authority = w && (w.authority || w.byPage?.length) ? pageStanding(w, at.page, {}).authority : void 0;
2553
+ return {
2554
+ ...titled(i),
2555
+ ...reason ? { reason } : {},
2556
+ ...at && settings.has(at.source) ? { source: at.source } : {},
2557
+ ...authority ? { authority } : {}
2558
+ };
2559
+ }),
2560
+ nearMisses: (doc?.nearMisses ?? []).map(titled),
2561
+ ...conflicts.length ? { conflicts: conflicts.map(conflictView) } : {},
2562
+ command: `atticus change knowledge ${change}`
2563
+ };
2564
+ };
2565
+ const reviewed = async (id) => {
2566
+ const k = await readKnowledge(root, { cacheRoot: root });
2567
+ const item = [...k.items, ...k.rejected].find((i) => i.id === id);
2568
+ const conflicts = await readConflicts(root, {
2569
+ conflicts: k.report?.conflicts ?? [],
2570
+ records: await codeFactRecords(root)
2571
+ }).catch(() => []);
2572
+ return item ? itemView(item, k.reviews[id], conflicts) : void 0;
2573
+ };
2574
+ const findView = (f) => ({
2575
+ slug: f.slug,
2576
+ phrase: f.phrase,
2577
+ at: f.at,
2578
+ file: `${knowledgeRelOf(root)}/finds/${f.slug}.yaml`,
2579
+ matches: f.matches,
2580
+ candidates: f.candidates.map(candidateView)
2581
+ });
2582
+ const draftView = async () => {
2583
+ const op = await loadOrgPack(orgPackDirOf(root));
2584
+ const platforms = [];
2585
+ for (const { platform } of op.profiles) {
2586
+ const text2 = await readFile4(join6(proposalsDirOf(root), "orgpack", `${platform}.yaml`), "utf8").catch(() => void 0);
2587
+ const data = text2 === void 0 ? void 0 : (0, import_yaml3.parse)(text2);
2588
+ const proposals = Array.isArray(data?.proposals) ? data.proposals.flatMap((p) => {
2589
+ const x = OrgPackProposal.safeParse(p);
2590
+ return x.success ? [x.data] : [];
2591
+ }) : [];
2592
+ platforms.push({
2593
+ platform,
2594
+ file: `${proposalsRelOf(root)}/orgpack/${platform}.yaml`,
2595
+ present: text2 !== void 0,
2596
+ ...typeof data?.at === "string" ? { at: data.at } : {},
2597
+ proposals,
2598
+ command: `atticus orgpack draft --platform ${platform}`
2599
+ });
2600
+ }
2601
+ return { platforms };
2602
+ };
2603
+ return r.add("GET", "/api/knowledge/sources", async () => {
2604
+ const op = await loadOrgPack(orgPackDirOf(root));
2605
+ const problems = op.problems.filter((p) => p.file === "sources.yaml").map((p) => ({
2606
+ file: `org-pack/${p.file}`,
2607
+ message: `${p.path ? `${p.path}: ` : ""}${p.message}`
2608
+ }));
2609
+ const k = await readKnowledge(root, { raw: true, cacheRoot: root });
2610
+ const scanned = new Map((k.report?.snapshots ?? []).map((s) => [s.source, s]));
2611
+ const today = now().getTime();
2612
+ const sources = [];
2613
+ for (const w of op.sources.wikis) {
2614
+ const date = (await snapshotDates(root, w.id))[0];
2615
+ let snapshot;
2616
+ if (date) {
2617
+ const file = join6(knowledgeDirOf(root), "snapshots", w.id, date, SNAPSHOT_FILE);
2618
+ const text2 = await readFile4(file, "utf8").catch(() => void 0);
2619
+ const m = text2 === void 0 ? void 0 : SnapshotManifest.safeParse((0, import_yaml3.parse)(text2));
2620
+ if (m?.success)
2621
+ snapshot = {
2622
+ date,
2623
+ takenAt: m.data.takenAt,
2624
+ ...m.data.commit ? { commit: m.data.commit } : {},
2625
+ pages: m.data.pages.length,
2626
+ skipped: m.data.skipped.length,
2627
+ ageDays: Math.max(0, Math.floor((today - Date.parse(`${date}T00:00:00Z`)) / DAY))
2628
+ };
2629
+ else
2630
+ problems.push({
2631
+ file: `${knowledgeRelOf(root)}/snapshots/${w.id}/${date}/${SNAPSHOT_FILE}`,
2632
+ message: "not a snapshot manifest"
2633
+ });
2634
+ }
2635
+ const s = scanned.get(w.id);
2636
+ sources.push({
2637
+ id: w.id,
2638
+ type: w.type,
2639
+ location: w.location,
2640
+ ...w.ref ? { ref: w.ref } : {},
2641
+ ...w.path ? { path: w.path } : {},
2642
+ ...w.checkout ? { checkout: w.checkout } : {},
2643
+ ...w.owner ? { owner: w.owner } : {},
2644
+ tags: { kinds: w.tags?.kinds ?? [], frameworks: w.tags?.frameworks ?? [] },
2645
+ // 2.30.0 (ADR 0132): how much its word counts and where it applies.
2646
+ ...w.authority ? { authority: w.authority } : {},
2647
+ ...w.appliesTo ? { appliesTo: w.appliesTo, scope: scopeWords(w.appliesTo) } : {},
2648
+ ...w.byPage?.length ? { byPage: w.byPage.length } : {},
2649
+ ...w.readRestricted !== void 0 ? { readRestricted: w.readRestricted } : {},
2650
+ ...w.maxPages ? { maxPages: w.maxPages } : {},
2651
+ ...snapshot ? { snapshot } : {},
2652
+ ...s ? { scanned: { date: s.date, pages: s.pages } } : {},
2653
+ stale: !snapshot || snapshot.ageDays > STALE_DAYS,
2654
+ commands: {
2655
+ sync: `atticus sources sync --source ${w.id}`,
2656
+ scan: `atticus knowledge scan --source ${w.id}`,
2657
+ slash: `/atticus-knowledge ${w.id}`
2658
+ }
2659
+ });
2660
+ }
2661
+ const body = {
2662
+ file: "org-pack/sources.yaml",
2663
+ types: [...api_exports.WIKI_TYPES],
2664
+ kindSuggestions: [...SOURCE_KIND_SUGGESTIONS],
2665
+ staleDays: STALE_DAYS,
2666
+ sources,
2667
+ problems
2668
+ };
2669
+ return { status: 200, body };
2670
+ }).add("POST", "/api/knowledge/sources", async ({ body }) => {
2671
+ const b = api_exports.AddSourceRequest.safeParse(body ?? {});
2672
+ if (!b.success)
2673
+ throw invalid("Some fields aren't right.", b.error);
2674
+ const { type, location, kinds, frameworks, ...o } = b.data;
2675
+ const c = capture();
2676
+ const code = await sourcesAdd(root, type, location, c.io, {
2677
+ ...o.id ? { id: o.id } : {},
2678
+ ...o.path ? { path: o.path } : {},
2679
+ ...o.ref ? { ref: o.ref } : {},
2680
+ ...o.submodule ? { submodule: true } : {},
2681
+ ...kinds?.length ? { kinds } : {},
2682
+ ...frameworks?.length ? { frameworks } : {},
2683
+ ...o.owner ? { owner: o.owner } : {},
2684
+ save: o.save
2685
+ });
2686
+ const words = [...c.problems, ...c.lines].join("\n") || "Atticus refused.";
2687
+ if (code === 2)
2688
+ throw badRequest(words);
2689
+ if (code !== 0)
2690
+ throw new HttpError(422, "Atticus refused", words);
2691
+ if (o.save)
2692
+ changed("org-pack/sources.yaml");
2693
+ return { status: 200, body: { code, lines: c.lines, problems: c.problems } };
2694
+ }).add("POST", "/api/knowledge/sources/{sourceId}/sync", async ({ params }) => {
2695
+ const id = idOf3(params.sourceId, "source");
2696
+ const op = await loadOrgPack(orgPackDirOf(root));
2697
+ if (id !== INPUTS_SOURCE_ID && !op.sources.wikis.some((w) => w.id === id))
2698
+ throw notFound(`There is no source ${id} in org-pack/sources.yaml.`);
2699
+ const c = capture();
2700
+ const code = await sourcesSync(root, c.io, { source: id, now: stamp(now()) });
2701
+ if (code === 2)
2702
+ throw new HttpError(422, "Atticus refused", c.problems.join("\n") || "Nothing to sync.");
2703
+ changed(`${knowledgeRelOf(root)}/snapshots/${id}`);
2704
+ return { status: 200, body: { code, lines: c.lines, problems: c.problems } };
2705
+ }).add("GET", "/api/knowledge/scan", async () => {
2706
+ const k = await readKnowledge(root, { cacheRoot: root });
2707
+ const r2 = k.report;
2708
+ const body = {
2709
+ ...r2 ? { scannedAt: r2.scannedAt } : {},
2710
+ snapshots: (r2?.snapshots ?? []).map((s) => ({
2711
+ source: s.source,
2712
+ date: s.date,
2713
+ pages: s.pages
2714
+ })),
2715
+ ...r2?.changes ? { changes: r2.changes } : {},
2716
+ checkAgain: [...k.items, ...k.rejected].filter((i) => k.reviews[i.id]?.state === "stale").sort((a, b) => a.id.localeCompare(b.id)).map((i) => ({
2717
+ id: i.id,
2718
+ kind: knowledgeKindOf(i) ?? keepAs(i.kind)[0],
2719
+ title: itemTitle(i),
2720
+ by: k.reviews[i.id].by
2721
+ })),
2722
+ conflicts: r2?.conflicts.length ?? 0,
2723
+ injections: r2?.injections.length ?? 0,
2724
+ failed: r2?.failed.length ?? 0,
2725
+ command: "atticus knowledge scan",
2726
+ slash: "/atticus-knowledge"
2727
+ };
2728
+ return { status: 200, body };
2729
+ }).add("GET", "/api/knowledge/items", async ({ query }) => {
2730
+ const kind = strParam(query, "kind");
2731
+ if (kind !== void 0 && !KNOWLEDGE_KINDS.includes(kind))
2732
+ throw badRequest(`There is no knowledge kind ${kind}. Use one of: ${KNOWLEDGE_KINDS.join(", ")}.`);
2733
+ const tag = strParam(query, "tag");
2734
+ const k = await readKnowledge(root, { cacheRoot: root });
2735
+ const items = filterItems([...k.items, ...k.rejected], {
2736
+ ...kind ? { kind } : {},
2737
+ ...tag ? { tag } : {}
2738
+ }).sort((a, b) => a.id.localeCompare(b.id));
2739
+ const conflicts = await readConflicts(root, {
2740
+ conflicts: k.report?.conflicts ?? [],
2741
+ records: await codeFactRecords(root)
2742
+ }).catch(() => []);
2743
+ const body = {
2744
+ items: items.map((i) => itemView(i, k.reviews[i.id], conflicts)),
2745
+ problems: k.problems.map((p) => ({
2746
+ file: `${knowledgeRelOf(root)}/${p.file.startsWith("reviews/") || p.file === "scan.yaml" ? "" : "items/"}${p.file}`,
2747
+ message: p.message
2748
+ }))
2749
+ };
2750
+ return { status: 200, body };
2751
+ }).add("POST", "/api/knowledge/items/{itemId}/review", async ({ params, body }) => {
2752
+ const id = idOf3(params.itemId, "knowledge item");
2753
+ const b = api_exports.ReviewItemRequest.safeParse(body ?? {});
2754
+ if (!b.success)
2755
+ throw invalid("Say verified, corrected or rejected.", b.error);
2756
+ const raw = await readKnowledge(root, { raw: true, cacheRoot: root });
2757
+ const item = raw.items.find((i) => i.id === id);
2758
+ if (!item)
2759
+ throw notFound(`There is no knowledge item ${id}.`);
2760
+ let set = asSet(b.data.set);
2761
+ if (b.data.verdict === "corrected") {
2762
+ const before = (await readReviews(root)).reviews.get(id);
2763
+ if (before?.corrections)
2764
+ set = { ...asSet(before.corrections), ...set };
2765
+ }
2766
+ const res = reviewItem(item, {
2767
+ verdict: b.data.verdict,
2768
+ by: ctx.user,
2769
+ at: stamp(now()),
2770
+ ...b.data.note ? { note: b.data.note } : {},
2771
+ ...set && Object.keys(set).length ? { set } : {}
2772
+ });
2773
+ if ("error" in res)
2774
+ throw badRequest(res.error);
2775
+ const file = await writeReview(root, res.review);
2776
+ changed(`${knowledgeRelOf(root)}/${file}`);
2777
+ return {
2778
+ status: 200,
2779
+ body: { item: await reviewed(id), file: `${knowledgeRelOf(root)}/${file}` }
2780
+ };
2781
+ }).add("GET", "/api/knowledge/search", async ({ query }) => {
2782
+ const phrase = strParam(query, "q")?.trim();
2783
+ if (!phrase)
2784
+ throw badRequest("Say what to look for: ?q=<a term or phrase>.");
2785
+ if (phrase.length > 200)
2786
+ throw badRequest("Search for a term or phrase of up to 200 characters.");
2787
+ const source = strParam(query, "source");
2788
+ if (source !== void 0)
2789
+ idOf3(source, "source");
2790
+ const ids = await snapshotSources(root);
2791
+ if (source && !ids.includes(source))
2792
+ throw notFound(`${source} has no snapshot yet: atticus sources sync --source ${source}.`);
2793
+ const snapshots = [];
2794
+ for (const s of source ? [source] : ids) {
2795
+ const snap = await readSnapshot(root, s).catch(() => void 0);
2796
+ if (snap)
2797
+ snapshots.push(snap);
2798
+ }
2799
+ const matches = searchPages(snapshots, phrase);
2800
+ const body = {
2801
+ phrase,
2802
+ sources: snapshots.length,
2803
+ matches,
2804
+ pages: [...new Set(matches.map((m) => `${m.source}/${m.page}`))],
2805
+ command: `atticus knowledge find ${JSON.stringify(phrase)}${source ? ` --source ${source}` : ""}`,
2806
+ slash: `/atticus-find ${phrase}`
2807
+ };
2808
+ return { status: 200, body };
2809
+ }).add("GET", "/api/knowledge/finds", async () => {
2810
+ const finds = [];
2811
+ for (const f of await readFinds(root)) {
2812
+ const s = await readFind(root, f.slug);
2813
+ if (s)
2814
+ finds.push(findView(s));
2815
+ }
2816
+ return { status: 200, body: { finds } };
2817
+ }).add("GET", "/api/knowledge/finds/{slug}", async ({ params }) => {
2818
+ const slug = params.slug;
2819
+ if (!SLUG.test(slug))
2820
+ throw badRequest(`${slug} isn't a saved search.`);
2821
+ const f = await readFind(root, slug);
2822
+ if (!f)
2823
+ throw notFound(`There is no saved search ${slug}.`);
2824
+ return { status: 200, body: findView(f) };
2825
+ }).add("POST", "/api/knowledge/finds/{slug}/keep", async ({ params, body }) => {
2826
+ const slug = params.slug;
2827
+ if (!SLUG.test(slug))
2828
+ throw badRequest(`${slug} isn't a saved search.`);
2829
+ const b = api_exports.KeepCandidateRequest.safeParse(body ?? {});
2830
+ if (!b.success)
2831
+ throw invalid("Say which candidate to keep: { item }.", b.error);
2832
+ const f = await readFind(root, slug);
2833
+ if (!f)
2834
+ throw notFound(`There is no saved search ${slug}.`);
2835
+ if (!f.candidates.some((c) => c.record.id === b.data.item))
2836
+ throw notFound(`The search ${slug} has no candidate ${b.data.item}.`);
2837
+ const set = asSet(b.data.set);
2838
+ const res = await keepCandidate(root, slug, b.data.item, {
2839
+ ...b.data.as ? { as: b.data.as } : {},
2840
+ by: ctx.user,
2841
+ at: stamp(now()),
2842
+ ...b.data.note ? { note: b.data.note } : {},
2843
+ ...set && Object.keys(set).length ? { set } : {}
2844
+ });
2845
+ if ("error" in res)
2846
+ throw new HttpError(422, "Atticus refused", res.error);
2847
+ changed(`${knowledgeRelOf(root)}/items`);
2848
+ const item = await reviewed(b.data.item) ?? itemView(res.item, void 0);
2849
+ return { status: 200, body: { item, as: res.as } };
2850
+ }).add("GET", "/api/changes/{changeId}/needs", async ({ params }) => {
2851
+ const change = idOf3(params.changeId, "change");
2852
+ if (!await exists(changeFolder(root, change)))
2853
+ throw notFound(`There is no change ${change}.`);
2854
+ const fileExists = await exists(join6(changeFolder(root, change), NEEDS_FILE));
2855
+ const f = await readNeeds(root, change);
2856
+ if (fileExists && !f)
2857
+ throw new HttpError(422, "Atticus can't read that file", `${changesRelOf(root)}/${change}/${NEEDS_FILE} isn't a valid needs file.`);
2858
+ const k = f ? await readKnowledge(root, { cacheRoot: root }) : void 0;
2859
+ const view2 = (n) => ({
2860
+ ...n,
2861
+ ...k?.reviews[n.item] ? { review: k.reviews[n.item] } : {}
2862
+ });
2863
+ const body = {
2864
+ change,
2865
+ file: `${changesRelOf(root)}/${change}/${NEEDS_FILE}`,
2866
+ present: !!f,
2867
+ ...f ? { updatedAt: f.updatedAt } : {},
2868
+ needs: (f?.needs ?? []).map(view2),
2869
+ considered: f?.considered ?? [],
2870
+ command: `atticus change needs ${change}`,
2871
+ slash: `/atticus-needs ${change}`
2872
+ };
2873
+ return { status: 200, body };
2874
+ }).add("POST", "/api/changes/{changeId}/needs/{itemId}", async ({ params, body }) => {
2875
+ const change = idOf3(params.changeId, "change");
2876
+ const item = idOf3(params.itemId, "knowledge item");
2877
+ const b = api_exports.MarkNeedRequest.safeParse(body ?? {});
2878
+ if (!b.success)
2879
+ throw invalid("Say done, not-needed or open.", b.error);
2880
+ if (!await exists(changeFolder(root, change)))
2881
+ throw notFound(`There is no change ${change}.`);
2882
+ const res = await markNeed(root, change, item, {
2883
+ status: b.data.status,
2884
+ by: ctx.user,
2885
+ at: stamp(now()),
2886
+ ...b.data.note ? { note: b.data.note } : {},
2887
+ ...b.data.for ? { for: b.data.for } : {}
2888
+ });
2889
+ if ("error" in res) {
2890
+ if (/has no need /.test(res.error))
2891
+ throw notFound(res.error);
2892
+ if (/has no needs yet/.test(res.error))
2893
+ throw new HttpError(422, "Atticus can't do that yet", res.error);
2894
+ throw badRequest(res.error);
2895
+ }
2896
+ const file = `${changesRelOf(root)}/${change}/${NEEDS_FILE}`;
2897
+ changed(file);
2898
+ const k = await readKnowledge(root, { cacheRoot: root });
2899
+ return {
2900
+ status: 200,
2901
+ body: {
2902
+ need: { ...res.need, ...k.reviews[item] ? { review: k.reviews[item] } : {} },
2903
+ file
2904
+ }
2905
+ };
2906
+ }).add("GET", "/api/changes/{changeId}/knowledge", async ({ params }) => {
2907
+ const change = idOf3(params.changeId, "change");
2908
+ if (!await exists(changeFolder(root, change)))
2909
+ throw notFound(`There is no change ${change}.`);
2910
+ return { status: 200, body: await appliesView(change) };
2911
+ }).add("POST", "/api/changes/{changeId}/knowledge/{itemId}", async ({ params, body }) => {
2912
+ const change = idOf3(params.changeId, "change");
2913
+ const item = params.itemId;
2914
+ if (!item.trim() || item.length > 400 || /[\p{Cc}]/u.test(item))
2915
+ throw badRequest(`${JSON.stringify(item)} isn't a knowledge item id.`);
2916
+ const b = api_exports.SetChangeKnowledgeRequest.safeParse(body ?? {});
2917
+ if (!b.success)
2918
+ throw invalid("Say confirmed or excluded: { status }.", b.error);
2919
+ if (!await exists(changeFolder(root, change)))
2920
+ throw notFound(`There is no change ${change}.`);
2921
+ const before = await appliesView(change);
2922
+ if (!before.present)
2923
+ throw new HttpError(422, "Atticus can't do that yet", `${before.file} doesn't exist yet: its stages write it when they select context (${before.command}).`);
2924
+ try {
2925
+ await setKnowledgeStatus(root, change, item, b.data.status);
2926
+ } catch (e) {
2927
+ throw notFound(e.message);
2928
+ }
2929
+ changed(before.file);
2930
+ const result = {
2931
+ knowledge: await appliesView(change),
2932
+ file: before.file,
2933
+ commit: `git add ${before.file} && git commit -m "${change}: the knowledge that applies"`
2934
+ };
2935
+ return { status: 200, body: result };
2936
+ }).add("GET", "/api/orgpack/draft", async () => ({ status: 200, body: await draftView() })).add("POST", "/api/orgpack/draft", async ({ body }) => {
2937
+ const b = api_exports.DraftOrgPackRequest.safeParse(body ?? {});
2938
+ if (!b.success)
2939
+ throw invalid("Say which platform: { platform }.", b.error);
2940
+ try {
2941
+ await draftOrgPack(root, {
2942
+ ...b.data.platform ? { platform: b.data.platform } : {},
2943
+ now: now()
2944
+ });
2945
+ } catch (e) {
2946
+ throw new HttpError(422, "Atticus refused", e.message);
2947
+ }
2948
+ changed(`${proposalsRelOf(root)}/orgpack`);
2949
+ return { status: 200, body: await draftView() };
2950
+ }).add("POST", "/api/orgpack/draft/{platform}/accept", async ({ params, body }) => {
2951
+ const platform = idOf3(params.platform, "platform");
2952
+ const b = api_exports.AcceptOrgPackRequest.safeParse(body ?? {});
2953
+ if (!b.success)
2954
+ throw invalid("Pick the systems to accept: { only: [ids] }.", b.error);
2955
+ try {
2956
+ const res = await acceptOrgPackDraft(root, platform, { only: b.data.only });
2957
+ changed(res.file);
2958
+ return { status: 200, body: res };
2959
+ } catch (e) {
2960
+ const words = e.message;
2961
+ if (/There is no draft/.test(words))
2962
+ throw notFound(words);
2963
+ throw new HttpError(422, "Atticus refused", words);
2964
+ }
2965
+ });
2966
+ }
2967
+
2968
+ // packages/cli/dist/ui-api-runs.js
2969
+ import { readFile as readFile5 } from "node:fs/promises";
2970
+ import { join as join7 } from "node:path";
2971
+ var import_yaml4 = __toESM(require_dist(), 1);
2972
+ async function serverModel(root) {
2973
+ return (await configuredModel(root))?.type ?? null;
2974
+ }
2975
+ async function configuredModel(root) {
2976
+ const text2 = await readFile5(join7(orgPackDirOf(root), "runtime.yaml"), "utf8").catch(() => void 0);
2977
+ if (!text2)
2978
+ return null;
2979
+ let y;
2980
+ try {
2981
+ y = (0, import_yaml4.parse)(text2);
2982
+ } catch {
2983
+ return null;
2984
+ }
2985
+ const type = y?.adapters?.model?.type;
2986
+ if (typeof type !== "string" || type === "mock")
2987
+ return null;
2988
+ const allowed = y?.modes?.allowed;
2989
+ if (Array.isArray(allowed) && !allowed.includes("model"))
2990
+ return null;
2991
+ const max = y?.adapters?.model?.settings?.maxInput;
2992
+ return typeof max === "number" && Number.isInteger(max) && max >= 1e3 ? { type, maxInput: max } : { type };
2993
+ }
2994
+ function uiModelRunRoutes(r, ctx, deps = {}) {
2995
+ const root = ctx.root;
2996
+ const runStep = deps.runStep ?? (async (args) => {
2997
+ const { main } = await import("./chunk-P63VL4HE.js");
2998
+ const out = [];
2999
+ const err = [];
3000
+ const code = await main([...args, "--root", root], { out: (l) => out.push(l), err: (l) => err.push(l) }, { quiet: true });
3001
+ return { code, out, err };
3002
+ });
3003
+ return r.add("POST", "/api/runs", async ({ body }) => {
3004
+ const args = body?.args;
3005
+ if (!Array.isArray(args) || args.length < 2 || !args.every((a) => typeof a === "string" && a.trim() !== ""))
3006
+ throw badRequest(`args is the step's command words and arguments, for example ["scan", "survey", "forge-ledger"].`);
3007
+ const words = args;
3008
+ if (!isHandoffStep(words[0], words[1]))
3009
+ throw badRequest(`atticus ${words.slice(0, 2).join(" ")} isn't an AI step; the app starts only those.`);
3010
+ const option = words.find((a) => a.startsWith("-"));
3011
+ if (option)
3012
+ throw badRequest(`${option} would be read as an option; give the step's arguments only.`);
3013
+ const configured = await configuredModel(root);
3014
+ const model = configured?.type;
3015
+ if (!model)
3016
+ throw conflict("This server's runtime.yaml configures no model, so it can't run an AI step. Run it in your assistant (its slash command), or from VS Code with Copilot.");
3017
+ const job = ctx.jobs.start("step", async (c) => {
3018
+ c.progress(0, 1, `Running atticus ${words.join(" ")} with ${model}`);
3019
+ const before = new Set((await listRuns(root, { limit: 1e5 })).map((x) => x.runId));
3020
+ const res = await runStep([
3021
+ ...words,
3022
+ "--mode",
3023
+ "model",
3024
+ ...configured?.maxInput ? ["--max-tokens", String(configured.maxInput)] : []
3025
+ ]);
3026
+ const made = (await listRuns(root, { limit: 1e5 })).find((x) => !before.has(x.runId));
3027
+ c.progress(1, 1, res.code === 0 ? "Done" : "Stopped");
3028
+ ctx.events.publish({ type: "file.changed", path: runsRelOf(root) });
3029
+ return {
3030
+ result: {
3031
+ code: res.code,
3032
+ lines: [...res.out, ...res.err],
3033
+ ...made ? { runId: made.runId } : {}
3034
+ },
3035
+ errors: res.code === 0 ? [] : res.err
3036
+ };
3037
+ });
3038
+ return { status: 202, body: { jobId: job.id, model } };
3039
+ }).add("GET", "/api/runs", async ({ query }) => {
3040
+ const change = query.get("change") ?? void 0;
3041
+ const limitText = query.get("limit");
3042
+ const limit = limitText === null ? 200 : Number(limitText);
3043
+ if (!Number.isInteger(limit) || limit < 1)
3044
+ throw badRequest("limit is a whole number of 1 or more.");
3045
+ const runs = await listRuns(root, { ...change ? { change } : {}, limit });
3046
+ return {
3047
+ status: 200,
3048
+ body: { runs, serverModel: await serverModel(root) }
3049
+ };
3050
+ }).add("GET", "/api/runs/{runId}/transcript", async ({ params }) => {
3051
+ const t = await runTranscript(root, params.runId);
3052
+ if (!t)
3053
+ throw notFound(`There is no run ${params.runId}.`);
3054
+ return { status: 200, body: t };
3055
+ });
3056
+ }
3057
+
3058
+ // packages/cli/dist/ui-api-history.js
3059
+ function uiHistoryRoutes(r, ctx) {
3060
+ const root = ctx.root;
3061
+ return r.add("GET", "/api/history", async ({ query }) => {
3062
+ const limitText = query.get("limit");
3063
+ const limit = limitText === null ? 200 : Number(limitText);
3064
+ if (!Number.isInteger(limit) || limit < 1)
3065
+ throw badRequest("limit is a whole number of 1 or more.");
3066
+ const opt = (k) => {
3067
+ const v = query.get(k)?.trim();
3068
+ return v ? v : void 0;
3069
+ };
3070
+ const change = opt("change");
3071
+ const session = opt("session");
3072
+ const filter = opt("q");
3073
+ const body = await listHistory(root, {
3074
+ ...change ? { change } : {},
3075
+ ...session ? { session } : {},
3076
+ ...filter ? { filter } : {},
3077
+ limit
3078
+ });
3079
+ return { status: 200, body };
3080
+ }).add("GET", "/api/history/{historyId}", async ({ params }) => {
3081
+ const e = await historyEntry(root, params.historyId);
3082
+ if (!e)
3083
+ throw notFound(`There is no record ${params.historyId}.`);
3084
+ return { status: 200, body: e };
3085
+ });
3086
+ }
3087
+
3088
+ // packages/cli/dist/ui-api-ai.js
3089
+ var import_yaml5 = __toESM(require_dist(), 1);
3090
+ import { existsSync } from "node:fs";
3091
+ import { readFile as readFile6 } from "node:fs/promises";
3092
+ import { join as join8, relative, sep } from "node:path";
3093
+ var unprocessable = (detail) => new HttpError(422, "The settings aren't valid", detail);
3094
+ var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
3095
+ var FIELDS = ["ai", "modes", "assistants", "models", "budgets"];
3096
+ function uiAiRoutes(r, ctx, deps = {}) {
3097
+ const root = ctx.root;
3098
+ const env = deps.env ?? process.env;
3099
+ const keys = () => deps.keys ?? defaultKeyStore();
3100
+ const file = () => join8(orgPackDirOf(root), "runtime.yaml");
3101
+ const rel = () => relative(root, file()).split(sep).join("/");
3102
+ const build = async (op, type, settings) => {
3103
+ if (deps.provider)
3104
+ return deps.provider(type, settings);
3105
+ const make = await providerFactoryFor(op, type);
3106
+ return make?.(settings);
3107
+ };
3108
+ return r.add("GET", "/api/settings/ai", async () => {
3109
+ const op = await loadOrgPack(orgPackDirOf(root));
3110
+ const rt = op.runtime;
3111
+ const model = rt.adapters.model;
3112
+ const keyEnv = typeof model?.settings.keyEnv === "string" ? model.settings.keyEnv : void 0;
3113
+ return {
3114
+ status: 200,
3115
+ body: {
3116
+ // The assistant atticus probe measured here, when one was (ADR 0124 addendum A-probe).
3117
+ runs: whatRunsWhere(rt, { probes: (await readProbes(root)).results }),
3118
+ ai: rt.ai,
3119
+ modes: rt.modes,
3120
+ assistants: ASSISTANTS.map((id) => ({
3121
+ id,
3122
+ name: ASSISTANT_FORMATS[id].name,
3123
+ allowed: rt.assistants.allowed.includes(id),
3124
+ default: rt.assistants.default === id
3125
+ })),
3126
+ providers: [
3127
+ ...Object.entries(PROVIDER_SETTINGS).map(([type, settings]) => ({
3128
+ type,
3129
+ settings: [...settings]
3130
+ })),
3131
+ // The org pack's model-provider plugins, with what each takes (ADR 0121 item 4).
3132
+ ...op.modelProviders.map(({ manifest: m }) => ({
3133
+ type: m.id,
3134
+ settings: [
3135
+ ...COMMON_SETTINGS,
3136
+ ...m.auth === "none" ? [] : ["keyEnv"],
3137
+ ...m.settings
3138
+ ],
3139
+ plugin: true,
3140
+ pinned: !!m.sha256
3141
+ }))
3142
+ ],
3143
+ model: model ? { type: model.type, settings: model.settings } : null,
3144
+ // Whether the key's variable is set here, by name: its value is never read out.
3145
+ keySet: keyEnv ? !!env[keyEnv]?.trim() || !!keys().get(keyEnv) : false,
3146
+ models: rt.models,
3147
+ budgets: rt.budgets,
3148
+ path: rel()
3149
+ }
3150
+ };
3151
+ }).add("PUT", "/api/settings/ai", async ({ body }) => {
3152
+ const b = body ?? {};
3153
+ const text2 = existsSync(file()) ? await readFile6(file(), "utf8") : void 0;
3154
+ const raw = text2 !== void 0 ? (0, import_yaml5.parse)(text2) ?? {} : structuredClone(DEFAULT_RUNTIME);
3155
+ for (const f of FIELDS)
3156
+ if (b[f] !== void 0)
3157
+ raw[f] = b[f];
3158
+ if (b.model !== void 0) {
3159
+ const adapters = { ...raw.adapters ?? {} };
3160
+ if (b.model === null)
3161
+ delete adapters.model;
3162
+ else {
3163
+ const m = b.model;
3164
+ const settings = Object.fromEntries(Object.entries(m.settings ?? {}).filter(([, v]) => v !== "" && v !== void 0));
3165
+ for (const [name, v] of Object.entries(settings))
3166
+ for (const one of Array.isArray(v) ? v : [v])
3167
+ if (typeof one === "string" && looksLikeKey(one))
3168
+ throw unprocessable(`${name} looks like a key. A key never goes in runtime.yaml: keep it in an environment variable (or the OS's credential store) and give that variable's name as keyEnv.`);
3169
+ if (settings.keyEnv !== void 0 && !ENV_NAME.test(String(settings.keyEnv)))
3170
+ throw unprocessable(`keyEnv is the name of an environment variable, such as ATTICUS_MODEL_KEY.`);
3171
+ adapters.model = { type: m.type, settings };
3172
+ }
3173
+ raw.adapters = adapters;
3174
+ }
3175
+ const checked = Runtime.safeParse(raw);
3176
+ if (!checked.success)
3177
+ throw unprocessable(checked.error.issues.map((i) => `${i.path.join(".") || "runtime.yaml"}: ${i.message}`).join("; "));
3178
+ const header = (text2 ?? "").split(/\r?\n/).filter((l, i, a) => a.slice(0, i + 1).every((x) => x.startsWith("#")));
3179
+ await writeFileAtomic(file(), `${header.length ? `${header.join("\n")}
3180
+ ` : ""}${(0, import_yaml5.stringify)(raw, { lineWidth: 0 })}`);
3181
+ return {
3182
+ status: 200,
3183
+ body: {
3184
+ ok: true,
3185
+ path: rel(),
3186
+ note: `Saved ${rel()}. Commit it in a pull request: the org pack is reviewed like code.`
3187
+ }
3188
+ };
3189
+ }).add("POST", "/api/settings/ai/key", async ({ body }) => {
3190
+ const b = body ?? {};
3191
+ const name = typeof b.name === "string" ? b.name.trim() : "";
3192
+ const value = typeof b.value === "string" ? b.value.trim() : "";
3193
+ if (!ENV_NAME.test(name))
3194
+ throw unprocessable("The key's name is keyEnv's: a variable's name, such as ATTICUS_MODEL_KEY.");
3195
+ if (!value)
3196
+ throw unprocessable("No key was given.");
3197
+ keys().set(name, value);
3198
+ return { status: 200, body: { ok: true, name } };
3199
+ }).add("POST", "/api/settings/ai/test", async () => {
3200
+ const op = await loadOrgPack(orgPackDirOf(root));
3201
+ const model = op.runtime.adapters.model;
3202
+ if (!model || model.type === "mock")
3203
+ throw conflict("No model provider is set in runtime.yaml: choose one first.");
3204
+ const started = Date.now();
3205
+ try {
3206
+ const p = await build(op, model.type, model.settings);
3207
+ if (!p)
3208
+ return {
3209
+ status: 200,
3210
+ body: { ok: false, error: `No ${model.type} provider is installed here.` }
3211
+ };
3212
+ await p.complete({
3213
+ system: "You are checking that a connection works.",
3214
+ prompt: "Reply with the word OK.",
3215
+ maxOutputTokens: 5
3216
+ });
3217
+ return { status: 200, body: { ok: true, model: p.model, ms: Date.now() - started } };
3218
+ } catch (e) {
3219
+ return {
3220
+ status: 200,
3221
+ body: { ok: false, error: e instanceof Error ? e.message : String(e) }
3222
+ };
3223
+ }
3224
+ });
3225
+ }
3226
+
3227
+ // packages/cli/dist/ui-api-documents.js
3228
+ var MAX_FILE = 15 * 1024 * 1024;
3229
+ function uiDocumentRoutes(r, ctx) {
3230
+ const root = ctx.root;
3231
+ const changed = (path) => ctx.events.publish({ type: "file.changed", path });
3232
+ return r.add("GET", "/api/knowledge/documents", async () => {
3233
+ const { listDocuments } = await import("./chunk-HJDICQA4.js");
3234
+ return { status: 200, body: api_exports.DocumentList.parse(await listDocuments(root)) };
3235
+ }).add("POST", "/api/knowledge/documents", async ({ body }) => {
3236
+ const b = api_exports.AttachFileRequest.safeParse(body ?? {});
3237
+ if (!b.success)
3238
+ throw badRequest("Send the file's name and its bytes in base64.");
3239
+ const bytes = new Uint8Array(Buffer.from(b.data.content, "base64"));
3240
+ if (bytes.length > MAX_FILE)
3241
+ throw new HttpError(413, "The file is too large", "Add a file of 15 MB or less.");
3242
+ const { addToInbox } = await import("./chunk-WZ2BXIGG.js");
3243
+ const res = await addToInbox(root, [{ name: b.data.name, bytes }]);
3244
+ if (!res.added.length)
3245
+ throw new HttpError(422, "Atticus can't add that", res.refused[0]?.why ?? "", {
3246
+ lines: res.refused.map((x) => `${x.name}: ${x.why}`)
3247
+ });
3248
+ for (const a of res.added)
3249
+ changed(a.path);
3250
+ return { status: 201, body: api_exports.AddDocumentResult.parse(res) };
3251
+ }, { maxBody: Math.ceil(MAX_FILE * 4 / 3) + 64 * 1024 }).add("POST", "/api/changes/{changeId}/inputs/{name}/promote", async ({ params }) => {
3252
+ const { AttachError, promoteChangeInput } = await import("./chunk-BYZCY6SP.js");
3253
+ try {
3254
+ const res = await promoteChangeInput(root, params.changeId, params.name);
3255
+ changed(res.path);
3256
+ return { status: 200, body: api_exports.PromoteResult.parse(res) };
3257
+ } catch (e) {
3258
+ if (e instanceof AttachError)
3259
+ throw e.code === "not-found" ? notFound(e.message) : new HttpError(422, "Atticus can't copy that", e.message);
3260
+ throw e;
3261
+ }
3262
+ });
3263
+ }
3264
+
3265
+ // packages/cli/dist/ui-api-repos.js
3266
+ function uiRepoRoutes(r, ctx) {
3267
+ const root = ctx.root;
3268
+ return r.add("GET", "/api/repos/branches", async () => {
3269
+ const { repoBranches } = await import("./chunk-NHFQMOVK.js");
3270
+ return { status: 200, body: api_exports.RepoBranchesList.parse(await repoBranches(root)) };
3271
+ }).add("GET", "/api/repos/compare", async ({ query }) => {
3272
+ const branch = query.get("branch")?.trim() ?? "";
3273
+ if (!branch)
3274
+ throw badRequest("Say which branch: ?branch=<name>.");
3275
+ const { compareBranch } = await import("./chunk-NHFQMOVK.js");
3276
+ const c = await compareBranch(root, branch);
3277
+ if (!c)
3278
+ throw notFound(`${branch} hasn't been scanned beside the baseline yet.`);
3279
+ return { status: 200, body: api_exports.BranchCompare.parse(c) };
3280
+ }).add("PUT", "/api/repos/{repoId}/branches", async ({ params, body }) => {
3281
+ const b = api_exports.EditRepoBranchesRequest.safeParse(body ?? {});
3282
+ if (!b.success)
3283
+ throw badRequest("Send defaultBranch and branches as branch names.");
3284
+ const { editRepoBranches, RepoEditError } = await import("./chunk-NHFQMOVK.js");
3285
+ try {
3286
+ const file = await editRepoBranches(root, params.repoId, b.data);
3287
+ ctx.events.publish({ type: "file.changed", path: file });
3288
+ return {
3289
+ status: 200,
3290
+ body: {
3291
+ file,
3292
+ note: `${file} changed: commit it in a pull request. Run atticus workspace sync if the default branch changed.`
3293
+ }
3294
+ };
3295
+ } catch (e) {
3296
+ if (e instanceof RepoEditError)
3297
+ throw e.code === "not-found" ? notFound(e.message) : badRequest(e.message);
3298
+ throw e;
3299
+ }
3300
+ }).add("PUT", "/api/repos/{repoId}/description", async ({ params, body }) => {
3301
+ const b = api_exports.EditRepoDescriptionRequest.safeParse(body ?? {});
3302
+ if (!b.success)
3303
+ throw badRequest("Send what the repo is as text, or accept: true to confirm the draft, or reject: true to drop it.");
3304
+ const { editRepoDescription, RepoEditError } = await import("./chunk-NHFQMOVK.js");
3305
+ try {
3306
+ const r2 = await editRepoDescription(root, params.repoId, b.data);
3307
+ ctx.events.publish({ type: "file.changed", path: r2.file });
3308
+ return {
3309
+ status: 200,
3310
+ body: {
3311
+ file: r2.file,
3312
+ ...r2.description ? { description: r2.description } : {},
3313
+ note: r2.description ? `${r2.file} changed: commit it in a pull request. Every reading prompt, question and view of ${params.repoId} now says what it is.` : `${r2.file} changed: commit it in a pull request. Nothing says what ${params.repoId} is now.`
3314
+ }
3315
+ };
3316
+ } catch (e) {
3317
+ if (e instanceof RepoEditError)
3318
+ throw e.code === "not-found" ? notFound(e.message) : badRequest(e.message);
3319
+ throw e;
3320
+ }
3321
+ });
3322
+ }
3323
+
3324
+ // packages/cli/dist/ui-api-cockpit.js
3325
+ function uiCockpitRoutes(r, ctx) {
3326
+ return r.add("GET", "/api/changes/{changeId}/cockpit", async ({ params }) => {
3327
+ const { cockpit } = await import("./chunk-2C5B464C.js");
3328
+ try {
3329
+ return {
3330
+ status: 200,
3331
+ body: api_exports.ChangeCockpit.parse(await cockpit(ctx.root, params.changeId))
3332
+ };
3333
+ } catch (e) {
3334
+ if (e instanceof Error && e.name === "ChangeFileError")
3335
+ throw notFound(`There is no change ${params.changeId}.`);
3336
+ throw e;
3337
+ }
3338
+ }).add("GET", "/api/past-changes", async ({ query }) => {
3339
+ const { changeAboutText, similarChanges } = await import("./chunk-HVIMMDVB.js");
3340
+ const change = query.get("change");
3341
+ const text2 = change ? await changeAboutText(ctx.root, change) : query.get("text")?.trim();
3342
+ if (change && !text2)
3343
+ throw notFound(`There is no change ${change}.`);
3344
+ if (!text2)
3345
+ throw badRequest("Say what the new change is about: ?text=<its title or brief>, or ?change=<a change's id>.");
3346
+ const limit = Number(query.get("limit") ?? 5);
3347
+ return {
3348
+ status: 200,
3349
+ body: api_exports.SimilarChanges.parse(await similarChanges(ctx.root, text2.slice(0, 2e4), {
3350
+ limit: Number.isInteger(limit) && limit > 0 && limit <= 20 ? limit : 5
3351
+ }))
3352
+ };
3353
+ }).add("POST", "/api/changes/{changeId}/like", async ({ params, body }) => {
3354
+ const b = api_exports.AttachPastChangeRequest.safeParse(body ?? {});
3355
+ if (!b.success)
3356
+ throw badRequest("Say which archived change: { like: <its folder or id> }.");
3357
+ const { attachPastChange, findPastChange } = await import("./chunk-HVIMMDVB.js");
3358
+ const folder = await findPastChange(ctx.root, b.data.like);
3359
+ if (!folder)
3360
+ throw notFound(`There is no archived change ${b.data.like}.`);
3361
+ const { AttachError } = await import("./chunk-BYZCY6SP.js");
3362
+ try {
3363
+ const name = await attachPastChange(ctx.root, params.changeId, folder, ctx.user);
3364
+ ctx.events.publish({ type: "file.changed", path: `inputs/${name}` });
3365
+ return {
3366
+ status: 201,
3367
+ body: api_exports.AttachPastChangeResult.parse({ like: folder, file: `inputs/${name}` })
3368
+ };
3369
+ } catch (e) {
3370
+ if (e instanceof AttachError)
3371
+ throw e.code === "not-found" ? notFound(e.message) : new HttpError(422, "Atticus can't attach that", e.message);
3372
+ throw e;
3373
+ }
3374
+ });
3375
+ }
3376
+
3377
+ // packages/cli/dist/ui-api-trust.js
3378
+ var import_yaml6 = __toESM(require_dist(), 1);
3379
+ import { readdir as readdir3, readFile as readFile7, stat as stat4 } from "node:fs/promises";
3380
+ import { join as join9, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
3381
+ var SHA = /^[0-9a-f]{7,64}$/;
3382
+ var FULL_SHA = /^[0-9a-f]{40,64}$/;
3383
+ var FACT_BY = /* @__PURE__ */ new Set(["assistant", "reader", "person"]);
3384
+ var UNKNOWN_SHOWN = 50;
3385
+ var firstLine = (e) => String(e.message ?? e).split("\n")[0];
3386
+ var text = (p) => readFile7(p, "utf8").catch(() => void 0);
3387
+ async function headFromFiles(dir) {
3388
+ let git = join9(dir, ".git");
3389
+ const st = await stat4(git).catch(() => void 0);
3390
+ if (!st)
3391
+ return void 0;
3392
+ if (st.isFile()) {
3393
+ const m = /^gitdir:\s*(.+)$/m.exec(await text(git) ?? "");
3394
+ if (!m)
3395
+ return void 0;
3396
+ git = resolve2(dir, m[1].trim());
3397
+ }
3398
+ const common = (await text(join9(git, "commondir")))?.trim();
3399
+ const commonDir = common ? resolve2(git, common) : git;
3400
+ const head = (await text(join9(git, "HEAD")) ?? "").trim();
3401
+ if (FULL_SHA.test(head))
3402
+ return head;
3403
+ const ref = /^ref:\s*(refs\/[A-Za-z0-9._/-]+)$/.exec(head)?.[1];
3404
+ if (!ref || ref.split("/").includes(".."))
3405
+ return void 0;
3406
+ for (const d of [git, commonDir]) {
3407
+ const loose = (await text(join9(d, ...ref.split("/"))) ?? "").trim();
3408
+ if (FULL_SHA.test(loose))
3409
+ return loose;
3410
+ }
3411
+ for (const line of (await text(join9(commonDir, "packed-refs")) ?? "").split(/\r?\n/)) {
3412
+ const [sha2, name] = line.split(" ");
3413
+ if (name === ref && sha2 && FULL_SHA.test(sha2))
3414
+ return sha2;
3415
+ }
3416
+ return void 0;
3417
+ }
3418
+ function reportOf(raw) {
3419
+ const r = raw;
3420
+ if (!r || typeof r.accepted !== "number" || typeof r.rejected !== "number")
3421
+ return "not a verifier's report: accepted and rejected are missing";
3422
+ const list = (v) => Array.isArray(v) ? v.map(String) : [];
3423
+ const unknown = list(r.coverage?.unknown);
3424
+ return {
3425
+ accepted: r.accepted,
3426
+ rejected: r.rejected,
3427
+ redacted: typeof r.redacted === "number" ? r.redacted : 0,
3428
+ coverage: {
3429
+ ratio: typeof r.coverage?.ratio === "number" ? r.coverage.ratio : 0,
3430
+ read: typeof r.coverage?.read === "number" ? r.coverage.read : 0,
3431
+ unknown: unknown.length,
3432
+ excluded: list(r.coverage?.excluded).length
3433
+ },
3434
+ unknownFiles: unknown.slice(0, UNKNOWN_SHOWN),
3435
+ ...typeof r.commit === "string" && SHA.test(r.commit) ? { commit: r.commit } : {}
3436
+ };
3437
+ }
3438
+ var sameCommit = (a, b) => a && b ? a.startsWith(b) || b.startsWith(a) : null;
3439
+ function nextOf(repo, r) {
3440
+ const read = { command: `atticus map ${repo}`, slash: `/atticus-map ${repo}` };
3441
+ if (!r.facts.present)
3442
+ return { state: "not-read", next: read };
3443
+ const report = r.verify.report;
3444
+ if (!report)
3445
+ return { state: "unverified", next: { command: `atticus verify ${repo}` } };
3446
+ if (report.rejected > 0)
3447
+ return {
3448
+ state: "rejected",
3449
+ next: { command: `atticus verify ${repo}`, slash: `/atticus-map ${repo}` }
3450
+ };
3451
+ if (r.sameCommit === false)
3452
+ return { state: "stale", next: read };
3453
+ if (r.facts.unverified > 0)
3454
+ return { state: "unverified", next: { command: `atticus verify ${repo}` } };
3455
+ if (r.catalog.fromFacts === 0)
3456
+ return { state: "unwritten", next: { command: `atticus verify ${repo} --write` } };
3457
+ if (report.coverage.unknown > 0)
3458
+ return { state: "unknown-files", next: read };
3459
+ return { state: "trusted" };
3460
+ }
3461
+ async function trustView(root) {
3462
+ const rel = (p) => relative2(root, p).split(sep2).join("/") || ".";
3463
+ const problems = [];
3464
+ const dir = factsDirOf(root);
3465
+ const factsRel = factsRelOf(root);
3466
+ const ids = [];
3467
+ try {
3468
+ for (const r of await loadRepos(root))
3469
+ ids.push(r.id);
3470
+ } catch (e) {
3471
+ problems.push({ file: "org-pack/profiles", message: firstLine(e) });
3472
+ }
3473
+ const files = await readdir3(dir).catch(() => []);
3474
+ const named = /* @__PURE__ */ new Set();
3475
+ for (const f of files) {
3476
+ const of = factsFileOf(f);
3477
+ if (of && (of.facts || f.endsWith(".verify.yaml")))
3478
+ named.add(of.repo);
3479
+ }
3480
+ for (const id of [...named].sort())
3481
+ if (!ids.includes(id))
3482
+ ids.push(id);
3483
+ const catalog = await readCatalog(join9(catalogDirOf(root), "generated"), { cacheRoot: root });
3484
+ for (const p of catalog.problems)
3485
+ problems.push({ file: rel(join9(catalogDirOf(root), "generated", p.file)), message: p.message });
3486
+ const counts = /* @__PURE__ */ new Map();
3487
+ const count = (r) => {
3488
+ const seen = /* @__PURE__ */ new Map();
3489
+ for (const e of r.evidence) {
3490
+ if (!e.repo)
3491
+ continue;
3492
+ const s = seen.get(e.repo) ?? { facts: false, readers: false };
3493
+ if (!e.extractor && e.by && FACT_BY.has(e.by))
3494
+ s.facts = true;
3495
+ else if (e.source !== "manual")
3496
+ s.readers = true;
3497
+ seen.set(e.repo, s);
3498
+ }
3499
+ for (const [repo, s] of seen) {
3500
+ const c = counts.get(repo) ?? { facts: 0, readers: 0 };
3501
+ if (s.facts)
3502
+ c.facts += 1;
3503
+ else if (s.readers)
3504
+ c.readers += 1;
3505
+ counts.set(repo, c);
3506
+ }
3507
+ };
3508
+ for (const r of catalog.records)
3509
+ count(r);
3510
+ const adopted = isAdopted(root);
3511
+ const repos = [];
3512
+ for (const repo of ids) {
3513
+ const factsPath = join9(dir, `${repo}.jsonl`);
3514
+ const raw = await text(factsPath);
3515
+ const facts = {
3516
+ path: rel(factsPath),
3517
+ present: raw !== void 0,
3518
+ count: 0,
3519
+ unverified: 0
3520
+ };
3521
+ let frameworks;
3522
+ if (raw !== void 0 && raw.trim()) {
3523
+ const parsed = parseFacts(raw);
3524
+ if (parsed.ok) {
3525
+ facts.count = parsed.facts.length;
3526
+ facts.unverified = parsed.facts.filter((f) => f.verified === "none").length;
3527
+ const commit = parsed.facts[0]?.commit;
3528
+ if (commit)
3529
+ facts.commit = commit;
3530
+ const gaps = new Map(frameworkGaps(parsed.facts).map((g) => [g.id, g.question]));
3531
+ const built = frameworkFacts(parsed.facts).map((f) => ({
3532
+ name: f.name,
3533
+ ...f.version ? { version: f.version } : {},
3534
+ ...f.component ? { component: f.component } : {},
3535
+ cite: `${f.cite.file}:${f.cite.line}`,
3536
+ ...gaps.has(f.id) ? { gap: gaps.get(f.id) } : {}
3537
+ }));
3538
+ if (built.length)
3539
+ frameworks = built;
3540
+ } else
3541
+ problems.push({
3542
+ file: rel(factsPath),
3543
+ message: `doesn't read as facts: ${parsed.issues[0]?.message ?? "invalid"}`
3544
+ });
3545
+ }
3546
+ const reportPath = join9(dir, `${repo}.verify.yaml`);
3547
+ let report = null;
3548
+ const reportText = await text(reportPath);
3549
+ if (reportText !== void 0) {
3550
+ try {
3551
+ const r = reportOf((0, import_yaml6.parse)(reportText));
3552
+ if (typeof r === "string")
3553
+ problems.push({ file: rel(reportPath), message: r });
3554
+ else
3555
+ report = r;
3556
+ } catch (e) {
3557
+ problems.push({ file: rel(reportPath), message: `can't read YAML: ${firstLine(e)}` });
3558
+ }
3559
+ }
3560
+ if (!facts.commit && report?.commit)
3561
+ facts.commit = report.commit;
3562
+ let checkoutDir;
3563
+ try {
3564
+ checkoutDir = adopted ? root : baseClonePath(root, repo);
3565
+ } catch {
3566
+ checkoutDir = void 0;
3567
+ }
3568
+ const head = checkoutDir ? await headFromFiles(checkoutDir) : void 0;
3569
+ const present = checkoutDir ? await stat4(join9(checkoutDir, ".git")).then(() => true, () => false) : false;
3570
+ const checkout = {
3571
+ path: checkoutDir ? rel(checkoutDir) : `workspace/${repo}`,
3572
+ present,
3573
+ ...head ? { commit: head } : {}
3574
+ };
3575
+ const c = counts.get(repo) ?? { facts: 0, readers: 0 };
3576
+ const from = c.facts && c.readers ? "both" : c.facts ? "facts" : c.readers ? "readers" : "none";
3577
+ const base = {
3578
+ repo,
3579
+ facts,
3580
+ verify: { path: rel(reportPath), report },
3581
+ checkout,
3582
+ sameCommit: sameCommit(report?.commit ?? facts.commit, head),
3583
+ catalog: { from, fromFacts: c.facts, fromReaders: c.readers }
3584
+ };
3585
+ const { state, next } = nextOf(repo, base);
3586
+ repos.push({
3587
+ ...base,
3588
+ state,
3589
+ ...next ? { next } : {},
3590
+ ...frameworks ? { frameworks } : {}
3591
+ });
3592
+ }
3593
+ return { factsDir: factsRel, repos, problems };
3594
+ }
3595
+ function uiTrustRoutes(r, ctx) {
3596
+ return r.add("GET", "/api/trust", async () => ({ status: 200, body: await trustView(ctx.root) }));
3597
+ }
3598
+
3599
+ // packages/cli/dist/ui-api-facts.js
3600
+ import { appendFile, mkdir, readFile as readFile8 } from "node:fs/promises";
3601
+ import { join as join10 } from "node:path";
3602
+ var REPO = /^[a-z0-9][a-z0-9._-]{0,99}$/i;
3603
+ var fixed = new Set(FACT_FIXED_FIELDS);
3604
+ function factLabel(f) {
3605
+ switch (f.kind) {
3606
+ case "entry":
3607
+ return `${f.method ? `${f.method} ` : ""}${f.path}`;
3608
+ case "call":
3609
+ return `${f.method ? `${f.method} ` : ""}${f.path}${f.to ? ` \u2192 ${f.to}` : ""}`;
3610
+ case "store":
3611
+ return `${f.name} (${f.op})`;
3612
+ case "field":
3613
+ return `${f.name}${f.type ? `: ${f.type}` : ""}`;
3614
+ case "deploy":
3615
+ return `${f.platform}${f.resourceName ? ` ${f.resourceName}` : ""}`;
3616
+ case "config":
3617
+ return `${f.key}${f.value !== void 0 ? ` = ${f.value}` : ""}`;
3618
+ // ADR 0124 addendum A-framework: what the repo or the module is built on.
3619
+ case "framework":
3620
+ return `${f.name}${f.version ? ` ${f.version}` : ""}`;
3621
+ case "flow":
3622
+ return f.name;
3623
+ }
3624
+ }
3625
+ function claimOf(f) {
3626
+ const out = {};
3627
+ for (const [k, v] of Object.entries(f))
3628
+ if (!fixed.has(k) && typeof v === "string")
3629
+ out[k] = v;
3630
+ out.value = f.cite.value;
3631
+ return out;
3632
+ }
3633
+ var spot = (c) => ({
3634
+ file: c.file,
3635
+ line: c.line,
3636
+ value: c.value,
3637
+ ...c.quote ? { quote: c.quote } : {}
3638
+ });
3639
+ async function readFacts(root, repo) {
3640
+ const file = `${factsRelOf(root)}/${repo}.jsonl`;
3641
+ const text2 = await readFile8(join10(factsDirOf(root), `${repo}.jsonl`), "utf8").catch(() => void 0);
3642
+ if (text2 === void 0)
3643
+ return { file, present: false, facts: [], bad: [] };
3644
+ const p = parseFactsLines(text2);
3645
+ return {
3646
+ file,
3647
+ present: true,
3648
+ facts: p.facts,
3649
+ bad: p.bad.map((b) => ({
3650
+ file,
3651
+ message: `line ${b.line} isn't a fact: ${b.issues.map((i) => i.message).join("; ")}`
3652
+ }))
3653
+ };
3654
+ }
3655
+ async function repoFactsView(root, repo) {
3656
+ const f = await readFacts(root, repo);
3657
+ const cFile = `${factsRelOf(root)}/${repo}.corrections.jsonl`;
3658
+ const c = await readCorrections(root, repo);
3659
+ const problems = [
3660
+ ...f.bad,
3661
+ ...c.bad.map((b) => ({
3662
+ file: cFile,
3663
+ message: `line ${b.line} isn't a correction: ${b.message}`
3664
+ }))
3665
+ ];
3666
+ const facts = f.facts.map((x) => {
3667
+ const corr = correctionFor(x, c.corrections);
3668
+ return {
3669
+ id: x.id,
3670
+ kind: x.kind,
3671
+ label: factLabel(x),
3672
+ claim: claimOf(x),
3673
+ cite: spot(x.cite),
3674
+ ...x.kind === "flow" ? {
3675
+ steps: [
3676
+ ...x.steps.map((s) => ({ ...spot(s.cite), what: s.what })),
3677
+ ...x.errors.map((e) => ({ ...spot(e.cite), what: `${e.when} \u2192 ${e.then}` }))
3678
+ ]
3679
+ } : {},
3680
+ verified: x.verified,
3681
+ by: x.by,
3682
+ ...corr ? { correction: corr } : {}
3683
+ };
3684
+ });
3685
+ const stale = applyCorrections(f.facts, c.corrections).stale.filter((s) => s.why === "none").map((s) => s.correction.fact);
3686
+ const questions = await listQuestions(root, { repo, state: "open" }).then((l) => l.questions.map((q) => ({ id: q.id, question: q.question, evidence: q.evidence })), () => []);
3687
+ return {
3688
+ repo,
3689
+ file: f.file,
3690
+ present: f.present,
3691
+ ...f.facts[0] ? { commit: f.facts[0].commit } : {},
3692
+ facts,
3693
+ corrections: { file: cFile, count: c.corrections.length, stale: [...new Set(stale)].sort() },
3694
+ questions,
3695
+ verify: `atticus verify ${repo}`,
3696
+ problems
3697
+ };
3698
+ }
3699
+ var repoOf = (v) => {
3700
+ if (!REPO.test(v))
3701
+ throw badRequest(`${JSON.stringify(v)} isn't a repo name.`);
3702
+ return v;
3703
+ };
3704
+ function uiFactsRoutes(r, ctx, deps = {}) {
3705
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
3706
+ return r.add("GET", "/api/facts/{repo}", async ({ params }) => ({
3707
+ status: 200,
3708
+ body: await repoFactsView(ctx.root, repoOf(params.repo))
3709
+ })).add("POST", "/api/facts/{repo}/corrections", async ({ params, body }) => {
3710
+ const repo = repoOf(params.repo);
3711
+ const b = api_exports.FactCorrectionRequest.safeParse(body ?? {});
3712
+ if (!b.success)
3713
+ throw badRequest("Say which fact and what is wrong: { fact, action, set, line, reason }.", {
3714
+ errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
3715
+ });
3716
+ const f = await readFacts(ctx.root, repo);
3717
+ if (!f.present)
3718
+ throw notFound(`There are no facts for ${repo} at ${f.file}.`);
3719
+ const fact = f.facts.find((x) => x.id === b.data.fact);
3720
+ if (!fact)
3721
+ throw notFound(`${f.file} has no fact ${b.data.fact}.`);
3722
+ const was = claimOf(fact);
3723
+ const set = Object.fromEntries(Object.entries(b.data.set ?? {}).map(([k, v]) => [k, v.trim()]).filter(([k, v]) => was[k] !== v));
3724
+ const line = b.data.line !== void 0 && b.data.line !== fact.cite.line ? b.data.line : void 0;
3725
+ const c = FactCorrection.safeParse({
3726
+ schemaVersion: 1,
3727
+ fact: fact.id,
3728
+ target: {
3729
+ kind: fact.kind,
3730
+ file: fact.cite.file,
3731
+ line: fact.cite.line,
3732
+ value: fact.cite.value
3733
+ },
3734
+ action: b.data.action,
3735
+ ...b.data.action === "edit" && Object.keys(set).length ? { set } : {},
3736
+ ...b.data.action === "edit" && line !== void 0 ? { line } : {},
3737
+ ...b.data.reason ? { reason: b.data.reason } : {},
3738
+ by: ctx.user,
3739
+ at: now().toISOString()
3740
+ });
3741
+ if (!c.success)
3742
+ throw new HttpError(422, "Atticus can't record that", c.error.issues.map((i) => i.message).join("; "));
3743
+ if (c.data.action === "edit") {
3744
+ const fixedFact = correctedFact(fact, c.data);
3745
+ if (typeof fixedFact === "string")
3746
+ throw new HttpError(422, "That isn't a fact", `The corrected ${fact.kind} isn't one: ${fixedFact}.`);
3747
+ }
3748
+ const rel = `${factsRelOf(ctx.root)}/${repo}.corrections.jsonl`;
3749
+ await mkdir(factsDirOf(ctx.root), { recursive: true });
3750
+ await appendFile(join10(factsDirOf(ctx.root), `${repo}.corrections.jsonl`), `${correctionLine(c.data)}
3751
+ `, "utf8");
3752
+ ctx.events.publish({ type: "file.changed", path: rel });
3753
+ const result = {
3754
+ correction: c.data,
3755
+ file: rel,
3756
+ commit: `git add ${rel} && git commit -m "${repo}: a person's correction of ${fact.id}"`,
3757
+ verify: `atticus verify ${repo}`
3758
+ };
3759
+ return { status: 200, body: result };
3760
+ });
3761
+ }
3762
+
3763
+ // packages/cli/dist/ui-api-stages.js
3764
+ import { stat as stat5 } from "node:fs/promises";
3765
+ import { join as join11 } from "node:path";
3766
+ var ID2 = /^[a-z0-9][a-z0-9._-]{0,99}$/;
3767
+ function findingOptions(f) {
3768
+ const open = f.state === "open";
3769
+ const issue = f.kind === "issue";
3770
+ return {
3771
+ fix: f.fix.text,
3772
+ apply: open && issue,
3773
+ dismiss: open && issue,
3774
+ waive: open && issue && f.severity === "blocking" && f.waivable,
3775
+ ...open && f.question ? { ask: { text: f.question.text, owner: f.question.owner } } : {}
3776
+ };
3777
+ }
3778
+ var exists2 = (p) => stat5(p).then(() => true, () => false);
3779
+ var slashOf = (stage, change) => `/atticus-${stage} ${change}`;
3780
+ async function gateOf(root, change, stage) {
3781
+ const problems = [];
3782
+ let gate;
3783
+ try {
3784
+ gate = stageGate(await loadChangeContext(root), stage);
3785
+ } catch (e) {
3786
+ problems.push({
3787
+ file: "org-pack",
3788
+ message: `The org pack doesn't load, so the stage's gate isn't known: ${String(e.message ?? e).split("\n")[0]}`
3789
+ });
3790
+ }
3791
+ if (!gate && (await reviewedGates(root, change)).includes(STAGE_PHASE[stage]))
3792
+ gate = STAGE_PHASE[stage];
3793
+ return { gate, problems };
3794
+ }
3795
+ async function latest(root, change, gate) {
3796
+ if (!gate)
3797
+ return { round: null, findings: [], bad: [] };
3798
+ const rounds = await roundsOf(root, change, gate);
3799
+ const round = rounds.at(-1);
3800
+ if (round === void 0)
3801
+ return { round: null, findings: [], bad: [] };
3802
+ const r = await readRound(root, change, gate, round);
3803
+ return { round, findings: r.findings, bad: r.bad };
3804
+ }
3805
+ var view = (c) => ({
3806
+ question: c.question,
3807
+ text: c.text,
3808
+ by: c.by,
3809
+ at: c.at,
3810
+ read: c.read
3811
+ });
3812
+ async function stageReviewView(root, change, stage) {
3813
+ const rel = `${changesRelOf(root)}/${change}`;
3814
+ const { gate, problems } = await gateOf(root, change, stage);
3815
+ const l = await latest(root, change, gate);
3816
+ for (const b of l.bad)
3817
+ problems.push({ file: `${rel}/${b.path}`, message: b.message });
3818
+ const comments = await stageComments(root, change, stage);
3819
+ const { loop, present, file } = await readStageLoop(changeFolder(root, change), change, stage);
3820
+ const order = { blocking: 0, gap: 1, risk: 2, idea: 3 };
3821
+ const proposals = gate && l.round !== null ? await readProposals(root, change, gate, l.round) : [];
3822
+ const findings = l.findings.map((s) => {
3823
+ const fileRel = `${rel}/${s.path}`;
3824
+ const p = s.finding.state === "open" ? proposals.find((x) => x.finding === s.finding.id) : void 0;
3825
+ return {
3826
+ finding: s.finding,
3827
+ file: fileRel,
3828
+ options: findingOptions(s.finding),
3829
+ comments: comments.comments.filter((c) => c.about === fileRel).map(view),
3830
+ ...p ? {
3831
+ proposal: {
3832
+ claim: p.claim,
3833
+ by: p.by,
3834
+ at: p.at,
3835
+ ...p.reason ? { reason: p.reason } : {}
3836
+ }
3837
+ } : {}
3838
+ };
3839
+ }).sort((a, b) => Number(a.finding.state !== "open") - Number(b.finding.state !== "open") || order[a.finding.severity] - order[b.finding.severity] || a.finding.id.localeCompare(b.finding.id));
3840
+ const questions = [];
3841
+ for (const id of comments.others) {
3842
+ const q = await readQuestion(root, id).catch((e) => {
3843
+ problems.push({ file: `questions/${id}.yaml`, message: String(e.message ?? e) });
3844
+ return void 0;
3845
+ });
3846
+ if (q && q.state !== "retired")
3847
+ questions.push(q);
3848
+ }
3849
+ questions.sort((a, b) => Number(a.state !== "open") - Number(b.state !== "open"));
3850
+ return {
3851
+ change,
3852
+ stage,
3853
+ gate: gate ?? null,
3854
+ round: l.round,
3855
+ loop: {
3856
+ file: `${rel}/${file}`,
3857
+ present,
3858
+ ...present ? { state: loop.state, iteration: loop.iteration } : {}
3859
+ },
3860
+ findings,
3861
+ questions,
3862
+ next: { command: rerunOf(stage, change), slash: slashOf(stage, change) },
3863
+ problems
3864
+ };
3865
+ }
3866
+ function tabOf(v) {
3867
+ const r = api_exports.StageTab.safeParse(v);
3868
+ if (!r.success)
3869
+ throw badRequest(`${JSON.stringify(v)} isn't a stage: feature, architecture or stories.`);
3870
+ return r.data;
3871
+ }
3872
+ function uiStageRoutes(r, ctx, deps = {}) {
3873
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
3874
+ const changeOf = async (v) => {
3875
+ if (!ID2.test(v))
3876
+ throw badRequest(`${JSON.stringify(v)} isn't a change id.`);
3877
+ if (!await exists2(join11(changeFolder(ctx.root, v), "change.yaml")))
3878
+ throw notFound(`There is no change ${v}.`);
3879
+ return v;
3880
+ };
3881
+ return r.add("GET", "/api/changes/{changeId}/stages/{stage}", async ({ params }) => {
3882
+ const stage = tabOf(params.stage);
3883
+ const change = await changeOf(params.changeId);
3884
+ return { status: 200, body: await stageReviewView(ctx.root, change, stage) };
3885
+ }).add("POST", "/api/changes/{changeId}/stages/{stage}/comments", async ({ params, body }) => {
3886
+ const stage = tabOf(params.stage);
3887
+ const change = await changeOf(params.changeId);
3888
+ const b = api_exports.StageCommentRequest.safeParse(body ?? {});
3889
+ if (!b.success)
3890
+ throw badRequest("Say which finding and write the comment: { finding, text }.", {
3891
+ errors: b.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }))
3892
+ });
3893
+ const v = await stageReviewView(ctx.root, change, stage);
3894
+ const hits = v.findings.filter((f2) => f2.finding.id === b.data.finding && (!b.data.gate || f2.finding.gate === b.data.gate));
3895
+ if (!hits.length)
3896
+ throw notFound(`The ${stage} stage of ${change} has no finding ${b.data.finding} in ${v.gate ? `the ${v.gate} gate's latest round` : "any gate"}.`);
3897
+ const f = hits[0];
3898
+ let written;
3899
+ try {
3900
+ written = await commentOnFinding(ctx.root, {
3901
+ change,
3902
+ stage,
3903
+ finding: {
3904
+ id: f.finding.id,
3905
+ rule: f.finding.rule,
3906
+ summary: f.finding.summary,
3907
+ artifact: f.finding.artifact,
3908
+ ...f.finding.locator.line ? { line: f.finding.locator.line } : {},
3909
+ file: f.file
3910
+ },
3911
+ text: b.data.text,
3912
+ by: ctx.user,
3913
+ at: now().toISOString()
3914
+ });
3915
+ } catch (e) {
3916
+ if (e instanceof PlannerError && e.code === "refused")
3917
+ throw conflict(e.message);
3918
+ if (e instanceof PlannerError)
3919
+ throw new HttpError(422, "Atticus refused", e.message);
3920
+ throw e;
3921
+ }
3922
+ for (const p of written.files)
3923
+ ctx.events.publish({ type: "file.changed", path: p });
3924
+ const q = written.question;
3925
+ const result = {
3926
+ comment: {
3927
+ question: q.id,
3928
+ text: q.answer.text,
3929
+ by: q.answer.by,
3930
+ at: q.answer.at,
3931
+ read: false
3932
+ },
3933
+ files: written.files,
3934
+ commit: `git add ${written.files.join(" ")} && git commit -m "${change}: a comment on ${f.finding.id}"`,
3935
+ next: v.next
3936
+ };
3937
+ return { status: 200, body: result };
3938
+ });
3939
+ }
3940
+
3941
+ // packages/cli/dist/ui-commands.js
3942
+ function catalogMayHaveChanged(root, path) {
3943
+ const p = path.replace(/^\.\/?/, "").replace(/\/+$/, "");
3944
+ const near = (dir) => {
3945
+ const rel = relative3(root, dir).split(sep3).join("/");
3946
+ if (rel.startsWith("..") || isAbsolute(rel))
3947
+ return true;
3948
+ return p === "" || p === rel || p.startsWith(rel + "/") || rel.startsWith(p + "/");
3949
+ };
3950
+ return near(catalogDirOf(root)) || near(orgPackDirOf(root));
3951
+ }
3952
+ async function startUi(root, o = {}, deps = {}) {
3953
+ const srv = await startServer({
3954
+ root,
3955
+ port: o.port ?? DEFAULT_PORT,
3956
+ ...o.embed ? { embed: o.embed } : {},
3957
+ ...o.full ? { full: true } : {},
3958
+ ...o.token ? { token: o.token } : {},
3959
+ staticDir: o.staticDir ?? uiDistDir(),
3960
+ ...o.user ? { user: o.user } : {},
3961
+ ...o.version ? { version: o.version } : {},
3962
+ onRequest: (() => {
3963
+ const log = createLog(root);
3964
+ return (r) => {
3965
+ const fields = { method: r.method, path: r.path, status: r.status, ms: r.ms };
3966
+ if (r.error)
3967
+ log.error("request", r.error, fields);
3968
+ else if (r.status >= 500)
3969
+ log.warn("request", fields);
3970
+ else
3971
+ log.debug("request", fields);
3972
+ };
3973
+ })(),
3974
+ extend: (r, ctx) => {
3975
+ const base = uiKnowRoutes(uiRunRoutes(uiRoutes(r, ctx, deps), ctx, deps), ctx, {
3976
+ warm: o.warm === true
3977
+ });
3978
+ const views = uiGraphRoutes(uiAcrossRoutes(uiMeRoutes(base, ctx), ctx), ctx);
3979
+ const specs = uiSpecRoutes(uiOnboardingRoutes(uiQuestionRoutes(views, ctx), ctx), ctx);
3980
+ return uiStageRoutes(uiFactsRoutes(uiTrustRoutes(uiCockpitRoutes(uiRepoRoutes(uiDocumentRoutes(uiAiRoutes(uiHistoryRoutes(uiModelRunRoutes(uiKnowledgeRoutes(uiCiteRoutes(specs, ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx), ctx);
3981
+ }
3982
+ });
3983
+ keepCatalogsInMemory(true);
3984
+ void readCatalog(join12(catalogDirOf(root), "generated"), { cacheRoot: root }).catch(() => void 0);
3985
+ const stop = srv.events.listen((e) => {
3986
+ const changed = e.type === "file.changed" && (typeof e.path !== "string" || catalogMayHaveChanged(root, e.path));
3987
+ if (changed || e.type === "index.rebuilt")
3988
+ forgetCatalogs();
3989
+ });
3990
+ const close = srv.close;
3991
+ return Object.assign(srv, {
3992
+ close: async () => {
3993
+ stop();
3994
+ keepCatalogsInMemory(false);
3995
+ await close();
3996
+ }
3997
+ });
3998
+ }
3999
+ function openBrowser(url) {
4000
+ const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
4001
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
4002
+ try {
4003
+ spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
4004
+ } catch {
4005
+ }
4006
+ }
4007
+ function untilStopped(embedded, stdin = process.stdin, o = {}) {
4008
+ return new Promise((resolve3) => {
4009
+ const alive = o.alive ?? ((pid) => {
4010
+ try {
4011
+ process.kill(pid, 0);
4012
+ return true;
4013
+ } catch (e) {
4014
+ return e.code === "EPERM";
4015
+ }
4016
+ });
4017
+ const watch = o.parent !== void 0 ? setInterval(() => {
4018
+ if (!alive(o.parent))
4019
+ stop();
4020
+ }, o.everyMs ?? 2e3) : void 0;
4021
+ const stop = () => {
4022
+ if (watch)
4023
+ clearInterval(watch);
4024
+ process.off("SIGINT", stop);
4025
+ process.off("SIGTERM", stop);
4026
+ if (embedded) {
4027
+ stdin.off("end", stop);
4028
+ stdin.off("close", stop);
4029
+ stdin.pause();
4030
+ }
4031
+ resolve3();
4032
+ };
4033
+ process.once("SIGINT", stop);
4034
+ process.once("SIGTERM", stop);
4035
+ if (embedded) {
4036
+ stdin.once("end", stop);
4037
+ stdin.once("close", stop);
4038
+ stdin.on("data", () => void 0);
4039
+ stdin.resume();
4040
+ }
4041
+ });
4042
+ }
4043
+ function startedLines(url, token, o) {
4044
+ return [
4045
+ `Atticus is running at ${url} (Ctrl+C stops it).`,
4046
+ o.embed || o.parent !== void 0 ? "The session token isn't printed: the editor that started the app has it." : o.showToken ? `For assistants and scripts, the session token is ${token}` : "The page has the session token; --show-token prints it for an assistant or a script."
4047
+ ];
4048
+ }
4049
+ async function uiCommand(root, o, io) {
4050
+ const given = o.embed ? process.env.ATTICUS_UI_TOKEN : void 0;
4051
+ delete process.env.ATTICUS_UI_TOKEN;
4052
+ let srv;
4053
+ try {
4054
+ srv = await startUi(root, {
4055
+ ...given ? { token: given } : {},
4056
+ warm: true,
4057
+ ...o.full ? { full: true } : {},
4058
+ ...o.port !== void 0 ? { port: o.port } : {},
4059
+ ...o.user ? { user: o.user } : {},
4060
+ ...o.version ? { version: o.version } : {},
4061
+ ...o.embed ? { embed: o.embed } : {}
4062
+ });
4063
+ } catch (e) {
4064
+ const code = e.code;
4065
+ io.err(code === "EADDRINUSE" ? `Port ${o.port ?? DEFAULT_PORT} is in use. Start with --port <another port>.` : `Couldn't start the app: ${e instanceof Error ? e.message : String(e)}`);
4066
+ return 1;
4067
+ }
4068
+ for (const l of startedLines(srv.url, srv.token, o))
4069
+ io.out(l);
4070
+ const p = srv.person;
4071
+ io.out(p.source === "git" ? `You are ${p.handle}${p.name ? ` (${p.name})` : ""}, from your Git user.` : `You are ${p.handle}.`);
4072
+ if (p.warning)
4073
+ io.err(p.warning);
4074
+ if (o.open)
4075
+ openBrowser(srv.url);
4076
+ await untilStopped(o.embed === "vscode", process.stdin, o.parent !== void 0 ? { parent: o.parent } : {});
4077
+ await srv.close();
4078
+ io.out("Stopped.");
4079
+ return 0;
4080
+ }
4081
+ export {
4082
+ catalogMayHaveChanged,
4083
+ startUi,
4084
+ startedLines,
4085
+ uiCommand,
4086
+ untilStopped
4087
+ };