@fontdata/ai-harness 3.2.0 → 3.2.2

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "fontdata",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "mcpServers": {
5
- "banco_dados": { "command": "npx", "args": ["-y", "@fontdata/ai-harness@3.2.0", "banco-dados-mcp"] }
5
+ "banco_dados": { "command": "npx", "args": ["-y", "@fontdata/ai-harness@3.2.2", "banco-dados-mcp"] }
6
6
  },
7
7
  "description": "Harness de engenharia com IA da Fontdata para Claude Code",
8
8
  "author": { "name": "Fontdata" },
package/README.md CHANGED
@@ -95,7 +95,7 @@ npx @fontdata/ai-harness init --comandos resolver-bug,nova-tarefa --sem-harness
95
95
  | `--comandos a,b,c` | Seleção manual |
96
96
  | `--sem-harness` | Não inclui o comando-mestre `/harness` |
97
97
  | `--sem-hooks` / `--sem-agents` | Não instala hooks / agents |
98
- | `--sem-comentarios` | Não instala a rule de comentários, o hook e a catraca do legado |
98
+ | `--sem-comentarios` | Não instala a rule de comentários, o hook e a catraca do legado e lembra a recusa no config |
99
99
  | `--sem-banco-dados` | Não registra o servidor MCP de banco no `.mcp.json` e lembra a recusa no config |
100
100
  | `--claude-md criar\|manter\|backup\|sobrescrever` | O que fazer com o `CLAUDE.md` (padrão: `criar`) |
101
101
  | `--conflito sobrescrever\|pular` | Arquivos que já existem (padrão: `pular`) |
@@ -157,7 +157,7 @@ projeto, cadastro, limites do filtro, produção, mascaramento e dúvidas freque
157
157
  |---|---|
158
158
  | `init [caminho]` | Instala comandos, skills, agents e hooks no projeto |
159
159
  | `doctor [caminho]` | Diagnostica instalação, contrato dos documentos, rastro, bloqueios antigos, citações às pastas antigas de `docs/` no código e versão do Node |
160
- | `update [caminho]` | Atualiza o que foi instalado sem sobrescrever alterações locais (`--check`, `--yes`, `--forcar`) |
160
+ | `update [caminho]` | Atualiza o que foi instalado sem sobrescrever alterações locais (`--check`, `--yes`, `--forcar`, `--sem-banco-dados`, `--sem-comentarios`) |
161
161
  | `migrar [caminho]` | Move os documentos do layout anterior para `docs/trabalhos/<id>/` (`--yes`); `--reclassificar` corrige planos migrados que ficaram em execução |
162
162
  | `memoria [caminho]` | Índice de quem mexeu em cada arquivo (`--arquivo <caminho>`, `--regressao <id>`) |
163
163
  | `evento <evento>` | Grava no rastro do trabalho (usado por fluxos e agents) |
@@ -248,7 +248,11 @@ também acionado pelo modelo quando o pedido combina.
248
248
  ### Setup guiado
249
249
  - `/harness` **(skill)** — conduz o setup em etapas (ideia, contexto, ignores, plano do start, scaffold, peças do harness, auto-tag e execução); `/harness <etapa>` roda uma etapa isolada
250
250
 
251
- O padrão de comentários não é um comando: a rule, o hook e a catraca do legado vêm do `init` e do `update`.
251
+ O padrão de comentários não é um comando: a rule, o hook e a catraca do legado vêm do `init` e do `update`. O
252
+ `update` também o instala em projetos cujo lock é anterior a ele. Para recusar, use `--sem-comentarios` no `init` ou
253
+ no `update`: a escolha fica em `comentarios.instalar: false` no `.ai-harness/config.json`, e o `update` a respeita.
254
+ Para reativar, apague a chave e rode `update --yes` (ou `npx @fontdata/ai-harness comentarios --yes`). Sem o padrão
255
+ e sem a recusa, o `doctor` avisa com `comentarios-ausente`.
252
256
 
253
257
  A skill `lgpd-checker`, usada pela auditoria de segurança, é uma cópia vendorizada de
