openprompt-lang 1.2.7 β†’ 1.4.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 (79) hide show
  1. package/README.md +62 -8
  2. package/bin/cli.js +2 -0
  3. package/docs/00-ARCHITECTURE/OPL-BOOST-MULTI-AGENT.md +406 -0
  4. package/docs/02-STANDARDS/AGENTS.template.md +89 -0
  5. package/docs/02-STANDARDS/ticket-driven-development.md +99 -0
  6. package/docs/04-TICKETS/BOOST-001-profile-registry.md +66 -0
  7. package/docs/04-TICKETS/BOOST-002-context-compression.md +58 -0
  8. package/docs/04-TICKETS/BOOST-003-template-hydration.md +69 -0
  9. package/docs/04-TICKETS/BOOST-004-fewshot-engine.md +58 -0
  10. package/docs/04-TICKETS/BOOST-005-agent-pool.md +69 -0
  11. package/docs/04-TICKETS/BOOST-006-specialized-agents.md +53 -0
  12. package/docs/04-TICKETS/BOOST-007-validation-loop.md +56 -0
  13. package/docs/04-TICKETS/BOOST-008-orchestrator.md +71 -0
  14. package/docs/04-TICKETS/BOOST-009-cache-system.md +56 -0
  15. package/docs/04-TICKETS/BOOST-010-cli-mcp.md +67 -0
  16. package/docs/04-TICKETS/BOOST-011-self-learning.md +50 -0
  17. package/docs/04-TICKETS/BOOST-012-prompt-preamble.md +109 -0
  18. package/docs/04-TICKETS/BOOST-013-hydrator-duplicate-code.md +132 -0
  19. package/docs/04-TICKETS/BOOST-014-multiagent-missing-parts.md +87 -0
  20. package/docs/04-TICKETS/BOOST-015-skeleton-type-missing.md +76 -0
  21. package/docs/04-TICKETS/BOOST-016-output-path-duplicate.md +68 -0
  22. package/docs/04-TICKETS/INDEX.md +89 -0
  23. package/docs/04-TICKETS/_archive/BOOST-005-micro-tasking.md +67 -0
  24. package/docs/04-TICKETS/_archive/BOOST-006-validation-loop.md +66 -0
  25. package/docs/04-TICKETS/_archive/BOOST-007-progressive-pipeline.md +69 -0
  26. package/docs/04-TICKETS/_archive/BOOST-008-cli-mcp-integration.md +74 -0
  27. package/docs/AI_CONTEXT.md +16 -0
  28. package/docs/EMBEDDINGS.md +214 -0
  29. package/docs/ONBOARDING_WORKFLOW.md +151 -0
  30. package/docs/OPL_ACADEMIC_ISSUES.md +158 -0
  31. package/docs/WEB_SCRAPER_PLAN.md +454 -0
  32. package/package.json +9 -2
  33. package/scripts/postinstall.js +37 -0
  34. package/src/boost/agent-pool.js +442 -0
  35. package/src/boost/agents/index.js +79 -0
  36. package/src/boost/cache.js +241 -0
  37. package/src/boost/context-compressor.js +354 -0
  38. package/src/boost/fewshot-retriever.js +332 -0
  39. package/src/boost/hardware-detector.js +486 -0
  40. package/src/boost/hydrator.js +398 -0
  41. package/src/boost/index.js +60 -0
  42. package/src/boost/orchestrator.js +615 -0
  43. package/src/boost/preamble.js +217 -0
  44. package/src/boost/profile-registry.js +264 -0
  45. package/src/boost/self-learn.js +247 -0
  46. package/src/boost/skeletons/component.skeleton.js +24 -0
  47. package/src/boost/skeletons/hook.skeleton.js +27 -0
  48. package/src/boost/skeletons/index.js +67 -0
  49. package/src/boost/skeletons/page.skeleton.js +22 -0
  50. package/src/boost/skeletons/service.skeleton.js +20 -0
  51. package/src/boost/skeletons/store.skeleton.js +18 -0
  52. package/src/boost/skeletons/type.skeleton.js +11 -0
  53. package/src/boost/task-dispatcher.js +142 -0
  54. package/src/boost/validation-loop.js +495 -0
  55. package/src/cli/commands-boost.js +394 -0
  56. package/src/cli/commands-knowledge.js +1 -0
  57. package/src/cli/commands-opl.js +79 -1
  58. package/src/cli/commands-workflow.js +125 -6
  59. package/src/commands/init-core.js +169 -5
  60. package/src/commands/knowledge-ops.js +52 -0
  61. package/src/commands/opl-embeddings.js +556 -0
  62. package/src/commands/opl-help.js +26 -2
  63. package/src/commands/opl-search.js +106 -2
  64. package/src/commands/opl-webscrape.js +390 -0
  65. package/src/commands/workflow/epic-cli.js +192 -0
  66. package/src/commands/workflow/select.js +146 -0
  67. package/src/commands/workflow/sprint-cli.js +174 -0
  68. package/src/core/webscrape/analyzer.js +481 -0
  69. package/src/core/webscrape/deep-scraper.js +1027 -0
  70. package/src/core/workflow/epic-manager.js +845 -0
  71. package/src/core/workflow/gates.js +180 -1
  72. package/src/core/workflow/selector.js +707 -0
  73. package/src/embeddings/chunker.js +450 -0
  74. package/src/embeddings/embedder.js +431 -0
  75. package/src/embeddings/index-pipeline.js +320 -0
  76. package/src/embeddings/vector-store.js +505 -0
  77. package/src/mcp-refactor/handlers/boost.js +295 -0
  78. package/src/mcp-refactor/router.js +19 -0
  79. package/src/mcp-refactor/tools.js +113 -0
