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,736 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolved customizations generator (audit item 19, 2026-09-11).
|
|
3
|
+
*
|
|
4
|
+
* After the modules are installed, walks the freshly written `_wizz/` tree
|
|
5
|
+
* for every `customize.toml`, applies the same three-layer merge the
|
|
6
|
+
* runtime resolver (`src/scripts/resolve_customization.py`) applies, and
|
|
7
|
+
* writes the merged result to `_wizz/_resolved/<skill-name>.json`.
|
|
8
|
+
*
|
|
9
|
+
* Why: agent activation today spawns Python and/or reads all three TOML
|
|
10
|
+
* layers on every skill activation (48 SKILL.md call sites). With a
|
|
11
|
+
* resolved snapshot on disk, activation is a single file read and NOTHING
|
|
12
|
+
* else — no mtime check, no stat, no spawn. The Python resolver is the
|
|
13
|
+
* fallback for one case only: the snapshot file is missing (never
|
|
14
|
+
* generated, or skipped because the base `customize.toml` is broken).
|
|
15
|
+
*
|
|
16
|
+
* Staleness is therefore a WRITE-time concern, not a read-time one: the
|
|
17
|
+
* snapshots are regenerated by `wizz install`/`wizz update`, by the new
|
|
18
|
+
* `wizz resolve [--skill <name>]`, and by the wizz-customize skill after
|
|
19
|
+
* it edits a layer. `wizz doctor` detects a snapshot that fell behind by
|
|
20
|
+
* comparing `_resolved/.index.json` (sha256 of every input that fed each
|
|
21
|
+
* snapshot: base customize.toml, its includes, the team/user layers and
|
|
22
|
+
* their includes) with the files on disk.
|
|
23
|
+
*
|
|
24
|
+
* The merge here must stay byte-for-byte SEMANTICALLY identical to the
|
|
25
|
+
* Python one (same rules, same precedence, same `include` expansion) so
|
|
26
|
+
* that a resolved snapshot and a fresh `resolve_customization.py --skill`
|
|
27
|
+
* run agree. `test/test-resolved-customizations.js` enforces exactly that
|
|
28
|
+
* parity by running both against fixture fixtures and diffing the JSON.
|
|
29
|
+
*
|
|
30
|
+
* Nothing here prompts; failures are fail-open (warn + skip) so a broken
|
|
31
|
+
* customization file never blocks an install — same philosophy as the
|
|
32
|
+
* Python resolver itself.
|
|
33
|
+
*
|
|
34
|
+
* `_resolved/` is a GENERATED, gitignored artifact and stays OUT of
|
|
35
|
+
* `files-manifest.csv`: nothing here calls the installer's `trackFile`. The
|
|
36
|
+
* manifest exists to tell an installed file from a user-authored one so an
|
|
37
|
+
* update can preserve user edits and detect drift; a snapshot has no user
|
|
38
|
+
* edits to preserve (editing one is pointless — the next `wizz resolve`
|
|
39
|
+
* overwrites it) and its hash changes on every regeneration, so tracking it
|
|
40
|
+
* would report drift on every single run. Drift detection therefore ignores
|
|
41
|
+
* `_resolved/` entirely; staleness is `.index.json`'s job, not the
|
|
42
|
+
* manifest's.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const path = require('node:path');
|
|
46
|
+
const nodeFs = require('node:fs');
|
|
47
|
+
const crypto = require('node:crypto');
|
|
48
|
+
const fs = require('../fs-native');
|
|
49
|
+
const { parse: parseToml } = require('smol-toml');
|
|
50
|
+
const { WIZZ_FOLDER_NAME } = require('../ide/shared/path-utils');
|
|
51
|
+
const { resolveIncludes, splitIncludeParagraphs, safeSet, hasOwn, isPlainObject } = require('./resolved-includes');
|
|
52
|
+
// Audit item 24 — closed key sets for the user-authored team/user layers.
|
|
53
|
+
const { findUnknownKeys } = require('./resolved-schema');
|
|
54
|
+
|
|
55
|
+
const KEYED_MERGE_FIELDS = ['code', 'id'];
|
|
56
|
+
|
|
57
|
+
// Names inside `_resolved/` that are NOT skill snapshots (never pruned).
|
|
58
|
+
const INDEX_FILE = '.index.json';
|
|
59
|
+
const GITIGNORE_FILE = '.gitignore';
|
|
60
|
+
// `_resolved/` holds generated artifacts only: ignore everything but the
|
|
61
|
+
// ignore file itself, so a `wizz update` never shows up as 340 KB of diff.
|
|
62
|
+
const GITIGNORE_BODY = '*\n!.gitignore\n';
|
|
63
|
+
const INDEX_VERSION = 1;
|
|
64
|
+
|
|
65
|
+
// ─── Merge rules (ported 1:1 from resolve_customization.py) ───
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Return 'code' or 'id' if every item in the array carries that same
|
|
69
|
+
* identifier field; otherwise null (append semantics). Mixed code/id
|
|
70
|
+
* arrays intentionally fall through to append.
|
|
71
|
+
*/
|
|
72
|
+
function detectKeyedMergeField(items) {
|
|
73
|
+
if (!Array.isArray(items) || items.length === 0) return null;
|
|
74
|
+
if (!items.every((item) => item !== null && typeof item === 'object' && !Array.isArray(item))) return null;
|
|
75
|
+
for (const candidate of KEYED_MERGE_FIELDS) {
|
|
76
|
+
if (items.every((item) => item[candidate] !== undefined && item[candidate] !== null)) return candidate;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Keyed merge: matching identifiers replace in place, new ones append.
|
|
83
|
+
* Non-table items in the override are appended (Python parity).
|
|
84
|
+
*/
|
|
85
|
+
function mergeByKey(base, override, keyName) {
|
|
86
|
+
const result = [];
|
|
87
|
+
const indexByKey = new Map();
|
|
88
|
+
for (const item of base) {
|
|
89
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) continue;
|
|
90
|
+
if (item[keyName] !== undefined && item[keyName] !== null) {
|
|
91
|
+
indexByKey.set(item[keyName], result.length);
|
|
92
|
+
}
|
|
93
|
+
result.push({ ...item });
|
|
94
|
+
}
|
|
95
|
+
for (const item of override) {
|
|
96
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
97
|
+
result.push(item);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const key = item[keyName];
|
|
101
|
+
if (key !== undefined && key !== null && indexByKey.has(key)) {
|
|
102
|
+
result[indexByKey.get(key)] = { ...item };
|
|
103
|
+
} else {
|
|
104
|
+
if (key !== undefined && key !== null) indexByKey.set(key, result.length);
|
|
105
|
+
result.push({ ...item });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Shape-aware array merge: keyed merge when every item (base + override)
|
|
113
|
+
* shares `code` or `id`, else append.
|
|
114
|
+
*/
|
|
115
|
+
function mergeArrays(base, override) {
|
|
116
|
+
const baseArr = Array.isArray(base) ? base : [];
|
|
117
|
+
const overrideArr = Array.isArray(override) ? override : [];
|
|
118
|
+
const keyedField = detectKeyedMergeField([...baseArr, ...overrideArr]);
|
|
119
|
+
if (keyedField) return mergeByKey(baseArr, overrideArr, keyedField);
|
|
120
|
+
return [...baseArr, ...overrideArr];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Structural deep merge. Tables deep-merge, arrays shape-merge,
|
|
125
|
+
* everything else: override wins. Python `deep_merge` parity.
|
|
126
|
+
*/
|
|
127
|
+
function deepMerge(base, override) {
|
|
128
|
+
if (isPlainObject(base) && isPlainObject(override)) {
|
|
129
|
+
const result = { ...base };
|
|
130
|
+
for (const [key, overVal] of Object.entries(override)) {
|
|
131
|
+
// hasOwn, not `in`: `'constructor' in result` is true via the
|
|
132
|
+
// prototype chain even when the base layer never declared it, which
|
|
133
|
+
// would deep-merge the override into Object's constructor function.
|
|
134
|
+
safeSet(result, key, hasOwn(result, key) ? deepMerge(result[key], overVal) : overVal);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
if (Array.isArray(base) && Array.isArray(override)) return mergeArrays(base, override);
|
|
139
|
+
return override;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Load and parse a TOML layer. Missing file → null (layer absent).
|
|
144
|
+
* Parse/read failure → null + warning (fail-open, Python parity where
|
|
145
|
+
* optional layers warn and degrade to {}).
|
|
146
|
+
*/
|
|
147
|
+
function loadTomlLayer(filePath, warnings) {
|
|
148
|
+
let raw;
|
|
149
|
+
try {
|
|
150
|
+
raw = nodeFs.readFileSync(filePath, 'utf8');
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const parsed = parseToml(raw);
|
|
156
|
+
return isPlainObject(parsed) ? parsed : null;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
warnings.push(`failed to parse ${filePath}: ${error.message}`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Generator ───
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Recursively collect paths of every customize.toml under rootDir.
|
|
167
|
+
* Skips `_resolved` itself and hidden dirs.
|
|
168
|
+
*/
|
|
169
|
+
function findCustomizeTomls(rootDir) {
|
|
170
|
+
const found = [];
|
|
171
|
+
const stack = [rootDir];
|
|
172
|
+
while (stack.length > 0) {
|
|
173
|
+
const dir = stack.pop();
|
|
174
|
+
let entries;
|
|
175
|
+
try {
|
|
176
|
+
entries = nodeFs.readdirSync(dir, { withFileTypes: true });
|
|
177
|
+
} catch {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
for (const entry of entries) {
|
|
181
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
182
|
+
const full = path.join(dir, entry.name);
|
|
183
|
+
if (entry.isDirectory()) {
|
|
184
|
+
if (full === path.join(rootDir, '_resolved')) continue;
|
|
185
|
+
stack.push(full);
|
|
186
|
+
} else if (entry.name === 'customize.toml') {
|
|
187
|
+
found.push(full);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return found.sort();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Names of every skill under `wizzDir` that ships a `customize.toml`
|
|
196
|
+
* (i.e. every skill the generator will snapshot).
|
|
197
|
+
* @param {string} wizzDir
|
|
198
|
+
* @returns {string[]} sorted, de-duplicated skill names
|
|
199
|
+
*/
|
|
200
|
+
function listSkillNames(wizzDir) {
|
|
201
|
+
const names = new Set(findCustomizeTomls(wizzDir).map((p) => path.basename(path.dirname(p))));
|
|
202
|
+
return [...names].sort();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Every `customize.toml` a project carries, by skill name — the `_wizz/`
|
|
207
|
+
* tree AND the deployed copies in each IDE's skills folder
|
|
208
|
+
* (`<projectRoot>/.<ide>/skills/<skill>/customize.toml`). They are not the
|
|
209
|
+
* same set: the verbatim pipeline moves each skill directory OUT of
|
|
210
|
+
* `_wizz/` into the IDE folders, so after an install the file the user is
|
|
211
|
+
* told to edit lives at `.claude/skills/<skill>/customize.toml` while
|
|
212
|
+
* `_wizz/` keeps only the shared includes. Shallow scan on purpose.
|
|
213
|
+
*
|
|
214
|
+
* Deterministic by construction: every readdir is sorted, so the order of
|
|
215
|
+
* the paths a skill maps to never depends on the order the filesystem
|
|
216
|
+
* happens to hand back its entries (`_wizz/` staged copy first, then the
|
|
217
|
+
* deployed copies with the IDE directories sorted by name).
|
|
218
|
+
* @param {object} args
|
|
219
|
+
* @param {string} args.projectRoot
|
|
220
|
+
* @param {string} args.wizzDir
|
|
221
|
+
* @returns {Map<string, string[]>} skill name → absolute paths, `_wizz/` first
|
|
222
|
+
*/
|
|
223
|
+
function discoverSkillCustomizeTomls({ projectRoot, wizzDir }) {
|
|
224
|
+
const found = new Map();
|
|
225
|
+
const add = (filePath) => {
|
|
226
|
+
const name = path.basename(path.dirname(filePath));
|
|
227
|
+
if (!found.has(name)) found.set(name, []);
|
|
228
|
+
const list = found.get(name);
|
|
229
|
+
if (!list.includes(filePath)) list.push(filePath);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
for (const filePath of findCustomizeTomls(wizzDir)) add(filePath);
|
|
233
|
+
|
|
234
|
+
let dotDirs = [];
|
|
235
|
+
try {
|
|
236
|
+
dotDirs = nodeFs
|
|
237
|
+
.readdirSync(projectRoot, { withFileTypes: true })
|
|
238
|
+
.filter((e) => e.isDirectory() && e.name.startsWith('.') && e.name !== '.git')
|
|
239
|
+
.map((e) => e.name)
|
|
240
|
+
.sort();
|
|
241
|
+
} catch {
|
|
242
|
+
return found;
|
|
243
|
+
}
|
|
244
|
+
for (const dot of dotDirs) {
|
|
245
|
+
const skillsDir = path.join(projectRoot, dot, 'skills');
|
|
246
|
+
let entries;
|
|
247
|
+
try {
|
|
248
|
+
entries = nodeFs
|
|
249
|
+
.readdirSync(skillsDir, { withFileTypes: true })
|
|
250
|
+
.filter((e) => e.isDirectory())
|
|
251
|
+
.map((e) => e.name)
|
|
252
|
+
.sort();
|
|
253
|
+
} catch {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
for (const entry of entries) {
|
|
257
|
+
const candidate = path.join(skillsDir, entry, 'customize.toml');
|
|
258
|
+
if (nodeFs.existsSync(candidate)) add(candidate);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return found;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** The IDE directory `.claude/skills/<skill>/customize.toml` is deployed from. */
|
|
265
|
+
const PRIMARY_IDE_DIR = '.claude';
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Rank a deployed copy: `.claude/` first (the canonical IDE folder — it is
|
|
269
|
+
* what wizz-customize points the user at and what the docs name), then every
|
|
270
|
+
* other IDE folder alphabetically. Never filesystem order.
|
|
271
|
+
*/
|
|
272
|
+
function deployedRank(projectRoot, filePath) {
|
|
273
|
+
const rel = path.relative(projectRoot, filePath);
|
|
274
|
+
const ideDir = rel.split(path.sep)[0];
|
|
275
|
+
return [ideDir === PRIMARY_IDE_DIR ? 0 : 1, rel];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* ONE customize.toml per skill, DEPLOYED copy preferred over the `_wizz/`
|
|
280
|
+
* staged copy. After an install the file the user edits (and the one
|
|
281
|
+
* wizz-customize points at) is `.claude/skills/<skill>/customize.toml`; the
|
|
282
|
+
* staged copy under `_wizz/` is only what shipped. `wizz resolve` must
|
|
283
|
+
* regenerate from what the user edits, so it asks for this list.
|
|
284
|
+
*
|
|
285
|
+
* The choice is deterministic, never filesystem order: a project that
|
|
286
|
+
* deploys to several IDE folders (`.claude/`, `.agents/`, `.cline/`, …)
|
|
287
|
+
* carries the same skill several times, and picking "whichever readdir
|
|
288
|
+
* returned first" made `wizz resolve` resolve from a different copy on a
|
|
289
|
+
* different machine. Order is: `.claude/skills/<skill>/customize.toml`, then
|
|
290
|
+
* the other deployed copies by path, then the `_wizz/` staged copy. When two
|
|
291
|
+
* or more deployed copies exist AND their bytes differ, that is a real
|
|
292
|
+
* divergence (one IDE folder was edited and the others were not) and it
|
|
293
|
+
* warns, naming both paths and the one that won.
|
|
294
|
+
*
|
|
295
|
+
* @param {object} args
|
|
296
|
+
* @param {string} args.projectRoot
|
|
297
|
+
* @param {string} args.wizzDir
|
|
298
|
+
* @param {string[]} [args.warnings] - sink for divergence warnings
|
|
299
|
+
* @returns {string[]} absolute paths, ordered by skill name
|
|
300
|
+
*/
|
|
301
|
+
function preferredCustomizePaths({ projectRoot, wizzDir, warnings }) {
|
|
302
|
+
const byName = discoverSkillCustomizeTomls({ projectRoot, wizzDir });
|
|
303
|
+
const chosen = [];
|
|
304
|
+
for (const name of [...byName.keys()].sort()) {
|
|
305
|
+
const paths = byName.get(name);
|
|
306
|
+
const deployed = paths
|
|
307
|
+
.filter((p) => {
|
|
308
|
+
const rel = path.relative(wizzDir, p);
|
|
309
|
+
return rel.startsWith('..') || path.isAbsolute(rel);
|
|
310
|
+
})
|
|
311
|
+
.sort((a, b) => {
|
|
312
|
+
const [rankA, relA] = deployedRank(projectRoot, a);
|
|
313
|
+
const [rankB, relB] = deployedRank(projectRoot, b);
|
|
314
|
+
if (rankA !== rankB) return rankA - rankB;
|
|
315
|
+
return relA < relB ? -1 : relA > relB ? 1 : 0;
|
|
316
|
+
});
|
|
317
|
+
const winner = deployed[0] || paths[0];
|
|
318
|
+
chosen.push(winner);
|
|
319
|
+
|
|
320
|
+
if (warnings && deployed.length > 1) {
|
|
321
|
+
const winnerHash = sha256File(winner);
|
|
322
|
+
const divergent = deployed.slice(1).filter((p) => sha256File(p) !== winnerHash);
|
|
323
|
+
if (divergent.length > 0) {
|
|
324
|
+
warnings.push(
|
|
325
|
+
`skill ${name}: deployed customize.toml copies diverge — ${[winner, ...divergent].join(' vs ')}; ` +
|
|
326
|
+
`resolved from ${winner} (run wizz update to redeploy the others)`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return chosen;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** Human-readable locations `wizz resolve` looked in, for the not-found message. */
|
|
335
|
+
function searchedLocations({ projectRoot, wizzDir, skillName }) {
|
|
336
|
+
return [path.join(wizzDir, '**', skillName, 'customize.toml'), path.join(projectRoot, '.<ide>', 'skills', skillName, 'customize.toml')];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ─── Inputs index (staleness detection, audit item 19 / bug 4) ───
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* sha256 of a file's bytes, or null when it can't be read. Same digest the
|
|
343
|
+
* installer's `calculateFileHash` produces, so doctor can compare either way.
|
|
344
|
+
*/
|
|
345
|
+
function sha256File(absPath) {
|
|
346
|
+
try {
|
|
347
|
+
return crypto.createHash('sha256').update(nodeFs.readFileSync(absPath)).digest('hex');
|
|
348
|
+
} catch {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Path as recorded in `.index.json`: POSIX, relative to the PROJECT root,
|
|
355
|
+
* with anything inside the install rewritten under `_wizz/` so a fresh
|
|
356
|
+
* install (which resolves inside a throwaway `_wizz.tmp-<pid>/` scaffold)
|
|
357
|
+
* records the path the file will have after the atomic swap.
|
|
358
|
+
*/
|
|
359
|
+
function indexRelativePath(projectRoot, wizzDir, absPath) {
|
|
360
|
+
const toPosix = (p) => p.split(path.sep).join('/');
|
|
361
|
+
const relToWizz = path.relative(wizzDir, absPath);
|
|
362
|
+
if (relToWizz && !relToWizz.startsWith('..') && !path.isAbsolute(relToWizz)) {
|
|
363
|
+
return `${WIZZ_FOLDER_NAME}/${toPosix(relToWizz)}`;
|
|
364
|
+
}
|
|
365
|
+
return toPosix(path.relative(projectRoot, absPath));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Hash every input that exists into `{ "<relative path>": "<sha256>" }`,
|
|
370
|
+
* sorted by path so the index is byte-stable across runs. Pure: no writes,
|
|
371
|
+
* no warnings. Inputs that do not exist are omitted — a layer the user has
|
|
372
|
+
* not created yet has nothing to hash, so `wizz doctor` cannot see a
|
|
373
|
+
* brand-new team override (install/update/resolve regenerate regardless).
|
|
374
|
+
* @param {object} args
|
|
375
|
+
* @param {string} args.projectRoot
|
|
376
|
+
* @param {string} args.wizzDir
|
|
377
|
+
* @param {string[]} args.inputPaths - absolute paths
|
|
378
|
+
* @returns {Object<string, string>}
|
|
379
|
+
*/
|
|
380
|
+
function computeInputsIndex({ projectRoot, wizzDir, inputPaths }) {
|
|
381
|
+
const pairs = [];
|
|
382
|
+
const seen = new Set();
|
|
383
|
+
for (const abs of inputPaths) {
|
|
384
|
+
if (seen.has(abs)) continue;
|
|
385
|
+
seen.add(abs);
|
|
386
|
+
const hash = sha256File(abs);
|
|
387
|
+
if (hash === null) continue;
|
|
388
|
+
pairs.push([indexRelativePath(projectRoot, wizzDir, abs), hash]);
|
|
389
|
+
}
|
|
390
|
+
pairs.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
391
|
+
return Object.fromEntries(pairs);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Read `_resolved/.index.json`. Missing/unreadable/malformed → null (the
|
|
396
|
+
* caller treats that as "never generated"), never throws.
|
|
397
|
+
* @param {string} resolvedDir
|
|
398
|
+
* @returns {object|null}
|
|
399
|
+
*/
|
|
400
|
+
function readIndex(resolvedDir) {
|
|
401
|
+
try {
|
|
402
|
+
const parsed = JSON.parse(nodeFs.readFileSync(path.join(resolvedDir, INDEX_FILE), 'utf8'));
|
|
403
|
+
return isPlainObject(parsed) ? parsed : null;
|
|
404
|
+
} catch {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ─── Snapshot writing ───
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Resolve and write ONE skill's snapshot.
|
|
413
|
+
*
|
|
414
|
+
* The base layer is load-bearing: a snapshot written from a `{}` base would
|
|
415
|
+
* be a fresh, valid-looking file with no persona and no persistent_facts,
|
|
416
|
+
* and the runtime reads it unconditionally — the agent would activate empty
|
|
417
|
+
* with no signal. So a broken base is a SKIP: any stale snapshot is
|
|
418
|
+
* deleted, the runtime falls back to `resolve_customization.py`, and the
|
|
419
|
+
* user is warned. Team/user layers stay fail-open (losing an override
|
|
420
|
+
* degrades to the shipped defaults, a safe state).
|
|
421
|
+
*
|
|
422
|
+
* @returns {Promise<{skillName: string, status: 'written'|'skipped', warnings: string[], entry: object|null}>}
|
|
423
|
+
*/
|
|
424
|
+
async function resolveSkillSnapshot({ customizePath, customDir, resolvedDir, projectRoot, wizzDir }) {
|
|
425
|
+
const skillName = path.basename(path.dirname(customizePath));
|
|
426
|
+
const warnings = [];
|
|
427
|
+
const outPath = path.join(resolvedDir, `${skillName}.json`);
|
|
428
|
+
|
|
429
|
+
const baseRaw = loadTomlLayer(customizePath, warnings);
|
|
430
|
+
if (!isPlainObject(baseRaw)) {
|
|
431
|
+
warnings.push(
|
|
432
|
+
`base customize.toml inválido ou ilegível (${customizePath}) — snapshot NÃO gravado; ` +
|
|
433
|
+
`a ativação cai no fallback resolve_customization.py até você corrigir o arquivo e rodar wizz resolve`,
|
|
434
|
+
);
|
|
435
|
+
await fs.remove(outPath);
|
|
436
|
+
return { skillName, status: 'skipped', warnings, entry: null };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const inputPaths = [customizePath];
|
|
440
|
+
const teamPath = path.join(customDir, `${skillName}.toml`);
|
|
441
|
+
const userPath = path.join(customDir, `${skillName}.user.toml`);
|
|
442
|
+
inputPaths.push(teamPath, userPath);
|
|
443
|
+
|
|
444
|
+
// `context` carries the `_shared/` fallback: a skill deployed to
|
|
445
|
+
// `.claude/skills/<skill>/` keeps `include = ["../../_shared/x.md"]`, which
|
|
446
|
+
// from there points at a directory that does not exist. See resolved-includes.js.
|
|
447
|
+
const context = { projectRoot, skillName };
|
|
448
|
+
const defaults = resolveIncludes(baseRaw, path.dirname(customizePath), warnings, inputPaths, context);
|
|
449
|
+
const teamRaw = loadTomlLayer(teamPath, warnings);
|
|
450
|
+
const userRaw = loadTomlLayer(userPath, warnings);
|
|
451
|
+
const team = resolveIncludes(teamRaw || {}, customDir, warnings, inputPaths, context);
|
|
452
|
+
const user = resolveIncludes(userRaw || {}, customDir, warnings, inputPaths, context);
|
|
453
|
+
|
|
454
|
+
// Audit item 24: closed-key warnings for user-authored layers. The base
|
|
455
|
+
// layer is exempt (it owns the LLM-only fields and new experimental
|
|
456
|
+
// keys); a typo here is exactly the silent-merge the audit flagged.
|
|
457
|
+
// Warnings, never errors — the snapshot is still written (fail-open).
|
|
458
|
+
for (const [layerName, layer] of [
|
|
459
|
+
['team', teamRaw],
|
|
460
|
+
['user', userRaw],
|
|
461
|
+
]) {
|
|
462
|
+
for (const unknown of findUnknownKeys(layer)) {
|
|
463
|
+
warnings.push(`${layerName} override: unknown key "${unknown}" (not a recognized workflow field — typo? check customize.toml docs)`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const merged = deepMerge(deepMerge(defaults, team), user);
|
|
468
|
+
const body = `${JSON.stringify(merged, null, 2)}\n`;
|
|
469
|
+
// Atomic write via fs-native (tmp + rename), consistent with every other
|
|
470
|
+
// config file the installer writes.
|
|
471
|
+
await fs.writeFile(outPath, body, 'utf8');
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
skillName,
|
|
475
|
+
status: 'written',
|
|
476
|
+
warnings,
|
|
477
|
+
entry: {
|
|
478
|
+
inputs: computeInputsIndex({ projectRoot, wizzDir, inputPaths }),
|
|
479
|
+
output: crypto.createHash('sha256').update(body).digest('hex'),
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Remove `_resolved/*.json` snapshots whose skill no longer exists (a
|
|
486
|
+
* module uninstalled, a skill renamed). `.index.json` and `.gitignore`
|
|
487
|
+
* are never touched here.
|
|
488
|
+
* @returns {Promise<string[]>} names of the pruned files
|
|
489
|
+
*/
|
|
490
|
+
async function pruneOrphanSnapshots(resolvedDir, keepNames) {
|
|
491
|
+
let entries;
|
|
492
|
+
try {
|
|
493
|
+
entries = nodeFs.readdirSync(resolvedDir, { withFileTypes: true });
|
|
494
|
+
} catch {
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
const pruned = [];
|
|
498
|
+
for (const entry of entries) {
|
|
499
|
+
if (!entry.isFile()) continue;
|
|
500
|
+
if (entry.name === INDEX_FILE || entry.name === GITIGNORE_FILE) continue;
|
|
501
|
+
if (!entry.name.endsWith('.json')) continue;
|
|
502
|
+
if (keepNames.has(entry.name)) continue;
|
|
503
|
+
await fs.remove(path.join(resolvedDir, entry.name));
|
|
504
|
+
pruned.push(entry.name);
|
|
505
|
+
}
|
|
506
|
+
return pruned;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/** Write `_resolved/.gitignore` (generated artifacts stay out of git). */
|
|
510
|
+
async function writeResolvedGitignore(resolvedDir) {
|
|
511
|
+
await fs.writeFile(path.join(resolvedDir, GITIGNORE_FILE), GITIGNORE_BODY, 'utf8');
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Write `_resolved/.index.json` — the staleness oracle. Activation never
|
|
516
|
+
* reads it (it reads the snapshot and nothing else); `wizz doctor` does,
|
|
517
|
+
* comparing each recorded input hash against the file on disk.
|
|
518
|
+
*
|
|
519
|
+
* `config` carries the same shape for `_config.json` (the central-config
|
|
520
|
+
* snapshot, item 29); null when that snapshot could not be written.
|
|
521
|
+
*/
|
|
522
|
+
async function writeResolvedIndex(resolvedDir, skills, config = null) {
|
|
523
|
+
const sorted = Object.fromEntries(Object.entries(skills).sort((a, b) => (a[0] < b[0] ? -1 : 1)));
|
|
524
|
+
const index = { version: INDEX_VERSION, generated_at: new Date().toISOString(), skills: sorted, config };
|
|
525
|
+
await fs.writeFile(path.join(resolvedDir, INDEX_FILE), `${JSON.stringify(index, null, 2)}\n`, 'utf8');
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Generate `_wizz/_resolved/<skill-name>.json` for every skill dir under
|
|
530
|
+
* `wizzDir` that carries a `customize.toml`, plus `.index.json` (input
|
|
531
|
+
* hashes for staleness detection) and `.gitignore`.
|
|
532
|
+
*
|
|
533
|
+
* The base layer comes from the freshly written tree (`wizzDir`, which may
|
|
534
|
+
* be the A14 tmp scaffold on a fresh install), but the team/user layers
|
|
535
|
+
* are read from the REAL project's `_wizz/custom/` — user customizations
|
|
536
|
+
* survive a fresh install, so the snapshot must reflect them even when
|
|
537
|
+
* `wizzDir` is a throwaway scaffold that never saw them.
|
|
538
|
+
*
|
|
539
|
+
* Nothing written here is tracked in `files-manifest.csv` — see the header.
|
|
540
|
+
*
|
|
541
|
+
* @param {Object} options
|
|
542
|
+
* @param {string} options.wizzDir - The (freshly written) `_wizz/` dir.
|
|
543
|
+
* @param {string} options.projectRoot - Real project root (for `_wizz/custom/`).
|
|
544
|
+
* @param {Function} [options.warn] - Async warn sink (defaults to console).
|
|
545
|
+
* @returns {Promise<{written: number, skipped: number, pruned: string[], warnings: string[]}>}
|
|
546
|
+
*/
|
|
547
|
+
async function resolveAll({ wizzDir, projectRoot, warn, includeDeployed = false }) {
|
|
548
|
+
const warnSink = warn || (async (msg) => console.warn(msg));
|
|
549
|
+
const customDir = path.join(projectRoot, WIZZ_FOLDER_NAME, 'custom');
|
|
550
|
+
const resolvedDir = path.join(wizzDir, '_resolved');
|
|
551
|
+
const emitted = [];
|
|
552
|
+
const emit = async (line) => {
|
|
553
|
+
emitted.push(line);
|
|
554
|
+
await warnSink(line);
|
|
555
|
+
};
|
|
556
|
+
// Install/update stay on the staged tree (`wizzDir` may be the A14 tmp
|
|
557
|
+
// scaffold, and the deployed copies still belong to the PREVIOUS install).
|
|
558
|
+
// `wizz resolve` opts into the deployed copies, which is what the user edits.
|
|
559
|
+
const discoveryWarnings = [];
|
|
560
|
+
const customizePaths = includeDeployed
|
|
561
|
+
? preferredCustomizePaths({ projectRoot, wizzDir, warnings: discoveryWarnings })
|
|
562
|
+
: findCustomizeTomls(wizzDir);
|
|
563
|
+
for (const warning of discoveryWarnings) await emit(`_resolved: ${warning}`);
|
|
564
|
+
|
|
565
|
+
// NO early return on an empty list: a project that just lost its last
|
|
566
|
+
// customize.toml still needs its orphan snapshots pruned and an empty
|
|
567
|
+
// index written, otherwise `_resolved/` keeps serving stale snapshots for
|
|
568
|
+
// skills that no longer exist and doctor compares against an old index.
|
|
569
|
+
await fs.mkdir(resolvedDir, { recursive: true });
|
|
570
|
+
let written = 0;
|
|
571
|
+
let skipped = 0;
|
|
572
|
+
const skills = {};
|
|
573
|
+
const keepNames = new Set();
|
|
574
|
+
|
|
575
|
+
for (const customizePath of customizePaths) {
|
|
576
|
+
const skillName = path.basename(path.dirname(customizePath));
|
|
577
|
+
keepNames.add(`${skillName}.json`);
|
|
578
|
+
let result;
|
|
579
|
+
try {
|
|
580
|
+
result = await resolveSkillSnapshot({ customizePath, customDir, resolvedDir, projectRoot, wizzDir });
|
|
581
|
+
} catch (error) {
|
|
582
|
+
// Fail-open: one broken skill never blocks the install.
|
|
583
|
+
result = { skillName, status: 'skipped', warnings: [`skill ${skillName}: ${error.message}`], entry: null };
|
|
584
|
+
}
|
|
585
|
+
if (result.status === 'written') {
|
|
586
|
+
written++;
|
|
587
|
+
skills[result.skillName] = result.entry;
|
|
588
|
+
} else {
|
|
589
|
+
skipped++;
|
|
590
|
+
}
|
|
591
|
+
for (const warning of result.warnings) await emit(`_resolved/${result.skillName}: ${warning}`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Conservador de propósito: só é órfão o snapshot de uma skill que não
|
|
595
|
+
// tem customize.toml em lugar nenhum do projeto. O pipeline verbatim move
|
|
596
|
+
// os diretórios de skill de `_wizz/` para as pastas dos IDEs, então "não
|
|
597
|
+
// está no wizzDir" NÃO significa "foi desinstalada" — podar por esse
|
|
598
|
+
// critério apagaria snapshots válidos.
|
|
599
|
+
for (const name of discoverSkillCustomizeTomls({ projectRoot, wizzDir }).keys()) keepNames.add(`${name}.json`);
|
|
600
|
+
|
|
601
|
+
// Central config (item 29): `_resolved/_config.json` replaces the runtime
|
|
602
|
+
// `python3 resolve_config.py` call. Lazy require — resolved-config.js
|
|
603
|
+
// imports this module's merge rules, so a top-level require here would
|
|
604
|
+
// close the cycle and hand it a half-built exports object.
|
|
605
|
+
const { writeResolvedConfig, CONFIG_SNAPSHOT } = require('./resolved-config');
|
|
606
|
+
keepNames.add(CONFIG_SNAPSHOT);
|
|
607
|
+
const config = await writeResolvedConfig({ projectRoot, wizzDir, resolvedDir });
|
|
608
|
+
for (const warning of config.warnings) await emit(`_resolved/${CONFIG_SNAPSHOT}: ${warning}`);
|
|
609
|
+
|
|
610
|
+
const pruned = await pruneOrphanSnapshots(resolvedDir, keepNames);
|
|
611
|
+
await writeResolvedGitignore(resolvedDir);
|
|
612
|
+
await writeResolvedIndex(resolvedDir, skills, config.entry);
|
|
613
|
+
|
|
614
|
+
return { written, skipped, pruned, warnings: emitted, config: config.written };
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Regenerate ONE skill's snapshot in place, preserving every other entry of
|
|
619
|
+
* `.index.json`. Used by `wizz resolve --skill <name>` (and by anything that
|
|
620
|
+
* edits a single customize layer, e.g. the wizz-customize skill).
|
|
621
|
+
* @param {string} projectRoot
|
|
622
|
+
* @param {string} wizzDir
|
|
623
|
+
* @param {string} skillName
|
|
624
|
+
* When `.index.json` is missing, unreadable or malformed there is no "every
|
|
625
|
+
* other entry" left to preserve: rewriting it with only this skill would
|
|
626
|
+
* silently drop every other skill from the staleness oracle, and doctor
|
|
627
|
+
* would then report them as "sem snapshot" forever. So that case falls back
|
|
628
|
+
* to the all-skills path (`resolveAll`), which rebuilds the FULL index.
|
|
629
|
+
*
|
|
630
|
+
* @param {string} projectRoot
|
|
631
|
+
* @param {string} wizzDir
|
|
632
|
+
* @param {string} skillName
|
|
633
|
+
* @param {object} [options] - { warn, includeDeployed }
|
|
634
|
+
* @returns {Promise<{written: number, skipped: number, warnings: string[], found: boolean}>}
|
|
635
|
+
*/
|
|
636
|
+
async function resolveOne(projectRoot, wizzDir, skillName, options = {}) {
|
|
637
|
+
const warnSink = options.warn || (async (msg) => console.warn(msg));
|
|
638
|
+
const customDir = path.join(projectRoot, WIZZ_FOLDER_NAME, 'custom');
|
|
639
|
+
const resolvedDir = path.join(wizzDir, '_resolved');
|
|
640
|
+
const discoveryWarnings = [];
|
|
641
|
+
const candidates = options.includeDeployed
|
|
642
|
+
? preferredCustomizePaths({ projectRoot, wizzDir, warnings: discoveryWarnings })
|
|
643
|
+
: findCustomizeTomls(wizzDir);
|
|
644
|
+
const customizePath = candidates.find((p) => path.basename(path.dirname(p)) === skillName);
|
|
645
|
+
const emitted = [];
|
|
646
|
+
if (!customizePath) {
|
|
647
|
+
return { written: 0, skipped: 0, warnings: emitted, found: false, searched: searchedLocations({ projectRoot, wizzDir, skillName }) };
|
|
648
|
+
}
|
|
649
|
+
await fs.mkdir(resolvedDir, { recursive: true });
|
|
650
|
+
|
|
651
|
+
const previous = readIndex(resolvedDir);
|
|
652
|
+
if (!previous || !isPlainObject(previous.skills)) {
|
|
653
|
+
// (the fallback below re-derives the discovery warnings itself)
|
|
654
|
+
// No usable index: regenerate ALL of it rather than replacing it with a
|
|
655
|
+
// one-entry index that erases every other skill.
|
|
656
|
+
const all = await resolveAll({ wizzDir, projectRoot, warn: options.warn, includeDeployed: options.includeDeployed });
|
|
657
|
+
const wrote = nodeFs.existsSync(path.join(resolvedDir, `${skillName}.json`));
|
|
658
|
+
return {
|
|
659
|
+
written: wrote ? 1 : 0,
|
|
660
|
+
skipped: wrote ? 0 : 1,
|
|
661
|
+
warnings: [...emitted, ...all.warnings],
|
|
662
|
+
found: true,
|
|
663
|
+
customizePath,
|
|
664
|
+
regeneratedIndex: true,
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Only the divergence warning for THIS skill is relevant to a --skill run.
|
|
669
|
+
for (const warning of discoveryWarnings.filter((w) => w.startsWith(`skill ${skillName}:`))) {
|
|
670
|
+
const line = `_resolved: ${warning}`;
|
|
671
|
+
emitted.push(line);
|
|
672
|
+
await warnSink(line);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
let result;
|
|
676
|
+
try {
|
|
677
|
+
result = await resolveSkillSnapshot({ customizePath, customDir, resolvedDir, projectRoot, wizzDir });
|
|
678
|
+
} catch (error) {
|
|
679
|
+
result = { skillName, status: 'skipped', warnings: [`skill ${skillName}: ${error.message}`], entry: null };
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const skills = { ...previous.skills };
|
|
683
|
+
if (result.status === 'written') {
|
|
684
|
+
skills[skillName] = result.entry;
|
|
685
|
+
} else {
|
|
686
|
+
delete skills[skillName];
|
|
687
|
+
}
|
|
688
|
+
await writeResolvedGitignore(resolvedDir);
|
|
689
|
+
// A --skill run touches one snapshot: the central-config entry of the
|
|
690
|
+
// index it is patching must survive untouched, not be dropped to null.
|
|
691
|
+
await writeResolvedIndex(resolvedDir, skills, previous.config || null);
|
|
692
|
+
|
|
693
|
+
for (const warning of result.warnings) {
|
|
694
|
+
const line = `_resolved/${skillName}: ${warning}`;
|
|
695
|
+
emitted.push(line);
|
|
696
|
+
await warnSink(line);
|
|
697
|
+
}
|
|
698
|
+
return {
|
|
699
|
+
written: result.status === 'written' ? 1 : 0,
|
|
700
|
+
skipped: result.status === 'skipped' ? 1 : 0,
|
|
701
|
+
warnings: emitted,
|
|
702
|
+
found: true,
|
|
703
|
+
customizePath,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Back-compat wrapper kept for the installer call site: same behaviour as
|
|
709
|
+
* `resolveAll`, but returns just the number of snapshots written.
|
|
710
|
+
* @returns {Promise<number>}
|
|
711
|
+
*/
|
|
712
|
+
async function generateResolvedCustomizations(options) {
|
|
713
|
+
const { written } = await resolveAll(options);
|
|
714
|
+
return written;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
module.exports = {
|
|
718
|
+
generateResolvedCustomizations,
|
|
719
|
+
resolveAll,
|
|
720
|
+
resolveOne,
|
|
721
|
+
deepMerge,
|
|
722
|
+
loadTomlLayer,
|
|
723
|
+
resolveIncludes,
|
|
724
|
+
splitIncludeParagraphs,
|
|
725
|
+
findUnknownKeys,
|
|
726
|
+
listSkillNames,
|
|
727
|
+
discoverSkillCustomizeTomls,
|
|
728
|
+
preferredCustomizePaths,
|
|
729
|
+
computeInputsIndex,
|
|
730
|
+
indexRelativePath,
|
|
731
|
+
readIndex,
|
|
732
|
+
writeResolvedIndex,
|
|
733
|
+
sha256File,
|
|
734
|
+
INDEX_FILE,
|
|
735
|
+
GITIGNORE_FILE,
|
|
736
|
+
};
|