Capitulo 15: Plugins
Capitulo 15: Plugins
< Volver al Indice del Tutorial
Que son los Plugins
Los plugins son extensiones que agregan funcionalidad a OpenCode más allá de lo que permiten MCP servers, skills o comandos custom. Mientras que MCP conecta con servicios externos y los skills definen instrucciones, los plugins pueden modificar el comportamiento interno de OpenCode: registrar hooks en el ciclo de vida, interceptar permisos, reaccionar a eventos de la sesión y más.
Son el mecanismo de extensión más profundo disponible. Un plugin puede ejecutar código cada vez que se completa una sesión, proteger archivos sensibles de edición, enviar notificaciones cuando el agente termina una tarea o integrar OpenCode con herramientas externas del equipo.
A diferencia de los MCP servers que exponen herramientas al agente, los plugins operan a nivel de la infraestructura de OpenCode. El agente no “usa” un plugin como usa una herramienta; el plugin intercepta y reacciona a lo que el agente hace.
Metodos de Carga
OpenCode soporta dos métodos para cargar plugins: archivos locales y paquetes npm.
Archivos Locales
Los plugins locales se colocan en directorios específicos que OpenCode reconoce automáticamente:
Plugins del proyecto:
tu-proyecto/
.opencode/
plugins/
mi-plugin.ts
otro-plugin.ts
Plugins globales:
~/.config/opencode/
plugins/
mi-plugin-global.ts
notificaciones.ts
Los plugins locales del proyecto son específicos del repositorio y se comparten con el equipo. Los plugins globales aplican a todos tus proyectos y contienen extensiones personales.
Paquetes NPM
Los paquetes npm se configuran en opencode.json:
{
"plugin": ["opencode-helicone-session", "@my-org/custom-plugin"]
}
OpenCode instala automáticamente los paquetes npm usando Bun. Los módulos se almacenan en ~/.cache/opencode/node_modules/, separados de las dependencias de tu proyecto para evitar conflictos. No necesitas ejecutar bun install manualmente; OpenCode lo hace al detectar plugins nuevos en la configuración.
Orden de Carga
OpenCode carga los plugins en un orden determinístico:
flowchart TD
A["1. Config global"] --> B["2. Config del proyecto"]
B --> C["3. Plugins globales"]
C --> D["4. Plugins del proyecto"]
- Configuración global (
~/.config/opencode/config.json): se procesa primero - Configuración del proyecto (
opencode.json): puede sobrescribir la global - Plugins globales (
~/.config/opencode/plugins/): se cargan en orden alfabético - Plugins del proyecto (
.opencode/plugins/): se cargan al final
Este orden importa porque los hooks se ejecutan en el mismo orden en que se registraron. Si un plugin global y uno del proyecto registran el mismo hook, el global se ejecuta primero.
Estructura de un Plugin
Un plugin es una función que recibe un PluginContext y retorna un objeto con hooks registrados:
import type { PluginContext } from "@opencode-ai/plugin"
export default function(context: PluginContext) {
// context proporciona acceso a:
// - context.project: información del proyecto
// - context.client: cliente de OpenCode
// - context.$: ejecutor de comandos shell (tagged template)
// - context.directory: directorio del proyecto
// - context.worktree: directorio de trabajo actual
return {
// hooks registrados aquí
}
}
El PluginContext es el punto de acceso a todo lo que un plugin necesita. Proporciona información del proyecto, un cliente para interactuar con OpenCode, un ejecutor de comandos shell y las rutas relevantes del workspace.
El operador $ es especialmente útil: permite ejecutar comandos shell directamente desde el plugin usando tagged templates:
export default function(ctx: PluginContext) {
return {
session: {
onComplete: async () => {
await ctx.$`notify-send "OpenCode" "Sesion completada"`.run()
}
}
}
}
Hooks Disponibles
Los hooks son el corazón del sistema de plugins. Cada hook se ejecuta en un momento específico del ciclo de vida de OpenCode. Los hooks disponibles cubren prácticamente todos los aspectos de la operación:
command: Ejecución de Comandos
Se activa cuando se ejecutan comandos en OpenCode. Permite interceptar, modificar o registrar la ejecución de comandos.
file: Edición de Archivos
Se activa cuando el agente edita archivos. Permite validar, aprobar o rechazar ediciones antes de que se apliquen.
install: Actualizaciones
Se activa durante actualizaciones de OpenCode o sus dependencias. Permite ejecutar tareas de migración o mantenimiento.
lsp: Diagnósticos
Se activa cuando el Language Server Protocol reporta diagnósticos. Permite filtrar, agregar o transformar diagnósticos antes de mostrarlos al agente.
message: Mensajes
Se activa con cada mensaje enviado o recibido. Permite interceptar mensajes para logging, analytics o transformación.
permission: Permisos
Se activa cuando OpenCode necesita evaluar un permiso. Permite implementar lógica de permisos personalizada.
session: Sesiones
Se activa al iniciar y completar sesiones. Permite ejecutar setup/teardown, enviar notificaciones o registrar métricas.
tools: Herramientas
Se activa cuando el agente usa herramientas. Permite interceptar llamadas a herramientas, agregar logging o modificar parámetros.
tui: Interacciones de Interfaz
Se activa con interacciones en la interfaz de terminal. Permite personalizar la experiencia de la TUI.
shell: Operaciones Shell
Se activa cuando se ejecutan operaciones en el shell. Permite interceptar o modificar comandos shell.
flowchart LR
subgraph Hooks
A[session] --> B[message]
B --> C[tools]
C --> D[file]
C --> E[shell]
D --> F[permission]
E --> F
F --> G[lsp]
end
subgraph Lifecycle
H[Inicio sesión] --> I[Conversación]
I --> J[Acciones]
J --> K[Permisos]
K --> L[Diagnósticos]
end
Ejemplos Prácticos
Notificación al Completar Sesión
Uno de los usos más comunes es enviar una notificación cuando el agente termina una tarea larga:
export default function(ctx: PluginContext) {
return {
session: {
onComplete: () => {
ctx.$`notify-send "OpenCode" "Sesion completada"`.run()
}
}
}
}
En Linux usa notify-send, en macOS podrías usar osascript -e 'display notification "Sesion completada"'. Este plugin es particularmente útil cuando lanzas tareas largas y cambias a otra ventana mientras el agente trabaja.
Proteger Archivos Sensibles
Un plugin que impide la edición de archivos con secretos:
export default function(ctx: PluginContext) {
return {
permission: {
edit: (file: string) => {
const protectedPatterns = ['.env', 'credentials', 'secrets', '.pem', '.key']
const isProtected = protectedPatterns.some(p => file.includes(p))
if (isProtected) return 'deny'
return 'allow'
}
}
}
}
Este plugin actúa como una capa de seguridad adicional. Aunque el agente intente editar un archivo .env o credentials.json, el plugin lo bloquea antes de que la edición se aplique. Es especialmente útil en equipos donde múltiples personas usan OpenCode y quieres garantizar que nadie exponga secretos accidentalmente.
Logging de Herramientas
Un plugin que registra cada vez que el agente usa una herramienta:
export default function(ctx: PluginContext) {
return {
tools: {
onCall: (tool: string, params: unknown) => {
ctx.client.app.log(`Tool: ${tool}, Params: ${JSON.stringify(params)}`)
}
}
}
}
Validar Archivos Antes de Editar
Un plugin que ejecuta validaciones antes de permitir ediciones:
export default function(ctx: PluginContext) {
return {
file: {
beforeEdit: async (filepath: string) => {
// No permitir editar archivos generados
if (filepath.includes('/generated/') || filepath.includes('/dist/')) {
return 'deny'
}
// No permitir editar lockfiles
if (filepath.endsWith('lock') || filepath.endsWith('.lock')) {
return 'deny'
}
return 'allow'
}
}
}
}
Métricas de Sesión
Un plugin que registra cuánto dura cada sesión y cuántas herramientas se usaron:
export default function(ctx: PluginContext) {
let startTime: number
let toolCount = 0
return {
session: {
onStart: () => {
startTime = Date.now()
toolCount = 0
},
onComplete: () => {
const duration = Math.round((Date.now() - startTime) / 1000)
ctx.client.app.log(
`Sesion: ${duration}s, Herramientas: ${toolCount}`
)
}
},
tools: {
onCall: () => { toolCount++ }
}
}
}
Logging en Plugins
Un detalle importante: dentro de un plugin, no uses console.log(). Los logs del plugin deben ir a través de la API del cliente:
// Incorrecto
console.log("Algo pasó")
// Correcto
ctx.client.app.log("Algo pasó")
client.app.log() integra los mensajes del plugin con el sistema de logging de OpenCode. Los mensajes de console.log() pueden perderse o interferir con la TUI.
Dependencias Externas
Si tu plugin necesita dependencias npm que no son parte de OpenCode, crea un archivo package.json dentro del directorio de plugins:
.opencode/
plugins/
mi-plugin.ts
package.json
El package.json lista las dependencias que tu plugin necesita:
{
"dependencies": {
"node-notifier": "^10.0.0",
"chalk": "^5.0.0"
}
}
OpenCode detecta este archivo y ejecuta bun install automáticamente antes de cargar los plugins. Las dependencias se instalan en el contexto del directorio .opencode/, sin contaminar el node_modules de tu proyecto principal.
Desarrollo de un Plugin Local
El flujo completo para crear un plugin local:
Paso 1: Crear el archivo
mkdir -p .opencode/plugins
Paso 2: Escribir el plugin
Crea .opencode/plugins/proteger-produccion.ts:
import type { PluginContext } from "@opencode-ai/plugin"
export default function(ctx: PluginContext) {
const dangerousCommands = ['rm -rf', 'drop table', 'DELETE FROM', 'git push --force']
return {
shell: {
beforeExecute: (command: string) => {
const isDangerous = dangerousCommands.some(
dc => command.toLowerCase().includes(dc.toLowerCase())
)
if (isDangerous) {
ctx.client.app.log(`Comando peligroso bloqueado: ${command}`)
return 'deny'
}
return 'allow'
}
}
}
}
Paso 3: Probar
Reinicia OpenCode. El plugin se carga automáticamente al detectar el archivo en .opencode/plugins/. Prueba pidiendo al agente que ejecute un comando que contenga alguno de los patrones bloqueados para verificar que el plugin funciona.
Paso 4: Iterar
Los plugins locales se recargan cada vez que inicias una nueva sesión de OpenCode. Modifica el archivo, reinicia, y los cambios se aplican inmediatamente. No necesitas compilar ni publicar nada durante el desarrollo.
Cuando Usar Cada Mecanismo de Extension
OpenCode ofrece múltiples formas de extender su funcionalidad. Elegir la correcta depende de tu necesidad:
| Necesidad | Mecanismo | Por qué |
|---|---|---|
| Conectar con Sentry/Slack/GitHub | MCP Server | Protocolo estándar, interoperable |
| Notificar al completar sesión | Plugin (hook) | Reacciona a eventos del ciclo de vida |
| Proteger archivos de edición | Plugin (permission) | Intercepta antes de que ocurra |
| Ejecutar un script y devolver resultado | Custom Tool | Menor overhead de configuración |
| Logging de todas las interacciones | Plugin (hooks) | Acceso a todos los eventos |
| Convenciones de testing | Skill | Instrucciones reutilizables |
| Ejecutar review rápido | Command | Atajo a prompt predefinido |
La mayoría de usuarios solo necesitarán MCP servers, skills y comandos custom. Los plugins son para casos avanzados donde necesitas interceptar el comportamiento de OpenCode o reaccionar a eventos que no están expuestos de otra forma.
Buenas Practicas
- Empieza con local: desarrolla como plugin local antes de publicar en npm. Iterar localmente es mucho más rápido que el ciclo publish/install.
- Mantén los plugins simples: un plugin debe tener un propósito claro y acotado. Si crece demasiado, divídelo en plugins más pequeños.
- Maneja errores con gracia: los plugins no deben crashear OpenCode si algo falla. Envuelve la lógica en try/catch y usa
client.app.log()para reportar errores. - Usa
client.app.log(): nuncaconsole.log(). Los logs del plugin deben integrarse con el sistema de OpenCode. - Respeta el rendimiento: los hooks se ejecutan síncronamente en el flujo principal. Si tu plugin necesita hacer operaciones pesadas, hazlas asíncronas y no bloquees.
- Documenta: si publicas un plugin npm, incluye un README con instrucciones de uso, configuración y ejemplos.
- Versiona correctamente: usa semver si publicas en npm para que los usuarios sepan qué esperar con cada actualización.
- Commitea los plugins del proyecto:
.opencode/plugins/debe estar en el repositorio si el plugin es relevante para el equipo. - No dupliques funcionalidad: antes de crear un plugin, verifica que no existe un hook, skill o MCP server que ya haga lo que necesitas.
Siguiente: Capitulo 16: GitHub Actions y CI/CD —>