@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 +27 -0
- package/core/rules/adr.md +11 -0
- package/core/rules/fluxo-git.md +5 -0
- package/core/rules/limites.md +11 -0
- package/core/rules/publicacao.md +7 -0
- package/core/rules/questionamento.md +8 -0
- package/core/rules/state.md +16 -0
- package/core/skills/architect/SKILL.md +46 -0
- package/core/skills/dev/SKILL.md +43 -0
- package/core/skills/discovery/SKILL.md +47 -0
- package/core/skills/prd/SKILL.md +43 -0
- package/dist/atomicWrite.js +89 -0
- package/dist/capabilities.js +361 -0
- package/dist/capabilityCommands.js +285 -0
- package/dist/cli.js +165 -0
- package/dist/config.js +159 -0
- package/dist/coreClassification.js +76 -0
- package/dist/errors.js +38 -0
- package/dist/gateRunner.js +109 -0
- package/dist/generate.js +467 -0
- package/dist/gitPlumbing.js +213 -0
- package/dist/hookBehavior.js +212 -0
- package/dist/hooks.js +49 -0
- package/dist/init.js +265 -0
- package/dist/integrate.js +192 -0
- package/dist/lock.js +108 -0
- package/dist/outputPlan.js +61 -0
- package/dist/paths.js +22 -0
- package/dist/project.js +29 -0
- package/dist/publish.js +207 -0
- package/dist/update.js +434 -0
- package/dist/validate.js +142 -0
- package/docs/guia/01-comecar.md +156 -0
- package/docs/guia/02-conceitos.md +53 -0
- package/docs/guia/03-comandos.md +65 -0
- package/docs/guia/04-equivalentes-manuais.md +135 -0
- package/docs/guia/05-metodo.md +56 -0
- package/docs/guia/06-falhas.md +103 -0
- package/package.json +38 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Começar um projeto
|
|
2
|
+
|
|
3
|
+
Esta é a jornada completa, do repositório vazio à primeira publicação. O
|
|
4
|
+
Harness não instala TypeScript, ESLint ou Vitest: os passos abaixo fazem isso
|
|
5
|
+
explicitamente antes do `init`.
|
|
6
|
+
|
|
7
|
+
Antes de começar, configure sua identidade do Git e a autenticação SSH do
|
|
8
|
+
GitHub. Estes comandos precisam imprimir seu nome e e-mail; se não imprimirem,
|
|
9
|
+
configure-os com `git config --global user.name "Seu Nome"` e
|
|
10
|
+
`git config --global user.email "voce@example.com"`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
git config --global user.name
|
|
14
|
+
git config --global user.email
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 1. Criar e clonar o repositório
|
|
18
|
+
|
|
19
|
+
No GitHub, crie um repositório **privado e vazio**, sem README, `.gitignore` ou
|
|
20
|
+
licença. Troque os dois valores abaixo e execute:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export GITHUB_USER="seu-usuario"
|
|
24
|
+
export PROJECT_NAME="seu-projeto"
|
|
25
|
+
git clone "git@github.com:${GITHUB_USER}/${PROJECT_NAME}.git"
|
|
26
|
+
cd "$PROJECT_NAME"
|
|
27
|
+
git switch -c main
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Um clone vazio pode avisar que não tem branch; `git switch -c main` é esperado.
|
|
31
|
+
|
|
32
|
+
## 2. Criar o projeto TypeScript e os quatro gates
|
|
33
|
+
|
|
34
|
+
Os comandos abaixo criam uma configuração mínima que realmente compila, passa
|
|
35
|
+
no lint e aceita um projeto ainda sem testes:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm init
|
|
39
|
+
pnpm pkg set type=module
|
|
40
|
+
pnpm add -D typescript@^5.9.3 eslint@^10.9.1 @eslint/js@^10.0.1 typescript-eslint@^8.68.0 vitest@^4.1.11
|
|
41
|
+
|
|
42
|
+
cat > .gitignore <<'EOF'
|
|
43
|
+
node_modules/
|
|
44
|
+
dist/
|
|
45
|
+
.DS_Store
|
|
46
|
+
EOF
|
|
47
|
+
|
|
48
|
+
cat > tsconfig.json <<'EOF'
|
|
49
|
+
{
|
|
50
|
+
"compilerOptions": {
|
|
51
|
+
"target": "ES2022",
|
|
52
|
+
"module": "NodeNext",
|
|
53
|
+
"moduleResolution": "NodeNext",
|
|
54
|
+
"strict": true,
|
|
55
|
+
"noUncheckedIndexedAccess": true,
|
|
56
|
+
"exactOptionalPropertyTypes": true,
|
|
57
|
+
"outDir": "dist",
|
|
58
|
+
"rootDir": "src"
|
|
59
|
+
},
|
|
60
|
+
"include": ["src/**/*.ts"]
|
|
61
|
+
}
|
|
62
|
+
EOF
|
|
63
|
+
|
|
64
|
+
cat > eslint.config.js <<'EOF'
|
|
65
|
+
import js from '@eslint/js';
|
|
66
|
+
import tseslint from 'typescript-eslint';
|
|
67
|
+
|
|
68
|
+
export default tseslint.config(
|
|
69
|
+
js.configs.recommended,
|
|
70
|
+
...tseslint.configs.recommended,
|
|
71
|
+
{ ignores: ['dist/'] },
|
|
72
|
+
);
|
|
73
|
+
EOF
|
|
74
|
+
|
|
75
|
+
mkdir -p src
|
|
76
|
+
cat > src/index.ts <<'EOF'
|
|
77
|
+
export function hello(name: string): string {
|
|
78
|
+
return `Olá, ${name}.`;
|
|
79
|
+
}
|
|
80
|
+
EOF
|
|
81
|
+
|
|
82
|
+
pnpm pkg set scripts.typecheck="tsc --noEmit"
|
|
83
|
+
pnpm pkg set scripts.lint="eslint ."
|
|
84
|
+
pnpm pkg set scripts.test="vitest run --passWithNoTests"
|
|
85
|
+
pnpm pkg set scripts.build="tsc"
|
|
86
|
+
|
|
87
|
+
pnpm typecheck
|
|
88
|
+
pnpm lint
|
|
89
|
+
pnpm test
|
|
90
|
+
pnpm build
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
O `--passWithNoTests` é deliberado: o gate existe desde o primeiro commit, antes
|
|
94
|
+
de o primeiro teste existir.
|
|
95
|
+
|
|
96
|
+
## 3. Instalar o Harness e criar o commit raiz
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pnpm add -D @kuyper/harness
|
|
100
|
+
pnpm exec kuyper --version
|
|
101
|
+
git add -A
|
|
102
|
+
git commit -m "chore: inicia projeto"
|
|
103
|
+
git switch -c dev
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
O commit raiz precisa acontecer na `main`, antes dos hooks existirem. Todo
|
|
107
|
+
trabalho posterior acontece na `dev`.
|
|
108
|
+
|
|
109
|
+
## 4. Inicializar o Harness
|
|
110
|
+
|
|
111
|
+
`init` é o único comando interativo. Ele pergunta nome, descrição e providers;
|
|
112
|
+
pressionar Enter aceita os valores do `package.json` e ambos os providers. A
|
|
113
|
+
quarta pergunta apenas confirma o plano.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pnpm exec kuyper init
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Revise `.kuyper/`, `CLAUDE.md`, `AGENTS.md`, `.claude/skills/`,
|
|
120
|
+
`.agents/skills/`, `.kuyper/hooks/` e a alteração de `package.json`. Então:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pnpm exec kuyper validate
|
|
124
|
+
git add -A
|
|
125
|
+
git commit -m "chore: inicializa Kuyper Harness"
|
|
126
|
+
pnpm exec kuyper integrate
|
|
127
|
+
pnpm exec kuyper publish
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Ao final, `main` local e `origin/main` apontam para o merge criado pelo
|
|
131
|
+
`integrate`, e o checkout volta para `dev`.
|
|
132
|
+
|
|
133
|
+
## 5. Trabalhar depois do primeiro publish
|
|
134
|
+
|
|
135
|
+
Faça alterações e commits na `dev`. Ao terminar cada tarefa:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pnpm exec kuyper integrate
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Quando quiser publicar uma ou várias tarefas já integradas:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pnpm exec kuyper publish
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Quando uma segunda versão do Harness existir na npm:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pnpm exec kuyper update
|
|
151
|
+
git add -A
|
|
152
|
+
git commit -m "chore: atualiza Kuyper Harness"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
O MVP não certifica `update` no primeiro publish: ainda não existe uma segunda
|
|
156
|
+
versão para `@latest` resolver. Essa prova fecha na primeira atualização real.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Conceitos
|
|
2
|
+
|
|
3
|
+
## Fonte canônica
|
|
4
|
+
|
|
5
|
+
Tudo que você mantém vive em `.kuyper/` e é versionado. `core/` vem no pacote e
|
|
6
|
+
só `update` o troca; `project/` é seu. Rules são instruções permanentes; skills
|
|
7
|
+
são procedimentos chamados quando necessários. Nunca edite de volta uma saída
|
|
8
|
+
de provider esperando que ela vire fonte.
|
|
9
|
+
|
|
10
|
+
## Providers e materialização
|
|
11
|
+
|
|
12
|
+
Claude e Codex são providers: formatos de destino derivados da mesma fonte.
|
|
13
|
+
`generate` produz `CLAUDE.md`, `AGENTS.md`, skills e hooks de forma
|
|
14
|
+
determinística. `generated.lock` registra as saídas conhecidas para distinguir
|
|
15
|
+
uma mudança de fonte de uma edição manual na saída.
|
|
16
|
+
|
|
17
|
+
Uma rule do projeto pode declarar `replaces: core:<nome>`; a original não é
|
|
18
|
+
materializada. Uma skill substituta usa outro nome e declara `replaces: <nome>`;
|
|
19
|
+
as duas continuam instaladas, mas a lista de roteamento recomenda a substituta.
|
|
20
|
+
|
|
21
|
+
## Gates
|
|
22
|
+
|
|
23
|
+
Gates são scripts do próprio projeto. A configuração inicial usa `typecheck` e
|
|
24
|
+
`lint` no `pre-commit`, e os quatro (`typecheck`, `lint`, `test`, `build`) no
|
|
25
|
+
`publish`. O Harness não instala nem interpreta essas ferramentas: executa os
|
|
26
|
+
comandos declarados e exige que o resultado certificado seja o que será
|
|
27
|
+
commitado ou enviado.
|
|
28
|
+
|
|
29
|
+
## Fluxo Git
|
|
30
|
+
|
|
31
|
+
`dev` é a branch de trabalho e `main` é a história publicável. `integrate` roda
|
|
32
|
+
as verificações localmente e cria um merge `--no-ff` na `main`; `publish` envia
|
|
33
|
+
a `main` para `origin`. Integrar é frequente e local. Publicar é deliberado e
|
|
34
|
+
remoto.
|
|
35
|
+
|
|
36
|
+
Os hooks recusam commit direto na `main` e verificam pushes da `main`. Eles não
|
|
37
|
+
provam quem está no teclado: `--no-verify` é uma porta técnica disponível ao
|
|
38
|
+
humano, e uma rule permanente proíbe a LLM de usá-la.
|
|
39
|
+
|
|
40
|
+
## Autoridade
|
|
41
|
+
|
|
42
|
+
O humano decide produto, pode atravessar qualquer barreira e tem a palavra
|
|
43
|
+
final. A LLM deve seguir o caminho protegido, declarar discordância com motivo
|
|
44
|
+
e alternativa antes de executar, e então cumprir integralmente a decisão do
|
|
45
|
+
humano. Hooks, gates e rules reduzem risco num shell compartilhado; não são uma
|
|
46
|
+
fronteira de segurança contra uma LLM que ignore instruções.
|
|
47
|
+
|
|
48
|
+
## Estado compartilhado
|
|
49
|
+
|
|
50
|
+
Descobertas úteis à próxima sessão pertencem ao projeto, não à memória privada
|
|
51
|
+
de Claude ou Codex. Achado operacional vai para `.kuyper/STATE.md`; decisão
|
|
52
|
+
técnica durável vai para um ADR do próprio projeto; mudança de valor ou
|
|
53
|
+
comportamento volta ao Discovery ou PRD.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Referência de comandos
|
|
2
|
+
|
|
3
|
+
Todos os comandos, exceto `init`, são não interativos. Todos recusam fora de um
|
|
4
|
+
projeto inicializado. Use sempre `pnpm exec kuyper`.
|
|
5
|
+
|
|
6
|
+
## `init`
|
|
7
|
+
|
|
8
|
+
Confere Git, `package.json`, os quatro scripts e conflito de `core.hooksPath`;
|
|
9
|
+
pergunta nome, descrição e providers; mostra o plano; depois cria `.kuyper/`,
|
|
10
|
+
copia o core, materializa providers e hooks e acrescenta
|
|
11
|
+
`"prepare": "pnpm exec kuyper generate"`. Não commita. Exige TTY.
|
|
12
|
+
|
|
13
|
+
## `generate [--force]`
|
|
14
|
+
|
|
15
|
+
Lê a fonte canônica, valida schemas, substituições, colisões, core e saídas;
|
|
16
|
+
calcula todo o plano antes de escrever; materializa providers e hooks; grava o
|
|
17
|
+
lock e aponta `core.hooksPath` para `.kuyper/hooks`. `--force` descarta apenas
|
|
18
|
+
edições em saídas geradas; não autoriza core híbrido nem caminho ocupado.
|
|
19
|
+
|
|
20
|
+
## `validate`
|
|
21
|
+
|
|
22
|
+
Compara fonte, saídas, lock, hooks, `core.hooksPath` e script `prepare` sem
|
|
23
|
+
escrever. Código `0`: coerente; `1`: inconclusivo; `2`: divergente.
|
|
24
|
+
|
|
25
|
+
## `integrate`
|
|
26
|
+
|
|
27
|
+
Exige árvore limpa, checkout em `dev`, `main` ancestral e commits novos. Roda
|
|
28
|
+
`validate`, executa os gates `publish`, cria `git merge --no-ff dev` na `main`,
|
|
29
|
+
volta à `dev` e faz fast-forward até o merge. Não acessa o remoto.
|
|
30
|
+
|
|
31
|
+
## `publish`
|
|
32
|
+
|
|
33
|
+
Exige árvore limpa e commits novos na `main`. Guarda a branch atual, muda para
|
|
34
|
+
`main`, valida, roda gates `publish`, mostra o que será enviado, faz push de
|
|
35
|
+
`main` para `origin` e volta à branch original. Não integra.
|
|
36
|
+
|
|
37
|
+
## `update`
|
|
38
|
+
|
|
39
|
+
Comprova árvore limpa e geração sem achados; instala
|
|
40
|
+
`@kuyper/harness@latest`; relança o binário instalado; iguala toda a árvore
|
|
41
|
+
`.kuyper/core/` ao pacote; verifica substituições aposentadas; gera, valida e
|
|
42
|
+
relata a versão. Não faz rollback automático.
|
|
43
|
+
|
|
44
|
+
## `rule`
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm exec kuyper rule create <nome> --from <arquivo|-> [--dry-run]
|
|
48
|
+
pnpm exec kuyper rule edit <nome> [--from <arquivo|->] [--dry-run]
|
|
49
|
+
pnpm exec kuyper rule delete <nome> [--dry-run]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Mexe apenas em `.kuyper/project/rules/<nome>.md` e chama `generate`. `edit` sem
|
|
53
|
+
`--from` valida uma edição já feita no arquivo. `--from -` lê stdin.
|
|
54
|
+
|
|
55
|
+
## `skill`
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm exec kuyper skill create <nome> --from <SKILL.md|-> [--dry-run]
|
|
59
|
+
pnpm exec kuyper skill edit <nome> [--from <SKILL.md|->] [--dry-run]
|
|
60
|
+
pnpm exec kuyper skill delete <nome> [--dry-run]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Mexe em `.kuyper/project/skills/<nome>/`. `edit --from` troca somente
|
|
64
|
+
`SKILL.md` e preserva `scripts/`; `delete` remove a árvore canônica e deixa o
|
|
65
|
+
`generate` remover saídas órfãs. Comandos de capacidade não alteram `core:`.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Equivalentes manuais
|
|
2
|
+
|
|
3
|
+
Estas sequências tornam visível o que cada interface automatiza. Execute-as com
|
|
4
|
+
a árvore limpa e pare diante de qualquer erro. Quando aparece
|
|
5
|
+
`pnpm exec kuyper generate`, ele representa a materialização, a única operação
|
|
6
|
+
sem equivalente em Git ou pnpm.
|
|
7
|
+
|
|
8
|
+
## `init`
|
|
9
|
+
|
|
10
|
+
1. Crie `.kuyper/config.yaml` com providers e gates.
|
|
11
|
+
2. Copie `node_modules/@kuyper/harness/core/` inteiro para `.kuyper/core/`.
|
|
12
|
+
3. Crie `.kuyper/project/rules/sobre-o-projeto.md`, substituindo `<nome>` e
|
|
13
|
+
`<descrição>`:
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
---
|
|
17
|
+
title: Sobre este projeto
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
`<nome>` — <descrição>.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Se não houver descrição, use apenas `` `<nome>`. `` no corpo.
|
|
24
|
+
|
|
25
|
+
4. Crie `.kuyper/STATE.md` com `Atualizado por` e as seções `Onde parei`,
|
|
26
|
+
`O que descobri que não estava óbvio`, `Próximo passo concreto` e `Cuidado`.
|
|
27
|
+
5. Declare `"prepare": "pnpm exec kuyper generate"` em `package.json`.
|
|
28
|
+
6. Execute:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
git config core.hooksPath .kuyper/hooks
|
|
32
|
+
pnpm exec kuyper generate
|
|
33
|
+
pnpm exec kuyper validate
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## `generate`
|
|
37
|
+
|
|
38
|
+
A materialização de fonte canônica para dois formatos, com reconciliação do
|
|
39
|
+
lock, **não tem equivalente manual**. Essa é a capacidade própria do produto.
|
|
40
|
+
O apontamento dos hooks tem:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git config core.hooksPath .kuyper/hooks
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Ele não é versionado e desaparece num clone novo.
|
|
47
|
+
|
|
48
|
+
## `validate`
|
|
49
|
+
|
|
50
|
+
Calcule o resultado esperado da fonte e compare byte a byte todas as saídas,
|
|
51
|
+
modos dos hooks e entradas do lock; confirme ainda:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
test "$(git config --get core.hooksPath)" = ".kuyper/hooks"
|
|
55
|
+
node -e "const p=require('./package.json'); process.exit(p.scripts?.prepare === 'pnpm exec kuyper generate' ? 0 : 1)"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Na prática essa comparação é justamente o trabalho que `validate` torna
|
|
59
|
+
repetível.
|
|
60
|
+
|
|
61
|
+
## `integrate`
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git status --porcelain # tem de sair vazio
|
|
65
|
+
git switch dev
|
|
66
|
+
pnpm exec kuyper validate
|
|
67
|
+
pnpm typecheck && pnpm lint && pnpm test && pnpm build
|
|
68
|
+
git merge-base --is-ancestor main dev
|
|
69
|
+
test "$(git rev-parse main)" != "$(git rev-parse dev)"
|
|
70
|
+
git switch main
|
|
71
|
+
git merge --no-ff dev
|
|
72
|
+
git switch dev
|
|
73
|
+
git merge --ff-only main
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## `publish`
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git status --porcelain # tem de sair vazio
|
|
80
|
+
ORIG=$(git branch --show-current)
|
|
81
|
+
git switch main
|
|
82
|
+
git log --oneline origin/main..main 2>/dev/null || git log --oneline
|
|
83
|
+
pnpm exec kuyper validate
|
|
84
|
+
pnpm typecheck && pnpm lint && pnpm test && pnpm build
|
|
85
|
+
git push -u origin main
|
|
86
|
+
git switch "$ORIG"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## `update`
|
|
90
|
+
|
|
91
|
+
A ordem protege edições do core. Não mova a instalação antes das comprovações:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
git status --porcelain # tem de sair vazio
|
|
95
|
+
pnpm exec kuyper generate
|
|
96
|
+
git status --porcelain # tem de continuar vazio
|
|
97
|
+
pnpm add -D @kuyper/harness@latest
|
|
98
|
+
rm -rf .kuyper/core
|
|
99
|
+
cp -R node_modules/@kuyper/harness/core .kuyper/core
|
|
100
|
+
pnpm exec kuyper generate
|
|
101
|
+
pnpm exec kuyper validate
|
|
102
|
+
git diff .kuyper/core/
|
|
103
|
+
git diff
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Se o primeiro `generate` recusar, pare antes do `pnpm add` e do `rm`. Para
|
|
107
|
+
restaurar um core manualmente alterado à versão já instalada, iguale-o ao
|
|
108
|
+
pacote, commite e recomece desde a árvore limpa.
|
|
109
|
+
|
|
110
|
+
## `rule`
|
|
111
|
+
|
|
112
|
+
Crie ou edite `.kuyper/project/rules/<nome>.md`, ou remova o arquivo, e rode:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pnpm exec kuyper generate
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## `skill`
|
|
119
|
+
|
|
120
|
+
Crie ou edite `.kuyper/project/skills/<nome>/SKILL.md`, preservando scripts
|
|
121
|
+
irmãos quando for uma edição; para apagar, remova a árvore da skill. Depois:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pnpm exec kuyper generate
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
O `generate`, e não `skill delete`, identifica e remove as saídas órfãs.
|
|
128
|
+
|
|
129
|
+
## Como comparar
|
|
130
|
+
|
|
131
|
+
Execute comando e equivalente em cópias do mesmo commit. Ignore apenas texto de
|
|
132
|
+
relatório e metadados internos do Git; compare `package.json`, lockfile,
|
|
133
|
+
`.kuyper/`, saídas dos providers, `git config core.hooksPath`, branch atual e
|
|
134
|
+
SHAs/refs que o procedimento deve alterar. A certificação executada fica em
|
|
135
|
+
`docs/certificacao/` no repositório-fonte.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# O método: Discovery, PRD, Architect e Dev
|
|
2
|
+
|
|
3
|
+
O método separa quatro perguntas que costumam ser misturadas: vale construir,
|
|
4
|
+
o que deve acontecer, como construir e qual bloco executar agora.
|
|
5
|
+
|
|
6
|
+
## Discovery
|
|
7
|
+
|
|
8
|
+
Explora uma ideia e define fronteira, princípios, exclusões, itens adiados com
|
|
9
|
+
gatilho, critérios de sucesso e riscos. A skill organiza a análise; **o humano
|
|
10
|
+
decide se vale construir**. Encerra com aprovação ou decisão de não prosseguir.
|
|
11
|
+
Quando aprovado, entrega ao PRD.
|
|
12
|
+
|
|
13
|
+
Não é necessário quando o humano já tomou uma decisão contida e não existe
|
|
14
|
+
incerteza real de valor, usuário ou fronteira. Registre essa decisão como entrada
|
|
15
|
+
equivalente; não fabrique uma Discovery retrospectiva para cumprir ritual.
|
|
16
|
+
|
|
17
|
+
## PRD
|
|
18
|
+
|
|
19
|
+
Transforma a fronteira aprovada em comportamento observável: capacidades,
|
|
20
|
+
recusas com rota de recuperação, mensagens e critérios de aceitação. A skill
|
|
21
|
+
decide a organização do contrato; **o humano decide o que o produto faz**.
|
|
22
|
+
Encerra aprovado e entrega ao Architect.
|
|
23
|
+
|
|
24
|
+
Não é necessário para uma correção cujo comportamento esperado já está decidido
|
|
25
|
+
e rastreável. É necessário quando a pergunta ainda é “o que deveria acontecer?”.
|
|
26
|
+
|
|
27
|
+
## Architect
|
|
28
|
+
|
|
29
|
+
Decide escolhas técnicas, registra ADRs do projeto para decisões contidas,
|
|
30
|
+
SPECs para protocolos que atravessam blocos e um BUILD com ordem e critério de
|
|
31
|
+
pronto. Não
|
|
32
|
+
pode mudar silenciosamente o PRD: um buraco de comportamento volta ao humano e
|
|
33
|
+
ao PRD. Entrega um bloco por vez à skill Dev.
|
|
34
|
+
|
|
35
|
+
Não é necessário quando a mudança não cria decisão arquitetural nova e cabe
|
|
36
|
+
integralmente num bloco já aprovado. Um detalhe de implementação reversível pode
|
|
37
|
+
ser decidido no código; uma escolha durável ou transversal precisa de registro.
|
|
38
|
+
|
|
39
|
+
## Dev
|
|
40
|
+
|
|
41
|
+
Executa um bloco concreto, escreve código e testes, roda gates, audita cada item
|
|
42
|
+
de pronto e atualiza o STATE. Decide apenas dentro dos contratos herdados. Se
|
|
43
|
+
descobrir que PRD, SPEC ou decisão humana estão errados, declara a discordância,
|
|
44
|
+
propõe alternativa e espera a decisão antes de desviar.
|
|
45
|
+
|
|
46
|
+
## Regra prática
|
|
47
|
+
|
|
48
|
+
Use só a etapa que resolve a incerteza existente:
|
|
49
|
+
|
|
50
|
+
- “Vale fazer e qual é a fronteira?” → Discovery.
|
|
51
|
+
- “Qual comportamento e como sabemos que terminou?” → PRD.
|
|
52
|
+
- “Qual desenho técnico e ordem de construção?” → Architect.
|
|
53
|
+
- “O comportamento e o bloco já estão decididos” → Dev.
|
|
54
|
+
|
|
55
|
+
Pular uma etapa porque não há decisão para ela é enxuto. Pular uma decisão que
|
|
56
|
+
ainda está aberta apenas a empurra para o código, onde fica implícita.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Quando algo falha
|
|
2
|
+
|
|
3
|
+
O Harness faz preflight antes de escrever e torna estado parcial visível. Não
|
|
4
|
+
faz rollback automático.
|
|
5
|
+
|
|
6
|
+
## Recusa antes de escrever
|
|
7
|
+
|
|
8
|
+
Nada foi alterado. Siga os comandos mostrados pela recusa e execute novamente.
|
|
9
|
+
|
|
10
|
+
## Interrupção durante uma saída do `generate`
|
|
11
|
+
|
|
12
|
+
O alvo fica antigo inteiro ou novo inteiro; sucesso não é reportado. Um
|
|
13
|
+
`.kuyper-tmp` pode permanecer, mas leitores o ignoram. Corrija a causa e rode:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm exec kuyper generate
|
|
17
|
+
pnpm exec kuyper validate
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
A próxima materialização bem-sucedida remove todos os temporários do Harness.
|
|
21
|
+
|
|
22
|
+
## Interrupção do `init` antes da base completa
|
|
23
|
+
|
|
24
|
+
A base incompleta recusa. Como este é o bootstrap e ainda não há estado válido,
|
|
25
|
+
apague apenas a base parcial e recomece:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
rm -rf .kuyper
|
|
29
|
+
pnpm exec kuyper init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Interrupção enquanto `update` iguala o core
|
|
33
|
+
|
|
34
|
+
A árvore híbrida aparece em `git status`; `generate` recusa sem mudar saídas ou
|
|
35
|
+
lock. Conclua a igualação inteira ao pacote instalado:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
rm -rf .kuyper/core
|
|
39
|
+
cp -R node_modules/@kuyper/harness/core .kuyper/core
|
|
40
|
+
pnpm exec kuyper generate
|
|
41
|
+
pnpm exec kuyper validate
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Se surgir um `replaces:` órfão, edite ou remova a capacidade apontada pela R9 e
|
|
45
|
+
rode os dois últimos comandos novamente.
|
|
46
|
+
|
|
47
|
+
## Falha depois de um push aceito
|
|
48
|
+
|
|
49
|
+
O remoto pode já ter recebido a `main`. Não repita o push às cegas: rode
|
|
50
|
+
`pnpm exec kuyper publish`; ele compara `origin/main` e reconhece o que já foi
|
|
51
|
+
publicado.
|
|
52
|
+
|
|
53
|
+
## Falha do pnpm no `update`
|
|
54
|
+
|
|
55
|
+
`.kuyper/core/` ainda está íntegro. Escolha uma rota:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Voltar à versão do último commit
|
|
59
|
+
git restore package.json pnpm-lock.yaml
|
|
60
|
+
pnpm install
|
|
61
|
+
|
|
62
|
+
# Ou insistir na nova
|
|
63
|
+
pnpm add -D @kuyper/harness@latest
|
|
64
|
+
pnpm exec kuyper update
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Falha do `update` depois do pnpm
|
|
68
|
+
|
|
69
|
+
`package.json` e o lockfile já mudaram. O relatório informa o passo. Se a
|
|
70
|
+
instalação concluiu mas o core ficou antigo ou híbrido, use:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
rm -rf .kuyper/core
|
|
74
|
+
cp -R node_modules/@kuyper/harness/core .kuyper/core
|
|
75
|
+
pnpm exec kuyper generate
|
|
76
|
+
pnpm exec kuyper validate
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Falha do `integrate` depois do merge
|
|
80
|
+
|
|
81
|
+
A `main` já avançou corretamente e a `dev` ficou atrás. Conclua:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git switch dev
|
|
85
|
+
git merge --ff-only main
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Push recusado porque um gate alterou a árvore
|
|
89
|
+
|
|
90
|
+
Nada saiu da máquina; `main` local e remota estão intactas. A correção do gate
|
|
91
|
+
está viva e não commitada em `git status`. Leve-a para `dev`, commite onde o
|
|
92
|
+
`pre-commit` pode convergir e integre:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git switch dev
|
|
96
|
+
git add -A
|
|
97
|
+
git commit
|
|
98
|
+
pnpm exec kuyper integrate
|
|
99
|
+
pnpm exec kuyper publish
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Não use `git push --no-verify` nessa situação.** Ele enviaria o commit sem a
|
|
103
|
+
correção que o gate acabou de produzir.
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kuyper/harness",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Copiloto de desenvolvimento: fonte canônica de rules e skills para Claude e Codex, gates e fluxo Git.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kuyper": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"core",
|
|
13
|
+
"docs/guia"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20",
|
|
17
|
+
"pnpm": ">=9"
|
|
18
|
+
},
|
|
19
|
+
"packageManager": "pnpm@10.30.3",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"test": "vitest run --passWithNoTests",
|
|
24
|
+
"build": "tsc -p tsconfig.build.json",
|
|
25
|
+
"pretest": "tsc -p tsconfig.build.json"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@eslint/js": "^10.0.1",
|
|
29
|
+
"@types/node": "^26.3.0",
|
|
30
|
+
"eslint": "^10.9.1",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"typescript-eslint": "^8.68.0",
|
|
33
|
+
"vitest": "^4.1.11"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"yaml": "^2.9.0"
|
|
37
|
+
}
|
|
38
|
+
}
|