wizz-method 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/LICENSE +60 -30
  2. package/README.md +80 -31
  3. package/SECURITY.md +24 -0
  4. package/THIRD-PARTY-NOTICES.md +136 -0
  5. package/package.json +31 -17
  6. package/skills-registry.yaml +128 -7
  7. package/src/bmm-skills/1-analysis/research/wizz-domain-research/SKILL.md +3 -4
  8. package/src/bmm-skills/1-analysis/research/wizz-domain-research/domain-steps/step-06-research-synthesis.md +1 -1
  9. package/src/bmm-skills/1-analysis/research/wizz-market-research/SKILL.md +3 -4
  10. package/src/bmm-skills/1-analysis/research/wizz-market-research/steps/step-06-research-completion.md +1 -1
  11. package/src/bmm-skills/1-analysis/research/wizz-technical-research/SKILL.md +3 -4
  12. package/src/bmm-skills/1-analysis/research/wizz-technical-research/technical-steps/step-06-research-synthesis.md +1 -1
  13. package/src/bmm-skills/1-analysis/wizz-agent-analyst/SKILL.md +3 -4
  14. package/src/bmm-skills/1-analysis/wizz-agent-tech-writer/SKILL.md +3 -4
  15. package/src/bmm-skills/1-analysis/wizz-document-project/SKILL.md +3 -4
  16. package/src/bmm-skills/1-analysis/wizz-document-project/workflows/deep-dive-instructions.md +1 -1
  17. package/src/bmm-skills/1-analysis/wizz-document-project/workflows/full-scan-instructions.md +1 -1
  18. package/src/bmm-skills/1-analysis/wizz-prfaq/SKILL.md +3 -4
  19. package/src/bmm-skills/1-analysis/wizz-prfaq/references/verdict.md +1 -1
  20. package/src/bmm-skills/1-analysis/wizz-product-brief/SKILL.md +1 -1
  21. package/src/bmm-skills/2-plan-workflows/wizz-agent-pm/SKILL.md +3 -4
  22. package/src/bmm-skills/2-plan-workflows/wizz-agent-ux-designer/SKILL.md +3 -4
  23. package/src/bmm-skills/2-plan-workflows/wizz-create-prd/SKILL.md +1 -1
  24. package/src/bmm-skills/2-plan-workflows/wizz-edit-prd/SKILL.md +1 -1
  25. package/src/bmm-skills/2-plan-workflows/wizz-prd/SKILL.md +1 -1
  26. package/src/bmm-skills/2-plan-workflows/wizz-ux/SKILL.md +1 -1
  27. package/src/bmm-skills/2-plan-workflows/wizz-validate-prd/SKILL.md +1 -1
  28. package/src/bmm-skills/3-solutioning/wizz-agent-architect/SKILL.md +3 -4
  29. package/src/bmm-skills/3-solutioning/wizz-architecture/SKILL.md +9 -9
  30. package/src/bmm-skills/3-solutioning/wizz-architecture/references/headless.md +1 -1
  31. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/SKILL.md +3 -4
  32. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/steps/step-06-final-assessment.md +1 -1
  33. package/src/bmm-skills/3-solutioning/wizz-create-architecture/SKILL.md +1 -1
  34. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/SKILL.md +3 -4
  35. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
  36. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/SKILL.md +3 -4
  37. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/steps/step-03-complete.md +1 -1
  38. package/src/bmm-skills/4-implementation/wizz-agent-dev/SKILL.md +3 -4
  39. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/SKILL.md +3 -4
  40. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/step-05-wrapup.md +1 -1
  41. package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +3 -4
  42. package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +3 -8
  43. package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/blind-hunter.md +20 -0
  44. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +1 -1
  45. package/src/bmm-skills/4-implementation/wizz-correct-course/SKILL.md +4 -5
  46. package/src/bmm-skills/4-implementation/wizz-create-story/SKILL.md +4 -5
  47. package/src/bmm-skills/4-implementation/wizz-dev-story/SKILL.md +4 -5
  48. package/src/bmm-skills/4-implementation/wizz-investigate/SKILL.md +1 -1
  49. package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/SKILL.md +4 -5
  50. package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +4 -5
  51. package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +10 -22
  52. package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/blind-hunter.md +20 -0
  53. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-05-present.md +1 -1
  54. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-oneshot.md +1 -1
  55. package/src/bmm-skills/4-implementation/wizz-retrospective/SKILL.md +1 -2
  56. package/src/bmm-skills/4-implementation/wizz-sprint-planning/SKILL.md +1 -1
  57. package/src/bmm-skills/4-implementation/wizz-sprint-status/SKILL.md +5 -6
  58. package/src/core-skills/_shared/handoff-protocol.md +9 -0
  59. package/src/core-skills/wizz-advanced-elicitation/SKILL.md +2 -2
  60. package/src/core-skills/wizz-brainstorming/SKILL.md +6 -6
  61. package/src/core-skills/wizz-brainstorming/references/converge.md +1 -1
  62. package/src/core-skills/wizz-brainstorming/references/finalize.md +1 -1
  63. package/src/core-skills/wizz-brainstorming/references/headless.md +3 -3
  64. package/src/core-skills/wizz-brainstorming/references/mode-autonomous.md +1 -1
  65. package/src/core-skills/wizz-customize/SKILL.md +13 -10
  66. package/src/core-skills/wizz-forge-idea/SKILL.md +6 -6
  67. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
  68. package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +17 -1
  69. package/src/core-skills/wizz-party-mode/SKILL.md +3 -3
  70. package/src/core-skills/wizz-party-mode/references/create-party.md +1 -1
  71. package/src/core-skills/wizz-party-mode/references/party-memory.md +2 -2
  72. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
  73. package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +17 -1
  74. package/src/core-skills/wizz-review-adversarial-general/SKILL.md +1 -1
  75. package/src/core-skills/wizz-spec/SKILL.md +5 -5
  76. package/src/modules/lowticket/README.md +6 -6
  77. package/src/modules/lowticket/module.yaml +4 -1
  78. package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/SKILL.md +4 -4
  79. package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/copy-das-paginas.md +1 -1
  80. package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/SKILL.md +1 -1
  81. package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/SKILL.md +3 -3
  82. package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/references/rubrica.md +1 -1
  83. package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/SKILL.md +4 -4
  84. package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/SKILL.md +3 -3
  85. package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/reguas.md +1 -1
  86. package/src/modules/wizz/_shared/communication-rules.md +1 -1
  87. package/src/modules/wizz/_shared/despacho-barato.md +9 -0
  88. package/src/modules/wizz/_shared/token-economy.md +7 -0
  89. package/src/modules/wizz/agents/wizz-ads/SKILL.md +1 -1
  90. package/src/modules/wizz/agents/wizz-ads/customize.toml +1 -2
  91. package/src/modules/wizz/agents/wizz-copy/SKILL.md +1 -1
  92. package/src/modules/wizz/agents/wizz-copy/customize.toml +1 -2
  93. package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
  94. package/src/modules/wizz/agents/wizz-designer/customize.toml +1 -2
  95. package/src/modules/wizz/agents/wizz-growth/SKILL.md +1 -1
  96. package/src/modules/wizz/agents/wizz-growth/customize.toml +1 -2
  97. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +5 -5
  98. package/src/modules/wizz/agents/wizz-maestro/customize.toml +4 -8
  99. package/src/modules/wizz/agents/wizz-memoria/SKILL.md +1 -1
  100. package/src/modules/wizz/agents/wizz-memoria/customize.toml +1 -2
  101. package/src/modules/wizz/agents/wizz-qa/SKILL.md +1 -1
  102. package/src/modules/wizz/agents/wizz-qa/customize.toml +1 -2
  103. package/src/modules/wizz/agents/wizz-seo/SKILL.md +1 -1
  104. package/src/modules/wizz/agents/wizz-seo/customize.toml +1 -2
  105. package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -1
  106. package/src/modules/wizz/agents/wizz-social/customize.toml +1 -2
  107. package/src/modules/wizz/module.yaml +3 -0
  108. package/src/modules/wizz/overrides/wizz-agent-analyst.toml +1 -2
  109. package/src/modules/wizz/overrides/wizz-agent-architect.toml +1 -2
  110. package/src/modules/wizz/overrides/wizz-agent-dev.toml +1 -2
  111. package/src/modules/wizz/overrides/wizz-agent-pm.toml +1 -2
  112. package/src/modules/wizz/overrides/wizz-agent-tech-writer.toml +1 -2
  113. package/src/modules/wizz/overrides/wizz-agent-ux-designer.toml +1 -2
  114. package/src/modules/wizz/subagents/codex/wizz-exec-haiku.toml +2 -0
  115. package/src/modules/wizz/subagents/codex/wizz-exec-opus.toml +2 -0
  116. package/src/modules/wizz/subagents/codex/wizz-exec-review.toml +2 -0
  117. package/src/modules/wizz/subagents/codex/wizz-exec-sonnet.toml +2 -0
  118. package/src/modules/wizz/subagents/gemini/wizz-exec-haiku.md +4 -1
  119. package/src/modules/wizz/subagents/gemini/wizz-exec-opus.md +4 -1
  120. package/src/modules/wizz/subagents/gemini/wizz-exec-review.md +4 -1
  121. package/src/modules/wizz/subagents/gemini/wizz-exec-sonnet.md +4 -1
  122. package/src/modules/wizz/subagents/opencode/wizz-exec-haiku.md +4 -1
  123. package/src/modules/wizz/subagents/opencode/wizz-exec-opus.md +4 -1
  124. package/src/modules/wizz/subagents/opencode/wizz-exec-review.md +4 -1
  125. package/src/modules/wizz/subagents/opencode/wizz-exec-sonnet.md +4 -1
  126. package/src/modules/wizz/subagents/spec.yaml +148 -0
  127. package/src/modules/wizz/subagents/wizz-exec-haiku.md +4 -1
  128. package/src/modules/wizz/subagents/wizz-exec-opus.md +4 -1
  129. package/src/modules/wizz/subagents/wizz-exec-review.md +4 -1
  130. package/src/modules/wizz/subagents/wizz-exec-sonnet.md +4 -1
  131. package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
  132. package/src/scripts/memlog.js +368 -0
  133. package/src/scripts/resolve_customization.py +124 -15
  134. package/src/skills-lib/_shared/complexity-signal.md +14 -0
  135. package/src/skills-lib/wizz-canvas-design/canvas-fonts/InstrumentSerif-OFL.txt +93 -0
  136. package/src/skills-lib/wizz-huashu-design/references/sfx-library.md +1 -1
  137. package/src/skills-lib/wizz-huashu-design/scripts/add-music.sh +1 -1
  138. package/src/skills-lib/wizz-huashu-design/scripts/mix-voiceover.sh +1 -1
  139. package/src/skills-lib/wizz-impeccable/LICENSE.txt +202 -0
  140. package/src/skills-lib/wizz-offer-forge/SKILL.md +1 -1
  141. package/src/skills-lib/wizz-react-components/LICENSE.txt +202 -0
  142. package/src/skills-lib/wizz-router/SKILL.md +2 -2
  143. package/src/skills-lib/wizz-supabase-postgres-best-practices/LICENSE +21 -0
  144. package/src/skills-lib/wizz-taste-skill/SKILL.md +1 -1
  145. package/src/skills-lib/wizz-tracking-audit/SKILL.md +1 -1
  146. package/tools/fetch-assets.mjs +70 -38
  147. package/tools/hooks/README.md +22 -0
  148. package/tools/hooks/rtk-rewrite.sh +280 -0
  149. package/tools/hooks/security-defensive-context.js +70 -0
  150. package/tools/hooks/session-rules.js +368 -0
  151. package/tools/hooks/wizz-decision-trace.js +389 -0
  152. package/tools/hooks/wizz-router-enforce.js +258 -0
  153. package/tools/installer/banner.js +1 -1
  154. package/tools/installer/commands/doctor.js +124 -0
  155. package/tools/installer/commands/fetch-assets.js +7 -8
  156. package/tools/installer/commands/install.js +1 -1
  157. package/tools/installer/commands/resolve.js +95 -0
  158. package/tools/installer/core/doctor.js +648 -0
  159. package/tools/installer/core/hash-cache.js +156 -0
  160. package/tools/installer/core/help-catalog.js +4 -1
  161. package/tools/installer/core/hooks-settings.js +178 -0
  162. package/tools/installer/core/installer.js +297 -15
  163. package/tools/installer/core/legacy-warnings.js +0 -4
  164. package/tools/installer/core/manifest-generator.js +121 -43
  165. package/tools/installer/core/manifest.js +36 -32
  166. package/tools/installer/core/module-config-writer.js +4 -1
  167. package/tools/installer/core/python-check.js +60 -63
  168. package/tools/installer/core/quick-update.js +76 -43
  169. package/tools/installer/core/resolved-config.js +168 -0
  170. package/tools/installer/core/resolved-customizations.js +736 -0
  171. package/tools/installer/core/resolved-includes.js +262 -0
  172. package/tools/installer/core/resolved-schema.js +148 -0
  173. package/tools/installer/core/user-file-preservation.js +40 -8
  174. package/tools/installer/core/wizz-dir.js +117 -0
  175. package/tools/installer/core/yaml-cache.js +105 -0
  176. package/tools/installer/fs-native.js +97 -6
  177. package/tools/installer/ide/_config-driven.js +12 -75
  178. package/tools/installer/ide/platform-codes.js +111 -16
  179. package/tools/installer/ide/platform-codes.yaml +45 -100
  180. package/tools/installer/ide/shared/path-utils.js +6 -208
  181. package/tools/installer/install-messages.yaml +4 -14
  182. package/tools/installer/modules/channel-plan.js +15 -2
  183. package/tools/installer/modules/config-collector.js +762 -0
  184. package/tools/installer/modules/config-values.js +352 -0
  185. package/tools/installer/modules/custom-module-manager.js +161 -28
  186. package/tools/installer/modules/env-vars.js +119 -0
  187. package/tools/installer/modules/external-manager.js +133 -60
  188. package/tools/installer/modules/git-cache-utils.js +125 -0
  189. package/tools/installer/modules/module-catalog.js +565 -0
  190. package/tools/installer/modules/module-installer.js +618 -0
  191. package/tools/installer/modules/module-interface.js +86 -0
  192. package/tools/installer/modules/official-modules.js +98 -2172
  193. package/tools/installer/modules/pin-spec.js +125 -0
  194. package/tools/installer/modules/plugin-resolver.js +4 -0
  195. package/tools/installer/modules/skills-lib.js +3 -6
  196. package/tools/installer/prompts.js +0 -3
  197. package/tools/installer/ui.js +89 -54
  198. package/tools/installer/wizz-cli.js +1 -1
  199. package/tools/lib/frontmatter.js +139 -0
  200. package/tools/lib/sync-targets.mjs +49 -0
  201. package/tools/lib/walk.js +93 -0
  202. package/wizz-modules.yaml +9 -0
  203. package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
  204. package/src/scripts/memlog.py +0 -233
  205. package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
  206. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
  207. package/src/scripts/tests/test_memlog.py +0 -306
  208. package/src/skills-lib/wizz-huashu-design/assets/showcases/INDEX.md +0 -115
  209. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.html +0 -235
  210. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.png +0 -0
  211. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.html +0 -229
  212. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.png +0 -0
  213. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.html +0 -288
  214. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.png +0 -0
  215. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.html +0 -503
  216. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.png +0 -0
  217. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.html +0 -600
  218. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.png +0 -0
  219. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.html +0 -670
  220. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.png +0 -0
  221. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.html +0 -382
  222. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.png +0 -0
  223. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.html +0 -536
  224. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.png +0 -0
  225. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.html +0 -497
  226. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.png +0 -0
  227. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.html +0 -385
  228. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.png +0 -0
  229. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.html +0 -422
  230. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.png +0 -0
  231. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.html +0 -499
  232. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.png +0 -0
  233. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.html +0 -562
  234. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.png +0 -0
  235. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.html +0 -548
  236. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.png +0 -0
  237. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.html +0 -696
  238. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.png +0 -0
  239. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.html +0 -372
  240. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.png +0 -0
  241. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.html +0 -460
  242. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.png +0 -0
  243. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.html +0 -494
  244. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.png +0 -0
  245. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.html +0 -367
  246. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.png +0 -0
  247. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.html +0 -368
  248. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.png +0 -0
  249. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.html +0 -459
  250. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.png +0 -0
  251. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.html +0 -493
  252. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.png +0 -0
  253. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.html +0 -556
  254. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.png +0 -0
  255. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.html +0 -604
  256. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.png +0 -0
  257. package/tools/installer/file-ops.js +0 -204
  258. package/tools/installer/ide/shared/skill-manifest.js +0 -57
  259. package/tools/installer/yaml-format.js +0 -245
  260. package/web-bundles/README.md +0 -46
  261. package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +0 -86
  262. package/web-bundles/brainstorming-coach/SKILL.md +0 -83
  263. package/web-bundles/brainstorming-coach/brain-methods.csv +0 -62
  264. package/web-bundles/bundles.json +0 -139
  265. package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +0 -88
  266. package/web-bundles/market-and-industry-research/SKILL.md +0 -59
  267. package/web-bundles/prd-coach/INSTRUCTIONS.md +0 -86
  268. package/web-bundles/prd-coach/SKILL.md +0 -101
  269. package/web-bundles/prd-coach/prd-template.md +0 -165
  270. package/web-bundles/prd-coach/prd-validation-checklist.md +0 -135
  271. package/web-bundles/prfaq-coach/INSTRUCTIONS.md +0 -86
  272. package/web-bundles/prfaq-coach/SKILL.md +0 -139
  273. package/web-bundles/product-brief-coach/INSTRUCTIONS.md +0 -86
  274. package/web-bundles/product-brief-coach/SKILL.md +0 -113
  275. package/web-bundles/ux-coach/INSTRUCTIONS.md +0 -92
  276. package/web-bundles/ux-coach/SKILL.md +0 -187
  277. package/web-bundles/ux-coach/ux-validation.md +0 -100
  278. /package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/mapa-do-funil.md +0 -0
  279. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/CATALOGO.md +0 -0
  280. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/01-principios.md +0 -0
  281. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/02-diagnostico-funil.md +0 -0
  282. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/03-campanha-bidcap.md +0 -0
  283. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04-criativos.md +0 -0
  284. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04a-criativos-ia.md +0 -0
  285. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04b-criativos-formatos.md +0 -0
  286. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/05-oferta.md +0 -0
  287. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06-pagina-vendas.md +0 -0
  288. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06a-instagram.md +0 -0
  289. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06b-player-vsl.md +0 -0
  290. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07-funil-upsell.md +0 -0
  291. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07a-copy-humanizacao.md +0 -0
  292. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/08-pesquisa-publico.md +0 -0
  293. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/09-especialista-coproducao.md +0 -0
  294. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/10-levantar-caixa.md +0 -0
  295. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/11-checklist-roi.md +0 -0
  296. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12-mineracao-ofertas.md +0 -0
  297. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12a-mineracao-veredito.md +0 -0
  298. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12b-fabrica-de-ofertas.md +0 -0
  299. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12c-anatomia-low-ticket.md +0 -0
  300. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/13-benchmarks-vsl.md +0 -0
  301. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/14-vocabulario.md +0 -0
  302. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/15-como-estender.md +0 -0
  303. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/17-rastreamento.md +0 -0
  304. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/18-copy-desejo-oferta.md +0 -0
  305. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/INDEX.md +0 -0
  306. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/guardrails.md +0 -0
  307. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/pontos-em-aberto.md +0 -0
  308. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/references/swipe-file.md +0 -0
  309. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/metas-e-medicao.md +0 -0
  310. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/padroes-de-codigo.md +0 -0
  311. /package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/formato-do-diagnostico.md +0 -0
