wizz-method 2.0.0 → 2.1.0

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 (310) 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 +62 -37
  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/install.js +1 -1
  156. package/tools/installer/commands/resolve.js +95 -0
  157. package/tools/installer/core/doctor.js +648 -0
  158. package/tools/installer/core/hash-cache.js +156 -0
  159. package/tools/installer/core/help-catalog.js +4 -1
  160. package/tools/installer/core/hooks-settings.js +178 -0
  161. package/tools/installer/core/installer.js +297 -15
  162. package/tools/installer/core/legacy-warnings.js +0 -4
  163. package/tools/installer/core/manifest-generator.js +121 -43
  164. package/tools/installer/core/manifest.js +36 -32
  165. package/tools/installer/core/module-config-writer.js +4 -1
  166. package/tools/installer/core/python-check.js +60 -63
  167. package/tools/installer/core/quick-update.js +76 -43
  168. package/tools/installer/core/resolved-config.js +168 -0
  169. package/tools/installer/core/resolved-customizations.js +736 -0
  170. package/tools/installer/core/resolved-includes.js +262 -0
  171. package/tools/installer/core/resolved-schema.js +148 -0
  172. package/tools/installer/core/user-file-preservation.js +40 -8
  173. package/tools/installer/core/wizz-dir.js +117 -0
  174. package/tools/installer/core/yaml-cache.js +105 -0
  175. package/tools/installer/fs-native.js +97 -6
  176. package/tools/installer/ide/_config-driven.js +12 -75
  177. package/tools/installer/ide/platform-codes.js +111 -16
  178. package/tools/installer/ide/platform-codes.yaml +45 -100
  179. package/tools/installer/ide/shared/path-utils.js +6 -208
  180. package/tools/installer/install-messages.yaml +4 -14
  181. package/tools/installer/modules/channel-plan.js +15 -2
  182. package/tools/installer/modules/config-collector.js +762 -0
  183. package/tools/installer/modules/config-values.js +352 -0
  184. package/tools/installer/modules/custom-module-manager.js +161 -28
  185. package/tools/installer/modules/env-vars.js +119 -0
  186. package/tools/installer/modules/external-manager.js +133 -60
  187. package/tools/installer/modules/git-cache-utils.js +125 -0
  188. package/tools/installer/modules/module-catalog.js +565 -0
  189. package/tools/installer/modules/module-installer.js +618 -0
  190. package/tools/installer/modules/module-interface.js +86 -0
  191. package/tools/installer/modules/official-modules.js +98 -2172
  192. package/tools/installer/modules/pin-spec.js +125 -0
  193. package/tools/installer/modules/plugin-resolver.js +4 -0
  194. package/tools/installer/modules/skills-lib.js +3 -6
  195. package/tools/installer/prompts.js +0 -3
  196. package/tools/installer/ui.js +89 -54
  197. package/tools/installer/wizz-cli.js +1 -1
  198. package/tools/lib/frontmatter.js +139 -0
  199. package/tools/lib/sync-targets.mjs +49 -0
  200. package/tools/lib/walk.js +93 -0
  201. package/wizz-modules.yaml +9 -0
  202. package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
  203. package/src/scripts/memlog.py +0 -233
  204. package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
  205. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
  206. package/src/scripts/tests/test_memlog.py +0 -306
  207. package/src/skills-lib/wizz-huashu-design/assets/showcases/INDEX.md +0 -115
  208. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.html +0 -235
  209. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.png +0 -0
  210. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.html +0 -229
  211. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.png +0 -0
  212. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.html +0 -288
  213. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.png +0 -0
  214. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.html +0 -503
  215. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.png +0 -0
  216. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.html +0 -600
  217. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.png +0 -0
  218. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.html +0 -670
  219. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.png +0 -0
  220. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.html +0 -382
  221. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.png +0 -0
  222. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.html +0 -536
  223. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.png +0 -0
  224. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.html +0 -497
  225. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.png +0 -0
  226. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.html +0 -385
  227. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.png +0 -0
  228. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.html +0 -422
  229. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.png +0 -0
  230. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.html +0 -499
  231. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.png +0 -0
  232. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.html +0 -562
  233. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.png +0 -0
  234. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.html +0 -548
  235. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.png +0 -0
  236. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.html +0 -696
  237. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.png +0 -0
  238. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.html +0 -372
  239. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.png +0 -0
  240. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.html +0 -460
  241. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.png +0 -0
  242. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.html +0 -494
  243. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.png +0 -0
  244. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.html +0 -367
  245. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.png +0 -0
  246. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.html +0 -368
  247. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.png +0 -0
  248. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.html +0 -459
  249. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.png +0 -0
  250. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.html +0 -493
  251. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.png +0 -0
  252. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.html +0 -556
  253. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.png +0 -0
  254. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.html +0 -604
  255. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.png +0 -0
  256. package/tools/installer/file-ops.js +0 -204
  257. package/tools/installer/ide/shared/skill-manifest.js +0 -57
  258. package/tools/installer/yaml-format.js +0 -245
  259. package/web-bundles/README.md +0 -46
  260. package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +0 -86
  261. package/web-bundles/brainstorming-coach/SKILL.md +0 -83
  262. package/web-bundles/brainstorming-coach/brain-methods.csv +0 -62
  263. package/web-bundles/bundles.json +0 -139
  264. package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +0 -88
  265. package/web-bundles/market-and-industry-research/SKILL.md +0 -59
  266. package/web-bundles/prd-coach/INSTRUCTIONS.md +0 -86
  267. package/web-bundles/prd-coach/SKILL.md +0 -101
  268. package/web-bundles/prd-coach/prd-template.md +0 -165
  269. package/web-bundles/prd-coach/prd-validation-checklist.md +0 -135
  270. package/web-bundles/prfaq-coach/INSTRUCTIONS.md +0 -86
  271. package/web-bundles/prfaq-coach/SKILL.md +0 -139
  272. package/web-bundles/product-brief-coach/INSTRUCTIONS.md +0 -86
  273. package/web-bundles/product-brief-coach/SKILL.md +0 -113
  274. package/web-bundles/ux-coach/INSTRUCTIONS.md +0 -92
  275. package/web-bundles/ux-coach/SKILL.md +0 -187
  276. package/web-bundles/ux-coach/ux-validation.md +0 -100
  277. /package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/mapa-do-funil.md +0 -0
  278. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/CATALOGO.md +0 -0
  279. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/01-principios.md +0 -0
  280. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/02-diagnostico-funil.md +0 -0
  281. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/03-campanha-bidcap.md +0 -0
  282. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04-criativos.md +0 -0
  283. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04a-criativos-ia.md +0 -0
  284. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04b-criativos-formatos.md +0 -0
  285. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/05-oferta.md +0 -0
  286. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06-pagina-vendas.md +0 -0
  287. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06a-instagram.md +0 -0
  288. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06b-player-vsl.md +0 -0
  289. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07-funil-upsell.md +0 -0
  290. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07a-copy-humanizacao.md +0 -0
  291. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/08-pesquisa-publico.md +0 -0
  292. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/09-especialista-coproducao.md +0 -0
  293. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/10-levantar-caixa.md +0 -0
  294. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/11-checklist-roi.md +0 -0
  295. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12-mineracao-ofertas.md +0 -0
  296. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12a-mineracao-veredito.md +0 -0
  297. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12b-fabrica-de-ofertas.md +0 -0
  298. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12c-anatomia-low-ticket.md +0 -0
  299. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/13-benchmarks-vsl.md +0 -0
  300. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/14-vocabulario.md +0 -0
  301. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/15-como-estender.md +0 -0
  302. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/17-rastreamento.md +0 -0
  303. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/18-copy-desejo-oferta.md +0 -0
  304. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/INDEX.md +0 -0
  305. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/guardrails.md +0 -0
  306. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/pontos-em-aberto.md +0 -0
  307. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/references/swipe-file.md +0 -0
  308. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/metas-e-medicao.md +0 -0
  309. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/padroes-de-codigo.md +0 -0
  310. /package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/formato-do-diagnostico.md +0 -0
