wizz-method 1.4.1 → 1.5.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 (574) hide show
  1. package/README.md +6 -4
  2. package/package.json +39 -3
  3. package/skills-registry.yaml +102 -7
  4. package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +2 -0
  5. package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +5 -2
  6. package/src/core-skills/_shared/handoff-protocol.md +61 -0
  7. package/src/core-skills/wizz-customize/SKILL.md +2 -2
  8. package/src/core-skills/wizz-party-mode/references/mode-agent-team.md +1 -1
  9. package/src/core-skills/wizz-party-mode/references/mode-subagent.md +1 -1
  10. package/src/core-skills/wizz-review-adversarial-general/SKILL.md +2 -0
  11. package/src/modules/wizz/README.md +3 -3
  12. package/src/modules/wizz/_shared/communication-rules.md +13 -0
  13. package/src/modules/wizz/_shared/encerramento.md +2 -2
  14. package/src/modules/wizz/agents/wizz-ads/customize.toml +5 -3
  15. package/src/modules/wizz/agents/wizz-copy/customize.toml +5 -3
  16. package/src/modules/wizz/agents/wizz-designer/SKILL.md +2 -2
  17. package/src/modules/wizz/agents/wizz-designer/customize.toml +6 -4
  18. package/src/modules/wizz/agents/wizz-growth/customize.toml +11 -3
  19. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +9 -5
  20. package/src/modules/wizz/agents/wizz-maestro/customize.toml +5 -7
  21. package/src/modules/wizz/agents/wizz-memoria/customize.toml +2 -4
  22. package/src/modules/wizz/agents/wizz-qa/SKILL.md +5 -5
  23. package/src/modules/wizz/agents/wizz-qa/customize.toml +9 -7
  24. package/src/modules/wizz/agents/wizz-seo/customize.toml +5 -3
  25. package/src/modules/wizz/agents/wizz-social/customize.toml +9 -8
  26. package/src/modules/wizz/module.yaml +1 -1
  27. package/src/modules/wizz/overrides/wizz-agent-analyst.toml +4 -5
  28. package/src/modules/wizz/overrides/wizz-agent-architect.toml +11 -3
  29. package/src/modules/wizz/overrides/wizz-agent-dev.toml +6 -4
  30. package/src/modules/wizz/overrides/wizz-agent-pm.toml +4 -2
  31. package/src/modules/wizz/overrides/wizz-agent-tech-writer.toml +3 -5
  32. package/src/modules/wizz/overrides/wizz-agent-ux-designer.toml +4 -2
  33. package/src/modules/wizz/scripts/wizz-init.mjs +3 -1
  34. package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
  35. package/src/scripts/memlog.py +9 -0
  36. package/src/scripts/resolve_customization.py +93 -3
  37. package/src/skills-lib/adversarial-reviewer/SKILL.md +2 -0
  38. package/src/skills-lib/algorithmic-art/SKILL.md +29 -369
  39. package/src/skills-lib/algorithmic-art/references/interactive-artifact.md +139 -0
  40. package/src/skills-lib/algorithmic-art/references/p5js-implementation.md +121 -0
  41. package/src/skills-lib/algorithmic-art/references/philosophy-creation.md +88 -0
  42. package/src/skills-lib/canvas-design/SKILL.md +9 -38
  43. package/src/skills-lib/canvas-design/references/canvas-execution.md +21 -0
  44. package/src/skills-lib/canvas-design/references/philosophy-examples.md +25 -0
  45. package/src/skills-lib/cerebro/SKILL.md +8 -273
  46. package/src/skills-lib/cerebro/references/comandos-pesquisa-e-sync.md +44 -0
  47. package/src/skills-lib/cerebro/references/comandos-rotina-e-captura.md +94 -0
  48. package/src/skills-lib/cerebro/references/comandos-sessao.md +122 -0
  49. package/src/skills-lib/content-strategy/SKILL.md +12 -316
  50. package/src/skills-lib/content-strategy/references/content-types.md +52 -0
  51. package/src/skills-lib/content-strategy/references/ideation-sources.md +78 -0
  52. package/src/skills-lib/content-strategy/references/pillars-and-keywords.md +78 -0
  53. package/src/skills-lib/content-strategy/references/prioritization-and-output.md +57 -0
  54. package/src/skills-lib/decision-maker/SKILL.md +13 -466
  55. package/src/skills-lib/decision-maker/references/commands-and-edge-cases.md +40 -0
  56. package/src/skills-lib/decision-maker/references/output-format.md +161 -0
  57. package/src/skills-lib/decision-maker/references/references-and-extraction.md +129 -0
  58. package/src/skills-lib/decision-maker/references/six-decisions.md +122 -0
  59. package/src/skills-lib/decision-maker/references/wizz-method-handoff.md +22 -0
  60. package/src/skills-lib/find-skills/SKILL.md +9 -6
  61. package/src/skills-lib/form-cro/SKILL.md +12 -390
  62. package/src/skills-lib/form-cro/references/experiments.md +73 -0
  63. package/src/skills-lib/form-cro/references/field-optimization.md +92 -0
  64. package/src/skills-lib/form-cro/references/flow-and-errors.md +71 -0
  65. package/src/skills-lib/form-cro/references/measurement-output.md +41 -0
  66. package/src/skills-lib/form-cro/references/trust-and-form-types.md +54 -0
  67. package/src/skills-lib/graphify/SKILL.md +2 -20
  68. package/src/skills-lib/huashu-design/SKILL.md +42 -596
  69. package/src/skills-lib/huashu-design/references/anti-slop-principles.md +62 -0
  70. package/src/skills-lib/huashu-design/references/audio-design-rules.md +1 -1
  71. package/src/skills-lib/huashu-design/references/cross-agent-adaptation.md +14 -0
  72. package/src/skills-lib/huashu-design/references/design-direction-fallback.md +113 -0
  73. package/src/skills-lib/huashu-design/references/exception-handling.md +18 -0
  74. package/src/skills-lib/huashu-design/references/fact-verification.md +36 -0
  75. package/src/skills-lib/huashu-design/references/ios-prototype.md +146 -0
  76. package/src/skills-lib/huashu-design/references/sfx-library.md +7 -0
  77. package/src/skills-lib/huashu-design/references/starter-components.md +24 -0
  78. package/src/skills-lib/huashu-design/references/video-export.md +1 -1
  79. package/src/skills-lib/huashu-design/references/watermark-policy.md +23 -0
  80. package/src/skills-lib/huashu-design/scripts/add-music.sh +12 -1
  81. package/src/skills-lib/huashu-design/scripts/mix-voiceover.sh +11 -0
  82. package/src/skills-lib/humanizer/SKILL.md +23 -431
  83. package/src/skills-lib/humanizer/references/ai-writing-patterns.md +314 -0
  84. package/src/skills-lib/humanizer/references/full-example.md +72 -0
  85. package/src/skills-lib/humanizer/references/personality-and-soul.md +36 -0
  86. package/src/skills-lib/impeccable/SKILL.md +37 -153
  87. package/src/skills-lib/impeccable/references/design-rules.md +95 -0
  88. package/src/skills-lib/impeccable/references/pin-unpin-and-hooks.md +17 -0
  89. package/src/skills-lib/impeccable/references/routing-rules.md +28 -0
  90. package/src/skills-lib/implementation-planner/SKILL.md +22 -529
  91. package/src/skills-lib/implementation-planner/references/folder-structure-and-dependencies.md +132 -0
  92. package/src/skills-lib/implementation-planner/references/handoff-template.md +34 -0
  93. package/src/skills-lib/implementation-planner/references/implementation-phases.md +54 -0
  94. package/src/skills-lib/implementation-planner/references/implementation-types.md +28 -0
  95. package/src/skills-lib/implementation-planner/references/motion-path-implementation.md +57 -0
  96. package/src/skills-lib/implementation-planner/references/output-templates.md +92 -0
  97. package/src/skills-lib/implementation-planner/references/performance-plan.md +52 -0
  98. package/src/skills-lib/inicio-de-projeto/SKILL.md +4 -224
  99. package/src/skills-lib/inicio-de-projeto/references/implementacao-api-forms.md +112 -0
  100. package/src/skills-lib/inicio-de-projeto/references/padroes-arquiteturais-e-pastas.md +71 -0
  101. package/src/skills-lib/inicio-de-projeto/references/seguranca-e-regras-codigo.md +26 -0
  102. package/src/skills-lib/launch-strategy/SKILL.md +9 -311
  103. package/src/skills-lib/launch-strategy/references/five-phase-approach.md +76 -0
  104. package/src/skills-lib/launch-strategy/references/launch-checklist.md +34 -0
  105. package/src/skills-lib/launch-strategy/references/orb-framework.md +76 -0
  106. package/src/skills-lib/launch-strategy/references/post-launch-and-ongoing.md +64 -0
  107. package/src/skills-lib/launch-strategy/references/product-hunt-strategy.md +52 -0
  108. package/src/skills-lib/marketing-psychology/SKILL.md +18 -401
  109. package/src/skills-lib/marketing-psychology/references/design-and-delivery-models.md +56 -0
  110. package/src/skills-lib/marketing-psychology/references/foundational-thinking-models.md +76 -0
  111. package/src/skills-lib/marketing-psychology/references/growth-and-scaling-models.md +46 -0
  112. package/src/skills-lib/marketing-psychology/references/influencing-behavior.md +71 -0
  113. package/src/skills-lib/marketing-psychology/references/pricing-psychology.md +31 -0
  114. package/src/skills-lib/marketing-psychology/references/understanding-buyers.md +116 -0
  115. package/src/skills-lib/motion-3d-director/SKILL.md +23 -370
  116. package/src/skills-lib/motion-3d-director/references/image-to-video-workflow.md +166 -0
  117. package/src/skills-lib/motion-3d-director/references/motion-paths.md +119 -0
  118. package/src/skills-lib/motion-3d-director/references/performance-and-handoff.md +51 -0
  119. package/src/skills-lib/popup-cro/SKILL.md +11 -414
  120. package/src/skills-lib/popup-cro/references/design-copy-compliance.md +81 -0
  121. package/src/skills-lib/popup-cro/references/experiments.md +82 -0
  122. package/src/skills-lib/popup-cro/references/rules-measurement-output.md +87 -0
  123. package/src/skills-lib/popup-cro/references/triggers-and-popup-types.md +108 -0
  124. package/src/skills-lib/signup-flow-cro/SKILL.md +13 -321
  125. package/src/skills-lib/signup-flow-cro/references/experiments.md +72 -0
  126. package/src/skills-lib/signup-flow-cro/references/field-optimization.md +49 -0
  127. package/src/skills-lib/signup-flow-cro/references/flow-structure.md +38 -0
  128. package/src/skills-lib/signup-flow-cro/references/measurement-output-patterns.md +62 -0
  129. package/src/skills-lib/signup-flow-cro/references/trust-and-post-submit.md +40 -0
  130. package/src/skills-lib/taste-redesign/SKILL.md +10 -146
  131. package/src/skills-lib/taste-redesign/references/design-audit.md +120 -0
  132. package/src/skills-lib/taste-redesign/references/upgrade-techniques.md +31 -0
  133. package/src/skills-lib/ui-ux-pro-max/SKILL.md +9 -353
  134. package/src/skills-lib/ui-ux-pro-max/references/common-rules-checklist.md +76 -0
  135. package/src/skills-lib/ui-ux-pro-max/references/rules-quick-reference.md +70 -0
  136. package/src/skills-lib/ui-ux-pro-max/references/search-reference.md +45 -0
  137. package/src/skills-lib/ui-ux-pro-max/references/workflow-guide.md +157 -0
  138. package/src/skills-lib/web-security/SKILL.md +13 -187
  139. package/src/skills-lib/web-security/references/checklist-deploy.md +52 -0
  140. package/src/skills-lib/web-security/references/headers-rate-limit-cors.md +78 -0
  141. package/src/skills-lib/web-security/references/owasp-top5-detalhado.md +51 -0
  142. package/src/skills-lib/wizz-router/SKILL.md +20 -9
  143. package/src/skills-lib/wizz-router/references/auditoria-360.md +4 -4
  144. package/src/skills-lib/wizz-router/references/routing-table-flat.md +15 -15
  145. package/tools/fetch-assets.mjs +277 -0
  146. package/tools/installer/commands/fetch-assets.js +71 -0
  147. package/tools/installer/commands/install.js +35 -3
  148. package/tools/installer/commands/trace-report.js +131 -0
  149. package/tools/installer/core/config.js +9 -0
  150. package/tools/installer/core/help-catalog.js +181 -0
  151. package/tools/installer/core/install-paths.js +48 -3
  152. package/tools/installer/core/installer.js +253 -618
  153. package/tools/installer/core/manifest-generator.js +8 -1
  154. package/tools/installer/core/manifest.js +67 -22
  155. package/tools/installer/core/module-config-writer.js +110 -0
  156. package/tools/installer/core/preflight-tools-check.js +82 -0
  157. package/tools/installer/core/quick-update.js +209 -0
  158. package/tools/installer/core/user-file-preservation.js +250 -0
  159. package/tools/installer/modules/channel-resolver.js +34 -2
  160. package/tools/installer/modules/cli-config.js +127 -34
  161. package/tools/installer/modules/deps-cache.js +28 -1
  162. package/tools/installer/modules/env-vars.js +446 -0
  163. package/tools/installer/modules/external-manager.js +40 -6
  164. package/tools/installer/modules/mcp-config.js +180 -37
  165. package/tools/installer/modules/module-help-schema.js +2 -1
  166. package/tools/installer/modules/official-modules.js +1 -1
  167. package/tools/installer/modules/registry-resolve.js +74 -0
  168. package/tools/installer/modules/skills-lib.js +11 -15
  169. package/tools/installer/ui.js +71 -5
  170. package/wizz-modules.yaml +15 -5
  171. package/.claude-plugin/marketplace.json +0 -80
  172. package/AGENTS.md +0 -12
  173. package/build/artifacts/llms-full.txt +0 -4049
  174. package/build/artifacts/llms.txt +0 -28
  175. package/build/site/404.html +0 -55
  176. package/build/site/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.hwBsy0Mo.js +0 -1
  177. package/build/site/_astro/Search.astro_astro_type_script_index_0_lang.cjYDvRdi.js +0 -1
  178. package/build/site/_astro/TableOfContents.astro_astro_type_script_index_0_lang.FuRcXuRY.js +0 -1
  179. package/build/site/_astro/ec.0vx5m.js +0 -3
  180. package/build/site/_astro/ec.v4551.css +0 -1
  181. package/build/site/_astro/index.DdjI3lF_.css +0 -1
  182. package/build/site/_astro/page.B1D-nYk3.js +0 -1
  183. package/build/site/_astro/print.DNXP8c50.css +0 -1
  184. package/build/site/_astro/ui-core.D_Lfcn_I.js +0 -2
  185. package/build/site/diagrams/checkpoint-preview-diagram-fr.webp +0 -0
  186. package/build/site/diagrams/checkpoint-preview-diagram.png +0 -0
  187. package/build/site/diagrams/quick-dev-diagram-fr.webp +0 -0
  188. package/build/site/diagrams/quick-dev-diagram.png +0 -0
  189. package/build/site/explanation/advanced-elicitation/index.html +0 -105
  190. package/build/site/explanation/adversarial-review/index.html +0 -109
  191. package/build/site/explanation/analysis-phase/index.html +0 -129
  192. package/build/site/explanation/brainstorming/index.html +0 -92
  193. package/build/site/explanation/checkpoint-preview/index.html +0 -121
  194. package/build/site/explanation/established-projects-faq/index.html +0 -107
  195. package/build/site/explanation/forensic-investigation/index.html +0 -167
  196. package/build/site/explanation/named-agents/index.html +0 -187
  197. package/build/site/explanation/party-mode/index.html +0 -256
  198. package/build/site/explanation/preventing-agent-conflicts/index.html +0 -185
  199. package/build/site/explanation/project-context/index.html +0 -198
  200. package/build/site/explanation/quick-dev/index.html +0 -113
  201. package/build/site/explanation/web-bundles/index.html +0 -178
  202. package/build/site/explanation/why-solutioning-matters/index.html +0 -168
  203. package/build/site/how-to/customize-wizz/index.html +0 -277
  204. package/build/site/how-to/established-projects/index.html +0 -165
  205. package/build/site/how-to/expand-wizz-for-your-org/index.html +0 -245
  206. package/build/site/how-to/get-answers-about-wizz/index.html +0 -129
  207. package/build/site/how-to/install-custom-modules/index.html +0 -197
  208. package/build/site/how-to/install-wizz/index.html +0 -330
  209. package/build/site/how-to/non-interactive-installation/index.html +0 -73
  210. package/build/site/how-to/project-context/index.html +0 -141
  211. package/build/site/how-to/quick-fixes/index.html +0 -122
  212. package/build/site/how-to/shard-large-documents/index.html +0 -103
  213. package/build/site/how-to/upgrade-to-v6/index.html +0 -185
  214. package/build/site/how-to/use-web-bundles/index.html +0 -99
  215. package/build/site/img/wizz-dark.png +0 -0
  216. package/build/site/img/wizz-light.png +0 -0
  217. package/build/site/index.html +0 -128
  218. package/build/site/llms-full.txt +0 -4049
  219. package/build/site/llms.txt +0 -28
  220. package/build/site/pagefind/fragment/en_175de32.pf_fragment +0 -0
  221. package/build/site/pagefind/fragment/en_24258fb.pf_fragment +0 -0
  222. package/build/site/pagefind/fragment/en_24f53a6.pf_fragment +0 -0
  223. package/build/site/pagefind/fragment/en_27ba8f9.pf_fragment +0 -0
  224. package/build/site/pagefind/fragment/en_296ddb9.pf_fragment +0 -0
  225. package/build/site/pagefind/fragment/en_2b5aa5c.pf_fragment +0 -0
  226. package/build/site/pagefind/fragment/en_49047e0.pf_fragment +0 -0
  227. package/build/site/pagefind/fragment/en_4a187d2.pf_fragment +0 -0
  228. package/build/site/pagefind/fragment/en_514725b.pf_fragment +0 -0
  229. package/build/site/pagefind/fragment/en_583316f.pf_fragment +0 -0
  230. package/build/site/pagefind/fragment/en_6465dfb.pf_fragment +0 -0
  231. package/build/site/pagefind/fragment/en_67825a4.pf_fragment +0 -0
  232. package/build/site/pagefind/fragment/en_82aec61.pf_fragment +0 -0
  233. package/build/site/pagefind/fragment/en_85dcebf.pf_fragment +0 -0
  234. package/build/site/pagefind/fragment/en_887ce56.pf_fragment +0 -0
  235. package/build/site/pagefind/fragment/en_89a1a7a.pf_fragment +0 -0
  236. package/build/site/pagefind/fragment/en_8ade291.pf_fragment +0 -0
  237. package/build/site/pagefind/fragment/en_8c11f8a.pf_fragment +0 -0
  238. package/build/site/pagefind/fragment/en_8c1bde3.pf_fragment +0 -0
  239. package/build/site/pagefind/fragment/en_92d7cc7.pf_fragment +0 -0
  240. package/build/site/pagefind/fragment/en_94bfc21.pf_fragment +0 -0
  241. package/build/site/pagefind/fragment/en_9b57d7d.pf_fragment +0 -0
  242. package/build/site/pagefind/fragment/en_ab62b53.pf_fragment +0 -0
  243. package/build/site/pagefind/fragment/en_b0d23ee.pf_fragment +0 -0
  244. package/build/site/pagefind/fragment/en_ba9fb37.pf_fragment +0 -0
  245. package/build/site/pagefind/fragment/en_bb113eb.pf_fragment +0 -0
  246. package/build/site/pagefind/fragment/en_bfc56b6.pf_fragment +0 -0
  247. package/build/site/pagefind/fragment/en_c6d9e48.pf_fragment +0 -0
  248. package/build/site/pagefind/fragment/en_cdd772f.pf_fragment +0 -0
  249. package/build/site/pagefind/fragment/en_cdd885c.pf_fragment +0 -0
  250. package/build/site/pagefind/fragment/en_cefed6a.pf_fragment +0 -0
  251. package/build/site/pagefind/fragment/en_d890205.pf_fragment +0 -0
  252. package/build/site/pagefind/fragment/en_da351b7.pf_fragment +0 -0
  253. package/build/site/pagefind/fragment/en_ea289f9.pf_fragment +0 -0
  254. package/build/site/pagefind/fragment/en_f1c3711.pf_fragment +0 -0
  255. package/build/site/pagefind/index/en_5b9d8c6.pf_index +0 -0
  256. package/build/site/pagefind/index/en_868614c.pf_index +0 -0
  257. package/build/site/pagefind/index/en_e4f9bff.pf_index +0 -0
  258. package/build/site/pagefind/pagefind-entry.json +0 -1
  259. package/build/site/pagefind/pagefind-highlight.js +0 -1064
  260. package/build/site/pagefind/pagefind-modular-ui.css +0 -214
  261. package/build/site/pagefind/pagefind-modular-ui.js +0 -8
  262. package/build/site/pagefind/pagefind-ui.css +0 -1
  263. package/build/site/pagefind/pagefind-ui.js +0 -2
  264. package/build/site/pagefind/pagefind.en_4e69d7f053.pf_meta +0 -0
  265. package/build/site/pagefind/pagefind.js +0 -6
  266. package/build/site/pagefind/wasm.en.pagefind +0 -0
  267. package/build/site/pagefind/wasm.unknown.pagefind +0 -0
  268. package/build/site/reference/agents/index.html +0 -168
  269. package/build/site/reference/commands/index.html +0 -254
  270. package/build/site/reference/core-tools/index.html +0 -384
  271. package/build/site/reference/modules/index.html +0 -132
  272. package/build/site/reference/testing/index.html +0 -218
  273. package/build/site/reference/workflow-map/index.html +0 -266
  274. package/build/site/roadmap/index.html +0 -77
  275. package/build/site/robots.txt +0 -37
  276. package/build/site/sitemap-0.xml +0 -1
  277. package/build/site/sitemap-index.xml +0 -1
  278. package/build/site/tutorials/getting-started/index.html +0 -386
  279. package/build/site/wizz-icon-orange.svg +0 -7
  280. package/build/site/workflow-map-diagram-fr.html +0 -375
  281. package/build/site/workflow-map-diagram.html +0 -375
  282. package/evals/bmm-skills/wizz-product-brief/evals.json +0 -237
  283. package/evals/bmm-skills/wizz-product-brief/files/branfield-memo.md +0 -46
  284. package/evals/bmm-skills/wizz-product-brief/files/forkbird-brief/addendum.md +0 -40
  285. package/evals/bmm-skills/wizz-product-brief/files/forkbird-brief/brief.md +0 -56
  286. package/evals/bmm-skills/wizz-product-brief/files/forkbird-brief/decision-log.md +0 -27
  287. package/evals/bmm-skills/wizz-product-brief/files/meridian-mobility-report.md +0 -116
  288. package/evals/bmm-skills/wizz-product-brief/files/mossridge-brief/addendum.md +0 -41
  289. package/evals/bmm-skills/wizz-product-brief/files/mossridge-brief/brief.md +0 -57
  290. package/evals/bmm-skills/wizz-product-brief/files/mossridge-brief/decision-log.md +0 -29
  291. package/evals/bmm-skills/wizz-product-brief/files/pantry-bridge-interviews.md +0 -90
  292. package/evals/bmm-skills/wizz-product-brief/files/q2-brainstorm.md +0 -101
  293. package/evals/bmm-skills/wizz-product-brief/triggers.json +0 -18
  294. package/evals/routing/dataset.json +0 -290
  295. package/evals/routing/run-routing-eval.mjs +0 -89
  296. package/src/skills-lib/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  297. package/src/skills-lib/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  298. package/src/skills-lib/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  299. package/src/skills-lib/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  300. package/src/skills-lib/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  301. package/src/skills-lib/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  302. package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  303. package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  304. package/src/skills-lib/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  305. package/src/skills-lib/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  306. package/src/skills-lib/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  307. package/src/skills-lib/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  308. package/src/skills-lib/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  309. package/src/skills-lib/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  310. package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  311. package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  312. package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  313. package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  314. package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  315. package/src/skills-lib/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  316. package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  317. package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  318. package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  319. package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  320. package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  321. package/src/skills-lib/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  322. package/src/skills-lib/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  323. package/src/skills-lib/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  324. package/src/skills-lib/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  325. package/src/skills-lib/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  326. package/src/skills-lib/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  327. package/src/skills-lib/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  328. package/src/skills-lib/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  329. package/src/skills-lib/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  330. package/src/skills-lib/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  331. package/src/skills-lib/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  332. package/src/skills-lib/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  333. package/src/skills-lib/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  334. package/src/skills-lib/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  335. package/src/skills-lib/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  336. package/src/skills-lib/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  337. package/src/skills-lib/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  338. package/src/skills-lib/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  339. package/src/skills-lib/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  340. package/src/skills-lib/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  341. package/src/skills-lib/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  342. package/src/skills-lib/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  343. package/src/skills-lib/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  344. package/src/skills-lib/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  345. package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  346. package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  347. package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  348. package/src/skills-lib/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  349. package/src/skills-lib/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  350. package/src/skills-lib/huashu-design/assets/bgm-ad.mp3 +0 -0
  351. package/src/skills-lib/huashu-design/assets/bgm-educational-alt.mp3 +0 -0
  352. package/src/skills-lib/huashu-design/assets/bgm-educational.mp3 +0 -0
  353. package/src/skills-lib/huashu-design/assets/bgm-tech.mp3 +0 -0
  354. package/src/skills-lib/huashu-design/assets/bgm-tutorial-alt.mp3 +0 -0
  355. package/src/skills-lib/huashu-design/assets/bgm-tutorial.mp3 +0 -0
  356. package/src/skills-lib/huashu-design/assets/sfx/container/card-flip.mp3 +0 -0
  357. package/src/skills-lib/huashu-design/assets/sfx/container/card-snap.mp3 +0 -0
  358. package/src/skills-lib/huashu-design/assets/sfx/container/modal-open.mp3 +0 -0
  359. package/src/skills-lib/huashu-design/assets/sfx/container/stack-collapse.mp3 +0 -0
  360. package/src/skills-lib/huashu-design/assets/sfx/feedback/achievement.mp3 +0 -0
  361. package/src/skills-lib/huashu-design/assets/sfx/feedback/error-tone.mp3 +0 -0
  362. package/src/skills-lib/huashu-design/assets/sfx/feedback/notification-pop.mp3 +0 -0
  363. package/src/skills-lib/huashu-design/assets/sfx/feedback/success-chime.mp3 +0 -0
  364. package/src/skills-lib/huashu-design/assets/sfx/impact/brand-stamp.mp3 +0 -0
  365. package/src/skills-lib/huashu-design/assets/sfx/impact/drop-thud.mp3 +0 -0
  366. package/src/skills-lib/huashu-design/assets/sfx/impact/logo-reveal-v2.mp3 +0 -0
  367. package/src/skills-lib/huashu-design/assets/sfx/impact/logo-reveal.mp3 +0 -0
  368. package/src/skills-lib/huashu-design/assets/sfx/keyboard/delete-key.mp3 +0 -0
  369. package/src/skills-lib/huashu-design/assets/sfx/keyboard/enter.mp3 +0 -0
  370. package/src/skills-lib/huashu-design/assets/sfx/keyboard/space-tap.mp3 +0 -0
  371. package/src/skills-lib/huashu-design/assets/sfx/keyboard/type-fast.mp3 +0 -0
  372. package/src/skills-lib/huashu-design/assets/sfx/keyboard/type.mp3 +0 -0
  373. package/src/skills-lib/huashu-design/assets/sfx/magic/ai-process.mp3 +0 -0
  374. package/src/skills-lib/huashu-design/assets/sfx/magic/sparkle.mp3 +0 -0
  375. package/src/skills-lib/huashu-design/assets/sfx/magic/transform.mp3 +0 -0
  376. package/src/skills-lib/huashu-design/assets/sfx/progress/complete-done.mp3 +0 -0
  377. package/src/skills-lib/huashu-design/assets/sfx/progress/generate-start.mp3 +0 -0
  378. package/src/skills-lib/huashu-design/assets/sfx/progress/loading-tick.mp3 +0 -0
  379. package/src/skills-lib/huashu-design/assets/sfx/terminal/command-execute.mp3 +0 -0
  380. package/src/skills-lib/huashu-design/assets/sfx/terminal/cursor-blink.mp3 +0 -0
  381. package/src/skills-lib/huashu-design/assets/sfx/terminal/output-appear.mp3 +0 -0
  382. package/src/skills-lib/huashu-design/assets/sfx/transition/dissolve.mp3 +0 -0
  383. package/src/skills-lib/huashu-design/assets/sfx/transition/slide-in.mp3 +0 -0
  384. package/src/skills-lib/huashu-design/assets/sfx/transition/swipe-horizontal.mp3 +0 -0
  385. package/src/skills-lib/huashu-design/assets/sfx/transition/whoosh-fast.mp3 +0 -0
  386. package/src/skills-lib/huashu-design/assets/sfx/transition/whoosh.mp3 +0 -0
  387. package/src/skills-lib/huashu-design/assets/sfx/ui/click-soft.mp3 +0 -0
  388. package/src/skills-lib/huashu-design/assets/sfx/ui/click.mp3 +0 -0
  389. package/src/skills-lib/huashu-design/assets/sfx/ui/focus.mp3 +0 -0
  390. package/src/skills-lib/huashu-design/assets/sfx/ui/hover-subtle.mp3 +0 -0
  391. package/src/skills-lib/huashu-design/assets/sfx/ui/tap-finger.mp3 +0 -0
  392. package/src/skills-lib/huashu-design/assets/sfx/ui/toggle-on.mp3 +0 -0
  393. package/src/squads/README.md +0 -39
  394. package/src/squads/advisory-board/README.md +0 -34
  395. package/src/squads/advisory-board/agents/board-chair.md +0 -161
  396. package/src/squads/advisory-board/agents/brene-brown.md +0 -296
  397. package/src/squads/advisory-board/agents/charlie-munger.md +0 -590
  398. package/src/squads/advisory-board/agents/derek-sivers.md +0 -268
  399. package/src/squads/advisory-board/agents/naval-ravikant.md +0 -251
  400. package/src/squads/advisory-board/agents/patrick-lencioni.md +0 -394
  401. package/src/squads/advisory-board/agents/peter-thiel.md +0 -269
  402. package/src/squads/advisory-board/agents/ray-dalio.md +0 -567
  403. package/src/squads/advisory-board/agents/reid-hoffman.md +0 -278
  404. package/src/squads/advisory-board/agents/simon-sinek.md +0 -291
  405. package/src/squads/advisory-board/agents/yvon-chouinard.md +0 -275
  406. package/src/squads/advisory-board/squad.yaml +0 -50
  407. package/src/squads/brand-squad/README.md +0 -60
  408. package/src/squads/brand-squad/agents/al-ries.md +0 -175
  409. package/src/squads/brand-squad/agents/alina-wheeler.md +0 -184
  410. package/src/squads/brand-squad/agents/archetype-consultant.md +0 -215
  411. package/src/squads/brand-squad/agents/brand-chief.md +0 -158
  412. package/src/squads/brand-squad/agents/byron-sharp.md +0 -169
  413. package/src/squads/brand-squad/agents/david-aaker.md +0 -166
  414. package/src/squads/brand-squad/agents/denise-yohn.md +0 -172
  415. package/src/squads/brand-squad/agents/domain-scout.md +0 -135
  416. package/src/squads/brand-squad/agents/donald-miller.md +0 -179
  417. package/src/squads/brand-squad/agents/emily-heyward.md +0 -147
  418. package/src/squads/brand-squad/agents/jean-noel-kapferer.md +0 -189
  419. package/src/squads/brand-squad/agents/kevin-keller.md +0 -199
  420. package/src/squads/brand-squad/agents/marty-neumeier.md +0 -170
  421. package/src/squads/brand-squad/agents/miller-sticky-brand.md +0 -180
  422. package/src/squads/brand-squad/agents/naming-strategist.md +0 -171
  423. package/src/squads/brand-squad/squad.yaml +0 -117
  424. package/src/squads/c-level-squad/README.md +0 -29
  425. package/src/squads/c-level-squad/agents/caio-architect.md +0 -235
  426. package/src/squads/c-level-squad/agents/cio-engineer.md +0 -209
  427. package/src/squads/c-level-squad/agents/cmo-architect.md +0 -208
  428. package/src/squads/c-level-squad/agents/coo-orchestrator.md +0 -193
  429. package/src/squads/c-level-squad/agents/cto-architect.md +0 -194
  430. package/src/squads/c-level-squad/agents/vision-chief.md +0 -201
  431. package/src/squads/c-level-squad/squad.yaml +0 -45
  432. package/src/squads/claude-code-mastery/README.md +0 -146
  433. package/src/squads/claude-code-mastery/agents/claude-mastery-chief.md +0 -554
  434. package/src/squads/claude-code-mastery/agents/config-engineer.md +0 -865
  435. package/src/squads/claude-code-mastery/agents/hooks-architect.md +0 -1013
  436. package/src/squads/claude-code-mastery/agents/mcp-integrator.md +0 -791
  437. package/src/squads/claude-code-mastery/agents/project-integrator.md +0 -1196
  438. package/src/squads/claude-code-mastery/agents/roadmap-sentinel.md +0 -931
  439. package/src/squads/claude-code-mastery/agents/skill-craftsman.md +0 -1250
  440. package/src/squads/claude-code-mastery/agents/swarm-orchestrator.md +0 -1008
  441. package/src/squads/copy-squad/README.md +0 -65
  442. package/src/squads/copy-squad/agents/andre-chaperon.md +0 -206
  443. package/src/squads/copy-squad/agents/ben-settle.md +0 -194
  444. package/src/squads/copy-squad/agents/claude-hopkins.md +0 -182
  445. package/src/squads/copy-squad/agents/clayton-makepeace.md +0 -225
  446. package/src/squads/copy-squad/agents/copy-chief.md +0 -166
  447. package/src/squads/copy-squad/agents/dan-kennedy.md +0 -202
  448. package/src/squads/copy-squad/agents/dan-koe.md +0 -223
  449. package/src/squads/copy-squad/agents/david-deutsch.md +0 -197
  450. package/src/squads/copy-squad/agents/david-ogilvy.md +0 -198
  451. package/src/squads/copy-squad/agents/eugene-schwartz.md +0 -238
  452. package/src/squads/copy-squad/agents/frank-kern.md +0 -195
  453. package/src/squads/copy-squad/agents/gary-bencivenga.md +0 -197
  454. package/src/squads/copy-squad/agents/gary-halbert.md +0 -219
  455. package/src/squads/copy-squad/agents/jim-rutz.md +0 -193
  456. package/src/squads/copy-squad/agents/joe-sugarman.md +0 -234
  457. package/src/squads/copy-squad/agents/john-carlton.md +0 -203
  458. package/src/squads/copy-squad/agents/jon-benson.md +0 -196
  459. package/src/squads/copy-squad/agents/parris-lampropoulos.md +0 -188
  460. package/src/squads/copy-squad/agents/robert-collier.md +0 -183
  461. package/src/squads/copy-squad/agents/russell-brunson.md +0 -229
  462. package/src/squads/copy-squad/agents/ry-schwartz.md +0 -185
  463. package/src/squads/copy-squad/agents/stefan-georgi.md +0 -188
  464. package/src/squads/copy-squad/agents/todd-brown.md +0 -171
  465. package/src/squads/copy-squad/squad.yaml +0 -87
  466. package/src/squads/cybersecurity/README.md +0 -38
  467. package/src/squads/cybersecurity/agents/busterer.md +0 -108
  468. package/src/squads/cybersecurity/agents/cartographer.md +0 -114
  469. package/src/squads/cybersecurity/agents/chris-sanders.md +0 -205
  470. package/src/squads/cybersecurity/agents/command-generator.md +0 -121
  471. package/src/squads/cybersecurity/agents/cyber-chief.md +0 -120
  472. package/src/squads/cybersecurity/agents/dirber.md +0 -111
  473. package/src/squads/cybersecurity/agents/fuzzer.md +0 -121
  474. package/src/squads/cybersecurity/agents/georgia-weidman.md +0 -178
  475. package/src/squads/cybersecurity/agents/jim-manico.md +0 -182
  476. package/src/squads/cybersecurity/agents/marcus-carey.md +0 -200
  477. package/src/squads/cybersecurity/agents/omar-santos.md +0 -206
  478. package/src/squads/cybersecurity/agents/peter-kim.md +0 -179
  479. package/src/squads/cybersecurity/agents/ripper.md +0 -121
  480. package/src/squads/cybersecurity/agents/rogue.md +0 -116
  481. package/src/squads/cybersecurity/agents/shannon-runner.md +0 -108
  482. package/src/squads/cybersecurity/squad.yaml +0 -56
  483. package/src/squads/data-squad/README.md +0 -30
  484. package/src/squads/data-squad/agents/avinash-kaushik.md +0 -268
  485. package/src/squads/data-squad/agents/data-chief.md +0 -167
  486. package/src/squads/data-squad/agents/david-spinks.md +0 -277
  487. package/src/squads/data-squad/agents/nick-mehta.md +0 -294
  488. package/src/squads/data-squad/agents/peter-fader.md +0 -246
  489. package/src/squads/data-squad/agents/sean-ellis.md +0 -303
  490. package/src/squads/data-squad/agents/wes-kao.md +0 -268
  491. package/src/squads/data-squad/squad.yaml +0 -46
  492. package/src/squads/design-squad/README.md +0 -31
  493. package/src/squads/design-squad/agents/brad-frost.md +0 -185
  494. package/src/squads/design-squad/agents/dan-mall.md +0 -178
  495. package/src/squads/design-squad/agents/dave-malouf.md +0 -198
  496. package/src/squads/design-squad/agents/design-chief.md +0 -109
  497. package/src/squads/design-squad/agents/design-system-architect.md +0 -109
  498. package/src/squads/design-squad/agents/ui-engineer.md +0 -102
  499. package/src/squads/design-squad/agents/ux-designer.md +0 -105
  500. package/src/squads/design-squad/agents/visual-generator.md +0 -108
  501. package/src/squads/design-squad/squad.yaml +0 -48
  502. package/src/squads/hormozi-squad/README.md +0 -39
  503. package/src/squads/hormozi-squad/agents/hormozi-ads.md +0 -159
  504. package/src/squads/hormozi-squad/agents/hormozi-advisor.md +0 -178
  505. package/src/squads/hormozi-squad/agents/hormozi-audit.md +0 -178
  506. package/src/squads/hormozi-squad/agents/hormozi-chief.md +0 -111
  507. package/src/squads/hormozi-squad/agents/hormozi-closer.md +0 -179
  508. package/src/squads/hormozi-squad/agents/hormozi-content.md +0 -166
  509. package/src/squads/hormozi-squad/agents/hormozi-copy.md +0 -152
  510. package/src/squads/hormozi-squad/agents/hormozi-hooks.md +0 -177
  511. package/src/squads/hormozi-squad/agents/hormozi-launch.md +0 -160
  512. package/src/squads/hormozi-squad/agents/hormozi-leads.md +0 -198
  513. package/src/squads/hormozi-squad/agents/hormozi-models.md +0 -191
  514. package/src/squads/hormozi-squad/agents/hormozi-offers.md +0 -197
  515. package/src/squads/hormozi-squad/agents/hormozi-pricing.md +0 -151
  516. package/src/squads/hormozi-squad/agents/hormozi-retention.md +0 -166
  517. package/src/squads/hormozi-squad/agents/hormozi-scale.md +0 -175
  518. package/src/squads/hormozi-squad/agents/hormozi-workshop.md +0 -160
  519. package/src/squads/hormozi-squad/squad.yaml +0 -60
  520. package/src/squads/movement/README.md +0 -29
  521. package/src/squads/movement/agents/analista-de-impacto.md +0 -350
  522. package/src/squads/movement/agents/estrategista-de-ciclo.md +0 -301
  523. package/src/squads/movement/agents/fenomenologo.md +0 -169
  524. package/src/squads/movement/agents/identitario.md +0 -265
  525. package/src/squads/movement/agents/manifestador.md +0 -279
  526. package/src/squads/movement/agents/movement-architect.md +0 -210
  527. package/src/squads/movement/agents/movement-chief.md +0 -135
  528. package/src/squads/movement/squad.yaml +0 -45
  529. package/src/squads/storytelling/README.md +0 -35
  530. package/src/squads/storytelling/agents/blake-snyder.md +0 -225
  531. package/src/squads/storytelling/agents/dan-harmon.md +0 -167
  532. package/src/squads/storytelling/agents/joseph-campbell.md +0 -184
  533. package/src/squads/storytelling/agents/keith-johnstone.md +0 -157
  534. package/src/squads/storytelling/agents/kindra-hall.md +0 -162
  535. package/src/squads/storytelling/agents/marshall-ganz.md +0 -167
  536. package/src/squads/storytelling/agents/matthew-dicks.md +0 -152
  537. package/src/squads/storytelling/agents/nancy-duarte.md +0 -154
  538. package/src/squads/storytelling/agents/oren-klaff.md +0 -166
  539. package/src/squads/storytelling/agents/park-howell.md +0 -152
  540. package/src/squads/storytelling/agents/shawn-coyne.md +0 -208
  541. package/src/squads/storytelling/agents/story-chief.md +0 -155
  542. package/src/squads/storytelling/squad.yaml +0 -52
  543. package/src/squads/traffic-masters/README.md +0 -39
  544. package/src/squads/traffic-masters/agents/ad-midas.md +0 -149
  545. package/src/squads/traffic-masters/agents/ads-analyst.md +0 -135
  546. package/src/squads/traffic-masters/agents/creative-analyst.md +0 -142
  547. package/src/squads/traffic-masters/agents/depesh-mandalia.md +0 -174
  548. package/src/squads/traffic-masters/agents/fiscal.md +0 -144
  549. package/src/squads/traffic-masters/agents/kasim-aslam.md +0 -158
  550. package/src/squads/traffic-masters/agents/media-buyer.md +0 -159
  551. package/src/squads/traffic-masters/agents/molly-pittman.md +0 -185
  552. package/src/squads/traffic-masters/agents/nicholas-kusmich.md +0 -157
  553. package/src/squads/traffic-masters/agents/pedro-sobral.md +0 -158
  554. package/src/squads/traffic-masters/agents/performance-analyst.md +0 -135
  555. package/src/squads/traffic-masters/agents/pixel-specialist.md +0 -144
  556. package/src/squads/traffic-masters/agents/ralph-burns.md +0 -163
  557. package/src/squads/traffic-masters/agents/scale-optimizer.md +0 -144
  558. package/src/squads/traffic-masters/agents/tom-breeze.md +0 -175
  559. package/src/squads/traffic-masters/agents/traffic-chief.md +0 -97
  560. package/src/squads/traffic-masters/squad.yaml +0 -59
  561. package/tools/bundle-web-bundles.js +0 -117
  562. package/tools/format-workflow-md.js +0 -263
  563. package/tools/hooks/README.md +0 -17
  564. package/tools/hooks/security-defensive-context.js +0 -70
  565. package/tools/hooks/session-rules.js +0 -38
  566. package/tools/hooks/wizz-router-enforce.js +0 -188
  567. package/tools/javascript-conventions.md +0 -5
  568. package/tools/migrate-custom-module-paths.js +0 -124
  569. package/tools/skill-validator.md +0 -368
  570. package/tools/sync-global.mjs +0 -84
  571. package/tools/validate-sidebar-order.js +0 -388
  572. package/tools/validate-skills.js +0 -708
  573. package/tools/validate-svg-changes.sh +0 -356
  574. /package/tools/installer/{cli-utils.js → banner.js} +0 -0
