agentic-kdd 2.1.1 → 2.1.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.
- package/package.json +1 -1
- package/src/init.js +179 -400
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -10,223 +10,6 @@ const inquirer = require('inquirer');
|
|
|
10
10
|
const GITHUB_REPO = 'Adrianlpz211/Agentic-KDD';
|
|
11
11
|
const TEMP_DIR = path.join(require('os').tmpdir(), 'agentic-kdd-download');
|
|
12
12
|
|
|
13
|
-
// ── Plantillas por stack ────────────────────────────────────────
|
|
14
|
-
const TEMPLATES = {
|
|
15
|
-
nextjs: {
|
|
16
|
-
nombre: 'Next.js App',
|
|
17
|
-
tipo: 'NUEVO',
|
|
18
|
-
modulos_impl: [
|
|
19
|
-
'| 1 | Auth y middleware | /login, /registro, middleware.ts |',
|
|
20
|
-
'| 2 | Layout y navegación | app/layout.tsx, componentes nav |',
|
|
21
|
-
],
|
|
22
|
-
modulos_pend: [
|
|
23
|
-
'- [ ] Dashboard principal',
|
|
24
|
-
'- [ ] Módulo de usuarios',
|
|
25
|
-
'- [ ] API routes principales',
|
|
26
|
-
],
|
|
27
|
-
reglas: [
|
|
28
|
-
'- App Router (no Pages Router)',
|
|
29
|
-
'- Server Components por defecto, Client Components solo cuando necesario',
|
|
30
|
-
'- Queries de base de datos solo en lib/queries/',
|
|
31
|
-
'- Tailwind CSS para estilos',
|
|
32
|
-
'- TypeScript estricto',
|
|
33
|
-
],
|
|
34
|
-
descripcion: 'Aplicación web con Next.js 14 App Router, TypeScript y Tailwind CSS.',
|
|
35
|
-
},
|
|
36
|
-
laravel: {
|
|
37
|
-
nombre: 'Laravel App',
|
|
38
|
-
tipo: 'NUEVO',
|
|
39
|
-
modulos_impl: [
|
|
40
|
-
'| 1 | Auth y roles | routes/auth.php, AuthController |',
|
|
41
|
-
'| 2 | Modelos base | app/Models/, migraciones |',
|
|
42
|
-
],
|
|
43
|
-
modulos_pend: [
|
|
44
|
-
'- [ ] Panel de administración',
|
|
45
|
-
'- [ ] API REST principal',
|
|
46
|
-
'- [ ] Jobs y queues',
|
|
47
|
-
],
|
|
48
|
-
reglas: [
|
|
49
|
-
'- Lógica de negocio en Services, no en Controllers',
|
|
50
|
-
'- Queries complejas en Repositories',
|
|
51
|
-
'- Form Requests para validación',
|
|
52
|
-
'- Resources para transformar respuestas API',
|
|
53
|
-
'- Jobs para procesos en background',
|
|
54
|
-
],
|
|
55
|
-
descripcion: 'Aplicación web con Laravel, MySQL y arquitectura MVC.',
|
|
56
|
-
},
|
|
57
|
-
node: {
|
|
58
|
-
nombre: 'Node.js API',
|
|
59
|
-
tipo: 'NUEVO',
|
|
60
|
-
modulos_impl: [
|
|
61
|
-
'| 1 | Servidor y configuración | index.js, config/, middleware |',
|
|
62
|
-
'| 2 | Auth JWT | routes/auth.js, middleware/auth.js |',
|
|
63
|
-
],
|
|
64
|
-
modulos_pend: [
|
|
65
|
-
'- [ ] Endpoints principales',
|
|
66
|
-
'- [ ] Base de datos y modelos',
|
|
67
|
-
'- [ ] Tests de integración',
|
|
68
|
-
],
|
|
69
|
-
reglas: [
|
|
70
|
-
'- Express con middleware centralizado',
|
|
71
|
-
'- Lógica en services/, no en routes',
|
|
72
|
-
'- Variables de entorno en .env, nunca hardcoded',
|
|
73
|
-
'- Manejo de errores centralizado',
|
|
74
|
-
],
|
|
75
|
-
descripcion: 'API REST con Node.js, Express y base de datos.',
|
|
76
|
-
},
|
|
77
|
-
react: {
|
|
78
|
-
nombre: 'React App',
|
|
79
|
-
tipo: 'NUEVO',
|
|
80
|
-
modulos_impl: [
|
|
81
|
-
'| 1 | Estructura base | App.tsx, router, layout |',
|
|
82
|
-
'| 2 | Estado global | context o zustand |',
|
|
83
|
-
],
|
|
84
|
-
modulos_pend: [
|
|
85
|
-
'- [ ] Componentes principales',
|
|
86
|
-
'- [ ] Integración con API',
|
|
87
|
-
'- [ ] Tests de componentes',
|
|
88
|
-
],
|
|
89
|
-
reglas: [
|
|
90
|
-
'- Componentes funcionales con hooks',
|
|
91
|
-
'- Estado local con useState, global con Context/Zustand',
|
|
92
|
-
'- Llamadas API centralizadas en services/',
|
|
93
|
-
'- CSS Modules o Tailwind, no inline styles',
|
|
94
|
-
],
|
|
95
|
-
descripcion: 'Aplicación React con TypeScript y gestión de estado.',
|
|
96
|
-
},
|
|
97
|
-
php: {
|
|
98
|
-
nombre: 'PHP App',
|
|
99
|
-
tipo: 'NUEVO',
|
|
100
|
-
modulos_impl: [
|
|
101
|
-
'| 1 | Configuración base | config.php, conexión DB |',
|
|
102
|
-
'| 2 | Auth | login.php, session |',
|
|
103
|
-
],
|
|
104
|
-
modulos_pend: [
|
|
105
|
-
'- [ ] Módulo principal',
|
|
106
|
-
'- [ ] Panel de administración',
|
|
107
|
-
],
|
|
108
|
-
reglas: [
|
|
109
|
-
'- Queries preparadas siempre (PDO)',
|
|
110
|
-
'- Validación en el servidor',
|
|
111
|
-
'- Sin lógica en las vistas',
|
|
112
|
-
],
|
|
113
|
-
descripcion: 'Aplicación PHP con MySQL.',
|
|
114
|
-
},
|
|
115
|
-
python: {
|
|
116
|
-
nombre: 'Python App',
|
|
117
|
-
tipo: 'NUEVO',
|
|
118
|
-
modulos_impl: [
|
|
119
|
-
'| 1 | Estructura base | main.py, config.py |',
|
|
120
|
-
'| 2 | Auth | auth/, middleware |',
|
|
121
|
-
],
|
|
122
|
-
modulos_pend: [
|
|
123
|
-
'- [ ] Endpoints principales',
|
|
124
|
-
'- [ ] Base de datos',
|
|
125
|
-
'- [ ] Tests',
|
|
126
|
-
],
|
|
127
|
-
reglas: [
|
|
128
|
-
'- FastAPI o Django según el proyecto',
|
|
129
|
-
'- Pydantic para validación',
|
|
130
|
-
'- SQLAlchemy para queries',
|
|
131
|
-
'- pytest para tests',
|
|
132
|
-
],
|
|
133
|
-
descripcion: 'Aplicación Python con FastAPI/Django.',
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// ── Detectar stack ──────────────────────────────────────────────
|
|
138
|
-
function detectStack(projectPath) {
|
|
139
|
-
const stack = {
|
|
140
|
-
framework: '—', language: '—', runtime: '—',
|
|
141
|
-
base_datos: '—', packageManager: 'npm',
|
|
142
|
-
templateKey: null,
|
|
143
|
-
commands: { install: 'npm install', dev: 'npm run dev', build: 'npm run build', test: 'npm test', lint: 'npm run lint' }
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
if (fs.existsSync(path.join(projectPath, 'package.json'))) {
|
|
147
|
-
const pkg = fs.readJsonSync(path.join(projectPath, 'package.json'), { throws: false }) || {};
|
|
148
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
149
|
-
stack.language = deps['typescript'] ? 'TypeScript' : 'JavaScript';
|
|
150
|
-
stack.runtime = 'Node.js';
|
|
151
|
-
|
|
152
|
-
if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
|
|
153
|
-
stack.packageManager = 'pnpm';
|
|
154
|
-
stack.commands = { install: 'pnpm install', dev: 'pnpm dev', build: 'pnpm build', test: 'pnpm test', lint: 'pnpm lint' };
|
|
155
|
-
} else if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
|
|
156
|
-
stack.packageManager = 'yarn';
|
|
157
|
-
stack.commands = { install: 'yarn', dev: 'yarn dev', build: 'yarn build', test: 'yarn test', lint: 'yarn lint' };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (deps['next']) {
|
|
161
|
-
stack.framework = `Next.js ${(deps['next']||'').replace(/[\^~]/,'')}`;
|
|
162
|
-
stack.templateKey = 'nextjs';
|
|
163
|
-
if (deps['@supabase/supabase-js']) stack.base_datos = 'Supabase PostgreSQL';
|
|
164
|
-
else if (deps['prisma'] || deps['@prisma/client']) stack.base_datos = 'Prisma';
|
|
165
|
-
else if (deps['mongoose']) stack.base_datos = 'MongoDB';
|
|
166
|
-
} else if (deps['vue']) {
|
|
167
|
-
stack.framework = `Vue ${(deps['vue']||'').replace(/[\^~]/,'')}`;
|
|
168
|
-
} else if (deps['react'] && !deps['next']) {
|
|
169
|
-
stack.framework = 'React';
|
|
170
|
-
stack.templateKey = 'react';
|
|
171
|
-
} else if (deps['express']) {
|
|
172
|
-
stack.framework = 'Express';
|
|
173
|
-
stack.templateKey = 'node';
|
|
174
|
-
stack.commands.dev = pkg.scripts?.dev || 'node index.js';
|
|
175
|
-
} else if (deps['fastify']) {
|
|
176
|
-
stack.framework = 'Fastify';
|
|
177
|
-
stack.templateKey = 'node';
|
|
178
|
-
} else if (deps['@nestjs/core']) {
|
|
179
|
-
stack.framework = 'NestJS';
|
|
180
|
-
stack.templateKey = 'node';
|
|
181
|
-
stack.commands.dev = 'npm run start:dev';
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (fs.existsSync(path.join(projectPath, 'composer.json'))) {
|
|
186
|
-
const composer = fs.readJsonSync(path.join(projectPath, 'composer.json'), { throws: false }) || {};
|
|
187
|
-
stack.language = 'PHP'; stack.runtime = 'PHP';
|
|
188
|
-
stack.packageManager = 'composer';
|
|
189
|
-
stack.commands = { install: 'composer install', dev: 'php artisan serve', build: 'composer build', test: './vendor/bin/phpunit', lint: 'composer lint' };
|
|
190
|
-
if ((composer.require || {})['laravel/framework']) {
|
|
191
|
-
stack.framework = `Laravel ${((composer.require||{})['laravel/framework']||'').replace(/[\^~]/,'')}`;
|
|
192
|
-
stack.templateKey = 'laravel';
|
|
193
|
-
} else {
|
|
194
|
-
stack.framework = 'PHP';
|
|
195
|
-
stack.templateKey = 'php';
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (fs.existsSync(path.join(projectPath, 'pyproject.toml')) ||
|
|
200
|
-
fs.existsSync(path.join(projectPath, 'requirements.txt'))) {
|
|
201
|
-
stack.language = 'Python'; stack.runtime = 'Python';
|
|
202
|
-
stack.packageManager = 'pip';
|
|
203
|
-
stack.templateKey = 'python';
|
|
204
|
-
stack.commands = { install: 'pip install -r requirements.txt', dev: 'uvicorn main:app --reload', build: 'pip install -e .', test: 'pytest', lint: 'flake8' };
|
|
205
|
-
if (fs.existsSync(path.join(projectPath, 'pyproject.toml'))) {
|
|
206
|
-
const toml = fs.readFileSync(path.join(projectPath, 'pyproject.toml'), 'utf8');
|
|
207
|
-
if (toml.includes('fastapi')) { stack.framework = 'FastAPI'; }
|
|
208
|
-
else if (toml.includes('django')) { stack.framework = 'Django'; }
|
|
209
|
-
else { stack.framework = 'Python'; }
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return stack;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ── Detectar si el proyecto tiene contenido ─────────────────────
|
|
217
|
-
function detectProjectState(projectPath) {
|
|
218
|
-
const hasCode = fs.existsSync(path.join(projectPath, 'src')) ||
|
|
219
|
-
fs.existsSync(path.join(projectPath, 'app')) ||
|
|
220
|
-
fs.existsSync(path.join(projectPath, 'pages')) ||
|
|
221
|
-
fs.existsSync(path.join(projectPath, 'api'));
|
|
222
|
-
const hasKnowledge = fs.existsSync(path.join(projectPath, '.agentic', 'conocimiento')) &&
|
|
223
|
-
fs.readdirSync(path.join(projectPath, '.agentic', 'conocimiento')).filter(f => f !== 'README.md').length > 0;
|
|
224
|
-
const hasPackageFile = fs.existsSync(path.join(projectPath, 'package.json')) ||
|
|
225
|
-
fs.existsSync(path.join(projectPath, 'composer.json')) ||
|
|
226
|
-
fs.existsSync(path.join(projectPath, 'pyproject.toml'));
|
|
227
|
-
return { hasCode, hasKnowledge, hasPackageFile };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
13
|
// ── Descargar desde GitHub ──────────────────────────────────────
|
|
231
14
|
async function downloadFromGitHub(spinner) {
|
|
232
15
|
const tmpFile = path.join(require('os').tmpdir(), 'agentic-kdd.tar.gz');
|
|
@@ -243,110 +26,127 @@ async function downloadFromGitHub(spinner) {
|
|
|
243
26
|
|
|
244
27
|
// ── Copiar archivos al proyecto ─────────────────────────────────
|
|
245
28
|
function copyAgenticFiles(sourcePath, projectPath) {
|
|
246
|
-
const
|
|
247
|
-
for (const file of
|
|
248
|
-
const src
|
|
29
|
+
const rootFiles = ['CLAUDE.md', '_LOCKS.md', '.cursorrules', 'dashboard.cjs', 'docs', '.cursor', '.audit'];
|
|
30
|
+
for (const file of rootFiles) {
|
|
31
|
+
const src = path.join(sourcePath, file);
|
|
249
32
|
const dest = path.join(projectPath, file);
|
|
250
33
|
if (fs.existsSync(src)) fs.copySync(src, dest, { overwrite: true });
|
|
251
34
|
}
|
|
252
35
|
|
|
253
|
-
const agSrc
|
|
36
|
+
const agSrc = path.join(sourcePath, '.agentic');
|
|
254
37
|
const agDest = path.join(projectPath, '.agentic');
|
|
255
38
|
|
|
256
39
|
if (fs.existsSync(agSrc)) {
|
|
257
40
|
fs.copySync(path.join(agSrc, 'agentes'), path.join(agDest, 'agentes'), { overwrite: true });
|
|
258
|
-
fs.copySync(path.join(agSrc, 'grafo'),
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
41
|
+
fs.copySync(path.join(agSrc, 'grafo'), path.join(agDest, 'grafo'), { overwrite: true });
|
|
42
|
+
|
|
43
|
+
const onlyCreate = ['memoria', 'specs', 'conocimiento'];
|
|
44
|
+
for (const dir of onlyCreate) {
|
|
45
|
+
const dest = path.join(agDest, dir);
|
|
46
|
+
if (!fs.existsSync(dest)) {
|
|
47
|
+
const src = path.join(agSrc, dir);
|
|
48
|
+
if (fs.existsSync(src)) fs.copySync(src, dest);
|
|
49
|
+
else fs.ensureDirSync(dest);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
265
52
|
|
|
266
53
|
const planDest = path.join(agDest, 'PLAN.md');
|
|
267
|
-
if (!fs.existsSync(planDest))
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
fs.ensureDirSync(path.dirname(knDest));
|
|
272
|
-
if (!fs.existsSync(knDest)) fs.copySync(knSrc, knDest);
|
|
54
|
+
if (!fs.existsSync(planDest)) {
|
|
55
|
+
const planSrc = path.join(agSrc, 'PLAN.md');
|
|
56
|
+
if (fs.existsSync(planSrc)) fs.copySync(planSrc, planDest);
|
|
57
|
+
}
|
|
273
58
|
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// ── Escribir config.md completo y listo ────────────────────────
|
|
277
|
-
function writeConfig(projectPath, data) {
|
|
278
|
-
const { name, description, isNew, stack, template } = data;
|
|
279
|
-
const tpl = template || {};
|
|
280
|
-
|
|
281
|
-
const modulosImpl = tpl.modulos_impl ? tpl.modulos_impl.join('\n') : '_El agente Setup los detecta al correr aa: configurar_';
|
|
282
|
-
const modulosPend = tpl.modulos_pend ? tpl.modulos_pend.join('\n') : '_El agente Setup los detecta al correr aa: configurar_';
|
|
283
|
-
const reglas = tpl.reglas ? tpl.reglas.join('\n') : '_Se definen durante el desarrollo._';
|
|
284
|
-
|
|
285
|
-
const config = `# Agentic KDD — Configuración del proyecto
|
|
286
|
-
CONFIGURADO: SI
|
|
287
|
-
VERSION: 2.0
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
## Proyecto
|
|
292
|
-
Nombre: ${name}
|
|
293
|
-
Descripción: ${description}
|
|
294
|
-
Tipo: ${isNew ? 'NUEVO' : 'EXISTENTE'}
|
|
295
|
-
|
|
296
|
-
## Stack
|
|
297
|
-
\`\`\`yaml
|
|
298
|
-
frontend:
|
|
299
|
-
framework: ${stack.framework}
|
|
300
|
-
language: ${stack.language}
|
|
301
59
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
framework: ${stack.framework}
|
|
305
|
-
base_datos: ${stack.base_datos || '—'}
|
|
306
|
-
|
|
307
|
-
devops:
|
|
308
|
-
package_manager: ${stack.packageManager}
|
|
309
|
-
|
|
310
|
-
commands:
|
|
311
|
-
install: ${stack.commands.install}
|
|
312
|
-
dev: ${stack.commands.dev}
|
|
313
|
-
build: ${stack.commands.build}
|
|
314
|
-
test: ${stack.commands.test}
|
|
315
|
-
lint: ${stack.commands.lint}
|
|
316
|
-
\`\`\`
|
|
317
|
-
|
|
318
|
-
## Módulos
|
|
319
|
-
|
|
320
|
-
### Implementados
|
|
321
|
-
| Fase | Módulo | Rutas clave |
|
|
322
|
-
|------|--------|-------------|
|
|
323
|
-
${modulosImpl}
|
|
324
|
-
|
|
325
|
-
### Pendientes
|
|
326
|
-
${modulosPend}
|
|
327
|
-
|
|
328
|
-
## Archivos compartidos críticos
|
|
329
|
-
_El Setup los detecta al correr aa: configurar._
|
|
330
|
-
|
|
331
|
-
## Reglas del proyecto
|
|
332
|
-
|
|
333
|
-
### Agentic KDD
|
|
334
|
-
- \`aa:\` → pipeline completo sin pausas ni confirmaciones entre agentes
|
|
335
|
-
- \`audit:\` → solo auditar, no modificar código
|
|
336
|
-
- Context Guard: validar instrucción contra config/conocimiento/código
|
|
60
|
+
fs.ensureDirSync(path.join(projectPath, '_output'));
|
|
61
|
+
}
|
|
337
62
|
|
|
338
|
-
|
|
339
|
-
|
|
63
|
+
// ── Detectar stack ──────────────────────────────────────────────
|
|
64
|
+
function detectStack(projectPath) {
|
|
65
|
+
const stack = { framework: '—', language: '—', packageManager: 'npm' };
|
|
66
|
+
if (fs.existsSync(path.join(projectPath, 'package.json'))) {
|
|
67
|
+
const pkg = fs.readJsonSync(path.join(projectPath, 'package.json'), { throws: false }) || {};
|
|
68
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
69
|
+
stack.language = deps['typescript'] ? 'TypeScript' : 'JavaScript';
|
|
70
|
+
if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) stack.packageManager = 'pnpm';
|
|
71
|
+
else if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) stack.packageManager = 'yarn';
|
|
72
|
+
if (deps['next']) stack.framework = `Next.js ${(deps['next']||'').replace(/[\^~]/,'')}`;
|
|
73
|
+
else if (deps['react'] && !deps['next']) stack.framework = 'React';
|
|
74
|
+
else if (deps['express']) stack.framework = 'Express';
|
|
75
|
+
else if (deps['fastify']) stack.framework = 'Fastify';
|
|
76
|
+
else if (deps['@nestjs/core']) stack.framework = 'NestJS';
|
|
77
|
+
}
|
|
78
|
+
if (fs.existsSync(path.join(projectPath, 'composer.json'))) {
|
|
79
|
+
const composer = fs.readJsonSync(path.join(projectPath, 'composer.json'), { throws: false }) || {};
|
|
80
|
+
stack.language = 'PHP'; stack.packageManager = 'composer';
|
|
81
|
+
if ((composer.require||{})['laravel/framework']) stack.framework = 'Laravel';
|
|
82
|
+
else stack.framework = 'PHP';
|
|
83
|
+
}
|
|
84
|
+
if (fs.existsSync(path.join(projectPath, 'pyproject.toml')) ||
|
|
85
|
+
fs.existsSync(path.join(projectPath, 'requirements.txt'))) {
|
|
86
|
+
stack.language = 'Python'; stack.packageManager = 'pip';
|
|
87
|
+
stack.framework = 'Python';
|
|
88
|
+
}
|
|
89
|
+
return stack;
|
|
90
|
+
}
|
|
340
91
|
|
|
341
|
-
|
|
342
|
-
|
|
92
|
+
// ── Consolidar docs en conocimiento/ ───────────────────────────
|
|
93
|
+
function consolidarDocs(projectPath) {
|
|
94
|
+
const conocimientoPath = path.join(projectPath, '.agentic', 'conocimiento');
|
|
95
|
+
const consolidados = [];
|
|
96
|
+
const ignorar = ['node_modules', '.git', '.agentic', '_output', 'dist', 'build', '.next', 'vendor'];
|
|
97
|
+
|
|
98
|
+
// Extensiones útiles como conocimiento
|
|
99
|
+
const extensionesUtiles = ['.md', '.pdf', '.txt'];
|
|
100
|
+
// Nombres específicos que siempre son útiles
|
|
101
|
+
const nombresUtiles = ['README', 'SPEC', 'SPECS', 'CONTEXT', 'CONTEXTO', 'REQUIREMENTS',
|
|
102
|
+
'ARCHITECTURE', 'DISEÑO', 'DESIGN', 'BRIEF', 'PRD', 'CLAUDE', 'AGENTS'];
|
|
103
|
+
|
|
104
|
+
function esArchivoUtil(nombre) {
|
|
105
|
+
const upper = nombre.toUpperCase().replace(/\.[^.]+$/, '');
|
|
106
|
+
const ext = path.extname(nombre).toLowerCase();
|
|
107
|
+
if (extensionesUtiles.includes(ext)) return true;
|
|
108
|
+
if (nombresUtiles.some(n => upper.includes(n))) return true;
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
343
111
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
112
|
+
function recorrer(dir, nivel) {
|
|
113
|
+
if (nivel > 3) return; // máximo 3 niveles de profundidad
|
|
114
|
+
let items;
|
|
115
|
+
try { items = fs.readdirSync(dir); } catch(e) { return; }
|
|
116
|
+
|
|
117
|
+
for (const item of items) {
|
|
118
|
+
if (ignorar.includes(item) || item.startsWith('.')) continue;
|
|
119
|
+
const fullPath = path.join(dir, item);
|
|
120
|
+
const stat = fs.statSync(fullPath);
|
|
121
|
+
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
recorrer(fullPath, nivel + 1);
|
|
124
|
+
} else if (stat.isFile() && esArchivoUtil(item)) {
|
|
125
|
+
// No mover repomix — solo usarlo como referencia
|
|
126
|
+
if (item.includes('repomix')) {
|
|
127
|
+
consolidados.push({ src: fullPath, nombre: item, tipo: 'referencia' });
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// No copiar si ya está en conocimiento/
|
|
131
|
+
if (fullPath.startsWith(conocimientoPath)) continue;
|
|
132
|
+
|
|
133
|
+
const destNombre = item;
|
|
134
|
+
const destPath = path.join(conocimientoPath, destNombre);
|
|
135
|
+
// Si ya existe uno con el mismo nombre, agregar prefijo del directorio padre
|
|
136
|
+
const finalDest = fs.existsSync(destPath)
|
|
137
|
+
? path.join(conocimientoPath, path.basename(dir) + '_' + destNombre)
|
|
138
|
+
: destPath;
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
fs.copySync(fullPath, finalDest, { overwrite: false });
|
|
142
|
+
consolidados.push({ src: fullPath, nombre: destNombre, tipo: 'copiado' });
|
|
143
|
+
} catch(e) {}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
347
147
|
|
|
348
|
-
|
|
349
|
-
|
|
148
|
+
recorrer(projectPath, 0);
|
|
149
|
+
return consolidados;
|
|
350
150
|
}
|
|
351
151
|
|
|
352
152
|
// ── Comando principal: akdd init ────────────────────────────────
|
|
@@ -357,46 +157,32 @@ async function init() {
|
|
|
357
157
|
console.log(chalk.gray(' github.com/Adrianlpz211/Agentic-KDD\n'));
|
|
358
158
|
|
|
359
159
|
// Verificar si ya está instalado
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const { confirm } = await inquirer.prompt([{
|
|
365
|
-
type: 'confirm', name: 'confirm',
|
|
366
|
-
message: chalk.yellow('Agentic KDD ya está instalado. ¿Reinstalar?'),
|
|
367
|
-
default: false
|
|
368
|
-
}]);
|
|
369
|
-
if (!confirm) {
|
|
370
|
-
console.log(chalk.gray('\n Usa akdd update para actualizar los agentes sin perder tu memoria.\n'));
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
160
|
+
if (fs.existsSync(path.join(projectPath, '.agentic', 'agentes'))) {
|
|
161
|
+
console.log(chalk.yellow(' Agentic KDD ya está instalado en este proyecto.'));
|
|
162
|
+
console.log(chalk.gray(' Para actualizar los agentes sin perder tu memoria: akdd update\n'));
|
|
163
|
+
return;
|
|
374
164
|
}
|
|
375
165
|
|
|
376
|
-
// Detectar
|
|
377
|
-
const state = detectProjectState(projectPath);
|
|
166
|
+
// Detectar stack
|
|
378
167
|
const stack = detectStack(projectPath);
|
|
168
|
+
const hasCode = fs.existsSync(path.join(projectPath, 'src')) ||
|
|
169
|
+
fs.existsSync(path.join(projectPath, 'app')) ||
|
|
170
|
+
fs.existsSync(path.join(projectPath, 'pages'));
|
|
379
171
|
|
|
380
|
-
|
|
381
|
-
if (stack.framework !== '—') {
|
|
172
|
+
if (stack.framework !== '—')
|
|
382
173
|
console.log(chalk.green(` ✓ Stack detectado: ${stack.framework} · ${stack.language} · ${stack.packageManager}`));
|
|
383
|
-
|
|
384
|
-
if (state.hasCode) {
|
|
174
|
+
if (hasCode)
|
|
385
175
|
console.log(chalk.green(' ✓ Código existente detectado'));
|
|
386
|
-
}
|
|
387
|
-
if (state.hasKnowledge) {
|
|
388
|
-
console.log(chalk.green(' ✓ Documentación encontrada en conocimiento/'));
|
|
389
|
-
}
|
|
390
176
|
console.log('');
|
|
391
177
|
|
|
392
|
-
// ── PREGUNTA 1
|
|
178
|
+
// ── PREGUNTA 1: Nombre ──────────────────────────────────────
|
|
393
179
|
const { name } = await inquirer.prompt([{
|
|
394
180
|
type: 'input', name: 'name',
|
|
395
181
|
message: 'Nombre del proyecto:',
|
|
396
182
|
default: path.basename(projectPath)
|
|
397
183
|
}]);
|
|
398
184
|
|
|
399
|
-
// ── PREGUNTA 2
|
|
185
|
+
// ── PREGUNTA 2: Nuevo o existente ──────────────────────────
|
|
400
186
|
const { isNew } = await inquirer.prompt([{
|
|
401
187
|
type: 'list', name: 'isNew',
|
|
402
188
|
message: '¿El proyecto es nuevo o ya tiene código?',
|
|
@@ -404,86 +190,79 @@ async function init() {
|
|
|
404
190
|
{ name: 'Nuevo — empezando desde cero', value: true },
|
|
405
191
|
{ name: 'Existente — ya tiene código o avance', value: false }
|
|
406
192
|
],
|
|
407
|
-
default: !
|
|
193
|
+
default: !hasCode
|
|
408
194
|
}]);
|
|
409
195
|
|
|
410
|
-
// ──
|
|
411
|
-
let description = '—';
|
|
412
|
-
if (isNew && !state.hasKnowledge) {
|
|
413
|
-
const { desc } = await inquirer.prompt([{
|
|
414
|
-
type: 'input', name: 'desc',
|
|
415
|
-
message: 'Describe brevemente qué hace el proyecto:',
|
|
416
|
-
validate: (v) => v.trim().length > 5 || 'Al menos 5 caracteres'
|
|
417
|
-
}]);
|
|
418
|
-
description = desc;
|
|
419
|
-
} else if (!isNew && state.hasCode) {
|
|
420
|
-
description = 'Proyecto existente — el agente Setup detectará módulos y describirá el proyecto al correr aa: configurar';
|
|
421
|
-
} else if (state.hasKnowledge) {
|
|
422
|
-
description = 'El agente Setup leerá conocimiento/ y generará la descripción';
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// ── DOCUMENTACIÓN ──────────────────────────────────────────────
|
|
426
|
-
if (!state.hasKnowledge) {
|
|
427
|
-
const { hasDocs } = await inquirer.prompt([{
|
|
428
|
-
type: 'confirm', name: 'hasDocs',
|
|
429
|
-
message: '¿Tienes specs, wireframes o documentación del proyecto?',
|
|
430
|
-
default: false
|
|
431
|
-
}]);
|
|
432
|
-
if (hasDocs) {
|
|
433
|
-
console.log('\n' + chalk.cyan(' Sube tus archivos a:'));
|
|
434
|
-
console.log(chalk.bold(' .agentic/conocimiento/'));
|
|
435
|
-
console.log(chalk.gray(' (PDFs, Markdown, specs, wireframes — cualquier formato)\n'));
|
|
436
|
-
await inquirer.prompt([{ type: 'input', name: 'ready', message: 'Presiona Enter cuando hayas subido los archivos...' }]);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// ── Instalar ────────────────────────────────────────────────────
|
|
196
|
+
// ── INSTALAR — crear carpetas PRIMERO antes de preguntar docs ──
|
|
441
197
|
const spinner = ora({ text: 'Descargando Agentic KDD...', color: 'magenta' }).start();
|
|
442
|
-
|
|
198
|
+
let sourcePath;
|
|
443
199
|
try {
|
|
444
|
-
|
|
445
|
-
|
|
200
|
+
sourcePath = await downloadFromGitHub(spinner);
|
|
446
201
|
spinner.text = 'Instalando archivos...';
|
|
447
202
|
copyAgenticFiles(sourcePath, projectPath);
|
|
448
|
-
|
|
449
|
-
spinner.text = 'Generando configuración...';
|
|
450
|
-
const template = stack.templateKey ? TEMPLATES[stack.templateKey] : null;
|
|
451
|
-
writeConfig(projectPath, { name, description, isNew, stack, template });
|
|
452
|
-
|
|
453
203
|
fs.removeSync(TEMP_DIR);
|
|
454
|
-
spinner.succeed(chalk.green('
|
|
455
|
-
|
|
456
|
-
// Resumen de lo instalado
|
|
457
|
-
console.log('\n' + chalk.bold(' Instalado:'));
|
|
458
|
-
console.log(chalk.gray(' .agentic/ — pipeline de agentes + memoria KDD'));
|
|
459
|
-
console.log(chalk.gray(' .audit/ — departamento QA (7 subagentes)'));
|
|
460
|
-
console.log(chalk.gray(' dashboard.cjs — dashboard visual'));
|
|
461
|
-
console.log(chalk.gray(' CLAUDE.md — activación aa: / ag: / audit:'));
|
|
462
|
-
console.log(chalk.gray(' .cursorrules — reglas para Cursor'));
|
|
463
|
-
if (template) {
|
|
464
|
-
console.log(chalk.green(`\n Plantilla aplicada: ${stack.framework}`));
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
console.log('\n' + chalk.bold(' Siguiente paso:\n'));
|
|
468
|
-
if (!isNew && state.hasCode) {
|
|
469
|
-
console.log(' ' + chalk.hex('#a78bfa')('aa: configurar') + chalk.gray(' ← el Setup leerá el código y configurará todo'));
|
|
470
|
-
} else {
|
|
471
|
-
console.log(' ' + chalk.hex('#a78bfa')('aa: configurar') + chalk.gray(' ← termina la configuración'));
|
|
472
|
-
}
|
|
473
|
-
console.log(' ' + chalk.hex('#10b981')('aa: [tu tarea]') + chalk.gray(' ← cuando esté listo, a construir'));
|
|
474
|
-
console.log(' ' + chalk.hex('#06b6d4')('node dashboard.cjs') + chalk.gray(' ← abrir dashboard visual\n'));
|
|
475
|
-
|
|
476
|
-
if (stack.templateKey === null) {
|
|
477
|
-
console.log(chalk.yellow(' 💡 Stack no detectado automáticamente. Edita .agentic/config.md'));
|
|
478
|
-
console.log(chalk.yellow(' con tu stack y luego corre aa: configurar\n'));
|
|
479
|
-
}
|
|
480
|
-
|
|
204
|
+
spinner.succeed(chalk.green('Archivos instalados'));
|
|
481
205
|
} catch (err) {
|
|
482
206
|
spinner.fail(chalk.red('Error en la instalación'));
|
|
483
|
-
console.error(chalk.red('\n ' + err.message));
|
|
484
|
-
console.log(chalk.gray(' Descarga manual: https://github.com/Adrianlpz211/Agentic-KDD\n'));
|
|
207
|
+
console.error(chalk.red('\n ' + err.message + '\n'));
|
|
485
208
|
process.exit(1);
|
|
486
209
|
}
|
|
210
|
+
|
|
211
|
+
// ── PREGUNTA 3: Docs (DESPUÉS de crear carpetas) ───────────
|
|
212
|
+
const { hasDocs } = await inquirer.prompt([{
|
|
213
|
+
type: 'confirm', name: 'hasDocs',
|
|
214
|
+
message: '¿Tienes specs, wireframes o documentación del proyecto?',
|
|
215
|
+
default: false
|
|
216
|
+
}]);
|
|
217
|
+
|
|
218
|
+
if (hasDocs) {
|
|
219
|
+
// Recorrer el proyecto y consolidar docs automáticamente
|
|
220
|
+
console.log('');
|
|
221
|
+
const docsSpinner = ora({ text: 'Buscando archivos de conocimiento...', color: 'cyan' }).start();
|
|
222
|
+
const consolidados = consolidarDocs(projectPath);
|
|
223
|
+
const copiados = consolidados.filter(d => d.tipo === 'copiado');
|
|
224
|
+
const referencias = consolidados.filter(d => d.tipo === 'referencia');
|
|
225
|
+
|
|
226
|
+
if (copiados.length > 0) {
|
|
227
|
+
docsSpinner.succeed(chalk.green(`Archivos de conocimiento centralizados en .agentic/conocimiento/`));
|
|
228
|
+
copiados.forEach(d => console.log(chalk.gray(` ✓ ${d.nombre}`)));
|
|
229
|
+
if (referencias.length > 0) {
|
|
230
|
+
console.log(chalk.gray(`\n Como referencia (no movido):`));
|
|
231
|
+
referencias.forEach(d => console.log(chalk.gray(` ~ ${d.nombre}`)));
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
docsSpinner.warn(chalk.yellow('No se encontraron archivos de conocimiento en el proyecto.'));
|
|
235
|
+
console.log(chalk.gray(' Puedes agregarlos manualmente en .agentic/conocimiento/'));
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
console.log('');
|
|
239
|
+
console.log(chalk.gray(' Tip: puedes agregar specs, docs o wireframes en'));
|
|
240
|
+
console.log(chalk.gray(' .agentic/conocimiento/ en cualquier momento.'));
|
|
241
|
+
console.log(chalk.gray(' Agentic los usará automáticamente en el siguiente aa:'));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── RESUMEN FINAL ───────────────────────────────────────────
|
|
245
|
+
console.log('\n' + chalk.bold(' Instalado:'));
|
|
246
|
+
console.log(chalk.gray(' .agentic/agentes/ — pipeline de 9 agentes'));
|
|
247
|
+
console.log(chalk.gray(' .agentic/grafo/ — motor SQLite de conocimiento'));
|
|
248
|
+
console.log(chalk.gray(' .agentic/memoria/ — errores, patrones, decisiones'));
|
|
249
|
+
console.log(chalk.gray(' .agentic/conocimiento/ — documentación del proyecto'));
|
|
250
|
+
console.log(chalk.gray(' .agentic/specs/ — specs auto-generadas'));
|
|
251
|
+
console.log(chalk.gray(' .audit/ — departamento QA (7 subagentes)'));
|
|
252
|
+
console.log(chalk.gray(' dashboard.cjs — dashboard visual'));
|
|
253
|
+
console.log(chalk.gray(' CLAUDE.md — activa aa: / ag: / audit:'));
|
|
254
|
+
console.log(chalk.gray(' .cursorrules — reglas para Cursor'));
|
|
255
|
+
|
|
256
|
+
// Instrucción final
|
|
257
|
+
console.log('\n' + chalk.dim(' ─────────────────────────────────────────────'));
|
|
258
|
+
console.log(chalk.bold(' Último paso — abre este proyecto en'));
|
|
259
|
+
console.log(chalk.bold(' Cursor o Claude Code y ejecuta:'));
|
|
260
|
+
console.log('');
|
|
261
|
+
console.log(' ' + chalk.bold.hex('#a78bfa')('aa: configurar'));
|
|
262
|
+
console.log('');
|
|
263
|
+
console.log(chalk.gray(' Esto completa la configuración leyendo tu'));
|
|
264
|
+
console.log(chalk.gray(' código real. Solo se hace una vez.'));
|
|
265
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────\n'));
|
|
487
266
|
}
|
|
488
267
|
|
|
489
268
|
module.exports = { init };
|