@aksp/opencrew 1.8.0 → 1.9.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +45 -12
  3. package/package.json +1 -1
  4. package/src/commands/update.js +9 -3
  5. package/src/lib/blocos.js +7 -3
  6. package/src/lib/resumo.js +3 -0
  7. package/templates/AGENTS.md +1 -0
  8. package/templates/_opencrew/.opencrew-version +1 -1
  9. package/templates/_opencrew/core/prompts/discovery.prompt.md +1 -1
  10. package/templates/_opencrew/core/prompts/entrega.prompt.md +90 -16
  11. package/templates/_opencrew/core/prompts/export.prompt.md +5 -81
  12. package/templates/_opencrew/core/runner.pipeline.md +20 -20
  13. package/templates/_opencrew/core/scripts/entrega/argumentos.mjs +13 -9
  14. package/templates/_opencrew/core/scripts/entrega/canais.mjs +5 -3
  15. package/templates/_opencrew/core/scripts/entrega/comparar.mjs +104 -0
  16. package/templates/_opencrew/core/scripts/entrega/copia.mjs +138 -0
  17. package/templates/_opencrew/core/scripts/entrega/destino.mjs +98 -0
  18. package/templates/_opencrew/core/scripts/entrega/fora.mjs +4 -3
  19. package/templates/_opencrew/core/scripts/entrega/guardar.mjs +60 -0
  20. package/templates/_opencrew/core/scripts/entrega/leiame.mjs +52 -16
  21. package/templates/_opencrew/core/scripts/entrega/leitor.mjs +16 -3
  22. package/templates/_opencrew/core/scripts/entrega/lembrar.mjs +65 -0
  23. package/templates/_opencrew/core/scripts/entrega/passos.mjs +10 -5
  24. package/templates/_opencrew/core/scripts/entrega/pendencias.mjs +16 -6
  25. package/templates/_opencrew/core/scripts/entrega/ressalvas.mjs +78 -0
  26. package/templates/_opencrew/core/scripts/entrega/resumo.mjs +29 -0
  27. package/templates/_opencrew/core/scripts/entrega/retrato.mjs +42 -0
  28. package/templates/_opencrew/core/scripts/entrega/separar.mjs +6 -3
  29. package/templates/_opencrew/core/scripts/entregar.mjs +64 -39
  30. package/templates/_opencrew/core/scripts/verificar/argumentos.mjs +4 -4
  31. package/templates/_opencrew/core/scripts/verificar/entradas.mjs +26 -0
  32. package/templates/_opencrew/core/scripts/verificar/gravacao.mjs +41 -0
  33. package/templates/_opencrew/core/scripts/verificar/relatorio.mjs +10 -3
  34. package/templates/_opencrew/core/scripts/verificar.mjs +17 -27
  35. package/templates/gitignore +1 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,75 @@
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.9.0] — 2026-10-07
7
+
8
+ Fase U3a, fatia 2 "Entrega no projeto" (`specs/fase-u3a2-entrega-no-projeto.md`). Chega a quem já
9
+ usa com um `npx @aksp/opencrew@latest update`, e funciona nas crews que já existem.
10
+
11
+ Ainda não nesta versão: a crew que publica sozinha continua publicando como na 1.8.0 (o publicador
12
+ ainda não lê a pasta da entrega); legenda, post e tweet continuam medidos sem as hashtags no fim
13
+ (só o alerta); documento Word fica para a 1.10.0.
14
+
15
+ ### Added
16
+ - **A entrega vai para uma pasta do seu projeto.** Na primeira entrega de cada crew, a IA pergunta
17
+ "Quer que eu copie o resultado para uma pasta do projeto?". Com a pasta escolhida (por exemplo,
18
+ `Conteudo/Prontos`), cada execução ganha a sua subpasta, `<pasta>/<execução>/`, com o LEIA-ME e
19
+ as pastas dos canais prontos. Antes, a entrega só existia em `crews/<crew>/output/…/entrega/`,
20
+ fora do git, e quem queria guardar copiava à mão.
21
+ - **A pergunta é feita uma vez.** A resposta — também o "não" — fica na linha `entrega.destino` do
22
+ `crew.yaml` da crew; o arquivo anterior é guardado em `crew.yaml.bak`.
23
+ - **Nada do que foi copiado é sobrescrito.** Entregar de novo sem mudança não cria nada; canal que
24
+ ficou pronto depois entra na mesma pasta; se algo já copiado mudou, a entrega nova vai para
25
+ `<execução>-reentrega-2`, ao lado, e o LEIA-ME da anterior avisa. Arquivo seu dentro da cópia
26
+ nunca é tocado: você pode editar a cópia à vontade, porque a entrega seguinte é comparada com o
27
+ que foi copiado, e não com o que você mudou depois.
28
+ - **"Entregar assim mesmo".** Quando um canal não está pronto, a crew oferece três saídas: corrigir
29
+ agora, entregar assim mesmo ou deixar para depois. Em "entregar assim mesmo", o que falta fica
30
+ escrito como ressalva no começo do LEIA-ME, o canal aparece como "Pronto, com ressalva" e é
31
+ copiado com os outros. Pendência nova depois do aceite pede novo aceite — também a que foi
32
+ resolvida e voltou.
33
+ - **Legenda de Instagram sem marcador.** Arquivo de legenda que é só o texto, pronto para colar,
34
+ sai como `instagram/legenda.txt`, com um aviso para conferir e com o alerta de tamanho. Antes, o
35
+ arquivo ia inteiro, sem ser medido.
36
+ - **Mudar a pasta da cópia depois.** Peça à IA ("muda a pasta de entrega", "não quero mais cópia",
37
+ "volta a copiar"): ela refaz a entrega da última execução e grava a resposta nova.
38
+ - **"Não tenho esse dado".** Na aprovação final, se você não tem a informação de um `[PREENCHER]`,
39
+ a crew não insiste e não inventa: deixa o `[PREENCHER]` no texto e você decide na entrega.
40
+
41
+ ### Changed
42
+ - **PDF e "posts formatados" deixaram de ser gerados.** O PDF era prometido e o método não
43
+ funcionava; o "post formatado" foi substituído pela entrega por canal. Para ter um PDF, abra o
44
+ arquivo e use Imprimir → Salvar como PDF (o LEIA-ME ensina). Crew antiga com um passo de
45
+ `format: pdf` ou `format: formatted-post`: a execução avisa que o formato não é mais gerado e o
46
+ passo grava o texto em markdown (`.md`); nenhum `.pdf` é criado. O CSV continua.
47
+ - **Canal que não está pronto não é copiado para o projeto.** Ele continua em `entrega/`, marcado
48
+ como "Não está pronto", e entra na cópia quando ficar pronto ou quando você aceitar a ressalva.
49
+ - **"Corrigir agora" corrige no arquivo de origem**, verifica o texto de novo e só então monta a
50
+ entrega. Antes, a correção podia ser feita sem nova verificação.
51
+ - **`[PREENCHER]` aparece no relatório como "✏️ A preencher"**, e não mais como "❌ Bloqueio"; o
52
+ resumo conta à parte ("1 a preencher"). O que impede a entrega não mudou: texto com `[PREENCHER]`
53
+ continua "Não está pronto" até você preencher ou aceitar.
54
+ - No LEIA-ME, o aviso de trecho que ficou fora do texto para colar diz em qual arquivo de origem
55
+ ele está.
56
+ - No laço de revisão, "Aceitar assim mesmo" diz que a escolha fica registrada na entrega.
57
+ - **`update` em instalação que não terminou** (`_opencrew/core` sem o registro de versão): não
58
+ altera nada e pede `npx @aksp/opencrew init`, que conclui. Antes, atualizava mostrando a versão
59
+ "unknown".
60
+
61
+ ### Fixed
62
+ - **O relatório de cada ciclo de revisão é gravado pelo verificador**
63
+ (`verificacao-ciclo-N.md`). Antes, a IA copiava a saída à mão e podia truncar.
64
+ - **`.gitignore` com um marcador do bloco sem o par** (só `# opencrew:start` ou só
65
+ `# opencrew:end`): o `update` guarda o arquivo como estava em `.opencrew-backup/<data>/`, põe um
66
+ bloco completo no fim e lista a cópia. Nenhuma linha sua é apagada.
67
+
68
+ ### Internal
69
+ - O bloco do `.gitignore` começa por `# gerenciado pelo OpenCrew: suas linhas ficam fora deste
70
+ bloco` (o primeiro `update` regrava só o bloco).
71
+ - `entregar.mjs` ganha `--destino`, `--lembrar-destino` e `--aceitar-pendencias`; `verificar.mjs`
72
+ ganha `--relatorio`; módulos novos em `scripts/entrega/` e `scripts/verificar/` (sem dependência,
73
+ até 200 linhas cada). `export.prompt.md` fica só com o CSV. O runner não cresceu (874 linhas).
74
+
6
75
  ## [1.8.0] — 2026-10-07
