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.
- 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 +62 -37
- 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/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,762 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lado "coleta de configuração" de OfficialModules — item 20, etapa 3
|
|
3
|
+
* (auditoria 2026-09-11).
|
|
4
|
+
*
|
|
5
|
+
* Único módulo do split que importa `prompts`: collectAllConfigurations
|
|
6
|
+
* (gateway express/customize), collectModuleConfig(Quick), buildQuestion e
|
|
7
|
+
* a exibição das post-install notes. Os helpers puros de valor
|
|
8
|
+
* (processResultTemplate, replacePlaceholders, resolveConfigValue,
|
|
9
|
+
* normalizeExistingValueForPrompt, ...) foram para config-values.js e voltam
|
|
10
|
+
* por spread, sem mudar nada para os callers.
|
|
11
|
+
*
|
|
12
|
+
* Os métodos são montados no prototype de OfficialModules (Object.assign em
|
|
13
|
+
* official-modules.js): o estado da coleta (collectedConfig, allAnswers,
|
|
14
|
+
* _existingConfig, currentProjectDir, _silentConfig, skipPrompts,
|
|
15
|
+
* modulesToCustomize) vive na mesma instância usada pelo install.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
const prompts = require('../prompts');
|
|
20
|
+
const { CLIUtils } = require('../banner');
|
|
21
|
+
const { getProjectRoot } = require('../project-root');
|
|
22
|
+
const { isMissingDefault, resolveModuleConfigPath, MODULE_METADATA_FIELDS } = require('./module-catalog');
|
|
23
|
+
const { loadYamlCached } = require('../core/yaml-cache');
|
|
24
|
+
const { ConfigValueMethods, buildQuestionChoices } = require('./config-values');
|
|
25
|
+
|
|
26
|
+
const ConfigCollectorMethods = {
|
|
27
|
+
// Helpers puros de valor (processResultTemplate, replacePlaceholders,
|
|
28
|
+
// resolveConfigValue, ...) moram em config-values.js desde o item 20 e são
|
|
29
|
+
// remontados aqui para os callers continuarem chamando-os como métodos.
|
|
30
|
+
...ConfigValueMethods,
|
|
31
|
+
/**
|
|
32
|
+
* Collect configuration for all modules
|
|
33
|
+
* @param {Array} modules - List of modules to configure (including 'core')
|
|
34
|
+
* @param {string} projectDir - Target project directory
|
|
35
|
+
* @param {Object} options - Additional options
|
|
36
|
+
* @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
|
|
37
|
+
*/
|
|
38
|
+
async collectAllConfigurations(modules, projectDir, options = {}) {
|
|
39
|
+
this.skipPrompts = options.skipPrompts || false;
|
|
40
|
+
this.modulesToCustomize = undefined;
|
|
41
|
+
await this.loadExistingConfig(projectDir);
|
|
42
|
+
|
|
43
|
+
// Check if core was already collected (e.g., in early collection phase)
|
|
44
|
+
const coreAlreadyCollected = this.collectedConfig.core && Object.keys(this.collectedConfig.core).length > 0;
|
|
45
|
+
|
|
46
|
+
// If core wasn't already collected, include it
|
|
47
|
+
const allModules = coreAlreadyCollected ? modules.filter((m) => m !== 'core') : ['core', ...modules.filter((m) => m !== 'core')];
|
|
48
|
+
|
|
49
|
+
// Store all answers across modules for cross-referencing
|
|
50
|
+
if (!this.allAnswers) {
|
|
51
|
+
this.allAnswers = {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Split processing: core first, then gateway, then remaining modules
|
|
55
|
+
const coreModules = allModules.filter((m) => m === 'core');
|
|
56
|
+
const nonCoreModules = allModules.filter((m) => m !== 'core');
|
|
57
|
+
|
|
58
|
+
// Collect core config first (always fully prompted)
|
|
59
|
+
for (const moduleName of coreModules) {
|
|
60
|
+
await this.collectModuleConfig(moduleName, projectDir);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Show batch configuration gateway for non-core modules
|
|
64
|
+
// Scan all non-core module schemas for display names and config metadata
|
|
65
|
+
let scannedModules = [];
|
|
66
|
+
if (!this.skipPrompts && nonCoreModules.length > 0) {
|
|
67
|
+
scannedModules = await this.scanModuleSchemas(nonCoreModules);
|
|
68
|
+
const customizableModules = scannedModules.filter((m) => m.questionCount > 0);
|
|
69
|
+
|
|
70
|
+
if (customizableModules.length > 0) {
|
|
71
|
+
const configMode = await prompts.select({
|
|
72
|
+
message: 'Module configuration',
|
|
73
|
+
choices: [
|
|
74
|
+
{ name: 'Express Setup', value: 'express', hint: 'accept all defaults (recommended)' },
|
|
75
|
+
{ name: 'Customize', value: 'customize', hint: 'choose modules to configure' },
|
|
76
|
+
],
|
|
77
|
+
default: 'express',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (configMode === 'customize') {
|
|
81
|
+
const choices = customizableModules.map((m) => ({
|
|
82
|
+
name: `${m.displayName} (${m.questionCount} option${m.questionCount === 1 ? '' : 's'})`,
|
|
83
|
+
value: m.moduleName,
|
|
84
|
+
hint: m.hasFieldsWithoutDefaults ? 'has fields without defaults' : undefined,
|
|
85
|
+
checked: m.hasFieldsWithoutDefaults,
|
|
86
|
+
}));
|
|
87
|
+
const selected = await prompts.multiselect({
|
|
88
|
+
message: 'Select modules to customize:',
|
|
89
|
+
choices,
|
|
90
|
+
required: false,
|
|
91
|
+
});
|
|
92
|
+
this.modulesToCustomize = new Set(selected);
|
|
93
|
+
} else {
|
|
94
|
+
// Express mode: no modules to customize
|
|
95
|
+
this.modulesToCustomize = new Set();
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
// All non-core modules have zero config - no gateway needed
|
|
99
|
+
this.modulesToCustomize = new Set();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Collect remaining non-core modules
|
|
104
|
+
if (this.modulesToCustomize === undefined) {
|
|
105
|
+
// No gateway was shown (skipPrompts, no non-core modules, or direct call) - process all normally
|
|
106
|
+
for (const moduleName of nonCoreModules) {
|
|
107
|
+
await this.collectModuleConfig(moduleName, projectDir);
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
// Split into default modules (tasks progress) and customized modules (interactive)
|
|
111
|
+
const defaultModules = nonCoreModules.filter((m) => !this.modulesToCustomize.has(m));
|
|
112
|
+
const customizeModules = nonCoreModules.filter((m) => this.modulesToCustomize.has(m));
|
|
113
|
+
|
|
114
|
+
// Run default modules with a single spinner
|
|
115
|
+
if (defaultModules.length > 0) {
|
|
116
|
+
// Build display name map from all scanned modules for pre-call spinner messages
|
|
117
|
+
const displayNameMap = new Map();
|
|
118
|
+
for (const m of scannedModules) {
|
|
119
|
+
displayNameMap.set(m.moduleName, m.displayName);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const configSpinner = await prompts.spinner();
|
|
123
|
+
configSpinner.start('Configuring modules...');
|
|
124
|
+
try {
|
|
125
|
+
for (const moduleName of defaultModules) {
|
|
126
|
+
const displayName = displayNameMap.get(moduleName) || moduleName.toUpperCase();
|
|
127
|
+
configSpinner.message(`Configuring ${displayName}...`);
|
|
128
|
+
try {
|
|
129
|
+
this._silentConfig = true;
|
|
130
|
+
await this.collectModuleConfig(moduleName, projectDir);
|
|
131
|
+
} finally {
|
|
132
|
+
this._silentConfig = false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} finally {
|
|
136
|
+
configSpinner.stop(customizeModules.length > 0 ? 'Module defaults applied' : 'Module configuration complete');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Run customized modules individually (may show interactive prompts)
|
|
141
|
+
for (const moduleName of customizeModules) {
|
|
142
|
+
await this.collectModuleConfig(moduleName, projectDir);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (customizeModules.length > 0) {
|
|
146
|
+
await prompts.log.step('Module configuration complete');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Add metadata
|
|
151
|
+
this.collectedConfig._meta = {
|
|
152
|
+
version: require(path.join(getProjectRoot(), 'package.json')).version,
|
|
153
|
+
installDate: new Date().toISOString(),
|
|
154
|
+
lastModified: new Date().toISOString(),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return this.collectedConfig;
|
|
158
|
+
},
|
|
159
|
+
/**
|
|
160
|
+
* Collect configuration for a single module (Quick Update mode - only new fields)
|
|
161
|
+
* @param {string} moduleName - Module name
|
|
162
|
+
* @param {string} projectDir - Target project directory
|
|
163
|
+
* @param {boolean} silentMode - If true, only prompt for new/missing fields
|
|
164
|
+
* @returns {boolean} True if new fields were prompted, false if all fields existed
|
|
165
|
+
*/
|
|
166
|
+
async collectModuleConfigQuick(moduleName, projectDir, silentMode = true) {
|
|
167
|
+
this.currentProjectDir = projectDir;
|
|
168
|
+
// Load existing config if not already loaded
|
|
169
|
+
if (!this._existingConfig) {
|
|
170
|
+
await this.loadExistingConfig(projectDir);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Initialize allAnswers if not already initialized
|
|
174
|
+
if (!this.allAnswers) {
|
|
175
|
+
this.allAnswers = {};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Load module's config schema from module.yaml ( resolução unificada no
|
|
179
|
+
// catálogo — item 20, etapa 2: o mesmo bloco estava copiado 3x)
|
|
180
|
+
const moduleConfigPath = await resolveModuleConfigPath((code, opts) => this.findModuleSource(code, opts), moduleName);
|
|
181
|
+
|
|
182
|
+
const moduleConfig = moduleConfigPath ? await loadYamlCached(moduleConfigPath) : null;
|
|
183
|
+
|
|
184
|
+
// No usable config schema for this module — keep the existing values.
|
|
185
|
+
// `moduleConfigPath` is non-null whenever a module SOURCE was found, even
|
|
186
|
+
// if that source has no module.yaml; before the item 20 split the check
|
|
187
|
+
// was `pathExists(module.yaml)`, so both cases landed here. Treating only
|
|
188
|
+
// the null-path case as "no schema" silently dropped the module's prior
|
|
189
|
+
// answers from collectedConfig on quick update.
|
|
190
|
+
if (!moduleConfig) {
|
|
191
|
+
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
192
|
+
if (!this.collectedConfig[moduleName]) {
|
|
193
|
+
this.collectedConfig[moduleName] = {};
|
|
194
|
+
}
|
|
195
|
+
this.collectedConfig[moduleName] = { ...this._existingConfig[moduleName] };
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Compare schema with existing config to find new/missing fields
|
|
201
|
+
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
|
202
|
+
const existingKeys = this._existingConfig && this._existingConfig[moduleName] ? Object.keys(this._existingConfig[moduleName]) : [];
|
|
203
|
+
|
|
204
|
+
// Check if this module has no configuration keys at all (like CIS)
|
|
205
|
+
// Filter out metadata fields and only count actual config objects
|
|
206
|
+
const actualConfigKeys = configKeys.filter((key) => !MODULE_METADATA_FIELDS.has(key));
|
|
207
|
+
const hasNoConfig = actualConfigKeys.length === 0;
|
|
208
|
+
|
|
209
|
+
// If module has no config keys at all, handle it specially
|
|
210
|
+
if (hasNoConfig && moduleConfig.subheader) {
|
|
211
|
+
const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
|
|
212
|
+
await prompts.log.step(moduleDisplayName);
|
|
213
|
+
await prompts.log.message(` \u2713 ${moduleConfig.subheader}`);
|
|
214
|
+
return false; // No new fields
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Find new interactive fields (with prompt)
|
|
218
|
+
const newKeys = configKeys.filter((key) => {
|
|
219
|
+
const item = moduleConfig[key];
|
|
220
|
+
// Check if it's a config item and doesn't exist in existing config
|
|
221
|
+
return item && typeof item === 'object' && item.prompt && !existingKeys.includes(key);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Find new static fields (without prompt, just result)
|
|
225
|
+
const newStaticKeys = configKeys.filter((key) => {
|
|
226
|
+
const item = moduleConfig[key];
|
|
227
|
+
return item && typeof item === 'object' && !item.prompt && item.result && !existingKeys.includes(key);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// If in silent mode and no new keys (neither interactive nor static), use existing config and skip prompts
|
|
231
|
+
if (silentMode && newKeys.length === 0 && newStaticKeys.length === 0) {
|
|
232
|
+
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
233
|
+
if (!this.collectedConfig[moduleName]) {
|
|
234
|
+
this.collectedConfig[moduleName] = {};
|
|
235
|
+
}
|
|
236
|
+
this.collectedConfig[moduleName] = { ...this._existingConfig[moduleName] };
|
|
237
|
+
|
|
238
|
+
// Special handling for user_name: ensure it has a value
|
|
239
|
+
if (
|
|
240
|
+
moduleName === 'core' &&
|
|
241
|
+
(!this.collectedConfig[moduleName].user_name || this.collectedConfig[moduleName].user_name === '[USER_NAME]')
|
|
242
|
+
) {
|
|
243
|
+
this.collectedConfig[moduleName].user_name = this.getDefaultUsername();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Also populate allAnswers for cross-referencing
|
|
247
|
+
for (const [key, value] of Object.entries(this._existingConfig[moduleName])) {
|
|
248
|
+
// Ensure user_name is properly set in allAnswers too
|
|
249
|
+
let finalValue = value;
|
|
250
|
+
if (moduleName === 'core' && key === 'user_name' && (!value || value === '[USER_NAME]')) {
|
|
251
|
+
finalValue = this.getDefaultUsername();
|
|
252
|
+
}
|
|
253
|
+
this.allAnswers[`${moduleName}_${key}`] = finalValue;
|
|
254
|
+
}
|
|
255
|
+
} else if (moduleName === 'core') {
|
|
256
|
+
// No existing core config - ensure we at least have user_name
|
|
257
|
+
if (!this.collectedConfig[moduleName]) {
|
|
258
|
+
this.collectedConfig[moduleName] = {};
|
|
259
|
+
}
|
|
260
|
+
if (!this.collectedConfig[moduleName].user_name) {
|
|
261
|
+
this.collectedConfig[moduleName].user_name = this.getDefaultUsername();
|
|
262
|
+
this.allAnswers[`${moduleName}_user_name`] = this.getDefaultUsername();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Show "no config" message for modules with no new questions (that have config keys)
|
|
267
|
+
await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module already up to date`);
|
|
268
|
+
return false; // No new fields
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// If we have new fields (interactive or static), process them
|
|
272
|
+
if (newKeys.length > 0 || newStaticKeys.length > 0) {
|
|
273
|
+
const questions = [];
|
|
274
|
+
const staticAnswers = {};
|
|
275
|
+
|
|
276
|
+
// Build questions for interactive fields
|
|
277
|
+
for (const key of newKeys) {
|
|
278
|
+
const item = moduleConfig[key];
|
|
279
|
+
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
|
|
280
|
+
if (question) {
|
|
281
|
+
questions.push(question);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Prepare static answers (no prompt, just result)
|
|
286
|
+
for (const key of newStaticKeys) {
|
|
287
|
+
staticAnswers[`${moduleName}_${key}`] = undefined;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Collect all answers (static + prompted)
|
|
291
|
+
let allAnswers = { ...staticAnswers };
|
|
292
|
+
|
|
293
|
+
if (questions.length > 0 && silentMode) {
|
|
294
|
+
// In silent mode (quick update), use defaults for new fields instead of prompting
|
|
295
|
+
for (const q of questions) {
|
|
296
|
+
allAnswers[q.name] = typeof q.default === 'function' ? q.default({}) : q.default;
|
|
297
|
+
}
|
|
298
|
+
await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configured with defaults`);
|
|
299
|
+
} else if (questions.length > 0) {
|
|
300
|
+
// Only show header if we actually have questions
|
|
301
|
+
await CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
|
302
|
+
await prompts.log.message('');
|
|
303
|
+
const promptedAnswers = await prompts.prompt(questions);
|
|
304
|
+
|
|
305
|
+
// Merge prompted answers with static answers
|
|
306
|
+
Object.assign(allAnswers, promptedAnswers);
|
|
307
|
+
} else if (newStaticKeys.length > 0) {
|
|
308
|
+
// Only static fields, no questions - show no config message
|
|
309
|
+
await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configuration updated`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Store all answers for cross-referencing
|
|
313
|
+
Object.assign(this.allAnswers, allAnswers);
|
|
314
|
+
|
|
315
|
+
// Process all answers (both static and prompted)
|
|
316
|
+
// First, copy existing config to preserve values that aren't being updated
|
|
317
|
+
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
318
|
+
this.collectedConfig[moduleName] = { ...this._existingConfig[moduleName] };
|
|
319
|
+
} else {
|
|
320
|
+
this.collectedConfig[moduleName] = {};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
for (const key of Object.keys(allAnswers)) {
|
|
324
|
+
const originalKey = key.replace(`${moduleName}_`, '');
|
|
325
|
+
const item = moduleConfig[originalKey];
|
|
326
|
+
const value = allAnswers[key];
|
|
327
|
+
|
|
328
|
+
let result;
|
|
329
|
+
if (Array.isArray(value)) {
|
|
330
|
+
result = value;
|
|
331
|
+
} else if (item.result) {
|
|
332
|
+
result = this.processResultTemplate(item.result, value);
|
|
333
|
+
} else {
|
|
334
|
+
result = value;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Update the collected config with new/updated values
|
|
338
|
+
this.collectedConfig[moduleName][originalKey] = result;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Copy over existing values for fields that weren't prompted
|
|
343
|
+
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
344
|
+
if (!this.collectedConfig[moduleName]) {
|
|
345
|
+
this.collectedConfig[moduleName] = {};
|
|
346
|
+
}
|
|
347
|
+
for (const [key, value] of Object.entries(this._existingConfig[moduleName])) {
|
|
348
|
+
if (!this.collectedConfig[moduleName][key]) {
|
|
349
|
+
this.collectedConfig[moduleName][key] = value;
|
|
350
|
+
this.allAnswers[`${moduleName}_${key}`] = value;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
await this.displayModulePostConfigNotes(moduleName, moduleConfig);
|
|
356
|
+
|
|
357
|
+
return newKeys.length > 0 || newStaticKeys.length > 0; // Return true if we had any new fields (interactive or static)
|
|
358
|
+
},
|
|
359
|
+
/**
|
|
360
|
+
* Collect configuration for a single module
|
|
361
|
+
* @param {string} moduleName - Module name
|
|
362
|
+
* @param {string} projectDir - Target project directory
|
|
363
|
+
* @param {boolean} skipLoadExisting - Skip loading existing config (for early core collection)
|
|
364
|
+
* @param {boolean} skipCompletion - Skip showing completion message (for early core collection)
|
|
365
|
+
*/
|
|
366
|
+
async collectModuleConfig(moduleName, projectDir, skipLoadExisting = false, skipCompletion = false) {
|
|
367
|
+
this.currentProjectDir = projectDir;
|
|
368
|
+
// Load existing config if needed and not already loaded
|
|
369
|
+
if (!skipLoadExisting && !this._existingConfig) {
|
|
370
|
+
await this.loadExistingConfig(projectDir);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Initialize allAnswers if not already initialized
|
|
374
|
+
if (!this.allAnswers) {
|
|
375
|
+
this.allAnswers = {};
|
|
376
|
+
}
|
|
377
|
+
// Load module's config (resolução unificada no catálogo — item 20, etapa 2)
|
|
378
|
+
const moduleConfigPath = await resolveModuleConfigPath((code, opts) => this.findModuleSource(code, opts), moduleName);
|
|
379
|
+
|
|
380
|
+
if (!moduleConfigPath) {
|
|
381
|
+
// No config for this module
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const moduleConfig = await loadYamlCached(moduleConfigPath);
|
|
386
|
+
|
|
387
|
+
if (!moduleConfig) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Process each config item
|
|
392
|
+
const questions = [];
|
|
393
|
+
const staticAnswers = {};
|
|
394
|
+
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
|
395
|
+
|
|
396
|
+
for (const key of configKeys) {
|
|
397
|
+
const item = moduleConfig[key];
|
|
398
|
+
|
|
399
|
+
// Skip if not a config object
|
|
400
|
+
if (!item || typeof item !== 'object') {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Handle static values (no prompt, just result)
|
|
405
|
+
if (!item.prompt && item.result) {
|
|
406
|
+
// Add to static answers with a marker value
|
|
407
|
+
staticAnswers[`${moduleName}_${key}`] = undefined;
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Handle interactive values (with prompt)
|
|
412
|
+
if (item.prompt) {
|
|
413
|
+
const question = await this.buildQuestion(moduleName, key, item, moduleConfig);
|
|
414
|
+
if (question) {
|
|
415
|
+
questions.push(question);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Collect all answers (static + prompted)
|
|
421
|
+
let allAnswers = { ...staticAnswers };
|
|
422
|
+
|
|
423
|
+
// If there are questions to ask, prompt for accepting defaults vs customizing
|
|
424
|
+
if (questions.length > 0) {
|
|
425
|
+
const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
|
|
426
|
+
|
|
427
|
+
// Skip prompts mode: use all defaults without asking
|
|
428
|
+
if (this.skipPrompts) {
|
|
429
|
+
await prompts.log.info(`Using default configuration for ${moduleDisplayName}`);
|
|
430
|
+
// Use defaults for all questions
|
|
431
|
+
for (const question of questions) {
|
|
432
|
+
const hasDefault = !isMissingDefault(question.default);
|
|
433
|
+
if (hasDefault && typeof question.default !== 'function') {
|
|
434
|
+
allAnswers[question.name] = question.default;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
if (!this._silentConfig) await prompts.log.step(`Configuring ${moduleDisplayName}`);
|
|
439
|
+
let useDefaults = true;
|
|
440
|
+
if (moduleName === 'core') {
|
|
441
|
+
useDefaults = false; // Core: always show all questions
|
|
442
|
+
} else if (this.modulesToCustomize === undefined) {
|
|
443
|
+
// Fallback: original per-module confirm (backward compat for direct calls)
|
|
444
|
+
const customizeAnswer = await prompts.prompt([
|
|
445
|
+
{
|
|
446
|
+
type: 'confirm',
|
|
447
|
+
name: 'customize',
|
|
448
|
+
message: 'Accept Defaults (no to customize)?',
|
|
449
|
+
default: true,
|
|
450
|
+
},
|
|
451
|
+
]);
|
|
452
|
+
useDefaults = customizeAnswer.customize;
|
|
453
|
+
} else {
|
|
454
|
+
// Batch mode: use defaults unless module was selected for customization
|
|
455
|
+
useDefaults = !this.modulesToCustomize.has(moduleName);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (useDefaults && moduleName !== 'core') {
|
|
459
|
+
// Accept defaults - only ask questions that have NO default value
|
|
460
|
+
const questionsWithoutDefaults = questions.filter((q) => isMissingDefault(q.default));
|
|
461
|
+
|
|
462
|
+
if (questionsWithoutDefaults.length > 0) {
|
|
463
|
+
await prompts.log.message(` Asking required questions for ${moduleName.toUpperCase()}...`);
|
|
464
|
+
const promptedAnswers = await prompts.prompt(questionsWithoutDefaults);
|
|
465
|
+
Object.assign(allAnswers, promptedAnswers);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// For questions with defaults that weren't asked, we need to process them with their default values
|
|
469
|
+
const questionsWithDefaults = questions.filter((q) => !isMissingDefault(q.default));
|
|
470
|
+
for (const question of questionsWithDefaults) {
|
|
471
|
+
// Skip function defaults - these are dynamic and will be evaluated later
|
|
472
|
+
if (typeof question.default === 'function') {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
allAnswers[question.name] = question.default;
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
const promptedAnswers = await prompts.prompt(questions);
|
|
479
|
+
Object.assign(allAnswers, promptedAnswers);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Store all answers for cross-referencing
|
|
485
|
+
Object.assign(this.allAnswers, allAnswers);
|
|
486
|
+
|
|
487
|
+
// Process all answers (both static and prompted)
|
|
488
|
+
// Always process if we have any answers or static answers
|
|
489
|
+
if (Object.keys(allAnswers).length > 0 || Object.keys(staticAnswers).length > 0) {
|
|
490
|
+
const answers = allAnswers;
|
|
491
|
+
|
|
492
|
+
// Process answers and build result values
|
|
493
|
+
for (const key of Object.keys(answers)) {
|
|
494
|
+
const originalKey = key.replace(`${moduleName}_`, '');
|
|
495
|
+
const item = moduleConfig[originalKey];
|
|
496
|
+
const value = answers[key];
|
|
497
|
+
|
|
498
|
+
// Build the result using the template (OM-01: usa o processResultTemplate
|
|
499
|
+
// compartilhado em vez da reimplementação inline que tinha aqui — as duas
|
|
500
|
+
// versões já tinham divergido no lookup cross-module)
|
|
501
|
+
let result;
|
|
502
|
+
if (Array.isArray(value)) {
|
|
503
|
+
result = value;
|
|
504
|
+
} else if (item.result) {
|
|
505
|
+
result = this.processResultTemplate(item.result, value);
|
|
506
|
+
} else {
|
|
507
|
+
result = value;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Store only the result value (no prompts, defaults, examples, etc.)
|
|
511
|
+
if (!this.collectedConfig[moduleName]) {
|
|
512
|
+
this.collectedConfig[moduleName] = {};
|
|
513
|
+
}
|
|
514
|
+
this.collectedConfig[moduleName][originalKey] = result;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// No longer display completion boxes - keep output clean
|
|
518
|
+
} else {
|
|
519
|
+
// No questions for this module - show completion message with header if available
|
|
520
|
+
const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
|
|
521
|
+
|
|
522
|
+
// Check if this module has NO configuration keys at all (like CIS)
|
|
523
|
+
// Filter out metadata fields and only count actual config objects
|
|
524
|
+
const actualConfigKeys = configKeys.filter((key) => !MODULE_METADATA_FIELDS.has(key));
|
|
525
|
+
const hasNoConfig = actualConfigKeys.length === 0;
|
|
526
|
+
|
|
527
|
+
if (!this._silentConfig) {
|
|
528
|
+
if (hasNoConfig && (moduleConfig.subheader || moduleConfig.header)) {
|
|
529
|
+
await prompts.log.step(moduleDisplayName);
|
|
530
|
+
if (moduleConfig.subheader) {
|
|
531
|
+
await prompts.log.message(` \u2713 ${moduleConfig.subheader}`);
|
|
532
|
+
} else {
|
|
533
|
+
await prompts.log.message(` \u2713 No custom configuration required`);
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
// Module has config but just no questions to ask
|
|
537
|
+
await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configured`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// If we have no collected config for this module, but we have a module schema,
|
|
543
|
+
// ensure we have at least an empty object
|
|
544
|
+
if (!this.collectedConfig[moduleName]) {
|
|
545
|
+
this.collectedConfig[moduleName] = {};
|
|
546
|
+
|
|
547
|
+
// If we accepted defaults and have no answers, we still need to check
|
|
548
|
+
// if there are any static values in the schema that should be applied
|
|
549
|
+
if (moduleConfig) {
|
|
550
|
+
for (const key of Object.keys(moduleConfig)) {
|
|
551
|
+
if (key !== 'prompt' && moduleConfig[key] && typeof moduleConfig[key] === 'object') {
|
|
552
|
+
const item = moduleConfig[key];
|
|
553
|
+
// For static items (no prompt, just result), apply the result
|
|
554
|
+
if (!item.prompt && item.result) {
|
|
555
|
+
// Apply any placeholder replacements to the result
|
|
556
|
+
let result = item.result;
|
|
557
|
+
if (typeof result === 'string') {
|
|
558
|
+
result = this.replacePlaceholders(result, moduleName, moduleConfig);
|
|
559
|
+
}
|
|
560
|
+
this.collectedConfig[moduleName][key] = result;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
await this.displayModulePostConfigNotes(moduleName, moduleConfig);
|
|
568
|
+
},
|
|
569
|
+
/**
|
|
570
|
+
* Build a prompt question from a config item
|
|
571
|
+
* @param {string} moduleName - Module name
|
|
572
|
+
* @param {string} key - Config key
|
|
573
|
+
* @param {Object} item - Config item definition
|
|
574
|
+
* @param {Object} moduleConfig - Full module config schema (for resolving defaults)
|
|
575
|
+
*/
|
|
576
|
+
async buildQuestion(moduleName, key, item, moduleConfig = null) {
|
|
577
|
+
const questionName = `${moduleName}_${key}`;
|
|
578
|
+
|
|
579
|
+
// Check for existing value
|
|
580
|
+
let existingValue = null;
|
|
581
|
+
if (this._existingConfig && this._existingConfig[moduleName]) {
|
|
582
|
+
existingValue = this._existingConfig[moduleName][key];
|
|
583
|
+
existingValue = this.normalizeExistingValueForPrompt(existingValue, moduleName, item, moduleConfig);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Special handling for user_name: default to system user
|
|
587
|
+
if (moduleName === 'core' && key === 'user_name' && !existingValue) {
|
|
588
|
+
item.default = this.getDefaultUsername();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Determine question type and default value (helpers puros em
|
|
592
|
+
// config-values.js — item 20)
|
|
593
|
+
const resolved = this.resolveQuestionDefault(item.default, moduleName, moduleConfig);
|
|
594
|
+
let defaultValue = resolved.defaultValue;
|
|
595
|
+
const dynamicDefault = resolved.dynamicDefault;
|
|
596
|
+
|
|
597
|
+
// Handle different question types
|
|
598
|
+
const { questionType, choices } = buildQuestionChoices(item, existingValue, defaultValue);
|
|
599
|
+
if (questionType === 'list' && existingValue) {
|
|
600
|
+
defaultValue = existingValue;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Build the prompt message
|
|
604
|
+
let message = '';
|
|
605
|
+
|
|
606
|
+
// Handle array prompts for multi-line messages
|
|
607
|
+
if (Array.isArray(item.prompt)) {
|
|
608
|
+
message = item.prompt.join('\n');
|
|
609
|
+
} else {
|
|
610
|
+
message = item.prompt;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Replace placeholders in prompt message with collected config values
|
|
614
|
+
if (typeof message === 'string') {
|
|
615
|
+
message = this.replacePlaceholders(message, moduleName, moduleConfig);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Add current value indicator for existing configs
|
|
619
|
+
const color = await prompts.getColor();
|
|
620
|
+
if (existingValue !== null && existingValue !== undefined) {
|
|
621
|
+
if (typeof existingValue === 'boolean') {
|
|
622
|
+
message += color.dim(` (current: ${existingValue ? 'true' : 'false'})`);
|
|
623
|
+
} else if (Array.isArray(existingValue)) {
|
|
624
|
+
message += color.dim(` (current: ${existingValue.join(', ')})`);
|
|
625
|
+
} else if (questionType !== 'list') {
|
|
626
|
+
// Show the cleaned value (without {project-root}/) for display
|
|
627
|
+
message += color.dim(` (current: ${existingValue})`);
|
|
628
|
+
}
|
|
629
|
+
} else if (item.example && questionType === 'input') {
|
|
630
|
+
// Show example for input fields
|
|
631
|
+
let exampleText = typeof item.example === 'string' ? item.example : JSON.stringify(item.example);
|
|
632
|
+
// Replace placeholders in example
|
|
633
|
+
if (typeof exampleText === 'string') {
|
|
634
|
+
exampleText = this.replacePlaceholders(exampleText, moduleName, moduleConfig);
|
|
635
|
+
exampleText = exampleText.replace('{project-root}/', '');
|
|
636
|
+
}
|
|
637
|
+
message += color.dim(` (e.g., ${exampleText})`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Build the question object
|
|
641
|
+
const question = {
|
|
642
|
+
type: questionType,
|
|
643
|
+
name: questionName,
|
|
644
|
+
message: message,
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// Set default - if it's dynamic, use a function that the prompt will evaluate with current answers
|
|
648
|
+
// But if we have an existing value, always use that instead
|
|
649
|
+
if (existingValue !== null && existingValue !== undefined && questionType !== 'list') {
|
|
650
|
+
question.default = existingValue;
|
|
651
|
+
} else if (dynamicDefault && typeof item.default === 'string') {
|
|
652
|
+
const originalDefault = item.default;
|
|
653
|
+
question.default = (answers) => {
|
|
654
|
+
// Replace placeholders using answers from previous questions in the same batch
|
|
655
|
+
let resolved = originalDefault;
|
|
656
|
+
resolved = resolved.replaceAll(/{([^}]+)}/g, (match, fieldName) => {
|
|
657
|
+
// Look for the answer in the current batch (prefixed with module name)
|
|
658
|
+
const answerKey = `${moduleName}_${fieldName}`;
|
|
659
|
+
if (answers[answerKey] !== undefined) {
|
|
660
|
+
return answers[answerKey];
|
|
661
|
+
}
|
|
662
|
+
// Fall back to collected config
|
|
663
|
+
return this.collectedConfig[moduleName]?.[fieldName] || match;
|
|
664
|
+
});
|
|
665
|
+
// Strip {project-root}/ for cleaner display
|
|
666
|
+
if (resolved.includes('{project-root}/')) {
|
|
667
|
+
resolved = resolved.replace('{project-root}/', '');
|
|
668
|
+
}
|
|
669
|
+
return resolved;
|
|
670
|
+
};
|
|
671
|
+
} else {
|
|
672
|
+
question.default = defaultValue;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Add choices for select types
|
|
676
|
+
if (choices) {
|
|
677
|
+
question.choices = choices;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Add validation for input fields
|
|
681
|
+
if (questionType === 'input') {
|
|
682
|
+
question.validate = (input) => {
|
|
683
|
+
if (!input && item.required) {
|
|
684
|
+
return 'This field is required';
|
|
685
|
+
}
|
|
686
|
+
// Validate against regex pattern if provided
|
|
687
|
+
if (input && item.regex) {
|
|
688
|
+
const regex = new RegExp(item.regex);
|
|
689
|
+
if (!regex.test(input)) {
|
|
690
|
+
return `Invalid format. Must match pattern: ${item.regex}`;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return true;
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Add validation for checkbox (multi-select) fields
|
|
698
|
+
if (questionType === 'checkbox' && item.required) {
|
|
699
|
+
question.validate = (answers) => {
|
|
700
|
+
if (!answers || answers.length === 0) {
|
|
701
|
+
return 'At least one option must be selected';
|
|
702
|
+
}
|
|
703
|
+
return true;
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return question;
|
|
708
|
+
},
|
|
709
|
+
/**
|
|
710
|
+
* Display post-configuration notes for a module
|
|
711
|
+
* Shows prerequisite guidance based on collected config values
|
|
712
|
+
* Reads notes from the module's `post-install-notes` section in module.yaml
|
|
713
|
+
* Supports two formats:
|
|
714
|
+
* - Simple string: always displayed
|
|
715
|
+
* - Object keyed by config field name, with value-specific messages
|
|
716
|
+
* @param {string} moduleName - Module name
|
|
717
|
+
* @param {Object} moduleConfig - Parsed module.yaml content
|
|
718
|
+
*/
|
|
719
|
+
async displayModulePostConfigNotes(moduleName, moduleConfig) {
|
|
720
|
+
if (this._silentConfig) return;
|
|
721
|
+
if (!moduleConfig || !moduleConfig['post-install-notes']) return;
|
|
722
|
+
|
|
723
|
+
const notes = moduleConfig['post-install-notes'];
|
|
724
|
+
const color = await prompts.getColor();
|
|
725
|
+
|
|
726
|
+
// Format 1: Simple string - always display
|
|
727
|
+
if (typeof notes === 'string') {
|
|
728
|
+
await prompts.log.message('');
|
|
729
|
+
for (const line of notes.trim().split('\n')) {
|
|
730
|
+
await prompts.log.message(color.dim(line));
|
|
731
|
+
}
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Format 2: Conditional on config values
|
|
736
|
+
if (typeof notes === 'object') {
|
|
737
|
+
const config = this.collectedConfig[moduleName];
|
|
738
|
+
if (!config) return;
|
|
739
|
+
|
|
740
|
+
let hasOutput = false;
|
|
741
|
+
for (const [configKey, valueMessages] of Object.entries(notes)) {
|
|
742
|
+
const selectedValue = config[configKey];
|
|
743
|
+
if (!selectedValue || !valueMessages[selectedValue]) continue;
|
|
744
|
+
|
|
745
|
+
if (hasOutput) await prompts.log.message('');
|
|
746
|
+
hasOutput = true;
|
|
747
|
+
|
|
748
|
+
const message = valueMessages[selectedValue];
|
|
749
|
+
for (const line of message.trim().split('\n')) {
|
|
750
|
+
const trimmedLine = line.trim();
|
|
751
|
+
if (trimmedLine.endsWith(':') && !trimmedLine.startsWith(' ')) {
|
|
752
|
+
await prompts.log.info(color.bold(trimmedLine));
|
|
753
|
+
} else {
|
|
754
|
+
await prompts.log.message(color.dim(' ' + trimmedLine));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
module.exports = { ConfigCollectorMethods };
|