Clase Magistral • Junio 2026 • Basado en documentación oficial y análisis de código fuente
Tres plataformas compiten hoy por ser el entorno definitivo para programación agentica asistida por IA. Cada una tiene una filosofía distinta, un ecosistema de plugins diferente, y —lo más importante— un modelo mental particular sobre qué significa "extender" la plataforma.
Minimalista elegante. Filosofía Unix: herramientas pequeñas que hacen una cosa bien. Plugin system tipo middleware con hooks asíncronos.
Corporativo completo. Codex CLI (OpenAI). Plugins tipo agente MCP con ciclo de vida y sandboxing.
Castillo con todas las torres. El ecosistema más maduro: MCP nativo, hooks CLI, modo "edict" remoto, y el SDK más documentado.
Analicemos las capacidades reales de cada plataforma desde sus fundamentos arquitectónicos. No se trata de declarar un ganador absoluto, sino de entender dónde se pueden construir extensiones, qué tipo de extensiones permite cada una, y cómo portar un plugin entre las tres con el mínimo esfuerzo.
Cada plataforma responde una pregunta distinta sobre la extensibilidad:
OpenCode implementa un sistema de plugins basado en middleware asíncrono. Cada plugin es una función que recibe un contexto (PluginInput) y devuelve un objeto con hooks. La plataforma ofrece 17 hooks que cubren prácticamente todos los puntos de extensión del pipeline de conversación.
Cada hook es un punto de extensión en el pipeline de la conversación. Los hooks reciben un contexto y pueden devolver modificaciones parciales o completas del comportamiento.
| Hook | ¿Solo observa? | ¿Modifica? | ¿Qué modifica? |
|---|---|---|---|
event | Sí | No | Eventos del ciclo de vida (start, stop, error, tool_call) |
chat.before | Sí | Parcial | Lectura del mensaje entrante (no modifica) |
chat.message | No | Sí | System prompt, messages[], model, params LLM |
chat.after | Sí | Parcial | Post-procesamiento de respuesta (lectura) |
tool.execute.before | No | Sí | Bloquear tool call, validar argumentos, modificar args |
tool.execute.after | No | Sí | Post-procesar resultado, detectar errores/éxito |
tool.execute.error | No | Sí | Manejar errores, retry, fallback |
tool.register | No | Sí | Modificar definición de tools, añadir/quitar tools |
tool.addTool | No | Sí | Registrar custom tools nativas (JavaScript/TypeScript) |
provider.before | No | Sí | Modificar config del provider (modelo, apiKey, baseUrl) |
provider.after | Sí | Parcial | Observar respuesta del provider |
auth.register | No | Sí | Registrar auth providers (OAuth, API key, OIDC) |
command.register | No | Sí | Registrar slash commands |
config | No | Sí | Modificar configuración global de OpenCode |
compact.before | No | Sí | Customizar lógica de compactación de contexto |
compact.after | Sí | Parcial | Observar resultado de compactación |
env.register | No | Sí | Inyectar/eliminar variables de entorno |
event, chat.before y chat.after son puramente observacionales (y aún así pueden ser útiles para logging/monitoreo).
En OpenCode, un plugin es simplemente una función que recibe PluginInput y devuelve hooks:
type Plugin = (
input: PluginInput,
options?: PluginOptions
) => Promise<Hooks>
El contexto que OpenCode inyecta a cada plugin contiene:
interface PluginInput {
client: Client // Cliente LLM (OpenAI, Anthropic, etc.)
project: Project // Proyecto actual
directory: string // Directorio del proyecto
worktree: string // Worktree de git (si aplica)
experimental_workspace: Workspace // Workspace experimental
serverUrl: string // URL del servidor MCP/local
$: typeof Bun.$ // Template literal para shell
}
name, version, description) y permisos con un sistema whitelist/blacklist granular para cada hook.
OpenCode descubre plugins mediante tres mecanismos:
.ts/.js en .opencode/plugins/ se cargan automáticamente al iniciar, sin necesidad de declararlos. El nombre canónico del directorio es plugins (plural), aunque plugin (singular) también se acepta por retrocompatibilidad. También escanea ~/.config/opencode/plugins/ a nivel global. El patrón es {plugin,plugins}/*.{ts,js} (solo primer nivel)."plugins" en opencode.json con nombres de paquetes npm."plugins" en opencode.json con URLs file:// o rutas absolutas.Soporta hot reload nativo: los cambios en plugins se reflejan sin reiniciar la sesión. Esto es único entre las tres plataformas.
Este plugin en producción usa 3 hooks para implementar failover automático entre modelos LLM. Veamos cómo funciona:
| Hook | Propósito | Qué hace |
|---|---|---|
event | Detección de errores | Escucha eventos rate_limit, quota_exceeded, 5xx |
chat.message | Cambio de modelo en runtime | Inyecta variant para cambiar modelo en la request |
tool.execute.after | Detección de éxito | Si el tool call succeed con el fallback, registra recuperación |
import { Plugin, PluginInput } from "opencode/plugin";
interface Config {
fallbackModels: Record<string, string[]>;
cooldownMs: number;
retryOriginalAfterMs: number;
maxFallbackDepth: number;
}
const state = new Map<string, {
currentModel: string;
depth: number;
lastFallbackAt: number;
}>();
export const plugin: Plugin = async (input: PluginInput, options?: Config) => ({
async event({ type, payload, sessionId }) {
if (["rate_limit", "quota_exceeded", "5xx", "timeout", "overloaded"]
.includes(type)) {
const s = state.get(sessionId) ?? {
currentModel: payload.model,
depth: 0, lastFallbackAt: Date.now()
};
s.depth++; s.lastFallbackAt = Date.now();
state.set(sessionId, s);
}
},
async chat.message({ messages, sessionId, variant }) {
const s = state.get(sessionId);
if (!s || s.depth === 0) return { messages };
const agentName = variant ?? "default";
const fallbacks = options?.fallbackModels?.[agentName];
const fallbackIdx = Math.min(s.depth - 1, (fallbacks?.length ?? 1) - 1);
if (fallbacks?.[fallbackIdx] && s.depth <= (options?.maxFallbackDepth ?? 3)) {
return { messages, variant: fallbacks[fallbackIdx] };
}
return { messages };
},
async tool.execute.after({ sessionId, result }) {
const s = state.get(sessionId);
if (s && s.depth > 0 && !result.isError) {
setTimeout(() => { s.depth = 0; s.currentModel = ""; },
options?.retryOriginalAfterMs ?? 900000);
}
return { result };
}
});
El patrón —detectar evento → modificar mensaje → verificar éxito— es reproducible en las 3 plataformas con adaptaciones mínimas en la capa de integración, aunque el cambio de modelo en runtime es exclusivo de OpenCode.
opencode-model-fallback y se configura desde opencode.json con mapeo de agentes a modelos de fallback. Soporta wildcards ("*") y profundidad máxima configurable.
Codex CLI, la plataforma de OpenAI, implementa un sistema de plugins con un enfoque enterprise: ciclo de vida explícito, manifests con metadatos, y un SDK multi-lenguaje que permite escribir plugins en Python, TypeScript, Go y Java.
Cada plugin en Codex CLI pasa por tres fases:
| Fase | Método | Propósito |
|---|---|---|
| Inicialización | init() | Cargar configuración, validar dependencias, conectar servicios externos |
| Ejecución | start() | Activar hooks, registrar MCP tools, iniciar background workers |
| Terminación | stop() | Liberar recursos, cerrar conexiones, persistir estado |
A diferencia de OpenCode (donde el plugin es una función pura), en Codex CLI el plugin es una clase que extiende CodexPlugin y puede mantener estado interno con un ciclo de vida gestionado por la plataforma.
Cada plugin de Codex CLI requiere un manifest explícito:
{
"name": "model-fallback",
"version": "1.0.0",
"description": "Automatic model failover",
"hooks": ["onError", "postExec"],
"env": {
"FALLBACK_COOLDOWN_MS": "300000"
},
"permissions": ["network", "fs.read"]
}
Los plugins de Codex CLI pueden integrarse mediante:
onError, postExec, preMessage.start() para monitoreo, watch mode, o tareas periódicas.Codex CLI es la única plataforma que ofrece un SDK oficial para múltiples lenguajes:
| Lenguaje | SDK | Casos de uso |
|---|---|---|
| TypeScript | @openai/codex-sdk | Plugins nativos, integración con el runtime |
| Python | openai-codex-sdk | Ciencia de datos, ML, scripting |
| Go | github.com/openai/codex-sdk-go | Microservicios, herramientas de red |
| Java | com.openai:codex-sdk | Integración enterprise, JVM ecosystem |
OpenAI está construyendo un registry de plugins para Codex CLI. Los plugins se pueden descubrir y compartir via el sistema de manifests, aunque el marketplace aún está en fase emergente. La carga puede ser desde un registry o desde rutas locales definidas en codex.json.
Claude Code, también de Anthropic, tiene el ecosistema de plugins más maduro de las tres plataformas. Con aproximadamente 30 hooks CLI, un marketplace funcional, y el protocolo MCP como columna vertebral, representa el estado del arte en extensibilidad agentica desde la perspectiva de producto.
Claude Code expone hooks CLI que se declaran en claude.json y se ejecutan en puntos específicos del pipeline:
| Categoría | Hooks | Propósito |
|---|---|---|
| Pre-ejecución | pre_message, pre_tool | Modificar mensaje o tool antes de enviar al LLM |
| Post-ejecución | post_tool, post_message | Procesar respuesta, detectar errores |
| Comandos | command | Registrar comandos slash personalizados |
| Aprobación | approval | Interceptar y aprobar/rechazar operaciones |
| Background | monitor, watch | Tareas periódicas y watch mode |
| LSP | lsp.* | Integración con Language Server Protocol |
Los plugins de Claude Code se declaran con un archivo de configuración similar a Codex pero con un ecosistema más estandarizado:
{
"name": "model-fallback",
"version": "1.0.0",
"hooks": {
"pre_message": "./hooks/pre-message.js",
"post_tool": "./hooks/post-tool.js"
},
"env": { "FALLBACK_COOLDOWN_MS": "300000" },
"mcp_servers": ["./mcp/server.js"]
}
Claude Code descubre plugins mediante escaneo de node_modules/ (paquetes @anthropic-ai/*) y configuración explícita en claude.json. No soporta hot reload — los cambios requieren reiniciar el agente.
Las diferencias no están en "quién tiene más features", sino en dónde cada plataforma pone los límites de extensibilidad. Veamos la tabla más completa hasta la fecha:
| Dimensión | OpenCode | Codex CLI | Claude Code |
|---|---|---|---|
| Hooks API | 17 hooks asíncronos, tipados |
Plugin lifecycle init/start/stop + MCP |
CLI hooks + MCP ~30 hooks, edict remoto |
| Capacidad de bloqueo | Sí en tool.execute.before |
Parcial vía MCP tools |
Parcial vía hook approval |
| Modificar modelo runtime | Sí vía chat.message + variant |
No config fija por sesión |
No solo vía config global |
| Modificar params LLM | Sí temperature, max_tokens, etc. |
No fijo por provider |
No fijo por perfil |
| Custom tools nativas | Sítool.addTool (JS/TS) |
Sí SDK MCP nativo (4 lenguajes) |
Sí SDK MCP + STDIO |
| Registrar providers | Síprovider.before |
No solo OpenAI |
No solo Anthropic |
| Auth OAuth/API | Síauth.register |
Limitado solo API key |
Sí OAuth nativo |
| Modificar config global | Síconfig hook |
No | No |
| Modificar system prompt | Síchat.message |
Vía MCP resource template |
Sí CLI hook + MCP |
| Transformar historial | Síchat.message |
No | No |
| Customizar compactación | Sícompact.before |
No | No |
| Plugin manifest | Implícito package.json + naming |
Explícito plugin.json |
Explícito claude.json |
| Marketplace | No comunitario npm |
Emergente registry OpenAI |
Sí Claude Marketplace |
| LSP bundling | No no necesita |
Sí SDK multi-lenguaje |
Sí LSP nativo |
| Monitors background | No solo hooks síncronos |
Sí background workers |
Sí watch mode |
| Open Source | Sí MIT License |
Parcial código disponible |
No código cerrado |
| Model-agnóstico | Sí OpenAI, Anthropic, Google, openrouter, local |
No solo OpenAI |
No solo Anthropic |
.opencode/plugins/ por auto-descubrimiento o vía opencode.json.Este es el mapa que necesitas para portar un plugin entre las 3 plataformas. Cada fila es una capacidad que quieres implementar y cómo se logra en cada plataforma:
| Capacidad | OpenCode | Codex CLI | Claude Code |
|---|---|---|---|
| Bloquear tool call | tool.execute.before → throw Error |
MCP tool validation | Hook approval → reject |
| Validar argumentos | tool.execute.before |
JSON Schema en tool def | JSON Schema + hook |
| Post-procesar tool | tool.execute.after |
MCP middleware | CLI hook post-exec |
| Cambiar modelo | chat.message + variant |
No soportado | No soportado |
| Ajustar temperature | chat.message → modify params |
No soportado | No soportado |
| System prompt dinámico | chat.message → prepend system |
MCP resource template | pre_message hook |
| Auth provider | auth.register |
API key env vars | OAuth nativo |
| Custom tool nativa | tool.addTool (JS/TS) |
MCP SDK (Python, TS, Go, Java) | MCP SDK + STDIO |
| Comando slash | command.register |
MCP tool con trigger | CLI hook command |
| Inyectar env vars | env.register |
plugin.json env block | claude.json env block |
| Modificar definición de tool | tool.register |
No soportado | No soportado |
| Plugins desde npm/directorio | .opencode/plugins/ + npm + opencode.json |
Git repo + codex.json | npm package + claude.json |
| Carga automática | .opencode/plugins/(auto-descubrimiento) |
Registry + dependencias | Scan node_modules/ @anthropic-ai/* |
| Recarga sin reiniciar | Hot reload nativo | Requiere restart | Requiere restart |
| Modificar config en runtime | Sí config hook | No | No |
La estrategia para construir plugins que funcionen en las tres plataformas no es "elegir una y adaptar", sino Core-First: aislar la lógica portable en un núcleo independiente y construir adapters específicos para cada plataforma.
Este código se escribe una vez y funciona en todas partes:
| Plataforma | Formato de plugin | Hook de entrada | Cómo se registra |
|---|---|---|---|
| OpenCode | Función asíncrona(input, opts) => Hooks |
event, chat.message, tool.execute.* |
opencode.json → plugins[] |
| Codex CLI | Plugin classclass P extends CodexPlugin |
onError, postExec |
.codex-plugin/plugin.json |
| Claude Code | Hook function(context) => void |
pre_message, post_tool |
claude.json → hooks[] |
EventHandler, MessageMutator, ToolInterceptor.Este patrón permite que ~80% del código sea portable y solo ~20% sea específico de plataforma. En la práctica, para el plugin model-fallback, el Core Layer tiene 120 líneas y los adapters ~30 líneas cada uno.
No es la lógica de negocio. Es dónde y cómo te conectas al pipeline:
| Dimensión | OpenCode | Codex | Claude |
|---|---|---|---|
| Entrypoint | Función exportada | Plugin class | Hook function array |
| Contexto | PluginInput completo | Context tipado parcial | Context tipado parcial |
| Modificación | Return object merge | Muta context | Return partial |
| Error handling | Throw en hook | Plugin.onError | Hook error callback |
| Carga de plugins | Auto-descubrimiento + npm + config | Registry + local | Scan + config |
| Hot reload | Nativo | No | No |
No hay un ganador absoluto. Hay ganadores por categoría:
Hook más potente
17 hooks, 14 modifican comportamiento. La plataforma más extensible. Model-agnóstico, open source, hot reload.
Gana en: profundidad de extensibilidad, runtime modification, model-agnostic
Packaging más maduro
Marketplace funcional, SDK documentado, ~30 hooks CLI. La experiencia de desarrollo más pulida.
Gana en: marketplace, documentación, DX, edict remoto
Enterprise más completo
SDK multi-lenguaje (Python, TS, Go, Java), background workers, LSP bundling. La plataforma para equipos.
Gana en: multi-lenguaje, background tasks, enterprise features
Usa esta guía para elegir plataforma según tu caso de uso:
| Si necesitas... | Elige | Por qué |
|---|---|---|
| Modificar modelo/params en runtime | OpenCode | Única plataforma con chat.message + variant |
| Soporte multi-lenguaje en tools | Codex CLI | SDK para Python, TS, Go, Java |
| Marketplace y distribución | Claude Code | Claude Marketplace es el más maduro |
| Plugin 100% portable | Core-First | Core Layer + adapters específicos |
| Open Source / auditoría | OpenCode | MIT License, código abierto |
| Multi-provider LLM | OpenCode | OpenAI, Anthropic, Google, OpenRouter, Ollama |
| Enterprise / equipos grandes | Codex CLI | Background workers, LSP, watch mode |
La lección más importante de esta clase magistral es metodológica: cuando analices arquitecturas de software, no te detengas en la documentación superficial. El código fuente es la única fuente de verdad. La documentación miente por omisión; el código fuente no.
El sistema de plugins de OpenCode es significativamente más potente de lo que su documentación superficial sugiere. Codex CLI tiene un SDK multi-lenguaje que ninguna otra plataforma iguala. Claude Code tiene el marketplace y la experiencia de desarrollo más madura. Cada una gana en una dimensión distinta, y la estrategia correcta no es elegir una, sino diseñar para portabilidad desde el día 1.
El patrón Core-First permite que ~80% del código de un plugin viaje entre plataformas sin cambios. Los adapters específicos (ese ~20% restante) son el precio justo por acceder a las capacidades únicas de cada ecosistema.
Todas las afirmaciones en esta clase están respaldadas por documentación oficial y código fuente verificable.
@opencode-ai/plugin en npm — SDK oficial para crear plugins y custom tools@opencode-ai/plugin — SDK de plugins para OpenCodeopencode-model-fallback — Plugin de fallback de modelos (implementación real de referencia)@modelcontextprotocol/sdk — SDK oficial de MCP para TypeScriptmcp — SDK oficial de MCP para Python con FastMCPÚltima verificación: Junio 2026. Las especificaciones y documentación pueden cambiar; consulte las fuentes oficiales para información actualizada.