254
258
  [bittencourtthulio/lgpd-checker](https://github.com/bittencourtthulio/lgpd-checker); veja
package/dist/cli.js CHANGED
@@ -52,6 +52,7 @@ Gerenciar:
52
52
  init [caminho] Instala os slash commands em .claude/commands do projeto (interativo)
53
53
  doctor [caminho] Diagnostica a instalação (arquivos, lock, skills, agents, hooks, Node)
54
54
  update [caminho] Atualiza o que foi instalado: --check só lista, --yes aplica, --forcar sobrescreve conflitos
55
+ --sem-comentarios recusa o padrão de comentários (grava comentarios.instalar: false)
55
56
  --sem-banco-dados: não registra o MCP de banco no .mcp.json e lembra a recusa no config
56
57
  migrar [caminho] Move docs/specs, plans, bugs e investigacoes para docs/trabalhos/<id>/ (--yes aplica)
57
58
  --reclassificar: planos já migrados em em-execucao viram concluido ou legado
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { EOL } from 'node:os';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { templatesDir } from '../catalog.js';
5
+ import { gravarInstalarComentarios } from '../config/config.js';
5
6
  import { suggestFlag } from '../install/flags.js';
6
7
  import { hashContent } from '../install/lock.js';
7
8
  import { executePlan, packageInfo } from '../install/plan.js';
@@ -66,6 +67,8 @@ export function runCommentsCommand(argv) {
66
67
  };
67
68
  executePlan(root, plan, packageInfo());
68
69
  mergeSettings(root, { hooks: commentsHooksConfig() });
70
+ // Instalar pelo comando desfaz uma recusa guardada pelo `--sem-comentarios`.
71
+ gravarInstalarComentarios(root, true);
69
72
  const legacy = ensureCommentsBaseline(root);
70
73
  if (legacy !== null) {
71
74
  console.log(`${EOL}Catraca gerada: ${legacy} arquivo(s) legado(s) com violações toleradas. Eles não precisam ser limpos de uma vez, só não podem piorar.`);
@@ -72,6 +72,7 @@ export function defaultConfig() {
72
72
  projeto: {},
73
73
  declarado: false,
74
74
  },
75
+ comentarios: { instalar: true },
75
76
  };
76
77
  }
77
78
  function inteiroEntre(value, min, max) {
@@ -232,6 +233,12 @@ export function readConfig(root) {
232
233
  else
233
234
  problems.push(`bloqueios.dias_para_aviso deve ser um inteiro positivo em ${CONFIG_PATH}.`);
234
235
  }
236
+ if (isObject(raw.comentarios) && 'instalar' in raw.comentarios) {
237
+ if (typeof raw.comentarios.instalar === 'boolean')
238
+ config.comentarios.instalar = raw.comentarios.instalar;
239
+ else
240
+ problems.push(`comentarios.instalar deve ser true ou false em ${CONFIG_PATH}.`);
241
+ }
235
242
  const secao = soSecaoLegada(raw) ? raw[BANCO_CHAVE_LEGADA] : raw[BANCO_CHAVE];
236
243
  if (isObject(secao))
237
244
  lerBanco(secao, config.banco_dados, problems);
@@ -243,14 +250,18 @@ export function readConfig(root) {
243
250
  }
244
251
  return { config, problems };
245
252
  }
246
- /** Cria a config com os padrões, sem a seção banco_dados; retorna false se já existia (nunca sobrescreve). */
253
+ /** Os padrões que vão para o arquivo: sem as seções banco_dados e comentarios, que só entram quando mudam. */
254
+ function configGravada() {
255
+ const { banco_dados: _banco, comentarios: _comentarios, ...gravada } = defaultConfig();
256
+ return gravada;
257
+ }
258
+ /** Cria a config com os padrões, sem as seções banco_dados e comentarios; retorna false se já existia (nunca sobrescreve). */
247
259
  export function writeDefaultConfig(root) {
248
260
  const file = join(root, CONFIG_PATH);
249
261
  if (existsSync(file))
250
262
  return false;
251
263
  mkdirSync(dirname(file), { recursive: true });
252
- const { banco_dados: _banco, ...gravada } = defaultConfig();
253
- writeFileSync(file, `${JSON.stringify(gravada, null, 2)}\n`);
264
+ writeFileSync(file, `${JSON.stringify(configGravada(), null, 2)}\n`);
254
265
  return true;
255
266
  }