@@ -3,23 +3,26 @@ const fs = require('../fs-native');
3
3
  const { Manifest } = require('./manifest');
4
4
  const { OfficialModules } = require('../modules/official-modules');
5
5
  const { installSkillsLib } = require('../modules/skills-lib');
6
- const { writeMcpConfig, renderAddCommand, prepareMcps } = require('../modules/mcp-config');
6
+ const { writeMcpConfig, renderAddCommand, prepareMcps, partitionAlreadyConfigured } = require('../modules/mcp-config');
7
+ const { promptMissingEnvVars } = require('../modules/env-vars');
7
8
  const { installClis, renderInstallCommand } = require('../modules/cli-config');
8
- const { writeDepsCache } = require('../modules/deps-cache');
9
+ const { writeDepsCache, readPreviousMcpPins } = require('../modules/deps-cache');
9
10
  const { IdeManager } = require('../ide/manager');
10
11
  const { FileOps } = require('../file-ops');
11
12
  const { Config } = require('./config');
12
- const { getProjectRoot, getSourcePath } = require('../project-root');
13
13
  const { ManifestGenerator } = require('./manifest-generator');
14
14
  const prompts = require('../prompts');
15
15
  const { WIZZ_FOLDER_NAME } = require('../ide/shared/path-utils');
