@qubiit/lmagent 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/.editorconfig +18 -0
  2. package/AGENTS.md +169 -0
  3. package/CLAUDE.md +122 -0
  4. package/CONTRIBUTING.md +90 -0
  5. package/LICENSE +21 -0
  6. package/README.md +195 -0
  7. package/config/commands.yaml +194 -0
  8. package/config/levels.yaml +135 -0
  9. package/config/models.yaml +192 -0
  10. package/config/settings.yaml +405 -0
  11. package/config/tools-extended.yaml +534 -0
  12. package/config/tools.yaml +437 -0
  13. package/docs/assets/logo.png +0 -0
  14. package/docs/commands.md +132 -0
  15. package/docs/customization-guide.md +445 -0
  16. package/docs/getting-started.md +154 -0
  17. package/docs/how-to-start.md +242 -0
  18. package/docs/navigation-index.md +227 -0
  19. package/docs/usage-guide.md +113 -0
  20. package/install.js +1044 -0
  21. package/package.json +35 -0
  22. package/pyproject.toml +182 -0
  23. package/rules/_bootstrap.md +138 -0
  24. package/rules/agents-ia.md +607 -0
  25. package/rules/api-design.md +337 -0
  26. package/rules/automations-n8n.md +646 -0
  27. package/rules/code-style.md +570 -0
  28. package/rules/documentation.md +98 -0
  29. package/rules/security.md +316 -0
  30. package/rules/stack.md +395 -0
  31. package/rules/testing.md +326 -0
  32. package/rules/workflow.md +353 -0
  33. package/scripts/create_skill.js +300 -0
  34. package/scripts/validate_skills.js +283 -0
  35. package/skills/ai-agent-engineer/SKILL.md +394 -0
  36. package/skills/ai-agent-engineer/references/agent-patterns.md +149 -0
  37. package/skills/api-designer/SKILL.md +429 -0
  38. package/skills/api-designer/references/api-standards.md +13 -0
  39. package/skills/architect/SKILL.md +285 -0
  40. package/skills/architect/references/c4-model.md +133 -0
  41. package/skills/automation-engineer/SKILL.md +352 -0
  42. package/skills/automation-engineer/references/n8n-patterns.md +127 -0
  43. package/skills/backend-engineer/SKILL.md +261 -0
  44. package/skills/backend-engineer/assets/fastapi-project-structure.yaml +74 -0
  45. package/skills/backend-engineer/references/debugging-guide.md +174 -0
  46. package/skills/backend-engineer/references/design-patterns.md +208 -0
  47. package/skills/backend-engineer/scripts/scaffold_backend.py +313 -0
  48. package/skills/bmad-methodology/SKILL.md +202 -0
  49. package/skills/bmad-methodology/references/scale-adaptive-levels.md +141 -0
  50. package/skills/browser-agent/SKILL.md +502 -0
  51. package/skills/browser-agent/scripts/playwright_setup.ts +16 -0
  52. package/skills/code-reviewer/SKILL.md +306 -0
  53. package/skills/code-reviewer/references/code-review-checklist.md +16 -0
  54. package/skills/data-engineer/SKILL.md +474 -0
  55. package/skills/data-engineer/assets/pg-monitoring-queries.sql +154 -0
  56. package/skills/data-engineer/references/index-strategy.md +128 -0
  57. package/skills/data-engineer/scripts/backup_postgres.py +221 -0
  58. package/skills/devops-engineer/SKILL.md +547 -0
  59. package/skills/devops-engineer/references/ci-cd-patterns.md +265 -0
  60. package/skills/devops-engineer/scripts/docker_healthcheck.py +125 -0
  61. package/skills/document-generator/SKILL.md +746 -0
  62. package/skills/document-generator/references/pdf-generation.md +22 -0
  63. package/skills/frontend-engineer/SKILL.md +532 -0
  64. package/skills/frontend-engineer/references/accessibility-guide.md +146 -0
  65. package/skills/frontend-engineer/scripts/audit_bundle.py +144 -0
  66. package/skills/git-workflow/SKILL.md +374 -0
  67. package/skills/git-workflow/references/git-flow.md +25 -0
  68. package/skills/mcp-builder/SKILL.md +471 -0
  69. package/skills/mcp-builder/references/mcp-server-guide.md +23 -0
  70. package/skills/mobile-engineer/SKILL.md +502 -0
  71. package/skills/mobile-engineer/references/platform-guidelines.md +160 -0
  72. package/skills/orchestrator/SKILL.md +246 -0
  73. package/skills/orchestrator/references/methodology-routing.md +117 -0
  74. package/skills/orchestrator/references/persona-mapping.md +85 -0
  75. package/skills/orchestrator/references/routing-logic.md +110 -0
  76. package/skills/performance-engineer/SKILL.md +549 -0
  77. package/skills/performance-engineer/references/caching-patterns.md +181 -0
  78. package/skills/performance-engineer/scripts/profile_endpoint.py +170 -0
  79. package/skills/product-manager/SKILL.md +488 -0
  80. package/skills/product-manager/references/prioritization-frameworks.md +126 -0
  81. package/skills/prompt-engineer/SKILL.md +433 -0
  82. package/skills/prompt-engineer/references/prompt-patterns.md +158 -0
  83. package/skills/qa-engineer/SKILL.md +441 -0
  84. package/skills/qa-engineer/references/testing-strategy.md +166 -0
  85. package/skills/qa-engineer/scripts/run_coverage.py +147 -0
  86. package/skills/scrum-master/SKILL.md +225 -0
  87. package/skills/scrum-master/references/sprint-ceremonies.md +159 -0
  88. package/skills/security-analyst/SKILL.md +390 -0
  89. package/skills/security-analyst/references/owasp-top10.md +188 -0
  90. package/skills/security-analyst/scripts/audit_security.py +242 -0
  91. package/skills/seo-auditor/SKILL.md +523 -0
  92. package/skills/seo-auditor/references/seo-checklist.md +17 -0
  93. package/skills/spec-driven-dev/SKILL.md +342 -0
  94. package/skills/spec-driven-dev/references/phase-gates.md +107 -0
  95. package/skills/supabase-expert/SKILL.md +602 -0
  96. package/skills/supabase-expert/references/supabase-patterns.md +19 -0
  97. package/skills/swe-agent/SKILL.md +311 -0
  98. package/skills/swe-agent/references/trajectory-format.md +134 -0
  99. package/skills/systematic-debugger/SKILL.md +512 -0
  100. package/skills/systematic-debugger/references/debugging-guide.md +12 -0
  101. package/skills/tech-lead/SKILL.md +409 -0
  102. package/skills/tech-lead/references/code-review-checklist.md +111 -0
  103. package/skills/technical-writer/SKILL.md +631 -0
  104. package/skills/technical-writer/references/doc-templates.md +218 -0
  105. package/skills/testing-strategist/SKILL.md +476 -0
  106. package/skills/testing-strategist/references/testing-pyramid.md +16 -0
  107. package/skills/ux-ui-designer/SKILL.md +419 -0
  108. package/skills/ux-ui-designer/references/design-system-foundation.md +168 -0
  109. package/skills_overview.txt +94 -0
  110. package/templates/PROJECT_KICKOFF.md +284 -0
  111. package/templates/SKILL_TEMPLATE.md +131 -0
  112. package/templates/USAGE.md +95 -0
  113. package/templates/agent-python/README.md +71 -0
  114. package/templates/agent-python/agent.py +272 -0
  115. package/templates/agent-python/config.yaml +76 -0
  116. package/templates/agent-python/prompts/system.md +109 -0
  117. package/templates/agent-python/requirements.txt +7 -0
  118. package/templates/automation-n8n/README.md +14 -0
  119. package/templates/automation-n8n/webhook-handler.json +57 -0
  120. package/templates/backend-node/Dockerfile +12 -0
  121. package/templates/backend-node/README.md +15 -0
  122. package/templates/backend-node/package.json +30 -0
  123. package/templates/backend-node/src/index.ts +19 -0
  124. package/templates/backend-node/src/routes.ts +7 -0
  125. package/templates/backend-node/tsconfig.json +22 -0
  126. package/templates/backend-python/Dockerfile +11 -0
  127. package/templates/backend-python/README.md +78 -0
  128. package/templates/backend-python/app/core/config.py +12 -0
  129. package/templates/backend-python/app/core/database.py +12 -0
  130. package/templates/backend-python/app/main.py +17 -0
  131. package/templates/backend-python/app/routers/__init__.py +1 -0
  132. package/templates/backend-python/app/routers/health.py +7 -0
  133. package/templates/backend-python/requirements-dev.txt +6 -0
  134. package/templates/backend-python/requirements.txt +4 -0
  135. package/templates/backend-python/tests/test_health.py +9 -0
  136. package/templates/checkpoint.yaml +117 -0
  137. package/templates/database/README.md +474 -0
  138. package/templates/frontend-react/README.md +446 -0
  139. package/templates/plan.yaml +320 -0
  140. package/templates/session.yaml +125 -0
  141. package/templates/spec.yaml +229 -0
  142. package/templates/tasks.yaml +330 -0
  143. package/workflows/bugfix-backend.md +380 -0
  144. package/workflows/documentation.md +232 -0
  145. package/workflows/generate-prd.md +320 -0
  146. package/workflows/ideation.md +396 -0
  147. package/workflows/new-agent-ia.md +497 -0
  148. package/workflows/new-automation.md +374 -0
  149. package/workflows/new-feature.md +290 -0
  150. package/workflows/optimize-performance.md +373 -0
  151. package/workflows/resolve-github-issue.md +524 -0
  152. package/workflows/security-review.md +291 -0
  153. package/workflows/spec-driven.md +476 -0
  154. package/workflows/testing-strategy.md +296 -0
  155. package/workflows/third-party-integration.md +277 -0