@@ -3,7 +3,6 @@ const https = require('node:https');
3
3
  const { execFile } = require('node:child_process');
4
4
  const { promisify } = require('node:util');
5
5
  const fs = require('../fs-native');
6
- const crypto = require('node:crypto');
7
6
  const { resolveModuleVersion } = require('../modules/version-resolver');
8
7
  const prompts = require('../prompts');
9
8
 
@@ -317,17 +316,14 @@ class Manifest {
317
316
  }
318
317
 
319
318
  /**
320
- * Calculate SHA256 hash of a file
319
+ * Calculate SHA256 hash of a file. Delegates to the single canonical
320
+ * implementation in fs-native.js (memoized by path+size+mtime within the
321
+ * same process) — see F06/F10 in the 2026-09-11 audit.
321
322
  * @param {string} filePath - Path to file
322
- * @returns {string} SHA256 hash
323
+ * @returns {string|null} SHA256 hash, or null if the file can't be read
323
324
  */
324
325
  async calculateFileHash(filePath) {
325
- try {
326
- const content = await fs.readFile(filePath);
327
- return crypto.createHash('sha256').update(content).digest('hex');
328
- } catch {
329
- return null;
330
- }
326
+ return fs.calculateFileHash(filePath);
331
327
  }
332
328
 
