@aksp/opencrew 1.4.1 → 1.5.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 (34) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +38 -17
  3. package/bin/opencrew.js +4 -4
  4. package/package.json +4 -3
  5. package/src/cli.js +93 -43
  6. package/src/commands/init.js +79 -53
  7. package/src/commands/update.js +44 -17
  8. package/src/lib/errors.js +12 -0
  9. package/src/lib/fsx.js +18 -9
  10. package/src/lib/ides.js +9 -34
  11. package/templates/AGENTS.md +42 -63
  12. package/templates/_opencrew/.opencrew-version +1 -1
  13. package/templates/_opencrew/core/best-practices/copywriting.md +4 -1
  14. package/templates/_opencrew/core/best-practices/image-design.md +5 -5
  15. package/templates/_opencrew/core/best-practices/instagram-feed.md +4 -4
  16. package/templates/_opencrew/core/best-practices/instagram-reels.md +1 -1
  17. package/templates/_opencrew/core/best-practices/review.md +7 -0
  18. package/templates/_opencrew/core/best-practices/social-networks-publishing.md +8 -8
  19. package/templates/_opencrew/core/prompts/build.prompt.md +25 -0
  20. package/templates/_opencrew/core/prompts/design.prompt.md +9 -5
  21. package/templates/_opencrew/core/runner.pipeline.md +56 -7
  22. package/templates/_opencrew/core/scripts/verificar/leitura.mjs +99 -0
  23. package/templates/_opencrew/core/scripts/verificar/regras.mjs +127 -0
  24. package/templates/_opencrew/core/scripts/verificar.mjs +118 -0
  25. package/templates/gitignore +3 -1
  26. package/templates/skills/image-ai-generator/SKILL.md +9 -5
  27. package/templates/skills/image-creator/SKILL.md +5 -3
  28. package/templates/skills/image-fetcher/SKILL.md +1 -1
  29. package/templates/skills/instagram-publisher/SKILL.md +34 -16
  30. package/templates/skills/instagram-publisher/scripts/publish.js +58 -27
  31. package/templates/skills/template-designer/SKILL.md +3 -3
  32. package/templates/skills/template-designer/base-templates/model-a.html +1 -1
  33. package/templates/skills/template-designer/base-templates/model-b.html +1 -1
  34. package/templates/skills/template-designer/base-templates/model-c.html +1 -1
@@ -2,26 +2,30 @@ import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import { templatesDir, packageJsonPath } from '../lib/paths.js';
4
4
  import { copyDir, exists, writeFileSafe, readJson, writeBridgeFile } from '../lib/fsx.js';
5
- import { ideById, allIdeIds } from '../lib/ides.js';
6
- import { pickIdes } from '../lib/prompts.js';
5
+ import { ideById, allIdeIds, AGENTS_BRIDGE } from '../lib/ides.js';
6
+ import { pickIdes as promptIdes } from '../lib/prompts.js';
7
+ import { UsageError } from '../lib/errors.js';
7
8
  import { c, log, info, ok, warn, step } from '../lib/ui.js';
8
9
 
