dd-harness 0.32.1 → 0.33.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,31 @@
1
+ /**
2
+ * O diagnostico de defasagem de um projeto que consome o dd-harness.
3
+ *
4
+ * Existe porque a atualizacao nao era automatica em lugar nenhum: o CLI e pacote global
5
+ * que fica na versao do dia da instalacao, e o bloco nos pontos de entrada era detectado
6
+ * de forma binaria — tem ou nao tem —, o que nunca responde "esta na versao atual?".
7
+ * Projeto em producao ficava para tras sem nada avisar.
8
+ *
9
+ * Aqui so DIAGNOSTICA. Aplicar e decisao de quem chama, porque parte do que sai daqui
10
+ * exige julgamento humano (bloco editado a mao) e parte nao (hook faltando).
11
+ */
12
+ export type Item = {
13
+ o_que: string;
14
+ situacao: string;
15
+ acao: "aplicar" | "perguntar" | "em-dia" | "manual";
16
+ detalhe?: string;
17
+ };
18
+ /**
19
+ * `aplicar` e o que esta funcao pode fazer sozinha com seguranca; `perguntar` exige o dono
20
+ * do arquivo; `manual` e o que nao se resolve de dentro do repositorio (o CLI global e por
21
+ * maquina, nao por projeto).
22
+ */
23
+ export declare function diagnosticaAtualizacao(raiz: string, versaoInstalada: string, versaoPublicada: string | null): Promise<Item[]>;
24
+ /**
25
+ * Aplica so os itens marcados `aplicar`. Os de `perguntar` ficam intactos de proposito —
26
+ * quem decide sobre arquivo editado a mao e o dono dele.
27
+ */
28
+ export declare function aplicaAtualizacao(raiz: string, itens: Item[]): Promise<string[]>;
29
+ /** A versao publicada no npm. Nunca lanca: diagnostico sem rede ainda vale para o resto. */
30
+ export declare function versaoPublicada(signal?: AbortSignal): Promise<string | null>;
31
+ export declare function versaoInstalada(url: string): Promise<string>;
@@ -0,0 +1,103 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { analisa } from "./bloco.js";
4
+ import { MOLDES_HISTORICOS } from "./moldes-historicos.js";
5
+ import { COMANDO_DO_CINTO, COMANDO_DO_HOOK, SUGESTAO_AGENTS, VERSAO_DO_MOLDE, escreveHook, escrevePonteiro } from "./escreve-config.js";
6
+ import { versaoDoPacote } from "./versao.js";
7
+ /**
8
+ * `aplicar` e o que esta funcao pode fazer sozinha com seguranca; `perguntar` exige o dono
9
+ * do arquivo; `manual` e o que nao se resolve de dentro do repositorio (o CLI global e por
10
+ * maquina, nao por projeto).
11
+ */
12
+ export async function diagnosticaAtualizacao(raiz, versaoInstalada, versaoPublicada) {
13
+ const itens = [];
14
+ // 1. O CLI global — por maquina, nao por projeto. Rodar em dez pastas nao muda nada.
15
+ if (versaoPublicada && versaoPublicada !== versaoInstalada) {
16
+ itens.push({
17
+ o_que: "CLI",
18
+ situacao: `instalado ${versaoInstalada}, publicado ${versaoPublicada}`,
19
+ acao: "manual",
20
+ detalhe: "npm i -g dd-harness@latest (por máquina, não por projeto)",
21
+ });
22
+ }
23
+ else {
24
+ itens.push({ o_que: "CLI", situacao: `${versaoInstalada}`, acao: "em-dia" });
25
+ }
26
+ // 2. Os dois pontos de entrada. Espelhos: o mesmo bloco, a mesma versao.
27
+ for (const arquivo of ["CLAUDE.md", "AGENTS.md"]) {
28
+ const cru = await readFile(join(raiz, arquivo), "utf8").catch(() => null);
29
+ if (cru === null) {
30
+ itens.push({ o_que: arquivo, situacao: "ausente", acao: "aplicar", detalhe: "será criado" });
31
+ continue;
32
+ }
33
+ const a = analisa(cru, [SUGESTAO_AGENTS, ...MOLDES_HISTORICOS]);
34
+ if (a.caso === "ambiguo") {
35
+ itens.push({ o_que: arquivo, situacao: "precisa de decisão", acao: "perguntar", detalhe: a.motivo });
36
+ }
37
+ else if (a.caso === "ausente") {
38
+ itens.push({ o_que: arquivo, situacao: "sem o bloco do harness", acao: "aplicar", detalhe: "entra no fim do arquivo" });
39
+ }
40
+ else if (a.caso === "migravel") {
41
+ itens.push({ o_que: arquivo, situacao: "bloco sem carimbo (instalado antes das marcas)", acao: "aplicar",
42
+ detalhe: `será marcado e carimbado ${VERSAO_DO_MOLDE}` });
43
+ }
44
+ else if (a.versao !== VERSAO_DO_MOLDE) {
45
+ itens.push({ o_que: arquivo, situacao: `bloco em ${a.versao}, atual ${VERSAO_DO_MOLDE}`, acao: "aplicar",
46
+ detalhe: "troca só o miolo do bloco; o resto do arquivo não é tocado" });
47
+ }
48
+ else if (a.corpo !== SUGESTAO_AGENTS.trim()) {
49
+ // Mesma versao, texto diferente: alguem editou dentro do bloco.
50
+ itens.push({ o_que: arquivo, situacao: `carimbado ${a.versao}, mas o texto foi editado`, acao: "perguntar",
51
+ detalhe: "o conteúdo dentro do bloco não é o do molde" });
52
+ }
53
+ else {
54
+ itens.push({ o_que: arquivo, situacao: `${a.versao}`, acao: "em-dia" });
55
+ }
56
+ }
57
+ // 3. Hooks. `escreveHook` ja e idempotente e nao toca em hook alheio.
58
+ // Pelas constantes, nao por string repetida: o hook do cinto so entrou numa versao
59
+ // posterior, e um projeto antigo tem o SessionStart sem ele — que e o caso mais comum
60
+ // de defasagem, e o que mais custa, porque o cinto e o que avisa antes da edicao.
61
+ const settings = await readFile(join(raiz, ".claude/settings.json"), "utf8").catch(() => "");
62
+ const temOsDois = settings.includes(COMANDO_DO_HOOK) && settings.includes(COMANDO_DO_CINTO);
63
+ itens.push(temOsDois
64
+ ? { o_que: "hooks", situacao: "sessão e cinto declarados", acao: "em-dia" }
65
+ : { o_que: "hooks", situacao: settings ? "incompletos ou legados" : "ausentes", acao: "aplicar",
66
+ detalhe: "acrescenta sem duplicar nem tocar em hook de terceiro" });
67
+ return itens;
68
+ }
69
+ /**
70
+ * Aplica so os itens marcados `aplicar`. Os de `perguntar` ficam intactos de proposito —
71
+ * quem decide sobre arquivo editado a mao e o dono dele.
72
+ */
73
+ export async function aplicaAtualizacao(raiz, itens) {
74
+ const feitos = [];
75
+ for (const item of itens.filter(i => i.acao === "aplicar")) {
76
+ if (item.o_que === "CLAUDE.md" || item.o_que === "AGENTS.md") {
77
+ const r = await escrevePonteiro(raiz, item.o_que);
78
+ feitos.push(`${item.o_que}: ${r.estado}${r.de ? ` (de ${r.de} para ${VERSAO_DO_MOLDE})` : ""}`);
79
+ }
80
+ else if (item.o_que === "hooks") {
81
+ const r = await escreveHook(raiz);
82
+ // `ok: false` e settings.json invalido — nao foi escrito, e dizer "ok" aqui esconderia
83
+ // um projeto que continua sem o cinto.
84
+ feitos.push(r.ok ? `hooks: ${r.estado}` : `hooks: NÃO aplicado (${r.motivo})`);
85
+ }
86
+ }
87
+ return feitos;
88
+ }
89
+ /** A versao publicada no npm. Nunca lanca: diagnostico sem rede ainda vale para o resto. */
90
+ export async function versaoPublicada(signal) {
91
+ try {
92
+ const r = await fetch("https://registry.npmjs.org/dd-harness/latest", { signal });
93
+ if (!r.ok)
94
+ return null;
95
+ return (await r.json()).version ?? null;
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ export async function versaoInstalada(url) {
102
+ return versaoDoPacote(url, "dd-harness").catch(() => "desconhecida");
103
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * O bloco do harness dentro de um CLAUDE.md/AGENTS.md que pertence ao projeto.
3
+ *
4
+ * O arquivo e do repositorio, nao nosso — a decisao esta no Brain
5
+ * (`import-do-claude-md-falha-calado`): a parte gerenciada CONVIVE com o que o projeto
6
+ * tem de proprio, em vez de sobrescrever o arquivo de alguem. O que este modulo
7
+ * acrescenta e a fronteira explicita dessa convivencia: marcas de inicio e fim, com a
8
+ * versao carimbada.
9
+ *
10
+ * Sem a fronteira, atualizar e adivinhacao. A deteccao anterior era binaria — o arquivo
11
+ * contem `ler_artefato`, sim ou nao —, o que responde "ja foi instalado?" e nunca "esta
12
+ * na versao atual?". Foi assim que um projeto em producao ficou para tras: tinha o bloco,
13
+ * entao o init disse "ja-tinha" e nao tocou em nada, enquanto o texto do molde mudava.
14
+ *
15
+ * Com a fronteira, trocar o miolo e seguro por CONSTRUCAO: o que esta fora das marcas
16
+ * nunca e lido para decidir, muito menos reescrito. A regra do dono do projeto — "o
17
+ * CLAUDE.md e diferente para cada um; edite so a linha que e nossa" — deixa de depender
18
+ * da atencao de quem roda a atualizacao.
19
+ */
20
+ export type Analise = {
21
+ caso: "ausente";
22
+ } | {
23
+ caso: "marcado";
24
+ versao: string;
25
+ corpo: string;
26
+ antes: string;
27
+ depois: string;
28
+ } | {
29
+ caso: "migravel";
30
+ corpo: string;
31
+ antes: string;
32
+ depois: string;
33
+ } | {
34
+ caso: "ambiguo";
35
+ motivo: string;
36
+ };
37
+ /** `<!-- dd-harness:inicio v1.3.0 -->` — a versao e o que responde "atualizado ate onde?". */
38
+ export declare function marcaDeInicio(versao: string): string;
39
+ /**
40
+ * Le o arquivo e diz em que caso ele esta. Nao decide nada e nao escreve: quem chama e
41
+ * que sabe se pode aplicar sozinho ou se precisa perguntar.
42
+ *
43
+ * `textoConhecido` e o molde de uma versao anterior, usado so para reconhecer um bloco
44
+ * que ainda nao tem marca — o caso de todo projeto instalado antes desta mudanca.
45
+ */
46
+ export declare function analisa(arquivo: string, textoConhecido: string | string[]): Analise;
47
+ /** Monta o bloco com as marcas. Uma linha em branco de cada lado: markdown gruda. */
48
+ export declare function envolve(corpo: string, versao: string): string;
49
+ /**
50
+ * Devolve o arquivo com o bloco na versao nova, preservando tudo que esta fora dele.
51
+ *
52
+ * Nao decide se DEVE atualizar — so produz o texto. Caso ambiguo nem chega aqui.
53
+ */
54
+ export declare function aplica(arquivo: string, corpo: string, versao: string, analise: Analise): string;
package/dist/bloco.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * O bloco do harness dentro de um CLAUDE.md/AGENTS.md que pertence ao projeto.
3
+ *
4
+ * O arquivo e do repositorio, nao nosso — a decisao esta no Brain
5
+ * (`import-do-claude-md-falha-calado`): a parte gerenciada CONVIVE com o que o projeto
6
+ * tem de proprio, em vez de sobrescrever o arquivo de alguem. O que este modulo
7
+ * acrescenta e a fronteira explicita dessa convivencia: marcas de inicio e fim, com a
8
+ * versao carimbada.
9
+ *
10
+ * Sem a fronteira, atualizar e adivinhacao. A deteccao anterior era binaria — o arquivo
11
+ * contem `ler_artefato`, sim ou nao —, o que responde "ja foi instalado?" e nunca "esta
12
+ * na versao atual?". Foi assim que um projeto em producao ficou para tras: tinha o bloco,
13
+ * entao o init disse "ja-tinha" e nao tocou em nada, enquanto o texto do molde mudava.
14
+ *
15
+ * Com a fronteira, trocar o miolo e seguro por CONSTRUCAO: o que esta fora das marcas
16
+ * nunca e lido para decidir, muito menos reescrito. A regra do dono do projeto — "o
17
+ * CLAUDE.md e diferente para cada um; edite so a linha que e nossa" — deixa de depender
18
+ * da atencao de quem roda a atualizacao.
19
+ */
20
+ /** Comentario HTML: invisivel no render, e o modelo que le o arquivo cru ignora sem ruido. */
21
+ const INICIO = "<!-- dd-harness:inicio";
22
+ const FIM = "<!-- dd-harness:fim -->";
23
+ /** `<!-- dd-harness:inicio v1.3.0 -->` — a versao e o que responde "atualizado ate onde?". */
24
+ export function marcaDeInicio(versao) {
25
+ return `${INICIO} ${versao} -->`;
26
+ }
27
+ /**
28
+ * Le o arquivo e diz em que caso ele esta. Nao decide nada e nao escreve: quem chama e
29
+ * que sabe se pode aplicar sozinho ou se precisa perguntar.
30
+ *
31
+ * `textoConhecido` e o molde de uma versao anterior, usado so para reconhecer um bloco
32
+ * que ainda nao tem marca — o caso de todo projeto instalado antes desta mudanca.
33
+ */
34
+ export function analisa(arquivo, textoConhecido) {
35
+ const conhecidos = (Array.isArray(textoConhecido) ? textoConhecido : [textoConhecido])
36
+ .map(t => t.trim()).filter(Boolean);
37
+ const i = arquivo.indexOf(INICIO);
38
+ if (i !== -1) {
39
+ const fimDaMarca = arquivo.indexOf("-->", i);
40
+ const f = arquivo.indexOf(FIM, i);
41
+ // Marca de inicio sem fim e arquivo truncado ou editado a mao. Mexer nele as cegas
42
+ // apagaria conteudo do projeto — exatamente o que este modulo existe para impedir.
43
+ if (fimDaMarca === -1 || f === -1) {
44
+ return { caso: "ambiguo", motivo: "marca de início sem marca de fim correspondente" };
45
+ }
46
+ if (arquivo.indexOf(INICIO, i + 1) !== -1) {
47
+ return { caso: "ambiguo", motivo: "mais de um bloco do dd-harness no arquivo" };
48
+ }
49
+ const versao = arquivo.slice(i + INICIO.length, fimDaMarca).trim();
50
+ return {
51
+ caso: "marcado",
52
+ versao,
53
+ corpo: arquivo.slice(fimDaMarca + 3, f).trim(),
54
+ antes: arquivo.slice(0, i),
55
+ depois: arquivo.slice(f + FIM.length),
56
+ };
57
+ }
58
+ // Sem marca: procura o bloco pelo texto EXATO de alguma versao que ja existiu. Exato de
59
+ // proposito — encontrar "parecido" e o comeco de reescrever o que o dono escreveu.
60
+ //
61
+ // Varias versoes porque um projeto instalado ha meses tem o molde daquela epoca, e sem
62
+ // reconhece-lo ele cairia em "editado a mao": o caminho seguro, mas errado, porque
63
+ // ninguem editou nada. Projeto atrasado e o caso COMUM, e ele tem que se resolver sozinho.
64
+ for (const conhecido of conhecidos) {
65
+ const j = arquivo.indexOf(conhecido);
66
+ if (j !== -1) {
67
+ return {
68
+ caso: "migravel",
69
+ corpo: conhecido,
70
+ antes: arquivo.slice(0, j),
71
+ depois: arquivo.slice(j + conhecido.length),
72
+ };
73
+ }
74
+ }
75
+ // Tem nosso protocolo, mas nao como o molde o escreveu: foi editado. Caso de perguntar,
76
+ // nunca de sobrescrever.
77
+ if (arquivo.includes("ler_artefato")) {
78
+ return { caso: "ambiguo", motivo: "o bloco existe mas foi editado; não bate com nenhum molde conhecido" };
79
+ }
80
+ return { caso: "ausente" };
81
+ }
82
+ /** Monta o bloco com as marcas. Uma linha em branco de cada lado: markdown gruda. */
83
+ export function envolve(corpo, versao) {
84
+ return `${marcaDeInicio(versao)}\n\n${corpo.trim()}\n\n${FIM}`;
85
+ }
86
+ /**
87
+ * Devolve o arquivo com o bloco na versao nova, preservando tudo que esta fora dele.
88
+ *
89
+ * Nao decide se DEVE atualizar — so produz o texto. Caso ambiguo nem chega aqui.
90
+ */
91
+ export function aplica(arquivo, corpo, versao, analise) {
92
+ const bloco = envolve(corpo, versao);
93
+ if (analise.caso === "marcado" || analise.caso === "migravel") {
94
+ return `${analise.antes}${bloco}${analise.depois}`;
95
+ }
96
+ // Ausente: entra no fim, que e onde o molde sempre acrescentou.
97
+ if (arquivo.trim() === "")
98
+ return `${bloco}\n`;
99
+ return `${arquivo}${arquivo.endsWith("\n") ? "\n" : "\n\n"}${bloco}\n`;
100
+ }
@@ -19,6 +19,8 @@ export type ResultadoDaEscrita<T extends string> = {
19
19
  motivo: "json-invalido";
20
20
  caminho: string;
21
21
  };
22
+ export declare const COMANDO_DO_HOOK = "dd-harness politica --hook";
23
+ export declare const COMANDO_DO_CINTO = "dd-harness cinto";
22
24
  /**
23
25
  * Acrescenta o hook do dd-harness a `.claude/settings.json`, sem tocar em outros hooks.
24
26
  *
@@ -44,10 +46,26 @@ export declare function escreveMcp(raiz: string): Promise<ResultadoDaEscrita<"cr
44
46
  * de objeto.
45
47
  */
46
48
  export declare function escrevePonteiro(raiz: string, arquivo: "AGENTS.md" | "CLAUDE.md"): Promise<{
47
- estado: "criado" | "ja-tinha" | "acrescentado";
49
+ estado: "criado" | "ja-tinha" | "acrescentado" | "atualizado" | "migrado" | "precisa-decisao";
50
+ de?: string;
51
+ motivo?: string;
48
52
  }>;
49
53
  /** Compatibilidade: `init` ainda chama pelo nome antigo. */
50
54
  export declare const escreveAgents: (raiz: string) => Promise<{
51
- estado: "criado" | "ja-tinha" | "acrescentado";
55
+ estado: "criado" | "ja-tinha" | "acrescentado" | "atualizado" | "migrado" | "precisa-decisao";
56
+ de?: string;
57
+ motivo?: string;
52
58
  }>;
59
+ /**
60
+ * A versao do MOLDE, que nao e a do CLI.
61
+ *
62
+ * O CLI publica por correcao — 0.32.1 saiu de um fix que nao mudou uma linha do texto
63
+ * abaixo. Carimbar a versao do pacote faria todo projeto parecer desatualizado a cada
64
+ * publicacao, e "desatualizado" que aparece sem nada para fazer e o aviso que ensina a
65
+ * ignorar aviso.
66
+ *
67
+ * Suba este numero SO quando o texto do bloco mudar de fato. E o carimbo que um projeto
68
+ * em producao compara para saber se precisa de atualizacao.
69
+ */
70
+ export declare const VERSAO_DO_MOLDE = "v1.3.0";
53
71
  export declare const SUGESTAO_AGENTS = "## Protocolo do dd-harness\n\nEste projeto tem um protocolo de trabalho que **n\u00E3o est\u00E1 neste arquivo**.\n\n**ANTES de qualquer outra coisa**, chame a ferramenta MCP `ler_artefato` com\n`tipo: \"politica\"` e siga o que ela devolver. \u00C9 a primeira a\u00E7\u00E3o da sess\u00E3o, antes\nde ler c\u00F3digo, responder ou planejar.\n\n- Se a ferramenta n\u00E3o existir, o servidor MCP do dd-harness n\u00E3o est\u00E1 declarado:\n avise o usu\u00E1rio e **n\u00E3o modifique nada** at\u00E9 ele resolver.\n- Se ela devolver vazio, este projeto nunca foi briefado \u2014 diga isso ao usu\u00E1rio.\n\nLeia tamb\u00E9m o briefing com a mesma ferramenta (tipo briefing). Os dois s\u00E3o obrigat\u00F3rios.\n\nDepois, `ler_roadmap`: se houver uma fase **Agora**, \u00E9 dela que saem os passos\ndesta sess\u00E3o. Lista vazia significa que este projeto n\u00E3o usa roadmap, e isso \u00E9\nv\u00E1lido \u2014 n\u00E3o crie fase sem o usu\u00E1rio pedir.\n\nE `listar_skills`: s\u00E3o os procedimentos deste projeto. **Invoque a que couber\nANTES de fazer o trabalho, n\u00E3o depois** \u2014 skill lida no fim vira revis\u00E3o do que\nj\u00E1 saiu errado, e \u00E9 tarde.\n\nVale mesmo quando o pedido parece pequeno: \"\u00E9 s\u00F3 um ajuste\" \u00E9 exatamente a\nfrase que antecede pular o procedimento. A pol\u00EDtica diz quais s\u00E3o obrigat\u00F3rias\ne quando.";
@@ -1,7 +1,9 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
- const COMANDO_DO_HOOK = "dd-harness politica --hook";
4
- const COMANDO_DO_CINTO = "dd-harness cinto";
3
+ import { analisa, aplica } from "./bloco.js";
4
+ import { MOLDES_HISTORICOS } from "./moldes-historicos.js";
5
+ export const COMANDO_DO_HOOK = "dd-harness politica --hook";
6
+ export const COMANDO_DO_CINTO = "dd-harness cinto";
5
7
  /**
6
8
  * Acrescenta o hook do dd-harness a `.claude/settings.json`, sem tocar em outros hooks.
7
9
  *
@@ -92,7 +94,6 @@ export async function escreveMcp(raiz) {
92
94
  */
93
95
  export async function escrevePonteiro(raiz, arquivo) {
94
96
  const caminho = join(raiz, arquivo);
95
- const instrucao = SUGESTAO_AGENTS;
96
97
  let atual = null;
97
98
  try {
98
99
  atual = await readFile(caminho, "utf8");
@@ -101,39 +102,66 @@ export async function escrevePonteiro(raiz, arquivo) {
101
102
  // Sem arquivo: cria do zero.
102
103
  }
103
104
  if (atual === null) {
104
- await writeFile(caminho, `${instrucao}\n`, "utf8");
105
+ await writeFile(caminho, `${aplica("", SUGESTAO_AGENTS, VERSAO_DO_MOLDE, { caso: "ausente" })}`, "utf8");
105
106
  return { estado: "criado" };
106
107
  }
107
- if (atual.includes("ler_artefato"))
108
+ const analise = analisa(atual, [SUGESTAO_AGENTS, ...MOLDES_HISTORICOS]);
109
+ // Bloco editado, truncado ou duplicado: quem decide e o dono do arquivo. Escrever aqui
110
+ // seria apagar o que alguem escreveu — o unico erro desta funcao que nao da para desfazer
111
+ // sem o git.
112
+ if (analise.caso === "ambiguo") {
113
+ return { estado: "precisa-decisao", motivo: analise.motivo };
114
+ }
115
+ // Ja esta na versao atual: nao reescreve, para o `start` nao sujar o git toda vez.
116
+ if (analise.caso === "marcado" && analise.versao === VERSAO_DO_MOLDE && analise.corpo === SUGESTAO_AGENTS.trim()) {
117
+ return { estado: "ja-tinha" };
118
+ }
119
+ const novo = aplica(atual, SUGESTAO_AGENTS, VERSAO_DO_MOLDE, analise);
120
+ if (novo === atual)
108
121
  return { estado: "ja-tinha" };
109
- const separador = atual.endsWith("\n") ? "\n" : "\n\n";
110
- await writeFile(caminho, `${atual}${separador}${instrucao}\n`, "utf8");
122
+ await writeFile(caminho, novo, "utf8");
123
+ if (analise.caso === "marcado")
124
+ return { estado: "atualizado", de: analise.versao };
125
+ if (analise.caso === "migravel")
126
+ return { estado: "migrado" };
111
127
  return { estado: "acrescentado" };
112
128
  }
113
129
  /** Compatibilidade: `init` ainda chama pelo nome antigo. */
114
130
  export const escreveAgents = (raiz) => escrevePonteiro(raiz, "AGENTS.md");
131
+ /**
132
+ * A versao do MOLDE, que nao e a do CLI.
133
+ *
134
+ * O CLI publica por correcao — 0.32.1 saiu de um fix que nao mudou uma linha do texto
135
+ * abaixo. Carimbar a versao do pacote faria todo projeto parecer desatualizado a cada
136
+ * publicacao, e "desatualizado" que aparece sem nada para fazer e o aviso que ensina a
137
+ * ignorar aviso.
138
+ *
139
+ * Suba este numero SO quando o texto do bloco mudar de fato. E o carimbo que um projeto
140
+ * em producao compara para saber se precisa de atualizacao.
141
+ */
142
+ export const VERSAO_DO_MOLDE = "v1.3.0";
115
143
  export const SUGESTAO_AGENTS = `## Protocolo do dd-harness
116
-
117
- Este projeto tem um protocolo de trabalho que **não está neste arquivo**.
118
-
119
- **ANTES de qualquer outra coisa**, chame a ferramenta MCP \`ler_artefato\` com
120
- \`tipo: "politica"\` e siga o que ela devolver. É a primeira ação da sessão, antes
121
- de ler código, responder ou planejar.
122
-
123
- - Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado:
124
- avise o usuário e **não modifique nada** até ele resolver.
125
- - Se ela devolver vazio, este projeto nunca foi briefado — diga isso ao usuário.
126
-
144
+
145
+ Este projeto tem um protocolo de trabalho que **não está neste arquivo**.
146
+
147
+ **ANTES de qualquer outra coisa**, chame a ferramenta MCP \`ler_artefato\` com
148
+ \`tipo: "politica"\` e siga o que ela devolver. É a primeira ação da sessão, antes
149
+ de ler código, responder ou planejar.
150
+
151
+ - Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado:
152
+ avise o usuário e **não modifique nada** até ele resolver.
153
+ - Se ela devolver vazio, este projeto nunca foi briefado — diga isso ao usuário.
154
+
127
155
  Leia também o briefing com a mesma ferramenta (tipo briefing). Os dois são obrigatórios.
128
156
 
129
157
  Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
130
- desta sessão. Lista vazia significa que este projeto não usa roadmap, e isso é
131
- válido — não crie fase sem o usuário pedir.
132
-
133
- E \`listar_skills\`: são os procedimentos deste projeto. **Invoque a que couber
134
- ANTES de fazer o trabalho, não depois** — skill lida no fim vira revisão do que
135
- já saiu errado, e é tarde.
136
-
137
- Vale mesmo quando o pedido parece pequeno: "é só um ajuste" é exatamente a
138
- frase que antecede pular o procedimento. A política diz quais são obrigatórias
158
+ desta sessão. Lista vazia significa que este projeto não usa roadmap, e isso é
159
+ válido — não crie fase sem o usuário pedir.
160
+
161
+ E \`listar_skills\`: são os procedimentos deste projeto. **Invoque a que couber
162
+ ANTES de fazer o trabalho, não depois** — skill lida no fim vira revisão do que
163
+ já saiu errado, e é tarde.
164
+
165
+ Vale mesmo quando o pedido parece pequeno: "é só um ajuste" é exatamente a
166
+ frase que antecede pular o procedimento. A política diz quais são obrigatórias
139
167
  e quando.`;
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { diagnostico } from "./diagnostico.js";
3
+ import { aplicaAtualizacao, diagnosticaAtualizacao, versaoInstalada, versaoPublicada } from "./atualizar.js";
3
4
  import { instalaHosts, selecionaHosts } from "./hosts.js";
4
5
  import { achaRaiz } from "./config.js";
5
6
  import { abreSessao, guardaSessao, saidaDaGuarda, saidaDoBoot } from "./sessao.js";
@@ -43,84 +44,89 @@ import { REGRA_DE_COMMITS } from "./regras-de-commit.js";
43
44
  * em repositorio alheio tem que envelhecer bem, e cada dependencia e uma chance de nao
44
45
  * envelhecer.
45
46
  */
46
- const AJUDA = `dd-harness — a política e o Brain do projeto, no serviço
47
-
47
+ const AJUDA = `dd-harness — a política e o Brain do projeto, no serviço
48
+
48
49
  dd-harness diagnostico [--mcp] confere projeto, contexto e conexão MCP
50
+ dd-harness atualizar [--aplicar] diz o que neste projeto ficou para trás do
51
+ harness atual (CLI, hooks, bloco do
52
+ CLAUDE.md/AGENTS.md) e, com --aplicar,
53
+ atualiza o que é seguro. O que está FORA
54
+ do bloco é do projeto e nunca é tocado
49
55
  dd-harness integrar --host todos instala MCP/regras/hooks em projeto existente
50
56
  dd-harness sessao --host <host> hook de boot (stdin JSON)
51
57
  dd-harness guarda --host <host> hook antes de ferramentas (stdin JSON)
52
- dd-harness start [--host <host>] numa pasta vazia: conduz tudo (login,
53
- espaço, projeto, config) numa tacada
54
- dd-harness login --token <token> [--api <url>]
55
- guarda a credencial desta máquina
56
- dd-harness projeto <slug> --nome "<nome>" [--tenant <t>] [--api <url>]
57
- cria o projeto no serviço (antes do init)
58
- dd-harness init --tenant <t> --projeto <p> [--api <url>]
59
- escreve .dd-harness.json; depois rode integrar
60
- dd-harness pasta <slug> --definicao "o que entra e o que não entra"
61
- cria a pasta que o gravar exige
62
- dd-harness gravar <arquivo.md> registra uma memória a partir de um markdown
63
- dd-harness editar <arquivo.md> corrige o que já está gravado
64
- dd-harness arquivar <pasta>/<slug> --motivo <obsoleta|incorreta|fora_dos_filtros>
65
- [--substituida-por <pasta>/<slug>]
66
- tira de circulação sem apagar
67
- dd-harness promover <pasta>/<slug>
68
- torna a memória global: vale para TODO
69
- projeto do espaço, inclusive os futuros
70
- dd-harness despromover <pasta>/<slug>
71
- traz de volta ao alcance dos vínculos
72
- dd-harness apagar <pasta>/<slug> [--confirmar <espaço>]
73
- apaga de vez, em cascata — sem desfazer.
74
- Para tirar de circulação guardando o
75
- conteúdo, use "arquivar"
76
- dd-harness buscar "<pergunta>" acha memória por relevância, não por arquivo
77
- dd-harness ler <pasta>/<slug> imprime a memória inteira, no formato de gravar
78
- dd-harness reancorar <pasta>/<slug> --de "<alvo>" --para "<alvo>"
79
- troca o alvo de uma âncora que mudou de lugar
80
- dd-harness check [--commit <sha>] mede as âncoras e reporta a deriva
81
- dd-harness status só lê: o tamanho do Brain e o que espera julgamento
82
- dd-harness politica [--hook] imprime a política do serviço
83
- saída 0 = veio; 3 = projeto sem política;
84
- 1 = não consegui buscar
85
- --hook: fala o protocolo do SessionStart do
86
- Claude Code, para pôr a política no contexto
87
- dd-harness cinto o interceptador pré-voo: lê a edição no stdin
88
- e devolve a memória que fala daquele trecho.
89
- Quem chama é o hook PreToolUse, não você
90
- dd-harness skills as skills deste projeto e quando cada uma serve
91
- dd-harness skill <nome> o procedimento de uma delas (o mesmo que o
92
- agente recebe ao invocá-la)
93
- dd-harness roadmap as fases abertas: a atual inteira, as próximas
94
- por título (opcional — projeto sem fase não tem)
95
- dd-harness changelog [--versao <v>]
96
- o que já foi concluído, agrupado por versão
97
- dd-harness fase criar --titulo "<t>" [--conteudo <arquivo.md>] [--versao <v>]
98
- [--status ideia|aberta|concluida] [--slug <s>]
99
- dd-harness fase editar <slug> [--titulo "<t>"] [--conteudo <arquivo.md>]
100
- [--versao <v> | --sem-versao] [--status <s>] [--ordem <n>]
101
- concluir = --status concluida: a fase sai do
102
- roadmap e entra no changelog, nada migra
103
- dd-harness --help
104
- dd-harness --version qual binário está instalado nesta máquina
105
-
58
+ dd-harness start [--host <host>] numa pasta vazia: conduz tudo (login,
59
+ espaço, projeto, config) numa tacada
60
+ dd-harness login --token <token> [--api <url>]
61
+ guarda a credencial desta máquina
62
+ dd-harness projeto <slug> --nome "<nome>" [--tenant <t>] [--api <url>]
63
+ cria o projeto no serviço (antes do init)
64
+ dd-harness init --tenant <t> --projeto <p> [--api <url>]
65
+ escreve .dd-harness.json; depois rode integrar
66
+ dd-harness pasta <slug> --definicao "o que entra e o que não entra"
67
+ cria a pasta que o gravar exige
68
+ dd-harness gravar <arquivo.md> registra uma memória a partir de um markdown
69
+ dd-harness editar <arquivo.md> corrige o que já está gravado
70
+ dd-harness arquivar <pasta>/<slug> --motivo <obsoleta|incorreta|fora_dos_filtros>
71
+ [--substituida-por <pasta>/<slug>]
72
+ tira de circulação sem apagar
73
+ dd-harness promover <pasta>/<slug>
74
+ torna a memória global: vale para TODO
75
+ projeto do espaço, inclusive os futuros
76
+ dd-harness despromover <pasta>/<slug>
77
+ traz de volta ao alcance dos vínculos
78
+ dd-harness apagar <pasta>/<slug> [--confirmar <espaço>]
79
+ apaga de vez, em cascata — sem desfazer.
80
+ Para tirar de circulação guardando o
81
+ conteúdo, use "arquivar"
82
+ dd-harness buscar "<pergunta>" acha memória por relevância, não por arquivo
83
+ dd-harness ler <pasta>/<slug> imprime a memória inteira, no formato de gravar
84
+ dd-harness reancorar <pasta>/<slug> --de "<alvo>" --para "<alvo>"
85
+ troca o alvo de uma âncora que mudou de lugar
86
+ dd-harness check [--commit <sha>] mede as âncoras e reporta a deriva
87
+ dd-harness status só lê: o tamanho do Brain e o que espera julgamento
88
+ dd-harness politica [--hook] imprime a política do serviço
89
+ saída 0 = veio; 3 = projeto sem política;
90
+ 1 = não consegui buscar
91
+ --hook: fala o protocolo do SessionStart do
92
+ Claude Code, para pôr a política no contexto
93
+ dd-harness cinto o interceptador pré-voo: lê a edição no stdin
94
+ e devolve a memória que fala daquele trecho.
95
+ Quem chama é o hook PreToolUse, não você
96
+ dd-harness skills as skills deste projeto e quando cada uma serve
97
+ dd-harness skill <nome> o procedimento de uma delas (o mesmo que o
98
+ agente recebe ao invocá-la)
99
+ dd-harness roadmap as fases abertas: a atual inteira, as próximas
100
+ por título (opcional — projeto sem fase não tem)
101
+ dd-harness changelog [--versao <v>]
102
+ o que já foi concluído, agrupado por versão
103
+ dd-harness fase criar --titulo "<t>" [--conteudo <arquivo.md>] [--versao <v>]
104
+ [--status ideia|aberta|concluida] [--slug <s>]
105
+ dd-harness fase editar <slug> [--titulo "<t>"] [--conteudo <arquivo.md>]
106
+ [--versao <v> | --sem-versao] [--status <s>] [--ordem <n>]
107
+ concluir = --status concluida: a fase sai do
108
+ roadmap e entra no changelog, nada migra
109
+ dd-harness --help
110
+ dd-harness --version qual binário está instalado nesta máquina
111
+
106
112
  Política e briefing chegam do serviço. O disco guarda configuração, ponteiros
107
113
  para skills e estado descartável em ~/.dd-harness. Hosts: claude, codex,
108
- antigravity, lista separada por vírgulas ou todos.
114
+ antigravity, lista separada por vírgulas ou todos.
109
115
  `;
110
116
  /**
111
117
  * Os ganchos sao IMPRESSOS, nunca instalados. `.git/hooks` nao e versionado e nao e
112
118
  * nosso: escrever la dentro sem a pessoa pedir e o mesmo tipo de invasao que
113
119
  * sobrescrever o CLAUDE.md dela. Quem cola, decide.
114
120
  */
115
- const GANCHOS = `
116
- Opcional — o gancho que devolve a memória ao code review:
117
-
118
- .git/hooks/post-commit (avisa quais memórias falam do que você mudou)
119
- #!/bin/sh
120
- dd-harness check --commit "$(git rev-parse HEAD)" || true
121
-
122
- Termina em sucesso mesmo com deriva: avisa, não bloqueia.
123
-
121
+ const GANCHOS = `
122
+ Opcional — o gancho que devolve a memória ao code review:
123
+
124
+ .git/hooks/post-commit (avisa quais memórias falam do que você mudou)
125
+ #!/bin/sh
126
+ dd-harness check --commit "$(git rev-parse HEAD)" || true
127
+
128
+ Termina em sucesso mesmo com deriva: avisa, não bloqueia.
129
+
124
130
  Instale os hooks de sessão e guarda com \`dd-harness integrar --host <host>\`.`;
125
131
  function argumento(argv, nome) {
126
132
  const i = argv.indexOf(`--${nome}`);
@@ -155,7 +161,7 @@ async function comandoSkill(argv) {
155
161
  throw new Error("uso: dd-harness skill <nome>");
156
162
  }
157
163
  const s = await leSkill(process.cwd(), slug);
158
- console.log(`# ${s.slug}
164
+ console.log(`# ${s.slug}
159
165
  `);
160
166
  console.log(s.descricao);
161
167
  console.log("");
@@ -516,6 +522,40 @@ async function temWorkerConfigurado() {
516
522
  const { workerRepo } = await leConfigDaMaquina();
517
523
  return Boolean(workerRepo);
518
524
  }
525
+ /**
526
+ * Diagnostica (e opcionalmente aplica) a defasagem deste projeto.
527
+ *
528
+ * O padrao e SO RELATAR: comando que edita `CLAUDE.md` de projeto em producao nao faz isso
529
+ * por omissao. `--aplicar` escreve o que e seguro, e nunca o que exige decisao do dono.
530
+ */
531
+ async function comandoAtualizar(argv) {
532
+ const raiz = await achaRaiz();
533
+ const itens = await diagnosticaAtualizacao(raiz, await versaoInstalada(import.meta.url), await versaoPublicada(AbortSignal.timeout(6000)));
534
+ const simbolo = { "em-dia": "ok ", aplicar: "-> ", perguntar: "?? ", manual: "! " };
535
+ for (const i of itens) {
536
+ console.log(`${simbolo[i.acao]}${i.o_que}: ${i.situacao}${i.detalhe ? ` — ${i.detalhe}` : ""}`);
537
+ }
538
+ const aAplicar = itens.filter(i => i.acao === "aplicar");
539
+ const aPerguntar = itens.filter(i => i.acao === "perguntar");
540
+ if (!argv.includes("--aplicar")) {
541
+ console.log(aAplicar.length
542
+ ? `\n${aAplicar.length} item(ns) podem ser aplicados: dd-harness atualizar --aplicar`
543
+ : "\nNada a aplicar automaticamente.");
544
+ }
545
+ else if (aAplicar.length) {
546
+ console.log("");
547
+ for (const linha of await aplicaAtualizacao(raiz, itens))
548
+ console.log(linha);
549
+ }
550
+ // Estes ficam de fora do `--aplicar` de proposito: o arquivo foi editado a mao, e so o
551
+ // dono sabe se a edicao dele continua valendo depois da mudanca do molde.
552
+ if (aPerguntar.length) {
553
+ console.log(`\n${aPerguntar.length} item(ns) NÃO foram tocados por exigirem decisão sua:`);
554
+ for (const i of aPerguntar)
555
+ console.log(` ${i.o_que}: ${i.detalhe ?? i.situacao}`);
556
+ console.log(" Use a skill atualizar-harness para conduzir esses casos.");
557
+ }
558
+ }
519
559
  async function comandoInit(argv) {
520
560
  const tenant = argumento(argv, "tenant");
521
561
  const projeto = argumento(argv, "projeto");
@@ -591,7 +631,7 @@ async function comandoApagar(argv) {
591
631
  if (!r.apagou) {
592
632
  console.log(r.detalhe);
593
633
  if (r.confirmacaoEsperada) {
594
- console.log(`
634
+ console.log(`
595
635
  dd-harness apagar ${endereco} --confirmar ${r.confirmacaoEsperada}`);
596
636
  }
597
637
  // Sem exit diferente de zero: recusar por falta de confirmacao nao e falha, e o
@@ -644,7 +684,7 @@ async function comandoBuscar(argv) {
644
684
  if (!r.semantica) {
645
685
  console.log(" (só busca textual: sem provedor de embedding no serviço)");
646
686
  }
647
- avisaSobreAFila(r.esperandoIndexacao);
687
+ await avisaSobreAFila(r.esperandoIndexacao);
648
688
  return;
649
689
  }
650
690
  console.log(r.semantica
@@ -654,19 +694,26 @@ async function comandoBuscar(argv) {
654
694
  console.log(` ${a.endereco} — ${a.titulo}`);
655
695
  console.log(` ${a.resumo}`);
656
696
  }
657
- avisaSobreAFila(r.esperandoIndexacao);
697
+ await avisaSobreAFila(r.esperandoIndexacao);
658
698
  }
659
699
  /**
660
700
  * Memoria sem vetor nao aparece na busca semantica, e a resposta parece completa do
661
701
  * mesmo jeito. `semantica: true` diz so que a CONSULTA foi vetorizada — a base pode
662
702
  * estar inteira na fila, e ai a busca responde lexical com cara de semantica.
663
703
  */
664
- function avisaSobreAFila(esperando) {
704
+ async function avisaSobreAFila(esperando) {
665
705
  if (esperando <= 0)
666
706
  return;
707
+ // Dispara em vez de pedir: indexar e trabalho da maquina. O comando so seria executavel
708
+ // dentro do monorepo do harness, e quem roda `buscar` esta no projeto dele.
709
+ const worker = await subiuOWorker(process.cwd(), esperando);
667
710
  console.log("");
668
711
  console.log(`ATENÇÃO: ${esperando} memória(s) ainda sem vetor — podem existir respostas`);
669
- console.log(" melhores que não apareceram aqui. Rode `start-worker.bat`.");
712
+ console.log(worker.subiu
713
+ ? " melhores que não apareceram aqui. Indexação disparada; repita a busca em instantes."
714
+ : worker.motivo === "sem-worker"
715
+ ? " melhores que não apareceram aqui. Configure o worker com `dd-harness worker --configurar`."
716
+ : " melhores que não apareceram aqui. Não consegui disparar o worker automaticamente.");
670
717
  }
671
718
  async function comandoReancorar(argv) {
672
719
  const endereco = argv[0];
@@ -1179,6 +1226,8 @@ async function principal() {
1179
1226
  case "diagnostico":
1180
1227
  console.log((await diagnostico(process.cwd(), resto.includes("--mcp"))).join("\n"));
1181
1228
  break;
1229
+ case "atualizar":
1230
+ return comandoAtualizar(resto);
1182
1231
  case "integrar": {
1183
1232
  const hosts = selecionaHosts(argumento(resto, "host") ?? "todos");
1184
1233
  for (const aviso of await instalaHosts(process.cwd(), hosts))
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Os textos do molde que ja estiveram em projetos reais.
3
+ *
4
+ * GERADO por scripts/extrai-moldes-historicos.mjs a partir do historico do git. Serve a uma
5
+ * coisa so: reconhecer o bloco de um projeto que ficou para tras, para migra-lo sozinho em
6
+ * vez de perguntar. Sem isto, "projeto desatualizado" e "arquivo editado a mao" ficam
7
+ * indistinguiveis — e o primeiro, que e o caso comum, receberia o tratamento do segundo.
8
+ *
9
+ * Do mais novo para o mais antigo. Nao editar a mao: regenerar.
10
+ */
11
+ export declare const MOLDES_HISTORICOS: string[];
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Os textos do molde que ja estiveram em projetos reais.
3
+ *
4
+ * GERADO por scripts/extrai-moldes-historicos.mjs a partir do historico do git. Serve a uma
5
+ * coisa so: reconhecer o bloco de um projeto que ficou para tras, para migra-lo sozinho em
6
+ * vez de perguntar. Sem isto, "projeto desatualizado" e "arquivo editado a mao" ficam
7
+ * indistinguiveis — e o primeiro, que e o caso comum, receberia o tratamento do segundo.
8
+ *
9
+ * Do mais novo para o mais antigo. Nao editar a mao: regenerar.
10
+ */
11
+ export const MOLDES_HISTORICOS = [
12
+ // ca1e529
13
+ `## Protocolo do dd-harness
14
+
15
+ Este projeto tem um protocolo de trabalho que **não está neste arquivo**.
16
+
17
+ **ANTES de qualquer outra coisa**, chame a ferramenta MCP \`ler_artefato\` com
18
+ \`tipo: "politica"\` e siga o que ela devolver. É a primeira ação da sessão, antes
19
+ de ler código, responder ou planejar.
20
+
21
+ - Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado:
22
+ avise o usuário e **não modifique nada** até ele resolver.
23
+ - Se ela devolver vazio, este projeto nunca foi briefado — diga isso ao usuário.
24
+
25
+ Leia também o briefing com a mesma ferramenta (tipo briefing). Os dois são obrigatórios.
26
+
27
+ Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
28
+ desta sessão. Lista vazia significa que este projeto não usa roadmap, e isso é
29
+ válido — não crie fase sem o usuário pedir.
30
+
31
+ E \`listar_skills\`: são os procedimentos deste projeto. **Invoque a que couber
32
+ ANTES de fazer o trabalho, não depois** — skill lida no fim vira revisão do que
33
+ já saiu errado, e é tarde.
34
+
35
+ Vale mesmo quando o pedido parece pequeno: "é só um ajuste" é exatamente a
36
+ frase que antecede pular o procedimento. A política diz quais são obrigatórias
37
+ e quando.`,
38
+ // 875342e
39
+ `## Protocolo do dd-harness
40
+
41
+ Este projeto tem um protocolo de trabalho que **não está neste arquivo**.
42
+
43
+ **ANTES de qualquer outra coisa**, chame a ferramenta MCP \`ler_artefato\` com
44
+ \`tipo: "politica"\` e siga o que ela devolver. É a primeira ação da sessão, antes
45
+ de ler código, responder ou planejar.
46
+
47
+ - Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado:
48
+ avise o usuário e **não modifique nada** até ele resolver.
49
+ - Se ela devolver vazio, este projeto nunca foi briefado — diga isso ao usuário.
50
+
51
+ Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
52
+ desta sessão. Lista vazia significa que este projeto não usa roadmap, e isso é
53
+ válido — não crie fase sem o usuário pedir.
54
+
55
+ E \`listar_skills\`: são os procedimentos deste projeto. **Invoque a que couber
56
+ ANTES de fazer o trabalho, não depois** — skill lida no fim vira revisão do que
57
+ já saiu errado, e é tarde.
58
+
59
+ Vale mesmo quando o pedido parece pequeno: "é só um ajuste" é exatamente a
60
+ frase que antecede pular o procedimento. A política diz quais são obrigatórias
61
+ e quando.`,
62
+ // f59da48
63
+ `## Protocolo do dd-harness
64
+
65
+ Este projeto tem um protocolo de trabalho que **não está neste arquivo**.
66
+
67
+ **ANTES de qualquer outra coisa**, chame a ferramenta MCP \`ler_artefato\` com
68
+ \`tipo: "politica"\` e siga o que ela devolver. É a primeira ação da sessão, antes
69
+ de ler código, responder ou planejar.
70
+
71
+ - Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado:
72
+ avise o usuário e **não modifique nada** até ele resolver.
73
+ - Se ela devolver vazio, este projeto nunca foi briefado — diga isso ao usuário.
74
+
75
+ Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
76
+ desta sessão. Lista vazia significa que este projeto não usa roadmap, e isso é
77
+ válido — não crie fase sem o usuário pedir.`,
78
+ // 5fb3f02
79
+ `## Protocolo do dd-harness
80
+
81
+ Este projeto tem um protocolo de trabalho que **não está neste arquivo**.
82
+
83
+ **ANTES de qualquer outra coisa**, chame a ferramenta MCP \`ler_artefato\` com
84
+ \`tipo: "politica"\` e siga o que ela devolver. É a primeira ação da sessão, antes
85
+ de ler código, responder ou planejar.
86
+
87
+ - Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado:
88
+ avise o usuário e **não modifique nada** até ele resolver.
89
+ - Se ela devolver vazio, este projeto nunca foi briefado — diga isso ao usuário.`,
90
+ ];
@@ -11,10 +11,13 @@ import type { NovaSkillInicial } from "./skill.js";
11
11
  * recebe nada: se alguem apagou uma delas, foi decisao — e recria-la a cada `start`
12
12
  * desfaria a decisao em silencio, toda vez.
13
13
  *
14
- * GERADO a partir dos SKILL.md em .claude/skills deste repositorio. Corrija la e
15
- * regenere; editar este arquivo a mao faz a correcao se perder na proxima geracao.
14
+ * GERADO por scripts/gera-skills-iniciais.mjs a partir dos SKILL.md em .claude/skills
15
+ * deste repositorio. Corrija la e regenere; editar este arquivo a mao faz a correcao se
16
+ * perder na proxima geracao.
16
17
  */
17
18
  export type SkillInicial = NovaSkillInicial & {
18
19
  ferramentas?: string[];
20
+ so_por_comando?: boolean;
21
+ dica_de_argumento?: string;
19
22
  };
20
23
  export declare const SKILLS_INICIAIS: SkillInicial[];
@@ -458,4 +458,121 @@ Se a decisão vier com uma razão que sobrevive ao dia de hoje — "escolhemos X
458
458
  fornecedor limita Y" —, isso pode ser memória. Passe pelos três filtros antes de gravar;
459
459
  "experimentamos e gostamos" não passa em nenhum deles.`,
460
460
  },
461
+ {
462
+ slug: "atualizar-harness",
463
+ descricao: `Põe um projeto que consome o dd-harness na versão atual — CLI, hooks e o bloco do protocolo no CLAUDE.md/AGENTS.md — sem tocar no que é do projeto. Invoque quando o usuário pedir para atualizar ou alinhar um projeto ao harness atual, passando o caminho da pasta. Só por pedido explícito: escreve em OUTRO repositório.`,
464
+ ferramentas: ["mcp__dd-harness__ler_changelog", "Read", "Glob", "Grep", "Edit", "Bash"],
465
+ so_por_comando: true,
466
+ dica_de_argumento: "<caminho da pasta do projeto>",
467
+ conteudo: `# Atualizar o harness de um projeto
468
+
469
+ Põe um projeto que consome o dd-harness na versão atual: CLI, hooks, e o bloco do
470
+ protocolo nos pontos de entrada (\`CLAUDE.md\` e \`AGENTS.md\`).
471
+
472
+ **Entrada esperada:** o caminho da pasta do projeto. Se o usuário não passou, pergunte —
473
+ não presuma que é o diretório atual, porque esta skill quase sempre roda apontando para
474
+ OUTRO projeto.
475
+
476
+ ---
477
+
478
+ ## 0. Antes de tudo: entenda o \`CLAUDE.md\` do alvo
479
+
480
+ **Este é o passo mais importante da skill, e o único que não dá para automatizar.** Faça-o
481
+ antes de rodar qualquer comando que escreva.
482
+
483
+ O \`CLAUDE.md\` **é do projeto, não do dd-harness**. Cada um escreve o seu: regras de
484
+ negócio, combinados da equipe, jeito de trabalhar daquele time. O harness ocupa ali um
485
+ bloco delimitado — e **só esse bloco é nosso**.
486
+
487
+ Leia o arquivo inteiro e responda, para você mesmo, três perguntas:
488
+
489
+ 1. **O que ali é do harness?** O bloco entre \`<!-- dd-harness:inicio ... -->\` e
490
+ \`<!-- dd-harness:fim -->\`. Se não houver marcas, o bloco é o trecho que fala de
491
+ \`ler_artefato\`/\`ler_roadmap\`/\`listar_skills\` — o protocolo que o molde escreve.
492
+
493
+ 2. **O que ali é do projeto?** Todo o resto. Regras próprias, seções inventadas pelo time,
494
+ uma linha que alguém acrescentou numa sexta-feira e não explicou. **Nada disso se toca.**
495
+
496
+ 3. **Alguém escreveu dentro do bloco?** É o caso que exige você. Uma regra do projeto pode
497
+ ter sido acrescentada lá dentro por conveniência — e ela tem que sobreviver à
498
+ atualização. Perceber isso é a diferença entre atualizar e destruir.
499
+
500
+ > Já aconteceu: uma linha escrita à mão pelo dono do projeto foi tratada como texto do
501
+ > molde e sobrescrita. Ele teve que dizer "é pq eu botei la". O arquivo é dele; a régua é
502
+ > essa.
503
+
504
+ **Regra que resume tudo:** na dúvida sobre se um trecho é nosso ou do projeto, **é do
505
+ projeto**. Pergunte em vez de decidir.
506
+
507
+ ---
508
+
509
+ ## 1. Diagnostique
510
+
511
+ \`\`\`
512
+ cd <caminho-do-projeto>
513
+ dd-harness atualizar
514
+ \`\`\`
515
+
516
+ Ele relata, sem escrever nada, quatro coisas: a versão do CLI, o estado do bloco em
517
+ \`CLAUDE.md\` e \`AGENTS.md\`, e os hooks. Cada item vem com uma ação:
518
+
519
+ | Marca | Significa |
520
+ |---|---|
521
+ | \`ok\` | em dia, nada a fazer |
522
+ | \`->\` | pode ser aplicado com segurança |
523
+ | \`??\` | **exige decisão sua** — arquivo editado a mão |
524
+ | \`!\` | manual (o CLI global é por máquina, não por projeto) |
525
+
526
+ ## 2. Descubra o que mudou
527
+
528
+ O bloco carimba a versão do molde (ex: \`v1.2.0\`). Compare com a atual e leia, no
529
+ \`CHANGELOG.md\` do dd-harness, o que mudou entre as duas. É isso que diz **o que** está
530
+ chegando ao projeto — e permite explicar ao usuário, em vez de só dizer "atualizei".
531
+
532
+ Se a diferença não mexe com o projeto, diga isso: atualização que não muda nada é
533
+ informação útil, não fracasso.
534
+
535
+ ## 3. Aplique o seguro
536
+
537
+ \`\`\`
538
+ dd-harness atualizar --aplicar
539
+ \`\`\`
540
+
541
+ Isto escreve **só** os itens \`->\`: troca o miolo do bloco, acrescenta hooks que faltam,
542
+ cria o arquivo que não existe. O que está fora do bloco não é lido nem reescrito.
543
+
544
+ Os itens \`??\` ficam intactos de propósito — o comando não os toca.
545
+
546
+ ## 4. Conduza os casos que exigem decisão
547
+
548
+ Para cada item \`??\`, faça o trabalho que o comando não pode fazer:
549
+
550
+ 1. **Mostre ao usuário o que está lá** e o que o molde novo traz.
551
+ 2. **Diga o que você acha que aconteceu** — "parece que você acrescentou esta linha
552
+ sobre o Slack dentro do nosso bloco".
553
+ 3. **Proponha o encaixe**, que quase sempre é: manter a linha dele, atualizar o resto, e
554
+ sugerir mover a linha dele para FORA do bloco, para não conflitar de novo na próxima
555
+ versão. Essa sugestão é o que impede o problema de se repetir.
556
+ 4. **Aguarde o OK.** Nunca edite um bloco \`??\` sem resposta explícita.
557
+
558
+ ## 5. O CLI global
559
+
560
+ Se o diagnóstico marcou o CLI com \`!\`, o comando é:
561
+
562
+ \`\`\`
563
+ npm i -g dd-harness@latest
564
+ \`\`\`
565
+
566
+ Diga ao usuário que isso vale para **a máquina**, não para o projeto — rodar em cada pasta
567
+ não muda nada, e ele só precisa fazer isso uma vez.
568
+
569
+ ## 6. Feche
570
+
571
+ Relate, em poucas linhas: o que foi atualizado, de que versão para qual, o que ficou
572
+ pendente de decisão e por quê. Se você tocou em \`CLAUDE.md\`, **diga explicitamente que o
573
+ que estava fora do bloco continua intacto** — é a garantia que o usuário quer ouvir.
574
+
575
+ Se o projeto tem git, sugira conferir o diff antes de commitar. Não commite: o projeto é
576
+ do usuário e as regras de commit dele são dele.`,
577
+ },
461
578
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-harness",
3
- "version": "0.32.1",
3
+ "version": "0.33.0",
4
4
  "type": "module",
5
5
  "description": "Política, memória e integrações de Claude Code, Codex e Antigravity. CLI sem dependências de runtime.",
6
6
  "license": "UNLICENSED",