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.
Files changed (3) hide show
  1. package/README.md +54 -62
  2. package/package.json +1 -1
  3. 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
- Instala automaticamente:
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
- ## Bundles disponíveis
17
-
18
- ```bash
19
- npx maestro-bundle ai-agents # Python + LangChain + LangGraph + FastAPI
20
- npx maestro-bundle jhipster-monorepo # Java 21 + Spring Boot + Angular + PostgreSQL
21
- npx maestro-bundle jhipster-microservices # Java 21 + Spring Boot + Kafka + Consul + K8s
22
- npx maestro-bundle data-pipeline # Python + Pandas + Scikit-learn + MLflow
23
- npx maestro-bundle frontend-spa # React + TypeScript + Tailwind + Vite
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
- ## Uso
20
+ ## Bundles disponíveis
27
21
 
28
22
  ```bash
29
- # Instalar no diretório atual
30
- npx maestro-bundle ai-agents
31
-
32
- # Instalar em outro diretório
33
- npx maestro-bundle jhipster-monorepo ./meu-projeto
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
- Instalando: Sistema Multi-Agente com AI
45
- Destino: /home/user/meu-projeto
35
+ Bundle: Sistema Multi-Agente com AI
36
+ Editor: Claude Code, Cursor, OpenAI Codex, GitHub Copilot, Windsurf
46
37
 
47
- Estrutura criada
48
- AGENTS.md instalado na raiz
49
- 14 skills instaladas
50
- ✔ .spec/constitution.md instalado
51
- References instalados
52
- Spec Kit já disponível
53
- ✔ Spec Kit inicializado
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
- ## Resultado no projeto
50
+ ## Onde cada editor procura
70
51
 
71
52
  ```
72
53
  seu-projeto/
73
- ├── AGENTS.md # Lido por qualquer editor AI
74
- ├── .spec/
75
- │ └── constitution.md # Princípios (GitHub Spec Kit)
76
- ├── skills/
77
- ├── commit-pattern/SKILL.md # Do bundle-base
78
- ├── code-review/SKILL.md # Do bundle-base
79
- ├── rag-pipeline/SKILL.md # Do bundle escolhido
80
- ├── agent-orchestration/SKILL.md
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
- ├── references/
83
- │ └── ...
84
- └── src/
67
+ └── references/ ← Docs sob demanda
85
68
  ```
86
69
 
87
- ## Compatibilidade
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
- O `AGENTS.md` segue o padrão [agents.md](https://agents.md/) e funciona com 30+ editores AI.
80
+ ## Links
90
81
 
91
- O `.spec/` segue o padrão [GitHub Spec Kit](https://github.com/github/spec-kit) para SDD.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maestro-bundle",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Instala bundles de governança para projetos com AI agents. Um comando, tudo configurado.",
5
5
  "bin": {
6
6
  "maestro-bundle": "./src/cli.mjs"
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
- description: "Python + LangChain + LangGraph + FastAPI + pgvector",
18
+ desc: "Python + LangChain + LangGraph + FastAPI + pgvector",
16
19
  },
17
20
  "jhipster-monorepo": {
18
21
  name: "JHipster Monorepo",
19
- description: "Java 21 + Spring Boot + Angular + PostgreSQL + Liquibase",
22
+ desc: "Java 21 + Spring Boot + Angular + PostgreSQL + Liquibase",
20
23
  },
21
24
  "jhipster-microservices": {
22
25
  name: "JHipster Microservices",
23
- description: "Java 21 + Spring Boot + Angular + Kafka + Consul + K8s",
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
- description: "Python + Pandas + Scikit-learn + MLflow + Airflow",
30
+ desc: "Python + Pandas + Scikit-learn + MLflow + Airflow",
28
31
  },
29
32
  "frontend-spa": {
30
33
  name: "Frontend SPA",
31
- description: "React + TypeScript + Tailwind + Vite",
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.name}`);
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
- function countSkills(dir) {
57
- if (!existsSync(dir)) return 0;
58
- let count = 0;
59
- for (const item of readdirSync(dir, { withFileTypes: true })) {
60
- if (item.isDirectory()) {
61
- const skillFile = join(dir, item.name, "SKILL.md");
62
- if (existsSync(skillFile)) count++;
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 count;
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 === 0 || args.includes("--help") || args.includes("-h")) {
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 targetDir = resolve(args[1] || ".");
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 info = BUNDLES[bundleName];
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 do bundle "${bundleName}" não encontrados em ${bundleDir}\n`));
271
+ console.error(chalk.red(`\n Templates de "${bundleName}" não encontrados.\n`));
92
272
  process.exit(1);
