@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.
- package/.editorconfig +18 -0
- package/AGENTS.md +169 -0
- package/CLAUDE.md +122 -0
- package/CONTRIBUTING.md +90 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/config/commands.yaml +194 -0
- package/config/levels.yaml +135 -0
- package/config/models.yaml +192 -0
- package/config/settings.yaml +405 -0
- package/config/tools-extended.yaml +534 -0
- package/config/tools.yaml +437 -0
- package/docs/assets/logo.png +0 -0
- package/docs/commands.md +132 -0
- package/docs/customization-guide.md +445 -0
- package/docs/getting-started.md +154 -0
- package/docs/how-to-start.md +242 -0
- package/docs/navigation-index.md +227 -0
- package/docs/usage-guide.md +113 -0
- package/install.js +1044 -0
- package/package.json +35 -0
- package/pyproject.toml +182 -0
- package/rules/_bootstrap.md +138 -0
- package/rules/agents-ia.md +607 -0
- package/rules/api-design.md +337 -0
- package/rules/automations-n8n.md +646 -0
- package/rules/code-style.md +570 -0
- package/rules/documentation.md +98 -0
- package/rules/security.md +316 -0
- package/rules/stack.md +395 -0
- package/rules/testing.md +326 -0
- package/rules/workflow.md +353 -0
- package/scripts/create_skill.js +300 -0
- package/scripts/validate_skills.js +283 -0
- package/skills/ai-agent-engineer/SKILL.md +394 -0
- package/skills/ai-agent-engineer/references/agent-patterns.md +149 -0
- package/skills/api-designer/SKILL.md +429 -0
- package/skills/api-designer/references/api-standards.md +13 -0
- package/skills/architect/SKILL.md +285 -0
- package/skills/architect/references/c4-model.md +133 -0
- package/skills/automation-engineer/SKILL.md +352 -0
- package/skills/automation-engineer/references/n8n-patterns.md +127 -0
- package/skills/backend-engineer/SKILL.md +261 -0
- package/skills/backend-engineer/assets/fastapi-project-structure.yaml +74 -0
- package/skills/backend-engineer/references/debugging-guide.md +174 -0
- package/skills/backend-engineer/references/design-patterns.md +208 -0
- package/skills/backend-engineer/scripts/scaffold_backend.py +313 -0
- package/skills/bmad-methodology/SKILL.md +202 -0
- package/skills/bmad-methodology/references/scale-adaptive-levels.md +141 -0
- package/skills/browser-agent/SKILL.md +502 -0
- package/skills/browser-agent/scripts/playwright_setup.ts +16 -0
- package/skills/code-reviewer/SKILL.md +306 -0
- package/skills/code-reviewer/references/code-review-checklist.md +16 -0
- package/skills/data-engineer/SKILL.md +474 -0
- package/skills/data-engineer/assets/pg-monitoring-queries.sql +154 -0
- package/skills/data-engineer/references/index-strategy.md +128 -0
- package/skills/data-engineer/scripts/backup_postgres.py +221 -0
- package/skills/devops-engineer/SKILL.md +547 -0
- package/skills/devops-engineer/references/ci-cd-patterns.md +265 -0
- package/skills/devops-engineer/scripts/docker_healthcheck.py +125 -0
- package/skills/document-generator/SKILL.md +746 -0
- package/skills/document-generator/references/pdf-generation.md +22 -0
- package/skills/frontend-engineer/SKILL.md +532 -0
- package/skills/frontend-engineer/references/accessibility-guide.md +146 -0
- package/skills/frontend-engineer/scripts/audit_bundle.py +144 -0
- package/skills/git-workflow/SKILL.md +374 -0
- package/skills/git-workflow/references/git-flow.md +25 -0
- package/skills/mcp-builder/SKILL.md +471 -0
- package/skills/mcp-builder/references/mcp-server-guide.md +23 -0
- package/skills/mobile-engineer/SKILL.md +502 -0
- package/skills/mobile-engineer/references/platform-guidelines.md +160 -0
- package/skills/orchestrator/SKILL.md +246 -0
- package/skills/orchestrator/references/methodology-routing.md +117 -0
- package/skills/orchestrator/references/persona-mapping.md +85 -0
- package/skills/orchestrator/references/routing-logic.md +110 -0
- package/skills/performance-engineer/SKILL.md +549 -0
- package/skills/performance-engineer/references/caching-patterns.md +181 -0
- package/skills/performance-engineer/scripts/profile_endpoint.py +170 -0
- package/skills/product-manager/SKILL.md +488 -0
- package/skills/product-manager/references/prioritization-frameworks.md +126 -0
- package/skills/prompt-engineer/SKILL.md +433 -0
- package/skills/prompt-engineer/references/prompt-patterns.md +158 -0
- package/skills/qa-engineer/SKILL.md +441 -0
- package/skills/qa-engineer/references/testing-strategy.md +166 -0
- package/skills/qa-engineer/scripts/run_coverage.py +147 -0
- package/skills/scrum-master/SKILL.md +225 -0
- package/skills/scrum-master/references/sprint-ceremonies.md +159 -0
- package/skills/security-analyst/SKILL.md +390 -0
- package/skills/security-analyst/references/owasp-top10.md +188 -0
- package/skills/security-analyst/scripts/audit_security.py +242 -0
- package/skills/seo-auditor/SKILL.md +523 -0
- package/skills/seo-auditor/references/seo-checklist.md +17 -0
- package/skills/spec-driven-dev/SKILL.md +342 -0
- package/skills/spec-driven-dev/references/phase-gates.md +107 -0
- package/skills/supabase-expert/SKILL.md +602 -0
- package/skills/supabase-expert/references/supabase-patterns.md +19 -0
- package/skills/swe-agent/SKILL.md +311 -0
- package/skills/swe-agent/references/trajectory-format.md +134 -0
- package/skills/systematic-debugger/SKILL.md +512 -0
- package/skills/systematic-debugger/references/debugging-guide.md +12 -0
- package/skills/tech-lead/SKILL.md +409 -0
- package/skills/tech-lead/references/code-review-checklist.md +111 -0
- package/skills/technical-writer/SKILL.md +631 -0
- package/skills/technical-writer/references/doc-templates.md +218 -0
- package/skills/testing-strategist/SKILL.md +476 -0
- package/skills/testing-strategist/references/testing-pyramid.md +16 -0
- package/skills/ux-ui-designer/SKILL.md +419 -0
- package/skills/ux-ui-designer/references/design-system-foundation.md +168 -0
- package/skills_overview.txt +94 -0
- package/templates/PROJECT_KICKOFF.md +284 -0
- package/templates/SKILL_TEMPLATE.md +131 -0
- package/templates/USAGE.md +95 -0
- package/templates/agent-python/README.md +71 -0
- package/templates/agent-python/agent.py +272 -0
- package/templates/agent-python/config.yaml +76 -0
- package/templates/agent-python/prompts/system.md +109 -0
- package/templates/agent-python/requirements.txt +7 -0
- package/templates/automation-n8n/README.md +14 -0
- package/templates/automation-n8n/webhook-handler.json +57 -0
- package/templates/backend-node/Dockerfile +12 -0
- package/templates/backend-node/README.md +15 -0
- package/templates/backend-node/package.json +30 -0
- package/templates/backend-node/src/index.ts +19 -0
- package/templates/backend-node/src/routes.ts +7 -0
- package/templates/backend-node/tsconfig.json +22 -0
- package/templates/backend-python/Dockerfile +11 -0
- package/templates/backend-python/README.md +78 -0
- package/templates/backend-python/app/core/config.py +12 -0
- package/templates/backend-python/app/core/database.py +12 -0
- package/templates/backend-python/app/main.py +17 -0
- package/templates/backend-python/app/routers/__init__.py +1 -0
- package/templates/backend-python/app/routers/health.py +7 -0
- package/templates/backend-python/requirements-dev.txt +6 -0
- package/templates/backend-python/requirements.txt +4 -0
- package/templates/backend-python/tests/test_health.py +9 -0
- package/templates/checkpoint.yaml +117 -0
- package/templates/database/README.md +474 -0
- package/templates/frontend-react/README.md +446 -0
- package/templates/plan.yaml +320 -0
- package/templates/session.yaml +125 -0
- package/templates/spec.yaml +229 -0
- package/templates/tasks.yaml +330 -0
- package/workflows/bugfix-backend.md +380 -0
- package/workflows/documentation.md +232 -0
- package/workflows/generate-prd.md +320 -0
- package/workflows/ideation.md +396 -0
- package/workflows/new-agent-ia.md +497 -0
- package/workflows/new-automation.md +374 -0
- package/workflows/new-feature.md +290 -0
- package/workflows/optimize-performance.md +373 -0
- package/workflows/resolve-github-issue.md +524 -0
- package/workflows/security-review.md +291 -0
- package/workflows/spec-driven.md +476 -0
- package/workflows/testing-strategy.md +296 -0
- 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
|
+
}
|