wizz-method 2.18.0 → 2.19.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.
Files changed (166) hide show
  1. package/package.json +5 -3
  2. package/removals.txt +10 -0
  3. package/skills-registry.yaml +60 -39
  4. package/src/bmm-skills/1-analysis/wizz-set-feature/SKILL.md +9 -9
  5. package/src/bmm-skills/2-plan-workflows/wizz-ux/references/creative-tools.md +2 -0
  6. package/src/bmm-skills/2-plan-workflows/wizz-ux/references/design-md-spec.md +2 -0
  7. package/src/bmm-skills/4-implementation/wizz-create-story/SKILL.md +1 -1
  8. package/src/bmm-skills/4-implementation/wizz-dev-story/SKILL.md +1 -1
  9. package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/SKILL.md +1 -1
  10. package/src/bmm-skills/4-implementation/wizz-qa-generate-e2e-tests/checklist.md +1 -1
  11. package/src/bmm-skills/4-implementation/wizz-quick-dev/SKILL.md +1 -1
  12. package/src/bmm-skills/4-implementation/wizz-sprint-status/SKILL.md +5 -5
  13. package/src/bmm-skills/module-help.csv +1 -1
  14. package/src/bmm-skills/module.yaml +1 -1
  15. package/src/core-skills/_shared/handoff-protocol.md +2 -2
  16. package/src/modules/lowticket/skills/wizz-lowticket-funil/SKILL.md +1 -1
  17. package/src/modules/lowticket/skills/wizz-lowticket-guia/SKILL.md +1 -1
  18. package/src/modules/lowticket/skills/wizz-lowticket-metodologia/SKILL.md +1 -1
  19. package/src/modules/lowticket/skills/wizz-lowticket-minerador/SKILL.md +1 -1
  20. package/src/modules/lowticket/skills/wizz-lowticket-pagina/SKILL.md +1 -1
  21. package/src/modules/lowticket/skills/wizz-lowticket-trafego/SKILL.md +1 -1
  22. package/src/modules/wizz/_shared/cerebro.md +7 -7
  23. package/src/modules/wizz/_shared/communication-rules.md +1 -1
  24. package/src/modules/wizz/_shared/encerramento.md +4 -3
  25. package/src/modules/wizz/_shared/organizacao.md +8 -4
  26. package/src/modules/wizz/_shared/token-economy.md +1 -1
  27. package/src/modules/wizz/_shared/video-pipeline.md +4 -4
  28. package/src/modules/wizz/agents/wizz-ads/SKILL.md +4 -1
  29. package/src/modules/wizz/agents/wizz-ads/customize.toml +12 -3
  30. package/src/modules/wizz/agents/wizz-ads/knowledge/playbook.md +36 -0
  31. package/src/modules/wizz/agents/wizz-copy/SKILL.md +4 -1
  32. package/src/modules/wizz/agents/wizz-copy/customize.toml +2 -3
  33. package/src/modules/wizz/agents/wizz-copy/knowledge/playbook.md +34 -0
  34. package/src/modules/wizz/agents/wizz-designer/SKILL.md +2 -2
  35. package/src/modules/wizz/agents/wizz-designer/customize.toml +2 -3
  36. package/src/modules/wizz/agents/wizz-growth/SKILL.md +4 -1
  37. package/src/modules/wizz/agents/wizz-growth/customize.toml +33 -4
  38. package/src/modules/wizz/agents/wizz-growth/knowledge/playbook.md +39 -0
  39. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +6 -8
  40. package/src/modules/wizz/agents/wizz-maestro/customize.toml +2 -3
  41. package/src/modules/wizz/agents/wizz-memoria/SKILL.md +7 -7
  42. package/src/modules/wizz/agents/wizz-memoria/customize.toml +5 -6
  43. package/src/modules/wizz/agents/wizz-qa/SKILL.md +5 -6
  44. package/src/modules/wizz/agents/wizz-qa/customize.toml +3 -4
  45. package/src/modules/wizz/agents/wizz-seo/SKILL.md +4 -1
  46. package/src/modules/wizz/agents/wizz-seo/customize.toml +2 -3
  47. package/src/modules/wizz/agents/wizz-seo/knowledge/playbook.md +31 -0
  48. package/src/modules/wizz/agents/wizz-social/SKILL.md +3 -3
  49. package/src/modules/wizz/agents/wizz-social/customize.toml +2 -3
  50. package/src/modules/wizz/overrides/wizz-agent-analyst.toml +2 -2
  51. package/src/modules/wizz/overrides/wizz-agent-architect.toml +3 -3
  52. package/src/modules/wizz/overrides/wizz-agent-dev.toml +3 -3
  53. package/src/modules/wizz/overrides/wizz-agent-pm.toml +2 -2
  54. package/src/modules/wizz/overrides/wizz-agent-tech-writer.toml +2 -2
  55. package/src/modules/wizz/overrides/wizz-agent-ux-designer.toml +2 -2
  56. package/src/skills-lib/_shared/complexity-signal.md +8 -9
  57. package/src/skills-lib/_shared/dispatch-rule.yaml +21 -0
  58. package/src/skills-lib/{cerebro → wizz-cerebro}/SKILL.md +1 -1
  59. package/src/skills-lib/{ctc-align → wizz-ctc-align}/SKILL.md +1 -1
  60. package/src/skills-lib/wizz-decision-maker/references/output-format.md +21 -0
  61. package/src/skills-lib/wizz-decision-maker/references/references-and-extraction.md +32 -1
  62. package/src/skills-lib/wizz-design-needs/references/design-state.md +1 -1
  63. package/src/skills-lib/wizz-design-needs/references/gap-analysis.md +3 -1
  64. package/src/skills-lib/{enhance-prompt → wizz-enhance-prompt}/SKILL.md +1 -1
  65. package/src/skills-lib/{find-skills → wizz-find-skills}/SKILL.md +1 -1
  66. package/src/skills-lib/wizz-huashu-design/references/sfx-library.md +1 -1
  67. package/src/skills-lib/wizz-huashu-design/scripts/add-music.sh +1 -1
  68. package/src/skills-lib/wizz-huashu-design/scripts/html2pptx.js +45 -37
  69. package/src/skills-lib/wizz-huashu-design/scripts/mix-voiceover.sh +1 -1
  70. package/src/skills-lib/wizz-huashu-design/scripts/narrate-pipeline.mjs +11 -5
  71. package/src/skills-lib/wizz-huashu-design/scripts/test_scripts.py +209 -0
  72. package/src/skills-lib/wizz-huashu-design/scripts/tts-doubao.mjs +42 -28
  73. package/src/skills-lib/wizz-ig-profile-optimizer/SKILL.md +1 -1
  74. package/src/skills-lib/wizz-inicio-de-projeto/SKILL.md +1 -1
  75. package/src/skills-lib/wizz-launch-readiness/SKILL.md +7 -7
  76. package/src/skills-lib/wizz-launch-readiness/references/02-seguranca.md +3 -3
  77. package/src/skills-lib/wizz-launch-readiness/references/03-seo-descoberta.md +5 -5
  78. package/src/skills-lib/wizz-launch-readiness/references/04-analytics-medicao.md +1 -1
  79. package/src/skills-lib/wizz-launch-readiness/references/05-conteudo-prova-social.md +6 -6
  80. package/src/skills-lib/wizz-launch-readiness/references/06-legal-lgpd.md +1 -1
  81. package/src/skills-lib/wizz-launch-readiness/references/08-fusao-priorizacao.md +1 -1
  82. package/src/skills-lib/wizz-offer-forge/SKILL.md +3 -3
  83. package/src/skills-lib/{pesquisa-de-publico-do-piva → wizz-pesquisa-de-publico-do-piva}/README.md +1 -1
  84. package/src/skills-lib/{pesquisa-de-publico-do-piva → wizz-pesquisa-de-publico-do-piva}/SKILL.md +1 -1
  85. package/src/skills-lib/wizz-premium-landing-ui-researcher/references/component-sources.md +23 -23
  86. package/src/skills-lib/wizz-premium-landing-ui-researcher/references/source-first-protocol.md +15 -3
  87. package/src/skills-lib/wizz-premium-landing-ui-researcher/references/source-links.md +14 -8
  88. package/src/skills-lib/wizz-premium-landing-ui-researcher/references/web-access-and-evidence.md +4 -3
  89. package/src/skills-lib/wizz-router/SKILL.md +3 -3
  90. package/src/skills-lib/wizz-router/references/routing-table-flat.md +10 -9
  91. package/src/skills-lib/{security-audit-pentest → wizz-security-audit-pentest}/SKILL.md +1 -1
  92. package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/SKILL.md +1 -1
  93. package/src/skills-lib/wizz-ui-component-curator/SKILL.md +1 -1
  94. package/src/skills-lib/wizz-video-effects/SKILL.md +3 -3
  95. package/src/skills-lib/wizz-video-effects/references/data-contract.md +3 -3
  96. package/src/skills-lib/wizz-video-effects/scripts/fill_breaths.py +15 -1
  97. package/src/skills-lib/wizz-video-effects/scripts/test_fill_breaths.py +20 -0
  98. package/src/skills-lib/wizz-video-styles/SKILL.md +2 -2
  99. package/src/skills-lib/wizz-video-styles/data/recipes.json +32 -1
  100. package/src/skills-lib/wizz-video-styles/data/references.json +211 -1
  101. package/src/skills-lib/wizz-video-styles/data/styles.json +54 -0
  102. package/src/skills-lib/wizz-video-styles/references/generated-prompts.md +13 -3
  103. package/src/skills-lib/wizz-video-styles/references/style-picker.md +4 -2
  104. package/src/skills-lib/wizz-video-styles/references/styles/ui-morph.md +68 -0
  105. package/src/skills-lib/wizz-video-styles/scripts/analyze.sh +21 -9
  106. package/src/skills-lib/wizz-video-styles/scripts/references.mjs +1 -0
  107. package/src/skills-lib/wizz-web-security/SKILL.md +1 -1
  108. package/tools/fetch-assets.mjs +1 -1
  109. package/tools/hooks/README.md +18 -14
  110. package/tools/hooks/rtk-rewrite.js +95 -0
  111. package/tools/hooks/security-defensive-context.js +17 -1
  112. package/tools/hooks/session-rules.js +25 -5
  113. package/tools/hooks/wizz-arrumar.js +126 -12
  114. package/tools/hooks/wizz-artifact-guard.js +41 -7
  115. package/tools/hooks/wizz-cerebro-lib.js +239 -0
  116. package/tools/hooks/wizz-close-reminder.js +124 -0
  117. package/tools/hooks/wizz-debug-lib.js +55 -0
  118. package/tools/hooks/wizz-decision-trace.js +19 -2
  119. package/tools/hooks/wizz-feature.js +20 -4
  120. package/tools/hooks/wizz-router-enforce.js +30 -13
  121. package/tools/installer/command-text.js +243 -0
  122. package/tools/installer/commands/catalog.js +2 -0
  123. package/tools/installer/commands/doctor.js +10 -12
  124. package/tools/installer/commands/fetch-assets.js +5 -5
  125. package/tools/installer/commands/install.js +6 -7
  126. package/tools/installer/commands/resolve.js +17 -17
  127. package/tools/installer/commands/trace-report.js +40 -37
  128. package/tools/installer/core/doctor.js +60 -55
  129. package/tools/installer/core/hooks-settings.js +88 -14
  130. package/tools/installer/core/hooks-smoke.js +134 -0
  131. package/tools/installer/core/installer.js +61 -52
  132. package/tools/installer/core/quick-update.js +2 -3
  133. package/tools/installer/guided/labels.js +5 -0
  134. package/tools/installer/install-text.js +201 -0
  135. package/tools/installer/modules/channel-plan.js +2 -2
  136. package/tools/installer/modules/env-vars.js +5 -6
  137. package/tools/installer/modules/skills-lib.js +1 -1
  138. package/tools/installer/ui.js +17 -9
  139. /package/src/skills-lib/{cerebro → wizz-cerebro}/references/comandos-pesquisa-e-memoria.md +0 -0
  140. /package/src/skills-lib/{cerebro → wizz-cerebro}/references/comandos-rotina-e-captura.md +0 -0
  141. /package/src/skills-lib/{cerebro → wizz-cerebro}/references/comandos-sessao.md +0 -0
  142. /package/src/skills-lib/{ctc-align → wizz-ctc-align}/scripts/align.py +0 -0
  143. /package/src/skills-lib/{enhance-prompt → wizz-enhance-prompt}/README.md +0 -0
  144. /package/src/skills-lib/{enhance-prompt → wizz-enhance-prompt}/references/KEYWORDS.md +0 -0
  145. /package/src/skills-lib/{pesquisa-de-publico-do-piva → wizz-pesquisa-de-publico-do-piva}/assets/template.html +0 -0
  146. /package/src/skills-lib/{pesquisa-de-publico-do-piva → wizz-pesquisa-de-publico-do-piva}/prompts.md +0 -0
  147. /package/src/skills-lib/{security-audit-pentest → wizz-security-audit-pentest}/references/stage-1-validacao-entrada.md +0 -0
  148. /package/src/skills-lib/{security-audit-pentest → wizz-security-audit-pentest}/references/stage-2-autorizacao.md +0 -0
  149. /package/src/skills-lib/{security-audit-pentest → wizz-security-audit-pentest}/references/stage-3-abuso-volume.md +0 -0
  150. /package/src/skills-lib/{security-audit-pentest → wizz-security-audit-pentest}/references/stage-4-vazamento-resposta.md +0 -0
  151. /package/src/skills-lib/{security-audit-pentest → wizz-security-audit-pentest}/references/stage-5-fusao-plano.md +0 -0
  152. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/01-cta-principal.md +0 -0
  153. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/02-barra-fixa-mobile.md +0 -0
  154. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/03-tempo-de-resposta.md +0 -0
  155. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/04-prova-social.md +0 -0
  156. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/05-faq-decisao.md +0 -0
  157. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/06-imagens-reais.md +0 -0
  158. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/07-endereco-como-chegar.md +0 -0
  159. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/08-titles.md +0 -0
  160. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/09-open-graph.md +0 -0
  161. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/10-breadcrumbs.md +0 -0
  162. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/11-alt-text.md +0 -0
  163. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/12-schema-local.md +0 -0
  164. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/13-robots-sitemap.md +0 -0
  165. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/14-privacidade-termos.md +0 -0
  166. /package/src/skills-lib/{site-launch-kit → wizz-site-launch-kit}/references/15-medicao.md +0 -0
