wizz-method 2.13.0 → 2.15.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.
Files changed (108) hide show
  1. package/THIRD-PARTY-NOTICES.md +2 -0
  2. package/package.json +10 -3
  3. package/skills-registry.yaml +30 -25
  4. package/src/bmm-skills/1-analysis/research/wizz-domain-research/customize.toml +4 -3
  5. package/src/bmm-skills/1-analysis/research/wizz-market-research/customize.toml +4 -3
  6. package/src/bmm-skills/1-analysis/research/wizz-technical-research/customize.toml +3 -1
  7. package/src/bmm-skills/1-analysis/wizz-agent-analyst/customize.toml +3 -1
  8. package/src/bmm-skills/1-analysis/wizz-agent-tech-writer/customize.toml +4 -3
  9. package/src/bmm-skills/1-analysis/wizz-document-project/customize.toml +4 -3
  10. package/src/bmm-skills/1-analysis/wizz-prfaq/customize.toml +4 -3
  11. package/src/bmm-skills/1-analysis/wizz-product-brief/customize.toml +5 -6
  12. package/src/bmm-skills/2-plan-workflows/wizz-agent-pm/customize.toml +3 -1
  13. package/src/bmm-skills/2-plan-workflows/wizz-agent-ux-designer/customize.toml +3 -1
  14. package/src/bmm-skills/2-plan-workflows/wizz-create-prd/customize.toml +4 -3
  15. package/src/bmm-skills/2-plan-workflows/wizz-edit-prd/customize.toml +4 -3
  16. package/src/bmm-skills/2-plan-workflows/wizz-prd/customize.toml +5 -6
  17. package/src/bmm-skills/2-plan-workflows/wizz-ux/customize.toml +4 -3
  18. package/src/bmm-skills/2-plan-workflows/wizz-validate-prd/customize.toml +4 -3
  19. package/src/bmm-skills/3-solutioning/wizz-agent-architect/customize.toml +3 -1
  20. package/src/bmm-skills/3-solutioning/wizz-architecture/customize.toml +5 -6
  21. package/src/bmm-skills/3-solutioning/wizz-check-implementation-readiness/customize.toml +4 -3
  22. package/src/bmm-skills/3-solutioning/wizz-create-architecture/customize.toml +4 -3
  23. package/src/bmm-skills/3-solutioning/wizz-create-epics-and-stories/customize.toml +4 -3
  24. package/src/bmm-skills/3-solutioning/wizz-generate-project-context/steps/step-03-complete.md +7 -0
  25. package/src/bmm-skills/4-implementation/wizz-agent-dev/customize.toml +3 -1
  26. package/src/bmm-skills/4-implementation/wizz-checkpoint-preview/customize.toml +4 -3
  27. package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +4 -3
  28. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-03-triage.md +23 -14
  29. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +2 -2
  30. package/src/bmm-skills/4-implementation/wizz-correct-course/customize.toml +4 -3
  31. package/src/bmm-skills/4-implementation/wizz-create-story/customize.toml +4 -3
  32. package/src/bmm-skills/4-implementation/wizz-dev-story/customize.toml +4 -3
  33. package/src/bmm-skills/4-implementation/wizz-investigate/customize.toml +4 -3
  34. package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/customize.toml +4 -3
  35. package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +1 -1
  36. package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +4 -3
  37. package/src/bmm-skills/4-implementation/wizz-quick-dev/spec-template.md +6 -0
  38. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-04-review.md +27 -16
  39. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-oneshot.md +27 -6
  40. package/src/bmm-skills/4-implementation/wizz-retrospective/customize.toml +4 -3
  41. package/src/bmm-skills/4-implementation/wizz-sprint-planning/customize.toml +4 -3
  42. package/src/bmm-skills/4-implementation/wizz-sprint-status/customize.toml +4 -3
  43. package/src/core-skills/wizz-brainstorming/customize.toml +5 -6
  44. package/src/core-skills/wizz-brainstorming/scripts/brain.py +19 -0
  45. package/src/core-skills/wizz-brainstorming/scripts/tests/test_brain.py +33 -0
  46. package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +3 -1
  47. package/src/core-skills/wizz-forge-idea/scripts/tests/test_resolve_personas.py +21 -0
  48. package/src/core-skills/wizz-help/SKILL.md +5 -3
  49. package/src/core-skills/wizz-party-mode/customize.toml +5 -4
  50. package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +3 -1
  51. package/src/core-skills/wizz-party-mode/scripts/tests/test_resolve_party.py +21 -0
  52. package/src/core-skills/wizz-spec/customize.toml +4 -5
  53. package/src/modules/wizz/_shared/model-ladder.md +4 -4
  54. package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
  55. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +1 -1
  56. package/src/modules/wizz/subagents/opencode/wizz-exec-review.md +2 -2
  57. package/src/modules/wizz/subagents/opencode/wizz-exec-standard.md +2 -2
  58. package/src/modules/wizz/subagents/opencode/wizz-exec-strong.md +2 -2
  59. package/src/modules/wizz/subagents/spec.yaml +3 -3
  60. package/src/scripts/resolve_config.py +8 -0
  61. package/src/scripts/resolve_customization.py +24 -3
  62. package/src/scripts/tests/test_resolve_config.py +42 -0
  63. package/src/scripts/tests/test_resolve_customization.py +45 -0
  64. package/src/skills-lib/_shared/complexity-signal.md +1 -1
  65. package/src/skills-lib/ctc-align/SKILL.md +1 -1
  66. package/src/skills-lib/wizz-agent-browser/SKILL.md +5 -0
  67. package/src/skills-lib/wizz-design-needs/SKILL.md +1 -1
  68. package/src/skills-lib/wizz-huashu-design/scripts/html2pptx.js +902 -1061
  69. package/src/skills-lib/wizz-humanizer/README.md +134 -71
  70. package/src/skills-lib/wizz-humanizer/SKILL.md +51 -37
  71. package/src/skills-lib/wizz-humanizer/references/ai-writing-patterns.md +208 -190
  72. package/src/skills-lib/wizz-premium-landing-ui-researcher/SKILL.md +1 -1
  73. package/src/skills-lib/wizz-router/SKILL.md +1 -1
  74. package/src/skills-lib/wizz-ui-component-curator/SKILL.md +22 -2
  75. package/tools/installer/core/doctor.js +31 -1
  76. package/tools/installer/core/installer.js +32 -0
  77. package/tools/installer/core/project-instructions.js +199 -0
  78. package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/__pycache__/lint_spine.cpython-313.pyc +0 -0
  79. package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/tests/__pycache__/test_lint_spine.cpython-313-pytest-9.1.1.pyc +0 -0
  80. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/__pycache__/sprint_status.cpython-313.pyc +0 -0
  81. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
  82. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-313-pytest-9.1.1.pyc +0 -0
  83. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/__pycache__/sprint_plan.cpython-313.pyc +0 -0
  84. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-313-pytest-9.1.1.pyc +0 -0
  85. package/src/core-skills/wizz-advanced-elicitation/scripts/__pycache__/pick_methods.cpython-313.pyc +0 -0
  86. package/src/core-skills/wizz-advanced-elicitation/scripts/tests/__pycache__/test_pick_methods.cpython-313-pytest-9.1.1.pyc +0 -0
  87. package/src/core-skills/wizz-brainstorming/scripts/__pycache__/brain.cpython-313.pyc +0 -0
  88. package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-313-pytest-9.1.1.pyc +0 -0
  89. package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-314.pyc +0 -0
  90. package/src/core-skills/wizz-customize/scripts/__pycache__/list_customizable_skills.cpython-313.pyc +0 -0
  91. package/src/core-skills/wizz-customize/scripts/tests/__pycache__/test_list_customizable_skills.cpython-313-pytest-9.1.1.pyc +0 -0
  92. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-313.pyc +0 -0
  93. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
  94. package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-313-pytest-9.1.1.pyc +0 -0
  95. package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-314.pyc +0 -0
  96. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-313.pyc +0 -0
  97. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
  98. package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-313-pytest-9.1.1.pyc +0 -0
  99. package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-314.pyc +0 -0
  100. package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
  101. package/src/skills-lib/wizz-huashu-design/package-lock.json +0 -846
  102. package/src/skills-lib/wizz-humanizer/WARP.md +0 -53
  103. package/src/skills-lib/wizz-humanizer/references/full-example.md +0 -72
  104. package/src/skills-lib/wizz-humanizer/references/personality-and-soul.md +0 -36
  105. package/src/skills-lib/wizz-react-components/package-lock.json +0 -231
  106. package/src/skills-lib/wizz-supabase-postgres-best-practices/CLAUDE.md +0 -68
  107. package/src/skills-lib/wizz-ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  108. package/src/skills-lib/wizz-ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wizz-ui-component-curator