7
76
 
8
77
  Fase U3a, fatia 1 "Pasta de entrega" (`specs/fase-u3a1-pasta-de-entrega.md`). Chega a quem já usa
package/README.md CHANGED
@@ -30,11 +30,12 @@ dentro da sua IDE.**
30
30
  rejeita o mesmo erro 3 vezes, vira Regra de Ouro automática.
31
31
  - 📦 **Templates prontos** — blog semanal, Instagram carrossel, newsletter
32
32
  mensal, lançamento de produto. Comece em 2 minutos.
33
- - 📤 **Exportação multi-formato** — PDF, CSV e posts formatados por plataforma,
34
- sem abrir editor nenhum.
33
+ - 📤 **Tabelas em CSV** — as tabelas de um resultado saem em CSV, prontas para a planilha.
35
34
  - 📬 **Entrega por canal** — depois de aprovar, você encontra a pasta `entrega/`: uma pasta por
36
35
  canal (Instagram, LinkedIn, blog, e-mail, WhatsApp, X/Twitter, YouTube), o texto pronto para
37
36
  colar, as imagens e um `LEIA-ME.md` com o passo a passo. O que não está pronto fica marcado.
37
+ - 📁 **Entrega no seu projeto** — a crew pergunta uma vez onde guardar e copia a entrega para uma
38
+ pasta do seu projeto, uma subpasta por execução, sem sobrescrever nada.
38
39
  - 🎛️ **Seleção inteligente de agentes** — o sistema analisa seu pedido e
39
40
  sugere quais agentes são necessários para aquela tarefa. Você confirma ou
