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,105 @@
|
|
|
1
|
+
// In-process (no disk) memoization for the YAML/CSV files the installer
|
|
2
|
+
// parses repeatedly in a single run: skills-registry.yaml (up to 4x per
|
|
3
|
+
// install), each module's module.yaml (up to 3x per module), and
|
|
4
|
+
// skill-manifest.csv (up to 3x per platform). See audit 2026-09-11,
|
|
5
|
+
// ACH-07/ACH-08/ACH-09: same file, same content, parsed several times with
|
|
6
|
+
// several different ad-hoc try/catch blocks.
|
|
7
|
+
//
|
|
8
|
+
// This centralizes both the memoization and the error handling: "file
|
|
9
|
+
// missing" always resolves to a normal value (null for YAML, [] for CSV)
|
|
10
|
+
// instead of throwing, while "file exists but fails to parse" always throws
|
|
11
|
+
// an Error whose message names the file path, so a bad manifest is
|
|
12
|
+
// diagnosable instead of surfacing as a generic parser error.
|
|
13
|
+
//
|
|
14
|
+
// Cache key is the absolute path; each entry also stores the mtimeMs/size
|
|
15
|
+
// seen at read time, mirroring the calculateFileHash cache in fs-native.js.
|
|
16
|
+
// A file the installer itself rewrites mid-run (e.g. regenerating
|
|
17
|
+
// manifest.yaml) is picked up fresh on the next read because its stat
|
|
18
|
+
// changes; `invalidate(path)` is available for callers that want to force
|
|
19
|
+
// that without waiting on stat granularity.
|
|
20
|
+
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const yaml = require('yaml');
|
|
23
|
+
const csvParseSync = require('csv-parse/sync');
|
|
24
|
+
const fs = require('../fs-native');
|
|
25
|
+
|
|
26
|
+
const yamlCache = new Map(); // absPath -> { mtimeMs, size, data }
|
|
27
|
+
const csvCache = new Map(); // absPath -> { mtimeMs, size, data }
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read + parse a file, memoized per absolute path until its mtime/size
|
|
31
|
+
* changes. Returns `whenMissing` (not a Promise) when the file doesn't
|
|
32
|
+
* exist. Throws an Error naming the path when it exists but `parse` throws.
|
|
33
|
+
* @param {string} filePath
|
|
34
|
+
* @param {Map} cache
|
|
35
|
+
* @param {(content: string) => *} parse
|
|
36
|
+
* @param {string} kind - Used in error messages ("YAML" | "CSV")
|
|
37
|
+
* @param {*} whenMissing
|
|
38
|
+
* @returns {Promise<*>}
|
|
39
|
+
*/
|
|
40
|
+
async function loadCachedFile(filePath, cache, parse, kind, whenMissing) {
|
|
41
|
+
const absPath = path.resolve(filePath);
|
|
42
|
+
|
|
43
|
+
if (!(await fs.pathExists(absPath))) {
|
|
44
|
+
cache.delete(absPath);
|
|
45
|
+
return whenMissing;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const stat = await fs.stat(absPath);
|
|
49
|
+
const cached = cache.get(absPath);
|
|
50
|
+
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
|
51
|
+
return cached.data;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const content = await fs.readFile(absPath, 'utf8');
|
|
55
|
+
let data;
|
|
56
|
+
try {
|
|
57
|
+
data = parse(content);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(`Failed to parse ${kind} at ${absPath}: ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
cache.set(absPath, { mtimeMs: stat.mtimeMs, size: stat.size, data });
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse a YAML file, memoized per absolute path for the life of the process.
|
|
68
|
+
* @param {string} filePath
|
|
69
|
+
* @returns {Promise<Object|null>} Parsed document, or null when absent
|
|
70
|
+
*/
|
|
71
|
+
async function loadYamlCached(filePath) {
|
|
72
|
+
return loadCachedFile(filePath, yamlCache, (content) => yaml.parse(content), 'YAML', null);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse a CSV file (columns:true, skip_empty_lines:true by default),
|
|
77
|
+
* memoized per absolute path for the life of the process.
|
|
78
|
+
* @param {string} filePath
|
|
79
|
+
* @param {Object} [csvOptions] - Overrides merged into the csv-parse options
|
|
80
|
+
* @returns {Promise<Array<Object>>} Parsed rows, or [] when absent
|
|
81
|
+
*/
|
|
82
|
+
async function loadCsvCached(filePath, csvOptions = {}) {
|
|
83
|
+
return loadCachedFile(
|
|
84
|
+
filePath,
|
|
85
|
+
csvCache,
|
|
86
|
+
(content) => csvParseSync.parse(content, { columns: true, skip_empty_lines: true, ...csvOptions }),
|
|
87
|
+
'CSV',
|
|
88
|
+
[],
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Force the next loadYamlCached/loadCsvCached call for this path to reread
|
|
94
|
+
* from disk, regardless of what mtime/size it reports. Call this after the
|
|
95
|
+
* installer itself rewrites a file it may need to read again in the same
|
|
96
|
+
* process.
|
|
97
|
+
* @param {string} filePath
|
|
98
|
+
*/
|
|
99
|
+
function invalidate(filePath) {
|
|
100
|
+
const absPath = path.resolve(filePath);
|
|
101
|
+
yamlCache.delete(absPath);
|
|
102
|
+
csvCache.delete(absPath);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { loadYamlCached, loadCsvCached, invalidate };
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const fsp = require('node:fs/promises');
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const path = require('node:path');
|
|
7
|
+
const crypto = require('node:crypto');
|
|
7
8
|
|
|
8
9
|
async function pathExists(p) {
|
|
9
10
|
try {
|
|
@@ -22,6 +23,30 @@ async function remove(p) {
|
|
|
22
23
|
await fsp.rm(p, { recursive: true, force: true });
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
// Concurrency cap for batched I/O (directory copy, file hashing): enough
|
|
27
|
+
// parallelism to saturate typical disk I/O on a tree with hundreds/thousands
|
|
28
|
+
// of small files, without opening an unbounded number of file descriptors
|
|
29
|
+
// at once on very large trees.
|
|
30
|
+
const DEFAULT_BATCH_SIZE = 32;
|
|
31
|
+
|
|
32
|
+
// Runs `mapper` over `items` in concurrent batches of `batchSize`, returning
|
|
33
|
+
// results in the same order as `items`. Each batch is awaited fully before
|
|
34
|
+
// the next one starts, so the first rejection in a batch propagates and
|
|
35
|
+
// stops further batches — same "first error wins" semantics as the
|
|
36
|
+
// sequential loops this replaces, just with up to `batchSize` operations in
|
|
37
|
+
// flight at once instead of one at a time.
|
|
38
|
+
async function batchMap(items, mapper, batchSize = DEFAULT_BATCH_SIZE) {
|
|
39
|
+
const results = Array.from({ length: items.length });
|
|
40
|
+
for (let start = 0; start < items.length; start += batchSize) {
|
|
41
|
+
const batch = items.slice(start, start + batchSize);
|
|
42
|
+
const batchResults = await Promise.all(batch.map((item, i) => mapper(item, start + i)));
|
|
43
|
+
for (const [i, batchResult] of batchResults.entries()) {
|
|
44
|
+
results[start + i] = batchResult;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
async function copy(src, dest, options = {}) {
|
|
26
51
|
const filterFn = options.filter;
|
|
27
52
|
const overwrite = options.overwrite !== false;
|
|
@@ -40,6 +65,16 @@ async function copy(src, dest, options = {}) {
|
|
|
40
65
|
}
|
|
41
66
|
}
|
|
42
67
|
await fsp.copyFile(src, dest);
|
|
68
|
+
// Preserve the source mtime: node's copyFile doesn't, and the
|
|
69
|
+
// persistent hash cache (hash-cache.json, audit item 23) keys on
|
|
70
|
+
// size+mtime — a freshly stamped mtime would make every re-copied
|
|
71
|
+
// file look changed on the next update and invalidate the cache.
|
|
72
|
+
// Best-effort: mtime is an optimization, not a correctness contract.
|
|
73
|
+
try {
|
|
74
|
+
await fsp.utimes(dest, srcStat.atime, srcStat.mtime);
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore — cache miss on the next run costs a hash, nothing more
|
|
77
|
+
}
|
|
43
78
|
return;
|
|
44
79
|
}
|
|
45
80
|
|
|
@@ -47,9 +82,7 @@ async function copy(src, dest, options = {}) {
|
|
|
47
82
|
if (filterFn && !(await filterFn(src, dest))) return;
|
|
48
83
|
await fsp.mkdir(dest, { recursive: true });
|
|
49
84
|
const entries = await fsp.readdir(src, { withFileTypes: true });
|
|
50
|
-
|
|
51
|
-
await copy(path.join(src, entry.name), path.join(dest, entry.name), options);
|
|
52
|
-
}
|
|
85
|
+
await batchMap(entries, (entry) => copy(path.join(src, entry.name), path.join(dest, entry.name), options));
|
|
53
86
|
}
|
|
54
87
|
}
|
|
55
88
|
|
|
@@ -70,22 +103,77 @@ function readJsonSync(p) {
|
|
|
70
103
|
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
71
104
|
}
|
|
72
105
|
|
|
106
|
+
// Async sibling of readJsonSync. This used to exist as an fs-extra helper;
|
|
107
|
+
// when fs-native replaced fs-extra (a6d075bd) it was dropped even though
|
|
108
|
+
// custom-module-manager.js kept calling `fs.readJson(...)` — every one of
|
|
109
|
+
// those calls threw TypeError, silently swallowed by surrounding
|
|
110
|
+
// try/catch blocks, so cache-version checks there never actually read the
|
|
111
|
+
// recorded metadata. Re-added (item 8c, auditoria 2026-09-11) because the
|
|
112
|
+
// TAG@SHA early-return depends on reading .wizz-source.json.
|
|
113
|
+
async function readJson(p) {
|
|
114
|
+
return JSON.parse(await fsp.readFile(p, 'utf8'));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Central atomic-write primitive: write to a sibling `<dest>.tmp-<pid>-<rand>`
|
|
118
|
+
// in the same directory (so the rename below is same-filesystem, never
|
|
119
|
+
// `os.tmpdir()`) and `rename` it over the destination once the write
|
|
120
|
+
// finishes — a reader never observes a partially written file. On any
|
|
121
|
+
// failure the tmp file is removed (best-effort) and the error is rethrown;
|
|
122
|
+
// the previous destination content, if any, is left untouched. Every write
|
|
123
|
+
// through fs-native's `writeJson`/`writeFile` goes through this, so it's a
|
|
124
|
+
// one-time fix for every config/manifest writer in the installer.
|
|
125
|
+
async function writeFileAtomic(p, data, options) {
|
|
126
|
+
const tmpPath = `${p}.tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
|
|
127
|
+
try {
|
|
128
|
+
await fsp.writeFile(tmpPath, data, options);
|
|
129
|
+
await fsp.rename(tmpPath, p);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
await fsp.rm(tmpPath, { force: true }).catch(() => {});
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
73
136
|
async function writeJson(p, data, options = {}) {
|
|
74
137
|
const spaces = options.spaces ?? 2;
|
|
75
|
-
await
|
|
138
|
+
await writeFileAtomic(p, JSON.stringify(data, null, spaces) + '\n', 'utf8');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Single canonical SHA256-of-file-content implementation (previously
|
|
142
|
+
// duplicated verbatim in core/manifest.js and core/manifest-generator.js,
|
|
143
|
+
// with a third dead copy in file-ops.js). Memoized in-process by
|
|
144
|
+
// `path:size:mtimeMs` — no disk cache — so the same unchanged file hashed
|
|
145
|
+
// twice in one install/update run (e.g. once to detect user modifications,
|
|
146
|
+
// once to write the new files-manifest.csv) only reads/hashes it once.
|
|
147
|
+
// Returns `null` (never throws) when the file can't be read; callers that
|
|
148
|
+
// need a different falsy sentinel adapt it locally.
|
|
149
|
+
const fileHashCache = new Map();
|
|
150
|
+
|
|
151
|
+
async function calculateFileHash(filePath) {
|
|
152
|
+
try {
|
|
153
|
+
const stat = await fsp.stat(filePath);
|
|
154
|
+
const cacheKey = `${filePath}:${stat.size}:${stat.mtimeMs}`;
|
|
155
|
+
if (fileHashCache.has(cacheKey)) {
|
|
156
|
+
return fileHashCache.get(cacheKey);
|
|
157
|
+
}
|
|
158
|
+
const content = await fsp.readFile(filePath);
|
|
159
|
+
const hash = crypto.createHash('sha256').update(content).digest('hex');
|
|
160
|
+
fileHashCache.set(cacheKey, hash);
|
|
161
|
+
return hash;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
76
165
|
}
|
|
77
166
|
|
|
78
167
|
module.exports = {
|
|
79
168
|
// Native async (node:fs/promises)
|
|
80
169
|
readFile: fsp.readFile,
|
|
81
|
-
writeFile:
|
|
170
|
+
writeFile: writeFileAtomic,
|
|
82
171
|
stat: fsp.stat,
|
|
83
172
|
readdir: fsp.readdir,
|
|
84
173
|
access: fsp.access,
|
|
85
174
|
realpath: fsp.realpath,
|
|
86
175
|
rename: fsp.rename,
|
|
87
176
|
rmdir: fsp.rmdir,
|
|
88
|
-
unlink: fsp.unlink,
|
|
89
177
|
chmod: fsp.chmod,
|
|
90
178
|
mkdir: fsp.mkdir,
|
|
91
179
|
mkdtemp: fsp.mkdtemp,
|
|
@@ -99,7 +187,10 @@ module.exports = {
|
|
|
99
187
|
copy,
|
|
100
188
|
move,
|
|
101
189
|
readJsonSync,
|
|
190
|
+
readJson,
|
|
102
191
|
writeJson,
|
|
192
|
+
calculateFileHash,
|
|
193
|
+
batchMap,
|
|
103
194
|
|
|
104
195
|
// Sync methods from core node:fs
|
|
105
196
|
existsSync: fs.existsSync.bind(fs),
|
|
@@ -2,8 +2,9 @@ const path = require('node:path');
|
|
|
2
2
|
const fs = require('../fs-native');
|
|
3
3
|
const yaml = require('yaml');
|
|
4
4
|
const prompts = require('../prompts');
|
|
5
|
-
const
|
|
5
|
+
const { loadCsvCached } = require('../core/yaml-cache');
|
|
6
6
|
const { WIZZ_FOLDER_NAME } = require('./shared/path-utils');
|
|
7
|
+
const { findExistingWizzDir } = require('../core/wizz-dir');
|
|
7
8
|
const { getInstalledCanonicalIds, isWizzOwnedEntry } = require('./shared/installed-skills');
|
|
8
9
|
|
|
9
10
|
// Reserved OpenCode slash commands. A skill whose canonicalId collides with
|
|
@@ -187,25 +188,6 @@ class ConfigDrivenIdeSetup {
|
|
|
187
188
|
* @returns {Promise<Object>} Setup result
|
|
188
189
|
*/
|
|
189
190
|
async setup(projectDir, wizzDir, options = {}) {
|
|
190
|
-
// Check for WIZZ files in ancestor directories that would cause duplicates
|
|
191
|
-
if (this.installerConfig?.ancestor_conflict_check) {
|
|
192
|
-
const conflict = await this.findAncestorConflict(projectDir);
|
|
193
|
-
if (conflict) {
|
|
194
|
-
await prompts.log.error(
|
|
195
|
-
`Found existing WIZZ skills in ancestor installation: ${conflict}\n` +
|
|
196
|
-
` ${this.name} inherits skills from parent directories, so this would cause duplicates.\n` +
|
|
197
|
-
` Please remove the WIZZ files from that directory first:\n` +
|
|
198
|
-
` rm -rf "${conflict}"/wizz*`,
|
|
199
|
-
);
|
|
200
|
-
return {
|
|
201
|
-
success: false,
|
|
202
|
-
reason: 'ancestor-conflict',
|
|
203
|
-
error: `Ancestor conflict: ${conflict}`,
|
|
204
|
-
conflictDir: conflict,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
191
|
if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`);
|
|
210
192
|
|
|
211
193
|
// Clean up any old WIZZ installation first
|
|
@@ -320,8 +302,7 @@ class ConfigDrivenIdeSetup {
|
|
|
320
302
|
const targetDir = config.target_dir;
|
|
321
303
|
const filter = config.commands_filter || null;
|
|
322
304
|
|
|
323
|
-
const
|
|
324
|
-
const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true });
|
|
305
|
+
const records = await loadCsvCached(csvPath);
|
|
325
306
|
|
|
326
307
|
for (const record of records) {
|
|
327
308
|
const canonicalId = record.canonicalId;
|
|
@@ -428,11 +409,7 @@ class ConfigDrivenIdeSetup {
|
|
|
428
409
|
|
|
429
410
|
if (!(await fs.pathExists(csvPath))) return 0;
|
|
430
411
|
|
|
431
|
-
const
|
|
432
|
-
const records = csv.parse(csvContent, {
|
|
433
|
-
columns: true,
|
|
434
|
-
skip_empty_lines: true,
|
|
435
|
-
});
|
|
412
|
+
const records = await loadCsvCached(csvPath);
|
|
436
413
|
|
|
437
414
|
let count = 0;
|
|
438
415
|
|
|
@@ -651,13 +628,13 @@ class ConfigDrivenIdeSetup {
|
|
|
651
628
|
}
|
|
652
629
|
|
|
653
630
|
/**
|
|
654
|
-
* Find the _wizz directory in a project
|
|
631
|
+
* Find the _wizz directory in a project (delegated to core/wizz-dir.js, the
|
|
632
|
+
* single owner of `_wizz` resolution — item 20, auditoria 2026-09-11).
|
|
655
633
|
* @param {string} projectDir - Project directory
|
|
656
634
|
* @returns {string|null} Path to wizz dir or null
|
|
657
635
|
*/
|
|
658
636
|
async _findWizzDir(projectDir) {
|
|
659
|
-
|
|
660
|
-
return (await fs.pathExists(wizzDir)) ? wizzDir : null;
|
|
637
|
+
return findExistingWizzDir(projectDir);
|
|
661
638
|
}
|
|
662
639
|
|
|
663
640
|
/**
|
|
@@ -672,13 +649,10 @@ class ConfigDrivenIdeSetup {
|
|
|
672
649
|
// Also add all currently installed skills from skill-manifest.csv
|
|
673
650
|
const csvPath = path.join(wizzDir, '_config', 'skill-manifest.csv');
|
|
674
651
|
try {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if (record.canonicalId) {
|
|
680
|
-
removals.add(record.canonicalId);
|
|
681
|
-
}
|
|
652
|
+
const records = await loadCsvCached(csvPath);
|
|
653
|
+
for (const record of records) {
|
|
654
|
+
if (record.canonicalId) {
|
|
655
|
+
removals.add(record.canonicalId);
|
|
682
656
|
}
|
|
683
657
|
}
|
|
684
658
|
} catch {
|
|
@@ -857,10 +831,8 @@ class ConfigDrivenIdeSetup {
|
|
|
857
831
|
const ids = new Set();
|
|
858
832
|
if (!wizzDir) return ids;
|
|
859
833
|
const csvPath = path.join(wizzDir, '_config', 'skill-manifest.csv');
|
|
860
|
-
if (!(await fs.pathExists(csvPath))) return ids;
|
|
861
834
|
try {
|
|
862
|
-
const
|
|
863
|
-
const records = csv.parse(content, { columns: true, skip_empty_lines: true });
|
|
835
|
+
const records = await loadCsvCached(csvPath);
|
|
864
836
|
for (const record of records) {
|
|
865
837
|
if (record.canonicalId) ids.add(record.canonicalId);
|
|
866
838
|
}
|
|
@@ -1053,41 +1025,6 @@ class ConfigDrivenIdeSetup {
|
|
|
1053
1025
|
}
|
|
1054
1026
|
}
|
|
1055
1027
|
}
|
|
1056
|
-
|
|
1057
|
-
/**
|
|
1058
|
-
* Check ancestor directories for existing WIZZ files in the same target_dir.
|
|
1059
|
-
* IDEs like Claude Code inherit commands from parent directories, so an existing
|
|
1060
|
-
* installation in an ancestor would cause duplicate commands.
|
|
1061
|
-
* @param {string} projectDir - Project directory being installed to
|
|
1062
|
-
* @returns {Promise<string|null>} Path to conflicting directory, or null if clean
|
|
1063
|
-
*/
|
|
1064
|
-
async findAncestorConflict(projectDir) {
|
|
1065
|
-
const targetDir = this.installerConfig?.target_dir;
|
|
1066
|
-
if (!targetDir) return null;
|
|
1067
|
-
|
|
1068
|
-
const resolvedProject = await fs.realpath(path.resolve(projectDir));
|
|
1069
|
-
let current = path.dirname(resolvedProject);
|
|
1070
|
-
const root = path.parse(current).root;
|
|
1071
|
-
|
|
1072
|
-
while (current !== root && current.length > root.length) {
|
|
1073
|
-
const candidatePath = path.join(current, targetDir);
|
|
1074
|
-
try {
|
|
1075
|
-
if (await fs.pathExists(candidatePath)) {
|
|
1076
|
-
const entries = await fs.readdir(candidatePath);
|
|
1077
|
-
const ancestorWizzDir = await this._findWizzDir(current);
|
|
1078
|
-
const canonicalIds = await getInstalledCanonicalIds(ancestorWizzDir);
|
|
1079
|
-
if (entries.some((e) => isWizzOwnedEntry(e, canonicalIds))) {
|
|
1080
|
-
return candidatePath;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
} catch {
|
|
1084
|
-
// Can't read directory — skip
|
|
1085
|
-
}
|
|
1086
|
-
current = path.dirname(current);
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
return null;
|
|
1090
|
-
}
|
|
1091
1028
|
}
|
|
1092
1029
|
|
|
1093
1030
|
module.exports = { ConfigDrivenIdeSetup };
|
|
@@ -6,6 +6,67 @@ const PLATFORM_CODES_PATH = path.join(__dirname, 'platform-codes.yaml');
|
|
|
6
6
|
|
|
7
7
|
let _cachedPlatformCodes = null;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Shallow-merge a platform's `installer` block onto the shared defaults.
|
|
11
|
+
* The entry's own keys win; any key whose merged value is `null` is dropped
|
|
12
|
+
* (this is how an entry declares "no global_target_dir at all", e.g. ona,
|
|
13
|
+
* replit, trae). Never mutates either input.
|
|
14
|
+
* @param {Object} [defaultInstaller] - `defaults.installer` from the YAML
|
|
15
|
+
* @param {Object} entryInstaller - the platform's own `installer` block
|
|
16
|
+
* @returns {Object} New merged installer object
|
|
17
|
+
*/
|
|
18
|
+
function mergeInstaller(defaultInstaller, entryInstaller) {
|
|
19
|
+
const merged = { ...defaultInstaller, ...entryInstaller };
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
22
|
+
if (value !== null) {
|
|
23
|
+
result[key] = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Apply `defaults` (preferred + installer.target_dir/global_target_dir) to
|
|
31
|
+
* one platform entry. A platform with no `installer:` block at all is
|
|
32
|
+
* returned untouched (defaults only apply to declared installer sections).
|
|
33
|
+
* @param {Object} defaults - top-level `defaults` from the YAML
|
|
34
|
+
* @param {Object} entry - one entry from `platforms`
|
|
35
|
+
* @returns {Object} New platform entry with defaults applied
|
|
36
|
+
*/
|
|
37
|
+
function applyDefaultsToEntry(defaults, entry) {
|
|
38
|
+
const { name, preferred, installer, ...rest } = entry;
|
|
39
|
+
|
|
40
|
+
// Rebuild in the pre-`defaults:` key order (name, preferred, installer,
|
|
41
|
+
// then anything else) so a plain object dump matches byte-for-byte.
|
|
42
|
+
const merged = { name };
|
|
43
|
+
merged.preferred = preferred === undefined ? defaults?.preferred : preferred;
|
|
44
|
+
if (installer) {
|
|
45
|
+
merged.installer = mergeInstaller(defaults?.installer, installer);
|
|
46
|
+
}
|
|
47
|
+
return { ...merged, ...rest };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Apply `defaults` across every entry in `platforms`, returning the config
|
|
52
|
+
* in the old shape (no top-level `defaults` key, consumers never read it).
|
|
53
|
+
* @param {Object} rawConfig - parsed platform-codes.yaml
|
|
54
|
+
* @returns {Object} New config object, same shape as before the `defaults:` block existed
|
|
55
|
+
*/
|
|
56
|
+
function applyDefaults(rawConfig) {
|
|
57
|
+
const { defaults, platforms, ...rest } = rawConfig || {};
|
|
58
|
+
if (!defaults) {
|
|
59
|
+
return rawConfig;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const mergedPlatforms = {};
|
|
63
|
+
for (const [code, entry] of Object.entries(platforms || {})) {
|
|
64
|
+
mergedPlatforms[code] = applyDefaultsToEntry(defaults, entry);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { ...rest, platforms: mergedPlatforms };
|
|
68
|
+
}
|
|
69
|
+
|
|
9
70
|
/**
|
|
10
71
|
* Load the platform codes configuration from YAML
|
|
11
72
|
* @returns {Object} Platform codes configuration
|
|
@@ -20,7 +81,7 @@ async function loadPlatformCodes() {
|
|
|
20
81
|
}
|
|
21
82
|
|
|
22
83
|
const content = await fs.readFile(PLATFORM_CODES_PATH, 'utf8');
|
|
23
|
-
_cachedPlatformCodes = yaml.parse(content);
|
|
84
|
+
_cachedPlatformCodes = applyDefaults(yaml.parse(content));
|
|
24
85
|
return _cachedPlatformCodes;
|
|
25
86
|
}
|
|
26
87
|
|
|
@@ -31,26 +92,58 @@ function clearCache() {
|
|
|
31
92
|
_cachedPlatformCodes = null;
|
|
32
93
|
}
|
|
33
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The installable platforms, exactly as IdeManager exposes them: a platform is
|
|
97
|
+
* installable when it declares an `installer` section (IdeManager only builds
|
|
98
|
+
* handlers for those) and is not suspended (getAvailableIdes skips those).
|
|
99
|
+
* Same shape, same order — preferred first, then alphabetical by name.
|
|
100
|
+
*
|
|
101
|
+
* Kept here, in the leaf, so `formatPlatformList` does not have to reach back
|
|
102
|
+
* into `./manager` — that back-reference was the ide/manager ↔ ide/platform-codes
|
|
103
|
+
* require cycle (item 20, auditoria 2026-09-11). Test Suite 42 of
|
|
104
|
+
* test-installation-components.js asserts this list and the IDs `--tools`
|
|
105
|
+
* accepts stay in agreement.
|
|
106
|
+
* @param {Object} platformConfig - Parsed platform-codes.yaml
|
|
107
|
+
* @returns {Array<{id: string, name: string, targetDir: string, preferred: boolean}>}
|
|
108
|
+
*/
|
|
109
|
+
function listInstallablePlatforms(platformConfig) {
|
|
110
|
+
const entries = [];
|
|
111
|
+
|
|
112
|
+
for (const [platformCode, platformInfo] of Object.entries(platformConfig?.platforms || {})) {
|
|
113
|
+
// Skip if no installer config (platform may not need installation)
|
|
114
|
+
if (!platformInfo?.installer) continue;
|
|
115
|
+
// Skip suspended platforms (e.g., IDE doesn't support skills yet)
|
|
116
|
+
if (platformInfo.suspended) continue;
|
|
117
|
+
|
|
118
|
+
const name = platformInfo.name || platformCode;
|
|
119
|
+
// Filter out invalid entries (undefined name, empty key, etc.)
|
|
120
|
+
if (!platformCode || !name || typeof platformCode !== 'string' || typeof name !== 'string') continue;
|
|
121
|
+
|
|
122
|
+
entries.push({
|
|
123
|
+
id: platformCode,
|
|
124
|
+
name,
|
|
125
|
+
targetDir: platformInfo.installer.target_dir || '',
|
|
126
|
+
preferred: platformInfo.preferred || false,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Sort: preferred first, then alphabetical
|
|
131
|
+
entries.sort((a, b) => {
|
|
132
|
+
if (a.preferred && !b.preferred) return -1;
|
|
133
|
+
if (!a.preferred && b.preferred) return 1;
|
|
134
|
+
return a.name.localeCompare(b.name);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return entries;
|
|
138
|
+
}
|
|
139
|
+
|
|
34
140
|
/**
|
|
35
141
|
* Format the installable platform list for human-readable output (used by --list-tools).
|
|
36
|
-
*
|
|
37
|
-
* (suspended platforms excluded).
|
|
142
|
+
* Matches what --tools accepts at install time (suspended platforms excluded).
|
|
38
143
|
* @returns {Promise<string>} Formatted multi-line string with id, name, target_dir, preferred flag.
|
|
39
144
|
*/
|
|
40
145
|
async function formatPlatformList() {
|
|
41
|
-
const
|
|
42
|
-
const ideManager = new IdeManager();
|
|
43
|
-
await ideManager.ensureInitialized();
|
|
44
|
-
|
|
45
|
-
const entries = ideManager.getAvailableIdes().map((ide) => {
|
|
46
|
-
const handler = ideManager.handlers.get(ide.value);
|
|
47
|
-
return {
|
|
48
|
-
id: ide.value,
|
|
49
|
-
name: ide.name,
|
|
50
|
-
targetDir: handler?.installerConfig?.target_dir || '',
|
|
51
|
-
preferred: ide.preferred,
|
|
52
|
-
};
|
|
53
|
-
});
|
|
146
|
+
const entries = listInstallablePlatforms(await loadPlatformCodes());
|
|
54
147
|
|
|
55
148
|
const idWidth = Math.max(...entries.map((e) => e.id.length), 'ID'.length);
|
|
56
149
|
const nameWidth = Math.max(...entries.map((e) => e.name.length), 'Name'.length);
|
|
@@ -76,5 +169,7 @@ async function formatPlatformList() {
|
|
|
76
169
|
module.exports = {
|
|
77
170
|
loadPlatformCodes,
|
|
78
171
|
clearCache,
|
|
172
|
+
applyDefaults,
|
|
173
|
+
listInstallablePlatforms,
|
|
79
174
|
formatPlatformList,
|
|
80
175
|
};
|