maestro-bundle 1.0.0 → 1.2.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/README.md +54 -62
- package/package.json +1 -1
- package/src/cli.mjs +345 -108
package/README.md
CHANGED
|
@@ -1,91 +1,83 @@
|
|
|
1
1
|
# maestro-bundle
|
|
2
2
|
|
|
3
|
-
Um comando. Bundle instalado. Agente governado.
|
|
3
|
+
Um comando. Bundle instalado. Agente governado. Funciona com **qualquer editor AI**.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx maestro-bundle ai-agents
|
|
6
|
+
npx maestro-bundle ai-agents claude
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- `AGENTS.md` na raiz (compatível com Cursor, Claude Code, Copilot, Windsurf, etc.)
|
|
11
|
-
- `skills/` com todas as skills do bundle
|
|
12
|
-
- `.spec/constitution.md` do GitHub Spec Kit
|
|
13
|
-
- `references/` com docs de referência
|
|
14
|
-
- Instala o GitHub Spec Kit se necessário
|
|
9
|
+
## Editores suportados
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
npx maestro-bundle
|
|
20
|
-
npx maestro-bundle
|
|
21
|
-
npx maestro-bundle
|
|
22
|
-
npx maestro-bundle
|
|
23
|
-
npx maestro-bundle
|
|
24
|
-
```
|
|
11
|
+
| Editor | Comando | O que instala |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **Claude Code** | `npx maestro-bundle <bundle> claude` | `CLAUDE.md` + `.claude/rules/` |
|
|
14
|
+
| **Cursor** | `npx maestro-bundle <bundle> cursor` | `.cursorrules` + `.cursor/rules/` |
|
|
15
|
+
| **OpenAI Codex** | `npx maestro-bundle <bundle> codex` | `AGENTS.md` |
|
|
16
|
+
| **GitHub Copilot** | `npx maestro-bundle <bundle> copilot` | `.github/copilot-instructions.md` + `.github/instructions/` |
|
|
17
|
+
| **Windsurf** | `npx maestro-bundle <bundle> windsurf` | `.windsurfrules` |
|
|
18
|
+
| **Todos** | `npx maestro-bundle <bundle> all` | Tudo acima de uma vez |
|
|
25
19
|
|
|
26
|
-
##
|
|
20
|
+
## Bundles disponíveis
|
|
27
21
|
|
|
28
22
|
```bash
|
|
29
|
-
#
|
|
30
|
-
npx maestro-bundle
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
npx maestro-bundle
|
|
34
|
-
|
|
35
|
-
# Ver bundles disponíveis
|
|
36
|
-
npx maestro-bundle --help
|
|
23
|
+
npx maestro-bundle ai-agents claude # Agentes AI com LangChain
|
|
24
|
+
npx maestro-bundle jhipster-monorepo cursor # JHipster monolítico
|
|
25
|
+
npx maestro-bundle jhipster-microservices all # JHipster microsserviços
|
|
26
|
+
npx maestro-bundle data-pipeline codex # Pipeline dados/ML
|
|
27
|
+
npx maestro-bundle frontend-spa copilot # React SPA
|
|
37
28
|
```
|
|
38
29
|
|
|
39
30
|
## O que acontece
|
|
40
31
|
|
|
41
32
|
```
|
|
42
|
-
$ npx maestro-bundle ai-agents
|
|
33
|
+
$ npx maestro-bundle ai-agents all
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
Bundle: Sistema Multi-Agente com AI
|
|
36
|
+
Editor: Claude Code, Cursor, OpenAI Codex, GitHub Copilot, Windsurf
|
|
46
37
|
|
|
47
|
-
✔
|
|
48
|
-
✔
|
|
49
|
-
✔
|
|
50
|
-
✔ .
|
|
51
|
-
✔
|
|
52
|
-
✔
|
|
53
|
-
✔ Spec Kit
|
|
38
|
+
✔ Claude Code: CLAUDE.md + AGENTS.md, 14 rules em .claude/rules/
|
|
39
|
+
✔ Cursor: .cursorrules, 14 rules em .cursor/rules/
|
|
40
|
+
✔ OpenAI Codex: AGENTS.md
|
|
41
|
+
✔ GitHub Copilot: .github/copilot-instructions.md, 14 rules em .github/instructions/
|
|
42
|
+
✔ Windsurf: .windsurfrules
|
|
43
|
+
✔ 14 skills canônicas
|
|
44
|
+
✔ .spec/constitution.md (GitHub Spec Kit)
|
|
45
|
+
✔ Spec Kit instalado
|
|
54
46
|
|
|
55
47
|
Pronto!
|
|
56
|
-
|
|
57
|
-
Arquivos instalados:
|
|
58
|
-
AGENTS.md Comportamento do agente (qualquer editor AI)
|
|
59
|
-
.spec/constitution.md Princípios do projeto (GitHub Spec Kit)
|
|
60
|
-
skills/ (14 skills) Capacidades do agente
|
|
61
|
-
references/ Documentos de referência
|
|
62
|
-
|
|
63
|
-
Próximos passos:
|
|
64
|
-
1. Abra o projeto no seu editor AI (Cursor, Claude Code, Copilot, etc.)
|
|
65
|
-
2. O agente já conhece os padrões do projeto
|
|
66
|
-
3. Para nova demanda: /speckit.specify
|
|
67
48
|
```
|
|
68
49
|
|
|
69
|
-
##
|
|
50
|
+
## Onde cada editor procura
|
|
70
51
|
|
|
71
52
|
```
|
|
72
53
|
seu-projeto/
|
|
73
|
-
├──
|
|
74
|
-
├── .
|
|
75
|
-
|
|
76
|
-
├──
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
├── CLAUDE.md ← Claude Code
|
|
55
|
+
├── AGENTS.md ← Codex, agents.md universal
|
|
56
|
+
├── .cursorrules ← Cursor
|
|
57
|
+
├── .windsurfrules ← Windsurf
|
|
58
|
+
├── .claude/rules/*.md ← Claude Code (skills como rules)
|
|
59
|
+
├── .cursor/rules/*.md ← Cursor (skills como rules)
|
|
60
|
+
├── .github/copilot-instructions.md ← Copilot
|
|
61
|
+
├── .github/instructions/*.instructions.md ← Copilot (skills)
|
|
62
|
+
├── .spec/constitution.md ← GitHub Spec Kit (SDD)
|
|
63
|
+
├── skills/ ← Canônicas (Deep Agents, referência)
|
|
64
|
+
│ ├── rag-pipeline/SKILL.md
|
|
65
|
+
│ ├── clean-architecture/SKILL.md
|
|
81
66
|
│ └── ...
|
|
82
|
-
|
|
83
|
-
│ └── ...
|
|
84
|
-
└── src/
|
|
67
|
+
└── references/ ← Docs sob demanda
|
|
85
68
|
```
|
|
86
69
|
|
|
87
|
-
##
|
|
70
|
+
## Uso avançado
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Instalar em diretório específico
|
|
74
|
+
npx maestro-bundle jhipster-monorepo cursor ./meu-projeto
|
|
75
|
+
|
|
76
|
+
# Ver bundles e editores disponíveis
|
|
77
|
+
npx maestro-bundle --help
|
|
78
|
+
```
|
|
88
79
|
|
|
89
|
-
|
|
80
|
+
## Links
|
|
90
81
|
|
|
91
|
-
|
|
82
|
+
- [AGENTS.md spec](https://agents.md/) — Padrão universal para instruções de agentes
|
|
83
|
+
- [GitHub Spec Kit](https://github.com/github/spec-kit) — SDD (Specification-Driven Development)
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -9,183 +9,398 @@ import ora from "ora";
|
|
|
9
9
|
|
|
10
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
|
|
12
|
+
// ============================================================
|
|
13
|
+
// BUNDLES
|
|
14
|
+
// ============================================================
|
|
12
15
|
const BUNDLES = {
|
|
13
16
|
"ai-agents": {
|
|
14
17
|
name: "Sistema Multi-Agente com AI",
|
|
15
|
-
|
|
18
|
+
desc: "Python + LangChain + LangGraph + FastAPI + pgvector",
|
|
16
19
|
},
|
|
17
20
|
"jhipster-monorepo": {
|
|
18
21
|
name: "JHipster Monorepo",
|
|
19
|
-
|
|
22
|
+
desc: "Java 21 + Spring Boot + Angular + PostgreSQL + Liquibase",
|
|
20
23
|
},
|
|
21
24
|
"jhipster-microservices": {
|
|
22
25
|
name: "JHipster Microservices",
|
|
23
|
-
|
|
26
|
+
desc: "Java 21 + Spring Boot + Angular + Kafka + Consul + K8s",
|
|
24
27
|
},
|
|
25
28
|
"data-pipeline": {
|
|
26
29
|
name: "Pipeline de Dados e ML",
|
|
27
|
-
|
|
30
|
+
desc: "Python + Pandas + Scikit-learn + MLflow + Airflow",
|
|
28
31
|
},
|
|
29
32
|
"frontend-spa": {
|
|
30
33
|
name: "Frontend SPA",
|
|
31
|
-
|
|
34
|
+
desc: "React + TypeScript + Tailwind + Vite",
|
|
32
35
|
},
|
|
33
36
|
};
|
|
34
37
|
|
|
38
|
+
// ============================================================
|
|
39
|
+
// EDITORS — paths corretos para cada editor
|
|
40
|
+
//
|
|
41
|
+
// Claude Code:
|
|
42
|
+
// Instruções: CLAUDE.md (raiz) com @AGENTS.md
|
|
43
|
+
// Skills: .claude/skills/<nome>/SKILL.md
|
|
44
|
+
// Rules: .claude/rules/<nome>.md
|
|
45
|
+
//
|
|
46
|
+
// Cursor:
|
|
47
|
+
// Instruções: .cursor/rules/ (rules .mdc ou .md)
|
|
48
|
+
// Também lê: AGENTS.md (raiz)
|
|
49
|
+
//
|
|
50
|
+
// Codex (OpenAI):
|
|
51
|
+
// Instruções: AGENTS.md (raiz)
|
|
52
|
+
// Sem pasta de skills/rules
|
|
53
|
+
//
|
|
54
|
+
// Copilot:
|
|
55
|
+
// Instruções: .github/copilot-instructions.md
|
|
56
|
+
// Rules: .github/instructions/<nome>.instructions.md
|
|
57
|
+
//
|
|
58
|
+
// Windsurf:
|
|
59
|
+
// Instruções: .windsurfrules (raiz)
|
|
60
|
+
// Sem pasta de skills/rules
|
|
61
|
+
// ============================================================
|
|
62
|
+
const EDITORS = {
|
|
63
|
+
claude: {
|
|
64
|
+
name: "Claude Code",
|
|
65
|
+
instructionsFile: "CLAUDE.md",
|
|
66
|
+
skillsDir: ".claude/skills", // Skills como SKILL.md com frontmatter
|
|
67
|
+
rulesDir: ".claude/rules", // Rules curtas (padrões gerais)
|
|
68
|
+
needsAgentsMd: true, // CLAUDE.md faz @AGENTS.md
|
|
69
|
+
},
|
|
70
|
+
cursor: {
|
|
71
|
+
name: "Cursor",
|
|
72
|
+
instructionsFile: null, // Cursor lê AGENTS.md na raiz
|
|
73
|
+
skillsDir: ".cursor/skills", // Skills como SKILL.md (mesmo formato do Claude)
|
|
74
|
+
rulesDir: null, // Não usar rules, usar skills
|
|
75
|
+
needsAgentsMd: true, // Cursor lê AGENTS.md na raiz
|
|
76
|
+
},
|
|
77
|
+
codex: {
|
|
78
|
+
name: "OpenAI Codex",
|
|
79
|
+
instructionsFile: "AGENTS.md",
|
|
80
|
+
skillsDir: null,
|
|
81
|
+
rulesDir: null,
|
|
82
|
+
needsAgentsMd: false, // AGENTS.md É o arquivo principal
|
|
83
|
+
},
|
|
84
|
+
copilot: {
|
|
85
|
+
name: "GitHub Copilot",
|
|
86
|
+
instructionsFile: ".github/copilot-instructions.md",
|
|
87
|
+
skillsDir: null,
|
|
88
|
+
rulesDir: ".github/instructions", // .instructions.md com frontmatter
|
|
89
|
+
needsAgentsMd: false,
|
|
90
|
+
},
|
|
91
|
+
windsurf: {
|
|
92
|
+
name: "Windsurf",
|
|
93
|
+
instructionsFile: ".windsurfrules",
|
|
94
|
+
skillsDir: null,
|
|
95
|
+
rulesDir: null,
|
|
96
|
+
needsAgentsMd: false,
|
|
97
|
+
},
|
|
98
|
+
all: {
|
|
99
|
+
name: "Todos os editores",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// ============================================================
|
|
104
|
+
// HELP
|
|
105
|
+
// ============================================================
|
|
35
106
|
function showHelp() {
|
|
36
107
|
console.log("");
|
|
37
108
|
console.log(chalk.bold(" maestro-bundle") + " — Instala bundles de governança para projetos com AI");
|
|
38
109
|
console.log("");
|
|
39
110
|
console.log(chalk.dim(" Uso:"));
|
|
40
|
-
console.log(` npx maestro-bundle ${chalk.green("<bundle>")} ${chalk.dim("[diretório]")}`);
|
|
41
|
-
console.log("");
|
|
42
|
-
console.log(chalk.dim(" Bundles disponíveis:"));
|
|
111
|
+
console.log(` npx maestro-bundle ${chalk.green("<bundle>")} ${chalk.yellow("<editor>")} ${chalk.dim("[diretório]")}`);
|
|
43
112
|
console.log("");
|
|
113
|
+
console.log(chalk.dim(" Bundles:"));
|
|
44
114
|
for (const [key, info] of Object.entries(BUNDLES)) {
|
|
45
|
-
console.log(` ${chalk.green(key.padEnd(26))} ${info.
|
|
46
|
-
console.log(` ${"".padEnd(26)} ${chalk.dim(info.description)}`);
|
|
47
|
-
console.log("");
|
|
115
|
+
console.log(` ${chalk.green(key.padEnd(26))} ${info.desc}`);
|
|
48
116
|
}
|
|
117
|
+
console.log("");
|
|
118
|
+
console.log(chalk.dim(" Editores:"));
|
|
119
|
+
console.log(` ${chalk.yellow("claude".padEnd(12))} CLAUDE.md + .claude/skills/<nome>/SKILL.md`);
|
|
120
|
+
console.log(` ${chalk.yellow("cursor".padEnd(12))} AGENTS.md + .cursor/skills/<nome>/SKILL.md`);
|
|
121
|
+
console.log(` ${chalk.yellow("codex".padEnd(12))} AGENTS.md (tudo em um arquivo)`);
|
|
122
|
+
console.log(` ${chalk.yellow("copilot".padEnd(12))} .github/copilot-instructions.md + .github/instructions/`);
|
|
123
|
+
console.log(` ${chalk.yellow("windsurf".padEnd(12))} .windsurfrules (tudo em um arquivo)`);
|
|
124
|
+
console.log(` ${chalk.yellow("all".padEnd(12))} Instala para todos os editores no mesmo repo`);
|
|
125
|
+
console.log("");
|
|
49
126
|
console.log(chalk.dim(" Exemplos:"));
|
|
50
|
-
console.log(` npx maestro-bundle ai-agents`);
|
|
51
|
-
console.log(` npx maestro-bundle jhipster-monorepo ./meu-projeto`);
|
|
52
|
-
console.log(` npx maestro-bundle frontend-spa
|
|
127
|
+
console.log(` npx maestro-bundle ai-agents claude`);
|
|
128
|
+
console.log(` npx maestro-bundle jhipster-monorepo cursor ./meu-projeto`);
|
|
129
|
+
console.log(` npx maestro-bundle frontend-spa all`);
|
|
53
130
|
console.log("");
|
|
54
131
|
}
|
|
55
132
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
133
|
+
// ============================================================
|
|
134
|
+
// UTILS
|
|
135
|
+
// ============================================================
|
|
136
|
+
function ensureDir(dir) { mkdirSync(dir, { recursive: true }); }
|
|
137
|
+
function copyDir(src, dest) { if (existsSync(src)) cpSync(src, dest, { recursive: true }); }
|
|
138
|
+
function readFile(path) { return existsSync(path) ? readFileSync(path, "utf-8") : ""; }
|
|
139
|
+
|
|
140
|
+
function getSkillDirs(templatesDir, bundleName) {
|
|
141
|
+
const baseSkills = join(templatesDir, "bundle-base", "skills");
|
|
142
|
+
const bundleSkills = join(templatesDir, `bundle-${bundleName}`, "skills");
|
|
143
|
+
return [baseSkills, bundleSkills].filter(existsSync);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function listSkills(skillDirs) {
|
|
147
|
+
const skills = [];
|
|
148
|
+
for (const dir of skillDirs) {
|
|
149
|
+
for (const item of readdirSync(dir, { withFileTypes: true })) {
|
|
150
|
+
if (item.isDirectory() && existsSync(join(dir, item.name, "SKILL.md"))) {
|
|
151
|
+
skills.push({ name: item.name, dir: join(dir, item.name) });
|
|
152
|
+
}
|
|
63
153
|
}
|
|
64
154
|
}
|
|
65
|
-
return
|
|
155
|
+
return skills;
|
|
66
156
|
}
|
|
67
157
|
|
|
158
|
+
// ============================================================
|
|
159
|
+
// INSTALL PER EDITOR
|
|
160
|
+
// ============================================================
|
|
161
|
+
function installForEditor(editorKey, agentsMd, skills, targetDir) {
|
|
162
|
+
const editor = EDITORS[editorKey];
|
|
163
|
+
const installed = [];
|
|
164
|
+
|
|
165
|
+
// --- AGENTS.md (universal) ---
|
|
166
|
+
if (editor.needsAgentsMd || editorKey === "codex") {
|
|
167
|
+
writeFileSync(join(targetDir, "AGENTS.md"), agentsMd);
|
|
168
|
+
installed.push("AGENTS.md");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- Arquivo de instruções principal ---
|
|
172
|
+
if (editor.instructionsFile) {
|
|
173
|
+
const filePath = join(targetDir, editor.instructionsFile);
|
|
174
|
+
ensureDir(dirname(filePath));
|
|
175
|
+
|
|
176
|
+
if (editorKey === "claude") {
|
|
177
|
+
// CLAUDE.md aponta para @AGENTS.md (sem duplicar conteúdo)
|
|
178
|
+
writeFileSync(filePath, "@AGENTS.md\n");
|
|
179
|
+
installed.push("CLAUDE.md → @AGENTS.md");
|
|
180
|
+
} else if (editorKey === "codex") {
|
|
181
|
+
// AGENTS.md já foi escrito acima
|
|
182
|
+
} else {
|
|
183
|
+
// Copilot, Windsurf: copiar conteúdo completo
|
|
184
|
+
writeFileSync(filePath, agentsMd);
|
|
185
|
+
installed.push(editor.instructionsFile);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// --- Skills (Claude Code: .claude/skills/<nome>/SKILL.md) ---
|
|
190
|
+
if (editor.skillsDir) {
|
|
191
|
+
const skillsPath = join(targetDir, editor.skillsDir);
|
|
192
|
+
ensureDir(skillsPath);
|
|
193
|
+
for (const skill of skills) {
|
|
194
|
+
const destDir = join(skillsPath, skill.name);
|
|
195
|
+
copyDir(skill.dir, destDir);
|
|
196
|
+
}
|
|
197
|
+
installed.push(`${skills.length} skills em ${editor.skillsDir}/`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// --- Rules por editor ---
|
|
201
|
+
if (editor.rulesDir) {
|
|
202
|
+
const rulesPath = join(targetDir, editor.rulesDir);
|
|
203
|
+
ensureDir(rulesPath);
|
|
204
|
+
|
|
205
|
+
for (const skill of skills) {
|
|
206
|
+
const content = readFileSync(join(skill.dir, "SKILL.md"), "utf-8");
|
|
207
|
+
|
|
208
|
+
if (editorKey === "copilot") {
|
|
209
|
+
// Copilot: .instructions.md
|
|
210
|
+
writeFileSync(join(rulesPath, `${skill.name}.instructions.md`), content);
|
|
211
|
+
|
|
212
|
+
} else if (editorKey === "claude") {
|
|
213
|
+
// Claude rules: regras curtas derivadas do AGENTS.md
|
|
214
|
+
// Skills já vão em .claude/skills/, rules é só para padrões gerais
|
|
215
|
+
// Não duplicar skills como rules
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (editorKey !== "claude") {
|
|
220
|
+
const ruleFiles = readdirSync(rulesPath).length;
|
|
221
|
+
installed.push(`${ruleFiles} rules em ${editor.rulesDir}/`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return installed;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function parseFrontmatter(content) {
|
|
229
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
230
|
+
if (!match) return { description: "", body: content };
|
|
231
|
+
|
|
232
|
+
const fm = match[1];
|
|
233
|
+
const body = match[2];
|
|
234
|
+
const descMatch = fm.match(/description:\s*(.+)/);
|
|
235
|
+
const description = descMatch ? descMatch[1].trim() : "";
|
|
236
|
+
return { description, body };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ============================================================
|
|
240
|
+
// MAIN
|
|
241
|
+
// ============================================================
|
|
68
242
|
async function main() {
|
|
69
243
|
const args = process.argv.slice(2);
|
|
70
244
|
|
|
71
|
-
if (args.length
|
|
245
|
+
if (args.length < 2 || args.includes("--help") || args.includes("-h")) {
|
|
72
246
|
showHelp();
|
|
73
|
-
process.exit(0);
|
|
247
|
+
process.exit(args.length < 2 && !args.includes("--help") ? 1 : 0);
|
|
74
248
|
}
|
|
75
249
|
|
|
76
250
|
const bundleName = args[0];
|
|
77
|
-
const
|
|
251
|
+
const editorArg = args[1];
|
|
252
|
+
const targetDir = resolve(args[2] || ".");
|
|
78
253
|
|
|
79
254
|
if (!BUNDLES[bundleName]) {
|
|
80
255
|
console.error(chalk.red(`\n Bundle "${bundleName}" não encontrado.\n`));
|
|
81
256
|
showHelp();
|
|
82
257
|
process.exit(1);
|
|
83
258
|
}
|
|
259
|
+
if (!EDITORS[editorArg]) {
|
|
260
|
+
console.error(chalk.red(`\n Editor "${editorArg}" não encontrado.\n`));
|
|
261
|
+
showHelp();
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
84
264
|
|
|
85
|
-
const
|
|
265
|
+
const bundleInfo = BUNDLES[bundleName];
|
|
86
266
|
const templatesDir = join(__dirname, "..", "templates");
|
|
87
267
|
const baseDir = join(templatesDir, "bundle-base");
|
|
88
268
|
const bundleDir = join(templatesDir, `bundle-${bundleName}`);
|
|
89
269
|
|
|
90
270
|
if (!existsSync(bundleDir)) {
|
|
91
|
-
console.error(chalk.red(`\n Templates
|
|
271
|
+
console.error(chalk.red(`\n Templates de "${bundleName}" não encontrados.\n`));
|
|
92
272
|
process.exit(1);
|
|
93
273
|
}
|
|
94
274
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
275
|
+
// Montar AGENTS.md (base + bundle)
|
|
276
|
+
let agentsMd = readFile(join(baseDir, "AGENTS.md"));
|
|
277
|
+
const bundleAgents = readFile(join(bundleDir, "AGENTS.md"));
|
|
278
|
+
if (bundleAgents) agentsMd += "\n\n---\n\n" + bundleAgents;
|
|
99
279
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
mkdirSync(join(targetDir, dir), { recursive: true });
|
|
104
|
-
}
|
|
105
|
-
spinner.succeed("Estrutura criada");
|
|
280
|
+
// Listar skills
|
|
281
|
+
const skillDirs = getSkillDirs(templatesDir, bundleName);
|
|
282
|
+
const skills = listSkills(skillDirs);
|
|
106
283
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const bundleAgentsPath = join(bundleDir, "AGENTS.md");
|
|
284
|
+
// Editores a instalar
|
|
285
|
+
const editorsToInstall = editorArg === "all"
|
|
286
|
+
? ["claude", "cursor", "codex", "copilot", "windsurf"]
|
|
287
|
+
: [editorArg];
|
|
112
288
|
|
|
113
|
-
|
|
114
|
-
agentsMd += readFileSync(baseAgentsPath, "utf-8");
|
|
115
|
-
}
|
|
116
|
-
if (existsSync(bundleAgentsPath)) {
|
|
117
|
-
agentsMd += "\n\n---\n\n";
|
|
118
|
-
agentsMd += readFileSync(bundleAgentsPath, "utf-8");
|
|
119
|
-
}
|
|
120
|
-
writeFileSync(join(targetDir, "AGENTS.md"), agentsMd);
|
|
121
|
-
spinner.succeed("AGENTS.md instalado na raiz");
|
|
289
|
+
const editorNames = editorsToInstall.map(e => EDITORS[e].name).join(", ");
|
|
122
290
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
291
|
+
console.log("");
|
|
292
|
+
console.log(chalk.bold(` Bundle: ${chalk.green(bundleInfo.name)}`));
|
|
293
|
+
console.log(chalk.bold(` Editor: ${chalk.yellow(editorNames)}`));
|
|
294
|
+
console.log(chalk.dim(` Destino: ${targetDir}`));
|
|
295
|
+
console.log("");
|
|
296
|
+
|
|
297
|
+
// 1. Instalar para cada editor
|
|
298
|
+
for (const editorKey of editorsToInstall) {
|
|
299
|
+
const spinner = ora(`Instalando para ${EDITORS[editorKey].name}`).start();
|
|
300
|
+
const results = installForEditor(editorKey, agentsMd, skills, targetDir);
|
|
301
|
+
spinner.succeed(`${EDITORS[editorKey].name}: ${results.join(", ")}`);
|
|
131
302
|
}
|
|
132
|
-
const skillCount = countSkills(skillsDir);
|
|
133
|
-
spinner.succeed(`${skillCount} skills instaladas`);
|
|
134
303
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
304
|
+
// 2. Skills canônicas (sempre, para Deep Agents e referência)
|
|
305
|
+
const spinner2 = ora("Instalando skills canônicas").start();
|
|
306
|
+
const skillsDest = join(targetDir, "skills");
|
|
307
|
+
ensureDir(skillsDest);
|
|
308
|
+
for (const skill of skills) {
|
|
309
|
+
copyDir(skill.dir, join(skillsDest, skill.name));
|
|
139
310
|
}
|
|
140
|
-
|
|
311
|
+
spinner2.succeed(`${skills.length} skills canônicas em skills/`);
|
|
141
312
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
313
|
+
// 3. References
|
|
314
|
+
const spinner3 = ora("Instalando references").start();
|
|
315
|
+
const refsSrc = join(bundleDir, "references");
|
|
316
|
+
ensureDir(join(targetDir, "references"));
|
|
317
|
+
if (existsSync(refsSrc)) {
|
|
318
|
+
copyDir(refsSrc, join(targetDir, "references"));
|
|
146
319
|
}
|
|
147
|
-
|
|
320
|
+
spinner3.succeed("references/ pronto");
|
|
321
|
+
|
|
322
|
+
// 4. GitHub Spec Kit — instalar CLI + inicializar no projeto
|
|
323
|
+
// Mapear editor para flag --ai do specify
|
|
324
|
+
const aiFlags = {
|
|
325
|
+
claude: "claude",
|
|
326
|
+
cursor: "cursor",
|
|
327
|
+
codex: "codex",
|
|
328
|
+
copilot: "copilot",
|
|
329
|
+
windsurf: "windsurf",
|
|
330
|
+
};
|
|
331
|
+
// Usar o primeiro editor como --ai (ou claude como default)
|
|
332
|
+
const primaryEditor = editorsToInstall[0];
|
|
333
|
+
const aiFlag = aiFlags[primaryEditor] || "claude";
|
|
148
334
|
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
let
|
|
335
|
+
// 4a. Instalar specify-cli se não tiver
|
|
336
|
+
const spinner4 = ora("Verificando GitHub Spec Kit (specify-cli)").start();
|
|
337
|
+
let specifyInstalled = false;
|
|
152
338
|
try {
|
|
153
339
|
execSync("specify --version", { stdio: "ignore" });
|
|
154
|
-
|
|
155
|
-
|
|
340
|
+
specifyInstalled = true;
|
|
341
|
+
spinner4.succeed("specify-cli já instalado");
|
|
156
342
|
} catch {
|
|
157
|
-
|
|
158
|
-
|
|
343
|
+
spinner4.info("specify-cli não encontrado");
|
|
344
|
+
const spinner4b = ora("Instalando specify-cli...").start();
|
|
159
345
|
try {
|
|
160
|
-
execSync(
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
spinner.succeed("Spec Kit instalado");
|
|
346
|
+
execSync("uv tool install specify-cli --from git+https://github.com/github/spec-kit.git", {
|
|
347
|
+
stdio: "ignore", timeout: 120000,
|
|
348
|
+
});
|
|
349
|
+
specifyInstalled = true;
|
|
350
|
+
spinner4b.succeed("specify-cli instalado");
|
|
166
351
|
} catch {
|
|
167
352
|
try {
|
|
168
|
-
execSync(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
);
|
|
172
|
-
specKitInstalled = true;
|
|
173
|
-
spinner.succeed("Spec Kit instalado via pip");
|
|
353
|
+
execSync("pip install specify-cli", { stdio: "ignore", timeout: 60000 });
|
|
354
|
+
specifyInstalled = true;
|
|
355
|
+
spinner4b.succeed("specify-cli instalado via pip");
|
|
174
356
|
} catch {
|
|
175
|
-
|
|
357
|
+
spinner4b.warn("Não foi possível instalar. Instale manualmente:");
|
|
176
358
|
console.log(chalk.dim(" uv tool install specify-cli --from git+https://github.com/github/spec-kit.git"));
|
|
177
359
|
}
|
|
178
360
|
}
|
|
179
361
|
}
|
|
180
362
|
|
|
181
|
-
//
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
363
|
+
// 4b. Rodar specify init no projeto para criar .specify/ e registrar /speckit.* commands
|
|
364
|
+
if (specifyInstalled) {
|
|
365
|
+
const spinner4c = ora(`Inicializando Spec Kit no projeto (--ai ${aiFlag})`).start();
|
|
366
|
+
const specifyEnv = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" };
|
|
367
|
+
let specInitOk = false;
|
|
368
|
+
const initCmds = [
|
|
369
|
+
`specify init --here --ai ${aiFlag}`,
|
|
370
|
+
`specify init . --ai ${aiFlag}`,
|
|
371
|
+
`specify init --here --ai ${aiFlag} --no-git`,
|
|
372
|
+
];
|
|
373
|
+
for (const cmd of initCmds) {
|
|
374
|
+
try {
|
|
375
|
+
execSync(cmd, { stdio: "ignore", timeout: 30000, cwd: targetDir, env: specifyEnv });
|
|
376
|
+
specInitOk = true;
|
|
377
|
+
break;
|
|
378
|
+
} catch { /* try next */ }
|
|
379
|
+
}
|
|
380
|
+
if (specInitOk) {
|
|
381
|
+
spinner4c.succeed(`Spec Kit inicializado (/speckit.* commands disponíveis)`);
|
|
382
|
+
} else {
|
|
383
|
+
spinner4c.warn("Inicialize manualmente no terminal:");
|
|
384
|
+
console.log(chalk.dim(` cd ${targetDir}`));
|
|
385
|
+
console.log(chalk.dim(` specify init --here --ai ${aiFlag}`));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// 4c. Copiar constitution.md do bundle para dentro do .specify/memory/
|
|
389
|
+
const specifyMemoryDir = join(targetDir, ".specify", "memory");
|
|
390
|
+
const bundleConstitution = join(bundleDir, ".spec", "constitution.md");
|
|
391
|
+
if (existsSync(bundleConstitution)) {
|
|
392
|
+
ensureDir(specifyMemoryDir);
|
|
393
|
+
const constitutionDest = join(specifyMemoryDir, "constitution.md");
|
|
394
|
+
// Append os princípios do bundle ao constitution gerado pelo specify
|
|
395
|
+
if (existsSync(constitutionDest)) {
|
|
396
|
+
const existing = readFileSync(constitutionDest, "utf-8");
|
|
397
|
+
const bundleContent = readFileSync(bundleConstitution, "utf-8");
|
|
398
|
+
writeFileSync(constitutionDest, existing + "\n\n---\n\n" + bundleContent);
|
|
399
|
+
} else {
|
|
400
|
+
cpSync(bundleConstitution, constitutionDest);
|
|
401
|
+
}
|
|
402
|
+
const spinner4d = ora("Constitution do bundle integrado ao Spec Kit").start();
|
|
403
|
+
spinner4d.succeed("Constitution do bundle integrado ao Spec Kit");
|
|
189
404
|
}
|
|
190
405
|
}
|
|
191
406
|
|
|
@@ -193,16 +408,38 @@ async function main() {
|
|
|
193
408
|
console.log("");
|
|
194
409
|
console.log(chalk.green.bold(" Pronto!"));
|
|
195
410
|
console.log("");
|
|
196
|
-
console.log("
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
411
|
+
console.log(" Estrutura instalada:");
|
|
412
|
+
|
|
413
|
+
for (const editorKey of editorsToInstall) {
|
|
414
|
+
const e = EDITORS[editorKey];
|
|
415
|
+
console.log(` ${chalk.yellow(e.name)}:`);
|
|
416
|
+
if (editorKey === "claude") {
|
|
417
|
+
console.log(` ${chalk.cyan("CLAUDE.md")} → @AGENTS.md`);
|
|
418
|
+
console.log(` ${chalk.cyan(".claude/skills/")} (${skills.length} skills com SKILL.md)`);
|
|
419
|
+
} else if (editorKey === "cursor") {
|
|
420
|
+
console.log(` ${chalk.cyan("AGENTS.md")} (instruções gerais)`);
|
|
421
|
+
console.log(` ${chalk.cyan(".cursor/skills/")} (${skills.length} skills com SKILL.md)`);
|
|
422
|
+
} else if (editorKey === "codex") {
|
|
423
|
+
console.log(` ${chalk.cyan("AGENTS.md")} (tudo em um arquivo)`);
|
|
424
|
+
} else if (editorKey === "copilot") {
|
|
425
|
+
console.log(` ${chalk.cyan(".github/copilot-instructions.md")}`);
|
|
426
|
+
console.log(` ${chalk.cyan(".github/instructions/")} (${skills.length} .instructions.md)`);
|
|
427
|
+
} else if (editorKey === "windsurf") {
|
|
428
|
+
console.log(` ${chalk.cyan(".windsurfrules")}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
console.log(` ${chalk.cyan("skills/")} (${skills.length} canônicas para Deep Agents)`);
|
|
432
|
+
console.log(` ${chalk.cyan(".specify/")} (GitHub Spec Kit — /speckit.* commands)`);
|
|
433
|
+
console.log("");
|
|
434
|
+
console.log(" Comandos SDD disponíveis no editor:");
|
|
435
|
+
console.log(` ${chalk.cyan("/speckit.constitution")} — Definir princípios do projeto`);
|
|
436
|
+
console.log(` ${chalk.cyan("/speckit.specify")} — Especificar O QUE e POR QUÊ`);
|
|
437
|
+
console.log(` ${chalk.cyan("/speckit.plan")} — Planejar arquitetura e stack`);
|
|
438
|
+
console.log(` ${chalk.cyan("/speckit.tasks")} — Quebrar em tasks atômicas`);
|
|
439
|
+
console.log(` ${chalk.cyan("/speckit.implement")} — Executar as tasks`);
|
|
201
440
|
console.log("");
|
|
202
|
-
console.log("
|
|
203
|
-
console.log(
|
|
204
|
-
console.log(" 2. O agente já conhece os padrões do projeto");
|
|
205
|
-
console.log(` 3. Para nova demanda: ${chalk.cyan("/speckit.specify")}`);
|
|
441
|
+
console.log(" Próximo passo:");
|
|
442
|
+
console.log(" Abra o projeto no editor AI e use " + chalk.cyan("/speckit.specify") + " para começar");
|
|
206
443
|
console.log("");
|
|
207
444
|
}
|
|
208
445
|
|