40
41
  ajusta com um clique. Agentes pulados não gastam tokens naquele run.
@@ -199,7 +200,7 @@ meu-projeto/
199
200
  │ ├── blog-semanal/ ← template: blog semanal
200
201
  │ │ └── output/<execução>/ ← criada a cada execução
201
202
  │ │ ├── v1/ v2/ … ← o que cada passo gravou
202
- │ │ └── entrega/ ← o que você usa: LEIA-ME.md + uma pasta por canal
203
+ │ │ └── entrega/ ← o que você usa: LEIA-ME.md + uma pasta por canal (copiada para a pasta do projeto que você escolher)
203
204
  │ ├── instagram-carrossel/ ← template: Instagram carrossel
204
205
  │ ├── newsletter-mensal/ ← template: newsletter
205
206
  │ └── lancamento-produto/ ← template: lançamento
@@ -239,20 +240,46 @@ Só aparecem as pastas que a execução tem.
239
240
  - **Texto pronto para colar.** Os `.txt` saem sem `#`, `**`, rótulos nem recados internos, com as
240
241
  hashtags no fim. Com mais de uma peça do mesmo tipo, os arquivos são numerados (`post-1.txt`,
241
242
  `post-2.txt`).
242
- - **O `LEIA-ME.md` diz o que fazer.** Cada canal tem a situação ("Pronto" ou "Não está pronto"),
243
- os arquivos, de onde cada um veio e os passos, numerados. "Antes de usar" junta o que falta;
244
- "O que não foi conferido" lembra o que ninguém mediu (links e fatos, texto dentro das imagens,
245
- aparência final em cada rede).
246
- - **O que não está pronto fica marcado.** Sobrou um `[PREENCHER]` ou um texto acima do limite? O
247
- canal aparece como "Não está pronto" e a crew pergunta se você quer corrigir agora ou seguir
248
- assim.
243
+ - **O `LEIA-ME.md` diz o que fazer.** Cada canal tem a situação ("Pronto", "Pronto, com ressalva"
244
+ ou "Não está pronto"), os arquivos, de onde cada um veio e os passos, numerados. "Antes de usar"
245
+ junta o que falta e o que foi entregue com ressalva; "O que não foi conferido" lembra o que
246
+ ninguém mediu (links e fatos, texto dentro das imagens, aparência final em cada rede).
247
+ - **O que não está pronto fica marcado, e você escolhe.** Sobrou um `[PREENCHER]` ou um texto
248
+ acima do limite? O canal aparece como "Não está pronto" e a crew oferece três saídas:
249
+ 1. **Corrigir agora** — ela ajusta no arquivo de origem, verifica e monta a entrega de novo.
250
+ 2. **Entregar assim mesmo** — o que falta fica escrito como ressalva no começo do LEIA-ME, e o
251
+ canal passa a "Pronto, com ressalva".
252
+ 3. **Deixar para depois** — o canal fica como "Não está pronto" e não é copiado para o projeto.
253
+ Quando o dado existir, peça a entrega dessa execução de novo.
254
+ - **Não tem o dado na hora?** Na aprovação final, diga "não tenho esse dado": a crew não insiste e
255
+ não inventa — o `[PREENCHER]` fica no texto e você decide na entrega.
249
256
  - **Arquivo sem canal vai para `outros/`**, inteiro e com o nome original: nada some.
250
- - **A pasta é refeita a cada entrega e fica fora do git.** O que você editar ali se perde; para
251
- guardar, copie a pasta para outro lugar do projeto.
257
+ - **A pasta `entrega/` é refeita a cada entrega e fica fora do git.** O que você editar ali se
258
+ perde: o que é para guardar está na cópia do seu projeto (abaixo).
252
259
  - **Execução antiga?** Peça à IA: "monte a entrega da execução X da crew Y". Ela lista os
253
260
  arquivos, pede o seu "sim" e monta a pasta. Funciona em crews criadas antes da 1.8.0, depois
254
261
  do `update`.
255
262
 
263
+ ### A cópia no seu projeto
264
+
265
+ Na primeira entrega de cada crew, a IA pergunta: "Quer que eu copie o resultado para uma pasta do
266
+ projeto?". Se você disser uma pasta (por exemplo, `Conteudo/Prontos`), a crew copia a entrega para
267
+ uma pasta do seu projeto, uma subpasta por execução: `<pasta>/<execução>/`, com o `LEIA-ME.md` e
268
+ as pastas dos canais prontos.
269
+
270
+ - **A pergunta é feita uma vez por crew.** A resposta — também o "não" — fica na linha
271
+ `entrega.destino` do `crew.yaml` da crew (o arquivo anterior fica em `crew.yaml.bak`). Para
272
+ mudar depois, peça à IA ou edite essa linha.
273
+ - **Nada é sobrescrito.** Entregar de novo sem mudança não cria nada. Canal que ficou pronto
274
+ depois entra na mesma pasta. Se algo que já foi copiado mudou, a entrega nova vai para
275
+ `<execução>-reentrega-2` (depois `-3`…), ao lado, e a anterior fica como estava, com um aviso no
276
+ LEIA-ME dela. O que você editar ou guardar na cópia continua lá, e editar a cópia não faz a
277
+ entrega seguinte virar reentrega.
278
+ - **Só vai o que está pronto.** Canal "Não está pronto" fica fora da cópia, a menos que você
279
+ escolha "Entregar assim mesmo".
280
+ - **A pasta fica dentro do projeto**, fora de `_opencrew/`, `crews/`, `skills/`, `.git/` e
281
+ `node_modules/`; se não existe, é criada. Se ela entra no git, a escolha é sua.
282
+
256
283
  A entrega não gera PDF nem imagem: `artigo.md`, `corpo.md` e os roteiros saem em markdown, e o