256
267
  function lerJsonParaEscrita(file, chave) {
@@ -336,8 +347,7 @@ export function gravarRegistrarMcp(root, valor) {
336
347
  raw = lerJsonParaEscrita(file, 'banco_dados.registrar_mcp');
337
348
  }
338
349
  else {
339
- const { banco_dados: _banco, ...padrao } = defaultConfig();
340
- raw = padrao;
350
+ raw = configGravada();
341
351
  }
342
352
  const banco = secaoBanco(raw);
343
353
  if (banco.registrar_mcp === valor)
@@ -345,6 +355,24 @@ export function gravarRegistrarMcp(root, valor) {
345
355
  mkdirSync(dirname(file), { recursive: true });
346
356
  writeFileSync(file, `${JSON.stringify(comSecaoBanco(raw, { ...banco, registrar_mcp: valor }), null, 2)}\n`);
347
357
  }
358
+ /**
359
+ * Grava a recusa do padrão de comentários (`comentarios.instalar: false`), preservando o resto do JSON; sem config,
360
+ * cria uma com os padrões e essa chave. `true` desfaz a recusa apagando a chave, e não cria o arquivo. JSON inválido
361
+ * é recusado sem tocar no arquivo.
362
+ */
363
+ export function gravarInstalarComentarios(root, valor) {
364
+ const file = join(root, CONFIG_PATH);
365
+ if (!existsSync(file) && valor)
366
+ return;
367
+ const raw = existsSync(file) ? lerJsonParaEscrita(file, 'comentarios.instalar') : configGravada();
368
+ const { instalar: atual, ...resto } = isObject(raw.comentarios) ? raw.comentarios : {};
369
+ if (valor ? atual === undefined || atual === true : atual === false)
370
+ return;
371
+ const { comentarios: _comentarios, ...semSecao } = raw;
372
+ const proxima = valor ? (Object.keys(resto).length ? { ...raw, comentarios: resto } : semSecao) : { ...raw, comentarios: { ...resto, instalar: false } };
373
+ mkdirSync(dirname(file), { recursive: true });
374
+ writeFileSync(file, `${JSON.stringify(proxima, null, 2)}\n`);
375
+ }
348
376
  const BLOCK_START = '# ai-harness (gerenciado pelo harness; não edite entre estas linhas)';
349
377
  const BLOCK_END = '# /ai-harness';
350
378
  /**
@@ -81,6 +81,10 @@ function validateLine(event, line) {
81
81
  }
82
82
  return problems;
83
83
  }
84
+ /** Valida um evento já montado (o mesmo contrato do doctor), para recusá-lo antes de gravar. */
85
+ export function validateEvent(event) {
86
+ return validateLine(event, 1);
87
+ }
84
88
  /** Valida o conteúdo de um arquivo de rastro; a última linha sem `\n` é ignorada (escrita em andamento). */
85
89
  export function validateEventLog(content) {
86
90
  const complete = content.split('\n').slice(0, -1);
@@ -390,6 +390,19 @@ function checkOldCommentsBlock(root) {
390
390
  },
391
391
  ];
392
392
  }
