safeword 0.78.2 → 0.78.4

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 (224) 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-TCSDEFKH.js} +9 -9
  12. package/dist/{architecture-document-UBA5UT4J.js → architecture-document-L2ADWVMV.js} +6 -6
  13. package/dist/{architecture-monorepo-E4JUYJGV.js → architecture-monorepo-NKOQZBEY.js} +4 -4
  14. package/dist/{boundary-HLJILY7R.js → boundary-QP2H7C46.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-GZ36UZJG.js → chunk-73TABQRT.js} +10 -10
  19. package/dist/{chunk-4CKCRZDO.js → chunk-7ULZND56.js} +2 -2
  20. package/dist/{chunk-34NCOTP5.js → chunk-7XELS325.js} +2 -2
  21. package/dist/{chunk-USPVNZ63.js → chunk-BZWO3BVD.js} +11 -7
  22. package/dist/chunk-BZWO3BVD.js.map +1 -0
  23. package/dist/{chunk-BQTC6YGN.js → chunk-D5ENZR6N.js} +2 -2
  24. package/dist/{chunk-SME5SU3O.js → chunk-DBXX5K74.js} +258 -48
  25. package/dist/chunk-DBXX5K74.js.map +1 -0
  26. package/dist/{chunk-V63TSN3C.js → chunk-FOYGZ4MK.js} +31 -10
  27. package/dist/chunk-FOYGZ4MK.js.map +1 -0
  28. package/dist/{chunk-ZRV7TOCX.js → chunk-G5AFQXXW.js} +2 -2
  29. package/dist/{chunk-AXAGJLMP.js → chunk-G757F3EU.js} +4 -4
  30. package/dist/{chunk-KXHVFCDT.js → chunk-GFDINP7M.js} +153 -27
  31. package/dist/chunk-GFDINP7M.js.map +1 -0
  32. package/dist/{chunk-S2T356DA.js → chunk-HF3DXZEA.js} +7 -10
  33. package/dist/chunk-HF3DXZEA.js.map +1 -0
  34. package/dist/{chunk-NSZGI7HK.js → chunk-HNCUIXDY.js} +9 -9
  35. package/dist/{chunk-NSZGI7HK.js.map → chunk-HNCUIXDY.js.map} +1 -1
  36. package/dist/{chunk-BSOUMHMQ.js → chunk-IPTM5VE7.js} +101 -1094
  37. package/dist/chunk-IPTM5VE7.js.map +1 -0
  38. package/dist/chunk-JBOLNV2L.js +21 -0
  39. package/dist/chunk-JBOLNV2L.js.map +1 -0
  40. package/dist/chunk-JSR3HXFT.js +1231 -0
  41. package/dist/chunk-JSR3HXFT.js.map +1 -0
  42. package/dist/{chunk-BAVDDAN3.js → chunk-KS2ESRQL.js} +9 -9
  43. package/dist/chunk-LFC6CMMX.js +136 -0
  44. package/dist/chunk-LFC6CMMX.js.map +1 -0
  45. package/dist/{chunk-QTG2EXFT.js → chunk-LUNHU6Z3.js} +2 -2
  46. package/dist/{chunk-QTG2EXFT.js.map → chunk-LUNHU6Z3.js.map} +1 -1
  47. package/dist/{chunk-WVDRXD67.js → chunk-MLBZFB5A.js} +1 -1
  48. package/dist/chunk-MLBZFB5A.js.map +1 -0
  49. package/dist/{chunk-KFVEPQ7X.js → chunk-MVFHTUQ3.js} +2 -2
  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-3SOJBMM6.js → chunk-R6BSRNU4.js} +6 -6
  58. package/dist/{chunk-3SOJBMM6.js.map → chunk-R6BSRNU4.js.map} +1 -1
  59. package/dist/{chunk-J7UDLLTK.js → chunk-RQ7RZYNB.js} +39 -25
  60. package/dist/chunk-RQ7RZYNB.js.map +1 -0
  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-I4KBHRZW.js → chunk-VJUPD7JI.js} +7 -7
  67. package/dist/{chunk-3EJNDIAO.js → chunk-VZ5B2SIO.js} +2 -2
  68. package/dist/chunk-WAMQTZAH.js +19 -0
  69. package/dist/chunk-WAMQTZAH.js.map +1 -0
  70. package/dist/{chunk-FDHJ63FZ.js → chunk-WVA4Q6BX.js} +1 -1
  71. package/dist/{chunk-FDHJ63FZ.js.map → chunk-WVA4Q6BX.js.map} +1 -1
  72. package/dist/{chunk-AV5LTEME.js → chunk-YZ5JGDOV.js} +30 -134
  73. package/dist/chunk-YZ5JGDOV.js.map +1 -0
  74. package/dist/{cleanup-X556LNWM.js → cleanup-JNLZ7AXA.js} +9 -9
  75. package/dist/{cleanup-command-RV2Q7G72.js → cleanup-command-MFAYNYRW.js} +15 -15
  76. package/dist/cli.js +152 -109
  77. package/dist/cli.js.map +1 -1
  78. package/dist/{clients-HP23HRN2.js → clients-7AJBKEWF.js} +2 -2
  79. package/dist/codex-bootstrap-KFNUS63Y.js +252 -0
  80. package/dist/codex-bootstrap-KFNUS63Y.js.map +1 -0
  81. package/dist/{codex-hook-IPX3PDXX.js → codex-hook-TNMYVOR7.js} +57 -49
  82. package/dist/codex-hook-TNMYVOR7.js.map +1 -0
  83. package/dist/{codify-J5ZLJ3HK.js → codify-RBWBRXQO.js} +11 -8
  84. package/dist/codify-RBWBRXQO.js.map +1 -0
  85. package/dist/{commands-L4AINARC.js → commands-4UXHBZFD.js} +127 -71
  86. package/dist/commands-4UXHBZFD.js.map +1 -0
  87. package/dist/{config-LUSJAK6B.js → config-GS7BTZJD.js} +2 -2
  88. package/dist/{configured-paths-SPQ5XWOM.js → configured-paths-CREUDQF3.js} +4 -4
  89. package/dist/contract-42CIWIWV.js +8 -0
  90. package/dist/{coordinator-SA75UB3C.js → coordinator-WOPGWDXK.js} +231 -144
  91. package/dist/coordinator-WOPGWDXK.js.map +1 -0
  92. package/dist/corpus-CAFYEMBO.js +15 -0
  93. package/dist/{doctor-DSC36R4U.js → doctor-AZP5D7QP.js} +21 -21
  94. package/dist/{feature-directories-VOHVV26X.js → feature-directories-6TLM7Y4H.js} +6 -6
  95. package/dist/{finalization-7SMLMVLV.js → finalization-AQT3SOP7.js} +4 -4
  96. package/dist/{gh-cli-ZMO7D2KE.js → gh-cli-XCQT5ST7.js} +2 -2
  97. package/dist/{github-rest-KGKS4V2H.js → github-rest-CXFJ2NFF.js} +2 -2
  98. package/dist/index.js +2 -2
  99. package/dist/{job-CDADLJDF.js → job-4POTQJZK.js} +20 -21
  100. package/dist/job-4POTQJZK.js.map +1 -0
  101. package/dist/{learning-sync-6BTHM6ZC.js → learning-sync-QVXBOP6J.js} +4 -4
  102. package/dist/{legacy-global-guidance-2JL4Y5EB.js → legacy-global-guidance-CPGUUNJI.js} +5 -5
  103. package/dist/{lint-gherkin-COSHVS6Z.js → lint-gherkin-LZ45NG4A.js} +7 -7
  104. package/dist/{operations-XVRQSLJG.js → operations-ODOA5CDG.js} +18 -16
  105. package/dist/{output-3LB2OEHN.js → output-QTZCFLAY.js} +2 -2
  106. package/dist/{packet-5WT5M3OS.js → packet-5G6RT2EW.js} +2 -2
  107. package/dist/presets/typescript/index.js +2 -2
  108. package/dist/{profile-UMKQ5JVO.js → profile-QGLZNLRU.js} +7 -7
  109. package/dist/{prompt-GWYDFMLO.js → prompt-UCTSCXSS.js} +2 -2
  110. package/dist/remove-UQPEXXNP.js +23 -0
  111. package/dist/{retro-IFSVU5KS.js → retro-ELKR6GHF.js} +8 -8
  112. package/dist/{retro-draft-spool-4S3TCHWL.js → retro-draft-spool-M75BTT32.js} +2 -2
  113. package/dist/{retro-extract-OSBFAXLU.js → retro-extract-RA6THOC4.js} +2 -2
  114. package/dist/{review-pr-6S2352CK.js → review-pr-MN6QESAJ.js} +2 -2
  115. package/dist/{review-pr-publication-CGHCRRUI.js → review-pr-publication-6AVOGWR2.js} +2 -2
  116. package/dist/{run-5JESKXUR.js → run-JFDGRAH2.js} +4 -4
  117. package/dist/{self-report-VPHBPPSJ.js → self-report-5APRPB5P.js} +2 -2
  118. package/dist/status-ATFG6Q3K.js +21 -0
  119. package/dist/{status-EX4L3ELO.js → status-HIQSP3F5.js} +21 -21
  120. package/dist/{sync-config-O7J7IDGE.js → sync-config-XPAJKBRO.js} +5 -5
  121. package/dist/{sync-tracker-U2GYOFUO.js → sync-tracker-3E5BWX73.js} +6 -6
  122. package/dist/{test-execution-6LIPMBIH.js → test-execution-VDECX7TO.js} +8 -7
  123. package/dist/{test-execution-6LIPMBIH.js.map → test-execution-VDECX7TO.js.map} +1 -1
  124. package/dist/{test-plan-VASHZXJ7.js → test-plan-2L7A7UZV.js} +7 -6
  125. package/dist/{test-plan-VASHZXJ7.js.map → test-plan-2L7A7UZV.js.map} +1 -1
  126. package/dist/{ticket-new-3YXDIWPC.js → ticket-new-HZKNZ5GO.js} +11 -11
  127. package/dist/ticket-new-HZKNZ5GO.js.map +1 -0
  128. package/dist/{ticket-sync-LLUBXZ2R.js → ticket-sync-4QJD3KOL.js} +5 -5
  129. package/dist/{tracker-map-HR4MBYG5.js → tracker-map-CFMFIEER.js} +2 -2
  130. package/dist/{tracker-sync-YDICZN66.js → tracker-sync-MOPJE6M7.js} +2 -2
  131. package/package.json +1 -1
  132. package/templates/SAFEWORD.md +2 -2
  133. package/templates/commands/lint.md +1 -1
  134. package/templates/scripts/closeout-cleanup.ts +127 -31
  135. package/templates/skills/bdd/SCENARIOS.md +3 -0
  136. package/templates/skills/bdd/TDD.md +11 -5
  137. package/templates/skills/closeout/SKILL.md +25 -17
  138. package/templates/skills/finish-review/SKILL.md +4 -3
  139. package/templates/skills/lint/SKILL.md +1 -1
  140. package/templates/skills/review-spec/SKILL.md +5 -1
  141. package/templates/skills/self-review/SKILL.md +2 -2
  142. package/templates/skills/ticket-system/SKILL.md +3 -0
  143. package/dist/chunk-6S7CTKGC.js +0 -128
  144. package/dist/chunk-6S7CTKGC.js.map +0 -1
  145. package/dist/chunk-AV5LTEME.js.map +0 -1
  146. package/dist/chunk-BSOUMHMQ.js.map +0 -1
  147. package/dist/chunk-J7UDLLTK.js.map +0 -1
  148. package/dist/chunk-KXHVFCDT.js.map +0 -1
  149. package/dist/chunk-NOTBZCKG.js.map +0 -1
  150. package/dist/chunk-QUDIOBHP.js.map +0 -1
  151. package/dist/chunk-S2T356DA.js.map +0 -1
  152. package/dist/chunk-SME5SU3O.js.map +0 -1
  153. package/dist/chunk-TJ627SKI.js.map +0 -1
  154. package/dist/chunk-USPVNZ63.js.map +0 -1
  155. package/dist/chunk-V63TSN3C.js.map +0 -1
  156. package/dist/chunk-WRUXO3WA.js +0 -48
  157. package/dist/chunk-WRUXO3WA.js.map +0 -1
  158. package/dist/chunk-WVDRXD67.js.map +0 -1
  159. package/dist/codex-bootstrap-MGY2D6HU.js +0 -178
  160. package/dist/codex-bootstrap-MGY2D6HU.js.map +0 -1
  161. package/dist/codex-hook-IPX3PDXX.js.map +0 -1
  162. package/dist/codify-J5ZLJ3HK.js.map +0 -1
  163. package/dist/commands-L4AINARC.js.map +0 -1
  164. package/dist/contract-KEZY6YC3.js +0 -8
  165. package/dist/coordinator-SA75UB3C.js.map +0 -1
  166. package/dist/corpus-PPOBTF4J.js +0 -15
  167. package/dist/job-CDADLJDF.js.map +0 -1
  168. package/dist/remove-E6HGNHOU.js +0 -22
  169. package/dist/status-7VIGNTUI.js +0 -21
  170. package/dist/ticket-new-3YXDIWPC.js.map +0 -1
  171. /package/dist/{architecture-N5ZC4CDU.js.map → architecture-TCSDEFKH.js.map} +0 -0
  172. /package/dist/{architecture-document-UBA5UT4J.js.map → architecture-document-L2ADWVMV.js.map} +0 -0
  173. /package/dist/{architecture-monorepo-E4JUYJGV.js.map → architecture-monorepo-NKOQZBEY.js.map} +0 -0
  174. /package/dist/{boundary-HLJILY7R.js.map → boundary-QP2H7C46.js.map} +0 -0
  175. /package/dist/{chunk-4CZS4YJV.js.map → chunk-3OKZ3X4O.js.map} +0 -0
  176. /package/dist/{chunk-GZ36UZJG.js.map → chunk-73TABQRT.js.map} +0 -0
  177. /package/dist/{chunk-4CKCRZDO.js.map → chunk-7ULZND56.js.map} +0 -0
  178. /package/dist/{chunk-34NCOTP5.js.map → chunk-7XELS325.js.map} +0 -0
  179. /package/dist/{chunk-BQTC6YGN.js.map → chunk-D5ENZR6N.js.map} +0 -0
  180. /package/dist/{chunk-ZRV7TOCX.js.map → chunk-G5AFQXXW.js.map} +0 -0
  181. /package/dist/{chunk-AXAGJLMP.js.map → chunk-G757F3EU.js.map} +0 -0
  182. /package/dist/{chunk-BAVDDAN3.js.map → chunk-KS2ESRQL.js.map} +0 -0
  183. /package/dist/{chunk-KFVEPQ7X.js.map → chunk-MVFHTUQ3.js.map} +0 -0
  184. /package/dist/{chunk-Q5KLE6ZF.js.map → chunk-OKPRQGFG.js.map} +0 -0
  185. /package/dist/{chunk-664YNAMR.js.map → chunk-RTFPKJJV.js.map} +0 -0
  186. /package/dist/{chunk-GKR44BCH.js.map → chunk-RVDXBHUG.js.map} +0 -0
  187. /package/dist/{chunk-A7Z27COA.js.map → chunk-VFWAZLEF.js.map} +0 -0
  188. /package/dist/{chunk-I4KBHRZW.js.map → chunk-VJUPD7JI.js.map} +0 -0
  189. /package/dist/{chunk-3EJNDIAO.js.map → chunk-VZ5B2SIO.js.map} +0 -0
  190. /package/dist/{cleanup-X556LNWM.js.map → cleanup-JNLZ7AXA.js.map} +0 -0
  191. /package/dist/{cleanup-command-RV2Q7G72.js.map → cleanup-command-MFAYNYRW.js.map} +0 -0
  192. /package/dist/{clients-HP23HRN2.js.map → clients-7AJBKEWF.js.map} +0 -0
  193. /package/dist/{config-LUSJAK6B.js.map → config-GS7BTZJD.js.map} +0 -0
  194. /package/dist/{configured-paths-SPQ5XWOM.js.map → configured-paths-CREUDQF3.js.map} +0 -0
  195. /package/dist/{contract-KEZY6YC3.js.map → contract-42CIWIWV.js.map} +0 -0
  196. /package/dist/{corpus-PPOBTF4J.js.map → corpus-CAFYEMBO.js.map} +0 -0
  197. /package/dist/{doctor-DSC36R4U.js.map → doctor-AZP5D7QP.js.map} +0 -0
  198. /package/dist/{feature-directories-VOHVV26X.js.map → feature-directories-6TLM7Y4H.js.map} +0 -0
  199. /package/dist/{finalization-7SMLMVLV.js.map → finalization-AQT3SOP7.js.map} +0 -0
  200. /package/dist/{gh-cli-ZMO7D2KE.js.map → gh-cli-XCQT5ST7.js.map} +0 -0
  201. /package/dist/{github-rest-KGKS4V2H.js.map → github-rest-CXFJ2NFF.js.map} +0 -0
  202. /package/dist/{learning-sync-6BTHM6ZC.js.map → learning-sync-QVXBOP6J.js.map} +0 -0
  203. /package/dist/{legacy-global-guidance-2JL4Y5EB.js.map → legacy-global-guidance-CPGUUNJI.js.map} +0 -0
  204. /package/dist/{lint-gherkin-COSHVS6Z.js.map → lint-gherkin-LZ45NG4A.js.map} +0 -0
  205. /package/dist/{operations-XVRQSLJG.js.map → operations-ODOA5CDG.js.map} +0 -0
  206. /package/dist/{output-3LB2OEHN.js.map → output-QTZCFLAY.js.map} +0 -0
  207. /package/dist/{packet-5WT5M3OS.js.map → packet-5G6RT2EW.js.map} +0 -0
  208. /package/dist/{profile-UMKQ5JVO.js.map → profile-QGLZNLRU.js.map} +0 -0
  209. /package/dist/{prompt-GWYDFMLO.js.map → prompt-UCTSCXSS.js.map} +0 -0
  210. /package/dist/{remove-E6HGNHOU.js.map → remove-UQPEXXNP.js.map} +0 -0
  211. /package/dist/{retro-IFSVU5KS.js.map → retro-ELKR6GHF.js.map} +0 -0
  212. /package/dist/{retro-draft-spool-4S3TCHWL.js.map → retro-draft-spool-M75BTT32.js.map} +0 -0
  213. /package/dist/{retro-extract-OSBFAXLU.js.map → retro-extract-RA6THOC4.js.map} +0 -0
  214. /package/dist/{review-pr-6S2352CK.js.map → review-pr-MN6QESAJ.js.map} +0 -0
  215. /package/dist/{review-pr-publication-CGHCRRUI.js.map → review-pr-publication-6AVOGWR2.js.map} +0 -0
  216. /package/dist/{run-5JESKXUR.js.map → run-JFDGRAH2.js.map} +0 -0
  217. /package/dist/{self-report-VPHBPPSJ.js.map → self-report-5APRPB5P.js.map} +0 -0
  218. /package/dist/{status-7VIGNTUI.js.map → status-ATFG6Q3K.js.map} +0 -0
  219. /package/dist/{status-EX4L3ELO.js.map → status-HIQSP3F5.js.map} +0 -0
  220. /package/dist/{sync-config-O7J7IDGE.js.map → sync-config-XPAJKBRO.js.map} +0 -0
  221. /package/dist/{sync-tracker-U2GYOFUO.js.map → sync-tracker-3E5BWX73.js.map} +0 -0
  222. /package/dist/{ticket-sync-LLUBXZ2R.js.map → ticket-sync-4QJD3KOL.js.map} +0 -0
  223. /package/dist/{tracker-map-HR4MBYG5.js.map → tracker-map-CFMFIEER.js.map} +0 -0
  224. /package/dist/{tracker-sync-YDICZN66.js.map → tracker-sync-MOPJE6M7.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/reconcile.ts","../src/packs/rust/setup.ts","../src/packs/config.ts","../src/packs/golang/setup.ts","../src/packs/golang/index.ts","../src/packs/python/index.ts","../src/packs/rust/index.ts","../src/packs/sql/index.ts","../src/packs/typescript/index.ts","../src/packs/typescript/setup.ts","../src/packs/registry.ts","../src/utils/context.ts","../src/utils/git.ts","../src/utils/hook-manager.ts","../src/utils/project-detector.ts","../src/utils/cucumber-template-revisions.ts"],"sourcesContent":["/**\n * Reconciliation Engine\n *\n * Computes and executes plans based on SAFEWORD_SCHEMA and project state.\n * This is the single source of truth for all file/dir/config operations.\n */\n\nimport { lstatSync, readdirSync, readlinkSync, unlinkSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport type {\n FileDefinition,\n JsonMergeDefinition,\n ManagedFileDefinition,\n ProjectContext,\n SafewordSchema,\n TextPatchDefinition,\n} from './schema.js';\nimport {\n NAMESPACE_ROOT_LEGACY,\n readConfiguredPath,\n resolveNamespaceRoot,\n} from './utils/configured-paths.js';\nimport {\n ensureDirectory,\n exists,\n getTemplatesDirectory,\n makeScriptsExecutable,\n readFile,\n readFileSafe,\n readJson,\n remove,\n removeIfEmpty,\n writeFile,\n writeJson,\n} from './utils/fs.js';\nimport type { ProjectType } from './utils/project-detector.js';\nimport { getWorkspacePackageNames } from './utils/workspaces.js';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst HUSKY_DIR = '.husky';\n\n/**\n * Directories containing executable scripts that need chmod +x.\n * Used by both install and upgrade plans.\n */\nconst CHMOD_PATHS = ['.safeword/hooks', '.safeword/hooks/cursor', '.safeword/scripts'];\n\n/**\n * Prettier-related packages that should be skipped for projects with existing formatter.\n */\nconst PRETTIER_PACKAGES = new Set([\n 'prettier',\n 'prettier-plugin-astro',\n 'prettier-plugin-tailwindcss',\n 'prettier-plugin-sh',\n]);\n\n// Conditional-package keys whose condition is the ABSENCE of existing tooling,\n// rather than a truthy ProjectType field: \"standard\" = no existing formatter.\nconst INVERTED_PACKAGE_CONDITIONS: Record<string, (projectType: ProjectType) => boolean> = {\n standard: projectType => !projectType.existingFormatter,\n};\n\n/**\n * Get conditional packages based on project type.\n * Handles inverted keys (standard) and prettier filtering for existing formatters.\n */\nfunction getConditionalPackages(\n conditionalPackages: Record<string, string[]>,\n projectType: ProjectType,\n): string[] {\n const packages: string[] = [];\n\n for (const [key, dependencies] of Object.entries(conditionalPackages)) {\n const inverted = INVERTED_PACKAGE_CONDITIONS[key];\n if (inverted) {\n if (inverted(projectType)) packages.push(...dependencies);\n continue;\n }\n\n // Check if this condition is met\n const conditionMet = projectType[key as keyof ProjectType];\n if (conditionMet) {\n // For projects with existing formatter, skip prettier-related packages\n if (projectType.existingFormatter) {\n packages.push(...dependencies.filter(pkg => !PRETTIER_PACKAGES.has(pkg)));\n } else {\n packages.push(...dependencies);\n }\n }\n }\n\n return packages;\n}\n\n/** Check if path should be skipped in non-git repos (husky files) */\nfunction shouldSkipForNonGit(path: string, isGitRepo: boolean): boolean {\n return path.startsWith(HUSKY_DIR) && !isGitRepo;\n}\n\n/** Plan mkdir actions for directories that don't exist */\nfunction planMissingDirectories(\n directories: string[],\n cwd: string,\n isGitRepo: boolean,\n): { actions: Action[]; created: string[] } {\n const actions: Action[] = [];\n const created: string[] = [];\n for (const dir of directories) {\n if (shouldSkipForNonGit(dir, isGitRepo)) continue;\n if (!exists(nodePath.join(cwd, dir))) {\n actions.push({ type: 'mkdir', path: dir });\n created.push(dir);\n }\n }\n return { actions, created };\n}\n\n/**\n * Plan text-patch actions for all eligible targets. The executor\n * (`executeTextPatch`) decides whether to prepend, heal a legacy `---#`\n * artifact, or no-op based on the file's current contents — so the planner\n * stays uniform across modes for unguarded patches.\n * Keeping the marker check in the executor (not here) ensures `safeword\n * upgrade` reaches the heal path on pre-fix installs, which is what commit\n * a304af8 promised.\n */\nfunction asPatchList(entry: TextPatchDefinition | TextPatchDefinition[]): TextPatchDefinition[] {\n return Array.isArray(entry) ? entry : [entry];\n}\n\nfunction planTextPatches(\n patches: Record<string, TextPatchDefinition | TextPatchDefinition[]>,\n ctx: ProjectContext,\n): Action[] {\n const actions: Action[] = [];\n for (const [filePath, entry] of Object.entries(patches)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n // Apply in list order so later patches land beneath earlier ones.\n actions.push(...planTextPatchesForFile(filePath, entry, ctx));\n }\n return actions;\n}\n\nfunction planTextPatchesForFile(\n filePath: string,\n entry: TextPatchDefinition | TextPatchDefinition[],\n ctx: ProjectContext,\n): Action[] {\n const actions: Action[] = [];\n for (const definition of asPatchList(entry)) {\n // Application-only gate (ZJMZ50): unpatch stays ungated in planTextUnpatches.\n if (definition.when !== undefined && !definition.when(ctx)) continue;\n if (!passesTextPatchContentGuard(ctx.cwd, filePath, definition)) continue;\n actions.push({\n type: 'text-patch',\n path: filePath,\n definition: resolveTextPatch(definition, ctx),\n });\n }\n return actions;\n}\n\nfunction passesTextPatchContentGuard(\n cwd: string,\n filePath: string,\n definition: TextPatchDefinition,\n): boolean {\n if (!definition.applyWhenContentIncludes) return true;\n\n const content = readFileSafe(nodePath.join(cwd, filePath));\n return (\n content !== undefined &&\n definition.applyWhenContentIncludes.every(required => content.includes(required))\n );\n}\n\nfunction planTextUnpatches(\n patches: Record<string, TextPatchDefinition | TextPatchDefinition[]>,\n ctx: ProjectContext,\n): Action[] {\n const hasLegacyPreamble = (content: string, definition: TextPatchDefinition): boolean =>\n containsTextPatchContent(content, definition);\n\n const actions: Action[] = [];\n for (const [filePath, entry] of Object.entries(patches)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const fullPath = nodePath.join(ctx.cwd, filePath);\n if (!exists(fullPath)) continue;\n\n const content = readFileSafe(fullPath) ?? '';\n // Unpatch in reverse list order so the patch that owns file removal\n // (removeFileIfContentEquals) runs last, after siblings strip their blocks.\n for (const definition of asPatchList(entry).toReversed()) {\n if (hasLegacyPreamble(content, definition)) {\n actions.push({\n type: 'text-unpatch',\n path: filePath,\n definition: resolveTextPatch(definition, ctx),\n });\n }\n }\n }\n return actions;\n}\n\nfunction stripLeadingSafewordSeparator(value: string): string {\n if (value.startsWith('\\n---')) {\n return value.slice('\\n---'.length).replace(/^\\n+/, '');\n }\n if (value.startsWith('---')) {\n return value.slice('---'.length).replace(/^\\n+/, '');\n }\n return value;\n}\n\n/**\n * Generic file write planner with configurable skip condition.\n */\nfunction planFileWrites(\n files: Record<string, FileDefinition>,\n ctx: ProjectContext,\n shouldSkip: (filePath: string, ctx: ProjectContext) => boolean,\n): { actions: Action[]; created: string[] } {\n const actions: Action[] = [];\n const created: string[] = [];\n for (const [filePath, definition] of Object.entries(files)) {\n if (shouldSkip(filePath, ctx)) continue;\n const content = resolveFileContent(definition, ctx);\n // Skip files where generator returned undefined (e.g., non-JS projects)\n if (content === undefined) continue;\n actions.push({ type: 'write', path: filePath, content });\n created.push(filePath);\n }\n return { actions, created };\n}\n\n/** Owned files: skip husky files in non-git repos */\nfunction planOwnedFileWrites(\n files: Record<string, FileDefinition>,\n ctx: ProjectContext,\n): { actions: Action[]; created: string[] } {\n return planFileWrites(files, ctx, (filePath, c) => shouldSkipForNonGit(filePath, c.isGitRepo));\n}\n\n/**\n * Managed files: skip if file already exists OR if the entry has a\n * `configKey` whose `paths.<configKey>` override is set in\n * `.safeword/config.json` — see ticket K7N2QM.\n */\nfunction planManagedFileWrites(\n files: Record<string, ManagedFileDefinition>,\n ctx: ProjectContext,\n): { actions: Action[]; created: string[] } {\n return planFileWrites(files, ctx, (filePath, c) => {\n const definition = files[filePath];\n if (definition && isConfigOverridden(definition, c.cwd)) return true;\n return exists(nodePath.join(c.cwd, filePath));\n });\n}\n\n/** Plan rmdir actions for directories that exist */\nfunction planExistingDirectoriesRemoval(\n directories: string[],\n cwd: string,\n scheduledRemovals: ReadonlySet<string> = new Set(),\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const dir of directories) {\n const fullPath = nodePath.join(cwd, dir);\n if (!exists(fullPath)) continue;\n\n actions.push({ type: 'rmdir', path: dir });\n const plannedBeforeThisDirectory = new Set([...scheduledRemovals, ...removed]);\n const entries = readdirSync(fullPath);\n const willBeEmpty = entries.every(entry =>\n plannedBeforeThisDirectory.has(nodePath.posix.join(dir, entry)),\n );\n if (willBeEmpty) removed.push(dir);\n }\n return { actions, removed };\n}\n\nfunction matchesUnmodifiedScaffold(\n definition: ManagedFileDefinition,\n ctx: ProjectContext,\n installed: string,\n): boolean {\n const expected = definition.removeIfUnmodified?.(ctx);\n if (expected === undefined) return false;\n const normalize = definition.normalizeForUnmodifiedComparison;\n return normalize === undefined\n ? installed === expected\n : normalize(installed) === normalize(expected);\n}\n\nfunction matchesManagedScaffold(\n definition: ManagedFileDefinition,\n ctx: ProjectContext,\n installed: string | undefined,\n): boolean {\n if (installed === undefined) return false;\n if (definition.removeIfUnmodified !== undefined) {\n return matchesUnmodifiedScaffold(definition, ctx, installed);\n }\n const expected = resolveFileContent(definition, ctx);\n return expected !== undefined && installed === expected;\n}\n\n/** Plan rm actions for files that exist */\n/**\n * Conditional managed-file removal on DEFAULT reset (ticket V4MATC): entries\n * opting in via `removeIfUnmodified` are removed iff the on-disk content\n * byte-equals the expected scaffold — a user-extended or user-authored file\n * never matches and survives.\n */\nfunction planConditionalManagedRemoval(\n managedFiles: Record<string, ManagedFileDefinition>,\n ctx: ProjectContext,\n includeAllUnmodified = false,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const [filePath, definition] of Object.entries(managedFiles)) {\n if (!includeAllUnmodified && definition.removeIfUnmodified === undefined) continue;\n if (isConfigOverridden(definition, ctx.cwd)) continue;\n const fullPath = nodePath.join(ctx.cwd, filePath);\n if (!exists(fullPath)) continue;\n const installed = readFileSafe(fullPath);\n if (matchesManagedScaffold(definition, ctx, installed)) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\n/**\n * Conditional feature opt-out during upgrade: when a managed file's generator\n * now omits it, remove only the exact scaffold Safeword would have installed.\n * A customized file is project-owned and must survive the configuration change.\n */\nfunction planOmittedManagedRemoval(\n managedFiles: Record<string, ManagedFileDefinition>,\n ctx: ProjectContext,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const [filePath, definition] of Object.entries(managedFiles)) {\n if (!definition.removeWhenGeneratorOmitted) continue;\n if (definition.removeIfUnmodified === undefined) continue;\n if (isConfigOverridden(definition, ctx.cwd)) continue;\n if (resolveFileContent(definition, ctx) !== undefined) continue;\n\n const fullPath = nodePath.join(ctx.cwd, filePath);\n if (!exists(fullPath)) continue;\n const installed = readFileSafe(fullPath);\n if (installed !== undefined && matchesUnmodifiedScaffold(definition, ctx, installed)) {\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n }\n return { actions, removed };\n}\n\nfunction planExistingFilesRemoval(\n files: string[],\n cwd: string,\n): { actions: Action[]; removed: string[] } {\n const actions: Action[] = [];\n const removed: string[] = [];\n for (const filePath of files) {\n const fullPath = nodePath.join(cwd, filePath);\n const stat = lstatIfExists(fullPath);\n if (stat === undefined) continue;\n if (!isRemovableFile(stat)) continue;\n\n actions.push({ type: 'rm', path: filePath });\n removed.push(filePath);\n }\n return { actions, removed };\n}\n\nfunction lstatIfExists(path: string): ReturnType<typeof lstatSync> | undefined {\n try {\n return lstatSync(path);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR') return undefined;\n throw error;\n }\n}\n\n/** A schema file entry may unlink a file or symlink, but must preserve directories. */\nfunction isRemovableFile(stat: NonNullable<ReturnType<typeof lstatSync>>): boolean {\n return stat.isFile() || stat.isSymbolicLink();\n}\n\n/** Check if a .claude path needs parent dir cleanup */\nfunction getClaudeParentDirectoryForCleanup(filePath: string): string | undefined {\n if (!filePath.startsWith('.claude/')) return undefined;\n const parentDirectory = filePath.slice(0, Math.max(0, filePath.lastIndexOf('/')));\n if (\n !parentDirectory ||\n parentDirectory === '.claude' ||\n parentDirectory === '.claude/skills' ||\n parentDirectory === '.claude/commands'\n ) {\n return undefined;\n }\n return parentDirectory;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype ReconcileMode = 'install' | 'upgrade' | 'uninstall' | 'uninstall-full';\n\nexport type Action =\n | { type: 'mkdir'; path: string }\n | { type: 'rmdir'; path: string }\n | { type: 'write'; path: string; content: string }\n | { type: 'rm'; path: string }\n | { type: 'chmod'; paths: string[] }\n | { type: 'json-merge'; path: string; definition: JsonMergeDefinition }\n | { type: 'json-unmerge'; path: string; definition: JsonMergeDefinition }\n | { type: 'text-patch'; path: string; definition: ResolvedTextPatch }\n | { type: 'text-unpatch'; path: string; definition: ResolvedTextPatch };\n\n// A TextPatchDefinition whose ctx-factory `content` has been resolved to a string\n// at plan time, so executors never see a function (#293).\ntype ResolvedTextPatch = Omit<TextPatchDefinition, 'content' | 'replacementAfterUnpatch'> & {\n content: string;\n replacementAfterUnpatch?: string;\n};\n\nfunction resolveTextPatch(definition: TextPatchDefinition, ctx: ProjectContext): ResolvedTextPatch {\n return {\n ...definition,\n content:\n typeof definition.content === 'function' ? definition.content(ctx) : definition.content,\n replacementAfterUnpatch:\n typeof definition.replacementAfterUnpatch === 'function'\n ? definition.replacementAfterUnpatch(ctx)\n : definition.replacementAfterUnpatch,\n };\n}\n\nexport interface ReconcileResult {\n actions: Action[];\n applied: boolean;\n created: string[];\n updated: string[];\n removed: string[];\n packagesToInstall: string[];\n packagesToRemove: string[];\n // Non-fatal issues surfaced during execution (e.g. a jsonMerge target that\n // exists but doesn't parse, so the merge was skipped rather than silently lost).\n // Empty on a dry run, which never executes merges.\n warnings: string[];\n}\n\ninterface ReconcileOptions {\n dryRun?: boolean;\n}\n\n// ============================================================================\n// Main reconcile function\n// ============================================================================\n\n/**\n * Realize the schema's namespace paths at the resolved root (ticket N9S5XG).\n *\n * The schema manifest stays static with legacy-prefixed namespace entries\n * (`.safeword-project/...`); at planning time those entries are mapped onto\n * the resolved root — `.project/` for fresh repos, an adopted existing\n * `.project/`, a configured `paths.projectRoot`, or the legacy root itself\n * (identity translation) where only it exists. One seam covers install,\n * upgrade, diff (dry-run upgrade), and uninstall alike.\n */\nfunction withResolvedNamespaceRoot(schema: SafewordSchema, ctx: ProjectContext): SafewordSchema {\n const root = ctx.namespaceRoot ?? resolveNamespaceRoot(ctx.cwd);\n // Empty relative label means the namespace root IS the repo root\n // (paths.projectRoot: '.') — translate to bare subpaths, not legacy.\n const label = nodePath.relative(ctx.cwd, root) || '.';\n if (label === NAMESPACE_ROOT_LEGACY) return schema;\n\n const translate = (path: string): string => {\n if (path !== NAMESPACE_ROOT_LEGACY && !path.startsWith(`${NAMESPACE_ROOT_LEGACY}/`)) {\n return path;\n }\n const subpath = path.slice(NAMESPACE_ROOT_LEGACY.length).replace(/^\\//, '');\n return label === '.' ? subpath || '.' : nodePath.join(label, subpath);\n };\n\n return {\n ...schema,\n sharedDirs: schema.sharedDirs.map(path => translate(path)),\n preservedDirs: schema.preservedDirs.map(path => translate(path)),\n managedFiles: Object.fromEntries(\n Object.entries(schema.managedFiles)\n .map(([path, definition]) => [translate(path), definition] as const)\n // A per-root `.gitignore` (issue #272) translated onto the repo root\n // (paths.projectRoot: '.') would BE the user's own root `.gitignore` —\n // owned by the textPatch, never created by us (create-if-missing skips\n // an existing file). Drop it there so a full uninstall can't delete a\n // file we didn't write.\n .filter(([translatedPath]) => translatedPath !== '.gitignore'),\n ),\n };\n}\n\nexport async function reconcile(\n schema: SafewordSchema,\n mode: ReconcileMode,\n ctx: ProjectContext,\n options?: ReconcileOptions,\n): Promise<ReconcileResult> {\n // Public API contract is Promise<ReconcileResult>; some plan executors are\n // sync today, but the signature reserves room for async I/O without\n // breaking callers. Token await keeps the contract honest.\n await Promise.resolve();\n const isDryRun = options?.dryRun ?? false;\n\n const plan = computePlan(withResolvedNamespaceRoot(schema, ctx), mode, ctx);\n\n if (isDryRun) {\n return {\n actions: plan.actions,\n applied: false,\n created: plan.wouldCreate,\n updated: plan.wouldUpdate,\n removed: plan.wouldRemove,\n packagesToInstall: plan.packagesToInstall,\n packagesToRemove: plan.packagesToRemove,\n warnings: [],\n };\n }\n\n const result = executePlan(plan, ctx);\n\n return {\n actions: plan.actions,\n applied: true,\n created: result.created,\n updated: result.updated,\n removed: result.removed,\n packagesToInstall: plan.packagesToInstall,\n packagesToRemove: plan.packagesToRemove,\n warnings: result.warnings,\n };\n}\n\n// ============================================================================\n// Plan computation\n// ============================================================================\n\ninterface ReconcilePlan {\n actions: Action[];\n wouldCreate: string[];\n wouldUpdate: string[];\n wouldRemove: string[];\n packagesToInstall: string[];\n packagesToRemove: string[];\n}\n\nfunction computePlan(\n schema: SafewordSchema,\n mode: ReconcileMode,\n ctx: ProjectContext,\n): ReconcilePlan {\n switch (mode) {\n case 'install': {\n return computeInstallPlan(schema, ctx);\n }\n case 'upgrade': {\n return computeUpgradePlan(schema, ctx);\n }\n case 'uninstall': {\n return computeUninstallPlan(schema, ctx, false);\n }\n case 'uninstall-full': {\n return computeUninstallPlan(schema, ctx, true);\n }\n default: {\n // Exhaustive check - TypeScript ensures all cases are handled\n const _exhaustiveCheck: never = mode;\n return _exhaustiveCheck;\n }\n }\n}\n\nfunction computeInstallPlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n const wouldUpdate: string[] = [];\n const wouldRemove: string[] = [];\n\n // 1. Create all directories\n const allDirectories = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const directories = planMissingDirectories(allDirectories, ctx.cwd, ctx.isGitRepo);\n actions.push(...directories.actions);\n wouldCreate.push(...directories.created);\n\n // 2. Write owned files\n const owned = planOwnedFileWrites(schema.ownedFiles, ctx);\n actions.push(...owned.actions);\n wouldCreate.push(...owned.created);\n\n // 3. Write managed files (only if missing)\n const managed = planManagedFileWrites(schema.managedFiles, ctx);\n actions.push(...managed.actions);\n wouldCreate.push(...managed.created);\n\n // 4. chmod hook/lib/scripts directories\n const chmodPaths = [...CHMOD_PATHS];\n if (ctx.isGitRepo) chmodPaths.push(HUSKY_DIR);\n actions.push({ type: 'chmod', paths: chmodPaths });\n\n // 5. JSON merges\n for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition });\n }\n\n // 6. Text patches — unguarded patches create absent target files; guarded\n // patches only run when the current file proves it is safe to touch.\n actions.push(...planTextUnpatches(schema.legacyTextPatches, ctx));\n const textPatchActions = planTextPatches(schema.textPatches, ctx);\n actions.push(...textPatchActions);\n const dynamicChanges = planDynamicFileChanges(actions, ctx);\n wouldCreate.push(...dynamicChanges.created);\n wouldUpdate.push(...dynamicChanges.updated);\n wouldRemove.push(...dynamicChanges.removed);\n\n // 7. Compute packages to install\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.developmentDeps,\n ctx.cwd,\n );\n\n return {\n actions,\n wouldCreate: [...new Set(wouldCreate)],\n wouldUpdate: [...new Set(wouldUpdate)],\n wouldRemove: [...new Set(wouldRemove)],\n packagesToInstall,\n packagesToRemove: [],\n };\n}\n\ninterface FileActionResult {\n actions: Action[];\n created: string[];\n updated: string[];\n}\n\n/**\n * Plan actions for owned files (always update if content changed).\n */\nfunction planOwnedFilesActions(\n ownedFiles: Record<string, FileDefinition>,\n ctx: ProjectContext,\n): FileActionResult {\n const actions: Action[] = [];\n const created: string[] = [];\n const updated: string[] = [];\n\n for (const [filePath, definition] of Object.entries(ownedFiles)) {\n if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;\n\n const fullPath = nodePath.join(ctx.cwd, filePath);\n const newContent = resolveFileContent(definition, ctx);\n\n if (newContent === undefined) continue;\n if (!fileNeedsUpdate(fullPath, newContent)) continue;\n\n actions.push({ type: 'write', path: filePath, content: newContent });\n if (exists(fullPath)) {\n updated.push(filePath);\n } else {\n created.push(filePath);\n }\n }\n\n return { actions, created, updated };\n}\n\n/**\n * True when the managed-file entry has a `configKey` AND a corresponding\n * `paths.<configKey>` override is set in `.safeword/config.json`. Used to\n * suppress install scaffolding when the user has redirected the read target\n * (ticket K7N2QM). Authored knowledge is always preserved during uninstall.\n */\nfunction isConfigOverridden(definition: ManagedFileDefinition, cwd: string): boolean {\n if (!definition.configKey) return false;\n return readConfiguredPath(cwd, definition.configKey) !== undefined;\n}\n\n/**\n * Plan actions for managed files (only create if missing).\n *\n * Entries with a `configKey` whose `paths.<configKey>` override is set in\n * `.safeword/config.json` are suppressed — the user has redirected\n * safeword to read from elsewhere, so the default location is no longer\n * safeword's concern (ticket K7N2QM).\n */\nfunction planManagedFilesActions(\n managedFiles: Record<string, ManagedFileDefinition>,\n ctx: ProjectContext,\n): FileActionResult {\n const actions: Action[] = [];\n const created: string[] = [];\n\n for (const [filePath, definition] of Object.entries(managedFiles)) {\n if (isConfigOverridden(definition, ctx.cwd)) continue;\n\n const fullPath = nodePath.join(ctx.cwd, filePath);\n const newContent = resolveFileContent(definition, ctx);\n\n if (newContent === undefined) continue;\n if (exists(fullPath)) continue; // Don't update during upgrade\n\n actions.push({ type: 'write', path: filePath, content: newContent });\n created.push(filePath);\n }\n\n return { actions, created, updated: [] };\n}\n\nfunction computeUpgradePlan(schema: SafewordSchema, ctx: ProjectContext): ReconcilePlan {\n const actions: Action[] = [];\n const wouldCreate: string[] = [];\n const wouldUpdate: string[] = [];\n const wouldRemove: string[] = [];\n\n // 1. Ensure directories exist (skip .husky if not a git repo)\n const allDirectories = [...schema.ownedDirs, ...schema.sharedDirs, ...schema.preservedDirs];\n const missingDirectories = planMissingDirectories(allDirectories, ctx.cwd, ctx.isGitRepo);\n actions.push(...missingDirectories.actions);\n wouldCreate.push(...missingDirectories.created);\n\n // 2. Update owned files if content changed\n const ownedFilesResult = planOwnedFilesActions(schema.ownedFiles, ctx);\n actions.push(...ownedFilesResult.actions);\n wouldCreate.push(...ownedFilesResult.created);\n wouldUpdate.push(...ownedFilesResult.updated);\n\n // 3. Create missing managed files (don't update existing)\n const managedFilesResult = planManagedFilesActions(schema.managedFiles, ctx);\n actions.push(...managedFilesResult.actions);\n wouldCreate.push(...managedFilesResult.created);\n\n // 3b. Remove exact conditional scaffolds when their feature is disabled.\n const omittedManagedFiles = planOmittedManagedRemoval(schema.managedFiles, ctx);\n actions.push(...omittedManagedFiles.actions);\n wouldRemove.push(...omittedManagedFiles.removed);\n\n // 4. Remove deprecated files (renamed or removed in newer versions)\n const deprecatedFiles = planExistingFilesRemoval(schema.deprecatedFiles, ctx.cwd);\n actions.push(...deprecatedFiles.actions);\n wouldRemove.push(...deprecatedFiles.removed);\n\n // 4b. Remove deprecated directories (no longer managed by safeword)\n const deprecatedDirectories = planExistingDirectoriesRemoval(\n schema.deprecatedDirs,\n ctx.cwd,\n new Set(wouldRemove),\n );\n actions.push(...deprecatedDirectories.actions);\n wouldRemove.push(...deprecatedDirectories.removed);\n\n // 5. chmod\n actions.push({ type: 'chmod', paths: CHMOD_PATHS });\n\n // 6. JSON merges (always apply to ensure keys are present)\n for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-merge', path: filePath, definition });\n }\n\n // 7. Text patches\n actions.push(\n ...planTextUnpatches(schema.legacyTextPatches, ctx),\n ...planTextPatches(schema.textPatches, ctx),\n );\n const dynamicChanges = planDynamicFileChanges(actions, ctx);\n wouldCreate.push(...dynamicChanges.created);\n wouldUpdate.push(...dynamicChanges.updated);\n wouldRemove.push(...dynamicChanges.removed);\n\n // 8. Compute packages to install\n const packagesToInstall = computePackagesToInstall(\n schema,\n ctx.projectType,\n ctx.developmentDeps,\n ctx.cwd,\n );\n\n // 9. Compute deprecated packages to remove (only those actually installed)\n const packagesToRemove = schema.deprecatedPackages.filter(pkg =>\n Object.hasOwn(ctx.developmentDeps, pkg),\n );\n\n return {\n actions,\n wouldCreate: [...new Set(wouldCreate)],\n wouldUpdate: [...new Set(wouldUpdate)],\n wouldRemove: [...new Set(wouldRemove)],\n packagesToInstall,\n packagesToRemove,\n };\n}\n\nfunction computeUninstallPlan(\n schema: SafewordSchema,\n ctx: ProjectContext,\n full: boolean,\n): ReconcilePlan {\n const actions: Action[] = [];\n const wouldRemove: string[] = [];\n\n // 1. Remove all owned files and track parent dirs for cleanup.\n // cucumber.mjs is only safeword's when the lane is safeword's to scaffold —\n // in a suppressed-lane repo the root cucumber.mjs is the HOST's own config\n // and must survive uninstall (ticket 56JCFZ, TB1.AC3).\n const removableOwnedPaths = Object.keys(schema.ownedFiles).filter(\n filePath => filePath !== 'cucumber.mjs' || ctx.projectType.scaffoldBddLane,\n );\n const ownedFiles = planExistingFilesRemoval(removableOwnedPaths, ctx.cwd);\n actions.push(...ownedFiles.actions);\n wouldRemove.push(...ownedFiles.removed);\n\n // Collect parent dirs that need cleanup (for .claude/* skill dirs)\n const directoriesToCleanup = new Set<string>();\n for (const filePath of ownedFiles.removed) {\n const parentDirectory = getClaudeParentDirectoryForCleanup(filePath);\n if (parentDirectory) directoriesToCleanup.add(parentDirectory);\n }\n const cleanupDirectories = planExistingDirectoriesRemoval(\n [...directoriesToCleanup],\n ctx.cwd,\n new Set(wouldRemove),\n );\n actions.push(...cleanupDirectories.actions);\n wouldRemove.push(...cleanupDirectories.removed);\n\n // 2. JSON unmerges\n for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {\n actions.push({ type: 'json-unmerge', path: filePath, definition });\n }\n\n // 3. Text unpatches\n actions.push(\n ...planTextUnpatches(schema.textPatches, ctx),\n ...planTextUnpatches(schema.legacyTextPatches, ctx),\n );\n const dynamicChanges = planDynamicFileChanges(actions, ctx);\n wouldRemove.push(...dynamicChanges.removed);\n\n // 4. Remove preserved directories first (reverse order, only if empty)\n const preserved = planExistingDirectoriesRemoval(\n schema.preservedDirs.toReversed(),\n ctx.cwd,\n new Set(wouldRemove),\n );\n actions.push(...preserved.actions);\n wouldRemove.push(...preserved.removed);\n\n // 5. Remove owned directories (reverse order ensures children before parents)\n const owned = planExistingDirectoriesRemoval(\n schema.ownedDirs.toReversed(),\n ctx.cwd,\n new Set(wouldRemove),\n );\n actions.push(...owned.actions);\n wouldRemove.push(...owned.removed);\n\n // 6. Full uninstall: remove managed tooling, but preserve every project-owned\n // knowledge entry identified by configKey. Those files become authored\n // project context after scaffolding and must survive Safeword removal,\n // regardless of whether their configured path is default or overridden.\n if (full) {\n const managed = planConditionalManagedRemoval(schema.managedFiles, ctx, true);\n actions.push(...managed.actions);\n wouldRemove.push(...managed.removed);\n } else {\n // 6b. Default uninstall: conditional managed-file removal (ticket V4MATC).\n const conditional = planConditionalManagedRemoval(schema.managedFiles, ctx);\n actions.push(...conditional.actions);\n wouldRemove.push(...conditional.removed);\n }\n\n // 7. Compute packages to remove (full only)\n let packagesToRemove: string[] = [];\n if (full) {\n packagesToRemove = computePackagesToRemove(schema, ctx.projectType, ctx.developmentDeps);\n } else if (Object.hasOwn(ctx.developmentDeps, 'safeword')) {\n packagesToRemove = ['safeword'];\n }\n\n return {\n actions,\n wouldCreate: dynamicChanges.created,\n wouldUpdate: dynamicChanges.updated,\n wouldRemove: [...new Set(wouldRemove)],\n packagesToInstall: [],\n packagesToRemove,\n };\n}\n\ninterface DynamicFileChanges {\n readonly created: string[];\n readonly updated: string[];\n readonly removed: string[];\n}\n\n/**\n * Predict content-level action effects without executing them. Reconciliation\n * deliberately keeps executable actions even when their merge or patch is\n * already satisfied; public Plans must describe changes, not executor work.\n */\nfunction planDynamicFileChanges(\n actions: readonly Action[],\n ctx: ProjectContext,\n): DynamicFileChanges {\n const created = new Set<string>();\n const updated = new Set<string>();\n const removed = new Set<string>();\n\n for (const action of actions) {\n planDynamicActionChange(action, ctx, { created, updated, removed });\n }\n\n return {\n created: [...created],\n updated: [...updated],\n removed: [...removed],\n };\n}\n\nfunction planDynamicActionChange(\n action: Action,\n ctx: ProjectContext,\n changes: {\n readonly created: Set<string>;\n readonly updated: Set<string>;\n readonly removed: Set<string>;\n },\n): void {\n switch (action.type) {\n case 'json-merge': {\n planJsonMergeChange(action, ctx, changes.created, changes.updated);\n return;\n }\n case 'json-unmerge': {\n planJsonUnmergeChange(action, ctx, changes.updated, changes.removed);\n return;\n }\n case 'text-patch': {\n planTextPatchChange(action, ctx, changes.created, changes.updated);\n return;\n }\n case 'text-unpatch': {\n planTextUnpatchChange(action, ctx, changes.updated, changes.removed);\n return;\n }\n case 'chmod':\n case 'mkdir':\n case 'rm':\n case 'rmdir':\n case 'write': {\n return;\n }\n }\n}\n\nfunction planJsonMergeChange(\n action: Extract<Action, { type: 'json-merge' }>,\n ctx: ProjectContext,\n created: Set<string>,\n updated: Set<string>,\n): void {\n const fullPath = nodePath.join(ctx.cwd, action.path);\n const existing = readJson(fullPath) as Record<string, unknown> | undefined;\n if (existing === undefined && (exists(fullPath) || action.definition.skipIfMissing)) return;\n const merged = action.definition.merge(existing ?? {}, ctx);\n if (JSON.stringify(existing ?? {}) === JSON.stringify(merged)) return;\n if (existing === undefined) created.add(action.path);\n else updated.add(action.path);\n}\n\nfunction planJsonUnmergeChange(\n action: Extract<Action, { type: 'json-unmerge' }>,\n ctx: ProjectContext,\n updated: Set<string>,\n removed: Set<string>,\n): void {\n const existing = readJson(nodePath.join(ctx.cwd, action.path)) as\n Record<string, unknown> | undefined;\n if (existing === undefined) return;\n const unmerged = action.definition.unmerge(existing, ctx);\n const remainingKeys = Object.keys(unmerged).filter(key => unmerged[key] !== undefined);\n if (action.definition.removeFileIfEmpty && remainingKeys.length === 0) {\n removed.add(action.path);\n } else if (JSON.stringify(existing) !== JSON.stringify(unmerged)) {\n updated.add(action.path);\n }\n}\n\nfunction planTextPatchChange(\n action: Extract<Action, { type: 'text-patch' }>,\n ctx: ProjectContext,\n created: Set<string>,\n updated: Set<string>,\n): void {\n const original = readFileSafe(nodePath.join(ctx.cwd, action.path));\n const patched = computePatchedContent(original ?? '', action.definition);\n if (patched === original) return;\n if (original === undefined) created.add(action.path);\n else updated.add(action.path);\n}\n\nfunction planTextUnpatchChange(\n action: Extract<Action, { type: 'text-unpatch' }>,\n ctx: ProjectContext,\n updated: Set<string>,\n removed: Set<string>,\n): void {\n const original = readFileSafe(nodePath.join(ctx.cwd, action.path));\n if (!original) return;\n const unpatched = computeUnpatchedContent(original, action.definition);\n if (unpatched === original) return;\n if (shouldRemoveTextPatchTarget(unpatched, action.definition)) removed.add(action.path);\n else updated.add(action.path);\n}\n\n// ============================================================================\n// Plan execution\n// ============================================================================\n\nexport interface ExecutionResult {\n created: string[];\n updated: string[];\n removed: string[];\n warnings: string[];\n}\n\nexport class ReconcileExecutionError extends Error {\n readonly partial: ExecutionResult;\n readonly completedActions: readonly Action[];\n\n constructor(cause: unknown, partial: ExecutionResult, completedActions: readonly Action[]) {\n super(cause instanceof Error ? cause.message : String(cause));\n this.name = 'ReconcileExecutionError';\n this.partial = partial;\n this.completedActions = completedActions;\n }\n}\n\nexport function executeReconciliationActions(\n actions: readonly Action[],\n ctx: ProjectContext,\n): ExecutionResult {\n const created: string[] = [];\n const updated: string[] = [];\n const removed: string[] = [];\n const warnings: string[] = [];\n const result = { created, updated, removed, warnings };\n const completedActions: Action[] = [];\n\n for (const action of actions) {\n const targets = action.type === 'chmod' ? action.paths : [action.path];\n const before = new Map(\n targets.map(target => [target, observePath(nodePath.join(ctx.cwd, target))]),\n );\n try {\n executeAction(action, ctx, result);\n for (const target of targets) {\n recordObservedChange(\n target,\n before.get(target),\n observePath(nodePath.join(ctx.cwd, target)),\n result,\n );\n }\n completedActions.push(action);\n } catch (executionError) {\n throw new ReconcileExecutionError(executionError, result, completedActions);\n }\n }\n\n return result;\n}\n\nfunction executePlan(plan: ReconcilePlan, ctx: ProjectContext): ExecutionResult {\n return executeReconciliationActions(plan.actions, ctx);\n}\n\nfunction observePath(path: string): string | undefined {\n try {\n const stat = lstatSync(path);\n if (stat.isSymbolicLink()) return `link:${stat.mode}:${readlinkSync(path)}`;\n if (stat.isDirectory()) return `directory:${stat.mode}`;\n if (stat.isFile()) return `file:${stat.mode}:${readFile(path)}`;\n return `other:${stat.mode}`;\n } catch {\n return undefined;\n }\n}\n\nfunction recordObservedChange(\n target: string,\n before: string | undefined,\n after: string | undefined,\n result: ExecutionResult,\n): void {\n if (before === after) return;\n let changes = result.updated;\n if (before === undefined) changes = result.created;\n else if (after === undefined) changes = result.removed;\n if (!changes.includes(target)) changes.push(target);\n}\n\nfunction executeChmod(cwd: string, paths: string[]): void {\n for (const path of paths) {\n const fullPath = nodePath.join(cwd, path);\n if (exists(fullPath)) makeScriptsExecutable(fullPath);\n }\n}\n\nfunction executeRmdir(cwd: string, path: string): void {\n removeIfEmpty(nodePath.join(cwd, path));\n}\n\nfunction executeAction(action: Action, ctx: ProjectContext, result: ExecutionResult): void {\n switch (action.type) {\n case 'mkdir': {\n ensureDirectory(nodePath.join(ctx.cwd, action.path));\n break;\n }\n case 'rmdir': {\n executeRmdir(ctx.cwd, action.path);\n break;\n }\n case 'write': {\n executeWrite(ctx.cwd, action.path, action.content);\n break;\n }\n case 'rm': {\n executeFileRemoval(ctx.cwd, action.path, result);\n break;\n }\n case 'chmod': {\n executeChmod(ctx.cwd, action.paths);\n break;\n }\n case 'json-merge': {\n executeJsonMerge(ctx.cwd, action.path, action.definition, ctx, result);\n break;\n }\n case 'json-unmerge': {\n executeJsonUnmerge(ctx.cwd, action.path, action.definition, ctx);\n break;\n }\n case 'text-patch': {\n executeTextPatch(ctx.cwd, action.path, action.definition);\n break;\n }\n case 'text-unpatch': {\n executeTextUnpatch(ctx.cwd, action.path, action.definition);\n break;\n }\n }\n}\n\nfunction executeFileRemoval(cwd: string, path: string, result: ExecutionResult): void {\n const fullPath = nodePath.join(cwd, path);\n const stat = lstatIfExists(fullPath);\n if (stat === undefined) return;\n if (!isRemovableFile(stat)) {\n result.warnings.push(`Skipped file removal because ${path} is not a file or symlink.`);\n return;\n }\n\n try {\n // unlinkSync never recurses, so a path swapped to a directory after the\n // lstat check remains intact rather than being removed by a recursive rm.\n unlinkSync(fullPath);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR') return;\n if (code === 'EISDIR' || code === 'EPERM') {\n result.warnings.push(`Skipped file removal because ${path} is no longer a file or symlink.`);\n return;\n }\n throw error;\n }\n}\n\nfunction executeWrite(cwd: string, path: string, content: string): void {\n writeFile(nodePath.join(cwd, path), content);\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\nfunction resolveFileContent(definition: FileDefinition, ctx: ProjectContext): string | undefined {\n if (definition.generator) {\n // Generator decides (undefined = skip file). Takes precedence over\n // `template` so an entry can declare its template provenance (for the\n // schema↔templates contract) while gating on project context (56JCFZ).\n return definition.generator(ctx);\n }\n\n if (definition.template) {\n const templatesDirectory = getTemplatesDirectory();\n return readFile(nodePath.join(templatesDirectory, definition.template));\n }\n\n if (definition.content) {\n return typeof definition.content === 'function' ? definition.content() : definition.content;\n }\n\n throw new Error('FileDefinition must have template, content, or generator');\n}\n\nfunction fileNeedsUpdate(installedPath: string, newContent: string): boolean {\n if (!exists(installedPath)) return true;\n const currentContent = readFileSafe(installedPath);\n return currentContent?.trim() !== newContent.trim();\n}\n\nexport function computePackagesToInstall(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevelopmentDependencies: Record<string, string>,\n cwd?: string,\n): string[] {\n // Combine base packages with conditional packages\n const needed = [\n ...schema.packages.base,\n ...getConditionalPackages(schema.packages.conditional, projectType),\n ];\n\n // Skip packages provided by workspace members (prevents circular self-install in monorepos)\n const workspaceMembers = cwd ? getWorkspacePackageNames(cwd) : new Set<string>();\n\n // Strip version specifier (e.g. 'eslint@^9' → 'eslint') when checking installed deps\n return needed.filter(pkg => {\n const name = stripVersionSpecifier(pkg);\n return !workspaceMembers.has(name) && !Object.hasOwn(installedDevelopmentDependencies, name);\n });\n}\n\nfunction computePackagesToRemove(\n schema: SafewordSchema,\n projectType: ProjectType,\n installedDevelopmentDependencies: Record<string, string>,\n): string[] {\n const safewordPackages = [\n ...schema.packages.base,\n ...getConditionalPackages(schema.packages.conditional, projectType),\n ];\n\n // Only remove packages that are actually installed\n // Strip version specifier (e.g. 'eslint@^9' → 'eslint') when checking installed deps\n return safewordPackages.filter(pkg =>\n Object.hasOwn(installedDevelopmentDependencies, stripVersionSpecifier(pkg)),\n );\n}\n\n/**\n * Strip version specifier from a package name.\n * e.g. 'eslint@^9' → 'eslint', 'safeword' → 'safeword', '@next/eslint-plugin-next@^16' → '@next/eslint-plugin-next'\n */\nfunction stripVersionSpecifier(pkg: string): string {\n // Scoped packages start with @ — find the version @ after the scope\n const atIndex = pkg.startsWith('@') ? pkg.indexOf('@', 1) : pkg.indexOf('@');\n return atIndex === -1 ? pkg : pkg.slice(0, atIndex);\n}\n\nfunction executeJsonMerge(\n cwd: string,\n path: string,\n definition: JsonMergeDefinition,\n ctx: ProjectContext,\n result: ExecutionResult,\n): void {\n const fullPath = nodePath.join(cwd, path);\n const rawExisting = readJson(fullPath) as Record<string, unknown> | undefined;\n\n if (!rawExisting) {\n // A present-but-unparseable target (JSONC comments, a trailing comma, or genuine\n // malformation) reads as undefined just like an absent file. Skipping is correct —\n // overwriting would clobber the customer's config — but doing it silently hides the\n // merge that never landed (the #262 commented-`.markdownlint-cli2.jsonc` case, and the\n // same trap for any jsonMerge target). Distinguish the two: warn when the file exists,\n // stay silent when it's genuinely absent.\n if (exists(fullPath)) {\n result.warnings.push(\n `Skipped merging safeword config into ${path}: the file exists but could not be read as JSON ` +\n `(it may contain JSONC comments, which the merge engine does not support, or otherwise not be valid JSON). ` +\n `Add these keys manually if you need them: ${definition.keys.join(', ')}.`,\n );\n return;\n }\n // Genuinely absent: honor skipIfMissing; otherwise fall through and create it.\n if (definition.skipIfMissing) return;\n }\n\n const existing = rawExisting ?? {};\n const merged = definition.merge(existing, ctx);\n\n // Skip write if content is unchanged (avoids formatting churn)\n if (JSON.stringify(existing) === JSON.stringify(merged)) return;\n\n writeJson(fullPath, merged);\n}\n\nfunction executeJsonUnmerge(\n cwd: string,\n path: string,\n definition: JsonMergeDefinition,\n ctx: ProjectContext,\n): void {\n const fullPath = nodePath.join(cwd, path);\n if (!exists(fullPath)) return;\n\n const existing = readJson(fullPath) as Record<string, unknown> | undefined;\n if (!existing) return;\n\n const unmerged = definition.unmerge(existing, ctx);\n\n // Check if file should be removed\n if (definition.removeFileIfEmpty) {\n const remainingKeys = Object.keys(unmerged).filter(k => unmerged[k] !== undefined);\n if (remainingKeys.length === 0) {\n remove(fullPath);\n return;\n }\n }\n\n if (JSON.stringify(existing) === JSON.stringify(unmerged)) return;\n writeJson(fullPath, unmerged);\n}\n\n/**\n * The safeword-owned body lines of a re-renderable block, in order, taken from\n * the freshly-resolved content (the header/marker line excluded).\n */\nfunction rerenderBlockLines(definition: ResolvedTextPatch): string[] {\n return definition.content\n .split('\\n')\n .filter(line => line !== '' && !line.includes(definition.marker));\n}\n\nfunction isOwnedRerenderLine(\n line: string,\n expectedLine: string | undefined,\n definition: ResolvedTextPatch,\n): boolean {\n return (\n line === expectedLine ||\n (expectedLine !== undefined && definition.rerenderOwnedLinePattern?.test(line) === true)\n );\n}\n\n/**\n * Remove a re-renderable managed block so a fresh, ctx-resolved version can be\n * re-appended (#293). Consumes the `marker` header plus the contiguous run of\n * disk lines that match the fresh block's body lines *in order*. Definitions\n * whose ctx can replace lines may also supply a narrow owned-line recognizer;\n * either way, the first non-owned line terminates removal.\n */\nfunction stripRerenderBlock(content: string, definition: ResolvedTextPatch): string {\n const lines = content.split('\\n');\n const startIndex = lines.findIndex(line => line.includes(definition.marker));\n if (startIndex === -1) return content;\n\n const blockLines = rerenderBlockLines(definition);\n let endIndex = startIndex;\n let expected = 0;\n while (endIndex + 1 < lines.length) {\n const nextLine = lines[endIndex + 1] ?? '';\n const expectedLine = blockLines[expected];\n if (!isOwnedRerenderLine(nextLine, expectedLine, definition)) break;\n endIndex += 1;\n expected += 1;\n }\n\n // Also drop a single blank separator line immediately before the header.\n const blockStart = startIndex > 0 && lines[startIndex - 1] === '' ? startIndex - 1 : startIndex;\n lines.splice(blockStart, endIndex - blockStart + 1);\n return lines.join('\\n').replaceAll(/\\n{3,}/g, '\\n\\n');\n}\n\nfunction stripRerenderBlocks(content: string, definition: ResolvedTextPatch): string {\n let stripped = content;\n while (stripped.includes(definition.marker)) {\n const next = stripRerenderBlock(stripped, definition);\n if (next === stripped) break;\n stripped = next;\n }\n return stripped;\n}\n\nfunction executeTextPatch(cwd: string, path: string, definition: ResolvedTextPatch): void {\n // rerender re-appends the block at EOF, so it only makes sense for `append`.\n if (definition.rerender && definition.operation !== 'append') {\n throw new Error(`rerender text patch ${path} must use operation: 'append'`);\n }\n\n const fullPath = nodePath.join(cwd, path);\n const original = readFileSafe(fullPath) ?? '';\n const patched = computePatchedContent(original, definition);\n if (patched !== original) writeFile(fullPath, patched);\n}\n\nfunction computePatchedContent(original: string, definition: ResolvedTextPatch): string {\n // Supersede: byte-exact strip of the legacy block this patch replaces (a no-op\n // when absent), so a customized managed file migrates the old block to `content`\n // on upgrade. Runs before the marker check so the swap still applies when the\n // replacement block is being added for the first time.\n const content =\n definition.supersedes === undefined ? original : original.replace(definition.supersedes, '');\n\n // Already patched: re-render/heal in place, persisting any supersede strip.\n if (content.includes(definition.marker)) {\n return healAlreadyPatchedContent(content, definition);\n }\n\n // Apply patch (this write also persists any supersede strip above).\n if (definition.operation === 'prepend') return definition.content + content;\n const separator =\n content !== '' && !content.endsWith('\\n') && !definition.content.startsWith('\\n') ? '\\n' : '';\n return content + separator + definition.content;\n}\n\n// The marker is already present. Re-render a drifted managed block (#293), heal a\n// legacy `---#` separator artifact from safeword <=0.30.1, and/or persist a\n// supersede strip. `content` is the post-supersede on-disk text.\nfunction healAlreadyPatchedContent(content: string, definition: ResolvedTextPatch): string {\n // Re-renderable block (#293): when the resolved content has drifted from what's\n // on disk (e.g. a custom paths.projectRoot was added), replace the managed block\n // in place so existing installs heal on upgrade. A no-op when already current.\n if (definition.rerender && !content.includes(definition.content)) {\n return stripRerenderBlocks(content, definition) + definition.content;\n }\n // Heal the bare-`---` separator that glued to a heading (`---# CLAUDE.md`) in\n // files corrupted by past installs. Narrowly scoped: the marker proves we\n // authored the block, and only the exact artifact is repaired.\n let healed = content;\n if (healed.includes('\\n\\n---#')) {\n healed = healed.replaceAll('\\n\\n---#', '\\n\\n---\\n\\n#');\n }\n // Persist if a heal and/or supersede strip changed the file (the latter is rare\n // here — a legacy block lingering alongside its replacement).\n return healed;\n}\n\nfunction computeUnpatchedContent(content: string, definition: ResolvedTextPatch): string {\n let unpatched = removeExactTextPatchContent(content, definition);\n\n // Re-render patches (#293): the on-disk block can differ from the ctx-resolved\n // content (e.g. paths.projectRoot changed since install), so an exact removal\n // misses it. Fall back to the marker-anchored, sequence-bounded strip — which\n // never consumes a customer line that follows the block.\n if (definition.rerender && unpatched.includes(definition.marker)) {\n unpatched = stripRerenderBlocks(unpatched, definition);\n }\n\n // Legacy prepend blocks may have a malformed separator (`---# Heading`) from\n // safeword <=0.30.1. If the file starts with our managed marker, remove the\n // whole preamble through the first separator and preserve the customer text.\n if (unpatched === content && content.startsWith(definition.marker)) {\n const separatorIndex = content.indexOf('\\n---');\n if (separatorIndex !== -1) {\n unpatched = content.slice(separatorIndex + '\\n---'.length).replace(/^\\n+/, '');\n }\n }\n\n const firstLine = content.split('\\n', 1)[0] ?? '';\n if (unpatched === content && firstLine.includes(definition.marker)) {\n unpatched = content.split('\\n').slice(1).join('\\n').replace(/^\\n+/, ''); // drop leading blanks\n unpatched = stripLeadingSafewordSeparator(unpatched);\n }\n\n return appendReplacementAfterUnpatch(content, unpatched, definition.replacementAfterUnpatch);\n}\n\nfunction appendReplacementAfterUnpatch(\n original: string,\n unpatched: string,\n replacement: string | undefined,\n): string {\n return unpatched === original || replacement === undefined ? unpatched : unpatched + replacement;\n}\n\nfunction executeTextUnpatch(cwd: string, path: string, definition: ResolvedTextPatch): void {\n const fullPath = nodePath.join(cwd, path);\n const content = readFileSafe(fullPath);\n if (!content) return;\n\n const unpatched = computeUnpatchedContent(content, definition);\n\n if (shouldRemoveTextPatchTarget(unpatched, definition)) {\n remove(fullPath);\n return;\n }\n\n if (unpatched !== content) writeFile(fullPath, unpatched);\n}\n\nfunction removeExactTextPatchContent(content: string, definition: ResolvedTextPatch): string {\n let unpatched = content.replace(definition.content, '');\n const extraContents = definition.unpatchContent ?? [];\n for (const extraContent of extraContents) {\n unpatched = unpatched.replace(extraContent, '');\n }\n return unpatched;\n}\n\nfunction containsTextPatchContent(content: string, definition: TextPatchDefinition): boolean {\n return (\n content.includes(definition.marker) ||\n (definition.unpatchContent?.some(extraContent => content.includes(extraContent)) ?? false)\n );\n}\n\nfunction shouldRemoveTextPatchTarget(content: string, definition: TextPatchDefinition): boolean {\n const trimmed = content.trim();\n return (\n definition.removeFileIfContentEquals?.some(candidate => trimmed === candidate.trim()) ?? false\n );\n}\n","/**\n * Rust language pack setup\n *\n * Handles Rust-specific setup logic including:\n * - Cargo.toml [lints.clippy] merge\n */\n\nimport {\n existsSync,\n lstatSync,\n readdirSync,\n readFileSync,\n realpathSync,\n writeFileSync,\n} from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { parse } from 'smol-toml';\n\nimport type { SetupResult } from '../types.js';\n\n// Default lint configuration for Cargo.toml\nconst SAFEWORD_CLIPPY_LINTS = `[lints.clippy]\n# Enable pedantic for stricter linting (priority -1 allows individual overrides)\npedantic = { level = \"deny\", priority = -1 }\n\n# Cherry-picked restriction lints for LLM enforcement\nunwrap_used = \"deny\"\nexpect_used = \"deny\"\ntodo = \"deny\"\n\n# Allow common pedantic noise\nmissing_errors_doc = \"allow\"\nmissing_panics_doc = \"allow\"\nmodule_name_repetitions = \"allow\"\n\n[lints.rust]\n# Deny unsafe code by default (LLMs shouldn't write unsafe)\nunsafe_code = \"deny\"\n`;\n\n/**\n * Regex to extract package name from Cargo.toml.\n * Matches: [package] ... name = \"package-name\"\n * Captures the package name in group 1.\n *\n * Note: This regex is also defined in templates/hooks/lib/lint.ts because\n * template files must be self-contained (they're copied to user projects).\n */\nconst CARGO_PACKAGE_NAME_REGEX = /\\[package\\][^[]*name\\s*=\\s*\"([^\"]+)\"/;\n\nfunction isContainedPath(root: string, candidate: string): boolean {\n const relative = nodePath.relative(root, candidate);\n return (\n relative === '' || (!relative.startsWith(`..${nodePath.sep}`) && !nodePath.isAbsolute(relative))\n );\n}\n\nfunction containedWorkspaceMember(cwd: string, member: string): string | undefined {\n if (nodePath.isAbsolute(member)) return undefined;\n const root = nodePath.resolve(cwd);\n const candidate = nodePath.resolve(root, member);\n if (!isContainedPath(root, candidate)) return undefined;\n if (existsSync(candidate) && !isContainedPath(realpathSync(root), realpathSync(candidate))) {\n return undefined;\n }\n return nodePath.relative(root, candidate);\n}\n\nfunction isSafeCargoManifest(cwd: string, cargoPath: string): boolean {\n if (!existsSync(cargoPath)) return false;\n const stat = lstatSync(cargoPath);\n return (\n stat.isFile() &&\n !stat.isSymbolicLink() &&\n isContainedPath(realpathSync(cwd), realpathSync(cargoPath))\n );\n}\n\nconst SAFEWORD_WORKSPACE_LINTS = `[workspace.lints.clippy]\n# Enable pedantic for stricter linting (priority -1 allows individual overrides)\npedantic = { level = \"deny\", priority = -1 }\n\n# Cherry-picked restriction lints for LLM enforcement\nunwrap_used = \"deny\"\nexpect_used = \"deny\"\ntodo = \"deny\"\n\n# Allow common pedantic noise\nmissing_errors_doc = \"allow\"\nmissing_panics_doc = \"allow\"\nmodule_name_repetitions = \"allow\"\n\n[workspace.lints.rust]\n# Deny unsafe code by default (LLMs shouldn't write unsafe)\nunsafe_code = \"deny\"\n`;\n\n/**\n * Detect the Rust package name for a given file path.\n *\n * Walks up from the file's directory to find the nearest Cargo.toml\n * with a [package] section, then extracts the package name.\n *\n * @param filePath - Absolute path to a .rs file\n * @param cwd - Project root directory (to stop walking)\n * @returns Package name, or undefined if not found\n */\nexport function detectRustPackage(filePath: string, cwd: string): string | undefined {\n // Start from the file's directory and walk up\n let currentDirectory = nodePath.dirname(filePath);\n\n // Normalize paths for comparison\n const normalizedCwd = nodePath.resolve(cwd);\n\n while (isContainedPath(normalizedCwd, currentDirectory)) {\n if (\n existsSync(currentDirectory) &&\n !isContainedPath(realpathSync(normalizedCwd), realpathSync(currentDirectory))\n ) {\n return undefined;\n }\n const cargoPath = nodePath.join(currentDirectory, 'Cargo.toml');\n\n if (isSafeCargoManifest(normalizedCwd, cargoPath)) {\n const content = readFileSync(cargoPath, 'utf8');\n\n // Check if this Cargo.toml has a [package] section\n if (content.includes('[package]')) {\n const nameMatch = CARGO_PACKAGE_NAME_REGEX.exec(content);\n return nameMatch?.[1];\n }\n }\n\n // Move up one directory\n const parentDirectory = nodePath.dirname(currentDirectory);\n if (parentDirectory === currentDirectory) break; // Reached filesystem root\n currentDirectory = parentDirectory;\n }\n\n return undefined;\n}\n\n/**\n * Detect workspace type from Cargo.toml content\n */\nexport function detectWorkspaceType(\n cargoContent: string,\n): 'virtual' | 'root-package' | 'single-crate' {\n const hasWorkspace = hasExactTableHeader(cargoContent, 'workspace');\n const hasPackage = hasExactTableHeader(cargoContent, 'package');\n\n if (hasWorkspace && !hasPackage) return 'virtual';\n if (hasWorkspace && hasPackage) return 'root-package';\n return 'single-crate';\n}\n\nfunction hasExactTableHeader(content: string, table: string): boolean {\n let multilineDelimiter: '\"\"\"' | \"'''\" | undefined;\n const header = `[${table}]`;\n for (const line of content.split(/\\r?\\n/u)) {\n if (multilineDelimiter !== undefined) {\n if (hasOddOccurrences(line, multilineDelimiter)) multilineDelimiter = undefined;\n continue;\n }\n const trimmed = line.trim();\n if (trimmed === header || trimmed.startsWith(`${header} #`)) return true;\n multilineDelimiter = openingMultilineDelimiter(line);\n }\n return false;\n}\n\nfunction hasOddOccurrences(line: string, delimiter: string): boolean {\n return line.split(delimiter).length % 2 === 0;\n}\n\nfunction openingMultilineDelimiter(line: string): '\"\"\"' | \"'''\" | undefined {\n const candidates = ['\"\"\"', \"'''\"] as const;\n return candidates\n .filter(delimiter => hasOddOccurrences(line, delimiter))\n .toSorted((left, right) => line.indexOf(left) - line.indexOf(right))[0];\n}\n\n/**\n * Check if Cargo.toml already has lint configuration\n */\nfunction hasExistingLints(cargoContent: string): boolean {\n const manifest = parseCargoManifest(cargoContent);\n const workspace = isTomlTable(manifest?.workspace) ? manifest.workspace : undefined;\n return isTomlTable(manifest?.lints) || isTomlTable(workspace?.lints);\n}\n\ntype TomlTable = Record<string, unknown>;\n\nfunction isTomlTable(value: unknown): value is TomlTable {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseCargoManifest(content: string): TomlTable | undefined {\n try {\n const manifest = parse(content);\n return isTomlTable(manifest) ? manifest : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Cargo manifests the Rust pack will update, without changing the project. */\nexport function rustToolingTargets(cwd: string): string[] {\n const rootTarget = 'Cargo.toml';\n const cargoPath = nodePath.join(cwd, rootTarget);\n if (!isSafeCargoManifest(cwd, cargoPath)) return [];\n const content = readFileSync(cargoPath, 'utf8');\n const targets = hasExistingLints(content) ? [] : [rootTarget];\n if (detectWorkspaceType(content) === 'single-crate') return targets;\n for (const member of parseWorkspaceMembers(content, cwd)) {\n const target = nodePath.join(member, 'Cargo.toml');\n const memberPath = nodePath.join(cwd, target);\n if (!isSafeCargoManifest(cwd, memberPath)) continue;\n if (!hasExistingLints(readFileSync(memberPath, 'utf8'))) targets.push(target);\n }\n return targets;\n}\n\n/**\n * Expand a single member pattern (handles glob patterns like \"crates/*\")\n */\nfunction expandMemberPattern(cwd: string, pattern: string): string[] {\n // Simple glob: only handle trailing /* (most common Cargo workspace pattern)\n if (pattern.endsWith('/*')) {\n const baseDirectory = containedWorkspaceMember(cwd, pattern.slice(0, -2));\n if (baseDirectory === undefined) return [];\n const fullPath = nodePath.join(cwd, baseDirectory);\n\n if (!existsSync(fullPath)) return [];\n\n try {\n const entries = readdirSync(fullPath, { withFileTypes: true });\n return entries\n .filter(entry => {\n if (!entry.isDirectory()) return false;\n // Only include directories that have Cargo.toml (are crates)\n const cargoPath = nodePath.join(fullPath, entry.name, 'Cargo.toml');\n return existsSync(cargoPath);\n })\n .map(entry => nodePath.join(baseDirectory, entry.name));\n } catch {\n return [];\n }\n }\n\n // Not a glob pattern, return as-is\n const member = containedWorkspaceMember(cwd, pattern);\n return member === undefined ? [] : [member];\n}\n\n/**\n * Parse workspace members from Cargo.toml\n * Supports both inline array and multi-line formats:\n * - members = [\"crates/a\", \"crates/b\"]\n * - members = [\\n \"crates/a\",\\n \"crates/b\"\\n]\n *\n * Also expands simple glob patterns like \"crates/*\"\n *\n * @param cargoContent - Content of root Cargo.toml\n * @param cwd - Project root directory (needed for glob expansion)\n * @returns Array of expanded member paths\n */\nexport function parseWorkspaceMembers(cargoContent: string, cwd: string): string[] {\n const manifest = parseCargoManifest(cargoContent);\n const members = isTomlTable(manifest?.workspace) ? manifest.workspace.members : undefined;\n if (!Array.isArray(members)) return [];\n const rawMembers = members.filter((member): member is string => typeof member === 'string');\n\n // Expand glob patterns\n const expandedMembers: string[] = [];\n for (const member of rawMembers) {\n expandedMembers.push(...expandMemberPattern(cwd, member));\n }\n\n return expandedMembers;\n}\n\n/**\n * Add [lints] workspace = true to a member crate's Cargo.toml\n */\nfunction addWorkspaceLints(cwd: string, memberCargoPath: string): void {\n if (!isSafeCargoManifest(cwd, memberCargoPath)) return;\n\n const content = readFileSync(memberCargoPath, 'utf8');\n\n // Skip if already has any lints configuration\n if (hasExistingLints(content)) return;\n\n const lintsSection = `[lints]\nworkspace = true\n`;\n\n const newContent = `${content.trimEnd()}\\n\\n${lintsSection}`;\n writeFileSync(memberCargoPath, newContent, 'utf8');\n}\n\n/**\n * Add lint configuration to root Cargo.toml if not present\n */\nfunction addRootLints(cargoPath: string, content: string, isWorkspace: boolean): void {\n if (hasExistingLints(content)) return;\n\n const lintsToAdd = isWorkspace ? SAFEWORD_WORKSPACE_LINTS : SAFEWORD_CLIPPY_LINTS;\n const newContent = `${content.trimEnd()}\\n\\n${lintsToAdd}`;\n writeFileSync(cargoPath, newContent, 'utf8');\n}\n\n/**\n * Add lints.workspace = true to all workspace member crates\n */\nfunction addMemberLints(cwd: string, content: string): void {\n const members = parseWorkspaceMembers(content, cwd);\n for (const member of members) {\n const memberCargoPath = nodePath.join(cwd, member, 'Cargo.toml');\n addWorkspaceLints(cwd, memberCargoPath);\n }\n}\n\n/**\n * Set up Rust tooling configuration.\n *\n * @param cwd - Project root directory\n * @returns Setup result\n */\nexport function setupRustTooling(cwd: string): SetupResult {\n const cargoPath = nodePath.join(cwd, 'Cargo.toml');\n if (!isSafeCargoManifest(cwd, cargoPath)) return { files: [] };\n\n // Read Cargo.toml once\n const content = readFileSync(cargoPath, 'utf8');\n const workspaceType = detectWorkspaceType(content);\n const isWorkspace = workspaceType !== 'single-crate';\n\n // Add lint configuration to root\n addRootLints(cargoPath, content, isWorkspace);\n\n // For workspaces, add lints.workspace = true to member crates\n if (isWorkspace) {\n addMemberLints(cwd, content);\n }\n\n return { files: [] };\n}\n","/**\n * Pack Config Tracking\n *\n * Helpers to read/write installedPacks in .safeword/config.json\n */\n\nimport nodePath from 'node:path';\nimport process from 'node:process';\n\nimport { readFileSafe, writeFile } from '../utils/fs.js';\n\nconst CONFIG_PATH = '.safeword/config.json';\n\ninterface SafewordConfig {\n installedPacks: string[];\n autoUpgrade?: boolean;\n /** SQL pack options — `fix: true` opts in to edit-time `sqlfluff fix` (#638). */\n sql?: { fix?: boolean };\n}\n\nfunction readConfig(cwd: string): SafewordConfig | undefined {\n const configPath = nodePath.join(cwd, CONFIG_PATH);\n const content = readFileSafe(configPath);\n if (!content) return undefined;\n return JSON.parse(content) as SafewordConfig;\n}\n\nfunction writeConfig(cwd: string, config: SafewordConfig): void {\n const configPath = nodePath.join(cwd, CONFIG_PATH);\n writeFile(configPath, JSON.stringify(config, undefined, 2));\n}\n\n/**\n * Check if auto-upgrade is enabled for this project.\n * Env vars override config: SAFEWORD_NO_AUTO_UPGRADE or CI disables.\n * Config file: autoUpgrade defaults to true when absent.\n */\nexport function shouldAutoUpgrade(cwd: string): boolean {\n if (process.env.SAFEWORD_NO_AUTO_UPGRADE) return false;\n if (process.env.CI) return false;\n const config = readConfig(cwd);\n return config?.autoUpgrade !== false;\n}\n\n/**\n * Get the list of installed packs from config.\n *\n * @param cwd - Project root directory\n * @returns Array of installed pack IDs, or empty array if no config\n */\nexport function getInstalledPacks(cwd: string): string[] {\n const config = readConfig(cwd);\n return config?.installedPacks ?? [];\n}\n\n/**\n * Check if a specific pack is installed.\n *\n * @param cwd - Project root directory\n * @param packId - Pack ID to check\n * @returns true if pack is in installedPacks\n */\nexport function isPackInstalled(cwd: string, packId: string): boolean {\n return getInstalledPacks(cwd).includes(packId);\n}\n\n/**\n * Add a pack to the installed packs list.\n * Creates config.json if it doesn't exist.\n */\nexport function addInstalledPack(cwd: string, packId: string): void {\n const config = readConfig(cwd) ?? { installedPacks: [] };\n\n if (!config.installedPacks.includes(packId)) {\n config.installedPacks.push(packId);\n writeConfig(cwd, config);\n }\n}\n","/**\n * Go-specific Setup Utilities\n *\n * Setup logic for Go projects.\n * Config generators are in files.ts (same pattern as TypeScript and Python).\n *\n * Future: Layer detection for depguard import rules.\n */\n\nimport type { SetupResult } from '../types.js';\n\n/**\n * Set up Go tooling configuration.\n *\n * Note: .golangci.yml is now created by the schema system (managedFiles).\n * This function exists for future Go-specific setup (layer detection).\n *\n * @returns Empty result (schema handles file creation)\n */\nexport function setupGoTooling(): SetupResult {\n // .golangci.yml is created by schema.ts managedFiles\n // Future: Add layer detection for depguard rules here\n return { files: [] };\n}\n","/**\n * Go Language Pack\n *\n * Detects Go projects. Config file (.golangci.yml) is created by schema.\n */\n\nimport { existsInTree } from '../../utils/fs.js';\nimport type { LanguagePack, SetupContext, SetupResult } from '../types.js';\nimport { setupGoTooling } from './setup.js';\n\nexport const golangPack: LanguagePack = {\n id: 'golang',\n name: 'Go',\n extensions: ['.go'],\n\n detect(cwd: string): boolean {\n return existsInTree(cwd, 'go.mod');\n },\n\n setup(_cwd: string, _ctx: SetupContext): SetupResult {\n // .golangci.yml is created by schema.ts (managedFiles)\n return setupGoTooling();\n },\n};\n","/**\n * Python Language Pack\n *\n * Wraps existing setup.ts functionality in the LanguagePack interface.\n */\n\nimport { existsInTree } from '../../utils/fs.js';\nimport type { LanguagePack, SetupContext, SetupResult } from '../types.js';\nimport { setupPythonTooling } from './setup.js';\n\nexport const pythonPack: LanguagePack = {\n id: 'python',\n name: 'Python',\n extensions: ['.py', '.pyi'],\n\n detect(cwd: string): boolean {\n return (\n existsInTree(cwd, 'pyproject.toml') ||\n existsInTree(cwd, 'requirements.txt') ||\n existsInTree(cwd, 'Pipfile') ||\n existsInTree(cwd, 'setup.py') ||\n existsInTree(cwd, 'setup.cfg')\n );\n },\n\n setup(_cwd: string, _ctx: SetupContext): SetupResult {\n // Config files created by schema.ts (ownedFiles/managedFiles)\n return setupPythonTooling();\n },\n};\n","/**\n * Rust language pack\n *\n * Provides strict Clippy and rustfmt configuration for Rust projects.\n */\n\nimport { existsInTree } from '../../utils/fs.js';\nimport type { LanguagePack, SetupContext, SetupResult } from '../types.js';\nimport { setupRustTooling } from './setup.js';\n\nexport const rustPack: LanguagePack = {\n id: 'rust',\n name: 'Rust',\n extensions: ['.rs'],\n\n detect(cwd: string): boolean {\n return existsInTree(cwd, 'Cargo.toml');\n },\n\n setup(cwd: string, _ctx: SetupContext): SetupResult {\n return setupRustTooling(cwd);\n },\n};\n","/**\n * SQL Language Pack\n *\n * Detects SQL projects (dbt, sqlc, Flyway, Atlas, Prisma, Drizzle, etc.)\n * and configures SQLFluff for SQL linting with Jinja-aware parsing.\n */\n\nimport { existsSync, readdirSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { existsInTree } from '../../utils/fs.js';\nimport type { LanguagePack, SetupContext, SetupResult } from '../types.js';\n\n// Tier 1: Config file markers that unambiguously indicate SQL work\nconst TIER1_MARKERS = [\n 'dbt_project.yml',\n '.sqlfluff',\n 'sqlc.yaml',\n 'sqlc.yml',\n 'sqlc.json',\n 'flyway.toml',\n 'flyway.conf',\n 'atlas.hcl',\n 'liquibase.properties',\n 'schemachange-config.yml',\n];\n\n// Tier 2: Directories that indicate SQL work when they contain .sql files\nconst TIER2_DIRECTORIES = ['prisma/migrations', 'drizzle', 'db/migrations'];\n\n/** Check if a directory exists and contains at least one .sql file (checks one level of subdirs too). */\nfunction directoryHasSqlFiles(directory: string): boolean {\n if (!existsSync(directory)) return false;\n try {\n const entries = readdirSync(directory, { withFileTypes: true });\n // Check direct .sql files (drizzle/, db/migrations/)\n if (entries.some(entry => entry.isFile() && entry.name.endsWith('.sql'))) return true;\n // Check subdirectories for .sql files (prisma/migrations/20210313_init/migration.sql)\n for (const entry of entries) {\n if (!entry.isDirectory()) {\n continue;\n }\n\n const subEntries = readdirSync(nodePath.join(directory, entry.name));\n if (subEntries.some(f => f.endsWith('.sql'))) return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport const sqlPack: LanguagePack = {\n id: 'sql',\n name: 'SQL',\n extensions: ['.sql'],\n\n detect(cwd: string): boolean {\n // Tier 1: config file markers (high confidence)\n for (const marker of TIER1_MARKERS) {\n if (existsInTree(cwd, marker)) return true;\n }\n\n // Tier 2: directory conventions with .sql files\n for (const directory of TIER2_DIRECTORIES) {\n if (directoryHasSqlFiles(nodePath.join(cwd, directory))) return true;\n }\n\n return false;\n },\n\n setup(_cwd: string, _ctx: SetupContext): SetupResult {\n // Config files created by schema.ts (ownedFiles/managedFiles)\n return { files: [] };\n },\n};\n","/**\n * TypeScript Language Pack\n *\n * Detects TypeScript/JavaScript projects.\n * Config files (ESLint, Prettier) are created by schema.\n */\n\nimport nodePath from 'node:path';\n\nimport { exists } from '../../utils/fs.js';\nimport type { LanguagePack, SetupContext, SetupResult } from '../types.js';\nimport { setupTypescriptTooling } from './setup.js';\n\nexport const typescriptPack: LanguagePack = {\n id: 'typescript',\n name: 'TypeScript',\n extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],\n\n detect(cwd: string): boolean {\n return exists(nodePath.join(cwd, 'package.json'));\n },\n\n setup(_cwd: string, _ctx: SetupContext): SetupResult {\n // ESLint/Prettier configs created by schema.ts (ownedFiles/managedFiles)\n return setupTypescriptTooling();\n },\n};\n","/**\n * TypeScript-specific Setup Utilities\n *\n * Setup logic for TypeScript/JavaScript projects.\n * Config generators are in files.ts (same pattern as Python and Go).\n *\n * Note: ESLint/Prettier setup is handled by the schema system.\n * This file exists for consistency with other language packs.\n */\n\nimport type { SetupResult } from '../types.js';\n\n/**\n * Set up TypeScript tooling configuration.\n *\n * Note: ESLint and Prettier configs are created by the schema system\n * (ownedFiles and managedFiles). This function exists for consistency\n * with other language packs and future TypeScript-specific setup.\n *\n * @returns Empty result (schema handles file creation)\n */\nexport function setupTypescriptTooling(): SetupResult {\n // Config files created by schema.ts ownedFiles/managedFiles\n // Future: Add TypeScript-specific setup logic here\n return { files: [] };\n}\n","/**\n * Pack Registry\n *\n * Central registry of language packs with lookup helpers.\n */\n\nimport { getInstalledPacks } from './config.js';\nimport { golangPack } from './golang/index.js';\nimport { pythonPack } from './python/index.js';\nimport { rustPack } from './rust/index.js';\nimport { sqlPack } from './sql/index.js';\nimport type { LanguagePack } from './types.js';\nimport { typescriptPack } from './typescript/index.js';\n\n/**\n * All registered language packs.\n */\nexport const LANGUAGE_PACKS: Record<string, LanguagePack> = {\n sql: sqlPack,\n golang: golangPack,\n python: pythonPack,\n rust: rustPack,\n typescript: typescriptPack,\n};\n\n/**\n * Find the language pack for a file extension.\n *\n * @param ext - File extension including dot (e.g., '.py')\n * @returns The matching pack or undefined\n */\nexport function findPackForExtension(extension: string): LanguagePack | undefined {\n for (const pack of Object.values(LANGUAGE_PACKS)) {\n if (pack.extensions.includes(extension)) {\n return pack;\n }\n }\n return undefined;\n}\n\n/**\n * Detect which languages are present in a project.\n *\n * @param cwd - Project root directory\n * @returns Array of pack IDs for detected languages\n */\nexport function detectLanguages(cwd: string): string[] {\n const detected: string[] = [];\n\n for (const pack of Object.values(LANGUAGE_PACKS)) {\n if (pack.detect(cwd)) {\n detected.push(pack.id);\n }\n }\n\n return detected;\n}\n\n/**\n * Get pack IDs for detected languages that aren't installed.\n *\n * @param cwd - Project root directory\n * @returns Array of missing pack IDs\n */\nexport function getMissingPacks(cwd: string): string[] {\n const detected = detectLanguages(cwd);\n const installed = getInstalledPacks(cwd);\n return detected.filter(packId => !installed.includes(packId));\n}\n","/**\n * Project Context Utilities\n *\n * Shared helpers for creating ProjectContext objects used by reconcile().\n */\n\nimport nodePath from 'node:path';\n\nimport type { ProjectContext } from '../schema.js';\nimport { resolveNamespaceRoot } from './configured-paths.js';\nimport { readJson } from './fs.js';\nimport { isGitRepo } from './git.js';\nimport { detectHookManagerWorld } from './hook-manager.js';\nimport { detectLanguages, detectProjectType, type PackageJson } from './project-detector.js';\n\n/**\n * Create a ProjectContext from the current working directory.\n *\n * Reads package.json, detects project type and languages for use with reconcile().\n * @param cwd - Working directory\n */\nexport function createProjectContext(cwd: string): ProjectContext {\n const packageJson = readJson(nodePath.join(cwd, 'package.json')) as PackageJson | undefined;\n\n return {\n cwd,\n projectType: detectProjectType(packageJson ?? {}, cwd),\n developmentDeps: packageJson?.devDependencies ?? {},\n productionDeps: packageJson?.dependencies ?? {},\n isGitRepo: isGitRepo(cwd),\n languages: detectLanguages(cwd),\n namespaceRoot: resolveNamespaceRoot(cwd),\n hookManager: detectHookManagerWorld(cwd, {\n ...packageJson?.dependencies,\n ...packageJson?.devDependencies,\n }),\n };\n}\n","/**\n * Git utilities for CLI operations\n */\n\nimport { execFileSync } from 'node:child_process';\nimport nodePath from 'node:path';\n\nimport { exists } from './fs.js';\n\n/**\n * Check if directory is a git repository\n * @param cwd\n */\nexport function isGitRepo(cwd: string): boolean {\n return exists(nodePath.join(cwd, '.git'));\n}\n\n/** The git worktree root at/above cwd, or undefined outside any repository. */\nexport function gitToplevel(cwd: string): string | undefined {\n try {\n const top = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'pipe'],\n }).trim();\n return top === '' ? undefined : top;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Stop tracking files that are now gitignored but were committed before the\n * ignore rule existed (e.g. safeword's transient hook state). `git rm --cached`\n * removes them from the index only — the on-disk files, which the hooks\n * read/write directly, are left untouched. Behaviour-neutral; it just clears\n * the perpetual-dirty-tree churn that aborts branch switches. No-op outside a\n * git repo or when a pathspec matches nothing (`--ignore-unmatch`).\n * @param cwd project root\n * @param paths gitignore-relative pathspecs to untrack (globs allowed)\n */\nexport function untrackIgnoredFiles(cwd: string, paths: readonly string[]): void {\n if (!isGitRepo(cwd)) return;\n for (const pattern of paths) {\n try {\n execFileSync('git', ['rm', '--cached', '--ignore-unmatch', '--', pattern], {\n cwd,\n stdio: 'pipe',\n });\n } catch {\n // Best-effort: an un-removable path must never fail the upgrade.\n }\n }\n}\n","/**\n * Hook-manager world detection (ZJMZ50, #810 child 2).\n *\n * Decides how the boundary-gate shims reach a host repo: `husky` hosts get\n * marker-block appends into `.husky/*`; every other world gets a printed\n * integration snippet (nudge), never an edited config. The rule is\n * conservative-when-contested — safeword appends to `.husky` only when husky\n * is uncontested by another manager's config, or actively confirmed by\n * `core.hooksPath`. A wrong nudge is noise; a wrong append is dead lines in\n * someone else's hook file.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport nodePath from 'node:path';\n\nimport type { HookManagerWorld } from '../packs/types.js';\nimport { exists } from './fs.js';\n\nexport type { HookManagerWorld } from '../packs/types.js';\n\n/** Lefthook's recognized config filenames — shared with the nudge's quiesce check. */\nexport const LEFTHOOK_CONFIGS = [\n 'lefthook.yml',\n 'lefthook.yaml',\n '.lefthook.yml',\n '.lefthook.yaml',\n];\nconst PRE_COMMIT_CONFIG = '.pre-commit-config.yaml';\n\n/** The repo's configured core.hooksPath, or undefined when unset/unreadable. */\nfunction configuredHooksPath(cwd: string): string | undefined {\n try {\n const value = execFileSync('git', ['config', '--get', 'core.hooksPath'], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'pipe'],\n }).trim();\n return value === '' ? undefined : value;\n } catch {\n return undefined;\n }\n}\n\n/** True when a (relative) hooks path lives inside `.husky` — husky's signature. */\nfunction pointsIntoHusky(hooksPath: string): boolean {\n return hooksPath === '.husky' || hooksPath.startsWith('.husky/');\n}\n\n/** Which hook-manager world governs this host's git hooks. */\nexport function detectHookManagerWorld(\n cwd: string,\n allDependencies: Record<string, string>,\n): HookManagerWorld {\n const hooksPath = configuredHooksPath(cwd);\n const hasLefthook = LEFTHOOK_CONFIGS.some(name => exists(nodePath.join(cwd, name)));\n const hasPreCommit = exists(nodePath.join(cwd, PRE_COMMIT_CONFIG));\n\n // An explicit core.hooksPath is the active-manager signal and wins outright.\n if (hooksPath !== undefined) {\n if (pointsIntoHusky(hooksPath)) return 'husky';\n if (hasLefthook) return 'lefthook';\n if (hasPreCommit) return 'pre-commit';\n return 'bare';\n }\n\n // No active signal (e.g. a fresh clone before `prepare`): another manager's\n // config contests any leftover .husky dir, so nudge instead of appending.\n if (hasLefthook) return 'lefthook';\n if (hasPreCommit) return 'pre-commit';\n if (exists(nodePath.join(cwd, '.husky'))) return 'husky';\n if ('husky' in allDependencies) return 'husky-uninitialized';\n return 'bare';\n}\n","/**\n * Project type detection from package.json\n *\n * Detects frameworks and tools used in the project to configure\n * appropriate linting rules.\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { LANGUAGE_PACKS } from '../packs/registry.js';\nimport type { Languages, ProjectType } from '../packs/types.js';\nimport { detect, SAFEWORD_PRIMARY_LINT_SCRIPT } from '../presets/typescript/detect.js';\nimport { isShippedCucumberTemplateRevision } from './cucumber-template-revisions.js';\nimport { findInTree, readFileSafe, readJson } from './fs.js';\nimport { WORKSPACE_ROOTS } from './workspaces.js';\n\nconst {\n TAILWIND_PACKAGES,\n TANSTACK_QUERY_PACKAGES,\n hasExistingLinter,\n hasExistingFormatter,\n hasExistingPrettierConfig,\n} = detect;\n\n// Python project file markers (used by detectPythonType and config detection)\nconst PYPROJECT_TOML = 'pyproject.toml';\nconst REQUIREMENTS_TXT = 'requirements.txt';\nconst UV_LOCK = 'uv.lock';\n\n// Rust config file markers\nconst CLIPPY_CONFIG_FILES = ['clippy.toml', '.clippy.toml'];\nconst RUSTFMT_CONFIG_FILES = ['rustfmt.toml', '.rustfmt.toml'];\n\n// ESLint config file markers (flat config and legacy)\nconst ESLINT_CONFIG_FILES = [\n 'eslint.config.ts',\n 'eslint.config.mts',\n 'eslint.config.mjs',\n 'eslint.config.js',\n 'eslint.config.cjs',\n '.eslintrc.js',\n '.eslintrc.cjs',\n '.eslintrc.json',\n '.eslintrc.yml',\n '.eslintrc.yaml',\n] as const;\n\n// golangci-lint config file markers\nconst GOLANGCI_CONFIG_FILES = [\n '.golangci.yml',\n '.golangci.yaml',\n '.golangci.toml',\n '.golangci.json',\n];\n\n// Python frameworks to detect (order matters - first match wins)\nconst PYTHON_FRAMEWORKS = ['django', 'flask', 'fastapi'] as const;\n\nexport interface PackageJson {\n name?: string;\n version?: string;\n private?: boolean;\n main?: string;\n module?: string;\n exports?: unknown;\n types?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Python-specific detection (returned only if languages.python)\n * @see ARCHITECTURE.md → Language Detection\n */\nexport interface PythonProjectType {\n framework: 'django' | 'flask' | 'fastapi' | undefined;\n packageManager: 'poetry' | 'uv' | 'pip';\n}\n\n/**\n * Detects which languages are used in the project\n * @param cwd - Working directory to scan\n * @returns Languages object indicating which languages are present\n * @see ARCHITECTURE.md → Language Detection\n */\nexport function detectLanguages(cwd: string): Languages {\n // Delegate to each pack's detect() method — single source of truth.\n // TypeScript pack checks root-only (subdirectory package.json is too common).\n // All other packs use recursive tree scanning via existsInTree().\n const hasLanguage = (key: string): boolean => LANGUAGE_PACKS[key]?.detect(cwd) ?? false;\n return {\n javascript: hasLanguage('typescript'),\n python: hasLanguage('python'),\n golang: hasLanguage('golang'),\n rust: hasLanguage('rust'),\n sql: hasLanguage('sql'),\n };\n}\n\n/**\n * Detects Python project type (framework and package manager)\n * @param cwd - Working directory to scan\n * @returns PythonProjectType or undefined if not a Python project\n * @see ARCHITECTURE.md → Language Detection\n */\nexport function detectPythonType(cwd: string): PythonProjectType | undefined {\n // Search root and immediate subdirectories for Python manifests\n const pyprojectDirectory = findInTree(cwd, PYPROJECT_TOML);\n const requirementsDirectory = findInTree(cwd, REQUIREMENTS_TXT);\n\n // Determine manifest directory — pyproject.toml takes priority\n const manifestDirectory = pyprojectDirectory ?? requirementsDirectory;\n if (!manifestDirectory) {\n return undefined;\n }\n\n // Read project file for dependency/tool detection\n const content = readFileSync(\n pyprojectDirectory\n ? nodePath.join(pyprojectDirectory, PYPROJECT_TOML)\n : nodePath.join(manifestDirectory, REQUIREMENTS_TXT),\n 'utf8',\n );\n\n // Detect package manager (priority: poetry > uv > pip)\n let packageManager: PythonProjectType['packageManager'] = 'pip';\n if (pyprojectDirectory && content.includes('[tool.poetry]')) {\n packageManager = 'poetry';\n } else if (existsSync(nodePath.join(manifestDirectory, UV_LOCK))) {\n packageManager = 'uv';\n }\n\n // Detect framework (first match wins)\n const contentLower = content.toLowerCase();\n const framework = PYTHON_FRAMEWORKS.find(fw => contentLower.includes(fw));\n\n return { framework, packageManager };\n}\n\n/**\n * Checks if a directory contains any .sh files up to specified depth.\n * Excludes dependency, VCS, and agent-configuration directories.\n * @param cwd\n * @param maxDepth\n */\nfunction hasShellScripts(cwd: string, maxDepth = 4): boolean {\n const excludeDirectories = new Set([\n 'node_modules',\n '.git',\n '.agents',\n '.claude',\n '.codex',\n '.cursor',\n '.safeword',\n ]);\n\n function scan(dir: string, depth: number): boolean {\n if (depth > maxDepth) return false;\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isFile() && entry.name.endsWith('.sh')) {\n return true;\n }\n if (\n entry.isDirectory() &&\n !excludeDirectories.has(entry.name) &&\n scan(nodePath.join(dir, entry.name), depth + 1)\n ) {\n return true;\n }\n }\n } catch {\n // Ignore permission errors\n }\n return false;\n }\n\n return scan(cwd, 0);\n}\n\ninterface PackageJsonWithScripts extends PackageJson {\n scripts?: Record<string, string>;\n}\n\n/** Returns the first file from candidates that exists in cwd, or undefined. */\nfunction findFirstExisting(cwd: string, candidates: readonly string[]): string | undefined {\n return candidates.find(file => existsSync(nodePath.join(cwd, file)));\n}\n\n/**\n * Check if project has existing ESLint config.\n * @param cwd - Working directory to scan\n * @returns The config file path if found, undefined otherwise.\n */\nfunction findExistingEslintConfig(cwd: string): string | undefined {\n return findFirstExisting(cwd, ESLINT_CONFIG_FILES);\n}\n\n/**\n * Find existing Ruff config location.\n * @param cwd - Working directory to scan\n * @returns 'ruff.toml' if standalone config exists, 'pyproject.toml' if [tool.ruff] exists, undefined if none\n */\nfunction findExistingRuffConfig(cwd: string): 'ruff.toml' | 'pyproject.toml' | undefined {\n // Check for standalone ruff.toml first (takes precedence)\n if (existsSync(nodePath.join(cwd, 'ruff.toml'))) return 'ruff.toml';\n\n // Check for [tool.ruff] in pyproject.toml\n return fileContainsSection(nodePath.join(cwd, PYPROJECT_TOML), '[tool.ruff]')\n ? 'pyproject.toml'\n : undefined;\n}\n\n/**\n * Check if project has existing mypy config.\n * @param cwd - Working directory to scan\n * @returns True if mypy.ini/.mypy.ini exists OR [tool.mypy] in pyproject.toml\n */\nfunction hasExistingMypyConfig(cwd: string): boolean {\n // Check for standalone mypy config files\n if (existsSync(nodePath.join(cwd, 'mypy.ini'))) return true;\n if (existsSync(nodePath.join(cwd, '.mypy.ini'))) return true;\n\n // Check for [tool.mypy] in pyproject.toml\n return fileContainsSection(nodePath.join(cwd, PYPROJECT_TOML), '[tool.mypy]');\n}\n\n/**\n * Check if project has existing import-linter config.\n * @param cwd - Working directory to scan\n * @returns True if .importlinter exists OR [tool.importlinter] in pyproject.toml\n */\nfunction hasExistingImportLinterConfig(cwd: string): boolean {\n // Check for standalone .importlinter file\n if (existsSync(nodePath.join(cwd, '.importlinter'))) return true;\n\n // Check for [importlinter] in setup.cfg (import-linter's third auto-detected form)\n if (fileContainsSection(nodePath.join(cwd, 'setup.cfg'), '[importlinter]')) return true;\n\n // Check for [tool.importlinter] in pyproject.toml\n return fileContainsSection(nodePath.join(cwd, PYPROJECT_TOML), '[tool.importlinter]');\n}\n\n/** True when `filePath` exists and contains `section`; false on absence or read error. */\nfunction fileContainsSection(filePath: string, section: string): boolean {\n if (!existsSync(filePath)) return false;\n try {\n return readFileSync(filePath, 'utf8').includes(section);\n } catch {\n return false;\n }\n}\n\n// SQLFluff config file markers\nconst SQLFLUFF_CONFIG_FILES = ['.sqlfluff', 'setup.cfg'];\n\n/**\n * Detect JavaScript framework dependencies from package.json.\n */\nfunction detectFrameworks(\n dependencies: Record<string, string>,\n developmentDependencies: Record<string, string>,\n allDependencies: Record<string, string>,\n): Pick<\n ProjectType,\n | 'typescript'\n | 'react'\n | 'nextjs'\n | 'astro'\n | 'vitest'\n | 'playwright'\n | 'tailwind'\n | 'tanstackQuery'\n> {\n const hasNextJs = 'next' in dependencies;\n return {\n typescript: 'typescript' in allDependencies,\n react: 'react' in dependencies || 'react' in developmentDependencies || hasNextJs,\n nextjs: hasNextJs,\n astro: 'astro' in dependencies || 'astro' in developmentDependencies,\n vitest: 'vitest' in developmentDependencies,\n playwright: '@playwright/test' in developmentDependencies,\n tailwind: TAILWIND_PACKAGES.some(pkg => Object.hasOwn(allDependencies, pkg)),\n tanstackQuery: TANSTACK_QUERY_PACKAGES.some(pkg => Object.hasOwn(allDependencies, pkg)),\n };\n}\n\n/**\n * Detect if package is publishable (has entry points and not private).\n */\nfunction detectPublishable(packageJson: PackageJsonWithScripts): boolean {\n const hasEntryPoints = !!(packageJson.main || packageJson.module || packageJson.exports);\n return hasEntryPoints && packageJson.private !== true;\n}\n\nfunction hasSafewordPrimaryLinter(\n cwd: string | undefined,\n scripts: Record<string, string>,\n): boolean {\n return (\n cwd !== undefined &&\n existsSync(nodePath.join(cwd, '.safeword')) &&\n scripts.lint === SAFEWORD_PRIMARY_LINT_SCRIPT\n );\n}\n\n/**\n * Detect existing JS/TS/Python tooling configuration.\n */\nfunction detectCoreTooling(\n cwd: string | undefined,\n scripts: Record<string, string>,\n): Pick<\n ProjectType,\n | 'existingLinter'\n | 'existingFormatter'\n | 'existingPrettierConfig'\n | 'existingEslintConfig'\n | 'legacyEslint'\n | 'existingRuffConfig'\n | 'existingMypyConfig'\n | 'existingImportLinterConfig'\n> {\n const eslintConfig = cwd ? findExistingEslintConfig(cwd) : undefined;\n return {\n // Do not let the lint script generated by setup activate a conditional\n // schema branch on the next run. The installed marker distinguishes it\n // from an identical customer-owned script in an unconfigured project.\n existingLinter: hasExistingLinter(scripts) && !hasSafewordPrimaryLinter(cwd, scripts),\n existingFormatter: cwd ? hasExistingFormatter(cwd, scripts) : 'format' in scripts,\n existingPrettierConfig: cwd ? hasExistingPrettierConfig(cwd) : false,\n existingEslintConfig: eslintConfig,\n legacyEslint: eslintConfig?.startsWith('.eslintrc') ?? false,\n existingRuffConfig: cwd ? findExistingRuffConfig(cwd) : undefined,\n existingMypyConfig: cwd ? hasExistingMypyConfig(cwd) : false,\n existingImportLinterConfig: cwd ? hasExistingImportLinterConfig(cwd) : false,\n };\n}\n\n/**\n * Detect existing Go/Rust tooling configuration.\n */\nfunction detectSystemsTooling(\n cwd: string | undefined,\n): Pick<\n ProjectType,\n | 'existingGolangciConfig'\n | 'existingClippyConfig'\n | 'existingRustfmtConfig'\n | 'existingSqlfluffConfig'\n> {\n return {\n existingGolangciConfig: cwd ? findFirstExisting(cwd, GOLANGCI_CONFIG_FILES) : undefined,\n existingClippyConfig: cwd ? findFirstExisting(cwd, CLIPPY_CONFIG_FILES) : undefined,\n existingRustfmtConfig: cwd ? findFirstExisting(cwd, RUSTFMT_CONFIG_FILES) : undefined,\n existingSqlfluffConfig: cwd ? findFirstExisting(cwd, SQLFLUFF_CONFIG_FILES) : undefined,\n };\n}\n\n/**\n * Detect existing tooling configuration from file system.\n */\nfunction detectExistingTooling(\n cwd: string | undefined,\n scripts: Record<string, string>,\n): Pick<\n ProjectType,\n | 'existingLinter'\n | 'existingFormatter'\n | 'existingPrettierConfig'\n | 'existingEslintConfig'\n | 'legacyEslint'\n | 'existingRuffConfig'\n | 'existingMypyConfig'\n | 'existingImportLinterConfig'\n | 'existingGolangciConfig'\n | 'existingClippyConfig'\n | 'existingRustfmtConfig'\n | 'existingSqlfluffConfig'\n> {\n return {\n ...detectCoreTooling(cwd, scripts),\n ...detectSystemsTooling(cwd),\n };\n}\n\n/**\n * Detects project type from package.json contents and optional file scanning\n * @param packageJson - Package.json contents including scripts\n * @param cwd - Working directory for file-based detection\n */\n// JS/TS application-source detection — distinguishes a real JS project from a\n// pure Python/Go/Rust repo that only carries safeword's TS BDD lane (ticket 102b).\n// Gates JS-app-only tooling (knip, dependency-cruiser) so non-JS repos don't get it.\nconst JS_SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.mts',\n '.cts',\n]);\nconst JS_SOURCE_DIR_EXCLUDE = new Set([\n 'node_modules',\n '.git',\n '.safeword',\n 'dist',\n 'build',\n 'target',\n 'coverage',\n 'vendor',\n]);\n// Root-level lane scaffolding + safeword-owned files are NOT \"real\" JS source.\nconst LANE_DIRS = new Set(['steps', 'features']);\nconst LANE_FILES = new Set(['cucumber.mjs', 'eslint.config.mjs', '.prettierrc']);\n\n/** A directory to skip while scanning for JS source (excludes + the root-level lane). */\nfunction isExcludedSourceDirectory(name: string, depth: number): boolean {\n if (name.startsWith('.') || JS_SOURCE_DIR_EXCLUDE.has(name)) return true;\n return depth === 0 && LANE_DIRS.has(name); // root steps/ features/ are the BDD lane\n}\n\n/** A file that counts as real JS/TS application source (not root-level lane scaffolding). */\nfunction isJsSourceFile(name: string, depth: number): boolean {\n if (depth === 0 && LANE_FILES.has(name)) return false;\n return JS_SOURCE_EXTENSIONS.has(nodePath.extname(name));\n}\n\nfunction scanForJsSource(directory: string, depth: number, maxDepth: number): boolean {\n let entries;\n try {\n entries = readdirSync(directory, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (entry.isFile()) {\n if (isJsSourceFile(entry.name, depth)) return true;\n continue;\n }\n if (!entry.isDirectory() || depth >= maxDepth || isExcludedSourceDirectory(entry.name, depth))\n continue;\n if (scanForJsSource(nodePath.join(directory, entry.name), depth + 1, maxDepth)) return true;\n }\n return false;\n}\n\n/**\n * True if the repo has real JS/TS application source — not just safeword's TS BDD\n * lane scaffolding. Stable across `setup` and `upgrade` (it reads source, not the\n * stub `package.json`), so it can gate JS-app-only tooling off non-JS projects.\n */\nexport function hasJsSource(cwd: string, maxDepth = 6): boolean {\n return scanForJsSource(cwd, 0, maxDepth);\n}\n\n// The cucumber-js config filename safeword scaffolds its own lane as. Load-bearing\n// for own-scaffold identity: its membership in the discovery list (below), the\n// self-exclusion in detectCucumberHarnessEvidence, and the own-lane probe in\n// detectCucumberLane must all name the same file so a scaffold rename stays in lockstep.\nconst SAFEWORD_LANE_CONFIG_FILE = 'cucumber.mjs';\n\n// Cucumber-js native config discovery order (first wins). A root config with\n// any of these names is a harness safeword must not compete with — except\n// safeword's own scaffolded cucumber.mjs, excluded by content match below.\nconst CUCUMBER_CONFIG_FILES = [\n 'cucumber.json',\n 'cucumber.yaml',\n 'cucumber.yml',\n 'cucumber.js',\n 'cucumber.cjs',\n SAFEWORD_LANE_CONFIG_FILE,\n] as const;\n\n/**\n * True when the file is safeword's own lane scaffold: a hash-match against a\n * shipped template revision (so upgrades from older safewords never read\n * their own lane as a host harness). The current template is always in the\n * registry — the cucumber-template-revisions contract test enforces it — so\n * no separate byte-compare against the bundled template is needed.\n */\nfunction isSafewordLaneTemplate(configPath: string): boolean {\n const content = readFileSafe(configPath);\n return content !== undefined && isShippedCucumberTemplateRevision(content);\n}\n\nfunction manifestDependsOnCucumber(manifestPath: string): boolean {\n const manifest = readJson(manifestPath) as\n { dependencies?: Record<string, string>; devDependencies?: Record<string, string> } | undefined;\n return Boolean(\n manifest?.dependencies?.['@cucumber/cucumber'] ??\n manifest?.devDependencies?.['@cucumber/cucumber'],\n );\n}\n\n/**\n * Cucumber evidence inside one workspace package: a config file of any name\n * (no self-exclusion — safeword only ever scaffolds the lane at the root) or\n * a @cucumber/cucumber dependency.\n */\nfunction findCucumberEvidenceInPackage(cwd: string, packagePath: string): string | undefined {\n for (const name of CUCUMBER_CONFIG_FILES) {\n if (existsSync(nodePath.join(cwd, packagePath, name))) {\n return `${packagePath}/${name}`;\n }\n }\n if (manifestDependsOnCucumber(nodePath.join(cwd, packagePath, 'package.json'))) {\n return `${packagePath}/package.json (@cucumber/cucumber)`;\n }\n return undefined;\n}\n\n/** First cucumber config file or dep under `<cwd>/<root>/*`, as evidence. */\nfunction findCucumberEvidenceUnderRoot(cwd: string, root: string): string | undefined {\n let entries;\n try {\n entries = readdirSync(nodePath.join(cwd, root), { withFileTypes: true });\n } catch {\n return undefined;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const evidence = findCucumberEvidenceInPackage(cwd, `${root}/${entry.name}`);\n if (evidence !== undefined) return evidence;\n }\n return undefined;\n}\n\n/** First direct workspace package with cucumber evidence (config file or dep). */\nfunction detectWorkspaceCucumberDependency(cwd: string): string | undefined {\n for (const root of WORKSPACE_ROOTS) {\n const evidence = findCucumberEvidenceUnderRoot(cwd, root);\n if (evidence !== undefined) return evidence;\n }\n return undefined;\n}\n\nexport interface CucumberLaneDetection {\n existingCucumberHarness: string | undefined;\n scaffoldBddLane: boolean;\n}\n\n/** Host-harness evidence: first foreign config file, else workspace dep, else root dep. */\nfunction detectCucumberHarnessEvidence(cwd: string, ownLanePresent: boolean): string | undefined {\n for (const name of CUCUMBER_CONFIG_FILES) {\n const configPath = nodePath.join(cwd, name);\n if (!existsSync(configPath)) continue;\n if (name === SAFEWORD_LANE_CONFIG_FILE && ownLanePresent) continue;\n return name;\n }\n\n const workspaceEvidence = detectWorkspaceCucumberDependency(cwd);\n if (workspaceEvidence !== undefined) return workspaceEvidence;\n\n // A root dep with safeword's scaffolded lane present is safeword's own\n // install, not host evidence — otherwise every existing install would\n // self-trigger detection at upgrade (the 56JCFZ self-trigger trap).\n if (!ownLanePresent && manifestDependsOnCucumber(nodePath.join(cwd, 'package.json'))) {\n return 'package.json (@cucumber/cucumber)';\n }\n return undefined;\n}\n\n/**\n * Detect a cucumber harness safeword did not scaffold, and whether the\n * starter lane is safeword's to scaffold/maintain (ticket 56JCFZ).\n * Evidence and suppression are deliberately separate: a bitten repo (own\n * lane + host harness) keeps lane maintenance while advisories surface the\n * duplicate; a fresh repo with a host harness gets no lane at all.\n */\nexport function detectCucumberLane(cwd: string | undefined): CucumberLaneDetection {\n if (!cwd) return { existingCucumberHarness: undefined, scaffoldBddLane: true };\n\n const ownLanePresent = isSafewordLaneTemplate(nodePath.join(cwd, SAFEWORD_LANE_CONFIG_FILE));\n const evidence = detectCucumberHarnessEvidence(cwd, ownLanePresent);\n return {\n existingCucumberHarness: evidence,\n scaffoldBddLane: ownLanePresent || evidence === undefined,\n };\n}\n\nexport function detectProjectType(packageJson: PackageJsonWithScripts, cwd?: string): ProjectType {\n const dependencies = packageJson.dependencies ?? {};\n const developmentDependencies = packageJson.devDependencies ?? {};\n const allDependencies = { ...dependencies, ...developmentDependencies };\n const scripts = packageJson.scripts ?? {};\n\n return {\n ...detectFrameworks(dependencies, developmentDependencies, allDependencies),\n publishableLibrary: detectPublishable(packageJson),\n shell: cwd ? hasShellScripts(cwd) : false,\n hasJsSource: cwd ? hasJsSource(cwd) : false,\n ...detectCucumberLane(cwd),\n ...detectExistingTooling(cwd, scripts),\n };\n}\n\nexport { type Languages, type ProjectType } from '../packs/types.js';\n","/**\n * SHA-256 hashes of every shipped revision of\n * `templates/cucumber/cucumber.mjs` (ticket 56JCFZ). Harness detection\n * treats a root `cucumber.mjs` matching ANY of these as safeword's own\n * scaffold — an install from an older safeword must never be mistaken for a\n * host cucumber harness at upgrade.\n *\n * MAINTENANCE: when the template changes, append the hash of the NEW content\n * (`sha256sum packages/cli/templates/cucumber/cucumber.mjs`). A contract\n * test (cucumber-template-revisions.test.ts) fails when the current\n * template's hash is missing, so this list cannot silently rot.\n *\n * History source: `git log --follow -- packages/cli/templates/cucumber/cucumber.mjs`,\n * one entry per distinct content (oldest first).\n */\n\nimport { createHash } from 'node:crypto';\n\nconst CUCUMBER_TEMPLATE_REVISION_HASHES: ReadonlySet<string> = new Set([\n // f11d63b2 — 102a initial lane config (root features/ only)\n '1a9a1bd23c1f7b249800a631e4f3d59abc24678b9bcb1a21891a95378dbadc58',\n // a874ea92 — VM78NC workspace feature paths\n '761b20d508207b58e234fdcf592ae4cd0e916548ef46177469e2165c1e13832b',\n // 4b3aefd4 — CLI feature-path passthrough\n '3d22842f73c62e497a525dbe0a126b42bf2202fb8369ede29ba45b6d1e493fb6',\n // 56JCFZ — runtime paths.features/paths.steps config read (current)\n '929014fb4ce6ca1483b292757b6044be89932028f5bb21ba82c28ffd66b09e53',\n // 4DK9H4 — live profile excludes @manual while selecting @live scenarios\n '1295e832b1b3bdd0faa963e218bd489c9c252a981fc8e315d4814d4208384a5f',\n // ECGEP9 — automated lane distinguishes Vitest-backed proof from unfinished work\n '0ec4cb6d65b39e01f8aff27c4ca3841e0271a43b5435492676450f0fb3cfac27',\n]);\n\n/** True when `content` is a shipped revision of safeword's lane template. */\nexport function isShippedCucumberTemplateRevision(content: string): boolean {\n const hash = createHash('sha256').update(content).digest('hex');\n return CUCUMBER_TEMPLATE_REVISION_HASHES.has(hash);\n}\n"],"mappings":"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","names":["isGitRepo","lstatSync","readdirSync","nodePath","nodePath","lstatSync","nodePath","readdirSync","nodePath","nodePath","existsSync","readdirSync","nodePath","existsSync","readdirSync","nodePath","nodePath","nodePath","nodePath","nodePath","nodePath","execFileSync","nodePath","execFileSync","nodePath","existsSync","readdirSync","readFileSync","nodePath","detectLanguages","readdirSync","nodePath","existsSync","readFileSync","nodePath","detectLanguages"]}
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  TICKETS_RELATIVE_PATH,
3
3
  readTickets
4
- } from "./chunk-KFVEPQ7X.js";
4
+ } from "./chunk-MVFHTUQ3.js";
5
5
  import {
6
6
  resolveTicketsDirectory
7
- } from "./chunk-GKR44BCH.js";
7
+ } from "./chunk-RVDXBHUG.js";
8
8
 
