openprompt-lang 0.4.0 → 0.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 (45) hide show
  1. package/README.md +70 -0
  2. package/bin/cli.js +222 -144
  3. package/bin/create.js +56 -56
  4. package/bin/lint.js +13 -13
  5. package/docs/AI-MANUAL.md +578 -0
  6. package/docs/ROADMAP-DESARROLLO.md +274 -0
  7. package/package.json +3 -2
  8. package/scaffolds/tailwind.config.js +3 -3
  9. package/src/ai/learn-prompt.js +90 -0
  10. package/src/ai/prompt-builder.js +75 -77
  11. package/src/ai/providers.js +74 -74
  12. package/src/annotations/registry.js +11 -11
  13. package/src/commands/ai-gen.js +58 -58
  14. package/src/commands/component.js +152 -94
  15. package/src/commands/context.js +320 -197
  16. package/src/commands/db-rules.js +117 -0
  17. package/src/commands/extract.js +377 -57
  18. package/src/commands/figma.js +37 -37
  19. package/src/commands/init.js +658 -245
  20. package/src/commands/integrate.js +238 -100
  21. package/src/commands/lang.js +26 -26
  22. package/src/commands/learn.js +182 -0
  23. package/src/commands/qa-gen.js +27 -27
  24. package/src/commands/scaffold.js +23 -23
  25. package/src/commands/suggest.js +22 -10
  26. package/src/commands/teach.js +37 -38
  27. package/src/commands/validate.js +93 -53
  28. package/src/commands/wizard.js +187 -185
  29. package/src/generators/figma-prompt.js +16 -16
  30. package/src/knowledge/db-rules.json +162 -0
  31. package/src/language-service/plugin.cjs +45 -45
  32. package/src/mcp-server.js +329 -224
  33. package/src/templates/semantic-index.json +84 -0
  34. package/src/ts-transformer/index.cjs +40 -40
  35. package/src/utils/ai.js +49 -42
  36. package/src/utils/annotations.js +148 -148
  37. package/src/utils/config.js +36 -33
  38. package/src/utils/doc-ingest.js +101 -0
  39. package/src/utils/error-learner.js +41 -39
  40. package/src/utils/file-utils.js +26 -26
  41. package/src/utils/language-loader.js +29 -29
  42. package/src/utils/module-generator.js +235 -0
  43. package/src/utils/semantic-index.js +93 -0
  44. package/src/utils/template-utils.js +11 -12
  45. package/src/vite-plugin/index.js +11 -11
package/README.md CHANGED
@@ -11,6 +11,14 @@ openPrompt-Lang --version
11
11
  ## Índice
12
12
 
