@kuyper/harness 0.1.0 → 0.1.1

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 CHANGED
@@ -18,6 +18,14 @@ Requisitos: Node.js 20 ou posterior, pnpm 9 ou posterior, Git e um repositório
18
18
  remoto chamado `origin`. O MVP assume uma pessoa, branches literais `main` e
19
19
  `dev`, e GitHub como remoto.
20
20
 
21
+ **Versão mínima suportada e versão de certificação são coisas diferentes.** O
22
+ mínimo acima é o que o pacote declara em `engines`. A jornada do guia 1 foi
23
+ executada literalmente com **pnpm 12.4.2** — é essa a versão reproduzível do
24
+ bootstrap. Atualize o pnpm **antes** de criar o projeto: depois do `pnpm init` a
25
+ versão fica fixada no campo `packageManager` do projeto, e trocá-la no meio do
26
+ caminho quebra os quatro gates. O passo 0 de
27
+ [Começar um projeto](docs/guia/01-comecar.md) mostra a ordem correta.
28
+
21
29
  As oito interfaces públicas são `init`, `generate`, `validate`, `integrate`,
22
30
  `publish`, `update`, `rule` e `skill`. Invoque sempre o binário instalado no
23
31
  projeto:
package/dist/init.js CHANGED
@@ -229,7 +229,9 @@ export async function init(options = {}) {
229
229
  await writeFileAtomic(join(kuyperDir, 'project', 'rules', 'sobre-o-projeto.md'), projectRuleContent);
230
230
  await writeFileAtomic(join(kuyperDir, 'STATE.md'), stateMd(todayIso()));
231
231
  await copyCoreTree(options.packageCoreDir ?? packageCorePath(), join(kuyperDir, 'core'));
232
- await generate(options.packageCoreDir === undefined ? { projectRoot } : { projectRoot, packageCoreDir: options.packageCoreDir });
232
+ await generate(options.packageCoreDir === undefined
233
+ ? { projectRoot, silent: true }
234
+ : { projectRoot, packageCoreDir: options.packageCoreDir, silent: true });
233
235
  const pkgWithPrepare = { ...pkg, scripts: { ...pkg.scripts, prepare: PREPARE_SCRIPT } };
234
236
  await writeFileAtomic(packageJsonPath, `${JSON.stringify(pkgWithPrepare, null, 2)}\n`);
235
237
  const claudeEnabled = providers.includes('claude');
package/dist/update.js CHANGED
@@ -315,7 +315,11 @@ export async function update(options = {}) {
315
315
  refuseR21CoreEdited(diffKeys(L, D).map((k) => join(CORE_REL, k)));
316
316
  }
317
317
  }
318
- await generate({ projectRoot, ...(options.packageCoreDir !== undefined ? { packageCoreDir: options.packageCoreDir } : {}) });
318
+ await generate({
319
+ projectRoot,
320
+ silent: true,
321
+ ...(options.packageCoreDir !== undefined ? { packageCoreDir: options.packageCoreDir } : {}),
322
+ });
319
323
  const dirtyAfterGenerate = await dirtyPaths(projectRoot);
320
324
  if (dirtyAfterGenerate.length > 0)
321
325
  refuseR21PendingWrites(dirtyAfterGenerate);
@@ -413,7 +417,7 @@ export async function continueUpdate(options = {}) {
413
417
  refuseOrphanReplaces(rule, target);
414
418
  }
415
419
  }
416
- const generateReport = await generate({ projectRoot, packageCoreDir });
420
+ const generateReport = await generate({ projectRoot, packageCoreDir, silent: true });
417
421
  const validateReport = await validate({ projectRoot, packageCoreDir, silent: true });