16
16
  const { InstallPaths } = require('./install-paths');
17
17
  const { ExternalModuleManager } = require('../modules/external-manager');
18
18
  const { resolveModuleVersion } = require('../modules/version-resolver');
19
- const { MODULE_HELP_CSV_HEADER } = require('../modules/module-help-schema');
20
19
 
21
20
  const { ExistingInstall } = require('./existing-install');
22
21
  const { warnPreNativeSkillsLegacy } = require('./legacy-warnings');
22
+ const userFilePreservation = require('./user-file-preservation');
23
+ const moduleConfigWriter = require('./module-config-writer');
24
+ const helpCatalog = require('./help-catalog');
25
+ const quickUpdateModule = require('./quick-update');
23
26
 
24
27
  class Installer {
25
28
  constructor() {
@@ -40,12 +43,33 @@ class Installer {
40
43
  */
41
44
  async install(originalConfig) {
42
45
  let updateState = null;
46
+ // Fresh installs (A14, atomic install): the writing pipeline runs against
47
+ // a sibling `_wizz.tmp-<pid>/` scaffold and only gets renamed into
48
+ // `_wizz/` after `generateManifests` succeeds. Tracked here (outside the
49
+ // try's local scope) so the outer catch can remove an orphaned tmp dir
50
+ // without ever touching a real `_wizz/` — cleared the moment the swap
51
+ // actually happens, so a later best-effort failure (e.g. IDE setup) never
52
+ // deletes a now-valid install.
53
+ let freshInstallTmpDir = null;
43
54
 
44
55
  try {
45
56
  const config = Config.build(originalConfig);
46
- const paths = await InstallPaths.create(config);
57
+
58
+ // Detect BEFORE creating any scaffold — a plain, read-only check
59
+ // against the path _wizz/ would live at, untouched by InstallPaths.
60
+ // This decides tmp-vs-real for InstallPaths.create below; detecting
61
+ // after paths creation (as before A14) meant the real `_wizz/` was
62
+ // already force-created as a side effect even for a first-time
63
+ // install, which is exactly the corruption window this task closes.
64
+ const projectRootCandidate = path.resolve(config.directory);
65
+ const realWizzDirCandidate = path.join(projectRootCandidate, WIZZ_FOLDER_NAME);
66
+ const existingInstall = await ExistingInstall.detect(realWizzDirCandidate);
67
+ const isFreshInstall = !existingInstall.installed;
68
+
69
+ let paths = await InstallPaths.create(config, { useTmp: isFreshInstall });
70
+ if (isFreshInstall) freshInstallTmpDir = paths.wizzDir;
71
+
47
72
  const officialModules = await OfficialModules.build(config, paths);
48
- const existingInstall = await ExistingInstall.detect(paths.wizzDir);
49
73
 
50
74
  try {
51
75
  await warnPreNativeSkillsLegacy({
@@ -87,7 +111,7 @@ class Installer {
87
111
 
88
112
  const allModules = config.modules || [];
89
113
 
90
- await this._installAndConfigure(
114
+ const configureResult = await this._installAndConfigure(
91
115
  config,
92
116
  originalConfig,
93
117
  paths,
@@ -97,6 +121,13 @@ class Installer {
97
121
  officialModules,
98
122
  previousSkillManifestRows,
99
123
  );
124
+ // Fresh installs got promoted from the tmp scaffold to the real
125
+ // `_wizz/` inside _installAndConfigure (right after generateManifests
126
+ // succeeded) — pick up the rewritten paths so every step below
127
+ // (IDE setup, cleanup, summary) reads/writes the real location, and
128
+ // the outer catch stops treating this as an orphaned tmp dir.
129
+ paths = configureResult.paths;
130
+ if (configureResult.swapped) freshInstallTmpDir = null;
100
131
 
101
132
  await this._setupIdes(config, allModules, paths, addResult, previousSkillIds);
102
133
 
@@ -114,14 +145,30 @@ class Installer {
114
145
  customFiles: restoreResult.customFiles.length > 0 ? restoreResult.customFiles : undefined,
115
146
  modifiedFiles: restoreResult.modifiedFiles.length > 0 ? restoreResult.modifiedFiles : undefined,
116
147
  preInstallVersions,
148
+ traceEnabled: config.traceEnabled,
117
149
  });
118
150
 
151
+ // Some steps (IDE setup, global skills/MCP/CLI provisioning) are
152
+ // best-effort and record their outcome in `results` instead of
153
+ // throwing, so one broken IDE or MCP never aborts the rest of the
154
+ // install. That used to mean a real per-step failure was invisible to
155
+ // the caller: `success` was always true and the CLI always exited 0.
156
+ // Surface it structurally instead: 'error' rows (hard failures, e.g.
157
+ // a suspended/broken IDE setup) flip success to false; 'warn' rows
158
+ // (recoverable, e.g. a single MCP whose setup script failed) keep
159
+ // success true but are still counted so CI/automation can detect
160
+ // "succeeded with warnings" without parsing log text (see M24).
161
+ const errorCount = results.filter((r) => r.status === 'error').length;
162
+ const warningCount = results.filter((r) => r.status === 'warn').length;
163
+
119
164
  return {
120
- success: true,
165
+ success: errorCount === 0,
121
166
  path: paths.wizzDir,
122
167
  modules: config.modules,
123
168
  ides: config.ides,
124
169
  projectDir: paths.projectRoot,
170
+ errors: errorCount,
171
+ warnings: warningCount,
125
172
  };
126
173
  } catch (error) {
127
174
  await prompts.log.error('Installation failed');
@@ -134,6 +181,12 @@ class Installer {
134
181
  if (updateState?.tempModifiedBackupDir && (await fs.pathExists(updateState.tempModifiedBackupDir))) {
135
182
  await fs.remove(updateState.tempModifiedBackupDir);
136
183
  }
184
+ // A14: a fresh install that never reached the tmp→real swap leaves
185
+ // only the orphaned `_wizz.tmp-<pid>/` scaffold behind — remove just
186
+ // that, never the real `_wizz/` (which never existed on this path).
187
+ if (freshInstallTmpDir && (await fs.pathExists(freshInstallTmpDir))) {
188
+ await fs.remove(freshInstallTmpDir);
189
+ }
137
190
  } catch {
138
191
  // Best-effort cleanup — don't mask the original error
139
192
  }
@@ -229,6 +282,12 @@ class Installer {
229
282
  const moduleConfigs = officialModules.moduleConfigs;
230
283
 
231
284
  const dirResults = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
285
+ // Set true inside configTask the moment the A14 tmp→real swap actually
286
+ // runs, so the return value below tells `install()` precisely when it's
287
+ // safe to stop treating the tmp scaffold as something to clean up on
288
+ // failure — never inferred from path shape (updates and fresh-but-
289
+ // never-swapped both have `paths.isTmp === false` for different reasons).
290
+ let swapped = false;
232
291
 
233
292
  const installTasks = [];
234
293
 
@@ -294,6 +353,19 @@ class Installer {
294
353
  const configTask = {
295
354
  title: 'Generating configurations',
296
355
  task: async (message) => {
356
+ // A14 (atomic fresh install): `.mcp.json` lives outside `_wizz/`, at
357
+ // the project root. For a fresh install it must not be mutated until
358
+ // AFTER the tmp→real swap below (right after generateManifests
359
+ // succeeds) — writing it earlier would leave a mutated `.mcp.json`
360
+ // even when the install as a whole later fails and the tmp scaffold
361
+ // gets thrown away. `writeMcpConfig` below runs in `dryRun` mode in
362
+ // that case (same merge computation, no disk write) so the log
363
+ // lines/results and the `pinHashes` fed to `writeDepsCache` stay
364
+ // identical to today; the real write happens once, right after the
365
+ // swap. Updates (paths.isTmp === false) are out of scope for this
366
+ // task and keep writing inline, as before.
367
+ let pendingMcpWrite = null;
368
+
297
369
  await this.generateModuleConfigs(paths.wizzDir, moduleConfigs);
298
370
  addResult('Configurations', 'ok', 'generated');
299
371
 
@@ -322,6 +394,7 @@ class Installer {
322
394
  }
323
395
  } catch (error) {
324
396
  await prompts.log.warn(`Falha ao instalar skills globais: ${error.message}`);
397
+ addResult('Global skills', 'warn', error.message);
325
398
  }
326
399
 
327
400
  // Configure recommended MCP servers for the chosen areas. The user's
@@ -330,23 +403,62 @@ class Installer {
330
403
  // the rest. Additive + placeholder-secret, so it never clobbers config
331
404
  // or leaks tokens; a failure warns and never aborts the install.
332
405
  const mcpPlan = config.mcpPlan || { toWrite: [], toRecommend: [] };
406
+ let mcpPinHashes = {};
333
407
  try {
334
408
  const toWrite = mcpPlan.toWrite || [];
335
409
  if (toWrite.length > 0) {
336
410
  message('Configuring MCP servers...');
337
- // Some servers (e.g. scrapling) shell out to a binary that must be
338
- // installed and resolved to an absolute path first — otherwise we
339
- // would write a config the client can't launch (ENOENT). prepareMcps
340
- // installs + verifies those, patching server.command to the absolute
341
- // path; a server whose setup fails is dropped here (never written)
342
- // and reported so the user can fix it, instead of leaving a broken
343
- // config behind. Servers without a `setup` block pass through as-is.
411
+ // Every resolved MCP goes through prepareMcps (DETECT→INSTALL→
412
+ // VERIFY→RESOLVE) including ids already present in .mcp.json.
413
+ // A13 needs the RESOLVED server (setup-bearing entries like
414
+ // scrapling get their command rewritten to an absolute path) to
415
+ // hash-compare against what's on disk; comparing against the raw
416
+ // registry command would always "look changed" for those. This
417
+ // is still cheap for an already-installed binary: prepareMcp is
418
+ // detect-first, so only a genuinely missing binary pays for the
419
+ // network-heavy install/post_install steps.
344
420
  const { ready: mcpsReady, failed: mcpsFailed } = await prepareMcps({ mcps: toWrite });
345
421
  for (const f of mcpsFailed) {
346
422
  await prompts.log.warn(
347
423
  `MCP ${f.id} não pôde ser preparado (${f.error}). Config NÃO escrita para evitar ENOENT — instale manualmente e adicione depois com \`claude mcp add\`.`,
348
424
  );
349
425
  }
426
+ if (mcpsFailed.length > 0) {
427
+ addResult('MCP servers', 'warn', `falharam: ${mcpsFailed.map((f) => f.id).join(', ')}`);
428
+ }
429
+
430
+ // Env var assistance (3.3, design em _audit parte 3 seção E):
431
+ // before writing .mcp.json, detect missing `${VAR}`
432
+ // placeholders in the servers about to be written and — only
433
+ // when interactive (never --yes/no-TTY, E1/E5) — offer to fill
434
+ // them, persisting into `.claude/settings.local.json` (the only
435
+ // place confirmed to reach the MCP subprocess's env; C7). Runs
436
+ // on `mcpsReady` (never `toRecommend`) and skips any id already
437
+ // present in `.mcp.json` — its vars are moot, the additive
438
+ // merge below ignores that server entirely. A dedicated
439
+ // try/catch so a failure here only warns and never blocks the
440
+ // `.mcp.json` write that follows (own principle of this
441
+ // feature: never fail the install over a skipped/failed var).
442
+ try {
443
+ const { toPrepare: mcpsForEnvVars } = await partitionAlreadyConfigured({
444
+ projectDir: paths.projectRoot,
445
+ mcps: mcpsReady,
446
+ });
447
+ const envInteractive = !config.skipPrompts && !!process.stdin.isTTY;
448
+ await promptMissingEnvVars(mcpsForEnvVars, {
449
+ projectDir: paths.projectRoot,
450
+ interactive: envInteractive,
451
+ });
452
+ } catch (error) {
453
+ await prompts.log.warn(`Falha ao configurar env vars dos MCPs: ${error.message}`);
454
+ addResult('MCP env vars', 'warn', error.message);
455
+ }
456
+
457
+ // A13 ("merge aditivo congela pins para sempre"): read the pin
458
+ // hashes the LAST install recorded so writeMcpConfig can tell an
459
+ // untouched block (safe to auto-update the pin) from a
460
+ // user-customized one (never overwritten, only warned about).
461
+ const previousPins = await readPreviousMcpPins(paths.wizzDir);
350
462
  // Intentionally NOT tracked in installedFiles: .mcp.json is a
351
463
  // shared, user-owned config we merge into (it may hold the user's
352
464
  // own servers). Tracking it would expose it to uninstall removal
@@ -354,12 +466,33 @@ class Installer {
354
466
  const mcpResult = await writeMcpConfig({
355
467
  projectDir: paths.projectRoot,
356
468
  mcps: mcpsReady,
469
+ previousPins,
470
+ dryRun: paths.isTmp,
357
471
  });
472
+ if (paths.isTmp) {
473
+ // Real write deferred until after the tmp→real swap (see the
474
+ // comment at the top of this task and step 4/README below).
475
+ pendingMcpWrite = { mcpsReady, previousPins };
476
+ }
477
+ mcpPinHashes = mcpResult.pinHashes || {};
358
478
  if (mcpResult.added.length > 0) {
359
479
  addResult('MCP servers', 'ok', `${mcpResult.added.join(', ')} → .mcp.json`);
360
480
  }
481
+ const pinUpdated = mcpResult.pinUpdated || [];
482
+ if (pinUpdated.length > 0) {
483
+ await prompts.log.info(
484
+ `MCPs com pin atualizado (bloco não havia sido customizado): ${pinUpdated.map((u) => u.id).join(', ')}`,
485
+ );
486
+ addResult('MCP servers', 'ok', `pin atualizado: ${pinUpdated.map((u) => u.id).join(', ')}`);
487
+ }
488
+ const pinCustomized = mcpResult.pinCustomized || [];
489
+ if (pinCustomized.length > 0) {
490
+ await prompts.log.warn(
491
+ `Há atualização de pin disponível para ${pinCustomized.map((c) => c.id).join(', ')}, mas o bloco no .mcp.json foi customizado (diferente do que o installer escreveu) — mantido intacto. Atualize manualmente se quiser o pin novo.`,
492
+ );
493
+ }
361
494
  if (mcpResult.skipped.length > 0) {
362
- await prompts.log.info(`MCPs já presentes no .mcp.json (mantidos): ${mcpResult.skipped.join(', ')}`);
495
+ await prompts.log.info(`MCPs já presentes e atualizados no .mcp.json (mantidos): ${mcpResult.skipped.join(', ')}`);
363
496
  }
364
497
  }
365
498
  const toRecommend = mcpPlan.toRecommend || [];
@@ -375,6 +508,7 @@ class Installer {
375
508
  }
376
509
  } catch (error) {
377
510
  await prompts.log.warn(`Falha ao configurar MCPs: ${error.message}`);
511
+ addResult('MCP servers', 'warn', error.message);
378
512
  }
379
513
 
380
514
  // Install/recommend the CLIs the agents shell out to (agent-browser,
@@ -401,6 +535,15 @@ class Installer {
401
535
  `Falha ao instalar CLI ${f.id}: ${f.error}. Rode manualmente: ${renderInstallCommand(toInstall.find((c) => c.id === f.id) || {})}`,
402
536
  );
403
537
  }
538
+ if (cliResult.failed.length > 0) {
539
+ addResult('CLIs', 'warn', `falharam: ${cliResult.failed.map((f) => f.id).join(', ')}`);
540
+ }
541
+ // M14: verify failure never aborts (CLIs are opt-in) — just a
542
+ // clear warning that the tool cloned/installed but a runtime
543
+ // dependency might still be missing.
544
+ for (const w of cliResult.warnings || []) {
545
+ await prompts.log.warn(`CLI ${w.id} instalado, mas ${w.warning}`);
546
+ }
404
547
  }
405
548
  const toRecommend = cliPlan.toRecommend || [];
406
549
  if (toRecommend.length > 0) {
@@ -409,6 +552,7 @@ class Installer {
409
552
  }
410
553
  } catch (error) {
411
554
  await prompts.log.warn(`Falha ao configurar CLIs: ${error.message}`);
555
+ addResult('CLIs', 'warn', error.message);
412
556
  }
413
557
 
414
558
  // Persist a project-local cache of the skill dependencies (CLIs +
@@ -422,7 +566,10 @@ class Installer {
422
566
  wizzDir: paths.wizzDir,
423
567
  selectedAreas: config.selectedAreas || [],
424
568
  cliPlan: config.cliPlan || {},
425
- mcpPlan: config.mcpPlan || {},
569
+ // pinHashes (A13) is the writeMcpConfig result computed above,
570
+ // not part of the config-time plan — merged in here so the
571
+ // NEXT install's readPreviousMcpPins() can see it.
572
+ mcpPlan: { ...config.mcpPlan, pinHashes: mcpPinHashes },
426
573
  trackFile: (p) => this.installedFiles.add(p),
427
574
  });
428
575
  if (depsResult.wrote) {
@@ -430,7 +577,16 @@ class Installer {
430
577
  }
431
578
  } catch (error) {
432
579
  await prompts.log.warn(`Falha ao gravar cache de dependências: ${error.message}`);
580
+ addResult('Skill deps cache', 'warn', error.message);
433
581
  }
582
+ } else if (config.isQuickUpdate() && (config.modules || []).includes('bmm')) {
583
+ // Gate above only runs for fresh installs / Modify. Quick Update
584
+ // preserves settings and skips provisioning entirely, so say it
585
+ // explicitly instead of leaving the user to notice by absence
586
+ // (see project memory: "atualizei e não veio skill").
587
+ await prompts.log.info(
588
+ 'Quick Update: skills globais, MCPs e CLIs não foram alterados. Rode install e escolha "Modify Wizz Installation" para instalar itens novos.',
589
+ );
434
590
  }
435
591
 
436
592
  message('Generating manifests...');
@@ -456,7 +612,44 @@ class Installer {
456
612
  ides: config.ides || [],
457
613
  preservedModules: modulesForCsvPreserve,
458
614
  moduleConfigs,
615
+ // A14: wizzDir is still the tmp scaffold at this point for a fresh
616
+ // install (swap happens right below, after this call succeeds) —
617
+ // skill-manifest.csv must embed the REAL folder name regardless,
618
+ // or every skill path recorded in it (read later by IDE setup,
619
+ // post-swap) would point at a `_wizz.tmp-<pid>/` dir that no
620
+ // longer exists. `paths.realWizzDir` is set by InstallPaths.create
621
+ // for both tmp and non-tmp installs; guarded here because some
622
+ // tests hand-roll a minimal `paths` stand-in without it, in which
623
+ // case falling back to generateManifests' own default (basename of
624
+ // wizzDir) is exactly the pre-A14 behavior.
625
+ wizzFolderName: paths.realWizzDir ? path.basename(paths.realWizzDir) : undefined,
459
626
  });
627
+
628
+ // A14 atomic swap: generateManifests just succeeded, so the tmp
629
+ // scaffold now has a valid manifest.yaml — promote it to the real
630
+ // `_wizz/` in one same-filesystem rename. Anything that threw above
631
+ // this line (module copy, configs, global skills, MCP prepare, CLI
632
+ // install, deps cache, or generateManifests itself) never reaches
633
+ // here, so `install()`'s catch finds only the orphaned tmp dir and
634
+ // the real `_wizz/` never existed. No-op for updates (paths.isTmp
635
+ // is false; they were never given a tmp scaffold).
636
+ if (paths.isTmp) {
637
+ await fs.rename(paths.wizzDir, paths.realWizzDir);
638
+ paths = paths.toReal();
639
+ swapped = true;
640
+ }
641
+
642
+ // Perform the .mcp.json write deferred above, now that the swap
643
+ // succeeded. No-op when nothing was staged (non-wizz install, no
644
+ // areas selected, or an update that already wrote inline).
645
+ if (pendingMcpWrite) {
646
+ await writeMcpConfig({
647
+ projectDir: paths.projectRoot,
648
+ mcps: pendingMcpWrite.mcpsReady,
649
+ previousPins: pendingMcpWrite.previousPins,
650
+ });
651
+ }
652
+
460
653
  await this._appendPreservedSkillManifestRows(paths.wizzDir, previousSkillManifestRows, preservedModules);
461
654
 
462
655
  // Apply post-install --set TOML patches. Runs after writeCentralConfig
@@ -500,6 +693,11 @@ class Installer {
500
693
  }
501
694
 
502
695
  await prompts.tasks([configTask]);
696
+
697
+ // `paths` may have been reassigned above (fresh install: tmp → real,
698
+ // see the A14 swap inside configTask) — return it so `install()` picks
699
+ // up the real location for every step that runs after this returns.
700
+ return { paths, swapped };
503
701
  }
504
702
 
505
703
  /**
@@ -577,7 +775,8 @@ class Installer {
577
775
  const entries = await fs.readdir(current);
578
776
  if (entries.length > 0) break;
579
777
  await fs.rmdir(current);
580
- } catch {
778
+ } catch (error) {
779
+ await prompts.log.warn(`Warning: could not remove empty directory ${current}: ${error.message}`);
581
780
  break;
582
781
  }
583
782
  current = path.dirname(current);
@@ -648,66 +847,7 @@ class Installer {
648
847
  * @returns {Object} { customFiles, modifiedFiles } — lists of restored files
649
848
  */
650
849
  async _restoreUserFiles(paths, updateState) {
651
- const noFiles = { customFiles: [], modifiedFiles: [] };
652
-
653
- if (!updateState || (updateState.customFiles.length === 0 && updateState.modifiedFiles.length === 0)) {
654
- return noFiles;
655
- }
656
-
657
- let restoredCustomFiles = [];
658
- let restoredModifiedFiles = [];
659
-
660
- await prompts.tasks([
661
- {
662
- title: 'Finalizing installation',
663
- task: async (message) => {
664
- if (updateState.customFiles.length > 0) {
665
- message(`Restoring ${updateState.customFiles.length} custom files...`);
666
-
667
- for (const originalPath of updateState.customFiles) {
668
- const relativePath = path.relative(paths.wizzDir, originalPath);
669
- const backupPath = path.join(updateState.tempBackupDir, relativePath);
670
-
671
- if (await fs.pathExists(backupPath)) {
672
- await fs.ensureDir(path.dirname(originalPath));
673
- await fs.copy(backupPath, originalPath, { overwrite: true });
674
- }
675
- }
676
-
677
- if (updateState.tempBackupDir && (await fs.pathExists(updateState.tempBackupDir))) {
678
- await fs.remove(updateState.tempBackupDir);
679
- }
680
-
681
- restoredCustomFiles = updateState.customFiles;
682
- }
683
-
684
- if (updateState.modifiedFiles.length > 0) {
685
- restoredModifiedFiles = updateState.modifiedFiles;
686
-
687
- if (updateState.tempModifiedBackupDir && (await fs.pathExists(updateState.tempModifiedBackupDir))) {
688
- message(`Restoring ${restoredModifiedFiles.length} modified files as .bak...`);
689
-
690
- for (const modifiedFile of restoredModifiedFiles) {
691
- const relativePath = path.relative(paths.wizzDir, modifiedFile.path);
692
- const tempBackupPath = path.join(updateState.tempModifiedBackupDir, relativePath);
693
- const bakPath = modifiedFile.path + '.bak';
694
-
695
- if (await fs.pathExists(tempBackupPath)) {
696
- await fs.ensureDir(path.dirname(bakPath));
697
- await fs.copy(tempBackupPath, bakPath, { overwrite: true });
698
- }
699
- }
700
-
701
- await fs.remove(updateState.tempModifiedBackupDir);
702
- }
703
- }
704
-
705
- return 'Installation finalized';
706
- },
707
- },
708
- ]);
709
-
710
- return { customFiles: restoredCustomFiles, modifiedFiles: restoredModifiedFiles };
850
+ return userFilePreservation.restoreUserFiles(paths, updateState);
711
851
  }
712
852
 
713
853
  /**
@@ -758,34 +898,7 @@ class Installer {
758
898
  * @returns {Object} { tempBackupDir, tempModifiedBackupDir } — undefined if no files
759
899
  */
760
900
  async _backupUserFiles(paths, customFiles, modifiedFiles) {
761
- let tempBackupDir;
762
- let tempModifiedBackupDir;
763
-
764
- if (customFiles.length > 0) {
765
- tempBackupDir = path.join(paths.projectRoot, '_wizz-custom-backup-temp');
766
- await fs.ensureDir(tempBackupDir);
767
-
768
- for (const customFile of customFiles) {
769
- const relativePath = path.relative(paths.wizzDir, customFile);
770
- const backupPath = path.join(tempBackupDir, relativePath);
771
- await fs.ensureDir(path.dirname(backupPath));
772
- await fs.copy(customFile, backupPath);
773
- }
774
- }
775
-
776
- if (modifiedFiles.length > 0) {
777
- tempModifiedBackupDir = path.join(paths.projectRoot, '_wizz-modified-backup-temp');
778
- await fs.ensureDir(tempModifiedBackupDir);
779
-
780
- for (const modifiedFile of modifiedFiles) {
781
- const relativePath = path.relative(paths.wizzDir, modifiedFile.path);
782
- const tempBackupPath = path.join(tempModifiedBackupDir, relativePath);
783
- await fs.ensureDir(path.dirname(tempBackupPath));
784
- await fs.copy(modifiedFile.path, tempBackupPath, { overwrite: true });
785
- }
786
- }
787
-
788
- return { tempBackupDir, tempModifiedBackupDir };
901
+ return userFilePreservation.backupUserFiles(paths, customFiles, modifiedFiles);
789
902
  }
790
903
 
791
904
  /**
@@ -966,115 +1079,7 @@ class Installer {
966
1079
  * @returns {Object} Object with customFiles and modifiedFiles arrays
967
1080
  */
968
1081
  async detectCustomFiles(wizzDir, existingFilesManifest) {
969
- const customFiles = [];
970
- const modifiedFiles = [];
971
-
972
- // Memory subtrees (v6.1: _wizz/_memory, current: _wizz/memory) hold
973
- // per-user runtime data generated by agents with sidecars. These files
974
- // aren't installer-managed and must never be reported as "custom" or
975
- // "modified" — they're user state, not user overrides.
976
- const wizzMemoryPaths = ['_memory', 'memory'];
977
-
978
- // Check if the manifest has hashes - if not, we can't detect modifications
979
- let manifestHasHashes = false;
980
- if (existingFilesManifest && existingFilesManifest.length > 0) {
981
- manifestHasHashes = existingFilesManifest.some((f) => f.hash);
982
- }
983
-
984
- // Build map of previously installed files from files-manifest.csv with their hashes
985
- const installedFilesMap = new Map();
986
- for (const fileEntry of existingFilesManifest) {
987
- if (fileEntry.path) {
988
- const absolutePath = path.join(wizzDir, fileEntry.path);
989
- installedFilesMap.set(path.normalize(absolutePath), {
990
- hash: fileEntry.hash,
991
- relativePath: fileEntry.path,
992
- });
993
- }
994
- }
995
-
996
- // Recursively scan wizzDir for all files
997
- const scanDirectory = async (dir) => {
998
- try {
999
- const entries = await fs.readdir(dir, { withFileTypes: true });
1000
- for (const entry of entries) {
1001
- const fullPath = path.join(dir, entry.name);
1002
-
1003
- if (entry.isDirectory()) {
1004
- // Skip certain directories
1005
- if (entry.name === 'node_modules' || entry.name === '.git') {
1006
- continue;
1007
- }
1008
- await scanDirectory(fullPath);
1009
- } else if (entry.isFile()) {
1010
- const normalizedPath = path.normalize(fullPath);
1011
- const fileInfo = installedFilesMap.get(normalizedPath);
1012
-
1013
- // Skip certain system files that are auto-generated
1014
- const relativePath = path.relative(wizzDir, fullPath);
1015
- const fileName = path.basename(fullPath);
1016
-
1017
- // Skip _config directory EXCEPT for modified agent customizations
1018
- if (relativePath.startsWith('_config/') || relativePath.startsWith('_config\\')) {
1019
- // Special handling for .customize.yaml files - only preserve if modified
1020
- if (relativePath.includes('/agents/') && fileName.endsWith('.customize.yaml')) {
1021
- // Check if the customization file has been modified from manifest
1022
- const manifestPath = path.join(wizzDir, '_config', 'manifest.yaml');
1023
- if (await fs.pathExists(manifestPath)) {
1024
- const crypto = require('node:crypto');
1025
- const currentContent = await fs.readFile(fullPath, 'utf8');
1026
- const currentHash = crypto.createHash('sha256').update(currentContent).digest('hex');
1027
-
1028
- const yaml = require('yaml');
1029
- const manifestContent = await fs.readFile(manifestPath, 'utf8');
1030
- const manifestData = yaml.parse(manifestContent);
1031
- const originalHash = manifestData.agentCustomizations?.[relativePath];
1032
-
1033
- // Only add to customFiles if hash differs (user modified)
1034
- if (originalHash && currentHash !== originalHash) {
1035
- customFiles.push(fullPath);
1036
- }
1037
- }
1038
- }
1039
- continue;
1040
- }
1041
-
1042
- if (wizzMemoryPaths.some((mp) => relativePath === mp || relativePath.startsWith(mp + '/'))) {
1043
- continue;
1044
- }
1045
-
1046
- // Skip config.yaml files - these are regenerated on each install/update
1047
- if (fileName === 'config.yaml') {
1048
- continue;
1049
- }
1050
-
1051
- if (!fileInfo) {
1052
- // File not in manifest = custom file
1053
- // EXCEPT: Agent .md files in module folders are generated files, not custom
1054
- // Only treat .md files under _config/agents/ as custom
1055
- if (!(fileName.endsWith('.md') && relativePath.includes('/agents/') && !relativePath.startsWith('_config/'))) {
1056
- customFiles.push(fullPath);
1057
- }
1058
- } else if (manifestHasHashes && fileInfo.hash) {
1059
- // File in manifest with hash - check if it was modified
1060
- const currentHash = await this.manifest.calculateFileHash(fullPath);
1061
- if (currentHash && currentHash !== fileInfo.hash) {
1062
- // Hash changed = file was modified
1063
- modifiedFiles.push({
1064
- path: fullPath,
1065
- relativePath: fileInfo.relativePath,
1066
- });
1067
- }
1068
- }
1069
- }
1070
- }
1071
- } catch {
1072
- // Ignore errors scanning directories
1073
- }
1074
- };
1075
-
1076
- await scanDirectory(wizzDir);
1077
- return { customFiles, modifiedFiles };
1082
+ return userFilePreservation.detectCustomFiles(wizzDir, existingFilesManifest, { manifest: this.manifest });
1078
1083
  }
1079
1084
 
1080
1085
  /**
@@ -1083,93 +1088,7 @@ class Installer {
1083
1088
  * @param {Object} moduleConfigs - Collected configuration values
1084
1089
  */
1085
1090
  async generateModuleConfigs(wizzDir, moduleConfigs) {
1086
- const yaml = require('yaml');
1087
-
1088
- // Extract core config values to share with other modules
1089
- const coreConfig = moduleConfigs.core || {};
1090
-
1091
- // Get all installed module directories
1092
- const entries = await fs.readdir(wizzDir, { withFileTypes: true });
1093
- const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
1094
- const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
1095
-
1096
- // Generate config.yaml for each installed module
1097
- for (const moduleName of installedModules) {
1098
- const modulePath = path.join(wizzDir, moduleName);
1099
-
1100
- // Get module-specific config or use empty object if none
1101
- const config = moduleConfigs[moduleName] || {};
1102
-
1103
- if (await fs.pathExists(modulePath)) {
1104
- const configPath = path.join(modulePath, 'config.yaml');
1105
-
1106
- // Create header
1107
- const packageJson = require(path.join(getProjectRoot(), 'package.json'));
1108
- const header = `# ${moduleName.toUpperCase()} Module Configuration
1109
- # Generated by Wizz Method installer
1110
- # Version: ${packageJson.version}
1111
- # Date: ${new Date().toISOString()}
1112
-
1113
- `;
1114
-
1115
- // For non-core modules, add core config values directly
1116
- let finalConfig = { ...config };
1117
- let coreSection = '';
1118
-
1119
- if (moduleName !== 'core' && coreConfig && Object.keys(coreConfig).length > 0) {
1120
- // Add core values directly to the module config
1121
- // These will be available for reference in the module
1122
- finalConfig = {
1123
- ...config,
1124
- ...coreConfig, // Spread core config values directly into the module config
1125
- };
1126
-
1127
- // Create a comment section to identify core values
1128
- coreSection = '\n# Core Configuration Values\n';
1129
- }
1130
-
1131
- // Clean the config to remove any non-serializable values (like functions)
1132
- const cleanConfig = structuredClone(finalConfig);
1133
-
1134
- // Convert config to YAML
1135
- let yamlContent = yaml.stringify(cleanConfig, {
1136
- indent: 2,
1137
- lineWidth: 0,
1138
- minContentWidth: 0,
1139
- });
1140
-
1141
- // If we have core values, reorganize the YAML to group them with their comment
1142
- if (coreSection && moduleName !== 'core') {
1143
- // Split the YAML into lines
1144
- const lines = yamlContent.split('\n');
1145
- const moduleConfigLines = [];
1146
- const coreConfigLines = [];
1147
-
1148
- // Separate module-specific and core config lines
1149
- for (const line of lines) {
1150
- const key = line.split(':')[0].trim();
1151
- if (Object.prototype.hasOwnProperty.call(coreConfig, key)) {
1152
- coreConfigLines.push(line);
1153
- } else {
1154
- moduleConfigLines.push(line);
1155
- }
1156
- }
1157
-
1158
- // Rebuild YAML with module config first, then core config with comment
1159
- yamlContent = moduleConfigLines.join('\n');
1160
- if (coreConfigLines.length > 0) {
1161
- yamlContent += coreSection + coreConfigLines.join('\n');
1162
- }
1163
- }
1164
-
1165
- // Write the clean config file with POSIX-compliant final newline
1166
- const content = header + yamlContent;
1167
- await fs.writeFile(configPath, content.endsWith('\n') ? content : content + '\n', 'utf8');
1168
-
1169
- // Track the config file in installedFiles
1170
- this.installedFiles.add(configPath);
1171
- }
1172
- }
1091
+ return moduleConfigWriter.generateModuleConfigs(wizzDir, moduleConfigs, { trackFile: (p) => this.installedFiles.add(p) });
1173
1092
  }
1174
1093
 
1175
1094
  /**
@@ -1179,118 +1098,14 @@ class Installer {
1179
1098
  * @param {string} wizzDir - WIZZ installation directory
1180
1099
  * @param {Array<Object>} _agentEntries - Unused; retained for call-site compatibility
1181
1100
  */
1182
- async mergeModuleHelpCatalogs(wizzDir, _agentEntries = []) {
1183
- const allRows = [];
1184
- const headerRow = MODULE_HELP_CSV_HEADER;
1185
- const COLUMN_COUNT = 13;
1186
- const PHASE_INDEX = 7;
1187
-
1188
- // Get all installed module directories
1189
- const entries = await fs.readdir(wizzDir, { withFileTypes: true });
1190
- const nonModuleDirs = new Set(['_config', '_memory', 'memory', 'docs', 'scripts', 'custom']);
1191
- const installedModules = entries.filter((entry) => entry.isDirectory() && !nonModuleDirs.has(entry.name)).map((entry) => entry.name);
1192
-
1193
- // Add core module to scan (it's installed at root level as _config, but we check src/core-skills)
1194
- const coreModulePath = getSourcePath('core-skills');
1195
- const modulePaths = new Map();
1196
-
1197
- // Map all module source paths
1198
- if (await fs.pathExists(coreModulePath)) {
1199
- modulePaths.set('core', coreModulePath);
1200
- }
1201
-
1202
- // Map installed module paths
1203
- for (const moduleName of installedModules) {
1204
- const modulePath = path.join(wizzDir, moduleName);
1205
- modulePaths.set(moduleName, modulePath);
1206
- }
1207
-
1208
- // Scan each module for module-help.csv
1209
- for (const [moduleName, modulePath] of modulePaths) {
1210
- const helpFilePath = path.join(modulePath, 'module-help.csv');
1211
-
1212
- if (await fs.pathExists(helpFilePath)) {
1213
- try {
1214
- const content = await fs.readFile(helpFilePath, 'utf8');
1215
- const lines = content.split('\n').filter((line) => line.trim() && !line.startsWith('#'));
1216
-
1217
- let headerWarned = false;
1218
- for (const line of lines) {
1219
- // Header row: warn on drift from canonical schema, then skip.
1220
- // Data rows are loaded positionally regardless, so the warning
1221
- // is advisory — the maintainer should rename their columns.
1222
- if (line.startsWith('module,')) {
1223
- if (!headerWarned && line.trim() !== headerRow) {
1224
- await prompts.log.warn(
1225
- ` ${moduleName}/module-help.csv header does not match canonical schema. ` +
1226
- `Expected: ${headerRow} | Found: ${line.trim()} | Data loaded positionally.`,
1227
- );
1228
- headerWarned = true;
1229
- }
1230
- continue;
1231
- }
1232
-
1233
- // Parse the line - handle quoted fields with commas
1234
- const columns = this.parseCSVLine(line);
1235
- if (columns.length < COLUMN_COUNT - 1) continue;
1236
-
1237
- // Pad short rows; truncate over-long rows
1238
- const padded = columns.slice(0, COLUMN_COUNT);
1239
- while (padded.length < COLUMN_COUNT) padded.push('');
1240
-
1241
- // If module column is empty, fill with this module's name
1242
- // (core stays empty so its rows render as universal tools)
1243
- if ((!padded[0] || padded[0].trim() === '') && moduleName !== 'core') {
1244
- padded[0] = moduleName;
1245
- }
1246
-
1247
- allRows.push(padded.map((c) => this.escapeCSVField(c)).join(','));
1248
- }
1249
-
1250
- if (process.env.WIZZ_VERBOSE_INSTALL === 'true') {
1251
- await prompts.log.message(` Merged module-help from: ${moduleName}`);
1252
- }
1253
- } catch (error) {
1254
- await prompts.log.warn(` Warning: Failed to read module-help.csv from ${moduleName}: ${error.message}`);
1255
- }
1256
- }
1257
- }
1258
-
1259
- // Sort by module, then phase. Stable sort preserves authored order within a phase.
1260
- const decorated = allRows.map((row, index) => ({ row, index, cols: this.parseCSVLine(row) }));
1261
- decorated.sort((a, b) => {
1262
- const moduleA = (a.cols[0] || '').toLowerCase();
1263
- const moduleB = (b.cols[0] || '').toLowerCase();
1264
- if (moduleA !== moduleB) return moduleA.localeCompare(moduleB);
1265
-
1266
- const phaseA = a.cols[PHASE_INDEX] || '';
1267
- const phaseB = b.cols[PHASE_INDEX] || '';
1268
- if (phaseA !== phaseB) return phaseA.localeCompare(phaseB);
1269
-
1270
- return a.index - b.index;
1271
- });
1272
- const sortedRows = decorated.map((d) => d.row);
1273
-
1274
- // Write merged catalog
1275
- const outputDir = path.join(wizzDir, '_config');
1276
- await fs.ensureDir(outputDir);
1277
- const outputPath = path.join(outputDir, 'wizz-help.csv');
1278
-
1279
- const mergedContent = [headerRow, ...sortedRows].join('\n');
1280
- await fs.writeFile(outputPath, mergedContent, 'utf8');
1281
-
1282
- // Track the installed file
1283
- this.installedFiles.add(outputPath);
1284
-
1285
- if (process.env.WIZZ_VERBOSE_INSTALL === 'true') {
1286
- await prompts.log.message(` Generated wizz-help.csv: ${sortedRows.length} workflows`);
1287
- }
1101
+ async mergeModuleHelpCatalogs(wizzDir, agentEntries = []) {
1102
+ return helpCatalog.mergeModuleHelpCatalogs(wizzDir, agentEntries, { trackFile: (p) => this.installedFiles.add(p) });
1288
1103
  }
1289
1104
 
1290
1105
  /**
1291
1106
  * Render a consolidated install summary using prompts.note()
1292
1107
  * @param {Array} results - Array of {step, status: 'ok'|'error'|'warn', detail}
1293
- * @param {Object} context - {wizzDir, modules, ides, customFiles, modifiedFiles}
1108
+ * @param {Object} context - {wizzDir, modules, ides, customFiles, modifiedFiles, traceEnabled}
1294
1109
  */
1295
1110
  async renderInstallSummary(results, context = {}) {
1296
1111
  const color = await prompts.getColor();
@@ -1362,6 +1177,19 @@ class Installer {
1362
1177
  lines.push(` ${color.yellow(`Modified files backed up (.bak): ${context.modifiedFiles.length}`)}`);
1363
1178
  }
1364
1179
 
1180
+ // 3.8-E1: mention the routing-trace opt-in choice. `traceEnabled` is
1181
+ // `null` when the question was never asked (quick-update/update, or a
1182
+ // module set without bmm) — that case is intentionally silent instead of
1183
+ // misreporting an unasked question as "desligado".
1184
+ if (context.traceEnabled === true) {
1185
+ lines.push(
1186
+ ` ${color.cyan('Medidor de roteamento: ligado')} (WIZZ_TRACE=1 em .claude/settings.local.json)`,
1187
+ ` Para desligar: remova "WIZZ_TRACE" de .claude/settings.local.json (ou defina "0")`,
1188
+ );
1189
+ } else if (context.traceEnabled === false) {
1190
+ lines.push(` Medidor de roteamento: desligado (rode o install de novo para ligar)`);
1191
+ }
1192
+
1365
1193
  // Next steps
1366
1194
  lines.push(
1367
1195
  '',
@@ -1385,172 +1213,7 @@ class Installer {
1385
1213
  * @returns {Object} Update result
1386
1214
  */
1387
1215
  async quickUpdate(config) {
1388
- const projectDir = path.resolve(config.directory);
1389
- const { wizzDir } = await this.findWizzDir(projectDir);
1390
-
1391
- // Check if wizz directory exists
1392
- if (!(await fs.pathExists(wizzDir))) {
1393
- throw new Error(`Wizz not installed at ${wizzDir}. Use regular install for first-time setup.`);
1394
- }
1395
-
1396
- // Detect existing installation
1397
- const existingInstall = await ExistingInstall.detect(wizzDir);
1398
- const installedModules = existingInstall.moduleIds;
1399
- const configuredIdes = existingInstall.ides;
1400
- const projectRoot = path.dirname(wizzDir);
1401
-
1402
- // Get available modules (what we have source for)
1403
- const availableModulesData = await new OfficialModules().listAvailable();
1404
- const availableModules = [...availableModulesData.modules];
1405
-
1406
- // Add external official modules to available modules
1407
- const externalModules = await this.externalModuleManager.listAvailable();
1408
- for (const externalModule of externalModules) {
1409
- if (installedModules.includes(externalModule.code) && !availableModules.some((m) => m.id === externalModule.code)) {
1410
- availableModules.push({
1411
- id: externalModule.code,
1412
- name: externalModule.name,
1413
- isExternal: true,
1414
- fromExternal: true,
1415
- });
1416
- }
1417
- }
1418
-
1419
- // Add installed custom modules to available modules
1420
- const { CustomModuleManager } = require('../modules/custom-module-manager');
1421
- const customMgr = new CustomModuleManager();
1422
- for (const moduleId of installedModules) {
1423
- if (!availableModules.some((m) => m.id === moduleId)) {
1424
- const customSource = await customMgr.findModuleSourceByCode(moduleId, { wizzDir });
1425
- if (customSource) {
1426
- availableModules.push({
1427
- id: moduleId,
1428
- name: moduleId,
1429
- isExternal: true,
1430
- fromCustom: true,
1431
- });
1432
- }
1433
- }
1434
- }
1435
-
1436
- const availableModuleIds = new Set(availableModules.map((m) => m.id));
1437
-
1438
- // Only update modules that are BOTH installed AND available (we have source for)
1439
- const modulesToUpdate = installedModules.filter((id) => availableModuleIds.has(id));
1440
- const skippedModules = installedModules.filter((id) => !availableModuleIds.has(id));
1441
-
1442
- if (skippedModules.length > 0) {
1443
- await prompts.log.warn(`Skipping ${skippedModules.length} module(s) - no source available: ${skippedModules.join(', ')}`);
1444
- }
1445
-
1446
- // Build channel options from the existing manifest FIRST so the config
1447
- // collector below (which triggers external-module clones via
1448
- // findModuleSource) knows each module's recorded channel and doesn't
1449
- // silently redecide it. Without this, modules previously on 'next' or
1450
- // 'pinned' would trigger a stable-channel tag lookup at config-collection
1451
- // time, burning GitHub API quota and potentially failing.
1452
- const manifestData = await this.manifest.read(wizzDir);
1453
- const channelOptions = { global: null, nextSet: new Set(), pins: new Map(), warnings: [] };
1454
- if (manifestData?.modulesDetailed) {
1455
- const { fetchStableTags, classifyUpgrade, parseGitHubRepo } = require('../modules/channel-resolver');
1456
- for (const entry of manifestData.modulesDetailed) {
1457
- if (!entry?.name || !entry?.channel) continue;
1458
- if (entry.channel === 'pinned' && entry.version) {
1459
- channelOptions.pins.set(entry.name, entry.version);
1460
- continue;
1461
- }
1462
- if (entry.channel === 'next') {
1463
- channelOptions.nextSet.add(entry.name);
1464
- continue;
1465
- }
1466
- // Stable: classify the available upgrade. Patches and minors fall
1467
- // through (stable default picks up the top tag). A major upgrade
1468
- // requires opt-in, so under quick-update's non-interactive semantics
1469
- // we pin to the current version to prevent a silent breaking jump.
1470
- if (entry.channel === 'stable' && entry.version && entry.repoUrl) {
1471
- const parsed = parseGitHubRepo(entry.repoUrl);
1472
- if (!parsed) continue;
1473
- try {
1474
- const tags = await fetchStableTags(parsed.owner, parsed.repo);
1475
- if (tags.length === 0) continue;
1476
- const topTag = tags[0].tag;
1477
- const cls = classifyUpgrade(entry.version, topTag);
1478
- if (cls === 'major') {
1479
- channelOptions.pins.set(entry.name, entry.version);
1480
- await prompts.log.warn(
1481
- `${entry.name} ${entry.version} → ${topTag} is a new major release; staying on ${entry.version}. ` +
1482
- `Run \`wizz install\` (Modify) with \`--pin ${entry.name}=${topTag}\` to accept.`,
1483
- );
1484
- }
1485
- } catch (error) {
1486
- // Tag lookup failed (offline, rate-limited). Stay on the current
1487
- // version rather than guessing — the existing cache is already
1488
- // at that ref, so re-using it keeps the install stable.
1489
- channelOptions.pins.set(entry.name, entry.version);
1490
- await prompts.log.warn(`Could not check ${entry.name} for updates (${error.message}); staying on ${entry.version}.`);
1491
- }
1492
- }
1493
- }
1494
- }
1495
-
1496
- // Load existing configs and collect new fields (if any)
1497
- await prompts.log.info('Checking for new configuration options...');
1498
- const quickModules = new OfficialModules({ channelOptions });
1499
- await quickModules.loadExistingConfig(projectDir);
1500
-
1501
- let promptedForNewFields = false;
1502
-
1503
- const corePrompted = await quickModules.collectModuleConfigQuick('core', projectDir, true);
1504
- if (corePrompted) {
1505
- promptedForNewFields = true;
1506
- }
1507
-
1508
- for (const moduleName of modulesToUpdate) {
1509
- if (moduleName === 'core') continue; // Already collected above
1510
- const modulePrompted = await quickModules.collectModuleConfigQuick(moduleName, projectDir, true);
1511
- if (modulePrompted) {
1512
- promptedForNewFields = true;
1513
- }
1514
- }
1515
-
1516
- if (!promptedForNewFields) {
1517
- await prompts.log.success('All configuration is up to date, no new options to configure');
1518
- }
1519
-
1520
- quickModules.collectedConfig._meta = {
1521
- version: require(path.join(getProjectRoot(), 'package.json')).version,
1522
- installDate: new Date().toISOString(),
1523
- lastModified: new Date().toISOString(),
1524
- };
1525
-
1526
- // Build config and delegate to install()
1527
- const installConfig = {
1528
- directory: projectDir,
1529
- modules: modulesToUpdate,
1530
- ides: configuredIdes,
1531
- coreConfig: quickModules.collectedConfig.core,
1532
- moduleConfigs: quickModules.collectedConfig,
1533
- // Forward `--set` overrides so the post-install patch step
1534
- // (`applySetOverrides`) runs at the end of quick-update too. The
1535
- // installer.install path applies them after writeCentralConfig.
1536
- setOverrides: config.setOverrides || {},
1537
- actionType: 'install',
1538
- _quickUpdate: true,
1539
- _preserveModules: skippedModules,
1540
- _existingModules: installedModules,
1541
- channelOptions,
1542
- };
1543
-
1544
- await this.install(installConfig);
1545
-
1546
- return {
1547
- success: true,
1548
- moduleCount: modulesToUpdate.length,
1549
- hadNewFields: promptedForNewFields,
1550
- modules: modulesToUpdate,
1551
- skippedModules: skippedModules,
1552
- ides: configuredIdes,
1553
- };
1216
+ return quickUpdateModule.quickUpdate(this, config);
1554
1217
  }
1555
1218
 
1556
1219
  /**
@@ -1703,8 +1366,8 @@ class Installer {
1703
1366
  // Strip {project-root}/ prefix if present
1704
1367
  return config.output_folder.replace(/^\{project-root\}[/\\]/, '');
1705
1368
  }
1706
- } catch {
1707
- // Fall through to other modules
1369
+ } catch (error) {
1370
+ await prompts.log.warn(`Warning: could not read ${bmmConfigPath}: ${error.message}`);
1708
1371
  }
1709
1372
  }
1710
1373
 
@@ -1721,13 +1384,13 @@ class Installer {
1721
1384
  if (config && config.output_folder) {
1722
1385
  return config.output_folder.replace(/^\{project-root\}[/\\]/, '');
1723
1386
  }
1724
- } catch {
1725
- // Continue scanning
1387
+ } catch (error) {
1388
+ await prompts.log.warn(`Warning: could not read ${configPath}: ${error.message}`);
1726
1389
  }
1727
1390
  }
1728
1391
  }
1729
- } catch {
1730
- // Directory scan failed
1392
+ } catch (error) {
1393
+ await prompts.log.warn(`Warning: could not scan ${wizzDir} for module configs: ${error.message}`);
1731
1394
  }
1732
1395
 
1733
1396
  // Default fallback
@@ -1735,54 +1398,26 @@ class Installer {
1735
1398
  }
1736
1399
 
1737
1400
  /**
1738
- * Parse a CSV line, handling quoted fields
1401
+ * Parse a CSV line, handling quoted fields.
1402
+ * Thin wrapper — see `core/help-catalog.js` for the implementation and the
1403
+ * M15 rationale (auditoria 2026-07-07) on why this delegates to
1404
+ * `csv-parse/sync`.
1739
1405
  * @param {string} line - CSV line to parse
1740
1406
  * @returns {Array} Array of field values
1741
1407
  */
1742
1408
  parseCSVLine(line) {
1743
- const result = [];
1744
- let current = '';
1745
- let inQuotes = false;
1746
-
1747
- for (let i = 0; i < line.length; i++) {
1748
- const char = line[i];
1749
- const nextChar = line[i + 1];
1750
-
1751
- if (char === '"') {
1752
- if (inQuotes && nextChar === '"') {
1753
- // Escaped quote
1754
- current += '"';
1755
- i++; // Skip next quote
1756
- } else {
1757
- // Toggle quote mode
1758
- inQuotes = !inQuotes;
1759
- }
1760
- } else if (char === ',' && !inQuotes) {
1761
- result.push(current);
1762
- current = '';
1763
- } else {
1764
- current += char;
1765
- }
1766
- }
1767
- result.push(current);
1768
- return result;
1409
+ return helpCatalog.parseCSVLine(line);
1769
1410
  }
1770
1411
 
1771
1412
  /**
1772
- * Escape a CSV field if it contains special characters
1413
+ * Escape a CSV field if it contains special characters.
1414
+ * Thin wrapper — see `core/help-catalog.js` for the implementation and the
1415
+ * M15 rationale (auditoria 2026-07-07) on why this stays manual.
1773
1416
  * @param {string} field - Field value to escape
1774
1417
  * @returns {string} Escaped field
1775
1418
  */
1776
1419
  escapeCSVField(field) {
1777
- if (field === null || field === undefined) {
1778
- return '';
1779
- }
1780
- const str = String(field);
1781
- // If field contains comma, quote, or newline, wrap in quotes and escape inner quotes
1782
- if (str.includes(',') || str.includes('"') || str.includes('\n')) {
1783
- return `"${str.replaceAll('"', '""')}"`;
1784
- }
1785
- return str;
1420
+ return helpCatalog.escapeCSVField(field);
1786
1421
  }
1787
1422
  }
1788
1423