agentic-kdd 2.0.2 → 2.0.3

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 +3 -3
  2. package/src/init.js +192 -167
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "agentic-kdd",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "Autonomous development pipeline with Knowledge-Driven Development. Works with Cursor and Claude Code. Just type aa:",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
- "akdd": "bin/akdd.js"
7
+ "akdd": "./bin/akdd.js"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "node bin/akdd.js --version"
@@ -24,7 +24,7 @@
24
24
  "license": "MIT",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "git+https://github.com/Adrianlpz211/Agentic-KDD.git"
27
+ "url": "https://github.com/Adrianlpz211/Agentic-KDD.git"
28
28
  },
29
29
  "homepage": "https://github.com/Adrianlpz211/Agentic-KDD",
30
30
  "engines": {
package/src/init.js CHANGED
@@ -2,25 +2,19 @@
2
2
 
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
- const https = require('https');
6
5
  const { execSync } = require('child_process');
7
-
8
- // Chalk y ora en versiones CommonJS
9
6
  const chalk = require('chalk');
10
7
  const ora = require('ora');
11
8
  const inquirer = require('inquirer');
12
9
 
13
10
  const GITHUB_REPO = 'Adrianlpz211/Agentic-KDD';
14
- const GITHUB_API = `https://api.github.com/repos/${GITHUB_REPO}/tarball/main`;
15
11
  const TEMP_DIR = path.join(require('os').tmpdir(), 'agentic-kdd-download');
16
12
 
17
- // ── Detectar stack del proyecto ────────────────────────────────
13
+ // ── Detectar stack ─────────────────────────────────────────────
18
14
  function detectStack(projectPath) {
19
15
  const stack = {
20
- hasNode: false,
21
- hasPhp: false,
22
- hasPython: false,
23
16
  framework: '—',
17
+ language: '—',
24
18
  packageManager: 'npm',
25
19
  commands: {
26
20
  install: 'npm install',
@@ -31,162 +25,141 @@ function detectStack(projectPath) {
31
25
  }
32
26
  };
33
27
 
34
- // Node.js
35
28
  if (fs.existsSync(path.join(projectPath, 'package.json'))) {
36
- stack.hasNode = true;
37
29
  const pkg = fs.readJsonSync(path.join(projectPath, 'package.json'), { throws: false }) || {};
38
30
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
31
+ stack.language = 'TypeScript/JavaScript';
39
32
 
40
- // Package manager
41
33
  if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
42
34
  stack.packageManager = 'pnpm';
43
- stack.commands.install = 'pnpm install';
44
- stack.commands.dev = 'pnpm dev';
45
- stack.commands.build = 'pnpm build';
46
- stack.commands.test = 'pnpm test';
47
- stack.commands.lint = 'pnpm lint';
35
+ stack.commands = { install: 'pnpm install', dev: 'pnpm dev', build: 'pnpm build', test: 'pnpm test', lint: 'pnpm lint' };
48
36
  } else if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
49
37
  stack.packageManager = 'yarn';
50
- stack.commands.install = 'yarn install';
51
- stack.commands.dev = 'yarn dev';
52
- stack.commands.build = 'yarn build';
53
- stack.commands.test = 'yarn test';
54
- stack.commands.lint = 'yarn lint';
38
+ stack.commands = { install: 'yarn install', dev: 'yarn dev', build: 'yarn build', test: 'yarn test', lint: 'yarn lint' };
55
39
  }
56
40
 
57
- // Framework
58
- if (deps['next']) stack.framework = `Next.js ${deps['next'].replace('^', '')}`;
59
- else if (deps['react']) stack.framework = `React ${deps['react'].replace('^', '')}`;
60
- else if (deps['vue']) stack.framework = `Vue ${deps['vue'].replace('^', '')}`;
61
- else if (deps['express']) stack.framework = `Express ${deps['express'].replace('^', '')}`;
62
- else if (deps['fastify']) stack.framework = `Fastify`;
41
+ if (deps['next']) stack.framework = `Next.js ${deps['next'].replace('^','')}`;
42
+ else if (deps['vue']) stack.framework = `Vue ${deps['vue'].replace('^','')}`;
43
+ else if (deps['react']) stack.framework = `React`;
44
+ else if (deps['express']) stack.framework = `Express`;
63
45
  else if (deps['@nestjs/core']) stack.framework = `NestJS`;
64
46
  }
65
47
 
66
- // PHP
67
48
  if (fs.existsSync(path.join(projectPath, 'composer.json'))) {
68
- stack.hasPhp = true;
49
+ stack.language = 'PHP';
69
50
  stack.packageManager = 'composer';
70
- stack.commands.install = 'composer install';
71
- stack.commands.dev = 'php artisan serve';
72
- stack.commands.build = 'composer build';
73
- stack.commands.test = './vendor/bin/phpunit';
74
- stack.commands.lint = 'composer lint';
51
+ stack.commands = { install: 'composer install', dev: 'php artisan serve', build: 'composer build', test: './vendor/bin/phpunit', lint: 'composer lint' };
75
52
  const composer = fs.readJsonSync(path.join(projectPath, 'composer.json'), { throws: false }) || {};
76
- const require = composer.require || {};
77
- if (require['laravel/framework']) stack.framework = 'Laravel';
53
+ if ((composer.require || {})['laravel/framework']) stack.framework = 'Laravel';
54
+ else stack.framework = 'PHP';
78
55
  }
79
56
 
80
- // Python
81
57
  if (fs.existsSync(path.join(projectPath, 'pyproject.toml')) ||
82
58
  fs.existsSync(path.join(projectPath, 'requirements.txt'))) {
83
- stack.hasPython = true;
59
+ stack.language = 'Python';
84
60
  stack.packageManager = 'pip';
85
- stack.commands.install = 'pip install -r requirements.txt';
86
- stack.commands.dev = 'uvicorn main:app --reload';
87
- stack.commands.build = 'pip install -e .';
88
- stack.commands.test = 'pytest';
89
- stack.commands.lint = 'flake8';
61
+ stack.commands = { install: 'pip install -r requirements.txt', dev: 'uvicorn main:app --reload', build: 'pip install -e .', test: 'pytest', lint: 'flake8' };
62
+ stack.framework = 'Python';
90
63
  }
91
64
 
92
65
  return stack;
93
66
  }
