@aksp/opencrew 1.0.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 +19 -0
- package/LICENSE +24 -0
- package/README.md +118 -0
- package/bin/opencrew.js +8 -0
- package/package.json +57 -0
- package/src/cli.js +70 -0
- package/src/commands/init.js +97 -0
- package/src/commands/update.js +58 -0
- package/src/lib/fsx.js +55 -0
- package/src/lib/ides.js +120 -0
- package/src/lib/paths.js +8 -0
- package/src/lib/prompts.js +35 -0
- package/src/lib/ui.js +20 -0
- package/templates/.env.example +23 -0
- package/templates/.mcp.json +8 -0
- package/templates/AGENTS.md +105 -0
- package/templates/_opencrew/.opencrew-version +1 -0
- package/templates/_opencrew/_investigations/.gitkeep +0 -0
- package/templates/_opencrew/_memory/company.md +4 -0
- package/templates/_opencrew/_memory/preferences.md +9 -0
- package/templates/_opencrew/config/playwright.config.json +11 -0
- package/templates/_opencrew/core/architect.agent.yaml +110 -0
- package/templates/_opencrew/core/best-practices/_catalog.yaml +116 -0
- package/templates/_opencrew/core/best-practices/blog-post.md +151 -0
- package/templates/_opencrew/core/best-practices/blog-seo.md +146 -0
- package/templates/_opencrew/core/best-practices/copywriting.md +446 -0
- package/templates/_opencrew/core/best-practices/data-analysis.md +420 -0
- package/templates/_opencrew/core/best-practices/email-newsletter.md +136 -0
- package/templates/_opencrew/core/best-practices/email-sales.md +127 -0
- package/templates/_opencrew/core/best-practices/image-design.md +365 -0
- package/templates/_opencrew/core/best-practices/instagram-feed.md +252 -0
- package/templates/_opencrew/core/best-practices/instagram-reels.md +128 -0
- package/templates/_opencrew/core/best-practices/instagram-stories.md +123 -0
- package/templates/_opencrew/core/best-practices/linkedin-article.md +133 -0
- package/templates/_opencrew/core/best-practices/linkedin-post.md +138 -0
- package/templates/_opencrew/core/best-practices/researching.md +366 -0
- package/templates/_opencrew/core/best-practices/review.md +286 -0
- package/templates/_opencrew/core/best-practices/social-networks-publishing.md +311 -0
- package/templates/_opencrew/core/best-practices/strategist.md +361 -0
- package/templates/_opencrew/core/best-practices/technical-writing.md +382 -0
- package/templates/_opencrew/core/best-practices/twitter-post.md +122 -0
- package/templates/_opencrew/core/best-practices/twitter-thread.md +139 -0
- package/templates/_opencrew/core/best-practices/whatsapp-broadcast.md +124 -0
- package/templates/_opencrew/core/best-practices/youtube-script.md +139 -0
- package/templates/_opencrew/core/best-practices/youtube-shorts.md +129 -0
- package/templates/_opencrew/core/prompts/build.prompt.md +547 -0
- package/templates/_opencrew/core/prompts/design.prompt.md +469 -0
- package/templates/_opencrew/core/prompts/discovery.prompt.md +269 -0
- package/templates/_opencrew/core/prompts/sherlock-instagram.md +123 -0
- package/templates/_opencrew/core/prompts/sherlock-linkedin.md +73 -0
- package/templates/_opencrew/core/prompts/sherlock-shared.md +684 -0
- package/templates/_opencrew/core/prompts/sherlock-twitter.md +78 -0
- package/templates/_opencrew/core/prompts/sherlock-youtube.md +85 -0
- package/templates/_opencrew/core/runner.pipeline.md +611 -0
- package/templates/_opencrew/core/skills.engine.md +388 -0
- package/templates/_opencrew/logs/.gitkeep +0 -0
- package/templates/crews/.gitkeep +0 -0
- package/templates/gitignore +8 -0
- package/templates/skills/apify/SKILL.md +55 -0
- package/templates/skills/blotato/SKILL.md +63 -0
- package/templates/skills/canva/SKILL.md +60 -0
- package/templates/skills/image-ai-generator/SKILL.md +124 -0
- package/templates/skills/image-ai-generator/scripts/generate.py +175 -0
- package/templates/skills/image-creator/SKILL.md +155 -0
- package/templates/skills/image-fetcher/SKILL.md +91 -0
- package/templates/skills/instagram-publisher/SKILL.md +119 -0
- package/templates/skills/instagram-publisher/scripts/publish.js +165 -0
- package/templates/skills/opencrew-best-practice-creator/SKILL.md +192 -0
- package/templates/skills/opencrew-skill-creator/SKILL.md +420 -0
- package/templates/skills/opencrew-skill-creator/agents/analyzer.md +274 -0
- package/templates/skills/opencrew-skill-creator/agents/comparator.md +202 -0
- package/templates/skills/opencrew-skill-creator/agents/grader.md +223 -0
- package/templates/skills/opencrew-skill-creator/assets/eval_review.html +146 -0
- package/templates/skills/opencrew-skill-creator/eval-viewer/generate_review.py +471 -0
- package/templates/skills/opencrew-skill-creator/eval-viewer/viewer.html +1325 -0
- package/templates/skills/opencrew-skill-creator/references/schemas.md +430 -0
- package/templates/skills/opencrew-skill-creator/references/skill-format.md +235 -0
- package/templates/skills/opencrew-skill-creator/scripts/__init__.py +0 -0
- package/templates/skills/opencrew-skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/templates/skills/opencrew-skill-creator/scripts/quick_validate.py +103 -0
- package/templates/skills/opencrew-skill-creator/scripts/run_eval.py +310 -0
- package/templates/skills/opencrew-skill-creator/scripts/utils.py +47 -0
- package/templates/skills/resend/SKILL.md +80 -0
- package/templates/skills/template-designer/SKILL.md +208 -0
- package/templates/skills/template-designer/base-templates/model-a.html +27 -0
- package/templates/skills/template-designer/base-templates/model-b.html +31 -0
- package/templates/skills/template-designer/base-templates/model-c.html +42 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to opencrew are documented here.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
5
|
+
|
|
6
|
+
## [1.0.0] — 2026-08-01
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- npm-style installer: `npx @aksp/opencrew init` scaffolds a full opencrew workspace.
|
|
10
|
+
- `npx @aksp/opencrew update` refreshes only the framework (`_opencrew/core`, catalog
|
|
11
|
+
skills, `AGENTS.md`) while preserving `crews/`, `_memory/`, IDE bridges and `.env`.
|
|
12
|
+
- Interactive IDE selection during `init` (or `--ide=`, `--all`, non-interactive fallback).
|
|
13
|
+
- Single source of truth: `AGENTS.md`. Every IDE receives only a thin bridge file that
|
|
14
|
+
points to it — adding a new IDE is one entry in `src/lib/ides.js`.
|
|
15
|
+
- Version stamping via `_opencrew/.opencrew-version`, read by `update`.
|
|
16
|
+
|
|
17
|
+
### Notes
|
|
18
|
+
- Reformulation of the OpenSquad framework (originally by Renato Asse) published under
|
|
19
|
+
the `opencrew` name by [aksp](https://www.npmjs.com/~aksp). MIT licensed.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aksp
|
|
4
|
+
|
|
5
|
+
opencrew framework originally created by Renato Asse (https://github.com/renatoasse/opensquad).
|
|
6
|
+
opencrew is an independent distribution/reformulation released under the same MIT terms.
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# opencrew
|
|
2
|
+
|
|
3
|
+
**Create AI agent crews that work together — right from your IDE.**
|
|
4
|
+
|
|
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.
|
|
9
|
+
|
|
10
|
+
> **This is not an original framework — it's my personal build of OpenSquad.**
|
|
11
|
+
> [OpenSquad](https://github.com/renatoasse/opensquad) was created by
|
|
12
|
+
> [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.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
**Prerequisite:** Node.js 20+
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @aksp/opencrew init
|
|
26
|
+
```
|
|
27
|
+
|
|
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:
|
|
30
|
+
|
|
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.
|
|
34
|
+
|
|
35
|
+
Preselect IDEs (skip the prompt) or configure all of them:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx @aksp/opencrew init --ide=claude-code,codex
|
|
39
|
+
npx @aksp/opencrew init --all
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Updating
|
|
43
|
+
|
|
44
|
+
Refresh the framework without losing your work:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx @aksp/opencrew update
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`update` refreshes only `_opencrew/core`, the catalog skills and `AGENTS.md`.
|
|
51
|
+
Your `crews/`, memory, IDE bridges and `.env` are left untouched.
|
|
52
|
+
|
|
53
|
+
## Supported IDEs
|
|
54
|
+
|
|
55
|
+
| IDE | Bridge file(s) generated |
|
|
56
|
+
|-----|--------------------------|
|
|
57
|
+
| Claude Code | `.claude/skills/opencrew/SKILL.md`, `CLAUDE.md` |
|
|
58
|
+
| Codex (OpenAI) | `AGENTS.md` (native) + `.agents/skills/opencrew/SKILL.md` |
|
|
59
|
+
| Cursor | `.cursor/rules/opencrew.mdc` |
|
|
60
|
+
| VS Code + Copilot | `.github/copilot-instructions.md` |
|
|
61
|
+
| OpenCode | `.opencode/commands/opencrew.md` |
|
|
62
|
+
| Antigravity | `.agent/rules/opencrew.md`, `.agent/workflows/opencrew.md` |
|
|
63
|
+
| Gemini CLI | `GEMINI.md` |
|
|
64
|
+
| Qwen Code | `QWEN.md` |
|
|
65
|
+
| Trae | `.trae/rules/opencrew.md` |
|
|
66
|
+
|
|
67
|
+
Every bridge is a thin pointer to the single source of truth, **`AGENTS.md`**.
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
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.
|
|
76
|
+
|
|
77
|
+
## Commands (inside your IDE)
|
|
78
|
+
|
|
79
|
+
| Command | What it does |
|
|
80
|
+
|---------|--------------|
|
|
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
|
|
114
|
+
[`npx opensquad init`](https://github.com/renatoasse/opensquad).
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT — see [LICENSE](LICENSE). Original OpenSquad framework © Renato Asse, also MIT.
|
package/bin/opencrew.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aksp/opencrew",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "A reworked build of OpenSquad (by Renato Asse) — create AI agent crews that work together, right from your IDE. Multi-agent orchestration with an npm installer.",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"bin": {
|
|
10
|
+
"opencrew": "bin/opencrew.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"src/",
|
|
15
|
+
"templates/",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"start": "node bin/opencrew.js",
|
|
22
|
+
"test": "node --test tests/*.test.js",
|
|
23
|
+
"version": "node -e \"require('fs').writeFileSync('templates/_opencrew/.opencrew-version', require('./package.json').version + '\\n')\""
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"ai-agents",
|
|
27
|
+
"multi-agent",
|
|
28
|
+
"orchestration",
|
|
29
|
+
"crews",
|
|
30
|
+
"claude-code",
|
|
31
|
+
"cursor",
|
|
32
|
+
"codex",
|
|
33
|
+
"gemini",
|
|
34
|
+
"opencode",
|
|
35
|
+
"cli"
|
|
36
|
+
],
|
|
37
|
+
"author": {
|
|
38
|
+
"name": "aksp",
|
|
39
|
+
"url": "https://www.npmjs.com/~aksp"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/alberthpalhares/opencrew.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/alberthpalhares/opencrew#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/alberthpalhares/opencrew/issues"
|
|
48
|
+
},
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@inquirer/checkbox": "^5.1.0",
|
|
55
|
+
"@inquirer/confirm": "^5.1.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readJson } from './lib/fsx.js';
|
|
2
|
+
import { packageJsonPath } from './lib/paths.js';
|
|
3
|
+
import { allIdeIds } from './lib/ides.js';
|
|
4
|
+
import { init } from './commands/init.js';
|
|
5
|
+
import { update } from './commands/update.js';
|
|
6
|
+
import { c, log, err } from './lib/ui.js';
|
|
7
|
+
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const opts = { _: [] };
|
|
10
|
+
for (const a of argv) {
|
|
11
|
+
if (a.startsWith('--')) {
|
|
12
|
+
const [k, v] = a.slice(2).split('=');
|
|
13
|
+
opts[k] = v === undefined ? true : v;
|
|
14
|
+
} else {
|
|
15
|
+
opts._.push(a);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return opts;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function help(version) {
|
|
22
|
+
log(`
|
|
23
|
+
${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — create AI agent crews that work together
|
|
24
|
+
|
|
25
|
+
${c.bold('Usage')}
|
|
26
|
+
npx @aksp/opencrew <command> [options]
|
|
27
|
+
|
|
28
|
+
${c.bold('Commands')}
|
|
29
|
+
init Scaffold an opencrew workspace in the current folder
|
|
30
|
+
update Refresh the framework (keeps your crews, memory and .env)
|
|
31
|
+
help Show this help
|
|
32
|
+
version Print the version
|
|
33
|
+
|
|
34
|
+
${c.bold('Options for init')}
|
|
35
|
+
--ide=a,b Preselect IDEs (skip the prompt). Valid: ${allIdeIds().join(', ')}
|
|
36
|
+
--all Configure every supported IDE
|
|
37
|
+
--yes Non-interactive; accept defaults
|
|
38
|
+
|
|
39
|
+
${c.bold('Examples')}
|
|
40
|
+
npx @aksp/opencrew init
|
|
41
|
+
npx @aksp/opencrew init --ide=claude-code,codex
|
|
42
|
+
npx @aksp/opencrew init --all
|
|
43
|
+
npx @aksp/opencrew update
|
|
44
|
+
`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function run(argv) {
|
|
48
|
+
const opts = parseArgs(argv);
|
|
49
|
+
const pkg = await readJson(packageJsonPath);
|
|
50
|
+
const version = pkg.version;
|
|
51
|
+
const cmd = opts._[0] || (opts.help ? 'help' : 'init');
|
|
52
|
+
|
|
53
|
+
switch (cmd) {
|
|
54
|
+
case 'init':
|
|
55
|
+
return init(opts);
|
|
56
|
+
case 'update':
|
|
57
|
+
case 'upgrade':
|
|
58
|
+
return update(opts);
|
|
59
|
+
case 'version':
|
|
60
|
+
case '--version':
|
|
61
|
+
return log(version);
|
|
62
|
+
case 'help':
|
|
63
|
+
case '--help':
|
|
64
|
+
return help(version);
|
|
65
|
+
default:
|
|
66
|
+
err(`Unknown command: ${cmd}`);
|
|
67
|
+
help(version);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import { templatesDir, packageJsonPath } from '../lib/paths.js';
|
|
4
|
+
import { copyDir, exists, writeFileSafe, readJson } from '../lib/fsx.js';
|
|
5
|
+
import { IDES, ideById, allIdeIds } from '../lib/ides.js';
|
|
6
|
+
import { pickIdes } from '../lib/prompts.js';
|
|
7
|
+
import { c, log, info, ok, warn, step } from '../lib/ui.js';
|
|
8
|
+
|
|
9
|
+
export async function init(opts = {}) {
|
|
10
|
+
const target = process.cwd();
|
|
11
|
+
const pkg = await readJson(packageJsonPath);
|
|
12
|
+
const version = pkg.version;
|
|
13
|
+
|
|
14
|
+
log(`\n${c.bold(c.cyan('opencrew'))} ${c.dim('v' + version)} — scaffolding a crew workspace`);
|
|
15
|
+
log(c.dim(`Target: ${target}\n`));
|
|
16
|
+
|
|
17
|
+
const alreadyInstalled = await exists(path.join(target, '_opencrew', 'core'));
|
|
18
|
+
if (alreadyInstalled) {
|
|
19
|
+
warn('An opencrew workspace already exists here.');
|
|
20
|
+
warn(`Framework files are refreshed but your crews, memory and .env are kept intact.`);
|
|
21
|
+
warn(`To update only the framework, use: ${c.cyan('npx @aksp/opencrew update')}\n`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 1. Copy the framework payload (never clobber user work).
|
|
25
|
+
step('Installing framework files');
|
|
26
|
+
const copied = { count: 0 };
|
|
27
|
+
await copyDir(path.join(templatesDir, '_opencrew'), path.join(target, '_opencrew'), {
|
|
28
|
+
overwrite: false,
|
|
29
|
+
// Never ship stray logs or browser sessions; keep the empty dir via .gitkeep only.
|
|
30
|
+
skip: (rel) =>
|
|
31
|
+
(rel.startsWith('logs/') && rel !== 'logs/.gitkeep') ||
|
|
32
|
+
rel.startsWith('_browser_profile/'),
|
|
33
|
+
onCopy: () => (copied.count += 1),
|
|
34
|
+
});
|
|
35
|
+
await copyDir(path.join(templatesDir, 'skills'), path.join(target, 'skills'), {
|
|
36
|
+
overwrite: false,
|
|
37
|
+
onCopy: () => (copied.count += 1),
|
|
38
|
+
});
|
|
39
|
+
await copyDir(path.join(templatesDir, 'crews'), path.join(target, 'crews'), {
|
|
40
|
+
overwrite: false,
|
|
41
|
+
});
|
|
42
|
+
ok(`Framework files ready (${copied.count} written, existing files preserved)`);
|
|
43
|
+
|
|
44
|
+
// 2. Canonical system doc + root configs.
|
|
45
|
+
step('Writing configuration');
|
|
46
|
+
await writeFileSafe(path.join(target, 'AGENTS.md'), await tpl('AGENTS.md'));
|
|
47
|
+
ok('AGENTS.md (single source of truth)');
|
|
48
|
+
|
|
49
|
+
const mcpWritten = await writeFileSafe(path.join(target, '.mcp.json'), await tpl('.mcp.json'), {
|
|
50
|
+
overwrite: false,
|
|
51
|
+
});
|
|
52
|
+
info(mcpWritten ? '.mcp.json' : '.mcp.json (kept existing)');
|
|
53
|
+
|
|
54
|
+
await writeFileSafe(path.join(target, '.env.example'), await tpl('.env.example'));
|
|
55
|
+
const giWritten = await writeFileSafe(path.join(target, '.gitignore'), await tpl('gitignore'), {
|
|
56
|
+
overwrite: false,
|
|
57
|
+
});
|
|
58
|
+
info(giWritten ? '.gitignore' : '.gitignore (kept existing)');
|
|
59
|
+
|
|
60
|
+
// 3. IDE bridge files.
|
|
61
|
+
step('Configuring AI IDEs');
|
|
62
|
+
let ids = normalizeIdes(opts.ide);
|
|
63
|
+
if (opts.all) ids = allIdeIds();
|
|
64
|
+
if (!ids) ids = await pickIdes();
|
|
65
|
+
|
|
66
|
+
for (const id of ids) {
|
|
67
|
+
const ide = ideById(id);
|
|
68
|
+
if (!ide) {
|
|
69
|
+
warn(`Unknown IDE "${id}" — skipped. Valid: ${allIdeIds().join(', ')}`);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
for (const f of ide.files) {
|
|
73
|
+
await writeFileSafe(path.join(target, f.path), f.content);
|
|
74
|
+
}
|
|
75
|
+
ok(`${ide.label} → ${ide.files.map((f) => f.path).join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 4. Version stamp.
|
|
79
|
+
await fs.writeFile(path.join(target, '_opencrew', '.opencrew-version'), version + '\n');
|
|
80
|
+
|
|
81
|
+
// 5. Done.
|
|
82
|
+
log(`\n${c.green(c.bold('Done!'))} opencrew is installed.\n`);
|
|
83
|
+
log(`${c.bold('Next steps:')}`);
|
|
84
|
+
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`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function tpl(name) {
|
|
90
|
+
return fs.readFile(path.join(templatesDir, name), 'utf8');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalizeIdes(val) {
|
|
94
|
+
if (!val) return null;
|
|
95
|
+
const list = Array.isArray(val) ? val : String(val).split(',');
|
|
96
|
+
return list.map((s) => s.trim()).filter(Boolean);
|
|
97
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import { templatesDir, packageJsonPath } from '../lib/paths.js';
|
|
4
|
+
import { copyDir, exists, writeFileSafe, readJson } from '../lib/fsx.js';
|
|
5
|
+
import { c, log, info, ok, warn, step } from '../lib/ui.js';
|
|
6
|
+
|
|
7
|
+
// Update refreshes ONLY the framework. It never touches:
|
|
8
|
+
// crews/, _opencrew/_memory/, _opencrew/_browser_profile/, .env, IDE bridges.
|
|
9
|
+
export async function update() {
|
|
10
|
+
const target = process.cwd();
|
|
11
|
+
const pkg = await readJson(packageJsonPath);
|
|
12
|
+
const version = pkg.version;
|
|
13
|
+
|
|
14
|
+
if (!(await exists(path.join(target, '_opencrew', 'core')))) {
|
|
15
|
+
warn('No opencrew workspace found here.');
|
|
16
|
+
info(`Run ${c.cyan('npx @aksp/opencrew init')} first.`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const versionFile = path.join(target, '_opencrew', '.opencrew-version');
|
|
21
|
+
const current = (await exists(versionFile))
|
|
22
|
+
? (await fs.readFile(versionFile, 'utf8')).trim()
|
|
23
|
+
: 'unknown';
|
|
24
|
+
|
|
25
|
+
log(`\n${c.bold(c.cyan('opencrew update'))}`);
|
|
26
|
+
log(c.dim(`Installed: ${current} → Package: ${version}\n`));
|
|
27
|
+
|
|
28
|
+
if (current === version) {
|
|
29
|
+
ok('Already up to date. Refreshing framework files anyway.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Refresh core framework: _opencrew/core is fully overwritten (it is not user data).
|
|
33
|
+
step('Refreshing framework');
|
|
34
|
+
let n = 0;
|
|
35
|
+
await copyDir(path.join(templatesDir, '_opencrew', 'core'), path.join(target, '_opencrew', 'core'), {
|
|
36
|
+
overwrite: true,
|
|
37
|
+
onCopy: () => (n += 1),
|
|
38
|
+
});
|
|
39
|
+
ok(`_opencrew/core refreshed (${n} files)`);
|
|
40
|
+
|
|
41
|
+
// Refresh catalog skills but preserve any user-modified/added ones:
|
|
42
|
+
// only overwrite skills that ship with the package; leave others untouched.
|
|
43
|
+
step('Refreshing catalog skills');
|
|
44
|
+
let s = 0;
|
|
45
|
+
await copyDir(path.join(templatesDir, 'skills'), path.join(target, 'skills'), {
|
|
46
|
+
overwrite: true,
|
|
47
|
+
onCopy: () => (s += 1),
|
|
48
|
+
});
|
|
49
|
+
ok(`Catalog skills refreshed (${s} files)`);
|
|
50
|
+
|
|
51
|
+
// Canonical system doc.
|
|
52
|
+
await writeFileSafe(path.join(target, 'AGENTS.md'), await fs.readFile(path.join(templatesDir, 'AGENTS.md'), 'utf8'));
|
|
53
|
+
ok('AGENTS.md refreshed');
|
|
54
|
+
|
|
55
|
+
await fs.writeFile(versionFile, version + '\n');
|
|
56
|
+
log(`\n${c.green(c.bold('Updated to v' + version))}.`);
|
|
57
|
+
log(c.dim('Your crews, memory, IDE bridges and .env were left untouched.\n'));
|
|
58
|
+
}
|
package/src/lib/fsx.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Filesystem helpers (no external deps).
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export async function exists(p) {
|
|
6
|
+
try {
|
|
7
|
+
await fs.access(p);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function ensureDir(p) {
|
|
15
|
+
await fs.mkdir(p, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Recursively copy `src` into `dest`.
|
|
20
|
+
* @param {object} opts
|
|
21
|
+
* @param {boolean} opts.overwrite Overwrite existing files (default false).
|
|
22
|
+
* @param {(rel:string)=>boolean} opts.skip Return true to skip a relative path.
|
|
23
|
+
* @param {(rel:string)=>void} opts.onCopy Called for each file copied.
|
|
24
|
+
*/
|
|
25
|
+
export async function copyDir(src, dest, opts = {}) {
|
|
26
|
+
const { overwrite = false, skip = () => false, onCopy, _root = src } = opts;
|
|
27
|
+
await ensureDir(dest);
|
|
28
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const from = path.join(src, entry.name);
|
|
31
|
+
const to = path.join(dest, entry.name);
|
|
32
|
+
const rel = path.relative(_root, from).split(path.sep).join('/');
|
|
33
|
+
if (skip(rel)) continue;
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
await copyDir(from, to, { ...opts, _root });
|
|
36
|
+
} else {
|
|
37
|
+
const present = await exists(to);
|
|
38
|
+
if (present && !overwrite) continue;
|
|
39
|
+
await ensureDir(path.dirname(to));
|
|
40
|
+
await fs.copyFile(from, to);
|
|
41
|
+
if (onCopy) onCopy(rel);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function writeFileSafe(p, content, { overwrite = true } = {}) {
|
|
47
|
+
if (!overwrite && (await exists(p))) return false;
|
|
48
|
+
await ensureDir(path.dirname(p));
|
|
49
|
+
await fs.writeFile(p, content);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function readJson(p) {
|
|
54
|
+
return JSON.parse(await fs.readFile(p, 'utf8'));
|
|
55
|
+
}
|
package/src/lib/ides.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Single source of truth = AGENTS.md (shipped at project root).
|
|
2
|
+
// Every IDE gets only a THIN bridge file that points at AGENTS.md.
|
|
3
|
+
// Adding support for a new IDE = one more entry in this list.
|
|
4
|
+
|
|
5
|
+
const BRIDGE = `Read \`AGENTS.md\` at the project root and adopt the opencrew system role.
|
|
6
|
+
Follow all initialization, command routing, and workflow instructions defined there.
|
|
7
|
+
|
|
8
|
+
If invoked with arguments (e.g. \`/opencrew create ...\`, \`/opencrew run ...\`),
|
|
9
|
+
route to the matching action from the Command Routing table in AGENTS.md.
|
|
10
|
+
If invoked without arguments, show the Main Menu.`;
|
|
11
|
+
|
|
12
|
+
// Claude Code needs one extra rule (checkpoints must use AskUserQuestion) and a
|
|
13
|
+
// note that its own Playwright plugin must be off (opencrew ships its own via .mcp.json).
|
|
14
|
+
const CLAUDE_SKILL = `---
|
|
15
|
+
name: opencrew
|
|
16
|
+
description: "opencrew — multi-agent orchestration. Use when the user types /opencrew or asks to create, run, or manage AI agent crews."
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# opencrew (Claude Code entry point)
|
|
20
|
+
|
|
21
|
+
${BRIDGE}
|
|
22
|
+
|
|
23
|
+
## Claude Code specifics (override AGENTS.md where they conflict)
|
|
24
|
+
|
|
25
|
+
- **Checkpoints MUST use \`AskUserQuestion\`** — never output a checkpoint question as plain text.
|
|
26
|
+
Combine multiple questions into a single call (max 4 slots, each with 2–4 options).
|
|
27
|
+
- Checkpoint steps always run inline (never dispatched as subagents).
|
|
28
|
+
- opencrew uses its own \`@playwright/mcp\` server (see \`.mcp.json\`). The native Claude Code
|
|
29
|
+
Playwright plugin must be disabled to avoid conflicts.
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const CLAUDE_MD = `# opencrew — Project Instructions
|
|
33
|
+
|
|
34
|
+
This project uses **opencrew**, a multi-agent orchestration framework.
|
|
35
|
+
The full system definition lives in \`AGENTS.md\` — read it and adopt that role.
|
|
36
|
+
|
|
37
|
+
Type \`/opencrew\` to open the main menu.
|
|
38
|
+
|
|
39
|
+
## Notes for Claude Code
|
|
40
|
+
|
|
41
|
+
- All checkpoint questions use \`AskUserQuestion\`.
|
|
42
|
+
- opencrew ships its own Playwright MCP (\`.mcp.json\`); disable the native Playwright plugin.
|
|
43
|
+
- Do not manually edit files under \`_opencrew/core/\` unless you know what you're doing.
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const render = (title, extra = '') =>
|
|
47
|
+
`# ${title}\n\n${BRIDGE}${extra ? `\n\n${extra}` : ''}\n`;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Each IDE lists the files its bridge writes. `content` is plain text.
|
|
51
|
+
* `mdc` files (Cursor) get a small frontmatter so the rule always applies.
|
|
52
|
+
*/
|
|
53
|
+
export const IDES = [
|
|
54
|
+
{
|
|
55
|
+
id: 'claude-code',
|
|
56
|
+
label: 'Claude Code',
|
|
57
|
+
files: [
|
|
58
|
+
{ path: '.claude/skills/opencrew/SKILL.md', content: CLAUDE_SKILL },
|
|
59
|
+
{ path: 'CLAUDE.md', content: CLAUDE_MD },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'codex',
|
|
64
|
+
label: 'Codex (OpenAI)',
|
|
65
|
+
// Codex reads AGENTS.md natively — always shipped. Add a slash-style helper too.
|
|
66
|
+
files: [
|
|
67
|
+
{ path: '.agents/skills/opencrew/SKILL.md', content: `---\nname: opencrew\ndescription: Run opencrew — multi-agent orchestration. Use when the user types /opencrew or asks to create, run, or manage crews.\n---\n\n${BRIDGE}\n` },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'cursor',
|
|
72
|
+
label: 'Cursor',
|
|
73
|
+
files: [
|
|
74
|
+
{ path: '.cursor/rules/opencrew.mdc', content: `---\ndescription: opencrew — multi-agent orchestration framework.\nalwaysApply: true\n---\n\n${BRIDGE}\n` },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'copilot',
|
|
79
|
+
label: 'VS Code + Copilot',
|
|
80
|
+
files: [
|
|
81
|
+
{ path: '.github/copilot-instructions.md', content: render('opencrew — Copilot Instructions') },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'opencode',
|
|
86
|
+
label: 'OpenCode',
|
|
87
|
+
files: [
|
|
88
|
+
{ path: '.opencode/commands/opencrew.md', content: `---\ndescription: opencrew — Multi-agent orchestration framework. Create and run AI crews.\n---\n\n${BRIDGE}\n` },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'antigravity',
|
|
93
|
+
label: 'Antigravity (Gemini)',
|
|
94
|
+
files: [
|
|
95
|
+
{
|
|
96
|
+
path: '.agent/rules/opencrew.md',
|
|
97
|
+
content: `---\nname: opencrew\n---\n\n${BRIDGE}\n\n## Antigravity specifics\n\n- This environment does not support background/parallel subagents. Run all tasks inline and\n sequentially — never announce parallel work and then skip it.\n- Ask only one question per message; present options as a numbered list.\n`,
|
|
98
|
+
},
|
|
99
|
+
{ path: '.agent/workflows/opencrew.md', content: render('opencrew Workflow (Antigravity)') },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'gemini',
|
|
104
|
+
label: 'Gemini CLI',
|
|
105
|
+
files: [{ path: 'GEMINI.md', content: render('opencrew — Gemini CLI') }],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'qwen',
|
|
109
|
+
label: 'Qwen Code',
|
|
110
|
+
files: [{ path: 'QWEN.md', content: render('opencrew — Qwen Code') }],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'trae',
|
|
114
|
+
label: 'Trae',
|
|
115
|
+
files: [{ path: '.trae/rules/opencrew.md', content: render('opencrew — Trae') }],
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
export const ideById = (id) => IDES.find((i) => i.id === id);
|
|
120
|
+
export const allIdeIds = () => IDES.map((i) => i.id);
|
package/src/lib/paths.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Resolve locations relative to the installed package.
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const here = path.dirname(fileURLToPath(import.meta.url)); // src/lib
|
|
6
|
+
export const packageRoot = path.resolve(here, '..', '..');
|
|
7
|
+
export const templatesDir = path.join(packageRoot, 'templates');
|
|
8
|
+
export const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Interactive prompts with a non-interactive fallback.
|
|
2
|
+
// Uses @inquirer/checkbox when a TTY is available; otherwise returns defaults.
|
|
3
|
+
import { IDES, allIdeIds } from './ides.js';
|
|
4
|
+
import { warn } from './ui.js';
|
|
5
|
+
|
|
6
|
+
export async function pickIdes({ preselected } = {}) {
|
|
7
|
+
const isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
8
|
+
if (!isTTY) {
|
|
9
|
+
const chosen = preselected?.length ? preselected : allIdeIds();
|
|
10
|
+
warn(`Non-interactive terminal — configuring IDEs: ${chosen.join(', ')}`);
|
|
11
|
+
return chosen;
|
|
12
|
+
}
|
|
13
|
+
const { default: checkbox, Separator } = await import('@inquirer/checkbox');
|
|
14
|
+
const answer = await checkbox({
|
|
15
|
+
message: 'Which AI IDEs do you use? (space to toggle, enter to confirm)',
|
|
16
|
+
instructions: false,
|
|
17
|
+
choices: [
|
|
18
|
+
...IDES.map((i) => ({
|
|
19
|
+
name: i.label,
|
|
20
|
+
value: i.id,
|
|
21
|
+
checked: preselected ? preselected.includes(i.id) : i.id === 'claude-code',
|
|
22
|
+
})),
|
|
23
|
+
new Separator(),
|
|
24
|
+
],
|
|
25
|
+
validate: (a) => (a.length ? true : 'Select at least one IDE.'),
|
|
26
|
+
});
|
|
27
|
+
return answer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function confirm(message, def = true) {
|
|
31
|
+
const isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
32
|
+
if (!isTTY) return def;
|
|
33
|
+
const { default: confirmPrompt } = await import('@inquirer/confirm');
|
|
34
|
+
return confirmPrompt({ message, default: def });
|
|
35
|
+
}
|