@kuyper/harness 0.1.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 ADDED
@@ -0,0 +1,27 @@
1
+ # Kuyper Harness
2
+
3
+ `@kuyper/harness` mantém rules e skills numa fonte canônica, materializa essa
4
+ fonte para Claude e Codex e aplica gates ao fluxo Git de um projeto com uma
5
+ pessoa desenvolvedora.
6
+
7
+ O Harness ensina o método além de automatizar comandos. Comece por
8
+ [Começar um projeto](docs/guia/01-comecar.md) e siga os guias na ordem:
9
+
10
+ 1. [Começar um projeto](docs/guia/01-comecar.md)
11
+ 2. [Conceitos](docs/guia/02-conceitos.md)
12
+ 3. [Referência de comandos](docs/guia/03-comandos.md)
13
+ 4. [Equivalentes manuais](docs/guia/04-equivalentes-manuais.md)
14
+ 5. [O método](docs/guia/05-metodo.md)
15
+ 6. [Quando algo falha](docs/guia/06-falhas.md)
16
+
17
+ Requisitos: Node.js 20 ou posterior, pnpm 9 ou posterior, Git e um repositório
18
+ remoto chamado `origin`. O MVP assume uma pessoa, branches literais `main` e
19
+ `dev`, e GitHub como remoto.
20
+
21
+ As oito interfaces públicas são `init`, `generate`, `validate`, `integrate`,
22
+ `publish`, `update`, `rule` e `skill`. Invoque sempre o binário instalado no
23
+ projeto:
24
+
25
+ ```bash
26
+ pnpm exec kuyper
27
+ ```
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Quando abrir um ADR
3
+ ---
4
+
5
+ Uma decisão técnica vira ADR quando é durável — sobrevive à tarefa que a
6
+ motivou — e quando alguém precisaria entender o porquê depois, não só o
7
+ resultado. Registre o contexto, a decisão e o que foi descartado, não o
8
+ processo de chegar até ela.
9
+
10
+ Decisão de implementação contida numa tarefa, que ninguém vai precisar
11
+ revisitar fora dela, não precisa de ADR — só de ser feita.
@@ -0,0 +1,5 @@
1
+ ---
2
+ title: Fluxo Git
3
+ ---
4
+
5
+ Trabalhe na `dev`. A `main` recebe merges, não commits diretos.
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Limites
3
+ ---
4
+
5
+ A LLM não usa `--no-verify` nem nenhum outro bypass de gate ou hook. Essa
6
+ porta é do humano, não dela.
7
+
8
+ O shell é compartilhado, e nada nele distingue quem digitou um comando —
9
+ esta regra reduz o risco de a LLM usar o escape, não o elimina, e não finge
10
+ o contrário. É por isso que os demais mecanismos do produto verificam
11
+ qualidade, nunca procedência: essas barreiras valem para todo mundo, sempre.
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: Publicação
3
+ ---
4
+
5
+ A sequência: `integrate` a cada tarefa, `publish` quando a feature ou a versão
6
+ estiver pronta. São atos separados — integrar leva a mudança para a `main`;
7
+ publicar manda a `main` para o remoto.
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: Questionamento
3
+ ---
4
+
5
+ Documento herdado não é verdade absoluta, pode ser questionado. Discordou
6
+ de uma decisão do Discovery, do PRD ou do usuário? Diga, com o motivo e a
7
+ alternativa, antes de executar. Depois execute a decisão dele por inteiro.
8
+ Aceitar em silêncio algo que se acredita ser pior é o que esta regra proíbe.
@@ -0,0 +1,16 @@
1
+ ---
2
+ title: Uso do STATE
3
+ ---
4
+
5
+ Leia `.kuyper/STATE.md` ao começar ou retomar uma tarefa. Sobrescreva
6
+ somente ao concluir a tarefa ou ao entregá-la a outra sessão, provider ou
7
+ pessoa antes de terminar.
8
+
9
+ O STATE é o post-it operacional da troca de turno, não o lugar para tudo.
10
+ Um achado que vale para qualquer LLM trabalhando neste projeto, não só para
11
+ quem retomar esta tarefa, tem destino próprio: decisão técnica durável vira
12
+ ADR; mudança de comportamento ainda não aprovada vira proposal; instrução
13
+ permanente sobre como trabalhar aqui vira rule do projeto. O que sobra para
14
+ o STATE é o que é específico desta tarefa — o resto, guardado só ali, some
15
+ na próxima troca de sessão ou de ferramenta, e quem paga o custo é você de
16
+ novo, ou a próxima pessoa.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: architect
3
+ description: Decide como será construído. Use depois de um PRD aprovado, para transformar comportamento em decisão técnica e ordem de execução.
4
+ ---
5
+
6
+ # Architect
7
+
8
+ ## Contrato
9
+
10
+ - **Entrada:** um PRD aprovado.
11
+ - **Saída:** um ADR por decisão técnica contida, ou uma SPEC quando a
12
+ decisão atravessa mais de um bloco de construção ou muda o comportamento
13
+ do PRD; e um documento de construção (`kind: build`) com a decomposição em
14
+ blocos, a ordem de execução e o critério de "pronto" de cada um.
15
+ - **Decide:** a escolha técnica e as alternativas descartadas, por quê.
16
+ - **Não decide:** mudar o comportamento do PRD sem avisar. Se a decisão
17
+ técnica revela um buraco no PRD, aponta o buraco e questiona — não decide
18
+ sozinha por cima dele.
19
+ - **Encerra quando:** cada decisão técnica tem ADR ou SPEC, e o documento de
20
+ construção cobre toda recusa e todo critério de aceitação do PRD, sem
21
+ nenhum órfão.
22
+ - **Handoff:** skill `dev`, um bloco por vez.
23
+
24
+ ## Procedimento
25
+
26
+ 1. Leia o PRD inteiro. Para cada decisão técnica que ele deixa em aberto,
27
+ pergunte primeiro "quem já resolve isso" — Git, o gerenciador de
28
+ pacotes, o sistema operacional — antes de propor mecanismo novo. Nunca
29
+ reconstruir o que já existe.
30
+ 2. Decisão contida num bloco — não obriga dois blocos a se coordenarem antes
31
+ de existir código, não muda o comportamento do PRD: registre como ADR —
32
+ contexto, decisão, alternativas descartadas e por quê.
33
+ 3. Decisão que atravessa blocos, ou que muda o comportamento do PRD:
34
+ registre como SPEC, com o escopo só daquela decisão. Ela morre quando os
35
+ blocos que cobre fecharem — não é o PRD reescrito noutro arquivo.
36
+ 4. Se uma decisão técnica revelar um buraco no comportamento que o PRD
37
+ descreve, não decida por conta própria: aponte o buraco, proponha a
38
+ mudança, peça aprovação, volte ao PRD — só depois continue (rule
39
+ `questionamento`).
40
+ 5. Decomponha em blocos: um por vez, cada um com um critério de "pronto"
41
+ executável — algo que se roda e se observa, nunca conferido de olho.
42
+ 6. Ordene a execução pelo que os outros blocos dependem. O que não tem
43
+ equivalente manual, e que os demais chamam ou repetem, vem primeiro.
44
+ 7. Termine conferindo a rastreabilidade: toda recusa e todo critério de
45
+ aceitação do PRD aparece em algum bloco. Um órfão aqui é um furo
46
+ silencioso que só aparece depois.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: dev
3
+ description: Executa um bloco de construção — abre o arquivo indicado e implementa. Use quando há PRD/SPEC/BUILD já decididos e uma tarefa concreta pra fazer.
4
+ ---
5
+
6
+ # Dev
7
+
8
+ ## Contrato
9
+
10
+ - **Entrada:** um bloco do documento de construção, ou um pedido
11
+ equivalente — *"abra o arquivo X e execute"*.
12
+ - **Saída:** código, testes, e o `STATE.md` atualizado.
13
+ - **Decide:** como implementar dentro do que já foi decidido no PRD, na SPEC
14
+ e no ADR do bloco.
15
+ - **Não decide:** mudar arquitetura ou comportamento sem escalar. Se a
16
+ implementação revelar que o PRD ou a SPEC estão errados, questiona antes
17
+ de desviar da decisão registrada — não corrige em silêncio.
18
+ - **Encerra quando:** os gates do projeto passam a partir de árvore limpa, e
19
+ cada item do critério de "pronto" do bloco foi conferido um a um contra o
20
+ que foi implementado — não só rodado.
21
+ - **Handoff:** de volta para o humano, ao fechar o bloco; ou o próximo
22
+ bloco, quando ele existir.
23
+
24
+ ## Procedimento
25
+
26
+ 1. Leia o bloco inteiro antes de escrever qualquer código. O critério de
27
+ "pronto" é a régua da tarefa, não uma lista pra conferir depois de
28
+ terminar.
29
+ 2. Releia o PRD e a SPEC que o bloco toca. Comportamento não se decide lendo
30
+ o código nem adivinhando — está escrito, ou não existe ainda.
31
+ 3. Antes de escrever uma função nova, procure se ela já existe. Reaproveitar
32
+ é a regra; duplicar é a exceção que precisa de motivo.
33
+ 4. Escreva teste para cada item do critério de "pronto", não só para o
34
+ caminho feliz.
35
+ 5. Rode os gates do projeto a partir de árvore limpa.
36
+ 6. Audite o critério de "pronto" item a item contra o que foi implementado.
37
+ Rodar o comando e ele funcionar não é suficiente para saber que o bloco
38
+ fechou — a lista é.
39
+ 7. Atualize o `STATE.md`: onde parou, o que descobriu que não estava óbvio,
40
+ o próximo passo concreto, e o que quem retomar precisa ter cuidado.
41
+ 8. Se o código revelar que o PRD ou a SPEC estão errados, pare e questione
42
+ antes de desviar — diga o motivo e a alternativa, execute a decisão que
43
+ vier depois por inteiro (rule `questionamento`).
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: discovery
3
+ description: Explora uma ideia e decide se vale construir. Use quando uma proposta ainda precisa ser descoberta.
4
+ ---
5
+
6
+ # Discovery
7
+
8
+ ## Contrato
9
+
10
+ - **Entrada:** uma ideia ou um problema, em qualquer estágio de maturidade —
11
+ de uma frase a uma conversa longa.
12
+ - **Saída:** um documento em `.kuyper/docs/discovery/<data>-<slug>.md`, com o
13
+ frontmatter completo (`schemaVersion`, `kind: discovery`, `status`,
14
+ `revision`, `createdAt`/`approvedAt`, `depends: []` — Discovery não depende
15
+ de nada).
16
+ - **Decide:** como organizar a fronteira do que vale a pena, que princípios
17
+ resolvem as tensões reais da proposta, o que fica de fora e por quê.
18
+ - **Não decide:** se vale a pena construir. Isso é sempre do humano — a
19
+ skill redige, ele aprova ou recusa.
20
+ - **Encerra quando:** o humano aprova (`status: approved`) ou decide não
21
+ seguir. As duas são saídas válidas — uma recomendação de "não proceder" não
22
+ é falha da skill, é o trabalho dela funcionando.
23
+ - **Handoff:** skill `prd`, quando aprovado.
24
+
25
+ ## Procedimento
26
+
27
+ 1. Entenda o problema antes de propor solução. Pergunte até a pessoa não
28
+ conseguir mais responder "por quê" sem repetir o que já disse.
29
+ 2. Escreva o que é e o problema primeiro. Se não sair em poucas frases, o
30
+ problema ainda não está claro o bastante pra continuar — não force a
31
+ fronteira antes disso.
32
+ 3. Proponha princípios: frase curta, cada um resolvendo uma tensão real da
33
+ proposta. Uma lista de boas intenções não é princípio.
34
+ 4. Desenhe a fronteira em três partes — **incluído** (o que entra),
35
+ **excluído** (o que fica de fora desta versão, sem gatilho de volta) e
36
+ **adiado com gatilho** (o que fica de fora por agora, com o evento
37
+ observável que o traz de volta; nunca "quando tivermos mais tempo").
38
+ 5. Liste as decisões que restringem a fronteira, uma por linha, numeradas.
39
+ Não registre decisão que não restringe nada.
40
+ 6. Escreva critérios de sucesso executáveis — algo que se roda e observa, não
41
+ "o usuário vai gostar".
42
+ 7. Liste riscos, cada um com mitigação. Um risco sem mitigação é um risco não
43
+ avaliado.
44
+ 8. Termine com uma recomendação clara: proceder, ou não — e por quê.
45
+ 9. Pergunte ao humano se aprova. Se sim, `status: approved`, siga para o
46
+ handoff. Se não, ele diz o motivo, você ajusta, repete — sem aceitar em
47
+ silêncio uma direção que você acha pior (rule `questionamento`).
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: prd
3
+ description: Define o que será construído e quando está pronto. Use depois de um Discovery aprovado, ou quando não há incerteza de valor a resolver.
4
+ ---
5
+
6
+ # PRD
7
+
8
+ ## Contrato
9
+
10
+ - **Entrada:** um Discovery aprovado, ou uma decisão equivalente do humano
11
+ quando não havia incerteza de valor a fechar.
12
+ - **Saída:** um documento em `.kuyper/docs/prd/<data>-<slug>.md`, com
13
+ `depends` apontando para o Discovery (quando existir) e frontmatter
14
+ completo.
15
+ - **Decide:** como organizar o comportamento em contratos observáveis, quais
16
+ recusas nomear, o formato das mensagens do produto.
17
+ - **Não decide:** o que o produto faz. O humano decide o quê; a skill
18
+ redige o comportamento resultante.
19
+ - **Encerra quando:** o humano aprova (`status: approved`).
20
+ - **Handoff:** skill `architect`.
21
+
22
+ ## Procedimento
23
+
24
+ 1. Releia o Discovery aprovado — não resuma. Extraia só as conclusões que
25
+ restringem a construção; o porquê fica lá, não aqui. Se uma frase só faz
26
+ sentido citando a motivação original, ela está no documento errado.
27
+ 2. Para cada capacidade que o Discovery decidiu incluir, escreva um contrato
28
+ observável: o que faz, quando recusa, quando está pronto. Nunca "deveria
29
+ funcionar bem".
30
+ 3. Nomeie toda recusa prevista com um código, e a rota que resolve —
31
+ comandos copiáveis, nunca "corrija o problema".
32
+ 4. Se o produto fala com o usuário, decida a forma da mensagem: um fato na
33
+ primeira linha, os caminhos envolvidos, a consequência quando não for
34
+ óbvia, a rota em comandos que concluem sozinhos.
35
+ 5. Tamanho vem do formato de seções que você decidiu (comandos, contratos,
36
+ recusas, critérios de aceitação), nunca de um teto de linhas.
37
+ 6. Pergunte ao humano se o comportamento descrito é o certo — ele decide o
38
+ quê, você decide como registrar. Discordou de algo que ele decidiu? Diga,
39
+ com o motivo e a alternativa, antes de escrever como se tivesse
40
+ concordado (rule `questionamento`).
41
+ 7. Termine com critérios de aceitação executáveis, cada um rastreável a uma
42
+ futura decomposição em blocos de construção — mesmo que os blocos ainda
43
+ não existam.
@@ -0,0 +1,89 @@
1
+ import { mkdir, readdir, rename, rm, writeFile, chmod } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ /**
4
+ * Sufixo reservado do Harness (PRD §3.2, ADR escrita-atomica).
5
+ * Dentro dele a regra "não destrói o que não criou" não vale.
6
+ */
7
+ export const TMP_SUFFIX = '.kuyper-tmp';
8
+ /**
9
+ * Escreve `content` em `<targetPath>.kuyper-tmp`, no mesmo diretório do alvo,
10
+ * e renomeia por cima. Quem lê vê o arquivo velho inteiro ou o novo inteiro,
11
+ * nunca truncado — a atomicidade é a do próprio `rename()` do sistema de
12
+ * arquivos, não uma máquina de estados do Harness (ADR escrita-atomica,
13
+ * decisão 1).
14
+ *
15
+ * O Harness **não** promete atomicidade da operação inteira: se `generate`
16
+ * parar entre dois arquivos, algumas saídas ficam antigas e outras novas — a
17
+ * comparação de três pontas reconhece o que já foi materializado e a
18
+ * reexecução converge. Não há seam de injeção de falha aqui: estados
19
+ * parciais são fixtures de disco nos blocos que os interpretam (B2, B9).
20
+ */
21
+ export async function writeFileAtomic(targetPath, content, options = {}) {
22
+ const tmpPath = `${targetPath}${TMP_SUFFIX}`;
23
+ await mkdir(dirname(targetPath), { recursive: true });
24
+ await writeFile(tmpPath, content);
25
+ if (options.mode !== undefined) {
26
+ await chmod(tmpPath, options.mode);
27
+ }
28
+ await rename(tmpPath, targetPath);
29
+ }
30
+ /** Verdadeiro se o caminho pertence ao namespace reservado do Harness. */
31
+ export function isTempPath(path) {
32
+ return path.endsWith(TMP_SUFFIX);
33
+ }
34
+ /**
35
+ * Remove `path` se, e só se, ele existir e terminar em `.kuyper-tmp`.
36
+ * Usado para os candidatos explícitos que não vivem sob um diretório
37
+ * controlado (ex.: CLAUDE.md.kuyper-tmp na raiz do projeto).
38
+ */
39
+ export async function removeIfTemp(path) {
40
+ if (!isTempPath(path))
41
+ return false;
42
+ try {
43
+ await rm(path, { force: false });
44
+ return true;
45
+ }
46
+ catch (err) {
47
+ if (err.code === 'ENOENT')
48
+ return false;
49
+ throw err;
50
+ }
51
+ }
52
+ /**
53
+ * Varre recursivamente cada diretório em `dirs` removendo todo arquivo
54
+ * `*.kuyper-tmp` encontrado, sem distinguir qual comando o deixou (ADR
55
+ * escrita-atomica, decisão 2 — o namespace é do Harness inteiro, não de uma
56
+ * operação). Diretório ausente é ignorado, não é erro.
57
+ *
58
+ * Este é o primitivo; a política de "quais raízes varrer, e quando" — a
59
+ * limpeza global e silenciosa ao final de uma materialização bem-sucedida,
60
+ * antes de reportar sucesso — pertence a quem chama (o `generate` do B2).
61
+ */
62
+ export async function sweepTempFiles(dirs) {
63
+ const removed = [];
64
+ for (const dir of dirs) {
65
+ await walk(dir);
66
+ }
67
+ return removed;
68
+ async function walk(dir) {
69
+ let entries;
70
+ try {
71
+ entries = await readdir(dir, { withFileTypes: true });
72
+ }
73
+ catch (err) {
74
+ if (err.code === 'ENOENT')
75
+ return;
76
+ throw err;
77
+ }
78
+ for (const entry of entries) {
79
+ const full = join(dir, entry.name);
80
+ if (entry.isDirectory()) {
81
+ await walk(full);
82
+ }
83
+ else if (entry.isFile() && isTempPath(entry.name)) {
84
+ await rm(full, { force: true });
85
+ removed.push(full);
86
+ }
87
+ }
88
+ }
89
+ }