openprompt-lang 0.11.0 → 1.0.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/bin/cli.js CHANGED
@@ -20,6 +20,11 @@ import { register as registerLearning } from "../src/cli/commands-learning.js"
20
20
  import { register as registerOpl } from "../src/cli/commands-opl.js"
21
21
  import { register as registerMisc } from "../src/cli/commands-misc.js"
22
22
 
23
+ import { handleUncaughtErrors, EXIT_CODES } from "../src/utils/errors.js"
24
+
25
+ // Global error handler for uncaught exceptions
26
+ handleUncaughtErrors()
27
+
23
28
  const __dirname = dirname(fileURLToPath(import.meta.url))
24
29
  const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"))
25
30
 
@@ -62,6 +67,12 @@ registerMisc(program)
62
67
  try {
63
68
  await program.parseAsync(process.argv)
64
69
  } catch (err) {
65
- console.error(chalk.red(`✘ Error: ${err.message}`))
66
- process.exitCode = 1
70
+ if (err.exitCode !== undefined) {
71
+ console.error('\n' + err.userMessage + '\n')
72
+ process.exitCode = err.exitCode
73
+ } else {
74
+ console.error(chalk.red(`\n✘ Error inesperado: ${err.message}\n`))
75
+ if (process.env.DEBUG) console.error(chalk.gray(err.stack))
76
+ process.exitCode = EXIT_CODES.GENERIC
77
+ }
67
78
  }
package/bin/opl.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // @use(kind, contract, limit)
3
+ // @kind(util)
4
+ // @contract(in: argv -> out: exitCode, sideEffect: delega a cli.js con los mismos argumentos)
5
+ // @limit(lines: 30)
6
+
7
+ // Shortcut: 'opl' → 'openPrompt-Lang'
8
+ // Este archivo permite usar 'opl' como comando corto en vez de 'openPrompt-Lang'
9
+ // Ej: opl validate, opl assess, opl search react
10
+
11
+ import { fileURLToPath } from 'url'
12
+ import { dirname, join } from 'path'
13
+ import { execFileSync } from 'child_process'
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url))
16
+ const cliPath = join(__dirname, 'cli.js')
17
+
18
+ try {
19
+ const result = execFileSync(process.execPath, [cliPath, ...process.argv.slice(2)], {
20
+ cwd: process.cwd(),
21
+ stdio: 'inherit',
22
+ env: { ...process.env, OPL_ALIAS: '1' }
23
+ })
24
+ process.exitCode = result?.status ?? 0
25
+ } catch (err) {
26
+ process.exitCode = err.status ?? 1
27
+ }
package/docs/FRAMEWORK.md CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  ---
8
8
 
9
+
9
10
  ## 1. Anotaciones Disponibles
10
11
 
11
12
  | Tag | Categoría | Descripción |
@@ -24,7 +25,7 @@
24
25
  | `@pattern()` | Arquitectura | Patrón de diseño (ver sección 5) |
25
26
  | `@test()` | Calidad | Metadata de tests: `@test(coverage: 80)` |
26
27
  | `@learn-error` | Calidad | Error aprendido: `@learn-error id=ID fix='...'` |
27
- | `@scar` | Calidad | Cicatriz de Guerra — error pasado evitable: `@scar(id: string, symptom: string, cause: string, fix: string, severity?: blocker|critical|warning|info, detect?: regex)` |
28
+ | `@scar` | Calidad | Cicatriz de Guerra |
28
29
  | `@goodPractice()` | Documentación | Buena práctica |
29
30
  | `@badPractice()` | Documentación | Anti-patrón |
30
31
  | `@teachMe` | Documentación | Archivo es template de enseñanza |
@@ -95,21 +96,21 @@ Comandos disponibles desde `bin/cli.js` o `npm run opl` o `node bin/cli.js`:
95
96
  opl # Pantalla de bienvenida "puerta de entrada"
96
97
  opl help # Misma pantalla de bienvenida
97
98
  opl tutorial # Recorrido guiado interactivo (7 min)
98
- opl index [path] # Navegar conocimiento por niveles (general → dominio → documento → sistemas)
99
+ opl index [path] # Navegar conocimiento por niveles
99
100
  opl read <dominio>/<id> # Leer contenido específico (--chapter n --full)
