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
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  WORKSPACE_ROOTS,
3
3
  getWorkspacePackageNames
4
- } from "./chunk-Q5KLE6ZF.js";
4
+ } from "./chunk-OKPRQGFG.js";
5
5
  import {
6
6
  parse,
7
7
  setupPythonTooling
8
- } from "./chunk-BSOUMHMQ.js";
8
+ } from "./chunk-JSR3HXFT.js";
9
9
  import {
10
10
  SAFEWORD_PRIMARY_LINT_SCRIPT,
11
11
  detect
@@ -14,7 +14,7 @@ import {
14
14
  NAMESPACE_ROOT_LEGACY,
15
15
  readConfiguredPath,
16
16
  resolveNamespaceRoot
17
- } from "./chunk-GKR44BCH.js";
17
+ } from "./chunk-RVDXBHUG.js";
18
18
  import {
19
19
  ensureDirectory,
20
20
  exists,
@@ -28,10 +28,10 @@ import {
28
28
  removeIfEmpty,
29
29
  writeFile,
30
30
  writeJson
31
- } from "./chunk-S2T356DA.js";
31
+ } from "./chunk-HF3DXZEA.js";
32
32
 
33
33
  // src/reconcile.ts
34
- import { lstatSync, readlinkSync, unlinkSync } from "fs";
34
+ import { lstatSync, readdirSync, readlinkSync, unlinkSync } from "fs";
35
35
  import nodePath from "path";
36
36
  var HUSKY_DIR = ".husky";
37
37
  var CHMOD_PATHS = [".safeword/hooks", ".safeword/hooks/cursor", ".safeword/scripts"];
@@ -108,10 +108,7 @@ function passesTextPatchContentGuard(cwd, filePath, definition) {
108
108
  return content !== void 0 && definition.applyWhenContentIncludes.every((required) => content.includes(required));
109
109
  }
110
110
  function planTextUnpatches(patches, ctx) {
111
- const hasLegacyPreamble = (content, definition) => {
112
- const firstLine = content.split("\n", 1)[0] ?? "";
113
- return containsTextPatchContent(content, definition) || firstLine.includes(definition.marker);
114
- };
111
+ const hasLegacyPreamble = (content, definition) => containsTextPatchContent(content, definition);
115
112
  const actions = [];
116
113
  for (const [filePath, entry] of Object.entries(patches)) {
117
114
  if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;
@@ -161,15 +158,19 @@ function planManagedFileWrites(files, ctx) {
161
158
  return exists(nodePath.join(c.cwd, filePath));
162
159
  });
163
160
  }
