@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.
- package/LICENSE +177 -0
- package/README.md +17 -2
- package/THIRD-PARTY-LICENSES.txt +2607 -0
- package/dist/bin.js +47 -0
- package/dist/chunk-23W7VWKR.js +2609 -0
- package/dist/chunk-2C5B464C.js +106 -0
- package/dist/chunk-2LLFKP7C.js +53 -0
- package/dist/chunk-2MD7TXSG.js +166 -0
- package/dist/chunk-2UIQBDJO.js +131 -0
- package/dist/chunk-32MPK524.js +378 -0
- package/dist/chunk-3ALD2W2A.js +95 -0
- package/dist/chunk-3G4YI4ZU.js +16 -0
- package/dist/chunk-4AK7IBVP.js +915 -0
- package/dist/chunk-4BMDGTED.js +69 -0
- package/dist/chunk-4NDAAFQT.js +103 -0
- package/dist/chunk-4Q3NN7VF.js +32 -0
- package/dist/chunk-4RS5KZQD.js +25 -0
- package/dist/chunk-4SKZDI62.js +22 -0
- package/dist/chunk-55YODD3N.js +385 -0
- package/dist/chunk-56YQ6ROQ.js +3479 -0
- package/dist/chunk-5A5L3T2U.js +583 -0
- package/dist/chunk-5FEOD73Z.js +374 -0
- package/dist/chunk-5GDRCDQB.js +32 -0
- package/dist/chunk-5WS2YOGD.js +95 -0
- package/dist/chunk-65NYPGRC.js +766 -0
- package/dist/chunk-6BVONWJ7.js +50 -0
- package/dist/chunk-6D4FRTFF.js +53 -0
- package/dist/chunk-6GPJOFV4.js +149 -0
- package/dist/chunk-6JCPI55B.js +92 -0
- package/dist/chunk-6JUBZBMW.js +56 -0
- package/dist/chunk-6SCVQZX5.js +2522 -0
- package/dist/chunk-6TACQN3N.js +41 -0
- package/dist/chunk-72TXNLOC.js +496 -0
- package/dist/chunk-76WKMA4J.js +14 -0
- package/dist/chunk-7EFILTCI.js +67 -0
- package/dist/chunk-7EKXJZ2Y.js +2902 -0
- package/dist/chunk-7EOBHUMC.js +133 -0
- package/dist/chunk-7JUKDRSZ.js +1726 -0
- package/dist/chunk-7NN5VWDC.js +43 -0
- package/dist/chunk-7UFQZXZA.js +5949 -0
- package/dist/chunk-A3KPZJ5M.js +131 -0
- package/dist/chunk-A6AC72A5.js +48 -0
- package/dist/chunk-A7KZKIZX.js +166 -0
- package/dist/chunk-ABWHYWNI.js +101 -0
- package/dist/chunk-AFFIK6JI.js +46 -0
- package/dist/chunk-AHOQZ4OK.js +37 -0
- package/dist/chunk-AVJDSJ2F.js +18 -0
- package/dist/chunk-AXXCPAP6.js +203 -0
- package/dist/chunk-AY3QXYMB.js +41 -0
- package/dist/chunk-B2OKWJIU.js +143 -0
- package/dist/chunk-B632FH3I.js +251 -0
- package/dist/chunk-BLW4SXCU.js +38 -0
- package/dist/chunk-BVS2K73A.js +33 -0
- package/dist/chunk-BWZ77RQT.js +25 -0
- package/dist/chunk-BYBUDX4T.js +163 -0
- package/dist/chunk-BYZCY6SP.js +230 -0
- package/dist/chunk-C6EKFH7J.js +169 -0
- package/dist/chunk-C6MZFGN7.js +271 -0
- package/dist/chunk-CAQZUNOH.js +1150 -0
- package/dist/chunk-CD4XHYGY.js +856 -0
- package/dist/chunk-CEZQOLEO.js +1217 -0
- package/dist/chunk-CHWTETW7.js +128 -0
- package/dist/chunk-CMV7TGU6.js +31 -0
- package/dist/chunk-CNUKCS2T.js +2741 -0
- package/dist/chunk-CP3L3FH7.js +549 -0
- package/dist/chunk-CPGCA6GZ.js +41 -0
- package/dist/chunk-CWM6KM5G.js +315 -0
- package/dist/chunk-CXDSTRUJ.js +873 -0
- package/dist/chunk-D3PO4DOZ.js +261 -0
- package/dist/chunk-DFBCSHVT.js +69 -0
- package/dist/chunk-DHPACDG7.js +104 -0
- package/dist/chunk-DPC5Y5DL.js +3898 -0
- package/dist/chunk-DPUYDDQG.js +164 -0
- package/dist/chunk-DQN7TF5D.js +325 -0
- package/dist/chunk-EADP3PEN.js +450 -0
- package/dist/chunk-ED2LWWIP.js +878 -0
- package/dist/chunk-EHVTK3UE.js +116 -0
- package/dist/chunk-EPTBZ3GP.js +101 -0
- package/dist/chunk-EXIMEJ2X.js +545 -0
- package/dist/chunk-EYWIDJFU.js +130 -0
- package/dist/chunk-F44OMYIN.js +54 -0
- package/dist/chunk-F7HYOUFF.js +287 -0
- package/dist/chunk-FALXWAMD.js +182 -0
- package/dist/chunk-FBNPE7HS.js +49 -0
- package/dist/chunk-FSWUD5QB.js +108 -0
- package/dist/chunk-FVQISWPW.js +296 -0
- package/dist/chunk-G2B4ZQGH.js +240 -0
- package/dist/chunk-GK6XK5SP.js +1227 -0
- package/dist/chunk-GLEBK5L3.js +34 -0
- package/dist/chunk-GNB5QDDI.js +25 -0
- package/dist/chunk-GQ4N4WKF.js +1142 -0
- package/dist/chunk-GRMR5TRQ.js +119 -0
- package/dist/chunk-HC6XKREB.js +334 -0
- package/dist/chunk-HFQCFSJR.js +60 -0
- package/dist/chunk-HJDICQA4.js +102 -0
- package/dist/chunk-HLJAXMM4.js +233 -0
- package/dist/chunk-HVIMMDVB.js +162 -0
- package/dist/chunk-HZVLAPN5.js +32 -0
- package/dist/chunk-I3E7CX4H.js +155 -0
- package/dist/chunk-IQFTZ37C.js +2919 -0
- package/dist/chunk-ITY3VJRX.js +285 -0
- package/dist/chunk-JBTK5OP2.js +55 -0
- package/dist/chunk-JPGCERE2.js +26 -0
- package/dist/chunk-JQDNHIBC.js +38 -0
- package/dist/chunk-JRBCWLOR.js +307 -0
- package/dist/chunk-JRZQA2JM.js +212 -0
- package/dist/chunk-KCBJCA66.js +45 -0
- package/dist/chunk-KJPMEAI4.js +705 -0
- package/dist/chunk-KQDZXGNR.js +14 -0
- package/dist/chunk-KUUQR4HA.js +264 -0
- package/dist/chunk-L24C55VN.js +7923 -0
- package/dist/chunk-L3BX5ZIB.js +400 -0
- package/dist/chunk-L6SGQIR3.js +471 -0
- package/dist/chunk-LBENYT3H.js +66 -0
- package/dist/chunk-LNAJW6FB.js +47 -0
- package/dist/chunk-MC3H7736.js +19910 -0
- package/dist/chunk-MMOSZPWJ.js +273 -0
- package/dist/chunk-MXCHWULV.js +285 -0
- package/dist/chunk-N3E56YPN.js +344 -0
- package/dist/chunk-NHFQMOVK.js +180 -0
- package/dist/chunk-NQU6KNQF.js +27 -0
- package/dist/chunk-OIK5YPTN.js +391 -0
- package/dist/chunk-OLWFG75W.js +4330 -0
- package/dist/chunk-ONPUIE7Y.js +29 -0
- package/dist/chunk-OW7D2PJL.js +376 -0
- package/dist/chunk-P63VL4HE.js +2357 -0
- package/dist/chunk-PA4YNUMU.js +45 -0
- package/dist/chunk-PAMPPUR6.js +4500 -0
- package/dist/chunk-PBS3OFNN.js +113 -0
- package/dist/chunk-PFT3VTS4.js +214 -0
- package/dist/chunk-PFTA6BZF.js +49 -0
- package/dist/chunk-PNN5KLAY.js +119 -0
- package/dist/chunk-PRPPGYZG.js +104 -0
- package/dist/chunk-PUKEZ37F.js +666 -0
- package/dist/chunk-Q3HOFXZJ.js +76 -0
- package/dist/chunk-Q5AMFEWD.js +119 -0
- package/dist/chunk-QKRNMBJO.js +80 -0
- package/dist/chunk-QSU47IQ5.js +36 -0
- package/dist/chunk-QSXK2RGH.js +688 -0
- package/dist/chunk-QXNWYUYI.js +52 -0
- package/dist/chunk-R6RY4Q5T.js +51 -0
- package/dist/chunk-RCU3MPY2.js +1496 -0
- package/dist/chunk-RLIB4JUN.js +174 -0
- package/dist/chunk-RYSCLMU3.js +1991 -0
- package/dist/chunk-RZUZ4RJJ.js +415 -0
- package/dist/chunk-S2A7YC3Q.js +41433 -0
- package/dist/chunk-S2FBBX7D.js +540 -0
- package/dist/chunk-S34B4QPI.js +430 -0
- package/dist/chunk-SETWVXOJ.js +2078 -0
- package/dist/chunk-SMA4N4AC.js +211 -0
- package/dist/chunk-SP2I63OO.js +252 -0
- package/dist/chunk-STHSY74Q.js +242 -0
- package/dist/chunk-STUPFGXY.js +3206 -0
- package/dist/chunk-T5I4S2W3.js +34 -0
- package/dist/chunk-T6AIP7H5.js +3851 -0
- package/dist/chunk-TANFV3MZ.js +531 -0
- package/dist/chunk-TAVDMUV3.js +189 -0
- package/dist/chunk-TBSQJ6IV.js +324 -0
- package/dist/chunk-TIZHP7KE.js +408 -0
- package/dist/chunk-TV45EUGM.js +51 -0
- package/dist/chunk-TW2KPPUC.js +4087 -0
- package/dist/chunk-V2YTAIQO.js +1075 -0
- package/dist/chunk-V5PKHHQQ.js +1963 -0
- package/dist/chunk-VF4IOZZO.js +184 -0
- package/dist/chunk-VJTINHZI.js +48 -0
- package/dist/chunk-VKLILA2D.js +205 -0
- package/dist/chunk-VOGCSYOI.js +16 -0
- package/dist/chunk-VPTTCYOC.js +16101 -0
- package/dist/chunk-WY6PP6AA.js +53 -0
- package/dist/chunk-WYFIXP54.js +174 -0
- package/dist/chunk-WZ2BXIGG.js +34 -0
- package/dist/chunk-XJNDSYGO.js +147 -0
- package/dist/chunk-XPCM366L.js +70 -0
- package/dist/chunk-XSQKGVYA.js +69 -0
- package/dist/chunk-XUPVB7TL.js +207 -0
- package/dist/chunk-XZRON6IV.js +131 -0
- package/dist/chunk-Y4NG2FP5.js +94 -0
- package/dist/chunk-YEDDOYSC.js +27809 -0
- package/dist/chunk-YGMCXNTY.js +41 -0
- package/dist/chunk-YJKPTX6J.js +228 -0
- package/dist/chunk-YMZ3TNYB.js +18 -0
- package/dist/chunk-Z6LF7HQN.js +22 -0
- package/dist/chunk-Z7SG32ZY.js +55 -0
- package/dist/chunk-ZDPAS56H.js +76 -0
- package/dist/chunk-ZJSP24X4.js +1247 -0
- package/dist/chunk-ZMAINWBW.js +18 -0
- package/dist/chunk-ZMFPW66A.js +22 -0
- package/dist/chunk-ZUIAOKXI.js +1611 -0
- package/dist/chunk-ZVM6AKKM.js +47 -0
- package/dist/files.json +1888 -0
- package/dist/fixtures/forge/README.md +19 -0
- package/dist/fixtures/forge/expected/discrepancies.yaml +54 -0
- package/dist/fixtures/forge/expected/inputs.yaml +38 -0
- package/dist/fixtures/forge/expected/scan.yaml +342 -0
- package/dist/fixtures/forge/inputs/diagrams/forge-context.drawio +23 -0
- package/dist/fixtures/forge/inputs/partner-specs/northwind-pay-openapi.yaml +11 -0
- package/dist/fixtures/forge/inputs/tickets/ado-stories.json +4 -0
- package/dist/fixtures/forge/inputs/tickets/closed-service-requests.json +5 -0
- package/dist/fixtures/forge/inputs/wiki/integration-patterns.md +11 -0
- package/dist/fixtures/forge/inputs/wiki/ledger-solution-spec.md +9 -0
- package/dist/fixtures/forge/inputs/wiki/snow-firewall-request.md +4 -0
- package/dist/fixtures/forge/metarepo/judgments/reaches/84af5b6af24f7ca8c23013f3.yaml +17 -0
- package/dist/fixtures/forge/metarepo/judgments/reaches/c3917555edebddce8cecb647.yaml +17 -0
- package/dist/fixtures/forge/metarepo/judgments/reaches/c5fafd6601f8ca9e34e0bbd0.yaml +17 -0
- package/dist/fixtures/forge/metarepo/org-pack/agents/architecture-critic.yaml +9 -0
- package/dist/fixtures/forge/metarepo/org-pack/agents/story-writer.yaml +10 -0
- package/dist/fixtures/forge/metarepo/org-pack/checklists/architecture.yaml +115 -0
- package/dist/fixtures/forge/metarepo/org-pack/dialects/forge.yaml +111 -0
- package/dist/fixtures/forge/metarepo/org-pack/gates.yaml +22 -0
- package/dist/fixtures/forge/metarepo/org-pack/pack.yaml +11 -0
- package/dist/fixtures/forge/metarepo/org-pack/patterns/A.EB.PUB.yaml +12 -0
- package/dist/fixtures/forge/metarepo/org-pack/patterns/D.JDBC.yaml +12 -0
- package/dist/fixtures/forge/metarepo/org-pack/patterns/S.DIR.BFF.yaml +12 -0
- package/dist/fixtures/forge/metarepo/org-pack/patterns/S.GW.SOR.yaml +13 -0
- package/dist/fixtures/forge/metarepo/org-pack/patterns/S.GW.VND.yaml +14 -0
- package/dist/fixtures/forge/metarepo/org-pack/process/change-templates/feature.md +37 -0
- package/dist/fixtures/forge/metarepo/org-pack/process/skills/add-forge-endpoint/SKILL.md +17 -0
- package/dist/fixtures/forge/metarepo/org-pack/process/snow/firewall-rule.yaml +14 -0
- package/dist/fixtures/forge/metarepo/org-pack/process/snow/gateway-route.yaml +13 -0
- package/dist/fixtures/forge/metarepo/org-pack/profiles/forge/profile.yaml +12 -0
- package/dist/fixtures/forge/metarepo/org-pack/profiles/forge/repos.manifest.yaml +44 -0
- package/dist/fixtures/forge/metarepo/org-pack/roles.yaml +14 -0
- package/dist/fixtures/forge/metarepo/org-pack/runtime.yaml +14 -0
- package/dist/fixtures/forge/metarepo/org-pack/sources.yaml +3 -0
- package/dist/fixtures/forge/repos/forge-events/topics/payments.initiated.yaml +4 -0
- package/dist/fixtures/forge/repos/forge-events/topics/transfers.completed.yaml +4 -0
- package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/ledger-v1.xml +3 -0
- package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/policies/SpikeArrest.xml +3 -0
- package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/policies/VerifyOAuth.xml +4 -0
- package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/proxies/default.xml +14 -0
- package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/targets/ledger.xml +5 -0
- package/dist/fixtures/forge/repos/forge-infra/alerts/ledger-api.yaml +5 -0
- package/dist/fixtures/forge/repos/forge-infra/k8s/ledger-api.yaml +15 -0
- package/dist/fixtures/forge/repos/forge-infra/k8s/portal-bff.yaml +7 -0
- package/dist/fixtures/forge/repos/forge-infra/terraform/ledger-db.tf +9 -0
- package/dist/fixtures/forge/repos/forge-ledger/api/ledger-openapi.yaml +31 -0
- package/dist/fixtures/forge/repos/forge-ledger/forge.yaml +4 -0
- package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/LedgerEndpoints.java +13 -0
- package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/LedgerEvents.java +11 -0
- package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/NorthwindClient.java +10 -0
- package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/application.yml +3 -0
- package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/db/migration/V1__create_account.sql +5 -0
- package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/db/migration/V2__create_entry.sql +7 -0
- package/dist/fixtures/forge/repos/forge-portal-bff/pom.xml +1 -0
- package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/AuditPublisher.java +12 -0
- package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/BalanceController.java +16 -0
- package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/LedgerClient.java +13 -0
- package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/PaymentEvents.java +13 -0
- package/dist/fixtures/forge/repos/forge-portal-bff/src/main/resources/application.yml +8 -0
- package/dist/fixtures/forge/repos/forge-portal-ui/package.json +1 -0
- package/dist/fixtures/forge/repos/forge-portal-ui/src/api/balance.ts +5 -0
- package/dist/fixtures/forge/repos/forge-reports-job/src/main/java/forge/reports/DailyEntriesJob.java +19 -0
- package/dist/fixtures/forge/repos/forge-reports-job/src/main/resources/application.yml +3 -0
- package/dist/fixtures/forge-change/README.md +41 -0
- package/dist/fixtures/forge-change/expected/findings.yaml +301 -0
- package/dist/fixtures/forge-change/expected/stories.yaml +101 -0
- package/dist/fixtures/forge-change/judgments/personal-data/0b5f3dbf5257a22275d8c74b.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/2759a66770134c417440bf2d.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/31b1ea2f14d0737f1aca79fd.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/49afd70c08228e65ad6ca1ed.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/761cabcb8a39f5e5866d2dd6.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/adae902b90cc8ffa0c765145.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/ba11bb933105ec9493e065dd.yaml +18 -0
- package/dist/fixtures/forge-change/judgments/personal-data/bdbcd1cdac3021c68063c3e7.yaml +18 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/brief.md +35 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/change.yaml +120 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/data.md +18 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/design.md +29 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/interfaces.md +17 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/mappings.md +26 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/proposal.md +35 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/snow.md +12 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/specs/transfer-limits/spec.md +19 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s1-refuse-over-limit.md +17 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s2-show-remaining.md +16 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s3-audit-refusals.md +18 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s4-velocity-check.md +16 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories.md +8 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/tasks.md +12 -0
- package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/ux.md +7 -0
- package/dist/fixtures/forge-change/org-pack/agents/code-and-test-critic.yaml +13 -0
- package/dist/fixtures/forge-change/org-pack/agents/complete-critic.yaml +12 -0
- package/dist/fixtures/forge-change/org-pack/agents/safe-critic.yaml +12 -0
- package/dist/fixtures/forge-change/org-pack/agents/ship-critic.yaml +11 -0
- package/dist/fixtures/forge-change/org-pack/agents/true-critic.yaml +11 -0
- package/dist/fixtures/forge-change/org-pack/checklists/review.yaml +54 -0
- package/dist/fixtures/forge-change/org-pack/gates.yaml +25 -0
- package/dist/fixtures/forge-change/org-pack/teams/team-ledger/checklists/ledger-team.yaml +227 -0
- package/dist/fixtures/forge-change/recordings/complete-critic/2ad84f84bf39c1d5412697e69fe23034d6983ec45a00314bf0975e627589a3c5.json +12 -0
- package/dist/fixtures/forge-change/recordings/complete-critic/417c98d8e54a4387d0fd93441c49f44c2868958739070d855c1dce0c41e24db3.json +12 -0
- package/dist/fixtures/forge-change/recordings/complete-critic/50203ee22e9cb3ef047295edb6538fe7d5695e1e4b139f3c929013dc6ce47021.json +12 -0
- package/dist/fixtures/forge-change/recordings/complete-critic/89f18d67f5d9c325bf35e2a9e020f9ddb6ee13b8523315037f0905cc57c040b1.json +12 -0
- package/dist/fixtures/forge-change/recordings/complete-critic/b4097867d26ae8eadc8b9efa8cdd86897013358035f0b40086c299a59c4ba121.json +12 -0
- package/dist/fixtures/forge-change/recordings/safe-critic/302d06f49664dd67ad2e21ab9cf74c1b0f7d3f750709c6ed8eff2b70983274e2.json +12 -0
- package/dist/fixtures/forge-change/recordings/safe-critic/32f1049c9a5ab4a759543290db4644ec6e221cc45fdfdcf06193837c6a8ce0b9.json +12 -0
- package/dist/fixtures/forge-change/recordings/safe-critic/4e4a84bfc04b9f26ca4250319a879901ff7a1c081c71e46a117675fee05026b0.json +12 -0
- package/dist/fixtures/forge-change/recordings/safe-critic/604af0fcdd6df6b76208fa88305fdc15c37606c2a4d1ae816477e46ac8c2dd04.json +12 -0
- package/dist/fixtures/forge-change/recordings/safe-critic/f0bf50af11f14c50d07904f0dffe1c8c20fb0cd89770645a8824c1d30a443955.json +12 -0
- package/dist/fixtures/forge-change/recordings/ship-critic/350954b58833b2c04880c67db45a1b328b7fd541d9f0d943b63905b8875488a6.json +12 -0
- package/dist/fixtures/forge-change/recordings/ship-critic/4af95c6c96a206777cec9de1f9c2d8c55a36ddc9b8db2f81d6eb83217b835efa.json +12 -0
- package/dist/fixtures/forge-change/recordings/ship-critic/76c34522a49cf7587a4e5689a5030d384603fa780b8aba97d27815cbc829683e.json +12 -0
- package/dist/fixtures/forge-change/recordings/ship-critic/a3b2cd1ace48587e5994333db03b34c3a0f9dec4f1932f1730ddfb687f6918c8.json +12 -0
- package/dist/fixtures/forge-change/recordings/ship-critic/de288a4fa64d0fc20890f9a787080ce6eac5203b7b9b72d884df11e8cf111835.json +12 -0
- package/dist/fixtures/forge-change/recordings/story-writer/1ddb00eac2aaf91a5ec5d9dfd220116be9f75aec6546a4a3a3b050ed89af2864.json +12 -0
- package/dist/packages/assistant-kit/frameworks-kit/README.md +86 -0
- package/dist/packages/assistant-kit/frameworks-kit/checklist.md +11 -0
- package/dist/packages/assistant-kit/frameworks-kit/examples/tide-lantern.yaml +56 -0
- package/dist/packages/assistant-kit/frameworks-kit/examples/tide-platform.yaml +43 -0
- package/dist/packages/assistant-kit/frameworks-kit/prompts/write-framework-model.md +12 -0
- package/dist/packages/assistant-kit/kit/README.md +98 -0
- package/dist/packages/assistant-kit/kit/c2-reference.md +334 -0
- package/dist/packages/assistant-kit/kit/checklist.md +39 -0
- package/dist/packages/assistant-kit/kit/examples/house-http-client.yaml +102 -0
- package/dist/packages/assistant-kit/kit/examples/tide-apigee.yaml +56 -0
- package/dist/packages/assistant-kit/kit/examples/tide-apim.yaml +32 -0
- package/dist/packages/assistant-kit/kit/examples/tide-kong.yaml +44 -0
- package/dist/packages/assistant-kit/kit/prompts/write-dialect.md +34 -0
- package/dist/packages/assistant-kit/kit/worked-example.md +198 -0
- package/dist/packages/conformance/sample/README.md +4 -0
- package/dist/packages/conformance/sample/inputs/notes.md +3 -0
- package/dist/packages/conformance/sample/inputs/systems.csv +3 -0
- package/dist/packages/conformance/sample/repo/api/greeter.proto +6 -0
- package/dist/packages/conformance/sample/repo/api/openapi.yaml +10 -0
- package/dist/packages/conformance/sample/repo/api/schema.graphql +3 -0
- package/dist/packages/conformance/sample/repo/db/V1__create_greeting.sql +4 -0
- package/dist/packages/conformance/sample/repo/package.json +7 -0
- package/dist/packages/conformance/sample/repo/src/main/java/sample/HelloController.java +10 -0
- package/dist/packages/conformance/sample/repo/web/app.ts +4 -0
- package/dist/packages/evals/datasets/critic-lenses.yaml +62 -0
- package/dist/packages/evals/datasets/dialect-writer.yaml +14 -0
- package/dist/packages/evals/datasets/feature-namer.yaml +34 -0
- package/dist/packages/evals/datasets/glossary-definer.yaml +25 -0
- package/dist/packages/evals/datasets/story-writer.yaml +13 -0
- package/dist/packages/evals/datasets/template-writer.yaml +16 -0
- package/dist/packages/evals/datasets/test-writer.yaml +18 -0
- package/dist/packages/executors/reference/reference-executor.mjs +233 -0
- package/dist/packages/extractors/dialects/argocd.yaml +45 -0
- package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/AccountResource.java +47 -0
- package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/AuditClient.java +12 -0
- package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/LedgerClient.java +17 -0
- package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/LedgerClient.java +14 -0
- package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/RatesClient.java +11 -0
- package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/StatementController.java +36 -0
- package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/resources/application.yml +7 -0
- package/dist/packages/extractors/dialects/fixtures/quarkus-reactive-messaging/fraud-service/src/main/java/forge/fraud/FraudScorer.java +27 -0
- package/dist/packages/extractors/dialects/fixtures/quarkus-reactive-messaging/fraud-service/src/main/resources/application.properties +4 -0
- package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/api.tf +38 -0
- package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/main.tf +95 -0
- package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/terraform.tfvars +1 -0
- package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/variables.tf +4 -0
- package/dist/packages/extractors/dialects/fixtures/terraform-azure/forge-azure-infra/main.tf +102 -0
- package/dist/packages/extractors/dialects/fixtures/terraform-gcp/forge-gcp-infra/main.tf +51 -0
- package/dist/packages/extractors/dialects/harness.yaml +101 -0
- package/dist/packages/extractors/dialects/jaxrs.yaml +155 -0
- package/dist/packages/extractors/dialects/micronaut.yaml +207 -0
- package/dist/packages/extractors/dialects/quarkus-reactive-messaging.yaml +158 -0
- package/dist/packages/extractors/dialects/tekton.yaml +71 -0
- package/dist/packages/extractors/dialects/terraform-aws.yaml +267 -0
- package/dist/packages/extractors/dialects/terraform-azure.yaml +212 -0
- package/dist/packages/extractors/dialects/terraform-gcp.yaml +109 -0
- package/dist/packages/extractors/grammars/README.md +11 -0
- package/dist/packages/extractors/grammars/tree-sitter-groovy.LICENSE +21 -0
- package/dist/packages/extractors/grammars/tree-sitter-groovy.wasm +0 -0
- package/dist/packages/extractors/grammars/vendored.json +11 -0
- package/dist/packages/learn/lessons/architect-diagrams-and-code.yaml +69 -0
- package/dist/packages/learn/lessons/business-analyst-story-bar.yaml +94 -0
- package/dist/packages/learn/lessons/delivery-lead-release-readiness.yaml +44 -0
- package/dist/packages/learn/lessons/designer-features-in-user-words.yaml +57 -0
- package/dist/packages/learn/lessons/developer-find-your-way.yaml +60 -0
- package/dist/packages/learn/lessons/everyone-first-hour.yaml +74 -0
- package/dist/packages/learn/lessons/platform-lead-coverage-and-defences.yaml +58 -0
- package/dist/packages/learn/lessons/product-owner-first-change.yaml +167 -0
- package/dist/packages/learn/lessons/qa-tests-and-trace.yaml +53 -0
- package/dist/packages/learn/lessons/team-lead-initiative.yaml +146 -0
- package/dist/packages/naysayer/constitution/constitution.yaml +286 -0
- package/dist/packages/naysayer/starter/review.yaml +256 -0
- package/dist/packages/sdk/npm/index.js +8892 -0
- package/dist/packages/speckit/rules/drift.yaml +78 -0
- package/dist/packages/taskrun/rules/conform.yaml +38 -0
- package/dist/packages/testgen/rules/trace.yaml +49 -0
- package/dist/packages/ui/dist/assets/elk-api-tma0MCuh.js +2 -0
- package/dist/packages/ui/dist/assets/elk-worker.min--dAJroGl.js +6401 -0
- package/dist/packages/ui/dist/assets/elk-worker.min-DdYmS5kP.js +2 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +29 -0
- package/dist/packages/ui/dist/assets/index-DD_hufQz.css +1 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/packages/ui/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-devanagari-600-normal-BtY48IV_.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-devanagari-600-normal-DhS7ScYx.woff2 +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-devanagari-700-normal-BQQOj9BB.woff2 +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-devanagari-700-normal-DBRwWuxA.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-600-normal-CXCVEoA9.woff2 +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-600-normal-Dx02qI2_.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-700-normal-BPDUZo87.woff2 +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-700-normal-e0Uj8ZlN.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-ext-600-normal-BleIgkPp.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-ext-600-normal-C9ZjJQAt.woff2 +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-ext-700-normal-BPaQ7tlD.woff +0 -0
- package/dist/packages/ui/dist/assets/rajdhani-latin-ext-700-normal-S6aYESQK.woff2 +0 -0
- package/dist/packages/ui/dist/favicon.svg +1 -0
- package/dist/packages/ui/dist/index.html +15 -0
- package/dist/third-party.json +575 -0
- package/dist/vendor/@fission-ai/openspec/bin/openspec.js +85986 -0
- package/dist/vendor/@fission-ai/openspec/package.json +82 -0
- package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/schema.yaml +248 -0
- package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/design.md +21 -0
- package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/proposal.md +34 -0
- package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/spec.md +13 -0
- package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/tasks.md +11 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
- package/dist/vendor/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
- package/dist/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
- package/package.json +36 -3
|
@@ -0,0 +1,2919 @@
|
|
|
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
|
+
findSection,
|
|
8
|
+
generateAssistantFiles,
|
|
9
|
+
loadOrgCommands,
|
|
10
|
+
parseHeader,
|
|
11
|
+
renderAssistantFiles,
|
|
12
|
+
writeAssistantFiles
|
|
13
|
+
} from "./chunk-V5PKHHQQ.js";
|
|
14
|
+
import {
|
|
15
|
+
BUILT_IN_CHECKS,
|
|
16
|
+
STARTER_REVIEW_PATH,
|
|
17
|
+
starterReviewText
|
|
18
|
+
} from "./chunk-7UFQZXZA.js";
|
|
19
|
+
import {
|
|
20
|
+
changeFootprint,
|
|
21
|
+
changeStore,
|
|
22
|
+
loadChangeContext
|
|
23
|
+
} from "./chunk-56YQ6ROQ.js";
|
|
24
|
+
import {
|
|
25
|
+
ORG_FLOW,
|
|
26
|
+
createOpenSpec,
|
|
27
|
+
setDefaultSchema,
|
|
28
|
+
storeIdOf,
|
|
29
|
+
writeOrgFlowSchema,
|
|
30
|
+
writeStoreMarker
|
|
31
|
+
} from "./chunk-4AK7IBVP.js";
|
|
32
|
+
import {
|
|
33
|
+
readCatalog
|
|
34
|
+
} from "./chunk-OLWFG75W.js";
|
|
35
|
+
import {
|
|
36
|
+
componentExtractor
|
|
37
|
+
} from "./chunk-MC3H7736.js";
|
|
38
|
+
import {
|
|
39
|
+
createGit,
|
|
40
|
+
ensureGitignore
|
|
41
|
+
} from "./chunk-KJPMEAI4.js";
|
|
42
|
+
import {
|
|
43
|
+
DEFAULT_IGNORES,
|
|
44
|
+
fsRepo,
|
|
45
|
+
memoryRepo,
|
|
46
|
+
runExtractor
|
|
47
|
+
} from "./chunk-GQ4N4WKF.js";
|
|
48
|
+
import {
|
|
49
|
+
ChangeLifecycle,
|
|
50
|
+
ChecklistPack,
|
|
51
|
+
ChecklistRule,
|
|
52
|
+
DEFAULT_CHANGE_TYPES,
|
|
53
|
+
DEFAULT_GATES,
|
|
54
|
+
DEFAULT_RUNTIME,
|
|
55
|
+
DEFAULT_SOURCES,
|
|
56
|
+
DEFAULT_TEST_STRATEGY,
|
|
57
|
+
RecordId,
|
|
58
|
+
RepoManifest,
|
|
59
|
+
SOURCES_SCHEMA_VERSION,
|
|
60
|
+
SkillMeta,
|
|
61
|
+
Sources,
|
|
62
|
+
loadOrgPack,
|
|
63
|
+
require_dist
|
|
64
|
+
} from "./chunk-S2A7YC3Q.js";
|
|
65
|
+
import {
|
|
66
|
+
LOCAL_ONLY
|
|
67
|
+
} from "./chunk-D3PO4DOZ.js";
|
|
68
|
+
import {
|
|
69
|
+
__toESM
|
|
70
|
+
} from "./chunk-TV45EUGM.js";
|
|
71
|
+
|
|
72
|
+
// packages/adopt/dist/errors.js
|
|
73
|
+
var AdoptError = class extends Error {
|
|
74
|
+
code;
|
|
75
|
+
constructor(code, message) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.code = code;
|
|
78
|
+
this.name = "AdoptError";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// packages/adopt/dist/files.js
|
|
83
|
+
import { createHash } from "node:crypto";
|
|
84
|
+
import { cp, mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
85
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
86
|
+
var toPosix = (p) => p.split(sep).join("/");
|
|
87
|
+
async function exists(path) {
|
|
88
|
+
try {
|
|
89
|
+
await stat(path);
|
|
90
|
+
return true;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function isDir(path) {
|
|
96
|
+
try {
|
|
97
|
+
return (await stat(path)).isDirectory();
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function readText(path) {
|
|
103
|
+
try {
|
|
104
|
+
return (await readFile(path, "utf8")).replace(/\r\n/g, "\n");
|
|
105
|
+
} catch {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
var sha256 = (data) => createHash("sha256").update(data).digest("hex");
|
|
110
|
+
var WALK_SKIP = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
111
|
+
async function listFiles(dir, o = {}) {
|
|
112
|
+
const out = [];
|
|
113
|
+
const skipNames = o.skipNames ?? WALK_SKIP;
|
|
114
|
+
const skipPaths = new Set(o.skipPaths ?? []);
|
|
115
|
+
async function walk(abs) {
|
|
116
|
+
let entries;
|
|
117
|
+
try {
|
|
118
|
+
entries = await readdir(abs, { withFileTypes: true });
|
|
119
|
+
} catch {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
for (const e of entries) {
|
|
123
|
+
if (e.isSymbolicLink())
|
|
124
|
+
continue;
|
|
125
|
+
const full = join(abs, e.name);
|
|
126
|
+
const rel = toPosix(relative(dir, full));
|
|
127
|
+
if (e.isDirectory()) {
|
|
128
|
+
if (!skipNames.has(e.name) && !skipPaths.has(rel))
|
|
129
|
+
await walk(full);
|
|
130
|
+
} else if (e.isFile())
|
|
131
|
+
out.push(rel);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
await walk(dir);
|
|
135
|
+
return out.sort();
|
|
136
|
+
}
|
|
137
|
+
async function listDirs(dir, skipNames = WALK_SKIP) {
|
|
138
|
+
const out = [];
|
|
139
|
+
async function walk(abs) {
|
|
140
|
+
let entries;
|
|
141
|
+
try {
|
|
142
|
+
entries = await readdir(abs, { withFileTypes: true });
|
|
143
|
+
} catch {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
for (const e of entries) {
|
|
147
|
+
if (!e.isDirectory() || e.isSymbolicLink() || skipNames.has(e.name))
|
|
148
|
+
continue;
|
|
149
|
+
const full = join(abs, e.name);
|
|
150
|
+
out.push(toPosix(relative(dir, full)));
|
|
151
|
+
await walk(full);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
await walk(dir);
|
|
155
|
+
return out.sort();
|
|
156
|
+
}
|
|
157
|
+
async function hashTree(dir, o = {}) {
|
|
158
|
+
const out = {};
|
|
159
|
+
for (const f of await listFiles(dir, o))
|
|
160
|
+
out[f] = sha256(await readFile(join(dir, f)));
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
async function writeNew(path, text2, keep = true) {
|
|
164
|
+
if (keep && await exists(path))
|
|
165
|
+
return false;
|
|
166
|
+
await mkdir(dirname(path), { recursive: true });
|
|
167
|
+
await writeFile(path, text2, "utf8");
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
async function copyDir(from, to) {
|
|
171
|
+
await cp(from, to, { recursive: true, force: false, errorOnExist: false });
|
|
172
|
+
}
|
|
173
|
+
function slug(s) {
|
|
174
|
+
const v = s.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+/, "").replace(/-+$/g, "").slice(0, 100).replace(/[-.]+$/, "");
|
|
175
|
+
return v || "item";
|
|
176
|
+
}
|
|
177
|
+
function frontMatter(text2) {
|
|
178
|
+
const m = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(text2.replace(/\r\n/g, "\n"));
|
|
179
|
+
return m ? { data: m[1], body: m[2] } : { data: void 0, body: text2 };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// packages/adopt/dist/layout.js
|
|
183
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
184
|
+
import { join as join2 } from "node:path";
|
|
185
|
+
var ATTICUS_DIR = ".atticus";
|
|
186
|
+
var ORG_PACK_DIR = "org-pack";
|
|
187
|
+
var STATE_FOLDERS = ["workspace", ".index", ".runs", "state", "absorbed"];
|
|
188
|
+
var LOCAL_STATE = [
|
|
189
|
+
...STATE_FOLDERS,
|
|
190
|
+
...LOCAL_ONLY.map((l) => l.endsWith("/") ? l.slice(0, -1) : l)
|
|
191
|
+
];
|
|
192
|
+
function isLocalState(rel, atRoot = false) {
|
|
193
|
+
const parts = rel.split("/");
|
|
194
|
+
const inside = parts[0] === ATTICUS_DIR;
|
|
195
|
+
if (!inside && !atRoot)
|
|
196
|
+
return false;
|
|
197
|
+
const at = inside ? 1 : 0;
|
|
198
|
+
const head = parts[at];
|
|
199
|
+
if (head === void 0 || parts.length <= at)
|
|
200
|
+
return false;
|
|
201
|
+
return LOCAL_STATE.includes(head) && (parts.length > at + 1 || head.includes("."));
|
|
202
|
+
}
|
|
203
|
+
async function readLayout(orgPackDir) {
|
|
204
|
+
const text2 = await readText(join2(orgPackDir, "sources.yaml"));
|
|
205
|
+
if (text2 === void 0)
|
|
206
|
+
return DEFAULT_SOURCES.layout;
|
|
207
|
+
try {
|
|
208
|
+
const r = Sources.safeParse((0, import_yaml.parse)(text2));
|
|
209
|
+
return r.success ? r.data.layout : DEFAULT_SOURCES.layout;
|
|
210
|
+
} catch {
|
|
211
|
+
return DEFAULT_SOURCES.layout;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function resolveMetarepo(root) {
|
|
215
|
+
const adopted = !await isDir(join2(root, ORG_PACK_DIR)) && await isDir(join2(root, ATTICUS_DIR, ORG_PACK_DIR));
|
|
216
|
+
return pathsFor(root, adopted, await readLayout(orgPackDirOf(root, adopted)));
|
|
217
|
+
}
|
|
218
|
+
function orgPackDirOf(root, adopted) {
|
|
219
|
+
return adopted ? join2(root, ATTICUS_DIR, ORG_PACK_DIR) : join2(root, ORG_PACK_DIR);
|
|
220
|
+
}
|
|
221
|
+
function pathsFor(root, adopted, layout) {
|
|
222
|
+
const stateDir = adopted ? join2(root, ATTICUS_DIR) : root;
|
|
223
|
+
const proposalsRel = adopted ? `${ATTICUS_DIR}/proposals` : "proposals";
|
|
224
|
+
return {
|
|
225
|
+
root,
|
|
226
|
+
adopted,
|
|
227
|
+
orgPackDir: orgPackDirOf(root, adopted),
|
|
228
|
+
orgPackRel: adopted ? `${ATTICUS_DIR}/${ORG_PACK_DIR}` : ORG_PACK_DIR,
|
|
229
|
+
layout,
|
|
230
|
+
catalogDir: join2(root, ...layout.catalog.split("/")),
|
|
231
|
+
openspecDir: join2(root, ...layout.openspec.split("/")),
|
|
232
|
+
stateDir,
|
|
233
|
+
proposalsDir: join2(root, ...proposalsRel.split("/")),
|
|
234
|
+
proposalsRel,
|
|
235
|
+
inputsDir: adopted ? join2(root, ATTICUS_DIR, "inputs") : join2(root, "inputs")
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function inPlacePath(url) {
|
|
239
|
+
if (url === ".")
|
|
240
|
+
return ".";
|
|
241
|
+
if (url.startsWith("./")) {
|
|
242
|
+
const rest = url.slice(2).replace(/\/+$/, "");
|
|
243
|
+
if (rest === "")
|
|
244
|
+
return ".";
|
|
245
|
+
if (rest.split("/").includes(".."))
|
|
246
|
+
return void 0;
|
|
247
|
+
return rest;
|
|
248
|
+
}
|
|
249
|
+
return void 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// packages/adopt/dist/detect.js
|
|
253
|
+
var REPO_TYPES = [
|
|
254
|
+
"spring-boot",
|
|
255
|
+
"java",
|
|
256
|
+
"frontend",
|
|
257
|
+
"node",
|
|
258
|
+
"python",
|
|
259
|
+
"go",
|
|
260
|
+
"dotnet",
|
|
261
|
+
"gateway",
|
|
262
|
+
"mainframe",
|
|
263
|
+
"infra",
|
|
264
|
+
"event-config",
|
|
265
|
+
"unknown"
|
|
266
|
+
];
|
|
267
|
+
var FRONTEND_DEPS = ["react", "vue", "@angular/core", "svelte", "next", "nuxt", "solid-js"];
|
|
268
|
+
async function first(repo, globs) {
|
|
269
|
+
for await (const p of repo.listFiles(globs))
|
|
270
|
+
return p;
|
|
271
|
+
return void 0;
|
|
272
|
+
}
|
|
273
|
+
async function all(repo, globs, max = 3) {
|
|
274
|
+
const out = [];
|
|
275
|
+
for await (const p of repo.listFiles(globs)) {
|
|
276
|
+
out.push(p);
|
|
277
|
+
if (out.length >= max)
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
async function marker(repo, names) {
|
|
283
|
+
const root = await first(repo, names);
|
|
284
|
+
if (root)
|
|
285
|
+
return root;
|
|
286
|
+
const deep = [];
|
|
287
|
+
for await (const p of repo.listFiles(names.map((n) => `**/${n}`)))
|
|
288
|
+
deep.push(p);
|
|
289
|
+
return deep.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b))[0];
|
|
290
|
+
}
|
|
291
|
+
async function text(repo, path) {
|
|
292
|
+
try {
|
|
293
|
+
return await repo.readText(path);
|
|
294
|
+
} catch {
|
|
295
|
+
return "";
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
var TYPE_WORDS = [
|
|
299
|
+
["gateway", ["api gateway", "gateway", "api proxy", "reverse proxy"]],
|
|
300
|
+
["infra", ["infrastructure", "terraform", "kubernetes manifests", "helm chart", "deployment"]],
|
|
301
|
+
["event-config", ["topic definitions", "event topics", "topic config", "event catalogue"]],
|
|
302
|
+
["mainframe", ["mainframe", "cobol", "batch jobs"]],
|
|
303
|
+
["frontend", ["web app", "single-page app", "front end", "frontend", "the pages"]]
|
|
304
|
+
];
|
|
305
|
+
function typeFromDescription(text2) {
|
|
306
|
+
const t = (text2 ?? "").toLowerCase();
|
|
307
|
+
if (!t)
|
|
308
|
+
return void 0;
|
|
309
|
+
for (const [type, words] of TYPE_WORDS)
|
|
310
|
+
if (words.some((w) => t.includes(w)))
|
|
311
|
+
return type;
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
var TYPE_OF_FRAMEWORK = [
|
|
315
|
+
["spring-boot", "spring-boot"],
|
|
316
|
+
["spring", "spring-boot"],
|
|
317
|
+
["quarkus", "java"],
|
|
318
|
+
["micronaut", "java"],
|
|
319
|
+
["hibernate", "java"],
|
|
320
|
+
["asp.net", "dotnet"],
|
|
321
|
+
["entity-framework", "dotnet"],
|
|
322
|
+
["angular", "frontend"],
|
|
323
|
+
["react", "frontend"],
|
|
324
|
+
["vue", "frontend"],
|
|
325
|
+
["svelte", "frontend"],
|
|
326
|
+
["next", "frontend"],
|
|
327
|
+
["express", "node"],
|
|
328
|
+
["nest", "node"],
|
|
329
|
+
["typeorm", "node"],
|
|
330
|
+
["prisma", "node"],
|
|
331
|
+
["django", "python"],
|
|
332
|
+
["django-rest-framework", "python"],
|
|
333
|
+
["flask", "python"],
|
|
334
|
+
["fastapi", "python"],
|
|
335
|
+
["celery", "python"]
|
|
336
|
+
];
|
|
337
|
+
function typeFromFrameworks(declared) {
|
|
338
|
+
for (const [name, type] of TYPE_OF_FRAMEWORK) {
|
|
339
|
+
const hit = declared.filter((d) => d.name.trim().toLowerCase() === name);
|
|
340
|
+
if (hit.length)
|
|
341
|
+
return { type, signals: hit.map((d) => d.cite) };
|
|
342
|
+
}
|
|
343
|
+
return void 0;
|
|
344
|
+
}
|
|
345
|
+
async function detectRepoType(repo, o = {}) {
|
|
346
|
+
const comp = await runExtractor(componentExtractor, repo, {
|
|
347
|
+
scannedAt: "1970-01-01T00:00:00Z"
|
|
348
|
+
});
|
|
349
|
+
const component = comp.records.some((r) => r.kind === "Component");
|
|
350
|
+
const d = (type, signals, confidence) => ({
|
|
351
|
+
type,
|
|
352
|
+
signals,
|
|
353
|
+
component,
|
|
354
|
+
confidence
|
|
355
|
+
});
|
|
356
|
+
const apigeeFirst = await all(repo, ["**/apiproxy/*.xml", "**/apiproxy/proxies/*.xml"]);
|
|
357
|
+
const declared = apigeeFirst.length ? void 0 : typeFromFrameworks(o.frameworks ?? []);
|
|
358
|
+
if (declared)
|
|
359
|
+
return d(declared.type, declared.signals, "high");
|
|
360
|
+
const apigee = apigeeFirst;
|
|
361
|
+
const kong = await first(repo, ["kong.yaml", "kong.yml", "**/kong.yaml", "**/kong.yml"]);
|
|
362
|
+
if (apigee.length || kong)
|
|
363
|
+
return d("gateway", apigee.length ? apigee : [kong], "high");
|
|
364
|
+
const pom = await marker(repo, ["pom.xml", "build.gradle", "build.gradle.kts"]);
|
|
365
|
+
const pkg = await marker(repo, ["package.json"]);
|
|
366
|
+
const py = await marker(repo, ["pyproject.toml", "requirements.txt", "setup.py"]);
|
|
367
|
+
const gomod = await marker(repo, ["go.mod"]);
|
|
368
|
+
const dotnet = await first(repo, ["*.csproj", "*.sln", "*.fsproj", "**/*.csproj"]);
|
|
369
|
+
const anyBuild = pom ?? pkg ?? py ?? gomod ?? dotnet;
|
|
370
|
+
const cobol = await all(repo, [
|
|
371
|
+
"**/*.cbl",
|
|
372
|
+
"**/*.CBL",
|
|
373
|
+
"**/*.cob",
|
|
374
|
+
"**/*.COB",
|
|
375
|
+
"**/*.jcl",
|
|
376
|
+
"**/*.JCL"
|
|
377
|
+
]);
|
|
378
|
+
if (cobol.length && !anyBuild)
|
|
379
|
+
return d("mainframe", cobol, "high");
|
|
380
|
+
if (pom) {
|
|
381
|
+
const t = await text(repo, pom);
|
|
382
|
+
return /spring-boot|org\.springframework\.boot/.test(t) ? d("spring-boot", [pom], "high") : d("java", [pom], "medium");
|
|
383
|
+
}
|
|
384
|
+
if (pkg) {
|
|
385
|
+
let deps = {};
|
|
386
|
+
try {
|
|
387
|
+
const j = JSON.parse(await text(repo, pkg));
|
|
388
|
+
deps = { ...j.dependencies ?? {}, ...j.devDependencies ?? {} };
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
const fe = FRONTEND_DEPS.find((n) => n in deps);
|
|
392
|
+
return fe ? d("frontend", [pkg], "high") : d("node", [pkg], "medium");
|
|
393
|
+
}
|
|
394
|
+
if (py)
|
|
395
|
+
return d("python", [py], "medium");
|
|
396
|
+
if (gomod)
|
|
397
|
+
return d("go", [gomod], "high");
|
|
398
|
+
if (dotnet)
|
|
399
|
+
return d("dotnet", [dotnet], "high");
|
|
400
|
+
const infra = await all(repo, [
|
|
401
|
+
"**/*.tf",
|
|
402
|
+
"**/k8s/**/*.yaml",
|
|
403
|
+
"**/k8s/**/*.yml",
|
|
404
|
+
"**/kubernetes/**/*.yaml",
|
|
405
|
+
"**/Chart.yaml",
|
|
406
|
+
"**/kustomization.yaml"
|
|
407
|
+
]);
|
|
408
|
+
if (infra.length)
|
|
409
|
+
return d("infra", infra, "medium");
|
|
410
|
+
const topics = await all(repo, ["**/topics/*.yaml", "**/topics/*.yml", "**/topics/**/*.yaml"]);
|
|
411
|
+
if (topics.length)
|
|
412
|
+
return d("event-config", topics, "medium");
|
|
413
|
+
const said = typeFromDescription(o.description);
|
|
414
|
+
if (said)
|
|
415
|
+
return d(said, ["the repo's description in repos.manifest.yaml"], "low");
|
|
416
|
+
return d("unknown", [], "low");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// packages/adopt/dist/discover.js
|
|
420
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
421
|
+
import { readdir as readdir2, realpath } from "node:fs/promises";
|
|
422
|
+
import { basename, isAbsolute, join as join3, relative as relative2, resolve } from "node:path";
|
|
423
|
+
var UNASSIGNED = "unassigned";
|
|
424
|
+
var MockRepoListing = class {
|
|
425
|
+
orgs;
|
|
426
|
+
constructor(orgs) {
|
|
427
|
+
this.orgs = orgs;
|
|
428
|
+
}
|
|
429
|
+
async listRepos(org) {
|
|
430
|
+
const repos = this.orgs[org];
|
|
431
|
+
if (!repos)
|
|
432
|
+
throw new Error(`no organisation ${org} on the mock Git host`);
|
|
433
|
+
return repos.map(({ files: _f, ...r }) => r);
|
|
434
|
+
}
|
|
435
|
+
async openRepo(repo) {
|
|
436
|
+
for (const repos of Object.values(this.orgs)) {
|
|
437
|
+
const r = repos.find((x) => x.name === repo.name);
|
|
438
|
+
if (r)
|
|
439
|
+
return memoryRepo(r.name, "0000000", r.files);
|
|
440
|
+
}
|
|
441
|
+
throw new Error(`no repo ${repo.name} on the mock Git host`);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
async function originUrl(dir) {
|
|
445
|
+
const cfg = await readText(join3(dir, ".git", "config"));
|
|
446
|
+
if (!cfg)
|
|
447
|
+
return void 0;
|
|
448
|
+
const m = /\[remote "origin"\][^[]*?url\s*=\s*(\S+)/.exec(cfg);
|
|
449
|
+
return m?.[1]?.replace(/^([a-z][a-z0-9+.-]*:\/\/)[^/@]+@/i, "$1");
|
|
450
|
+
}
|
|
451
|
+
async function headBranch(dir) {
|
|
452
|
+
const head = await readText(join3(dir, ".git", "HEAD"));
|
|
453
|
+
return head ? /^ref: refs\/heads\/(\S+)/.exec(head)?.[1] : void 0;
|
|
454
|
+
}
|
|
455
|
+
var SKIP_FOLDERS = /* @__PURE__ */ new Set([
|
|
456
|
+
".git",
|
|
457
|
+
".atticus",
|
|
458
|
+
".github",
|
|
459
|
+
".claude",
|
|
460
|
+
".specify",
|
|
461
|
+
".vscode",
|
|
462
|
+
".idea",
|
|
463
|
+
"node_modules",
|
|
464
|
+
"workspace",
|
|
465
|
+
"openspec",
|
|
466
|
+
"org-pack",
|
|
467
|
+
"catalog",
|
|
468
|
+
"proposals",
|
|
469
|
+
"inputs",
|
|
470
|
+
"specs"
|
|
471
|
+
]);
|
|
472
|
+
var SELF_IGNORE = [".git", "node_modules", ".atticus", "openspec", "specs", ".specify"];
|
|
473
|
+
async function hiddenAndNested(dir) {
|
|
474
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
475
|
+
const nested = [];
|
|
476
|
+
const walk = async (abs, rel) => {
|
|
477
|
+
let entries;
|
|
478
|
+
try {
|
|
479
|
+
entries = await readdir2(abs, { withFileTypes: true });
|
|
480
|
+
} catch {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
for (const e of entries) {
|
|
484
|
+
if (!e.isDirectory())
|
|
485
|
+
continue;
|
|
486
|
+
if (e.name.startsWith(".")) {
|
|
487
|
+
hidden.add(e.name);
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (DEFAULT_IGNORES.includes(e.name))
|
|
491
|
+
continue;
|
|
492
|
+
const sub = join3(abs, e.name);
|
|
493
|
+
const path = rel ? `${rel}/${e.name}` : e.name;
|
|
494
|
+
if (await exists(join3(sub, ".git")))
|
|
495
|
+
nested.push(path);
|
|
496
|
+
else
|
|
497
|
+
await walk(sub, path);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
await walk(dir, "");
|
|
501
|
+
return { hidden: [...hidden].sort(), nested: nested.sort() };
|
|
502
|
+
}
|
|
503
|
+
async function visibleRepo(name, dir, ignore = DEFAULT_IGNORES) {
|
|
504
|
+
const { hidden, nested } = await hiddenAndNested(dir);
|
|
505
|
+
const h = fsRepo(name, dir, "0000000", { ignore: [.../* @__PURE__ */ new Set([...ignore, ...hidden])] });
|
|
506
|
+
const shown = (p) => {
|
|
507
|
+
const folders = p.split("/").slice(0, -1);
|
|
508
|
+
return !folders.some((s) => s.startsWith(".") || s === "node_modules") && !nested.some((n) => p.startsWith(`${n}/`));
|
|
509
|
+
};
|
|
510
|
+
return {
|
|
511
|
+
name: h.name,
|
|
512
|
+
commit: h.commit,
|
|
513
|
+
async *listFiles(globs) {
|
|
514
|
+
for await (const p of h.listFiles(globs))
|
|
515
|
+
if (shown(p))
|
|
516
|
+
yield p;
|
|
517
|
+
},
|
|
518
|
+
readText: (p) => h.readText(p)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
async function localCandidates(dir) {
|
|
522
|
+
const skipped = [];
|
|
523
|
+
const repos = [];
|
|
524
|
+
const builds = [];
|
|
525
|
+
const listed = (await readdir2(dir, { withFileTypes: true })).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !SKIP_FOLDERS.has(e.name) && !e.name.startsWith(".")).sort((a, b) => a.name < b.name ? -1 : 1);
|
|
526
|
+
const here = await realpath(dir);
|
|
527
|
+
const real = /* @__PURE__ */ new Map();
|
|
528
|
+
for (const e of listed)
|
|
529
|
+
if (e.isDirectory())
|
|
530
|
+
real.set(await realpath(join3(dir, e.name)), e.name);
|
|
531
|
+
const entries = [];
|
|
532
|
+
for (const e of listed) {
|
|
533
|
+
if (e.isDirectory()) {
|
|
534
|
+
entries.push(e.name);
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
const to = await realpath(join3(dir, e.name)).catch(() => void 0);
|
|
538
|
+
if (!to || !await isDir(to)) {
|
|
539
|
+
skipped.push({ path: e.name, reason: "a link to nothing" });
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
const up = relative2(to, here);
|
|
543
|
+
if (up === "" || !up.startsWith("..") && !isAbsolute(up)) {
|
|
544
|
+
skipped.push({ path: e.name, reason: "a link to this folder or one it is in" });
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const had = real.get(to);
|
|
548
|
+
if (had) {
|
|
549
|
+
skipped.push({ path: e.name, reason: `a link to ${had}, which is read already` });
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
real.set(to, e.name);
|
|
553
|
+
entries.push(e.name);
|
|
554
|
+
}
|
|
555
|
+
const selfClone = await exists(join3(dir, ".git"));
|
|
556
|
+
for (const name of entries) {
|
|
557
|
+
const sub = join3(dir, name);
|
|
558
|
+
const repo = await visibleRepo(name, sub);
|
|
559
|
+
const isClone = await isDir(join3(sub, ".git"));
|
|
560
|
+
if (selfClone && !isClone && await exists(join3(sub, ".git"))) {
|
|
561
|
+
skipped.push({
|
|
562
|
+
path: name,
|
|
563
|
+
reason: "a git worktree (a checkout of another clone), not a repo"
|
|
564
|
+
});
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
const det = await detectRepoType(repo);
|
|
568
|
+
if (isClone)
|
|
569
|
+
repos.push({ dir: sub, repo });
|
|
570
|
+
else if (det.type !== "unknown" || det.component)
|
|
571
|
+
builds.push(name);
|
|
572
|
+
else
|
|
573
|
+
skipped.push({ path: name, reason: "no build file, code or platform files" });
|
|
574
|
+
}
|
|
575
|
+
skipped.sort((a, b) => a.path < b.path ? -1 : 1);
|
|
576
|
+
if (selfClone) {
|
|
577
|
+
const self = await visibleRepo(basename(resolve(dir)), dir, SELF_IGNORE);
|
|
578
|
+
return { repos: [{ dir, repo: self, modules: builds }, ...repos], skipped };
|
|
579
|
+
}
|
|
580
|
+
for (const name of builds)
|
|
581
|
+
repos.push({ dir: join3(dir, name), repo: await visibleRepo(name, join3(dir, name)) });
|
|
582
|
+
repos.sort((a, b) => basename(a.dir) < basename(b.dir) ? -1 : 1);
|
|
583
|
+
if (repos.length === 0) {
|
|
584
|
+
const self = await visibleRepo(basename(resolve(dir)), dir, SELF_IGNORE);
|
|
585
|
+
const det = await detectRepoType(self);
|
|
586
|
+
if (await isDir(join3(dir, ".git")) || det.type !== "unknown" || det.component)
|
|
587
|
+
return { repos: [{ dir, repo: self }], skipped: [] };
|
|
588
|
+
}
|
|
589
|
+
return { repos, skipped };
|
|
590
|
+
}
|
|
591
|
+
function questionsFor(entry, det) {
|
|
592
|
+
const q = [];
|
|
593
|
+
if (entry.owner === UNASSIGNED)
|
|
594
|
+
q.push({
|
|
595
|
+
repo: entry.id,
|
|
596
|
+
field: "owner",
|
|
597
|
+
question: `Which team owns ${entry.id}? Set owner in repos.manifest.yaml (a team id).`
|
|
598
|
+
});
|
|
599
|
+
if (!entry.system)
|
|
600
|
+
q.push({
|
|
601
|
+
repo: entry.id,
|
|
602
|
+
field: "system",
|
|
603
|
+
question: `Which system does ${entry.id} belong to? Set system in repos.manifest.yaml, or leave it out if the repo serves several.`
|
|
604
|
+
});
|
|
605
|
+
if (det.type === "unknown")
|
|
606
|
+
q.push({
|
|
607
|
+
repo: entry.id,
|
|
608
|
+
field: "type",
|
|
609
|
+
question: `What kind of repo is ${entry.id}? No build file or platform file told us; set type in repos.manifest.yaml.`
|
|
610
|
+
});
|
|
611
|
+
return q;
|
|
612
|
+
}
|
|
613
|
+
async function existingManifest(path) {
|
|
614
|
+
const text2 = await readText(path);
|
|
615
|
+
if (text2 === void 0)
|
|
616
|
+
return void 0;
|
|
617
|
+
try {
|
|
618
|
+
const r = RepoManifest.safeParse((0, import_yaml2.parse)(text2));
|
|
619
|
+
return r.success ? r.data.repos : null;
|
|
620
|
+
} catch {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
var HEADER = "# Proposed by atticus repos discover. Types come from each repo's files; owners marked\n# `unassigned` and missing systems are questions (see QUESTIONS.md). Review before use.\n";
|
|
625
|
+
async function reposDiscover(root, source, o = {}, paths) {
|
|
626
|
+
const p = paths ?? await resolveMetarepo(root);
|
|
627
|
+
let platform = o.platform;
|
|
628
|
+
if (!platform) {
|
|
629
|
+
try {
|
|
630
|
+
const profiles = (await readdir2(join3(p.orgPackDir, "profiles"), { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
631
|
+
platform = profiles.length === 1 ? profiles[0] : "default";
|
|
632
|
+
} catch {
|
|
633
|
+
platform = "default";
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const found = [];
|
|
637
|
+
let skipped = [];
|
|
638
|
+
const used = /* @__PURE__ */ new Set();
|
|
639
|
+
const uniqueId = (name) => {
|
|
640
|
+
let id = slug(name);
|
|
641
|
+
for (let n = 2; used.has(id); n++)
|
|
642
|
+
id = `${slug(name)}-${n}`;
|
|
643
|
+
used.add(id);
|
|
644
|
+
return id;
|
|
645
|
+
};
|
|
646
|
+
if ("dir" in source) {
|
|
647
|
+
const c = await localCandidates(source.dir);
|
|
648
|
+
skipped = c.skipped;
|
|
649
|
+
for (const { dir, repo, modules } of c.repos) {
|
|
650
|
+
const det = await detectRepoType(repo);
|
|
651
|
+
const rel = toPosix(relative2(root, dir));
|
|
652
|
+
const inside = !rel.startsWith("..") && !isAbsolute(rel);
|
|
653
|
+
const url = o.inPlace && inside ? rel === "" ? "." : `./${rel}` : await originUrl(dir) ?? toPosix(resolve(dir));
|
|
654
|
+
const entry = {
|
|
655
|
+
id: uniqueId(dir === source.dir ? basename(resolve(dir)) : basename(dir)),
|
|
656
|
+
url,
|
|
657
|
+
defaultBranch: await headBranch(dir) ?? "main",
|
|
658
|
+
branches: [],
|
|
659
|
+
type: det.type,
|
|
660
|
+
owner: UNASSIGNED,
|
|
661
|
+
extractors: [],
|
|
662
|
+
dialects: [],
|
|
663
|
+
sparse: [],
|
|
664
|
+
archived: false,
|
|
665
|
+
hosts: [],
|
|
666
|
+
modules: (modules ?? []).map((m) => ({ path: m, component: slug(m), hosts: [] }))
|
|
667
|
+
};
|
|
668
|
+
found.push({ entry, detection: det, dir });
|
|
669
|
+
}
|
|
670
|
+
} else {
|
|
671
|
+
const listed = (await source.host.listRepos(source.org)).sort((a, b) => a.name.localeCompare(b.name));
|
|
672
|
+
for (const l of listed) {
|
|
673
|
+
const det = await detectRepoType(await source.host.openRepo(l));
|
|
674
|
+
found.push({
|
|
675
|
+
entry: {
|
|
676
|
+
id: uniqueId(l.name),
|
|
677
|
+
url: l.url,
|
|
678
|
+
defaultBranch: l.defaultBranch ?? "main",
|
|
679
|
+
branches: [],
|
|
680
|
+
type: det.type,
|
|
681
|
+
owner: UNASSIGNED,
|
|
682
|
+
extractors: [],
|
|
683
|
+
dialects: [],
|
|
684
|
+
sparse: [],
|
|
685
|
+
archived: l.archived ?? false,
|
|
686
|
+
hosts: [],
|
|
687
|
+
modules: []
|
|
688
|
+
},
|
|
689
|
+
detection: det
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const questions = found.flatMap((f) => questionsFor(f.entry, f.detection));
|
|
694
|
+
const manifestPath = join3(p.orgPackDir, "profiles", platform, "repos.manifest.yaml");
|
|
695
|
+
const current = await existingManifest(manifestPath);
|
|
696
|
+
const result = { platform, repos: found, skipped, questions, wrote: "nothing" };
|
|
697
|
+
if (o.dryRun)
|
|
698
|
+
return result;
|
|
699
|
+
const doc = (repos) => (0, import_yaml2.stringify)({
|
|
700
|
+
schemaVersion: 1,
|
|
701
|
+
platform,
|
|
702
|
+
repos: repos.map((r) => ({
|
|
703
|
+
id: r.id,
|
|
704
|
+
url: r.url,
|
|
705
|
+
defaultBranch: r.defaultBranch,
|
|
706
|
+
...r.branches.length ? { branches: r.branches } : {},
|
|
707
|
+
type: r.type,
|
|
708
|
+
owner: r.owner,
|
|
709
|
+
...r.system ? { system: r.system } : {},
|
|
710
|
+
...r.component ? { component: r.component } : {},
|
|
711
|
+
...r.archived ? { archived: true } : {},
|
|
712
|
+
...r.modules.length ? {
|
|
713
|
+
modules: r.modules.map((m) => ({
|
|
714
|
+
path: m.path,
|
|
715
|
+
component: m.component,
|
|
716
|
+
...m.hosts.length ? { hosts: m.hosts } : {}
|
|
717
|
+
}))
|
|
718
|
+
} : {}
|
|
719
|
+
}))
|
|
720
|
+
}, { lineWidth: 0 });
|
|
721
|
+
const proposalDir = join3(p.proposalsDir, "repos", platform);
|
|
722
|
+
if (current === void 0 || current !== null && current.length === 0) {
|
|
723
|
+
await writeNew(manifestPath, HEADER + doc(found.map((f) => f.entry)), false);
|
|
724
|
+
result.wrote = "manifest";
|
|
725
|
+
result.file = toPosix(relative2(root, manifestPath));
|
|
726
|
+
} else {
|
|
727
|
+
const kept = current ?? [];
|
|
728
|
+
const have = new Set(kept.flatMap((r) => [r.id, r.url]));
|
|
729
|
+
const added = found.filter((f) => !have.has(f.entry.id) && !have.has(f.entry.url));
|
|
730
|
+
const file = join3(proposalDir, "repos.manifest.yaml");
|
|
731
|
+
await writeNew(file, HEADER + doc([...kept, ...added.map((a) => a.entry)]), false);
|
|
732
|
+
result.wrote = "proposal";
|
|
733
|
+
result.file = toPosix(relative2(root, file));
|
|
734
|
+
}
|
|
735
|
+
if (questions.length) {
|
|
736
|
+
const qf = join3(proposalDir, "QUESTIONS.md");
|
|
737
|
+
await writeNew(qf, [
|
|
738
|
+
`# Questions for ${platform}`,
|
|
739
|
+
"",
|
|
740
|
+
"Discovery read files only; these need a person.",
|
|
741
|
+
"",
|
|
742
|
+
...questions.map((q) => `- [ ] ${q.repo} (${q.field}): ${q.question}`),
|
|
743
|
+
""
|
|
744
|
+
].join("\n"), false);
|
|
745
|
+
result.questionsFile = toPosix(relative2(root, qf));
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// packages/adopt/dist/orgpack-files.js
|
|
751
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
752
|
+
import { join as join4 } from "node:path";
|
|
753
|
+
var yaml = (data, header) => `${header ? `# ${header}
|
|
754
|
+
` : ""}${(0, import_yaml3.stringify)(data, { lineWidth: 0 })}`;
|
|
755
|
+
var DEFAULT_HEADER = "Built-in default written out by Atticus. Change it through a pull request; delete the file to go back to the default.";
|
|
756
|
+
var CHECKLISTS_README = "# Checklists\n\nCritic rule packs (contract C3), one YAML file per pack. The constitution is built in; packs here add organisation rules.\n";
|
|
757
|
+
var STARTER_CHECKLISTS_README = `${CHECKLISTS_README}
|
|
758
|
+
\`review.yaml\` is the starter review (ADR 0120): AI rules the critic's lenses apply to every draft, judging its meaning against the estate and the brief, and two code checks (a call's timeout, a store write's rollback step). It is this organisation's configuration, not the constitution: change, waive or retire its rules, or delete one, through a pull request. Delete the file to turn the starter review off; \`atticus upgrade\` won't add it back.
|
|
759
|
+
`;
|
|
760
|
+
var FOLDER_READMES = [
|
|
761
|
+
[
|
|
762
|
+
"patterns/README.md",
|
|
763
|
+
"# Integration patterns\n\nOne YAML file per approved pattern (`<code>.yaml`). Planned interfaces must name one.\n"
|
|
764
|
+
],
|
|
765
|
+
[
|
|
766
|
+
"dialects/README.md",
|
|
767
|
+
"# Dialects\n\nRule files that teach the scan a house framework (contract C2). Start one with `atticus dialect new`.\n"
|
|
768
|
+
],
|
|
769
|
+
[
|
|
770
|
+
"agents/README.md",
|
|
771
|
+
"# Agents\n\nAI agent definitions (contract C5), one YAML file per agent.\n"
|
|
772
|
+
],
|
|
773
|
+
[
|
|
774
|
+
"process/snow/README.md",
|
|
775
|
+
"# Service-desk templates\n\nOne YAML file per request template. `atticus templates draft` proposes them from wikis and past tickets.\n"
|
|
776
|
+
]
|
|
777
|
+
];
|
|
778
|
+
async function writeStartingPack(dir, p) {
|
|
779
|
+
const files = [
|
|
780
|
+
[
|
|
781
|
+
"pack.yaml",
|
|
782
|
+
yaml({ schemaVersion: 1, name: p.name, version: "0.1.0", owner: p.owner }, "The org pack.")
|
|
783
|
+
],
|
|
784
|
+
["sources.yaml", yaml(p.sources, "Where things are and what Atticus reads.")],
|
|
785
|
+
[
|
|
786
|
+
`profiles/${p.platform}/profile.yaml`,
|
|
787
|
+
yaml({
|
|
788
|
+
schemaVersion: 1,
|
|
789
|
+
platform: p.platform,
|
|
790
|
+
name: p.platformName ?? p.platform,
|
|
791
|
+
systems: []
|
|
792
|
+
})
|
|
793
|
+
],
|
|
794
|
+
[
|
|
795
|
+
`profiles/${p.platform}/repos.manifest.yaml`,
|
|
796
|
+
yaml({ schemaVersion: 1, platform: p.platform, repos: [] })
|
|
797
|
+
],
|
|
798
|
+
["checklists/README.md", p.defaults ? STARTER_CHECKLISTS_README : CHECKLISTS_README],
|
|
799
|
+
...FOLDER_READMES
|
|
800
|
+
];
|
|
801
|
+
if (p.defaults)
|
|
802
|
+
files.push([STARTER_REVIEW_PATH, await starterReviewText()], ["change-types.yaml", yaml(DEFAULT_CHANGE_TYPES, DEFAULT_HEADER)], ["gates.yaml", yaml(DEFAULT_GATES, DEFAULT_HEADER)], ["runtime.yaml", yaml(DEFAULT_RUNTIME, DEFAULT_HEADER)], ["test-strategy.yaml", yaml(DEFAULT_TEST_STRATEGY, DEFAULT_HEADER)]);
|
|
803
|
+
const written = [];
|
|
804
|
+
for (const [rel, text2] of files)
|
|
805
|
+
if (await writeNew(join4(dir, ...rel.split("/")), text2))
|
|
806
|
+
written.push(rel);
|
|
807
|
+
return written.sort();
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// packages/adopt/dist/init.js
|
|
811
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
812
|
+
import { dirname as dirname2, join as join5, resolve as resolve2 } from "node:path";
|
|
813
|
+
var ATTICUS_FOLDER_IGNORE = ["/.atticus/*", "!/.atticus/metarepo.yaml"];
|
|
814
|
+
async function insideRepo(dir) {
|
|
815
|
+
for (let d = resolve2(dir); ; d = dirname2(d)) {
|
|
816
|
+
if (await exists(join5(d, ".git")))
|
|
817
|
+
return true;
|
|
818
|
+
if (dirname2(d) === d)
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async function makeRepo(dir, git) {
|
|
823
|
+
try {
|
|
824
|
+
await git(["init", "-b", "main"], dir);
|
|
825
|
+
} catch {
|
|
826
|
+
await git(["init"], dir);
|
|
827
|
+
await git(["symbolic-ref", "HEAD", "refs/heads/main"], dir);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
var README = (name, platform) => `# ${name}
|
|
831
|
+
|
|
832
|
+
The Atticus metarepo for the ${platform} platform.
|
|
833
|
+
|
|
834
|
+
- \`org-pack/\`: how this organisation works (change types, gates, patterns, checklists, skills). Changed only through pull requests.
|
|
835
|
+
- \`catalog/generated/\`: the catalog Atticus writes on every scan, one YAML record per file. \`catalog/curated/\` holds people's decisions.
|
|
836
|
+
- \`openspec/\`: changes (\`openspec/changes/<id>/\`) and the living specs.
|
|
837
|
+
- \`workspace/\`, \`.index/\`, \`.runs/\`: local state, never committed.
|
|
838
|
+
|
|
839
|
+
Next: \`atticus repos discover <folder of clones>\` and \`atticus workspace sync\`, then read each repo: \`/atticus-map <repo>\` in your assistant (or \`atticus map <repo>\`), then \`atticus verify <repo> --write\`.
|
|
840
|
+
`;
|
|
841
|
+
async function init(dir, o) {
|
|
842
|
+
if (!RecordId.safeParse(o.name).success)
|
|
843
|
+
throw new AdoptError("usage", `"${o.name}" can't be an org pack name; use a lowercase slug such as shop-platform.`);
|
|
844
|
+
if (!RecordId.safeParse(o.platform).success)
|
|
845
|
+
throw new AdoptError("usage", `"${o.platform}" can't be a platform id; use a lowercase slug such as shop.`);
|
|
846
|
+
if (await isDir(join5(dir, ORG_PACK_DIR)))
|
|
847
|
+
throw new AdoptError("exists", `${join5(dir, ORG_PACK_DIR)} already exists; this folder is already a metarepo.`);
|
|
848
|
+
if (await isDir(join5(dir, ".atticus", ORG_PACK_DIR)))
|
|
849
|
+
throw new AdoptError("exists", "This repo is adopted in place (.atticus/org-pack); use it as it is, or run atticus unadopt first.");
|
|
850
|
+
if (o.packFrom && !await isDir(o.packFrom))
|
|
851
|
+
throw new AdoptError("usage", `There is no org pack folder at ${o.packFrom}.`);
|
|
852
|
+
await mkdir2(dir, { recursive: true });
|
|
853
|
+
let gitState = "inside";
|
|
854
|
+
let gitNote;
|
|
855
|
+
if (!await insideRepo(dir)) {
|
|
856
|
+
try {
|
|
857
|
+
await makeRepo(dir, o.git ?? createGit());
|
|
858
|
+
gitState = "initialised";
|
|
859
|
+
} catch (e) {
|
|
860
|
+
gitState = "unavailable";
|
|
861
|
+
gitNote = `git didn't run (${e.message.split("\n")[0]}), so the folder isn't a git repository yet: run git init -b main in it.`;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
const before = new Set(await listFiles(dir));
|
|
865
|
+
const orgPack = join5(dir, ORG_PACK_DIR);
|
|
866
|
+
const questions = [];
|
|
867
|
+
const owner = o.owner ?? "unassigned";
|
|
868
|
+
if (!o.owner && !o.packFrom)
|
|
869
|
+
questions.push("Who owns the org pack? Set owner in org-pack/pack.yaml (normally the platform lead).");
|
|
870
|
+
if (o.packFrom) {
|
|
871
|
+
await copyDir(o.packFrom, orgPack);
|
|
872
|
+
await writeStartingPack(orgPack, {
|
|
873
|
+
name: o.name,
|
|
874
|
+
platform: o.platform,
|
|
875
|
+
owner,
|
|
876
|
+
sources: { schemaVersion: SOURCES_SCHEMA_VERSION },
|
|
877
|
+
defaults: false
|
|
878
|
+
});
|
|
879
|
+
} else {
|
|
880
|
+
await writeStartingPack(orgPack, {
|
|
881
|
+
name: o.name,
|
|
882
|
+
platform: o.platform,
|
|
883
|
+
owner,
|
|
884
|
+
sources: { schemaVersion: SOURCES_SCHEMA_VERSION },
|
|
885
|
+
defaults: true
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
let mode = "folders";
|
|
889
|
+
let note;
|
|
890
|
+
if ((o.openspec ?? "auto") !== "folders") {
|
|
891
|
+
try {
|
|
892
|
+
const os = createOpenSpec({ root: dir });
|
|
893
|
+
await os.assertSupported();
|
|
894
|
+
if (!await exists(join5(dir, "openspec", "config.yaml")))
|
|
895
|
+
await os.init();
|
|
896
|
+
mode = "cli";
|
|
897
|
+
} catch (e) {
|
|
898
|
+
if (o.openspec === "cli")
|
|
899
|
+
throw e;
|
|
900
|
+
note = `The OpenSpec CLI didn't run (${e.message.split("\n")[0]}), so openspec/ was made as plain folders.`;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if (mode === "folders") {
|
|
904
|
+
await mkdir2(join5(dir, "openspec", "specs"), { recursive: true });
|
|
905
|
+
await mkdir2(join5(dir, "openspec", "changes", "archive"), { recursive: true });
|
|
906
|
+
await writeNew(join5(dir, "openspec", "specs", "README.md"), "# Living specs\n");
|
|
907
|
+
await writeNew(join5(dir, "openspec", "changes", "archive", "README.md"), "# Archived changes\n");
|
|
908
|
+
}
|
|
909
|
+
await writeOrgFlowSchema(dir);
|
|
910
|
+
await setDefaultSchema(dir, ORG_FLOW);
|
|
911
|
+
let store;
|
|
912
|
+
if (o.openspecStore) {
|
|
913
|
+
const id = storeIdOf(o.name);
|
|
914
|
+
await writeStoreMarker(dir, { id });
|
|
915
|
+
store = { id };
|
|
916
|
+
}
|
|
917
|
+
await writeNew(join5(dir, "catalog", "generated", "README.md"), "# Generated catalog\n\nWritten by `atticus scan`. Don't edit: record decisions in catalog/curated/.\n");
|
|
918
|
+
await writeNew(join5(dir, "catalog", "curated", "README.md"), "# Curated decisions\n\nPeople's decisions (aliases, names, confirmations) applied over every scan.\n");
|
|
919
|
+
await writeNew(join5(dir, "README.md"), README(o.name, o.platform));
|
|
920
|
+
await ensureGitignore(dir);
|
|
921
|
+
const gi = join5(dir, ".gitignore");
|
|
922
|
+
const text2 = await readFile2(gi, "utf8");
|
|
923
|
+
if (!/^\/?\.atticus(\/\*?)?$/m.test(text2))
|
|
924
|
+
await writeFile2(gi, `${text2.endsWith("\n") ? text2 : `${text2}
|
|
925
|
+
`}${ATTICUS_FOLDER_IGNORE.join("\n")}
|
|
926
|
+
`, "utf8");
|
|
927
|
+
const op = await loadOrgPack(orgPack);
|
|
928
|
+
let assistantFiles = 0;
|
|
929
|
+
try {
|
|
930
|
+
const a = await generateAssistantFiles(dir);
|
|
931
|
+
assistantFiles = a.results.filter((x) => x.written).length;
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
934
|
+
const created = (await listFiles(dir)).filter((f) => !before.has(f));
|
|
935
|
+
return {
|
|
936
|
+
root: dir,
|
|
937
|
+
created,
|
|
938
|
+
assistantFiles,
|
|
939
|
+
openspec: mode,
|
|
940
|
+
...store ? { openspecStore: store } : {},
|
|
941
|
+
...note ? { openspecNote: note } : {},
|
|
942
|
+
problems: op.problems,
|
|
943
|
+
questions,
|
|
944
|
+
git: gitState,
|
|
945
|
+
...gitNote ? { gitNote } : {}
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// packages/adopt/dist/adopt.js
|
|
950
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
951
|
+
import { mkdir as mkdir3, readdir as readdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
952
|
+
import { basename as basename2, join as join6, resolve as resolve3 } from "node:path";
|
|
953
|
+
var SNAPSHOT_FILE = `${ATTICUS_DIR}/state/adopt-snapshot.json`;
|
|
954
|
+
var ADOPTED_FILE = `${ATTICUS_DIR}/adopted.yaml`;
|
|
955
|
+
var PROMPT_DIRS = [".github/prompts", ".claude/commands", "prompts", ".prompts"];
|
|
956
|
+
var AGENT_DIRS = [".github/agents", ".claude/agents", ".github/chatmodes"];
|
|
957
|
+
var SKILL_DIRS = [".claude/skills", ".github/instructions", "skills"];
|
|
958
|
+
var INSTRUCTION_FILES = ["CLAUDE.md", "AGENTS.md", ".github/copilot-instructions.md"];
|
|
959
|
+
var CONSTITUTIONS = [
|
|
960
|
+
".specify/memory/constitution.md",
|
|
961
|
+
"memory/constitution.md",
|
|
962
|
+
"CONSTITUTION.md",
|
|
963
|
+
"constitution.md",
|
|
964
|
+
"docs/constitution.md"
|
|
965
|
+
];
|
|
966
|
+
async function isOpenSpecFolder(dir) {
|
|
967
|
+
return await exists(join6(dir, "config.yaml")) || await exists(join6(dir, "project.md")) || await isDir(join6(dir, "specs")) || await isDir(join6(dir, "changes"));
|
|
968
|
+
}
|
|
969
|
+
async function detect(root) {
|
|
970
|
+
const d = { prompts: [], agents: [], skills: [], instructions: [] };
|
|
971
|
+
if (await isDir(join6(root, "openspec")) && await isOpenSpecFolder(join6(root, "openspec")))
|
|
972
|
+
d.openspec = "openspec";
|
|
973
|
+
else {
|
|
974
|
+
for (const e of (await readdir3(root, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
975
|
+
if (!e.isDirectory() || e.name.startsWith(".") || e.name === "node_modules")
|
|
976
|
+
continue;
|
|
977
|
+
const cand = join6(root, e.name, "openspec");
|
|
978
|
+
if (await isDir(cand) && await isOpenSpecFolder(cand)) {
|
|
979
|
+
d.openspec = `${e.name}/openspec`;
|
|
980
|
+
break;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (await isDir(join6(root, ".specify"))) {
|
|
985
|
+
d.speckit = { specify: ".specify" };
|
|
986
|
+
if (await isDir(join6(root, "specs")))
|
|
987
|
+
d.speckit.specs = "specs";
|
|
988
|
+
if (await exists(join6(root, ".specify/memory/constitution.md")))
|
|
989
|
+
d.speckit.constitution = ".specify/memory/constitution.md";
|
|
990
|
+
}
|
|
991
|
+
for (const c of CONSTITUTIONS)
|
|
992
|
+
if (await exists(join6(root, c))) {
|
|
993
|
+
d.constitution = c;
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
for (const p of PROMPT_DIRS)
|
|
997
|
+
if (await isDir(join6(root, p)))
|
|
998
|
+
d.prompts.push(p);
|
|
999
|
+
for (const p of AGENT_DIRS)
|
|
1000
|
+
if (await isDir(join6(root, p)))
|
|
1001
|
+
d.agents.push(p);
|
|
1002
|
+
for (const p of SKILL_DIRS)
|
|
1003
|
+
if (await isDir(join6(root, p)))
|
|
1004
|
+
d.skills.push(p);
|
|
1005
|
+
for (const f of INSTRUCTION_FILES)
|
|
1006
|
+
if (await exists(join6(root, f)))
|
|
1007
|
+
d.instructions.push(f);
|
|
1008
|
+
if (await isDir(join6(root, "catalog", "generated")))
|
|
1009
|
+
d.catalog = "catalog";
|
|
1010
|
+
for (const c of ["CODEOWNERS", ".github/CODEOWNERS", "docs/CODEOWNERS"])
|
|
1011
|
+
if (await exists(join6(root, c))) {
|
|
1012
|
+
d.codeowners = c;
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
return d;
|
|
1016
|
+
}
|
|
1017
|
+
async function takeSnapshot(root) {
|
|
1018
|
+
const skipNames = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
1019
|
+
const files = await hashTree(root, { skipPaths: [ATTICUS_DIR] });
|
|
1020
|
+
const dirs = (await listDirs(root, skipNames)).filter((d) => d !== ATTICUS_DIR && !d.startsWith(`${ATTICUS_DIR}/`));
|
|
1021
|
+
return { schemaVersion: 1, files, dirs };
|
|
1022
|
+
}
|
|
1023
|
+
var GITIGNORE = `# Atticus local state in an adopted repo: never committed.
|
|
1024
|
+
${STATE_FOLDERS.map((f) => `/${f}/`).join("\n")}
|
|
1025
|
+
/executors.yaml
|
|
1026
|
+
/inputs/inbox/
|
|
1027
|
+
`;
|
|
1028
|
+
var README2 = `# .atticus
|
|
1029
|
+
|
|
1030
|
+
This repo is adopted by Atticus in place. Everything Atticus owns is in this folder; no other
|
|
1031
|
+
file of the repo was changed.
|
|
1032
|
+
|
|
1033
|
+
- \`org-pack/\`: how Atticus works here. \`org-pack/sources.yaml\` \`layout\` points at the repo's
|
|
1034
|
+
own OpenSpec folder, catalog, constitution, prompts and skills; nothing was moved.
|
|
1035
|
+
- \`adopted.yaml\`: what adopt found.
|
|
1036
|
+
- \`proposals/\`: what Atticus proposes for people to review (repos, absorbed items).
|
|
1037
|
+
- Local state (\`workspace/\`, \`.index/\`, \`.runs/\`, \`state/\`) is ignored by \`.gitignore\` here.
|
|
1038
|
+
|
|
1039
|
+
To remove Atticus: \`atticus unadopt\`. It deletes this folder and anything else Atticus created
|
|
1040
|
+
since adoption, and leaves every other file exactly as it was.
|
|
1041
|
+
`;
|
|
1042
|
+
async function adopt(path, o = {}) {
|
|
1043
|
+
const t0 = performance.now();
|
|
1044
|
+
const root = resolve3(path);
|
|
1045
|
+
if (!await isDir(root))
|
|
1046
|
+
throw new AdoptError("not-found", `There is no folder ${path}.`);
|
|
1047
|
+
if (await isDir(join6(root, ATTICUS_DIR, ORG_PACK_DIR)))
|
|
1048
|
+
throw new AdoptError("exists", `${path} is already adopted (${ATTICUS_DIR}/${ORG_PACK_DIR}).`);
|
|
1049
|
+
if (await isDir(join6(root, ORG_PACK_DIR)))
|
|
1050
|
+
throw new AdoptError("exists", `${path} is already an Atticus metarepo (org-pack/); use it with --root as it is.`);
|
|
1051
|
+
const snapshot2 = await takeSnapshot(root);
|
|
1052
|
+
const detected = await detect(root);
|
|
1053
|
+
const folder = slug(basename2(root));
|
|
1054
|
+
const platform = o.platform ?? folder;
|
|
1055
|
+
const name = o.name ?? folder;
|
|
1056
|
+
const layout = {
|
|
1057
|
+
openspec: detected.openspec ?? `${ATTICUS_DIR}/openspec`,
|
|
1058
|
+
catalog: detected.catalog ?? `${ATTICUS_DIR}/catalog`,
|
|
1059
|
+
...detected.constitution ? { constitution: detected.constitution } : {},
|
|
1060
|
+
skills: detected.skills,
|
|
1061
|
+
prompts: detected.prompts
|
|
1062
|
+
};
|
|
1063
|
+
const paths = pathsFor(root, true, layout);
|
|
1064
|
+
const a = join6(root, ATTICUS_DIR);
|
|
1065
|
+
await mkdir3(a, { recursive: true });
|
|
1066
|
+
await writeStartingPack(paths.orgPackDir, {
|
|
1067
|
+
name,
|
|
1068
|
+
platform,
|
|
1069
|
+
owner: "unassigned",
|
|
1070
|
+
sources: { schemaVersion: SOURCES_SCHEMA_VERSION, layout },
|
|
1071
|
+
defaults: true
|
|
1072
|
+
});
|
|
1073
|
+
if (!detected.openspec) {
|
|
1074
|
+
await writeNew(join6(a, "openspec", "specs", "README.md"), "# Living specs\n");
|
|
1075
|
+
await writeNew(join6(a, "openspec", "changes", "archive", "README.md"), "# Archived changes\n");
|
|
1076
|
+
await writeOrgFlowSchema(a);
|
|
1077
|
+
await setDefaultSchema(a, ORG_FLOW);
|
|
1078
|
+
}
|
|
1079
|
+
if (!detected.catalog) {
|
|
1080
|
+
await writeNew(join6(a, "catalog", "generated", "README.md"), "# Generated catalog\n\nWritten by `atticus scan`.\n");
|
|
1081
|
+
await writeNew(join6(a, "catalog", "curated", "README.md"), "# Curated decisions\n\nPeople's decisions applied over every scan.\n");
|
|
1082
|
+
}
|
|
1083
|
+
await writeNew(join6(a, ".gitignore"), GITIGNORE);
|
|
1084
|
+
await writeNew(join6(a, "README.md"), README2);
|
|
1085
|
+
const disc = await reposDiscover(root, { dir: root }, { platform, inPlace: true }, paths);
|
|
1086
|
+
await writeNew(join6(root, ADOPTED_FILE), (0, import_yaml4.stringify)({
|
|
1087
|
+
schemaVersion: 1,
|
|
1088
|
+
platform,
|
|
1089
|
+
detected,
|
|
1090
|
+
repos: disc.repos.map((r) => ({
|
|
1091
|
+
id: r.entry.id,
|
|
1092
|
+
path: r.entry.url,
|
|
1093
|
+
type: r.detection.type,
|
|
1094
|
+
signals: r.detection.signals
|
|
1095
|
+
}))
|
|
1096
|
+
}, { lineWidth: 0 }));
|
|
1097
|
+
await mkdir3(join6(root, ATTICUS_DIR, "state"), { recursive: true });
|
|
1098
|
+
await writeFile3(join6(root, SNAPSHOT_FILE), JSON.stringify(snapshot2, null, 1), "utf8");
|
|
1099
|
+
const after = await hashTree(root, { skipPaths: [ATTICUS_DIR] });
|
|
1100
|
+
const changed = Object.keys({ ...snapshot2.files, ...after }).filter((f) => snapshot2.files[f] !== after[f]);
|
|
1101
|
+
if (changed.length)
|
|
1102
|
+
throw new AdoptError("source", `Adopt changed files outside ${ATTICUS_DIR}/, which it must never do: ${changed.join(", ")}. Run atticus unadopt.`);
|
|
1103
|
+
const op = await loadOrgPack(paths.orgPackDir);
|
|
1104
|
+
const written = (await listFiles(a)).map((f) => toPosix(`${ATTICUS_DIR}/${f}`));
|
|
1105
|
+
return {
|
|
1106
|
+
root,
|
|
1107
|
+
platform,
|
|
1108
|
+
detected,
|
|
1109
|
+
layout,
|
|
1110
|
+
repos: disc.repos,
|
|
1111
|
+
questions: disc.questions,
|
|
1112
|
+
written,
|
|
1113
|
+
problems: op.problems,
|
|
1114
|
+
ms: Math.round(performance.now() - t0)
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// packages/adopt/dist/unadopt.js
|
|
1119
|
+
import { readFile as readFile3, rm, rmdir, writeFile as writeFile4 } from "node:fs/promises";
|
|
1120
|
+
import { join as join7, resolve as resolve4 } from "node:path";
|
|
1121
|
+
var ROOT_STATE = ["workspace", ".index", ".runs"];
|
|
1122
|
+
var CHANGE_OWNED = /* @__PURE__ */ new Set(["change.yaml", "brief.md", "repos.yaml", "ux.md"]);
|
|
1123
|
+
function changeOwned(path, openspec, sure, newChange) {
|
|
1124
|
+
const prefix = `${openspec}/changes/`;
|
|
1125
|
+
if (!path.startsWith(prefix))
|
|
1126
|
+
return false;
|
|
1127
|
+
const parts = path.slice(prefix.length).split("/");
|
|
1128
|
+
const lead = parts[0] === "archive" ? 2 : 1;
|
|
1129
|
+
if (parts.length <= lead)
|
|
1130
|
+
return false;
|
|
1131
|
+
const inChange = parts.slice(lead);
|
|
1132
|
+
if (inChange[0] === "review")
|
|
1133
|
+
return true;
|
|
1134
|
+
if (inChange.length !== 1)
|
|
1135
|
+
return false;
|
|
1136
|
+
if (inChange[0] === "change.yaml")
|
|
1137
|
+
return true;
|
|
1138
|
+
if (!sure)
|
|
1139
|
+
return false;
|
|
1140
|
+
if (CHANGE_OWNED.has(inChange[0]))
|
|
1141
|
+
return true;
|
|
1142
|
+
const changeYaml = `${prefix}${parts.slice(0, lead).join("/")}/change.yaml`;
|
|
1143
|
+
return inChange[0] === ".openspec.yaml" && newChange(changeYaml);
|
|
1144
|
+
}
|
|
1145
|
+
function withoutSection(text2) {
|
|
1146
|
+
const s = findSection(text2);
|
|
1147
|
+
if (!s)
|
|
1148
|
+
return [];
|
|
1149
|
+
const lines = text2.split("\n");
|
|
1150
|
+
const before = lines.slice(0, s.start).join("\n");
|
|
1151
|
+
const after = lines.slice(s.end + 1).join("\n");
|
|
1152
|
+
const out = /* @__PURE__ */ new Set();
|
|
1153
|
+
if (after === "" || after === "\n") {
|
|
1154
|
+
for (const b of [before, `${before}
|
|
1155
|
+
`, before.replace(/\n$/, ""), before.replace(/\n\n$/, "")])
|
|
1156
|
+
out.add(b);
|
|
1157
|
+
if (before === "")
|
|
1158
|
+
out.add("");
|
|
1159
|
+
} else
|
|
1160
|
+
out.add([...lines.slice(0, s.start), ...lines.slice(s.end + 1)].join("\n"));
|
|
1161
|
+
return [...out];
|
|
1162
|
+
}
|
|
1163
|
+
async function unadopt(path) {
|
|
1164
|
+
const root = resolve4(path);
|
|
1165
|
+
const a = join7(root, ATTICUS_DIR);
|
|
1166
|
+
if (!await isDir(a))
|
|
1167
|
+
throw new AdoptError("not-adopted", `${path} isn't adopted: there is no ${ATTICUS_DIR}/ folder.`);
|
|
1168
|
+
const layout = await readLayout(join7(a, "org-pack"));
|
|
1169
|
+
let snap;
|
|
1170
|
+
try {
|
|
1171
|
+
snap = JSON.parse(await readFile3(join7(root, SNAPSHOT_FILE), "utf8"));
|
|
1172
|
+
} catch {
|
|
1173
|
+
snap = void 0;
|
|
1174
|
+
}
|
|
1175
|
+
const res = {
|
|
1176
|
+
removed: [],
|
|
1177
|
+
restored: [],
|
|
1178
|
+
keptNew: [],
|
|
1179
|
+
changed: [],
|
|
1180
|
+
missing: [],
|
|
1181
|
+
hadSnapshot: !!snap
|
|
1182
|
+
};
|
|
1183
|
+
const before = snap?.files ?? {};
|
|
1184
|
+
const existed = (p) => p in before;
|
|
1185
|
+
await rm(a, { recursive: true, force: true });
|
|
1186
|
+
res.removed.push(`${ATTICUS_DIR}/`);
|
|
1187
|
+
for (const f of ROOT_STATE) {
|
|
1188
|
+
const had = snap ? Object.keys(before).some((p) => p === f || p.startsWith(`${f}/`)) || snap.dirs.includes(f) : true;
|
|
1189
|
+
if (!had && await isDir(join7(root, f))) {
|
|
1190
|
+
await rm(join7(root, f), { recursive: true, force: true });
|
|
1191
|
+
res.removed.push(`${f}/`);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
const openspecs = [
|
|
1195
|
+
...layout.openspec.startsWith(`${ATTICUS_DIR}/`) ? [] : [layout.openspec],
|
|
1196
|
+
...snap && layout.openspec !== "openspec" && !snap.dirs.includes("openspec") ? ["openspec"] : []
|
|
1197
|
+
];
|
|
1198
|
+
const strayCatalog = !!snap && layout.catalog !== "catalog" && !snap.dirs.includes("catalog") && await isDir(join7(root, "catalog"));
|
|
1199
|
+
if (strayCatalog) {
|
|
1200
|
+
await rm(join7(root, "catalog"), { recursive: true, force: true });
|
|
1201
|
+
res.removed.push("catalog/");
|
|
1202
|
+
}
|
|
1203
|
+
const files = await listFiles(root);
|
|
1204
|
+
const present = new Set(files);
|
|
1205
|
+
const newChange = (changeYaml) => present.has(changeYaml) && !existed(changeYaml);
|
|
1206
|
+
for (const f of files) {
|
|
1207
|
+
const full = join7(root, f);
|
|
1208
|
+
if (existed(f)) {
|
|
1209
|
+
const now = sha256(await readFile3(full));
|
|
1210
|
+
if (now === before[f])
|
|
1211
|
+
continue;
|
|
1212
|
+
const text2 = await readText(full) ?? "";
|
|
1213
|
+
const back = withoutSection(text2).find((t) => sha256(t) === before[f]);
|
|
1214
|
+
if (back !== void 0) {
|
|
1215
|
+
await writeFile4(full, back, "utf8");
|
|
1216
|
+
res.restored.push(f);
|
|
1217
|
+
} else
|
|
1218
|
+
res.changed.push(f);
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
let atticus = openspecs.some((o) => changeOwned(f, o, !!snap, newChange));
|
|
1222
|
+
if (!atticus) {
|
|
1223
|
+
const text2 = await readText(full);
|
|
1224
|
+
if (text2 !== void 0 && parseHeader(text2))
|
|
1225
|
+
atticus = true;
|
|
1226
|
+
else if (text2 !== void 0 && findSection(text2)) {
|
|
1227
|
+
const back = withoutSection(text2)[0];
|
|
1228
|
+
if (back === void 0 || back.trim() === "")
|
|
1229
|
+
atticus = true;
|
|
1230
|
+
else {
|
|
1231
|
+
await writeFile4(full, back, "utf8");
|
|
1232
|
+
res.restored.push(f);
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
if (atticus) {
|
|
1238
|
+
await rm(full, { force: true });
|
|
1239
|
+
res.removed.push(f);
|
|
1240
|
+
} else if (snap)
|
|
1241
|
+
res.keptNew.push(f);
|
|
1242
|
+
}
|
|
1243
|
+
if (snap) {
|
|
1244
|
+
for (const f of Object.keys(before))
|
|
1245
|
+
if (!await exists(join7(root, f)))
|
|
1246
|
+
res.missing.push(f);
|
|
1247
|
+
}
|
|
1248
|
+
if (snap) {
|
|
1249
|
+
const had = new Set(snap.dirs);
|
|
1250
|
+
for (const d of (await listDirs(root)).reverse()) {
|
|
1251
|
+
if (had.has(d))
|
|
1252
|
+
continue;
|
|
1253
|
+
try {
|
|
1254
|
+
await rmdir(join7(root, d));
|
|
1255
|
+
} catch {
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
res.removed.sort();
|
|
1260
|
+
return res;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// packages/adopt/dist/absorb/types.js
|
|
1264
|
+
var NOT_TRANSLATABLE = /* @__PURE__ */ new Set(["doc", "code"]);
|
|
1265
|
+
|
|
1266
|
+
// packages/adopt/dist/absorb/critic.js
|
|
1267
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
1268
|
+
var known = new Set(BUILT_IN_CHECKS.map((c) => c.id));
|
|
1269
|
+
var ALL_MAPPINGS = [
|
|
1270
|
+
{
|
|
1271
|
+
re: /pattern code.*registry|registry.*pattern code|pattern.*in the (pattern )?registry/i,
|
|
1272
|
+
check: "pattern-code-in-registry",
|
|
1273
|
+
lens: "architecture",
|
|
1274
|
+
artifacts: ["interfaces"]
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
re: /approved (integration )?pattern/i,
|
|
1278
|
+
check: "interface-pattern-approved",
|
|
1279
|
+
lens: "architecture",
|
|
1280
|
+
artifacts: ["interfaces"]
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
re: /(names?|has|uses) an? (integration )?pattern/i,
|
|
1284
|
+
check: "interface-has-pattern",
|
|
1285
|
+
lens: "architecture",
|
|
1286
|
+
artifacts: ["interfaces"]
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
re: /both ends|(caller|callee)s?.*(catalog|known)|ends? of (every|each) interface/i,
|
|
1290
|
+
check: "interface-ends-in-catalog",
|
|
1291
|
+
lens: "architecture",
|
|
1292
|
+
artifacts: ["interfaces"]
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
re: /vendor.*operation|operation.*vendor/i,
|
|
1296
|
+
check: "vendor-operation-known",
|
|
1297
|
+
lens: "architecture",
|
|
1298
|
+
artifacts: ["interfaces"]
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
re: /timeouts?|retr(y|ies)|circuit.?breaker/i,
|
|
1302
|
+
check: "settings-present",
|
|
1303
|
+
lens: "architecture",
|
|
1304
|
+
artifacts: ["interfaces", "design"],
|
|
1305
|
+
params: (t) => ({
|
|
1306
|
+
settingKinds: [
|
|
1307
|
+
.../timeout/i.test(t) ? ["timeout"] : [],
|
|
1308
|
+
.../retr(y|ies)/i.test(t) ? ["retry"] : [],
|
|
1309
|
+
.../circuit.?breaker/i.test(t) ? ["circuit-breaker"] : []
|
|
1310
|
+
]
|
|
1311
|
+
})
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
re: /alerts?|dashboards?|observability/i,
|
|
1315
|
+
check: "observability-present",
|
|
1316
|
+
lens: "release",
|
|
1317
|
+
artifacts: ["design"],
|
|
1318
|
+
params: (t) => ({
|
|
1319
|
+
assetKinds: [
|
|
1320
|
+
.../alert/i.test(t) ? ["alert"] : [],
|
|
1321
|
+
.../dashboard/i.test(t) ? ["dashboard"] : []
|
|
1322
|
+
].concat(/alert|dashboard/i.test(t) ? [] : ["alert"])
|
|
1323
|
+
})
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
re: /acceptance criteria/i,
|
|
1327
|
+
check: "story-has-acceptance-criteria",
|
|
1328
|
+
lens: "story",
|
|
1329
|
+
artifacts: ["stories"]
|
|
1330
|
+
},
|
|
1331
|
+
{
|
|
1332
|
+
re: /requirement.*scenario|scenario.*requirement/i,
|
|
1333
|
+
check: "spec-scenarios-present",
|
|
1334
|
+
lens: "story",
|
|
1335
|
+
artifacts: ["specs"]
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
re: /(story|stories).*(covered by|has|have).*task|task.*(every|each) story/i,
|
|
1339
|
+
check: "tasks-cover-stories",
|
|
1340
|
+
lens: "delivery",
|
|
1341
|
+
artifacts: ["tasks"]
|
|
1342
|
+
},
|
|
1343
|
+
{
|
|
1344
|
+
re: /mappings?/i,
|
|
1345
|
+
check: "mappings-cover-interfaces",
|
|
1346
|
+
lens: "architecture",
|
|
1347
|
+
artifacts: ["mappings"]
|
|
1348
|
+
},
|
|
1349
|
+
{
|
|
1350
|
+
re: /how long.*(kept|retained)|retention/i,
|
|
1351
|
+
check: "data-retention-stated",
|
|
1352
|
+
lens: "architecture",
|
|
1353
|
+
artifacts: ["data"]
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
re: /personal data|classif/i,
|
|
1357
|
+
check: "data-columns-classified",
|
|
1358
|
+
lens: "architecture",
|
|
1359
|
+
artifacts: ["data"]
|
|
1360
|
+
},
|
|
1361
|
+
{
|
|
1362
|
+
re: /migration/i,
|
|
1363
|
+
check: "data-migration-named",
|
|
1364
|
+
lens: "architecture",
|
|
1365
|
+
artifacts: ["data"]
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
re: /data (design|doc)|data\.md|touches a data store/i,
|
|
1369
|
+
check: "data-doc-present",
|
|
1370
|
+
lens: "architecture",
|
|
1371
|
+
artifacts: ["data"]
|
|
1372
|
+
},
|
|
1373
|
+
{
|
|
1374
|
+
re: /service.?desk|change request|snow/i,
|
|
1375
|
+
check: "snow-drafts-present",
|
|
1376
|
+
lens: "delivery",
|
|
1377
|
+
artifacts: ["snow"]
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
re: /instructions? (aimed at|for|to) an? (assistant|ai|agent|model)|prompt injection/i,
|
|
1381
|
+
check: "no-embedded-instructions",
|
|
1382
|
+
lens: "architecture",
|
|
1383
|
+
artifacts: ["brief", "proposal", "design", "stories"]
|
|
1384
|
+
}
|
|
1385
|
+
];
|
|
1386
|
+
var MAPPINGS = ALL_MAPPINGS.filter((m) => known.has(m.check));
|
|
1387
|
+
function lensOf(t) {
|
|
1388
|
+
if (/\b(tests?|coverage|lint|dependency|library|libraries|code)\b/i.test(t))
|
|
1389
|
+
return "code-and-test";
|
|
1390
|
+
if (/\b(story|stories|sprint|acceptance|persona|user journey)\b/i.test(t))
|
|
1391
|
+
return "story";
|
|
1392
|
+
if (/\b(screen|ux|accessib\w*|wcag|usability|copy)\b/i.test(t))
|
|
1393
|
+
return "ux";
|
|
1394
|
+
if (/\b(roll(ed|s|ing)? ?back|release|deploy\w*|go-?live|runbook|on-?call)\b/i.test(t))
|
|
1395
|
+
return "release";
|
|
1396
|
+
if (/\b(business|value|kpi|cost|benefit|customer)\b/i.test(t))
|
|
1397
|
+
return "business";
|
|
1398
|
+
if (/\b(estimate|plan|milestone|owner|team)\b/i.test(t))
|
|
1399
|
+
return "delivery";
|
|
1400
|
+
return "architecture";
|
|
1401
|
+
}
|
|
1402
|
+
var ARTIFACTS_BY_LENS = {
|
|
1403
|
+
business: ["brief", "proposal"],
|
|
1404
|
+
architecture: ["design", "interfaces"],
|
|
1405
|
+
story: ["stories"],
|
|
1406
|
+
ux: ["ux"],
|
|
1407
|
+
delivery: ["tasks"],
|
|
1408
|
+
"code-and-test": ["code", "tests"],
|
|
1409
|
+
release: ["design", "release"]
|
|
1410
|
+
};
|
|
1411
|
+
function severityOf(item, deterministic) {
|
|
1412
|
+
const s = item.severity?.toLowerCase();
|
|
1413
|
+
if (s) {
|
|
1414
|
+
if (/block|critical|high|must|error/.test(s))
|
|
1415
|
+
return "blocking";
|
|
1416
|
+
if (/medium|warn|major|risk/.test(s))
|
|
1417
|
+
return "risk";
|
|
1418
|
+
if (/low|minor|info|idea|nice/.test(s))
|
|
1419
|
+
return "idea";
|
|
1420
|
+
}
|
|
1421
|
+
if (/\b(never|must|no (credentials|secrets|tokens))\b/i.test(item.text))
|
|
1422
|
+
return "blocking";
|
|
1423
|
+
return deterministic ? "blocking" : "risk";
|
|
1424
|
+
}
|
|
1425
|
+
function needsOf(t) {
|
|
1426
|
+
if (/timeouts?|retr(y|ies)/i.test(t))
|
|
1427
|
+
return [
|
|
1428
|
+
{
|
|
1429
|
+
kind: "Setting",
|
|
1430
|
+
scope: "touched-components",
|
|
1431
|
+
question: "What timeouts and retries does ${record} use?"
|
|
1432
|
+
}
|
|
1433
|
+
];
|
|
1434
|
+
if (/alerts?|dashboards?/i.test(t))
|
|
1435
|
+
return [
|
|
1436
|
+
{
|
|
1437
|
+
kind: "ObservabilityAsset",
|
|
1438
|
+
scope: "touched-components",
|
|
1439
|
+
question: "Which alerts and dashboards cover ${record}?"
|
|
1440
|
+
}
|
|
1441
|
+
];
|
|
1442
|
+
if (/retention|how long/i.test(t))
|
|
1443
|
+
return [
|
|
1444
|
+
{
|
|
1445
|
+
kind: "Table",
|
|
1446
|
+
field: "retention",
|
|
1447
|
+
scope: "touched-data-stores",
|
|
1448
|
+
question: "How long are the rows of ${record} kept?"
|
|
1449
|
+
}
|
|
1450
|
+
];
|
|
1451
|
+
return [];
|
|
1452
|
+
}
|
|
1453
|
+
var clean = (t) => t.replace(/^\s*(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?/, "").replace(/\s+/g, " ").trim();
|
|
1454
|
+
function translateCriticItem(item, o) {
|
|
1455
|
+
const text2 = clean(item.text);
|
|
1456
|
+
const map = MAPPINGS.find((m) => m.re.test(text2));
|
|
1457
|
+
const deterministic = !!map;
|
|
1458
|
+
const severity = o.layer === "constitution" && !item.severity ? "blocking" : severityOf(item, deterministic);
|
|
1459
|
+
const lens = map?.lens ?? lensOf(`${item.section ?? ""} ${text2}`);
|
|
1460
|
+
const base = slug(`${o.prefix}-${item.sourceId ?? text2.split(/\s+/).slice(0, 7).join(" ")}`).slice(0, 90);
|
|
1461
|
+
let id = base;
|
|
1462
|
+
for (let n = 2; o.usedIds.has(id); n++)
|
|
1463
|
+
id = `${base}-${n}`;
|
|
1464
|
+
o.usedIds.add(id);
|
|
1465
|
+
const title = text2.length > 120 ? `${text2.slice(0, 117)}...` : text2;
|
|
1466
|
+
const rule = {
|
|
1467
|
+
id,
|
|
1468
|
+
title,
|
|
1469
|
+
...text2.length > 120 ? { description: text2 } : {},
|
|
1470
|
+
layer: o.layer,
|
|
1471
|
+
lens,
|
|
1472
|
+
appliesTo: { artifacts: map?.artifacts ?? ARTIFACTS_BY_LENS[lens] },
|
|
1473
|
+
severity,
|
|
1474
|
+
check: map ? { type: "deterministic", check: map.check, params: map.params?.(text2) ?? {} } : {
|
|
1475
|
+
type: "ai",
|
|
1476
|
+
prompt: `Check that this holds for the change: ${text2} Quote the artifact line that shows it; when nothing shows it, report a finding with the fix.`
|
|
1477
|
+
},
|
|
1478
|
+
waivable: o.layer !== "constitution" && severity === "blocking",
|
|
1479
|
+
source: `${o.source}:${item.line}`,
|
|
1480
|
+
status: "draft",
|
|
1481
|
+
needs: needsOf(text2)
|
|
1482
|
+
};
|
|
1483
|
+
const r = ChecklistRule.safeParse(rule);
|
|
1484
|
+
return {
|
|
1485
|
+
rule,
|
|
1486
|
+
deterministic,
|
|
1487
|
+
confidence: map ? "high" : item.severity ? "medium" : "low",
|
|
1488
|
+
problems: r.success ? [] : r.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
function markdownItems(text2) {
|
|
1492
|
+
const out = [];
|
|
1493
|
+
let section;
|
|
1494
|
+
text2.split("\n").forEach((l, i) => {
|
|
1495
|
+
const h = /^#{1,6}\s+(.*)$/.exec(l);
|
|
1496
|
+
if (h) {
|
|
1497
|
+
section = h[1].trim();
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
if (/^\s{0,3}(?:[-*+]|\d+[.)])\s+\S/.test(l) && clean(l).length >= 12)
|
|
1501
|
+
out.push({ text: l, line: i + 1, ...section ? { section } : {} });
|
|
1502
|
+
});
|
|
1503
|
+
return out;
|
|
1504
|
+
}
|
|
1505
|
+
function yamlItems(text2) {
|
|
1506
|
+
let data;
|
|
1507
|
+
try {
|
|
1508
|
+
data = (0, import_yaml5.parse)(text2);
|
|
1509
|
+
} catch {
|
|
1510
|
+
return void 0;
|
|
1511
|
+
}
|
|
1512
|
+
const obj = data && typeof data === "object" ? data : {};
|
|
1513
|
+
const list = ["items", "rules", "checks", "checklist"].map((k) => obj[k]).find(Array.isArray);
|
|
1514
|
+
if (!list?.length)
|
|
1515
|
+
return void 0;
|
|
1516
|
+
const lines = text2.split("\n");
|
|
1517
|
+
const out = [];
|
|
1518
|
+
for (const raw of list) {
|
|
1519
|
+
const e = typeof raw === "string" ? { text: raw } : raw ?? {};
|
|
1520
|
+
const t = [e.text, e.title, e.rule, e.check, e.description].find((v) => typeof v === "string" && v.trim());
|
|
1521
|
+
if (!t)
|
|
1522
|
+
continue;
|
|
1523
|
+
const at = lines.findIndex((l) => l.includes(t.slice(0, 40)));
|
|
1524
|
+
out.push({
|
|
1525
|
+
text: t,
|
|
1526
|
+
line: at >= 0 ? at + 1 : 1,
|
|
1527
|
+
...typeof e.id === "string" ? { sourceId: e.id } : {},
|
|
1528
|
+
...typeof e.severity === "string" ? { severity: e.severity } : {}
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
return out.length ? out : void 0;
|
|
1532
|
+
}
|
|
1533
|
+
function packFor(name, description, rules) {
|
|
1534
|
+
const pack = { schemaVersion: 1, name, version: "0.1.0", description, rules };
|
|
1535
|
+
const r = ChecklistPack.safeParse(pack);
|
|
1536
|
+
return {
|
|
1537
|
+
content: `# Drafted by atticus absorb. Every rule is a draft: review, then set status: active.
|
|
1538
|
+
${(0, import_yaml5.stringify)(pack, { lineWidth: 0 })}`,
|
|
1539
|
+
problems: r.success ? [] : r.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// packages/adopt/dist/absorb/speckit.js
|
|
1544
|
+
function parseConstitution(text2) {
|
|
1545
|
+
const lines = text2.split("\n");
|
|
1546
|
+
const out = {
|
|
1547
|
+
principles: [],
|
|
1548
|
+
sections: [],
|
|
1549
|
+
generatedByAtticus: /Generated from the Atticus constitution/.test(text2)
|
|
1550
|
+
};
|
|
1551
|
+
let h2;
|
|
1552
|
+
let current;
|
|
1553
|
+
const flush = () => {
|
|
1554
|
+
if (!current)
|
|
1555
|
+
return;
|
|
1556
|
+
const body = current.body.join("\n").trim();
|
|
1557
|
+
if (current.principle)
|
|
1558
|
+
out.principles.push({
|
|
1559
|
+
title: current.title.replace(/^[IVXLCDM]+\.\s*/, "").replace(/\s*\(NON-NEGOTIABLE\)\s*$/i, ""),
|
|
1560
|
+
body,
|
|
1561
|
+
line: current.line,
|
|
1562
|
+
nonNegotiable: /NON-NEGOTIABLE/i.test(current.title)
|
|
1563
|
+
});
|
|
1564
|
+
else if (body)
|
|
1565
|
+
out.sections.push({ title: current.title, body, line: current.line });
|
|
1566
|
+
current = void 0;
|
|
1567
|
+
};
|
|
1568
|
+
lines.forEach((l, i) => {
|
|
1569
|
+
const m2 = /^##\s+(.+)$/.exec(l);
|
|
1570
|
+
const m3 = /^###\s+(.+)$/.exec(l);
|
|
1571
|
+
if (m2) {
|
|
1572
|
+
flush();
|
|
1573
|
+
h2 = m2[1].trim();
|
|
1574
|
+
if (!/principles/i.test(h2))
|
|
1575
|
+
current = { title: h2, body: [], line: i + 1, principle: false };
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
if (m3 && h2 && /principles/i.test(h2)) {
|
|
1579
|
+
flush();
|
|
1580
|
+
current = { title: m3[1].trim(), body: [], line: i + 1, principle: true };
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (/^\*\*Version\*\*/.test(l))
|
|
1584
|
+
return;
|
|
1585
|
+
current?.body.push(l);
|
|
1586
|
+
});
|
|
1587
|
+
flush();
|
|
1588
|
+
return out;
|
|
1589
|
+
}
|
|
1590
|
+
function changeTemplateFrom(id, name, text2) {
|
|
1591
|
+
const body = [];
|
|
1592
|
+
for (const raw of text2.split("\n")) {
|
|
1593
|
+
const l = raw.replace(/\[FEATURE NAME\]|\[FEATURE\]/g, "{{title}}").replace(/\[DATE\]/g, "{{date}}").replace(/\[###-feature-name\]/g, "{{id}}");
|
|
1594
|
+
if (/^#\s/.test(l))
|
|
1595
|
+
body.push("# {{title}}", "");
|
|
1596
|
+
else if (/^#{2,6}\s/.test(l))
|
|
1597
|
+
body.push(l.replace(/\s*\*\([^)]*\)\*\s*$/, ""), "");
|
|
1598
|
+
else if (l.trim() && !/^\*\*(Feature Branch|Created|Status)\*\*/.test(l))
|
|
1599
|
+
body.push(`<!-- ${l.trim().replace(/-->/g, "")} -->`, "");
|
|
1600
|
+
}
|
|
1601
|
+
const skeleton = body.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
1602
|
+
return `---
|
|
1603
|
+
id: ${id}
|
|
1604
|
+
name: ${name}
|
|
1605
|
+
changeType: feature
|
|
1606
|
+
description: Drafted by atticus absorb from a Spec Kit spec template.
|
|
1607
|
+
---
|
|
1608
|
+
${skeleton}
|
|
1609
|
+
`;
|
|
1610
|
+
}
|
|
1611
|
+
function openSpecFrom(feature, text2) {
|
|
1612
|
+
const title = /^#\s+Feature Specification:\s*(.+)$/m.exec(text2)?.[1]?.trim() ?? feature;
|
|
1613
|
+
const reqs = [...text2.matchAll(/^\s*-\s+\*\*(FR-\d+)\*\*:\s*(.+)$/gm)].map((m) => ({
|
|
1614
|
+
id: m[1],
|
|
1615
|
+
text: m[2].trim().replace(/\bMUST\b/, "SHALL")
|
|
1616
|
+
}));
|
|
1617
|
+
const scenarios = [
|
|
1618
|
+
...text2.matchAll(/\*\*Given\*\*\s*(.+?),\s*\*\*When\*\*\s*(.+?),\s*\*\*Then\*\*\s*(.+)$/gm)
|
|
1619
|
+
].map((m, i) => ({ n: i + 1, given: m[1], when: m[2], then: m[3].trim() }));
|
|
1620
|
+
const out = [
|
|
1621
|
+
`# ${feature} Specification`,
|
|
1622
|
+
"",
|
|
1623
|
+
"## Purpose",
|
|
1624
|
+
"",
|
|
1625
|
+
title,
|
|
1626
|
+
"",
|
|
1627
|
+
"## Requirements",
|
|
1628
|
+
""
|
|
1629
|
+
];
|
|
1630
|
+
reqs.forEach((r, i) => {
|
|
1631
|
+
out.push(`### Requirement: ${r.id}`, "", r.text, "");
|
|
1632
|
+
const mine = i === 0 ? scenarios : [];
|
|
1633
|
+
for (const s of mine)
|
|
1634
|
+
out.push(`#### Scenario: Acceptance scenario ${s.n}`, "", `- **GIVEN** ${s.given}`, `- **WHEN** ${s.when}`, `- **THEN** ${s.then}`, "");
|
|
1635
|
+
if (!mine.length)
|
|
1636
|
+
out.push(`#### Scenario: ${r.id} holds`, "", `- **WHEN** the feature is used`, `- **THEN** ${r.text.replace(/^System SHALL\s*/i, "the system does this: ")}`, "");
|
|
1637
|
+
});
|
|
1638
|
+
return { spec: `${out.join("\n").trimEnd()}
|
|
1639
|
+
`, requirements: reqs.length };
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// packages/adopt/dist/absorb/translate.js
|
|
1643
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
1644
|
+
function meta(text2) {
|
|
1645
|
+
const fm = frontMatter(text2);
|
|
1646
|
+
let data = {};
|
|
1647
|
+
if (fm.data !== void 0)
|
|
1648
|
+
try {
|
|
1649
|
+
const v = (0, import_yaml6.parse)(fm.data);
|
|
1650
|
+
if (v && typeof v === "object")
|
|
1651
|
+
data = v;
|
|
1652
|
+
} catch {
|
|
1653
|
+
}
|
|
1654
|
+
return { data, body: fm.body };
|
|
1655
|
+
}
|
|
1656
|
+
var str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
1657
|
+
function firstLine(body) {
|
|
1658
|
+
for (const l of body.split("\n")) {
|
|
1659
|
+
const t = l.replace(/^#+\s*/, "").replace(/^\d+[.)]\s*/, "").trim();
|
|
1660
|
+
if (t)
|
|
1661
|
+
return t.length > 160 ? `${t.slice(0, 157)}...` : t;
|
|
1662
|
+
}
|
|
1663
|
+
return void 0;
|
|
1664
|
+
}
|
|
1665
|
+
var normaliseArgs = (body) => body.replace(/\$\{input:[^}]+\}/g, "$ARGUMENTS").replace(/\{\{ARGS\}\}/g, "$ARGUMENTS");
|
|
1666
|
+
function bodyKey(text2) {
|
|
1667
|
+
return normaliseArgs(meta(text2).body).replace(/\$ARGUMENTS/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1668
|
+
}
|
|
1669
|
+
function promptToCommand(file, text2, prefix) {
|
|
1670
|
+
const { data, body } = meta(text2);
|
|
1671
|
+
const base = file.split("/").pop().replace(/\.prompt\.md$|\.md$/i, "");
|
|
1672
|
+
const id = slug(`${prefix}-${base}`);
|
|
1673
|
+
const heading = firstLine(body);
|
|
1674
|
+
const description = str(data.description) ?? heading?.replace(/\s*prompt$/i, "") ?? `Prompt ${base} from ${prefix}.`;
|
|
1675
|
+
const hint = str(data["argument-hint"]) ?? str(data.argumentHint);
|
|
1676
|
+
const fm = { id, description };
|
|
1677
|
+
if (hint)
|
|
1678
|
+
fm.argumentHint = hint;
|
|
1679
|
+
return {
|
|
1680
|
+
id,
|
|
1681
|
+
title: description,
|
|
1682
|
+
path: `org-pack/process/commands/${id}.md`,
|
|
1683
|
+
content: `---
|
|
1684
|
+
${(0, import_yaml6.stringify)(fm, { lineWidth: 0 }).trimEnd()}
|
|
1685
|
+
---
|
|
1686
|
+
|
|
1687
|
+
${normaliseArgs(body).trim()}
|
|
1688
|
+
`,
|
|
1689
|
+
confidence: str(data.description) ? "high" : "medium"
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function toSkill(file, text2, o) {
|
|
1693
|
+
const { data, body } = meta(text2);
|
|
1694
|
+
const base = file.split("/").filter((s) => s !== "SKILL.md").pop().replace(/\.(agent|chatmode|instructions)\.md$|\.md$/i, "");
|
|
1695
|
+
const id = slug(`${o.prefix}-${o.kind === "instructions" ? `${base}-conventions` : base}`);
|
|
1696
|
+
const description = str(data.description) ?? (o.kind === "instructions" ? `Conventions ${o.prefix} gives its assistants (${file}).` : firstLine(body) ?? `Skill ${base} from ${o.prefix}.`);
|
|
1697
|
+
const name = str(data.name) ?? base.replace(/[-_.]+/g, " ");
|
|
1698
|
+
const m = {
|
|
1699
|
+
id,
|
|
1700
|
+
name,
|
|
1701
|
+
description,
|
|
1702
|
+
owner: o.owner,
|
|
1703
|
+
source: o.source,
|
|
1704
|
+
status: "draft"
|
|
1705
|
+
};
|
|
1706
|
+
const r = SkillMeta.safeParse(m);
|
|
1707
|
+
return {
|
|
1708
|
+
id,
|
|
1709
|
+
title: description,
|
|
1710
|
+
path: `org-pack/process/skills/${id}/SKILL.md`,
|
|
1711
|
+
content: `---
|
|
1712
|
+
${(0, import_yaml6.stringify)(m, { lineWidth: 0 }).trimEnd()}
|
|
1713
|
+
---
|
|
1714
|
+
|
|
1715
|
+
${body.trim()}
|
|
1716
|
+
`,
|
|
1717
|
+
confidence: str(data.description) ? "high" : "medium",
|
|
1718
|
+
problems: r.success ? [] : r.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function schemaKind(file, text2) {
|
|
1722
|
+
if (!/\.(ya?ml|json)$/i.test(file))
|
|
1723
|
+
return void 0;
|
|
1724
|
+
let data;
|
|
1725
|
+
try {
|
|
1726
|
+
data = /\.json$/i.test(file) ? JSON.parse(text2) : (0, import_yaml6.parse)(text2);
|
|
1727
|
+
} catch {
|
|
1728
|
+
return void 0;
|
|
1729
|
+
}
|
|
1730
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
1731
|
+
return void 0;
|
|
1732
|
+
const d = data;
|
|
1733
|
+
if (typeof d.openapi === "string" || typeof d.swagger === "string")
|
|
1734
|
+
return "openapi";
|
|
1735
|
+
if (typeof d.asyncapi === "string")
|
|
1736
|
+
return "asyncapi";
|
|
1737
|
+
if (typeof d.$schema === "string" || /\.schema\.json$/i.test(file))
|
|
1738
|
+
return "json-schema";
|
|
1739
|
+
return void 0;
|
|
1740
|
+
}
|
|
1741
|
+
function schemaReference(file, text2, kind, prefix) {
|
|
1742
|
+
const data = /\.json$/i.test(file) ? JSON.parse(text2) : (0, import_yaml6.parse)(text2);
|
|
1743
|
+
const info = data.info ?? {};
|
|
1744
|
+
const title = str(info.title) ?? str(data.title) ?? file;
|
|
1745
|
+
const version = str(info.version) ?? str(data.version);
|
|
1746
|
+
const operations = [];
|
|
1747
|
+
const paths = data.paths ?? data.channels ?? {};
|
|
1748
|
+
for (const [p, ops] of Object.entries(paths))
|
|
1749
|
+
for (const m of Object.keys(ops ?? {}))
|
|
1750
|
+
if (/^(get|put|post|delete|patch|head|options|publish|subscribe|send|receive)$/i.test(m))
|
|
1751
|
+
operations.push(`${m.toUpperCase()} ${p}`);
|
|
1752
|
+
const fields = Object.keys(data.properties ?? {});
|
|
1753
|
+
const id = slug(`${prefix}-${file.replace(/\.(ya?ml|json)$/i, "")}`);
|
|
1754
|
+
const ref = {
|
|
1755
|
+
kind,
|
|
1756
|
+
title,
|
|
1757
|
+
...version ? { version } : {},
|
|
1758
|
+
source: `${prefix}:${file}`,
|
|
1759
|
+
...operations.length ? { operations } : {},
|
|
1760
|
+
...fields.length ? { fields } : {},
|
|
1761
|
+
use: kind === "json-schema" ? "Referenced from interfaces and mappings; intake reads it when copied into inputs/." : "A partner or team API: intake reads it into planned or vendor endpoints when copied into inputs/specs/."
|
|
1762
|
+
};
|
|
1763
|
+
return {
|
|
1764
|
+
id,
|
|
1765
|
+
title,
|
|
1766
|
+
path: `references/schemas/${id}.yaml`,
|
|
1767
|
+
content: (0, import_yaml6.stringify)(ref, { lineWidth: 0 }),
|
|
1768
|
+
confidence: "high"
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
function reference(kind, file, prefix, note) {
|
|
1772
|
+
const id = slug(`${prefix}-${file.replace(/\.[a-z]+$/i, "")}`);
|
|
1773
|
+
return {
|
|
1774
|
+
id,
|
|
1775
|
+
title: file,
|
|
1776
|
+
path: `references/${kind}/${id}.yaml`,
|
|
1777
|
+
content: (0, import_yaml6.stringify)({ kind, source: `${prefix}:${file}`, note }, { lineWidth: 0 }),
|
|
1778
|
+
confidence: "high"
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
var VERBS = /@(get|put|post|patch|delete|head)\b/;
|
|
1782
|
+
var ROUTE = /@route\(\s*"([^"]*)"\s*\)/;
|
|
1783
|
+
function typespecOutline(text2) {
|
|
1784
|
+
const lines = text2.replace(/\/\/.*$/gm, "").split("\n");
|
|
1785
|
+
const out = { operations: [], models: [] };
|
|
1786
|
+
let pendingRoute;
|
|
1787
|
+
let prefix;
|
|
1788
|
+
let depth = 0;
|
|
1789
|
+
let interfaceDepth;
|
|
1790
|
+
for (const raw of lines) {
|
|
1791
|
+
const l = raw.trim();
|
|
1792
|
+
const ns = /^namespace\s+([\w.]+)/.exec(l);
|
|
1793
|
+
if (ns && !out.namespace)
|
|
1794
|
+
out.namespace = ns[1];
|
|
1795
|
+
const route = ROUTE.exec(l)?.[1];
|
|
1796
|
+
const model = /\bmodel\s+(\w+)/.exec(l);
|
|
1797
|
+
if (model)
|
|
1798
|
+
out.models.push(model[1]);
|
|
1799
|
+
const iface = /\binterface\s+\w+/.test(l);
|
|
1800
|
+
if (iface) {
|
|
1801
|
+
prefix = pendingRoute ?? route ?? "";
|
|
1802
|
+
interfaceDepth = depth;
|
|
1803
|
+
pendingRoute = void 0;
|
|
1804
|
+
}
|
|
1805
|
+
const op = /^((?:@\w+(?:\([^)]*\))?\s+)*)(?:op\s+)?(\w+)\s*\((.*)$/.exec(l);
|
|
1806
|
+
const inInterface = interfaceDepth !== void 0 && depth === interfaceDepth + 1;
|
|
1807
|
+
if (op && !model && !iface && (inInterface || /^(?:@\w+(?:\([^)]*\))?\s+)*op\s/.test(l))) {
|
|
1808
|
+
const decorators = op[1] ?? "";
|
|
1809
|
+
const own = ROUTE.exec(decorators)?.[1] ?? (inInterface ? void 0 : pendingRoute);
|
|
1810
|
+
const verb = VERBS.exec(decorators)?.[1] ?? (/@body\b/.test(op[3] ?? "") ? "post" : "get");
|
|
1811
|
+
const params = [...(op[3] ?? "").matchAll(/@path\s+(\w+)/g)].map((m) => `/{${m[1]}}`);
|
|
1812
|
+
const path = `${inInterface ? prefix ?? "" : ""}${own ?? ""}${params.join("")}` || "/";
|
|
1813
|
+
out.operations.push(`${verb.toUpperCase()} ${path.replace(/\/{2,}/g, "/")}`);
|
|
1814
|
+
pendingRoute = void 0;
|
|
1815
|
+
} else if (route && !iface)
|
|
1816
|
+
pendingRoute = route;
|
|
1817
|
+
for (const c of l) {
|
|
1818
|
+
if (c === "{")
|
|
1819
|
+
depth++;
|
|
1820
|
+
else if (c === "}") {
|
|
1821
|
+
depth--;
|
|
1822
|
+
if (interfaceDepth !== void 0 && depth === interfaceDepth) {
|
|
1823
|
+
interfaceDepth = void 0;
|
|
1824
|
+
prefix = void 0;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
out.operations.sort();
|
|
1830
|
+
return out;
|
|
1831
|
+
}
|
|
1832
|
+
function typespecReference(file, text2, prefix, compiled) {
|
|
1833
|
+
const o = typespecOutline(text2);
|
|
1834
|
+
const id = slug(`${prefix}-${file.replace(/\.tsp$/i, "")}`);
|
|
1835
|
+
const note = compiled.length ? `TypeSpec source. Intake reads its compiled OpenAPI, absorbed as a schema: ${compiled.join(", ")}.` : "TypeSpec source. Intake reads OpenAPI, not TypeSpec: run tsp compile . in its repo (emitter @typespec/openapi3) and absorb again, or put the OpenAPI it writes in inputs/specs/.";
|
|
1836
|
+
return {
|
|
1837
|
+
id,
|
|
1838
|
+
title: o.namespace ? `${o.namespace} (${file})` : file,
|
|
1839
|
+
path: `references/typespec/${id}.yaml`,
|
|
1840
|
+
content: (0, import_yaml6.stringify)({
|
|
1841
|
+
kind: "typespec",
|
|
1842
|
+
source: `${prefix}:${file}`,
|
|
1843
|
+
...o.namespace ? { namespace: o.namespace } : {},
|
|
1844
|
+
...o.operations.length ? { operations: o.operations } : {},
|
|
1845
|
+
...o.models.length ? { models: o.models } : {},
|
|
1846
|
+
note
|
|
1847
|
+
}, { lineWidth: 0 }),
|
|
1848
|
+
confidence: "high"
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
// packages/adopt/dist/absorb/source.js
|
|
1853
|
+
import { mkdir as mkdir4, rm as rm2 } from "node:fs/promises";
|
|
1854
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join8, resolve as resolve5 } from "node:path";
|
|
1855
|
+
import { fileURLToPath } from "node:url";
|
|
1856
|
+
var REMOTE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
1857
|
+
var SCP = /^[^/\\]+@[^:/\\]+:/;
|
|
1858
|
+
async function isBare(dir) {
|
|
1859
|
+
return await isDir(join8(dir, "objects")) && await exists(join8(dir, "HEAD")) && !await isDir(join8(dir, ".git"));
|
|
1860
|
+
}
|
|
1861
|
+
async function openSource(source, o) {
|
|
1862
|
+
let local;
|
|
1863
|
+
if (source.startsWith("file://"))
|
|
1864
|
+
local = fileURLToPath(source);
|
|
1865
|
+
else if (!REMOTE.test(source) && !SCP.test(source))
|
|
1866
|
+
local = isAbsolute2(source) ? source : resolve5(o.root, source);
|
|
1867
|
+
if (local) {
|
|
1868
|
+
if (!await isDir(local))
|
|
1869
|
+
throw new AdoptError("source", `There is no folder or repository at ${source}.`);
|
|
1870
|
+
if (!await isBare(local) && !o.ref)
|
|
1871
|
+
return { dir: local, kind: "folder", url: source };
|
|
1872
|
+
}
|
|
1873
|
+
const target = join8(o.stateDir, "absorbed", o.id);
|
|
1874
|
+
await rm2(target, { recursive: true, force: true });
|
|
1875
|
+
const git = o.git ?? createGit();
|
|
1876
|
+
const url = local && !source.startsWith("file://") ? local : source;
|
|
1877
|
+
await mkdir4(dirname3(target), { recursive: true });
|
|
1878
|
+
try {
|
|
1879
|
+
await git(["clone", "--depth", "1", ...o.ref ? ["--branch", o.ref] : [], "--", url, target], dirname3(target));
|
|
1880
|
+
} catch (e) {
|
|
1881
|
+
throw new AdoptError("source", `Couldn't clone ${source}: ${e.message}`);
|
|
1882
|
+
}
|
|
1883
|
+
return { dir: target, kind: "clone", url: source };
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// packages/adopt/dist/absorb/absorb.js
|
|
1887
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
1888
|
+
import { mkdir as mkdir5, readFile as readFile4, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
|
|
1889
|
+
import { dirname as dirname4, join as join9 } from "node:path";
|
|
1890
|
+
var DOC = /(^|\/)(README|LICENSE|LICENCE|CHANGELOG|CONTRIBUTING|CODE_OF_CONDUCT|SECURITY|NOTICE|CODEOWNERS)(\.(md|txt|rst|adoc))?$/i;
|
|
1891
|
+
var CODE = /\.(java|kt|scala|groovy|ts|tsx|js|jsx|mjs|cjs|vue|py|go|cs|fs|rb|php|rs|c|h|cpp|swift|sql|sh|ps1|bat|cbl|cob|jcl|cpy|tf|hcl|gradle|xml|properties|toml|lock|csproj|sln|dockerfile|ini|cfg)$|(^|\/)(Dockerfile|Makefile|tspconfig\.ya?ml|go\.mod|go\.sum|package\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|requirements\.txt|\.gitignore|\.gitattributes|\.editorconfig|\.npmrc|\.nvmrc)$/i;
|
|
1892
|
+
var CRITIC_PATH = /(^|\/)(reviews?|critic|critics|checklists?|checks)(\/|$)|checklist|critic/i;
|
|
1893
|
+
var PROMPT_DIR = /(^|\/)\.?prompts\//i;
|
|
1894
|
+
async function formatOf(dir) {
|
|
1895
|
+
if (await isDir(join9(dir, "org-pack")))
|
|
1896
|
+
return { format: "atticus", orgPack: "org-pack" };
|
|
1897
|
+
if (await isDir(join9(dir, ".atticus", "org-pack")))
|
|
1898
|
+
return { format: "atticus", orgPack: ".atticus/org-pack" };
|
|
1899
|
+
if (await isDir(join9(dir, "openspec")))
|
|
1900
|
+
return { format: "openspec", openspec: "openspec" };
|
|
1901
|
+
if (await isDir(join9(dir, ".specify")))
|
|
1902
|
+
return { format: "speckit" };
|
|
1903
|
+
return { format: "plain" };
|
|
1904
|
+
}
|
|
1905
|
+
async function absorb(root, source, o, paths) {
|
|
1906
|
+
const t0 = performance.now();
|
|
1907
|
+
if (!RecordId.safeParse(o.id).success)
|
|
1908
|
+
throw new AdoptError("usage", `"${o.id}" can't be an absorbed id; use a lowercase slug such as payments-team.`);
|
|
1909
|
+
const p = paths ?? await resolveMetarepo(root);
|
|
1910
|
+
const opened = await openSource(source, {
|
|
1911
|
+
root,
|
|
1912
|
+
stateDir: p.stateDir,
|
|
1913
|
+
id: o.id,
|
|
1914
|
+
...o.ref ? { ref: o.ref } : {},
|
|
1915
|
+
...o.git ? { git: o.git } : {}
|
|
1916
|
+
});
|
|
1917
|
+
const dir = opened.dir;
|
|
1918
|
+
const fmt = await formatOf(dir);
|
|
1919
|
+
if (fmt.format === "atticus" && fmt.orgPack) {
|
|
1920
|
+
const theirs = await loadOrgPack(join9(dir, ...fmt.orgPack.split("/")));
|
|
1921
|
+
fmt.openspec = theirs.sources.layout.openspec;
|
|
1922
|
+
}
|
|
1923
|
+
const files = (await listFiles(dir, { skipNames: /* @__PURE__ */ new Set([".git", "node_modules"]) })).filter((f) => !isLocalState(f, fmt.format === "atticus"));
|
|
1924
|
+
const read = async (f) => (await readFile4(join9(dir, f), "utf8")).replace(/\r\n/g, "\n");
|
|
1925
|
+
const items = [];
|
|
1926
|
+
const problems = [];
|
|
1927
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1928
|
+
const prefix = o.id;
|
|
1929
|
+
const owner = o.owner ?? o.id;
|
|
1930
|
+
const usedRuleIds = /* @__PURE__ */ new Set();
|
|
1931
|
+
const packs = /* @__PURE__ */ new Map();
|
|
1932
|
+
const add = (i) => items.push(i);
|
|
1933
|
+
const claim = (f) => claimed.add(f);
|
|
1934
|
+
if (fmt.format === "atticus") {
|
|
1935
|
+
const op = fmt.orgPack;
|
|
1936
|
+
const theirs = await loadOrgPack(join9(dir, ...op.split("/")));
|
|
1937
|
+
const bad = new Set(theirs.problems.map((x) => x.file));
|
|
1938
|
+
for (const x of theirs.problems)
|
|
1939
|
+
problems.push(`${op}/${x.file}: ${x.path ? `${x.path}: ` : ""}${x.message}`);
|
|
1940
|
+
const adoptable = /^(checklists|patterns|dialects|agents|process)\//;
|
|
1941
|
+
for (const f of files.filter((f2) => f2.startsWith(`${op}/`))) {
|
|
1942
|
+
claim(f);
|
|
1943
|
+
const rel = f.slice(op.length + 1);
|
|
1944
|
+
add({
|
|
1945
|
+
id: `atticus-orgpack:${f}`,
|
|
1946
|
+
kind: "atticus-orgpack",
|
|
1947
|
+
path: f,
|
|
1948
|
+
title: rel,
|
|
1949
|
+
action: "link",
|
|
1950
|
+
reason: adoptable.test(rel) ? "Their org-pack file, read as it is (federation keeps full fidelity); adopt to own a copy." : "Part of their org pack's identity (pack, roles, profiles, sources): read for federation, never copied.",
|
|
1951
|
+
confidence: bad.has(rel) ? "medium" : "high",
|
|
1952
|
+
translated: true,
|
|
1953
|
+
...adoptable.test(rel) ? { artifact: { path: `org-pack/${rel}`, content: await read(f) } } : {}
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
const catalogRel = theirs.sources.layout.catalog;
|
|
1957
|
+
const cat = files.filter((f) => f.startsWith(`${catalogRel}/`));
|
|
1958
|
+
if (cat.length) {
|
|
1959
|
+
cat.forEach(claim);
|
|
1960
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1961
|
+
for (const f of cat.filter((f2) => f2.startsWith(`${catalogRel}/generated/`))) {
|
|
1962
|
+
const k = f.split("/")[catalogRel.split("/").length + 1] ?? "other";
|
|
1963
|
+
kinds.set(k, (kinds.get(k) ?? 0) + 1);
|
|
1964
|
+
}
|
|
1965
|
+
const n = [...kinds.values()].reduce((a, b) => a + b, 0);
|
|
1966
|
+
add({
|
|
1967
|
+
id: `atticus-catalog:${catalogRel}`,
|
|
1968
|
+
kind: "atticus-catalog",
|
|
1969
|
+
path: catalogRel,
|
|
1970
|
+
title: `${n} catalog records (${[...kinds.entries()].map(([k, v]) => `${v} ${k}`).join(", ")})`,
|
|
1971
|
+
action: "link",
|
|
1972
|
+
reason: "Federated at full fidelity: every scan adds their records with their origin kept, and local code wins.",
|
|
1973
|
+
confidence: "high",
|
|
1974
|
+
translated: true
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
const os2 = fmt.openspec ?? "openspec";
|
|
1978
|
+
const changeDirs = new Set(files.filter((f) => f.startsWith(`${os2}/changes/`) && f.endsWith("/change.yaml")).map((f) => dirname4(f)));
|
|
1979
|
+
for (const cd of [...changeDirs].sort()) {
|
|
1980
|
+
const inside = files.filter((f) => f.startsWith(`${cd}/`));
|
|
1981
|
+
inside.forEach(claim);
|
|
1982
|
+
add({
|
|
1983
|
+
id: `atticus-change:${cd}`,
|
|
1984
|
+
kind: "atticus-change",
|
|
1985
|
+
path: cd,
|
|
1986
|
+
title: cd.split("/").pop(),
|
|
1987
|
+
action: "link",
|
|
1988
|
+
reason: cd.includes("/archive/") ? "An archived change: history, read only." : "Their open change: visible read-only for conflict detection with ours.",
|
|
1989
|
+
confidence: "high",
|
|
1990
|
+
translated: true
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
const os = fmt.openspec ?? (files.some((f) => f.startsWith("openspec/")) ? "openspec" : void 0);
|
|
1995
|
+
if (os) {
|
|
1996
|
+
for (const f of files.filter((f2) => !claimed.has(f2) && f2.startsWith(`${os}/specs/`) && f2.endsWith("/spec.md"))) {
|
|
1997
|
+
claim(f);
|
|
1998
|
+
const cap = f.split("/").slice(-2)[0];
|
|
1999
|
+
add({
|
|
2000
|
+
id: `openspec-spec:${f}`,
|
|
2001
|
+
kind: "openspec-spec",
|
|
2002
|
+
path: f,
|
|
2003
|
+
title: `Living spec ${cap}`,
|
|
2004
|
+
action: o.mode === "adopt" ? "adopt" : "link",
|
|
2005
|
+
reason: o.mode === "adopt" ? "Imported once into our openspec/specs (already OpenSpec: no conversion)." : "Followed as their living spec; adopt to import it into ours.",
|
|
2006
|
+
confidence: "high",
|
|
2007
|
+
translated: true,
|
|
2008
|
+
artifact: {
|
|
2009
|
+
path: o.mode === "adopt" ? `openspec/specs/${prefix}-${cap}/spec.md` : `references/openspec/${prefix}-${cap}.md`,
|
|
2010
|
+
content: await read(f)
|
|
2011
|
+
}
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
const changeDirs = new Set(files.filter((f) => !claimed.has(f) && f.startsWith(`${os}/changes/`)).map((f) => f.slice(`${os}/changes/`.length).split("/")).filter((s) => s.length > 1 && s[0] !== "archive").map((s) => `${os}/changes/${s[0]}`));
|
|
2015
|
+
for (const cd of [...changeDirs].sort()) {
|
|
2016
|
+
const inside = files.filter((f) => f.startsWith(`${cd}/`));
|
|
2017
|
+
inside.forEach(claim);
|
|
2018
|
+
const proposal = inside.includes(`${cd}/proposal.md`) ? await read(`${cd}/proposal.md`) : "";
|
|
2019
|
+
const title = /^#\s+(.+)$/m.exec(proposal)?.[1]?.trim() ?? cd.split("/").pop();
|
|
2020
|
+
add({
|
|
2021
|
+
id: `openspec-change:${cd}`,
|
|
2022
|
+
kind: "openspec-change",
|
|
2023
|
+
path: cd,
|
|
2024
|
+
title,
|
|
2025
|
+
action: "link",
|
|
2026
|
+
reason: "Their change in progress: followed read-only so overlaps with our changes show up.",
|
|
2027
|
+
confidence: "high",
|
|
2028
|
+
translated: true,
|
|
2029
|
+
artifact: {
|
|
2030
|
+
path: `references/openspec-changes/${prefix}-${cd.split("/").pop()}.yaml`,
|
|
2031
|
+
content: (0, import_yaml7.stringify)({
|
|
2032
|
+
change: cd.split("/").pop(),
|
|
2033
|
+
title,
|
|
2034
|
+
files: inside.map((f) => f.slice(cd.length + 1))
|
|
2035
|
+
}, { lineWidth: 0 })
|
|
2036
|
+
}
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
for (const f of files.filter((f2) => !claimed.has(f2) && f2.startsWith(`${os}/`))) {
|
|
2040
|
+
claim(f);
|
|
2041
|
+
add({
|
|
2042
|
+
id: `openspec-config:${f}`,
|
|
2043
|
+
kind: "openspec-config",
|
|
2044
|
+
path: f,
|
|
2045
|
+
title: f.slice(os.length + 1),
|
|
2046
|
+
action: /\/(archive)\//.test(f) ? "ignore" : "link",
|
|
2047
|
+
reason: /\/schemas\//.test(f) ? "Their OpenSpec workflow schema: Atticus keeps its own org-flow schema; kept as a reference." : /\/archive\//.test(f) ? "An archived change: history only." : "OpenSpec project context, kept as a reference.",
|
|
2048
|
+
confidence: "high",
|
|
2049
|
+
translated: true,
|
|
2050
|
+
artifact: reference("openspec", f, prefix, "OpenSpec configuration or context of the absorbed repo.")
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
for (const f of files.filter((f2) => !claimed.has(f2) && f2.startsWith(".specify/"))) {
|
|
2055
|
+
claim(f);
|
|
2056
|
+
if (f.endsWith("memory/constitution.md")) {
|
|
2057
|
+
const c = parseConstitution(await read(f));
|
|
2058
|
+
if (c.generatedByAtticus) {
|
|
2059
|
+
add({
|
|
2060
|
+
id: `constitution-principle:${f}`,
|
|
2061
|
+
kind: "constitution-principle",
|
|
2062
|
+
path: f,
|
|
2063
|
+
title: "Constitution generated by Atticus",
|
|
2064
|
+
action: "ignore",
|
|
2065
|
+
reason: "Generated by Atticus's own hand-down from an org pack: nothing new to absorb.",
|
|
2066
|
+
confidence: "high",
|
|
2067
|
+
translated: true,
|
|
2068
|
+
sameAs: "the Atticus constitution"
|
|
2069
|
+
});
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
const packPath = `org-pack/checklists/${prefix}-constitution.yaml`;
|
|
2073
|
+
const pack = {
|
|
2074
|
+
name: `${prefix}-constitution`,
|
|
2075
|
+
description: `Constitution principles absorbed from ${prefix} (Spec Kit).`,
|
|
2076
|
+
drafts: [],
|
|
2077
|
+
items: []
|
|
2078
|
+
};
|
|
2079
|
+
packs.set(packPath, pack);
|
|
2080
|
+
c.principles.forEach((pr, n) => {
|
|
2081
|
+
const draft = translateCriticItem({
|
|
2082
|
+
text: `${pr.title}: ${pr.body.split("\n").join(" ")}`,
|
|
2083
|
+
line: pr.line,
|
|
2084
|
+
...pr.nonNegotiable ? { severity: "blocking" } : {},
|
|
2085
|
+
sourceId: pr.title
|
|
2086
|
+
}, { prefix, source: `${prefix}:${f}`, layer: "constitution", usedIds: usedRuleIds });
|
|
2087
|
+
draft.rule.title = pr.title;
|
|
2088
|
+
draft.rule.description = pr.body.replace(/\s+/g, " ").trim();
|
|
2089
|
+
const item = {
|
|
2090
|
+
id: `constitution-principle:${f}#${n + 1}`,
|
|
2091
|
+
kind: "constitution-principle",
|
|
2092
|
+
path: f,
|
|
2093
|
+
line: pr.line,
|
|
2094
|
+
title: pr.title,
|
|
2095
|
+
action: o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2096
|
+
reason: draft.deterministic ? `A constitution-layer rule run by the built-in check ${draft.rule.check.check}; never waived.` : "A constitution-layer rule with an AI check (its words are the prompt); never waived.",
|
|
2097
|
+
confidence: draft.problems.length ? "low" : draft.deterministic ? "high" : "medium",
|
|
2098
|
+
translated: draft.problems.length === 0,
|
|
2099
|
+
artifact: { path: packPath, content: (0, import_yaml7.stringify)(draft.rule, { lineWidth: 0 }) }
|
|
2100
|
+
};
|
|
2101
|
+
if (draft.problems.length)
|
|
2102
|
+
problems.push(`${f} principle ${pr.title}: ${draft.problems.join("; ")}`);
|
|
2103
|
+
pack.drafts.push(draft);
|
|
2104
|
+
pack.items.push(item);
|
|
2105
|
+
add(item);
|
|
2106
|
+
});
|
|
2107
|
+
for (const s of c.sections) {
|
|
2108
|
+
const governance = /governance/i.test(s.title);
|
|
2109
|
+
if (governance) {
|
|
2110
|
+
add({
|
|
2111
|
+
id: `constitution-principle:${f}#${slug(s.title)}`,
|
|
2112
|
+
kind: "constitution-principle",
|
|
2113
|
+
path: f,
|
|
2114
|
+
line: s.line,
|
|
2115
|
+
title: s.title,
|
|
2116
|
+
action: "ignore",
|
|
2117
|
+
reason: "Atticus's governance applies instead: constitution rules are never waived, tightened only, and changed by PR to the org pack.",
|
|
2118
|
+
confidence: "high",
|
|
2119
|
+
translated: true,
|
|
2120
|
+
sameAs: "Atticus constitution governance"
|
|
2121
|
+
});
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
const draft = translateCriticItem({ text: `${s.title}: ${s.body.split("\n").join(" ")}`, line: s.line, sourceId: s.title }, { prefix, source: `${prefix}:${f}`, layer: "constitution", usedIds: usedRuleIds });
|
|
2125
|
+
draft.rule.title = s.title;
|
|
2126
|
+
draft.rule.severity = "risk";
|
|
2127
|
+
const item = {
|
|
2128
|
+
id: `constitution-principle:${f}#${slug(s.title)}`,
|
|
2129
|
+
kind: "constitution-principle",
|
|
2130
|
+
path: f,
|
|
2131
|
+
line: s.line,
|
|
2132
|
+
title: s.title,
|
|
2133
|
+
action: o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2134
|
+
reason: "Extra constraints in the constitution, as a constitution-layer risk rule with an AI check.",
|
|
2135
|
+
confidence: "medium",
|
|
2136
|
+
translated: true,
|
|
2137
|
+
artifact: { path: packPath, content: (0, import_yaml7.stringify)(draft.rule, { lineWidth: 0 }) }
|
|
2138
|
+
};
|
|
2139
|
+
pack.drafts.push(draft);
|
|
2140
|
+
pack.items.push(item);
|
|
2141
|
+
add(item);
|
|
2142
|
+
}
|
|
2143
|
+
continue;
|
|
2144
|
+
}
|
|
2145
|
+
if (f.startsWith(".specify/templates/")) {
|
|
2146
|
+
const base = f.split("/").pop().replace(/\.md$/, "");
|
|
2147
|
+
if (/^spec-template$/.test(base)) {
|
|
2148
|
+
const id = slug(`${prefix}-spec`);
|
|
2149
|
+
add({
|
|
2150
|
+
id: `speckit-template:${f}`,
|
|
2151
|
+
kind: "speckit-template",
|
|
2152
|
+
path: f,
|
|
2153
|
+
title: "Spec Kit feature spec template",
|
|
2154
|
+
action: o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2155
|
+
reason: "Becomes an org-pack change template: a brief skeleton with the same sections.",
|
|
2156
|
+
confidence: "medium",
|
|
2157
|
+
translated: true,
|
|
2158
|
+
artifact: {
|
|
2159
|
+
path: `org-pack/process/change-templates/${id}.md`,
|
|
2160
|
+
content: changeTemplateFrom(id, `${prefix} feature spec`, await read(f))
|
|
2161
|
+
}
|
|
2162
|
+
});
|
|
2163
|
+
} else
|
|
2164
|
+
add({
|
|
2165
|
+
id: `speckit-template:${f}`,
|
|
2166
|
+
kind: "speckit-template",
|
|
2167
|
+
path: f,
|
|
2168
|
+
title: `Spec Kit ${base}`,
|
|
2169
|
+
action: o.mode === "ignore" ? "ignore" : "link",
|
|
2170
|
+
reason: "Atticus hands down plan.md and tasks.md itself; their template is kept as a reference for the build skill.",
|
|
2171
|
+
confidence: "high",
|
|
2172
|
+
translated: true,
|
|
2173
|
+
artifact: reference("speckit-templates", f, prefix, "A Spec Kit template of the absorbed repo.")
|
|
2174
|
+
});
|
|
2175
|
+
continue;
|
|
2176
|
+
}
|
|
2177
|
+
add({
|
|
2178
|
+
id: `speckit-script:${f}`,
|
|
2179
|
+
kind: "speckit-script",
|
|
2180
|
+
path: f,
|
|
2181
|
+
title: f,
|
|
2182
|
+
action: "ignore",
|
|
2183
|
+
reason: "Spec Kit's own machinery: Atticus installs the pinned Spec Kit files through assistant files.",
|
|
2184
|
+
confidence: "high",
|
|
2185
|
+
translated: true,
|
|
2186
|
+
sameAs: "Atticus Spec Kit adapter files"
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
const speckitSpecs = files.filter((f) => !claimed.has(f) && /^specs\/[^/]+\//.test(f));
|
|
2190
|
+
if (speckitSpecs.length && (fmt.format === "speckit" || files.some((f) => f.startsWith(".specify/")))) {
|
|
2191
|
+
for (const f of speckitSpecs) {
|
|
2192
|
+
const feature = f.split("/")[1];
|
|
2193
|
+
const name = f.split("/").slice(2).join("/");
|
|
2194
|
+
if (name === "spec.md") {
|
|
2195
|
+
claim(f);
|
|
2196
|
+
const conv = openSpecFrom(feature, await read(f));
|
|
2197
|
+
const adopt2 = o.mode === "adopt";
|
|
2198
|
+
add({
|
|
2199
|
+
id: `speckit-spec:${f}`,
|
|
2200
|
+
kind: "speckit-spec",
|
|
2201
|
+
path: f,
|
|
2202
|
+
title: `Feature spec ${feature}`,
|
|
2203
|
+
action: adopt2 ? "adopt" : o.mode === "ignore" ? "ignore" : "link",
|
|
2204
|
+
reason: adopt2 ? `One-off import into our OpenSpec living specs (${conv.requirements} requirements).` : "A read-only reference: the speckit extractor reads it on every scan; adopt to import it into OpenSpec.",
|
|
2205
|
+
confidence: conv.requirements ? "high" : "low",
|
|
2206
|
+
translated: conv.requirements > 0,
|
|
2207
|
+
artifact: {
|
|
2208
|
+
path: adopt2 ? `openspec/specs/${prefix}-${feature}/spec.md` : `references/specs/${prefix}-${feature}.md`,
|
|
2209
|
+
content: conv.spec
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
} else if (/^(plan|tasks|research|quickstart|data-model)\.md$/.test(name)) {
|
|
2213
|
+
claim(f);
|
|
2214
|
+
add({
|
|
2215
|
+
id: `speckit-plan:${f}`,
|
|
2216
|
+
kind: "speckit-plan",
|
|
2217
|
+
path: f,
|
|
2218
|
+
title: `${feature} ${name}`,
|
|
2219
|
+
action: o.mode === "ignore" ? "ignore" : "link",
|
|
2220
|
+
reason: "Repo-level plan: the speckit extractor reads it as planned records and progress when the repo is scanned.",
|
|
2221
|
+
confidence: "high",
|
|
2222
|
+
translated: true,
|
|
2223
|
+
artifact: reference("speckit-plans", f, prefix, "Read by the speckit extractor when the repo is in a manifest.")
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2229
|
+
const dup = (text2, id) => {
|
|
2230
|
+
const k = bodyKey(text2);
|
|
2231
|
+
const other = seen.get(k);
|
|
2232
|
+
if (!other)
|
|
2233
|
+
seen.set(k, id);
|
|
2234
|
+
return other;
|
|
2235
|
+
};
|
|
2236
|
+
const isSpecKitCommand = (f) => /(^|\/)speckit\.[a-z-]+(\.prompt|\.agent)?\.md$/i.test(f);
|
|
2237
|
+
for (const f of files.filter((f2) => !claimed.has(f2))) {
|
|
2238
|
+
const prompt = /^\.github\/prompts\/.+\.prompt\.md$/i.test(f) || /^\.claude\/commands\/.+\.md$/i.test(f) || PROMPT_DIR.test(f) && /\.md$/i.test(f) && !DOC.test(f);
|
|
2239
|
+
const agent = /^\.github\/agents\/.+\.agent\.md$/i.test(f) || /^\.github\/chatmodes\/.+\.chatmode\.md$/i.test(f) || /^\.claude\/agents\/.+\.md$/i.test(f);
|
|
2240
|
+
const skill = /^\.claude\/skills\/.+\/SKILL\.md$/.test(f) || /^\.github\/instructions\/.+\.instructions\.md$/i.test(f);
|
|
2241
|
+
const instructions = /^(CLAUDE\.md|AGENTS\.md|\.github\/copilot-instructions\.md)$/.test(f);
|
|
2242
|
+
if (!prompt && !agent && !skill && !instructions)
|
|
2243
|
+
continue;
|
|
2244
|
+
claim(f);
|
|
2245
|
+
const text2 = await read(f);
|
|
2246
|
+
if (isSpecKitCommand(f)) {
|
|
2247
|
+
add({
|
|
2248
|
+
id: `speckit-command:${f}`,
|
|
2249
|
+
kind: "speckit-command",
|
|
2250
|
+
path: f,
|
|
2251
|
+
title: f.split("/").pop(),
|
|
2252
|
+
action: "ignore",
|
|
2253
|
+
reason: "A Spec Kit command: Atticus generates the Spec Kit-compatible commands itself.",
|
|
2254
|
+
confidence: "high",
|
|
2255
|
+
translated: true,
|
|
2256
|
+
sameAs: "Atticus Spec Kit commands"
|
|
2257
|
+
});
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
const id = `${prompt ? "prompt" : agent ? "agent" : skill ? "skill" : "instructions"}:${f}`;
|
|
2261
|
+
const same = dup(text2, id);
|
|
2262
|
+
if (prompt) {
|
|
2263
|
+
const t = promptToCommand(f, text2, prefix);
|
|
2264
|
+
add({
|
|
2265
|
+
id,
|
|
2266
|
+
kind: "prompt",
|
|
2267
|
+
path: f,
|
|
2268
|
+
title: t.title,
|
|
2269
|
+
action: same ? "ignore" : o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2270
|
+
reason: same ? `The same prompt as ${same.split(":").slice(1).join(":")} (one command serves both assistants).` : "Becomes an org-pack command, generated for Copilot and Claude Code from one source.",
|
|
2271
|
+
confidence: t.confidence,
|
|
2272
|
+
translated: true,
|
|
2273
|
+
artifact: { path: t.path, content: t.content },
|
|
2274
|
+
...same ? { sameAs: same } : {}
|
|
2275
|
+
});
|
|
2276
|
+
continue;
|
|
2277
|
+
}
|
|
2278
|
+
const s = toSkill(f, text2, {
|
|
2279
|
+
prefix,
|
|
2280
|
+
owner,
|
|
2281
|
+
source: `${prefix}:${f}`,
|
|
2282
|
+
kind: agent ? "agent" : skill ? "skill" : "instructions"
|
|
2283
|
+
});
|
|
2284
|
+
if (s.problems.length)
|
|
2285
|
+
problems.push(`${f}: ${s.problems.join("; ")}`);
|
|
2286
|
+
add({
|
|
2287
|
+
id,
|
|
2288
|
+
kind: agent ? "agent" : skill ? "skill" : "instructions",
|
|
2289
|
+
path: f,
|
|
2290
|
+
title: s.title,
|
|
2291
|
+
action: same ? "ignore" : instructions ? o.mode === "ignore" ? "ignore" : o.mode === "adopt" ? "adopt" : "link" : o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2292
|
+
reason: same ? `The same text as ${same.split(":").slice(1).join(":")}.` : instructions ? "Their standing instructions, as a draft skill: linked (they describe their repo), adopt to make them an org skill." : "Becomes an org-pack skill (one source for Copilot instructions and Claude Code skills).",
|
|
2293
|
+
confidence: s.problems.length ? "low" : s.confidence,
|
|
2294
|
+
translated: s.problems.length === 0,
|
|
2295
|
+
artifact: { path: s.path, content: s.content },
|
|
2296
|
+
...same ? { sameAs: same } : {}
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
const compiled = files.filter((f) => /(^|\/)tsp-output\/.+\.(ya?ml|json)$/i.test(f));
|
|
2300
|
+
for (const f of files.filter((f2) => !claimed.has(f2) && /\.tsp$/i.test(f2))) {
|
|
2301
|
+
claim(f);
|
|
2302
|
+
const t = typespecReference(f, await read(f), prefix, compiled);
|
|
2303
|
+
add({
|
|
2304
|
+
id: `typespec:${f}`,
|
|
2305
|
+
kind: "typespec",
|
|
2306
|
+
path: f,
|
|
2307
|
+
title: t.title,
|
|
2308
|
+
action: o.mode === "adopt" ? "adopt" : o.mode === "ignore" ? "ignore" : "link",
|
|
2309
|
+
reason: compiled.length ? "A TypeSpec contract: kept as a reference with its operations and models; its compiled OpenAPI is absorbed as a schema for intake." : "A TypeSpec contract: kept as a reference with its operations and models. No compiled OpenAPI is in the repo: run tsp compile . there and absorb again for intake to read it.",
|
|
2310
|
+
confidence: "high",
|
|
2311
|
+
translated: true,
|
|
2312
|
+
artifact: { path: t.path, content: t.content }
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
for (const f of files.filter((f2) => !claimed.has(f2))) {
|
|
2316
|
+
const text2 = /\.(ya?ml|json)$/i.test(f) ? await read(f) : "";
|
|
2317
|
+
const kind = text2 ? schemaKind(f, text2) : void 0;
|
|
2318
|
+
if (!kind)
|
|
2319
|
+
continue;
|
|
2320
|
+
claim(f);
|
|
2321
|
+
const t = schemaReference(f, text2, kind, prefix);
|
|
2322
|
+
const adopt2 = o.mode === "adopt";
|
|
2323
|
+
add({
|
|
2324
|
+
id: `schema:${f}`,
|
|
2325
|
+
kind: "schema",
|
|
2326
|
+
path: f,
|
|
2327
|
+
title: `${t.title} (${kind})`,
|
|
2328
|
+
action: adopt2 ? "adopt" : o.mode === "ignore" ? "ignore" : "link",
|
|
2329
|
+
reason: adopt2 ? "Copied into inputs/specs/ so intake reads it on every scan." : "Kept as a reference; copy it into inputs/specs/ (adopt) for intake to read it.",
|
|
2330
|
+
confidence: "high",
|
|
2331
|
+
translated: true,
|
|
2332
|
+
artifact: adopt2 ? { path: `inputs/specs/${prefix}/${f}`, content: text2 } : { path: t.path, content: t.content }
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
for (const f of files.filter((f2) => !claimed.has(f2) && CRITIC_PATH.test(f2) && !DOC.test(f2))) {
|
|
2336
|
+
const text2 = await read(f);
|
|
2337
|
+
const list = /\.ya?ml$/i.test(f) ? yamlItems(text2) : /\.md$/i.test(f) ? markdownItems(text2) : void 0;
|
|
2338
|
+
if (!list || list.length < 2)
|
|
2339
|
+
continue;
|
|
2340
|
+
claim(f);
|
|
2341
|
+
const fileSlug = slug(f.replace(/\.[a-z]+$/i, "").split("/").pop());
|
|
2342
|
+
const packPath = `org-pack/checklists/${prefix}-${fileSlug}.yaml`;
|
|
2343
|
+
const pack = {
|
|
2344
|
+
name: slug(`${prefix}-${fileSlug}`),
|
|
2345
|
+
description: `Checklist absorbed from ${prefix}: ${f}.`,
|
|
2346
|
+
drafts: [],
|
|
2347
|
+
items: []
|
|
2348
|
+
};
|
|
2349
|
+
packs.set(packPath, pack);
|
|
2350
|
+
list.forEach((ci, n) => {
|
|
2351
|
+
const draft = translateCriticItem(ci, {
|
|
2352
|
+
prefix,
|
|
2353
|
+
source: `${prefix}:${f}`,
|
|
2354
|
+
layer: "org",
|
|
2355
|
+
usedIds: usedRuleIds
|
|
2356
|
+
});
|
|
2357
|
+
if (draft.problems.length)
|
|
2358
|
+
problems.push(`${f}:${ci.line}: ${draft.problems.join("; ")}`);
|
|
2359
|
+
const item = {
|
|
2360
|
+
id: `critic-rule:${f}#${n + 1}`,
|
|
2361
|
+
kind: "critic-rule",
|
|
2362
|
+
path: f,
|
|
2363
|
+
line: ci.line,
|
|
2364
|
+
title: draft.rule.title,
|
|
2365
|
+
action: o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2366
|
+
reason: draft.deterministic ? `Maps to the built-in check ${draft.rule.check.check}: a deterministic ${draft.rule.severity} rule.` : `No built-in check asks this: an AI rule on the ${draft.rule.lens} lens (${draft.rule.severity}, a guess to review).`,
|
|
2367
|
+
confidence: draft.problems.length ? "low" : draft.confidence,
|
|
2368
|
+
translated: draft.problems.length === 0,
|
|
2369
|
+
artifact: { path: packPath, content: (0, import_yaml7.stringify)(draft.rule, { lineWidth: 0 }) }
|
|
2370
|
+
};
|
|
2371
|
+
pack.drafts.push(draft);
|
|
2372
|
+
pack.items.push(item);
|
|
2373
|
+
add(item);
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
const code = [];
|
|
2377
|
+
for (const f of files.filter((f2) => !claimed.has(f2))) {
|
|
2378
|
+
if (DOC.test(f)) {
|
|
2379
|
+
add({
|
|
2380
|
+
id: `doc:${f}`,
|
|
2381
|
+
kind: "doc",
|
|
2382
|
+
path: f,
|
|
2383
|
+
title: f,
|
|
2384
|
+
action: "ignore",
|
|
2385
|
+
reason: "About the repo itself (readme, licence, changelog): nothing to absorb.",
|
|
2386
|
+
confidence: "high",
|
|
2387
|
+
translated: false
|
|
2388
|
+
});
|
|
2389
|
+
continue;
|
|
2390
|
+
}
|
|
2391
|
+
if (CODE.test(f) || !/\.(md|ya?ml|json|txt)$/i.test(f)) {
|
|
2392
|
+
code.push(f);
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
if (/\.md$/i.test(f) && CRITIC_PATH.test(f)) {
|
|
2396
|
+
const t = promptToCommand(f, await read(f), prefix);
|
|
2397
|
+
add({
|
|
2398
|
+
id: `prompt:${f}`,
|
|
2399
|
+
kind: "prompt",
|
|
2400
|
+
path: f,
|
|
2401
|
+
title: t.title,
|
|
2402
|
+
action: o.mode === "link" || o.mode === "ignore" ? o.mode : "adopt",
|
|
2403
|
+
reason: "A review prompt without checklist items: becomes an org-pack command.",
|
|
2404
|
+
confidence: "medium",
|
|
2405
|
+
translated: true,
|
|
2406
|
+
artifact: { path: t.path, content: t.content }
|
|
2407
|
+
});
|
|
2408
|
+
continue;
|
|
2409
|
+
}
|
|
2410
|
+
add({
|
|
2411
|
+
id: `other:${f}`,
|
|
2412
|
+
kind: "other",
|
|
2413
|
+
path: f,
|
|
2414
|
+
title: f,
|
|
2415
|
+
action: "ignore",
|
|
2416
|
+
reason: "Absorb couldn't tell what this is; adopt or link it by hand if it matters.",
|
|
2417
|
+
confidence: "low",
|
|
2418
|
+
translated: false
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
if (code.length)
|
|
2422
|
+
add({
|
|
2423
|
+
id: `code:${code.length}-files`,
|
|
2424
|
+
kind: "code",
|
|
2425
|
+
path: ".",
|
|
2426
|
+
title: `${code.length} code and build files`,
|
|
2427
|
+
action: "ignore",
|
|
2428
|
+
reason: "Code is read by the scan when the repo is in a repos manifest, not absorbed.",
|
|
2429
|
+
confidence: "high",
|
|
2430
|
+
translated: false
|
|
2431
|
+
});
|
|
2432
|
+
for (const [path, pk] of packs) {
|
|
2433
|
+
const rules = pk.drafts.filter((_, i) => pk.items[i].action === "adopt").map((d) => d.rule);
|
|
2434
|
+
if (rules.length)
|
|
2435
|
+
for (const x of packFor(pk.name, pk.description, rules).problems)
|
|
2436
|
+
problems.push(`${path}: ${x}`);
|
|
2437
|
+
}
|
|
2438
|
+
const summary = summarise(items);
|
|
2439
|
+
const linkish = items.some((i) => i.action === "link");
|
|
2440
|
+
const entry = {
|
|
2441
|
+
id: o.id,
|
|
2442
|
+
url: opened.url,
|
|
2443
|
+
format: fmt.format,
|
|
2444
|
+
...o.ref ? { ref: o.ref } : {},
|
|
2445
|
+
mode: o.mode ?? (linkish ? "link" : "adopt")
|
|
2446
|
+
};
|
|
2447
|
+
const result = {
|
|
2448
|
+
id: o.id,
|
|
2449
|
+
format: fmt.format,
|
|
2450
|
+
sourceDir: dir,
|
|
2451
|
+
items,
|
|
2452
|
+
summary,
|
|
2453
|
+
entry,
|
|
2454
|
+
files: [],
|
|
2455
|
+
problems,
|
|
2456
|
+
ms: 0,
|
|
2457
|
+
sourceFiles: Object.fromEntries(await Promise.all(files.map(async (f) => [f, sha256(await readFile4(join9(dir, f)))])))
|
|
2458
|
+
};
|
|
2459
|
+
if (!o.dryRun) {
|
|
2460
|
+
const out = join9(p.proposalsDir, "absorb", o.id);
|
|
2461
|
+
result.files = await writeProposal(out, result, packs);
|
|
2462
|
+
result.proposal = toPosix(`${p.proposalsRel}/absorb/${o.id}`);
|
|
2463
|
+
}
|
|
2464
|
+
result.ms = Math.round(performance.now() - t0);
|
|
2465
|
+
return result;
|
|
2466
|
+
}
|
|
2467
|
+
function summarise(items) {
|
|
2468
|
+
const translatable = items.filter((i) => !NOT_TRANSLATABLE.has(i.kind));
|
|
2469
|
+
const translated = translatable.filter((i) => i.translated).length;
|
|
2470
|
+
const byAction = { adopt: 0, link: 0, ignore: 0 };
|
|
2471
|
+
const byKind = {};
|
|
2472
|
+
for (const i of items) {
|
|
2473
|
+
byAction[i.action]++;
|
|
2474
|
+
byKind[i.kind] = (byKind[i.kind] ?? 0) + 1;
|
|
2475
|
+
}
|
|
2476
|
+
return {
|
|
2477
|
+
items: items.length,
|
|
2478
|
+
translatable: translatable.length,
|
|
2479
|
+
translated,
|
|
2480
|
+
translatedPercent: translatable.length ? Math.round(translated / translatable.length * 1e3) / 10 : 100,
|
|
2481
|
+
byAction,
|
|
2482
|
+
byKind
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
async function writeProposal(out, r, packs) {
|
|
2486
|
+
await rm3(out, { recursive: true, force: true });
|
|
2487
|
+
const files = /* @__PURE__ */ new Map();
|
|
2488
|
+
for (const i of r.items) {
|
|
2489
|
+
if (!i.artifact || i.action === "ignore")
|
|
2490
|
+
continue;
|
|
2491
|
+
if (packs.has(i.artifact.path))
|
|
2492
|
+
continue;
|
|
2493
|
+
const path = i.action === "link" && !i.artifact.path.startsWith("references/") ? `references/${i.kind}/${i.artifact.path.replace(/^org-pack\//, "")}` : i.artifact.path;
|
|
2494
|
+
if (!files.has(path))
|
|
2495
|
+
files.set(path, i.artifact.content);
|
|
2496
|
+
}
|
|
2497
|
+
for (const [path, pk] of packs) {
|
|
2498
|
+
const rules = pk.drafts.filter((_, n) => pk.items[n].action === "adopt").map((d) => d.rule);
|
|
2499
|
+
if (rules.length)
|
|
2500
|
+
files.set(path, packFor(pk.name, pk.description, rules).content);
|
|
2501
|
+
}
|
|
2502
|
+
files.set("org-pack/sources.absorbed.yaml", sourcesEntry(r));
|
|
2503
|
+
files.set("SUMMARY.md", summaryMd(r));
|
|
2504
|
+
files.set("PR.md", prMd(r));
|
|
2505
|
+
files.set("inventory.json", `${JSON.stringify({
|
|
2506
|
+
id: r.id,
|
|
2507
|
+
format: r.format,
|
|
2508
|
+
entry: r.entry,
|
|
2509
|
+
summary: r.summary,
|
|
2510
|
+
items: r.items.map(({ artifact, ...i }) => ({
|
|
2511
|
+
...i,
|
|
2512
|
+
...artifact ? { artifact: artifact.path } : {}
|
|
2513
|
+
})),
|
|
2514
|
+
sources: r.sourceFiles
|
|
2515
|
+
}, null, 2)}
|
|
2516
|
+
`);
|
|
2517
|
+
for (const [rel, text2] of files) {
|
|
2518
|
+
const full = join9(out, ...rel.split("/"));
|
|
2519
|
+
await mkdir5(dirname4(full), { recursive: true });
|
|
2520
|
+
await writeFile5(full, text2, "utf8");
|
|
2521
|
+
}
|
|
2522
|
+
return [...files.keys()].sort();
|
|
2523
|
+
}
|
|
2524
|
+
function sourcesEntry(r) {
|
|
2525
|
+
return `# Add this entry under absorbed: in org-pack/sources.yaml (in the same pull request).
|
|
2526
|
+
${(0, import_yaml7.stringify)({ absorbed: [r.entry] }, { lineWidth: 0 })}`;
|
|
2527
|
+
}
|
|
2528
|
+
var cell = (s) => s.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
2529
|
+
function summaryMd(r) {
|
|
2530
|
+
const s = r.summary;
|
|
2531
|
+
return [
|
|
2532
|
+
`# Absorb ${r.id}`,
|
|
2533
|
+
"",
|
|
2534
|
+
`Format: ${r.format}. ${s.items} items: ${s.byAction.adopt} adopt, ${s.byAction.link} link, ${s.byAction.ignore} ignore. Translated: ${s.translated} of ${s.translatable} (${s.translatedPercent}%).`,
|
|
2535
|
+
"",
|
|
2536
|
+
"| Item | Kind | Action | Confidence | Reason | Artifact |",
|
|
2537
|
+
"| --- | --- | --- | --- | --- | --- |",
|
|
2538
|
+
...r.items.map((i) => `| ${cell(i.path + (i.line ? `:${i.line}` : ""))} | ${i.kind} | ${i.action} | ${i.confidence} | ${cell(i.reason)} | ${i.artifact ? cell(i.artifact.path) : i.sameAs ? cell(`same as ${i.sameAs}`) : ""} |`),
|
|
2539
|
+
...r.problems.length ? ["", "## Problems", "", ...r.problems.map((p) => `- ${p}`)] : [],
|
|
2540
|
+
""
|
|
2541
|
+
].join("\n");
|
|
2542
|
+
}
|
|
2543
|
+
function prMd(r) {
|
|
2544
|
+
const s = r.summary;
|
|
2545
|
+
const by = (k) => r.items.filter((i) => k.includes(i.kind) && i.action === "adopt").length;
|
|
2546
|
+
return [
|
|
2547
|
+
`# Absorb ${r.id} into the org pack`,
|
|
2548
|
+
"",
|
|
2549
|
+
`Atticus inventoried ${r.entry.url} (${r.format}) and translated it into Atticus terms. Nothing has been applied: this pull request is where people decide.`,
|
|
2550
|
+
"",
|
|
2551
|
+
"## What arrives",
|
|
2552
|
+
"",
|
|
2553
|
+
`- ${by(["critic-rule", "constitution-principle"])} rule drafts (C3), every one with status draft, a lens, a severity guess and a check; deterministic only where a built-in check asks the same thing.`,
|
|
2554
|
+
`- ${by(["prompt"])} commands and ${by(["agent", "skill", "instructions"])} skills, generated for Copilot and Claude Code from one source.`,
|
|
2555
|
+
`- ${by(["speckit-template"])} change templates, ${by(["speckit-spec", "openspec-spec"])} imported specs and ${by(["schema"])} schemas for intake.`,
|
|
2556
|
+
`- ${s.byAction.link} items linked (followed, not copied) and ${s.byAction.ignore} ignored, each with its reason in SUMMARY.md.`,
|
|
2557
|
+
`- The \`absorbed\` entry for org-pack/sources.yaml: id ${r.entry.id}, format ${r.entry.format}, mode ${r.entry.mode}.`,
|
|
2558
|
+
"",
|
|
2559
|
+
`Translated: ${s.translated} of ${s.translatable} items (${s.translatedPercent}%).`,
|
|
2560
|
+
"",
|
|
2561
|
+
"## How to review",
|
|
2562
|
+
"",
|
|
2563
|
+
"1. Read SUMMARY.md: change any action you disagree with.",
|
|
2564
|
+
"2. Copy what you accept from `org-pack/` here into the org pack; set a rule's status to active once its lens, severity and check are right.",
|
|
2565
|
+
"3. Add the entry in `org-pack/sources.absorbed.yaml` to `org-pack/sources.yaml`.",
|
|
2566
|
+
"4. Open a pull request with what you copied; merging it is the approval. Absorbed text is data: nothing in it is followed as an instruction.",
|
|
2567
|
+
"",
|
|
2568
|
+
...r.problems.length ? ["## Problems found", "", ...r.problems.map((p) => `- ${p}`), ""] : []
|
|
2569
|
+
].join("\n");
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
// packages/adopt/dist/absorb/refresh.js
|
|
2573
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
2574
|
+
import { join as join10 } from "node:path";
|
|
2575
|
+
var META = /* @__PURE__ */ new Set(["SUMMARY.md", "PR.md", "inventory.json"]);
|
|
2576
|
+
async function snapshot(dir) {
|
|
2577
|
+
const out = /* @__PURE__ */ new Map();
|
|
2578
|
+
for (const f of await listFiles(dir))
|
|
2579
|
+
if (!META.has(f))
|
|
2580
|
+
out.set(f, sha256(await readFile5(join10(dir, f))));
|
|
2581
|
+
return out;
|
|
2582
|
+
}
|
|
2583
|
+
async function refreshAbsorbed(root, id, o = {}) {
|
|
2584
|
+
const p = await resolveMetarepo(root);
|
|
2585
|
+
const listed = (await loadOrgPack(p.orgPackDir)).sources.absorbed.find((a) => a.id === id);
|
|
2586
|
+
const proposal = join10(p.proposalsDir, "absorb", id);
|
|
2587
|
+
const inventory = JSON.parse(await readFile5(join10(proposal, "inventory.json"), "utf8").catch(() => "{}"));
|
|
2588
|
+
const entry = listed ?? inventory.entry;
|
|
2589
|
+
if (!entry)
|
|
2590
|
+
throw new AdoptError("not-found", `There is no absorbed source ${id}: absorb it first with atticus absorb <git-url|path> --id ${id}.`);
|
|
2591
|
+
if (entry.mode === "ignore")
|
|
2592
|
+
throw new AdoptError("usage", `${id} is absorbed with mode ignore; there is nothing to refresh.`);
|
|
2593
|
+
const before = await snapshot(proposal);
|
|
2594
|
+
const beforeItems = new Map((inventory.items ?? []).map((i) => [i.id, i.action]));
|
|
2595
|
+
const result = await absorb(root, entry.url, {
|
|
2596
|
+
id,
|
|
2597
|
+
...entry.mode ? { mode: entry.mode } : {},
|
|
2598
|
+
...entry.ref ? { ref: entry.ref } : {},
|
|
2599
|
+
...o.git ? { git: o.git } : {}
|
|
2600
|
+
});
|
|
2601
|
+
const after = await snapshot(proposal);
|
|
2602
|
+
const afterItems = new Map(result.items.map((i) => [i.id, i.action]));
|
|
2603
|
+
const sorted = (xs) => [...xs].sort();
|
|
2604
|
+
const diff = (a, b) => ({
|
|
2605
|
+
added: sorted([...b.keys()].filter((f) => !a.has(f))),
|
|
2606
|
+
removed: sorted([...a.keys()].filter((f) => !b.has(f))),
|
|
2607
|
+
changed: sorted([...b.keys()].filter((f) => a.has(f) && a.get(f) !== b.get(f)))
|
|
2608
|
+
});
|
|
2609
|
+
return {
|
|
2610
|
+
id,
|
|
2611
|
+
entry: { ...entry },
|
|
2612
|
+
from: listed ? "sources.yaml" : "proposal",
|
|
2613
|
+
result,
|
|
2614
|
+
source: diff(new Map(Object.entries(inventory.sources ?? {})), new Map(Object.entries(result.sourceFiles))),
|
|
2615
|
+
files: diff(before, after),
|
|
2616
|
+
items: {
|
|
2617
|
+
added: sorted([...afterItems.keys()].filter((i) => !beforeItems.has(i))),
|
|
2618
|
+
removed: sorted([...beforeItems.keys()].filter((i) => !afterItems.has(i))),
|
|
2619
|
+
changed: [...afterItems].filter(([i, a]) => beforeItems.has(i) && beforeItems.get(i) !== a).map(([i, to]) => ({ id: i, from: beforeItems.get(i), to })).sort((a, b) => a.id.localeCompare(b.id))
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
async function refreshAllAbsorbed(root, o = {}) {
|
|
2624
|
+
const p = await resolveMetarepo(root);
|
|
2625
|
+
const linked = (await loadOrgPack(p.orgPackDir)).sources.absorbed.filter((a) => a.mode === "link").sort((a, b) => a.id.localeCompare(b.id));
|
|
2626
|
+
const out = [];
|
|
2627
|
+
for (const a of linked)
|
|
2628
|
+
out.push(await refreshAbsorbed(root, a.id, o));
|
|
2629
|
+
return out;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
// packages/adopt/dist/absorb/accept.js
|
|
2633
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
2634
|
+
import { copyFile, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
|
|
2635
|
+
import { dirname as dirname5, join as join11, relative as relative3 } from "node:path";
|
|
2636
|
+
function targetOf(p, file) {
|
|
2637
|
+
const [head, ...rest] = file.split("/");
|
|
2638
|
+
const base = head === "org-pack" ? p.orgPackDir : head === "openspec" ? p.openspecDir : head === "inputs" ? p.inputsDir : void 0;
|
|
2639
|
+
return base && rest.length ? join11(base, ...rest) : void 0;
|
|
2640
|
+
}
|
|
2641
|
+
async function acceptAbsorbed(root, id, o = {}, paths) {
|
|
2642
|
+
const p = paths ?? await resolveMetarepo(root);
|
|
2643
|
+
const dir = join11(p.proposalsDir, "absorb", id);
|
|
2644
|
+
const text2 = await readText(join11(dir, "inventory.json"));
|
|
2645
|
+
if (text2 === void 0)
|
|
2646
|
+
throw new AdoptError("not-found", `There is no absorb proposal ${id}. Make it first: atticus absorb <git-url|folder> --id ${id}`);
|
|
2647
|
+
const inv = JSON.parse(text2);
|
|
2648
|
+
const rel = (f) => toPosix(relative3(root, f));
|
|
2649
|
+
const out = {
|
|
2650
|
+
id,
|
|
2651
|
+
proposal: rel(dir),
|
|
2652
|
+
copied: [],
|
|
2653
|
+
same: [],
|
|
2654
|
+
kept: [],
|
|
2655
|
+
linked: inv.items.filter((i) => i.action === "link").length,
|
|
2656
|
+
entry: "already",
|
|
2657
|
+
sourcesFile: rel(join11(p.orgPackDir, "sources.yaml"))
|
|
2658
|
+
};
|
|
2659
|
+
const files = [
|
|
2660
|
+
...new Set(inv.items.filter((i) => i.action === "adopt" && i.artifact).map((i) => i.artifact))
|
|
2661
|
+
].sort();
|
|
2662
|
+
for (const f of files) {
|
|
2663
|
+
const to = targetOf(p, f);
|
|
2664
|
+
if (!to)
|
|
2665
|
+
continue;
|
|
2666
|
+
const from = join11(dir, ...f.split("/"));
|
|
2667
|
+
const theirs = await readFile6(from);
|
|
2668
|
+
const ours = await readFile6(to).catch(() => void 0);
|
|
2669
|
+
if (ours && ours.equals(theirs)) {
|
|
2670
|
+
out.same.push(rel(to));
|
|
2671
|
+
continue;
|
|
2672
|
+
}
|
|
2673
|
+
if (ours && !o.replace) {
|
|
2674
|
+
out.kept.push(rel(to));
|
|
2675
|
+
continue;
|
|
2676
|
+
}
|
|
2677
|
+
out.copied.push(rel(to));
|
|
2678
|
+
if (!o.dryRun) {
|
|
2679
|
+
await mkdir6(dirname5(to), { recursive: true });
|
|
2680
|
+
await copyFile(from, to);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
const file = join11(p.orgPackDir, "sources.yaml");
|
|
2684
|
+
const doc = (0, import_yaml8.parseDocument)(await readText(file) ?? "schemaVersion: 1\n");
|
|
2685
|
+
const list = doc.get("absorbed");
|
|
2686
|
+
const entries = (0, import_yaml8.isSeq)(list) ? list.toJSON() : [];
|
|
2687
|
+
const at = entries.findIndex((e) => e.id === inv.entry.id);
|
|
2688
|
+
if (at < 0) {
|
|
2689
|
+
if ((0, import_yaml8.isSeq)(list))
|
|
2690
|
+
list.add(doc.createNode(inv.entry));
|
|
2691
|
+
else
|
|
2692
|
+
doc.set("absorbed", doc.createNode([inv.entry]));
|
|
2693
|
+
out.entry = "added";
|
|
2694
|
+
} else if (o.replace && JSON.stringify(entries[at]) !== JSON.stringify(inv.entry)) {
|
|
2695
|
+
doc.setIn(["absorbed", at], doc.createNode(inv.entry));
|
|
2696
|
+
out.entry = "replaced";
|
|
2697
|
+
}
|
|
2698
|
+
if (out.entry !== "already") {
|
|
2699
|
+
const checked = Sources.safeParse(doc.toJS());
|
|
2700
|
+
if (!checked.success)
|
|
2701
|
+
throw new AdoptError("usage", `sources.yaml would not be valid with ${id}'s entry: ${checked.error.issues[0]?.message ?? "invalid"}`);
|
|
2702
|
+
if (!o.dryRun)
|
|
2703
|
+
await writeFile6(file, doc.toString());
|
|
2704
|
+
}
|
|
2705
|
+
return out;
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// packages/adopt/dist/federation.js
|
|
2709
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
2710
|
+
import { readdir as readdir4, readFile as readFile7 } from "node:fs/promises";
|
|
2711
|
+
import { isAbsolute as isAbsolute3, join as join12, resolve as resolve6 } from "node:path";
|
|
2712
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2713
|
+
var REMOTE2 = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
2714
|
+
var SCP2 = /^[^/\\]+@[^:/\\]+:/;
|
|
2715
|
+
async function federatedSources(root, paths) {
|
|
2716
|
+
const p = paths ?? await resolveMetarepo(root);
|
|
2717
|
+
const op = await loadOrgPack(p.orgPackDir);
|
|
2718
|
+
const out = [];
|
|
2719
|
+
for (const a of op.sources.absorbed) {
|
|
2720
|
+
if (a.format !== "atticus" || a.mode !== "link")
|
|
2721
|
+
continue;
|
|
2722
|
+
const local = a.url.startsWith("file://") ? fileURLToPath2(a.url) : REMOTE2.test(a.url) || SCP2.test(a.url) ? void 0 : isAbsolute3(a.url) ? a.url : resolve6(root, a.url);
|
|
2723
|
+
const copy = join12(p.stateDir, "absorbed", a.id);
|
|
2724
|
+
if (local && await isDir(local) && !await isDir(join12(local, "objects")))
|
|
2725
|
+
out.push({ id: a.id, dir: local });
|
|
2726
|
+
else if (await isDir(copy))
|
|
2727
|
+
out.push({ id: a.id, dir: copy });
|
|
2728
|
+
}
|
|
2729
|
+
return out;
|
|
2730
|
+
}
|
|
2731
|
+
async function federatedChanges(sources) {
|
|
2732
|
+
const out = [];
|
|
2733
|
+
for (const s of sources) {
|
|
2734
|
+
const orgPack = await isDir(join12(s.dir, "org-pack")) ? join12(s.dir, "org-pack") : join12(s.dir, ".atticus", "org-pack");
|
|
2735
|
+
const layout = await readLayout(orgPack);
|
|
2736
|
+
const changes = join12(s.dir, ...layout.openspec.split("/"), "changes");
|
|
2737
|
+
let names;
|
|
2738
|
+
try {
|
|
2739
|
+
names = (await readdir4(changes, { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== "archive").map((d) => d.name).sort();
|
|
2740
|
+
} catch {
|
|
2741
|
+
continue;
|
|
2742
|
+
}
|
|
2743
|
+
for (const n of names) {
|
|
2744
|
+
let text2;
|
|
2745
|
+
try {
|
|
2746
|
+
text2 = await readFile7(join12(changes, n, "change.yaml"), "utf8");
|
|
2747
|
+
} catch {
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2750
|
+
const r = ChangeLifecycle.safeParse((0, import_yaml9.parse)(text2));
|
|
2751
|
+
if (r.success && r.data.state !== "archived")
|
|
2752
|
+
out.push({ origin: s.id, change: r.data, dir: s.dir });
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
return out;
|
|
2756
|
+
}
|
|
2757
|
+
var both = (a, b) => a.filter((x) => b.includes(x));
|
|
2758
|
+
async function federationConflicts(root, o = {}) {
|
|
2759
|
+
const p = o.paths ?? await resolveMetarepo(root);
|
|
2760
|
+
const sources = o.sources ?? await federatedSources(root, p);
|
|
2761
|
+
if (!sources.length)
|
|
2762
|
+
return [];
|
|
2763
|
+
const base = await loadChangeContext(root);
|
|
2764
|
+
const ctx = {
|
|
2765
|
+
...base,
|
|
2766
|
+
orgPack: await loadOrgPack(p.orgPackDir),
|
|
2767
|
+
records: o.records ?? (await readCatalog(join12(p.catalogDir, "generated"), { cacheRoot: root })).records
|
|
2768
|
+
};
|
|
2769
|
+
const ours = (await changeStore(root).list()).filter((c) => c.state !== "archived");
|
|
2770
|
+
const theirs = await federatedChanges(sources);
|
|
2771
|
+
const mine = await Promise.all(ours.map(async (c) => [c.id, await changeFootprint(ctx, c)]));
|
|
2772
|
+
const out = [];
|
|
2773
|
+
for (const t of theirs) {
|
|
2774
|
+
const fp = await changeFootprint({ ...ctx, root: t.dir }, t.change);
|
|
2775
|
+
for (const [id, m] of mine) {
|
|
2776
|
+
const overlap = {
|
|
2777
|
+
systems: both(m.systems, fp.systems),
|
|
2778
|
+
interfaces: both(m.interfaces.map((x) => x.id), fp.interfaces.map((x) => x.id)),
|
|
2779
|
+
dataStores: both(m.dataStores, fp.dataStores),
|
|
2780
|
+
tables: both(m.tables, fp.tables),
|
|
2781
|
+
records: both(m.records, fp.records),
|
|
2782
|
+
repos: both(m.repos, fp.repos),
|
|
2783
|
+
// Flows both change (ADR 0124 addendum B-change).
|
|
2784
|
+
flows: both(m.flows.filter((x) => x.change !== "read").map((x) => x.id), fp.flows.filter((x) => x.change !== "read").map((x) => x.id))
|
|
2785
|
+
};
|
|
2786
|
+
if (Object.values(overlap).some((v) => v.length > 0))
|
|
2787
|
+
out.push({ changes: [id, `${t.origin}:${t.change.id}`], overlap });
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
return out.sort((a, b) => a.changes.join().localeCompare(b.changes.join()));
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
// packages/adopt/dist/zero-install.js
|
|
2794
|
+
import { join as join13 } from "node:path";
|
|
2795
|
+
function relocate(text2, orgPackRel) {
|
|
2796
|
+
if (orgPackRel === "org-pack")
|
|
2797
|
+
return text2;
|
|
2798
|
+
return text2.replace(/(^|[^\w./-])org-pack\//g, `$1${orgPackRel}/`);
|
|
2799
|
+
}
|
|
2800
|
+
async function renderZeroInstall(paths) {
|
|
2801
|
+
const orgPack = await loadOrgPack(paths.orgPackDir);
|
|
2802
|
+
const { commands } = await loadOrgCommands(paths.orgPackDir);
|
|
2803
|
+
return renderAssistantFiles({ orgPack, commands, profile: { kind: "metarepo" } }).map((f) => ({
|
|
2804
|
+
...f,
|
|
2805
|
+
content: relocate(f.content, paths.orgPackRel),
|
|
2806
|
+
source: relocate(f.source, paths.orgPackRel)
|
|
2807
|
+
}));
|
|
2808
|
+
}
|
|
2809
|
+
async function zeroInstallFiles(root, o = {}) {
|
|
2810
|
+
const paths = await resolveMetarepo(root);
|
|
2811
|
+
const files = await renderZeroInstall(paths);
|
|
2812
|
+
return { ...await writeAssistantFiles(root, files, o), files };
|
|
2813
|
+
}
|
|
2814
|
+
function referencedPaths(text2, paths) {
|
|
2815
|
+
const roots = [
|
|
2816
|
+
paths.orgPackRel,
|
|
2817
|
+
paths.layout.catalog,
|
|
2818
|
+
paths.layout.openspec,
|
|
2819
|
+
".specify",
|
|
2820
|
+
".atticus"
|
|
2821
|
+
];
|
|
2822
|
+
const out = /* @__PURE__ */ new Set();
|
|
2823
|
+
for (const m of text2.matchAll(/`([^`\n]+)`/g)) {
|
|
2824
|
+
let t = m[1];
|
|
2825
|
+
if (/\s/.test(t.replace(/<[^>]*>/g, "")))
|
|
2826
|
+
continue;
|
|
2827
|
+
if (!roots.some((r) => t === r || t === `${r}/` || t.startsWith(`${r}/`)))
|
|
2828
|
+
continue;
|
|
2829
|
+
const cut = t.search(/[<*{$]/);
|
|
2830
|
+
if (cut >= 0)
|
|
2831
|
+
t = t.slice(0, t.lastIndexOf("/", cut) + 1);
|
|
2832
|
+
out.add(t.replace(/\/$/, ""));
|
|
2833
|
+
}
|
|
2834
|
+
return [...out].sort();
|
|
2835
|
+
}
|
|
2836
|
+
async function checkZeroInstall(root, files) {
|
|
2837
|
+
const paths = await resolveMetarepo(root);
|
|
2838
|
+
const problems = [];
|
|
2839
|
+
for (const f of files)
|
|
2840
|
+
for (const ref of referencedPaths(f.content, paths)) {
|
|
2841
|
+
const full = join13(root, ...ref.split("/"));
|
|
2842
|
+
if (!await exists(full) && !await isDir(full))
|
|
2843
|
+
problems.push({ file: f.path, missing: ref });
|
|
2844
|
+
}
|
|
2845
|
+
return problems;
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
export {
|
|
2849
|
+
AdoptError,
|
|
2850
|
+
toPosix,
|
|
2851
|
+
exists,
|
|
2852
|
+
isDir,
|
|
2853
|
+
readText,
|
|
2854
|
+
sha256,
|
|
2855
|
+
WALK_SKIP,
|
|
2856
|
+
listFiles,
|
|
2857
|
+
listDirs,
|
|
2858
|
+
hashTree,
|
|
2859
|
+
writeNew,
|
|
2860
|
+
copyDir,
|
|
2861
|
+
slug,
|
|
2862
|
+
frontMatter,
|
|
2863
|
+
ATTICUS_DIR,
|
|
2864
|
+
ORG_PACK_DIR,
|
|
2865
|
+
STATE_FOLDERS,
|
|
2866
|
+
isLocalState,
|
|
2867
|
+
readLayout,
|
|
2868
|
+
resolveMetarepo,
|
|
2869
|
+
orgPackDirOf,
|
|
2870
|
+
pathsFor,
|
|
2871
|
+
inPlacePath,
|
|
2872
|
+
REPO_TYPES,
|
|
2873
|
+
typeFromDescription,
|
|
2874
|
+
typeFromFrameworks,
|
|
2875
|
+
detectRepoType,
|
|
2876
|
+
UNASSIGNED,
|
|
2877
|
+
MockRepoListing,
|
|
2878
|
+
reposDiscover,
|
|
2879
|
+
CHECKLISTS_README,
|
|
2880
|
+
STARTER_CHECKLISTS_README,
|
|
2881
|
+
writeStartingPack,
|
|
2882
|
+
ATTICUS_FOLDER_IGNORE,
|
|
2883
|
+
init,
|
|
2884
|
+
SNAPSHOT_FILE,
|
|
2885
|
+
ADOPTED_FILE,
|
|
2886
|
+
detect,
|
|
2887
|
+
takeSnapshot,
|
|
2888
|
+
adopt,
|
|
2889
|
+
unadopt,
|
|
2890
|
+
NOT_TRANSLATABLE,
|
|
2891
|
+
translateCriticItem,
|
|
2892
|
+
markdownItems,
|
|
2893
|
+
yamlItems,
|
|
2894
|
+
packFor,
|
|
2895
|
+
parseConstitution,
|
|
2896
|
+
changeTemplateFrom,
|
|
2897
|
+
openSpecFrom,
|
|
2898
|
+
bodyKey,
|
|
2899
|
+
promptToCommand,
|
|
2900
|
+
toSkill,
|
|
2901
|
+
schemaKind,
|
|
2902
|
+
schemaReference,
|
|
2903
|
+
reference,
|
|
2904
|
+
typespecOutline,
|
|
2905
|
+
typespecReference,
|
|
2906
|
+
openSource,
|
|
2907
|
+
absorb,
|
|
2908
|
+
summarise,
|
|
2909
|
+
refreshAbsorbed,
|
|
2910
|
+
refreshAllAbsorbed,
|
|
2911
|
+
acceptAbsorbed,
|
|
2912
|
+
federatedSources,
|
|
2913
|
+
federatedChanges,
|
|
2914
|
+
federationConflicts,
|
|
2915
|
+
renderZeroInstall,
|
|
2916
|
+
zeroInstallFiles,
|
|
2917
|
+
referencedPaths,
|
|
2918
|
+
checkZeroInstall
|
|
2919
|
+
};
|