3
- description: analyze an existing frontend project, infer its visual style and product tone, then research and recommend compatible UI components and effects from public component catalogs, registries and source repositories. use when the user wants help choosing components, effects, sections, or visual patterns that fit an existing project — especially hero effects, cards, buttons, testimonials, pricing sections, navigation, or any interactive pattern. trigger whenever the user wants to add a UI element and wants Claude to study the project first before suggesting options. always inspect the codebase and exact component sources, provide evidence and plan before editing.
3
+ description: analyze an existing frontend project, infer its visual style and product tone, then research and recommend compatible UI components and effects from public component catalogs, registries and source repositories. use when the user wants help choosing components, effects, sections, or visual patterns that fit an existing project or dashboard — especially hero effects, cards, buttons, hovers, loading animations, testimonials, pricing sections, navigation, or any interactive pattern. trigger whenever the user wants to add a UI element and wants Claude to study the project first before suggesting options. always inspect the codebase and exact component sources, provide evidence and plan before editing.
4
4
  ---
5
5
 
6
6
  # Overview
@@ -19,6 +19,8 @@ Act as a UI curator with taste and restraint — not a gallery explorer. Optimiz
19
19
 
20
20
  If `{output_folder}/design-state.yaml` exists, the need being worked is already written there: take its `query`, `budget.fontes` and `floor` instead of improvising them, honour the `proibidos` block, and write the result back under `discoveries` with the `need` id. If the request does not match any open need, say so before researching: an unplanned need is either a gap in the map (go back to `wizz-design-needs`) or scope creep.