100
- opl search <query> # Búsqueda híbrida (tags + fulltext + semántica + detecta sistema)
101
- opl system [name] # Explorar sistemas de conocimiento (capas, patrones, errores)
102
- opl graph [concept] # Grafo de relaciones entre conceptos y documentos
103
- opl assess [path] # Evaluar Production Readiness Assessment (PRA 0-100)
104
- opl assess --verbose # Mostrar 7 dimensiones × 28 criterios detallados
105
- opl assess --save # Guardar reporte JSON en el proyecto
106
- opl init --knowledge # Configurar biblioteca personal ~/.opl/knowledge/
107
- opl session doc/close/diff # Gestión de sesiones con documentación obligatoria
108
- opl doc flow/framework # Generar documentación (Mermaid, FRAMEWORK.md)
109
- opl upgrade # Migrar configuración a versión actual
110
- opl contract list # Listar contratos abstractos (IR)
101
+ opl search <query> # Búsqueda híbrida
102
+ opl system [name] # Explorar sistemas de conocimiento
103
+ opl graph [concept] # Grafo de relaciones
104
+ opl assess [path] # Evaluar PRA 0-100
105
+ opl assess --verbose # 7 dimensiones × 28 criterios
106
+ opl assess --save # Guardar reporte JSON
107
+ opl init --knowledge # Configurar biblioteca personal
108
+ opl session doc/close/diff # Gestión de sesiones
109
+ opl doc flow/framework # Generar documentación
110
+ opl upgrade # Migrar configuración
111
+ opl contract list # Listar contratos abstractos
111
112
  opl contract show <id> # Mostrar detalle de contrato
112
- opl contract search <query> # Buscar contratos por nombre/tags
113
+ opl contract search <query> # Buscar contratos
113
114
  ```
114
115
 
115
116
  ## 8. Production Readiness Assessment (PRA)
@@ -243,8 +244,7 @@ npx openPrompt-Lang mcp
243
244
  - **SIEMPRE** revisar @scar vinculados en contratos abstractos antes de implementar
244
245
  - **SIEMPRE** declarar `@use()` al inicio de archivos anotados
245
246
  - **SIEMPRE** ejecutar `npx openPrompt-Lang validate` antes de cada commit
246
- - **SIEMPRE** documentar errores en proyectos reales en `ERRORES-Y-HALLAZGOS.md` (sin modificar código)
247
- - **PRA zero-tolerance**: F5 (transacciones atómicas), S2 (deny-by-default), S4 (no hardcoded secrets)
247
+ - **PRA zero-tolerance**: F5, S2, S4
248
248
  - **Sistemas de conocimiento**: usar `opl system` antes de implementar features complejas
249
249
  - Preferir `export function Name() {}` sobre arrow functions para componentes
250
250
  - Preferir Named Exports sobre Default Exports
@@ -255,7 +255,7 @@ npx openPrompt-Lang mcp
255
255
 
256
256
  La IA dispone de **36 fuentes de conocimiento** organizadas en 13 dominios (incluyendo 2 proyectos reales extraídos).
257
257
 
258
- El conocimiento se agrupa en **sistemas** — conjuntos de fuentes que se necesitan juntas para construir algo real (app fullstack, pasarela de pagos, microservicios, POS, etc.).
258
+ El conocimiento se agrupa en **sistemas** — conjuntos de fuentes que se necesitan juntas para construir algo real.
259
259
  Explorar: `opl system list` para ver los 10 sistemas disponibles.
260
260
 
261
261
  ### Dominios
@@ -337,8 +337,6 @@ Explorar: `opl system list` para ver los 10 sistemas disponibles.
337
337
 
338
338
  ### Sistemas de Conocimiento
339
339
 
340
- Sistemas agrupados por contexto de construcción (capas, patrones, errores comunes):
341
-
342
340
  | Sistema | Capas | Fuentes |
343
341
  |---------|-------|---------|
344
342
  | react-fullstack-app | frontend, backend, auth, db, deploy | 7+ |
@@ -352,8 +350,6 @@ Sistemas agrupados por contexto de construcción (capas, patrones, errores comun
352
350
  | microservicios-multi-lenguaje | gateway, java, node, python, monitor | 7+ |
353
351
  | saas-pos-chileno | saas, multitenant, pos, billing, compliance | 7+ |
354
352
 
355
- Explorar: `opl system [nombre]` — ver capas, patrones y errores documentados.
356
-
357
353
  ### Contratos Abstractos
358
354
 
359
355
  IR de componentes que separa **Contrato (esencia)** de **Sintaxis (framework)**:
@@ -372,7 +368,7 @@ Los contratos se almacenan en `knowledge-repo/abstract-contracts/` con schema en
372
368
 
373
369
  ### Cicatrices de Guerra (@scar)
374
370
 
375
- Anotación para **conocimiento negativo** — registrar lo que salió mal para que no se repita:
371
+ Anotación para **conocimiento negativo**:
376
372
 
377
373
  ```typescript