164
- function planExistingDirectoriesRemoval(directories, cwd) {
161
+ function planExistingDirectoriesRemoval(directories, cwd, scheduledRemovals = /* @__PURE__ */ new Set()) {
165
162
  const actions = [];
166
163
  const removed = [];
167
164
  for (const dir of directories) {
168
- if (!exists(nodePath.join(cwd, dir))) {
169
- continue;
170
- }
165
+ const fullPath = nodePath.join(cwd, dir);
166
+ if (!exists(fullPath)) continue;
171
167
  actions.push({ type: "rmdir", path: dir });
172
- removed.push(dir);
168
+ const plannedBeforeThisDirectory = /* @__PURE__ */ new Set([...scheduledRemovals, ...removed]);
169
+ const entries = readdirSync(fullPath);
170
+ const willBeEmpty = entries.every(
171
+ (entry) => plannedBeforeThisDirectory.has(nodePath.posix.join(dir, entry))
172
+ );
173
+ if (willBeEmpty) removed.push(dir);
173
174
  }
174
175
  return { actions, removed };
175
176
  }
@@ -179,16 +180,24 @@ function matchesUnmodifiedScaffold(definition, ctx, installed) {
179
180
  const normalize = definition.normalizeForUnmodifiedComparison;
180
181
  return normalize === void 0 ? installed === expected : normalize(installed) === normalize(expected);
181
182
  }
182
- function planConditionalManagedRemoval(managedFiles, ctx) {
183
+ function matchesManagedScaffold(definition, ctx, installed) {
184
+ if (installed === void 0) return false;
185
+ if (definition.removeIfUnmodified !== void 0) {
186
+ return matchesUnmodifiedScaffold(definition, ctx, installed);
187
+ }
188
+ const expected = resolveFileContent(definition, ctx);
189
+ return expected !== void 0 && installed === expected;
190
+ }
191
+ function planConditionalManagedRemoval(managedFiles, ctx, includeAllUnmodified = false) {
183
192
  const actions = [];
184
193
  const removed = [];
185
194
  for (const [filePath, definition] of Object.entries(managedFiles)) {
186
- if (definition.removeIfUnmodified === void 0) continue;
195
+ if (!includeAllUnmodified && definition.removeIfUnmodified === void 0) continue;
187
196
  if (isConfigOverridden(definition, ctx.cwd)) continue;
188
197
  const fullPath = nodePath.join(ctx.cwd, filePath);
189
198
  if (!exists(fullPath)) continue;
190
199
  const installed = readFileSafe(fullPath);
191
- if (installed !== void 0 && matchesUnmodifiedScaffold(definition, ctx, installed)) {
200
+ if (matchesManagedScaffold(definition, ctx, installed)) {
192
201
  actions.push({ type: "rm", path: filePath });
193
202
  removed.push(filePath);
194
203
  }
@@ -249,7 +258,8 @@ function getClaudeParentDirectoryForCleanup(filePath) {
249
258
  function resolveTextPatch(definition, ctx) {
250
259
  return {
251
260
  ...definition,
252
- content: typeof definition.content === "function" ? definition.content(ctx) : definition.content
261
+ content: typeof definition.content === "function" ? definition.content(ctx) : definition.content,
262
+ replacementAfterUnpatch: typeof definition.replacementAfterUnpatch === "function" ? definition.replacementAfterUnpatch(ctx) : definition.replacementAfterUnpatch
253
263
  };
254
264
  }
255
265
  function withResolvedNamespaceRoot(schema, ctx) {
@@ -265,6 +275,7 @@ function withResolvedNamespaceRoot(schema, ctx) {
265
275
  };
266
276
  return {
267
277
  ...schema,
278
+ sharedDirs: schema.sharedDirs.map((path) => translate(path)),
268
279
  preservedDirs: schema.preservedDirs.map((path) => translate(path)),
269
280
  managedFiles: Object.fromEntries(
270
281
  Object.entries(schema.managedFiles).map(([path, definition]) => [translate(path), definition]).filter(([translatedPath]) => translatedPath !== ".gitignore")
@@ -421,7 +432,11 @@ function computeUpgradePlan(schema, ctx) {
421
432
  const deprecatedFiles = planExistingFilesRemoval(schema.deprecatedFiles, ctx.cwd);
422
433
  actions.push(...deprecatedFiles.actions);
423
434
  wouldRemove.push(...deprecatedFiles.removed);
424
- const deprecatedDirectories = planExistingDirectoriesRemoval(schema.deprecatedDirs, ctx.cwd);
435
+ const deprecatedDirectories = planExistingDirectoriesRemoval(
436
+ schema.deprecatedDirs,
437
+ ctx.cwd,
438
+ new Set(wouldRemove)
439
+ );
425
440
  actions.push(...deprecatedDirectories.actions);
426
441
  wouldRemove.push(...deprecatedDirectories.removed);
427
442
  actions.push({ type: "chmod", paths: CHMOD_PATHS });
@@ -468,7 +483,11 @@ function computeUninstallPlan(schema, ctx, full) {
468
483
  const parentDirectory = getClaudeParentDirectoryForCleanup(filePath);
469
484
  if (parentDirectory) directoriesToCleanup.add(parentDirectory);
470
485
  }
471
- const cleanupDirectories = planExistingDirectoriesRemoval([...directoriesToCleanup], ctx.cwd);
486
+ const cleanupDirectories = planExistingDirectoriesRemoval(
487
+ [...directoriesToCleanup],
488
+ ctx.cwd,
489
+ new Set(wouldRemove)
490
+ );
472
491
  actions.push(...cleanupDirectories.actions);
473
492
  wouldRemove.push(...cleanupDirectories.removed);
474
493
  for (const [filePath, definition] of Object.entries(schema.jsonMerges)) {
@@ -480,15 +499,22 @@ function computeUninstallPlan(schema, ctx, full) {
480
499
  );
481
500
  const dynamicChanges = planDynamicFileChanges(actions, ctx);
482
501
  wouldRemove.push(...dynamicChanges.removed);
483
- const preserved = planExistingDirectoriesRemoval(schema.preservedDirs.toReversed(), ctx.cwd);
502
+ const preserved = planExistingDirectoriesRemoval(
503
+ schema.preservedDirs.toReversed(),
504
+ ctx.cwd,
505
+ new Set(wouldRemove)
506
+ );
484
507
  actions.push(...preserved.actions);
485
508
  wouldRemove.push(...preserved.removed);
486
- const owned = planExistingDirectoriesRemoval(schema.ownedDirs.toReversed(), ctx.cwd);
509
+ const owned = planExistingDirectoriesRemoval(
510
+ schema.ownedDirs.toReversed(),
511
+ ctx.cwd,
512
+ new Set(wouldRemove)
513
+ );
487
514
  actions.push(...owned.actions);
488
515
  wouldRemove.push(...owned.removed);
489
516
  if (full) {
490
- const removable = Object.entries(schema.managedFiles).filter(([, definition]) => definition.configKey === void 0).map(([filePath]) => filePath);
491
- const managed = planExistingFilesRemoval(removable, ctx.cwd);
517
+ const managed = planConditionalManagedRemoval(schema.managedFiles, ctx, true);
492
518
  actions.push(...managed.actions);
493
519
  wouldRemove.push(...managed.removed);
494
520
  } else {
@@ -496,7 +522,12 @@ function computeUninstallPlan(schema, ctx, full) {
496
522
  actions.push(...conditional.actions);
497
523
  wouldRemove.push(...conditional.removed);
498
524
  }
499
- const packagesToRemove = full ? computePackagesToRemove(schema, ctx.projectType, ctx.developmentDeps) : [];
525
+ let packagesToRemove = [];
526
+ if (full) {
527
+ packagesToRemove = computePackagesToRemove(schema, ctx.projectType, ctx.developmentDeps);
528
+ } else if (Object.hasOwn(ctx.developmentDeps, "safeword")) {
529
+ packagesToRemove = ["safeword"];
530
+ }
500
531
  return {
501
532
  actions,
502
533
  wouldCreate: dynamicChanges.created,
@@ -791,6 +822,9 @@ function executeJsonUnmerge(cwd, path, definition, ctx) {
791
822
  function rerenderBlockLines(definition) {
792
823
  return definition.content.split("\n").filter((line) => line !== "" && !line.includes(definition.marker));
793
824
  }
825
+ function isOwnedRerenderLine(line, expectedLine, definition) {
826
+ return line === expectedLine || expectedLine !== void 0 && definition.rerenderOwnedLinePattern?.test(line) === true;
827
+ }
794
828
  function stripRerenderBlock(content, definition) {
795
829
  const lines = content.split("\n");
796
830
  const startIndex = lines.findIndex((line) => line.includes(definition.marker));
@@ -798,7 +832,10 @@ function stripRerenderBlock(content, definition) {
798
832
  const blockLines = rerenderBlockLines(definition);
799
833
  let endIndex = startIndex;
800
834
  let expected = 0;
801
- while (endIndex + 1 < lines.length && expected < blockLines.length && lines[endIndex + 1] === blockLines[expected]) {
835
+ while (endIndex + 1 < lines.length) {
836
+ const nextLine = lines[endIndex + 1] ?? "";
837
+ const expectedLine = blockLines[expected];
838
+ if (!isOwnedRerenderLine(nextLine, expectedLine, definition)) break;
802
839
  endIndex += 1;
803
840
  expected += 1;
804
841
  }
@@ -806,6 +843,15 @@ function stripRerenderBlock(content, definition) {
806
843
  lines.splice(blockStart, endIndex - blockStart + 1);
807
844
  return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n");
808
845
  }
846
+ function stripRerenderBlocks(content, definition) {
847
+ let stripped = content;
848
+ while (stripped.includes(definition.marker)) {
849
+ const next = stripRerenderBlock(stripped, definition);
850
+ if (next === stripped) break;
851
+ stripped = next;
852
+ }
853
+ return stripped;
854
+ }
809
855
  function executeTextPatch(cwd, path, definition) {
810
856
  if (definition.rerender && definition.operation !== "append") {
811
857
  throw new Error(`rerender text patch ${path} must use operation: 'append'`);
@@ -820,11 +866,13 @@ function computePatchedContent(original, definition) {
820
866
  if (content.includes(definition.marker)) {
821
867
  return healAlreadyPatchedContent(content, definition);
822
868
  }
823
- return definition.operation === "prepend" ? definition.content + content : content + definition.content;
869
+ if (definition.operation === "prepend") return definition.content + content;
870
+ const separator = content !== "" && !content.endsWith("\n") && !definition.content.startsWith("\n") ? "\n" : "";
871
+ return content + separator + definition.content;
824
872
  }
825
873
  function healAlreadyPatchedContent(content, definition) {
826
874
  if (definition.rerender && !content.includes(definition.content)) {
827
- return stripRerenderBlock(content, definition) + definition.content;
875
+ return stripRerenderBlocks(content, definition) + definition.content;
828
876
  }
829
877
  let healed = content;
830
878
  if (healed.includes("\n\n---#")) {
@@ -834,8 +882,8 @@ function healAlreadyPatchedContent(content, definition) {
834
882
  }
835
883
  function computeUnpatchedContent(content, definition) {
836
884
  let unpatched = removeExactTextPatchContent(content, definition);
837
- if (unpatched === content && definition.rerender) {
838
- unpatched = stripRerenderBlock(content, definition);
885
+ if (definition.rerender && unpatched.includes(definition.marker)) {
886
+ unpatched = stripRerenderBlocks(unpatched, definition);
839
887
  }
840
888
  if (unpatched === content && content.startsWith(definition.marker)) {
841
889
  const separatorIndex = content.indexOf("\n---");
@@ -848,7 +896,10 @@ function computeUnpatchedContent(content, definition) {
848
896
  unpatched = content.split("\n").slice(1).join("\n").replace(/^\n+/, "");
849
897
  unpatched = stripLeadingSafewordSeparator(unpatched);
850
898
  }
851
- return unpatched;
899
+ return appendReplacementAfterUnpatch(content, unpatched, definition.replacementAfterUnpatch);
900
+ }
901
+ function appendReplacementAfterUnpatch(original, unpatched, replacement) {
902
+ return unpatched === original || replacement === void 0 ? unpatched : unpatched + replacement;
852
903
  }
853
904
  function executeTextUnpatch(cwd, path, definition) {
854
905
  const fullPath = nodePath.join(cwd, path);
@@ -881,7 +932,7 @@ function shouldRemoveTextPatchTarget(content, definition) {
881
932
  import {
882
933
  existsSync,
883
934
  lstatSync as lstatSync2,
884
- readdirSync,
935
+ readdirSync as readdirSync2,
885
936
  readFileSync,
886
937
  realpathSync,
887
938
  writeFileSync
@@ -1008,7 +1059,7 @@ function expandMemberPattern(cwd, pattern) {
1008
1059
  const fullPath = nodePath2.join(cwd, baseDirectory);
1009
1060
  if (!existsSync(fullPath)) return [];
1010
1061
  try {
1011
- const entries = readdirSync(fullPath, { withFileTypes: true });
1062
+ const entries = readdirSync2(fullPath, { withFileTypes: true });
1012
1063
  return entries.filter((entry) => {
1013
1064
  if (!entry.isDirectory()) return false;
1014
1065
  const cargoPath = nodePath2.join(fullPath, entry.name, "Cargo.toml");
@@ -1125,7 +1176,7 @@ var pythonPack = {
1125
1176
  name: "Python",
1126
1177
  extensions: [".py", ".pyi"],
1127
1178
  detect(cwd) {
1128
- return existsInTree(cwd, "pyproject.toml") || existsInTree(cwd, "requirements.txt");
1179
+ return existsInTree(cwd, "pyproject.toml") || existsInTree(cwd, "requirements.txt") || existsInTree(cwd, "Pipfile") || existsInTree(cwd, "setup.py") || existsInTree(cwd, "setup.cfg");
1129
1180
  },
1130
1181
  setup(_cwd, _ctx) {
1131
1182
  return setupPythonTooling();
@@ -1146,7 +1197,7 @@ var rustPack = {
1146
1197
  };
1147
1198
 
1148
1199
  // src/packs/sql/index.ts
1149
- import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
1200
+ import { existsSync as existsSync2, readdirSync as readdirSync3 } from "fs";
1150
1201
  import nodePath4 from "path";
1151
1202
  var TIER1_MARKERS = [
1152
1203
  "dbt_project.yml",
@@ -1164,13 +1215,13 @@ var TIER2_DIRECTORIES = ["prisma/migrations", "drizzle", "db/migrations"];
1164
1215
  function directoryHasSqlFiles(directory) {
1165
1216
  if (!existsSync2(directory)) return false;
1166
1217
  try {
1167
- const entries = readdirSync2(directory, { withFileTypes: true });
1218
+ const entries = readdirSync3(directory, { withFileTypes: true });
1168
1219
  if (entries.some((entry) => entry.isFile() && entry.name.endsWith(".sql"))) return true;
1169
1220
  for (const entry of entries) {
1170
1221
  if (!entry.isDirectory()) {
1171
1222
  continue;
1172
1223
  }
1173
- const subEntries = readdirSync2(nodePath4.join(directory, entry.name));
1224
+ const subEntries = readdirSync3(nodePath4.join(directory, entry.name));
1174
1225
  if (subEntries.some((f) => f.endsWith(".sql"))) return true;
1175
1226
  }
1176
1227
  return false;
@@ -1305,7 +1356,7 @@ function detectHookManagerWorld(cwd, allDependencies) {
1305
1356
  }
1306
1357
 
1307
1358
  // src/utils/project-detector.ts
1308
- import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync2 } from "fs";
1359
+ import { existsSync as existsSync3, readdirSync as readdirSync4, readFileSync as readFileSync2 } from "fs";
1309
1360
  import nodePath8 from "path";
1310
1361
 
1311
1362
  // src/utils/cucumber-template-revisions.ts
@@ -1381,7 +1432,7 @@ function hasShellScripts(cwd, maxDepth = 4) {
1381
1432
  function scan(dir, depth) {
1382
1433
  if (depth > maxDepth) return false;
1383
1434
  try {
1384
- const entries = readdirSync3(dir, { withFileTypes: true });
1435
+ const entries = readdirSync4(dir, { withFileTypes: true });
1385
1436
  for (const entry of entries) {
1386
1437
  if (entry.isFile() && entry.name.endsWith(".sh")) {
1387
1438
  return true;
@@ -1508,7 +1559,7 @@ function isJsSourceFile(name, depth) {
1508
1559
  function scanForJsSource(directory, depth, maxDepth) {
1509
1560
  let entries;
1510
1561
  try {
1511
- entries = readdirSync3(directory, { withFileTypes: true });
1562
+ entries = readdirSync4(directory, { withFileTypes: true });
1512
1563
  } catch {
1513
1564
  return false;
1514
1565
  }
@@ -1559,7 +1610,7 @@ function findCucumberEvidenceInPackage(cwd, packagePath) {
1559
1610
  function findCucumberEvidenceUnderRoot(cwd, root) {
1560
1611
  let entries;
1561
1612
  try {
1562
- entries = readdirSync3(nodePath8.join(cwd, root), { withFileTypes: true });
1613
+ entries = readdirSync4(nodePath8.join(cwd, root), { withFileTypes: true });
1563
1614
  } catch {
1564
1615
  return void 0;
1565
1616
  }
@@ -1646,4 +1697,4 @@ export {
1646
1697
  LEFTHOOK_CONFIGS,
1647
1698
  createProjectContext
1648
1699
  };
1649
- //# sourceMappingURL=chunk-NOTBZCKG.js.map
1700
+ //# sourceMappingURL=chunk-2XR7J5WL.js.map