@aksp/opencrew 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,44 @@
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.1.0] — 2026-08-01
7
+
8
+ ### Fixed
9
+ - **Skill catalog URLs**: `/opencrew install` now fetches skills from the correct fork
10
+ (`alberthpalhares/opencrew/templates/skills/`) instead of the upstream OpenSquad repo.
11
+ - **Publish workflow**: restored `push: tags` as the sole trigger — the actual release
12
+ flow is `npm version` + `git push --tags`, not GitHub Releases. Documented in
13
+ `CONTRIBUTING.md`.
14
+ - **Cross-platform test script**: replaced shell glob (`tests/*.test.js`) with an
15
+ explicit file list so `npm test` works on Windows PowerShell + Node 20.
16
+ - **CI matrix**: test suite now runs on Ubuntu and Windows on every push/PR.
17
+
18
+ ### Added
19
+ - **Test suite**: 30 tests (`node:test`) covering `fsx.js`, init, update, IDE bridge
20
+ validation, and documentation contracts.
21
+ - **CI version-sync check**: `scripts/check-version-sync.js` fails the build if
22
+ `.opencrew-version` drifts from `package.json`.
23
+ - **Playwright plugin warning**: `init` now warns Claude Code users to disable the
24
+ native Playwright extension (opencrew ships its own via `.mcp.json`).
25
+
26
+ ### Changed
27
+ - **Dashboard opt-in**: Pipeline Runner `state.json` writes are now gated on
28
+ `Dashboard: enabled` in `preferences.md` (default: disabled). Removed the
29
+ unconditional 10-second sleep at the end of every pipeline run.
30
+ - **Smaller fixes**: removed `AskUserQuestion` references from IDE-neutral files,
31
+ corrected `update.js` comment about overwrite behavior, pinned `@playwright/mcp`
32
+ version, removed stale root `skills/` directory (drifted duplicate of
33
+ `templates/skills/`).
34
+
35
+ ## [1.0.1] — 2026-08-01
36
+
37
+ ### Changed
38
+ - API keys for optional skills are now requested conversationally in chat (during crew
39
+ creation or skill install) instead of requiring the user to manually copy/edit `.env`
40
+ beforehand. Values are collected and written to `.env` automatically.
41
+ - `init` no longer tells users to configure `.env` as a next step — no setup is required
42
+ to start using opencrew.
43
+
6
44
  ## [1.0.0] — 2026-08-01
7
45
 
8
46
  ### Added
package/README.md CHANGED
@@ -1,61 +1,79 @@
1
1
  # opencrew
2
2
 
