@fontdata/ai-harness 3.2.1 → 3.2.3
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 +8 -3
- package/dist/cli.js +1 -0
- package/dist/comentarios/command.js +3 -0
- package/dist/config/config.js +33 -5
- package/dist/doctor/checks.js +20 -1
- package/dist/install/plan.js +9 -1
- package/dist/update/update.js +59 -14
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fontdata",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.3",
|
|
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.3", "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,12 @@ 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; um hook próprio que já existe, de versão antiga ou
|
|
253
|
+
adaptado, é mantido e só entra no lock. Para recusar, use `--sem-comentarios` no `init` ou
|
|
254
|
+
no `update`: a escolha fica em `comentarios.instalar: false` no `.ai-harness/config.json`, e o `update` a respeita.
|
|
255
|
+
Para reativar, apague a chave e rode `update --yes` (ou `npx @fontdata/ai-harness comentarios --yes`). Sem o padrão
|
|
256
|
+
e sem a recusa, o `doctor` avisa com `comentarios-ausente`.
|
|
252
257
|
|
|
253
258
|
A skill `lgpd-checker`, usada pela auditoria de segurança, é uma cópia vendorizada de
|
|
254
259
|
[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
|
/**
|
package/dist/doctor/checks.js
CHANGED
|
@@ -4,6 +4,7 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { lerEnvSettingsLocal, lerVariavel, SETTINGS_LOCAL_PATH } from '../banco-dados/ambiente.js';
|
|
5
5
|
import { caminhoCadastro, lerCadastro } from '../banco-dados/cadastro.js';
|
|
6
6
|
import { allCommands, loadCatalog } from '../catalog.js';
|
|
7
|
+
import { COMMENTS_HOOK_PATH } from '../comentarios/instalar.js';
|
|
7
8
|
import { CONFIG_PATH, readConfig } from '../config/config.js';
|
|
8
9
|
import { readArtifact, validateAllWorks, WORKS_DIR } from '../contratos/artefatos.js';
|
|
9
10
|
import { validateEventLog } from '../contratos/eventos.js';
|
|
@@ -390,6 +391,24 @@ function checkOldCommentsBlock(root) {
|
|
|
390
391
|
},
|
|
391
392
|
];
|
|
392
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Lock anterior ao padrão de comentários e sem a recusa no config: o update o instala. Hook no disco fora do lock é
|
|
396
|
+
* instalação antiga ou adaptada, que o update só registra no lock.
|
|
397
|
+
*/
|
|
398
|
+
function checkComments(root, lock, config) {
|
|
399
|
+
if (!config.comentarios.instalar || existsSync(join(root, COMMENTS_HOOK_PATH)))
|
|
400
|
+
return [];
|
|
401
|
+
if (lock.itens.some((item) => item.tipo === 'hook' && item.id === 'comentarios'))
|
|
402
|
+
return [];
|
|
403
|
+
return [
|
|
404
|
+
{
|
|
405
|
+
id: 'comentarios-ausente',
|
|
406
|
+
severidade: 'aviso',
|
|
407
|
+
mensagem: 'O padrão de comentários (rule, hook e catraca do legado) não está instalado, e a recusa não está registrada.',
|
|
408
|
+
correcao: `Instale com \`npx @fontdata/ai-harness update --yes\`, ou recuse com \`update --sem-comentarios --yes\`, que grava comentarios.instalar: false no ${CONFIG_PATH}.`,
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
}
|
|
393
412
|
function checkConfig(problems) {
|
|
394
413
|
return problems.map((mensagem) => ({
|
|
395
414
|
id: 'config-invalida',
|
|
@@ -507,7 +526,7 @@ export function runDoctor(root, options = {}) {
|
|
|
507
526
|
return [
|
|
508
527
|
...checkNode(options.nodeVersion ?? process.versions.node),
|
|
509
528
|
...lockFindings,
|
|
510
|
-
...(lock ? [...checkFiles(root, lock), ...checkRequiredSkills(root, lock)] : []),
|
|
529
|
+
...(lock ? [...checkFiles(root, lock), ...checkRequiredSkills(root, lock), ...checkComments(root, lock, config)] : []),
|
|
511
530
|
...checkShadowedCommands(root),
|
|
512
531
|
...checkOldCommentsBlock(root),
|
|
513
532
|
...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/update/update.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
|
-
import { allCommands, loadCatalog } from '../catalog.js';
|
|
4
|
-
import { commentsHooksConfig, ensureCommentsBaseline } from '../comentarios/instalar.js';
|
|
5
|
-
import { CONFIG_PATH, gravarRegistrarMcp, migrarSecaoBanco, readConfig, syncGitignore, temSecaoBancoLegada, writeDefaultConfig, } from '../config/config.js';
|
|
3
|
+
import { allCommands, loadCatalog, templatesDir } from '../catalog.js';
|
|
4
|
+
import { COMMENTS_HOOK_PATH, commentsHooksConfig, ensureCommentsBaseline } from '../comentarios/instalar.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,13 @@ function legacyPath(root, path) {
|
|
|
50
50
|
return candidate;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
const COMMENTS_HOOK_TEMPLATE = join(templatesDir, 'hooks', 'comentarios', 'check.mjs');
|
|
54
|
+
/** O hook de comentários existe no projeto com conteúdo diferente do template do pacote. */
|
|
55
|
+
function hookProprio(root) {
|
|
56
|
+
const local = join(root, COMMENTS_HOOK_PATH);
|
|
57
|
+
return existsSync(local) && hashContent(readFileSync(local)) !== hashContent(readFileSync(COMMENTS_HOOK_TEMPLATE));
|
|
58
|
+
}
|
|
59
|
+
export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBanco = false, semComentarios = false } = {}) {
|
|
54
60
|
const recorded = readLock(root);
|
|
55
61
|
const lock = recorded ?? adoptLegacyInstall(root, legacy);
|
|
56
62
|
if (!lock) {
|
|
@@ -127,6 +133,7 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
|
|
|
127
133
|
addItem('skill', skill.id, false);
|
|
128
134
|
}
|
|
129
135
|
}
|
|
136
|
+
const { config } = readConfig(root);
|
|
130
137
|
// Na adoção, a instalação antiga ganha o que a 2.1 instala e ela não tinha.
|
|
131
138
|
const adotada = !recorded;
|
|
132
139
|
const pluginHooks = adotada && isHarnessPluginEnabled(root, home);
|
|
@@ -135,10 +142,18 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
|
|
|
135
142
|
addItem('agent', agent.id, true);
|
|
136
143
|
if (!pluginHooks)
|
|
137
144
|
addItem('hook', HOOKS_ITEM_ID, true);
|
|
145
|
+
}
|
|
146
|
+
// 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
|
|
147
|
+
// referência: o comando `comentarios` não registra a rule quando o projeto já tem uma própria.
|
|
148
|
+
const querComentarios = !semComentarios && config.comentarios.instalar;
|
|
149
|
+
const hookNoLock = trackedItems.has('hook:comentarios');
|
|
150
|
+
const registrarHookComentarios = querComentarios && !hookNoLock && hookProprio(root);
|
|
151
|
+
const registrarComentarios = querComentarios && !registrarHookComentarios && (adotada || !hookNoLock);
|
|
152
|
+
if (registrarComentarios) {
|
|
138
153
|
addItem('rule', 'comentarios', true);
|
|
139
154
|
addItem('hook', 'comentarios', true);
|
|
140
155
|
}
|
|
141
|
-
const registrarMcpConfig =
|
|
156
|
+
const registrarMcpConfig = config.banco_dados.registrar_mcp;
|
|
142
157
|
const permissoesBanco = !semBanco && registrarMcpConfig;
|
|
143
158
|
const permissoesFaltando = [
|
|
144
159
|
...harnessPermissions({ banco: permissoesBanco }).filter((rule) => !hasAllowRule(root, rule)),
|
|
@@ -152,7 +167,9 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
|
|
|
152
167
|
stale,
|
|
153
168
|
adotada,
|
|
154
169
|
registrarHooks: adotada && !pluginHooks,
|
|
155
|
-
registrarComentarios
|
|
170
|
+
registrarComentarios,
|
|
171
|
+
registrarHookComentarios,
|
|
172
|
+
recusarComentarios: semComentarios && config.comentarios.instalar,
|
|
156
173
|
permissaoAusente: permissoesFaltando.length > 0,
|
|
157
174
|
permissoesFaltando,
|
|
158
175
|
permissoesLegadas,
|
|
@@ -238,6 +255,10 @@ export function applyUpdate(root, report, options, pkg, now = new Date()) {
|
|
|
238
255
|
gravarRegistrarMcp(root, false);
|
|
239
256
|
ajustes.push(`banco_dados.registrar_mcp: false gravado em ${CONFIG_PATH}.`);
|
|
240
257
|
}
|
|
258
|
+
if (report.recusarComentarios) {
|
|
259
|
+
gravarInstalarComentarios(root, false);
|
|
260
|
+
ajustes.push(`comentarios.instalar: false gravado em ${CONFIG_PATH}.`);
|
|
261
|
+
}
|
|
241
262
|
const itemOf = new Map(report.changes.map((change) => [change.caminho, change.item]));
|
|
242
263
|
const writtenHash = new Map(write.map((file) => [file.path, hashContent(file.content)]));
|
|
243
264
|
const dropped = new Set([...remove, ...report.stale, ...legacy]);
|
|
@@ -270,6 +291,10 @@ export function applyUpdate(root, report, options, pkg, now = new Date()) {
|
|
|
270
291
|
for (const added of addedItems.values()) {
|
|
271
292
|
itens.push({ ...added, arquivos: added.arquivos.sort((a, b) => a.caminho.localeCompare(b.caminho)) });
|
|
272
293
|
}
|
|
294
|
+
// Com o hash do template, o arquivo do projeto aparece como modificado-local e nunca é regravado sem --forcar.
|
|
295
|
+
if (report.registrarHookComentarios) {
|
|
296
|
+
itens.push({ tipo: 'hook', id: 'comentarios', arquivos: [{ caminho: COMMENTS_HOOK_PATH, sha256: hashContent(readFileSync(COMMENTS_HOOK_TEMPLATE)) }] });
|
|
297
|
+
}
|
|
273
298
|
writeLock(root, { lock_versao: LOCK_VERSION, pacote: pkg, gerado_em: now.toISOString(), itens });
|
|
274
299
|
return { written: write.map((file) => file.path), removed: remove, kept, renamed, avisos, ajustes };
|
|
275
300
|
}
|
|
@@ -294,7 +319,7 @@ export function describeUpdate(report) {
|
|
|
294
319
|
const adopted = report.adotada
|
|
295
320
|
? [
|
|
296
321
|
`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, ` +
|
|
322
|
+
`O --yes grava o ${LOCK_PATH}, a configuração do harness${report.registrarHooks ? ', os hooks' : ''}${report.registrarComentarios ? ', o padrão de comentários' : ''}, ` +
|
|
298
323
|
'a rule de projeto e a permissão do `ai-harness evento`.',
|
|
299
324
|
]
|
|
300
325
|
: [];
|
|
@@ -303,6 +328,21 @@ export function describeUpdate(report) {
|
|
|
303
328
|
`Faltam permissões do harness em .claude/settings.json (${report.permissoesFaltando.join(', ')}); o --yes as acrescenta.`,
|
|
304
329
|
]
|
|
305
330
|
: [];
|
|
331
|
+
const comentarios = [
|
|
332
|
+
...(report.registrarComentarios && !report.adotada
|
|
333
|
+
? [
|
|
334
|
+
'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. ' +
|
|
335
|
+
`Para recusar, rode com --sem-comentarios, que grava comentarios.instalar: false em ${CONFIG_PATH}.`,
|
|
336
|
+
]
|
|
337
|
+
: []),
|
|
338
|
+
...(report.registrarHookComentarios
|
|
339
|
+
? [
|
|
340
|
+
`O hook de comentários ${COMMENTS_HOOK_PATH} é do projeto (instalado por versão antiga ou adaptado) e não está no lock: ` +
|
|
341
|
+
'o --yes o deixa registrado no lock como alterado localmente, sem tocar no arquivo.',
|
|
342
|
+
]
|
|
343
|
+
: []),
|
|
344
|
+
...(report.recusarComentarios ? [`--sem-comentarios: o --yes grava comentarios.instalar: false em ${CONFIG_PATH} e não instala o padrão de comentários.`] : []),
|
|
345
|
+
];
|
|
306
346
|
const legadas = report.permissoesLegadas.length
|
|
307
347
|
? [`Permissões do MCP de banco com o nome antigo em .claude/settings.json (${report.permissoesLegadas.join(', ')}); o --yes as remove.`]
|
|
308
348
|
: [];
|
|
@@ -316,11 +356,14 @@ export function describeUpdate(report) {
|
|
|
316
356
|
...(report.recusarMcp ? [`--sem-banco-dados: o --yes grava banco_dados.registrar_mcp: false em ${CONFIG_PATH} e não registra o MCP.`] : []),
|
|
317
357
|
];
|
|
318
358
|
if (!report.changes.length) {
|
|
319
|
-
const status = adopted.length || permission.length || legadas.length || mcp.length
|
|
320
|
-
|
|
359
|
+
const status = adopted.length || comentarios.length || permission.length || legadas.length || mcp.length
|
|
360
|
+
? 'Nenhum arquivo a atualizar.'
|
|
361
|
+
: 'A instalação está em dia: nenhum arquivo a atualizar.';
|
|
362
|
+
return [...adopted, ...comentarios, ...permission, ...legadas, ...mcp, status].join('\n');
|
|
321
363
|
}
|
|
322
364
|
return [
|
|
323
365
|
...adopted,
|
|
366
|
+
...comentarios,
|
|
324
367
|
...permission,
|
|
325
368
|
...legadas,
|
|
326
369
|
...mcp,
|
|
@@ -328,9 +371,9 @@ export function describeUpdate(report) {
|
|
|
328
371
|
...report.changes.map((change) => ` ${change.tipo.padEnd(16)} ${change.caminho}${change.destino ? ` → ${change.destino}` : ''} (${DESCRIPTIONS[change.tipo]})`),
|
|
329
372
|
].join('\n');
|
|
330
373
|
}
|
|
331
|
-
const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar', '--sem-banco-dados'];
|
|
374
|
+
const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar', '--sem-banco-dados', '--sem-comentarios'];
|
|
332
375
|
export function parseUpdateArgs(argv) {
|
|
333
|
-
const args = { path: undefined, check: false, yes: false, force: false, semBanco: false };
|
|
376
|
+
const args = { path: undefined, check: false, yes: false, force: false, semBanco: false, semComentarios: false };
|
|
334
377
|
for (const token of argv) {
|
|
335
378
|
if (!token.startsWith('-')) {
|
|
336
379
|
if (args.path !== undefined)
|
|
@@ -343,6 +386,8 @@ export function parseUpdateArgs(argv) {
|
|
|
343
386
|
args.force = true;
|
|
344
387
|
else if (token === '--sem-banco-dados')
|
|
345
388
|
args.semBanco = true;
|
|
389
|
+
else if (token === '--sem-comentarios')
|
|
390
|
+
args.semComentarios = true;
|
|
346
391
|
else if (token === '--yes' || token === '-y' || token === '--sim')
|
|
347
392
|
args.yes = true;
|
|
348
393
|
else {
|
|
@@ -352,16 +397,16 @@ export function parseUpdateArgs(argv) {
|
|
|
352
397
|
}
|
|
353
398
|
return args;
|
|
354
399
|
}
|
|
355
|
-
/** `ai-harness update [caminho] [--check] [--yes] [--forcar] [--sem-banco-dados]`; sem `--yes` só simula. */
|
|
400
|
+
/** `ai-harness update [caminho] [--check] [--yes] [--forcar] [--sem-banco-dados] [--sem-comentarios]`; sem `--yes` só simula. */
|
|
356
401
|
export function runUpdateCommand(argv) {
|
|
357
402
|
const args = parseUpdateArgs(argv);
|
|
358
403
|
const root = resolve(process.cwd(), args.path ?? '.');
|
|
359
404
|
if (!existsSync(root))
|
|
360
405
|
throw new Error(`Pasta não encontrada: ${root}`);
|
|
361
|
-
const report = checkUpdate(root, undefined, undefined, undefined, { semBanco: args.semBanco });
|
|
406
|
+
const report = checkUpdate(root, undefined, undefined, undefined, { semBanco: args.semBanco, semComentarios: args.semComentarios });
|
|
362
407
|
console.log(describeUpdate(report));
|
|
363
408
|
// 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;
|
|
409
|
+
const pendente = report.adotada || report.permissaoAusente || report.permissoesLegadas.length > 0 || report.mudancaMcp !== null || report.recusarMcp || report.recusarComentarios || report.registrarHookComentarios || report.renomearSecaoBanco;
|
|
365
410
|
if (args.check || (!report.changes.length && !pendente))
|
|
366
411
|
return 0;
|
|
367
412
|
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.3",
|
|
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": {
|