@@ -90,6 +90,43 @@ try {
90
90
  console.log(" πŸ’‘ O instΓ‘lalo manualmente segΓΊn tu plataforma.")
91
91
  }
92
92
  }
93
+ // ──────────────────────────────────────────────
94
+ // InformaciΓ³n sobre embeddings y Ollama
95
+ // ──────────────────────────────────────────────
96
+ try {
97
+ const { execSync } = await import("child_process")
98
+ const ollamaPath = execSync("which ollama 2>/dev/null || echo ''", {
99
+ encoding: "utf-8",
100
+ }).trim()
101
+
102
+ if (ollamaPath) {
103
+ const models = execSync("ollama list 2>/dev/null || echo ''", {
104
+ encoding: "utf-8",
105
+ }).trim()
106
+ const hasEmbedModel = models.toLowerCase().includes("nomic-embed-text")
107
+
108
+ if (hasEmbedModel) {
109
+ console.log("βœ… Ollama + nomic-embed-text listos para embeddings semΓ‘nticos")
110
+ } else {
111
+ console.log("πŸ“¦ Ollama detectado. Para embeddings semΓ‘nticos ejecuta:")
112
+ console.log(" ollama pull nomic-embed-text")
113
+ }
114
+ } else {
115
+ console.log("πŸ“¦ Ollama no detectado. Usando Transformers.js como fallback.")
116
+ console.log(" πŸ’‘ Instala Ollama para mejor rendimiento: https://ollama.com")
117
+ console.log(" πŸ’‘ Luego: ollama pull nomic-embed-text")
118
+ }
119
+ } catch {
120
+ // No crΓ­tico
121
+ }
122
+
123
+ console.log("")
124
+ console.log("πŸ” BΓΊsqueda semΓ‘ntica disponible:")
125
+ console.log(' opl search "consulta" --mode vector')
126
+ console.log("")
127
+ console.log("πŸ“Š Indexa documentos en el vector store:")
128
+ console.log(" opl embeddings index <docId>")
129
+ console.log("")
93
130
  console.log("πŸ’‘ Ejecuta 'opl init' para crear un nuevo proyecto con planificaciΓ³n")
