dd-harness 0.10.0 → 0.12.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.
- package/dist/index.js +126 -57
- package/package.json +44 -44
package/dist/index.js
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { access } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
// DEP0190: Node imprime esse warning direto no stderr no instante em que
|
|
6
|
+
// `spawn(..., { shell: true })` recebe um array de args — de forma sincrona, ANTES de
|
|
7
|
+
// qualquer listener rodar, entao filtrar dentro de um `process.on("warning")` normal nao
|
|
8
|
+
// bloqueia a impressao (so o listener default e que imprime, e ele continua registrado
|
|
9
|
+
// junto do nosso). Por isso remover o listener default e a unica forma de suprimir.
|
|
10
|
+
// Real quando algum arg vem de fora, mas `instalaMcp` (mais abaixo) so passa literais
|
|
11
|
+
// fixos — sem isto o wizard de `start` mostrava um "DeprecationWarning" no meio do fluxo
|
|
12
|
+
// que parecia falha para quem esta vendo por dentro pela primeira vez.
|
|
13
|
+
process.removeAllListeners("warning");
|
|
5
14
|
import { termosDaConsulta } from "./argv.js";
|
|
6
15
|
import { check, status } from "./check.js";
|
|
7
16
|
import { CAMINHO_CONFIG, guardaConfigDaMaquina, guardaToken, leConfigDaMaquina, leConfigDoRepo, leToken, } from "./config.js";
|
|
@@ -24,55 +33,55 @@ import { subiuOWorker } from "./worker.js";
|
|
|
24
33
|
* em repositorio alheio tem que envelhecer bem, e cada dependencia e uma chance de nao
|
|
25
34
|
* envelhecer.
|
|
26
35
|
*/
|
|
27
|
-
const AJUDA = `dd-harness — a política e o Brain do projeto, no serviço
|
|
28
|
-
|
|
29
|
-
dd-harness start numa pasta vazia: conduz tudo (login,
|
|
30
|
-
espaço, projeto, config) numa tacada
|
|
31
|
-
dd-harness login --token <token> [--api <url>]
|
|
32
|
-
guarda a credencial desta máquina
|
|
33
|
-
dd-harness projeto <slug> --nome "<nome>" [--tenant <t>] [--api <url>]
|
|
34
|
-
cria o projeto no serviço (antes do init)
|
|
35
|
-
dd-harness init --tenant <t> --projeto <p> [--api <url>]
|
|
36
|
-
prepara o repositório (config + CLAUDE.md)
|
|
37
|
-
dd-harness pasta <slug> --definicao "o que entra e o que não entra"
|
|
38
|
-
cria a pasta que o gravar exige
|
|
39
|
-
dd-harness gravar <arquivo.md> registra uma memória a partir de um markdown
|
|
40
|
-
dd-harness editar <arquivo.md> corrige o que já está gravado
|
|
41
|
-
dd-harness arquivar <pasta>/<slug> --motivo <obsoleta|incorreta|fora_dos_filtros>
|
|
42
|
-
[--substituida-por <pasta>/<slug>]
|
|
43
|
-
tira de circulação sem apagar
|
|
44
|
-
dd-harness buscar "<pergunta>" acha memória por relevância, não por arquivo
|
|
45
|
-
dd-harness ler <pasta>/<slug> imprime a memória inteira, no formato de gravar
|
|
46
|
-
dd-harness reancorar <pasta>/<slug> --de "<alvo>" --para "<alvo>"
|
|
47
|
-
troca o alvo de uma âncora que mudou de lugar
|
|
48
|
-
dd-harness check [--commit <sha>] mede as âncoras e reporta a deriva
|
|
49
|
-
dd-harness status só lê: o tamanho do Brain e o que espera julgamento
|
|
50
|
-
dd-harness politica [--hook] imprime a política do serviço
|
|
51
|
-
saída 0 = veio; 3 = projeto sem política;
|
|
52
|
-
1 = não consegui buscar
|
|
53
|
-
--hook: fala o protocolo do SessionStart do
|
|
54
|
-
Claude Code, para pôr a política no contexto
|
|
55
|
-
dd-harness --help
|
|
56
|
-
dd-harness --version qual binário está instalado nesta máquina
|
|
57
|
-
|
|
58
|
-
Nada do dd-harness fica em disco: a política chega pelo hook de sessão, e a
|
|
59
|
-
memória pela busca, na hora.
|
|
36
|
+
const AJUDA = `dd-harness — a política e o Brain do projeto, no serviço
|
|
37
|
+
|
|
38
|
+
dd-harness start numa pasta vazia: conduz tudo (login,
|
|
39
|
+
espaço, projeto, config) numa tacada
|
|
40
|
+
dd-harness login --token <token> [--api <url>]
|
|
41
|
+
guarda a credencial desta máquina
|
|
42
|
+
dd-harness projeto <slug> --nome "<nome>" [--tenant <t>] [--api <url>]
|
|
43
|
+
cria o projeto no serviço (antes do init)
|
|
44
|
+
dd-harness init --tenant <t> --projeto <p> [--api <url>]
|
|
45
|
+
prepara o repositório (config + CLAUDE.md)
|
|
46
|
+
dd-harness pasta <slug> --definicao "o que entra e o que não entra"
|
|
47
|
+
cria a pasta que o gravar exige
|
|
48
|
+
dd-harness gravar <arquivo.md> registra uma memória a partir de um markdown
|
|
49
|
+
dd-harness editar <arquivo.md> corrige o que já está gravado
|
|
50
|
+
dd-harness arquivar <pasta>/<slug> --motivo <obsoleta|incorreta|fora_dos_filtros>
|
|
51
|
+
[--substituida-por <pasta>/<slug>]
|
|
52
|
+
tira de circulação sem apagar
|
|
53
|
+
dd-harness buscar "<pergunta>" acha memória por relevância, não por arquivo
|
|
54
|
+
dd-harness ler <pasta>/<slug> imprime a memória inteira, no formato de gravar
|
|
55
|
+
dd-harness reancorar <pasta>/<slug> --de "<alvo>" --para "<alvo>"
|
|
56
|
+
troca o alvo de uma âncora que mudou de lugar
|
|
57
|
+
dd-harness check [--commit <sha>] mede as âncoras e reporta a deriva
|
|
58
|
+
dd-harness status só lê: o tamanho do Brain e o que espera julgamento
|
|
59
|
+
dd-harness politica [--hook] imprime a política do serviço
|
|
60
|
+
saída 0 = veio; 3 = projeto sem política;
|
|
61
|
+
1 = não consegui buscar
|
|
62
|
+
--hook: fala o protocolo do SessionStart do
|
|
63
|
+
Claude Code, para pôr a política no contexto
|
|
64
|
+
dd-harness --help
|
|
65
|
+
dd-harness --version qual binário está instalado nesta máquina
|
|
66
|
+
|
|
67
|
+
Nada do dd-harness fica em disco: a política chega pelo hook de sessão, e a
|
|
68
|
+
memória pela busca, na hora.
|
|
60
69
|
`;
|
|
61
70
|
/**
|
|
62
71
|
* Os ganchos sao IMPRESSOS, nunca instalados. `.git/hooks` nao e versionado e nao e
|
|
63
72
|
* nosso: escrever la dentro sem a pessoa pedir e o mesmo tipo de invasao que
|
|
64
73
|
* sobrescrever o CLAUDE.md dela. Quem cola, decide.
|
|
65
74
|
*/
|
|
66
|
-
const GANCHOS = `
|
|
67
|
-
Opcional — o gancho que devolve a memória ao code review:
|
|
68
|
-
|
|
69
|
-
.git/hooks/post-commit (avisa quais memórias falam do que você mudou)
|
|
70
|
-
#!/bin/sh
|
|
71
|
-
dd-harness check --commit "$(git rev-parse HEAD)" || true
|
|
72
|
-
|
|
73
|
-
Termina em sucesso mesmo com deriva: avisa, não bloqueia.
|
|
74
|
-
|
|
75
|
-
O hook da política (\`dd-harness politica --hook\`) é outra coisa, e não é
|
|
75
|
+
const GANCHOS = `
|
|
76
|
+
Opcional — o gancho que devolve a memória ao code review:
|
|
77
|
+
|
|
78
|
+
.git/hooks/post-commit (avisa quais memórias falam do que você mudou)
|
|
79
|
+
#!/bin/sh
|
|
80
|
+
dd-harness check --commit "$(git rev-parse HEAD)" || true
|
|
81
|
+
|
|
82
|
+
Termina em sucesso mesmo com deriva: avisa, não bloqueia.
|
|
83
|
+
|
|
84
|
+
O hook da política (\`dd-harness politica --hook\`) é outra coisa, e não é
|
|
76
85
|
opcional — \`dd-harness init\` imprime a linha para o \`.claude/settings.json\`.`;
|
|
77
86
|
function argumento(argv, nome) {
|
|
78
87
|
const i = argv.indexOf(`--${nome}`);
|
|
@@ -87,14 +96,44 @@ const API_PADRAO = "https://dd-harness.vercel.app";
|
|
|
87
96
|
*/
|
|
88
97
|
function instalaMcp() {
|
|
89
98
|
return new Promise((resolve) => {
|
|
99
|
+
// stdio "inherit": o usuario ve o download do npx acontecendo (pode levar alguns
|
|
100
|
+
// segundos) e, se falhar, ve POR QUE — em vez do "nao consegui" mudo que nao dava
|
|
101
|
+
// pista nenhuma para diagnosticar. O DEP0190 que isto dispara e suprimido no topo
|
|
102
|
+
// do arquivo (`process.removeAllListeners("warning")`).
|
|
90
103
|
const p = spawn("npx", ["-y", "dd-harness-mcp", "--help"], {
|
|
91
104
|
shell: process.platform === "win32",
|
|
92
|
-
stdio: "
|
|
105
|
+
stdio: "inherit",
|
|
93
106
|
});
|
|
94
|
-
p.on("exit", (code) => resolve(code === 0));
|
|
95
|
-
p.on("error", () => resolve(false));
|
|
107
|
+
p.on("exit", (code) => resolve({ ok: code === 0, erro: code === 0 ? undefined : `código de saída ${code}` }));
|
|
108
|
+
p.on("error", (e) => resolve({ ok: false, erro: e.message }));
|
|
96
109
|
});
|
|
97
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* `start` e o primeiro comando de quem esta chegando — rodar desatualizado aqui e o
|
|
113
|
+
* peor lugar para isso: foi assim que a pre-instalacao do MCP (esta mesma funcao,
|
|
114
|
+
* mais abaixo) ficou muda numa versao anterior que nao tinha essa etapa ainda.
|
|
115
|
+
* Falha de rede aqui nunca trava o wizard — so segue sem avisar.
|
|
116
|
+
*/
|
|
117
|
+
async function versaoDesatualizada() {
|
|
118
|
+
try {
|
|
119
|
+
const atual = await versao();
|
|
120
|
+
const controle = new AbortController();
|
|
121
|
+
const corte = setTimeout(() => controle.abort(), 3000);
|
|
122
|
+
const resp = await fetch("https://registry.npmjs.org/dd-harness/latest", {
|
|
123
|
+
signal: controle.signal,
|
|
124
|
+
});
|
|
125
|
+
clearTimeout(corte);
|
|
126
|
+
if (!resp.ok)
|
|
127
|
+
return null;
|
|
128
|
+
const { version: ultima } = (await resp.json());
|
|
129
|
+
if (!ultima || ultima === atual)
|
|
130
|
+
return null;
|
|
131
|
+
return { atual, ultima };
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
98
137
|
/**
|
|
99
138
|
* Tenta abrir o navegador padrao. Nunca lanca — falhar em abrir nao pode travar o
|
|
100
139
|
* wizard, so degrada para "aqui esta a URL, abra voce mesmo".
|
|
@@ -112,6 +151,10 @@ function tentaAbrirNavegador(url) {
|
|
|
112
151
|
// Segue sem navegador — a URL impressa antes desta chamada ja resolve.
|
|
113
152
|
}
|
|
114
153
|
}
|
|
154
|
+
/** Token no terminal aparece mascarado: o prefixo basta para reconhecer, o segredo nao precisa ser lido em voz alta. */
|
|
155
|
+
function mascara(token) {
|
|
156
|
+
return token.length <= 16 ? "***" : `${token.slice(0, 12)}...${token.slice(-4)}`;
|
|
157
|
+
}
|
|
115
158
|
/**
|
|
116
159
|
* `dd-harness start` — o wizard para pasta vazia.
|
|
117
160
|
*
|
|
@@ -136,14 +179,37 @@ async function comandoStart() {
|
|
|
136
179
|
// ENOENT: exatamente o caso esperado, segue.
|
|
137
180
|
}
|
|
138
181
|
console.log("Iniciando o dd-harness nesta pasta.\n");
|
|
182
|
+
// 0. Versao. `start` e o comando de bootstrap: rodar velho aqui significa perder
|
|
183
|
+
// correcoes deste proprio wizard sem nenhum aviso (foi o que aconteceu com a
|
|
184
|
+
// pre-instalacao do MCP, passo 5.5 abaixo, numa versao anterior a ela existir).
|
|
185
|
+
const desatualizado = await versaoDesatualizada();
|
|
186
|
+
if (desatualizado) {
|
|
187
|
+
console.log(`AVISO: você está na versão ${desatualizado.atual}, e a mais recente é ` +
|
|
188
|
+
`${desatualizado.ultima}. Rode \`npm install -g dd-harness@latest\` e comece de ` +
|
|
189
|
+
"novo — start segue mesmo assim, mas pode faltar correção.\n");
|
|
190
|
+
}
|
|
139
191
|
// 1. Credencial. Sem ela nada do resto e possivel — nem listar tenant.
|
|
192
|
+
// O estado aparece SEMPRE, mesmo quando ja existe credencial: antes o passo era
|
|
193
|
+
// pulado em silencio, e quem rodava `start` com um token velho (ou de outra conta)
|
|
194
|
+
// nao tinha como perceber — o wizard so falhava depois, ao listar espacos.
|
|
140
195
|
let token = await leToken(API_PADRAO);
|
|
196
|
+
if (token) {
|
|
197
|
+
console.log(`Credencial: ${mascara(token)} (${API_PADRAO})`);
|
|
198
|
+
const troca = await pergunta("Usar esta credencial? [S/n]: ");
|
|
199
|
+
if (/^n/i.test(troca))
|
|
200
|
+
token = null;
|
|
201
|
+
}
|
|
141
202
|
if (!token) {
|
|
142
|
-
|
|
143
|
-
|
|
203
|
+
const url = `${API_PADRAO}/tokens`;
|
|
204
|
+
console.log(`\nCrie um token pessoal em ${url} — abrindo no navegador...`);
|
|
205
|
+
tentaAbrirNavegador(url);
|
|
206
|
+
token = await pergunta("Cole o token aqui: ");
|
|
207
|
+
if (!token)
|
|
208
|
+
throw new Error("sem token não dá para seguir: `start` fala com o serviço em todos os passos.");
|
|
144
209
|
await guardaToken(API_PADRAO, token);
|
|
145
|
-
console.log("Credencial guardada
|
|
210
|
+
console.log("Credencial guardada.");
|
|
146
211
|
}
|
|
212
|
+
console.log("");
|
|
147
213
|
// 2. Espaco (tenant). Criar um e ato de dono — nunca por aqui — mas ESCOLHER entre
|
|
148
214
|
// os que ja existem e so leitura, e e o que faltava para nao exigir o slug de memoria.
|
|
149
215
|
let tenants = await listaTenants(API_PADRAO, token);
|
|
@@ -155,18 +221,20 @@ async function comandoStart() {
|
|
|
155
221
|
tenants = await listaTenants(API_PADRAO, token);
|
|
156
222
|
}
|
|
157
223
|
const tenant = await escolha("\nEscolha um espaço:", tenants.map((t) => ({ rotulo: `${t.nome} (${t.slug})`, valor: t.slug })));
|
|
158
|
-
// 3. Projeto. Criar e subordinado a estar num espaco
|
|
224
|
+
// 3. Projeto. Criar e subordinado a estar num espaco - isso o CLI ja faz sozinho.
|
|
159
225
|
const nomeSugerido = process.cwd().split(/[\\/]/).pop() ?? "meu-projeto";
|
|
160
226
|
const nome = (await pergunta(`\nNome do projeto [${nomeSugerido}]: `)) || nomeSugerido;
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
227
|
+
// O slug sai do nome, sem perguntar: e identificador, nao escolha editorial, e quem
|
|
228
|
+
// esta comecando nao tem como saber que ele vira a chave do `.dd-harness.json`.
|
|
229
|
+
// Nome so com caracteres especiais (ex: "!!!") normaliza para string vazia - o
|
|
230
|
+
// fallback evita slug vazio, que o servidor recusaria com erro confuso.
|
|
231
|
+
const slugDoProjeto = nome
|
|
164
232
|
.toLowerCase()
|
|
165
233
|
.normalize("NFD")
|
|
166
234
|
.replace(/[̀-ͯ]/g, "")
|
|
167
235
|
.replace(/[^a-z0-9]+/g, "-")
|
|
168
236
|
.replace(/^-+|-+$/g, "") || "meu-projeto";
|
|
169
|
-
|
|
237
|
+
console.log(`Identificador: ${slugDoProjeto}`);
|
|
170
238
|
const resultadoProjeto = await criaProjeto(process.cwd(), slugDoProjeto, nome, {
|
|
171
239
|
tenant,
|
|
172
240
|
api: API_PADRAO,
|
|
@@ -204,11 +272,12 @@ async function comandoStart() {
|
|
|
204
272
|
}
|
|
205
273
|
if (mcp.ok) {
|
|
206
274
|
console.log("\nInstalando o servidor dd-harness-mcp (primeira vez pode demorar um pouco)...");
|
|
207
|
-
const instalou = await instalaMcp();
|
|
275
|
+
const { ok: instalou, erro: erroInstalacao } = await instalaMcp();
|
|
208
276
|
console.log(instalou
|
|
209
277
|
? "dd-harness-mcp instalado — a próxima sessão do Claude Code conecta na hora."
|
|
210
|
-
:
|
|
211
|
-
"Code pode demorar ou falhar; se falhar, abra uma nova sessão
|
|
278
|
+
: `AVISO: não consegui pré-instalar dd-harness-mcp (${erroInstalacao}). A primeira ` +
|
|
279
|
+
"conexão do Claude Code pode demorar ou falhar; se falhar, abra uma nova sessão " +
|
|
280
|
+
"e tente de novo.");
|
|
212
281
|
}
|
|
213
282
|
// 6. AGENTS.md — so quem usa outra ferramenta alem do Claude Code precisa.
|
|
214
283
|
const outraFerramenta = await pergunta("\nVai abrir este projeto em Codex, Cursor ou outra ferramenta além do Claude " +
|
package/package.json
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dd-harness",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Cliente do dd-harness: politica no inicio da sessao, e memoria por busca — nada em disco. Sem dependencia: fetch, crypto e fs sao do Node.",
|
|
6
|
-
"license": "UNLICENSED",
|
|
7
|
-
"author": "Diego Dias",
|
|
8
|
-
"keywords": [
|
|
9
|
-
"claude-code",
|
|
10
|
-
"ai-agents",
|
|
11
|
-
"memory",
|
|
12
|
-
"brain",
|
|
13
|
-
"cli"
|
|
14
|
-
],
|
|
15
|
-
"homepage": "https://dd-harness.vercel.app",
|
|
16
|
-
"repository": {
|
|
17
|
-
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/diegodias93/dd-harness-online.git",
|
|
19
|
-
"directory": "packages/cli"
|
|
20
|
-
},
|
|
21
|
-
"bin": {
|
|
22
|
-
"dd-harness": "dist/index.js"
|
|
23
|
-
},
|
|
24
|
-
"//exports": "Aponta para `dist` porque quem IMPORTA isto em tempo de execucao e o Node, que nao executa TypeScript. Nao aponte para `src`: o Node segue os imports relativos de dentro do arquivo e tenta abrir `./api.js` ao lado do `.ts`, que nao existe. Quem consome no workspace e o `packages/mcp`, e ele compila o fonte do CLI junto (ver o tsconfig.build.json dele) em vez de depender deste campo — assim o build funciona num checkout limpo, sem `dist` previo.",
|
|
25
|
-
"exports": {
|
|
26
|
-
"./api": "./dist/api.js",
|
|
27
|
-
"./buscar": "./dist/buscar.js",
|
|
28
|
-
"./curar": "./dist/curar.js",
|
|
29
|
-
"./gravar": "./dist/gravar.js",
|
|
30
|
-
"./pasta": "./dist/pasta.js",
|
|
31
|
-
"./projeto": "./dist/projeto.js"
|
|
32
|
-
},
|
|
33
|
-
"files": [
|
|
34
|
-
"dist"
|
|
35
|
-
],
|
|
36
|
-
"engines": {
|
|
37
|
-
"node": ">=20"
|
|
38
|
-
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"typecheck": "tsc -p . --noEmit",
|
|
41
|
-
"build": "tsc -p tsconfig.build.json",
|
|
42
|
-
"prepublishOnly": "npm run build"
|
|
43
|
-
}
|
|
44
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dd-harness",
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Cliente do dd-harness: politica no inicio da sessao, e memoria por busca — nada em disco. Sem dependencia: fetch, crypto e fs sao do Node.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"author": "Diego Dias",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"claude-code",
|
|
10
|
+
"ai-agents",
|
|
11
|
+
"memory",
|
|
12
|
+
"brain",
|
|
13
|
+
"cli"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://dd-harness.vercel.app",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/diegodias93/dd-harness-online.git",
|
|
19
|
+
"directory": "packages/cli"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"dd-harness": "dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"//exports": "Aponta para `dist` porque quem IMPORTA isto em tempo de execucao e o Node, que nao executa TypeScript. Nao aponte para `src`: o Node segue os imports relativos de dentro do arquivo e tenta abrir `./api.js` ao lado do `.ts`, que nao existe. Quem consome no workspace e o `packages/mcp`, e ele compila o fonte do CLI junto (ver o tsconfig.build.json dele) em vez de depender deste campo — assim o build funciona num checkout limpo, sem `dist` previo.",
|
|
25
|
+
"exports": {
|
|
26
|
+
"./api": "./dist/api.js",
|
|
27
|
+
"./buscar": "./dist/buscar.js",
|
|
28
|
+
"./curar": "./dist/curar.js",
|
|
29
|
+
"./gravar": "./dist/gravar.js",
|
|
30
|
+
"./pasta": "./dist/pasta.js",
|
|
31
|
+
"./projeto": "./dist/projeto.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"typecheck": "tsc -p . --noEmit",
|
|
41
|
+
"build": "tsc -p tsconfig.build.json",
|
|
42
|
+
"prepublishOnly": "npm run build"
|
|
43
|
+
}
|
|
44
|
+
}
|