@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +35 -14
  3. package/package.json +1 -1
  4. package/src/cli.js +2 -1
  5. package/src/commands/init.js +42 -30
  6. package/src/commands/update.js +58 -42
  7. package/src/lib/ides.js +15 -12
  8. package/src/lib/manifest.js +89 -0
  9. package/src/lib/migrations.js +147 -0
  10. package/templates/.mcp.json +1 -1
  11. package/templates/AGENTS.md +2 -1
  12. package/templates/_opencrew/.opencrew-version +1 -1
  13. package/templates/_opencrew/core/prompts/build.prompt.md +14 -0
  14. package/templates/_opencrew/core/prompts/discovery.prompt.md +15 -2
  15. package/templates/_opencrew/core/runner.pipeline.md +86 -20
  16. package/templates/_opencrew/core/scripts/comum.mjs +48 -0
  17. package/templates/_opencrew/core/scripts/conferir-fontes/busca.mjs +82 -0
  18. package/templates/_opencrew/core/scripts/conferir-fontes/coleta.mjs +104 -0
  19. package/templates/_opencrew/core/scripts/conferir-fontes/relatorio.mjs +52 -0
  20. package/templates/_opencrew/core/scripts/conferir-fontes.mjs +135 -0
  21. package/templates/_opencrew/core/scripts/verificar/argumentos.mjs +33 -0
  22. package/templates/_opencrew/core/scripts/verificar/arquivos.mjs +50 -0
  23. package/templates/_opencrew/core/scripts/verificar/html.mjs +52 -0
  24. package/templates/_opencrew/core/scripts/verificar/leitura.mjs +138 -66
  25. package/templates/_opencrew/core/scripts/verificar/medicao.mjs +142 -0
  26. package/templates/_opencrew/core/scripts/verificar/pecas.mjs +190 -0
  27. package/templates/_opencrew/core/scripts/verificar/proibicoes.mjs +100 -0
  28. package/templates/_opencrew/core/scripts/verificar/regras.mjs +110 -91
  29. package/templates/_opencrew/core/scripts/verificar/relatorio.mjs +43 -0
  30. package/templates/_opencrew/core/scripts/verificar/secoes.mjs +145 -0
  31. package/templates/_opencrew/core/scripts/verificar.mjs +162 -88
  32. 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
- > Fase 4 da auditoria em `docs/auditoria/`).
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
- O `update` não toca nas suas crews nem na sua memória. Ele atualiza apenas:
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 — sobrescrito por inteiro) | `crews/` (suas crews) |
212
- | Skills do catálogo (sobrescritas — edições locais se perdem) | `_opencrew/_memory/` (perfil, preferências) |
213
- | `_opencrew/core/system.md` | `.env` (suas chaves) |
214
- | Bloco `<!-- opencrew -->` no `AGENTS.md` | Pontes das IDEs (`CLAUDE.md`, `.cursor/`, `.agents/`…) |
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
- As **pontes das IDEs não são atualizadas** pelo `update`. Para regravá-las com a versão
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 --ide=claude-code
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
- (troque `claude-code` pelas IDEs que você usa, separadas por vírgula; sem `--ide`, o
224
- comando grava as pontes de **todas** as IDEs suportadas).
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 de IDE num workspace existente |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aksp/opencrew",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
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 Regenerate IDE bridge files in an existing workspace
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
@@ -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 { copyDir, exists, writeFileSafe, readJson, writeBridgeFile } from '../lib/fsx.js';
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
- // --repair-bridges mode: regenerate IDE bridge files in an existing workspace.
25
- if (opts['repair-bridges'] && state !== 'none') {
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 copied = await installPayload(target);
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 writeFileSafe(path.join(target, '_opencrew', 'core', 'system.md'), await tpl('AGENTS.md'));
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. Version stamp — written LAST: it is what marks the install as complete.
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
- let count = 0;
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 copyDir(path.join(templatesDir, 'skills'), path.join(target, 'skills'), { overwrite: false, onCopy });
117
- await copyDir(path.join(templatesDir, 'crews'), path.join(target, 'crews'), { overwrite: false });
118
- return count;
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). Throws UsageError if --ide names no
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 writeFileSafe(fp, f.content, { overwrite });
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)`);
@@ -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 { copyDir, exists, writeFileSafe, readJson, writeBridgeFile, readFile } from '../lib/fsx.js';
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 { c, log, info, ok, warn, step } from '../lib/ui.js';
7
-
8
- // Update refreshes ONLY the framework. It never touches:
9
- // crews/, _opencrew/_memory/, _opencrew/_browser_profile/, .env, IDE bridges
10
- // (one exception: the CLAUDE.md block leaked by 1.4.0/1.4.1 — see removeLeakedStatusSection).
11
- // Note: catalog skills (skills/<name>/ that ship with the package) ARE fully
12
- // overwritten below — user edits to a catalog skill's own files are not preserved.
13
- // Only skill directories that don't exist in the package's templates/skills/ at all
14
- // (i.e. custom/user-authored skills) are left untouched.
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
- ok(`Up to date (v${version}).`);
37
- } else {
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 (current === version) {
46
- ok('Already up to date. Refreshing framework files anyway.');
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
- // Refresh core framework: _opencrew/core is fully overwritten (it is not user data).
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
- let n = 0;
52
- await copyDir(path.join(templatesDir, '_opencrew', 'core'), path.join(target, '_opencrew', 'core'), {
53
- overwrite: true,
54
- onCopy: () => (n += 1),
55
- });
56
- ok(`_opencrew/core refreshed (${n} files)`);
57
-
58
- // Refresh catalog skills: every skill shipped in templates/skills/ is fully
59
- // overwritten (edits to a catalog skill's files do not survive an update).
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, IDE bridges and .env were left untouched.\n'));
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 = AGENTS.md (shipped at project root).
2
- // Every IDE gets only a THIN bridge file that points at AGENTS.md.
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
- const BRIDGE = `Read \`AGENTS.md\` at the project root and adopt the opencrew system role.
6
- Follow all initialization, command routing, and workflow instructions defined there.
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 AGENTS.md.
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 AGENTS.md where they conflict)
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
- The full system definition lives in \`AGENTS.md\` — read it and adopt that role.
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 = '# opencrew\n\n'
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)';