9
9
  // src/tracker-sync/corpus.ts
10
10
  import { existsSync, readFileSync } from "fs";
@@ -59,4 +59,4 @@ export {
59
59
  toTicketInput,
60
60
  readCorpus
61
61
  };
62
- //# sourceMappingURL=chunk-4CZS4YJV.js.map
62
+ //# sourceMappingURL=chunk-3OKZ3X4O.js.map
@@ -2,10 +2,10 @@ import {
2
2
  assertSafeClaudeCleanupTarget,
3
3
  containedClaudeCleanupPath,
4
4
  observeClaudeLegacy
5
- } from "./chunk-4CKCRZDO.js";
5
+ } from "./chunk-7ULZND56.js";
6
6
  import {
7
7
  canonicalClaudeProjectRoot
8
- } from "./chunk-34NCOTP5.js";
8
+ } from "./chunk-7XELS325.js";
9
9
  import {
10
10
  CLAUDE_MIGRATION_SCHEMA,
11
11
  advisoryStateDigest,
@@ -14,19 +14,19 @@ import {
14
14
  readClaudePluginMode,
15
15
  writeClaudeMigrationAttention,
16
16
  writeClaudePluginMode
17
- } from "./chunk-FDHJ63FZ.js";
18
- import {
19
- cataloguedClaudeLegacyPaths,
20
- isAcceptedHistoricalFile,
21
- isAcceptedHistoricalHook
22
- } from "./chunk-NSZGI7HK.js";
17
+ } from "./chunk-WVA4Q6BX.js";
23
18
  import {
24
19
  writeDurableFile,
25
20
  writeDurableFileExclusive
26
21
  } from "./chunk-QOLHGAD5.js";