21
21
 
22
+ Not there? Look before deciding it does not exist: `find . -name design-state.yaml -not -path '*/node_modules/*'`. A map found anywhere else is still the map: use it, and tell the user it is off-place (it belongs in `{output_folder}`).
23
+
22
24
  With no state file, continue normally.
23
25
 
24
26
  ## 1. Read and infer the style DNA
@@ -38,7 +40,9 @@ Do not ask the user to describe the style unless the project has no inspectable
38
40
 
39
41
  ## 2. Search and inspect public sources
40
42
 
41
- Start with existing project components. Then search appropriate public sources: [React Bits](https://reactbits.dev/), [Cult UI](https://www.cult-ui.com/docs), [Componentry](https://componentry.dev/), or [shadcn/ui](https://ui.shadcn.com/docs). Compare at least two suitable sources when available.
43
+ Start with existing project components. Then search the public sources. The full authorized list (React Bits, Cult UI, Ali Imam, Watermelon, StyleUI, Skiper UI, Bklit UI, Componentry, shadcn/ui, plus the reference galleries) lives in one place: [component-sources](../wizz-premium-landing-ui-researcher/references/component-sources.md). Read it instead of relying on memory. Compare at least two suitable sources per need.
44
+
45
+ Use whatever search tools the session has: web search (Exa when configured, WebSearch otherwise), WebFetch, scrapling, and `agent-browser` to watch hover and motion live. A tool that errors or hangs is not the end of the research: record the failure, move to the next tool, and never stop without reporting what was and was not checked.
42
46
 
43
47
  Open the exact demo and source file/registry item. Check the actual imports, license, dependencies, stack variant and revision; use current official documentation (Context7 when available) for API compatibility. Inspect animation/interaction in the available browser; label static-only inspection honestly. If a source fails, record the failure and continue elsewhere.
44
48
 
@@ -68,6 +72,22 @@ Send the exact candidate links and recommendation. If the user authorized implem
68
72
 
69
73
  ---
70
74
 
75
+ ## Dashboard and app panels
76
+
77
+ A panel is used every day, so the bar is different from a landing page: motion that delights once tires on the tenth visit. When the project is a dashboard, admin or operational panel, cover these categories (only the ones the request or the map opens):
78
+
79
+ - loading: skeleton, spinner, route progress, branded loader
80
+ - action feedback: toast, confirmation, undo, a discreet success state
81
+ - numbers: animated counter, sparkline, gauge, KPI card
82
+ - lists and queues: enter/exit animation, swipe actions on mobile
83
+ - navigation: sidebar, command menu, tooltip, dock
84
+ - surfaces: hover states, border glow, spotlight, subtle background
85
+ - empty states, charts and form controls
86
+
87
+ For each candidate, judge "does it tire with daily use", reduced-motion support and weight. Reject landing-page effects that do not serve a task.
88
+
89
+ ---
90
+
71
91
  # Consistency rules
72
92
 
73
93
  If there is tension between the most impressive option and the best-fitting option: **choose the one that fits.**
@@ -25,7 +25,7 @@ const { HOOK_DEFS, isOurHookCommand } = require('./hooks-settings');
25
25
  const { classifyPython, PYTHON_ONLY_SKILLS } = require('./python-check');
26
26
 
27
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
28
+ // (o registry pin do CLI é mais alto, 0.50.0, e é aplicado pelo detectClis
29
29
  // no install; aqui reportamos o piso que quebra o hook hoje).
30
30
  const RTK_HOOK_FLOOR = '0.23.0';
31
31
 
@@ -464,6 +464,28 @@ async function currentInputHash({ projectDir, relPath, skill, sources, fileExist
464
464
  * @param {object} [options.deps] - checkers injetáveis (ver chamadas abaixo)
465
465
  * @returns {Promise<{results: Array<{check, status, detail}>, exitCode: number, hasError: boolean}>}
466
466
  */
467
+ /**
468
+ * CLAUDE.md/AGENTS.md: bloco Wizz presente e sem regra legada fora dele.
469
+ * Sem o bloco, um CLAUDE.md antigo vence as regras do Wizz (2026-09-30).
470
+ * @returns {Promise<{check: 'instructions', status: 'ok'|'warn', detail: string}>}
471
+ */
472
+ async function checkProjectInstructions({ projectDir, fileExists, readFile }) {
473
+ const { INSTRUCTION_FILES, BLOCK_BEGIN, scanLegacy } = require('./project-instructions');
474
+ const problems = [];
475
+ let seen = 0;
476
+ for (const name of INSTRUCTION_FILES) {
477
+ const file = path.join(projectDir, name);
478
+ if (!(await fileExists(file))) continue;
479
+ seen += 1;
480
+ const text = await readFile(file);
481
+ if (!text.includes(BLOCK_BEGIN)) problems.push(`${name} sem o bloco Wizz no topo (rode wizz install)`);
482
+ for (const f of scanLegacy(text)) problems.push(`${name}:${f.line} ${f.why}: ${f.snippet}`);
483
+ }
484
+ if (seen === 0) return { check: 'instructions', status: 'ok', detail: 'Sem CLAUDE.md/AGENTS.md no projeto.' };
485
+ if (problems.length === 0) return { check: 'instructions', status: 'ok', detail: 'Bloco Wizz presente, nenhuma regra legada.' };
486
+ return { check: 'instructions', status: 'warn', detail: problems.join('\n') };
487
+ }
488
+
467
489
  async function runDoctor(options = {}) {
468
490
  const deps = options.deps || {};
469
491
  const {
@@ -578,6 +600,13 @@ async function runDoctor(options = {}) {
578
600
  });
579
601
  }
580
602
 
603
+ // 7) instructions — CLAUDE.md/AGENTS.md com bloco Wizz e sem regra legada.
604
+ if (skip.includes('instructions')) {
605
+ results.push({ check: 'instructions', status: 'ok', detail: 'pulado.' });
606
+ } else if (wizzDir) {
607
+ results.push(await checkProjectInstructions({ projectDir, fileExists, readFile }));
608
+ }
609
+
581
610
  const hasError = results.some((r) => r.status === 'error');
582
611
  return { results, exitCode: hasError ? 1 : 0, hasError };
583
612
  }
@@ -631,6 +660,7 @@ function safeReadJson(file) {
631
660
  }
632
661
 
633
662
  module.exports = {
663
+ checkProjectInstructions,
634
664
  CHECKS,
635
665
  RTK_HOOK_FLOOR,
636
666
  extractVersion,
@@ -20,6 +20,7 @@ const { buildHookEntries, filterHooksRegisteredGlobally, mergeHooksIntoSettings
20
20
 
21
21
  const { ExistingInstall } = require('./existing-install');
22
22
  const { warnPreNativeSkillsLegacy } = require('./legacy-warnings');
23
+ const { applyProjectInstructions, removeProjectInstructions } = require('./project-instructions');
23
24
  const userFilePreservation = require('./user-file-preservation');
24
25
  const moduleConfigWriter = require('./module-config-writer');
25
26
  const helpCatalog = require('./help-catalog');
@@ -808,6 +809,11 @@ class Installer {
808
809
  addResult('Hooks settings', 'ok', `${pendingHooksSettingsWrite.count} hook(s) → .claude/settings.json${skippedDetail}`);
809
810
  }
810
811
 
812
+ // Bloco gerenciado no CLAUDE.md/AGENTS.md (2026-09-30): sem ele, um
813
+ // CLAUDE.md antigo vence as regras do Wizz, que chegam só por hook.
814
+ // Depois do swap pelo mesmo motivo do settings.json: vive fora de _wizz/.
815
+ await this._applyProjectInstructions(paths, config, addResult);
816
+
811
817
  await this._appendPreservedSkillManifestRows(paths.wizzDir, previousSkillManifestRows, preservedModules);
812
818
 
813
819
  // Apply post-install --set TOML patches. Runs after writeCentralConfig
@@ -1223,6 +1229,31 @@ class Installer {
1223
1229
  * @param {string} file
1224
1230
  * @returns {Promise<{settings: object, invalidJson: boolean}>}
1225
1231
  */
1232
+ async _applyProjectInstructions(paths, config, addResult) {
1233
+ if (!(await fs.pathExists(path.join(paths.wizzDir, 'wizz')))) return;
1234
+ try {
1235
+ const results = await applyProjectInstructions({
1236
+ projectRoot: paths.projectRoot,
1237
+ fs,
1238
+ createIfMissing: (config.ides || []).includes('claude-code'),
1239
+ });
1240
+ for (const r of results) {
1241
+ const name = path.basename(r.file);
1242
+ const legacy = r.findings.length > 0 ? `, ${r.findings.length} trecho(s) legado(s) marcados` : '';
1243
+ addResult('Project instructions', r.findings.length > 0 ? 'warn' : 'ok', `bloco Wizz no topo de ${name}${legacy}`);
1244
+ }
1245
+ const totalLegacy = results.reduce((n, r) => n + r.findings.length, 0);
1246
+ if (totalLegacy > 0) {
1247
+ await prompts.log.warn(
1248
+ `${totalLegacy} regra(s) antiga(s) no CLAUDE.md/AGENTS.md (GSD, superpowers, BMAD puro ou modelo aposentado). O bloco Wizz no topo tem prioridade, mas vale limpar: rode wizz doctor para ver as linhas.`,
1249
+ );
1250
+ }
1251
+ } catch (error) {
1252
+ await prompts.log.warn(`Falha ao gravar o bloco Wizz no CLAUDE.md/AGENTS.md: ${error.message}`);
1253
+ addResult('Project instructions', 'warn', error.message);
1254
+ }
1255
+ }
1256
+
1226
1257
  async _readSettingsJson(file) {
1227
1258
  if (!(await fs.pathExists(file))) return { settings: {}, invalidJson: false };
1228
1259
  try {
@@ -1600,6 +1631,7 @@ class Installer {
1600
1631
  // 2. IDE CLEANUP (before _wizz/ deletion so configs are accessible)
1601
1632
  if (options.removeIdeConfigs !== false) {
1602
1633
  await this.uninstallIdeConfigs(projectDir, existingInstall, { silent: options.silent });
1634
+ await removeProjectInstructions({ projectRoot: projectDir, fs });
1603
1635
  removed.ideConfigs = true;
1604
1636
  }
1605
1637
 
@@ -0,0 +1,199 @@
1
+ // project-instructions.js — bloco gerenciado no CLAUDE.md/AGENTS.md do projeto.
2
+ //
3
+ // Motivo (2026-09-30): o Claude Code carrega o CLAUDE.md do projeto como
4
+ // instrução de projeto (prioridade alta), enquanto as regras do Wizz chegam
5
+ // por hook e skill (contexto extra, prioridade menor). Num projeto que já
6
+ // tinha CLAUDE.md antigo (GSD, superpowers, BMAD puro, modelos aposentados),
7
+ // a regra velha vencia a nova. O installer agora grava um bloco no TOPO do
8
+ // arquivo dizendo que o Wizz vence em conflito e listando os trechos legados
9
+ // detectados. Nada fora do bloco é apagado ou reescrito.
10
+ //
11
+ // Lógica pura (texto → texto) + um aplicador fino com fs injetável, para os
12
+ // testes não dependerem de disco real.
13
+
14
+ const path = require('node:path');
15
+ const fsp = require('node:fs/promises');
16
+
17
+ const BLOCK_BEGIN = '<!-- wizz:begin -->';
18
+ const BLOCK_END = '<!-- wizz:end -->';
19
+ const INSTRUCTION_FILES = ['CLAUDE.md', 'AGENTS.md'];
20
+
21
+ // IDs de modelo aposentados. Compartilhado com tools/validate-model-ids.mjs.
22
+ // claude-haiku-4-5 continua atual (não há Haiku mais novo em 2026-09-30).
23
+ // `claude-(opus|sonnet)-5` sem sufixo foi trocado por 5-5; o lookahead evita
24
+ // casar o próprio `-5-5`.
25
+ const RETIRED_MODEL_RE =
26
+ /\bclaude-(?:instant|2|3|3-5|3-7)(?:-[a-z0-9]+)*\b|\bclaude-(?:opus|sonnet)-4(?:-\d+)?\b|\bclaude-(?:opus|sonnet)-5\b(?!-)/gi;
27
+
28
+ const LEGACY_RULES = [
29
+ { id: 'gsd', re: /\bGSD\b|get-shit-done|\/gsd[:-]/, why: 'GSD foi substituído pelas fases e checkpoints do Wizz Method' },
30
+ { id: 'superpowers', re: /\bsuperpowers\b/i, why: 'superpowers foi substituído pelo Wizz Method' },
31
+ {
32
+ id: 'bmad',
33
+ re: /_bmad\/|\/bmad[-:]|\bbmad-(?:master|agent|method|help)\b/i,
34
+ why: 'comandos e pastas do BMAD puro viraram wizz-* e _wizz/',
35
+ },
36
+ { id: 'modelo-aposentado', re: RETIRED_MODEL_RE, why: 'ID de modelo aposentado; a escada do Wizz usa os apelidos atuais' },
37
+ ];
38
+
39
+ const MAX_SNIPPET = 120;
40
+
41
+ // project-context.md (2026-09-30, porte do BMAD 6.12 #2750): em vez de cada
42
+ // skill procurar o arquivo em toda ativação (persistent_facts), o bloco
43
+ // importa uma vez por sessão. Busca rasa, pulando pastas de dependência/build.
44
+ const CONTEXT_FILE = 'project-context.md';
45
+ const CONTEXT_MAX_DEPTH = 4;
46
+ const CONTEXT_SKIP_DIRS = new Set(['node_modules', '.git', '_wizz', '.claude', 'build', 'dist', '.next', 'vendor', '.venv']);
47
+
48
+ /** Caminho relativo (posix) do project-context.md mais raso, ou null. */
49
+ async function findProjectContext(projectRoot) {
50
+ let level = [projectRoot];
51
+ for (let depth = 0; depth <= CONTEXT_MAX_DEPTH && level.length > 0; depth++) {
52
+ const next = [];
53
+ for (const dir of level.sort()) {
54
+ let entries;
55
+ try {
56
+ entries = await fsp.readdir(dir, { withFileTypes: true });
57
+ } catch {
58
+ continue;
59
+ }
60
+ if (entries.some((e) => e.isFile() && e.name === CONTEXT_FILE)) {
61
+ return path.relative(projectRoot, path.join(dir, CONTEXT_FILE)).split(path.sep).join('/');
62
+ }
63
+ for (const e of entries) if (e.isDirectory() && !CONTEXT_SKIP_DIRS.has(e.name)) next.push(path.join(dir, e.name));
64
+ }
65
+ level = next;
66
+ }
67
+ return null;
68
+ }
69
+
70
+ /** Linha de contexto por arquivo: CLAUDE.md importa com @; AGENTS.md só aponta. */
71
+ function contextLine(fileName, contextPath) {
72
+ if (!contextPath) return null;
73
+ return fileName === 'CLAUDE.md'
74
+ ? `Contexto do projeto (regras de stack e padrões, importado uma vez por sessão):\n@${contextPath}`
75
+ : `- Contexto do projeto: leia \`${contextPath}\` antes de implementar (regras de stack e padrões).`;
76
+ }
77
+
78
+ /** Separa o bloco gerenciado do resto do arquivo. */
79
+ function splitManagedBlock(text) {
80
+ const start = text.indexOf(BLOCK_BEGIN);
81
+ const end = text.indexOf(BLOCK_END);
82
+ if (start === -1 || end === -1 || end < start) return { block: null, rest: text };
83
+ const after = text.slice(end + BLOCK_END.length).replace(/^\r?\n+/, '');
84
+ return { block: text.slice(start, end + BLOCK_END.length), rest: text.slice(0, start) + after };
85
+ }
86
+
87
+ /**
88
+ * Procura regras legadas no texto, pulando as linhas do bloco gerenciado.
89
+ * `line` é a linha real no arquivo (1-based), para o doctor apontar.
90
+ * @returns {{id: string, line: number, snippet: string, why: string}[]}
91
+ */
92
+ function scanLegacy(text) {
93
+ const findings = [];
94
+ let inBlock = false;
95
+ for (const [i, line] of text.split(/\r?\n/).entries()) {
96
+ if (line.includes(BLOCK_BEGIN)) inBlock = true;
97
+ if (inBlock) {
98
+ if (line.includes(BLOCK_END)) inBlock = false;
99
+ continue;
100
+ }
101
+ const rule = LEGACY_RULES.find((r) => new RegExp(r.re.source, r.re.flags.replace('g', '')).test(line));
102
+ if (rule) findings.push({ id: rule.id, line: i + 1, snippet: line.trim().slice(0, MAX_SNIPPET), why: rule.why });
103
+ }
104
+ return findings;
105
+ }
106
+
107
+ /** Monta o bloco. Sem número de linha: o próprio bloco desloca as linhas. */
108
+ function renderManagedBlock(findings = [], context = null) {
109
+ const lines = [
110
+ BLOCK_BEGIN,
111
+ '<!-- Gerenciado pelo Wizz Method: o install reescreve este bloco. Edite fora dele. -->',
112
+ '## Wizz Method: estas regras têm prioridade',
113
+ '',
114
+ 'Este projeto usa o Wizz Method (`_wizz/`). Se alguma instrução deste arquivo, fora deste bloco, contradizer o Wizz Method, vale o Wizz Method.',
115
+ '',
116
+ '- Pedido não trivial: uma área vai para o agente da área; duas ou mais áreas vão para o `wizz-maestro`. Edição trivial de 1 linha e conversa executam direto.',
117
+ '- Planejamento, TDD, checkpoints e review seguem o Wizz Method. Não usar GSD, superpowers nem os comandos antigos do BMAD (`/bmad-*`, `_bmad/`).',
118
+ '- Modelos seguem a escada do Wizz (`wizz-exec-mechanical`, `-standard`, `-strong`, `-ultra`, `-review`), que usa os apelidos atuais. Ignore IDs de modelo fixos deste arquivo.',
119
+ ];
120
+ if (context) lines.push('', ...context.split('\n'));
121
+ if (findings.length > 0) {
122
+ lines.push('', 'Trechos legados detectados abaixo (não valem mais; `wizz doctor` lista de novo):');
123
+ for (const f of findings) lines.push(`- \`${f.snippet.replaceAll('`', "'")}\` (${f.why})`);
124
+ }
125
+ lines.push(BLOCK_END);
126
+ return lines.join('\n');
127
+ }
128
+
129
+ /** Insere ou substitui o bloco no topo. Idempotente. */
130
+ function upsertManagedBlock(text, context = null) {
131
+ const { rest } = splitManagedBlock(text);
132
+ const findings = scanLegacy(rest);
133
+ const block = renderManagedBlock(findings, context);
134
+ const body = rest.replace(/^\s+/, '');
135
+ return { text: body ? `${block}\n\n${body}` : `${block}\n`, findings };
136
+ }
137
+
138
+ /** Remove o bloco (uninstall). Devolve o texto intacto se não houver bloco. */
139
+ function removeManagedBlock(text) {
140
+ return splitManagedBlock(text).rest;
141
+ }
142
+
143
+ /**
144
+ * Aplica o bloco nos arquivos de instrução que existem na raiz do projeto.
145
+ * Sem nenhum dos dois, cria CLAUDE.md só se `createIfMissing`.
146
+ * Com project-context.md no projeto, o bloco importa (CLAUDE.md) ou aponta (AGENTS.md) para ele.
147
+ * @returns {Promise<{file: string, changed: boolean, findings: object[]}[]>}
148
+ */
149
+ async function applyProjectInstructions({ projectRoot, fs, createIfMissing = false }) {
150
+ const results = [];
151
+ const targets = [];
152
+ for (const name of INSTRUCTION_FILES) {
153
+ const file = path.join(projectRoot, name);
154
+ if (await fs.pathExists(file)) targets.push(file);
155
+ }
156
+ if (targets.length === 0 && createIfMissing) targets.push(path.join(projectRoot, 'CLAUDE.md'));
157
+
158
+ const contextPath = await findProjectContext(projectRoot);
159
+ for (const file of targets) {
160
+ const before = (await fs.pathExists(file)) ? await fs.readFile(file, 'utf8') : '';
161
+ const { text, findings } = upsertManagedBlock(before, contextLine(path.basename(file), contextPath));
162
+ const changed = text !== before;
163
+ if (changed) await fs.writeFile(file, text, 'utf8');
164
+ results.push({ file, changed, findings });
165
+ }
166
+ return results;
167
+ }
168
+
169
+ /** Tira o bloco dos arquivos de instrução (uninstall). */
170
+ async function removeProjectInstructions({ projectRoot, fs }) {
171
+ const removed = [];
172
+ for (const name of INSTRUCTION_FILES) {
173
+ const file = path.join(projectRoot, name);
174
+ if (!(await fs.pathExists(file))) continue;
175
+ const before = await fs.readFile(file, 'utf8');
176
+ const after = removeManagedBlock(before);
177
+ if (after !== before) {
178
+ await fs.writeFile(file, after, 'utf8');
179
+ removed.push(file);
180
+ }
181
+ }
182
+ return removed;
183
+ }
184
+
185
+ module.exports = {
186
+ BLOCK_BEGIN,
187
+ BLOCK_END,
188
+ INSTRUCTION_FILES,
189
+ findProjectContext,
190
+ contextLine,
191
+ RETIRED_MODEL_RE,
192
+ splitManagedBlock,
193
+ scanLegacy,
194
+ renderManagedBlock,
195
+ upsertManagedBlock,
196
+ removeManagedBlock,
197
+ applyProjectInstructions,
198
+ removeProjectInstructions,
199
+ };