wizz-method 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/LICENSE +60 -30
  2. package/README.md +80 -31
  3. package/SECURITY.md +24 -0
  4. package/THIRD-PARTY-NOTICES.md +136 -0
  5. package/package.json +31 -17
  6. package/skills-registry.yaml +128 -7
  7. package/src/bmm-skills/1-analysis/research/wizz-domain-research/SKILL.md +3 -4
  8. package/src/bmm-skills/1-analysis/research/wizz-domain-research/domain-steps/step-06-research-synthesis.md +1 -1
  9. package/src/bmm-skills/1-analysis/research/wizz-market-research/SKILL.md +3 -4
  10. package/src/bmm-skills/1-analysis/research/wizz-market-research/steps/step-06-research-completion.md +1 -1
  11. package/src/bmm-skills/1-analysis/research/wizz-technical-research/SKILL.md +3 -4
  12. package/src/bmm-skills/1-analysis/research/wizz-technical-research/technical-steps/step-06-research-synthesis.md +1 -1
  13. package/src/bmm-skills/1-analysis/wizz-agent-analyst/SKILL.md +3 -4
  14. package/src/bmm-skills/1-analysis/wizz-agent-tech-writer/SKILL.md +3 -4
  15. package/src/bmm-skills/1-analysis/wizz-document-project/SKILL.md +3 -4
  16. package/src/bmm-skills/1-analysis/wizz-document-project/workflows/deep-dive-instructions.md +1 -1
  17. package/src/bmm-skills/1-analysis/wizz-document-project/workflows/full-scan-instructions.md +1 -1
  18. package/src/bmm-skills/1-analysis/wizz-prfaq/SKILL.md +3 -4
  19. package/src/bmm-skills/1-analysis/wizz-prfaq/references/verdict.md +1 -1
  20. package/src/bmm-skills/1-analysis/wizz-product-brief/SKILL.md +1 -1
  21. package/src/bmm-skills/2-plan-workflows/wizz-agent-pm/SKILL.md +3 -4
  22. package/src/bmm-skills/2-plan-workflows/wizz-agent-ux-designer/SKILL.md +3 -4
  23. package/src/bmm-skills/2-plan-workflows/wizz-create-prd/SKILL.md +1 -1
  24. package/src/bmm-skills/2-plan-workflows/wizz-edit-prd/SKILL.md +1 -1
  25. package/src/bmm-skills/2-plan-workflows/wizz-prd/SKILL.md +1 -1
  26. package/src/bmm-skills/2-plan-workflows/wizz-ux/SKILL.md +1 -1
  27. package/src/bmm-skills/2-plan-workflows/wizz-validate-prd/SKILL.md +1 -1
  28. package/src/bmm-skills/3-solutioning/wizz-agent-architect/SKILL.md +3 -4
  29. package/src/bmm-skills/3-solutioning/wizz-architecture/SKILL.md +9 -9
  30. package/src/bmm-skills/3-solutioning/wizz-architecture/references/headless.md +1 -1
  31. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/SKILL.md +3 -4
  32. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/steps/step-06-final-assessment.md +1 -1
  33. package/src/bmm-skills/3-solutioning/wizz-create-architecture/SKILL.md +1 -1
  34. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/SKILL.md +3 -4
  35. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
  36. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/SKILL.md +3 -4
  37. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/steps/step-03-complete.md +1 -1
  38. package/src/bmm-skills/4-implementation/wizz-agent-dev/SKILL.md +3 -4
  39. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/SKILL.md +3 -4
  40. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/step-05-wrapup.md +1 -1
  41. package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +3 -4
  42. package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +3 -8
  43. package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/blind-hunter.md +20 -0
  44. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +1 -1
  45. package/src/bmm-skills/4-implementation/wizz-correct-course/SKILL.md +4 -5
  46. package/src/bmm-skills/4-implementation/wizz-create-story/SKILL.md +4 -5
  47. package/src/bmm-skills/4-implementation/wizz-dev-story/SKILL.md +4 -5
  48. package/src/bmm-skills/4-implementation/wizz-investigate/SKILL.md +1 -1
  49. package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/SKILL.md +4 -5
  50. package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +4 -5
  51. package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +10 -22
  52. package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/blind-hunter.md +20 -0
  53. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-05-present.md +1 -1
  54. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-oneshot.md +1 -1
  55. package/src/bmm-skills/4-implementation/wizz-retrospective/SKILL.md +1 -2
  56. package/src/bmm-skills/4-implementation/wizz-sprint-planning/SKILL.md +1 -1
  57. package/src/bmm-skills/4-implementation/wizz-sprint-status/SKILL.md +5 -6
  58. package/src/core-skills/_shared/handoff-protocol.md +9 -0
  59. package/src/core-skills/wizz-advanced-elicitation/SKILL.md +2 -2
  60. package/src/core-skills/wizz-brainstorming/SKILL.md +6 -6
  61. package/src/core-skills/wizz-brainstorming/references/converge.md +1 -1
  62. package/src/core-skills/wizz-brainstorming/references/finalize.md +1 -1
  63. package/src/core-skills/wizz-brainstorming/references/headless.md +3 -3
  64. package/src/core-skills/wizz-brainstorming/references/mode-autonomous.md +1 -1
  65. package/src/core-skills/wizz-customize/SKILL.md +13 -10
  66. package/src/core-skills/wizz-forge-idea/SKILL.md +6 -6
  67. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
  68. package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +17 -1
  69. package/src/core-skills/wizz-party-mode/SKILL.md +3 -3
  70. package/src/core-skills/wizz-party-mode/references/create-party.md +1 -1
  71. package/src/core-skills/wizz-party-mode/references/party-memory.md +2 -2
  72. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
  73. package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +17 -1
  74. package/src/core-skills/wizz-review-adversarial-general/SKILL.md +1 -1
  75. package/src/core-skills/wizz-spec/SKILL.md +5 -5
  76. package/src/modules/lowticket/README.md +6 -6
  77. package/src/modules/lowticket/module.yaml +4 -1
  78. package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/SKILL.md +4 -4
  79. package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/copy-das-paginas.md +1 -1
  80. package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/SKILL.md +1 -1
  81. package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/SKILL.md +3 -3
  82. package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/references/rubrica.md +1 -1
  83. package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/SKILL.md +4 -4
  84. package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/SKILL.md +3 -3
  85. package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/reguas.md +1 -1
  86. package/src/modules/wizz/_shared/communication-rules.md +1 -1
  87. package/src/modules/wizz/_shared/despacho-barato.md +9 -0
  88. package/src/modules/wizz/_shared/token-economy.md +7 -0
  89. package/src/modules/wizz/agents/wizz-ads/SKILL.md +1 -1
  90. package/src/modules/wizz/agents/wizz-ads/customize.toml +1 -2
  91. package/src/modules/wizz/agents/wizz-copy/SKILL.md +1 -1
  92. package/src/modules/wizz/agents/wizz-copy/customize.toml +1 -2
  93. package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
  94. package/src/modules/wizz/agents/wizz-designer/customize.toml +1 -2
  95. package/src/modules/wizz/agents/wizz-growth/SKILL.md +1 -1
  96. package/src/modules/wizz/agents/wizz-growth/customize.toml +1 -2
  97. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +5 -5
  98. package/src/modules/wizz/agents/wizz-maestro/customize.toml +4 -8
  99. package/src/modules/wizz/agents/wizz-memoria/SKILL.md +1 -1
  100. package/src/modules/wizz/agents/wizz-memoria/customize.toml +1 -2
  101. package/src/modules/wizz/agents/wizz-qa/SKILL.md +1 -1
  102. package/src/modules/wizz/agents/wizz-qa/customize.toml +1 -2
  103. package/src/modules/wizz/agents/wizz-seo/SKILL.md +1 -1
  104. package/src/modules/wizz/agents/wizz-seo/customize.toml +1 -2
  105. package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -1
  106. package/src/modules/wizz/agents/wizz-social/customize.toml +1 -2
  107. package/src/modules/wizz/module.yaml +3 -0
  108. package/src/modules/wizz/overrides/wizz-agent-analyst.toml +1 -2
  109. package/src/modules/wizz/overrides/wizz-agent-architect.toml +1 -2
  110. package/src/modules/wizz/overrides/wizz-agent-dev.toml +1 -2
  111. package/src/modules/wizz/overrides/wizz-agent-pm.toml +1 -2
  112. package/src/modules/wizz/overrides/wizz-agent-tech-writer.toml +1 -2
  113. package/src/modules/wizz/overrides/wizz-agent-ux-designer.toml +1 -2
  114. package/src/modules/wizz/subagents/codex/wizz-exec-haiku.toml +2 -0
  115. package/src/modules/wizz/subagents/codex/wizz-exec-opus.toml +2 -0
  116. package/src/modules/wizz/subagents/codex/wizz-exec-review.toml +2 -0
  117. package/src/modules/wizz/subagents/codex/wizz-exec-sonnet.toml +2 -0
  118. package/src/modules/wizz/subagents/gemini/wizz-exec-haiku.md +4 -1
  119. package/src/modules/wizz/subagents/gemini/wizz-exec-opus.md +4 -1
  120. package/src/modules/wizz/subagents/gemini/wizz-exec-review.md +4 -1
  121. package/src/modules/wizz/subagents/gemini/wizz-exec-sonnet.md +4 -1
  122. package/src/modules/wizz/subagents/opencode/wizz-exec-haiku.md +4 -1
  123. package/src/modules/wizz/subagents/opencode/wizz-exec-opus.md +4 -1
  124. package/src/modules/wizz/subagents/opencode/wizz-exec-review.md +4 -1
  125. package/src/modules/wizz/subagents/opencode/wizz-exec-sonnet.md +4 -1
  126. package/src/modules/wizz/subagents/spec.yaml +148 -0
  127. package/src/modules/wizz/subagents/wizz-exec-haiku.md +4 -1
  128. package/src/modules/wizz/subagents/wizz-exec-opus.md +4 -1
  129. package/src/modules/wizz/subagents/wizz-exec-review.md +4 -1
  130. package/src/modules/wizz/subagents/wizz-exec-sonnet.md +4 -1
  131. package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
  132. package/src/scripts/memlog.js +368 -0
  133. package/src/scripts/resolve_customization.py +124 -15
  134. package/src/skills-lib/_shared/complexity-signal.md +14 -0
  135. package/src/skills-lib/wizz-canvas-design/canvas-fonts/InstrumentSerif-OFL.txt +93 -0
  136. package/src/skills-lib/wizz-huashu-design/references/sfx-library.md +1 -1
  137. package/src/skills-lib/wizz-huashu-design/scripts/add-music.sh +1 -1
  138. package/src/skills-lib/wizz-huashu-design/scripts/mix-voiceover.sh +1 -1
  139. package/src/skills-lib/wizz-impeccable/LICENSE.txt +202 -0
  140. package/src/skills-lib/wizz-offer-forge/SKILL.md +1 -1
  141. package/src/skills-lib/wizz-react-components/LICENSE.txt +202 -0
  142. package/src/skills-lib/wizz-router/SKILL.md +2 -2
  143. package/src/skills-lib/wizz-supabase-postgres-best-practices/LICENSE +21 -0
  144. package/src/skills-lib/wizz-taste-skill/SKILL.md +1 -1
  145. package/src/skills-lib/wizz-tracking-audit/SKILL.md +1 -1
  146. package/tools/fetch-assets.mjs +70 -38
  147. package/tools/hooks/README.md +22 -0
  148. package/tools/hooks/rtk-rewrite.sh +280 -0
  149. package/tools/hooks/security-defensive-context.js +70 -0
  150. package/tools/hooks/session-rules.js +368 -0
  151. package/tools/hooks/wizz-decision-trace.js +389 -0
  152. package/tools/hooks/wizz-router-enforce.js +258 -0
  153. package/tools/installer/banner.js +1 -1
  154. package/tools/installer/commands/doctor.js +124 -0
  155. package/tools/installer/commands/fetch-assets.js +7 -8
  156. package/tools/installer/commands/install.js +1 -1
  157. package/tools/installer/commands/resolve.js +95 -0
  158. package/tools/installer/core/doctor.js +648 -0
  159. package/tools/installer/core/hash-cache.js +156 -0
  160. package/tools/installer/core/help-catalog.js +4 -1
  161. package/tools/installer/core/hooks-settings.js +178 -0
  162. package/tools/installer/core/installer.js +297 -15
  163. package/tools/installer/core/legacy-warnings.js +0 -4
  164. package/tools/installer/core/manifest-generator.js +121 -43
  165. package/tools/installer/core/manifest.js +36 -32
  166. package/tools/installer/core/module-config-writer.js +4 -1
  167. package/tools/installer/core/python-check.js +60 -63
  168. package/tools/installer/core/quick-update.js +76 -43
  169. package/tools/installer/core/resolved-config.js +168 -0
  170. package/tools/installer/core/resolved-customizations.js +736 -0
  171. package/tools/installer/core/resolved-includes.js +262 -0
  172. package/tools/installer/core/resolved-schema.js +148 -0
  173. package/tools/installer/core/user-file-preservation.js +40 -8
  174. package/tools/installer/core/wizz-dir.js +117 -0
  175. package/tools/installer/core/yaml-cache.js +105 -0
  176. package/tools/installer/fs-native.js +97 -6
  177. package/tools/installer/ide/_config-driven.js +12 -75
  178. package/tools/installer/ide/platform-codes.js +111 -16
  179. package/tools/installer/ide/platform-codes.yaml +45 -100
  180. package/tools/installer/ide/shared/path-utils.js +6 -208
  181. package/tools/installer/install-messages.yaml +4 -14
  182. package/tools/installer/modules/channel-plan.js +15 -2
  183. package/tools/installer/modules/config-collector.js +762 -0
  184. package/tools/installer/modules/config-values.js +352 -0
  185. package/tools/installer/modules/custom-module-manager.js +161 -28
  186. package/tools/installer/modules/env-vars.js +119 -0
  187. package/tools/installer/modules/external-manager.js +133 -60
  188. package/tools/installer/modules/git-cache-utils.js +125 -0
  189. package/tools/installer/modules/module-catalog.js +565 -0
  190. package/tools/installer/modules/module-installer.js +618 -0
  191. package/tools/installer/modules/module-interface.js +86 -0
  192. package/tools/installer/modules/official-modules.js +98 -2172
  193. package/tools/installer/modules/pin-spec.js +125 -0
  194. package/tools/installer/modules/plugin-resolver.js +4 -0
  195. package/tools/installer/modules/skills-lib.js +3 -6
  196. package/tools/installer/prompts.js +0 -3
  197. package/tools/installer/ui.js +89 -54
  198. package/tools/installer/wizz-cli.js +1 -1
  199. package/tools/lib/frontmatter.js +139 -0
  200. package/tools/lib/sync-targets.mjs +49 -0
  201. package/tools/lib/walk.js +93 -0
  202. package/wizz-modules.yaml +9 -0
  203. package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
  204. package/src/scripts/memlog.py +0 -233
  205. package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
  206. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
  207. package/src/scripts/tests/test_memlog.py +0 -306
  208. package/src/skills-lib/wizz-huashu-design/assets/showcases/INDEX.md +0 -115
  209. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.html +0 -235
  210. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.png +0 -0
  211. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.html +0 -229
  212. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.png +0 -0
  213. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.html +0 -288
  214. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.png +0 -0
  215. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.html +0 -503
  216. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.png +0 -0
  217. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.html +0 -600
  218. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.png +0 -0
  219. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.html +0 -670
  220. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.png +0 -0
  221. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.html +0 -382
  222. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.png +0 -0
  223. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.html +0 -536
  224. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.png +0 -0
  225. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.html +0 -497
  226. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.png +0 -0
  227. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.html +0 -385
  228. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.png +0 -0
  229. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.html +0 -422
  230. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.png +0 -0
  231. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.html +0 -499
  232. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.png +0 -0
  233. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.html +0 -562
  234. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.png +0 -0
  235. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.html +0 -548
  236. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.png +0 -0
  237. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.html +0 -696
  238. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.png +0 -0
  239. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.html +0 -372
  240. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.png +0 -0
  241. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.html +0 -460
  242. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.png +0 -0
  243. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.html +0 -494
  244. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.png +0 -0
  245. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.html +0 -367
  246. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.png +0 -0
  247. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.html +0 -368
  248. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.png +0 -0
  249. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.html +0 -459
  250. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.png +0 -0
  251. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.html +0 -493
  252. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.png +0 -0
  253. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.html +0 -556
  254. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.png +0 -0
  255. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.html +0 -604
  256. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.png +0 -0
  257. package/tools/installer/file-ops.js +0 -204
  258. package/tools/installer/ide/shared/skill-manifest.js +0 -57
  259. package/tools/installer/yaml-format.js +0 -245
  260. package/web-bundles/README.md +0 -46
  261. package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +0 -86
  262. package/web-bundles/brainstorming-coach/SKILL.md +0 -83
  263. package/web-bundles/brainstorming-coach/brain-methods.csv +0 -62
  264. package/web-bundles/bundles.json +0 -139
  265. package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +0 -88
  266. package/web-bundles/market-and-industry-research/SKILL.md +0 -59
  267. package/web-bundles/prd-coach/INSTRUCTIONS.md +0 -86
  268. package/web-bundles/prd-coach/SKILL.md +0 -101
  269. package/web-bundles/prd-coach/prd-template.md +0 -165
  270. package/web-bundles/prd-coach/prd-validation-checklist.md +0 -135
  271. package/web-bundles/prfaq-coach/INSTRUCTIONS.md +0 -86
  272. package/web-bundles/prfaq-coach/SKILL.md +0 -139
  273. package/web-bundles/product-brief-coach/INSTRUCTIONS.md +0 -86
  274. package/web-bundles/product-brief-coach/SKILL.md +0 -113
  275. package/web-bundles/ux-coach/INSTRUCTIONS.md +0 -92
  276. package/web-bundles/ux-coach/SKILL.md +0 -187
  277. package/web-bundles/ux-coach/ux-validation.md +0 -100
  278. /package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/mapa-do-funil.md +0 -0
  279. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/CATALOGO.md +0 -0
  280. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/01-principios.md +0 -0
  281. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/02-diagnostico-funil.md +0 -0
  282. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/03-campanha-bidcap.md +0 -0
  283. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04-criativos.md +0 -0
  284. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04a-criativos-ia.md +0 -0
  285. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04b-criativos-formatos.md +0 -0
  286. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/05-oferta.md +0 -0
  287. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06-pagina-vendas.md +0 -0
  288. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06a-instagram.md +0 -0
  289. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06b-player-vsl.md +0 -0
  290. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07-funil-upsell.md +0 -0
  291. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07a-copy-humanizacao.md +0 -0
  292. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/08-pesquisa-publico.md +0 -0
  293. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/09-especialista-coproducao.md +0 -0
  294. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/10-levantar-caixa.md +0 -0
  295. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/11-checklist-roi.md +0 -0
  296. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12-mineracao-ofertas.md +0 -0
  297. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12a-mineracao-veredito.md +0 -0
  298. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12b-fabrica-de-ofertas.md +0 -0
  299. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12c-anatomia-low-ticket.md +0 -0
  300. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/13-benchmarks-vsl.md +0 -0
  301. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/14-vocabulario.md +0 -0
  302. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/15-como-estender.md +0 -0
  303. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/17-rastreamento.md +0 -0
  304. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/18-copy-desejo-oferta.md +0 -0
  305. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/INDEX.md +0 -0
  306. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/guardrails.md +0 -0
  307. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/pontos-em-aberto.md +0 -0
  308. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/references/swipe-file.md +0 -0
  309. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/metas-e-medicao.md +0 -0
  310. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/padroes-de-codigo.md +0 -0
  311. /package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/formato-do-diagnostico.md +0 -0