94
67
 
95
- // ── Descargar archivos de Agentic KDD desde GitHub ────────────
96
- async function downloadFromGitHub(spinner) {
97
- return new Promise((resolve, reject) => {
98
- spinner.text = 'Downloading Agentic KDD from GitHub...';
99
-
100
- const tmpFile = path.join(require('os').tmpdir(), 'agentic-kdd.tar.gz');
101
-
102
- // Usar curl o wget si están disponibles (más confiable que https nativo)
103
- try {
104
- execSync(`curl -sL "https://github.com/${GITHUB_REPO}/archive/refs/heads/main.tar.gz" -o "${tmpFile}"`, {
105
- stdio: 'pipe'
106
- });
107
-
108
- // Extraer
109
- fs.ensureDirSync(TEMP_DIR);
110
- execSync(`tar -xzf "${tmpFile}" -C "${TEMP_DIR}" --strip-components=1`, {
111
- stdio: 'pipe'
112
- });
113
-
114
- fs.removeSync(tmpFile);
115
- resolve(TEMP_DIR);
116
- } catch (err) {
117
- reject(new Error('Could not download from GitHub. Check your internet connection.'));
118
- }
119
- });
68
+ // ── Detectar si el proyecto tiene contenido ────────────────────
69
+ function detectProjectState(projectPath) {
70
+ const hasCode = fs.existsSync(path.join(projectPath, 'src')) ||
71
+ fs.existsSync(path.join(projectPath, 'app')) ||
72
+ fs.existsSync(path.join(projectPath, 'pages')) ||
73
+ fs.existsSync(path.join(projectPath, 'api'));
74
+
75
+ const hasKnowledge = fs.existsSync(path.join(projectPath, '.agentic', 'conocimiento')) &&
76
+ fs.readdirSync(path.join(projectPath, '.agentic', 'conocimiento'))
77
+ .filter(f => f !== 'README.md').length > 0;
78
+
79
+ const hasPackageFile = fs.existsSync(path.join(projectPath, 'package.json')) ||
80
+ fs.existsSync(path.join(projectPath, 'composer.json')) ||
81
+ fs.existsSync(path.join(projectPath, 'pyproject.toml'));
82
+
83
+ return { hasCode, hasKnowledge, hasPackageFile };
120
84
  }
121
85
 
122
- // ── Copiar archivos de Agentic al proyecto ─────────────────────
123
- function copyAgenticFiles(sourcePath, projectPath, spinner) {
124
- const filesToCopy = [
125
- '.agentic',
126
- '.cursor',
127
- 'CLAUDE.md',
128
- '_LOCKS.md',
129
- '_output',
130
- 'docs'
131
- ];
86
+ // ── Descargar desde GitHub ─────────────────────────────────────
87
+ async function downloadFromGitHub(spinner) {
88
+ const tmpFile = path.join(require('os').tmpdir(), 'agentic-kdd.tar.gz');
89
+ try {
90
+ execSync(`curl -sL "https://github.com/${GITHUB_REPO}/archive/refs/heads/main.tar.gz" -o "${tmpFile}"`, { stdio: 'pipe' });
91
+ fs.ensureDirSync(TEMP_DIR);
92
+ execSync(`tar -xzf "${tmpFile}" -C "${TEMP_DIR}" --strip-components=1`, { stdio: 'pipe' });
93
+ fs.removeSync(tmpFile);
94
+ return TEMP_DIR;
95
+ } catch (err) {
96
+ throw new Error('No se pudo descargar desde GitHub. Verifica tu conexión.');
97
+ }
98
+ }
132
99
 
133
- spinner.text = 'Installing Agentic KDD files...';
100
+ // ── Copiar archivos al proyecto ────────────────────────────────
101
+ function copyAgenticFiles(sourcePath, projectPath) {
102
+ const files = ['CLAUDE.md', '_LOCKS.md', '_output', 'docs', '.cursor'];
134
103
 
135
- for (const file of filesToCopy) {
104
+ for (const file of files) {
136
105
  const src = path.join(sourcePath, file);
137
106
  const dest = path.join(projectPath, file);
107
+ if (fs.existsSync(src)) fs.copySync(src, dest, { overwrite: true });
108
+ }
138
109
 
139
- if (fs.existsSync(src)) {
140
- // No sobreescribir memoria si ya existe
141
- if (file === '.agentic' && fs.existsSync(dest)) {
142
- // Solo actualizar agentes, no memoria ni config
143
- const agentsSrc = path.join(src, 'agentes');
144
- const agentsDest = path.join(dest, 'agentes');
145
- if (fs.existsSync(agentsSrc)) {
146
- fs.copySync(agentsSrc, agentsDest, { overwrite: true });
147
- }
148
- // Copiar conocimiento README si no existe
149
- const knowledgeReadmeSrc = path.join(src, 'conocimiento', 'README.md');
150
- const knowledgeReadmeDest = path.join(dest, 'conocimiento', 'README.md');
151
- if (fs.existsSync(knowledgeReadmeSrc) && !fs.existsSync(knowledgeReadmeDest)) {
152
- fs.copySync(knowledgeReadmeSrc, knowledgeReadmeDest);
153
- }
154
- } else {
155
- fs.copySync(src, dest, { overwrite: true });
156
- }
110
+ // .agentic — copiar agentes y estructura pero NO sobreescribir memoria si existe
111
+ const agSrc = path.join(sourcePath, '.agentic');
112
+ const agDest = path.join(projectPath, '.agentic');
113
+
114
+ if (fs.existsSync(agSrc)) {
115
+ // Agentes siempre se actualizan
116
+ fs.copySync(path.join(agSrc, 'agentes'), path.join(agDest, 'agentes'), { overwrite: true });
117
+
118
+ // Memoria y config solo si no existen
119
+ const memorySrc = path.join(agSrc, 'memoria');
120
+ const memoryDest = path.join(agDest, 'memoria');
121
+ if (!fs.existsSync(memoryDest)) fs.copySync(memorySrc, memoryDest);
122
+
123
+ const configDest = path.join(agDest, 'config.md');
124
+ if (!fs.existsSync(configDest)) {
125
+ fs.copySync(path.join(agSrc, 'config.md'), configDest);
157
126
  }
158
- }
159
- }
160
127
 
161
- // ── Configurar config.md con el stack detectado ────────────────
162
- function configureProject(projectPath, stack, answers) {
163
- const configPath = path.join(projectPath, '.agentic', 'config.md');
128
+ // PLAN.md solo si no existe
129
+ const planDest = path.join(agDest, 'PLAN.md');
130
+ if (!fs.existsSync(planDest)) {
131
+ fs.copySync(path.join(agSrc, 'PLAN.md'), planDest);
132
+ }
164
133
 
165
- if (!fs.existsSync(configPath)) return;
134
+ // conocimiento/README siempre
135
+ const knSrc = path.join(agSrc, 'conocimiento', 'README.md');
136
+ const knDest = path.join(agDest, 'conocimiento', 'README.md');
137
+ fs.ensureDirSync(path.dirname(knDest));
138
+ if (!fs.existsSync(knDest)) fs.copySync(knSrc, knDest);
139
+ }
140
+ }
166
141
 
167
- const projectName = answers.name || path.basename(projectPath);
168
- const projectDesc = answers.description || '—';
169
- const isNew = answers.isNew;
142
+ // ── Escribir config.md con la info recopilada ──────────────────
143
+ function writeConfig(projectPath, data) {
144
+ const { name, description, isNew, stack } = data;
170
145
 
171
146
  const config = `# Agentic KDD — Configuración del proyecto
172
147
  CONFIGURADO: SI
173
148
  VERSION: 2.0
174
149
 
175
- ---
176
-
177
150
  ## Proyecto
178
- Nombre: ${projectName}
179
- Descripción: ${projectDesc}
151
+ Nombre: ${name}
152
+ Descripción: ${description}
180
153
  Tipo: ${isNew ? 'NUEVO' : 'EXISTENTE'}
181
154
 
182
155
  ## Stack
183
156
  \`\`\`yaml
184
157
  frontend:
185
158
  framework: ${stack.framework}
186
- language: ${stack.hasPhp ? 'PHP' : stack.hasPython ? 'Python' : 'TypeScript'}
159
+ language: ${stack.language}
187
160
 
188
161
  backend:
189
- runtime: ${stack.hasNode ? 'Node.js' : stack.hasPhp ? 'PHP' : 'Python'}
162
+ runtime: ${stack.language}
190
163
  framework: ${stack.framework}
191
164
  base_datos: —
192
165
 
@@ -201,15 +174,12 @@ commands:
201
174
  lint: ${stack.commands.lint}
202
175
  \`\`\`
203
176
 
204
- ## Arquitectura
205
- _El agente Setup completa esto al analizar el proyecto._
206
-
207
177
  ## Módulos
208
178
  ### Implementados
209
- _Ninguno aún._
179
+ _El agente Setup los detecta al correr aa: configurar_
210
180
 
211
181
  ### Pendientes
212
- _Ninguno aún._
182
+ _El agente Setup los detecta al correr aa: configurar_
213
183
 
214
184
  ## Archivos compartidos críticos
215
185
  _El Setup los detecta._
@@ -221,7 +191,7 @@ _Se definen durante el desarrollo._
221
191
  _Sin sinónimos registrados aún._
222
192
  `;
223
193
 
224
- fs.writeFileSync(configPath, config, 'utf8');
194
+ fs.writeFileSync(path.join(projectPath, '.agentic', 'config.md'), config, 'utf8');
225
195
  }
226
196
 
227
197
  // ── Comando principal: akdd init ───────────────────────────────
@@ -234,74 +204,129 @@ async function init() {
234
204
  // Verificar si ya está instalado
235
205
  const alreadyInstalled = fs.existsSync(path.join(projectPath, '.agentic', 'config.md'));
236
206
  if (alreadyInstalled) {
237
- const { confirm } = await inquirer.prompt([{
207
+ const content = fs.readFileSync(path.join(projectPath, '.agentic', 'config.md'), 'utf8');
208
+ if (content.includes('CONFIGURADO: SI')) {
209
+ const { confirm } = await inquirer.prompt([{
210
+ type: 'confirm',
211
+ name: 'confirm',
212
+ message: chalk.yellow('Agentic KDD ya está instalado. ¿Reinstalar?'),
213
+ default: false
214
+ }]);
215
+ if (!confirm) {
216
+ console.log(chalk.gray('\n Usa akdd update para actualizar sin perder tu memoria.\n'));
217
+ return;
218
+ }
219
+ }
220
+ }
221
+
222
+ // Detectar estado del proyecto
223
+ const state = detectProjectState(projectPath);
224
+ const stack = detectStack(projectPath);
225
+
226
+ // ── PREGUNTA 1 — Nuevo o existente ────────────────────────────
227
+ const { isNew } = await inquirer.prompt([{
228
+ type: 'list',
229
+ name: 'isNew',
230
+ message: '¿El proyecto es nuevo o ya está encaminado?',
231
+ choices: [
232
+ { name: 'Nuevo — empezando desde cero', value: true },
233
+ { name: 'Existente — ya tiene código o avance', value: false }
234
+ ],
235
+ default: !state.hasCode
236
+ }]);
237
+
238
+ // ── PREGUNTA 2 — Documentación ────────────────────────────────
239
+ let hasDocsAnswer = false;
240
+
241
+ if (!state.hasKnowledge) {
242
+ const { hasDocs } = await inquirer.prompt([{
238
243
  type: 'confirm',
239
- name: 'confirm',
240
- message: chalk.yellow('Agentic KDD is already installed in this project. Reinstall?'),
244
+ name: 'hasDocs',
245
+ message: '¿Tienes documentación del proyecto? (specs, wireframes, reglas de negocio, etc.)',
241
246
  default: false
242
247
  }]);
243
- if (!confirm) {
244
- console.log(chalk.gray('\n Run akdd update to update without losing your memory.\n'));
245
- return;
248
+ hasDocsAnswer = hasDocs;
249
+
250
+ if (hasDocs) {
251
+ console.log('\n' + chalk.cyan(' Sube tus archivos a la carpeta:'));
252
+ console.log(chalk.bold(' .agentic/conocimiento/'));
253
+ console.log(chalk.gray(' (PDFs, Markdown, Word, specs — cualquier formato)\n'));
254
+
255
+ await inquirer.prompt([{
256
+ type: 'input',
257
+ name: 'ready',
258
+ message: 'Presiona Enter cuando hayas subido los archivos...'
259
+ }]);
246
260
  }
261
+ } else {
262
+ console.log(chalk.green(' ✓ Encontré archivos en conocimiento/ — el Setup los leerá al configurar'));
263
+ hasDocsAnswer = true;
247
264
  }
248
265
 
249
- // Detectar stack
250
- const spinner = ora({ text: 'Detecting project stack...', color: 'blue' }).start();
251
- const stack = detectStack(projectPath);
252
- spinner.succeed(chalk.green(`Stack detected: ${stack.framework} (${stack.packageManager})`));
266
+ // ── PREGUNTA 3 — Nombre ────────────────────────────────────────
267
+ const { name } = await inquirer.prompt([{
268
+ type: 'input',
269
+ name: 'name',
270
+ message: 'Nombre del proyecto:',
271
+ default: path.basename(projectPath)
272
+ }]);
253
273
 
254
- // Preguntas
255
- const answers = await inquirer.prompt([
256
- {
257
- type: 'input',
258
- name: 'name',
259
- message: 'Project name:',
260
- default: path.basename(projectPath)
261
- },
262
- {
274
+ // Descripción según el caso
275
+ let description = '—';
276
+
277
+ if (isNew && !hasDocsAnswer) {
278
+ // Proyecto nuevo sin docs — pedir descripción
279
+ const { desc } = await inquirer.prompt([{
263
280
  type: 'input',
264
- name: 'description',
265
- message: 'What does this project do? (1-2 lines):',
266
- default: ''
267
- },
268
- {
269
- type: 'confirm',
270
- name: 'isNew',
271
- message: 'Is this a new project (from scratch)?',
272
- default: !fs.existsSync(path.join(projectPath, 'src')) &&
273
- !fs.existsSync(path.join(projectPath, 'app'))
274
- }
275
- ]);
281
+ name: 'desc',
282
+ message: 'Describe brevemente el proyecto (qué hace, para quién):',
283
+ validate: (v) => v.trim().length > 10 || 'Por favor da una descripción de al menos 10 caracteres'
284
+ }]);
285
+ description = desc;
286
+ } else if (!isNew && state.hasCode) {
287
+ description = 'Proyecto existente — el agente Setup detectará los módulos y describirá el proyecto al correr aa: configurar';
288
+ } else if (hasDocsAnswer) {
289
+ description = 'El agente Setup leerá conocimiento/ y generará la descripción al correr aa: configurar';
290
+ }
276
291
 
277
- // Descargar e instalar
278
- const installSpinner = ora({ text: 'Downloading Agentic KDD...', color: 'blue' }).start();
292
+ // ── Descargar e instalar ───────────────────────────────────────
293
+ const spinner = ora({ text: 'Descargando Agentic KDD desde GitHub...', color: 'blue' }).start();
279
294
 
280
295
  try {
281
- const sourcePath = await downloadFromGitHub(installSpinner);
296
+ const sourcePath = await downloadFromGitHub(spinner);
282
297
 
283
- installSpinner.text = 'Installing files...';
284
- copyAgenticFiles(sourcePath, projectPath, installSpinner);
298
+ spinner.text = 'Instalando archivos...';
299
+ copyAgenticFiles(sourcePath, projectPath);
285
300
 
286
- installSpinner.text = 'Configuring project...';
287
- configureProject(projectPath, stack, answers);
301
+ spinner.text = 'Configurando proyecto...';
302
+ writeConfig(projectPath, { name, description, isNew, stack });
288
303
 
289
- // Limpiar temp
290
304
  fs.removeSync(TEMP_DIR);
291
305
 
292
- installSpinner.succeed(chalk.green('Agentic KDD installed successfully!'));
306
+ spinner.succeed(chalk.green('¡Agentic KDD instalado!'));
307
+
308
+ console.log('\n' + chalk.bold(' Siguiente paso — abre el proyecto en Cursor o Claude Code y escribe:\n'));
293
309
 
294
- // Mensaje final
295
- console.log('\n' + chalk.bold(' Ready. Open this project in Cursor or Claude Code and type:\n'));
296
- console.log(chalk.cyan(' aa: configurar') + chalk.gray(' ← let the Setup agent fine-tune the configuration'));
297
- console.log(chalk.cyan(' aa: [your task]') + chalk.gray(' start building\n'));
310
+ if (!isNew && state.hasCode) {
311
+ console.log(chalk.cyan(' aa: configurar') + chalk.gray(' el Setup leerá el código y configurará todo'));
312
+ } else if (hasDocsAnswer) {
313
+ console.log(chalk.cyan(' aa: configurar') + chalk.gray(' el Setup leerá conocimiento/ y configurará todo'));
314
+ } else {
315
+ console.log(chalk.cyan(' aa: configurar') + chalk.gray(' ← el Setup terminará la configuración'));
316
+ }
298
317
 
299
- console.log(chalk.gray(' Documentation: https://github.com/Adrianlpz211/Agentic-KDD\n'));
318
+ console.log(chalk.cyan(' aa: [tu tarea]') + chalk.gray(' ← cuando esté configurado, a trabajar\n'));
319
+
320
+ if (!hasDocsAnswer && isNew) {
321
+ console.log(chalk.yellow(' 💡 Tip: si tienes specs o docs del proyecto, súbelos a'));
322
+ console.log(chalk.yellow(' .agentic/conocimiento/ antes de correr aa: configurar'));
323
+ console.log(chalk.gray(' Esto hará que el Context Guard funcione mejor.\n'));
324
+ }
300
325
 
301
326
  } catch (err) {
302
- installSpinner.fail(chalk.red('Installation failed'));
303
- console.error(chalk.red('\n Error: ' + err.message));
304
- console.log(chalk.gray(' Try downloading manually from: https://github.com/Adrianlpz211/Agentic-KDD\n'));
327
+ spinner.fail(chalk.red('Error en la instalación'));
328
+ console.error(chalk.red('\n ' + err.message));
329
+ console.log(chalk.gray(' Descarga manual: https://github.com/Adrianlpz211/Agentic-KDD\n'));
305
330
  process.exit(1);
306
331
  }
307
332
  }