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.
- package/LICENSE +60 -30
- package/README.md +80 -31
- package/SECURITY.md +24 -0
- package/THIRD-PARTY-NOTICES.md +136 -0
- package/package.json +31 -17
- package/skills-registry.yaml +128 -7
- package/src/bmm-skills/1-analysis/research/wizz-domain-research/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/research/wizz-domain-research/domain-steps/step-06-research-synthesis.md +1 -1
- package/src/bmm-skills/1-analysis/research/wizz-market-research/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/research/wizz-market-research/steps/step-06-research-completion.md +1 -1
- package/src/bmm-skills/1-analysis/research/wizz-technical-research/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/research/wizz-technical-research/technical-steps/step-06-research-synthesis.md +1 -1
- package/src/bmm-skills/1-analysis/wizz-agent-analyst/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/wizz-agent-tech-writer/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/wizz-document-project/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/wizz-document-project/workflows/deep-dive-instructions.md +1 -1
- package/src/bmm-skills/1-analysis/wizz-document-project/workflows/full-scan-instructions.md +1 -1
- package/src/bmm-skills/1-analysis/wizz-prfaq/SKILL.md +3 -4
- package/src/bmm-skills/1-analysis/wizz-prfaq/references/verdict.md +1 -1
- package/src/bmm-skills/1-analysis/wizz-product-brief/SKILL.md +1 -1
- package/src/bmm-skills/2-plan-workflows/wizz-agent-pm/SKILL.md +3 -4
- package/src/bmm-skills/2-plan-workflows/wizz-agent-ux-designer/SKILL.md +3 -4
- package/src/bmm-skills/2-plan-workflows/wizz-create-prd/SKILL.md +1 -1
- package/src/bmm-skills/2-plan-workflows/wizz-edit-prd/SKILL.md +1 -1
- package/src/bmm-skills/2-plan-workflows/wizz-prd/SKILL.md +1 -1
- package/src/bmm-skills/2-plan-workflows/wizz-ux/SKILL.md +1 -1
- package/src/bmm-skills/2-plan-workflows/wizz-validate-prd/SKILL.md +1 -1
- package/src/bmm-skills/3-solutioning/wizz-agent-architect/SKILL.md +3 -4
- package/src/bmm-skills/3-solutioning/wizz-architecture/SKILL.md +9 -9
- package/src/bmm-skills/3-solutioning/wizz-architecture/references/headless.md +1 -1
- package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/SKILL.md +3 -4
- package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/steps/step-06-final-assessment.md +1 -1
- package/src/bmm-skills/3-solutioning/wizz-create-architecture/SKILL.md +1 -1
- package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/SKILL.md +3 -4
- package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/steps/step-04-final-validation.md +1 -1
- package/src/bmm-skills/3-solutioning/wizz-generate-project-context/SKILL.md +3 -4
- package/src/bmm-skills/3-solutioning/wizz-generate-project-context/steps/step-03-complete.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-agent-dev/SKILL.md +3 -4
- package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/SKILL.md +3 -4
- package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/step-05-wrapup.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +3 -4
- package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +3 -8
- package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/blind-hunter.md +20 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-correct-course/SKILL.md +4 -5
- package/src/bmm-skills/4-implementation/wizz-create-story/SKILL.md +4 -5
- package/src/bmm-skills/4-implementation/wizz-dev-story/SKILL.md +4 -5
- package/src/bmm-skills/4-implementation/wizz-investigate/SKILL.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/SKILL.md +4 -5
- package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +4 -5
- package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +10 -22
- package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/blind-hunter.md +20 -0
- package/src/bmm-skills/4-implementation/wizz-quick-dev/step-05-present.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-quick-dev/step-oneshot.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-retrospective/SKILL.md +1 -2
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/SKILL.md +1 -1
- package/src/bmm-skills/4-implementation/wizz-sprint-status/SKILL.md +5 -6
- package/src/core-skills/_shared/handoff-protocol.md +9 -0
- package/src/core-skills/wizz-advanced-elicitation/SKILL.md +2 -2
- package/src/core-skills/wizz-brainstorming/SKILL.md +6 -6
- package/src/core-skills/wizz-brainstorming/references/converge.md +1 -1
- package/src/core-skills/wizz-brainstorming/references/finalize.md +1 -1
- package/src/core-skills/wizz-brainstorming/references/headless.md +3 -3
- package/src/core-skills/wizz-brainstorming/references/mode-autonomous.md +1 -1
- package/src/core-skills/wizz-customize/SKILL.md +13 -10
- package/src/core-skills/wizz-forge-idea/SKILL.md +6 -6
- package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +17 -1
- package/src/core-skills/wizz-party-mode/SKILL.md +3 -3
- package/src/core-skills/wizz-party-mode/references/create-party.md +1 -1
- package/src/core-skills/wizz-party-mode/references/party-memory.md +2 -2
- package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +17 -1
- package/src/core-skills/wizz-review-adversarial-general/SKILL.md +1 -1
- package/src/core-skills/wizz-spec/SKILL.md +5 -5
- package/src/modules/lowticket/README.md +6 -6
- package/src/modules/lowticket/module.yaml +4 -1
- package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/SKILL.md +4 -4
- package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/copy-das-paginas.md +1 -1
- package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/SKILL.md +1 -1
- package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/SKILL.md +3 -3
- package/src/modules/lowticket/skills/{lowticket-minerador → wizz-lowticket-minerador}/references/rubrica.md +1 -1
- package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/SKILL.md +4 -4
- package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/SKILL.md +3 -3
- package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/reguas.md +1 -1
- package/src/modules/wizz/_shared/communication-rules.md +1 -1
- package/src/modules/wizz/_shared/despacho-barato.md +9 -0
- package/src/modules/wizz/_shared/token-economy.md +7 -0
- package/src/modules/wizz/agents/wizz-ads/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-ads/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-copy/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-copy/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-designer/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-growth/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-growth/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-maestro/SKILL.md +5 -5
- package/src/modules/wizz/agents/wizz-maestro/customize.toml +4 -8
- package/src/modules/wizz/agents/wizz-memoria/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-memoria/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-qa/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-qa/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-seo/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-seo/customize.toml +1 -2
- package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-social/customize.toml +1 -2
- package/src/modules/wizz/module.yaml +3 -0
- package/src/modules/wizz/overrides/wizz-agent-analyst.toml +1 -2
- package/src/modules/wizz/overrides/wizz-agent-architect.toml +1 -2
- package/src/modules/wizz/overrides/wizz-agent-dev.toml +1 -2
- package/src/modules/wizz/overrides/wizz-agent-pm.toml +1 -2
- package/src/modules/wizz/overrides/wizz-agent-tech-writer.toml +1 -2
- package/src/modules/wizz/overrides/wizz-agent-ux-designer.toml +1 -2
- package/src/modules/wizz/subagents/codex/wizz-exec-haiku.toml +2 -0
- package/src/modules/wizz/subagents/codex/wizz-exec-opus.toml +2 -0
- package/src/modules/wizz/subagents/codex/wizz-exec-review.toml +2 -0
- package/src/modules/wizz/subagents/codex/wizz-exec-sonnet.toml +2 -0
- package/src/modules/wizz/subagents/gemini/wizz-exec-haiku.md +4 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-opus.md +4 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-review.md +4 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-sonnet.md +4 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-haiku.md +4 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-opus.md +4 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-review.md +4 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-sonnet.md +4 -1
- package/src/modules/wizz/subagents/spec.yaml +148 -0
- package/src/modules/wizz/subagents/wizz-exec-haiku.md +4 -1
- package/src/modules/wizz/subagents/wizz-exec-opus.md +4 -1
- package/src/modules/wizz/subagents/wizz-exec-review.md +4 -1
- package/src/modules/wizz/subagents/wizz-exec-sonnet.md +4 -1
- package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
- package/src/scripts/memlog.js +368 -0
- package/src/scripts/resolve_customization.py +124 -15
- package/src/skills-lib/_shared/complexity-signal.md +14 -0
- package/src/skills-lib/wizz-canvas-design/canvas-fonts/InstrumentSerif-OFL.txt +93 -0
- package/src/skills-lib/wizz-huashu-design/references/sfx-library.md +1 -1
- package/src/skills-lib/wizz-huashu-design/scripts/add-music.sh +1 -1
- package/src/skills-lib/wizz-huashu-design/scripts/mix-voiceover.sh +1 -1
- package/src/skills-lib/wizz-impeccable/LICENSE.txt +202 -0
- package/src/skills-lib/wizz-offer-forge/SKILL.md +1 -1
- package/src/skills-lib/wizz-react-components/LICENSE.txt +202 -0
- package/src/skills-lib/wizz-router/SKILL.md +2 -2
- package/src/skills-lib/wizz-supabase-postgres-best-practices/LICENSE +21 -0
- package/src/skills-lib/wizz-taste-skill/SKILL.md +1 -1
- package/src/skills-lib/wizz-tracking-audit/SKILL.md +1 -1
- package/tools/fetch-assets.mjs +70 -38
- package/tools/hooks/README.md +22 -0
- package/tools/hooks/rtk-rewrite.sh +280 -0
- package/tools/hooks/security-defensive-context.js +70 -0
- package/tools/hooks/session-rules.js +368 -0
- package/tools/hooks/wizz-decision-trace.js +389 -0
- package/tools/hooks/wizz-router-enforce.js +258 -0
- package/tools/installer/banner.js +1 -1
- package/tools/installer/commands/doctor.js +124 -0
- package/tools/installer/commands/fetch-assets.js +7 -8
- package/tools/installer/commands/install.js +1 -1
- package/tools/installer/commands/resolve.js +95 -0
- package/tools/installer/core/doctor.js +648 -0
- package/tools/installer/core/hash-cache.js +156 -0
- package/tools/installer/core/help-catalog.js +4 -1
- package/tools/installer/core/hooks-settings.js +178 -0
- package/tools/installer/core/installer.js +297 -15
- package/tools/installer/core/legacy-warnings.js +0 -4
- package/tools/installer/core/manifest-generator.js +121 -43
- package/tools/installer/core/manifest.js +36 -32
- package/tools/installer/core/module-config-writer.js +4 -1
- package/tools/installer/core/python-check.js +60 -63
- package/tools/installer/core/quick-update.js +76 -43
- package/tools/installer/core/resolved-config.js +168 -0
- package/tools/installer/core/resolved-customizations.js +736 -0
- package/tools/installer/core/resolved-includes.js +262 -0
- package/tools/installer/core/resolved-schema.js +148 -0
- package/tools/installer/core/user-file-preservation.js +40 -8
- package/tools/installer/core/wizz-dir.js +117 -0
- package/tools/installer/core/yaml-cache.js +105 -0
- package/tools/installer/fs-native.js +97 -6
- package/tools/installer/ide/_config-driven.js +12 -75
- package/tools/installer/ide/platform-codes.js +111 -16
- package/tools/installer/ide/platform-codes.yaml +45 -100
- package/tools/installer/ide/shared/path-utils.js +6 -208
- package/tools/installer/install-messages.yaml +4 -14
- package/tools/installer/modules/channel-plan.js +15 -2
- package/tools/installer/modules/config-collector.js +762 -0
- package/tools/installer/modules/config-values.js +352 -0
- package/tools/installer/modules/custom-module-manager.js +161 -28
- package/tools/installer/modules/env-vars.js +119 -0
- package/tools/installer/modules/external-manager.js +133 -60
- package/tools/installer/modules/git-cache-utils.js +125 -0
- package/tools/installer/modules/module-catalog.js +565 -0
- package/tools/installer/modules/module-installer.js +618 -0
- package/tools/installer/modules/module-interface.js +86 -0
- package/tools/installer/modules/official-modules.js +98 -2172
- package/tools/installer/modules/pin-spec.js +125 -0
- package/tools/installer/modules/plugin-resolver.js +4 -0
- package/tools/installer/modules/skills-lib.js +3 -6
- package/tools/installer/prompts.js +0 -3
- package/tools/installer/ui.js +89 -54
- package/tools/installer/wizz-cli.js +1 -1
- package/tools/lib/frontmatter.js +139 -0
- package/tools/lib/sync-targets.mjs +49 -0
- package/tools/lib/walk.js +93 -0
- package/wizz-modules.yaml +9 -0
- package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
- package/src/scripts/memlog.py +0 -233
- package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/scripts/tests/test_memlog.py +0 -306
- package/src/skills-lib/wizz-huashu-design/assets/showcases/INDEX.md +0 -115
- package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.html +0 -235
- package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.html +0 -229
- package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.html +0 -288
- package/src/skills-lib/wizz-huashu-design/assets/showcases/cover/cover-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.html +0 -503
- package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.html +0 -600
- package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.html +0 -670
- package/src/skills-lib/wizz-huashu-design/assets/showcases/infographic/infographic-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.html +0 -382
- package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.html +0 -536
- package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.html +0 -497
- package/src/skills-lib/wizz-huashu-design/assets/showcases/ppt/ppt-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.html +0 -385
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.html +0 -422
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.html +0 -499
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-nav/ainav-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.html +0 -562
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.html +0 -548
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.html +0 -696
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-ai-writing/aiwriting-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.html +0 -372
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.html +0 -460
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.html +0 -494
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-devdocs/devdocs-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.html +0 -367
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.html +0 -368
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.html +0 -459
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-homepage/homepage-takram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.html +0 -493
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-build.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.html +0 -556
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-pentagram.png +0 -0
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.html +0 -604
- package/src/skills-lib/wizz-huashu-design/assets/showcases/website-saas/saas-takram.png +0 -0
- package/tools/installer/file-ops.js +0 -204
- package/tools/installer/ide/shared/skill-manifest.js +0 -57
- package/tools/installer/yaml-format.js +0 -245
- package/web-bundles/README.md +0 -46
- package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +0 -86
- package/web-bundles/brainstorming-coach/SKILL.md +0 -83
- package/web-bundles/brainstorming-coach/brain-methods.csv +0 -62
- package/web-bundles/bundles.json +0 -139
- package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +0 -88
- package/web-bundles/market-and-industry-research/SKILL.md +0 -59
- package/web-bundles/prd-coach/INSTRUCTIONS.md +0 -86
- package/web-bundles/prd-coach/SKILL.md +0 -101
- package/web-bundles/prd-coach/prd-template.md +0 -165
- package/web-bundles/prd-coach/prd-validation-checklist.md +0 -135
- package/web-bundles/prfaq-coach/INSTRUCTIONS.md +0 -86
- package/web-bundles/prfaq-coach/SKILL.md +0 -139
- package/web-bundles/product-brief-coach/INSTRUCTIONS.md +0 -86
- package/web-bundles/product-brief-coach/SKILL.md +0 -113
- package/web-bundles/ux-coach/INSTRUCTIONS.md +0 -92
- package/web-bundles/ux-coach/SKILL.md +0 -187
- package/web-bundles/ux-coach/ux-validation.md +0 -100
- /package/src/modules/lowticket/skills/{lowticket-funil → wizz-lowticket-funil}/references/mapa-do-funil.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/CATALOGO.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/01-principios.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/02-diagnostico-funil.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/03-campanha-bidcap.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04-criativos.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04a-criativos-ia.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/04b-criativos-formatos.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/05-oferta.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06-pagina-vendas.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06a-instagram.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/06b-player-vsl.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07-funil-upsell.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/07a-copy-humanizacao.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/08-pesquisa-publico.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/09-especialista-coproducao.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/10-levantar-caixa.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/11-checklist-roi.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12-mineracao-ofertas.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12a-mineracao-veredito.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12b-fabrica-de-ofertas.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/12c-anatomia-low-ticket.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/13-benchmarks-vsl.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/14-vocabulario.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/15-como-estender.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/17-rastreamento.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/18-copy-desejo-oferta.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/INDEX.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/guardrails.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/knowledge/pontos-em-aberto.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-metodologia → wizz-lowticket-metodologia}/references/swipe-file.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/metas-e-medicao.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-pagina → wizz-lowticket-pagina}/references/padroes-de-codigo.md +0 -0
- /package/src/modules/lowticket/skills/{lowticket-trafego → wizz-lowticket-trafego}/references/formato-do-diagnostico.md +0 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lado "install" de OfficialModules — item 20, etapa 3 (auditoria 2026-09-11).
|
|
3
|
+
*
|
|
4
|
+
* O que copia/instala módulos em disco: atomicModuleSwap (ACH-03/F01),
|
|
5
|
+
* install/installFromResolution, copyModuleWithFiltering,
|
|
6
|
+
* createModuleDirectories e remove. Leitura de catálogo mora em
|
|
7
|
+
* module-catalog.js; coleta interativa de config mora em config-collector.js.
|
|
8
|
+
*
|
|
9
|
+
* Os métodos são montados no prototype de OfficialModules (Object.assign em
|
|
10
|
+
* official-modules.js) em vez de virarem uma classe separada: o estado
|
|
11
|
+
* (externalModuleManager, catalog, collectedConfig) é compartilhado na
|
|
12
|
+
* mesma instância, e os testes fazem monkey-patch em métodos de instância
|
|
13
|
+
* (ex.: `officialModules.findModuleSource = async () => ...`) — uma classe
|
|
14
|
+
* dona própria quebra esse contrato.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const fs = require('../fs-native');
|
|
19
|
+
const prompts = require('../prompts');
|
|
20
|
+
const { getSourcePath } = require('../project-root');
|
|
21
|
+
const { loadYamlCached } = require('../core/yaml-cache');
|
|
22
|
+
const { assertModuleInterfaceVersion } = require('./module-interface');
|
|
23
|
+
|
|
24
|
+
const MODULE_SWAP_SUFFIX_RE = /\.(?:tmp|old)-\d+$/;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Remove any `<module>.tmp-<pid>` / `<module>.old-<pid>` leftovers directly
|
|
28
|
+
* under `wizzDir` — orphans from an `atomicModuleSwap` that was killed
|
|
29
|
+
* mid-swap in a previous run. Safe to call before every module swap: once
|
|
30
|
+
* the directory is clean there is nothing left to remove, and the entries
|
|
31
|
+
* a swap currently in flight creates are never visible here (this only runs
|
|
32
|
+
* before that swap starts writing anything).
|
|
33
|
+
* @param {string} wizzDir
|
|
34
|
+
*/
|
|
35
|
+
async function cleanupStaleModuleSwapEntries(wizzDir) {
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = await fs.readdir(wizzDir, { withFileTypes: true });
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
if (MODULE_SWAP_SUFFIX_RE.test(entry.name)) {
|
|
44
|
+
await fs.remove(path.join(wizzDir, entry.name)).catch(() => {});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Atomic per-module install/update swap. Writes the new module content to a
|
|
51
|
+
* sibling `<targetPath>.tmp-<pid>` (same filesystem as `targetPath`, so every
|
|
52
|
+
* rename below is atomic — never `os.tmpdir()`), then promotes it: rename the
|
|
53
|
+
* existing module (if any) to `<targetPath>.old-<pid>`, rename the tmp copy
|
|
54
|
+
* onto `targetPath`, and only then remove the `.old`. Mirrors the
|
|
55
|
+
* `_wizz.tmp-<pid>` + rename scaffold used for fresh installs (A14, see
|
|
56
|
+
* `InstallPaths`/`Installer.install`), scoped to a single module directory
|
|
57
|
+
* instead of the whole `_wizz/` tree — this is what closes ACH-03/F01
|
|
58
|
+
* (remove-then-copy update with no rollback) for updates, which never went
|
|
59
|
+
* through that scaffold.
|
|
60
|
+
*
|
|
61
|
+
* Failure modes:
|
|
62
|
+
* - copy into tmp throws: tmp is removed, `targetPath` (old or absent) is
|
|
63
|
+
* never touched.
|
|
64
|
+
* - promotion rename (tmp → targetPath) throws after the demotion rename
|
|
65
|
+
* (targetPath → old) already succeeded: the old content is renamed back
|
|
66
|
+
* onto `targetPath`, so the module ends up exactly as it started.
|
|
67
|
+
* - anything else (process killed between renames): the next call for any
|
|
68
|
+
* module in this `wizzDir` sweeps the orphaned `.tmp-`/`.old-` entries via
|
|
69
|
+
* `cleanupStaleModuleSwapEntries` before doing its own swap.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} wizzDir - Parent `_wizz/` (or tmp install scaffold) directory.
|
|
72
|
+
* @param {string} targetPath - Final module directory (`wizzDir/<module>`).
|
|
73
|
+
* @param {(tmpPath: string, track: (filePath: string) => void) => Promise<void>} copyInto -
|
|
74
|
+
* Copies the new module content into `tmpPath`. Must report every file it
|
|
75
|
+
* writes through `track(filePath)` instead of the caller's own tracking
|
|
76
|
+
* callback — those paths still live under the tmp directory and are
|
|
77
|
+
* rewritten onto their final `targetPath`-relative location only after a
|
|
78
|
+
* successful swap, so a caller's file list never references a path that
|
|
79
|
+
* stops existing once the tmp dir is renamed away.
|
|
80
|
+
* @param {Function|null} fileTrackingCallback - Caller's real tracking callback.
|
|
81
|
+
*/
|
|
82
|
+
async function atomicModuleSwap(wizzDir, targetPath, copyInto, fileTrackingCallback) {
|
|
83
|
+
await cleanupStaleModuleSwapEntries(wizzDir);
|
|
84
|
+
|
|
85
|
+
const pid = process.pid;
|
|
86
|
+
const tmpPath = `${targetPath}.tmp-${pid}`;
|
|
87
|
+
const oldPath = `${targetPath}.old-${pid}`;
|
|
88
|
+
|
|
89
|
+
// Same pid reusing a leftover from an earlier crash of this exact process
|
|
90
|
+
// id is astronomically unlikely, but start clean regardless.
|
|
91
|
+
await fs.remove(tmpPath).catch(() => {});
|
|
92
|
+
await fs.remove(oldPath).catch(() => {});
|
|
93
|
+
|
|
94
|
+
const trackedTmpPaths = [];
|
|
95
|
+
const track = fileTrackingCallback ? (filePath) => trackedTmpPaths.push(filePath) : () => {};
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await copyInto(tmpPath, track);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
await fs.remove(tmpPath).catch(() => {});
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const hadExisting = await fs.pathExists(targetPath);
|
|
105
|
+
if (hadExisting) {
|
|
106
|
+
await fs.rename(targetPath, oldPath);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
await fs.rename(tmpPath, targetPath);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (hadExisting) {
|
|
113
|
+
await fs.rename(oldPath, targetPath).catch(() => {});
|
|
114
|
+
}
|
|
115
|
+
await fs.remove(tmpPath).catch(() => {});
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (hadExisting) {
|
|
120
|
+
await fs.remove(oldPath).catch(() => {});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (fileTrackingCallback) {
|
|
124
|
+
for (const tmpFilePath of trackedTmpPaths) {
|
|
125
|
+
fileTrackingCallback(targetPath + tmpFilePath.slice(tmpPath.length));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const ModuleInstallerMethods = {
|
|
131
|
+
/**
|
|
132
|
+
* Copy a file to the target location
|
|
133
|
+
* @param {string} sourcePath - Source file path
|
|
134
|
+
* @param {string} targetPath - Target file path
|
|
135
|
+
* @param {boolean} overwrite - Whether to overwrite existing files (default: true)
|
|
136
|
+
*/
|
|
137
|
+
async copyFile(sourcePath, targetPath, overwrite = true) {
|
|
138
|
+
await fs.copy(sourcePath, targetPath, { overwrite });
|
|
139
|
+
},
|
|
140
|
+
/**
|
|
141
|
+
* Install a module
|
|
142
|
+
* @param {string} moduleName - Code of the module to install (from module.yaml)
|
|
143
|
+
* @param {string} wizzDir - Target wizz directory
|
|
144
|
+
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
|
145
|
+
* @param {Object} options - Additional installation options
|
|
146
|
+
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
|
147
|
+
* @param {Object} options.moduleConfig - Module configuration from config collector
|
|
148
|
+
* @param {Object} options.logger - Logger instance for output
|
|
149
|
+
*/
|
|
150
|
+
async install(moduleName, wizzDir, fileTrackingCallback = null, options = {}) {
|
|
151
|
+
// Aliases (ex.: bauto → bmad-loop) chegam crus via --modules e via
|
|
152
|
+
// moduleIds de instalações antigas; sem normalizar aqui, o cache de
|
|
153
|
+
// getPluginResolution, o targetPath e o manifest usariam o código legado.
|
|
154
|
+
moduleName = await this.externalModuleManager.resolveCanonicalCode(moduleName);
|
|
155
|
+
|
|
156
|
+
// Check if this module has a plugin resolution (custom marketplace install)
|
|
157
|
+
const { CustomModuleManager } = require('./custom-module-manager');
|
|
158
|
+
const customMgr = new CustomModuleManager();
|
|
159
|
+
const resolved = customMgr.getResolution(moduleName);
|
|
160
|
+
if (resolved) {
|
|
161
|
+
return this.installFromResolution(resolved, wizzDir, fileTrackingCallback, options);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const sourcePath = await this.findModuleSource(moduleName, {
|
|
165
|
+
silent: options.silent,
|
|
166
|
+
channelOptions: options.channelOptions,
|
|
167
|
+
});
|
|
168
|
+
const targetPath = path.join(wizzDir, moduleName);
|
|
169
|
+
|
|
170
|
+
if (!sourcePath) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Source for module '${moduleName}' is not available. It will be retained but cannot be updated without its source files.`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Contract gate (auditoria 2026-09-11, item 25): a module.yaml declaring
|
|
177
|
+
// an interface_version this installer doesn't support must never reach
|
|
178
|
+
// _wizz/ — check before anything below writes a single byte. Missing
|
|
179
|
+
// module.yaml (null) is handled elsewhere (it's not this gate's job to
|
|
180
|
+
// require one); a module.yaml that exists but fails to parse aborts the
|
|
181
|
+
// install with a clear error instead of the generic parser error.
|
|
182
|
+
let moduleYamlForInterfaceCheck;
|
|
183
|
+
try {
|
|
184
|
+
moduleYamlForInterfaceCheck = await loadYamlCached(path.join(sourcePath, 'module.yaml'));
|
|
185
|
+
} catch (error) {
|
|
186
|
+
throw new Error(`Invalid module.yaml for '${moduleName}': ${error.message}`);
|
|
187
|
+
}
|
|
188
|
+
if (moduleYamlForInterfaceCheck) {
|
|
189
|
+
assertModuleInterfaceVersion(moduleYamlForInterfaceCheck, moduleName);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Módulos marketplace-plugin do registry mantêm as skills instaláveis fora do
|
|
193
|
+
// diretório que contém o module.yaml (sourcePath aponta para o assets/ da
|
|
194
|
+
// skill -setup), então não dá para instalá-los copiando o sourcePath. Copia
|
|
195
|
+
// os diretórios de skill resolvidos, do mesmo jeito que um install custom de
|
|
196
|
+
// marketplace monta um módulo. Todo o resto (manifest, versão) segue pelo
|
|
197
|
+
// caminho padrão de módulo externo abaixo.
|
|
198
|
+
const marketplaceInfo = await this.externalModuleManager.getModuleByCode(moduleName);
|
|
199
|
+
let pluginResolution = null;
|
|
200
|
+
if (marketplaceInfo && marketplaceInfo.marketplacePlugin) {
|
|
201
|
+
pluginResolution = this.externalModuleManager.getPluginResolution(moduleName);
|
|
202
|
+
// Falha alto: copiar o sourcePath aqui instalaria só o assets/ da skill
|
|
203
|
+
// -setup (module.yaml + module-help.csv) e nenhuma skill — um install
|
|
204
|
+
// parcial e silenciosamente quebrado. Aborta em vez disso.
|
|
205
|
+
if (!pluginResolution || !Array.isArray(pluginResolution.skillPaths) || pluginResolution.skillPaths.length === 0) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Module '${moduleName}' is registered as a marketplace plugin but its skills could not be resolved ` +
|
|
208
|
+
`from .claude-plugin/marketplace.json (missing or malformed on the selected channel). ` +
|
|
209
|
+
`Aborting to avoid a partial install with no skills.`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Atomic swap (ACH-03/F01): write the new content beside the target and
|
|
215
|
+
// only then promote it — an update killed mid-write never leaves the
|
|
216
|
+
// module directory missing or half-copied. See `atomicModuleSwap` above.
|
|
217
|
+
await atomicModuleSwap(
|
|
218
|
+
wizzDir,
|
|
219
|
+
targetPath,
|
|
220
|
+
async (tmpPath, track) => {
|
|
221
|
+
if (pluginResolution) {
|
|
222
|
+
await this._copyResolvedSkills(pluginResolution, tmpPath, track, options.moduleConfig);
|
|
223
|
+
} else {
|
|
224
|
+
await this.copyModuleWithFiltering(sourcePath, tmpPath, track, options.moduleConfig);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
fileTrackingCallback,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (!options.skipModuleInstaller) {
|
|
231
|
+
await this.createModuleDirectories(moduleName, wizzDir, { ...options, sourcePath });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const { Manifest } = require('../core/manifest');
|
|
235
|
+
const manifestObj = new Manifest();
|
|
236
|
+
const versionInfo = await manifestObj.getModuleVersionInfo(moduleName, wizzDir, sourcePath);
|
|
237
|
+
|
|
238
|
+
// Pick up channel resolution recorded by the external manager (the only
|
|
239
|
+
// manager that does pre-clone resolution now that community is retired).
|
|
240
|
+
const resolution = this.externalModuleManager.getResolution(moduleName);
|
|
241
|
+
|
|
242
|
+
await manifestObj.addModule(wizzDir, moduleName, {
|
|
243
|
+
version: resolution?.version || versionInfo.version,
|
|
244
|
+
source: versionInfo.source,
|
|
245
|
+
npmPackage: versionInfo.npmPackage,
|
|
246
|
+
repoUrl: versionInfo.repoUrl,
|
|
247
|
+
channel: resolution?.channel,
|
|
248
|
+
sha: resolution?.sha,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
return { success: true, module: moduleName, path: targetPath, versionInfo };
|
|
252
|
+
},
|
|
253
|
+
/**
|
|
254
|
+
* Assenta em disco uma resolução do PluginResolver: copia cada diretório de
|
|
255
|
+
* skill resolvido (achatado pelo nome da folha) para targetPath e coloca o
|
|
256
|
+
* module-help.csv na raiz do módulo. Compartilhado pelos installs custom de
|
|
257
|
+
* marketplace (installFromResolution) e pelos installs oficiais de módulo
|
|
258
|
+
* marketplace-plugin (install), para os dois caminhos não divergirem.
|
|
259
|
+
* @param {Object} resolved - ResolvedModule do PluginResolver
|
|
260
|
+
* @param {string} targetPath - Diretório de destino do módulo (ex.: wizzDir/<code>)
|
|
261
|
+
* @param {Function} fileTrackingCallback - Callback opcional de rastreio de arquivos
|
|
262
|
+
* @param {Object} moduleConfig - Configuração do módulo repassada ao filtro de cópia
|
|
263
|
+
*/
|
|
264
|
+
async _copyResolvedSkills(resolved, targetPath, fileTrackingCallback = null, moduleConfig = {}) {
|
|
265
|
+
await fs.ensureDir(targetPath);
|
|
266
|
+
|
|
267
|
+
// Copia cada diretório de skill, achatado pelo nome da folha. Os nomes de
|
|
268
|
+
// folha precisam ser únicos — duas skills que achatam para o mesmo diretório
|
|
269
|
+
// se sobrescreveriam em silêncio, então falha alto em vez disso.
|
|
270
|
+
const seenLeaves = new Map();
|
|
271
|
+
for (const skillPath of resolved.skillPaths) {
|
|
272
|
+
const skillDirName = path.basename(skillPath);
|
|
273
|
+
if (seenLeaves.has(skillDirName)) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`Cannot install module '${resolved.code}': skill directories '${seenLeaves.get(skillDirName)}' and ` +
|
|
276
|
+
`'${skillPath}' share the leaf name '${skillDirName}' and would overwrite each other. ` +
|
|
277
|
+
`Skill directory names must be unique.`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
seenLeaves.set(skillDirName, skillPath);
|
|
281
|
+
const skillTarget = path.join(targetPath, skillDirName);
|
|
282
|
+
await this.copyModuleWithFiltering(skillPath, skillTarget, fileTrackingCallback, moduleConfig);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Coloca o module-help.csv na raiz do módulo.
|
|
286
|
+
const helpTarget = path.join(targetPath, 'module-help.csv');
|
|
287
|
+
if (resolved.moduleHelpCsvPath) {
|
|
288
|
+
// Estratégias 1-4: copia o arquivo existente.
|
|
289
|
+
await fs.copy(resolved.moduleHelpCsvPath, helpTarget, { overwrite: true });
|
|
290
|
+
if (fileTrackingCallback) fileTrackingCallback(helpTarget);
|
|
291
|
+
} else if (resolved.synthesizedHelpCsv) {
|
|
292
|
+
// Estratégia 5: escreve o conteúdo sintetizado.
|
|
293
|
+
await fs.writeFile(helpTarget, resolved.synthesizedHelpCsv, 'utf8');
|
|
294
|
+
if (fileTrackingCallback) fileTrackingCallback(helpTarget);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
/**
|
|
298
|
+
* Install a module from a PluginResolver resolution result.
|
|
299
|
+
* Copies specific skill directories and places module-help.csv at the target root.
|
|
300
|
+
* @param {Object} resolved - ResolvedModule from PluginResolver
|
|
301
|
+
* @param {string} wizzDir - Target wizz directory
|
|
302
|
+
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
|
303
|
+
* @param {Object} options - Installation options
|
|
304
|
+
*/
|
|
305
|
+
async installFromResolution(resolved, wizzDir, fileTrackingCallback = null, options = {}) {
|
|
306
|
+
const targetPath = path.join(wizzDir, resolved.code);
|
|
307
|
+
|
|
308
|
+
// Contract gate (auditoria 2026-09-11, item 25) — same check as
|
|
309
|
+
// `install()` above, but a ResolvedModule never loads the full
|
|
310
|
+
// module.yaml, only the interfaceVersion PluginResolver carried over.
|
|
311
|
+
assertModuleInterfaceVersion({ interface_version: resolved.interfaceVersion }, resolved.code);
|
|
312
|
+
|
|
313
|
+
// Atomic swap (ACH-03/F01) — see `atomicModuleSwap` above.
|
|
314
|
+
await atomicModuleSwap(
|
|
315
|
+
wizzDir,
|
|
316
|
+
targetPath,
|
|
317
|
+
(tmpPath, track) => this._copyResolvedSkills(resolved, tmpPath, track, options.moduleConfig),
|
|
318
|
+
fileTrackingCallback,
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Create directories declared in module.yaml (strategies 1-4 may have these)
|
|
322
|
+
if (!options.skipModuleInstaller) {
|
|
323
|
+
await this.createModuleDirectories(resolved.code, wizzDir, options);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Update manifest. For custom-source installs we derive channel from the
|
|
327
|
+
// cloneRef (present → pinned, absent → next; local paths have no channel).
|
|
328
|
+
const { Manifest } = require('../core/manifest');
|
|
329
|
+
const manifestObj = new Manifest();
|
|
330
|
+
|
|
331
|
+
const hasGitClone = !!resolved.repoUrl;
|
|
332
|
+
const manifestEntry = {
|
|
333
|
+
version: resolved.cloneRef || (hasGitClone ? 'main' : resolved.version || null),
|
|
334
|
+
source: 'custom',
|
|
335
|
+
npmPackage: null,
|
|
336
|
+
repoUrl: resolved.repoUrl || null,
|
|
337
|
+
};
|
|
338
|
+
if (hasGitClone) {
|
|
339
|
+
manifestEntry.channel = resolved.cloneRef ? 'pinned' : 'next';
|
|
340
|
+
if (resolved.cloneSha) manifestEntry.sha = resolved.cloneSha;
|
|
341
|
+
if (resolved.rawInput) manifestEntry.rawSource = resolved.rawInput;
|
|
342
|
+
}
|
|
343
|
+
if (resolved.localPath) manifestEntry.localPath = resolved.localPath;
|
|
344
|
+
await manifestObj.addModule(wizzDir, resolved.code, manifestEntry);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
success: true,
|
|
348
|
+
module: resolved.code,
|
|
349
|
+
path: targetPath,
|
|
350
|
+
// Mirror the manifestEntry.version precedence above so downstream summary
|
|
351
|
+
// lines show the same string we just wrote to disk (custom git-backed
|
|
352
|
+
// installs show the cloned ref or 'main').
|
|
353
|
+
versionInfo: {
|
|
354
|
+
version: resolved.cloneRef || (hasGitClone ? 'main' : resolved.version || ''),
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
},
|
|
358
|
+
/**
|
|
359
|
+
* Remove a module
|
|
360
|
+
* @param {string} moduleName - Name of the module to remove
|
|
361
|
+
* @param {string} wizzDir - Target wizz directory
|
|
362
|
+
*/
|
|
363
|
+
async remove(moduleName, wizzDir) {
|
|
364
|
+
const targetPath = path.join(wizzDir, moduleName);
|
|
365
|
+
|
|
366
|
+
if (!(await fs.pathExists(targetPath))) {
|
|
367
|
+
throw new Error(`Module '${moduleName}' is not installed`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
await fs.remove(targetPath);
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
success: true,
|
|
374
|
+
module: moduleName,
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
/**
|
|
378
|
+
* Copy module with filtering for localskip agents and conditional content
|
|
379
|
+
* @param {string} sourcePath - Source module path
|
|
380
|
+
* @param {string} targetPath - Target module path
|
|
381
|
+
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
|
382
|
+
* @param {Object} moduleConfig - Module configuration with conditional flags
|
|
383
|
+
*/
|
|
384
|
+
async copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback = null, moduleConfig = {}) {
|
|
385
|
+
// Get all files in source
|
|
386
|
+
const sourceFiles = await this.getFileList(sourcePath);
|
|
387
|
+
|
|
388
|
+
for (const file of sourceFiles) {
|
|
389
|
+
// Skip sub-modules directory - these are IDE-specific and handled separately
|
|
390
|
+
if (file.startsWith('sub-modules/')) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Skip sidecar directories - these contain agent-specific assets not needed at install time
|
|
395
|
+
const isInSidecarDirectory = path
|
|
396
|
+
.dirname(file)
|
|
397
|
+
.split('/')
|
|
398
|
+
.some((dir) => dir.toLowerCase().endsWith('-sidecar'));
|
|
399
|
+
|
|
400
|
+
if (isInSidecarDirectory) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Skip module.yaml at root - it's only needed at install time
|
|
405
|
+
if (file === 'module.yaml') {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Skip module root config.yaml only - generated by config collector with actual values
|
|
410
|
+
// Workflow-level config.yaml (e.g. workflows/orchestrate-story/config.yaml) must be copied
|
|
411
|
+
// for custom modules that use workflow-specific configuration
|
|
412
|
+
if (file === 'config.yaml') {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const sourceFile = path.join(sourcePath, file);
|
|
417
|
+
const targetFile = path.join(targetPath, file);
|
|
418
|
+
|
|
419
|
+
// Check if this is an agent file
|
|
420
|
+
if (file.startsWith('agents/') && file.endsWith('.md')) {
|
|
421
|
+
// Read the file to check for localskip
|
|
422
|
+
const content = await fs.readFile(sourceFile, 'utf8');
|
|
423
|
+
|
|
424
|
+
// Check for localskip="true" in the agent tag
|
|
425
|
+
const agentMatch = content.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
|
|
426
|
+
if (agentMatch) {
|
|
427
|
+
await prompts.log.message(` Skipping web-only agent: ${path.basename(file)}`);
|
|
428
|
+
continue; // Skip this agent
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Copy the file with placeholder replacement
|
|
433
|
+
await this.copyFile(sourceFile, targetFile);
|
|
434
|
+
|
|
435
|
+
// Track the file if callback provided
|
|
436
|
+
if (fileTrackingCallback) {
|
|
437
|
+
fileTrackingCallback(targetFile);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
/**
|
|
442
|
+
* Create directories declared in module.yaml's `directories` key
|
|
443
|
+
* This replaces the security-risky module installer pattern with declarative config
|
|
444
|
+
* During updates, if a directory path changed, moves the old directory to the new path
|
|
445
|
+
* @param {string} moduleName - Name of the module
|
|
446
|
+
* @param {string} wizzDir - Target wizz directory
|
|
447
|
+
* @param {Object} options - Installation options
|
|
448
|
+
* @param {Object} options.moduleConfig - Module configuration from config collector
|
|
449
|
+
* @param {Object} options.existingModuleConfig - Previous module config (for detecting path changes during updates)
|
|
450
|
+
* @param {Object} options.coreConfig - Core configuration
|
|
451
|
+
* @returns {Promise<{createdDirs: string[], movedDirs: string[], createdWdsFolders: string[]}>} Created directories info
|
|
452
|
+
*/
|
|
453
|
+
async createModuleDirectories(moduleName, wizzDir, options = {}) {
|
|
454
|
+
const moduleConfig = options.moduleConfig || {};
|
|
455
|
+
const existingModuleConfig = options.existingModuleConfig || {};
|
|
456
|
+
const projectRoot = path.dirname(wizzDir);
|
|
457
|
+
const emptyResult = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
|
|
458
|
+
|
|
459
|
+
// Special handling for core module - it's in src/core-skills not src/modules.
|
|
460
|
+
// OM-04 (auditoria 2026-09-11): install() já resolveu a fonte; aceita via
|
|
461
|
+
// options.sourcePath para não re-varrer caches externos nem quebrar quando
|
|
462
|
+
// o teste stuba findModuleSource na instância.
|
|
463
|
+
let sourcePath = options.sourcePath;
|
|
464
|
+
if (!sourcePath) {
|
|
465
|
+
if (moduleName === 'core') {
|
|
466
|
+
sourcePath = getSourcePath('core-skills');
|
|
467
|
+
} else {
|
|
468
|
+
sourcePath = await this.findModuleSource(moduleName, { silent: true });
|
|
469
|
+
if (!sourcePath) {
|
|
470
|
+
return emptyResult; // No source found, skip
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Read module.yaml to find the `directories` key
|
|
476
|
+
const moduleYamlPath = path.join(sourcePath, 'module.yaml');
|
|
477
|
+
let moduleYaml;
|
|
478
|
+
try {
|
|
479
|
+
moduleYaml = await loadYamlCached(moduleYamlPath);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
await prompts.log.warn(`Invalid module.yaml for ${moduleName}: ${error.message}`);
|
|
482
|
+
return emptyResult;
|
|
483
|
+
}
|
|
484
|
+
if (!moduleYaml) {
|
|
485
|
+
return emptyResult; // No module.yaml, skip
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (!moduleYaml.directories) {
|
|
489
|
+
return emptyResult; // No directories declared, skip
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const directories = moduleYaml.directories;
|
|
493
|
+
const wdsFolders = moduleYaml.wds_folders || [];
|
|
494
|
+
const createdDirs = [];
|
|
495
|
+
const movedDirs = [];
|
|
496
|
+
const createdWdsFolders = [];
|
|
497
|
+
|
|
498
|
+
for (const dirRef of directories) {
|
|
499
|
+
// Parse variable reference like "{design_artifacts}"
|
|
500
|
+
const varMatch = dirRef.match(/^\{([^}]+)\}$/);
|
|
501
|
+
if (!varMatch) {
|
|
502
|
+
// Not a variable reference, skip
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const configKey = varMatch[1];
|
|
507
|
+
const dirValue = moduleConfig[configKey];
|
|
508
|
+
if (!dirValue || typeof dirValue !== 'string') {
|
|
509
|
+
continue; // No value or not a string, skip
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Strip {project-root}/ prefix if present
|
|
513
|
+
let dirPath = dirValue.replace(/^\{project-root\}\/?/, '');
|
|
514
|
+
|
|
515
|
+
// Handle remaining {project-root} anywhere in the path
|
|
516
|
+
dirPath = dirPath.replaceAll('{project-root}', '');
|
|
517
|
+
|
|
518
|
+
// Resolve to absolute path
|
|
519
|
+
const fullPath = path.join(projectRoot, dirPath);
|
|
520
|
+
|
|
521
|
+
// Validate path is within project root (prevent directory traversal)
|
|
522
|
+
const normalizedPath = path.normalize(fullPath);
|
|
523
|
+
const normalizedRoot = path.normalize(projectRoot);
|
|
524
|
+
if (!normalizedPath.startsWith(normalizedRoot + path.sep) && normalizedPath !== normalizedRoot) {
|
|
525
|
+
const color = await prompts.getColor();
|
|
526
|
+
await prompts.log.warn(color.yellow(`${configKey} path escapes project root, skipping: ${dirPath}`));
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Check if directory path changed from previous config (update/modify scenario)
|
|
531
|
+
const oldDirValue = existingModuleConfig[configKey];
|
|
532
|
+
let oldFullPath = null;
|
|
533
|
+
let oldDirPath = null;
|
|
534
|
+
if (oldDirValue && typeof oldDirValue === 'string') {
|
|
535
|
+
// F3: Normalize both values before comparing to avoid false negatives
|
|
536
|
+
// from trailing slashes, separator differences, or prefix format variations
|
|
537
|
+
let normalizedOld = oldDirValue.replace(/^\{project-root\}\/?/, '');
|
|
538
|
+
normalizedOld = path.normalize(normalizedOld.replaceAll('{project-root}', ''));
|
|
539
|
+
const normalizedNew = path.normalize(dirPath);
|
|
540
|
+
|
|
541
|
+
if (normalizedOld !== normalizedNew) {
|
|
542
|
+
oldDirPath = normalizedOld;
|
|
543
|
+
oldFullPath = path.join(projectRoot, oldDirPath);
|
|
544
|
+
const normalizedOldAbsolute = path.normalize(oldFullPath);
|
|
545
|
+
if (!normalizedOldAbsolute.startsWith(normalizedRoot + path.sep) && normalizedOldAbsolute !== normalizedRoot) {
|
|
546
|
+
oldFullPath = null; // Old path escapes project root, ignore it
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// F13: Prevent parent/child move (e.g. docs/planning → docs/planning/v2)
|
|
550
|
+
if (oldFullPath) {
|
|
551
|
+
const normalizedNewAbsolute = path.normalize(fullPath);
|
|
552
|
+
if (
|
|
553
|
+
normalizedOldAbsolute.startsWith(normalizedNewAbsolute + path.sep) ||
|
|
554
|
+
normalizedNewAbsolute.startsWith(normalizedOldAbsolute + path.sep)
|
|
555
|
+
) {
|
|
556
|
+
const color = await prompts.getColor();
|
|
557
|
+
await prompts.log.warn(
|
|
558
|
+
color.yellow(
|
|
559
|
+
`${configKey}: cannot move between parent/child paths (${oldDirPath} / ${dirPath}), creating new directory instead`,
|
|
560
|
+
),
|
|
561
|
+
);
|
|
562
|
+
oldFullPath = null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const dirName = configKey.replaceAll('_', ' ');
|
|
569
|
+
|
|
570
|
+
if (oldFullPath && (await fs.pathExists(oldFullPath)) && !(await fs.pathExists(fullPath))) {
|
|
571
|
+
// Path changed and old dir exists → move old to new location
|
|
572
|
+
// F1: Use fs.move() instead of fs.rename() for cross-device/volume support
|
|
573
|
+
// F2: Wrap in try/catch — fallback to creating new dir on failure
|
|
574
|
+
try {
|
|
575
|
+
await fs.ensureDir(path.dirname(fullPath));
|
|
576
|
+
await fs.move(oldFullPath, fullPath);
|
|
577
|
+
movedDirs.push(`${dirName}: ${oldDirPath} → ${dirPath}`);
|
|
578
|
+
} catch (moveError) {
|
|
579
|
+
const color = await prompts.getColor();
|
|
580
|
+
await prompts.log.warn(
|
|
581
|
+
color.yellow(
|
|
582
|
+
`Failed to move ${oldDirPath} → ${dirPath}: ${moveError.message}\n Creating new directory instead. Please move contents from the old directory manually.`,
|
|
583
|
+
),
|
|
584
|
+
);
|
|
585
|
+
await fs.ensureDir(fullPath);
|
|
586
|
+
createdDirs.push(`${dirName}: ${dirPath}`);
|
|
587
|
+
}
|
|
588
|
+
} else if (oldFullPath && (await fs.pathExists(oldFullPath)) && (await fs.pathExists(fullPath))) {
|
|
589
|
+
// F5: Both old and new directories exist — warn user about potential orphaned documents
|
|
590
|
+
const color = await prompts.getColor();
|
|
591
|
+
await prompts.log.warn(
|
|
592
|
+
color.yellow(
|
|
593
|
+
`${dirName}: path changed but both directories exist:\n Old: ${oldDirPath}\n New: ${dirPath}\n Old directory may contain orphaned documents — please review and merge manually.`,
|
|
594
|
+
),
|
|
595
|
+
);
|
|
596
|
+
} else if (!(await fs.pathExists(fullPath))) {
|
|
597
|
+
// New directory doesn't exist yet → create it
|
|
598
|
+
createdDirs.push(`${dirName}: ${dirPath}`);
|
|
599
|
+
await fs.ensureDir(fullPath);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Create WDS subfolders if this is the design_artifacts directory
|
|
603
|
+
if (configKey === 'design_artifacts' && wdsFolders.length > 0) {
|
|
604
|
+
for (const subfolder of wdsFolders) {
|
|
605
|
+
const subPath = path.join(fullPath, subfolder);
|
|
606
|
+
if (!(await fs.pathExists(subPath))) {
|
|
607
|
+
await fs.ensureDir(subPath);
|
|
608
|
+
createdWdsFolders.push(subfolder);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return { createdDirs, movedDirs, createdWdsFolders };
|
|
615
|
+
},
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
module.exports = { ModuleInstallerMethods };
|