257
284
  LEIA-ME ensina a salvar como PDF pelo "Imprimir" do seu editor. O LEIA-ME e os nomes dos arquivos
258
285
  são sempre em português.
@@ -323,6 +350,12 @@ o que você fez:
323
350
  copiou. Essa pasta fica fora do git (entra no bloco do `.gitignore`). No primeiro `update` para
324
351
  a 1.6.3 há cópia do `.gitignore` mesmo sem edição sua: as versões anteriores não registravam o
325
352
  bloco.
353
+ - **O `.gitignore` tem um marcador do bloco sem o par** (só `# opencrew:start` ou só
354
+ `# opencrew:end`)? Nenhuma linha sua é apagada: o `update` guarda o arquivo como estava e põe
355
+ um bloco completo no fim. O bloco começa por um comentário que diz que ele é do OpenCrew: as
356
+ suas linhas ficam fora dele.
357
+ - **A instalação anterior parou no meio?** O `update` não altera nada e pede para você rodar
358
+ `npx @aksp/opencrew init`, que conclui a instalação sem apagar o que já existe.
326
359
  - **Apagou um modelo de crew ou um skill do catálogo?** Ele volta no `update`, e a saída diz o
327
360
  que foi entregue de novo.
328
361
  - **Removeu o servidor Playwright do `.mcp.json`?** O `update` o entrega uma única vez; se você
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aksp/opencrew",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -9,7 +9,7 @@ import { detectInstalledIdes } from '../lib/deteccao.js';
9
9
  import { withoutLegacy } from '../lib/legado.js';
10
10
  import { updateMcp } from '../lib/mcp.js';
11
11
  import { compareVersions, installedVersion, findLeftovers } from '../lib/migrations.js';
12
- import { say, recreatedLines, updateSummary, UNTOUCHED } from '../lib/resumo.js';
12
+ import { say, recreatedLines, updateSummary, UNTOUCHED, INTERRUPTED } from '../lib/resumo.js';
13
13
  import { c, log, info, ok, warn, err, step } from '../lib/ui.js';
14
14
 
15
15
  const tpl = (...p) => path.join(templatesDir, ...p);
@@ -39,7 +39,13 @@ export async function update(opts = {}) {
39
39
  return;
40
40
  }
41
41
 
42
- const current = (await installedVersion(target)) ?? 'unknown';
42
+ // No stamp = an install that did not finish: nothing is written or stamped (rule 31).
43
+ const current = await installedVersion(target);
44
+ if (!current) {
45
+ err(INTERRUPTED);
46
+ process.exitCode = 1;
47
+ return;
48
+ }
43
49
  log(`\n${c.bold(c.cyan('opencrew update'))}`);
44
50
  log(c.dim(`Installed: ${current} → Package: ${version}\n`));
45
51
  if (canApply(opts, current, version)) await apply(target, version);
