@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,2078 @@
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
+ upgradeCommand
8
+ } from "./chunk-72TXNLOC.js";
9
+ import {
10
+ table
11
+ } from "./chunk-VOGCSYOI.js";
12
+ import {
13
+ generateAssistantFiles
14
+ } from "./chunk-V5PKHHQQ.js";
15
+ import {
16
+ changeStore
17
+ } from "./chunk-56YQ6ROQ.js";
18
+ import {
19
+ fileStepStore
20
+ } from "./chunk-7JUKDRSZ.js";
21
+ import {
22
+ readCatalog,
23
+ scanEstate,
24
+ writeCatalog
25
+ } from "./chunk-OLWFG75W.js";
26
+ import {
27
+ buildContextPack,
28
+ formatOf,
29
+ screenText
30
+ } from "./chunk-CNUKCS2T.js";
31
+ import {
32
+ AgentDefinition,
33
+ CORE_ROLES,
34
+ Finding,
35
+ LIFECYCLE_STATES,
36
+ LifecycleState,
37
+ RecordId,
38
+ catalogDirOf,
39
+ external_exports,
40
+ orgPackDirOf,
41
+ recordReferences,
42
+ require_dist,
43
+ stateDirOf,
44
+ toCanonicalYaml
45
+ } from "./chunk-S2A7YC3Q.js";
46
+ import {
47
+ __toESM
48
+ } from "./chunk-TV45EUGM.js";
49
+
50
+ // packages/learn/dist/lessons.js
51
+ var import_yaml = __toESM(require_dist(), 1);
52
+ import { readdir, readFile } from "node:fs/promises";
53
+ import { dirname, join } from "node:path";
54
+ import { fileURLToPath } from "node:url";
55
+ var __atticus_import_meta_url = __atticusUrl("packages/learn/dist/lessons.js");
56
+ var LESSON_SCHEMA_VERSION = 1;
57
+ var Truth = external_exports.discriminatedUnion("from", [
58
+ /** Components that read a table (Table.readers). */
59
+ external_exports.strictObject({ from: external_exports.literal("table-readers"), id: RecordId }),
60
+ /** Components that call a component, optionally only through one route kind. */
61
+ external_exports.strictObject({
62
+ from: external_exports.literal("interface-callers"),
63
+ to: RecordId,
64
+ via: external_exports.string().optional()
65
+ }),
66
+ /** The owner of a system. */
67
+ external_exports.strictObject({ from: external_exports.literal("system-owner"), id: RecordId }),
68
+ /** Systems with no owner in the catalog. */
69
+ external_exports.strictObject({ from: external_exports.literal("systems-without-owner") }),
70
+ /** Files under a folder the ingested-content defences rate medium or high. */
71
+ external_exports.strictObject({ from: external_exports.literal("flagged-files"), path: external_exports.string().min(1) }),
72
+ /** A change's state. */
73
+ external_exports.strictObject({ from: external_exports.literal("change-state"), change: RecordId }),
74
+ /** The feature an entry point belongs to. */
75
+ external_exports.strictObject({ from: external_exports.literal("feature-of"), entryPoint: RecordId }),
76
+ /** The pattern code a change's interfaces.md names that the org pack doesn't know. */
77
+ external_exports.strictObject({ from: external_exports.literal("unknown-pattern"), change: RecordId }),
78
+ /** Stories of a change with no acceptance criteria. */
79
+ external_exports.strictObject({ from: external_exports.literal("stories-without-criteria"), change: RecordId }),
80
+ /** A fixed answer, for questions the files can't decide (yes/no, a name on a diagram). */
81
+ external_exports.strictObject({ from: external_exports.literal("fixed"), values: external_exports.array(external_exports.string().min(1)).min(1) })
82
+ ]);
83
+ var Check = external_exports.lazy(() => external_exports.discriminatedUnion("type", [
84
+ external_exports.strictObject({
85
+ type: external_exports.literal("catalog-has"),
86
+ kind: external_exports.string().min(1),
87
+ id: external_exports.string().optional(),
88
+ min: external_exports.number().int().positive().optional()
89
+ }),
90
+ external_exports.strictObject({ type: external_exports.literal("index-built") }),
91
+ external_exports.strictObject({
92
+ type: external_exports.literal("file"),
93
+ path: external_exports.string().min(1),
94
+ contains: external_exports.array(external_exports.string()).optional(),
95
+ notContains: external_exports.array(external_exports.string()).optional()
96
+ }),
97
+ external_exports.strictObject({
98
+ type: external_exports.literal("files"),
99
+ dir: external_exports.string().min(1),
100
+ suffix: external_exports.string().optional(),
101
+ min: external_exports.number().int().positive().optional()
102
+ }),
103
+ external_exports.strictObject({
104
+ type: external_exports.literal("curated-decision"),
105
+ action: external_exports.string().min(1),
106
+ target: external_exports.string().min(1),
107
+ includes: external_exports.string().optional()
108
+ }),
109
+ external_exports.strictObject({
110
+ type: external_exports.literal("record"),
111
+ kind: external_exports.string().min(1),
112
+ id: external_exports.string().min(1),
113
+ where: external_exports.record(external_exports.string(), external_exports.string()).optional()
114
+ }),
115
+ external_exports.strictObject({ type: external_exports.literal("change-state"), change: RecordId, atLeast: LifecycleState }),
116
+ external_exports.strictObject({
117
+ type: external_exports.literal("findings"),
118
+ change: RecordId,
119
+ gate: external_exports.string().optional(),
120
+ rule: external_exports.string().optional(),
121
+ artifact: external_exports.string().optional(),
122
+ min: external_exports.number().int().min(0).optional(),
123
+ max: external_exports.number().int().min(0).optional(),
124
+ state: external_exports.string().optional(),
125
+ latest: external_exports.boolean().optional()
126
+ }),
127
+ external_exports.strictObject({ type: external_exports.literal("answer"), question: external_exports.string().min(1), truth: Truth }),
128
+ external_exports.strictObject({
129
+ type: external_exports.literal("steps"),
130
+ action: external_exports.string().optional(),
131
+ state: external_exports.enum(["open", "done"]).optional(),
132
+ min: external_exports.number().int().positive().optional()
133
+ }),
134
+ external_exports.strictObject({ type: external_exports.literal("all"), checks: external_exports.array(Check).min(1) })
135
+ ]));
136
+ var LessonStep = external_exports.strictObject({
137
+ id: RecordId,
138
+ title: external_exports.string().min(1),
139
+ /** What to read and do, in Markdown. */
140
+ body: external_exports.string().min(1),
141
+ /** The commands to try, as arguments after `atticus` (the sandbox root is added). */
142
+ do: external_exports.array(external_exports.array(external_exports.string())).default([]),
143
+ /** Files to write for this step, when it asks the learner to write something (also the worked solution). */
144
+ solution: external_exports.strictObject({
145
+ answer: external_exports.string().optional(),
146
+ files: external_exports.array(external_exports.strictObject({ path: external_exports.string().min(1), content: external_exports.string() })).default([])
147
+ }).optional(),
148
+ check: Check,
149
+ /** Shown when the check fails. */
150
+ hint: external_exports.string().min(1)
151
+ });
152
+ var Lesson = external_exports.strictObject({
153
+ schemaVersion: external_exports.literal(LESSON_SCHEMA_VERSION),
154
+ id: RecordId,
155
+ /** A role id from roles.yaml (core or custom). */
156
+ role: RecordId,
157
+ title: external_exports.string().min(1),
158
+ summary: external_exports.string().min(1),
159
+ minutes: external_exports.number().int().positive(),
160
+ /** What the sandbox must have before the first step: "scan" scans it and builds the index. */
161
+ setup: external_exports.array(external_exports.enum(["scan"])).default([]),
162
+ intro: external_exports.string().min(1),
163
+ steps: external_exports.array(LessonStep).min(1),
164
+ outro: external_exports.string().min(1)
165
+ }).superRefine((l, ctx) => {
166
+ const seen = /* @__PURE__ */ new Set();
167
+ for (const s of l.steps) {
168
+ if (seen.has(s.id))
169
+ ctx.addIssue({ code: "custom", path: ["steps"], message: `step ${s.id} appears twice` });
170
+ seen.add(s.id);
171
+ }
172
+ });
173
+ var LESSONS_DIR = join(dirname(fileURLToPath(__atticus_import_meta_url)), "..", "lessons");
174
+ async function readDir(dir, label, out) {
175
+ let names;
176
+ try {
177
+ names = (await readdir(dir)).filter((n) => /\.ya?ml$/.test(n)).sort();
178
+ } catch {
179
+ return;
180
+ }
181
+ for (const n of names) {
182
+ try {
183
+ const r = Lesson.safeParse((0, import_yaml.parse)(await readFile(join(dir, n), "utf8")));
184
+ if (!r.success) {
185
+ out.problems.push(`${label}/${n}: ${r.error.issues.map((i) => `${i.path.join(".") || "(file)"} ${i.message}`).join("; ")}`);
186
+ continue;
187
+ }
188
+ const at = out.lessons.findIndex((l) => l.id === r.data.id);
189
+ const lesson = { ...r.data, source: `${label}/${n}` };
190
+ if (at >= 0)
191
+ out.lessons[at] = lesson;
192
+ else
193
+ out.lessons.push(lesson);
194
+ } catch (e) {
195
+ out.problems.push(`${label}/${n}: ${e.message.split("\n")[0]}`);
196
+ }
197
+ }
198
+ }
199
+ async function loadLessons(o = {}) {
200
+ const out = { lessons: [], problems: [] };
201
+ await readDir(o.dir ?? LESSONS_DIR, "lessons", out);
202
+ if (o.orgPackDir)
203
+ await readDir(join(o.orgPackDir, "learn", "lessons"), "org-pack/learn/lessons", out);
204
+ const order = (role) => {
205
+ if (role === "everyone")
206
+ return -1;
207
+ const i = CORE_ROLES.indexOf(role);
208
+ return i === -1 ? CORE_ROLES.length : i;
209
+ };
210
+ out.lessons.sort((a, b) => order(a.role) - order(b.role) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
211
+ return out;
212
+ }
213
+
214
+ // packages/learn/dist/sandbox.js
215
+ import { execFile } from "node:child_process";
216
+ import { existsSync } from "node:fs";
217
+ import { appendFile, cp, mkdir, readdir as readdir2, readFile as readFile2, rm, stat, writeFile } from "node:fs/promises";
218
+ import { dirname as dirname2, join as join2, resolve } from "node:path";
219
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
220
+ var __atticus_import_meta_url2 = __atticusUrl("packages/learn/dist/sandbox.js");
221
+ var SANDBOX_MARKER = ".atticus-sandbox.json";
222
+ var SEEDED_CHANGE = "add-transfer-limit";
223
+ var SANDBOX_METAREPO = "forge/forge-metarepo";
224
+ var SandboxError = class extends Error {
225
+ constructor(message) {
226
+ super(message);
227
+ this.name = "SandboxError";
228
+ }
229
+ };
230
+ function findFixtures(start = dirname2(fileURLToPath2(__atticus_import_meta_url2))) {
231
+ let d = resolve(start);
232
+ for (; ; ) {
233
+ if (existsSync(join2(d, "fixtures", "forge", "README.md")))
234
+ return join2(d, "fixtures");
235
+ const up = dirname2(d);
236
+ if (up === d)
237
+ return void 0;
238
+ d = up;
239
+ }
240
+ }
241
+ var GIT_TIMEOUT_MS = 12e4;
242
+ function git(cwd, args, timeoutMs, noHooks) {
243
+ return new Promise((done, fail2) => {
244
+ execFile("git", [
245
+ "-c",
246
+ "user.name=Atticus sandbox",
247
+ "-c",
248
+ "user.email=sandbox@example.com",
249
+ "-c",
250
+ "init.defaultBranch=main",
251
+ "-c",
252
+ "core.autocrlf=false",
253
+ "-c",
254
+ `core.hooksPath=${noHooks}`,
255
+ "-c",
256
+ "commit.gpgsign=false",
257
+ ...args
258
+ ], {
259
+ cwd,
260
+ timeout: timeoutMs,
261
+ windowsHide: true,
262
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
263
+ }, (e, _out, err) => {
264
+ if (!e)
265
+ return done();
266
+ if (e.killed)
267
+ return fail2(new Error(`git took longer than ${timeoutMs / 1e3} s (git ${args[0]})`));
268
+ fail2(new Error(`${e.message.split("\n")[0]}${err ? `: ${err.trim()}` : ""}`));
269
+ }).stdin?.end();
270
+ });
271
+ }
272
+ var isEmptyDir = async (d) => {
273
+ try {
274
+ return (await readdir2(d)).length === 0;
275
+ } catch {
276
+ return true;
277
+ }
278
+ };
279
+ async function createSandbox(dir, o = {}) {
280
+ const fixtures = o.fixtures ?? findFixtures();
281
+ if (!fixtures || !existsSync(join2(fixtures, "forge")))
282
+ throw new SandboxError("The Forge fixture isn't here: the practice sandbox is built from fixtures/forge in the Atticus checkout.");
283
+ const target = resolve(dir);
284
+ if (existsSync(target) && !(await stat(target)).isDirectory())
285
+ throw new SandboxError(`${dir} is a file; give a new or empty folder.`);
286
+ if (!await isEmptyDir(target))
287
+ throw new SandboxError(`${dir} isn't empty; give a new or empty folder.`);
288
+ await mkdir(target, { recursive: true });
289
+ const forge = join2(fixtures, "forge");
290
+ const change = join2(fixtures, "forge-change");
291
+ await cp(join2(forge, "repos"), join2(target, "repos"), { recursive: true });
292
+ await cp(join2(forge, "inputs"), join2(target, "inputs"), { recursive: true });
293
+ const root = join2(target, "metarepo");
294
+ await cp(join2(forge, "metarepo"), root, { recursive: true });
295
+ await cp(join2(change, "org-pack"), join2(root, "org-pack"), { recursive: true });
296
+ await cp(join2(forge, "inputs"), join2(root, "inputs"), { recursive: true });
297
+ await cp(join2(change, "openspec", "changes", SEEDED_CHANGE), join2(root, "openspec", "changes", SEEDED_CHANGE), {
298
+ recursive: true
299
+ });
300
+ const runtime = join2(root, "org-pack", "runtime.yaml");
301
+ await writeFile(runtime, (await readFile2(runtime, "utf8")).replace(/^(\s*)gitHost:.*$/m, `$1gitHost: { type: manual, settings: { metarepo: ${SANDBOX_METAREPO} } }`), "utf8");
302
+ const repos = (await readdir2(join2(target, "repos"))).sort();
303
+ const timeout = o.gitTimeoutMs ?? GIT_TIMEOUT_MS;
304
+ const noHooks = join2(target, ".no-git-hooks").split("\\").join("/");
305
+ const run = (cwd, args) => git(cwd, args, timeout, noHooks);
306
+ for (const id of repos) {
307
+ const base = join2(root, "workspace", id);
308
+ await cp(join2(target, "repos", id), base, { recursive: true });
309
+ try {
310
+ await run(base, ["init", "-q"]);
311
+ await appendFile(join2(base, ".git", "config"), "[user]\n name = Atticus sandbox\n email = sandbox@example.com\n[commit]\n gpgsign = false\n[tag]\n gpgsign = false\n", "utf8");
312
+ await run(base, ["add", "-A"]);
313
+ await run(base, ["commit", "-q", "-m", "Forge fixture"]);
314
+ const remote = join2(target, "remotes", `${id}.git`);
315
+ await mkdir(dirname2(remote), { recursive: true });
316
+ await run(target, ["clone", "-q", "--bare", base, remote]);
317
+ await run(base, ["remote", "add", "origin", remote]);
318
+ await run(base, ["fetch", "-q", "origin"]);
319
+ await run(base, ["branch", "-q", "--set-upstream-to=origin/main", "main"]);
320
+ } catch (e) {
321
+ await rm(join2(base, ".git"), { recursive: true, force: true });
322
+ throw new SandboxError(`git couldn't set up the base clone of ${id}: ${e.message.split("\n")[0]}`);
323
+ }
324
+ }
325
+ await writeFile(join2(root, ".gitignore"), ["workspace/", ".index/", ".runs/", ".learn/", ".evals/", ""].join("\n"), "utf8");
326
+ await writeFile(join2(root, "README.md"), [
327
+ "# Atticus practice sandbox",
328
+ "",
329
+ "A throwaway metarepo built from the invented Forge ecosystem. Everything here is made up;",
330
+ "every adapter is a mock but the Git host, whose steps you record with `atticus todo done`.",
331
+ "Try anything, then delete the folder.",
332
+ "",
333
+ "Run commands from this folder, or add `--root " + root.replace(/\\/g, "/") + "`.",
334
+ ""
335
+ ].join("\n"), "utf8");
336
+ const info = { dir: target, root, repos, change: SEEDED_CHANGE };
337
+ await writeFile(join2(root, SANDBOX_MARKER), JSON.stringify({ sandbox: 1, createdAt: (o.now ?? /* @__PURE__ */ new Date()).toISOString(), repos, change: SEEDED_CHANGE }, null, 2) + "\n", "utf8");
338
+ return info;
339
+ }
340
+ async function isSandbox(root) {
341
+ try {
342
+ const m = JSON.parse(await readFile2(join2(root, SANDBOX_MARKER), "utf8"));
343
+ return m.sandbox === 1;
344
+ } catch {
345
+ return false;
346
+ }
347
+ }
348
+
349
+ // packages/learn/dist/checks.js
350
+ var import_yaml2 = __toESM(require_dist(), 1);
351
+ import { existsSync as existsSync2 } from "node:fs";
352
+ import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "node:fs/promises";
353
+ import { join as join3, relative, sep } from "node:path";
354
+ var ok = (message) => ({ pass: true, message });
355
+ var no = (message) => ({ pass: false, message });
356
+ async function catalog(root) {
357
+ const dir = catalogDirOf(root);
358
+ const g = await readCatalog(join3(dir, "generated"), { cacheRoot: root });
359
+ const c = await readCatalog(join3(dir, "curated"), { cacheRoot: root });
360
+ return [...g.records, ...c.records];
361
+ }
362
+ function answerSet(text) {
363
+ return [
364
+ ...new Set(text.toLowerCase().split(/[\s,;]+|\band\b/).map((w) => w.replace(/^["'`([{<]+|["'`)\]}>.:!?]+$/g, "")).filter(Boolean))
365
+ ].sort();
366
+ }
367
+ async function walk(dir) {
368
+ const out = [];
369
+ const go = async (d) => {
370
+ let entries;
371
+ try {
372
+ entries = await readdir3(d, { withFileTypes: true });
373
+ } catch {
374
+ return;
375
+ }
376
+ for (const e of entries.sort((a, b) => a.name < b.name ? -1 : 1)) {
377
+ const full = join3(d, e.name);
378
+ if (e.isDirectory())
379
+ await go(full);
380
+ else
381
+ out.push(full);
382
+ }
383
+ };
384
+ await go(dir);
385
+ return out;
386
+ }
387
+ async function truthOf(root, t) {
388
+ switch (t.from) {
389
+ case "fixed":
390
+ return answerSet(t.values.join(" "));
391
+ case "table-readers": {
392
+ const r = (await catalog(root)).find((x) => x.kind === "Table" && x.id === t.id);
393
+ return r && r.kind === "Table" ? [...r.readers].sort() : [];
394
+ }
395
+ case "interface-callers": {
396
+ const rs = (await catalog(root)).filter((x) => x.kind === "Interface" && x.to === t.to && (!t.via || x.via === t.via));
397
+ return [...new Set(rs.map((r) => r.from))].sort();
398
+ }
399
+ case "system-owner": {
400
+ const r = (await catalog(root)).find((x) => x.kind === "System" && x.id === t.id);
401
+ return r && r.kind === "System" && r.owner ? [r.owner] : [];
402
+ }
403
+ case "systems-without-owner":
404
+ return (await catalog(root)).filter((x) => x.kind === "System" && !x.owner).map((x) => x.id).sort();
405
+ case "flagged-files": {
406
+ const base = join3(root, t.path);
407
+ const out = [];
408
+ for (const f of await walk(base)) {
409
+ const text = await readFile3(f, "utf8").catch(() => "");
410
+ const rel = relative(root, f).split(sep).join("/");
411
+ const s = screenText(text, { format: formatOf("document", rel) });
412
+ if (s.risk === "medium" || s.risk === "high")
413
+ out.push(rel.split("/").pop().replace(/\.[a-z0-9]+$/i, "").toLowerCase());
414
+ }
415
+ return out.sort();
416
+ }
417
+ case "change-state": {
418
+ const c = await changeStore(root).tryRead(t.change);
419
+ return c ? [c.state] : [];
420
+ }
421
+ case "feature-of": {
422
+ const f = (await catalog(root)).find((x) => x.kind === "Capability" && x.entryPoints.includes(t.entryPoint));
423
+ return f ? [f.id] : [];
424
+ }
425
+ case "unknown-pattern": {
426
+ const text = await readFile3(join3(root, "openspec/changes", t.change, "interfaces.md"), "utf8").catch(() => "");
427
+ const known = new Set((await readdir3(join3(root, "org-pack", "patterns")).catch(() => [])).map((f) => f.replace(/\.ya?ml$/, "").toLowerCase()));
428
+ const codes = [...text.matchAll(/\b([A-Z]\.[A-Z]{2,5}(?:\.[A-Z]{2,5})*)\b/g)].map((m) => m[1].toLowerCase());
429
+ return [...new Set(codes.filter((c) => !known.has(c)))].sort();
430
+ }
431
+ case "stories-without-criteria": {
432
+ const dir = join3(root, "openspec/changes", t.change, "stories");
433
+ const out = [];
434
+ for (const f of (await readdir3(dir).catch(() => [])).filter((n) => n.endsWith(".md")).sort()) {
435
+ const text = await readFile3(join3(dir, f), "utf8");
436
+ const section = /## Acceptance criteria\s*\n([\s\S]*?)(\n## |$)/.exec(text)?.[1] ?? "";
437
+ if (!/^\s*-\s*Given\b/im.test(section))
438
+ out.push(f.replace(/\.md$/, ""));
439
+ }
440
+ return out;
441
+ }
442
+ }
443
+ }
444
+ var rank = (s) => s === "post-release-review" ? LIFECYCLE_STATES.indexOf("released") + 0.5 : LIFECYCLE_STATES.indexOf(s);
445
+ async function findingFiles(root, change) {
446
+ const dir = join3(root, "openspec/changes", change, "review");
447
+ const out = [];
448
+ for (const f of await walk(dir)) {
449
+ if (!f.endsWith(".yaml") || f.endsWith("round.yaml"))
450
+ continue;
451
+ const rel = relative(dir, f).split(sep);
452
+ try {
453
+ const v = (0, import_yaml2.parse)(await readFile3(f, "utf8"));
454
+ if (v && typeof v === "object" && "rule" in v)
455
+ out.push({ ...v, _gate: rel[0], _round: rel[1] });
456
+ } catch {
457
+ }
458
+ }
459
+ return out;
460
+ }
461
+ async function runCheck(root, check, answer) {
462
+ switch (check.type) {
463
+ case "all": {
464
+ for (const c of check.checks) {
465
+ const r = await runCheck(root, c, answer);
466
+ if (!r.pass)
467
+ return r;
468
+ }
469
+ return ok("Done.");
470
+ }
471
+ case "catalog-has": {
472
+ const rs = (await catalog(root)).filter((r) => r.kind === check.kind && (!check.id || r.id === check.id));
473
+ const min = check.min ?? 1;
474
+ return rs.length >= min ? ok(`The catalog has ${rs.length} ${check.kind} record${rs.length === 1 ? "" : "s"}${check.id ? ` (${check.id})` : ""}.`) : no(`The catalog has ${rs.length} ${check.kind} record${rs.length === 1 ? "" : "s"}${check.id ? ` named ${check.id}` : ""}; it needs ${min}.`);
475
+ }
476
+ case "index-built":
477
+ return existsSync2(join3(stateDirOf(root), ".index", "catalog.sqlite")) ? ok("The index is built.") : no("There is no index yet.");
478
+ case "file": {
479
+ const p = join3(root, ...check.path.split("/"));
480
+ let text;
481
+ try {
482
+ if (!(await stat2(p)).isFile())
483
+ return no(`${check.path} isn't a file.`);
484
+ text = await readFile3(p, "utf8");
485
+ } catch {
486
+ return no(`${check.path} doesn't exist yet.`);
487
+ }
488
+ for (const c of check.contains ?? [])
489
+ if (!text.includes(c))
490
+ return no(`${check.path} doesn't say "${c}" yet.`);
491
+ for (const c of check.notContains ?? [])
492
+ if (text.includes(c))
493
+ return no(`${check.path} still says "${c}".`);
494
+ return ok(`${check.path} is there.`);
495
+ }
496
+ case "files": {
497
+ const fs = (await walk(join3(root, ...check.dir.split("/")))).filter((f) => !check.suffix || f.endsWith(check.suffix));
498
+ const min = check.min ?? 1;
499
+ return fs.length >= min ? ok(`${check.dir} has ${fs.length} file${fs.length === 1 ? "" : "s"}.`) : no(`${check.dir} has ${fs.length} file${fs.length === 1 ? "" : "s"}${check.suffix ? ` ending ${check.suffix}` : ""}; it needs ${min}.`);
500
+ }
501
+ case "curated-decision": {
502
+ const d = (await catalog(root)).find((r) => r.kind === "CuratedDecision" && r.action === check.action && r.target === check.target && (!check.includes || JSON.stringify(r.value ?? null).toLowerCase().includes(check.includes.toLowerCase())));
503
+ return d ? ok(`A ${check.action} decision on ${check.target} is recorded (${d.id}).`) : no(`No ${check.action} decision on ${check.target}${check.includes ? ` naming "${check.includes}"` : ""} is recorded yet.`);
504
+ }
505
+ case "record": {
506
+ const r = (await catalog(root)).find((x) => x.kind === check.kind && x.id === check.id);
507
+ if (!r)
508
+ return no(`There is no ${check.kind} ${check.id} yet.`);
509
+ for (const [k, v] of Object.entries(check.where ?? {}))
510
+ if (String(r[k] ?? "") !== v)
511
+ return no(`${check.kind} ${check.id} has ${k} ${String(r[k] ?? "unset")}, not ${v}.`);
512
+ return ok(`${check.kind} ${check.id} is there.`);
513
+ }
514
+ case "change-state": {
515
+ const c = await changeStore(root).tryRead(check.change).catch(() => void 0);
516
+ if (!c)
517
+ return no(`There is no change ${check.change} yet.`);
518
+ return c.state !== "returned" && rank(c.state) >= rank(check.atLeast) ? ok(`${check.change} is in ${c.state}.`) : no(`${check.change} is in ${c.state}; it needs to reach ${check.atLeast}.`);
519
+ }
520
+ case "findings": {
521
+ const all = await findingFiles(root, check.change);
522
+ const newest = /* @__PURE__ */ new Map();
523
+ for (const f of all)
524
+ newest.set(String(f._gate), Math.max(newest.get(String(f._gate)) ?? 0, Number(f._round)));
525
+ const fs = all.filter((f) => (!check.latest || Number(f._round) === newest.get(String(f._gate))) && (!check.gate || f._gate === check.gate) && (!check.rule || f.rule === check.rule) && (!check.artifact || f.artifact === check.artifact) && (!check.state || f.state === check.state));
526
+ const min = check.min ?? 1;
527
+ const what = `finding${fs.length === 1 ? "" : "s"}${check.rule ? ` for ${check.rule}` : ""}${check.gate ? ` at ${check.gate}` : ""}`;
528
+ if (fs.length < min)
529
+ return no(`${check.change} has ${fs.length} ${what}; run the critic first.`);
530
+ if (check.max !== void 0 && fs.length > check.max)
531
+ return no(`${check.change} still has ${fs.length} ${what}.`);
532
+ return ok(`${check.change} has ${fs.length} ${what}.`);
533
+ }
534
+ case "answer": {
535
+ if (answer === void 0 || !answer.trim())
536
+ return no(`Answer the question: ${check.question}`);
537
+ const want = await truthOf(root, check.truth);
538
+ if (!want.length)
539
+ return no("The sandbox can't answer this yet: do the steps before it first.");
540
+ const got = answerSet(answer);
541
+ const missing = want.filter((w) => !got.includes(w));
542
+ const extra = got.filter((g) => !want.includes(g));
543
+ if (!missing.length && !extra.length)
544
+ return ok("Right.");
545
+ return no(missing.length && extra.length ? "Not quite: some of that is wrong and something is missing." : missing.length ? `Not quite: there is more (${missing.length} missing).` : "Not quite: some of that isn't right.");
546
+ }
547
+ case "steps": {
548
+ const steps = await fileStepStore(root).list({
549
+ ...check.action ? { action: check.action } : {},
550
+ ...check.state ? { state: check.state } : {}
551
+ });
552
+ const min = check.min ?? 1;
553
+ return steps.length >= min ? ok(`${steps.length} manual step${steps.length === 1 ? "" : "s"} recorded.`) : no(`No manual step${check.action ? ` (${check.action})` : ""}${check.state ? ` ${check.state}` : ""} yet.`);
554
+ }
555
+ }
556
+ }
557
+
558
+ // packages/learn/dist/engine.js
559
+ import { mkdir as mkdir2, readFile as readFile4, rename, writeFile as writeFile2 } from "node:fs/promises";
560
+ import { dirname as dirname3, join as join4 } from "node:path";
561
+ var LearnError = class extends Error {
562
+ code;
563
+ constructor(message, code = "usage") {
564
+ super(message);
565
+ this.code = code;
566
+ this.name = "LearnError";
567
+ }
568
+ };
569
+ var progressPath = (root) => join4(stateDirOf(root), ".learn", "progress.json");
570
+ async function readProgress(root) {
571
+ try {
572
+ const p = JSON.parse(await readFile4(progressPath(root), "utf8"));
573
+ if (p.schemaVersion === 1 && p.lessons)
574
+ return p;
575
+ } catch {
576
+ }
577
+ return { schemaVersion: 1, lessons: {} };
578
+ }
579
+ async function writeProgress(root, p) {
580
+ const f = progressPath(root);
581
+ await mkdir2(dirname3(f), { recursive: true });
582
+ await writeFile2(`${f}.tmp`, JSON.stringify(p, null, 2) + "\n", "utf8");
583
+ await rename(`${f}.tmp`, f);
584
+ }
585
+ var iso = (d) => d.toISOString().replace(/\.\d{3}Z$/, "Z");
586
+ async function lessonsFor(root) {
587
+ return loadLessons(root ? { orgPackDir: orgPackDirOf(root) } : {});
588
+ }
589
+ async function findLesson(root, roleOrLesson) {
590
+ const { lessons } = await lessonsFor(root);
591
+ const l = lessons.find((x) => x.id === roleOrLesson) ?? lessons.find((x) => x.role === roleOrLesson);
592
+ if (!l) {
593
+ const roles = [...new Set(lessons.map((x) => x.role))].join(", ");
594
+ throw new LearnError(`No lesson or role ${roleOrLesson}. Roles with lessons: ${roles}.`, "not-found");
595
+ }
596
+ return l;
597
+ }
598
+ async function requireSandbox(root) {
599
+ if (!await isSandbox(root))
600
+ throw new LearnError(`${root} isn't a practice sandbox. Make one with atticus sandbox create <dir> and run lessons there.`, "not-sandbox");
601
+ }
602
+ async function startLesson(root, roleOrLesson, o = {}) {
603
+ await requireSandbox(root);
604
+ const lesson = await findLesson(root, roleOrLesson);
605
+ const p = await readProgress(root);
606
+ const resumed = !!p.lessons[lesson.id];
607
+ const setup = [];
608
+ if (!resumed) {
609
+ for (const s of lesson.setup) {
610
+ if (s === "scan") {
611
+ const scan = await scanEstate({ root });
612
+ await writeCatalog(join4(catalogDirOf(root), "generated"), scan.records);
613
+ setup.push(`Scanned the sandbox: ${scan.records.length} records in catalog/generated.`);
614
+ }
615
+ }
616
+ p.lessons[lesson.id] = { startedAt: iso(o.now ?? /* @__PURE__ */ new Date()), steps: {} };
617
+ }
618
+ p.current = lesson.id;
619
+ await writeProgress(root, p);
620
+ const done = p.lessons[lesson.id].steps;
621
+ const step = lesson.steps.find((s) => !done[s.id]);
622
+ return { lesson, ...step ? { step } : {}, setup, resumed };
623
+ }
624
+ async function checkStep(root, o = {}) {
625
+ await requireSandbox(root);
626
+ const p = await readProgress(root);
627
+ const id = o.lesson ?? p.current;
628
+ if (!id)
629
+ throw new LearnError("No lesson started. Start one with atticus learn start <role|lesson>.");
630
+ const lesson = await findLesson(root, id);
631
+ const lp = p.lessons[lesson.id] ??= { startedAt: iso(o.now ?? /* @__PURE__ */ new Date()), steps: {} };
632
+ const step = o.step ? lesson.steps.find((s) => s.id === o.step) : lesson.steps.find((s) => !lp.steps[s.id]) ?? lesson.steps.at(-1);
633
+ if (!step)
634
+ throw new LearnError(`Lesson ${lesson.id} has no step ${o.step}.`, "not-found");
635
+ const result = await runCheck(root, step.check, o.answer);
636
+ if (result.pass && !lp.steps[step.id])
637
+ lp.steps[step.id] = {
638
+ passedAt: iso(o.now ?? /* @__PURE__ */ new Date()),
639
+ ...o.answer ? { answer: o.answer } : {}
640
+ };
641
+ const next = lesson.steps.find((s) => !lp.steps[s.id]);
642
+ const completed = !next;
643
+ if (completed && !lp.completedAt)
644
+ lp.completedAt = iso(o.now ?? /* @__PURE__ */ new Date());
645
+ p.current = lesson.id;
646
+ await writeProgress(root, p);
647
+ return { lesson, step, result, ...next ? { next } : {}, completed };
648
+ }
649
+
650
+ // packages/learn/dist/commands.js
651
+ var COMMANDS = [
652
+ {
653
+ name: "scan",
654
+ usage: "atticus scan [--repos a,b] [--full] [--dry-run] [--json]",
655
+ what: "Reads every repo's code and config (never runs it) and writes what it finds as catalog records: systems, components, endpoints, interfaces, tables, settings.",
656
+ who: ["developer", "architect", "platform-lead"],
657
+ changes: "catalog/generated (the records found in code)",
658
+ related: ["catalog diff", "index rebuild"]
659
+ },
660
+ {
661
+ name: "map",
662
+ usage: "atticus map <repo> [--estimate] [--handoff | --copy [--answer <file>] | --mode m] [--next] [--max-tokens n] [--force]",
663
+ what: "Has your assistant read a repo into facts, each citing its file and line: an agentic assistant follows /atticus-map; one without tools gets the repo in batches that fit its model, each answer checked before the next.",
664
+ who: ["developer", "architect"],
665
+ changes: "facts/<repo>.jsonl and the questions the reader raised",
666
+ related: ["verify", "map render"]
667
+ },
668
+ {
669
+ name: "map render",
670
+ usage: "atticus map render <repo> [--json]",
671
+ what: "Renders the repo's map from the catalog: map.md, a page per flow with its sequence diagram, and an OpenAPI file per component.",
672
+ who: ["developer", "architect"],
673
+ changes: "map/<repo>/ (the rendered pages)",
674
+ related: ["map", "verify"]
675
+ },
676
+ {
677
+ name: "join",
678
+ usage: "atticus join [--json]",
679
+ what: "Follows each repo's flows into the flows their calls reach in other repos, through the calls the repos' verified facts join (atticus verify <repo> --write makes those joins), and asks which repo answers a host nobody lists. The same command as atticus estate join.",
680
+ who: ["architect", "developer"],
681
+ changes: "specs/estate.yaml, and a question for each host no repo lists",
682
+ related: ["verify", "estate flow"]
683
+ },
684
+ {
685
+ name: "probe",
686
+ usage: "atticus probe [--assistant id] [--max-tokens n] [--json]",
687
+ what: "Measures what your assistant can do here, so the tier it reads a repo at is measured, not read off runtime.yaml: it writes a file holding a code and says to run /atticus-probe, which asks the assistant to open the file, write the code back and run atticus probe answer itself.",
688
+ who: ["developer", "architect", "platform-lead"],
689
+ changes: "the probe's files in the run folder (.runs/probe/), on this machine",
690
+ related: ["probe answer", "map"]
691
+ },
692
+ {
693
+ name: "probe answer",
694
+ usage: "atticus probe answer <code|none> [--pasted] [--max-tokens n] [--json]",
695
+ what: "Checks the assistant's answer to the probe: the right code means it opened the file, the code in answer.txt that it wrote one, and an answer not --pasted that it ran a command. A wrong code records nothing. The model's limit is what --max-tokens states; nothing guesses it.",
696
+ who: ["developer", "architect", "platform-lead"],
697
+ changes: ".atticus/probe/<assistant>.yaml, on this machine only (never committed); doctor, init, adopt and map --estimate use it",
698
+ related: ["probe", "map"]
699
+ },
700
+ {
701
+ name: "verify",
702
+ usage: "atticus verify <repo> [--write] [--dir path] [--commit sha] [--routed a,b] [--json]",
703
+ what: "Checks every fact in a repo's facts file against the line it cites, keeps the ones that hold, writes the rest with why to <repo>.rejected.jsonl, and says which source files no fact has read yet. With --write the kept facts become catalog records.",
704
+ who: ["developer", "architect"],
705
+ changes: "facts/ (the kept facts, the rejected ones, the coverage); catalog/generated with --write",
706
+ related: ["map", "map render"]
707
+ },
708
+ {
709
+ name: "catalog validate",
710
+ usage: "atticus catalog validate <dir> [--fix]",
711
+ what: "Checks record files against the catalog schema, their place and their canonical form.",
712
+ who: ["platform-lead"],
713
+ changes: "nothing, or the files' layout with --fix"
714
+ },
715
+ {
716
+ name: "catalog diff",
717
+ usage: "atticus catalog diff <a> <b>",
718
+ what: "Compares two catalog folders and lists what was added, removed or changed.",
719
+ who: ["architect", "platform-lead"],
720
+ changes: "nothing"
721
+ },
722
+ {
723
+ name: "query",
724
+ usage: 'atticus query "<query>" [--limit n] [--json]',
725
+ what: 'Finds records with a short query such as "kind:interface via:gateway".',
726
+ who: ["developer", "architect", "business-analyst"],
727
+ changes: "nothing",
728
+ related: ["impact"]
729
+ },
730
+ {
731
+ name: "impact",
732
+ usage: "atticus impact <id> [--depth n] [--json]",
733
+ what: "Shows what a change to one record reaches: who calls it, who reads it, their owners and the open questions on the way.",
734
+ who: ["architect", "developer", "team-lead"],
735
+ changes: "nothing",
736
+ related: ["query"]
737
+ },
738
+ {
739
+ name: "index rebuild",
740
+ usage: "atticus index rebuild",
741
+ what: "Rebuilds the local search index from the catalog files. Queries rebuild it on their own when files change.",
742
+ who: ["developer"],
743
+ changes: "the local index (.index/), never committed"
744
+ },
745
+ {
746
+ name: "reconcile",
747
+ usage: "atticus reconcile [diagram...] [--json]",
748
+ what: "Compares the architects' diagrams with what the code says: missing edges, wrong routes, names that need an alias.",
749
+ who: ["architect"],
750
+ changes: "nothing",
751
+ related: ["alias", "triage"]
752
+ },
753
+ {
754
+ name: "alias",
755
+ usage: 'atticus alias "<name>" <id> [--reason text]',
756
+ what: "Records that a name used on diagrams or in documents means a catalog id, so they line up from now on.",
757
+ who: ["architect"],
758
+ changes: "a curated decision in catalog/curated"
759
+ },
760
+ {
761
+ name: "sources add",
762
+ usage: "atticus sources add <type> <location>",
763
+ what: "Prints the sources.yaml entry for a wiki; the org pack changes by pull request.",
764
+ who: ["platform-lead"],
765
+ changes: "nothing"
766
+ },
767
+ {
768
+ name: "sources sync",
769
+ usage: "atticus sources sync [--json]",
770
+ what: "Reads the wikis: stale pages, pages that contradict code, facts only a document gives.",
771
+ who: ["architect", "platform-lead"],
772
+ changes: "nothing"
773
+ },
774
+ {
775
+ name: "patterns scan",
776
+ usage: "atticus patterns scan [--json]",
777
+ what: "Finds integration patterns written in the wikis and compares them with the org pack.",
778
+ who: ["architect"],
779
+ changes: "nothing"
780
+ },
781
+ {
782
+ name: "coverage",
783
+ usage: "atticus coverage [--json]",
784
+ what: "What the catalog knows for each system and area: verified from code, only from documents, or unknown.",
785
+ who: ["platform-lead", "architect"],
786
+ changes: "nothing",
787
+ related: ["questions"]
788
+ },
789
+ {
790
+ name: "questions",
791
+ usage: "atticus questions [--json]",
792
+ what: "The knowledge gaps and the question to send each system's owner to close them.",
793
+ who: ["architect", "platform-lead"],
794
+ changes: "nothing"
795
+ },
796
+ {
797
+ name: "intake add",
798
+ usage: "atticus intake add <files...>",
799
+ what: "Drops documents, specs, diagrams or replies into inputs/inbox; the next scan reads them as data.",
800
+ who: ["business-analyst", "architect", "product-owner"],
801
+ changes: "inputs/inbox"
802
+ },
803
+ {
804
+ name: "intake list",
805
+ usage: "atticus intake list [--json]",
806
+ what: "What intake read from inputs/: kinds, systems, duplicates and versions.",
807
+ who: ["business-analyst"],
808
+ changes: "nothing"
809
+ },
810
+ {
811
+ name: "triage",
812
+ usage: "atticus triage [--json]",
813
+ what: "What needs a person's decision: unknown names, documents that contradict code, edges read from images.",
814
+ who: ["architect"],
815
+ changes: "nothing",
816
+ related: ["triage decide"]
817
+ },
818
+ {
819
+ name: "triage decide",
820
+ usage: "atticus triage decide <item> <n> [--reason text]",
821
+ what: "Chooses an option for a triage item and records it as a curated decision.",
822
+ who: ["architect"],
823
+ changes: "a curated decision in catalog/curated"
824
+ },
825
+ {
826
+ name: "diagram render",
827
+ usage: "atticus diagram render <view|file> [--focus id] [--format mermaid|drawio] [--out file]",
828
+ what: "Draws a view of the catalog (context, container, data, event, sequence) as Mermaid or draw.io.",
829
+ who: ["architect", "designer"],
830
+ changes: "the file named with --out"
831
+ },
832
+ {
833
+ name: "diagram library",
834
+ usage: "atticus diagram library <file>",
835
+ what: "Writes the draw.io shape library.",
836
+ who: ["architect"],
837
+ changes: "the file named"
838
+ },
839
+ {
840
+ name: "stub serve",
841
+ usage: "atticus stub serve <spec|id>",
842
+ what: "Serves a fake of an API from its spec or the catalog, on this machine only.",
843
+ who: ["developer", "qa"],
844
+ changes: "nothing"
845
+ },
846
+ {
847
+ name: "stub check",
848
+ usage: "atticus stub check <spec>",
849
+ what: "Says which operations of a spec have examples a stub can answer with.",
850
+ who: ["developer", "qa"],
851
+ changes: "nothing"
852
+ },
853
+ {
854
+ name: "dialect new",
855
+ usage: "atticus dialect new <name> --from <repo>...",
856
+ what: "Starts a dialect (rules for an in-house framework) from sample repos.",
857
+ who: ["platform-lead", "developer"],
858
+ changes: "org-pack/dialects/<name>.yaml"
859
+ },
860
+ {
861
+ name: "dialect test",
862
+ usage: "atticus dialect test <name>",
863
+ what: "Runs a dialect's own tests.",
864
+ who: ["platform-lead", "developer"],
865
+ changes: "nothing"
866
+ },
867
+ {
868
+ name: "dialect run",
869
+ usage: "atticus dialect run <name> <repo> [--json]",
870
+ what: "Shows the records a dialect produces from one repo.",
871
+ who: ["platform-lead", "developer"],
872
+ changes: "nothing"
873
+ },
874
+ {
875
+ name: "dialect kit",
876
+ usage: "atticus dialect kit [dir]",
877
+ what: "Exports the dialect build kit for an assistant.",
878
+ who: ["platform-lead"],
879
+ changes: "the folder named"
880
+ },
881
+ {
882
+ name: "change new",
883
+ usage: 'atticus change new "<title>" --type <type>',
884
+ what: "Starts a change from its type's template; it begins in define with a brief to write.",
885
+ who: ["product-owner"],
886
+ changes: "openspec/changes/<id>/"
887
+ },
888
+ {
889
+ name: "next",
890
+ usage: "atticus next [--all] [--json]",
891
+ what: "The next step to take in the metarepo, with the slash command to type in your assistant and why it is next.",
892
+ who: ["product-owner", "architect", "developer", "team-lead"],
893
+ changes: "nothing"
894
+ },
895
+ {
896
+ name: "guide",
897
+ usage: "atticus guide <system>",
898
+ what: "A system's guide, from the catalog: its parts, calls, data and owners, every fact citing code, and what it doesn't know yet.",
899
+ who: ["developer", "architect"],
900
+ changes: "nothing"
901
+ },
902
+ {
903
+ name: "watch",
904
+ usage: "atticus watch [<change>] [--off] [--json]",
905
+ what: "Follow a change without being on it; your digest says how it is doing. With no change, how each one you watch is doing.",
906
+ who: ["product-owner", "team-lead", "delivery-lead"],
907
+ changes: "your own .atticus/me.yaml (never committed)"
908
+ },
909
+ {
910
+ name: "change knowledge",
911
+ usage: "atticus change knowledge <id> [--confirm id] [--exclude id] [--json]",
912
+ what: "Lists the patterns, standards, decisions, processes, requests and wiki and document sections that apply to a change, picked by each of its stages from the feature stage on, with the near-misses; confirm one so later stages pick it, or exclude it so they leave it out.",
913
+ who: ["developer", "business-analyst", "architect"],
914
+ changes: "the change's knowledge.yaml",
915
+ related: ["change status"]
916
+ },
917
+ {
918
+ name: "change status",
919
+ usage: "atticus change status <id> [--json]",
920
+ what: "Where a change is, what it needs next, what blocks it, its risk, the hand-off PR to merge and the tests it needs.",
921
+ who: ["product-owner", "team-lead", "delivery-lead"],
922
+ changes: "nothing"
923
+ },
924
+ {
925
+ name: "change handoff",
926
+ usage: "atticus change handoff <id> --approve",
927
+ what: "Opens the hand-off pull request for the phase (Send to architecture, Approve design, Plan ready, ...); merging it approves the gate. Without --approve it says what it would open. Refused while blocking findings are open.",
928
+ who: ["product-owner", "architect", "team-lead"],
929
+ changes: "a hand-off PR on the Git host and change.yaml",
930
+ approval: true
931
+ },
932
+ {
933
+ name: "change sync",
934
+ usage: "atticus change sync <id> [--approve]",
935
+ what: "Reads the hand-off PR; once it is merged the change moves to the next phase. With --approve it merges a PR that is approved and meets the Git host's rules.",
936
+ who: ["product-owner", "architect", "team-lead"],
937
+ changes: "the PR (merge) and change.yaml",
938
+ approval: true
939
+ },
940
+ {
941
+ name: "change return",
942
+ usage: 'atticus change return <id> --reason "<why>" --approve',
943
+ what: "Sends a change back to the product owner.",
944
+ who: ["architect", "team-lead"],
945
+ changes: "change.yaml and the hand-off PR",
946
+ approval: true
947
+ },
948
+ {
949
+ name: "change archive",
950
+ usage: "atticus change archive <id>",
951
+ what: "Archives a released change after the archive gate passes.",
952
+ who: ["delivery-lead"],
953
+ changes: "the change folder"
954
+ },
955
+ {
956
+ name: "change list",
957
+ usage: "atticus change list [--json]",
958
+ what: "Every change and its state.",
959
+ who: ["team-lead", "delivery-lead"],
960
+ changes: "nothing"
961
+ },
962
+ {
963
+ name: "change conflicts",
964
+ usage: "atticus change conflicts [--json]",
965
+ what: "Open changes that touch the same systems, records or repos.",
966
+ who: ["team-lead", "architect"],
967
+ changes: "nothing"
968
+ },
969
+ {
970
+ name: "critic",
971
+ usage: "atticus critic <change> [--gate g] [--lens l] [--json]",
972
+ what: "Reviews a change at a gate: rule checks first (free, repeatable), then AI lenses on what passed. Findings come with evidence and a fix.",
973
+ who: ["architect", "business-analyst", "product-owner"],
974
+ changes: "review/<gate>/<round>/ finding files"
975
+ },
976
+ {
977
+ name: "critic stats",
978
+ usage: "atticus critic stats [--json]",
979
+ what: "How often each rule's findings are dismissed or waived, with a suggested checklist change.",
980
+ who: ["platform-lead"],
981
+ changes: "nothing"
982
+ },
983
+ {
984
+ name: "findings",
985
+ usage: "atticus findings <change> [--json]",
986
+ what: "The change's findings by gate and round.",
987
+ who: ["architect", "business-analyst"],
988
+ changes: "nothing"
989
+ },
990
+ {
991
+ name: "finding",
992
+ usage: "atticus finding <change> <id> <action>",
993
+ what: "Acts on a finding: apply its fix, edit and apply, not relevant (with a reason), waive (blocking only, with a reason) or ask its owner. It is recorded with you and reaches main with the hand-off PR.",
994
+ who: ["architect", "business-analyst"],
995
+ changes: "the finding file and, for apply, the artifact"
996
+ },
997
+ {
998
+ name: "feature list",
999
+ usage: "atticus feature list [--json]",
1000
+ what: "Features Atticus found in code, named by people or proposed by AI.",
1001
+ who: ["product-owner", "designer", "business-analyst"],
1002
+ changes: "nothing"
1003
+ },
1004
+ {
1005
+ name: "feature show",
1006
+ usage: "atticus feature show <id> [--json]",
1007
+ what: "A feature's entry points, flow, owner, links and gaps.",
1008
+ who: ["product-owner", "designer"],
1009
+ changes: "nothing"
1010
+ },
1011
+ {
1012
+ name: "feature name",
1013
+ usage: 'atticus feature name <id> "<name>"',
1014
+ what: "Confirms or corrects a feature's name in the words its users use.",
1015
+ who: ["product-owner", "designer"],
1016
+ changes: "a curated decision in catalog/curated"
1017
+ },
1018
+ {
1019
+ name: "feature tag",
1020
+ usage: "atticus feature tag <id> <tags...>",
1021
+ what: "Tags a feature with business areas.",
1022
+ who: ["product-owner"],
1023
+ changes: "a curated decision"
1024
+ },
1025
+ {
1026
+ name: "feature merge",
1027
+ usage: "atticus feature merge <into> <from...>",
1028
+ what: "Merges features that are really one.",
1029
+ who: ["product-owner", "architect"],
1030
+ changes: "a curated decision"
1031
+ },
1032
+ {
1033
+ name: "feature split",
1034
+ usage: "atticus feature split <id> <entry-points...>",
1035
+ what: "Splits entry points into their own feature.",
1036
+ who: ["product-owner", "architect"],
1037
+ changes: "a curated decision"
1038
+ },
1039
+ {
1040
+ name: "feature link",
1041
+ usage: "atticus feature link <id> <ref>",
1042
+ what: "Links a feature to a work item or page.",
1043
+ who: ["product-owner"],
1044
+ changes: "a curated decision"
1045
+ },
1046
+ {
1047
+ name: "feature suggest-links",
1048
+ usage: "atticus feature suggest-links [--json]",
1049
+ what: "Suggests links between features and work items.",
1050
+ who: ["product-owner"],
1051
+ changes: "nothing"
1052
+ },
1053
+ {
1054
+ name: "feature spec",
1055
+ usage: "atticus feature spec <id>",
1056
+ what: "Drafts an OpenSpec capability spec from a confirmed feature.",
1057
+ who: ["product-owner", "business-analyst"],
1058
+ changes: "a spec draft"
1059
+ },
1060
+ {
1061
+ name: "feature suggest-names",
1062
+ usage: "atticus feature suggest-names [--mode m]",
1063
+ what: "Asks AI to propose names for detected features; they stay proposals until a person confirms them.",
1064
+ who: ["product-owner"],
1065
+ changes: "catalog/proposals"
1066
+ },
1067
+ {
1068
+ name: "catalog migrate",
1069
+ usage: "atticus catalog migrate [--dry-run]",
1070
+ what: "Rewrites catalog files to the current schema version.",
1071
+ who: ["platform-lead"],
1072
+ changes: "catalog files"
1073
+ },
1074
+ {
1075
+ name: "task run",
1076
+ usage: "atticus task run <change> <task> [--level plan|draft|pr] [--approve]",
1077
+ what: "Runs a task with AI: a plan only, a draft in the worktree, or a pull request. AI never pushes to protected branches or deploys.",
1078
+ who: ["developer"],
1079
+ changes: "the task's worktree or a task branch",
1080
+ approval: true
1081
+ },
1082
+ {
1083
+ name: "task",
1084
+ usage: "atticus task <change> <task> [--level plan|draft|pr] [--approve]",
1085
+ what: "Runs one task of a change with your assistant: a plan only, a draft in the worktree, or a pull request. The same command as atticus task run; atticus task context <change> <task> gives the task's context.",
1086
+ who: ["developer"],
1087
+ changes: "the task's worktree or a task branch",
1088
+ approval: true,
1089
+ related: ["task run", "stories"]
1090
+ },
1091
+ {
1092
+ name: "spec hand-down",
1093
+ usage: "atticus spec hand-down <change> <task>",
1094
+ what: "Creates the task's Spec Kit feature in its repo from the approved design.",
1095
+ who: ["developer"],
1096
+ changes: "the repo's specs/ folder"
1097
+ },
1098
+ {
1099
+ name: "spec hand-up",
1100
+ usage: "atticus spec hand-up <change> <task>",
1101
+ what: "Reads the Spec Kit feature back at the pull request and compares it with the design.",
1102
+ who: ["developer", "architect"],
1103
+ changes: "the change's drift record"
1104
+ },
1105
+ {
1106
+ name: "drift",
1107
+ usage: "atticus drift <change> [--json]",
1108
+ what: "Compares the design, each repo's plan and the code.",
1109
+ who: ["architect", "developer"],
1110
+ changes: "nothing"
1111
+ },
1112
+ {
1113
+ name: "assistant-files",
1114
+ usage: "atticus assistant-files [--repo id] [--check]",
1115
+ what: "Generates Copilot and Claude Code files from one source.",
1116
+ who: ["platform-lead", "developer"],
1117
+ changes: "assistant files in the repo"
1118
+ },
1119
+ {
1120
+ name: "plan stories",
1121
+ usage: "atticus plan stories <change>",
1122
+ what: "Writes stories, the story index and tasks from the approved design.",
1123
+ who: ["business-analyst", "team-lead"],
1124
+ changes: "stories/, stories.md, tasks.md"
1125
+ },
1126
+ {
1127
+ name: "stories",
1128
+ usage: "atticus stories <change> [--draft] [--mode m]",
1129
+ what: "The stories stage, one of the twelve commands atticus help shows: your assistant writes the change's stories with their acceptance criteria and tasks from the design, every one checked. It is the same command as atticus plan stories.",
1130
+ who: ["business-analyst", "team-lead"],
1131
+ changes: "stories/, stories.md, tasks.md",
1132
+ related: ["plan stories", "task"]
1133
+ },
1134
+ {
1135
+ name: "plan snow",
1136
+ usage: "atticus plan snow <change>",
1137
+ what: "Drafts the service-desk requests the change needs (firewall rules, gateway proxies).",
1138
+ who: ["team-lead", "business-analyst"],
1139
+ changes: "snow.md"
1140
+ },
1141
+ {
1142
+ name: "plan bar",
1143
+ usage: "atticus plan bar <change> [--eval file]",
1144
+ what: "Measures the stories against the story bar: ready, gaps covered, nothing invented.",
1145
+ who: ["business-analyst"],
1146
+ changes: "nothing"
1147
+ },
1148
+ {
1149
+ name: "plan submit",
1150
+ usage: "atticus plan submit <change> [--approve]",
1151
+ what: "Files the drafted service-desk requests.",
1152
+ who: ["team-lead"],
1153
+ changes: "the service desk (or manual steps)",
1154
+ approval: true
1155
+ },
1156
+ {
1157
+ name: "ado sync",
1158
+ usage: "atticus ado sync <change> [--approve]",
1159
+ what: "Creates or updates work items for the stories and tasks.",
1160
+ who: ["team-lead", "business-analyst"],
1161
+ changes: "the work tracker (or manual steps)",
1162
+ approval: true
1163
+ },
1164
+ {
1165
+ name: "templates draft",
1166
+ usage: "atticus templates draft [--no-ai]",
1167
+ what: "Drafts request and work-item templates from wikis, uploads and past tickets, as a proposal.",
1168
+ who: ["platform-lead"],
1169
+ changes: "proposals/templates"
1170
+ },
1171
+ {
1172
+ name: "todo",
1173
+ usage: "atticus todo [--change id] [--all]",
1174
+ what: "Manual steps waiting for a person: things Atticus can't do in a system it can't reach.",
1175
+ who: ["team-lead", "delivery-lead", "developer"],
1176
+ changes: "nothing",
1177
+ related: ["todo done"]
1178
+ },
1179
+ {
1180
+ name: "todo show",
1181
+ usage: "atticus todo show <step> [--export text|csv|import]",
1182
+ what: "One manual step with every field ready to copy.",
1183
+ who: ["team-lead"],
1184
+ changes: "nothing"
1185
+ },
1186
+ {
1187
+ name: "todo done",
1188
+ usage: "atticus todo done <step> [--result k=v]...",
1189
+ what: "Records a manual step's result (the new id, the PR URL) so the workflow carries on.",
1190
+ who: ["team-lead", "delivery-lead", "developer"],
1191
+ changes: "the step file"
1192
+ },
1193
+ {
1194
+ name: "todo run",
1195
+ usage: "atticus todo run <step> --with <executor>",
1196
+ what: "Runs a manual step with a team's own tool, after a person approves the exact inputs.",
1197
+ who: ["platform-lead"],
1198
+ changes: "the outside system, through the executor",
1199
+ approval: true
1200
+ },
1201
+ {
1202
+ name: "executors check",
1203
+ usage: "atticus executors check",
1204
+ what: "Pings configured executors and shows what each covers.",
1205
+ who: ["platform-lead"],
1206
+ changes: "nothing"
1207
+ },
1208
+ {
1209
+ name: "init",
1210
+ usage: "atticus init <name> --platform <p>",
1211
+ what: "Creates a new metarepo with the default org pack written out as files.",
1212
+ who: ["platform-lead"],
1213
+ changes: "a new folder"
1214
+ },
1215
+ {
1216
+ name: "adopt",
1217
+ usage: "atticus adopt <path>",
1218
+ what: "Uses Atticus in an existing repo; writes only .atticus/ and changes nothing else.",
1219
+ who: ["platform-lead", "developer"],
1220
+ changes: ".atticus/ in the repo"
1221
+ },
1222
+ {
1223
+ name: "unadopt",
1224
+ usage: "atticus unadopt <path>",
1225
+ what: "Removes Atticus from an adopted repo, leaving it as it was.",
1226
+ who: ["platform-lead"],
1227
+ changes: "removes .atticus/"
1228
+ },
1229
+ {
1230
+ name: "repos discover",
1231
+ usage: "atticus repos discover <folder>",
1232
+ what: "Proposes repos.manifest.yaml entries for the repos in a folder.",
1233
+ who: ["platform-lead"],
1234
+ changes: "the manifest, or a proposal"
1235
+ },
1236
+ {
1237
+ name: "absorb",
1238
+ usage: "atticus absorb <git-url|path> --id <id>",
1239
+ what: "Translates another team's prompts, agents, checklists or Spec Kit into an org-pack proposal; never applies it.",
1240
+ who: ["platform-lead"],
1241
+ changes: "a proposal folder"
1242
+ },
1243
+ {
1244
+ name: "tests generate",
1245
+ usage: "atticus tests generate <change> <story>",
1246
+ what: "Generates tests from a story's acceptance criteria and the API spec.",
1247
+ who: ["qa", "developer"],
1248
+ changes: "test files"
1249
+ },
1250
+ {
1251
+ name: "trace",
1252
+ usage: "atticus trace <change> [--json] [--csv]",
1253
+ what: "Links each requirement to its stories, tests, code, records and feature; the gaps are findings.",
1254
+ who: ["qa", "delivery-lead"],
1255
+ changes: "the trace matrix and trace gate findings"
1256
+ },
1257
+ {
1258
+ name: "ci drift",
1259
+ usage: "atticus ci drift [repo] --catalog <path>",
1260
+ what: "In a repo's pipeline: compares the repo with the committed catalog; planned changes are not drift.",
1261
+ who: ["platform-lead", "developer"],
1262
+ changes: "nothing"
1263
+ },
1264
+ {
1265
+ name: "ci snippet",
1266
+ usage: "atticus ci snippet <github|azure|gitlab|jenkins>",
1267
+ what: "Prints the pipeline template for the drift check.",
1268
+ who: ["platform-lead"],
1269
+ changes: "nothing"
1270
+ },
1271
+ {
1272
+ name: "ci archive-gate",
1273
+ usage: "atticus ci archive-gate <change>",
1274
+ what: "Says whether a change may be archived: released, no open blocking findings, PRs merged, no drift.",
1275
+ who: ["delivery-lead"],
1276
+ changes: "nothing"
1277
+ },
1278
+ {
1279
+ name: "glossary list",
1280
+ usage: "atticus glossary list [--json]",
1281
+ what: "Business terms from wikis, code and people.",
1282
+ who: ["business-analyst", "product-owner"],
1283
+ changes: "nothing"
1284
+ },
1285
+ {
1286
+ name: "glossary define",
1287
+ usage: 'atticus glossary define <term> "<definition>"',
1288
+ what: "Defines a business term.",
1289
+ who: ["business-analyst", "product-owner"],
1290
+ changes: "a curated decision"
1291
+ },
1292
+ {
1293
+ name: "orgpack validate",
1294
+ usage: "atticus orgpack validate [dir]",
1295
+ what: "Checks the org pack against its schemas.",
1296
+ who: ["platform-lead"],
1297
+ changes: "nothing"
1298
+ },
1299
+ {
1300
+ name: "openspec setup",
1301
+ usage: "atticus openspec setup",
1302
+ what: "Sets up OpenSpec with the org-flow schema in the metarepo.",
1303
+ who: ["platform-lead"],
1304
+ changes: "openspec/"
1305
+ },
1306
+ {
1307
+ name: "handoff done",
1308
+ usage: "atticus handoff done <run-id>",
1309
+ what: "Tells Atticus the assistant has written a hand-off artifact; Atticus checks it.",
1310
+ who: ["developer", "business-analyst"],
1311
+ changes: "the run's state"
1312
+ },
1313
+ {
1314
+ name: "workspace sync",
1315
+ usage: "atticus workspace sync [--repos a,b]",
1316
+ what: "Clones or updates the base clone of every repo.",
1317
+ who: ["developer"],
1318
+ changes: "workspace/"
1319
+ },
1320
+ {
1321
+ name: "workspace open",
1322
+ usage: "atticus workspace open <change> --repos a,b",
1323
+ what: "Creates the change's worktrees and editor workspaces.",
1324
+ who: ["developer"],
1325
+ changes: "workspace/.worktrees"
1326
+ },
1327
+ {
1328
+ name: "workspace prune",
1329
+ usage: "atticus workspace prune <change>",
1330
+ what: "Removes worktrees whose PRs merged or closed.",
1331
+ who: ["developer"],
1332
+ changes: "workspace/.worktrees"
1333
+ },
1334
+ {
1335
+ name: "initiative new",
1336
+ usage: 'atticus initiative new "<title>" --journey <journey>',
1337
+ what: "Starts an initiative: changes across teams and vendors toward one outcome, on a guided journey.",
1338
+ who: ["product-owner", "team-lead", "delivery-lead"],
1339
+ changes: "an Initiative record in catalog/curated"
1340
+ },
1341
+ {
1342
+ name: "initiative plan",
1343
+ usage: "atticus initiative plan <id> --target <system> [--release YYYY-MM-DD]",
1344
+ what: "Plans the journey back from the target release: the changes each system needs and the commitments of other teams and vendors, with dates from their lead times.",
1345
+ who: ["team-lead", "delivery-lead"],
1346
+ changes: "the plan and Commitment records"
1347
+ },
1348
+ {
1349
+ name: "initiative status",
1350
+ usage: "atticus initiative status <id> [--json]",
1351
+ what: "The critical path and the commitments that slipped or are at risk, from dates and change states.",
1352
+ who: ["team-lead", "delivery-lead", "product-owner"],
1353
+ changes: "nothing"
1354
+ },
1355
+ {
1356
+ name: "initiative request",
1357
+ usage: "atticus initiative request <id> <party>",
1358
+ what: "Sends a team or vendor a one-page system card to correct and answer; the reply goes into intake and is tracked as a commitment.",
1359
+ who: ["team-lead", "architect"],
1360
+ changes: "the card, a Commitment and a manual step to send it"
1361
+ },
1362
+ {
1363
+ name: "learn",
1364
+ usage: "atticus learn [list]",
1365
+ what: "Lists the lessons for each role and your progress.",
1366
+ who: ["everyone"],
1367
+ changes: "nothing"
1368
+ },
1369
+ {
1370
+ name: "learn start",
1371
+ usage: "atticus learn start <role|lesson>",
1372
+ what: "Starts a lesson in the practice sandbox.",
1373
+ who: ["everyone"],
1374
+ changes: "the sandbox's progress file"
1375
+ },
1376
+ {
1377
+ name: "learn check",
1378
+ usage: "atticus learn check [--answer text]",
1379
+ what: "Checks the step you are on and moves to the next one when it passes.",
1380
+ who: ["everyone"],
1381
+ changes: "the sandbox's progress file"
1382
+ },
1383
+ {
1384
+ name: "sandbox create",
1385
+ usage: "atticus sandbox create <dir>",
1386
+ what: "Makes a throwaway practice metarepo from the invented Forge ecosystem.",
1387
+ who: ["everyone"],
1388
+ changes: "the folder named"
1389
+ },
1390
+ {
1391
+ name: "explain",
1392
+ usage: "atticus explain <id|command>",
1393
+ what: "Explains a record, finding, change or command in plain words.",
1394
+ who: ["everyone"],
1395
+ changes: "nothing"
1396
+ },
1397
+ {
1398
+ name: "legacy",
1399
+ usage: "atticus legacy [<command...>] [--all]",
1400
+ what: "Lists every command outside the twelve atticus help shows, grouped by area. atticus legacy <command> runs one, exactly as atticus <command> does: the old names keep working.",
1401
+ who: ["everyone"],
1402
+ changes: "nothing (the command it runs may)"
1403
+ },
1404
+ {
1405
+ name: "ui",
1406
+ usage: "atticus ui [--port n] [--open] [--full]",
1407
+ what: "Starts the app on this machine (127.0.0.1 only): Home, Map, Change, Questions, Knowledge and Set up. Ctrl+C stops it.",
1408
+ who: ["everyone"],
1409
+ changes: "nothing by itself; what you do in the app writes metarepo files, never Git"
1410
+ },
1411
+ {
1412
+ name: "eval run",
1413
+ usage: "atticus eval run [--agent a] [--mode mock|replay|live] [--runs n]",
1414
+ what: "Runs the AI agents' evaluation cases and grades them; live mode needs a configured model and a person running it.",
1415
+ who: ["platform-lead"],
1416
+ changes: ".evals/ results"
1417
+ },
1418
+ {
1419
+ name: "eval report",
1420
+ usage: "atticus eval report [--json]",
1421
+ what: "Pass rates per agent, the 4-of-5 live bar and regressions since the last run.",
1422
+ who: ["platform-lead"],
1423
+ changes: "nothing"
1424
+ },
1425
+ {
1426
+ name: "defences scan",
1427
+ usage: "atticus defences scan [path]",
1428
+ what: "Scans files for text that tries to instruct an AI (hidden, encoded or plain) and says what Atticus would do with each.",
1429
+ who: ["platform-lead", "architect"],
1430
+ changes: "nothing"
1431
+ }
1432
+ ];
1433
+ function commandNamed(text) {
1434
+ const words2 = text.trim().replace(/^atticus\s+/, "").split(/\s+/).filter((w) => w && !w.startsWith("-"));
1435
+ for (let n = Math.min(words2.length, 3); n > 0; n--) {
1436
+ const name = words2.slice(0, n).join(" ");
1437
+ const hit = COMMANDS.find((c) => c.name === name);
1438
+ if (hit)
1439
+ return hit;
1440
+ }
1441
+ return void 0;
1442
+ }
1443
+
1444
+ // packages/learn/dist/explain.js
1445
+ var import_yaml3 = __toESM(require_dist(), 1);
1446
+ import { readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
1447
+ import { join as join5, relative as relative2, sep as sep2 } from "node:path";
1448
+
1449
+ // packages/learn/dist/glossary.js
1450
+ var PRODUCT_TERMS = [
1451
+ {
1452
+ term: "metarepo",
1453
+ aliases: ["meta repo", "meta-repo"],
1454
+ says: "The Git repo Atticus keeps for your estate: the catalog read from your repos' code, the changes in flight, the knowledge and the org pack. Its pull requests are how things get approved."
1455
+ },
1456
+ {
1457
+ term: "org pack",
1458
+ aliases: ["org-pack", "orgpack"],
1459
+ says: "Your organisation's settings for Atticus, in org-pack/: the repos and their types, the change types, the checklists and how AI runs here, changed through a pull request."
1460
+ },
1461
+ {
1462
+ term: "catalog",
1463
+ aliases: [],
1464
+ says: "What Atticus read from your code: systems, components, endpoints, tables, settings and how they connect, each citing the file and line it came from."
1465
+ },
1466
+ {
1467
+ term: "estate",
1468
+ aliases: [],
1469
+ says: "All your systems and the repos behind them, as the catalog describes them."
1470
+ },
1471
+ {
1472
+ term: "flow",
1473
+ aliases: [],
1474
+ says: "One path through your code from a trigger (an endpoint, a job, a message) to what it reads, writes and returns, step by step, read from the code."
1475
+ },
1476
+ {
1477
+ term: "change",
1478
+ aliases: [],
1479
+ says: "A piece of work from idea to release, kept in the metarepo with its brief, its design, its stories and its tasks."
1480
+ },
1481
+ {
1482
+ term: "target flow",
1483
+ aliases: ["target flows"],
1484
+ says: "How a flow will run once a change is in: the steps it adds, changes or removes, with what each reads and writes."
1485
+ },
1486
+ {
1487
+ term: "hand-off",
1488
+ aliases: ["handoff", "hand-off pr", "handoff pr", "the pr"],
1489
+ says: "The pull request in the metarepo that ends a phase of a change; merging it approves the phase's gate."
1490
+ },
1491
+ {
1492
+ term: "gate",
1493
+ aliases: [],
1494
+ says: "The point between two phases of a change where its findings are checked and a person approves, by merging the hand-off pull request."
1495
+ },
1496
+ {
1497
+ term: "finding",
1498
+ aliases: ["findings"],
1499
+ says: "Something the critic or a check raised about a change (blocking, a risk, a gap or an idea), citing where it is; a person decides what to do with it."
1500
+ },
1501
+ {
1502
+ term: "critic",
1503
+ aliases: [],
1504
+ says: "The AI reviewer that reads a change at a gate, one lens at a time, and raises findings."
1505
+ },
1506
+ {
1507
+ term: "lens",
1508
+ aliases: ["lenses"],
1509
+ says: "One way the critic reads a change, such as security, data or architecture, each with its own rules."
1510
+ },
1511
+ {
1512
+ term: "checking round",
1513
+ aliases: [],
1514
+ says: "One more critic round after a gate's rounds are used, which reviews only what a person fixed."
1515
+ },
1516
+ {
1517
+ term: "question",
1518
+ aliases: ["questions"],
1519
+ says: "Something Atticus asked a person because the code doesn't say, with options and the lines it read; the answer is kept in the metarepo for the steps that read it."
1520
+ },
1521
+ {
1522
+ term: "knowledge item",
1523
+ aliases: ["knowledge items", "knowledge"],
1524
+ says: "A fact read from a wiki or a document you added (a process, a standard, a decision, a team), quoted with its page, until a person verifies it."
1525
+ },
1526
+ {
1527
+ term: "need",
1528
+ aliases: ["needs"],
1529
+ says: "Something a change needs from the organisation, such as a firewall rule or an access request, with how to ask for it and who handles it."
1530
+ },
1531
+ {
1532
+ term: "dialect",
1533
+ aliases: ["dialects"],
1534
+ says: "The rules that tell Atticus's scanner how to read one framework or language, your own house frameworks included."
1535
+ },
1536
+ {
1537
+ term: "base clone",
1538
+ aliases: ["base clones"],
1539
+ says: "Atticus's own read-only copy of a repo, in the metarepo's workspace/, which the scans read; it never touches your working copy."
1540
+ },
1541
+ {
1542
+ term: "slash command",
1543
+ aliases: ["slash commands"],
1544
+ says: "A command you type in your assistant's chat, such as /atticus-next, that runs one Atticus step with the assistant's model."
1545
+ },
1546
+ {
1547
+ term: "service desk",
1548
+ aliases: ["snow", "servicenow"],
1549
+ says: "Your operations ticketing system, where a change's release and access requests are raised."
1550
+ }
1551
+ ];
1552
+ var fold = (s) => s.trim().toLowerCase().replace(/[-_\s]+/g, " ");
1553
+ function termNamed(word) {
1554
+ const w = fold(word);
1555
+ return PRODUCT_TERMS.find((t) => fold(t.term) === w || t.aliases.some((a) => fold(a) === w));
1556
+ }
1557
+
1558
+ // packages/learn/dist/explain.js
1559
+ var KIND_TEXT = {
1560
+ System: "a system: a product or service people know by name, made of components",
1561
+ Component: "a component: one deployable part of a system, usually one repo or service",
1562
+ Endpoint: "an endpoint: something a component offers (an API operation, a topic, a queue, a job)",
1563
+ Interface: "an interface: one component calling or messaging another",
1564
+ GatewayRoute: "a gateway route: a proxy on the API gateway that forwards calls to a component",
1565
+ Schema: "a schema: the shape of a message or payload",
1566
+ FieldMapping: "a field mapping: how a field on one side of an interface becomes a field on the other",
1567
+ DataStore: "a data store: a database or other store a component owns",
1568
+ Table: "a table: a table or collection in a data store",
1569
+ Column: "a column: one field of a table",
1570
+ Migration: "a migration: a script that changes a data store's structure",
1571
+ Setting: "a setting: a configuration value of a component",
1572
+ Deployment: "a deployment: where and how a component runs in one environment",
1573
+ ObservabilityAsset: "an observability asset: an alert, dashboard or log query",
1574
+ AccessGrant: "an access grant: who may call or read what",
1575
+ Obligation: "an obligation: a promise such as a service level or a retention rule",
1576
+ SourceDocument: "a source document: a document Atticus took in as data",
1577
+ Party: "a party: an internal team or a vendor Atticus works with",
1578
+ Initiative: "an initiative: several changes across teams and vendors toward one outcome",
1579
+ Commitment: "a commitment: something a party promised to deliver by a date",
1580
+ OwnerQuestion: "an owner question: a question to a system's owner that closes a knowledge gap",
1581
+ ProcessArtifact: "a process artifact: a ticket, work item or change linked to the catalog",
1582
+ Capability: "a feature: what a set of entry points adds up to for its users",
1583
+ Unresolved: "a loose end: a call or access found on one side only, not yet matched",
1584
+ CuratedDecision: "a curated decision: a person's decision replayed over the catalog on every scan",
1585
+ GlossaryTerm: "a glossary term: a business word and what it means"
1586
+ };
1587
+ var CONFIDENCE = {
1588
+ verified: "verified: read directly from code, config or a published spec",
1589
+ inferred: "inferred: seen from one side only, or read from a diagram or document",
1590
+ asserted: "asserted: stated or confirmed by a person"
1591
+ };
1592
+ var SEVERITY = {
1593
+ blocking: "blocking: it stops the hand-off until it is fixed or, where allowed, waived with a reason",
1594
+ gap: "a knowledge gap: a fact only a person can give; it needs an answer or a recorded reason",
1595
+ risk: "a risk: worth knowing, never blocks",
1596
+ idea: "an idea: a suggestion, never blocks"
1597
+ };
1598
+ var ACTIONS = {
1599
+ issue: "apply the fix, edit and apply, mark not relevant with a reason, or (if blocking and waivable) waive it with a reason",
1600
+ "knowledge-gap": "ask the owner the question, or edit and apply the answer"
1601
+ };
1602
+ var STATE_TEXT = {
1603
+ draft: "being drafted",
1604
+ define: "in Define: the product owner writes the brief, proposal and specs",
1605
+ design: "in Design: the architect designs interfaces, data and the solution",
1606
+ plan: "in Plan: stories, tasks and service-desk requests are written",
1607
+ build: "in Build: the code is written in each repo",
1608
+ qa: "in QA: testing before release",
1609
+ "release-ready": "ready for the go/no-go decision",
1610
+ released: "released",
1611
+ "post-release-review": "released, with the skipped reviews to run after the fact",
1612
+ returned: "returned to the product owner",
1613
+ archived: "archived"
1614
+ };
1615
+ var words = (r, keys) => keys.filter((k) => r[k] !== void 0 && r[k] !== "" && !(Array.isArray(r[k]) && r[k].length === 0));
1616
+ function recordLines(r, all) {
1617
+ const x = r;
1618
+ const lines = [];
1619
+ const seeAlso = [];
1620
+ switch (r.kind) {
1621
+ case "System":
1622
+ lines.push(`${r.name} is a ${r.boundary} system${r.owner ? ` owned by ${r.owner}` : " with no known owner"}.`);
1623
+ if (r.repos.length)
1624
+ lines.push(`Its code is in ${r.repos.join(", ")}.`);
1625
+ break;
1626
+ case "Component":
1627
+ lines.push(`${r.id} is a ${r.type} in system ${r.system}${r.repo ? `, in repo ${r.repo}` : ""}.`);
1628
+ break;
1629
+ case "Interface":
1630
+ lines.push(`${r.from} calls ${r.to}${r.method ? ` with ${r.method}` : ""} ${r.normalizedPath}, ${r.via === "gateway" ? "through the gateway" : r.via === "direct" ? "directly" : `over ${r.via}`}, ${r.sync === "sync" ? "waiting for the answer" : "without waiting (async)"}.`);
1631
+ if (r.pattern)
1632
+ lines.push(`Its integration pattern is ${r.pattern.code} (${r.pattern.source}).`);
1633
+ else
1634
+ lines.push("No integration pattern is recorded for it.");
1635
+ if (r.conflicts.length)
1636
+ lines.push(`Sources disagree about ${r.conflicts.map((c) => c.field).join(", ")}.`);
1637
+ break;
1638
+ case "Endpoint":
1639
+ lines.push(`${r.component} offers ${r.method ? `${r.method} ` : ""}${r.pathOrTopic} (${r.endpointType}).`);
1640
+ break;
1641
+ case "Table":
1642
+ lines.push(`${r.name} is a ${r.tableKind} in ${r.dataStore}, owned by ${r.ownerComponent ?? "an unknown component"}.`);
1643
+ if (r.writers.length)
1644
+ lines.push(`Written by ${r.writers.join(", ")}.`);
1645
+ if (r.readers.length)
1646
+ lines.push(`Read by ${r.readers.join(", ")}.`);
1647
+ break;
1648
+ case "DataStore":
1649
+ lines.push(`${r.id} is a ${r.storeType} store owned by ${r.ownerComponent ?? "an unknown component"}.`);
1650
+ break;
1651
+ case "Party":
1652
+ lines.push(`${r.name} is ${r.partyKind === "vendor" ? "a vendor" : "an internal team"}${r.owner ? ` (${r.owner})` : ""}.`);
1653
+ for (const [k, v] of Object.entries(r.typicalLeadTimes))
1654
+ lines.push(`A ${k} usually takes ${v}.`);
1655
+ break;
1656
+ case "Initiative":
1657
+ lines.push(`"${r.title}" follows the ${r.journey} journey${r.targetRelease ? `, aiming to release on ${r.targetRelease}` : ""}.`);
1658
+ if (r.changes.length)
1659
+ lines.push(`Its changes: ${r.changes.join(", ")}.`);
1660
+ if (r.parties.length)
1661
+ lines.push(`It depends on ${r.parties.join(", ")}.`);
1662
+ break;
1663
+ case "Commitment":
1664
+ lines.push(`${r.party} committed to: ${r.what}, by ${r.neededBy} (${r.commitmentStatus}).`);
1665
+ if (r.blocks.length)
1666
+ lines.push(`Until then it blocks ${r.blocks.join(", ")}.`);
1667
+ break;
1668
+ case "OwnerQuestion":
1669
+ lines.push(`${r.owner} was asked (${r.questionState}): ${r.question}`);
1670
+ break;
1671
+ case "Capability":
1672
+ lines.push(`"${r.name}" starts at ${r.entryPoints.join(", ") || "no known entry point"} and reaches ${r.records.length} record${r.records.length === 1 ? "" : "s"}.`);
1673
+ break;
1674
+ case "CuratedDecision":
1675
+ lines.push(`${r.by} decided to ${r.action} ${r.target} on ${r.at.slice(0, 10)}: ${r.reason}`);
1676
+ break;
1677
+ default: {
1678
+ const keys = words(x, ["name", "title", "component", "system", "owner"]);
1679
+ if (keys.length)
1680
+ lines.push(keys.map((k) => `${k}: ${String(x[k])}`).join("; ") + ".");
1681
+ }
1682
+ }
1683
+ const refs = recordReferences(r);
1684
+ if (refs.length) {
1685
+ const ids = [...new Set(refs.map((f) => f.id))];
1686
+ lines.push(`It refers to ${ids.slice(0, 8).join(", ")}${ids.length > 8 ? ` and ${ids.length - 8} more` : ""}.`);
1687
+ seeAlso.push(...ids.slice(0, 5));
1688
+ }
1689
+ const back = all.filter((o) => o !== r && recordReferences(o).some((f) => f.id === r.id));
1690
+ if (back.length) {
1691
+ const ids = [...new Set(back.map((o) => o.id))];
1692
+ lines.push(`${ids.length} record${ids.length === 1 ? " refers" : "s refer"} to it, such as ${ids.slice(0, 5).join(", ")}.`);
1693
+ seeAlso.push(`atticus impact ${r.id}`);
1694
+ }
1695
+ return { lines, seeAlso };
1696
+ }
1697
+ async function findFinding(root, id) {
1698
+ const changes = join5(root, "openspec", "changes");
1699
+ const walk2 = async (d) => {
1700
+ let entries;
1701
+ try {
1702
+ entries = await readdir4(d, { withFileTypes: true });
1703
+ } catch {
1704
+ return void 0;
1705
+ }
1706
+ for (const e of entries.sort((a, b) => a.name < b.name ? 1 : -1)) {
1707
+ const full = join5(d, e.name);
1708
+ if (e.isDirectory()) {
1709
+ const hit = await walk2(full);
1710
+ if (hit)
1711
+ return hit;
1712
+ } else if (e.name === `${id}.yaml` && full.split(sep2).includes("review")) {
1713
+ const r = Finding.safeParse((0, import_yaml3.parse)(await readFile5(full, "utf8")));
1714
+ if (r.success)
1715
+ return { path: relative2(root, full).split(sep2).join("/"), finding: r.data };
1716
+ }
1717
+ }
1718
+ return void 0;
1719
+ };
1720
+ return walk2(changes);
1721
+ }
1722
+ var EXPLAINER = AgentDefinition.parse({
1723
+ schemaVersion: 1,
1724
+ id: "explainer",
1725
+ role: "developer",
1726
+ description: "Rewords an Atticus explanation in plain words for a newcomer, adding nothing that isn't in it.",
1727
+ inputs: ["catalog-slice"],
1728
+ outputArtifact: "proposal",
1729
+ tokenBudget: 2e4,
1730
+ allowedRuntimes: ["model", "mock", "replay", "hand-off"]
1731
+ });
1732
+ async function explain(root, subject, o = {}) {
1733
+ const s = subject.trim();
1734
+ let out;
1735
+ const cmd = /\s/.test(s) || /^atticus\b/.test(s) ? commandNamed(s) : void 0;
1736
+ if (cmd)
1737
+ out = commandExplanation(s, cmd);
1738
+ if (!out) {
1739
+ const dir = catalogDirOf(root);
1740
+ const records = [
1741
+ ...(await readCatalog(join5(dir, "generated"), { cacheRoot: root })).records,
1742
+ ...(await readCatalog(join5(dir, "curated"), { cacheRoot: root })).records
1743
+ ];
1744
+ const hits = records.filter((r) => r.id === s);
1745
+ if (hits.length) {
1746
+ const r = hits[0];
1747
+ const { lines, seeAlso } = recordLines(r, records);
1748
+ out = {
1749
+ subject: s,
1750
+ kind: "record",
1751
+ title: `${s} is ${KIND_TEXT[r.kind] ?? `a ${r.kind} record`}.`,
1752
+ lines: [
1753
+ ...lines,
1754
+ `Its confidence is ${CONFIDENCE[r.confidence] ?? r.confidence}.`,
1755
+ `Evidence: ${[...new Set(r.evidence.map((e) => e.source === "manual" ? `stated by ${e.by ?? "a person"}` : `${e.source}${e.path ? ` ${e.repo ? `${e.repo}/` : ""}${e.path}${e.line ? `:${e.line}` : ""}` : ""}`))].slice(0, 4).join("; ")}.`,
1756
+ ...hits.length > 1 ? [
1757
+ `${hits.length - 1} other record${hits.length > 2 ? "s have" : " has"} the same id (${hits.slice(1).map((h) => h.kind).join(", ")}).`
1758
+ ] : []
1759
+ ],
1760
+ seeAlso
1761
+ };
1762
+ }
1763
+ }
1764
+ if (!out) {
1765
+ const c = await changeStore(root).tryRead(s).catch(() => void 0);
1766
+ if (c)
1767
+ out = {
1768
+ subject: s,
1769
+ kind: "change",
1770
+ title: `${s} is a change: "${c.title}" (${c.type}).`,
1771
+ lines: [
1772
+ `It is ${STATE_TEXT[c.state] ?? c.state}.`,
1773
+ ...c.systems.length ? [`It touches ${c.systems.join(", ")}.`] : [],
1774
+ ...c.history.length ? [
1775
+ `Last move: ${c.history.at(-1).from} to ${c.history.at(-1).to} by ${c.history.at(-1).by}.`
1776
+ ] : []
1777
+ ],
1778
+ seeAlso: [`atticus change status ${s}`, `atticus findings ${s}`]
1779
+ };
1780
+ }
1781
+ if (!out) {
1782
+ const f = await findFinding(root, s);
1783
+ if (f) {
1784
+ const x = f.finding;
1785
+ out = {
1786
+ subject: s,
1787
+ kind: "finding",
1788
+ title: `${s} is a critic finding on change ${x.change} at the ${x.gate} gate (round ${x.round}).`,
1789
+ lines: [
1790
+ `It says: ${x.summary}`,
1791
+ `Why it matters: ${x.why}`,
1792
+ `It is ${SEVERITY[x.severity] ?? x.severity}.`,
1793
+ `Rule ${x.rule} (${x.layer} layer, ${x.lens} lens) raised it about ${x.artifact}${x.locator.line ? ` line ${x.locator.line}` : ""}.`,
1794
+ `Evidence: ${x.evidence.map((e) => e.quote ? `"${e.quote}"` : e.record ?? e.path ?? "").filter(Boolean).slice(0, 2).join("; ")}.`,
1795
+ `Fix: ${x.fix.text}`,
1796
+ ...x.question ? [`Question for ${x.question.owner}: ${x.question.text}`] : [],
1797
+ x.state === "open" ? `It is open. You can ${ACTIONS[x.kind]}${x.waivable ? "" : "; it can't be waived"}.` : `It is ${x.state}${x.resolution ? ` (by ${x.resolution.by})` : ""}.`
1798
+ ],
1799
+ seeAlso: [`atticus finding ${x.change} ${x.id} <action>`, `atticus explain ${x.rule}`]
1800
+ };
1801
+ }
1802
+ }
1803
+ if (!out) {
1804
+ const t = termNamed(s);
1805
+ if (t)
1806
+ out = {
1807
+ subject: s,
1808
+ kind: "term",
1809
+ title: t.term,
1810
+ lines: [t.says],
1811
+ seeAlso: commandNamed(s) ? [`atticus explain "atticus ${s}"`] : []
1812
+ };
1813
+ }
1814
+ if (!out) {
1815
+ const c = commandNamed(s);
1816
+ if (c)
1817
+ out = commandExplanation(s, c);
1818
+ }
1819
+ out ??= {
1820
+ subject: s,
1821
+ kind: "unknown",
1822
+ title: `Atticus knows nothing called ${s}.`,
1823
+ lines: [
1824
+ 'Try a record id (atticus query to find one), a change id, a finding id, or a command such as "atticus explain scan".'
1825
+ ],
1826
+ seeAlso: ["atticus query", "atticus change list"]
1827
+ };
1828
+ if (o.runtime && out.kind !== "unknown")
1829
+ out.reworded = await reword(out, o.runtime, o.now ?? /* @__PURE__ */ new Date());
1830
+ return out;
1831
+ }
1832
+ function commandExplanation(s, c) {
1833
+ return {
1834
+ subject: s,
1835
+ kind: "command",
1836
+ title: `atticus ${c.name}: ${c.what}`,
1837
+ lines: [
1838
+ `Usage: ${c.usage}`,
1839
+ `Usually run by: ${c.who.join(", ")}.`,
1840
+ c.changes === "nothing" ? "It only reads; it changes nothing." : `It changes: ${c.changes}.`,
1841
+ ...c.approval ? [
1842
+ "It writes to a shared system, so it needs your approval each time (--approve; it shows what it would do first)."
1843
+ ] : []
1844
+ ],
1845
+ seeAlso: (c.related ?? []).map((r) => `atticus explain ${r}`)
1846
+ };
1847
+ }
1848
+ async function reword(e, runtime, now) {
1849
+ const pack = buildContextPack({
1850
+ change: "explain",
1851
+ agent: EXPLAINER.id,
1852
+ budget: EXPLAINER.tokenBudget,
1853
+ levels: ["public", "internal", "confidential", "restricted"],
1854
+ maxClassification: "internal",
1855
+ now,
1856
+ sources: [
1857
+ {
1858
+ id: "0-instructions",
1859
+ kind: "instruction",
1860
+ origin: "atticus/learn/explain",
1861
+ text: "Reword the explanation below in plain, short sentences for someone new to Atticus. Keep every fact; add none. Write only the reworded text.",
1862
+ classification: "public",
1863
+ trusted: true
1864
+ },
1865
+ {
1866
+ id: "explanation",
1867
+ kind: "document",
1868
+ origin: `explain/${e.subject}`,
1869
+ text: toCanonicalYaml({ title: e.title, lines: e.lines }),
1870
+ classification: "internal"
1871
+ }
1872
+ ]
1873
+ });
1874
+ try {
1875
+ const run = await runtime.prepare({
1876
+ change: "explain",
1877
+ agent: EXPLAINER,
1878
+ pack,
1879
+ outputPath: "explain.md"
1880
+ });
1881
+ const done = await runtime.run(run.runId);
1882
+ const text = done.output?.trim();
1883
+ return done.state === "validated" && text ? text : void 0;
1884
+ } catch {
1885
+ return void 0;
1886
+ }
1887
+ }
1888
+
1889
+ // packages/cli/dist/learn-commands.js
1890
+ function fail(io, e) {
1891
+ if (e instanceof LearnError || e instanceof SandboxError) {
1892
+ io.err(e.message);
1893
+ return e instanceof LearnError && e.code === "usage" ? 2 : 1;
1894
+ }
1895
+ throw e;
1896
+ }
1897
+ var command = (argv) => `atticus ${argv.map((a) => /[\s"']/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a).join(" ")}`;
1898
+ function printStep(io, lesson, step) {
1899
+ const n = lesson.steps.indexOf(step) + 1;
1900
+ io.out(`Step ${n} of ${lesson.steps.length}: ${step.title}`);
1901
+ io.out("");
1902
+ for (const l of step.body.trimEnd().split("\n"))
1903
+ io.out(l);
1904
+ if (step.do.length) {
1905
+ io.out("");
1906
+ for (const argv of step.do)
1907
+ io.out(` ${command(argv)}`);
1908
+ }
1909
+ const ask = step.check.type === "answer" ? step.check : step.check.type === "all" ? step.check.checks.find((c) => c.type === "answer") : void 0;
1910
+ if (ask) {
1911
+ io.out("");
1912
+ io.out(`Question: ${ask.question}`);
1913
+ io.out('Answer with: atticus learn check --answer "<your answer>"');
1914
+ } else {
1915
+ io.out("");
1916
+ io.out("When you have done it: atticus learn check");
1917
+ }
1918
+ }
1919
+ async function learnList(root, o, io) {
1920
+ const { lessons, problems } = await lessonsFor(root);
1921
+ const sandbox = await isSandbox(root);
1922
+ const progress = sandbox ? await readProgress(root) : void 0;
1923
+ const state = (l) => {
1924
+ const p = progress?.lessons[l.id];
1925
+ if (!p)
1926
+ return "not started";
1927
+ if (p.completedAt)
1928
+ return "done";
1929
+ return `${Object.keys(p.steps).length} of ${l.steps.length} steps`;
1930
+ };
1931
+ if (o.json) {
1932
+ io.out(JSON.stringify({
1933
+ sandbox,
1934
+ lessons: lessons.map((l) => ({
1935
+ id: l.id,
1936
+ role: l.role,
1937
+ title: l.title,
1938
+ minutes: l.minutes,
1939
+ steps: l.steps.length,
1940
+ state: state(l)
1941
+ })),
1942
+ problems
1943
+ }, null, 2));
1944
+ return problems.length ? 1 : 0;
1945
+ }
1946
+ for (const l of table(["ROLE", "LESSON", "MINUTES", "PROGRESS", "TITLE"], lessons.map((l2) => [l2.role, l2.id, String(l2.minutes), sandbox ? state(l2) : "-", l2.title])))
1947
+ io.out(l);
1948
+ for (const p of problems)
1949
+ io.err(p);
1950
+ io.out(sandbox ? "Start one with atticus learn start <role|lesson>." : "Lessons run in a practice sandbox: atticus sandbox create <dir>, then run atticus learn start <role> there.");
1951
+ return problems.length ? 1 : 0;
1952
+ }
1953
+ async function learnStart(root, roleOrLesson, o, io, deps = {}) {
1954
+ if (!roleOrLesson) {
1955
+ io.err("Usage: atticus learn start <role|lesson>");
1956
+ return 2;
1957
+ }
1958
+ try {
1959
+ const r = await startLesson(root, roleOrLesson, deps.now ? { now: deps.now } : {});
1960
+ if (o.json) {
1961
+ io.out(JSON.stringify({ lesson: r.lesson.id, step: r.step?.id ?? null, setup: r.setup, resumed: r.resumed }, null, 2));
1962
+ return 0;
1963
+ }
1964
+ io.out(`${r.lesson.title} (${r.lesson.role}, about ${r.lesson.minutes} minutes)${r.resumed ? ": resumed" : ""}`);
1965
+ for (const s of r.setup)
1966
+ io.out(s);
1967
+ io.out("");
1968
+ if (!r.resumed) {
1969
+ for (const l of r.lesson.intro.trimEnd().split("\n"))
1970
+ io.out(l);
1971
+ io.out("");
1972
+ }
1973
+ if (r.step)
1974
+ printStep(io, r.lesson, r.step);
1975
+ else
1976
+ io.out("You have finished this lesson. Pick another with atticus learn.");
1977
+ return 0;
1978
+ } catch (e) {
1979
+ return fail(io, e);
1980
+ }
1981
+ }
1982
+ async function learnCheck(root, o, io, deps = {}) {
1983
+ try {
1984
+ const r = await checkStep(root, {
1985
+ ...o.answer !== void 0 ? { answer: o.answer } : {},
1986
+ ...o.lesson ? { lesson: o.lesson } : {},
1987
+ ...o.step ? { step: o.step } : {},
1988
+ ...deps.now ? { now: deps.now } : {}
1989
+ });
1990
+ if (o.json) {
1991
+ io.out(JSON.stringify({
1992
+ lesson: r.lesson.id,
1993
+ step: r.step.id,
1994
+ pass: r.result.pass,
1995
+ message: r.result.message,
1996
+ next: r.next?.id ?? null,
1997
+ completed: r.completed
1998
+ }, null, 2));
1999
+ return r.result.pass ? 0 : 1;
2000
+ }
2001
+ if (!r.result.pass) {
2002
+ io.out(`Not yet: ${r.result.message}`);
2003
+ io.out(`Hint: ${r.step.hint}`);
2004
+ return 1;
2005
+ }
2006
+ io.out(`Passed: ${r.result.message}`);
2007
+ if (r.next) {
2008
+ io.out("");
2009
+ printStep(io, r.lesson, r.next);
2010
+ } else {
2011
+ io.out("");
2012
+ for (const l of r.lesson.outro.trimEnd().split("\n"))
2013
+ io.out(l);
2014
+ }
2015
+ return 0;
2016
+ } catch (e) {
2017
+ return fail(io, e);
2018
+ }
2019
+ }
2020
+ async function sandboxCreate(dir, o, io, deps = {}) {
2021
+ if (!dir) {
2022
+ io.err("Usage: atticus sandbox create <dir>");
2023
+ return 2;
2024
+ }
2025
+ try {
2026
+ const s = await createSandbox(dir, deps.now ? { now: deps.now } : {});
2027
+ const silent = { out: () => void 0, err: () => void 0 };
2028
+ await upgradeCommand(s.root, { by: "atticus-sandbox", ...deps.now ? { now: deps.now } : {} }, silent);
2029
+ await generateAssistantFiles(s.root);
2030
+ if (o.json) {
2031
+ io.out(JSON.stringify(s, null, 2));
2032
+ return 0;
2033
+ }
2034
+ io.out(`Made a practice sandbox in ${s.dir}: ${s.repos.length} Forge repos and the seeded change ${s.change}.`);
2035
+ io.out(`Its metarepo is ${s.root}. Run commands there (or add --root), starting with atticus learn. Go there with:`);
2036
+ io.out(`cd "${s.root}"`);
2037
+ return 0;
2038
+ } catch (e) {
2039
+ return fail(io, e);
2040
+ }
2041
+ }
2042
+ async function explainCommand(root, idOrCommand, o, io, deps = {}) {
2043
+ if (!idOrCommand?.trim()) {
2044
+ io.err("Usage: atticus explain <record id | finding id | change id | command>");
2045
+ return 2;
2046
+ }
2047
+ const e = await explain(root, idOrCommand, {
2048
+ ...deps.runtime ? { runtime: deps.runtime } : {},
2049
+ ...deps.now ? { now: deps.now } : {}
2050
+ });
2051
+ if (o.json) {
2052
+ io.out(JSON.stringify(e, null, 2));
2053
+ return e.kind === "unknown" ? 1 : 0;
2054
+ }
2055
+ io.out(e.title);
2056
+ for (const l of e.lines)
2057
+ io.out(` ${l}`);
2058
+ if (e.reworded) {
2059
+ io.out("");
2060
+ io.out("In plain words (AI):");
2061
+ for (const l of e.reworded.split("\n"))
2062
+ io.out(` ${l}`);
2063
+ }
2064
+ if (e.seeAlso.length)
2065
+ io.out(`See also: ${e.seeAlso.join(", ")}`);
2066
+ return e.kind === "unknown" ? 1 : 0;
2067
+ }
2068
+
2069
+ export {
2070
+ isSandbox,
2071
+ readProgress,
2072
+ lessonsFor,
2073
+ learnList,
2074
+ learnStart,
2075
+ learnCheck,
2076
+ sandboxCreate,
2077
+ explainCommand
2078
+ };