@@ -0,0 +1,239 @@
1
+ // wizz-cerebro-lib.js — grava o fechamento de uma feature no Cérebro.
2
+ //
3
+ // Fonte de verdade: wizz-method/tools/hooks/. Não é hook registrado: é a
4
+ // biblioteca do `wizz-feature.js encerrar --cerebro`, copiada junto (installer
5
+ // e sync:global). Tira da memória do modelo o dever de fechamento "bloco
6
+ // Estado + 1 linha na tabela Sessões" (`_shared/cerebro.md`).
7
+ //
8
+ // Regras do protocolo do Cérebro (skill `wizz-cerebro`, comandos-sessao.md):
9
+ // - vault: `~/Documents/projects/Obsidian Vault/CEREBRO.md`. O `find ~`
10
+ // do protocolo fica de fora (caro demais para uma CLI); quem usa outro
11
+ // lugar aponta `WIZZ_CEREBRO_VAULT` ou `cerebro_vault:` no config.
12
+ // - estado mora em `projetos/<nome>.md`, bloco `## Estado` no topo.
13
+ // - `Sessões` com teto de 15 linhas; nunca apagar histórico: as antigas
14
+ // vão para `_index/cerebro-archive.md`, seção `## Sessões antigas —
15
+ // <projeto>`. Decisão do dono (2026-10-15): sessão antiga já foi feita,
16
+ // então sai do arquivo que o `/wizz-cerebro ver` lê sempre e fica no arquivo
17
+ // morto, que só se lê por grep quando precisar. Fora isso, a CLI não toca
18
+ // nenhum outro arquivo do vault.
19
+ //
20
+ // O bloco Estado é curado à mão; sobrescrever inteiro apagaria o que a CLI
21
+ // não sabe. Então só duas linhas são da CLI: `**Feature `<slug>`:**` (uma
22
+ // por feature) e `**Última sessão:**`. Ambas são substituídas, nunca
23
+ // empilhadas: rodar duas vezes deixa o arquivo idêntico.
24
+
25
+ const fs = require('node:fs');
26
+ const os = require('node:os');
27
+ const path = require('node:path');
28
+ const lib = require('./wizz-feature-lib.js');
29
+
30
+ const SESSIONS_CAP = 15;
31
+ const SUMMARY_MAX = 160;
32
+ const DEFAULT_VAULT = path.join('Documents', 'projects', 'Obsidian Vault');
33
+ const ARCHIVE_FILE = path.join('_index', 'cerebro-archive.md');
34
+ const ARCHIVE_HEADER = [
35
+ '# Arquivo do Cérebro',
36
+ '',
37
+ '> Excedente movido do CEREBRO.md e dos arquivos de projeto. Nada aqui foi apagado, só tirado do índice.',
38
+ '> Este arquivo é para **grep**, nunca para Read completo.',
39
+ '',
40
+ ];
41
+
42
+ const hasCerebro = (dir) => {
43
+ try {
44
+ return Boolean(dir) && fs.statSync(path.join(dir, 'CEREBRO.md')).isFile();
45
+ } catch {
46
+ return false;
47
+ }
48
+ };
49
+
50
+ const expandHome = (p) => (p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : p);
51
+
52
+ /**
53
+ * Acha o vault: `WIZZ_CEREBRO_VAULT` > `cerebro_vault` do config > padrão do
54
+ * protocolo no HOME. Só vale pasta com CEREBRO.md.
55
+ * @param {Record<string,string>} configValues
56
+ * @returns {string} caminho do vault ou ''
57
+ */
58
+ function findVault(configValues = {}) {
59
+ const candidates = [process.env.WIZZ_CEREBRO_VAULT, configValues.cerebro_vault, path.join(os.homedir(), DEFAULT_VAULT)];
60
+ for (const c of candidates) {
61
+ if (typeof c === 'string' && c.trim() && hasCerebro(expandHome(c.trim()))) return expandHome(c.trim());
62
+ }
63
+ return '';
64
+ }
65
+
66
+ /** Arquivo `projetos/<nome>.md` do projeto: --projeto > project_name > pasta. */
67
+ function findProjectFile(vault, root, configValues, projeto) {
68
+ const names = [projeto, configValues.project_name, path.basename(root)].filter((n) => typeof n === 'string' && n.trim());
69
+ for (const n of names) {
70
+ for (const variant of new Set([n.trim(), lib.toSlug(n)])) {
71
+ const file = path.join(vault, 'projetos', `${variant}.md`);
72
+ if (variant && fs.existsSync(file)) return file;
73
+ }
74
+ }
75
+ return '';
76
+ }
77
+
78
+ const oneLine = (s) => s.replaceAll(/\s+/g, ' ').trim();
79
+ const cell = (s) => oneLine(s).replaceAll('|', String.raw`\|`);
80
+
81
+ /** Primeira linha útil da seção `## Implementação` da spec, ou ''. */
82
+ function implementationSummary(specText) {
83
+ const content = lib.sectionContent(specText || '', 'Implementa[çc][ãa]o') || '';
84
+ const first = content.split('\n').find((l) => l.trim()) || '';
85
+ const clean = oneLine(first.replace(/^[-*]\s+/, ''));
86
+ return clean.length > SUMMARY_MAX ? `${clean.slice(0, SUMMARY_MAX - 1)}…` : clean;
87
+ }
88
+
89
+ /** Fim de uma seção `## X` que começa em `start` (próximo `#`/`##` ou EOF). */
90
+ function sectionEnd(lines, start) {
91
+ let end = start + 1;
92
+ while (end < lines.length && !/^#{1,2}\s/.test(lines[end])) end++;
93
+ return end;
94
+ }
95
+
96
+ const headingIndex = (lines, re) => lines.findIndex((l) => re.test(l.trim()));
97
+
98
+ /** Onde nasce um bloco Estado novo: abaixo do título e das linhas `>`. */
99
+ function estadoInsertAt(lines) {
100
+ const title = lines.findIndex((l) => /^#\s/.test(l));
101
+ if (title === -1) return 0;
102
+ let i = title + 1;
103
+ while (i < lines.length && lines[i].startsWith('>')) i++;
104
+ return i;
105
+ }
106
+
107
+ /**
108
+ * Atualiza (ou cria) o bloco `## Estado` com as duas linhas da CLI.
109
+ * @returns {string[]} linhas novas
110
+ */
111
+ function upsertEstado(lines, { featureLine, sessionLine, slug }) {
112
+ const head = headingIndex(lines, /^##\s+Estado\s*$/i);
113
+ if (head === -1) {
114
+ const at = estadoInsertAt(lines);
115
+ const block = ['', '## Estado', featureLine, sessionLine];
116
+ const rest = lines.slice(at);
117
+ return [...lines.slice(0, at), ...block, ...(rest[0] === '' ? rest : ['', ...rest])];
118
+ }
119
+ const end = sectionEnd(lines, head);
120
+ const featureKey = `**Feature \`${slug}\`:**`;
121
+ let body = lines.slice(head + 1, end);
122
+ const featIdx = body.findIndex((l) => l.includes(featureKey));
123
+ if (featIdx === -1) body = [...(body[0] === '' ? [''] : []), featureLine, ...body.slice(body[0] === '' ? 1 : 0)];
124
+ else body = body.map((l, i) => (i === featIdx ? featureLine : l));
125
+ const sessIdx = body.findIndex((l) => /^\s*-\s+\*\*Última sessão:\*\*/.test(l));
126
+ if (sessIdx === -1) {
127
+ let last = body.length;
128
+ while (last > 0 && body[last - 1].trim() === '') last--;
129
+ body = [...body.slice(0, last), sessionLine, ...body.slice(last)];
130
+ } else body = body.map((l, i) => (i === sessIdx ? sessionLine : l));
131
+ return [...lines.slice(0, head + 1), ...body, ...lines.slice(end)];
132
+ }
133
+
134
+ /** Separa a tabela de uma seção: {header, rows, tableStart, tableEnd}. */
135
+ function readTable(lines, head) {
136
+ const end = sectionEnd(lines, head);
137
+ let start = head + 1;
138
+ while (start < end && !lines[start].startsWith('|')) start++;
139
+ if (start >= end || !lines[start + 1] || !/^\|[\s:|-]+\|?\s*$/.test(lines[start + 1])) return null;
140
+ let stop = start + 2;
141
+ while (stop < end && lines[stop].startsWith('|')) stop++;
142
+ return { header: lines.slice(start, start + 2), rows: lines.slice(start + 2, stop), tableStart: start, tableEnd: stop };
143
+ }
144
+
145
+ const isBlankRow = (row) => row.replaceAll(/[|\s]/g, '') === '';
146
+
147
+ const escapeRe = (text) => text.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
148
+
149
+ /**
150
+ * Põe linhas antigas na seção `## Sessões antigas — <projeto>` (formato da skill cerebro) do arquivo
151
+ * morto (cria a seção no fim se faltar).
152
+ */
153
+ function archiveRows(lines, overflow, header, projeto) {
154
+ if (overflow.length === 0) return lines;
155
+ const title = `## Sessões antigas — ${projeto}`;
156
+ const head = headingIndex(lines, new RegExp(`^${escapeRe(title)}\\s*$`));
157
+ if (head === -1) {
158
+ const base = lines.at(-1) === '' ? lines.slice(0, -1) : lines;
159
+ return [...base, '', title, ...header, ...overflow, ''];
160
+ }
161
+ const table = readTable(lines, head);
162
+ if (!table) return [...lines.slice(0, head + 1), ...header, ...overflow, ...lines.slice(head + 1)];
163
+ return [...lines.slice(0, table.tableEnd), ...overflow, ...lines.slice(table.tableEnd)];
164
+ }
165
+
166
+ /**
167
+ * Põe (ou substitui) a linha da feature na tabela Sessões, com teto.
168
+ * Chave de idempotência: mesma data + "`<slug>` encerrada".
169
+ * @returns {{lines: string[], overflow: string[], header: string[]}} overflow = linhas que saem para o arquivo morto
170
+ */
171
+ function upsertSessoes(lines, { row, date, slug }) {
172
+ const head = headingIndex(lines, /^##\s+(Sess(õ|o)es|Sessions)\s*$/i);
173
+ const header = ['| Data | O que rolou |', '|---|---|'];
174
+ if (head === -1) {
175
+ const base = lines.at(-1) === '' ? lines.slice(0, -1) : lines;
176
+ return { lines: [...base, '', '## Sessões', ...header, row, ''], overflow: [], header };
177
+ }
178
+ const table = readTable(lines, head);
179
+ if (!table) return { lines: [...lines.slice(0, head + 1), ...header, row, ...lines.slice(head + 1)], overflow: [], header };
180
+ const key = `\`${slug}\` encerrada`;
181
+ const same = (r) => r.startsWith(`| ${date} |`) && r.includes(key);
182
+ let rows = table.rows.filter((r) => !isBlankRow(r));
183
+ rows = rows.some(same) ? rows.map((r) => (same(r) ? row : r)) : [...rows, row];
184
+ const overflow = rows.length > SESSIONS_CAP ? rows.slice(0, rows.length - SESSIONS_CAP) : [];
185
+ const kept = rows.slice(overflow.length);
186
+ const next = [...lines.slice(0, table.tableStart), ...table.header, ...kept, ...lines.slice(table.tableEnd)];
187
+ return { lines: next, overflow, header: table.header };
188
+ }
189
+
190
+ /** Grava as linhas que saíram da tabela no arquivo morto do vault. */
191
+ function writeArchive(vault, projeto, overflow, header) {
192
+ if (overflow.length === 0) return;
193
+ const file = path.join(vault, ARCHIVE_FILE);
194
+ const original = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : ARCHIVE_HEADER.join('\n');
195
+ const next = archiveRows(original.split('\n'), overflow, header, projeto).join('\n');
196
+ fs.mkdirSync(path.dirname(file), { recursive: true });
197
+ lib.atomicWrite(file, next);
198
+ }
199
+
200
+ /**
201
+ * Grava o fechamento no arquivo do projeto no Cérebro. Fail-open: qualquer
202
+ * falha devolve {gravado: false, motivo} sem lançar.
203
+ * @param {{root: string, slug: string, status: string, projeto?: string}} input
204
+ * @returns {{gravado: boolean, arquivo?: string, motivo?: string}}
205
+ */
206
+ function recordClose({ root, slug, status, projeto }) {
207
+ try {
208
+ const { values } = lib.readConfig(root);
209
+ const vault = findVault(values);
210
+ if (!vault) return { gravado: false, motivo: 'vault do Cérebro não encontrado (WIZZ_CEREBRO_VAULT)' };
211
+ const file = findProjectFile(vault, root, values, projeto);
212
+ if (!file) return { gravado: false, motivo: `sem projetos/<projeto>.md no vault (use --projeto <nome>)` };
213
+
214
+ const date = lib.today();
215
+ const dirs = lib.artifactDirs(root);
216
+ const spec = path.join(dirs.planning, slug, `spec-${slug}.md`);
217
+ const specText = fs.existsSync(spec) ? fs.readFileSync(spec, 'utf8') : '';
218
+ const specRel = path.relative(root, spec);
219
+ const resumo = implementationSummary(specText);
220
+ const featureLine = `- **Feature \`${slug}\`:** ${status} em ${date}. Spec: \`${specRel}\`.${resumo ? ` ${resumo}` : ''}`;
221
+ const sessionLine = `- **Última sessão:** ${date} — feature \`${slug}\` encerrada (${status}).`;
222
+ const row = `| ${date} | Feature \`${slug}\` encerrada (${status}). ${cell(resumo || `Spec: ${specRel}`)} |`;
223
+
224
+ const original = fs.readFileSync(file, 'utf8');
225
+ let lines = original.split('\n');
226
+ lines = upsertEstado(lines, { featureLine, sessionLine, slug });
227
+ const sessoes = upsertSessoes(lines, { row, date, slug });
228
+ const next = sessoes.lines.join('\n');
229
+ // Arquivo morto antes do projeto: se a 2ª escrita falhar, a linha fica
230
+ // duplicada no arquivo morto em vez de sumir.
231
+ writeArchive(vault, path.basename(file, '.md'), sessoes.overflow, sessoes.header);
232
+ if (next !== original) lib.atomicWrite(file, next);
233
+ return { gravado: true, arquivo: file };
234
+ } catch (error) {
235
+ return { gravado: false, motivo: error.message };
236
+ }
237
+ }
238
+
239
+ module.exports = { findVault, findProjectFile, implementationSummary, upsertEstado, upsertSessoes, recordClose, SESSIONS_CAP };
@@ -0,0 +1,124 @@
1
+ // wizz-close-reminder.js — Stop hook: lembrete de fechamento de feature.
2
+ //
3
+ // Fonte de verdade: wizz-method/tools/hooks/. Instalado em `_wizz/hooks/`
4
+ // (installer) e em `~/.claude/hooks/` (sync:global).
5
+ //
6
+ // No fim de cada turno, se a feature ativa já foi ENTREGUE (spec com status
7
+ // `implementado` ou `no-ar`) e o checklist de fechamento do `wizz-feature.js`
8
+ // ainda tem pendência, mostra 1 aviso ao usuário. Antes isso dependia de o
9
+ // modelo lembrar de rodar `verificar`.
10
+ //
11
+ // Forma da saída: `{"systemMessage": "..."}` no stdout, exit 0. É aviso
12
+ // VISÍVEL ao usuário e não bloqueia: `decision: "block"` forçaria o modelo a
13
+ // continuar o turno, o que vira laço quando a pendência depende do dono.
14
+ //
15
+ // Caminho quente barato: sem subprocesso; sai cedo fora de projeto Wizz, sem
16
+ // feature ativa ou com a feature ainda em andamento (rascunho/aprovado).
17
+ // Silencioso quando não há pendência. Mesmo aviso não repete na mesma sessão
18
+ // (estado por session_id em os.tmpdir(), ou WIZZ_CLOSE_REMINDER_STATE).
19
+ // Fail-open: qualquer erro sai 0 em silêncio.
20
+
21
+ const fs = require('node:fs');
22
+ const os = require('node:os');
23
+ const path = require('node:path');
24
+ const crypto = require('node:crypto');
25
+
26
+ // Diagnóstico opt-in (WIZZ_DEBUG=1). Lib ausente numa cópia parcial: ainda avisa em stderr.
27
+ let debug = (hook, phase, err) => {
28
+ if (process.env.WIZZ_DEBUG !== '1') return;
29
+ try {
30
+ process.stderr.write(`[wizz-debug] ${hook} ${phase}: ${(err && err.message) || err}\n`);
31
+ } catch {
32
+ // stderr fechado
33
+ }
34
+ };
35
+ try {
36
+ ({ debug } = require('./wizz-debug-lib.js'));
37
+ } catch (error) {
38
+ debug('close-reminder', 'require wizz-debug-lib', error);
39
+ }
40
+
41
+ const DELIVERED = new Set(['implementado', 'no-ar']);
42
+ const CLI = 'node _wizz/hooks/wizz-feature.js';
43
+
44
+ /**
45
+ * Texto do aviso para o projeto em `cwd`, ou '' quando não há o que avisar.
46
+ * @param {string} cwd
47
+ * @returns {string}
48
+ */
49
+ function pendingMessage(cwd) {
50
+ const lib = require('./wizz-feature-lib.js');
51
+ const root = lib.findWizzRoot(cwd);
52
+ if (!root) return '';
53
+ const state = lib.resolveFeature(root);
54
+ if (!state.slug) return '';
55
+ const { artifactPath, checklist } = require('./wizz-feature.js');
56
+ const spec = artifactPath(root, 'spec', state.slug);
57
+ if (!fs.existsSync(spec)) return '';
58
+ const status = lib.parseFrontmatter(fs.readFileSync(spec, 'utf8')).data.status;
59
+ if (!DELIVERED.has(status)) return '';
60
+ const { pendentes } = checklist(state, state.slug);
61
+ const stillActive = status === 'no-ar' && state.fromConfig === state.slug;
62
+ if (pendentes.length === 0 && !stillActive) return '';
63
+ const what =
64
+ pendentes.length > 0
65
+ ? `pendente: ${pendentes.map((p) => p.item + (p.detalhe ? ` (${p.detalhe})` : '')).join('; ')}`
66
+ : 'entregue mas ainda ativa';
67
+ return `⚠️ Fechamento da feature ${state.slug} (${status}) ${what}. Resolva, rode \`${CLI} verificar\` e feche com \`${CLI} encerrar --cerebro\`.`;
68
+ }
69
+
70
+ function stateFile(sessionId) {
71
+ const dir = process.env.WIZZ_CLOSE_REMINDER_STATE || path.join(os.tmpdir(), 'wizz-close-reminder');
72
+ const safe = String(sessionId)
73
+ .replaceAll(/[^\w-]/g, '')
74
+ .slice(0, 80);
75
+ return safe ? path.join(dir, `${safe}.txt`) : '';
76
+ }
77
+
78
+ /** true se este aviso já saiu nesta sessão; senão grava e devolve false. */
79
+ function alreadyShown(sessionId, message) {
80
+ const file = sessionId ? stateFile(sessionId) : '';
81
+ if (!file) return false;
82
+ const hash = crypto.createHash('sha1').update(message).digest('hex');
83
+ try {
84
+ if (fs.readFileSync(file, 'utf8') === hash) return true;
85
+ } catch {
86
+ // primeira vez nesta sessão
87
+ }
88
+ try {
89
+ fs.mkdirSync(path.dirname(file), { recursive: true });
90
+ fs.writeFileSync(file, hash);
91
+ } catch (error) {
92
+ // sem dedup: avisa de novo no próximo turno, nunca falha
93
+ debug('close-reminder', 'alreadyShown write', error);
94
+ }
95
+ return false;
96
+ }
97
+
98
+ function runHook() {
99
+ const chunks = [];
100
+ const timer = setTimeout(() => {
101
+ debug('close-reminder', 'stdin', 'timeout de 3s sem fim de stdin; saindo sem avisar');
102
+ process.exit(0);
103
+ }, 3000);
104
+ process.stdin.on('data', (c) => chunks.push(c));
105
+ process.stdin.on('end', () => {
106
+ clearTimeout(timer);
107
+ try {
108
+ const payload = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}');
109
+ if (payload.stop_hook_active) return;
110
+ const cwd = payload.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();
111
+ const message = pendingMessage(cwd);
112
+ if (message && !alreadyShown(payload.session_id, message)) {
113
+ process.stdout.write(`${JSON.stringify({ systemMessage: message })}\n`);
114
+ }
115
+ } catch (error) {
116
+ // fail-open: payload ruim, projeto ilegível, nada disso derruba a sessão
117
+ debug('close-reminder', 'pendingMessage', error);
118
+ }
119
+ });
120
+ }
121
+
122
+ if (require.main === module) runHook();
123
+
124
+ module.exports = { pendingMessage };
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Diagnóstico opt-in dos hooks (fail-open continua valendo).
5
+ *
6
+ * Os hooks engolem erro de propósito: nada pode derrubar a sessão. O custo é
7
+ * que uma cópia parcial em `_wizz/hooks/` faz a feature sumir sem rastro.
8
+ * Com `WIZZ_DEBUG=1`, cada erro engolido vira UMA linha em stderr e é
9
+ * anexado a `~/.claude/wizz-debug.log` (`WIZZ_DEBUG_FILE` troca o caminho,
10
+ * é por onde os testes não encostam no home real).
11
+ * Sem `WIZZ_DEBUG=1`: zero saída, zero disco.
12
+ */
13
+
14
+ const fs = require('node:fs');
15
+ const os = require('node:os');
16
+ const path = require('node:path');
17
+
18
+ function isDebugEnabled() {
19
+ return process.env.WIZZ_DEBUG === '1';
20
+ }
21
+
22
+ function debugFilePath() {
23
+ return process.env.WIZZ_DEBUG_FILE || path.join(os.homedir(), '.claude', 'wizz-debug.log');
24
+ }
25
+
26
+ /** Formata a linha: `[wizz-debug] <hook> <fase>: <mensagem>` (sem quebra interna). */
27
+ function formatLine(hook, phase, err) {
28
+ const message = String((err && err.message) || err || 'erro desconhecido').replaceAll(/\s*\n\s*/g, ' ');
29
+ return `[wizz-debug] ${hook} ${phase}: ${message}`;
30
+ }
31
+
32
+ /**
33
+ * Registra um erro engolido. Nunca lança, nunca altera o exit code.
34
+ * @param {string} hook - nome do hook (ex.: 'session-rules')
35
+ * @param {string} phase - onde falhou (ex.: 'require wizz-feature-lib')
36
+ * @param {unknown} err
37
+ */
38
+ function debug(hook, phase, err) {
39
+ if (!isDebugEnabled()) return;
40
+ const line = formatLine(hook, phase, err);
41
+ try {
42
+ process.stderr.write(`${line}\n`);
43
+ } catch {
44
+ // stderr fechado: o log em disco ainda tenta
45
+ }
46
+ try {
47
+ const file = debugFilePath();
48
+ fs.mkdirSync(path.dirname(file), { recursive: true });
49
+ fs.appendFileSync(file, `${new Date().toISOString()} ${line}\n`);
50
+ } catch {
51
+ // diagnóstico nunca derruba o hook
52
+ }
53
+ }
54
+
55
+ module.exports = { debug, isDebugEnabled, debugFilePath, formatLine };
@@ -56,6 +56,21 @@
56
56
  // erro do hook: qualquer falha aqui (arquivo de offsets corrompido, disco
57
57
  // cheio, permissão) nunca derruba o hook — fail-silent, volta a reler tudo.
58
58
 
59
+ // Diagnóstico opt-in (WIZZ_DEBUG=1). Lib ausente numa cópia parcial: ainda avisa em stderr.
60
+ let debug = (hook, phase, err) => {
61
+ if (process.env.WIZZ_DEBUG !== '1') return;
62
+ try {
63
+ process.stderr.write(`[wizz-debug] ${hook} ${phase}: ${(err && err.message) || err}\n`);
64
+ } catch {
65
+ // stderr fechado
66
+ }
67
+ };
68
+ try {
69
+ ({ debug } = require('./wizz-debug-lib.js'));
70
+ } catch (error) {
71
+ debug('decision-trace', 'require wizz-debug-lib', error);
72
+ }
73
+
59
74
  const fs = require('node:fs');
60
75
  const path = require('node:path');
61
76
  const os = require('node:os');
@@ -294,8 +309,9 @@ function extractDecision(text) {
294
309
  function appendDecision(entry) {
295
310
  try {
296
311
  fs.appendFileSync(getTraceFile(), JSON.stringify(entry) + '\n');
297
- } catch {
312
+ } catch (error) {
298
313
  // trace nunca pode derrubar o hook
314
+ debug('decision-trace', 'appendDecision', error);
299
315
  }
300
316
  }
301
317
 
@@ -358,8 +374,9 @@ function runHook() {
358
374
  });
359
375
 
360
376
  process.exit(0);
361
- } catch {
377
+ } catch (error) {
362
378
  // Falha em silêncio em qualquer etapa — nunca bloqueia a sessão.
379
+ debug('decision-trace', 'runHook', error);
363
380
  process.exit(0);
364
381
  }
365
382
  });
