@qubiit/lmagent 3.3.1 → 3.4.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/.agents/docs/commands.md +16 -18
- package/.agents/docs/getting-started.md +10 -23
- package/.agents/scripts/create_skill.js +5 -5
- package/.agents/scripts/validate_skills.js +5 -5
- package/AGENTS.md +4 -4
- package/CLAUDE.md +5 -5
- package/CONTRIBUTING.md +3 -8
- package/GEMINI.md +5 -5
- package/README.md +18 -25
- package/install.js +361 -786
- package/package.json +2 -2
- package/scripts/create_skill.js +5 -5
- package/scripts/validate_skills.js +3 -3
package/install.js
CHANGED
|
@@ -29,13 +29,57 @@ const INIT_FILES = [
|
|
|
29
29
|
// para evitar conflictos de contexto duplicado en agentes como Cursor y Zed que leen múltiples .md del raíz.
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// Directorios core a verificar en .agents/ (usado por doctor)
|
|
33
|
+
const CORE_DIRS = [
|
|
34
|
+
{ src: 'skills', desc: 'Skills especializados' },
|
|
35
|
+
{ src: 'rules', desc: 'Reglas de comportamiento' },
|
|
36
|
+
{ src: 'workflows', desc: 'SOPs y Procedimientos' },
|
|
33
37
|
{ src: 'config', desc: 'Configuración del framework' },
|
|
34
|
-
{ src: 'templates', desc: 'Templates de proyecto' },
|
|
35
38
|
{ src: 'docs', desc: 'Documentación extendida' },
|
|
36
|
-
{ src: '
|
|
39
|
+
{ src: 'memory', desc: 'Contexto persistente' },
|
|
37
40
|
];
|
|
38
41
|
|
|
42
|
+
// HOME_PATHS: Rutas globales de configuración de cada agente en el HOME del usuario
|
|
43
|
+
// Se usan SOLO para DETECCIÓN (saber si el agente está instalado en el sistema)
|
|
44
|
+
// La instalación del framework siempre va al directorio del proyecto
|
|
45
|
+
const HOME_PATHS = {
|
|
46
|
+
'cursor': ['.cursor'],
|
|
47
|
+
'windsurf': ['.windsurf', '.codeium/windsurf'],
|
|
48
|
+
'claude': ['.claude'],
|
|
49
|
+
'cline': ['.cline'],
|
|
50
|
+
'roo': ['.roo'],
|
|
51
|
+
'vscode': ['.vscode'],
|
|
52
|
+
'trae': ['.trae'],
|
|
53
|
+
'zed': ['.config/zed'],
|
|
54
|
+
'augment': ['.augment'],
|
|
55
|
+
'gemini': ['.gemini'],
|
|
56
|
+
'codex': ['.codex'],
|
|
57
|
+
'continue': ['.continue'],
|
|
58
|
+
'goose': ['.config/goose'],
|
|
59
|
+
'junie': ['.junie'],
|
|
60
|
+
'opencode': ['.opencode'],
|
|
61
|
+
'openhands': ['.openhands'],
|
|
62
|
+
'antigravity': ['.agent'],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Helper: Detectar agentes instalados GLOBALMENTE en el HOME del usuario
|
|
66
|
+
function detectGlobalAgents() {
|
|
67
|
+
const homeDir = os.homedir();
|
|
68
|
+
const globallyDetected = new Set();
|
|
69
|
+
|
|
70
|
+
for (const [agentValue, paths] of Object.entries(HOME_PATHS)) {
|
|
71
|
+
for (const p of paths) {
|
|
72
|
+
const fullPath = path.join(homeDir, p);
|
|
73
|
+
if (fs.existsSync(fullPath)) {
|
|
74
|
+
globallyDetected.add(agentValue);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return globallyDetected;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
39
83
|
// IDE_CONFIGS: Lista ÚNICA y DEDUPLICADA de todos los agentes soportados
|
|
40
84
|
const IDE_CONFIGS = [
|
|
41
85
|
// --- IDEs Principales (Auto-Detectados) ---
|
|
@@ -122,11 +166,12 @@ program.command('update')
|
|
|
122
166
|
});
|
|
123
167
|
|
|
124
168
|
program.command('init')
|
|
125
|
-
.description('Inicializar proyecto con LMAgent (
|
|
169
|
+
.description('Inicializar proyecto con LMAgent (alias de install)')
|
|
126
170
|
.option('-f, --force', 'Sobrescribir archivos existentes')
|
|
127
171
|
.option('-y, --yes', 'No preguntar, instalar todo')
|
|
128
172
|
.action((options) => {
|
|
129
|
-
|
|
173
|
+
console.log(chalk.blue('ℹ `init` es ahora alias de `install`. Ejecutando instalación unificada...'));
|
|
174
|
+
runInstall(options);
|
|
130
175
|
});
|
|
131
176
|
|
|
132
177
|
program.command('doctor')
|
|
@@ -435,106 +480,62 @@ async function runInstall(options) {
|
|
|
435
480
|
|
|
436
481
|
const projectRoot = process.cwd();
|
|
437
482
|
|
|
483
|
+
// ── PASO 1: Desplegar Pilares (AGENTS.md) ──
|
|
438
484
|
await deployCorePillars(options, projectRoot);
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
485
|
+
|
|
486
|
+
// ── PASO 2: Detección Automática de Agentes (Global + Proyecto) ──
|
|
487
|
+
// Detección global: busca agentes instalados en el HOME del usuario
|
|
488
|
+
const globalAgents = detectGlobalAgents();
|
|
489
|
+
// Detección en proyecto: busca marcadores en el directorio del proyecto
|
|
490
|
+
const detectedIdes = IDE_CONFIGS.filter(ide => {
|
|
491
|
+
if (ide.value === 'custom' || ide.value === 'generic') return false;
|
|
492
|
+
const markerInProject = ide.markerFile && fs.existsSync(path.join(projectRoot, ide.markerFile));
|
|
493
|
+
const rulesDirRoot = ide.rulesDir && fs.existsSync(path.join(projectRoot, ide.rulesDir.split('/')[0]));
|
|
494
|
+
const installedGlobally = globalAgents.has(ide.value);
|
|
495
|
+
return markerInProject || rulesDirRoot || installedGlobally;
|
|
496
|
+
});
|
|
443
497
|
|
|
444
498
|
let targetIdes = [];
|
|
445
499
|
let selectedSkills = [];
|
|
446
500
|
let selectedRules = [];
|
|
447
|
-
let selectedWorkflows = [];
|
|
448
|
-
let
|
|
449
|
-
let installTarget = 'project';
|
|
450
|
-
let targetRoot = projectRoot;
|
|
501
|
+
let selectedWorkflows = [];
|
|
502
|
+
let installDirs = { config: true, templates: true, docs: true, memory: true };
|
|
451
503
|
|
|
452
504
|
if (options.yes) {
|
|
505
|
+
// ── Modo No Interactivo ──
|
|
453
506
|
console.log(chalk.yellow('⚡ Modo: No interactivo'));
|
|
454
|
-
targetIdes = IDE_CONFIGS.
|
|
455
|
-
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
targetIdes = [IDE_CONFIGS.find(i => i.value === 'cursor')];
|
|
459
|
-
}
|
|
460
|
-
selectedSkills = getAllItems(SOURCE_SKILLS, true);
|
|
461
|
-
selectedRules = getAllItems(SOURCE_RULES, false);
|
|
462
|
-
selectedWorkflows = getAllItems(SOURCE_WORKFLOWS, false);
|
|
507
|
+
targetIdes = detectedIdes.length > 0 ? detectedIdes : [IDE_CONFIGS.find(i => i.value === 'cursor')];
|
|
508
|
+
selectedSkills = getAllItems(PACKAGE_SKILLS_DIR, true);
|
|
509
|
+
selectedRules = getAllItems(PACKAGE_RULES_DIR, false);
|
|
510
|
+
selectedWorkflows = getAllItems(PACKAGE_WORKFLOWS_DIR, false);
|
|
463
511
|
} else {
|
|
512
|
+
// ── Modo Interactivo ──
|
|
464
513
|
console.log(chalk.gray('================================================================'));
|
|
465
|
-
console.log(chalk.cyan('🔹
|
|
514
|
+
console.log(chalk.cyan('🔹 Instalación Unificada LMAgent'));
|
|
466
515
|
console.log(chalk.gray('================================================================'));
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
// 1. Detect Environment
|
|
470
|
-
const isWindows = os.platform() === 'win32';
|
|
471
|
-
installMethod = isWindows ? 'copy' : 'symlink';
|
|
472
|
-
installTarget = 'project';
|
|
473
|
-
targetRoot = projectRoot;
|
|
474
|
-
|
|
475
|
-
// 2. Banner simplified
|
|
476
|
-
console.log(`📍 Destino: ${chalk.green('Proyecto Actual')}`);
|
|
477
|
-
console.log(`🔧 Método: ${chalk.green(isWindows ? 'Copy (Windows Safe)' : 'Symlink (Live Updates)')}`);
|
|
516
|
+
console.log(`📍 Destino: ${chalk.green(projectRoot)}`);
|
|
517
|
+
console.log(`🔧 Core: ${chalk.green('.agents/ (centralizado)')}`);
|
|
478
518
|
console.log('');
|
|
479
519
|
|
|
480
|
-
//
|
|
481
|
-
// Primero busca en el PROYECTO actual, luego en HOME del usuario.
|
|
482
|
-
// HOME_PATHS se usa SOLO para detectar agentes instalados en el sistema (pre-selección).
|
|
483
|
-
// La instalación siempre va al PROYECTO actual, nunca a HOME.
|
|
484
|
-
const userHome = os.homedir();
|
|
485
|
-
const HOME_PATHS = {
|
|
486
|
-
'cursor': ['.cursor'], 'windsurf': ['.windsurf', '.codeium/windsurf'],
|
|
487
|
-
'cline': ['.cline'], 'roo': ['.roo'],
|
|
488
|
-
'vscode': ['.vscode'], 'trae': ['.trae'], 'trae-cn': ['.trae-cn'],
|
|
489
|
-
'claude': ['.claude'], 'zed': ['.config/zed', '.zed'],
|
|
490
|
-
'antigravity': ['.gemini'], 'gemini': ['.gemini'],
|
|
491
|
-
'augment': ['.augment'], 'continue': ['.continue'], 'codex': ['.codex'],
|
|
492
|
-
'goose': ['.config/goose', '.goose'], 'junie': ['.junie'],
|
|
493
|
-
'kilo': ['.kilocode'], 'kiro': ['.kiro'], 'opencode': ['.opencode'],
|
|
494
|
-
'openhands': ['.openhands'], 'amp': ['.config/amp'],
|
|
495
|
-
'zencoder': ['.zencoder'], 'codebuddy': ['.codebuddy'],
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
const detectedIdes = IDE_CONFIGS.filter(ide => {
|
|
499
|
-
if (ide.value === 'custom' || ide.value === 'generic') return false;
|
|
500
|
-
|
|
501
|
-
// 1) Buscar en el PROYECTO actual
|
|
502
|
-
const markerInProject = ide.markerFile && fs.existsSync(path.join(projectRoot, ide.markerFile));
|
|
503
|
-
const rulesDirInProject = ide.rulesDir && fs.existsSync(path.join(projectRoot, ide.rulesDir));
|
|
504
|
-
const skillsDirInProject = ide.skillsDir && fs.existsSync(path.join(projectRoot, ide.skillsDir));
|
|
505
|
-
const configInProject = ide.configFile && fs.existsSync(path.join(projectRoot, ide.configFile));
|
|
506
|
-
const inProject = markerInProject || rulesDirInProject || skillsDirInProject || configInProject;
|
|
507
|
-
|
|
508
|
-
// 2) Buscar en HOME del usuario (detectar agente instalado en el sistema)
|
|
509
|
-
const homePaths = HOME_PATHS[ide.value] || [];
|
|
510
|
-
const inSystem = homePaths.some(p => fs.existsSync(path.join(userHome, p)));
|
|
511
|
-
|
|
512
|
-
return inProject || inSystem;
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
// 4. Smart Multi-Agent Auto-Detection Setup
|
|
520
|
+
// Auto-Detect IDEs
|
|
516
521
|
if (detectedIdes.length === 0) {
|
|
517
|
-
console.log(chalk.yellow('⚠️ No se detectaron agentes
|
|
518
|
-
console.log(chalk.blue('ℹ
|
|
522
|
+
console.log(chalk.yellow('⚠️ No se detectaron agentes en este proyecto.'));
|
|
523
|
+
console.log(chalk.blue('ℹ Se creará estructura base + Cursor por defecto.\n'));
|
|
519
524
|
targetIdes = [IDE_CONFIGS.find(i => i.value === 'cursor')];
|
|
520
525
|
} else {
|
|
521
|
-
console.log(chalk.green(`\n🚀 ¡Agentes Detectados! (${detectedIdes.length})`));
|
|
522
526
|
const names = detectedIdes.map(i => i.name).join(', ');
|
|
523
|
-
console.log(chalk.
|
|
527
|
+
console.log(chalk.green(`🚀 Agentes Detectados: ${chalk.bold(names)}\n`));
|
|
524
528
|
targetIdes = detectedIdes;
|
|
525
529
|
}
|
|
526
530
|
|
|
527
|
-
const availableSkills = getAllItems(
|
|
528
|
-
const availableRules = getAllItems(
|
|
529
|
-
const availableWorkflows = getAllItems(
|
|
530
|
-
// Memory logic: usually just a directory, not individual items to select, but we can check if it exists
|
|
531
|
-
const hasMemory = fs.existsSync(path.join(SOURCE_SKILLS, '../memory')); // Hacky relative check or use defined constant if available in scope
|
|
531
|
+
const availableSkills = getAllItems(PACKAGE_SKILLS_DIR, true);
|
|
532
|
+
const availableRules = getAllItems(PACKAGE_RULES_DIR, false);
|
|
533
|
+
const availableWorkflows = getAllItems(PACKAGE_WORKFLOWS_DIR, false);
|
|
532
534
|
|
|
533
|
-
console.log('');
|
|
534
535
|
const quickInstall = await inquirer.prompt([{
|
|
535
536
|
type: 'confirm',
|
|
536
537
|
name: 'all',
|
|
537
|
-
message: '⚡
|
|
538
|
+
message: '⚡ ¿Instalar TODO (Skills, Rules, Workflows, Memory, Config, Docs)?',
|
|
538
539
|
default: true
|
|
539
540
|
}]);
|
|
540
541
|
|
|
@@ -542,480 +543,319 @@ async function runInstall(options) {
|
|
|
542
543
|
selectedSkills = availableSkills;
|
|
543
544
|
selectedRules = availableRules;
|
|
544
545
|
selectedWorkflows = availableWorkflows;
|
|
545
|
-
options.installMemory = true; // Flag to install memory
|
|
546
546
|
} else {
|
|
547
|
-
//
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
{
|
|
552
|
-
|
|
553
|
-
name: 'skills',
|
|
554
|
-
message: 'Selecciona (Espacio para elegir, Enter para confirmar):',
|
|
555
|
-
choices: availableSkills.map(s => ({ name: s, checked: true })),
|
|
556
|
-
pageSize: 15
|
|
557
|
-
}
|
|
558
|
-
]);
|
|
547
|
+
// Selección manual de componentes
|
|
548
|
+
const skillsAnswer = await inquirer.prompt([{
|
|
549
|
+
type: 'checkbox', name: 'skills',
|
|
550
|
+
message: '🧩 Skills:', pageSize: 15,
|
|
551
|
+
choices: availableSkills.map(s => ({ name: s, checked: true }))
|
|
552
|
+
}]);
|
|
559
553
|
selectedSkills = skillsAnswer.skills;
|
|
560
554
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
{
|
|
565
|
-
|
|
566
|
-
name: 'rules',
|
|
567
|
-
message: 'Selecciona (Espacio para elegir, Enter para confirmar):',
|
|
568
|
-
choices: availableRules.map(r => ({ name: r, checked: true })),
|
|
569
|
-
pageSize: 15
|
|
570
|
-
}
|
|
571
|
-
]);
|
|
555
|
+
const rulesAnswer = await inquirer.prompt([{
|
|
556
|
+
type: 'checkbox', name: 'rules',
|
|
557
|
+
message: '📜 Rules:', pageSize: 15,
|
|
558
|
+
choices: availableRules.map(r => ({ name: r, checked: true }))
|
|
559
|
+
}]);
|
|
572
560
|
selectedRules = rulesAnswer.rules;
|
|
573
561
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
{
|
|
578
|
-
|
|
579
|
-
name: 'workflows',
|
|
580
|
-
message: 'Selecciona (Espacio para elegir, Enter para confirmar):',
|
|
581
|
-
choices: availableWorkflows.map(w => ({ name: w, checked: true })),
|
|
582
|
-
pageSize: 15
|
|
583
|
-
}
|
|
584
|
-
]);
|
|
562
|
+
const workflowsAnswer = await inquirer.prompt([{
|
|
563
|
+
type: 'checkbox', name: 'workflows',
|
|
564
|
+
message: '🔄 Workflows:', pageSize: 15,
|
|
565
|
+
choices: availableWorkflows.map(w => ({ name: w, checked: true }))
|
|
566
|
+
}]);
|
|
585
567
|
selectedWorkflows = workflowsAnswer.workflows;
|
|
586
568
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
name: '
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
]);
|
|
597
|
-
|
|
569
|
+
const dirsAnswer = await inquirer.prompt([{
|
|
570
|
+
type: 'checkbox', name: 'dirs',
|
|
571
|
+
message: '📁 Directorios adicionales:',
|
|
572
|
+
choices: [
|
|
573
|
+
{ name: 'config/ - Configuración del framework', value: 'config', checked: true },
|
|
574
|
+
{ name: 'templates/ - Plantillas de proyecto', value: 'templates', checked: true },
|
|
575
|
+
{ name: 'docs/ - Documentación', value: 'docs', checked: true },
|
|
576
|
+
{ name: 'memory/ - Contexto persistente', value: 'memory', checked: true },
|
|
577
|
+
]
|
|
578
|
+
}]);
|
|
579
|
+
installDirs = {
|
|
580
|
+
config: dirsAnswer.dirs.includes('config'),
|
|
581
|
+
templates: dirsAnswer.dirs.includes('templates'),
|
|
582
|
+
docs: dirsAnswer.dirs.includes('docs'),
|
|
583
|
+
memory: dirsAnswer.dirs.includes('memory'),
|
|
584
|
+
};
|
|
598
585
|
}
|
|
599
586
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
console.log('');
|
|
603
587
|
const { confirm } = await inquirer.prompt([{
|
|
604
|
-
type: 'confirm',
|
|
605
|
-
|
|
606
|
-
message: '¿Proceder con la instalación?',
|
|
607
|
-
default: true
|
|
588
|
+
type: 'confirm', name: 'confirm',
|
|
589
|
+
message: '¿Proceder con la instalación?', default: true
|
|
608
590
|
}]);
|
|
609
591
|
if (!confirm) return;
|
|
610
592
|
}
|
|
611
593
|
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const allRootFiles = [
|
|
616
|
-
'.cursorrules', '.windsurfrules', '.windsurfrules.md', '.continuerules',
|
|
617
|
-
'.goosehints', '.roorules',
|
|
618
|
-
'CLAUDE.md', 'GEMINI.md', 'openclaw.json'
|
|
619
|
-
];
|
|
594
|
+
// ── PASO 3: Instalar CORE en .agents/ (ÚNICA ubicación) ──
|
|
595
|
+
const coreDir = path.join(projectRoot, '.agents');
|
|
596
|
+
if (!fs.existsSync(coreDir)) fs.mkdirSync(coreDir, { recursive: true });
|
|
620
597
|
|
|
621
|
-
|
|
622
|
-
const requiredRootFiles = new Set();
|
|
598
|
+
console.log(chalk.bold('\n📦 Instalando Core en .agents/ (centralizado):'));
|
|
623
599
|
|
|
624
|
-
//
|
|
625
|
-
|
|
600
|
+
// Skills
|
|
601
|
+
if (selectedSkills.length > 0) {
|
|
602
|
+
const targetDir = path.join(coreDir, 'skills');
|
|
603
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
604
|
+
for (const skill of selectedSkills) {
|
|
605
|
+
const src = path.join(PACKAGE_SKILLS_DIR, skill);
|
|
606
|
+
const dest = path.join(targetDir, skill);
|
|
607
|
+
if (fs.existsSync(src)) {
|
|
608
|
+
copyRecursiveSync(src, dest, true);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
console.log(` ${chalk.green('✔')} Skills: ${selectedSkills.length} instalados en .agents/skills/`);
|
|
612
|
+
}
|
|
626
613
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
614
|
+
// Rules
|
|
615
|
+
if (selectedRules.length > 0) {
|
|
616
|
+
const targetDir = path.join(coreDir, 'rules');
|
|
617
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
618
|
+
for (const rule of selectedRules) {
|
|
619
|
+
const src = path.join(PACKAGE_RULES_DIR, rule);
|
|
620
|
+
const dest = path.join(targetDir, rule);
|
|
621
|
+
if (fs.existsSync(src)) fs.copyFileSync(src, dest);
|
|
630
622
|
}
|
|
631
|
-
|
|
632
|
-
|
|
623
|
+
console.log(` ${chalk.green('✔')} Rules: ${selectedRules.length} instaladas en .agents/rules/`);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Workflows
|
|
627
|
+
if (selectedWorkflows.length > 0) {
|
|
628
|
+
const targetDir = path.join(coreDir, 'workflows');
|
|
629
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
630
|
+
for (const wf of selectedWorkflows) {
|
|
631
|
+
const src = path.join(PACKAGE_WORKFLOWS_DIR, wf);
|
|
632
|
+
const dest = path.join(targetDir, wf);
|
|
633
|
+
if (fs.existsSync(src)) fs.copyFileSync(src, dest);
|
|
633
634
|
}
|
|
635
|
+
console.log(` ${chalk.green('✔')} Workflows: ${selectedWorkflows.length} instalados en .agents/workflows/`);
|
|
634
636
|
}
|
|
635
637
|
|
|
636
|
-
//
|
|
638
|
+
// Directorios adicionales (config, templates, docs, memory)
|
|
639
|
+
if (installDirs.config && fs.existsSync(PACKAGE_CONFIG_DIR)) {
|
|
640
|
+
copyRecursiveSync(PACKAGE_CONFIG_DIR, path.join(coreDir, 'config'), true);
|
|
641
|
+
console.log(` ${chalk.green('✔')} Config copiado a .agents/config/`);
|
|
642
|
+
}
|
|
643
|
+
if (installDirs.templates && fs.existsSync(PACKAGE_TEMPLATES_DIR)) {
|
|
644
|
+
copyRecursiveSync(PACKAGE_TEMPLATES_DIR, path.join(coreDir, 'templates'), true);
|
|
645
|
+
console.log(` ${chalk.green('✔')} Templates copiado a .agents/templates/`);
|
|
646
|
+
}
|
|
647
|
+
if (installDirs.docs && fs.existsSync(PACKAGE_DOCS_DIR)) {
|
|
648
|
+
copyRecursiveSync(PACKAGE_DOCS_DIR, path.join(coreDir, 'docs'), true);
|
|
649
|
+
console.log(` ${chalk.green('✔')} Docs copiado a .agents/docs/`);
|
|
650
|
+
}
|
|
651
|
+
if (installDirs.memory && fs.existsSync(PACKAGE_MEMORY_DIR)) {
|
|
652
|
+
const memTarget = path.join(coreDir, 'memory');
|
|
653
|
+
if (!fs.existsSync(memTarget)) {
|
|
654
|
+
copyRecursiveSync(PACKAGE_MEMORY_DIR, memTarget, true);
|
|
655
|
+
console.log(` ${chalk.green('✔')} Memory copiado a .agents/memory/`);
|
|
656
|
+
} else {
|
|
657
|
+
// Solo copiar archivos que no existan (no sobrescribir contexto del usuario)
|
|
658
|
+
const memFiles = fs.readdirSync(PACKAGE_MEMORY_DIR);
|
|
659
|
+
let newCount = 0;
|
|
660
|
+
for (const mf of memFiles) {
|
|
661
|
+
const dest = path.join(memTarget, mf);
|
|
662
|
+
if (!fs.existsSync(dest)) {
|
|
663
|
+
fs.copyFileSync(path.join(PACKAGE_MEMORY_DIR, mf), dest);
|
|
664
|
+
newCount++;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
console.log(` ${chalk.cyan('ℹ')} Memory: ${newCount} nuevos (existentes preservados)`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// ── PASO 4: Limpieza de Root ──
|
|
672
|
+
console.log(chalk.bold('\n🧹 Limpieza de archivos root:'));
|
|
673
|
+
const allRootFiles = [
|
|
674
|
+
'.cursorrules', '.windsurfrules', '.windsurfrules.md', '.continuerules',
|
|
675
|
+
'.goosehints', '.roorules'
|
|
676
|
+
];
|
|
677
|
+
const requiredRootFiles = new Set(['AGENTS.md']);
|
|
678
|
+
for (const ide of targetIdes) {
|
|
679
|
+
if (ide.configFile && !ide.configFile.includes('/')) requiredRootFiles.add(ide.configFile);
|
|
680
|
+
}
|
|
637
681
|
for (const file of allRootFiles) {
|
|
638
682
|
if (!requiredRootFiles.has(file)) {
|
|
639
|
-
const filePath = path.join(
|
|
683
|
+
const filePath = path.join(projectRoot, file);
|
|
640
684
|
if (fs.existsSync(filePath)) {
|
|
641
685
|
try {
|
|
642
686
|
fs.unlinkSync(filePath);
|
|
643
|
-
console.log(` ${chalk.green('✔')} Eliminado
|
|
687
|
+
console.log(` ${chalk.green('✔')} Eliminado: ${chalk.cyan(file)}`);
|
|
644
688
|
} catch (e) {
|
|
645
|
-
console.error(` ${chalk.red('❌')} Error
|
|
689
|
+
console.error(` ${chalk.red('❌')} Error eliminando ${file}: ${e.message}`);
|
|
646
690
|
}
|
|
647
691
|
}
|
|
648
692
|
}
|
|
649
693
|
}
|
|
650
694
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
currentInstallMethod = 'copy';
|
|
657
|
-
}
|
|
658
|
-
|
|
695
|
+
// ── PASO 5: Desplegar Bridge Files y ConfigFiles por Agente ──
|
|
696
|
+
console.log(chalk.bold('\n🔗 Configurando Agentes Detectados:'));
|
|
697
|
+
const skillCount = selectedSkills.length;
|
|
698
|
+
const ruleCount = selectedRules.length;
|
|
699
|
+
const wfCount = selectedWorkflows.length;
|
|
659
700
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (fs.existsSync(srcFolder)) {
|
|
672
|
-
await applyFile(srcFolder, destFolder, currentInstallMethod);
|
|
673
|
-
console.log(` ${chalk.green('✔')} ${skill}/`);
|
|
674
|
-
}
|
|
701
|
+
for (const ide of targetIdes) {
|
|
702
|
+
// 5a. Limpiar skills/rules/workflows legacy copiados en carpetas de agente
|
|
703
|
+
if (ide.value !== 'generic' && ide.value !== 'amp') {
|
|
704
|
+
const legacyDirs = [ide.skillsDir, ide.workflowsDir].filter(d => d && !arePathsEqual(path.join(projectRoot, d), path.join(coreDir, 'skills')) && !arePathsEqual(path.join(projectRoot, d), path.join(coreDir, 'workflows')));
|
|
705
|
+
for (const ld of legacyDirs) {
|
|
706
|
+
const legacyPath = path.join(projectRoot, ld);
|
|
707
|
+
if (fs.existsSync(legacyPath)) {
|
|
708
|
+
try {
|
|
709
|
+
fs.rmSync(legacyPath, { recursive: true, force: true });
|
|
710
|
+
console.log(` ${chalk.yellow('🗑')} ${ide.name}: Eliminado ${ld}/ (legacy, ahora en .agents/)`);
|
|
711
|
+
} catch (e) { }
|
|
675
712
|
}
|
|
676
|
-
} catch (e) {
|
|
677
|
-
console.error(chalk.red(`❌ Error installing skills for ${ide.name}: ${e.message}`));
|
|
678
713
|
}
|
|
679
714
|
}
|
|
680
715
|
|
|
681
|
-
//
|
|
682
|
-
let bootstrapStatus = 'SKIP';
|
|
716
|
+
// 5b. ConfigFile (CLAUDE.md, GEMINI.md, etc.)
|
|
683
717
|
if (ide.configFile) {
|
|
684
|
-
// Safety: Don't inject Markdown into JSON/YAML
|
|
685
718
|
if (ide.configFile.endsWith('.json') || ide.configFile.endsWith('.yaml') || ide.configFile.endsWith('.yml')) {
|
|
686
|
-
//
|
|
687
|
-
|
|
719
|
+
// Structured configs: use template if exists
|
|
720
|
+
const AGENT_CONFIGS_TEMPLATE_DIR = path.join(__dirname, '.agents', 'templates', 'agent-configs');
|
|
721
|
+
const templateFile = ide.configTemplate ? path.join(AGENT_CONFIGS_TEMPLATE_DIR, ide.configTemplate) : null;
|
|
722
|
+
if (templateFile && fs.existsSync(templateFile)) {
|
|
723
|
+
const configPath = path.join(projectRoot, ide.configFile);
|
|
724
|
+
if (!fs.existsSync(configPath) || options.force) {
|
|
725
|
+
const content = fs.readFileSync(templateFile, 'utf8')
|
|
726
|
+
.replace(/\{\{VERSION\}\}/g, PKG_VERSION)
|
|
727
|
+
.replace(/\{\{MAJOR\}\}/g, PKG_VERSION.split('.')[0]);
|
|
728
|
+
if (!fs.existsSync(path.dirname(configPath))) fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
729
|
+
fs.writeFileSync(configPath, content);
|
|
730
|
+
console.log(` ${chalk.green('✔')} ${ide.name}: ${ide.configFile} (config)`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
688
733
|
} else {
|
|
689
|
-
|
|
690
|
-
const
|
|
691
|
-
const relativeCatalogPath = getRelLink(ide.configFile, 'AGENTS.md');
|
|
692
|
-
const relativeContextPath = getRelLink(ide.configFile, 'CLAUDE.md');
|
|
693
|
-
|
|
694
|
-
// Usar template específico del agente si existe, si no usar contenido genérico
|
|
734
|
+
// Markdown configs (CLAUDE.md, GEMINI.md, copilot-instructions, etc.)
|
|
735
|
+
const configPath = path.join(projectRoot, ide.configFile);
|
|
695
736
|
const AGENT_CONFIGS_TEMPLATE_DIR = path.join(__dirname, '.agents', 'templates', 'agent-configs');
|
|
696
737
|
const templateFile = ide.configTemplate
|
|
697
738
|
? path.join(AGENT_CONFIGS_TEMPLATE_DIR, ide.configTemplate)
|
|
698
739
|
: path.join(AGENT_CONFIGS_TEMPLATE_DIR, '_generic.md');
|
|
699
740
|
|
|
700
741
|
let content;
|
|
701
|
-
if (fs.existsSync(templateFile)
|
|
702
|
-
// Usar template del archivo, inyectando VERSION
|
|
703
|
-
content = fs.readFileSync(templateFile, 'utf8')
|
|
704
|
-
.replace(/\{\{VERSION\}\}/g, PKG_VERSION)
|
|
705
|
-
.replace(/\{\{MAJOR\}\}/g, PKG_VERSION.split('.')[0])
|
|
706
|
-
.replace(/\{\{SKILLS_DIR\}\}/g, ide.skillsDir || '.agents/skills')
|
|
707
|
-
.replace(/\{\{RULES_DIR\}\}/g, ide.rulesDir || '.agents/rules')
|
|
708
|
-
.replace(/\{\{WORKFLOWS_DIR\}\}/g, ide.workflowsDir || '.agents/workflows');
|
|
709
|
-
} else if (ide.configFile.endsWith('.json') && fs.existsSync(templateFile)) {
|
|
710
|
-
// JSON: usar template y reemplazar VERSION
|
|
742
|
+
if (fs.existsSync(templateFile)) {
|
|
711
743
|
content = fs.readFileSync(templateFile, 'utf8')
|
|
712
744
|
.replace(/\{\{VERSION\}\}/g, PKG_VERSION)
|
|
713
745
|
.replace(/\{\{MAJOR\}\}/g, PKG_VERSION.split('.')[0])
|
|
714
|
-
.replace(/\{\{SKILLS_DIR\}\}/g,
|
|
715
|
-
.replace(/\{\{RULES_DIR\}\}/g,
|
|
716
|
-
.replace(/\{\{WORKFLOWS_DIR\}\}/g,
|
|
746
|
+
.replace(/\{\{SKILLS_DIR\}\}/g, '.agents/skills')
|
|
747
|
+
.replace(/\{\{RULES_DIR\}\}/g, '.agents/rules')
|
|
748
|
+
.replace(/\{\{WORKFLOWS_DIR\}\}/g, '.agents/workflows');
|
|
717
749
|
} else {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
# 🤖 LMAgent Framework v${PKG_VERSION}
|
|
721
|
-
> Contexto Activo: Este proyecto utiliza el estándar LMAgent V${PKG_VERSION.split('.')[0]}.
|
|
722
|
-
|
|
723
|
-
## 🚨 SOURCE OF TRUTH (CEREBRO)
|
|
724
|
-
**TU CONTEXTO Y REGLAS VIVEN AQUÍ 👉 [AGENTS.md](${relativeCatalogPath})**
|
|
725
|
-
*Lee este archivo INMEDIATAMENTE para obtener tu identidad, skills y reglas operativas.*
|
|
726
|
-
|
|
727
|
-
## 🔄 ARRANQUE AUTOMÁTICO (Haz esto al iniciar cada sesión)
|
|
728
|
-
1. Lee [AGENTS.md](${relativeCatalogPath}) — Tu catálogo completo de capacidades
|
|
729
|
-
2. Lee \`.agents/rules/00-master.md\` — Reglas y protocolo de trabajo
|
|
730
|
-
3. Si existe \`.agents/memory/04-active-context.md\` — Recupera contexto previo
|
|
731
|
-
4. Clasifica la tarea (Nivel 0-4) y activa el skill apropiado
|
|
732
|
-
|
|
733
|
-
## 📁 RUTAS DE ENTORNO
|
|
734
|
-
- **Skills**: \`${ide.skillsDir || '.agents/skills'}\` (31 skills disponibles)
|
|
735
|
-
- **Rules**: \`${ide.rulesDir || '.agents/rules'}\` (11 reglas)
|
|
736
|
-
- **Workflows**: \`${ide.workflowsDir || '.agents/workflows'}\` (13 SOPs)
|
|
737
|
-
- **Memory**: \`.agents/memory/\`
|
|
738
|
-
- **Config**: \`.agents/config/\`
|
|
739
|
-
|
|
740
|
-
## ⚡ QUICK START TRIGGERS (Menu Rápido)
|
|
741
|
-
Usa estos comandos para activar un rol. Para el catálogo completo de 31 skills, consulta \`AGENTS.md\`.
|
|
742
|
-
|
|
743
|
-
| Trigger | Rol / Skill | Objetivo |
|
|
744
|
-
|:--- |:--- |:--- |
|
|
745
|
-
| \`/orch\` | **Orchestrator** | Clasificar y delegar. |
|
|
746
|
-
| \`/dev\` | **Backend** | APIs y Lógica. |
|
|
747
|
-
| \`/front\` | **Frontend** | UI/UX, React. |
|
|
748
|
-
| \`/pm\` | **Product** | PRDs y Roadmap. |
|
|
749
|
-
| \`/fix\` | **Debugger** | Análisis de bugs. |
|
|
750
|
-
| \`/arch\` | **Architect** | Diseño de sistemas. |
|
|
751
|
-
|
|
752
|
-
> **IMPORTANTE**: Para activar un skill, lee su \`SKILL.md\` completo en \`${ide.skillsDir || '.agents/skills'}/[nombre]/SKILL.md\`.
|
|
750
|
+
content = generateMinimalConfig(ide, PKG_VERSION);
|
|
751
|
+
}
|
|
753
752
|
|
|
754
|
-
|
|
755
|
-
|
|
753
|
+
// Si es un archivo raíz (CLAUDE.md, GEMINI.md), usar contenido del paquete
|
|
754
|
+
if (!ide.configFile.includes('/')) {
|
|
755
|
+
const srcFile = path.join(__dirname, ide.configFile);
|
|
756
|
+
if (fs.existsSync(srcFile)) {
|
|
757
|
+
content = fs.readFileSync(srcFile, 'utf8')
|
|
758
|
+
.replace(/\{\{VERSION\}\}/g, PKG_VERSION)
|
|
759
|
+
.replace(/\{\{MAJOR\}\}/g, PKG_VERSION.split('.')[0]);
|
|
760
|
+
}
|
|
756
761
|
}
|
|
757
|
-
|
|
762
|
+
|
|
758
763
|
try {
|
|
759
|
-
if (fs.existsSync(configPath)) {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
+
if (!fs.existsSync(path.dirname(configPath))) fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
765
|
+
if (!fs.existsSync(configPath) || options.force) {
|
|
766
|
+
fs.writeFileSync(configPath, content);
|
|
767
|
+
console.log(` ${chalk.green('✔')} ${ide.name}: ${ide.configFile}`);
|
|
768
|
+
} else {
|
|
769
|
+
const existingContent = fs.readFileSync(configPath, 'utf8');
|
|
770
|
+
if (!existingContent.includes('AGENTS.md')) {
|
|
771
|
+
fs.appendFileSync(configPath, '\n' + content);
|
|
772
|
+
console.log(` ${chalk.blue('ℹ')} ${ide.name}: ${ide.configFile} (actualizado)`);
|
|
764
773
|
} else {
|
|
765
|
-
|
|
766
|
-
if (!existingContent.includes('QUICK START TRIGGERS')) {
|
|
767
|
-
fs.appendFileSync(configPath, '\n' + content);
|
|
768
|
-
bootstrapStatus = 'UPDATED';
|
|
769
|
-
} else {
|
|
770
|
-
bootstrapStatus = 'OK';
|
|
771
|
-
}
|
|
774
|
+
console.log(` ${chalk.cyan('✔')} ${ide.name}: ${ide.configFile} (OK)`);
|
|
772
775
|
}
|
|
773
|
-
} else {
|
|
774
|
-
// Create parent dir if needed (for .github/copilot... etc)
|
|
775
|
-
if (!fs.existsSync(path.dirname(configPath))) fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
776
|
-
fs.writeFileSync(configPath, content);
|
|
777
|
-
bootstrapStatus = 'CREATED';
|
|
778
776
|
}
|
|
779
777
|
} catch (e) {
|
|
780
|
-
console.error(chalk.red(` ❌ Error
|
|
781
|
-
bootstrapStatus = 'ERROR';
|
|
778
|
+
console.error(chalk.red(` ❌ Error con ${ide.configFile}: ${e.message}`));
|
|
782
779
|
}
|
|
783
780
|
}
|
|
784
781
|
}
|
|
785
782
|
|
|
786
|
-
|
|
787
|
-
console.log(` ${bootstrapStatus === 'CREATED' ? chalk.green('✔') : chalk.blue('ℹ')} ${ide.name} Bootstrap: ${bootstrapStatus}`);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// 4.1 Generate Bridge Rule
|
|
791
|
-
// Si el agente NO tiene configFile, necesita un archivo puente en rulesDir para auto-invocarse.
|
|
792
|
-
// Si tampoco tiene bridgeFile definido, usamos '00-lmagent.md' como default genérico.
|
|
783
|
+
// 5c. Bridge File (ligero, solo para agentes sin configFile)
|
|
793
784
|
const bridgeFile = ide.bridgeFile || (ide.rulesDir && !ide.configFile ? '00-lmagent.md' : null);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
const bridgePath = path.join(targetRoot, ide.rulesDir, bridgeFile);
|
|
801
|
-
const relativeBridgeToRoot = path.join(ide.rulesDir, bridgeFile);
|
|
802
|
-
// Usar entry point universal (AGENTS.md) en vez de hardcodear CLAUDE.md
|
|
803
|
-
const agentEntryPoint = ide.configFile || 'AGENTS.md';
|
|
804
|
-
const relContext = getRelLink(relativeBridgeToRoot, agentEntryPoint);
|
|
805
|
-
const relCatalog = getRelLink(relativeBridgeToRoot, 'AGENTS.md');
|
|
806
|
-
const relRules = getRelLink(relativeBridgeToRoot, '.agents/rules/00-master.md');
|
|
807
|
-
const relMemory = getRelLink(relativeBridgeToRoot, '.agents/memory/04-active-context.md');
|
|
808
|
-
|
|
809
|
-
let bridgeContent = '';
|
|
785
|
+
if (bridgeFile && !ide.configFile) {
|
|
786
|
+
if (ide.rulesDir && !fs.existsSync(path.join(projectRoot, ide.rulesDir))) {
|
|
787
|
+
fs.mkdirSync(path.join(projectRoot, ide.rulesDir), { recursive: true });
|
|
788
|
+
}
|
|
789
|
+
const bridgePath = path.join(projectRoot, ide.rulesDir, bridgeFile);
|
|
790
|
+
const relCatalog = getRelLink(path.join(ide.rulesDir, bridgeFile), 'AGENTS.md');
|
|
810
791
|
|
|
792
|
+
let bridgeContent;
|
|
811
793
|
if (bridgeFile.endsWith('.mdc')) {
|
|
812
|
-
// Cursor MDC Format
|
|
813
794
|
bridgeContent = `---
|
|
814
|
-
description: LMAgent Framework Entry Point -
|
|
795
|
+
description: LMAgent Framework Entry Point - Read AGENTS.md for full context
|
|
815
796
|
globs: **/*
|
|
816
797
|
---
|
|
817
798
|
|
|
818
|
-
# 🤖 LMAgent
|
|
819
|
-
|
|
820
|
-
Este proyecto está potenciado por **LMAgent v${PKG_VERSION}**.
|
|
799
|
+
# 🤖 LMAgent v${PKG_VERSION}
|
|
800
|
+
> **LEE [AGENTS.md](${relCatalog})** para obtener tu contexto completo.
|
|
821
801
|
|
|
822
|
-
##
|
|
823
|
-
**
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
1. Lee [AGENTS.md](${relCatalog}) — Tu catálogo completo de capacidades
|
|
828
|
-
2. Lee [00-master.md](${relRules}) — Reglas y protocolo de trabajo
|
|
829
|
-
3. Si existe [04-active-context.md](${relMemory}) — Léelo para recuperar contexto de la sesión anterior
|
|
830
|
-
4. Clasifica la tarea (Nivel 0-4) y activa el skill apropiado
|
|
831
|
-
|
|
832
|
-
## 📁 RUTAS DE ENTORNO
|
|
833
|
-
- **Tus Skills**: \`${ide.skillsDir || '.agents/skills'}\` (31 skills disponibles)
|
|
834
|
-
- **Tus Rules**: \`${ide.rulesDir || '.agents/rules'}\` (11 reglas)
|
|
835
|
-
- **Tus Workflows**: \`${ide.workflowsDir || '.agents/workflows'}\` (13 SOPs)
|
|
836
|
-
- **Memory**: \`${ide.skillsDir ? ide.skillsDir.replace(/\/[^/]+$/, '/memory') : '.agents/memory'}\`
|
|
837
|
-
- **Config**: \`${ide.skillsDir ? ide.skillsDir.replace(/\/[^/]+$/, '/config') : '.agents/config'}\`
|
|
838
|
-
|
|
839
|
-
## ⚡ QUICK START TRIGGERS (Menu Rápido)
|
|
840
|
-
Usa estos comandos para activar un rol. Para el catálogo completo de 31 skills, consulta \`AGENTS.md\`.
|
|
841
|
-
|
|
842
|
-
| Trigger | Rol / Skill | Objetivo |
|
|
843
|
-
|:--- |:--- |:--- |
|
|
844
|
-
| \`/orch\` | **Orchestrator** | Clasificar y delegar. |
|
|
845
|
-
| \`/dev\` | **Backend** | APIs y Lógica. |
|
|
846
|
-
| \`/front\` | **Frontend** | UI/UX, React. |
|
|
847
|
-
| \`/pm\` | **Product** | PRDs y Roadmap. |
|
|
848
|
-
| \`/fix\` | **Debugger** | Análisis de bugs. |
|
|
849
|
-
| \`/arch\` | **Architect** | Diseño de sistemas. |
|
|
850
|
-
|
|
851
|
-
> **IMPORTANTE**: Para activar un skill, lee su \`SKILL.md\` completo en \`${ide.skillsDir || '.agents/skills'}/[nombre]/SKILL.md\`.
|
|
802
|
+
## Rutas del Framework
|
|
803
|
+
- **Skills**: \`.agents/skills/\` (${skillCount} skills)
|
|
804
|
+
- **Rules**: \`.agents/rules/\` (${ruleCount} reglas)
|
|
805
|
+
- **Workflows**: \`.agents/workflows/\` (${wfCount} workflows)
|
|
806
|
+
- **Memory**: \`.agents/memory/\`
|
|
852
807
|
|
|
853
|
-
!! SYSTEM NOTE: You MUST read AGENTS.md at startup
|
|
808
|
+
!! SYSTEM NOTE: You MUST read AGENTS.md at startup. !!
|
|
854
809
|
`;
|
|
855
810
|
} else {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
Este proyecto utiliza **LMAgent v${PKG_VERSION}**.
|
|
860
|
-
|
|
861
|
-
## 🚨 SOURCE OF TRUTH (CEREBRO)
|
|
862
|
-
**TU CONTEXTO Y REGLAS VIVEN AQUÍ 👉 [AGENTS.md](${relCatalog})**
|
|
863
|
-
*Lee este archivo INMEDIATAMENTE para obtener tu identidad, skills y reglas operativas.*
|
|
864
|
-
|
|
865
|
-
## 🔄 ARRANQUE AUTOMÁTICO (Haz esto al iniciar)
|
|
866
|
-
1. Lee [AGENTS.md](${relCatalog}) — Tu catálogo completo de capacidades
|
|
867
|
-
2. Lee [00-master.md](${relRules}) — Reglas y protocolo de trabajo
|
|
868
|
-
3. Si existe [04-active-context.md](${relMemory}) — Léelo para recuperar contexto de la sesión anterior
|
|
869
|
-
4. Clasifica la tarea (Nivel 0-4) y activa el skill apropiado
|
|
870
|
-
|
|
871
|
-
## 📁 RUTAS DE ENTORNO
|
|
872
|
-
- **Tus Skills**: \`${ide.skillsDir || '.agents/skills'}\` (31 skills disponibles)
|
|
873
|
-
- **Tus Rules**: \`${ide.rulesDir || '.agents/rules'}\` (11 reglas)
|
|
874
|
-
- **Tus Workflows**: \`${ide.workflowsDir || '.agents/workflows'}\` (13 SOPs)
|
|
875
|
-
- **Memory**: \`${ide.skillsDir ? ide.skillsDir.replace(/\/[^/]+$/, '/memory') : '.agents/memory'}\`
|
|
876
|
-
- **Config**: \`${ide.skillsDir ? ide.skillsDir.replace(/\/[^/]+$/, '/config') : '.agents/config'}\`
|
|
877
|
-
|
|
878
|
-
## ⚡ QUICK START TRIGGERS (Menu Rápido)
|
|
879
|
-
Usa estos comandos para activar un rol. Para el catálogo completo de 31 skills, consulta \`AGENTS.md\`.
|
|
880
|
-
|
|
881
|
-
| Trigger | Rol / Skill | Objetivo |
|
|
882
|
-
|:--- |:--- |:--- |
|
|
883
|
-
| \`/orch\` | **Orchestrator** | Clasificar y delegar. |
|
|
884
|
-
| \`/dev\` | **Backend** | APIs y Lógica. |
|
|
885
|
-
| \`/front\` | **Frontend** | UI/UX, React. |
|
|
886
|
-
| \`/pm\` | **Product** | PRDs y Roadmap. |
|
|
887
|
-
| \`/fix\` | **Debugger** | Análisis de bugs. |
|
|
888
|
-
| \`/arch\` | **Architect** | Diseño de sistemas. |
|
|
889
|
-
|
|
890
|
-
> **IMPORTANTE**: Para activar un skill, lee su \`SKILL.md\` completo en \`${ide.skillsDir || '.agents/skills'}/[nombre]/SKILL.md\`.
|
|
891
|
-
`;
|
|
892
|
-
}
|
|
811
|
+
bridgeContent = `# 🤖 LMAgent v${PKG_VERSION}
|
|
812
|
+
> **LEE [AGENTS.md](${relCatalog})** para obtener tu contexto completo.
|
|
893
813
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
fs.rmSync(legacySkillsDir, { recursive: true, force: true });
|
|
901
|
-
console.log(` ${chalk.yellow('🗑 Eliminado directorio obsoleto:')} .cursor / skills(Movido a.cursor / rules / skills)`);
|
|
902
|
-
} catch (e) {
|
|
903
|
-
console.error(chalk.red(` ⚠️ No se pudo eliminar.cursor / skills: ${e.message} `));
|
|
904
|
-
}
|
|
905
|
-
}
|
|
814
|
+
## Rutas del Framework
|
|
815
|
+
- **Skills**: \`.agents/skills/\` (${skillCount} skills)
|
|
816
|
+
- **Rules**: \`.agents/rules/\` (${ruleCount} reglas)
|
|
817
|
+
- **Workflows**: \`.agents/workflows/\` (${wfCount} workflows)
|
|
818
|
+
- **Memory**: \`.agents/memory/\`
|
|
819
|
+
`;
|
|
906
820
|
}
|
|
907
821
|
|
|
908
822
|
try {
|
|
909
823
|
if (!fs.existsSync(path.dirname(bridgePath))) fs.mkdirSync(path.dirname(bridgePath), { recursive: true });
|
|
910
824
|
fs.writeFileSync(bridgePath, bridgeContent);
|
|
911
|
-
console.log(` ${chalk.green('✔')} ${ide.name}
|
|
825
|
+
console.log(` ${chalk.green('✔')} ${ide.name}: ${bridgeFile} (bridge)`);
|
|
912
826
|
} catch (e) {
|
|
913
|
-
console.error(chalk.red(` ❌ Error
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
// 2. Install RULES (Files)
|
|
917
|
-
if (selectedRules.length > 0 && ide.rulesDir) {
|
|
918
|
-
const targetDir = path.join(targetRoot, ide.rulesDir);
|
|
919
|
-
console.log(chalk.bold(`\nInstalling Rules to ${chalk.cyan(targetDir)}: `));
|
|
920
|
-
|
|
921
|
-
try {
|
|
922
|
-
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
923
|
-
|
|
924
|
-
// CLEANUP: Remove legacy rules (V2 & Duplicates)
|
|
925
|
-
const legacyRules = [
|
|
926
|
-
'_bootstrap.md', '_bootstrap.mdc', '00-bootstrap.md',
|
|
927
|
-
'agents-ia.md', 'stack.md', 'testing.md', 'security.md', 'code-style.md', 'documentation.md',
|
|
928
|
-
'workflow.md', 'api-design.md', 'automations-n8n.md', 'frontend.md', 'backend.md'
|
|
929
|
-
];
|
|
930
|
-
for (const legacy of legacyRules) {
|
|
931
|
-
const legacyPath = path.join(targetDir, legacy);
|
|
932
|
-
if (fs.existsSync(legacyPath)) {
|
|
933
|
-
fs.unlinkSync(legacyPath);
|
|
934
|
-
console.log(` ${chalk.yellow('🗑 Eliminado regla obsoleta:')} ${legacy} `);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
for (const rule of selectedRules) {
|
|
939
|
-
const srcVal = path.join(SOURCE_RULES, rule);
|
|
940
|
-
const destVal = path.join(targetDir, rule);
|
|
941
|
-
|
|
942
|
-
if (fs.existsSync(srcVal)) {
|
|
943
|
-
await applyFile(srcVal, destVal, currentInstallMethod);
|
|
944
|
-
console.log(` ${chalk.blue('✔')} ${rule} `);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
} catch (e) {
|
|
948
|
-
console.error(chalk.red(`❌ Error installing rules for ${ide.name}: ${e.message} `));
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// 3. Install WORKFLOWS (Files)
|
|
953
|
-
if (selectedWorkflows.length > 0 && ide.workflowsDir) {
|
|
954
|
-
const targetDir = path.join(targetRoot, ide.workflowsDir);
|
|
955
|
-
console.log(chalk.bold(`\nInstalling Workflows to ${chalk.cyan(targetDir)}: `));
|
|
956
|
-
|
|
957
|
-
try {
|
|
958
|
-
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
959
|
-
|
|
960
|
-
for (const wf of selectedWorkflows) {
|
|
961
|
-
const srcVal = path.join(SOURCE_WORKFLOWS, wf);
|
|
962
|
-
const destVal = path.join(targetDir, wf);
|
|
963
|
-
|
|
964
|
-
if (fs.existsSync(srcVal)) {
|
|
965
|
-
await applyFile(srcVal, destVal, currentInstallMethod);
|
|
966
|
-
console.log(` ${chalk.magenta('✔')} ${wf} `);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
} catch (e) {
|
|
970
|
-
console.error(chalk.red(`❌ Error installing workflows for ${ide.name}: ${e.message} `));
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
if (SOURCE_MEMORY && ide.skillsDir) {
|
|
977
|
-
const parentDir = path.dirname(ide.skillsDir);
|
|
978
|
-
const targetDir = path.join(targetRoot, parentDir, 'memory');
|
|
979
|
-
const targetDirLower = targetDir.toLowerCase();
|
|
980
|
-
const sourceMemoryLower = SOURCE_MEMORY.toLowerCase();
|
|
981
|
-
|
|
982
|
-
if (targetDirLower !== sourceMemoryLower) {
|
|
983
|
-
try {
|
|
984
|
-
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
985
|
-
copyRecursiveSync(SOURCE_MEMORY, targetDir, true);
|
|
986
|
-
console.log(` ${chalk.cyan('✔')} Memory(Context) optimized.`);
|
|
987
|
-
} catch (e) {
|
|
988
|
-
console.error(chalk.red(`❌ Error installing memory for ${ide.name}: ${e.message} `));
|
|
989
|
-
}
|
|
990
|
-
} else {
|
|
991
|
-
console.log(` ${chalk.cyan('ℹ')} Memory(Context) already in origin path.`);
|
|
827
|
+
console.error(chalk.red(` ❌ Error bridge ${ide.name}: ${e.message}`));
|
|
992
828
|
}
|
|
993
829
|
}
|
|
994
830
|
}
|
|
995
831
|
|
|
996
|
-
//
|
|
997
|
-
await syncSkillCatalog(
|
|
832
|
+
// ── PASO 6: Sincronizar Catálogo ──
|
|
833
|
+
await syncSkillCatalog(projectRoot);
|
|
998
834
|
|
|
835
|
+
// ── Resumen Final ──
|
|
999
836
|
console.log(gradient.pastel.multiline('\n✨ Instalación Finalizada ✨'));
|
|
1000
|
-
|
|
1001
837
|
console.log(chalk.gray('================================================================'));
|
|
1002
|
-
console.log(chalk.bold.green('🎉 ¡Todo listo!
|
|
838
|
+
console.log(chalk.bold.green('🎉 ¡Todo listo!'));
|
|
1003
839
|
console.log('');
|
|
1004
|
-
|
|
1005
|
-
// Mensaje dinámico según agentes instalados
|
|
1006
840
|
const ideNames = targetIdes.map(i => i.name).join(', ');
|
|
1007
|
-
console.log(chalk.cyan(`🤖 Agentes
|
|
841
|
+
console.log(chalk.cyan(`🤖 Agentes: ${chalk.bold(ideNames)}`));
|
|
842
|
+
console.log(chalk.cyan(`📦 Core: ${chalk.bold('.agents/')} (${skillCount} skills, ${ruleCount} rules, ${wfCount} workflows)`));
|
|
1008
843
|
console.log('');
|
|
1009
|
-
console.log(chalk.white(' 1. Abre tu agente
|
|
1010
|
-
console.log(chalk.white(' 2. Usa
|
|
1011
|
-
console.log(chalk.gray(' /dev → Backend | /front → Frontend | /arch → Arquitecto'));
|
|
1012
|
-
console.log(chalk.gray(' /fix → Debugger | /pm → Product | /orch → Orchestrator'));
|
|
844
|
+
console.log(chalk.white(' 1. Abre tu agente — leerá el contexto automáticamente.'));
|
|
845
|
+
console.log(chalk.white(' 2. Usa triggers: /dev /front /arch /fix /pm /orch'));
|
|
1013
846
|
console.log('');
|
|
1014
|
-
console.log(chalk.dim(' 💡
|
|
1015
|
-
console.log(chalk.dim(' 💡 Ejecuta `lmagent tokens` para ver el consumo de tokens del framework.'));
|
|
847
|
+
console.log(chalk.dim(' 💡 `lmagent doctor` para verificar | `lmagent tokens` para ver consumo'));
|
|
1016
848
|
console.log(chalk.gray('================================================================'));
|
|
1017
849
|
}
|
|
1018
850
|
|
|
851
|
+
// Helper: Genera config mínimo si no hay template
|
|
852
|
+
function generateMinimalConfig(ide, version) {
|
|
853
|
+
return `# 🤖 LMAgent Framework v${version}
|
|
854
|
+
> LEE [AGENTS.md](./AGENTS.md) para obtener tu contexto completo.
|
|
855
|
+
> Skills: \`.agents/skills/\` | Rules: \`.agents/rules/\` | Workflows: \`.agents/workflows/\`
|
|
856
|
+
`;
|
|
857
|
+
}
|
|
858
|
+
|
|
1019
859
|
// ─── Sincronización Dinámica del Catálogo de Skills ───────────────────────────
|
|
1020
860
|
// Escanea .agents/skills/*/SKILL.md, extrae frontmatter y regenera las tablas
|
|
1021
861
|
// entre <!-- SKILLS_CATALOG_START --> y <!-- SKILLS_CATALOG_END --> en:
|
|
@@ -1109,65 +949,7 @@ ${masterTableLines.join('\n')}`;
|
|
|
1109
949
|
}
|
|
1110
950
|
}
|
|
1111
951
|
|
|
1112
|
-
async function applyFile(source, dest, method) {
|
|
1113
|
-
const srcPath = path.resolve(source);
|
|
1114
|
-
const destPath = path.resolve(dest);
|
|
1115
952
|
|
|
1116
|
-
// Case-insensitive check for Windows compatibility
|
|
1117
|
-
if (srcPath.toLowerCase() === destPath.toLowerCase()) {
|
|
1118
|
-
// console.log(chalk.gray(` (Skipping self - install: ${ path.basename(source) })`));
|
|
1119
|
-
return;
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
try {
|
|
1123
|
-
if (fs.existsSync(dest) || (fs.existsSync(path.dirname(dest)) && fs.readdirSync(path.dirname(dest)).includes(path.basename(dest)))) {
|
|
1124
|
-
const lstat = fs.lstatSync(dest);
|
|
1125
|
-
if (lstat.isSymbolicLink()) {
|
|
1126
|
-
fs.unlinkSync(dest); // Safe to remove old symlink
|
|
1127
|
-
} else if (lstat.isDirectory()) {
|
|
1128
|
-
// AUGMENTATION: No borramos el directorio real para no perder archivos del usuario.
|
|
1129
|
-
// Si el usuario pidió symlink pero hay un directorio real, forzamos merge por copia.
|
|
1130
|
-
if (method === 'symlink') method = 'copy';
|
|
1131
|
-
} else {
|
|
1132
|
-
fs.unlinkSync(dest); // It is a file, overwrite it
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
} catch (e) { }
|
|
1136
|
-
|
|
1137
|
-
const destDir = path.dirname(dest);
|
|
1138
|
-
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
1139
|
-
|
|
1140
|
-
const srcStat = fs.statSync(source);
|
|
1141
|
-
const isDir = srcStat.isDirectory();
|
|
1142
|
-
|
|
1143
|
-
if (method === 'symlink') {
|
|
1144
|
-
try {
|
|
1145
|
-
const type = isDir ? 'junction' : 'file';
|
|
1146
|
-
fs.symlinkSync(source, dest, type);
|
|
1147
|
-
} catch (e) {
|
|
1148
|
-
try {
|
|
1149
|
-
if (isDir) {
|
|
1150
|
-
copyRecursiveSync(source, dest, true);
|
|
1151
|
-
} else {
|
|
1152
|
-
fs.copyFileSync(source, dest);
|
|
1153
|
-
}
|
|
1154
|
-
const isWindows = os.platform() === 'win32';
|
|
1155
|
-
const msg = isWindows && !isDir
|
|
1156
|
-
? `(Symlink falló[Requiere Admin / DevMode en Win].Copiado.)`
|
|
1157
|
-
: `(Symlink falló, se usó copia)`;
|
|
1158
|
-
console.log(chalk.yellow(` ${msg} `));
|
|
1159
|
-
} catch (err) {
|
|
1160
|
-
console.error(chalk.red(` Error copiando ${path.basename(dest)}: ${err.message} `));
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
} else {
|
|
1164
|
-
if (isDir) {
|
|
1165
|
-
copyRecursiveSync(source, dest, true);
|
|
1166
|
-
} else {
|
|
1167
|
-
fs.copyFileSync(source, dest);
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
953
|
|
|
1172
954
|
function copyRecursiveSync(src, dest, overwrite) {
|
|
1173
955
|
if (fs.cpSync) {
|
|
@@ -1220,224 +1002,15 @@ function getAllItems(dir, isNested) {
|
|
|
1220
1002
|
}
|
|
1221
1003
|
}
|
|
1222
1004
|
|
|
1223
|
-
//
|
|
1224
|
-
//
|
|
1225
|
-
// ============================================
|
|
1226
|
-
|
|
1227
|
-
async function runInit(options) {
|
|
1228
|
-
let targetIdes = []; // Initialize targetIdes
|
|
1229
|
-
console.clear();
|
|
1230
|
-
const branding = figlet.textSync('LMAGENT', { font: 'ANSI Shadow' });
|
|
1231
|
-
console.log(gradient.pastel.multiline(branding));
|
|
1232
|
-
console.log(gradient.cristal(' by QuBit\n'));
|
|
1233
|
-
|
|
1234
|
-
const projectRoot = process.cwd();
|
|
1235
|
-
const targetRoot = projectRoot; // Fix for ReferenceError in runInit
|
|
1236
|
-
console.log(chalk.cyan(`📦 Inicializando proyecto LMAgent en: ${chalk.bold(projectRoot)} \n`));
|
|
1237
|
-
|
|
1238
|
-
// Verificar si ya está inicializado
|
|
1239
|
-
const agentsExists = fs.existsSync(path.join(projectRoot, 'AGENTS.md'));
|
|
1240
|
-
if (agentsExists && !options.force) {
|
|
1241
|
-
console.log(chalk.yellow('⚠️ Este proyecto ya tiene AGENTS.md'));
|
|
1242
|
-
if (!options.yes) {
|
|
1243
|
-
const { overwrite } = await inquirer.prompt([{
|
|
1244
|
-
type: 'confirm',
|
|
1245
|
-
name: 'overwrite',
|
|
1246
|
-
message: '¿Sobrescribir archivos existentes?',
|
|
1247
|
-
default: false
|
|
1248
|
-
}]);
|
|
1249
|
-
if (!overwrite) {
|
|
1250
|
-
console.log(chalk.yellow('Cancelado. Usa --force para forzar.'));
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
let filesToCopy = [...INIT_FILES];
|
|
1257
|
-
let dirsToCopy = [...INIT_DIRS];
|
|
1258
|
-
|
|
1259
|
-
// Modo interactivo: preguntar qué copiar
|
|
1260
|
-
if (!options.yes) {
|
|
1261
|
-
const answers = await inquirer.prompt([
|
|
1262
|
-
{
|
|
1263
|
-
type: 'checkbox',
|
|
1264
|
-
name: 'files',
|
|
1265
|
-
message: 'Archivos de entry point a copiar:',
|
|
1266
|
-
choices: INIT_FILES.map(f => ({
|
|
1267
|
-
name: `${f.src} - ${f.desc} `,
|
|
1268
|
-
value: f.src,
|
|
1269
|
-
checked: true
|
|
1270
|
-
}))
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
type: 'checkbox',
|
|
1274
|
-
name: 'dirs',
|
|
1275
|
-
message: 'Directorios a copiar:',
|
|
1276
|
-
choices: INIT_DIRS.map(d => ({
|
|
1277
|
-
name: `${d.src}/ - ${d.desc}`,
|
|
1278
|
-
value: d.src,
|
|
1279
|
-
checked: true
|
|
1280
|
-
}))
|
|
1281
|
-
}
|
|
1282
|
-
]);
|
|
1283
|
-
dirsToCopy = INIT_DIRS.filter(d => answers.dirs.includes(d.src));
|
|
1284
|
-
|
|
1285
|
-
// Seleccionar IDE para destino de archivos
|
|
1286
|
-
console.log('');
|
|
1287
|
-
const ideAnswer = await inquirer.prompt([
|
|
1288
|
-
{
|
|
1289
|
-
type: 'checkbox',
|
|
1290
|
-
name: 'ides',
|
|
1291
|
-
message: 'Selecciona tu IDE principal (para ubicar las carpetas):',
|
|
1292
|
-
choices: IDE_CONFIGS.filter(i => i.value !== 'custom').map(i => ({
|
|
1293
|
-
name: i.name,
|
|
1294
|
-
value: i.value,
|
|
1295
|
-
checked: i.value === 'cursor'
|
|
1296
|
-
}))
|
|
1297
|
-
}
|
|
1298
|
-
]);
|
|
1299
|
-
targetIdes = IDE_CONFIGS.filter(i => ideAnswer.ides.includes(i.value));
|
|
1300
|
-
} else {
|
|
1301
|
-
// Defaults for non-interactive
|
|
1302
|
-
targetIdes = [IDE_CONFIGS.find(i => i.value === 'cursor')];
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
// Copiar archivos del framework a la carpeta del Agente (Clean Root)
|
|
1306
|
-
console.log(chalk.bold('\n📦 Instalando framework en directorios de Agente:'));
|
|
1307
|
-
|
|
1308
|
-
for (const ide of targetIdes) {
|
|
1309
|
-
if (!ide.skillsDir) continue; // Skip custom/manual if no dir defined
|
|
1310
|
-
|
|
1311
|
-
// Determinar "Agent Root" (ej: .cursor/ o .github/)
|
|
1312
|
-
// Asume que skillsDir es "root/skills", así que dirname obtiene "root"
|
|
1313
|
-
const agentRootDir = path.join(targetRoot, path.dirname(ide.skillsDir));
|
|
1314
|
-
|
|
1315
|
-
console.log(chalk.dim(` Destino: ${agentRootDir}`));
|
|
1316
|
-
|
|
1317
|
-
// Crear directorio root si no existe
|
|
1318
|
-
if (!fs.existsSync(agentRootDir)) fs.mkdirSync(agentRootDir, { recursive: true });
|
|
1319
|
-
|
|
1320
|
-
// 5. Install Root Configs (CLAUDE.md, AGENTS.md) with Prompt
|
|
1321
|
-
console.log(chalk.bold('\nChecking Root Configurations:'));
|
|
1322
|
-
for (const file of INIT_FILES) {
|
|
1323
|
-
const srcPath = path.join(__dirname, file.src);
|
|
1324
|
-
const destPath = path.join(targetRoot, file.src);
|
|
1005
|
+
// runInit() eliminada en v3.4.0 — unificada en runInstall()
|
|
1006
|
+
// El comando `init` ahora es alias de `install`
|
|
1325
1007
|
|
|
1326
|
-
if (fs.existsSync(srcPath)) {
|
|
1327
|
-
if (!fs.existsSync(destPath)) {
|
|
1328
|
-
let content = fs.readFileSync(srcPath, 'utf8');
|
|
1329
|
-
if (file.versionTemplate) content = content.replace(/\{\{VERSION\}\}/g, PKG_VERSION);
|
|
1330
|
-
fs.writeFileSync(destPath, content, 'utf8');
|
|
1331
|
-
console.log(` ${chalk.green('✔')} ${file.src} (Created)`);
|
|
1332
|
-
} else {
|
|
1333
|
-
// Exists: Ask to overwrite (unless force/yes)
|
|
1334
|
-
let shouldOverwrite = false;
|
|
1335
|
-
if (options.force) {
|
|
1336
|
-
shouldOverwrite = true;
|
|
1337
|
-
} else if (!options.yes) {
|
|
1338
|
-
const answer = await inquirer.prompt([{
|
|
1339
|
-
type: 'confirm',
|
|
1340
|
-
name: 'overwrite',
|
|
1341
|
-
message: `⚠️ ${file.src} ya existe. ¿Sobrescribir?`,
|
|
1342
|
-
default: false
|
|
1343
|
-
}]);
|
|
1344
|
-
shouldOverwrite = answer.overwrite;
|
|
1345
|
-
}
|
|
1346
1008
|
|
|
1347
|
-
if (shouldOverwrite) {
|
|
1348
|
-
let content = fs.readFileSync(srcPath, 'utf8');
|
|
1349
|
-
if (file.versionTemplate) content = content.replace(/\{\{VERSION\}\}/g, PKG_VERSION);
|
|
1350
|
-
fs.writeFileSync(destPath, content, 'utf8');
|
|
1351
|
-
console.log(` ${chalk.yellow('✎')} ${file.src} (Overwritten)`);
|
|
1352
|
-
} else {
|
|
1353
|
-
console.log(` ${chalk.gray('SKIP')} ${file.src} (Kept existing)`);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
1009
|
|
|
1359
|
-
// Copiar Directorios (docs, config, templates)
|
|
1360
|
-
for (const dir of dirsToCopy) {
|
|
1361
|
-
const src = path.join(__dirname, dir.src);
|
|
1362
|
-
const dest = path.join(agentRootDir, dir.src);
|
|
1363
|
-
if (fs.existsSync(src)) {
|
|
1364
|
-
copyRecursiveSync(src, dest, true); // Force overwrite
|
|
1365
|
-
console.log(` ${chalk.green('✔')} ${dir.src}/ -> ${path.dirname(ide.skillsDir)}/${dir.src}/`);
|
|
1366
|
-
|
|
1367
|
-
// CLEANUP: If docs, remove assets (legacy logo, etc.)
|
|
1368
|
-
if (dir.src === 'docs') {
|
|
1369
|
-
const assetsDir = path.join(dest, 'assets');
|
|
1370
|
-
if (fs.existsSync(assetsDir)) {
|
|
1371
|
-
try {
|
|
1372
|
-
fs.rmSync(assetsDir, { recursive: true, force: true });
|
|
1373
|
-
console.log(` ${chalk.yellow('🗑 Eliminado assets heredados (logo, etc.)')}`);
|
|
1374
|
-
} catch (e) { }
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
1010
|
|
|
1381
|
-
// Crear .env.example si no existe
|
|
1382
|
-
const envExampleDest = path.join(projectRoot, '.env.example');
|
|
1383
|
-
if (!fs.existsSync(envExampleDest)) {
|
|
1384
|
-
const envContent = `# ============================================
|
|
1385
|
-
# LMAgent Project - Environment Variables
|
|
1386
|
-
# ============================================
|
|
1387
|
-
# Copiar este archivo a .env y completar los valores
|
|
1388
1011
|
|
|
1389
|
-
# Database
|
|
1390
|
-
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
|
|
1391
1012
|
|
|
1392
|
-
# Redis
|
|
1393
|
-
REDIS_URL=redis://localhost:6379
|
|
1394
1013
|
|
|
1395
|
-
# Security
|
|
1396
|
-
JWT_SECRET=change-this-to-a-strong-secret-minimum-32-characters
|
|
1397
|
-
|
|
1398
|
-
# LLM API Keys
|
|
1399
|
-
OPENAI_API_KEY=sk-...
|
|
1400
|
-
ANTHROPIC_API_KEY=sk-ant-...
|
|
1401
|
-
GOOGLE_API_KEY=...
|
|
1402
|
-
|
|
1403
|
-
# n8n (si aplica)
|
|
1404
|
-
N8N_WEBHOOK_URL=https://n8n.yourserver.com/webhook
|
|
1405
|
-
|
|
1406
|
-
# Environment
|
|
1407
|
-
ENVIRONMENT=development
|
|
1408
|
-
DEBUG=true
|
|
1409
|
-
`;
|
|
1410
|
-
fs.writeFileSync(envExampleDest, envContent);
|
|
1411
|
-
console.log(` ${chalk.green('✔')} .env.example ${chalk.green('(nuevo)')}`);
|
|
1412
|
-
} else {
|
|
1413
|
-
console.log(` ${chalk.blue('ℹ')} .env.example ya existe, no se sobrescribe`);
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
// Resumen
|
|
1417
|
-
console.log(gradient.pastel.multiline(`\n✨ Proyecto inicializado con LMAgent v${PKG_VERSION} ✨`));
|
|
1418
|
-
console.log('');
|
|
1419
|
-
console.log(chalk.cyan('Próximos pasos:'));
|
|
1420
|
-
console.log(` 1. ${chalk.bold('lmagent install')} - Instalar skills/rules/workflows en tu IDE`);
|
|
1421
|
-
console.log(` 2. Editar ${chalk.bold('.env.example')} → ${chalk.bold('.env')} con tus credenciales`);
|
|
1422
|
-
console.log(` 3. Leer ${chalk.bold('AGENTS.md')} para conocer las capacidades disponibles`);
|
|
1423
|
-
console.log('');
|
|
1424
|
-
|
|
1425
|
-
// Preguntar si quiere ejecutar install también
|
|
1426
|
-
if (!options.yes) {
|
|
1427
|
-
const { runInstallNow } = await inquirer.prompt([{
|
|
1428
|
-
type: 'confirm',
|
|
1429
|
-
name: 'runInstallNow',
|
|
1430
|
-
message: '¿Ejecutar lmagent install ahora para conectar al IDE?',
|
|
1431
|
-
default: true
|
|
1432
|
-
}]);
|
|
1433
|
-
if (runInstallNow) {
|
|
1434
|
-
console.log('');
|
|
1435
|
-
await runInstall(options);
|
|
1436
|
-
}
|
|
1437
|
-
} else {
|
|
1438
|
-
console.log(chalk.cyan('💡 Ejecuta `lmagent install` para conectar al IDE.\n'));
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
1014
|
|
|
1442
1015
|
// ============================================
|
|
1443
1016
|
// DOCTOR: Verificar configuración del proyecto
|
|
@@ -1463,91 +1036,93 @@ async function runDoctor() {
|
|
|
1463
1036
|
console.log(` ${chalk.green('✔')} ${file.src}`);
|
|
1464
1037
|
ok++;
|
|
1465
1038
|
} else {
|
|
1466
|
-
console.log(` ${chalk.red('✘')} ${file.src} - ${chalk.red('FALTANTE')} → ejecuta ${chalk.bold('lmagent
|
|
1039
|
+
console.log(` ${chalk.red('✘')} ${file.src} - ${chalk.red('FALTANTE')} → ejecuta ${chalk.bold('lmagent install')}`);
|
|
1467
1040
|
issues++;
|
|
1468
1041
|
}
|
|
1469
1042
|
}
|
|
1470
1043
|
|
|
1471
|
-
// 2.
|
|
1472
|
-
console.log(chalk.bold('\n
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1044
|
+
// 2. Core centralizado en .agents/
|
|
1045
|
+
console.log(chalk.bold('\n📦 Core (.agents/):'));
|
|
1046
|
+
const coreDir = path.join(projectRoot, '.agents');
|
|
1047
|
+
if (fs.existsSync(coreDir)) {
|
|
1048
|
+
console.log(` ${chalk.green('✔')} .agents/ existe`);
|
|
1049
|
+
ok++;
|
|
1050
|
+
|
|
1051
|
+
for (const dir of CORE_DIRS) {
|
|
1052
|
+
const dirPath = path.join(coreDir, dir.src);
|
|
1053
|
+
if (fs.existsSync(dirPath)) {
|
|
1054
|
+
let count = 0;
|
|
1055
|
+
try {
|
|
1056
|
+
const items = fs.readdirSync(dirPath);
|
|
1057
|
+
count = items.filter(i => !i.startsWith('.')).length;
|
|
1058
|
+
} catch (e) { }
|
|
1059
|
+
console.log(` ${chalk.green('✔')} ${dir.src}/ (${count} elementos)`);
|
|
1060
|
+
ok++;
|
|
1061
|
+
} else {
|
|
1062
|
+
console.log(` ${chalk.yellow('⚠')} ${dir.src}/ - No encontrado → ejecuta ${chalk.bold('lmagent install')}`);
|
|
1063
|
+
}
|
|
1480
1064
|
}
|
|
1065
|
+
|
|
1066
|
+
// Verificar conteo de skills
|
|
1067
|
+
const skillsDir = path.join(coreDir, 'skills');
|
|
1068
|
+
if (fs.existsSync(skillsDir)) {
|
|
1069
|
+
const installedSkills = fs.readdirSync(skillsDir)
|
|
1070
|
+
.filter(item => fs.statSync(path.join(skillsDir, item)).isDirectory());
|
|
1071
|
+
const expectedSkillsCount = getAllItems(PACKAGE_SKILLS_DIR, true).length;
|
|
1072
|
+
|
|
1073
|
+
if (installedSkills.length < expectedSkillsCount) {
|
|
1074
|
+
console.log(` ${chalk.yellow('⚠')} Solo ${installedSkills.length}/${expectedSkillsCount} skills → ejecuta ${chalk.bold('lmagent install')}`);
|
|
1075
|
+
} else {
|
|
1076
|
+
console.log(` ${chalk.green('✔')} ${installedSkills.length}/${expectedSkillsCount} skills completos`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
} else {
|
|
1080
|
+
console.log(` ${chalk.red('✘')} .agents/ NO existe → ejecuta ${chalk.bold('lmagent install')}`);
|
|
1081
|
+
issues++;
|
|
1481
1082
|
}
|
|
1482
1083
|
|
|
1483
|
-
// 3. Detectar
|
|
1484
|
-
console.log(chalk.bold('\n🔧
|
|
1084
|
+
// 3. Detectar agentes (Global + Proyecto)
|
|
1085
|
+
console.log(chalk.bold('\n🔧 Agentes detectados:'));
|
|
1086
|
+
const globalAgents = detectGlobalAgents();
|
|
1485
1087
|
let ideFound = false;
|
|
1486
1088
|
for (const ide of IDE_CONFIGS) {
|
|
1487
|
-
if (ide.value === 'custom') continue;
|
|
1488
|
-
const rulesExist = ide.rulesDir && fs.existsSync(path.join(projectRoot, ide.rulesDir));
|
|
1489
|
-
const skillsExist = ide.skillsDir && fs.existsSync(path.join(projectRoot, ide.skillsDir));
|
|
1089
|
+
if (ide.value === 'custom' || ide.value === 'generic') continue;
|
|
1490
1090
|
const markerExist = ide.markerFile && fs.existsSync(path.join(projectRoot, ide.markerFile));
|
|
1091
|
+
const rulesExist = ide.rulesDir && fs.existsSync(path.join(projectRoot, ide.rulesDir));
|
|
1092
|
+
const isGlobal = globalAgents.has(ide.value);
|
|
1093
|
+
const bridgeExists = ide.bridgeFile && ide.rulesDir && fs.existsSync(path.join(projectRoot, ide.rulesDir, ide.bridgeFile));
|
|
1094
|
+
const configExists = ide.configFile && fs.existsSync(path.join(projectRoot, ide.configFile));
|
|
1491
1095
|
|
|
1492
|
-
if (
|
|
1096
|
+
if (markerExist || rulesExist || isGlobal) {
|
|
1493
1097
|
ideFound = true;
|
|
1494
1098
|
const parts = [];
|
|
1495
|
-
if (rulesExist) parts.push('
|
|
1496
|
-
if (
|
|
1099
|
+
if (isGlobal && !markerExist && !rulesExist) parts.push(chalk.blue('global'));
|
|
1100
|
+
if (bridgeExists) parts.push(chalk.green('bridge ✔'));
|
|
1101
|
+
else if (configExists) parts.push(chalk.green('config ✔'));
|
|
1102
|
+
else parts.push(chalk.yellow('sin bridge'));
|
|
1103
|
+
|
|
1497
1104
|
console.log(` ${chalk.green('✔')} ${ide.name} (${parts.join(', ')})`);
|
|
1498
1105
|
ok++;
|
|
1499
|
-
|
|
1500
|
-
// Verificar que tiene los skills correctos
|
|
1501
|
-
if (skillsExist) {
|
|
1502
|
-
const installedSkills = fs.readdirSync(path.join(projectRoot, ide.skillsDir))
|
|
1503
|
-
.filter(item => fs.statSync(path.join(projectRoot, ide.skillsDir, item)).isDirectory());
|
|
1504
|
-
|
|
1505
|
-
// Calcular skills esperados dinámicamente
|
|
1506
|
-
const expectedSkillsCount = getAllItems(PACKAGE_SKILLS_DIR, true).length;
|
|
1507
|
-
const skillCount = installedSkills.length;
|
|
1508
|
-
|
|
1509
|
-
if (skillCount < expectedSkillsCount) {
|
|
1510
|
-
console.log(` ${chalk.yellow('⚠')} Solo ${skillCount}/${expectedSkillsCount} skills instalados → ejecuta ${chalk.bold('lmagent install')}`);
|
|
1511
|
-
} else {
|
|
1512
|
-
console.log(` ${chalk.green('✔')} ${skillCount} skills instalados`);
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
1106
|
}
|
|
1516
1107
|
}
|
|
1517
1108
|
if (!ideFound) {
|
|
1518
|
-
console.log(` ${chalk.red('✘')} Ningún
|
|
1109
|
+
console.log(` ${chalk.red('✘')} Ningún agente detectado → ejecuta ${chalk.bold('lmagent install')}`);
|
|
1519
1110
|
issues++;
|
|
1520
1111
|
}
|
|
1521
1112
|
|
|
1522
|
-
// 4. Verificar .
|
|
1113
|
+
// 4. Verificar .gitignore
|
|
1523
1114
|
console.log(chalk.bold('\n🔒 Seguridad:'));
|
|
1524
|
-
const envExists = fs.existsSync(path.join(projectRoot, '.env'));
|
|
1525
|
-
const envExampleExists = fs.existsSync(path.join(projectRoot, '.env.example'));
|
|
1526
1115
|
const gitignoreExists = fs.existsSync(path.join(projectRoot, '.gitignore'));
|
|
1527
|
-
|
|
1528
|
-
if (envExampleExists) {
|
|
1529
|
-
console.log(` ${chalk.green('✔')} .env.example existe`);
|
|
1530
|
-
ok++;
|
|
1531
|
-
} else {
|
|
1532
|
-
console.log(` ${chalk.yellow('⚠')} .env.example no encontrado`);
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
if (envExists) {
|
|
1536
|
-
console.log(` ${chalk.green('✔')} .env existe`);
|
|
1537
|
-
ok++;
|
|
1538
|
-
} else {
|
|
1539
|
-
console.log(` ${chalk.yellow('⚠')} .env no encontrado (necesario para ejecutar)`);
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
1116
|
if (gitignoreExists) {
|
|
1543
1117
|
const gitignore = fs.readFileSync(path.join(projectRoot, '.gitignore'), 'utf-8');
|
|
1544
1118
|
if (gitignore.includes('.env')) {
|
|
1545
1119
|
console.log(` ${chalk.green('✔')} .env está en .gitignore`);
|
|
1546
1120
|
ok++;
|
|
1547
1121
|
} else {
|
|
1548
|
-
console.log(` ${chalk.
|
|
1549
|
-
issues++;
|
|
1122
|
+
console.log(` ${chalk.yellow('⚠')} .env no está en .gitignore`);
|
|
1550
1123
|
}
|
|
1124
|
+
} else {
|
|
1125
|
+
console.log(` ${chalk.yellow('⚠')} .gitignore no encontrado`);
|
|
1551
1126
|
}
|
|
1552
1127
|
|
|
1553
1128
|
// Resumen
|