@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.
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +7 -3
- package/dist/cli.js +1 -0
- package/dist/comentarios/command.js +3 -0
- package/dist/config/config.js +33 -5
- package/dist/contratos/eventos.js +4 -0
- package/dist/doctor/checks.js +14 -1
- package/dist/install/plan.js +9 -1
- package/dist/rastro/command.js +8 -1
- package/dist/update/update.js +37 -12
- package/package.json +1 -1
- package/templates/agents/qa.md +3 -0
- package/templates/skills/executar-plan/references/delegacao.md +2 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fontdata",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"mcpServers": {
|
|
5
|
-
"banco_dados": { "command": "npx", "args": ["-y", "@fontdata/ai-harness@3.2.
|
|
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.`);
|
package/dist/config/config.js
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/dist/doctor/checks.js
CHANGED
|
@@ -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),
|
package/dist/install/plan.js
CHANGED
|
@@ -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
|
}
|
package/dist/rastro/command.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/dist/update/update.js
CHANGED
|
@@ -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 =
|
|
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
|
|
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
|
|
320
|
-
|
|
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.
|
|
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": {
|
package/templates/agents/qa.md
CHANGED
|
@@ -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
|