94
131
  } catch (err) {
95
132
  console.log(` ⚠️ opencode: ${err.message}`)
@@ -0,0 +1,442 @@
1
+ // @use(kind, contract, limit, error)
2
+ // @kind(util)
3
+ // @contract(in: prompt, role, profile -> out: AgentPool instance + callOllama, buildPrompt, checkOllama @error: OllamaConnectionError, AgentTimeoutError)
4
+ // @limit(lines: 200)
5
+
6
+ /**
7
+ * Agent Pool β€” MΓ³dulo OPL Boost
8
+ *
9
+ * Conector a Ollama + pool de agentes para orquestaciΓ³n interna.
10
+ * Permite que OPL llame a modelos locales sin depender de OpenCode.
11
+ *
12
+ * Modo de uso:
13
+ * import { agentPool } from './agent-pool.js'
14
+ * const result = await agentPool.submit('types', 'hook useAuth')
15
+ *
16
+ * Modo cola (FIFO): default, una solicitud a la vez
17
+ * Modo paralelo: configurable via profile.agentPool.maxConcurrency
18
+ */
19
+
20
+ import { getProfile } from "./profile-registry.js"
21
+
22
+ // ──────────────────────────────────────────────
23
+ // ConfiguraciΓ³n
24
+ // ──────────────────────────────────────────────
25
+
26
+ const OLLAMA_HOST = process.env.OLLAMA_HOST || "http://localhost:11434"
27
+ const DEFAULT_TIMEOUT = 30_000 // 30s
28
+
29
+ // ──────────────────────────────────────────────
30
+ // System prompts por rol de agente
31
+ // ──────────────────────────────────────────────
32
+
33
+ const AGENT_SYSTEM_PROMPTS = {
34
+ types:
35
+ "Eres un experto en TypeScript. Tu ΓΊnica tarea es definir TYPES e INTERFACES. " +
36
+ "No implementes lΓ³gica. No generes JSX. Solo tipos, interfaces, y anotaciones de tipo. " +
37
+ "Output exacto: bloques de cΓ³digo TypeScript con export.",
38
+
39
+ state:
40
+ "Eres un experto en React state management. Recibes types ya definidos. " +
41
+ "Tu tarea es diseΓ±ar el ESTADO del componente/hook: useState, useReducer, useEffect. " +
42
+ "No implementes lΓ³gica de negocio. Solo estructura de estado y efectos. " +
43
+ "Output exacto: bloques de cΓ³digo con hooks de React.",
44
+
45
+ logic:
46
+ "Eres un experto en implementar lΓ³gica de negocio. Recibes types + estado ya definidos. " +
47
+ "Tu tarea es IMPLEMENTAR las funciones con lΓ³gica real. " +
48
+ "Usa los types y estado existentes. No los redefinas. " +
49
+ "Output exacto: la funciΓ³n/componente completo con implementaciΓ³n.",
50
+
51
+ cleaner:
52
+ "Eres un compresor de contexto para desarrollo asistido por IA. " +
53
+ "Recibes reglas extensas y debes devolver SOLO las reglas relevantes para la tarea especΓ­fica. " +
54
+ "Prioriza: lΓ­mites de lΓ­neas (@kind + @limit), reglas de enforcement, prohibiciones (NUNCA). " +
55
+ "Omite: stack, UI, colores, documentaciΓ³n de comandos no relevantes. " +
56
+ "Output exacto: markdown compacto con las reglas esenciales.",
57
+
58
+ validate:
59
+ "Eres un corrector de cΓ³digo. Recibes cΓ³digo que fallΓ³ validaciΓ³n y los errores especΓ­ficos. " +
60
+ "Tu tarea es CORREGIR solo los errores reportados, no reescribir todo. " +
61
+ "Conserva la estructura y lΓ³gica original. Arregla solo lo que fallΓ³. " +
62
+ "Output exacto: el cΓ³digo completo corregido.",
63
+ }
64
+
65
+ // ──────────────────────────────────────────────
66
+ // Builders de prompts
67
+ // ──────────────────────────────────────────────
68
+
69
+ const PROMPT_TEMPLATES = {
70
+ types: (input) =>
71
+ `Task: Define types for "${input}"
72
+
73
+ Rules:
74
+ - Export all interfaces
75
+ - Use TypeScript strict mode
76
+ - Add JSDoc comments
77
+ - Maximum 60 lines
78
+
79
+ Output ONLY TypeScript types.`,
80
+
81
+ state: (input) =>
82
+ `Task: Design state management
83
+
84
+ Context: ${input}
85
+
86
+ Rules:
87
+ - Use React hooks (useState, useEffect, useCallback)
88
+ - Reference existing types
89
+ - No business logic
90
+ - Maximum 60 lines
91
+
92
+ Output ONLY React hooks and state.`,
93
+
94
+ logic: (input) =>
95
+ `Task: Implement business logic
96
+
97
+ Context: ${input}
98
+
99
+ Rules:
100
+ - Use existing types and state
101
+ - Implement full logic
102
+ - Handle errors and edge cases
103
+ - Maximum 80 lines
104
+
105
+ Output ONLY the implementation.`,
106
+
107
+ cleaner: (input) =>
108
+ `Task: Compress context for code generation
109
+
110
+ Target task: ${input.substring(0, 200)}
111
+
112
+ Rules to preserve (if present in context):
113
+ - @kind limits (hook:80, component:120, page:200, service:150, store:100, util:100)
114
+ - @limit blocks commits, NOT a suggestion
115
+ - @use() required at file top
116
+ - Named exports over default
117
+ - NUNCA rules (any, template files, etc.)
118
+ - Annotations first, implementation second
119
+
120
+ Compress to <200 lines. Preserve critical rules only.`,
121
+
122
+ validate: (input) =>
123
+ `Task: Fix validation errors
124
+
125
+ Code with errors:
126
+ \`\`\`
127
+ ${input.substring(0, 1500)}
128
+ \`\`\`
129
+
130
+ Fix ONLY the reported errors. Do not rewrite.
131
+ Output the ENTIRE corrected file.`,
132
+ }
133
+
134
+ export function buildPrompt(role, input) {
135
+ const systemPrompt = AGENT_SYSTEM_PROMPTS[role]
136
+ const template = PROMPT_TEMPLATES[role]
137
+
138
+ if (!systemPrompt || !template) {
139
+ throw new Error(`Rol de agente desconocido: "${role}". Roles: ${Object.keys(AGENT_SYSTEM_PROMPTS).join(", ")}`)
140
+ }
141
+
142
+ return {
143
+ system: systemPrompt,
144
+ prompt: template(input),
145
+ role,
146
+ }
147
+ }
148
+
149
+ // ──────────────────────────────────────────────
150
+ // Ollama connector
151
+ // ──────────────────────────────────────────────
152
+
153
+ export class OllamaConnectionError extends Error {
154
+ constructor(message, cause) {
155
+ super(message)
156
+ this.name = "OllamaConnectionError"
157
+ this.cause = cause
158
+ }
159
+ }
160
+
161
+ export class AgentTimeoutError extends Error {
162
+ constructor(role, timeout) {
163
+ super(`Agent "${role}" timed out after ${timeout}ms`)
164
+ this.name = "AgentTimeoutError"
165
+ this.role = role
166
+ this.timeout = timeout
167
+ }
168
+ }
169
+
170
+ // Cache de modelo seleccionado para no re-detectar en cada llamada
171
+ let _bestModelCache = null
172
+
173
+ export async function callOllama(promptText, options = {}) {
174
+ const {
175
+ model = _bestModelCache || "llama3.2:latest", // default seguro siempre disponible
176
+ system = "",
177
+ temperature = 0.2,
178
+ maxTokens = 4096,
179
+ timeout = DEFAULT_TIMEOUT,
180
+ } = options
181
+
182
+ const controller = new AbortController()
183
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
184
+
185
+ try {
186
+ const body = {
187
+ model,
188
+ prompt: promptText,
189
+ system,
190
+ stream: false,
191
+ options: {
192
+ temperature,
193
+ num_predict: maxTokens,
194
+ },
195
+ }
196
+
197
+ const response = await fetch(`${OLLAMA_HOST}/api/generate`, {
198
+ method: "POST",
199
+ headers: { "Content-Type": "application/json" },
200
+ body: JSON.stringify(body),
201
+ signal: controller.signal,
202
+ })
203
+
204
+ if (!response.ok) {
205
+ throw new OllamaConnectionError(
206
+ `Ollama responded with ${response.status}: ${response.statusText}`
207
+ )
208
+ }
209
+
210
+ const data = await response.json()
211
+
212
+ if (data.error) {
213
+ throw new OllamaConnectionError(`Ollama error: ${data.error}`)
214
+ }
215
+
216
+ return {
217
+ text: data.response || "",
218
+ totalDuration: data.total_duration || 0,
219
+ tokensPerSecond: data.tokens_per_second || 0,
220
+ tokenCount: (data.tokens_evaluated || 0) + (data.prompt_eval_count || 0),
221
+ }
222
+ } catch (err) {
223
+ if (err.name === "AbortError") {
224
+ throw new AgentTimeoutError(options.role || "unknown", timeout)
225
+ }
226
+ if (err instanceof OllamaConnectionError || err instanceof AgentTimeoutError) {
227
+ throw err
228
+ }
229
+ throw new OllamaConnectionError(
230
+ `Failed to connect to Ollama at ${OLLAMA_HOST}: ${err.message}`,
231
+ err
232
+ )
233
+ } finally {
234
+ clearTimeout(timeoutId)
235
+ }
236
+ }
237
+
238
+ // ──────────────────────────────────────────────
239
+ // Pool de agentes (cola FIFO)
240
+ // ──────────────────────────────────────────────
241
+
242
+ class AgentPool {
243
+ constructor() {
244
+ this._queue = []
245
+ this._running = false
246
+ this._currentTask = null
247
+ this._stats = { completed: 0, failed: 0, totalTime: 0 }
248
+ }
249
+
250
+ /**
251
+ * Encola una solicitud de agente y retorna el resultado.
252
+ * Antes de encolar, verifica el estado del sistema (RAM, CPU).
253
+ *
254
+ * @param {string} role - Rol del agente (types, state, logic, cleaner, validate)
255
+ * @param {string} input - Input para el agente
256
+ * @param {object} [profile] - Perfil Boost (opcional, auto-detecta si se omite)
257
+ * @returns {Promise<{text: string, role: string, duration: number, metrics: object}>}
258
+ */
259
+ async submit(role, input, profile) {
260
+ const prof = profile || getProfile()
261
+ const agentConfig = prof?.agentPool || {}
262
+
263
+ // Verificar estado del sistema antes de encolar
264
+ const { getSafeParallelism, getRAMWarning } = await import("./hardware-detector.js")
265
+ const safety = getSafeParallelism(prof)
266
+
267
+ if (!safety.safe) {
268
+ const warning = getRAMWarning()
269
+ throw new Error(
270
+ `Boost bloqueado por recursos insuficientes.\n${warning}\n` +
271
+ `β†’ Libera memoria y vuelve a intentarlo.`
272
+ )
273
+ }
274
+
275
+ return new Promise((resolve, reject) => {
276
+ this._queue.push({
277
+ role,
278
+ input,
279
+ profile: prof,
280
+ agentConfig,
281
+ safety, // guardamos la recomendaciΓ³n de seguridad
282
+ resolve,
283
+ reject,
284
+ })
285
+ this._processQueue()
286
+ })
287
+ }
288
+
289
+ async _processQueue() {
290
+ if (this._running || this._queue.length === 0) return
291
+ this._running = true
292
+
293
+ while (this._queue.length > 0) {
294
+ const task = this._queue.shift()
295
+ this._currentTask = task
296
+ const startTime = Date.now()
297
+
298
+ // Re-verificar sistema antes de CADA agente (por si cambiΓ³)
299
+ try {
300
+ const { getSafeParallelism, getRAMWarning } = await import("./hardware-detector.js")
301
+ const currentSafety = getSafeParallelism(task.profile)
302
+
303
+ if (!currentSafety.safe) {
304
+ // Si el sistema se saturΓ³ mientras esperaba, re-encolar
305
+ const warning = getRAMWarning()
306
+ console.warn(`\n ⏸ Agente "${task.role}" pausado β€” ${warning}`)
307
+ // Esperar 5 segundos y re-intentar
308
+ await new Promise((r) => setTimeout(r, 5000))
309
+ this._queue.unshift(task) // re-encolar al principio
310
+ continue
311
+ }
312
+
313
+ // Si hay demasiados agentes en paralelo y estamos en modo cola, esperar
314
+ if (currentSafety.mode === "queue" && task.safety?.mode === "parallel") {
315
+ // La situaciΓ³n cambiΓ³: degradar a cola automΓ‘ticamente
316
+ console.warn(`\n ⏸ Cambiando a modo cola por carga del sistema.`)
317
+ }
318
+ } catch {
319
+ // Si falla la verificaciΓ³n, continuar de todas formas
320
+ }
321
+
322
+ try {
323
+ const prompt = buildPrompt(task.role, task.input)
324
+ const modelName =
325
+ task.agentConfig.modelName || task.profile?.agentPool?.modelName || _bestModelCache || await selectBestModel(task.profile) || "llama3.2:latest"
326
+
327
+ // Estimar RAM que usarΓ‘ el modelo
328
+ const { estimateModelRAM } = await import("./hardware-detector.js")
329
+ const estimatedRAM = estimateModelRAM(modelName)
330
+
331
+ const result = await callOllama(prompt.prompt, {
332
+ model: modelName,
333
+ system: prompt.system,
334
+ temperature: task.agentConfig.temperature || 0.2,
335
+ timeout: task.agentConfig.timeout || DEFAULT_TIMEOUT,
336
+ role: task.role,
337
+ })
338
+
339
+ const duration = Date.now() - startTime
340
+ this._stats.completed++
341
+ this._stats.totalTime += duration
342
+
343
+ task.resolve({
344
+ text: result.text.trim(),
345
+ role: task.role,
346
+ duration,
347
+ metrics: {
348
+ tokensPerSecond: result.tokensPerSecond,
349
+ tokenCount: result.tokenCount,
350
+ model: modelName,
351
+ estimatedRAMGB: estimatedRAM,
352
+ },
353
+ })
354
+ } catch (err) {
355
+ this._stats.failed++
356
+ task.reject(err)
357
+ }
358
+ }
359
+
360
+ this._running = false
361
+ this._currentTask = null
362
+ }
363
+
364
+ get stats() {
365
+ return { ...this._stats, queueLength: this._queue.length, isRunning: this._running }
366
+ }
367
+
368
+ async clear() {
369
+ this._queue = []
370
+ }
371
+ }
372
+
373
+ // Singleton
374
+ export const agentPool = new AgentPool()
375
+
376
+ // ──────────────────────────────────────────────
377
+ // Utilidades de detecciΓ³n
378
+ // ──────────────────────────────────────────────
379
+
380
+ export async function checkOllama() {
381
+ try {
382
+ const response = await fetch(`${OLLAMA_HOST}/api/tags`, {
383
+ signal: AbortSignal.timeout(5000),
384
+ })
385
+ return response.ok
386
+ } catch {
387
+ return false
388
+ }
389
+ }
390
+
391
+ export async function listOllamaModels() {
392
+ try {
393
+ const response = await fetch(`${OLLAMA_HOST}/api/tags`, {
394
+ signal: AbortSignal.timeout(5000),
395
+ })
396
+ if (!response.ok) return []
397
+ const data = await response.json()
398
+ return (data.models || []).map((m) => ({
399
+ name: m.name,
400
+ size: m.size,
401
+ modifiedAt: m.modified_at,
402
+ }))
403
+ } catch {
404
+ return []
405
+ }
406
+ }
407
+
408
+ export async function selectBestModel(profile) {
409
+ const models = await listOllamaModels()
410
+ if (models.length === 0) return null
411
+
412
+ const profileName = profile?.name || "medium"
413
+
414
+ // Preferencias segΓΊn perfil β€” mapeadas a modelos que existen en esta instancia
415
+ const preferences = {
416
+ small: ["qwen2.5-coder:7b", "qwen2.5-coder", "llama3.2", "llama3.2:latest", "phi", "deepseek-coder"],
417
+ medium: ["qwen2.5-coder:7b", "qwen2.5-coder", "codellama", "mistral", "llama3"],
418
+ large: ["qwen2.5-72b", "llama-3-70b", "llama3:latest", "qwen2.5-coder:7b"],
419
+ }
420
+
421
+ const preferred = preferences[profileName] || preferences.medium
422
+
423
+ // Buscar el mejor modelo disponible segΓΊn preferencias
424
+ for (const pref of preferred) {
425
+ const match = models.find((m) => m.name === pref || m.name.toLowerCase().includes(pref))
426
+ if (match) {
427
+ _bestModelCache = match.name
428
+ return match.name
429
+ }
430
+ }
431
+
432
+ // Fallback: primer modelo disponible (excluir embeddings)
433
+ const nonEmbedding = models.find((m) => !m.name.includes("nomic") && !m.name.includes("embed"))
434
+ if (nonEmbedding) {
435
+ _bestModelCache = nonEmbedding.name
436
+ return nonEmbedding.name
437
+ }
438
+
439
+ // Último recurso
440
+ _bestModelCache = models[0].name
441
+ return models[0].name
442
+ }
@@ -0,0 +1,79 @@
1
+ // @use(kind, contract, limit)
2
+ // @kind(util)
3
+ // @contract(in: none -> out: agentTypes, agentState, agentLogic, agentValidate, agentCleaner, registry)
4
+ // @limit(lines: 60)
5
+
6
+ /**
7
+ * Export unificado de agentes especializados del mΓ³dulo Boost.
8
+ *
9
+ * Cada agente exporta:
10
+ * role β†’ string identificador
11
+ * buildPrompt β†’ (input) => { system, prompt }
12
+ * validate β†’ (output) => boolean
13
+ * parseOutput β†’ (rawText) => parsed
14
+ */
15
+
16
+ import { buildPrompt } from "../agent-pool.js"
17
+
18
+ // ──────────────────────────────────────────────
19
+ // Registry de agentes
20
+ // ──────────────────────────────────────────────
21
+
22
+ const AGENTS = {
23
+ types: {
24
+ role: "types",
25
+ label: "Type Designer",
26
+ description: "Define tipos e interfaces",
27
+ buildPrompt: (input) => buildPrompt("types", input),
28
+ validateOutput: (text) => text.includes("interface") || text.includes("type "),
29
+ parseOutput: (text) => text.trim(),
30
+ },
31
+ state: {
32
+ role: "state",
33
+ label: "State Architect",
34
+ description: "DiseΓ±a estado y efectos",
35
+ buildPrompt: (input) => buildPrompt("state", input),
36
+ validateOutput: (text) => text.includes("useState") || text.includes("useEffect") || text.includes("useReducer"),
37
+ parseOutput: (text) => text.trim(),
38
+ },
39
+ logic: {
40
+ role: "logic",
41
+ label: "Logic Implementer",
42
+ description: "Implementa lΓ³gica de negocio",
43
+ buildPrompt: (input) => buildPrompt("logic", input),
44
+ validateOutput: (text) => text.length > 50,
45
+ parseOutput: (text) => text.trim(),
46
+ },
47
+ cleaner: {
48
+ role: "cleaner",
49
+ label: "Context Cleaner",
50
+ description: "Comprime contexto eliminando secciones irrelevantes",
51
+ buildPrompt: (input) => buildPrompt("cleaner", input),
52
+ validateOutput: (text) => text.length > 0, // cleaner debe reducir contexto
53
+ parseOutput: (text) => text.trim(),
54
+ },
55
+ validate: {
56
+ role: "validate",
57
+ label: "Code Validator",
58
+ description: "Corrige errores de validaciΓ³n",
59
+ buildPrompt: (input) => buildPrompt("validate", input),
60
+ validateOutput: (text) => text.length > 20,
61
+ parseOutput: (text) => text.trim(),
62
+ },
63
+ }
64
+
65
+ export function getAgent(role) {
66
+ const agent = AGENTS[role]
67
+ if (!agent) throw new Error(`Agente desconocido: "${role}". Agentes: ${Object.keys(AGENTS).join(", ")}`)
68
+ return agent
69
+ }
70
+
71
+ export function listAgents() {
72
+ return Object.entries(AGENTS).map(([key, agent]) => ({
73
+ id: key,
74
+ label: agent.label,
75
+ description: agent.description,
76
+ }))
77
+ }
78
+
79
+ export default AGENTS