package/install.js ADDED
@@ -0,0 +1,1044 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { Command } = require('commander');
7
+ const chalk = require('chalk');
8
+ const inquirer = require('inquirer');
9
+ const figlet = require('figlet');
10
+ const gradient = require('gradient-string');
11
+
12
+ const program = new Command();
13
+
14
+ // Configuración: Directorios fuente del paquete
15
+ const PACKAGE_SKILLS_DIR = path.join(__dirname, 'skills');
16
+ const PACKAGE_RULES_DIR = path.join(__dirname, 'rules');
17
+ const PACKAGE_WORKFLOWS_DIR = path.join(__dirname, 'workflows');
18
+ const PACKAGE_CONFIG_DIR = path.join(__dirname, 'config');
19
+ const PACKAGE_TEMPLATES_DIR = path.join(__dirname, 'templates');
20
+ const PACKAGE_DOCS_DIR = path.join(__dirname, 'docs');
21
+
22
+ // Archivos de proyecto que init copia a la raíz
23
+ const INIT_FILES = [
24
+ { src: 'CLAUDE.md', desc: 'Instrucciones para Claude Code' },
25
+ { src: 'AGENTS.md', desc: 'Catálogo de capacidades LMAgent' },
26
+ ];
27
+
28
+ const INIT_DIRS = [
29
+ { src: 'config', desc: 'Configuración del framework' },
30
+ { src: 'templates', desc: 'Templates de proyecto' },
31
+ { src: 'docs', desc: 'Documentación extendida' },
32
+ ];
33
+
34
+ // Configuración: IDEs y Agentes soportados
35
+ const IDE_CONFIGS = [
36
+ // --- IDEs Principales ---
37
+ {
38
+ name: 'Cursor',
39
+ value: 'cursor',
40
+ rulesDir: '.cursor/rules',
41
+ skillsDir: '.cursor/skills',
42
+ workflowsDir: '.cursor/workflows',
43
+ markerFile: '.cursorrules'
44
+ },
45
+ {
46
+ name: 'Windsurf',
47
+ value: 'windsurf',
48
+ rulesDir: '.windsurf/rules',
49
+ skillsDir: '.windsurf/skills',
50
+ workflowsDir: '.windsurf/workflows',
51
+ markerFile: '.windsurf'
52
+ },
53
+ {
54
+ name: 'VSCode / GitHub Copilot',
55
+ value: 'vscode',
56
+ rulesDir: '.github/instructions',
57
+ skillsDir: '.github/skills',
58
+ workflowsDir: '.github/workflows',
59
+ markerFile: '.vscode'
60
+ },
61
+ {
62
+ name: 'Claude Code',
63
+ value: 'claude',
64
+ rulesDir: '.claude/rules',
65
+ skillsDir: '.claude/skills',
66
+ workflowsDir: '.claude/workflows',
67
+ markerFile: '.claude'
68
+ },
69
+ {
70
+ name: 'Cline',
71
+ value: 'cline',
72
+ rulesDir: '.clinesrules',
73
+ skillsDir: '.cline/skills',
74
+ workflowsDir: '.cline/workflows',
75
+ markerFile: '.clinesrules'
76
+ },
77
+ {
78
+ name: 'Roo Code',
79
+ value: 'roo',
80
+ rulesDir: '.roo/rules',
81
+ skillsDir: '.roo/skills',
82
+ workflowsDir: '.roo/workflows',
83
+ markerFile: '.roo'
84
+ },
85
+ {
86
+ name: 'Trae',
87
+ value: 'trae',
88
+ rulesDir: '.trae/rules',
89
+ skillsDir: '.trae/skills',
90
+ workflowsDir: '.trae/workflows',
91
+ markerFile: '.trae'
92
+ },
93
+
94
+ // --- Otros Agentes ---
95
+ {
96
+ name: 'Antigravity',
97
+ value: 'antigravity',
98
+ rulesDir: '.agent/rules',
99
+ skillsDir: '.agent/skills',
100
+ workflowsDir: '.agent/workflows',
101
+ markerFile: '.agent'
102
+ },
103
+ {
104
+ name: 'Amp / Kimi / Replit',
105
+ value: 'amp',
106
+ rulesDir: '.agents/rules',
107
+ skillsDir: '.agents/skills',
108
+ workflowsDir: '.agents/workflows',
109
+ markerFile: '.agents'
110
+ },
111
+ {
112
+ name: 'Augment',
113
+ value: 'augment',
114
+ rulesDir: '.augment/rules',
115
+ skillsDir: '.augment/skills',
116
+ workflowsDir: '.augment/workflows',
117
+ markerFile: '.augment'
118
+ },
119
+ {
120
+ name: 'Continue',
121
+ value: 'continue',
122
+ rulesDir: '.continue/rules',
123
+ skillsDir: '.continue/skills',
124
+ workflowsDir: '.continue/workflows',
125
+ markerFile: '.continue'
126
+ },
127
+ {
128
+ name: 'OpenHands',
129
+ value: 'openhands',
130
+ rulesDir: '.openhands/rules',
131
+ skillsDir: '.openhands/skills',
132
+ workflowsDir: '.openhands/workflows',
133
+ markerFile: '.openhands'
134
+ },
135
+ {
136
+ name: 'Goose',
137
+ value: 'goose',
138
+ rulesDir: '.goose/rules',
139
+ skillsDir: '.goose/skills',
140
+ workflowsDir: '.goose/workflows',
141
+ markerFile: '.goose'
142
+ },
143
+ {
144
+ name: 'Mistral Vibe',
145
+ value: 'vibe',
146
+ rulesDir: '.vibe/rules',
147
+ skillsDir: '.vibe/skills',
148
+ workflowsDir: '.vibe/workflows',
149
+ markerFile: '.vibe'
150
+ },
151
+ {
152
+ name: 'Zed',
153
+ value: 'zed',
154
+ rulesDir: '.rules',
155
+ skillsDir: '.rules/skills',
156
+ workflowsDir: '.rules/workflows',
157
+ markerFile: '.zed'
158
+ },
159
+ {
160
+ name: 'Envoid (OpenClaw)',
161
+ value: 'openclaw',
162
+ rulesDir: 'rules',
163
+ skillsDir: 'skills',
164
+ workflowsDir: 'workflows',
165
+ markerFile: 'openclaw.json'
166
+ },
167
+ {
168
+ name: 'CodeBuddy',
169
+ value: 'codebuddy',
170
+ rulesDir: '.codebuddy/rules',
171
+ skillsDir: '.codebuddy/skills',
172
+ workflowsDir: '.codebuddy/workflows',
173
+ markerFile: '.codebuddy'
174
+ },
175
+ {
176
+ name: 'Command Code',
177
+ value: 'command-code',
178
+ rulesDir: '.commandcode/rules',
179
+ skillsDir: '.commandcode/skills',
180
+ workflowsDir: '.commandcode/workflows',
181
+ markerFile: '.commandcode'
182
+ },
183
+ {
184
+ name: 'Crush',
185
+ value: 'crush',
186
+ rulesDir: '.crush/rules',
187
+ skillsDir: '.crush/skills',
188
+ workflowsDir: '.crush/workflows',
189
+ markerFile: '.crush'
190
+ },
191
+ {
192
+ name: 'Droid',
193
+ value: 'droid',
194
+ rulesDir: '.factory/rules',
195
+ skillsDir: '.factory/skills',
196
+ workflowsDir: '.factory/workflows',
197
+ markerFile: '.factory'
198
+ },
199
+ {
200
+ name: 'Junie',
201
+ value: 'junie',
202
+ rulesDir: '.junie/rules',
203
+ skillsDir: '.junie/skills',
204
+ workflowsDir: '.junie/workflows',
205
+ markerFile: '.junie'
206
+ },
207
+ {
208
+ name: 'iFlow',
209
+ value: 'iflow',
210
+ rulesDir: '.iflow/rules',
211
+ skillsDir: '.iflow/skills',
212
+ workflowsDir: '.iflow/workflows',
213
+ markerFile: '.iflow'
214
+ },
215
+ {
216
+ name: 'Kilo Code',
217
+ value: 'kilo',
218
+ rulesDir: '.kilocode/rules',
219
+ skillsDir: '.kilocode/skills',
220
+ workflowsDir: '.kilocode/workflows',
221
+ markerFile: '.kilocode'
222
+ },
223
+ {
224
+ name: 'Kiro',
225
+ value: 'kiro',
226
+ rulesDir: '.kiro/rules',
227
+ skillsDir: '.kiro/skills',
228
+ workflowsDir: '.kiro/workflows',
229
+ markerFile: '.kiro'
230
+ },
231
+ {
232
+ name: 'Kode',
233
+ value: 'kode',
234
+ rulesDir: '.kode/rules',
235
+ skillsDir: '.kode/skills',
236
+ workflowsDir: '.kode/workflows',
237
+ markerFile: '.kode'
238
+ },
239
+ {
240
+ name: 'MCPJam',
241
+ value: 'mcpjam',
242
+ rulesDir: '.mcpjam/rules',
243
+ skillsDir: '.mcpjam/skills',
244
+ workflowsDir: '.mcpjam/workflows',
245
+ markerFile: '.mcpjam'
246
+ },
247
+ {
248
+ name: 'Mux',
249
+ value: 'mux',
250
+ rulesDir: '.mux/rules',
251
+ skillsDir: '.mux/skills',
252
+ workflowsDir: '.mux/workflows',
253
+ markerFile: '.mux'
254
+ },
255
+ {
256
+ name: 'Pi',
257
+ value: 'pi',
258
+ rulesDir: '.pi/rules',
259
+ skillsDir: '.pi/skills',
260
+ workflowsDir: '.pi/workflows',
261
+ markerFile: '.pi'
262
+ },
263
+ {
264
+ name: 'Qoder',
265
+ value: 'qoder',
266
+ rulesDir: '.qoder/rules',
267
+ skillsDir: '.qoder/skills',
268
+ workflowsDir: '.qoder/workflows',
269
+ markerFile: '.qoder'
270
+ },
271
+ {
272
+ name: 'Qwen Code',
273
+ value: 'qwen',
274
+ rulesDir: '.qwen/rules',
275
+ skillsDir: '.qwen/skills',
276
+ workflowsDir: '.qwen/workflows',
277
+ markerFile: '.qwen'
278
+ },
279
+ {
280
+ name: 'Trae CN',
281
+ value: 'trae-cn',
282
+ rulesDir: '.trae-cn/rules',
283
+ skillsDir: '.trae-cn/skills',
284
+ workflowsDir: '.trae-cn/workflows',
285
+ markerFile: '.trae-cn'
286
+ },
287
+ {
288
+ name: 'Zencoder',
289
+ value: 'zencoder',
290
+ rulesDir: '.zencoder/rules',
291
+ skillsDir: '.zencoder/skills',
292
+ workflowsDir: '.zencoder/workflows',
293
+ markerFile: '.zencoder'
294
+ },
295
+ {
296
+ name: 'Neovate',
297
+ value: 'neovate',
298
+ rulesDir: '.neovate/rules',
299
+ skillsDir: '.neovate/skills',
300
+ workflowsDir: '.neovate/workflows',
301
+ markerFile: '.neovate'
302
+ },
303
+ {
304
+ name: 'Pochi',
305
+ value: 'pochi',
306
+ rulesDir: '.pochi/rules',
307
+ skillsDir: '.pochi/skills',
308
+ workflowsDir: '.pochi/workflows',
309
+ markerFile: '.pochi'
310
+ },
311
+ {
312
+ name: 'AdaL',
313
+ value: 'adal',
314
+ rulesDir: '.adal/rules',
315
+ skillsDir: '.adal/skills',
316
+ workflowsDir: '.adal/workflows',
317
+ markerFile: '.adal'
318
+ },
319
+ {
320
+ name: 'Generic/Other',
321
+ value: 'generic',
322
+ rulesDir: '.agents/rules',
323
+ skillsDir: '.agents/skills',
324
+ workflowsDir: '.agents/workflows',
325
+ markerFile: '.agents'
326
+ },
327
+ {
328
+ name: 'Custom Path (Manual)',
329
+ value: 'custom',
330
+ rulesDir: '',
331
+ skillsDir: '',
332
+ workflowsDir: '',
333
+ markerFile: ''
334
+ }
335
+ ];
336
+
337
+ program
338
+ .name('lmagent-skills')
339
+ .description('CLI para instalar skills y reglas de LMAgent')
340
+ .version('2.5.0'); // Version bump
341
+
342
+ program.command('install')
343
+ .description('Instalar skills, rules y workflows en el IDE del proyecto')
344
+ .option('-f, --force', 'Forzar instalación')
345
+ .option('-y, --yes', 'Instalar todo sin preguntar')
346
+ .action((options) => {
347
+ runInstall(options);
348
+ });
349
+
350
+ program.command('update')
351
+ .description('Actualizar skills y reglas en el proyecto (alias de install)')
352
+ .option('-f, --force', 'Forzar actualización')
353
+ .option('-y, --yes', 'Instalar todo sin preguntar')
354
+ .action((options) => {
355
+ console.log(chalk.blue('ℹ Iniciando actualización...'));
356
+ runInstall(options);
357
+ });
358
+
359
+ program.command('init')
360
+ .description('Inicializar proyecto con LMAgent (copia CLAUDE.md, AGENTS.md, config, etc.)')
361
+ .option('-f, --force', 'Sobrescribir archivos existentes')
362
+ .option('-y, --yes', 'No preguntar, instalar todo')
363
+ .action((options) => {
364
+ runInit(options);
365
+ });
366
+
367
+ program.command('doctor')
368
+ .description('Verificar que el proyecto está correctamente configurado')
369
+ .action(() => {
370
+ runDoctor();
371
+ });
372
+
373
+ program.command('validate')
374
+ .description('Validar integridad de todos los skills (frontmatter, estructura)')
375
+ .argument('[skill]', 'Nombre parcial del skill a validar (opcional)')
376
+ .action((skill) => {
377
+ const { execSync } = require('child_process');
378
+ const scriptPath = path.join(__dirname, 'scripts', 'validate_skills.js');
379
+ const args = skill ? ` ${skill}` : '';
380
+ try {
381
+ execSync(`node "${scriptPath}"${args}`, { stdio: 'inherit' });
382
+ } catch (e) {
383
+ process.exit(e.status || 1);
384
+ }
385
+ });
386
+
387
+ program.command('create-skill')
388
+ .description('Crear un nuevo skill interactivamente')
389
+ .action(() => {
390
+ const { execSync } = require('child_process');
391
+ const scriptPath = path.join(__dirname, 'scripts', 'create_skill.js');
392
+ try {
393
+ execSync(`node "${scriptPath}"`, { stdio: 'inherit' });
394
+ } catch (e) {
395
+ process.exit(e.status || 1);
396
+ }
397
+ });
398
+
399
+ if (process.argv.length === 2) {
400
+ runInstall({});
401
+ } else {
402
+ program.parse();
403
+ }
404
+
405
+ async function runInstall(options) {
406
+ console.clear();
407
+ const branding = figlet.textSync('LMAGENT', { font: 'ANSI Shadow' });
408
+ console.log(gradient.pastel.multiline(branding));
409
+ console.log(gradient.cristal(' by QuBit\n'));
410
+
411
+ const projectRoot = process.cwd();
412
+ const userHome = os.homedir();
413
+ const globalAgentDir = path.join(userHome, '.agents');
414
+ const globalSkillsDir = path.join(globalAgentDir, 'skills');
415
+ const globalRulesDir = path.join(globalAgentDir, 'rules');
416
+ const globalWorkflowsDir = path.join(globalAgentDir, 'workflows'); // New
417
+
418
+ // 1. Sincronizar (Copiar) Package -> Global Repo (~/.agents)
419
+ console.log(chalk.cyan('🔄 Sincronizando repositorio global (~/.agents)...'));
420
+ try {
421
+ if (!fs.existsSync(globalAgentDir)) fs.mkdirSync(globalAgentDir, { recursive: true });
422
+
423
+ if (fs.existsSync(PACKAGE_SKILLS_DIR)) {
424
+ copyRecursiveSync(PACKAGE_SKILLS_DIR, globalSkillsDir, true);
425
+ }
426
+ if (fs.existsSync(PACKAGE_RULES_DIR)) {
427
+ copyRecursiveSync(PACKAGE_RULES_DIR, globalRulesDir, true);
428
+ }
429
+ if (fs.existsSync(PACKAGE_WORKFLOWS_DIR)) {
430
+ copyRecursiveSync(PACKAGE_WORKFLOWS_DIR, globalWorkflowsDir, true);
431
+ }
432
+ console.log(chalk.green('✔ Repositorio global sincronizado correctamente.'));
433
+ } catch (e) {
434
+ console.error(chalk.red(`❌ Error al sincronizar repositorio global: ${e.message}`));
435
+ }
436
+ console.log('');
437
+
438
+ const SOURCE_SKILLS = fs.existsSync(globalSkillsDir) ? globalSkillsDir : PACKAGE_SKILLS_DIR;
439
+ const SOURCE_RULES = fs.existsSync(globalRulesDir) ? globalRulesDir : PACKAGE_RULES_DIR;
440
+ const SOURCE_WORKFLOWS = fs.existsSync(globalWorkflowsDir) ? globalWorkflowsDir : PACKAGE_WORKFLOWS_DIR;
441
+
442
+ let targetIdes = [];
443
+ let selectedSkills = [];
444
+ let selectedRules = [];
445
+ let selectedWorkflows = []; // New
446
+ let installMethod = 'symlink';
447
+ let installTarget = 'project';
448
+ let targetRoot = projectRoot;
449
+
450
+ if (options.yes) {
451
+ console.log(chalk.yellow('⚡ Modo: No interactivo'));
452
+ targetIdes = IDE_CONFIGS.filter(ide =>
453
+ ide.value !== 'custom' && (fs.existsSync(path.join(projectRoot, ide.rulesDir ? ide.rulesDir.split('/')[0] : '')) || (ide.markerFile && fs.existsSync(path.join(projectRoot, ide.markerFile))))
454
+ );
455
+ if (targetIdes.length === 0 && options.force) {
456
+ targetIdes = [IDE_CONFIGS.find(i => i.value === 'cursor')];
457
+ }
458
+ selectedSkills = getAllItems(SOURCE_SKILLS, true);
459
+ selectedRules = getAllItems(SOURCE_RULES, false);
460
+ selectedWorkflows = getAllItems(SOURCE_WORKFLOWS, false);
461
+ } else {
462
+ console.log(chalk.cyan('🔹 Configuración de Instalación'));
463
+
464
+ const targetAnswer = await inquirer.prompt([
465
+ {
466
+ type: 'list',
467
+ name: 'target',
468
+ message: '¿Dónde quieres instalar los artefactos?',
469
+ choices: [
470
+ { name: 'En mi Usuario / Global IDE Config (~/) (Recomendado)', value: 'user' },
471
+ { name: 'En este Proyecto (./)', value: 'project' }
472
+ ]
473
+ }
474
+ ]);
475
+ installTarget = targetAnswer.target;
476
+ targetRoot = (installTarget === 'user') ? userHome : projectRoot;
477
+
478
+ const methodAnswer = await inquirer.prompt([
479
+ {
480
+ type: 'list',
481
+ name: 'method',
482
+ message: 'Método de Instalación:',
483
+ choices: [
484
+ { name: 'Symlink (Recomendado - Live Updates)', value: 'symlink' },
485
+ { name: 'Copia Física (Archivos independientes)', value: 'copy' }
486
+ ],
487
+ default: 'symlink'
488
+ }
489
+ ]);
490
+ installMethod = methodAnswer.method;
491
+
492
+ const ideAnswer = await inquirer.prompt([{
493
+ type: 'checkbox',
494
+ name: 'ides',
495
+ message: `¿En qué Agentes/IDEs quieres instalar? (Target: ${installTarget === 'project' ? 'Project' : 'User Home'})`,
496
+ choices: IDE_CONFIGS.map(ide => {
497
+ const searchPath = ide.rulesDir ? path.join(targetRoot, ide.rulesDir.split('/')[0]) : null;
498
+ const detected = searchPath && fs.existsSync(searchPath);
499
+ const markerDetected = ide.markerFile && fs.existsSync(path.join(targetRoot, ide.markerFile));
500
+
501
+ return {
502
+ name: ide.name + ((detected || markerDetected) ? chalk.green(' (Detectado)') : ''),
503
+ value: ide.value,
504
+ checked: (detected || markerDetected)
505
+ };
506
+ })
507
+ }]);
508
+
509
+ if (ideAnswer.ides.length === 0) {
510
+ console.log(chalk.yellow('⚠️ Ningún IDE seleccionado. Saliendo...'));
511
+ return;
512
+ }
513
+
514
+ targetIdes = [];
515
+ for (const ideValue of ideAnswer.ides) {
516
+ if (ideValue === 'custom') {
517
+ const customPath = await inquirer.prompt([{
518
+ type: 'input',
519
+ name: 'path',
520
+ message: 'Ingresa la ruta de Rules:',
521
+ validate: (input) => input.length > 0 ? true : 'Requerido'
522
+ }, {
523
+ type: 'input',
524
+ name: 'skillsPath',
525
+ message: 'Ingresa la ruta de Skills (Opcional):'
526
+ }, {
527
+ type: 'input',
528
+ name: 'workflowsPath',
529
+ message: 'Ingresa la ruta de Workflows (Opcional):'
530
+ }]);
531
+ targetIdes.push({
532
+ name: 'Custom',
533
+ value: 'custom',
534
+ rulesDir: customPath.path,
535
+ skillsDir: customPath.skillsPath || customPath.path,
536
+ workflowsDir: customPath.workflowsPath || customPath.path // fallback to rules
537
+ });
538
+ } else {
539
+ targetIdes.push(IDE_CONFIGS.find(i => i.value === ideValue));
540
+ }
541
+ }
542
+
543
+ const availableSkills = getAllItems(SOURCE_SKILLS, true);
544
+ const availableRules = getAllItems(SOURCE_RULES, false);
545
+ const availableWorkflows = getAllItems(SOURCE_WORKFLOWS, false);
546
+
547
+ const contentAnswers = await inquirer.prompt([
548
+ {
549
+ type: 'checkbox',
550
+ name: 'skills',
551
+ message: 'Selecciona los Skills:',
552
+ choices: availableSkills.map(s => ({ name: s, checked: true })),
553
+ pageSize: 15
554
+ },
555
+ {
556
+ type: 'checkbox',
557
+ name: 'rules',
558
+ message: 'Selecciona las Reglas:',
559
+ choices: availableRules.map(r => ({ name: r, checked: true })),
560
+ pageSize: 15
561
+ },
562
+ {
563
+ type: 'checkbox',
564
+ name: 'workflows',
565
+ message: 'Selecciona los Workflows:',
566
+ choices: availableWorkflows.map(w => ({ name: w, checked: true })),
567
+ pageSize: 15
568
+ }
569
+ ]);
570
+ selectedSkills = contentAnswers.skills;
571
+ selectedRules = contentAnswers.rules;
572
+ selectedWorkflows = contentAnswers.workflows;
573
+
574
+ const { confirm } = await inquirer.prompt([{
575
+ type: 'confirm',
576
+ name: 'confirm',
577
+ message: '¿Proceder con la instalación?',
578
+ default: true
579
+ }]);
580
+ if (!confirm) return;
581
+ }
582
+
583
+ console.log('');
584
+ for (const ide of targetIdes) {
585
+ let currentInstallMethod = installMethod;
586
+ if (ide.value === 'cursor' && currentInstallMethod === 'symlink') {
587
+ console.log(chalk.yellow(`⚠️ Cursor detectado: Forzando método 'copy' (Skills no soportan symlinks en Cursor)`));
588
+ currentInstallMethod = 'copy';
589
+ }
590
+
591
+ // 1. Install SKILLS (Folders)
592
+ if (selectedSkills.length > 0 && ide.skillsDir) {
593
+ const targetDir = path.join(targetRoot, ide.skillsDir);
594
+ console.log(chalk.bold(`\nInstalling Skills to ${chalk.cyan(targetDir)}:`));
595
+
596
+ try {
597
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
598
+
599
+ for (const skill of selectedSkills) {
600
+ const srcFolder = path.join(SOURCE_SKILLS, skill);
601
+ const destFolder = path.join(targetDir, skill);
602
+
603
+ if (fs.existsSync(srcFolder)) {
604
+ await applyFile(srcFolder, destFolder, currentInstallMethod);
605
+ console.log(` ${chalk.green('✔')} ${skill}/`);
606
+ }
607
+ }
608
+ } catch (e) {
609
+ console.error(chalk.red(`❌ Error installing skills for ${ide.name}: ${e.message}`));
610
+ }
611
+ }
612
+
613
+ // 2. Install RULES (Files)
614
+ if (selectedRules.length > 0 && ide.rulesDir) {
615
+ const targetDir = path.join(targetRoot, ide.rulesDir);
616
+ console.log(chalk.bold(`\nInstalling Rules to ${chalk.cyan(targetDir)}:`));
617
+
618
+ try {
619
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
620
+
621
+ for (const rule of selectedRules) {
622
+ const srcVal = path.join(SOURCE_RULES, rule);
623
+ const destVal = path.join(targetDir, rule);
624
+
625
+ if (fs.existsSync(srcVal)) {
626
+ await applyFile(srcVal, destVal, currentInstallMethod);
627
+ console.log(` ${chalk.blue('✔')} ${rule}`);
628
+ }
629
+ }
630
+ } catch (e) {
631
+ console.error(chalk.red(`❌ Error installing rules for ${ide.name}: ${e.message}`));
632
+ }
633
+ }
634
+
635
+ // 3. Install WORKFLOWS (Files)
636
+ if (selectedWorkflows.length > 0 && ide.workflowsDir) {
637
+ const targetDir = path.join(targetRoot, ide.workflowsDir);
638
+ console.log(chalk.bold(`\nInstalling Workflows to ${chalk.cyan(targetDir)}:`));
639
+
640
+ try {
641
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
642
+
643
+ for (const wf of selectedWorkflows) {
644
+ const srcVal = path.join(SOURCE_WORKFLOWS, wf);
645
+ const destVal = path.join(targetDir, wf);
646
+
647
+ if (fs.existsSync(srcVal)) {
648
+ await applyFile(srcVal, destVal, currentInstallMethod);
649
+ console.log(` ${chalk.magenta('✔')} ${wf}`);
650
+ }
651
+ }
652
+ } catch (e) {
653
+ console.error(chalk.red(`❌ Error installing workflows for ${ide.name}: ${e.message}`));
654
+ }
655
+ }
656
+ }
657
+ console.log(gradient.pastel.multiline('\n✨ Instalación Finalizada ✨'));
658
+ }
659
+
660
+ async function applyFile(source, dest, method) {
661
+ if (fs.existsSync(dest) || (fs.existsSync(path.dirname(dest)) && fs.readdirSync(path.dirname(dest)).includes(path.basename(dest)))) {
662
+ try {
663
+ const stat = fs.statSync(dest);
664
+ if (stat.isDirectory()) {
665
+ fs.rmSync(dest, { recursive: true, force: true });
666
+ } else {
667
+ fs.unlinkSync(dest);
668
+ }
669
+ } catch (e) { }
670
+ }
671
+
672
+ const destDir = path.dirname(dest);
673
+ if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
674
+
675
+ const srcStat = fs.statSync(source);
676
+ const isDir = srcStat.isDirectory();
677
+
678
+ if (method === 'symlink') {
679
+ try {
680
+ const type = isDir ? 'junction' : 'file';
681
+ fs.symlinkSync(source, dest, type);
682
+ } catch (e) {
683
+ try {
684
+ if (isDir) {
685
+ copyRecursiveSync(source, dest, true);
686
+ } else {
687
+ fs.copyFileSync(source, dest);
688
+ }
689
+ const isWindows = os.platform() === 'win32';
690
+ const msg = isWindows && !isDir
691
+ ? `(Symlink falló [Requiere Admin/DevMode en Win]. Copiado.)`
692
+ : `(Symlink falló, se usó copia)`;
693
+ console.log(chalk.yellow(` ${msg}`));
694
+ } catch (err) {
695
+ console.error(chalk.red(` Error copiando ${path.basename(dest)}: ${err.message}`));
696
+ }
697
+ }
698
+ } else {
699
+ if (isDir) {
700
+ copyRecursiveSync(source, dest, true);
701
+ } else {
702
+ fs.copyFileSync(source, dest);
703
+ }
704
+ }
705
+ }
706
+
707
+ function copyRecursiveSync(src, dest, overwrite) {
708
+ if (fs.cpSync) {
709
+ try {
710
+ fs.cpSync(src, dest, { recursive: true, force: overwrite, errorOnExist: false });
711
+ } catch (e) {
712
+ console.error(chalk.red(`Error copying (cpSync) ${path.basename(src)}: ${e.message}`));
713
+ // Fallback manual implementation just in case
714
+ if (fs.existsSync(src)) {
715
+ const stat = fs.statSync(src);
716
+ if (stat.isDirectory()) {
717
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
718
+ fs.readdirSync(src).forEach(child => {
719
+ copyRecursiveSync(path.join(src, child), path.join(dest, child), overwrite);
720
+ });
721
+ } else {
722
+ fs.copyFileSync(src, dest);
723
+ }
724
+ }
725
+ }
726
+ } else {
727
+ // Fallback for older Node versions
728
+ const exists = fs.existsSync(src);
729
+ const stats = exists && fs.statSync(src);
730
+ const isDirectory = exists && stats.isDirectory();
731
+ if (isDirectory) {
732
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
733
+ fs.readdirSync(src).forEach(function (childItemName) {
734
+ copyRecursiveSync(path.join(src, childItemName),
735
+ path.join(dest, childItemName), overwrite);
736
+ });
737
+ } else {
738
+ if (overwrite || !fs.existsSync(dest)) {
739
+ fs.copyFileSync(src, dest);
740
+ }
741
+ }
742
+ }
743
+ }
744
+
745
+ function getAllItems(dir, isNested) {
746
+ if (!fs.existsSync(dir)) return [];
747
+ const items = fs.readdirSync(dir);
748
+ if (isNested) {
749
+ return items.filter(item => {
750
+ const p = path.join(dir, item);
751
+ return fs.statSync(p).isDirectory() && fs.existsSync(path.join(p, 'SKILL.md'));
752
+ });
753
+ } else {
754
+ return items.filter(item => item.endsWith('.md') || item.endsWith('.txt') || item.endsWith('.cursorrules') || item.endsWith('.toml'));
755
+ }
756
+ }
757
+
758
+ // ============================================
759
+ // INIT: Inicializar proyecto con LMAgent
760
+ // ============================================
761
+
762
+ async function runInit(options) {
763
+ console.clear();
764
+ const branding = figlet.textSync('LMAGENT', { font: 'ANSI Shadow' });
765
+ console.log(gradient.pastel.multiline(branding));
766
+ console.log(gradient.cristal(' by QuBit\n'));
767
+
768
+ const projectRoot = process.cwd();
769
+ console.log(chalk.cyan(`📦 Inicializando proyecto LMAgent en: ${chalk.bold(projectRoot)}\n`));
770
+
771
+ // Verificar si ya está inicializado
772
+ const agentsExists = fs.existsSync(path.join(projectRoot, 'AGENTS.md'));
773
+ if (agentsExists && !options.force) {
774
+ console.log(chalk.yellow('⚠️ Este proyecto ya tiene AGENTS.md'));
775
+ if (!options.yes) {
776
+ const { overwrite } = await inquirer.prompt([{
777
+ type: 'confirm',
778
+ name: 'overwrite',
779
+ message: '¿Sobrescribir archivos existentes?',
780
+ default: false
781
+ }]);
782
+ if (!overwrite) {
783
+ console.log(chalk.yellow('Cancelado. Usa --force para forzar.'));
784
+ return;
785
+ }
786
+ }
787
+ }
788
+
789
+ let filesToCopy = [...INIT_FILES];
790
+ let dirsToCopy = [...INIT_DIRS];
791
+
792
+ // Modo interactivo: preguntar qué copiar
793
+ if (!options.yes) {
794
+ const answers = await inquirer.prompt([
795
+ {
796
+ type: 'checkbox',
797
+ name: 'files',
798
+ message: 'Archivos de entry point a copiar:',
799
+ choices: INIT_FILES.map(f => ({
800
+ name: `${f.src} - ${f.desc}`,
801
+ value: f.src,
802
+ checked: true
803
+ }))
804
+ },
805
+ {
806
+ type: 'checkbox',
807
+ name: 'dirs',
808
+ message: 'Directorios a copiar:',
809
+ choices: INIT_DIRS.map(d => ({
810
+ name: `${d.src}/ - ${d.desc}`,
811
+ value: d.src,
812
+ checked: true
813
+ }))
814
+ }
815
+ ]);
816
+ filesToCopy = INIT_FILES.filter(f => answers.files.includes(f.src));
817
+ dirsToCopy = INIT_DIRS.filter(d => answers.dirs.includes(d.src));
818
+ }
819
+
820
+ // Copiar archivos individuales
821
+ console.log(chalk.bold('\n📄 Copiando archivos de proyecto:'));
822
+ for (const file of filesToCopy) {
823
+ const src = path.join(__dirname, file.src);
824
+ const dest = path.join(projectRoot, file.src);
825
+ if (fs.existsSync(src)) {
826
+ const exists = fs.existsSync(dest);
827
+ fs.copyFileSync(src, dest);
828
+ console.log(` ${chalk.green('✔')} ${file.src} ${exists ? chalk.yellow('(actualizado)') : chalk.green('(nuevo)')}`);
829
+ } else {
830
+ console.log(` ${chalk.red('✘')} ${file.src} ${chalk.red('(no encontrado en el paquete)')}`);
831
+ }
832
+ }
833
+
834
+ // Copiar directorios
835
+ console.log(chalk.bold('\n📁 Copiando directorios:'));
836
+ for (const dir of dirsToCopy) {
837
+ const src = path.join(__dirname, dir.src);
838
+ const dest = path.join(projectRoot, dir.src);
839
+ if (fs.existsSync(src)) {
840
+ const exists = fs.existsSync(dest);
841
+ copyRecursiveSync(src, dest, true);
842
+ const itemCount = getAllItemsFlat(src).length;
843
+ console.log(` ${chalk.green('✔')} ${dir.src}/ (${itemCount} archivos) ${exists ? chalk.yellow('(actualizado)') : chalk.green('(nuevo)')}`);
844
+ } else {
845
+ console.log(` ${chalk.red('✘')} ${dir.src}/ ${chalk.red('(no encontrado en el paquete)')}`);
846
+ }
847
+ }
848
+
849
+ // Crear .env.example si no existe
850
+ const envExampleDest = path.join(projectRoot, '.env.example');
851
+ if (!fs.existsSync(envExampleDest)) {
852
+ const envContent = `# ============================================
853
+ # LMAgent Project - Environment Variables
854
+ # ============================================
855
+ # Copiar este archivo a .env y completar los valores
856
+
857
+ # Database
858
+ DATABASE_URL=postgresql://user:password@localhost:5432/dbname
859
+
860
+ # Redis
861
+ REDIS_URL=redis://localhost:6379
862
+
863
+ # Security
864
+ JWT_SECRET=change-this-to-a-strong-secret-minimum-32-characters
865
+
866
+ # LLM API Keys
867
+ OPENAI_API_KEY=sk-...
868
+ ANTHROPIC_API_KEY=sk-ant-...
869
+ GOOGLE_API_KEY=...
870
+
871
+ # n8n (si aplica)
872
+ N8N_WEBHOOK_URL=https://n8n.yourserver.com/webhook
873
+
874
+ # Environment
875
+ ENVIRONMENT=development
876
+ DEBUG=true
877
+ `;
878
+ fs.writeFileSync(envExampleDest, envContent);
879
+ console.log(` ${chalk.green('✔')} .env.example ${chalk.green('(nuevo)')}`);
880
+ } else {
881
+ console.log(` ${chalk.blue('ℹ')} .env.example ya existe, no se sobrescribe`);
882
+ }
883
+
884
+ // Resumen
885
+ console.log(gradient.pastel.multiline('\n✨ Proyecto inicializado con LMAgent v2.3.0 ✨'));
886
+ console.log('');
887
+ console.log(chalk.cyan('Próximos pasos:'));
888
+ console.log(` 1. ${chalk.bold('lmagent install')} - Instalar skills/rules/workflows en tu IDE`);
889
+ console.log(` 2. Editar ${chalk.bold('.env.example')} → ${chalk.bold('.env')} con tus credenciales`);
890
+ console.log(` 3. Leer ${chalk.bold('AGENTS.md')} para conocer las capacidades disponibles`);
891
+ console.log('');
892
+
893
+ // Preguntar si quiere ejecutar install también
894
+ if (!options.yes) {
895
+ const { runInstallNow } = await inquirer.prompt([{
896
+ type: 'confirm',
897
+ name: 'runInstallNow',
898
+ message: '¿Ejecutar lmagent install ahora para conectar al IDE?',
899
+ default: true
900
+ }]);
901
+ if (runInstallNow) {
902
+ console.log('');
903
+ await runInstall(options);
904
+ }
905
+ } else {
906
+ console.log(chalk.cyan('💡 Ejecuta `lmagent install` para conectar al IDE.\n'));
907
+ }
908
+ }
909
+
910
+ // ============================================
911
+ // DOCTOR: Verificar configuración del proyecto
912
+ // ============================================
913
+
914
+ async function runDoctor() {
915
+ console.clear();
916
+ const branding = figlet.textSync('LMAGENT', { font: 'ANSI Shadow' });
917
+ console.log(gradient.pastel.multiline(branding));
918
+ console.log(gradient.cristal(' Doctor - Diagnóstico\n'));
919
+
920
+ const projectRoot = process.cwd();
921
+ let issues = 0;
922
+ let ok = 0;
923
+
924
+ console.log(chalk.bold('🔍 Verificando proyecto en: ' + chalk.cyan(projectRoot) + '\n'));
925
+
926
+ // 1. Archivos de entry point
927
+ console.log(chalk.bold('📄 Entry Points:'));
928
+ for (const file of INIT_FILES) {
929
+ const exists = fs.existsSync(path.join(projectRoot, file.src));
930
+ if (exists) {
931
+ console.log(` ${chalk.green('✔')} ${file.src}`);
932
+ ok++;
933
+ } else {
934
+ console.log(` ${chalk.red('✘')} ${file.src} - ${chalk.red('FALTANTE')} → ejecuta ${chalk.bold('lmagent init')}`);
935
+ issues++;
936
+ }
937
+ }
938
+
939
+ // 2. Directorios de configuración
940
+ console.log(chalk.bold('\n📁 Configuración:'));
941
+ for (const dir of INIT_DIRS) {
942
+ const exists = fs.existsSync(path.join(projectRoot, dir.src));
943
+ if (exists) {
944
+ console.log(` ${chalk.green('✔')} ${dir.src}/`);
945
+ ok++;
946
+ } else {
947
+ console.log(` ${chalk.yellow('⚠')} ${dir.src}/ - Opcional, ejecuta ${chalk.bold('lmagent init')} para copiar`);
948
+ }
949
+ }
950
+
951
+ // 3. Detectar IDEs configurados
952
+ console.log(chalk.bold('\n🔧 IDEs detectados:'));
953
+ let ideFound = false;
954
+ for (const ide of IDE_CONFIGS) {
955
+ if (ide.value === 'custom') continue;
956
+ const rulesExist = ide.rulesDir && fs.existsSync(path.join(projectRoot, ide.rulesDir));
957
+ const skillsExist = ide.skillsDir && fs.existsSync(path.join(projectRoot, ide.skillsDir));
958
+ const markerExist = ide.markerFile && fs.existsSync(path.join(projectRoot, ide.markerFile));
959
+
960
+ if (rulesExist || skillsExist || markerExist) {
961
+ ideFound = true;
962
+ const parts = [];
963
+ if (rulesExist) parts.push('rules');
964
+ if (skillsExist) parts.push('skills');
965
+ console.log(` ${chalk.green('✔')} ${ide.name} (${parts.join(', ')})`);
966
+ ok++;
967
+
968
+ // Verificar que tiene los skills correctos
969
+ if (skillsExist) {
970
+ const installedSkills = fs.readdirSync(path.join(projectRoot, ide.skillsDir))
971
+ .filter(item => fs.statSync(path.join(projectRoot, ide.skillsDir, item)).isDirectory());
972
+
973
+ // Calcular skills esperados dinámicamente
974
+ const expectedSkillsCount = getAllItems(PACKAGE_SKILLS_DIR, true).length;
975
+ const skillCount = installedSkills.length;
976
+
977
+ if (skillCount < expectedSkillsCount) {
978
+ console.log(` ${chalk.yellow('⚠')} Solo ${skillCount}/${expectedSkillsCount} skills instalados → ejecuta ${chalk.bold('lmagent install')}`);
979
+ } else {
980
+ console.log(` ${chalk.green('✔')} ${skillCount} skills instalados`);
981
+ }
982
+ }
983
+ }
984
+ }
985
+ if (!ideFound) {
986
+ console.log(` ${chalk.red('✘')} Ningún IDE detectado → ejecuta ${chalk.bold('lmagent install')}`);
987
+ issues++;
988
+ }
989
+
990
+ // 4. Verificar .env
991
+ console.log(chalk.bold('\n🔒 Seguridad:'));
992
+ const envExists = fs.existsSync(path.join(projectRoot, '.env'));
993
+ const envExampleExists = fs.existsSync(path.join(projectRoot, '.env.example'));
994
+ const gitignoreExists = fs.existsSync(path.join(projectRoot, '.gitignore'));
995
+
996
+ if (envExampleExists) {
997
+ console.log(` ${chalk.green('✔')} .env.example existe`);
998
+ ok++;
999
+ } else {
1000
+ console.log(` ${chalk.yellow('⚠')} .env.example no encontrado`);
1001
+ }
1002
+
1003
+ if (envExists) {
1004
+ console.log(` ${chalk.green('✔')} .env existe`);
1005
+ ok++;
1006
+ } else {
1007
+ console.log(` ${chalk.yellow('⚠')} .env no encontrado (necesario para ejecutar)`);
1008
+ }
1009
+
1010
+ if (gitignoreExists) {
1011
+ const gitignore = fs.readFileSync(path.join(projectRoot, '.gitignore'), 'utf-8');
1012
+ if (gitignore.includes('.env')) {
1013
+ console.log(` ${chalk.green('✔')} .env está en .gitignore`);
1014
+ ok++;
1015
+ } else {
1016
+ console.log(` ${chalk.red('✘')} .env NO está en .gitignore → ${chalk.red('RIESGO DE SEGURIDAD')}`);
1017
+ issues++;
1018
+ }
1019
+ }
1020
+
1021
+ // Resumen
1022
+ console.log('');
1023
+ if (issues === 0) {
1024
+ console.log(gradient.pastel(`\n✨ Todo en orden! ${ok} verificaciones pasadas.\n`));
1025
+ } else {
1026
+ console.log(chalk.yellow(`\n⚠️ ${issues} problema(s) encontrado(s), ${ok} verificaciones OK.\n`));
1027
+ }
1028
+ }
1029
+
1030
+ // Helper: Contar archivos recursivamente
1031
+ function getAllItemsFlat(dir) {
1032
+ let results = [];
1033
+ if (!fs.existsSync(dir)) return results;
1034
+ const items = fs.readdirSync(dir);
1035
+ for (const item of items) {
1036
+ const fullPath = path.join(dir, item);
1037
+ if (fs.statSync(fullPath).isDirectory()) {
1038
+ results = results.concat(getAllItemsFlat(fullPath));
1039
+ } else {
1040
+ results.push(fullPath);
1041
+ }
1042
+ }
1043
+ return results;
1044
+ }