@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
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,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,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
|
+
}
|