9
- export async function init(opts = {}) {
10
+ const STAMP = path.join('_opencrew', '.opencrew-version');
11
+ // .gitignore / .env.example belong to the user: opencrew only owns a marked block at the end.
12
+ const SHARED_BLOCK = { comment: 'hash', position: 'append' };
13
+
14
+ /**
15
+ * @param {object} opts parsed CLI options
16
+ * @param {{ pickIdes?: () => Promise<string[]> }} deps injectable for tests
17
+ */
18
+ export async function init(opts = {}, { pickIdes = promptIdes } = {}) {
10
19
  const target = process.cwd();
11
20
  const pkg = await readJson(packageJsonPath);
12
21
  const version = pkg.version;
13
- const repairBridges = opts['repair-bridges'];
14
-
15
- const alreadyInstalled = await exists(path.join(target, '_opencrew', 'core'));
22
+ const state = await workspaceState(target);
16
23
 
17
24
  // --repair-bridges mode: regenerate IDE bridge files in an existing workspace.
18
- if (repairBridges && alreadyInstalled) {
25
+ if (opts['repair-bridges'] && state !== 'none') {
26
+ const ids = await resolveIdes(opts, async () => allIdeIds());
19
27
  log(`\n${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — repairing IDE bridges`);
20
28
  log(c.dim(`Target: ${target}\n`));
21
-
22
- let ids = normalizeIdes(opts.ide);
23
- if (opts.all) ids = allIdeIds();
24
- if (opts.yes || !ids) ids = allIdeIds();
25
29
  await writeBridges(target, ids, { overwrite: true });
26
30
 
27
31
  log(`\n${c.green(c.bold('Done!'))} IDE bridges regenerated.\n`);
@@ -29,7 +33,7 @@ export async function init(opts = {}) {
29
33
  return;
30
34
  }
31
35
 
32
- if (alreadyInstalled) {
36
+ if (state === 'complete') {
33
37
  warn('An opencrew workspace already exists here.');
34
38
  info(`To update only the framework, use: ${c.cyan('npx @aksp/opencrew update')}`);
35
39
  info(`To repair IDE bridges, use: ${c.cyan('npx @aksp/opencrew init --repair-bridges')}`);
@@ -37,28 +41,18 @@ export async function init(opts = {}) {
37
41
  return;
38
42
  }
39
43
 
44
+ // Every choice is resolved BEFORE the first write: a bad --ide or Ctrl+C on the
45
+ // prompt leaves the folder exactly as it was.
46
+ const ids = await resolveIdes(opts, pickIdes);
47
+
40
48
  log(`\n${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — scaffolding a crew workspace`);
41
49
  log(c.dim(`Target: ${target}\n`));
50
+ if (state === 'partial') info('A previous install was interrupted — resuming (existing files are kept).');
42
51
 
43
52
  // 1. Copy the framework payload (never clobber user work).
44
53
  step('Installing framework files');
45
- const copied = { count: 0 };
46
- await copyDir(path.join(templatesDir, '_opencrew'), path.join(target, '_opencrew'), {
47
- overwrite: false,
48
- // Never ship stray logs or browser sessions; keep the empty dir via .gitkeep only.
49
- skip: (rel) =>
50
- (rel.startsWith('logs/') && rel !== 'logs/.gitkeep') ||
51
- rel.startsWith('_browser_profile/'),
52
- onCopy: () => (copied.count += 1),
53
- });
54
- await copyDir(path.join(templatesDir, 'skills'), path.join(target, 'skills'), {
55
- overwrite: false,
56
- onCopy: () => (copied.count += 1),
57
- });
58
- await copyDir(path.join(templatesDir, 'crews'), path.join(target, 'crews'), {
59
- overwrite: false,
60
- });
61
- ok(`Framework files ready (${copied.count} written, existing files preserved)`);
54
+ const copied = await installPayload(target);
55
+ ok(`Framework files ready (${copied} written, existing files preserved)`);
62
56
 
63
57
  // 2. System doc + root configs.
64
58
  step('Writing configuration');
@@ -68,13 +62,7 @@ export async function init(opts = {}) {
68
62
  await writeFileSafe(path.join(target, '_opencrew', 'core', 'system.md'), await tpl('AGENTS.md'));
69
63
  ok('_opencrew/core/system.md (full system definition)');
70
64
 
71
- const agentsBridge = '# opencrew\n\n'
72
- + 'The opencrew system definition lives at `_opencrew/core/system.md`.\n'
73
- + 'Read that file and adopt the opencrew system role — follow all initialization,\n'
74
- + 'command routing, and workflow instructions defined there.\n\n'
75
- + 'Type `/opencrew` to open the main menu.\n';
76
-
77
- const agentsResult = await writeBridgeFile(path.join(target, 'AGENTS.md'), agentsBridge);
65
+ const agentsResult = await writeBridgeFile(path.join(target, 'AGENTS.md'), AGENTS_BRIDGE);
78
66
  if (agentsResult.merged) info('AGENTS.md (merged — existing content preserved)');
79
67
  else ok('AGENTS.md (bridge to system.md)');
80
68
 
@@ -83,18 +71,13 @@ export async function init(opts = {}) {
83
71
  });
84
72
  info(mcpWritten ? '.mcp.json' : '.mcp.json (kept existing)');
85
73
 
86
- await writeFileSafe(path.join(target, '.env.example'), await tpl('.env.example'));
87
- const giWritten = await writeFileSafe(path.join(target, '.gitignore'), await tpl('gitignore'), {
88
- overwrite: false,
89
- });
90
- info(giWritten ? '.gitignore' : '.gitignore (kept existing)');
74
+ for (const [file, template] of [['.env.example', '.env.example'], ['.gitignore', 'gitignore']]) {
75
+ const res = await writeBridgeFile(path.join(target, file), await tpl(template), SHARED_BLOCK);
76
+ info(res.merged ? `${file} (opencrew block added at the end — your lines kept)` : file);
77
+ }
91
78
 
92
79
  // 3. IDE bridge files.
93
80
  step('Configuring AI IDEs');
94
- let ids = normalizeIdes(opts.ide);
95
- if (opts.all) ids = allIdeIds();
96
- if (opts.yes) ids = allIdeIds();
97
- if (!ids) ids = await pickIdes();
98
81
  await writeBridges(target, ids, { overwrite: false });
99
82
 
100
83
  if (ids.includes('claude-code')) {
@@ -102,8 +85,8 @@ export async function init(opts = {}) {
102
85
  warn(`native Playwright plugin/extension to avoid the two conflicting.`);
103
86
  }
104
87
 
105
- // 4. Version stamp.
106
- await fs.writeFile(path.join(target, '_opencrew', '.opencrew-version'), version + '\n');
88
+ // 4. Version stamp — written LAST: it is what marks the install as complete.
89
+ await fs.writeFile(path.join(target, STAMP), version + '\n');
107
90
 
108
91
  // 5. Done.
109
92
  log(`\n${c.green(c.bold('Done!'))} opencrew is installed.\n`);
@@ -113,10 +96,56 @@ export async function init(opts = {}) {
113
96
  log(` No API keys needed up front — opencrew asks for them in chat only if a skill you use requires one.\n`);
114
97
  }
115
98
 
99
+ /**
100
+ * Copy the framework payload into `target` without overwriting anything.
101
+ * Never copies the version stamp: only a finished init writes it.
102
+ * @returns {Promise<number>} files written
103
+ */
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'), {
108
+ overwrite: false,
109
+ // Never ship stray logs, browser sessions or the template's own stamp.
110
+ skip: (rel) =>
111
+ (rel.startsWith('logs/') && rel !== 'logs/.gitkeep') ||
112
+ rel.startsWith('_browser_profile/') ||
113
+ rel === '.opencrew-version',
114
+ onCopy,
115
+ });
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;
119
+ }
120
+
121
+ /** 'none' (no core) · 'partial' (core without stamp: interrupted install) · 'complete'. */
122
+ async function workspaceState(target) {
123
+ if (!(await exists(path.join(target, '_opencrew', 'core')))) return 'none';
124
+ return (await exists(path.join(target, STAMP))) ? 'complete' : 'partial';
125
+ }
126
+
127
+ /**
128
+ * 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.
131
+ */
132
+ async function resolveIdes(opts, fallback) {
133
+ if (opts.all || opts.yes) return allIdeIds();
134
+ const ids = normalizeIdes(opts.ide);
135
+ if (!ids) return fallback();
136
+ const invalid = ids.filter((id) => !ideById(id));
137
+ const valid = ids.filter((id) => ideById(id));
138
+ if (!valid.length) {
139
+ throw new UsageError(`Unknown IDE "${invalid.join('", "')}". Valid: ${allIdeIds().join(', ')}`);
140
+ }
141
+ for (const id of invalid) warn(`Unknown IDE "${id}" — skipped. Valid: ${allIdeIds().join(', ')}`);
142
+ return valid;
143
+ }
144
+
116
145
  /**
117
146
  * Write IDE bridge files to the target directory.
118
147
  * @param {string} target — project root
119
- * @param {string[]} ids — IDE ids to configure
148
+ * @param {string[]} ids — validated IDE ids to configure
120
149
  * @param {{ overwrite: boolean }} opts
121
150
  */
122
151
  async function writeBridges(target, ids, { overwrite }) {
@@ -124,10 +153,6 @@ async function writeBridges(target, ids, { overwrite }) {
124
153
 
125
154
  for (const id of ids) {
126
155
  const ide = ideById(id);
127
- if (!ide) {
128
- warn(`Unknown IDE "${id}" — skipped. Valid: ${allIdeIds().join(', ')}`);
129
- continue;
130
- }
131
156
  for (const f of ide.files) {
132
157
  if (writtenPaths.has(f.path)) {
133
158
  info(`${f.path} (shared path — written once)`);
@@ -155,5 +180,6 @@ async function tpl(name) {
155
180
  function normalizeIdes(val) {
156
181
  if (!val || val === true) return null;
157
182
  const list = Array.isArray(val) ? val : String(val).split(',');
158
- return list.map((s) => s.trim()).filter(Boolean);
183
+ const ids = list.map((s) => s.trim()).filter(Boolean);
184
+ return ids.length ? ids : null;
159
185
  }
@@ -2,10 +2,12 @@ import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import { templatesDir, packageJsonPath } from '../lib/paths.js';
4
4
  import { copyDir, exists, writeFileSafe, readJson, writeBridgeFile, readFile } from '../lib/fsx.js';
5
+ import { AGENTS_BRIDGE, LEAKED_STATUS_SECTION, ideById } from '../lib/ides.js';
5
6
  import { c, log, info, ok, warn, step } from '../lib/ui.js';
6
7
 
7
8
  // Update refreshes ONLY the framework. It never touches:
8
- // crews/, _opencrew/_memory/, _opencrew/_browser_profile/, .env, IDE bridges.
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).
9
11
  // Note: catalog skills (skills/<name>/ that ship with the package) ARE fully
10
12
  // overwritten below — user edits to a catalog skill's own files are not preserved.
11
13
  // Only skill directories that don't exist in the package's templates/skills/ at all
@@ -72,24 +74,49 @@ export async function update(opts = {}) {
72
74
  await writeFileSafe(path.join(target, '_opencrew', 'core', 'system.md'), systemContent);
73
75
  ok('_opencrew/core/system.md refreshed');
74
76
 
75
- const agentsBridge = '# opencrew\n\n'
76
- + 'The opencrew system definition lives at `_opencrew/core/system.md`.\n'
77
- + 'Read that file and adopt the opencrew system role — follow all initialization,\n'
78
- + 'command routing, and workflow instructions defined there.\n\n'
79
- + 'Type `/opencrew` to open the main menu.\n';
80
-
81
- // If AGENTS.md is a legacy full-system doc (pre-v1.3), replace it entirely with the thin bridge.
82
- const agentsPath = path.join(target, 'AGENTS.md');
83
- const existingAgents = await readFile(agentsPath);
84
- if (existingAgents.includes('# opencrew Instructions') && !existingAgents.includes('<!-- opencrew:start -->')) {
85
- await writeFileSafe(agentsPath, agentsBridge);
86
- ok('AGENTS.md (migrated from legacy full-system to thin bridge)');
87
- } else {
88
- await writeBridgeFile(agentsPath, agentsBridge);
89
- ok('AGENTS.md refreshed');
90
- }
77
+ await refreshAgentsBridge(target);
78
+ await removeLeakedStatusSection(target);
91
79
 
80
+ // Stamp last: a crash above leaves the old version, so the next update retries.
92
81
  await fs.writeFile(versionFile, version + '\n');
93
82
  log(`\n${c.green(c.bold('Updated to v' + version))}.`);
94
83
  log(c.dim('Your crews, memory, IDE bridges and .env were left untouched.\n'));
95
84
  }
85
+
86
+ // Root AGENTS.md: create it if missing; a legacy full-system doc (pre-v1.3) is backed up
87
+ // byte for byte and replaced by the thin bridge; otherwise only the marked block changes.
88
+ async function refreshAgentsBridge(target) {
89
+ const agentsPath = path.join(target, 'AGENTS.md');
90
+ if (!(await exists(agentsPath))) {
91
+ await writeBridgeFile(agentsPath, AGENTS_BRIDGE);
92
+ ok('AGENTS.md (bridge created)');
93
+ return;
94
+ }
95
+ const existing = await readFile(agentsPath);
96
+ if (existing.includes('# opencrew Instructions') && !existing.includes('<!-- opencrew:start -->')) {
97
+ const backup = await freeBackupPath(agentsPath);
98
+ await fs.copyFile(agentsPath, backup);
99
+ await writeFileSafe(agentsPath, AGENTS_BRIDGE);
100
+ ok(`AGENTS.md (migrated from legacy full-system to thin bridge — backed up to ${path.basename(backup)})`);
101
+ return;
102
+ }
103
+ await writeBridgeFile(agentsPath, AGENTS_BRIDGE);
104
+ ok('AGENTS.md refreshed');
105
+ }
106
+
107
+ async function freeBackupPath(file) {
108
+ const bak = `${file}.bak`;
109
+ if (!(await exists(bak))) return bak;
110
+ return `${file}.bak-${new Date().toISOString().replace(/[:.]/g, '-')}`;
111
+ }
112
+
113
+ // 1.4.0/1.4.1 shipped the maintainer's STATUS.md workflow inside CLAUDE.md's opencrew
114
+ // block. Rewrite that block (only that block, only if the leak is there).
115
+ async function removeLeakedStatusSection(target) {
116
+ const claudePath = path.join(target, 'CLAUDE.md');
117
+ if (!(await exists(claudePath))) return;
118
+ if (!(await readFile(claudePath)).includes(LEAKED_STATUS_SECTION)) return;
119
+ const bridge = ideById('claude-code').files.find((f) => f.path === 'CLAUDE.md');
120
+ await writeBridgeFile(claudePath, bridge.content);
121
+ ok('CLAUDE.md (removed the STATUS.md section shipped by mistake in 1.4.0/1.4.1)');
122
+ }
@@ -0,0 +1,12 @@
1
+ // Error types the CLI turns into exit codes (see src/cli.js → exitCodeFor).
2
+
3
+ /** Wrong flags/arguments — reported in one line, exit 1, nothing written. */
4
+ export class UsageError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'UsageError';
8
+ }
9
+ }
10
+
11
+ /** @inquirer throws this when the user presses Ctrl+C on a prompt. */
12
+ export const isPromptCancel = (e) => e?.name === 'ExitPromptError';
package/src/lib/fsx.js CHANGED
@@ -54,17 +54,21 @@ export async function writeFileSafe(p, content, { overwrite = true } = {}) {
54
54
  * Write a bridge file using marked-block strategy.
55
55
  * - File doesn't exist → creates with content wrapped in HTML markers
56
56
  * - File exists with markers → replaces only the block between markers
57
- * - File exists without markers → prepends marked block, preserves existing content
57
+ * - File exists without markers → adds the marked block (prepend or append), preserving
58
+ * existing content byte for byte
58
59
  *
59
60
  * @param {string} p File path
60
61
  * @param {string} block Content to place between markers
61
62
  * @param {object} opts
62
- * @param {string} opts.marker Marker name (default: 'opencrew')
63
+ * @param {string} opts.marker Marker name (default: 'opencrew')
64
+ * @param {'html'|'hash'} opts.comment `<!-- x:start -->` (markdown) or `# x:start` (.gitignore, .env)
65
+ * @param {'prepend'|'append'} opts.position Where a new block goes in an unmarked file
63
66
  * @returns {Promise<{written: boolean, merged: boolean}>}
64
67
  */
65
- export async function writeBridgeFile(p, block, { marker = 'opencrew' } = {}) {
66
- const start = `<!-- ${marker}:start -->`;
67
- const end = `<!-- ${marker}:end -->`;
68
+ export async function writeBridgeFile(p, block, { marker = 'opencrew', comment = 'html', position = 'prepend' } = {}) {
69
+ const [start, end] = comment === 'hash'
70
+ ? [`# ${marker}:start`, `# ${marker}:end`]
71
+ : [`<!-- ${marker}:start -->`, `<!-- ${marker}:end -->`];
68
72
  const marked = `${start}\n${block.trimEnd()}\n${end}`;
69
73
 
70
74
  if (!(await exists(p))) {
@@ -77,9 +81,11 @@ export async function writeBridgeFile(p, block, { marker = 'opencrew' } = {}) {
77
81
 
78
82
  // Already has markers → replace just the block, keep everything else
79
83
  if (existing.includes(start) && existing.includes(end)) {
84
+ // Innermost block only: an orphan start marker (end deleted by hand) must never make
85
+ // the match swallow the user lines between it and the real block.
80
86
  const updated = existing.replace(
81
- new RegExp(escapeRx(start) + '[\\s\\S]*?' + escapeRx(end), 'g'),
82
- marked,
87
+ new RegExp(`${escapeRx(start)}(?:(?!${escapeRx(start)})[\\s\\S])*?${escapeRx(end)}`, 'g'),
88
+ () => marked,
83
89
  );
84
90
  if (updated !== existing) {
85
91
  await fs.writeFile(p, updated);
@@ -88,8 +94,11 @@ export async function writeBridgeFile(p, block, { marker = 'opencrew' } = {}) {
88
94
  return { written: false, merged: false };
89
95
  }
90
96
 
91
- // User content exists → prepend block, preserve everything
92
- await fs.writeFile(p, marked + '\n\n' + existing.trimStart());
97
+ // User content exists → add the block, preserve everything
98
+ const merged = position === 'append'
99
+ ? `${existing.trimEnd()}\n\n${marked}\n`
100
+ : `${marked}\n\n${existing.trimStart()}`;
101
+ await fs.writeFile(p, merged);
93
102
  return { written: true, merged: true };
94
103
  }
95
104
 
package/src/lib/ides.js CHANGED
@@ -41,42 +41,17 @@ Type \`/opencrew\` to open the main menu.
41
41
  - All checkpoint questions use \`AskUserQuestion\`.
42
42
  - opencrew ships its own Playwright MCP (\`.mcp.json\`); disable the native Playwright plugin.
43
43
  - Do not manually edit files under \`_opencrew/core/\` unless you know what you're doing.
44
+ `;
44
45
 
45
- ## STATUS.md (gestão de sessão)
46
-
47
- This project uses \`STATUS.md\` for session continuity. The file is local (gitignored).
48
-
49
- **At the start of every session:**
50
- - Read \`STATUS.md\`. If it doesn't exist, create it with the template below.
51
- - Report a 3-line summary: what was in progress, what's next, any blockers.
52
-
53
- **During the session:**
54
- - Move items from ⬜ Pendente to 🔄 Em andamento when you start working on them.
55
- - Move items to ✅ Concluído when finished.
56
- - Add new items that emerge during work.
57
-
58
- **Before ending the session:**
59
- - Ensure \`STATUS.md\` reflects the real state.
60
- - Update \`Última sessão\` timestamp.
61
-
62
- **Template:**
63
- \`\`\`markdown
64
- # STATUS — OpenCrew
65
-
66
- > Última sessão: {today}
67
- > Skill: /status
68
-
69
- ## 🔄 Em andamento
70
-
71
- ## ⬜ Pendente
72
-
73
- ## ✅ Concluído (esta sessão)
74
-
75
- ## 📋 Backlog
46
+ // 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';
76
52
 
77
- ## 💡 Decisões
78
- \`\`\`
79
- `;
53
+ // Marker that identifies the maintainer STATUS.md section leaked into CLAUDE.md by 1.4.0/1.4.1.
54
+ export const LEAKED_STATUS_SECTION = '## STATUS.md (gestão de sessão)';
80
55
 
81
56
  const render = (title, extra = '') =>
82
57
  `# ${title}\n\n${BRIDGE}${extra ? `\n\n${extra}` : ''}\n`;
@@ -1,6 +1,7 @@
1
1
  # opencrew Instructions
2
2
 
3
- You are now operating as the opencrew system. Your primary role is to help users create, manage, and run AI agent crews.
3
+ You are operating as the opencrew system. Your primary role is to help users
4
+ create, manage, and run AI agent crews.
4
5
 
5
6
  ## Initialization
6
7
 
@@ -8,55 +9,48 @@ On activation, perform these steps IN ORDER:
8
9
 
9
10
  1. Read the company context file: `{project-root}/_opencrew/_memory/company.md`
10
11
  2. Read the preferences file: `{project-root}/_opencrew/_memory/preferences.md`
11
- 3. Check if company.md is empty or contains only the template — if so, trigger ONBOARDING flow
12
+ 3. Check if company.md is empty or contains only the template — if so, trigger ONBOARDING
12
13
  4. Otherwise, display the MAIN MENU
13
14
 
14
15
  ## Onboarding Flow (first time only)
15
16
 
16
17
  If `company.md` is empty or contains `<!-- NOT CONFIGURED -->`:
17
18
 
18
- 1. Welcome the user warmly to opencrew
19
- 2. Ask their name (save to preferences.md)
20
- 3. Ask their preferred language for outputs (save to preferences.md)
21
- 4. Ask for their company name/description and website URL
22
- 5. Use WebFetch on their URL + WebSearch with their company name to research:
23
- - Company description and sector
24
- - Target audience
25
- - Products/services offered
26
- - Tone of voice (inferred from website copy)
27
- - Social media profiles found
28
- 6. Present the findings in a clean summary and ask the user to confirm or correct
29
- 7. Save the confirmed profile to `_opencrew/_memory/company.md`
30
- 8. Show the main menu
19
+ 1. Welcome the user warmly; ask their name and preferred output language (save to
20
+ preferences.md)
21
+ 2. Ask for company name/description and website URL
22
+ 3. Use WebFetch on the URL + WebSearch on the company name to research:
23
+ description/sector, target audience, products/services, tone of voice,
24
+ social media profiles
25
+ 4. Present findings in a clean summary, ask the user to confirm or correct,
26
+ save the profile to `_opencrew/_memory/company.md`
27
+ 5. Show the main menu
31
28
 
32
29
  ## Main Menu
33
30
 
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:
31
+ When the user types `/opencrew` or asks for the menu, present an interactive
32
+ selector with these options (max 4 per question). Use your IDE's native
33
+ interactive-choice mechanism if it has one; otherwise present the options as a
34
+ numbered list and ask the user to reply with a number.
35
35
 
36
- **Primary menu (first question):**
37
- - **Create a new crew** — Describe what you need and I'll build a crew for you
38
- - **Run an existing crew** — Execute a crew's pipeline
39
- - **My crews** — View, edit, repair, or delete your crews
40
- - **More options** — Skills, company profile, settings, and help
36
+ **Primary menu:** Create a new crew · Run an existing crew · My crews ·
37
+ More options
41
38
 
42
- If the user selects "More options", present a second selector the same way:
43
- - **Skills** — Browse, install, create, and manage skills for your crews
44
- - **Company profile** — View or update your company information
45
- - **Settings & Help** — Language, preferences, configuration, and help
39
+ **More options:** Skills · Company profile · Settings & Help
46
40
 
47
41
  ## Command Routing
48
42
 
49
- Parse user input and route to the appropriate action:
43
+ Route input to the matching action:
50
44
 
51
45
  | Input Pattern | Action |
52
46
  |---------------|--------|
53
47
  | `/opencrew` or `/opencrew menu` | Show main menu |
54
48
  | `/opencrew help` | Show help text |
55
49
  | `/opencrew create <description>` | Load Architect → Create Crew flow |
56
- | `/opencrew list` | List all crews in `crews/` directory |
50
+ | `/opencrew list` | List all crews in `crews/` |
57
51
  | `/opencrew run <name>` | Load Pipeline Runner → Execute crew |
58
52
  | `/opencrew edit <name> <changes>` | Load Architect → Edit Crew flow |
59
- | `/opencrew repair <name>` | Load `_opencrew/core/prompts/repair.prompt.md` → fix agent names / rebuild crew-party.csv manifest |
53
+ | `/opencrew repair <name>` | Load `_opencrew/core/prompts/repair.prompt.md` → fix agent names / rebuild crew-party.csv |
60
54
  | `/opencrew skills` | Load Skills Engine → Show skills menu |
61
55
  | `/opencrew install <name>` | Install a skill from the catalog |
62
56
  | `/opencrew uninstall <name>` | Remove an installed skill |
@@ -74,7 +68,7 @@ When a specific agent needs to be activated:
74
68
  1. Read the agent's `.agent.md` file completely
75
69
  2. Adopt the agent's persona (role, identity, communication_style, principles)
76
70
  3. Follow the agent's menu/workflow instructions
77
- 4. When the agent's task is complete, return to opencrew main context
71
+ 4. When the agent's task is complete, return to the opencrew main context
78
72
 
79
73
  ## Loading the Pipeline Runner
80
74
 
@@ -82,37 +76,26 @@ When running a crew:
82
76
 
83
77
  1. Read `crews/{name}/crew.yaml` to understand the pipeline
84
78
  2. Read `crews/{name}/crew-party.csv` to load all agent personas
85
- 3. For each agent in the party CSV, also read their full `.agent.md` file from agents/ directory
86
- 4. Load company context from `_opencrew/_memory/company.md`
79
+ 3. For each agent in the party CSV, also read their full `.agent.md` file
80
+ 4. Load company context from `_opencrew/_memory/company.md` and user preferences
81
+ from `_opencrew/_memory/preferences.md` (used to check the Dashboard toggle)
87
82
  5. Load crew memory from `crews/{name}/_memory/memories.md`
88
- 6. Load user preferences from `_opencrew/_memory/preferences.md` (used to check the Dashboard toggle — see below)
89
- 7. Read the pipeline runner instructions from `_opencrew/core/runner.pipeline.md`
90
- 7b. **Pre-Execution Agent Selection** — only when `crew.yaml` declares
91
- `agent_dependencies:`. The runner analyzes the user's request against the decision
92
- matrix, presents the agents as a numbered multi-select (IDE-neutral), lets the user
93
- confirm/adjust, warns about broken dependencies, and builds the filtered step list
94
- (see `_opencrew/core/runner.pipeline.md` step 4b). Crews without the field skip this
95
- and run all agents.
96
- 8. Execute the pipeline step by step following runner instructions
83
+ 6. Read the pipeline runner instructions from `_opencrew/core/runner.pipeline.md`
84
+ 7. **Pre-Execution Agent Selection** — only when `crew.yaml` declares
85
+ `agent_dependencies:`. Analyze the user's request against the decision matrix,
86
+ present the agents as a numbered multi-select (IDE-neutral), let the user
87
+ confirm/adjust, warn about broken dependencies, and build the filtered step
88
+ list. Crews without the field skip this and run all agents.
89
+ 8. Execute the pipeline step by step following the runner instructions
97
90
 
98
91
  ## Dashboard (Optional)
99
92
 
100
- opencrew ships an optional visual dashboard — a self-contained HTML file
101
- (`dashboard/index.html`) that shows a crew run in progress as an animated
102
- virtual office. It is **disabled by default** and most installs never use it,
103
- so the Pipeline Runner does not write `state.json` unless the user has turned
104
- it on.
105
-
106
- - To use: open `dashboard/index.html` in a browser and point it at the
107
- `crews/{name}/state.json` written during a run.
108
- - Toggle: `Dashboard: enabled` (or `disabled`) in `_opencrew/_memory/preferences.md`,
109
- editable via `/opencrew settings`.
110
- - When disabled (default): the runner never creates, writes, or deletes `state.json`.
111
- - When enabled: the runner writes `crews/{name}/state.json` before each step and at
112
- every handoff, exactly as described in `_opencrew/core/runner.pipeline.md`.
113
- - The dashboard auto-polls `state.json` every 1.5 seconds when in live mode;
114
- it also includes a built-in demo mode so you can see what it looks like
115
- without running a real crew.
93
+ The dashboard is an optional animated view of a crew run (`dashboard/index.html`).
94
+ It is **disabled by default**; most installs never use it. Toggle it via
95
+ `Dashboard: enabled|disabled` in `_opencrew/_memory/preferences.md` (editable via
96
+ `/opencrew settings`). When disabled, the runner never writes `state.json`; when
97
+ enabled, it writes `crews/{name}/state.json` before each step and at every handoff
98
+ (see `_opencrew/core/runner.pipeline.md`).
116
99
 
117
100
  ## Language Handling
118
101
 
@@ -120,13 +103,9 @@ it on.
120
103
  - All user-facing output should be in the user's preferred language
121
104
  - Internal file names and code remain in English
122
105
  - Agent personas communicate in the user's language
123
- - **Exception — crew memory scaffolding stays in PT-BR regardless of Output Language.**
124
- The section headers in `crews/{name}/_memory/memories.md` (e.g. `## Estilo de Escrita`)
125
- and the table columns in `crews/{name}/_memory/runs.md` (e.g. `Data | Run ID | Tema`) are
126
- fixed structural labels, not generated prose — see `_opencrew/core/runner.pipeline.md`.
127
- opencrew's primary supported audience is PT-BR (see README), so these are intentionally
128
- not localized per-user. Only the *content* written into those sections follows the
129
- user's Output Language.
106
+ - Exception: crew memory scaffolding (`memories.md` headers, `runs.md` columns)
107
+ keeps fixed PT-BR structural labels regardless of the user's language —
108
+ see `_opencrew/core/runner.pipeline.md`
130
109
 
131
110
  ## Critical Rules
132
111
 
@@ -1 +1 @@
1
- 1.4.1
1
+ 1.5.0
@@ -17,7 +17,10 @@ version: "1.0.0"
17
17
  5. Present 3 distinct hook options before drafting the body.
18
18
  6. Align completely with brand voice and audience-specific vocabulary.
19
19
  7. Write concise, one-idea sentences and short paragraphs.
20
- 8. Use specific numbers and concrete details instead of vague claims.
20
+ 8. Use specific numbers and concrete details instead of vague claims — but only REAL ones
21
+ (briefing, research with source, company profile). Never invent cases, testimonials, clients,
22
+ numbers or first-person stories: nunca invente; write `[PREENCHER: o que falta]` instead and
23
+ the user fills it in at final approval.
21
24
  9. Select one dominant psychological driver and anchor the piece to it.
22
25
  10. Deploy the appropriate framework (AIDA, PAS, BAB) based on the funnel stage.
23
26
  11. Inject an objection neutralizer immediately before the CTA.
@@ -88,7 +88,7 @@ Present all rendered images to the user or downstream agent. Include the design
88
88
  ## Platform Specifications
89
89
 
90
90
  ### Instagram Post / Carousel
91
- - **Viewport**: 1080 x 1440 (3:4 portrait)
91
+ - **Viewport**: 1080 x 1350 (4:5 portrait)
92
92
  - **Min font sizes**: Hero 58px, Heading 43px, Body 34px, Caption 24px
93
93
  - **Optimal slide count**: 5-10 slides. Under 5 feels incomplete, over 10 causes drop-off.
94
94
  - **Structure**: Hook on slide 1, CTA on last slide, value in between.
@@ -135,7 +135,7 @@ Present all rendered images to the user or downstream agent. Include the design
135
135
  DESIGN SYSTEM
136
136
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
137
137
  Platform: Instagram Carousel
138
- Viewport: 1080 x 1440
138
+ Viewport: 1080 x 1350
139
139
  Slides: 7 (hook + 5 content + CTA)
140
140
 
141
141
  Colors:
@@ -181,7 +181,7 @@ File: slide-01.html
181
181
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@500;700&display=swap');
182
182
  * { margin: 0; padding: 0; box-sizing: border-box; }
183
183
  body {
184
- width: 1080px; height: 1440px; overflow: hidden;
184
+ width: 1080px; height: 1350px; overflow: hidden;
185
185
  background: #1A1A2E;
186
186
  font-family: 'Inter', sans-serif;
187
187
  display: flex; flex-direction: column;
@@ -342,7 +342,7 @@ Design rationale: Clean white background matches LinkedIn's professional aesthet
342
342
 
343
343
  3. **Document design rationale.** After each completed design, briefly explain why you made the key visual choices: color rationale, font selection, layout strategy. This helps the user understand the design thinking and makes iteration faster.
344
344
 
345
- 4. **Match viewport exactly.** Body width and height in CSS must match the browser viewport resize dimensions exactly. A 1080x1440 carousel slide means body { width: 1080px; height: 1440px; }.
345
+ 4. **Match viewport exactly.** Body width and height in CSS must match the browser viewport resize dimensions exactly. A 1080x1350 carousel slide means body { width: 1080px; height: 1350px; }.
346
346
 
347
347
  ## Vocabulary Guidance
348
348
 
@@ -350,7 +350,7 @@ Design rationale: Clean white background matches LinkedIn's professional aesthet
350
350
 
351
351
  - **"Design system"**: The foundational term for consistent visual identity across pieces. Always define it before creating individual assets.
352
352
  - **"Visual hierarchy"**: How the eye moves through the design. Use this when explaining font size, weight, and positioning choices.
353
- - **"Viewport: WxH"**: Always state the target dimensions explicitly. "Instagram carousel at 1080x1440" not "standard Instagram size."
353
+ - **"Viewport: WxH"**: Always state the target dimensions explicitly. "Instagram carousel at 1080x1350" not "standard Instagram size."
354
354
  - **"Contrast ratio"**: Reference WCAG contrast standards when justifying color combinations. "4.5:1 minimum for body text."
355
355
  - **"Self-contained HTML"**: The non-negotiable constraint. Reinforce that every file must render independently without external dependencies.
356
356
  - **"Rendering verification"**: The step where you visually confirm the screenshot matches the intended design before proceeding.