openprompt-lang 1.2.6 → 1.3.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/README.md +62 -8
- package/docs/EMBEDDINGS.md +214 -0
- package/docs/FRAMEWORK.md +52 -0
- package/docs/ONBOARDING_WORKFLOW.md +151 -0
- package/docs/OPL-ERRORES.md +504 -0
- package/docs/OPL_ACADEMIC_ISSUES.md +158 -0
- package/docs/WEB_SCRAPER_PLAN.md +454 -0
- package/package.json +7 -1
- package/scripts/postinstall.js +37 -0
- package/src/cli/commands-knowledge.js +1 -0
- package/src/cli/commands-opl.js +79 -1
- package/src/cli/commands-work.js +3 -1
- package/src/cli/commands-workflow.js +125 -6
- package/src/commands/init-core.js +188 -12
- package/src/commands/init-existing.js +13 -6
- package/src/commands/init-helpers.js +20 -14
- package/src/commands/knowledge-ops.js +52 -0
- package/src/commands/opl-embeddings.js +556 -0
- package/src/commands/opl-help.js +26 -2
- package/src/commands/opl-search.js +106 -2
- package/src/commands/opl-webscrape.js +390 -0
- package/src/commands/work-context.js +17 -0
- package/src/commands/workflow/close/index.js +2 -1
- package/src/commands/workflow/delivery/index.js +4 -0
- package/src/commands/workflow/discovery/index.js +4 -0
- package/src/commands/workflow/epic-cli.js +192 -0
- package/src/commands/workflow/select.js +146 -0
- package/src/commands/workflow/specification/index.js +4 -0
- package/src/commands/workflow/sprint-cli.js +174 -0
- package/src/core/engine/sandbox.js +7 -3
- package/src/core/webscrape/analyzer.js +481 -0
- package/src/core/webscrape/deep-scraper.js +1027 -0
- package/src/core/workflow/epic-manager.js +845 -0
- package/src/core/workflow/gates.js +180 -1
- package/src/core/workflow/selector.js +707 -0
- package/src/embeddings/chunker.js +450 -0
- package/src/embeddings/embedder.js +431 -0
- package/src/embeddings/index-pipeline.js +320 -0
- package/src/embeddings/vector-store.js +505 -0
- package/src/mcp-plan-server.js +12 -5
- package/src/mcp-shared-state.js +25 -0
- package/src/mcp-refactor/mcp-server.js +0 -171
- package/src/mcp-server-backup.js +0 -1913
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
// @use(commander, kind, contract, limit)
|
|
2
2
|
// @kind(util)
|
|
3
3
|
// @contract(in: program -> out: void, sideEffect: registra comandos workflow)
|
|
4
|
-
// @limit(lines:
|
|
4
|
+
// @limit(lines: 200)
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Registra los comandos de workflow OPL.
|
|
8
8
|
*
|
|
9
9
|
* Comandos:
|
|
10
|
-
* opl workflow discovery
|
|
11
|
-
* opl workflow specification
|
|
12
|
-
* opl workflow delivery
|
|
13
|
-
* opl workflow close
|
|
10
|
+
* opl workflow discovery → E.1 — Wizard de descubrimiento
|
|
11
|
+
* opl workflow specification → E.2 — Generación de documentos
|
|
12
|
+
* opl workflow delivery → E.3 — Desarrollo por tickets
|
|
13
|
+
* opl workflow close → E.4 — Cierre de sesión
|
|
14
|
+
* opl workflow select → E.5 — Selector inteligente de workflow
|
|
15
|
+
* opl epic → E.6 — Gestión de épicas
|
|
16
|
+
* opl sprint → E.7 — Gestión de sprints
|
|
14
17
|
*/
|
|
15
18
|
|
|
16
19
|
export function register(program) {
|
|
20
|
+
// ──────────────────────────────────────────────
|
|
21
|
+
// opl workflow — Gestión de proyectos
|
|
22
|
+
// ──────────────────────────────────────────────
|
|
17
23
|
const workflow = program
|
|
18
24
|
.command("workflow")
|
|
19
|
-
.description(
|
|
25
|
+
.description(
|
|
26
|
+
"Gestionar workflows de proyecto: discovery, specification, delivery, close, select"
|
|
27
|
+
)
|
|
20
28
|
|
|
21
29
|
workflow
|
|
22
30
|
.command("discovery")
|
|
@@ -71,4 +79,115 @@ export function register(program) {
|
|
|
71
79
|
reportDir: options.reports,
|
|
72
80
|
})
|
|
73
81
|
})
|
|
82
|
+
|
|
83
|
+
// ─── E.5 — Selector inteligente de workflow ─────────────────────────────
|
|
84
|
+
workflow
|
|
85
|
+
.command("select [description]")
|
|
86
|
+
.description("Seleccionar workflow óptimo según descripción de la tarea")
|
|
87
|
+
.option("--list", "Listar workflows disponibles")
|
|
88
|
+
.option("--strict", "Modo estricto: muestra gates requeridos")
|
|
89
|
+
.option("--json", "Salida en JSON")
|
|
90
|
+
.option("--status", "Mostrar reporte de estado del proyecto")
|
|
91
|
+
.action(async (description, options) => {
|
|
92
|
+
const { workflowSelect } = await import("../commands/workflow/select.js")
|
|
93
|
+
await workflowSelect(description, options)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// ──────────────────────────────────────────────
|
|
97
|
+
// opl epic — Gestión de épicas
|
|
98
|
+
// ──────────────────────────────────────────────
|
|
99
|
+
const epic = program
|
|
100
|
+
.command("epic")
|
|
101
|
+
.description("Gestionar épicas: agrupar tickets en features grandes")
|
|
102
|
+
|
|
103
|
+
epic
|
|
104
|
+
.command("create")
|
|
105
|
+
.description("Crear una nueva épica con tickets automáticos")
|
|
106
|
+
.option("--title <title>", "Título de la épica")
|
|
107
|
+
.option("--desc <desc>", "Descripción detallada (genera tickets automáticamente)")
|
|
108
|
+
.option("--domain <domain>", "Dominio asociado")
|
|
109
|
+
.option("--no-auto-tickets", "No generar tickets automáticamente")
|
|
110
|
+
.action(async (options) => {
|
|
111
|
+
const { epicCreate } = await import("../commands/workflow/epic-cli.js")
|
|
112
|
+
await epicCreate(options)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
epic
|
|
116
|
+
.command("list")
|
|
117
|
+
.description("Listar todas las épicas")
|
|
118
|
+
.option("--status <status>", "Filtrar por estado: planning, active, done, cancelled")
|
|
119
|
+
.option("--domain <domain>", "Filtrar por dominio")
|
|
120
|
+
.action(async (options) => {
|
|
121
|
+
const { epicList } = await import("../commands/workflow/epic-cli.js")
|
|
122
|
+
await epicList(options)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
epic
|
|
126
|
+
.command("show <epicId>")
|
|
127
|
+
.description("Mostrar detalle de una épica")
|
|
128
|
+
.action(async (epicId) => {
|
|
129
|
+
const { epicShow } = await import("../commands/workflow/epic-cli.js")
|
|
130
|
+
await epicShow(epicId)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
epic
|
|
134
|
+
.command("close <epicId>")
|
|
135
|
+
.description("Cerrar una épica")
|
|
136
|
+
.option("--status <status>", "Estado final (default: done)", "done")
|
|
137
|
+
.action(async (epicId, options) => {
|
|
138
|
+
const { epicClose } = await import("../commands/workflow/epic-cli.js")
|
|
139
|
+
await epicClose(epicId, options)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
epic
|
|
143
|
+
.command("status")
|
|
144
|
+
.description("Mostrar reporte de estado del proyecto basado en épicas y sprints")
|
|
145
|
+
.action(async () => {
|
|
146
|
+
const { epicStatus } = await import("../commands/workflow/epic-cli.js")
|
|
147
|
+
await epicStatus()
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
// ──────────────────────────────────────────────
|
|
151
|
+
// opl sprint — Gestión de sprints
|
|
152
|
+
// ──────────────────────────────────────────────
|
|
153
|
+
const sprint = program
|
|
154
|
+
.command("sprint")
|
|
155
|
+
.description("Gestionar sprints: planificar iteraciones de desarrollo")
|
|
156
|
+
|
|
157
|
+
sprint
|
|
158
|
+
.command("create <name>")
|
|
159
|
+
.description("Crear un nuevo sprint")
|
|
160
|
+
.requiredOption("--goal <goal>", "Objetivo del sprint")
|
|
161
|
+
.option("--duration <days>", "Duración en días (default: 14)")
|
|
162
|
+
.option("--start-date <date>", "Fecha de inicio (default: hoy)")
|
|
163
|
+
.action(async (name, options) => {
|
|
164
|
+
const { sprintCreate } = await import("../commands/workflow/sprint-cli.js")
|
|
165
|
+
await sprintCreate(name, options)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
sprint
|
|
169
|
+
.command("list")
|
|
170
|
+
.description("Listar sprints")
|
|
171
|
+
.option("--status <status>", "Filtrar por estado: planning, active, completed")
|
|
172
|
+
.action(async (options) => {
|
|
173
|
+
const { sprintList } = await import("../commands/workflow/sprint-cli.js")
|
|
174
|
+
await sprintList(options)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
sprint
|
|
178
|
+
.command("plan <sprintId>")
|
|
179
|
+
.description("Planificar sprint automáticamente desde épicas activas")
|
|
180
|
+
.option("--capacity <n>", "Capacidad en tickets (default: 10)")
|
|
181
|
+
.action(async (sprintId, options) => {
|
|
182
|
+
const { sprintPlan } = await import("../commands/workflow/sprint-cli.js")
|
|
183
|
+
await sprintPlan(sprintId, options)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
sprint
|
|
187
|
+
.command("close <sprintId>")
|
|
188
|
+
.description("Cerrar un sprint")
|
|
189
|
+
.action(async (sprintId) => {
|
|
190
|
+
const { sprintClose } = await import("../commands/workflow/sprint-cli.js")
|
|
191
|
+
await sprintClose(sprintId)
|
|
192
|
+
})
|
|
74
193
|
}
|
|
@@ -14,8 +14,13 @@ export async function init(options) {
|
|
|
14
14
|
const interactive = options.interactive !== false && !!process.stdin.isTTY
|
|
15
15
|
const dryRun = options.dryRun || false
|
|
16
16
|
|
|
17
|
+
// Stack y extensiones (se actualizan si selectFullStack se ejecuta)
|
|
18
|
+
const finalStack = options.stack ? options.stack.split(",").map((s) => s.trim()) : []
|
|
19
|
+
const finalExtensions = []
|
|
20
|
+
|
|
17
21
|
const cwd = process.cwd()
|
|
18
|
-
const isInitialized =
|
|
22
|
+
const isInitialized =
|
|
23
|
+
existsSync(join(cwd, "prompt-lang.json")) || existsSync(join(cwd, ".openprompt"))
|
|
19
24
|
|
|
20
25
|
if (interactive && !options.existing && !options.name && !options.preset && !options.stack) {
|
|
21
26
|
console.log(chalk.cyan("\n🧙 openPrompt-Lang — Inicialización\n"))
|
|
@@ -23,7 +28,10 @@ export async function init(options) {
|
|
|
23
28
|
const choices = []
|
|
24
29
|
|
|
25
30
|
if (isInitialized) {
|
|
26
|
-
choices.push({
|
|
31
|
+
choices.push({
|
|
32
|
+
name: chalk.yellow(" Actualizar/Reconstruir la integración (Rebuild)"),
|
|
33
|
+
value: "rebuild",
|
|
34
|
+
})
|
|
27
35
|
choices.push(new inquirer.Separator())
|
|
28
36
|
}
|
|
29
37
|
|
|
@@ -41,7 +49,9 @@ export async function init(options) {
|
|
|
41
49
|
{
|
|
42
50
|
type: "list",
|
|
43
51
|
name: "projectType",
|
|
44
|
-
message: isInitialized
|
|
52
|
+
message: isInitialized
|
|
53
|
+
? "Este proyecto ya tiene OPL. ¿Qué deseas hacer?"
|
|
54
|
+
: "¿Qué tipo de proyecto?",
|
|
45
55
|
choices,
|
|
46
56
|
},
|
|
47
57
|
])
|
|
@@ -134,22 +144,188 @@ export async function init(options) {
|
|
|
134
144
|
// ── Verificar OpenCode ──
|
|
135
145
|
await checkAndInstallOpencode(interactive)
|
|
136
146
|
|
|
147
|
+
// ── Generar ONBOARDING_WORKFLOW.md ──
|
|
148
|
+
if (!dryRun) {
|
|
149
|
+
try {
|
|
150
|
+
const onboardingPath = join(finalBaseDir, "docs", "ONBOARDING_WORKFLOW.md")
|
|
151
|
+
const onboardingContent = generateOnboardingMd(finalName, finalStack, finalExtensions)
|
|
152
|
+
if (!existsSync(join(finalBaseDir, "docs"))) {
|
|
153
|
+
mkdirSync(join(finalBaseDir, "docs"), { recursive: true })
|
|
154
|
+
}
|
|
155
|
+
writeFileSync(onboardingPath, onboardingContent, "utf-8")
|
|
156
|
+
console.log(chalk.green(` ✅ docs/ONBOARDING_WORKFLOW.md — Guía para tu IA`))
|
|
157
|
+
} catch {
|
|
158
|
+
// No crítico si falla
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
137
162
|
// ── Handoff a la IA ──
|
|
138
163
|
console.log(chalk.cyan("\n✨ OPL Init: Estructura base lista."))
|
|
139
164
|
console.log(chalk.yellow("OPL delega ahora la inicialización estratégica a tu Agente IA."))
|
|
140
|
-
console.log(
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
console.log(
|
|
166
|
+
chalk.white(
|
|
167
|
+
"Copia y pega el siguiente prompt en tu chat con la IA (ej. Cline, Cursor, OpenCode, etc.):\n"
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const stackStr = finalStack ? finalStack.join(", ") : "por definir"
|
|
172
|
+
const extStr = finalExtensions?.length ? `\nExtensiones: ${finalExtensions.join(", ")}` : ""
|
|
173
|
+
|
|
174
|
+
console.log(
|
|
175
|
+
chalk.bgGray.white(`
|
|
143
176
|
"Asume el rol de Arquitecto Principal OPL. Acabo de ejecutar opl init para un proyecto NUEVO llamado '${finalName}'.
|
|
177
|
+
Stack base: ${stackStr}${extStr}
|
|
144
178
|
|
|
145
179
|
Tu proceso obligatorio es:
|
|
146
180
|
1. Ejecuta 'analyze_project' para inspeccionar el directorio base.
|
|
147
|
-
2.
|
|
148
|
-
3.
|
|
149
|
-
4.
|
|
150
|
-
5.
|
|
151
|
-
6.
|
|
152
|
-
|
|
181
|
+
2. Lee el archivo 'docs/ONBOARDING_WORKFLOW.md' (recién generado).
|
|
182
|
+
3. Hazme preguntas para entender el contexto del proyecto (dominio, stack preferido, funcionalidades clave).
|
|
183
|
+
4. Decide el stack óptimo con base en mis respuestas.
|
|
184
|
+
5. Usa 'init_project' y 'scaffold_project' para configurar el proyecto.
|
|
185
|
+
6. Genera los archivos canónicos: AGENTS.md, prompt-lang.json, .openprompt/FRAMEWORK.md.
|
|
186
|
+
7. Confirma explícitamente que el proyecto quedó estabilizado e indícame el siguiente paso operativo."
|
|
187
|
+
`)
|
|
188
|
+
)
|
|
153
189
|
|
|
154
190
|
return finalBaseDir
|
|
155
191
|
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Genera el contenido de ONBOARDING_WORKFLOW.md para un proyecto nuevo.
|
|
195
|
+
*
|
|
196
|
+
* @param {string} projectName
|
|
197
|
+
* @param {string[]} stack
|
|
198
|
+
* @param {string[]} extensions
|
|
199
|
+
* @returns {string}
|
|
200
|
+
*/
|
|
201
|
+
function generateOnboardingMd(projectName, stack, extensions) {
|
|
202
|
+
const stackSection = stack?.length
|
|
203
|
+
? stack.map((s) => ` - ${s}`).join("\n")
|
|
204
|
+
: " - Por definir durante la inicialización"
|
|
205
|
+
|
|
206
|
+
const extSection = extensions?.length
|
|
207
|
+
? extensions.map((e) => ` - ${e}`).join("\n")
|
|
208
|
+
: " - (ninguna por ahora)"
|
|
209
|
+
|
|
210
|
+
return `# ONBOARDING WORKFLOW — ${projectName}
|
|
211
|
+
|
|
212
|
+
Generado automáticamente por \`opl init\`.
|
|
213
|
+
|
|
214
|
+
## 🎯 Propósito
|
|
215
|
+
|
|
216
|
+
Este documento le indica a la IA qué pasos seguir al ser invocada en este proyecto por primera vez. Reduce la fricción de onboarding y asegura que la IA entienda el stack, la estructura y el workflow del proyecto.
|
|
217
|
+
|
|
218
|
+
## 📋 Stack Detectado
|
|
219
|
+
|
|
220
|
+
${stackSection}
|
|
221
|
+
|
|
222
|
+
## 🔌 Extensiones
|
|
223
|
+
|
|
224
|
+
${extSection}
|
|
225
|
+
|
|
226
|
+
## 🏗️ Estructura del Proyecto
|
|
227
|
+
|
|
228
|
+
\`\`\`
|
|
229
|
+
.
|
|
230
|
+
├── AGENTS.md → Contexto para IA (stack, convenciones, reglas)
|
|
231
|
+
├── prompt-lang.json → Configuración OPL
|
|
232
|
+
├── docs/
|
|
233
|
+
│ ├── AI_CONTEXT.md → Contexto extendido
|
|
234
|
+
│ ├── FRAMEWORK.md → Referencia canónica del framework
|
|
235
|
+
│ ├── ONBOARDING_WORKFLOW.md → Este archivo
|
|
236
|
+
│ └── ...
|
|
237
|
+
├── .openprompt/
|
|
238
|
+
│ └── FRAMEWORK.md → Manual completo de OPL
|
|
239
|
+
├── .opencode/
|
|
240
|
+
│ └── work-context/ → Sesiones, planes, logs
|
|
241
|
+
└── src/
|
|
242
|
+
└── ...
|
|
243
|
+
\`\`\`
|
|
244
|
+
|
|
245
|
+
## 🔄 Workflow Obligatorio para la IA (v1.3.0)
|
|
246
|
+
|
|
247
|
+
Siempre que se te pida trabajar en este proyecto, SIGUE ESTE FLUJO ESTRICTAMENTE.
|
|
248
|
+
No hay excepciones. Los gates bloquean herramientas si se omiten pasos.
|
|
249
|
+
|
|
250
|
+
### ⚠️ REGLAS INQUEBRANTABLES
|
|
251
|
+
|
|
252
|
+
1. **workflow select primero**: Sin workflow seleccionado no hay implementación
|
|
253
|
+
2. **ticket create antes de código**: Sin ticket abierto no se escribe código
|
|
254
|
+
3. **docs_updated antes de cerrar**: Sin documentación no se cierra sesión
|
|
255
|
+
|
|
256
|
+
### Flujo Completo (8 pasos, siempre en este orden)
|
|
257
|
+
|
|
258
|
+
\`\`\`
|
|
259
|
+
PASO 1 → workflow_select
|
|
260
|
+
"opl workflow select <descripción de la tarea>"
|
|
261
|
+
La IA describe qué necesita hacer, el sistema selecciona el workflow óptimo
|
|
262
|
+
(feature, bugfix, refactor, docs, research, etc.)
|
|
263
|
+
|
|
264
|
+
PASO 2 → create_ticket
|
|
265
|
+
"opl ticket create --title ... --severity ..."
|
|
266
|
+
TODO cambio requiere ticket ANTES de tocar código
|
|
267
|
+
|
|
268
|
+
PASO 3 → check_gates
|
|
269
|
+
Verificar: workflow_selected ✓, ticket_created ✓, plan_approved ✓
|
|
270
|
+
|
|
271
|
+
PASO 4 → work_context_plan
|
|
272
|
+
Planificar implementación con búsqueda de conocimiento
|
|
273
|
+
|
|
274
|
+
PASO 5 → implement
|
|
275
|
+
Anotaciones OPL primero (@kind → @contract → @limit → @deps)
|
|
276
|
+
Luego implementar lógica
|
|
277
|
+
|
|
278
|
+
PASO 6 → validate
|
|
279
|
+
"opl validate" — verificar anotaciones antes de cerrar
|
|
280
|
+
|
|
281
|
+
PASO 7 → docs_updated
|
|
282
|
+
Actualizar documentación. OBLIGATORIO antes de cerrar sesión.
|
|
283
|
+
|
|
284
|
+
PASO 8 → work_context_close
|
|
285
|
+
Cerrar sesión y generar reporte con aprendizajes
|
|
286
|
+
\`\`\`
|
|
287
|
+
|
|
288
|
+
### Para features grandes: Épicas y Sprints
|
|
289
|
+
|
|
290
|
+
\`\`\`
|
|
291
|
+
# Crear épica (agrupa tickets automáticamente desde la descripción)
|
|
292
|
+
opl epic create --title "Sistema de Reportes" --desc "Dashboard + exportación PDF + filtros"
|
|
293
|
+
|
|
294
|
+
# Crear sprint con objetivo y duración
|
|
295
|
+
opl sprint create "Sprint 1" --goal "Implementar core del sistema"
|
|
296
|
+
|
|
297
|
+
# Planificar sprint automáticamente desde épicas activas
|
|
298
|
+
opl sprint plan SPRINT-001 --capacity 10
|
|
299
|
+
|
|
300
|
+
# Ver estado del proyecto
|
|
301
|
+
opl epic status
|
|
302
|
+
\`\`\`
|
|
303
|
+
|
|
304
|
+
## ⚙️ Comandos Rápidos
|
|
305
|
+
|
|
306
|
+
| Comando | Descripción |
|
|
307
|
+
|---------|-------------|
|
|
308
|
+
| \`opl workflow select <desc>\` | 🥇 Seleccionar workflow óptimo (USAR SIEMPRE PRIMERO) |
|
|
309
|
+
| \`opl ticket create --title "..."\` | 🥇 Crear ticket (USAR SIEMPRE ANTES DE CÓDIGO) |
|
|
310
|
+
| \`opl epic create --title "..." --desc "..."\` | Crear épica con tickets automáticos |
|
|
311
|
+
| \`opl epic list\` | Listar épicas con progreso |
|
|
312
|
+
| \`opl sprint create <name> --goal "..."\` | Crear sprint |
|
|
313
|
+
| \`opl sprint plan <id> --capacity N\` | Planificar sprint automáticamente |
|
|
314
|
+
| \`opl index\` | Navegar conocimiento |
|
|
315
|
+
| \`opl search <q>\` | Buscar en conocimiento (--mode vector para semántica) |
|
|
316
|
+
| \`opl read <d>/<id>\` | Leer documento |
|
|
317
|
+
| \`opl system <name>\` | Explorar sistemas |
|
|
318
|
+
| \`opl assess\` | Production Readiness Assessment |
|
|
319
|
+
| \`opl embeddings status\` | Estado del índice vectorial |
|
|
320
|
+
| \`opl knowledge ingest <file>\` | Ingestar PDF |
|
|
321
|
+
| \`opl webscrape <url>\` | Extraer contenido web e indexar |
|
|
322
|
+
|
|
323
|
+
## 📚 Documentación Vinculada
|
|
324
|
+
|
|
325
|
+
- \`docs/AI_CONTEXT.md\` → Stack técnico y módulos
|
|
326
|
+
- \`docs/FRAMEWORK.md\` → Especificación del framework
|
|
327
|
+
- \`docs/EMBEDDINGS.md\` → Sistema de embeddings vectoriales
|
|
328
|
+
- \`AGENTS.md\` → Contexto completo para IA
|
|
329
|
+
- \`.openprompt/FRAMEWORK.md\` → Manual canónico de OPL
|
|
330
|
+
`
|
|
331
|
+
}
|
|
@@ -138,15 +138,20 @@ export async function initExisting(options = {}) {
|
|
|
138
138
|
const interactive = options.interactive !== false && !!process.stdin.isTTY
|
|
139
139
|
if (!rebuild) {
|
|
140
140
|
console.log(chalk.cyan(`\n🔧 Inicializando OPL en proyecto existente: ${projectName}\n`))
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
// ── Verificar OpenCode ──
|
|
143
143
|
await checkAndInstallOpencode(interactive)
|
|
144
144
|
|
|
145
145
|
console.log(chalk.cyan("✨ OPL Init: Entorno detectado."))
|
|
146
146
|
console.log(chalk.yellow("OPL delega ahora la inicialización estratégica a tu Agente IA."))
|
|
147
|
-
console.log(
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
console.log(
|
|
148
|
+
chalk.white(
|
|
149
|
+
"Copia y pega el siguiente prompt en tu chat con la IA (ej. Cline, Cursor, OpenCode, etc.):\n"
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
console.log(
|
|
154
|
+
chalk.bgGray.white(`
|
|
150
155
|
"Asume el rol de Arquitecto Principal OPL. Acabo de ejecutar opl init para un proyecto EXISTENTE llamado '${projectName}'.
|
|
151
156
|
|
|
152
157
|
Tu proceso obligatorio es:
|
|
@@ -155,7 +160,8 @@ Tu proceso obligatorio es:
|
|
|
155
160
|
3. Genera la estructura que falte (docs/, work-context, AGENTS.md, prompt-lang.json, .openprompt/FRAMEWORK.md).
|
|
156
161
|
4. Organiza un backlog inicial de refactorización con la deuda detectada.
|
|
157
162
|
5. Confirma explícitamente que el proyecto quedó estabilizado e indícame el siguiente paso operativo."
|
|
158
|
-
`)
|
|
163
|
+
`)
|
|
164
|
+
)
|
|
159
165
|
return cwd
|
|
160
166
|
}
|
|
161
167
|
|
|
@@ -431,7 +437,8 @@ Tu proceso obligatorio es:
|
|
|
431
437
|
{
|
|
432
438
|
type: "confirm",
|
|
433
439
|
name: "updateKnowledge",
|
|
434
|
-
message:
|
|
440
|
+
message:
|
|
441
|
+
"¿Deseas descargar/actualizar la biblioteca de conocimientos (Entorno Académico)?",
|
|
435
442
|
default: false,
|
|
436
443
|
},
|
|
437
444
|
])
|
|
@@ -535,35 +535,41 @@ export async function selectFullStack(options) {
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
export async function checkAndInstallOpencode(interactive) {
|
|
538
|
-
if (!interactive) return
|
|
538
|
+
if (!interactive) return
|
|
539
539
|
|
|
540
|
-
let isInstalled = false
|
|
540
|
+
let isInstalled = false
|
|
541
541
|
try {
|
|
542
|
-
execSync("opencode --version", { stdio: "ignore" })
|
|
543
|
-
isInstalled = true
|
|
542
|
+
execSync("opencode --version", { stdio: "ignore" })
|
|
543
|
+
isInstalled = true
|
|
544
544
|
} catch (e) {
|
|
545
|
-
isInstalled = false
|
|
545
|
+
isInstalled = false
|
|
546
546
|
}
|
|
547
547
|
|
|
548
548
|
if (!isInstalled) {
|
|
549
|
-
console.log(chalk.yellow("\n⚠️ No se detectó 'opencode' instalado globalmente."))
|
|
550
|
-
console.log(
|
|
549
|
+
console.log(chalk.yellow("\n⚠️ No se detectó 'opencode' instalado globalmente."))
|
|
550
|
+
console.log(
|
|
551
|
+
chalk.gray("OpenCode es el agente CLI recomendado para aprovechar la integración MCP de OPL.")
|
|
552
|
+
)
|
|
551
553
|
const { install } = await inquirer.prompt([
|
|
552
554
|
{
|
|
553
555
|
type: "confirm",
|
|
554
556
|
name: "install",
|
|
555
557
|
message: "¿Deseas instalar opencode globalmente ahora? (npm install -g opencode)",
|
|
556
|
-
default: true
|
|
557
|
-
}
|
|
558
|
-
])
|
|
558
|
+
default: true,
|
|
559
|
+
},
|
|
560
|
+
])
|
|
559
561
|
|
|
560
562
|
if (install) {
|
|
561
|
-
console.log(chalk.cyan("⏳ Instalando opencode..."))
|
|
563
|
+
console.log(chalk.cyan("⏳ Instalando opencode..."))
|
|
562
564
|
try {
|
|
563
|
-
execSync("npm install -g opencode", { stdio: "inherit" })
|
|
564
|
-
console.log(chalk.green("✅ opencode instalado exitosamente.\n"))
|
|
565
|
+
execSync("npm install -g opencode", { stdio: "inherit" })
|
|
566
|
+
console.log(chalk.green("✅ opencode instalado exitosamente.\n"))
|
|
565
567
|
} catch (e) {
|
|
566
|
-
console.log(
|
|
568
|
+
console.log(
|
|
569
|
+
chalk.red(
|
|
570
|
+
"❌ Error instalando opencode. Puedes instalarlo manualmente con: npm install -g opencode\n"
|
|
571
|
+
)
|
|
572
|
+
)
|
|
567
573
|
}
|
|
568
574
|
}
|
|
569
575
|
}
|
|
@@ -287,9 +287,61 @@ export async function ingest(filePath, options = {}) {
|
|
|
287
287
|
rebuildIndex()
|
|
288
288
|
|
|
289
289
|
console.log(chalk.cyan(`\n✅ PDF procesado: ${shortName}\n`))
|
|
290
|
+
|
|
291
|
+
// ── Auto-embedding hook (TICKET-007) ─────────────────────
|
|
292
|
+
if (options.embed !== false) {
|
|
293
|
+
await autoEmbedPdf(pdfId, entry, chapters)
|
|
294
|
+
}
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
export async function rebuild(_options = {}) {
|
|
293
298
|
const count = rebuildIndex()
|
|
294
299
|
console.log(chalk.cyan(`\n✅ Índice reconstruido: ${count.length} PDFs\n`))
|
|
295
300
|
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Auto-embedding hook: después de ingestar un PDF, indexa sus embeddings.
|
|
304
|
+
*
|
|
305
|
+
* @param {string} pdfId - ID del PDF procesado
|
|
306
|
+
* @param {Object} entry - Metadatos del PDF
|
|
307
|
+
* @param {Array} chapters - Capítulos detectados
|
|
308
|
+
*/
|
|
309
|
+
async function autoEmbedPdf(pdfId, entry, chapters) {
|
|
310
|
+
try {
|
|
311
|
+
const { indexDocument } = await import("../embeddings/index-pipeline.js")
|
|
312
|
+
|
|
313
|
+
// Construir documento en formato OPL
|
|
314
|
+
const doc = {
|
|
315
|
+
id: pdfId,
|
|
316
|
+
title: entry.title || pdfId,
|
|
317
|
+
chapters: chapters.map((ch, i) => ({
|
|
318
|
+
index: ch.index ?? i,
|
|
319
|
+
title: ch.title || "",
|
|
320
|
+
content: ch.content || "",
|
|
321
|
+
})),
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (doc.chapters.length === 0) {
|
|
325
|
+
console.log(chalk.gray(" ⏭️ Sin capítulos para indexar (embedding omitido)."))
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
console.log(chalk.blue(" 🧠 Indexando embeddings..."))
|
|
330
|
+
|
|
331
|
+
const result = await indexDocument(doc, { strategy: "section" })
|
|
332
|
+
|
|
333
|
+
if (result.success) {
|
|
334
|
+
console.log(
|
|
335
|
+
chalk.green(` ✅ Embeddings: ${result.indexedChunks} chunks · ${result.durationMs}ms`)
|
|
336
|
+
)
|
|
337
|
+
} else {
|
|
338
|
+
console.log(
|
|
339
|
+
chalk.yellow(
|
|
340
|
+
` ⚠️ Embedding parcial: ${result.indexedChunks}/${result.totalChunks} chunks`
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.log(chalk.gray(` ⏭️ Embedding no disponible (${error.message})`))
|
|
346
|
+
}
|
|
347
|
+
}
|