openprompt-lang 1.2.7 → 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/ONBOARDING_WORKFLOW.md +151 -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-workflow.js +125 -6
- package/src/commands/init-core.js +169 -5
- 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/workflow/epic-cli.js +192 -0
- package/src/commands/workflow/select.js +146 -0
- package/src/commands/workflow/sprint-cli.js +174 -0
- 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
|
@@ -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,6 +14,10 @@ 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
22
|
const isInitialized =
|
|
19
23
|
existsSync(join(cwd, "prompt-lang.json")) || existsSync(join(cwd, ".openprompt"))
|
|
@@ -140,6 +144,21 @@ export async function init(options) {
|
|
|
140
144
|
// ── Verificar OpenCode ──
|
|
141
145
|
await checkAndInstallOpencode(interactive)
|
|
142
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
|
+
|
|
143
162
|
// ── Handoff a la IA ──
|
|
144
163
|
console.log(chalk.cyan("\n✨ OPL Init: Estructura base lista."))
|
|
145
164
|
console.log(chalk.yellow("OPL delega ahora la inicialización estratégica a tu Agente IA."))
|
|
@@ -149,19 +168,164 @@ export async function init(options) {
|
|
|
149
168
|
)
|
|
150
169
|
)
|
|
151
170
|
|
|
171
|
+
const stackStr = finalStack ? finalStack.join(", ") : "por definir"
|
|
172
|
+
const extStr = finalExtensions?.length ? `\nExtensiones: ${finalExtensions.join(", ")}` : ""
|
|
173
|
+
|
|
152
174
|
console.log(
|
|
153
175
|
chalk.bgGray.white(`
|
|
154
176
|
"Asume el rol de Arquitecto Principal OPL. Acabo de ejecutar opl init para un proyecto NUEVO llamado '${finalName}'.
|
|
177
|
+
Stack base: ${stackStr}${extStr}
|
|
155
178
|
|
|
156
179
|
Tu proceso obligatorio es:
|
|
157
180
|
1. Ejecuta 'analyze_project' para inspeccionar el directorio base.
|
|
158
|
-
2.
|
|
159
|
-
3.
|
|
160
|
-
4.
|
|
161
|
-
5.
|
|
162
|
-
6.
|
|
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."
|
|
163
187
|
`)
|
|
164
188
|
)
|
|
165
189
|
|
|
166
190
|
return finalBaseDir
|
|
167
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
|
+
}
|
|
@@ -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
|
+
}
|