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,648 @@
|
|
|
1
|
+
// doctor.js — diagnóstico do projeto Wizz (`wizz doctor`, item 17 da
|
|
2
|
+
// auditoria 2026-09-11). Fecha os achados RT-M3 (drift silencioso entre
|
|
3
|
+
// _wizz/ e o pacote) e RT-M4 (cadeia de tools quebrada sem sinal):
|
|
4
|
+
// um comando que verifica, com saída legível, os 6 pontos que o dia a dia
|
|
5
|
+
// do Wizz Method depende:
|
|
6
|
+
//
|
|
7
|
+
// 1. hooks — arquivos em `_wizz/hooks/` + registro em `.claude/settings.json`
|
|
8
|
+
// 2. rtk — binário no PATH + versão (piso do hook: 0.23.0)
|
|
9
|
+
// 3. jq — binário no PATH (o hook rtk-rewrite.sh precisa dele)
|
|
10
|
+
// 4. python — informativo: `python3` é OPCIONAL desde o item 29 (só as
|
|
11
|
+
// skills BMAD com scripts próprios usam); reusa python-check.js
|
|
12
|
+
// 5. drift — hashes do files-manifest.csv vs os arquivos instalados em _wizz/
|
|
13
|
+
// 6. resolved — snapshots de `_wizz/_resolved/` contra o hash das entradas
|
|
14
|
+
// que os geraram (`.index.json`)
|
|
15
|
+
//
|
|
16
|
+
// Forma dos resultados: cada check devolve
|
|
17
|
+
// { check, status: 'ok'|'warn'|'error', detail }
|
|
18
|
+
// e o agregador devolve { results, exitCode } seguindo a convenção do repo
|
|
19
|
+
// (0 = ok/warn, 1 = error). Toda detecção de ambiente é INJETÁVEL: os
|
|
20
|
+
// checkers (hasCommand, runVersion, pythonDetect, ...) chegam por `deps`
|
|
21
|
+
// — os testes rodam sem rtk/jq/python instalados e sem rede.
|
|
22
|
+
|
|
23
|
+
const path = require('node:path');
|
|
24
|
+
const { HOOK_DEFS, isOurHookCommand } = require('./hooks-settings');
|
|
25
|
+
const { classifyPython, PYTHON_ONLY_SKILLS } = require('./python-check');
|
|
26
|
+
|
|
27
|
+
// Piso de versão do rtk que o hook rtk-rewrite.sh exige para `rtk rewrite`
|
|
28
|
+
// (o registry pin do CLI é mais alto, 0.45.0, e é aplicado pelo detectClis
|
|
29
|
+
// no install; aqui reportamos o piso que quebra o hook hoje).
|
|
30
|
+
const RTK_HOOK_FLOOR = '0.23.0';
|
|
31
|
+
|
|
32
|
+
const CHECKS = ['hooks', 'rtk', 'jq', 'python', 'drift', 'resolved'];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Versão numérica comparável a partir de texto de `rtk --version` etc.
|
|
36
|
+
* (mesmo formato "x.y.z" que cli-config.js#extractVersion usa).
|
|
37
|
+
* @param {string} text
|
|
38
|
+
* @returns {string|null}
|
|
39
|
+
*/
|
|
40
|
+
function extractVersion(text) {
|
|
41
|
+
const m = String(text || '').match(/\d+\.\d+\.\d+/);
|
|
42
|
+
return m ? m[0] : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compara "x.y.z" por segmento numérico (a<b negativo, igual 0, a>b positivo).
|
|
47
|
+
* @param {string} a
|
|
48
|
+
* @param {string} b
|
|
49
|
+
* @returns {number}
|
|
50
|
+
*/
|
|
51
|
+
function compareSemver(a, b) {
|
|
52
|
+
const pa = String(a)
|
|
53
|
+
.split('.')
|
|
54
|
+
.map((n) => Number(n) || 0);
|
|
55
|
+
const pb = String(b)
|
|
56
|
+
.split('.')
|
|
57
|
+
.map((n) => Number(n) || 0);
|
|
58
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
59
|
+
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
60
|
+
if (diff !== 0) return diff;
|
|
61
|
+
}
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Lê CSV de uma linha só respeitando aspas duplas com vírgula/aspas dentro
|
|
67
|
+
* (suficiente pros manifests que o próprio installer escreve). Não suporta
|
|
68
|
+
* aspas escapadas embutidas — os manifests não geram esse caso.
|
|
69
|
+
* @param {string} line
|
|
70
|
+
* @returns {string[]}
|
|
71
|
+
*/
|
|
72
|
+
function parseCsvLine(line) {
|
|
73
|
+
const fields = [];
|
|
74
|
+
let current = '';
|
|
75
|
+
let inQuotes = false;
|
|
76
|
+
for (let i = 0; i < line.length; i++) {
|
|
77
|
+
const ch = line[i];
|
|
78
|
+
if (inQuotes) {
|
|
79
|
+
if (ch === '"') {
|
|
80
|
+
if (line[i + 1] === '"') {
|
|
81
|
+
current += '"';
|
|
82
|
+
i++;
|
|
83
|
+
} else {
|
|
84
|
+
inQuotes = false;
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
current += ch;
|
|
88
|
+
}
|
|
89
|
+
} else if (ch === '"') {
|
|
90
|
+
inQuotes = true;
|
|
91
|
+
} else if (ch === ',') {
|
|
92
|
+
fields.push(current);
|
|
93
|
+
current = '';
|
|
94
|
+
} else {
|
|
95
|
+
current += ch;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
fields.push(current);
|
|
99
|
+
return fields;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Checagem de hooks: para cada HOOK_DEF canônico, o arquivo existe em
|
|
104
|
+
* `_wizz/hooks/` e (para o settings) há uma entrada nossa no evento certo
|
|
105
|
+
* de `.claude/settings.json`? Hooks com `requires: 'rtk'` (rtk-rewrite.sh)
|
|
106
|
+
* degradam para warn quando o arquivo não existe — o installer também os
|
|
107
|
+
* pula quando rtk não está no PATH (RT-C1, item 4). Falta de registro no
|
|
108
|
+
* settings é warn (hook roda menos, não quebra), não error.
|
|
109
|
+
* @param {object} deps
|
|
110
|
+
* @param {string} deps.wizzDir
|
|
111
|
+
* @param {string} deps.settingsFile - .claude/settings.json do projeto
|
|
112
|
+
* @param {string} [deps.globalSettingsFile] - ~/.claude/settings.json (só para a mensagem)
|
|
113
|
+
* @param {(p: string) => boolean} deps.fileExists
|
|
114
|
+
* @param {() => object|null} deps.readProjectSettings
|
|
115
|
+
* @param {() => object|null} deps.readGlobalSettings - ~/.claude/settings.json
|
|
116
|
+
* @param {boolean} [deps.rtkOnPath] - igual ao installer: sem rtk no PATH,
|
|
117
|
+
* o registro do rtk-rewrite.sh não é exigido (ele é opcional, RT-C1 item 4)
|
|
118
|
+
* @returns {{check: 'hooks', status: 'ok'|'warn'|'error', detail: string}}
|
|
119
|
+
*/
|
|
120
|
+
function checkHooks({ wizzDir, settingsFile, globalSettingsFile, fileExists, readProjectSettings, readGlobalSettings, rtkOnPath = false }) {
|
|
121
|
+
// `wizzDir` já É o diretório _wizz/ do projeto; HOOKS_DIR ('_wizz/hooks')
|
|
122
|
+
// é relativo à raiz, então o subdiretório aqui é só 'hooks'.
|
|
123
|
+
const hooksDir = path.join(wizzDir, 'hooks');
|
|
124
|
+
const missingFiles = [];
|
|
125
|
+
const missingSettings = [];
|
|
126
|
+
const skippedGlobal = [];
|
|
127
|
+
|
|
128
|
+
const globalSettings = readGlobalSettings ? readGlobalSettings() : null;
|
|
129
|
+
const projectSettings = readProjectSettings ? readProjectSettings() : null;
|
|
130
|
+
|
|
131
|
+
for (const def of HOOK_DEFS) {
|
|
132
|
+
const fileOnDisk = fileExists(path.join(hooksDir, def.id));
|
|
133
|
+
if (!fileOnDisk) {
|
|
134
|
+
if (def.requires === 'rtk') {
|
|
135
|
+
// Opcional por design: o installer só registra rtk-rewrite.sh
|
|
136
|
+
// quando rtk está no PATH; ausência aqui é estado normal.
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
missingFiles.push(def.id);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Registrado no global? Mesma semântica de filterHooksRegisteredGlobally
|
|
144
|
+
// (hooks-settings.js): basename dentro do command, qualquer matcher do
|
|
145
|
+
// evento conta. O path global (~/.claude/hooks/...) nunca contém
|
|
146
|
+
// _wizz/hooks/, então isOurHookCommand não serve aqui — compara por id.
|
|
147
|
+
const registeredGlobally = (() => {
|
|
148
|
+
const blocks =
|
|
149
|
+
globalSettings && globalSettings.hooks && Array.isArray(globalSettings.hooks[def.event]) ? globalSettings.hooks[def.event] : [];
|
|
150
|
+
return blocks.some(
|
|
151
|
+
(block) => Array.isArray(block.hooks) && block.hooks.some((h) => h && typeof h.command === 'string' && h.command.includes(def.id)),
|
|
152
|
+
);
|
|
153
|
+
})();
|
|
154
|
+
if (registeredGlobally) {
|
|
155
|
+
skippedGlobal.push(def.id);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const blocks =
|
|
160
|
+
projectSettings && projectSettings.hooks && Array.isArray(projectSettings.hooks[def.event]) ? projectSettings.hooks[def.event] : [];
|
|
161
|
+
const registered = blocks.some(
|
|
162
|
+
(block) => Array.isArray(block.hooks) && block.hooks.some((h) => isOurHookCommand(h && h.command, def.id)),
|
|
163
|
+
);
|
|
164
|
+
if (!registered) {
|
|
165
|
+
// rtk-rewrite.sh presente em disco mas rtk ausente: o installer não
|
|
166
|
+
// o registra (RT-C1 item 4), então não cobrar registro aqui.
|
|
167
|
+
if (def.requires === 'rtk' && !rtkOnPath) continue;
|
|
168
|
+
missingSettings.push(def.id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (missingFiles.length > 0) {
|
|
173
|
+
return {
|
|
174
|
+
check: 'hooks',
|
|
175
|
+
status: 'error',
|
|
176
|
+
detail: `Faltam arquivos de hook em _wizz/hooks/: ${missingFiles.join(', ')}. Execute wizz update para repor.`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (missingSettings.length > 0) {
|
|
181
|
+
return {
|
|
182
|
+
check: 'hooks',
|
|
183
|
+
status: 'warn',
|
|
184
|
+
detail:
|
|
185
|
+
`Arquivos ok, mas sem registro no settings do PROJETO (${settingsFile}): ${missingSettings.join(', ')}. ` +
|
|
186
|
+
`Também não estão no settings GLOBAL (${globalSettingsFile || '~/.claude/settings.json'}). ` +
|
|
187
|
+
`Rode wizz install para registrar no projeto, ou registre no global.`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const skippedNote =
|
|
192
|
+
skippedGlobal.length > 0
|
|
193
|
+
? ` (${skippedGlobal.length} já registrado(s) no settings global ${globalSettingsFile || '~/.claude/settings.json'}, ok)`
|
|
194
|
+
: '';
|
|
195
|
+
return { check: 'hooks', status: 'ok', detail: `${HOOK_DEFS.length} hooks verificados${skippedNote}.` };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Checagem genérica de binário no PATH, com versão opcional contra um piso.
|
|
200
|
+
* `runVersion(cmd)` devolve stdout+stderr de `<cmd> --version` (ou null).
|
|
201
|
+
* @param {object} args
|
|
202
|
+
* @param {string} args.check - 'rtk' | 'jq'
|
|
203
|
+
* @param {string|null} args.version - saída de --version (null se ausente)
|
|
204
|
+
* @param {string} [args.floor] - versão mínima (só rtk)
|
|
205
|
+
* @returns {{check: 'rtk'|'jq', status: 'ok'|'warn'|'error', detail: string}}
|
|
206
|
+
*/
|
|
207
|
+
function checkCli({ check, version, floor }) {
|
|
208
|
+
if (check === 'rtk') {
|
|
209
|
+
if (!version) {
|
|
210
|
+
return {
|
|
211
|
+
check,
|
|
212
|
+
status: 'warn',
|
|
213
|
+
detail: `rtk não encontrado no PATH. Opcional, mas sem ele o hook rtk-rewrite.sh não é registrado e a economia de tokens não roda. Instale: cargo install rtk`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const installed = extractVersion(version);
|
|
217
|
+
if (installed && compareSemver(installed, RTK_HOOK_FLOOR) < 0) {
|
|
218
|
+
return {
|
|
219
|
+
check,
|
|
220
|
+
status: 'warn',
|
|
221
|
+
detail: `rtk ${installed} é mais antigo que o piso do hook (${RTK_HOOK_FLOOR}); o rewrite falha em warn. Atualize: cargo install rtk`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return { check, status: 'ok', detail: `rtk ${installed || '(versão não informada)'} no PATH.` };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// jq — sem verificação de versão, o hook só precisa dele presente.
|
|
228
|
+
if (!version) {
|
|
229
|
+
return {
|
|
230
|
+
check: 'jq',
|
|
231
|
+
status: 'warn',
|
|
232
|
+
detail:
|
|
233
|
+
'jq não encontrado no PATH. O hook rtk-rewrite.sh precisa dele; sem jq o rewrite de Bash avisa e segue sem reescrever. Instale: brew install jq',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return { check: 'jq', status: 'ok', detail: 'jq no PATH.' };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Checagem de python: INFORMATIVA desde o item 29 (2026-09-12). O núcleo do
|
|
241
|
+
* Wizz roda só com Node — memlog é `memlog.js` e as configs TOML viram
|
|
242
|
+
* `_wizz/_resolved/*.json` na instalação. Python só compra as skills BMAD que
|
|
243
|
+
* trazem script próprio, então a ausência dele nunca é `warn`: é `ok` com o
|
|
244
|
+
* detalhe dizendo o que fica de fora. Reusa classifyPython de python-check.js
|
|
245
|
+
* sobre o resultado do detector injetado (mesmo contrato de detectPython).
|
|
246
|
+
* @param {object} args
|
|
247
|
+
* @param {{command: string, version: {major: number, minor: number, patch: number, raw: string}, isRuntimeCommand: boolean}|null} args.detected
|
|
248
|
+
* @returns {{check: 'python', status: 'ok'|'warn'|'error', detail: string}}
|
|
249
|
+
*/
|
|
250
|
+
function checkPython({ detected }) {
|
|
251
|
+
const status = classifyPython(detected ? detected.version : null);
|
|
252
|
+
// Nunca warn: Python é opcional. O detalhe diz o que está disponível.
|
|
253
|
+
const detail = pythonDetail(status, detected);
|
|
254
|
+
return { check: 'python', status: 'ok', detail };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Frase do check de python por caso detectado. Toda variante fecha dizendo
|
|
259
|
+
* que o núcleo não depende disso, para ninguém ler "opcional" como "quebrado".
|
|
260
|
+
* @param {'full'|'partial'|'unsupported'|'none'} status
|
|
261
|
+
* @param {object|null} detected
|
|
262
|
+
* @returns {string}
|
|
263
|
+
*/
|
|
264
|
+
function pythonDetail(status, detected) {
|
|
265
|
+
const optional = `Opcional: o núcleo roda só com Node; Python só habilita as skills com script próprio (${PYTHON_ONLY_SKILLS}).`;
|
|
266
|
+
if (status === 'full' && detected.isRuntimeCommand) {
|
|
267
|
+
return `Python ${detected.version.raw} via ${detected.command} (tomllib ok): as skills Python também rodam.`;
|
|
268
|
+
}
|
|
269
|
+
if (status === 'full') {
|
|
270
|
+
return `Python ${detected.version.raw} existe via ${detected.command}, mas os scripts chamam python3, que não está no PATH. ${optional}`;
|
|
271
|
+
}
|
|
272
|
+
if (status === 'partial') {
|
|
273
|
+
return `Python ${detected.version.raw} no PATH, sem tomllib (as skills com script próprio pedem 3.11+). ${optional}`;
|
|
274
|
+
}
|
|
275
|
+
if (status === 'unsupported') {
|
|
276
|
+
return `Python ${detected.version.raw} é antigo demais (as skills com script próprio pedem 3.11+). ${optional}`;
|
|
277
|
+
}
|
|
278
|
+
return `python3 não encontrado no PATH. ${optional} Para tê-las: brew install python3 (macOS).`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Checagem de drift (RT-M3): compara o hash de cada linha do
|
|
283
|
+
* `_wizz/_config/files-manifest.csv` com o hash atual do arquivo em
|
|
284
|
+
* `_wizz/<path>`. Hash divergente ou arquivo sumido = drift. Não tenta
|
|
285
|
+
* adivinhar fonte no pacote: o files-manifest é escrito pelo installer
|
|
286
|
+
* contra o INSTALADO, então basta o estado em disco — funciona igual para
|
|
287
|
+
* módulos builtin, externos e custom.
|
|
288
|
+
* @param {object} args
|
|
289
|
+
* @param {string} args.wizzDir
|
|
290
|
+
* @param {string|null} args.manifestContent - conteúdo de files-manifest.csv (null = ausente)
|
|
291
|
+
* @param {(p: string) => Promise<string|null>} args.hashFile - sha256 (fs-native.calculateFileHash)
|
|
292
|
+
* @param {(p: string) => Promise<boolean>} args.fileExists
|
|
293
|
+
* @returns {Promise<{check: 'drift', status: 'ok'|'warn'|'error', detail: string}>}
|
|
294
|
+
*/
|
|
295
|
+
async function checkDrift({ wizzDir, manifestContent, hashFile, fileExists }) {
|
|
296
|
+
if (manifestContent == null) {
|
|
297
|
+
return {
|
|
298
|
+
check: 'drift',
|
|
299
|
+
status: 'warn',
|
|
300
|
+
detail: 'files-manifest.csv não encontrado em _wizz/_config/. Sem ele não dá para detectar drift; rode wizz update para gerar.',
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const lines = manifestContent.split('\n').filter((l) => l.trim());
|
|
305
|
+
// header: type,name,module,path,hash
|
|
306
|
+
const rows = lines
|
|
307
|
+
.slice(1)
|
|
308
|
+
.map(parseCsvLine)
|
|
309
|
+
.filter((f) => f.length >= 5 && f[3] && f[4]);
|
|
310
|
+
if (rows.length === 0) {
|
|
311
|
+
return { check: 'drift', status: 'ok', detail: 'files-manifest.csv vazio, nada a comparar.' };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const drifted = [];
|
|
315
|
+
const missing = [];
|
|
316
|
+
for (const fields of rows) {
|
|
317
|
+
const relPath = fields[3];
|
|
318
|
+
const recorded = fields[4];
|
|
319
|
+
const absPath = path.join(wizzDir, relPath);
|
|
320
|
+
if (!(await fileExists(absPath))) {
|
|
321
|
+
missing.push(relPath);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const current = await hashFile(absPath);
|
|
325
|
+
if (current && current !== recorded) {
|
|
326
|
+
drifted.push(relPath);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (drifted.length === 0 && missing.length === 0) {
|
|
331
|
+
return { check: 'drift', status: 'ok', detail: `${rows.length} arquivos batem com o files-manifest.csv.` };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const parts = [];
|
|
335
|
+
if (drifted.length > 0) parts.push(`${drifted.length} alterado(s)`);
|
|
336
|
+
if (missing.length > 0) parts.push(`${missing.length} ausente(s)`);
|
|
337
|
+
return {
|
|
338
|
+
check: 'drift',
|
|
339
|
+
status: 'warn',
|
|
340
|
+
detail: `Drift em _wizz/: ${parts.join(' e ')} contra o files-manifest.csv (ex.: ${[...drifted, ...missing].slice(0, 3).join(', ')}). Edita manuais em _wizz/ são perdidos no próximo update — mova para _wizz/custom/ e rode wizz update.`,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Checagem dos snapshots de customização (`_wizz/_resolved/`, item 19).
|
|
346
|
+
*
|
|
347
|
+
* A ativação de agente lê `_resolved/<skill>.json` com um único Read e não
|
|
348
|
+
* checa nada — sem mtime, sem stat, sem spawn. O preço disso é que um
|
|
349
|
+
* snapshot desatualizado é invisível em runtime, então a detecção mora
|
|
350
|
+
* aqui: `_resolved/.index.json` guarda o sha256 de cada arquivo que
|
|
351
|
+
* alimentou cada snapshot (customize.toml base, seus includes, as camadas
|
|
352
|
+
* team/user e os includes delas) e este check recalcula.
|
|
353
|
+
*
|
|
354
|
+
* Duas sutilezas do layout instalado:
|
|
355
|
+
* - o pipeline verbatim MOVE o diretório da skill de `_wizz/` para a pasta
|
|
356
|
+
* de skills do IDE, então o path do customize.toml gravado no índice
|
|
357
|
+
* costuma não existir mais; a cópia implantada
|
|
358
|
+
* (`.claude/skills/<skill>/customize.toml`) é a que o usuário edita e é
|
|
359
|
+
* contra ela que o hash é comparado nesse caso;
|
|
360
|
+
* - entrada que sumiu e não tem cópia implantada é "não verificável", não
|
|
361
|
+
* é obsolescência: avisar ali seria gritar em toda instalação limpa.
|
|
362
|
+
*
|
|
363
|
+
* Nunca é error: um snapshot ausente ou velho degrada para o fallback
|
|
364
|
+
* Python ou para uma customização antiga, nunca quebra a instalação.
|
|
365
|
+
*
|
|
366
|
+
* @param {object} args
|
|
367
|
+
* @param {string} args.projectDir - raiz do projeto (os paths do índice são relativos a ela)
|
|
368
|
+
* @param {Map<string, string[]>} args.skillSources - skill → customize.toml encontrados
|
|
369
|
+
* @param {object|null} args.index - `.index.json` já lido (null = ausente/ilegível)
|
|
370
|
+
* @param {(p: string) => boolean|Promise<boolean>} args.fileExists
|
|
371
|
+
* @param {(p: string) => Promise<string|null>} args.hashFile - sha256
|
|
372
|
+
* @param {string} args.resolvedDir
|
|
373
|
+
* @returns {Promise<{check: 'resolved', status: 'ok'|'warn', detail: string}>}
|
|
374
|
+
*/
|
|
375
|
+
async function checkResolved({ projectDir, skillSources, index, fileExists, hashFile, resolvedDir }) {
|
|
376
|
+
const sources = skillSources instanceof Map ? skillSources : new Map(Object.entries(skillSources || {}));
|
|
377
|
+
const skills = [...sources.keys()].sort();
|
|
378
|
+
if (skills.length === 0) {
|
|
379
|
+
return { check: 'resolved', status: 'ok', detail: 'Nenhuma skill com customize.toml instalada; nada a resolver.' };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!index || typeof index !== 'object' || !index.skills || typeof index.skills !== 'object') {
|
|
383
|
+
return {
|
|
384
|
+
check: 'resolved',
|
|
385
|
+
status: 'warn',
|
|
386
|
+
detail: `Índice ${path.join(resolvedDir, '.index.json')} ausente ou ilegível: não dá para saber se os ${skills.length} snapshots estão atualizados. Rode wizz resolve.`,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const missing = [];
|
|
391
|
+
const stale = [];
|
|
392
|
+
for (const skill of skills) {
|
|
393
|
+
// Prototype-safe: um skill chamado "constructor" (ou qualquer outra
|
|
394
|
+
// chave herdada de Object.prototype) não deve resolver para
|
|
395
|
+
// Object.prototype.constructor quando não há entrada própria no índice.
|
|
396
|
+
const entry = Object.hasOwn(index.skills, skill) ? index.skills[skill] : undefined;
|
|
397
|
+
const snapshotPath = path.join(resolvedDir, `${skill}.json`);
|
|
398
|
+
if (!entry || !(await fileExists(snapshotPath))) {
|
|
399
|
+
missing.push(skill);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
// O snapshot em si também é conferido: truncado, corrompido ou editado
|
|
403
|
+
// à mão diverge do hash gravado na geração (entry.output).
|
|
404
|
+
if (typeof entry.output === 'string') {
|
|
405
|
+
const currentOutput = await hashFile(snapshotPath);
|
|
406
|
+
if (currentOutput !== null && currentOutput !== entry.output) {
|
|
407
|
+
stale.push(`${skill} (snapshot)`);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const inputs = entry.inputs && typeof entry.inputs === 'object' ? entry.inputs : {};
|
|
412
|
+
for (const [relPath, recorded] of Object.entries(inputs)) {
|
|
413
|
+
const current = await currentInputHash({ projectDir, relPath, skill, sources, fileExists, hashFile, recorded });
|
|
414
|
+
if (current !== null && current !== recorded) {
|
|
415
|
+
stale.push(`${skill} (${relPath})`);
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (missing.length === 0 && stale.length === 0) {
|
|
422
|
+
return {
|
|
423
|
+
check: 'resolved',
|
|
424
|
+
status: 'ok',
|
|
425
|
+
detail: `${skills.length} snapshots em _wizz/_resolved/ batem com as entradas que os geraram.`,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const parts = [];
|
|
430
|
+
if (missing.length > 0) parts.push(`${missing.length} sem snapshot (${missing.slice(0, 3).join(', ')})`);
|
|
431
|
+
if (stale.length > 0) parts.push(`${stale.length} com snapshot obsoleto (${stale.slice(0, 3).join(', ')})`);
|
|
432
|
+
const prefix = stale.length > 0 ? 'snapshot obsoleto: rode wizz resolve' : 'rode wizz resolve';
|
|
433
|
+
return { check: 'resolved', status: 'warn', detail: `${prefix} — ${parts.join('; ')}.` };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Hash atual de uma entrada do índice, ou null quando não dá para verificar.
|
|
438
|
+
* O path gravado vale primeiro; se o arquivo sumiu e a entrada é o
|
|
439
|
+
* `customize.toml` da skill, compara com as cópias implantadas nas pastas
|
|
440
|
+
* de skills dos IDEs (é lá que o arquivo vive depois do install, e é lá que
|
|
441
|
+
* o usuário edita). Nenhuma cópia encontrada = não verificável (null).
|
|
442
|
+
* @returns {Promise<string|null>}
|
|
443
|
+
*/
|
|
444
|
+
async function currentInputHash({ projectDir, relPath, skill, sources, fileExists, hashFile, recorded }) {
|
|
445
|
+
const absPath = path.resolve(projectDir, relPath);
|
|
446
|
+
if (await fileExists(absPath)) return hashFile(absPath);
|
|
447
|
+
if (path.basename(relPath) !== 'customize.toml') return null;
|
|
448
|
+
|
|
449
|
+
const candidates = (sources.get(skill) || []).filter((p) => p !== absPath);
|
|
450
|
+
let first = null;
|
|
451
|
+
for (const candidate of candidates) {
|
|
452
|
+
const hash = await hashFile(candidate);
|
|
453
|
+
if (hash === null) continue;
|
|
454
|
+
if (hash === recorded) return hash;
|
|
455
|
+
if (first === null) first = hash;
|
|
456
|
+
}
|
|
457
|
+
return first;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Orquestra os 6 checks. Todos os efeitos de ambiente chegam por `deps`,
|
|
462
|
+
* então esta função é pura o bastante para testes com fixtures em tmp dir.
|
|
463
|
+
* @param {object} [options]
|
|
464
|
+
* @param {object} [options.deps] - checkers injetáveis (ver chamadas abaixo)
|
|
465
|
+
* @returns {Promise<{results: Array<{check, status, detail}>, exitCode: number, hasError: boolean}>}
|
|
466
|
+
*/
|
|
467
|
+
async function runDoctor(options = {}) {
|
|
468
|
+
const deps = options.deps || {};
|
|
469
|
+
const {
|
|
470
|
+
wizzDir,
|
|
471
|
+
projectDir = process.cwd(),
|
|
472
|
+
fileExists = (p) => require('../fs-native').pathExistsSync(p),
|
|
473
|
+
hashFile = (p) => require('../fs-native').calculateFileHash(p),
|
|
474
|
+
readFile = (p) => require('../fs-native').readFileSync(p, 'utf8'),
|
|
475
|
+
hasCommand = defaultHasCommand,
|
|
476
|
+
runVersion = defaultRunVersion,
|
|
477
|
+
detectPython: detectPy = () => require('./python-check').detectPython(),
|
|
478
|
+
readProjectSettings,
|
|
479
|
+
readGlobalSettings,
|
|
480
|
+
globalSettingsFile,
|
|
481
|
+
discoverSkillSources = (root, dir) =>
|
|
482
|
+
require('./resolved-customizations').discoverSkillCustomizeTomls({ projectRoot: root, wizzDir: dir }),
|
|
483
|
+
readResolvedIndex = (dir) => require('./resolved-customizations').readIndex(dir),
|
|
484
|
+
rtkOnPath = () => hasCommand('rtk'),
|
|
485
|
+
skip = [],
|
|
486
|
+
} = deps;
|
|
487
|
+
|
|
488
|
+
const settingsFile = path.join(projectDir, '.claude', 'settings.json');
|
|
489
|
+
const readSettingsSafe = readProjectSettings || (() => safeReadJson(settingsFile));
|
|
490
|
+
const readGlobalSafe = readGlobalSettings || (() => null);
|
|
491
|
+
|
|
492
|
+
const results = [];
|
|
493
|
+
|
|
494
|
+
// 1) hooks — só faz sentido com _wizz/ presente.
|
|
495
|
+
if (skip.includes('hooks')) {
|
|
496
|
+
results.push({ check: 'hooks', status: 'ok', detail: 'pulado.' });
|
|
497
|
+
} else if (wizzDir) {
|
|
498
|
+
results.push(
|
|
499
|
+
checkHooks({
|
|
500
|
+
wizzDir,
|
|
501
|
+
settingsFile,
|
|
502
|
+
globalSettingsFile,
|
|
503
|
+
fileExists,
|
|
504
|
+
readProjectSettings: readSettingsSafe,
|
|
505
|
+
readGlobalSettings: readGlobalSafe,
|
|
506
|
+
rtkOnPath: rtkOnPath(),
|
|
507
|
+
}),
|
|
508
|
+
);
|
|
509
|
+
} else {
|
|
510
|
+
results.push({
|
|
511
|
+
check: 'hooks',
|
|
512
|
+
status: 'warn',
|
|
513
|
+
detail: 'Sem instalação Wizz neste diretório (_wizz/ ausente); hooks do projeto não verificados. Rode wizz install.',
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// 2) rtk (com piso de versão do hook) e 3) jq (presença).
|
|
518
|
+
for (const cli of ['rtk', 'jq']) {
|
|
519
|
+
if (skip.includes(cli)) {
|
|
520
|
+
results.push({ check: cli, status: 'ok', detail: 'pulado.' });
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const onPath = hasCommand(cli);
|
|
524
|
+
let version = null;
|
|
525
|
+
if (onPath) {
|
|
526
|
+
version = runVersion(cli);
|
|
527
|
+
if (version == null && cli === 'rtk') {
|
|
528
|
+
// No PATH mas --version falhou: reporta presente sem versão em vez
|
|
529
|
+
// de "não encontrado" — o binário quebra por outro motivo.
|
|
530
|
+
version = '';
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
results.push(checkCli({ check: cli, version }));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// 4) python — detector stubável (mesmo padrão de checkPythonEnvironment).
|
|
537
|
+
if (skip.includes('python')) {
|
|
538
|
+
results.push({ check: 'python', status: 'ok', detail: 'pulado.' });
|
|
539
|
+
} else {
|
|
540
|
+
results.push(checkPython({ detected: detectPy() }));
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// 5) drift — só com _wizz/ presente; sem instalação, skip informado.
|
|
544
|
+
if (skip.includes('drift')) {
|
|
545
|
+
results.push({ check: 'drift', status: 'ok', detail: 'pulado.' });
|
|
546
|
+
} else if (wizzDir) {
|
|
547
|
+
const manifestPath = path.join(wizzDir, '_config', 'files-manifest.csv');
|
|
548
|
+
const manifestContent = (await fileExists(manifestPath)) ? await readFile(manifestPath) : null;
|
|
549
|
+
results.push(await checkDrift({ wizzDir, manifestContent, hashFile, fileExists }));
|
|
550
|
+
} else {
|
|
551
|
+
results.push({
|
|
552
|
+
check: 'drift',
|
|
553
|
+
status: 'warn',
|
|
554
|
+
detail: 'Sem instalação Wizz neste diretório; drift não verificado. Rode wizz install.',
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// 6) resolved — snapshots de customização contra o hash das entradas.
|
|
559
|
+
if (skip.includes('resolved')) {
|
|
560
|
+
results.push({ check: 'resolved', status: 'ok', detail: 'pulado.' });
|
|
561
|
+
} else if (wizzDir) {
|
|
562
|
+
const resolvedDir = path.join(wizzDir, '_resolved');
|
|
563
|
+
results.push(
|
|
564
|
+
await checkResolved({
|
|
565
|
+
projectDir,
|
|
566
|
+
skillSources: discoverSkillSources(projectDir, wizzDir),
|
|
567
|
+
index: readResolvedIndex(resolvedDir),
|
|
568
|
+
fileExists,
|
|
569
|
+
hashFile,
|
|
570
|
+
resolvedDir,
|
|
571
|
+
}),
|
|
572
|
+
);
|
|
573
|
+
} else {
|
|
574
|
+
results.push({
|
|
575
|
+
check: 'resolved',
|
|
576
|
+
status: 'warn',
|
|
577
|
+
detail: 'Sem instalação Wizz neste diretório; snapshots de _wizz/_resolved/ não verificados. Rode wizz install.',
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const hasError = results.some((r) => r.status === 'error');
|
|
582
|
+
return { results, exitCode: hasError ? 1 : 0, hasError };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* `command -v <cmd>` via sh — mesmo padrão dos testes do repo; spawnSync
|
|
587
|
+
* com args estáticos, sem risco de injection (cmd é literal da lista).
|
|
588
|
+
* @param {string} cmd
|
|
589
|
+
* @returns {boolean}
|
|
590
|
+
*/
|
|
591
|
+
function defaultHasCommand(cmd) {
|
|
592
|
+
try {
|
|
593
|
+
const { spawnSync } = require('node:child_process');
|
|
594
|
+
const res = spawnSync('sh', ['-c', `command -v ${cmd}`], { encoding: 'utf8', timeout: 5000 });
|
|
595
|
+
return res.status === 0;
|
|
596
|
+
} catch {
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Roda `<cmd> --version` e devolve stdout+stderr juntos (o rtk imprime em
|
|
603
|
+
* stdout, python em stderr; ambos os casos caem no mesmo regex).
|
|
604
|
+
* @param {string} cmd
|
|
605
|
+
* @returns {string|null} saída combinada, null quando o comando falha
|
|
606
|
+
*/
|
|
607
|
+
function defaultRunVersion(cmd) {
|
|
608
|
+
try {
|
|
609
|
+
const { spawnSync } = require('node:child_process');
|
|
610
|
+
const res = spawnSync(cmd, ['--version'], { encoding: 'utf8', timeout: 5000 });
|
|
611
|
+
if (res.error || (res.status !== 0 && !res.stdout)) return null;
|
|
612
|
+
return `${res.stdout || ''}\n${res.stderr || ''}`;
|
|
613
|
+
} catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Lê JSON de settings.json com fallback seguro para null (nunca lança).
|
|
620
|
+
* @param {string} file
|
|
621
|
+
* @returns {object|null}
|
|
622
|
+
*/
|
|
623
|
+
function safeReadJson(file) {
|
|
624
|
+
try {
|
|
625
|
+
const raw = require('../fs-native').readFileSync(file, 'utf8');
|
|
626
|
+
if (!raw.trim()) return null;
|
|
627
|
+
return JSON.parse(raw);
|
|
628
|
+
} catch {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
module.exports = {
|
|
634
|
+
CHECKS,
|
|
635
|
+
RTK_HOOK_FLOOR,
|
|
636
|
+
extractVersion,
|
|
637
|
+
compareSemver,
|
|
638
|
+
parseCsvLine,
|
|
639
|
+
checkHooks,
|
|
640
|
+
checkCli,
|
|
641
|
+
checkPython,
|
|
642
|
+
pythonDetail,
|
|
643
|
+
checkDrift,
|
|
644
|
+
checkResolved,
|
|
645
|
+
runDoctor,
|
|
646
|
+
defaultHasCommand,
|
|
647
|
+
defaultRunVersion,
|
|
648
|
+
};
|