393
+ /** Lock anterior ao padrão de comentários e sem a recusa no config: o update o instala. */
394
+ function checkComments(lock, config) {
395
+ if (!config.comentarios.instalar || lock.itens.some((item) => item.tipo === 'hook' && item.id === 'comentarios'))
396
+ return [];
397
+ return [
398
+ {
399
+ id: 'comentarios-ausente',
400
+ severidade: 'aviso',
401
+ mensagem: 'O padrão de comentários (rule, hook e catraca do legado) não está instalado, e a recusa não está registrada.',
402
+ correcao: `Instale com \`npx @fontdata/ai-harness update --yes\`, ou recuse com \`update --sem-comentarios --yes\`, que grava comentarios.instalar: false no ${CONFIG_PATH}.`,
403
+ },
404
+ ];
405
+ }
393
406
  function checkConfig(problems) {
394
407
  return problems.map((mensagem) => ({
395
408
  id: 'config-invalida',
@@ -507,7 +520,7 @@ export function runDoctor(root, options = {}) {
507
520
  return [
508
521
  ...checkNode(options.nodeVersion ?? process.versions.node),
509
522
  ...lockFindings,
510
- ...(lock ? [...checkFiles(root, lock), ...checkRequiredSkills(root, lock)] : []),
523
+ ...(lock ? [...checkFiles(root, lock), ...checkRequiredSkills(root, lock), ...checkComments(lock, config)] : []),
511
524
  ...checkShadowedCommands(root),
512
525
  ...checkOldCommentsBlock(root),
513
526
  ...checkAgents(root),
@@ -7,7 +7,7 @@ import { buildLockItem, LOCK_VERSION, readLock, writeLock } from './lock.js';
7
7
  import { HOOKS_ITEM_ID, PROJECT_HOOKS_DIR, projectHooksConfig } from './hooks.js';
8
8
  import { neededSkillsFor, resolveSelection } from './selection.js';
9
9
  import { BANCO_MCP_NOME, bancoMcpServer, MCP_PATH, mergeMcpServers } from './mcp.js';
10
- import { gravarRegistrarMcp } from '../config/config.js';
10
+ import { gravarInstalarComentarios, gravarRegistrarMcp } from '../config/config.js';
11
11
  import { COMMENTS_RULE_PATH, commentsHooksConfig, ensureCommentsBaseline } from '../comentarios/instalar.js';
12
12
  import { combineHooks, HARNESS_DENY, harnessPermissions, mergeSettings } from './settings.js';
13
13
  /** Rule de convenção com o que é específico do projeto (D-38): é do projeto, então fica fora do lock. */
@@ -107,6 +107,7 @@ export function planInstall(catalog, root, options) {
107
107
  claudeMdBackup: false,
108
108
  registerHooks: Boolean(options.hooks),
109
109
  registerComments: Boolean(options.comments),
110
+ comentariosRecusados: !options.comments,
110
111
  };
111
112
  const add = (tipo, id) => {
112
113
  const paths = [];
@@ -173,6 +174,10 @@ export function executePlan(root, plan, pkg, now = new Date()) {
173
174
  mergeMcpServers(root, { [BANCO_MCP_NOME]: bancoMcpServer(pkg.versao) }, now);
174
175
  if (plan.mcpRecusado)
175
176
  gravarRegistrarMcp(root, false);
177
+ if (plan.comentariosRecusados)
178
+ gravarInstalarComentarios(root, false);
179
+ else if (plan.registerComments)
180
+ gravarInstalarComentarios(root, true);
176
181
  const items = new Map();
177
182
  for (const item of previous?.itens ?? [])
178
183
  items.set(`${item.tipo}:${item.id}`, item);
@@ -232,5 +237,8 @@ export function describePlan(plan) {
232
237
  if (plan.registerComments) {
233
238
  lines.push('Padrão de comentários: rule carregada em toda sessão, hook que bloqueia a violação e catraca do legado (gerada só na primeira instalação).');
234
239
  }
240
+ else if (plan.comentariosRecusados) {
241
+ lines.push(`Padrão de comentários não instalado (recusado); a escolha fica em comentarios.instalar: false no .ai-harness/config.json, e o update a respeita.`);
242
+ }
235
243
  return lines.join('\n');
236
244
  }
@@ -1,6 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import { pathToFileURL } from 'node:url';
3
3
  import { templatesDir } from '../catalog.js';
4
+ import { validateEvent } from '../contratos/eventos.js';
4
5
  import { suggestFlag } from '../install/flags.js';
5
6
  import { acharRaiz } from '../raiz.js';
6
7
  /** O CLI grava pela mesma biblioteca dos hooks, para as duas formas produzirem o mesmo formato. */
@@ -42,6 +43,12 @@ export async function runEventCommand(argv) {
42
43
  }
43
44
  }
44
45
  }
45
- (await loadRastroLib()).recordEvent(acharRaiz(), fields);
46
+ // Recusa antes de gravar o que o doctor apontaria como rastro-formato-invalido: o rastro é append-only.
47
+ const lib = await loadRastroLib();
48
+ const problems = validateEvent(lib.buildEvent(fields));
49
+ if (problems.length) {
50
+ throw new Error(`Evento fora do contrato (docs/contratos/EVENTOS.md); nada foi gravado.\n${problems.map((p) => `- ${p.mensagem}`).join('\n')}`);
51
+ }
52
+ lib.recordEvent(acharRaiz(), fields);
46
53
  return 0;
47
54
  }
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
  import { allCommands, loadCatalog } from '../catalog.js';
4
4
  import { commentsHooksConfig, ensureCommentsBaseline } from '../comentarios/instalar.js';
5
- import { CONFIG_PATH, gravarRegistrarMcp, migrarSecaoBanco, readConfig, syncGitignore, temSecaoBancoLegada, writeDefaultConfig, } from '../config/config.js';
5
+ import { CONFIG_PATH, gravarInstalarComentarios, gravarRegistrarMcp, migrarSecaoBanco, readConfig, syncGitignore, temSecaoBancoLegada, writeDefaultConfig, } from '../config/config.js';
6
6
  import { applyAtomically } from '../install/atomic.js';
7
7
  import { suggestFlag } from '../install/flags.js';
8
8
  import { HOOKS_ITEM_ID, isHarnessPluginEnabled, projectHooksConfig } from '../install/hooks.js';
@@ -50,7 +50,7 @@ function legacyPath(root, path) {
50
50
  return candidate;
51
51
  }
52
52
  }
53
- export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBanco = false } = {}) {
53
+ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBanco = false, semComentarios = false } = {}) {
54
54
  const recorded = readLock(root);
55
55
  const lock = recorded ?? adoptLegacyInstall(root, legacy);
56
56
  if (!lock) {
@@ -127,6 +127,7 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
127
127
  addItem('skill', skill.id, false);
128
128
  }
129
129
  }
130
+ const { config } = readConfig(root);
130
131
  // Na adoção, a instalação antiga ganha o que a 2.1 instala e ela não tinha.
131
132
  const adotada = !recorded;
132
133
  const pluginHooks = adotada && isHarnessPluginEnabled(root, home);
@@ -135,10 +136,15 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
135
136
  addItem('agent', agent.id, true);
136
137
  if (!pluginHooks)
137
138
  addItem('hook', HOOKS_ITEM_ID, true);
139
+ }
140
+ // O padrão de comentários também chega ao lock anterior a ele, salvo com a recusa no config ou na flag. O hook é a
141
+ // referência: o comando `comentarios` não registra a rule quando o projeto já tem uma própria.
142
+ const registrarComentarios = !semComentarios && config.comentarios.instalar && (adotada || !trackedItems.has('hook:comentarios'));
143
+ if (registrarComentarios) {
138
144
  addItem('rule', 'comentarios', true);
139
145
  addItem('hook', 'comentarios', true);
140
146
  }