378
374
  // @scar(
@@ -387,8 +383,6 @@ Anotación para **conocimiento negativo** — registrar lo que salió mal para q
387
383
 
388
384
  Campos: `id` (req), `symptom` (req), `cause` (req), `fix` (req), `detect` (opcional, regex), `severity` (opcional).
389
385
 
390
- Integración PRA: @scar con severity=blocker penaliza el score de calidad. @scar no resueltos reducen F3.
391
-
392
386
  ### Pipeline Auto-curativo
393
387
 
394
388
  El paso [4/5] de validate ejecuta un loop de auto-corrección:
@@ -438,55 +432,9 @@ knowledge_concept → { bookId, conceptSlug? }
438
432
  ## 14. Stack del Proyecto
439
433
 
440
434
  - **Lenguaje:** node
441
- - **Stack:** node, javascript, esm, commander
435
+ - **Stack base:** node, javascript, esm, commander
442
436
  - **Extensiones:** vitest, eslint, inquirer, chalk, pdf-parse
443
437
  - **Perfil:** senior
444
-
445
- ## 15. Flujo de Trabajo Recomendado
446
-
447
- ### Para desarrollo con MCP (ahorra tiempo)
448
-
449
- ```
450
- analyze_project → context_unified → knowledge_search/recall → work_context_plan → work_context_start → implementar → validate → work_context_close
451
- ```
452
-
453
- 1. **analyze_project** — Auditoría inicial del proyecto (30s, ahorra ~15min)
454
- 2. **context_unified** — Búsqueda cruzada en 7 fuentes (10s, ahorra ~20min)
455
- 3. **knowledge_search** o **recall** — Buscar antes de crear (10s, ahorra ~30min)
456
- 4. **work_context_plan** — Planificar antes de implementar (1min, ahorra ~1hr)
457
- 5. **work_context_start** — Iniciar sesión con tracking
458
- 6. **implementar** — Escribir código siguiendo anotaciones
459
- 7. **validate** — Verificar anotaciones antes de commit
460
- 8. **work_context_close** — Cerrar sesión y registrar métricas
461
-
462
- ### Para evaluación de producción (PRA)
463
-
464
- ```
465
- opl assess --verbose → opl assess --save → revisar PRA-ASSESSMENT.json
466
- ```
467
-
468
- 1. **opl assess --verbose** — Ver 7 dimensiones × 28 criterios
469
- 2. **opl assess --save** — Guardar reporte JSON en el proyecto
470
- 3. **Revisar zero-tolerance** — F5, S2, S4 deben estar en verde
471
- 4. **Priorizar mejoras** — Empezar por dimensiones con menor score
472
-
473
- ### Para QA completo (workflow integrado)
474
-
475
- ```
476
- opl assess --save && npx openPrompt-Lang validate && recall "errores"
477
- ```
478
-
479
- ### Para validación de anotaciones
480
-
481
- ```
482
- validate → lint_file → qa-gen → validate
483
- ```
484
-
485
- 1. **validate** — Verificar anotaciones antes de cambios
486
- 2. **lint_file** — Lint detallado del archivo modificado
487
- 3. **qa-gen** — Generar tests desde errores aprendidos
488
- 4. **validate** — Validación final antes de commit
489
-
490
- ---
491
-
492
- *Generado automáticamente por openPrompt-Lang v0.10.1 — 2026-05-21*
438
+ - **Modo oscuro:** por defecto
439
+ - **Testing:** Vitest
440
+ - **Linting:** ESLint + Prettier
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "openprompt-lang",
3
- "version": "0.11.0",
3
+ "version": "1.0.0",
4
4
  "description": "PromptLang CLI — Context Engine de anotaciones para desarrollo asistido por IA",