@@ -47,7 +53,7 @@ export async function update(opts = {}) {
47
53
 
48
54
  /** `--check` only reports, and a package older than the workspace stops: false = write nothing. */
49
55
  function canApply(opts, current, version) {
50
- const newer = current !== 'unknown' && compareVersions(current, version) > 0;
56
+ const newer = compareVersions(current, version) > 0;
51
57
  if (opts.check) {
52
58
  if (current === version) ok(`Up to date (v${version}).`);
53
59
  else if (newer) info(`A versão instalada (v${current}) é mais nova que este pacote (v${version}).`);
package/src/lib/blocos.js CHANGED
@@ -70,11 +70,14 @@ function planBlock(file, text, marked) {
70
70
  const block = bytesOf(marked).replace(/\n/g, eol);
71
71
  const ranges = blockRanges(text, file);
72
72
  if (!ranges.length) {
73
+ // A marker left alone (its pair deleted by hand): the block still goes in whole, and the
74
+ // file as it was is copied first (spec U3a-2, rule 30).
75
+ const orphan = markersOf(file).some((marker) => text.includes(marker));
73
76
  const bom = text.startsWith(BOM) ? BOM : '';
74
77
  const added = AT_END.has(file)
75
78
  ? `${text.replace(/[ \t\r\n]+$/, '')}${eol}${eol}${block}${eol}`
76
79
  : `${bom}${block}${eol}${eol}${text.slice(bom.length).replace(/^[ \t\r\n]+/, '')}`;
77
- return { action: 'added', text: added, hashes: [] };
80
+ return { action: 'added', text: added, hashes: [], orphan };
78
81
  }
79
82
  const hashes = ranges.map(([from, to]) => hashOf(Buffer.from(text.slice(from, to), 'latin1').toString('utf8')));
80
83
  if (hashes.every((h) => h === hashOf(marked))) return { action: 'kept', hashes };
@@ -85,7 +88,8 @@ function planBlock(file, text, marked) {
85
88
  * Put `content` between the opencrew markers of `file` (path relative to the project root,
86
89
  * with `/`) and record the block in `ctx.files`.
87
90
  * - no file → `created`; file with no block → `added` at the top (at the end in .gitignore
88
- * and .env.example), the user's content kept, no copy;
91
+ * and .env.example), the user's content kept, no copy — unless a marker was left alone
92
+ * in it: then the whole file is copied first;
89
93
  * - block equal to the new one → `kept`, nothing written;
90
94
  * - otherwise → `updated`: only the block is rewritten, in the line ending of the file. The
91
95
  * whole file is copied first unless the block is exactly what the manifest recorded.
@@ -102,7 +106,7 @@ export async function deliverBlock(ctx, file, content) {
102
106
  const edited = plan.action === 'updated' && !plan.hashes.every((h) => h === record);
103
107
  // A UTF-16 file cannot take a UTF-8 block without damage: the whole file is copied first.
104
108
  const risky = raw && plan.text !== undefined && isUtf16(raw);
105
- const copied = (edited || risky) && (await backupFile(ctx, file));
109
+ const copied = (edited || risky || plan.orphan) && (await backupFile(ctx, file));
106
110
  if (plan.text !== undefined) {
107
111
  await fs.mkdir(path.dirname(dest), { recursive: true });
108
112
  await fs.writeFile(dest, Buffer.from(plan.text, 'latin1'));
package/src/lib/resumo.js CHANGED
@@ -15,6 +15,9 @@ export const UNTOUCHED = 'Não foram alterados: as crews que você criou, `_open
15
15
  /** `init` in a workspace that is already installed (rule 15). */
16
16
  export const ALREADY_INSTALLED = 'Para atualizar, rode `npx @aksp/opencrew@latest update`. Não apague `_opencrew/` para reinstalar: a pasta guarda a sua memória (`_opencrew/_memory/`) e as suas best-practices (`_opencrew/best-practices.local/`).';
17
17
 
18
+ /** `update` over `_opencrew/core` with no version stamp (spec U3a-2, rule 31). */
19
+ export const INTERRUPTED = 'A instalação anterior não terminou. Rode `npx @aksp/opencrew init` para concluir.';
20
+
18
21
  const NO_BRIDGES = 'Nenhuma ponte de IDE encontrada: nada a atualizar. Para criar a ponte de uma IDE: `npx @aksp/opencrew@latest init --repair-bridges --ide=<id>`.';
19
22
  const LEAK_REMOVED = 'CLAUDE.md: removi a seção de STATUS.md que as versões 1.4.0 e 1.4.1 gravaram por engano.';
20
23
  const FIRST_PROTECTED = 'Primeira atualização com proteção: sem registro anterior, guardamos tudo o que diferia. Daqui em diante, só o que você editar.';
@@ -62,6 +62,7 @@ Route input to the matching action:
62
62
  | `/opencrew dashboard off` | Turn the Escritório off — see "Dashboard (Optional)" |
63
63
  | `/opencrew reset` | Confirm and reset all configuration |
64
64
  | Request to deliver a run that already ended ("monte a entrega da execução …") | Load `_opencrew/core/prompts/entrega.prompt.md` → build the `entrega/` folder of that run |
65
+ | Request to change where the delivery is copied ("muda a pasta de entrega", "não quero mais cópia", "volta a copiar") | Load `_opencrew/core/prompts/entrega.prompt.md` → "Changing the folder later" |
65
66
  | Natural language about crews | Infer intent and route accordingly |
66
67
 
67
68
  ## Loading Agents
@@ -1 +1 @@
1
- 1.8.0
1
+ 1.9.0
@@ -112,7 +112,7 @@ Based on the detected domain, ask the most relevant contextual question first. W
112
112
  **If domain = `analysis`:**
113
113
  1. Where does the data come from? (open-ended — let the user describe their data sources)
114
114
  2. What decisions should this analysis help you make? (open-ended)
115
- 3. What format should the output take? (multiple choice: dashboard / PDF report / spreadsheet / automated alert / other)
115
+ 3. What format should the output take? (multiple choice: dashboard / written report / spreadsheet / automated alert / other)
116
116
 
117
117
  **If domain = `mixed`:**
118
118
  Ask the most pressing question from each relevant domain, starting with the primary one. Cap at 3 questions total in this step.
@@ -2,7 +2,9 @@
2
2
 
3
3
  One script turns the approved files of a run into `crews/{name}/output/{run_id}/entrega/`: one
4
4
  folder per channel, text ready to paste and a `LEIA-ME.md` that tells the user what to do with each
5
- file. Your part is to build the list of files, run the script and act on its last line.
5
+ file. The same script copies what is ready to a folder of the project the user chose, one folder per
6
+ run. Your part is to build the list of files, run the script, act on its last line and ask, once per
7
+ crew, where the copy goes.
6
8
 
7
9
  You do NOT copy, split, rename or rewrite a file yourself, and you never write inside `entrega/`:
8
10
  the script rebuilds that folder from scratch on every call. **When** the delivery runs is in the
@@ -59,31 +61,97 @@ With channels from Step 2, the same command ends with one option per channel:
59
61
 
60
62
  ## Step 4: Show the result and read the last line
61
63
 
62
- The output of the script is the final summary of the run (the folder, each channel as "Pronto" or
63
- "Não está pronto", what is missing, the path of the `LEIA-ME.md`): show it to the user as it came,
64
+ The output of the script is the final summary of the run (the folder, each channel as "Pronto",
65
+ "Pronto, com ressalva" or "Não está pronto", what is missing, the line of the copy, the path of
66
+ the `LEIA-ME.md`): show it to the user as it came,
64
67
  without the `ENTREGA:` line. Do not rewrite it and do not add files it does not list. Besides the
65
68
  `entrega/` folder, the script also writes `crews/{name}/output/{run_id}/verificacao-entrega.md`,
66
69
  the report of the check made at delivery time (it is not part of the delivery).
67
70
 
68
- - `ENTREGA:OK` → go on with the run.
69
- - `ENTREGA:INCOMPLETA` → a channel is not ready, or a file could not be written. The files were
70
- generated anyway and the `LEIA-ME.md` marks the channel. Show what is missing and ask:
71
+ - `ENTREGA:OK` → go on with the run (Step 5 first, when it applies).
72
+ - `ENTREGA:COM_RESSALVA` → everything that was missing is a ressalva the user accepted: the
73
+ `LEIA-ME.md` opens with it and the channel is "Pronto, com ressalva"; go on as with `ENTREGA:OK`.
74
+ - `ENTREGA:INCOMPLETA` → a channel is not ready, the destination was refused or a file could not be
75
+ written. `entrega/` was generated anyway and the `LEIA-ME.md` marks the channel; a channel that
76
+ is not ready is not copied to the project. The channels that are ready were already copied by
77
+ this same call, before the user answers: nothing waits for the answer. Show what is missing and
78
+ ask:
71
79
  ```
72
80
  ⚠️ A entrega ficou incompleta: {o que falta}
73
- 1. Corrigir agora (eu ajusto e monto a entrega de novo)
74
- 2. Seguir assim (no LEIA-ME, o canal fica marcado como "Não está pronto")
81
+ 1. Corrigir agora (eu ajusto no arquivo de origem, verifico e monto a entrega de novo)
82
+ 2. Entregar assim mesmo (fica registrado como ressalva no LEIA-ME)
83
+ 3. Deixar para depois (o canal fica como "Não está pronto" e não é copiado)
75
84
  ```
76
85
  Wait for the answer.
77
- - **1** — for each item that is missing, fix it in the source file the summary names: ask the
78
- user for the real information of every `[PREENCHER: …]`, shorten what is over a limit, write
79
- again a file that is not there (when the summary says a file could not be written, there is
80
- nothing to fix in the text). Then run the delivery again, with the same list.
81
- - **2** — go on, with the delivery as it is. Every irreversible step still asks for its own
82
- confirmation, as it does today.
86
+ - **1** — for each item that is missing, fix it in the source file the pending item names, never
87
+ inside `entrega/`, and in place — the same file at the same path, no new `vN` folder and no
88
+ copy — so the list does not change: ask the user for the real information of every
89
+ `[PREENCHER: …]`, shorten what is over a limit, write again a file that is not there. Then run
90
+ the checker on that file
91
+ (`node _opencrew/core/scripts/verificar.mjs --crew "crews/{name}" --arquivo "{caminho}={formato}"`)
92
+ and only then run the delivery again, with the same list.
93
+ - **2** — run the same command again, ending with `--aceitar-pendencias`: the script records
94
+ each pending item as a ressalva (in `ressalvas.json`, in the run folder, and at the top of the
95
+ `LEIA-ME.md`), the channel becomes "Pronto, com ressalva" and is copied like the ready ones.
96
+ It does **not** solve a file that does not exist, a refused destination or a file that could
97
+ not be written: for those, see below.
98
+ - **3** — go on: the channel stays "Não está pronto" and is not copied. Every irreversible step
99
+ still asks for its own confirmation, as it does today. Tell the user what is missing and that
100
+ it is enough to ask for the delivery of this run when the data exists.
101
+ - **Destination refused, or a file that could not be written** (the lines "Não copiei: …" and
102
+ "Não consegui gravar …"): show the message as it came and ask for another folder (Step 5, with
103
+ the new answer) or for a new attempt, which is the same command again. A file of the list that
104
+ does not exist: ask for it, or take it out of the list.
105
+
106
+ The first call never has `--aceitar-pendencias`. Outside option 2 it goes only when the user
107
+ already chose "Aceitar assim mesmo" in the review loop of this run and what is missing is only
108
+ what was accepted there: then run the command again with it, without asking.
83
109
 
84
110
  After "Edit this content" (the final menu of the runner) changes an approved file, or any step
85
111
  runs again after the delivery, run the delivery again with the new paths: the folder is rebuilt
86
- from scratch, so whatever was edited inside `entrega/` is lost — the `LEIA-ME.md` says so.
112
+ from scratch, so whatever was edited inside `entrega/` is lost — the `LEIA-ME.md` says so. The copy
113
+ in the project is never overwritten: when something already copied changed, the script puts the
114
+ new delivery in a folder beside it (`{run_id}-reentrega-2`) and the summary says so.
115
+
116
+ ## Step 5: The folder of the project that keeps the copy
117
+
118
+ The line `Cópia:` of the summary says what was copied and where (or "Criei a pasta …" before it):
119
+ show it as it came. A crew whose answer was "não" has no such line, and nothing is asked.
120
+
121
+ After **any** delivery whose summary has the line "Cópia: nenhuma pasta escolhida para esta crew."
122
+ — `ENTREGA:OK` and `ENTREGA:COM_RESSALVA` included — ask, once. When the last line was
123
+ `ENTREGA:INCOMPLETA`, ask only after it was resolved (Step 4):
124
+
125
+ ```
126
+ Quer que eu copie o resultado para uma pasta do projeto? Se sim, diga qual (por exemplo, `Conteudo/Prontos`). Se não, não pergunto de novo.
127
+ ```
128
+
129
+ - A folder → run the same command again (same list, same options), ending with
130
+ `--lembrar-destino "{pasta}"`.
131
+ - "Não" → the same command again, ending with `--lembrar-destino nao`.
132
+
133
+ The script writes the answer in the `crew.yaml` of the crew (`entrega.destino`, with a `.bak` copy
134
+ of the file) and makes the copy in the same call: never edit the `crew.yaml` yourself for this. The
135
+ next deliveries of the crew do not ask again.
136
+
137
+ - **`{pasta}` was typed by the user and goes into a command** — the safe-name rule (nome seguro)
138
+ of the runner applies: between double quotes and only if it is made of letters (accents
139
+ included), digits, space and `. _ - / \ : ( )`. With any other character do NOT run the command;
140
+ say `⚠️ O nome `{pasta}` tem um caractere que não posso usar em comandos ({caractere}). Use só letras, números, espaço, ponto, hífen, sublinhado e parênteses.`
141
+ and ask for the folder again.
142
+ - The folder is a path inside the project, written from its root (`Conteudo/Prontos`). When the
143
+ script refuses it ("Não copiei: …"), nothing was recorded — also when it is the only line of the
144
+ output, with no `ENTREGA:` line: show it as it came and ask for another folder (or "não").
145
+ - To copy one delivery somewhere else without changing the answer of the crew, the same command
146
+ ends with `--destino "{pasta}"` (same rule for `{pasta}`); only when the user asks for it.
147
+
148
+ ## Changing the folder later
149
+
150
+ When the user asks to change where the copy goes, to stop copying or to copy again ("muda a pasta
151
+ de entrega", "não quero mais cópia", "volta a copiar"): run the delivery of the **last run** of the
152
+ crew ("A run that already ended", below), with the command ending with
153
+ `--lembrar-destino "{pasta}"` (same rule for `{pasta}`), or with `--lembrar-destino nao` to stop.
154
+ With no folder in the request, ask which. Never edit the `crew.yaml` by hand.
87
155
 
88
156
  ## When the script does not run
89
157
 
@@ -101,6 +169,11 @@ Os arquivos aprovados estão em:
101
169
  `{motivo}` is the line the script printed, or what kept it from running. The completion summary
102
170
  of the runner then shows this list in place of the `entrega/` folder.
103
171
 
172
+ **A refused destination is not that.** When the only line is "Não copiei: … Recebi: {valor}." (the
173
+ folder given to `--lembrar-destino` was refused; nothing was written), the script did run: show
174
+ that line to the user as it came and ask for another folder (or "não"), as in Step 5 — never answer
175
+ it with "A entrega automática não rodou".
176
+
104
177
  ## A run that already ended
105
178
 
106
179
  When the user asks to deliver a run that is over (it was run before this folder existed, or the
@@ -119,7 +192,7 @@ delivery did not run):
119
192
  - {caminho} ({formato})
120
193
  Posso montar a entrega com esta lista? (sim / não)
121
194
  ```
122
- 4. On "sim", follow Steps 2 to 4. No step of the pipeline runs again, and nothing is published.
195
+ 4. On "sim", follow Steps 2 to 5. No step of the pipeline runs again, and nothing is published.
123
196
 
124
197
  ## Rules
125
198
 
@@ -127,5 +200,6 @@ delivery did not run):
127
200
  are fixed PT-BR, whatever the user's language.
128
201
  - **DO** run the delivery again whenever an approved file changes.
129
202
  - **DO NOT** create, edit or delete anything inside `entrega/` yourself.
203
+ - **DO NOT** write the destination in the `crew.yaml` yourself, nor copy the delivery by hand.
130
204
  - **DO NOT** put in the list a file the user did not approve, nor a path you guessed.
131
205
  - **DO NOT** treat `ENTREGA:INCOMPLETA` as an error of the script: it is its answer.
@@ -1,59 +1,16 @@
1
- # Export — Multi-Format Output
1
+ # Export — Tables to CSV
2
2
 
3
- You are the opencrew Export agent. Your role is to transform pipeline outputs from markdown into the requested delivery format. You do NOT create content or make editorial decisions — you transform existing, approved content.
3
+ You are the opencrew Export agent. Your role is to transform the tables of a pipeline output from markdown into CSV. You do NOT create content or make editorial decisions — you transform existing, approved content.
4
4
 
5
5
  ## Context Loading
6
6
 
7
7
  Before starting, read:
8
8
  - The input file specified by the step's `inputFile` field — this is the source content to export
9
- - The step's `format:` field — this determines the target output format
9
+ - The step's `format:` field — `csv` is the only export format
10
10
 
11
11
  ---
12
12
 
13
- ## Supported Formats
14
-
15
- ### PDF (`format: pdf`)
16
-
17
- Transform markdown content into a PDF file using Playwright (already available in the project).
18
-
19
- **Process:**
20
- 1. Read the full input markdown file
21
- 2. Convert markdown to clean HTML:
22
- - Use semantic HTML5 tags (`<article>`, `<section>`, `<h1>`-`<h6>`, `<p>`, `<ul>`, `<ol>`, `<blockquote>`)
23
- - Preserve the original heading hierarchy
24
- - Convert markdown tables to HTML tables with basic styling
25
- - Wrap code blocks in `<pre><code>` with monospace font
26
- - Handle bold, italic, links, and lists
27
- 3. Wrap in a minimal HTML document with print-friendly CSS:
28
- ```html
29
- <!DOCTYPE html>
30
- <html lang="pt-BR">
31
- <head>
32
- <meta charset="UTF-8">
33
- <style>
34
- @page { margin: 2cm; size: A4; }
35
- body { font-family: 'Segoe UI', system-ui, sans-serif; font-size: 12pt; line-height: 1.6; color: #1a1a1a; }
36
- h1 { font-size: 22pt; margin-top: 0; }
37
- h2 { font-size: 16pt; border-bottom: 1px solid #ddd; padding-bottom: 4pt; }
38
- h3 { font-size: 13pt; }
39
- table { border-collapse: collapse; width: 100%; margin: 12pt 0; }
40
- th, td { border: 1px solid #ddd; padding: 6pt 8pt; text-align: left; }
41
- th { background: #f5f5f5; }
42
- code { font-family: 'Cascadia Code', 'Fira Code', monospace; font-size: 10pt; background: #f0f0f0; padding: 1pt 4pt; border-radius: 3pt; }
43
- pre code { display: block; padding: 8pt 12pt; overflow-x: auto; }
44
- blockquote { border-left: 3pt solid #ccc; margin-left: 0; padding-left: 12pt; color: #555; }
45
- </style>
46
- </head>
47
- <body>{content}</body>
48
- </html>
49
- ```
50
- 4. Write the HTML to a temporary file: `crews/{crew-name}/output/{run_id}/export/temp.html`
51
- 5. Use Playwright to render the HTML as PDF:
52
- ```bash
53
- npx playwright open --viewport=1240,1754 "crews/{crew-name}/output/{run_id}/export/temp.html"
54
- ```
55
- Then use the print-to-PDF functionality.
56
- 6. Save the PDF to the step's `outputFile` path
13
+ ## Supported Format
57
14
 
58
15
  ### CSV / Excel (`format: csv`)
59
16
 
@@ -84,50 +41,17 @@ Keyword,Intent,Volume,Competition
84
41
  "product adoption",Informational,Low,Low
85
42
  ```
86
43
 
87
- ### Formatted Social Post (`format: formatted-post`)
88
-
89
- Transform markdown content into a platform-ready post with proper formatting.
90
-
91
- **Process:**
92
- 1. Read the full input markdown file
93
- 2. Extract the post content: caption/hook, body, CTA, hashtags
94
- 3. Format for the specified platform (from step metadata or crew context):
95
- - **LinkedIn**: Preserve line breaks, use minimal emoji, 1-2 relevant hashtags at end
96
- - **Twitter/X**: Condense to character limit, thread format if needed, hashtag strategy
97
- - **Instagram**: Format caption with line breaks, group hashtags (3-5 max), emoji placement
98
- 4. Output as clean text with platform-specific formatting notes:
99
- ```markdown
100
- # Formatted Post — {platform}
101
-
102
- **Caption:**
103
- {formatted caption text}
104
-
105
- **Hashtags:**
106
- {hashtag list}
107
-
108
- **Formatting notes:**
109
- - Line breaks: {count} intentional breaks
110
- - Character count: {N}
111
- - Best posting time: {recommendation based on crew context}
112
- ```
113
-
114
- ---
115
-
116
44
  ## Smart Recommendations
117
45
 
118
- - **Multiple outputs from same content**: If the crew produces one piece of content that needs to go to multiple platforms, batch the exports. Export the same source to all required formats in sequence.
119
- - **PDF quality**: The print CSS is minimal but functional. For brand-specific PDFs (logos, custom fonts, color schemes), the user should use a design template (via `template-designer` skill).
46
+ - **Text for each channel**: the delivery folder of the run already has the text of each channel ready to paste, and its `LEIA-ME.md` — this prompt does not format posts.
120
47
  - **CSV structure**: The CSV export extracts ALL tables from the source. If the source has one main data table, it produces one clean CSV. If it has many, they're separated by `# Table:` headers.
121
48
 
122
49
  ## Limitations
123
50
 
124
- - PDF export uses Playwright's built-in print-to-PDF. Complex layouts (multi-column, absolute positioning) may not render correctly.
125
51
  - CSV export is from markdown tables only — it does not parse JSON, YAML, or unstructured data.
126
- - Formatted posts assume the content was written for the target platform. Cross-platform adaptation (e.g., blog → Twitter thread) should be done by a content agent before export.
127
52
 
128
53
  ## Error Handling
129
54
 
130
55
  - If the input file is missing → **ERROR**: stop, inform the user
131
56
  - If the input file has no extractable content for the target format (e.g., CSV requested but no tables found) → warn the user, save a note in the output file
132
- - If Playwright is unavailable for PDF export → fall back to saving the HTML file as the output, inform the user
133
57
  - **Never fabricate content — only transform what exists in the input file**