@@ -13,7 +13,9 @@
13
13
  // status <status> rascunho|aprovado|implementado|no-ar na spec
14
14
  // verificar checklist de fechamento (exit 1 se houver pendência)
15
15
  // encerrar status final na spec, limpa/troca active_feature
16
- // (recusa com pendência do checklist, salvo --forcar)
16
+ // (recusa com pendência do checklist, salvo --forcar);
17
+ // com --cerebro grava Estado + linha de Sessões no
18
+ // projetos/<projeto>.md do Cérebro (--projeto <nome>)
17
19
  // listar features com pasta e o status de cada spec
18
20
  //
19
21
  // Opções: --json (saída para máquina), --cwd <dir>, --feature <slug>.
@@ -22,6 +24,7 @@
22
24
  const fs = require('node:fs');
23
25
  const path = require('node:path');
24
26
  const lib = require('./wizz-feature-lib.js');
27
+ const cerebro = require('./wizz-cerebro-lib.js');
25
28
 
26
29
  const ALIASES = {
27
30
  resolve: 'resolver',
@@ -286,7 +289,7 @@ function checklist(state, slug, { statusOverride } = {}) {
286
289
  }
287
290
  const pendentes = items.filter((i) => !i.ok);
288
291
  const confirmar = [
289
- 'Cérebro: bloco Estado do projeto atualizado + 1 linha na tabela Sessões',
292
+ 'Cérebro: bloco Estado do projeto atualizado + 1 linha na tabela Sessões (`encerrar --cerebro` grava sozinho)',
290
293
  'Auto-memória: só armadilha reproduzível ou preferência do dono (estado e decisão vão no Cérebro)',
291
294
  ];
292
295
  return { items, pendentes, confirmar };
@@ -305,6 +308,12 @@ function cmdVerificar(state, opts) {
305
308
  };
306
309
  }