22
+ import {
23
+ cataloguedClaudeLegacyPaths,
24
+ isAcceptedHistoricalFile,
25
+ isAcceptedHistoricalHook
26
+ } from "./chunk-HNCUIXDY.js";
27
27
  import {
28
28
  createResult
29
- } from "./chunk-V63TSN3C.js";
29
+ } from "./chunk-FOYGZ4MK.js";
30
30
 
31
31
  // src/claude-plugin/cleanup.ts
32
32
  import { createHash, randomUUID } from "crypto";
@@ -833,4 +833,4 @@ export {
833
833
  migrateClaudeLegacyAutomatically,
834
834
  recoverClaudeCleanup
835
835
  };
836
- //# sourceMappingURL=chunk-GZ36UZJG.js.map
836
+ //# sourceMappingURL=chunk-73TABQRT.js.map
@@ -2,7 +2,7 @@ import {
2
2
  cataloguedClaudeLegacyPaths,
3
3
  isAcceptedHistoricalFile,
4
4
  isAcceptedHistoricalHook
5
- } from "./chunk-NSZGI7HK.js";
5
+ } from "./chunk-HNCUIXDY.js";
6
6
 
7
7
  // src/claude-plugin/cleanup-target.ts
8
8
  import { existsSync, lstatSync } from "fs";