141
- const registrarMcpConfig = readConfig(root).config.banco_dados.registrar_mcp;
147
+ const registrarMcpConfig = config.banco_dados.registrar_mcp;
142
148
  const permissoesBanco = !semBanco && registrarMcpConfig;
143
149
  const permissoesFaltando = [
144
150
  ...harnessPermissions({ banco: permissoesBanco }).filter((rule) => !hasAllowRule(root, rule)),
@@ -152,7 +158,8 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
152
158
  stale,
153
159
  adotada,
154
160
  registrarHooks: adotada && !pluginHooks,
155
- registrarComentarios: adotada,
161
+ registrarComentarios,
162
+ recusarComentarios: semComentarios && config.comentarios.instalar,
156
163
  permissaoAusente: permissoesFaltando.length > 0,
157
164
  permissoesFaltando,
158
165
  permissoesLegadas,
@@ -238,6 +245,10 @@ export function applyUpdate(root, report, options, pkg, now = new Date()) {
238
245
  gravarRegistrarMcp(root, false);
239
246
  ajustes.push(`banco_dados.registrar_mcp: false gravado em ${CONFIG_PATH}.`);
240
247
  }
248
+ if (report.recusarComentarios) {
249
+ gravarInstalarComentarios(root, false);
250
+ ajustes.push(`comentarios.instalar: false gravado em ${CONFIG_PATH}.`);
251
+ }
241
252
  const itemOf = new Map(report.changes.map((change) => [change.caminho, change.item]));
242
253
  const writtenHash = new Map(write.map((file) => [file.path, hashContent(file.content)]));
243
254
  const dropped = new Set([...remove, ...report.stale, ...legacy]);
@@ -294,7 +305,7 @@ export function describeUpdate(report) {
294
305
  const adopted = report.adotada
295
306
  ? [
296
307
  `Instalação ${report.lock.pacote.versao} sem lock, reconhecida pelos arquivos em .claude/. ` +
297
- `O --yes grava o ${LOCK_PATH}, a configuração do harness${report.registrarHooks ? ', os hooks' : ''}, o padrão de comentários, ` +
308
+ `O --yes grava o ${LOCK_PATH}, a configuração do harness${report.registrarHooks ? ', os hooks' : ''}${report.registrarComentarios ? ', o padrão de comentários' : ''}, ` +
298
309
  'a rule de projeto e a permissão do `ai-harness evento`.',
299
310
  ]
300
311
  : [];
@@ -303,6 +314,15 @@ export function describeUpdate(report) {
303
314
  `Faltam permissões do harness em .claude/settings.json (${report.permissoesFaltando.join(', ')}); o --yes as acrescenta.`,
304
315
  ]
305
316
  : [];
317
+ const comentarios = [
318
+ ...(report.registrarComentarios && !report.adotada
319
+ ? [
320
+ 'O padrão de comentários não está instalado: o --yes grava a rule e o hook, registra o hook em .claude/settings.json e gera a catraca do legado. ' +
321
+ `Para recusar, rode com --sem-comentarios, que grava comentarios.instalar: false em ${CONFIG_PATH}.`,
322
+ ]
323
+ : []),
324
+ ...(report.recusarComentarios ? [`--sem-comentarios: o --yes grava comentarios.instalar: false em ${CONFIG_PATH} e não instala o padrão de comentários.`] : []),
325
+ ];
306
326
  const legadas = report.permissoesLegadas.length
307
327
  ? [`Permissões do MCP de banco com o nome antigo em .claude/settings.json (${report.permissoesLegadas.join(', ')}); o --yes as remove.`]
308
328
  : [];
@@ -316,11 +336,14 @@ export function describeUpdate(report) {
316
336
  ...(report.recusarMcp ? [`--sem-banco-dados: o --yes grava banco_dados.registrar_mcp: false em ${CONFIG_PATH} e não registra o MCP.`] : []),
317
337
  ];
318
338
  if (!report.changes.length) {
319
- const status = adopted.length || permission.length || legadas.length || mcp.length ? 'Nenhum arquivo a atualizar.' : 'A instalação está em dia: nenhum arquivo a atualizar.';
320
- return [...adopted, ...permission, ...legadas, ...mcp, status].join('\n');
339
+ const status = adopted.length || comentarios.length || permission.length || legadas.length || mcp.length
340
+ ? 'Nenhum arquivo a atualizar.'
341
+ : 'A instalação está em dia: nenhum arquivo a atualizar.';
342
+ return [...adopted, ...comentarios, ...permission, ...legadas, ...mcp, status].join('\n');
321
343
  }
322
344
  return [
323
345
  ...adopted,
346
+ ...comentarios,
324
347
  ...permission,
325
348
  ...legadas,
326
349
  ...mcp,
@@ -328,9 +351,9 @@ export function describeUpdate(report) {
328
351
  ...report.changes.map((change) => ` ${change.tipo.padEnd(16)} ${change.caminho}${change.destino ? ` → ${change.destino}` : ''} (${DESCRIPTIONS[change.tipo]})`),
329
352
  ].join('\n');
330
353
  }
331
- const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar', '--sem-banco-dados'];
354
+ const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar', '--sem-banco-dados', '--sem-comentarios'];
332
355
  export function parseUpdateArgs(argv) {
333
- const args = { path: undefined, check: false, yes: false, force: false, semBanco: false };
356
+ const args = { path: undefined, check: false, yes: false, force: false, semBanco: false, semComentarios: false };
334
357
  for (const token of argv) {
335
358
  if (!token.startsWith('-')) {
336
359
  if (args.path !== undefined)
@@ -343,6 +366,8 @@ export function parseUpdateArgs(argv) {
343
366
  args.force = true;
344
367
  else if (token === '--sem-banco-dados')
345
368
  args.semBanco = true;
369
+ else if (token === '--sem-comentarios')
370
+ args.semComentarios = true;
346
371
  else if (token === '--yes' || token === '-y' || token === '--sim')
347
372
  args.yes = true;
348
373
  else {
@@ -352,16 +377,16 @@ export function parseUpdateArgs(argv) {
352
377
  }
353
378
  return args;
354
379
  }
355
- /** `ai-harness update [caminho] [--check] [--yes] [--forcar] [--sem-banco-dados]`; sem `--yes` só simula. */
380
+ /** `ai-harness update [caminho] [--check] [--yes] [--forcar] [--sem-banco-dados] [--sem-comentarios]`; sem `--yes` só simula. */
356
381
  export function runUpdateCommand(argv) {
357
382
  const args = parseUpdateArgs(argv);
358
383
  const root = resolve(process.cwd(), args.path ?? '.');
359
384
  if (!existsSync(root))
360
385
  throw new Error(`Pasta não encontrada: ${root}`);
361
- const report = checkUpdate(root, undefined, undefined, undefined, { semBanco: args.semBanco });
386
+ const report = checkUpdate(root, undefined, undefined, undefined, { semBanco: args.semBanco, semComentarios: args.semComentarios });
362
387
  console.log(describeUpdate(report));
363
388
  // Sem arquivo a atualizar, a adoção ainda grava o lock, e a permissão e o MCP ainda são acrescentados.
364
- const pendente = report.adotada || report.permissaoAusente || report.permissoesLegadas.length > 0 || report.mudancaMcp !== null || report.recusarMcp || report.renomearSecaoBanco;
389
+ const pendente = report.adotada || report.permissaoAusente || report.permissoesLegadas.length > 0 || report.mudancaMcp !== null || report.recusarMcp || report.recusarComentarios || report.renomearSecaoBanco;
365
390
  if (args.check || (!report.changes.length && !pendente))
366
391
  return 0;
367
392
  if (!args.yes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fontdata/ai-harness",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "Harness de engenharia com IA da Fontdata para Claude Code: roda os slash commands direto na pasta do projeto via npx, sem instalar nada — ou instala-os em .claude/commands.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,3 +42,6 @@ npx @fontdata/ai-harness evento agent_veredito --trabalho <id> --task <T-SS.NN>
42
42
  Com falhas, use `suite_vermelha` e `--resultado falha`. Na verificação manual, registre `suite_verde` com
43
43
  `"comando":"manual"`, `total` igual ao número de itens do roteiro e `falhas` igual a 0; com item reprovado,
44
44
  `suite_vermelha`.
45
+
46
+ O CLI recusa valor fora do contrato: `--resultado` aceita só `ok`, `aviso`, `bloqueio` ou `falha` (o veredito
47
+ `aprovado`/`reprovado` vai em `--dados`), e `--task` leva só o id `T-SS.NN`, sem sufixo.
@@ -47,6 +47,8 @@ sabendo para que a task serve, o sub-agent decide melhor o que a lista de campos
47
47
  2. **qa** recebe a task, os critérios de aceite e o comando da suíte completa. Ele registra a suíte no rastro:
48
48
  - `suite_verde` com `--resultado ok` e `{"comando", "total", "falhas": 0}`;
49
49
  - `suite_vermelha` com `--resultado falha` e o número de falhas.
50
+ - `--resultado` aceita só `ok`, `aviso`, `bloqueio` ou `falha`, e `--task` só o id `T-SS.NN`; o CLI recusa
51
+ outro valor (o veredito `aprovado`/`reprovado` vai em `--dados`).
50
52
  3. **Voltas:** no máximo 2 retornos ao implementador por task. Na terceira reprovação, a task vira bloqueio.
51
53
 
52
54
  ## Fechamento