5
5
  "type": "module",
6
6
  "main": "./bin/cli.js",
7
7
  "bin": {
8
8
  "openPrompt-Lang": "./bin/cli.js",
9
9
  "openprompt-lang": "./bin/cli.js",
10
+ "opl": "./bin/opl.js",
10
11
  "openprompt-create": "./bin/create.js"
11
12
  },
12
13
  "files": [
@@ -1,4 +1,7 @@
1
- // @use(generated, kind, props, limit, test)
1
+ // @use(kind, contract, limit, generated)
2
+ // @kind(feature)
3
+ // @contract(in: provider name + prompt -> out: generated text, sideEffect: llama API externa)
4
+ // @limit(lines: 260)
2
5
  // ─── AI Provider abstraction for openPrompt-Lang ────────────────────────────
3
6
  // Each provider must implement: generate(prompt, options) => Promise<string>
4
7
 
@@ -1,15 +1,16 @@
1
1
  // @use(commander, kind, contract, limit)
2
- // @kind(util)
3
- // @contract(in: program -> out: void, sideEffect: registra comandos init/wizard/figma/suggest)
4
- // @limit(lines: 80)
2
+ // @kind(feature)
3
+ // @contract(in: program -> out: void, sideEffect: registra comandos init)
4
+ // @limit(lines: 70)
5
5
 
6
6
  export function register(program) {
7
- program
7
+ const init = program
8
8
  .command('init')
9
9
  .description('Inicializar proyecto o biblioteca de conocimiento personal')
10
10
  .option('--name <name>', 'Nombre del proyecto')
11
11
  .option('--describe <text>', 'Descripción del proyecto para sugerencia IA')
12
12
  .option('--existing', 'Configurar en proyecto existente (solo archivos de configuración)')
13
+ .option('--rebuild', 'Reconstruir integración OPL en proyecto existente (force-regenera archivos generados, aplica migraciones)')
13
14
  .option('--knowledge', 'Configurar biblioteca de conocimiento personal (~/.opl/knowledge/)')
14
15
  .option('--dry-run', 'Previsualizar sin crear archivos')
15
16
  .option('--stack <items>', 'Stack base (separado por comas, ej: react,typescript,vite,tailwind)')
@@ -1,7 +1,7 @@
1
1
  // @use(commander, kind, contract, limit)
2
2
  // @kind(util)
3
3
  // @contract(in: program -> out: void, sideEffect: registra comandos misceláneos)
4
- // @limit(lines: 200)
4
+ // @limit(lines: 270)
5
5
 
6
6
  export function register(program) {
7
7
  const component = program
@@ -249,4 +249,14 @@ export function register(program) {
249
249
  const { recall } = await import('../commands/recall.js')
250
250
  await recall(query, options)
251
251
  })
252
+
253
+ program
254
+ .command('rebuild')
255
+ .description('Reconstruir integración OPL en proyecto existente (force-regenera archivos, aplica migraciones)')
256
+ .option('--dir <path>', 'Directorio del proyecto (default: cwd)')
257
+ .option('--yes', 'Auto-aceptar migraciones y prompts')
258
+ .action(async (options) => {
259
+ const { initExisting } = await import('../commands/init-existing.js')
260
+ await initExisting({ rebuild: true, dir: options.dir || process.cwd() })
261
+ })
252
262
  }
@@ -1,7 +1,7 @@
1
1
  // @use(commander, kind, contract, limit)
2
2
  // @kind(util)
3
3
  // @contract(in: program -> out: void, sideEffect: registra todos los comandos OPL)
4
- // @limit(lines: 200)
4
+ // @limit(lines: 280)
5
5
 
6
6
  import chalk from 'chalk'
7
7
 
@@ -1,7 +1,7 @@
1
1
  // @use(kind, contract, limit)
2
2
  // @kind(util)
3
3
  // @contract(in: args -> out: void, sideEffect: gestión de componentes)
4
- // @limit(lines: 200)
4
+ // @limit(lines: 450)
5
5
  import {
6
6
  readFileSync,
7
7
  writeFileSync,
@@ -1,7 +1,7 @@
1
1
  // @use(kind, contract, limit)
2
2
  // @kind(util)
3
3
  // @contract(in: args -> out: void, sideEffect: muestra reglas de DB)
4
- // @limit(lines: 100)
4
+ // @limit(lines: 130)
5
5
  import { readFileSync, existsSync } from 'fs'
6
6
  import { join, dirname } from 'path'
7
7
  import { fileURLToPath } from 'url'
@@ -1,7 +1,7 @@
1
1
  // @use(kind, contract, limit)
2
2
  // @kind(feature)
3
3
  // @contract(in: options -> out: baseDir)
4
- // @limit(lines: 300)
4
+ // @limit(lines: 430)
5
5
 
6
6
  import { writeFileSync, readFileSync, existsSync, mkdirSync, cpSync } from 'fs'
7
7
  import { join, dirname } from 'path'
@@ -138,10 +138,10 @@ export async function init(options) {
138
138
  ]
139
139
  }])