@@ -0,0 +1,105 @@
1
+ // In-process (no disk) memoization for the YAML/CSV files the installer
2
+ // parses repeatedly in a single run: skills-registry.yaml (up to 4x per
3
+ // install), each module's module.yaml (up to 3x per module), and
4
+ // skill-manifest.csv (up to 3x per platform). See audit 2026-09-11,
5
+ // ACH-07/ACH-08/ACH-09: same file, same content, parsed several times with
6
+ // several different ad-hoc try/catch blocks.
7
+ //
8
+ // This centralizes both the memoization and the error handling: "file
9
+ // missing" always resolves to a normal value (null for YAML, [] for CSV)
10
+ // instead of throwing, while "file exists but fails to parse" always throws
11
+ // an Error whose message names the file path, so a bad manifest is
12
+ // diagnosable instead of surfacing as a generic parser error.
13
+ //
14
+ // Cache key is the absolute path; each entry also stores the mtimeMs/size
15
+ // seen at read time, mirroring the calculateFileHash cache in fs-native.js.
16
+ // A file the installer itself rewrites mid-run (e.g. regenerating
17
+ // manifest.yaml) is picked up fresh on the next read because its stat
18
+ // changes; `invalidate(path)` is available for callers that want to force
19
+ // that without waiting on stat granularity.
20
+
21
+ const path = require('node:path');
22
+ const yaml = require('yaml');
23
+ const csvParseSync = require('csv-parse/sync');
24
+ const fs = require('../fs-native');
25
+
26
+ const yamlCache = new Map(); // absPath -> { mtimeMs, size, data }
27
+ const csvCache = new Map(); // absPath -> { mtimeMs, size, data }
28
+
29
+ /**
30
+ * Read + parse a file, memoized per absolute path until its mtime/size
31
+ * changes. Returns `whenMissing` (not a Promise) when the file doesn't
32
+ * exist. Throws an Error naming the path when it exists but `parse` throws.
33
+ * @param {string} filePath
34
+ * @param {Map} cache
35
+ * @param {(content: string) => *} parse
36
+ * @param {string} kind - Used in error messages ("YAML" | "CSV")
37
+ * @param {*} whenMissing
38
+ * @returns {Promise<*>}
39
+ */
40
+ async function loadCachedFile(filePath, cache, parse, kind, whenMissing) {
41
+ const absPath = path.resolve(filePath);
42
+
43
+ if (!(await fs.pathExists(absPath))) {
44
+ cache.delete(absPath);
45
+ return whenMissing;
46
+ }
47
+
48
+ const stat = await fs.stat(absPath);
49
+ const cached = cache.get(absPath);
50
+ if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
51
+ return cached.data;
52
+ }
53
+
54
+ const content = await fs.readFile(absPath, 'utf8');
55
+ let data;
56
+ try {
57
+ data = parse(content);
58
+ } catch (error) {
59
+ throw new Error(`Failed to parse ${kind} at ${absPath}: ${error.message}`);
60
+ }
61
+
62
+ cache.set(absPath, { mtimeMs: stat.mtimeMs, size: stat.size, data });
63
+ return data;
64
+ }
65
+
66
+ /**
67
+ * Parse a YAML file, memoized per absolute path for the life of the process.
68
+ * @param {string} filePath
69
+ * @returns {Promise<Object|null>} Parsed document, or null when absent
70
+ */
71
+ async function loadYamlCached(filePath) {
72
+ return loadCachedFile(filePath, yamlCache, (content) => yaml.parse(content), 'YAML', null);
73
+ }
74
+
75
+ /**
76
+ * Parse a CSV file (columns:true, skip_empty_lines:true by default),
77
+ * memoized per absolute path for the life of the process.
78
+ * @param {string} filePath
79
+ * @param {Object} [csvOptions] - Overrides merged into the csv-parse options
80
+ * @returns {Promise<Array<Object>>} Parsed rows, or [] when absent
81
+ */
82
+ async function loadCsvCached(filePath, csvOptions = {}) {
83
+ return loadCachedFile(
84
+ filePath,
85
+ csvCache,
86
+ (content) => csvParseSync.parse(content, { columns: true, skip_empty_lines: true, ...csvOptions }),
87
+ 'CSV',
88
+ [],
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Force the next loadYamlCached/loadCsvCached call for this path to reread
94
+ * from disk, regardless of what mtime/size it reports. Call this after the
95
+ * installer itself rewrites a file it may need to read again in the same
96
+ * process.
97
+ * @param {string} filePath
98
+ */
99
+ function invalidate(filePath) {
100
+ const absPath = path.resolve(filePath);
101
+ yamlCache.delete(absPath);
102
+ csvCache.delete(absPath);
103
+ }
104
+
105
+ module.exports = { loadYamlCached, loadCsvCached, invalidate };
@@ -4,6 +4,7 @@
4
4
  const fsp = require('node:fs/promises');
5
5
  const fs = require('node:fs');
6
6
  const path = require('node:path');
7
+ const crypto = require('node:crypto');
7
8
 
8
9
  async function pathExists(p) {
9
10
  try {
@@ -22,6 +23,30 @@ async function remove(p) {
22
23
  await fsp.rm(p, { recursive: true, force: true });
23
24
  }
24
25
 
26
+ // Concurrency cap for batched I/O (directory copy, file hashing): enough
27
+ // parallelism to saturate typical disk I/O on a tree with hundreds/thousands
28
+ // of small files, without opening an unbounded number of file descriptors
29
+ // at once on very large trees.
30
+ const DEFAULT_BATCH_SIZE = 32;
31
+
32
+ // Runs `mapper` over `items` in concurrent batches of `batchSize`, returning
33
+ // results in the same order as `items`. Each batch is awaited fully before
34
+ // the next one starts, so the first rejection in a batch propagates and
35
+ // stops further batches — same "first error wins" semantics as the
36
+ // sequential loops this replaces, just with up to `batchSize` operations in
37
+ // flight at once instead of one at a time.
38
+ async function batchMap(items, mapper, batchSize = DEFAULT_BATCH_SIZE) {
39
+ const results = Array.from({ length: items.length });
40
+ for (let start = 0; start < items.length; start += batchSize) {
41
+ const batch = items.slice(start, start + batchSize);
42
+ const batchResults = await Promise.all(batch.map((item, i) => mapper(item, start + i)));
43
+ for (const [i, batchResult] of batchResults.entries()) {
44
+ results[start + i] = batchResult;
45
+ }
46
+ }
47
+ return results;
48
+ }
49
+
25
50
  async function copy(src, dest, options = {}) {
26
51
  const filterFn = options.filter;
27
52
  const overwrite = options.overwrite !== false;
@@ -40,6 +65,16 @@ async function copy(src, dest, options = {}) {
40
65
  }
41
66
  }
42
67
  await fsp.copyFile(src, dest);
68
+ // Preserve the source mtime: node's copyFile doesn't, and the
69
+ // persistent hash cache (hash-cache.json, audit item 23) keys on
70
+ // size+mtime — a freshly stamped mtime would make every re-copied
71
+ // file look changed on the next update and invalidate the cache.
72
+ // Best-effort: mtime is an optimization, not a correctness contract.
73
+ try {
74
+ await fsp.utimes(dest, srcStat.atime, srcStat.mtime);
75
+ } catch {
76
+ // ignore — cache miss on the next run costs a hash, nothing more
77
+ }
43
78
  return;
44
79
  }
45
80
 
@@ -47,9 +82,7 @@ async function copy(src, dest, options = {}) {
47
82
  if (filterFn && !(await filterFn(src, dest))) return;
48
83
  await fsp.mkdir(dest, { recursive: true });
49
84
  const entries = await fsp.readdir(src, { withFileTypes: true });
50
- for (const entry of entries) {
51
- await copy(path.join(src, entry.name), path.join(dest, entry.name), options);
52
- }
85
+ await batchMap(entries, (entry) => copy(path.join(src, entry.name), path.join(dest, entry.name), options));
53
86
  }
54
87
  }
55
88
 
@@ -70,22 +103,77 @@ function readJsonSync(p) {
70
103
  return JSON.parse(fs.readFileSync(p, 'utf8'));
71
104
  }
72
105
 
106
+ // Async sibling of readJsonSync. This used to exist as an fs-extra helper;
107
+ // when fs-native replaced fs-extra (a6d075bd) it was dropped even though
108
+ // custom-module-manager.js kept calling `fs.readJson(...)` — every one of
109
+ // those calls threw TypeError, silently swallowed by surrounding
110
+ // try/catch blocks, so cache-version checks there never actually read the
111
+ // recorded metadata. Re-added (item 8c, auditoria 2026-09-11) because the
112
+ // TAG@SHA early-return depends on reading .wizz-source.json.
113
+ async function readJson(p) {
114
+ return JSON.parse(await fsp.readFile(p, 'utf8'));
115
+ }
116
+
117
+ // Central atomic-write primitive: write to a sibling `<dest>.tmp-<pid>-<rand>`
118
+ // in the same directory (so the rename below is same-filesystem, never
119
+ // `os.tmpdir()`) and `rename` it over the destination once the write
120
+ // finishes — a reader never observes a partially written file. On any
121
+ // failure the tmp file is removed (best-effort) and the error is rethrown;
122
+ // the previous destination content, if any, is left untouched. Every write
123
+ // through fs-native's `writeJson`/`writeFile` goes through this, so it's a
124
+ // one-time fix for every config/manifest writer in the installer.
125
+ async function writeFileAtomic(p, data, options) {
126
+ const tmpPath = `${p}.tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
127
+ try {
128
+ await fsp.writeFile(tmpPath, data, options);
129
+ await fsp.rename(tmpPath, p);
130
+ } catch (error) {
131
+ await fsp.rm(tmpPath, { force: true }).catch(() => {});
132
+ throw error;
133
+ }
134
+ }
135
+
73
136
  async function writeJson(p, data, options = {}) {
74
137
  const spaces = options.spaces ?? 2;
75
- await fsp.writeFile(p, JSON.stringify(data, null, spaces) + '\n', 'utf8');
138
+ await writeFileAtomic(p, JSON.stringify(data, null, spaces) + '\n', 'utf8');
139
+ }
140
+
141
+ // Single canonical SHA256-of-file-content implementation (previously
142
+ // duplicated verbatim in core/manifest.js and core/manifest-generator.js,
143
+ // with a third dead copy in file-ops.js). Memoized in-process by
144
+ // `path:size:mtimeMs` — no disk cache — so the same unchanged file hashed
145
+ // twice in one install/update run (e.g. once to detect user modifications,
146
+ // once to write the new files-manifest.csv) only reads/hashes it once.
147
+ // Returns `null` (never throws) when the file can't be read; callers that
148
+ // need a different falsy sentinel adapt it locally.
149
+ const fileHashCache = new Map();
150
+
151
+ async function calculateFileHash(filePath) {
152
+ try {
153
+ const stat = await fsp.stat(filePath);
154
+ const cacheKey = `${filePath}:${stat.size}:${stat.mtimeMs}`;
155
+ if (fileHashCache.has(cacheKey)) {
156
+ return fileHashCache.get(cacheKey);
157
+ }
158
+ const content = await fsp.readFile(filePath);
159
+ const hash = crypto.createHash('sha256').update(content).digest('hex');
160
+ fileHashCache.set(cacheKey, hash);
161
+ return hash;
162
+ } catch {
163
+ return null;
164
+ }
76
165
  }
77
166
 
78
167
  module.exports = {
79
168
  // Native async (node:fs/promises)
80
169
  readFile: fsp.readFile,
81
- writeFile: fsp.writeFile,
170
+ writeFile: writeFileAtomic,
82
171
  stat: fsp.stat,
83
172
  readdir: fsp.readdir,
84
173
  access: fsp.access,
85
174
  realpath: fsp.realpath,
86
175
  rename: fsp.rename,
87
176
  rmdir: fsp.rmdir,
88
- unlink: fsp.unlink,
89
177
  chmod: fsp.chmod,
90
178
  mkdir: fsp.mkdir,
91
179
  mkdtemp: fsp.mkdtemp,
@@ -99,7 +187,10 @@ module.exports = {
99
187
  copy,
100
188
  move,
101
189
  readJsonSync,
190
+ readJson,
102
191
  writeJson,
192
+ calculateFileHash,
193
+ batchMap,
103
194
 
104
195
  // Sync methods from core node:fs
105
196
  existsSync: fs.existsSync.bind(fs),
@@ -2,8 +2,9 @@ const path = require('node:path');
2
2
  const fs = require('../fs-native');
3
3
  const yaml = require('yaml');
4
4
  const prompts = require('../prompts');
5
- const csv = require('csv-parse/sync');
5
+ const { loadCsvCached } = require('../core/yaml-cache');
6
6
  const { WIZZ_FOLDER_NAME } = require('./shared/path-utils');
7
+ const { findExistingWizzDir } = require('../core/wizz-dir');
7
8
  const { getInstalledCanonicalIds, isWizzOwnedEntry } = require('./shared/installed-skills');
8
9
 
9
10
  // Reserved OpenCode slash commands. A skill whose canonicalId collides with
@@ -187,25 +188,6 @@ class ConfigDrivenIdeSetup {
187
188
  * @returns {Promise<Object>} Setup result
188
189
  */
189
190
  async setup(projectDir, wizzDir, options = {}) {
190
- // Check for WIZZ files in ancestor directories that would cause duplicates
191
- if (this.installerConfig?.ancestor_conflict_check) {
192
- const conflict = await this.findAncestorConflict(projectDir);
193
- if (conflict) {
194
- await prompts.log.error(
195
- `Found existing WIZZ skills in ancestor installation: ${conflict}\n` +
196
- ` ${this.name} inherits skills from parent directories, so this would cause duplicates.\n` +
197
- ` Please remove the WIZZ files from that directory first:\n` +
198
- ` rm -rf "${conflict}"/wizz*`,
199
- );
200
- return {
201
- success: false,
202
- reason: 'ancestor-conflict',
203
- error: `Ancestor conflict: ${conflict}`,
204
- conflictDir: conflict,
205
- };
206
- }
207
- }
208
-
209
191
  if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`);
210
192
 
211
193
  // Clean up any old WIZZ installation first
@@ -320,8 +302,7 @@ class ConfigDrivenIdeSetup {
320
302
  const targetDir = config.target_dir;
321
303
  const filter = config.commands_filter || null;
322
304
 
323
- const csvContent = await fs.readFile(csvPath, 'utf8');
324
- const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true });
305
+ const records = await loadCsvCached(csvPath);
325
306
 
326
307
  for (const record of records) {
327
308
  const canonicalId = record.canonicalId;
@@ -428,11 +409,7 @@ class ConfigDrivenIdeSetup {
428
409
 
429
410
  if (!(await fs.pathExists(csvPath))) return 0;
430
411
 
431
- const csvContent = await fs.readFile(csvPath, 'utf8');
432
- const records = csv.parse(csvContent, {
433
- columns: true,
434
- skip_empty_lines: true,
435
- });
412
+ const records = await loadCsvCached(csvPath);
436
413
 
437
414
  let count = 0;
438
415
 
@@ -651,13 +628,13 @@ class ConfigDrivenIdeSetup {
651
628
  }
652
629
 
653
630
  /**
654
- * Find the _wizz directory in a project
631
+ * Find the _wizz directory in a project (delegated to core/wizz-dir.js, the
632
+ * single owner of `_wizz` resolution — item 20, auditoria 2026-09-11).
655
633
  * @param {string} projectDir - Project directory
656
634
  * @returns {string|null} Path to wizz dir or null
657
635
  */
658
636
  async _findWizzDir(projectDir) {
659
- const wizzDir = path.join(projectDir, WIZZ_FOLDER_NAME);
660
- return (await fs.pathExists(wizzDir)) ? wizzDir : null;
637
+ return findExistingWizzDir(projectDir);
661
638
  }
662
639
 
663
640
  /**
@@ -672,13 +649,10 @@ class ConfigDrivenIdeSetup {
672
649
  // Also add all currently installed skills from skill-manifest.csv
673
650
  const csvPath = path.join(wizzDir, '_config', 'skill-manifest.csv');
674
651
  try {
675
- if (await fs.pathExists(csvPath)) {
676
- const content = await fs.readFile(csvPath, 'utf8');
677
- const records = csv.parse(content, { columns: true, skip_empty_lines: true });
678
- for (const record of records) {
679
- if (record.canonicalId) {
680
- removals.add(record.canonicalId);
681
- }
652
+ const records = await loadCsvCached(csvPath);
653
+ for (const record of records) {
654
+ if (record.canonicalId) {
655
+ removals.add(record.canonicalId);
682
656
  }
683
657
  }
684
658
  } catch {
@@ -857,10 +831,8 @@ class ConfigDrivenIdeSetup {
857
831
  const ids = new Set();
858
832
  if (!wizzDir) return ids;
859
833
  const csvPath = path.join(wizzDir, '_config', 'skill-manifest.csv');
860
- if (!(await fs.pathExists(csvPath))) return ids;
861
834
  try {
862
- const content = await fs.readFile(csvPath, 'utf8');
863
- const records = csv.parse(content, { columns: true, skip_empty_lines: true });
835
+ const records = await loadCsvCached(csvPath);
864
836
  for (const record of records) {
865
837
  if (record.canonicalId) ids.add(record.canonicalId);
866
838
  }
@@ -1053,41 +1025,6 @@ class ConfigDrivenIdeSetup {
1053
1025
  }
1054
1026
  }
1055
1027
  }
1056
-
1057
- /**
1058
- * Check ancestor directories for existing WIZZ files in the same target_dir.
1059
- * IDEs like Claude Code inherit commands from parent directories, so an existing
1060
- * installation in an ancestor would cause duplicate commands.
1061
- * @param {string} projectDir - Project directory being installed to
1062
- * @returns {Promise<string|null>} Path to conflicting directory, or null if clean
1063
- */
1064
- async findAncestorConflict(projectDir) {
1065
- const targetDir = this.installerConfig?.target_dir;
1066
- if (!targetDir) return null;
1067
-
1068
- const resolvedProject = await fs.realpath(path.resolve(projectDir));
1069
- let current = path.dirname(resolvedProject);
1070
- const root = path.parse(current).root;
1071
-
1072
- while (current !== root && current.length > root.length) {
1073
- const candidatePath = path.join(current, targetDir);
1074
- try {
1075
- if (await fs.pathExists(candidatePath)) {
1076
- const entries = await fs.readdir(candidatePath);
1077
- const ancestorWizzDir = await this._findWizzDir(current);
1078
- const canonicalIds = await getInstalledCanonicalIds(ancestorWizzDir);
1079
- if (entries.some((e) => isWizzOwnedEntry(e, canonicalIds))) {
1080
- return candidatePath;
1081
- }
1082
- }
1083
- } catch {
1084
- // Can't read directory — skip
1085
- }
1086
- current = path.dirname(current);
1087
- }
1088
-
1089
- return null;
1090
- }
1091
1028
  }
1092
1029
 
1093
1030
  module.exports = { ConfigDrivenIdeSetup };
@@ -6,6 +6,67 @@ const PLATFORM_CODES_PATH = path.join(__dirname, 'platform-codes.yaml');
6
6
 
7
7
  let _cachedPlatformCodes = null;
8
8
 
9
+ /**
10
+ * Shallow-merge a platform's `installer` block onto the shared defaults.
11
+ * The entry's own keys win; any key whose merged value is `null` is dropped
12
+ * (this is how an entry declares "no global_target_dir at all", e.g. ona,
13
+ * replit, trae). Never mutates either input.
14
+ * @param {Object} [defaultInstaller] - `defaults.installer` from the YAML
15
+ * @param {Object} entryInstaller - the platform's own `installer` block
16
+ * @returns {Object} New merged installer object
17
+ */
18
+ function mergeInstaller(defaultInstaller, entryInstaller) {
19
+ const merged = { ...defaultInstaller, ...entryInstaller };
20
+ const result = {};
21
+ for (const [key, value] of Object.entries(merged)) {
22
+ if (value !== null) {
23
+ result[key] = value;
24
+ }
25
+ }
26
+ return result;
27
+ }
28
+
29
+ /**
30
+ * Apply `defaults` (preferred + installer.target_dir/global_target_dir) to
31
+ * one platform entry. A platform with no `installer:` block at all is
32
+ * returned untouched (defaults only apply to declared installer sections).
33
+ * @param {Object} defaults - top-level `defaults` from the YAML
34
+ * @param {Object} entry - one entry from `platforms`
35
+ * @returns {Object} New platform entry with defaults applied
36
+ */
37
+ function applyDefaultsToEntry(defaults, entry) {
38
+ const { name, preferred, installer, ...rest } = entry;
39
+
40
+ // Rebuild in the pre-`defaults:` key order (name, preferred, installer,
41
+ // then anything else) so a plain object dump matches byte-for-byte.
42
+ const merged = { name };
43
+ merged.preferred = preferred === undefined ? defaults?.preferred : preferred;
44
+ if (installer) {
45
+ merged.installer = mergeInstaller(defaults?.installer, installer);
46
+ }
47
+ return { ...merged, ...rest };
48
+ }
49
+
50
+ /**
51
+ * Apply `defaults` across every entry in `platforms`, returning the config
52
+ * in the old shape (no top-level `defaults` key, consumers never read it).
53
+ * @param {Object} rawConfig - parsed platform-codes.yaml
54
+ * @returns {Object} New config object, same shape as before the `defaults:` block existed
55
+ */
56
+ function applyDefaults(rawConfig) {
57
+ const { defaults, platforms, ...rest } = rawConfig || {};
58
+ if (!defaults) {
59
+ return rawConfig;
60
+ }
61
+
62
+ const mergedPlatforms = {};
63
+ for (const [code, entry] of Object.entries(platforms || {})) {
64
+ mergedPlatforms[code] = applyDefaultsToEntry(defaults, entry);
65
+ }
66
+
67
+ return { ...rest, platforms: mergedPlatforms };
68
+ }
69
+
9
70
  /**
10
71
  * Load the platform codes configuration from YAML
11
72
  * @returns {Object} Platform codes configuration
@@ -20,7 +81,7 @@ async function loadPlatformCodes() {
20
81
  }
21
82
 
22
83
  const content = await fs.readFile(PLATFORM_CODES_PATH, 'utf8');
23
- _cachedPlatformCodes = yaml.parse(content);
84
+ _cachedPlatformCodes = applyDefaults(yaml.parse(content));
24
85
  return _cachedPlatformCodes;
25
86
  }
26
87
 
@@ -31,26 +92,58 @@ function clearCache() {
31
92
  _cachedPlatformCodes = null;
32
93
  }
33
94
 
95
+ /**
96
+ * The installable platforms, exactly as IdeManager exposes them: a platform is
97
+ * installable when it declares an `installer` section (IdeManager only builds
98
+ * handlers for those) and is not suspended (getAvailableIdes skips those).
99
+ * Same shape, same order — preferred first, then alphabetical by name.
100
+ *
101
+ * Kept here, in the leaf, so `formatPlatformList` does not have to reach back
102
+ * into `./manager` — that back-reference was the ide/manager ↔ ide/platform-codes
103
+ * require cycle (item 20, auditoria 2026-09-11). Test Suite 42 of
104
+ * test-installation-components.js asserts this list and the IDs `--tools`
105
+ * accepts stay in agreement.
106
+ * @param {Object} platformConfig - Parsed platform-codes.yaml
107
+ * @returns {Array<{id: string, name: string, targetDir: string, preferred: boolean}>}
108
+ */
109
+ function listInstallablePlatforms(platformConfig) {
110
+ const entries = [];
111
+
112
+ for (const [platformCode, platformInfo] of Object.entries(platformConfig?.platforms || {})) {
113
+ // Skip if no installer config (platform may not need installation)
114
+ if (!platformInfo?.installer) continue;
115
+ // Skip suspended platforms (e.g., IDE doesn't support skills yet)
116
+ if (platformInfo.suspended) continue;
117
+
118
+ const name = platformInfo.name || platformCode;
119
+ // Filter out invalid entries (undefined name, empty key, etc.)
120
+ if (!platformCode || !name || typeof platformCode !== 'string' || typeof name !== 'string') continue;
121
+
122
+ entries.push({
123
+ id: platformCode,
124
+ name,
125
+ targetDir: platformInfo.installer.target_dir || '',
126
+ preferred: platformInfo.preferred || false,
127
+ });
128
+ }
129
+
130
+ // Sort: preferred first, then alphabetical
131
+ entries.sort((a, b) => {
132
+ if (a.preferred && !b.preferred) return -1;
133
+ if (!a.preferred && b.preferred) return 1;
134
+ return a.name.localeCompare(b.name);
135
+ });
136
+
137
+ return entries;
138
+ }
139
+
34
140
  /**
35
141
  * Format the installable platform list for human-readable output (used by --list-tools).
36
- * Sourced from IdeManager so this view matches what --tools accepts at install time
37
- * (suspended platforms excluded).
142
+ * Matches what --tools accepts at install time (suspended platforms excluded).
38
143
  * @returns {Promise<string>} Formatted multi-line string with id, name, target_dir, preferred flag.
39
144
  */
40
145
  async function formatPlatformList() {
41
- const { IdeManager } = require('./manager');
42
- const ideManager = new IdeManager();
43
- await ideManager.ensureInitialized();
44
-
45
- const entries = ideManager.getAvailableIdes().map((ide) => {
46
- const handler = ideManager.handlers.get(ide.value);
47
- return {
48
- id: ide.value,
49
- name: ide.name,
50
- targetDir: handler?.installerConfig?.target_dir || '',
51
- preferred: ide.preferred,
52
- };
53
- });
146
+ const entries = listInstallablePlatforms(await loadPlatformCodes());
54
147
 
55
148
  const idWidth = Math.max(...entries.map((e) => e.id.length), 'ID'.length);
56
149
  const nameWidth = Math.max(...entries.map((e) => e.name.length), 'Name'.length);
@@ -76,5 +169,7 @@ async function formatPlatformList() {
76
169
  module.exports = {
77
170
  loadPlatformCodes,
78
171
  clearCache,
172
+ applyDefaults,
173
+ listInstallablePlatforms,
79
174
  formatPlatformList,
80
175
  };