@@ -119,4 +119,4 @@ export {
119
119
  observeClaudeLegacy,
120
120
  legacyObservationIsEmpty
121
121
  };
122
- //# sourceMappingURL=chunk-4CKCRZDO.js.map
122
+ //# sourceMappingURL=chunk-7ULZND56.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  canonicalDirectory
3
- } from "./chunk-S2T356DA.js";
3
+ } from "./chunk-HF3DXZEA.js";
4
4
 
5
5
  // src/claude-plugin/project-root.ts
6
6
  import { spawnSync } from "child_process";
@@ -27,4 +27,4 @@ function canonicalClaudeProjectRoot(cwd) {
27
27
  export {
28
28
  canonicalClaudeProjectRoot
29
29
  };
30
- //# sourceMappingURL=chunk-34NCOTP5.js.map
30
+ //# sourceMappingURL=chunk-7XELS325.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  WORKSPACE_ROOTS
3
- } from "./chunk-Q5KLE6ZF.js";
3
+ } from "./chunk-OKPRQGFG.js";
4
4
  import {
5
5
  resolveConfiguredLaneDirectory,
6
6
  resolveTicketsDirectory,
7
7
  toRepoDirectory,
8
8
  toRepoPath
9
- } from "./chunk-GKR44BCH.js";
9
+ } from "./chunk-RVDXBHUG.js";
10
10
  import {
11
11
  readFileSafe
12
- } from "./chunk-S2T356DA.js";
12
+ } from "./chunk-HF3DXZEA.js";
13
13
 