140
140
 
141
- if (projectType === 'existing') return initExisting()
141
+ if (projectType === 'existing') return initExisting(options)
142
142
  }
143
143
 
144
- if (options.existing) return initExisting()
144
+ if (options.existing) return initExisting(options)
145
145
 
146
146
  const baseDir = join(process.cwd(), (options.name || 'mi-proyecto'))
147
147
 
@@ -1,7 +1,7 @@
1
1
  // @use(kind, contract, limit)
2
2
  // @kind(feature)
3
3
  // @contract(in: options -> out: cwd)
4
- // @limit(lines: 300)
4
+ // @limit(lines: 450)
5
5
 
6
6
  import { writeFileSync, readFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'
7
7
  import { join } from 'path'
@@ -98,13 +98,18 @@ function auditProject(cwd) {
98
98
  return results
99
99
  }
100
100
 
101
- export async function initExisting() {
102
- const cwd = process.cwd()
101
+ export async function initExisting(options = {}) {
102
+ const cwd = options.dir || process.cwd()
103
+ const rebuild = options.rebuild || false
103
104
  const projectName = existsSync(join(cwd, 'package.json'))
104
105
  ? JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8')).name || 'mi-proyecto'
105
106
  : 'mi-proyecto'
106
107
 
107
- console.log(chalk.cyan(`\n🔧 Configurando openPrompt-Lang en proyecto existente: ${projectName}\n`))
108
+ if (rebuild) {
109
+ console.log(chalk.cyan(`\n🔧 Reconstruyendo integración OPL en: ${projectName}\n`))
110
+ } else {
111
+ console.log(chalk.cyan(`\n🔧 Configurando openPrompt-Lang en proyecto existente: ${projectName}\n`))
112
+ }
108
113
 
109
114
  const stack = enhancedDetectStack(cwd)
110
115
  const detected = Object.entries(stack)
@@ -148,16 +153,31 @@ export async function initExisting() {
148
153
  extractor: { ignore: ['.env', '*.local', 'dist', 'node_modules'], output: 'contexto.md' }
149
154
  }
150
155
 
151
- if (existsSync(join(cwd, 'prompt-lang.json'))) {
152
- console.log(chalk.yellow(' ⚠️ prompt-lang.json ya existe. Saltando.'))
156
+ // Add OPL version tracking
157
+ try {
158
+ const pkgPath = new URL('../../package.json', import.meta.url).pathname
159
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
160
+ config.oplVersion = pkg.version
161
+ } catch {}
162
+
163
+ const configPath = join(cwd, 'prompt-lang.json')
164
+ if (rebuild || !existsSync(configPath)) {
165
+ // Merge with existing config if rebuilding (preserve user fields)
166
+ if (rebuild && existsSync(configPath)) {
167
+ try {
168
+ const existing = JSON.parse(readFileSync(configPath, 'utf-8'))
169
+ // Preserve user customizations, merge new defaults
170
+ Object.assign(config, existing, { oplVersion: config.oplVersion })
171
+ } catch {}
172
+ }
173
+ writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8')
174
+ console.log(chalk.green(` ✅ prompt-lang.json ${rebuild ? 'actualizado' : 'creado'}`))
153
175
  } else {
154
- writeFileSync(join(cwd, 'prompt-lang.json'), JSON.stringify(config, null, 2), 'utf-8')
155
- console.log(chalk.green(' ✅ prompt-lang.json'))
176
+ console.log(chalk.yellow(' ⚠️ prompt-lang.json ya existe. Saltando.'))
156
177
  }
157
178
 
158
- if (existsSync(join(cwd, 'AGENTS.md'))) {
159
- console.log(chalk.yellow(' ⚠️ AGENTS.md ya existe. Saltando.'))
160
- } else {
179
+ const agentsPath = join(cwd, 'AGENTS.md')
180
+ if (rebuild || !existsSync(agentsPath)) {
161
181
  const agentsLines = [
162
182
  '# AGENTS.md — Contexto para IA',
163
183
  '',
@@ -177,26 +197,24 @@ export async function initExisting() {
177
197
  '- Framework: openPrompt-Lang',
178
198
  '',
179
199
  ]
180
- writeFileSync(join(cwd, 'AGENTS.md'), agentsLines.join('\n'), 'utf-8')
181
- console.log(chalk.green(' ✅ AGENTS.md'))
200
+ writeFileSync(agentsPath, agentsLines.join('\n'), 'utf-8')
201
+ console.log(chalk.green(` ✅ AGENTS.md ${rebuild ? 'actualizado' : 'creado'}`))
202
+ } else {
203
+ console.log(chalk.yellow(' ⚠️ AGENTS.md ya existe. Saltando.'))
182
204
  }
183
205
 
184
206
  mkdirSync(join(cwd, '.openprompt'), { recursive: true })
185
207
  const frameworkPath = join(cwd, '.openprompt', 'FRAMEWORK.md')
186
- if (!existsSync(frameworkPath)) {
208
+ if (rebuild || !existsSync(frameworkPath)) {
187
209
  writeFileSync(frameworkPath, generateFrameworkMd({ dir: cwd }), 'utf-8')
188
- console.log(chalk.green(' ✅ .openprompt/FRAMEWORK.md'))
210
+ console.log(chalk.green(` ✅ .openprompt/FRAMEWORK.md ${rebuild ? 'actualizado' : 'creado'}`))
211
+ } else {
212
+ console.log(chalk.yellow(' ⚠️ .openprompt/FRAMEWORK.md ya existe. Saltando.'))
189
213
  }
190
214
 
191
215
  if (existsSync(join(cwd, 'package.json'))) {
192
- const { addScripts } = await inquirer.prompt([{
193
- type: 'confirm',
194
- name: 'addScripts',
195
- message: '¿Agregar scripts descriptivos al package.json?',
196
- default: false
197
- }])
198
-
199
- if (addScripts) {
216
+ if (rebuild) {
217
+ // Always add scripts in rebuild mode
200
218
  const pkgPath = join(cwd, 'package.json')
201
219
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
202
220
  pkg.scripts = pkg.scripts || {}
@@ -206,32 +224,106 @@ export async function initExisting() {
206
224
  }
207
225
  if (added > 0) {
208
226
  writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8')
209
- console.log(chalk.green(` ✅ ${added} scripts agregados`))
227
+ console.log(chalk.green(` ✅ ${added} scripts descriptivos agregados`))
228
+ }
229
+ } else {
230
+ const { addScripts } = await inquirer.prompt([{
231
+ type: 'confirm',
232
+ name: 'addScripts',
233
+ message: '¿Agregar scripts descriptivos al package.json?',
234
+ default: false
235
+ }])
236
+
237
+ if (addScripts) {
238
+ const pkgPath = join(cwd, 'package.json')
239
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
240
+ pkg.scripts = pkg.scripts || {}
241
+ let added = 0
242
+ for (const [name, cmd] of Object.entries(DESCRIPTIVE_SCRIPTS)) {
243
+ if (!pkg.scripts[name]) { pkg.scripts[name] = cmd; added++ }
244
+ }
245
+ if (added > 0) {
246
+ writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8')
247
+ console.log(chalk.green(` ✅ ${added} scripts agregados`))
248
+ }
210
249
  }
211
250
  }
212
251
  }
213
252
 
214
- const { createDocs } = await inquirer.prompt([{
215
- type: 'confirm',
216
- name: 'createDocs',
217
- message: '¿Crear estructura docs/ (BACKLOG, COMMITS, LOGS)?',
218
- default: false
219
- }])
253
+ if (!rebuild) {
254
+ const { createDocs } = await inquirer.prompt([{
255
+ type: 'confirm',
256
+ name: 'createDocs',
257
+ message: '¿Crear estructura docs/ (BACKLOG, COMMITS, LOGS)?',
258
+ default: false
259
+ }])
260
+
261
+ if (createDocs) {
262
+ for (const dir of ['docs/BACKLOG', 'docs/PHASES', 'docs/COMMITS', 'docs/LOGS/ERRORES', 'docs/LOGS/ACTIVIDAD']) {
263
+ mkdirSync(join(cwd, dir), { recursive: true })
264
+ }
265
+ if (!existsSync(join(cwd, 'docs/BACKLOG/tasks.md'))) {
266
+ writeFileSync(join(cwd, 'docs/BACKLOG/tasks.md'), '# Backlog — Tareas\n\n## TASK-001\n- **Módulo:** \n- **Criterios:**\n - [ ] \n', 'utf-8')
267
+ }
268
+ if (!existsSync(join(cwd, 'docs/PHASES/roadmap.md'))) {
269
+ writeFileSync(join(cwd, 'docs/PHASES/roadmap.md'), '# Fases\n\n## Fase 1\n**Estado:** Actual\n**Objetivo:** _definir_\n', 'utf-8')
270
+ }
271
+ if (!existsSync(join(cwd, 'docs/COMMITS/INDEX.md'))) {
272
+ writeFileSync(join(cwd, 'docs/COMMITS/INDEX.md'), '# Índice de Commits\n\n| Fecha | Versión | Descripción |\n|---|---|---|\n', 'utf-8')
273
+ }
274
+ console.log(chalk.green(' ✅ Estructura docs/'))
275
+ }
276
+ }
220
277
 
221
- if (createDocs) {
222
- for (const dir of ['docs/BACKLOG', 'docs/PHASES', 'docs/COMMITS', 'docs/LOGS/ERRORES', 'docs/LOGS/ACTIVIDAD']) {
223
- mkdirSync(join(cwd, dir), { recursive: true })
278
+ if (rebuild) {
279
+ // Rebuild mode: always set up build plugins
280
+ const viteConfig = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'].find(f => existsSync(join(cwd, f)))
281
+ if (viteConfig) {
282
+ const vitePath = join(cwd, viteConfig)
283
+ const existing = readFileSync(vitePath, 'utf-8')
284
+ if (!existing.includes('openPromptAnnotations')) {
285
+ const finalContent = `import { openPromptAnnotations } from "openprompt-lang/vite-plugin";\n${existing.replace(/plugins\s*:\s*\[/, 'plugins: [\n openPromptAnnotations(),')}`
286
+ writeFileSync(vitePath, finalContent, 'utf-8')
287
+ console.log(chalk.green(` ✅ Plugin Vite agregado a ${viteConfig}`))
288
+ }
224
289
  }
225
- if (!existsSync(join(cwd, 'docs/BACKLOG/tasks.md'))) {
226
- writeFileSync(join(cwd, 'docs/BACKLOG/tasks.md'), '# Backlog — Tareas\n\n## TASK-001\n- **Módulo:** \n- **Criterios:**\n - [ ] \n', 'utf-8')
290
+
291
+ const tsconfig = ['tsconfig.json', 'tsconfig.app.json'].find(f => existsSync(join(cwd, f)))
292
+ if (tsconfig) {
293
+ const tsPath = join(cwd, tsconfig)
294
+ const tsConfig = JSON.parse(readFileSync(tsPath, 'utf-8'))
295
+ tsConfig.compilerOptions = tsConfig.compilerOptions || {}
296
+ tsConfig.compilerOptions.plugins = tsConfig.compilerOptions.plugins || []
297
+ if (!tsConfig.compilerOptions.plugins.some(p => p.name?.includes('openprompt'))) {
298
+ tsConfig.compilerOptions.plugins.push({ name: 'openprompt-lang/ts-plugin' })
299
+ writeFileSync(tsPath, `${JSON.stringify(tsConfig, null, 2)}\n`, 'utf-8')
300
+ console.log(chalk.green(` ✅ TS plugin agregado a ${tsconfig}`))
301
+ }
227
302
  }
228
- if (!existsSync(join(cwd, 'docs/PHASES/roadmap.md'))) {
229
- writeFileSync(join(cwd, 'docs/PHASES/roadmap.md'), '# Fases\n\n## Fase 1\n**Estado:** Actual\n**Objetivo:** _definir_\n', 'utf-8')
303
+
304
+ // Rebuild mode: always integrate all editors
305
+ try {
306
+ const { integrate } = await import('./integrate.js')
307
+ console.log(chalk.cyan('\n🔄 Reconstruyendo integración de editores...'))
308
+ await integrate({ dir: cwd, all: true, silent: true })
309
+ } catch {
310
+ console.log(chalk.yellow(' ⚠️ No se pudo ejecutar integrate automáticamente.'))
230
311
  }
231
- if (!existsSync(join(cwd, 'docs/COMMITS/INDEX.md'))) {
232
- writeFileSync(join(cwd, 'docs/COMMITS/INDEX.md'), '# Índice de Commits\n\n| Fecha | Versión | Descripción |\n|---|---|---|\n', 'utf-8')
312
+
313
+ // Apply pending migrations (upgrade)
314
+ try {
315
+ const { upgrade } = await import('./opl-upgrade.js')
316
+ console.log(chalk.cyan('🔄 Aplicando migraciones pendientes...'))
317
+ await upgrade({ dir: cwd, yes: true })
318
+ } catch {
319
+ console.log(chalk.yellow(' ⚠️ No se pudo ejecutar upgrade automáticamente.'))
233
320
  }
234
- console.log(chalk.green(' ✅ Estructura docs/'))
321
+
322
+ console.log(chalk.cyan(`\n✅ Integración OLP reconstruida para: ${projectName}`))
323
+ console.log(chalk.cyan(` Versión OPL: ${config.oplVersion || 'desconocida'}`))
324
+ console.log(chalk.gray(' Archivos generados forzaron actualización.'))
325
+ console.log(chalk.gray(' Las personalizaciones del usuario fueron preservadas en prompt-lang.json.\n'))
326
+ return
235
327
  }
236
328
 
237
329
  const { setupBuild } = await inquirer.prompt([{
@@ -1,7 +1,7 @@
1
1
  // @use(kind, contract, limit)
2
2
  // @kind(feature)
3
3
  // @contract(in: options -> out: baseDir)
4
- // @limit(lines: 300)
4
+ // @limit(lines: 430)
5
5
 
6
6
  import { writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs'
7
7
  import { join } from 'path'
@@ -1,4 +1,7 @@
1
1
  // @use(kind, contract, limit, learn-error, generated)
2
+ // @kind(feature)
3
+ // @contract(in: options -> out: void, sideEffect: genera archivos de integración en el proyecto)
4
+ // @limit(lines: 700)
2
5
  import { writeFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'fs'
3
6
  import { join, relative } from 'path'
4
7
  import os from 'os'
@@ -1,7 +1,7 @@
1
1
  // @use(kind, contract, limit)
2
2
  // @kind(util)
3
3
  // @contract(in: args -> out: void, sideEffect: gestión de aprendizaje semántico)
4
- // @limit(lines: 200)
4
+ // @limit(lines: 500)
5
5
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'
6
6
  import { join, relative, dirname, isAbsolute } from 'path'
7
7
  import chalk from 'chalk'