Sistema de Plugins
El sistema de plugins de Karajan Code permite registrar agentes de IA personalizados sin modificar el código fuente. Crea un fichero .js, implementa la interfaz BaseAgent, y tu agente estará disponible en todo el pipeline.
Descubrimiento de plugins
Sección titulada «Descubrimiento de plugins»Los plugins se auto-descubren desde dos ubicaciones:
- Nivel de proyecto:
<proyecto>/.karajan/plugins/*.js - Nivel de usuario:
~/.karajan/plugins/*.js(o$KJ_HOME/plugins/)
Todos los ficheros .js en estos directorios se cargan. Los plugins de proyecto se cargan primero, luego los de usuario. Los plugins que fallen al cargar se registran como warnings sin afectar al resto.
Crear un plugin
Sección titulada «Crear un plugin»Un plugin es un módulo JavaScript que exporta una función register:
import { BaseAgent } from "karajan-code/src/agents/base-agent.js";import { runCommand } from "karajan-code/src/utils/process.js";import { resolveBin } from "karajan-code/src/agents/resolve-bin.js";
class MyAgent extends BaseAgent { async runTask(task) { const timeout = this.config.session.max_iteration_minutes * 60 * 1000; const model = this.getRoleModel(task.role || "coder");
const args = ["generate", "--input", task.prompt]; if (model) args.push("--model", model);
const res = await runCommand(resolveBin("my-cli"), args, { timeout, onOutput: task.onOutput });
return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode }; }
async reviewTask(task) { const timeout = this.config.session.max_iteration_minutes * 60 * 1000; const model = this.getRoleModel(task.role || "reviewer");
const args = ["review", "--format", "json", "--input", task.prompt]; if (model) args.push("--model", model);
const res = await runCommand(resolveBin("my-cli"), args, { timeout, onOutput: task.onOutput });
return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode }; }}
export function register(api) { api.registerAgent("my-agent", MyAgent, { bin: "my-cli", installUrl: "https://github.com/example/my-cli#install" });
return { name: "my-agent-plugin" };}La interfaz BaseAgent
Sección titulada «La interfaz BaseAgent»Cada agente custom debe extender BaseAgent e implementar dos métodos:
runTask(task)
Sección titulada «runTask(task)»Se llama cuando el agente actúa como coder, planner, refactorer, o cualquier rol que escribe código.
Input:
{ prompt: "string", // Prompt completo con instrucciones role: "coder", // Nombre del rol actual onOutput: function // Opcional: callback para streaming de output}Return esperado:
{ ok: true, // true si el agente tuvo éxito output: "string", // stdout del agente error: "string", // stderr del agente exitCode: 0 // Código de salida del proceso}reviewTask(task)
Sección titulada «reviewTask(task)»Se llama cuando el agente actúa como reviewer. Mismo formato de input/output que runTask.
Métodos helper
Sección titulada «Métodos helper»BaseAgent proporciona estos helpers:
this.getRoleModel(role)— Devuelve el modelo configurado para el rol dado (deconfig.roles[role].model)this.isAutoApproveEnabled(role)— Si auto-approve está activadothis.config— Configuración completa de Karajanthis.logger— Instancia del loggerthis.name— Nombre del agente
La API de register
Sección titulada «La API de register»El objeto api pasado a tu función register proporciona:
api.registerAgent(name, AgentClass, metadata)
Sección titulada «api.registerAgent(name, AgentClass, metadata)»Registrar un agente custom en el registry global.
Parámetros:
name(string) — Identificador único del agente usado en config y CLIAgentClass(class) — Clase que extiendeBaseAgentmetadata(object) — Metadatos del agente:bin(string) — Nombre del binario CLI (usado para verificar disponibilidad)installUrl(string) — URL de instalación (mostrada cuando falta el agente)
Valor de retorno:
Tu función register debe devolver un objeto con al menos name:
return { name: "my-plugin", version: "1.0.0" };Usar tu plugin
Sección titulada «Usar tu plugin»Una vez el plugin está en su sitio, usa el nombre del agente en config o CLI:
Config:
coder: my-agentreviewer: my-agent
roles: coder: model: my-agent/fast-model reviewer: model: my-agent/smart-modelCLI:
kj run "Añadir autenticación" --coder my-agent --reviewer my-agentMCP:
{ "tool": "kj_run", "params": { "task": "Añadir autenticación", "coder": "my-agent", "reviewer": "my-agent" }}Ejemplo mínimo de plugin
Sección titulada «Ejemplo mínimo de plugin»El plugin más simple posible:
import { BaseAgent } from "karajan-code/src/agents/base-agent.js";
class EchoAgent extends BaseAgent { async runTask(task) { return { ok: true, output: `Echo: ${task.prompt}`, error: "", exitCode: 0 }; }
async reviewTask(task) { const review = JSON.stringify({ approved: true, blocking_issues: [], suggestions: [], confidence: 1.0 }); return { ok: true, output: review, error: "", exitCode: 0 }; }}
export function register(api) { api.registerAgent("echo", EchoAgent, { bin: null }); return { name: "echo-plugin" };}Manejo de errores
Sección titulada «Manejo de errores»Los plugins deben manejar errores de forma grácil:
async runTask(task) { try { const res = await runCommand(resolveBin("my-cli"), args, { timeout }); return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode }; } catch (err) { this.logger.error(`Error del agente: ${err.message}`); return { ok: false, output: "", error: err.message, exitCode: 1 }; }}Si un plugin falla al cargar (error de sintaxis, dependencia faltante), Karajan registra un warning y continúa. Otros plugins y agentes built-in no se ven afectados.
Agentes built-in como referencia
Sección titulada «Agentes built-in como referencia»Los agentes built-in siguen el mismo patrón que los plugins:
src/agents/claude-agent.js— Anthropic Claudesrc/agents/codex-agent.js— OpenAI Codexsrc/agents/gemini-agent.js— Google Geminisrc/agents/aider-agent.js— Aidersrc/agents/opencode-agent.js— OpenCode CLI wrapper
Estudia estos ficheros para ver ejemplos reales de implementaciones de runTask y reviewTask.