dd-harness 0.28.0 → 0.29.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/git.d.ts +37 -0
- package/dist/git.js +67 -0
- package/dist/index.js +47 -5
- package/package.json +1 -1
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O repositorio git do projeto — criado pelo `start` quando ainda nao existe.
|
|
3
|
+
*
|
|
4
|
+
* Sem git, metade do dd-harness fica muda e ninguem avisa: `check --commit` nao tem
|
|
5
|
+
* commit para cruzar, `sugerir_ancoras` devolve lista vazia (a rodada 006 bateu nisso), e
|
|
6
|
+
* o gancho `post-commit` nao tem onde morar. Nada disso falha com erro — tudo degrada em
|
|
7
|
+
* silencio, que e a classe de defeito que este projeto existe para combater.
|
|
8
|
+
*
|
|
9
|
+
* Criar e barato e reversivel (`rm -rf .git`); nao criar custa features que a pessoa nem
|
|
10
|
+
* descobre que tem.
|
|
11
|
+
*/
|
|
12
|
+
export type EstadoDoGit = "ja-era" | "criado" | "git-indisponivel" | "falhou";
|
|
13
|
+
/** Ja e um repositorio? Responde pelo proprio git, nao pela existencia de `.git` — submodulo e worktree tem `.git` como ARQUIVO, e checar a pasta erraria nos dois. */
|
|
14
|
+
export declare function ehRepositorio(raiz: string): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Cria o repositorio, se ainda nao houver.
|
|
17
|
+
*
|
|
18
|
+
* Nao faz commit inicial: o que commitar e decisao de quem esta comecando o projeto, e um
|
|
19
|
+
* commit automatico com os arquivos que o `start` acabou de escrever criaria uma historia
|
|
20
|
+
* que ninguem pediu — inclusive num repositorio que a pessoa talvez queira que comece
|
|
21
|
+
* vazio.
|
|
22
|
+
*
|
|
23
|
+
* Nunca lanca. `start` nao pode falhar porque o git nao esta instalado.
|
|
24
|
+
*/
|
|
25
|
+
export declare function iniciaRepositorio(raiz: string): Promise<EstadoDoGit>;
|
|
26
|
+
/**
|
|
27
|
+
* Escreve o gancho `post-commit`, que devolve a memoria ao code review.
|
|
28
|
+
*
|
|
29
|
+
* Ate aqui ele era IMPRESSO para a pessoa colar, pela regra de que `.git/hooks` nao e
|
|
30
|
+
* nosso. A regra continua valendo para repositorio que ja existia — mas num que o
|
|
31
|
+
* proprio `start` acabou de criar nao ha nada de ninguem para preservar, e imprimir
|
|
32
|
+
* instrucao que so vale se alguem copiar e colar e o mesmo "rode X depois" que a rodada
|
|
33
|
+
* 006 mostrou que ninguem segue.
|
|
34
|
+
*
|
|
35
|
+
* Nunca sobrescreve um gancho existente: ali ha trabalho de outra pessoa.
|
|
36
|
+
*/
|
|
37
|
+
export declare function escreveGanchoDeCommit(raiz: string): Promise<"criado" | "ja-tinha" | "falhou">;
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const roda = promisify(execFile);
|
|
4
|
+
/** Ja e um repositorio? Responde pelo proprio git, nao pela existencia de `.git` — submodulo e worktree tem `.git` como ARQUIVO, e checar a pasta erraria nos dois. */
|
|
5
|
+
export async function ehRepositorio(raiz) {
|
|
6
|
+
try {
|
|
7
|
+
const { stdout } = await roda("git", ["rev-parse", "--is-inside-work-tree"], { cwd: raiz });
|
|
8
|
+
return stdout.trim() === "true";
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Cria o repositorio, se ainda nao houver.
|
|
16
|
+
*
|
|
17
|
+
* Nao faz commit inicial: o que commitar e decisao de quem esta comecando o projeto, e um
|
|
18
|
+
* commit automatico com os arquivos que o `start` acabou de escrever criaria uma historia
|
|
19
|
+
* que ninguem pediu — inclusive num repositorio que a pessoa talvez queira que comece
|
|
20
|
+
* vazio.
|
|
21
|
+
*
|
|
22
|
+
* Nunca lanca. `start` nao pode falhar porque o git nao esta instalado.
|
|
23
|
+
*/
|
|
24
|
+
export async function iniciaRepositorio(raiz) {
|
|
25
|
+
if (await ehRepositorio(raiz))
|
|
26
|
+
return "ja-era";
|
|
27
|
+
try {
|
|
28
|
+
await roda("git", ["init"], { cwd: raiz });
|
|
29
|
+
return "criado";
|
|
30
|
+
}
|
|
31
|
+
catch (erro) {
|
|
32
|
+
return erro.code === "ENOENT" ? "git-indisponivel" : "falhou";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Escreve o gancho `post-commit`, que devolve a memoria ao code review.
|
|
37
|
+
*
|
|
38
|
+
* Ate aqui ele era IMPRESSO para a pessoa colar, pela regra de que `.git/hooks` nao e
|
|
39
|
+
* nosso. A regra continua valendo para repositorio que ja existia — mas num que o
|
|
40
|
+
* proprio `start` acabou de criar nao ha nada de ninguem para preservar, e imprimir
|
|
41
|
+
* instrucao que so vale se alguem copiar e colar e o mesmo "rode X depois" que a rodada
|
|
42
|
+
* 006 mostrou que ninguem segue.
|
|
43
|
+
*
|
|
44
|
+
* Nunca sobrescreve um gancho existente: ali ha trabalho de outra pessoa.
|
|
45
|
+
*/
|
|
46
|
+
export async function escreveGanchoDeCommit(raiz) {
|
|
47
|
+
const { join } = await import("node:path");
|
|
48
|
+
const { readFile, writeFile, chmod } = await import("node:fs/promises");
|
|
49
|
+
const caminho = join(raiz, ".git", "hooks", "post-commit");
|
|
50
|
+
try {
|
|
51
|
+
await readFile(caminho, "utf8");
|
|
52
|
+
return "ja-tinha";
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Sem gancho: escreve.
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
// `|| true` porque aviso nao pode falhar um commit que ja aconteceu — o gancho roda
|
|
59
|
+
// DEPOIS, e sair diferente de zero aqui so polui a saida de quem commitou.
|
|
60
|
+
await writeFile(caminho, '#!/bin/sh\n# dd-harness: avisa quais memórias falam do que este commit mudou.\ndd-harness check --commit "$(git rev-parse HEAD)" || true\n', "utf8");
|
|
61
|
+
await chmod(caminho, 0o755);
|
|
62
|
+
return "criado";
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return "falhou";
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { termosDaConsulta } from "./argv.js";
|
|
|
15
15
|
import { check, status } from "./check.js";
|
|
16
16
|
import { CAMINHO_CONFIG, guardaConfigDaMaquina, guardaToken, leConfigDaMaquina, leConfigDoRepo, leToken, } from "./config.js";
|
|
17
17
|
import { escreveHook, escreveMcp, escrevePonteiro } from "./escreve-config.js";
|
|
18
|
+
import { escreveGanchoDeCommit, iniciaRepositorio } from "./git.js";
|
|
18
19
|
import { grava } from "./gravar.js";
|
|
19
20
|
import { init, SUGESTAO_AGENTS, SUGESTAO_HOOK, SUGESTAO_MCP } from "./init.js";
|
|
20
21
|
import { pergunta, escolha, fechaPerguntas } from "./pergunta.js";
|
|
@@ -426,6 +427,28 @@ async function comandoStart() {
|
|
|
426
427
|
console.log(resultadoProjeto.jaExistia
|
|
427
428
|
? `\nProjeto ${resultadoProjeto.projeto} já existia — usando ele.`
|
|
428
429
|
: `\nProjeto ${resultadoProjeto.projeto} criado.`);
|
|
430
|
+
// 3.5. O repositorio git.
|
|
431
|
+
//
|
|
432
|
+
// Sem ele, metade do dd-harness fica muda SEM AVISAR: `check --commit` nao tem o que
|
|
433
|
+
// cruzar, `sugerir_ancoras` devolve vazio (a rodada 006 bateu nisso), e o gancho de
|
|
434
|
+
// commit nao tem onde morar. Criar aqui e barato e reversivel; nao criar custa features
|
|
435
|
+
// que a pessoa nem descobre que tem.
|
|
436
|
+
const git = await iniciaRepositorio(process.cwd());
|
|
437
|
+
console.log({
|
|
438
|
+
criado: "criado repositório git (`git init`)",
|
|
439
|
+
"ja-era": "mantido repositório git — já existia",
|
|
440
|
+
"git-indisponivel": "AVISO: git não encontrado nesta máquina. `check --commit` e a sugestão de " +
|
|
441
|
+
"âncoras não vão funcionar até você instalá-lo.",
|
|
442
|
+
falhou: "AVISO: não consegui rodar `git init` aqui — siga e crie à mão se quiser.",
|
|
443
|
+
}[git]);
|
|
444
|
+
// O gancho so quando FOMOS nos que criamos o repositorio: num que ja existia,
|
|
445
|
+
// `.git/hooks` e de outra pessoa, e a regra de sempre vale — sugerir, nunca escrever.
|
|
446
|
+
if (git === "criado") {
|
|
447
|
+
const gancho = await escreveGanchoDeCommit(process.cwd());
|
|
448
|
+
if (gancho === "criado") {
|
|
449
|
+
console.log("criado .git/hooks/post-commit (devolve a memória ao code review)");
|
|
450
|
+
}
|
|
451
|
+
}
|
|
429
452
|
// 4. Config do repositorio — o unico arquivo que `init` tambem escreveria.
|
|
430
453
|
const r = await init(process.cwd(), { tenant, projeto: resultadoProjeto.projeto, api: API_PADRAO });
|
|
431
454
|
console.log(r.config === "criada" ? `criado ${CAMINHO_CONFIG}` : `mantido ${CAMINHO_CONFIG}`);
|
|
@@ -925,9 +948,28 @@ function contextoDaSessao(r, worker) {
|
|
|
925
948
|
"que ele já diz é ponto de partida, não é para ser ignorado.\n" +
|
|
926
949
|
"2. Pergunte ao usuário o que o código e o `CLAUDE.md` não revelarem: " +
|
|
927
950
|
"objetivo do projeto, stack principal, restrições e convenções.\n" +
|
|
928
|
-
|
|
951
|
+
// O git decide o quanto o agente age sozinho, e e a UNICA decisao subtrativa do
|
|
952
|
+
// protocolo: nasce fechada e so abre por escolha explicita. Perguntar AQUI, junto
|
|
953
|
+
// da stack, e o unico momento em que alguem esta pensando no fluxo do projeto —
|
|
954
|
+
// depois vira negociacao no meio da tarefa, que e onde a permissao acaba sendo
|
|
955
|
+
// dada por conveniencia.
|
|
956
|
+
"3. Pergunte SOBRE GIT — é isso que define o quanto o agente age sozinho, e a " +
|
|
957
|
+
"resposta vira regra escrita na política, nunca acordo tácito:\n" +
|
|
958
|
+
" - **O agente pode commitar?** O padrão é NÃO. Só abra se o usuário disser " +
|
|
959
|
+
"que sim, e escreva na política exatamente o que ele autorizou.\n" +
|
|
960
|
+
" - **E `push`?** Trate separado de commit: commit local é reversível, `push` " +
|
|
961
|
+
"publica. Quem autoriza um não autoriza o outro por tabela.\n" +
|
|
962
|
+
" - **Com que frequência?** A cada mudança que funciona, ao fim da tarefa, ou " +
|
|
963
|
+
"só quando pedirem? Isso muda quanto ruído o histórico carrega.\n" +
|
|
964
|
+
" - **Branch ou direto na principal?** Se houver fluxo de branch, escreva " +
|
|
965
|
+
"qual — o agente não adivinha convenção de nome.\n" +
|
|
966
|
+
" - **O que NUNCA fazer**, mesmo com autorização: `push --force`, " +
|
|
967
|
+
"`reset --hard`, reescrever história publicada. Ficam fora por padrão.\n" +
|
|
968
|
+
" Escreva cada resposta como regra. \"Ele sabe o que pode\" não sobrevive à " +
|
|
969
|
+
"próxima sessão, que começa sem memória disto.\n" +
|
|
970
|
+
"4. Mostre o rascunho da política e do briefing e espere a confirmação " +
|
|
929
971
|
"explícita do usuário antes de escrever.\n" +
|
|
930
|
-
"
|
|
972
|
+
"5. Escreva os dois com a ferramenta MCP `escrever_artefato` " +
|
|
931
973
|
"(`tipo: \"politica\"` e `tipo: \"briefing\"`, uma chamada cada). Se essa " +
|
|
932
974
|
"ferramenta não estiver disponível nesta sessão, pare e diga ao usuário que " +
|
|
933
975
|
"o servidor MCP do dd-harness precisa estar conectado para isto.\n" +
|
|
@@ -938,7 +980,7 @@ function contextoDaSessao(r, worker) {
|
|
|
938
980
|
"sessão, e ao terminá-la o agente **propõe** concluí-la (`editar_fase` com " +
|
|
939
981
|
"`status: \"concluida\"`) — nunca conclui por conta própria, porque quem decide " +
|
|
940
982
|
"que uma fase acabou é o usuário.\n" +
|
|
941
|
-
"
|
|
983
|
+
"6. Depois de escrever os dois, sobrescreva o `CLAUDE.md` da raiz com um " +
|
|
942
984
|
"ponteiro curto para a política deste serviço — a verdade passa a morar " +
|
|
943
985
|
"aqui, e o `CLAUDE.md` local nunca mais precisa ser editado à mão. O ponteiro " +
|
|
944
986
|
"precisa citar `ler_artefato` **e** `ler_roadmap`: quem chega por ele (Codex, " +
|
|
@@ -947,7 +989,7 @@ function contextoDaSessao(r, worker) {
|
|
|
947
989
|
// O momento certo de perguntar: quem acabou de descrever o projeto tem o contexto
|
|
948
990
|
// fresco para dizer por onde ele vai. Depois disso, ninguem mais pergunta — e uma
|
|
949
991
|
// feature que so se descobre lendo a lista de ferramentas nao se descobre.
|
|
950
|
-
"
|
|
992
|
+
"7. Pergunte se o trabalho deste projeto tem **fases** — algo que " +
|
|
951
993
|
"atravessa sessões, com ordem entre as partes. Se tiver, proponha as primeiras e " +
|
|
952
994
|
"crie-as com `criar_fase`; a de menor ordem vira o foco de cada sessão, e " +
|
|
953
995
|
"concluí-la a move para o changelog sozinha. Se for um projeto pequeno, diga que " +
|
|
@@ -955,7 +997,7 @@ function contextoDaSessao(r, worker) {
|
|
|
955
997
|
// O unico momento em que a stack acabou de ser descrita. Depois disto ninguem
|
|
956
998
|
// pergunta de novo, e uma skill que so se descobre lendo a lista nao se descobre —
|
|
957
999
|
// foi o mesmo gap que o roadmap teve, e que o passo 6 fechou.
|
|
958
|
-
"
|
|
1000
|
+
"8. Por fim, invoque a skill `propor-ferramentas`. A stack acabou de ser " +
|
|
959
1001
|
"descrita e as restrições estão frescas — é o único momento em que a pesquisa " +
|
|
960
1002
|
"tem o filtro certo. Ela **propõe**: MCP, skill ou lib que combine com este " +
|
|
961
1003
|
"projeto, nada é instalado sem o OK do usuário, e recusar tudo é resposta " +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"type": "module",
|
|
5
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
6
|
"license": "UNLICENSED",
|