333
329
  /**
@@ -445,33 +441,41 @@ class Manifest {
445
441
  async checkForUpdates(wizzDir) {
446
442
  const semver = require('semver');
447
443
  const modules = await this.getAllModuleVersions(wizzDir);
448
- const updates = [];
449
444
 
450
- for (const module of modules) {
451
- if (!module.npmPackage) {
452
- continue; // Skip modules without npm package (built-in)
453
- }
454
-
455
- const latestVersion = await this.fetchNpmVersion(module.npmPackage);
456
- if (!latestVersion) {
457
- continue;
458
- }
445
+ // One npm lookup per module (subprocess + HTTPS fallback, 10s timeout
446
+ // each) has no dependency between iterations, so resolve them all
447
+ // concurrently instead of N sequential round-trips (F04). fetchNpmVersion
448
+ // never throws — it already resolves to null on any per-module failure —
449
+ // so a single module's error can't sink the others, and Promise.all
450
+ // preserves `modules` order in `results` regardless of resolution timing.
451
+ const results = await Promise.all(
452
+ modules.map(async (module) => {
453
+ if (!module.npmPackage) {
454
+ return null; // Skip modules without npm package (built-in)
455
+ }
459
456
 
460
- const installedVersion = semver.valid(module.version) || semver.valid(semver.coerce(module.version || ''));
461
- const availableVersion = semver.valid(latestVersion) || semver.valid(semver.coerce(latestVersion));
457
+ const latestVersion = await this.fetchNpmVersion(module.npmPackage);
458
+ if (!latestVersion) {
459
+ return null;
460
+ }
462
461
 
463
- if (installedVersion && availableVersion && semver.gt(availableVersion, installedVersion)) {
464
- updates.push({
465
- name: module.name,
466
- installedVersion: module.version,
467
- latestVersion: latestVersion,
468
- npmPackage: module.npmPackage,
469
- updateAvailable: true,
470
- });
471
- }
472
- }
462
+ const installedVersion = semver.valid(module.version) || semver.valid(semver.coerce(module.version || ''));
463
+ const availableVersion = semver.valid(latestVersion) || semver.valid(semver.coerce(latestVersion));
464
+
465
+ if (installedVersion && availableVersion && semver.gt(availableVersion, installedVersion)) {
466
+ return {
467
+ name: module.name,
468
+ installedVersion: module.version,
469
+ latestVersion: latestVersion,
470
+ npmPackage: module.npmPackage,
471
+ updateAvailable: true,
472
+ };
473
+ }
474
+ return null;
475
+ }),
476
+ );
473
477
 
474
- return updates;
478
+ return results.filter(Boolean);
475
479
  }
476
480
  }
477
481
 
@@ -25,7 +25,10 @@ async function generateModuleConfigs(wizzDir, moduleConfigs, { trackFile }) {
25
25
 
26
26
  // Get all installed module directories
27
27
  const entries = await fs.readdir(wizzDir, { withFileTypes: true });
28
- const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
28
+ // 'hooks' (RT-C1, tools/hooks/* copied here by Installer#_installHooks) is
29
+ // infra, not a module — without this exclusion this scanner treated it as
30
+ // one and wrote a spurious _wizz/hooks/config.yaml.
31
+ const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom', 'hooks']);
29
32
  const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
30
33
 
31
34
  // Generate config.yaml for each installed module
@@ -1,16 +1,27 @@
1
1
  const { spawnSync } = require('node:child_process');
2
2
  const prompts = require('../prompts');
3
3
 
4
- // Python 3.11 added stdlib `tomllib` (PEP 680), which the shared scripts in
5
- // src/scripts/ (resolve_config.py, resolve_customization.py) require to read
6
- // WIZZ's TOML config files. memlog.py is more lenient and runs on 3.8+.
4
+ // Since audit item 29 (2026-09-12) Node 20.12+ is the ONLY prerequisite of the
5
+ // Wizz core: memlog is Node (src/scripts/memlog.js), and every TOML merge the
6
+ // core needs is resolved at install time into `_wizz/_resolved/` by the
7
+ // installer's own smol-toml resolver. Python is now optional and belongs to
8
+ // the BMAD skills that ship their own Python scripts.
9
+ //
10
+ // The version bands stay as they are because those skill-owned scripts still
11
+ // read TOML: Python 3.11 added stdlib `tomllib` (PEP 680), and the older
12
+ // scripts run on 3.8+.
7
13
  const PYTHON_FULL_SUPPORT = { major: 3, minor: 11 };
8
14
  const PYTHON_PARTIAL_SUPPORT = { major: 3, minor: 8 };
9
15
 
10
- // Every runtime call site (skill steps, on_complete hooks) invokes a literal
11
- // `python3`, so only that command's version vouches for WIZZ features. The
12
- // fallback probes exist to tell the user "Python is installed, but not under
13
- // the name WIZZ uses" instead of a misleading "No Python found".
16
+ // The skills that stop working without `python3` — named explicitly so the
17
+ // warning says what the user loses instead of a vague "some features".
18
+ const PYTHON_ONLY_SKILLS =
19
+ 'wizz-brainstorming, wizz-retrospective, wizz-sprint-planning, wizz-architecture, wizz-party-mode, wizz-forge-idea, wizz-advanced-elicitation, wizz-customize';
20
+
21
+ // Every skill-owned call site invokes a literal `python3`, so only that
22
+ // command's version vouches for those skills. The fallback probes exist to
23
+ // tell the user "Python is installed, but not under the name WIZZ uses"
24
+ // instead of a misleading "No Python found".
14
25
  const RUNTIME_COMMAND = 'python3';
15
26
  const PROBE_CANDIDATES =
16
27
  process.platform === 'win32'
@@ -108,7 +119,7 @@ function detectPython() {
108
119
 
109
120
  function upgradeHints() {
110
121
  return [
111
- 'How to get Python 3.11+ (as `python3`):',
122
+ 'How to get Python 3.11+ (as `python3`), if you want those skills:',
112
123
  ' macOS: brew install python3',
113
124
  ' Windows: winget install Python.Python.3.12 (then ensure `python3` resolves, e.g. enable the python3 alias)',
114
125
  ' Linux/WSL: sudo apt install python3 (Ubuntu 24.04+ ships 3.12; older distros: use pyenv or deadsnakes)',
@@ -117,83 +128,69 @@ function upgradeHints() {
117
128
  }
118
129
 
119
130
  /**
120
- * Check the local Python environment and warn about degraded WIZZ features.
131
+ * One line saying exactly what a degraded Python means, per detection case.
132
+ * @param {'full'|'partial'|'unsupported'|'none'} status
133
+ * @param {Object|null} detected
134
+ * @returns {string}
135
+ */
136
+ function degradedMessage(status, detected) {
137
+ if (detected && !detected.isRuntimeCommand) {
138
+ return (
139
+ `Python ${detected.version.raw} found via \`${detected.command}\`, but the skill scripts invoke \`python3\`, which is not on PATH.\n` +
140
+ `The Wizz core is unaffected (Node only). These skills stay unavailable until \`python3\` resolves: ${PYTHON_ONLY_SKILLS}.`
141
+ );
142
+ }
143
+ if (status === 'partial') {
144
+ return (
145
+ `Python ${detected.version.raw} detected (${detected.command}) — the skill scripts that read TOML need Python 3.11+ (stdlib tomllib).\n` +
146
+ `The Wizz core is unaffected (Node only). Partially available: ${PYTHON_ONLY_SKILLS}.`
147
+ );
148
+ }
149
+ const found =
150
+ status === 'unsupported' ? `Python ${detected.version.raw} detected (${detected.command}) — too old.` : 'No Python found on PATH.';
151
+ return (
152
+ `${found} That is fine: Node is the only prerequisite of the Wizz core.\n` +
153
+ `Only these skills need Python 3.11+ and stay unavailable until it is: ${PYTHON_ONLY_SKILLS}.`
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Check the local Python environment and report which skills it costs.
159
+ *
160
+ * Informational only: since item 29 nothing in the core needs Python, so this
161
+ * never blocks, never prompts and never asks for an acknowledgement — it logs
162
+ * one line and the install moves on. The installer runs in the destination
163
+ * environment, so probing PATH here tests the right machine.
121
164
  *
122
- * Warn-don't-block: most of WIZZ works without Python, so the install always
123
- * may proceed — but the user must explicitly acknowledge the warning so it
124
- * can't scroll past unseen. In non-interactive runs (--yes, or stdin is not
125
- * a TTY) the warning is logged and the install continues without a prompt.
165
+ * `options.nonInteractive` is kept for call-site compatibility and is not read.
126
166
  *
127
167
  * @param {Object} [options]
128
- * @param {boolean} [options.nonInteractive=false] - Skip the ack prompt (--yes, or no TTY)
129
168
  * @returns {Promise<{status: string, detected: Object|null}>}
130
169
  */
131
- async function checkPythonEnvironment({ nonInteractive = false } = {}) {
170
+ async function checkPythonEnvironment() {
132
171
  // Called via module.exports so tests can stub detection.
133
172
  const detected = module.exports.detectPython();
134
173
  const status = classifyPython(detected ? detected.version : null);
135
174
 
136
175
  if (status === 'full' && detected.isRuntimeCommand) {
137
- await prompts.log.success(`Python ${detected.version.raw} detected (${detected.command}) — all Wizz features supported.`);
138
- return { status, detected };
139
- }
140
-
141
- if (detected && !detected.isRuntimeCommand) {
142
- await prompts.log.warn(
143
- `Python ${detected.version.raw} found via \`${detected.command}\`, but Wizz scripts invoke \`python3\`, which is not on PATH.\n` +
144
- `Python-powered features (memlog session memory, TOML config resolution) won't run until \`python3\` resolves —\n` +
145
- `add a python3 alias/shim, or reinstall Python with the python3 launcher enabled.`,
146
- );
147
- } else if (status === 'partial') {
148
- await prompts.log.warn(
149
- `Python ${detected.version.raw} detected (${detected.command}) — Wizz's TOML config tools need Python 3.11+ (stdlib tomllib).\n` +
150
- `Works: memlog session memory. Won't work: config/customization resolution scripts.`,
151
- );
152
- } else {
153
- const found =
154
- status === 'unsupported' ? `Python ${detected.version.raw} detected (${detected.command}) — too old.` : 'No Python found on PATH.';
155
- await prompts.log.warn(
156
- `${found} Wizz installs fine without it, but Python-powered features\n` +
157
- `(memlog session memory, TOML config resolution) won't run until Python 3.11+ is available.`,
176
+ await prompts.log.success(
177
+ `Python ${detected.version.raw} detected (${detected.command}) — the optional Python-backed skills are available too.`,
158
178
  );
159
- }
160
- await prompts.note(upgradeHints(), 'Python 3.11+ recommended');
161
-
162
- if (nonInteractive) {
163
- await prompts.log.info('Continuing anyway (non-interactive run). You can fix Python later — no reinstall needed.');
164
179
  return { status, detected };
165
180
  }
166
181
 
167
- const choice = await prompts.select({
168
- message: "Wizz's Python-powered features won't work yet. How do you want to proceed?",
169
- choices: [
170
- {
171
- name: 'Continue install',
172
- value: 'continue',
173
- hint: 'Wizz works without Python — you can fix Python later, no reinstall needed',
174
- },
175
- {
176
- name: 'Quit and fix Python first',
177
- value: 'quit',
178
- hint: 'make Python 3.11+ available as python3, then re-run the installer',
179
- },
180
- ],
181
- default: 'continue',
182
- });
183
-
184
- if (choice === 'quit') {
185
- await prompts.cancel('Make Python 3.11+ available as `python3` (see hints above), then re-run the installer.');
186
- process.exit(0);
187
- }
188
-
182
+ await prompts.log.info(degradedMessage(status, detected));
183
+ await prompts.note(upgradeHints(), 'Python 3.11+ optional');
189
184
  return { status, detected };
190
185
  }
191
186
 
192
187
  module.exports = {
193
188
  checkPythonEnvironment,
189
+ degradedMessage,
194
190
  detectPython,
195
191
  parsePythonVersion,
196
192
  classifyPython,
197
193
  PYTHON_FULL_SUPPORT,
198
194
  PYTHON_PARTIAL_SUPPORT,
195
+ PYTHON_ONLY_SKILLS,
199
196
  };
@@ -14,6 +14,80 @@ const { OfficialModules } = require('../modules/official-modules');
14
14
  const { ExistingInstall } = require('./existing-install');
15
15
  const { getProjectRoot } = require('../project-root');
16
16
  const prompts = require('../prompts');
17
+ // Item 10 (review da fase 3, 2026-09-11): rebuild TAG@SHA pins from a
18
+ // manifest entry's version + shaRequested. pin-spec.js is a leaf module
19
+ // (no requires of its own), safe to pull in at top level here.
20
+ const { formatPinValue } = require('../modules/pin-spec');
21
+
22
+ /**
23
+ * Build channelOptions (pins/nextSet) from the existing manifest's recorded
24
+ * per-module channel, so the config collector and clone steps that follow
25
+ * don't silently redecide a module's channel.
26
+ *
27
+ * Item 10 (review da fase 3, 2026-09-11): rebuilds `TAG@SHA`, not just
28
+ * `TAG`, whenever the manifest entry carries `shaRequested` — the SHA half
29
+ * of the `--pin CODE=TAG@SHA` that produced it (manifest-generator.js).
30
+ * Dropping that half here silently downgraded the pin to tag-only on every
31
+ * quick-update, so a tag force-moved on the remote between installs would
32
+ * pass unnoticed on the single most common reinstall path.
33
+ *
34
+ * Extracted from `quickUpdate` as its own function (rather than inlined) so
35
+ * it can be exercised directly in tests without mocking the whole
36
+ * `Installer` — see test/test-pin-sha.js.
37
+ *
38
+ * @param {Object|null} manifestData - `installer.manifest.read(wizzDir)` result
39
+ * @param {Object} [deps] - Injectable seams for tests
40
+ * @param {Function} [deps.warn] - Defaults to `prompts.log.warn`
41
+ * @param {{fetchStableTags: Function, classifyUpgrade: Function, parseGitHubRepo: Function}} [deps.channelResolver]
42
+ * Defaults to requiring `../modules/channel-resolver` (real GitHub tag lookups).
43
+ * @returns {Promise<{global: null, nextSet: Set<string>, pins: Map<string,string>, warnings: string[]}>}
44
+ */
45
+ async function buildChannelOptionsFromManifest(manifestData, deps = {}) {
46
+ const warn = deps.warn || prompts.log.warn;
47
+ const channelOptions = { global: null, nextSet: new Set(), pins: new Map(), warnings: [] };
48
+ if (!manifestData?.modulesDetailed) return channelOptions;
49
+
50
+ const { fetchStableTags, classifyUpgrade, parseGitHubRepo } = deps.channelResolver || require('../modules/channel-resolver');
51
+ for (const entry of manifestData.modulesDetailed) {
52
+ if (!entry?.name || !entry?.channel) continue;
53
+ if (entry.channel === 'pinned' && entry.version) {
54
+ channelOptions.pins.set(entry.name, formatPinValue(entry.version, entry.shaRequested));
55
+ continue;
56
+ }
57
+ if (entry.channel === 'next') {
58
+ channelOptions.nextSet.add(entry.name);
59
+ continue;
60
+ }
61
+ // Stable: classify the available upgrade. Patches and minors fall
62
+ // through (stable default picks up the top tag). A major upgrade
63
+ // requires opt-in, so under quick-update's non-interactive semantics
64
+ // we pin to the current version to prevent a silent breaking jump.
65
+ if (entry.channel === 'stable' && entry.version && entry.repoUrl) {
66
+ const parsed = parseGitHubRepo(entry.repoUrl);
67
+ if (!parsed) continue;
68
+ try {
69
+ const tags = await fetchStableTags(parsed.owner, parsed.repo);
70
+ if (tags.length === 0) continue;
71
+ const topTag = tags[0].tag;
72
+ const cls = classifyUpgrade(entry.version, topTag);
73
+ if (cls === 'major') {
74
+ channelOptions.pins.set(entry.name, formatPinValue(entry.version, entry.shaRequested));
75
+ await warn(
76
+ `${entry.name} ${entry.version} → ${topTag} is a new major release; staying on ${entry.version}. ` +
77
+ `Run \`wizz install\` (Modify) with \`--pin ${entry.name}=${topTag}\` to accept.`,
78
+ );
79
+ }
80
+ } catch (error) {
81
+ // Tag lookup failed (offline, rate-limited). Stay on the current
82
+ // version rather than guessing — the existing cache is already
83
+ // at that ref, so re-using it keeps the install stable.
84
+ channelOptions.pins.set(entry.name, formatPinValue(entry.version, entry.shaRequested));
85
+ await warn(`Could not check ${entry.name} for updates (${error.message}); staying on ${entry.version}.`);
86
+ }
87
+ }
88
+ }
89
+ return channelOptions;
90
+ }
17
91
 
18
92
  /**
19
93
  * Quick update: preserves all settings and only prompts for new config fields.
@@ -97,48 +171,7 @@ async function quickUpdate(installer, config) {
97
171
  // 'pinned' would trigger a stable-channel tag lookup at config-collection
98
172
  // time, burning GitHub API quota and potentially failing.
99
173
  const manifestData = await installer.manifest.read(wizzDir);
100
- const channelOptions = { global: null, nextSet: new Set(), pins: new Map(), warnings: [] };
101
- if (manifestData?.modulesDetailed) {
102
- const { fetchStableTags, classifyUpgrade, parseGitHubRepo } = require('../modules/channel-resolver');
103
- for (const entry of manifestData.modulesDetailed) {
104
- if (!entry?.name || !entry?.channel) continue;
105
- if (entry.channel === 'pinned' && entry.version) {
106
- channelOptions.pins.set(entry.name, entry.version);
107
- continue;
108
- }
109
- if (entry.channel === 'next') {
110
- channelOptions.nextSet.add(entry.name);
111
- continue;
112
- }
113
- // Stable: classify the available upgrade. Patches and minors fall
114
- // through (stable default picks up the top tag). A major upgrade
115
- // requires opt-in, so under quick-update's non-interactive semantics
116
- // we pin to the current version to prevent a silent breaking jump.
117
- if (entry.channel === 'stable' && entry.version && entry.repoUrl) {
118
- const parsed = parseGitHubRepo(entry.repoUrl);
119
- if (!parsed) continue;
120
- try {
121
- const tags = await fetchStableTags(parsed.owner, parsed.repo);
122
- if (tags.length === 0) continue;
123
- const topTag = tags[0].tag;
124
- const cls = classifyUpgrade(entry.version, topTag);
125
- if (cls === 'major') {
126
- channelOptions.pins.set(entry.name, entry.version);
127
- await prompts.log.warn(
128
- `${entry.name} ${entry.version} → ${topTag} is a new major release; staying on ${entry.version}. ` +
129
- `Run \`wizz install\` (Modify) with \`--pin ${entry.name}=${topTag}\` to accept.`,
130
- );
131
- }
132
- } catch (error) {
133
- // Tag lookup failed (offline, rate-limited). Stay on the current
134
- // version rather than guessing — the existing cache is already
135
- // at that ref, so re-using it keeps the install stable.
136
- channelOptions.pins.set(entry.name, entry.version);
137
- await prompts.log.warn(`Could not check ${entry.name} for updates (${error.message}); staying on ${entry.version}.`);
138
- }
139
- }
140
- }
141
- }
174
+ const channelOptions = await buildChannelOptionsFromManifest(manifestData);
142
175
 
143
176
  // Load existing configs and collect new fields (if any)
144
177
  await prompts.log.info('Checking for new configuration options...');
@@ -206,4 +239,4 @@ async function quickUpdate(installer, config) {
206
239
  };
207
240
  }
208
241
 
209
- module.exports = { quickUpdate };
242
+ module.exports = { quickUpdate, buildChannelOptionsFromManifest };
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Resolved central config generator (audit item 29, 2026-09-12).
3
+ *
4
+ * Sibling of `resolved-customizations.js`. Where that one snapshots each
5
+ * skill's `customize.toml`, this one snapshots Wizz's CENTRAL config — the
6
+ * four-layer merge that `src/scripts/resolve_config.py` performs at runtime:
7
+ *
8
+ * 1. `_wizz/config.toml` (installer-owned team)
9
+ * 2. `_wizz/config.user.toml` (installer-owned user)
10
+ * 3. `_wizz/custom/config.toml` (human-authored team, committed)
11
+ * 4. `_wizz/custom/config.user.toml` (human-authored user, gitignored)
12
+ *
13
+ * The merged result is written to `_wizz/_resolved/_config.json`, so a skill
14
+ * that needs `core.user_name`, `core.communication_language` or
15
+ * `core.output_folder` reads ONE json file instead of spawning `python3`.
16
+ * That is what makes Node the only prerequisite for activating an agent:
17
+ * Python stays only for the BMAD skills that ship their own Python scripts.
18
+ *
19
+ * Merge rules are not re-implemented here — `deepMerge` from
20
+ * `resolved-customizations.js` is already the 1:1 port of the Python rules
21
+ * (scalars override, tables deep-merge, arrays of tables keyed by `code`/`id`
22
+ * merge by that key, every other array appends). One motor, one behaviour.
23
+ *
24
+ * Layer sourcing mirrors `resolveAll`: the two base layers come from the
25
+ * freshly written tree (`wizzDir`, possibly the A14 tmp scaffold), the two
26
+ * `custom/` layers from the REAL project root, so a fresh install still sees
27
+ * the user's overrides.
28
+ *
29
+ * Fail-open, like every other generator here: a missing or broken base
30
+ * `config.toml` means NO snapshot (a stale one is removed) and a warning —
31
+ * never a thrown install. Callers that find no `_config.json` fall back to
32
+ * `resolve_config.py` (where Python exists) or to neutral defaults.
33
+ */
34
+
35
+ const path = require('node:path');
36
+ const nodeFs = require('node:fs');
37
+ const fs = require('../fs-native');
38
+ const { WIZZ_FOLDER_NAME } = require('../ide/shared/path-utils');
39
+ const { deepMerge, loadTomlLayer, computeInputsIndex } = require('./resolved-customizations');
40
+
41
+ // Name of the central-config snapshot inside `_resolved/`. Leading underscore
42
+ // keeps it out of the `<skill-name>.json` namespace (skills are `wizz-*`).
43
+ const CONFIG_SNAPSHOT = '_config.json';
44
+
45
+ const BASE_TEAM = 'config.toml';
46
+ const BASE_USER = 'config.user.toml';
47
+
48
+ /**
49
+ * Absolute paths of the four config layers, in merge order (lowest first).
50
+ * @param {{projectRoot: string, wizzDir: string}} args
51
+ * @returns {string[]}
52
+ */
53
+ function configLayerPaths({ projectRoot, wizzDir }) {
54
+ const customDir = path.join(projectRoot, WIZZ_FOLDER_NAME, 'custom');
55
+ return [path.join(wizzDir, BASE_TEAM), path.join(wizzDir, BASE_USER), path.join(customDir, BASE_TEAM), path.join(customDir, BASE_USER)];
56
+ }
57
+
58
+ /**
59
+ * Merge the four layers into one object.
60
+ *
61
+ * The base layer (`_wizz/config.toml`) is load-bearing: without it there is
62
+ * no `core` table at all, and a snapshot written from `{}` would look valid
63
+ * while telling every skill the user has no name and no language. So a
64
+ * missing/broken base is reported as `{ config: null }`, not as `{}`.
65
+ *
66
+ * `absent` separates the two reasons, because they deserve different noise:
67
+ * a base that does not exist YET is the normal state during a fresh install
68
+ * (the resolver runs before `writeCentralConfig`) and must stay silent; a
69
+ * base that exists and does not parse is a real problem and warns.
70
+ *
71
+ * @param {{projectRoot: string, wizzDir: string}} args
72
+ * @returns {{config: object|null, absent: boolean, warnings: string[], inputs: string[]}}
73
+ */
74
+ function resolveCentralConfig({ projectRoot, wizzDir }) {
75
+ const warnings = [];
76
+ const layers = configLayerPaths({ projectRoot, wizzDir });
77
+ const [baseTeamPath] = layers;
78
+
79
+ if (!nodeFs.existsSync(baseTeamPath)) {
80
+ return { config: null, absent: true, warnings, inputs: [] };
81
+ }
82
+
83
+ const baseTeam = loadTomlLayer(baseTeamPath, warnings);
84
+ if (baseTeam === null) {
85
+ warnings.push(`base config not readable (${baseTeamPath}): no _config.json written`);
86
+ return { config: null, absent: false, warnings, inputs: [] };
87
+ }
88
+
89
+ let merged = baseTeam;
90
+ for (const layerPath of layers.slice(1)) {
91
+ const layer = loadTomlLayer(layerPath, warnings);
92
+ if (layer !== null) merged = deepMerge(merged, layer);
93
+ }
94
+ return { config: merged, absent: false, warnings, inputs: layers.filter((p) => nodeFs.existsSync(p)) };
95
+ }
96
+
97
+ /**
98
+ * Write `_resolved/_config.json`, or remove a stale one when the central
99
+ * config cannot be resolved.
100
+ *
101
+ * @param {object} args
102
+ * @param {string} args.projectRoot
103
+ * @param {string} args.wizzDir
104
+ * @param {string} args.resolvedDir - `{wizzDir}/_resolved`, already created
105
+ * @returns {Promise<{written: boolean, absent: boolean, warnings: string[], entry: object|null}>}
106
+ */
107
+ async function writeResolvedConfig({ projectRoot, wizzDir, resolvedDir }) {
108
+ const snapshotPath = path.join(resolvedDir, CONFIG_SNAPSHOT);
109
+ const { config, absent, warnings, inputs } = resolveCentralConfig({ projectRoot, wizzDir });
110
+
111
+ if (config === null) {
112
+ await fs.remove(snapshotPath);
113
+ return { written: false, absent, warnings, entry: null };
114
+ }
115
+
116
+ await fs.writeFile(snapshotPath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
117
+ return {
118
+ written: true,
119
+ absent: false,
120
+ warnings,
121
+ entry: { inputs: computeInputsIndex({ projectRoot, wizzDir, inputPaths: inputs }) },
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Second pass for the installer: write `_config.json` AFTER the central
127
+ * config exists, patching only the `config` key of an `.index.json` that is
128
+ * already on disk.
129
+ *
130
+ * Why a second pass at all: on a fresh install the snapshot generator runs
131
+ * inside the tmp scaffold, before `writeCentralConfig` (and before
132
+ * `--set` overrides) have produced `_wizz/config.toml`. Resolving the
133
+ * central config there would snapshot a file that does not exist yet. So
134
+ * the installer calls this once more on the REAL `_wizz/`, after the swap
135
+ * and after the overrides, and only the central-config half is redone —
136
+ * the skill snapshots are already correct and are not touched.
137
+ *
138
+ * Fail-open like everything else here: any failure returns `written: false`
139
+ * with the reason, and the caller degrades to `wizz resolve` later.
140
+ *
141
+ * @param {object} args
142
+ * @param {string} args.projectRoot
143
+ * @param {string} args.wizzDir - the REAL `_wizz/`, post-swap
144
+ * @returns {Promise<{written: boolean, absent: boolean, warnings: string[]}>}
145
+ */
146
+ async function refreshResolvedConfig({ projectRoot, wizzDir }) {
147
+ const resolvedDir = path.join(wizzDir, '_resolved');
148
+ await fs.mkdir(resolvedDir, { recursive: true });
149
+ const result = await writeResolvedConfig({ projectRoot, wizzDir, resolvedDir });
150
+
151
+ // Patch the index in place: `skills` was written by the first pass and is
152
+ // still valid, so re-deriving it here would be wasted work and a chance to
153
+ // get it wrong.
154
+ const { readIndex, writeResolvedIndex } = require('./resolved-customizations');
155
+ const index = readIndex(resolvedDir);
156
+ if (index && index.skills) {
157
+ await writeResolvedIndex(resolvedDir, index.skills, result.entry);
158
+ }
159
+ return { written: result.written, absent: result.absent, warnings: result.warnings };
160
+ }
161
+
162
+ module.exports = {
163
+ resolveCentralConfig,
164
+ writeResolvedConfig,
165
+ refreshResolvedConfig,
166
+ configLayerPaths,
167
+ CONFIG_SNAPSHOT,
168
+ };