@joshuafolkken/kit 1.3041.0 → 1.3092.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/workflow-commands/backlogrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/fullrun-steps.md +3 -3
- package/.claude/skills/workflow-commands/halfrun.md +3 -2
- package/.claude/skills/workflow-commands/kickoff.md +9 -2
- package/.claude/skills/workflow-commands/prerequisite.md +2 -0
- package/.claude/skills/workflow-commands/progress-watcher.md +10 -8
- package/.claude/skills/workflow-commands/retrospective.md +2 -1
- package/.claude/skills/workflow-commands/split-assessment.md +4 -0
- package/.github/workflows/ci.yml +0 -20
- package/.github/workflows/pr-classification.yml +0 -5
- package/.github/workflows/publish.yml +0 -1
- package/.github/workflows/sonar-qube.yml +0 -5
- package/README.md +2 -0
- package/SECURITY.md +2 -1
- package/dist/.inputs-verified.json +1 -1
- package/dist/commands/adopt.js +29 -20
- package/dist/commands/auto-ok-judge-cli.js +43 -33
- package/dist/commands/auto-ok-main.js +73 -74
- package/dist/commands/backlog-budget-main.js +5 -6
- package/dist/commands/backlog-drive-cli.js +286 -305
- package/dist/commands/backlog-next-main.js +111 -108
- package/dist/commands/backlog-offer-main.js +148 -112
- package/dist/commands/backlog-plan-cli.js +123 -120
- package/dist/commands/backlog-triage-cli.js +131 -128
- package/dist/commands/backlogrun-cli.js +273 -264
- package/dist/commands/behavior-cli.js +23 -26
- package/dist/commands/bytes-command.js +56 -59
- package/dist/commands/cases-cli.js +4 -5
- package/dist/commands/check-commit-message-main.js +3 -4
- package/dist/commands/check-related.js +125 -119
- package/dist/commands/chunk-26JL4UU4.js +365 -0
- package/dist/commands/chunk-2BIVHPCA.js +85 -0
- package/dist/commands/chunk-2CKJODCK.js +245 -0
- package/dist/commands/chunk-2E6PSONB.js +46 -0
- package/dist/commands/chunk-2GA4775H.js +77 -0
- package/dist/commands/chunk-2O64H5UJ.js +462 -0
- package/dist/commands/chunk-2OEXY4GM.js +252 -0
- package/dist/commands/chunk-2OMIHPPE.js +51 -0
- package/dist/commands/chunk-2ONSHOAY.js +46 -0
- package/dist/commands/chunk-2Q7MGFYB.js +1208 -0
- package/dist/commands/chunk-2SA3W4JF.js +41 -0
- package/dist/commands/chunk-2VY7CJ2R.js +212 -0
- package/dist/commands/chunk-2WRDAQPJ.js +74 -0
- package/dist/commands/chunk-2XLR7R6E.js +541 -0
- package/dist/commands/chunk-2XZF3YFY.js +47 -0
- package/dist/commands/chunk-33TAOZUE.js +29 -0
- package/dist/commands/chunk-374XGAWD.js +158 -0
- package/dist/commands/chunk-3CJNQU7P.js +179 -0
- package/dist/commands/chunk-3D5T6KMW.js +59 -0
- package/dist/commands/chunk-3EW5LTN3.js +114 -0
- package/dist/commands/chunk-3F3RNIG2.js +128 -0
- package/dist/commands/chunk-3FLTEJZY.js +74 -0
- package/dist/commands/chunk-3JWPTRNI.js +60 -0
- package/dist/commands/chunk-3MQTOUXT.js +94 -0
- package/dist/commands/chunk-3PCUERXE.js +19 -0
- package/dist/commands/chunk-3U6A5WON.js +173 -0
- package/dist/commands/chunk-3YT3RCSI.js +140 -0
- package/dist/commands/chunk-43DU7DYZ.js +540 -0
- package/dist/commands/chunk-43H725LG.js +132 -0
- package/dist/commands/chunk-46STCE6Q.js +48 -0
- package/dist/commands/chunk-46WEC6HZ.js +82 -0
- package/dist/commands/chunk-47J4KOZP.js +46 -0
- package/dist/commands/chunk-47W7PF4T.js +22 -0
- package/dist/commands/chunk-4F4KARQN.js +229 -0
- package/dist/commands/chunk-4GUGC4IW.js +338 -0
- package/dist/commands/chunk-4HB7NWXP.js +263 -0
- package/dist/commands/chunk-4HOTJAO6.js +89 -0
- package/dist/commands/chunk-4I5AA4P5.js +237 -0
- package/dist/commands/chunk-4IDINPBE.js +525 -0
- package/dist/commands/chunk-4K7KLR7L.js +69 -0
- package/dist/commands/chunk-4MGTNY2L.js +347 -0
- package/dist/commands/chunk-4NSIW3UB.js +104 -0
- package/dist/commands/chunk-4YTKV6WV.js +37 -0
- package/dist/commands/chunk-5ARNFMQK.js +1263 -0
- package/dist/commands/chunk-5AZTSESD.js +114 -0
- package/dist/commands/chunk-5FUI2MLY.js +89 -0
- package/dist/commands/chunk-5GI5GLC4.js +214 -0
- package/dist/commands/chunk-5IHEBIPD.js +333 -0
- package/dist/commands/chunk-5KAW5GPF.js +44 -0
- package/dist/commands/chunk-5KSJEWXR.js +77 -0
- package/dist/commands/chunk-5OF2B4BF.js +35 -0
- package/dist/commands/chunk-5PLJ7R3D.js +126 -0
- package/dist/commands/chunk-5PPWXOCF.js +64 -0
- package/dist/commands/chunk-5VQCL33M.js +46 -0
- package/dist/commands/chunk-5Y7FOJLX.js +186 -0
- package/dist/commands/chunk-5YTEMF4O.js +110 -0
- package/dist/commands/chunk-62NYXZCC.js +241 -0
- package/dist/commands/chunk-6KUA7MRG.js +64 -0
- package/dist/commands/chunk-6LDEZFYR.js +88 -0
- package/dist/commands/chunk-6NXIZMIK.js +122 -0
- package/dist/commands/chunk-6O45F6YI.js +86 -0
- package/dist/commands/chunk-6OHJBQDE.js +184 -0
- package/dist/commands/chunk-6WDCCG3M.js +1249 -0
- package/dist/commands/chunk-6ZHXEQAI.js +715 -0
- package/dist/commands/chunk-736NECRD.js +16 -0
- package/dist/commands/chunk-77LEKVEE.js +32 -0
- package/dist/commands/chunk-7CX5VYOL.js +50 -0
- package/dist/commands/chunk-7DGHFBU7.js +247 -0
- package/dist/commands/chunk-7E43YW53.js +128 -0
- package/dist/commands/chunk-7EAYFMDN.js +235 -0
- package/dist/commands/chunk-7HP3FQGQ.js +51 -0
- package/dist/commands/chunk-7JOOMFDI.js +155 -0
- package/dist/commands/chunk-7OEBSPPU.js +321 -0
- package/dist/commands/chunk-A3JYZWBS.js +803 -0
- package/dist/commands/chunk-A3LCN4KA.js +84 -0
- package/dist/commands/chunk-ABLXBG6C.js +98 -0
- package/dist/commands/chunk-AN7BFVHU.js +244 -0
- package/dist/commands/chunk-AQCVEQSM.js +99 -0
- package/dist/commands/chunk-AT46EY7H.js +393 -0
- package/dist/commands/chunk-AU563VKH.js +75 -0
- package/dist/commands/chunk-AUTD5FV2.js +1077 -0
- package/dist/commands/chunk-AV26CMY6.js +304 -0
- package/dist/commands/chunk-B2PJEV3S.js +246 -0
- package/dist/commands/chunk-B545ENGY.js +214 -0
- package/dist/commands/chunk-B5HP6T4L.js +170 -0
- package/dist/commands/chunk-B5XYAWNN.js +37 -0
- package/dist/commands/chunk-B73IIRMS.js +207 -0
- package/dist/commands/chunk-BAEDHPXW.js +160 -0
- package/dist/commands/chunk-BDSG6JKH.js +977 -0
- package/dist/commands/chunk-BE6F4YK2.js +769 -0
- package/dist/commands/chunk-BFKTUD3V.js +33 -0
- package/dist/commands/chunk-BLRXHK2V.js +17 -0
- package/dist/commands/chunk-BLUAO4QW.js +173 -0
- package/dist/commands/chunk-BML6MEUB.js +326 -0
- package/dist/commands/chunk-BPDNGJXY.js +63 -0
- package/dist/commands/chunk-BQLKWGGC.js +118 -0
- package/dist/commands/chunk-BRMJVCP7.js +167 -0
- package/dist/commands/chunk-BRWR5YKU.js +268 -0
- package/dist/commands/chunk-BUZAYEBD.js +319 -0
- package/dist/commands/chunk-BVUL57IX.js +139 -0
- package/dist/commands/chunk-CC4BIGCA.js +20 -0
- package/dist/commands/chunk-CCHJZEF5.js +4220 -0
- package/dist/commands/chunk-CDK7QZWG.js +44 -0
- package/dist/commands/chunk-CDYFUCDC.js +72 -0
- package/dist/commands/chunk-CEADAQIL.js +170 -0
- package/dist/commands/chunk-CNL3SLM6.js +116 -0
- package/dist/commands/chunk-CNQ7D2GZ.js +104 -0
- package/dist/commands/chunk-CTXJVMHW.js +48 -0
- package/dist/commands/chunk-CUWT6UQO.js +13 -0
- package/dist/commands/chunk-CVRDJCOE.js +125 -0
- package/dist/commands/chunk-D6CF5QGM.js +80 -0
- package/dist/commands/chunk-DACNQAXC.js +20 -0
- package/dist/commands/chunk-DDOS32JU.js +74 -0
- package/dist/commands/chunk-DDZVMXZD.js +481 -0
- package/dist/commands/chunk-DF6QD4AF.js +52 -0
- package/dist/commands/chunk-DFEXNB44.js +159 -0
- package/dist/commands/chunk-DHDKG5HZ.js +151 -0
- package/dist/commands/chunk-DHJRFBKO.js +223 -0
- package/dist/commands/chunk-DKDWOE3C.js +78 -0
- package/dist/commands/chunk-DKO5VTEH.js +65 -0
- package/dist/commands/chunk-DLHH3MLS.js +85 -0
- package/dist/commands/chunk-DOYNK72G.js +81 -0
- package/dist/commands/chunk-DS53UUDH.js +679 -0
- package/dist/commands/chunk-DUA4E4ES.js +230 -0
- package/dist/commands/chunk-DURHGVRC.js +33 -0
- package/dist/commands/chunk-DVCMQMRN.js +191 -0
- package/dist/commands/chunk-E3YKMCCJ.js +108 -0
- package/dist/commands/chunk-E4F2RTKC.js +272 -0
- package/dist/commands/chunk-E4PFCTXN.js +127 -0
- package/dist/commands/chunk-EAOTR2WL.js +24 -0
- package/dist/commands/chunk-EE4VOZJP.js +167 -0
- package/dist/commands/chunk-EHVEWJQ4.js +33 -0
- package/dist/commands/chunk-EMFSPPMH.js +98 -0
- package/dist/commands/chunk-EN65ZS3S.js +73 -0
- package/dist/commands/chunk-ES42XPAD.js +123 -0
- package/dist/commands/chunk-ESUY7KTZ.js +137 -0
- package/dist/commands/chunk-ET6IRQ3L.js +330 -0
- package/dist/commands/chunk-EWCUT7U2.js +1314 -0
- package/dist/commands/chunk-EWXXEGXL.js +149 -0
- package/dist/commands/chunk-EZYW54OQ.js +7569 -0
- package/dist/commands/chunk-F2RDEF2J.js +205 -0
- package/dist/commands/chunk-F2U46HAE.js +52 -0
- package/dist/commands/chunk-F2VYWU6Z.js +653 -0
- package/dist/commands/chunk-F4SGOTUF.js +36 -0
- package/dist/commands/chunk-F4YMLVWN.js +183 -0
- package/dist/commands/chunk-F5N2QIFD.js +213 -0
- package/dist/commands/chunk-F7B4ESZE.js +80 -0
- package/dist/commands/chunk-FJM2HWKW.js +299 -0
- package/dist/commands/chunk-FJMXUDEY.js +3985 -0
- package/dist/commands/chunk-FM7VVAK6.js +426 -0
- package/dist/commands/chunk-FQWRM5FC.js +67 -0
- package/dist/commands/chunk-FT33ZCSB.js +298 -0
- package/dist/commands/chunk-G2DNS74C.js +53 -0
- package/dist/commands/chunk-G75GWNNY.js +101 -0
- package/dist/commands/chunk-GA2YSPFH.js +32 -0
- package/dist/commands/chunk-GAVSK6P6.js +230 -0
- package/dist/commands/chunk-GB2BRQVL.js +300 -0
- package/dist/commands/chunk-GBMZXFFC.js +44 -0
- package/dist/commands/chunk-GDP7QXYE.js +76 -0
- package/dist/commands/chunk-GGWXF7RJ.js +70 -0
- package/dist/commands/chunk-GHOTKHI4.js +30 -0
- package/dist/commands/chunk-GMVN6ZP2.js +48 -0
- package/dist/commands/chunk-GNTTGRCX.js +133 -0
- package/dist/commands/chunk-GOY5JECO.js +130 -0
- package/dist/commands/chunk-GVDRRBHZ.js +507 -0
- package/dist/commands/chunk-GWTBIGYC.js +550 -0
- package/dist/commands/chunk-GZL6ISVM.js +72 -0
- package/dist/commands/chunk-HALHQILI.js +51 -0
- package/dist/commands/chunk-HAT7AFBF.js +85 -0
- package/dist/commands/chunk-HDVSZ6CZ.js +292 -0
- package/dist/commands/chunk-HE3GOMR7.js +88 -0
- package/dist/commands/chunk-HEFUC4ZW.js +65 -0
- package/dist/commands/chunk-HESWKELD.js +60 -0
- package/dist/commands/chunk-HNFBEB4L.js +77 -0
- package/dist/commands/chunk-HRN73FQJ.js +77 -0
- package/dist/commands/chunk-HYE3MBAN.js +113 -0
- package/dist/commands/chunk-IAB6LCJL.js +18 -0
- package/dist/commands/chunk-IAGENI2U.js +42 -0
- package/dist/commands/chunk-ICPVBHIX.js +48 -0
- package/dist/commands/chunk-IEKW3ON6.js +161 -0
- package/dist/commands/chunk-IOKOHB7P.js +109 -0
- package/dist/commands/chunk-IONGJJYM.js +179 -0
- package/dist/commands/chunk-IP23WOWP.js +126 -0
- package/dist/commands/chunk-IPS7EBE5.js +140 -0
- package/dist/commands/chunk-IXZZSST7.js +459 -0
- package/dist/commands/chunk-J4XHEOOW.js +156 -0
- package/dist/commands/chunk-JDHLHYJJ.js +208 -0
- package/dist/commands/chunk-JEYITIWB.js +423 -0
- package/dist/commands/chunk-JGX7JIW4.js +126 -0
- package/dist/commands/chunk-JHIGDFBS.js +59 -0
- package/dist/commands/chunk-JI2EGXZX.js +53 -0
- package/dist/commands/chunk-JNV3VRK2.js +47 -0
- package/dist/commands/chunk-JPMAW2VZ.js +91 -0
- package/dist/commands/chunk-JQEY6KDP.js +90 -0
- package/dist/commands/chunk-JSJDIEGZ.js +88 -0
- package/dist/commands/chunk-JT5ICRFF.js +44 -0
- package/dist/commands/chunk-JUCJ4Y42.js +77 -0
- package/dist/commands/chunk-K3OZBV3L.js +49 -0
- package/dist/commands/chunk-K5H3CLZZ.js +430 -0
- package/dist/commands/chunk-K6P5PISC.js +114 -0
- package/dist/commands/chunk-KD3SEAMV.js +402 -0
- package/dist/commands/chunk-KDCHY7XO.js +88 -0
- package/dist/commands/chunk-KDGX3JTV.js +51 -0
- package/dist/commands/chunk-KGSEWOKI.js +353 -0
- package/dist/commands/chunk-KHGDZQRG.js +271 -0
- package/dist/commands/chunk-KIS7JFNP.js +217 -0
- package/dist/commands/chunk-KKHD3C2P.js +114 -0
- package/dist/commands/chunk-KKIIY5I5.js +67 -0
- package/dist/commands/chunk-KN46UC77.js +114 -0
- package/dist/commands/chunk-KP2BVAS5.js +125 -0
- package/dist/commands/chunk-KPAJBJXG.js +63 -0
- package/dist/commands/chunk-KPMAJP6D.js +145 -0
- package/dist/commands/chunk-KR6WUAJM.js +122 -0
- package/dist/commands/chunk-KWLNK2NJ.js +391 -0
- package/dist/commands/chunk-L2CLCNXW.js +144 -0
- package/dist/commands/chunk-L7MDU6D7.js +155 -0
- package/dist/commands/chunk-L7VD4MLF.js +78 -0
- package/dist/commands/chunk-LBMAJDGN.js +124 -0
- package/dist/commands/chunk-LBVYFW3I.js +38 -0
- package/dist/commands/chunk-LE3M42CF.js +350 -0
- package/dist/commands/chunk-LIPUSINQ.js +140 -0
- package/dist/commands/chunk-LJVZWE3A.js +1885 -0
- package/dist/commands/chunk-LKSSQVDA.js +49 -0
- package/dist/commands/chunk-LL6G34MF.js +93 -0
- package/dist/commands/chunk-LNQOL2CL.js +321 -0
- package/dist/commands/chunk-LS7P324N.js +72 -0
- package/dist/commands/chunk-LU5EOGFQ.js +50 -0
- package/dist/commands/chunk-LX2ENXHZ.js +197 -0
- package/dist/commands/chunk-M32UPB2D.js +110 -0
- package/dist/commands/chunk-M3UGRNNB.js +103 -0
- package/dist/commands/chunk-M4WG74Q7.js +260 -0
- package/dist/commands/chunk-M5HA2ZCG.js +105 -0
- package/dist/commands/chunk-MDGPJOLK.js +351 -0
- package/dist/commands/chunk-MDKRTUE4.js +288 -0
- package/dist/commands/chunk-MFZVRWRS.js +34 -0
- package/dist/commands/chunk-MHXIJKGP.js +304 -0
- package/dist/commands/chunk-MK4CARBI.js +90 -0
- package/dist/commands/chunk-MLJDW7YN.js +64 -0
- package/dist/commands/chunk-MNLHYAJO.js +80 -0
- package/dist/commands/chunk-MOD3FG4V.js +419 -0
- package/dist/commands/chunk-MRAEJLXL.js +83 -0
- package/dist/commands/chunk-MRW2WYJS.js +68 -0
- package/dist/commands/chunk-MXPCEZL5.js +23 -0
- package/dist/commands/chunk-N2XH7QBY.js +103 -0
- package/dist/commands/chunk-N2ZF6T4E.js +332 -0
- package/dist/commands/chunk-N36NZMAL.js +79 -0
- package/dist/commands/chunk-N4EMFHQ6.js +114 -0
- package/dist/commands/chunk-N4P64UIH.js +211 -0
- package/dist/commands/chunk-NABSSY4P.js +204 -0
- package/dist/commands/chunk-NDQIJBRQ.js +100 -0
- package/dist/commands/chunk-NDR4PCVG.js +29 -0
- package/dist/commands/chunk-NEOCCN6D.js +302 -0
- package/dist/commands/chunk-NFKWFOFM.js +45 -0
- package/dist/commands/chunk-NFUPJBBG.js +516 -0
- package/dist/commands/chunk-NHEBKL7X.js +170 -0
- package/dist/commands/chunk-NHTEC5U4.js +99 -0
- package/dist/commands/chunk-NOSG3RUP.js +84 -0
- package/dist/commands/chunk-NOSODWWK.js +1132 -0
- package/dist/commands/chunk-NPXXYG6Z.js +580 -0
- package/dist/commands/chunk-NR2JDBPV.js +75 -0
- package/dist/commands/chunk-NRULNBYI.js +1153 -0
- package/dist/commands/chunk-NTGF5N3N.js +110 -0
- package/dist/commands/chunk-NTIVNQKF.js +73 -0
- package/dist/commands/chunk-NUCFIYZL.js +119 -0
- package/dist/commands/chunk-NVMV7BYX.js +88 -0
- package/dist/commands/chunk-NYWPTYGL.js +169 -0
- package/dist/commands/chunk-NZ45KOLL.js +104 -0
- package/dist/commands/chunk-O45BX4EL.js +39 -0
- package/dist/commands/chunk-O46T4ANI.js +229 -0
- package/dist/commands/chunk-O5JGV7NA.js +245 -0
- package/dist/commands/chunk-O7ESC4HL.js +566 -0
- package/dist/commands/chunk-OBYLXHCH.js +86 -0
- package/dist/commands/chunk-OKN63ZH5.js +109 -0
- package/dist/commands/chunk-ON4IKRGS.js +469 -0
- package/dist/commands/chunk-OT74RSQO.js +82 -0
- package/dist/commands/chunk-OVCVINYW.js +143 -0
- package/dist/commands/chunk-OZKLVTYI.js +342 -0
- package/dist/commands/chunk-P3LJ2Q4N.js +555 -0
- package/dist/commands/chunk-P43GWYR6.js +185 -0
- package/dist/commands/chunk-P6ASP2DY.js +132 -0
- package/dist/commands/chunk-PBAEJ5PT.js +38 -0
- package/dist/commands/chunk-PCZROH3R.js +100 -0
- package/dist/commands/chunk-PF3PMT2Q.js +46 -0
- package/dist/commands/chunk-PILH72AV.js +170 -0
- package/dist/commands/chunk-PJYTUUTH.js +34 -0
- package/dist/commands/chunk-PKXTISWR.js +58 -0
- package/dist/commands/chunk-PSGLUBSB.js +74 -0
- package/dist/commands/chunk-PUQA7F6Y.js +186 -0
- package/dist/commands/chunk-PVRDJU2B.js +126 -0
- package/dist/commands/chunk-Q3HM2BEH.js +45 -0
- package/dist/commands/chunk-Q42NF2TL.js +47 -0
- package/dist/commands/chunk-Q4ULMZAS.js +106 -0
- package/dist/commands/chunk-QCRUPRQ2.js +207 -0
- package/dist/commands/chunk-QFIKPGMC.js +280 -0
- package/dist/commands/chunk-QSBYULSS.js +100 -0
- package/dist/commands/chunk-QSJCZATC.js +18 -0
- package/dist/commands/chunk-QTJPCXEW.js +23 -0
- package/dist/commands/chunk-QTQAI2M2.js +179 -0
- package/dist/commands/chunk-QTVVFV4Q.js +127 -0
- package/dist/commands/chunk-QX2EMUXV.js +102 -0
- package/dist/commands/chunk-QXBOECGL.js +272 -0
- package/dist/commands/chunk-QXCG3SG4.js +88 -0
- package/dist/commands/chunk-QZVMGAWV.js +307 -0
- package/dist/commands/chunk-R36VZIKU.js +53 -0
- package/dist/commands/chunk-R3F7F3TX.js +94 -0
- package/dist/commands/chunk-R5AABJZJ.js +1062 -0
- package/dist/commands/chunk-R7PYR4HW.js +389 -0
- package/dist/commands/chunk-RDUFVQVJ.js +997 -0
- package/dist/commands/chunk-RESNXNDG.js +54 -0
- package/dist/commands/chunk-RFIOZTXY.js +305 -0
- package/dist/commands/chunk-RFWB2KAV.js +102 -0
- package/dist/commands/chunk-RFXON3FK.js +25 -0
- package/dist/commands/chunk-RGEB5L37.js +66 -0
- package/dist/commands/chunk-RGRLLF2J.js +119 -0
- package/dist/commands/chunk-RLRIMYXM.js +178 -0
- package/dist/commands/chunk-RNB3DQOZ.js +70 -0
- package/dist/commands/chunk-RO4QEZJO.js +172 -0
- package/dist/commands/chunk-RS5HBJY6.js +161 -0
- package/dist/commands/chunk-RV7EQIE4.js +202 -0
- package/dist/commands/chunk-RWPCK3CO.js +41 -0
- package/dist/commands/chunk-RWYSVFIA.js +250 -0
- package/dist/commands/chunk-RYKK75YT.js +289 -0
- package/dist/commands/chunk-S4JE63QE.js +704 -0
- package/dist/commands/chunk-S4OZJIXD.js +134 -0
- package/dist/commands/chunk-SD4EX75H.js +177 -0
- package/dist/commands/chunk-SJITWFNY.js +480 -0
- package/dist/commands/chunk-SKGDMAIA.js +33 -0
- package/dist/commands/chunk-SNSZ25XZ.js +393 -0
- package/dist/commands/chunk-SQZZHK6X.js +119 -0
- package/dist/commands/chunk-SRTEE34O.js +93 -0
- package/dist/commands/chunk-SYTCTLMD.js +285 -0
- package/dist/commands/chunk-T6JUDQMW.js +181 -0
- package/dist/commands/chunk-T6OUNP7E.js +426 -0
- package/dist/commands/chunk-TEXUEBGE.js +208 -0
- package/dist/commands/chunk-TFDVTU6O.js +910 -0
- package/dist/commands/chunk-TFYYXETQ.js +472 -0
- package/dist/commands/chunk-TMFKVJUW.js +121 -0
- package/dist/commands/chunk-TNJU4CXD.js +32 -0
- package/dist/commands/chunk-TRHWBUWE.js +61 -0
- package/dist/commands/chunk-TROCFVCU.js +45 -0
- package/dist/commands/chunk-TTTZZN7F.js +269 -0
- package/dist/commands/chunk-TUBBESHU.js +85 -0
- package/dist/commands/chunk-TVZIFZZN.js +62 -0
- package/dist/commands/chunk-TXRWAL63.js +144 -0
- package/dist/commands/chunk-U46YTYJQ.js +92 -0
- package/dist/commands/chunk-U5EO4QEV.js +48 -0
- package/dist/commands/chunk-UCO333EX.js +47 -0
- package/dist/commands/chunk-UIXQ4IYT.js +445 -0
- package/dist/commands/chunk-UJGNYPMD.js +716 -0
- package/dist/commands/chunk-URZ4RDC4.js +220 -0
- package/dist/commands/chunk-UUH3DE77.js +57 -0
- package/dist/commands/chunk-UVY5ODCM.js +60 -0
- package/dist/commands/chunk-UWMB6EOU.js +45 -0
- package/dist/commands/chunk-UX3HCPLX.js +449 -0
- package/dist/commands/chunk-UY62WJV6.js +120 -0
- package/dist/commands/chunk-V2T5DP3B.js +224 -0
- package/dist/commands/chunk-VAHQ4AFM.js +585 -0
- package/dist/commands/chunk-VCWNMRH7.js +71 -0
- package/dist/commands/chunk-VHLDQ3PH.js +30 -0
- package/dist/commands/chunk-VL5I2IOD.js +332 -0
- package/dist/commands/chunk-VLN6TSPJ.js +139 -0
- package/dist/commands/chunk-VMG3QB6C.js +81 -0
- package/dist/commands/chunk-VO25PMGS.js +58 -0
- package/dist/commands/chunk-VTLWODJ2.js +133 -0
- package/dist/commands/chunk-VXE3V3OC.js +335 -0
- package/dist/commands/chunk-VY7ZJMDT.js +469 -0
- package/dist/commands/chunk-W3MDLQZF.js +137 -0
- package/dist/commands/chunk-W4BR6QNU.js +1241 -0
- package/dist/commands/chunk-WCYRNNKF.js +76 -0
- package/dist/commands/chunk-WEUUJIWQ.js +84 -0
- package/dist/commands/chunk-WNBJK6QE.js +84 -0
- package/dist/commands/chunk-WR5PKCQW.js +61 -0
- package/dist/commands/chunk-WR73HTRG.js +121 -0
- package/dist/commands/chunk-WRNMUQTD.js +95 -0
- package/dist/commands/chunk-WROJTXWZ.js +138 -0
- package/dist/commands/chunk-WTIPX5NG.js +43 -0
- package/dist/commands/chunk-WWLCQREY.js +225 -0
- package/dist/commands/chunk-WXLW7S4M.js +71 -0
- package/dist/commands/chunk-WXXLJQBO.js +67 -0
- package/dist/commands/chunk-X4LYH6Q4.js +56 -0
- package/dist/commands/chunk-XAEL24EG.js +66 -0
- package/dist/commands/chunk-XCCT6GKA.js +74 -0
- package/dist/commands/chunk-XDR2BRDL.js +216 -0
- package/dist/commands/chunk-XRQMVAB4.js +85 -0
- package/dist/commands/chunk-XSKJAG72.js +91 -0
- package/dist/commands/chunk-XTJA34ND.js +492 -0
- package/dist/commands/chunk-XUXCFPDK.js +218 -0
- package/dist/commands/chunk-XXPDFJRI.js +48 -0
- package/dist/commands/chunk-Y6OGDG5I.js +49 -0
- package/dist/commands/chunk-YEAE46TS.js +98 -0
- package/dist/commands/chunk-YEQFAEVW.js +112 -0
- package/dist/commands/chunk-YJZAD4Z5.js +103 -0
- package/dist/commands/chunk-YMAEYY5R.js +278 -0
- package/dist/commands/chunk-YNCF66RK.js +1052 -0
- package/dist/commands/chunk-YOA25SII.js +210 -0
- package/dist/commands/chunk-YOZTZMAT.js +38 -0
- package/dist/commands/chunk-YSGRHVZG.js +897 -0
- package/dist/commands/chunk-YSLGWRQN.js +646 -0
- package/dist/commands/chunk-YTXCLGGL.js +168 -0
- package/dist/commands/chunk-YVHJN2HS.js +74 -0
- package/dist/commands/chunk-YY7XVVSB.js +43 -0
- package/dist/commands/chunk-Z5GZV4DV.js +16 -0
- package/dist/commands/chunk-Z6QLFPFB.js +907 -0
- package/dist/commands/chunk-ZB52ZIEA.js +1629 -0
- package/dist/commands/chunk-ZCD4AV2P.js +73 -0
- package/dist/commands/chunk-ZCHZU6NX.js +799 -0
- package/dist/commands/chunk-ZEH2B3DF.js +239 -0
- package/dist/commands/chunk-ZEUKX3RZ.js +245 -0
- package/dist/commands/chunk-ZGANVZCA.js +92 -0
- package/dist/commands/chunk-ZGBXUY7R.js +198 -0
- package/dist/commands/chunk-ZGEUWHFF.js +297 -0
- package/dist/commands/chunk-ZHSNQ2F7.js +161 -0
- package/dist/commands/chunk-ZIIUBGON.js +773 -0
- package/dist/commands/chunk-ZJ5S4VFD.js +258 -0
- package/dist/commands/chunk-ZNIIA275.js +59 -0
- package/dist/commands/chunk-ZV6BZZJ2.js +271 -0
- package/dist/commands/ci-timing-cli.js +22 -3
- package/dist/commands/code-symbol-cli.js +21 -7
- package/dist/commands/codex-hook-adapter.js +124 -119
- package/dist/commands/command-derive.js +9 -11
- package/dist/commands/cost-audit-main.js +118 -119
- package/dist/commands/cost-main.js +5 -7
- package/dist/commands/cspell-cached.js +55 -57
- package/dist/commands/defect-origin-main.js +42 -37
- package/dist/commands/defect-report-main.js +33 -25
- package/dist/commands/diff-summary-cli.js +3 -4
- package/dist/commands/disposition-cli.js +117 -118
- package/dist/commands/doctor.js +24 -11
- package/dist/commands/document-read-cli.js +50 -53
- package/dist/commands/document-section-main.js +50 -53
- package/dist/commands/dogfood-commit-cli.js +19 -4
- package/dist/commands/effect-audit-cli.js +40 -41
- package/dist/commands/epic-audit-main.js +88 -86
- package/dist/commands/epic-bundle-main.js +122 -119
- package/dist/commands/epic-check.js +37 -31
- package/dist/commands/epic-main.js +129 -126
- package/dist/commands/epic-next-main.js +85 -83
- package/dist/commands/eval-run.js +6 -7
- package/dist/commands/format-edited-cli.js +71 -46
- package/dist/commands/format.js +41 -56
- package/dist/commands/fresh-machine-cli.js +24 -7
- package/dist/commands/gh-usage-cli.js +22 -22
- package/dist/commands/git-followup-workflow.js +158 -158
- package/dist/commands/git-gh-checks-failed-main.js +23 -15
- package/dist/commands/git-workflow.js +52 -53
- package/dist/commands/github-release-cli.js +3 -3
- package/dist/commands/hook-ledger-cli.js +93 -94
- package/dist/commands/init-main.js +40 -40
- package/dist/commands/inputs.json +2042 -2002
- package/dist/commands/issue-ack-cli.js +28 -21
- package/dist/commands/issue-already-done-cli.js +107 -110
- package/dist/commands/issue-body-cli.js +71 -73
- package/dist/commands/issue-cite-main.js +35 -28
- package/dist/commands/issue-comment-main.js +36 -30
- package/dist/commands/issue-consolidate-cli.js +54 -54
- package/dist/commands/issue-file-main.js +274 -148
- package/dist/commands/issue-fold-existing-cli.js +126 -123
- package/dist/commands/issue-fold-main.js +57 -60
- package/dist/commands/issue-lint-main.js +59 -61
- package/dist/commands/issue-measured-cli.js +108 -110
- package/dist/commands/issue-plan-cli.js +156 -155
- package/dist/commands/issue-premise-unmet-cli.js +111 -113
- package/dist/commands/issue-priority-cli.js +38 -32
- package/dist/commands/issue-read-main.js +38 -33
- package/dist/commands/issue-release-class-cli.js +37 -31
- package/dist/commands/issue-release-main.js +35 -29
- package/dist/commands/issue-scout-main.js +123 -120
- package/dist/commands/issue-spill-main.js +149 -144
- package/dist/commands/issue-state-main.js +36 -31
- package/dist/commands/issue-tick-cli.js +37 -31
- package/dist/commands/josh-completion.js +21 -24
- package/dist/commands/lane-adopt-cli.js +53 -55
- package/dist/commands/lane-breakdown-cli.js +193 -172
- package/dist/commands/lane-cli.js +199 -180
- package/dist/commands/lane-launch-main.js +106 -56
- package/dist/commands/lane-limit-cli.js +119 -116
- package/dist/commands/lane-measure-cli.js +408 -245
- package/dist/commands/lane-pause-cli.js +51 -54
- package/dist/commands/lane-plan-cli.js +148 -147
- package/dist/commands/lane-resolve-cli.js +48 -50
- package/dist/commands/lane-restart-cli.js +185 -168
- package/dist/commands/lane-why-cli.js +121 -122
- package/dist/commands/lane-withdraw-cli.js +84 -86
- package/dist/commands/latest-corepack.js +3 -2
- package/dist/commands/latest-if-required-cli.js +12 -15
- package/dist/commands/latest-lane-guard-main.js +2 -3
- package/dist/commands/latest-scope-main.js +5 -7
- package/dist/commands/latest-update.js +3 -2
- package/dist/commands/lines-command.js +8 -10
- package/dist/commands/lint-parallel-main.js +65 -67
- package/dist/commands/lint-related.js +149 -133
- package/dist/commands/lint-rule.js +64 -66
- package/dist/commands/lockfile-policy.js +29 -32
- package/dist/commands/main-merge-main.js +23 -25
- package/dist/commands/main-sync-main.js +23 -24
- package/dist/commands/managed-config-scope-cli.js +11 -10
- package/dist/commands/measure-rerun-cli.js +31 -26
- package/dist/commands/metrics-command.js +97 -100
- package/dist/commands/observation-record-cli.js +24 -14
- package/dist/commands/observations-flush-cli.js +50 -52
- package/dist/commands/overrides-check.js +1 -1
- package/dist/commands/package-scout-cli.js +70 -73
- package/dist/commands/pin-verify-cli.js +21 -2
- package/dist/commands/plan-consumers-main.js +3 -4
- package/dist/commands/plan-scaffold-cli.js +145 -146
- package/dist/commands/pr-classification-main.js +22 -3
- package/dist/commands/pre-commit-lint.js +30 -34
- package/dist/commands/pre-commit-type-check.js +55 -55
- package/dist/commands/pre-push-unit.js +69 -70
- package/dist/commands/pretool-guard-cli.js +111 -113
- package/dist/commands/prevent-main-commit.js +2 -3
- package/dist/commands/project-profile-cli.js +24 -9
- package/dist/commands/propagate.js +31 -23
- package/dist/commands/prose-stale-cli.js +48 -42
- package/dist/commands/publishable-range-check.js +3 -3
- package/dist/commands/read-set-cli.js +58 -61
- package/dist/commands/refactor-scan-cli.js +8 -9
- package/dist/commands/release-cli.js +57 -57
- package/dist/commands/release-scope-main.js +41 -36
- package/dist/commands/rename-cli.js +64 -36
- package/dist/commands/repo-party-cli.js +3 -4
- package/dist/commands/report-template-cli.js +4 -5
- package/dist/commands/reserved-run.js +17 -20
- package/dist/commands/retrospective-cli.js +126 -127
- package/dist/commands/review-attest-cli.js +28 -20
- package/dist/commands/review-brief-main.js +184 -167
- package/dist/commands/review-findings-cli.js +23 -13
- package/dist/commands/review-pair-cli.js +180 -156
- package/dist/commands/review-record-main.js +171 -147
- package/dist/commands/review-round2-cli.js +89 -91
- package/dist/commands/review-stops-cli.js +51 -54
- package/dist/commands/review-submit-cli.js +184 -167
- package/dist/commands/rule-list.js +78 -80
- package/dist/commands/rule-value-cli.js +115 -116
- package/dist/commands/ruleset-check-cli.js +23 -6
- package/dist/commands/run-add-cli.js +115 -75
- package/dist/commands/run-board-main.js +172 -169
- package/dist/commands/run-carry-cli.js +182 -174
- package/dist/commands/run-chain-cli.js +184 -160
- package/dist/commands/run-cut-cli.js +206 -168
- package/dist/commands/run-ending-cli.js +51 -54
- package/dist/commands/run-entry-cli.js +230 -283
- package/dist/commands/run-event-cli.js +18 -21
- package/dist/commands/run-handoff-cli.js +75 -78
- package/dist/commands/run-handoff-lanes-cli.js +48 -51
- package/dist/commands/run-hold-main.js +105 -107
- package/dist/commands/run-liveness-cli.js +47 -49
- package/dist/commands/run-merge-main.js +159 -151
- package/dist/commands/run-next-cli.js +128 -129
- package/dist/commands/run-park-cli.js +137 -140
- package/dist/commands/run-pause-cli.js +190 -186
- package/dist/commands/run-pickup-cli.js +49 -52
- package/dist/commands/run-prep-main.js +124 -125
- package/dist/commands/run-prime-cli.js +175 -147
- package/dist/commands/run-progress-main.js +149 -146
- package/dist/commands/run-report-cli.js +60 -60
- package/dist/commands/run-review-cli.js +229 -214
- package/dist/commands/run-ship-cli.js +258 -252
- package/dist/commands/run-ship-evidence-cli.js +71 -72
- package/dist/commands/run-ship-lap-W66LTXJJ.js +28 -0
- package/dist/commands/run-ship-wait-main.js +83 -85
- package/dist/commands/run-split-cli.js +176 -172
- package/dist/commands/run-stale-main.js +72 -75
- package/dist/commands/run-status-cli.js +44 -44
- package/dist/commands/run-step-cli.js +213 -211
- package/dist/commands/run-tail-cli.js +23 -18
- package/dist/commands/run-tidy-main.js +67 -69
- package/dist/commands/run-wake-main.js +206 -187
- package/dist/commands/run-watcher-guard-cli.js +63 -66
- package/dist/commands/security-audit-main.js +21 -24
- package/dist/commands/security-audit-provision-main.js +9 -11
- package/dist/commands/selfcheck-cli.js +63 -57
- package/dist/commands/session-language-main.js +70 -73
- package/dist/commands/sonar-hotspots-cli.js +9 -8
- package/dist/commands/sonar-new-code-cli.js +3 -4
- package/dist/commands/split-assess-cli.js +55 -58
- package/dist/commands/split-child-recover-cli.js +38 -39
- package/dist/commands/start.js +64 -64
- package/dist/commands/stash-pop-main.js +4 -5
- package/dist/commands/stash-push-main.js +7 -9
- package/dist/commands/stop-guard.js +180 -176
- package/dist/commands/sync-main.js +30 -15
- package/dist/commands/telegram-test-main.js +29 -22
- package/dist/commands/test-declared.js +29 -15
- package/dist/commands/test-e2e-guard.js +54 -54
- package/dist/commands/test-pinned-main.js +57 -56
- package/dist/commands/test-reach-cli.js +40 -32
- package/dist/commands/test-red-main.js +12 -14
- package/dist/commands/test-related-Z6YY2GFH.js +113 -0
- package/dist/commands/test-related-main.js +89 -89
- package/dist/commands/test-unit-guard-main.js +37 -37
- package/dist/commands/time-cli.js +54 -57
- package/dist/commands/time-density-cli.js +26 -29
- package/dist/commands/tree-status-cli.js +6 -8
- package/dist/commands/type-check-command.js +26 -11
- package/dist/commands/ui-routes-cli.js +5 -6
- package/dist/commands/ui-shot-cli.js +1 -1
- package/dist/commands/uninstall.js +44 -43
- package/dist/commands/unused-members-KVKZDTXD.js +329 -0
- package/dist/commands/unused-members-cli.js +27 -12
- package/dist/commands/upstream-interrupt-main.js +159 -151
- package/dist/commands/verification-gate.js +85 -86
- package/dist/commands/version-check.js +25 -8
- package/dist/hooks/chunk-3SA3FIQC.js +6869 -0
- package/dist/hooks/chunk-46PTTMGE.js +1625 -0
- package/dist/hooks/chunk-5NIBRHWG.js +77 -0
- package/dist/hooks/chunk-76QKN2K3.js +258 -0
- package/dist/hooks/chunk-CHEKMGFP.js +321 -0
- package/dist/hooks/chunk-DCILEVDR.js +5173 -0
- package/dist/hooks/chunk-HVNGIM5W.js +5092 -0
- package/dist/hooks/chunk-L6IZZ2IL.js +11219 -0
- package/dist/hooks/chunk-UZXMFOVK.js +9170 -0
- package/dist/hooks/chunk-WCJ2GWYJ.js +1381 -0
- package/dist/hooks/chunk-YRKS5ZKE.js +1915 -0
- package/dist/hooks/codex-hook-adapter.js +11 -12
- package/dist/hooks/daemon.js +2140 -1387
- package/dist/hooks/format-edited.js +6 -6
- package/dist/hooks/inputs.json +512 -488
- package/dist/hooks/pretool-guard.js +8 -8
- package/dist/hooks/run-ship-lap-P5555IG7.js +8 -0
- package/dist/hooks/session-lang.js +10 -8
- package/dist/hooks/stop-guard.js +962 -669
- package/dist/inputs.json +73 -64
- package/dist/josh.js +1029 -577
- package/dist/registry-migrate.mjs +33938 -0
- package/dist/version/index.js +4251 -175
- package/package.json +4 -1
- package/prompts/collaboration-workflow/principles.md +4 -1
- package/prompts/collaboration-workflow/rule-delivery.md +1 -1
- package/scripts/agent/codex-launch-identity.ts +70 -0
- package/scripts/auto-ok/auto-ok-cli.ts +2 -0
- package/scripts/auto-ok/auto-ok-judge-write.ts +2 -0
- package/scripts/auto-ok/auto-ok-judge.ts +3 -1
- package/scripts/backlog/backlog-arrival.ts +1 -1
- package/scripts/backlog/backlog-budget.ts +5 -2
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-dispatch-bound.ts +17 -0
- package/scripts/backlog/backlog-drain-flush.ts +38 -3
- package/scripts/backlog/backlog-drive-cli.ts +1 -1
- package/scripts/backlog/backlog-drive-hold.ts +28 -5
- package/scripts/backlog/backlog-drive-launch.ts +37 -15
- package/scripts/backlog/backlog-drive-listing.ts +39 -23
- package/scripts/backlog/backlog-drive-retrospective-unit.ts +3 -2
- package/scripts/backlog/backlog-drive-split-unit.ts +1 -0
- package/scripts/backlog/backlog-hold-codes.ts +7 -0
- package/scripts/backlog/backlog-hold-text.ts +14 -1
- package/scripts/backlog/backlog-measure-close.ts +6 -0
- package/scripts/backlog/backlog-next-shared.ts +20 -2
- package/scripts/backlog/backlog-next.ts +24 -3
- package/scripts/backlog/backlog-offer-cli.ts +69 -16
- package/scripts/build/build-migration.ts +25 -0
- package/scripts/build/build.ts +2 -0
- package/scripts/epic/epic-classify.ts +6 -1
- package/scripts/epic/epic-live.ts +46 -0
- package/scripts/followup/followup-failed-tests.ts +71 -10
- package/scripts/followup/followup-job-rerun.ts +2 -2
- package/scripts/gate/change-scope-action.integration.test.ts +88 -0
- package/scripts/gate/change-scope-action.ts +2 -0
- package/scripts/gate/check-related-order.ts +6 -7
- package/scripts/gate/check-related.ts +18 -8
- package/scripts/gate/core-admission.ts +17 -30
- package/scripts/gate/core-budget-poll.ts +183 -0
- package/scripts/gate/core-budget-timing.ts +1 -1
- package/scripts/gate/core-budget.ts +24 -112
- package/scripts/gate/core-wait-record.ts +52 -7
- package/scripts/gate/machine-capacity.ts +6 -2
- package/scripts/gate/metrics-totals.ts +11 -0
- package/scripts/gate/path-glob.test.ts +18 -0
- package/scripts/gate/toolchain-inputs.consumer.test.ts +36 -0
- package/scripts/gh/fix-gh-packages-logic.ts +22 -15
- package/scripts/gh/gh-request-tally.ts +4 -0
- package/scripts/hooks/bash-revert.ts +79 -31
- package/scripts/hooks/format-edited-file.ts +6 -8
- package/scripts/hooks/implementation-shell.ts +3 -2
- package/scripts/init/distributed-paths.ts +18 -5
- package/scripts/init/init-actions.ts +16 -1
- package/scripts/init/init-bootstrap.ts +2 -0
- package/scripts/init/init-logic.ts +40 -8
- package/scripts/init/init.ts +11 -3
- package/scripts/issue/defect-rate-cli.ts +19 -13
- package/scripts/issue/issue-file-batch.ts +7 -3
- package/scripts/issue/issue-file-cli.ts +19 -6
- package/scripts/issue/issue-file-main.ts +6 -1
- package/scripts/issue/issue-file-pass.ts +17 -0
- package/scripts/issue/issue-file.ts +3 -0
- package/scripts/issue/issue-not-before.ts +9 -2
- package/scripts/issue/issue-observation-gate.ts +141 -0
- package/scripts/issue/issue-observation-prime.ts +83 -0
- package/scripts/issue/issue-publication-state.ts +61 -0
- package/scripts/issue/issue-publication.ts +144 -0
- package/scripts/issue/issue-sample-prime.ts +2 -0
- package/scripts/issue/issue-search-count.ts +26 -0
- package/scripts/issue/issue-search.ts +128 -3
- package/scripts/issue/issue-spill-cli.ts +4 -0
- package/scripts/issue/upstream-interrupt-cli.ts +21 -5
- package/scripts/josh/josh-commands-ai.ts +1 -1
- package/scripts/josh/josh-commands-development.ts +1 -1
- package/scripts/josh/josh-commands-issue-write.ts +9 -0
- package/scripts/josh/josh-commands-run.ts +6 -1
- package/scripts/josh/josh-commands-ship.ts +5 -5
- package/scripts/josh/josh-run.ts +5 -2
- package/scripts/josh/session-language-cli.ts +1 -1
- package/scripts/josh/shared-cache.ts +16 -1
- package/scripts/lane/lane-await.ts +15 -0
- package/scripts/lane/lane-cli.ts +2 -0
- package/scripts/lane/lane-dispatch-dependency.ts +102 -0
- package/scripts/lane/lane-dispatch-wait-stats.ts +239 -0
- package/scripts/lane/lane-dispatch.ts +44 -5
- package/scripts/lane/lane-early-push-count.ts +21 -0
- package/scripts/lane/lane-install.ts +15 -5
- package/scripts/lane/lane-launch-cli.ts +35 -14
- package/scripts/lane/lane-ledger-kind.ts +2 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +5 -1
- package/scripts/lane/lane-ledger-schema.ts +15 -1
- package/scripts/lane/lane-ledger-write.ts +4 -1
- package/scripts/lane/lane-relaunch.ts +25 -2
- package/scripts/lane/lane-state-record.ts +2 -0
- package/scripts/lane/lane-state.ts +8 -1
- package/scripts/lane/lane-stats-tables.ts +2 -0
- package/scripts/lane/openai-lane-supervisor.ts +16 -1
- package/scripts/lib/inherited-scope.ts +48 -0
- package/scripts/lint/format-steps.ts +93 -0
- package/scripts/lint/format.ts +6 -49
- package/scripts/lint/lint-related-fix.ts +35 -0
- package/scripts/lint/lint-related.ts +22 -35
- package/scripts/propagate/propagate-steps.ts +9 -4
- package/scripts/registry-migration/migrate-cli.ts +50 -0
- package/scripts/registry-migration/migrate-files.ts +154 -0
- package/scripts/registry-migration/migrate-lock.ts +147 -0
- package/scripts/registry-migration/migrate.ts +235 -0
- package/scripts/registry-migration/npm-acquisition.ts +118 -0
- package/scripts/review/review-record-cli.ts +27 -13
- package/scripts/review/review-record-pinned.ts +15 -6
- package/scripts/run/add/run-add-cli.ts +21 -3
- package/scripts/run/add/run-add.ts +26 -9
- package/scripts/run/board/run-board-header.ts +4 -5
- package/scripts/run/board/run-board-layout.ts +2 -2
- package/scripts/run/board/run-board-read.ts +7 -1
- package/scripts/run/board/run-board-reason.ts +12 -0
- package/scripts/run/board/run-board-retry-wait.ts +35 -0
- package/scripts/run/board/run-board-roots.ts +25 -2
- package/scripts/run/board/run-board-seat.ts +9 -2
- package/scripts/run/board/run-board-setup.ts +19 -11
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +7 -1
- package/scripts/run/carry/run-carry-reclaim.ts +12 -2
- package/scripts/run/carry/run-carry-session.ts +48 -0
- package/scripts/run/carry/run-carry.ts +21 -14
- package/scripts/run/cut/run-cut-resume.ts +15 -3
- package/scripts/run/cut/run-cut.ts +1 -1
- package/scripts/run/detached-launch.ts +14 -5
- package/scripts/run/early-push-record.ts +6 -2
- package/scripts/run/event/run-event-setup.ts +2 -0
- package/scripts/run/event/run-event-stream-emit.ts +27 -7
- package/scripts/run/hold/run-hold-cli.ts +40 -5
- package/scripts/run/merge/run-merge-cut-release.ts +17 -0
- package/scripts/run/merge/run-merge-human-review.ts +6 -0
- package/scripts/run/merge/run-merge-steps.ts +4 -57
- package/scripts/run/merge/run-merge-waits.ts +54 -0
- package/scripts/run/prime/run-prime-cli.ts +16 -2
- package/scripts/run/review/run-review-cli.ts +5 -7
- package/scripts/run/review/run-review-early-push.ts +19 -8
- package/scripts/run/review/run-review-join-read.ts +33 -6
- package/scripts/run/review/run-review-pinned-remaining.ts +124 -0
- package/scripts/run/review/run-review-pinned-steps.ts +7 -1
- package/scripts/run/run-halfrun-resume.ts +49 -6
- package/scripts/run/run-headless.ts +3 -0
- package/scripts/run/run-pr-return.ts +14 -5
- package/scripts/run/run-repair-attempt.ts +219 -0
- package/scripts/run/ship/run-ship-lap.ts +5 -17
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +4 -13
- package/scripts/run/ship/run-ship-scoped.ts +66 -30
- package/scripts/run/startup/run-startup-schema.ts +31 -0
- package/scripts/run/startup/run-startup-store.ts +130 -0
- package/scripts/run/startup/run-startup.ts +119 -0
- package/scripts/run/wake/run-wake-codex.ts +144 -0
- package/scripts/run/wake/run-wake-driver.ts +32 -7
- package/scripts/run/wake/run-wake-handoff.ts +9 -8
- package/scripts/run/wake/run-wake-loop.ts +23 -3
- package/scripts/run/wake/run-wake-ports.ts +17 -3
- package/scripts/run/wake/run-wake-takeover.ts +6 -3
- package/scripts/run/wake/run-wake-window.ts +4 -1
- package/scripts/run/wake/run-wake.ts +5 -0
- package/scripts/sync/sync-ai-files.ts +17 -0
- package/scripts/sync/sync-configs.ts +11 -1
- package/scripts/sync/sync.ts +5 -1
- package/scripts/test/test-related-cap.ts +7 -3
- package/scripts/test/test-related-imports.ts +92 -0
- package/scripts/test/vitest-failure-summary.ts +7 -0
- package/scripts/version/npm-registry.ts +45 -7
- package/scripts/version/upgrade-shell-command.ts +34 -2
- package/templates/workflows/ci.yml +0 -15
- package/templates/workflows/github-release.yml +0 -5
- package/dist/commands/chunk-23GH6B3D.js +0 -333
- package/dist/commands/chunk-24ZDAWML.js +0 -48
- package/dist/commands/chunk-2BB5ZR2F.js +0 -128
- package/dist/commands/chunk-2BJJUTZA.js +0 -59
- package/dist/commands/chunk-2CXT6MBR.js +0 -51
- package/dist/commands/chunk-2DJLAUYB.js +0 -109
- package/dist/commands/chunk-2EZTYLO4.js +0 -179
- package/dist/commands/chunk-2FJJYCBD.js +0 -149
- package/dist/commands/chunk-2PHOLX62.js +0 -43
- package/dist/commands/chunk-2QM72JCJ.js +0 -338
- package/dist/commands/chunk-2VRITY55.js +0 -41
- package/dist/commands/chunk-2XAH3MHV.js +0 -23
- package/dist/commands/chunk-34KZDKQJ.js +0 -304
- package/dist/commands/chunk-352KLSFX.js +0 -67
- package/dist/commands/chunk-36LOGVJ7.js +0 -38
- package/dist/commands/chunk-3AEDZZNL.js +0 -223
- package/dist/commands/chunk-3B2HEFNO.js +0 -51
- package/dist/commands/chunk-3PEXBHRN.js +0 -33
- package/dist/commands/chunk-3Z425MM3.js +0 -61
- package/dist/commands/chunk-3ZOX4G4U.js +0 -88
- package/dist/commands/chunk-452HWOJ7.js +0 -395
- package/dist/commands/chunk-475EFIJV.js +0 -178
- package/dist/commands/chunk-47UZB54M.js +0 -73
- package/dist/commands/chunk-4DN4XVPL.js +0 -716
- package/dist/commands/chunk-4DVM57T7.js +0 -86
- package/dist/commands/chunk-4EADHJVC.js +0 -247
- package/dist/commands/chunk-4HERA2GY.js +0 -95
- package/dist/commands/chunk-4JI35CVO.js +0 -70
- package/dist/commands/chunk-4L4LNWZ7.js +0 -143
- package/dist/commands/chunk-4N32QKJZ.js +0 -170
- package/dist/commands/chunk-4NRSFLG7.js +0 -185
- package/dist/commands/chunk-4OKRPAUT.js +0 -98
- package/dist/commands/chunk-4PUKDAXL.js +0 -42
- package/dist/commands/chunk-4Q64PG46.js +0 -478
- package/dist/commands/chunk-4UDTQVDD.js +0 -278
- package/dist/commands/chunk-4WR2LEZL.js +0 -123
- package/dist/commands/chunk-4ZJOC6OK.js +0 -298
- package/dist/commands/chunk-4ZQM4I7R.js +0 -533
- package/dist/commands/chunk-54X37JEJ.js +0 -170
- package/dist/commands/chunk-56KW5EOK.js +0 -85
- package/dist/commands/chunk-5CG3XU3O.js +0 -74
- package/dist/commands/chunk-5ESZLHDZ.js +0 -514
- package/dist/commands/chunk-5FBRMARM.js +0 -578
- package/dist/commands/chunk-5RG4S66L.js +0 -47
- package/dist/commands/chunk-5UJGACVN.js +0 -137
- package/dist/commands/chunk-5Y7B7FDL.js +0 -44
- package/dist/commands/chunk-5Z6E5ZUV.js +0 -89
- package/dist/commands/chunk-5ZNUH4LV.js +0 -1722
- package/dist/commands/chunk-5ZTQX7OS.js +0 -125
- package/dist/commands/chunk-66NX6EXR.js +0 -29
- package/dist/commands/chunk-66TGK5XL.js +0 -84
- package/dist/commands/chunk-6CFHLMKM.js +0 -555
- package/dist/commands/chunk-6DDK5XWK.js +0 -74
- package/dist/commands/chunk-6GQX752S.js +0 -145
- package/dist/commands/chunk-6HRRZANN.js +0 -55
- package/dist/commands/chunk-6MAZ55GF.js +0 -1241
- package/dist/commands/chunk-6OP63TBT.js +0 -78
- package/dist/commands/chunk-6Q3XUCLM.js +0 -204
- package/dist/commands/chunk-6RUWU2SB.js +0 -45
- package/dist/commands/chunk-6UCWAFQY.js +0 -245
- package/dist/commands/chunk-6WKJ3GDA.js +0 -408
- package/dist/commands/chunk-6YURRFRU.js +0 -461
- package/dist/commands/chunk-6YVURZAJ.js +0 -350
- package/dist/commands/chunk-6Z4YZG7P.js +0 -169
- package/dist/commands/chunk-77BIN247.js +0 -122
- package/dist/commands/chunk-7ABHNNY3.js +0 -529
- package/dist/commands/chunk-7C7FOKJK.js +0 -115
- package/dist/commands/chunk-7HAGC6SW.js +0 -1062
- package/dist/commands/chunk-7KKUFLH5.js +0 -56
- package/dist/commands/chunk-7LJQMM2K.js +0 -102
- package/dist/commands/chunk-7MJYLUF4.js +0 -99
- package/dist/commands/chunk-7NHZCI5Y.js +0 -704
- package/dist/commands/chunk-7OQ56DVW.js +0 -239
- package/dist/commands/chunk-7VWPGTZO.js +0 -260
- package/dist/commands/chunk-7YZXS3AK.js +0 -30
- package/dist/commands/chunk-A4HOQKOG.js +0 -85
- package/dist/commands/chunk-A6T2N7VC.js +0 -159
- package/dist/commands/chunk-AAUDI7WJ.js +0 -250
- package/dist/commands/chunk-AC26ZUG5.js +0 -259
- package/dist/commands/chunk-ACZ52JPI.js +0 -140
- package/dist/commands/chunk-AJ65WNJB.js +0 -230
- package/dist/commands/chunk-AK7SEO4Y.js +0 -177
- package/dist/commands/chunk-AKK56GNX.js +0 -205
- package/dist/commands/chunk-AKQYYHP2.js +0 -461
- package/dist/commands/chunk-ALDAS2WG.js +0 -137
- package/dist/commands/chunk-ANUTT56I.js +0 -85
- package/dist/commands/chunk-AVVMDVGO.js +0 -68
- package/dist/commands/chunk-B3M5WSGN.js +0 -72
- package/dist/commands/chunk-B7ZNIEDV.js +0 -351
- package/dist/commands/chunk-BCUDQWYK.js +0 -77
- package/dist/commands/chunk-BF6YX4HE.js +0 -70
- package/dist/commands/chunk-BHRS7HAM.js +0 -272
- package/dist/commands/chunk-BIKGPT52.js +0 -29
- package/dist/commands/chunk-BJA5U4Q7.js +0 -75
- package/dist/commands/chunk-BMKWSWWE.js +0 -126
- package/dist/commands/chunk-BMTYUJNT.js +0 -59
- package/dist/commands/chunk-BRHPRDDV.js +0 -127
- package/dist/commands/chunk-BRUBX64M.js +0 -33
- package/dist/commands/chunk-BTZJDHQT.js +0 -46
- package/dist/commands/chunk-BYFC6MD2.js +0 -72
- package/dist/commands/chunk-BYGJ3BCJ.js +0 -803
- package/dist/commands/chunk-C2SEDMZ3.js +0 -238
- package/dist/commands/chunk-C54QXJD6.js +0 -4208
- package/dist/commands/chunk-C6QSVO5K.js +0 -56
- package/dist/commands/chunk-CBAKBWSZ.js +0 -332
- package/dist/commands/chunk-CFHRNTE5.js +0 -326
- package/dist/commands/chunk-CGLLAEKZ.js +0 -110
- package/dist/commands/chunk-CHKYPF5O.js +0 -82
- package/dist/commands/chunk-CJBW6BWG.js +0 -800
- package/dist/commands/chunk-CJKPF5DJ.js +0 -156
- package/dist/commands/chunk-CKUKK7JG.js +0 -252
- package/dist/commands/chunk-CMHGDCGN.js +0 -263
- package/dist/commands/chunk-CNGTHSUS.js +0 -750
- package/dist/commands/chunk-CP2J4RV2.js +0 -3987
- package/dist/commands/chunk-CQZI7RIK.js +0 -72
- package/dist/commands/chunk-CWCKCZS6.js +0 -188
- package/dist/commands/chunk-D5HIP4HT.js +0 -289
- package/dist/commands/chunk-D6D6WQR6.js +0 -125
- package/dist/commands/chunk-D7JRJI44.js +0 -297
- package/dist/commands/chunk-D7OAU32Z.js +0 -119
- package/dist/commands/chunk-DAAX7XDS.js +0 -1208
- package/dist/commands/chunk-DBKTFB5V.js +0 -18
- package/dist/commands/chunk-DEZF7QZZ.js +0 -184
- package/dist/commands/chunk-DMD74E3F.js +0 -39
- package/dist/commands/chunk-DPJVKTO4.js +0 -41
- package/dist/commands/chunk-DXJQHJQ4.js +0 -73
- package/dist/commands/chunk-DYZOOWVW.js +0 -912
- package/dist/commands/chunk-EBI2BAKQ.js +0 -47
- package/dist/commands/chunk-EDKOSZ3K.js +0 -88
- package/dist/commands/chunk-EFDVE5P5.js +0 -121
- package/dist/commands/chunk-EGIHAEMT.js +0 -72
- package/dist/commands/chunk-EKLU6QR7.js +0 -507
- package/dist/commands/chunk-ELY76WAR.js +0 -65
- package/dist/commands/chunk-EM3HAZF6.js +0 -52
- package/dist/commands/chunk-EMTZAGT5.js +0 -51
- package/dist/commands/chunk-ENKFCOVY.js +0 -472
- package/dist/commands/chunk-ERG4QRNG.js +0 -224
- package/dist/commands/chunk-EUGNQCYK.js +0 -139
- package/dist/commands/chunk-EW4NPUMQ.js +0 -363
- package/dist/commands/chunk-EWVR5AEX.js +0 -140
- package/dist/commands/chunk-EXDFKKMH.js +0 -113
- package/dist/commands/chunk-EXWB34R2.js +0 -238
- package/dist/commands/chunk-EYMPBHDN.js +0 -61
- package/dist/commands/chunk-EZ7IPKIH.js +0 -20
- package/dist/commands/chunk-EZSO25UZ.js +0 -80
- package/dist/commands/chunk-F6IQHZYY.js +0 -94
- package/dist/commands/chunk-FDS65XOJ.js +0 -212
- package/dist/commands/chunk-FFHEGWPK.js +0 -91
- package/dist/commands/chunk-FGL6DBZU.js +0 -71
- package/dist/commands/chunk-FHFWNNUC.js +0 -144
- package/dist/commands/chunk-FIUCHMCF.js +0 -32
- package/dist/commands/chunk-FJX7AENO.js +0 -74
- package/dist/commands/chunk-FKZ5E4JD.js +0 -97
- package/dist/commands/chunk-FMZWRDKH.js +0 -302
- package/dist/commands/chunk-FOKCRIAN.js +0 -67
- package/dist/commands/chunk-FQGWO2ZH.js +0 -43
- package/dist/commands/chunk-FROACLVU.js +0 -525
- package/dist/commands/chunk-FURNDIT3.js +0 -34
- package/dist/commands/chunk-FVDQNMP6.js +0 -93
- package/dist/commands/chunk-FVVZIJUC.js +0 -425
- package/dist/commands/chunk-FWU3H4TX.js +0 -17
- package/dist/commands/chunk-G3HBYHFA.js +0 -16
- package/dist/commands/chunk-G4E4M2P4.js +0 -299
- package/dist/commands/chunk-G5KSL2Q2.js +0 -151
- package/dist/commands/chunk-G6XC7V6Y.js +0 -58
- package/dist/commands/chunk-GCPP5XIG.js +0 -90
- package/dist/commands/chunk-GITT5A5A.js +0 -198
- package/dist/commands/chunk-GJYMI2CM.js +0 -48
- package/dist/commands/chunk-GLC47H7I.js +0 -285
- package/dist/commands/chunk-GUR54ELE.js +0 -328
- package/dist/commands/chunk-GW4UWR3O.js +0 -126
- package/dist/commands/chunk-GY4DMCKS.js +0 -306
- package/dist/commands/chunk-H6JQRHDC.js +0 -80
- package/dist/commands/chunk-HATKF6VX.js +0 -173
- package/dist/commands/chunk-HG2NZQD5.js +0 -135
- package/dist/commands/chunk-HH3CVIFU.js +0 -93
- package/dist/commands/chunk-HHMMGDMP.js +0 -469
- package/dist/commands/chunk-HI5WPM6C.js +0 -127
- package/dist/commands/chunk-HQGNXR6I.js +0 -245
- package/dist/commands/chunk-HTIDHEEN.js +0 -108
- package/dist/commands/chunk-HVCQYZVD.js +0 -38
- package/dist/commands/chunk-HWEZRNPL.js +0 -66
- package/dist/commands/chunk-HX3FFIO5.js +0 -74
- package/dist/commands/chunk-HXDTA5VU.js +0 -208
- package/dist/commands/chunk-I2KCGKJI.js +0 -107
- package/dist/commands/chunk-I63RNFFR.js +0 -213
- package/dist/commands/chunk-IAD4WLLI.js +0 -98
- package/dist/commands/chunk-IB5TT73I.js +0 -177
- package/dist/commands/chunk-IBAYXCMU.js +0 -156
- package/dist/commands/chunk-IF3EIFK4.js +0 -7515
- package/dist/commands/chunk-IGDJNXX2.js +0 -49
- package/dist/commands/chunk-ILGPZ47M.js +0 -33
- package/dist/commands/chunk-IYJEBWW5.js +0 -44
- package/dist/commands/chunk-J22PSIXR.js +0 -418
- package/dist/commands/chunk-J66YBAAM.js +0 -161
- package/dist/commands/chunk-J6WKZRV2.js +0 -391
- package/dist/commands/chunk-J7IO3AYH.js +0 -179
- package/dist/commands/chunk-JAKC2BHX.js +0 -88
- package/dist/commands/chunk-JCAQ7B6J.js +0 -124
- package/dist/commands/chunk-JHN6RCE6.js +0 -123
- package/dist/commands/chunk-JIGBNCDR.js +0 -88
- package/dist/commands/chunk-JJHWTFWQ.js +0 -76
- package/dist/commands/chunk-JJRKTWLK.js +0 -64
- package/dist/commands/chunk-JKOGWGQM.js +0 -92
- package/dist/commands/chunk-JM6EVOFN.js +0 -172
- package/dist/commands/chunk-JQXPLMCO.js +0 -81
- package/dist/commands/chunk-JV24VP2O.js +0 -81
- package/dist/commands/chunk-JVKXJ763.js +0 -45
- package/dist/commands/chunk-K275REYP.js +0 -37
- package/dist/commands/chunk-K4G3HOKS.js +0 -105
- package/dist/commands/chunk-K64EIHD2.js +0 -138
- package/dist/commands/chunk-KB3OQWDT.js +0 -71
- package/dist/commands/chunk-KCFOP5RI.js +0 -1310
- package/dist/commands/chunk-KGE4AS2Y.js +0 -134
- package/dist/commands/chunk-KIILCKQR.js +0 -220
- package/dist/commands/chunk-KK2SL42W.js +0 -241
- package/dist/commands/chunk-KKIMVXAP.js +0 -280
- package/dist/commands/chunk-KODXWMBX.js +0 -77
- package/dist/commands/chunk-KPSKWTOS.js +0 -208
- package/dist/commands/chunk-KVHNQHX3.js +0 -100
- package/dist/commands/chunk-KWYQR5MK.js +0 -269
- package/dist/commands/chunk-KXHXMERX.js +0 -214
- package/dist/commands/chunk-KZ5BBBMO.js +0 -271
- package/dist/commands/chunk-KZ7HD37E.js +0 -121
- package/dist/commands/chunk-KZSM5M5P.js +0 -89
- package/dist/commands/chunk-KZZWPUJW.js +0 -25
- package/dist/commands/chunk-L7DAL54N.js +0 -347
- package/dist/commands/chunk-LAR76BFA.js +0 -114
- package/dist/commands/chunk-LB557AHJ.js +0 -192
- package/dist/commands/chunk-LJXKVLYI.js +0 -229
- package/dist/commands/chunk-LOOFYHUM.js +0 -773
- package/dist/commands/chunk-LQXFVZTS.js +0 -13
- package/dist/commands/chunk-LT7QSHR4.js +0 -995
- package/dist/commands/chunk-LUXWDMKI.js +0 -426
- package/dist/commands/chunk-LWAXXZPS.js +0 -118
- package/dist/commands/chunk-LX7TIEMI.js +0 -1612
- package/dist/commands/chunk-LZ5QXE5N.js +0 -244
- package/dist/commands/chunk-M4UV3BEV.js +0 -313
- package/dist/commands/chunk-M6JDU5EK.js +0 -133
- package/dist/commands/chunk-MDLOETXL.js +0 -211
- package/dist/commands/chunk-MFBNNTBM.js +0 -44
- package/dist/commands/chunk-MGF64OHV.js +0 -32
- package/dist/commands/chunk-MI2OS6TL.js +0 -132
- package/dist/commands/chunk-MLW4S35K.js +0 -389
- package/dist/commands/chunk-MN425CU6.js +0 -63
- package/dist/commands/chunk-MPGJ4ONJ.js +0 -634
- package/dist/commands/chunk-MRORPFSO.js +0 -402
- package/dist/commands/chunk-MUGTOJUL.js +0 -319
- package/dist/commands/chunk-MVHPRYHE.js +0 -91
- package/dist/commands/chunk-MWO6FOSZ.js +0 -393
- package/dist/commands/chunk-MXC4IBZ7.js +0 -104
- package/dist/commands/chunk-N7QTMTST.js +0 -210
- package/dist/commands/chunk-NDBX7LSS.js +0 -32
- package/dist/commands/chunk-NEGJ77RV.js +0 -173
- package/dist/commands/chunk-NEGRE5VW.js +0 -101
- package/dist/commands/chunk-NFPD2P3V.js +0 -53
- package/dist/commands/chunk-NHIW7JE6.js +0 -335
- package/dist/commands/chunk-NILSN7A4.js +0 -158
- package/dist/commands/chunk-NOLFSPOB.js +0 -74
- package/dist/commands/chunk-NSAVEXVR.js +0 -353
- package/dist/commands/chunk-NT67QTZO.js +0 -65
- package/dist/commands/chunk-NU7W6QGT.js +0 -155
- package/dist/commands/chunk-NYWGJLBU.js +0 -19
- package/dist/commands/chunk-OAAVY2JK.js +0 -49
- package/dist/commands/chunk-OGS2DK7I.js +0 -47
- package/dist/commands/chunk-OHNP3QL2.js +0 -186
- package/dist/commands/chunk-OIPV3PWW.js +0 -870
- package/dist/commands/chunk-OIX2363L.js +0 -16
- package/dist/commands/chunk-OJ65BZBV.js +0 -646
- package/dist/commands/chunk-OKZWEVR4.js +0 -352
- package/dist/commands/chunk-OL6IF4JC.js +0 -20
- package/dist/commands/chunk-OMPRILFH.js +0 -29
- package/dist/commands/chunk-OOWGH2RG.js +0 -85
- package/dist/commands/chunk-OP2RFVLS.js +0 -102
- package/dist/commands/chunk-ORKVN2LO.js +0 -56
- package/dist/commands/chunk-OSBWXZJ4.js +0 -492
- package/dist/commands/chunk-OT3UMEWK.js +0 -47
- package/dist/commands/chunk-OVWPZDWP.js +0 -64
- package/dist/commands/chunk-OWYK2BEJ.js +0 -1222
- package/dist/commands/chunk-OX3VQLV2.js +0 -488
- package/dist/commands/chunk-OZJP77UM.js +0 -103
- package/dist/commands/chunk-OZZWRJQI.js +0 -246
- package/dist/commands/chunk-P3H3DKTE.js +0 -69
- package/dist/commands/chunk-P4D5RESF.js +0 -88
- package/dist/commands/chunk-P4KIQRSL.js +0 -119
- package/dist/commands/chunk-P5SOYWH2.js +0 -37
- package/dist/commands/chunk-P7FPXAHP.js +0 -29
- package/dist/commands/chunk-PE7B7242.js +0 -84
- package/dist/commands/chunk-PFO7EYAP.js +0 -45
- package/dist/commands/chunk-PHLOTUB7.js +0 -366
- package/dist/commands/chunk-PTNQCCIB.js +0 -41
- package/dist/commands/chunk-PTZEFQUX.js +0 -211
- package/dist/commands/chunk-Q5Q2QYVU.js +0 -112
- package/dist/commands/chunk-Q7EEIOAD.js +0 -120
- package/dist/commands/chunk-QABRNGQV.js +0 -88
- package/dist/commands/chunk-QAHFPCFR.js +0 -50
- package/dist/commands/chunk-QDFVVRQP.js +0 -92
- package/dist/commands/chunk-QGAERR7D.js +0 -99
- package/dist/commands/chunk-QHWJJ6X5.js +0 -53
- package/dist/commands/chunk-QJRVDE7T.js +0 -307
- package/dist/commands/chunk-QMDKDQKW.js +0 -114
- package/dist/commands/chunk-QOSC5RKD.js +0 -18
- package/dist/commands/chunk-QQ4CIYMF.js +0 -132
- package/dist/commands/chunk-QV5FKMOU.js +0 -80
- package/dist/commands/chunk-QWWTC3XR.js +0 -109
- package/dist/commands/chunk-QXQ56E56.js +0 -27
- package/dist/commands/chunk-R54Z2Z3X.js +0 -114
- package/dist/commands/chunk-R6QE5VJG.js +0 -46
- package/dist/commands/chunk-RG53UFNW.js +0 -418
- package/dist/commands/chunk-RGGEQM6G.js +0 -140
- package/dist/commands/chunk-RH6ZWJOA.js +0 -77
- package/dist/commands/chunk-RHKCWJQX.js +0 -62
- package/dist/commands/chunk-RKIAX4DZ.js +0 -191
- package/dist/commands/chunk-RLBFEHHC.js +0 -300
- package/dist/commands/chunk-RN2YDNLF.js +0 -229
- package/dist/commands/chunk-RRAIRS2T.js +0 -54
- package/dist/commands/chunk-RS764ZMU.js +0 -46
- package/dist/commands/chunk-RUIBQMOV.js +0 -38
- package/dist/commands/chunk-RUQY5TS3.js +0 -83
- package/dist/commands/chunk-RVUJ6F5L.js +0 -181
- package/dist/commands/chunk-RW64VHEB.js +0 -130
- package/dist/commands/chunk-RXYMJPJB.js +0 -114
- package/dist/commands/chunk-S4M6UWR2.js +0 -103
- package/dist/commands/chunk-SASX3W2M.js +0 -99
- package/dist/commands/chunk-SFNBUB6D.js +0 -110
- package/dist/commands/chunk-SJ2ZN575.js +0 -90
- package/dist/commands/chunk-SP7DKYGU.js +0 -1263
- package/dist/commands/chunk-SSL6A3Y4.js +0 -165
- package/dist/commands/chunk-SVAKCXZ7.js +0 -44
- package/dist/commands/chunk-SYOXIKB7.js +0 -705
- package/dist/commands/chunk-SZG5HDNP.js +0 -321
- package/dist/commands/chunk-T25S4QHL.js +0 -52
- package/dist/commands/chunk-T4F33UKZ.js +0 -114
- package/dist/commands/chunk-T4UYEHWG.js +0 -50
- package/dist/commands/chunk-TAEGXID2.js +0 -58
- package/dist/commands/chunk-TCBUX52E.js +0 -701
- package/dist/commands/chunk-TEPYEUYG.js +0 -585
- package/dist/commands/chunk-TFLHYC2F.js +0 -141
- package/dist/commands/chunk-TJKM56WJ.js +0 -109
- package/dist/commands/chunk-TLAB552K.js +0 -445
- package/dist/commands/chunk-TP6L4W3W.js +0 -82
- package/dist/commands/chunk-TR7WWYD5.js +0 -60
- package/dist/commands/chunk-TVZXG4Y4.js +0 -197
- package/dist/commands/chunk-TYNH2R66.js +0 -1150
- package/dist/commands/chunk-U3IVVR4W.js +0 -33
- package/dist/commands/chunk-U5P6G3UV.js +0 -77
- package/dist/commands/chunk-U7XU5RG3.js +0 -86
- package/dist/commands/chunk-UKBBWMLE.js +0 -1189
- package/dist/commands/chunk-ULVVGGTY.js +0 -64
- package/dist/commands/chunk-UNMSZGSG.js +0 -269
- package/dist/commands/chunk-URDT2O2K.js +0 -36
- package/dist/commands/chunk-UTHUX7VU.js +0 -88
- package/dist/commands/chunk-V4Z7G4UH.js +0 -73
- package/dist/commands/chunk-V5FD7DVV.js +0 -48
- package/dist/commands/chunk-VCPGFBMN.js +0 -655
- package/dist/commands/chunk-VGPIQNDB.js +0 -24
- package/dist/commands/chunk-VJE3RJAX.js +0 -23
- package/dist/commands/chunk-VK6D5CWA.js +0 -161
- package/dist/commands/chunk-VOF3N4WO.js +0 -114
- package/dist/commands/chunk-VQVPAVQ3.js +0 -45
- package/dist/commands/chunk-VV4TQYGD.js +0 -38
- package/dist/commands/chunk-W3FX7FYN.js +0 -84
- package/dist/commands/chunk-W7DRUOF7.js +0 -122
- package/dist/commands/chunk-WAEN2RTT.js +0 -161
- package/dist/commands/chunk-WCEUMOKK.js +0 -84
- package/dist/commands/chunk-WJ4NEDNI.js +0 -288
- package/dist/commands/chunk-WMMGXJXP.js +0 -92
- package/dist/commands/chunk-WOFLW2LX.js +0 -26
- package/dist/commands/chunk-WTH6NTXU.js +0 -128
- package/dist/commands/chunk-WWVYEHQT.js +0 -272
- package/dist/commands/chunk-X3TNPALI.js +0 -213
- package/dist/commands/chunk-XC6GG5JR.js +0 -78
- package/dist/commands/chunk-XDF25GSU.js +0 -536
- package/dist/commands/chunk-XFPEUYDV.js +0 -67
- package/dist/commands/chunk-XFWI6WHH.js +0 -60
- package/dist/commands/chunk-XGMRAKSL.js +0 -85
- package/dist/commands/chunk-XHGWMD3L.js +0 -104
- package/dist/commands/chunk-XKSUBUIH.js +0 -116
- package/dist/commands/chunk-XPBF5DG3.js +0 -419
- package/dist/commands/chunk-XUMYPESL.js +0 -160
- package/dist/commands/chunk-XXC32PVL.js +0 -541
- package/dist/commands/chunk-XXN2EZBY.js +0 -133
- package/dist/commands/chunk-XXNEY4B3.js +0 -110
- package/dist/commands/chunk-Y27PTXXI.js +0 -258
- package/dist/commands/chunk-Y4EBUI6M.js +0 -237
- package/dist/commands/chunk-YAU3LYNF.js +0 -60
- package/dist/commands/chunk-YDURQICF.js +0 -204
- package/dist/commands/chunk-YGFOGA7C.js +0 -1153
- package/dist/commands/chunk-YHJ2AJMA.js +0 -66
- package/dist/commands/chunk-YIA6FC3K.js +0 -78
- package/dist/commands/chunk-YM77CF4D.js +0 -183
- package/dist/commands/chunk-YNPRJCEO.js +0 -81
- package/dist/commands/chunk-YTBQAD5W.js +0 -170
- package/dist/commands/chunk-YVH2OLDS.js +0 -104
- package/dist/commands/chunk-Z3WGHIVB.js +0 -122
- package/dist/commands/chunk-ZAAHZY2A.js +0 -19
- package/dist/commands/chunk-ZBZ5DDCH.js +0 -1866
- package/dist/commands/chunk-ZLRUHSOP.js +0 -48
- package/dist/commands/chunk-ZNR75OXP.js +0 -444
- package/dist/commands/chunk-ZSOR4ISJ.js +0 -75
- package/dist/commands/chunk-ZVAIIHBE.js +0 -63
- package/dist/commands/chunk-ZYFHYQA7.js +0 -51
- package/dist/commands/chunk-ZZFFSQF4.js +0 -218
- package/dist/commands/chunk-ZZULXNC2.js +0 -47
- package/dist/commands/chunk-ZZXO6TFH.js +0 -232
- package/dist/commands/run-ship-lap-W5T2VHFC.js +0 -30
- package/dist/commands/test-related-ZCNC46MS.js +0 -113
- package/dist/commands/unused-members-STULPRBU.js +0 -331
- package/dist/hooks/chunk-5CUXGQT6.js +0 -8353
- package/dist/hooks/chunk-5R5YFQR5.js +0 -931
- package/dist/hooks/chunk-6RLY27FT.js +0 -232
- package/dist/hooks/chunk-6YGMIODI.js +0 -86
- package/dist/hooks/chunk-AO4TOV4V.js +0 -5026
- package/dist/hooks/chunk-FOW4B7OQ.js +0 -1795
- package/dist/hooks/chunk-G5WSPYJ6.js +0 -5663
- package/dist/hooks/chunk-HHIVWGHX.js +0 -6869
- package/dist/hooks/chunk-IURJANNZ.js +0 -1082
- package/dist/hooks/chunk-OV22V63P.js +0 -10980
- package/dist/hooks/chunk-SXABK6UV.js +0 -321
- package/dist/hooks/run-ship-lap-VCEOUHQI.js +0 -8
|
@@ -0,0 +1,3985 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decision_oracle,
|
|
3
|
+
oracle_firing
|
|
4
|
+
} from "./chunk-VJS2MCLA.js";
|
|
5
|
+
import {
|
|
6
|
+
run_progress_config
|
|
7
|
+
} from "./chunk-SXJM4DZ5.js";
|
|
8
|
+
import {
|
|
9
|
+
edit_tool,
|
|
10
|
+
implementation_cut
|
|
11
|
+
} from "./chunk-ZEH2B3DF.js";
|
|
12
|
+
import {
|
|
13
|
+
run_progress
|
|
14
|
+
} from "./chunk-RGDFWJPZ.js";
|
|
15
|
+
import {
|
|
16
|
+
run_progress_clock
|
|
17
|
+
} from "./chunk-3YT3RCSI.js";
|
|
18
|
+
import {
|
|
19
|
+
test_declared_changed
|
|
20
|
+
} from "./chunk-RESNXNDG.js";
|
|
21
|
+
import {
|
|
22
|
+
command_memory
|
|
23
|
+
} from "./chunk-QKTTKAXM.js";
|
|
24
|
+
import {
|
|
25
|
+
codex_prompt,
|
|
26
|
+
concise_reply,
|
|
27
|
+
lane_background,
|
|
28
|
+
last_prompt,
|
|
29
|
+
pre_gate_cut,
|
|
30
|
+
run_tail_rule,
|
|
31
|
+
stop_rules
|
|
32
|
+
} from "./chunk-ZIIUBGON.js";
|
|
33
|
+
import {
|
|
34
|
+
tail_commands
|
|
35
|
+
} from "./chunk-77LEKVEE.js";
|
|
36
|
+
import {
|
|
37
|
+
lane_park
|
|
38
|
+
} from "./chunk-5FUI2MLY.js";
|
|
39
|
+
import {
|
|
40
|
+
direct_pr_create
|
|
41
|
+
} from "./chunk-R2THGPI3.js";
|
|
42
|
+
import {
|
|
43
|
+
doctor_consumer
|
|
44
|
+
} from "./chunk-S4OZJIXD.js";
|
|
45
|
+
import {
|
|
46
|
+
consultation_handoff,
|
|
47
|
+
main_model_reader
|
|
48
|
+
} from "./chunk-KDCHY7XO.js";
|
|
49
|
+
import {
|
|
50
|
+
run_headless
|
|
51
|
+
} from "./chunk-Q4ULMZAS.js";
|
|
52
|
+
import {
|
|
53
|
+
bash_triggers,
|
|
54
|
+
interpreter_options
|
|
55
|
+
} from "./chunk-2BC33QPY.js";
|
|
56
|
+
import {
|
|
57
|
+
shell_segments,
|
|
58
|
+
shell_tokens
|
|
59
|
+
} from "./chunk-CVJHQOI3.js";
|
|
60
|
+
import {
|
|
61
|
+
agent_consultation_model,
|
|
62
|
+
agent_role_profile
|
|
63
|
+
} from "./chunk-O5JGV7NA.js";
|
|
64
|
+
import {
|
|
65
|
+
investigation_reads,
|
|
66
|
+
lane_guard_policy,
|
|
67
|
+
run_cut_handoff
|
|
68
|
+
} from "./chunk-TFDVTU6O.js";
|
|
69
|
+
import {
|
|
70
|
+
delegation_policy
|
|
71
|
+
} from "./chunk-6I2JM5UX.js";
|
|
72
|
+
import {
|
|
73
|
+
time_batch_guard
|
|
74
|
+
} from "./chunk-YOA25SII.js";
|
|
75
|
+
import {
|
|
76
|
+
time_markers
|
|
77
|
+
} from "./chunk-R5AABJZJ.js";
|
|
78
|
+
import {
|
|
79
|
+
time_reported_failure,
|
|
80
|
+
time_transcript_line
|
|
81
|
+
} from "./chunk-YSLGWRQN.js";
|
|
82
|
+
import {
|
|
83
|
+
cost_blocks,
|
|
84
|
+
time_josh_names,
|
|
85
|
+
time_shell
|
|
86
|
+
} from "./chunk-LHKXJD6L.js";
|
|
87
|
+
import {
|
|
88
|
+
hook_decision
|
|
89
|
+
} from "./chunk-GB2BRQVL.js";
|
|
90
|
+
import {
|
|
91
|
+
hook_context
|
|
92
|
+
} from "./chunk-CUWT6UQO.js";
|
|
93
|
+
import {
|
|
94
|
+
transcript_tail
|
|
95
|
+
} from "./chunk-2OMIHPPE.js";
|
|
96
|
+
import {
|
|
97
|
+
interactive_ask
|
|
98
|
+
} from "./chunk-TBNQIFWL.js";
|
|
99
|
+
import {
|
|
100
|
+
agent_headless
|
|
101
|
+
} from "./chunk-HDVSZ6CZ.js";
|
|
102
|
+
import {
|
|
103
|
+
repo_party
|
|
104
|
+
} from "./chunk-DKDWOE3C.js";
|
|
105
|
+
import {
|
|
106
|
+
agent_session_role
|
|
107
|
+
} from "./chunk-DURHGVRC.js";
|
|
108
|
+
import {
|
|
109
|
+
issue_citation,
|
|
110
|
+
session_cite
|
|
111
|
+
} from "./chunk-22DVQCP6.js";
|
|
112
|
+
import {
|
|
113
|
+
token_shape
|
|
114
|
+
} from "./chunk-AQ3PMSXK.js";
|
|
115
|
+
import {
|
|
116
|
+
gh_api
|
|
117
|
+
} from "./chunk-QVOFWFWC.js";
|
|
118
|
+
import {
|
|
119
|
+
git_argv
|
|
120
|
+
} from "./chunk-G6BXCWPH.js";
|
|
121
|
+
import {
|
|
122
|
+
run_carry
|
|
123
|
+
} from "./chunk-DS53UUDH.js";
|
|
124
|
+
import {
|
|
125
|
+
agent_provider
|
|
126
|
+
} from "./chunk-DDZVMXZD.js";
|
|
127
|
+
import {
|
|
128
|
+
lane_child_marker
|
|
129
|
+
} from "./chunk-XXPDFJRI.js";
|
|
130
|
+
import {
|
|
131
|
+
issue_number_shape,
|
|
132
|
+
lane_paths
|
|
133
|
+
} from "./chunk-G75GWNNY.js";
|
|
134
|
+
import {
|
|
135
|
+
git_common_directory
|
|
136
|
+
} from "./chunk-JPMAW2VZ.js";
|
|
137
|
+
import {
|
|
138
|
+
platform_temporary
|
|
139
|
+
} from "./chunk-VLN6TSPJ.js";
|
|
140
|
+
import {
|
|
141
|
+
find_package_directory
|
|
142
|
+
} from "./chunk-LR22UK5W.js";
|
|
143
|
+
import {
|
|
144
|
+
core_weights
|
|
145
|
+
} from "./chunk-ENFPR7J2.js";
|
|
146
|
+
import {
|
|
147
|
+
MS_PER_SECOND
|
|
148
|
+
} from "./chunk-WPAQUMHC.js";
|
|
149
|
+
import {
|
|
150
|
+
file_reader
|
|
151
|
+
} from "./chunk-ZNX2LJCQ.js";
|
|
152
|
+
import {
|
|
153
|
+
json_value
|
|
154
|
+
} from "./chunk-HIU2WXB6.js";
|
|
155
|
+
|
|
156
|
+
// scripts/rules/agent-model.ts
|
|
157
|
+
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["Agent", "Task"]);
|
|
158
|
+
var GENERAL_PURPOSE = "general-purpose";
|
|
159
|
+
function is_general_purpose(call) {
|
|
160
|
+
if (!SUBAGENT_TOOLS.has(call.name) || !json_value.is_record(call.input)) return false;
|
|
161
|
+
const type = call.input["subagent_type"];
|
|
162
|
+
return type === void 0 || type === GENERAL_PURPOSE;
|
|
163
|
+
}
|
|
164
|
+
function names_model(call) {
|
|
165
|
+
const model = json_value.is_record(call.input) ? call.input["model"] : void 0;
|
|
166
|
+
return typeof model === "string" && model.length > 0;
|
|
167
|
+
}
|
|
168
|
+
function is_unnamed_model(call) {
|
|
169
|
+
return is_general_purpose(call) && !names_model(call);
|
|
170
|
+
}
|
|
171
|
+
function keeps_model(call) {
|
|
172
|
+
return is_general_purpose(call) && names_model(call);
|
|
173
|
+
}
|
|
174
|
+
var AGENT_MODEL_REASON = "\u26D4 agent model: a `general-purpose` subagent with no `model` inherits the parent model. Reissue the call with `model` set \u2014 `sonnet` for routine work, `opus` for judgment such as a review. It fires on every occurrence (`.claude/skills/workflow-commands/delegation.md`).";
|
|
175
|
+
var ROW = {
|
|
176
|
+
id: "agent-model",
|
|
177
|
+
is_trigger: is_unnamed_model,
|
|
178
|
+
reason: AGENT_MODEL_REASON,
|
|
179
|
+
decide: () => true,
|
|
180
|
+
keeps: keeps_model,
|
|
181
|
+
reaches: is_general_purpose,
|
|
182
|
+
about: {
|
|
183
|
+
title: "Subagent model named",
|
|
184
|
+
topic: '`.claude/skills/workflow-commands/delegation.md` \u2192 "A `general-purpose` subagent names its model"',
|
|
185
|
+
fires: "an `Agent` / `Task` call of the `general-purpose` type with no `model`. Every occurrence",
|
|
186
|
+
quiet: "a named agent type, or a `model` set"
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var agent_model = { AGENT_MODEL_REASON, ROW, SUBAGENT_TOOLS, is_unnamed_model };
|
|
190
|
+
|
|
191
|
+
// scripts/rules/broad-investigation.ts
|
|
192
|
+
import { dirname as __josh_dirname } from "node:path";
|
|
193
|
+
import { fileURLToPath as __josh_file_path } from "node:url";
|
|
194
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
195
|
+
import path2 from "node:path";
|
|
196
|
+
import { fileURLToPath } from "node:url";
|
|
197
|
+
|
|
198
|
+
// scripts/rules/broad-investigation-shell.ts
|
|
199
|
+
import { readFileSync, statSync } from "node:fs";
|
|
200
|
+
import path from "node:path";
|
|
201
|
+
var READ_LINE_CAP = 200;
|
|
202
|
+
var DEFAULT_HEAD_LINES = 10;
|
|
203
|
+
var SHORT_FLAGS = /^-[a-zA-Z]+$/u;
|
|
204
|
+
var RECURSIVE_LETTERS = /[rR]/u;
|
|
205
|
+
var LONG_RECURSIVE = /* @__PURE__ */ new Set(["--recursive", "--dereference-recursive"]);
|
|
206
|
+
var SED_RANGE = /^(?<first>\d+),(?<last>\d+)p$/u;
|
|
207
|
+
var SED_TO_END = /^\d+,\$p$/u;
|
|
208
|
+
var LINE_COUNT_PREFIXES = ["--lines=", "-n", "-"];
|
|
209
|
+
var DIGITS = /^\d+$/u;
|
|
210
|
+
var FROM_LINE = /^\+\d+$/u;
|
|
211
|
+
var QUOTES = /^['"]|['"]$/gu;
|
|
212
|
+
var ASSIGNMENT = /^\w+=/u;
|
|
213
|
+
var ALWAYS_BROAD = /* @__PURE__ */ new Set(["find", "for", "node", "python", "python3"]);
|
|
214
|
+
var ANALYSIS_PREFIX = "josh cost:";
|
|
215
|
+
var FLAG_PREFIX = "-";
|
|
216
|
+
var HEREDOC = "<<";
|
|
217
|
+
function resolved(file) {
|
|
218
|
+
return path.resolve(process.env["JOSH_HOOK_CWD"] ?? process.cwd(), file);
|
|
219
|
+
}
|
|
220
|
+
function is_file(file) {
|
|
221
|
+
return statSync(resolved(file), { throwIfNoEntry: false })?.isFile() === true;
|
|
222
|
+
}
|
|
223
|
+
function is_directory(file) {
|
|
224
|
+
return statSync(resolved(file), { throwIfNoEntry: false })?.isDirectory() === true;
|
|
225
|
+
}
|
|
226
|
+
function line_count(file) {
|
|
227
|
+
if (!is_file(file)) return 0;
|
|
228
|
+
const text = readFileSync(resolved(file), "utf8");
|
|
229
|
+
return text.split("\n").length - (text.endsWith("\n") ? 1 : 0);
|
|
230
|
+
}
|
|
231
|
+
function is_recursive_flag(flag) {
|
|
232
|
+
return LONG_RECURSIVE.has(flag) || SHORT_FLAGS.test(flag) && RECURSIVE_LETTERS.test(flag);
|
|
233
|
+
}
|
|
234
|
+
function searches_tree(operands, flags) {
|
|
235
|
+
if (flags.some((flag) => is_recursive_flag(flag))) return true;
|
|
236
|
+
return operands.slice(1).some((operand) => is_directory(operand));
|
|
237
|
+
}
|
|
238
|
+
function rg_searches_tree(operands, _flags, is_piped) {
|
|
239
|
+
const paths = operands.slice(1);
|
|
240
|
+
if (paths.length === 0) return !is_piped;
|
|
241
|
+
return paths.some((operand) => !is_file(operand));
|
|
242
|
+
}
|
|
243
|
+
function sed_span(script) {
|
|
244
|
+
const groups = SED_RANGE.exec(script)?.groups;
|
|
245
|
+
return groups === void 0 ? 0 : Number(groups["last"]) - Number(groups["first"]) + 1;
|
|
246
|
+
}
|
|
247
|
+
function sed_reads_past_cap(operands, flags) {
|
|
248
|
+
if (!flags.includes("-n")) return false;
|
|
249
|
+
const [script = ""] = operands;
|
|
250
|
+
return SED_TO_END.test(script) || sed_span(script) > READ_LINE_CAP;
|
|
251
|
+
}
|
|
252
|
+
function number_of(word = "") {
|
|
253
|
+
return DIGITS.test(word) ? Number(word) : void 0;
|
|
254
|
+
}
|
|
255
|
+
function count_of(word) {
|
|
256
|
+
const prefix = LINE_COUNT_PREFIXES.find((candidate) => word.startsWith(candidate));
|
|
257
|
+
const rest = prefix === void 0 ? "" : word.slice(prefix.length);
|
|
258
|
+
return number_of(rest);
|
|
259
|
+
}
|
|
260
|
+
function counted_lines(operands, flags) {
|
|
261
|
+
const inline = flags.map((flag) => count_of(flag)).find((count) => count !== void 0);
|
|
262
|
+
const separate = flags.includes("-n") ? number_of(operands[0]) : void 0;
|
|
263
|
+
return inline ?? separate ?? DEFAULT_HEAD_LINES;
|
|
264
|
+
}
|
|
265
|
+
function ends_past_cap(operands, flags) {
|
|
266
|
+
if (operands.some((word) => FROM_LINE.test(word))) return true;
|
|
267
|
+
return counted_lines(operands, flags) > READ_LINE_CAP;
|
|
268
|
+
}
|
|
269
|
+
function is_past_cap(file) {
|
|
270
|
+
return line_count(file) > READ_LINE_CAP;
|
|
271
|
+
}
|
|
272
|
+
function cat_reads_past_cap(operands) {
|
|
273
|
+
return operands.some((operand) => is_past_cap(operand));
|
|
274
|
+
}
|
|
275
|
+
var COMMAND_TESTS = /* @__PURE__ */ new Map([
|
|
276
|
+
["grep", searches_tree],
|
|
277
|
+
["rg", rg_searches_tree],
|
|
278
|
+
["sed", sed_reads_past_cap],
|
|
279
|
+
["head", ends_past_cap],
|
|
280
|
+
["tail", ends_past_cap],
|
|
281
|
+
["cat", cat_reads_past_cap]
|
|
282
|
+
]);
|
|
283
|
+
function words_of(segment) {
|
|
284
|
+
const words = shell_tokens.words_of(segment).map((word) => word.text.replaceAll(QUOTES, ""));
|
|
285
|
+
const start = words.findIndex((word) => !ASSIGNMENT.test(word));
|
|
286
|
+
return start === -1 ? [] : words.slice(start);
|
|
287
|
+
}
|
|
288
|
+
function reads_past_cap(segment, name, rest, is_piped) {
|
|
289
|
+
const test = COMMAND_TESTS.get(name);
|
|
290
|
+
if (test === void 0 || segment.includes(HEREDOC)) return false;
|
|
291
|
+
const flags = rest.filter((word) => word.startsWith(FLAG_PREFIX));
|
|
292
|
+
const operands = rest.filter((word) => !word.startsWith(FLAG_PREFIX));
|
|
293
|
+
return test(operands, flags, is_piped);
|
|
294
|
+
}
|
|
295
|
+
function is_broad_segment(segment, is_piped) {
|
|
296
|
+
if (time_josh_names.josh_command_of(segment).startsWith(ANALYSIS_PREFIX)) return true;
|
|
297
|
+
const [name = "", ...rest] = words_of(segment);
|
|
298
|
+
return ALWAYS_BROAD.has(name) || reads_past_cap(segment, name, rest, is_piped);
|
|
299
|
+
}
|
|
300
|
+
function is_broad_pipeline(pipeline) {
|
|
301
|
+
return shell_segments.quote_aware_commands_of(pipeline).some((stage, index) => is_broad_segment(stage, index > 0));
|
|
302
|
+
}
|
|
303
|
+
function is_broad_command(command) {
|
|
304
|
+
return shell_segments.segments_of(command).some((pipeline) => is_broad_pipeline(pipeline));
|
|
305
|
+
}
|
|
306
|
+
var broad_investigation_shell = { READ_LINE_CAP, is_broad_command, is_file, is_past_cap };
|
|
307
|
+
|
|
308
|
+
// scripts/rules/broad-investigation.ts
|
|
309
|
+
var __josh_meta = import.meta;
|
|
310
|
+
var __josh_source_url = new URL("../../scripts/rules/broad-investigation.ts", __josh_meta.url).href;
|
|
311
|
+
var __josh_source_filename = __josh_file_path(__josh_source_url);
|
|
312
|
+
var __josh_source_dirname = __josh_dirname(__josh_source_filename);
|
|
313
|
+
var { READ_LINE_CAP: READ_LINE_CAP2, is_broad_command: is_broad_command2, is_file: is_file2, is_past_cap: is_past_cap2 } = broad_investigation_shell;
|
|
314
|
+
var ALWAYS_BROAD2 = /* @__PURE__ */ new Set(["Glob", "WebFetch"]);
|
|
315
|
+
var GENERAL_PURPOSE2 = "general-purpose";
|
|
316
|
+
var INVESTIGATION_AGENTS = /* @__PURE__ */ new Set([
|
|
317
|
+
"investigator",
|
|
318
|
+
"Explore",
|
|
319
|
+
GENERAL_PURPOSE2
|
|
320
|
+
]);
|
|
321
|
+
var FRONTMATTER_MODEL = /^model:\s*(?<model>\S+)\s*$/mu;
|
|
322
|
+
var PACKAGE_DIRECTORY = find_package_directory(path2.dirname(fileURLToPath(__josh_source_url)));
|
|
323
|
+
function field(call, key) {
|
|
324
|
+
return json_value.is_record(call.input) ? call.input[key] : void 0;
|
|
325
|
+
}
|
|
326
|
+
function is_broad_read(call) {
|
|
327
|
+
const limit = field(call, "limit");
|
|
328
|
+
if (typeof limit === "number") return limit > READ_LINE_CAP2;
|
|
329
|
+
const file = field(call, "file_path");
|
|
330
|
+
return typeof file !== "string" || is_past_cap2(file);
|
|
331
|
+
}
|
|
332
|
+
function is_broad_grep(call) {
|
|
333
|
+
const target = field(call, "path");
|
|
334
|
+
return typeof target !== "string" || !is_file2(target);
|
|
335
|
+
}
|
|
336
|
+
function definition_model(name) {
|
|
337
|
+
const file = path2.join(PACKAGE_DIRECTORY, ".claude", "agents", `${name}.md`);
|
|
338
|
+
const body = is_file2(file) ? readFileSync2(file, "utf8") : "";
|
|
339
|
+
return FRONTMATTER_MODEL.exec(body)?.groups?.["model"];
|
|
340
|
+
}
|
|
341
|
+
function agent_name(call) {
|
|
342
|
+
const type = field(call, "subagent_type");
|
|
343
|
+
if (typeof type !== "string") return GENERAL_PURPOSE2;
|
|
344
|
+
return type.split(":").at(-1) ?? type;
|
|
345
|
+
}
|
|
346
|
+
function effective_model(call, name) {
|
|
347
|
+
const named = field(call, "model");
|
|
348
|
+
return typeof named === "string" && named.length > 0 ? named : definition_model(name);
|
|
349
|
+
}
|
|
350
|
+
function dispatched_model(call) {
|
|
351
|
+
return effective_model(call, agent_name(call));
|
|
352
|
+
}
|
|
353
|
+
function is_investigation_dispatch(call) {
|
|
354
|
+
return agent_model.SUBAGENT_TOOLS.has(call.name) && INVESTIGATION_AGENTS.has(agent_name(call));
|
|
355
|
+
}
|
|
356
|
+
function is_consultation(model) {
|
|
357
|
+
return model === void 0 || agent_consultation_model.consultation_provider(model) !== void 0;
|
|
358
|
+
}
|
|
359
|
+
function inherits_consultation(call) {
|
|
360
|
+
if (!INVESTIGATION_AGENTS.has(agent_name(call))) return false;
|
|
361
|
+
return is_consultation(dispatched_model(call));
|
|
362
|
+
}
|
|
363
|
+
function is_delegated_investigation(call) {
|
|
364
|
+
return is_investigation_dispatch(call) && !is_consultation(dispatched_model(call));
|
|
365
|
+
}
|
|
366
|
+
var TOOL_TESTS = new Map([
|
|
367
|
+
["Read", is_broad_read],
|
|
368
|
+
["Grep", is_broad_grep],
|
|
369
|
+
["Bash", (call) => is_broad_command2(time_shell.bash_command(call.input))],
|
|
370
|
+
...[...agent_model.SUBAGENT_TOOLS].map((name) => [
|
|
371
|
+
name,
|
|
372
|
+
inherits_consultation
|
|
373
|
+
])
|
|
374
|
+
]);
|
|
375
|
+
function is_broad_investigation(call) {
|
|
376
|
+
if (ALWAYS_BROAD2.has(call.name)) return true;
|
|
377
|
+
return TOOL_TESTS.get(call.name)?.(call) === true;
|
|
378
|
+
}
|
|
379
|
+
var broad_investigation = { READ_LINE_CAP: READ_LINE_CAP2, is_broad_investigation, is_delegated_investigation };
|
|
380
|
+
|
|
381
|
+
// scripts/rules/consultation-investigation.ts
|
|
382
|
+
var { ANTHROPIC_PROVIDER, OPENAI_PROVIDER } = agent_provider;
|
|
383
|
+
var { READ_LINE_CAP: READ_LINE_CAP3, is_broad_investigation: is_broad_investigation2, is_delegated_investigation: is_delegated_investigation2 } = broad_investigation;
|
|
384
|
+
var DELEGATION = ".claude/skills/workflow-commands/delegation.md";
|
|
385
|
+
var TOPIC = `\`${DELEGATION}\` \u2192 "A consultation main line hands its investigation to a unit"`;
|
|
386
|
+
var NARROW_EXAMPLE = `\`Read\` of a file or an \`offset\` / \`limit\` range <= ${String(READ_LINE_CAP3)} lines, \`grep -n <pattern> <one file>\``;
|
|
387
|
+
var QUIET = `a launched session (lane, reviewer, scheduler), a delegated unit, a main line at or below the investigation rate (Sonnet, Haiku, Sol), on another provider's or an unnamed model, or a narrow check (${NARROW_EXAMPLE})`;
|
|
388
|
+
function reason(provider, dispatch) {
|
|
389
|
+
const model = agent_role_profile.investigation_model(provider);
|
|
390
|
+
return (
|
|
391
|
+
// The dispatch opens the reason, so the Claude and Codex rows keep distinct signatures.
|
|
392
|
+
`\u26D4 consultation investigation \u2192 ${dispatch} on \`${model}\`: the consultation main line hands broad investigation to it, and it returns \`file:line\` citations. Keep only a narrow check here \u2014 ${NARROW_EXAMPLE}. It fires on every occurrence (\`${DELEGATION}\`).`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
function is_handed_on(provider, model) {
|
|
396
|
+
return model !== void 0 && agent_consultation_model.rated_provider(model) === provider;
|
|
397
|
+
}
|
|
398
|
+
function not_consulted_on(provider) {
|
|
399
|
+
return function not_consulted(tail, _call, run) {
|
|
400
|
+
if (run.is_unit === true || agent_session_role.is_launched(run.context.env)) return true;
|
|
401
|
+
return !is_handed_on(provider, main_model_reader.main_model(tail, run.model));
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function row(id, provider, dispatch, about) {
|
|
405
|
+
return {
|
|
406
|
+
id,
|
|
407
|
+
is_trigger: is_broad_investigation2,
|
|
408
|
+
// Read at delivery, not import: the hook loads `.env`, which may name the investigation model,
|
|
409
|
+
// only after the rows are imported.
|
|
410
|
+
get reason() {
|
|
411
|
+
return reason(provider, dispatch);
|
|
412
|
+
},
|
|
413
|
+
already_satisfied: not_consulted_on(provider),
|
|
414
|
+
decide: () => true,
|
|
415
|
+
keeps: is_delegated_investigation2,
|
|
416
|
+
about
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
var ROW2 = row("consultation-investigation", ANTHROPIC_PROVIDER, "an `investigator`", {
|
|
420
|
+
title: "Consultation main line delegates investigation (Claude)",
|
|
421
|
+
topic: TOPIC,
|
|
422
|
+
fires: "a broad investigation call \u2014 `Glob`, `WebFetch`, a `Read` past the line cap, a tree `Grep` or shell search, an investigation dispatch inheriting the model \u2014 on the consultation main line on Opus or Fable. Every occurrence",
|
|
423
|
+
quiet: QUIET
|
|
424
|
+
});
|
|
425
|
+
var CODEX_ROW = row("consultation-investigation-codex", OPENAI_PROVIDER, "a Codex subagent", {
|
|
426
|
+
title: "Consultation main line delegates investigation (Codex)",
|
|
427
|
+
topic: TOPIC,
|
|
428
|
+
fires: "a broad shell investigation on the consultation main line on Codex Astra. Every occurrence",
|
|
429
|
+
quiet: QUIET
|
|
430
|
+
});
|
|
431
|
+
var consultation_investigation = { CODEX_ROW, ROW: ROW2 };
|
|
432
|
+
|
|
433
|
+
// scripts/rules/credential-body.ts
|
|
434
|
+
var TYPED_ESCAPE = /\\./gu;
|
|
435
|
+
function sent_text(source) {
|
|
436
|
+
if (source.kind === "text") return source.value;
|
|
437
|
+
return token_shape.decoded(file_reader.read_if_readable(source.value) ?? "");
|
|
438
|
+
}
|
|
439
|
+
function holds_token(text) {
|
|
440
|
+
return [text, text.replaceAll(TYPED_ESCAPE, () => " ")].some(
|
|
441
|
+
(reading) => token_shape.holds(reading)
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
function is_credential_segment(segment) {
|
|
445
|
+
const args = git_argv.arguments_of(segment, direct_pr_create.GH_COMMAND);
|
|
446
|
+
if (args === void 0) return false;
|
|
447
|
+
const call = [direct_pr_create.GH_COMMAND, ...args].join(" ");
|
|
448
|
+
if (!gh_api.is_gh_api(call)) return false;
|
|
449
|
+
return gh_api.field_sources(call).some((source) => holds_token(sent_text(source)));
|
|
450
|
+
}
|
|
451
|
+
function sends_credential(command) {
|
|
452
|
+
return direct_pr_create.has_run_segment(command, is_credential_segment);
|
|
453
|
+
}
|
|
454
|
+
var CREDENTIAL_BODY_REASON = "\u26D4 credential in a gh api body: a field value, or the file `-F key=@<path>` / `--input <path>` names, holds a token-shaped string (`scripts/lib/token-shape.ts`), and the call would publish it. Remove the credential from the body and send it again. **This rule fires on every occurrence, not once per run.**";
|
|
455
|
+
var ROW3 = {
|
|
456
|
+
id: "credential-body",
|
|
457
|
+
is_trigger: bash_triggers.on_bash_command(sends_credential),
|
|
458
|
+
reason: CREDENTIAL_BODY_REASON,
|
|
459
|
+
decide: () => true,
|
|
460
|
+
about: {
|
|
461
|
+
title: "Credential in a body",
|
|
462
|
+
topic: "`shell-body.md`",
|
|
463
|
+
fires: "a `gh api` call whose field value, or the file `-F key=@<path>` / `--input <path>` names, holds a token-shaped string (`token-shape.ts`). Every occurrence",
|
|
464
|
+
quiet: "a body with no credential, or a body sent through kit, which runs the same scan"
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var credential_body = { CREDENTIAL_BODY_REASON, ROW: ROW3, sends_credential };
|
|
468
|
+
|
|
469
|
+
// scripts/delegation/investigation-guard.ts
|
|
470
|
+
var SWITCH_ENV_KEY = "JOSH_INVESTIGATION_GUARD";
|
|
471
|
+
var STAMP_PREFIX = "josh-investigation-guard-";
|
|
472
|
+
function should_block(tail, call, refused_at_ms2, run) {
|
|
473
|
+
const { cwd, env } = run.context;
|
|
474
|
+
if (agent_session_role.is_reviewer(env) || lane_guard_policy.mode_here("investigation", cwd, env) !== "refuse") {
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
return investigation_reads.should_block(tail, call, refused_at_ms2, run);
|
|
478
|
+
}
|
|
479
|
+
function is_candidate(call, context) {
|
|
480
|
+
return investigation_reads.is_refusable_call(call, context.cwd);
|
|
481
|
+
}
|
|
482
|
+
var GUARD = hook_decision.create_transcript_guard({
|
|
483
|
+
prefix: STAMP_PREFIX,
|
|
484
|
+
switch_key: SWITCH_ENV_KEY,
|
|
485
|
+
is_candidate,
|
|
486
|
+
should_block,
|
|
487
|
+
reason: investigation_reads.REASON
|
|
488
|
+
});
|
|
489
|
+
var investigation_refusal = GUARD.refusal;
|
|
490
|
+
var investigation_outcome = GUARD.outcome;
|
|
491
|
+
var { is_enabled, refusal_path } = GUARD;
|
|
492
|
+
var { deny_envelope, load_environment_file, DISABLED_VALUES } = hook_decision;
|
|
493
|
+
var STAMP = hook_decision.create_refusal_stamp(STAMP_PREFIX);
|
|
494
|
+
function will_refuse(tail, call, run) {
|
|
495
|
+
if (!is_enabled(run.context.env) || !is_candidate(call, run.context)) return false;
|
|
496
|
+
return should_block(tail, call, STAMP.last_ms(STAMP.path(run.transcript)), run);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// scripts/hooks/batch-guard.ts
|
|
500
|
+
var SWITCH_ENV_KEY2 = "JOSH_BATCH_GUARD";
|
|
501
|
+
function is_batch_candidate(call) {
|
|
502
|
+
return time_batch_guard.is_guarded_call(call) || time_batch_guard.is_notice_call(call);
|
|
503
|
+
}
|
|
504
|
+
function batching_mode(context) {
|
|
505
|
+
return lane_guard_policy.mode_here("batching", context.cwd, context.env);
|
|
506
|
+
}
|
|
507
|
+
function is_search_refused_here(context) {
|
|
508
|
+
return lane_guard_policy.mode_here("search-batching", context.cwd, context.env) === "refuse";
|
|
509
|
+
}
|
|
510
|
+
function is_candidate2(call, context) {
|
|
511
|
+
if (batching_mode(context) !== "off") return is_batch_candidate(call);
|
|
512
|
+
return is_search_refused_here(context) && time_batch_guard.is_search_chain_call(call);
|
|
513
|
+
}
|
|
514
|
+
function should_block_here(tail, call, refused_at_ms2, run) {
|
|
515
|
+
if (batching_mode(run.context) === "refuse") {
|
|
516
|
+
return time_batch_guard.should_block(tail, call, refused_at_ms2);
|
|
517
|
+
}
|
|
518
|
+
if (!is_search_refused_here(run.context) || will_refuse(tail, call, run)) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
return time_batch_guard.should_block_search_chain(tail, call, refused_at_ms2);
|
|
522
|
+
}
|
|
523
|
+
function should_notify_here(tail, call, notified_at_ms, run) {
|
|
524
|
+
if (time_batch_guard.should_notify(tail, call, notified_at_ms)) return true;
|
|
525
|
+
if (batching_mode(run.context) !== "notice") return false;
|
|
526
|
+
return time_batch_guard.should_block(
|
|
527
|
+
tail,
|
|
528
|
+
call,
|
|
529
|
+
notified_at_ms,
|
|
530
|
+
time_batch_guard.NOTICE_REFIRE_EVERY
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
function notice_text(call, tail) {
|
|
534
|
+
const base = time_batch_guard.is_notice_call(call) ? time_batch_guard.NOTICE : time_batch_guard.LANE_NOTICE;
|
|
535
|
+
return `${base}${time_batch_guard.recent_candidates(tail)}`;
|
|
536
|
+
}
|
|
537
|
+
var GUARD2 = hook_decision.create_transcript_guard({
|
|
538
|
+
prefix: time_batch_guard.STAMP_PREFIX,
|
|
539
|
+
switch_key: SWITCH_ENV_KEY2,
|
|
540
|
+
is_candidate: is_candidate2,
|
|
541
|
+
should_block: should_block_here,
|
|
542
|
+
reason: time_batch_guard.REASON,
|
|
543
|
+
// The whole-file write is notified rather than refused, on a record of its own so it never spends
|
|
544
|
+
// the refusal's stamp; the refusable-call notice fires only under a `notice`-mode batching row.
|
|
545
|
+
notify: {
|
|
546
|
+
prefix: time_batch_guard.NOTICE_STAMP_PREFIX,
|
|
547
|
+
should_notify: should_notify_here,
|
|
548
|
+
text: notice_text
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
var batch_refusal = GUARD2.refusal;
|
|
552
|
+
var batch_outcome = GUARD2.outcome;
|
|
553
|
+
var { is_enabled: is_enabled2, refusal_path: refusal_path2 } = GUARD2;
|
|
554
|
+
var notice_refusal_path = hook_decision.create_refusal_stamp(
|
|
555
|
+
time_batch_guard.NOTICE_STAMP_PREFIX
|
|
556
|
+
).path;
|
|
557
|
+
var { deny_envelope: deny_envelope2, load_environment_file: load_environment_file2, DISABLED_VALUES: DISABLED_VALUES2 } = hook_decision;
|
|
558
|
+
var STAMP2 = hook_decision.create_refusal_stamp(time_batch_guard.STAMP_PREFIX);
|
|
559
|
+
function will_refuse2(tail, call, run) {
|
|
560
|
+
if (!is_enabled2(run.context.env) || !is_candidate2(call, run.context)) return false;
|
|
561
|
+
return should_block_here(tail, call, STAMP2.last_ms(STAMP2.path(run.transcript)), run);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// scripts/rules/delivered-rules-guard.ts
|
|
565
|
+
var SWITCH_ENV_KEY3 = "JOSH_RULE_GUARD";
|
|
566
|
+
var STAMP_PREFIX2 = "josh-rule-guard-";
|
|
567
|
+
var BATCH_STAMP = hook_decision.create_refusal_stamp(time_batch_guard.STAMP_PREFIX);
|
|
568
|
+
var BATCH_REFUSAL_WINDOW_MS = 1e4;
|
|
569
|
+
function will_batch_guard_refuse(tail, call, run) {
|
|
570
|
+
const refused_at_ms2 = BATCH_STAMP.last_ms(BATCH_STAMP.path(run.transcript));
|
|
571
|
+
if (run.now_ms - refused_at_ms2 < BATCH_REFUSAL_WINDOW_MS) return true;
|
|
572
|
+
return will_refuse2(tail, call, run);
|
|
573
|
+
}
|
|
574
|
+
function is_first_delivery(tail, call, delivered_at_ms, run) {
|
|
575
|
+
return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run);
|
|
576
|
+
}
|
|
577
|
+
function delivery_decision(rule) {
|
|
578
|
+
const { decide: decide5, already_satisfied } = rule;
|
|
579
|
+
if (decide5 === void 0 && already_satisfied === void 0) return is_first_delivery;
|
|
580
|
+
return function should_block2(tail, call, delivered_at_ms, run) {
|
|
581
|
+
if (already_satisfied?.(tail, call, run) === true) return false;
|
|
582
|
+
if (decide5 === void 0) return true;
|
|
583
|
+
return decide5(call, run, !will_batch_guard_refuse(tail, call, run), delivered_at_ms);
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
function call_only(row2) {
|
|
587
|
+
return { ...row2, is_trigger: (call) => row2.is_trigger(call) };
|
|
588
|
+
}
|
|
589
|
+
function prefix_of(rule_id) {
|
|
590
|
+
return `${STAMP_PREFIX2}${rule_id}-`;
|
|
591
|
+
}
|
|
592
|
+
function guard_of(rule) {
|
|
593
|
+
return hook_decision.create_transcript_guard({
|
|
594
|
+
prefix: prefix_of(rule.id),
|
|
595
|
+
switch_key: SWITCH_ENV_KEY3,
|
|
596
|
+
is_candidate: (call, context) => rule.is_trigger(call, context),
|
|
597
|
+
should_block: delivery_decision(rule),
|
|
598
|
+
reason: rule.reason
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
var delivered_rules_guard = { SWITCH_ENV_KEY: SWITCH_ENV_KEY3, call_only, guard_of, prefix_of };
|
|
602
|
+
|
|
603
|
+
// scripts/rules/direct-filing.ts
|
|
604
|
+
var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path>`, never with `gh api \u2026/issues` or `gh issue create` \u2014 it runs the scout, the lint, the labels and `epic:bundle` itself. It fires on every occurrence (`docs/josh-commands-backlog.md` \u2192 "`josh issue:file`").';
|
|
605
|
+
function is_any_filing(command) {
|
|
606
|
+
return bash_triggers.is_direct_filing(command) || bash_triggers.is_issue_filing(command);
|
|
607
|
+
}
|
|
608
|
+
var ROW4 = {
|
|
609
|
+
id: "direct-filing",
|
|
610
|
+
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_direct_filing),
|
|
611
|
+
reason: DIRECT_FILING_REASON,
|
|
612
|
+
decide: () => true,
|
|
613
|
+
keeps: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
614
|
+
reaches: bash_triggers.on_bash_command(is_any_filing),
|
|
615
|
+
about: {
|
|
616
|
+
title: "Direct filing",
|
|
617
|
+
topic: "`docs/josh-commands-backlog.md` \u2192 `josh issue:file`",
|
|
618
|
+
fires: "a `Bash` that creates an Issue outside `josh issue:file` (`gh issue create`, or a POST with `title` to `\u2026/issues`); the refusal hands over the `pnpm josh issue:file` form. Every occurrence",
|
|
619
|
+
quiet: "filing through `pnpm josh issue:file` \u2014 the duplicate scan, the body check and `epic:bundle` all run"
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var direct_filing = { DIRECT_FILING_REASON, ROW: ROW4 };
|
|
623
|
+
|
|
624
|
+
// scripts/rules/direct-git-status.ts
|
|
625
|
+
var GIT_PROGRAM = "git";
|
|
626
|
+
var STATUS_SUBCOMMAND = "status";
|
|
627
|
+
var COVERED_FLAGS = /* @__PURE__ */ new Set([
|
|
628
|
+
"-s",
|
|
629
|
+
"--short",
|
|
630
|
+
"-b",
|
|
631
|
+
"--branch",
|
|
632
|
+
"--porcelain"
|
|
633
|
+
]);
|
|
634
|
+
var SHORT_BRANCH_CLUSTER = /^-[sb]+$/u;
|
|
635
|
+
function is_covered_flag(argument) {
|
|
636
|
+
return COVERED_FLAGS.has(argument) || SHORT_BRANCH_CLUSTER.test(argument);
|
|
637
|
+
}
|
|
638
|
+
function is_covered_call(call) {
|
|
639
|
+
const [subcommand, ...flags] = call.args;
|
|
640
|
+
const is_status = call.name === GIT_PROGRAM && subcommand === STATUS_SUBCOMMAND;
|
|
641
|
+
return is_status && flags.every((flag) => is_covered_flag(flag));
|
|
642
|
+
}
|
|
643
|
+
function is_covered_status(command) {
|
|
644
|
+
return command_memory.tool_calls_of(command).some((call) => is_covered_call(call));
|
|
645
|
+
}
|
|
646
|
+
var TREE_STATUS = /* @__PURE__ */ new Set(["tree:status"]);
|
|
647
|
+
function is_tree_status(command) {
|
|
648
|
+
return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, TREE_STATUS));
|
|
649
|
+
}
|
|
650
|
+
function is_any_status(command) {
|
|
651
|
+
return is_covered_status(command) || is_tree_status(command);
|
|
652
|
+
}
|
|
653
|
+
var DIRECT_GIT_STATUS_REASON = '\u26D4 direct git status: pnpm josh tree:status\nRun that in place of this `git status` \u2014 it prints `git status --porcelain --branch` with the stash list and the run hold beside it (`docs/josh-commands-backlog.md` \u2192 "`josh tree:status`"). A `git -C <dir> status` or one narrowed by a pathspec or another flag passes. Once per run: the reissue passes.';
|
|
654
|
+
var ROW5 = {
|
|
655
|
+
id: "direct-git-status",
|
|
656
|
+
is_trigger: bash_triggers.on_bash_command(is_covered_status),
|
|
657
|
+
reason: DIRECT_GIT_STATUS_REASON,
|
|
658
|
+
keeps: bash_triggers.on_bash_command(is_tree_status),
|
|
659
|
+
reaches: bash_triggers.on_bash_command(is_any_status),
|
|
660
|
+
about: {
|
|
661
|
+
title: "Direct git status",
|
|
662
|
+
topic: '`docs/josh-commands-backlog.md` \u2192 "`josh tree:status`"',
|
|
663
|
+
fires: "a `git status` with no flag or only `-s` / `--short` / `-b` / `--branch` / `--porcelain` (bare, wrapped or inside `sh -c`); it points at `pnpm josh tree:status`. Once per run",
|
|
664
|
+
quiet: "`pnpm josh tree:status`, a global flag before the subcommand (`git -C <dir> status`), or a pathspec or any other flag"
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
var direct_git_status = { DIRECT_GIT_STATUS_REASON, ROW: ROW5, is_covered_status };
|
|
668
|
+
|
|
669
|
+
// scripts/rules/direct-issue-comment.ts
|
|
670
|
+
import path3 from "node:path";
|
|
671
|
+
|
|
672
|
+
// scripts/rules/standalone-gh-call.ts
|
|
673
|
+
var WORD_SEPARATOR = " ";
|
|
674
|
+
var PLAIN_WORD = /^[\w./=-]+$/u;
|
|
675
|
+
function line_of(words) {
|
|
676
|
+
return words.join(WORD_SEPARATOR);
|
|
677
|
+
}
|
|
678
|
+
function is_plain_word(word) {
|
|
679
|
+
return PLAIN_WORD.test(word);
|
|
680
|
+
}
|
|
681
|
+
function split_issue(rest) {
|
|
682
|
+
const issues = rest.filter((word) => issue_number_shape.is_issue_number(word));
|
|
683
|
+
const [issue] = issues;
|
|
684
|
+
const flags = line_of(rest.filter((word) => !issue_number_shape.is_issue_number(word)));
|
|
685
|
+
return issue !== void 0 && issues.length === 1 ? { issue, flags } : void 0;
|
|
686
|
+
}
|
|
687
|
+
function call_of(line, lead) {
|
|
688
|
+
const words = line.trim().split(WORD_SEPARATOR);
|
|
689
|
+
const is_lead = line_of(words.slice(0, lead.length)) === line_of(lead);
|
|
690
|
+
return is_lead && words.every((word) => is_plain_word(word)) ? split_issue(words.slice(lead.length)) : void 0;
|
|
691
|
+
}
|
|
692
|
+
function input_of(call, command_of) {
|
|
693
|
+
if (call.name !== cost_blocks.BASH_TOOL || !json_value.is_record(call.input)) return void 0;
|
|
694
|
+
const command = command_of(time_shell.bash_command(call.input));
|
|
695
|
+
return command === void 0 ? void 0 : { ...call.input, command };
|
|
696
|
+
}
|
|
697
|
+
var standalone_gh_call = { call_of, input_of, is_plain_word, line_of };
|
|
698
|
+
|
|
699
|
+
// scripts/rules/direct-issue-comment.ts
|
|
700
|
+
var ISSUE_GROUP = "issue";
|
|
701
|
+
var COMMENT_ACTION = "comment";
|
|
702
|
+
var API_SUBCOMMAND = "api";
|
|
703
|
+
var REPO_SELECTOR = /^(?:-R|--repo)(?:=|$)/u;
|
|
704
|
+
var SEGMENT = String.raw`[^/\s'"]+`;
|
|
705
|
+
var QUOTE = `['"]?`;
|
|
706
|
+
var API_ORIGIN = String.raw`(?:https://api\.github\.com)?/?`;
|
|
707
|
+
var COMMENTS_PATH = new RegExp(
|
|
708
|
+
`^${QUOTE}${API_ORIGIN}repos/${SEGMENT}/${SEGMENT}/issues/${SEGMENT}/comments/?${QUOTE}$`,
|
|
709
|
+
"u"
|
|
710
|
+
);
|
|
711
|
+
function is_comment_cli_call(args) {
|
|
712
|
+
if (args.some((argument) => REPO_SELECTOR.test(argument))) return false;
|
|
713
|
+
const [group, action] = direct_pr_create.positional_words(
|
|
714
|
+
args,
|
|
715
|
+
direct_pr_create.VALUE_TAKING_FLAGS
|
|
716
|
+
);
|
|
717
|
+
return group === ISSUE_GROUP && action === COMMENT_ACTION;
|
|
718
|
+
}
|
|
719
|
+
function is_own_comments_endpoint(endpoint, resolve_own) {
|
|
720
|
+
const target = gh_api.repo_target(endpoint);
|
|
721
|
+
if (target === void 0 || !COMMENTS_PATH.test(endpoint)) return false;
|
|
722
|
+
return gh_api.is_owner_placeholder(target.owner) || repo_party.is_own_repository(target, resolve_own());
|
|
723
|
+
}
|
|
724
|
+
function is_comment_api_call(args, resolve_own) {
|
|
725
|
+
const call = [direct_pr_create.GH_COMMAND, ...args].join(" ");
|
|
726
|
+
if (!gh_api.is_gh_api(call) || !gh_api.is_write(call)) return false;
|
|
727
|
+
const [subcommand, endpoint = ""] = direct_pr_create.positional_words(
|
|
728
|
+
args,
|
|
729
|
+
direct_pr_create.API_VALUE_TAKING_FLAGS
|
|
730
|
+
);
|
|
731
|
+
return subcommand === API_SUBCOMMAND && is_own_comments_endpoint(endpoint, resolve_own);
|
|
732
|
+
}
|
|
733
|
+
function is_comment_segment(segment, resolve_own) {
|
|
734
|
+
const args = git_argv.arguments_of(segment, direct_pr_create.GH_COMMAND);
|
|
735
|
+
if (args === void 0 || direct_pr_create.is_help_call(args)) return false;
|
|
736
|
+
return is_comment_cli_call(args) || is_comment_api_call(args, resolve_own);
|
|
737
|
+
}
|
|
738
|
+
function is_direct_issue_comment(command, resolve_own = repo_party.current_repository) {
|
|
739
|
+
return direct_pr_create.has_run_segment(
|
|
740
|
+
command,
|
|
741
|
+
(segment) => is_comment_segment(segment, resolve_own)
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
function is_direct_issue_comment_in(command, context) {
|
|
745
|
+
return is_direct_issue_comment(
|
|
746
|
+
command,
|
|
747
|
+
() => repo_party.current_repository(context.cwd, context.env)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
var COMMENT_NAMES = /* @__PURE__ */ new Set(["issue:comment"]);
|
|
751
|
+
function is_josh_comment(command) {
|
|
752
|
+
return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, COMMENT_NAMES));
|
|
753
|
+
}
|
|
754
|
+
function is_any_comment(command) {
|
|
755
|
+
return is_direct_issue_comment(command) || is_josh_comment(command);
|
|
756
|
+
}
|
|
757
|
+
var DIRECT_ISSUE_COMMENT_REASON = "\u26D4 direct issue comment: `gh issue comment` (or a `gh api` write to `\u2026/issues/<N>/comments`) posts without the run marker, so the run reads its own comment as a person updating the issue \u2014 the review goes stale and `pnpm josh followup` refuses the merge. Post with `pnpm josh issue:comment <N> --body-file <path>` instead (`shell-body.md`). **This rule fires on every occurrence, not once per run.**";
|
|
758
|
+
var COMMENT_WORDS = ["gh", ISSUE_GROUP, COMMENT_ACTION];
|
|
759
|
+
var BODY_FILE_SPELLING = /^(?:(?:-F|--body-file) |--body-file=)(?<file>[\w./-]+)$/u;
|
|
760
|
+
var STDIN_PATH = "-";
|
|
761
|
+
var ISSUE_COMMENT_COMMAND = "pnpm josh issue:comment";
|
|
762
|
+
var BODY_FILE_FLAG = "--body-file";
|
|
763
|
+
function body_file_of(flags, cwd) {
|
|
764
|
+
const file = BODY_FILE_SPELLING.exec(flags)?.groups?.["file"];
|
|
765
|
+
return file === void 0 || file === STDIN_PATH ? void 0 : path3.resolve(cwd, file);
|
|
766
|
+
}
|
|
767
|
+
function issue_comment_of(comment, cwd) {
|
|
768
|
+
const resolved2 = body_file_of(comment.flags, cwd);
|
|
769
|
+
return resolved2 !== void 0 && standalone_gh_call.is_plain_word(resolved2) ? standalone_gh_call.line_of([ISSUE_COMMENT_COMMAND, comment.issue, BODY_FILE_FLAG, resolved2]) : void 0;
|
|
770
|
+
}
|
|
771
|
+
function issue_comment_command(command, context) {
|
|
772
|
+
const comment = standalone_gh_call.call_of(command, COMMENT_WORDS);
|
|
773
|
+
const post = comment === void 0 ? void 0 : issue_comment_of(comment, context.cwd);
|
|
774
|
+
return post !== void 0 && is_direct_issue_comment_in(command.trim(), context) ? post : void 0;
|
|
775
|
+
}
|
|
776
|
+
function issue_comment_input(call, _tail, context) {
|
|
777
|
+
return standalone_gh_call.input_of(call, (command) => issue_comment_command(command, context));
|
|
778
|
+
}
|
|
779
|
+
var ISSUE_COMMENT_NOTE = "\u21BB direct issue comment: this `gh issue comment <N> --body-file <path>` ran as `pnpm josh issue:comment <N> --body-file <path>`, which posts the run marker (`shell-body.md`).";
|
|
780
|
+
var ROW6 = {
|
|
781
|
+
id: "direct-issue-comment",
|
|
782
|
+
is_trigger: bash_triggers.on_bash_command(is_direct_issue_comment_in),
|
|
783
|
+
reason: DIRECT_ISSUE_COMMENT_REASON,
|
|
784
|
+
decide: () => true,
|
|
785
|
+
keeps: bash_triggers.on_bash_command(is_josh_comment),
|
|
786
|
+
reaches: bash_triggers.on_bash_command(is_any_comment),
|
|
787
|
+
rewrite: issue_comment_input,
|
|
788
|
+
rewrite_note: ISSUE_COMMENT_NOTE,
|
|
789
|
+
about: {
|
|
790
|
+
title: "Direct issue comment",
|
|
791
|
+
topic: "`shell-body.md`",
|
|
792
|
+
fires: "a comment to the checkout's own repository outside `josh issue:comment` (`gh issue comment` with no `--repo`, or a `gh api` write to `repos/<owner>/<r>/issues/<N>/comments` whose owner is the `{owner}` placeholder or the checkout's own `owner/repo` spelled out); it points at `pnpm josh issue:comment`. A line that is only `gh issue comment <N>` with one `--body-file` / `-F <path>` is rewritten to `pnpm josh issue:comment <N> --body-file <path>`; every other form is refused, every occurrence",
|
|
793
|
+
quiet: "`pnpm josh issue:comment`, or reading the comments \u2014 the path that writes the run marker"
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
var direct_issue_comment = {
|
|
797
|
+
DIRECT_ISSUE_COMMENT_REASON,
|
|
798
|
+
ISSUE_COMMENT_NOTE,
|
|
799
|
+
ROW: ROW6,
|
|
800
|
+
is_direct_issue_comment,
|
|
801
|
+
issue_comment_command
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
// scripts/rules/prior-comment-read.ts
|
|
805
|
+
var ISSUE_PATH_NUMBER = /issues\/(\d+)/gu;
|
|
806
|
+
var ISSUE_VIEW_NUMBER = /\bissue\s+view\s+(?:-{1,2}\S+\s+)*(\d+)/u;
|
|
807
|
+
var ISSUE_READ_ARGUMENTS = /\b(?:issue:read|run:entry|run:prep)\b([\s\d]*)/u;
|
|
808
|
+
var COMMENTS_ENDPOINT = /issues\/\d+\/comments\b/u;
|
|
809
|
+
var VIEW_WITH_COMMENTS = /\bissue\s+view\b[^\n]*--comments\b/u;
|
|
810
|
+
var ISSUE_READ_COMMAND = /\b(?:issue:read|run:entry|run:prep)\b/u;
|
|
811
|
+
function add_issue_read_numbers(command, numbers) {
|
|
812
|
+
const match = ISSUE_READ_ARGUMENTS.exec(command);
|
|
813
|
+
if (match === null) return;
|
|
814
|
+
const tokens = (match[1] ?? "").split(/\s+/u);
|
|
815
|
+
for (const token of tokens) {
|
|
816
|
+
if (issue_number_shape.ISSUE_NUMBER_PATTERN.test(token)) numbers.add(Number(token));
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
function issue_numbers_of(command) {
|
|
820
|
+
const numbers = /* @__PURE__ */ new Set();
|
|
821
|
+
for (const match of command.matchAll(ISSUE_PATH_NUMBER)) numbers.add(Number(match[1]));
|
|
822
|
+
const view = ISSUE_VIEW_NUMBER.exec(command);
|
|
823
|
+
if (view !== null) numbers.add(Number(view[1]));
|
|
824
|
+
add_issue_read_numbers(command, numbers);
|
|
825
|
+
return numbers;
|
|
826
|
+
}
|
|
827
|
+
function fetches_comments(command) {
|
|
828
|
+
return COMMENTS_ENDPOINT.test(command) || VIEW_WITH_COMMENTS.test(command) || ISSUE_READ_COMMAND.test(command);
|
|
829
|
+
}
|
|
830
|
+
function reads_target_comments(command, target) {
|
|
831
|
+
if (!fetches_comments(command)) return false;
|
|
832
|
+
for (const number of issue_numbers_of(command)) {
|
|
833
|
+
if (target.has(number)) return true;
|
|
834
|
+
}
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
function already_read_comments_for(tail, command) {
|
|
838
|
+
const target = issue_numbers_of(command);
|
|
839
|
+
if (target.size === 0) return false;
|
|
840
|
+
return tail_commands.prior_bash_commands(tail).some((prior) => reads_target_comments(prior, target));
|
|
841
|
+
}
|
|
842
|
+
function already_read_for_call(tail, call) {
|
|
843
|
+
if (call.name !== cost_blocks.BASH_TOOL) return false;
|
|
844
|
+
return already_read_comments_for(tail, time_shell.bash_command(call.input));
|
|
845
|
+
}
|
|
846
|
+
var prior_comment_read = {
|
|
847
|
+
already_read_comments_for,
|
|
848
|
+
already_read_for_call,
|
|
849
|
+
fetches_comments,
|
|
850
|
+
issue_numbers_of
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// scripts/rules/issue-comments.ts
|
|
854
|
+
var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
|
|
855
|
+
var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
|
|
856
|
+
var FETCHES_COMMENTS = /--comments\b|--json[=\s]['"]?[\w,]*\bcomments\b|\/comments\b/u;
|
|
857
|
+
var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
|
|
858
|
+
var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
|
|
859
|
+
var NAMES_THE_BODY = /\bbody\b/u;
|
|
860
|
+
var NAMES_A_FIELD = /(?:^|[.{,\s'"])[A-Za-z_]/u;
|
|
861
|
+
function projects_away_body(segment) {
|
|
862
|
+
const value = FIELD_PROJECTION.exec(segment)?.[1];
|
|
863
|
+
return value !== void 0 && NAMES_A_FIELD.test(value) && !NAMES_THE_BODY.test(value);
|
|
864
|
+
}
|
|
865
|
+
function is_body_read_segment(segment) {
|
|
866
|
+
if (projects_away_body(segment)) return false;
|
|
867
|
+
if (ISSUE_VIEW_COMMAND.test(segment)) return true;
|
|
868
|
+
return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
|
|
869
|
+
}
|
|
870
|
+
function fetches_issue_comments(segment) {
|
|
871
|
+
if (!FETCHES_COMMENTS.test(segment)) return false;
|
|
872
|
+
return ISSUE_VIEW_COMMAND.test(segment) || gh_api.is_gh_api(segment) && ISSUES_PATH.test(segment);
|
|
873
|
+
}
|
|
874
|
+
function is_body_only_issue_read(command) {
|
|
875
|
+
const segments = shell_segments.segments_of(command);
|
|
876
|
+
if (segments.some((segment) => fetches_issue_comments(segment))) return false;
|
|
877
|
+
return segments.some((segment) => is_body_read_segment(segment));
|
|
878
|
+
}
|
|
879
|
+
function reads_issue_comments(command) {
|
|
880
|
+
return shell_segments.segments_of(command).some((segment) => fetches_issue_comments(segment));
|
|
881
|
+
}
|
|
882
|
+
var RULE_DOC = "`.claude/skills/workflow-commands/issue-comments.md`";
|
|
883
|
+
var COMMENTS_FALLBACK = "gh api repos/<owner>/<repo>/issues/<N>/comments";
|
|
884
|
+
var ISSUE_COMMENTS_REASON = `\u26D4 issue comments: pnpm josh issue:read <N>
|
|
885
|
+
Run that in place of this read \u2014 an Issue's comments are part of it, and a later comment supersedes the body it contradicts; refused until that command is on the transcript. Without josh, read \`${COMMENTS_FALLBACK}\` beside the body (${RULE_DOC}).`;
|
|
886
|
+
var COMMENTS_JQ = String.raw`'.[] | "— \(.user.login) \(.created_at)\n\(.body)\n"'`;
|
|
887
|
+
var COMMENTS_NOTE = `\u21BB issue comments: this read returned only the Issue's body, so the Issue's comments were fetched on the same line \u2014 a later comment supersedes the body it contradicts (${RULE_DOC}).`;
|
|
888
|
+
function rewritable_path(command) {
|
|
889
|
+
const read = shell_segments.first_of(command);
|
|
890
|
+
if (read === void 0 || ISSUE_VIEW_COMMAND.test(read)) return void 0;
|
|
891
|
+
return is_body_read_segment(read) ? ISSUE_BODY_PATH.exec(read)?.[0] : void 0;
|
|
892
|
+
}
|
|
893
|
+
function comments_command(command) {
|
|
894
|
+
if (!is_body_only_issue_read(command)) return void 0;
|
|
895
|
+
const path6 = rewritable_path(command);
|
|
896
|
+
if (path6 === void 0) return void 0;
|
|
897
|
+
return `gh api --paginate ${path6}/comments --jq ${COMMENTS_JQ}
|
|
898
|
+
${command}`;
|
|
899
|
+
}
|
|
900
|
+
function comments_input(call, tail) {
|
|
901
|
+
if (call.name !== cost_blocks.BASH_TOOL || !json_value.is_record(call.input)) return void 0;
|
|
902
|
+
const command = comments_command(time_shell.bash_command(call.input));
|
|
903
|
+
if (command === void 0 || prior_comment_read.already_read_for_call(tail(), call)) {
|
|
904
|
+
return void 0;
|
|
905
|
+
}
|
|
906
|
+
return { ...call.input, command };
|
|
907
|
+
}
|
|
908
|
+
var ROW7 = {
|
|
909
|
+
id: "issue-comments",
|
|
910
|
+
is_trigger: bash_triggers.on_bash_command(is_body_only_issue_read),
|
|
911
|
+
reason: ISSUE_COMMENTS_REASON,
|
|
912
|
+
already_satisfied: prior_comment_read.already_read_for_call,
|
|
913
|
+
keeps: bash_triggers.on_bash_command(reads_issue_comments),
|
|
914
|
+
rewrite: comments_input,
|
|
915
|
+
rewrite_note: COMMENTS_NOTE,
|
|
916
|
+
// The opening the reason carried before it led with the command.
|
|
917
|
+
former_reasons: ["\u26D4 an Issue's comments are part of the Issue: read them before implementing"],
|
|
918
|
+
about: {
|
|
919
|
+
title: "Reading Issue comments",
|
|
920
|
+
topic: "`issue-comments.md`",
|
|
921
|
+
fires: "a `Bash` that reads only the Issue body (`gh issue view <N>`, or a GET ending in `\u2026/issues/<N>`). Refused until it is re-read with the comments",
|
|
922
|
+
quiet: "no Issue read, or a read with `--comments` / `--json comments` / `\u2026/comments` from the start"
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
var issue_comments = {
|
|
926
|
+
COMMENTS_NOTE,
|
|
927
|
+
ISSUE_COMMENTS_REASON,
|
|
928
|
+
ROW: ROW7,
|
|
929
|
+
comments_input,
|
|
930
|
+
fetches_issue_comments,
|
|
931
|
+
is_body_only_issue_read,
|
|
932
|
+
projects_away_body
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
// scripts/rules/direct-issue-read.ts
|
|
936
|
+
var API_SUBCOMMAND2 = "api";
|
|
937
|
+
var ISSUE_GROUP2 = "issue";
|
|
938
|
+
var VIEW_ACTION = "view";
|
|
939
|
+
var WEB_FLAGS = /* @__PURE__ */ new Set(["-w", "--web"]);
|
|
940
|
+
var VIEW_VALUE_TAKING_FLAGS = /* @__PURE__ */ new Set([
|
|
941
|
+
...direct_pr_create.VALUE_TAKING_FLAGS,
|
|
942
|
+
"--json",
|
|
943
|
+
"-q",
|
|
944
|
+
"--jq",
|
|
945
|
+
"-t",
|
|
946
|
+
"--template"
|
|
947
|
+
]);
|
|
948
|
+
var REPO_EQUALS = "--repo=";
|
|
949
|
+
var ISSUE_URL = /^https?:\/\/[^/\s]+\/(?<named>[^/\s]+\/[^/\s]+)\/issues\/\d+/u;
|
|
950
|
+
var OWNER_REPO_PARTS = 2;
|
|
951
|
+
var QUOTE2 = `['"]?`;
|
|
952
|
+
var API_ORIGIN2 = String.raw`(?:https://api\.github\.com)?/?`;
|
|
953
|
+
var SEGMENT2 = String.raw`[^/\s'"?]+`;
|
|
954
|
+
var NUMBER = String.raw`(?:\d+|\$\{?\w+\}?)`;
|
|
955
|
+
var ISSUE_READ_PATH = new RegExp(
|
|
956
|
+
String.raw`^${QUOTE2}${API_ORIGIN2}repos/${SEGMENT2}/${SEGMENT2}/issues/${NUMBER}(?:/comments|/timeline)?/?(?:\?[^\s'"]*)?${QUOTE2}$`,
|
|
957
|
+
"u"
|
|
958
|
+
);
|
|
959
|
+
function is_own_repository(owner, repo) {
|
|
960
|
+
if (gh_api.is_owner_placeholder(owner)) return true;
|
|
961
|
+
return `${owner}/${repo}`.toLowerCase() === session_cite.session_repo()?.toLowerCase();
|
|
962
|
+
}
|
|
963
|
+
function is_own_issue_endpoint(endpoint) {
|
|
964
|
+
const target = gh_api.repo_target(endpoint);
|
|
965
|
+
if (target === void 0) return false;
|
|
966
|
+
return is_own_repository(target.owner, target.repo) && ISSUE_READ_PATH.test(endpoint);
|
|
967
|
+
}
|
|
968
|
+
function is_body_api_read(call) {
|
|
969
|
+
return gh_api.is_gh_api(call) && gh_api.is_read(call) && !issue_comments.projects_away_body(call);
|
|
970
|
+
}
|
|
971
|
+
function is_api_issue_read(args, call) {
|
|
972
|
+
if (!is_body_api_read(call)) return false;
|
|
973
|
+
const [subcommand, endpoint = ""] = direct_pr_create.positional_words(
|
|
974
|
+
args,
|
|
975
|
+
direct_pr_create.API_VALUE_TAKING_FLAGS
|
|
976
|
+
);
|
|
977
|
+
return subcommand === API_SUBCOMMAND2 && is_own_issue_endpoint(endpoint);
|
|
978
|
+
}
|
|
979
|
+
function repo_flag(args) {
|
|
980
|
+
const index = args.findIndex((argument) => direct_pr_create.VALUE_TAKING_FLAGS.has(argument));
|
|
981
|
+
if (index !== -1) return args[index + 1];
|
|
982
|
+
return args.find((argument) => argument.startsWith(REPO_EQUALS))?.slice(REPO_EQUALS.length);
|
|
983
|
+
}
|
|
984
|
+
function named_repository(args, issue) {
|
|
985
|
+
return ISSUE_URL.exec(issue)?.groups?.["named"] ?? repo_flag(args);
|
|
986
|
+
}
|
|
987
|
+
function is_own_view_target(args, issue) {
|
|
988
|
+
const named = named_repository(args, issue);
|
|
989
|
+
if (named === void 0) return true;
|
|
990
|
+
const [owner = "", repo = ""] = named.split("/").slice(-OWNER_REPO_PARTS);
|
|
991
|
+
return is_own_repository(owner, repo);
|
|
992
|
+
}
|
|
993
|
+
function prints_body_or_comments(call) {
|
|
994
|
+
if (issue_comments.fetches_issue_comments(call)) return true;
|
|
995
|
+
return !issue_comments.projects_away_body(call);
|
|
996
|
+
}
|
|
997
|
+
function is_printed_view(args, issue, call) {
|
|
998
|
+
if (args.some((argument) => WEB_FLAGS.has(argument))) return false;
|
|
999
|
+
return is_own_view_target(args, issue) && prints_body_or_comments(call);
|
|
1000
|
+
}
|
|
1001
|
+
function is_issue_view_read(args, call) {
|
|
1002
|
+
const [group, action, issue = ""] = direct_pr_create.positional_words(
|
|
1003
|
+
args,
|
|
1004
|
+
VIEW_VALUE_TAKING_FLAGS
|
|
1005
|
+
);
|
|
1006
|
+
const is_view = group === ISSUE_GROUP2 && action === VIEW_ACTION;
|
|
1007
|
+
return is_view && is_printed_view(args, issue, call);
|
|
1008
|
+
}
|
|
1009
|
+
function is_issue_read_segment(segment) {
|
|
1010
|
+
const args = git_argv.arguments_of(segment, direct_pr_create.GH_COMMAND);
|
|
1011
|
+
if (args === void 0 || direct_pr_create.is_help_call(args)) return false;
|
|
1012
|
+
const call = [direct_pr_create.GH_COMMAND, ...args].join(" ");
|
|
1013
|
+
return is_api_issue_read(args, call) || is_issue_view_read(args, call);
|
|
1014
|
+
}
|
|
1015
|
+
function is_direct_issue_read(command) {
|
|
1016
|
+
return direct_pr_create.has_run_segment(command, is_issue_read_segment);
|
|
1017
|
+
}
|
|
1018
|
+
var READ_NAMES = /* @__PURE__ */ new Set(["issue:read", "run:prep", "run:entry"]);
|
|
1019
|
+
function is_josh_read(command) {
|
|
1020
|
+
return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, READ_NAMES));
|
|
1021
|
+
}
|
|
1022
|
+
function is_any_read(command) {
|
|
1023
|
+
return is_direct_issue_read(command) || is_josh_read(command);
|
|
1024
|
+
}
|
|
1025
|
+
var VIEW_WORDS = ["gh", ISSUE_GROUP2, VIEW_ACTION];
|
|
1026
|
+
var REPO_SPELLING = /^(?:(?:-R|--repo) |--repo=)(?<repo>[\w.-]+\/[\w.-]+)$/u;
|
|
1027
|
+
var ISSUE_READ_COMMAND2 = "pnpm josh issue:read";
|
|
1028
|
+
var REPO_FLAG = "--repo";
|
|
1029
|
+
function carried_repo(flags) {
|
|
1030
|
+
if (flags === "") return [];
|
|
1031
|
+
const repo = REPO_SPELLING.exec(flags)?.groups?.["repo"];
|
|
1032
|
+
return repo === void 0 ? void 0 : [REPO_FLAG, repo];
|
|
1033
|
+
}
|
|
1034
|
+
function issue_read_of(view) {
|
|
1035
|
+
const repo = carried_repo(view.flags);
|
|
1036
|
+
return repo === void 0 ? void 0 : standalone_gh_call.line_of([ISSUE_READ_COMMAND2, view.issue, ...repo]);
|
|
1037
|
+
}
|
|
1038
|
+
function issue_read_command(command) {
|
|
1039
|
+
const view = standalone_gh_call.call_of(command, VIEW_WORDS);
|
|
1040
|
+
const read = view === void 0 ? void 0 : issue_read_of(view);
|
|
1041
|
+
return read !== void 0 && is_direct_issue_read(command.trim()) ? read : void 0;
|
|
1042
|
+
}
|
|
1043
|
+
function issue_read_input(call) {
|
|
1044
|
+
return standalone_gh_call.input_of(call, issue_read_command);
|
|
1045
|
+
}
|
|
1046
|
+
var ISSUE_READ_NOTE = "\u21BB direct issue read: this `gh issue view <N>` ran as `pnpm josh issue:read <N>`, which withholds a comment from an author without write access (`issue-comments.md`).";
|
|
1047
|
+
var DIRECT_ISSUE_READ_REASON = "\u26D4 direct issue read: pnpm josh issue:read <N>\nRun that in place of this read of `\u2026/issues/<N>`, its comments or its timeline (`gh api` or `gh issue view`) \u2014 it withholds a comment from an author without write access and marks an untrusted body as a report, which a raw read prints whole as if it were an instruction (`issue-comments.md`). **This rule fires on every occurrence, not once per run.**";
|
|
1048
|
+
var ROW8 = {
|
|
1049
|
+
id: "direct-issue-read",
|
|
1050
|
+
is_trigger: bash_triggers.on_bash_command(is_direct_issue_read),
|
|
1051
|
+
reason: DIRECT_ISSUE_READ_REASON,
|
|
1052
|
+
decide: () => true,
|
|
1053
|
+
keeps: bash_triggers.on_bash_command(is_josh_read),
|
|
1054
|
+
reaches: bash_triggers.on_bash_command(is_any_read),
|
|
1055
|
+
rewrite: issue_read_input,
|
|
1056
|
+
rewrite_note: ISSUE_READ_NOTE,
|
|
1057
|
+
about: {
|
|
1058
|
+
title: "Direct issue read",
|
|
1059
|
+
topic: "`issue-comments.md`",
|
|
1060
|
+
fires: "a `gh api` GET of the checkout's own `repos/<o>/<r>/issues/<N>`, `\u2026/comments` or `\u2026/timeline` (the `{owner}` placeholder or the origin spelled out), or a `gh issue view <N>` of it, with no projection or one naming the body (or, for the view, the comments); it points at `pnpm josh issue:read`. A line that is only `gh issue view <N>`, with at most `-R` / `--repo <owner>/<repo>`, is rewritten to `pnpm josh issue:read <N>`; every other form is refused, every occurrence",
|
|
1061
|
+
quiet: "`pnpm josh issue:read` / `run:prep` / `run:entry`, a `--jq` / `--json` projection without the body or the comments, `gh issue view --web`, or another repository named outright"
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
var direct_issue_read = {
|
|
1065
|
+
DIRECT_ISSUE_READ_REASON,
|
|
1066
|
+
ISSUE_READ_NOTE,
|
|
1067
|
+
ROW: ROW8,
|
|
1068
|
+
is_direct_issue_read,
|
|
1069
|
+
issue_read_command
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
// scripts/rules/direct-vitest-run.ts
|
|
1073
|
+
var VITEST_PROGRAM = "vitest";
|
|
1074
|
+
var OTHER_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
1075
|
+
"bench",
|
|
1076
|
+
"list",
|
|
1077
|
+
"init",
|
|
1078
|
+
"typecheck",
|
|
1079
|
+
"related",
|
|
1080
|
+
"watch",
|
|
1081
|
+
"dev"
|
|
1082
|
+
]);
|
|
1083
|
+
var EXEMPT_FLAGS = /* @__PURE__ */ new Set([
|
|
1084
|
+
"--project",
|
|
1085
|
+
"--config",
|
|
1086
|
+
"-c",
|
|
1087
|
+
"--help",
|
|
1088
|
+
"-h",
|
|
1089
|
+
"--version",
|
|
1090
|
+
"-v"
|
|
1091
|
+
]);
|
|
1092
|
+
var FLAG_VALUE_SEPARATOR = "=";
|
|
1093
|
+
function is_exempt_flag(argument) {
|
|
1094
|
+
return EXEMPT_FLAGS.has(argument.split(FLAG_VALUE_SEPARATOR, 1)[0] ?? argument);
|
|
1095
|
+
}
|
|
1096
|
+
function is_carried_arguments(args) {
|
|
1097
|
+
if (OTHER_SUBCOMMANDS.has(args[0] ?? "")) return false;
|
|
1098
|
+
return args.every((argument) => !is_exempt_flag(argument));
|
|
1099
|
+
}
|
|
1100
|
+
function is_carried_run(call) {
|
|
1101
|
+
if (call.name !== VITEST_PROGRAM || call.is_reserved) return false;
|
|
1102
|
+
return is_carried_arguments(call.args);
|
|
1103
|
+
}
|
|
1104
|
+
function is_direct_vitest_run(command) {
|
|
1105
|
+
return command_memory.tool_calls_of(command).some((call) => is_carried_run(call));
|
|
1106
|
+
}
|
|
1107
|
+
var TEST_RELATED = /* @__PURE__ */ new Set(["test:related"]);
|
|
1108
|
+
function is_test_related(command) {
|
|
1109
|
+
return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, TEST_RELATED));
|
|
1110
|
+
}
|
|
1111
|
+
function is_any_test_run(command) {
|
|
1112
|
+
return is_direct_vitest_run(command) || is_test_related(command);
|
|
1113
|
+
}
|
|
1114
|
+
var DIRECT_VITEST_RUN_REASON = '\u26D4 direct vitest run: pnpm josh test:related [<file>\u2026]\nRun that in place of this `vitest run` \u2014 it hands the files and `--flag=value` flags on to vitest and leaves the record the gate reuses (`docs/josh-commands.md` \u2192 "`josh test:related`"). `--project` / `--config` and `vitest bench` / `list` pass. **This rule fires on every occurrence.**';
|
|
1115
|
+
var ROW9 = {
|
|
1116
|
+
id: "direct-vitest-run",
|
|
1117
|
+
is_trigger: bash_triggers.on_bash_command(is_direct_vitest_run),
|
|
1118
|
+
reason: DIRECT_VITEST_RUN_REASON,
|
|
1119
|
+
decide: () => true,
|
|
1120
|
+
keeps: bash_triggers.on_bash_command(is_test_related),
|
|
1121
|
+
reaches: bash_triggers.on_bash_command(is_any_test_run),
|
|
1122
|
+
about: {
|
|
1123
|
+
title: "Direct vitest run",
|
|
1124
|
+
topic: '`docs/josh-commands.md` \u2192 "`josh test:related`"',
|
|
1125
|
+
fires: "`vitest run` or bare `vitest` (directly, through `pnpm` / `npx` / `pnpm exec`, or inside `sh -c`); it points at `pnpm josh test:related`. Every occurrence",
|
|
1126
|
+
quiet: "`pnpm josh test:related`, `--project` / `--config` / `-c`, another subcommand (`bench`, `list`, \u2026), `--help` / `--version`, or a run inside `pnpm josh reserved-run`"
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
var direct_vitest_run = { DIRECT_VITEST_RUN_REASON, ROW: ROW9, is_direct_vitest_run };
|
|
1130
|
+
|
|
1131
|
+
// scripts/rules/early-heartbeat.ts
|
|
1132
|
+
var ARM_PREFIX = "josh-heartbeat-arm-";
|
|
1133
|
+
var SEGMENT_SEPARATOR = /&&|\|\||;|&|\n/u;
|
|
1134
|
+
var SLEEP_SEGMENT = /^sleep\s+(\d+(?:\.\d+)?)([smhd]?)$/u;
|
|
1135
|
+
var INERT_SEGMENT = /^(?::|true|(?:echo|printf|date)\b.*)$/u;
|
|
1136
|
+
var LOOP_KEYWORD = /\b(?:while|until|for|do|done)\b/u;
|
|
1137
|
+
var SECONDS_PER_UNIT = { d: 86400, h: 3600, m: 60, s: 1 };
|
|
1138
|
+
var BARE_NUMBER_SECONDS = 1;
|
|
1139
|
+
var AMOUNT_GROUP = 1;
|
|
1140
|
+
var UNIT_GROUP = 2;
|
|
1141
|
+
var NO_SECONDS = 0;
|
|
1142
|
+
var ARM_RECORD = hook_decision.create_refusal_stamp(ARM_PREFIX);
|
|
1143
|
+
function segments_of(command) {
|
|
1144
|
+
return command.split(SEGMENT_SEPARATOR).map((segment) => segment.trim()).filter((segment) => segment !== "");
|
|
1145
|
+
}
|
|
1146
|
+
function sleep_seconds(segment) {
|
|
1147
|
+
const matched = SLEEP_SEGMENT.exec(segment);
|
|
1148
|
+
if (matched === null) return void 0;
|
|
1149
|
+
const unit = matched[UNIT_GROUP] ?? "";
|
|
1150
|
+
return Number(matched[AMOUNT_GROUP]) * (SECONDS_PER_UNIT[unit] ?? BARE_NUMBER_SECONDS);
|
|
1151
|
+
}
|
|
1152
|
+
function is_wait_or_inert(segment) {
|
|
1153
|
+
return sleep_seconds(segment) !== void 0 || INERT_SEGMENT.test(segment);
|
|
1154
|
+
}
|
|
1155
|
+
function is_wait_timer(command) {
|
|
1156
|
+
if (LOOP_KEYWORD.test(command)) return false;
|
|
1157
|
+
const segments = segments_of(command);
|
|
1158
|
+
if (segments.every((segment) => sleep_seconds(segment) === void 0)) return false;
|
|
1159
|
+
return segments.every((segment) => is_wait_or_inert(segment));
|
|
1160
|
+
}
|
|
1161
|
+
function wait_duration_ms(command) {
|
|
1162
|
+
const seconds = segments_of(command).reduce(
|
|
1163
|
+
(total, segment) => total + (sleep_seconds(segment) ?? NO_SECONDS),
|
|
1164
|
+
NO_SECONDS
|
|
1165
|
+
);
|
|
1166
|
+
return seconds * MS_PER_SECOND;
|
|
1167
|
+
}
|
|
1168
|
+
function is_armed(target, now_ms) {
|
|
1169
|
+
return now_ms < ARM_RECORD.last_ms(target);
|
|
1170
|
+
}
|
|
1171
|
+
function landing_ms(command, now_ms, last_report_ms) {
|
|
1172
|
+
const interval_ms = run_progress_config.configured_interval_ms();
|
|
1173
|
+
const duration_ms = wait_duration_ms(command);
|
|
1174
|
+
if (duration_ms > interval_ms) return void 0;
|
|
1175
|
+
const lands_ms = now_ms + duration_ms;
|
|
1176
|
+
return run_progress.is_due(last_report_ms, lands_ms, interval_ms) ? lands_ms : void 0;
|
|
1177
|
+
}
|
|
1178
|
+
function decide(call, run, can_record) {
|
|
1179
|
+
const last_report_ms = run_progress_clock.read_last_report_sync();
|
|
1180
|
+
if (last_report_ms === void 0) return false;
|
|
1181
|
+
const target = ARM_RECORD.path(run.transcript);
|
|
1182
|
+
if (is_armed(target, run.now_ms)) return true;
|
|
1183
|
+
const lands_ms = landing_ms(time_shell.bash_command(call.input), run.now_ms, last_report_ms);
|
|
1184
|
+
if (lands_ms === void 0) return true;
|
|
1185
|
+
if (can_record) ARM_RECORD.record(target, lands_ms);
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
var EARLY_HEARTBEAT_REASON = "\u26D4 early heartbeat: this call arms a wait timer of its own. Start `pnpm josh run:progress --wait` once in the background: it reports to the run's event stream on its own \u2014 relay nothing. When asked, `pnpm josh run:progress --once` \u2014 under `backlogrun`, `pnpm josh run:board --chat`; for CI, `pnpm josh followup`. It fires on every occurrence (`.claude/skills/workflow-commands/progress-watcher.md`).";
|
|
1189
|
+
var PROGRESS_NAMES = /* @__PURE__ */ new Set(["run:progress"]);
|
|
1190
|
+
var WATCH_FLAG = /(?:^|\s)--(?:wait|once)(?=\s|$)/u;
|
|
1191
|
+
function is_progress_watch_segment(segment) {
|
|
1192
|
+
return shell_segments.is_josh_command(segment, PROGRESS_NAMES) && WATCH_FLAG.test(segment);
|
|
1193
|
+
}
|
|
1194
|
+
function is_progress_watch(command) {
|
|
1195
|
+
return segments_of(time_shell.unquoted(command)).some(
|
|
1196
|
+
(segment) => is_progress_watch_segment(segment)
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
function waits_for_progress(command) {
|
|
1200
|
+
return is_wait_timer(command) || is_progress_watch(command);
|
|
1201
|
+
}
|
|
1202
|
+
var early_heartbeat = {
|
|
1203
|
+
ARM_PREFIX,
|
|
1204
|
+
EARLY_HEARTBEAT_REASON,
|
|
1205
|
+
decide,
|
|
1206
|
+
is_progress_watch,
|
|
1207
|
+
is_wait_or_inert,
|
|
1208
|
+
is_wait_timer,
|
|
1209
|
+
sleep_seconds,
|
|
1210
|
+
wait_duration_ms,
|
|
1211
|
+
waits_for_progress
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
// scripts/rules/file-body.ts
|
|
1215
|
+
import { existsSync } from "node:fs";
|
|
1216
|
+
var HEREDOC2 = /(?<!<)<<(?!<)-?\s*['"]?([A-Z_a-z]\w*)/u;
|
|
1217
|
+
var LINE_BREAK = "\n";
|
|
1218
|
+
function has_heredoc(command) {
|
|
1219
|
+
return HEREDOC2.test(command);
|
|
1220
|
+
}
|
|
1221
|
+
function with_body_line(lines, line) {
|
|
1222
|
+
const opener = lines.at(-1);
|
|
1223
|
+
if (opener === void 0) return [...lines];
|
|
1224
|
+
return [...lines.slice(0, -1), { ...opener, body: `${opener.body}${line}${LINE_BREAK}` }];
|
|
1225
|
+
}
|
|
1226
|
+
function scan_line(scan, line) {
|
|
1227
|
+
if (scan.delimiter === void 0) {
|
|
1228
|
+
return { lines: [...scan.lines, { text: line, body: "" }], delimiter: HEREDOC2.exec(line)?.[1] };
|
|
1229
|
+
}
|
|
1230
|
+
if (line.trim() === scan.delimiter) return { ...scan, delimiter: void 0 };
|
|
1231
|
+
return { ...scan, lines: with_body_line(scan.lines, line) };
|
|
1232
|
+
}
|
|
1233
|
+
function command_lines(command) {
|
|
1234
|
+
let scan = { lines: [], delimiter: void 0 };
|
|
1235
|
+
for (const line of command.split(LINE_BREAK)) scan = scan_line(scan, line);
|
|
1236
|
+
return scan.lines;
|
|
1237
|
+
}
|
|
1238
|
+
var FILE_REDIRECT = /(?:^|\s)>>?\s*(?!&)("[^"]+"|'[^']+'|\S+)/u;
|
|
1239
|
+
var TEE_TARGET = /\btee\s+(?:-a\s+)?(?!-)("[^"]+"|'[^']+'|\S+)/u;
|
|
1240
|
+
var QUOTES2 = /^['"]|['"]$/gu;
|
|
1241
|
+
function redirect_target(line) {
|
|
1242
|
+
const target = FILE_REDIRECT.exec(line)?.[1] ?? TEE_TARGET.exec(line)?.[1];
|
|
1243
|
+
return target === void 0 ? void 0 : target.replaceAll(QUOTES2, "");
|
|
1244
|
+
}
|
|
1245
|
+
var BODY_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
1246
|
+
"python",
|
|
1247
|
+
"python3",
|
|
1248
|
+
"ruby",
|
|
1249
|
+
"perl",
|
|
1250
|
+
"php",
|
|
1251
|
+
"node"
|
|
1252
|
+
]);
|
|
1253
|
+
var NODE = "node";
|
|
1254
|
+
var NODE_EVAL = /(?:^|\s)(?:-e|--eval)\b/u;
|
|
1255
|
+
var WRITE_CALL = /writeFile|appendFile|createWriteStream|\.write(?:lines|_text|_bytes)?\s*\(/u;
|
|
1256
|
+
var WRITE_MODE = /open\s*\([^)]*['"][rbt+]*[wax]|os\.replace|shutil\.(?:copy|move)/u;
|
|
1257
|
+
function body_writes(body) {
|
|
1258
|
+
return WRITE_CALL.test(body) || WRITE_MODE.test(body);
|
|
1259
|
+
}
|
|
1260
|
+
var SCRIPT_FILE = /\.(?:py|rb|pl|php|js|mjs|cjs)$/u;
|
|
1261
|
+
function is_heredoc_write(line, has_file) {
|
|
1262
|
+
const target = has_heredoc(line.text) ? redirect_target(line.text) : void 0;
|
|
1263
|
+
if (target === void 0) return false;
|
|
1264
|
+
if (has_file(target)) return true;
|
|
1265
|
+
return SCRIPT_FILE.test(target) && body_writes(line.body);
|
|
1266
|
+
}
|
|
1267
|
+
function is_interpreter_write(segment, body) {
|
|
1268
|
+
const leading = time_shell.leading_word(segment);
|
|
1269
|
+
if (!BODY_INTERPRETERS.has(leading)) return false;
|
|
1270
|
+
if (has_heredoc(segment)) return body_writes(body);
|
|
1271
|
+
return leading === NODE && NODE_EVAL.test(segment) && body_writes(segment);
|
|
1272
|
+
}
|
|
1273
|
+
var PERL = "perl";
|
|
1274
|
+
var PERL_IN_PLACE = /^-[A-Za-z0-9]*i/u;
|
|
1275
|
+
var WHITESPACE = /\s+/u;
|
|
1276
|
+
function is_in_place_perl(segment) {
|
|
1277
|
+
if (time_shell.leading_word(segment) !== PERL) return false;
|
|
1278
|
+
return segment.split(WHITESPACE).some((word) => PERL_IN_PLACE.test(word));
|
|
1279
|
+
}
|
|
1280
|
+
function has_body_interpreter(line) {
|
|
1281
|
+
return shell_segments.segments_of(line.text).some((segment) => is_interpreter_write(segment, line.body) || is_in_place_perl(segment));
|
|
1282
|
+
}
|
|
1283
|
+
function carries_a_file_body(command, has_file = existsSync) {
|
|
1284
|
+
return command_lines(command).some(
|
|
1285
|
+
(line) => is_heredoc_write(line, has_file) || has_body_interpreter(line)
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
var FILE_BODY_REASON = "\u26D4 file body in a shell command: this carries a file's new text inline, which is re-read on every later request. Edit a region with the Edit tool, rewrite a whole file with the Write tool, and pass a body by path (`--body-file <path>`); a new-file `cat > <new-file> <<EOF` stays allowed. This rule fires on every occurrence (`prompts/collaboration-workflow/file-edits.md`).";
|
|
1289
|
+
var ROW10 = {
|
|
1290
|
+
id: "file-body",
|
|
1291
|
+
is_trigger: bash_triggers.on_bash_command((command) => carries_a_file_body(command)),
|
|
1292
|
+
reason: FILE_BODY_REASON,
|
|
1293
|
+
decide: () => true,
|
|
1294
|
+
about: {
|
|
1295
|
+
title: "No file body on the shell",
|
|
1296
|
+
topic: "`file-edits.md`",
|
|
1297
|
+
fires: "a heredoc write to an existing file, a writing `node -e` / interpreter heredoc, or `perl -0pi -e`. Every occurrence",
|
|
1298
|
+
quiet: "a new file, a read-only heredoc, or a short `sed -i`"
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
var file_body = {
|
|
1302
|
+
FILE_BODY_REASON,
|
|
1303
|
+
ROW: ROW10,
|
|
1304
|
+
carries_a_file_body
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
// scripts/rules/file-rewrite.ts
|
|
1308
|
+
var { WRITE, EDIT, is_edit_tool, string_field } = edit_tool;
|
|
1309
|
+
var MAX_SHARED_RATIO = 0.5;
|
|
1310
|
+
var PERCENT = 100;
|
|
1311
|
+
var LINE_BREAK2 = "\n";
|
|
1312
|
+
var ISSUE_PLACEHOLDER = "<N>";
|
|
1313
|
+
var REGEX_SPECIALS = /[.*+?^${}()|[\]\\]/gu;
|
|
1314
|
+
var ISSUE_DIGITS = String.raw`\d+`;
|
|
1315
|
+
var HANDOFF_PATTERN = run_cut_handoff.HANDOFF_PATH.replaceAll(
|
|
1316
|
+
REGEX_SPECIALS,
|
|
1317
|
+
(special) => `\\${special}`
|
|
1318
|
+
).replace(ISSUE_PLACEHOLDER, () => ISSUE_DIGITS);
|
|
1319
|
+
var HANDOFF = new RegExp(`(?:^|/)${HANDOFF_PATTERN}$`, "u");
|
|
1320
|
+
function body_lines(text) {
|
|
1321
|
+
return text.split(LINE_BREAK2).map((line) => line.trim()).filter((line) => line !== "");
|
|
1322
|
+
}
|
|
1323
|
+
function count_lines(lines) {
|
|
1324
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1325
|
+
for (const line of lines) counts.set(line, (counts.get(line) ?? 0) + 1);
|
|
1326
|
+
return counts;
|
|
1327
|
+
}
|
|
1328
|
+
function take_line(counts, line) {
|
|
1329
|
+
const left = counts.get(line) ?? 0;
|
|
1330
|
+
if (left === 0) return false;
|
|
1331
|
+
counts.set(line, left - 1);
|
|
1332
|
+
return true;
|
|
1333
|
+
}
|
|
1334
|
+
function shared_ratio(old_text, new_text) {
|
|
1335
|
+
const new_lines = body_lines(new_text);
|
|
1336
|
+
if (new_lines.length === 0) return 0;
|
|
1337
|
+
const old_counts = count_lines(body_lines(old_text));
|
|
1338
|
+
const shared = new_lines.filter((line) => take_line(old_counts, line)).length;
|
|
1339
|
+
return shared / new_lines.length;
|
|
1340
|
+
}
|
|
1341
|
+
function write_input(call) {
|
|
1342
|
+
if (call.name !== WRITE || !json_value.is_record(call.input)) return void 0;
|
|
1343
|
+
return HANDOFF.test(string_field(call.input, "file_path")) ? void 0 : call.input;
|
|
1344
|
+
}
|
|
1345
|
+
function rewrite_ratio(call, read) {
|
|
1346
|
+
const input = write_input(call);
|
|
1347
|
+
const old_text = input === void 0 ? void 0 : read(string_field(input, "file_path"));
|
|
1348
|
+
if (input === void 0 || old_text === void 0) return void 0;
|
|
1349
|
+
return shared_ratio(old_text, string_field(input, "content"));
|
|
1350
|
+
}
|
|
1351
|
+
function rewrites_kept_body(call, read = file_reader.read_optional) {
|
|
1352
|
+
return (rewrite_ratio(call, read) ?? 0) > MAX_SHARED_RATIO;
|
|
1353
|
+
}
|
|
1354
|
+
function reaches_file_change(call) {
|
|
1355
|
+
return is_edit_tool(call.name);
|
|
1356
|
+
}
|
|
1357
|
+
function keeps_region_edit(call) {
|
|
1358
|
+
return call.name === EDIT;
|
|
1359
|
+
}
|
|
1360
|
+
var FILE_REWRITE_REASON = `\u26D4 whole-file Write of an existing file: the new body keeps more than ${String(MAX_SHARED_RATIO * PERCENT)}% of the old lines, so it is a region edit carried as a whole file and its diff cannot be read. Change the region with the Edit tool; a Write stays for a new file or a rebuild sharing almost nothing. This rule fires on every occurrence (\`prompts/collaboration-workflow/file-edits.md\`).`;
|
|
1361
|
+
var ROW11 = {
|
|
1362
|
+
id: "file-rewrite",
|
|
1363
|
+
is_trigger: (call) => rewrites_kept_body(call),
|
|
1364
|
+
reason: FILE_REWRITE_REASON,
|
|
1365
|
+
decide: () => true,
|
|
1366
|
+
keeps: keeps_region_edit,
|
|
1367
|
+
reaches: reaches_file_change,
|
|
1368
|
+
about: {
|
|
1369
|
+
title: "No whole-file Write that keeps most of the old body",
|
|
1370
|
+
topic: "`file-edits.md`",
|
|
1371
|
+
fires: "a `Write` over an existing file whose new body shares more than half its non-blank lines with the old one. Every occurrence",
|
|
1372
|
+
quiet: "a new file, the run's handoff, a rebuild sharing half or less, or an `Edit`"
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
var file_rewrite = {
|
|
1376
|
+
FILE_REWRITE_REASON,
|
|
1377
|
+
MAX_SHARED_RATIO,
|
|
1378
|
+
ROW: ROW11,
|
|
1379
|
+
rewrites_kept_body,
|
|
1380
|
+
shared_ratio
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// scripts/rules/sed-in-place.ts
|
|
1384
|
+
var SED = "sed";
|
|
1385
|
+
var IN_PLACE_SHORT = /^-[A-Za-z]*i/u;
|
|
1386
|
+
var IN_PLACE_LONG = /^--in-place(?:=|$)/u;
|
|
1387
|
+
var IN_PLACE_CLUSTER = /^-[A-Za-z]*i$/u;
|
|
1388
|
+
var EXTENDED_SHORT = /^-[A-Za-z]*[Er]/u;
|
|
1389
|
+
var EXTENDED_LONG = "--regexp-extended";
|
|
1390
|
+
var PLAIN_FLAGS = /* @__PURE__ */ new Set(["", "g"]);
|
|
1391
|
+
var SCRIPT_FLAG = "-e";
|
|
1392
|
+
var FLAG_PREFIX2 = "-";
|
|
1393
|
+
var SURROUNDING_QUOTES = /^(['"])(.*)\1$/su;
|
|
1394
|
+
var QUOTED_BODY = 2;
|
|
1395
|
+
var META_CHARACTER = /[/&\\.*[\]^$]/u;
|
|
1396
|
+
var SUBSTITUTE = "s";
|
|
1397
|
+
var SUBSTITUTE_PARTS = 3;
|
|
1398
|
+
var ONE_MATCH = 1;
|
|
1399
|
+
function unquoted(word) {
|
|
1400
|
+
return SURROUNDING_QUOTES.exec(word)?.[QUOTED_BODY] ?? word;
|
|
1401
|
+
}
|
|
1402
|
+
function is_in_place_flag(word) {
|
|
1403
|
+
return IN_PLACE_LONG.test(word) || IN_PLACE_SHORT.test(word);
|
|
1404
|
+
}
|
|
1405
|
+
function without_suffix(words) {
|
|
1406
|
+
const index = words.findIndex((word) => IN_PLACE_CLUSTER.test(word));
|
|
1407
|
+
if (index === -1 || words[index + 1] !== "") return [...words];
|
|
1408
|
+
return words.toSpliced(index + 1, 1);
|
|
1409
|
+
}
|
|
1410
|
+
function sed_words(segment) {
|
|
1411
|
+
const words = git_argv.arguments_of(segment, SED)?.map((word) => unquoted(word));
|
|
1412
|
+
return words?.some((word) => is_in_place_flag(word)) === true ? without_suffix(words) : void 0;
|
|
1413
|
+
}
|
|
1414
|
+
function is_script_value(words, index) {
|
|
1415
|
+
return words[index - 1] === SCRIPT_FLAG;
|
|
1416
|
+
}
|
|
1417
|
+
function sed_call(words) {
|
|
1418
|
+
const scripts = words.filter((_word, index) => is_script_value(words, index));
|
|
1419
|
+
const operands = words.filter(
|
|
1420
|
+
(word, index) => !word.startsWith(FLAG_PREFIX2) && !is_script_value(words, index)
|
|
1421
|
+
);
|
|
1422
|
+
if (scripts.length > 0) return { scripts, files: operands };
|
|
1423
|
+
const [script, ...files] = operands;
|
|
1424
|
+
return { scripts: script === void 0 ? [] : [script], files };
|
|
1425
|
+
}
|
|
1426
|
+
function substitute_parts(script) {
|
|
1427
|
+
if (!script.startsWith(SUBSTITUTE)) return void 0;
|
|
1428
|
+
const parts = script.slice(SUBSTITUTE.length + 1).split(script.charAt(SUBSTITUTE.length));
|
|
1429
|
+
return parts.length === SUBSTITUTE_PARTS ? parts : void 0;
|
|
1430
|
+
}
|
|
1431
|
+
function has_plain_flags(parts) {
|
|
1432
|
+
return PLAIN_FLAGS.has(parts.at(-1) ?? "");
|
|
1433
|
+
}
|
|
1434
|
+
function is_plain(parts) {
|
|
1435
|
+
const [pattern = "", replacement = ""] = parts;
|
|
1436
|
+
return pattern !== "" && has_plain_flags(parts) && !META_CHARACTER.test(pattern + replacement);
|
|
1437
|
+
}
|
|
1438
|
+
function is_extended(words) {
|
|
1439
|
+
return words.some((word) => word === EXTENDED_LONG || EXTENDED_SHORT.test(word));
|
|
1440
|
+
}
|
|
1441
|
+
function single_script(words) {
|
|
1442
|
+
if (is_extended(words)) return void 0;
|
|
1443
|
+
const { scripts } = sed_call(words);
|
|
1444
|
+
return scripts.length === 1 ? scripts[0] : void 0;
|
|
1445
|
+
}
|
|
1446
|
+
function plain_pattern(words) {
|
|
1447
|
+
const script = single_script(words);
|
|
1448
|
+
const parts = script === void 0 ? void 0 : substitute_parts(script);
|
|
1449
|
+
return parts !== void 0 && is_plain(parts) ? parts[0] : void 0;
|
|
1450
|
+
}
|
|
1451
|
+
function match_count(text, pattern) {
|
|
1452
|
+
return text.split(pattern).length - 1;
|
|
1453
|
+
}
|
|
1454
|
+
function is_unsafe_segment(segment, read) {
|
|
1455
|
+
const words = sed_words(segment);
|
|
1456
|
+
if (words === void 0) return false;
|
|
1457
|
+
const { files } = sed_call(words);
|
|
1458
|
+
const pattern = plain_pattern(words);
|
|
1459
|
+
if (pattern === void 0 || files.length === 0) return true;
|
|
1460
|
+
return files.some((file) => match_count(read(file), pattern) !== ONE_MATCH);
|
|
1461
|
+
}
|
|
1462
|
+
function is_unsafe_sed(command, read = file_reader.read_file_or_empty) {
|
|
1463
|
+
return shell_segments.segments_of(command).some((segment) => is_unsafe_segment(segment, read));
|
|
1464
|
+
}
|
|
1465
|
+
function is_in_place_sed(command) {
|
|
1466
|
+
return shell_segments.segments_of(command).some((segment) => sed_words(segment) !== void 0);
|
|
1467
|
+
}
|
|
1468
|
+
function keeps_plain_sed(command) {
|
|
1469
|
+
return shell_segments.segments_of(command).some((segment) => {
|
|
1470
|
+
const words = sed_words(segment);
|
|
1471
|
+
return words !== void 0 && plain_pattern(words) !== void 0;
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
var SED_IN_PLACE_REASON = "\u26D4 unsafe `sed -i`: its pattern or replacement carries a meta character (`/ & \\ . * [ ] ^ $`), it is not one plain `s` command (a flag other than `g`, or `-E`), or it does not match exactly once in each target file, so it can rewrite a line you did not mean to. Change the region with the Edit tool. This rule fires on every occurrence (`prompts/collaboration-workflow/file-edits.md`).";
|
|
1475
|
+
var ROW12 = {
|
|
1476
|
+
id: "sed-in-place",
|
|
1477
|
+
is_trigger: bash_triggers.on_bash_command((command) => is_unsafe_sed(command)),
|
|
1478
|
+
reason: SED_IN_PLACE_REASON,
|
|
1479
|
+
decide: () => true,
|
|
1480
|
+
keeps: bash_triggers.on_bash_command(keeps_plain_sed),
|
|
1481
|
+
reaches: bash_triggers.on_bash_command(is_in_place_sed),
|
|
1482
|
+
about: {
|
|
1483
|
+
title: "No `sed -i` its countable conditions do not clear",
|
|
1484
|
+
topic: "`file-edits.md`",
|
|
1485
|
+
fires: "a `sed -i` whose script is not one `s` command, whose pattern or replacement has a meta character, or which does not match exactly once in a target file. Every occurrence",
|
|
1486
|
+
quiet: "not in place, or one plain `s` command matching once in each file"
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
var sed_in_place = {
|
|
1490
|
+
ROW: ROW12,
|
|
1491
|
+
is_in_place_sed,
|
|
1492
|
+
is_unsafe_sed,
|
|
1493
|
+
keeps_plain_sed
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
// scripts/rules/file-edit-rows.ts
|
|
1497
|
+
var SHELL_ROWS = [file_body.ROW, sed_in_place.ROW];
|
|
1498
|
+
var file_edit_rows = {
|
|
1499
|
+
FILE_BODY_REASON: file_body.FILE_BODY_REASON,
|
|
1500
|
+
REWRITE_ROW: file_rewrite.ROW,
|
|
1501
|
+
SHELL_ROWS
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
// scripts/rules/git-force.ts
|
|
1505
|
+
var FORCE_LONG_PREFIX = "--force";
|
|
1506
|
+
var DELETE_LONG = "--delete";
|
|
1507
|
+
var DESTRUCTIVE_PUSH_LONG = /* @__PURE__ */ new Set([DELETE_LONG, "--mirror", "--prune"]);
|
|
1508
|
+
var PUSH_SHORT_LETTERS = "fd";
|
|
1509
|
+
var FORCE_OR_DELETE_REFSPEC = /^[+:]/u;
|
|
1510
|
+
function is_force_or_delete_push(args) {
|
|
1511
|
+
return args.some(
|
|
1512
|
+
(argument) => argument.startsWith(FORCE_LONG_PREFIX) || DESTRUCTIVE_PUSH_LONG.has(argument) || git_argv.short_cluster_has(argument, PUSH_SHORT_LETTERS) || FORCE_OR_DELETE_REFSPEC.test(argument)
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
var BRANCH_DELETE_LETTERS = "dD";
|
|
1516
|
+
function is_branch_delete(args) {
|
|
1517
|
+
return args.some(
|
|
1518
|
+
(argument) => argument === DELETE_LONG || git_argv.short_cluster_has(argument, BRANCH_DELETE_LETTERS)
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
var PUSH_SUBCOMMAND = "push";
|
|
1522
|
+
var BRANCH_SUBCOMMAND = "branch";
|
|
1523
|
+
function is_destructive_segment(segment) {
|
|
1524
|
+
const call = git_argv.parse(segment);
|
|
1525
|
+
if (call === void 0) return false;
|
|
1526
|
+
if (call.subcommand === PUSH_SUBCOMMAND) return is_force_or_delete_push(call.args);
|
|
1527
|
+
return call.subcommand === BRANCH_SUBCOMMAND && is_branch_delete(call.args);
|
|
1528
|
+
}
|
|
1529
|
+
function is_force_or_delete(command) {
|
|
1530
|
+
return shell_segments.commands_of(command).some((segment) => is_destructive_segment(segment));
|
|
1531
|
+
}
|
|
1532
|
+
var GIT_FORCE_REASON = "\u26D4 force push or branch delete: this rewrites or removes shared history, which is Tier C and needs explicit current-turn user instruction. Push with `pnpm josh git`; leave a force push or branch delete to the user's own terminal. This rule fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md`).";
|
|
1533
|
+
var ROW13 = {
|
|
1534
|
+
id: "git-force",
|
|
1535
|
+
is_trigger: bash_triggers.on_bash_command(is_force_or_delete),
|
|
1536
|
+
reason: GIT_FORCE_REASON,
|
|
1537
|
+
decide: () => true,
|
|
1538
|
+
about: {
|
|
1539
|
+
title: "Force push / branch delete",
|
|
1540
|
+
topic: "`operating-rules.md`",
|
|
1541
|
+
fires: "a `git push` / `git branch` read as force (`--force` / `-f` / `-uf` \u2026) or delete (`--delete` / `-d` / `-D` / `:branch`) in any spelling. Every occurrence",
|
|
1542
|
+
quiet: "a plain `git push` / `git branch`"
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
var git_force = {
|
|
1546
|
+
GIT_FORCE_REASON,
|
|
1547
|
+
ROW: ROW13,
|
|
1548
|
+
is_force_or_delete
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
// scripts/rules/investigator-foreground.ts
|
|
1552
|
+
var INVESTIGATOR_FOREGROUND_REASON = "\u26D4 investigator foreground: a background `investigator` leaves its result queued until a later turn. Reissue the call with `run_in_background: false`, and put the reads of files this run will edit in the same message. It fires on every occurrence (`.claude/skills/workflow-commands/delegation.md`).";
|
|
1553
|
+
var BACKGROUND_FIELD = "run_in_background";
|
|
1554
|
+
var BODY_FIELDS = ["description", "prompt"];
|
|
1555
|
+
var GUARD_LABEL = time_transcript_line.guard_from_refusal(INVESTIGATOR_FOREGROUND_REASON);
|
|
1556
|
+
var NO_FIELD_STAMP = hook_decision.create_refusal_stamp("josh-investigator-no-field-");
|
|
1557
|
+
function is_investigator(call) {
|
|
1558
|
+
if (!agent_model.SUBAGENT_TOOLS.has(call.name) || !json_value.is_record(call.input)) return false;
|
|
1559
|
+
const type = call.input["subagent_type"];
|
|
1560
|
+
return typeof type === "string" && type.split(":").at(-1) === delegation_policy.INVESTIGATOR_AGENT;
|
|
1561
|
+
}
|
|
1562
|
+
function field_of(input, key) {
|
|
1563
|
+
return json_value.is_record(input) ? input[key] : void 0;
|
|
1564
|
+
}
|
|
1565
|
+
function is_foreground(call) {
|
|
1566
|
+
return field_of(call.input, BACKGROUND_FIELD) === false;
|
|
1567
|
+
}
|
|
1568
|
+
function is_background_investigator(call) {
|
|
1569
|
+
return is_investigator(call) && !is_foreground(call);
|
|
1570
|
+
}
|
|
1571
|
+
function keeps_foreground(call) {
|
|
1572
|
+
return is_investigator(call) && is_foreground(call);
|
|
1573
|
+
}
|
|
1574
|
+
function is_untyped_field(name, input) {
|
|
1575
|
+
if (!agent_model.SUBAGENT_TOOLS.has(name) || !json_value.is_record(input)) return false;
|
|
1576
|
+
return typeof input[BACKGROUND_FIELD] === "string";
|
|
1577
|
+
}
|
|
1578
|
+
function placed_of(lines) {
|
|
1579
|
+
return lines.flatMap(
|
|
1580
|
+
(line) => line.blocks.map((block) => ({ block, message_id: line.message_id, at_ms: line.timestamp_ms }))
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
function results_of(placed) {
|
|
1584
|
+
const results = placed.filter(({ block }) => block.result_id !== "");
|
|
1585
|
+
const refused = results.filter(({ block }) => block.refusal_guard === GUARD_LABEL);
|
|
1586
|
+
return {
|
|
1587
|
+
answered: new Set(results.map(({ block }) => block.result_id)),
|
|
1588
|
+
refused: new Map(refused.map(({ block, at_ms }) => [block.result_id, at_ms]))
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function writes_foreground(block) {
|
|
1592
|
+
return String(field_of(block.input, BACKGROUND_FIELD)) === "false";
|
|
1593
|
+
}
|
|
1594
|
+
function has_same_body(block, call) {
|
|
1595
|
+
return BODY_FIELDS.every((key) => field_of(block.input, key) === field_of(call.input, key));
|
|
1596
|
+
}
|
|
1597
|
+
function is_pending_foreground(block, results) {
|
|
1598
|
+
return !results.answered.has(block.id) && writes_foreground(block);
|
|
1599
|
+
}
|
|
1600
|
+
function is_same_dispatch(block, call, results) {
|
|
1601
|
+
if (!is_investigator(block) || !has_same_body(block, call)) return false;
|
|
1602
|
+
return results.refused.has(block.id) || is_pending_foreground(block, results);
|
|
1603
|
+
}
|
|
1604
|
+
function is_dropped_foreground(block, results) {
|
|
1605
|
+
return results.refused.has(block.id) && is_investigator(block) && writes_foreground(block);
|
|
1606
|
+
}
|
|
1607
|
+
function refused_at_ms(dispatch, results) {
|
|
1608
|
+
return results.refused.get(dispatch?.block.id ?? "") ?? hook_decision.NEVER_MS;
|
|
1609
|
+
}
|
|
1610
|
+
function is_alone(dispatch, dispatches) {
|
|
1611
|
+
return dispatches.filter(({ message_id }) => message_id === dispatch.message_id).length === 1;
|
|
1612
|
+
}
|
|
1613
|
+
function is_reissue(placed, results, now_ms) {
|
|
1614
|
+
const dispatches = placed.filter(({ block }) => is_investigator(block));
|
|
1615
|
+
const last = dispatches.findLast(({ block }) => results.answered.has(block.id));
|
|
1616
|
+
const at_ms = refused_at_ms(last, results);
|
|
1617
|
+
if (last === void 0 || at_ms === hook_decision.NEVER_MS) return false;
|
|
1618
|
+
return is_alone(last, dispatches) && now_ms - at_ms < implementation_cut.REISSUE_WINDOW_MS;
|
|
1619
|
+
}
|
|
1620
|
+
function tail_shows_no_field(tail, call, now_ms) {
|
|
1621
|
+
const placed = placed_of(time_transcript_line.parse_text(tail));
|
|
1622
|
+
const results = results_of(placed);
|
|
1623
|
+
const has_block_tell = placed.some(
|
|
1624
|
+
({ block }) => is_untyped_field(block.name, block.input) || is_same_dispatch(block, call, results) || is_dropped_foreground(block, results)
|
|
1625
|
+
);
|
|
1626
|
+
return has_block_tell || is_reissue(placed, results, now_ms);
|
|
1627
|
+
}
|
|
1628
|
+
function remember_no_field(run) {
|
|
1629
|
+
NO_FIELD_STAMP.record(NO_FIELD_STAMP.path(run.transcript), run.now_ms);
|
|
1630
|
+
return true;
|
|
1631
|
+
}
|
|
1632
|
+
function is_known_no_field(run) {
|
|
1633
|
+
return run.context.env["CLAUDE_CODE_ENTRYPOINT"] === "claude-vscode" || NO_FIELD_STAMP.last_ms(NO_FIELD_STAMP.path(run.transcript)) !== hook_decision.NEVER_MS;
|
|
1634
|
+
}
|
|
1635
|
+
function lacks_background_field(tail, call, run) {
|
|
1636
|
+
if (field_of(call.input, BACKGROUND_FIELD) === true) return false;
|
|
1637
|
+
if (is_known_no_field(run)) return true;
|
|
1638
|
+
const has_tell = is_untyped_field(call.name, call.input) || tail_shows_no_field(tail, call, run.now_ms);
|
|
1639
|
+
return has_tell && remember_no_field(run);
|
|
1640
|
+
}
|
|
1641
|
+
var ROW14 = {
|
|
1642
|
+
id: "investigator-foreground",
|
|
1643
|
+
is_trigger: is_background_investigator,
|
|
1644
|
+
reason: INVESTIGATOR_FOREGROUND_REASON,
|
|
1645
|
+
already_satisfied: lacks_background_field,
|
|
1646
|
+
decide: () => true,
|
|
1647
|
+
keeps: keeps_foreground,
|
|
1648
|
+
reaches: is_investigator,
|
|
1649
|
+
about: {
|
|
1650
|
+
title: "Investigator dispatched in the foreground",
|
|
1651
|
+
topic: '`.claude/skills/workflow-commands/delegation.md` \u2192 "The pre-implementation reading \u2014 what goes to a unit, and from which file"',
|
|
1652
|
+
fires: "an `Agent` / `Task` call of the `investigator` type without `run_in_background: false`. Every occurrence",
|
|
1653
|
+
quiet: "another agent type, `run_in_background: false` set, VS Code identified by `CLAUDE_CODE_ENTRYPOINT=claude-vscode`, or a session whose subagent tool declares no `run_in_background` (the field written as a string, the tail holding the same description and prompt, a refused dispatch that wrote it `false`, or a reissue still without the field just after this row refused a lone dispatch) \u2014 remembered for the session"
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1656
|
+
var investigator_foreground = {
|
|
1657
|
+
INVESTIGATOR_FOREGROUND_REASON,
|
|
1658
|
+
ROW: ROW14,
|
|
1659
|
+
is_background_investigator,
|
|
1660
|
+
lacks_background_field
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
// scripts/rules/josh-git-bare.ts
|
|
1664
|
+
var GIT_COMMAND = /* @__PURE__ */ new Set(["git"]);
|
|
1665
|
+
var CONFIRM_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes"]);
|
|
1666
|
+
function has_confirm_flag(args) {
|
|
1667
|
+
return args.some((argument) => CONFIRM_FLAGS.has(argument));
|
|
1668
|
+
}
|
|
1669
|
+
function is_bare_segment(segment) {
|
|
1670
|
+
if (!shell_segments.is_josh_command(segment, GIT_COMMAND)) return false;
|
|
1671
|
+
return !has_confirm_flag(time_shell.josh_arguments(segment));
|
|
1672
|
+
}
|
|
1673
|
+
function is_bare_josh_git(command) {
|
|
1674
|
+
return shell_segments.segments_of(command).some((segment) => is_bare_segment(segment));
|
|
1675
|
+
}
|
|
1676
|
+
var JOSH_GIT_BARE_REASON = '\u26D4 bare `pnpm josh git`: this prompts to confirm the staging, and with no TTY the prompt falls to its cancel branch. Run `pnpm josh git -y "<title> #<N>"`, or `pnpm josh git -y --skip-commit --skip-push` to open the PR for a push that already landed. It fires on every occurrence.';
|
|
1677
|
+
var ROW15 = {
|
|
1678
|
+
id: "josh-git-bare",
|
|
1679
|
+
is_trigger: bash_triggers.on_bash_command(is_bare_josh_git),
|
|
1680
|
+
reason: JOSH_GIT_BARE_REASON,
|
|
1681
|
+
decide: () => true,
|
|
1682
|
+
about: {
|
|
1683
|
+
title: "Bare `pnpm josh git`",
|
|
1684
|
+
topic: '`operating-rules.md` \u2192 "git-rules"',
|
|
1685
|
+
fires: "`pnpm josh git` without `-y`, whose staging prompt cancels with no TTY. Every occurrence",
|
|
1686
|
+
quiet: "`pnpm josh git -y`"
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
var josh_git_bare = {
|
|
1690
|
+
JOSH_GIT_BARE_REASON,
|
|
1691
|
+
ROW: ROW15,
|
|
1692
|
+
is_bare_josh_git
|
|
1693
|
+
};
|
|
1694
|
+
|
|
1695
|
+
// scripts/rules/lane-carry-conflict.ts
|
|
1696
|
+
var MERGE_COMMAND = "run:merge";
|
|
1697
|
+
var HANDOFF_COMMAND = "run:handoff-lanes";
|
|
1698
|
+
var RUN_BUDGET_COMMANDS = /* @__PURE__ */ new Set([
|
|
1699
|
+
MERGE_COMMAND,
|
|
1700
|
+
"run:carry",
|
|
1701
|
+
HANDOFF_COMMAND
|
|
1702
|
+
]);
|
|
1703
|
+
var LEADING_ASSIGNMENT = String.raw`[A-Za-z_]\w*=(?:"[^"]*"|'[^']*'|\$\((?:[^()]|\([^()]*\))*\)|[^\s"'])*\s+`;
|
|
1704
|
+
var REDIRECTED_PREFIX = new RegExp(
|
|
1705
|
+
String.raw`^(?:${LEADING_ASSIGNMENT})*${platform_temporary.TEMP_ROOT_KEY}=(?!""|''|\s|$|"?\$\{?[A-Za-z_])`,
|
|
1706
|
+
"u"
|
|
1707
|
+
);
|
|
1708
|
+
var SHARED_STATE_COMMANDS = /* @__PURE__ */ new Set([MERGE_COMMAND, HANDOFF_COMMAND]);
|
|
1709
|
+
var LEADING_ASSIGNMENTS = new RegExp(`^(?:${LEADING_ASSIGNMENT})*`, "u");
|
|
1710
|
+
function is_budget_segment(segment) {
|
|
1711
|
+
const commands = REDIRECTED_PREFIX.test(segment) ? SHARED_STATE_COMMANDS : RUN_BUDGET_COMMANDS;
|
|
1712
|
+
return shell_segments.is_josh_command(segment.replace(LEADING_ASSIGNMENTS, ""), commands);
|
|
1713
|
+
}
|
|
1714
|
+
function invokes_budget_command(command) {
|
|
1715
|
+
return shell_segments.segments_of(command).some((segment) => is_budget_segment(segment));
|
|
1716
|
+
}
|
|
1717
|
+
function is_carry_conflict(command, context) {
|
|
1718
|
+
if (!invokes_budget_command(command)) return false;
|
|
1719
|
+
return lane_child_marker.is_child_of(context.cwd, context.env);
|
|
1720
|
+
}
|
|
1721
|
+
var LANE_CARRY_CONFLICT_REASON = "\u26D4 lane child carry conflict: this session is a dispatched lane child, and `run:merge` / `run:carry` are the parent budget commands; the live record belongs to the parent, not to a competing run. Continue implementing this Issue: no `needs-decision`, no Telegram. Live `run:carry` evidence takes a fresh `JOSH_TEMP_ROOT`; `run:merge` is refused under any root, on every occurrence.";
|
|
1722
|
+
var ROW16 = {
|
|
1723
|
+
id: "lane-carry-conflict",
|
|
1724
|
+
is_trigger: bash_triggers.on_bash_command(is_carry_conflict),
|
|
1725
|
+
reason: LANE_CARRY_CONFLICT_REASON,
|
|
1726
|
+
decide: () => true,
|
|
1727
|
+
about: {
|
|
1728
|
+
title: "Lane child carry commands",
|
|
1729
|
+
topic: "`.claude/skills/workflow-commands/backlogrun-lanes.md`",
|
|
1730
|
+
fires: "a dispatched lane child running `pnpm josh run:merge` / `run:carry` / `run:handoff-lanes`. Every occurrence",
|
|
1731
|
+
quiet: "not a lane child \u2014 the parent owns the run budget"
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
var lane_carry_conflict = {
|
|
1735
|
+
LANE_CARRY_CONFLICT_REASON,
|
|
1736
|
+
ROW: ROW16,
|
|
1737
|
+
invokes_budget_command,
|
|
1738
|
+
is_carry_conflict
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
// scripts/rules/lane-direct-tool.ts
|
|
1742
|
+
import os from "node:os";
|
|
1743
|
+
|
|
1744
|
+
// scripts/rules/timed-rerun.ts
|
|
1745
|
+
var TIMED_SEGMENT = /^\s*(?:[A-Za-z_]\w*=\S*\s+)*(?:\S*\/)?(?:time|hyperfine)\s/u;
|
|
1746
|
+
function is_timed_run(command) {
|
|
1747
|
+
return shell_segments.quote_aware_commands_of(command).some((segment) => TIMED_SEGMENT.test(segment));
|
|
1748
|
+
}
|
|
1749
|
+
var RECORD_READ = /\bgh\s+api\b.*\/actions\/runs\b|\bjosh\s+(?:lane:stats|lst)\b/u;
|
|
1750
|
+
function reads_the_record(command) {
|
|
1751
|
+
return RECORD_READ.test(command);
|
|
1752
|
+
}
|
|
1753
|
+
function measures_duration(command) {
|
|
1754
|
+
return reads_the_record(command) || is_timed_run(command);
|
|
1755
|
+
}
|
|
1756
|
+
var TIMED_RERUN_REASON = "\u26D4 timed re-run: a CI or gate duration is already on record \u2014 read the Actions jobs with `gh api \u2026/actions/runs/<run_id>/jobs` or the gate timings with `pnpm josh lane:stats`. Re-run only when the record cannot answer: reissue the same command once, and post the result on the Issue (`prompts/collaboration-workflow/measurement.md`).";
|
|
1757
|
+
var ROW17 = {
|
|
1758
|
+
id: "timed-rerun",
|
|
1759
|
+
is_trigger: bash_triggers.on_bash_command(is_timed_run),
|
|
1760
|
+
reason: TIMED_RERUN_REASON,
|
|
1761
|
+
keeps: bash_triggers.on_bash_command(reads_the_record),
|
|
1762
|
+
reaches: bash_triggers.on_bash_command(measures_duration),
|
|
1763
|
+
about: {
|
|
1764
|
+
title: "Timed re-run",
|
|
1765
|
+
topic: "`measurement.md`",
|
|
1766
|
+
fires: "a command timed with `time` / `/usr/bin/time` / `hyperfine`",
|
|
1767
|
+
quiet: "the duration is read from the Actions jobs or `pnpm josh lane:stats`"
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
var timed_rerun = {
|
|
1771
|
+
ROW: ROW17,
|
|
1772
|
+
TIMED_RERUN_REASON,
|
|
1773
|
+
is_timed_run,
|
|
1774
|
+
reads_the_record
|
|
1775
|
+
};
|
|
1776
|
+
|
|
1777
|
+
// scripts/rules/lane-direct-tool.ts
|
|
1778
|
+
var ESLINT = "eslint";
|
|
1779
|
+
var VITEST = "vitest";
|
|
1780
|
+
var HEAVY_TOOLS = /* @__PURE__ */ new Set([ESLINT, VITEST, "cspell", "tsc"]);
|
|
1781
|
+
var TOOL_WEIGHTS = {
|
|
1782
|
+
[ESLINT]: core_weights.CORE_WEIGHTS.lint,
|
|
1783
|
+
tsc: core_weights.CORE_WEIGHTS.type_check,
|
|
1784
|
+
cspell: core_weights.CORE_WEIGHTS.spell_check
|
|
1785
|
+
};
|
|
1786
|
+
var SINGLE_CORE = 1;
|
|
1787
|
+
var RESERVED_RUN = "pnpm josh reserved-run";
|
|
1788
|
+
var MULTI_COMMAND_SYNTAX = /[;|&<>()`#\n]|\$\(/u;
|
|
1789
|
+
var KEPT_REDIRECT = /2>&1/gu;
|
|
1790
|
+
var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/u;
|
|
1791
|
+
var RESERVED_NOTE = "\u21BB lane direct tool: a lane child ran a heavy tool directly, so the line was wrapped in `pnpm josh reserved-run <weight> --` to claim its cores beside the other lanes. Prefer `pnpm josh lint:related` / `test:related` / `cspell:dot` \u2014 `docs/josh-commands-automation.md`.";
|
|
1792
|
+
function direct_calls(command) {
|
|
1793
|
+
return command_memory.tool_calls_of(command).filter((call) => !call.is_reserved && HEAVY_TOOLS.has(call.name));
|
|
1794
|
+
}
|
|
1795
|
+
function runs_heavy_tool(command) {
|
|
1796
|
+
return direct_calls(command).length > 0;
|
|
1797
|
+
}
|
|
1798
|
+
function weight_of(call, cores) {
|
|
1799
|
+
if (call.name === VITEST) return command_memory.workers_of(call, cores);
|
|
1800
|
+
return TOOL_WEIGHTS[call.name] ?? SINGLE_CORE;
|
|
1801
|
+
}
|
|
1802
|
+
function is_one_command(command) {
|
|
1803
|
+
const judged = time_shell.blanked(command);
|
|
1804
|
+
if (judged === void 0) return false;
|
|
1805
|
+
return !MULTI_COMMAND_SYNTAX.test(judged.replaceAll(KEPT_REDIRECT, " ")) && !ENV_ASSIGNMENT.test(judged);
|
|
1806
|
+
}
|
|
1807
|
+
function can_wrap(line, cores) {
|
|
1808
|
+
if (!is_one_command(line) || timed_rerun.is_timed_run(line)) return false;
|
|
1809
|
+
return !command_memory.runs_multi_worker_eslint(line, cores);
|
|
1810
|
+
}
|
|
1811
|
+
function reserved_command(command, cores = os.availableParallelism()) {
|
|
1812
|
+
const line = command.trim();
|
|
1813
|
+
const calls = direct_calls(line);
|
|
1814
|
+
if (!can_wrap(line, cores) || calls.length === 0) return void 0;
|
|
1815
|
+
const weight = Math.max(...calls.map((call) => weight_of(call, cores)));
|
|
1816
|
+
return `${RESERVED_RUN} ${String(weight)} -- ${line}`;
|
|
1817
|
+
}
|
|
1818
|
+
function is_lane_bash(call, context) {
|
|
1819
|
+
if (call.name !== cost_blocks.BASH_TOOL) return false;
|
|
1820
|
+
return lane_child_marker.is_child_of(context.cwd, context.env);
|
|
1821
|
+
}
|
|
1822
|
+
function reserved_input(call, _tail, context) {
|
|
1823
|
+
if (!is_lane_bash(call, context) || !json_value.is_record(call.input)) return void 0;
|
|
1824
|
+
const command = reserved_command(time_shell.bash_command(call.input));
|
|
1825
|
+
return command === void 0 ? void 0 : { ...call.input, command };
|
|
1826
|
+
}
|
|
1827
|
+
function is_lane_direct_tool(command, is_lane_child) {
|
|
1828
|
+
if (!is_lane_child) return false;
|
|
1829
|
+
return runs_heavy_tool(command) || command_memory.runs_multi_worker_eslint(command);
|
|
1830
|
+
}
|
|
1831
|
+
function is_lane_direct_tool_in(command, context) {
|
|
1832
|
+
return is_lane_direct_tool(command, lane_child_marker.is_child_of(context.cwd, context.env));
|
|
1833
|
+
}
|
|
1834
|
+
var LANE_DIRECT_TOOL_REASON = "\u26D4 lane child running `eslint` / `vitest` / `cspell` / `tsc` directly, or multi-worker eslint: it overlaps the other lanes. Run `pnpm josh lint:related`, `pnpm josh test:related`, `pnpm josh cspell:dot` or `pnpm josh reserved-run <weight> -- <command>`, eslint at `--concurrency off`. Only a one-command line is wrapped for you. Refused on every occurrence.";
|
|
1835
|
+
var ROW18 = {
|
|
1836
|
+
id: "lane-direct-tool",
|
|
1837
|
+
is_trigger: bash_triggers.on_bash_command(is_lane_direct_tool_in),
|
|
1838
|
+
reason: LANE_DIRECT_TOOL_REASON,
|
|
1839
|
+
decide: () => true,
|
|
1840
|
+
rewrite: reserved_input,
|
|
1841
|
+
rewrite_note: RESERVED_NOTE,
|
|
1842
|
+
about: {
|
|
1843
|
+
title: "Lane child running a heavy tool directly",
|
|
1844
|
+
topic: "`docs/josh-commands-automation.md` \u2192 `josh reserved-run`",
|
|
1845
|
+
fires: "a dispatched lane child launching `eslint` / `vitest` / `cspell` / `tsc` (bare, `pnpm exec`, `npx`, inside `sh -c`), or eslint with `--concurrency` other than `off` / `1` by any path, `reserved-run` included. A one-command line is rewritten behind `pnpm josh reserved-run <weight> --` at the gate's weight; a chained, piped, redirected or env-prefixed line and a multi-worker eslint are refused, every occurrence",
|
|
1846
|
+
quiet: "not a lane child, or the run goes through `pnpm josh` with single-worker eslint"
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
var lane_direct_tool = {
|
|
1850
|
+
LANE_DIRECT_TOOL_REASON,
|
|
1851
|
+
RESERVED_NOTE,
|
|
1852
|
+
ROW: ROW18,
|
|
1853
|
+
is_lane_direct_tool,
|
|
1854
|
+
reserved_command,
|
|
1855
|
+
reserved_input,
|
|
1856
|
+
runs_heavy_tool
|
|
1857
|
+
};
|
|
1858
|
+
|
|
1859
|
+
// scripts/rules/lane-interactive-ask.ts
|
|
1860
|
+
function is_interactive_ask(call, context) {
|
|
1861
|
+
if (!interactive_ask.is_interactive_tool(call.name)) return false;
|
|
1862
|
+
return lane_child_marker.is_child_of(context.cwd, context.env);
|
|
1863
|
+
}
|
|
1864
|
+
var LANE_INTERACTIVE_ASK_REASON = "\u26D4 lane child interactive ask: this session is a dispatched lane child and cannot ask a person, so park instead: `pnpm josh run:park <N> --body-file <path>` (it applies `needs-decision`), the `confirmation` Telegram, stop. It fires on every occurrence (`.claude/skills/workflow-commands/pre-gate-cut.md`, `backlogrun-park.md`).";
|
|
1865
|
+
var ROW19 = {
|
|
1866
|
+
id: "lane-interactive-ask",
|
|
1867
|
+
is_trigger: is_interactive_ask,
|
|
1868
|
+
reason: LANE_INTERACTIVE_ASK_REASON,
|
|
1869
|
+
decide: () => true,
|
|
1870
|
+
// A reply asking a person for the git index is the same ask, delivered by the Stop hook.
|
|
1871
|
+
stop_reasons: [stop_rules.LANE_INDEX_REASON],
|
|
1872
|
+
about: {
|
|
1873
|
+
title: "Lane child interactive ask",
|
|
1874
|
+
topic: '`.claude/skills/workflow-commands/pre-gate-cut.md` \u2192 "The interactive ask is refused one call earlier"',
|
|
1875
|
+
fires: "a dispatched lane child's `AskUserQuestion`; it hands over the park procedure; the Stop hook also blocks a reply asking a person for the git index. Every occurrence",
|
|
1876
|
+
quiet: "not a lane child, or not an interactive tool"
|
|
1877
|
+
}
|
|
1878
|
+
};
|
|
1879
|
+
var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW19, is_interactive_ask };
|
|
1880
|
+
|
|
1881
|
+
// scripts/rules/lane-spilled-read.ts
|
|
1882
|
+
var SPILLED_RESULT = /\/tool-results\/[^/]+\.txt$/u;
|
|
1883
|
+
function read_input(call) {
|
|
1884
|
+
if (call.name !== "Read" || !json_value.is_record(call.input)) return void 0;
|
|
1885
|
+
return call.input;
|
|
1886
|
+
}
|
|
1887
|
+
function reads_spilled_result(input) {
|
|
1888
|
+
const { file_path } = input;
|
|
1889
|
+
return typeof file_path === "string" && SPILLED_RESULT.test(file_path);
|
|
1890
|
+
}
|
|
1891
|
+
function is_paginated(input) {
|
|
1892
|
+
return input["offset"] !== void 0 || input["limit"] !== void 0;
|
|
1893
|
+
}
|
|
1894
|
+
function reaches_spilled_result(call) {
|
|
1895
|
+
const input = read_input(call);
|
|
1896
|
+
return input !== void 0 && reads_spilled_result(input);
|
|
1897
|
+
}
|
|
1898
|
+
function keeps_narrowed_read(call) {
|
|
1899
|
+
const input = read_input(call);
|
|
1900
|
+
return input !== void 0 && reads_spilled_result(input) && is_paginated(input);
|
|
1901
|
+
}
|
|
1902
|
+
function is_whole_spilled_read(call, context) {
|
|
1903
|
+
if (!reaches_spilled_result(call) || keeps_narrowed_read(call)) return false;
|
|
1904
|
+
return lane_child_marker.is_child_of(context.cwd, context.env);
|
|
1905
|
+
}
|
|
1906
|
+
var LANE_SPILLED_READ_REASON = "\u26D4 lane child reading a spilled Bash result whole: `tool-results/<id>.txt` is the output that overflowed the cap, and reading it back whole fills the context the cut is measured on. Read only the lines you need: `grep -n <pattern> <path>` or `sed -n <from>,<to>p <path>`. Refused on every occurrence.";
|
|
1907
|
+
var ROW20 = {
|
|
1908
|
+
id: "lane-spilled-read",
|
|
1909
|
+
is_trigger: is_whole_spilled_read,
|
|
1910
|
+
reason: LANE_SPILLED_READ_REASON,
|
|
1911
|
+
decide: () => true,
|
|
1912
|
+
keeps: keeps_narrowed_read,
|
|
1913
|
+
reaches: reaches_spilled_result,
|
|
1914
|
+
about: {
|
|
1915
|
+
title: "Lane child reading a spilled Bash result whole",
|
|
1916
|
+
topic: "`prompts/collaboration-workflow/output-bounds.md`",
|
|
1917
|
+
fires: "a dispatched lane child's `Read` of a `tool-results/<id>.txt` file with no `offset` or `limit`. Every occurrence",
|
|
1918
|
+
quiet: "not a lane child, another file, or a paginated `Read`"
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
var lane_spilled_read = {
|
|
1922
|
+
LANE_SPILLED_READ_REASON,
|
|
1923
|
+
ROW: ROW20,
|
|
1924
|
+
is_whole_spilled_read,
|
|
1925
|
+
keeps_narrowed_read,
|
|
1926
|
+
reaches_spilled_result
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
// scripts/rules/lane-split-park.ts
|
|
1930
|
+
var EPIC_COMMANDS = /* @__PURE__ */ new Set(["epic"]);
|
|
1931
|
+
var PROMOTED_ISSUE = /--promote[=\s]+["']?(?<issue>\d+)\b/u;
|
|
1932
|
+
var LABEL_TARGETS = [
|
|
1933
|
+
/\/issues\/(?<issue>\d+)\/labels\b/u,
|
|
1934
|
+
/\bissue\s+edit\s+["']?(?<issue>\d+)\b/u,
|
|
1935
|
+
/\brun:park\s+["']?(?<issue>\d+)\b/u
|
|
1936
|
+
];
|
|
1937
|
+
var ISSUE_URL2 = /--issue-url[=\s]+["']?([^\s"']+)/u;
|
|
1938
|
+
var URL_ISSUE = /\/issues\/(\d+)\b/u;
|
|
1939
|
+
function promoted_in(segment) {
|
|
1940
|
+
if (!shell_segments.is_josh_command(segment, EPIC_COMMANDS)) return void 0;
|
|
1941
|
+
return PROMOTED_ISSUE.exec(segment)?.groups?.["issue"];
|
|
1942
|
+
}
|
|
1943
|
+
function promoted_issues(commands) {
|
|
1944
|
+
const segments = commands.flatMap((command) => shell_segments.segments_of(command));
|
|
1945
|
+
const issues = segments.map((segment) => promoted_in(segment));
|
|
1946
|
+
return new Set(issues.filter((issue) => issue !== void 0));
|
|
1947
|
+
}
|
|
1948
|
+
function label_target(command) {
|
|
1949
|
+
for (const pattern of LABEL_TARGETS) {
|
|
1950
|
+
const issue = pattern.exec(command)?.groups?.["issue"];
|
|
1951
|
+
if (issue !== void 0) return issue;
|
|
1952
|
+
}
|
|
1953
|
+
return void 0;
|
|
1954
|
+
}
|
|
1955
|
+
function is_park_of(command, issue) {
|
|
1956
|
+
return lane_park.records_the_park(command) && label_target(command) === issue;
|
|
1957
|
+
}
|
|
1958
|
+
function notified_issue(command) {
|
|
1959
|
+
const [, url = ""] = ISSUE_URL2.exec(command) ?? [];
|
|
1960
|
+
const [, issue] = URL_ISSUE.exec(url) ?? [];
|
|
1961
|
+
return issue;
|
|
1962
|
+
}
|
|
1963
|
+
function is_notify_of(command, issue) {
|
|
1964
|
+
return lane_park.is_confirmation_notify(command) && notified_issue(command) === issue;
|
|
1965
|
+
}
|
|
1966
|
+
function is_candidate3(command, context) {
|
|
1967
|
+
const issue = lane_child_marker.marked_issue(context.env);
|
|
1968
|
+
if (issue === void 0) return false;
|
|
1969
|
+
if (!is_park_of(command, issue) && !is_notify_of(command, issue)) return false;
|
|
1970
|
+
return lane_child_marker.is_child_of(context.cwd, context.env);
|
|
1971
|
+
}
|
|
1972
|
+
function decide2(_call, run) {
|
|
1973
|
+
const issue = lane_child_marker.marked_issue(run.context.env);
|
|
1974
|
+
const commands = tail_commands.prior_bash_commands(transcript_tail.read_tail(run.transcript));
|
|
1975
|
+
return issue !== void 0 && promoted_issues(commands).has(issue);
|
|
1976
|
+
}
|
|
1977
|
+
var LANE_SPLIT_PARK_REASON = '\u26D4 lane split park: this session is a dispatched lane child that promoted its own Issue to an epic this run, and a split is not a park: no `needs-decision` on the epic, no `confirmation` Telegram, on every occurrence. Park a child issue if the rest needs a person, else end the turn (`.claude/skills/workflow-commands/backlogrun-park.md` \u2192 "Splitting a child mid-run").';
|
|
1978
|
+
var ROW21 = {
|
|
1979
|
+
id: "lane-split-park",
|
|
1980
|
+
is_trigger: bash_triggers.on_bash_command(is_candidate3),
|
|
1981
|
+
reason: LANE_SPLIT_PARK_REASON,
|
|
1982
|
+
decide: decide2,
|
|
1983
|
+
about: {
|
|
1984
|
+
title: "A split is not a park",
|
|
1985
|
+
topic: '`.claude/skills/workflow-commands/backlogrun-park.md` \u2192 "Splitting a child mid-run"',
|
|
1986
|
+
fires: "a lane child that promoted its Issue to an epic this run parking it (`needs-decision`, `confirmation` notify). Every occurrence",
|
|
1987
|
+
quiet: "no promotion this run"
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
var lane_split_park = {
|
|
1991
|
+
LANE_SPLIT_PARK_REASON,
|
|
1992
|
+
ROW: ROW21,
|
|
1993
|
+
promoted_issues
|
|
1994
|
+
};
|
|
1995
|
+
|
|
1996
|
+
// scripts/rules/lane-switch-main.ts
|
|
1997
|
+
var SWITCH_SUBCOMMAND = "switch";
|
|
1998
|
+
var NON_COLLIDING_FLAGS = /* @__PURE__ */ new Set([
|
|
1999
|
+
"-c",
|
|
2000
|
+
"-C",
|
|
2001
|
+
"--create",
|
|
2002
|
+
"--force-create",
|
|
2003
|
+
"-d",
|
|
2004
|
+
"--detach"
|
|
2005
|
+
]);
|
|
2006
|
+
var FLAG_PREFIX3 = "-";
|
|
2007
|
+
function switch_target(segment) {
|
|
2008
|
+
const call = git_argv.parse(segment);
|
|
2009
|
+
if (call?.subcommand !== SWITCH_SUBCOMMAND || call.args.some((argument) => NON_COLLIDING_FLAGS.has(argument))) {
|
|
2010
|
+
return void 0;
|
|
2011
|
+
}
|
|
2012
|
+
return call.args.find((argument) => !argument.startsWith(FLAG_PREFIX3));
|
|
2013
|
+
}
|
|
2014
|
+
function switch_targets(command) {
|
|
2015
|
+
return shell_segments.segments_of(command).map((segment) => switch_target(segment)).filter((target) => target !== void 0);
|
|
2016
|
+
}
|
|
2017
|
+
function own_lane_branch(directory) {
|
|
2018
|
+
const issue = lane_paths.lane_issue_of(directory);
|
|
2019
|
+
return issue === void 0 ? void 0 : lane_paths.lane_branch(issue);
|
|
2020
|
+
}
|
|
2021
|
+
function is_switch_away_from_lane(command, is_lane_child, own_branch) {
|
|
2022
|
+
if (!is_lane_child || own_branch === void 0) return false;
|
|
2023
|
+
return switch_targets(command).some((target) => target !== own_branch);
|
|
2024
|
+
}
|
|
2025
|
+
function is_switch_away_in(command, context) {
|
|
2026
|
+
const is_lane_child = lane_child_marker.is_child_of(context.cwd, context.env);
|
|
2027
|
+
return is_switch_away_from_lane(command, is_lane_child, own_lane_branch(context.cwd));
|
|
2028
|
+
}
|
|
2029
|
+
var LANE_SWITCH_MAIN_REASON = "\u26D4 lane child on `git switch main`: this session is a dispatched lane child, and switching to the default branch here always fails (`already used by worktree`: the primary checkout holds it). Skip this step and keep implementing on the current branch; `pnpm josh ship`'s sync brings the default in before its gate. Refused on every occurrence (`backlogrun-lanes.md`, `backlogrun-child.md`).";
|
|
2030
|
+
var ROW22 = {
|
|
2031
|
+
id: "lane-switch-main",
|
|
2032
|
+
is_trigger: bash_triggers.on_bash_command(is_switch_away_in),
|
|
2033
|
+
reason: LANE_SWITCH_MAIN_REASON,
|
|
2034
|
+
decide: () => true,
|
|
2035
|
+
about: {
|
|
2036
|
+
title: "Lane child on `git switch main`",
|
|
2037
|
+
topic: "`.claude/skills/workflow-commands/backlogrun-lanes.md`",
|
|
2038
|
+
fires: "a dispatched lane child switching to the default branch or running `josh main:sync`. Every occurrence",
|
|
2039
|
+
quiet: "not a lane child \u2014 `pnpm josh main:merge` brings the default in at the gate"
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
var lane_switch_main = {
|
|
2043
|
+
LANE_SWITCH_MAIN_REASON,
|
|
2044
|
+
ROW: ROW22,
|
|
2045
|
+
is_switch_away_from_lane,
|
|
2046
|
+
switch_target,
|
|
2047
|
+
switch_targets
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
// scripts/rules/oracle-consulted.ts
|
|
2051
|
+
function command_names(oracle) {
|
|
2052
|
+
return /* @__PURE__ */ new Set([decision_oracle.get_command(oracle)]);
|
|
2053
|
+
}
|
|
2054
|
+
function runs_the_oracle(command, names) {
|
|
2055
|
+
return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names));
|
|
2056
|
+
}
|
|
2057
|
+
function command_line(oracle) {
|
|
2058
|
+
const command = decision_oracle.get_command(oracle);
|
|
2059
|
+
return oracle.args ? `pnpm josh ${command} ${oracle.args}` : `pnpm josh ${command}`;
|
|
2060
|
+
}
|
|
2061
|
+
function reason_for(oracle, firing_point) {
|
|
2062
|
+
return `\u26D4 decision oracle not consulted: this is ${firing_point.describes}. Run \`${command_line(oracle)}\` first \u2014 it decides ${oracle.decision.toLowerCase()} and answers ${oracle.vocabulary.join(" | ")} (${oracle.single_source}). Every such call is refused until that command is on the transcript.`;
|
|
2063
|
+
}
|
|
2064
|
+
var ABOUT = {
|
|
2065
|
+
title: "An act its oracle was not asked about",
|
|
2066
|
+
topic: "`decision-oracle.ts`",
|
|
2067
|
+
fires: "a `Bash` governed by an oracle that declared a firing point (`pkg:scout` governs `pnpm add`), issued without that `pnpm josh <command>`. Refused until it ran",
|
|
2068
|
+
quiet: "no governed act, or the oracle already ran"
|
|
2069
|
+
};
|
|
2070
|
+
function row_for(oracle, firing_point) {
|
|
2071
|
+
const names = command_names(oracle);
|
|
2072
|
+
return {
|
|
2073
|
+
id: `oracle-consulted:${oracle.name}`,
|
|
2074
|
+
is_trigger: bash_triggers.on_bash_command(firing_point.governs),
|
|
2075
|
+
reason: reason_for(oracle, firing_point),
|
|
2076
|
+
already_satisfied: (tail, _call) => tail_commands.prior_bash_commands(tail).some((command) => runs_the_oracle(command, names)),
|
|
2077
|
+
keeps: bash_triggers.on_bash_command((command) => runs_the_oracle(command, names)),
|
|
2078
|
+
about: ABOUT
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
var ROWS = oracle_firing.firing_oracles().map(({ oracle, firing_point }) => row_for(oracle, firing_point));
|
|
2082
|
+
var oracle_consulted = { ROWS, reason_for };
|
|
2083
|
+
|
|
2084
|
+
// scripts/rules/destructive-command.ts
|
|
2085
|
+
var RM_COMMAND = "rm";
|
|
2086
|
+
var RECURSIVE_LONG = "--recursive";
|
|
2087
|
+
var RECURSIVE_LETTERS2 = "rR";
|
|
2088
|
+
var FORCE_LONG = "--force";
|
|
2089
|
+
var FORCE_LETTER = "f";
|
|
2090
|
+
function has_flag(args, long, letters) {
|
|
2091
|
+
return args.some((argument) => argument === long || git_argv.short_cluster_has(argument, letters));
|
|
2092
|
+
}
|
|
2093
|
+
function is_recursive_forced_rm(segment) {
|
|
2094
|
+
const args = git_argv.arguments_of(segment, RM_COMMAND);
|
|
2095
|
+
if (args === void 0) return false;
|
|
2096
|
+
return has_flag(args, RECURSIVE_LONG, RECURSIVE_LETTERS2) && has_flag(args, FORCE_LONG, FORCE_LETTER);
|
|
2097
|
+
}
|
|
2098
|
+
var GH_COMMAND = "gh";
|
|
2099
|
+
var REPO_GROUP = "repo";
|
|
2100
|
+
var REPO_REMOVALS = /* @__PURE__ */ new Set(["delete", "archive"]);
|
|
2101
|
+
var PR_GROUP = "pr";
|
|
2102
|
+
var PR_CLOSE = "close";
|
|
2103
|
+
function is_destructive_gh_command(args) {
|
|
2104
|
+
const [group, action = ""] = args;
|
|
2105
|
+
if (group === PR_GROUP) return action === PR_CLOSE;
|
|
2106
|
+
return group === REPO_GROUP && REPO_REMOVALS.has(action);
|
|
2107
|
+
}
|
|
2108
|
+
var DELETE_METHOD = "DELETE";
|
|
2109
|
+
var REPOSITORY_PATH = "repos/";
|
|
2110
|
+
var ISSUE_LABEL_PATH = /^['"]?\/?repos\/[^/\s'"]+\/[^/\s'"]+\/issues\/\d+\/labels\/[^/\s'"]+['"]?$/u;
|
|
2111
|
+
function is_destructive_api_call(args) {
|
|
2112
|
+
const call = [GH_COMMAND, ...args].join(" ");
|
|
2113
|
+
if (!gh_api.is_gh_api(call) || gh_api.method_of(call) !== DELETE_METHOD) return false;
|
|
2114
|
+
return args.some(
|
|
2115
|
+
(argument) => argument.includes(REPOSITORY_PATH) && !ISSUE_LABEL_PATH.test(argument)
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
function is_destructive_gh(segment) {
|
|
2119
|
+
const args = git_argv.arguments_of(segment, GH_COMMAND);
|
|
2120
|
+
if (args === void 0) return false;
|
|
2121
|
+
return is_destructive_gh_command(args) || is_destructive_api_call(args);
|
|
2122
|
+
}
|
|
2123
|
+
function is_destructive_segment2(segment) {
|
|
2124
|
+
return is_recursive_forced_rm(segment) || is_destructive_gh(segment);
|
|
2125
|
+
}
|
|
2126
|
+
function is_destructive_command(command) {
|
|
2127
|
+
return shell_segments.commands_of(command).some((segment) => is_destructive_segment2(segment));
|
|
2128
|
+
}
|
|
2129
|
+
var DESTRUCTIVE_COMMAND_REASON = "\u26D4 destructive command: a recursive forced `rm`, a `gh repo delete` / `archive`, `gh pr close` or a `gh api -X DELETE` cannot be undone, which is Tier C. Remove one path at a time without `-f`, or ask the user to run it. A blocked-by removal: `pnpm josh backlog:triage --apply <N> --unblock <M> --reason <text>`. It fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md`).";
|
|
2130
|
+
var ROW23 = {
|
|
2131
|
+
id: "destructive-command",
|
|
2132
|
+
is_trigger: bash_triggers.on_bash_command(is_destructive_command),
|
|
2133
|
+
reason: DESTRUCTIVE_COMMAND_REASON,
|
|
2134
|
+
decide: () => true,
|
|
2135
|
+
// The opening the reason carried before it was cut to the action and the next command.
|
|
2136
|
+
former_reasons: ["\u26D4 destructive command: a recursive forced `rm` (`rm -rf`"],
|
|
2137
|
+
about: {
|
|
2138
|
+
title: "Destructive command",
|
|
2139
|
+
topic: "`operating-rules.md`",
|
|
2140
|
+
fires: "`rm -rf` (any spelling), `gh repo delete` / `archive`, `gh pr close`, `gh api -X DELETE`. Every occurrence",
|
|
2141
|
+
quiet: "`gh issue close` or removing an Issue label \u2014 the workflow's own steps"
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW23, is_destructive_command };
|
|
2145
|
+
|
|
2146
|
+
// scripts/rules/keychain-read.ts
|
|
2147
|
+
var SECURITY_COMMAND = "security";
|
|
2148
|
+
var FLAG_PREFIX4 = "-";
|
|
2149
|
+
var PROMPT_FLAG = "p";
|
|
2150
|
+
var STEP = 1;
|
|
2151
|
+
var VALUE_STEP = 2;
|
|
2152
|
+
var INTERACTIVE_FLAG = "i";
|
|
2153
|
+
var SECRET_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
2154
|
+
"dump-keychain",
|
|
2155
|
+
"export",
|
|
2156
|
+
"export-identities",
|
|
2157
|
+
"find-generic-password",
|
|
2158
|
+
"find-internet-password"
|
|
2159
|
+
]);
|
|
2160
|
+
function cluster_of(word) {
|
|
2161
|
+
const letters = word.slice(FLAG_PREFIX4.length);
|
|
2162
|
+
const prompt_at = letters.indexOf(PROMPT_FLAG);
|
|
2163
|
+
if (prompt_at === -1) return { flags: letters, step: STEP };
|
|
2164
|
+
const step = prompt_at === letters.length - 1 ? VALUE_STEP : STEP;
|
|
2165
|
+
return { flags: letters.slice(0, prompt_at), step };
|
|
2166
|
+
}
|
|
2167
|
+
function global_options(args) {
|
|
2168
|
+
const [word = ""] = args;
|
|
2169
|
+
if (!word.startsWith(FLAG_PREFIX4)) return { is_interactive: false, subcommand: 0 };
|
|
2170
|
+
const { flags, step } = cluster_of(word);
|
|
2171
|
+
const rest = global_options(args.slice(step));
|
|
2172
|
+
return {
|
|
2173
|
+
is_interactive: flags.includes(INTERACTIVE_FLAG) || rest.is_interactive,
|
|
2174
|
+
subcommand: step + rest.subcommand
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
function is_keychain_read(_segment, call) {
|
|
2178
|
+
if (call.name !== SECURITY_COMMAND) return false;
|
|
2179
|
+
const { is_interactive, subcommand } = global_options(call.args);
|
|
2180
|
+
return is_interactive || SECRET_SUBCOMMANDS.has(call.args[subcommand] ?? "");
|
|
2181
|
+
}
|
|
2182
|
+
var keychain_read = { is_keychain_read };
|
|
2183
|
+
|
|
2184
|
+
// scripts/rules/launched-call.ts
|
|
2185
|
+
var FLAG_PREFIX5 = "-";
|
|
2186
|
+
var NEXT = 1;
|
|
2187
|
+
var BEFORE_PROGRAM = 2;
|
|
2188
|
+
var ENV_VALUE_FLAGS = /* @__PURE__ */ new Set(["-u", "--unset", "-C", "--chdir"]);
|
|
2189
|
+
var NAME_NOISE = /["'\\]/gu;
|
|
2190
|
+
var LOOKUP_WRAPPER = "command";
|
|
2191
|
+
var LOOKUP_FLAGS = /* @__PURE__ */ new Set(["-v", "-V"]);
|
|
2192
|
+
function launcher(value_flags, operands = 0) {
|
|
2193
|
+
return { value_flags: new Set(value_flags), operands };
|
|
2194
|
+
}
|
|
2195
|
+
var LAUNCHERS = /* @__PURE__ */ new Map([
|
|
2196
|
+
["busybox", launcher([])],
|
|
2197
|
+
["nice", launcher(["-n", "--adjustment"])],
|
|
2198
|
+
["nohup", launcher([])],
|
|
2199
|
+
["stdbuf", launcher(["-i", "-o", "-e", "--input", "--output", "--error"])],
|
|
2200
|
+
["timeout", launcher(["-s", "--signal", "-k", "--kill-after"], 1)],
|
|
2201
|
+
["xcrun", launcher(["--sdk", "-sdk", "--toolchain", "-toolchain"])],
|
|
2202
|
+
[
|
|
2203
|
+
"xargs",
|
|
2204
|
+
launcher(["-a", "--arg-file", "-d", "--delimiter", "-E", "-I", "-L", "-n", "-P", "-s"])
|
|
2205
|
+
]
|
|
2206
|
+
]);
|
|
2207
|
+
function plain_name(word) {
|
|
2208
|
+
return word.replaceAll(NAME_NOISE, "").split("/").at(-1) ?? "";
|
|
2209
|
+
}
|
|
2210
|
+
function is_environment_operand(words, index) {
|
|
2211
|
+
const word = words[index] ?? "";
|
|
2212
|
+
return !word.startsWith(FLAG_PREFIX5) && !word.includes("=") && !ENV_VALUE_FLAGS.has(words[index - NEXT] ?? "");
|
|
2213
|
+
}
|
|
2214
|
+
function past_environment_flags(words) {
|
|
2215
|
+
const index = words.findIndex((_word, position) => is_environment_operand(words, position));
|
|
2216
|
+
return index === -1 ? [] : words.slice(index);
|
|
2217
|
+
}
|
|
2218
|
+
function is_launcher_operand(args, index, value_flags) {
|
|
2219
|
+
return !(args[index] ?? "").startsWith(FLAG_PREFIX5) && !value_flags.has(args[index - NEXT] ?? "");
|
|
2220
|
+
}
|
|
2221
|
+
function launched_index(args, spec) {
|
|
2222
|
+
const operands = args.map((_word, index) => index).filter((index) => is_launcher_operand(args, index, spec.value_flags));
|
|
2223
|
+
return operands[spec.operands] ?? -1;
|
|
2224
|
+
}
|
|
2225
|
+
function past_launcher(call) {
|
|
2226
|
+
const spec = LAUNCHERS.get(call.name);
|
|
2227
|
+
if (spec === void 0) return void 0;
|
|
2228
|
+
const index = launched_index(call.args, spec);
|
|
2229
|
+
return index === -1 ? void 0 : call.args.slice(index);
|
|
2230
|
+
}
|
|
2231
|
+
function inner_words_of(call) {
|
|
2232
|
+
if (call === void 0) return void 0;
|
|
2233
|
+
if (call.name.startsWith(FLAG_PREFIX5)) return past_environment_flags([call.name, ...call.args]);
|
|
2234
|
+
return past_launcher(call);
|
|
2235
|
+
}
|
|
2236
|
+
function word_before(segment, call) {
|
|
2237
|
+
const words = git_argv.words_of(segment);
|
|
2238
|
+
return words[words.length - call.args.length - BEFORE_PROGRAM] ?? "";
|
|
2239
|
+
}
|
|
2240
|
+
function call_of2(segment) {
|
|
2241
|
+
const call = git_argv.program_of(segment);
|
|
2242
|
+
if (call === void 0) return void 0;
|
|
2243
|
+
const is_lookup = LOOKUP_FLAGS.has(call.name) && plain_name(word_before(segment, call)) === LOOKUP_WRAPPER;
|
|
2244
|
+
return { name: is_lookup ? LOOKUP_WRAPPER : plain_name(call.name), args: call.args };
|
|
2245
|
+
}
|
|
2246
|
+
function resolved_call(segment) {
|
|
2247
|
+
let call = call_of2(segment);
|
|
2248
|
+
let inner = inner_words_of(call);
|
|
2249
|
+
while (inner !== void 0) {
|
|
2250
|
+
call = call_of2(inner.join(" "));
|
|
2251
|
+
inner = inner_words_of(call);
|
|
2252
|
+
}
|
|
2253
|
+
return call;
|
|
2254
|
+
}
|
|
2255
|
+
var launched_call = { plain_name, resolved_call };
|
|
2256
|
+
|
|
2257
|
+
// scripts/rules/nested-commands.ts
|
|
2258
|
+
var { SHELLS } = interpreter_options;
|
|
2259
|
+
var EVAL = "eval";
|
|
2260
|
+
var SUBSTITUTION = /[$<>]\((?<paren>[^()]*)\)|`(?<backtick>[^`]*)`/gu;
|
|
2261
|
+
var OUTER_QUOTES = /^["']|["']$/gu;
|
|
2262
|
+
var NEXT2 = 1;
|
|
2263
|
+
function is_shell(segment) {
|
|
2264
|
+
const program = git_argv.program_of(segment)?.name;
|
|
2265
|
+
return program !== void 0 && SHELLS.has(program);
|
|
2266
|
+
}
|
|
2267
|
+
function quoted_script_of(args, name) {
|
|
2268
|
+
if (name === EVAL) return args.join(" ");
|
|
2269
|
+
const flag_index = interpreter_options.shell_script_index({ name, args: [...args] });
|
|
2270
|
+
return flag_index === -1 ? "" : args[flag_index + NEXT2] ?? "";
|
|
2271
|
+
}
|
|
2272
|
+
function script_of(segment) {
|
|
2273
|
+
const call = git_argv.program_of(segment);
|
|
2274
|
+
if (call === void 0) return "";
|
|
2275
|
+
return quoted_script_of(call.args, call.name).replaceAll(OUTER_QUOTES, "");
|
|
2276
|
+
}
|
|
2277
|
+
function bodies_of(segment) {
|
|
2278
|
+
const substituted = [...segment.matchAll(SUBSTITUTION)].map(
|
|
2279
|
+
(match) => match.groups?.["paren"] ?? match.groups?.["backtick"] ?? ""
|
|
2280
|
+
);
|
|
2281
|
+
const script = is_shell(segment) ? [script_of(segment)] : [];
|
|
2282
|
+
return [...script, ...substituted].filter((body) => body !== "");
|
|
2283
|
+
}
|
|
2284
|
+
function commands_of(command) {
|
|
2285
|
+
const found = [];
|
|
2286
|
+
let pending = [command];
|
|
2287
|
+
while (pending.length > 0) {
|
|
2288
|
+
const segments = pending.flatMap((line) => shell_segments.quote_aware_commands_of(line));
|
|
2289
|
+
found.push(...segments);
|
|
2290
|
+
pending = segments.flatMap((segment) => bodies_of(segment));
|
|
2291
|
+
}
|
|
2292
|
+
return found;
|
|
2293
|
+
}
|
|
2294
|
+
var nested_commands = { SHELLS, commands_of };
|
|
2295
|
+
|
|
2296
|
+
// scripts/rules/gh-repo-selector.ts
|
|
2297
|
+
var GH_COMMAND2 = "gh";
|
|
2298
|
+
var API_GROUP = "api";
|
|
2299
|
+
var SELECTOR_FLAGS = /* @__PURE__ */ new Set(["-R", "--repo"]);
|
|
2300
|
+
var ATTACHED_SELECTOR = /^(?:-R=?|--repo=)(.+)$/u;
|
|
2301
|
+
var QUOTES3 = /['"]/gu;
|
|
2302
|
+
var NEXT3 = 1;
|
|
2303
|
+
var READ_GROUPS = /* @__PURE__ */ new Set(["browse", "search", "status"]);
|
|
2304
|
+
var READ_ACTIONS = /* @__PURE__ */ new Set([
|
|
2305
|
+
"check",
|
|
2306
|
+
"checkout",
|
|
2307
|
+
"checks",
|
|
2308
|
+
"clone",
|
|
2309
|
+
"diff",
|
|
2310
|
+
"download",
|
|
2311
|
+
"get",
|
|
2312
|
+
"list",
|
|
2313
|
+
"status",
|
|
2314
|
+
"verify",
|
|
2315
|
+
"verify-asset",
|
|
2316
|
+
"view",
|
|
2317
|
+
"watch"
|
|
2318
|
+
]);
|
|
2319
|
+
var GH_REPO_ASSIGNMENT = /(?:^|\s)GH_REPO=(\S+)/gu;
|
|
2320
|
+
var SELECTOR_GROUP = 1;
|
|
2321
|
+
function selector_value(word, next) {
|
|
2322
|
+
if (SELECTOR_FLAGS.has(word)) return next;
|
|
2323
|
+
return ATTACHED_SELECTOR.exec(word)?.[1];
|
|
2324
|
+
}
|
|
2325
|
+
function selectors_of(args) {
|
|
2326
|
+
return args.map((word, index) => selector_value(word, args[index + NEXT3])).filter((value) => value !== void 0).map((value) => value.replaceAll(QUOTES3, ""));
|
|
2327
|
+
}
|
|
2328
|
+
function is_write_call(args) {
|
|
2329
|
+
const [group = "", action = ""] = direct_pr_create.positional_words(args, SELECTOR_FLAGS);
|
|
2330
|
+
if (group === API_GROUP || READ_GROUPS.has(group)) return false;
|
|
2331
|
+
return !READ_ACTIONS.has(action);
|
|
2332
|
+
}
|
|
2333
|
+
function assigned_selectors(segments) {
|
|
2334
|
+
return segments.flatMap((segment) => [...segment.matchAll(GH_REPO_ASSIGNMENT)]).map((match) => (match[SELECTOR_GROUP] ?? "").replaceAll(QUOTES3, ""));
|
|
2335
|
+
}
|
|
2336
|
+
function write_selectors(segments, index) {
|
|
2337
|
+
const args = git_argv.arguments_of(segments[index] ?? "", GH_COMMAND2);
|
|
2338
|
+
if (args === void 0 || !is_write_call(args)) return [];
|
|
2339
|
+
return [...selectors_of(args), ...assigned_selectors(segments.slice(0, index + NEXT3))];
|
|
2340
|
+
}
|
|
2341
|
+
var gh_repo_selector = { is_write_call, write_selectors };
|
|
2342
|
+
|
|
2343
|
+
// scripts/rules/pr-merge.ts
|
|
2344
|
+
var GH_COMMAND3 = "gh";
|
|
2345
|
+
var CURL_COMMAND = "curl";
|
|
2346
|
+
var PR_GROUP2 = "pr";
|
|
2347
|
+
var MERGE_ACTION = "merge";
|
|
2348
|
+
var API_SUBCOMMAND3 = "api";
|
|
2349
|
+
var GRAPHQL_ENDPOINT = "graphql";
|
|
2350
|
+
var MERGE_PATH = /pulls\/[^/\s'"]+\/merge(?![\w-])/u;
|
|
2351
|
+
var MERGE_MUTATIONS = /\b(?:mergePullRequest|enablePullRequestAutoMerge)\b/u;
|
|
2352
|
+
var GRAPHQL_URL = /\/graphql\b/u;
|
|
2353
|
+
var FILE_BODY_VALUE = /^(?:-[A-Za-z]|[^=\s]*=)?['"]?@/u;
|
|
2354
|
+
var FILE_BODY_FLAGS = /* @__PURE__ */ new Set(["--input", "-T", "--upload-file"]);
|
|
2355
|
+
var SUBSTITUTED_BODY = /\$\(|`/u;
|
|
2356
|
+
function positional_words(args) {
|
|
2357
|
+
return direct_pr_create.positional_words(args, direct_pr_create.API_VALUE_TAKING_FLAGS);
|
|
2358
|
+
}
|
|
2359
|
+
function is_pr_merge_cli_call(args) {
|
|
2360
|
+
const [group, action] = positional_words(args);
|
|
2361
|
+
return group === PR_GROUP2 && action === MERGE_ACTION;
|
|
2362
|
+
}
|
|
2363
|
+
function is_github_api_call(name, args) {
|
|
2364
|
+
if (name === CURL_COMMAND) return true;
|
|
2365
|
+
return name === GH_COMMAND3 && positional_words(args)[0] === API_SUBCOMMAND3;
|
|
2366
|
+
}
|
|
2367
|
+
function is_graphql_call(name, args) {
|
|
2368
|
+
if (args.some((argument) => GRAPHQL_URL.test(argument))) return true;
|
|
2369
|
+
return name === GH_COMMAND3 && positional_words(args)[1] === GRAPHQL_ENDPOINT;
|
|
2370
|
+
}
|
|
2371
|
+
function is_unreadable_body(argument) {
|
|
2372
|
+
if (FILE_BODY_FLAGS.has(argument)) return true;
|
|
2373
|
+
return FILE_BODY_VALUE.test(argument) || SUBSTITUTED_BODY.test(argument);
|
|
2374
|
+
}
|
|
2375
|
+
function is_graphql_merge(command, name, args) {
|
|
2376
|
+
if (!is_graphql_call(name, args)) return false;
|
|
2377
|
+
return MERGE_MUTATIONS.test(command) || args.some((argument) => is_unreadable_body(argument));
|
|
2378
|
+
}
|
|
2379
|
+
function is_api_merge(command, segment, call) {
|
|
2380
|
+
if (MERGE_PATH.test(segment) || MERGE_MUTATIONS.test(segment)) return true;
|
|
2381
|
+
return is_graphql_merge(command, call.name, call.args);
|
|
2382
|
+
}
|
|
2383
|
+
function is_pr_merge_segment(command, segment) {
|
|
2384
|
+
const call = git_argv.program_of(segment);
|
|
2385
|
+
if (call === void 0) return false;
|
|
2386
|
+
if (call.name === GH_COMMAND3 && is_pr_merge_cli_call(call.args)) return true;
|
|
2387
|
+
return is_github_api_call(call.name, call.args) && is_api_merge(command, segment, call);
|
|
2388
|
+
}
|
|
2389
|
+
function is_pr_merge(command) {
|
|
2390
|
+
return shell_segments.commands_of(command).some((segment) => is_pr_merge_segment(command, segment));
|
|
2391
|
+
}
|
|
2392
|
+
var PR_MERGE_REASON = "\u26D4 direct PR merge: `gh pr merge`, a `gh api` / `curl` call to `\u2026/pulls/<N>/merge`, a `mergePullRequest` / `enablePullRequestAutoMerge` mutation, or a GraphQL body read from a file merges outside the verified route, and a merge is Tier C. Merge with `pnpm josh followup` if the run authorizes it; otherwise leave it to the user (`operating-rules.md` \u2192 git-rules). This rule fires on every occurrence.";
|
|
2393
|
+
var ROW24 = {
|
|
2394
|
+
id: "pr-merge",
|
|
2395
|
+
is_trigger: bash_triggers.on_bash_command(is_pr_merge),
|
|
2396
|
+
reason: PR_MERGE_REASON,
|
|
2397
|
+
decide: () => true,
|
|
2398
|
+
about: {
|
|
2399
|
+
title: "Direct PR merge",
|
|
2400
|
+
topic: '`operating-rules.md` \u2192 "git-rules"',
|
|
2401
|
+
fires: "`gh pr merge`, a `gh api` / `curl` call to `\u2026/pulls/<N>/merge`, a `mergePullRequest` / `enablePullRequestAutoMerge` mutation, or a GraphQL body read from a file. Every occurrence",
|
|
2402
|
+
quiet: "`pnpm josh followup` and `pnpm josh observations:flush`, which merge in their own process"
|
|
2403
|
+
}
|
|
2404
|
+
};
|
|
2405
|
+
var pr_merge = { PR_MERGE_REASON, ROW: ROW24, is_graphql_call, is_pr_merge };
|
|
2406
|
+
|
|
2407
|
+
// scripts/rules/repo-admin.ts
|
|
2408
|
+
var GH_COMMAND4 = "gh";
|
|
2409
|
+
var DELETE_METHOD2 = "DELETE";
|
|
2410
|
+
var PATCH_METHOD = "PATCH";
|
|
2411
|
+
var REPOSITORY_PREFIX = String.raw`^['"]?(?:https?:\/\/[^/\s'"]+)?\/?repos\/[^/\s'"]+\/[^/\s'"]+`;
|
|
2412
|
+
var ADMIN_PATH = new RegExp(
|
|
2413
|
+
REPOSITORY_PREFIX + String.raw`\/(?:rulesets|branches\/[^\s'"]+\/protection|collaborators|invitations|hooks|keys|` + String.raw`transfer|actions\/permissions|environments|` + String.raw`(?:actions|dependabot|codespaces)\/(?:secrets|variables))(?:[/?'"]|$)`,
|
|
2414
|
+
"u"
|
|
2415
|
+
);
|
|
2416
|
+
var REPOSITORY_BODY = new RegExp(String.raw`${REPOSITORY_PREFIX}\/?['"]?$`, "u");
|
|
2417
|
+
var ADMIN_MUTATIONS = /\b(?:updateRepository|archiveRepository|(?:create|update|delete)(?:BranchProtectionRule|RepositoryRuleset|Environment))\b/u;
|
|
2418
|
+
function is_admin_target(argument, method) {
|
|
2419
|
+
return ADMIN_PATH.test(argument) || method === PATCH_METHOD && REPOSITORY_BODY.test(argument);
|
|
2420
|
+
}
|
|
2421
|
+
function is_admin_graphql_call(command, args) {
|
|
2422
|
+
return pr_merge.is_graphql_call(GH_COMMAND4, args) && ADMIN_MUTATIONS.test(command);
|
|
2423
|
+
}
|
|
2424
|
+
function is_admin_api_call(command, args) {
|
|
2425
|
+
const call = [GH_COMMAND4, ...args].join(" ");
|
|
2426
|
+
if (!gh_api.is_gh_api(call) || !gh_api.is_write(call)) return false;
|
|
2427
|
+
if (is_admin_graphql_call(command, args)) return true;
|
|
2428
|
+
const method = gh_api.method_of(call);
|
|
2429
|
+
if (method === DELETE_METHOD2) return false;
|
|
2430
|
+
return args.some((argument) => is_admin_target(argument, method));
|
|
2431
|
+
}
|
|
2432
|
+
var READ_ACTIONS2 = /* @__PURE__ */ new Set(["list", "ls", "get", "view"]);
|
|
2433
|
+
var ACTION_POSITION = 1;
|
|
2434
|
+
var SUB_ACTION_POSITION = 2;
|
|
2435
|
+
var VERB_POSITIONS = /* @__PURE__ */ new Map([
|
|
2436
|
+
["secret", ACTION_POSITION],
|
|
2437
|
+
["variable", ACTION_POSITION],
|
|
2438
|
+
["repo edit", ACTION_POSITION],
|
|
2439
|
+
["repo deploy-key", SUB_ACTION_POSITION]
|
|
2440
|
+
]);
|
|
2441
|
+
function admin_verb(args) {
|
|
2442
|
+
const words = direct_pr_create.positional_words(args, direct_pr_create.API_VALUE_TAKING_FLAGS);
|
|
2443
|
+
const position = VERB_POSITIONS.get(words[0] ?? "") ?? VERB_POSITIONS.get(words.slice(0, SUB_ACTION_POSITION).join(" "));
|
|
2444
|
+
if (position === void 0) return void 0;
|
|
2445
|
+
return words[position] ?? "";
|
|
2446
|
+
}
|
|
2447
|
+
function is_admin_cli_call(args) {
|
|
2448
|
+
const verb = admin_verb(args);
|
|
2449
|
+
return verb !== void 0 && !READ_ACTIONS2.has(verb);
|
|
2450
|
+
}
|
|
2451
|
+
function is_admin_segment(command, segment) {
|
|
2452
|
+
const args = git_argv.arguments_of(segment, GH_COMMAND4);
|
|
2453
|
+
if (args === void 0) return false;
|
|
2454
|
+
return is_admin_cli_call(args) || is_admin_api_call(command, args);
|
|
2455
|
+
}
|
|
2456
|
+
function is_repo_admin_write(command) {
|
|
2457
|
+
return shell_segments.commands_of(command).some((segment) => is_admin_segment(command, segment));
|
|
2458
|
+
}
|
|
2459
|
+
var REPO_ADMIN_REASON = "\u26D4 repository administration: a write to the rulesets, branch protection, collaborators, invitations, secrets, variables, webhooks, deploy keys or repository settings moves the boundary an unattended run relies on, which is Tier C. Ask the user to run it. It fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md`).";
|
|
2460
|
+
var ROW25 = {
|
|
2461
|
+
id: "repo-admin",
|
|
2462
|
+
is_trigger: bash_triggers.on_bash_command(is_repo_admin_write),
|
|
2463
|
+
reason: REPO_ADMIN_REASON,
|
|
2464
|
+
decide: () => true,
|
|
2465
|
+
about: {
|
|
2466
|
+
title: "Repository administration",
|
|
2467
|
+
topic: "`operating-rules.md`",
|
|
2468
|
+
fires: "a `gh api` POST / PUT / PATCH (path or URL) to the rulesets, branch protection, collaborators, invitations, secrets, variables, hooks, keys, environments, actions permissions or transfer, a `PATCH` of the repository, the matching GraphQL mutations, `gh secret` / `gh variable` writes, `gh repo edit` / `deploy-key` writes. Every occurrence",
|
|
2469
|
+
quiet: "`gh secret list`, `gh variable get`, a `gh api` read \u2014 a `DELETE` is `destructive-command`"
|
|
2470
|
+
}
|
|
2471
|
+
};
|
|
2472
|
+
var repo_admin = { REPO_ADMIN_REASON, ROW: ROW25, is_repo_admin_write };
|
|
2473
|
+
|
|
2474
|
+
// scripts/rules/third-party-write.ts
|
|
2475
|
+
function is_third_party_write_segment(segment, session_owner, resolve_placeholder_owners) {
|
|
2476
|
+
if (!gh_api.is_gh_api(segment) || !gh_api.is_write(segment)) return false;
|
|
2477
|
+
const target = gh_api.repo_target(segment);
|
|
2478
|
+
if (target === void 0) return false;
|
|
2479
|
+
if (!gh_api.is_owner_placeholder(target.owner)) {
|
|
2480
|
+
return repo_party.classify(session_owner, target.owner) === repo_party.THIRD_PARTY;
|
|
2481
|
+
}
|
|
2482
|
+
return resolve_placeholder_owners().some(
|
|
2483
|
+
(owner) => owner === void 0 || repo_party.classify(session_owner, owner) === repo_party.THIRD_PARTY
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
var GH_CONTEXT_CHANGE = /(?:^|\s)(?:cd|pushd)(?:\s|$)|(?:^|\s)(?:export\s+)?GH_REPO=/u;
|
|
2487
|
+
function unreadable_candidates() {
|
|
2488
|
+
return [void 0];
|
|
2489
|
+
}
|
|
2490
|
+
function candidates_at(segments, index, resolve_placeholder_owners) {
|
|
2491
|
+
const has_moved = segments.slice(0, index).some((segment) => GH_CONTEXT_CHANGE.test(segment));
|
|
2492
|
+
return has_moved ? unreadable_candidates : resolve_placeholder_owners;
|
|
2493
|
+
}
|
|
2494
|
+
function is_claimed_by_narrower_row(command) {
|
|
2495
|
+
return pr_merge.is_pr_merge(command) || repo_admin.is_repo_admin_write(command);
|
|
2496
|
+
}
|
|
2497
|
+
var GRAPHQL_MUTATION = /\bmutation\b/u;
|
|
2498
|
+
var EXPANDED_QUERY = /^(?:-[fF])?"?query=(?!')[\s\S]*\$/u;
|
|
2499
|
+
function is_hidden_or_mutation(command, args) {
|
|
2500
|
+
return GRAPHQL_MUTATION.test(command) || args.some((argument) => EXPANDED_QUERY.test(argument));
|
|
2501
|
+
}
|
|
2502
|
+
function graphql_args(segment) {
|
|
2503
|
+
const call = git_argv.program_of(segment);
|
|
2504
|
+
if (call === void 0 || !gh_api.is_gh_api(segment)) return void 0;
|
|
2505
|
+
return pr_merge.is_graphql_call(call.name, call.args) ? call.args : void 0;
|
|
2506
|
+
}
|
|
2507
|
+
function is_graphql_mutation(command, segment) {
|
|
2508
|
+
const args = graphql_args(segment);
|
|
2509
|
+
if (args === void 0) return false;
|
|
2510
|
+
return is_hidden_or_mutation(command, args) && !is_claimed_by_narrower_row(command);
|
|
2511
|
+
}
|
|
2512
|
+
function is_third_party_selector_write(command, segments, index, session_owner) {
|
|
2513
|
+
const selectors = gh_repo_selector.write_selectors(segments, index);
|
|
2514
|
+
const is_third_party = selectors.some((selector) => {
|
|
2515
|
+
const owner = repo_party.selector_owner(selector);
|
|
2516
|
+
return owner === void 0 || repo_party.classify(session_owner, owner) === repo_party.THIRD_PARTY;
|
|
2517
|
+
});
|
|
2518
|
+
return is_third_party && !is_claimed_by_narrower_row(command);
|
|
2519
|
+
}
|
|
2520
|
+
function is_owner_dependent_write(segments, index) {
|
|
2521
|
+
const segment = segments[index] ?? "";
|
|
2522
|
+
if (gh_api.is_gh_api(segment)) return gh_api.is_write(segment);
|
|
2523
|
+
return gh_repo_selector.write_selectors(segments, index).length > 0;
|
|
2524
|
+
}
|
|
2525
|
+
function writes_to_third_party_owner(command, segments, resolve_owner, resolve_placeholder_owners) {
|
|
2526
|
+
if (segments.every((_segment, index) => !is_owner_dependent_write(segments, index))) return false;
|
|
2527
|
+
const session_owner = resolve_owner();
|
|
2528
|
+
return segments.some(
|
|
2529
|
+
(segment, index) => is_third_party_selector_write(command, segments, index, session_owner) || is_third_party_write_segment(
|
|
2530
|
+
segment,
|
|
2531
|
+
session_owner,
|
|
2532
|
+
candidates_at(segments, index, resolve_placeholder_owners)
|
|
2533
|
+
)
|
|
2534
|
+
);
|
|
2535
|
+
}
|
|
2536
|
+
function writes_third_party(command, resolve_owner = repo_party.current_owner, resolve_placeholder_owners = repo_party.placeholder_owners) {
|
|
2537
|
+
const segments = shell_segments.quote_aware_commands_of(
|
|
2538
|
+
shell_segments.joined_continuations(command)
|
|
2539
|
+
);
|
|
2540
|
+
if (segments.some((segment) => is_graphql_mutation(command, segment))) return true;
|
|
2541
|
+
return writes_to_third_party_owner(command, segments, resolve_owner, resolve_placeholder_owners);
|
|
2542
|
+
}
|
|
2543
|
+
var THIRD_PARTY_WRITE_REASON = "\u26D4 third-party write: this `gh` call writes to another owner's repository, or one it cannot read (a GraphQL mutation, an unreadable `-R`) \u2014 Tier C. Check `pnpm josh repo:party <owner/repo>`; first-party, use a REST `repos/<owner>/<repo>` path. Else record it on our Issue and draft it for a person to post. Fires on every occurrence (`upstream-interrupt.md`).";
|
|
2544
|
+
var ROW26 = {
|
|
2545
|
+
id: "third-party-write",
|
|
2546
|
+
is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
|
|
2547
|
+
reason: THIRD_PARTY_WRITE_REASON,
|
|
2548
|
+
decide: () => true,
|
|
2549
|
+
about: {
|
|
2550
|
+
title: "Third-party write",
|
|
2551
|
+
topic: "`upstream-interrupt.md`",
|
|
2552
|
+
fires: "a `gh api` write (field flag or a write method) or a `gh <group> <action>` write selected by `-R` / `GH_REPO` to a repository whose owner is not this session, or a `gh api graphql` call whose line names `mutation` or whose query the shell expands, outside the merge and administration rows. Every occurrence",
|
|
2553
|
+
quiet: "a read, a GraphQL query, or a first-party owner (pinned at session start, `pnpm josh repo:party`)"
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
var third_party_write = {
|
|
2557
|
+
ROW: ROW26,
|
|
2558
|
+
THIRD_PARTY_WRITE_REASON,
|
|
2559
|
+
writes_third_party
|
|
2560
|
+
};
|
|
2561
|
+
|
|
2562
|
+
// scripts/rules/shell-permissions.ts
|
|
2563
|
+
var FLAG_PREFIX6 = "-";
|
|
2564
|
+
var ORIGIN = "origin";
|
|
2565
|
+
var PUSH = "push";
|
|
2566
|
+
var PUSH_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
2567
|
+
"--repo",
|
|
2568
|
+
"--receive-pack",
|
|
2569
|
+
"--exec",
|
|
2570
|
+
"-o",
|
|
2571
|
+
"--push-option"
|
|
2572
|
+
]);
|
|
2573
|
+
var QUOTING = /["'\\]/gu;
|
|
2574
|
+
var PUSH_CONFIG = /^(?:remote\.|url\.|core\.(?:sshCommand|gitProxy))/iu;
|
|
2575
|
+
var CONFIG_ENVIRONMENT = /^GIT_CONFIG(?:_[A-Z0-9_]+)?=/u;
|
|
2576
|
+
function has_push_config(line) {
|
|
2577
|
+
return git_argv.config_arguments_of(line).some((word) => PUSH_CONFIG.test(word.replaceAll(QUOTING, "")));
|
|
2578
|
+
}
|
|
2579
|
+
function has_config_environment(segment, call) {
|
|
2580
|
+
const prefix = git_argv.words_of(segment).slice(0, -(call.args.length + 1));
|
|
2581
|
+
return prefix.some((word) => CONFIG_ENVIRONMENT.test(word.replaceAll(QUOTING, "")));
|
|
2582
|
+
}
|
|
2583
|
+
function has_push_override(segment, call, args) {
|
|
2584
|
+
const line = [call.name, ...call.args].join(" ");
|
|
2585
|
+
return has_config_environment(segment, call) || has_push_config(line) || args.some((word) => PUSH_VALUE_FLAGS.has(word.split("=", 1)[0] ?? ""));
|
|
2586
|
+
}
|
|
2587
|
+
function is_gh_write(call) {
|
|
2588
|
+
if (call.name !== "gh") return false;
|
|
2589
|
+
const line = [call.name, ...call.args].join(" ");
|
|
2590
|
+
if (pr_merge.is_graphql_call(call.name, call.args)) {
|
|
2591
|
+
return [pr_merge.is_pr_merge, third_party_write.writes_third_party].some((test) => test(line));
|
|
2592
|
+
}
|
|
2593
|
+
if (gh_api.is_gh_api(line)) return gh_api.is_write(line);
|
|
2594
|
+
return !/^gh (?:--help|--version|-h)$/u.test(line) && gh_repo_selector.is_write_call(call.args);
|
|
2595
|
+
}
|
|
2596
|
+
function is_foreign_push(segment, call) {
|
|
2597
|
+
const line = [call.name, ...call.args].join(" ");
|
|
2598
|
+
const parsed = git_argv.parse(line);
|
|
2599
|
+
if (parsed?.subcommand !== PUSH) return false;
|
|
2600
|
+
if (has_push_override(segment, call, parsed.args)) return true;
|
|
2601
|
+
const remote = parsed.args.find((word) => !word.startsWith(FLAG_PREFIX6));
|
|
2602
|
+
return remote?.replaceAll(QUOTING, "") !== ORIGIN;
|
|
2603
|
+
}
|
|
2604
|
+
function is_restricted(segment, call) {
|
|
2605
|
+
const plain = {
|
|
2606
|
+
name: call.name,
|
|
2607
|
+
args: call.args.map((word) => interpreter_options.syntax_word(word))
|
|
2608
|
+
};
|
|
2609
|
+
return interpreter_options.is_inline_execution(plain) || is_gh_write(plain) || is_foreign_push(segment, plain);
|
|
2610
|
+
}
|
|
2611
|
+
var shell_permissions = { is_restricted };
|
|
2612
|
+
|
|
2613
|
+
// scripts/rules/egress-guard.ts
|
|
2614
|
+
var GH_COMMAND5 = "gh";
|
|
2615
|
+
var ENV_COMMAND = "env";
|
|
2616
|
+
var FLAG_PREFIX7 = "-";
|
|
2617
|
+
var NEXT4 = 1;
|
|
2618
|
+
var NETWORK_PROGRAMS = /* @__PURE__ */ new Set([
|
|
2619
|
+
"curl",
|
|
2620
|
+
"ncat",
|
|
2621
|
+
"nc",
|
|
2622
|
+
"scp",
|
|
2623
|
+
"sftp",
|
|
2624
|
+
"socat",
|
|
2625
|
+
"ssh",
|
|
2626
|
+
"telnet",
|
|
2627
|
+
"wget"
|
|
2628
|
+
]);
|
|
2629
|
+
var LOCAL_URL = /^['"]?(?:https?:\/\/)?(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?(?:[/?#]\S*)?['"]?$/u;
|
|
2630
|
+
var LOCAL_FETCHERS = /* @__PURE__ */ new Map([
|
|
2631
|
+
[
|
|
2632
|
+
"curl",
|
|
2633
|
+
{
|
|
2634
|
+
value_flags: /* @__PURE__ */ new Set([
|
|
2635
|
+
"-H",
|
|
2636
|
+
"--header",
|
|
2637
|
+
"-X",
|
|
2638
|
+
"--request",
|
|
2639
|
+
"-m",
|
|
2640
|
+
"--max-time",
|
|
2641
|
+
"-o",
|
|
2642
|
+
"--output",
|
|
2643
|
+
"-w",
|
|
2644
|
+
"--write-out"
|
|
2645
|
+
]),
|
|
2646
|
+
bare_prefix: /^[fiIkLsSv]*/u
|
|
2647
|
+
}
|
|
2648
|
+
],
|
|
2649
|
+
[
|
|
2650
|
+
"wget",
|
|
2651
|
+
{
|
|
2652
|
+
value_flags: /* @__PURE__ */ new Set([
|
|
2653
|
+
"--header",
|
|
2654
|
+
"-O",
|
|
2655
|
+
"--output-document",
|
|
2656
|
+
"-o",
|
|
2657
|
+
"--output-file",
|
|
2658
|
+
"-T",
|
|
2659
|
+
"--timeout"
|
|
2660
|
+
]),
|
|
2661
|
+
bare_prefix: /^[qSv]*/u
|
|
2662
|
+
}
|
|
2663
|
+
]
|
|
2664
|
+
]);
|
|
2665
|
+
var GLUED_LONG = /^(?<flag>--[^=]+)=/u;
|
|
2666
|
+
var SHORT_CLUSTER = /^-(?<letters>[^-].+)$/u;
|
|
2667
|
+
var REDIRECT_WORD = /^[\d&]?[<>]/u;
|
|
2668
|
+
var RSYNC_COMMAND = "rsync";
|
|
2669
|
+
var REMOTE_OPERAND = /^['"]?(?:[\w.-]+@)?[\w.-]+:|^['"]?rsync:\/\//u;
|
|
2670
|
+
var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "api.github.com"]);
|
|
2671
|
+
var HOSTNAME_FLAG = "--hostname";
|
|
2672
|
+
var HOSTNAME_VALUE = /^--hostname=(?<host>.*)$/u;
|
|
2673
|
+
var AUTH_GROUP = "auth";
|
|
2674
|
+
var TOKEN_ACTION = "token";
|
|
2675
|
+
var STATUS_ACTION = "status";
|
|
2676
|
+
var HOST_ASSIGNMENT = "GH_HOST=";
|
|
2677
|
+
var URL_SCHEME = /^['"]?https?:\/\//u;
|
|
2678
|
+
var QUOTES4 = /["']/gu;
|
|
2679
|
+
var SHOW_TOKEN_FLAGS = /* @__PURE__ */ new Set(["--show-token", "-t"]);
|
|
2680
|
+
var SECRET_NAME = /TOKEN|SECRET|KEY/u;
|
|
2681
|
+
var SECRET_REFERENCE = /\$\{?\w*(?:TOKEN|SECRET|KEY)/u;
|
|
2682
|
+
function is_flag_word(word) {
|
|
2683
|
+
return word.startsWith(FLAG_PREFIX7) || REDIRECT_WORD.test(word);
|
|
2684
|
+
}
|
|
2685
|
+
function is_destination_word(args, index, value_flags) {
|
|
2686
|
+
if (is_flag_word(args[index] ?? "")) return false;
|
|
2687
|
+
const previous = args[index - NEXT4] ?? "";
|
|
2688
|
+
return !value_flags.has(previous) && !REDIRECT_WORD.test(previous);
|
|
2689
|
+
}
|
|
2690
|
+
function host_of(word) {
|
|
2691
|
+
if (!URL_SCHEME.test(word)) return void 0;
|
|
2692
|
+
const url = word.replaceAll(QUOTES4, "");
|
|
2693
|
+
return URL.canParse(url) ? new URL(url).hostname : "";
|
|
2694
|
+
}
|
|
2695
|
+
function is_allowed_destination(word) {
|
|
2696
|
+
return LOCAL_URL.test(word) || GITHUB_HOSTS.has(host_of(word) ?? "");
|
|
2697
|
+
}
|
|
2698
|
+
function is_known_cluster(letters, fetcher) {
|
|
2699
|
+
const stop = fetcher.bare_prefix.exec(letters)?.[0].length ?? 0;
|
|
2700
|
+
if (stop === letters.length) return true;
|
|
2701
|
+
return stop < letters.length - 1 && fetcher.value_flags.has(FLAG_PREFIX7 + letters.charAt(stop));
|
|
2702
|
+
}
|
|
2703
|
+
function group_of(pattern, word, name) {
|
|
2704
|
+
return pattern.exec(word)?.groups?.[name];
|
|
2705
|
+
}
|
|
2706
|
+
function is_known_flag(word, fetcher) {
|
|
2707
|
+
const long = group_of(GLUED_LONG, word, "flag");
|
|
2708
|
+
if (long !== void 0) return fetcher.value_flags.has(long);
|
|
2709
|
+
const letters = group_of(SHORT_CLUSTER, word, "letters");
|
|
2710
|
+
return letters === void 0 || is_known_cluster(letters, fetcher);
|
|
2711
|
+
}
|
|
2712
|
+
function has_known_flags(args, fetcher) {
|
|
2713
|
+
return args.every((word) => !word.startsWith(FLAG_PREFIX7) || is_known_flag(word, fetcher));
|
|
2714
|
+
}
|
|
2715
|
+
function is_local_fetch(call) {
|
|
2716
|
+
const fetcher = LOCAL_FETCHERS.get(call.name);
|
|
2717
|
+
if (fetcher === void 0 || !has_known_flags(call.args, fetcher)) return false;
|
|
2718
|
+
const destinations = call.args.filter(
|
|
2719
|
+
(_word, index) => is_destination_word(call.args, index, fetcher.value_flags)
|
|
2720
|
+
);
|
|
2721
|
+
return destinations.length > 0 && destinations.every((word) => is_allowed_destination(word));
|
|
2722
|
+
}
|
|
2723
|
+
function is_network_send(_segment, call) {
|
|
2724
|
+
if (call.name === RSYNC_COMMAND) {
|
|
2725
|
+
return call.args.some((word) => !word.startsWith(FLAG_PREFIX7) && REMOTE_OPERAND.test(word));
|
|
2726
|
+
}
|
|
2727
|
+
return NETWORK_PROGRAMS.has(call.name) && !is_local_fetch(call);
|
|
2728
|
+
}
|
|
2729
|
+
function flagged_hosts(args) {
|
|
2730
|
+
return args.map(
|
|
2731
|
+
(word, index) => word === HOSTNAME_FLAG ? args[index + NEXT4] : HOSTNAME_VALUE.exec(word)?.groups?.["host"]
|
|
2732
|
+
).filter((host) => host !== void 0);
|
|
2733
|
+
}
|
|
2734
|
+
function named_hosts(segment, args) {
|
|
2735
|
+
const assigned = git_argv.words_of(segment).filter((word) => word.startsWith(HOST_ASSIGNMENT)).map((word) => word.slice(HOST_ASSIGNMENT.length));
|
|
2736
|
+
const urls = args.map((word) => host_of(word)).filter((host) => host !== void 0);
|
|
2737
|
+
return [...flagged_hosts(args), ...assigned, ...urls].map((host) => host.replaceAll(QUOTES4, ""));
|
|
2738
|
+
}
|
|
2739
|
+
function is_auth_print(args) {
|
|
2740
|
+
const [group, action] = direct_pr_create.positional_words(
|
|
2741
|
+
args,
|
|
2742
|
+
direct_pr_create.API_VALUE_TAKING_FLAGS
|
|
2743
|
+
);
|
|
2744
|
+
if (group !== AUTH_GROUP) return false;
|
|
2745
|
+
return action === TOKEN_ACTION || action === STATUS_ACTION && args.some((word) => SHOW_TOKEN_FLAGS.has(word));
|
|
2746
|
+
}
|
|
2747
|
+
function is_gh_credential(segment, call) {
|
|
2748
|
+
if (call.name !== GH_COMMAND5) return false;
|
|
2749
|
+
if (is_auth_print(call.args)) return true;
|
|
2750
|
+
return named_hosts(segment, call.args).some((host) => !GITHUB_HOSTS.has(host));
|
|
2751
|
+
}
|
|
2752
|
+
function names_secret(args) {
|
|
2753
|
+
return args.some((word) => SECRET_REFERENCE.test(word));
|
|
2754
|
+
}
|
|
2755
|
+
function is_printenv_dump(args) {
|
|
2756
|
+
return args.length === 0 || args.some((word) => SECRET_NAME.test(word));
|
|
2757
|
+
}
|
|
2758
|
+
function is_bare(args) {
|
|
2759
|
+
return args.length === 0;
|
|
2760
|
+
}
|
|
2761
|
+
function is_export_listing(args) {
|
|
2762
|
+
return args.length === 0 || args.includes("-p");
|
|
2763
|
+
}
|
|
2764
|
+
var DUMP_CHECKS = /* @__PURE__ */ new Map([
|
|
2765
|
+
["printenv", is_printenv_dump],
|
|
2766
|
+
["set", is_bare],
|
|
2767
|
+
["export", is_export_listing],
|
|
2768
|
+
["echo", names_secret],
|
|
2769
|
+
["printf", names_secret]
|
|
2770
|
+
]);
|
|
2771
|
+
function is_environment_dump(_segment, call) {
|
|
2772
|
+
return DUMP_CHECKS.get(call.name)?.(call.args) ?? false;
|
|
2773
|
+
}
|
|
2774
|
+
var PROGRAM_CHECKS = [
|
|
2775
|
+
is_network_send,
|
|
2776
|
+
shell_permissions.is_restricted,
|
|
2777
|
+
is_gh_credential,
|
|
2778
|
+
is_environment_dump,
|
|
2779
|
+
keychain_read.is_keychain_read
|
|
2780
|
+
];
|
|
2781
|
+
function is_egress_segment(segment) {
|
|
2782
|
+
const call = launched_call.resolved_call(segment);
|
|
2783
|
+
if (call === void 0) {
|
|
2784
|
+
return git_argv.words_of(segment).some((word) => launched_call.plain_name(word) === ENV_COMMAND);
|
|
2785
|
+
}
|
|
2786
|
+
return PROGRAM_CHECKS.some((check) => check(segment, call));
|
|
2787
|
+
}
|
|
2788
|
+
function is_egress(command) {
|
|
2789
|
+
return nested_commands.commands_of(command).some((segment) => is_egress_segment(segment));
|
|
2790
|
+
}
|
|
2791
|
+
var EGRESS_REASON = '\u26D4 egress: no external network send, inline interpreter execution, direct `gh` write, push without explicit `origin`, or credential/environment print. Use named scripts and `pnpm josh` write routes; otherwise ask the user to run it (`SECURITY.md` \u2192 "Secrets"). This rule fires on every occurrence.';
|
|
2792
|
+
var ROW27 = {
|
|
2793
|
+
id: "egress",
|
|
2794
|
+
is_trigger: bash_triggers.on_bash_command(is_egress),
|
|
2795
|
+
reason: EGRESS_REASON,
|
|
2796
|
+
decide: () => true,
|
|
2797
|
+
about: {
|
|
2798
|
+
title: "Network egress or credential print",
|
|
2799
|
+
topic: '`SECURITY.md` \u2192 "Secrets"',
|
|
2800
|
+
fires: "network sends, inline interpreter execution, direct `gh` writes, `git push` without explicit `origin`, credential, keychain or environment prints \u2014 in each shell segment and nested command. Every occurrence",
|
|
2801
|
+
quiet: "ordinary reads, `gh` reads against github.com, `git fetch`, `git push origin`, named scripts and `pnpm josh` routes. A plain `curl` / `wget` is still refused by the deny list; the hook allows local or GitHub destinations with known flags"
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
var egress_guard = { EGRESS_REASON, ROW: ROW27, is_egress };
|
|
2805
|
+
|
|
2806
|
+
// scripts/rules/worktree-guard.ts
|
|
2807
|
+
var CHECKOUT_SUBCOMMAND = "checkout";
|
|
2808
|
+
var RESTORE_SUBCOMMAND = "restore";
|
|
2809
|
+
var STASH_SUBCOMMAND = "stash";
|
|
2810
|
+
var PATHSPEC_SEPARATOR = "--";
|
|
2811
|
+
function is_worktree_checkout(args) {
|
|
2812
|
+
return args.includes(PATHSPEC_SEPARATOR);
|
|
2813
|
+
}
|
|
2814
|
+
var STAGED_LONG = "--staged";
|
|
2815
|
+
var STAGED_LETTER = "S";
|
|
2816
|
+
var WORKTREE_LONG = "--worktree";
|
|
2817
|
+
var WORKTREE_LETTER = "W";
|
|
2818
|
+
function touches_worktree(args) {
|
|
2819
|
+
const is_staged = args.some(
|
|
2820
|
+
(argument) => argument === STAGED_LONG || git_argv.short_cluster_has(argument, STAGED_LETTER)
|
|
2821
|
+
);
|
|
2822
|
+
const is_worktree = args.some(
|
|
2823
|
+
(argument) => argument === WORKTREE_LONG || git_argv.short_cluster_has(argument, WORKTREE_LETTER)
|
|
2824
|
+
);
|
|
2825
|
+
return !is_staged || is_worktree;
|
|
2826
|
+
}
|
|
2827
|
+
var PUSH_STASH = "push";
|
|
2828
|
+
var MESSAGE_LONG = "--message";
|
|
2829
|
+
var MESSAGE_SHORT_LETTER = "m";
|
|
2830
|
+
var READ_ONLY_STASH = /* @__PURE__ */ new Set(["list", "show"]);
|
|
2831
|
+
function has_message(args) {
|
|
2832
|
+
return args.some(
|
|
2833
|
+
(argument) => argument === MESSAGE_LONG || argument.startsWith(`${MESSAGE_LONG}=`) || git_argv.short_cluster_has(argument, MESSAGE_SHORT_LETTER)
|
|
2834
|
+
);
|
|
2835
|
+
}
|
|
2836
|
+
function is_unauthorized_stash(args) {
|
|
2837
|
+
const [stash_subcommand, ...rest] = args;
|
|
2838
|
+
if (stash_subcommand === void 0) return true;
|
|
2839
|
+
if (READ_ONLY_STASH.has(stash_subcommand)) return false;
|
|
2840
|
+
if (stash_subcommand === PUSH_STASH) return !has_message(rest);
|
|
2841
|
+
return true;
|
|
2842
|
+
}
|
|
2843
|
+
var CLEAN_SUBCOMMAND = "clean";
|
|
2844
|
+
var FORCE_LONG2 = "--force";
|
|
2845
|
+
var FORCE_LETTER2 = "f";
|
|
2846
|
+
function is_forced_clean(args) {
|
|
2847
|
+
return args.some(
|
|
2848
|
+
(argument) => argument === FORCE_LONG2 || git_argv.short_cluster_has(argument, FORCE_LETTER2)
|
|
2849
|
+
);
|
|
2850
|
+
}
|
|
2851
|
+
var JUDGED_SUBCOMMANDS = /* @__PURE__ */ new Map([
|
|
2852
|
+
[CHECKOUT_SUBCOMMAND, is_worktree_checkout],
|
|
2853
|
+
[RESTORE_SUBCOMMAND, touches_worktree],
|
|
2854
|
+
[CLEAN_SUBCOMMAND, is_forced_clean],
|
|
2855
|
+
[STASH_SUBCOMMAND, is_unauthorized_stash]
|
|
2856
|
+
]);
|
|
2857
|
+
function is_unauthorized_segment(segment) {
|
|
2858
|
+
const call = git_argv.parse(segment);
|
|
2859
|
+
if (call === void 0) return false;
|
|
2860
|
+
return JUDGED_SUBCOMMANDS.get(call.subcommand ?? "")?.(call.args) === true;
|
|
2861
|
+
}
|
|
2862
|
+
function is_unauthorized_worktree_change(command) {
|
|
2863
|
+
return shell_segments.commands_of(command).some((segment) => is_unauthorized_segment(segment));
|
|
2864
|
+
}
|
|
2865
|
+
var SINGLE_FILE_RESTORE = "git diff -- <path> | git apply -R";
|
|
2866
|
+
var WORKTREE_MUTATION_REASON = `\u26D4 unauthorized working-tree change: undo only this run's edit: \`${SINGLE_FILE_RESTORE}\`. A documented flow stashes with \`pnpm josh stash:push "<message>"\`; a handed-over stash is \`pnpm josh stash:pop "<message>"\`; record the substitution on the Issue (Tier A) unless the plan says why the owner keeps the stash: then stop and confirm. It fires on every occurrence (\`operating-rules.md\`).`;
|
|
2867
|
+
var FORMER_OPENING = "\u26D4 unauthorized working-tree change: `git checkout --`, `git restore`";
|
|
2868
|
+
var ROW28 = {
|
|
2869
|
+
id: "worktree-mutation",
|
|
2870
|
+
is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
|
|
2871
|
+
reason: WORKTREE_MUTATION_REASON,
|
|
2872
|
+
former_reasons: [FORMER_OPENING],
|
|
2873
|
+
decide: () => true,
|
|
2874
|
+
about: {
|
|
2875
|
+
title: "Unauthorized work-tree mutation",
|
|
2876
|
+
topic: "`operating-rules.md`",
|
|
2877
|
+
fires: "`git checkout -- <path>` / `git restore <path>` / forced `git clean -f` / an unauthorized `git stash` (bare, push without a message, positional pop). Every occurrence",
|
|
2878
|
+
quiet: `\`git stash push -m\`, \`git stash list\`, \`pnpm josh git\`, \`pnpm josh stash:push\`, \`pnpm josh stash:pop\`, \`${SINGLE_FILE_RESTORE}\``
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
var worktree_guard = {
|
|
2882
|
+
ROW: ROW28,
|
|
2883
|
+
SINGLE_FILE_RESTORE,
|
|
2884
|
+
WORKTREE_MUTATION_REASON,
|
|
2885
|
+
is_unauthorized_worktree_change,
|
|
2886
|
+
touches_worktree
|
|
2887
|
+
};
|
|
2888
|
+
|
|
2889
|
+
// scripts/rules/index-guard.ts
|
|
2890
|
+
var INDEX_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
2891
|
+
"add",
|
|
2892
|
+
"commit",
|
|
2893
|
+
"mv",
|
|
2894
|
+
"reset",
|
|
2895
|
+
"rm",
|
|
2896
|
+
"stage"
|
|
2897
|
+
]);
|
|
2898
|
+
var RESTORE_SUBCOMMAND2 = "restore";
|
|
2899
|
+
function is_index_restore(args) {
|
|
2900
|
+
return !worktree_guard.touches_worktree(args);
|
|
2901
|
+
}
|
|
2902
|
+
function is_index_segment(segment) {
|
|
2903
|
+
const call = git_argv.parse(segment);
|
|
2904
|
+
if (call === void 0) return false;
|
|
2905
|
+
if (INDEX_SUBCOMMANDS.has(call.subcommand ?? "")) return true;
|
|
2906
|
+
return call.subcommand === RESTORE_SUBCOMMAND2 && is_index_restore(call.args);
|
|
2907
|
+
}
|
|
2908
|
+
function is_index_mutation(command) {
|
|
2909
|
+
return shell_segments.commands_of(command).some((segment) => is_index_segment(segment));
|
|
2910
|
+
}
|
|
2911
|
+
var INDEX_MUTATION_REASON = "\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user's staged snapshot. Commit with `pnpm josh git -y`; any other staging is the user's, on explicit instruction. It fires on every occurrence (`prompts/collaboration-workflow/operating-rules.md` \u2192 \"no-self-staging\").";
|
|
2912
|
+
var ROW29 = {
|
|
2913
|
+
id: "index-mutation",
|
|
2914
|
+
is_trigger: bash_triggers.on_bash_command(is_index_mutation),
|
|
2915
|
+
reason: INDEX_MUTATION_REASON,
|
|
2916
|
+
decide: () => true,
|
|
2917
|
+
about: {
|
|
2918
|
+
title: "Index mutation",
|
|
2919
|
+
topic: "`operating-rules.md`",
|
|
2920
|
+
fires: "`git add` / `commit` / `reset` / `rm` / `mv` / `restore --staged` in any spelling. Every occurrence",
|
|
2921
|
+
quiet: "`pnpm josh git` \u2014 the approved commit flow"
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
var index_guard = { INDEX_MUTATION_REASON, ROW: ROW29, is_index_mutation };
|
|
2925
|
+
|
|
2926
|
+
// scripts/rules/protected-files.ts
|
|
2927
|
+
import path5 from "node:path";
|
|
2928
|
+
|
|
2929
|
+
// scripts/rules/secret-paths.ts
|
|
2930
|
+
import os2 from "node:os";
|
|
2931
|
+
import path4 from "node:path";
|
|
2932
|
+
var TOKEN_QUOTES = /["']/gu;
|
|
2933
|
+
var BRACED_VARIABLE = /\$\{(\w+)\}/gu;
|
|
2934
|
+
var BACKSLASH = /\\/gu;
|
|
2935
|
+
var SECRET_FILES = /* @__PURE__ */ new Set([".env", ".dev.vars"]);
|
|
2936
|
+
var ENVIRONMENT_VARIANT_PREFIX = ".env.";
|
|
2937
|
+
var TRACKED_VARIANTS = /* @__PURE__ */ new Set([".env.example", ".env.test"]);
|
|
2938
|
+
var SECRET_SUFFIXES = [
|
|
2939
|
+
"gh/hosts.yml",
|
|
2940
|
+
".codex/auth.json",
|
|
2941
|
+
"reviewer-home/auth.json",
|
|
2942
|
+
"$CODEX_HOME/auth.json"
|
|
2943
|
+
];
|
|
2944
|
+
var HOME_PREFIXES = ["~/", "$HOME/"];
|
|
2945
|
+
var HOME_SECRET_FILES = /* @__PURE__ */ new Set([".netrc", ".curlrc", ".npmrc"]);
|
|
2946
|
+
var HOME_SECRET_DIRECTORIES = [".ssh", ".config/gh"];
|
|
2947
|
+
function is_environment_file(token) {
|
|
2948
|
+
const name = path4.basename(token);
|
|
2949
|
+
if (TRACKED_VARIANTS.has(name)) return false;
|
|
2950
|
+
return SECRET_FILES.has(name) || name.startsWith(ENVIRONMENT_VARIANT_PREFIX);
|
|
2951
|
+
}
|
|
2952
|
+
function is_under(relative, directory) {
|
|
2953
|
+
return relative === directory || relative.startsWith(`${directory}/`);
|
|
2954
|
+
}
|
|
2955
|
+
function ends_with_path(token, suffix) {
|
|
2956
|
+
return token === suffix || token.endsWith(`/${suffix}`);
|
|
2957
|
+
}
|
|
2958
|
+
function home_relative(token) {
|
|
2959
|
+
const prefix = [...HOME_PREFIXES, `${os2.homedir()}/`].find((home) => token.startsWith(home));
|
|
2960
|
+
return prefix === void 0 ? void 0 : token.slice(prefix.length);
|
|
2961
|
+
}
|
|
2962
|
+
function is_home_secret(token) {
|
|
2963
|
+
const relative = home_relative(token);
|
|
2964
|
+
if (relative === void 0) return false;
|
|
2965
|
+
return HOME_SECRET_FILES.has(relative) || HOME_SECRET_DIRECTORIES.some((directory) => is_under(relative, directory));
|
|
2966
|
+
}
|
|
2967
|
+
function is_secret_token(token) {
|
|
2968
|
+
return is_environment_file(token) || SECRET_SUFFIXES.some((suffix) => ends_with_path(token, suffix)) || is_home_secret(token);
|
|
2969
|
+
}
|
|
2970
|
+
function spellings_of(word) {
|
|
2971
|
+
const token = word.replaceAll(TOKEN_QUOTES, "").replaceAll(BRACED_VARIABLE, "$$$1");
|
|
2972
|
+
return [token.replaceAll(BACKSLASH, ""), token.replaceAll(BACKSLASH, "/")].map(
|
|
2973
|
+
(spelling) => path4.posix.normalize(spelling)
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
function is_secret_path(word) {
|
|
2977
|
+
return spellings_of(word).some((token) => is_secret_token(token));
|
|
2978
|
+
}
|
|
2979
|
+
var secret_paths = { is_secret_path };
|
|
2980
|
+
|
|
2981
|
+
// scripts/rules/shell-redirects.ts
|
|
2982
|
+
var READ_MARK = "read";
|
|
2983
|
+
var WRITE_MARK = "write";
|
|
2984
|
+
var READ_OPERATOR = "<";
|
|
2985
|
+
var REDIRECT = />\||[<>]/gu;
|
|
2986
|
+
var PIECE_END = "\0";
|
|
2987
|
+
var NEXT5 = 1;
|
|
2988
|
+
var ANY_MARK = /* @__PURE__ */ new Set([READ_MARK, WRITE_MARK]);
|
|
2989
|
+
var { SHELLS: SHELLS2 } = nested_commands;
|
|
2990
|
+
function redirect_marks_of(segment, program) {
|
|
2991
|
+
if (program !== void 0 && SHELLS2.has(program)) return segment;
|
|
2992
|
+
return time_shell.blanked(segment) ?? segment;
|
|
2993
|
+
}
|
|
2994
|
+
function mark_of(operator) {
|
|
2995
|
+
return operator === READ_OPERATOR ? READ_MARK : WRITE_MARK;
|
|
2996
|
+
}
|
|
2997
|
+
function marked_pieces(word, marks) {
|
|
2998
|
+
const pieced = word.text.replaceAll(
|
|
2999
|
+
REDIRECT,
|
|
3000
|
+
(operator, offset) => marks.startsWith(operator, word.begin + offset) ? `${PIECE_END}${mark_of(operator)}${PIECE_END}` : operator
|
|
3001
|
+
);
|
|
3002
|
+
return pieced.split(PIECE_END).filter((piece) => piece !== "");
|
|
3003
|
+
}
|
|
3004
|
+
function marked_words(segment, program) {
|
|
3005
|
+
const marks = redirect_marks_of(segment, program);
|
|
3006
|
+
return shell_tokens.words_of(segment).flatMap((word) => marked_pieces(word, marks));
|
|
3007
|
+
}
|
|
3008
|
+
function targets_of(segment, program) {
|
|
3009
|
+
const words = marked_words(segment, program);
|
|
3010
|
+
return words.map((word, index) => ANY_MARK.has(word) ? words[index + NEXT5] : void 0).filter((target) => target !== void 0 && !ANY_MARK.has(target));
|
|
3011
|
+
}
|
|
3012
|
+
var shell_redirects = { targets_of };
|
|
3013
|
+
|
|
3014
|
+
// scripts/rules/protected-files.ts
|
|
3015
|
+
var READ_TOOL = "Read";
|
|
3016
|
+
var GREP_TOOL = "Grep";
|
|
3017
|
+
var FILE_TOOLS = /* @__PURE__ */ new Set([READ_TOOL, GREP_TOOL]);
|
|
3018
|
+
var { is_edit_tool: is_edit_tool2 } = edit_tool;
|
|
3019
|
+
var { is_secret_path: is_secret_path2 } = secret_paths;
|
|
3020
|
+
var SETTINGS_FILE = path5.join(".claude", "settings.json");
|
|
3021
|
+
var SHELL_PROGRAMS = /* @__PURE__ */ new Set([
|
|
3022
|
+
"cp",
|
|
3023
|
+
"install",
|
|
3024
|
+
"ln",
|
|
3025
|
+
"mv",
|
|
3026
|
+
"tee",
|
|
3027
|
+
".",
|
|
3028
|
+
"awk",
|
|
3029
|
+
"base64",
|
|
3030
|
+
"bat",
|
|
3031
|
+
"cat",
|
|
3032
|
+
"cmp",
|
|
3033
|
+
"column",
|
|
3034
|
+
"comm",
|
|
3035
|
+
"cut",
|
|
3036
|
+
"diff",
|
|
3037
|
+
"fold",
|
|
3038
|
+
"grep",
|
|
3039
|
+
"head",
|
|
3040
|
+
"hexdump",
|
|
3041
|
+
"jq",
|
|
3042
|
+
"less",
|
|
3043
|
+
"more",
|
|
3044
|
+
"nl",
|
|
3045
|
+
"od",
|
|
3046
|
+
"paste",
|
|
3047
|
+
"perl",
|
|
3048
|
+
"python",
|
|
3049
|
+
"python3",
|
|
3050
|
+
"rev",
|
|
3051
|
+
"rg",
|
|
3052
|
+
"ruby",
|
|
3053
|
+
"sed",
|
|
3054
|
+
"sort",
|
|
3055
|
+
"source",
|
|
3056
|
+
"strings",
|
|
3057
|
+
"tac",
|
|
3058
|
+
"tail",
|
|
3059
|
+
"uniq",
|
|
3060
|
+
"xxd",
|
|
3061
|
+
"yq"
|
|
3062
|
+
]);
|
|
3063
|
+
function file_path_of(call) {
|
|
3064
|
+
if (!json_value.is_record(call.input)) return "";
|
|
3065
|
+
const value = call.input[call.name === GREP_TOOL ? "path" : "file_path"];
|
|
3066
|
+
return typeof value === "string" ? value : "";
|
|
3067
|
+
}
|
|
3068
|
+
function is_secret_shell_segment(segment) {
|
|
3069
|
+
const call = git_argv.program_of(segment);
|
|
3070
|
+
const is_redirected = shell_redirects.targets_of(segment, call?.name).some((target) => is_secret_path2(target));
|
|
3071
|
+
if (is_redirected || call === void 0) return is_redirected;
|
|
3072
|
+
return SHELL_PROGRAMS.has(call.name) && call.args.some((word) => is_secret_path2(word));
|
|
3073
|
+
}
|
|
3074
|
+
function is_secret_shell_access(command) {
|
|
3075
|
+
return nested_commands.commands_of(command).some((segment) => is_secret_shell_segment(segment));
|
|
3076
|
+
}
|
|
3077
|
+
function is_secret_access(call) {
|
|
3078
|
+
if (FILE_TOOLS.has(call.name) || is_edit_tool2(call.name)) {
|
|
3079
|
+
return is_secret_path2(file_path_of(call));
|
|
3080
|
+
}
|
|
3081
|
+
return bash_triggers.on_bash_command(is_secret_shell_access)(call);
|
|
3082
|
+
}
|
|
3083
|
+
function is_consumer_settings_edit(call, is_kit_consumer) {
|
|
3084
|
+
if (!is_edit_tool2(call.name)) return false;
|
|
3085
|
+
const file_path = path5.resolve(file_path_of(call));
|
|
3086
|
+
if (!file_path.endsWith(`${path5.sep}${SETTINGS_FILE}`)) return false;
|
|
3087
|
+
return is_kit_consumer(path5.dirname(path5.dirname(file_path)));
|
|
3088
|
+
}
|
|
3089
|
+
function is_protected_file_call(call, is_kit_consumer = doctor_consumer.is_kit_consumer) {
|
|
3090
|
+
return is_secret_access(call) || is_consumer_settings_edit(call, is_kit_consumer);
|
|
3091
|
+
}
|
|
3092
|
+
var PROTECTED_FILE_REASON = "\u26D4 protected file: `.env`, a `.env.*` variant, `.dev.vars` and the credential files (gh `hosts.yml`, Codex's `auth.json`, `~/.netrc`, `~/.curlrc`, `~/.npmrc`, `~/.ssh/**`) hold secrets and are read and written only by the person, and a consumer `.claude/settings.json` is owned by `josh sync` (a change belongs upstream in kit). Leave these files alone, or ask the user. It fires on every occurrence.";
|
|
3093
|
+
var ROW30 = {
|
|
3094
|
+
id: "protected-file",
|
|
3095
|
+
is_trigger: is_protected_file_call,
|
|
3096
|
+
reason: PROTECTED_FILE_REASON,
|
|
3097
|
+
decide: () => true,
|
|
3098
|
+
// The opening the reason carried before it was cut to the action and the next command.
|
|
3099
|
+
former_reasons: ["\u26D4 protected file: `.env` holds the Telegram bot token"],
|
|
3100
|
+
about: {
|
|
3101
|
+
title: "Protected file",
|
|
3102
|
+
topic: "`operating-rules.md`",
|
|
3103
|
+
fires: "a `Read`, `Grep`, `Edit` or `Write` of `.env` or a credential file such as gh `hosts.yml`, Codex `auth.json`, `~/.netrc` or `~/.ssh/**` (or a shell read or write of one), or an `Edit` / `Write` of `.claude/settings.json` outside kit. Every occurrence",
|
|
3104
|
+
quiet: "kit itself, or the user's `~/.claude/settings.json`"
|
|
3105
|
+
}
|
|
3106
|
+
};
|
|
3107
|
+
var protected_files = { ROW: ROW30, is_protected_file_call };
|
|
3108
|
+
|
|
3109
|
+
// scripts/rules/permission-guards.ts
|
|
3110
|
+
var ROWS2 = [
|
|
3111
|
+
index_guard.ROW,
|
|
3112
|
+
destructive_command.ROW,
|
|
3113
|
+
delivered_rules_guard.call_only(protected_files.ROW),
|
|
3114
|
+
direct_pr_create.ROW,
|
|
3115
|
+
pr_merge.ROW,
|
|
3116
|
+
repo_admin.ROW,
|
|
3117
|
+
egress_guard.ROW
|
|
3118
|
+
];
|
|
3119
|
+
var permission_guards = { ROWS: ROWS2 };
|
|
3120
|
+
|
|
3121
|
+
// scripts/rules/piped-verification.ts
|
|
3122
|
+
var VERIFICATION_COMMANDS = /* @__PURE__ */ new Set([
|
|
3123
|
+
"check",
|
|
3124
|
+
"check:related",
|
|
3125
|
+
"cspell",
|
|
3126
|
+
"cspell:dot",
|
|
3127
|
+
"eval",
|
|
3128
|
+
"gate",
|
|
3129
|
+
"lint",
|
|
3130
|
+
"lint:eslint",
|
|
3131
|
+
"lint:prettier",
|
|
3132
|
+
"lint:related",
|
|
3133
|
+
"overrides",
|
|
3134
|
+
"ranges",
|
|
3135
|
+
"test",
|
|
3136
|
+
"test:e2e",
|
|
3137
|
+
"test:related",
|
|
3138
|
+
"test:unit"
|
|
3139
|
+
]);
|
|
3140
|
+
function is_verification_command(segment) {
|
|
3141
|
+
return shell_segments.is_josh_command(segment, VERIFICATION_COMMANDS);
|
|
3142
|
+
}
|
|
3143
|
+
function is_masked_verification(command) {
|
|
3144
|
+
return time_shell.discarded_commands(command).some((segment) => is_verification_command(segment));
|
|
3145
|
+
}
|
|
3146
|
+
var PIPED_VERIFICATION_REASON = "\u26D4 piped verification: a pipeline exits with its last command's status, so `pnpm josh gate | tail` reports success on a gate that failed. Reissue this call with no pipe (josh prints its verdict last), or redirect it to a file and read ranges from that file, or prefix `set -o pipefail`; read the verdict line, never the exit code alone (`prompts/collaboration-workflow/output-bounds.md`).";
|
|
3147
|
+
function pipelines_of(command) {
|
|
3148
|
+
return shell_segments.segments_of(time_shell.unquoted(command)).map((segment) => segment.split("|").map((piece) => piece.trim()));
|
|
3149
|
+
}
|
|
3150
|
+
function check_filters(command) {
|
|
3151
|
+
return pipelines_of(command).flatMap(
|
|
3152
|
+
([head = "", ...filters]) => is_verification_command(head) ? filters : []
|
|
3153
|
+
);
|
|
3154
|
+
}
|
|
3155
|
+
function command_pieces(command) {
|
|
3156
|
+
return pipelines_of(command).flat();
|
|
3157
|
+
}
|
|
3158
|
+
function runs_verification(command) {
|
|
3159
|
+
return command_pieces(command).some((piece) => is_verification_command(piece));
|
|
3160
|
+
}
|
|
3161
|
+
function keeps_verdict_intact(command) {
|
|
3162
|
+
return runs_verification(command) && !is_masked_verification(command);
|
|
3163
|
+
}
|
|
3164
|
+
var PIPEFAIL_OPEN = "(set -o pipefail; ";
|
|
3165
|
+
var PIPEFAIL_CLOSE = ")";
|
|
3166
|
+
var COMMENT_START = /(?:^|\s)#/u;
|
|
3167
|
+
var FULL_READING_FILTER = /^(?:tail|grep|cut|sort|uniq|wc|tee)(?=\s|$)/u;
|
|
3168
|
+
var EARLY_EXIT_GREP = /^grep\b.*\s(?:-[a-zA-Z]*[lLmq]|--(?:quiet|silent|max-count|files-with(?:out)?-match))/u;
|
|
3169
|
+
var AWK_PROGRAM = /^awk\s+(?:-F\s*\S+\s+)?'[^']*'$/u;
|
|
3170
|
+
var AWK_EARLY_EXIT = /\b(?:exit|nextfile|system|getline)\b|\|/u;
|
|
3171
|
+
var SED_TEXT = String.raw`(?:[^/\\']|\\.)*`;
|
|
3172
|
+
var SED_ADDRESS = String.raw`(?:\d+|\$|/${SED_TEXT}/)`;
|
|
3173
|
+
var SED_COMMAND = String.raw`(?:${SED_ADDRESS}(?:,${SED_ADDRESS})?)?!?(?:p|d|s/${SED_TEXT}/${SED_TEXT}/[gip\d]*)`;
|
|
3174
|
+
var SED_PROGRAM = new RegExp(
|
|
3175
|
+
String.raw`^sed\s+(?:-[nEr]+\s+)*'${SED_COMMAND}(?:;\s*${SED_COMMAND})*'$`,
|
|
3176
|
+
"u"
|
|
3177
|
+
);
|
|
3178
|
+
var ALLOWED_OPERATORS = /2>&1|&&/gu;
|
|
3179
|
+
var SIDE_EFFECT_SYNTAX = /[&<>`]|\$\(/u;
|
|
3180
|
+
var GROUP_SYNTAX = /[()]/u;
|
|
3181
|
+
var PIPEFAIL_NOTE = "\u21BB piped verification: the josh check was piped, so its pipeline was wrapped in `(set -o pipefail; \u2026)` to carry the check's status, and the rest of the line ran as typed. Read the printed verdict, never the exit code alone \u2014 `prompts/collaboration-workflow/output-bounds.md`.";
|
|
3182
|
+
function is_full_reading_script(typed) {
|
|
3183
|
+
if (AWK_PROGRAM.test(typed)) return !AWK_EARLY_EXIT.test(typed);
|
|
3184
|
+
return SED_PROGRAM.test(typed);
|
|
3185
|
+
}
|
|
3186
|
+
function reads_to_the_end(piece, typed) {
|
|
3187
|
+
if (FULL_READING_FILTER.test(piece)) return !EARLY_EXIT_GREP.test(piece);
|
|
3188
|
+
return is_full_reading_script(typed);
|
|
3189
|
+
}
|
|
3190
|
+
function typed_slices(raw, pieces, gap) {
|
|
3191
|
+
const offset = { at: 0 };
|
|
3192
|
+
return pieces.map((piece) => {
|
|
3193
|
+
const typed = raw.slice(offset.at, offset.at + piece.length);
|
|
3194
|
+
offset.at += piece.length + gap;
|
|
3195
|
+
return typed;
|
|
3196
|
+
});
|
|
3197
|
+
}
|
|
3198
|
+
function is_filtered_check(pieces, typed) {
|
|
3199
|
+
const [head = "", ...filters] = pieces.map((piece) => piece.trim());
|
|
3200
|
+
return is_verification_command(head) && filters.every((piece, index) => reads_to_the_end(piece, typed[index + 1]?.trim() ?? ""));
|
|
3201
|
+
}
|
|
3202
|
+
function has_side_effect_syntax(command) {
|
|
3203
|
+
return SIDE_EFFECT_SYNTAX.test(command.replaceAll(ALLOWED_OPERATORS, " "));
|
|
3204
|
+
}
|
|
3205
|
+
function is_rewritable_part(raw, judged) {
|
|
3206
|
+
const pieces = judged.split("|");
|
|
3207
|
+
return !has_side_effect_syntax(raw) && !GROUP_SYNTAX.test(judged) && is_filtered_check(pieces, typed_slices(raw, pieces, 1));
|
|
3208
|
+
}
|
|
3209
|
+
function code_end(judged) {
|
|
3210
|
+
const comment = COMMENT_START.exec(judged);
|
|
3211
|
+
return comment === null ? judged.length : comment.index;
|
|
3212
|
+
}
|
|
3213
|
+
function wrapped(raw, judged) {
|
|
3214
|
+
const code = raw.slice(0, code_end(judged));
|
|
3215
|
+
const core = code.trim();
|
|
3216
|
+
const lead = code.slice(0, code.indexOf(core));
|
|
3217
|
+
return `${lead}${PIPEFAIL_OPEN}${core}${PIPEFAIL_CLOSE}${raw.slice(lead.length + core.length)}`;
|
|
3218
|
+
}
|
|
3219
|
+
function rewritten_part(raw, judged) {
|
|
3220
|
+
if (!is_masked_verification(judged)) return raw;
|
|
3221
|
+
return is_rewritable_part(raw, judged) ? wrapped(raw, judged) : void 0;
|
|
3222
|
+
}
|
|
3223
|
+
function pipefail_command(command) {
|
|
3224
|
+
const judged = time_shell.blanked(command);
|
|
3225
|
+
if (judged === void 0 || !is_masked_verification(command)) return void 0;
|
|
3226
|
+
const pieces = shell_segments.parts_of(judged);
|
|
3227
|
+
const parts = typed_slices(command, pieces, 0).map(
|
|
3228
|
+
(raw, index) => rewritten_part(raw, pieces[index] ?? "")
|
|
3229
|
+
);
|
|
3230
|
+
const rewritten = parts.includes(void 0) ? void 0 : parts.join("");
|
|
3231
|
+
return rewritten === command ? void 0 : rewritten;
|
|
3232
|
+
}
|
|
3233
|
+
function pipefail_input(call) {
|
|
3234
|
+
if (call.name !== cost_blocks.BASH_TOOL || !json_value.is_record(call.input)) return void 0;
|
|
3235
|
+
const command = pipefail_command(time_shell.bash_command(call.input));
|
|
3236
|
+
return command === void 0 ? void 0 : { ...call.input, command };
|
|
3237
|
+
}
|
|
3238
|
+
var piped_verification = {
|
|
3239
|
+
EARLY_EXIT_GREP,
|
|
3240
|
+
PIPED_VERIFICATION_REASON,
|
|
3241
|
+
PIPEFAIL_NOTE,
|
|
3242
|
+
VERIFICATION_COMMANDS,
|
|
3243
|
+
check_filters,
|
|
3244
|
+
is_masked_verification,
|
|
3245
|
+
keeps_verdict_intact,
|
|
3246
|
+
pipefail_input,
|
|
3247
|
+
runs_verification
|
|
3248
|
+
};
|
|
3249
|
+
|
|
3250
|
+
// scripts/rules/poll-loop.ts
|
|
3251
|
+
var LOOP_KEYWORD2 = /\b(?:while|until)\s/u;
|
|
3252
|
+
var SLEEP_COMMAND = /\bsleep\s+\S/u;
|
|
3253
|
+
var COMMAND_POSITION = String.raw`(?:^|[;&|({\n]|\b(?:do|then|else)\s)\s*`;
|
|
3254
|
+
var UNTIL_HEADER = new RegExp(String.raw`${COMMAND_POSITION}until\s`, "u");
|
|
3255
|
+
var WHILE_HEADER = String.raw`${COMMAND_POSITION}while\s+(?:!\s*)?`;
|
|
3256
|
+
var PROBE_HEADERS = [
|
|
3257
|
+
String.raw`\[\[?\s+(?:!\s*)?-[defs]\s`,
|
|
3258
|
+
String.raw`test\s+(?:!\s*)?-[defs]\s`,
|
|
3259
|
+
String.raw`(?:kill\s+-0|pgrep)\b`,
|
|
3260
|
+
String.raw`grep\s+-[A-Za-z]*q`
|
|
3261
|
+
].map((probe) => new RegExp(`${WHILE_HEADER}${probe}`, "u"));
|
|
3262
|
+
var COUNTED_FOR = new RegExp(
|
|
3263
|
+
String.raw`${COMMAND_POSITION}for(?:\s*\(\(|\s+\w+\s+in\s+(?:\$\(\s*seq\b|\{\d+\.\.))`,
|
|
3264
|
+
"u"
|
|
3265
|
+
);
|
|
3266
|
+
var THROUGH_FIRST_DONE = /^[\s\S]*?\bdone\b/u;
|
|
3267
|
+
var SETTLE_SECONDS = 10;
|
|
3268
|
+
var NO_SECONDS2 = 0;
|
|
3269
|
+
var READ_COMMAND = /^(?:tail|cat|head|ps|stat|grep|wc|ls|pgrep|test|kill\s+-0|\[\[?)(?=\s|$)/u;
|
|
3270
|
+
function slept_seconds(segments) {
|
|
3271
|
+
return segments.reduce(
|
|
3272
|
+
(total, segment) => total + (early_heartbeat.sleep_seconds(segment) ?? NO_SECONDS2),
|
|
3273
|
+
NO_SECONDS2
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
function is_sleep_then_read(plain) {
|
|
3277
|
+
const segments = shell_segments.commands_of(plain).filter((segment) => segment !== "");
|
|
3278
|
+
const first_work = segments.findIndex((segment) => !early_heartbeat.is_wait_or_inert(segment));
|
|
3279
|
+
const waited = first_work === -1 ? NO_SECONDS2 : slept_seconds(segments.slice(0, first_work));
|
|
3280
|
+
if (waited < SETTLE_SECONDS) return false;
|
|
3281
|
+
return segments.slice(first_work).every((segment) => early_heartbeat.is_wait_or_inert(segment) || READ_COMMAND.test(segment));
|
|
3282
|
+
}
|
|
3283
|
+
function is_state_wait(plain) {
|
|
3284
|
+
return UNTIL_HEADER.test(plain) || PROBE_HEADERS.some((header) => header.test(plain));
|
|
3285
|
+
}
|
|
3286
|
+
function is_counted_sleep(plain) {
|
|
3287
|
+
const header = COUNTED_FOR.exec(plain);
|
|
3288
|
+
if (header === null) return false;
|
|
3289
|
+
const rest = plain.slice(header.index);
|
|
3290
|
+
return SLEEP_COMMAND.test(THROUGH_FIRST_DONE.exec(rest)?.[0] ?? rest);
|
|
3291
|
+
}
|
|
3292
|
+
function is_sleeping_loop(plain) {
|
|
3293
|
+
return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain) || is_counted_sleep(plain);
|
|
3294
|
+
}
|
|
3295
|
+
var STREAM_READER = /\bwhile\s+read\b/u;
|
|
3296
|
+
function is_stream_reader(plain) {
|
|
3297
|
+
return STREAM_READER.test(plain);
|
|
3298
|
+
}
|
|
3299
|
+
function is_poll_loop(command) {
|
|
3300
|
+
const plain = time_shell.unquoted(command);
|
|
3301
|
+
if (is_stream_reader(plain)) return false;
|
|
3302
|
+
if (is_state_wait(plain)) return true;
|
|
3303
|
+
return is_sleeping_loop(plain) || is_sleep_then_read(plain);
|
|
3304
|
+
}
|
|
3305
|
+
var POLL_LOOP_REASON = "\u26D4 output poll loop: a `while`/`until` or counted `for` loop, or a `sleep` chain before a read, does not know when the task finished. `run_in_background` and end the turn \u2014 its exit re-invokes you; a background `Agent` is waited on by its completion notification (lane child: bare `sleep 20`, `pnpm josh run:liveness`); CI by `pnpm josh followup` (`background-commands.md`). Fires on every occurrence.";
|
|
3306
|
+
var ROW31 = {
|
|
3307
|
+
id: "poll-loop",
|
|
3308
|
+
is_trigger: bash_triggers.on_bash_command(is_poll_loop),
|
|
3309
|
+
reason: POLL_LOOP_REASON,
|
|
3310
|
+
decide: () => true,
|
|
3311
|
+
about: {
|
|
3312
|
+
title: "Output poll loop",
|
|
3313
|
+
topic: "`.claude/skills/workflow-commands/background-commands.md`",
|
|
3314
|
+
fires: "a hand-written `while` / `until` wait loop over a command's output, a marker file or a process, a counted `for` that sleeps, or a `sleep` chain of 10 s or more before a read of a file or process. Every occurrence",
|
|
3315
|
+
quiet: "the command started with `run_in_background` \u2014 its exit ends the wait"
|
|
3316
|
+
}
|
|
3317
|
+
};
|
|
3318
|
+
var poll_loop = {
|
|
3319
|
+
POLL_LOOP_REASON,
|
|
3320
|
+
ROW: ROW31,
|
|
3321
|
+
is_poll_loop
|
|
3322
|
+
};
|
|
3323
|
+
|
|
3324
|
+
// scripts/rules/raw-field-body.ts
|
|
3325
|
+
var RAW_FIELD_BODY_AT = /(?:^|\s)(?:-f|--raw-field)[\s=]+['"]?body=['"]?@/u;
|
|
3326
|
+
function posts_at_path_literally(command) {
|
|
3327
|
+
return RAW_FIELD_BODY_AT.test(command);
|
|
3328
|
+
}
|
|
3329
|
+
var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which posts the string `@<path>` rather than the file. Post the comment with `pnpm josh issue:comment <N> --body-file <path>`; this rule fires on every occurrence (`prompts/collaboration-workflow/shell-body.md`).";
|
|
3330
|
+
var ROW32 = {
|
|
3331
|
+
id: "raw-field-body",
|
|
3332
|
+
is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
|
|
3333
|
+
reason: RAW_FIELD_BODY_REASON,
|
|
3334
|
+
decide: () => true,
|
|
3335
|
+
about: {
|
|
3336
|
+
title: "Posting `@path` literally",
|
|
3337
|
+
topic: "`shell-body.md`",
|
|
3338
|
+
fires: "a raw field (`-f` / `--raw-field`) passing `body=@<path>`. Every occurrence",
|
|
3339
|
+
quiet: "`-F` / `--field body=@`, a body without `@`, or `pnpm josh issue:comment`"
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW32, posts_at_path_literally };
|
|
3343
|
+
|
|
3344
|
+
// scripts/rules/round-one-check-related.ts
|
|
3345
|
+
var CHECK_COMMANDS = /* @__PURE__ */ new Set(["check:related"]);
|
|
3346
|
+
var REVIEW_COMMANDS = /* @__PURE__ */ new Set(["run:review", "run:chain"]);
|
|
3347
|
+
var SHIP_COMMANDS = /* @__PURE__ */ new Set(["ship"]);
|
|
3348
|
+
var JOIN_FLAG = /(?:^|\s)--join(?:[=\s]|$)/u;
|
|
3349
|
+
var READING_MODE_FLAG = /(?:^|\s)--(?:pinned|push)(?:[=\s]|$)/u;
|
|
3350
|
+
var DETACH_FLAG = /(?:^|\s)--detach(?:[=\s]|$)/u;
|
|
3351
|
+
var TOUCHED_FLAG = /(?:^|\s)--touched(?:[=\s]|$)/u;
|
|
3352
|
+
function is_check_related(segment) {
|
|
3353
|
+
return shell_segments.is_josh_command(segment, CHECK_COMMANDS);
|
|
3354
|
+
}
|
|
3355
|
+
function is_full_reach_check(segment) {
|
|
3356
|
+
return is_check_related(segment) && !TOUCHED_FLAG.test(segment);
|
|
3357
|
+
}
|
|
3358
|
+
function review_event(segment) {
|
|
3359
|
+
if (JOIN_FLAG.test(segment)) return "close";
|
|
3360
|
+
return READING_MODE_FLAG.test(segment) ? "none" : "brief";
|
|
3361
|
+
}
|
|
3362
|
+
function segment_event(segment) {
|
|
3363
|
+
if (shell_segments.is_josh_command(segment, REVIEW_COMMANDS)) return review_event(segment);
|
|
3364
|
+
if (is_full_reach_check(segment)) return "check";
|
|
3365
|
+
const is_detach = shell_segments.is_josh_command(segment, SHIP_COMMANDS) && DETACH_FLAG.test(segment);
|
|
3366
|
+
return is_detach ? "close" : "none";
|
|
3367
|
+
}
|
|
3368
|
+
function block_events(block) {
|
|
3369
|
+
if (tail_commands.is_bash_use(block)) {
|
|
3370
|
+
const segments = shell_segments.segments_of(time_shell.bash_command(block.input));
|
|
3371
|
+
return segments.map((segment) => segment_event(segment));
|
|
3372
|
+
}
|
|
3373
|
+
const is_edit = time_markers.tool_marker(block.name, block.input) === time_markers.EDIT_MARKER;
|
|
3374
|
+
return is_edit ? ["edit"] : [];
|
|
3375
|
+
}
|
|
3376
|
+
function ran_call_ids(blocks) {
|
|
3377
|
+
const results = blocks.filter((block) => block.result_id !== "" && block.refusal_guard === "");
|
|
3378
|
+
return new Set(results.map((block) => block.result_id));
|
|
3379
|
+
}
|
|
3380
|
+
function round_events(tail) {
|
|
3381
|
+
const blocks = time_transcript_line.parse_text(tail).flatMap((line) => line.blocks);
|
|
3382
|
+
const ran = ran_call_ids(blocks);
|
|
3383
|
+
return blocks.flatMap(
|
|
3384
|
+
(block) => block_events(block).map((event) => event === "check" && !ran.has(block.id) ? "none" : event)
|
|
3385
|
+
);
|
|
3386
|
+
}
|
|
3387
|
+
function fixes_an_open_round(tail) {
|
|
3388
|
+
const events = round_events(tail);
|
|
3389
|
+
const brief_at = events.lastIndexOf("brief");
|
|
3390
|
+
if (brief_at === -1) return false;
|
|
3391
|
+
const since_brief = new Set(events.slice(brief_at));
|
|
3392
|
+
return since_brief.has("edit") && !since_brief.has("close");
|
|
3393
|
+
}
|
|
3394
|
+
function runs_in_lane_child(command, is_counted, context) {
|
|
3395
|
+
if (shell_segments.segments_of(command).every((segment) => !is_counted(segment))) return false;
|
|
3396
|
+
return lane_child_marker.is_child_of(context.cwd, context.env);
|
|
3397
|
+
}
|
|
3398
|
+
function is_candidate4(command, context) {
|
|
3399
|
+
return runs_in_lane_child(command, is_check_related, context);
|
|
3400
|
+
}
|
|
3401
|
+
function is_full_reach_candidate(command, context) {
|
|
3402
|
+
return runs_in_lane_child(command, is_full_reach_check, context);
|
|
3403
|
+
}
|
|
3404
|
+
function decide3(_call, run) {
|
|
3405
|
+
return fixes_an_open_round(transcript_tail.read_tail(run.transcript));
|
|
3406
|
+
}
|
|
3407
|
+
var ROUND_ONE_CHECK_RELATED_REASON = "\u26D4 round-one check:related: round 1's fixes need no `check:related` \u2014 ship's pre-detach reruns the scoped pair on the fixed tree; refused on every occurrence. Go on to `pnpm josh run:review --join`; `--join --fixing` while round 1 has fixes to make (`.claude/skills/workflow-commands/chain-rule.md` \u2192 \"A lane child\").";
|
|
3408
|
+
var ROW33 = {
|
|
3409
|
+
id: "round-one-check-related",
|
|
3410
|
+
is_trigger: bash_triggers.on_bash_command(is_candidate4),
|
|
3411
|
+
reason: ROUND_ONE_CHECK_RELATED_REASON,
|
|
3412
|
+
decide: decide3,
|
|
3413
|
+
about: {
|
|
3414
|
+
title: "No check:related on round 1's fixes",
|
|
3415
|
+
topic: '`.claude/skills/workflow-commands/chain-rule.md` \u2192 "A lane child"',
|
|
3416
|
+
fires: "`pnpm josh check:related` (`josh cr`) in a lane child after an `Edit` / `Write` that follows `run:review`'s (or `run:chain`'s) brief, before `run:review --join`, `run:chain --join` or `ship --detach`. Every occurrence",
|
|
3417
|
+
quiet: "no fix since the brief, already joined or detached, or not a lane child"
|
|
3418
|
+
}
|
|
3419
|
+
};
|
|
3420
|
+
var round_one_check_related = {
|
|
3421
|
+
ROUND_ONE_CHECK_RELATED_REASON,
|
|
3422
|
+
ROW: ROW33,
|
|
3423
|
+
fixes_an_open_round,
|
|
3424
|
+
round_events,
|
|
3425
|
+
is_full_reach_candidate
|
|
3426
|
+
};
|
|
3427
|
+
|
|
3428
|
+
// scripts/rules/repeat-check-related.ts
|
|
3429
|
+
function repeats_before_brief(tail) {
|
|
3430
|
+
const events = round_one_check_related.round_events(tail);
|
|
3431
|
+
const since_close = events.slice(events.lastIndexOf("close") + 1);
|
|
3432
|
+
if (since_close.includes("brief")) return false;
|
|
3433
|
+
return since_close.includes("check");
|
|
3434
|
+
}
|
|
3435
|
+
function decide4(_call, run) {
|
|
3436
|
+
return repeats_before_brief(transcript_tail.read_tail(run.transcript));
|
|
3437
|
+
}
|
|
3438
|
+
var REPEAT_CHECK_RELATED_REASON = '\u26D4 repeat check:related: the full-reach `check:related` runs once before the brief; refused on every later occurrence. Mid-run, run `pnpm josh check:related --touched` (never refused), or the failing check by name \u2014 `pnpm josh lint:related`, `check`, `test:related` (`--touched` mid-run), `cspell:dot` \u2014 then `pnpm josh run:review` (`.claude/skills/workflow-commands/chain-rule.md` \u2192 "A lane child").';
|
|
3439
|
+
var ROW34 = {
|
|
3440
|
+
id: "repeat-check-related",
|
|
3441
|
+
is_trigger: bash_triggers.on_bash_command(round_one_check_related.is_full_reach_candidate),
|
|
3442
|
+
reason: REPEAT_CHECK_RELATED_REASON,
|
|
3443
|
+
decide: decide4,
|
|
3444
|
+
about: {
|
|
3445
|
+
title: "One check:related before the brief",
|
|
3446
|
+
topic: '`.claude/skills/workflow-commands/chain-rule.md` \u2192 "A lane child"',
|
|
3447
|
+
fires: "`pnpm josh check:related` (`josh cr`) in a lane child after an earlier one that ran, with no `run:review` (or `run:chain`) brief, join or `ship --detach` since. Every occurrence",
|
|
3448
|
+
quiet: "the first since the last join or detach, a brief since, a `--touched` run, or not a lane child"
|
|
3449
|
+
}
|
|
3450
|
+
};
|
|
3451
|
+
var repeat_check_related = {
|
|
3452
|
+
REPEAT_CHECK_RELATED_REASON,
|
|
3453
|
+
ROW: ROW34,
|
|
3454
|
+
repeats_before_brief
|
|
3455
|
+
};
|
|
3456
|
+
|
|
3457
|
+
// scripts/rules/reviewer-foreground.ts
|
|
3458
|
+
var REVIEWER_AGENTS = /* @__PURE__ */ new Set(["reviewer", "reviewer-verification"]);
|
|
3459
|
+
var BACKGROUND_FIELD2 = "run_in_background";
|
|
3460
|
+
var CHAIN_RULE_STEP = '`.claude/skills/workflow-commands/chain-rule.md` \u2192 "A lane child"';
|
|
3461
|
+
var KILLED = "reviewer foreground: a lane child\u2019s turn end kills a background `reviewer`";
|
|
3462
|
+
var FOREGROUND = `\`${BACKGROUND_FIELD2}: false\``;
|
|
3463
|
+
var REVIEWER_FOREGROUND_NOTE = `\u21BB ${KILLED}, so the call ran with ${FOREGROUND} (${CHAIN_RULE_STEP}).`;
|
|
3464
|
+
var REVIEWER_FOREGROUND_REASON = `\u26D4 ${KILLED}. Reissue the call with ${FOREGROUND} (${CHAIN_RULE_STEP}). It fires on every occurrence.`;
|
|
3465
|
+
function is_reviewer(call) {
|
|
3466
|
+
if (!agent_model.SUBAGENT_TOOLS.has(call.name) || !json_value.is_record(call.input)) return false;
|
|
3467
|
+
const type = call.input["subagent_type"];
|
|
3468
|
+
return typeof type === "string" && REVIEWER_AGENTS.has(type.split(":").at(-1) ?? "");
|
|
3469
|
+
}
|
|
3470
|
+
function is_foreground2(call) {
|
|
3471
|
+
return json_value.is_record(call.input) && call.input[BACKGROUND_FIELD2] === false;
|
|
3472
|
+
}
|
|
3473
|
+
function is_background_reviewer(call, is_lane_child) {
|
|
3474
|
+
return is_lane_child && is_reviewer(call) && !is_foreground2(call);
|
|
3475
|
+
}
|
|
3476
|
+
function foreground_input(call, is_lane_child) {
|
|
3477
|
+
if (!is_background_reviewer(call, is_lane_child) || !json_value.is_record(call.input)) {
|
|
3478
|
+
return void 0;
|
|
3479
|
+
}
|
|
3480
|
+
return { ...call.input, [BACKGROUND_FIELD2]: false };
|
|
3481
|
+
}
|
|
3482
|
+
var ROW35 = {
|
|
3483
|
+
id: "reviewer-foreground",
|
|
3484
|
+
is_trigger: (call, context) => is_background_reviewer(call, lane_child_marker.is_child_of(context.cwd, context.env)),
|
|
3485
|
+
reason: REVIEWER_FOREGROUND_REASON,
|
|
3486
|
+
decide: () => true,
|
|
3487
|
+
keeps: (call) => is_reviewer(call) && is_foreground2(call),
|
|
3488
|
+
reaches: is_reviewer,
|
|
3489
|
+
rewrite: (call, _tail, context) => foreground_input(call, lane_child_marker.is_child_of(context.cwd, context.env)),
|
|
3490
|
+
rewrite_note: REVIEWER_FOREGROUND_NOTE,
|
|
3491
|
+
about: {
|
|
3492
|
+
title: "Lane child reviewer dispatched in the foreground",
|
|
3493
|
+
topic: CHAIN_RULE_STEP,
|
|
3494
|
+
fires: "a dispatched lane child\u2019s `Agent` / `Task` call of the `reviewer` or `reviewer-verification` type without `run_in_background: false` \u2014 rewritten with it, every occurrence",
|
|
3495
|
+
quiet: "not a lane child, another agent type, or `run_in_background: false` set"
|
|
3496
|
+
}
|
|
3497
|
+
};
|
|
3498
|
+
var reviewer_foreground = {
|
|
3499
|
+
REVIEWER_FOREGROUND_NOTE,
|
|
3500
|
+
REVIEWER_FOREGROUND_REASON,
|
|
3501
|
+
ROW: ROW35,
|
|
3502
|
+
foreground_input,
|
|
3503
|
+
is_background_reviewer
|
|
3504
|
+
};
|
|
3505
|
+
|
|
3506
|
+
// scripts/rules/rule-body-guard.ts
|
|
3507
|
+
var { WRITE: WRITE2, is_edit_tool: is_edit_tool3, string_field: string_field2 } = edit_tool;
|
|
3508
|
+
var CLAUDE_DOCUMENT = /(?:^|\/)CLAUDE\.md$/u;
|
|
3509
|
+
var PROMPT_DOCUMENT = /(?:^|\/)prompts\/.+\.md$/u;
|
|
3510
|
+
var SKILL_DOCUMENT = /(?:^|\/)\.claude\/skills\/.+\.md$/u;
|
|
3511
|
+
function is_rule_document(file_path) {
|
|
3512
|
+
return CLAUDE_DOCUMENT.test(file_path) || PROMPT_DOCUMENT.test(file_path) || SKILL_DOCUMENT.test(file_path);
|
|
3513
|
+
}
|
|
3514
|
+
var LINK_TARGET = /\]\([^)]*\)/gu;
|
|
3515
|
+
var BARE_URL = /https?:\/\/\S+/gu;
|
|
3516
|
+
function prose_of(text) {
|
|
3517
|
+
return text.replaceAll(LINK_TARGET, "]()").replaceAll(BARE_URL, "");
|
|
3518
|
+
}
|
|
3519
|
+
var MIN_APPENDED_PROSE = 120;
|
|
3520
|
+
function adds_rule_prose(old_text, new_text) {
|
|
3521
|
+
return prose_of(new_text).length - prose_of(old_text).length >= MIN_APPENDED_PROSE;
|
|
3522
|
+
}
|
|
3523
|
+
function read_existing(file_path) {
|
|
3524
|
+
return file_reader.read_file_or_empty(file_path);
|
|
3525
|
+
}
|
|
3526
|
+
function texts_of(name, input, read) {
|
|
3527
|
+
if (name === WRITE2) {
|
|
3528
|
+
return {
|
|
3529
|
+
old_text: read(string_field2(input, "file_path")),
|
|
3530
|
+
new_text: string_field2(input, "content")
|
|
3531
|
+
};
|
|
3532
|
+
}
|
|
3533
|
+
return {
|
|
3534
|
+
old_text: string_field2(input, "old_string"),
|
|
3535
|
+
new_text: string_field2(input, "new_string")
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
function rule_document_input(call) {
|
|
3539
|
+
if (!is_edit_tool3(call.name) || !json_value.is_record(call.input)) return void 0;
|
|
3540
|
+
return is_rule_document(string_field2(call.input, "file_path")) ? call.input : void 0;
|
|
3541
|
+
}
|
|
3542
|
+
function writes_rule_prose(call, read = read_existing) {
|
|
3543
|
+
const input = rule_document_input(call);
|
|
3544
|
+
if (input === void 0) return false;
|
|
3545
|
+
const { old_text, new_text } = texts_of(call.name, input, read);
|
|
3546
|
+
return adds_rule_prose(old_text, new_text);
|
|
3547
|
+
}
|
|
3548
|
+
var RULE_BODY_REASON = "\u26D4 rule written into prose: this edit adds rule text to CLAUDE.md, a `prompts/**` doc or a skill body. Answer question 0 (`pnpm josh oracle:list`) and the ordering question (`pnpm josh run:step`) of `prompts/collaboration-workflow/residency.md`; refused until both commands are on the transcript, then reissue this as a trigger-plus-pointer line (`prompts/collaboration-workflow/rule-delivery.md`).";
|
|
3549
|
+
var PLACEMENT_ORACLES = [
|
|
3550
|
+
/* @__PURE__ */ new Set(["oracle:list"]),
|
|
3551
|
+
/* @__PURE__ */ new Set(["run:step", "run:next"])
|
|
3552
|
+
];
|
|
3553
|
+
function ran_oracle(tail_commands_run, names) {
|
|
3554
|
+
return tail_commands_run.some(
|
|
3555
|
+
(command) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
|
|
3556
|
+
);
|
|
3557
|
+
}
|
|
3558
|
+
function answered_placement(tail, _call) {
|
|
3559
|
+
const commands = tail_commands.prior_bash_commands(tail);
|
|
3560
|
+
return PLACEMENT_ORACLES.every((names) => ran_oracle(commands, names));
|
|
3561
|
+
}
|
|
3562
|
+
function runs_placement_oracle(command) {
|
|
3563
|
+
return PLACEMENT_ORACLES.some(
|
|
3564
|
+
(names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
|
|
3565
|
+
);
|
|
3566
|
+
}
|
|
3567
|
+
var ROW36 = {
|
|
3568
|
+
id: "rule-body",
|
|
3569
|
+
is_trigger: writes_rule_prose,
|
|
3570
|
+
reason: RULE_BODY_REASON,
|
|
3571
|
+
already_satisfied: answered_placement,
|
|
3572
|
+
keeps: bash_triggers.on_bash_command(runs_placement_oracle),
|
|
3573
|
+
about: {
|
|
3574
|
+
title: "Question 0 and the ordering question before writing a rule into prose",
|
|
3575
|
+
topic: "`residency.md`",
|
|
3576
|
+
fires: "an `Edit` / `Write` to `CLAUDE.md` / `prompts/**/*.md` / `.claude/skills/**/*.md` appending at least the threshold. Refused until both `pnpm josh oracle:list` and `pnpm josh run:step` ran",
|
|
3577
|
+
quiet: "not a rule document, an append under the threshold (typo, link swap, deletion), or both commands ran"
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
var rule_body_guard = {
|
|
3581
|
+
RULE_BODY_REASON,
|
|
3582
|
+
ROW: ROW36,
|
|
3583
|
+
answered_placement,
|
|
3584
|
+
is_rule_document,
|
|
3585
|
+
writes_rule_prose
|
|
3586
|
+
};
|
|
3587
|
+
|
|
3588
|
+
// scripts/rules/shell-body-trigger.ts
|
|
3589
|
+
var FIELD_FLAGS = "(?:-f|-F|--field|--raw-field)";
|
|
3590
|
+
var MESSAGE_FLAGS = "(?:--body|--notify-message|--recommended|--alternative)";
|
|
3591
|
+
var FIELD_OPENING = String.raw`${FIELD_FLAGS}[\s=]*(?:'?body="|"body=)`;
|
|
3592
|
+
var MESSAGE_OPENING = String.raw`${MESSAGE_FLAGS}[\s=]+"`;
|
|
3593
|
+
var NESTED_SUBSTITUTION = String.raw`\$\([^()]*\)`;
|
|
3594
|
+
var SUBSTITUTION2 = String.raw`\$\((?:[^()$]|\$(?!\()|${NESTED_SUBSTITUTION})*\)`;
|
|
3595
|
+
var QUOTED_VALUE = String.raw`(?:[^"\\$]|\\.|\$(?!\()|${SUBSTITUTION2})*`;
|
|
3596
|
+
var INLINE_BODY_VALUE = new RegExp(
|
|
3597
|
+
`(?:${FIELD_OPENING}|${MESSAGE_OPENING})(${QUOTED_VALUE})"`,
|
|
3598
|
+
"gu"
|
|
3599
|
+
);
|
|
3600
|
+
var SHELL_EVALUATED = /[`$]/u;
|
|
3601
|
+
var WHOLE_VALUE_SUBSTITUTION = new RegExp(`^${SUBSTITUTION2}$`, "u");
|
|
3602
|
+
var BACKTICK = "`";
|
|
3603
|
+
var WHOLE_VALUE_FILE_REFERENCE = /^@\S+$/u;
|
|
3604
|
+
var ESCAPED_PAIR = /\\./gu;
|
|
3605
|
+
function is_whole_value_substitution(literal) {
|
|
3606
|
+
return WHOLE_VALUE_SUBSTITUTION.test(literal) && !literal.includes(BACKTICK);
|
|
3607
|
+
}
|
|
3608
|
+
function is_body_already_in_a_file(literal) {
|
|
3609
|
+
return WHOLE_VALUE_FILE_REFERENCE.test(literal) || is_whole_value_substitution(literal);
|
|
3610
|
+
}
|
|
3611
|
+
function is_evaluated_value(raw_value) {
|
|
3612
|
+
const literal = raw_value.replaceAll(ESCAPED_PAIR, "");
|
|
3613
|
+
if (is_body_already_in_a_file(literal)) return false;
|
|
3614
|
+
return SHELL_EVALUATED.test(literal);
|
|
3615
|
+
}
|
|
3616
|
+
function is_shell_evaluated_body(command) {
|
|
3617
|
+
for (const match of command.matchAll(INLINE_BODY_VALUE)) {
|
|
3618
|
+
if (is_evaluated_value(match[1] ?? "")) return true;
|
|
3619
|
+
}
|
|
3620
|
+
return false;
|
|
3621
|
+
}
|
|
3622
|
+
var FILE_FLAG = /(?:--body-file|--notify-message-file)(?:[\s=]|$)/u;
|
|
3623
|
+
var FIELD_FILE_REFERENCE = new RegExp(String.raw`${FIELD_FLAGS}[\s=]*body=@\S`, "u");
|
|
3624
|
+
var ANSI_C_BODY = /(?:(?:--body|--notify-message)[\s=]+|body=)\$'/u;
|
|
3625
|
+
var DOUBLE_QUOTED_SPAN = /"[^"]*"/gu;
|
|
3626
|
+
var BODY_FLAG = new RegExp(
|
|
3627
|
+
String.raw`${FIELD_FLAGS}[\s=]*'?"?body=|${MESSAGE_FLAGS}[\s=]|--body-file[\s=]|--notify-message-file[\s=]`,
|
|
3628
|
+
"u"
|
|
3629
|
+
);
|
|
3630
|
+
var QUOTED_SPAN = /"[^"]*"|'[^']*'/gu;
|
|
3631
|
+
function inline_body_literals(command) {
|
|
3632
|
+
return Array.from(
|
|
3633
|
+
command.matchAll(INLINE_BODY_VALUE),
|
|
3634
|
+
(match) => (match[1] ?? "").replaceAll(ESCAPED_PAIR, "")
|
|
3635
|
+
);
|
|
3636
|
+
}
|
|
3637
|
+
function is_safe_body_form(command) {
|
|
3638
|
+
if (ANSI_C_BODY.test(command.replaceAll(DOUBLE_QUOTED_SPAN, " "))) return true;
|
|
3639
|
+
const flags = command.replaceAll(QUOTED_SPAN, " ");
|
|
3640
|
+
if (FILE_FLAG.test(flags) || FIELD_FILE_REFERENCE.test(flags)) return true;
|
|
3641
|
+
return inline_body_literals(command).some((literal) => is_body_already_in_a_file(literal));
|
|
3642
|
+
}
|
|
3643
|
+
function keeps_body_safe(command) {
|
|
3644
|
+
return is_safe_body_form(command) && !is_shell_evaluated_body(command);
|
|
3645
|
+
}
|
|
3646
|
+
function carries_a_body(command) {
|
|
3647
|
+
if (BODY_FLAG.test(command.replaceAll(QUOTED_SPAN, " "))) return true;
|
|
3648
|
+
return inline_body_literals(command).length > 0;
|
|
3649
|
+
}
|
|
3650
|
+
var SHELL_BODY_REASON = "\u26D4 shell-evaluated body: this command carries a body inline in double quotes, and that body contains a backtick or a `$`, so the shell executes it. Reissue this call once the body is in a file, passed by path (`pnpm josh issue:comment <N> --body-file <path>`, `--notify-message-file <path>`), or use `$'\u2026'` quoting (`prompts/collaboration-workflow/shell-body.md`).";
|
|
3651
|
+
var shell_body_trigger = {
|
|
3652
|
+
SHELL_BODY_REASON,
|
|
3653
|
+
carries_a_body,
|
|
3654
|
+
is_shell_evaluated_body,
|
|
3655
|
+
keeps_body_safe
|
|
3656
|
+
};
|
|
3657
|
+
|
|
3658
|
+
// scripts/rules/test-declared-commit.ts
|
|
3659
|
+
function is_untested_commit(call) {
|
|
3660
|
+
if (!run_tail_rule.is_push_step_call(call)) return false;
|
|
3661
|
+
return test_declared_changed.current_verdict() === "required";
|
|
3662
|
+
}
|
|
3663
|
+
var REASON = "\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads it `required` and names the files. Tests are required for ALL code changes: add the test and reissue, or, for a change that touches no runtime path, declare the non-runtime exception in your Step 0 work summary and reissue. It fires once per run (`CLAUDE.md` \u2192 Code Change Rules).";
|
|
3664
|
+
var ROW37 = {
|
|
3665
|
+
id: "test-declared",
|
|
3666
|
+
is_trigger: is_untested_commit,
|
|
3667
|
+
reason: REASON,
|
|
3668
|
+
about: {
|
|
3669
|
+
title: "Test declaration",
|
|
3670
|
+
topic: '`testing-guide.md` \u2192 "0. Per-Requirement Test Coverage (MANDATORY)"',
|
|
3671
|
+
fires: "`pnpm josh git -y` (`josh g`) while `pnpm josh test:declared` answers `required`",
|
|
3672
|
+
quiet: "`exempt` (non-runtime only) or `satisfied` (tests included)"
|
|
3673
|
+
}
|
|
3674
|
+
};
|
|
3675
|
+
var test_declared_commit = { REASON, ROW: ROW37 };
|
|
3676
|
+
|
|
3677
|
+
// scripts/rules/unasked-filing.ts
|
|
3678
|
+
import { z } from "zod";
|
|
3679
|
+
var FILING_WORDS = [
|
|
3680
|
+
/起票/u,
|
|
3681
|
+
/issue\s*[に化]/iu,
|
|
3682
|
+
/\bfile (?:an? |the |this |these )?(?:issues?|it|them)\b/iu,
|
|
3683
|
+
// A typed command whose procedure files: a split, a prerequisite, an upstream interrupt, a `new` form.
|
|
3684
|
+
/^\/?(?:kickoff|fullrun|halfrun|prrun|backlogrun)\b/iu
|
|
3685
|
+
];
|
|
3686
|
+
var AFFIRMATIVE = /^(?:はい|うん|お願い|どうぞ|進めて|yes|ok|okay|sure|please do|go ahead)/iu;
|
|
3687
|
+
var SHORT_REPLY = 40;
|
|
3688
|
+
var PROPOSALS = [
|
|
3689
|
+
/起票(?:しますか|しましょうか|してよい|して良い|してもよい)/u,
|
|
3690
|
+
/issue\s*に(?:しますか|しましょうか)/iu,
|
|
3691
|
+
/\b(?:shall|should) I file\b/iu,
|
|
3692
|
+
/\bfile (?:an? |this as an? |it as an? )?issues?\?/iu
|
|
3693
|
+
];
|
|
3694
|
+
var ASSISTANT_LINE_SCHEMA = z.object({
|
|
3695
|
+
type: z.literal("assistant"),
|
|
3696
|
+
message: z.object({ content: z.unknown() })
|
|
3697
|
+
});
|
|
3698
|
+
var CHECK_FLAG = /\s--check\b/u;
|
|
3699
|
+
var QUOTED = /"[^"]*"|'[^']*'/gu;
|
|
3700
|
+
var UNASKED_FILING_REASON = "\u26D4 unasked filing: this interactive session files an Issue only on the person\u2019s explicit instruction (\u300C\u8D77\u7968\u300D, \u201Cfile an issue\u201D, or a command whose procedure files, such as `kickoff new`). Propose the Issue in the reply and file it once they confirm. It fires on every occurrence (`.claude/skills/workflow-commands/observation-filing.md`).";
|
|
3701
|
+
function is_instructed(prompt) {
|
|
3702
|
+
const trimmed = issue_citation.prose_lines(prompt).join("\n").replaceAll(/(?<!`)(`+)(?!`)[\s\S]*?(?<!`)\1(?!`)/gu, "").replaceAll(/「[^「」\n]*」|『[^『』\n]*』|"[^"\n]*"|'[^'\n]*'/gu, "").trim();
|
|
3703
|
+
return FILING_WORDS.some((pattern) => pattern.test(trimmed));
|
|
3704
|
+
}
|
|
3705
|
+
function reply_text(line) {
|
|
3706
|
+
const parsed = ASSISTANT_LINE_SCHEMA.safeParse(
|
|
3707
|
+
json_value.parse_or_undefined(codex_prompt.normalize(line))
|
|
3708
|
+
);
|
|
3709
|
+
return parsed.success ? time_reported_failure.result_text(parsed.data.message.content) : "";
|
|
3710
|
+
}
|
|
3711
|
+
function is_person_line(line) {
|
|
3712
|
+
return last_prompt.person_prompt(line) !== void 0;
|
|
3713
|
+
}
|
|
3714
|
+
function reply_before_prompt(tail) {
|
|
3715
|
+
const lines = tail.split("\n");
|
|
3716
|
+
const prompt_at = lines.findLastIndex((line) => is_person_line(line));
|
|
3717
|
+
const replies = lines.slice(0, Math.max(prompt_at, 0)).map((line) => reply_text(line));
|
|
3718
|
+
return replies.findLast((text) => text !== "") ?? "";
|
|
3719
|
+
}
|
|
3720
|
+
function is_confirmed(prompt, tail) {
|
|
3721
|
+
const trimmed = prompt.trim();
|
|
3722
|
+
if (trimmed.length > SHORT_REPLY || !AFFIRMATIVE.test(trimmed)) return false;
|
|
3723
|
+
const reply = reply_before_prompt(tail);
|
|
3724
|
+
return PROPOSALS.some((pattern) => pattern.test(reply));
|
|
3725
|
+
}
|
|
3726
|
+
function is_asked(tail) {
|
|
3727
|
+
const prompt = last_prompt.person_prompt(tail) ?? "";
|
|
3728
|
+
return is_instructed(prompt) || is_confirmed(prompt, tail);
|
|
3729
|
+
}
|
|
3730
|
+
function is_run_parent(context) {
|
|
3731
|
+
const repository = git_common_directory.repository(context.cwd);
|
|
3732
|
+
if (repository === void 0) return false;
|
|
3733
|
+
const read = run_carry.read_carry(run_carry.carry_path(repository));
|
|
3734
|
+
return run_headless.driven_here(read, context.ancestry) !== void 0;
|
|
3735
|
+
}
|
|
3736
|
+
function is_unattended(context) {
|
|
3737
|
+
const { env } = context;
|
|
3738
|
+
if (agent_headless.is_headless(env) || run_headless.is_headless(env)) return true;
|
|
3739
|
+
return lane_child_marker.marked_issue(env) !== void 0 || is_run_parent(context);
|
|
3740
|
+
}
|
|
3741
|
+
function is_writing_segment(segment) {
|
|
3742
|
+
if (!bash_triggers.is_issue_filing_segment(segment)) return false;
|
|
3743
|
+
return !CHECK_FLAG.test(segment.replaceAll(QUOTED, ""));
|
|
3744
|
+
}
|
|
3745
|
+
function is_writing_filing(command) {
|
|
3746
|
+
return shell_segments.segments_of(command).some((segment) => is_writing_segment(segment));
|
|
3747
|
+
}
|
|
3748
|
+
function is_unasked_filing(call, context) {
|
|
3749
|
+
if (!bash_triggers.on_bash_command(is_writing_filing)(call)) return false;
|
|
3750
|
+
return !is_unattended(context);
|
|
3751
|
+
}
|
|
3752
|
+
var ROW38 = {
|
|
3753
|
+
id: "unasked-filing",
|
|
3754
|
+
is_trigger: is_unasked_filing,
|
|
3755
|
+
reason: UNASKED_FILING_REASON,
|
|
3756
|
+
already_satisfied: is_asked,
|
|
3757
|
+
decide: () => true,
|
|
3758
|
+
about: {
|
|
3759
|
+
title: "Unasked filing",
|
|
3760
|
+
topic: "`.claude/skills/workflow-commands/observation-filing.md`",
|
|
3761
|
+
fires: "an interactive session\u2019s `pnpm josh issue:file` when the person\u2019s last prompt asks for no filing; it hands over propose-then-file. Every occurrence",
|
|
3762
|
+
quiet: "an unattended run (a kit-launched or headless session, a lane child, the `backlogrun` parent), `--check`, or a prompt that asks for filing"
|
|
3763
|
+
}
|
|
3764
|
+
};
|
|
3765
|
+
var unasked_filing = { ROW: ROW38, is_unasked_filing };
|
|
3766
|
+
|
|
3767
|
+
// scripts/rules/delivered-rules.ts
|
|
3768
|
+
var PRETOOL_GUARD = "pnpm josh pretool:guard";
|
|
3769
|
+
var { is_issue_filing, on_bash_command } = bash_triggers;
|
|
3770
|
+
var { SHELL_BODY_REASON: SHELL_BODY_REASON2, carries_a_body: carries_a_body2, is_shell_evaluated_body: is_shell_evaluated_body2, keeps_body_safe: keeps_body_safe2 } = shell_body_trigger;
|
|
3771
|
+
var DELIVERED_RULES = [
|
|
3772
|
+
// First: a third-party write is stopped before the first-party filing rows count anything.
|
|
3773
|
+
third_party_write.ROW,
|
|
3774
|
+
// Before every first-party row: a credential is refused whatever the call's other faults, and the
|
|
3775
|
+
// commands those rows point at run the same scan in-process.
|
|
3776
|
+
credential_body.ROW,
|
|
3777
|
+
// The direct call only. The filing cap, the WIP cap and the fold question are `issue:file`'s own
|
|
3778
|
+
// steps, so no row gates them.
|
|
3779
|
+
direct_filing.ROW,
|
|
3780
|
+
// An interactive session's `issue:file` the person did not ask for; unattended runs never fire it.
|
|
3781
|
+
unasked_filing.ROW,
|
|
3782
|
+
// Before the body rows: a comment posted by hand is refused whatever its body, and the command it
|
|
3783
|
+
// points at takes the body by path, so the body rows have nothing left to say.
|
|
3784
|
+
direct_issue_comment.ROW,
|
|
3785
|
+
// Before `issue-comments`, whose rewrite would add the unfiltered comments to the same read.
|
|
3786
|
+
direct_issue_read.ROW,
|
|
3787
|
+
issue_comments.ROW,
|
|
3788
|
+
{
|
|
3789
|
+
id: "shell-body",
|
|
3790
|
+
is_trigger: on_bash_command(is_shell_evaluated_body2),
|
|
3791
|
+
reason: SHELL_BODY_REASON2,
|
|
3792
|
+
keeps: on_bash_command(keeps_body_safe2),
|
|
3793
|
+
reaches: on_bash_command(carries_a_body2),
|
|
3794
|
+
about: {
|
|
3795
|
+
title: "No body on the shell",
|
|
3796
|
+
topic: "`shell-body.md`",
|
|
3797
|
+
fires: 'a double-quoted body value (`-f body="\u2026"` / `-f "body=\u2026"` / `--body "\u2026"` / `--notify-message "\u2026"`) carrying a backtick or `$`',
|
|
3798
|
+
quiet: "the shell does not evaluate the body"
|
|
3799
|
+
}
|
|
3800
|
+
},
|
|
3801
|
+
raw_field_body.ROW,
|
|
3802
|
+
// Before `piped-verification`, whose rewrite would run under `pipefail` a check that must not run.
|
|
3803
|
+
round_one_check_related.ROW,
|
|
3804
|
+
repeat_check_related.ROW,
|
|
3805
|
+
{
|
|
3806
|
+
id: "piped-verification",
|
|
3807
|
+
is_trigger: on_bash_command(piped_verification.is_masked_verification),
|
|
3808
|
+
reason: piped_verification.PIPED_VERIFICATION_REASON,
|
|
3809
|
+
keeps: on_bash_command(piped_verification.keeps_verdict_intact),
|
|
3810
|
+
reaches: on_bash_command(piped_verification.runs_verification),
|
|
3811
|
+
// A masking whose filter reads to the end is rewritten under `pipefail`, never refused.
|
|
3812
|
+
rewrite: piped_verification.pipefail_input,
|
|
3813
|
+
rewrite_note: piped_verification.PIPEFAIL_NOTE,
|
|
3814
|
+
about: {
|
|
3815
|
+
title: "Piped verification",
|
|
3816
|
+
topic: "`output-bounds.md`",
|
|
3817
|
+
fires: "a pass/fail josh check (`gate` / `check*` / `lint*` / `cspell*` / `test*` / `eval` / `overrides` / `ranges`) in front of a pipe \u2014 rewritten under `set -o pipefail` when every filter reads to the end (`tail`, a full-reading `grep`, `cut`, `sort`, `uniq`, `wc`, `tee`, an `awk` / `sed` that cannot exit early and carries no `<` / `>` / `&`), refused otherwise",
|
|
3818
|
+
quiet: "the exit code reaches the caller \u2014 a failure reads as a failure"
|
|
3819
|
+
}
|
|
3820
|
+
},
|
|
3821
|
+
{
|
|
3822
|
+
id: "early-heartbeat",
|
|
3823
|
+
is_trigger: on_bash_command(early_heartbeat.is_wait_timer),
|
|
3824
|
+
reason: early_heartbeat.EARLY_HEARTBEAT_REASON,
|
|
3825
|
+
decide: early_heartbeat.decide,
|
|
3826
|
+
keeps: on_bash_command(early_heartbeat.is_progress_watch),
|
|
3827
|
+
reaches: on_bash_command(early_heartbeat.waits_for_progress),
|
|
3828
|
+
about: {
|
|
3829
|
+
title: "Early progress report",
|
|
3830
|
+
topic: '`.claude/skills/workflow-commands/progress-watcher.md` \u2192 "Progress while the run is quiet"',
|
|
3831
|
+
fires: "a `Bash` whose only purpose is waiting (`sleep` alone, or `echo` / `:` / `date`) while a timer is live or before the interval. Every occurrence",
|
|
3832
|
+
quiet: "no hand-made wait timer \u2014 `run:progress` holds the one clock"
|
|
3833
|
+
}
|
|
3834
|
+
},
|
|
3835
|
+
// Before `run-tail`: whether to commit at all is decided before how to push.
|
|
3836
|
+
test_declared_commit.ROW,
|
|
3837
|
+
// Before `run-tail`, so a lane child goes straight to `ship --detach` instead of through both.
|
|
3838
|
+
lane_background.ROW,
|
|
3839
|
+
run_tail_rule.ROW,
|
|
3840
|
+
// Reads the lane name and the cut record. Once per run, not `decide`: taking the cut ends the
|
|
3841
|
+
// process, and the verdicts that leave a run at the gate all need the reissue through. `reaches`
|
|
3842
|
+
// tells the cutting run from the resumed one by the run's calls. Overlaps `piped-verification`
|
|
3843
|
+
// safely: that row speaks first and this one delivers on the reissue.
|
|
3844
|
+
{
|
|
3845
|
+
id: "pre-gate-cut",
|
|
3846
|
+
is_trigger: on_bash_command(pre_gate_cut.is_uncut_gate_in),
|
|
3847
|
+
reason: pre_gate_cut.PRE_GATE_CUT_REASON,
|
|
3848
|
+
keeps: on_bash_command(pre_gate_cut.takes_the_cut),
|
|
3849
|
+
reaches: pre_gate_cut.reaches_the_pre_gate_boundary,
|
|
3850
|
+
about: {
|
|
3851
|
+
title: "The cut before the gate",
|
|
3852
|
+
topic: "`.claude/skills/workflow-commands/pre-gate-cut.md`",
|
|
3853
|
+
fires: "`pnpm josh gate` (`josh ga`) in a lane work tree whose `JOSH_LANE_CHILD` names the Issue and has no cut record",
|
|
3854
|
+
quiet: "outside a lane, no marker (a person in the lane), or already cut"
|
|
3855
|
+
}
|
|
3856
|
+
},
|
|
3857
|
+
// A split child's park is refused before `lane-park` sees it.
|
|
3858
|
+
lane_split_park.ROW,
|
|
3859
|
+
lane_park.ROW,
|
|
3860
|
+
lane_interactive_ask.ROW,
|
|
3861
|
+
agent_model.ROW,
|
|
3862
|
+
investigator_foreground.ROW,
|
|
3863
|
+
reviewer_foreground.ROW,
|
|
3864
|
+
// After `agent-model`, its one overlap (an unnamed `general-purpose` on Fable): `model` fixes both.
|
|
3865
|
+
consultation_investigation.ROW,
|
|
3866
|
+
consultation_investigation.CODEX_ROW,
|
|
3867
|
+
consultation_handoff.ROW,
|
|
3868
|
+
lane_carry_conflict.ROW,
|
|
3869
|
+
lane_switch_main.ROW,
|
|
3870
|
+
// Before `timed-rerun` and `lane-direct-tool`, its overlaps (a timed or lane `vitest run`):
|
|
3871
|
+
// `test:related` replaces the direct run, so neither the record nor the reservation comes first. A lane
|
|
3872
|
+
// child's plain `vitest run` never reaches this order: `lane-direct-tool`'s rewrite runs before
|
|
3873
|
+
// judgment (`rule-rewrite.ts`), and this row is silent on the `reserved-run` it produces.
|
|
3874
|
+
direct_vitest_run.ROW,
|
|
3875
|
+
// Before `lane-direct-tool`, its one overlap (a timed tool in a lane): the record is the right first
|
|
3876
|
+
// act, and `lane-direct-tool` delivers on the reissue of a `time`-prefixed tool.
|
|
3877
|
+
timed_rerun.ROW,
|
|
3878
|
+
lane_direct_tool.ROW,
|
|
3879
|
+
lane_spilled_read.ROW,
|
|
3880
|
+
// Disjoint from `run-tail` by the `-y` flag.
|
|
3881
|
+
josh_git_bare.ROW,
|
|
3882
|
+
// The Bash-string gaps the deny glob cannot express, read by argv; `delivered-rules-bash.test.ts`
|
|
3883
|
+
// pins that no two rows claim one command.
|
|
3884
|
+
git_force.ROW,
|
|
3885
|
+
worktree_guard.ROW,
|
|
3886
|
+
...file_edit_rows.SHELL_ROWS,
|
|
3887
|
+
...permission_guards.ROWS,
|
|
3888
|
+
poll_loop.ROW,
|
|
3889
|
+
// After the safety rows: a harmless read is steered only once nothing on the line is refused.
|
|
3890
|
+
direct_git_status.ROW,
|
|
3891
|
+
// Before `rule-body`, its one overlap: the cut is the right first act, and `rule-body` delivers on
|
|
3892
|
+
// the reissue in the fresh process.
|
|
3893
|
+
implementation_cut.ROW,
|
|
3894
|
+
// Before `rule-body`, its one overlap (a whole-file `Write` of a rule document): the right tool is
|
|
3895
|
+
// chosen before the placement questions are asked of its text.
|
|
3896
|
+
file_edit_rows.REWRITE_ROW,
|
|
3897
|
+
delivered_rules_guard.call_only(rule_body_guard.ROW),
|
|
3898
|
+
...oracle_consulted.ROWS
|
|
3899
|
+
];
|
|
3900
|
+
var ALONE_IN_TURN = 1;
|
|
3901
|
+
function batches_the_turn(call, turn) {
|
|
3902
|
+
return time_batch_guard.is_guarded_call(call) && turn.length > ALONE_IN_TURN;
|
|
3903
|
+
}
|
|
3904
|
+
var BATCHING_RULE = {
|
|
3905
|
+
id: "batching",
|
|
3906
|
+
reason: time_batch_guard.REASON,
|
|
3907
|
+
keeps: batches_the_turn,
|
|
3908
|
+
reaches: time_batch_guard.is_guarded_call,
|
|
3909
|
+
about: {
|
|
3910
|
+
title: "Turn batching",
|
|
3911
|
+
topic: "`turn-batching.md`",
|
|
3912
|
+
fires: "the `Bash` / `Edit` / `Read` after three single-call turns in a row; fires **again** every three more such turns",
|
|
3913
|
+
quiet: "calls per round trip above the floor"
|
|
3914
|
+
}
|
|
3915
|
+
};
|
|
3916
|
+
function is_investigation_read(call) {
|
|
3917
|
+
return investigation_reads.is_refusable_call(call);
|
|
3918
|
+
}
|
|
3919
|
+
var INVESTIGATION_RULE = {
|
|
3920
|
+
id: "investigation",
|
|
3921
|
+
reason: investigation_reads.REASON,
|
|
3922
|
+
reaches: is_investigation_read,
|
|
3923
|
+
about: {
|
|
3924
|
+
title: "Investigation delegation threshold",
|
|
3925
|
+
topic: "`delegation.md`",
|
|
3926
|
+
fires: "the `Read` / `Bash` that makes the third read of a file the run will not edit; fires **again** once reads pile up to the threshold again",
|
|
3927
|
+
quiet: "reads under the threshold \u2014 nothing to delegate yet"
|
|
3928
|
+
}
|
|
3929
|
+
};
|
|
3930
|
+
var MEASURED_RULES = [
|
|
3931
|
+
...DELIVERED_RULES,
|
|
3932
|
+
BATCHING_RULE,
|
|
3933
|
+
INVESTIGATION_RULE,
|
|
3934
|
+
// Delivered by `stop:guard`; listed here so `rule:value` counts its refusals.
|
|
3935
|
+
concise_reply.ROW
|
|
3936
|
+
];
|
|
3937
|
+
var { SWITCH_ENV_KEY: SWITCH_ENV_KEY4, guard_of: guard_of2 } = delivered_rules_guard;
|
|
3938
|
+
var GUARDS = new Map(DELIVERED_RULES.map((rule) => [rule.id, guard_of2(rule)]));
|
|
3939
|
+
function delivery_path(rule_id, transcript_path) {
|
|
3940
|
+
return GUARDS.get(rule_id)?.refusal_path(transcript_path) ?? "";
|
|
3941
|
+
}
|
|
3942
|
+
function delivery(raw_payload, now_ms = Date.now(), context = hook_context.from_process()) {
|
|
3943
|
+
for (const guard of GUARDS.values()) {
|
|
3944
|
+
const reason2 = guard.refusal(raw_payload, now_ms, context);
|
|
3945
|
+
if (reason2 !== void 0) return reason2;
|
|
3946
|
+
}
|
|
3947
|
+
return void 0;
|
|
3948
|
+
}
|
|
3949
|
+
function is_enabled3() {
|
|
3950
|
+
return hook_decision.is_switch_enabled(SWITCH_ENV_KEY4);
|
|
3951
|
+
}
|
|
3952
|
+
var delivered_rules = {
|
|
3953
|
+
DELIVERED_RULES,
|
|
3954
|
+
EARLY_HEARTBEAT_REASON: early_heartbeat.EARLY_HEARTBEAT_REASON,
|
|
3955
|
+
FILE_BODY_REASON: file_edit_rows.FILE_BODY_REASON,
|
|
3956
|
+
DIRECT_FILING_REASON: direct_filing.DIRECT_FILING_REASON,
|
|
3957
|
+
GIT_FORCE_REASON: git_force.GIT_FORCE_REASON,
|
|
3958
|
+
ISSUE_COMMENTS_REASON: issue_comments.ISSUE_COMMENTS_REASON,
|
|
3959
|
+
LANE_INTERACTIVE_ASK_REASON: lane_interactive_ask.LANE_INTERACTIVE_ASK_REASON,
|
|
3960
|
+
LANE_PARK_REASON: lane_park.LANE_PARK_REASON,
|
|
3961
|
+
MEASURED_RULES,
|
|
3962
|
+
PIPED_VERIFICATION_REASON: piped_verification.PIPED_VERIFICATION_REASON,
|
|
3963
|
+
POLL_LOOP_REASON: poll_loop.POLL_LOOP_REASON,
|
|
3964
|
+
PRE_GATE_CUT_REASON: pre_gate_cut.PRE_GATE_CUT_REASON,
|
|
3965
|
+
PRETOOL_GUARD,
|
|
3966
|
+
RAW_FIELD_BODY_REASON: raw_field_body.RAW_FIELD_BODY_REASON,
|
|
3967
|
+
RUN_TAIL_REASON: run_tail_rule.RUN_TAIL_REASON,
|
|
3968
|
+
SHELL_BODY_REASON: SHELL_BODY_REASON2,
|
|
3969
|
+
SWITCH_ENV_KEY: SWITCH_ENV_KEY4,
|
|
3970
|
+
THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
|
|
3971
|
+
WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
|
|
3972
|
+
delivery,
|
|
3973
|
+
delivery_path,
|
|
3974
|
+
is_body_only_issue_read: issue_comments.is_body_only_issue_read,
|
|
3975
|
+
is_enabled: is_enabled3,
|
|
3976
|
+
is_issue_filing
|
|
3977
|
+
};
|
|
3978
|
+
|
|
3979
|
+
export {
|
|
3980
|
+
investigation_outcome,
|
|
3981
|
+
batch_outcome,
|
|
3982
|
+
nested_commands,
|
|
3983
|
+
piped_verification,
|
|
3984
|
+
delivered_rules
|
|
3985
|
+
};
|