aiteam-x 0.8.1 → 0.9.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 (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/cli.mjs +101 -45
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiteam-x",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Painel pixel art para orquestrar agentes BMAD em tempo real",
5
5
  "keywords": [
6
6
  "bmad",
package/scripts/cli.mjs CHANGED
@@ -3,83 +3,140 @@
3
3
  * AITEAM-X CLI — Instalação simplificada via npx
4
4
  *
5
5
  * Uso:
6
- * npx aiteam-x@latest → cria no diretório atual
6
+ * npx aiteam-x@latest → instala no diretório atual
7
7
  * npx aiteam-x@latest meu-projeto → cria em meu-projeto/
8
8
  *
9
- * Fluxo: degit (baixa template) → npm install → npm run setup → npm run dev
9
+ * Fluxo: degit (baixa template) → mescla arquivos → npm install → npm run setup → npm run dev
10
10
  */
11
11
 
12
12
  import { execSync, spawn } from "child_process";
13
13
  import fs from "fs";
14
14
  import path from "path";
15
+ import os from "os";
16
+ import readline from "readline";
15
17
  import { fileURLToPath } from "url";
16
18
 
17
19
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
20
  const REPO = "INOSX/AITeam";
19
21
 
20
22
  const C = {
21
- green: (s) => `\x1b[32m${s}\x1b[0m`,
23
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
22
24
  yellow: (s) => `\x1b[33m${s}\x1b[0m`,
23
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
24
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
25
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
25
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
26
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
27
+ dim: (s) => `\x1b[2m${s}\x1b[0m`,
26
28
  };
27
29
 
28
30
  function isDirEmpty(dir) {
29
31
  if (!fs.existsSync(dir)) return true;
30
- const entries = fs.readdirSync(dir);
31
- return entries.length === 0;
32
+ return fs.readdirSync(dir).length === 0;
32
33
  }
33
34
 
34
- function main() {
35
+ function detectBmad(dir) {
36
+ return (
37
+ fs.existsSync(path.join(dir, "bmad")) ||
38
+ fs.existsSync(path.join(dir, ".cursor", "rules", "bmad"))
39
+ );
40
+ }
41
+
42
+ function ask(question) {
43
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
44
+ return new Promise((resolve) => {
45
+ rl.question(question, (answer) => {
46
+ rl.close();
47
+ resolve(answer.trim().toLowerCase());
48
+ });
49
+ });
50
+ }
51
+
52
+ function mergePackageJson(templatePath, targetPath) {
53
+ const tpl = JSON.parse(fs.readFileSync(templatePath, "utf-8"));
54
+ const cur = JSON.parse(fs.readFileSync(targetPath, "utf-8"));
55
+ const merged = {
56
+ ...cur,
57
+ dependencies: { ...(tpl.dependencies ?? {}), ...(cur.dependencies ?? {}) },
58
+ devDependencies: { ...(tpl.devDependencies ?? {}), ...(cur.devDependencies ?? {}) },
59
+ scripts: { ...(tpl.scripts ?? {}), ...(cur.scripts ?? {}) },
60
+ };
61
+ if (tpl.bin && !cur.bin) merged.bin = tpl.bin;
62
+ fs.writeFileSync(targetPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
63
+ console.log(C.dim(" package.json mesclado (deps + scripts adicionados)"));
64
+ }
65
+
66
+ function copyMerge(src, dest) {
67
+ const entries = fs.readdirSync(src, { withFileTypes: true });
68
+ for (const entry of entries) {
69
+ if (entry.name === ".git") continue;
70
+ if (entry.name === "node_modules") continue;
71
+ const srcPath = path.join(src, entry.name);
72
+ const destPath = path.join(dest, entry.name);
73
+ if (entry.isDirectory()) {
74
+ fs.mkdirSync(destPath, { recursive: true });
75
+ copyMerge(srcPath, destPath);
76
+ } else if (entry.name === "package.json" && fs.existsSync(destPath)) {
77
+ mergePackageJson(srcPath, destPath);
78
+ } else if (!fs.existsSync(destPath)) {
79
+ fs.copyFileSync(srcPath, destPath);
80
+ }
81
+ }
82
+ }
83
+
84
+ async function main() {
35
85
  const targetDir = process.argv[2] || ".";
36
- const cwd = process.cwd();
86
+ const cwd = process.cwd();
37
87
  const absTarget = path.resolve(cwd, targetDir);
38
88
 
39
89
  console.log("\n" + C.bold(C.cyan("╔══════════════════════════════════╗")));
40
- console.log(C.bold(C.cyan("║ AITEAM-X — Instalação rápida ║")));
90
+ console.log(C.bold(C.cyan("║ AITEAM-X — Instalação rápida ║")));
41
91
  console.log(C.bold(C.cyan("╚══════════════════════════════════╝")) + "\n");
42
92
 
43
- // Se diretório atual, verificar se está vazio
44
- if (targetDir === "." && !isDirEmpty(cwd)) {
45
- console.log(C.yellow(" O diretório atual não está vazio."));
46
- console.log(C.dim(" Use: npx aiteam-x@latest nome-do-projeto"));
47
- console.log(C.dim(" Ou crie uma pasta vazia e execute o comando dentro dela.\n"));
48
- process.exit(1);
93
+ const empty = isDirEmpty(absTarget);
94
+ const hasBmad = !empty && detectBmad(absTarget);
95
+
96
+ if (!empty) {
97
+ if (hasBmad) {
98
+ console.log(C.green(" Projeto BMAD detectado!"));
99
+ console.log(C.dim(" Os agentes existentes serão reconhecidos automaticamente."));
100
+ console.log(C.dim(" Arquivos existentes não serão sobrescritos.\n"));
101
+ } else {
102
+ console.log(C.yellow(" Diretório não está vazio."));
103
+ console.log(C.dim(" Arquivos existentes não serão sobrescritos.\n"));
104
+ }
105
+ const answer = await ask(" Instalar AITEAM-X aqui? (s/N) ");
106
+ if (answer !== "s" && answer !== "sim") {
107
+ console.log(C.dim("\n Instalação cancelada.\n"));
108
+ process.exit(0);
109
+ }
110
+ console.log();
49
111
  }
50
112
 
51
- if (targetDir !== "." && fs.existsSync(absTarget) && !isDirEmpty(absTarget)) {
52
- console.log(C.yellow(` O diretório "${targetDir}" já existe e não está vazio.\n`));
53
- process.exit(1);
54
- }
113
+ fs.mkdirSync(absTarget, { recursive: true });
114
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "aiteam-x-"));
55
115
 
56
116
  try {
57
- // 1. Baixar template via degit
58
117
  console.log(C.bold("1. Baixando template AITEAM-X..."));
59
- execSync(`npx degit@latest ${REPO} ${targetDir}`, {
60
- stdio: "inherit",
61
- cwd,
62
- });
118
+ execSync(`npx degit@latest ${REPO} "${tmpDir}" --force`, { stdio: "inherit", cwd });
63
119
  console.log(C.green(" ✓ Template baixado\n"));
64
120
 
65
- // 2. npm install
66
- console.log(C.bold("2. Instalando dependências..."));
67
- execSync("npm install", {
68
- stdio: "inherit",
69
- cwd: absTarget,
70
- });
121
+ if (!empty) {
122
+ console.log(C.bold("2. Mesclando arquivos (existentes preservados)..."));
123
+ copyMerge(tmpDir, absTarget);
124
+ console.log(C.green(" ✓ Arquivos mesclados\n"));
125
+ } else {
126
+ copyMerge(tmpDir, absTarget);
127
+ }
128
+
129
+ const n = empty ? 2 : 3;
130
+
131
+ console.log(C.bold(`${n}. Instalando dependências...`));
132
+ execSync("npm install", { stdio: "inherit", cwd: absTarget });
71
133
  console.log(C.green(" ✓ Dependências instaladas\n"));
72
134
 
73
- // 3. npm run setup
74
- console.log(C.bold("3. Executando wizard de configuração..."));
75
- execSync("npm run setup", {
76
- stdio: "inherit",
77
- cwd: absTarget,
78
- });
135
+ console.log(C.bold(`${n + 1}. Executando wizard de configuração...`));
136
+ execSync("npm run setup", { stdio: "inherit", cwd: absTarget });
79
137
  console.log(C.green(" ✓ Configuração concluída\n"));
80
138
 
81
- // 4. Iniciar servidor
82
- console.log(C.bold("4. Iniciando servidor de desenvolvimento..."));
139
+ console.log(C.bold(`${n + 2}. Iniciando servidor de desenvolvimento...`));
83
140
  console.log(C.dim(" Acesse http://localhost:3000 quando estiver pronto.\n"));
84
141
 
85
142
  const child = spawn("npm", ["run", "dev"], {
@@ -87,19 +144,18 @@ function main() {
87
144
  cwd: absTarget,
88
145
  shell: true,
89
146
  });
90
-
91
147
  child.on("error", (err) => {
92
- console.error(C.yellow("Erro ao iniciar:"), err);
148
+ console.error(C.yellow("Erro ao iniciar servidor:"), err.message);
93
149
  process.exit(1);
94
150
  });
151
+ child.on("exit", (code) => process.exit(code ?? 0));
95
152
 
96
- child.on("exit", (code) => {
97
- process.exit(code ?? 0);
98
- });
99
153
  } catch (err) {
100
154
  if (err.status !== undefined) process.exit(err.status);
101
155
  console.error("\n" + C.yellow("Erro:"), err.message);
102
156
  process.exit(1);
157
+ } finally {
158
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
103
159
  }
104
160
  }
105
161