dd-harness 0.37.0 → 0.38.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/README.md +103 -103
- package/dist/diagnostico.js +13 -3
- package/dist/escreve-config.js +24 -24
- package/dist/hosts.js +80 -4
- package/dist/index.js +169 -107
- package/dist/init.js +45 -45
- package/dist/materializa.js +0 -5
- package/dist/moldes-historicos.js +70 -70
- package/dist/pergunta.d.ts +9 -2
- package/dist/pergunta.js +71 -2
- package/dist/regras-de-commit.js +8 -8
- package/dist/skills-iniciais.js +606 -606
- package/dist/sync.js +1 -2
- package/package.json +44 -44
- package/dist/materializa.d.ts +0 -70
- package/dist/sync.d.ts +0 -59
package/README.md
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
# dd-harness
|
|
2
|
-
|
|
3
|
-
Política e briefing carregados do serviço; memória consultada por MCP ou CLI.
|
|
4
|
-
A fonte dos artefatos continua no serviço. O cliente mantém configuração, ponteiros
|
|
5
|
-
para skills e estado descartável fora do repositório.
|
|
6
|
-
|
|
7
|
-
Requer Node >=20.3.0. CLI sem dependências de runtime.
|
|
8
|
-
|
|
9
|
-
## Instalação e configuração
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
npm install -g dd-harness@latest
|
|
13
|
-
npx -y dd-harness-mcp@latest --help
|
|
14
|
-
dd-harness start --host todos
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Para um projeto já criado no serviço:
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
dd-harness init --tenant meu-espaco --projeto meu-projeto
|
|
21
|
-
dd-harness login --token <token>
|
|
22
|
-
dd-harness integrar --host claude,codex,antigravity
|
|
23
|
-
dd-harness diagnostico --mcp
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
`start` usa Claude por padrão. `--host` aceita `claude`, `codex`,
|
|
27
|
-
`antigravity`, uma lista separada por vírgulas ou `todos`.
|
|
28
|
-
`integrar` instala nos três por padrão e preserva configurações alheias.
|
|
29
|
-
O token nasce em /tokens e fica em ~/.dd-harness/credentials.json, por origem de API.
|
|
30
|
-
Para serviço local, use `init --api http://localhost:3000` antes do login.
|
|
31
|
-
|
|
32
|
-
## Hosts
|
|
33
|
-
|
|
34
|
-
| Host | MCP | Boot e guarda | Skills |
|
|
35
|
-
|---|---|---|---|
|
|
36
|
-
| Claude Code | .mcp.json | .claude/settings.json | .claude/skills |
|
|
37
|
-
| Codex | .codex/config.toml | .codex/hooks.json | .agents/skills |
|
|
38
|
-
| Antigravity | .agents/mcp_config.json | .agents/hooks.json | .agents/skills |
|
|
39
|
-
|
|
40
|
-
Codex exige revisão/confiança dos hooks em /hooks. Instalação não comprova ativação.
|
|
41
|
-
Antigravity usa PreInvocation e mensagem efêmera: política e briefing são consultados
|
|
42
|
-
antes de cada inferência. Sua guarda usa `ask` para respeitar concessões já existentes;
|
|
43
|
-
ela pode acrescentar confirmações. Não amplia permissões automaticamente.
|
|
44
|
-
|
|
45
|
-
Sem boot válido, a guarda recusa ferramentas cobertas, inclusive shell. Leitura conhecida
|
|
46
|
-
e onboarding controlado continuam possíveis. Os hooks precisam estar ativos: não são
|
|
47
|
-
sandbox nem controlam ferramentas que o aplicativo não encaminha a eles.
|
|
48
|
-
Após boot válido, shell não tem análise de diff antecipada; avisos por âncora cobrem
|
|
49
|
-
edições estruturadas, patches, remoções e renomeações.
|
|
50
|
-
|
|
51
|
-
Configurações MCP novas levam DD_HARNESS_ROOT explícito. Ao copiar/mover um checkout,
|
|
52
|
-
confira essa raiz com diagnostico. O MCP recusa divergência entre a raiz explícita e
|
|
53
|
-
o projeto identificado no diretório de execução. Corrija a configuração e reinicie o host.
|
|
54
|
-
|
|
55
|
-
## Uso diário
|
|
56
|
-
|
|
57
|
-
```sh
|
|
58
|
-
dd-harness politica
|
|
59
|
-
dd-harness buscar "contrato de integração"
|
|
60
|
-
dd-harness ler regras/contrato
|
|
61
|
-
dd-harness gravar memoria.md
|
|
62
|
-
dd-harness editar memoria.md
|
|
63
|
-
dd-harness arquivar regras/contrato --motivo obsoleta
|
|
64
|
-
dd-harness status
|
|
65
|
-
dd-harness skills
|
|
66
|
-
dd-harness roadmap
|
|
67
|
-
dd-harness changelog
|
|
68
|
-
dd-harness --help
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
`check` mede e grava observações de deriva; `status` só consulta.
|
|
72
|
-
O antigo comando `sync` não faz parte do CLI atual.
|
|
73
|
-
`politica --hook` e `cinto` permanecem como compatibilidade legada;
|
|
74
|
-
use `integrar` para instalar `sessao` e `guarda`.
|
|
75
|
-
|
|
76
|
-
## Estado e conflitos
|
|
77
|
-
|
|
78
|
-
- Política/briefing não são materializados. Âncoras/resumos, sessão e manifestos ficam
|
|
79
|
-
em ~/.dd-harness/repos, isolados por caminho/projeto/credencial quando aplicável.
|
|
80
|
-
- DD_HARNESS_HOME permite isolamento explícito em testes. Nunca aponte testes ao estado pessoal.
|
|
81
|
-
- Uma sessão validada expira em quatro horas. Boot, retomada e compactação revalidam.
|
|
82
|
-
Alterações locais de política/briefing via MCP invalidam a sessão; reabra depois de editar.
|
|
83
|
-
Alterações remotas feitas por outro cliente são percebidas no próximo boot/revalidação.
|
|
84
|
-
- Curadoria invalida as âncoras do checkout atual. A próxima edição as consulta novamente.
|
|
85
|
-
- Só ponteiros registrados e intactos são atualizados/removidos. Skills manuais, editadas
|
|
86
|
-
ou redirecionadas por links são preservadas e aparecem como conflitos.
|
|
87
|
-
- Skills marcadas só por comando não são instaladas para descoberta automática em
|
|
88
|
-
.agents; use listar_skills/ler_skill após pedido explícito. Metadados próprios do
|
|
89
|
-
Claude não são prometidos como portáveis.
|
|
90
|
-
- Se há fila e worker local configurado, o boot solicita um lote. O log fica em
|
|
91
|
-
~/.dd-harness/worker.log; solicitar não significa que a indexação concluiu.
|
|
92
|
-
|
|
93
|
-
## Escritas e commits
|
|
94
|
-
|
|
95
|
-
Só GET/HEAD têm retry automático (até três tentativas, timeout por tentativa).
|
|
96
|
-
POST/PATCH/PUT/DELETE não são repetidos. Falha de conexão, 5xx ou resposta incompleta
|
|
97
|
-
em escrita exige consultar o estado antes de tentar novamente.
|
|
98
|
-
|
|
99
|
-
A política entregue exige `[IDENTIFICAÇÃO] - tipo: descrição` nos commits de agentes:
|
|
100
|
-
`[CODEX] - feat: ...`, `[CLAUDE] - fix: ...`, `[GEMINI] - docs: ...`.
|
|
101
|
-
Identifique quem realmente commitou, inclusive ao usar outro editor. A regra não altera
|
|
102
|
-
Git author nem concede autorização de commit/push. É regra de trabalho do agente,
|
|
103
|
-
não inferência automática de identidade nem hook que renomeia commits humanos.
|
|
1
|
+
# dd-harness
|
|
2
|
+
|
|
3
|
+
Política e briefing carregados do serviço; memória consultada por MCP ou CLI.
|
|
4
|
+
A fonte dos artefatos continua no serviço. O cliente mantém configuração, ponteiros
|
|
5
|
+
para skills e estado descartável fora do repositório.
|
|
6
|
+
|
|
7
|
+
Requer Node >=20.3.0. CLI sem dependências de runtime.
|
|
8
|
+
|
|
9
|
+
## Instalação e configuração
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install -g dd-harness@latest
|
|
13
|
+
npx -y dd-harness-mcp@latest --help
|
|
14
|
+
dd-harness start --host todos
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Para um projeto já criado no serviço:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
dd-harness init --tenant meu-espaco --projeto meu-projeto
|
|
21
|
+
dd-harness login --token <token>
|
|
22
|
+
dd-harness integrar --host claude,codex,antigravity
|
|
23
|
+
dd-harness diagnostico --mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`start` usa Claude por padrão. `--host` aceita `claude`, `codex`,
|
|
27
|
+
`antigravity`, uma lista separada por vírgulas ou `todos`.
|
|
28
|
+
`integrar` instala nos três por padrão e preserva configurações alheias.
|
|
29
|
+
O token nasce em /tokens e fica em ~/.dd-harness/credentials.json, por origem de API.
|
|
30
|
+
Para serviço local, use `init --api http://localhost:3000` antes do login.
|
|
31
|
+
|
|
32
|
+
## Hosts
|
|
33
|
+
|
|
34
|
+
| Host | MCP | Boot e guarda | Skills |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| Claude Code | .mcp.json | .claude/settings.json | .claude/skills |
|
|
37
|
+
| Codex | .codex/config.toml | .codex/hooks.json | .agents/skills |
|
|
38
|
+
| Antigravity | .agents/mcp_config.json | .agents/hooks.json | .agents/skills |
|
|
39
|
+
|
|
40
|
+
Codex exige revisão/confiança dos hooks em /hooks. Instalação não comprova ativação.
|
|
41
|
+
Antigravity usa PreInvocation e mensagem efêmera: política e briefing são consultados
|
|
42
|
+
antes de cada inferência. Sua guarda usa `ask` para respeitar concessões já existentes;
|
|
43
|
+
ela pode acrescentar confirmações. Não amplia permissões automaticamente.
|
|
44
|
+
|
|
45
|
+
Sem boot válido, a guarda recusa ferramentas cobertas, inclusive shell. Leitura conhecida
|
|
46
|
+
e onboarding controlado continuam possíveis. Os hooks precisam estar ativos: não são
|
|
47
|
+
sandbox nem controlam ferramentas que o aplicativo não encaminha a eles.
|
|
48
|
+
Após boot válido, shell não tem análise de diff antecipada; avisos por âncora cobrem
|
|
49
|
+
edições estruturadas, patches, remoções e renomeações.
|
|
50
|
+
|
|
51
|
+
Configurações MCP novas levam DD_HARNESS_ROOT explícito. Ao copiar/mover um checkout,
|
|
52
|
+
confira essa raiz com diagnostico. O MCP recusa divergência entre a raiz explícita e
|
|
53
|
+
o projeto identificado no diretório de execução. Corrija a configuração e reinicie o host.
|
|
54
|
+
|
|
55
|
+
## Uso diário
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
dd-harness politica
|
|
59
|
+
dd-harness buscar "contrato de integração"
|
|
60
|
+
dd-harness ler regras/contrato
|
|
61
|
+
dd-harness gravar memoria.md
|
|
62
|
+
dd-harness editar memoria.md
|
|
63
|
+
dd-harness arquivar regras/contrato --motivo obsoleta
|
|
64
|
+
dd-harness status
|
|
65
|
+
dd-harness skills
|
|
66
|
+
dd-harness roadmap
|
|
67
|
+
dd-harness changelog
|
|
68
|
+
dd-harness --help
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`check` mede e grava observações de deriva; `status` só consulta.
|
|
72
|
+
O antigo comando `sync` não faz parte do CLI atual.
|
|
73
|
+
`politica --hook` e `cinto` permanecem como compatibilidade legada;
|
|
74
|
+
use `integrar` para instalar `sessao` e `guarda`.
|
|
75
|
+
|
|
76
|
+
## Estado e conflitos
|
|
77
|
+
|
|
78
|
+
- Política/briefing não são materializados. Âncoras/resumos, sessão e manifestos ficam
|
|
79
|
+
em ~/.dd-harness/repos, isolados por caminho/projeto/credencial quando aplicável.
|
|
80
|
+
- DD_HARNESS_HOME permite isolamento explícito em testes. Nunca aponte testes ao estado pessoal.
|
|
81
|
+
- Uma sessão validada expira em quatro horas. Boot, retomada e compactação revalidam.
|
|
82
|
+
Alterações locais de política/briefing via MCP invalidam a sessão; reabra depois de editar.
|
|
83
|
+
Alterações remotas feitas por outro cliente são percebidas no próximo boot/revalidação.
|
|
84
|
+
- Curadoria invalida as âncoras do checkout atual. A próxima edição as consulta novamente.
|
|
85
|
+
- Só ponteiros registrados e intactos são atualizados/removidos. Skills manuais, editadas
|
|
86
|
+
ou redirecionadas por links são preservadas e aparecem como conflitos.
|
|
87
|
+
- Skills marcadas só por comando não são instaladas para descoberta automática em
|
|
88
|
+
.agents; use listar_skills/ler_skill após pedido explícito. Metadados próprios do
|
|
89
|
+
Claude não são prometidos como portáveis.
|
|
90
|
+
- Se há fila e worker local configurado, o boot solicita um lote. O log fica em
|
|
91
|
+
~/.dd-harness/worker.log; solicitar não significa que a indexação concluiu.
|
|
92
|
+
|
|
93
|
+
## Escritas e commits
|
|
94
|
+
|
|
95
|
+
Só GET/HEAD têm retry automático (até três tentativas, timeout por tentativa).
|
|
96
|
+
POST/PATCH/PUT/DELETE não são repetidos. Falha de conexão, 5xx ou resposta incompleta
|
|
97
|
+
em escrita exige consultar o estado antes de tentar novamente.
|
|
98
|
+
|
|
99
|
+
A política entregue exige `[IDENTIFICAÇÃO] - tipo: descrição` nos commits de agentes:
|
|
100
|
+
`[CODEX] - feat: ...`, `[CLAUDE] - fix: ...`, `[GEMINI] - docs: ...`.
|
|
101
|
+
Identifique quem realmente commitou, inclusive ao usar outro editor. A regra não altera
|
|
102
|
+
Git author nem concede autorização de commit/push. É regra de trabalho do agente,
|
|
103
|
+
não inferência automática de identidade nem hook que renomeia commits humanos.
|
package/dist/diagnostico.js
CHANGED
|
@@ -5,9 +5,14 @@ import { achaRaiz, leConfigDoRepo, leToken } from "./config.js";
|
|
|
5
5
|
import { buscaPolitica } from "./politica.js";
|
|
6
6
|
export async function handshakeMcp(raiz, comando = "npx", args = ["-y", "dd-harness-mcp@latest"]) {
|
|
7
7
|
return new Promise((resolve, reject) => {
|
|
8
|
+
// `DD_HARNESS_ROOT` fica FORA: passa-la aqui testaria um cenario que o host nao
|
|
9
|
+
// reproduz, e esconderia justamente a falha procurada — com a raiz explicita correta o
|
|
10
|
+
// servidor sobe ate quando a declaracao versionada esta quebrada para outra maquina.
|
|
11
|
+
// O cwd e a raiz, que e como o host lanca.
|
|
12
|
+
const { DD_HARNESS_ROOT: _ignorado, ...ambiente } = process.env;
|
|
8
13
|
const filho = spawn(comando, args, { cwd: raiz, windowsHide: true,
|
|
9
14
|
shell: process.platform === "win32" && comando === "npx", stdio: ["pipe", "pipe", "pipe"],
|
|
10
|
-
env:
|
|
15
|
+
env: ambiente });
|
|
11
16
|
let buffer = "", terminou = false, versao = "desconhecida";
|
|
12
17
|
const fim = (erro, resultado) => {
|
|
13
18
|
if (terminou)
|
|
@@ -79,8 +84,13 @@ export async function diagnostico(inicio, testarMcp = false) {
|
|
|
79
84
|
const h = await readFile(join(raiz, hook), "utf8").catch(() => "");
|
|
80
85
|
linhas.push(`${host}: MCP ${m.includes("dd-harness") ? "declarado" : "ausente"}; ` +
|
|
81
86
|
`hooks ${h.includes("dd-harness guarda") && h.includes("dd-harness sessao") ? "declarados" : "ausentes/legados"}; ativação no host requer verificação.`);
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
// Invertido de proposito: a raiz explicita era exigida, e passou a ser o defeito. O
|
|
88
|
+
// arquivo vai para o git, entao um caminho absoluto so vale na maquina que gerou —
|
|
89
|
+
// noutro checkout o servidor lanca antes do handshake e o host so ve conexao fechada.
|
|
90
|
+
if (m.includes("dd-harness") && m.includes("DD_HARNESS_ROOT")) {
|
|
91
|
+
linhas.push(`${host}: DD_HARNESS_ROOT declarado em ${config} — remova. ` +
|
|
92
|
+
`Caminho absoluto em arquivo versionado quebra em outro checkout; sem ele a raiz sai do cwd.`);
|
|
93
|
+
}
|
|
84
94
|
}
|
|
85
95
|
const politica = await buscaPolitica(raiz);
|
|
86
96
|
linhas.push(`Contexto: ${politica.estado}${politica.estado === "inalcancavel" ? " — " + politica.motivo : ""}`);
|
package/dist/escreve-config.js
CHANGED
|
@@ -140,28 +140,28 @@ export const escreveAgents = (raiz) => escrevePonteiro(raiz, "AGENTS.md");
|
|
|
140
140
|
* em producao compara para saber se precisa de atualizacao.
|
|
141
141
|
*/
|
|
142
142
|
export const VERSAO_DO_MOLDE = "v1.3.0";
|
|
143
|
-
export const SUGESTAO_AGENTS = `## Protocolo do dd-harness
|
|
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
|
-
|
|
155
|
-
Leia também o briefing com a mesma ferramenta (tipo briefing). Os dois são obrigatórios.
|
|
156
|
-
|
|
157
|
-
Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
|
|
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
|
|
143
|
+
export const SUGESTAO_AGENTS = `## Protocolo do dd-harness
|
|
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
|
+
|
|
155
|
+
Leia também o briefing com a mesma ferramenta (tipo briefing). Os dois são obrigatórios.
|
|
156
|
+
|
|
157
|
+
Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
|
|
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
|
|
167
167
|
e quando.`;
|
package/dist/hosts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { access, readFile, mkdir } from "node:fs/promises";
|
|
2
3
|
import { dirname, join, resolve } from "node:path";
|
|
3
4
|
import { escreveAtomico, estadoDoRepo } from "./estado-local.js";
|
|
4
5
|
import { escrevePonteiro, SUGESTAO_AGENTS } from "./escreve-config.js";
|
|
@@ -48,11 +49,84 @@ function grupo(doc, evento, command, antigos = []) {
|
|
|
48
49
|
preservados.push({ hooks: [{ type: "command", command, timeout: 60 }] });
|
|
49
50
|
doc[evento] = preservados;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Onde o `dd-harness-mcp` instalado guarda o arquivo de entrada, ou `null` se nao houver.
|
|
54
|
+
*
|
|
55
|
+
* O caminho sai do `bin` do proprio pacote, nao de um literal daqui: ele ja mudou uma vez
|
|
56
|
+
* (`dist/mcp/src/`, porque o build abrange dois diretorios) e um literal duplicado
|
|
57
|
+
* envelheceria em silencio — apontando para um arquivo que nao existe, o que o host
|
|
58
|
+
* reporta como CONNECTION_CLOSED sem dizer mais nada.
|
|
59
|
+
*/
|
|
60
|
+
async function entradaDoMcp() {
|
|
61
|
+
// `devolve` e nao `resolve`: o `resolve` de `node:path` esta no escopo deste modulo, e
|
|
62
|
+
// o parametro o sombrearia dentro do callback.
|
|
63
|
+
const raizGlobal = await new Promise(devolve => {
|
|
64
|
+
execFile("npm", ["root", "-g"], { shell: process.platform === "win32" }, (erro, saida) => devolve(erro ? "" : saida.trim()));
|
|
65
|
+
});
|
|
66
|
+
if (!raizGlobal)
|
|
67
|
+
return null;
|
|
68
|
+
const pasta = join(raizGlobal, "dd-harness-mcp");
|
|
69
|
+
try {
|
|
70
|
+
const { bin } = JSON.parse(await readFile(join(pasta, "package.json"), "utf8"));
|
|
71
|
+
const relativo = typeof bin === "string" ? bin : bin?.["dd-harness-mcp"];
|
|
72
|
+
if (!relativo)
|
|
73
|
+
return null;
|
|
74
|
+
const entrada = join(pasta, relativo);
|
|
75
|
+
await access(entrada);
|
|
76
|
+
return entrada;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Config de MCP e artefato de MAQUINA, nao de projeto: o bloco carrega o caminho do node
|
|
84
|
+
* e do pacote instalado ali. Versionar isso entrega a um clone o valor de quem gerou, e o
|
|
85
|
+
* sintoma no outro lado e sempre o mesmo CONNECTION_CLOSED — que nao diz nada sobre a
|
|
86
|
+
* causa. Quem clona roda `integrar` e recebe o seu.
|
|
87
|
+
*
|
|
88
|
+
* Acrescenta ao que ja existir, sem reescrever: `.gitignore` e arquivo do projeto.
|
|
89
|
+
*/
|
|
90
|
+
async function ignoraConfigDeMaquina(raiz, caminhos) {
|
|
91
|
+
const arquivo = join(raiz, ".gitignore");
|
|
92
|
+
const atual = await readFile(arquivo, "utf8").catch((e) => {
|
|
93
|
+
if (e.code === "ENOENT")
|
|
94
|
+
return "";
|
|
95
|
+
throw e;
|
|
96
|
+
});
|
|
97
|
+
const linhas = new Set(atual.split(/\r?\n/).map(l => l.trim()));
|
|
98
|
+
const faltam = caminhos.filter(c => !linhas.has(c));
|
|
99
|
+
if (!faltam.length)
|
|
100
|
+
return;
|
|
101
|
+
const bloco = "# dd-harness: config de MCP e por maquina — rode `dd-harness integrar`\n" + faltam.join("\n") + "\n";
|
|
102
|
+
await escreveAtomico(arquivo, atual && !atual.endsWith("\n") ? `${atual}\n${bloco}` : atual + bloco);
|
|
103
|
+
}
|
|
51
104
|
/** Cada host usa seu formato. Configuração alheia não é inferida como gerenciada. */
|
|
52
105
|
export async function instalaHosts(raiz, hosts) {
|
|
53
106
|
raiz = resolve(raiz);
|
|
54
107
|
const avisos = [];
|
|
55
|
-
|
|
108
|
+
// `npx` NAO serve no Windows, e nao ha variante que sirva: o host lanca o servidor com
|
|
109
|
+
// `spawn` sem shell, e ali `npx` da ENOENT (nao existe executavel com esse nome) e
|
|
110
|
+
// `npx.cmd` da EINVAL (o Node recusa .bat/.cmd sem shell desde a CVE-2024-27980). Os
|
|
111
|
+
// dois chegam ao cliente como CONNECTION_CLOSED, que parece servidor morrendo e nao
|
|
112
|
+
// comando inalcancavel. Apontar o proprio node para o arquivo e o que sobra — e e o que
|
|
113
|
+
// todo MCP que funciona nesta plataforma ja faz.
|
|
114
|
+
//
|
|
115
|
+
// Gravar caminho de maquina aqui so e legitimo porque estes arquivos sao de maquina: o
|
|
116
|
+
// `integrar` os poe no .gitignore, e quem clona roda `integrar` e recebe o seu. Num
|
|
117
|
+
// arquivo versionado seria o mesmo erro que DD_HARNESS_ROOT ja foi.
|
|
118
|
+
//
|
|
119
|
+
// Sem DD_HARNESS_ROOT: `raizDoMcp` resolve a raiz pelo cwd, que e onde o host lanca o
|
|
120
|
+
// servidor. Com a variavel, um caminho de outro checkout o faz lancar antes do
|
|
121
|
+
// handshake — mesmo CONNECTION_CLOSED, outra causa.
|
|
122
|
+
const entrada = await entradaDoMcp();
|
|
123
|
+
const mcp = entrada
|
|
124
|
+
? { command: process.execPath, args: [entrada] }
|
|
125
|
+
: { command: "npx", args: ["-y", "dd-harness-mcp@latest"] };
|
|
126
|
+
if (!entrada && process.platform === "win32") {
|
|
127
|
+
avisos.push("MCP declarado via npx, que nao sobe no Windows: instale com " +
|
|
128
|
+
"`npm i -g dd-harness-mcp@latest` e rode `dd-harness integrar` de novo.");
|
|
129
|
+
}
|
|
56
130
|
for (const host of hosts) {
|
|
57
131
|
if (host === "codex") {
|
|
58
132
|
const caminho = join(raiz, ".codex", "config.toml");
|
|
@@ -62,10 +136,11 @@ export async function instalaHosts(raiz, hosts) {
|
|
|
62
136
|
throw e;
|
|
63
137
|
});
|
|
64
138
|
if (atual.includes("dd-harness")) {
|
|
65
|
-
avisos.push("Codex: servidor já configurado; preservado. Confira command
|
|
139
|
+
avisos.push("Codex: servidor já configurado; preservado. Confira o command com diagnostico.");
|
|
66
140
|
}
|
|
67
141
|
else {
|
|
68
|
-
|
|
142
|
+
const args = mcp.args.map(a => JSON.stringify(a)).join(", ");
|
|
143
|
+
await escreveAtomico(caminho, atual + `\n[mcp_servers.dd-harness]\ncommand = ${JSON.stringify(mcp.command)}\nargs = [${args}]\nstartup_timeout_sec = 60\n`);
|
|
69
144
|
}
|
|
70
145
|
await json(join(raiz, ".codex", "hooks.json"), doc => {
|
|
71
146
|
const h = objeto(doc.hooks);
|
|
@@ -120,6 +195,7 @@ export async function instalaHosts(raiz, hosts) {
|
|
|
120
195
|
}
|
|
121
196
|
}
|
|
122
197
|
}
|
|
198
|
+
await ignoraConfigDeMaquina(raiz, hosts.map(h => h === "claude" ? ".mcp.json" : h === "codex" ? ".codex/config.toml" : ".agents/mcp_config.json"));
|
|
123
199
|
await mkdir(dirname(join(raiz, "AGENTS.md")), { recursive: true });
|
|
124
200
|
await escrevePonteiro(raiz, "AGENTS.md");
|
|
125
201
|
if (hosts.includes("claude"))
|