safeword 0.78.2 → 0.78.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  2. package/codex-plugin/hooks.json +5 -5
  3. package/codex-plugin/skills/bdd/references/SCENARIOS.md +3 -0
  4. package/codex-plugin/skills/bdd/references/TDD.md +11 -5
  5. package/codex-plugin/skills/closeout/SKILL.md +24 -16
  6. package/codex-plugin/skills/finish-review/SKILL.md +4 -3
  7. package/codex-plugin/skills/lint/SKILL.md +1 -1
  8. package/codex-plugin/skills/review-spec/SKILL.md +5 -1
  9. package/codex-plugin/skills/self-review/SKILL.md +2 -2
  10. package/codex-plugin/skills/ticket-system/SKILL.md +3 -0
  11. package/dist/{architecture-N5ZC4CDU.js → architecture-RQ377ZER.js} +6 -6
  12. package/dist/{architecture-document-UBA5UT4J.js → architecture-document-KS3CLSYN.js} +6 -6
  13. package/dist/{architecture-monorepo-E4JUYJGV.js → architecture-monorepo-NJWWU65G.js} +4 -4
  14. package/dist/{boundary-HLJILY7R.js → boundary-OPTC2RVJ.js} +6 -6
  15. package/dist/{chunk-NOTBZCKG.js → chunk-2XR7J5WL.js} +93 -42
  16. package/dist/chunk-2XR7J5WL.js.map +1 -0
  17. package/dist/{chunk-4CZS4YJV.js → chunk-3OKZ3X4O.js} +3 -3
  18. package/dist/{chunk-J7UDLLTK.js → chunk-76XHADUG.js} +39 -25
  19. package/dist/chunk-76XHADUG.js.map +1 -0
  20. package/dist/{chunk-4CKCRZDO.js → chunk-7ULZND56.js} +2 -2
  21. package/dist/{chunk-34NCOTP5.js → chunk-7XELS325.js} +2 -2
  22. package/dist/{chunk-USPVNZ63.js → chunk-BZWO3BVD.js} +11 -7
  23. package/dist/chunk-BZWO3BVD.js.map +1 -0
  24. package/dist/{chunk-GZ36UZJG.js → chunk-CFME47IY.js} +6 -6
  25. package/dist/{chunk-SME5SU3O.js → chunk-CNBZ3S5U.js} +255 -45
  26. package/dist/chunk-CNBZ3S5U.js.map +1 -0
  27. package/dist/{chunk-AV5LTEME.js → chunk-CRGPYC35.js} +8 -6
  28. package/dist/{chunk-AV5LTEME.js.map → chunk-CRGPYC35.js.map} +1 -1
  29. package/dist/{chunk-BQTC6YGN.js → chunk-D5ENZR6N.js} +2 -2
  30. package/dist/{chunk-V63TSN3C.js → chunk-FOYGZ4MK.js} +31 -10
  31. package/dist/chunk-FOYGZ4MK.js.map +1 -0
  32. package/dist/{chunk-ZRV7TOCX.js → chunk-G5AFQXXW.js} +2 -2
  33. package/dist/{chunk-S2T356DA.js → chunk-HF3DXZEA.js} +7 -10
  34. package/dist/chunk-HF3DXZEA.js.map +1 -0
  35. package/dist/{chunk-NSZGI7HK.js → chunk-HNCUIXDY.js} +9 -9
  36. package/dist/{chunk-NSZGI7HK.js.map → chunk-HNCUIXDY.js.map} +1 -1
  37. package/dist/{chunk-KXHVFCDT.js → chunk-IDRLACDS.js} +145 -19
  38. package/dist/chunk-IDRLACDS.js.map +1 -0
  39. package/dist/{chunk-BSOUMHMQ.js → chunk-IPTM5VE7.js} +101 -1094
  40. package/dist/chunk-IPTM5VE7.js.map +1 -0
  41. package/dist/chunk-JBOLNV2L.js +21 -0
  42. package/dist/chunk-JBOLNV2L.js.map +1 -0
  43. package/dist/chunk-JSR3HXFT.js +1231 -0
  44. package/dist/chunk-JSR3HXFT.js.map +1 -0
  45. package/dist/{chunk-I4KBHRZW.js → chunk-LH2FAQTI.js} +4 -4
  46. package/dist/{chunk-WVDRXD67.js → chunk-MLBZFB5A.js} +1 -1
  47. package/dist/chunk-MLBZFB5A.js.map +1 -0
  48. package/dist/{chunk-KFVEPQ7X.js → chunk-MVFHTUQ3.js} +2 -2
  49. package/dist/{chunk-BAVDDAN3.js → chunk-MZKTOX6X.js} +7 -7
  50. package/dist/{chunk-TJ627SKI.js → chunk-O3GZ77F7.js} +15 -1
  51. package/dist/chunk-O3GZ77F7.js.map +1 -0
  52. package/dist/{chunk-Q5KLE6ZF.js → chunk-OKPRQGFG.js} +2 -2
  53. package/dist/{chunk-QUDIOBHP.js → chunk-PD7ZXRHV.js} +139 -18
  54. package/dist/chunk-PD7ZXRHV.js.map +1 -0
  55. package/dist/chunk-QAHJSG53.js +30 -0
  56. package/dist/chunk-QAHJSG53.js.map +1 -0
  57. package/dist/{chunk-QTG2EXFT.js → chunk-QLOUSTQE.js} +2 -2
  58. package/dist/{chunk-QTG2EXFT.js.map → chunk-QLOUSTQE.js.map} +1 -1
  59. package/dist/{chunk-3SOJBMM6.js → chunk-R6BSRNU4.js} +6 -6
  60. package/dist/{chunk-3SOJBMM6.js.map → chunk-R6BSRNU4.js.map} +1 -1
  61. package/dist/{chunk-664YNAMR.js → chunk-RTFPKJJV.js} +5 -5
  62. package/dist/{chunk-GKR44BCH.js → chunk-RVDXBHUG.js} +2 -2
  63. package/dist/chunk-THLEVFEX.js +22 -0
  64. package/dist/chunk-THLEVFEX.js.map +1 -0
  65. package/dist/{chunk-A7Z27COA.js → chunk-VFWAZLEF.js} +3 -3
  66. package/dist/{chunk-3EJNDIAO.js → chunk-VZ5B2SIO.js} +2 -2
  67. package/dist/chunk-WAMQTZAH.js +19 -0
  68. package/dist/chunk-WAMQTZAH.js.map +1 -0
  69. package/dist/{chunk-FDHJ63FZ.js → chunk-WVA4Q6BX.js} +1 -1
  70. package/dist/{chunk-FDHJ63FZ.js.map → chunk-WVA4Q6BX.js.map} +1 -1
  71. package/dist/{cleanup-X556LNWM.js → cleanup-ELVHZPRC.js} +9 -9
  72. package/dist/{cleanup-command-RV2Q7G72.js → cleanup-command-5BY4FWAT.js} +12 -12
  73. package/dist/cli.js +143 -108
  74. package/dist/cli.js.map +1 -1
  75. package/dist/{clients-HP23HRN2.js → clients-WRPBRO6V.js} +2 -2
  76. package/dist/{codex-bootstrap-MGY2D6HU.js → codex-bootstrap-GYG7N4XU.js} +77 -34
  77. package/dist/codex-bootstrap-GYG7N4XU.js.map +1 -0
  78. package/dist/{codex-hook-IPX3PDXX.js → codex-hook-7ILIPLYM.js} +56 -48
  79. package/dist/codex-hook-7ILIPLYM.js.map +1 -0
  80. package/dist/{codify-J5ZLJ3HK.js → codify-HSDVRZFI.js} +11 -8
  81. package/dist/codify-HSDVRZFI.js.map +1 -0
  82. package/dist/{commands-L4AINARC.js → commands-X4KBCMHS.js} +115 -60
  83. package/dist/commands-X4KBCMHS.js.map +1 -0
  84. package/dist/{config-LUSJAK6B.js → config-TXJRCN3W.js} +2 -2
  85. package/dist/{configured-paths-SPQ5XWOM.js → configured-paths-4CRHLBIF.js} +4 -4
  86. package/dist/contract-B7NDKO5B.js +8 -0
  87. package/dist/{coordinator-SA75UB3C.js → coordinator-ZMD3467F.js} +231 -144
  88. package/dist/coordinator-ZMD3467F.js.map +1 -0
  89. package/dist/corpus-PUBXCCSN.js +15 -0
  90. package/dist/{doctor-DSC36R4U.js → doctor-RPXETRSJ.js} +18 -18
  91. package/dist/{feature-directories-VOHVV26X.js → feature-directories-ASNDEM7J.js} +6 -6
  92. package/dist/{finalization-7SMLMVLV.js → finalization-BGPOEBAL.js} +2 -2
  93. package/dist/{gh-cli-ZMO7D2KE.js → gh-cli-OTX7PORT.js} +2 -2
  94. package/dist/{github-rest-KGKS4V2H.js → github-rest-JCW32377.js} +2 -2
  95. package/dist/index.js +2 -2
  96. package/dist/{job-CDADLJDF.js → job-NXXG4U5K.js} +20 -21
  97. package/dist/job-NXXG4U5K.js.map +1 -0
  98. package/dist/{learning-sync-6BTHM6ZC.js → learning-sync-2WIAOTW3.js} +4 -4
  99. package/dist/{legacy-global-guidance-2JL4Y5EB.js → legacy-global-guidance-WTDI53H7.js} +5 -5
  100. package/dist/{lint-gherkin-COSHVS6Z.js → lint-gherkin-LS3CHW3J.js} +7 -7
  101. package/dist/{operations-XVRQSLJG.js → operations-OZKEQCS4.js} +12 -12
  102. package/dist/{output-3LB2OEHN.js → output-ZYTEW6SC.js} +2 -2
  103. package/dist/{packet-5WT5M3OS.js → packet-7S6CZJQR.js} +2 -2
  104. package/dist/presets/typescript/index.js +2 -2
  105. package/dist/{profile-UMKQ5JVO.js → profile-SDFSBKH5.js} +7 -7
  106. package/dist/{prompt-GWYDFMLO.js → prompt-LYPODDC3.js} +2 -2
  107. package/dist/remove-P5JQNS5P.js +23 -0
  108. package/dist/{retro-IFSVU5KS.js → retro-EQK2Z2Z6.js} +8 -8
  109. package/dist/{retro-draft-spool-4S3TCHWL.js → retro-draft-spool-MDPBVM6J.js} +2 -2
  110. package/dist/{retro-extract-OSBFAXLU.js → retro-extract-CZMDD4WB.js} +2 -2
  111. package/dist/{review-pr-6S2352CK.js → review-pr-CWRD446M.js} +2 -2
  112. package/dist/{review-pr-publication-CGHCRRUI.js → review-pr-publication-AIX2U3VM.js} +2 -2
  113. package/dist/{run-5JESKXUR.js → run-T45X6Z6C.js} +4 -4
  114. package/dist/{self-report-VPHBPPSJ.js → self-report-V7PHKTS6.js} +2 -2
  115. package/dist/status-F5TFEIZ6.js +21 -0
  116. package/dist/{status-EX4L3ELO.js → status-S24PDFXB.js} +18 -18
  117. package/dist/{sync-config-O7J7IDGE.js → sync-config-EUVBPUUG.js} +5 -5
  118. package/dist/{sync-tracker-U2GYOFUO.js → sync-tracker-5FSIIAMS.js} +6 -6
  119. package/dist/{test-execution-6LIPMBIH.js → test-execution-HASVO6EL.js} +8 -7
  120. package/dist/{test-execution-6LIPMBIH.js.map → test-execution-HASVO6EL.js.map} +1 -1
  121. package/dist/{test-plan-VASHZXJ7.js → test-plan-EM3PVGIX.js} +7 -6
  122. package/dist/{test-plan-VASHZXJ7.js.map → test-plan-EM3PVGIX.js.map} +1 -1
  123. package/dist/{ticket-new-3YXDIWPC.js → ticket-new-H6ZNIRS4.js} +11 -11
  124. package/dist/ticket-new-H6ZNIRS4.js.map +1 -0
  125. package/dist/{ticket-sync-LLUBXZ2R.js → ticket-sync-7QRH6PVT.js} +5 -5
  126. package/dist/{tracker-map-HR4MBYG5.js → tracker-map-7AGCTWBZ.js} +2 -2
  127. package/dist/{tracker-sync-YDICZN66.js → tracker-sync-MLVYGFQJ.js} +2 -2
  128. package/package.json +1 -1
  129. package/templates/SAFEWORD.md +2 -2
  130. package/templates/commands/lint.md +1 -1
  131. package/templates/scripts/closeout-cleanup.ts +127 -31
  132. package/templates/skills/bdd/SCENARIOS.md +3 -0
  133. package/templates/skills/bdd/TDD.md +11 -5
  134. package/templates/skills/closeout/SKILL.md +25 -17
  135. package/templates/skills/finish-review/SKILL.md +4 -3
  136. package/templates/skills/lint/SKILL.md +1 -1
  137. package/templates/skills/review-spec/SKILL.md +5 -1
  138. package/templates/skills/self-review/SKILL.md +2 -2
  139. package/templates/skills/ticket-system/SKILL.md +3 -0
  140. package/dist/chunk-6S7CTKGC.js +0 -128
  141. package/dist/chunk-6S7CTKGC.js.map +0 -1
  142. package/dist/chunk-BSOUMHMQ.js.map +0 -1
  143. package/dist/chunk-J7UDLLTK.js.map +0 -1
  144. package/dist/chunk-KXHVFCDT.js.map +0 -1
  145. package/dist/chunk-NOTBZCKG.js.map +0 -1
  146. package/dist/chunk-QUDIOBHP.js.map +0 -1
  147. package/dist/chunk-S2T356DA.js.map +0 -1
  148. package/dist/chunk-SME5SU3O.js.map +0 -1
  149. package/dist/chunk-TJ627SKI.js.map +0 -1
  150. package/dist/chunk-USPVNZ63.js.map +0 -1
  151. package/dist/chunk-V63TSN3C.js.map +0 -1
  152. package/dist/chunk-WRUXO3WA.js +0 -48
  153. package/dist/chunk-WRUXO3WA.js.map +0 -1
  154. package/dist/chunk-WVDRXD67.js.map +0 -1
  155. package/dist/codex-bootstrap-MGY2D6HU.js.map +0 -1
  156. package/dist/codex-hook-IPX3PDXX.js.map +0 -1
  157. package/dist/codify-J5ZLJ3HK.js.map +0 -1
  158. package/dist/commands-L4AINARC.js.map +0 -1
  159. package/dist/contract-KEZY6YC3.js +0 -8
  160. package/dist/coordinator-SA75UB3C.js.map +0 -1
  161. package/dist/corpus-PPOBTF4J.js +0 -15
  162. package/dist/job-CDADLJDF.js.map +0 -1
  163. package/dist/remove-E6HGNHOU.js +0 -22
  164. package/dist/status-7VIGNTUI.js +0 -21
  165. package/dist/ticket-new-3YXDIWPC.js.map +0 -1
  166. /package/dist/{architecture-N5ZC4CDU.js.map → architecture-RQ377ZER.js.map} +0 -0
  167. /package/dist/{architecture-document-UBA5UT4J.js.map → architecture-document-KS3CLSYN.js.map} +0 -0
  168. /package/dist/{architecture-monorepo-E4JUYJGV.js.map → architecture-monorepo-NJWWU65G.js.map} +0 -0
  169. /package/dist/{boundary-HLJILY7R.js.map → boundary-OPTC2RVJ.js.map} +0 -0
  170. /package/dist/{chunk-4CZS4YJV.js.map → chunk-3OKZ3X4O.js.map} +0 -0
  171. /package/dist/{chunk-4CKCRZDO.js.map → chunk-7ULZND56.js.map} +0 -0
  172. /package/dist/{chunk-34NCOTP5.js.map → chunk-7XELS325.js.map} +0 -0
  173. /package/dist/{chunk-GZ36UZJG.js.map → chunk-CFME47IY.js.map} +0 -0
  174. /package/dist/{chunk-BQTC6YGN.js.map → chunk-D5ENZR6N.js.map} +0 -0
  175. /package/dist/{chunk-ZRV7TOCX.js.map → chunk-G5AFQXXW.js.map} +0 -0
  176. /package/dist/{chunk-I4KBHRZW.js.map → chunk-LH2FAQTI.js.map} +0 -0
  177. /package/dist/{chunk-KFVEPQ7X.js.map → chunk-MVFHTUQ3.js.map} +0 -0
  178. /package/dist/{chunk-BAVDDAN3.js.map → chunk-MZKTOX6X.js.map} +0 -0
  179. /package/dist/{chunk-Q5KLE6ZF.js.map → chunk-OKPRQGFG.js.map} +0 -0
  180. /package/dist/{chunk-664YNAMR.js.map → chunk-RTFPKJJV.js.map} +0 -0
  181. /package/dist/{chunk-GKR44BCH.js.map → chunk-RVDXBHUG.js.map} +0 -0
  182. /package/dist/{chunk-A7Z27COA.js.map → chunk-VFWAZLEF.js.map} +0 -0
  183. /package/dist/{chunk-3EJNDIAO.js.map → chunk-VZ5B2SIO.js.map} +0 -0
  184. /package/dist/{cleanup-X556LNWM.js.map → cleanup-ELVHZPRC.js.map} +0 -0
  185. /package/dist/{cleanup-command-RV2Q7G72.js.map → cleanup-command-5BY4FWAT.js.map} +0 -0
  186. /package/dist/{clients-HP23HRN2.js.map → clients-WRPBRO6V.js.map} +0 -0
  187. /package/dist/{config-LUSJAK6B.js.map → config-TXJRCN3W.js.map} +0 -0
  188. /package/dist/{configured-paths-SPQ5XWOM.js.map → configured-paths-4CRHLBIF.js.map} +0 -0
  189. /package/dist/{contract-KEZY6YC3.js.map → contract-B7NDKO5B.js.map} +0 -0
  190. /package/dist/{corpus-PPOBTF4J.js.map → corpus-PUBXCCSN.js.map} +0 -0
  191. /package/dist/{doctor-DSC36R4U.js.map → doctor-RPXETRSJ.js.map} +0 -0
  192. /package/dist/{feature-directories-VOHVV26X.js.map → feature-directories-ASNDEM7J.js.map} +0 -0
  193. /package/dist/{finalization-7SMLMVLV.js.map → finalization-BGPOEBAL.js.map} +0 -0
  194. /package/dist/{gh-cli-ZMO7D2KE.js.map → gh-cli-OTX7PORT.js.map} +0 -0
  195. /package/dist/{github-rest-KGKS4V2H.js.map → github-rest-JCW32377.js.map} +0 -0
  196. /package/dist/{learning-sync-6BTHM6ZC.js.map → learning-sync-2WIAOTW3.js.map} +0 -0
  197. /package/dist/{legacy-global-guidance-2JL4Y5EB.js.map → legacy-global-guidance-WTDI53H7.js.map} +0 -0
  198. /package/dist/{lint-gherkin-COSHVS6Z.js.map → lint-gherkin-LS3CHW3J.js.map} +0 -0
  199. /package/dist/{operations-XVRQSLJG.js.map → operations-OZKEQCS4.js.map} +0 -0
  200. /package/dist/{output-3LB2OEHN.js.map → output-ZYTEW6SC.js.map} +0 -0
  201. /package/dist/{packet-5WT5M3OS.js.map → packet-7S6CZJQR.js.map} +0 -0
  202. /package/dist/{profile-UMKQ5JVO.js.map → profile-SDFSBKH5.js.map} +0 -0
  203. /package/dist/{prompt-GWYDFMLO.js.map → prompt-LYPODDC3.js.map} +0 -0
  204. /package/dist/{remove-E6HGNHOU.js.map → remove-P5JQNS5P.js.map} +0 -0
  205. /package/dist/{retro-IFSVU5KS.js.map → retro-EQK2Z2Z6.js.map} +0 -0
  206. /package/dist/{retro-draft-spool-4S3TCHWL.js.map → retro-draft-spool-MDPBVM6J.js.map} +0 -0
  207. /package/dist/{retro-extract-OSBFAXLU.js.map → retro-extract-CZMDD4WB.js.map} +0 -0
  208. /package/dist/{review-pr-6S2352CK.js.map → review-pr-CWRD446M.js.map} +0 -0
  209. /package/dist/{review-pr-publication-CGHCRRUI.js.map → review-pr-publication-AIX2U3VM.js.map} +0 -0
  210. /package/dist/{run-5JESKXUR.js.map → run-T45X6Z6C.js.map} +0 -0
  211. /package/dist/{self-report-VPHBPPSJ.js.map → self-report-V7PHKTS6.js.map} +0 -0
  212. /package/dist/{status-7VIGNTUI.js.map → status-F5TFEIZ6.js.map} +0 -0
  213. /package/dist/{status-EX4L3ELO.js.map → status-S24PDFXB.js.map} +0 -0
  214. /package/dist/{sync-config-O7J7IDGE.js.map → sync-config-EUVBPUUG.js.map} +0 -0
  215. /package/dist/{sync-tracker-U2GYOFUO.js.map → sync-tracker-5FSIIAMS.js.map} +0 -0
  216. /package/dist/{ticket-sync-LLUBXZ2R.js.map → ticket-sync-7QRH6PVT.js.map} +0 -0
  217. /package/dist/{tracker-map-HR4MBYG5.js.map → tracker-map-7AGCTWBZ.js.map} +0 -0
  218. /package/dist/{tracker-sync-YDICZN66.js.map → tracker-sync-MLVYGFQJ.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/codex-plugin/legacy-authority.ts","../src/codex-plugin/legacy-command.ts","../src/codex-plugin/profile-proof.ts","../src/codex-plugin/host-process.ts"],"sourcesContent":["import { accessSync, constants, lstatSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { parse } from 'smol-toml';\n\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { CODEX_MIGRATION_SCHEMA } from './inventory.js';\nimport { legacyCommandIdentity } from './legacy-command.js';\n\ntype LegacyHook = { command?: unknown; type?: unknown };\ntype LegacyHookGroup = { hooks?: unknown };\ntype LegacyConfig = { hooks?: unknown };\n\nfunction regularFile(path: string): boolean {\n try {\n return lstatSync(path).isFile();\n } catch {\n return false;\n }\n}\n\nfunction eventCommands(config: LegacyConfig, eventName: string): string[] {\n if (typeof config.hooks !== 'object' || config.hooks === null) return [];\n const groups = (config.hooks as Record<string, unknown>)[eventName];\n if (!Array.isArray(groups)) return [];\n\n return groups.flatMap(group => {\n const hooks = (group as LegacyHookGroup).hooks;\n if (!Array.isArray(hooks)) return [];\n return hooks.flatMap(hook => {\n const candidate = hook as LegacyHook;\n return candidate.type === 'command' && typeof candidate.command === 'string'\n ? [candidate.command]\n : [];\n });\n });\n}\n\nfunction packageRunnerIsAvailable(cwd: string, environment: NodeJS.ProcessEnv): boolean {\n const runner = SAFEWORD_SCHEMA.codexMigration.packageRunner;\n const pathEntries = (environment.PATH ?? '').split(nodePath.delimiter).filter(Boolean);\n const extensions =\n process.platform === 'win32' ? (environment.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';') : [''];\n return pathEntries.some(entry =>\n extensions.some(extension => {\n const candidate = nodePath.resolve(cwd, entry, `${runner}${extension.toLowerCase()}`);\n try {\n accessSync(candidate, process.platform === 'win32' ? constants.F_OK : constants.X_OK);\n return regularFile(candidate);\n } catch {\n return false;\n }\n }),\n );\n}\n\nfunction commandIsViable(\n command: string,\n event: string,\n cwd: string,\n environment: NodeJS.ProcessEnv,\n): boolean {\n const identity = legacyCommandIdentity(command);\n if (identity?.event !== event) return false;\n if (identity.kind === 'package') {\n return (\n regularFile(nodePath.join(cwd, SAFEWORD_SCHEMA.codexMigration.projectMarker)) &&\n packageRunnerIsAvailable(cwd, environment)\n );\n }\n return (\n identity.kind === 'script' &&\n regularFile(nodePath.join(cwd, CODEX_MIGRATION_SCHEMA.paths.hookRuntimeRoot, identity.script))\n );\n}\n\nexport function legacyCodexEventIsViable(\n cwd: string,\n event: string,\n environment: NodeJS.ProcessEnv = process.env,\n): boolean {\n const eventName = SAFEWORD_SCHEMA.codexMigration.hookEventNames[event];\n if (eventName === undefined) return false;\n\n const configPath = nodePath.join(cwd, CODEX_MIGRATION_SCHEMA.paths.config);\n if (!regularFile(configPath)) return false;\n\n let config: LegacyConfig;\n try {\n config = parse(readFileSync(configPath, 'utf8'));\n } catch {\n return false;\n }\n\n return eventCommands(config, eventName).some(command =>\n commandIsViable(command, event, cwd, environment),\n );\n}\n","import { CODEX_MIGRATION_SCHEMA } from './inventory.js';\n\nexport type LegacyCommandIdentity =\n | {\n readonly kind: 'package';\n readonly event: string;\n }\n | {\n readonly kind: 'script';\n readonly event: string;\n readonly script: string;\n };\n\nfunction commandParts(command: string): string[] {\n return command\n .trim()\n .split(' ')\n .filter(part => part !== '');\n}\n\nfunction isHistoricalPackagePrefix(parts: readonly string[]): boolean {\n return (\n parts[0] === CODEX_MIGRATION_SCHEMA.packageRunner &&\n parts[1] === '--yes' &&\n parts[2] === 'safeword'\n );\n}\n\nfunction packagedHookEvent(hook: readonly string[]): string | undefined {\n if (\n hook.length === 3 &&\n hook[0] === 'hook' &&\n hook[1] === 'codex' &&\n CODEX_MIGRATION_SCHEMA.hookEvents.includes(hook[2] ?? '')\n ) {\n return hook[2];\n }\n if (\n hook.length === 2 &&\n hook[0] === 'codex-hook' &&\n CODEX_MIGRATION_SCHEMA.hookEvents.includes(hook[1] ?? '')\n ) {\n return hook[1];\n }\n return undefined;\n}\n\nfunction packageIdentity(command: string): LegacyCommandIdentity | undefined {\n const parts = commandParts(command);\n if (!isHistoricalPackagePrefix(parts)) return undefined;\n const event = packagedHookEvent(parts.slice(3));\n return event === undefined ? undefined : { kind: 'package', event };\n}\n\nfunction scriptIdentity(command: string): LegacyCommandIdentity | undefined {\n const prefix = CODEX_MIGRATION_SCHEMA.hookScriptPrefix;\n if (!command.startsWith(prefix)) return undefined;\n const remainder = command.slice(prefix.length);\n const quote = remainder.indexOf('\"');\n if (quote === -1) return undefined;\n const script = remainder.slice(0, quote);\n const event = CODEX_MIGRATION_SCHEMA.hookScriptEvents[script];\n if (event === undefined) return undefined;\n const arguments_ = remainder.slice(quote + 1);\n const supportedArguments =\n arguments_ === '' ||\n (script === 'session-safeword-context.ts' && arguments_ === ' --agent=codex');\n return supportedArguments ? { kind: 'script', event, script } : undefined;\n}\n\nexport function legacyCommandIdentity(command: string): LegacyCommandIdentity | undefined {\n return packageIdentity(command) ?? scriptIdentity(command);\n}\n","/* eslint-disable unicorn/no-null -- versioned JSON uses explicit null for unavailable values */\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport {\n closeSync,\n constants as fsConstants,\n existsSync,\n fstatSync,\n openSync,\n readFileSync,\n readSync,\n realpathSync,\n rmSync,\n type Stats,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport nodePath from 'node:path';\n\nimport { SAFEWORD_SCHEMA } from '../schema.js';\nimport { writeDurableFile } from './durable-write.js';\nimport {\n type CodexHostProcessIdentity,\n type CodexHostProcessObservation,\n observeCodexHostProcesses,\n observeRunningCodexHosts,\n sameCodexHost,\n} from './host-process.js';\n\nexport type { CodexHostProcessIdentity } from './host-process.js';\n\nexport interface CodexPluginIdentity {\n plugin_version: string;\n manifest_sha256: string;\n}\n\ninterface CodexHookProofV1 {\n schema_version: 1;\n event: CodexPluginHookEvent;\n plugin_version: string;\n manifest_sha256: string;\n recorded_at: string;\n}\n\nexport interface CodexHookProofV2 {\n schema_version: 2;\n event: CodexPluginHookEvent;\n plugin_version: string;\n manifest_sha256: string;\n activation_id: string | null;\n recorded_at: string;\n}\n\nexport interface CodexHookProofV3 extends CodexPluginIdentity {\n schema_version: 3;\n event: CodexPluginHookEvent;\n activation_id: string | null;\n project_directory: string;\n session_id: string;\n recorded_at: string;\n}\n\ninterface CodexSessionProofV1 extends CodexPluginIdentity {\n schema_version: 1;\n project_directory: string;\n session_id: string;\n recorded_at: string;\n}\n\ninterface CodexActivationMarkerV1 {\n schema_version: 1;\n plugin_version: string;\n manifest_sha256: string;\n}\n\nexport interface CodexActivationMarkerV2 {\n schema_version: 2;\n plugin_version: string;\n manifest_sha256: string;\n activation_id: string;\n installed_at: string;\n host_observation: 'observed' | 'unavailable';\n active_hosts: CodexHostProcessIdentity[];\n}\n\ninterface CodexActivationReceiptV1 {\n schema_version: 1;\n plugin_version: string;\n manifest_sha256: string;\n activation_id: string;\n activated_at: string;\n host: CodexHostProcessIdentity;\n}\n\nexport const CODEX_PLUGIN_HOOK_EVENTS = [\n 'session-start',\n 'pre-tool-use',\n 'post-tool-use',\n 'user-prompt-submit',\n 'stop',\n] as const;\nexport type CodexPluginHookEvent = (typeof CODEX_PLUGIN_HOOK_EVENTS)[number];\n\nexport interface CodexExactHookAuthorization extends CodexPluginIdentity {\n schema_version: 1;\n kind: 'current-exact-native-hook-proof';\n project_directory: string;\n session_id: string;\n activation_id: string | null;\n events: CodexPluginHookEvent[];\n hook_outcome: 'successful';\n trust_basis: 'host-invoked-plugin-hook';\n verified_at: string;\n}\n\ntype CodexHookProofStatus = 'current' | 'partial' | 'missing' | 'stale' | 'malformed';\n\nexport interface CodexHookProofObservation {\n status: CodexHookProofStatus;\n plugin_version: string | null;\n manifest_sha256: string | null;\n recorded_at: string | null;\n activation_id: string | null;\n events: readonly CodexPluginHookEvent[];\n missing_events: readonly CodexPluginHookEvent[];\n}\n\nfunction codexProfileDirectory(environment: NodeJS.ProcessEnv = process.env): string {\n return environment.CODEX_HOME ?? nodePath.join(homedir(), '.codex');\n}\n\nexport function codexProofPath(\n environment: NodeJS.ProcessEnv = process.env,\n event: CodexPluginHookEvent = 'session-start',\n): string {\n return nodePath.join(\n codexProfileDirectory(environment),\n 'safeword/hook-proof-v2',\n `${event}.json`,\n );\n}\n\nfunction canonicalProjectDirectory(projectDirectory: string): string {\n try {\n return realpathSync(projectDirectory);\n } catch {\n return nodePath.resolve(projectDirectory);\n }\n}\n\nfunction codexSessionProofPath(\n projectDirectory: string,\n sessionId: string,\n environment: NodeJS.ProcessEnv,\n): string {\n const project = canonicalProjectDirectory(projectDirectory);\n const projectDigest = createHash('sha256').update(project).digest('hex');\n const sessionDigest = createHash('sha256').update(sessionId).digest('hex');\n return nodePath.join(\n codexProfileDirectory(environment),\n 'safeword/session-proof-v1',\n projectDigest,\n `${sessionDigest}.json`,\n );\n}\n\nfunction legacyCodexActivationMarkerPath(environment: NodeJS.ProcessEnv = process.env): string {\n return nodePath.join(codexProfileDirectory(environment), 'safeword/activation-pending-v1.json');\n}\n\nfunction codexActivationMarkerPath(environment: NodeJS.ProcessEnv = process.env): string {\n return nodePath.join(codexProfileDirectory(environment), 'safeword/activation-pending-v2.json');\n}\n\nfunction codexActivationReceiptPath(environment: NodeJS.ProcessEnv = process.env): string {\n return nodePath.join(codexProfileDirectory(environment), 'safeword/activation-current-v1.json');\n}\n\nfunction legacyCodexRestartMarkerPath(environment: NodeJS.ProcessEnv = process.env): string {\n return nodePath.join(codexProfileDirectory(environment), 'safeword/restart-pending-v1.json');\n}\n\nexport function codexActivationIsPending(environment: NodeJS.ProcessEnv = process.env): boolean {\n const identity = currentCodexPluginIdentity();\n return (\n // The canonical marker is written atomically. If it exists but cannot be\n // parsed or matched, fail closed instead of accepting unbound hook proof.\n existsSync(codexActivationMarkerPath(environment)) ||\n [legacyCodexActivationMarkerPath(environment), legacyCodexRestartMarkerPath(environment)].some(\n path => legacyActivationMarkerMatches(path, identity),\n )\n );\n}\n\nfunction packagedHookManifestPath(): string {\n const candidates = [\n nodePath.resolve(import.meta.dirname, '../codex-plugin/hooks.json'),\n nodePath.resolve(import.meta.dirname, '../../codex-plugin/hooks.json'),\n ];\n const manifest = candidates.find(candidate => existsSync(candidate));\n if (manifest === undefined) {\n throw new Error('Could not locate the packaged Codex hook manifest.');\n }\n return manifest;\n}\n\nexport function currentCodexPluginIdentity(): CodexPluginIdentity {\n const manifest = readFileSync(packagedHookManifestPath());\n return {\n plugin_version: SAFEWORD_SCHEMA.version,\n manifest_sha256: createHash('sha256').update(manifest).digest('hex'),\n };\n}\n\nfunction matchesCodexPluginIdentity(\n value: { plugin_version?: unknown; manifest_sha256?: unknown },\n identity: CodexPluginIdentity,\n): boolean {\n return (\n value.plugin_version === identity.plugin_version &&\n value.manifest_sha256 === identity.manifest_sha256\n );\n}\n\nexport function writeCodexActivationMarker(\n environment: NodeJS.ProcessEnv = process.env,\n now = new Date(),\n options: {\n activationId?: string;\n activeHosts?: CodexHostProcessIdentity[] | null;\n } = {},\n): CodexActivationMarkerV2 {\n const path = codexActivationMarkerPath(environment);\n const activeHosts =\n options.activeHosts === undefined ? observeRunningCodexHosts() : options.activeHosts;\n const marker: CodexActivationMarkerV2 = {\n schema_version: 2,\n ...currentCodexPluginIdentity(),\n activation_id: options.activationId ?? randomUUID(),\n installed_at: now.toISOString(),\n host_observation: activeHosts === null ? 'unavailable' : 'observed',\n active_hosts: activeHosts ?? [],\n };\n writeAtomicJson(path, marker);\n // A new installation invalidates every earlier execution proof. The v2\n // marker is the only authority until SessionStart proves a different Codex\n // app-server loaded the installed plugin catalogue.\n for (const event of CODEX_PLUGIN_HOOK_EVENTS)\n rmSync(codexProofPath(environment, event), { force: true });\n rmSync(nodePath.join(codexProfileDirectory(environment), 'safeword/hook-proof-v1'), {\n recursive: true,\n force: true,\n });\n rmSync(nodePath.join(codexProfileDirectory(environment), 'safeword/session-proof-v1'), {\n recursive: true,\n force: true,\n });\n rmSync(codexActivationReceiptPath(environment), { force: true });\n rmSync(legacyCodexActivationMarkerPath(environment), { force: true });\n rmSync(legacyCodexRestartMarkerPath(environment), { force: true });\n return marker;\n}\n\nfunction hostObservationForOverride(\n current: CodexHostProcessIdentity | null = null,\n): CodexHostProcessObservation {\n return {\n available: true,\n current,\n running: current === null ? [] : [current],\n };\n}\n\nfunction activationReceiptForRestart(\n marker: CodexActivationMarkerV2,\n hostObservation: CodexHostProcessObservation,\n identity: CodexPluginIdentity,\n now: Date,\n): CodexActivationReceiptV1 | null {\n const currentHost = hostObservation.current;\n if (\n !hostObservation.available ||\n currentHost === null ||\n marker.host_observation !== 'observed' ||\n now.getTime() < Date.parse(marker.installed_at) ||\n marker.active_hosts.some(installedHost =>\n hostObservation.running.some(runningHost => sameCodexHost(installedHost, runningHost)),\n )\n ) {\n return null;\n }\n return {\n schema_version: 1,\n ...identity,\n activation_id: marker.activation_id,\n activated_at: now.toISOString(),\n host: currentHost,\n };\n}\n\n// eslint-disable-next-line complexity -- coordinates optional host overrides with durable proof writes\nexport function recordCodexHookProof(\n event: CodexPluginHookEvent,\n environment: NodeJS.ProcessEnv = process.env,\n now = new Date(),\n writeOptions: {\n beforeRename?: () => void;\n currentHost?: CodexHostProcessIdentity | null;\n hostObservation?: CodexHostProcessObservation;\n projectDirectory?: string;\n sessionId?: string;\n } = {},\n): CodexHookProofV2 | CodexHookProofV3 {\n const identity = currentCodexPluginIdentity();\n const marker = readActivationMarkerV2(environment, identity);\n let receipt = readActivationReceipt(environment, identity);\n if (event === 'session-start' && marker !== null) {\n const hostObservation =\n writeOptions.hostObservation ??\n ('currentHost' in writeOptions\n ? hostObservationForOverride(writeOptions.currentHost)\n : observeCodexHostProcesses());\n const restartedReceipt = activationReceiptForRestart(marker, hostObservation, identity, now);\n if (restartedReceipt !== null) {\n receipt = restartedReceipt;\n writeAtomicJson(codexActivationReceiptPath(environment), receipt);\n rmSync(codexActivationMarkerPath(environment), { force: true });\n }\n }\n const projectDirectory =\n writeOptions.projectDirectory === undefined\n ? undefined\n : canonicalProjectDirectory(writeOptions.projectDirectory);\n const sessionId = writeOptions.sessionId?.trim();\n const proof: CodexHookProofV2 | CodexHookProofV3 = {\n schema_version: projectDirectory !== undefined && sessionId ? 3 : 2,\n event,\n ...identity,\n activation_id: receipt?.activation_id ?? marker?.activation_id ?? null,\n ...(projectDirectory !== undefined &&\n sessionId && { project_directory: projectDirectory, session_id: sessionId }),\n recorded_at: now.toISOString(),\n } as CodexHookProofV2 | CodexHookProofV3;\n writeAtomicJson(codexProofPath(environment, event), proof, writeOptions);\n if (event === 'session-start' && projectDirectory !== undefined && sessionId) {\n const sessionProof: CodexSessionProofV1 = {\n schema_version: 1,\n ...identity,\n project_directory: projectDirectory,\n session_id: sessionId,\n recorded_at: now.toISOString(),\n };\n writeAtomicJson(codexSessionProofPath(projectDirectory, sessionId, environment), sessionProof);\n }\n return proof;\n}\n\nexport function codexSessionProofIsCurrent(\n projectDirectory: string,\n sessionId: string,\n environment: NodeJS.ProcessEnv = process.env,\n): boolean {\n const canonicalProject = canonicalProjectDirectory(projectDirectory);\n try {\n const value = JSON.parse(\n readFileSync(codexSessionProofPath(canonicalProject, sessionId, environment), 'utf8'),\n ) as Partial<CodexSessionProofV1>;\n return (\n value.schema_version === 1 &&\n value.project_directory === canonicalProject &&\n value.session_id === sessionId &&\n typeof value.recorded_at === 'string' &&\n !Number.isNaN(Date.parse(value.recorded_at)) &&\n matchesCodexPluginIdentity(value, currentCodexPluginIdentity())\n );\n } catch {\n return false;\n }\n}\n\nfunction writeAtomicJson(\n path: string,\n value: unknown,\n options: { beforeRename?: () => void } = {},\n): void {\n writeDurableFile(path, `${JSON.stringify(value)}\\n`, {\n mode: 0o600,\n beforeRename: options.beforeRename,\n });\n}\n\nfunction legacyActivationMarkerMatches(path: string, identity: CodexPluginIdentity): boolean {\n if (!existsSync(path)) return false;\n try {\n const marker = JSON.parse(readFileSync(path, 'utf8')) as Partial<CodexActivationMarkerV1>;\n return marker.schema_version === 1 && matchesCodexPluginIdentity(marker, identity);\n } catch {\n return false;\n }\n}\n\nfunction readActivationMarkerV2(\n environment: NodeJS.ProcessEnv,\n identity: CodexPluginIdentity,\n): CodexActivationMarkerV2 | null {\n return readIdentityBoundJson(\n codexActivationMarkerPath(environment),\n identity,\n isActivationMarkerV2,\n );\n}\n\nfunction readActivationReceipt(\n environment: NodeJS.ProcessEnv,\n identity: CodexPluginIdentity,\n): CodexActivationReceiptV1 | null {\n return readIdentityBoundJson(\n codexActivationReceiptPath(environment),\n identity,\n isActivationReceiptV1,\n );\n}\n\nfunction readIdentityBoundJson<T extends CodexPluginIdentity>(\n path: string,\n identity: CodexPluginIdentity,\n validator: (value: unknown) => value is T,\n): T | null {\n if (!existsSync(path)) return null;\n try {\n const value = JSON.parse(readFileSync(path, 'utf8')) as unknown;\n return validator(value) && matchesCodexPluginIdentity(value, identity) ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction readEventProof(\n path: string,\n expectedEvent: CodexPluginHookEvent,\n): CodexHookProofV1 | CodexHookProofV2 | CodexHookProofV3 | null {\n try {\n const proof = readTrustedHookProofJson(path);\n return isCodexHookProof(proof) && proof.event === expectedEvent ? proof : null;\n } catch {\n return null;\n }\n}\n\nconst MAX_CODEX_HOOK_PROOF_BYTES = 64 * 1024;\n\nfunction assertTrustedHookProofMetadata(metadata: Stats): void {\n const ownerMatches = process.getuid === undefined || metadata.uid === process.getuid();\n if (\n !metadata.isFile() ||\n metadata.nlink !== 1 ||\n !ownerMatches ||\n (metadata.mode & 0o077) !== 0 ||\n metadata.size > MAX_CODEX_HOOK_PROOF_BYTES\n ) {\n throw new Error('Untrusted Codex hook proof file.');\n }\n}\n\nfunction readDescriptorFully(descriptor: number, size: number): Buffer {\n const buffer = Buffer.alloc(size);\n let offset = 0;\n while (offset < buffer.length) {\n const count = readSync(descriptor, buffer, offset, buffer.length - offset, offset);\n if (count === 0) break;\n offset += count;\n }\n if (offset !== buffer.length)\n throw new Error('Codex hook proof changed while it was being read.');\n return buffer;\n}\n\nfunction assertHookProofUnchanged(before: Stats, after: Stats): void {\n if (\n after.size !== before.size ||\n after.mtimeMs !== before.mtimeMs ||\n after.ctimeMs !== before.ctimeMs\n ) {\n throw new Error('Codex hook proof changed while it was being read.');\n }\n}\n\nfunction readTrustedHookProofJson(path: string): unknown {\n const descriptor = openSync(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));\n try {\n const metadata = fstatSync(descriptor);\n assertTrustedHookProofMetadata(metadata);\n const buffer = readDescriptorFully(descriptor, metadata.size);\n assertHookProofUnchanged(metadata, fstatSync(descriptor));\n return JSON.parse(buffer.toString('utf8')) as unknown;\n } finally {\n closeSync(descriptor);\n }\n}\n\n// eslint-disable-next-line complexity -- aggregates independently written event proofs and preserves malformed/stale distinctions\nexport function observeCodexHookProof(\n environment: NodeJS.ProcessEnv = process.env,\n binding?: { projectDirectory: string; sessionId: string },\n): CodexHookProofObservation {\n const candidates = CODEX_PLUGIN_HOOK_EVENTS.map(event => ({\n event,\n path: codexProofPath(environment, event),\n }));\n const existing = candidates.filter(candidate => existsSync(candidate.path));\n if (existing.length === 0) {\n return {\n status: 'missing',\n plugin_version: null,\n manifest_sha256: null,\n recorded_at: null,\n activation_id: null,\n events: [],\n missing_events: CODEX_PLUGIN_HOOK_EVENTS,\n };\n }\n\n const parsed = existing.map(item => readEventProof(item.path, item.event));\n if (parsed.includes(null)) return malformedObservation();\n const validProofs = parsed.filter(proof => proof !== null);\n\n const identity = currentCodexPluginIdentity();\n const activationId =\n readActivationMarkerV2(environment, identity)?.activation_id ??\n readActivationReceipt(environment, identity)?.activation_id ??\n null;\n const canonicalProject =\n binding === undefined ? undefined : canonicalProjectDirectory(binding.projectDirectory);\n const current = validProofs.filter(\n proof =>\n matchesCodexPluginIdentity(proof, identity) &&\n (activationId === null ||\n (proof.schema_version !== 1 && proof.activation_id === activationId)) &&\n (binding === undefined ||\n (proof.schema_version === 3 &&\n proof.project_directory === canonicalProject &&\n proof.session_id === binding.sessionId)),\n );\n const events = current.map(proof => proof.event);\n const missingEvents = CODEX_PLUGIN_HOOK_EVENTS.filter(event => !events.includes(event));\n const latest = current\n .toSorted((left, right) => right.recorded_at.localeCompare(left.recorded_at))\n .at(0);\n let status: CodexHookProofStatus = 'stale';\n if (current.length === CODEX_PLUGIN_HOOK_EVENTS.length) status = 'current';\n else if (current.length > 0) status = 'partial';\n return {\n status,\n plugin_version: latest?.plugin_version ?? validProofs[0]?.plugin_version ?? null,\n manifest_sha256: latest?.manifest_sha256 ?? validProofs[0]?.manifest_sha256 ?? null,\n recorded_at: latest?.recorded_at ?? null,\n activation_id: activationId,\n events,\n missing_events: missingEvents,\n };\n}\n\nexport function observeCurrentExactCodexHookAuthorization(\n environment: NodeJS.ProcessEnv,\n binding: { projectDirectory: string; sessionId: string },\n now = new Date(),\n): CodexExactHookAuthorization | undefined {\n const observation = observeCodexHookProof(environment, binding);\n if (\n observation.status !== 'current' ||\n observation.plugin_version === null ||\n observation.manifest_sha256 === null\n ) {\n return undefined;\n }\n return {\n schema_version: 1,\n kind: 'current-exact-native-hook-proof',\n project_directory: canonicalProjectDirectory(binding.projectDirectory),\n session_id: binding.sessionId,\n plugin_version: observation.plugin_version,\n manifest_sha256: observation.manifest_sha256,\n activation_id: observation.activation_id,\n events: [...CODEX_PLUGIN_HOOK_EVENTS],\n hook_outcome: 'successful',\n trust_basis: 'host-invoked-plugin-hook',\n verified_at: now.toISOString(),\n };\n}\n\nfunction malformedObservation(): CodexHookProofObservation {\n return {\n status: 'malformed',\n plugin_version: null,\n manifest_sha256: null,\n recorded_at: null,\n activation_id: null,\n events: [],\n missing_events: CODEX_PLUGIN_HOOK_EVENTS,\n };\n}\n\n/*\n Each manifest entry is trusted independently by Codex, so finalization proof\n is intentionally one file per event. Separate files also avoid concurrent\n hook processes racing on a shared read-modify-write document.\n*/\n\n// eslint-disable-next-line complexity -- validates every field of two persisted proof schema versions\nfunction isCodexHookProof(\n value: unknown,\n): value is CodexHookProofV1 | CodexHookProofV2 | CodexHookProofV3 {\n if (typeof value !== 'object' || value === null) return false;\n const proof = value as Record<string, unknown>;\n return (\n [1, 2, 3].includes(proof.schema_version as number) &&\n isCodexPluginHookEvent(proof.event) &&\n typeof proof.plugin_version === 'string' &&\n typeof proof.manifest_sha256 === 'string' &&\n /^[\\da-f]{64}$/u.test(proof.manifest_sha256) &&\n (proof.schema_version === 1 ||\n ('activation_id' in proof &&\n (typeof proof.activation_id === 'string' || proof.activation_id === null))) &&\n (proof.schema_version !== 3 ||\n (typeof proof.project_directory === 'string' &&\n typeof proof.session_id === 'string' &&\n proof.session_id.trim() !== '')) &&\n typeof proof.recorded_at === 'string' &&\n !Number.isNaN(Date.parse(proof.recorded_at))\n );\n}\n\n// eslint-disable-next-line complexity -- validates each untrusted persisted marker field\nfunction isActivationMarkerV2(value: unknown): value is CodexActivationMarkerV2 {\n if (typeof value !== 'object' || value === null) return false;\n const marker = value as Partial<CodexActivationMarkerV2>;\n return (\n marker.schema_version === 2 &&\n typeof marker.plugin_version === 'string' &&\n typeof marker.manifest_sha256 === 'string' &&\n /^[\\da-f]{64}$/u.test(marker.manifest_sha256) &&\n typeof marker.activation_id === 'string' &&\n typeof marker.installed_at === 'string' &&\n !Number.isNaN(Date.parse(marker.installed_at)) &&\n (marker.host_observation === 'observed' || marker.host_observation === 'unavailable') &&\n Array.isArray(marker.active_hosts) &&\n marker.active_hosts.every(isCodexHostProcessIdentity)\n );\n}\n\nfunction isActivationReceiptV1(value: unknown): value is CodexActivationReceiptV1 {\n if (typeof value !== 'object' || value === null) return false;\n const receipt = value as Partial<CodexActivationReceiptV1>;\n return (\n receipt.schema_version === 1 &&\n typeof receipt.plugin_version === 'string' &&\n typeof receipt.manifest_sha256 === 'string' &&\n /^[\\da-f]{64}$/u.test(receipt.manifest_sha256) &&\n typeof receipt.activation_id === 'string' &&\n typeof receipt.activated_at === 'string' &&\n !Number.isNaN(Date.parse(receipt.activated_at)) &&\n isCodexHostProcessIdentity(receipt.host)\n );\n}\n\nfunction isCodexHostProcessIdentity(value: unknown): value is CodexHostProcessIdentity {\n if (typeof value !== 'object' || value === null) return false;\n const host = value as Partial<CodexHostProcessIdentity>;\n return (\n Number.isSafeInteger(host.pid) &&\n (host.pid ?? 0) > 0 &&\n typeof host.started_at === 'string' &&\n !Number.isNaN(Date.parse(host.started_at))\n );\n}\n\nfunction isCodexPluginHookEvent(value: unknown): value is CodexPluginHookEvent {\n return (\n typeof value === 'string' && CODEX_PLUGIN_HOOK_EVENTS.includes(value as CodexPluginHookEvent)\n );\n}\n","/* eslint-disable unicorn/no-null -- host observations use explicit null when ancestry is unavailable */\n\nimport { spawnSync } from 'node:child_process';\n\nexport interface CodexHostProcessIdentity {\n pid: number;\n started_at: string;\n}\n\ninterface ProcessRow extends CodexHostProcessIdentity {\n parentPid: number;\n command: string;\n}\n\nexport interface CodexHostProcessObservation {\n available: boolean;\n running: CodexHostProcessIdentity[];\n current: CodexHostProcessIdentity | null;\n}\n\nconst WINDOWS_PROCESS_COMMAND =\n 'Get-CimInstance Win32_Process | ForEach-Object { $started = [DateTimeOffset]$_.CreationDate; Write-Output (\"{0}`t{1}`t{2}`t{3}\" -f $_.ProcessId, $_.ParentProcessId, $started.ToUnixTimeMilliseconds(), $_.CommandLine) }';\n\nfunction parseProcessRows(output: string): ProcessRow[] {\n return output.split('\\n').flatMap(line => {\n const columns = line.trim().split(/\\s+/u);\n if (columns.length < 8) return [];\n const pid = Number(columns[0]);\n const parentPid = Number(columns[1]);\n if (!Number.isSafeInteger(pid) || !Number.isSafeInteger(parentPid)) return [];\n const startedAt = new Date(columns.slice(2, 7).join(' '));\n if (Number.isNaN(startedAt.getTime())) return [];\n return [\n {\n pid,\n parentPid,\n started_at: startedAt.toISOString(),\n command: columns.slice(7).join(' '),\n },\n ];\n });\n}\n\nfunction parseWindowsProcessRows(output: string): ProcessRow[] {\n return output.split('\\n').flatMap(line => {\n const [pidText, parentPidText, startedAtText, ...command] = line.trimEnd().split('\\t');\n const pid = Number(pidText);\n const parentPid = Number(parentPidText);\n const startedAt = new Date(Number(startedAtText));\n if (!Number.isSafeInteger(pid) || !Number.isSafeInteger(parentPid) || command.length === 0)\n return [];\n if (Number.isNaN(startedAt.getTime())) return [];\n return [\n {\n pid,\n parentPid,\n started_at: startedAt.toISOString(),\n command: command.join('\\t'),\n },\n ];\n });\n}\n\nfunction isCodexAppServer(row: ProcessRow): boolean {\n const command = row.command.replaceAll('\\\\', '/');\n return (\n /(?:^|\\/)codex(?:\\.exe)?[\"']?\\s/iu.test(command) && /(?:^|\\s)app-server(?:\\s|$)/u.test(command)\n );\n}\n\nexport function codexHostsFromProcessTable(\n output: string,\n parentPid: number,\n format: 'posix' | 'windows' = 'posix',\n): CodexHostProcessObservation {\n const rows = format === 'windows' ? parseWindowsProcessRows(output) : parseProcessRows(output);\n const running = rows\n .filter(row => isCodexAppServer(row))\n .map(({ pid, started_at }) => ({ pid, started_at }));\n const byPid = new Map(rows.map(row => [row.pid, row]));\n let candidate = byPid.get(parentPid);\n const visited = new Set<number>();\n while (candidate !== undefined && !visited.has(candidate.pid)) {\n if (isCodexAppServer(candidate)) {\n return {\n available: true,\n running,\n current: { pid: candidate.pid, started_at: candidate.started_at },\n };\n }\n visited.add(candidate.pid);\n candidate = byPid.get(candidate.parentPid);\n }\n return { available: true, running, current: null };\n}\n\nexport function observeCodexHostProcesses(): CodexHostProcessObservation {\n if (process.platform === 'win32') {\n const result = spawnSync(\n 'powershell.exe',\n ['-NoProfile', '-NonInteractive', '-Command', WINDOWS_PROCESS_COMMAND],\n { encoding: 'utf8', timeout: 2000 },\n );\n if (result.status !== 0) return { available: false, running: [], current: null };\n return codexHostsFromProcessTable(result.stdout, process.ppid, 'windows');\n }\n const result = spawnSync('ps', ['-axo', 'pid=,ppid=,lstart=,command='], {\n encoding: 'utf8',\n timeout: 2000,\n });\n if (result.status !== 0) return { available: false, running: [], current: null };\n return codexHostsFromProcessTable(result.stdout, process.ppid);\n}\n\nexport function observeRunningCodexHosts(): CodexHostProcessIdentity[] | null {\n const observation = observeCodexHostProcesses();\n return observation.available ? observation.running : null;\n}\n\nexport function sameCodexHost(\n left: CodexHostProcessIdentity,\n right: CodexHostProcessIdentity,\n): boolean {\n return left.pid === right.pid && left.started_at === right.started_at;\n}\n"],"mappings":"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","names":["readFileSync","nodePath","result","nodePath","readFileSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/presets/typescript/eslint-configs/astro.ts","../src/presets/typescript/eslint-configs/lazy.ts","../src/presets/typescript/eslint-configs/optional-dependency.ts","../src/presets/typescript/eslint-configs/base.ts","../src/presets/typescript/eslint-rules/no-accumulating-spread.ts","../src/presets/typescript/eslint-rules/no-incomplete-error-handling.ts","../src/presets/typescript/eslint-rules/no-re-export-all.ts","../src/presets/typescript/eslint-rules/index.ts","../src/presets/typescript/eslint-configs/test-file-globs.ts","../src/presets/typescript/eslint-configs/test-integrity-syntax.ts","../src/presets/typescript/eslint-configs/bun-test.ts","../src/presets/typescript/eslint-configs/overrides-cli.ts","../src/presets/typescript/eslint-configs/overrides-relaxed-types.ts","../src/presets/typescript/eslint-configs/playwright.ts","../src/presets/typescript/eslint-configs/recommended.ts","../src/presets/typescript/eslint-configs/recommended-nextjs.ts","../src/presets/typescript/eslint-configs/recommended-react.ts","../src/presets/typescript/eslint-configs/recommended-typescript.ts","../src/presets/typescript/eslint-configs/storybook.ts","../src/presets/typescript/eslint-configs/tailwind.ts","../src/presets/typescript/eslint-configs/tanstack-query.ts","../src/presets/typescript/eslint-configs/turbo.ts","../src/presets/typescript/eslint-configs/vendored-ignores.ts","../src/presets/typescript/eslint-configs/vitest.ts","../src/presets/typescript/index.ts"],"sourcesContent":["/**\n * ESLint configuration for Astro projects\n *\n * Applies to .astro files.\n * Includes recommended rules plus LLM-critical security/convention rules.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport eslintPluginAstro from 'eslint-plugin-astro';\n\nimport { lazyConfigArray } from './lazy.js';\nimport { hasOptionalDependency } from './optional-dependency.js';\n\ninterface BuildAstroConfigOptions {\n astroPlugin?: {\n configs: Record<string, any[]>;\n };\n hasJsxA11y?: boolean;\n}\n\n/**\n * Astro config\n *\n * Includes:\n * - Astro's recommended rules (all at error)\n * - 33 accessibility rules from jsx-a11y-strict (adapted for Astro)\n * - LLM-critical rules: no-set-html-directive (XSS), no-unsafe-inline-scripts (CSP), no-exports-from-components\n *\n * Note: jsx-a11y rules work with Astro files because eslint-plugin-astro\n * provides wrapped versions that understand Astro's JSX-like syntax.\n * Using eslint-plugin-jsx-a11y directly on Astro files does NOT work.\n *\n * Coverage change in eslint-plugin-astro v3: `astro/valid-compile` and\n * `astro/no-omitted-end-tags` were deprecated and dropped from\n * `flat/recommended`, so this config no longer carries them. Omitted end tags\n * are now rejected by Astro's compiler at parse time, so that rule is a no-op.\n * Compile errors, previously surfaced through ESLint by `astro/valid-compile`,\n * are now upstream's job for the `astro check` command — projects that relied\n * on ESLint alone for that signal should add `astro check` to their pipeline.\n * Both rules are still exported by the plugin, so this config could re-enable\n * them; it does not, because upstream's own rule documentation tells users to\n * remove `astro/valid-compile` from their ESLint config and run `astro check`\n * instead. Re-adding it here would contradict that guidance.\n *\n * Config objects are assembled lazily — only when this config is actually accessed.\n */\nexport function buildAstroConfig({\n astroPlugin = eslintPluginAstro,\n hasJsxA11y = hasOptionalDependency('eslint-plugin-jsx-a11y'),\n}: BuildAstroConfigOptions = {}): any[] {\n return [\n // Spread flat/recommended (5 config objects: plugin setup, file patterns, prettier overrides, rules)\n ...(astroPlugin.configs['flat/recommended'] ?? []),\n\n // Accessibility rules adapted for Astro when eslint-plugin-jsx-a11y is installed.\n ...(hasJsxA11y ? (astroPlugin.configs['flat/jsx-a11y-strict'] ?? []) : []),\n\n // Add LLM-critical rules\n {\n name: 'safeword/astro',\n rules: {\n // XSS prevention - LLMs often use set:html for rendering user content\n 'astro/no-set-html-directive': 'error',\n\n // CSP safety - inline scripts can break Content Security Policy\n 'astro/no-unsafe-inline-scripts': 'error',\n\n // Astro convention - LLMs try to export from .astro components (not allowed)\n 'astro/no-exports-from-components': 'error',\n },\n },\n ];\n}\n\nexport const astroConfig: any[] = lazyConfigArray(() => buildAstroConfig());\n","/**\n * Lazy ESLint config array helper.\n *\n * Wraps a builder function in a Proxy that delays execution until the array is\n * first accessed (read, iterated, spread, or `in`-tested). The result is cached\n * so subsequent accesses don't re-run the builder.\n *\n * Why: stack-specific ESLint plugins (Storybook, Turbo, Astro, etc.) cost\n * ~20ms each to load. The customer's generated `eslint.config.mjs` already\n * conditionally spreads these configs behind `detect.has*(deps)`, so plugins\n * for stacks the customer doesn't use should never load. This helper makes\n * that true — without changing the public shape of `xConfig` exports.\n *\n * Ticket H150ZW.\n */\n\n/**\n * Wrap a builder so its execution is deferred until the returned array is accessed.\n *\n * The returned value is a `Proxy<T[]>` that behaves as the eventual array for\n * read, iterate, spread, `in`, and `Object.keys` operations. Mutation traps are\n * not forwarded (configs are read-only by contract).\n */\nexport function lazyConfigArray<T>(builder: () => T[]): T[] {\n let cached: T[] | undefined;\n const resolve = (): T[] => (cached ??= builder());\n\n return new Proxy([] as T[], {\n get(_target, property, _receiver) {\n return Reflect.get(resolve(), property);\n },\n has(_target, property) {\n return Reflect.has(resolve(), property);\n },\n ownKeys(_target) {\n return Reflect.ownKeys(resolve());\n },\n getOwnPropertyDescriptor(_target, property) {\n return Reflect.getOwnPropertyDescriptor(resolve(), property);\n },\n });\n}\n","import { createRequire } from 'node:module';\nimport nodePath from 'node:path';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/**\n * Load an optional ESLint plugin from the user's project first, then from\n * safeword's own install context. This lets consumers opt into plugins that\n * safeword does not ship as production dependencies.\n */\nexport function optionalRequire(packageName: string): unknown {\n try {\n const requireFromCwd = createRequire(nodePath.join(process.cwd(), '__placeholder__.js'));\n return requireFromCwd(packageName);\n } catch {\n // Fall back to safeword's context below.\n }\n\n try {\n return requireFromHere(packageName);\n } catch {\n return undefined;\n }\n}\n\nexport function hasOptionalDependency(packageName: string): boolean {\n return optionalRequire(packageName) !== undefined;\n}\n","/**\n * Base ESLint plugins shared between JS and TypeScript configs\n *\n * These plugins work without type information and are included in both\n * `recommended` (JS) and `recommendedTypeScript` configs.\n */\n\nimport js from '@eslint/js';\nimport eslintComments from '@eslint-community/eslint-plugin-eslint-comments';\nimport { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';\nimport { importX } from 'eslint-plugin-import-x';\nimport pluginPromise from 'eslint-plugin-promise';\nimport { configs as regexpConfigs } from 'eslint-plugin-regexp';\nimport pluginSecurity from 'eslint-plugin-security';\nimport simpleImportSort from 'eslint-plugin-simple-import-sort';\nimport { configs as sonarConfigs } from 'eslint-plugin-sonarjs';\nimport unicorn from 'eslint-plugin-unicorn';\n\nimport { rules as safewordRules } from '../eslint-rules/index.js';\n\n/**\n * File patterns for base JS/TS rules\n * Excludes .astro, .vue, .svelte which use different parsers\n */\nexport const JS_TS_FILES = ['**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}'];\n\n/**\n * Add files restriction to config objects.\n * Handles both single config objects and arrays of configs.\n * Skips config objects that only have ignores (global ignores).\n * Skips config objects that already have files set.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nfunction scopeConfigToFiles(config: any, files: string[]): any {\n // Skip global ignores (config with only ignores property)\n if (config.ignores && Object.keys(config).length === 1) {\n return config;\n }\n // Skip configs that already have files set\n if (config.files) {\n return config;\n }\n // Add files restriction\n return { ...config, files };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nfunction scopeToFiles(configs: any[], files: string[]): any[] {\n return configs.flatMap(config => {\n // Handle arrays (third-party configs may be arrays)\n if (Array.isArray(config)) {\n return config.map(c => scopeConfigToFiles(c, files));\n }\n return scopeConfigToFiles(config, files);\n });\n}\n\n/**\n * Base plugins - shared between JS and TS configs\n * Does NOT include JSDoc (different config per language) or Prettier (must be last)\n *\n * Note: Uses any[] because ESLint plugin types are incompatible across packages.\n * Runtime validation by ESLint ensures correctness.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nconst basePluginsUnscoped: any[] = [\n // Default ignores - always skip these directories\n {\n ignores: [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/.claude/worktrees/**',\n ],\n },\n\n // ESLint core recommended\n js.configs.recommended,\n\n // Code style and design rules - catches common LLM patterns\n {\n name: 'safeword/code-style',\n rules: {\n 'no-unneeded-ternary': 'error', // x ? true : false → x\n 'prefer-template': 'error', // 'a' + b → `a${b}`\n 'dot-notation': 'error', // obj[\"prop\"] → obj.prop\n 'object-shorthand': 'error', // { foo: foo } → { foo }\n 'no-extra-boolean-cast': 'error', // !!value → Boolean(value) or value\n 'prefer-object-spread': 'error', // Object.assign({}, x) → { ...x }\n 'logical-assignment-operators': 'error', // x = x ?? y → x ??= y\n 'operator-assignment': 'error', // x = x + 1 → x += 1\n curly: 'error', // Require braces around if/else/for/while\n 'arrow-body-style': ['error', 'as-needed'], // () => { return x } → () => x\n 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], // function() {} → () => {}\n // Design constraints - forces LLMs to decompose code\n 'max-depth': ['error', 4], // Forces early returns, avoids deep nesting\n 'max-params': ['error', 5], // Forces object params or decomposition\n complexity: ['error', 10], // Cyclomatic complexity - LLMs write dense, complex code\n 'max-nested-callbacks': ['error', 3], // Prevents callback hell in async code\n eqeqeq: ['error', 'always', { null: 'ignore' }], // === required, except x == null\n 'preserve-caught-error': 'error', // Re-throw with { cause: error } to preserve stack\n },\n },\n\n // Import validation\n importX.flatConfigs.recommended,\n {\n name: 'safeword/import-rules',\n settings: {\n 'import-x/resolver-next': [createTypeScriptImportResolver()],\n },\n rules: {\n 'import-x/no-duplicates': 'error', // LLMs create duplicate imports\n 'import-x/no-cycle': 'error', // Circular dependencies A → B → A\n 'import-x/no-self-import': 'error', // File imports itself (copy-paste bug)\n // Turn off rules with high false-positive rate - documented justification:\n //\n // no-named-as-default: Flags valid patterns like `import Button from './Button'`\n // when Button.tsx has both default and named exports. Very common in React.\n // Issue: https://github.com/import-js/eslint-plugin-import/issues/1618\n 'import-x/no-named-as-default': 'off',\n //\n // no-named-as-default-member: Same issue - flags accessing static members\n // on default imports, e.g., `Button.displayName`. Common React pattern.\n 'import-x/no-named-as-default-member': 'off',\n },\n },\n\n // Code quality / complexity\n sonarConfigs.recommended,\n {\n name: 'safeword/sonarjs-rules',\n rules: {\n // Enable design rules (off by default but valuable for clean code)\n 'sonarjs/no-collapsible-if': 'error', // if(a) { if(b) } → if(a && b)\n 'sonarjs/no-nested-switch': 'error', // Switch inside switch is a smell\n 'sonarjs/prefer-immediate-return': 'error', // const x = y; return x → return y\n 'sonarjs/no-inconsistent-returns': 'error', // Some paths return, some don't\n },\n },\n\n // Security - detect common vulnerabilities\n pluginSecurity.configs.recommended,\n {\n name: 'safeword/security-rules',\n rules: {\n // Critical security rules at error (LLMs ignore warnings)\n 'security/detect-bidi-characters': 'error', // Trojan Source attacks\n 'security/detect-eval-with-expression': 'error',\n 'security/detect-non-literal-fs-filename': 'error',\n 'security/detect-non-literal-regexp': 'error',\n 'security/detect-non-literal-require': 'error',\n 'security/detect-child-process': 'error',\n 'security/detect-unsafe-regex': 'error',\n 'security/detect-disable-mustache-escape': 'error',\n 'security/detect-no-csrf-before-method-override': 'error',\n // Escalate all to error (LLMs ignore warnings)\n 'security/detect-object-injection': 'error',\n 'security/detect-possible-timing-attacks': 'error',\n 'security/detect-buffer-noassert': 'error',\n 'security/detect-new-buffer': 'error',\n 'security/detect-pseudoRandomBytes': 'error',\n },\n },\n\n // Promise handling - catches floating promises (critical for LLM code)\n pluginPromise.configs['flat/recommended'],\n {\n name: 'safeword/promise-rules',\n rules: {\n 'promise/no-multiple-resolved': 'error', // Catches missing return after resolve\n // LLMs mix callback/promise paradigms - escalate to error\n 'promise/no-callback-in-promise': 'error',\n 'promise/no-nesting': 'error',\n 'promise/no-promise-in-callback': 'error',\n 'promise/no-return-in-finally': 'error',\n 'promise/valid-params': 'error',\n },\n },\n\n // Regexp - catches ReDoS vulnerabilities and malformed regex\n regexpConfigs['flat/recommended'],\n {\n name: 'safeword/regexp-rules',\n rules: {\n // Escalate warn rules to error (LLMs ignore warnings)\n 'regexp/confusing-quantifier': 'error',\n 'regexp/no-empty-alternative': 'error',\n 'regexp/no-lazy-ends': 'error',\n 'regexp/no-potentially-useless-backreference': 'error',\n 'regexp/no-useless-flag': 'error',\n 'regexp/optimal-lookaround-quantifier': 'error',\n },\n },\n\n // Modern JS enforcement - strict for agents\n unicorn.configs.recommended,\n {\n name: 'safeword/unicorn-rules',\n rules: {\n // Keep off - documented justification for each:\n //\n // no-process-exit: CLI tools legitimately use process.exit() for:\n // - Error handling with non-zero exit codes\n // - Clean shutdown after completing work\n // Forcing throw-only would break CLI UX and exit code contracts.\n 'unicorn/no-process-exit': 'off',\n //\n // prefer-module: CommonJS is still valid in Node.js ecosystem:\n // - Config files (jest.config.js, .eslintrc.cjs)\n // - Packages with CJS-only dependencies\n // - Gradual ESM migration in progress\n 'unicorn/prefer-module': 'off',\n //\n // consistent-boolean-name (new in unicorn 68's recommended set): demands an\n // is/has/can/should/was/did/will prefix on every boolean identifier —\n // including function declarations that return a status boolean. It cannot\n // distinguish a boolean *state variable* (where the prefix helps) from an\n // *action function that returns success/changed* (where it corrupts the\n // name): installPythonDependencies -> isInstallPythonDependencies,\n // writeIfChanged -> isWriteIfChanged, exists -> isExisting. Even the JS\n // stdlib rejects the premise (Array.includes, String.startsWith, Map.has all\n // return boolean with no prefix). The rule offers no checkFunctions/allowList\n // knob to scope it, and we ship at error-level into customer agent code, so\n // the overreach is unfixable by config. Re-enable scoped to variables if\n // unicorn ever adds a checkFunctions:false option.\n 'unicorn/consistent-boolean-name': 'off',\n //\n // filename-case: unicorn 68 began checking directory names too. Dunder\n // dirs (__tests__, __mocks__, __snapshots__) are an ecosystem convention,\n // not kebab-case violations — ignore that segment, keep the rule otherwise.\n 'unicorn/filename-case': ['error', { case: 'kebabCase', ignore: [/^__\\w+__$/] }],\n // Escalated to error for LLM code\n 'unicorn/switch-case-braces': 'error',\n 'unicorn/catch-error-name': 'error',\n // Keep this off while safeword supports Node 22: the rule autofixes to the\n // Node 24-only Error static brand check and would break generated projects.\n 'unicorn/prefer-error-is-error': 'off',\n //\n // prefer-simple-condition-first (new in unicorn 72's recommended set):\n // demands the \"simpler\" operand lead in an && / || chain, but reordering\n // operands changes short-circuit behavior whenever one guards another\n // (`x && x.foo`, `m && Number(m[1])`). The rule is deliberately NOT\n // autofixable — its own message says \"after verifying short-circuit\n // behavior\" — i.e. it cannot tell a safe reorder from a guard swap. We ship\n // at error-level into customer agent code, so a rule that requires per-site\n // manual short-circuit verification (and silently corrupts a guard when\n // applied blindly) is unsafe to enforce. Ordering is a readability judgment,\n // not a correctness one.\n 'unicorn/prefer-simple-condition-first': 'off',\n 'unicorn/no-array-reduce': 'error', // LLMs write confusing reduce\n // Renamed from unicorn/prevent-abbreviations in eslint-plugin-unicorn 68.\n 'unicorn/name-replacements': [\n 'error',\n {\n allowList: {\n ctx: true, // context\n req: true, // request\n res: true, // response\n err: true, // error\n dir: true, // directory\n pkg: true, // package\n env: true, // environment\n args: true, // arguments\n params: true, // parameters\n props: true, // properties\n ref: true, // reference\n src: true, // source\n dest: true, // destination\n db: true, // database\n fn: true, // function\n cb: true, // callback\n acc: true, // accumulator\n prev: true, // previous\n curr: true, // current\n i: true, // index\n j: true, // index\n k: true, // index\n },\n },\n ],\n 'unicorn/no-null': 'error', // Use undefined\n 'unicorn/no-for-each': 'error', // Use for...of (renamed from no-array-for-each in unicorn 68)\n 'unicorn/no-negated-condition': 'error', // Clearer conditionals\n // Cherry-picked from 'all' config - high value for LLM code\n 'unicorn/no-unused-properties': 'error', // Dead code in enum-like objects\n 'unicorn/consistent-destructuring': 'error', // Don't mix props.x and {x} = props\n 'unicorn/prefer-import-meta-properties': 'error', // import.meta.dirname > __dirname\n },\n },\n\n // Import sorting - auto-fixable, reduces noise\n {\n name: 'safeword/import-sort',\n plugins: { 'simple-import-sort': simpleImportSort },\n rules: {\n 'simple-import-sort/imports': 'error',\n 'simple-import-sort/exports': 'error',\n 'import-x/order': 'off', // Disable in favor of simple-import-sort\n },\n },\n\n // Safeword custom rules - LLM-specific patterns\n {\n name: 'safeword/custom-rules',\n plugins: { safeword: { rules: safewordRules } },\n rules: {\n 'safeword/no-incomplete-error-handling': 'error',\n 'safeword/no-accumulating-spread': 'error', // O(n²) reduce pattern\n 'safeword/no-re-export-all': 'error', // Hurts tree-shaking\n },\n },\n\n // ESLint native: flag stale disable directives (ESLint 9 default is warn, we want error)\n {\n name: 'safeword/linter-options',\n linterOptions: {\n reportUnusedDisableDirectives: 'error',\n },\n },\n\n // ESLint disable comment governance - prevents broad/undocumented suppressions\n {\n name: 'safeword/eslint-comments',\n plugins: { 'eslint-comments': eslintComments },\n rules: {\n 'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],\n 'eslint-comments/no-unlimited-disable': 'error', // Must name specific rule\n 'eslint-comments/require-description': ['error', { ignore: ['eslint-enable'] }], // Must explain why\n 'eslint-comments/no-duplicate-disable': 'error', // No redundant disables\n 'eslint-comments/no-unused-enable': 'error', // No orphaned enables\n },\n },\n];\n\n/**\n * Base plugins scoped to JS/TS files only.\n * Prevents rules from running on .astro, .vue, .svelte files which use different parsers.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nexport const basePlugins: any[] = scopeToFiles(basePluginsUnscoped, JS_TS_FILES);\n\n/**\n * Prettier config - must be last to disable conflicting rules\n */\n\nexport { default as prettierConfig } from 'eslint-config-prettier';\n","/**\n * Rule: no-accumulating-spread\n *\n * Detects spread operator on accumulators in reduce callbacks, which causes\n * O(n²) time complexity. This is a common LLM performance mistake.\n *\n * Bad (O(n²)):\n * items.reduce((acc, item) => ({ ...acc, [item.id]: item }), {})\n * items.reduce((acc, item) => [...acc, item.name], [])\n *\n * Good (O(n)):\n * items.reduce((acc, item) => { acc[item.id] = item; return acc; }, {})\n * items.reduce((acc, item) => { acc.push(item.name); return acc; }, [])\n *\n * Or better - avoid reduce entirely:\n * Object.fromEntries(items.map(item => [item.id, item]))\n * items.map(item => item.name)\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionExpression,\n Node,\n SpreadElement,\n} from 'estree';\n\n/**\n * Check if a node is a call to .reduce()\n */\nfunction isReduceCall(node: CallExpression): boolean {\n const { callee } = node;\n return (\n callee.type === 'MemberExpression' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'reduce'\n );\n}\n\n/**\n * Get the accumulator parameter name from a reduce callback\n */\nfunction getAccumulatorName(\n callback: ArrowFunctionExpression | FunctionExpression,\n): string | undefined {\n const firstParameter = callback.params[0];\n if (firstParameter?.type === 'Identifier') {\n return firstParameter.name;\n }\n return undefined;\n}\n\n/**\n * Check if a spread element spreads the accumulator\n */\nfunction spreadsAccumulator(spread: SpreadElement, accName: string): boolean {\n return spread.argument.type === 'Identifier' && spread.argument.name === accName;\n}\n\n/**\n * Check object expression properties for accumulator spread.\n */\nfunction findSpreadInObject(\n node: Node & { type: 'ObjectExpression' },\n accName: string,\n): SpreadElement | undefined {\n for (const property of node.properties) {\n if (property.type === 'SpreadElement' && spreadsAccumulator(property, accName)) {\n return property;\n }\n }\n return undefined;\n}\n\n/**\n * Check array expression elements for accumulator spread.\n */\nfunction findSpreadInArray(\n node: Node & { type: 'ArrayExpression' },\n accName: string,\n): SpreadElement | undefined {\n for (const element of node.elements) {\n if (element?.type === 'SpreadElement' && spreadsAccumulator(element, accName)) {\n return element;\n }\n }\n return undefined;\n}\n\n/**\n * Recursively check if an expression contains a spread of the accumulator\n */\nfunction containsAccumulatorSpread(node: Node, accName: string): SpreadElement | undefined {\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- Only specific node types can contain spreads; all others return undefined via default\n switch (node.type) {\n case 'SpreadElement': {\n return spreadsAccumulator(node, accName) ? node : undefined;\n }\n\n case 'ObjectExpression': {\n return findSpreadInObject(node, accName);\n }\n\n case 'ArrayExpression': {\n return findSpreadInArray(node, accName);\n }\n\n case 'ConditionalExpression': {\n return (\n containsAccumulatorSpread(node.consequent, accName) ??\n containsAccumulatorSpread(node.alternate, accName)\n );\n }\n\n case 'LogicalExpression': {\n return (\n containsAccumulatorSpread(node.left, accName) ??\n containsAccumulatorSpread(node.right, accName)\n );\n }\n\n default: {\n return undefined;\n }\n }\n}\n\n/**\n * Check arrow function body for accumulator spread\n */\nfunction checkArrowBody(\n body: ArrowFunctionExpression['body'],\n accName: string,\n): SpreadElement | undefined {\n // Direct return: (acc, item) => ({ ...acc, ... })\n if (body.type === 'ObjectExpression' || body.type === 'ArrayExpression') {\n return containsAccumulatorSpread(body, accName);\n }\n\n // Parenthesized or conditional\n if (body.type === 'ConditionalExpression' || body.type === 'LogicalExpression') {\n return containsAccumulatorSpread(body, accName);\n }\n\n // Block body - check return statements\n if (body.type === 'BlockStatement') {\n for (const statement of body.body) {\n if (!(statement.type === 'ReturnStatement' && statement.argument)) {\n continue;\n }\n\n const found = containsAccumulatorSpread(statement.argument, accName);\n if (found) return found;\n }\n }\n\n return undefined;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow spreading accumulator in reduce (causes O(n²) complexity)',\n recommended: true,\n },\n messages: {\n accumulatingSpread:\n 'Spreading accumulator in reduce() causes O(n²) complexity. ' +\n 'Mutate the accumulator instead, or use map/filter/Object.fromEntries.',\n },\n schema: [],\n },\n\n create(context) {\n return {\n CallExpression(node: CallExpression) {\n if (!isReduceCall(node)) return;\n\n const callback = node.arguments[0];\n if (\n !callback ||\n (callback.type !== 'ArrowFunctionExpression' && callback.type !== 'FunctionExpression')\n ) {\n return;\n }\n\n const accName = getAccumulatorName(callback);\n if (!accName) return;\n\n const spreadNode = checkArrowBody(callback.body, accName);\n if (spreadNode) {\n context.report({\n node: spreadNode,\n messageId: 'accumulatingSpread',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: no-incomplete-error-handling\n *\n * Detects catch blocks that log an error but don't rethrow or return,\n * which swallows the error silently. This is a common LLM mistake.\n *\n * Bad:\n * catch (error) { console.error(error); } // swallowed!\n *\n * Good:\n * catch (error) { console.error(error); throw error; }\n * catch (error) { console.error(error); return null; }\n * catch (error) { throw new AppError('context', { cause: error }); }\n */\n\nimport type { Rule } from 'eslint';\nimport type { CallExpression, CatchClause, Statement } from 'estree';\n\nconst LOG_METHODS = new Set(['log', 'error', 'warn', 'info', 'debug', 'trace']);\n\nconst LOG_OBJECTS = new Set(['console', 'logger', 'log']);\n\n/**\n * Checks if a call expression is a logging call (console.log, logger.error, etc.)\n * @param node\n */\nfunction isLoggingCall(node: CallExpression): boolean {\n const { callee } = node;\n\n // console.error(...), logger.error(...), etc.\n if (\n callee.type === 'MemberExpression' &&\n callee.object.type === 'Identifier' &&\n callee.property.type === 'Identifier'\n ) {\n const object = callee.object.name.toLowerCase();\n const method = callee.property.name.toLowerCase();\n return LOG_OBJECTS.has(object) && LOG_METHODS.has(method);\n }\n\n return false;\n}\n\n/**\n * Check if a single statement terminates control flow.\n */\nfunction isTerminatingBranch(statement: Statement): boolean {\n if (statement.type === 'ThrowStatement' || statement.type === 'ReturnStatement') {\n return true;\n }\n if (statement.type === 'BlockStatement') {\n return hasTerminatingStatement(statement.body);\n }\n return false;\n}\n\n/**\n * Check if an if statement terminates (both branches must terminate).\n */\nfunction ifStatementTerminates(statement: Statement & { type: 'IfStatement' }): boolean {\n const isConsequentTerminates = isTerminatingBranch(statement.consequent);\n const isAlternateTerminates = statement.alternate\n ? isTerminatingBranch(statement.alternate)\n : false;\n return isConsequentTerminates && isAlternateTerminates;\n}\n\n/**\n * Checks if statements include a throw or return (error is properly handled)\n * @param statements\n */\nfunction hasTerminatingStatement(statements: Statement[]): boolean {\n for (const statement of statements) {\n if (statement.type === 'ThrowStatement' || statement.type === 'ReturnStatement') {\n return true;\n }\n if (statement.type === 'IfStatement' && ifStatementTerminates(statement)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a single statement is a logging call.\n */\nfunction isLoggingStatement(statement: Statement): boolean {\n return (\n statement.type === 'ExpressionStatement' &&\n statement.expression.type === 'CallExpression' &&\n isLoggingCall(statement.expression)\n );\n}\n\n/**\n * Get nested statements from a statement (for recursive search).\n */\nfunction getNestedStatements(statement: Statement): Statement[] {\n if (statement.type === 'BlockStatement') {\n return statement.body;\n }\n if (statement.type === 'IfStatement') {\n const nested = [statement.consequent];\n if (statement.alternate) nested.push(statement.alternate);\n return nested;\n }\n return [];\n}\n\n/**\n * Recursively checks if statements include a logging call (searches nested blocks)\n * @param statements\n */\nfunction containsLoggingCall(statements: Statement[]): boolean {\n for (const statement of statements) {\n if (isLoggingStatement(statement)) return true;\n\n const nested = getNestedStatements(statement);\n if (nested.length > 0 && containsLoggingCall(nested)) return true;\n }\n return false;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow catch blocks that log but do not rethrow or return',\n recommended: true,\n },\n messages: {\n incompleteErrorHandling:\n 'Catch block logs error but does not rethrow or return. This swallows the error silently.',\n },\n schema: [],\n },\n\n create(context) {\n return {\n CatchClause(node: CatchClause) {\n const { body } = node;\n if (body.type !== 'BlockStatement') return;\n\n const statements = body.body;\n\n // Only flag if there's a logging call but no terminating statement\n if (containsLoggingCall(statements) && !hasTerminatingStatement(statements)) {\n context.report({\n node,\n messageId: 'incompleteErrorHandling',\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: no-re-export-all\n *\n * Disallows `export * from` statements which re-export all exports from\n * another module. This pattern hurts tree-shaking and makes dependencies\n * unclear. LLMs often use this for convenience without understanding\n * the bundle size implications.\n *\n * Bad:\n * export * from './utils';\n * export * from '@/components';\n *\n * Good:\n * export { foo, bar } from './utils';\n * export { Button, Modal } from '@/components';\n *\n * Note: `export * as namespace from` is allowed as it creates a named export.\n */\n\nimport type { Rule } from 'eslint';\nimport type { ExportAllDeclaration } from 'estree';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Disallow wildcard re-exports (export * from)',\n recommended: true,\n },\n messages: {\n noReExportAll:\n 'Avoid `export * from`. Use named exports for better tree-shaking and clearer dependencies.',\n },\n schema: [],\n },\n\n create(context) {\n return {\n ExportAllDeclaration(node: ExportAllDeclaration) {\n // Allow `export * as namespace from` - this creates a named export\n if (node.exported) {\n return;\n }\n\n context.report({\n node,\n messageId: 'noReExportAll',\n });\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Safeword custom ESLint rules\n */\n\nimport noAccumulatingSpread from './no-accumulating-spread.js';\nimport noIncompleteErrorHandling from './no-incomplete-error-handling.js';\nimport noReExportAll from './no-re-export-all.js';\n\nexport const rules = {\n 'no-accumulating-spread': noAccumulatingSpread,\n 'no-incomplete-error-handling': noIncompleteErrorHandling,\n 'no-re-export-all': noReExportAll,\n};\n","/**\n * The glob patterns safeword treats as test files for lint purposes: `*.test`/`*.spec`\n * files plus everything under `tests/` and `e2e/`. Shared by the test-runner configs\n * (the `bun:test` globals and the Vitest rules) so their file scope cannot drift apart —\n * a file that one treats as a test and the other doesn't would lint inconsistently.\n */\nexport const TEST_FILE_GLOBS = [\n '**/*.test.{ts,tsx,js,jsx}',\n '**/*.spec.{ts,tsx,js,jsx}',\n '**/tests/**/*.{ts,tsx,js,jsx}',\n '**/e2e/**/*.{ts,tsx,js,jsx}',\n] as const;\n","/**\n * Shared no-restricted-syntax entries for the test-integrity graduation\n * (VFD6X1 + review hardening, issue #773): the testing-guide invariants that\n * need no plugin, consumable by both the vitest lane and the bun:test lane so\n * the guide's promise holds on either runner.\n */\n\ninterface RestrictedSyntaxEntry {\n selector: string;\n message: string;\n}\n\n/**\n * Arbitrary-sleep idioms. Selectors match the *idioms*, not every setTimeout —\n * fake-timer scheduling and template-string fixtures stay legal. Known\n * accepted false positive: a timeout-guard race (`await new Promise((res,\n * rej) => { start(res); setTimeout(() => rej(...), 5000); })`) matches the\n * descendant selector even though it isn't a sleep — vi.waitFor/expect.poll\n * are the better pattern there anyway, and an inline disable with a reason is\n * the escape hatch.\n */\nexport const SLEEP_RESTRICTED_SYNTAX: RestrictedSyntaxEntry[] = [\n {\n selector:\n \"AwaitExpression > NewExpression[callee.name='Promise'] CallExpression[callee.name='setTimeout']\",\n message:\n 'Arbitrary sleep (await new Promise + setTimeout) — poll an observable condition instead (expect.poll / vi.waitFor). See testing-guide.md.',\n },\n {\n selector: \"CallExpression[callee.object.name='Bun'][callee.property.name='sleep']\",\n message:\n 'Arbitrary sleep (Bun.sleep) — poll an observable condition instead (expect.poll / vi.waitFor). See testing-guide.md.',\n },\n {\n selector: \"CallExpression[callee.name='sleep']\",\n message:\n 'Arbitrary sleep — poll an observable condition instead (expect.poll / vi.waitFor). See testing-guide.md.',\n },\n];\n\n/**\n * Deferred-test marker for lanes WITHOUT the vitest plugin (the vitest lane\n * uses the plugin's warn rule for this marker, which robustly covers chained\n * modifiers). Two attribute shapes: a direct `it.<marker>(...)` and a chained\n * `it.concurrent.<marker>(...)` — the chained form was a verified bypass of\n * the direct-only selector (quality review, 2026-07-07).\n */\nexport const DEFERRED_TEST_RESTRICTED_SYNTAX: RestrictedSyntaxEntry[] = [\n {\n selector:\n \"CallExpression > MemberExpression[property.name='todo']:matches([object.name=/^(it|test|describe)$/], [object.object.name=/^(it|test|describe)$/])\",\n message:\n 'Deferred-test marker parks a gap the suite then reports as green — write the test or delete the stub (an inline eslint-disable with a reason is the sanctioned escape hatch).',\n },\n];\n","/**\n * ESLint configuration for Bun's built-in test runner (`bun:test`)\n *\n * Bun's transpiler injects `describe`/`test`/`expect`/etc. into test files\n * without requiring an explicit `import ... from \"bun:test\"` — officially\n * supported by Bun, not a customer mistake (see\n * https://bun.com/docs/test). ESLint lints the untranspiled source, so it\n * can't see that injection and flags the names as undefined.\n *\n * TypeScript files already tolerate this: typescript-eslint's\n * `eslint-recommended` override turns core `no-undef` off for .ts/.tsx,\n * deferring to the type checker. Plain .js/.jsx Bun test files have no such\n * override, so `no-undef` still fires there — this config declares the\n * exact `bun:test` export surface as known globals to close that gap\n * (ticket #513), scoped to test files only since Bun's injection doesn't\n * reach imported helper files either.\n */\n\nimport { TEST_FILE_GLOBS } from './test-file-globs.js';\nimport {\n DEFERRED_TEST_RESTRICTED_SYNTAX,\n SLEEP_RESTRICTED_SYNTAX,\n} from './test-integrity-syntax.js';\n\n/**\n * `bun:test` global names, all read-only (Bun owns these bindings).\n * Mirrors `bun:test`'s actual exports (verified against\n * https://github.com/oven-sh/bun/blob/main/packages/bun-types/test.d.ts and\n * https://bun.com/reference/bun/test) — not Jest's. Bun ships `xdescribe`/\n * `xtest`/`xit` as `.skip()` shorthands, but has no `fit` (focus) alias —\n * use `.only` on `test`/`describe`/`it` instead. Declaring a name Bun\n * doesn't actually inject would let a typo pass lint only to crash at\n * runtime.\n */\nconst BUN_TEST_GLOBALS = {\n test: 'readonly',\n it: 'readonly',\n xtest: 'readonly',\n xit: 'readonly',\n describe: 'readonly',\n xdescribe: 'readonly',\n beforeAll: 'readonly',\n beforeEach: 'readonly',\n afterAll: 'readonly',\n afterEach: 'readonly',\n onTestFinished: 'readonly',\n setDefaultTimeout: 'readonly',\n expect: 'readonly',\n expectTypeOf: 'readonly',\n mock: 'readonly',\n spyOn: 'readonly',\n jest: 'readonly',\n setSystemTime: 'readonly',\n vi: 'readonly',\n} as const;\n\n/**\n * Bun test global declarations.\n *\n * Same file scope as `vitestConfig` — both gate on their own detection\n * function, so this only applies when `detect.hasBunTest` is true.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nexport const bunTestConfig: any[] = [\n {\n name: 'safeword/bun-test',\n files: [...TEST_FILE_GLOBS],\n languageOptions: {\n globals: BUN_TEST_GLOBALS,\n },\n rules: {\n // Test-integrity graduation (VFD6X1 review hardening, #773): bun:test\n // projects share testing-guide.md, so the plugin-free invariants apply\n // here too — sleep idioms plus the deferred-test marker (selector-based\n // in this lane, since the vitest plugin's rule isn't available; the\n // selector covers both direct and chained-modifier forms).\n 'no-restricted-syntax': [\n 'error',\n ...SLEEP_RESTRICTED_SYNTAX,\n ...DEFERRED_TEST_RESTRICTED_SYNTAX,\n ],\n },\n },\n];\n","/**\n * CLI Tool preset\n *\n * Disables security rules that produce false positives for CLI tools,\n * build tools, and scripts that work with dynamic file paths and\n * shell commands by design.\n *\n * Usage:\n * import { defineConfig } from 'eslint/config';\n * import safeword from 'safeword/eslint';\n * export default defineConfig([\n * ...safeword.configs.recommendedTypeScript,\n * safeword.configs.cli,\n * ]);\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nexport const cliConfig: any = {\n name: 'safeword/cli',\n rules: {\n // CLI tools read/write user-provided paths — not an injection vector\n 'security/detect-non-literal-fs-filename': 'off',\n // CLI tools index objects with user-provided keys (parsed JSON/YAML)\n 'security/detect-object-injection': 'off',\n // CLI tools execute commands from PATH — this is expected, not an attack\n 'sonarjs/no-os-command-from-path': 'off',\n 'sonarjs/os-command': 'off',\n // CLI tools compare mixed types from parsed user input\n 'sonarjs/different-types-comparison': 'off',\n },\n};\n","/**\n * Relaxed Type-Checking preset\n *\n * Disables strict TypeScript rules that produce false positives when\n * code handles data without compile-time types: JSON parsing, YAML\n * loading, API responses, dynamic config files, and user input.\n *\n * Usage:\n * import { defineConfig } from 'eslint/config';\n * import safeword from 'safeword/eslint';\n * export default defineConfig([\n * ...safeword.configs.recommendedTypeScript,\n * safeword.configs.relaxedTypes,\n * ]);\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages\nexport const relaxedTypesConfig: any = {\n name: 'safeword/relaxed-types',\n rules: {\n // External data (JSON, YAML, API responses) has no compile-time types\n '@typescript-eslint/no-unsafe-argument': 'off',\n '@typescript-eslint/no-unsafe-assignment': 'off',\n '@typescript-eslint/no-unsafe-call': 'off',\n '@typescript-eslint/no-unsafe-member-access': 'off',\n '@typescript-eslint/no-unsafe-return': 'off',\n // Boolean checks on untyped values are valid guards, not unnecessary\n '@typescript-eslint/strict-boolean-expressions': 'off',\n '@typescript-eslint/no-unnecessary-condition': 'off',\n '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',\n // Fallback operators on untyped values are intentional\n '@typescript-eslint/prefer-nullish-coalescing': 'off',\n // Template literals with untyped values are common in logging/output\n '@typescript-eslint/restrict-template-expressions': 'off',\n },\n};\n","/**\n * ESLint configuration for Playwright e2e tests\n *\n * Applies ONLY to e2e test files to avoid conflicts with vitest:\n * - *.e2e.ts files (explicit e2e naming)\n * - Files in e2e/ directories\n *\n * Does NOT apply to regular *.test.ts files (those are vitest).\n *\n * All rules escalated to error (LLMs ignore warnings).\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { createRequire } from 'node:module';\n\nimport { lazyConfigArray } from './lazy.js';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/**\n * Playwright e2e test linting config\n *\n * Based on recommended config with all warns escalated to error.\n *\n * File patterns target only e2e tests to avoid vitest conflicts:\n * - Explicit e2e suffix (e.g., login.e2e.ts)\n * - Test/spec files in e2e directories only\n *\n * Plugin is loaded lazily — only when this config is actually accessed.\n */\nexport const playwrightConfig: any[] = lazyConfigArray(() => {\n const playwrightPlugin = requireFromHere('eslint-plugin-playwright');\n return [\n {\n name: 'safeword/playwright',\n files: ['**/*.e2e.{ts,tsx,js,jsx}', '**/e2e/**/*.{test,spec}.{ts,tsx,js,jsx}'],\n plugins: {\n playwright: playwrightPlugin,\n },\n rules: {\n // From recommended - already at error\n 'playwright/missing-playwright-await': 'error',\n 'playwright/no-focused-test': 'error',\n 'playwright/no-networkidle': 'error',\n 'playwright/no-standalone-expect': 'error',\n 'playwright/no-unsafe-references': 'error',\n 'playwright/no-unused-locators': 'error',\n 'playwright/no-wait-for-navigation': 'error',\n 'playwright/prefer-web-first-assertions': 'error',\n 'playwright/valid-describe-callback': 'error',\n 'playwright/valid-expect': 'error',\n 'playwright/valid-expect-in-promise': 'error',\n 'playwright/valid-test-tags': 'error',\n 'playwright/valid-title': 'error',\n\n // Escalated from warn to error (LLMs ignore warnings)\n 'playwright/expect-expect': 'error',\n 'playwright/max-nested-describe': 'error',\n 'playwright/no-conditional-expect': 'error',\n 'playwright/no-conditional-in-test': 'error',\n 'playwright/no-element-handle': 'error',\n 'playwright/no-eval': 'error',\n 'playwright/no-force-option': 'error',\n 'playwright/no-nested-step': 'error',\n 'playwright/no-page-pause': 'error',\n 'playwright/no-useless-await': 'error',\n 'playwright/no-useless-not': 'error',\n 'playwright/no-wait-for-selector': 'error',\n 'playwright/no-wait-for-timeout': 'error',\n\n 'playwright/no-skipped-test': 'error',\n\n // Relax base rules for test files - each override has documented justification:\n //\n // no-empty-function: Tests often need empty callbacks for mocks/stubs:\n // const mockFn = vi.fn(() => {}); // Valid mock with no implementation\n // await expect(action).rejects.toThrow(); // Empty catch in expect wrapper\n '@typescript-eslint/no-empty-function': 'off',\n //\n // detect-non-literal-fs-filename: Tests read fixtures from known safe paths:\n // const fixture = readFileSync(join(__dirname, 'fixtures', testCase.input));\n // Test fixtures are developer-controlled, not user input.\n 'security/detect-non-literal-fs-filename': 'off',\n //\n // no-unsafe-* rules: Tests legitimately use partial mocks, fixtures, and stubs.\n '@typescript-eslint/no-unsafe-assignment': 'off',\n '@typescript-eslint/no-unsafe-member-access': 'off',\n '@typescript-eslint/no-unsafe-call': 'off',\n '@typescript-eslint/no-unsafe-argument': 'off',\n '@typescript-eslint/no-unsafe-return': 'off',\n //\n // no-nested-functions: Test organization uses nested describe/it patterns.\n 'sonarjs/no-nested-functions': 'off',\n //\n // no-null: Playwright API explicitly uses null in signatures:\n // await page.waitForFunction(() => window.loaded, null, { timeout: 5000 });\n // See: https://playwright.dev/docs/api/class-page#page-wait-for-function\n 'unicorn/no-null': 'off',\n },\n },\n ];\n});\n","/**\n * Recommended ESLint configuration for JavaScript + LLM coding agents\n *\n * This preset bundles and configures multiple ESLint plugins with\n * severity levels optimized for catching common LLM-generated code issues.\n *\n * Philosophy: LLMs ignore warnings, so rules that catch real bugs are at \"error\".\n *\n * For TypeScript projects, use `recommendedTypeScript` instead.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport pluginJsdoc from 'eslint-plugin-jsdoc';\n\nimport { basePlugins, prettierConfig } from './base.js';\n\n/**\n * JavaScript recommended config - core plugins without TypeScript\n *\n * Note: Uses any[] because ESLint plugin types are incompatible across packages.\n * Runtime validation by ESLint ensures correctness.\n */\n\nexport const recommended: any[] = [\n // All base plugins (security, promise, unicorn, etc.)\n ...basePlugins,\n\n // JSDoc - JavaScript needs docs (no type safety net)\n // Using error config - LLMs ignore warnings\n pluginJsdoc.configs['flat/recommended-error'],\n\n // Prettier must be last to disable conflicting rules\n prettierConfig,\n\n // Re-enable curly after prettier (prettier turns it off but we want braces for LLM code)\n {\n rules: {\n curly: 'error', // Force braces on if/else/for/while - LLMs write unsafe single-line blocks\n },\n },\n];\n","/**\n * Recommended ESLint configuration for Next.js + TypeScript + LLM coding agents\n *\n * Extends the React config with Next.js-specific rules:\n * - @next/eslint-plugin-next: Framework rules (Image, Link, Head, etc.)\n *\n * Philosophy: LLMs make Next.js-specific mistakes. All rules at error severity.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { createRequire } from 'node:module';\n\nimport { lazyConfigArray } from './lazy.js';\nimport { recommendedTypeScriptReact } from './recommended-react.js';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/**\n * Next.js-only rules for monorepo scoping\n *\n * Contains ONLY Next.js-specific ESLint rules without React rules.\n * Use with `files:` scoping in monorepos where only some packages use Next.js,\n * while React rules apply to all React packages.\n *\n * Plugin is loaded lazily — only when this config is actually accessed.\n */\nexport const nextOnlyRules: any[] = lazyConfigArray(() => {\n const nextPlugin = requireFromHere('@next/eslint-plugin-next');\n return [\n // Next.js plugin with core-web-vitals config (stricter)\n nextPlugin.configs['core-web-vitals'],\n\n // Escalate ALL remaining warn rules to error (LLMs ignore warnings)\n {\n name: 'safeword/nextjs-rules',\n rules: {\n '@next/next/google-font-display': 'error',\n '@next/next/google-font-preconnect': 'error',\n '@next/next/next-script-for-ga': 'error',\n '@next/next/no-async-client-component': 'error',\n '@next/next/no-before-interactive-script-outside-document': 'error',\n '@next/next/no-css-tags': 'error',\n '@next/next/no-head-element': 'error',\n '@next/next/no-img-element': 'error',\n '@next/next/no-page-custom-font': 'error',\n '@next/next/no-styled-jsx-in-document': 'error',\n '@next/next/no-title-in-document-head': 'error',\n '@next/next/no-typos': 'error',\n '@next/next/no-unwanted-polyfillio': 'error',\n },\n },\n ];\n});\n\n/**\n * Next.js + TypeScript recommended config\n *\n * Extends React config with Next.js-specific rules for catching\n * common LLM mistakes: using <img> instead of <Image>, <a> instead of <Link>.\n *\n * Plugin loads lazily through `nextOnlyRules` — accessing this triggers the\n * Next.js plugin require, but only because the spread reaches into nextOnlyRules.\n */\nexport const recommendedTypeScriptNext: any[] = lazyConfigArray(() => [\n // All React + TypeScript rules\n ...recommendedTypeScriptReact,\n\n // Next.js-only rules (spreading triggers the lazy load above)\n ...nextOnlyRules,\n]);\n","/**\n * Recommended ESLint configuration for React + TypeScript + LLM coding agents\n *\n * Extends the TypeScript config with React-specific rules:\n * - @eslint-react/eslint-plugin: React, JSX, DOM, RSC, and Web API rules\n * - eslint-plugin-react-hooks 7.x: Hook rules + React Compiler diagnostics\n * - Optional eslint-plugin-jsx-a11y: Accessibility rules (strict preset)\n *\n * Philosophy: LLMs make React-specific mistakes. These rules catch them.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport eslintReactPlugin from '@eslint-react/eslint-plugin';\nimport reactHooksPluginImport from 'eslint-plugin-react-hooks';\n\nimport { lazyConfigArray } from './lazy.js';\nimport { optionalRequire } from './optional-dependency.js';\nimport { recommendedTypeScript } from './recommended-typescript.js';\n\n// Type assertion - react-hooks 7.x exports configs but types don't declare it\nconst reactHooksPlugin = reactHooksPluginImport as unknown as {\n configs?: { flat?: { 'recommended-latest'?: any } };\n};\n\nconst eslintReactConfig = eslintReactPlugin as unknown as {\n configs?: {\n 'recommended-typescript'?: any;\n };\n};\n\n// Runtime validation - ensure react-hooks 7.x with flat config support\nconst reactHooksConfig = reactHooksPlugin.configs?.flat?.['recommended-latest'];\nif (!reactHooksConfig) {\n throw new Error(\n 'safeword requires eslint-plugin-react-hooks >= 7.0.0 with flat config support. ' +\n 'Please upgrade react-hooks: npm install eslint-plugin-react-hooks@latest',\n );\n}\n\nconst eslintReactRecommendedTypeScriptConfig =\n eslintReactConfig.configs?.['recommended-typescript'];\nif (!eslintReactRecommendedTypeScriptConfig) {\n throw new Error(\n 'safeword requires @eslint-react/eslint-plugin with recommended-typescript flat config support. ' +\n 'Please upgrade ESLint React: npm install @eslint-react/eslint-plugin@latest',\n );\n}\n\nconst ESLINT_REACT_HOOK_COMPILER_RULES = [\n '@eslint-react/error-boundaries',\n '@eslint-react/exhaustive-deps',\n '@eslint-react/purity',\n '@eslint-react/rules-of-hooks',\n '@eslint-react/set-state-in-effect',\n '@eslint-react/set-state-in-render',\n '@eslint-react/static-components',\n '@eslint-react/unsupported-syntax',\n '@eslint-react/use-memo',\n] as const;\n\nfunction withSeverity(ruleConfig: unknown, severity: 'error' | 'off'): unknown {\n if (Array.isArray(ruleConfig)) return [severity, ...ruleConfig.slice(1)];\n return severity;\n}\n\nfunction normalizeEslintReactRulesToError(config: { rules?: Record<string, unknown> }) {\n const overrides: Record<string, unknown> = {};\n\n const ruleEntries = Object.entries(config.rules ?? {});\n for (const [ruleId, ruleConfig] of ruleEntries) {\n if (ruleId.startsWith('@eslint-react/')) {\n overrides[ruleId] = withSeverity(ruleConfig, 'error');\n }\n }\n\n return overrides;\n}\n\nconst eslintReactRuleOverrides = {\n ...normalizeEslintReactRulesToError(eslintReactRecommendedTypeScriptConfig),\n\n // Keep eslint-plugin-react-hooks as the only source for Hooks and Compiler diagnostics.\n ...Object.fromEntries(ESLINT_REACT_HOOK_COMPILER_RULES.map(ruleId => [ruleId, 'off'])),\n\n // Load-bearing React guardrails retained from the legacy react/* migration.\n '@eslint-react/no-missing-key': 'error', // LLMs forget keys in map()\n '@eslint-react/no-duplicate-key': 'error', // Replaces the duplicate-key coverage in jsx-key\n '@eslint-react/no-direct-mutation-state': 'error', // Critical React bug\n '@eslint-react/jsx-no-children-prop': 'error', // Anti-pattern\n '@eslint-react/dom-no-unsafe-target-blank': 'error', // Security\n '@eslint-react/dom-no-unknown-property': 'error', // class -> className, has autofix\n};\n\ninterface BuildRecommendedTypeScriptReactOptions {\n loadJsxA11yStrictConfig?: () => unknown;\n}\n\nfunction loadJsxA11yStrictConfig(): unknown {\n const jsxA11y = optionalRequire('eslint-plugin-jsx-a11y') as {\n flatConfigs?: { strict?: unknown };\n };\n return jsxA11y?.flatConfigs?.strict;\n}\n\n/**\n * React + TypeScript recommended config\n *\n * Extends TypeScript config with React-specific rules for catching\n * common LLM mistakes: missing keys, hook violations, stale closures.\n *\n * Uses @eslint-react for React/JSX/DOM rules, while retaining the official\n * eslint-plugin-react-hooks rules for Hooks and React Compiler diagnostics.\n *\n * Includes React Compiler rules (v7.x) for detecting purity violations,\n * improper memoization, and other compiler-incompatible patterns.\n */\nexport function buildRecommendedTypeScriptReact({\n loadJsxA11yStrictConfig: loadOptionalJsxA11yStrictConfig = loadJsxA11yStrictConfig,\n}: BuildRecommendedTypeScriptReactOptions = {}): any[] {\n const jsxA11yStrictConfig = loadOptionalJsxA11yStrictConfig();\n\n return [\n // All TypeScript rules (includes base plugins)\n ...recommendedTypeScript,\n\n // React, JSX, DOM, RSC, and Web API rules\n eslintReactRecommendedTypeScriptConfig,\n\n // React Hooks + Compiler rules (v7.x flat config)\n // Using recommended-latest which includes void-use-memo\n reactHooksConfig,\n\n // Accessibility rules - strict preset (all at error level) when installed.\n ...(jsxA11yStrictConfig ? [jsxA11yStrictConfig] : []),\n\n // Escalate warn rules to error + add LLM-critical rules\n {\n name: 'safeword/react-hooks-rules',\n rules: {\n // Escalate default warns to error (LLMs ignore warnings)\n 'react-hooks/exhaustive-deps': 'error', // Default: warn\n 'react-hooks/incompatible-library': 'error', // Default: warn\n 'react-hooks/unsupported-syntax': 'error', // Default: warn\n\n // LLM-critical rules NOT in recommended-latest preset\n 'react-hooks/memoized-effect-dependencies': 'error', // LLMs create unstable refs as deps\n 'react-hooks/no-deriving-state-in-effects': 'error', // LLMs derive state in useEffect\n },\n },\n\n // React rule overrides for TypeScript projects\n {\n name: 'safeword/react-rules',\n rules: eslintReactRuleOverrides,\n },\n ];\n}\n\nexport const recommendedTypeScriptReact: any[] = lazyConfigArray(() =>\n buildRecommendedTypeScriptReact(),\n);\n","/**\n * Recommended ESLint configuration for TypeScript + LLM coding agents\n *\n * Extends the base recommended config with typescript-eslint's\n * strictTypeChecked + stylisticTypeChecked presets.\n *\n * Type-checked rules are critical for LLM code - they catch:\n * - Floating promises (forgot await)\n * - Misused promises (passing promise where value expected)\n * - Unsafe any usage\n * - Incorrect async/await patterns\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { importX } from 'eslint-plugin-import-x';\nimport { configs as tseslintConfigs } from 'typescript-eslint';\n\nimport { basePlugins, prettierConfig } from './base.js';\n\n/**\n * File patterns for TypeScript files.\n * Used for parser options and type-checked rules.\n */\nconst TS_FILES = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];\n\n/**\n * TypeScript recommended config - all base plugins + typescript-eslint strict\n *\n * Requires: tsconfig.json in project root (or configured via languageOptions)\n *\n * Note: Uses any[] because ESLint plugin types are incompatible across packages.\n * Runtime validation by ESLint ensures correctness.\n */\n\nexport const recommendedTypeScript: any[] = [\n // All base plugins (security, promise, unicorn, etc.)\n ...basePlugins,\n\n // TypeScript-specific import config\n importX.flatConfigs.typescript,\n\n // typescript-eslint strict + stylistic (type-checked)\n ...tseslintConfigs.strictTypeChecked,\n ...tseslintConfigs.stylisticTypeChecked,\n\n // Enable projectService for type-checked rules (modern approach, auto-discovers tsconfig)\n {\n name: 'safeword/typescript-parser-options',\n files: TS_FILES,\n languageOptions: {\n parserOptions: {\n projectService: true,\n },\n },\n },\n\n // Disable type-checked rules for non-TS files (no type info available)\n // Includes JS files and .astro files (which use astro-eslint-parser)\n {\n ...tseslintConfigs.disableTypeChecked,\n name: 'safeword/disable-type-checked-for-non-ts',\n files: ['**/*.js', '**/*.mjs', '**/*.cjs', '**/*.jsx', '**/*.astro'],\n },\n\n // No JSDoc for TypeScript - types > docs\n // TypeScript signatures provide better documentation than JSDoc\n\n // TypeScript-specific rule overrides for LLM code\n // Only applies to TS files (JS files don't have type info for these rules)\n {\n name: 'safeword/typescript-rules',\n files: TS_FILES,\n rules: {\n // consistent-type-definitions: Disabled because both interface and type are valid:\n // - interface: Better error messages, extendable, declaration merging\n // - type: Union/intersection types, mapped types, template literals\n // Forcing one over the other limits expressiveness. Teams should choose per-case.\n // See: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces\n '@typescript-eslint/consistent-type-definitions': 'off',\n\n // LLMs use `any` when stuck - force them to use `unknown` instead\n '@typescript-eslint/no-explicit-any': 'error',\n\n // LLMs use truthy checks when they should be explicit\n // This catches bugs like `if (count)` when count could be 0\n '@typescript-eslint/strict-boolean-expressions': [\n 'error',\n {\n allowString: true, // Allow string checks (common pattern)\n allowNumber: false, // Disallow number checks (0 is falsy bug)\n allowNullableObject: true,\n allowNullableBoolean: true,\n allowNullableString: true,\n allowNullableNumber: false,\n allowAny: false,\n },\n ],\n\n // Underscore prefix convention for intentionally unused vars (matches TypeScript behavior)\n '@typescript-eslint/no-unused-vars': [\n 'error',\n {\n argsIgnorePattern: '^_',\n varsIgnorePattern: '^_',\n caughtErrorsIgnorePattern: '^_',\n },\n ],\n\n // Design rules not in strict+stylistic (high LLM value)\n '@typescript-eslint/consistent-type-imports': 'error', // import type { X } for types\n '@typescript-eslint/switch-exhaustiveness-check': 'error', // Missing case in union switch\n '@typescript-eslint/no-shadow': 'error', // Variable shadows outer scope\n '@typescript-eslint/require-array-sort-compare': 'error', // [].sort() needs compareFn\n '@typescript-eslint/no-unused-private-class-members': 'error', // Catch dead code in classes\n\n // Allow numbers in template literals - common for logging/debugging.\n // Integers stringify predictably; floats should use toFixed() for precision.\n '@typescript-eslint/restrict-template-expressions': [\n 'error',\n {\n allowNumber: true,\n allowBoolean: true,\n allowNullish: false, // Avoid \"undefined\" or \"null\" in strings\n allowRegExp: false,\n },\n ],\n },\n },\n\n // Prettier must be last to disable conflicting rules\n prettierConfig,\n\n // Re-enable curly after prettier (prettier turns it off but we want braces for LLM code)\n {\n name: 'safeword/post-prettier',\n rules: {\n curly: 'error', // Force braces on if/else/for/while - LLMs write unsafe single-line blocks\n },\n },\n];\n","/**\n * ESLint configuration for Storybook stories\n *\n * Uses official flat/recommended as base, then:\n * - Upgrades all warn → error (LLMs ignore warnings)\n * - Adds strict rules not in recommended\n *\n * @see https://github.com/storybookjs/eslint-plugin-storybook\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { createRequire } from 'node:module';\n\nimport { lazyConfigArray } from './lazy.js';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/**\n * Storybook story linting config\n *\n * Based on flat/recommended with stricter settings for LLM code generation.\n * Plugin is loaded lazily — only when this config is actually accessed.\n */\nexport const storybookConfig: any[] = lazyConfigArray(() => {\n const storybookPlugin = requireFromHere('eslint-plugin-storybook');\n return [\n // Use official flat/recommended as base (includes plugin setup + file patterns)\n ...storybookPlugin.configs['flat/recommended'],\n\n // Override warnings to errors + add strict rules\n {\n name: 'safeword/storybook-strict',\n files: ['**/*.stories.{ts,tsx,js,jsx,mjs,cjs}', '**/*.story.{ts,tsx,js,jsx,mjs,cjs}'],\n rules: {\n // Upgrade warn → error (LLMs ignore warnings)\n 'storybook/hierarchy-separator': 'error',\n 'storybook/no-redundant-story-name': 'error',\n 'storybook/prefer-pascal-case': 'error',\n\n // Strict rules not in recommended (useful for LLMs)\n 'storybook/csf-component': 'error', // component property should be set\n 'storybook/no-stories-of': 'error', // storiesOf is deprecated\n 'storybook/meta-inline-properties': 'error', // Meta should only have inline properties\n },\n },\n ];\n});\n","/**\n * ESLint configuration for Tailwind CSS projects\n *\n * Uses eslint-plugin-better-tailwindcss for native Tailwind v4 support.\n * All rules enabled at error level (LLMs ignore warnings).\n *\n * Includes 11 rules:\n * - Correctness: no-conflicting-classes, no-unknown-classes, no-restricted-classes\n * - Stylistic: enforce-consistent-class-order, enforce-shorthand-classes, no-duplicate-classes,\n * no-deprecated-classes, enforce-consistent-line-wrapping, no-unnecessary-whitespace,\n * enforce-consistent-variable-syntax, enforce-consistent-important-position\n *\n * @see https://github.com/schoero/eslint-plugin-better-tailwindcss\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { createRequire } from 'node:module';\n\nimport { lazyConfigArray } from './lazy.js';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/** File patterns for Tailwind rules - targets files containing Tailwind classes */\nexport const TAILWIND_FILES = ['**/*.{jsx,tsx,astro,html}'];\n\n/**\n * Tailwind config — plugin is loaded lazily, only when this config is accessed.\n */\nexport const tailwindConfig: any[] = lazyConfigArray(() => {\n const eslintPluginBetterTailwindcss = requireFromHere('eslint-plugin-better-tailwindcss');\n return [\n {\n name: 'safeword/tailwind',\n files: TAILWIND_FILES,\n plugins: {\n 'better-tailwindcss': eslintPluginBetterTailwindcss,\n },\n rules: {\n // Correctness rules - catch LLM mistakes\n 'better-tailwindcss/no-conflicting-classes': 'error',\n 'better-tailwindcss/no-unknown-classes': 'error',\n 'better-tailwindcss/no-restricted-classes': 'error', // no-op by default, configurable\n\n // Stylistic rules - enforce consistency\n 'better-tailwindcss/enforce-consistent-class-order': 'error',\n 'better-tailwindcss/enforce-consistent-line-wrapping': 'error',\n 'better-tailwindcss/enforce-consistent-variable-syntax': 'error',\n 'better-tailwindcss/enforce-consistent-important-position': 'error',\n 'better-tailwindcss/enforce-shorthand-classes': 'error',\n 'better-tailwindcss/no-duplicate-classes': 'error',\n 'better-tailwindcss/no-deprecated-classes': 'error',\n 'better-tailwindcss/no-unnecessary-whitespace': 'error',\n },\n },\n ];\n});\n","/**\n * ESLint configuration for TanStack Query\n *\n * Enforces best practices for TanStack Query (React Query).\n * All 7 rules at error severity - LLMs have no valid reason to violate these.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { createRequire } from 'node:module';\n\nimport { lazyConfigArray } from './lazy.js';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/**\n * TanStack Query linting config\n *\n * All rules at error severity:\n * - exhaustive-deps: Missing query key deps → stale cached data\n * - stable-query-client: Client in component → infinite re-render loop\n * - no-void-query-fn: No return → undefined cached\n * - no-rest-destructuring: ...rest loses reactivity\n * - no-unstable-deps: New function each render → unnecessary refetches\n * - infinite-query-property-order: Wrong order → TypeScript can't infer types\n * - mutation-property-order: Wrong order → TypeScript can't infer types\n *\n * Plugin is loaded lazily — only when this config is actually accessed.\n */\nexport const tanstackQueryConfig: any[] = lazyConfigArray(() => {\n const tanstackQueryPlugin = requireFromHere('@tanstack/eslint-plugin-query');\n return [\n {\n name: 'safeword/tanstack-query',\n plugins: {\n '@tanstack/query': tanstackQueryPlugin,\n },\n rules: {\n '@tanstack/query/exhaustive-deps': 'error',\n '@tanstack/query/stable-query-client': 'error',\n '@tanstack/query/no-void-query-fn': 'error',\n '@tanstack/query/no-rest-destructuring': 'error',\n '@tanstack/query/no-unstable-deps': 'error',\n '@tanstack/query/infinite-query-property-order': 'error',\n '@tanstack/query/mutation-property-order': 'error',\n },\n },\n ];\n});\n","/**\n * ESLint configuration for Turborepo projects\n *\n * Ensures environment variables used in code are declared in turbo.json\n * for proper cache invalidation.\n *\n * @see https://turbo.build/repo/docs/reference/eslint-plugin-turbo\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport { createRequire } from 'node:module';\n\nimport { lazyConfigArray } from './lazy.js';\n\nconst requireFromHere = createRequire(import.meta.url);\n\n/**\n * Turborepo env var validation config\n *\n * Uses official flat/recommended preset (already at error severity).\n * Catches undeclared env vars that would break Turborepo caching.\n * Plugin is loaded lazily — only when this config is actually accessed.\n */\nexport const turboConfig: any[] = lazyConfigArray(() => {\n const turboPlugin = requireFromHere('eslint-plugin-turbo');\n return [turboPlugin.configs?.['flat/recommended']].filter(Boolean);\n});\n","import { globalIgnores } from 'eslint/config';\n\n/**\n * Global ignore patterns for safeword's vendored content.\n *\n * Downstream consumers spread this into their `eslint.config.mjs` so that\n * their lint run skips files safeword installed under `.safeword/` and the\n * generated `.dependency-cruiser.cjs`. Without this, strict configs (e.g.,\n * `security/*` rules elevated to error) flag many false positives inside\n * vendored hook scripts that legitimately compute filesystem paths.\n *\n * Uses `globalIgnores()` (the explicit directory-pattern primitive per\n * ESLint's March 2025 guidance) rather than the older \"ignores-only object\"\n * form, so the block stays a global ignore even if it later gains keys.\n */\nexport const vendoredIgnoresConfig = [globalIgnores(['.safeword/**', '.dependency-cruiser.cjs'])];\n","/**\n * ESLint configuration for Vitest tests\n *\n * Applies to test files and test infrastructure:\n * - *.test.ts, *.test.tsx, *.spec.ts, *.spec.tsx\n * - tests/** /*.ts (test helpers, fixtures, integration setup)\n * - e2e/** /*.ts (end-to-end test infrastructure)\n *\n * Test infrastructure deliberately gets a laxer rule set than production\n * code — security rules target user-input attack surface that doesn't\n * exist in CI-only test runs, and common test patterns (throwaway\n * fixtures, describe-scoped helpers, regex-heavy assertions) trip\n * production-focused rules without finding real bugs.\n *\n * Enforces test best practices for LLM-generated tests.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport vitestPlugin from '@vitest/eslint-plugin';\n\nimport { TEST_FILE_GLOBS } from './test-file-globs.js';\nimport { SLEEP_RESTRICTED_SYNTAX } from './test-integrity-syntax.js';\n\n/**\n * Vitest test linting config\n *\n * Includes recommended rules plus no-focused-tests.\n * All rules at error severity.\n */\nexport const vitestConfig: any[] = [\n {\n name: 'safeword/vitest',\n files: [...TEST_FILE_GLOBS],\n plugins: {\n vitest: vitestPlugin,\n },\n rules: {\n // Recommended rules (all at error)\n // Allow expect* helper functions (e.g., expectErrorSeverity) as assertion functions\n 'vitest/expect-expect': ['error', { assertFunctionNames: ['expect', 'expect*'] }],\n 'vitest/no-identical-title': 'error',\n 'vitest/no-commented-out-tests': 'error',\n 'vitest/valid-title': 'error',\n 'vitest/valid-expect': ['error', { maxArgs: 2 }], // Allow custom message: expect(value, 'message')\n 'vitest/valid-describe-callback': 'error',\n 'vitest/require-local-test-context-for-concurrent-snapshots': 'error',\n 'vitest/no-import-node-test': 'error',\n\n // Additional strict rules\n 'vitest/no-focused-tests': 'error', // No .only() in CI\n 'vitest/max-nested-describe': ['error', { max: 5 }], // Limit describe nesting depth\n\n // Test-integrity graduation (VFD6X1, #773): testing-guide.md's \"never\n // skip/park tests without approval\" invariant, lint-owned. Unconditional\n // skips (it.skip, xit, xdescribe) need an inline eslint-disable with a\n // reason — that comment IS the auditable approval artifact. Environment-\n // conditional skipIf/runIf stay legal (the rule doesn't match them).\n 'vitest/no-disabled-tests': 'error',\n\n // Deferred-test marker (review hardening of VFD6X1): the plugin rule —\n // NOT a custom selector — because it also catches chained modifiers\n // (it.concurrent + marker), a verified bypass of the direct-only\n // selector this replaced. The plugin docs suggest warn severity; error-\n // with-disable-comment is deliberately safeword's policy (LLMs ignore\n // warnings, and the disable comment is the auditable approval).\n 'vitest/warn-todo': 'error',\n\n // No-arbitrary-sleep graduation (VFD6X1, #773): the guide's \"poll, never\n // sleep\" rule for the vitest lane (the playwright lane already has\n // no-wait-for-timeout). Shared selectors — see test-integrity-syntax.ts\n // for the idiom list and its documented accepted false positive.\n 'no-restricted-syntax': ['error', ...SLEEP_RESTRICTED_SYNTAX],\n\n // Relax base rules for test files - each override has documented justification:\n //\n // no-empty-function: Tests often need empty callbacks for mocks/stubs:\n // const mockFn = vi.fn(() => {}); // Valid mock with no implementation\n // await expect(action).rejects.toThrow(); // Empty catch in expect wrapper\n '@typescript-eslint/no-empty-function': 'off',\n //\n // detect-non-literal-fs-filename: Tests read fixtures from known safe paths:\n // const fixture = readFileSync(join(__dirname, 'fixtures', testCase.input));\n // Test fixtures are developer-controlled, not user input.\n 'security/detect-non-literal-fs-filename': 'off',\n //\n // no-unsafe-* rules: Tests legitimately use partial mocks, fixtures, and stubs\n // that trigger these rules. The typescript-eslint team acknowledges this:\n // \"If your project frequently stubs objects in test files, consider disabling.\"\n // See: https://typescript-eslint.io/rules/no-unsafe-member-access/\n '@typescript-eslint/no-unsafe-assignment': 'off',\n '@typescript-eslint/no-unsafe-member-access': 'off',\n '@typescript-eslint/no-unsafe-call': 'off',\n '@typescript-eslint/no-unsafe-argument': 'off',\n '@typescript-eslint/no-unsafe-return': 'off',\n //\n // no-nested-functions: describe/it nesting is standard test organization:\n // describe(\"Foo\", () => { describe(\"bar\", () => { it(\"works\", () => {}) }) })\n // This triggers at level 4+, which is normal for BDD-style tests.\n 'sonarjs/no-nested-functions': 'off',\n //\n // Keep max-nested-callbacks at reasonable threshold for tests.\n // Default is 10; we use 6 to catch excessive nesting early while allowing\n // typical patterns like: describe → it → array.filter → callback.\n 'max-nested-callbacks': ['error', { max: 6 }],\n //\n // The block below was previously project-local to safeword's monorepo\n // (eslint.config.ts cli-tests-override) — promoted to the preset so\n // any safeword customer's tests get the same treatment. Each rule has a\n // documented test-code rationale:\n //\n // no-unused-vars (4 variants): test fixtures intentionally hold setup\n // values that aren't asserted on (const _r = await call(); expect(spy)...)\n // and discard destructured returns (const { ignored, ...rest } = x;).\n 'sonarjs/no-unused-vars': 'off',\n 'sonarjs/no-dead-store': 'off',\n 'sonarjs/unused-import': 'off',\n '@typescript-eslint/no-unused-vars': 'off',\n //\n // slow-regex + detect-unsafe-regex: ReDoS rules target user-input\n // attack surface. Test regexes run on test-controlled inputs in\n // short-lived CI processes — not a real threat. Forcing tests\n // to refactor every chained-quantifier regex produces no security\n // value and degrades test readability.\n 'sonarjs/slow-regex': 'off',\n 'security/detect-unsafe-regex': 'off',\n //\n // no-dupe-disjunctions: tests intentionally use duplicate regex\n // alternatives for readability (/^(foo|foo-bar)/ — explicit about\n // both matched cases).\n 'regexp/no-dupe-disjunctions': 'off',\n //\n // assertions-in-tests: false-positives on vitest's expect() patterns\n // when assertions live in helper functions (e.g., expectErrorMessage).\n 'sonarjs/assertions-in-tests': 'off',\n //\n // consistent-function-scoping: describe-block-scoped helpers are the\n // standard test-organization pattern; hoisting them to file scope\n // fragments the test for marginal benefit.\n 'unicorn/consistent-function-scoping': 'off',\n //\n // no-array-callback-reference: test helpers use method references\n // (expect(arr.map(toIdString))) for clarity.\n 'unicorn/no-array-callback-reference': 'off',\n //\n // publicly-writable-directories: tests legitimately use os.tmpdir()\n // and /tmp for sandboxed-execution fixtures. The threat-model\n // assumption (other processes can clobber) is fine for short-lived\n // test runs.\n 'sonarjs/publicly-writable-directories': 'off',\n //\n // no-alphabetical-sort: tests sort arrays for deterministic assertion\n // ordering (expect(files.sort()).toEqual(...)) — not a code-smell\n // in test contexts.\n 'sonarjs/no-alphabetical-sort': 'off',\n //\n // parameterized-tests (new in sonarjs 4.2.0's recommended set): demands\n // repetitive similar cases collapse into one it.each table. That is a\n // readability *preference*, not a correctness rule — explicit separate\n // cases keep a failure pointing at the exact scenario, which we value over\n // table density. Enforcing it at error-level would force a large,\n // mechanical rewrite of customer test suites for no correctness gain.\n 'sonarjs/parameterized-tests': 'off',\n //\n // explicit-test-skip (new in sonarjs 4.2.0's recommended set): flags a\n // guard that `return`s early instead of calling ctx.skip(). Our\n // data-driven validation tests use `if (!precondition) return` for\n // conditional applicability; whether that reads better as ctx.skip() is a\n // style call, not a test-quality gate we impose on customer suites. (The\n // handful of internal early-return guards are tracked for a separate\n // assert-or-skip tightening pass — off here is not an endorsement of\n // vacuous passes.)\n 'sonarjs/explicit-test-skip': 'off',\n },\n },\n];\n","/**\n * TypeScript Preset\n *\n * ESLint configs, rules, and detection for TypeScript/JavaScript projects.\n * This is the main entry point for the TypeScript language preset.\n *\n * Usage in user's eslint.config.mjs:\n * import { defineConfig } from 'eslint/config';\n * import safeword from 'safeword/eslint';\n * export default defineConfig([\n * ...safeword.configs.recommendedTypeScript,\n * ]);\n *\n * Or with multiple configs:\n * export default defineConfig([\n * ...safeword.configs.recommendedTypeScript,\n * ...safeword.configs.vitest,\n * safeword.configs.cli,\n * ]);\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- ESLint config types are incompatible across plugin packages */\n\nimport type { Rule } from 'eslint';\n\nimport { VERSION } from '../../version.js';\nimport { detect } from './detect.js';\nimport { astroConfig } from './eslint-configs/astro.js';\nimport { prettierConfig } from './eslint-configs/base.js';\nimport { bunTestConfig } from './eslint-configs/bun-test.js';\nimport { cliConfig } from './eslint-configs/overrides-cli.js';\nimport { relaxedTypesConfig } from './eslint-configs/overrides-relaxed-types.js';\nimport { playwrightConfig } from './eslint-configs/playwright.js';\nimport { recommended } from './eslint-configs/recommended.js';\nimport { nextOnlyRules, recommendedTypeScriptNext } from './eslint-configs/recommended-nextjs.js';\nimport { recommendedTypeScriptReact } from './eslint-configs/recommended-react.js';\nimport { recommendedTypeScript } from './eslint-configs/recommended-typescript.js';\nimport { storybookConfig } from './eslint-configs/storybook.js';\nimport { tailwindConfig } from './eslint-configs/tailwind.js';\nimport { tanstackQueryConfig } from './eslint-configs/tanstack-query.js';\nimport { turboConfig } from './eslint-configs/turbo.js';\nimport { vendoredIgnoresConfig } from './eslint-configs/vendored-ignores.js';\nimport { vitestConfig } from './eslint-configs/vitest.js';\nimport { rules } from './eslint-rules/index.js';\n\ninterface SafewordEslint {\n meta: {\n name: string;\n version: string;\n /** Namespace for defineConfig string-extends resolution (e.g. `extends: [\"safeword/cli\"]`) */\n namespace: string;\n };\n configs: {\n recommended: any[];\n recommendedTypeScript: any[];\n recommendedTypeScriptReact: any[];\n recommendedTypeScriptNext: any[];\n /** Next.js-only rules for monorepo file scoping */\n nextOnlyRules: any[];\n astro: any[];\n tailwind: any[];\n tanstackQuery: any[];\n vitest: any[];\n bunTest: any[];\n playwright: any[];\n storybook: any[];\n turbo: any[];\n /** Security rules that are false positives for CLI tools, build tools, and scripts */\n cli: any;\n /** Strict TypeScript rules that conflict with untyped external data (JSON, YAML, APIs) */\n relaxedTypes: any;\n /** Global ignores for safeword's vendored content (`.safeword/**`, `.dependency-cruiser.cjs`). Spread into a downstream `eslint.config.mjs` to skip linting them. */\n vendoredIgnores: any[];\n };\n detect: typeof detect;\n rules: Record<string, Rule.RuleModule>;\n /** eslint-config-prettier, bundled for convenience */\n prettierConfig: any;\n}\n\n/**\n * ESLint plugin structure for TypeScript preset.\n * Can be used directly as an ESLint plugin or via safeword.eslint.\n */\nexport const eslintPlugin: SafewordEslint = {\n meta: {\n name: 'safeword',\n version: VERSION,\n namespace: 'safeword',\n },\n configs: {\n recommended,\n recommendedTypeScript,\n recommendedTypeScriptReact,\n recommendedTypeScriptNext,\n nextOnlyRules,\n astro: astroConfig,\n tailwind: tailwindConfig,\n tanstackQuery: tanstackQueryConfig,\n vitest: vitestConfig,\n bunTest: bunTestConfig,\n playwright: playwrightConfig,\n storybook: storybookConfig,\n turbo: turboConfig,\n cli: cliConfig,\n relaxedTypes: relaxedTypesConfig,\n vendoredIgnores: vendoredIgnoresConfig,\n },\n detect,\n rules,\n prettierConfig,\n};\n\n// Re-export configs for direct access\nexport { detect } from './detect.js';\nexport { astroConfig } from './eslint-configs/astro.js';\nexport { prettierConfig } from './eslint-configs/base.js';\nexport { bunTestConfig } from './eslint-configs/bun-test.js';\nexport { cliConfig } from './eslint-configs/overrides-cli.js';\nexport { relaxedTypesConfig } from './eslint-configs/overrides-relaxed-types.js';\nexport { playwrightConfig } from './eslint-configs/playwright.js';\nexport { recommended } from './eslint-configs/recommended.js';\nexport { nextOnlyRules, recommendedTypeScriptNext } from './eslint-configs/recommended-nextjs.js';\nexport { recommendedTypeScriptReact } from './eslint-configs/recommended-react.js';\nexport { recommendedTypeScript } from './eslint-configs/recommended-typescript.js';\nexport { storybookConfig } from './eslint-configs/storybook.js';\nexport { tailwindConfig } from './eslint-configs/tailwind.js';\nexport { tanstackQueryConfig } from './eslint-configs/tanstack-query.js';\nexport { turboConfig } from './eslint-configs/turbo.js';\nexport { vendoredIgnoresConfig } from './eslint-configs/vendored-ignores.js';\nexport { vitestConfig } from './eslint-configs/vitest.js';\nexport { rules } from './eslint-rules/index.js';\n\n// Default export for `import safeword from \"safeword/eslint\"`\nexport default eslintPlugin;\n"],"mappings":"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","names":["rule","rule","default","createRequire","requireFromHere","createRequire","default","createRequire","importX","importX","default","requireFromHere","createRequire","createRequire","requireFromHere","createRequire","createRequire","requireFromHere","createRequire","createRequire","requireFromHere","createRequire","createRequire","requireFromHere","createRequire","default"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/feature-source.ts","../src/utils/frontmatter.ts"],"sourcesContent":["import { existsSync, readdirSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport type { PhaseAnchorScope } from '../../templates/hooks/lib/phase-provenance.js';\nimport { resolveConfiguredLaneDirectory, resolveTicketsDirectory } from './configured-paths.js';\nimport { readFrontmatterScalar } from './frontmatter.js';\nimport { readFileSafe } from './fs.js';\nimport { toRepoDirectory, toRepoPath } from './repo-path.js';\nimport { WORKSPACE_ROOTS } from './workspaces.js';\n\n/** Read the authoritative ticket slug, falling back for legacy folders. */\nfunction slugForTicket(cwd: string, ticketFolder: string): string {\n const content = readFileSafe(\n nodePath.join(resolveTicketsDirectory(cwd), ticketFolder, 'ticket.md'),\n );\n const slug = readFrontmatterScalar(content, 'slug');\n if (slug !== undefined && slug !== '') return slug;\n\n const dashIndex = ticketFolder.indexOf('-');\n return dashIndex === -1 ? ticketFolder : ticketFolder.slice(dashIndex + 1);\n}\n\n/**\n * Find the executable `.feature` file for a ticket by slug. The search mirrors\n * the Cucumber lane: all feature files under root `features/`, then under each\n * direct workspace package's `features/` directory in stable order.\n */\nexport function findFeatureSourcePath(cwd: string, ticketFolder: string): string | undefined {\n const fileName = `${slugForTicket(cwd, ticketFolder)}.feature`;\n return collectExecutableFeatureFiles(cwd, fileName)[0];\n}\n\n/** Build the ticket-invariant feature ownership policy once per tree/config. */\nexport function createPhaseAnchorEnvironment(\n cwd: string,\n configuredFeatures?: string,\n): Omit<PhaseAnchorScope, 'ticketPath'> {\n const featureRoots = ['features'];\n if (configuredFeatures !== undefined) {\n const configuredRoot = toRepoDirectory(cwd, configuredFeatures);\n if (configuredRoot !== undefined && !featureRoots.includes(configuredRoot)) {\n featureRoots.push(configuredRoot);\n }\n }\n return { featureRoots, workspaceRoots: [...WORKSPACE_ROOTS] };\n}\n\n/** Build feature ownership policy without consulting feature-file existence. */\nexport function createPhaseAnchorScope(\n cwd: string,\n ticketPath: string,\n configuredFeatures?: string,\n): PhaseAnchorScope {\n return {\n ticketPath: toRepoPath(ticketPath),\n ...createPhaseAnchorEnvironment(cwd, configuredFeatures),\n };\n}\n\nexport function collectExecutableFeatureFiles(cwd: string, fileName?: string): string[] {\n return collectExecutableFeatureDirectories(cwd).flatMap(directory =>\n findFeatureFiles(directory, fileName),\n );\n}\n\n/** Whether Safeword's built-in feature locations already contain a real suite. */\nexport function hasDefaultExecutableFeatureFiles(cwd: string): boolean {\n return collectDefaultFeatureDirectories(cwd).some(directory => containsFeatureFile(directory));\n}\n\n/**\n * Return every directory that contributes executable feature files.\n *\n * Keep consumers that need feature-lane coverage (for example, audit checks)\n * on this shared enumeration rather than reconstructing workspace discovery.\n */\nexport function collectExecutableFeatureDirectories(cwd: string): string[] {\n const directories = collectDefaultFeatureDirectories(cwd);\n\n // paths.features AUGMENTS the defaults (ticket 56JCFZ) — relocated/host\n // lanes become readable without abandoning root features/.\n const configured = resolveConfiguredLaneDirectory(cwd, 'features');\n if (configured !== undefined && !directories.includes(configured)) {\n directories.push(configured);\n }\n return directories;\n}\n\nfunction collectDefaultFeatureDirectories(cwd: string): string[] {\n return [\n nodePath.join(cwd, 'features'),\n ...WORKSPACE_ROOTS.flatMap(root =>\n collectWorkspaceFeatureDirectories(nodePath.join(cwd, root)),\n ),\n ];\n}\n\nfunction collectWorkspaceFeatureDirectories(workspaceDirectory: string): string[] {\n if (!existsSync(workspaceDirectory)) return [];\n return readdirSync(workspaceDirectory, { withFileTypes: true })\n .filter(entry => entry.isDirectory())\n .map(entry => nodePath.join(workspaceDirectory, entry.name, 'features'));\n}\n\nfunction findFeatureFiles(directory: string, fileName?: string): string[] {\n let entries;\n try {\n entries = readdirSync(directory, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const matches: string[] = [];\n for (const entry of entries) {\n const absolute = nodePath.join(directory, entry.name);\n if (entry.isDirectory()) {\n matches.push(...findFeatureFiles(absolute, fileName));\n } else if (entry.isFile() && isMatchingFeatureFile(entry.name, fileName)) {\n matches.push(absolute);\n }\n }\n return matches.toSorted((a, b) => a.localeCompare(b));\n}\n\nfunction containsFeatureFile(directory: string): boolean {\n let entries;\n try {\n entries = readdirSync(directory, { withFileTypes: true });\n } catch {\n return false;\n }\n return entries.some(entry => {\n const absolute = nodePath.join(directory, entry.name);\n return entry.isDirectory()\n ? containsFeatureFile(absolute)\n : entry.isFile() && entry.name.endsWith('.feature');\n });\n}\n\nfunction isMatchingFeatureFile(entryName: string, fileName: string | undefined): boolean {\n if (!entryName.endsWith('.feature')) return false;\n return fileName === undefined || entryName === fileName;\n}\n","/** Read one scalar field from a `---`-fenced YAML frontmatter block. */\nexport function readFrontmatterScalar(\n content: string | undefined,\n field: string,\n): string | undefined {\n const lines = content?.split(/\\r?\\n/) ?? [];\n if (lines[0] !== '---') return undefined;\n const prefix = `${field}:`;\n for (const line of lines.slice(1)) {\n if (line === '---') return undefined;\n if (!line.startsWith(prefix)) continue;\n const value = line.slice(prefix.length).trim();\n return value === '' ? undefined : value;\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-protocol/review-presentation.ts","../src/cli-protocol/result.ts"],"sourcesContent":["import type { CliResult } from './result.js';\n\ntype ReviewCoverage = 'standard' | 'independent' | 'incomplete';\ntype ReviewVerdict = 'approve' | 'request_changes';\n\nconst REVIEW_AGENTS = new Set(['claude', 'codex']);\nconst REVIEW_AUTHORS = new Set(['claude', 'codex', 'cursor']);\nconst REPLACED_REVIEW_FINDINGS = new Set(['REVIEW_INDEPENDENCE', 'REVIEW_INDEPENDENCE_DEGRADED']);\nconst RETRYABLE_REVIEW_FAILURES = new Set([\n 'timed_out',\n 'process_failed',\n 'invalid_output',\n 'source_changed',\n]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction reviewCoverage(data: Record<string, unknown>): ReviewCoverage {\n const author = data.author_agent;\n const reviewer = data.actual_reviewer;\n if (typeof author !== 'string' || !REVIEW_AUTHORS.has(author) || typeof reviewer !== 'string') {\n return 'incomplete';\n }\n if (data.independence === 'degraded' && reviewer === author) return 'standard';\n if (data.independence === 'cross-agent' && REVIEW_AGENTS.has(reviewer) && reviewer !== author) {\n return 'independent';\n }\n return 'incomplete';\n}\n\nfunction reviewVerdict(data: Record<string, unknown>): ReviewVerdict | undefined {\n const output = data.reviewer_output;\n if (!isRecord(output)) return undefined;\n if (\n typeof output.reviewer_agent !== 'string' ||\n !REVIEW_AUTHORS.has(output.reviewer_agent) ||\n output.reviewer_agent !== data.actual_reviewer\n ) {\n return undefined;\n }\n return output.verdict === 'approve' || output.verdict === 'request_changes'\n ? output.verdict\n : undefined;\n}\n\nfunction reviewVerdictMatchesStatus(status: unknown, verdict: ReviewVerdict | undefined): boolean {\n if (status === 'approved') return verdict === 'approve';\n if (status === 'changes_requested') return verdict === 'request_changes';\n return status === 'blocked' && verdict !== undefined;\n}\n\nfunction reviewPolicyMatchesStatus(data: Record<string, unknown>): boolean {\n if (data.status === 'blocked') {\n return data.review_policy === 'prefer' || data.review_policy === 'require';\n }\n return (\n (data.status === 'approved' || data.status === 'changes_requested') &&\n data.review_policy === undefined\n );\n}\n\nfunction reviewStateMatchesStatus(state: CliResult['state'], status: unknown): boolean {\n if (status === 'approved') return state === 'healthy';\n return (status === 'changes_requested' || status === 'blocked') && state === 'action_required';\n}\n\nfunction isConsistentReviewResult(\n data: Record<string, unknown>,\n state: CliResult['state'],\n verdict: ReviewVerdict | undefined,\n): boolean {\n return (\n reviewStateMatchesStatus(state, data.status) &&\n reviewPolicyMatchesStatus(data) &&\n reviewVerdictMatchesStatus(data.status, verdict)\n );\n}\n\nfunction incompleteCoverageLine(data: Record<string, unknown>): string {\n return data.status === 'blocked' && data.review_policy === 'require'\n ? 'Review incomplete — required independent coverage is unsatisfied.'\n : 'Review incomplete.';\n}\n\nfunction blockedReviewCoverageLine(\n data: Record<string, unknown>,\n coverage: ReviewCoverage,\n verdict: ReviewVerdict,\n): string {\n if (coverage === 'standard' && data.review_policy === 'require') {\n return verdict === 'request_changes'\n ? 'Review blocked — changes requested with standard coverage; required independent coverage is unsatisfied.'\n : 'Review blocked — standard coverage achieved; required independent coverage is unsatisfied.';\n }\n return incompleteCoverageLine(data);\n}\n\nfunction reviewCoverageLine(data: Record<string, unknown>, state: CliResult['state']): string {\n const coverage = reviewCoverage(data);\n const status = data.status;\n const verdict = reviewVerdict(data);\n\n if (!reviewStateMatchesStatus(state, status)) return incompleteCoverageLine(data);\n if (!reviewPolicyMatchesStatus(data)) return 'Review incomplete.';\n if (!reviewVerdictMatchesStatus(status, verdict)) return incompleteCoverageLine(data);\n if (status === 'blocked' && verdict !== undefined) {\n return blockedReviewCoverageLine(data, coverage, verdict);\n }\n if (coverage === 'incomplete') return 'Review incomplete.';\n if (verdict === 'request_changes') return `Review changes requested — ${coverage} coverage.`;\n return `Review complete — ${coverage} coverage.`;\n}\n\nfunction isCompletedStandardReview(\n data: Record<string, unknown>,\n state: CliResult['state'],\n): boolean {\n const verdict = reviewVerdict(data);\n return (\n data.status === 'approved' &&\n isConsistentReviewResult(data, state, verdict) &&\n reviewCoverage(data) === 'standard'\n );\n}\n\nfunction reviewUpgradeSuggestion(\n data: Record<string, unknown>,\n state: CliResult['state'],\n): string | undefined {\n if (!isCompletedStandardReview(data, state)) return undefined;\n const author = data.author_agent;\n const reviewer = data.assigned_reviewer;\n if (\n typeof author !== 'string' ||\n typeof reviewer !== 'string' ||\n !REVIEW_AGENTS.has(reviewer) ||\n reviewer === author\n ) {\n return undefined;\n }\n const label = `${reviewer.charAt(0).toUpperCase()}${reviewer.slice(1)}`;\n return suggestionForFailure(data.preferred_failure, label);\n}\n\nfunction suggestionForFailure(failure: unknown, label: string): string | undefined {\n if (failure === 'not_installed') {\n return `To add independent coverage, install or update ${label}, then retry review.`;\n }\n if (failure === 'unsupported') {\n return `To add independent coverage, update ${label}, then retry review.`;\n }\n if (failure === 'probe_timed_out' || failure === 'launch_failed') {\n return `To add independent coverage, run ${label} --help to diagnose it, then retry review.`;\n }\n if (failure === 'not_authenticated') {\n return `To add independent coverage, sign in to ${label}, then retry review.`;\n }\n if (RETRYABLE_REVIEW_FAILURES.has(String(failure))) {\n return `To add independent coverage, retry ${label} review.`;\n }\n return undefined;\n}\n\nexport function reviewResultLines(\n result: CliResult,\n options: { verbose?: boolean },\n): string[] | undefined {\n if (!isRecord(result.data) || result.data.command !== 'review run') return undefined;\n const messages = result.findings\n .filter(finding => !REPLACED_REVIEW_FINDINGS.has(finding.code))\n .map(finding => finding.message);\n messages.push(...result.errors.map(error => error.message));\n const lines = [reviewCoverageLine(result.data, result.state), ...new Set(messages)];\n if (options.verbose === true) {\n const suggestion = reviewUpgradeSuggestion(result.data, result.state);\n if (suggestion !== undefined) lines.push(suggestion);\n }\n return lines;\n}\n","import { reviewResultLines } from './review-presentation.js';\n\ntype ResultState = 'healthy' | 'changed' | 'action_required' | 'failed';\n\nexport interface Finding {\n readonly code: string;\n readonly message: string;\n readonly severity: 'info' | 'warning' | 'error';\n readonly detail?: string;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\ninterface ResultError {\n readonly code: string;\n readonly message: string;\n readonly retryable: boolean;\n readonly detail?: string;\n}\n\ninterface CommandNextAction {\n readonly command: string;\n readonly mutates: boolean;\n readonly requiresHuman: boolean;\n}\n\ninterface HumanNextAction {\n readonly kind: 'human';\n readonly instruction: string;\n readonly mutates: false;\n readonly requiresHuman: true;\n}\n\nexport type NextAction = CommandNextAction | HumanNextAction;\n\ninterface RecoveryAction {\n readonly command: string;\n readonly description: string;\n readonly requiresHuman: boolean;\n}\n\nexport interface Effect {\n readonly kind: string;\n readonly target: string;\n readonly operation?: string;\n}\n\nexport interface Effects {\n readonly files: readonly Effect[];\n readonly packages: readonly Effect[];\n readonly configuration: readonly Effect[];\n readonly network: readonly Effect[];\n readonly destructive: readonly Effect[];\n}\n\nexport interface CliResult {\n readonly schemaVersion: 1;\n readonly ok: boolean;\n readonly state: ResultState;\n readonly changed: boolean;\n readonly findings: readonly Finding[];\n readonly effects: Effects;\n readonly errors: readonly ResultError[];\n readonly recovery: readonly RecoveryAction[];\n readonly nextActions: readonly NextAction[];\n /** Process status for commands that must preserve a delegated program's exit. */\n readonly exitCode?: number;\n readonly presentation?: {\n readonly kind: 'raw';\n readonly body: string;\n };\n readonly data?: unknown;\n}\n\ninterface ResultInput {\n readonly state: ResultState;\n readonly changed?: boolean;\n readonly findings?: readonly Finding[];\n readonly effects?: Partial<Effects>;\n readonly errors?: readonly ResultError[];\n readonly recovery?: readonly RecoveryAction[];\n readonly nextActions?: readonly NextAction[];\n readonly exitCode?: number;\n readonly presentation?: NonNullable<CliResult['presentation']>;\n readonly data?: unknown;\n}\n\nconst EMPTY_EFFECTS: Effects = {\n files: [],\n packages: [],\n configuration: [],\n network: [],\n destructive: [],\n};\n\nexport function combineEffects(groups: readonly Partial<Effects>[]): Effects {\n return {\n files: groups.flatMap(effects => effects.files ?? []),\n packages: groups.flatMap(effects => effects.packages ?? []),\n configuration: groups.flatMap(effects => effects.configuration ?? []),\n network: groups.flatMap(effects => effects.network ?? []),\n destructive: groups.flatMap(effects => effects.destructive ?? []),\n };\n}\n\n/** Aggregate state precedence shared by commands that combine several results. */\nexport function combinedResultState(results: readonly CliResult[]): CliResult['state'] {\n if (results.some(result => result.state === 'failed')) return 'failed';\n if (results.some(result => result.state === 'action_required')) return 'action_required';\n if (results.some(result => result.state === 'changed')) return 'changed';\n return 'healthy';\n}\n\nexport function createResult(input: ResultInput): CliResult {\n return {\n schemaVersion: 1,\n ok: input.state !== 'failed',\n state: input.state,\n changed: input.changed ?? input.state === 'changed',\n findings: input.findings ?? [],\n effects: { ...EMPTY_EFFECTS, ...input.effects },\n errors: input.errors ?? [],\n recovery: input.recovery ?? [],\n nextActions: input.nextActions ?? [],\n ...(input.exitCode !== undefined && { exitCode: input.exitCode }),\n ...(input.presentation !== undefined && { presentation: input.presentation }),\n ...(input.data !== undefined && { data: input.data }),\n };\n}\n\nexport function withDeprecation(\n result: CliResult,\n legacy: string,\n replacement: string,\n compatibility: {\n readonly introducedIn: string;\n readonly retention: 'indefinite';\n readonly redundantOptions?: readonly {\n readonly key: string;\n readonly flag: string;\n readonly replacement: string;\n }[];\n },\n options: Readonly<Record<string, unknown>> = {},\n): CliResult {\n const redundantOptionFindings: Finding[] = (compatibility.redundantOptions ?? [])\n .filter(option => options[option.key] === true)\n .map(option => ({\n code: 'CLI_OPTION_REDUNDANT',\n message: `\\`${option.flag}\\` is accepted for compatibility but has no effect; use \\`${option.replacement}\\`.`,\n severity: 'warning',\n metadata: {\n option: option.flag,\n replacement: option.replacement,\n retention: compatibility.retention,\n },\n }));\n return {\n ...result,\n findings: [\n ...result.findings,\n {\n code: 'CLI_ALIAS_DEPRECATED',\n message: `\\`${legacy}\\` is deprecated; use \\`${replacement}\\`.`,\n severity: 'warning',\n metadata: {\n replacement,\n introduced_in: compatibility.introducedIn,\n retention: compatibility.retention,\n },\n },\n ...redundantOptionFindings,\n ],\n };\n}\n\nexport function exitStatusFor(result: CliResult): number {\n if (result.exitCode !== undefined) return result.exitCode;\n if (result.state === 'failed') return 1;\n if (result.state === 'action_required') return 2;\n return 0;\n}\n\nfunction toWireResult(result: CliResult): Record<string, unknown> {\n return {\n schema_version: result.schemaVersion,\n ok: result.ok,\n state: result.state,\n changed: result.changed,\n findings: result.findings.map(({ code, message, severity, detail, metadata }) => ({\n code,\n message,\n severity,\n ...(detail !== undefined && { detail }),\n ...(metadata !== undefined && { metadata }),\n })),\n effects: result.effects,\n errors: result.errors.map(({ code, message, retryable, detail }) => ({\n code,\n message,\n retryable,\n ...(detail !== undefined && { detail }),\n })),\n recovery: result.recovery.map(({ command, description, requiresHuman }) => ({\n command,\n description,\n requires_human: requiresHuman,\n })),\n next_actions: result.nextActions.map(action =>\n 'command' in action\n ? {\n command: action.command,\n mutates: action.mutates,\n requires_human: action.requiresHuman,\n }\n : {\n kind: action.kind,\n instruction: action.instruction,\n mutates: action.mutates,\n requires_human: action.requiresHuman,\n },\n ),\n ...(result.data !== undefined && { data: result.data }),\n };\n}\n\nexport function renderJsonResult(result: CliResult): string {\n return JSON.stringify(toWireResult(result));\n}\n\nconst VERDICTS: Readonly<Record<ResultState, string>> = {\n healthy: 'Healthy',\n changed: 'Complete',\n action_required: 'Needs attention',\n failed: 'Failed',\n};\n\nfunction uniqueMessages(result: CliResult): string[] {\n const messages = result.findings.map(finding => finding.message);\n messages.push(...result.errors.map(error => error.message));\n return [...new Set(messages)];\n}\n\nfunction suppressHumanOutput(result: CliResult, options: { quiet?: boolean }): boolean {\n return options.quiet === true && result.state === 'healthy';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction effectLines(category: string, effects: unknown): string[] {\n if (!Array.isArray(effects)) return [];\n return effects.flatMap(effect =>\n isRecord(effect) && typeof effect.kind === 'string' && typeof effect.target === 'string'\n ? [`${category}: ${effect.kind} ${effect.target}`]\n : [],\n );\n}\n\nfunction nextActionLabel(action: NextAction): string {\n return 'command' in action ? action.command : action.instruction;\n}\n\nfunction plannedEffectLines(data: unknown): string[] {\n if (!isRecord(data)) return [];\n const plan = data.plan;\n if (!isRecord(plan)) return [];\n const effects = plan.effects;\n if (!isRecord(effects)) return [];\n const categories = ['files', 'packages', 'configuration', 'network', 'destructive'];\n const lines = categories.flatMap(category => effectLines(category, effects[category]));\n return lines.length === 0 ? [] : ['Planned effects:', ...lines];\n}\n\nconst SURFACE_LABELS: Readonly<Record<string, string>> = {\n project: 'Project',\n claude: 'Claude',\n codex: 'Codex',\n cursor: 'Cursor',\n};\n\nconst SURFACE_OUTCOMES: Readonly<Record<string, string>> = {\n healthy: 'ready',\n changed: 'updated',\n action_required: 'needs attention',\n failed: 'failed',\n};\n\n/** Labelled surfaces of a `command`-tagged result payload, or [] when it is not that command. */\nfunction labelledSurfaces(\n data: unknown,\n command: string,\n): { readonly label: string; readonly surface: Record<string, unknown> }[] {\n if (!isRecord(data) || data.command !== command || !Array.isArray(data.surfaces)) return [];\n return data.surfaces.flatMap(surface => {\n if (!isRecord(surface) || typeof surface.name !== 'string') return [];\n const label = SURFACE_LABELS[surface.name];\n return label === undefined ? [] : [{ label, surface }];\n });\n}\n\nfunction installSurfaceLines(data: unknown): string[] {\n return labelledSurfaces(data, 'install').flatMap(({ label, surface }) => {\n if (surface.selected === false) return [`${label}: not selected`];\n const outcome = typeof surface.state === 'string' ? SURFACE_OUTCOMES[surface.state] : undefined;\n return outcome === undefined ? [] : [`${label}: ${outcome}`];\n });\n}\n\nfunction installActivationLines(data: unknown): string[] {\n return labelledSurfaces(data, 'install').flatMap(({ label, surface }) => {\n if (!Array.isArray(surface.activation_actions)) return [];\n return surface.activation_actions.flatMap(action =>\n typeof action === 'string' ? [`${label} activation: ${action}`] : [],\n );\n });\n}\n\nfunction doctorDiagnosticCauses(data: unknown): ReadonlySet<string> {\n if (!isRecord(data) || data.command !== 'doctor' || !Array.isArray(data.diagnostics)) {\n return new Set();\n }\n return new Set(\n data.diagnostics.flatMap(diagnostic =>\n isRecord(diagnostic) && typeof diagnostic.cause === 'string' ? [diagnostic.cause] : [],\n ),\n );\n}\n\nfunction doctorDiagnosticLines(data: unknown): string[] {\n if (!isRecord(data) || data.command !== 'doctor' || !Array.isArray(data.coverage)) return [];\n\n const coverage = data.coverage.flatMap(item => {\n if (!isRecord(item) || typeof item.surface !== 'string') return [];\n const label = SURFACE_LABELS[item.surface] ?? item.surface;\n const outcome = typeof item.state === 'string' ? SURFACE_OUTCOMES[item.state] : undefined;\n const evidence = isRecord(item.evidence)\n ? Object.entries(item.evidence)\n .filter((entry): entry is [string, string | number | boolean] =>\n ['string', 'number', 'boolean'].includes(typeof entry[1]),\n )\n .map(([key, value]) => `${key.replaceAll('_', ' ')}=${String(value)}`)\n : [];\n const evidenceSuffix = evidence.length === 0 ? '' : ` (${evidence.join(', ')})`;\n return [`- ${label}: ${outcome ?? 'unknown'}${evidenceSuffix}`];\n });\n const diagnostics = Array.isArray(data.diagnostics)\n ? data.diagnostics.flatMap(item => {\n if (\n !isRecord(item) ||\n typeof item.surface !== 'string' ||\n typeof item.code !== 'string' ||\n typeof item.cause !== 'string'\n ) {\n return [];\n }\n const label = SURFACE_LABELS[item.surface] ?? item.surface;\n return [`- ${label} [${item.code}]: ${item.cause}`];\n })\n : [];\n\n return [\n ...(coverage.length === 0 ? [] : ['Diagnostic coverage:', ...coverage]),\n ...(diagnostics.length === 0 ? [] : ['Causes:', ...diagnostics]),\n ];\n}\n\nconst EFFECT_LABELS: Readonly<Record<string, string>> = {\n create: 'Created',\n update: 'Updated',\n delete: 'Deleted',\n install: 'Installed',\n uninstall: 'Uninstalled',\n remove: 'Removed',\n write: 'Written',\n};\n\nfunction completedEffectLines(result: CliResult): string[] {\n const visibleCategories = [\n result.effects.files,\n result.effects.packages,\n result.effects.configuration,\n result.effects.destructive,\n ];\n const lines = visibleCategories.flatMap(effects =>\n effects.map(effect => `${EFFECT_LABELS[effect.kind] ?? 'Changed'}: ${effect.target}`),\n );\n lines.push(\n ...result.effects.network.map(\n effect => `Network: ${effect.operation ?? effect.kind} ${effect.target}`,\n ),\n );\n return lines;\n}\n\nexport function renderHumanResult(\n result: CliResult,\n options: { quiet?: boolean; verbose?: boolean } = {},\n): string {\n const rendered = renderHumanStreams(result, options);\n return rendered.stdout || rendered.stderr;\n}\n\nexport interface HumanResultStreams {\n readonly stdout: string;\n readonly stderr: string;\n}\n\nfunction resultBodyLines(result: CliResult, options: { verbose?: boolean }): string[] {\n const reviewLines = reviewResultLines(result, options);\n if (reviewLines !== undefined) return reviewLines;\n const diagnosticCauses = doctorDiagnosticCauses(result.data);\n const messages = uniqueMessages(result).filter(message => !diagnosticCauses.has(message));\n const lines = [\n VERDICTS[result.state],\n `Changed: ${result.changed ? 'yes' : 'no'}`,\n ...installSurfaceLines(result.data),\n ...installActivationLines(result.data),\n ...doctorDiagnosticLines(result.data),\n ...messages,\n ...plannedEffectLines(result.data),\n ];\n\n if (options.verbose === true) {\n const details = [\n ...result.findings.map(finding => finding.detail),\n ...result.errors.map(error => error.detail),\n ].filter((detail): detail is string => detail !== undefined);\n lines.push(...completedEffectLines(result), ...details);\n }\n return lines;\n}\n\nexport function renderHumanStreams(\n result: CliResult,\n options: { quiet?: boolean; verbose?: boolean } = {},\n): HumanResultStreams {\n if (result.presentation?.kind === 'raw') {\n return {\n stdout: result.presentation.body.replace(/\\n$/, ''),\n stderr: options.quiet === true ? '' : uniqueMessages(result).join('\\n'),\n };\n }\n if (suppressHumanOutput(result, options)) return { stdout: '', stderr: '' };\n\n const lines = resultBodyLines(result, options);\n\n const primaryAction = result.nextActions[0];\n if (primaryAction !== undefined) lines.push(`Next: ${nextActionLabel(primaryAction)}`);\n const body = lines.join('\\n');\n return result.state === 'failed' ? { stdout: '', stderr: body } : { stdout: body, stderr: '' };\n}\n"],"mappings":";AAKA,IAAM,gBAAgB,oBAAI,IAAI,CAAC,UAAU,OAAO,CAAC;AACjD,IAAM,iBAAiB,oBAAI,IAAI,CAAC,UAAU,SAAS,QAAQ,CAAC;AAC5D,IAAM,2BAA2B,oBAAI,IAAI,CAAC,uBAAuB,8BAA8B,CAAC;AAChG,IAAM,4BAA4B,oBAAI,IAAI;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,eAAe,MAA+C;AACrE,QAAM,SAAS,KAAK;AACpB,QAAM,WAAW,KAAK;AACtB,MAAI,OAAO,WAAW,YAAY,CAAC,eAAe,IAAI,MAAM,KAAK,OAAO,aAAa,UAAU;AAC7F,WAAO;AAAA,EACT;AACA,MAAI,KAAK,iBAAiB,cAAc,aAAa,OAAQ,QAAO;AACpE,MAAI,KAAK,iBAAiB,iBAAiB,cAAc,IAAI,QAAQ,KAAK,aAAa,QAAQ;AAC7F,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,cAAc,MAA0D;AAC/E,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,SAAS,MAAM,EAAG,QAAO;AAC9B,MACE,OAAO,OAAO,mBAAmB,YACjC,CAAC,eAAe,IAAI,OAAO,cAAc,KACzC,OAAO,mBAAmB,KAAK,iBAC/B;AACA,WAAO;AAAA,EACT;AACA,SAAO,OAAO,YAAY,aAAa,OAAO,YAAY,oBACtD,OAAO,UACP;AACN;AAEA,SAAS,2BAA2B,QAAiB,SAA6C;AAChG,MAAI,WAAW,WAAY,QAAO,YAAY;AAC9C,MAAI,WAAW,oBAAqB,QAAO,YAAY;AACvD,SAAO,WAAW,aAAa,YAAY;AAC7C;AAEA,SAAS,0BAA0B,MAAwC;AACzE,MAAI,KAAK,WAAW,WAAW;AAC7B,WAAO,KAAK,kBAAkB,YAAY,KAAK,kBAAkB;AAAA,EACnE;AACA,UACG,KAAK,WAAW,cAAc,KAAK,WAAW,wBAC/C,KAAK,kBAAkB;AAE3B;AAEA,SAAS,yBAAyB,OAA2B,QAA0B;AACrF,MAAI,WAAW,WAAY,QAAO,UAAU;AAC5C,UAAQ,WAAW,uBAAuB,WAAW,cAAc,UAAU;AAC/E;AAEA,SAAS,yBACP,MACA,OACA,SACS;AACT,SACE,yBAAyB,OAAO,KAAK,MAAM,KAC3C,0BAA0B,IAAI,KAC9B,2BAA2B,KAAK,QAAQ,OAAO;AAEnD;AAEA,SAAS,uBAAuB,MAAuC;AACrE,SAAO,KAAK,WAAW,aAAa,KAAK,kBAAkB,YACvD,2EACA;AACN;AAEA,SAAS,0BACP,MACA,UACA,SACQ;AACR,MAAI,aAAa,cAAc,KAAK,kBAAkB,WAAW;AAC/D,WAAO,YAAY,oBACf,kHACA;AAAA,EACN;AACA,SAAO,uBAAuB,IAAI;AACpC;AAEA,SAAS,mBAAmB,MAA+B,OAAmC;AAC5F,QAAM,WAAW,eAAe,IAAI;AACpC,QAAM,SAAS,KAAK;AACpB,QAAM,UAAU,cAAc,IAAI;AAElC,MAAI,CAAC,yBAAyB,OAAO,MAAM,EAAG,QAAO,uBAAuB,IAAI;AAChF,MAAI,CAAC,0BAA0B,IAAI,EAAG,QAAO;AAC7C,MAAI,CAAC,2BAA2B,QAAQ,OAAO,EAAG,QAAO,uBAAuB,IAAI;AACpF,MAAI,WAAW,aAAa,YAAY,QAAW;AACjD,WAAO,0BAA0B,MAAM,UAAU,OAAO;AAAA,EAC1D;AACA,MAAI,aAAa,aAAc,QAAO;AACtC,MAAI,YAAY,kBAAmB,QAAO,mCAA8B,QAAQ;AAChF,SAAO,0BAAqB,QAAQ;AACtC;AAEA,SAAS,0BACP,MACA,OACS;AACT,QAAM,UAAU,cAAc,IAAI;AAClC,SACE,KAAK,WAAW,cAChB,yBAAyB,MAAM,OAAO,OAAO,KAC7C,eAAe,IAAI,MAAM;AAE7B;AAEA,SAAS,wBACP,MACA,OACoB;AACpB,MAAI,CAAC,0BAA0B,MAAM,KAAK,EAAG,QAAO;AACpD,QAAM,SAAS,KAAK;AACpB,QAAM,WAAW,KAAK;AACtB,MACE,OAAO,WAAW,YAClB,OAAO,aAAa,YACpB,CAAC,cAAc,IAAI,QAAQ,KAC3B,aAAa,QACb;AACA,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,GAAG,SAAS,OAAO,CAAC,EAAE,YAAY,CAAC,GAAG,SAAS,MAAM,CAAC,CAAC;AACrE,SAAO,qBAAqB,KAAK,mBAAmB,KAAK;AAC3D;AAEA,SAAS,qBAAqB,SAAkB,OAAmC;AACjF,MAAI,YAAY,iBAAiB;AAC/B,WAAO,kDAAkD,KAAK;AAAA,EAChE;AACA,MAAI,YAAY,eAAe;AAC7B,WAAO,uCAAuC,KAAK;AAAA,EACrD;AACA,MAAI,YAAY,qBAAqB,YAAY,iBAAiB;AAChE,WAAO,oCAAoC,KAAK;AAAA,EAClD;AACA,MAAI,YAAY,qBAAqB;AACnC,WAAO,2CAA2C,KAAK;AAAA,EACzD;AACA,MAAI,0BAA0B,IAAI,OAAO,OAAO,CAAC,GAAG;AAClD,WAAO,sCAAsC,KAAK;AAAA,EACpD;AACA,SAAO;AACT;AAEO,SAAS,kBACd,QACA,SACsB;AACtB,MAAI,CAAC,SAAS,OAAO,IAAI,KAAK,OAAO,KAAK,YAAY,aAAc,QAAO;AAC3E,QAAM,WAAW,OAAO,SACrB,OAAO,aAAW,CAAC,yBAAyB,IAAI,QAAQ,IAAI,CAAC,EAC7D,IAAI,aAAW,QAAQ,OAAO;AACjC,WAAS,KAAK,GAAG,OAAO,OAAO,IAAI,WAAS,MAAM,OAAO,CAAC;AAC1D,QAAM,QAAQ,CAAC,mBAAmB,OAAO,MAAM,OAAO,KAAK,GAAG,GAAG,IAAI,IAAI,QAAQ,CAAC;AAClF,MAAI,QAAQ,YAAY,MAAM;AAC5B,UAAM,aAAa,wBAAwB,OAAO,MAAM,OAAO,KAAK;AACpE,QAAI,eAAe,OAAW,OAAM,KAAK,UAAU;AAAA,EACrD;AACA,SAAO;AACT;;;AC9FA,IAAM,gBAAyB;AAAA,EAC7B,OAAO,CAAC;AAAA,EACR,UAAU,CAAC;AAAA,EACX,eAAe,CAAC;AAAA,EAChB,SAAS,CAAC;AAAA,EACV,aAAa,CAAC;AAChB;AAEO,SAAS,eAAe,QAA8C;AAC3E,SAAO;AAAA,IACL,OAAO,OAAO,QAAQ,aAAW,QAAQ,SAAS,CAAC,CAAC;AAAA,IACpD,UAAU,OAAO,QAAQ,aAAW,QAAQ,YAAY,CAAC,CAAC;AAAA,IAC1D,eAAe,OAAO,QAAQ,aAAW,QAAQ,iBAAiB,CAAC,CAAC;AAAA,IACpE,SAAS,OAAO,QAAQ,aAAW,QAAQ,WAAW,CAAC,CAAC;AAAA,IACxD,aAAa,OAAO,QAAQ,aAAW,QAAQ,eAAe,CAAC,CAAC;AAAA,EAClE;AACF;AAGO,SAAS,oBAAoB,SAAmD;AACrF,MAAI,QAAQ,KAAK,YAAU,OAAO,UAAU,QAAQ,EAAG,QAAO;AAC9D,MAAI,QAAQ,KAAK,YAAU,OAAO,UAAU,iBAAiB,EAAG,QAAO;AACvE,MAAI,QAAQ,KAAK,YAAU,OAAO,UAAU,SAAS,EAAG,QAAO;AAC/D,SAAO;AACT;AAEO,SAAS,aAAa,OAA+B;AAC1D,SAAO;AAAA,IACL,eAAe;AAAA,IACf,IAAI,MAAM,UAAU;AAAA,IACpB,OAAO,MAAM;AAAA,IACb,SAAS,MAAM,WAAW,MAAM,UAAU;AAAA,IAC1C,UAAU,MAAM,YAAY,CAAC;AAAA,IAC7B,SAAS,EAAE,GAAG,eAAe,GAAG,MAAM,QAAQ;AAAA,IAC9C,QAAQ,MAAM,UAAU,CAAC;AAAA,IACzB,UAAU,MAAM,YAAY,CAAC;AAAA,IAC7B,aAAa,MAAM,eAAe,CAAC;AAAA,IACnC,GAAI,MAAM,aAAa,UAAa,EAAE,UAAU,MAAM,SAAS;AAAA,IAC/D,GAAI,MAAM,iBAAiB,UAAa,EAAE,cAAc,MAAM,aAAa;AAAA,IAC3E,GAAI,MAAM,SAAS,UAAa,EAAE,MAAM,MAAM,KAAK;AAAA,EACrD;AACF;AAEO,SAAS,gBACd,QACA,QACA,aACA,eASA,UAA6C,CAAC,GACnC;AACX,QAAM,2BAAsC,cAAc,oBAAoB,CAAC,GAC5E,OAAO,YAAU,QAAQ,OAAO,GAAG,MAAM,IAAI,EAC7C,IAAI,aAAW;AAAA,IACd,MAAM;AAAA,IACN,SAAS,KAAK,OAAO,IAAI,6DAA6D,OAAO,WAAW;AAAA,IACxG,UAAU;AAAA,IACV,UAAU;AAAA,MACR,QAAQ,OAAO;AAAA,MACf,aAAa,OAAO;AAAA,MACpB,WAAW,cAAc;AAAA,IAC3B;AAAA,EACF,EAAE;AACJ,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU;AAAA,MACR,GAAG,OAAO;AAAA,MACV;AAAA,QACE,MAAM;AAAA,QACN,SAAS,KAAK,MAAM,2BAA2B,WAAW;AAAA,QAC1D,UAAU;AAAA,QACV,UAAU;AAAA,UACR;AAAA,UACA,eAAe,cAAc;AAAA,UAC7B,WAAW,cAAc;AAAA,QAC3B;AAAA,MACF;AAAA,MACA,GAAG;AAAA,IACL;AAAA,EACF;AACF;AAEO,SAAS,cAAc,QAA2B;AACvD,MAAI,OAAO,aAAa,OAAW,QAAO,OAAO;AACjD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,kBAAmB,QAAO;AAC/C,SAAO;AACT;AAEA,SAAS,aAAa,QAA4C;AAChE,SAAO;AAAA,IACL,gBAAgB,OAAO;AAAA,IACvB,IAAI,OAAO;AAAA,IACX,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,IAChB,UAAU,OAAO,SAAS,IAAI,CAAC,EAAE,MAAM,SAAS,UAAU,QAAQ,SAAS,OAAO;AAAA,MAChF;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,WAAW,UAAa,EAAE,OAAO;AAAA,MACrC,GAAI,aAAa,UAAa,EAAE,SAAS;AAAA,IAC3C,EAAE;AAAA,IACF,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO,OAAO,IAAI,CAAC,EAAE,MAAM,SAAS,WAAW,OAAO,OAAO;AAAA,MACnE;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,WAAW,UAAa,EAAE,OAAO;AAAA,IACvC,EAAE;AAAA,IACF,UAAU,OAAO,SAAS,IAAI,CAAC,EAAE,SAAS,aAAa,cAAc,OAAO;AAAA,MAC1E;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,IAClB,EAAE;AAAA,IACF,cAAc,OAAO,YAAY;AAAA,MAAI,YACnC,aAAa,SACT;AAAA,QACE,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,gBAAgB,OAAO;AAAA,MACzB,IACA;AAAA,QACE,MAAM,OAAO;AAAA,QACb,aAAa,OAAO;AAAA,QACpB,SAAS,OAAO;AAAA,QAChB,gBAAgB,OAAO;AAAA,MACzB;AAAA,IACN;AAAA,IACA,GAAI,OAAO,SAAS,UAAa,EAAE,MAAM,OAAO,KAAK;AAAA,EACvD;AACF;AAEO,SAAS,iBAAiB,QAA2B;AAC1D,SAAO,KAAK,UAAU,aAAa,MAAM,CAAC;AAC5C;AAEA,IAAM,WAAkD;AAAA,EACtD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,QAAQ;AACV;AAEA,SAAS,eAAe,QAA6B;AACnD,QAAM,WAAW,OAAO,SAAS,IAAI,aAAW,QAAQ,OAAO;AAC/D,WAAS,KAAK,GAAG,OAAO,OAAO,IAAI,WAAS,MAAM,OAAO,CAAC;AAC1D,SAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAC9B;AAEA,SAAS,oBAAoB,QAAmB,SAAuC;AACrF,SAAO,QAAQ,UAAU,QAAQ,OAAO,UAAU;AACpD;AAEA,SAASA,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,YAAY,UAAkB,SAA4B;AACjE,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,CAAC;AACrC,SAAO,QAAQ;AAAA,IAAQ,YACrBA,UAAS,MAAM,KAAK,OAAO,OAAO,SAAS,YAAY,OAAO,OAAO,WAAW,WAC5E,CAAC,GAAG,QAAQ,KAAK,OAAO,IAAI,IAAI,OAAO,MAAM,EAAE,IAC/C,CAAC;AAAA,EACP;AACF;AAEA,SAAS,gBAAgB,QAA4B;AACnD,SAAO,aAAa,SAAS,OAAO,UAAU,OAAO;AACvD;AAEA,SAAS,mBAAmB,MAAyB;AACnD,MAAI,CAACA,UAAS,IAAI,EAAG,QAAO,CAAC;AAC7B,QAAM,OAAO,KAAK;AAClB,MAAI,CAACA,UAAS,IAAI,EAAG,QAAO,CAAC;AAC7B,QAAM,UAAU,KAAK;AACrB,MAAI,CAACA,UAAS,OAAO,EAAG,QAAO,CAAC;AAChC,QAAM,aAAa,CAAC,SAAS,YAAY,iBAAiB,WAAW,aAAa;AAClF,QAAM,QAAQ,WAAW,QAAQ,cAAY,YAAY,UAAU,QAAQ,QAAQ,CAAC,CAAC;AACrF,SAAO,MAAM,WAAW,IAAI,CAAC,IAAI,CAAC,oBAAoB,GAAG,KAAK;AAChE;AAEA,IAAM,iBAAmD;AAAA,EACvD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,IAAM,mBAAqD;AAAA,EACzD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,QAAQ;AACV;AAGA,SAAS,iBACP,MACA,SACyE;AACzE,MAAI,CAACA,UAAS,IAAI,KAAK,KAAK,YAAY,WAAW,CAAC,MAAM,QAAQ,KAAK,QAAQ,EAAG,QAAO,CAAC;AAC1F,SAAO,KAAK,SAAS,QAAQ,aAAW;AACtC,QAAI,CAACA,UAAS,OAAO,KAAK,OAAO,QAAQ,SAAS,SAAU,QAAO,CAAC;AACpE,UAAM,QAAQ,eAAe,QAAQ,IAAI;AACzC,WAAO,UAAU,SAAY,CAAC,IAAI,CAAC,EAAE,OAAO,QAAQ,CAAC;AAAA,EACvD,CAAC;AACH;AAEA,SAAS,oBAAoB,MAAyB;AACpD,SAAO,iBAAiB,MAAM,SAAS,EAAE,QAAQ,CAAC,EAAE,OAAO,QAAQ,MAAM;AACvE,QAAI,QAAQ,aAAa,MAAO,QAAO,CAAC,GAAG,KAAK,gBAAgB;AAChE,UAAM,UAAU,OAAO,QAAQ,UAAU,WAAW,iBAAiB,QAAQ,KAAK,IAAI;AACtF,WAAO,YAAY,SAAY,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,OAAO,EAAE;AAAA,EAC7D,CAAC;AACH;AAEA,SAAS,uBAAuB,MAAyB;AACvD,SAAO,iBAAiB,MAAM,SAAS,EAAE,QAAQ,CAAC,EAAE,OAAO,QAAQ,MAAM;AACvE,QAAI,CAAC,MAAM,QAAQ,QAAQ,kBAAkB,EAAG,QAAO,CAAC;AACxD,WAAO,QAAQ,mBAAmB;AAAA,MAAQ,YACxC,OAAO,WAAW,WAAW,CAAC,GAAG,KAAK,gBAAgB,MAAM,EAAE,IAAI,CAAC;AAAA,IACrE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,uBAAuB,MAAoC;AAClE,MAAI,CAACA,UAAS,IAAI,KAAK,KAAK,YAAY,YAAY,CAAC,MAAM,QAAQ,KAAK,WAAW,GAAG;AACpF,WAAO,oBAAI,IAAI;AAAA,EACjB;AACA,SAAO,IAAI;AAAA,IACT,KAAK,YAAY;AAAA,MAAQ,gBACvBA,UAAS,UAAU,KAAK,OAAO,WAAW,UAAU,WAAW,CAAC,WAAW,KAAK,IAAI,CAAC;AAAA,IACvF;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,MAAyB;AACtD,MAAI,CAACA,UAAS,IAAI,KAAK,KAAK,YAAY,YAAY,CAAC,MAAM,QAAQ,KAAK,QAAQ,EAAG,QAAO,CAAC;AAE3F,QAAM,WAAW,KAAK,SAAS,QAAQ,UAAQ;AAC7C,QAAI,CAACA,UAAS,IAAI,KAAK,OAAO,KAAK,YAAY,SAAU,QAAO,CAAC;AACjE,UAAM,QAAQ,eAAe,KAAK,OAAO,KAAK,KAAK;AACnD,UAAM,UAAU,OAAO,KAAK,UAAU,WAAW,iBAAiB,KAAK,KAAK,IAAI;AAChF,UAAM,WAAWA,UAAS,KAAK,QAAQ,IACnC,OAAO,QAAQ,KAAK,QAAQ,EACzB;AAAA,MAAO,CAAC,UACP,CAAC,UAAU,UAAU,SAAS,EAAE,SAAS,OAAO,MAAM,CAAC,CAAC;AAAA,IAC1D,EACC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,IAAI,WAAW,KAAK,GAAG,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,IACvE,CAAC;AACL,UAAM,iBAAiB,SAAS,WAAW,IAAI,KAAK,KAAK,SAAS,KAAK,IAAI,CAAC;AAC5E,WAAO,CAAC,KAAK,KAAK,KAAK,WAAW,SAAS,GAAG,cAAc,EAAE;AAAA,EAChE,CAAC;AACD,QAAM,cAAc,MAAM,QAAQ,KAAK,WAAW,IAC9C,KAAK,YAAY,QAAQ,UAAQ;AAC/B,QACE,CAACA,UAAS,IAAI,KACd,OAAO,KAAK,YAAY,YACxB,OAAO,KAAK,SAAS,YACrB,OAAO,KAAK,UAAU,UACtB;AACA,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ,eAAe,KAAK,OAAO,KAAK,KAAK;AACnD,WAAO,CAAC,KAAK,KAAK,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,EAAE;AAAA,EACpD,CAAC,IACD,CAAC;AAEL,SAAO;AAAA,IACL,GAAI,SAAS,WAAW,IAAI,CAAC,IAAI,CAAC,wBAAwB,GAAG,QAAQ;AAAA,IACrE,GAAI,YAAY,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,WAAW;AAAA,EAChE;AACF;AAEA,IAAM,gBAAkD;AAAA,EACtD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,OAAO;AACT;AAEA,SAAS,qBAAqB,QAA6B;AACzD,QAAM,oBAAoB;AAAA,IACxB,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,EACjB;AACA,QAAM,QAAQ,kBAAkB;AAAA,IAAQ,aACtC,QAAQ,IAAI,YAAU,GAAG,cAAc,OAAO,IAAI,KAAK,SAAS,KAAK,OAAO,MAAM,EAAE;AAAA,EACtF;AACA,QAAM;AAAA,IACJ,GAAG,OAAO,QAAQ,QAAQ;AAAA,MACxB,YAAU,YAAY,OAAO,aAAa,OAAO,IAAI,IAAI,OAAO,MAAM;AAAA,IACxE;AAAA,EACF;AACA,SAAO;AACT;AAeA,SAAS,gBAAgB,QAAmB,SAA0C;AACpF,QAAM,cAAc,kBAAkB,QAAQ,OAAO;AACrD,MAAI,gBAAgB,OAAW,QAAO;AACtC,QAAM,mBAAmB,uBAAuB,OAAO,IAAI;AAC3D,QAAM,WAAW,eAAe,MAAM,EAAE,OAAO,aAAW,CAAC,iBAAiB,IAAI,OAAO,CAAC;AACxF,QAAM,QAAQ;AAAA,IACZ,SAAS,OAAO,KAAK;AAAA,IACrB,YAAY,OAAO,UAAU,QAAQ,IAAI;AAAA,IACzC,GAAG,oBAAoB,OAAO,IAAI;AAAA,IAClC,GAAG,uBAAuB,OAAO,IAAI;AAAA,IACrC,GAAG,sBAAsB,OAAO,IAAI;AAAA,IACpC,GAAG;AAAA,IACH,GAAG,mBAAmB,OAAO,IAAI;AAAA,EACnC;AAEA,MAAI,QAAQ,YAAY,MAAM;AAC5B,UAAM,UAAU;AAAA,MACd,GAAG,OAAO,SAAS,IAAI,aAAW,QAAQ,MAAM;AAAA,MAChD,GAAG,OAAO,OAAO,IAAI,WAAS,MAAM,MAAM;AAAA,IAC5C,EAAE,OAAO,CAAC,WAA6B,WAAW,MAAS;AAC3D,UAAM,KAAK,GAAG,qBAAqB,MAAM,GAAG,GAAG,OAAO;AAAA,EACxD;AACA,SAAO;AACT;AAEO,SAAS,mBACd,QACA,UAAkD,CAAC,GAC/B;AACpB,MAAI,OAAO,cAAc,SAAS,OAAO;AACvC,WAAO;AAAA,MACL,QAAQ,OAAO,aAAa,KAAK,QAAQ,OAAO,EAAE;AAAA,MAClD,QAAQ,QAAQ,UAAU,OAAO,KAAK,eAAe,MAAM,EAAE,KAAK,IAAI;AAAA,IACxE;AAAA,EACF;AACA,MAAI,oBAAoB,QAAQ,OAAO,EAAG,QAAO,EAAE,QAAQ,IAAI,QAAQ,GAAG;AAE1E,QAAM,QAAQ,gBAAgB,QAAQ,OAAO;AAE7C,QAAM,gBAAgB,OAAO,YAAY,CAAC;AAC1C,MAAI,kBAAkB,OAAW,OAAM,KAAK,SAAS,gBAAgB,aAAa,CAAC,EAAE;AACrF,QAAM,OAAO,MAAM,KAAK,IAAI;AAC5B,SAAO,OAAO,UAAU,WAAW,EAAE,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,QAAQ,MAAM,QAAQ,GAAG;AAC/F;","names":["isRecord"]}
@@ -1,48 +0,0 @@
1
- import {
2
- createResult
3
- } from "./chunk-V63TSN3C.js";
4
-
5
- // src/cli-protocol/online-required.ts
6
- function onlineRequired(name, nextCommand = name) {
7
- return createResult({
8
- state: "action_required",
9
- findings: [
10
- {
11
- code: "CLI_ONLINE_REQUIRED",
12
- message: `\`${name}\` requires declared network access for this operation.`,
13
- severity: "warning"
14
- }
15
- ],
16
- nextActions: [
17
- {
18
- command: `safeword ${nextCommand}`,
19
- mutates: true,
20
- requiresHuman: false
21
- }
22
- ],
23
- data: { command: name, offline: true }
24
- });
25
- }
26
-
27
- // src/cli-protocol/replay-command.ts
28
- function shellArgument(value) {
29
- const escapedSingleQuote = `'"'"'`;
30
- return `'${value.split("'").join(escapedSingleQuote)}'`;
31
- }
32
- function buildReplayCommand(input) {
33
- return [
34
- input.command,
35
- ...(input.operands ?? []).map((operand) => shellArgument(operand)),
36
- ...(input.options ?? []).flatMap(
37
- ([flag, value]) => value === void 0 ? [] : [flag, shellArgument(value)]
38
- ),
39
- "--cwd",
40
- shellArgument(input.cwd)
41
- ].join(" ");
42
- }
43
-
44
- export {
45
- onlineRequired,
46
- buildReplayCommand
47
- };
48
- //# sourceMappingURL=chunk-WRUXO3WA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-protocol/online-required.ts","../src/cli-protocol/replay-command.ts"],"sourcesContent":["import { type CliResult, createResult } from './result.js';\n\nexport function onlineRequired(name: string, nextCommand = name): CliResult {\n return createResult({\n state: 'action_required',\n findings: [\n {\n code: 'CLI_ONLINE_REQUIRED',\n message: `\\`${name}\\` requires declared network access for this operation.`,\n severity: 'warning',\n },\n ],\n nextActions: [\n {\n command: `safeword ${nextCommand}`,\n mutates: true,\n requiresHuman: false,\n },\n ],\n data: { command: name, offline: true },\n });\n}\n","export type ReplayOption = readonly [flag: string, value: string | undefined];\n\nfunction shellArgument(value: string): string {\n const escapedSingleQuote = `'\"'\"'`;\n return `'${value.split(\"'\").join(escapedSingleQuote)}'`;\n}\n\nexport function buildReplayCommand(input: {\n readonly command: string;\n readonly operands?: readonly string[];\n readonly options?: readonly ReplayOption[];\n readonly cwd: string;\n}): string {\n return [\n input.command,\n ...(input.operands ?? []).map(operand => shellArgument(operand)),\n ...(input.options ?? []).flatMap(([flag, value]) =>\n value === undefined ? [] : [flag, shellArgument(value)],\n ),\n '--cwd',\n shellArgument(input.cwd),\n ].join(' ');\n}\n"],"mappings":";;;;;AAEO,SAAS,eAAe,MAAc,cAAc,MAAiB;AAC1E,SAAO,aAAa;AAAA,IAClB,OAAO;AAAA,IACP,UAAU;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,SAAS,KAAK,IAAI;AAAA,QAClB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,aAAa;AAAA,MACX;AAAA,QACE,SAAS,YAAY,WAAW;AAAA,QAChC,SAAS;AAAA,QACT,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,MAAM,EAAE,SAAS,MAAM,SAAS,KAAK;AAAA,EACvC,CAAC;AACH;;;ACnBA,SAAS,cAAc,OAAuB;AAC5C,QAAM,qBAAqB;AAC3B,SAAO,IAAI,MAAM,MAAM,GAAG,EAAE,KAAK,kBAAkB,CAAC;AACtD;AAEO,SAAS,mBAAmB,OAKxB;AACT,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,MAAM,YAAY,CAAC,GAAG,IAAI,aAAW,cAAc,OAAO,CAAC;AAAA,IAC/D,IAAI,MAAM,WAAW,CAAC,GAAG;AAAA,MAAQ,CAAC,CAAC,MAAM,KAAK,MAC5C,UAAU,SAAY,CAAC,IAAI,CAAC,MAAM,cAAc,KAAK,CAAC;AAAA,IACxD;AAAA,IACA;AAAA,IACA,cAAc,MAAM,GAAG;AAAA,EACzB,EAAE,KAAK,GAAG;AACZ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/review/contract.ts"],"sourcesContent":["export type ReviewAgent = 'claude' | 'codex';\nexport type ReviewAuthor = ReviewAgent | 'cursor' | 'unknown';\nexport type ReviewKind = 'quality-review' | 'scenario-gate' | 'plan-implementation';\nexport type ReviewPolicy = 'prefer' | 'require' | 'off';\nexport type ReviewFailure =\n | 'not_installed'\n | 'unsupported'\n | 'probe_timed_out'\n | 'launch_failed'\n | 'not_authenticated'\n | 'process_failed'\n | 'timed_out'\n | 'invalid_output'\n | 'REVIEWER_PROVENANCE_MISSING'\n | 'REVIEWER_PROVENANCE_CONTRADICTORY'\n | 'source_changed';\n\ninterface ReviewFinding {\n readonly severity: 'info' | 'warning' | 'error';\n readonly message: string;\n}\n\nexport interface ReviewerOutput {\n readonly schema_version: 1;\n readonly dispatch_id: string;\n readonly reviewer_agent: ReviewAgent;\n readonly verdict: 'approve' | 'request_changes';\n readonly summary: string;\n readonly findings: readonly ReviewFinding[];\n}\n\nexport interface UnverifiedReviewerOutput {\n readonly schema_version: 1;\n readonly dispatch_id?: unknown;\n readonly reviewer_agent?: unknown;\n readonly verdict: 'approve' | 'request_changes';\n readonly summary: string;\n readonly findings: readonly ReviewFinding[];\n}\n\nexport interface ReviewPacket {\n readonly schema_version: 1;\n readonly dispatch_id: string;\n readonly kind: ReviewKind;\n readonly logical_files: readonly {\n readonly path: string;\n readonly content: string;\n }[];\n /** Bounded evidence available to the reviewer, but not part of the work product. */\n readonly context_files?: readonly {\n readonly path: string;\n readonly content: string;\n }[];\n}\n\nconst REVIEW_KINDS = new Set<ReviewKind>([\n 'quality-review',\n 'scenario-gate',\n 'plan-implementation',\n]);\n\nexport function isReviewKind(value: unknown): value is ReviewKind {\n return typeof value === 'string' && REVIEW_KINDS.has(value as ReviewKind);\n}\n"],"mappings":";AAuDA,IAAM,eAAe,oBAAI,IAAgB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,aAAa,OAAqC;AAChE,SAAO,OAAO,UAAU,YAAY,aAAa,IAAI,KAAmB;AAC1E;","names":[]}