@fontdata/ai-harness 2.7.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,15 +2,15 @@ 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, readConfig, syncGitignore, writeDefaultConfig } from '../config/config.js';
5
+ import { CONFIG_PATH, 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';
9
- import { bancoMcpServer, ehEntradaGerenciada, MCP_PATH, mergeMcpServers } from '../install/mcp.js';
9
+ import { BANCO_MCP_NOME, BANCO_MCP_NOME_LEGADO, bancoMcpServer, ehEntradaGerenciada, MCP_PATH, mergeMcpServers } from '../install/mcp.js';
10
10
  import { hashContent, LOCK_PATH, LOCK_VERSION, readLock, writeLock } from '../install/lock.js';
11
11
  import { itemSources, packageInfo, PROJECT_RULE_PATH, projectRuleContent } from '../install/plan.js';
12
12
  import { neededSkillsFor } from '../install/selection.js';
13
- import { combineHooks, HARNESS_DENY, HARNESS_PERMISSIONS, hasAllowRule, mergeSettings } from '../install/settings.js';
13
+ import { BANCO_PERMISSIONS_LEGADAS, combineHooks, HARNESS_DENY, harnessPermissions, hasAllowRule, mergeSettings, SETTINGS_PATH, } from '../install/settings.js';
14
14
  import { adoptLegacyInstall } from './legado.js';
15
15
  function isPlainObject(value) {
16
16
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -32,7 +32,9 @@ function mcpChange(root, versao) {
32
32
  const servers = parsed.mcpServers === undefined ? {} : parsed.mcpServers;
33
33
  if (!isPlainObject(servers))
34
34
  return 'invalido';
35
- const atual = servers.banco;
35
+ if (ehEntradaGerenciada(servers[BANCO_MCP_NOME_LEGADO]))
36
+ return 'renomear';
37
+ const atual = servers[BANCO_MCP_NOME];
36
38
  if (atual === undefined)
37
39
  return 'faltando';
38
40
  const pin = bancoMcpServer(versao).args.find((arg) => arg.startsWith('@fontdata/'));
@@ -136,11 +138,13 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
136
138
  addItem('rule', 'comentarios', true);
137
139
  addItem('hook', 'comentarios', true);
138
140
  }
141
+ const registrarMcpConfig = readConfig(root).config.banco_dados.registrar_mcp;
142
+ const permissoesBanco = !semBanco && registrarMcpConfig;
139
143
  const permissoesFaltando = [
140
- ...HARNESS_PERMISSIONS.filter((rule) => !hasAllowRule(root, rule)),
144
+ ...harnessPermissions({ banco: permissoesBanco }).filter((rule) => !hasAllowRule(root, rule)),
141
145
  ...HARNESS_DENY.filter((rule) => !hasAllowRule(root, rule, 'deny')).map((rule) => `deny ${rule}`),
142
146
  ];
143
- const registrarMcpConfig = readConfig(root).config.banco.registrar_mcp;
147
+ const permissoesLegadas = BANCO_PERMISSIONS_LEGADAS.filter((rule) => hasAllowRule(root, rule));
144
148
  const registrarMcp = !semBanco && registrarMcpConfig && !isHarnessPluginEnabled(root, home);
145
149
  return {
146
150
  changes,
@@ -151,9 +155,12 @@ export function checkUpdate(root, catalog = loadCatalog(), legacy, home, { semBa
151
155
  registrarComentarios: adotada,
152
156
  permissaoAusente: permissoesFaltando.length > 0,
153
157
  permissoesFaltando,
158
+ permissoesLegadas,
159
+ permissoesBanco,
154
160
  registrarMcp,
155
161
  mudancaMcp: registrarMcp ? mcpChange(root, packageInfo().versao) : null,
156
162
  recusarMcp: semBanco && registrarMcpConfig,
163
+ renomearSecaoBanco: temSecaoBancoLegada(root),
157
164
  };
158
165
  }
159
166
  /**
@@ -192,7 +199,10 @@ export function applyUpdate(root, report, options, pkg, now = new Date()) {
192
199
  return { de: path, para };
193
200
  });
194
201
  const hooks = combineHooks(report.registrarHooks ? projectHooksConfig() : {}, report.registrarComentarios ? commentsHooksConfig() : {});
195
- mergeSettings(root, { hooks, allow: HARNESS_PERMISSIONS, deny: HARNESS_DENY }, now);
202
+ const ajustes = [];
203
+ const settings = mergeSettings(root, { hooks, allow: harnessPermissions({ banco: report.permissoesBanco }), deny: HARNESS_DENY, removeAllow: BANCO_PERMISSIONS_LEGADAS }, now);
204
+ if (settings.changed)
205
+ ajustes.push(`${SETTINGS_PATH} atualizado${settings.backup ? ` (backup em ${settings.backup})` : ''}.`);
196
206
  if (report.registrarComentarios)
197
207
  ensureCommentsBaseline(root);
198
208
  if (report.adotada) {
@@ -211,14 +221,23 @@ export function applyUpdate(root, report, options, pkg, now = new Date()) {
211
221
  else if (report.registrarMcp) {
212
222
  // Uma falha no .mcp.json nunca deixa o update pela metade, sem lock.
213
223
  try {
214
- mergeMcpServers(root, { banco: bancoMcpServer(pkg.versao) }, now, { atualizarGerenciada: true });
224
+ const mcp = mergeMcpServers(root, { [BANCO_MCP_NOME]: bancoMcpServer(pkg.versao) }, now, {
225
+ atualizarGerenciada: true,
226
+ removerGerenciadas: [BANCO_MCP_NOME_LEGADO],
227
+ });
228
+ if (mcp.changed)
229
+ ajustes.push(`${MCP_PATH} com mcpServers.${BANCO_MCP_NOME} na versão ${pkg.versao}${mcp.backup ? ` (backup em ${mcp.backup})` : ''}.`);
215
230
  }
216
231
  catch (error) {
217
232
  avisos.push(error instanceof Error ? error.message : String(error));
218
233
  }
219
234
  }
220
- if (report.recusarMcp)
235
+ if (report.renomearSecaoBanco && migrarSecaoBanco(root))
236
+ ajustes.push(`seção banco renomeada para banco_dados em ${CONFIG_PATH}.`);
237
+ if (report.recusarMcp) {
221
238
  gravarRegistrarMcp(root, false);
239
+ ajustes.push(`banco_dados.registrar_mcp: false gravado em ${CONFIG_PATH}.`);
240
+ }
222
241
  const itemOf = new Map(report.changes.map((change) => [change.caminho, change.item]));
223
242
  const writtenHash = new Map(write.map((file) => [file.path, hashContent(file.content)]));
224
243
  const dropped = new Set([...remove, ...report.stale, ...legacy]);
@@ -252,13 +271,14 @@ export function applyUpdate(root, report, options, pkg, now = new Date()) {
252
271
  itens.push({ ...added, arquivos: added.arquivos.sort((a, b) => a.caminho.localeCompare(b.caminho)) });
253
272
  }
254
273
  writeLock(root, { lock_versao: LOCK_VERSION, pacote: pkg, gerado_em: now.toISOString(), itens });
255
- return { written: write.map((file) => file.path), removed: remove, kept, renamed, avisos };
274
+ return { written: write.map((file) => file.path), removed: remove, kept, renamed, avisos, ajustes };
256
275
  }
257
276
  const MCP_INVALIDO = `O ${MCP_PATH} está com JSON inválido: o registro do MCP de banco foi pulado e o arquivo não foi tocado. Corrija-o e rode o update de novo.`;
258
277
  const MCP_DESCRIPTIONS = {
259
278
  ausente: 'não existe',
260
- faltando: 'não tem a entrada banco',
261
- versao: 'fixa outra versão do servidor banco',
279
+ faltando: `não tem a entrada ${BANCO_MCP_NOME}`,
280
+ versao: `fixa outra versão do servidor ${BANCO_MCP_NOME}`,
281
+ renomear: `tem a entrada ${BANCO_MCP_NOME_LEGADO} do harness, com o nome antigo; ela é trocada pela ${BANCO_MCP_NOME}`,
262
282
  };
263
283
  const DESCRIPTIONS = {
264
284
  novo: 'novo no pacote, será gravado',
@@ -283,27 +303,32 @@ export function describeUpdate(report) {
283
303
  `Faltam permissões do harness em .claude/settings.json (${report.permissoesFaltando.join(', ')}); o --yes as acrescenta.`,
284
304
  ]
285
305
  : [];
306
+ const legadas = report.permissoesLegadas.length
307
+ ? [`Permissões do MCP de banco com o nome antigo em .claude/settings.json (${report.permissoesLegadas.join(', ')}); o --yes as remove.`]
308
+ : [];
286
309
  const mcp = [
287
310
  ...(report.mudancaMcp === 'invalido'
288
311
  ? [`O ${MCP_PATH} está com JSON inválido; o --yes pula o registro do MCP de banco e não toca no arquivo.`]
289
312
  : report.mudancaMcp
290
- ? [`O ${MCP_PATH} ${MCP_DESCRIPTIONS[report.mudancaMcp]}; o --yes grava mcpServers.banco na versão ${packageInfo().versao}.`]
313
+ ? [`O ${MCP_PATH} ${MCP_DESCRIPTIONS[report.mudancaMcp]}; o --yes grava mcpServers.${BANCO_MCP_NOME} na versão ${packageInfo().versao}.`]
291
314
  : []),
292
- ...(report.recusarMcp ? [`--sem-banco: o --yes grava banco.registrar_mcp: false em ${CONFIG_PATH} e não registra o MCP.`] : []),
315
+ ...(report.renomearSecaoBanco ? [`A seção banco de ${CONFIG_PATH} agora se chama banco_dados; o --yes a renomeia.`] : []),
316
+ ...(report.recusarMcp ? [`--sem-banco-dados: o --yes grava banco_dados.registrar_mcp: false em ${CONFIG_PATH} e não registra o MCP.`] : []),
293
317
  ];
294
318
  if (!report.changes.length) {
295
- const status = adopted.length || permission.length || mcp.length ? 'Nenhum arquivo a atualizar.' : 'A instalação está em dia: nenhum arquivo a atualizar.';
296
- return [...adopted, ...permission, ...mcp, status].join('\n');
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');
297
321
  }
298
322
  return [
299
323
  ...adopted,
300
324
  ...permission,
325
+ ...legadas,
301
326
  ...mcp,
302
327
  `Mudanças em relação ao pacote ${packageInfo().versao} (instalado: ${report.lock.pacote.versao}):`,
303
328
  ...report.changes.map((change) => ` ${change.tipo.padEnd(16)} ${change.caminho}${change.destino ? ` → ${change.destino}` : ''} (${DESCRIPTIONS[change.tipo]})`),
304
329
  ].join('\n');
305
330
  }
306
- const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar', '--sem-banco'];
331
+ const UPDATE_FLAGS = ['--check', '--yes', '-y', '--sim', '--forcar', '--sem-banco-dados'];
307
332
  export function parseUpdateArgs(argv) {
308
333
  const args = { path: undefined, check: false, yes: false, force: false, semBanco: false };
309
334
  for (const token of argv) {
@@ -316,7 +341,7 @@ export function parseUpdateArgs(argv) {
316
341
  args.check = true;
317
342
  else if (token === '--forcar')
318
343
  args.force = true;
319
- else if (token === '--sem-banco')
344
+ else if (token === '--sem-banco-dados')
320
345
  args.semBanco = true;
321
346
  else if (token === '--yes' || token === '-y' || token === '--sim')
322
347
  args.yes = true;
@@ -327,7 +352,7 @@ export function parseUpdateArgs(argv) {
327
352
  }
328
353
  return args;
329
354
  }
330
- /** `ai-harness update [caminho] [--check] [--yes] [--forcar] [--sem-banco]`; sem `--yes` só simula. */
355
+ /** `ai-harness update [caminho] [--check] [--yes] [--forcar] [--sem-banco-dados]`; sem `--yes` só simula. */
331
356
  export function runUpdateCommand(argv) {
332
357
  const args = parseUpdateArgs(argv);
333
358
  const root = resolve(process.cwd(), args.path ?? '.');
@@ -336,7 +361,7 @@ export function runUpdateCommand(argv) {
336
361
  const report = checkUpdate(root, undefined, undefined, undefined, { semBanco: args.semBanco });
337
362
  console.log(describeUpdate(report));
338
363
  // Sem arquivo a atualizar, a adoção ainda grava o lock, e a permissão e o MCP ainda são acrescentados.
339
- const pendente = report.adotada || report.permissaoAusente || report.mudancaMcp !== null || report.recusarMcp;
364
+ const pendente = report.adotada || report.permissaoAusente || report.permissoesLegadas.length > 0 || report.mudancaMcp !== null || report.recusarMcp || report.renomearSecaoBanco;
340
365
  if (args.check || (!report.changes.length && !pendente))
341
366
  return 0;
342
367
  if (!args.yes) {
@@ -347,6 +372,8 @@ export function runUpdateCommand(argv) {
347
372
  const result = applyUpdate(root, report, { force: args.force }, packageInfo());
348
373
  console.log(`\nAtualização aplicada: ${result.written.length} gravado(s), ${result.removed.length} apagado(s), ` +
349
374
  `${result.renamed.length} renomeado(s), ${result.kept.length} preservado(s).`);
375
+ for (const ajuste of result.ajustes)
376
+ console.log(`Gravado: ${ajuste}`);
350
377
  for (const aviso of result.avisos)
351
378
  console.log(`Aviso: ${aviso}`);
352
379
  if (result.renamed.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fontdata/ai-harness",
3
- "version": "2.7.0",
3
+ "version": "3.1.0",
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": {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes