wizz-method 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/LICENSE +60 -30
  2. package/README.md +80 -31
  3. package/SECURITY.md +24 -0
  4. package/THIRD-PARTY-NOTICES.md +136 -0
  5. package/package.json +31 -17
  6. package/skills-registry.yaml +128 -7
  7. package/src/bmm-skills/1-analysis/research/wizz-domain-research/SKILL.md +3 -4
  8. package/src/bmm-skills/1-analysis/research/wizz-domain-research/domain-steps/step-06-research-synthesis.md +1 -1
  9. package/src/bmm-skills/1-analysis/research/wizz-market-research/SKILL.md +3 -4
  10. package/src/bmm-skills/1-analysis/research/wizz-market-research/steps/step-06-research-completion.md +1 -1
  11. package/src/bmm-skills/1-analysis/research/wizz-technical-research/SKILL.md +3 -4
  12. package/src/bmm-skills/1-analysis/research/wizz-technical-research/technical-steps/step-06-research-synthesis.md +1 -1
  13. package/src/bmm-skills/1-analysis/wizz-agent-analyst/SKILL.md +3 -4
  14. package/src/bmm-skills/1-analysis/wizz-agent-tech-writer/SKILL.md +3 -4
  15. package/src/bmm-skills/1-analysis/wizz-document-project/SKILL.md +3 -4
  16. package/src/bmm-skills/1-analysis/wizz-document-project/workflows/deep-dive-instructions.md +1 -1
  17. package/src/bmm-skills/1-analysis/wizz-document-project/workflows/full-scan-instructions.md +1 -1
  18. package/src/bmm-skills/1-analysis/wizz-prfaq/SKILL.md +3 -4
  19. package/src/bmm-skills/1-analysis/wizz-prfaq/references/verdict.md +1 -1
  20. package/src/bmm-skills/1-analysis/wizz-product-brief/SKILL.md +1 -1
  21. package/src/bmm-skills/2-plan-workflows/wizz-agent-pm/SKILL.md +3 -4
  22. package/src/bmm-skills/2-plan-workflows/wizz-agent-ux-designer/SKILL.md +3 -4
  23. package/src/bmm-skills/2-plan-workflows/wizz-create-prd/SKILL.md +1 -1
  24. package/src/bmm-skills/2-plan-workflows/wizz-edit-prd/SKILL.md +1 -1
  25. package/src/bmm-skills/2-plan-workflows/wizz-prd/SKILL.md +1 -1
  26. package/src/bmm-skills/2-plan-workflows/wizz-ux/SKILL.md +1 -1
  27. package/src/bmm-skills/2-plan-workflows/wizz-validate-prd/SKILL.md +1 -1
  28. package/src/bmm-skills/3-solutioning/wizz-agent-architect/SKILL.md +3 -4
  29. package/src/bmm-skills/3-solutioning/wizz-architecture/SKILL.md +9 -9
  30. package/src/bmm-skills/3-solutioning/wizz-architecture/references/headless.md +1 -1
  31. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/SKILL.md +3 -4
  32. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/steps/step-06-final-assessment.md +1 -1
  33. package/src/bmm-skills/3-solutioning/wizz-create-architecture/SKILL.md +1 -1
  34. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/SKILL.md +3 -4
  35. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
  36. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/SKILL.md +3 -4
  37. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/steps/step-03-complete.md +1 -1
  38. package/src/bmm-skills/4-implementation/wizz-agent-dev/SKILL.md +3 -4
  39. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/SKILL.md +3 -4
  40. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/step-05-wrapup.md +1 -1
  41. package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +3 -4
  42. package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +3 -8
  43. package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/blind-hunter.md +20 -0
  44. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +1 -1
  45. package/src/bmm-skills/4-implementation/wizz-correct-course/SKILL.md +4 -5
  46. package/src/bmm-skills/4-implementation/wizz-create-story/SKILL.md +4 -5
  47. package/src/bmm-skills/4-implementation/wizz-dev-story/SKILL.md +4 -5
  48. package/src/bmm-skills/4-implementation/wizz-investigate/SKILL.md +1 -1
  49. package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/SKILL.md +4 -5
  50. package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +4 -5
  51. package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +10 -22
  52. package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/blind-hunter.md +20 -0
  53. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-05-present.md +1 -1
  54. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-oneshot.md +1 -1
  55. package/src/bmm-skills/4-implementation/wizz-retrospective/SKILL.md +1 -2
  56. package/src/bmm-skills/4-implementation/wizz-sprint-planning/SKILL.md +1 -1
  57. package/src/bmm-skills/4-implementation/wizz-sprint-status/SKILL.md +5 -6
  58. package/src/core-skills/_shared/handoff-protocol.md +9 -0
  59. package/src/core-skills/wizz-advanced-elicitation/SKILL.md +2 -2
  60. package/src/core-skills/wizz-brainstorming/SKILL.md +6 -6
  61. package/src/core-skills/wizz-brainstorming/references/converge.md +1 -1
  62. package/src/core-skills/wizz-brainstorming/references/finalize.md +1 -1
  63. package/src/core-skills/wizz-brainstorming/references/headless.md +3 -3
  64. package/src/core-skills/wizz-brainstorming/references/mode-autonomous.md +1 -1
  65. package/src/core-skills/wizz-customize/SKILL.md +13 -10
  66. package/src/core-skills/wizz-forge-idea/SKILL.md +6 -6
  67. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
  68. package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +17 -1
  69. package/src/core-skills/wizz-party-mode/SKILL.md +3 -3
  70. package/src/core-skills/wizz-party-mode/references/create-party.md +1 -1
  71. package/src/core-skills/wizz-party-mode/references/party-memory.md +2 -2
  72. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
  73. package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +17 -1
  74. package/src/core-skills/wizz-review-adversarial-general/SKILL.md +1 -1
  75. package/src/core-skills/wizz-spec/SKILL.md +5 -5
  76. package/src/modules/lowticket/README.md +6 -6
  77. package/src/modules/lowticket/module.yaml +4 -1
  78. package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/SKILL.md +4 -4
  79. package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/copy-das-paginas.md +1 -1
  80. package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/SKILL.md +1 -1
  81. package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/SKILL.md +3 -3
  82. package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/references/rubrica.md +1 -1
  83. package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/SKILL.md +4 -4
  84. package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/SKILL.md +3 -3
  85. package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/reguas.md +1 -1
  86. package/src/modules/wizz/_shared/communication-rules.md +1 -1
  87. package/src/modules/wizz/_shared/despacho-barato.md +9 -0
  88. package/src/modules/wizz/_shared/token-economy.md +7 -0
  89. package/src/modules/wizz/agents/wizz-ads/SKILL.md +1 -1
  90. package/src/modules/wizz/agents/wizz-ads/customize.toml +1 -2
  91. package/src/modules/wizz/agents/wizz-copy/SKILL.md +1 -1
  92. package/src/modules/wizz/agents/wizz-copy/customize.toml +1 -2
  93. package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
  94. package/src/modules/wizz/agents/wizz-designer/customize.toml +1 -2
  95. package/src/modules/wizz/agents/wizz-growth/SKILL.md +1 -1
  96. package/src/modules/wizz/agents/wizz-growth/customize.toml +1 -2
  97. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +5 -5
  98. package/src/modules/wizz/agents/wizz-maestro/customize.toml +4 -8
  99. package/src/modules/wizz/agents/wizz-memoria/SKILL.md +1 -1
  100. package/src/modules/wizz/agents/wizz-memoria/customize.toml +1 -2
  101. package/src/modules/wizz/agents/wizz-qa/SKILL.md +1 -1
  102. package/src/modules/wizz/agents/wizz-qa/customize.toml +1 -2
  103. package/src/modules/wizz/agents/wizz-seo/SKILL.md +1 -1
  104. package/src/modules/wizz/agents/wizz-seo/customize.toml +1 -2
  105. package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -1
  106. package/src/modules/wizz/agents/wizz-social/customize.toml +1 -2
  107. package/src/modules/wizz/module.yaml +3 -0
  108. package/src/modules/wizz/overrides/wizz-agent-analyst.toml +1 -2
  109. package/src/modules/wizz/overrides/wizz-agent-architect.toml +1 -2
  110. package/src/modules/wizz/overrides/wizz-agent-dev.toml +1 -2
  111. package/src/modules/wizz/overrides/wizz-agent-pm.toml +1 -2
  112. package/src/modules/wizz/overrides/wizz-agent-tech-writer.toml +1 -2
  113. package/src/modules/wizz/overrides/wizz-agent-ux-designer.toml +1 -2
  114. package/src/modules/wizz/subagents/codex/wizz-exec-haiku.toml +2 -0
  115. package/src/modules/wizz/subagents/codex/wizz-exec-opus.toml +2 -0
  116. package/src/modules/wizz/subagents/codex/wizz-exec-review.toml +2 -0
  117. package/src/modules/wizz/subagents/codex/wizz-exec-sonnet.toml +2 -0
  118. package/src/modules/wizz/subagents/gemini/wizz-exec-haiku.md +4 -1
  119. package/src/modules/wizz/subagents/gemini/wizz-exec-opus.md +4 -1
  120. package/src/modules/wizz/subagents/gemini/wizz-exec-review.md +4 -1
  121. package/src/modules/wizz/subagents/gemini/wizz-exec-sonnet.md +4 -1
  122. package/src/modules/wizz/subagents/opencode/wizz-exec-haiku.md +4 -1
  123. package/src/modules/wizz/subagents/opencode/wizz-exec-opus.md +4 -1
  124. package/src/modules/wizz/subagents/opencode/wizz-exec-review.md +4 -1
  125. package/src/modules/wizz/subagents/opencode/wizz-exec-sonnet.md +4 -1
  126. package/src/modules/wizz/subagents/spec.yaml +148 -0
  127. package/src/modules/wizz/subagents/wizz-exec-haiku.md +4 -1
  128. package/src/modules/wizz/subagents/wizz-exec-opus.md +4 -1
  129. package/src/modules/wizz/subagents/wizz-exec-review.md +4 -1
  130. package/src/modules/wizz/subagents/wizz-exec-sonnet.md +4 -1
  131. package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
  132. package/src/scripts/memlog.js +368 -0
  133. package/src/scripts/resolve_customization.py +124 -15
  134. package/src/skills-lib/_shared/complexity-signal.md +14 -0
  135. package/src/skills-lib/wizz-canvas-design/canvas-fonts/InstrumentSerif-OFL.txt +93 -0
  136. package/src/skills-lib/wizz-huashu-design/references/sfx-library.md +1 -1
  137. package/src/skills-lib/wizz-huashu-design/scripts/add-music.sh +1 -1
  138. package/src/skills-lib/wizz-huashu-design/scripts/mix-voiceover.sh +1 -1
  139. package/src/skills-lib/wizz-impeccable/LICENSE.txt +202 -0
  140. package/src/skills-lib/wizz-offer-forge/SKILL.md +1 -1
  141. package/src/skills-lib/wizz-react-components/LICENSE.txt +202 -0
  142. package/src/skills-lib/wizz-router/SKILL.md +2 -2
  143. package/src/skills-lib/wizz-supabase-postgres-best-practices/LICENSE +21 -0
  144. package/src/skills-lib/wizz-taste-skill/SKILL.md +1 -1
  145. package/src/skills-lib/wizz-tracking-audit/SKILL.md +1 -1
  146. package/tools/fetch-assets.mjs +62 -37
  147. package/tools/hooks/README.md +22 -0
  148. package/tools/hooks/rtk-rewrite.sh +280 -0
  149. package/tools/hooks/security-defensive-context.js +70 -0
  150. package/tools/hooks/session-rules.js +368 -0
  151. package/tools/hooks/wizz-decision-trace.js +389 -0
  152. package/tools/hooks/wizz-router-enforce.js +258 -0
  153. package/tools/installer/banner.js +1 -1
  154. package/tools/installer/commands/doctor.js +124 -0
  155. package/tools/installer/commands/install.js +1 -1
  156. package/tools/installer/commands/resolve.js +95 -0
  157. package/tools/installer/core/doctor.js +648 -0
  158. package/tools/installer/core/hash-cache.js +156 -0
  159. package/tools/installer/core/help-catalog.js +4 -1
  160. package/tools/installer/core/hooks-settings.js +178 -0
  161. package/tools/installer/core/installer.js +297 -15
  162. package/tools/installer/core/legacy-warnings.js +0 -4
  163. package/tools/installer/core/manifest-generator.js +121 -43
  164. package/tools/installer/core/manifest.js +36 -32
  165. package/tools/installer/core/module-config-writer.js +4 -1
  166. package/tools/installer/core/python-check.js +60 -63
  167. package/tools/installer/core/quick-update.js +76 -43
  168. package/tools/installer/core/resolved-config.js +168 -0
  169. package/tools/installer/core/resolved-customizations.js +736 -0
  170. package/tools/installer/core/resolved-includes.js +262 -0
  171. package/tools/installer/core/resolved-schema.js +148 -0
  172. package/tools/installer/core/user-file-preservation.js +40 -8
  173. package/tools/installer/core/wizz-dir.js +117 -0
  174. package/tools/installer/core/yaml-cache.js +105 -0
  175. package/tools/installer/fs-native.js +97 -6
  176. package/tools/installer/ide/_config-driven.js +12 -75
  177. package/tools/installer/ide/platform-codes.js +111 -16
  178. package/tools/installer/ide/platform-codes.yaml +45 -100
  179. package/tools/installer/ide/shared/path-utils.js +6 -208
  180. package/tools/installer/install-messages.yaml +4 -14
  181. package/tools/installer/modules/channel-plan.js +15 -2
  182. package/tools/installer/modules/config-collector.js +762 -0
  183. package/tools/installer/modules/config-values.js +352 -0
  184. package/tools/installer/modules/custom-module-manager.js +161 -28
  185. package/tools/installer/modules/env-vars.js +119 -0
  186. package/tools/installer/modules/external-manager.js +133 -60
  187. package/tools/installer/modules/git-cache-utils.js +125 -0
  188. package/tools/installer/modules/module-catalog.js +565 -0
  189. package/tools/installer/modules/module-installer.js +618 -0
  190. package/tools/installer/modules/module-interface.js +86 -0
  191. package/tools/installer/modules/official-modules.js +98 -2172
  192. package/tools/installer/modules/pin-spec.js +125 -0
  193. package/tools/installer/modules/plugin-resolver.js +4 -0
  194. package/tools/installer/modules/skills-lib.js +3 -6
  195. package/tools/installer/prompts.js +0 -3
  196. package/tools/installer/ui.js +89 -54
  197. package/tools/installer/wizz-cli.js +1 -1
  198. package/tools/lib/frontmatter.js +139 -0
  199. package/tools/lib/sync-targets.mjs +49 -0
  200. package/tools/lib/walk.js +93 -0
  201. package/wizz-modules.yaml +9 -0
  202. package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
  203. package/src/scripts/memlog.py +0 -233
  204. package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
  205. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
  206. package/src/scripts/tests/test_memlog.py +0 -306
  207. package/src/skills-lib/wizz-huashu-design/assets/showcases/INDEX.md +0 -115
  208. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.html +0 -235
  209. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.png +0 -0
  210. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.html +0 -229
  211. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.png +0 -0
  212. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.html +0 -288
  213. package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.png +0 -0
  214. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.html +0 -503
  215. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.png +0 -0
  216. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.html +0 -600
  217. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.png +0 -0
  218. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.html +0 -670
  219. package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.png +0 -0
  220. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.html +0 -382
  221. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.png +0 -0
  222. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.html +0 -536
  223. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.png +0 -0
  224. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.html +0 -497
  225. package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.png +0 -0
  226. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.html +0 -385
  227. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.png +0 -0
  228. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.html +0 -422
  229. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.png +0 -0
  230. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.html +0 -499
  231. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.png +0 -0
  232. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.html +0 -562
  233. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.png +0 -0
  234. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.html +0 -548
  235. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.png +0 -0
  236. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.html +0 -696
  237. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.png +0 -0
  238. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.html +0 -372
  239. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.png +0 -0
  240. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.html +0 -460
  241. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.png +0 -0
  242. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.html +0 -494
  243. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.png +0 -0
  244. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.html +0 -367
  245. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.png +0 -0
  246. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.html +0 -368
  247. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.png +0 -0
  248. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.html +0 -459
  249. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.png +0 -0
  250. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.html +0 -493
  251. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.png +0 -0
  252. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.html +0 -556
  253. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.png +0 -0
  254. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.html +0 -604
  255. package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.png +0 -0
  256. package/tools/installer/file-ops.js +0 -204
  257. package/tools/installer/ide/shared/skill-manifest.js +0 -57
  258. package/tools/installer/yaml-format.js +0 -245
  259. package/web-bundles/README.md +0 -46
  260. package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +0 -86
  261. package/web-bundles/brainstorming-coach/SKILL.md +0 -83
  262. package/web-bundles/brainstorming-coach/brain-methods.csv +0 -62
  263. package/web-bundles/bundles.json +0 -139
  264. package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +0 -88
  265. package/web-bundles/market-and-industry-research/SKILL.md +0 -59
  266. package/web-bundles/prd-coach/INSTRUCTIONS.md +0 -86
  267. package/web-bundles/prd-coach/SKILL.md +0 -101
  268. package/web-bundles/prd-coach/prd-template.md +0 -165
  269. package/web-bundles/prd-coach/prd-validation-checklist.md +0 -135
  270. package/web-bundles/prfaq-coach/INSTRUCTIONS.md +0 -86
  271. package/web-bundles/prfaq-coach/SKILL.md +0 -139
  272. package/web-bundles/product-brief-coach/INSTRUCTIONS.md +0 -86
  273. package/web-bundles/product-brief-coach/SKILL.md +0 -113
  274. package/web-bundles/ux-coach/INSTRUCTIONS.md +0 -92
  275. package/web-bundles/ux-coach/SKILL.md +0 -187
  276. package/web-bundles/ux-coach/ux-validation.md +0 -100
  277. /package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/mapa-do-funil.md +0 -0
  278. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/CATALOGO.md +0 -0
  279. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/01-principios.md +0 -0
  280. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/02-diagnostico-funil.md +0 -0
  281. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/03-campanha-bidcap.md +0 -0
  282. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04-criativos.md +0 -0
  283. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04a-criativos-ia.md +0 -0
  284. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04b-criativos-formatos.md +0 -0
  285. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/05-oferta.md +0 -0
  286. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06-pagina-vendas.md +0 -0
  287. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06a-instagram.md +0 -0
  288. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06b-player-vsl.md +0 -0
  289. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07-funil-upsell.md +0 -0
  290. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07a-copy-humanizacao.md +0 -0
  291. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/08-pesquisa-publico.md +0 -0
  292. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/09-especialista-coproducao.md +0 -0
  293. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/10-levantar-caixa.md +0 -0
  294. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/11-checklist-roi.md +0 -0
  295. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12-mineracao-ofertas.md +0 -0
  296. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12a-mineracao-veredito.md +0 -0
  297. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12b-fabrica-de-ofertas.md +0 -0
  298. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12c-anatomia-low-ticket.md +0 -0
  299. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/13-benchmarks-vsl.md +0 -0
  300. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/14-vocabulario.md +0 -0
  301. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/15-como-estender.md +0 -0
  302. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/17-rastreamento.md +0 -0
  303. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/18-copy-desejo-oferta.md +0 -0
  304. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/INDEX.md +0 -0
  305. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/guardrails.md +0 -0
  306. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/pontos-em-aberto.md +0 -0
  307. /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/references/swipe-file.md +0 -0
  308. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/metas-e-medicao.md +0 -0
  309. /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/padroes-de-codigo.md +0 -0
  310. /package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/formato-do-diagnostico.md +0 -0
