@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 +8 -0
- package/dist/init.js +3 -1
- package/dist/update.js +6 -2
- package/docs/guia/01-comecar.md +244 -16
- package/docs/guia/06-falhas.md +191 -0
- package/package.json +9 -10
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
|
|
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({
|
|
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('; '));
|
package/docs/guia/01-comecar.md
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 "
|
|
180
|
+
git clone "https://github.com/${GITHUB_USER}/${PROJECT_NAME}.git"
|
|
26
181
|
cd "$PROJECT_NAME"
|
|
27
|
-
|
|
182
|
+
|
|
183
|
+
if [ "$(git branch --show-current)" != "main" ]; then
|
|
184
|
+
git switch -c main
|
|
185
|
+
fi
|
|
28
186
|
```
|
|
29
187
|
|
|
30
|
-
|
|
188
|
+
O clone de um repositório vazio já 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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
```
|
package/docs/guia/06-falhas.md
CHANGED
|
@@ -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.
|
|
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
|
+
}
|