@aksp/opencrew 1.5.0 → 1.6.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/CHANGELOG.md +90 -0
- package/README.md +35 -14
- package/package.json +1 -1
- package/src/cli.js +2 -1
- package/src/commands/init.js +42 -30
- package/src/commands/update.js +58 -42
- package/src/lib/ides.js +15 -12
- package/src/lib/manifest.js +89 -0
- package/src/lib/migrations.js +147 -0
- package/templates/.mcp.json +1 -1
- package/templates/AGENTS.md +2 -1
- package/templates/_opencrew/.opencrew-version +1 -1
- package/templates/_opencrew/core/prompts/build.prompt.md +14 -0
- package/templates/_opencrew/core/prompts/discovery.prompt.md +15 -2
- package/templates/_opencrew/core/runner.pipeline.md +86 -20
- package/templates/_opencrew/core/scripts/comum.mjs +48 -0
- package/templates/_opencrew/core/scripts/conferir-fontes/busca.mjs +82 -0
- package/templates/_opencrew/core/scripts/conferir-fontes/coleta.mjs +104 -0
- package/templates/_opencrew/core/scripts/conferir-fontes/relatorio.mjs +52 -0
- package/templates/_opencrew/core/scripts/conferir-fontes.mjs +135 -0
- package/templates/_opencrew/core/scripts/verificar/argumentos.mjs +33 -0
- package/templates/_opencrew/core/scripts/verificar/arquivos.mjs +50 -0
- package/templates/_opencrew/core/scripts/verificar/html.mjs +52 -0
- package/templates/_opencrew/core/scripts/verificar/leitura.mjs +138 -66
- package/templates/_opencrew/core/scripts/verificar/medicao.mjs +142 -0
- package/templates/_opencrew/core/scripts/verificar/pecas.mjs +190 -0
- package/templates/_opencrew/core/scripts/verificar/proibicoes.mjs +100 -0
- package/templates/_opencrew/core/scripts/verificar/regras.mjs +110 -91
- package/templates/_opencrew/core/scripts/verificar/relatorio.mjs +43 -0
- package/templates/_opencrew/core/scripts/verificar/secoes.mjs +145 -0
- package/templates/_opencrew/core/scripts/verificar.mjs +162 -88
- package/templates/skills/opencrew-best-practice-creator/SKILL.md +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,96 @@
|
|
|
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.6.1] — 2026-10-05
|
|
7
|
+
|
|
8
|
+
Fase R1 "Reparos da 1.6.0: o verificador mede de verdade" (`specs/fase-r1-reparos-1-6-1.md`),
|
|
9
|
+
vinda da revisão das specs (`docs/auditoria/2026-10-04-revisao-specs.md`). Chega a quem já usa com
|
|
10
|
+
um `npx @aksp/opencrew@latest update`.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **O verificador mede o texto escrito com rótulos**, do jeito que os próprios best-practices
|
|
14
|
+
ensinam (`=== CAPTION ===`, `=== HASHTAGS ===`, `=== SLIDES ===`, `=== HOOK ===`, `=== TWEET ===`,
|
|
15
|
+
`=== TITLE ===`). Antes respondia "Nada a apontar" sem medir.
|
|
16
|
+
- **"Não medido" é dito**: com o formato informado e a peça principal não achada, o relatório
|
|
17
|
+
alerta em vez de aprovar em silêncio. O resumo passa a ser
|
|
18
|
+
`X bloqueios, Y alertas, Z não medidos`.
|
|
19
|
+
- **Bloqueios falsos**: cor hexadecimal (`#666666`), número comum, CEP e "XXX Congresso" não são
|
|
20
|
+
mais "Placeholder"; `{{name}}` em e-mail e WhatsApp vira nota; termo proibido vale como palavra
|
|
21
|
+
inteira ("IA" não bloqueia "dia a dia"); o termo que o usuário mandou preferir não é proibido.
|
|
22
|
+
- **Arquivo local sem limites não desliga o verificador**: os limites de
|
|
23
|
+
`_opencrew/best-practices.local/` somam aos do core, chave a chave.
|
|
24
|
+
- **Erros que passavam por OK**: rodar fora da pasta do projeto ou com crew inexistente agora dá
|
|
25
|
+
erro (código 1), sem linha de status; um arquivo ausente na lista não derruba a verificação dos
|
|
26
|
+
outros; imagem e `.docx` não são lidos como texto; no HTML, só o texto visível e os links;
|
|
27
|
+
arquivo de texto fora do UTF-8 vira alerta "Não verificado" (UTF-16 com marca é lido).
|
|
28
|
+
- **Relatório que não saía**: com o projeto aberto por junção ou link de pasta, os dois scripts
|
|
29
|
+
terminavam sem imprimir nada.
|
|
30
|
+
- **Várias peças no mesmo arquivo** são medidas uma a uma (três posts não viram uma soma); a linha
|
|
31
|
+
`---` não encerra mais a seção; slides contados nas escritas comuns ("📌 Slide 2", "Slide #3").
|
|
32
|
+
Título e meta description em bloco YAML (`>-`, `|`) são medidos inteiros; `[PREENCHER: …]`
|
|
33
|
+
longo não escapa; imagem e link de âncora não contam como link.
|
|
34
|
+
- **Conferência de fontes**: enxerga `caminho:` com comentário ou aspas e os arquivos de `agents/`
|
|
35
|
+
(agentes e tasks); recusa crew de fora do projeto; mensagens corrigidas ("Não há correção
|
|
36
|
+
automática…", aviso de busca parcial); caminho com marcador de modelo (`AAAA-MM-DD`) e comando
|
|
37
|
+
entre crases não são conferidos; o `--corrigir` troca só o caminho citado (antes trocava
|
|
38
|
+
qualquer trecho igual) e nunca aponta o destino de gravação de um agente para um arquivo que
|
|
39
|
+
já existe.
|
|
40
|
+
- **`init --repair-bridges`** sem `--ide` regrava só as IDEs instaladas (antes criava as pontes
|
|
41
|
+
das 9); `--all` regrava todas; o resumo lista as cópias de segurança. Numa pasta sem workspace,
|
|
42
|
+
para com erro em vez de instalar; num workspace sem manifesto, não cria um.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- **Runner**: passa o formato de cada arquivo ao verificador (`caminho=formato`); laço de revisão
|
|
46
|
+
com 3 ciclos por padrão (`max_review_cycles`) e saída também quando não há bloqueio; regras do
|
|
47
|
+
revisor injetadas em toda execução (valem para crews já criadas); avisa quando um script não
|
|
48
|
+
rodou; a conferência de fontes roda antes de carregar as fontes; a aprovação final mostra o que
|
|
49
|
+
ficou sem medir e as notas do relatório.
|
|
50
|
+
- Crew nova recebe o limite de ciclos de revisão pelo tier: Express 1, Standard 2, Full 3.
|
|
51
|
+
- Em blog, a seção com cabeçalho de outro canal ("Como postar no LinkedIn") não é medida como
|
|
52
|
+
post, e o relatório diz isso ("Não medido").
|
|
53
|
+
- Hashtags sob um cabeçalho que cita o canal ("Hashtags LinkedIn") somam à peça desse canal.
|
|
54
|
+
- "Aceitar assim mesmo" não promete mais registro: o registro chega com a entrega por canal.
|
|
55
|
+
- Texto solto depois de uma linha `---` passa a contar na peça de cima. Telefone falso de 8 ou 9
|
|
56
|
+
dígitos, fora de link, deixa de ser pego.
|
|
57
|
+
|
|
58
|
+
### Internal
|
|
59
|
+
- Scripts do runtime em módulos (`verificar/`, `conferir-fontes/`, `comum.mjs`); leitor de peças
|
|
60
|
+
exportado para a próxima fase. Todos os cenários R1 com teste de mesmo ID; teste de upgrade
|
|
61
|
+
1.6.0 → 1.6.1.
|
|
62
|
+
- Revisão do código antes da tag: sete leituras independentes e duas rodadas de conserto com
|
|
63
|
+
teste; o que ficou adiado tem destino na spec (§11 e §12).
|
|
64
|
+
- Revisão das specs (265 achados) e faxina de documentos; specs R1, U3a e U3b.
|
|
65
|
+
|
|
66
|
+
## [1.6.0] — 2026-10-02
|
|
67
|
+
|
|
68
|
+
Trilha U2 "Crew que conhece o projeto" + U6 "Convivência" (`specs/fase-u2-crew-que-conhece-o-projeto.md`).
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
- **`fontes:` no `crew.yaml`** — arquivos/pastas do projeto (caminho relativo) que a crew lê em todo
|
|
72
|
+
run e trata como verdade; o discovery pergunta quais são.
|
|
73
|
+
- **Conferência de fontes** (`_opencrew/core/scripts/conferir-fontes.mjs`) no início de cada run:
|
|
74
|
+
arquivo movido → acha o novo lugar e oferece corrigir (`--corrigir`, com `.bak`); nome diferente →
|
|
75
|
+
lista a pasta; caminho absoluto → alerta "não é portátil". No uso real (Projeto B) achou os 5
|
|
76
|
+
caminhos quebrados pela reorganização, cada um com o lugar exato.
|
|
77
|
+
- **Correção gravada na hora** — o que o usuário corrige num checkpoint vai para a memória antes do
|
|
78
|
+
próximo passo; termo removido vira proibição entre aspas (trava do verificador); conflito com o
|
|
79
|
+
`company.md` gera a pergunta "Atualizo o perfil da empresa?".
|
|
80
|
+
- **`_opencrew/best-practices.local/`** — best-practices do usuário (aprendidas/criadas), lidas antes
|
|
81
|
+
das do core e nunca tocadas pelo `update`; o verificador também lê os limites dali primeiro.
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
- **`update` completo e seguro**: entrega pastas novas do framework (agentes-base, config, templates
|
|
85
|
+
de crew) sem sobrescrever; guarda em `.opencrew-backup/<data>/` o que o usuário editou antes de
|
|
86
|
+
substituir (manifesto `_opencrew/manifest.json`); recusa voltar para versão mais antiga; atualiza
|
|
87
|
+
as pontes **só das IDEs instaladas**; faz merge do Playwright no `.mcp.json` (saída em
|
|
88
|
+
`_opencrew/logs/playwright/`, outros servidores intactos); avisa sobre pontes antigas (`opensquad`).
|
|
89
|
+
- **Convivência**: pontes e bloco do `AGENTS.md` só ativam o OpenCrew com `/opencrew` (ou pedido
|
|
90
|
+
sobre crews) e apontam direto para `_opencrew/core/system.md`; outras instruções do projeto têm
|
|
91
|
+
prioridade no resto.
|
|
92
|
+
- Migração do formato de memória faz `memories.md.bak` e avisa (fim do reset silencioso); regra única
|
|
93
|
+
sobre o que vai para a memória (só feedback explícito).
|
|
94
|
+
- `_build/discovery.yaml` agora em `crews/{code}/_build/`; build grava caminhos relativos à raiz.
|
|
95
|
+
|
|
6
96
|
## [1.5.0] — 2026-10-02
|
|
7
97
|
|
|
8
98
|
Trilha U1 "Revisor com dentes" — primeira melhoria vinda do uso real
|
package/README.md
CHANGED
|
@@ -40,6 +40,10 @@ 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
|
+
- 📂 **Crew que conhece o projeto** — liste em `fontes:` os arquivos e pastas do seu projeto
|
|
44
|
+
(decisões, calendário, manual de marca) e a crew os lê em todo run, tratando-os como verdade.
|
|
45
|
+
Reorganizou as pastas? No início do run ela confere os caminhos, acha para onde o arquivo foi
|
|
46
|
+
e oferece corrigir. Correções que você faz num checkpoint ficam gravadas na hora.
|
|
43
47
|
|
|
44
48
|
---
|
|
45
49
|
|
|
@@ -139,6 +143,12 @@ enxuto — todos apontam para a mesma fonte.
|
|
|
139
143
|
| `QWEN.md` (ponte) + `.agents/skills/opencrew/SKILL.md` | Qwen Code |
|
|
140
144
|
| `AGENTS.md` (ponte) + `.trae/rules/opencrew.md` | Trae |
|
|
141
145
|
|
|
146
|
+
> **Claude Cowork (modo alternativo):** o Cowork não reconhece o comando `/opencrew` (ele não lê
|
|
147
|
+
> skills de dentro da pasta do projeto). Funciona assim: abra a pasta do projeto e peça, em texto:
|
|
148
|
+
> *"Leia o arquivo `_opencrew/core/system.md` deste projeto e siga as instruções dele. Mostre o
|
|
149
|
+
> menu principal."* Depois use frases como "rodar a crew blog-semanal" no lugar dos comandos com `/`.
|
|
150
|
+
|
|
151
|
+
|
|
142
152
|
> ⚠️ **Importante:** `CLAUDE.md`, `GEMINI.md` e os demais arquivos de IDE são
|
|
143
153
|
> pontes geradas automaticamente. Eles são finos (5-10 linhas) e usam blocos
|
|
144
154
|
> marcados (`<!-- opencrew:start/end -->`) que permitem **merge não-destrutivo**
|
|
@@ -194,34 +204,45 @@ meu-projeto/
|
|
|
194
204
|
|
|
195
205
|
> O dashboard visual (`dashboard/index.html`) **não é instalado** pelo `init` — ele vive
|
|
196
206
|
> só no repositório do OpenCrew e ainda é experimental (decisão de publicar ou remover:
|
|
197
|
-
>
|
|
207
|
+
> fase U3a — ver `IDEIAS.md` no repositório).
|
|
198
208
|
|
|
199
209
|
---
|
|
200
210
|
|
|
201
211
|
## Mantendo o OpenCrew atualizado
|
|
202
212
|
|
|
203
213
|
```bash
|
|
204
|
-
npx @aksp/opencrew update
|
|
214
|
+
npx @aksp/opencrew@latest update
|
|
205
215
|
```
|
|
206
216
|
|
|
207
|
-
|
|
217
|
+
Um único comando traz **todas** as melhorias para quem já usa uma versão antiga — sem perder
|
|
218
|
+
o que você fez:
|
|
208
219
|
|
|
209
220
|
| O que é atualizado | O que NUNCA é tocado |
|
|
210
221
|
|---|---|
|
|
211
|
-
| `_opencrew/core/` (framework
|
|
212
|
-
|
|
|
213
|
-
| `_opencrew/
|
|
214
|
-
| Bloco `<!-- opencrew -->`
|
|
222
|
+
| `_opencrew/core/` (framework) e skills do catálogo | `crews/` (suas crews) |
|
|
223
|
+
| Pastas novas do framework (agentes-base, config) — só o que falta | `_opencrew/_memory/` (perfil, preferências) |
|
|
224
|
+
| Pontes das IDEs **que você já tem instaladas** (nunca cria de IDE nova) | `_opencrew/best-practices.local/` (suas best-practices) |
|
|
225
|
+
| Bloco `<!-- opencrew -->` do `AGENTS.md`/`CLAUDE.md` (o resto do arquivo fica intacto) | `.env` (suas chaves) |
|
|
226
|
+
| Servidor Playwright no `.mcp.json` (outros servidores intactos) | |
|
|
227
|
+
|
|
228
|
+
- **Editou um arquivo do framework ou um skill do catálogo?** Antes de substituir, o `update`
|
|
229
|
+
guarda a sua versão em `.opencrew-backup/<data>/` e lista o que copiou.
|
|
230
|
+
- **Versão mais nova instalada?** O `update` não volta para uma versão mais antiga (cache do
|
|
231
|
+
`npx`): ele para e pede `npx @aksp/opencrew@latest update`.
|
|
215
232
|
|
|
216
|
-
|
|
217
|
-
atual, use:
|
|
233
|
+
Para regravar as pontes de IDE num workspace que já existe:
|
|
218
234
|
|
|
219
235
|
```bash
|
|
220
|
-
npx @aksp/opencrew init --repair-bridges
|
|
236
|
+
npx @aksp/opencrew@latest init --repair-bridges # só as IDEs que você já tem instaladas
|
|
237
|
+
npx @aksp/opencrew@latest init --repair-bridges --ide=claude-code # só as indicadas (ou uma IDE nova)
|
|
238
|
+
npx @aksp/opencrew@latest init --repair-bridges --all # as 9 IDEs
|
|
221
239
|
```
|
|
222
240
|
|
|
223
|
-
|
|
224
|
-
|
|
241
|
+
Sem `--ide` e sem `--all`, o `init --repair-bridges` usa a mesma detecção do `update` (aqui o
|
|
242
|
+
`--yes` não escolhe IDE); se não encontra nenhuma ponte, para com erro e pede `--ide=<id>`.
|
|
243
|
+
O reparo não instala: numa pasta sem workspace do OpenCrew ele para com erro e pede o `init`.
|
|
244
|
+
Ponte de arquivo inteiro que você editou (ex.: `.claude/skills/opencrew/SKILL.md`) é copiada
|
|
245
|
+
antes para `.opencrew-backup/<data>/`, e o resumo do `init --repair-bridges` lista cada cópia.
|
|
225
246
|
|
|
226
247
|
Se você está migrando de uma versão anterior a v1.3, o `update` detecta
|
|
227
248
|
AGENTS.md legados (sistema completo de 150 linhas) e os substitui pela ponte
|
|
@@ -261,12 +282,12 @@ npx @aksp/opencrew update --check
|
|
|
261
282
|
| Comando | O que faz |
|
|
262
283
|
|---|---|
|
|
263
284
|
| `npx @aksp/opencrew init` | Instala o OpenCrew na pasta atual |
|
|
264
|
-
| `npx @aksp/opencrew update` | Atualiza o framework |
|
|
285
|
+
| `npx @aksp/opencrew@latest update` | Atualiza o framework |
|
|
265
286
|
| `npx @aksp/opencrew update --check` (ou `--dry-run`) | Verifica se há update disponível, sem alterar nada |
|
|
266
287
|
| `npx @aksp/opencrew upgrade` | Atalho para `update` |
|
|
267
288
|
| `npx @aksp/opencrew init --ide=claude-code,cursor` | Instala só as pontes das IDEs indicadas |
|
|
268
289
|
| `npx @aksp/opencrew init --all` (ou `-y`) | Instala as pontes de todas as IDEs |
|
|
269
|
-
| `npx @aksp/opencrew init --repair-bridges` | Regrava as pontes
|
|
290
|
+
| `npx @aksp/opencrew@latest init --repair-bridges` | Regrava as pontes das IDEs já instaladas num workspace existente (`--ide=a,b`: só as indicadas; `--all`: as 9) |
|
|
270
291
|
| `npx @aksp/opencrew version` | Mostra a versão instalada |
|
|
271
292
|
| `npx @aksp/opencrew help` | Mostra ajuda dos comandos CLI |
|
|
272
293
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -103,7 +103,8 @@ ${c.bold('Options for init')}
|
|
|
103
103
|
--ide=a,b Preselect IDEs (skip the prompt). Valid: ${allIdeIds().join(', ')}
|
|
104
104
|
--all Configure every supported IDE
|
|
105
105
|
--yes, -y Non-interactive; accept defaults
|
|
106
|
-
--repair-bridges
|
|
106
|
+
--repair-bridges Rewrite the bridges of the IDEs already installed in an existing
|
|
107
|
+
workspace (with --ide: only those; with --all: every IDE)
|
|
107
108
|
|
|
108
109
|
${c.bold('Options for update')}
|
|
109
110
|
--check Report whether an update is available without making changes
|
package/src/commands/init.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
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 {
|
|
4
|
+
import { exists, writeFileSafe, readJson, writeBridgeFile } from '../lib/fsx.js';
|
|
5
|
+
import { newDelivery, deliverTree, deliverFile, writeManifest, readManifest } from '../lib/manifest.js';
|
|
5
6
|
import { ideById, allIdeIds, AGENTS_BRIDGE } from '../lib/ides.js';
|
|
6
7
|
import { pickIdes as promptIdes } from '../lib/prompts.js';
|
|
7
8
|
import { UsageError } from '../lib/errors.js';
|
|
9
|
+
import { repairIdeIds, backupSummary, recordRepair, NO_BRIDGES_FOUND, NO_WORKSPACE } from '../lib/migrations.js';
|
|
8
10
|
import { c, log, info, ok, warn, step } from '../lib/ui.js';
|
|
9
11
|
|
|
10
12
|
const STAMP = path.join('_opencrew', '.opencrew-version');
|
|
@@ -21,22 +23,13 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
21
23
|
const version = pkg.version;
|
|
22
24
|
const state = await workspaceState(target);
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
if (opts['repair-bridges']
|
|
26
|
-
const ids = await resolveIdes(opts, async () => allIdeIds());
|
|
27
|
-
log(`\n${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — repairing IDE bridges`);
|
|
28
|
-
log(c.dim(`Target: ${target}\n`));
|
|
29
|
-
await writeBridges(target, ids, { overwrite: true });
|
|
30
|
-
|
|
31
|
-
log(`\n${c.green(c.bold('Done!'))} IDE bridges regenerated.\n`);
|
|
32
|
-
log(`${c.bold('Next step:')} Restart your IDE, then type ${c.cyan('/opencrew')} to verify.\n`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
26
|
+
if (opts['repair-bridges'] && state === 'none') throw new UsageError(NO_WORKSPACE); // before any write
|
|
27
|
+
if (opts['repair-bridges']) return repairBridges(target, version, opts);
|
|
35
28
|
|
|
36
29
|
if (state === 'complete') {
|
|
37
30
|
warn('An opencrew workspace already exists here.');
|
|
38
|
-
info(`To update only the framework, use: ${c.cyan('npx @aksp/opencrew update')}`);
|
|
39
|
-
info(`To repair IDE bridges, use: ${c.cyan('npx @aksp/opencrew init --repair-bridges')}`);
|
|
31
|
+
info(`To update only the framework, use: ${c.cyan('npx @aksp/opencrew@latest update')}`);
|
|
32
|
+
info(`To repair IDE bridges, use: ${c.cyan('npx @aksp/opencrew@latest init --repair-bridges')}`);
|
|
40
33
|
info(`To reinstall from scratch, delete _opencrew/ first, then run init again.`);
|
|
41
34
|
return;
|
|
42
35
|
}
|
|
@@ -51,7 +44,8 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
51
44
|
|
|
52
45
|
// 1. Copy the framework payload (never clobber user work).
|
|
53
46
|
step('Installing framework files');
|
|
54
|
-
const
|
|
47
|
+
const ctx = newDelivery(target, null);
|
|
48
|
+
const copied = await installPayload(target, ctx);
|
|
55
49
|
ok(`Framework files ready (${copied} written, existing files preserved)`);
|
|
56
50
|
|
|
57
51
|
// 2. System doc + root configs.
|
|
@@ -59,7 +53,7 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
59
53
|
|
|
60
54
|
// Full system definition lives in _opencrew/core/ — never at project root.
|
|
61
55
|
// The root AGENTS.md is just a thin bridge (like CLAUDE.md, GEMINI.md, etc.).
|
|
62
|
-
await
|
|
56
|
+
await deliverFile(ctx, path.join(target, '_opencrew', 'core', 'system.md'), await tpl('AGENTS.md'), { overwrite: true });
|
|
63
57
|
ok('_opencrew/core/system.md (full system definition)');
|
|
64
58
|
|
|
65
59
|
const agentsResult = await writeBridgeFile(path.join(target, 'AGENTS.md'), AGENTS_BRIDGE);
|
|
@@ -78,14 +72,16 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
78
72
|
|
|
79
73
|
// 3. IDE bridge files.
|
|
80
74
|
step('Configuring AI IDEs');
|
|
81
|
-
await writeBridges(target, ids, { overwrite: false });
|
|
75
|
+
await writeBridges(target, ids, { overwrite: false, ctx });
|
|
82
76
|
|
|
83
77
|
if (ids.includes('claude-code')) {
|
|
84
78
|
warn(`opencrew ships its own Playwright MCP server (.mcp.json) — disable Claude Code's`);
|
|
85
79
|
warn(`native Playwright plugin/extension to avoid the two conflicting.`);
|
|
86
80
|
}
|
|
87
81
|
|
|
88
|
-
// 4.
|
|
82
|
+
// 4. Manifest (what OpenCrew delivered — lets `update` spot the user's edits), then the
|
|
83
|
+
// version stamp LAST: it is what marks the install as complete.
|
|
84
|
+
await writeManifest(target, version, ctx.files);
|
|
89
85
|
await fs.writeFile(path.join(target, STAMP), version + '\n');
|
|
90
86
|
|
|
91
87
|
// 5. Done.
|
|
@@ -96,26 +92,42 @@ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
|
|
|
96
92
|
log(` No API keys needed up front — opencrew asks for them in chat only if a skill you use requires one.\n`);
|
|
97
93
|
}
|
|
98
94
|
|
|
95
|
+
/**
|
|
96
|
+
* --repair-bridges: rewrite IDE bridge files in an existing workspace. --ide wins (even next
|
|
97
|
+
* to --all); --all alone means every IDE; otherwise only the IDEs `update` would detect.
|
|
98
|
+
*/
|
|
99
|
+
async function repairBridges(target, version, opts) {
|
|
100
|
+
const ids = await resolveIdes({ ide: opts.ide }, () => repairIdeIds(target, opts));
|
|
101
|
+
if (!ids.length) throw new UsageError(NO_BRIDGES_FOUND); // before the first write
|
|
102
|
+
log(`\n${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — repairing IDE bridges`);
|
|
103
|
+
log(c.dim(`Target: ${target}\n`));
|
|
104
|
+
const ctx = newDelivery(target, await readManifest(target));
|
|
105
|
+
await writeBridges(target, ids, { overwrite: true, ctx });
|
|
106
|
+
await recordRepair(ctx, version); // only where a manifest already exists
|
|
107
|
+
const [copied, ...copies] = backupSummary(ctx);
|
|
108
|
+
if (copied) warn(copied);
|
|
109
|
+
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
|
+
}
|
|
113
|
+
|
|
99
114
|
/**
|
|
100
115
|
* Copy the framework payload into `target` without overwriting anything.
|
|
101
116
|
* Never copies the version stamp: only a finished init writes it.
|
|
102
117
|
* @returns {Promise<number>} files written
|
|
103
118
|
*/
|
|
104
|
-
export async function installPayload(target) {
|
|
105
|
-
|
|
106
|
-
const onCopy = () => (count += 1);
|
|
107
|
-
await copyDir(path.join(templatesDir, '_opencrew'), path.join(target, '_opencrew'), {
|
|
119
|
+
export async function installPayload(target, ctx = newDelivery(target, null)) {
|
|
120
|
+
await deliverTree(ctx, path.join(templatesDir, '_opencrew'), path.join(target, '_opencrew'), {
|
|
108
121
|
overwrite: false,
|
|
109
122
|
// Never ship stray logs, browser sessions or the template's own stamp.
|
|
110
123
|
skip: (rel) =>
|
|
111
124
|
(rel.startsWith('logs/') && rel !== 'logs/.gitkeep') ||
|
|
112
125
|
rel.startsWith('_browser_profile/') ||
|
|
113
126
|
rel === '.opencrew-version',
|
|
114
|
-
onCopy,
|
|
115
127
|
});
|
|
116
|
-
await
|
|
117
|
-
await
|
|
118
|
-
return
|
|
128
|
+
await deliverTree(ctx, path.join(templatesDir, 'skills'), path.join(target, 'skills'), { overwrite: false });
|
|
129
|
+
await deliverTree(ctx, path.join(templatesDir, 'crews'), path.join(target, 'crews'), { overwrite: false });
|
|
130
|
+
return ctx.written;
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
/** 'none' (no core) · 'partial' (core without stamp: interrupted install) · 'complete'. */
|
|
@@ -126,8 +138,8 @@ async function workspaceState(target) {
|
|
|
126
138
|
|
|
127
139
|
/**
|
|
128
140
|
* Decide which IDEs to configure. --all / --yes → every IDE; --ide → validated list;
|
|
129
|
-
* nothing → `fallback()` (the interactive prompt
|
|
130
|
-
* valid IDE.
|
|
141
|
+
* nothing → `fallback()` (the interactive prompt; in repair mode, the detection). Throws
|
|
142
|
+
* UsageError if --ide names no valid IDE.
|
|
131
143
|
*/
|
|
132
144
|
async function resolveIdes(opts, fallback) {
|
|
133
145
|
if (opts.all || opts.yes) return allIdeIds();
|
|
@@ -148,7 +160,7 @@ async function resolveIdes(opts, fallback) {
|
|
|
148
160
|
* @param {string[]} ids — validated IDE ids to configure
|
|
149
161
|
* @param {{ overwrite: boolean }} opts
|
|
150
162
|
*/
|
|
151
|
-
async function writeBridges(target, ids, { overwrite }) {
|
|
163
|
+
async function writeBridges(target, ids, { overwrite, ctx }) {
|
|
152
164
|
const writtenPaths = new Set();
|
|
153
165
|
|
|
154
166
|
for (const id of ids) {
|
|
@@ -162,7 +174,7 @@ async function writeBridges(target, ids, { overwrite }) {
|
|
|
162
174
|
const fp = path.join(target, f.path);
|
|
163
175
|
const hasFrontmatter = f.content.startsWith('---');
|
|
164
176
|
if (hasFrontmatter) {
|
|
165
|
-
await
|
|
177
|
+
await deliverFile(ctx, fp, f.content, { overwrite });
|
|
166
178
|
} else {
|
|
167
179
|
const result = await writeBridgeFile(fp, f.content);
|
|
168
180
|
if (result.merged) info(`${f.path} (merged — existing content preserved)`);
|
package/src/commands/update.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 {
|
|
4
|
+
import { exists, writeFileSafe, readJson, writeBridgeFile, readFile } from '../lib/fsx.js';
|
|
5
5
|
import { AGENTS_BRIDGE, LEAKED_STATUS_SECTION, ideById } from '../lib/ides.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// (
|
|
6
|
+
import { readManifest, writeManifest, newDelivery, deliverTree, deliverFile } from '../lib/manifest.js';
|
|
7
|
+
import { compareVersions, detectInstalledIdes, refreshBridges, mergeMcp, findLegacyBridges } from '../lib/migrations.js';
|
|
8
|
+
import { c, log, info, ok, warn, err, step } from '../lib/ui.js';
|
|
9
|
+
|
|
10
|
+
// `update` brings EVERY improvement to people who already use OpenCrew (AGENTS.md rule 14),
|
|
11
|
+
// without losing what they made:
|
|
12
|
+
// - _opencrew/core and catalog skills are replaced — a file the user edited is copied to
|
|
13
|
+
// .opencrew-backup/<date>/ first (manifest of hashes; none = copy whatever differs);
|
|
14
|
+
// - new framework folders (agents, config, crew templates) arrive without overwriting;
|
|
15
|
+
// - bridges of the IDEs already installed are refreshed (never new IDEs);
|
|
16
|
+
// - crews/, _opencrew/_memory/, _opencrew/best-practices.local/ and .env are never touched.
|
|
15
17
|
export async function update(opts = {}) {
|
|
16
18
|
const target = process.cwd();
|
|
17
19
|
const pkg = await readJson(packageJsonPath);
|
|
@@ -27,14 +29,15 @@ export async function update(opts = {}) {
|
|
|
27
29
|
const current = (await exists(versionFile))
|
|
28
30
|
? (await fs.readFile(versionFile, 'utf8')).trim()
|
|
29
31
|
: 'unknown';
|
|
32
|
+
const newer = current !== 'unknown' && compareVersions(current, version) > 0;
|
|
30
33
|
|
|
31
34
|
log(`\n${c.bold(c.cyan('opencrew update'))}`);
|
|
32
35
|
log(c.dim(`Installed: ${current} → Package: ${version}\n`));
|
|
33
36
|
|
|
34
37
|
if (opts.check) {
|
|
35
|
-
if (current === version) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
if (current === version) ok(`Up to date (v${version}).`);
|
|
39
|
+
else if (newer) info(`A versão instalada (v${current}) é mais nova que este pacote (v${version}).`);
|
|
40
|
+
else {
|
|
38
41
|
info(`Update available: v${current} → v${version}.`);
|
|
39
42
|
info(`Run ${c.cyan('npx @aksp/opencrew update')} to apply.`);
|
|
40
43
|
process.exitCode = 1;
|
|
@@ -42,45 +45,58 @@ export async function update(opts = {}) {
|
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
if (
|
|
46
|
-
|
|
48
|
+
if (newer) {
|
|
49
|
+
err(`Você tem a v${current} instalada e este pacote é a v${version} (mais antigo). Nada foi alterado.`);
|
|
50
|
+
info(`Use ${c.cyan('npx @aksp/opencrew@latest update')}.`);
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
return;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
const manifest = await readManifest(target);
|
|
56
|
+
const ctx = newDelivery(target, manifest);
|
|
57
|
+
const tpl = (...p) => path.join(templatesDir, ...p);
|
|
58
|
+
const dest = (...p) => path.join(target, ...p);
|
|
59
|
+
|
|
50
60
|
step('Refreshing framework');
|
|
51
|
-
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Skill directories that only exist in the user's project — i.e. not part of
|
|
61
|
-
// the catalog — are never touched, since copyDir only visits paths that exist
|
|
62
|
-
// in the source (templates/skills/).
|
|
63
|
-
step('Refreshing catalog skills');
|
|
64
|
-
warn('Catalog skills are fully overwritten — your edits to any built-in skill files will be lost.');
|
|
65
|
-
let s = 0;
|
|
66
|
-
await copyDir(path.join(templatesDir, 'skills'), path.join(target, 'skills'), {
|
|
67
|
-
overwrite: true,
|
|
68
|
-
onCopy: () => (s += 1),
|
|
69
|
-
});
|
|
70
|
-
ok(`Catalog skills refreshed (${s} files)`);
|
|
71
|
-
|
|
72
|
-
// System doc — full definition in _opencrew/core/, thin bridge at root.
|
|
73
|
-
const systemContent = await fs.readFile(path.join(templatesDir, 'AGENTS.md'), 'utf8');
|
|
74
|
-
await writeFileSafe(path.join(target, '_opencrew', 'core', 'system.md'), systemContent);
|
|
75
|
-
ok('_opencrew/core/system.md refreshed');
|
|
61
|
+
await deliverTree(ctx, tpl('_opencrew', 'core'), dest('_opencrew', 'core'), { overwrite: true });
|
|
62
|
+
await deliverFile(ctx, dest('_opencrew', 'core', 'system.md'), await fs.readFile(tpl('AGENTS.md')), { overwrite: true });
|
|
63
|
+
await deliverTree(ctx, tpl('skills'), dest('skills'), { overwrite: true });
|
|
64
|
+
// New framework folders (e.g. base agents since 1.3.2): only what is missing.
|
|
65
|
+
for (const dir of ['agents', 'config', '_investigations']) {
|
|
66
|
+
await deliverTree(ctx, tpl('_opencrew', dir), dest('_opencrew', dir), { overwrite: false });
|
|
67
|
+
}
|
|
68
|
+
await deliverTree(ctx, tpl('crews'), dest('crews'), { overwrite: false });
|
|
69
|
+
ok(`Framework and catalog skills refreshed (${ctx.written} files written)`);
|
|
76
70
|
|
|
71
|
+
step('Refreshing IDE bridges');
|
|
77
72
|
await refreshAgentsBridge(target);
|
|
73
|
+
const ides = await detectInstalledIdes(target);
|
|
74
|
+
await refreshBridges(ctx, ides);
|
|
75
|
+
ok(ides.length ? `Bridges refreshed: ${ides.map((i) => i.label).join(', ')}` : 'No IDE bridges found to refresh');
|
|
78
76
|
await removeLeakedStatusSection(target);
|
|
79
77
|
|
|
78
|
+
const mcp = await mergeMcp(target, tpl('.mcp.json'));
|
|
79
|
+
if (mcp === 'updated' || mcp === 'created') ok(`.mcp.json (Playwright: ${mcp === 'created' ? 'created' : 'saída em _opencrew/logs/playwright/'})`);
|
|
80
|
+
if (mcp === 'invalid') warn('.mcp.json não é um JSON válido — não alterado. Confira o arquivo.');
|
|
81
|
+
|
|
82
|
+
for (const legacy of await findLegacyBridges(target)) {
|
|
83
|
+
warn(`Ponte antiga encontrada: ${legacy} (aponta para _opensquad/, que não existe neste projeto). Pode apagar com segurança.`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (ctx.copied.length) {
|
|
87
|
+
const rel = path.relative(target, ctx.backupDir).split(path.sep).join('/');
|
|
88
|
+
const what = manifest ? 'que você tinha editado' : 'diferentes do pacote novo';
|
|
89
|
+
warn(`${ctx.copied.length} arquivo(s) ${what} foram copiados para ${rel}/ antes de serem substituídos:`);
|
|
90
|
+
for (const f of ctx.copied.slice(0, 15)) log(` ${f}`);
|
|
91
|
+
if (ctx.copied.length > 15) log(` … e mais ${ctx.copied.length - 15}`);
|
|
92
|
+
if (!manifest) info('Primeira atualização com proteção: sem registro anterior, guardamos tudo o que diferia. Daqui em diante, só o que você editar.');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await writeManifest(target, version, ctx.files);
|
|
80
96
|
// Stamp last: a crash above leaves the old version, so the next update retries.
|
|
81
97
|
await fs.writeFile(versionFile, version + '\n');
|
|
82
98
|
log(`\n${c.green(c.bold('Updated to v' + version))}.`);
|
|
83
|
-
log(c.dim('Your crews, memory,
|
|
99
|
+
log(c.dim('Your crews, memory, local best-practices and .env were left untouched.\n'));
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
// Root AGENTS.md: create it if missing; a legacy full-system doc (pre-v1.3) is backed up
|
package/src/lib/ides.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
// Single source of truth =
|
|
2
|
-
// Every IDE gets only a THIN bridge file that points at
|
|
1
|
+
// Single source of truth = _opencrew/core/system.md (from templates/AGENTS.md).
|
|
2
|
+
// Every IDE gets only a THIN bridge file that points at it.
|
|
3
3
|
// Adding support for a new IDE = one more entry in this list.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
// Coexistence (U6): opencrew only takes over when called — other agent systems in the same
|
|
6
|
+
// project keep priority for everything else.
|
|
7
|
+
const ACTIVATION = `Use opencrew ONLY when the user types \`/opencrew\` or asks to create, run or manage
|
|
8
|
+
AI agent crews. In that case, read \`_opencrew/core/system.md\` and follow its initialization,
|
|
9
|
+
command routing and workflow instructions. For anything else, the other instructions of this
|
|
10
|
+
project take precedence.`;
|
|
11
|
+
|
|
12
|
+
const BRIDGE = `${ACTIVATION}
|
|
7
13
|
|
|
8
14
|
If invoked with arguments (e.g. \`/opencrew create ...\`, \`/opencrew run ...\`),
|
|
9
|
-
route to the matching action from the Command Routing table in
|
|
15
|
+
route to the matching action from the Command Routing table in \`_opencrew/core/system.md\`.
|
|
10
16
|
If invoked without arguments, show the Main Menu.`;
|
|
11
17
|
|
|
12
18
|
// Claude Code needs one extra rule (checkpoints must use AskUserQuestion) and a
|
|
@@ -20,7 +26,7 @@ description: "opencrew — multi-agent orchestration. Use when the user types /o
|
|
|
20
26
|
|
|
21
27
|
${BRIDGE}
|
|
22
28
|
|
|
23
|
-
## Claude Code specifics (override
|
|
29
|
+
## Claude Code specifics (override system.md where they conflict)
|
|
24
30
|
|
|
25
31
|
- **Checkpoints MUST use \`AskUserQuestion\`** — never output a checkpoint question as plain text.
|
|
26
32
|
Combine multiple questions into a single call (max 4 slots, each with 2–4 options).
|
|
@@ -32,7 +38,8 @@ ${BRIDGE}
|
|
|
32
38
|
const CLAUDE_MD = `# opencrew — Project Instructions
|
|
33
39
|
|
|
34
40
|
This project uses **opencrew**, a multi-agent orchestration framework.
|
|
35
|
-
|
|
41
|
+
|
|
42
|
+
${ACTIVATION}
|
|
36
43
|
|
|
37
44
|
Type \`/opencrew\` to open the main menu.
|
|
38
45
|
|
|
@@ -44,11 +51,7 @@ Type \`/opencrew\` to open the main menu.
|
|
|
44
51
|
`;
|
|
45
52
|
|
|
46
53
|
// Root AGENTS.md: thin bridge to the full system definition (written by init and update).
|
|
47
|
-
export const AGENTS_BRIDGE =
|
|
48
|
-
+ 'The opencrew system definition lives at `_opencrew/core/system.md`.\n'
|
|
49
|
-
+ 'Read that file and adopt the opencrew system role — follow all initialization,\n'
|
|
50
|
-
+ 'command routing, and workflow instructions defined there.\n\n'
|
|
51
|
-
+ 'Type `/opencrew` to open the main menu.\n';
|
|
54
|
+
export const AGENTS_BRIDGE = `# opencrew\n\n${ACTIVATION}\n\nType \`/opencrew\` to open the main menu.\n`;
|
|
52
55
|
|
|
53
56
|
// Marker that identifies the maintainer STATUS.md section leaked into CLAUDE.md by 1.4.0/1.4.1.
|
|
54
57
|
export const LEAKED_STATUS_SECTION = '## STATUS.md (gestão de sessão)';
|