307
310
 
311
+ /** `--cerebro`: grava no vault; fail-open, nunca desfaz o encerramento. */
312
+ function cerebroClose(state, slug, status, opts) {
313
+ const projeto = typeof opts.projeto === 'string' ? opts.projeto : '';
314
+ return cerebro.recordClose({ root: state.root, slug, status, projeto });
315
+ }
316
+
308
317
  function cmdEncerrar(state, opts) {
309
318
  const slug = requireFeature(state, opts);
310
319
  if (!slug) return { ok: false, code: 2, line: 'Nenhuma feature ativa para encerrar.' };
@@ -330,9 +339,16 @@ function cmdEncerrar(state, opts) {
330
339
  const parts = [`Feature ${slug} encerrada (${status}) em ${lib.today()}. Spec: ${rel(state.root, file)}.`];
331
340
  parts.push(next ? `Feature ativa agora: ${next}.` : 'Nenhuma feature ativa agora.');
332
341
  if (state.fromBranch === slug) parts.push(`O branch ${state.git.branch} ainda aponta para ${slug}: volte ao tronco ou troque de branch.`);
333
- parts.push(`Registre no Cérebro: Estado do projeto + linha na tabela Sessões (${slug}: ${status} em ${lib.today()}).`);
342
+ const memo = opts.cerebro ? cerebroClose(state, slug, status, opts) : null;
343
+ if (memo && memo.gravado) parts.push(`Cérebro atualizado: ${memo.arquivo} (Estado + linha em Sessões).`);
344
+ else {
345
+ if (memo) parts.push(`Cérebro não gravado (${memo.motivo}).`);
346
+ parts.push(`Registre no Cérebro: Estado do projeto + linha na tabela Sessões (${slug}: ${status} em ${lib.today()}).`);
347
+ }
334
348
  if (pend.length > 0) parts.push(`Pendências: ${pend.map((p) => p.item).join('; ')}.`);
335
- return { ok: true, data: { feature: slug, status, proxima: next, pendencias: pend.map((p) => p.item) }, line: parts.join(' ') };
349
+ const data = { feature: slug, status, proxima: next, pendencias: pend.map((p) => p.item) };
350
+ if (memo) data.cerebro = memo;
351
+ return { ok: true, data, line: parts.join(' ') };
336
352
  }
337
353
 
338
354
  function cmdListar(state) {
@@ -120,17 +120,14 @@ function findWizzRoot(startDir) {
120
120
  return null;
121
121
  }
122
122
 
123
+ // Gerado por tools/generate-dispatch-rule.mjs a partir de
124
+ // src/skills-lib/_shared/dispatch-rule.yaml (`hook_hint`). Não edite a linha
125
+ // abaixo: edite o yaml e rode `npm run generate:dispatch-rule`.
126
+ const WIZZ_PROJECT_HINT =
127
+ '🎛️ Wizz: NÃO invoque wizz-router. Capacidades → rel necessário à entrega → áreas → atalhos. 2+ áreas → `wizz-maestro`. 1 área: 2+ fatores (multi-passo, planejar, artefato memorável) → `wizz-maestro`; senão → Skill da área. Auth/pagamento/permissão/migração → maestro. Personas são skills, não subagent_type; Agent usa tipos disponíveis wizz-exec-*; difícil → wizz-exec-strong (ESCALAR → ultra).';
128
+
123
129
  function buildContext(isWizzProject) {
124
- if (isWizzProject) {
125
- return (
126
- '🎛️ Wizz: NÃO invoque wizz-router. Capacidades → rel necessário à entrega → áreas → atalhos. 2+ áreas → `wizz-maestro`. ' +
127
- '1 área: 2+ fatores (multi-passo, planejar, artefato memorável) → `wizz-maestro`; ' +
128
- 'senão → Skill da área. ' +
129
- 'Auth/pagamento/permissão/migração → maestro. ' +
130
- 'Personas são skills, não subagent_type; Agent usa tipos disponíveis wizz-exec-*' +
131
- '; difícil → wizz-exec-strong (ESCALAR → ultra).'
132
- );
133
- }
130
+ if (isWizzProject) return WIZZ_PROJECT_HINT;
134
131
  return (
135
132
  '🎛️ Pedido não-trivial: invoque a Skill `wizz-router` antes de agir para ' +
136
133
  'mapear skills/MCPs (liste candidatas via AskUserQuestion e confirme). ' +
@@ -138,6 +135,21 @@ function buildContext(isWizzProject) {
138
135
  );
139
136
  }
140
137
 
138
+ // Diagnóstico opt-in (WIZZ_DEBUG=1). Lib ausente numa cópia parcial: ainda avisa em stderr.
139
+ let debug = (hook, phase, err) => {
140
+ if (process.env.WIZZ_DEBUG !== '1') return;
141
+ try {
142
+ process.stderr.write(`[wizz-debug] ${hook} ${phase}: ${(err && err.message) || err}\n`);
143
+ } catch {
144
+ // stderr fechado
145
+ }
146
+ };
147
+ try {
148
+ ({ debug } = require('./wizz-debug-lib.js'));
149
+ } catch (error) {
150
+ debug('router-enforce', 'require wizz-debug-lib', error);
151
+ }
152
+
141
153
  // Linha de feature, por prompt, em projeto Wizz (2026-10-02, caso Spider).
142
154
  // O SessionStart avisa 1x, mas é aqui que o pedido nomeado chega: sem este
143
155
  // lembrete o despacho saía sem feature e o artefato caía na raiz. Fail-open:
@@ -152,7 +164,8 @@ function featureLine(wizzRoot) {
152
164
  return ` Feature: nenhuma ativa. Pedido nomeado → ANTES de despachar \`${cmd} definir <slug do pedido>\` (sem perguntar); handoff leva \`feature:\`.`;
153
165
  }
154
166
  return ` Feature: ${state.slug}. Outro assunto → \`${cmd} definir <novo>\`; continuação mantém; entrega fechada → \`${cmd} encerrar\`. Handoff leva \`feature: ${state.slug}\`.`;
155
- } catch {
167
+ } catch (error) {
168
+ debug('router-enforce', 'featureLine', error);
156
169
  return '';
157
170
  }
158
171
  }
@@ -197,8 +210,9 @@ function checkFallbackWarnings(isWizzProject, wizzRoot) {
197
210
  warnings.push(`routing-table-flat.md não encontrada em ${flatTablePath} — rode "npm run sync:global" no repo wizz-method`);
198
211
  }
199
212
  }
200
- } catch {
213
+ } catch (error) {
201
214
  // fail-silent — checagem de fallback nunca bloqueia o prompt
215
+ debug('router-enforce', 'checkFallbackWarnings', error);
202
216
  }
203
217
  return warnings;
204
218
  }
@@ -218,7 +232,10 @@ function emitFallbackWarnings(warnings) {
218
232
 
219
233
  function runHook() {
220
234
  let input = '';
221
- const stdinTimeout = setTimeout(() => process.exit(0), 3000);
235
+ const stdinTimeout = setTimeout(() => {
236
+ debug('router-enforce', 'stdin', 'timeout de 3s sem fim de stdin; saindo sem rotear');
237
+ process.exit(0);
238
+ }, 3000);
222
239
  process.stdin.setEncoding('utf8');
223
240
  process.stdin.on('data', (chunk) => (input += chunk));
224
241
  process.stdin.on('end', () => {