418
422
  if (validateReport.code !== 0) {
419
423
  refuseValidateInconsistent(validateReport.code === 1 ? validateReport.reason : validateReport.findings.join('; '));
@@ -4,16 +4,171 @@ Esta é a jornada completa, do repositório vazio à primeira publicação. O
4
4
  Harness não instala TypeScript, ESLint ou Vitest: os passos abaixo fazem isso
5
5
  explicitamente antes do `init`.
6
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"`:
7
+ **Como ler os blocos de comando:** cada bloco é uma colagem copie inteiro,
8
+ cole, execute. Onde é preciso ler a saída antes de continuar, o bloco vem
9
+ sozinho e o texto diz o que observar. Nenhum bloco para sozinho no meio se um
10
+ comando falhar, então confira o resultado de um antes de colar o próximo.
11
+
12
+ ## 0. Preparar o ambiente
13
+
14
+ Três coisas precisam estar prontas **antes do primeiro commit**: a versão do
15
+ pnpm, a identidade do Git e a autenticação do GitHub. Mudar qualquer uma delas
16
+ depois não altera o que já foi feito — commits já criados mantêm o autor
17
+ antigo, e trocar de pnpm no meio do bootstrap quebra os gates.
18
+
19
+ ### Versão do pnpm
20
+
21
+ São duas coisas diferentes, e confundi-las quebra o bootstrap:
22
+
23
+ - **Versão mínima suportada** — pnpm 9 ou posterior, o que `@kuyper/harness`
24
+ declara em `engines`.
25
+ - **Versão que certificou esta jornada** — pnpm 12.4.2. É a versão com que os
26
+ comandos deste guia foram executados literalmente, do repositório vazio ao
27
+ primeiro `publish`.
28
+
29
+ Atualize o pnpm **agora**, fora de qualquer diretório de projeto, e confirme o
30
+ resultado antes de seguir:
31
+
32
+ ```bash
33
+ cd ~
34
+ curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=12.4.2 sh -
35
+ ```
36
+
37
+ Agora recarregue o shell, para que o `PATH` enxergue a instalação nova. Este
38
+ comando **substitui o processo do seu terminal**, então ele vai sozinho — nada
39
+ colado depois dele na mesma leva chegaria a rodar:
40
+
41
+ ```bash
42
+ exec zsh -l
43
+ ```
44
+
45
+ Com o shell novo, confirme:
46
+
47
+ ```bash
48
+ which pnpm
49
+ pnpm --version
50
+ ```
51
+
52
+ `source ~/.zshrc && rehash` tem o mesmo efeito de recarregar e não troca o
53
+ processo. Se `pnpm --version` não imprimir a versão que você acabou de instalar
54
+ — ou disser `command not found` —, a rota está em
55
+ [Quando algo falha](06-falhas.md#pnpm-not-found-depois-de-reinstalar).
56
+
57
+ **Não rode `pnpm self-update` durante a jornada.** A partir do `pnpm init` do
58
+ passo 2 o projeto passa a ter um campo `packageManager` fixando a versão, e
59
+ dentro de um projeto com esse campo o `self-update` não atualiza a instalação
60
+ global: ele só reescreve o pin. O comando seguinte tenta então baixar e alternar
61
+ sozinho para a versão nova, e uma troca incompleta derruba os quatro gates antes
62
+ mesmo de eles chegarem a rodar. A recuperação está em
63
+ [Quando algo falha](06-falhas.md#failed-to-switch-pnpm-depois-de-um-self-update).
64
+
65
+ Trocar de versão depois é legítimo — mas como mudança explícita do ambiente do
66
+ projeto: atualize a instalação fora dele, confirme com `pnpm --version` e ajuste
67
+ o `packageManager` num commit próprio, nunca como resposta automática ao aviso
68
+ de atualização.
69
+
70
+ ### Identidade do Git
71
+
72
+ Se a opção **Keep my email addresses private** estiver ativa no GitHub, abra
73
+ [Settings > Emails](https://github.com/settings/emails), copie o endereço
74
+ `noreply` mostrado pela própria página e use-o no Git. Ele costuma ter o formato
75
+ `ID+USUARIO@users.noreply.github.com`, mas copie o valor exato em vez de tentar
76
+ adivinhar o ID:
77
+
78
+ ```bash
79
+ git config --global user.name "Seu Nome"
80
+ git config --global user.email "ID+SEU-USUARIO@users.noreply.github.com"
81
+ ```
82
+
83
+ Se o seu e-mail pode aparecer publicamente nos commits, você pode configurá-lo
84
+ no lugar do `noreply`. Em qualquer caso, estes comandos precisam imprimir os
85
+ valores corretos antes de continuar:
11
86
 
12
87
  ```bash
13
88
  git config --global user.name
14
89
  git config --global user.email
15
90
  ```
16
91
 
92
+ ### Autenticação do GitHub
93
+
94
+ **HTTPS e SSH são dois métodos independentes.** Estar logado no navegador, ou ter
95
+ o GitHub CLI autenticado, **não** configura uma chave SSH. Numa máquina sem
96
+ chave, um clone `git@github.com:...` falha assim:
97
+
98
+ ```
99
+ git@github.com: Permission denied (publickey).
100
+ ```
101
+
102
+ Escolha um método aqui e use **o mesmo** na URL do clone do passo 1. Esta jornada
103
+ usa HTTPS com o GitHub CLI, porque o login fica armazenado e não precisa ser
104
+ repetido a cada operação:
105
+
106
+ ```bash
107
+ gh auth login -h github.com -p https -w
108
+ ```
109
+
110
+ Este comando faz perguntas e abre o navegador, então vai sozinho. `-w` é o que
111
+ abre o navegador para autorizar; `-p https` diz ao `gh` que as operações de Git
112
+ deste host usam HTTPS, e ele configura o Git para reaproveitar a credencial
113
+ guardada.
114
+
115
+ Terminado o fluxo no navegador, confirme:
116
+
117
+ ```bash
118
+ gh auth status
119
+ ```
120
+
121
+ Precisa mostrar `Logged in to github.com` antes de você seguir. Feito isso,
122
+ clone e push HTTPS funcionam sem pedir senha.
123
+
124
+ <details>
125
+ <summary>Alternativa: SSH</summary>
126
+
127
+ SSH também funciona, mas exige uma chave que exista na máquina **e** esteja
128
+ registrada no GitHub. Verifique as duas coisas antes de clonar:
129
+
130
+ ```bash
131
+ ls ~/.ssh/id_ed25519.pub
132
+ ```
133
+
134
+ ```bash
135
+ ssh -T git@github.com
136
+ ```
137
+
138
+ `ssh -T` vai sozinho: na primeira conexão ele pede confirmação da impressão
139
+ digital do servidor. Precisa responder
140
+ `Hi SEU-USUARIO! You've successfully authenticated`.
141
+
142
+ Se a chave não existir, ou se o GitHub não a reconhecer, crie uma. O
143
+ `ssh-keygen` faz três perguntas — onde salvar e a senha da chave, duas vezes —,
144
+ então também vai sozinho:
145
+
146
+ ```bash
147
+ ssh-keygen -t ed25519 -C "seu-email"
148
+ ```
149
+
150
+ Com a chave criada, registre-a no agente e no GitHub:
151
+
152
+ ```bash
153
+ eval "$(ssh-agent -s)"
154
+ ssh-add ~/.ssh/id_ed25519
155
+ gh ssh-key add ~/.ssh/id_ed25519.pub --title "$(hostname)"
156
+ ```
157
+
158
+ E confirme:
159
+
160
+ ```bash
161
+ ssh -T git@github.com
162
+ ```
163
+
164
+ Sem o GitHub CLI, registre a chave à mão em
165
+ [Settings > SSH and GPG keys](https://github.com/settings/keys), colando o
166
+ conteúdo de `~/.ssh/id_ed25519.pub`. Só depois de `ssh -T` autenticar, troque a
167
+ URL do clone do passo 1 por
168
+ `git@github.com:${GITHUB_USER}/${PROJECT_NAME}.git`.
169
+
170
+ </details>
171
+
17
172
  ## 1. Criar e clonar o repositório
18
173
 
19
174
  No GitHub, crie um repositório **privado e vazio**, sem README, `.gitignore` ou
@@ -22,23 +177,56 @@ licença. Troque os dois valores abaixo e execute:
22
177
  ```bash
23
178
  export GITHUB_USER="seu-usuario"
24
179
  export PROJECT_NAME="seu-projeto"
25
- git clone "git@github.com:${GITHUB_USER}/${PROJECT_NAME}.git"
180
+ git clone "https://github.com/${GITHUB_USER}/${PROJECT_NAME}.git"
26
181
  cd "$PROJECT_NAME"
27
- git switch -c main
182
+
183
+ if [ "$(git branch --show-current)" != "main" ]; then
184
+ git switch -c main
185
+ fi
28
186
  ```
29
187
 
30
- Um clone vazio pode avisar que não tem branch; `git switch -c main` é esperado.
188
+ O clone de um repositório vazio deixa o checkout numa branch **não nascida**
189
+ normalmente `main`, porque é o padrão do GitHub. `git status -sb` mostra:
190
+
191
+ ```
192
+ ## No commits yet on main...origin/main [gone]
193
+ ```
194
+
195
+ Isso é esperado, e não há nada a corrigir: `[gone]` só diz que o remoto ainda não
196
+ tem nenhum commit para essa branch rastrear. O `[gone]` desaparece sozinho no
197
+ primeiro `publish`.
198
+
199
+ O objetivo da condição acima é apenas **garantir que o primeiro commit nasça em
200
+ `main`**, não criar a branch a qualquer custo. Ela cobre os dois casos que
201
+ existem de verdade: um remoto cujo padrão não é `main` (o clone cai numa
202
+ `master` não nascida, e a branch precisa mesmo ser criada) e uma segunda
203
+ execução destes passos num repositório que já tem commits, onde
204
+ `git switch -c main` falharia com `fatal: a branch named 'main' already exists`.
31
205
 
32
206
  ## 2. Criar o projeto TypeScript e os quatro gates
33
207
 
34
- Os comandos abaixo criam uma configuração mínima que realmente compila, passa
35
- no lint e aceita um projeto ainda sem testes:
208
+ Quatro blocos, nesta ordem. O primeiro cria o projeto e baixa as ferramentas:
36
209
 
37
210
  ```bash
38
211
  pnpm init
39
212
  pnpm pkg set type=module
40
213
  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
214
+ ```
215
+
216
+ Este é o único bloco do passo 2 que usa a rede, e é onde uma matriz de versões
217
+ incompatível apareceria. Confirme que a instalação terminou sem erro antes de
218
+ seguir.
41
219
 
220
+ O `pnpm init` grava em `packageManager` a versão de pnpm que você confirmou no
221
+ passo 0 — é ela que passa a valer para este projeto, em qualquer máquina. Em
222
+ pnpm 12 ele grava também `devEngines.packageManager` com `"onFail": "download"`,
223
+ que é exatamente a autorização para o pnpm baixar e trocar de versão sozinho
224
+ quando o pin não bate com a instalação. Por isso o passo 0 insiste na ordem: com
225
+ a versão certa instalada antes, essa troca automática nunca precisa acontecer.
226
+
227
+ O segundo bloco só escreve arquivos — nenhum comando aqui acessa a rede:
228
+
229
+ ```bash
42
230
  cat > .gitignore <<'EOF'
43
231
  node_modules/
44
232
  dist/
@@ -78,26 +266,42 @@ export function hello(name: string): string {
78
266
  return `Olá, ${name}.`;
79
267
  }
80
268
  EOF
269
+ ```
270
+
271
+ O terceiro declara os quatro scripts no `package.json`:
81
272
 
273
+ ```bash
82
274
  pnpm pkg set scripts.typecheck="tsc --noEmit"
83
275
  pnpm pkg set scripts.lint="eslint ."
84
276
  pnpm pkg set scripts.test="vitest run --passWithNoTests"
85
277
  pnpm pkg set scripts.build="tsc"
86
-
87
- pnpm typecheck
88
- pnpm lint
89
- pnpm test
90
- pnpm build
91
278
  ```
92
279
 
93
280
  O `--passWithNoTests` é deliberado: o gate existe desde o primeiro commit, antes
94
281
  de o primeiro teste existir.
95
282
 
283
+ O quarto roda os quatro gates. Os `&&` fazem a sequência **parar no primeiro que
284
+ falhar**, deixando o erro como a última coisa na tela — é a mesma forma que o
285
+ Harness usa quando executa os gates por você:
286
+
287
+ ```bash
288
+ pnpm typecheck && pnpm lint && pnpm test && pnpm build
289
+ ```
290
+
291
+ Os quatro precisam passar antes de você seguir para o passo 3.
292
+
96
293
  ## 3. Instalar o Harness e criar o commit raiz
97
294
 
98
295
  ```bash
99
296
  pnpm add -D @kuyper/harness
100
297
  pnpm exec kuyper --version
298
+ ```
299
+
300
+ A versão impressa confirma que o binário do projeto responde. A invocação é
301
+ sempre `pnpm exec kuyper`: o `pnpm add -D` instala o binário em
302
+ `node_modules/.bin/`, que não está no `PATH` do seu terminal.
303
+
304
+ ```bash
101
305
  git add -A
102
306
  git commit -m "chore: inicia projeto"
103
307
  git switch -c dev
@@ -121,14 +325,31 @@ Revise `.kuyper/`, `CLAUDE.md`, `AGENTS.md`, `.claude/skills/`,
121
325
 
122
326
  ```bash
123
327
  pnpm exec kuyper validate
328
+ ```
329
+
330
+ O relatório precisa dizer que a materialização está coerente antes de você
331
+ commitar.
332
+
333
+ ```bash
124
334
  git add -A
125
335
  git commit -m "chore: inicializa Kuyper Harness"
336
+ ```
337
+
338
+ Este commit passa pelos hooks que o `init` acabou de criar — é a primeira vez
339
+ que os gates rodam sozinhos.
340
+
341
+ ```bash
126
342
  pnpm exec kuyper integrate
343
+ ```
344
+
345
+ ```bash
127
346
  pnpm exec kuyper publish
128
347
  ```
129
348
 
130
- Ao final, `main` local e `origin/main` apontam para o merge criado pelo
131
- `integrate`, e o checkout volta para `dev`.
349
+ `integrate` e `publish` vêm separados de propósito: cada um roda os gates e
350
+ imprime um relatório que vale ler antes do próximo. Ao final, `main` local e
351
+ `origin/main` apontam para o merge criado pelo `integrate`, e o checkout volta
352
+ para `dev`.
132
353
 
133
354
  ## 5. Trabalhar depois do primeiro publish
134
355
 
@@ -148,6 +369,13 @@ Quando uma segunda versão do Harness existir na npm:
148
369
 
149
370
  ```bash
150
371
  pnpm exec kuyper update
372
+ ```
373
+
374
+ O relatório diz qual versão entrou e quais capacidades mudaram. Revise o
375
+ `git diff` de `.kuyper/core/` antes de commitar — é para isso que a atualização
376
+ passa pelo Git:
377
+
378
+ ```bash
151
379
  git add -A
152
380
  git commit -m "chore: atualiza Kuyper Harness"
153
381
  ```
@@ -3,6 +3,153 @@
3
3
  O Harness faz preflight antes de escrever e torna estado parcial visível. Não
4
4
  faz rollback automático.
5
5
 
6
+ As três primeiras rotas abaixo são falhas do **ambiente**, durante o bootstrap do
7
+ guia [Começar um projeto](01-comecar.md). Acontecem antes de o Harness existir no
8
+ projeto e nenhuma delas é uma recusa dele.
9
+
10
+ ## Clone recusado com `Permission denied (publickey)`
11
+
12
+ ```
13
+ git@github.com: Permission denied (publickey).
14
+ ```
15
+
16
+ O clone usou SSH numa máquina sem chave SSH configurada. **HTTPS e SSH são
17
+ autenticações independentes**: estar logado no navegador, ou ter o GitHub CLI
18
+ autenticado, não cria nem registra uma chave SSH.
19
+
20
+ Nada foi criado no disco — o clone falhou antes de escrever. Escolha um método e
21
+ clone de novo com a URL correspondente. HTTPS com o GitHub CLI, que é o que esta
22
+ jornada usa:
23
+
24
+ ```bash
25
+ gh auth login -h github.com -p https -w
26
+ ```
27
+
28
+ ```bash
29
+ gh auth status
30
+ git clone "https://github.com/${GITHUB_USER}/${PROJECT_NAME}.git"
31
+ ```
32
+
33
+ Ou SSH, configurando a chave de verdade antes. O `ssh-keygen` faz três perguntas
34
+ e o `ssh -T` pede confirmação da impressão digital na primeira conexão, então os
35
+ dois vão sozinhos:
36
+
37
+ ```bash
38
+ ssh-keygen -t ed25519 -C "seu-email"
39
+ ```
40
+
41
+ ```bash
42
+ eval "$(ssh-agent -s)"
43
+ ssh-add ~/.ssh/id_ed25519
44
+ gh ssh-key add ~/.ssh/id_ed25519.pub --title "$(hostname)"
45
+ ```
46
+
47
+ ```bash
48
+ ssh -T git@github.com
49
+ ```
50
+
51
+ ```bash
52
+ git clone "git@github.com:${GITHUB_USER}/${PROJECT_NAME}.git"
53
+ ```
54
+
55
+ `ssh -T` precisa responder `Hi SEU-USUARIO! You've successfully authenticated`
56
+ antes do clone. Sem o GitHub CLI, registre `~/.ssh/id_ed25519.pub` à mão em
57
+ [Settings > SSH and GPG keys](https://github.com/settings/keys).
58
+
59
+ Se o repositório já tiver sido clonado com a URL errada, troque o remoto em vez
60
+ de clonar de novo:
61
+
62
+ ```bash
63
+ git remote set-url origin "https://github.com/${GITHUB_USER}/${PROJECT_NAME}.git"
64
+ git remote -v
65
+ ```
66
+
67
+ ## `Failed to switch pnpm` depois de um `self-update`
68
+
69
+ ```
70
+ ERROR Failed to switch pnpm to v12.4.2
71
+ spawnSync .../12.4.2/bin/pnpm Unknown system error -8
72
+ ```
73
+
74
+ O `pnpm self-update` foi executado **dentro** de um projeto que já tem
75
+ `packageManager` no `package.json`. Nesse caso ele não atualiza a instalação
76
+ global: só reescreve o pin do projeto. O comando seguinte tenta então baixar e
77
+ alternar sozinho para a versão nova, e essa instalação gerenciada ficou
78
+ incompleta — o arquivo colocado no lugar do executável nativo é o fallback
79
+ textual e sem shebang do `@pnpm/exe`, não um binário da sua plataforma. Os
80
+ quatro gates falham antes mesmo de chegarem a rodar TypeScript ou ESLint.
81
+
82
+ **Não volte o `packageManager` para a versão antiga.** Isso esconde a
83
+ atualização em vez de concluí-la. Repare a versão que o pin já escolheu,
84
+ reinstalando-a pelo instalador standalone oficial, **fora do projeto**:
85
+
86
+ ```bash
87
+ cd ~
88
+ curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=12.4.2 sh -
89
+ ```
90
+
91
+ Recarregue o shell. `exec` substitui o processo do terminal, então este comando
92
+ vai sozinho — o que for colado junto, depois dele, não chega a rodar:
93
+
94
+ ```bash
95
+ exec zsh -l
96
+ ```
97
+
98
+ Confirme no shell novo:
99
+
100
+ ```bash
101
+ which pnpm
102
+ pnpm --version
103
+ ```
104
+
105
+ `pnpm --version` precisa imprimir a mesma versão que está no `packageManager`
106
+ antes de você voltar ao projeto. Então reinstale as dependências e rode os
107
+ quatro gates:
108
+
109
+ ```bash
110
+ cd ~/Developer/seu-projeto
111
+ pnpm install
112
+ pnpm typecheck
113
+ pnpm lint
114
+ pnpm test
115
+ pnpm build
116
+ ```
117
+
118
+ `pnpm --version` dentro do projeto e fora dele pode dar respostas diferentes, e
119
+ isso é normal: dentro de um projeto o pnpm respeita o `packageManager`; fora,
120
+ responde a versão que está instalada. Compare sempre a versão **de fora** com o
121
+ valor do pin.
122
+
123
+ ## `pnpm not found` depois de reinstalar
124
+
125
+ O instalador standalone coloca o binário em `$PNPM_HOME/bin`, mas um shell que
126
+ já estava aberto continua com o `PATH` antigo — em instalações mais velhas,
127
+ `$PNPM_HOME` sem o `/bin`. Recarregue o shell:
128
+
129
+ ```bash
130
+ exec zsh -l
131
+ ```
132
+
133
+ Ou, sem trocar de processo:
134
+
135
+ ```bash
136
+ source ~/.zshrc
137
+ rehash
138
+ ```
139
+
140
+ Confira onde o binário está e o que o shell resolve:
141
+
142
+ ```bash
143
+ ls "$PNPM_HOME/bin/pnpm"
144
+ which pnpm
145
+ pnpm --version
146
+ ```
147
+
148
+ Se `ls` encontra o arquivo e `which pnpm` não encontra nada, o problema é o
149
+ `PATH`: o bloco `# pnpm` do seu `~/.zshrc` precisa exportar
150
+ `PATH="$PNPM_HOME/bin:$PATH"`. O instalador reescreve esse bloco, e abrir um
151
+ terminal novo depois disso basta.
152
+
6
153
  ## Recusa antes de escrever
7
154
 
8
155
  Nada foi alterado. Siga os comandos mostrados pela recusa e execute novamente.
@@ -50,6 +197,50 @@ O remoto pode já ter recebido a `main`. Não repita o push às cegas: rode
50
197
  `pnpm exec kuyper publish`; ele compara `origin/main` e reconhece o que já foi
51
198
  publicado.
52
199
 
200
+ ## Push recusado pelo GitHub por e-mail privado (`GH007`)
201
+
202
+ ```
203
+ remote: error: GH007: Your push would publish a private email address.
204
+ ```
205
+
206
+ O GitHub recusou o push antes de receber os commits. Não é necessário tornar seu
207
+ e-mail público, desativar a proteção nem recriar o projeto.
208
+
209
+ Primeiro, abra [Settings > Emails](https://github.com/settings/emails), copie o
210
+ endereço `noreply` exato mostrado pelo GitHub e configure-o:
211
+
212
+ ```bash
213
+ git config --global user.name "Seu Nome"
214
+ git config --global user.email "ID+SEU-USUARIO@users.noreply.github.com"
215
+ ```
216
+
217
+ Essa configuração vale somente para commits futuros. Se o `GH007` aconteceu no
218
+ **primeiro publish** deste guia, o remoto ainda está vazio e os commits locais
219
+ podem ser corrigidos sem refazer a jornada. O comando abaixo preserva arquivos,
220
+ mensagens, branches e merges, mas troca o autor e o responsável por todos os
221
+ commits locais. Substitua os quatro valores antes de executar:
222
+
223
+ ```bash
224
+ FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --env-filter '
225
+ export GIT_AUTHOR_NAME="Seu Nome"
226
+ export GIT_AUTHOR_EMAIL="ID+SEU-USUARIO@users.noreply.github.com"
227
+ export GIT_COMMITTER_NAME="Seu Nome"
228
+ export GIT_COMMITTER_EMAIL="ID+SEU-USUARIO@users.noreply.github.com"
229
+ ' -- --all
230
+ ```
231
+
232
+ Confira autor e responsável pelos commits e tente novamente:
233
+
234
+ ```bash
235
+ git log main dev --format='%h autor=%an <%ae> commit=%cn <%ce>'
236
+ pnpm exec kuyper publish
237
+ ```
238
+
239
+ Os identificadores dos commits mudam porque o e-mail faz parte deles. Como o
240
+ push foi recusado, não é necessário usar `--force`. **Não use essa recuperação
241
+ indiscriminadamente se algum desses commits já estiver num remoto compartilhado**;
242
+ reescrever commits já publicados exige combinar a mudança com as outras pessoas.
243
+
53
244
  ## Falha do pnpm no `update`
54
245
 
55
246
  `.kuyper/core/` ainda está íntegro. Escolha uma rota:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kuyper/harness",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Copiloto de desenvolvimento: fonte canônica de rules e skills para Claude e Codex, gates e fluxo Git.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -16,14 +16,6 @@
16
16
  "node": ">=20",
17
17
  "pnpm": ">=9"
18
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
19
  "devDependencies": {
28
20
  "@eslint/js": "^10.0.1",
29
21
  "@types/node": "^26.3.0",
@@ -34,5 +26,12 @@
34
26
  },
35
27
  "dependencies": {
36
28
  "yaml": "^2.9.0"
29
+ },
30
+ "scripts": {
31
+ "typecheck": "tsc --noEmit",
32
+ "lint": "eslint .",
33
+ "test": "vitest run --passWithNoTests",
34
+ "build": "tsc -p tsconfig.build.json",
35
+ "pretest": "tsc -p tsconfig.build.json"
37
36
  }
38
- }
37
+ }