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.
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks.json +5 -5
- package/codex-plugin/skills/bdd/references/SCENARIOS.md +3 -0
- package/codex-plugin/skills/bdd/references/TDD.md +11 -5
- package/codex-plugin/skills/closeout/SKILL.md +24 -16
- package/codex-plugin/skills/finish-review/SKILL.md +4 -3
- package/codex-plugin/skills/lint/SKILL.md +1 -1
- package/codex-plugin/skills/review-spec/SKILL.md +5 -1
- package/codex-plugin/skills/self-review/SKILL.md +2 -2
- package/codex-plugin/skills/ticket-system/SKILL.md +3 -0
- package/dist/{architecture-N5ZC4CDU.js → architecture-TCSDEFKH.js} +9 -9
- package/dist/{architecture-document-UBA5UT4J.js → architecture-document-L2ADWVMV.js} +6 -6
- package/dist/{architecture-monorepo-E4JUYJGV.js → architecture-monorepo-NKOQZBEY.js} +4 -4
- package/dist/{boundary-HLJILY7R.js → boundary-QP2H7C46.js} +6 -6
- package/dist/{chunk-NOTBZCKG.js → chunk-2XR7J5WL.js} +93 -42
- package/dist/chunk-2XR7J5WL.js.map +1 -0
- package/dist/{chunk-4CZS4YJV.js → chunk-3OKZ3X4O.js} +3 -3
- package/dist/{chunk-GZ36UZJG.js → chunk-73TABQRT.js} +10 -10
- package/dist/{chunk-4CKCRZDO.js → chunk-7ULZND56.js} +2 -2
- package/dist/{chunk-34NCOTP5.js → chunk-7XELS325.js} +2 -2
- package/dist/{chunk-USPVNZ63.js → chunk-BZWO3BVD.js} +11 -7
- package/dist/chunk-BZWO3BVD.js.map +1 -0
- package/dist/{chunk-BQTC6YGN.js → chunk-D5ENZR6N.js} +2 -2
- package/dist/{chunk-SME5SU3O.js → chunk-DBXX5K74.js} +258 -48
- package/dist/chunk-DBXX5K74.js.map +1 -0
- package/dist/{chunk-V63TSN3C.js → chunk-FOYGZ4MK.js} +31 -10
- package/dist/chunk-FOYGZ4MK.js.map +1 -0
- package/dist/{chunk-ZRV7TOCX.js → chunk-G5AFQXXW.js} +2 -2
- package/dist/{chunk-AXAGJLMP.js → chunk-G757F3EU.js} +4 -4
- package/dist/{chunk-KXHVFCDT.js → chunk-GFDINP7M.js} +153 -27
- package/dist/chunk-GFDINP7M.js.map +1 -0
- package/dist/{chunk-S2T356DA.js → chunk-HF3DXZEA.js} +7 -10
- package/dist/chunk-HF3DXZEA.js.map +1 -0
- package/dist/{chunk-NSZGI7HK.js → chunk-HNCUIXDY.js} +9 -9
- package/dist/{chunk-NSZGI7HK.js.map → chunk-HNCUIXDY.js.map} +1 -1
- package/dist/{chunk-BSOUMHMQ.js → chunk-IPTM5VE7.js} +101 -1094
- package/dist/chunk-IPTM5VE7.js.map +1 -0
- package/dist/chunk-JBOLNV2L.js +21 -0
- package/dist/chunk-JBOLNV2L.js.map +1 -0
- package/dist/chunk-JSR3HXFT.js +1231 -0
- package/dist/chunk-JSR3HXFT.js.map +1 -0
- package/dist/{chunk-BAVDDAN3.js → chunk-KS2ESRQL.js} +9 -9
- package/dist/chunk-LFC6CMMX.js +136 -0
- package/dist/chunk-LFC6CMMX.js.map +1 -0
- package/dist/{chunk-QTG2EXFT.js → chunk-LUNHU6Z3.js} +2 -2
- package/dist/{chunk-QTG2EXFT.js.map → chunk-LUNHU6Z3.js.map} +1 -1
- package/dist/{chunk-WVDRXD67.js → chunk-MLBZFB5A.js} +1 -1
- package/dist/chunk-MLBZFB5A.js.map +1 -0
- package/dist/{chunk-KFVEPQ7X.js → chunk-MVFHTUQ3.js} +2 -2
- package/dist/{chunk-TJ627SKI.js → chunk-O3GZ77F7.js} +15 -1
- package/dist/chunk-O3GZ77F7.js.map +1 -0
- package/dist/{chunk-Q5KLE6ZF.js → chunk-OKPRQGFG.js} +2 -2
- package/dist/{chunk-QUDIOBHP.js → chunk-PD7ZXRHV.js} +139 -18
- package/dist/chunk-PD7ZXRHV.js.map +1 -0
- package/dist/chunk-QAHJSG53.js +30 -0
- package/dist/chunk-QAHJSG53.js.map +1 -0
- package/dist/{chunk-3SOJBMM6.js → chunk-R6BSRNU4.js} +6 -6
- package/dist/{chunk-3SOJBMM6.js.map → chunk-R6BSRNU4.js.map} +1 -1
- package/dist/{chunk-J7UDLLTK.js → chunk-RQ7RZYNB.js} +39 -25
- package/dist/chunk-RQ7RZYNB.js.map +1 -0
- package/dist/{chunk-664YNAMR.js → chunk-RTFPKJJV.js} +5 -5
- package/dist/{chunk-GKR44BCH.js → chunk-RVDXBHUG.js} +2 -2
- package/dist/chunk-THLEVFEX.js +22 -0
- package/dist/chunk-THLEVFEX.js.map +1 -0
- package/dist/{chunk-A7Z27COA.js → chunk-VFWAZLEF.js} +3 -3
- package/dist/{chunk-I4KBHRZW.js → chunk-VJUPD7JI.js} +7 -7
- package/dist/{chunk-3EJNDIAO.js → chunk-VZ5B2SIO.js} +2 -2
- package/dist/chunk-WAMQTZAH.js +19 -0
- package/dist/chunk-WAMQTZAH.js.map +1 -0
- package/dist/{chunk-FDHJ63FZ.js → chunk-WVA4Q6BX.js} +1 -1
- package/dist/{chunk-FDHJ63FZ.js.map → chunk-WVA4Q6BX.js.map} +1 -1
- package/dist/{chunk-AV5LTEME.js → chunk-YZ5JGDOV.js} +30 -134
- package/dist/chunk-YZ5JGDOV.js.map +1 -0
- package/dist/{cleanup-X556LNWM.js → cleanup-JNLZ7AXA.js} +9 -9
- package/dist/{cleanup-command-RV2Q7G72.js → cleanup-command-MFAYNYRW.js} +15 -15
- package/dist/cli.js +152 -109
- package/dist/cli.js.map +1 -1
- package/dist/{clients-HP23HRN2.js → clients-7AJBKEWF.js} +2 -2
- package/dist/codex-bootstrap-KFNUS63Y.js +252 -0
- package/dist/codex-bootstrap-KFNUS63Y.js.map +1 -0
- package/dist/{codex-hook-IPX3PDXX.js → codex-hook-TNMYVOR7.js} +57 -49
- package/dist/codex-hook-TNMYVOR7.js.map +1 -0
- package/dist/{codify-J5ZLJ3HK.js → codify-RBWBRXQO.js} +11 -8
- package/dist/codify-RBWBRXQO.js.map +1 -0
- package/dist/{commands-L4AINARC.js → commands-4UXHBZFD.js} +127 -71
- package/dist/commands-4UXHBZFD.js.map +1 -0
- package/dist/{config-LUSJAK6B.js → config-GS7BTZJD.js} +2 -2
- package/dist/{configured-paths-SPQ5XWOM.js → configured-paths-CREUDQF3.js} +4 -4
- package/dist/contract-42CIWIWV.js +8 -0
- package/dist/{coordinator-SA75UB3C.js → coordinator-WOPGWDXK.js} +231 -144
- package/dist/coordinator-WOPGWDXK.js.map +1 -0
- package/dist/corpus-CAFYEMBO.js +15 -0
- package/dist/{doctor-DSC36R4U.js → doctor-AZP5D7QP.js} +21 -21
- package/dist/{feature-directories-VOHVV26X.js → feature-directories-6TLM7Y4H.js} +6 -6
- package/dist/{finalization-7SMLMVLV.js → finalization-AQT3SOP7.js} +4 -4
- package/dist/{gh-cli-ZMO7D2KE.js → gh-cli-XCQT5ST7.js} +2 -2
- package/dist/{github-rest-KGKS4V2H.js → github-rest-CXFJ2NFF.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{job-CDADLJDF.js → job-4POTQJZK.js} +20 -21
- package/dist/job-4POTQJZK.js.map +1 -0
- package/dist/{learning-sync-6BTHM6ZC.js → learning-sync-QVXBOP6J.js} +4 -4
- package/dist/{legacy-global-guidance-2JL4Y5EB.js → legacy-global-guidance-CPGUUNJI.js} +5 -5
- package/dist/{lint-gherkin-COSHVS6Z.js → lint-gherkin-LZ45NG4A.js} +7 -7
- package/dist/{operations-XVRQSLJG.js → operations-ODOA5CDG.js} +18 -16
- package/dist/{output-3LB2OEHN.js → output-QTZCFLAY.js} +2 -2
- package/dist/{packet-5WT5M3OS.js → packet-5G6RT2EW.js} +2 -2
- package/dist/presets/typescript/index.js +2 -2
- package/dist/{profile-UMKQ5JVO.js → profile-QGLZNLRU.js} +7 -7
- package/dist/{prompt-GWYDFMLO.js → prompt-UCTSCXSS.js} +2 -2
- package/dist/remove-UQPEXXNP.js +23 -0
- package/dist/{retro-IFSVU5KS.js → retro-ELKR6GHF.js} +8 -8
- package/dist/{retro-draft-spool-4S3TCHWL.js → retro-draft-spool-M75BTT32.js} +2 -2
- package/dist/{retro-extract-OSBFAXLU.js → retro-extract-RA6THOC4.js} +2 -2
- package/dist/{review-pr-6S2352CK.js → review-pr-MN6QESAJ.js} +2 -2
- package/dist/{review-pr-publication-CGHCRRUI.js → review-pr-publication-6AVOGWR2.js} +2 -2
- package/dist/{run-5JESKXUR.js → run-JFDGRAH2.js} +4 -4
- package/dist/{self-report-VPHBPPSJ.js → self-report-5APRPB5P.js} +2 -2
- package/dist/status-ATFG6Q3K.js +21 -0
- package/dist/{status-EX4L3ELO.js → status-HIQSP3F5.js} +21 -21
- package/dist/{sync-config-O7J7IDGE.js → sync-config-XPAJKBRO.js} +5 -5
- package/dist/{sync-tracker-U2GYOFUO.js → sync-tracker-3E5BWX73.js} +6 -6
- package/dist/{test-execution-6LIPMBIH.js → test-execution-VDECX7TO.js} +8 -7
- package/dist/{test-execution-6LIPMBIH.js.map → test-execution-VDECX7TO.js.map} +1 -1
- package/dist/{test-plan-VASHZXJ7.js → test-plan-2L7A7UZV.js} +7 -6
- package/dist/{test-plan-VASHZXJ7.js.map → test-plan-2L7A7UZV.js.map} +1 -1
- package/dist/{ticket-new-3YXDIWPC.js → ticket-new-HZKNZ5GO.js} +11 -11
- package/dist/ticket-new-HZKNZ5GO.js.map +1 -0
- package/dist/{ticket-sync-LLUBXZ2R.js → ticket-sync-4QJD3KOL.js} +5 -5
- package/dist/{tracker-map-HR4MBYG5.js → tracker-map-CFMFIEER.js} +2 -2
- package/dist/{tracker-sync-YDICZN66.js → tracker-sync-MOPJE6M7.js} +2 -2
- package/package.json +1 -1
- package/templates/SAFEWORD.md +2 -2
- package/templates/commands/lint.md +1 -1
- package/templates/scripts/closeout-cleanup.ts +127 -31
- package/templates/skills/bdd/SCENARIOS.md +3 -0
- package/templates/skills/bdd/TDD.md +11 -5
- package/templates/skills/closeout/SKILL.md +25 -17
- package/templates/skills/finish-review/SKILL.md +4 -3
- package/templates/skills/lint/SKILL.md +1 -1
- package/templates/skills/review-spec/SKILL.md +5 -1
- package/templates/skills/self-review/SKILL.md +2 -2
- package/templates/skills/ticket-system/SKILL.md +3 -0
- package/dist/chunk-6S7CTKGC.js +0 -128
- package/dist/chunk-6S7CTKGC.js.map +0 -1
- package/dist/chunk-AV5LTEME.js.map +0 -1
- package/dist/chunk-BSOUMHMQ.js.map +0 -1
- package/dist/chunk-J7UDLLTK.js.map +0 -1
- package/dist/chunk-KXHVFCDT.js.map +0 -1
- package/dist/chunk-NOTBZCKG.js.map +0 -1
- package/dist/chunk-QUDIOBHP.js.map +0 -1
- package/dist/chunk-S2T356DA.js.map +0 -1
- package/dist/chunk-SME5SU3O.js.map +0 -1
- package/dist/chunk-TJ627SKI.js.map +0 -1
- package/dist/chunk-USPVNZ63.js.map +0 -1
- package/dist/chunk-V63TSN3C.js.map +0 -1
- package/dist/chunk-WRUXO3WA.js +0 -48
- package/dist/chunk-WRUXO3WA.js.map +0 -1
- package/dist/chunk-WVDRXD67.js.map +0 -1
- package/dist/codex-bootstrap-MGY2D6HU.js +0 -178
- package/dist/codex-bootstrap-MGY2D6HU.js.map +0 -1
- package/dist/codex-hook-IPX3PDXX.js.map +0 -1
- package/dist/codify-J5ZLJ3HK.js.map +0 -1
- package/dist/commands-L4AINARC.js.map +0 -1
- package/dist/contract-KEZY6YC3.js +0 -8
- package/dist/coordinator-SA75UB3C.js.map +0 -1
- package/dist/corpus-PPOBTF4J.js +0 -15
- package/dist/job-CDADLJDF.js.map +0 -1
- package/dist/remove-E6HGNHOU.js +0 -22
- package/dist/status-7VIGNTUI.js +0 -21
- package/dist/ticket-new-3YXDIWPC.js.map +0 -1
- /package/dist/{architecture-N5ZC4CDU.js.map → architecture-TCSDEFKH.js.map} +0 -0
- /package/dist/{architecture-document-UBA5UT4J.js.map → architecture-document-L2ADWVMV.js.map} +0 -0
- /package/dist/{architecture-monorepo-E4JUYJGV.js.map → architecture-monorepo-NKOQZBEY.js.map} +0 -0
- /package/dist/{boundary-HLJILY7R.js.map → boundary-QP2H7C46.js.map} +0 -0
- /package/dist/{chunk-4CZS4YJV.js.map → chunk-3OKZ3X4O.js.map} +0 -0
- /package/dist/{chunk-GZ36UZJG.js.map → chunk-73TABQRT.js.map} +0 -0
- /package/dist/{chunk-4CKCRZDO.js.map → chunk-7ULZND56.js.map} +0 -0
- /package/dist/{chunk-34NCOTP5.js.map → chunk-7XELS325.js.map} +0 -0
- /package/dist/{chunk-BQTC6YGN.js.map → chunk-D5ENZR6N.js.map} +0 -0
- /package/dist/{chunk-ZRV7TOCX.js.map → chunk-G5AFQXXW.js.map} +0 -0
- /package/dist/{chunk-AXAGJLMP.js.map → chunk-G757F3EU.js.map} +0 -0
- /package/dist/{chunk-BAVDDAN3.js.map → chunk-KS2ESRQL.js.map} +0 -0
- /package/dist/{chunk-KFVEPQ7X.js.map → chunk-MVFHTUQ3.js.map} +0 -0
- /package/dist/{chunk-Q5KLE6ZF.js.map → chunk-OKPRQGFG.js.map} +0 -0
- /package/dist/{chunk-664YNAMR.js.map → chunk-RTFPKJJV.js.map} +0 -0
- /package/dist/{chunk-GKR44BCH.js.map → chunk-RVDXBHUG.js.map} +0 -0
- /package/dist/{chunk-A7Z27COA.js.map → chunk-VFWAZLEF.js.map} +0 -0
- /package/dist/{chunk-I4KBHRZW.js.map → chunk-VJUPD7JI.js.map} +0 -0
- /package/dist/{chunk-3EJNDIAO.js.map → chunk-VZ5B2SIO.js.map} +0 -0
- /package/dist/{cleanup-X556LNWM.js.map → cleanup-JNLZ7AXA.js.map} +0 -0
- /package/dist/{cleanup-command-RV2Q7G72.js.map → cleanup-command-MFAYNYRW.js.map} +0 -0
- /package/dist/{clients-HP23HRN2.js.map → clients-7AJBKEWF.js.map} +0 -0
- /package/dist/{config-LUSJAK6B.js.map → config-GS7BTZJD.js.map} +0 -0
- /package/dist/{configured-paths-SPQ5XWOM.js.map → configured-paths-CREUDQF3.js.map} +0 -0
- /package/dist/{contract-KEZY6YC3.js.map → contract-42CIWIWV.js.map} +0 -0
- /package/dist/{corpus-PPOBTF4J.js.map → corpus-CAFYEMBO.js.map} +0 -0
- /package/dist/{doctor-DSC36R4U.js.map → doctor-AZP5D7QP.js.map} +0 -0
- /package/dist/{feature-directories-VOHVV26X.js.map → feature-directories-6TLM7Y4H.js.map} +0 -0
- /package/dist/{finalization-7SMLMVLV.js.map → finalization-AQT3SOP7.js.map} +0 -0
- /package/dist/{gh-cli-ZMO7D2KE.js.map → gh-cli-XCQT5ST7.js.map} +0 -0
- /package/dist/{github-rest-KGKS4V2H.js.map → github-rest-CXFJ2NFF.js.map} +0 -0
- /package/dist/{learning-sync-6BTHM6ZC.js.map → learning-sync-QVXBOP6J.js.map} +0 -0
- /package/dist/{legacy-global-guidance-2JL4Y5EB.js.map → legacy-global-guidance-CPGUUNJI.js.map} +0 -0
- /package/dist/{lint-gherkin-COSHVS6Z.js.map → lint-gherkin-LZ45NG4A.js.map} +0 -0
- /package/dist/{operations-XVRQSLJG.js.map → operations-ODOA5CDG.js.map} +0 -0
- /package/dist/{output-3LB2OEHN.js.map → output-QTZCFLAY.js.map} +0 -0
- /package/dist/{packet-5WT5M3OS.js.map → packet-5G6RT2EW.js.map} +0 -0
- /package/dist/{profile-UMKQ5JVO.js.map → profile-QGLZNLRU.js.map} +0 -0
- /package/dist/{prompt-GWYDFMLO.js.map → prompt-UCTSCXSS.js.map} +0 -0
- /package/dist/{remove-E6HGNHOU.js.map → remove-UQPEXXNP.js.map} +0 -0
- /package/dist/{retro-IFSVU5KS.js.map → retro-ELKR6GHF.js.map} +0 -0
- /package/dist/{retro-draft-spool-4S3TCHWL.js.map → retro-draft-spool-M75BTT32.js.map} +0 -0
- /package/dist/{retro-extract-OSBFAXLU.js.map → retro-extract-RA6THOC4.js.map} +0 -0
- /package/dist/{review-pr-6S2352CK.js.map → review-pr-MN6QESAJ.js.map} +0 -0
- /package/dist/{review-pr-publication-CGHCRRUI.js.map → review-pr-publication-6AVOGWR2.js.map} +0 -0
- /package/dist/{run-5JESKXUR.js.map → run-JFDGRAH2.js.map} +0 -0
- /package/dist/{self-report-VPHBPPSJ.js.map → self-report-5APRPB5P.js.map} +0 -0
- /package/dist/{status-7VIGNTUI.js.map → status-ATFG6Q3K.js.map} +0 -0
- /package/dist/{status-EX4L3ELO.js.map → status-HIQSP3F5.js.map} +0 -0
- /package/dist/{sync-config-O7J7IDGE.js.map → sync-config-XPAJKBRO.js.map} +0 -0
- /package/dist/{sync-tracker-U2GYOFUO.js.map → sync-tracker-3E5BWX73.js.map} +0 -0
- /package/dist/{ticket-sync-LLUBXZ2R.js.map → ticket-sync-4QJD3KOL.js.map} +0 -0
- /package/dist/{tracker-map-HR4MBYG5.js.map → tracker-map-CFMFIEER.js.map} +0 -0
- /package/dist/{tracker-sync-YDICZN66.js.map → tracker-sync-MOPJE6M7.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/owned-paths.ts","../src/schema.ts","../src/packs/golang/files.ts","../src/packs/python/files.ts","../src/packs/rust/files.ts","../src/packs/sql/dialect.ts","../src/packs/sql/files.ts","../src/utils/json-merge.ts","../src/packs/typescript/files.ts","../src/cursor-wrappers.ts","../src/templates/content.ts"],"sourcesContent":["/**\n * Derive the set of top-level path prefixes that safeword may write to,\n * sourced from SAFEWORD_SCHEMA at build time. Consumed by the auto-upgrade\n * hook to identify files to stage after `safeword install` runs.\n *\n * Why this exists: the hook previously hardcoded the prefix list, which\n * silently drifts when a pack introduces a new top-level directory.\n *\n * Matching semantics: dir prefixes (trailing `/`) match by `startsWith`;\n * bare file paths match by exact equality. The generated module exports\n * both the flat list and an `isSafewordPath(file)` helper that uses these\n * semantics.\n */\n\nimport nodePath from 'node:path';\n\nimport type { JsonMergeDefinition, ProjectContext } from './packs/types.js';\nimport type { SafewordSchema } from './schema.js';\nimport { resolveNamespaceRoot } from './utils/configured-paths.js';\n\n/**\n * Safeword-owned top-level directories a customer's OWN formatter must skip, so\n * safeword's files never churn in their diffs/CI (ticket EYRK34). Single source\n * for every formatter's ignore wiring — `.prettierignore`, Biome excludes, ruff\n * `extend-exclude`, etc. — so adding an owned dir updates all of them in one\n * place. Bare names (no trailing slash); each tool maps to its own syntax.\n *\n * Covers the two well-known namespace roots; a *custom* `paths.projectRoot` is\n * layered on at apply time via `safewordIgnoreDirectories(resolvedNamespaceDirectory(ctx))`\n * (issue #273), since this static list can't name an arbitrary root.\n *\n * `features/` and `steps/` are intentionally absent: the customer authors the\n * BDD feature files and step definitions there and wants them formatted. A drift\n * test asserts every entry here is a directory the schema actually manages.\n */\nexport const SAFEWORD_IGNORE_DIRS: readonly string[] = [\n '.safeword',\n '.claude',\n '.cursor',\n '.codex',\n '.agents',\n '.project',\n '.safeword-project',\n];\n\n/**\n * Safeword-owned dirs a formatter must skip, including a custom `paths.projectRoot`\n * (issue #273). `namespaceRootLabel` is the repo-relative resolved root from\n * `resolvedNamespaceDirectory`; undefined (default/legacy/repo-root) leaves the static\n * base list untouched.\n */\nexport function safewordIgnoreDirectories(namespaceRootLabel?: string): readonly string[] {\n return namespaceRootLabel !== undefined && !SAFEWORD_IGNORE_DIRS.includes(namespaceRootLabel)\n ? [...SAFEWORD_IGNORE_DIRS, namespaceRootLabel]\n : SAFEWORD_IGNORE_DIRS;\n}\n\n/**\n * The resolved namespace root as a repo-relative dir label, returned only when it\n * is a custom root the static lists don't already cover. Returns undefined for the\n * default `.project`/legacy `.safeword-project` roots, for `paths.projectRoot:'.'`\n * (the repo root — excluding or staging it would match the whole repo), and for any\n * root resolved OUTSIDE the repo ('../…' would leak nonsensical entries).\n */\nexport function resolvedNamespaceDirectory(ctx: ProjectContext): string | undefined {\n const label = resolvedNamespaceRootLabel(ctx);\n // Skip the repo root ('.'), the well-known roots (already in the static lists),\n // and any root resolved OUTSIDE the repo ('../…') — a traversal label would leak\n // nonsensical ignore/prefix entries that match nothing under the repo.\n if (\n label === '.' ||\n label === '..' ||\n label.startsWith('../') ||\n SAFEWORD_IGNORE_DIRS.includes(label)\n ) {\n return undefined;\n }\n return label;\n}\n\n/**\n * The repo-relative resolved namespace-root label — always concrete (`.project` by\n * default, the legacy or custom `paths.projectRoot` otherwise), unlike\n * {@link resolvedNamespaceDirectory} which suppresses the well-known roots. Used where a\n * managed file must name the actual root path (e.g. the `.gitattributes` ticket-index\n * entries, #566). Repo-root namespace (`.`) yields `.`, so callers prefix-join cleanly.\n */\nexport function resolvedNamespaceRootLabel(ctx: ProjectContext): string {\n const root = ctx.namespaceRoot ?? resolveNamespaceRoot(ctx.cwd);\n return (nodePath.relative(ctx.cwd, root) || '.').replaceAll(nodePath.sep, '/');\n}\n\n/**\n * The safeword-owned ignore directories for a context — the static base list plus\n * the resolved custom `paths.projectRoot` (issue #273). Composition used by the\n * formatter merges so the two-step resolution lives in one place.\n */\nexport function resolvedIgnoreDirectories(ctx: ProjectContext): readonly string[] {\n return safewordIgnoreDirectories(resolvedNamespaceDirectory(ctx));\n}\n\n/** Namespace directories whose authored contents survive Safeword uninstall. */\nexport function durableNamespaceDirectories(ctx: ProjectContext): readonly string[] {\n const custom = resolvedNamespaceDirectory(ctx);\n return custom === undefined\n ? ['.project', '.safeword-project']\n : ['.project', '.safeword-project', custom];\n}\n\n/**\n * Build a JSON-merge that adds safeword-owned dirs to a customer formatter's\n * string-array exclude/ignore field so the tool skips them (ticket EYRK34).\n * Resolved at apply time from `ctx`, so a custom `paths.projectRoot` is excluded\n * too (issue #273); `skipIfMissing` → only ever touches a config the customer has.\n *\n * `globForDir` controls the per-dir glob and defaults to the bare trailing-globstar\n * form used by dprint (`excludes`) and oxfmt (`ignorePatterns`). markdownlint-cli2\n * (`ignores`) overrides it with a leading-globstar form so the glob also matches\n * the absolute paths lint-staged passes (ticket #262).\n */\nexport function dirGlobExcludeMerge(\n field: string,\n globForDirectory: (dir: string) => string = dir => `${dir}/**`,\n): JsonMergeDefinition {\n return {\n keys: [field],\n skipIfMissing: true,\n merge: (existing, ctx) => {\n const safewordGlobs = resolvedIgnoreDirectories(ctx).map(dir => globForDirectory(dir));\n const current = Array.isArray(existing[field]) ? (existing[field] as string[]) : [];\n const merged = [...current];\n for (const glob of safewordGlobs) {\n if (!merged.includes(glob)) merged.push(glob);\n }\n return { ...existing, [field]: merged };\n },\n unmerge: (existing, ctx) => {\n const safewordGlobs = new Set(\n resolvedIgnoreDirectories(ctx).map(dir => globForDirectory(dir)),\n );\n const durableGlobs = new Set(\n durableNamespaceDirectories(ctx).map(dir => globForDirectory(dir)),\n );\n const current = Array.isArray(existing[field]) ? (existing[field] as string[]) : [];\n const cleaned = current.filter(entry => !safewordGlobs.has(entry) || durableGlobs.has(entry));\n // Drop the field without a dynamic `delete` or unused binding, re-adding it\n // only when entries remain.\n const rest = Object.fromEntries(Object.entries(existing).filter(([key]) => key !== field));\n return cleaned.length > 0 ? { ...rest, [field]: cleaned } : rest;\n },\n };\n}\n\nexport function computeSafewordPathPrefixes(schema: SafewordSchema): readonly string[] {\n const allPaths = [\n ...Object.keys(schema.ownedFiles),\n ...Object.keys(schema.managedFiles),\n ...Object.keys(schema.jsonMerges),\n ...Object.keys(schema.textPatches),\n ];\n\n const prefixes = new Set<string>();\n for (const path of allPaths) {\n const slashIndex = path.indexOf('/');\n prefixes.add(slashIndex === -1 ? path : path.slice(0, slashIndex + 1));\n }\n\n return [...prefixes].toSorted((a, b) => a.localeCompare(b));\n}\n\n/**\n * Reference implementation of the matching predicate. The generated module's\n * `isSafewordPath` is a closure over SAFEWORD_PATHS that runs this same logic.\n * Extracted so tests can verify semantics without evaluating generated code.\n */\nexport function matchesSafewordPath(file: string, prefixes: readonly string[]): boolean {\n for (const prefix of prefixes) {\n if (prefix.endsWith('/')) {\n if (file.startsWith(prefix)) return true;\n } else if (file === prefix) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Reference implementation of the auto-upgrade staging filter. Mirrors the\n * `filterSafewordFiles` exported from the generated module so tests can\n * exercise the exact behavior the hook relies on.\n */\nexport function referenceFilterSafewordFiles(\n changedFiles: readonly string[],\n untrackedFiles: readonly string[],\n prefixes: readonly string[],\n): readonly string[] {\n return [...changedFiles, ...untrackedFiles].filter(f => matchesSafewordPath(f, prefixes));\n}\n\nexport function generateOwnedPathsModule(\n schema: SafewordSchema,\n namespaceRootLabel?: string,\n): string {\n // The schema manifest carries legacy-prefixed namespace entries; installed\n // projects may run either well-known root (AQJ95G), so emit both prefixes —\n // plus the resolved custom root (issue #273) so the auto-upgrade hook stages\n // files scaffolded under a custom `paths.projectRoot`.\n const customPrefix = namespaceRootLabel === undefined ? [] : [`${namespaceRootLabel}/`];\n const prefixes = [\n ...new Set([\n ...computeSafewordPathPrefixes(schema).flatMap(prefix =>\n prefix === '.safeword-project/' ? ['.safeword-project/', '.project/'] : [prefix],\n ),\n ...customPrefix,\n ]),\n ];\n const entries = prefixes.map(prefix => ` '${prefix}',`).join('\\n');\n\n return `// Auto-generated by safeword from SAFEWORD_SCHEMA. Do not edit by hand.\n// Source: ownedFiles ∪ managedFiles ∪ jsonMerges ∪ textPatches → top-level prefixes.\n// Regenerated on every \\`safeword install\\`.\n\nexport const SAFEWORD_PATHS: readonly string[] = [\n${entries}\n];\n\n/**\n * Match a file path against the safeword-managed set. Directory prefixes\n * (entries ending in \\`/\\`) match by \\`startsWith\\`; bare file paths must\n * match exactly so e.g. \\`package.json.bak\\` does not match \\`package.json\\`.\n */\nexport function isSafewordPath(file: string): boolean {\n for (const prefix of SAFEWORD_PATHS) {\n if (prefix.endsWith('/')) {\n if (file.startsWith(prefix)) return true;\n } else if (file === prefix) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Pick the safeword-managed subset of files reported by \\`git diff --name-only\\`\n * (changed) and \\`git ls-files --others --exclude-standard\\` (untracked).\n * Used by the auto-upgrade hook to decide what to stage after \\`safeword install\\`.\n */\nexport function filterSafewordFiles(\n changedFiles: readonly string[],\n untrackedFiles: readonly string[],\n): readonly string[] {\n return [...changedFiles, ...untrackedFiles].filter(f => isSafewordPath(f));\n}\n`;\n}\n","/**\n * SAFEWORD Schema - Single Source of Truth\n *\n * All files, directories, configurations, and packages managed by safeword\n * are defined here. Commands use this schema via the reconciliation engine.\n *\n * Adding a new file? Add it here and it will be handled by setup/upgrade/reset.\n */\n\nimport nodePath from 'node:path';\n\nimport { acceptedHistoricalHookEntries } from './claude-plugin/historical-ownership.js';\nimport { CODEX_MIGRATION_SCHEMA } from './codex-plugin/inventory.js';\nimport { golangManagedFiles, golangOwnedFiles } from './packs/golang/files.js';\nimport { pythonManagedFiles, pythonOwnedFiles } from './packs/python/files.js';\nimport { rustManagedFiles, rustOwnedFiles } from './packs/rust/files.js';\nimport { sqlManagedFiles, sqlOwnedFiles } from './packs/sql/files.js';\nimport {\n typescriptJsonMerges,\n typescriptManagedFiles,\n typescriptOwnedFiles,\n typescriptPackages,\n} from './packs/typescript/files.js';\n// Re-export shared types from packs/types.ts (breaks circular dependency)\nexport type {\n FileDefinition,\n JsonMergeDefinition,\n ManagedFileDefinition,\n ProjectContext,\n} from './packs/types.js';\nimport { CURSOR_COMMAND_WRAPPERS, CURSOR_RULE_WRAPPERS } from './cursor-wrappers.js';\nimport {\n dirGlobExcludeMerge,\n durableNamespaceDirectories,\n generateOwnedPathsModule,\n resolvedIgnoreDirectories,\n resolvedNamespaceDirectory,\n resolvedNamespaceRootLabel,\n} from './owned-paths.js';\nimport type {\n FileDefinition,\n JsonMergeDefinition,\n ManagedFileDefinition,\n ProjectContext,\n} from './packs/types.js';\nimport { CURSOR_HOOKS, SETTINGS_HOOKS } from './templates/config.js';\nimport { AGENTS_MD_LINK, CLAUDE_MD_IMPORT_BLOCK } from './templates/content.js';\nimport { getTemplatesDirectory, readFile, readFileSafe } from './utils/fs.js';\nimport { filterOutEquivalentSafewordHooks, filterOutSafewordHooks } from './utils/hooks.js';\nimport { MCP_SERVERS } from './utils/install.js';\nimport { assignOrPrune } from './utils/json-merge.js';\nimport { VERSION } from './version.js';\n\nexport interface TextPatchDefinition {\n operation: 'prepend' | 'append';\n // Static string, or a factory resolved with ctx at plan time so the block can\n // depend on the resolved namespace root (e.g. a custom paths.projectRoot, #293).\n content: string | ((ctx: ProjectContext) => string);\n marker: string; // Used to detect if already applied & for removal\n // On apply, first remove this exact legacy block if present, then add `content`\n // — a byte-exact, idempotent one-block swap for migrating a managed file (e.g.\n // replacing a superseded SessionStart hook with its replacement) without a\n // separate imperative path. A no-op when the block is absent; guarded by\n // applyWhenContentIncludes so it only touches safeword-scaffolded files.\n supersedes?: string;\n applyWhenContentIncludes?: string[]; // Optional guard for semi-owned config files\n // Context predicate gating APPLICATION only (install/upgrade/creation) —\n // e.g. hook shims apply only in husky hosts (ZJMZ50). Unpatch is deliberately\n // NOT gated: reset must strip a leftover block even when the host's world\n // changed after install (a husky -> lefthook migration).\n when?: (ctx: ProjectContext) => boolean;\n unpatchContent?: string[]; // Additional exact blocks to remove on uninstall/reset\n // Replacement appended after this managed block is removed. Used when an\n // uninstall must retain configuration for project-owned durable content.\n replacementAfterUnpatch?: string | ((ctx: ProjectContext) => string);\n removeFileIfContentEquals?: string[]; // Delete file only when remaining content is known scaffold\n // When set (append patches only), re-render the managed block in place on\n // upgrade instead of skip-on-marker — so a ctx-dependent block (e.g. a custom\n // projectRoot added to .prettierignore) heals on existing installs. A no-op\n // when the block is already current, so unchanged installs never churn (#293).\n rerender?: boolean;\n // Optional recognizer for every owned body line in a rerenderable block. Use\n // this when ctx changes can replace (rather than only append) body lines, so\n // stale variants can still be removed without consuming following user lines.\n rerenderOwnedLinePattern?: RegExp;\n}\n\nexport interface ContractDefinition {\n requires: string[]; // Strings that must appear verbatim in the file content\n}\n\ninterface CodexMigrationDefinition {\n legacyFiles: string[];\n cleanupFiles: string[];\n legacyDirs: string[];\n hookEvents: string[];\n hookEventNames: Record<string, string>;\n hookScripts: string[];\n sharedRuntimePaths: readonly string[];\n cleanupRuntimePaths: string[];\n hookScriptEvents: Record<string, string>;\n hookScriptPrefix: string;\n packageRunner: 'npx';\n projectMarker: string;\n}\n\nexport interface SafewordSchema {\n version: string;\n ownedDirs: string[]; // Fully owned - create on setup, delete on reset\n sharedDirs: string[]; // We add to but don't own\n preservedDirs: string[]; // Created on setup, NOT deleted on reset (user data)\n deprecatedFiles: string[]; // Files to delete on upgrade (renamed or removed)\n deprecatedPackages: string[]; // Packages to uninstall on upgrade (consolidated into safeword plugin)\n deprecatedDirs: string[]; // Directories to delete on upgrade (no longer managed)\n ownedFiles: Record<string, FileDefinition>; // Overwrite on upgrade (if changed)\n managedFiles: Record<string, ManagedFileDefinition>; // Create if missing, update if safeword content\n jsonMerges: Record<string, JsonMergeDefinition>;\n // A file may carry an ordered list of patches. Patches apply in list order and\n // unpatch in reverse, so the patch that owns file removal\n // (removeFileIfContentEquals) runs last on uninstall. See #269.\n textPatches: Record<string, TextPatchDefinition | TextPatchDefinition[]>;\n legacyTextPatches: Record<string, TextPatchDefinition>; // Remove old managed text patches without installing them\n contracts: Record<string, ContractDefinition>; // Files that must contain specific strings (predicate parity)\n codexMigration: CodexMigrationDefinition; // Historical Codex identities retained until explicit finalization\n packages: {\n base: string[];\n conditional: Record<string, string[]>;\n };\n}\n\n// ============================================================================\n// Shared JSON Merge Definitions\n// ============================================================================\n\n/**\n * MCP servers JSON merge - shared between .mcp.json and .cursor/mcp.json\n */\nconst MCP_JSON_MERGE: JsonMergeDefinition = {\n keys: ['mcpServers.context7', 'mcpServers.playwright'],\n removeFileIfEmpty: true,\n merge: existing => {\n const mcpServers = { ...(existing.mcpServers as Record<string, unknown>) };\n // Add-if-missing: preserve any user-authored entry — including its key\n // ordering — and inject safeword's default only when the server key is\n // absent. This keeps `upgrade` from clobbering a customized\n // context7/playwright definition (e.g. a hosted HTTP transport), and\n // spreading existing first means an already-correct file produces an\n // identical merge (no write churn). (#255)\n mcpServers.context7 ??= MCP_SERVERS.context7;\n mcpServers.playwright ??= MCP_SERVERS.playwright;\n return { ...existing, mcpServers };\n },\n unmerge: existing => {\n const result = { ...existing };\n let mcpServers = { ...(existing.mcpServers as Record<string, unknown>) };\n\n // Delete only definitions that are still byte-for-byte Safeword defaults.\n // Add-if-missing cannot prove ownership of a key that a customer later edits,\n // so ambiguous/customized entries must survive uninstall.\n for (const name of ['context7', 'playwright'] as const) {\n if (JSON.stringify(mcpServers[name]) === JSON.stringify(MCP_SERVERS[name])) {\n mcpServers = Object.fromEntries(Object.entries(mcpServers).filter(([key]) => key !== name));\n }\n }\n\n assignOrPrune(result, 'mcpServers', mcpServers);\n return result;\n },\n};\n\n/**\n * markdownlint-cli2 `ignores` merge — add safeword-owned dirs so a consuming\n * repo's markdownlint never flags safeword's generated agent docs (ticket #262,\n * extends the EYRK34 formatter-ignore family alongside prettier/biome/dprint/oxfmt).\n *\n * Why this and not `.markdownlintignore`: markdownlint-cli2 does NOT read\n * `.markdownlintignore` at all (it's a markdownlint-cli v1 file), and even when a\n * tool honors it, lint-staged passes explicit absolute file paths that bypass\n * ignore-file globbing entirely. The cli2 `ignores` array is the one mechanism\n * that filters files even when passed explicitly — verified against lint-staged's\n * default absolute-path invocation.\n *\n * Glob form (see the call below) prefixes each dir with a leading globstar,\n * unlike the bare trailing-globstar form used by dprint/oxfmt: lint-staged passes\n * absolute paths by default, and the leading globstar is required for the glob to\n * match `/abs/repo/.claude/...`. It also still matches the relative tree-glob and\n * relative-explicit invocations.\n *\n * `ignores` is a cli2-only option, so it lives solely in `.markdownlint-cli2.jsonc`\n * (the standard `.markdownlint.*` rule files have no `ignores` field). `skipIfMissing`\n * → only ever touches a config the customer already has, never imposes markdownlint.\n *\n * Limitation (shared by the sibling biome/dprint/oxfmt `.jsonc` merges): the merge\n * engine parses with `JSON.parse`, so a `.markdownlint-cli2.jsonc` that actually\n * uses comments parses as undefined and `skipIfMissing` makes this a safe no-op —\n * the ignores aren't added, but the customer's commented config is never clobbered.\n * Stripping comments to parse would round-trip the file through `JSON.stringify` and\n * destroy those comments, which is worse than the no-op; comment-preserving JSONC\n * editing is a future improvement for the whole merge engine, not this ticket.\n */\nconst MARKDOWNLINT_CLI2_IGNORES_MERGE = dirGlobExcludeMerge('ignores', dir => `**/${dir}/**`);\n\nconst CURSOR_RULE_WRAPPER_OWNED_FILES: Record<string, FileDefinition> = Object.fromEntries(\n CURSOR_RULE_WRAPPERS.map(wrapper => [\n `.cursor/rules/${wrapper.name}.mdc`,\n { template: `cursor/rules/${wrapper.name}.mdc` },\n ]),\n);\n\nconst CURSOR_COMMAND_WRAPPER_OWNED_FILES: Record<string, FileDefinition> = Object.fromEntries(\n CURSOR_COMMAND_WRAPPERS.map(wrapper => [\n `.cursor/commands/${wrapper.name}.md`,\n { template: `commands/${wrapper.name}.md` },\n ]),\n);\n\nconst CURSOR_SHARED_SKILL_FILES = [\n 'audit/SKILL.md',\n 'bdd/SKILL.md',\n 'bdd/DISCOVERY.md',\n 'bdd/PLAN_IMPLEMENTATION.md',\n 'bdd/SCENARIOS.md',\n 'bdd/TDD.md',\n 'bdd/DONE.md',\n 'bdd/SPLITTING.md',\n 'bdd/VERIFY.md',\n 'brainstorm/SKILL.md',\n 'cleanup-zombies/SKILL.md',\n 'closeout/SKILL.md',\n 'debug/SKILL.md',\n 'elicit/SKILL.md',\n 'explain/SKILL.md',\n 'figure-it-out/SKILL.md',\n 'finish-review/SKILL.md',\n 'finish-review/REVIEWER.md',\n 'lint/SKILL.md',\n 'quality-review/SKILL.md',\n 'refactor/SKILL.md',\n 'retro/SKILL.md',\n 'retro-filer/SKILL.md',\n 'review-spec/SKILL.md',\n 'self-review/SKILL.md',\n 'spike/SKILL.md',\n 'tdd-review/SKILL.md',\n 'testing/SKILL.md',\n 'ticket-system/SKILL.md',\n 'verify/SKILL.md',\n] as const;\n\nconst CURSOR_SHARED_SKILL_OWNED_FILES: Record<string, FileDefinition> = Object.fromEntries(\n CURSOR_SHARED_SKILL_FILES.map(path => [\n `.safeword/skills/${path}`,\n { template: `skills/${path}` },\n ]),\n);\n\nconst CURSOR_SHARED_SKILL_DIRS = [\n '.safeword/skills',\n ...new Set(CURSOR_SHARED_SKILL_FILES.map(path => `.safeword/skills/${path.split('/', 1)[0]}`)),\n];\n\nfunction skipCodexRuntimeAssetInstall(): undefined {\n return;\n}\n\nconst CODEX_RUNTIME_ASSET_FILENAMES = [\n 'pre-tool-quality.ts',\n 'pre-tool-quality-helpers.ts',\n 'post-tool-quality.ts',\n 'post-tool-skill-nudge.ts',\n 'stop.ts',\n] as const;\n\nconst CODEX_RUNTIME_ASSETS: Record<string, ManagedFileDefinition> = Object.fromEntries(\n CODEX_RUNTIME_ASSET_FILENAMES.map(file => [\n `.safeword/hooks/codex/${file}`,\n {\n dogfoodParity: true,\n template: `hooks/codex/${file}`,\n generator: skipCodexRuntimeAssetInstall,\n },\n ]),\n);\n\n// ============================================================================\n// SAFEWORD_SCHEMA - The Single Source of Truth\n// ============================================================================\n\n/**\n * Transient state files safeword's hooks write under the *resolved namespace\n * root* every turn, as root-relative names (quality-state, failure-counts,\n * skill-invocations, re-entry, dependency-readiness). Single source for both\n * the per-root `.gitignore` managed file (`NAMESPACE_GITIGNORE_CONTENT`) and the\n * repo-root `.gitignore` block (`SAFEWORD_TRANSIENT_PATHS`). Patterns are exact\n * filenames plus the one `quality-state*` glob — never a bare `*` — so durable\n * siblings (tickets/, learnings/, principles.md, personas.md, glossary.md,\n * surfaces.md) stay tracked.\n */\nconst NAMESPACE_TRANSIENT_BASENAMES: readonly string[] = [\n 'quality-state*.json',\n 'cursor-run-identity.json',\n 'codex-run-identity.json',\n 'failure-counts.json',\n 'skill-invocations.log',\n 're-entry.md',\n 'dependency-readiness.json',\n];\n\n/**\n * Runtime/transient state files safeword's hooks write to the working tree\n * every turn (update-cache plus the namespace-root state above). They must be\n * gitignored — and untracked on upgrade if a customer committed them before the\n * ignore rule existed — because the hooks read/write these paths directly, so\n * git tracking is never consulted. Single source for the repo-root managed\n * `.gitignore` block (below) and the upgrade-time untrack.\n *\n * Both well-known namespace roots are listed: hooks write transient state under\n * the resolved root (TAGWZ8), which is `.project/` on fresh installs and\n * `.safeword-project/` on legacy ones. A *custom* `paths.projectRoot` is covered\n * by the per-root `.gitignore` (`NAMESPACE_GITIGNORE_CONTENT`) instead, since a\n * static repo-root block cannot name an arbitrary root (issue #272).\n */\nexport const SAFEWORD_TRANSIENT_PATHS: readonly string[] = [\n '.safeword/.update-cache.json',\n '.safeword/retro-drafts/',\n '.safeword/self-reports/',\n '.safeword/boundary-audit.jsonl',\n // Native Claude plugin migration state. The plugin's UserPromptSubmit hook\n // writes these directly every session — plugin mode, the attention record it\n // dedupes advisories against, the per-session launch claims, and the durable\n // cleanup transaction. `attempts-v1/` in particular grows one file per Claude\n // session, so tracking it would commit churn to every customer repository.\n '.safeword/claude-plugin/',\n '.safeword/state/reviews/',\n ...['.project', '.safeword-project'].flatMap(root =>\n NAMESPACE_TRANSIENT_BASENAMES.map(name => `${root}/${name}`),\n ),\n];\n\n/**\n * Content of the `.gitignore` written *inside* the resolved namespace root\n * (issue #272). Because the ignore rules live with the files they describe, a\n * custom `paths.projectRoot` is handled for free — the file lands wherever the\n * root resolves, and git applies each pattern relative to that directory. The\n * legacy-prefixed managed-file key is remapped to the resolved root by\n * `withResolvedNamespaceRoot`.\n *\n * Patterns are leading-slash-anchored so they match only the transient files at\n * the namespace root — where the hooks write them — and never a same-named file\n * deeper in the tree (e.g. a `tickets/.../re-entry.md`). This mirrors the\n * repo-root block, whose `.project/re-entry.md` entries are likewise anchored.\n */\nconst NAMESPACE_GITIGNORE_PATTERNS = NAMESPACE_TRANSIENT_BASENAMES.map(name => `/${name}`).join(\n '\\n',\n);\nconst NAMESPACE_GITIGNORE_CONTENT = `# Safeword - transient session state (auto-managed)\\n${NAMESPACE_GITIGNORE_PATTERNS}\\n`;\n\n/**\n * Top-level dirs a customer's prettier must skip so safeword's files don't dirty\n * the tree on install. Derived from the single SAFEWORD_IGNORE_DIRS list (ticket\n * EYRK34) — wholesale dir excludes (not per-file INDEX lines), so all of\n * `.safeword/`, `.claude/`, `.cursor/`, `.codex/`, `.agents/` and both namespace\n * roots are covered — plus husky's generated `_` dir. Previously this listed only\n * the INDEX markdown under each root (TAGWZ8/1GGD28); the whole `.project/` is\n * safeword-generated, so excluding it wholesale also covers tickets and learnings.\n *\n * Resolved per-ctx (issue #293) so a custom `paths.projectRoot` is excluded too —\n * for the two well-known roots this is identical to the old static list, so a\n * default/legacy install's `.prettierignore` block is byte-identical (no churn).\n */\nfunction managedPrettierPaths(ctx: ProjectContext): string[] {\n return ['.husky/_', ...resolvedIgnoreDirectories(ctx).map(dir => `${dir}/`)];\n}\n\n// Header line of the managed .prettierignore block — also its marker (re-applied\n// and re-rendered against this exact string). The \"(owned dirs)\" suffix marks the\n// post-EYRK34 format; the stable prefix is what stale-config-scan detects.\nconst PRETTIER_EXCLUSIONS_HEADER = '# Safeword - managed prettier exclusions (owned dirs)';\n\n// Header line of the managed .gitattributes block — also its marker (re-applied and\n// re-rendered against this exact string). See GitHub #566 / ticket GA7T6M.\nconst GITATTRIBUTES_HEADER = '# Safeword - managed merge strategy for generated artifacts';\n\n/**\n * The managed `.gitattributes` block (issue #566): safeword's committed but\n * deterministically-regenerated artifacts — the architecture docs and the ticket index —\n * get `merge=union` so a local `git merge`/`rebase`/`pull` of the default branch\n * auto-resolves them instead of conflicting on the `fingerprint:` line + reconcile/stale\n * markers. `union` is a BUILT-IN driver (attribute-only, no `git config`), so it works on\n * any clone/CI from the committed file; the heal + `architecture --check` pipeline then\n * reconciles the union result to the correct content. `linguist-generated=true` collapses\n * their diffs and marks them generated on GitHub. Resolved per-ctx so a custom\n * `paths.projectRoot` ticket index is covered; the architecture-doc glob is root-agnostic.\n */\nfunction managedGitattributes(ctx: ProjectContext): string {\n const root = resolvedNamespaceRootLabel(ctx);\n return [\n GITATTRIBUTES_HEADER,\n '**/architecture.generated.md merge=union linguist-generated=true',\n `${root}/tickets/INDEX.md merge=union linguist-generated=true`,\n `${root}/tickets/INDEX-completed.md merge=union linguist-generated=true`,\n ].join('\\n');\n}\n\n/**\n * The starter BDD lane's full surface — files (the bddLaneFile entries\n * below), deps (typescriptPackages.conditional.scaffoldBddLane), and the\n * test:bdd script. The `safeword doctor` leftover-scaffold advisory\n * enumerates from these constants so its list can never drift from the\n * schema (ticket 56JCFZ, TB3.AC2).\n */\nexport const BDD_LANE_FILE_PATHS = [\n 'cucumber.mjs',\n 'features/safeword-lane.feature',\n 'steps/world.ts',\n 'steps/shared.steps.ts',\n] as const;\n\nexport const BDD_LANE_SCRIPT = 'test:bdd';\n\n/**\n * Starter-lane template entry, suppressed when the repo has its own cucumber\n * harness (ticket 56JCFZ, issue #645): the generator returns undefined so\n * reconcile skips the file entirely instead of scaffolding a competing lane.\n * Behaves exactly like `{ template }` when no harness is detected.\n */\nfunction bddLaneFile(templatePath: string): ManagedFileDefinition {\n const templateContent = (): string =>\n readFile(nodePath.join(getTemplatesDirectory(), templatePath));\n return {\n // `template` declares provenance for the schema↔templates contract;\n // the generator (which takes precedence) gates on harness detection.\n template: templatePath,\n generator: (ctx: ProjectContext): string | undefined =>\n ctx.projectType.scaffoldBddLane ? templateContent() : undefined,\n removeIfUnmodified: templateContent,\n };\n}\n\nfunction prReviewEnabled(cwd: string): boolean {\n const content = readFileSafe(nodePath.join(cwd, '.safeword', 'config.json'));\n if (content === undefined) return false;\n\n try {\n const config = JSON.parse(content) as { prReview?: { enabled?: unknown } };\n return config.prReview?.enabled === true;\n } catch {\n return false;\n }\n}\n\nfunction normalizePrReviewWorkflowVersionPins(content: string): string {\n const commandPrefix = 'npx --yes safeword@';\n const segments = content.split(commandPrefix);\n return segments\n .map((segment, index) => {\n if (index === 0) return segment;\n const end = segment.search(/\\s/u);\n if (end === -1) return segment;\n const version = segment.slice(0, end);\n const coreIdentifiers = version.split('-', 1)[0]?.split('.') ?? [];\n const isSemver =\n coreIdentifiers.length === 3 &&\n coreIdentifiers.every(identifier => {\n const numeric = Number(identifier);\n return Number.isSafeInteger(numeric) && numeric >= 0 && String(numeric) === identifier;\n });\n return isSemver ? `__SAFEWORD_VERSION__${segment.slice(end)}` : segment;\n })\n .join(commandPrefix);\n}\n\nfunction prReviewWorkflowFile(templatePath: string): ManagedFileDefinition {\n const workflowContent = (): string =>\n readFile(nodePath.join(getTemplatesDirectory(), templatePath))\n .split('__SAFEWORD_VERSION__')\n .join(VERSION);\n\n return {\n template: templatePath,\n generator: (ctx: ProjectContext): string | undefined =>\n prReviewEnabled(ctx.cwd) ? workflowContent() : undefined,\n normalizeForUnmodifiedComparison: normalizePrReviewWorkflowVersionPins,\n removeIfUnmodified: workflowContent,\n removeWhenGeneratorOmitted: true,\n };\n}\n\n// Filing invariants shared word-for-word by the retro and self-report-filing\n// guides (#801). Both carry guide-specific nuance around them, so the guides\n// can't be collapsed into one — instead the shared bullets are contract-pinned\n// in both files below. Single-line bullets on purpose: contracts match exact\n// substrings, so a rewrap would break the check.\nconst SHARED_FILING_INVARIANTS = [\n '- **Autonomous** — no human approval; sanitization + dedup + caps are the safeguards, not a human gate.',\n \"- **Upstream only** — `ArcadeAI/safeword`, never the host project's tracker.\",\n '- **Code owns egress** — nothing leaves beyond what the sanitized output contains.',\n];\n\n// One session-id → safe-token rule (FG6V57): triage (public ledger JSON), the\n// retro draft spool (filename), and self-report (local records) each reduce an\n// attacker-influenceable session id to a bare bounded token. The spool module is\n// deliberately self-contained (node:* only), so the rule is pinned byte-identical\n// across the three files instead of shared via import — edit them together.\n// (Keep this string minimal: an equivalent-but-reformatted regex fails the pin.)\nconst SESSION_TOKEN_RULE = [String.raw`.replaceAll(/[^\\w.-]/g, '_').slice(0, 80) || 'unknown'`];\n\n/** Marker substring identifying a boundary-gate shim line (ZJMZ50). */\nconst BOUNDARY_SHIM_MARKER = '# Safeword boundary gate';\n\n/**\n * The armored boundary-gate invocation, shared by every hook manager so the\n * safety semantics can never drift: the `[ -x … ]` guard keeps a fresh clone\n * (no node_modules) silent, and `|| true` keeps a crashing gate from blocking\n * (warn-only contract, TB1.R4). Husky appends it via the textPatch below;\n * the lefthook/pre-commit snippets interpolate it in hook-nudge.ts.\n */\nexport function boundaryShimCommand(at: 'commit' | 'push'): string {\n return `[ -x node_modules/.bin/safeword ] && node_modules/.bin/safeword boundary --at ${at} || true`;\n}\n\n/**\n * One-line boundary-gate shim for a husky hook file (see the textPatches\n * entry comment for the full design rationale). The marker is a trailing sh\n * comment on the command line itself, so the block IS the marker line.\n */\nfunction boundaryShimPatch(at: 'commit' | 'push'): TextPatchDefinition {\n return {\n operation: 'append',\n content: `${boundaryShimCommand(at)} ${BOUNDARY_SHIM_MARKER}: warn-only; removed by \\`safeword uninstall --agents=none\\`\\n`,\n marker: BOUNDARY_SHIM_MARKER,\n rerender: true,\n // A hook file that setup alone created holds nothing but the shim after\n // unpatch — delete it rather than leave an empty husk (TB1.R5).\n removeFileIfContentEquals: ['', '\\n'],\n when: ctx => ctx.hookManager === 'husky',\n };\n}\n\n/** The canonical schema is plugin-only for Codex. */\nexport const SAFEWORD_SCHEMA: SafewordSchema = {\n version: VERSION,\n codexMigration: CODEX_MIGRATION_SCHEMA,\n\n // Directories fully owned by safeword (created on setup, deleted on reset)\n ownedDirs: [\n '.safeword',\n '.safeword/hooks',\n '.safeword/hooks/cursor',\n '.safeword/hooks/lib',\n '.safeword/guides',\n '.safeword/templates',\n '.safeword/prompts',\n '.safeword/scripts',\n '.safeword/statusline',\n ...CURSOR_SHARED_SKILL_DIRS,\n '.cursor',\n '.cursor/rules',\n '.cursor/commands',\n '.cursor/agents',\n ],\n\n // Directories we add to but don't own (not deleted on reset)\n sharedDirs: [\n '.claude',\n '.claude/skills',\n '.claude/commands',\n // Custom-agent homes (GH628F): users keep their own agents in this dir —\n // add-to, never own. Codex is plugin-only and receives no project scaffold.\n '.claude/agents',\n // Project-owned namespace directories. Safeword creates these when missing,\n // but uninstall must leave the resolved namespace byte-for-byte untouched.\n '.safeword-project/learnings',\n '.safeword-project/tickets',\n '.safeword-project/tickets/completed',\n '.safeword-project/tmp',\n ],\n\n // Runtime data directories removed on reset only when empty.\n preservedDirs: [\n '.safeword/logs',\n // Runtime cloud-filing spool (BNGK9W) — per-session drafts + nudge markers the\n // retro writes at runtime; user/runtime data the schema does not own.\n '.safeword/retro-drafts',\n // Runtime self-report capture (already in SAFEWORD_TRANSIENT_PATHS /\n // gitignore) — per-session JSONL the hooks write; without this entry the\n // schema-drift test fails for any session that recorded a signal.\n '.safeword/self-reports',\n // Authored collisions retained during automatic .safeword-project → .project\n // migration. Recovery copies are user data, not deployed framework assets.\n '.safeword/namespace-migration-conflicts-v1',\n ],\n\n // Files to delete on upgrade (renamed or removed in newer versions)\n deprecatedFiles: [\n '.safeword/templates/user-stories-template.md',\n // Consolidated into planning-guide.md and testing-guide.md (v0.8.0)\n '.safeword/guides/development-workflow.md',\n '.safeword/guides/tdd-best-practices.md',\n '.safeword/guides/user-story-guide.md',\n '.safeword/guides/test-definitions-guide.md',\n // Boundaries config now project-specific (v0.9.0)\n '.safeword/eslint-boundaries.config.mjs',\n // Shell hooks replaced with TypeScript/Bun (v0.13.0)\n '.safeword/hooks/session-verify-agents.sh',\n // Replaced by session-safeword-context.ts (P30CRP): safeword no longer edits AGENTS.md.\n '.safeword/hooks/session-verify-agents.ts',\n '.safeword/hooks/session-version.sh',\n '.safeword/hooks/session-lint-check.sh',\n '.safeword/hooks/prompt-timestamp.sh',\n '.safeword/hooks/prompt-questions.sh',\n '.safeword/hooks/post-tool-lint.sh',\n '.safeword/hooks/stop-quality.sh',\n '.safeword/hooks/cursor/after-file-edit.sh',\n '.safeword/hooks/cursor/stop.sh',\n // Shell libraries no longer needed with Bun\n '.safeword/lib/common.sh',\n '.safeword/lib/jq-fallback.sh',\n // Skill renamed from enforcing-tdd to tdd-enforcing (v0.16.0)\n '.claude/skills/safeword-enforcing-tdd/SKILL.md',\n '.cursor/rules/safeword-enforcing-tdd.mdc',\n // TDD skill and command removed - BDD skill includes full TDD in the implement phase (v0.16.0)\n '.claude/skills/safeword-tdd-enforcing/SKILL.md',\n '.cursor/rules/safeword-tdd-enforcing.mdc',\n '.claude/commands/tdd.md',\n '.cursor/commands/tdd.md',\n // BDD skill split into phase files (v0.16.0)\n '.cursor/rules/safeword-bdd-orchestrating.mdc',\n '.safeword/commands/tdd.md',\n // Brainstorming skill: old safeword-* skill removed (v0.16.0); cursor rule was\n // re-introduced under same name (PR #103) for the new short-name brainstorm skill,\n // so .cursor/rules/safeword-brainstorming.mdc is no longer deprecated.\n '.claude/skills/safeword-brainstorming/SKILL.md',\n // Writing-plans skill removed - redundant with BDD decomposition + Claude Code native plan mode (v0.16.0)\n '.claude/skills/safeword-writing-plans/SKILL.md',\n '.cursor/rules/safeword-writing-plans.mdc',\n // Skills renamed from safeword-* to short names, overlapping commands removed (v0.17.0)\n '.claude/skills/safeword-debugging/SKILL.md',\n '.claude/skills/safeword-quality-reviewing/SKILL.md',\n '.claude/skills/safeword-refactoring/SKILL.md',\n '.claude/skills/safeword-bdd-orchestrating/SKILL.md',\n '.claude/skills/safeword-bdd-orchestrating/DISCOVERY.md',\n '.claude/skills/safeword-bdd-orchestrating/SCENARIOS.md',\n '.claude/skills/safeword-bdd-orchestrating/DECOMPOSITION.md',\n '.claude/skills/safeword-bdd-orchestrating/TDD.md',\n '.claude/skills/safeword-bdd-orchestrating/DONE.md',\n '.claude/skills/safeword-bdd-orchestrating/SPLITTING.md',\n // Command for debug was never a standalone command before v0.17.0 (always a skill)\n // /done renamed to /verify (v0.20.0)\n '.claude/commands/done.md',\n '.cursor/commands/done.md',\n // Shim commands removed — skills auto-create /slash-commands (v0.22.0)\n '.claude/commands/bdd.md',\n '.claude/commands/debug.md',\n '.claude/commands/quality-review.md',\n '.claude/commands/refactor.md',\n '.claude/commands/testing.md',\n // CLI reference inlined into SAFEWORD.md (v0.28.0, #112h)\n '.safeword/guides/cli-reference.md',\n // Commands converted to skills (ticket 038)\n '.claude/commands/lint.md',\n '.claude/commands/verify.md',\n '.claude/commands/audit.md',\n '.claude/commands/cleanup-zombies.md',\n '.safeword/.gherkin-lintrc',\n // Merged into session-auto-upgrade.ts — check + apply now run in one pass (XQ9CXA)\n '.safeword/hooks/session-update-check.ts',\n // Legacy Codex assets are intentionally absent. Generic maintenance must\n // preserve them until explicit, proof-gated migration finalization.\n ],\n\n // Packages to uninstall on upgrade (now bundled in safeword/eslint or replaced)\n deprecatedPackages: [\n // Individual ESLint plugins now bundled in safeword/eslint\n '@eslint/js',\n 'eslint-plugin-import-x',\n 'eslint-import-resolver-typescript',\n 'eslint-plugin-sonarjs',\n 'eslint-plugin-unicorn',\n 'eslint-plugin-boundaries', // replaced by dependency-cruiser\n 'eslint-plugin-playwright',\n 'eslint-plugin-promise',\n 'eslint-plugin-regexp',\n 'eslint-plugin-jsdoc',\n 'eslint-plugin-simple-import-sort',\n 'eslint-plugin-security',\n // Conditional ESLint plugins now in safeword\n 'typescript-eslint',\n 'eslint-plugin-react',\n 'eslint-plugin-react-hooks',\n 'eslint-plugin-jsx-a11y',\n '@next/eslint-plugin-next',\n 'eslint-plugin-astro',\n 'gherkin-lint',\n ],\n\n // Directories to delete on upgrade (no longer managed by safeword)\n deprecatedDirs: [\n '.safeword/lib', // Shell libraries no longer needed with Bun (v0.13.0)\n '.safeword/planning', // Moved to .safeword-project/tickets/ (v0.16.0)\n '.safeword/tickets', // Moved to .safeword-project/tickets/ (v0.16.0)\n '.claude/skills/safeword-enforcing-tdd', // Renamed to safeword-tdd-enforcing (v0.16.0)\n '.claude/skills/safeword-tdd-enforcing', // Removed - BDD includes TDD (v0.16.0)\n '.claude/skills/safeword-brainstorming', // Removed - BDD discovery phase covers this (v0.16.0)\n '.claude/skills/safeword-writing-plans', // Removed - redundant with BDD + native plan mode (v0.16.0)\n // Skills renamed from safeword-* to short names (v0.17.0)\n '.claude/skills/safeword-debugging',\n '.claude/skills/safeword-quality-reviewing',\n '.claude/skills/safeword-refactoring',\n '.claude/skills/safeword-bdd-orchestrating',\n // Legacy Codex skill directories remain until explicit migration finalization.\n ],\n\n // Files owned by safeword (overwritten on upgrade if content changed)\n // (bddLaneFile entries: see the helper defined above the schema)\n ownedFiles: {\n // BDD acceptance lane config (ticket 102b) — safeword-owned; the lane's\n // working files (features/, steps/) are customer-owned in managedFiles.\n // Suppressed when the repo has its own cucumber harness (56JCFZ).\n 'cucumber.mjs': bddLaneFile('cucumber/cucumber.mjs'),\n // Note: knip.json is in typescriptManagedFiles (with context-aware ignoreDependencies)\n\n // Core files\n '.safeword/AGENTS.md': { template: 'AGENTS.md' },\n '.safeword/SAFEWORD.md': { template: 'SAFEWORD.md' },\n '.safeword/version': { content: () => VERSION },\n // config.json is created by packs system but needs to be registered for cleanup on uninstall\n // Generator returns undefined = never created/updated by schema, but still deleted on uninstall\n '.safeword/config.json': { generator: (): undefined => undefined },\n\n // Language-specific safeword configs for hooks (extend project configs if they exist)\n ...typescriptOwnedFiles,\n ...pythonOwnedFiles,\n ...golangOwnedFiles,\n ...rustOwnedFiles,\n ...sqlOwnedFiles,\n\n // Hooks - Bash (no Bun dependency, must run before Bun hooks)\n '.safeword/hooks/session-bun-check.sh': {\n template: 'hooks/session-bun-check.sh',\n },\n '.safeword/hooks/lib/audit-scope.sh': {\n template: 'hooks/lib/audit-scope.sh',\n },\n '.safeword/hooks/resolve-namespace-root.ts': {\n template: 'hooks/resolve-namespace-root.ts',\n },\n '.safeword/hooks/resolve-verify-ticket.ts': {\n template: 'hooks/resolve-verify-ticket.ts',\n },\n '.safeword/hooks/resolve-project-knowledge.ts': {\n template: 'hooks/resolve-project-knowledge.ts',\n },\n '.safeword/hooks/audit-principle-trace.ts': {\n template: 'hooks/audit-principle-trace.ts',\n },\n '.safeword/hooks/record-skill-invocation.ts': {\n template: 'hooks/record-skill-invocation.ts',\n },\n '.safeword/hooks/run-review.ts': {\n template: 'hooks/run-review.ts',\n },\n\n // Hooks shared library - TypeScript with Bun runtime\n '.safeword/hooks/lib/active-ticket.ts': { template: 'hooks/lib/active-ticket.ts' },\n '.safeword/hooks/lib/feature-provenance.ts': { template: 'hooks/lib/feature-provenance.ts' },\n '.safeword/hooks/lib/inspiration.ts': { template: 'hooks/lib/inspiration.ts' },\n '.safeword/hooks/lib/markdown-structure.ts': { template: 'hooks/lib/markdown-structure.ts' },\n '.safeword/hooks/lib/architecture-document-nudge.ts': {\n template: 'hooks/lib/architecture-document-nudge.ts',\n },\n '.safeword/hooks/lib/architecture-staged-scope.ts': {\n template: 'hooks/lib/architecture-staged-scope.ts',\n },\n '.safeword/hooks/lib/branch-staleness.ts': { template: 'hooks/lib/branch-staleness.ts' },\n '.safeword/hooks/lib/blocked-on-gate.ts': { template: 'hooks/lib/blocked-on-gate.ts' },\n '.safeword/hooks/lib/closeout-binding.ts': { template: 'hooks/lib/closeout-binding.ts' },\n '.safeword/hooks/lib/cursor-run-identity.ts': {\n template: 'hooks/lib/cursor-run-identity.ts',\n },\n '.safeword/hooks/lib/cursor-state.ts': {\n template: 'hooks/lib/cursor-state.ts',\n },\n '.safeword/hooks/lib/git-operation.ts': { template: 'hooks/lib/git-operation.ts' },\n '.safeword/hooks/lib/re-entry.ts': { template: 'hooks/lib/re-entry.ts' },\n '.safeword/hooks/lib/hierarchy.ts': { template: 'hooks/lib/hierarchy.ts' },\n '.safeword/hooks/lib/lint.ts': { template: 'hooks/lib/lint.ts' },\n '.safeword/hooks/lib/host-toolchain.ts': { template: 'hooks/lib/host-toolchain.ts' },\n '.safeword/hooks/lib/quality.ts': { template: 'hooks/lib/quality.ts' },\n '.safeword/hooks/lib/quality-state.ts': { template: 'hooks/lib/quality-state.ts' },\n '.safeword/hooks/lib/run-identity.ts': { template: 'hooks/lib/run-identity.ts' },\n '.safeword/hooks/lib/dependency-readiness.ts': {\n template: 'hooks/lib/dependency-readiness.ts',\n },\n '.safeword/hooks/lib/done-gate.ts': { template: 'hooks/lib/done-gate.ts' },\n '.safeword/hooks/lib/jsonl-spool.ts': { template: 'hooks/lib/jsonl-spool.ts' },\n '.safeword/hooks/lib/namespace-root.ts': { template: 'hooks/lib/namespace-root.ts' },\n '.safeword/hooks/lib/drain-retro-spool.ts': { template: 'hooks/lib/drain-retro-spool.ts' },\n '.safeword/hooks/lib/retro-draft-spool.ts': { template: 'hooks/lib/retro-draft-spool.ts' },\n '.safeword/hooks/lib/retro-debug.ts': { template: 'hooks/lib/retro-debug.ts' },\n '.safeword/hooks/lib/retro-extract.ts': { template: 'hooks/lib/retro-extract.ts' },\n '.safeword/hooks/lib/retro-filing-gate.ts': { template: 'hooks/lib/retro-filing-gate.ts' },\n '.safeword/hooks/lib/retro-nudge.ts': { template: 'hooks/lib/retro-nudge.ts' },\n '.safeword/hooks/lib/retro-trigger.ts': { template: 'hooks/lib/retro-trigger.ts' },\n '.safeword/hooks/lib/self-report.ts': { template: 'hooks/lib/self-report.ts' },\n '.safeword/hooks/lib/skill-invocation-log.ts': {\n template: 'hooks/lib/skill-invocation-log.ts',\n },\n '.safeword/hooks/lib/parse-annotation.ts': { template: 'hooks/lib/parse-annotation.ts' },\n '.safeword/hooks/lib/jtbd.ts': { template: 'hooks/lib/jtbd.ts' },\n '.safeword/hooks/lib/phase-provenance.ts': { template: 'hooks/lib/phase-provenance.ts' },\n '.safeword/hooks/lib/impl-plan.ts': { template: 'hooks/lib/impl-plan.ts' },\n '.safeword/hooks/lib/project-knowledge.ts': { template: 'hooks/lib/project-knowledge.ts' },\n '.safeword/hooks/lib/principle-trace.ts': { template: 'hooks/lib/principle-trace.ts' },\n '.safeword/hooks/lib/plan-gate.ts': { template: 'hooks/lib/plan-gate.ts' },\n '.safeword/hooks/lib/replan-relevance.ts': { template: 'hooks/lib/replan-relevance.ts' },\n '.safeword/hooks/lib/replan.ts': { template: 'hooks/lib/replan.ts' },\n '.safeword/hooks/lib/review-ledger.ts': { template: 'hooks/lib/review-ledger.ts' },\n '.safeword/hooks/lib/lint-config.ts': { template: 'hooks/lib/lint-config.ts' },\n '.safeword/hooks/lib/typecheck-gate.ts': { template: 'hooks/lib/typecheck-gate.ts' },\n '.safeword/hooks/lib/checkbox-transitions.ts': {\n template: 'hooks/lib/checkbox-transitions.ts',\n },\n '.safeword/hooks/lib/shell-segments.ts': { template: 'hooks/lib/shell-segments.ts' },\n '.safeword/hooks/lib/bash-ledger-writes.ts': {\n template: 'hooks/lib/bash-ledger-writes.ts',\n },\n '.safeword/hooks/lib/process-kill-guard.ts': {\n template: 'hooks/lib/process-kill-guard.ts',\n },\n '.safeword/hooks/lib/work-log-stamp.ts': {\n template: 'hooks/lib/work-log-stamp.ts',\n },\n '.safeword/hooks/lib/review-trigger.ts': { template: 'hooks/lib/review-trigger.ts' },\n '.safeword/hooks/lib/dogfood.ts': { template: 'hooks/lib/dogfood.ts' },\n '.safeword/hooks/lib/ledger-git.ts': { template: 'hooks/lib/ledger-git.ts' },\n '.safeword/hooks/lib/ledger-validation.ts': { template: 'hooks/lib/ledger-validation.ts' },\n '.safeword/hooks/lib/scenario-format.ts': { template: 'hooks/lib/scenario-format.ts' },\n '.safeword/hooks/lib/skill-nudge.ts': { template: 'hooks/lib/skill-nudge.ts' },\n '.safeword/hooks/lib/test-runner.ts': { template: 'hooks/lib/test-runner.ts' },\n '.safeword/hooks/lib/auto-upgrade.ts': { template: 'hooks/lib/auto-upgrade.ts' },\n '.safeword/hooks/lib/auto-upgrade-lock.ts': { template: 'hooks/lib/auto-upgrade-lock.ts' },\n '.safeword/hooks/lib/safeword-context.ts': { template: 'hooks/lib/safeword-context.ts' },\n '.safeword/hooks/lib/update-cache.ts': { template: 'hooks/lib/update-cache.ts' },\n '.safeword/hooks/lib/version.ts': { template: 'hooks/lib/version.ts' },\n '.safeword/hooks/lib/learning-verification-stamps.ts': {\n template: 'hooks/lib/learning-verification-stamps.ts',\n },\n '.safeword/hooks/lib/readiness-pointer.ts': { template: 'hooks/lib/readiness-pointer.ts' },\n\n // Generated at setup/upgrade from SAFEWORD_SCHEMA itself — the prefix list\n // the auto-upgrade hook uses to decide which files to stage. See owned-paths.ts.\n '.safeword/hooks/lib/owned-paths.ts': {\n generator: (ctx): string =>\n generateOwnedPathsModule(SAFEWORD_SCHEMA, resolvedNamespaceDirectory(ctx)),\n },\n\n // Hooks - TypeScript with Bun runtime\n '.safeword/hooks/session-safeword-context.ts': {\n template: 'hooks/session-safeword-context.ts',\n },\n '.safeword/hooks/session-reply-format.ts': {\n template: 'hooks/session-reply-format.ts',\n },\n '.safeword/hooks/session-codex-start.ts': {\n template: 'hooks/session-codex-start.ts',\n // Packaged by the CLI for native Codex dispatch, but no longer copied\n // into projects. Existing copies are legacy migration inputs.\n generator: (): undefined => undefined,\n },\n '.safeword/hooks/session-cursor-auto-upgrade.ts': {\n template: 'hooks/session-cursor-auto-upgrade.ts',\n },\n '.safeword/hooks/session-dependency-readiness.ts': {\n template: 'hooks/session-dependency-readiness.ts',\n },\n '.safeword/hooks/dependency-bootstrap.ts': {\n template: 'hooks/dependency-bootstrap.ts',\n },\n '.safeword/hooks/session-version.ts': {\n template: 'hooks/session-version.ts',\n },\n '.safeword/hooks/session-lint-check.ts': {\n template: 'hooks/session-lint-check.ts',\n },\n '.safeword/hooks/session-architecture-heal.ts': {\n template: 'hooks/session-architecture-heal.ts',\n },\n '.safeword/hooks/session-author-model.ts': {\n template: 'hooks/session-author-model.ts',\n },\n '.safeword/hooks/session-compact-context.ts': {\n template: 'hooks/session-compact-context.ts',\n },\n '.safeword/hooks/prompt-timestamp.ts': {\n template: 'hooks/prompt-timestamp.ts',\n },\n '.safeword/hooks/prompt-questions.ts': {\n template: 'hooks/prompt-questions.ts',\n },\n '.safeword/hooks/prompt-retro-nudge.ts': {\n template: 'hooks/prompt-retro-nudge.ts',\n },\n '.safeword/hooks/post-tool-lint.ts': {\n template: 'hooks/post-tool-lint.ts',\n },\n '.safeword/hooks/post-tool-quality.ts': {\n template: 'hooks/post-tool-quality.ts',\n },\n '.safeword/hooks/post-tool-skill-nudge.ts': {\n template: 'hooks/post-tool-skill-nudge.ts',\n },\n '.safeword/hooks/post-tool-bypass-warn.ts': {\n template: 'hooks/post-tool-bypass-warn.ts',\n },\n '.safeword/hooks/pre-tool-quality.ts': {\n template: 'hooks/pre-tool-quality.ts',\n },\n '.safeword/hooks/pre-tool-architecture-stage.ts': {\n template: 'hooks/pre-tool-architecture-stage.ts',\n },\n '.safeword/hooks/pre-tool-stale-main.ts': {\n template: 'hooks/pre-tool-stale-main.ts',\n },\n '.safeword/hooks/write-review-stamp.ts': {\n template: 'hooks/write-review-stamp.ts',\n },\n '.safeword/hooks/pre-tool-config-guard.ts': {\n template: 'hooks/pre-tool-config-guard.ts',\n },\n '.safeword/hooks/pre-tool-dependency-readiness.ts': {\n template: 'hooks/pre-tool-dependency-readiness.ts',\n },\n '.safeword/hooks/post-tool-dependency-readiness.ts': {\n template: 'hooks/post-tool-dependency-readiness.ts',\n },\n '.safeword/hooks/pre-tool-git-bare-fix.sh': {\n template: 'hooks/pre-tool-git-bare-fix.sh',\n },\n '.safeword/hooks/session-auto-upgrade.ts': {\n template: 'hooks/session-auto-upgrade.ts',\n },\n '.safeword/hooks/session-cleanup-quality.ts': {\n template: 'hooks/session-cleanup-quality.ts',\n },\n '.safeword/hooks/stop-quality.ts': { template: 'hooks/stop-quality.ts' },\n '.safeword/hooks/stop-reentry.ts': { template: 'hooks/stop-reentry.ts' },\n '.safeword/hooks/stop-retro-filing.ts': { template: 'hooks/stop-retro-filing.ts' },\n '.safeword/hooks/stop-retro.ts': { template: 'hooks/stop-retro.ts' },\n '.safeword/hooks/stop-self-report.ts': { template: 'hooks/stop-self-report.ts' },\n '.safeword/hooks/session-start-reentry.ts': {\n template: 'hooks/session-start-reentry.ts',\n },\n '.safeword/statusline/reentry.ts': {\n template: 'statusline/reentry.ts',\n },\n '.safeword/hooks/post-tool-sync-learnings.ts': {\n template: 'hooks/post-tool-sync-learnings.ts',\n },\n '.safeword/hooks/post-tool-work-log.ts': {\n template: 'hooks/post-tool-work-log.ts',\n },\n\n // Retro filer subagent (GH628F, issue #628): the filing procedure lives in\n // the agent definition, dispatched by the per-harness stop gates. One\n // markdown source serves Claude and Cursor; Codex takes TOML. OWNED (not\n // managed): overwritten on upgrade and removed on reset — a leftover copy\n // would keep `.cursor/` alive after reset.\n '.claude/agents/safeword-retro-filer.md': { template: 'agents/safeword-retro-filer.md' },\n '.cursor/agents/safeword-retro-filer.md': { template: 'agents/safeword-retro-filer.md' },\n '.claude/agents/safeword-reviewer.md': { template: 'agents/safeword-reviewer.md' },\n '.cursor/agents/safeword-reviewer.md': { template: 'agents/safeword-reviewer.md' },\n\n // Guides\n '.safeword/guides/architecture-guide.md': {\n template: 'guides/architecture-guide.md',\n },\n '.safeword/guides/self-report-filing.md': {\n template: 'guides/self-report-filing.md',\n },\n '.safeword/guides/retro.md': {\n template: 'guides/retro.md',\n },\n '.safeword/guides/cold-start-check.md': {\n template: 'guides/cold-start-check.md',\n },\n '.safeword/guides/context-files-guide.md': {\n template: 'guides/context-files-guide.md',\n },\n '.safeword/guides/data-architecture-guide.md': {\n template: 'guides/data-architecture-guide.md',\n },\n '.safeword/guides/design-doc-guide.md': {\n template: 'guides/design-doc-guide.md',\n },\n '.safeword/guides/learning-extraction.md': {\n template: 'guides/learning-extraction.md',\n },\n '.safeword/guides/llm-writing-guide.md': {\n template: 'guides/llm-writing-guide.md',\n },\n '.safeword/guides/llm-evals-guide.md': {\n template: 'guides/llm-evals-guide.md',\n },\n '.safeword/guides/planning-guide.md': {\n template: 'guides/planning-guide.md',\n },\n '.safeword/guides/skill-eval-optimization-guide.md': {\n template: 'guides/skill-eval-optimization-guide.md',\n },\n '.safeword/guides/testing-guide.md': {\n template: 'guides/testing-guide.md',\n },\n '.safeword/guides/verification-lanes-guide.md': {\n template: 'guides/verification-lanes-guide.md',\n },\n '.safeword/guides/zombie-process-cleanup.md': {\n template: 'guides/zombie-process-cleanup.md',\n },\n\n // Templates\n '.safeword/templates/architecture-template.md': {\n template: 'doc-templates/architecture-template.md',\n },\n '.safeword/templates/design-doc-template.md': {\n template: 'doc-templates/design-doc-template.md',\n },\n '.safeword/templates/task-spec-template.md': {\n template: 'doc-templates/task-spec-template.md',\n },\n '.safeword/templates/test-definitions-feature.md': {\n template: 'doc-templates/test-definitions-feature.md',\n },\n '.safeword/templates/ticket-template.md': {\n template: 'doc-templates/ticket-template.md',\n },\n '.safeword/templates/feature-spec-template.md': {\n template: 'doc-templates/feature-spec-template.md',\n },\n // Per-ticket impl-plan.md scaffold (ticket XDNSZA) — authored at\n // the plan-implementation phase, validated by the stop hook's impl-plan gate.\n '.safeword/templates/impl-plan-template.md': {\n template: 'doc-templates/impl-plan-template.md',\n },\n '.safeword/templates/adr-template.md': {\n template: 'doc-templates/adr-template.md',\n },\n '.safeword/templates/work-log-template.md': {\n template: 'doc-templates/work-log-template.md',\n },\n // Upstream-workaround tripwire scaffold (issue #1907) — header runbook +\n // pinned-version test, emitted when a workaround's removal depends on\n // someone else's release. See guides/testing-guide.md.\n '.safeword/templates/tripwire-template.md': {\n template: 'doc-templates/tripwire-template.md',\n },\n // Per-ticket spec.md scaffold (ticket Y2HCNJ). ticket-writer reads this\n // from the bundled templates dir when scaffolding a feature's spec.md;\n // deployed here so it joins the other artifact templates and stays in the\n // schema's ownedFiles manifest.\n '.safeword/templates/spec-template.md': {\n template: 'spec-template.md',\n },\n\n // Prompts\n '.safeword/prompts/architecture.md': {\n template: 'prompts/architecture.md',\n },\n\n // Scripts\n '.safeword/scripts/bisect-test-pollution.sh': {\n template: 'scripts/bisect-test-pollution.sh',\n },\n '.safeword/scripts/bisect-zombie-processes.sh': {\n template: 'scripts/bisect-zombie-processes.sh',\n },\n '.safeword/scripts/cleanup-zombies.sh': {\n template: 'scripts/cleanup-zombies.sh',\n },\n '.safeword/scripts/closeout-cleanup.ts': {\n template: 'scripts/closeout-cleanup.ts',\n },\n\n // Host-neutral skill materialization retained for Cursor's thin wrappers.\n // Claude loads the same canonical sources from its native plugin cache.\n ...CURSOR_SHARED_SKILL_OWNED_FILES,\n\n // Claude skills (short names, auto-trigger + explicit invocation)\n '.claude/skills/debug/SKILL.md': {\n template: 'skills/debug/SKILL.md',\n },\n '.claude/skills/quality-review/SKILL.md': {\n template: 'skills/quality-review/SKILL.md',\n },\n '.claude/skills/finish-review/SKILL.md': {\n template: 'skills/finish-review/SKILL.md',\n },\n '.claude/skills/finish-review/REVIEWER.md': {\n template: 'skills/finish-review/REVIEWER.md',\n },\n '.claude/skills/refactor/SKILL.md': {\n template: 'skills/refactor/SKILL.md',\n },\n '.claude/skills/testing/SKILL.md': {\n template: 'skills/testing/SKILL.md',\n },\n '.claude/skills/bdd/SKILL.md': {\n template: 'skills/bdd/SKILL.md',\n },\n '.claude/skills/bdd/DISCOVERY.md': {\n template: 'skills/bdd/DISCOVERY.md',\n },\n '.claude/skills/bdd/PLAN_IMPLEMENTATION.md': {\n template: 'skills/bdd/PLAN_IMPLEMENTATION.md',\n },\n '.claude/skills/bdd/SCENARIOS.md': {\n template: 'skills/bdd/SCENARIOS.md',\n },\n '.claude/skills/bdd/TDD.md': {\n template: 'skills/bdd/TDD.md',\n },\n '.claude/skills/bdd/DONE.md': {\n template: 'skills/bdd/DONE.md',\n },\n '.claude/skills/bdd/SPLITTING.md': {\n template: 'skills/bdd/SPLITTING.md',\n },\n '.claude/skills/bdd/VERIFY.md': {\n template: 'skills/bdd/VERIFY.md',\n },\n '.claude/skills/ticket-system/SKILL.md': {\n template: 'skills/ticket-system/SKILL.md',\n },\n // Claude skills — action commands with disable-model-invocation\n // Skills auto-create /slash-commands, so separate commands are unnecessary\n '.claude/skills/lint/SKILL.md': { template: 'skills/lint/SKILL.md' },\n '.claude/skills/verify/SKILL.md': { template: 'skills/verify/SKILL.md' },\n '.claude/skills/closeout/SKILL.md': { template: 'skills/closeout/SKILL.md' },\n '.claude/skills/audit/SKILL.md': { template: 'skills/audit/SKILL.md' },\n '.claude/skills/explain/SKILL.md': { template: 'skills/explain/SKILL.md' },\n '.claude/skills/self-review/SKILL.md': { template: 'skills/self-review/SKILL.md' },\n '.claude/skills/review-spec/SKILL.md': {\n template: 'skills/review-spec/SKILL.md',\n },\n '.claude/skills/cleanup-zombies/SKILL.md': {\n template: 'skills/cleanup-zombies/SKILL.md',\n },\n '.claude/skills/retro/SKILL.md': { template: 'skills/retro/SKILL.md' },\n '.claude/skills/spike/SKILL.md': { template: 'skills/spike/SKILL.md' },\n '.claude/skills/retro-filer/SKILL.md': {\n template: 'skills/retro-filer/SKILL.md',\n },\n // Claude skills — contextual (auto-triggered, no slash command)\n '.claude/skills/brainstorm/SKILL.md': {\n template: 'skills/brainstorm/SKILL.md',\n },\n '.claude/skills/elicit/SKILL.md': {\n template: 'skills/elicit/SKILL.md',\n },\n '.claude/skills/tdd-review/SKILL.md': {\n template: 'skills/tdd-review/SKILL.md',\n },\n '.claude/skills/figure-it-out/SKILL.md': {\n template: 'skills/figure-it-out/SKILL.md',\n },\n\n // Cursor rules — generated from wrapper metadata; physical files stay installed.\n ...CURSOR_RULE_WRAPPER_OWNED_FILES,\n\n // Cursor commands (Cursor needs explicit commands for all action capabilities)\n ...CURSOR_COMMAND_WRAPPER_OWNED_FILES,\n '.cursor/commands/explain.md': { template: 'commands/explain.md' },\n '.cursor/commands/verify.md': { template: 'commands/verify.md' },\n '.cursor/commands/self-review.md': { template: 'commands/self-review.md' },\n '.cursor/commands/review-spec.md': { template: 'commands/review-spec.md' },\n '.cursor/commands/cleanup-zombies.md': {\n template: 'commands/cleanup-zombies.md',\n },\n '.cursor/commands/lint.md': { template: 'commands/lint.md' },\n\n // Cursor hooks adapters - TypeScript with Bun runtime\n '.safeword/hooks/cursor/after-file-edit.ts': {\n template: 'hooks/cursor/after-file-edit.ts',\n },\n '.safeword/hooks/cursor/gate-adapter.ts': {\n template: 'hooks/cursor/gate-adapter.ts',\n },\n '.safeword/hooks/cursor/pre-tool-quality.ts': {\n template: 'hooks/cursor/pre-tool-quality.ts',\n },\n '.safeword/hooks/cursor/before-shell-execution.ts': {\n template: 'hooks/cursor/before-shell-execution.ts',\n },\n '.safeword/hooks/cursor/post-tool-quality.ts': {\n template: 'hooks/cursor/post-tool-quality.ts',\n },\n '.safeword/hooks/cursor/post-tool-skill-nudge.ts': {\n template: 'hooks/cursor/post-tool-skill-nudge.ts',\n },\n '.safeword/hooks/cursor/stop.ts': { template: 'hooks/cursor/stop.ts' },\n },\n\n // Files created if missing, updated only if content matches current template\n managedFiles: {\n // Project-root audit config is scaffolded when absent, then customer-owned.\n // Full uninstall removes it only while it still matches Safeword's template.\n '.jscpd.json': { template: '.jscpd.json' },\n\n // Package-owned Codex runtime adapters. Their generator intentionally\n // returns undefined: the plugin CLI executes them from the npm package,\n // never from a customer repository.\n ...CODEX_RUNTIME_ASSETS,\n\n // BDD acceptance lane working files (ticket 102b) — scaffolded once; the\n // customer owns them thereafter (created if missing, updated only while\n // still safeword's template content). The lane config (cucumber.mjs) is\n // safeword-owned in ownedFiles. All suppressed when the repo has its own\n // cucumber harness (56JCFZ).\n 'features/safeword-lane.feature': bddLaneFile('cucumber/safeword-lane.feature'),\n 'steps/world.ts': bddLaneFile('cucumber/world.ts'),\n 'steps/shared.steps.ts': bddLaneFile('cucumber/shared.steps.ts'),\n\n // Default-off advisory PR review. Customers may customize managed workflow\n // files after setup; reconciliation updates only unchanged template content.\n '.github/workflows/safeword-pr-review.yml': prReviewWorkflowFile('workflows/pr-review.yml'),\n '.github/workflows/safeword-pr-review-publisher.yml': prReviewWorkflowFile(\n 'workflows/pr-review-publisher.yml',\n ),\n '.github/workflows/safeword-pr-review-worker.yml': prReviewWorkflowFile(\n 'workflows/pr-review-worker.yml',\n ),\n\n // TypeScript/JavaScript managed files (ESLint, tsconfig, Knip, Prettier configs)\n ...typescriptManagedFiles,\n // Python managed files (ruff.toml, mypy.ini, .importlinter)\n ...pythonManagedFiles,\n // Go managed files (.golangci.yml)\n ...golangManagedFiles,\n // Rust managed files (clippy.toml, rustfmt.toml, deny.toml)\n ...rustManagedFiles,\n // SQL managed files (.sqlfluff)\n ...sqlManagedFiles,\n\n // Project principles — scaffolded once; users author the small, durable set\n // that guides product and technical decisions. Like personas, a configured\n // paths.principles override suppresses the default scaffold uniformly.\n '.safeword-project/principles.md': {\n template: 'principles-template.md',\n configKey: 'principles',\n },\n\n // Project personas — scaffolded once with format header + commented example;\n // user authors real persona blocks thereafter (safeword reads, never overwrites\n // user content). See ticket 7YN5QB. `configKey: 'personas'` lets the user\n // redirect via `paths.personas` in .safeword/config.json — when set, reconcile\n // skips this entry uniformly (see ticket K7N2QM).\n '.safeword-project/personas.md': {\n template: 'personas-template.md',\n configKey: 'personas',\n },\n\n // Project glossary — scaffolded once with format header + commented example;\n // user authors real term blocks thereafter (safeword reads/validates, never\n // overwrites user content). See ticket YR6C49. `configKey: 'glossary'` lets\n // the user redirect via `paths.glossary` in .safeword/config.json — when set,\n // reconcile skips this entry uniformly (see ticket K7N2QM).\n '.safeword-project/glossary.md': {\n template: 'glossary-template.md',\n configKey: 'glossary',\n },\n\n // Project feature surfaces — scaffolded once with format header + commented\n // example; user authors real surface blocks thereafter. `configKey:\n // 'surfaces'` lets the user redirect via `paths.surfaces` in\n // .safeword/config.json, matching the personas/glossary contract.\n '.safeword-project/surfaces.md': {\n template: 'surfaces-template.md',\n configKey: 'surfaces',\n },\n\n // Per-root `.gitignore` for hook-written transient state. The legacy-prefixed\n // key is remapped to the resolved namespace root by withResolvedNamespaceRoot,\n // so a custom `paths.projectRoot` ignores its own transient files even though\n // the static repo-root block can't name that root (issue #272). Created once;\n // the repo-root block stays the belt-and-suspenders for `.project/`/legacy.\n '.safeword-project/.gitignore': {\n content: NAMESPACE_GITIGNORE_CONTENT,\n },\n },\n\n // JSON files where we merge specific keys\n jsonMerges: {\n // TypeScript/JavaScript JSON merges (package.json, .prettierrc, biome.json)\n ...typescriptJsonMerges,\n\n // Language-agnostic JSON merges\n '.claude/settings.json': {\n keys: ['hooks'],\n merge: existing => {\n // Preserve non-safeword hooks while adding/updating safeword hooks\n const existingHooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n const mergedHooks: Record<string, unknown[]> = { ...existingHooks };\n\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const eventHooks = mergedHooks[event] ?? [];\n const withoutCurrentHooks = filterOutEquivalentSafewordHooks(eventHooks, newHooks);\n const nonSafewordHooks = filterOutSafewordHooks(\n withoutCurrentHooks,\n acceptedHistoricalHookEntries(event),\n );\n mergedHooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks: mergedHooks };\n },\n unmerge: existing => {\n // Remove only safeword hooks, preserve custom hooks\n const existingHooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n const cleanedHooks: Record<string, unknown[]> = {};\n\n for (const [event, eventHooks] of Object.entries(existingHooks)) {\n const withoutCurrentHooks = filterOutEquivalentSafewordHooks(\n eventHooks,\n SETTINGS_HOOKS[event as keyof typeof SETTINGS_HOOKS] ?? [],\n );\n const nonSafewordHooks = filterOutSafewordHooks(\n withoutCurrentHooks,\n acceptedHistoricalHookEntries(event),\n );\n if (nonSafewordHooks.length > 0) {\n cleanedHooks[event] = nonSafewordHooks;\n }\n }\n\n const result = { ...existing };\n assignOrPrune(result, 'hooks', cleanedHooks);\n return result;\n },\n },\n\n '.mcp.json': MCP_JSON_MERGE,\n '.cursor/mcp.json': MCP_JSON_MERGE,\n\n // markdownlint-cli2 ignores - hide safeword's generated agent docs from a\n // consuming repo's markdown lint hooks (ticket #262). cli2's only JSON config\n // form is `.jsonc`; yaml/cjs/mjs variants fall back to manual wiring.\n '.markdownlint-cli2.jsonc': MARKDOWNLINT_CLI2_IGNORES_MERGE,\n\n '.cursor/hooks.json': {\n keys: [\n 'version',\n 'hooks.sessionStart',\n 'hooks.preToolUse',\n 'hooks.beforeShellExecution',\n 'hooks.afterFileEdit',\n 'hooks.postToolUse',\n 'hooks.stop',\n ],\n removeFileIfEmpty: true,\n merge: existing => {\n const existingHooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n const hooks: Record<string, unknown[]> = { ...existingHooks };\n for (const [event, newHooks] of Object.entries(CURSOR_HOOKS)) {\n const eventHooks = hooks[event] ?? [];\n const nonSafewordHooks = filterOutEquivalentSafewordHooks(eventHooks, newHooks);\n hooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n return {\n ...existing,\n version: 1, // Required by Cursor\n hooks,\n };\n },\n unmerge: existing => {\n const result = { ...existing };\n const existingHooks = (existing.hooks as Record<string, unknown[]>) ?? {};\n\n // Keep only hooks safeword did NOT install. Filtering entries instead of\n // whole events preserves user-authored Cursor hooks that share an event\n // with safeword, such as `sessionStart`.\n const hooks = Object.fromEntries(\n Object.entries(existingHooks)\n .map(\n ([name, eventHooks]) =>\n [\n name,\n filterOutEquivalentSafewordHooks(\n eventHooks,\n CURSOR_HOOKS[name as keyof typeof CURSOR_HOOKS] ?? [],\n ),\n ] as const,\n )\n .filter(([, eventHooks]) => eventHooks.length > 0),\n );\n\n // `version` is only meaningful while safeword's hooks remain; drop it\n // alongside an emptied hooks container.\n if (!assignOrPrune(result, 'hooks', hooks)) {\n delete result.version;\n }\n\n return result;\n },\n },\n },\n\n // Text files where we patch specific content\n textPatches: {\n // Boundary-gate shims for husky hosts (ZJMZ50, #810 child 2). One line,\n // all logic in the versioned CLI: explicit .bin path (husky's PATH\n // prepend is relative — worktree-unsafe, 9P3VVH), existence-guarded\n // (fresh clones), whole-line `|| true` (husky runs hooks with sh -e, so\n // an unguarded failure would BLOCK the commit — TB1.R4). The marker rides\n // the line as a trailing comment, which makes the whole block the marker\n // line: rerender then heals ANY future line change in place\n // (rerenderBlockLines excludes marker lines). Gated to the husky world —\n // lefthook/pre-commit/bare hosts get a printed nudge instead, and\n // `when` never gates unpatch, so reset still strips after a migration.\n '.husky/pre-commit': boundaryShimPatch('commit'),\n '.husky/pre-push': boundaryShimPatch('push'),\n '.gitignore': {\n operation: 'append',\n content: `\\n# Safeword - Local cache and transient state\\n${SAFEWORD_TRANSIENT_PATHS.join('\\n')}\\n`,\n // A stable header plus rerender heals the block whenever the transient\n // path list grows and lets uninstall recognize older variants.\n rerender: true,\n // eslint-disable-next-line security/detect-non-literal-regexp -- derived only from the source-controlled constant above\n rerenderOwnedLinePattern: new RegExp(\n `^(?:${SAFEWORD_TRANSIENT_PATHS.map(path =>\n path.replaceAll(/[.*+?^${}()|[\\]\\\\]/gu, String.raw`\\$&`),\n ).join('|')})$`,\n 'u',\n ),\n marker: '# Safeword - Local cache and transient state',\n },\n // Prettier ignores: safeword owns the dot-directories in SAFEWORD_IGNORE_DIRS\n // (.safeword/, .claude/, .cursor/, .codex/, .agents/, and both namespace\n // roots). Without this, `prettier --write .` reformats hooks, Cursor/Codex\n // rules, and the generated ticket/learning markdown — churning the tree on\n // install (ticket EYRK34). Biome/eslint exclude these via their own configs.\n //\n // The marker carries an \"(owned dirs)\" suffix the pre-EYRK34 block lacks, so an\n // existing install re-applies on upgrade and picks up the broadened excludes\n // (.codex/ + wholesale namespace roots). The stable\n // \"# Safeword - managed prettier exclusions\" header substring is preserved so\n // stale-config-scan still detects the block.\n '.prettierignore': {\n operation: 'append',\n // ctx factory + rerender (issue #293): a custom paths.projectRoot is excluded,\n // and the block re-renders in place on upgrade for an existing custom-root\n // install. Default/legacy output is byte-identical, so those installs no-op.\n content: ctx => `\\n${PRETTIER_EXCLUSIONS_HEADER}\\n${managedPrettierPaths(ctx).join('\\n')}\\n`,\n rerender: true,\n marker: PRETTIER_EXCLUSIONS_HEADER,\n replacementAfterUnpatch: ctx =>\n `\\n# Project namespace exclusions (preserved after Safeword uninstall)\\n${durableNamespaceDirectories(\n ctx,\n )\n .map(dir => `${dir}/`)\n .join('\\n')}\\n`,\n },\n '.gitattributes': {\n // ctx factory + rerender (issue #566), same shape as .prettierignore: a custom\n // paths.projectRoot resolves into the ticket-index paths, and the block re-renders\n // in place on upgrade. Default/legacy output is byte-identical, so those installs\n // no-op. Appended (marker-delimited) so a consumer's own .gitattributes is preserved.\n // No leading newline (unlike .prettierignore): prettier-plugin-sh DOES format\n // .gitattributes and rejects a leading blank line on a fresh install; existing\n // content separates via its own trailing newline.\n operation: 'append',\n content: ctx => `${managedGitattributes(ctx)}\\n`,\n rerender: true,\n rerenderOwnedLinePattern:\n /^(?:\\*\\*\\/architecture\\.generated\\.md|.+\\/tickets\\/INDEX(?:-completed)?\\.md) merge=union linguist-generated=true$/u,\n marker: GITATTRIBUTES_HEADER,\n },\n },\n\n // Cleanup-only text patches. Safeword used to prepend these blocks to\n // customer-owned context files; P30CRP moved SAFEWORD.md delivery to\n // safeword-owned hooks.\n legacyTextPatches: {\n 'AGENTS.md': {\n operation: 'prepend',\n content: AGENTS_MD_LINK,\n marker: '.safeword/SAFEWORD.md',\n },\n 'CLAUDE.md': {\n operation: 'prepend',\n content: CLAUDE_MD_IMPORT_BLOCK,\n marker: '@./.safeword/SAFEWORD.md',\n },\n },\n\n // Content predicate parity — files that must contain specific strings.\n // Different from ownedFiles (which requires byte equality between two files):\n // contracts assert one-way \"this file must include these tokens\" invariants.\n // Used by runParity() in src/parity.ts for both release tests and pre-commit\n // (see ticket 144). Path key = file relative to repo root.\n contracts: {\n 'packages/cli/src/retro/triage.ts': {\n // Shared session-token rule (FG6V57) — see SESSION_TOKEN_RULE above.\n requires: SESSION_TOKEN_RULE,\n },\n 'packages/cli/templates/hooks/lib/retro-draft-spool.ts': {\n // Shared session-token rule (FG6V57); the .safeword mirror follows via pairs.\n requires: SESSION_TOKEN_RULE,\n },\n 'packages/cli/templates/hooks/lib/self-report.ts': {\n // Shared session-token rule (FG6V57); sanitizeToken stays for non-session fields.\n requires: SESSION_TOKEN_RULE,\n },\n 'packages/cli/templates/guides/self-report-filing.md': {\n // Shared filing invariants with retro.md (#801): the two guides carried\n // independently hand-tuned Rules blocks that silently forked. The exact\n // bullet text below must appear in BOTH guides — edit them together.\n requires: SHARED_FILING_INVARIANTS,\n },\n 'packages/cli/templates/guides/retro.md': {\n // See self-report-filing.md contract above (#801).\n requires: SHARED_FILING_INVARIANTS,\n },\n 'packages/cli/templates/hooks/lib/quality.ts': {\n // Cursor's stop hook imports QUALITY_REVIEW_MESSAGE. The grammar, renderer,\n // and evaluator exports keep proactive wording and Stop validation coupled;\n // requiring individual labels here would create a second grammar source.\n requires: [\n 'QUALITY_REVIEW_MESSAGE',\n // prompt-questions.ts imports all three pre-response pointers; the Stop\n // header composes the bare rule inline. Removing any one forks the\n // reply-shape vocabulary back across two hooks.\n 'REPLY_FORMAT_LEAD_RULE',\n 'REPLY_FORMAT_LEAD',\n 'REPLY_FORMAT_REMINDER',\n 'DECISION_BRIEF_GRAMMAR',\n 'GENERIC_REVIEW_EVIDENCE',\n 'renderDecisionBriefContract',\n 'renderDecisionBriefCorrection',\n 'evaluateDecisionBriefCompliance',\n 'getQualityEvidence',\n ],\n },\n 'packages/cli/templates/doc-templates/test-definitions-feature.md': {\n // Canonical test-definitions.md format. Rule grouping (Gherkin 6+\n // Rule: keyword, Example Mapping alignment) + nested Scenario with\n // Given/When/Then + per-scenario RED/GREEN/REFACTOR sub-checkboxes.\n // The R/G/R checkboxes are load-bearing: parseTddStep in\n // hooks/lib/active-ticket.ts depends on them to inject TDD-step\n // guidance during the implement phase. Removing any marker silently\n // regresses the format the BDD skill teaches.\n requires: [\n '## Rule:',\n '### Scenario:',\n 'Given',\n 'When',\n 'Then',\n '- [ ] RED',\n '- [ ] GREEN',\n '- [ ] REFACTOR',\n ],\n },\n 'packages/cli/templates/hooks/lib/scenario-format.ts': {\n // Runtime gate that powers done-phase scenario-completeness checks\n // (stop-quality.ts) and progress reporting. analyzeScenarioFormat is\n // imported by the stop hook; isUnrecognized distinguishes \"no\n // scenarios yet\" from \"scenarios in legacy/malformed format\" so the\n // done gate can hard-block the latter. Removing either silently\n // regresses scenario-completeness enforcement.\n requires: ['analyzeScenarioFormat', 'isUnrecognized', 'export function'],\n },\n },\n\n // NPM packages to install (JS/TS specific packages from typescript pack)\n packages: typescriptPackages,\n};\n\nexport type ProjectSurface = 'core' | 'cursor';\n\nconst CURSOR_PROJECT_PATHS = new Set([\n '.safeword/hooks/lib/cursor-state.ts',\n '.safeword/hooks/session-cursor-auto-upgrade.ts',\n]);\n\nexport function isCursorProjectPath(path: string): boolean {\n return (\n path === '.cursor' ||\n path.startsWith('.cursor/') ||\n path === '.safeword/hooks/cursor' ||\n path.startsWith('.safeword/hooks/cursor/') ||\n CURSOR_PROJECT_PATHS.has(path)\n );\n}\n\nfunction withoutCursorEntries<T>(values: Record<string, T>): Record<string, T> {\n return Object.fromEntries(Object.entries(values).filter(([path]) => !isCursorProjectPath(path)));\n}\n\n/** Select project-owned surfaces without scattering Cursor path filters through commands. */\nexport function schemaForProjectSurfaces(\n schema: SafewordSchema,\n surfaces: readonly ProjectSurface[],\n): SafewordSchema {\n if (surfaces.includes('cursor')) return schema;\n return {\n ...schema,\n ownedDirs: schema.ownedDirs.filter(path => !isCursorProjectPath(path)),\n sharedDirs: schema.sharedDirs.filter(path => !isCursorProjectPath(path)),\n preservedDirs: schema.preservedDirs.filter(path => !isCursorProjectPath(path)),\n deprecatedFiles: schema.deprecatedFiles.filter(path => !isCursorProjectPath(path)),\n deprecatedDirs: schema.deprecatedDirs.filter(path => !isCursorProjectPath(path)),\n ownedFiles: withoutCursorEntries(schema.ownedFiles),\n managedFiles: withoutCursorEntries(schema.managedFiles),\n jsonMerges: withoutCursorEntries(schema.jsonMerges),\n textPatches: withoutCursorEntries(schema.textPatches),\n legacyTextPatches: withoutCursorEntries(schema.legacyTextPatches),\n contracts: withoutCursorEntries(schema.contracts),\n };\n}\n","/**\n * Go Language Pack - Schema Definitions\n *\n * All Go-specific file definitions and config generators.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n *\n * Mirrors the structure of typescript/files.ts and python/files.ts for consistency.\n */\n\nimport { readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport YAML from 'yaml';\n\nimport { exists } from '../../utils/fs.js';\nimport { warn } from '../../utils/output.js';\nimport type { FileDefinition, ManagedFileDefinition } from '../types.js';\n\n// ============================================================================\n// Shared Configuration Constants\n// ============================================================================\n\n/**\n * Curated linter set — covers bugs, security, complexity, and modern Go\n * without the upgrade-breakage of `default: all`.\n *\n * Convention: `default: standard` + explicit `enable` list.\n * Used by golangci-lint itself, Docker, Prometheus, and the maratori golden config.\n */\nconst SAFEWORD_ENABLE = [\n // Bug detection\n 'bodyclose', // unclosed HTTP response bodies\n 'nilerr', // returning nil after checking err != nil\n 'sqlclosecheck', // unclosed sql.Rows/Stmt\n 'rowserrcheck', // unchecked sql.Rows.Err()\n 'noctx', // HTTP requests without context\n 'reassign', // reassigning top-level variables\n\n // Code quality\n 'cyclop', // cyclomatic complexity (max 10 default)\n 'nestif', // nested if depth\n 'gocognit', // cognitive complexity\n 'unconvert', // unnecessary type conversions\n 'unparam', // unused function parameters\n 'usestdlibvars', // use stdlib constants (http.StatusOK, etc.)\n 'wastedassign', // wasted variable assignments\n 'prealloc', // preallocate slice capacity\n\n // Security\n 'gosec', // security issues\n\n // Style (low noise)\n 'goconst', // repeated strings -> constants\n 'misspell', // spelling in comments/strings\n 'errorlint', // error wrapping conventions\n 'gocritic', // opinionated but high-signal\n 'lll', // keep code and comments readable at the shared 100-column limit\n 'godoclint', // enforce readable Go documentation without requiring docs everywhere\n 'revive', // flexible metalinter\n 'perfsprint', // faster string formatting\n 'copyloopvar', // loop variable capture\n 'intrange', // integer range loops\n];\n\n/**\n * Linters to disable (too strict for LLM enforcement).\n *\n * We intentionally do NOT list any `wsl`/`wsl_v5` whitespace linter here: it is\n * not part of `default: standard`, so disabling it is a no-op in the standalone\n * config, and naming the deprecated `wsl` would draw a `golangci-lint config\n * verify` deprecation notice. A customer who explicitly enables `wsl_v5` keeps\n * their choice in merge scenarios.\n */\nconst SAFEWORD_DISABLE = [\n 'forbidigo', // blocks all print statements\n];\n\n/** Exclusion presets for common false positives */\nconst SAFEWORD_EXCLUSION_PRESETS = ['std-error-handling', 'common-false-positives'];\n\n/** Safeword linter settings (only fills gaps — customer settings win) */\nconst SAFEWORD_SETTINGS: Record<string, Record<string, unknown>> = {\n nestif: { 'min-complexity': 4 },\n lll: { 'line-length': 100 },\n gocritic: { commentedOutCode: { minLength: 50 } },\n godoclint: {\n default: 'none',\n enable: ['max-len'],\n options: { 'max-len': { length: 100 } },\n },\n};\n\n// ============================================================================\n// Config Generators\n// ============================================================================\n\n/**\n * Build standalone golangci-lint v2 config object.\n *\n * We deliberately pin NO Go language version (no `run.go`): golangci-lint\n * derives the analysis target from the project's `go.mod` `go` directive, so a\n * pin here could only diverge from — and suppress analysis relative to — the\n * user's real Go version. Deriving from `go.mod` is correct and zero-maintenance.\n */\nfunction buildStandaloneConfig(): Record<string, unknown> {\n return {\n version: '2',\n linters: {\n default: 'standard',\n enable: [...SAFEWORD_ENABLE],\n disable: [...SAFEWORD_DISABLE],\n exclusions: { presets: [...SAFEWORD_EXCLUSION_PRESETS] },\n settings: structuredClone(SAFEWORD_SETTINGS),\n },\n formatters: { enable: ['gofumpt'] },\n };\n}\n\n/**\n * Generate golangci-lint v2 configuration for project root.\n * Created only when no existing config exists.\n */\nfunction generateGolangciConfig(): string {\n const header = `# Generated by safeword - DO NOT EDIT\n# To customize, add overrides below the formatters section\n\n`;\n return header + YAML.stringify(buildStandaloneConfig());\n}\n\n/**\n * Generate safeword golangci-lint configuration for .safeword/.golangci.yml.\n *\n * If project has existing config, merges additively (preserves customer choices).\n * Handles both v1 and v2 config formats.\n */\nfunction generateSafewordGolangciConfig(existingConfig: string | undefined, cwd: string): string {\n if (!existingConfig) {\n return getSafewordGolangciStandalone();\n }\n\n const configPath = nodePath.join(cwd, existingConfig);\n if (!exists(configPath)) {\n return getSafewordGolangciStandalone();\n }\n\n try {\n const projectContent = readFileSync(configPath, 'utf8');\n const projectConfig = YAML.parse(projectContent) as Record<string, unknown> | undefined;\n\n if (!projectConfig) {\n return getSafewordGolangciStandalone();\n }\n\n const merge =\n projectConfig.version === '2' ? getSafewordGolangciMergedV2 : getSafewordGolangciMergedV1;\n return merge(projectConfig);\n } catch {\n warn(`Safeword: Could not parse ${existingConfig}, using standalone config`);\n return getSafewordGolangciStandalone();\n }\n}\n\n/** Standalone safeword golangci-lint config (v2 format) */\nfunction getSafewordGolangciStandalone(): string {\n const header = `# Safeword golangci-lint config - standalone (no project config to merge)\n# Used by hooks for LLM enforcement.\n\n`;\n return header + YAML.stringify(buildStandaloneConfig());\n}\n\n// ============================================================================\n// Additive Merge Helpers\n// ============================================================================\n\n/** Additively merge the v2 linters section — preserves customer choices. */\nfunction mergeLinters(projectLinters: Record<string, unknown>): Record<string, unknown> {\n const customerExclusions = (projectLinters.exclusions as Record<string, unknown>) || {};\n const customerPresets = (customerExclusions.presets as string[]) || [];\n const customerSettings = (projectLinters.settings as Record<string, unknown>) || {};\n\n return {\n ...projectLinters,\n default: projectLinters.default || 'standard',\n enable: unionArrays((projectLinters.enable as string[]) || [], SAFEWORD_ENABLE),\n disable: unionArrays((projectLinters.disable as string[]) || [], SAFEWORD_DISABLE),\n exclusions: {\n ...customerExclusions,\n presets: unionArrays(customerPresets, SAFEWORD_EXCLUSION_PRESETS),\n },\n settings: fillGapMerge(customerSettings, SAFEWORD_SETTINGS),\n };\n}\n\n/** Additively merge formatters — append gofumpt if missing. */\nfunction mergeFormatters(projectFormatters: Record<string, unknown>): Record<string, unknown> {\n const enable = (projectFormatters.enable as string[]) || [];\n return {\n ...projectFormatters,\n enable: enable.includes('gofumpt') ? enable : [...enable, 'gofumpt'],\n };\n}\n\n// ============================================================================\n// Additive Merge (v2)\n// ============================================================================\n\n/** Merge project v2 config with safeword rules — additive, customer preserved. */\nfunction getSafewordGolangciMergedV2(projectConfig: Record<string, unknown>): string {\n const merged: Record<string, unknown> = {\n ...projectConfig,\n version: '2',\n linters: mergeLinters((projectConfig.linters as Record<string, unknown>) || {}),\n formatters: mergeFormatters((projectConfig.formatters as Record<string, unknown>) || {}),\n };\n\n const header = `# Safeword golangci-lint config - merged with project config\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# Re-run \\`safeword install\\` to regenerate after project config changes.\n#\n# NOTE: Safeword adds linters via 'enable' without changing your 'default'.\n# Your disable list and exclusion presets are preserved (union, not replaced).\n\n`;\n\n return header + YAML.stringify(merged);\n}\n\n// ============================================================================\n// Additive Merge (v1 — legacy)\n// ============================================================================\n\n/** Merge project v1 config — additive, customer preserved. Outputs v1 format. */\nfunction getSafewordGolangciMergedV1(projectConfig: Record<string, unknown>): string {\n const merged: Record<string, unknown> = { ...projectConfig };\n const projectLinters = (projectConfig.linters as Record<string, unknown>) || {};\n\n merged.linters = {\n ...projectLinters,\n enable: unionArrays((projectLinters.enable as string[]) || [], SAFEWORD_ENABLE),\n disable: unionArrays((projectLinters.disable as string[]) || [], SAFEWORD_DISABLE),\n };\n\n const projectSettings = (projectConfig['linters-settings'] as Record<string, unknown>) || {};\n merged['linters-settings'] = fillGapMerge(projectSettings, {\n nestif: { 'min-complexity': 4 },\n gofumpt: { 'extra-rules': true },\n });\n\n const header = `# Safeword golangci-lint config - merged with project config (v1 format)\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# NOTE: Consider migrating to golangci-lint v2 format.\n# Re-run \\`safeword install\\` to regenerate after project config changes.\n\n`;\n\n return header + YAML.stringify(merged);\n}\n\n// ============================================================================\n// Merge Utilities\n// ============================================================================\n\n/** Union two string arrays, preserving order (base first), deduplicated. */\nfunction unionArrays(base: string[], additions: string[]): string[] {\n const set = new Set(base);\n for (const item of additions) {\n set.add(item);\n }\n return [...set];\n}\n\n/**\n * Fill-gap merge: base (customer) wins on conflict, override fills missing keys.\n * For nested objects, recurses. Arrays are NOT replaced — customer's win.\n */\nfunction fillGapMerge(\n base: Record<string, unknown>,\n defaults: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n\n for (const [key, defaultValue] of Object.entries(defaults)) {\n if (Object.hasOwn(result, key)) {\n const existingValue = result[key];\n if (\n defaultValue &&\n typeof defaultValue === 'object' &&\n !Array.isArray(defaultValue) &&\n existingValue &&\n typeof existingValue === 'object' &&\n !Array.isArray(existingValue)\n ) {\n result[key] = fillGapMerge(\n existingValue as Record<string, unknown>,\n defaultValue as Record<string, unknown>,\n );\n }\n } else {\n result[key] = defaultValue;\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\nexport const golangOwnedFiles: Record<string, FileDefinition> = {\n '.safeword/.golangci.yml': {\n generator: ctx =>\n ctx.languages?.golang\n ? generateSafewordGolangciConfig(ctx.projectType.existingGolangciConfig, ctx.cwd)\n : undefined,\n },\n};\n\n// ============================================================================\n// Managed Files (create if missing, update if matches template)\n// ============================================================================\n\nexport const golangManagedFiles: Record<string, ManagedFileDefinition> = {\n '.golangci.yml': {\n generator: ctx =>\n !ctx.projectType.existingGolangciConfig && ctx.languages?.golang\n ? generateGolangciConfig()\n : undefined,\n },\n};\n","/**\n * Python Language Pack - Schema Definitions\n *\n * All Python-specific file definitions and config generators.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n *\n * Mirrors the structure of typescript/files.ts and golang/files.ts for consistency.\n *\n * Note: Generator functions return `string | undefined` by design (undefined = skip file).\n * This conflicts with sonarjs/no-inconsistent-returns vs unicorn/no-useless-undefined (#1199).\n * Inline disables target only generator arrow functions, not regular functions in this file.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport type { FileDefinition, ManagedFileDefinition } from '../types.js';\nimport { detectPythonLayers, detectRootPackage, detectSolePackage } from './setup.js';\n\n/**\n * Detect a pre-138 legacy ruff.toml layout where the customer's project-level\n * ruff.toml extends `.safeword/ruff.toml`. In that case, making `.safeword/ruff.toml`\n * also extend `../ruff.toml` would create a circular configuration (ruff errors).\n *\n * We keep those projects on the old standalone shape until the customer migrates\n * by removing the `extend = \".safeword/ruff.toml\"` line from their ruff.toml.\n */\nfunction hasLegacyCustomerRuffExtend(cwd: string): boolean {\n const ruffPath = nodePath.join(cwd, 'ruff.toml');\n if (!existsSync(ruffPath)) return false;\n try {\n const content = readFileSync(ruffPath, 'utf8');\n return content.includes('extend = \".safeword/ruff.toml\"');\n } catch {\n return false;\n }\n}\n\n// ============================================================================\n// Shared Ruff Configuration\n// ============================================================================\n\n/**\n * Curated rule set — covers correctness, security, and modern Python\n * without the upgrade-breakage and noise of select = [\"ALL\"].\n *\n * See: https://docs.astral.sh/ruff/linter/ (ruff maintainers discourage ALL)\n */\nconst SAFEWORD_RULES = [\n '\"E\"', // pycodestyle errors\n '\"F\"', // Pyflakes\n '\"W\"', // pycodestyle warnings\n '\"I\"', // isort\n '\"UP\"', // pyupgrade\n '\"B\"', // flake8-bugbear\n '\"SIM\"', // flake8-simplify\n '\"C4\"', // flake8-comprehensions\n '\"RUF\"', // ruff-specific rules\n '\"PTH\"', // pathlib enforcement\n '\"RET\"', // return statement checks\n '\"S\"', // bandit security\n '\"T20\"', // print statement detection\n '\"TC\"', // type-checking imports\n '\"ERA\"', // commented-out code (LLMs leave dead comments)\n '\"PERF\"', // performance anti-patterns\n '\"PLE\"', // pylint errors\n '\"PLW\"', // pylint warnings\n].join(', ');\n\nconst RUFF_SHARED_SETTINGS = `ignore = [\n \"COM812\", # missing trailing comma - conflicts with formatter\n \"ISC001\", # single-line-implicit-string-concatenation - conflicts with formatter\n]\n\n[lint.per-file-ignores]\n\"tests/**\" = [\"S101\"] # allow assert in tests\n\n[lint.mccabe]\nmax-complexity = 10`;\n\n/**\n * Standalone lint rules — used in legacy mode (pre-138 customers whose\n * ruff.toml contains `extend = \".safeword/ruff.toml\"`). Defines the full\n * baseline via `select`. Kept to avoid circular extends until the customer\n * migrates by removing that line.\n */\nconst RUFF_LINT_STANDALONE = `[lint]\nselect = [${SAFEWORD_RULES}]\n${RUFF_SHARED_SETTINGS}`;\n\n/**\n * Additive lint rules — used in `.safeword/ruff.toml` in unified mode (ticket 138),\n * which extends the project-level `ruff.toml`. Uses `extend-select` so customer's\n * `select` is preserved.\n */\nconst RUFF_LINT_ADDITIVE = `[lint]\nextend-select = [${SAFEWORD_RULES}]\n${RUFF_SHARED_SETTINGS}`;\n\n// ============================================================================\n// Config Generators for .safeword/ (ownedFiles)\n// ============================================================================\n\n/**\n * Generate Ruff configuration for .safeword/ruff.toml.\n *\n * If project has existing ruff config, extends it with stricter rules.\n * Otherwise generates standalone config.\n *\n * Used by hooks via --config .safeword/ruff.toml flag.\n *\n * @param existingRuffConfig - Path to existing config ('ruff.toml' or 'pyproject.toml'), or undefined\n */\nfunction generateRuffBaseConfig(\n cwd: string,\n existingRuffConfig: 'ruff.toml' | 'pyproject.toml' | undefined = 'ruff.toml',\n): string {\n // Ticket 138 migration: if customer's ruff.toml still contains the pre-138\n // line `extend = \".safeword/ruff.toml\"`, extending customer back would create\n // a circular configuration (ruff errors). Fall back to legacy standalone mode\n // for those projects until the customer removes that line.\n if (hasLegacyCustomerRuffExtend(cwd)) {\n return `# Safeword Ruff config — LEGACY standalone mode\n# Your project's ruff.toml still extends this file (pre-138 layout). To enable\n# the unified override contract (your rules win in the LLM hook too), remove\n# the line \\`extend = \".safeword/ruff.toml\"\\` from your ruff.toml and re-run\n# \\`safeword install\\`.\n\nline-length = 100\n\n${RUFF_LINT_STANDALONE}\n`;\n }\n\n // Unified (ticket 138): always extend from a project-level ruff config.\n // - If customer had pre-existing config (ruff.toml or [tool.ruff] in pyproject.toml), extend that file.\n // - If not, safeword generates a bare project-level ruff.toml (see generateProjectRuffConfig) — extend it.\n // This makes the LLM hook consistently honor customer overrides via ruff's native `extend`.\n return `# Safeword Ruff config - adds rules on top of project config\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# Re-run \\`safeword install\\` to regenerate after project config changes.\n#\n# NOTE: Uses extend-select (additive) so your select/ignore are preserved.\n# Known edge case: ruff may drop parent ignore rules when child uses\n# extend-select (https://github.com/astral-sh/ruff/issues/10622).\n# If a project ignore stops working, duplicate it in this file.\n\n# Inherit from project's ${existingRuffConfig}\nextend = \"../${existingRuffConfig}\"\n\nline-length = 100\n\n${RUFF_LINT_ADDITIVE}\n`;\n}\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\n/**\n * Python owned files for .safeword/ directory.\n * These are overwritten on upgrade if content changed.\n */\nexport const pythonOwnedFiles: Record<string, FileDefinition> = {\n // Ruff config for hooks (extends project config if exists)\n '.safeword/ruff.toml': {\n generator: ctx =>\n ctx.languages?.python\n ? generateRuffBaseConfig(ctx.cwd, ctx.projectType.existingRuffConfig)\n : undefined,\n },\n};\n\n// ============================================================================\n// Config Generators for project root (managedFiles)\n// ============================================================================\n\n/**\n * Generate a bare project-level ruff.toml (ticket 138).\n *\n * Customer-owned from the moment it's created: safeword never overwrites it.\n * The LLM hook's `.safeword/ruff.toml` extends this file via ruff's native\n * `extend` directive, so anything the customer adds here wins in both human\n * and LLM lint runs.\n */\nfunction generateProjectRuffConfig(): string {\n return `# Your project's Ruff config — customer-owned, safe to edit.\n# Your rules take precedence in the LLM hook (via .safeword/ruff.toml's extend).\n#\n# Add your rules below, e.g.:\n# [lint]\n# select = [\"E\", \"F\"]\n# ignore = [\"E501\"]\n`;\n}\n\n/**\n * Generate standalone mypy.ini for project root.\n * Strict config for LLM agents - enforces type annotations.\n */\nfunction generateProjectMypyConfig(): string {\n return `# Generated by safeword\n# Strict config for LLM agents - type annotations enforced.\n\n[mypy]\nstrict = True\nwarn_unreachable = True\nignore_missing_imports = True\nshow_error_codes = True\npretty = True\n`;\n}\n\n/**\n * Generate standalone .importlinter for project root.\n *\n * @param layers - Detected layers (e.g., ['domain', 'services', 'api'])\n * @param rootPackage - Root package name for the project\n */\nfunction generateProjectImportLinterConfig(layers: string[], rootPackage: string): string {\n if (layers.length < 2) return '';\n\n const layerList = layers.map(l => ` ${rootPackage}.${l}`).join('\\n');\n\n return `# Generated by safeword\n# Layer architecture contracts - enforces clean dependencies\n\n[importlinter]\nroot_packages = ${rootPackage}\n\n[importlinter:contract:layers]\nname = Layer architecture\ntype = layers\nlayers =\n${layerList}\n`;\n}\n\n/**\n * Generic circular-import guard for a single-package project (ticket V4MATC,\n * upstream #847) — the Python analog of depcruise's no-circular rule, emitted\n * when no layer structure is detected but the top-level package is unambiguous.\n * Exact syntax per import-linter 2.x docs: `type = acyclic_siblings`\n * (underscore), `ancestors` is required, `root_package` singular is the\n * documented single-package form.\n */\nfunction generateAcyclicImportLinterConfig(packageName: string): string {\n return `# Generated by safeword\n# Circular-import guard - forbids dependency cycles between sibling modules\n\n[importlinter]\nroot_package = ${packageName}\n\n[importlinter:contract:safeword-acyclic]\nname = No circular imports between sibling modules (safeword)\ntype = acyclic_siblings\nancestors = ${packageName}\n`;\n}\n\n// ============================================================================\n// Managed Files (create if missing, reconciled on reset/upgrade)\n// ============================================================================\n\nexport const pythonManagedFiles: Record<string, ManagedFileDefinition> = {\n // Project-level ruff config (created only if no existing ruff config)\n 'ruff.toml': {\n generator: ctx =>\n ctx.languages?.python && !ctx.projectType.existingRuffConfig\n ? generateProjectRuffConfig()\n : undefined,\n },\n\n // Project-level mypy config (created only if no existing mypy config)\n 'mypy.ini': {\n generator: ctx =>\n ctx.languages?.python && !ctx.projectType.existingMypyConfig\n ? generateProjectMypyConfig()\n : undefined,\n },\n\n // Project-level import-linter config (created when no existing config and either\n // layers are detected or the top-level package is unambiguous — ticket V4MATC)\n '.importlinter': {\n // eslint-disable-next-line sonarjs/no-inconsistent-returns -- generator returns undefined to skip file\n generator: ctx => {\n if (!ctx.languages?.python) return;\n if (ctx.projectType.existingImportLinterConfig) return;\n return importLinterScaffoldContent(ctx.cwd);\n },\n // Default reset removes the file only when it byte-equals the scaffold we\n // would generate — gate-free content source, since at reset time the\n // scaffold's own existence trips the existing-config gate above (V4MATC R4).\n // If the project layout changed since scaffolding (package renamed, layers\n // appeared) or line endings were converted (e.g. autocrlf), the regenerated\n // content won't byte-match and the file survives reset — intentional:\n // never delete what we can't prove we wrote.\n removeIfUnmodified: ctx =>\n ctx.languages?.python ? importLinterScaffoldContent(ctx.cwd) : undefined,\n },\n};\n\n/**\n * True when this project is one safeword would scaffold an .importlinter for —\n * the single source of the \"should import-linter be part of this project\"\n * decision, shared by the scaffold generator and the install condition in\n * lifecycle/project-install.ts (so the two can never drift).\n */\nexport function hasImportLinterScaffoldTarget(cwd: string): boolean {\n return importLinterScaffoldContent(cwd) !== undefined;\n}\n\n/**\n * Expected .importlinter scaffold for this project, WITHOUT the existing-config\n * gate: layers contract when a layer structure is detected, otherwise the\n * generic acyclic guard when the top-level package is unambiguous, else\n * undefined. Shared by the create-time generator (which adds the gate) and the\n * reset-time removeIfUnmodified comparison (which must not have it).\n */\nfunction importLinterScaffoldContent(cwd: string): string | undefined {\n const layers = detectPythonLayers(cwd);\n if (layers.length >= 2) {\n return generateProjectImportLinterConfig(layers, detectRootPackage(cwd));\n }\n const solePackage = detectSolePackage(cwd);\n if (!solePackage) return undefined;\n return generateAcyclicImportLinterConfig(solePackage);\n}\n","/**\n * Rust language pack file definitions\n *\n * Defines config generators and file definitions for:\n * - clippy.toml (lint thresholds)\n * - rustfmt.toml (formatting options)\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport type { FileDefinition, ManagedFileDefinition } from '../types.js';\n\n// ============================================================================\n// Shared Configuration Constants\n// ============================================================================\n\n/**\n * Safeword's clippy thresholds — fills gaps only, customer values win.\n *\n * Clippy defaults are lenient (cognitive-complexity = 25, args = 7).\n * Safeword tightens for LLM code but respects explicit customer choices.\n */\nconst SAFEWORD_CLIPPY_THRESHOLDS: Record<string, number> = {\n 'cognitive-complexity-threshold': 10,\n 'too-many-arguments-threshold': 5,\n 'too-many-lines-threshold': 100,\n 'type-complexity-threshold': 250,\n};\n\n// ============================================================================\n// Config Generators\n// ============================================================================\n\n/** Generate standalone clippy.toml with safeword thresholds. */\nfunction generateClippyConfig(): string {\n const thresholds = Object.entries(SAFEWORD_CLIPPY_THRESHOLDS)\n .map(([key, value]) => `${key} = ${String(value)}`)\n .join('\\n');\n\n return `# Generated by safeword - DO NOT EDIT\n# Configure lint levels in Cargo.toml [lints.clippy] section\n\n${thresholds}\n`;\n}\n\n/**\n * Generate .safeword/clippy.toml — fills gaps only.\n * Customer's thresholds are preserved; safeword only adds missing ones.\n */\nfunction generateSafewordClippyConfig(existingConfig: string | undefined, cwd: string): string {\n if (!existingConfig) {\n return `# Safeword clippy config - standalone (no project config to merge)\n# Used by hooks for LLM enforcement.\n\n${generateClippyThresholds()}\n`;\n }\n\n const configPath = nodePath.join(cwd, existingConfig);\n if (!existsSync(configPath)) {\n return `# Safeword clippy config - standalone (no project config to merge)\n# Used by hooks for LLM enforcement.\n\n${generateClippyThresholds()}\n`;\n }\n\n try {\n const content = readFileSync(configPath, 'utf8');\n const customerKeys = parseClippyKeys(content);\n\n // Fill gaps: only add thresholds customer didn't set\n const gapEntries = Object.entries(SAFEWORD_CLIPPY_THRESHOLDS)\n .filter(([key]) => !customerKeys.has(key))\n .map(([key, value]) => `${key} = ${String(value)}`);\n\n // Include all customer content, then append safeword's gap-fills\n const safewordSection =\n gapEntries.length > 0\n ? `\\n# Safeword defaults (not set in project config)\\n${gapEntries.join('\\n')}\\n`\n : '';\n\n return `# Safeword clippy config - merged with project config\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# Re-run \\`safeword install\\` to regenerate after project config changes.\n#\n# NOTE: Your thresholds are preserved. Safeword only fills gaps.\n\n${content.trim()}\n${safewordSection}`;\n } catch {\n return `# Safeword clippy config - standalone (parse error on project config)\n# Used by hooks for LLM enforcement.\n\n${generateClippyThresholds()}\n`;\n }\n}\n\n/** Generate threshold lines from SAFEWORD_CLIPPY_THRESHOLDS. */\nfunction generateClippyThresholds(): string {\n return Object.entries(SAFEWORD_CLIPPY_THRESHOLDS)\n .map(([key, value]) => `${key} = ${String(value)}`)\n .join('\\n');\n}\n\n/** Parse a clippy.toml and return the set of keys that are defined. */\nfunction parseClippyKeys(content: string): Set<string> {\n const keys = new Set<string>();\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith('#')) {\n const match = /^([\\w-]+)\\s*=/.exec(trimmed);\n if (match?.[1]) keys.add(match[1]);\n }\n }\n return keys;\n}\n\n/**\n * Fallback Rust edition when the project's edition is truly unresolvable — an\n * unreadable/absent Cargo.toml, or a manifest with neither a `[package]` nor a\n * `[workspace.package]` edition to anchor to. Tracks the latest STABLE edition,\n * not the oldest: a fresh or unknowable project in 2026 is best served by the\n * current style edition (edition 2024, stable since Rust 1.85, 2025-02-20), not\n * a 2021 default that mis-sorts imports and can't parse 2024 syntax.\n */\nconst FALLBACK_RUST_EDITION = '2024';\n\n/**\n * The edition Cargo assigns a `[package]` that declares none. We mirror it so an\n * edition-less (legacy) crate is formatted as the 2015 it actually is, rather\n * than having 2024 parsing/style imposed on 2015 code (which uses `async`,\n * `await`, `dyn`, `try` as identifiers).\n */\nconst CARGO_DEFAULT_EDITION = '2015';\n\n/**\n * The `edition = \"…\"` value declared inside a named TOML section, or undefined.\n * Scoped to the section slice (header → next `[…]` header), comment-stripped,\n * and line-anchored so a `# edition = \"2018\"` comment or a `foo-edition` key\n * can't be mistaken for the real key. Accepts basic or literal TOML strings.\n */\nfunction editionInSection(content: string, header: string): string | undefined {\n const start = content.indexOf(header);\n if (start === -1) return undefined;\n\n const rest = content.slice(start + header.length);\n const nextHeader = rest.search(/\\n[ \\t]*\\[/);\n const section = nextHeader === -1 ? rest : rest.slice(0, nextHeader);\n\n for (const rawLine of section.split('\\n')) {\n const line = rawLine.replace(/#.*/, '').trim();\n const match = /^edition\\s*=\\s*[\"']([^\"']+)[\"']/.exec(line);\n if (match?.[1]) return match[1];\n }\n return undefined;\n}\n\n/**\n * Resolve the Rust edition to emit in rustfmt.toml by reading the project's\n * Cargo.toml. rustfmt's `edition` drives both parsing and (by default) the\n * style edition, and an explicit value in rustfmt.toml OVERRIDES what\n * `cargo fmt` would infer — so it must match the crate's real edition or it\n * mis-parses/mis-styles. Resolution order mirrors Cargo:\n * 1. `[package] edition`,\n * 2. workspace-inherited `[workspace.package] edition` (also the\n * virtual-manifest case, where there is no `[package]`),\n * 3. a `[package]` that declares no edition → Cargo's 2015 default,\n * 4. otherwise the modern fallback (unreadable/absent manifest, or no edition\n * anywhere to anchor to).\n * This never demands a newer toolchain than the crate already needs: a 2024\n * crate already requires Rust 1.85 to compile.\n */\nexport function resolveRustEdition(cwd: string): string {\n let content: string;\n try {\n content = readFileSync(nodePath.join(cwd, 'Cargo.toml'), 'utf8');\n } catch {\n return FALLBACK_RUST_EDITION;\n }\n\n const packageEdition = editionInSection(content, '[package]');\n if (packageEdition) return packageEdition;\n\n const workspaceEdition = editionInSection(content, '[workspace.package]');\n if (workspaceEdition) return workspaceEdition;\n\n // A present-but-edition-less `[package]` is edition 2015 by Cargo's default;\n // match it rather than imposing the modern fallback on legacy code. `.includes`\n // mirrors the pack's own `detectWorkspaceType` idiom.\n if (content.includes('[package]')) return CARGO_DEFAULT_EDITION;\n\n return FALLBACK_RUST_EDITION;\n}\n\n/**\n * Generate a lenient, forward-compatible `deny.toml` for the `cargo deny check`\n * supply-chain gate (`safeword project test-plan --kind deps`, run at the /verify gate).\n *\n * Design choices, driven by cargo-deny's schema churn and the cost of a false\n * red on a customer's done-gate:\n * - Only long-lived fields; NO `version`/severity keys (`vulnerability`,\n * `yanked`, …) — those were reshuffled across releases. cargo-deny denies\n * advisories by default, so an explicit-but-empty `[advisories]` is enough and\n * leaves an obvious place to add `ignore = [\"RUSTSEC-…\"]`.\n * - A BROAD permissive `allow` list (covers the overwhelming majority of\n * crates.io) so the licenses check doesn't fail on a routine transitive dep the\n * first time the gate runs. Users extend it when the gate flags a new license.\n * - `multiple-versions = \"warn\"` — duplicate versions are bloat, not a hard stop.\n */\nfunction generateDenyConfig(): string {\n return `# Generated by safeword — cargo-deny dependency config.\n# Safeword creates this file if missing and won't overwrite your edits.\n# Full schema: https://embarkstudios.github.io/cargo-deny/checks/cfg.html\n#\n# The /verify done-gate runs ONLY the advisories check (\\`cargo deny check\n# advisories\\`) — the universal security signal. The licenses/bans/sources\n# sections below are configured but NOT gated by default, because they false-red\n# on things unrelated to a change (missing license metadata, intentional git\n# deps). Enforce them in your own CI with a full \\`cargo deny check\\`.\n#\n# Note: the advisories check fetches the RustSec advisory-db over git on first\n# run, so an offline done-gate can fail here. Prime the cache once online, or run\n# \\`cargo deny check advisories\\` yourself, before relying on the gate offline.\n\n[advisories]\n# Security advisories from the RustSec DB (the cargo-audit source). cargo-deny\n# denies vulnerabilities by default; add \\`ignore = [\"RUSTSEC-YYYY-NNNN\"]\\` to\n# accept a specific one with a rationale.\n\n[licenses]\n# Permissive licenses covering the vast majority of crates.io. Extend this list\n# when the gate flags a dependency whose license you accept.\nallow = [\n \"MIT\",\n \"MIT-0\",\n \"Apache-2.0\",\n \"Apache-2.0 WITH LLVM-exception\",\n \"BSD-2-Clause\",\n \"BSD-3-Clause\",\n \"0BSD\",\n \"ISC\",\n \"Zlib\",\n \"MPL-2.0\",\n \"Unicode-3.0\",\n \"Unicode-DFS-2016\",\n \"CC0-1.0\",\n \"Unlicense\",\n \"BSL-1.0\",\n \"OpenSSL\",\n]\n\n[bans]\n# Duplicate versions bloat builds but rarely warrant a hard failure — warn only.\nmultiple-versions = \"warn\"\n\n[sources]\n# Trust only crates.io by default; flag crates pulled from unknown registries.\nunknown-registry = \"deny\"\nunknown-git = \"deny\"\n`;\n}\n\nfunction generateRustfmtConfig(cwd: string): string {\n const edition = resolveRustEdition(cwd);\n return `# Generated by safeword - DO NOT EDIT\n# edition is derived from your Cargo.toml (fallback: ${FALLBACK_RUST_EDITION}); it\n# must match the crate's edition because rustfmt.toml overrides what cargo fmt infers.\n# All other options are stable on any toolchain that supports this edition.\n\nedition = \"${edition}\"\nmax_width = 100\ntab_spaces = 4\nnewline_style = \"Unix\"\nreorder_imports = true\nuse_field_init_shorthand = true\n`;\n}\n\n// ============================================================================\n// Owned Files (.safeword/ - overwritten on upgrade)\n// ============================================================================\n\nexport const rustOwnedFiles: Record<string, FileDefinition> = {\n '.safeword/clippy.toml': {\n generator: ctx =>\n ctx.languages?.rust\n ? generateSafewordClippyConfig(ctx.projectType.existingClippyConfig, ctx.cwd)\n : undefined,\n },\n '.safeword/rustfmt.toml': {\n generator: ctx => (ctx.languages?.rust ? generateRustfmtConfig(ctx.cwd) : undefined),\n },\n};\n\n// ============================================================================\n// Managed Files (project root - create if missing)\n// ============================================================================\n\nexport const rustManagedFiles: Record<string, ManagedFileDefinition> = {\n 'clippy.toml': {\n generator: ctx =>\n ctx.languages?.rust && !ctx.projectType.existingClippyConfig\n ? generateClippyConfig()\n : undefined,\n },\n 'rustfmt.toml': {\n generator: ctx =>\n ctx.languages?.rust && !ctx.projectType.existingRustfmtConfig\n ? generateRustfmtConfig(ctx.cwd)\n : undefined,\n },\n // deny.toml is create-if-missing (managed): the `cargo deny check` supply-chain\n // gate reads it from the project root. Never overwrites a user's own config.\n 'deny.toml': {\n generator: ctx => (ctx.languages?.rust ? generateDenyConfig() : undefined),\n },\n};\n","/**\n * SQL dialect auto-detection\n *\n * Detects the correct SQLFluff dialect from project signals.\n * Returns undefined when no signal found (caller defaults to 'ansi').\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport nodePath from 'node:path';\nimport process from 'node:process';\n\nimport YAML from 'yaml';\n\nimport { findInTree } from '../../utils/fs.js';\n\n// dbt adapter name → SQLFluff dialect (most are 1:1)\nconst ADAPTER_TO_DIALECT: Record<string, string> = {\n postgres: 'postgres',\n postgresql: 'postgres',\n redshift: 'redshift',\n snowflake: 'snowflake',\n bigquery: 'bigquery',\n clickhouse: 'clickhouse',\n duckdb: 'duckdb',\n trino: 'trino',\n databricks: 'databricks',\n athena: 'athena',\n teradata: 'teradata',\n materialize: 'materialize',\n sqlite: 'sqlite',\n mysql: 'mysql',\n oracle: 'oracle',\n vertica: 'vertica',\n starrocks: 'starrocks',\n doris: 'doris',\n hive: 'hive',\n impala: 'impala',\n db2: 'db2',\n greenplum: 'greenplum',\n exasol: 'exasol',\n // Name mismatches\n spark: 'sparksql',\n fabric: 'tsql',\n synapse: 'tsql',\n // Compatibility aliases\n tidb: 'mysql',\n singlestore: 'mysql',\n cratedb: 'postgres',\n timescaledb: 'postgres',\n};\n\n// URL scheme → SQLFluff dialect\nconst SCHEME_TO_DIALECT: Record<string, string> = {\n postgresql: 'postgres',\n postgres: 'postgres',\n mysql: 'mysql',\n mariadb: 'mysql',\n sqlite: 'sqlite',\n sqlite3: 'sqlite',\n bigquery: 'bigquery',\n clickhouse: 'clickhouse',\n redshift: 'redshift',\n snowflake: 'snowflake',\n sqlserver: 'tsql',\n mssql: 'tsql',\n};\n\n// Drizzle dialect → SQLFluff dialect\nconst DRIZZLE_TO_DIALECT: Record<string, string> = {\n postgresql: 'postgres',\n mysql: 'mysql',\n sqlite: 'sqlite',\n turso: 'sqlite',\n singlestore: 'mysql',\n mssql: 'tsql',\n gel: 'postgres',\n cockroach: 'postgres',\n};\n\n// ORM provider → SQLFluff dialect\nconst PROVIDER_TO_DIALECT: Record<string, string> = {\n postgresql: 'postgres',\n mysql: 'mysql',\n sqlite: 'sqlite',\n sqlserver: 'tsql',\n cockroachdb: 'postgres',\n};\n\n/**\n * Detect SQL dialect from project signals.\n * Returns the SQLFluff dialect string or undefined (→ ansi default).\n *\n * Priority order: profiles.yml (canonical for dbt) → Python deps (always in repo)\n * → sqlc config → Prisma schema → Drizzle config → DATABASE_URL (lowest confidence).\n */\nexport function detectSqlDialect(cwd: string): string | undefined {\n return (\n detectFromProfiles(cwd) ??\n detectFromPythonDependencies(cwd) ??\n detectFromSqlcConfig(cwd) ??\n detectFromPrismaSchema(cwd) ??\n detectFromDrizzleConfig(cwd) ??\n detectFromDatabaseUrl(cwd)\n );\n}\n\n/** Read adapter type from a profiles.yml file for the given profile name. */\nfunction resolveProfileType(profilesPath: string, profileName: string): string | undefined {\n if (!existsSync(profilesPath)) return undefined;\n\n const content = readFileSync(profilesPath, 'utf8');\n const profiles = YAML.parse(content, { schema: 'failsafe' }) as Record<string, unknown>;\n const profile = profiles[profileName] as Record<string, unknown> | undefined;\n if (!profile?.outputs) return undefined;\n\n const outputs = profile.outputs as Record<string, Record<string, unknown>>;\n const targetName = typeof profile.target === 'string' ? profile.target : Object.keys(outputs)[0];\n if (!targetName) return undefined;\n\n const target = outputs[targetName];\n return typeof target?.type === 'string' ? target.type : undefined;\n}\n\n/** Signal 1: dbt profiles.yml → adapter type field. */\nfunction detectFromProfiles(cwd: string): string | undefined {\n const dbtProjectDirectory = findInTree(cwd, 'dbt_project.yml');\n if (!dbtProjectDirectory) return undefined;\n\n try {\n const projectContent = readFileSync(\n nodePath.join(dbtProjectDirectory, 'dbt_project.yml'),\n 'utf8',\n );\n const project = YAML.parse(projectContent, { schema: 'failsafe' }) as Record<string, unknown>;\n const profileName = project.profile;\n if (typeof profileName !== 'string') return undefined;\n\n // Search: project root → dbt/ subdir → ~/.dbt/ (dbt's standard fallback)\n const candidates = [\n nodePath.join(dbtProjectDirectory, 'profiles.yml'),\n nodePath.join(dbtProjectDirectory, 'dbt', 'profiles.yml'),\n nodePath.join(process.env.HOME ?? '', '.dbt', 'profiles.yml'),\n ];\n\n for (const profilesPath of candidates) {\n const adapterType = resolveProfileType(profilesPath, profileName);\n if (adapterType) return ADAPTER_TO_DIALECT[adapterType];\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Signal 2: dbt-{adapter} packages in Python dependency files. */\nfunction detectFromPythonDependencies(cwd: string): string | undefined {\n const directory = findInTree(cwd, 'requirements.txt') ?? findInTree(cwd, 'pyproject.toml');\n if (!directory) return undefined;\n\n const requirementsPath = nodePath.join(directory, 'requirements.txt');\n const pyprojectPath = nodePath.join(directory, 'pyproject.toml');\n\n try {\n let content: string | undefined;\n if (existsSync(requirementsPath)) {\n content = readFileSync(requirementsPath, 'utf8');\n } else if (existsSync(pyprojectPath)) {\n content = readFileSync(pyprojectPath, 'utf8');\n }\n if (!content) return undefined;\n\n // Match dbt-{adapter} packages, skipping dbt-core (framework, not adapter)\n const adapters = content\n .matchAll(/dbt-(\\w+)/g)\n .map(m => m[1])\n .filter((a): a is string => a !== undefined && a !== 'core')\n .toArray();\n for (const adapter of adapters) {\n const dialect = ADAPTER_TO_DIALECT[adapter];\n if (dialect) return dialect;\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Signal 3: sqlc config → engine field. */\nfunction detectFromSqlcConfig(cwd: string): string | undefined {\n for (const filename of ['sqlc.yaml', 'sqlc.yml', 'sqlc.json']) {\n const directory = findInTree(cwd, filename);\n if (!directory) continue;\n\n try {\n const content = readFileSync(nodePath.join(directory, filename), 'utf8');\n const match = /engine['\":\\s]+(\\w+)/.exec(content);\n if (match?.[1]) return ADAPTER_TO_DIALECT[match[1]] ?? match[1];\n } catch {\n continue;\n }\n }\n return undefined;\n}\n\n/** Signal 4: Prisma schema.prisma → provider field. */\nfunction detectFromPrismaSchema(cwd: string): string | undefined {\n const schemaPath = nodePath.join(cwd, 'prisma', 'schema.prisma');\n if (!existsSync(schemaPath)) return undefined;\n\n try {\n const content = readFileSync(schemaPath, 'utf8');\n const match = /provider\\s*=\\s*\"(\\w+)\"/.exec(content);\n if (!match?.[1]) return undefined;\n\n // Skip non-database providers (e.g., \"prisma-client-js\")\n return PROVIDER_TO_DIALECT[match[1]];\n } catch {\n return undefined;\n }\n}\n\n/** Signal 5: drizzle.config.ts → dialect field. */\nfunction detectFromDrizzleConfig(cwd: string): string | undefined {\n for (const filename of ['drizzle.config.ts', 'drizzle.config.js', 'drizzle.config.mjs']) {\n const filePath = nodePath.join(cwd, filename);\n if (!existsSync(filePath)) continue;\n\n try {\n const content = readFileSync(filePath, 'utf8');\n const match = /dialect\\s*:\\s*[\"'](\\w+)[\"']/.exec(content);\n if (match?.[1]) return DRIZZLE_TO_DIALECT[match[1]];\n } catch {\n continue;\n }\n }\n return undefined;\n}\n\n/** Signal 6: DATABASE_URL scheme in .env file. */\nfunction detectFromDatabaseUrl(cwd: string): string | undefined {\n const envPath = nodePath.join(cwd, '.env');\n if (!existsSync(envPath)) return undefined;\n\n try {\n const content = readFileSync(envPath, 'utf8');\n const match = /DATABASE_URL\\s*=\\s*[\"']?(\\w+):/.exec(content);\n if (!match?.[1]) return undefined;\n\n return SCHEME_TO_DIALECT[match[1]];\n } catch {\n return undefined;\n }\n}\n","/**\n * SQL Language Pack - Schema Definitions\n *\n * All SQL-specific file definitions and config generators.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n */\n\nimport type { FileDefinition, ManagedFileDefinition, ProjectContext } from '../types.js';\nimport { detectSqlDialect } from './dialect.js';\n\n/**\n * Whether the host repo already owns SQL formatting via prettier-plugin-sql\n * declared in its root package.json (#636/#638). Setup then generates no\n * sqlfluff configs — safeword defers to the host's formatter, the same way it\n * defers to Biome/dprint for JS/TS. Declared deps only: a transitive copy in\n * node_modules is not ownership. Hook-side twin: hostFormatsSqlWithPrettier in\n * templates/hooks/lib/lint-config.ts (kept in sync by hand — src can't import\n * templates).\n */\nfunction hostOwnsSqlFormatting(ctx: ProjectContext): boolean {\n return (\n 'prettier-plugin-sql' in ctx.developmentDeps || 'prettier-plugin-sql' in ctx.productionDeps\n );\n}\n\n// ============================================================================\n// SQLFluff Configuration\n// ============================================================================\n\n/**\n * Shared SQLFluff rules for .safeword/sqlfluff.cfg.\n * Stricter rules for LLM enforcement — extends project config.\n */\nconst SQLFLUFF_STRICT_RULES = `[sqlfluff:rules:capitalisation.keywords]\ncapitalisation_policy = upper\n\n[sqlfluff:rules:capitalisation.identifiers]\ncapitalisation_policy = lower\n\n[sqlfluff:rules:capitalisation.functions]\ncapitalisation_policy = upper\n\n[sqlfluff:rules:capitalisation.literals]\ncapitalisation_policy = upper\n\n[sqlfluff:rules:capitalisation.types]\ncapitalisation_policy = upper\n\n[sqlfluff:rules:layout.long_lines]\nmax_line_length = 120`;\n\n// ============================================================================\n// Config Generators for .safeword/ (ownedFiles)\n// ============================================================================\n\n/**\n * Generate SQLFluff config for .safeword/sqlfluff.cfg.\n *\n * If project has existing config, extends it with stricter rules.\n * Otherwise generates standalone config.\n */\nfunction generateSqlfluffBaseConfig(\n existingSqlfluffConfig: string | undefined,\n dialect: string,\n): string {\n if (existingSqlfluffConfig) {\n // Don't emit dialect or templater — let customer's config provide them.\n // sqlfluff --config patches key-by-key, so omitting means customer's values survive.\n return `# Safeword SQLFluff config - adds stricter rules on top of project config\n# Used by hooks for LLM enforcement. Human pre-commits use project config.\n# Re-run \\`safeword install\\` to regenerate after project config changes.\n#\n# NOTE: Your dialect and templater are preserved. Safeword only adds style rules.\n\n[sqlfluff]\nwarnings =\n\n${SQLFLUFF_STRICT_RULES}\n`;\n }\n\n return `# Generated by safeword - DO NOT EDIT\n# Customize by creating .sqlfluff at project root\n\n[sqlfluff]\ndialect = ${dialect}\ntemplater = jinja\nmax_line_length = 120\nwarnings =\n\n[sqlfluff:templater:jinja]\napply_dbt_builtins = True\n\n${SQLFLUFF_STRICT_RULES}\n`;\n}\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\nexport const sqlOwnedFiles: Record<string, FileDefinition> = {\n '.safeword/sqlfluff.cfg': {\n generator: ctx =>\n ctx.languages?.sql && !hostOwnsSqlFormatting(ctx)\n ? generateSqlfluffBaseConfig(\n ctx.projectType.existingSqlfluffConfig,\n detectSqlDialect(ctx.cwd) ?? 'ansi',\n )\n : undefined,\n },\n};\n\n// ============================================================================\n// Config Generators for project root (managedFiles)\n// ============================================================================\n\nfunction generateProjectSqlfluffConfig(dialect: string): string {\n return `# Generated by safeword\n# Add your project-specific overrides here.\n# Strict rules for LLM enforcement are in .safeword/sqlfluff.cfg\n\n[sqlfluff]\ndialect = ${dialect}\ntemplater = jinja\nmax_line_length = 120\n\n[sqlfluff:templater:jinja]\napply_dbt_builtins = True\n`;\n}\n\n// ============================================================================\n// Managed Files (create if missing, reconciled on reset/upgrade)\n// ============================================================================\n\nexport const sqlManagedFiles: Record<string, ManagedFileDefinition> = {\n '.sqlfluff': {\n generator: ctx =>\n ctx.languages?.sql && !ctx.projectType.existingSqlfluffConfig && !hostOwnsSqlFormatting(ctx)\n ? generateProjectSqlfluffConfig(detectSqlDialect(ctx.cwd) ?? 'ansi')\n : undefined,\n },\n};\n","/**\n * Helpers shared by `JsonMergeDefinition.unmerge` implementations.\n */\n\n/**\n * Assign `value` to `target[key]`, or delete the key when `value` has no\n * remaining entries. Unmerge functions strip safeword's managed keys out of a\n * nested object (scripts, hooks, mcpServers, …) and then must either write the\n * trimmed object back or drop the now-empty container entirely. Returns whether\n * the key was kept, so callers can prune sibling keys that only make sense when\n * the container survives (e.g. `.cursor/hooks.json`'s `version`).\n */\nexport function assignOrPrune(\n target: Record<string, unknown>,\n key: string,\n value: Record<string, unknown>,\n): boolean {\n if (Object.keys(value).length > 0) {\n target[key] = value;\n return true;\n }\n Reflect.deleteProperty(target, key);\n return false;\n}\n","/**\n * TypeScript/JavaScript Language Pack - Schema Definitions\n *\n * All JS/TS specific file definitions, JSON merges, and packages.\n * Imported by schema.ts and spread into SAFEWORD_SCHEMA.\n *\n * Note: Generator functions return `string | undefined` by design (undefined = skip file).\n * This conflicts with sonarjs/no-inconsistent-returns vs unicorn/no-useless-undefined (#1199).\n * Inline disables target only generator arrow functions, not regular functions in this file.\n */\n\nimport {\n dirGlobExcludeMerge,\n durableNamespaceDirectories,\n resolvedIgnoreDirectories,\n resolvedNamespaceDirectory,\n} from '../../owned-paths.js';\nimport { getEslintConfig, getSafewordEslintConfig } from '../../templates/config.js';\nimport { assignOrPrune } from '../../utils/json-merge.js';\nimport type {\n FileDefinition,\n JsonMergeDefinition,\n ManagedFileDefinition,\n ProjectContext,\n} from '../types.js';\n\n// Deliberately duplicated with presets/typescript/detect.ts: published presets must\n// remain self-contained, while language packs may not import preset implementation.\nconst SAFEWORD_PRIMARY_LINT_SCRIPT = 'eslint . && bun run lint:gherkin';\n\n// ============================================================================\n// Shared Definitions\n// ============================================================================\n\n/**\n * Add framework-specific ignores to Knip config.\n */\nfunction addKnipIgnores(config: { ignore: string[] }, ctx: ProjectContext): void {\n const allDependencies = ctx.developmentDeps;\n\n // Framework build/cache directories\n if (ctx.projectType.nextjs) config.ignore.push('.next/**');\n if (ctx.projectType.astro) config.ignore.push('.astro/**');\n if ('storybook' in allDependencies || '@storybook/react' in allDependencies) {\n config.ignore.push('.storybook/**', 'storybook-static/**');\n }\n if ('turbo' in allDependencies) config.ignore.push('.turbo/**');\n\n // Electron - Knip has no Electron plugin\n if ('electron' in allDependencies || 'electron' in ctx.productionDeps) {\n config.ignore.push('electron/**', 'dist-electron/**', 'out/**');\n }\n}\n\n/**\n * Add framework-specific ignoreDependencies to Knip config.\n */\nfunction addKnipIgnoreDependencies(\n config: { ignoreDependencies: string[] },\n ctx: ProjectContext,\n): void {\n const allDependencies = ctx.developmentDeps;\n\n // Dependencies used via config files, not imports\n if (ctx.projectType.tailwind) {\n config.ignoreDependencies.push('tailwindcss', '@tailwindcss/typography', '@tailwindcss/forms');\n }\n if (ctx.projectType.playwright) config.ignoreDependencies.push('@playwright/test');\n if ('husky' in allDependencies) config.ignoreDependencies.push('husky');\n if ('lint-staged' in allDependencies) config.ignoreDependencies.push('lint-staged');\n\n // Electron ecosystem packages\n if ('electron' in allDependencies || 'electron' in ctx.productionDeps) {\n config.ignoreDependencies.push(\n 'electron',\n 'electron-builder',\n 'electron-updater',\n 'electron-devtools-installer',\n 'electron-is-dev',\n 'electron-store',\n );\n }\n}\n\n/**\n * Generate Knip config based on project type.\n * Adds framework-specific ignores and ignoreDependencies.\n * Knip plugins auto-enable based on deps, so we just configure ignore patterns.\n */\nfunction getKnipConfig(ctx: ProjectContext): object {\n // A custom paths.projectRoot is added alongside the two well-known roots so\n // Knip doesn't scan (and false-positive on) the custom namespace dir (#273).\n // NB: Knip's ignore is intentionally just the namespace roots, not the full\n // safewordIgnoreDirectories/resolvedIgnoreDirectories list — so resolve the\n // custom root directly here rather than reusing that composer.\n const customRoot = resolvedNamespaceDirectory(ctx);\n const config = {\n ignore: [\n '.safeword/**',\n '.project/**',\n '.safeword-project/**',\n ...(customRoot === undefined ? [] : [`${customRoot}/**`]),\n ],\n ignoreDependencies: ['safeword', 'dependency-cruiser'],\n };\n\n addKnipIgnores(config, ctx);\n addKnipIgnoreDependencies(config, ctx);\n\n return config;\n}\n\n/**\n * Prettier styling defaults - shared between .safeword/.prettierrc and root .prettierrc\n */\nconst PRETTIER_DEFAULTS = {\n semi: true,\n singleQuote: true,\n tabWidth: 2,\n trailingComma: 'all',\n printWidth: 100,\n endOfLine: 'lf',\n useTabs: false,\n bracketSpacing: true,\n arrowParens: 'avoid',\n} as const;\n\n/**\n * Get Prettier plugins based on project type.\n * Tailwind plugin must be last for proper class sorting.\n */\nfunction getPrettierPlugins(projectType: {\n astro?: boolean;\n shell?: boolean;\n tailwind?: boolean;\n}): string[] {\n const plugins: string[] = [];\n if (projectType.astro) plugins.push('prettier-plugin-astro');\n if (projectType.shell) plugins.push('prettier-plugin-sh');\n if (projectType.tailwind) plugins.push('prettier-plugin-tailwindcss');\n return plugins;\n}\n\n/**\n * Biome config merge - adds safeword files to excludes list.\n * Biome v2 uses `includes` with `!` prefix for exclusions.\n */\nconst BIOME_JSON_MERGE: JsonMergeDefinition = {\n keys: ['files.includes'],\n skipIfMissing: true, // Only modify if project already uses Biome\n merge: (existing, ctx) => {\n const files = (existing.files as Record<string, unknown>) ?? {};\n const existingIncludes = Array.isArray(files.includes) ? files.includes : [];\n\n // Add safeword exclusions (! prefix) if not already present, sourced from\n // the single SAFEWORD_IGNORE_DIRS list (ticket EYRK34) so Biome skips every\n // safeword-owned dir, not just .safeword/ — including a custom paths.projectRoot\n // (issue #273). Biome v2.2.0+ doesn't need /**.\n const safewordExcludes = [\n '!eslint.config.mjs',\n ...resolvedIgnoreDirectories(ctx).map(dir => `!${dir}`),\n ];\n const newIncludes = [...existingIncludes];\n for (const exclude of safewordExcludes) {\n if (!newIncludes.includes(exclude)) {\n newIncludes.push(exclude);\n }\n }\n\n return {\n ...existing,\n files: {\n ...files,\n includes: newIncludes,\n },\n };\n },\n unmerge: (existing, ctx) => {\n const files = (existing.files as Record<string, unknown>) ?? {};\n const existingIncludes = Array.isArray(files.includes) ? files.includes : [];\n\n // Remove safeword exclusions from includes list (current set + the legacy\n // '!.safeword/**' folder form, cleaned up on unmerge). Mirrors merge, so a\n // custom paths.projectRoot exclusion is also removed (issue #273).\n const safewordExcludes = new Set([\n '!eslint.config.mjs',\n '!.safeword/**',\n ...resolvedIgnoreDirectories(ctx).map(dir => `!${dir}`),\n ]);\n const durableExcludes = new Set(durableNamespaceDirectories(ctx).map(dir => `!${dir}`));\n const cleanedIncludes = existingIncludes.filter(\n (entry: string) => !safewordExcludes.has(entry) || durableExcludes.has(entry),\n );\n\n // Build cleaned files object\n const cleanedFiles = { ...files };\n if (cleanedIncludes.length > 0) {\n cleanedFiles.includes = cleanedIncludes;\n } else {\n delete cleanedFiles.includes;\n }\n\n // Remove files key entirely if empty\n if (Object.keys(cleanedFiles).length === 0) {\n const result = { ...existing };\n delete result.files;\n return result;\n }\n\n return { ...existing, files: cleanedFiles };\n },\n};\n\n// dprint's `excludes` and oxfmt's `ignorePatterns` both take a glob list (EYRK34).\nconst DPRINT_JSON_MERGE = dirGlobExcludeMerge('excludes');\nconst OXFMT_JSON_MERGE = dirGlobExcludeMerge('ignorePatterns');\n\n// ============================================================================\n// Owned Files (overwritten on upgrade)\n// ============================================================================\n\nexport const typescriptOwnedFiles: Record<string, FileDefinition> = {\n // Language-specific safeword configs for hooks (extend project configs if they exist)\n // These configs are used by hooks for LLM enforcement with stricter rules\n '.safeword/eslint.config.mjs': {\n generator: ctx =>\n ctx.languages?.javascript\n ? getSafewordEslintConfig(\n ctx.projectType.existingEslintConfig,\n ctx.projectType.existingFormatter,\n )\n : undefined,\n },\n\n '.safeword/.prettierrc': {\n // eslint-disable-next-line sonarjs/no-inconsistent-returns -- generator returns undefined to skip file\n generator: ctx => {\n // Skip for non-JS projects, projects with an alternative formatter (Biome,\n // etc.), or projects that already have their own prettier config — writing\n // our own would shadow a config we can't merge into.\n if (\n !ctx.languages?.javascript ||\n ctx.projectType.existingFormatter ||\n ctx.projectType.existingPrettierConfig\n ) {\n return;\n }\n // Add plugins based on project type\n const plugins = getPrettierPlugins(ctx.projectType);\n const config = plugins.length > 0 ? { ...PRETTIER_DEFAULTS, plugins } : PRETTIER_DEFAULTS;\n return JSON.stringify(config, undefined, 2);\n },\n },\n};\n\n// ============================================================================\n// Managed Files (create if missing, update if matches template)\n// ============================================================================\n\nexport const typescriptManagedFiles: Record<string, ManagedFileDefinition> = {\n // Project-level ESLint config (created only if no existing ESLint config)\n 'eslint.config.mjs': {\n // eslint-disable-next-line sonarjs/no-inconsistent-returns -- generator returns undefined to skip file\n generator: ctx => {\n // Skip if project already has ESLint config (safeword will use .safeword/eslint.config.mjs)\n if (ctx.projectType.existingEslintConfig) return;\n if (!ctx.languages?.javascript) return;\n return getEslintConfig(ctx.projectType.existingFormatter);\n },\n },\n // Minimal tsconfig for ESLint type-checked linting (only if missing)\n 'tsconfig.json': {\n // eslint-disable-next-line sonarjs/no-inconsistent-returns -- generator returns undefined to skip file\n generator: ctx => {\n // Skip for non-JS projects (Python-only)\n if (!ctx.languages?.javascript) return;\n // Only create for TypeScript projects\n if (!ctx.developmentDeps.typescript && !ctx.developmentDeps['typescript-eslint']) {\n return;\n }\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n moduleResolution: 'NodeNext',\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n noEmit: true,\n },\n include: ['**/*.ts', '**/*.tsx'],\n exclude: ['node_modules', 'dist', 'build'],\n },\n undefined,\n 2,\n );\n },\n },\n // Knip config for dead code detection (used by /audit)\n // Plugins auto-enable based on deps, we just configure ignore patterns\n 'knip.json': {\n generator: ctx =>\n ctx.projectType?.hasJsSource ? JSON.stringify(getKnipConfig(ctx), undefined, 2) : undefined,\n },\n // Project-level Prettier config (created only if no existing formatter)\n '.prettierrc': {\n // eslint-disable-next-line sonarjs/no-inconsistent-returns -- generator returns undefined to skip file\n generator: ctx => {\n // Skip for non-JS projects, projects with an alternative formatter, or\n // projects that already have their own prettier config — a new .prettierrc\n // resolves ahead of prettier.config.* and would silently shadow it.\n if (!ctx.languages?.javascript) return;\n if (ctx.projectType.existingFormatter) return;\n if (ctx.projectType.existingPrettierConfig) return;\n // Create base config with styling defaults (no plugins - those are in .safeword/.prettierrc)\n return JSON.stringify(PRETTIER_DEFAULTS, undefined, 2);\n },\n },\n};\n\n// ============================================================================\n// JSON Merges\n// ============================================================================\n\n/**\n * Add a script if it doesn't exist.\n */\nfunction addScriptIfMissing(scripts: Record<string, string>, name: string, command: string): void {\n const existing = scripts[name];\n if (!existing) scripts[name] = command;\n}\n\nconst GHERKIN_LINT_SCRIPT = 'safeword project lint-gherkin';\n\nfunction removeScriptIfEqual(\n scripts: Record<string, string>,\n name: string,\n safewordValue: string,\n): Record<string, string> {\n return scripts[name] === safewordValue\n ? Object.fromEntries(Object.entries(scripts).filter(([key]) => key !== name))\n : scripts;\n}\n\n/**\n * Merge lint scripts based on project type.\n */\nfunction mergeLintScripts(\n scripts: Record<string, string>,\n projectType: { existingLinter: boolean },\n): void {\n addScriptIfMissing(scripts, 'lint:gherkin', GHERKIN_LINT_SCRIPT);\n if (projectType.existingLinter) {\n // Project with existing linter: add lint:eslint for safeword-specific rules\n addScriptIfMissing(scripts, 'lint:eslint', 'eslint .');\n } else {\n // No existing linter: ESLint is the primary linter\n addScriptIfMissing(scripts, 'lint', SAFEWORD_PRIMARY_LINT_SCRIPT);\n }\n}\n\n/**\n * Merge format scripts if no existing formatter.\n */\nfunction mergeFormatScripts(\n scripts: Record<string, string>,\n projectType: { existingFormatter: boolean },\n): void {\n if (projectType.existingFormatter) return;\n addScriptIfMissing(scripts, 'format', 'prettier --write .');\n addScriptIfMissing(scripts, 'format:check', 'prettier --check .');\n}\n\nexport const typescriptJsonMerges: Record<string, JsonMergeDefinition> = {\n 'package.json': {\n keys: [\n 'scripts.lint',\n 'scripts.lint:gherkin',\n 'scripts.format',\n 'scripts.format:check',\n 'scripts.test:bdd',\n ],\n skipIfMissing: true, // Setup creates package.json first (ensurePackageJson), so this only skips outside setup\n conditionalKeys: {\n existingLinter: ['scripts.lint:eslint'], // Projects with existing linter get separate ESLint script\n publishableLibrary: ['scripts.publint'],\n shell: ['scripts.lint:sh'],\n hasJsSource: ['scripts.knip'], // knip script only added for repos with real JS source (BE7C7B)\n },\n merge: (existing, ctx) => {\n const scripts = { ...(existing.scripts as Record<string, string>) };\n const result = { ...existing };\n\n mergeLintScripts(scripts, ctx.projectType);\n mergeFormatScripts(scripts, ctx.projectType);\n // knip is JS-app dead-code detection — only for repos with real JS source (BE7C7B).\n if (ctx.projectType.hasJsSource) addScriptIfMissing(scripts, 'knip', 'knip');\n // BDD acceptance lane (ticket 102b) — add-if-absent: an existing\n // customer test:bdd script always wins. Suppressed entirely when the\n // lane isn't safeword's to scaffold (56JCFZ).\n if (ctx.projectType.scaffoldBddLane) {\n addScriptIfMissing(scripts, 'test:bdd', 'cucumber-js');\n }\n\n // Conditional scripts based on project type\n if (ctx.projectType.publishableLibrary) {\n addScriptIfMissing(scripts, 'publint', 'publint');\n }\n if (ctx.projectType.shell) {\n addScriptIfMissing(\n scripts,\n 'lint:sh',\n \"if command -v shellcheck >/dev/null 2>&1; then shellcheck **/*.sh; else echo 'shellcheck not installed; skipping'; fi\",\n );\n }\n\n result.scripts = scripts;\n return result;\n },\n unmerge: (existing, _ctx) => {\n const result = { ...existing };\n let scripts = { ...(existing.scripts as Record<string, string>) };\n\n // Remove only unchanged Safeword defaults. A script edited or added by the\n // customer after installation is not ours to delete. lint:gherkin and the\n // primary lint wrapper must go when unchanged because both invoke Safeword.\n scripts = removeScriptIfEqual(scripts, 'lint', SAFEWORD_PRIMARY_LINT_SCRIPT);\n scripts = removeScriptIfEqual(scripts, 'lint:gherkin', GHERKIN_LINT_SCRIPT);\n scripts = removeScriptIfEqual(scripts, 'test:bdd', 'cucumber-js');\n\n assignOrPrune(result, 'scripts', scripts);\n return result;\n },\n },\n\n // Prettier config — adds safeword's plugins (and, greenfield only, its style\n // defaults) to safeword's OWN `.prettierrc`. A customer who already owns a\n // prettier config is left untouched — see the existingPrettierConfig gate below.\n '.prettierrc': {\n keys: ['plugins'],\n skipIfMissing: true,\n merge: (existing, ctx) => {\n // A customer who already owns a prettier config keeps it exactly as-is:\n // filling in safeword's style defaults or injecting plugins changes the\n // resolved style and churns their files on the next prettier run (ticket\n // 9C2CFX — revisits 8BNSTE's \"the additive merge is safe\" call). Only a\n // greenfield install, where safeword wrote this `.prettierrc` itself and\n // existingPrettierConfig is false, gets safeword's plugins.\n if (ctx.projectType.existingPrettierConfig) return existing;\n\n const result = { ...existing } as Record<string, unknown>;\n\n // Add defaults for missing styling options (preserves user customizations)\n for (const [key, value] of Object.entries(PRETTIER_DEFAULTS)) {\n if (result[key] === undefined) {\n result[key] = value;\n }\n }\n\n // Merge plugins: preserve user's plugins + add safeword's\n const safewordPlugins = getPrettierPlugins(ctx.projectType);\n const existingPlugins = Array.isArray(result.plugins) ? (result.plugins as string[]) : [];\n\n // Combine existing + safeword plugins (deduplicated)\n const allPlugins = [...existingPlugins];\n for (const plugin of safewordPlugins) {\n if (!allPlugins.includes(plugin)) {\n allPlugins.push(plugin);\n }\n }\n\n // Ensure tailwind plugin is always last (required for proper class sorting)\n const tailwindIndex = allPlugins.indexOf('prettier-plugin-tailwindcss');\n if (tailwindIndex !== -1 && tailwindIndex !== allPlugins.length - 1) {\n allPlugins.splice(tailwindIndex, 1);\n allPlugins.push('prettier-plugin-tailwindcss');\n }\n\n if (allPlugins.length > 0) {\n result.plugins = allPlugins;\n } else {\n delete result.plugins;\n }\n\n return result;\n },\n unmerge: (existing, ctx) => {\n if (ctx.projectType.existingPrettierConfig) return existing;\n const result = { ...existing } as Record<string, unknown>;\n // Only remove safeword's plugins, preserve user's custom plugins\n const safewordPlugins = new Set(getPrettierPlugins(ctx.projectType));\n if (Array.isArray(result.plugins)) {\n const remaining = (result.plugins as string[]).filter(p => !safewordPlugins.has(p));\n if (remaining.length > 0) {\n result.plugins = remaining;\n } else {\n delete result.plugins;\n }\n }\n return result;\n },\n },\n\n // Biome excludes - add safeword files so they don't get linted by Biome/Ultracite\n // Biome v2 uses `includes` with `!` prefix for exclusions (not a separate `ignore` key)\n // Support both biome.json and biome.jsonc\n 'biome.json': BIOME_JSON_MERGE,\n 'biome.jsonc': BIOME_JSON_MERGE,\n\n // dprint excludes - add safeword-owned dirs so a customer's dprint fmt skips them.\n 'dprint.json': DPRINT_JSON_MERGE,\n '.dprint.json': DPRINT_JSON_MERGE,\n 'dprint.jsonc': DPRINT_JSON_MERGE,\n '.dprint.jsonc': DPRINT_JSON_MERGE,\n\n // oxfmt ignorePatterns - JSON config forms only (oxfmt.config.* modules can't be\n // merged, like prettier.config.*); customers on those add the excludes themselves.\n '.oxfmtrc.json': OXFMT_JSON_MERGE,\n '.oxfmtrc.jsonc': OXFMT_JSON_MERGE,\n};\n\n// ============================================================================\n// Packages\n// ============================================================================\n\n// Default new installs to ESLint 10 — safeword's peer range is `^9.22.0 || ^10.0.0`\n// since v0.52.0, and the plugin-ecosystem blocker that pinned this to v9 is resolved.\n// Existing customers are NOT bumped: reconcile skips packages already installed, so\n// this only affects fresh setups. v10 keeps the `eslint/config` defineConfig helper\n// used in generated configs.\nexport const ESLINT_PACKAGE = 'eslint@^10.0.0';\nexport const JITI_PACKAGE = 'jiti@^2.2.0';\n\nexport const typescriptPackages = {\n base: [\n // Core tools (always needed for JS/TS — eslint/prettier also lint the BDD lane's .ts step files)\n ESLINT_PACKAGE,\n // Safeword (bundles eslint-config-prettier + all ESLint plugins)\n 'safeword',\n // Generated .safeword/eslint.config.mjs uses jiti to load eslint.config.ts.\n JITI_PACKAGE,\n ],\n conditional: {\n // BDD acceptance lane (ticket 102b) — cucumber-js runs the scaffolded\n // .feature files; tsx transpiles the TypeScript step definitions, and\n // @types/node lets the scaffolded steps (node: imports) pass typechecks.\n // Keyed on ProjectType.scaffoldBddLane: suppressed when the repo has its\n // own cucumber harness and safeword's lane is absent (56JCFZ).\n scaffoldBddLane: ['@cucumber/cucumber', 'tsx', '@types/node'],\n // Prettier (only for projects without existing formatter)\n standard: ['prettier'], // \"standard\" = !existingFormatter\n // Prettier plugins (only for projects without existing formatter that need them)\n astro: ['prettier-plugin-astro'],\n tailwind: ['prettier-plugin-tailwindcss'],\n shell: ['prettier-plugin-sh'],\n // Non-ESLint tools\n publishableLibrary: ['publint'],\n // Legacy ESLint config compat (needed when extending .eslintrc.* configs)\n legacyEslint: ['@eslint/eslintrc'],\n // Architecture + dead-code tools (used by /audit) — these scan JS APPLICATION\n // code/deps, so only for repos with real JS source, not a non-JS repo carrying\n // just the TS BDD lane (BE7C7B).\n hasJsSource: ['dependency-cruiser', 'knip'],\n },\n};\n","export interface CursorCommandWrapper {\n readonly name: string;\n readonly description: string;\n readonly skillPath: string;\n}\n\nexport interface CursorRuleWrapper {\n readonly name: string;\n readonly alwaysApply: boolean;\n readonly description?: string;\n readonly frontmatterOrder?: 'description-first';\n readonly globs?: readonly string[];\n readonly referencePath: string;\n readonly skill?: string;\n}\n\nexport interface SkillCursorPair {\n readonly skill: string;\n readonly cursorRules: readonly string[] | undefined;\n}\n\ninterface FrontmatterField {\n readonly key: string;\n readonly value: string | boolean | readonly string[];\n}\n\nconst CURSOR_ACTION_SKILLS = [\n 'lint',\n 'verify',\n 'closeout',\n 'audit',\n 'explain',\n 'cleanup-zombies',\n 'self-review',\n 'review-spec',\n 'retro',\n 'spike',\n] as const;\n\n// Shared verbatim by a skill's command wrapper and its rule wrapper.\nconst DEBUG_DESCRIPTION =\n \"Finds the real cause of a bug, in four steps, before you touch a fix. Use when encountering bugs, test failures, unexpected behavior, or when previous fix attempts failed. Enforces investigate-first discipline ('debug this', 'fix this error', 'test is failing', 'not working').\";\n\nconst REFACTOR_DESCRIPTION =\n \"Systematic refactoring with small-step discipline. Use when user says 'refactor', 'clean up', 'restructure', 'extract', 'rename', 'simplify', or mentions code smells. Enforces one change → test → commit when the commit can stay scoped. For structural improvements, NOT style/formatting (use /lint). NOT for adding features or fixing bugs.\";\n\nexport const CURSOR_COMMAND_WRAPPERS: readonly CursorCommandWrapper[] = [\n {\n name: 'bdd',\n description:\n \"BDD orchestrator for feature-level work. Use when user says 'add', 'implement', 'build', 'feature', 'iteration', 'story', 'phase', 'resume', 'continue', or references a ticket/iteration/story. Also use when work touches 3+ files with new state/flows, or when user runs /bdd. Do NOT use for bug fixes, typos, config changes, or 1-2 file tasks.\",\n skillPath: 'bdd/SKILL.md',\n },\n {\n name: 'debug',\n description: DEBUG_DESCRIPTION,\n skillPath: 'debug/SKILL.md',\n },\n {\n name: 'quality-review',\n description:\n \"Deep code review with web research. Use when the user says 'double check against latest', 'verify versions', or 'check security'. Adds live research on top of the automatic quality hook — confirms you're using current library versions and APIs, not stale ones.\",\n skillPath: 'quality-review/SKILL.md',\n },\n {\n name: 'finish-review',\n description:\n \"Internal fallback used only right after the shared review coordinator reports it's run out of reviewer routes. Not something a user invokes directly.\",\n skillPath: 'finish-review/SKILL.md',\n },\n {\n name: 'audit',\n description: 'Run comprehensive code audit for architecture, dead code, and test quality',\n skillPath: 'audit/SKILL.md',\n },\n {\n name: 'closeout',\n description:\n \"Close a completed local delivery safely after verification. Use when wrapping up a finished coding session: merging only when you've been explicitly told to merge, running the retro, and cleaning up the exact merged branch and worktree.\",\n skillPath: 'closeout/SKILL.md',\n },\n {\n name: 'refactor',\n description: REFACTOR_DESCRIPTION,\n skillPath: 'refactor/SKILL.md',\n },\n {\n name: 'retro',\n description:\n 'Run a safeword retrospective on the current session on demand — mine the transcript for friction (bugs / rough edges / gaps) and submit it through the outbound safety check before filing upstream. Use when the user says \"run a retro\", \"/retro\", or wants to capture friction before the session ends.',\n skillPath: 'retro/SKILL.md',\n },\n {\n name: 'spike',\n description:\n 'Run a quick, throwaway experiment to answer one open technical question before committing to the real build. Only runs when you ask for it directly.',\n skillPath: 'spike/SKILL.md',\n },\n {\n name: 'testing',\n description: 'How to write good tests — quality knowledge for any testing context',\n skillPath: 'testing/SKILL.md',\n },\n] as const;\n\nexport const CURSOR_RULE_WRAPPERS: readonly CursorRuleWrapper[] = [\n {\n name: 'safeword-core',\n alwaysApply: true,\n referencePath: '.safeword/SAFEWORD.md',\n },\n {\n name: 'safeword-brainstorming',\n alwaysApply: false,\n description:\n \"Collaborative brainstorming and rubber-ducking — divergence-first thinking partner. Use when the user wants to explore options, weigh approaches, or think through uncertainty before committing to a direction ('brainstorm', 'rubber duck', 'help me think', 'explore options', 'what are the tradeoffs').\",\n referencePath: '.safeword/skills/brainstorm/SKILL.md',\n skill: 'brainstorm',\n },\n {\n name: 'safeword-debugging',\n alwaysApply: false,\n description: DEBUG_DESCRIPTION,\n referencePath: '.safeword/skills/debug/SKILL.md',\n skill: 'debug',\n },\n {\n name: 'safeword-elicitation',\n alwaysApply: false,\n description:\n \"Extract tacit knowledge through non-obvious microquestions — things only the user knows that can't be found in code, docs, or research. Use when about to guess at intent, context, or constraints, or when user says 'ask me', 'what do you need to know', 'what's missing'. Skips questions answerable by reading the codebase or searching the web.\",\n referencePath: '.safeword/skills/elicit/SKILL.md',\n skill: 'elicit',\n },\n {\n name: 'safeword-figure-it-out',\n alwaysApply: false,\n description:\n 'Explore and debate options with fresh documentation and research before committing. Use when facing a real decision with multiple plausible approaches — library/framework choice, architecture call, API or schema design, algorithm selection. Looks up current docs and recent research, weighs options on correctness and elegance, and resists bloat.',\n referencePath: '.safeword/skills/figure-it-out/SKILL.md',\n skill: 'figure-it-out',\n },\n {\n name: 'safeword-finish-review',\n alwaysApply: false,\n description:\n \"Internal fallback used only right after the shared review coordinator reports it's run out of reviewer routes. Not something a user invokes directly.\",\n referencePath: '.safeword/skills/finish-review/SKILL.md',\n skill: 'finish-review',\n },\n {\n name: 'safeword-quality-reviewing',\n alwaysApply: false,\n description:\n \"Deep quality review of any work-product — code, docs, specs, plans, decisions — with web research. Use when explicitly verifying against latest docs ('double check against latest', 'verify versions', 'check security'), pressure-testing correctness and elegance, or needing deeper analysis beyond the automatic hook. Fetches current sources, checks versions and claims, and weighs alternatives.\",\n referencePath: '.safeword/skills/quality-review/SKILL.md',\n skill: 'quality-review',\n },\n {\n name: 'safeword-refactoring',\n alwaysApply: false,\n description: REFACTOR_DESCRIPTION,\n referencePath: '.safeword/skills/refactor/SKILL.md',\n skill: 'refactor',\n },\n {\n name: 'safeword-tdd-review',\n alwaysApply: false,\n description:\n \"Step-aware quality review at TDD phase boundaries — review test quality after RED, implementation correctness after GREEN, completed scenario after REFACTOR. Use when user says 'review my test', 'review my implementation', 'is this GREEN solid?', or just finished a TDD step. (Note: Claude Code triggers this automatically via the phase-tracking hook; Cursor users must invoke explicitly.)\",\n referencePath: '.safeword/skills/tdd-review/SKILL.md',\n skill: 'tdd-review',\n },\n {\n name: 'safeword-testing',\n alwaysApply: false,\n description:\n \"How to write good tests. Use when writing tests in any context — 'write tests', 'add tests', 'test this', 'need tests for', 'improve coverage'. Also consult when writing tests during BDD or debugging. Core knowledge for test quality across all workflows.\",\n referencePath: '.safeword/skills/testing/SKILL.md',\n skill: 'testing',\n },\n {\n name: 'safeword-ticket-system',\n alwaysApply: false,\n description:\n 'Ticket system and work logs for context anchoring during complex work. Use when creating tickets, managing work logs, referencing ticket IDs, or when work needs context anchoring (multi-step tasks, debugging, investigation).',\n globs: ['.project/tickets/**', '.safeword-project/tickets/**', '.safeword/logs/**'],\n referencePath: '.safeword/skills/ticket-system/SKILL.md',\n skill: 'ticket-system',\n },\n {\n name: 'safeword-retro-filer',\n alwaysApply: false,\n description:\n \"Files Safeword's sanitized spooled retrospective drafts to its upstream tracker. Use only when a trusted Safeword Stop continuation or authenticated closeout cleanup guard output names a spool path. Do not use for ordinary retros, project issues, or user-authored drafts.\",\n referencePath: '.safeword/skills/retro-filer/SKILL.md',\n skill: 'retro-filer',\n },\n {\n name: 'bdd-core',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n \"USE WHEN starting feature work, running /bdd, resuming a BDD ticket, or user says 'add', 'implement', 'build', 'feature', 'resume', 'continue'. Orchestrates BDD phases.\",\n referencePath: '.safeword/skills/bdd/SKILL.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-discovery',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n 'USE WHEN in BDD intake phase OR ticket has phase:intake. Guides discovery and context gathering for features.',\n referencePath: '.safeword/skills/bdd/DISCOVERY.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-scenarios',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n 'USE WHEN in BDD define-behavior or scenario-gate phase. Guides Given/When/Then scenario creation and validation.',\n referencePath: '.safeword/skills/bdd/SCENARIOS.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-plan-implementation',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n 'USE WHEN in BDD plan-implementation phase. Author impl-plan.md, run figure-it-out on load-bearing choices, ADR lifecycle, review before implement.',\n referencePath: '.safeword/skills/bdd/PLAN_IMPLEMENTATION.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-tdd',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description: 'USE WHEN in BDD implement phase. RED/GREEN TDD cycle; run /refactor after GREEN.',\n referencePath: '.safeword/skills/bdd/TDD.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-verify',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n 'USE WHEN in BDD verify phase OR all scenarios marked [x]. Evidence gate — cross-scenario refactor, then /verify and /audit.',\n referencePath: '.safeword/skills/bdd/VERIFY.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-done',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n 'USE WHEN in BDD done phase (verify.md exists). Close the ticket — verification already happened in verify.',\n referencePath: '.safeword/skills/bdd/DONE.md',\n skill: 'bdd',\n },\n {\n name: 'bdd-splitting',\n alwaysApply: false,\n frontmatterOrder: 'description-first',\n description:\n 'USE WHEN BDD thresholds exceeded (2+ stories, >15 scenarios, >20 tasks). Split protocol and examples.',\n referencePath: '.safeword/skills/bdd/SPLITTING.md',\n skill: 'bdd',\n },\n] as const;\n\nfunction cursorRuleSkillPairs(): SkillCursorPair[] {\n const skillRules = new Map<string, string[]>();\n\n for (const wrapper of CURSOR_RULE_WRAPPERS) {\n if (wrapper.skill === undefined) continue;\n\n const rules = skillRules.get(wrapper.skill);\n if (rules === undefined) {\n skillRules.set(wrapper.skill, [wrapper.name]);\n continue;\n }\n\n rules.push(wrapper.name);\n }\n\n return [...skillRules].map(([skill, cursorRules]) => ({ skill, cursorRules }));\n}\n\nexport const SKILL_CURSOR_PAIRS: readonly SkillCursorPair[] = [\n ...cursorRuleSkillPairs(),\n ...CURSOR_ACTION_SKILLS.map(skill => ({ skill, cursorRules: undefined })),\n];\n\nfunction renderFrontmatter({ fields }: { readonly fields: readonly FrontmatterField[] }): string {\n const lines = fields.flatMap(field => {\n if (Array.isArray(field.value)) {\n return [`${field.key}:`, ...field.value.map(item => ` - ${item}`)];\n }\n\n return [`${field.key}: ${field.value}`];\n });\n\n return `---\\n${lines.join('\\n')}\\n---`;\n}\n\nexport function renderCursorCommandWrapper({\n wrapper,\n}: {\n readonly wrapper: CursorCommandWrapper;\n}): string {\n return `${renderFrontmatter({\n fields: [{ key: 'description', value: wrapper.description }],\n })}\n\nRead and follow the instructions in .safeword/skills/${wrapper.skillPath}\n`;\n}\n\nexport function renderCursorRuleWrapper({\n wrapper,\n}: {\n readonly wrapper: CursorRuleWrapper;\n}): string {\n const fields: FrontmatterField[] = [];\n if (wrapper.frontmatterOrder === 'description-first' && wrapper.description !== undefined) {\n fields.push({ key: 'description', value: wrapper.description });\n }\n\n fields.push({ key: 'alwaysApply', value: wrapper.alwaysApply });\n\n if (wrapper.frontmatterOrder !== 'description-first' && wrapper.description !== undefined) {\n fields.push({ key: 'description', value: wrapper.description });\n }\n\n if (wrapper.globs !== undefined) {\n fields.push({ key: 'globs', value: wrapper.globs });\n }\n\n return `${renderFrontmatter({ fields })}\n\n@${wrapper.referencePath}\n`;\n}\n","/**\n * Content templates - static string content\n *\n * Note: Most templates (SAFEWORD.md, hooks, skills, guides, etc.) are now\n * file-based in the templates/ directory. This file contains only small\n * string constants that are used inline.\n */\n\n/**\n * Legacy prose reference for AGENTS.md. Kept only so upgrade/reset can remove\n * old safeword-managed blocks from customer-owned context files.\n */\nexport const AGENTS_MD_LINK = `**⚠️ ALWAYS READ FIRST:** \\`.safeword/SAFEWORD.md\\`\n\nThe SAFEWORD.md file contains core development patterns, workflows, and conventions.\nRead it BEFORE working on any task in this project.\n\n---\n\n`;\n\n/**\n * Legacy import block for CLAUDE.md. Kept only so upgrade/reset can remove old\n * safeword-managed imports from customer-owned context files.\n */\nexport const CLAUDE_MD_IMPORT_BLOCK = `@./.safeword/SAFEWORD.md\n\n---\n\n`;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA,OAAO,cAAc;AAqBd,IAAM,uBAA0C;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,SAAS,0BAA0B,oBAAgD;AACxF,SAAO,uBAAuB,UAAa,CAAC,qBAAqB,SAAS,kBAAkB,IACxF,CAAC,GAAG,sBAAsB,kBAAkB,IAC5C;AACN;AASO,SAAS,2BAA2B,KAAyC;AAClF,QAAM,QAAQ,2BAA2B,GAAG;AAI5C,MACE,UAAU,OACV,UAAU,QACV,MAAM,WAAW,KAAK,KACtB,qBAAqB,SAAS,KAAK,GACnC;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASO,SAAS,2BAA2B,KAA6B;AACtE,QAAM,OAAO,IAAI,iBAAiB,qBAAqB,IAAI,GAAG;AAC9D,UAAQ,SAAS,SAAS,IAAI,KAAK,IAAI,KAAK,KAAK,WAAW,SAAS,KAAK,GAAG;AAC/E;AAOO,SAAS,0BAA0B,KAAwC;AAChF,SAAO,0BAA0B,2BAA2B,GAAG,CAAC;AAClE;AAGO,SAAS,4BAA4B,KAAwC;AAClF,QAAM,SAAS,2BAA2B,GAAG;AAC7C,SAAO,WAAW,SACd,CAAC,YAAY,mBAAmB,IAChC,CAAC,YAAY,qBAAqB,MAAM;AAC9C;AAaO,SAAS,oBACd,OACA,mBAA4C,SAAO,GAAG,GAAG,OACpC;AACrB,SAAO;AAAA,IACL,MAAM,CAAC,KAAK;AAAA,IACZ,eAAe;AAAA,IACf,OAAO,CAAC,UAAU,QAAQ;AACxB,YAAM,gBAAgB,0BAA0B,GAAG,EAAE,IAAI,SAAO,iBAAiB,GAAG,CAAC;AACrF,YAAM,UAAU,MAAM,QAAQ,SAAS,KAAK,CAAC,IAAK,SAAS,KAAK,IAAiB,CAAC;AAClF,YAAM,SAAS,CAAC,GAAG,OAAO;AAC1B,iBAAW,QAAQ,eAAe;AAChC,YAAI,CAAC,OAAO,SAAS,IAAI,EAAG,QAAO,KAAK,IAAI;AAAA,MAC9C;AACA,aAAO,EAAE,GAAG,UAAU,CAAC,KAAK,GAAG,OAAO;AAAA,IACxC;AAAA,IACA,SAAS,CAAC,UAAU,QAAQ;AAC1B,YAAM,gBAAgB,IAAI;AAAA,QACxB,0BAA0B,GAAG,EAAE,IAAI,SAAO,iBAAiB,GAAG,CAAC;AAAA,MACjE;AACA,YAAM,eAAe,IAAI;AAAA,QACvB,4BAA4B,GAAG,EAAE,IAAI,SAAO,iBAAiB,GAAG,CAAC;AAAA,MACnE;AACA,YAAM,UAAU,MAAM,QAAQ,SAAS,KAAK,CAAC,IAAK,SAAS,KAAK,IAAiB,CAAC;AAClF,YAAM,UAAU,QAAQ,OAAO,WAAS,CAAC,cAAc,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,CAAC;AAG5F,YAAM,OAAO,OAAO,YAAY,OAAO,QAAQ,QAAQ,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,KAAK,CAAC;AACzF,aAAO,QAAQ,SAAS,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,GAAG,QAAQ,IAAI;AAAA,IAC9D;AAAA,EACF;AACF;AAEO,SAAS,4BAA4B,QAA2C;AACrF,QAAM,WAAW;AAAA,IACf,GAAG,OAAO,KAAK,OAAO,UAAU;AAAA,IAChC,GAAG,OAAO,KAAK,OAAO,YAAY;AAAA,IAClC,GAAG,OAAO,KAAK,OAAO,UAAU;AAAA,IAChC,GAAG,OAAO,KAAK,OAAO,WAAW;AAAA,EACnC;AAEA,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,QAAQ,UAAU;AAC3B,UAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,aAAS,IAAI,eAAe,KAAK,OAAO,KAAK,MAAM,GAAG,aAAa,CAAC,CAAC;AAAA,EACvE;AAEA,SAAO,CAAC,GAAG,QAAQ,EAAE,SAAS,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAC5D;AA+BO,SAAS,yBACd,QACA,oBACQ;AAKR,QAAM,eAAe,uBAAuB,SAAY,CAAC,IAAI,CAAC,GAAG,kBAAkB,GAAG;AACtF,QAAM,WAAW;AAAA,IACf,GAAG,oBAAI,IAAI;AAAA,MACT,GAAG,4BAA4B,MAAM,EAAE;AAAA,QAAQ,YAC7C,WAAW,uBAAuB,CAAC,sBAAsB,WAAW,IAAI,CAAC,MAAM;AAAA,MACjF;AAAA,MACA,GAAG;AAAA,IACL,CAAC;AAAA,EACH;AACA,QAAM,UAAU,SAAS,IAAI,YAAU,MAAM,MAAM,IAAI,EAAE,KAAK,IAAI;AAElE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BT;;;ACrPA,OAAOA,eAAc;;;ACArB,SAAS,oBAAoB;AAC7B,OAAOC,eAAc;AAErB,OAAO,UAAU;AAiBjB,IAAM,kBAAkB;AAAA;AAAA,EAEtB;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAWA,IAAM,mBAAmB;AAAA,EACvB;AAAA;AACF;AAGA,IAAM,6BAA6B,CAAC,sBAAsB,wBAAwB;AAGlF,IAAM,oBAA6D;AAAA,EACjE,QAAQ,EAAE,kBAAkB,EAAE;AAAA,EAC9B,KAAK,EAAE,eAAe,IAAI;AAAA,EAC1B,UAAU,EAAE,kBAAkB,EAAE,WAAW,GAAG,EAAE;AAAA,EAChD,WAAW;AAAA,IACT,SAAS;AAAA,IACT,QAAQ,CAAC,SAAS;AAAA,IAClB,SAAS,EAAE,WAAW,EAAE,QAAQ,IAAI,EAAE;AAAA,EACxC;AACF;AAcA,SAAS,wBAAiD;AACxD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,MACP,SAAS;AAAA,MACT,QAAQ,CAAC,GAAG,eAAe;AAAA,MAC3B,SAAS,CAAC,GAAG,gBAAgB;AAAA,MAC7B,YAAY,EAAE,SAAS,CAAC,GAAG,0BAA0B,EAAE;AAAA,MACvD,UAAU,gBAAgB,iBAAiB;AAAA,IAC7C;AAAA,IACA,YAAY,EAAE,QAAQ,CAAC,SAAS,EAAE;AAAA,EACpC;AACF;AAMA,SAAS,yBAAiC;AACxC,QAAM,SAAS;AAAA;AAAA;AAAA;AAIf,SAAO,SAAS,KAAK,UAAU,sBAAsB,CAAC;AACxD;AAQA,SAAS,+BAA+B,gBAAoC,KAAqB;AAC/F,MAAI,CAAC,gBAAgB;AACnB,WAAO,8BAA8B;AAAA,EACvC;AAEA,QAAM,aAAaC,UAAS,KAAK,KAAK,cAAc;AACpD,MAAI,CAAC,OAAO,UAAU,GAAG;AACvB,WAAO,8BAA8B;AAAA,EACvC;AAEA,MAAI;AACF,UAAM,iBAAiB,aAAa,YAAY,MAAM;AACtD,UAAM,gBAAgB,KAAK,MAAM,cAAc;AAE/C,QAAI,CAAC,eAAe;AAClB,aAAO,8BAA8B;AAAA,IACvC;AAEA,UAAM,QACJ,cAAc,YAAY,MAAM,8BAA8B;AAChE,WAAO,MAAM,aAAa;AAAA,EAC5B,QAAQ;AACN,SAAK,6BAA6B,cAAc,2BAA2B;AAC3E,WAAO,8BAA8B;AAAA,EACvC;AACF;AAGA,SAAS,gCAAwC;AAC/C,QAAM,SAAS;AAAA;AAAA;AAAA;AAIf,SAAO,SAAS,KAAK,UAAU,sBAAsB,CAAC;AACxD;AAOA,SAAS,aAAa,gBAAkE;AACtF,QAAM,qBAAsB,eAAe,cAA0C,CAAC;AACtF,QAAM,kBAAmB,mBAAmB,WAAwB,CAAC;AACrE,QAAM,mBAAoB,eAAe,YAAwC,CAAC;AAElF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,eAAe,WAAW;AAAA,IACnC,QAAQ,YAAa,eAAe,UAAuB,CAAC,GAAG,eAAe;AAAA,IAC9E,SAAS,YAAa,eAAe,WAAwB,CAAC,GAAG,gBAAgB;AAAA,IACjF,YAAY;AAAA,MACV,GAAG;AAAA,MACH,SAAS,YAAY,iBAAiB,0BAA0B;AAAA,IAClE;AAAA,IACA,UAAU,aAAa,kBAAkB,iBAAiB;AAAA,EAC5D;AACF;AAGA,SAAS,gBAAgB,mBAAqE;AAC5F,QAAM,SAAU,kBAAkB,UAAuB,CAAC;AAC1D,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,OAAO,SAAS,SAAS,IAAI,SAAS,CAAC,GAAG,QAAQ,SAAS;AAAA,EACrE;AACF;AAOA,SAAS,4BAA4B,eAAgD;AACnF,QAAM,SAAkC;AAAA,IACtC,GAAG;AAAA,IACH,SAAS;AAAA,IACT,SAAS,aAAc,cAAc,WAAuC,CAAC,CAAC;AAAA,IAC9E,YAAY,gBAAiB,cAAc,cAA0C,CAAC,CAAC;AAAA,EACzF;AAEA,QAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASf,SAAO,SAAS,KAAK,UAAU,MAAM;AACvC;AAOA,SAAS,4BAA4B,eAAgD;AACnF,QAAM,SAAkC,EAAE,GAAG,cAAc;AAC3D,QAAM,iBAAkB,cAAc,WAAuC,CAAC;AAE9E,SAAO,UAAU;AAAA,IACf,GAAG;AAAA,IACH,QAAQ,YAAa,eAAe,UAAuB,CAAC,GAAG,eAAe;AAAA,IAC9E,SAAS,YAAa,eAAe,WAAwB,CAAC,GAAG,gBAAgB;AAAA,EACnF;AAEA,QAAM,kBAAmB,cAAc,kBAAkB,KAAiC,CAAC;AAC3F,SAAO,kBAAkB,IAAI,aAAa,iBAAiB;AAAA,IACzD,QAAQ,EAAE,kBAAkB,EAAE;AAAA,IAC9B,SAAS,EAAE,eAAe,KAAK;AAAA,EACjC,CAAC;AAED,QAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAOf,SAAO,SAAS,KAAK,UAAU,MAAM;AACvC;AAOA,SAAS,YAAY,MAAgB,WAA+B;AAClE,QAAM,MAAM,IAAI,IAAI,IAAI;AACxB,aAAW,QAAQ,WAAW;AAC5B,QAAI,IAAI,IAAI;AAAA,EACd;AACA,SAAO,CAAC,GAAG,GAAG;AAChB;AAMA,SAAS,aACP,MACA,UACyB;AACzB,QAAM,SAAkC,EAAE,GAAG,KAAK;AAElD,aAAW,CAAC,KAAK,YAAY,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC1D,QAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAC9B,YAAM,gBAAgB,OAAO,GAAG;AAChC,UACE,gBACA,OAAO,iBAAiB,YACxB,CAAC,MAAM,QAAQ,YAAY,KAC3B,iBACA,OAAO,kBAAkB,YACzB,CAAC,MAAM,QAAQ,aAAa,GAC5B;AACA,eAAO,GAAG,IAAI;AAAA,UACZ;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;AAMO,IAAM,mBAAmD;AAAA,EAC9D,2BAA2B;AAAA,IACzB,WAAW,SACT,IAAI,WAAW,SACX,+BAA+B,IAAI,YAAY,wBAAwB,IAAI,GAAG,IAC9E;AAAA,EACR;AACF;AAMO,IAAM,qBAA4D;AAAA,EACvE,iBAAiB;AAAA,IACf,WAAW,SACT,CAAC,IAAI,YAAY,0BAA0B,IAAI,WAAW,SACtD,uBAAuB,IACvB;AAAA,EACR;AACF;;;AC9TA,SAAS,YAAY,gBAAAC,qBAAoB;AACzC,OAAOC,eAAc;AAarB,SAAS,4BAA4B,KAAsB;AACzD,QAAM,WAAWC,UAAS,KAAK,KAAK,WAAW;AAC/C,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAClC,MAAI;AACF,UAAM,UAAUC,cAAa,UAAU,MAAM;AAC7C,WAAO,QAAQ,SAAS,gCAAgC;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYA,IAAM,iBAAiB;AAAA,EACrB;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiB7B,IAAM,uBAAuB;AAAA,YACjB,cAAc;AAAA,EACxB,oBAAoB;AAOtB,IAAM,qBAAqB;AAAA,mBACR,cAAc;AAAA,EAC/B,oBAAoB;AAgBtB,SAAS,uBACP,KACA,qBAAiE,aACzD;AAKR,MAAI,4BAA4B,GAAG,GAAG;AACpC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,oBAAoB;AAAA;AAAA,EAEpB;AAMA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BASkB,kBAAkB;AAAA,eAC9B,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAI/B,kBAAkB;AAAA;AAEpB;AAUO,IAAM,mBAAmD;AAAA;AAAA,EAE9D,uBAAuB;AAAA,IACrB,WAAW,SACT,IAAI,WAAW,SACX,uBAAuB,IAAI,KAAK,IAAI,YAAY,kBAAkB,IAClE;AAAA,EACR;AACF;AAcA,SAAS,4BAAoC;AAC3C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQT;AAMA,SAAS,4BAAoC;AAC3C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUT;AAQA,SAAS,kCAAkC,QAAkB,aAA6B;AACxF,MAAI,OAAO,SAAS,EAAG,QAAO;AAE9B,QAAM,YAAY,OAAO,IAAI,OAAK,OAAO,WAAW,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AAEtE,SAAO;AAAA;AAAA;AAAA;AAAA,kBAIS,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM3B,SAAS;AAAA;AAEX;AAUA,SAAS,kCAAkC,aAA6B;AACtE,SAAO;AAAA;AAAA;AAAA;AAAA,iBAIQ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,cAKd,WAAW;AAAA;AAEzB;AAMO,IAAM,qBAA4D;AAAA;AAAA,EAEvE,aAAa;AAAA,IACX,WAAW,SACT,IAAI,WAAW,UAAU,CAAC,IAAI,YAAY,qBACtC,0BAA0B,IAC1B;AAAA,EACR;AAAA;AAAA,EAGA,YAAY;AAAA,IACV,WAAW,SACT,IAAI,WAAW,UAAU,CAAC,IAAI,YAAY,qBACtC,0BAA0B,IAC1B;AAAA,EACR;AAAA;AAAA;AAAA,EAIA,iBAAiB;AAAA;AAAA,IAEf,WAAW,SAAO;AAChB,UAAI,CAAC,IAAI,WAAW,OAAQ;AAC5B,UAAI,IAAI,YAAY,2BAA4B;AAChD,aAAO,4BAA4B,IAAI,GAAG;AAAA,IAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,oBAAoB,SAClB,IAAI,WAAW,SAAS,4BAA4B,IAAI,GAAG,IAAI;AAAA,EACnE;AACF;AAQO,SAAS,8BAA8B,KAAsB;AAClE,SAAO,4BAA4B,GAAG,MAAM;AAC9C;AASA,SAAS,4BAA4B,KAAiC;AACpE,QAAM,SAAS,mBAAmB,GAAG;AACrC,MAAI,OAAO,UAAU,GAAG;AACtB,WAAO,kCAAkC,QAAQ,kBAAkB,GAAG,CAAC;AAAA,EACzE;AACA,QAAM,cAAc,kBAAkB,GAAG;AACzC,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,kCAAkC,WAAW;AACtD;;;AChUA,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,OAAOC,eAAc;AAcrB,IAAM,6BAAqD;AAAA,EACzD,kCAAkC;AAAA,EAClC,gCAAgC;AAAA,EAChC,4BAA4B;AAAA,EAC5B,6BAA6B;AAC/B;AAOA,SAAS,uBAA+B;AACtC,QAAM,aAAa,OAAO,QAAQ,0BAA0B,EACzD,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM,OAAO,KAAK,CAAC,EAAE,EACjD,KAAK,IAAI;AAEZ,SAAO;AAAA;AAAA;AAAA,EAGP,UAAU;AAAA;AAEZ;AAMA,SAAS,6BAA6B,gBAAoC,KAAqB;AAC7F,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA;AAAA;AAAA,EAGT,yBAAyB,CAAC;AAAA;AAAA,EAE1B;AAEA,QAAM,aAAaA,UAAS,KAAK,KAAK,cAAc;AACpD,MAAI,CAACF,YAAW,UAAU,GAAG;AAC3B,WAAO;AAAA;AAAA;AAAA,EAGT,yBAAyB,CAAC;AAAA;AAAA,EAE1B;AAEA,MAAI;AACF,UAAM,UAAUC,cAAa,YAAY,MAAM;AAC/C,UAAM,eAAe,gBAAgB,OAAO;AAG5C,UAAM,aAAa,OAAO,QAAQ,0BAA0B,EACzD,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,aAAa,IAAI,GAAG,CAAC,EACxC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM,OAAO,KAAK,CAAC,EAAE;AAGpD,UAAM,kBACJ,WAAW,SAAS,IAChB;AAAA;AAAA,EAAsD,WAAW,KAAK,IAAI,CAAC;AAAA,IAC3E;AAEN,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,QAAQ,KAAK,CAAC;AAAA,EACd,eAAe;AAAA,EACf,QAAQ;AACN,WAAO;AAAA;AAAA;AAAA,EAGT,yBAAyB,CAAC;AAAA;AAAA,EAE1B;AACF;AAGA,SAAS,2BAAmC;AAC1C,SAAO,OAAO,QAAQ,0BAA0B,EAC7C,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,MAAM,OAAO,KAAK,CAAC,EAAE,EACjD,KAAK,IAAI;AACd;AAGA,SAAS,gBAAgB,SAA8B;AACrD,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,WAAW,CAAC,QAAQ,WAAW,GAAG,GAAG;AACvC,YAAM,QAAQ,gBAAgB,KAAK,OAAO;AAC1C,UAAI,QAAQ,CAAC,EAAG,MAAK,IAAI,MAAM,CAAC,CAAC;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;AAUA,IAAM,wBAAwB;AAQ9B,IAAM,wBAAwB;AAQ9B,SAAS,iBAAiB,SAAiB,QAAoC;AAC7E,QAAM,QAAQ,QAAQ,QAAQ,MAAM;AACpC,MAAI,UAAU,GAAI,QAAO;AAEzB,QAAM,OAAO,QAAQ,MAAM,QAAQ,OAAO,MAAM;AAChD,QAAM,aAAa,KAAK,OAAO,YAAY;AAC3C,QAAM,UAAU,eAAe,KAAK,OAAO,KAAK,MAAM,GAAG,UAAU;AAEnE,aAAW,WAAW,QAAQ,MAAM,IAAI,GAAG;AACzC,UAAM,OAAO,QAAQ,QAAQ,OAAO,EAAE,EAAE,KAAK;AAC7C,UAAM,QAAQ,kCAAkC,KAAK,IAAI;AACzD,QAAI,QAAQ,CAAC,EAAG,QAAO,MAAM,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAiBO,SAAS,mBAAmB,KAAqB;AACtD,MAAI;AACJ,MAAI;AACF,cAAUA,cAAaC,UAAS,KAAK,KAAK,YAAY,GAAG,MAAM;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,iBAAiB,SAAS,WAAW;AAC5D,MAAI,eAAgB,QAAO;AAE3B,QAAM,mBAAmB,iBAAiB,SAAS,qBAAqB;AACxE,MAAI,iBAAkB,QAAO;AAK7B,MAAI,QAAQ,SAAS,WAAW,EAAG,QAAO;AAE1C,SAAO;AACT;AAiBA,SAAS,qBAA6B;AACpC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkDT;AAEA,SAAS,sBAAsB,KAAqB;AAClD,QAAM,UAAU,mBAAmB,GAAG;AACtC,SAAO;AAAA,uDAC8C,qBAAqB;AAAA;AAAA;AAAA;AAAA,aAI/D,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOpB;AAMO,IAAM,iBAAiD;AAAA,EAC5D,yBAAyB;AAAA,IACvB,WAAW,SACT,IAAI,WAAW,OACX,6BAA6B,IAAI,YAAY,sBAAsB,IAAI,GAAG,IAC1E;AAAA,EACR;AAAA,EACA,0BAA0B;AAAA,IACxB,WAAW,SAAQ,IAAI,WAAW,OAAO,sBAAsB,IAAI,GAAG,IAAI;AAAA,EAC5E;AACF;AAMO,IAAM,mBAA0D;AAAA,EACrE,eAAe;AAAA,IACb,WAAW,SACT,IAAI,WAAW,QAAQ,CAAC,IAAI,YAAY,uBACpC,qBAAqB,IACrB;AAAA,EACR;AAAA,EACA,gBAAgB;AAAA,IACd,WAAW,SACT,IAAI,WAAW,QAAQ,CAAC,IAAI,YAAY,wBACpC,sBAAsB,IAAI,GAAG,IAC7B;AAAA,EACR;AAAA;AAAA;AAAA,EAGA,aAAa;AAAA,IACX,WAAW,SAAQ,IAAI,WAAW,OAAO,mBAAmB,IAAI;AAAA,EAClE;AACF;;;ACzTA,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,OAAOC,eAAc;AACrB,OAAO,aAAa;AAEpB,OAAOC,WAAU;AAKjB,IAAM,qBAA6C;AAAA,EACjD,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW;AAAA,EACX,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EAER,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA;AAAA,EAET,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AAAA,EACT,aAAa;AACf;AAGA,IAAM,oBAA4C;AAAA,EAChD,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,OAAO;AACT;AAGA,IAAM,qBAA6C;AAAA,EACjD,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,aAAa;AAAA,EACb,OAAO;AAAA,EACP,KAAK;AAAA,EACL,WAAW;AACb;AAGA,IAAM,sBAA8C;AAAA,EAClD,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,aAAa;AACf;AASO,SAAS,iBAAiB,KAAiC;AAChE,SACE,mBAAmB,GAAG,KACtB,6BAA6B,GAAG,KAChC,qBAAqB,GAAG,KACxB,uBAAuB,GAAG,KAC1B,wBAAwB,GAAG,KAC3B,sBAAsB,GAAG;AAE7B;AAGA,SAAS,mBAAmB,cAAsB,aAAyC;AACzF,MAAI,CAACC,YAAW,YAAY,EAAG,QAAO;AAEtC,QAAM,UAAUC,cAAa,cAAc,MAAM;AACjD,QAAM,WAAWC,MAAK,MAAM,SAAS,EAAE,QAAQ,WAAW,CAAC;AAC3D,QAAM,UAAU,SAAS,WAAW;AACpC,MAAI,CAAC,SAAS,QAAS,QAAO;AAE9B,QAAM,UAAU,QAAQ;AACxB,QAAM,aAAa,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS,OAAO,KAAK,OAAO,EAAE,CAAC;AAC/F,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,SAAS,QAAQ,UAAU;AACjC,SAAO,OAAO,QAAQ,SAAS,WAAW,OAAO,OAAO;AAC1D;AAGA,SAAS,mBAAmB,KAAiC;AAC3D,QAAM,sBAAsB,WAAW,KAAK,iBAAiB;AAC7D,MAAI,CAAC,oBAAqB,QAAO;AAEjC,MAAI;AACF,UAAM,iBAAiBD;AAAA,MACrBE,UAAS,KAAK,qBAAqB,iBAAiB;AAAA,MACpD;AAAA,IACF;AACA,UAAM,UAAUD,MAAK,MAAM,gBAAgB,EAAE,QAAQ,WAAW,CAAC;AACjE,UAAM,cAAc,QAAQ;AAC5B,QAAI,OAAO,gBAAgB,SAAU,QAAO;AAG5C,UAAM,aAAa;AAAA,MACjBC,UAAS,KAAK,qBAAqB,cAAc;AAAA,MACjDA,UAAS,KAAK,qBAAqB,OAAO,cAAc;AAAA,MACxDA,UAAS,KAAK,QAAQ,IAAI,QAAQ,IAAI,QAAQ,cAAc;AAAA,IAC9D;AAEA,eAAW,gBAAgB,YAAY;AACrC,YAAM,cAAc,mBAAmB,cAAc,WAAW;AAChE,UAAI,YAAa,QAAO,mBAAmB,WAAW;AAAA,IACxD;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,6BAA6B,KAAiC;AACrE,QAAM,YAAY,WAAW,KAAK,kBAAkB,KAAK,WAAW,KAAK,gBAAgB;AACzF,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,mBAAmBA,UAAS,KAAK,WAAW,kBAAkB;AACpE,QAAM,gBAAgBA,UAAS,KAAK,WAAW,gBAAgB;AAE/D,MAAI;AACF,QAAI;AACJ,QAAIH,YAAW,gBAAgB,GAAG;AAChC,gBAAUC,cAAa,kBAAkB,MAAM;AAAA,IACjD,WAAWD,YAAW,aAAa,GAAG;AACpC,gBAAUC,cAAa,eAAe,MAAM;AAAA,IAC9C;AACA,QAAI,CAAC,QAAS,QAAO;AAGrB,UAAM,WAAW,QACd,SAAS,YAAY,EACrB,IAAI,OAAK,EAAE,CAAC,CAAC,EACb,OAAO,CAAC,MAAmB,MAAM,UAAa,MAAM,MAAM,EAC1D,QAAQ;AACX,eAAW,WAAW,UAAU;AAC9B,YAAM,UAAU,mBAAmB,OAAO;AAC1C,UAAI,QAAS,QAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,qBAAqB,KAAiC;AAC7D,aAAW,YAAY,CAAC,aAAa,YAAY,WAAW,GAAG;AAC7D,UAAM,YAAY,WAAW,KAAK,QAAQ;AAC1C,QAAI,CAAC,UAAW;AAEhB,QAAI;AACF,YAAM,UAAUA,cAAaE,UAAS,KAAK,WAAW,QAAQ,GAAG,MAAM;AACvE,YAAM,QAAQ,sBAAsB,KAAK,OAAO;AAChD,UAAI,QAAQ,CAAC,EAAG,QAAO,mBAAmB,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;AAAA,IAChE,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,uBAAuB,KAAiC;AAC/D,QAAM,aAAaA,UAAS,KAAK,KAAK,UAAU,eAAe;AAC/D,MAAI,CAACH,YAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACF,UAAM,UAAUC,cAAa,YAAY,MAAM;AAC/C,UAAM,QAAQ,yBAAyB,KAAK,OAAO;AACnD,QAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAGxB,WAAO,oBAAoB,MAAM,CAAC,CAAC;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,wBAAwB,KAAiC;AAChE,aAAW,YAAY,CAAC,qBAAqB,qBAAqB,oBAAoB,GAAG;AACvF,UAAM,WAAWE,UAAS,KAAK,KAAK,QAAQ;AAC5C,QAAI,CAACH,YAAW,QAAQ,EAAG;AAE3B,QAAI;AACF,YAAM,UAAUC,cAAa,UAAU,MAAM;AAC7C,YAAM,QAAQ,8BAA8B,KAAK,OAAO;AACxD,UAAI,QAAQ,CAAC,EAAG,QAAO,mBAAmB,MAAM,CAAC,CAAC;AAAA,IACpD,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,sBAAsB,KAAiC;AAC9D,QAAM,UAAUE,UAAS,KAAK,KAAK,MAAM;AACzC,MAAI,CAACH,YAAW,OAAO,EAAG,QAAO;AAEjC,MAAI;AACF,UAAM,UAAUC,cAAa,SAAS,MAAM;AAC5C,UAAM,QAAQ,iCAAiC,KAAK,OAAO;AAC3D,QAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAExB,WAAO,kBAAkB,MAAM,CAAC,CAAC;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACzOA,SAAS,sBAAsB,KAA8B;AAC3D,SACE,yBAAyB,IAAI,mBAAmB,yBAAyB,IAAI;AAEjF;AAUA,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4B9B,SAAS,2BACP,wBACA,SACQ;AACR,MAAI,wBAAwB;AAG1B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,qBAAqB;AAAA;AAAA,EAErB;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA,YAIG,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQjB,qBAAqB;AAAA;AAEvB;AAMO,IAAM,gBAAgD;AAAA,EAC3D,0BAA0B;AAAA,IACxB,WAAW,SACT,IAAI,WAAW,OAAO,CAAC,sBAAsB,GAAG,IAC5C;AAAA,MACE,IAAI,YAAY;AAAA,MAChB,iBAAiB,IAAI,GAAG,KAAK;AAAA,IAC/B,IACA;AAAA,EACR;AACF;AAMA,SAAS,8BAA8B,SAAyB;AAC9D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,YAKG,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOnB;AAMO,IAAM,kBAAyD;AAAA,EACpE,aAAa;AAAA,IACX,WAAW,SACT,IAAI,WAAW,OAAO,CAAC,IAAI,YAAY,0BAA0B,CAAC,sBAAsB,GAAG,IACvF,8BAA8B,iBAAiB,IAAI,GAAG,KAAK,MAAM,IACjE;AAAA,EACR;AACF;;;ACnIO,SAAS,cACd,QACA,KACA,OACS;AACT,MAAI,OAAO,KAAK,KAAK,EAAE,SAAS,GAAG;AACjC,WAAO,GAAG,IAAI;AACd,WAAO;AAAA,EACT;AACA,UAAQ,eAAe,QAAQ,GAAG;AAClC,SAAO;AACT;;;ACKA,IAAM,+BAA+B;AASrC,SAAS,eAAe,QAA8B,KAA2B;AAC/E,QAAM,kBAAkB,IAAI;AAG5B,MAAI,IAAI,YAAY,OAAQ,QAAO,OAAO,KAAK,UAAU;AACzD,MAAI,IAAI,YAAY,MAAO,QAAO,OAAO,KAAK,WAAW;AACzD,MAAI,eAAe,mBAAmB,sBAAsB,iBAAiB;AAC3E,WAAO,OAAO,KAAK,iBAAiB,qBAAqB;AAAA,EAC3D;AACA,MAAI,WAAW,gBAAiB,QAAO,OAAO,KAAK,WAAW;AAG9D,MAAI,cAAc,mBAAmB,cAAc,IAAI,gBAAgB;AACrE,WAAO,OAAO,KAAK,eAAe,oBAAoB,QAAQ;AAAA,EAChE;AACF;AAKA,SAAS,0BACP,QACA,KACM;AACN,QAAM,kBAAkB,IAAI;AAG5B,MAAI,IAAI,YAAY,UAAU;AAC5B,WAAO,mBAAmB,KAAK,eAAe,2BAA2B,oBAAoB;AAAA,EAC/F;AACA,MAAI,IAAI,YAAY,WAAY,QAAO,mBAAmB,KAAK,kBAAkB;AACjF,MAAI,WAAW,gBAAiB,QAAO,mBAAmB,KAAK,OAAO;AACtE,MAAI,iBAAiB,gBAAiB,QAAO,mBAAmB,KAAK,aAAa;AAGlF,MAAI,cAAc,mBAAmB,cAAc,IAAI,gBAAgB;AACrE,WAAO,mBAAmB;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAAS,cAAc,KAA6B;AAMlD,QAAM,aAAa,2BAA2B,GAAG;AACjD,QAAM,SAAS;AAAA,IACb,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,eAAe,SAAY,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK;AAAA,IACzD;AAAA,IACA,oBAAoB,CAAC,YAAY,oBAAoB;AAAA,EACvD;AAEA,iBAAe,QAAQ,GAAG;AAC1B,4BAA0B,QAAQ,GAAG;AAErC,SAAO;AACT;AAKA,IAAM,oBAAoB;AAAA,EACxB,MAAM;AAAA,EACN,aAAa;AAAA,EACb,UAAU;AAAA,EACV,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,aAAa;AACf;AAMA,SAAS,mBAAmB,aAIf;AACX,QAAM,UAAoB,CAAC;AAC3B,MAAI,YAAY,MAAO,SAAQ,KAAK,uBAAuB;AAC3D,MAAI,YAAY,MAAO,SAAQ,KAAK,oBAAoB;AACxD,MAAI,YAAY,SAAU,SAAQ,KAAK,6BAA6B;AACpE,SAAO;AACT;AAMA,IAAM,mBAAwC;AAAA,EAC5C,MAAM,CAAC,gBAAgB;AAAA,EACvB,eAAe;AAAA;AAAA,EACf,OAAO,CAAC,UAAU,QAAQ;AACxB,UAAM,QAAS,SAAS,SAAqC,CAAC;AAC9D,UAAM,mBAAmB,MAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM,WAAW,CAAC;AAM3E,UAAM,mBAAmB;AAAA,MACvB;AAAA,MACA,GAAG,0BAA0B,GAAG,EAAE,IAAI,SAAO,IAAI,GAAG,EAAE;AAAA,IACxD;AACA,UAAM,cAAc,CAAC,GAAG,gBAAgB;AACxC,eAAW,WAAW,kBAAkB;AACtC,UAAI,CAAC,YAAY,SAAS,OAAO,GAAG;AAClC,oBAAY,KAAK,OAAO;AAAA,MAC1B;AAAA,IACF;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG;AAAA,QACH,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS,CAAC,UAAU,QAAQ;AAC1B,UAAM,QAAS,SAAS,SAAqC,CAAC;AAC9D,UAAM,mBAAmB,MAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM,WAAW,CAAC;AAK3E,UAAM,mBAAmB,oBAAI,IAAI;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,GAAG,0BAA0B,GAAG,EAAE,IAAI,SAAO,IAAI,GAAG,EAAE;AAAA,IACxD,CAAC;AACD,UAAM,kBAAkB,IAAI,IAAI,4BAA4B,GAAG,EAAE,IAAI,SAAO,IAAI,GAAG,EAAE,CAAC;AACtF,UAAM,kBAAkB,iBAAiB;AAAA,MACvC,CAAC,UAAkB,CAAC,iBAAiB,IAAI,KAAK,KAAK,gBAAgB,IAAI,KAAK;AAAA,IAC9E;AAGA,UAAM,eAAe,EAAE,GAAG,MAAM;AAChC,QAAI,gBAAgB,SAAS,GAAG;AAC9B,mBAAa,WAAW;AAAA,IAC1B,OAAO;AACL,aAAO,aAAa;AAAA,IACtB;AAGA,QAAI,OAAO,KAAK,YAAY,EAAE,WAAW,GAAG;AAC1C,YAAM,SAAS,EAAE,GAAG,SAAS;AAC7B,aAAO,OAAO;AACd,aAAO;AAAA,IACT;AAEA,WAAO,EAAE,GAAG,UAAU,OAAO,aAAa;AAAA,EAC5C;AACF;AAGA,IAAM,oBAAoB,oBAAoB,UAAU;AACxD,IAAM,mBAAmB,oBAAoB,gBAAgB;AAMtD,IAAM,uBAAuD;AAAA;AAAA;AAAA,EAGlE,+BAA+B;AAAA,IAC7B,WAAW,SACT,IAAI,WAAW,aACX;AAAA,MACE,IAAI,YAAY;AAAA,MAChB,IAAI,YAAY;AAAA,IAClB,IACA;AAAA,EACR;AAAA,EAEA,yBAAyB;AAAA;AAAA,IAEvB,WAAW,SAAO;AAIhB,UACE,CAAC,IAAI,WAAW,cAChB,IAAI,YAAY,qBAChB,IAAI,YAAY,wBAChB;AACA;AAAA,MACF;AAEA,YAAM,UAAU,mBAAmB,IAAI,WAAW;AAClD,YAAM,SAAS,QAAQ,SAAS,IAAI,EAAE,GAAG,mBAAmB,QAAQ,IAAI;AACxE,aAAO,KAAK,UAAU,QAAQ,QAAW,CAAC;AAAA,IAC5C;AAAA,EACF;AACF;AAMO,IAAM,yBAAgE;AAAA;AAAA,EAE3E,qBAAqB;AAAA;AAAA,IAEnB,WAAW,SAAO;AAEhB,UAAI,IAAI,YAAY,qBAAsB;AAC1C,UAAI,CAAC,IAAI,WAAW,WAAY;AAChC,aAAO,gBAAgB,IAAI,YAAY,iBAAiB;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA,EAEA,iBAAiB;AAAA;AAAA,IAEf,WAAW,SAAO;AAEhB,UAAI,CAAC,IAAI,WAAW,WAAY;AAEhC,UAAI,CAAC,IAAI,gBAAgB,cAAc,CAAC,IAAI,gBAAgB,mBAAmB,GAAG;AAChF;AAAA,MACF;AACA,aAAO,KAAK;AAAA,QACV;AAAA,UACE,iBAAiB;AAAA,YACf,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,kBAAkB;AAAA,YAClB,QAAQ;AAAA,YACR,iBAAiB;AAAA,YACjB,cAAc;AAAA,YACd,QAAQ;AAAA,UACV;AAAA,UACA,SAAS,CAAC,WAAW,UAAU;AAAA,UAC/B,SAAS,CAAC,gBAAgB,QAAQ,OAAO;AAAA,QAC3C;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,aAAa;AAAA,IACX,WAAW,SACT,IAAI,aAAa,cAAc,KAAK,UAAU,cAAc,GAAG,GAAG,QAAW,CAAC,IAAI;AAAA,EACtF;AAAA;AAAA,EAEA,eAAe;AAAA;AAAA,IAEb,WAAW,SAAO;AAIhB,UAAI,CAAC,IAAI,WAAW,WAAY;AAChC,UAAI,IAAI,YAAY,kBAAmB;AACvC,UAAI,IAAI,YAAY,uBAAwB;AAE5C,aAAO,KAAK,UAAU,mBAAmB,QAAW,CAAC;AAAA,IACvD;AAAA,EACF;AACF;AASA,SAAS,mBAAmB,SAAiC,MAAc,SAAuB;AAChG,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,CAAC,SAAU,SAAQ,IAAI,IAAI;AACjC;AAEA,IAAM,sBAAsB;AAE5B,SAAS,oBACP,SACA,MACA,eACwB;AACxB,SAAO,QAAQ,IAAI,MAAM,gBACrB,OAAO,YAAY,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,IAAI,CAAC,IAC1E;AACN;AAKA,SAAS,iBACP,SACA,aACM;AACN,qBAAmB,SAAS,gBAAgB,mBAAmB;AAC/D,MAAI,YAAY,gBAAgB;AAE9B,uBAAmB,SAAS,eAAe,UAAU;AAAA,EACvD,OAAO;AAEL,uBAAmB,SAAS,QAAQ,4BAA4B;AAAA,EAClE;AACF;AAKA,SAAS,mBACP,SACA,aACM;AACN,MAAI,YAAY,kBAAmB;AACnC,qBAAmB,SAAS,UAAU,oBAAoB;AAC1D,qBAAmB,SAAS,gBAAgB,oBAAoB;AAClE;AAEO,IAAM,uBAA4D;AAAA,EACvE,gBAAgB;AAAA,IACd,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,eAAe;AAAA;AAAA,IACf,iBAAiB;AAAA,MACf,gBAAgB,CAAC,qBAAqB;AAAA;AAAA,MACtC,oBAAoB,CAAC,iBAAiB;AAAA,MACtC,OAAO,CAAC,iBAAiB;AAAA,MACzB,aAAa,CAAC,cAAc;AAAA;AAAA,IAC9B;AAAA,IACA,OAAO,CAAC,UAAU,QAAQ;AACxB,YAAM,UAAU,EAAE,GAAI,SAAS,QAAmC;AAClE,YAAM,SAAS,EAAE,GAAG,SAAS;AAE7B,uBAAiB,SAAS,IAAI,WAAW;AACzC,yBAAmB,SAAS,IAAI,WAAW;AAE3C,UAAI,IAAI,YAAY,YAAa,oBAAmB,SAAS,QAAQ,MAAM;AAI3E,UAAI,IAAI,YAAY,iBAAiB;AACnC,2BAAmB,SAAS,YAAY,aAAa;AAAA,MACvD;AAGA,UAAI,IAAI,YAAY,oBAAoB;AACtC,2BAAmB,SAAS,WAAW,SAAS;AAAA,MAClD;AACA,UAAI,IAAI,YAAY,OAAO;AACzB;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,aAAO,UAAU;AACjB,aAAO;AAAA,IACT;AAAA,IACA,SAAS,CAAC,UAAU,SAAS;AAC3B,YAAM,SAAS,EAAE,GAAG,SAAS;AAC7B,UAAI,UAAU,EAAE,GAAI,SAAS,QAAmC;AAKhE,gBAAU,oBAAoB,SAAS,QAAQ,4BAA4B;AAC3E,gBAAU,oBAAoB,SAAS,gBAAgB,mBAAmB;AAC1E,gBAAU,oBAAoB,SAAS,YAAY,aAAa;AAEhE,oBAAc,QAAQ,WAAW,OAAO;AACxC,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe;AAAA,IACb,MAAM,CAAC,SAAS;AAAA,IAChB,eAAe;AAAA,IACf,OAAO,CAAC,UAAU,QAAQ;AAOxB,UAAI,IAAI,YAAY,uBAAwB,QAAO;AAEnD,YAAM,SAAS,EAAE,GAAG,SAAS;AAG7B,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,iBAAiB,GAAG;AAC5D,YAAI,OAAO,GAAG,MAAM,QAAW;AAC7B,iBAAO,GAAG,IAAI;AAAA,QAChB;AAAA,MACF;AAGA,YAAM,kBAAkB,mBAAmB,IAAI,WAAW;AAC1D,YAAM,kBAAkB,MAAM,QAAQ,OAAO,OAAO,IAAK,OAAO,UAAuB,CAAC;AAGxF,YAAM,aAAa,CAAC,GAAG,eAAe;AACtC,iBAAW,UAAU,iBAAiB;AACpC,YAAI,CAAC,WAAW,SAAS,MAAM,GAAG;AAChC,qBAAW,KAAK,MAAM;AAAA,QACxB;AAAA,MACF;AAGA,YAAM,gBAAgB,WAAW,QAAQ,6BAA6B;AACtE,UAAI,kBAAkB,MAAM,kBAAkB,WAAW,SAAS,GAAG;AACnE,mBAAW,OAAO,eAAe,CAAC;AAClC,mBAAW,KAAK,6BAA6B;AAAA,MAC/C;AAEA,UAAI,WAAW,SAAS,GAAG;AACzB,eAAO,UAAU;AAAA,MACnB,OAAO;AACL,eAAO,OAAO;AAAA,MAChB;AAEA,aAAO;AAAA,IACT;AAAA,IACA,SAAS,CAAC,UAAU,QAAQ;AAC1B,UAAI,IAAI,YAAY,uBAAwB,QAAO;AACnD,YAAM,SAAS,EAAE,GAAG,SAAS;AAE7B,YAAM,kBAAkB,IAAI,IAAI,mBAAmB,IAAI,WAAW,CAAC;AACnE,UAAI,MAAM,QAAQ,OAAO,OAAO,GAAG;AACjC,cAAM,YAAa,OAAO,QAAqB,OAAO,OAAK,CAAC,gBAAgB,IAAI,CAAC,CAAC;AAClF,YAAI,UAAU,SAAS,GAAG;AACxB,iBAAO,UAAU;AAAA,QACnB,OAAO;AACL,iBAAO,OAAO;AAAA,QAChB;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc;AAAA,EACd,eAAe;AAAA;AAAA,EAGf,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA;AAAA;AAAA,EAIjB,iBAAiB;AAAA,EACjB,kBAAkB;AACpB;AAWO,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAErB,IAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA;AAAA,IAEJ;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,EACF;AAAA,EACA,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMX,iBAAiB,CAAC,sBAAsB,OAAO,aAAa;AAAA;AAAA,IAE5D,UAAU,CAAC,UAAU;AAAA;AAAA;AAAA,IAErB,OAAO,CAAC,uBAAuB;AAAA,IAC/B,UAAU,CAAC,6BAA6B;AAAA,IACxC,OAAO,CAAC,oBAAoB;AAAA;AAAA,IAE5B,oBAAoB,CAAC,SAAS;AAAA;AAAA,IAE9B,cAAc,CAAC,kBAAkB;AAAA;AAAA;AAAA;AAAA,IAIjC,aAAa,CAAC,sBAAsB,MAAM;AAAA,EAC5C;AACF;;;AC3hBA,IAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,oBACJ;AAEF,IAAM,uBACJ;AAEK,IAAM,0BAA2D;AAAA,EACtE;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,EACb;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AACF;AAEO,IAAM,uBAAqD;AAAA,EAChE;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,IACb,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,IACb,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,OAAO,CAAC,uBAAuB,gCAAgC,mBAAmB;AAAA,IAClF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,aACE;AAAA,IACF,eAAe;AAAA,IACf,OAAO;AAAA,EACT;AACF;AAEA,SAAS,uBAA0C;AACjD,QAAM,aAAa,oBAAI,IAAsB;AAE7C,aAAW,WAAW,sBAAsB;AAC1C,QAAI,QAAQ,UAAU,OAAW;AAEjC,UAAM,QAAQ,WAAW,IAAI,QAAQ,KAAK;AAC1C,QAAI,UAAU,QAAW;AACvB,iBAAW,IAAI,QAAQ,OAAO,CAAC,QAAQ,IAAI,CAAC;AAC5C;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,IAAI;AAAA,EACzB;AAEA,SAAO,CAAC,GAAG,UAAU,EAAE,IAAI,CAAC,CAAC,OAAO,WAAW,OAAO,EAAE,OAAO,YAAY,EAAE;AAC/E;AAEO,IAAM,qBAAiD;AAAA,EAC5D,GAAG,qBAAqB;AAAA,EACxB,GAAG,qBAAqB,IAAI,YAAU,EAAE,OAAO,aAAa,OAAU,EAAE;AAC1E;;;ACxRO,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAavB,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;;;ATgHtC,IAAM,iBAAsC;AAAA,EAC1C,MAAM,CAAC,uBAAuB,uBAAuB;AAAA,EACrD,mBAAmB;AAAA,EACnB,OAAO,cAAY;AACjB,UAAM,aAAa,EAAE,GAAI,SAAS,WAAuC;AAOzE,eAAW,aAAa,YAAY;AACpC,eAAW,eAAe,YAAY;AACtC,WAAO,EAAE,GAAG,UAAU,WAAW;AAAA,EACnC;AAAA,EACA,SAAS,cAAY;AACnB,UAAM,SAAS,EAAE,GAAG,SAAS;AAC7B,QAAI,aAAa,EAAE,GAAI,SAAS,WAAuC;AAKvE,eAAW,QAAQ,CAAC,YAAY,YAAY,GAAY;AACtD,UAAI,KAAK,UAAU,WAAW,IAAI,CAAC,MAAM,KAAK,UAAU,YAAY,IAAI,CAAC,GAAG;AAC1E,qBAAa,OAAO,YAAY,OAAO,QAAQ,UAAU,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,IAAI,CAAC;AAAA,MAC5F;AAAA,IACF;AAEA,kBAAc,QAAQ,cAAc,UAAU;AAC9C,WAAO;AAAA,EACT;AACF;AAgCA,IAAM,kCAAkC,oBAAoB,WAAW,SAAO,MAAM,GAAG,KAAK;AAE5F,IAAM,kCAAkE,OAAO;AAAA,EAC7E,qBAAqB,IAAI,aAAW;AAAA,IAClC,iBAAiB,QAAQ,IAAI;AAAA,IAC7B,EAAE,UAAU,gBAAgB,QAAQ,IAAI,OAAO;AAAA,EACjD,CAAC;AACH;AAEA,IAAM,qCAAqE,OAAO;AAAA,EAChF,wBAAwB,IAAI,aAAW;AAAA,IACrC,oBAAoB,QAAQ,IAAI;AAAA,IAChC,EAAE,UAAU,YAAY,QAAQ,IAAI,MAAM;AAAA,EAC5C,CAAC;AACH;AAEA,IAAM,4BAA4B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,kCAAkE,OAAO;AAAA,EAC7E,0BAA0B,IAAI,UAAQ;AAAA,IACpC,oBAAoB,IAAI;AAAA,IACxB,EAAE,UAAU,UAAU,IAAI,GAAG;AAAA,EAC/B,CAAC;AACH;AAEA,IAAM,2BAA2B;AAAA,EAC/B;AAAA,EACA,GAAG,IAAI,IAAI,0BAA0B,IAAI,UAAQ,oBAAoB,KAAK,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC/F;AAEA,SAAS,+BAA0C;AACjD;AACF;AAEA,IAAM,gCAAgC;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,uBAA8D,OAAO;AAAA,EACzE,8BAA8B,IAAI,UAAQ;AAAA,IACxC,yBAAyB,IAAI;AAAA,IAC7B;AAAA,MACE,eAAe;AAAA,MACf,UAAU,eAAe,IAAI;AAAA,MAC7B,WAAW;AAAA,IACb;AAAA,EACF,CAAC;AACH;AAgBA,IAAM,gCAAmD;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgBO,IAAM,2BAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA,EACA;AAAA,EACA,GAAG,CAAC,YAAY,mBAAmB,EAAE;AAAA,IAAQ,UAC3C,8BAA8B,IAAI,UAAQ,GAAG,IAAI,IAAI,IAAI,EAAE;AAAA,EAC7D;AACF;AAeA,IAAM,+BAA+B,8BAA8B,IAAI,UAAQ,IAAI,IAAI,EAAE,EAAE;AAAA,EACzF;AACF;AACA,IAAM,8BAA8B;AAAA,EAAwD,4BAA4B;AAAA;AAexH,SAAS,qBAAqB,KAA+B;AAC3D,SAAO,CAAC,YAAY,GAAG,0BAA0B,GAAG,EAAE,IAAI,SAAO,GAAG,GAAG,GAAG,CAAC;AAC7E;AAKA,IAAM,6BAA6B;AAInC,IAAM,uBAAuB;AAa7B,SAAS,qBAAqB,KAA6B;AACzD,QAAM,OAAO,2BAA2B,GAAG;AAC3C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAG,IAAI;AAAA,IACP,GAAG,IAAI;AAAA,EACT,EAAE,KAAK,IAAI;AACb;AASO,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,kBAAkB;AAQ/B,SAAS,YAAY,cAA6C;AAChE,QAAM,kBAAkB,MACtB,SAASG,UAAS,KAAK,sBAAsB,GAAG,YAAY,CAAC;AAC/D,SAAO;AAAA;AAAA;AAAA,IAGL,UAAU;AAAA,IACV,WAAW,CAAC,QACV,IAAI,YAAY,kBAAkB,gBAAgB,IAAI;AAAA,IACxD,oBAAoB;AAAA,EACtB;AACF;AAEA,SAAS,gBAAgB,KAAsB;AAC7C,QAAM,UAAU,aAAaA,UAAS,KAAK,KAAK,aAAa,aAAa,CAAC;AAC3E,MAAI,YAAY,OAAW,QAAO;AAElC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO,OAAO,UAAU,YAAY;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qCAAqC,SAAyB;AACrE,QAAM,gBAAgB;AACtB,QAAM,WAAW,QAAQ,MAAM,aAAa;AAC5C,SAAO,SACJ,IAAI,CAAC,SAAS,UAAU;AACvB,QAAI,UAAU,EAAG,QAAO;AACxB,UAAM,MAAM,QAAQ,OAAO,KAAK;AAChC,QAAI,QAAQ,GAAI,QAAO;AACvB,UAAM,UAAU,QAAQ,MAAM,GAAG,GAAG;AACpC,UAAM,kBAAkB,QAAQ,MAAM,KAAK,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC;AACjE,UAAM,WACJ,gBAAgB,WAAW,KAC3B,gBAAgB,MAAM,gBAAc;AAClC,YAAM,UAAU,OAAO,UAAU;AACjC,aAAO,OAAO,cAAc,OAAO,KAAK,WAAW,KAAK,OAAO,OAAO,MAAM;AAAA,IAC9E,CAAC;AACH,WAAO,WAAW,uBAAuB,QAAQ,MAAM,GAAG,CAAC,KAAK;AAAA,EAClE,CAAC,EACA,KAAK,aAAa;AACvB;AAEA,SAAS,qBAAqB,cAA6C;AACzE,QAAM,kBAAkB,MACtB,SAASA,UAAS,KAAK,sBAAsB,GAAG,YAAY,CAAC,EAC1D,MAAM,sBAAsB,EAC5B,KAAK,OAAO;AAEjB,SAAO;AAAA,IACL,UAAU;AAAA,IACV,WAAW,CAAC,QACV,gBAAgB,IAAI,GAAG,IAAI,gBAAgB,IAAI;AAAA,IACjD,kCAAkC;AAAA,IAClC,oBAAoB;AAAA,IACpB,4BAA4B;AAAA,EAC9B;AACF;AAOA,IAAM,2BAA2B;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF;AAQA,IAAM,qBAAqB,CAAC,OAAO,2DAA2D;AAG9F,IAAM,uBAAuB;AAStB,SAAS,oBAAoB,IAA+B;AACjE,SAAO,iFAAiF,EAAE;AAC5F;AAOA,SAAS,kBAAkB,IAA4C;AACrE,SAAO;AAAA,IACL,WAAW;AAAA,IACX,SAAS,GAAG,oBAAoB,EAAE,CAAC,IAAI,oBAAoB;AAAA;AAAA,IAC3D,QAAQ;AAAA,IACR,UAAU;AAAA;AAAA;AAAA,IAGV,2BAA2B,CAAC,IAAI,IAAI;AAAA,IACpC,MAAM,SAAO,IAAI,gBAAgB;AAAA,EACnC;AACF;AAGO,IAAM,kBAAkC;AAAA,EAC7C,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA,EAGhB,WAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA,IAGA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,eAAe;AAAA,IACb;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA;AAAA;AAAA,IAGA;AAAA,EACF;AAAA;AAAA,EAGA,iBAAiB;AAAA,IACf;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA;AAAA;AAAA,EAGF;AAAA;AAAA,EAGA,oBAAoB;AAAA;AAAA,IAElB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,gBAAgB;AAAA,IACd;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,EAEF;AAAA;AAAA;AAAA,EAIA,YAAY;AAAA;AAAA;AAAA;AAAA,IAIV,gBAAgB,YAAY,uBAAuB;AAAA;AAAA;AAAA,IAInD,uBAAuB,EAAE,UAAU,YAAY;AAAA,IAC/C,yBAAyB,EAAE,UAAU,cAAc;AAAA,IACnD,qBAAqB,EAAE,SAAS,MAAM,QAAQ;AAAA;AAAA;AAAA,IAG9C,yBAAyB,EAAE,WAAW,MAAiB,OAAU;AAAA;AAAA,IAGjE,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA;AAAA,IAGH,wCAAwC;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA,IACA,sCAAsC;AAAA,MACpC,UAAU;AAAA,IACZ;AAAA,IACA,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,gDAAgD;AAAA,MAC9C,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,iCAAiC;AAAA,MAC/B,UAAU;AAAA,IACZ;AAAA;AAAA,IAGA,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,6CAA6C,EAAE,UAAU,kCAAkC;AAAA,IAC3F,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,6CAA6C,EAAE,UAAU,kCAAkC;AAAA,IAC3F,sDAAsD;AAAA,MACpD,UAAU;AAAA,IACZ;AAAA,IACA,oDAAoD;AAAA,MAClD,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C,EAAE,UAAU,gCAAgC;AAAA,IACvF,0CAA0C,EAAE,UAAU,+BAA+B;AAAA,IACrF,2CAA2C,EAAE,UAAU,gCAAgC;AAAA,IACvF,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,mCAAmC,EAAE,UAAU,wBAAwB;AAAA,IACvE,oCAAoC,EAAE,UAAU,yBAAyB;AAAA,IACzE,+BAA+B,EAAE,UAAU,oBAAoB;AAAA,IAC/D,yCAAyC,EAAE,UAAU,8BAA8B;AAAA,IACnF,kCAAkC,EAAE,UAAU,uBAAuB;AAAA,IACrE,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,uCAAuC,EAAE,UAAU,4BAA4B;AAAA,IAC/E,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,oCAAoC,EAAE,UAAU,yBAAyB;AAAA,IACzE,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,yCAAyC,EAAE,UAAU,8BAA8B;AAAA,IACnF,4CAA4C,EAAE,UAAU,iCAAiC;AAAA,IACzF,4CAA4C,EAAE,UAAU,iCAAiC;AAAA,IACzF,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,4CAA4C,EAAE,UAAU,iCAAiC;AAAA,IACzF,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C,EAAE,UAAU,gCAAgC;AAAA,IACvF,+BAA+B,EAAE,UAAU,oBAAoB;AAAA,IAC/D,2CAA2C,EAAE,UAAU,gCAAgC;AAAA,IACvF,oCAAoC,EAAE,UAAU,yBAAyB;AAAA,IACzE,4CAA4C,EAAE,UAAU,iCAAiC;AAAA,IACzF,0CAA0C,EAAE,UAAU,+BAA+B;AAAA,IACrF,oCAAoC,EAAE,UAAU,yBAAyB;AAAA,IACzE,2CAA2C,EAAE,UAAU,gCAAgC;AAAA,IACvF,iCAAiC,EAAE,UAAU,sBAAsB;AAAA,IACnE,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,yCAAyC,EAAE,UAAU,8BAA8B;AAAA,IACnF,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC,EAAE,UAAU,8BAA8B;AAAA,IACnF,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC,EAAE,UAAU,8BAA8B;AAAA,IACnF,kCAAkC,EAAE,UAAU,uBAAuB;AAAA,IACrE,qCAAqC,EAAE,UAAU,0BAA0B;AAAA,IAC3E,4CAA4C,EAAE,UAAU,iCAAiC;AAAA,IACzF,0CAA0C,EAAE,UAAU,+BAA+B;AAAA,IACrF,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,sCAAsC,EAAE,UAAU,2BAA2B;AAAA,IAC7E,uCAAuC,EAAE,UAAU,4BAA4B;AAAA,IAC/E,4CAA4C,EAAE,UAAU,iCAAiC;AAAA,IACzF,2CAA2C,EAAE,UAAU,gCAAgC;AAAA,IACvF,uCAAuC,EAAE,UAAU,4BAA4B;AAAA,IAC/E,kCAAkC,EAAE,UAAU,uBAAuB;AAAA,IACrE,uDAAuD;AAAA,MACrD,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C,EAAE,UAAU,iCAAiC;AAAA;AAAA;AAAA,IAIzF,sCAAsC;AAAA,MACpC,WAAW,CAAC,QACV,yBAAyB,iBAAiB,2BAA2B,GAAG,CAAC;AAAA,IAC7E;AAAA;AAAA,IAGA,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA,MACxC,UAAU;AAAA;AAAA;AAAA,MAGV,WAAW,MAAiB;AAAA,IAC9B;AAAA,IACA,kDAAkD;AAAA,MAChD,UAAU;AAAA,IACZ;AAAA,IACA,mDAAmD;AAAA,MACjD,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,sCAAsC;AAAA,MACpC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA,IACA,gDAAgD;AAAA,MAC9C,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA,IACA,qCAAqC;AAAA,MACnC,UAAU;AAAA,IACZ;AAAA,IACA,wCAAwC;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,kDAAkD;AAAA,MAChD,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,oDAAoD;AAAA,MAClD,UAAU;AAAA,IACZ;AAAA,IACA,qDAAqD;AAAA,MACnD,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,mCAAmC,EAAE,UAAU,wBAAwB;AAAA,IACvE,mCAAmC,EAAE,UAAU,wBAAwB;AAAA,IACvE,wCAAwC,EAAE,UAAU,6BAA6B;AAAA,IACjF,iCAAiC,EAAE,UAAU,sBAAsB;AAAA,IACnE,uCAAuC,EAAE,UAAU,4BAA4B;AAAA,IAC/E,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,mCAAmC;AAAA,MACjC,UAAU;AAAA,IACZ;AAAA,IACA,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,0CAA0C,EAAE,UAAU,iCAAiC;AAAA,IACvF,0CAA0C,EAAE,UAAU,iCAAiC;AAAA,IACvF,uCAAuC,EAAE,UAAU,8BAA8B;AAAA,IACjF,uCAAuC,EAAE,UAAU,8BAA8B;AAAA;AAAA,IAGjF,0CAA0C;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA,6BAA6B;AAAA,MAC3B,UAAU;AAAA,IACZ;AAAA,IACA,wCAAwC;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,wCAAwC;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA,IACA,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,sCAAsC;AAAA,MACpC,UAAU;AAAA,IACZ;AAAA,IACA,qDAAqD;AAAA,MACnD,UAAU;AAAA,IACZ;AAAA,IACA,qCAAqC;AAAA,MACnC,UAAU;AAAA,IACZ;AAAA,IACA,gDAAgD;AAAA,MAC9C,UAAU;AAAA,IACZ;AAAA,IACA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA;AAAA,IAGA,gDAAgD;AAAA,MAC9C,UAAU;AAAA,IACZ;AAAA,IACA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,mDAAmD;AAAA,MACjD,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA,gDAAgD;AAAA,MAC9C,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA,IAGA,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA,IAIA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,wCAAwC;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA;AAAA,IAGA,qCAAqC;AAAA,MACnC,UAAU;AAAA,IACZ;AAAA;AAAA,IAGA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,gDAAgD;AAAA,MAC9C,UAAU;AAAA,IACZ;AAAA,IACA,wCAAwC;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA,IAIA,GAAG;AAAA;AAAA,IAGH,iCAAiC;AAAA,MAC/B,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA,IACA,4CAA4C;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IACA,oCAAoC;AAAA,MAClC,UAAU;AAAA,IACZ;AAAA,IACA,mCAAmC;AAAA,MACjC,UAAU;AAAA,IACZ;AAAA,IACA,+BAA+B;AAAA,MAC7B,UAAU;AAAA,IACZ;AAAA,IACA,mCAAmC;AAAA,MACjC,UAAU;AAAA,IACZ;AAAA,IACA,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,mCAAmC;AAAA,MACjC,UAAU;AAAA,IACZ;AAAA,IACA,6BAA6B;AAAA,MAC3B,UAAU;AAAA,IACZ;AAAA,IACA,8BAA8B;AAAA,MAC5B,UAAU;AAAA,IACZ;AAAA,IACA,mCAAmC;AAAA,MACjC,UAAU;AAAA,IACZ;AAAA,IACA,gCAAgC;AAAA,MAC9B,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA,IAGA,gCAAgC,EAAE,UAAU,uBAAuB;AAAA,IACnE,kCAAkC,EAAE,UAAU,yBAAyB;AAAA,IACvE,oCAAoC,EAAE,UAAU,2BAA2B;AAAA,IAC3E,iCAAiC,EAAE,UAAU,wBAAwB;AAAA,IACrE,mCAAmC,EAAE,UAAU,0BAA0B;AAAA,IACzE,uCAAuC,EAAE,UAAU,8BAA8B;AAAA,IACjF,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,2CAA2C;AAAA,MACzC,UAAU;AAAA,IACZ;AAAA,IACA,iCAAiC,EAAE,UAAU,wBAAwB;AAAA,IACrE,iCAAiC,EAAE,UAAU,wBAAwB;AAAA,IACrE,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,sCAAsC;AAAA,MACpC,UAAU;AAAA,IACZ;AAAA,IACA,kCAAkC;AAAA,MAChC,UAAU;AAAA,IACZ;AAAA,IACA,sCAAsC;AAAA,MACpC,UAAU;AAAA,IACZ;AAAA,IACA,yCAAyC;AAAA,MACvC,UAAU;AAAA,IACZ;AAAA;AAAA,IAGA,GAAG;AAAA;AAAA,IAGH,GAAG;AAAA,IACH,+BAA+B,EAAE,UAAU,sBAAsB;AAAA,IACjE,8BAA8B,EAAE,UAAU,qBAAqB;AAAA,IAC/D,mCAAmC,EAAE,UAAU,0BAA0B;AAAA,IACzE,mCAAmC,EAAE,UAAU,0BAA0B;AAAA,IACzE,uCAAuC;AAAA,MACrC,UAAU;AAAA,IACZ;AAAA,IACA,4BAA4B,EAAE,UAAU,mBAAmB;AAAA;AAAA,IAG3D,6CAA6C;AAAA,MAC3C,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA,8CAA8C;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IACA,oDAAoD;AAAA,MAClD,UAAU;AAAA,IACZ;AAAA,IACA,+CAA+C;AAAA,MAC7C,UAAU;AAAA,IACZ;AAAA,IACA,mDAAmD;AAAA,MACjD,UAAU;AAAA,IACZ;AAAA,IACA,kCAAkC,EAAE,UAAU,uBAAuB;AAAA,EACvE;AAAA;AAAA,EAGA,cAAc;AAAA;AAAA;AAAA,IAGZ,eAAe,EAAE,UAAU,cAAc;AAAA;AAAA;AAAA;AAAA,IAKzC,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOH,kCAAkC,YAAY,gCAAgC;AAAA,IAC9E,kBAAkB,YAAY,mBAAmB;AAAA,IACjD,yBAAyB,YAAY,0BAA0B;AAAA;AAAA;AAAA,IAI/D,4CAA4C,qBAAqB,yBAAyB;AAAA,IAC1F,sDAAsD;AAAA,MACpD;AAAA,IACF;AAAA,IACA,mDAAmD;AAAA,MACjD;AAAA,IACF;AAAA;AAAA,IAGA,GAAG;AAAA;AAAA,IAEH,GAAG;AAAA;AAAA,IAEH,GAAG;AAAA;AAAA,IAEH,GAAG;AAAA;AAAA,IAEH,GAAG;AAAA;AAAA;AAAA;AAAA,IAKH,mCAAmC;AAAA,MACjC,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,iCAAiC;AAAA,MAC/B,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,iCAAiC;AAAA,MAC/B,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,iCAAiC;AAAA,MAC/B,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,gCAAgC;AAAA,MAC9B,SAAS;AAAA,IACX;AAAA,EACF;AAAA;AAAA,EAGA,YAAY;AAAA;AAAA,IAEV,GAAG;AAAA;AAAA,IAGH,yBAAyB;AAAA,MACvB,MAAM,CAAC,OAAO;AAAA,MACd,OAAO,cAAY;AAEjB,cAAM,gBAAiB,SAAS,SAAuC,CAAC;AACxE,cAAM,cAAyC,EAAE,GAAG,cAAc;AAElE,mBAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,cAAc,GAAG;AAC9D,gBAAM,aAAa,YAAY,KAAK,KAAK,CAAC;AAC1C,gBAAM,sBAAsB,iCAAiC,YAAY,QAAQ;AACjF,gBAAM,mBAAmB;AAAA,YACvB;AAAA,YACA,8BAA8B,KAAK;AAAA,UACrC;AACA,sBAAY,KAAK,IAAI,CAAC,GAAG,kBAAkB,GAAG,QAAQ;AAAA,QACxD;AAEA,eAAO,EAAE,GAAG,UAAU,OAAO,YAAY;AAAA,MAC3C;AAAA,MACA,SAAS,cAAY;AAEnB,cAAM,gBAAiB,SAAS,SAAuC,CAAC;AACxE,cAAM,eAA0C,CAAC;AAEjD,mBAAW,CAAC,OAAO,UAAU,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC/D,gBAAM,sBAAsB;AAAA,YAC1B;AAAA,YACA,eAAe,KAAoC,KAAK,CAAC;AAAA,UAC3D;AACA,gBAAM,mBAAmB;AAAA,YACvB;AAAA,YACA,8BAA8B,KAAK;AAAA,UACrC;AACA,cAAI,iBAAiB,SAAS,GAAG;AAC/B,yBAAa,KAAK,IAAI;AAAA,UACxB;AAAA,QACF;AAEA,cAAM,SAAS,EAAE,GAAG,SAAS;AAC7B,sBAAc,QAAQ,SAAS,YAAY;AAC3C,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,aAAa;AAAA,IACb,oBAAoB;AAAA;AAAA;AAAA;AAAA,IAKpB,4BAA4B;AAAA,IAE5B,sBAAsB;AAAA,MACpB,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,mBAAmB;AAAA,MACnB,OAAO,cAAY;AACjB,cAAM,gBAAiB,SAAS,SAAuC,CAAC;AACxE,cAAM,QAAmC,EAAE,GAAG,cAAc;AAC5D,mBAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC5D,gBAAM,aAAa,MAAM,KAAK,KAAK,CAAC;AACpC,gBAAM,mBAAmB,iCAAiC,YAAY,QAAQ;AAC9E,gBAAM,KAAK,IAAI,CAAC,GAAG,kBAAkB,GAAG,QAAQ;AAAA,QAClD;AACA,eAAO;AAAA,UACL,GAAG;AAAA,UACH,SAAS;AAAA;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA,SAAS,cAAY;AACnB,cAAM,SAAS,EAAE,GAAG,SAAS;AAC7B,cAAM,gBAAiB,SAAS,SAAuC,CAAC;AAKxE,cAAM,QAAQ,OAAO;AAAA,UACnB,OAAO,QAAQ,aAAa,EACzB;AAAA,YACC,CAAC,CAAC,MAAM,UAAU,MAChB;AAAA,cACE;AAAA,cACA;AAAA,gBACE;AAAA,gBACA,aAAa,IAAiC,KAAK,CAAC;AAAA,cACtD;AAAA,YACF;AAAA,UACJ,EACC,OAAO,CAAC,CAAC,EAAE,UAAU,MAAM,WAAW,SAAS,CAAC;AAAA,QACrD;AAIA,YAAI,CAAC,cAAc,QAAQ,SAAS,KAAK,GAAG;AAC1C,iBAAO,OAAO;AAAA,QAChB;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWX,qBAAqB,kBAAkB,QAAQ;AAAA,IAC/C,mBAAmB,kBAAkB,MAAM;AAAA,IAC3C,cAAc;AAAA,MACZ,WAAW;AAAA,MACX,SAAS;AAAA;AAAA,EAAmD,yBAAyB,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,MAG/F,UAAU;AAAA;AAAA,MAEV,0BAA0B,IAAI;AAAA,QAC5B,OAAO,yBAAyB;AAAA,UAAI,UAClC,KAAK,WAAW,wBAAwB,OAAO,QAAQ;AAAA,QACzD,EAAE,KAAK,GAAG,CAAC;AAAA,QACX;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,IACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYA,mBAAmB;AAAA,MACjB,WAAW;AAAA;AAAA;AAAA;AAAA,MAIX,SAAS,SAAO;AAAA,EAAK,0BAA0B;AAAA,EAAK,qBAAqB,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,MACxF,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,yBAAyB,SACvB;AAAA;AAAA,EAA0E;AAAA,QACxE;AAAA,MACF,EACG,IAAI,SAAO,GAAG,GAAG,GAAG,EACpB,KAAK,IAAI,CAAC;AAAA;AAAA,IACjB;AAAA,IACA,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQhB,WAAW;AAAA,MACX,SAAS,SAAO,GAAG,qBAAqB,GAAG,CAAC;AAAA;AAAA,MAC5C,UAAU;AAAA,MACV,0BACE;AAAA,MACF,QAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB;AAAA,IACjB,aAAa;AAAA,MACX,WAAW;AAAA,MACX,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,IACA,aAAa;AAAA,MACX,WAAW;AAAA,MACX,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW;AAAA,IACT,oCAAoC;AAAA;AAAA,MAElC,UAAU;AAAA,IACZ;AAAA,IACA,yDAAyD;AAAA;AAAA,MAEvD,UAAU;AAAA,IACZ;AAAA,IACA,mDAAmD;AAAA;AAAA,MAEjD,UAAU;AAAA,IACZ;AAAA,IACA,uDAAuD;AAAA;AAAA;AAAA;AAAA,MAIrD,UAAU;AAAA,IACZ;AAAA,IACA,0CAA0C;AAAA;AAAA,MAExC,UAAU;AAAA,IACZ;AAAA,IACA,+CAA+C;AAAA;AAAA;AAAA;AAAA,MAI7C,UAAU;AAAA,QACR;AAAA;AAAA;AAAA;AAAA,QAIA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,oEAAoE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQlE,UAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,uDAAuD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOrD,UAAU,CAAC,yBAAyB,kBAAkB,iBAAiB;AAAA,IACzE;AAAA,EACF;AAAA;AAAA,EAGA,UAAU;AACZ;AAIA,IAAM,uBAAuB,oBAAI,IAAI;AAAA,EACnC;AAAA,EACA;AACF,CAAC;AAEM,SAAS,oBAAoB,MAAuB;AACzD,SACE,SAAS,aACT,KAAK,WAAW,UAAU,KAC1B,SAAS,4BACT,KAAK,WAAW,yBAAyB,KACzC,qBAAqB,IAAI,IAAI;AAEjC;AAEA,SAAS,qBAAwB,QAA8C;AAC7E,SAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,MAAM,CAAC,oBAAoB,IAAI,CAAC,CAAC;AACjG;AAGO,SAAS,yBACd,QACA,UACgB;AAChB,MAAI,SAAS,SAAS,QAAQ,EAAG,QAAO;AACxC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,OAAO,UAAU,OAAO,UAAQ,CAAC,oBAAoB,IAAI,CAAC;AAAA,IACrE,YAAY,OAAO,WAAW,OAAO,UAAQ,CAAC,oBAAoB,IAAI,CAAC;AAAA,IACvE,eAAe,OAAO,cAAc,OAAO,UAAQ,CAAC,oBAAoB,IAAI,CAAC;AAAA,IAC7E,iBAAiB,OAAO,gBAAgB,OAAO,UAAQ,CAAC,oBAAoB,IAAI,CAAC;AAAA,IACjF,gBAAgB,OAAO,eAAe,OAAO,UAAQ,CAAC,oBAAoB,IAAI,CAAC;AAAA,IAC/E,YAAY,qBAAqB,OAAO,UAAU;AAAA,IAClD,cAAc,qBAAqB,OAAO,YAAY;AAAA,IACtD,YAAY,qBAAqB,OAAO,UAAU;AAAA,IAClD,aAAa,qBAAqB,OAAO,WAAW;AAAA,IACpD,mBAAmB,qBAAqB,OAAO,iBAAiB;AAAA,IAChE,WAAW,qBAAqB,OAAO,SAAS;AAAA,EAClD;AACF;","names":["nodePath","nodePath","nodePath","readFileSync","nodePath","nodePath","readFileSync","existsSync","readFileSync","nodePath","existsSync","readFileSync","nodePath","YAML","existsSync","readFileSync","YAML","nodePath","nodePath"]}
|