@fontdata/ai-harness 3.3.1 → 4.0.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.
@@ -0,0 +1,91 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ const TIPOS_PATCH = new Set(['fix', 'chore', 'refactor', 'docs', 'style', 'perf', 'test', 'build', 'ci']);
3
+ const SEP_CAMPO = '\x1f';
4
+ const SEP_COMMIT = '\x1e';
5
+ function rodarGit(raiz, args) {
6
+ const r = spawnSync('git', args, { cwd: raiz, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
7
+ if (r.error)
8
+ throw new Error(`Não foi possível rodar o git: ${r.error.message}`);
9
+ if (r.status !== 0)
10
+ throw new Error(`git ${args.join(' ')} falhou: ${r.stderr.trim()}`);
11
+ return r.stdout;
12
+ }
13
+ function escaparRegex(texto) {
14
+ return texto.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15
+ }
16
+ function regexDoModelo(modelo, app) {
17
+ if (modelo.split('{versao}').length !== 2) {
18
+ throw new Error(`O modelo de tag "${modelo}" precisa ter exatamente um {versao}.`);
19
+ }
20
+ if (modelo.includes('{app}') && !app) {
21
+ throw new Error(`O modelo de tag "${modelo}" usa {app}, mas nenhum app foi informado.`);
22
+ }
23
+ const literal = modelo.split('{versao}').map((parte) => escaparRegex(parte.replaceAll('{app}', app ?? '')));
24
+ return new RegExp(`^${literal[0]}(\\d+(?:\\.\\d+)*)${literal[1]}$`);
25
+ }
26
+ function compararVersoes(a, b) {
27
+ const sa = a.split('.').map(Number);
28
+ const sb = b.split('.').map(Number);
29
+ for (let i = 0; i < Math.max(sa.length, sb.length); i++) {
30
+ const diferenca = (sa[i] ?? 0) - (sb[i] ?? 0);
31
+ if (diferenca !== 0)
32
+ return diferenca;
33
+ }
34
+ return 0;
35
+ }
36
+ /**
37
+ * Acha a tag de maior versão alcançável do HEAD que casa com o modelo (ex.: `V{versao}`, `{app}-v{versao}`).
38
+ * Retags com sufixo `-N` não casam, porque `{versao}` só aceita segmentos numéricos separados por ponto.
39
+ * Sem tag, a base é o primeiro commit e `primeiroRelease` fica true.
40
+ */
41
+ export function acharTagBase(raiz, modelo, app) {
42
+ const regex = regexDoModelo(modelo, app);
43
+ let melhor = null;
44
+ for (const tag of rodarGit(raiz, ['tag', '--merged', 'HEAD']).split('\n')) {
45
+ const versao = regex.exec(tag.trim())?.[1];
46
+ if (!versao)
47
+ continue;
48
+ if (!melhor || compararVersoes(versao, melhor.versao) > 0)
49
+ melhor = { tag: tag.trim(), versao };
50
+ }
51
+ if (melhor)
52
+ return { ...melhor, base: melhor.tag, primeiroRelease: false };
53
+ const primeiro = rodarGit(raiz, ['rev-list', '--max-parents=0', 'HEAD']).trim().split('\n').at(-1) ?? '';
54
+ return { tag: null, versao: null, base: primeiro, primeiroRelease: true };
55
+ }
56
+ /**
57
+ * Commits de `base..HEAD`, do mais novo ao mais antigo. Com `primeiroRelease`, lista todo o histórico do HEAD,
58
+ * incluindo o primeiro commit. Com `caminhos`, só os commits que tocam esses caminhos.
59
+ */
60
+ export function listarCommits(raiz, base, caminhos, primeiroRelease = false) {
61
+ const intervalo = primeiroRelease ? 'HEAD' : `${base}..HEAD`;
62
+ const args = ['log', `--format=%H${SEP_CAMPO}%s${SEP_CAMPO}%b${SEP_COMMIT}`, intervalo];
63
+ if (caminhos?.length)
64
+ args.push('--', ...caminhos);
65
+ return rodarGit(raiz, args)
66
+ .split(SEP_COMMIT)
67
+ .map((bloco) => bloco.replace(/^\n+/, ''))
68
+ .filter((bloco) => bloco.trim())
69
+ .map((bloco) => {
70
+ const [hash = '', titulo = '', corpo = ''] = bloco.split(SEP_CAMPO);
71
+ return { hash: hash.trim(), titulo: titulo.trim(), corpo: corpo.trim() };
72
+ });
73
+ }
74
+ /** `feat` → minor; outro tipo semântico conhecido → patch. Mudança incompatível só gera aviso, nunca major. */
75
+ export function classificarBump(commits) {
76
+ let tipo = 'nenhum';
77
+ const avisos = [];
78
+ for (const commit of commits) {
79
+ const casamento = /^(\w+)(?:\([^)]*\))?(!)?:/.exec(commit.titulo);
80
+ const tipoCommit = casamento?.[1]?.toLowerCase();
81
+ if (tipoCommit === 'feat')
82
+ tipo = 'minor';
83
+ else if (tipoCommit && TIPOS_PATCH.has(tipoCommit) && tipo === 'nenhum')
84
+ tipo = 'patch';
85
+ if (casamento?.[2] || /^BREAKING[ -]CHANGE:?/m.test(commit.corpo)) {
86
+ avisos.push(`O commit ${commit.hash.slice(0, 7)} "${commit.titulo}" declara mudança incompatível; ` +
87
+ 'o bump calculado não passa de minor. Se for o caso, peça o major explicitamente.');
88
+ }
89
+ }
90
+ return { tipo, avisos };
91
+ }
@@ -0,0 +1,16 @@
1
+ import { readFileSync } from 'node:fs';
2
+ /** Lê o arquivo como bytes, sem decodificar; o EOL é o da primeira quebra (LF se não houver). */
3
+ export function lerTexto(caminho) {
4
+ const conteudo = readFileSync(caminho);
5
+ const bom = conteudo.length >= 3 && conteudo[0] === 0xef && conteudo[1] === 0xbb && conteudo[2] === 0xbf;
6
+ const lf = conteudo.indexOf(0x0a);
7
+ const eol = lf > 0 && conteudo[lf - 1] === 0x0d ? '\r\n' : '\n';
8
+ return { conteudo, bom, eol };
9
+ }
10
+ /** Troca os bytes [inicio, fim) por `novo` em UTF-8; o resto do buffer sai idêntico. */
11
+ export function substituirTrecho(buffer, inicio, fim, novo) {
12
+ if (!Number.isInteger(inicio) || !Number.isInteger(fim) || inicio < 0 || fim < inicio || fim > buffer.length) {
13
+ throw new Error(`intervalo inválido [${inicio}, ${fim}) para buffer de ${buffer.length} bytes`);
14
+ }
15
+ return Buffer.concat([buffer.subarray(0, inicio), Buffer.from(novo, 'utf8'), buffer.subarray(fim)]);
16
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fontdata/ai-harness",
3
- "version": "3.3.1",
3
+ "version": "4.0.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": {
@@ -79,7 +79,8 @@
79
79
  "title": "Utilitário",
80
80
  "commands": [
81
81
  { "id": "git-commit-push", "file": "commands/git-commit-push.md", "title": "/git-commit-push", "description": "Gera mensagem e executa git add/commit/push em PT-BR.", "recommended": true },
82
- { "id": "versao", "file": "commands/versao.md", "title": "/versao", "description": "Detecta o versionamento do projeto (qualquer stack) e aplica o bump de versão + changelog (autossuficiente), com gate de aprovação e sem push.", "recommended": true },
82
+ { "id": "versao", "file": "commands/versao.md", "title": "/versao", "description": "Detecta as fontes de versão do projeto (qualquer stack) pela CLI `ai-harness versao` e pelo `.ai-harness/versao.json` e aplica o bump de versão + changelog, com gate de aprovação e sem push.", "recommended": true },
83
+ { "id": "publicar-release", "file": "commands/publicar-release.md", "title": "/publicar-release", "description": "Depois do /versao, cria a tag pelo modelo do versao.json (exceto com auto-tag), faz o push após confirmação e cria o GitHub Release com notas geradas.", "recommended": false },
83
84
  { "id": "revisar-pr", "file": "commands/revisar-pr.md", "title": "/revisar-pr", "description": "Revisa um PR ou o diff atual em BLOQUEANTE/IMPORTANTE/SUGESTÃO e, se pedido, organiza as correções num trabalho com teste de regressão.", "recommended": false, "replaces": ["corrigir-review"] }
84
85
  ]
85
86
  }
@@ -0,0 +1,93 @@
1
+ ---
2
+ description: Publica no GitHub a versão que o /versao acabou de gerar — tag, push e release com notas geradas. Opcional; roda depois do /versao.
3
+ argument-hint: "[--app <nome>]"
4
+ ---
5
+
6
+ # /publicar-release — Tag, push e GitHub Release
7
+
8
+ Você publica a versão que o `/versao` acabou de commitar: cria a tag (quando o projeto não tem auto-tag), envia o
9
+ commit e a tag ao remoto e cria o GitHub Release. **O bump não é feito aqui**: versão, arquivos e changelog são do
10
+ `/versao`. Publicações em npm, lojas ou servidores seguem pelos workflows do próprio projeto.
11
+
12
+ > **🚦 Há um GATE de confirmação obrigatório antes do push (Passo 4).** Nada vai para o remoto sem o "sim" do
13
+ > usuário.
14
+
15
+ ## Uso
16
+
17
+ - `/publicar-release` — publica a versão do HEAD.
18
+ - `/publicar-release --app <nome>` — em monorepo, publica a versão do app indicado.
19
+
20
+ Argumento recebido: $ARGUMENTS
21
+
22
+ ## Passo 0 — Pré-checagens (somente leitura; abortar se alguma falhar)
23
+
24
+ 1. **Árvore limpa:** `git status --short` sem saída. Se houver mudança, pare e sugira `/git-commit-push`.
25
+ 2. **Branch e remoto:** `git branch --show-current` (pare se o HEAD estiver destacado) e `git fetch --tags`.
26
+ Compare com o upstream (`git rev-list --left-right --count @{u}...HEAD`): se estiver **atrás** ou divergente,
27
+ pare e avise. Estar à frente só pelo commit de release é o esperado.
28
+ 3. **GitHub CLI:** `gh auth status`. Sem autenticação, pare e peça para o usuário rodar `gh auth login`.
29
+
30
+ ## Passo 1 — Ler a versão e o modelo da tag (somente leitura)
31
+
32
+ Rode `npx @fontdata/ai-harness versao detectar --json` (com `--app <nome>` se ele veio em `$ARGUMENTS`). Do JSON,
33
+ use `versaoAtual`, `tag` (o modelo, ex.: `v{versao}` ou `{app}-v{versao}`), `fontes`, `divergencias` e `problemas`.
34
+
35
+ - `ok: false`, `problemas` não vazio ou `divergencias` não vazio: pare, mostre o que veio e sugira o `/versao`.
36
+ - `apps` não vazio e sem `--app`: pare e pergunte qual app publicar.
37
+ - `tag` nulo: pergunte o padrão de tag do projeto (veja `git tag --sort=-creatordate | head`) e sugira gravá-lo no
38
+ campo `tag` do `.ai-harness/versao.json`.
39
+
40
+ Nome da tag: troque `{versao}` por `versaoAtual` e `{app}` pelo nome do app. Se a CLI não responder, leia a versão
41
+ nas fontes do `.ai-harness/versao.json` (ou do projeto) e siga o padrão das tags existentes, avisando o usuário.
42
+
43
+ ## Passo 2 — Conferir que o HEAD é o commit de release do `/versao`
44
+
45
+ 1. A tag ainda não existe: `git rev-parse -q --verify "refs/tags/<tag>"` e `git ls-remote --tags origin "<tag>"`
46
+ sem saída. Se existir, pare: a versão já foi publicada ou o `/versao` não rodou.
47
+ 2. O commit do HEAD mudou a versão: `git show --stat HEAD` toca as fontes listadas em `fontes`, e
48
+ `git log -1 --format=%s` segue o modelo `commit` do `.ai-harness/versao.json` (quando houver) ou cita a versão.
49
+ 3. O valor das fontes no HEAD é `versaoAtual` (o `detectar` lê a árvore, que está limpa pelo Passo 0).
50
+
51
+ Se algum item falhar, pare e explique; o caminho é rodar o `/versao` antes.
52
+
53
+ ## Passo 3 — Tag (só se o projeto não tiver auto-tag)
54
+
55
+ - Procure `.github/workflows/*auto-tag*`. **Se existir**, não crie a tag: a Action cria quando o commit de release
56
+ chegar na branch de produção. Avise isso e siga sem tag local.
57
+ - **Senão**, crie a tag anotada local: `git tag -a "<tag>" -m "<tag>"`.
58
+
59
+ ## Passo 4 — 🚦 GATE: confirmar o envio
60
+
61
+ Mostre e **aguarde confirmação explícita** do usuário:
62
+
63
+ - branch e remoto de destino;
64
+ - commits que vão subir: `git log --oneline @{u}..HEAD`;
65
+ - tag que vai junto (ou "nenhuma, o auto-tag cria");
66
+ - o release que será criado em seguida.
67
+
68
+ Sem "sim", pare. Se a tag foi criada no Passo 3 e o usuário desistir, ofereça removê-la com `git tag -d "<tag>"`.
69
+
70
+ ## Passo 5 — Push
71
+
72
+ `git push --follow-tags` (envia o commit de release e a tag anotada). Se o push for recusado, pare e explique.
73
+
74
+ ## Passo 6 — GitHub Release
75
+
76
+ - Com tag local: `gh release create "<tag>" --title "<tag>" --generate-notes`.
77
+ - Com auto-tag: espere a tag aparecer no remoto (`git ls-remote --tags origin "<tag>"`, ou acompanhe com
78
+ `gh run list --workflow <arquivo-do-auto-tag>`) antes de criar o release; se o workflow já cria o release, não
79
+ crie outro.
80
+
81
+ ## Passo 7 — Resumo
82
+
83
+ - Link do release (`gh release view "<tag>" --json url -q .url`).
84
+ - Versão publicada, tag e status do push.
85
+ - Lembre que publicações (npm, lojas, deploy) seguem pelos workflows do projeto disparados pela tag ou pelo release;
86
+ aponte o `gh run list` para acompanhar.
87
+
88
+ ## Guardrails
89
+
90
+ - Nunca use `--force` em push nem em tag; nunca mova nem apague tag publicada no remoto.
91
+ - Não faça bump: sem `npm`, `dotnet`, `flutter` ou edição de arquivo de versão. Versão errada volta para o `/versao`.
92
+ - Não pule as pré-checagens nem o GATE do Passo 4.
93
+ - Se qualquer comando falhar, pare e explique o erro e o próximo passo; não contorne sozinho.
@@ -1,87 +1,81 @@
1
1
  ---
2
- description: Detecta como o versionamento funciona no projeto atual (qualquer stack) e aplica o bump de versão + changelog, com gate de aprovação. Para antes do push.
3
- argument-hint: "[X.Y.Z | major | minor | patch | build]"
2
+ description: Aplica o bump de versão em qualquer stack (Node, Delphi, .NET, Flutter, Python, monorepo) pela CLI versao do ai-harness, gera o changelog amigável e commita, com gate de aprovação. Para antes do push.
3
+ argument-hint: "[X.Y.Z | major | minor | patch | build] [--app <nome>]"
4
4
  ---
5
5
 
6
- # /versao — Bump de versão (agnóstico de stack)
6
+ # /versao — Bump de versão sobre a CLI
7
7
 
8
- Você é o **orquestrador de release** deste projeto. Sua tarefa é **descobrir como o versionamento já
9
- funciona aqui** (React+Vite, React+Next, Node, Delphi, .NET, Flutter, etc.) e **replicar exatamente esse
10
- padrão** ao aplicar o novo bump. Não imponha um formato seu: o projeto manda.
8
+ Você é o **orquestrador de release** deste projeto. Quem acha e grava a versão é a CLI determinística
9
+ `npx @fontdata/ai-harness versao detectar|calcular|aplicar`, que lê o `.ai-harness/versao.json` quando ele existe e
10
+ detecta as fontes por stack quando não existe. Ela não faz commit, tag nem push e não escreve changelog. A você
11
+ cabe o que exige julgamento: o changelog amigável, o gate, a validação e o commit, sempre no padrão que o projeto
12
+ já usa.
11
13
 
12
- > **🚦 Há um GATE de aprovação obrigatório** antes de qualquer escrita. **PARE antes de `git push`** —
13
- > push é sempre ação manual do usuário.
14
+ > **🚦 Há um GATE de aprovação obrigatório** antes de qualquer escrita. **PARE antes de `git push`**: push é sempre
15
+ > ação manual do usuário.
14
16
 
15
17
  ## Uso
16
18
 
17
- - `/versao` — **infere** a próxima versão pelos commits (regra SemVer abaixo) ou pelo padrão do projeto.
18
- - `/versao X.Y.Z` — usa a versão explícita como **override** (único caminho que permite subir `major`).
19
- - `/versao major|minor|patch|build` — incrementa o segmento indicado, respeitando o **número de segmentos**
20
- que o projeto usa (3 segmentos `X.Y.Z` ou 4 segmentos `X.Y.Z.W`, comum em Delphi/.NET).
19
+ - `/versao`: usa o `bump.padrao` do `versao.json` ou, sem ele, **infere** pelos commits (nunca sobe major).
20
+ - `/versao X.Y.Z`: versão explícita como **override** (único caminho, junto com `major`, que sobe o major).
21
+ - `/versao major|minor|patch|build`: incrementa o segmento indicado, respeitando os segmentos do projeto
22
+ (`X.Y.Z` ou `X.Y.Z.W`, comum em Delphi/.NET).
23
+ - `--app <nome>`: em monorepo, versiona só aquele app (versão, tag e changelogs próprios).
24
+
25
+ Todos os comandos da CLI aceitam `--json` e `--app <nome>`. Com `"ok": false` a resposta traz `erro` e o código de
26
+ saída é 1. Se o próprio `npx @fontdata/ai-harness versao` falhar (pacote antigo sem o subcomando, sem rede), siga a
27
+ seção **Sem a CLI** no fim deste arquivo. Erro de conteúdo (ex.: `versao.json` inválido, app inexistente) não é
28
+ motivo para o plano B: mostre o `erro` ao usuário e corrija a causa.
21
29
 
22
30
  ## Passo 0 — Pré-checagem (abortar se falhar)
23
31
 
24
- 1. **Working tree limpo:** rode `git status --short`. Se houver saída → **abortar** e instruir a
25
- commitar/limpar antes (sugerir `/git-commit-push`). Não bumpar com árvore suja.
26
- 2. **Base (última versão registrada):** `git describe --tags --abbrev=0 --match "v*"`. Se não houver tag,
27
- use o primeiro commit (`git rev-list --max-parents=0 HEAD`) como base e avise que é o primeiro release.
28
-
29
- ## Passo 1 — Detectar o mecanismo de versionamento (somente leitura)
30
-
31
- Investigue o projeto e **identifique TODAS as fontes de verdade da versão**. Não assuma; confirme lendo.
32
- Possíveis fontes, por stack:
33
-
34
- - **Node / React+Vite / React+Next / qualquer JS-TS:** campo `version` em `package.json` (e
35
- `package-lock.json`/`pnpm-lock.yaml`/`yarn.lock` se contiverem a versão do pacote raiz).
36
- - **Delphi:** `*.dproj` (chaves `VerInfo_Keys`, `FileVersion`/`ProductVersion` e os campos
37
- `MajorVersion/MinorVersion/Release/Build`) **e** constantes de versão em `.pas` (ex.: `About*`,
38
- `BibVariavel`). Liste cada `arquivo:linha`.
39
- - **.NET / C#:** `*.csproj` (`<Version>`, `<AssemblyVersion>`, `<FileVersion>`), `AssemblyInfo.cs`,
40
- `Directory.Build.props`.
41
- - **Flutter / Dart:** campo `version:` do `pubspec.yaml`.
42
- - **Python:** `pyproject.toml` (`[project] version` ou `[tool.poetry] version`), `setup.py`,
43
- `__version__` em `__init__.py`.
44
- - **Outras:** procure por arquivos `VERSION`, constantes `VERSION`/`APP_VERSION`, badges no README.
45
-
46
- Use `grep`/busca para encontrar **toda ocorrência** do número de versão atual, garantindo que nenhuma
47
- fique para trás. Determine também:
48
-
49
- - O **formato/número de segmentos** vigente (`X.Y.Z` ou `X.Y.Z.W`, com ou sem zero à esquerda).
50
- - Onde mora o **changelog**, replicando o que existir:
51
- - `CHANGELOG.md` (formato Keep a Changelog ou similar);
52
- - arquivos de "Novidades" consumidos pela UI (ex.: `src/data/novidades.ts`);
53
- - arquivos texto de histórico (ex.: `Versoes.txt` na raiz, com padrão de espaçamento/formatação próprio).
54
- - Se há **automação de tag** (ex.: `.github/workflows/auto-tag.yml` que cria a tag na `main`). Se houver,
55
- não crie a tag, só commite: a Action cria a tag quando o commit chega na branch alvo, e uma tag criada à mão
56
- duplica a dela.
57
- - O **gerenciador de pacotes / runner de testes** do projeto (npm/pnpm/yarn, `dotnet`, etc.), lendo
58
- `package.json` scripts ou os manifestos.
59
-
60
- > Se não encontrar nenhuma fonte de versão, **pergunte ao usuário** onde a versão deve ser registrada
61
- > antes de prosseguir.
62
-
63
- ## Passo 2 — Levantar commits e calcular a nova versão
64
-
65
- - `git log <base>..HEAD --pretty=format:%H%x09%s`. Liste ao usuário os commits considerados (título +
66
- tipo semântico) e a base usada.
67
- - **Se veio versão explícita `X.Y.Z`:** assuma exatamente ela (override total).
68
- - **Se veio um tipo (`major|minor|patch|build`):** incremente esse segmento e zere os à direita,
69
- respeitando o número de segmentos do projeto.
70
- - **Senão, infira por SemVer** (precedência minor > patch, com reset em cascata):
71
- 1. Algum `feat:` → **minor**: `Y = Y + 1`, demais à direita = `0`.
72
- 2. Senão, algum `fix:`/`chore:`/`refactor:`/`docs:`/`style:`/`perf:`/`test:` → **patch**.
73
- 3. Senão → sem bump elegível: avise e **peça confirmação** (abortar ou versão explícita).
74
- - **`major` é manual** — só sobe quando o usuário passa `X.Y.Z` explícito ou `major`, porque sinaliza quebra de
75
- compatibilidade e isso é decisão de quem mantém o projeto. A inferência não sobe major.
32
+ 1. **Working tree limpo:** rode `git status --short`. Se houver saída, **aborte** e instrua a commitar ou limpar
33
+ antes (sugira `/git-commit-push`). Bump com árvore suja mistura no commit de release o que não é release.
34
+ 2. **Argumento:** separe a versão ou o tipo de bump e o `--app <nome>`, para repassar à CLI nos passos seguintes.
35
+
36
+ ## Passo 1 — Detectar as fontes da versão (somente leitura)
37
+
38
+ Rode `npx @fontdata/ai-harness versao detectar --json` (com `--app <nome>` se for o caso) e leia:
39
+
40
+ - `versaoAtual`, `tag` (modelo da tag, ex.: `v{versao}`, `V{versao}`, `{app}-v{versao}`), `segmentos`, `formato` e
41
+ `base` (`tag`, `versao`, `primeiroRelease`: sem tag anterior, avise que é o primeiro release);
42
+ - `fontes`: cada `caminho` e `extrator`, com as `ocorrencias` (`linha`, `valor`). Fonte com `somenteLeitura`
43
+ (ex.: `Versoes.txt`) é lida, não gravada pela CLI; fonte com `naoTocar` nunca é gravada;
44
+ - `apps`: se não estiver vazio e o usuário não passou `--app`, liste os apps com as versões e pergunte qual
45
+ versionar antes de seguir;
46
+ - `divergencias` e `problemas`: **destaque** cada um. Fontes com versões diferentes precisam de decisão do usuário
47
+ (qual é a certa) antes do gate; não escolha por conta própria.
48
+
49
+ **Sem `versao.json` (`origem` é `deteccao`):** mostre o `versaoJson` proposto e explique em uma frase cada campo
50
+ relevante (`arquivos`, `tag`, `bump.padrao`, `changelogs`, `naoTocar`). Ofereça gravá-lo em
51
+ `.ai-harness/versao.json`, que passa a ser a fonte de verdade do projeto. Só grave com aprovação; se o usuário
52
+ ajustar algo, grave a versão ajustada e rode o `detectar` de novo. Se ele recusar, siga com a detecção.
53
+
54
+ ## Passo 2 — Calcular a nova versão, levantar os commits e simular a gravação
55
+
56
+ Rode `npx @fontdata/ai-harness versao calcular [argumento] --json` repassando o argumento do usuário (`X.Y.Z`,
57
+ `major`, `minor`, `patch` ou `build`) e o `--app`. Leia `versaoAtual`, `proxima`, `tagProxima`, `bump` (`tipo` e
58
+ `origem`: explícito, config ou inferido) e `commits` (`hash`, `titulo`, `corpo`), que já vêm filtrados pelos
59
+ `caminhos` do app em monorepo.
60
+
61
+ - Mostre a base (`base.tag`), a regra do bump e a lista de commits considerados (título e tipo semântico).
62
+ - Repasse os `avisos` (ex.: `!:`/`BREAKING CHANGE` virou minor, próxima versão não é maior que a atual).
63
+ - **`major` é manual:** a inferência nunca sobe major, porque isso sinaliza quebra de compatibilidade e é decisão
64
+ de quem mantém o projeto. Se houver commit de quebra, diga isso e ofereça `/versao major` ou `X.Y.Z`.
65
+ - Sem commit elegível ou sem `proxima`: avise e **peça confirmação** (abortar ou versão explícita).
66
+
67
+ Com a nova versão definida, rode `npx @fontdata/ai-harness versao aplicar <nova> --dry-run --json` (com `--app` se
68
+ for o caso), em que `<nova>` é a `proxima`. Ele não grava nada e devolve, por arquivo, o `diff` que o gate vai
69
+ mostrar. Se vier `problemas` ou alguma fonte com `status` de erro, pare e resolva antes do gate: o `aplicar` real
70
+ não grava nada quando uma fonte falha.
76
71
 
77
72
  ## Passo 3 — Gerar o changelog (no padrão do projeto)
78
73
 
79
- Este comando é **autossuficiente**: gere aqui mesmo o changelog **amigável ao usuário final** (pt-BR,
80
- sem hash/arquivo/jargão) a partir dos commits do intervalo **`<base>..HEAD`** (a base do Passo 0), sem
81
- depender de nenhuma skill ou comando externo. Quem grava os artefatos é o Passo 5.
74
+ Este comando é **autossuficiente**: gere aqui o changelog **amigável ao usuário final** (pt-BR, sem
75
+ hash/arquivo/jargão) a partir dos `commits` do Passo 2, sem depender de skill ou comando externo. A CLI não escreve
76
+ changelog; quem grava os blocos é o Passo 5.
82
77
 
83
- **3.1 — Leia os commits.** Use os commits já levantados no Passo 2 (`git log <base>..HEAD`); leia o corpo
84
- quando precisar de contexto para entender o que a mudança entrega.
78
+ **3.1 — Leia os commits.** Use o `titulo` e, quando precisar entender o que a mudança entrega, o `corpo`.
85
79
 
86
80
  **3.2 — Categorize (modo compacto, 3 categorias).** Agrupe mapeando os prefixos semânticos:
87
81
 
@@ -89,73 +83,116 @@ quando precisar de contexto para entender o que a mudança entrega.
89
83
  - **🐛 Correções** — `fix:`
90
84
  - **⚡ Melhorias** — `perf:`/`refactor:`/`chore:`/`style:`/`docs:`/`test:`/`build:`/`ci:`
91
85
 
92
- Mantenha **⚠️ Mudanças importantes** (`BREAKING CHANGE`/`!:` — destaque o que o usuário precisa fazer) e
93
- **🔒 Segurança** como blocos próprios **só se houver itens**. Commits sem prefixo: classifique pelo
94
- conteúdo da mensagem. Inclua apenas as seções que tiverem itens.
86
+ Mantenha **⚠️ Mudanças importantes** (`BREAKING CHANGE`/`!:`, destacando o que o usuário precisa fazer) e
87
+ **🔒 Segurança** como blocos próprios **só se houver itens**. Commits sem prefixo: classifique pelo conteúdo da
88
+ mensagem. Inclua apenas as seções que tiverem itens.
95
89
 
96
90
  **3.3 — Traduza técnico → usuário final.** Para cada item que vai aparecer:
97
91
 
98
- - Reescreva em **linguagem clara e não técnica**, focando no **benefício para quem usa o produto**, não na
99
- implementação. Quando fizer sentido, use **negrito com o nome do recurso + frase do ganho**
100
- (ex.: `**Sincronização mais rápida**: arquivos sincronizam 2x mais rápido entre dispositivos`).
101
- - **Filtre o ruído**: descarte merges, bumps triviais de versão, ajustes de lint/formatação e commits
102
- puramente internos sem impacto perceptível. O bloco "Melhorias" deve ser um resumo curto, não um
103
- despejo de commits. Ignore `docs:`/`chore:` puramente internos quando o projeto assim fizer no histórico.
92
+ - Reescreva em **linguagem clara e não técnica**, focando no **benefício para quem usa o produto**. Quando fizer
93
+ sentido, use **negrito com o nome do recurso + frase do ganho** (ex.: `**Sincronização mais rápida**: arquivos
94
+ sincronizam 2x mais rápido entre dispositivos`).
95
+ - **Filtre o ruído**: descarte merges, bumps triviais de versão, ajustes de lint/formatação e commits puramente
96
+ internos sem impacto perceptível. O bloco "Melhorias" é um resumo curto, não um despejo de commits. Ignore
97
+ `docs:`/`chore:` puramente internos quando o projeto assim fizer no histórico.
104
98
  - Não invente recursos que os commits não suportam; em dúvida, descreva de forma conservadora.
105
- - **Sem emoji dentro dos itens** — os emojis ficam apenas nos títulos das seções.
99
+ - **Sem emoji dentro dos itens**: os emojis ficam apenas nos títulos das seções.
106
100
 
107
- **3.4 — Siga o estilo do projeto.** Se existir um `CHANGELOG_STYLE.md` na raiz, **siga as diretrizes dele**
108
- (tom, formato, seções). De todo modo, **replique o formato existente** do arquivo de changelog/novidades/
109
- `Versoes.txt` do projeto (espaçamento, cabeçalho, formato do número de versão). Não introduza um formato
110
- novo — o Passo 5 é quem grava, seguindo exatamente esse padrão.
101
+ **3.4 — Siga o estilo do projeto.** Se existir um `CHANGELOG_STYLE.md` na raiz, **siga as diretrizes dele** (tom,
102
+ formato, seções). Prepare um bloco por arquivo de histórico, replicando o formato que já está nele:
103
+
104
+ - cada caminho de `changelogs` do `versao.json` (do app, em monorepo) e, sem config, o `CHANGELOG.md` e os
105
+ arquivos de novidades consumidos pela UI (ex.: `src/data/novidades.ts`);
106
+ - `CHANGELOG.md`: nova seção `## [X.Y.Z] - <currentDate>` no topo (após o cabeçalho), no estilo das anteriores;
107
+ - `Versoes.txt` (fonte `versoes-txt`, só leitura na CLI): o bloco que você escreve, com o **mesmo espaçamento**
108
+ entre blocos, o mesmo cabeçalho e o **mesmo formato de número** (inclusive zeros à esquerda, conforme o
109
+ `formato`) das entradas existentes;
110
+ - use a data de `currentDate`; **nunca invente data**.
111
111
 
112
112
  ## Passo 4 — 🚦 GATE de aprovação (obrigatório)
113
113
 
114
114
  Apresente ao usuário, **antes de escrever qualquer coisa**:
115
115
 
116
- - versão atual → nova versão;
117
- - a lista completa de **arquivos:linha** que serão alterados (todas as ocorrências do Passo 1);
118
- - o changelog proposto e **o bloco exato** que será anexado a cada arquivo de histórico.
116
+ - versão atual → nova versão, e a tag que o release terá (`tagProxima`);
117
+ - o **diff exato** de cada arquivo do `aplicar --dry-run` do Passo 2 (`caminho` e, de cada linha do `diff`, o
118
+ `antes` e o `depois`);
119
+ - o changelog proposto e **o bloco exato** de cada arquivo de histórico (inclusive o do `Versoes.txt`, que o
120
+ dry-run lista como changelog gravado pelo `/versao`);
121
+ - em destaque: arquivos `.dproj` (o bump mexe em várias chaves de cada configuração e a IDE reescreve o arquivo
122
+ ao salvar), fontes e campos em `naoTocar` que ficaram de fora, `divergencias` e `avisos`.
119
123
 
120
- **Peça aprovação explícita.** Não grave nada sem o "ok". Se o bump tocar arquivos sensíveis (ex.: `.dproj`,
121
- `.csproj`), destaque o risco.
124
+ **Peça aprovação explícita.** Não grave nada sem o "ok". Se o usuário pedir outra versão, volte ao Passo 2 com ela.
122
125
 
123
- ## Passo 5 — Aplicar artefatos (somente após aprovação)
126
+ ## Passo 5 — Aplicar (somente após aprovação)
124
127
 
125
- - Atualize **todas** as ocorrências da versão de forma **consistente** (todas as fontes do Passo 1).
126
- - Atualize o(s) arquivo(s) de changelog/novidades seguindo **exatamente o padrão existente**:
127
- - `CHANGELOG.md`: nova seção `## [X.Y.Z] - <currentDate>` no topo (após o cabeçalho); crie com cabeçalho
128
- se não existir.
129
- - Arquivo de novidades da UI (se houver): adicione o release no topo do array, no mesmo tipo/formato já
130
- usado (datas no formato local do arquivo).
131
- - `Versoes.txt`/histórico texto (se houver): anexe ao final, com o **mesmo espaçamento** entre blocos e o
132
- **mesmo formato de número** (inclusive zeros à esquerda) das entradas existentes.
133
- - Use a data de `currentDate`; **nunca invente data**.
128
+ 1. Rode `npx @fontdata/ai-harness versao aplicar <nova> --json` (com `--app` se for o caso). Confira que cada
129
+ arquivo saiu com `gravado: true`; se vier `ok: false` ou `problemas`, pare e reporte: nada foi gravado.
130
+ 2. Grave os blocos de changelog aprovados no gate, exatamente como mostrados (Edit, no topo ou no fim conforme o
131
+ padrão de cada arquivo).
132
+ 3. Se o `versao.json` tiver `gerador`, rode o comando para regenerar os arquivos derivados da versão e mostre o
133
+ que ele alterou (`git status --short`).
134
134
 
135
135
  ## Passo 6 — Validar (abortar se vermelho)
136
136
 
137
- - Rode os testes do projeto (ex.: `pnpm test`/`npm test`/`dotnet test`). Se fizer sentido para o escopo do
138
- release, rode também o build (`pnpm build`/`npm run build`/`dotnet build`).
139
- - Se algo falhar → **abortar** sem commitar e reportar o erro.
140
-
141
- ## Passo 7 — Finalizar (somente commit — SEM push)
142
-
143
- 1. Estagie **apenas** os arquivos do release (nunca `git add -A`): liste explicitamente os arquivos
144
- alterados nos passos anteriores.
145
- 2. Commit semântico (1ª letra maiúscula após `: `): `chore: Release vX.Y.Z` (ou, em projetos Delphi que já
146
- usem o padrão, `build: Modificada versão do sistema no projeto`). Finalize com a linha
147
- `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>`.
148
- 3. **Tag:** se existir automação de tag (Action na `main`), não crie a tag — informe que ela será criada
149
- automaticamente quando o commit chegar na branch alvo. Caso contrário, e somente se o usuário pedir,
150
- ofereça criar a tag `v<X.Y.Z>` localmente.
151
- 4. **Pare aqui, sem `git push`.** Informe o fluxo manual de push ao usuário.
137
+ - Com `validar` no `versao.json`, rode exatamente esse comando.
138
+ - Sem `validar`, use o runner detectado no projeto (scripts do `package.json` com npm/pnpm/yarn, `dotnet test`,
139
+ `flutter test`, `pytest` etc.) e, se fizer sentido para o release, o build.
140
+ - Se `validar` for `"ide"` ou não houver build por linha de comando (Delphi), peça ao usuário que abra o projeto
141
+ na IDE, compile e confirme que a versão aparece certa, e **aguarde a confirmação** antes de seguir.
142
+ - Se algo falhar, **aborte** sem commitar e reporte o erro.
143
+
144
+ ## Passo 7 — Finalizar (somente commit, sem `git push`)
145
+
146
+ 1. Estagie **apenas** os arquivos do release, listados um a um: os do `aplicar`, os changelogs e o que o
147
+ `gerador` alterou (e o `.ai-harness/versao.json`, se foi criado no Passo 1). Nunca `git add -A`.
148
+ 2. Mensagem: o modelo `commit` do `versao.json` com `{versao}` e `{app}` substituídos; sem ele, o padrão do
149
+ histórico do projeto (`git log --oneline` dos últimos releases) ou `chore: Release vX.Y.Z`. Commit semântico,
150
+ com maiúscula após `: `. Finalize com o rodapé `Co-Authored-By` vigente desta sessão, como indicado pelo
151
+ Claude Code, sem escrever um nome de modelo por conta própria.
152
+ 3. **Tag:** se existir automação de tag (ex.: `.github/workflows/auto-tag.yml`), não crie a tag: a Action cria
153
+ quando o commit chega na branch alvo, e uma tag manual duplicaria a dela. Caso contrário, só se o usuário pedir,
154
+ crie a tag `tagProxima` localmente.
155
+ 4. **Pare aqui, sem `git push`.** Informe o fluxo manual de push e, se o `/publicar-release` estiver instalado em
156
+ `.claude/commands/`, mencione que ele faz a publicação a partir deste commit.
157
+
158
+ ## Sem a CLI
159
+
160
+ Use este modo só quando o `npx @fontdata/ai-harness versao` não responder (pacote sem o subcomando, falha de rede).
161
+ Avise o usuário que está no modo manual e siga os mesmos Passos 0, 3, 4, 6 e 7, trocando a CLI pelo que segue.
162
+
163
+ - **Base:** liste as tags com `git tag --sort=-creatordate` e descubra o modelo do projeto: `v*`, `V*`,
164
+ `<app>-v*` (monorepo) ou sem prefixo (`1.2.3`). Use a tag mais recente desse modelo, **ignorando retags com
165
+ sufixo `-N`** (ex.: `v1.2.3-1` é a mesma versão retagueada). Sem tag no modelo, use o primeiro commit
166
+ (`git rev-list --max-parents=0 HEAD`) e avise que é o primeiro release. Os commits vêm de
167
+ `git log <base>..HEAD` (em monorepo, filtre pelo caminho do app).
168
+ - **Fontes:** se houver `.ai-harness/versao.json`, ele lista as fontes. Senão, procure por stack: `version` do
169
+ `package.json` (e do pacote raiz no lock), `.dproj` e constantes em `.pas`, `.csproj`/`AssemblyInfo.cs`/
170
+ `Directory.Build.props`, `pubspec.yaml`, `pyproject.toml`/`__version__`, arquivo `VERSION`. Busque a versão
171
+ atual em todo o projeto para não deixar ocorrência para trás e respeite o `naoTocar` (ex.: `versionCode`,
172
+ `buildNumber`, `runtimeVersion` têm outra numeração).
173
+ - **Cálculo:** versão explícita é override; tipo incrementa o segmento e zera os à direita; sem argumento, `feat:`
174
+ dá minor, os demais tipos dão patch, e quebra dá minor com aviso (major só explícito).
175
+ - **`.dproj`:** atualize as 3 famílias, coerentes entre si:
176
+ 1. `FileVersion` e `ProductVersion` dentro de **todos** os `VerInfo_Keys`, de todas as configurações;
177
+ 2. `VerInfo_MajorVer`, `VerInfo_MinorVer`, `VerInfo_Release` e `VerInfo_Build` em **cada** `PropertyGroup` que
178
+ tem `VerInfo_Keys`, criando as chaves que a IDE omitiu (ela não grava valor zero);
179
+ 3. o bloco legado `<VersionInfo>` (`MajorVer`, `MinorVer`, `Release`, `Build`), quando existir.
180
+
181
+ No `.dproj` os números são inteiros, sem zero à esquerda. Preserve o BOM e o fim de linha CRLF: use
182
+ **Edit, nunca Write** em `.dproj`, porque reescrever o arquivo inteiro perde o BOM e converte as quebras de linha.
183
+ - **Verificação pós-aplicação:** busque a versão antiga de novo e confirme que não sobrou ocorrência fora do
184
+ histórico (changelogs, `Versoes.txt`); em cada `.dproj`, confirme que existe um único valor de `FileVersion` e
185
+ que ele é a versão nova. Mostre o `git diff --stat` ao usuário.
152
186
 
153
187
  ## Guardrails
154
188
 
155
189
  - Sem `git push`: o push publica a versão para todos, e quem decide quando é o usuário. Não criar tag se houver
156
190
  automação que já faça isso.
157
- - **Sempre** confirmar versão + changelog no GATE antes de gravar.
158
- - **Abortar** se a working tree estiver suja ou se os testes/build falharem.
159
- - **Replicar o padrão do projeto** (formato de versão, changelog, commit) — nunca impor um novo.
191
+ - **Sempre** mostrar o diff do `aplicar --dry-run`, o changelog e os blocos no GATE antes de gravar.
192
+ - Versão nos arquivos-fonte só pela CLI (ou pelo modo **Sem a CLI** quando ela falhar); não edite à mão o que o
193
+ `aplicar` grava.
194
+ - **Abortar** se a working tree estiver suja ou se a validação falhar.
195
+ - **Replicar o padrão do projeto** (formato de versão, changelog, commit); nunca impor um novo.
160
196
  - `major` só por argumento explícito; a inferência jamais sobe major.
197
+ - Gravar `.ai-harness/versao.json` só com aprovação do usuário.
161
198
  - Changelog em **pt-BR**, voltado ao usuário final; usar a data de `currentDate`.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: evoluir-harness
3
- description: Cria ou ajusta peças do harness no projeto — agents, skills, rules, hooks, code-reviewer e design system — ou audita a pasta .claude e sugere o que mudar. Use de vez em quando, conforme o projeto cresce.
4
- argument-hint: "[agent|skill|rule|hook|code-reviewer|design-system|auditar]"
3
+ description: Cria ou ajusta peças do harness no projeto — agents, skills, rules, hooks, code-reviewer, design system e o versao.json do /versao — ou audita a pasta .claude e sugere o que mudar. Use de vez em quando, conforme o projeto cresce.
4
+ argument-hint: "[agent|skill|rule|hook|code-reviewer|design-system|versao|auditar]"
5
5
  ---
6
6
 
7
7
  # Evoluir o harness
@@ -27,6 +27,7 @@ A primeira palavra do pedido é o tipo; o resto é contexto (nome, domínio, URL
27
27
  | `hook` | `references/hook.md` | Hooks por agent sobre a biblioteca do harness |
28
28
  | `code-reviewer` | `references/code-reviewer.md` | Agent de review somente leitura e o portão extra |
29
29
  | `design-system` | a referência `design-system.md` em `references/` | Agent, skill e rules da identidade visual |
30
+ | `versao` | `references/versao.md` | `.ai-harness/versao.json` do `/versao`, inclusive a partir de uma cópia customizada do comando |
30
31
 
31
32
  Tipo desconhecido: mostre a tabela e pergunte. Não carregue as outras referências.
32
33