wizz-method 2.13.0 → 2.14.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 (100) hide show
  1. package/THIRD-PARTY-NOTICES.md +2 -0
  2. package/package.json +10 -3
  3. package/skills-registry.yaml +28 -23
  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/customize.toml +4 -3
  36. package/src/bmm-skills/4-implementation/wizz-quick-dev/spec-template.md +6 -0
  37. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-04-review.md +27 -16
  38. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-oneshot.md +27 -6
  39. package/src/bmm-skills/4-implementation/wizz-retrospective/customize.toml +4 -3
  40. package/src/bmm-skills/4-implementation/wizz-sprint-planning/customize.toml +4 -3
  41. package/src/bmm-skills/4-implementation/wizz-sprint-status/customize.toml +4 -3
  42. package/src/core-skills/wizz-brainstorming/customize.toml +5 -6
  43. package/src/core-skills/wizz-brainstorming/scripts/brain.py +19 -0
  44. package/src/core-skills/wizz-brainstorming/scripts/tests/test_brain.py +33 -0
  45. package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +3 -1
  46. package/src/core-skills/wizz-forge-idea/scripts/tests/test_resolve_personas.py +21 -0
  47. package/src/core-skills/wizz-help/SKILL.md +5 -3
  48. package/src/core-skills/wizz-party-mode/customize.toml +5 -4
  49. package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +3 -1
  50. package/src/core-skills/wizz-party-mode/scripts/tests/test_resolve_party.py +21 -0
  51. package/src/core-skills/wizz-spec/customize.toml +4 -5
  52. package/src/modules/wizz/_shared/model-ladder.md +4 -4
  53. package/src/modules/wizz/subagents/opencode/wizz-exec-review.md +2 -2
  54. package/src/modules/wizz/subagents/opencode/wizz-exec-standard.md +2 -2
  55. package/src/modules/wizz/subagents/opencode/wizz-exec-strong.md +2 -2
  56. package/src/modules/wizz/subagents/spec.yaml +3 -3
  57. package/src/scripts/resolve_config.py +8 -0
  58. package/src/scripts/resolve_customization.py +24 -3
  59. package/src/scripts/tests/test_resolve_config.py +42 -0
  60. package/src/scripts/tests/test_resolve_customization.py +45 -0
  61. package/src/skills-lib/ctc-align/SKILL.md +1 -1
  62. package/src/skills-lib/wizz-agent-browser/SKILL.md +5 -0
  63. package/src/skills-lib/wizz-huashu-design/scripts/html2pptx.js +902 -1061
  64. package/src/skills-lib/wizz-humanizer/README.md +134 -71
  65. package/src/skills-lib/wizz-humanizer/SKILL.md +51 -37
  66. package/src/skills-lib/wizz-humanizer/references/ai-writing-patterns.md +208 -190
  67. package/tools/installer/core/doctor.js +31 -1
  68. package/tools/installer/core/installer.js +32 -0
  69. package/tools/installer/core/project-instructions.js +199 -0
  70. package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/__pycache__/lint_spine.cpython-313.pyc +0 -0
  71. package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/tests/__pycache__/test_lint_spine.cpython-313-pytest-9.1.1.pyc +0 -0
  72. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/__pycache__/sprint_status.cpython-313.pyc +0 -0
  73. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
  74. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-313-pytest-9.1.1.pyc +0 -0
  75. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/__pycache__/sprint_plan.cpython-313.pyc +0 -0
  76. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-313-pytest-9.1.1.pyc +0 -0
  77. package/src/core-skills/wizz-advanced-elicitation/scripts/__pycache__/pick_methods.cpython-313.pyc +0 -0
  78. package/src/core-skills/wizz-advanced-elicitation/scripts/tests/__pycache__/test_pick_methods.cpython-313-pytest-9.1.1.pyc +0 -0
  79. package/src/core-skills/wizz-brainstorming/scripts/__pycache__/brain.cpython-313.pyc +0 -0
  80. package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-313-pytest-9.1.1.pyc +0 -0
  81. package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-314.pyc +0 -0
  82. package/src/core-skills/wizz-customize/scripts/__pycache__/list_customizable_skills.cpython-313.pyc +0 -0
  83. package/src/core-skills/wizz-customize/scripts/tests/__pycache__/test_list_customizable_skills.cpython-313-pytest-9.1.1.pyc +0 -0
  84. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-313.pyc +0 -0
  85. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
  86. package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-313-pytest-9.1.1.pyc +0 -0
  87. package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-314.pyc +0 -0
  88. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-313.pyc +0 -0
  89. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
  90. package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-313-pytest-9.1.1.pyc +0 -0
  91. package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-314.pyc +0 -0
  92. package/src/scripts/__pycache__/resolve_customization.cpython-314.pyc +0 -0
  93. package/src/skills-lib/wizz-huashu-design/package-lock.json +0 -846
  94. package/src/skills-lib/wizz-humanizer/WARP.md +0 -53
  95. package/src/skills-lib/wizz-humanizer/references/full-example.md +0 -72
  96. package/src/skills-lib/wizz-humanizer/references/personality-and-soul.md +0 -36
  97. package/src/skills-lib/wizz-react-components/package-lock.json +0 -231
  98. package/src/skills-lib/wizz-supabase-postgres-best-practices/CLAUDE.md +0 -68
  99. package/src/skills-lib/wizz-ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  100. package/src/skills-lib/wizz-ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
@@ -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
+ };