@@ -1,14 +1,49 @@
1
1
  const fs = require('../fs-native');
2
2
  const os = require('node:os');
3
3
  const path = require('node:path');
4
- const { execSync } = require('node:child_process');
4
+ const { execFileSync } = require('node:child_process');
5
5
  const prompts = require('../prompts');
6
+ // Timeout constants + timeout-aware helpers (ACH-01/EXT-01, auditoria
7
+ // 2026-09-11) agora vivem em git-cache-utils.js (item 20, etapa 1) — uma
8
+ // fonte única compartilhada com external-manager.js, que ainda re-exporta os
9
+ // mesmos símbolos para callers/testes antigos.
10
+ const {
11
+ GIT_NETWORK_TIMEOUT_MS,
12
+ GIT_LOCAL_TIMEOUT_MS,
13
+ NPM_INSTALL_TIMEOUT_MS,
14
+ describeGitTimeout,
15
+ validateGitRef: validateCustomRef,
16
+ } = require('./git-cache-utils');
17
+ // Item 8c (auditoria 2026-09-11): TAG@SHA pins for custom modules. The parse
18
+ // lives in pin-spec.js, shared with external-manager.js and surfaced at the
19
+ // CLI gate by channel-plan.js, so all three paths agree on format + errors.
20
+ const { parsePinTagAndSha, resolvePinOverride, buildPinMismatchError } = require('./pin-spec');
21
+
22
+ // validateCustomRef (forma defensiva de ref) agora é validateGitRef em
23
+ // git-cache-utils.js, importado acima com o rótulo antigo preservado.
24
+
25
+ // RFC-1123 hostname: labels of letters/digits/hyphen (no leading/trailing
26
+ // hyphen), joined by dots.
27
+ const SSH_HOST_RE = /^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$/;
28
+ // Owner/repo path segments: letters, digits, dot, underscore, hyphen only.
29
+ const SSH_PATH_SEGMENT_RE = /^[A-Za-z0-9._-]+$/;
6
30
 
