Extending
Plugin API
A plugin exports apply(ctx), as a named export or hung off a default export. The loader checks both.
Minimal plugin
// index.mjs
export async function apply(ctx) {
ctx.workflows.register({
kind: 'hello', // becomes "<row-id>:hello"
displayName: 'Hello',
execution: 'session',
surfaces: ['artifact'],
trigger: { kind: 'manual' },
run: async (runCtx) => {
ctx.logger.info({ repoPath: runCtx.repoPath }, 'hello');
},
});
}
PluginContext
| Field | What it is |
|---|---|
id | Your manifest row id, unqualified. |
logger | debug / info / warn / error, each taking an object and an optional message. |
settings | Async scoped settings: get() and update(patch). |
kv | Per-plugin key-value store: get / set / del / list(prefix). |
workflows integrations harnesses | The three registrars. These are the only way a plugin reaches core. |
Workflows
A workflow takes a kind, displayName, surfaces (session, artifact or feed), an optional config JSON Schema that drives the schedules form, an optional output schema that drives the run detail view, a trigger, and a run(ctx).
Integrations
An integration declares kind, displayName, configSchema, the events it reacts to, a validate(config) and a handler(envelope, config).
Events available: task_created, workflow_status_changed, summary_updated, note_added, ref_added, ref_removed, runtime_state_changed.
Harnesses
The hardest registrar. Requires newSessionId, buildLaunchCommand, buildResumeCommand, buildContinueCommand, installHooks, uninstallHooks, resolveFlags and validateSettings.
Two things to know before starting: command builders return a shell command string, not an argv array, so you own escaping and injection-safety; and several members on the Harness type are reserved but not yet called. Read server/harnesses/claude-code.ts and cursor.ts first — they are the reference implementations.
The five seams
A seam exists where two people would reasonably want different things. There are five.
| Seam | Decides | Status |
|---|---|---|
workflows | What starts work, and what the work is | shipped |
harnesses | Which agent does it | shipped |
compute | Which machine it runs on | coming |
surfaces | Where you see it and answer it | coming |
integrations | Where the result lands | shipped |
What is deliberately not a seam
Every one of these was considered and cut. A seam you do not need is a versioned interface you have to keep working forever.
| Not a seam | Why not |
|---|---|
| Skills | Skills are markdown files. A plugin ships them by shipping files — there is nothing to swap and no behaviour to override. |
| MCP servers | MCP is already the plugin protocol for agent tools. Wrapping it in a second one adds a layer and buys nothing. |
| Models | Your harness already picks the model. A parallel model registry would fight it and lose. |
| History | One append-only log, same shape for everyone. Plugins read it; nobody needs a different one. |
| Artifacts | Files a run produced. A plugin needs to write one, which is a method on ctx, not a registry. |
| Catalog | Reading what is installed is a query, not an implementation choice. |
| Isolation | A git worktree per run is the guarantee, not a preference. Where that worktree lives is the compute seam. |
| Storage | SQLite on your machine. If a team ever needs Postgres it is a connection string in settings, not a plugin. |
Coming
The following are designed but not released. They are tracked publicly and the landing page describes them as though shipped — that gap is a known issue.
| Seam | What it will do | Status |
|---|---|---|
ctx.http.route() | Routes as data, replacing the Express router handout — the change that makes unload possible | coming |
| Lifecycle | mount / unmount / dispose and hot reload without a restart | coming |
ctx.facts | A shared task-scoped log so plugins can read each other’s output | coming |
ctx.ui | Declarative panels and actions, still with no browser JavaScript | coming |
ctx.policy | Intercept an intent and deny or rewrite it — spend caps, model routing | coming |
ctx.attention | Ask a human and await the answer, routed to whatever surfaces are installed | coming |
Full reference for what ships today: api-reference.md.