93
273
  }
94
274
 
95
- console.log("");
96
- console.log(chalk.bold(` Instalando: ${chalk.green(info.name)}`));
97
- console.log(chalk.dim(` Destino: ${targetDir}`));
98
- console.log("");
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
- // 1. Criar diretórios
101
- let spinner = ora("Criando estrutura").start();
102
- for (const dir of ["skills", ".spec", "references"]) {
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
- // 2. AGENTS.md (base + bundle concatenados)
108
- spinner = ora("Instalando AGENTS.md").start();
109
- let agentsMd = "";
110
- const baseAgentsPath = join(baseDir, "AGENTS.md");
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
- if (existsSync(baseAgentsPath)) {
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
- // 3. Skills (base + bundle)
124
- spinner = ora("Instalando skills").start();
125
- const skillsDir = join(targetDir, "skills");
126
- if (existsSync(join(baseDir, "skills"))) {
127
- cpSync(join(baseDir, "skills"), skillsDir, { recursive: true });
128
- }
129
- if (existsSync(join(bundleDir, "skills"))) {
130
- cpSync(join(bundleDir, "skills"), skillsDir, { recursive: true });
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
- // 4. .spec/constitution.md
136
- spinner = ora("Instalando constitution (Spec Kit)").start();
137
- if (existsSync(join(bundleDir, ".spec"))) {
138
- cpSync(join(bundleDir, ".spec"), join(targetDir, ".spec"), { recursive: true });
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
- spinner.succeed(".spec/constitution.md instalado");
311
+ spinner2.succeed(`${skills.length} skills canônicas em skills/`);
141
312
 
142
- // 5. References
143
- spinner = ora("Instalando references").start();
144
- if (existsSync(join(bundleDir, "references"))) {
145
- cpSync(join(bundleDir, "references"), join(targetDir, "references"), { recursive: true });
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
- spinner.succeed("References instalados");
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
- // 6. GitHub Spec Kit
150
- spinner = ora("Verificando GitHub Spec Kit").start();
151
- let specKitInstalled = false;
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
- specKitInstalled = true;
155
- spinner.succeed("Spec Kit disponível");
340
+ specifyInstalled = true;
341
+ spinner4.succeed("specify-cliinstalado");
156
342
  } catch {
157
- spinner.info("Spec Kit não encontrado");
158
- spinner = ora("Instalando GitHub Spec Kit...").start();
343
+ spinner4.info("specify-cli não encontrado");
344
+ const spinner4b = ora("Instalando specify-cli...").start();
159
345
  try {
160
- execSync(
161
- "uv tool install specify-cli --from git+https://github.com/github/spec-kit.git",
162
- { stdio: "ignore", timeout: 120000 }
163
- );
164
- specKitInstalled = true;
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
- "pip install specify-cli",
170
- { stdio: "ignore", timeout: 60000 }
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
- spinner.warn("Instale o Spec Kit manualmente:");
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
- // 7. Inicializar Spec Kit
182
- if (specKitInstalled) {
183
- spinner = ora("Inicializando Spec Kit").start();
184
- try {
185
- execSync(`specify init "${targetDir}"`, { stdio: "ignore", timeout: 15000, cwd: targetDir });
186
- spinner.succeed("Spec Kit inicializado");
187
- } catch {
188
- spinner.info("Execute 'specify init .' no diretório do projeto");
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(" Arquivos instalados:");
197
- console.log(` ${chalk.cyan("AGENTS.md")} Comportamento do agente (qualquer editor AI)`);
198
- console.log(` ${chalk.cyan(".spec/constitution.md")} Princípios do projeto (GitHub Spec Kit)`);
199
- console.log(` ${chalk.cyan(`skills/ (${skillCount} skills)`)} Capacidades do agente`);
200
- console.log(` ${chalk.cyan("references/")} Documentos de referência`);
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(" Próximos passos:");
203
- console.log(` 1. Abra o projeto no seu editor AI ${chalk.dim("(Cursor, Claude Code, Copilot, etc.)")}`);
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