@aksp/opencrew 1.6.2 → 1.7.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/CHANGELOG.md +102 -0
- package/README.md +70 -13
- package/package.json +2 -2
- package/src/cli.js +15 -38
- package/src/commands/init.js +41 -44
- package/src/commands/update.js +78 -75
- package/src/lib/blocos.js +148 -0
- package/src/lib/deteccao.js +69 -0
- package/src/lib/fsx.js +1 -55
- package/src/lib/ides.js +4 -0
- package/src/lib/legado.js +142 -0
- package/src/lib/manifest.js +67 -26
- package/src/lib/mcp.js +131 -0
- package/src/lib/migrations.js +77 -74
- package/src/lib/node-version.js +43 -0
- package/src/lib/prompts.js +25 -2
- package/src/lib/resumo.js +125 -0
- package/templates/.mcp.json +1 -1
- package/templates/AGENTS.md +20 -6
- package/templates/_opencrew/.opencrew-version +1 -1
- package/templates/_opencrew/core/best-practices/social-networks-publishing.md +14 -14
- package/templates/_opencrew/core/escritorio/animacao.js +64 -0
- package/templates/_opencrew/core/escritorio/app.js +137 -0
- package/templates/_opencrew/core/escritorio/cena.js +132 -0
- package/templates/_opencrew/core/escritorio/demo.js +79 -0
- package/templates/_opencrew/core/escritorio/escala.js +27 -0
- package/templates/_opencrew/core/escritorio/index.html +166 -0
- package/templates/_opencrew/core/escritorio/modelo-agentes.js +93 -0
- package/templates/_opencrew/core/escritorio/modelo-estado.js +71 -0
- package/templates/_opencrew/core/escritorio/modelo-mesas.js +81 -0
- package/templates/_opencrew/core/escritorio/modelo-pagina.js +95 -0
- package/templates/_opencrew/core/escritorio/modelo-textos.js +65 -0
- package/templates/_opencrew/core/escritorio/modelo-visao.js +91 -0
- package/templates/_opencrew/core/escritorio/modelo.js +29 -0
- package/templates/_opencrew/core/escritorio/painel.js +120 -0
- package/templates/_opencrew/core/escritorio/quadro.js +106 -0
- package/templates/_opencrew/core/escritorio/rota.js +62 -0
- package/templates/_opencrew/core/escritorio/rotulos.js +78 -0
- package/templates/_opencrew/core/escritorio/sprites-mesa.js +122 -0
- package/templates/_opencrew/core/escritorio/sprites-sala.js +92 -0
- package/templates/_opencrew/core/escritorio/sprites.js +187 -0
- package/templates/_opencrew/core/prompts/build.prompt.md +3 -3
- package/templates/_opencrew/core/prompts/export.prompt.md +1 -1
- package/templates/_opencrew/core/prompts/repair.prompt.md +7 -12
- package/templates/_opencrew/core/prompts/sherlock-shared.md +5 -5
- package/templates/_opencrew/core/runner.pipeline.md +76 -139
- package/templates/_opencrew/core/scripts/comum.mjs +49 -4
- package/templates/_opencrew/core/scripts/conferir-fontes/busca.mjs +42 -3
- package/templates/_opencrew/core/scripts/conferir-fontes/relatorio.mjs +18 -6
- package/templates/_opencrew/core/scripts/conferir-fontes.mjs +97 -39
- package/templates/_opencrew/core/scripts/escritorio/leitura.mjs +31 -0
- package/templates/_opencrew/core/scripts/escritorio/porta.mjs +98 -0
- package/templates/_opencrew/core/scripts/escritorio/projeto.mjs +29 -0
- package/templates/_opencrew/core/scripts/escritorio/servidor.mjs +78 -0
- package/templates/_opencrew/core/scripts/escritorio.mjs +117 -0
- package/templates/_opencrew/core/scripts/estado/argumentos.mjs +61 -0
- package/templates/_opencrew/core/scripts/estado/arquivo.mjs +53 -0
- package/templates/_opencrew/core/scripts/estado/decisao.mjs +56 -0
- package/templates/_opencrew/core/scripts/estado/elenco.mjs +58 -0
- package/templates/_opencrew/core/scripts/estado/nucleo.mjs +113 -0
- package/templates/_opencrew/core/scripts/estado/preferencia.mjs +24 -0
- package/templates/_opencrew/core/scripts/estado.mjs +96 -0
- package/templates/_opencrew/core/scripts/verificar.mjs +7 -4
- package/templates/_opencrew/core/skills.engine.md +7 -3
- package/templates/gitignore +1 -0
- package/templates/skills/blotato/SKILL.md +39 -10
- package/templates/skills/image-ai-generator/SKILL.md +18 -5
- package/templates/skills/image-ai-generator/scripts/generate.py +52 -10
- package/templates/skills/instagram-publisher/SKILL.md +4 -0
- package/templates/skills/opencrew-skill-creator/references/skill-format.md +1 -0
- package/templates/skills/resend/SKILL.md +52 -13
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,108 @@
|
|
|
3
3
|
All notable changes to opencrew are documented here.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
|
|
6
|
+
## [1.7.0] — 2026-10-06
|
|
7
|
+
|
|
8
|
+
Fase E1 "Escritório ao vivo — a equipe trabalhando, em 8 bits"
|
|
9
|
+
(`specs/fase-e1-escritorio-ao-vivo.md`). Chega a quem já usa com um
|
|
10
|
+
`npx @aksp/opencrew@latest update`.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Escritório ao vivo.** Uma página em pixel-art, aberta no navegador, mostra a crew
|
|
14
|
+
trabalhando: cada agente na sua mesa, digitando na vez dele, levando o papel ao colega na
|
|
15
|
+
passagem de bastão, de mão levantada quando espera a sua resposta, com ✓ quando termina e "!"
|
|
16
|
+
quando falha. Ao lado, o passo atual, a lista dos agentes com o status por extenso e o que cada
|
|
17
|
+
um fez, e a última passagem de bastão. O título da aba acompanha a execução.
|
|
18
|
+
- **`/opencrew dashboard`** liga o Escritório, sobe a página e mostra o endereço
|
|
19
|
+
(`http://127.0.0.1:4747`, ou a porta livre seguinte); repetir o comando devolve o mesmo
|
|
20
|
+
endereço. **`/opencrew dashboard off`** desliga. Continua desligado por padrão.
|
|
21
|
+
- Roda só no seu computador, sem internet e sem medição: o servidor
|
|
22
|
+
(`_opencrew/core/scripts/escritorio.mjs`) escuta só em `127.0.0.1`, só lê e não escreve em disco.
|
|
23
|
+
- Sem execução nenhuma, a página roda uma demonstração e troca sozinha para a execução real
|
|
24
|
+
quando ela aparece (`?demo` no endereço força a demonstração). Com mais de uma crew, mostra a
|
|
25
|
+
de atualização mais recente e um seletor com as outras.
|
|
26
|
+
- A página não mente sobre o que não sabe: execução há mais de 2 minutos sem novidade mostra há
|
|
27
|
+
quanto tempo foi a última atualização; há mais de 20, o agente sai da pose de digitar e aparece
|
|
28
|
+
"sem sinal". Servidor fora do ar: a página avisa e tenta de novo sozinha.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- **Quem avisa o Escritório é um script, não a IA escrevendo JSON.** Com o Escritório ligado, o
|
|
32
|
+
runner roda um comando curto por passo (`_opencrew/core/scripts/estado.mjs`). Checkpoint, agente
|
|
33
|
+
pulado e execução que falha passam a aparecer; antes nunca eram gravados. Falha desse comando
|
|
34
|
+
não para a execução: o runner avisa uma vez e segue.
|
|
35
|
+
- **Com o Escritório ligado, o estado final deixa de ser copiado para
|
|
36
|
+
`crews/<crew>/output/<run>/state.json`.** O estado da execução mora só em
|
|
37
|
+
`crews/<crew>/state.json`.
|
|
38
|
+
- `state.json`: os agentes ganham os status `checkpoint` e `failed` e o campo `label`; a
|
|
39
|
+
execução ganha `checkpoint` e `failed`; `desk` e `delivering` deixam de ser gravados. Arquivo
|
|
40
|
+
gravado por versão anterior continua sendo lido pela página.
|
|
41
|
+
- Os prompts de criar e de consertar crew não escrevem mais `state.json`.
|
|
42
|
+
- README: a nota "o dashboard não é instalado" deu lugar à seção "Escritório ao vivo".
|
|
43
|
+
|
|
44
|
+
### Removed
|
|
45
|
+
- A pasta `dashboard/` do repositório (o desenho antigo, que nunca foi instalado pelo `init`). O
|
|
46
|
+
que servia migrou para `_opencrew/core/escritorio/`, sem os defeitos apontados na auditoria:
|
|
47
|
+
nome de agente entrava na página como HTML, o modo ao vivo lia o arquivo no lugar errado e a
|
|
48
|
+
página congelava se a primeira leitura falhasse.
|
|
49
|
+
|
|
50
|
+
### Internal
|
|
51
|
+
- `npm run verify`: o lint passa a cobrir `templates/_opencrew/core/escritorio/` (com os nomes
|
|
52
|
+
globais de navegador) e o alerta de tamanho mede os `.js` dessa pasta. Travas novas:
|
|
53
|
+
`tests/estado*.test.js`, `tests/escritorio*.test.js`, `tests/runtime-contracts-e1.test.js`,
|
|
54
|
+
`tests/docs-e1.test.js` e os cenários E1-07a, E1-07c e E1-upg nos testes de pacote, de
|
|
55
|
+
referências e de upgrade. A trava de conteúdo do mantenedor passa a ler também `.mjs` e `.css`.
|
|
56
|
+
|
|
57
|
+
## [1.6.3] — 2026-10-06
|
|
58
|
+
|
|
59
|
+
**O Node mínimo subiu para o 20.17, numa versão de correção.** O pacote dizia 20.0, mas a lista
|
|
60
|
+
de IDEs do `init` só abria a partir do 20.12 e as dependências só garantem o 20.17. Quem está no
|
|
61
|
+
Node 20.0 a 20.16 precisa atualizar o Node antes de rodar `init` ou `update` (saída de emergência:
|
|
62
|
+
`npx @aksp/opencrew@1.6.2 update`).
|
|
63
|
+
|
|
64
|
+
Fase R2 "update e envio seguros" (`specs/fase-r2-update-e-envio-seguros.md`): só defeitos já
|
|
65
|
+
achados em revisão. Chega a quem já usa com um `npx @aksp/opencrew@latest update`.
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
- **`blotato` e `resend` pedem confirmação antes de agir.** Mostram a prévia (contas ou
|
|
69
|
+
destinatários, texto, quando) e só publicam, enviam, agendam ou apagam depois da palavra
|
|
70
|
+
`publicar`, `enviar` ou `apagar`. Crew que hoje envia sem perguntar vai parar e pedir. Em falha,
|
|
71
|
+
não repetem sozinhas.
|
|
72
|
+
- **`.mcp.json`: o servidor Playwright é entregue uma última vez.** Depois, se você o remover ou
|
|
73
|
+
apagar o arquivo, o `update` não repõe. A versão fixada não é trocada, e o arquivo é copiado
|
|
74
|
+
antes de qualquer regravação, mantendo a indentação.
|
|
75
|
+
- **Primeiro `update` para esta versão copia o `.gitignore`** para `.opencrew-backup/`, mesmo sem
|
|
76
|
+
edição sua: o bloco do OpenCrew mudou (passa a ignorar `.opencrew-backup/`) e as versões
|
|
77
|
+
anteriores não registravam o bloco.
|
|
78
|
+
- **O reparo de pontes só roda com o pacote na mesma versão do projeto.** Com outra versão, para
|
|
79
|
+
sem alterar nada e pede o `update`.
|
|
80
|
+
- As frases do resumo do `update` saem em português e só afirmam o que foi feito.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
- **Ponte de IDE que você não instalou**: um `CLAUDE.md`, `GEMINI.md`, `QWEN.md` ou
|
|
84
|
+
`copilot-instructions.md` seu que só citava "opencrew" fazia o `update` criar a ponte e pôr um
|
|
85
|
+
bloco no seu arquivo. A IDE agora se prova pelo arquivo de ponte. O resumo não cita mais o
|
|
86
|
+
Codex sem ele estar instalado.
|
|
87
|
+
- **Bloco do OpenCrew editado por dentro** (em `AGENTS.md`, `CLAUDE.md`, `.gitignore`…) era
|
|
88
|
+
regravado sem cópia. Agora o arquivo inteiro é copiado antes, e o fim de linha é mantido.
|
|
89
|
+
- **Texto antigo das pontes** (instalações até a 1.2.2), que mandava adotar o papel do OpenCrew
|
|
90
|
+
sempre, é retirado, com cópia, quando está idêntico ao gerado; se foi editado, só aviso.
|
|
91
|
+
- **Manifesto ilegível** vira aviso e a atualização segue; `.mcp.json` fora do formato não derruba
|
|
92
|
+
mais o `update` no meio.
|
|
93
|
+
- **A dica de reinstalar** não manda mais apagar `_opencrew/`, que guarda a sua memória.
|
|
94
|
+
- **Restos do OpenSquad**: o aviso não promete mais "apagar com segurança" para arquivo que pode
|
|
95
|
+
ser seu, e cobre mais sete caminhos. Nada é apagado.
|
|
96
|
+
- **Texto do usuário em linha de comando**: o prompt de imagem vai por arquivo (`--prompt-file`),
|
|
97
|
+
nome de arquivo com caractere inseguro não entra em comando, e caminhos e URLs vão entre aspas.
|
|
98
|
+
- **Conferência de fontes**: o `--corrigir` não grava mais fora da crew por um link; caminho de
|
|
99
|
+
rede e endereço de site citados viram alerta "não conferido", sem tocar a rede e sem parar a
|
|
100
|
+
execução; erro de leitura sai com mensagem.
|
|
101
|
+
- **Publicação**: a tag só publica depois do CI verde (Ubuntu e Windows, Node 20.17 e 22) e da
|
|
102
|
+
auditoria de segurança. Três das cinco últimas versões saíram com o CI vermelho.
|
|
103
|
+
|
|
104
|
+
### Internal
|
|
105
|
+
- CLI em módulos novos (`blocos`, `deteccao`, `legado`, `mcp`, `resumo`, `node-version`); 47
|
|
106
|
+
cenários R2 com teste de mesmo ID; teste de upgrade 1.6.2 → 1.6.3.
|
|
107
|
+
|
|
6
108
|
## [1.6.2] — 2026-10-05
|
|
7
109
|
|
|
8
110
|
Correção da 1.6.1. Chega a quem já usa com um `npx @aksp/opencrew@latest update`.
|
package/README.md
CHANGED
|
@@ -40,6 +40,9 @@ dentro da sua IDE.**
|
|
|
40
40
|
termos que você proibiu e `[PREENCHER]` pendentes, e aponta afirmações a confirmar.
|
|
41
41
|
Bloqueio não passa, seja qual for a nota do revisor. A crew não inventa casos nem números:
|
|
42
42
|
quando falta um dado real, ela pergunta na aprovação final.
|
|
43
|
+
- 🖥️ **Escritório ao vivo** — veja a equipe trabalhando numa sala em pixel-art, no navegador:
|
|
44
|
+
quem está digitando, quem passou o bastão, quem espera a sua resposta. Opcional, desligado por
|
|
45
|
+
padrão, só neste computador. Liga com `/opencrew dashboard`.
|
|
43
46
|
- 📂 **Crew que conhece o projeto** — liste em `fontes:` os arquivos e pastas do seu projeto
|
|
44
47
|
(decisões, calendário, manual de marca) e a crew os lê em todo run, tratando-os como verdade.
|
|
45
48
|
Reorganizou as pastas? No início do run ela confere os caminhos, acha para onde o arquivo foi
|
|
@@ -69,7 +72,7 @@ dentro da sua IDE.**
|
|
|
69
72
|
|
|
70
73
|
### Pré-requisitos
|
|
71
74
|
|
|
72
|
-
- **Node.js 20
|
|
75
|
+
- **Node.js 20.17 ou mais novo** ([baixar](https://nodejs.org/))
|
|
73
76
|
- Uma IDE de IA com acesso a arquivos locais: **Claude Code**, **Cursor**,
|
|
74
77
|
**Codex (OpenAI)**, **Gemini CLI**, **Google Antigravity**, **OpenCode**,
|
|
75
78
|
**VS Code + Copilot**, **Qwen Code** ou **Trae**.
|
|
@@ -175,6 +178,8 @@ meu-projeto/
|
|
|
175
178
|
│ │ ├── skills.engine.md ← gerenciador de skills
|
|
176
179
|
│ │ ├── architect.agent.yaml ← definição do Arquiteto
|
|
177
180
|
│ │ ├── best-practices/ ← 22 guias de melhores práticas + _catalog.yaml
|
|
181
|
+
│ │ ├── scripts/ ← verificador, conferência de fontes e os scripts do Escritório
|
|
182
|
+
│ │ ├── escritorio/ ← página do Escritório ao vivo (abre com /opencrew dashboard)
|
|
178
183
|
│ │ └── prompts/ ← 13 prompts de fase (discovery, design, build, etc.)
|
|
179
184
|
│ ├── agents/ ← 5 agentes base compartilhados
|
|
180
185
|
│ │ ├── researcher.agent.md
|
|
@@ -184,7 +189,7 @@ meu-projeto/
|
|
|
184
189
|
│ │ └── strategist.agent.md
|
|
185
190
|
│ ├── _memory/
|
|
186
191
|
│ │ ├── company.md ← perfil da sua empresa (onboarding)
|
|
187
|
-
│ │ └── preferences.md ← idioma, tier padrão,
|
|
192
|
+
│ │ └── preferences.md ← idioma, tier padrão, Escritório ligado ou desligado
|
|
188
193
|
│ └── .opencrew-version
|
|
189
194
|
│
|
|
190
195
|
├── crews/ ← suas crews vivem aqui
|
|
@@ -202,9 +207,47 @@ meu-projeto/
|
|
|
202
207
|
│ └── ...
|
|
203
208
|
```
|
|
204
209
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Escritório ao vivo
|
|
213
|
+
|
|
214
|
+
Quer ver a equipe trabalhando? O **Escritório** é uma página em pixel-art, aberta no navegador,
|
|
215
|
+
em que cada agente tem a sua mesa: digita quando é a vez dele, leva o papel ao colega na passagem
|
|
216
|
+
de bastão e levanta a mão quando espera uma resposta sua. Ao lado do desenho ficam o passo atual,
|
|
217
|
+
a lista dos agentes (com o status por extenso e o que cada um fez) e a última passagem de bastão.
|
|
218
|
+
|
|
219
|
+
**Como abrir:** no chat da sua IDE, digite `/opencrew dashboard`. O comando liga o Escritório,
|
|
220
|
+
sobe a página e mostra o endereço — `http://127.0.0.1:4747`, ou a porta livre seguinte. A próxima
|
|
221
|
+
execução de crew aparece ali; enquanto não há nenhuma, a página roda uma demonstração. Repetir o
|
|
222
|
+
comando é seguro: ele devolve o mesmo endereço.
|
|
223
|
+
|
|
224
|
+
Se a sua IDE não roda comando em segundo plano, ela mostra o comando para você rodar em outro
|
|
225
|
+
terminal, na pasta do projeto:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
node _opencrew/core/scripts/escritorio.mjs # Ctrl+C para fechar
|
|
229
|
+
node _opencrew/core/scripts/escritorio.mjs --porta 5000
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Para desligar: `/opencrew dashboard off`.
|
|
233
|
+
|
|
234
|
+
O que vale saber antes de ligar:
|
|
235
|
+
|
|
236
|
+
- **Vem desligado.** Sem o `/opencrew dashboard`, nada muda nas suas execuções.
|
|
237
|
+
- **Roda só neste computador, sem internet e sem medição.** A página é servida em `127.0.0.1`,
|
|
238
|
+
só lê o estado das suas crews (`crews/<crew>/state.json`), não carrega nada de fora e não envia
|
|
239
|
+
dado nenhum para lugar nenhum.
|
|
240
|
+
- **Com ele ligado, cada passo custa um comando curto a mais.** É assim que a IA avisa o que está
|
|
241
|
+
fazendo: um comando de terminal por passo. Em IDE que pede aprovação a cada comando, libere o
|
|
242
|
+
`estado.mjs` uma vez.
|
|
243
|
+
- **A tela mostra o que a IA avisa, e pode atrasar.** Se a IA pular um aviso, o desenho só se
|
|
244
|
+
acerta no passo seguinte. Depois de 2 minutos sem novidade a página diz há quanto tempo foi a
|
|
245
|
+
última atualização; depois de 20, o agente aparece "sem sinal" — o que não quer dizer que
|
|
246
|
+
travou: um passo longo é normal.
|
|
247
|
+
- **O Escritório nunca para a execução.** Se o aviso falhar, a crew segue trabalhando.
|
|
248
|
+
- **Até 12 mesas.** Numa crew maior, os agentes a mais aparecem só na lista ao lado.
|
|
249
|
+
|
|
250
|
+
Quem já usa o OpenCrew recebe o Escritório com um `npx @aksp/opencrew@latest update`.
|
|
208
251
|
|
|
209
252
|
---
|
|
210
253
|
|
|
@@ -219,14 +262,24 @@ o que você fez:
|
|
|
219
262
|
|
|
220
263
|
| O que é atualizado | O que NUNCA é tocado |
|
|
221
264
|
|---|---|
|
|
222
|
-
| `_opencrew/core/` (framework) e skills do catálogo |
|
|
223
|
-
| Pastas novas do framework (agentes-base, config) — só o que falta | `_opencrew/_memory/` (perfil, preferências) |
|
|
265
|
+
| `_opencrew/core/` (framework) e skills do catálogo | As crews que você criou em `crews/` |
|
|
266
|
+
| Pastas novas do framework (agentes-base, config) e modelos de crew — só o que falta | `_opencrew/_memory/` (perfil, preferências) |
|
|
224
267
|
| Pontes das IDEs **que você já tem instaladas** (nunca cria de IDE nova) | `_opencrew/best-practices.local/` (suas best-practices) |
|
|
225
|
-
| Bloco
|
|
226
|
-
| Servidor Playwright no `.mcp.json
|
|
227
|
-
|
|
228
|
-
- **Editou um arquivo do framework
|
|
229
|
-
guarda
|
|
268
|
+
| Bloco do OpenCrew em `AGENTS.md`, `CLAUDE.md` e `.gitignore` (o resto do arquivo fica intacto) | `.env` (suas chaves) |
|
|
269
|
+
| Servidor Playwright no `.mcp.json`, entregue uma vez (outros servidores intactos) | |
|
|
270
|
+
|
|
271
|
+
- **Editou um arquivo do framework, um skill do catálogo ou o bloco do OpenCrew?** Antes de
|
|
272
|
+
substituir, o `update` guarda o arquivo inteiro em `.opencrew-backup/<data>/` e lista o que
|
|
273
|
+
copiou. Essa pasta fica fora do git (entra no bloco do `.gitignore`). No primeiro `update` para
|
|
274
|
+
a 1.6.3 há cópia do `.gitignore` mesmo sem edição sua: as versões anteriores não registravam o
|
|
275
|
+
bloco.
|
|
276
|
+
- **Apagou um modelo de crew ou um skill do catálogo?** Ele volta no `update`, e a saída diz o
|
|
277
|
+
que foi entregue de novo.
|
|
278
|
+
- **Removeu o servidor Playwright do `.mcp.json`?** O `update` o entrega uma única vez; se você
|
|
279
|
+
remover de novo, não volta. A versão fixada no arquivo não é trocada.
|
|
280
|
+
- **Uma IDE só conta como instalada pelo arquivo de ponte dela**, não por um arquivo seu que cite
|
|
281
|
+
o OpenCrew. Texto antigo das pontes (instalações até a 1.2.2) é retirado, com cópia, quando
|
|
282
|
+
está idêntico ao que o OpenCrew gravou.
|
|
230
283
|
- **Versão mais nova instalada?** O `update` não volta para uma versão mais antiga (cache do
|
|
231
284
|
`npx`): ele para e pede `npx @aksp/opencrew@latest update`.
|
|
232
285
|
|
|
@@ -241,6 +294,8 @@ npx @aksp/opencrew@latest init --repair-bridges --all # as 9 IDEs
|
|
|
241
294
|
Sem `--ide` e sem `--all`, o `init --repair-bridges` usa a mesma detecção do `update` (aqui o
|
|
242
295
|
`--yes` não escolhe IDE); se não encontra nenhuma ponte, para com erro e pede `--ide=<id>`.
|
|
243
296
|
O reparo não instala: numa pasta sem workspace do OpenCrew ele para com erro e pede o `init`.
|
|
297
|
+
Ele também só roda com o pacote na mesma versão do projeto: com outra versão, para sem alterar
|
|
298
|
+
nada e pede o `update`.
|
|
244
299
|
Ponte de arquivo inteiro que você editou (ex.: `.claude/skills/opencrew/SKILL.md`) é copiada
|
|
245
300
|
antes para `.opencrew-backup/<data>/`, e o resumo do `init --repair-bridges` lista cada cópia.
|
|
246
301
|
|
|
@@ -272,7 +327,9 @@ npx @aksp/opencrew update --check
|
|
|
272
327
|
| `/opencrew delete <nome>` | Remove uma crew |
|
|
273
328
|
| `/opencrew skills` | Navega, instala ou remove skills |
|
|
274
329
|
| `/opencrew install <skill>` | Instala uma skill do catálogo |
|
|
275
|
-
| `/opencrew settings` | Altera preferências (idioma, tier,
|
|
330
|
+
| `/opencrew settings` | Altera preferências (idioma, tier, Escritório) |
|
|
331
|
+
| `/opencrew dashboard` | Liga e abre o Escritório ao vivo (a equipe trabalhando, no navegador) |
|
|
332
|
+
| `/opencrew dashboard off` | Desliga o Escritório |
|
|
276
333
|
| `/opencrew show-company` | Mostra o perfil da empresa |
|
|
277
334
|
| `/opencrew edit-company` | Reconfigura o perfil da empresa |
|
|
278
335
|
| `/opencrew help` | Mostra a lista de comandos |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aksp/opencrew",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"engines": {
|
|
53
|
-
"node": ">=20.
|
|
53
|
+
"node": ">=20.17.0"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@inquirer/checkbox": "^5.1.0",
|
package/src/cli.js
CHANGED
|
@@ -5,34 +5,9 @@ import { allIdeIds } from './lib/ides.js';
|
|
|
5
5
|
import { UsageError, isPromptCancel } from './lib/errors.js';
|
|
6
6
|
import { init } from './commands/init.js';
|
|
7
7
|
import { update } from './commands/update.js';
|
|
8
|
+
import { nodeBelowFloor } from './lib/node-version.js';
|
|
8
9
|
import { c, log, err, warn, info } from './lib/ui.js';
|
|
9
10
|
|
|
10
|
-
// Extract the minimum required Node version from an engines.node range string.
|
|
11
|
-
// Handles: ">=20.0.0", "^20.5", ">=18.0.0 || >=20.0.0", plain "20.0.0".
|
|
12
|
-
function minNodeVersion(range) {
|
|
13
|
-
// Split on || and take the lowest version (user is expected to meet at least one).
|
|
14
|
-
const parts = range.split(/\s*\|\|\s*/);
|
|
15
|
-
let lowest = null;
|
|
16
|
-
for (const part of parts) {
|
|
17
|
-
const v = part.replace(/[^0-9.]/g, '');
|
|
18
|
-
if (!v) continue;
|
|
19
|
-
if (!lowest || lt(v, lowest)) lowest = v;
|
|
20
|
-
}
|
|
21
|
-
return lowest;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Simple semver comparison (no prerelease tags). Returns true if a < b.
|
|
25
|
-
function lt(a, b) {
|
|
26
|
-
const pa = a.split('.').map(Number);
|
|
27
|
-
const pb = b.split('.').map(Number);
|
|
28
|
-
for (let i = 0; i < 3; i++) {
|
|
29
|
-
const na = pa[i] || 0;
|
|
30
|
-
const nb = pb[i] || 0;
|
|
31
|
-
if (na !== nb) return na < nb;
|
|
32
|
-
}
|
|
33
|
-
return false; // equal
|
|
34
|
-
}
|
|
35
|
-
|
|
36
11
|
const OPTION_SPEC = {
|
|
37
12
|
help: { type: 'boolean', short: 'h' },
|
|
38
13
|
version: { type: 'boolean', short: 'v' },
|
|
@@ -128,11 +103,15 @@ export function reportError(e) {
|
|
|
128
103
|
}
|
|
129
104
|
err(e?.message ?? String(e));
|
|
130
105
|
if (e instanceof UsageError) info(`Run ${c.cyan('npx @aksp/opencrew help')} for usage.`);
|
|
131
|
-
else if (process.env.OPENCREW_DEBUG) console.error(e?.stack);
|
|
106
|
+
else if (process.env.OPENCREW_DEBUG) console.error(e?.cause ? e : e?.stack); // with a cause: both stacks
|
|
132
107
|
return 1;
|
|
133
108
|
}
|
|
134
109
|
|
|
135
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @param {string[]} argv
|
|
112
|
+
* @param {{ commands?: object, nodeVersion?: string }} deps injectable for tests
|
|
113
|
+
*/
|
|
114
|
+
export async function run(argv, { commands = { init, update }, nodeVersion = process.versions.node } = {}) {
|
|
136
115
|
let command, opts;
|
|
137
116
|
try {
|
|
138
117
|
({ command, opts } = parseArgs(argv));
|
|
@@ -154,16 +133,14 @@ export async function run(argv, { commands = { init, update } } = {}) {
|
|
|
154
133
|
return;
|
|
155
134
|
}
|
|
156
135
|
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
136
|
+
// Below the floor of engines.node every command stops here — --version and help too —
|
|
137
|
+
// before anything is written (spec R2, rule 27).
|
|
138
|
+
const tooOld = nodeBelowFloor(engines.node, nodeVersion);
|
|
139
|
+
if (tooOld) {
|
|
140
|
+
err(tooOld[0]);
|
|
141
|
+
info(tooOld[1]);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
167
144
|
}
|
|
168
145
|
|
|
169
146
|
// --version / --help never run a command (they may follow any command).
|
package/src/commands/init.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { promises as fs } from 'node:fs';
|
|
3
3
|
import { templatesDir, packageJsonPath } from '../lib/paths.js';
|
|
4
|
-
import { exists,
|
|
5
|
-
import { newDelivery, deliverTree, deliverFile, writeManifest, readManifest } from '../lib/manifest.js';
|
|
4
|
+
import { exists, readJson } from '../lib/fsx.js';
|
|
5
|
+
import { newDelivery, deliverTree, deliverFile, writeManifest, readManifest, manifestUnreadable, UNREADABLE } from '../lib/manifest.js';
|
|
6
|
+
import { deliverBlock, deliverBridges } from '../lib/blocos.js';
|
|
7
|
+
import { createMcp } from '../lib/mcp.js';
|
|
6
8
|
import { ideById, allIdeIds, AGENTS_BRIDGE } from '../lib/ides.js';
|
|
7
9
|
import { pickIdes as promptIdes } from '../lib/prompts.js';
|
|
8
10
|
import { UsageError } from '../lib/errors.js';
|
|
9
|
-
import {
|
|
11
|
+
import { withoutLegacy, legacyLines } from '../lib/legado.js';
|
|
12
|
+
import { repairIdeIds, repairVersionGuard, backupSummary, recordRepair, NO_BRIDGES_FOUND, NO_WORKSPACE } from '../lib/migrations.js';
|
|
13
|
+
import { ALREADY_INSTALLED } from '../lib/resumo.js';
|
|
10
14
|
import { c, log, info, ok, warn, step } from '../lib/ui.js';
|
|
11
15
|
|
|
12
16
|
const STAMP = path.join('_opencrew', '.opencrew-version');
|
|
13
|
-
// .gitignore / .env.example belong to the user: opencrew only owns a marked block at the end.
|
|
14
|
-
const SHARED_BLOCK = { comment: 'hash', position: 'append' };
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* @param {object} opts parsed CLI options
|
|
@@ -28,9 +30,8 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
28
30
|
|
|
29
31
|
if (state === 'complete') {
|
|
30
32
|
warn('An opencrew workspace already exists here.');
|
|
31
|
-
info(
|
|
33
|
+
info(ALREADY_INSTALLED); // R2 rule 15: never tells the user to delete _opencrew/
|
|
32
34
|
info(`To repair IDE bridges, use: ${c.cyan('npx @aksp/opencrew@latest init --repair-bridges')}`);
|
|
33
|
-
info(`To reinstall from scratch, delete _opencrew/ first, then run init again.`);
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -56,23 +57,22 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
56
57
|
await deliverFile(ctx, path.join(target, '_opencrew', 'core', 'system.md'), await tpl('AGENTS.md'), { overwrite: true });
|
|
57
58
|
ok('_opencrew/core/system.md (full system definition)');
|
|
58
59
|
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
60
|
+
const agents = await deliverBlock(ctx, 'AGENTS.md', AGENTS_BRIDGE);
|
|
61
|
+
if (agents.action === 'added') info('AGENTS.md (merged — existing content preserved)');
|
|
61
62
|
else ok('AGENTS.md (bridge to system.md)');
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
info(mcpWritten ? '.mcp.json' : '.mcp.json (kept existing)');
|
|
64
|
+
// Created here → recorded in the manifest as delivered; the user's own file is kept as it is.
|
|
65
|
+
info((await createMcp(ctx, await tpl('.mcp.json'))) ? '.mcp.json' : '.mcp.json (kept existing)');
|
|
67
66
|
|
|
67
|
+
// .gitignore / .env.example belong to the user: opencrew only owns a marked block at the end.
|
|
68
68
|
for (const [file, template] of [['.env.example', '.env.example'], ['.gitignore', 'gitignore']]) {
|
|
69
|
-
const res = await
|
|
70
|
-
info(res.
|
|
69
|
+
const res = await deliverBlock(ctx, file, await tpl(template));
|
|
70
|
+
info(res.action === 'added' ? `${file} (opencrew block added at the end — your lines kept)` : file);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// 3. IDE bridge files.
|
|
74
74
|
step('Configuring AI IDEs');
|
|
75
|
-
await writeBridges(
|
|
75
|
+
await writeBridges(ctx, ids, false);
|
|
76
76
|
|
|
77
77
|
if (ids.includes('claude-code')) {
|
|
78
78
|
warn(`opencrew ships its own Playwright MCP server (.mcp.json) — disable Claude Code's`);
|
|
@@ -83,6 +83,7 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
83
83
|
// version stamp LAST: it is what marks the install as complete.
|
|
84
84
|
await writeManifest(target, version, ctx.files);
|
|
85
85
|
await fs.writeFile(path.join(target, STAMP), version + '\n');
|
|
86
|
+
reportBackups(ctx); // a reinstall over blocks the user edited copies those files first
|
|
86
87
|
|
|
87
88
|
// 5. Done.
|
|
88
89
|
log(`\n${c.green(c.bold('Done!'))} opencrew is installed.\n`);
|
|
@@ -95,20 +96,30 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
95
96
|
/**
|
|
96
97
|
* --repair-bridges: rewrite IDE bridge files in an existing workspace. --ide wins (even next
|
|
97
98
|
* to --all); --all alone means every IDE; otherwise only the IDEs `update` would detect.
|
|
99
|
+
* Only with the package at the version of the workspace (R2 rule 6), whatever the options.
|
|
98
100
|
*/
|
|
99
101
|
async function repairBridges(target, version, opts) {
|
|
102
|
+
const otherVersion = await repairVersionGuard(target, version);
|
|
103
|
+
if (otherVersion) throw new Error(otherVersion); // before any write; exit 1, no usage hint
|
|
100
104
|
const ids = await resolveIdes({ ide: opts.ide }, () => repairIdeIds(target, opts));
|
|
101
105
|
if (!ids.length) throw new UsageError(NO_BRIDGES_FOUND); // before the first write
|
|
102
106
|
log(`\n${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — repairing IDE bridges`);
|
|
103
107
|
log(c.dim(`Target: ${target}\n`));
|
|
104
108
|
const ctx = newDelivery(target, await readManifest(target));
|
|
105
|
-
await
|
|
106
|
-
|
|
109
|
+
await withoutLegacy(ctx, ids.map(ideById), () => writeBridges(ctx, ids, true)); // R2 rule 3
|
|
110
|
+
for (const line of legacyLines(ctx)) warn(line);
|
|
111
|
+
if (await manifestUnreadable(target)) warn(UNREADABLE.repair); // R2 rule 12: copied as with none
|
|
112
|
+
await recordRepair(ctx, version); // only where a manifest already exists (and can be read)
|
|
113
|
+
reportBackups(ctx);
|
|
114
|
+
log(`\n${c.green(c.bold('Done!'))} IDE bridges regenerated.\n`);
|
|
115
|
+
log(`${c.bold('Next step:')} Restart your IDE, then type ${c.cyan('/opencrew')} to verify.\n`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Say where the backup copies of this run are (nothing when none was made). */
|
|
119
|
+
function reportBackups(ctx) {
|
|
107
120
|
const [copied, ...copies] = backupSummary(ctx);
|
|
108
121
|
if (copied) warn(copied);
|
|
109
122
|
for (const copy of copies) log(copy);
|
|
110
|
-
log(`\n${c.green(c.bold('Done!'))} IDE bridges regenerated.\n`);
|
|
111
|
-
log(`${c.bold('Next step:')} Restart your IDE, then type ${c.cyan('/opencrew')} to verify.\n`);
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
/**
|
|
@@ -155,31 +166,17 @@ async function resolveIdes(opts, fallback) {
|
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
/**
|
|
158
|
-
* Write
|
|
159
|
-
*
|
|
160
|
-
* @param {string[]} ids — validated IDE ids to configure
|
|
161
|
-
* @param {{ overwrite: boolean }} opts
|
|
169
|
+
* Write the bridge files of the IDEs `ids` (validated) and say what happened to each.
|
|
170
|
+
* `overwrite`: replace a whole-file bridge that differs (repair) or keep it (init).
|
|
162
171
|
*/
|
|
163
|
-
async function writeBridges(
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
for (const
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
writtenPaths.add(f.path);
|
|
174
|
-
const fp = path.join(target, f.path);
|
|
175
|
-
const hasFrontmatter = f.content.startsWith('---');
|
|
176
|
-
if (hasFrontmatter) {
|
|
177
|
-
await deliverFile(ctx, fp, f.content, { overwrite });
|
|
178
|
-
} else {
|
|
179
|
-
const result = await writeBridgeFile(fp, f.content);
|
|
180
|
-
if (result.merged) info(`${f.path} (merged — existing content preserved)`);
|
|
181
|
-
else if (result.written && overwrite) info(`${f.path} (regenerated)`);
|
|
182
|
-
}
|
|
172
|
+
async function writeBridges(ctx, ids, overwrite) {
|
|
173
|
+
const ides = ids.map(ideById);
|
|
174
|
+
const written = await deliverBridges(ctx, ides, { overwrite });
|
|
175
|
+
for (const ide of ides) {
|
|
176
|
+
for (const f of written.filter((w) => w.ide === ide)) {
|
|
177
|
+
if (f.shared) info(`${f.file} (shared path — written once)`);
|
|
178
|
+
else if (f.action === 'added') info(`${f.file} (merged — existing content preserved)`);
|
|
179
|
+
else if (f.block && f.action !== 'kept' && overwrite) info(`${f.file} (regenerated)`);
|
|
183
180
|
}
|
|
184
181
|
ok(`${ide.label} → ${ide.files.map((f) => f.path).join(', ')}`);
|
|
185
182
|
}
|