13
13
  - [Instalación](#instalación)
14
+ - [Nuevas Capacidades (v0.4.0)](#nuevas-capacidades-v040)
15
+ - [Aprender de proyectos reales](#aprender-de-proyectos-reales---extract---learn)
16
+ - [Aprender de documentación](#aprender-de-documentación---learn)
17
+ - [Transferencia de contexto](#transferencia-de-contexto---integrate---fromto)
18
+ - [Contexto por dominio](#contexto-por-dominio---context---domain)
19
+ - [Presets pre-ensamblados](#presets-pre-ensamblados---init---preset)
20
+ - [Reglas de base de datos](#reglas-de-base-de-datos---db-rules)
21
+ - [Filtros de componentes](#filtros-de-componentes---component-list---styletechvalidated)
14
22
  - [Anotaciones PromptLang (directorio completo)](#anotaciones-promptlang-directorio-completo)
15
23
  - [Tags de definición](#tags-de-definición)
16
24
  - [Tags de contrato](#tags-de-contrato)
@@ -406,6 +414,68 @@ export function Button(props: ButtonProps) { ... }
406
414
 
407
415
  ---
408
416
 
417
+ ## Nuevas Capacidades (v0.4.0)
418
+
419
+ ### Aprender de proyectos reales — `extract --learn`
420
+ Analiza un proyecto y extrae sus patrones: convención de nombres, arquitectura, estilo UI, hooks, anotaciones. Genera un perfil reutilizable.
421
+
422
+ ```bash
423
+ openPrompt-Lang extract ./proyecto-pulido --learn
424
+ openPrompt-Lang init --name nuevo --learn-from ./aprendizaje-perfil.json
425
+ ```
426
+
427
+ ### Aprender de documentación — `learn`
428
+ Usa IA para transformar documentación local en módulos de lenguaje funcionales con templates, tests y prácticas.
429
+
430
+ ```bash
431
+ openPrompt-Lang learn --docs ./docs-rust/ --name rust
432
+ openPrompt-Lang learn --docs ./docs-react/ --from ./proyecto-real/ --name react
433
+ openPrompt-Lang learn --docs ./docs/ --name django --integrate
434
+ ```
435
+
436
+ Los módulos se guardan en `~/.openprompt/knowledge/langs/<id>/` (accesibles para cualquier proyecto).
437
+
438
+ ### Transferencia de contexto — `integrate --from/--to`
439
+ Copia el contexto de negocio entre proyectos: stack, convenciones, @kind/@contract detectados, arquitectura.
440
+
441
+ ```bash
442
+ openPrompt-Lang integrate --from ./web-react --to ./app-mobile
443
+ ```
444
+
445
+ ### Contexto por dominio — `context --domain`
446
+ Filtra el contexto del proyecto por dominio de negocio: ecommerce, saas, mobile, api, admin, blog.
447
+
448
+ ```bash
449
+ openPrompt-Lang context --domain ecommerce
450
+ ```
451
+
452
+ ### Presets pre-ensamblados — `init --preset`
453
+ Stacks completos con servicios incluidos: Supabase, Stripe, Ionic, Tauri, Firebase.
454
+
455
+ ```bash
456
+ openPrompt-Lang init --preset react-supabase-stripe --name tienda
457
+ openPrompt-Lang init --list-presets
458
+ ```
459
+
460
+ ### Reglas de base de datos — `db-rules`
461
+ Knowledge base de buenas prácticas, anti-patrones y reglas para SQL, Supabase, Postgres, Docker.
462
+
463
+ ```bash
464
+ openPrompt-Lang db-rules --id db-001
465
+ openPrompt-Lang db-rules --tag performance
466
+ ```
467
+
468
+ ### Filtros de componentes — `component list --style/--tech/--validated`
469
+ ```bash
470
+ openPrompt-Lang component list --style shadcn
471
+ openPrompt-Lang component list --tech vue
472
+ openPrompt-Lang component list --validated
473
+ ```
474
+
475
+ > 📖 **Manual completo para IA:** `docs/AI-MANUAL.md` — todos los comandos, workflows ordenados y configuración.
476
+
477
+ ---
478
+
409
479
  ## Comandos CLI
410
480
 
411
481
  | Comando | Descripción |
package/bin/cli.js CHANGED
@@ -1,301 +1,379 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from "commander"
4
- import chalk from "chalk"
5
- import { readFileSync, existsSync } from "fs"
6
- import { join, dirname } from "path"
7
- import { fileURLToPath } from "url"
3
+ import { Command } from 'commander'
4
+ import chalk from 'chalk'
5
+ import { readFileSync, existsSync } from 'fs'
6
+ import { join, dirname } from 'path'
7
+ import { fileURLToPath } from 'url'
8
8
 
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url))
10
- const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"))
10
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))
11
11
 
12
12
  const program = new Command()
13
13
 
14
14
  program
15
- .name("openPrompt-Lang")
16
- .description(chalk.cyan("Framework de anotaciones para desarrollo asistido por IA"))
15
+ .name('openPrompt-Lang')
16
+ .description(chalk.cyan('Framework de anotaciones para desarrollo asistido por IA'))
17
17
  .version(pkg.version)
18
18
 
19
19
  program
20
- .command("init")
21
- .description("Inicializar proyecto con estructura PromptLang")
22
- .option("--name <name>", "Nombre del proyecto")
23
- .option("--describe <text>", "Descripción del proyecto para sugerencia IA")
24
- .option("--existing", "Configurar en proyecto existente (solo archivos de configuración)")
20
+ .command('init')
21
+ .description('Inicializar proyecto con estructura PromptLang')
22
+ .option('--name <name>', 'Nombre del proyecto')
23
+ .option('--describe <text>', 'Descripción del proyecto para sugerencia IA')
24
+ .option('--existing', 'Configurar en proyecto existente (solo archivos de configuración)')
25
+ .option('--dry-run', 'Previsualizar sin crear archivos')
26
+ .option('--stack <items>', 'Stack base (separado por comas, ej: react,typescript,vite,tailwind)')
27
+ .option('--preset <name>', 'Usar preset pre-ensamblado (ej: react-supabase-stripe, vue-firebase-netlify)')
28
+ .option('--list-presets', 'Listar presets disponibles y salir')
29
+ .option('--list-stacks', 'Listar stacks disponibles y salir')
30
+ .option('--learn-from <file>', 'Ruta a perfil de aprendizaje (aprendizaje-perfil.json) para heredar patrones')
31
+ .option('--no-interactive', 'Saltar preguntas interactivas y usar defaults')
25
32
  .action(async (options) => {
26
- const { init } = await import("../src/commands/init.js")
33
+ if (options.listStacks) {
34
+ const { listStacks } = await import('../src/commands/init.js')
35
+ await listStacks()
36
+ return
37
+ }
38
+ if (options.listPresets) {
39
+ const { listPresets } = await import('../src/commands/init.js')
40
+ await listPresets()
41
+ return
42
+ }
43
+ const { init } = await import('../src/commands/init.js')
27
44
  await init(options)
28
45
  })
29
46
 
30
47
  program
31
- .command("figma")
32
- .description("Generar prompt para diseño en Figma con convenciones React + Tailwind")
48
+ .command('figma')
49
+ .description('Generar prompt para diseño en Figma con convenciones React + Tailwind')
33
50
  .action(async () => {
34
- const { figma } = await import("../src/commands/figma.js")
51
+ const { figma } = await import('../src/commands/figma.js')
35
52
  await figma()
36
53
  })
37
54
 
38
55
  program
39
- .command("context")
40
- .description("Extraer contexto del proyecto para la IA")
41
- .option("--output <file>", "Archivo de salida", "contexto.md")
42
- .option("--scope <module>", "Filtrar por módulo (@scope)")
43
- .option("--no-ai", "No incluir anotaciones PromptLang en el output")
56
+ .command('context')
57
+ .description('Extraer contexto del proyecto para la IA')
58
+ .option('--output <file>', 'Archivo de salida', 'contexto.md')
59
+ .option('--scope <module>', 'Filtrar por módulo (@scope)')
60
+ .option('--no-ai', 'No incluir anotaciones PromptLang en el output')
61
+ .option('--mode <mode>', 'Modo de extracción: raw (solo archivos), index (solo índice), full (ambos)', 'full')
62
+ .option('--query <text>', 'Consulta semántica para sugerencias de componentes/patrones')
63
+ .option('--domain <domain>', 'Filtrar por dominio de negocio (ecommerce, saas, mobile, api, admin, blog)')
64
+ .option('--projects <list>', 'Proyectos vecinos a referenciar (separados por coma)')
65
+ .option('--dir <path>', 'Directorio base del proyecto (default: cwd)')
44
66
  .action(async (options) => {
45
- const { context } = await import("../src/commands/context.js")
67
+ const { context } = await import('../src/commands/context.js')
46
68
  await context(options)
47
69
  })
48
70
 
49
71
  program
50
- .command("validate")
51
- .description("Ejecutar pipeline de validación y verificar anotaciones")
52
- .option("--fix", "Auto-corregir errores cuando sea posible")
72
+ .command('validate')
73
+ .description('Ejecutar pipeline de validación y verificar anotaciones')
74
+ .option('--fix', 'Auto-corregir errores cuando sea posible')
53
75
  .action(async (options) => {
54
- const { validate } = await import("../src/commands/validate.js")
76
+ const { validate } = await import('../src/commands/validate.js')
55
77
  await validate(options)
56
78
  })
57
79
 
58
80
  program
59
- .command("suggest")
60
- .description("IA sugiere estructura/stack según descripción del proyecto")
61
- .argument("<description>", "Descripción del proyecto")
81
+ .command('suggest')
82
+ .description('IA sugiere estructura/stack según descripción del proyecto')
83
+ .argument('<description>', 'Descripción del proyecto')
62
84
  .action(async (description) => {
63
- const { suggest } = await import("../src/commands/suggest.js")
85
+ const { suggest } = await import('../src/commands/suggest.js')
64
86
  await suggest(description)
65
87
  })
66
88
 
67
89
  const component = program
68
- .command("component")
69
- .description("Gestionar biblioteca de componentes reutilizables")
90
+ .command('component')
91
+ .description('Gestionar biblioteca de componentes reutilizables')
70
92
 
71
93
  component
72
- .command("list")
73
- .description("Listar componentes disponibles")
74
- .option("--library <path>", "Ruta a la biblioteca de componentes")
94
+ .command('list')
95
+ .description('Listar componentes disponibles')
96
+ .option('--library <path>', 'Ruta a la biblioteca de componentes')
97
+ .option('--style <style>', 'Filtrar por estilo UI (shadcn, mantine, chakra, headless, mui)')
98
+ .option('--tech <tech>', 'Filtrar por tecnología (react, vue, angular)')
99
+ .option('--validated', 'Mostrar solo componentes validados/aprendidos')
75
100
  .action(async (options) => {
76
- const { componentList } = await import("../src/commands/component.js")
101
+ const { componentList } = await import('../src/commands/component.js')
77
102
  await componentList(options)
78
103
  })
79
104
 
80
105
  component
81
- .command("add <name>")
82
- .description("Agregar componente desde biblioteca o template del módulo de lenguaje")
83
- .option("--library <path>", "Ruta a la biblioteca de componentes")
84
- .option("--template", "Usar template del módulo de lenguaje (INDEX.json)")
85
- .option("--lang <lang>", "Lenguaje del template (default: de prompt-lang.json)")
86
- .option("--variants <list>", "Variantes a incluir (separadas por coma)")
87
- .option("--output <name>", "Nombre del archivo de salida (sin extensión)")
88
- .option("--force", "Sobrescribir archivo existente")
106
+ .command('add <name>')
107
+ .description('Agregar componente desde biblioteca o template del módulo de lenguaje')
108
+ .option('--library <path>', 'Ruta a la biblioteca de componentes')
109
+ .option('--template', 'Usar template del módulo de lenguaje (INDEX.json)')
110
+ .option('--lang <lang>', 'Lenguaje del template (default: de prompt-lang.json)')
111
+ .option('--variants <list>', 'Variantes a incluir (separadas por coma)')
112
+ .option('--output <name>', 'Nombre del archivo de salida (sin extensión)')
113
+ .option('--force', 'Sobrescribir archivo existente')
114
+ .option('--save', 'Marcar componente como validado/aprendido')
89
115
  .action(async (name, options) => {
90
- const { componentAdd } = await import("../src/commands/component.js")
116
+ const { componentAdd } = await import('../src/commands/component.js')
91
117
  await componentAdd(name, options)
92
118
  })
93
119
 
94
120
  component
95
- .command("init")
96
- .description("Inicializar una biblioteca de componentes")
97
- .option("--library <path>", "Ruta donde crear la biblioteca")
98
- .option("--name <name>", "Nombre de la biblioteca")
121
+ .command('init')
122
+ .description('Inicializar una biblioteca de componentes')
123
+ .option('--library <path>', 'Ruta donde crear la biblioteca')
124
+ .option('--name <name>', 'Nombre de la biblioteca')
99
125
  .action(async (options) => {
100
- const { componentInit } = await import("../src/commands/component.js")
126
+ const { componentInit } = await import('../src/commands/component.js')
101
127
  await componentInit(options)
102
128
  })
103
129
 
104
130
  component
105
- .command("manifest")
106
- .description("Mostrar el manifiesto de la biblioteca de componentes")
107
- .option("--library <path>", "Ruta a la biblioteca de componentes")
131
+ .command('manifest')
132
+ .description('Mostrar el manifiesto de la biblioteca de componentes')
133
+ .option('--library <path>', 'Ruta a la biblioteca de componentes')
108
134
  .action(async (options) => {
109
- const { componentManifest } = await import("../src/commands/component.js")
135
+ const { componentManifest } = await import('../src/commands/component.js')
110
136
  await componentManifest(options)
111
137
  })
112
138
 
113
139
  // ─── Wizard command ──────────────────────────────────────────────────────────
114
140
 
115
141
  program
116
- .command("wizard")
117
- .description("Asistente interactivo para configurar proyecto openPrompt-Lang")
142
+ .command('wizard')
143
+ .description('Asistente interactivo para configurar proyecto openPrompt-Lang')
118
144
  .action(async () => {
119
- const { wizard } = await import("../src/commands/wizard.js")
145
+ const { wizard } = await import('../src/commands/wizard.js')
120
146
  await wizard()
121
147
  })
122
148
 
123
149
  // ─── Teach command ───────────────────────────────────────────────────────────
124
150
 
125
151
  program
126
- .command("teach <templateId>")
127
- .description("Enseñanza estructurada desde template con @teachMe")
128
- .option("--lang <lang>", "Lenguaje del template (default: react)")
129
- .option("--code", "Mostrar código completo del template")
130
- .option("--force", "Mostrar template aunque no tenga @teachMe")
152
+ .command('teach <templateId>')
153
+ .description('Enseñanza estructurada desde template con @teachMe')
154
+ .option('--lang <lang>', 'Lenguaje del template (default: react)')
155
+ .option('--code', 'Mostrar código completo del template')
156
+ .option('--force', 'Mostrar template aunque no tenga @teachMe')
131
157
  .action(async (templateId, options) => {
132
- const { teach } = await import("../src/commands/teach.js")
158
+ const { teach } = await import('../src/commands/teach.js')
133
159
  await teach(templateId, options)
134
160
  })
135
161
 
162
+ // ─── Learn command (Phase D.1) ────────────────────────────────────────────────
163
+
164
+ program
165
+ .command('learn')
166
+ .description('Aprender desde documentación: genera módulos de lenguaje usando IA')
167
+ .option('--docs <paths...>', 'Rutas a archivos/directorios de documentación')
168
+ .option('--from <paths...>', 'Proyectos reales para extraer patrones (opcional)')
169
+ .option('--name <name>', 'Nombre del lenguaje (si no se puede detectar automáticamente)')
170
+ .option('--output <dir>', 'Directorio de salida (default: ~/.openprompt/knowledge/langs/<name>)')
171
+ .option('--update <dir>', 'Actualizar módulo existente en lugar de crear nuevo')
172
+ .option('--integrate', 'Copiar el módulo generado a src/templates/langs/ (solo maintainers)')
173
+ .option('--dry-run', 'Analizar y mostrar preview sin escribir archivos')
174
+ .option('--provider <provider>', 'AI provider: openai | anthropic | ollama | mock')
175
+ .action(async (options) => {
176
+ const { learn } = await import('../src/commands/learn.js')
177
+ await learn(options)
178
+ })
179
+
136
180
  // ─── QA commands ─────────────────────────────────────────────────────────────
137
181
 
138
182
  program
139
- .command("qa-gen")
140
- .description("Generate tests from learned errors (@learn-error)")
141
- .option("--lang <lang>", "Language module (default: from INDEX.json)")
142
- .option("--source <dir>", "Source directory to scan for @learn-error")
143
- .option("--output <dir>", "Output directory for generated tests (default: src/__tests__)")
144
- .option("--template <id>", "Generate tests only for a specific template")
145
- .option("--dry-run", "Preview without writing files")
146
- .option("--no-scan", "Skip scanning source for new @learn-error annotations")
183
+ .command('qa-gen')
184
+ .description('Generate tests from learned errors (@learn-error)')
185
+ .option('--lang <lang>', 'Language module (default: from INDEX.json)')
186
+ .option('--source <dir>', 'Source directory to scan for @learn-error')
187
+ .option('--output <dir>', 'Output directory for generated tests (default: src/__tests__)')
188
+ .option('--template <id>', 'Generate tests only for a specific template')
189
+ .option('--dry-run', 'Preview without writing files')
190
+ .option('--no-scan', 'Skip scanning source for new @learn-error annotations')
147
191
  .action(async (options) => {
148
- const { qaGen } = await import("../src/commands/qa-gen.js")
192
+ const { qaGen } = await import('../src/commands/qa-gen.js')
149
193
  await qaGen(options)
150
194
  })
151
195
 
152
196
  program
153
- .command("qa-learn")
154
- .description("Parse @learn-error from a file and persist to language module")
155
- .option("--file <path>", "Source file to scan for @learn-error annotations")
156
- .option("--lang <lang>", "Language module (default: react)")
197
+ .command('qa-learn')
198
+ .description('Parse @learn-error from a file and persist to language module')
199
+ .option('--file <path>', 'Source file to scan for @learn-error annotations')
200
+ .option('--lang <lang>', 'Language module (default: react)')
157
201
  .action(async (options) => {
158
- const { qaLearn } = await import("../src/commands/qa-gen.js")
202
+ const { qaLearn } = await import('../src/commands/qa-gen.js')
159
203
  await qaLearn(options)
160
204
  })
161
205
 
162
206
  // ─── Lang command ────────────────────────────────────────────────────────────
163
207
 
164
- const lang = program.command("lang").description("Gestionar módulos de lenguaje plugables")
208
+ const lang = program.command('lang').description('Gestionar módulos de lenguaje plugables')
165
209
 
166
210
  lang
167
- .command("list")
168
- .description("Listar lenguajes disponibles")
211
+ .command('list')
212
+ .description('Listar lenguajes disponibles')
169
213
  .action(async () => {
170
- const { langList } = await import("../src/commands/lang.js")
214
+ const { langList } = await import('../src/commands/lang.js')
171
215
  await langList()
172
216
  })
173
217
 
174
218
  lang
175
- .command("index <langId>")
176
- .description("Mostrar índice de templates de un lenguaje")
177
- .option("--category <category>", "Filtrar por categoría")
219
+ .command('index <langId>')
220
+ .description('Mostrar índice de templates de un lenguaje')
221
+ .option('--category <category>', 'Filtrar por categoría')
178
222
  .action(async (langId, options) => {
179
- const { langIndex } = await import("../src/commands/lang.js")
223
+ const { langIndex } = await import('../src/commands/lang.js')
180
224
  await langIndex(langId, options)
181
225
  })
182
226
 
183
227
  lang
184
- .command("search <query>")
185
- .description("Buscar templates en todos los lenguajes")
186
- .option("--lang <lang>", "Filtrar por lenguaje")
228
+ .command('search <query>')
229
+ .description('Buscar templates en todos los lenguajes')
230
+ .option('--lang <lang>', 'Filtrar por lenguaje')
187
231
  .action(async (query, options) => {
188
- const { langSearch } = await import("../src/commands/lang.js")
232
+ const { langSearch } = await import('../src/commands/lang.js')
189
233
  await langSearch(query, options)
190
234
  })
191
235
 
192
236
  lang
193
- .command("errors [langId]")
194
- .description("Mostrar errores aprendidos")
195
- .option("--template <id>", "Filtrar por template ID")
237
+ .command('errors [langId]')
238
+ .description('Mostrar errores aprendidos')
239
+ .option('--template <id>', 'Filtrar por template ID')
196
240
  .action(async (langId, options) => {
197
- const { langErrors } = await import("../src/commands/lang.js")
198
- await langErrors(langId || "react", options)
241
+ const { langErrors } = await import('../src/commands/lang.js')
242
+ await langErrors(langId || 'react', options)
243
+ })
244
+
245
+ // ─── DB Rules command ────────────────────────────────────────────────────────
246
+
247
+ program
248
+ .command('db-rules')
249
+ .description('Consultar reglas de base de datos, anti-patrones y buenas prácticas')
250
+ .option('--list', 'Listar todas las reglas disponibles')
251
+ .option('--id <id>', 'Ver detalle de una regla específica (ej: db-001)')
252
+ .option('--tag <tag>', 'Filtrar por tag (ej: performance, security, supabase)')
253
+ .option('--category <cat>', 'Filtrar por categoría (ej: rendimiento, arquitectura)')
254
+ .action(async (options) => {
255
+ const { dbRules } = await import('../src/commands/db-rules.js')
256
+ await dbRules(options)
199
257
  })
200
258
 
201
259
  // ─── Extract command ─────────────────────────────────────────────────────────
202
260
 
203
261
  program
204
- .command("extract [sourceDir]")
205
- .description("Analyze project and extract reusable components as templates")
206
- .option("--lang <lang>", "Language module to extract into (default: from prompt-lang.json)")
207
- .option("--min-reuse <n>", "Minimum imports to qualify (default: 2)")
208
- .option("--dry-run", "Analyze only, don't write files")
262
+ .command('extract [sourceDir]')
263
+ .description('Analyze project and extract reusable components as templates')
264
+ .option('--lang <lang>', 'Language module to extract into (default: from prompt-lang.json)')
265
+ .option('--min-reuse <n>', 'Minimum imports to qualify (default: 2)')
266
+ .option('--dry-run', "Analyze only, don't write files")
267
+ .option('--learn', 'Deep project analysis: extract patterns, conventions and ways of working')
268
+ .option('--output <file>', 'Output file for --learn profile (default: aprendizaje-perfil.json)')
209
269
  .action(async (sourceDir, options) => {
210
- const { extractTemplates } = await import("../src/commands/extract.js")
211
- await extractTemplates(sourceDir || "src", options)
270
+ if (options.learn) {
271
+ const { extractLearn } = await import('../src/commands/extract.js')
272
+ await extractLearn(sourceDir || 'src', options)
273
+ return
274
+ }
275
+ const { extractTemplates } = await import('../src/commands/extract.js')
276
+ await extractTemplates(sourceDir || 'src', options)
212
277
  })
213
278
 
214
279
  program
215
- .command("analyze [sourceDir]")
216
- .description("Analyze project structure, deps, and code health")
280
+ .command('analyze [sourceDir]')
281
+ .description('Analyze project structure, deps, and code health')
217
282
  .action(async (sourceDir) => {
218
- const { extractAnalyze } = await import("../src/commands/extract.js")
219
- await extractAnalyze(sourceDir || "src", {})
283
+ const { extractAnalyze } = await import('../src/commands/extract.js')
284
+ await extractAnalyze(sourceDir || 'src', {})
220
285
  })
221
286
 
222
287
  // ─── Integrate command ────────────────────────────────────────────────────────
223
288
 
224
289
  program
225
- .command("integrate")
226
- .description("Configure Opencode, Cursor, and VS Code for the project")
227
- .option("--all", "Setup all integrations without prompting")
228
- .option("--lang <lang>", "Language (auto-detects from prompt-lang.json)")
229
- .option("--no-ask", "Skip interactive prompts")
290
+ .command('integrate')
291
+ .description('Configure Opencode, Cursor, and VS Code for the project')
292
+ .option('--all', 'Setup all integrations without prompting')
293
+ .option('--lang <lang>', 'Language (auto-detects from prompt-lang.json)')
294
+ .option('--no-ask', 'Skip interactive prompts')
295
+ .option('--from <dir>', 'Transferir contexto desde otro proyecto (memoria estructurada)')
296
+ .option('--to <dir>', 'Proyecto destino para la transferencia de contexto')
297
+ .option('--migrate', 'Modo migración: copiar contexto de negocio entre proyectos')
230
298
  .action(async (options) => {
231
- const { integrate } = await import("../src/commands/integrate.js")
299
+ if (options.migrate || options.from) {
300
+ const { migrateContext } = await import('../src/commands/integrate.js')
301
+ await migrateContext(options)
302
+ return
303
+ }
304
+ const { integrate } = await import('../src/commands/integrate.js')
232
305
  await integrate(options)
233
306
  })
234
307
 
235
308
  // ─── AI commands ─────────────────────────────────────────────────────────────
236
309
 
237
310
  program
238
- .command("ai-gen")
239
- .description("Generate components from natural language descriptions")
240
- .option("--description <text>", "Description of what to generate")
241
- .option("--name <name>", "Component name (auto-suggested if omitted)")
242
- .option("--lang <lang>", "Language module (default: react)")
243
- .option("--profile <profile>", "senior | mid | junior (default: mid)")
311
+ .command('ai-gen')
312
+ .description('Generate components from natural language descriptions')
313
+ .option('--description <text>', 'Description of what to generate')
314
+ .option('--name <name>', 'Component name (auto-suggested if omitted)')
315
+ .option('--lang <lang>', 'Language module (default: react)')
316
+ .option('--profile <profile>', 'senior | mid | junior (default: mid)')
244
317
  .option(
245
- "--provider <provider>",
246
- "AI provider: openai | anthropic | ollama | mock (default: auto-detect)"
318
+ '--provider <provider>',
319
+ 'AI provider: openai | anthropic | ollama | mock (default: auto-detect)'
247
320
  )
248
- .option("--output <dir>", "Output directory (default: src/components)")
249
- .option("--force", "Overwrite existing files without asking")
250
- .option("--validate", "Run annotation validation after generation")
251
- .option("--verbose", "Show prompt and extended output")
252
- .option("--temperature <num>", "Temperature for generation (default: 0.3)")
253
- .option("--max-tokens <num>", "Max tokens for generation (default: 4096)")
321
+ .option('--output <dir>', 'Output directory (default: src/components)')
322
+ .option('--force', 'Overwrite existing files without asking')
323
+ .option('--validate', 'Run annotation validation after generation')
324
+ .option('--verbose', 'Show prompt and extended output')
325
+ .option('--temperature <num>', 'Temperature for generation (default: 0.3)')
326
+ .option('--max-tokens <num>', 'Max tokens for generation (default: 4096)')
254
327
  .action(async (options) => {
255
- const { aiGen } = await import("../src/commands/ai-gen.js")
328
+ const { aiGen } = await import('../src/commands/ai-gen.js')
256
329
  await aiGen(options)
257
330
  })
258
331
 
259
332
  program
260
- .command("ai-providers")
261
- .description("List available AI providers and their status")
333
+ .command('ai-providers')
334
+ .description('List available AI providers and their status')
262
335
  .action(async () => {
263
- const { aiProviders } = await import("../src/commands/ai-gen.js")
336
+ const { aiProviders } = await import('../src/commands/ai-gen.js')
264
337
  await aiProviders()
265
338
  })
266
339
 
267
340
  // ─── Scaffold command ────────────────────────────────────────────────────────
268
341
 
269
342
  const scaffold = program
270
- .command("scaffold")
271
- .description("Gestionar scafffolding de proyectos por framework")
343
+ .command('scaffold')
344
+ .description('Gestionar scafffolding de proyectos por framework')
272
345
 
273
346
  scaffold
274
- .command("folders <framework>")
275
- .description("Crear estructura de carpetas según framework")
276
- .option("--name <name>", "Nombre del proyecto (directorio)")
277
- .option("--force", "Sobrescribir archivos existentes")
347
+ .command('folders <framework>')
348
+ .description('Crear estructura de carpetas según framework')
349
+ .option('--name <name>', 'Nombre del proyecto (directorio)')
350
+ .option('--force', 'Sobrescribir archivos existentes')
278
351
  .action(async (framework, options) => {
279
- const { scaffoldFolders } = await import("../src/commands/scaffold.js")
352
+ const { scaffoldFolders } = await import('../src/commands/scaffold.js')
280
353
  await scaffoldFolders(framework, options)
281
354
  })
282
355
 
283
356
  scaffold
284
- .command("list")
285
- .description("Listar frameworks disponibles en el catálogo")
357
+ .command('list')
358
+ .description('Listar frameworks disponibles en el catálogo')
286
359
  .action(async () => {
287
- const { scaffoldList } = await import("../src/commands/scaffold.js")
360
+ const { scaffoldList } = await import('../src/commands/scaffold.js')
288
361
  await scaffoldList()
289
362
  })
290
363
 
291
364
  // ─── MCP server command ───────────────────────────────────────────────────────
292
365
 
293
366
  program
294
- .command("mcp")
295
- .description("Start MCP server for opencode integration via stdio")
367
+ .command('mcp')
368
+ .description('Start MCP server for opencode integration via stdio')
296
369
  .action(async () => {
297
- const { startServer } = await import("../src/mcp-server.js")
370
+ const { startServer } = await import('../src/mcp-server.js')
298
371
  await startServer()
299
372
  })
300
373
 
301
- program.parse(process.argv)
374
+ try {
375
+ await program.parseAsync(process.argv)
376
+ } catch (err) {
377
+ console.error(chalk.red(`✘ Error: ${err.message}`))
378
+ process.exitCode = 1
379
+ }