7
- function quoteCustomRef(ref) {
8
- if (typeof ref !== 'string' || !/^[\w.\-+/]+$/.test(ref)) {
9
- throw new Error(`Unsafe ref name: ${JSON.stringify(ref)}`);
10
- }
11
- return `"${ref}"`;
31
+ /**
32
+ * Validate the host/owner/repo captured from a `git@host:owner/repo` SSH URL
33
+ * match. The regex that captures these groups is intentionally permissive
34
+ * (it must accept any self-hosted Git host), so this is the layer that
35
+ * rejects shell metacharacters (quotes, backticks, `$()`, `;`, spaces, etc.)
36
+ * before the value is ever handed to `git`.
37
+ * @param {string} host
38
+ * @param {string} owner - May contain `/` for nested groups (GitLab subgroups).
39
+ * @param {string} repo
40
+ * @returns {boolean}
41
+ */
42
+ function isValidSshRepoParts(host, owner, repo) {
43
+ if (!SSH_HOST_RE.test(host)) return false;
44
+ if (!repo || !SSH_PATH_SEGMENT_RE.test(repo)) return false;
45
+ const ownerSegments = owner.split('/');
46
+ return ownerSegments.length > 0 && ownerSegments.every((segment) => SSH_PATH_SEGMENT_RE.test(segment));
12
47
  }
13
48
 
14
49
  /**
@@ -118,6 +153,13 @@ class CustomModuleManager {
118
153
  const sshMatch = trimmed.match(/^git@([^:]+):(.+?)\/([^/.]+?)(?:\.git)?$/);
119
154
  if (sshMatch) {
120
155
  const [, host, owner, repo] = sshMatch;
156
+ if (!isValidSshRepoParts(host, owner, repo)) {
157
+ throw new Error(
158
+ `URL SSH inválida: "${trimmedRaw}". O host, o owner e o nome do repositório só podem conter letras, ` +
159
+ 'números, ponto, hífen, underscore e barra (para subgrupos). Verifique se não há aspas, crase, `$()`, ' +
160
+ '`;` ou espaços na URL.',
161
+ );
162
+ }
121
163
  return {
122
164
  type: 'url',
123
165
  cloneUrl: trimmed,
@@ -376,12 +418,19 @@ class CustomModuleManager {
376
418
  const silent = options.silent || false;
377
419
  const displayName = parsed.displayName;
378
420
 
379
- // Pin override: --pin CODE=TAG resolved at module-selection time overrides
380
- // any @version suffix present in the URL.
381
- const effectiveVersion = options.pinOverride || parsed.version || null;
421
+ // Pin override (bug 8: keyed by displayName — see resolvePinOverride's JSDoc in pin-spec.js) beats any @version suffix in the URL. May be TAG@40-hex-sha — split below; verified against HEAD further down.
422
+ let effectiveVersion = resolvePinOverride(options, parsed.displayName) || parsed.version || null;
423
+ let expectedPinSha = null;
424
+ if (effectiveVersion && effectiveVersion.includes('@')) {
425
+ const parsedPin = parsePinTagAndSha(effectiveVersion);
426
+ effectiveVersion = parsedPin.tag;
427
+ expectedPinSha = parsedPin.expectedSha;
428
+ }
382
429
 
383
430
  await fs.ensureDir(path.dirname(repoCacheDir));
384
431
 
432
+ let fetchFailed = false; // Bug 4: set on fetch failure below; read by buildPinMismatchError further down.
433
+
385
434
  const createSpinner = async () => {
386
435
  if (silent) {
387
436
  return { start() {}, stop() {}, error() {} };
@@ -393,12 +442,30 @@ class CustomModuleManager {
393
442
  // Tracked via .wizz-source.json's recorded version.
394
443
  if (await fs.pathExists(repoCacheDir)) {
395
444
  let cachedVersion = null;
445
+ let cachedSha = null;
446
+ let cachedExpectedSha = null;
396
447
  try {
397
448
  const existing = await fs.readJson(path.join(repoCacheDir, '.wizz-source.json'));
398
449
  cachedVersion = existing?.version || null;
450
+ cachedSha = existing?.sha || null;
451
+ cachedExpectedSha = existing?.expectedSha || null;
399
452
  } catch {
400
453
  // no metadata; treat as mismatched to be safe if a version was requested
401
454
  }
455
+ // Item 8c: a TAG@SHA pin whose cache was already verified at this exact
456
+ // commit needs no refresh — fetching would only risk pulling a remotely
457
+ // moved tag over a known-good checkout. Same tag under a different
458
+ // expectedSha falls through to the fetch + post-clone check below.
459
+ if (
460
+ expectedPinSha &&
461
+ (cachedVersion || null) === (effectiveVersion || null) &&
462
+ cachedSha &&
463
+ cachedExpectedSha &&
464
+ cachedSha.toLowerCase() === cachedExpectedSha &&
465
+ cachedExpectedSha === expectedPinSha
466
+ ) {
467
+ return repoCacheDir;
468
+ }
402
469
  if ((effectiveVersion || null) !== (cachedVersion || null)) {
403
470
  await fs.remove(repoCacheDir);
404
471
  }
@@ -409,23 +476,30 @@ class CustomModuleManager {
409
476
  const fetchSpinner = await createSpinner();
410
477
  fetchSpinner.start(`Updating ${displayName}...`);
411
478
  try {
412
- execSync('git fetch origin --depth 1', {
479
+ execFileSync('git', ['fetch', 'origin', '--depth', '1'], {
413
480
  cwd: repoCacheDir,
414
481
  stdio: ['ignore', 'pipe', 'pipe'],
415
482
  env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
483
+ timeout: GIT_NETWORK_TIMEOUT_MS,
484
+ killSignal: 'SIGKILL',
416
485
  });
417
486
  if (effectiveVersion) {
418
487
  // Fetch the ref as either a tag or a branch — `origin <ref>` works
419
488
  // for both, whereas `origin tag <ref>` fails for branch refs parsed
420
489
  // out of /tree/<branch>/... URLs.
421
- execSync(`git fetch --depth 1 origin ${quoteCustomRef(effectiveVersion)} --no-tags`, {
490
+ validateCustomRef(effectiveVersion);
491
+ execFileSync('git', ['fetch', '--depth', '1', 'origin', effectiveVersion, '--no-tags'], {
422
492
  cwd: repoCacheDir,
423
493
  stdio: ['ignore', 'pipe', 'pipe'],
424
494
  env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
495
+ timeout: GIT_NETWORK_TIMEOUT_MS,
496
+ killSignal: 'SIGKILL',
425
497
  });
426
- execSync(`git checkout --quiet FETCH_HEAD`, {
498
+ execFileSync('git', ['checkout', '--quiet', 'FETCH_HEAD'], {
427
499
  cwd: repoCacheDir,
428
500
  stdio: ['ignore', 'pipe', 'pipe'],
501
+ timeout: GIT_LOCAL_TIMEOUT_MS,
502
+ killSignal: 'SIGKILL',
429
503
  });
430
504
  } else {
431
505
  // Resolve the default branch (origin/HEAD) and fetch it explicitly.
@@ -433,9 +507,11 @@ class CustomModuleManager {
433
507
  // origin/HEAD` never picks up new commits on the default branch.
434
508
  let defaultBranch = 'main';
435
509
  try {
436
- defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD --short', {
510
+ defaultBranch = execFileSync('git', ['symbolic-ref', 'refs/remotes/origin/HEAD', '--short'], {
437
511
  cwd: repoCacheDir,
438
512
  stdio: 'pipe',
513
+ timeout: GIT_LOCAL_TIMEOUT_MS,
514
+ killSignal: 'SIGKILL',
439
515
  })
440
516
  .toString()
441
517
  .trim()
@@ -443,24 +519,33 @@ class CustomModuleManager {
443
519
  } catch {
444
520
  // Fallback if origin/HEAD is not set
445
521
  }
446
- execSync(`git fetch --depth 1 origin ${quoteCustomRef(defaultBranch)}`, {
522
+ validateCustomRef(defaultBranch);
523
+ execFileSync('git', ['fetch', '--depth', '1', 'origin', defaultBranch], {
447
524
  cwd: repoCacheDir,
448
525
  stdio: ['ignore', 'pipe', 'pipe'],
449
526
  env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
527
+ timeout: GIT_NETWORK_TIMEOUT_MS,
528
+ killSignal: 'SIGKILL',
450
529
  });
451
- execSync(`git reset --hard origin/${quoteCustomRef(defaultBranch)}`, {
530
+ execFileSync('git', ['reset', '--hard', `origin/${defaultBranch}`], {
452
531
  cwd: repoCacheDir,
453
532
  stdio: ['ignore', 'pipe', 'pipe'],
533
+ timeout: GIT_LOCAL_TIMEOUT_MS,
534
+ killSignal: 'SIGKILL',
454
535
  });
455
536
  }
456
537
  fetchSpinner.stop(`Updated ${displayName}`);
457
- } catch {
538
+ } catch (error_) {
458
539
  // Fetch failed against an existing cache — most often the remote is
459
- // unreachable (network down, repo deleted/moved, auth revoked).
460
- // Preserve the previous clone so re-deploy still works from cached
461
- // content; surface a warning so the user knows the cache is stale.
540
+ // unreachable (network down, repo deleted/moved, auth revoked), or our
541
+ // own `timeout` killed a hung git process (ACH-01/EXT-01). Preserve
542
+ // the previous clone so re-deploy still works from cached content;
543
+ // surface a warning so the user knows the cache is stale.
544
+ fetchFailed = true;
462
545
  fetchSpinner.error(`Could not refresh ${displayName} — keeping cached copy`);
463
- await prompts.log.warn(`Custom module ${displayName} was not refreshed (remote unreachable). Using cached copy.`);
546
+ await prompts.log.warn(
547
+ `Custom module ${displayName} was not refreshed (remote unreachable). Using cached copy.${describeGitTimeout(error_)}`,
548
+ );
464
549
  }
465
550
  }
466
551
 
@@ -469,39 +554,74 @@ class CustomModuleManager {
469
554
  fetchSpinner.start(`Cloning ${displayName}${effectiveVersion ? ` @ ${effectiveVersion}` : ''}...`);
470
555
  try {
471
556
  if (effectiveVersion) {
472
- execSync(`git clone --depth 1 --branch ${quoteCustomRef(effectiveVersion)} "${parsed.cloneUrl}" "${repoCacheDir}"`, {
557
+ validateCustomRef(effectiveVersion);
558
+ execFileSync('git', ['clone', '--depth', '1', '--branch', effectiveVersion, parsed.cloneUrl, repoCacheDir], {
473
559
  stdio: ['ignore', 'pipe', 'pipe'],
474
560
  env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
561
+ timeout: GIT_NETWORK_TIMEOUT_MS,
562
+ killSignal: 'SIGKILL',
475
563
  });
476
564
  } else {
477
- execSync(`git clone --depth 1 "${parsed.cloneUrl}" "${repoCacheDir}"`, {
565
+ execFileSync('git', ['clone', '--depth', '1', parsed.cloneUrl, repoCacheDir], {
478
566
  stdio: ['ignore', 'pipe', 'pipe'],
479
567
  env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
568
+ timeout: GIT_NETWORK_TIMEOUT_MS,
569
+ killSignal: 'SIGKILL',
480
570
  });
481
571
  }
482
572
  fetchSpinner.stop(`Cloned ${displayName}`);
483
573
  } catch (error_) {
484
574
  fetchSpinner.error(`Failed to clone ${displayName}`);
485
575
  const refSuffix = effectiveVersion ? `@${effectiveVersion}` : '';
486
- throw new Error(`Failed to clone ${parsed.cloneUrl}${refSuffix}: ${error_.message}`);
576
+ throw new Error(`Failed to clone ${parsed.cloneUrl}${refSuffix}: ${error_.message}${describeGitTimeout(error_)}`);
487
577
  }
488
578
  }
489
579
 
490
580
  // Record the resolved SHA for the manifest writer.
491
581
  let resolvedSha = null;
492
582
  try {
493
- resolvedSha = execSync('git rev-parse HEAD', { cwd: repoCacheDir, stdio: 'pipe' }).toString().trim();
583
+ resolvedSha = execFileSync('git', ['rev-parse', 'HEAD'], {
584
+ cwd: repoCacheDir,
585
+ stdio: 'pipe',
586
+ timeout: GIT_LOCAL_TIMEOUT_MS,
587
+ killSignal: 'SIGKILL',
588
+ })
589
+ .toString()
590
+ .trim();
494
591
  } catch {
495
592
  // swallow — a non-git repo (local path) wouldn't reach here anyway
496
593
  }
594
+ // Item 8c (auditoria 2026-09-11): with a TAG@SHA pin, verify the commit
595
+ // we actually checked out matches what was requested. A git tag is a
596
+ // mutable ref — nothing stops the remote from force-moving it between
597
+ // installs — so this is a trust check, not a network hiccup: it must run
598
+ // before any metadata/marker/resolution a later run would trust is
599
+ // written, and it must never be retried as transient. On mismatch we
600
+ // remove the whole cache dir rather than leave a "cloned fine but at the
601
+ // wrong commit" checkout indistinguishable from a good one — same
602
+ // posture and error text as external-manager.js (item 8 fase 2). Strict equality per bug 3; buildPinMismatchError (pin-spec.js) picks fetch-failed vs. real-mismatch wording per bug 4.
603
+ if (expectedPinSha && (!resolvedSha || resolvedSha.toLowerCase() !== expectedPinSha)) {
604
+ await fs.remove(repoCacheDir);
605
+ const pinDisplay = `${parsed.displayName}=${effectiveVersion}@${expectedPinSha}`;
606
+ throw buildPinMismatchError({
607
+ displayName,
608
+ tag: effectiveVersion,
609
+ resolvedSha,
610
+ expectedSha: expectedPinSha,
611
+ pinDisplay,
612
+ fetchFailed,
613
+ });
614
+ }
497
615
  // Best-effort: capture the remote default branch name so channel marker
498
616
  // metadata for "next" reflects the actual tracked ref (not always "main").
499
617
  let defaultRef = 'main';
500
618
  if (!effectiveVersion) {
501
619
  try {
502
- const symbolic = execSync('git symbolic-ref --short refs/remotes/origin/HEAD', {
620
+ const symbolic = execFileSync('git', ['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], {
503
621
  cwd: repoCacheDir,
504
622
  stdio: 'pipe',
623
+ timeout: GIT_LOCAL_TIMEOUT_MS,
624
+ killSignal: 'SIGKILL',
505
625
  })
506
626
  .toString()
507
627
  .trim();
@@ -530,6 +650,10 @@ class CustomModuleManager {
530
650
  version: effectiveVersion || null,
531
651
  rawInput: parsed.rawInput || sourceInput,
532
652
  sha: resolvedSha,
653
+ // Item 8c: recorded so a later cloneRepo with the same TAG@SHA pin can
654
+ // early-return without re-fetching (a fetch could pull a moved tag over
655
+ // a verified checkout).
656
+ expectedSha: expectedPinSha || null,
533
657
  clonedAt: new Date().toISOString(),
534
658
  });
535
659
  // Keep a channel marker in custom cache too so update paths that rely on
@@ -539,6 +663,7 @@ class CustomModuleManager {
539
663
  channel: effectiveVersion ? 'pinned' : 'next',
540
664
  version: effectiveVersion || defaultRef,
541
665
  sha: resolvedSha,
666
+ expectedSha: expectedPinSha || null,
542
667
  writtenAt: new Date().toISOString(),
543
668
  });
544
669
 
@@ -548,10 +673,13 @@ class CustomModuleManager {
548
673
  const installSpinner = await createSpinner();
549
674
  installSpinner.start(`Installing dependencies for ${displayName}...`);
550
675
  try {
551
- execSync('npm install --omit=dev --no-audit --no-fund --no-progress --legacy-peer-deps', {
676
+ execFileSync('npm', ['install', '--omit=dev', '--no-audit', '--no-fund', '--no-progress', '--legacy-peer-deps'], {
677
+ // npm é npm.cmd no Windows e só roda via shell; os args acima são constantes, sem input do usuário.
678
+ shell: process.platform === 'win32',
552
679
  cwd: repoCacheDir,
553
680
  stdio: ['ignore', 'pipe', 'pipe'],
554
- timeout: 120_000,
681
+ timeout: NPM_INSTALL_TIMEOUT_MS,
682
+ killSignal: 'SIGKILL',
555
683
  });
556
684
  installSpinner.stop(`Installed dependencies for ${displayName}`);
557
685
  } catch (error_) {
@@ -781,7 +909,12 @@ class CustomModuleManager {
781
909
  try {
782
910
  await this.cloneRepo(metadata.rawInput, {
783
911
  silent: true,
784
- pinOverride: metadata.version || undefined,
912
+ // Round-trip both halves of a TAG@SHA pin (item 8c): re-passing only
913
+ // metadata.version (the plain tag) would drop the expected-SHA trust
914
+ // check on refresh. metadata.expectedSha is only ever set when the
915
+ // pin carried it, so plain-version caches are unaffected.
916
+ pinOverride:
917
+ metadata.version && metadata.expectedSha ? `${metadata.version}@${metadata.expectedSha}` : metadata.version || undefined,
785
918
  });
786
919
  CustomModuleManager._refreshedRepoPaths.add(repoPath);
787
920
  } catch (error_) {
@@ -48,6 +48,113 @@ const os = require('node:os');
48
48
  const fs = require('../fs-native');
49
49
  const prompts = require('../prompts');
50
50
 
51
+ /**
52
+ * Check if a gitignore line/pattern covers `.claude/settings.local.json`.
53
+ * Patterns that match:
54
+ * - `.claude/settings.local.json` (literal)
55
+ * - `settings.local.json` (basename only)
56
+ * - `*.local.json` (extension pattern)
57
+ * - `.claude/*.local.json` (dir + extension)
58
+ * - `.claude/` (entire directory)
59
+ * - `.claude` (directory without trailing /)
60
+ *
61
+ * @param {string} pattern - A line from .gitignore
62
+ * @returns {boolean}
63
+ */
64
+ function patternCoversSettingsLocal(pattern) {
65
+ if (!pattern || typeof pattern !== 'string') return false;
66
+ const trimmed = pattern.trim();
67
+ if (trimmed.startsWith('#') || trimmed === '') return false;
68
+
69
+ // Normalize: remove leading/trailing whitespace
70
+ const normalized = trimmed.trim();
71
+
72
+ // Exact literal match
73
+ if (normalized === '.claude/settings.local.json' || normalized === '.claude/settings.local.json/') {
74
+ return true;
75
+ }
76
+
77
+ // Basename only
78
+ if (normalized === 'settings.local.json' || normalized === 'settings.local.json/') {
79
+ return true;
80
+ }
81
+
82
+ // Extension pattern
83
+ if (normalized === '*.local.json') {
84
+ return true;
85
+ }
86
+
87
+ // Directory-scoped extension pattern
88
+ if (normalized === '.claude/*.local.json') {
89
+ return true;
90
+ }
91
+
92
+ // Entire .claude directory
93
+ if (normalized === '.claude/' || normalized === '.claude') {
94
+ return true;
95
+ }
96
+
97
+ return false;
98
+ }
99
+
100
+ /**
101
+ * Verify that `.claude/settings.local.json` is covered by a pattern in
102
+ * `.gitignore`. If not covered and `.git` exists (repo root), add the line.
103
+ * Pure function (testable without real filesystem, but performs I/O here for simplicity).
104
+ *
105
+ * @param {string} projectRoot - Project root (where `_wizz/`, `.git`, `.gitignore` live)
106
+ * @returns {Promise<{added: boolean, reason: string}>}
107
+ * - `{ added: true, reason: "..." }` if the line was added to .gitignore
108
+ * - `{ added: false, reason: "..." }` if already covered, not a git repo, or error
109
+ */
110
+ async function ensureSettingsLocalIgnored(projectRoot) {
111
+ if (!projectRoot) {
112
+ return { added: false, reason: 'projectRoot não fornecido' };
113
+ }
114
+
115
+ const gitDir = path.join(projectRoot, '.git');
116
+ const gitignorePath = path.join(projectRoot, '.gitignore');
117
+
118
+ // Check if this is a git repository
119
+ const isGitRepo = await fs.pathExists(gitDir);
120
+ if (!isGitRepo) {
121
+ return { added: false, reason: 'não é um repositório git' };
122
+ }
123
+
124
+ // Read .gitignore if it exists
125
+ let content = '';
126
+ if (await fs.pathExists(gitignorePath)) {
127
+ try {
128
+ content = await fs.readFile(gitignorePath, 'utf8');
129
+ } catch {
130
+ return { added: false, reason: 'não foi possível ler .gitignore' };
131
+ }
132
+ }
133
+
134
+ // Check if any line already covers .claude/settings.local.json
135
+ const lines = content.split('\n');
136
+ for (const line of lines) {
137
+ if (patternCoversSettingsLocal(line)) {
138
+ return { added: false, reason: '.claude/settings.local.json já está no .gitignore' };
139
+ }
140
+ }
141
+
142
+ // Not covered: add the line (only if .git exists, which we already checked above)
143
+ try {
144
+ // Ensure content ends with newline, then append the new line
145
+ let newContent = content;
146
+ if (newContent && !newContent.endsWith('\n')) {
147
+ newContent += '\n';
148
+ }
149
+ newContent += '.claude/settings.local.json\n';
150
+
151
+ await fs.writeFile(gitignorePath, newContent, 'utf8');
152
+ return { added: true, reason: 'linha adicionada ao .gitignore' };
153
+ } catch {
154
+ return { added: false, reason: 'falha ao escrever em .gitignore' };
155
+ }
156
+ }
157
+
51
158
  // Global key store, read by the INSTALLER only (never by the Claude Code
52
159
  // runtime — C7 still holds). A value found here is copied into the project's
53
160
  // `.claude/settings.local.json` at install time, which IS what reaches the
@@ -363,6 +470,16 @@ async function persistProjectEnv(projectDir, envRecord) {
363
470
  const file = path.join(claudeDir, 'settings.local.json');
364
471
  await fs.ensureDir(claudeDir);
365
472
 
473
+ // Ensure .claude/settings.local.json is in .gitignore before writing secrets
474
+ const gitignoreCheck = await ensureSettingsLocalIgnored(projectDir);
475
+ if (gitignoreCheck.added) {
476
+ await prompts.log.info('.claude/settings.local.json foi adicionado ao .gitignore do projeto (guarda API keys, não deve ir pro git).');
477
+ } else if (gitignoreCheck.reason === 'não é um repositório git') {
478
+ await prompts.log.warn(
479
+ 'Este diretório não é um repositório git: garanta que .claude/settings.local.json (API keys) nunca seja versionado.',
480
+ );
481
+ }
482
+
366
483
  let settings = {};
367
484
  if (await fs.pathExists(file)) {
368
485
  try {
@@ -642,4 +759,6 @@ module.exports = {
642
759
  createGlobalStoreProvider,
643
760
  defaultGlobalEnvPath,
644
761
  formatSummary,
762
+ ensureSettingsLocalIgnored,
763
+ patternCoversSettingsLocal,
645
764
  };