@@ -0,0 +1,736 @@
1
+ /**
2
+ * Resolved customizations generator (audit item 19, 2026-09-11).
3
+ *
4
+ * After the modules are installed, walks the freshly written `_wizz/` tree
5
+ * for every `customize.toml`, applies the same three-layer merge the
6
+ * runtime resolver (`src/scripts/resolve_customization.py`) applies, and
7
+ * writes the merged result to `_wizz/_resolved/<skill-name>.json`.
8
+ *
9
+ * Why: agent activation today spawns Python and/or reads all three TOML
10
+ * layers on every skill activation (48 SKILL.md call sites). With a
11
+ * resolved snapshot on disk, activation is a single file read and NOTHING
12
+ * else — no mtime check, no stat, no spawn. The Python resolver is the
13
+ * fallback for one case only: the snapshot file is missing (never
14
+ * generated, or skipped because the base `customize.toml` is broken).
15
+ *
16
+ * Staleness is therefore a WRITE-time concern, not a read-time one: the
17
+ * snapshots are regenerated by `wizz install`/`wizz update`, by the new
18
+ * `wizz resolve [--skill <name>]`, and by the wizz-customize skill after
19
+ * it edits a layer. `wizz doctor` detects a snapshot that fell behind by
20
+ * comparing `_resolved/.index.json` (sha256 of every input that fed each
21
+ * snapshot: base customize.toml, its includes, the team/user layers and
22
+ * their includes) with the files on disk.
23
+ *
24
+ * The merge here must stay byte-for-byte SEMANTICALLY identical to the
25
+ * Python one (same rules, same precedence, same `include` expansion) so
26
+ * that a resolved snapshot and a fresh `resolve_customization.py --skill`
27
+ * run agree. `test/test-resolved-customizations.js` enforces exactly that
28
+ * parity by running both against fixture fixtures and diffing the JSON.
29
+ *
30
+ * Nothing here prompts; failures are fail-open (warn + skip) so a broken
31
+ * customization file never blocks an install — same philosophy as the
32
+ * Python resolver itself.
33
+ *
34
+ * `_resolved/` is a GENERATED, gitignored artifact and stays OUT of
35
+ * `files-manifest.csv`: nothing here calls the installer's `trackFile`. The
36
+ * manifest exists to tell an installed file from a user-authored one so an
37
+ * update can preserve user edits and detect drift; a snapshot has no user
38
+ * edits to preserve (editing one is pointless — the next `wizz resolve`
39
+ * overwrites it) and its hash changes on every regeneration, so tracking it
40
+ * would report drift on every single run. Drift detection therefore ignores
41
+ * `_resolved/` entirely; staleness is `.index.json`'s job, not the
42
+ * manifest's.
43
+ */
44
+
45
+ const path = require('node:path');
46
+ const nodeFs = require('node:fs');
47
+ const crypto = require('node:crypto');
48
+ const fs = require('../fs-native');
49
+ const { parse: parseToml } = require('smol-toml');
50
+ const { WIZZ_FOLDER_NAME } = require('../ide/shared/path-utils');
51
+ const { resolveIncludes, splitIncludeParagraphs, safeSet, hasOwn, isPlainObject } = require('./resolved-includes');
52
+ // Audit item 24 — closed key sets for the user-authored team/user layers.
53
+ const { findUnknownKeys } = require('./resolved-schema');
54
+
55
+ const KEYED_MERGE_FIELDS = ['code', 'id'];
56
+
57
+ // Names inside `_resolved/` that are NOT skill snapshots (never pruned).
58
+ const INDEX_FILE = '.index.json';
59
+ const GITIGNORE_FILE = '.gitignore';
60
+ // `_resolved/` holds generated artifacts only: ignore everything but the
61
+ // ignore file itself, so a `wizz update` never shows up as 340 KB of diff.
62
+ const GITIGNORE_BODY = '*\n!.gitignore\n';
63
+ const INDEX_VERSION = 1;
64
+
65
+ // ─── Merge rules (ported 1:1 from resolve_customization.py) ───
66
+
67
+ /**
68
+ * Return 'code' or 'id' if every item in the array carries that same
69
+ * identifier field; otherwise null (append semantics). Mixed code/id
70
+ * arrays intentionally fall through to append.
71
+ */
72
+ function detectKeyedMergeField(items) {
73
+ if (!Array.isArray(items) || items.length === 0) return null;
74
+ if (!items.every((item) => item !== null && typeof item === 'object' && !Array.isArray(item))) return null;
75
+ for (const candidate of KEYED_MERGE_FIELDS) {
76
+ if (items.every((item) => item[candidate] !== undefined && item[candidate] !== null)) return candidate;
77
+ }
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Keyed merge: matching identifiers replace in place, new ones append.
83
+ * Non-table items in the override are appended (Python parity).
84
+ */
85
+ function mergeByKey(base, override, keyName) {
86
+ const result = [];
87
+ const indexByKey = new Map();
88
+ for (const item of base) {
89
+ if (item === null || typeof item !== 'object' || Array.isArray(item)) continue;
90
+ if (item[keyName] !== undefined && item[keyName] !== null) {
91
+ indexByKey.set(item[keyName], result.length);
92
+ }
93
+ result.push({ ...item });
94
+ }
95
+ for (const item of override) {
96
+ if (item === null || typeof item !== 'object' || Array.isArray(item)) {
97
+ result.push(item);
98
+ continue;
99
+ }
100
+ const key = item[keyName];
101
+ if (key !== undefined && key !== null && indexByKey.has(key)) {
102
+ result[indexByKey.get(key)] = { ...item };
103
+ } else {
104
+ if (key !== undefined && key !== null) indexByKey.set(key, result.length);
105
+ result.push({ ...item });
106
+ }
107
+ }
108
+ return result;
109
+ }
110
+
111
+ /**
112
+ * Shape-aware array merge: keyed merge when every item (base + override)
113
+ * shares `code` or `id`, else append.
114
+ */
115
+ function mergeArrays(base, override) {
116
+ const baseArr = Array.isArray(base) ? base : [];
117
+ const overrideArr = Array.isArray(override) ? override : [];
118
+ const keyedField = detectKeyedMergeField([...baseArr, ...overrideArr]);
119
+ if (keyedField) return mergeByKey(baseArr, overrideArr, keyedField);
120
+ return [...baseArr, ...overrideArr];
121
+ }
122
+
123
+ /**
124
+ * Structural deep merge. Tables deep-merge, arrays shape-merge,
125
+ * everything else: override wins. Python `deep_merge` parity.
126
+ */
127
+ function deepMerge(base, override) {
128
+ if (isPlainObject(base) && isPlainObject(override)) {
129
+ const result = { ...base };
130
+ for (const [key, overVal] of Object.entries(override)) {
131
+ // hasOwn, not `in`: `'constructor' in result` is true via the
132
+ // prototype chain even when the base layer never declared it, which
133
+ // would deep-merge the override into Object's constructor function.
134
+ safeSet(result, key, hasOwn(result, key) ? deepMerge(result[key], overVal) : overVal);
135
+ }
136
+ return result;
137
+ }
138
+ if (Array.isArray(base) && Array.isArray(override)) return mergeArrays(base, override);
139
+ return override;
140
+ }
141
+
142
+ /**
143
+ * Load and parse a TOML layer. Missing file → null (layer absent).
144
+ * Parse/read failure → null + warning (fail-open, Python parity where
145
+ * optional layers warn and degrade to {}).
146
+ */
147
+ function loadTomlLayer(filePath, warnings) {
148
+ let raw;
149
+ try {
150
+ raw = nodeFs.readFileSync(filePath, 'utf8');
151
+ } catch {
152
+ return null;
153
+ }
154
+ try {
155
+ const parsed = parseToml(raw);
156
+ return isPlainObject(parsed) ? parsed : null;
157
+ } catch (error) {
158
+ warnings.push(`failed to parse ${filePath}: ${error.message}`);
159
+ return null;
160
+ }
161
+ }
162
+
163
+ // ─── Generator ───
164
+
165
+ /**
166
+ * Recursively collect paths of every customize.toml under rootDir.
167
+ * Skips `_resolved` itself and hidden dirs.
168
+ */
169
+ function findCustomizeTomls(rootDir) {
170
+ const found = [];
171
+ const stack = [rootDir];
172
+ while (stack.length > 0) {
173
+ const dir = stack.pop();
174
+ let entries;
175
+ try {
176
+ entries = nodeFs.readdirSync(dir, { withFileTypes: true });
177
+ } catch {
178
+ continue;
179
+ }
180
+ for (const entry of entries) {
181
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
182
+ const full = path.join(dir, entry.name);
183
+ if (entry.isDirectory()) {
184
+ if (full === path.join(rootDir, '_resolved')) continue;
185
+ stack.push(full);
186
+ } else if (entry.name === 'customize.toml') {
187
+ found.push(full);
188
+ }
189
+ }
190
+ }
191
+ return found.sort();
192
+ }
193
+
194
+ /**
195
+ * Names of every skill under `wizzDir` that ships a `customize.toml`
196
+ * (i.e. every skill the generator will snapshot).
197
+ * @param {string} wizzDir
198
+ * @returns {string[]} sorted, de-duplicated skill names
199
+ */
200
+ function listSkillNames(wizzDir) {
201
+ const names = new Set(findCustomizeTomls(wizzDir).map((p) => path.basename(path.dirname(p))));
202
+ return [...names].sort();
203
+ }
204
+
205
+ /**
206
+ * Every `customize.toml` a project carries, by skill name — the `_wizz/`
207
+ * tree AND the deployed copies in each IDE's skills folder
208
+ * (`<projectRoot>/.<ide>/skills/<skill>/customize.toml`). They are not the
209
+ * same set: the verbatim pipeline moves each skill directory OUT of
210
+ * `_wizz/` into the IDE folders, so after an install the file the user is
211
+ * told to edit lives at `.claude/skills/<skill>/customize.toml` while
212
+ * `_wizz/` keeps only the shared includes. Shallow scan on purpose.
213
+ *
214
+ * Deterministic by construction: every readdir is sorted, so the order of
215
+ * the paths a skill maps to never depends on the order the filesystem
216
+ * happens to hand back its entries (`_wizz/` staged copy first, then the
217
+ * deployed copies with the IDE directories sorted by name).
218
+ * @param {object} args
219
+ * @param {string} args.projectRoot
220
+ * @param {string} args.wizzDir
221
+ * @returns {Map<string, string[]>} skill name → absolute paths, `_wizz/` first
222
+ */
223
+ function discoverSkillCustomizeTomls({ projectRoot, wizzDir }) {
224
+ const found = new Map();
225
+ const add = (filePath) => {
226
+ const name = path.basename(path.dirname(filePath));
227
+ if (!found.has(name)) found.set(name, []);
228
+ const list = found.get(name);
229
+ if (!list.includes(filePath)) list.push(filePath);
230
+ };
231
+
232
+ for (const filePath of findCustomizeTomls(wizzDir)) add(filePath);
233
+
234
+ let dotDirs = [];
235
+ try {
236
+ dotDirs = nodeFs
237
+ .readdirSync(projectRoot, { withFileTypes: true })
238
+ .filter((e) => e.isDirectory() && e.name.startsWith('.') && e.name !== '.git')
239
+ .map((e) => e.name)
240
+ .sort();
241
+ } catch {
242
+ return found;
243
+ }
244
+ for (const dot of dotDirs) {
245
+ const skillsDir = path.join(projectRoot, dot, 'skills');
246
+ let entries;
247
+ try {
248
+ entries = nodeFs
249
+ .readdirSync(skillsDir, { withFileTypes: true })
250
+ .filter((e) => e.isDirectory())
251
+ .map((e) => e.name)
252
+ .sort();
253
+ } catch {
254
+ continue;
255
+ }
256
+ for (const entry of entries) {
257
+ const candidate = path.join(skillsDir, entry, 'customize.toml');
258
+ if (nodeFs.existsSync(candidate)) add(candidate);
259
+ }
260
+ }
261
+ return found;
262
+ }
263
+
264
+ /** The IDE directory `.claude/skills/<skill>/customize.toml` is deployed from. */
265
+ const PRIMARY_IDE_DIR = '.claude';
266
+
267
+ /**
268
+ * Rank a deployed copy: `.claude/` first (the canonical IDE folder — it is
269
+ * what wizz-customize points the user at and what the docs name), then every
270
+ * other IDE folder alphabetically. Never filesystem order.
271
+ */
272
+ function deployedRank(projectRoot, filePath) {
273
+ const rel = path.relative(projectRoot, filePath);
274
+ const ideDir = rel.split(path.sep)[0];
275
+ return [ideDir === PRIMARY_IDE_DIR ? 0 : 1, rel];
276
+ }
277
+
278
+ /**
279
+ * ONE customize.toml per skill, DEPLOYED copy preferred over the `_wizz/`
280
+ * staged copy. After an install the file the user edits (and the one
281
+ * wizz-customize points at) is `.claude/skills/<skill>/customize.toml`; the
282
+ * staged copy under `_wizz/` is only what shipped. `wizz resolve` must
283
+ * regenerate from what the user edits, so it asks for this list.
284
+ *
285
+ * The choice is deterministic, never filesystem order: a project that
286
+ * deploys to several IDE folders (`.claude/`, `.agents/`, `.cline/`, …)
287
+ * carries the same skill several times, and picking "whichever readdir
288
+ * returned first" made `wizz resolve` resolve from a different copy on a
289
+ * different machine. Order is: `.claude/skills/<skill>/customize.toml`, then
290
+ * the other deployed copies by path, then the `_wizz/` staged copy. When two
291
+ * or more deployed copies exist AND their bytes differ, that is a real
292
+ * divergence (one IDE folder was edited and the others were not) and it
293
+ * warns, naming both paths and the one that won.
294
+ *
295
+ * @param {object} args
296
+ * @param {string} args.projectRoot
297
+ * @param {string} args.wizzDir
298
+ * @param {string[]} [args.warnings] - sink for divergence warnings
299
+ * @returns {string[]} absolute paths, ordered by skill name
300
+ */
301
+ function preferredCustomizePaths({ projectRoot, wizzDir, warnings }) {
302
+ const byName = discoverSkillCustomizeTomls({ projectRoot, wizzDir });
303
+ const chosen = [];
304
+ for (const name of [...byName.keys()].sort()) {
305
+ const paths = byName.get(name);
306
+ const deployed = paths
307
+ .filter((p) => {
308
+ const rel = path.relative(wizzDir, p);
309
+ return rel.startsWith('..') || path.isAbsolute(rel);
310
+ })
311
+ .sort((a, b) => {
312
+ const [rankA, relA] = deployedRank(projectRoot, a);
313
+ const [rankB, relB] = deployedRank(projectRoot, b);
314
+ if (rankA !== rankB) return rankA - rankB;
315
+ return relA < relB ? -1 : relA > relB ? 1 : 0;
316
+ });
317
+ const winner = deployed[0] || paths[0];
318
+ chosen.push(winner);
319
+
320
+ if (warnings && deployed.length > 1) {
321
+ const winnerHash = sha256File(winner);
322
+ const divergent = deployed.slice(1).filter((p) => sha256File(p) !== winnerHash);
323
+ if (divergent.length > 0) {
324
+ warnings.push(
325
+ `skill ${name}: deployed customize.toml copies diverge — ${[winner, ...divergent].join(' vs ')}; ` +
326
+ `resolved from ${winner} (run wizz update to redeploy the others)`,
327
+ );
328
+ }
329
+ }
330
+ }
331
+ return chosen;
332
+ }
333
+
334
+ /** Human-readable locations `wizz resolve` looked in, for the not-found message. */
335
+ function searchedLocations({ projectRoot, wizzDir, skillName }) {
336
+ return [path.join(wizzDir, '**', skillName, 'customize.toml'), path.join(projectRoot, '.<ide>', 'skills', skillName, 'customize.toml')];
337
+ }
338
+
339
+ // ─── Inputs index (staleness detection, audit item 19 / bug 4) ───
340
+
341
+ /**
342
+ * sha256 of a file's bytes, or null when it can't be read. Same digest the
343
+ * installer's `calculateFileHash` produces, so doctor can compare either way.
344
+ */
345
+ function sha256File(absPath) {
346
+ try {
347
+ return crypto.createHash('sha256').update(nodeFs.readFileSync(absPath)).digest('hex');
348
+ } catch {
349
+ return null;
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Path as recorded in `.index.json`: POSIX, relative to the PROJECT root,
355
+ * with anything inside the install rewritten under `_wizz/` so a fresh
356
+ * install (which resolves inside a throwaway `_wizz.tmp-<pid>/` scaffold)
357
+ * records the path the file will have after the atomic swap.
358
+ */
359
+ function indexRelativePath(projectRoot, wizzDir, absPath) {
360
+ const toPosix = (p) => p.split(path.sep).join('/');
361
+ const relToWizz = path.relative(wizzDir, absPath);
362
+ if (relToWizz && !relToWizz.startsWith('..') && !path.isAbsolute(relToWizz)) {
363
+ return `${WIZZ_FOLDER_NAME}/${toPosix(relToWizz)}`;
364
+ }
365
+ return toPosix(path.relative(projectRoot, absPath));
366
+ }
367
+
368
+ /**
369
+ * Hash every input that exists into `{ "<relative path>": "<sha256>" }`,
370
+ * sorted by path so the index is byte-stable across runs. Pure: no writes,
371
+ * no warnings. Inputs that do not exist are omitted — a layer the user has
372
+ * not created yet has nothing to hash, so `wizz doctor` cannot see a
373
+ * brand-new team override (install/update/resolve regenerate regardless).
374
+ * @param {object} args
375
+ * @param {string} args.projectRoot
376
+ * @param {string} args.wizzDir
377
+ * @param {string[]} args.inputPaths - absolute paths
378
+ * @returns {Object<string, string>}
379
+ */
380
+ function computeInputsIndex({ projectRoot, wizzDir, inputPaths }) {
381
+ const pairs = [];
382
+ const seen = new Set();
383
+ for (const abs of inputPaths) {
384
+ if (seen.has(abs)) continue;
385
+ seen.add(abs);
386
+ const hash = sha256File(abs);
387
+ if (hash === null) continue;
388
+ pairs.push([indexRelativePath(projectRoot, wizzDir, abs), hash]);
389
+ }
390
+ pairs.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
391
+ return Object.fromEntries(pairs);
392
+ }
393
+
394
+ /**
395
+ * Read `_resolved/.index.json`. Missing/unreadable/malformed → null (the
396
+ * caller treats that as "never generated"), never throws.
397
+ * @param {string} resolvedDir
398
+ * @returns {object|null}
399
+ */
400
+ function readIndex(resolvedDir) {
401
+ try {
402
+ const parsed = JSON.parse(nodeFs.readFileSync(path.join(resolvedDir, INDEX_FILE), 'utf8'));
403
+ return isPlainObject(parsed) ? parsed : null;
404
+ } catch {
405
+ return null;
406
+ }
407
+ }
408
+
409
+ // ─── Snapshot writing ───
410
+
411
+ /**
412
+ * Resolve and write ONE skill's snapshot.
413
+ *
414
+ * The base layer is load-bearing: a snapshot written from a `{}` base would
415
+ * be a fresh, valid-looking file with no persona and no persistent_facts,
416
+ * and the runtime reads it unconditionally — the agent would activate empty
417
+ * with no signal. So a broken base is a SKIP: any stale snapshot is
418
+ * deleted, the runtime falls back to `resolve_customization.py`, and the
419
+ * user is warned. Team/user layers stay fail-open (losing an override
420
+ * degrades to the shipped defaults, a safe state).
421
+ *
422
+ * @returns {Promise<{skillName: string, status: 'written'|'skipped', warnings: string[], entry: object|null}>}
423
+ */
424
+ async function resolveSkillSnapshot({ customizePath, customDir, resolvedDir, projectRoot, wizzDir }) {
425
+ const skillName = path.basename(path.dirname(customizePath));
426
+ const warnings = [];
427
+ const outPath = path.join(resolvedDir, `${skillName}.json`);
428
+
429
+ const baseRaw = loadTomlLayer(customizePath, warnings);
430
+ if (!isPlainObject(baseRaw)) {
431
+ warnings.push(
432
+ `base customize.toml inválido ou ilegível (${customizePath}) — snapshot NÃO gravado; ` +
433
+ `a ativação cai no fallback resolve_customization.py até você corrigir o arquivo e rodar wizz resolve`,
434
+ );
435
+ await fs.remove(outPath);
436
+ return { skillName, status: 'skipped', warnings, entry: null };
437
+ }
438
+
439
+ const inputPaths = [customizePath];
440
+ const teamPath = path.join(customDir, `${skillName}.toml`);
441
+ const userPath = path.join(customDir, `${skillName}.user.toml`);
442
+ inputPaths.push(teamPath, userPath);
443
+
444
+ // `context` carries the `_shared/` fallback: a skill deployed to
445
+ // `.claude/skills/<skill>/` keeps `include = ["../../_shared/x.md"]`, which
446
+ // from there points at a directory that does not exist. See resolved-includes.js.
447
+ const context = { projectRoot, skillName };
448
+ const defaults = resolveIncludes(baseRaw, path.dirname(customizePath), warnings, inputPaths, context);
449
+ const teamRaw = loadTomlLayer(teamPath, warnings);
450
+ const userRaw = loadTomlLayer(userPath, warnings);
451
+ const team = resolveIncludes(teamRaw || {}, customDir, warnings, inputPaths, context);
452
+ const user = resolveIncludes(userRaw || {}, customDir, warnings, inputPaths, context);
453
+
454
+ // Audit item 24: closed-key warnings for user-authored layers. The base
455
+ // layer is exempt (it owns the LLM-only fields and new experimental
456
+ // keys); a typo here is exactly the silent-merge the audit flagged.
457
+ // Warnings, never errors — the snapshot is still written (fail-open).
458
+ for (const [layerName, layer] of [
459
+ ['team', teamRaw],
460
+ ['user', userRaw],
461
+ ]) {
462
+ for (const unknown of findUnknownKeys(layer)) {
463
+ warnings.push(`${layerName} override: unknown key "${unknown}" (not a recognized workflow field — typo? check customize.toml docs)`);
464
+ }
465
+ }
466
+
467
+ const merged = deepMerge(deepMerge(defaults, team), user);
468
+ const body = `${JSON.stringify(merged, null, 2)}\n`;
469
+ // Atomic write via fs-native (tmp + rename), consistent with every other
470
+ // config file the installer writes.
471
+ await fs.writeFile(outPath, body, 'utf8');
472
+
473
+ return {
474
+ skillName,
475
+ status: 'written',
476
+ warnings,
477
+ entry: {
478
+ inputs: computeInputsIndex({ projectRoot, wizzDir, inputPaths }),
479
+ output: crypto.createHash('sha256').update(body).digest('hex'),
480
+ },
481
+ };
482
+ }
483
+
484
+ /**
485
+ * Remove `_resolved/*.json` snapshots whose skill no longer exists (a
486
+ * module uninstalled, a skill renamed). `.index.json` and `.gitignore`
487
+ * are never touched here.
488
+ * @returns {Promise<string[]>} names of the pruned files
489
+ */
490
+ async function pruneOrphanSnapshots(resolvedDir, keepNames) {
491
+ let entries;
492
+ try {
493
+ entries = nodeFs.readdirSync(resolvedDir, { withFileTypes: true });
494
+ } catch {
495
+ return [];
496
+ }
497
+ const pruned = [];
498
+ for (const entry of entries) {
499
+ if (!entry.isFile()) continue;
500
+ if (entry.name === INDEX_FILE || entry.name === GITIGNORE_FILE) continue;
501
+ if (!entry.name.endsWith('.json')) continue;
502
+ if (keepNames.has(entry.name)) continue;
503
+ await fs.remove(path.join(resolvedDir, entry.name));
504
+ pruned.push(entry.name);
505
+ }
506
+ return pruned;
507
+ }
508
+
509
+ /** Write `_resolved/.gitignore` (generated artifacts stay out of git). */
510
+ async function writeResolvedGitignore(resolvedDir) {
511
+ await fs.writeFile(path.join(resolvedDir, GITIGNORE_FILE), GITIGNORE_BODY, 'utf8');
512
+ }
513
+
514
+ /**
515
+ * Write `_resolved/.index.json` — the staleness oracle. Activation never
516
+ * reads it (it reads the snapshot and nothing else); `wizz doctor` does,
517
+ * comparing each recorded input hash against the file on disk.
518
+ *
519
+ * `config` carries the same shape for `_config.json` (the central-config
520
+ * snapshot, item 29); null when that snapshot could not be written.
521
+ */
522
+ async function writeResolvedIndex(resolvedDir, skills, config = null) {
523
+ const sorted = Object.fromEntries(Object.entries(skills).sort((a, b) => (a[0] < b[0] ? -1 : 1)));
524
+ const index = { version: INDEX_VERSION, generated_at: new Date().toISOString(), skills: sorted, config };
525
+ await fs.writeFile(path.join(resolvedDir, INDEX_FILE), `${JSON.stringify(index, null, 2)}\n`, 'utf8');
526
+ }
527
+
528
+ /**
529
+ * Generate `_wizz/_resolved/<skill-name>.json` for every skill dir under
530
+ * `wizzDir` that carries a `customize.toml`, plus `.index.json` (input
531
+ * hashes for staleness detection) and `.gitignore`.
532
+ *
533
+ * The base layer comes from the freshly written tree (`wizzDir`, which may
534
+ * be the A14 tmp scaffold on a fresh install), but the team/user layers
535
+ * are read from the REAL project's `_wizz/custom/` — user customizations
536
+ * survive a fresh install, so the snapshot must reflect them even when
537
+ * `wizzDir` is a throwaway scaffold that never saw them.
538
+ *
539
+ * Nothing written here is tracked in `files-manifest.csv` — see the header.
540
+ *
541
+ * @param {Object} options
542
+ * @param {string} options.wizzDir - The (freshly written) `_wizz/` dir.
543
+ * @param {string} options.projectRoot - Real project root (for `_wizz/custom/`).
544
+ * @param {Function} [options.warn] - Async warn sink (defaults to console).
545
+ * @returns {Promise<{written: number, skipped: number, pruned: string[], warnings: string[]}>}
546
+ */
547
+ async function resolveAll({ wizzDir, projectRoot, warn, includeDeployed = false }) {
548
+ const warnSink = warn || (async (msg) => console.warn(msg));
549
+ const customDir = path.join(projectRoot, WIZZ_FOLDER_NAME, 'custom');
550
+ const resolvedDir = path.join(wizzDir, '_resolved');
551
+ const emitted = [];
552
+ const emit = async (line) => {
553
+ emitted.push(line);
554
+ await warnSink(line);
555
+ };
556
+ // Install/update stay on the staged tree (`wizzDir` may be the A14 tmp
557
+ // scaffold, and the deployed copies still belong to the PREVIOUS install).
558
+ // `wizz resolve` opts into the deployed copies, which is what the user edits.
559
+ const discoveryWarnings = [];
560
+ const customizePaths = includeDeployed
561
+ ? preferredCustomizePaths({ projectRoot, wizzDir, warnings: discoveryWarnings })
562
+ : findCustomizeTomls(wizzDir);
563
+ for (const warning of discoveryWarnings) await emit(`_resolved: ${warning}`);
564
+
565
+ // NO early return on an empty list: a project that just lost its last
566
+ // customize.toml still needs its orphan snapshots pruned and an empty
567
+ // index written, otherwise `_resolved/` keeps serving stale snapshots for
568
+ // skills that no longer exist and doctor compares against an old index.
569
+ await fs.mkdir(resolvedDir, { recursive: true });
570
+ let written = 0;
571
+ let skipped = 0;
572
+ const skills = {};
573
+ const keepNames = new Set();
574
+
575
+ for (const customizePath of customizePaths) {
576
+ const skillName = path.basename(path.dirname(customizePath));
577
+ keepNames.add(`${skillName}.json`);
578
+ let result;
579
+ try {
580
+ result = await resolveSkillSnapshot({ customizePath, customDir, resolvedDir, projectRoot, wizzDir });
581
+ } catch (error) {
582
+ // Fail-open: one broken skill never blocks the install.
583
+ result = { skillName, status: 'skipped', warnings: [`skill ${skillName}: ${error.message}`], entry: null };
584
+ }
585
+ if (result.status === 'written') {
586
+ written++;
587
+ skills[result.skillName] = result.entry;
588
+ } else {
589
+ skipped++;
590
+ }
591
+ for (const warning of result.warnings) await emit(`_resolved/${result.skillName}: ${warning}`);
592
+ }
593
+
594
+ // Conservador de propósito: só é órfão o snapshot de uma skill que não
595
+ // tem customize.toml em lugar nenhum do projeto. O pipeline verbatim move
596
+ // os diretórios de skill de `_wizz/` para as pastas dos IDEs, então "não
597
+ // está no wizzDir" NÃO significa "foi desinstalada" — podar por esse
598
+ // critério apagaria snapshots válidos.
599
+ for (const name of discoverSkillCustomizeTomls({ projectRoot, wizzDir }).keys()) keepNames.add(`${name}.json`);
600
+
601
+ // Central config (item 29): `_resolved/_config.json` replaces the runtime
602
+ // `python3 resolve_config.py` call. Lazy require — resolved-config.js
603
+ // imports this module's merge rules, so a top-level require here would
604
+ // close the cycle and hand it a half-built exports object.
605
+ const { writeResolvedConfig, CONFIG_SNAPSHOT } = require('./resolved-config');
606
+ keepNames.add(CONFIG_SNAPSHOT);
607
+ const config = await writeResolvedConfig({ projectRoot, wizzDir, resolvedDir });
608
+ for (const warning of config.warnings) await emit(`_resolved/${CONFIG_SNAPSHOT}: ${warning}`);
609
+
610
+ const pruned = await pruneOrphanSnapshots(resolvedDir, keepNames);
611
+ await writeResolvedGitignore(resolvedDir);
612
+ await writeResolvedIndex(resolvedDir, skills, config.entry);
613
+
614
+ return { written, skipped, pruned, warnings: emitted, config: config.written };
615
+ }
616
+
617
+ /**
618
+ * Regenerate ONE skill's snapshot in place, preserving every other entry of
619
+ * `.index.json`. Used by `wizz resolve --skill <name>` (and by anything that
620
+ * edits a single customize layer, e.g. the wizz-customize skill).
621
+ * @param {string} projectRoot
622
+ * @param {string} wizzDir
623
+ * @param {string} skillName
624
+ * When `.index.json` is missing, unreadable or malformed there is no "every
625
+ * other entry" left to preserve: rewriting it with only this skill would
626
+ * silently drop every other skill from the staleness oracle, and doctor
627
+ * would then report them as "sem snapshot" forever. So that case falls back
628
+ * to the all-skills path (`resolveAll`), which rebuilds the FULL index.
629
+ *
630
+ * @param {string} projectRoot
631
+ * @param {string} wizzDir
632
+ * @param {string} skillName
633
+ * @param {object} [options] - { warn, includeDeployed }
634
+ * @returns {Promise<{written: number, skipped: number, warnings: string[], found: boolean}>}
635
+ */
636
+ async function resolveOne(projectRoot, wizzDir, skillName, options = {}) {
637
+ const warnSink = options.warn || (async (msg) => console.warn(msg));
638
+ const customDir = path.join(projectRoot, WIZZ_FOLDER_NAME, 'custom');
639
+ const resolvedDir = path.join(wizzDir, '_resolved');
640
+ const discoveryWarnings = [];
641
+ const candidates = options.includeDeployed
642
+ ? preferredCustomizePaths({ projectRoot, wizzDir, warnings: discoveryWarnings })
643
+ : findCustomizeTomls(wizzDir);
644
+ const customizePath = candidates.find((p) => path.basename(path.dirname(p)) === skillName);
645
+ const emitted = [];
646
+ if (!customizePath) {
647
+ return { written: 0, skipped: 0, warnings: emitted, found: false, searched: searchedLocations({ projectRoot, wizzDir, skillName }) };
648
+ }
649
+ await fs.mkdir(resolvedDir, { recursive: true });
650
+
651
+ const previous = readIndex(resolvedDir);
652
+ if (!previous || !isPlainObject(previous.skills)) {
653
+ // (the fallback below re-derives the discovery warnings itself)
654
+ // No usable index: regenerate ALL of it rather than replacing it with a
655
+ // one-entry index that erases every other skill.
656
+ const all = await resolveAll({ wizzDir, projectRoot, warn: options.warn, includeDeployed: options.includeDeployed });
657
+ const wrote = nodeFs.existsSync(path.join(resolvedDir, `${skillName}.json`));
658
+ return {
659
+ written: wrote ? 1 : 0,
660
+ skipped: wrote ? 0 : 1,
661
+ warnings: [...emitted, ...all.warnings],
662
+ found: true,
663
+ customizePath,
664
+ regeneratedIndex: true,
665
+ };
666
+ }
667
+
668
+ // Only the divergence warning for THIS skill is relevant to a --skill run.
669
+ for (const warning of discoveryWarnings.filter((w) => w.startsWith(`skill ${skillName}:`))) {
670
+ const line = `_resolved: ${warning}`;
671
+ emitted.push(line);
672
+ await warnSink(line);
673
+ }
674
+
675
+ let result;
676
+ try {
677
+ result = await resolveSkillSnapshot({ customizePath, customDir, resolvedDir, projectRoot, wizzDir });
678
+ } catch (error) {
679
+ result = { skillName, status: 'skipped', warnings: [`skill ${skillName}: ${error.message}`], entry: null };
680
+ }
681
+
682
+ const skills = { ...previous.skills };
683
+ if (result.status === 'written') {
684
+ skills[skillName] = result.entry;
685
+ } else {
686
+ delete skills[skillName];
687
+ }
688
+ await writeResolvedGitignore(resolvedDir);
689
+ // A --skill run touches one snapshot: the central-config entry of the
690
+ // index it is patching must survive untouched, not be dropped to null.
691
+ await writeResolvedIndex(resolvedDir, skills, previous.config || null);
692
+
693
+ for (const warning of result.warnings) {
694
+ const line = `_resolved/${skillName}: ${warning}`;
695
+ emitted.push(line);
696
+ await warnSink(line);
697
+ }
698
+ return {
699
+ written: result.status === 'written' ? 1 : 0,
700
+ skipped: result.status === 'skipped' ? 1 : 0,
701
+ warnings: emitted,
702
+ found: true,
703
+ customizePath,
704
+ };
705
+ }
706
+
707
+ /**
708
+ * Back-compat wrapper kept for the installer call site: same behaviour as
709
+ * `resolveAll`, but returns just the number of snapshots written.
710
+ * @returns {Promise<number>}
711
+ */
712
+ async function generateResolvedCustomizations(options) {
713
+ const { written } = await resolveAll(options);
714
+ return written;
715
+ }
716
+
717
+ module.exports = {
718
+ generateResolvedCustomizations,
719
+ resolveAll,
720
+ resolveOne,
721
+ deepMerge,
722
+ loadTomlLayer,
723
+ resolveIncludes,
724
+ splitIncludeParagraphs,
725
+ findUnknownKeys,
726
+ listSkillNames,
727
+ discoverSkillCustomizeTomls,
728
+ preferredCustomizePaths,
729
+ computeInputsIndex,
730
+ indexRelativePath,
731
+ readIndex,
732
+ writeResolvedIndex,
733
+ sha256File,
734
+ INDEX_FILE,
735
+ GITIGNORE_FILE,
736
+ };