@pathbit/agentdevkit 0.0.6 → 0.0.8
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/.pbai4devs/scripts/lib/gitignore.mjs +142 -25
- package/.pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs +41 -20
- package/.pbai4devs/scripts/preparar-ferramentas/tests/TEST.mjs +81 -0
- package/.pbai4devs/scripts/sincronizar-agentes/SCRIPT.mjs +120 -20
- package/.pbai4devs/scripts/sincronizar-agentes/tests/HUMAN_OWNERSHIP.mjs +145 -0
- package/.pbai4devs/scripts/sincronizar-agentes/tests/LOCAL_IGNORE.mjs +211 -1
- package/.pbai4devs/scripts/sincronizar-agentes/tests/TEST.mjs +2 -1
- package/CHANGELOG.md +16 -0
- package/README.en.md +2 -2
- package/README.es.md +2 -2
- package/README.md +2 -2
- package/docs/guides/cli.md +9 -0
- package/docs/guides/installation.md +17 -2
- package/docs/guides/tools-and-testing.md +2 -0
- package/docs/maintainers/packages.md +1 -1
- package/docs/product/roadmap.md +4 -0
- package/docs/specs/README.md +2 -0
- package/docs/specs/v0.0.7/README.md +10 -0
- package/docs/specs/v0.0.7/SPEC.md +59 -0
- package/docs/specs/v0.0.8/README.md +10 -0
- package/docs/specs/v0.0.8/SPEC.md +62 -0
- package/package.json +3 -2
- package/src/cli.mjs +10 -4
- package/src/install.mjs +61 -22
package/README.es.md
CHANGED
|
@@ -35,7 +35,7 @@ Después, abre el proyecto en tu agente y pide:
|
|
|
35
35
|
La [guía de instalación](docs/guides/installation.md) incluye Windows, Node ausente,
|
|
36
36
|
simulación y actualización. Instala y actualiza mediante el
|
|
37
37
|
[paquete npm público][packages];
|
|
38
|
-
la [release v0.0.
|
|
38
|
+
la [release v0.0.8][release] conserva artefactos y checksums para verificarlos.
|
|
39
39
|
No requiere instalación global.
|
|
40
40
|
La CLI guarda el idioma `pt`, `en` o `es` por proyecto.
|
|
41
41
|
|
|
@@ -80,7 +80,7 @@ formularios. Consulta [cómo participar](docs/maintainers/community.md).
|
|
|
80
80
|
al agente que lea `AGENTS.md`.
|
|
81
81
|
|
|
82
82
|
[clients]: .pbai4devs/COMPATIBILIDADE.md
|
|
83
|
-
[release]: https://github.com/pathbit/AgentDevKit/releases/tag/v0.0.
|
|
83
|
+
[release]: https://github.com/pathbit/AgentDevKit/releases/tag/v0.0.8
|
|
84
84
|
[npm-package]: https://www.npmjs.com/package/@pathbit/agentdevkit
|
|
85
85
|
[npm-badge]: https://img.shields.io/npm/v/%40pathbit%2Fagentdevkit
|
|
86
86
|
[license-badge]: https://img.shields.io/badge/license-MIT-blue
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Depois, abra o projeto no seu agente e peça:
|
|
|
32
32
|
|
|
33
33
|
Windows, instalação sem Node, simulação e atualização estão no
|
|
34
34
|
[guia de instalação](https://github.com/pathbit/AgentDevKit/blob/master/docs/guides/installation.md). A distribuição usa a
|
|
35
|
-
versão publicada no [npm público][packages] e na [release v0.0.
|
|
35
|
+
versão publicada no [npm público][packages] e na [release v0.0.8][release],
|
|
36
36
|
sem instalação global. Instalação e atualização usam o pacote do registro;
|
|
37
37
|
os arquivos da release servem para conferir os artefatos publicados.
|
|
38
38
|
O idioma da CLI pode ser `pt`, `en` ou `es`.
|
|
@@ -89,7 +89,7 @@ ao seu agente para ler `AGENTS.md`.
|
|
|
89
89
|
[help]: https://github.com/pathbit/AgentDevKit/blob/master/docs/guides/troubleshooting.md
|
|
90
90
|
[issues]: https://github.com/pathbit/AgentDevKit/issues/new/choose
|
|
91
91
|
[packages]: https://github.com/pathbit/AgentDevKit/blob/master/docs/maintainers/packages.md
|
|
92
|
-
[release]: https://github.com/pathbit/AgentDevKit/releases/tag/v0.0.
|
|
92
|
+
[release]: https://github.com/pathbit/AgentDevKit/releases/tag/v0.0.8
|
|
93
93
|
[npm-package]: https://www.npmjs.com/package/@pathbit/agentdevkit
|
|
94
94
|
[npm-badge]: https://img.shields.io/npm/v/%40pathbit%2Fagentdevkit
|
|
95
95
|
[license-badge]: https://img.shields.io/badge/license-MIT-blue
|
package/docs/guides/cli.md
CHANGED
|
@@ -63,6 +63,15 @@ e `unlock`. Consultar ou executar não instala silenciosamente.
|
|
|
63
63
|
`setup` prepara as ferramentas compatíveis; outros comandos podem receber
|
|
64
64
|
um nome ou grupo. Consulte [ferramentas e testes](tools-and-testing.md).
|
|
65
65
|
|
|
66
|
+
Em `tools`, use `--` antes dos argumentos que pertencem à ferramenta:
|
|
67
|
+
|
|
68
|
+
```console
|
|
69
|
+
npx @pathbit/agentdevkit tools run agent-browser -- --json --help
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Nesse exemplo, `--json` pertence ao agent-browser. Opções do kit ficam antes
|
|
73
|
+
do separador. `--help` e `--version` do kit não exigem um projeto instalado.
|
|
74
|
+
|
|
66
75
|
## JSON e retorno
|
|
67
76
|
|
|
68
77
|
As respostas incluem `ok`, `command`, `version`, `language`, `target` e
|
|
@@ -60,6 +60,13 @@ workflows, formulários de issues ou configurações humanas em `.github`.
|
|
|
60
60
|
Sem Git, uma proteção no `.gitignore` da raiz evita incluir os derivados
|
|
61
61
|
quando o repositório for iniciado.
|
|
62
62
|
|
|
63
|
+
O bloco local também protege `.agent-tests`, `tmp`, `temp`, `.playwright*`
|
|
64
|
+
e relatórios de testes, inclusive com letras maiúsculas e em subpastas.
|
|
65
|
+
O `.gitignore` existente e seus arquivos rastreados são preservados;
|
|
66
|
+
uma exceção humana que conflite com essa proteção é informada como conflito.
|
|
67
|
+
Nomes próprios de artefatos e arquivos existentes também são conferidos;
|
|
68
|
+
a validação fica restrita ao projeto e aos ignores de seus ancestrais.
|
|
69
|
+
|
|
63
70
|
Arquivos já rastreados exigem migração do índice: apenas saídas identificadas
|
|
64
71
|
do kit podem deixar de ser rastreadas, preservando seus bytes no disco.
|
|
65
72
|
Revise o plano e os conflitos em um projeto existente; use `--dry-run` antes
|
|
@@ -77,10 +84,18 @@ gerenciados alterados geram conflito; use `--force` somente após revisar o
|
|
|
77
84
|
backup e o conteúdo que será substituído. Preserve os dados locais, as
|
|
78
85
|
configurações próprias e os commits do projeto.
|
|
79
86
|
|
|
87
|
+
Links simbólicos em caminhos usados pela instalação podem bloquear a operação.
|
|
88
|
+
O kit informa o caminho e não converte nem remove o link automaticamente.
|
|
89
|
+
Use a simulação para identificar esse caso antes de aplicar.
|
|
90
|
+
|
|
91
|
+
Arquivos humanos não se tornam gerenciados apenas por conterem um link.
|
|
92
|
+
As alterações de arquivos existentes preservam permissões, inclusive em backup
|
|
93
|
+
e restauração após falha.
|
|
94
|
+
|
|
80
95
|
O [fluxo de trabalho](workflow.md) orienta a próxima etapa do projeto.
|
|
81
96
|
Se houver erro, consulte [solução de problemas](troubleshooting.md).
|
|
82
97
|
|
|
83
|
-
[Wiki](../README.md) · [Spec v0.0.
|
|
98
|
+
[Wiki](../README.md) · [Spec v0.0.8][spec]
|
|
84
99
|
|
|
85
100
|
[bootstrap]: ../../.pbai4devs/scripts/preparar-ferramentas/BOOTSTRAP.md
|
|
86
|
-
[spec]: ../specs/v0.0.
|
|
101
|
+
[spec]: ../specs/v0.0.8/SPEC.md
|
|
@@ -32,6 +32,8 @@ node .pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs setup
|
|
|
32
32
|
`setup` reutiliza instalações saudáveis e verifica as compatíveis. Um nome
|
|
33
33
|
ou grupo limita a seleção. `check`, `info` e `run` não trocam uma consulta
|
|
34
34
|
por instalação. Downloads, caches e recibos permanecem locais e ignorados.
|
|
35
|
+
O gerenciador recusa links simbólicos nos caminhos que escreve ou usa como
|
|
36
|
+
estado local; a atualização não segue esses links para fora da instalação.
|
|
35
37
|
|
|
36
38
|
## Escolher a prova
|
|
37
39
|
|
|
@@ -64,7 +64,7 @@ existir; a primeira publicação pode usar o secret `NPMJS_TOKEN`.
|
|
|
64
64
|
Para publicar somente o pacote de uma tag que já tem artefato aprovado:
|
|
65
65
|
|
|
66
66
|
```console
|
|
67
|
-
gh workflow run release.yml --ref master -f tag=v0.0.
|
|
67
|
+
gh workflow run release.yml --ref master -f tag=v0.0.8 -F package_only=true
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Se o artefato expirou, reexecute o CI original da tag antes da publicação.
|
package/docs/product/roadmap.md
CHANGED
|
@@ -17,6 +17,8 @@ status: vigente
|
|
|
17
17
|
| Estrutura e exemplos v0.0.4 | CLI, distribuição e gerador | [Spec][layout]. |
|
|
18
18
|
| Instalação pelo registro v0.0.5 | Pacote publicado | [Spec][registry]. |
|
|
19
19
|
| Apresentação e licença v0.0.6 | Distribuição e atualização | [Spec][public]. |
|
|
20
|
+
| Compatibilidade real v0.0.7 | Testes em clones existentes | [Spec][oss]. |
|
|
21
|
+
| Preservação v0.0.8 | Auditoria e regressões | [Spec][robustness]. |
|
|
20
22
|
|
|
21
23
|
A primeira entrega segue a [spec v0.0.1](../specs/v0.0.1/SPEC.md), disponível
|
|
22
24
|
na [release v0.0.1][release]. Os detalhes de mudanças estão no
|
|
@@ -36,3 +38,5 @@ comportamento existente, sem alterar a versão de uma release já publicada.
|
|
|
36
38
|
[layout]: ../specs/v0.0.4/SPEC.md
|
|
37
39
|
[registry]: ../specs/v0.0.5/SPEC.md
|
|
38
40
|
[public]: ../specs/v0.0.6/SPEC.md
|
|
41
|
+
[oss]: ../specs/v0.0.7/SPEC.md
|
|
42
|
+
[robustness]: ../specs/v0.0.8/SPEC.md
|
package/docs/specs/README.md
CHANGED
|
@@ -11,6 +11,8 @@ description: Incrementos verificáveis do AgentDevKit.
|
|
|
11
11
|
- [v0.0.4](v0.0.4/SPEC.md): estrutura padrão e exemplos na raiz.
|
|
12
12
|
- [v0.0.5](v0.0.5/SPEC.md): instalação pelo registro em todos os fluxos.
|
|
13
13
|
- [v0.0.6](v0.0.6/SPEC.md): apresentação pública, licença e atualização segura.
|
|
14
|
+
- [v0.0.7](v0.0.7/SPEC.md): projetos reais e proteção local de artefatos.
|
|
15
|
+
- [v0.0.8](v0.0.8/SPEC.md): preservação, transações e validação aprofundada.
|
|
14
16
|
|
|
15
17
|
Cada incremento vincula requisitos, critérios, testes e evidências locais.
|
|
16
18
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Compatibilidade do pacote com projetos open source existentes.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Versão 0.0.7
|
|
6
|
+
|
|
7
|
+
A [spec](SPEC.md) define testes em clones reais e a correção das exclusões
|
|
8
|
+
locais de artefatos, preservando os arquivos e as decisões dos projetos.
|
|
9
|
+
|
|
10
|
+
[Specs](../README.md)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Testes em projetos reais e proteção de artefatos locais.
|
|
3
|
+
status: aprovado
|
|
4
|
+
version: 0.0.7
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Compatibilidade com projetos existentes
|
|
8
|
+
|
|
9
|
+
## Objetivo e autorização
|
|
10
|
+
|
|
11
|
+
Atender ao pedido de testar o pacote publicado em projetos open source com
|
|
12
|
+
conteúdo real, usando clones em `tmp/` ignorado. Corrigir a falta de proteção
|
|
13
|
+
de artefatos revelada em projetos que não tinham as regras do AgentDevKit.
|
|
14
|
+
|
|
15
|
+
O trabalho usa o [PRD](../../product/prd.md),
|
|
16
|
+
o [design](../../product/design.md) e a
|
|
17
|
+
[distribuição pública](../v0.0.6/SPEC.md). Não altera as aplicações clonadas.
|
|
18
|
+
|
|
19
|
+
## Contrato
|
|
20
|
+
|
|
21
|
+
- Clonar commits fixos de Express, Zod e Chi; preservar os projetos originais.
|
|
22
|
+
- Comparar bytes, tipos, permissões, links, HEAD e alterações staged.
|
|
23
|
+
- Verificar instalação, atualização, repetição, skill própria e conflitos.
|
|
24
|
+
- Proteger artefatos no ignore local sem reescrever o `.gitignore` do usuário
|
|
25
|
+
nem desindexar arquivos humanos. Respeitar projetos irmãos e exceções.
|
|
26
|
+
- Manter as recusas de links simbólicos. Um bloqueio seguro não comprova
|
|
27
|
+
instalação bem-sucedida; relatá-lo e verificar ausência de alterações.
|
|
28
|
+
- Versionar o teste; guardar clones, logs e resultados somente em `tmp/`.
|
|
29
|
+
- Publicar correções como nova versão estável; repetir a prova pública.
|
|
30
|
+
|
|
31
|
+
## Critérios de aceite
|
|
32
|
+
|
|
33
|
+
| ID | Resultado observável |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| AC-001 | Matriz real registra resultado e preservação por projeto. |
|
|
36
|
+
| AC-002 | Artefatos são ignorados sem alterar fontes ou projetos irmãos. |
|
|
37
|
+
| AC-003 | Regressões e revisão independente aprovam a correção. |
|
|
38
|
+
| AC-004 | Pacote estável publicado repete os resultados e atualiza o router. |
|
|
39
|
+
|
|
40
|
+
## Verificação
|
|
41
|
+
|
|
42
|
+
- **AC-001:** executar o runner contra o npm, incluindo caso staged e bloqueios;
|
|
43
|
+
registrar commits, versão e hashes. Não confundir com testes das aplicações.
|
|
44
|
+
- **AC-002:** testar Git, instalação sem Git, projeto aninhado, caixa mista,
|
|
45
|
+
exceções humanas e artefatos já rastreados. Não forçar sobrescritas.
|
|
46
|
+
- **AC-003:** executar testes do pacote, infraestrutura, lint e distribuição.
|
|
47
|
+
Comparar testes nativos do Express antes e depois, sem modificar suas fontes.
|
|
48
|
+
- **AC-004:** conferir release, `latest`, checksum e instalação anônima; repetir
|
|
49
|
+
matriz com o pacote publicado e atualizar o router com backup e diagnóstico.
|
|
50
|
+
|
|
51
|
+
Evidências dos clones ficam em `tmp/oss-compatibility/`; o relatório de revisão
|
|
52
|
+
em `.agent-tests/oss-compatibility/` referencia cópias dessas provas.
|
|
53
|
+
Zod e Chi não terão testes nativos sem os runtimes/gerenciadores exigidos.
|
|
54
|
+
Não concluir compatibilidade universal a partir desta matriz.
|
|
55
|
+
|
|
56
|
+
## Documentação afetada
|
|
57
|
+
|
|
58
|
+
Atualizar instalação, testes, changelog, índices e roadmap. Documentar a
|
|
59
|
+
proteção de artefatos locais e os limites de configurações preexistentes.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Validação aprofundada e preservação de dados do AgentDevKit.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Versão 0.0.8
|
|
6
|
+
|
|
7
|
+
A [spec](SPEC.md) reúne correções da auditoria do instalador, sincronizador,
|
|
8
|
+
gerenciador de ferramentas, CLI e regras de ignore.
|
|
9
|
+
|
|
10
|
+
[Specs](../README.md)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Correções verificadas de preservação, transações e interface CLI.
|
|
3
|
+
status: aprovado
|
|
4
|
+
version: 0.0.8
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Preservação e validação aprofundada
|
|
8
|
+
|
|
9
|
+
## Objetivo e autorização
|
|
10
|
+
|
|
11
|
+
Atender ao pedido de revisão profunda, corrigindo falhas comprovadas e
|
|
12
|
+
publicando uma nova versão estável após validação. Esta etapa continua o
|
|
13
|
+
[PRD](../../product/prd.md), o [design](../../product/design.md) e os
|
|
14
|
+
[testes em projetos reais](../v0.0.7/SPEC.md).
|
|
15
|
+
|
|
16
|
+
## Contrato
|
|
17
|
+
|
|
18
|
+
- Não apagar ou sobrescrever arquivos humanos pela aparência de seu Markdown.
|
|
19
|
+
Exigir propriedade comprovada para limpeza automática de apontamentos.
|
|
20
|
+
- Recusar conflitos conhecidos antes de escrever. Em falhas posteriores,
|
|
21
|
+
restaurar as alterações próprias sem substituir edições humanas concorrentes.
|
|
22
|
+
- Preservar permissões em atualização, backup e rollback, inclusive em
|
|
23
|
+
arquivos somente para leitura quando a operação for permitida pelo sistema.
|
|
24
|
+
- Recusar componentes simbólicos nos caminhos locais de ferramentas, inclusive
|
|
25
|
+
após operações externas, antes de escrever ou confirmar uma instalação.
|
|
26
|
+
- Preservar integralmente argumentos após `tools ... --`; ajuda e versão
|
|
27
|
+
não exigem diretório de projeto válido.
|
|
28
|
+
- Conferir artefatos existentes e exceções literais além dos nomes de amostra,
|
|
29
|
+
sem ler ignores de projetos irmãos ou desindexar arquivos humanos.
|
|
30
|
+
- Manter a recusa de links simbólicos incompatíveis do Zod; não converter
|
|
31
|
+
esse bloqueio documentado em uma instalação bem-sucedida.
|
|
32
|
+
|
|
33
|
+
## Critérios de aceite
|
|
34
|
+
|
|
35
|
+
| ID | Resultado observável |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| AC-001 | Markdown humano é preservado; limpeza usa propriedade comprovada. |
|
|
38
|
+
| AC-002 | Falhas não deixam gravações próprias parciais nem mudam permissões. |
|
|
39
|
+
| AC-003 | Gerenciador não escreve fora da instalação por links simbólicos. |
|
|
40
|
+
| AC-004 | CLI repassa argumentos; ajuda funciona sem projeto. |
|
|
41
|
+
| AC-005 | Ignore protege artefatos sem invadir projetos irmãos. |
|
|
42
|
+
| AC-006 | Regressões, pacote público e atualização do router são verificados. |
|
|
43
|
+
|
|
44
|
+
## Verificação
|
|
45
|
+
|
|
46
|
+
Para AC-001 a AC-005, conservar reproduções anteriores e testes de regressão
|
|
47
|
+
que falham antes da correção, com assertivas de conteúdo, modo, estado e Git.
|
|
48
|
+
Executar os testes relevantes em Node 24/26 e revisar os diffs de forma
|
|
49
|
+
independente. Limitações de plataforma devem ficar explícitas.
|
|
50
|
+
|
|
51
|
+
Para AC-006, executar pipeline completo, cenários de maturidade e matriz OSS
|
|
52
|
+
do pacote gerado. Confirmar no CI Linux, macOS e Windows; publicar o mesmo TGZ,
|
|
53
|
+
comparar checksum e repetir o consumo público. Atualizar o router com preview,
|
|
54
|
+
backup, diagnóstico e comparação da baseline, preservando o índice existente.
|
|
55
|
+
|
|
56
|
+
Testes e fixtures são versionados. Clones e provas ficam em `tmp/` e o registro
|
|
57
|
+
de revisão em `.agent-tests/`. Não prometer ausência universal de bugs.
|
|
58
|
+
|
|
59
|
+
## Documentação afetada
|
|
60
|
+
|
|
61
|
+
Atualizar referência da CLI, instalação, ferramentas, changelog, README,
|
|
62
|
+
roadmap e índices. Não criar documentos de produto nos projetos consumidores.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pathbit/agentdevkit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Shared skills, specifications and workflows for AI coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"pack:release": "node scripts/release/assets.mjs",
|
|
51
51
|
"ci:full": "node scripts/release/pipeline.mjs full",
|
|
52
52
|
"ci:portable": "node scripts/release/pipeline.mjs portable",
|
|
53
|
-
"ci:smoke": "node scripts/release/pipeline.mjs smoke"
|
|
53
|
+
"ci:smoke": "node scripts/release/pipeline.mjs smoke",
|
|
54
|
+
"test:oss": "node tests/oss-compatibility.mjs"
|
|
54
55
|
},
|
|
55
56
|
"author": {
|
|
56
57
|
"name": "PathBit",
|
package/src/cli.mjs
CHANGED
|
@@ -18,7 +18,10 @@ export function parseArgs(argv) {
|
|
|
18
18
|
const options = { command: null, positionals: [], toolArgs: [], target: null, language: null, clients: undefined, yes: false, json: false, dryRun: false, force: false, check: false };
|
|
19
19
|
for (let index = 0; index < argv.length; index++) {
|
|
20
20
|
const arg = argv[index];
|
|
21
|
-
if (
|
|
21
|
+
if (arg === '--' && options.command === 'tools') {
|
|
22
|
+
options.toolArgs.push(...argv.slice(index));
|
|
23
|
+
break;
|
|
24
|
+
} else if (['--target', '--lang', '--agents', '--clients'].includes(arg)) {
|
|
22
25
|
const value = argv[++index]; if (!value || value.startsWith('-')) usage(`Missing value: ${arg}`);
|
|
23
26
|
if (arg === '--target') options.target = value;
|
|
24
27
|
else if (arg === '--lang') { options.language = normalizeLanguage(value); if (!options.language) usage('Use --lang pt, en or es.'); }
|
|
@@ -86,11 +89,14 @@ function print(result, options, output) {
|
|
|
86
89
|
|
|
87
90
|
export async function main(argv = process.argv.slice(2), context = {}) {
|
|
88
91
|
const output = context.stdout || process.stdout, input = context.stdin || process.stdin, errorOutput = context.stderr || process.stderr;
|
|
89
|
-
|
|
92
|
+
const separator = argv.indexOf('--'), globalArgs = separator < 0 ? argv : argv.slice(0, separator);
|
|
93
|
+
let options = { json: globalArgs.includes('--json') }, language = normalizeLanguage(globalArgs[globalArgs.indexOf('--lang') + 1]) || 'pt', target = resolve(context.cwd || process.cwd()), clients = ['all'];
|
|
90
94
|
try {
|
|
91
95
|
options = parseArgs(argv);
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
const informational = ['help', 'version'].includes(options.command);
|
|
97
|
+
target = informational ? resolve(context.cwd || process.cwd(), options.target || '.')
|
|
98
|
+
: resolveTarget(options.target || (options.command === 'init' ? options.positionals[0] : '.'), context.cwd || process.cwd());
|
|
99
|
+
const previous = informational ? null : loadState(target);
|
|
94
100
|
language = options.language || previous?.language || 'pt';
|
|
95
101
|
clients = options.clients ?? previous?.clients ?? ['all'];
|
|
96
102
|
if (options.command === 'init' && !previous && !options.yes && !options.json && !options.dryRun && input.isTTY && output.isTTY && (!options.language || options.clients === undefined)) {
|
package/src/install.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { existsSync, lstatSync, readFileSync, writeFileSync, readdirSync, mkdirSync, mkdtempSync, realpathSync, renameSync, rmSync, rmdirSync } from 'node:fs';
|
|
2
|
+
import { chmodSync, existsSync, lstatSync, readFileSync, writeFileSync, readdirSync, mkdirSync, mkdtempSync, realpathSync, renameSync, rmSync, rmdirSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
5
|
import { synchronize, isReference } from '../.pbai4devs/scripts/sincronizar-agentes/SCRIPT.mjs';
|
|
@@ -77,8 +77,24 @@ function walk(root, path = '', filter = () => true) {
|
|
|
77
77
|
return files;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function write(root, path, content) {
|
|
81
|
-
const target = safePath(root, path); mkdirSync(dirname(target), { recursive: true });
|
|
80
|
+
function write(root, path, content, mode) {
|
|
81
|
+
const target = safePath(root, path); mkdirSync(dirname(target), { recursive: true });
|
|
82
|
+
writeFileSync(target, content, mode === undefined ? undefined : { mode });
|
|
83
|
+
if (mode !== undefined) chmodSync(target, mode);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function atomicWrite(root, path, content, mode, onPublished) {
|
|
87
|
+
const file = safePath(root, path);
|
|
88
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
89
|
+
const temporary = `${file}.agentdevkit-${randomUUID()}`;
|
|
90
|
+
try {
|
|
91
|
+
writeFileSync(temporary, content, { flag: 'wx', ...(mode === undefined ? {} : { mode }) });
|
|
92
|
+
if (mode !== undefined) chmodSync(temporary, mode);
|
|
93
|
+
const writtenMode = stat(temporary).mode & 0o777;
|
|
94
|
+
renameSync(temporary, file);
|
|
95
|
+
onPublished?.(writtenMode);
|
|
96
|
+
return writtenMode;
|
|
97
|
+
} finally { rmSync(temporary, { force: true }); }
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
function read(root, path) {
|
|
@@ -294,42 +310,63 @@ export function applyPlan(plan) {
|
|
|
294
310
|
safePath(target, backup);
|
|
295
311
|
for (const change of plan.changes) safePath(target, change.path);
|
|
296
312
|
const before = new Map(plan.changes.map(change => [change.path, read(target, change.path)]));
|
|
313
|
+
const beforeModes = new Map(plan.changes.map(change => {
|
|
314
|
+
const info = stat(safePath(target, change.path));
|
|
315
|
+
return [change.path, info ? info.mode & 0o777 : undefined];
|
|
316
|
+
}));
|
|
297
317
|
for (const change of plan.changes) if ((before.get(change.path) ? hash(before.get(change.path)) : null) !== change.beforeHash) fail('E_CHANGED', `File changed after planning: ${change.path}`);
|
|
298
|
-
for (const [path, content] of before) if (content) write(target, `${backup}/${path}`, content);
|
|
299
|
-
const applied = [];
|
|
318
|
+
for (const [path, content] of before) if (content) write(target, `${backup}/${path}`, content, beforeModes.get(path));
|
|
319
|
+
const applied = [], removedDirectories = [];
|
|
300
320
|
let untracked = false;
|
|
301
321
|
try {
|
|
302
322
|
if (plan.untrack?.paths.length) { applyUntrack(plan.untrack); untracked = true; }
|
|
303
323
|
for (const change of plan.changes) {
|
|
304
324
|
const file = safePath(target, change.path);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
applied.push(change.path);
|
|
325
|
+
const content = change.content;
|
|
326
|
+
const signature = { path: change.path, hash: content === null ? null : hash(content) };
|
|
327
|
+
if (content === null) { rmSync(file, { force: true }); applied.push(signature); }
|
|
328
|
+
else atomicWrite(target, change.path, content, beforeModes.get(change.path), mode => {
|
|
329
|
+
signature.mode = mode; applied.push(signature);
|
|
330
|
+
});
|
|
313
331
|
}
|
|
314
|
-
pruneRemovedDirectories(plan);
|
|
332
|
+
pruneRemovedDirectories(plan, directory => removedDirectories.push(directory));
|
|
315
333
|
const changesFallback = plan.changes.some(change => change.path === '.gitignore');
|
|
316
334
|
// Metadata is committed last; its writer rolls back partial writes itself.
|
|
317
335
|
const ignored = applyLocalIgnore(changesFallback ? planLocalIgnore(target, { outputs: plan.ownedOutputs }) : plan.localIgnore);
|
|
318
336
|
return { changed: Boolean(plan.changes.length || ignored.changed || untracked), backup: [...before.values()].some(Boolean) ? backup : null };
|
|
319
337
|
} catch (error) {
|
|
320
|
-
const rollbackErrors = [];
|
|
321
|
-
for (const
|
|
338
|
+
const rollbackErrors = [], preserved = [];
|
|
339
|
+
for (const removed of [...removedDirectories].sort((a, b) => a.path.length - b.path.length)) {
|
|
340
|
+
let directory, current;
|
|
341
|
+
try { directory = safePath(target, removed.path); current = stat(directory); }
|
|
342
|
+
catch { preserved.push(removed.path); continue; }
|
|
343
|
+
if (current) continue;
|
|
344
|
+
try { mkdirSync(directory, { mode: removed.mode }); chmodSync(directory, removed.mode); }
|
|
345
|
+
catch (failure) { rollbackErrors.push(failure); }
|
|
346
|
+
}
|
|
347
|
+
for (const appliedFile of applied.reverse()) {
|
|
348
|
+
const { path } = appliedFile;
|
|
349
|
+
let unchanged = false;
|
|
350
|
+
try {
|
|
351
|
+
const file = safePath(target, path), info = stat(file);
|
|
352
|
+
unchanged = appliedFile.hash === null ? !info : Boolean(info?.isFile()
|
|
353
|
+
&& (info.mode & 0o777) === appliedFile.mode && hash(readFileSync(file)) === appliedFile.hash);
|
|
354
|
+
} catch { /* Não restaurar quando não é possível comprovar a própria gravação. */ }
|
|
355
|
+
if (!unchanged) { preserved.push(path); continue; }
|
|
322
356
|
const content = before.get(path);
|
|
323
|
-
try { if (content)
|
|
357
|
+
try { if (content) atomicWrite(target, path, content, beforeModes.get(path)); else rmSync(safePath(target, path), { force: true }); }
|
|
324
358
|
catch (failure) { rollbackErrors.push(failure); }
|
|
325
359
|
}
|
|
360
|
+
if (preserved.length) {
|
|
361
|
+
try { error.rollbackPreservedPaths = preserved; } catch { /* Preserve a exceção original. */ }
|
|
362
|
+
}
|
|
326
363
|
if (untracked) try { restoreUntrack(plan.untrack); } catch (failure) { rollbackErrors.push(failure); }
|
|
327
364
|
if (rollbackErrors.length) throw Object.assign(new AggregateError([error, ...rollbackErrors], `${error.message}; rollback incompleto: ${rollbackErrors.map(failure => failure.message).join('; ')}`, { cause: error }), { code: error.code || 'E_OPERATION' });
|
|
328
365
|
throw error;
|
|
329
366
|
}
|
|
330
367
|
}
|
|
331
368
|
|
|
332
|
-
function pruneRemovedDirectories(plan) {
|
|
369
|
+
function pruneRemovedDirectories(plan, onRemoved) {
|
|
333
370
|
const { target } = plan;
|
|
334
371
|
const legacy = '.pbai4devs/adapters/README.md';
|
|
335
372
|
const removed = plan.changes.filter(change => change.action === 'remove' && (allowedManaged(change.path) || change.path === legacy));
|
|
@@ -342,8 +379,9 @@ function pruneRemovedDirectories(plan) {
|
|
|
342
379
|
const boundary = parent.startsWith('.pbai4devs/') ? '.pbai4devs' : adapterRoots.find(root => parent.startsWith(`${root}/`));
|
|
343
380
|
while (boundary && parent.startsWith(`${boundary}/`)) {
|
|
344
381
|
const directory = safePath(target, parent);
|
|
345
|
-
|
|
346
|
-
|
|
382
|
+
const info = stat(directory);
|
|
383
|
+
if (!info?.isDirectory() || readdirSync(directory).length) break;
|
|
384
|
+
try { rmdirSync(directory); onRemoved?.({ path: parent, mode: info.mode & 0o777 }); }
|
|
347
385
|
catch (error) { if (['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(error.code)) break; throw error; }
|
|
348
386
|
parent = portable(dirname(parent));
|
|
349
387
|
}
|
|
@@ -351,8 +389,9 @@ function pruneRemovedDirectories(plan) {
|
|
|
351
389
|
for (const root of plan.retiredRoots || []) {
|
|
352
390
|
if (!adapterRoots.includes(root)) continue;
|
|
353
391
|
const directory = safePath(target, root);
|
|
354
|
-
|
|
355
|
-
|
|
392
|
+
const info = stat(directory);
|
|
393
|
+
if (!info?.isDirectory()) continue;
|
|
394
|
+
try { if (readdirSync(directory).length === 0) { rmdirSync(directory); onRemoved?.({ path: root, mode: info.mode & 0o777 }); } }
|
|
356
395
|
catch (error) { if (!['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(error.code)) throw error; }
|
|
357
396
|
}
|
|
358
397
|
}
|