dd-harness 0.28.0 → 0.30.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/escreve-config.js +5 -1
- package/dist/git.d.ts +37 -0
- package/dist/git.js +67 -0
- package/dist/index.js +82 -5
- package/package.json +1 -1
package/dist/escreve-config.js
CHANGED
|
@@ -126,4 +126,8 @@ de ler código, responder ou planejar.
|
|
|
126
126
|
|
|
127
127
|
Depois, \`ler_roadmap\`: se houver uma fase **Agora**, é dela que saem os passos
|
|
128
128
|
desta sessão. Lista vazia significa que este projeto não usa roadmap, e isso é
|
|
129
|
-
válido — não crie fase sem o usuário pedir
|
|
129
|
+
válido — não crie fase sem o usuário pedir.
|
|
130
|
+
|
|
131
|
+
E \`listar_skills\`: são os procedimentos deste projeto. **Invoque a que couber
|
|
132
|
+
ANTES de fazer o trabalho, não depois** — skill lida no fim vira revisão do que
|
|
133
|
+
já saiu errado. A política diz quais são obrigatórias e quando.`;
|
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}`);
|
|
@@ -899,6 +922,31 @@ async function comandoPolitica(argv) {
|
|
|
899
922
|
* so faria o Claude Code registrar falha no transcript — que ninguem le — enquanto a
|
|
900
923
|
* sessao seguiria sem saber que esta sem protocolo.
|
|
901
924
|
*/
|
|
925
|
+
/**
|
|
926
|
+
* Lembra que ha skills — so quando a POLITICA nao lembra.
|
|
927
|
+
*
|
|
928
|
+
* Medido em uso real: a skill aparecia na listagem do host, a descricao estava no
|
|
929
|
+
* contexto, e mesmo assim nao era invocada. Estar disponivel nao e ser usada.
|
|
930
|
+
*
|
|
931
|
+
* O conserto de verdade e a politica dizer quando cada skill e obrigatoria — e o passo 5
|
|
932
|
+
* do briefing agora manda escrever isso. Mas projeto ja briefado nao passa mais por la, e
|
|
933
|
+
* a politica dele continua sem a regra. Esta linha cobre esse intervalo.
|
|
934
|
+
*
|
|
935
|
+
* CALA quando a politica ja fala de skill: repetir o que ela diz treina a ler a politica
|
|
936
|
+
* como se fosse opcional, e a politica e a unica coisa aqui que o agente trata como
|
|
937
|
+
* regra. A deteccao e grosseira de proposito — qualquer mencao basta, porque o custo de
|
|
938
|
+
* calar a mais e zero e o de repetir e corroer a autoridade do texto principal.
|
|
939
|
+
*/
|
|
940
|
+
function lembreteDeSkills(politica) {
|
|
941
|
+
if (/skill/i.test(politica))
|
|
942
|
+
return "";
|
|
943
|
+
return ("\n\n---\n\n" +
|
|
944
|
+
"Este projeto pode ter **skills** — procedimentos escritos para o trabalho que se " +
|
|
945
|
+
"repete. `listar_skills` mostra quais, e `ler_skill` traz o passo a passo.\n\n" +
|
|
946
|
+
"Invoque a que couber **antes** de fazer o trabalho, não depois: skill lida no fim " +
|
|
947
|
+
"vira revisão do que já saiu errado. Se a política não disser quais são " +
|
|
948
|
+
"obrigatórias, vale propor ao usuário que ela passe a dizer.");
|
|
949
|
+
}
|
|
902
950
|
function contextoDaSessao(r, worker) {
|
|
903
951
|
const base = { hookEventName: "SessionStart" };
|
|
904
952
|
const fila = avisoDaFila(r.esperandoIndexacao ?? 0, worker);
|
|
@@ -911,6 +959,7 @@ function contextoDaSessao(r, worker) {
|
|
|
911
959
|
// Depois da politica, antes dos avisos operacionais: o roadmap e contexto de
|
|
912
960
|
// trabalho, a fila e ruido de infraestrutura. Vazio quando nao ha fase aberta.
|
|
913
961
|
blocoDeSessao(r.roadmap) +
|
|
962
|
+
lembreteDeSkills(r.conteudo) +
|
|
914
963
|
fila,
|
|
915
964
|
};
|
|
916
965
|
}
|
|
@@ -925,9 +974,28 @@ function contextoDaSessao(r, worker) {
|
|
|
925
974
|
"que ele já diz é ponto de partida, não é para ser ignorado.\n" +
|
|
926
975
|
"2. Pergunte ao usuário o que o código e o `CLAUDE.md` não revelarem: " +
|
|
927
976
|
"objetivo do projeto, stack principal, restrições e convenções.\n" +
|
|
928
|
-
|
|
977
|
+
// O git decide o quanto o agente age sozinho, e e a UNICA decisao subtrativa do
|
|
978
|
+
// protocolo: nasce fechada e so abre por escolha explicita. Perguntar AQUI, junto
|
|
979
|
+
// da stack, e o unico momento em que alguem esta pensando no fluxo do projeto —
|
|
980
|
+
// depois vira negociacao no meio da tarefa, que e onde a permissao acaba sendo
|
|
981
|
+
// dada por conveniencia.
|
|
982
|
+
"3. Pergunte SOBRE GIT — é isso que define o quanto o agente age sozinho, e a " +
|
|
983
|
+
"resposta vira regra escrita na política, nunca acordo tácito:\n" +
|
|
984
|
+
" - **O agente pode commitar?** O padrão é NÃO. Só abra se o usuário disser " +
|
|
985
|
+
"que sim, e escreva na política exatamente o que ele autorizou.\n" +
|
|
986
|
+
" - **E `push`?** Trate separado de commit: commit local é reversível, `push` " +
|
|
987
|
+
"publica. Quem autoriza um não autoriza o outro por tabela.\n" +
|
|
988
|
+
" - **Com que frequência?** A cada mudança que funciona, ao fim da tarefa, ou " +
|
|
989
|
+
"só quando pedirem? Isso muda quanto ruído o histórico carrega.\n" +
|
|
990
|
+
" - **Branch ou direto na principal?** Se houver fluxo de branch, escreva " +
|
|
991
|
+
"qual — o agente não adivinha convenção de nome.\n" +
|
|
992
|
+
" - **O que NUNCA fazer**, mesmo com autorização: `push --force`, " +
|
|
993
|
+
"`reset --hard`, reescrever história publicada. Ficam fora por padrão.\n" +
|
|
994
|
+
" Escreva cada resposta como regra. \"Ele sabe o que pode\" não sobrevive à " +
|
|
995
|
+
"próxima sessão, que começa sem memória disto.\n" +
|
|
996
|
+
"4. Mostre o rascunho da política e do briefing e espere a confirmação " +
|
|
929
997
|
"explícita do usuário antes de escrever.\n" +
|
|
930
|
-
"
|
|
998
|
+
"5. Escreva os dois com a ferramenta MCP `escrever_artefato` " +
|
|
931
999
|
"(`tipo: \"politica\"` e `tipo: \"briefing\"`, uma chamada cada). Se essa " +
|
|
932
1000
|
"ferramenta não estiver disponível nesta sessão, pare e diga ao usuário que " +
|
|
933
1001
|
"o servidor MCP do dd-harness precisa estar conectado para isto.\n" +
|
|
@@ -938,7 +1006,16 @@ function contextoDaSessao(r, worker) {
|
|
|
938
1006
|
"sessão, e ao terminá-la o agente **propõe** concluí-la (`editar_fase` com " +
|
|
939
1007
|
"`status: \"concluida\"`) — nunca conclui por conta própria, porque quem decide " +
|
|
940
1008
|
"que uma fase acabou é o usuário.\n" +
|
|
941
|
-
|
|
1009
|
+
// Medido em uso real: a skill aparecia na listagem, a descricao estava no
|
|
1010
|
+
// contexto, e mesmo assim nao era invocada. Estar disponivel nao e ser usada — o
|
|
1011
|
+
// que faz o agente parar e invocar e a POLITICA dizer que e obrigatorio, porque
|
|
1012
|
+
// ela e o unico texto que ele trata como regra em vez de catalogo.
|
|
1013
|
+
" Inclua também QUANDO cada skill é obrigatória — o gatilho, não a lista. " +
|
|
1014
|
+
"Chame `listar_skills` para ver o que o projeto tem e escreva uma linha por " +
|
|
1015
|
+
"skill que não pode ser pulada. Exemplo: \"ao escrever ou editar código, " +
|
|
1016
|
+
"invoque `como-desenvolver` ANTES de começar\". Sem isso a skill existe, " +
|
|
1017
|
+
"aparece na listagem e mesmo assim não é usada — foi medido.\n" +
|
|
1018
|
+
"6. Depois de escrever os dois, sobrescreva o `CLAUDE.md` da raiz com um " +
|
|
942
1019
|
"ponteiro curto para a política deste serviço — a verdade passa a morar " +
|
|
943
1020
|
"aqui, e o `CLAUDE.md` local nunca mais precisa ser editado à mão. O ponteiro " +
|
|
944
1021
|
"precisa citar `ler_artefato` **e** `ler_roadmap`: quem chega por ele (Codex, " +
|
|
@@ -947,7 +1024,7 @@ function contextoDaSessao(r, worker) {
|
|
|
947
1024
|
// O momento certo de perguntar: quem acabou de descrever o projeto tem o contexto
|
|
948
1025
|
// fresco para dizer por onde ele vai. Depois disso, ninguem mais pergunta — e uma
|
|
949
1026
|
// feature que so se descobre lendo a lista de ferramentas nao se descobre.
|
|
950
|
-
"
|
|
1027
|
+
"7. Pergunte se o trabalho deste projeto tem **fases** — algo que " +
|
|
951
1028
|
"atravessa sessões, com ordem entre as partes. Se tiver, proponha as primeiras e " +
|
|
952
1029
|
"crie-as com `criar_fase`; a de menor ordem vira o foco de cada sessão, e " +
|
|
953
1030
|
"concluí-la a move para o changelog sozinha. Se for um projeto pequeno, diga que " +
|
|
@@ -955,7 +1032,7 @@ function contextoDaSessao(r, worker) {
|
|
|
955
1032
|
// O unico momento em que a stack acabou de ser descrita. Depois disto ninguem
|
|
956
1033
|
// pergunta de novo, e uma skill que so se descobre lendo a lista nao se descobre —
|
|
957
1034
|
// foi o mesmo gap que o roadmap teve, e que o passo 6 fechou.
|
|
958
|
-
"
|
|
1035
|
+
"8. Por fim, invoque a skill `propor-ferramentas`. A stack acabou de ser " +
|
|
959
1036
|
"descrita e as restrições estão frescas — é o único momento em que a pesquisa " +
|
|
960
1037
|
"tem o filtro certo. Ela **propõe**: MCP, skill ou lib que combine com este " +
|
|
961
1038
|
"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.30.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",
|