3
- **Create AI agent crews that work together — right from your IDE.**
3
+ [![CI](https://github.com/alberthpalhares/opencrew/actions/workflows/ci.yml/badge.svg)](https://github.com/alberthpalhares/opencrew/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/%40aksp%2Fopencrew)](https://www.npmjs.com/package/@aksp/opencrew)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
6
 
5
- opencrew is a multi-agent orchestration framework. Describe what you need in plain
6
- language and it assembles a team of specialized agents that run as an automated pipeline
7
- with human approval checkpoints. It works across Claude Code, Cursor, Codex, Gemini CLI,
8
- OpenCode, Antigravity and more.
7
+ **Crie equipes de agentes de IA que trabalham juntos — direto na sua IDE.**
9
8
 
10
- > **This is not an original framework — it's my personal build of OpenSquad.**
11
- > [OpenSquad](https://github.com/renatoasse/opensquad) was created by
9
+ > 🇧🇷 This README is in Brazilian Portuguese (PT-BR), the project's primary audience. If
10
+ > you don't read Portuguese, use your browser's translator (e.g. Google Translate) or ask
11
+ > an AI assistant to translate this page.
12
+
13
+ opencrew é um framework de orquestração multi-agente. Descreva o que você precisa em
14
+ linguagem natural e ele monta um time de agentes especializados que rodam como um pipeline
15
+ automatizado, com pontos de aprovação humana. Funciona no Claude Code, Cursor, Codex,
16
+ Gemini CLI, OpenCode, Antigravity e mais.
17
+
18
+ > **Este não é um framework original — é a minha versão pessoal do OpenSquad.**
19
+ > O [OpenSquad](https://github.com/renatoasse/opensquad) foi criado por
12
20
  > [Renato Asse](https://github.com/renatoasse) ([Comunidade Sem Codar](https://semcodar.com.br)).
13
- > I ([aksp](https://www.npmjs.com/~aksp)) use OpenSquad daily and wanted to improve a few
14
- > things for my own workflow — so `opencrew` is that reworked version, shared in case it
15
- > helps others too. All credit for the original idea and framework goes to Renato Asse.
16
- > See [Origin & credits](#origin--credits) below. MIT licensed, same as the original.
21
+ > Eu ([aksp](https://www.npmjs.com/~aksp)) uso o OpenSquad no dia a dia e quis melhorar
22
+ > algumas coisas para o meu próprio fluxo de trabalho — o `opencrew` é essa versão
23
+ > reformulada, compartilhada caso ajude outras pessoas também. Todo o crédito pela ideia e
24
+ > pelo framework original é do Renato Asse. Veja [Origem e créditos](#origem-e-créditos)
25
+ > abaixo. Licenciado sob MIT, assim como o original.
17
26
 
18
27
  ---
19
28
 
20
- ## Installation
29
+ ## Instalação
21
30
 
22
- **Prerequisite:** Node.js 20+
31
+ **Pré-requisito:** Node.js 20+
23
32
 
24
33
  ```bash
25
34
  npx @aksp/opencrew init
26
35
  ```
27
36
 
28
- `init` scaffolds the workspace in the current folder and asks which AI IDEs you use, then
29
- generates the right integration files for each. Then:
37
+ O `init` monta o workspace na pasta atual e pergunta quais IDEs de IA você usa, gerando os
38
+ arquivos de integração certos para cada uma. Depois:
39
+
40
+ 1. Abra a pasta na sua IDE de IA.
41
+ 2. Digite `/opencrew` para começar — a primeira execução configura o perfil da sua empresa.
42
+
43
+ Isso já é suficiente para criar e rodar crews: nenhuma configuração prévia, instalação
44
+ extra ou chave de API é necessária para começar.
30
45
 
31
- 1. Open the folder in your AI IDE.
32
- 2. Copy `.env.example` to `.env` and fill in keys for the skills you plan to use.
33
- 3. Type `/opencrew` to start — the first run sets up your company profile.
46
+ Algumas **skills opcionais** dependem de serviços externos — por exemplo, publicar no
47
+ Instagram, gerar imagens com IA, fazer web scraping (Apify) ou enviar e-mails (Resend). Você
48
+ não precisa se preocupar com isso antes de começar: se, ao montar uma crew, o opencrew
49
+ identificar que ela precisa de uma dessas skills, ele pede a chave direto na conversa
50
+ (explicando o que é e onde consegui-la) e salva tudo por conta própria. Não é preciso abrir
51
+ nem editar nenhum arquivo manualmente.
34
52
 
35
- Preselect IDEs (skip the prompt) or configure all of them:
53
+ Você pode pré-selecionar as IDEs (pula a pergunta) ou configurar todas de uma vez:
36
54
 
37
55
  ```bash
38
56
  npx @aksp/opencrew init --ide=claude-code,codex
39
57
  npx @aksp/opencrew init --all
40
58
  ```
41
59
 
42
- ## Updating
60
+ ## Atualizando
43
61
 
44
- Refresh the framework without losing your work:
62
+ Atualize o framework sem perder o seu trabalho:
45
63
 
46
64
  ```bash
47
65
  npx @aksp/opencrew update
48
66
  ```
49
67
 
50
- `update` refreshes only `_opencrew/core`, the catalog skills and `AGENTS.md`.
51
- Your `crews/`, memory, IDE bridges and `.env` are left untouched.
68
+ O `update` atualiza apenas `_opencrew/core`, as skills do catálogo e o `AGENTS.md`. Suas
69
+ `crews/`, memória, integrações de IDE e `.env` continuam intactos.
52
70
 
53
- ## Supported IDEs
71
+ ## IDEs suportadas
54
72
 
55
- | IDE | Bridge file(s) generated |
73
+ | IDE | Arquivo(s) de integração gerado(s) |
56
74
  |-----|--------------------------|
57
75
  | Claude Code | `.claude/skills/opencrew/SKILL.md`, `CLAUDE.md` |
58
- | Codex (OpenAI) | `AGENTS.md` (native) + `.agents/skills/opencrew/SKILL.md` |
76
+ | Codex (OpenAI) | `AGENTS.md` (nativo) + `.agents/skills/opencrew/SKILL.md` |
59
77
  | Cursor | `.cursor/rules/opencrew.mdc` |
60
78
  | VS Code + Copilot | `.github/copilot-instructions.md` |
61
79
  | OpenCode | `.opencode/commands/opencrew.md` |
@@ -64,55 +82,69 @@ Your `crews/`, memory, IDE bridges and `.env` are left untouched.
64
82
  | Qwen Code | `QWEN.md` |
65
83
  | Trae | `.trae/rules/opencrew.md` |
66
84
 
67
- Every bridge is a thin pointer to the single source of truth, **`AGENTS.md`**.
85
+ Cada integração é só um ponteiro enxuto para a fonte única de verdade, o **`AGENTS.md`**.
86
+
87
+ ## Como funciona
88
+
89
+ - **Architect** projeta uma crew a partir da sua descrição (agentes, pipeline, skills).
90
+ - **Sherlock** (opcional) analisa perfis de referência para extrair padrões reais de conteúdo.
91
+ - **Pipeline Runner** executa a crew, pausando nos checkpoints para sua aprovação.
92
+ - **Skills Engine** carrega integrações (scraping, design, publicação, e-mail…) sob demanda,
93
+ usando um esquema em duas camadas para manter o consumo de tokens baixo.
94
+
95
+ ## Dashboard (opcional)
68
96
 
69
- ## How it works
97
+ O opencrew inclui um dashboard visual auto-contido — `dashboard/index.html`, um arquivo
98
+ HTML único sem dependências que mostra a execução de uma crew como um escritório virtual
99
+ animado (com agentes trabalhando em suas mesas, handoffs entre etapas, e indicador de
100
+ progresso).
70
101
 
71
- - **Architect** designs a crew from your description (agents, pipeline, skills).
72
- - **Sherlock** (optional) analyzes reference profiles to extract real content patterns.
73
- - **Pipeline Runner** executes the crew, pausing at checkpoints for your approval.
74
- - **Skills Engine** loads integrations (scraping, design, publishing, email…) on demand,
75
- using a two-tier scheme to keep token usage low.
102
+ Ele é **desligado por padrão** — o Pipeline Runner não escreve `state.json` a menos que
103
+ você ligue o recurso (`Dashboard: enabled` em `_opencrew/_memory/preferences.md`, via
104
+ `/opencrew settings`). Inclui modo demo embutido para visualização sem precisar rodar uma
105
+ crew real.
76
106
 
77
- ## Commands (inside your IDE)
107
+ ## Comandos (dentro da sua IDE)
78
108
 
79
- | Command | What it does |
109
+ | Comando | O que faz |
80
110
  |---------|--------------|
81
- | `/opencrew` | Open the main menu |
82
- | `/opencrew create <description>` | Create a new crew |
83
- | `/opencrew run <name>` | Run a crew |
84
- | `/opencrew list` | List your crews |
85
- | `/opencrew edit <name>` | Modify a crew |
86
- | `/opencrew skills` | Browse / install / remove skills |
87
-
88
- ## For maintainers
89
-
90
- See [CONTRIBUTING.md](CONTRIBUTING.md). The golden rule: `AGENTS.md` is the only place
91
- system instructions live; per-IDE files are generated from `src/lib/ides.js`.
92
-
93
- ## Origin & credits
94
-
95
- `opencrew` is a **reworked distribution of [OpenSquad](https://github.com/renatoasse/opensquad)**,
96
- the multi-agent orchestration framework created and maintained by
97
- **[Renato Asse](https://github.com/renatoasse)**, founder of
98
- [Comunidade Sem Codar](https://semcodar.com.br). The original project, its concept, agent
99
- model, pipeline design and skill system are his work — please star and follow the
100
- [upstream repository](https://github.com/renatoasse/opensquad) and watch the
101
- [launch video](https://www.youtube.com/watch?v=CL1ppI4qHeU).
102
-
103
- I use OpenSquad in my day-to-day and made a few changes that fit how I work, which I believe
104
- can help other people as well. What this build changes relative to upstream:
105
-
106
- - **npm installer under my scope** — `npx @aksp/opencrew init` / `update`, with a
107
- non-destructive update path that preserves your crews, memory and `.env`.
108
- - **Single source of truth for multi-IDE** — one canonical `AGENTS.md`; every IDE gets a
109
- thin generated bridge file instead of a hand-maintained document per tool.
110
- - **Token-economy and skill-invocation improvements** to the framework core.
111
-
112
- This is an independent, community fork — it is **not** affiliated with or endorsed by
113
- Renato Asse or Comunidade Sem Codar. If you want the official project, use
111
+ | `/opencrew` | Abre o menu principal |
112
+ | `/opencrew create <descrição>` | Cria uma nova crew |
113
+ | `/opencrew run <nome>` | Executa uma crew |
114
+ | `/opencrew list` | Lista suas crews |
115
+ | `/opencrew edit <nome>` | Modifica uma crew |
116
+ | `/opencrew skills` | Navega / instala / remove skills |
117
+
118
+ ## Para mantenedores
119
+
120
+ Veja [CONTRIBUTING.md](CONTRIBUTING.md). Regra de ouro: `AGENTS.md` é o único lugar onde
121
+ vivem as instruções do sistema; os arquivos por IDE são gerados a partir de `src/lib/ides.js`.
122
+
123
+ ## Origem e créditos
124
+
125
+ `opencrew` é uma **distribuição reformulada do [OpenSquad](https://github.com/renatoasse/opensquad)**,
126
+ o framework de orquestração multi-agente criado e mantido por
127
+ **[Renato Asse](https://github.com/renatoasse)**, fundador da
128
+ [Comunidade Sem Codar](https://semcodar.com.br). O projeto original, seu conceito, o modelo
129
+ de agentes, o design do pipeline e o sistema de skills são trabalho dele — dê uma estrela e
130
+ siga o [repositório original](https://github.com/renatoasse/opensquad) e assista ao
131
+ [vídeo de lançamento](https://www.youtube.com/watch?v=CL1ppI4qHeU).
132
+
133
+ Eu uso o OpenSquad no meu dia a dia e fiz algumas mudanças que se encaixam no meu jeito de
134
+ trabalhar, que acredito que também podem ajudar outras pessoas. O que esta versão muda em
135
+ relação ao original:
136
+
137
+ - **Instalador npm no meu escopo** — `npx @aksp/opencrew init` / `update`, com um caminho
138
+ de atualização não destrutivo que preserva suas crews, memória e `.env`.
139
+ - **Fonte única de verdade para multi-IDE** — um `AGENTS.md` canônico; cada IDE recebe um
140
+ arquivo de integração gerado e enxuto, em vez de um documento mantido manualmente para
141
+ cada ferramenta.
142
+ - **Melhorias de economia de tokens e de invocação de skills** no núcleo do framework.
143
+
144
+ Este é um fork independente, feito pela comunidade — **não** é afiliado nem endossado pelo
145
+ Renato Asse ou pela Comunidade Sem Codar. Se você quiser o projeto oficial, use
114
146
  [`npx opensquad init`](https://github.com/renatoasse/opensquad).
115
147
 
116
- ## License
148
+ ## Licença
117
149
 
118
- MIT — see [LICENSE](LICENSE). Original OpenSquad framework © Renato Asse, also MIT.
150
+ MIT — veja [LICENSE](LICENSE). Framework OpenSquad original © Renato Asse, também MIT.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aksp/opencrew",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "scripts": {
21
21
  "start": "node bin/opencrew.js",
22
- "test": "node --test tests/*.test.js",
22
+ "test": "node --test tests/cli.test.js tests/docs.test.js tests/fsx.test.js tests/ides.test.js tests/init.test.js tests/update.test.js",
23
23
  "version": "node -e \"require('fs').writeFileSync('templates/_opencrew/.opencrew-version', require('./package.json').version + '\\n')\""
24
24
  },
25
25
  "keywords": [
package/src/cli.js CHANGED
@@ -3,7 +3,7 @@ import { packageJsonPath } from './lib/paths.js';
3
3
  import { allIdeIds } from './lib/ides.js';
4
4
  import { init } from './commands/init.js';
5
5
  import { update } from './commands/update.js';
6
- import { c, log, err } from './lib/ui.js';
6
+ import { c, log, err, warn, info } from './lib/ui.js';
7
7
 
8
8
  function parseArgs(argv) {
9
9
  const opts = { _: [] };
@@ -36,18 +36,49 @@ ${c.bold('Options for init')}
36
36
  --all Configure every supported IDE
37
37
  --yes Non-interactive; accept defaults
38
38
 
39
+ ${c.bold('Options for update')}
40
+ --check Dry-run: report whether an update is available without making changes
41
+
39
42
  ${c.bold('Examples')}
40
43
  npx @aksp/opencrew init
41
44
  npx @aksp/opencrew init --ide=claude-code,codex
42
45
  npx @aksp/opencrew init --all
43
46
  npx @aksp/opencrew update
47
+ npx @aksp/opencrew update --check
44
48
  `);
45
49
  }
46
50
 
47
51
  export async function run(argv) {
48
52
  const opts = parseArgs(argv);
49
- const pkg = await readJson(packageJsonPath);
50
- const version = pkg.version;
53
+
54
+ let version = 'unknown';
55
+ let engines = {};
56
+ try {
57
+ const pkg = await readJson(packageJsonPath);
58
+ version = pkg.version;
59
+ engines = pkg.engines || {};
60
+ } catch {
61
+ err('Could not read package.json. The installation may be corrupted.');
62
+ info('Try reinstalling: npm install @aksp/opencrew');
63
+ process.exitCode = 1;
64
+ return;
65
+ }
66
+
67
+ // Validate Node version against engines.node requirement.
68
+ if (engines.node) {
69
+ const min = engines.node.replace(/[^0-9.]/g, '');
70
+ if (min) {
71
+ const major = Number(min.split('.')[0]);
72
+ const current = Number(process.versions.node.split('.')[0]);
73
+ if (current < major) {
74
+ warn(`opencrew requires Node.js ${engines.node}. You have v${process.versions.node}.`);
75
+ info(`Upgrade Node or use a compatible version.`);
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+ }
80
+ }
81
+
51
82
  const cmd = opts._[0] || (opts.help ? 'help' : 'init');
52
83
 
53
84
  switch (cmd) {
@@ -75,6 +75,11 @@ export async function init(opts = {}) {
75
75
  ok(`${ide.label} → ${ide.files.map((f) => f.path).join(', ')}`);
76
76
  }
77
77
 
78
+ if (ids.includes('claude-code')) {
79
+ warn(`opencrew ships its own Playwright MCP server (.mcp.json) — disable Claude Code's`);
80
+ warn(`native Playwright plugin/extension to avoid the two conflicting.`);
81
+ }
82
+
78
83
  // 4. Version stamp.
79
84
  await fs.writeFile(path.join(target, '_opencrew', '.opencrew-version'), version + '\n');
80
85
 
@@ -82,8 +87,8 @@ export async function init(opts = {}) {
82
87
  log(`\n${c.green(c.bold('Done!'))} opencrew is installed.\n`);
83
88
  log(`${c.bold('Next steps:')}`);
84
89
  log(` 1. Open this folder in your AI IDE.`);
85
- log(` 2. Copy ${c.cyan('.env.example')} to ${c.cyan('.env')} and fill in keys for the skills you use.`);
86
- log(` 3. Type ${c.cyan('/opencrew')} to start (first run sets up your company profile).\n`);
90
+ log(` 2. Type ${c.cyan('/opencrew')} to start (first run sets up your company profile).`);
91
+ log(` No API keys needed up front — opencrew asks for them in chat only if a skill you use requires one.\n`);
87
92
  }
88
93
 
89
94
  async function tpl(name) {
@@ -6,7 +6,11 @@ import { c, log, info, ok, warn, step } from '../lib/ui.js';
6
6
 
7
7
  // Update refreshes ONLY the framework. It never touches:
8
8
  // crews/, _opencrew/_memory/, _opencrew/_browser_profile/, .env, IDE bridges.
9
- export async function update() {
9
+ // Note: catalog skills (skills/<name>/ that ship with the package) ARE fully
10
+ // overwritten below — user edits to a catalog skill's own files are not preserved.
11
+ // Only skill directories that don't exist in the package's templates/skills/ at all
12
+ // (i.e. custom/user-authored skills) are left untouched.
13
+ export async function update(opts = {}) {
10
14
  const target = process.cwd();
11
15
  const pkg = await readJson(packageJsonPath);
12
16
  const version = pkg.version;
@@ -25,6 +29,17 @@ export async function update() {
25
29
  log(`\n${c.bold(c.cyan('opencrew update'))}`);
26
30
  log(c.dim(`Installed: ${current} → Package: ${version}\n`));
27
31
 
32
+ if (opts.check) {
33
+ if (current === version) {
34
+ ok(`Up to date (v${version}).`);
35
+ } else {
36
+ info(`Update available: v${current} → v${version}.`);
37
+ info(`Run ${c.cyan('npx @aksp/opencrew update')} to apply.`);
38
+ process.exitCode = 1;
39
+ }
40
+ return;
41
+ }
42
+
28
43
  if (current === version) {
29
44
  ok('Already up to date. Refreshing framework files anyway.');
30
45
  }
@@ -38,8 +53,11 @@ export async function update() {
38
53
  });
39
54
  ok(`_opencrew/core refreshed (${n} files)`);
40
55
 
41
- // Refresh catalog skills but preserve any user-modified/added ones:
42
- // only overwrite skills that ship with the package; leave others untouched.
56
+ // Refresh catalog skills: every skill shipped in templates/skills/ is fully
57
+ // overwritten (edits to a catalog skill's files do not survive an update).
58
+ // Skill directories that only exist in the user's project — i.e. not part of
59
+ // the catalog — are never touched, since copyDir only visits paths that exist
60
+ // in the source (templates/skills/).
43
61
  step('Refreshing catalog skills');
44
62
  let s = 0;
45
63
  await copyDir(path.join(templatesDir, 'skills'), path.join(target, 'skills'), {
package/src/lib/fsx.js CHANGED
@@ -50,6 +50,21 @@ export async function writeFileSafe(p, content, { overwrite = true } = {}) {
50
50
  return true;
51
51
  }
52
52
 
53
+ /**
54
+ * Remove a directory and all its contents.
55
+ * Does nothing silently if the path does not exist.
56
+ * @param {string} p Directory path to remove.
57
+ * @returns {Promise<boolean>} true if something was deleted, false if it didn't exist.
58
+ */
59
+ export async function deleteDir(p) {
60
+ try {
61
+ await fs.rm(p, { recursive: true, force: true });
62
+ return true;
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
53
68
  export async function readJson(p) {
54
69
  return JSON.parse(await fs.readFile(p, 'utf8'));
55
70
  }
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "playwright": {
4
4
  "command": "npx",
5
- "args": ["@playwright/mcp@latest", "--config", "_opencrew/config/playwright.config.json"]
5
+ "args": ["@playwright/mcp@0.0.78", "--config", "_opencrew/config/playwright.config.json"]
6
6
  }
7
7
  }
8
8
  }
@@ -31,7 +31,7 @@ If `company.md` is empty or contains `<!-- NOT CONFIGURED -->`:
31
31
 
32
32
  ## Main Menu
33
33
 
34
- When the user types `/opencrew` or asks for the menu, present an interactive selector using AskUserQuestion with these options (max 4 per question):
34
+ When the user types `/opencrew` or asks for the menu, present an interactive selector with these options (max 4 per question). Use your IDE's native interactive-choice mechanism if it has one (e.g. Claude Code's `AskUserQuestion`); otherwise present the options as a numbered list and ask the user to reply with a number:
35
35
 
36
36
  **Primary menu (first question):**
37
37
  - **Create a new crew** — Describe what you need and I'll build a crew for you
@@ -39,7 +39,7 @@ When the user types `/opencrew` or asks for the menu, present an interactive sel
39
39
  - **My crews** — View, edit, or delete your crews
40
40
  - **More options** — Skills, company profile, settings, and help
41
41
 
42
- If the user selects "More options", present a second AskUserQuestion:
42
+ If the user selects "More options", present a second selector the same way:
43
43
  - **Skills** — Browse, install, create, and manage skills for your crews
44
44
  - **Company profile** — View or update your company information
45
45
  - **Settings & Help** — Language, preferences, configuration, and help
@@ -84,8 +84,28 @@ When running a crew:
84
84
  3. For each agent in the party CSV, also read their full `.agent.md` file from agents/ directory
85
85
  4. Load company context from `_opencrew/_memory/company.md`
86
86
  5. Load crew memory from `crews/{name}/_memory/memories.md`
87
- 6. Read the pipeline runner instructions from `_opencrew/core/runner.pipeline.md`
88
- 7. Execute the pipeline step by step following runner instructions
87
+ 6. Load user preferences from `_opencrew/_memory/preferences.md` (used to check the Dashboard toggle — see below)
88
+ 7. Read the pipeline runner instructions from `_opencrew/core/runner.pipeline.md`
89
+ 8. Execute the pipeline step by step following runner instructions
90
+
91
+ ## Dashboard (Optional)
92
+
93
+ opencrew ships an optional visual dashboard — a self-contained HTML file
94
+ (`dashboard/index.html`) that shows a crew run in progress as an animated
95
+ virtual office. It is **disabled by default** and most installs never use it,
96
+ so the Pipeline Runner does not write `state.json` unless the user has turned
97
+ it on.
98
+
99
+ - To use: open `dashboard/index.html` in a browser and point it at the
100
+ `crews/{name}/state.json` written during a run.
101
+ - Toggle: `Dashboard: enabled` (or `disabled`) in `_opencrew/_memory/preferences.md`,
102
+ editable via `/opencrew settings`.
103
+ - When disabled (default): the runner never creates, writes, or deletes `state.json`.
104
+ - When enabled: the runner writes `crews/{name}/state.json` before each step and at
105
+ every handoff, exactly as described in `_opencrew/core/runner.pipeline.md`.
106
+ - The dashboard auto-polls `state.json` every 1.5 seconds when in live mode;
107
+ it also includes a built-in demo mode so you can see what it looks like
108
+ without running a real crew.
89
109
 
90
110
  ## Language Handling
91
111
 
@@ -93,6 +113,13 @@ When running a crew:
93
113
  - All user-facing output should be in the user's preferred language
94
114
  - Internal file names and code remain in English
95
115
  - Agent personas communicate in the user's language
116
+ - **Exception — crew memory scaffolding stays in PT-BR regardless of Output Language.**
117
+ The section headers in `crews/{name}/_memory/memories.md` (e.g. `## Estilo de Escrita`)
118
+ and the table columns in `crews/{name}/_memory/runs.md` (e.g. `Data | Run ID | Tema`) are
119
+ fixed structural labels, not generated prose — see `_opencrew/core/runner.pipeline.md`.
120
+ opencrew's primary supported audience is PT-BR (see README), so these are intentionally
121
+ not localized per-user. Only the *content* written into those sections follows the
122
+ user's Output Language.
96
123
 
97
124
  ## Critical Rules
98
125
 
@@ -1 +1 @@
1
- 0.1.15
1
+ 1.0.1
@@ -7,3 +7,4 @@
7
7
  - **Output Language:**
8
8
  - **IDEs:**
9
9
  - **Date Format:** YYYY-MM-DD
10
+ - **Dashboard:** disabled
@@ -14,6 +14,21 @@ Before starting execution:
14
14
  - The crew's `crew-party.csv` (all agent personas)
15
15
  - Company context from `_opencrew/_memory/company.md`
16
16
  - Crew memory from `crews/{name}/_memory/memories.md`
17
+ - User preferences from `_opencrew/_memory/preferences.md`
18
+
19
+ 1a. **Check the Dashboard toggle** — the visual dashboard (`state.json` writes) is an
20
+ optional, opt-in feature that most installs never use (it requires running the
21
+ separate dashboard app from source — see README). Scan the already-loaded
22
+ `preferences.md` for a `Dashboard:` field:
23
+ - If it reads `Dashboard: enabled` → set `dashboard_enabled = true` for this run.
24
+ - Otherwise (`disabled`, missing, or preferences.md not configured yet) →
25
+ set `dashboard_enabled = false`. This is the default.
26
+ Store `dashboard_enabled` in working memory for the rest of this run. Every
27
+ `state.json` read/write instruction in this document is conditional on it —
28
+ when `false`, skip ALL of them; never create, update, or delete
29
+ `crews/{name}/state.json`.
30
+
31
+ > **Note on language**: the section headers below (`## Estilo de Escrita`, `## Design Visual`, etc.) and the `runs.md` table columns (`Data | Run ID | Tema | ...`) are fixed PT-BR structural labels, not translated per Output Language — opencrew's primary supported audience is PT-BR (see AGENTS.md → Language Handling). Only the *content* written under these headers follows the user's preferred language.
17
32
 
18
33
  1b. **Memory format migration** — After loading `memories.md`, check whether it uses the new format by scanning for the `## Estilo de Escrita` section header:
19
34
  ```bash
@@ -76,8 +91,8 @@ Before starting execution:
76
91
  - If it does (sub-second collision), append `-2`, `-3`, etc. until the folder does not exist
77
92
  - Create the folder using Bash: `mkdir -p crews/{name}/output/{run_id}`
78
93
  - Store `run_id` in working memory for this run — it will be used for ALL output paths
79
- 6. **Initialize state.json**: Create `crews/{name}/state.json` from scratch (see below). State writes are always mandatory.
80
- - **IMPORTANT**: You MUST write to `crews/{name}/state.json` before every step and after every handoff. This is non-negotiable. Never skip these writes.
94
+ 6. **Initialize state.json** (only if `dashboard_enabled` — see step 1a; otherwise skip this entire step, including all sub-steps below):
95
+ - **IMPORTANT**: When enabled, write to `crews/{name}/state.json` before every step and after every handoff, as described throughout this document. When `dashboard_enabled` is false, never create, write, or delete this file.
81
96
  - Create `state.json` from scratch:
82
97
  a. Read `crews/{name}/crew-party.csv` — for each agent row (skip header), extract:
83
98
  - `id`: take the `path` column, strip `./agents/` prefix and `.agent.md` suffix
@@ -270,7 +285,7 @@ Apply this transformation consistently for every write in this step.
270
285
 
271
286
  ### For each pipeline step:
272
287
 
273
- 0. **Update dashboard** — MANDATORY. Write `crews/{name}/state.json` using the Write tool. Always write — it is never wrong to update the dashboard. Use this content:
288
+ 0. **Update dashboard** (only if `dashboard_enabled`; otherwise skip to step 1). Write `crews/{name}/state.json` using the Write tool. Use this content:
274
289
  ```json
275
290
  {
276
291
  "crew": "{crew code from crew.yaml}",
@@ -458,7 +473,8 @@ When a step has `on_reject: {step-id}`:
458
473
 
459
474
  ### Dashboard Handoff (between steps)
460
475
 
461
- After a step completes output and there IS a next step (MANDATORY):
476
+ Only if `dashboard_enabled` (otherwise skip this entire section). After a step
477
+ completes output and there IS a next step:
462
478
 
463
479
  1. **Write delivering state** — Write `crews/{name}/state.json` with:
464
480
  - Current step's agent: `"status": "delivering"`
@@ -489,13 +505,13 @@ After a step completes output and there IS a next step (MANDATORY):
489
505
  For reference, the complete execution order for each pipeline step is:
490
506
 
491
507
  ```
492
- 0. Dashboard update (state.json)
508
+ 0. Dashboard update (state.json) — only if dashboard_enabled
493
509
  1. Pre-Step Input Validation (bash gate)
494
510
  2. Read step file
495
511
  3. Check execution mode and execute (subagent / inline / checkpoint)
496
512
  4. Post-Step Output Validation (bash gate)
497
513
  5. Veto Condition Enforcement
498
- 6. Dashboard Handoff (to next step)
514
+ 6. Dashboard Handoff (to next step) — only if dashboard_enabled
499
515
  ```
500
516
 
501
517
  Steps 1 and 4 are binary bash gates. If either fails, the pipeline does NOT advance — the user is consulted.
@@ -504,7 +520,7 @@ Steps 1 and 4 are binary bash gates. If either fails, the pipeline does NOT adva
504
520
 
505
521
  1. Save final output to `crews/{name}/output/{run_id}/{filename}.md`
506
522
  (The run folder was created during initialization — no separate date subfolder needed)
507
- 1b. **Update dashboard** — MANDATORY. Write `crews/{name}/state.json` with:
523
+ 1b. **Update dashboard** (only if `dashboard_enabled`; otherwise skip to step 2 below). Write `crews/{name}/state.json` with:
508
524
  - `"status": "completed"`
509
525
  - All agents: `"status": "done"`
510
526
  - `"updatedAt"`: now
@@ -512,7 +528,7 @@ Steps 1 and 4 are binary bash gates. If either fails, the pipeline does NOT adva
512
528
  - `"startedAt"`: preserve from existing `state.json`
513
529
  - Keep existing `"handoff"` object
514
530
 
515
- ### Post-Completion Cleanup
531
+ ### Post-Completion Cleanup (only if `dashboard_enabled`)
516
532
 
517
533
  After writing the final "completed" state to `crews/{name}/state.json`:
518
534
 
@@ -521,13 +537,12 @@ After writing the final "completed" state to `crews/{name}/state.json`:
521
537
  ```bash
522
538
  cp crews/{name}/state.json crews/{name}/output/{run_id}/state.json
523
539
  ```
524
- 3. Wait 10 seconds (so the dashboard can display the completed state)
525
- 4. Delete the working copy:
526
- ```bash
527
- rm crews/{name}/state.json
528
- ```
540
+ 3. Leave the working copy of `crews/{name}/state.json` in place — do not delete it and
541
+ do not add an artificial delay. A dashboard watching the file already sees the
542
+ "completed" status the moment it's written; the next run's initialization (step 6)
543
+ overwrites this file from scratch. There is nothing to clean up.
529
544
 
530
- This archives the run state for the `runs` command while keeping the crew root clean.
545
+ This archives the run state for the `runs` command while keeping crew history available.
531
546
 
532
547
  2. **Update crew memory** — write to BOTH files (runs after Post-Completion Cleanup above):
533
548
 
@@ -12,9 +12,19 @@ You are the Skills Engine. Your job is to manage skill integrations for opencrew
12
12
  ## File Locations
13
13
 
14
14
  - **Installed skills**: `skills/` — each skill in its own subdirectory with SKILL.md
15
- - **Skill catalog**: `https://github.com/renatoasse/opensquad/tree/main/skills`
15
+ - **Skill catalog index**: `skills/catalog.json` (local) — structured skill metadata.
16
+ If missing, fall back to the catalog README:
17
+ `https://raw.githubusercontent.com/alberthpalhares/opencrew/main/templates/skills/README.md`
16
18
  - **Skill format reference**: `skills/opencrew-skill-creator/references/skill-format.md`
17
19
 
20
+ ### Catalog URL Resolution
21
+
22
+ When fetching skill files from the catalog, resolve the base URL in this order:
23
+ 1. Read `skills/catalog.json` (installed locally during init/update) → use its `baseUrl` field
24
+ 2. If not available, default to:
25
+ `https://raw.githubusercontent.com/alberthpalhares/opencrew/main/templates/skills`
26
+ 3. Append `/<name>/SKILL.md` (or other file paths) to the base URL
27
+
18
28
  ## How Skills Are Detected
19
29
 
20
30
  A skill is installed if and only if `skills/<name>/SKILL.md` exists.
@@ -96,9 +106,18 @@ For the full SKILL.md specification, see `skills/opencrew-skill-creator/referenc
96
106
 
97
107
  1. User provides a skill name (or selects from the catalog).
98
108
 
99
- 2. **Fetch SKILL.md from GitHub**:
109
+ 2. **Resolve the catalog base URL** (see Catalog URL Resolution above).
110
+
111
+ 3. **Validate minimum version** (if catalog.json available):
112
+ - Check `catalog.json` → `skills.<name>.minVersion`
113
+ - Compare against the installed opencrew version (from `_opencrew/.opencrew-version`)
114
+ - If installed version < minVersion → **ERROR**: "Skill '{name}' requires opencrew
115
+ v{minVersion} or newer. You have v{installed}. Run `npx @aksp/opencrew update`
116
+ to upgrade."
117
+
118
+ 4. **Fetch SKILL.md from catalog**:
100
119
  ```
101
- https://raw.githubusercontent.com/renatoasse/opensquad/main/skills/<name>/SKILL.md
120
+ {baseUrl}/<name>/SKILL.md
102
121
  ```
103
122
  - If fetch fails (404 or network error) → **ERROR**: "Skill '<name>' not found in the skills catalog."
104
123
  - Do NOT proceed if the SKILL.md cannot be fetched.
@@ -112,7 +131,7 @@ For the full SKILL.md specification, see `skills/opencrew-skill-creator/referenc
112
131
 
113
132
  5. **Fetch additional files** (if the skill requires them):
114
133
  - If the SKILL.md frontmatter has `script.path` → fetch the script file from:
115
- `https://raw.githubusercontent.com/renatoasse/opensquad/main/skills/<name>/{script.path}`
134
+ `{baseUrl}/<name>/{script.path}`
116
135
  Create subdirectories (e.g., `scripts/`) as needed.
117
136
  - If the skill has a `references/` directory mentioned → fetch those files too.
118
137
  - If the skill has an `assets/` directory mentioned → fetch those files too.
@@ -121,10 +140,25 @@ For the full SKILL.md specification, see `skills/opencrew-skill-creator/referenc
121
140
 
122
141
  #### a. Environment Variables (if `env:` is present)
123
142
 
124
- For each variable listed in the `env` array, check if it exists in the project `.env` file.
125
- - For each missing variable, inform the user:
126
- "⚠️ Environment variable `{VAR_NAME}` is required by {skill name}. Add it to your `.env` file."
127
- - Do NOT block installation — warn only. The skill is installed even without env vars configured.
143
+ This is a conversational step — never ask the user to open or edit `.env` manually.
144
+ The audience is non-technical (managers, analysts), so all secrets are collected in
145
+ chat and written to disk automatically.
146
+
147
+ For each variable listed in the `env` array:
148
+ 1. Check if it already exists (non-empty) in the project `.env` file → if so, reuse it
149
+ silently, skip to the next variable.
150
+ 2. If missing, ask the user directly in chat:
151
+ "This skill needs an API key to work: **{VAR_NAME}** ({one-line purpose, e.g. 'your
152
+ Instagram access token'}). Paste it here, or press Enter to skip and set it up later."
153
+ 3. If the user provides a value → write it to the project `.env` file (create the file
154
+ from `.env.example` if it doesn't exist yet). Confirm briefly: "✅ Saved."
155
+ 4. If the user skips → inform them:
156
+ "⚠️ {skill name} won't fully work until `{VAR_NAME}` is set. You can add it anytime —
157
+ just ask me to configure {skill name} again."
158
+ - Do NOT block installation either way — the skill is installed regardless of whether
159
+ env vars were provided.
160
+ - Values collected here are reused in steps b/c below — do not ask for the same
161
+ variable twice.
128
162
 
129
163
  #### b. MCP Setup — stdio transport (if `type: mcp` or `type: hybrid` with `mcp.transport: stdio` or no transport specified)
130
164
 
@@ -135,9 +169,8 @@ For the full SKILL.md specification, see `skills/opencrew-skill-creator/referenc
135
169
  1. Yes, overwrite
136
170
  2. No, keep existing
137
171
  If "No" → skip MCP configuration but still complete installation
138
- 3. For each env var listed in the skill's `env` array:
139
- - Check `.env` for an existing value
140
- - If missing, ask the user to type the value directly
172
+ 3. Use the values already collected in step 6.a for each env var in the skill's `env`
173
+ array (do not ask again).
141
174
  4. Add to `mcpServers`:
142
175
  ```json
143
176
  "{server_name}": {
@@ -154,9 +187,8 @@ For the full SKILL.md specification, see `skills/opencrew-skill-creator/referenc
154
187
 
155
188
  1. Read `.claude/settings.local.json` (create with `{"mcpServers": {}}` if it doesn't exist)
156
189
  2. Check for `server_name` conflict (same as stdio above)
157
- 3. For each env var listed in the skill's `env` array:
158
- - Check `.env` for an existing value
159
- - If missing, ask the user to type the value directly
190
+ 3. Use the values already collected in step 6.a for each env var in the skill's `env`
191
+ array (do not ask again).
160
192
  4. Build the mcpServers entry:
161
193
  - Start with `{ "type": "http", "url": "{url}" }`
162
194
  - If the skill has a `headers` field in `mcp`, add a `"headers"` object by resolving
@@ -267,8 +299,11 @@ before the pipeline begins (fail fast).
267
299
 
268
300
  d. **Verify env vars** (if `env:` is present):
269
301
  - Check each variable in `.env`
270
- - If any are missing → warn the user but do NOT block pipeline execution.
271
- "⚠️ Skill '{skill}' is missing environment variable(s): {list}. It may not work correctly."
302
+ - If any are missing → ask the user conversationally, right here in chat, the same
303
+ way as Operation 2, step 6.a (never point them to the `.env` file to edit it
304
+ themselves). If they provide a value, write it to `.env` and continue. If they
305
+ skip, warn: "⚠️ Skill '{skill}' is missing environment variable(s): {list}. It may
306
+ not work correctly." — but do NOT block pipeline execution either way.
272
307
 
273
308
  4. **Return resolved skill list**: Return all resolved skills with their parsed frontmatter
274
309
  and SKILL.md body content. This list is used by Operation 6 to inject instructions.
@@ -329,11 +364,16 @@ When the Architect reaches Phase 3.5 during crew creation:
329
364
  to get name, description, type, and categories.
330
365
 
331
366
  2. **Fetch the catalog index**:
332
- Fetch the catalog README from GitHub to see all available skills:
333
- ```
334
- https://raw.githubusercontent.com/renatoasse/opensquad/main/skills/README.md
335
- ```
336
- - If fetch fails → proceed with only installed skills (do not block crew creation).
367
+ a. First, try to read `skills/catalog.json` (installed locally). If it exists:
368
+ - Parse the JSON and extract the `skills` map and `baseUrl`
369
+ - Use this for discovery (skip the network fetch below)
370
+ b. If `skills/catalog.json` does NOT exist (pre-v1.1 install), fall back to fetching
371
+ the catalog README from GitHub:
372
+ ```
373
+ https://raw.githubusercontent.com/alberthpalhares/opencrew/main/templates/skills/README.md
374
+ ```
375
+ Parse the markdown table to extract skill names, types, and descriptions.
376
+ c. If both fail → proceed with only installed skills (do not block crew creation).
337
377
 
338
378
  3. **Analyze crew requirements**:
339
379
  From the discovery phase answers (Phase 1), identify what the crew needs:
@@ -0,0 +1,22 @@
1
+ # Catálogo de Skills — opencrew
2
+
3
+ Este é o índice de skills disponíveis no catálogo oficial do opencrew. O Skills Engine
4
+ (`_opencrew/core/skills.engine.md`) lê este arquivo durante a descoberta de skills (Operação 7)
5
+ e busca `SKILL.md` de cada uma via `https://raw.githubusercontent.com/alberthpalhares/opencrew/main/templates/skills/<name>/SKILL.md`
6
+ para instalação (Operação 2).
7
+
8
+ | Skill | Tipo | Descrição |
9
+ |-------|------|-----------|
10
+ | `apify` | mcp | Plataforma de web scraping e automação. Extrai dados de qualquer site usando Actors prontos da Apify Store. |
11
+ | `blotato` | mcp | Publicação e agendamento em redes sociais (Instagram, LinkedIn, Twitter/X, TikTok, YouTube). |
12
+ | `canva` | mcp | Cria, busca, preenche e exporta designs do Canva (autenticação via OAuth). |
13
+ | `image-ai-generator` | script | Gera imagens via API do OpenRouter (modo teste barato + modo produção de alta qualidade). |
14
+ | `image-creator` | mcp | Renderiza HTML/CSS em imagens pixel-perfect via Playwright. Motor genérico para qualquer formato visual. |
15
+ | `image-fetcher` | hybrid | Obtém assets visuais de múltiplas fontes: busca na web, screenshots via Playwright, arquivos do usuário. |
16
+ | `instagram-publisher` | script | Publica carrosséis do Instagram a partir de imagens locais via imgBB + Graph API. |
17
+ | `opencrew-best-practice-creator` | prompt | Guia a criação e manutenção de arquivos de best-practice na biblioteca do opencrew. |
18
+ | `opencrew-skill-creator` | prompt | Cria, edita e avalia (evals/benchmark) skills do opencrew de qualquer tipo. |
19
+ | `resend` | mcp | Envio de emails pelo servidor MCP oficial da Resend (individual, lote, anexos, agendamento). |
20
+ | `template-designer` | prompt | Seleção de template visual para agentes de design — gera variações, renderiza e salva a identidade aprovada. |
21
+
22
+ Para instalar qualquer uma delas dentro da sua IDE: `/opencrew install <nome>`.
@@ -0,0 +1,61 @@
1
+ {
2
+ "version": "1",
3
+ "baseUrl": "https://raw.githubusercontent.com/alberthpalhares/opencrew/main/templates/skills",
4
+ "skills": {
5
+ "apify": {
6
+ "type": "mcp",
7
+ "description": "Plataforma de web scraping e automação. Extrai dados de qualquer site usando Actors prontos da Apify Store.",
8
+ "minVersion": "1.0.0"
9
+ },
10
+ "blotato": {
11
+ "type": "mcp",
12
+ "description": "Publicação e agendamento em redes sociais (Instagram, LinkedIn, Twitter/X, TikTok, YouTube).",
13
+ "minVersion": "1.0.0"
14
+ },
15
+ "canva": {
16
+ "type": "mcp",
17
+ "description": "Cria, busca, preenche e exporta designs do Canva (autenticação via OAuth).",
18
+ "minVersion": "1.0.0"
19
+ },
20
+ "image-ai-generator": {
21
+ "type": "script",
22
+ "description": "Gera imagens via API do OpenRouter (modo teste barato + modo produção de alta qualidade).",
23
+ "minVersion": "1.0.0"
24
+ },
25
+ "image-creator": {
26
+ "type": "mcp",
27
+ "description": "Renderiza HTML/CSS em imagens pixel-perfect via Playwright. Motor genérico para qualquer formato visual.",
28
+ "minVersion": "1.0.0"
29
+ },
30
+ "image-fetcher": {
31
+ "type": "hybrid",
32
+ "description": "Obtém assets visuais de múltiplas fontes: busca na web, screenshots via Playwright, arquivos do usuário.",
33
+ "minVersion": "1.0.0"
34
+ },
35
+ "instagram-publisher": {
36
+ "type": "script",
37
+ "description": "Publica carrosséis do Instagram a partir de imagens locais via imgBB + Graph API.",
38
+ "minVersion": "1.0.0"
39
+ },
40
+ "opencrew-best-practice-creator": {
41
+ "type": "prompt",
42
+ "description": "Guia a criação e manutenção de arquivos de best-practice na biblioteca do opencrew.",
43
+ "minVersion": "1.0.0"
44
+ },
45
+ "opencrew-skill-creator": {
46
+ "type": "prompt",
47
+ "description": "Cria, edita e avalia (evals/benchmark) skills do opencrew de qualquer tipo.",
48
+ "minVersion": "1.0.0"
49
+ },
50
+ "resend": {
51
+ "type": "mcp",
52
+ "description": "Envio de emails pelo servidor MCP oficial da Resend (individual, lote, anexos, agendamento).",
53
+ "minVersion": "1.0.0"
54
+ },
55
+ "template-designer": {
56
+ "type": "prompt",
57
+ "description": "Seleção de template visual para agentes de design — gera variações, renderiza e salva a identidade aprovada.",
58
+ "minVersion": "1.0.0"
59
+ }
60
+ }
61
+ }
@@ -41,7 +41,7 @@ Use the Instagram Publisher when you need to publish carousel posts directly to
41
41
 
42
42
  1. List JPEG files in `crews/{crew}/output/images/` sorted by name.
43
43
  If no files found: stop and ask the user to add images before continuing.
44
- 2. Present the image list to the user with AskUserQuestion to confirm order.
44
+ 2. Present the image list to the user to confirm order (use your IDE's native interactive-choice mechanism if it has one; otherwise a numbered list).
45
45
  3. Extract the caption from the content draft:
46
46
  - Use the hook slide text + CTA slide text
47
47
  - Max 2200 characters (Instagram limit)