14
14
  // src/utils/feature-source.ts
15
15
  import { existsSync, readdirSync } from "fs";
@@ -39,9 +39,12 @@ function slugForTicket(cwd, ticketFolder) {
39
39
  const dashIndex = ticketFolder.indexOf("-");
40
40
  return dashIndex === -1 ? ticketFolder : ticketFolder.slice(dashIndex + 1);
41
41
  }
42
- function findFeatureSourcePath(cwd, ticketFolder) {
43
- const fileName = `${slugForTicket(cwd, ticketFolder)}.feature`;
44
- return collectExecutableFeatureFiles(cwd, fileName)[0];
42
+ function featureSourceFileName(cwd, ticketFolder) {
43
+ return `${slugForTicket(cwd, ticketFolder)}.feature`;
44
+ }
45
+ function findFeatureSourcePath(cwd, ticketFolder, featureFiles = collectExecutableFeatureFiles(cwd)) {
46
+ const fileName = featureSourceFileName(cwd, ticketFolder);
47
+ return featureFiles.find((path) => nodePath.basename(path) === fileName);
45
48
  }
46
49
  function createPhaseAnchorEnvironment(cwd, configuredFeatures) {
47
50
  const featureRoots = ["features"];
@@ -124,6 +127,7 @@ function isMatchingFeatureFile(entryName, fileName) {
124
127
 
125
128
  export {
126
129
  readFrontmatterScalar,
130
+ featureSourceFileName,
127
131
  findFeatureSourcePath,
128
132
  createPhaseAnchorEnvironment,
129
133
  createPhaseAnchorScope,
@@ -131,4 +135,4 @@ export {
131
135
  hasDefaultExecutableFeatureFiles,
132
136
  collectExecutableFeatureDirectories
133
137
  };
134
- //# sourceMappingURL=chunk-USPVNZ63.js.map
138
+ //# sourceMappingURL=chunk-BZWO3BVD.js.map
@@ -0,0 +1 @@
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 featureSourceFileName(cwd: string, ticketFolder: string): string {\n return `${slugForTicket(cwd, ticketFolder)}.feature`;\n}\n\nexport function findFeatureSourcePath(\n cwd: string,\n ticketFolder: string,\n featureFiles: readonly string[] = collectExecutableFeatureFiles(cwd),\n): string | undefined {\n const fileName = featureSourceFileName(cwd, ticketFolder);\n return featureFiles.find(path => nodePath.basename(path) === fileName);\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,6 +1,6 @@
1
1
  import {
2
2
  createPlan
3
- } from "./chunk-ZRV7TOCX.js";
3
+ } from "./chunk-G5AFQXXW.js";
4
4
 
5
5
  // src/codex-plugin/legacy-global-guidance.ts
6
6
  import { createHash } from "crypto";
@@ -248,4 +248,4 @@ export {
248
248
  planLegacyGlobalGuidanceCleanup,
249
249
  applyLegacyGlobalGuidanceCleanup
250
250
  };
251
- //# sourceMappingURL=chunk-BQTC6YGN.js.map
251
+ //# sourceMappingURL=chunk-D5ENZR6N.js.map