Skip to main content

Plugin SDK Overview

The plugin SDK is @vibecontrols/plugin-sdk. Plugins are NPM packages that export createPlugin: VibePluginFactory. There is no separate JSON manifest — everything is in code.

What you'll learn

  • The factory pattern
  • The VibePlugin shape
  • Lifecycle hooks
  • Providers (registering capabilities for the agent and other plugins)

The factory

// src/index.ts (your plugin)
import type {
HostServices,
ProfileContext,
VibePlugin,
VibePluginFactory,
} from "@vibecontrols/plugin-sdk/contract";
import {createLifecycleHooks} from "@vibecontrols/plugin-sdk/lifecycle";

export const createPlugin: VibePluginFactory = (ctx: ProfileContext): VibePlugin => {
const plugin: VibePlugin = {
name: "my-plugin",
version: "0.1.0",
description: "What it does",
tags: ["backend", "provider"],
capabilities: {
storage: "rw", // "ro" | "rw" — read or read/write persistent storage
secrets: "read", // "read" — fetch secrets the user granted access to
subprocess: true, // spawn child processes
audit: true, // emit audit events
telemetry: true, // emit telemetry
gateway: false, // proxy outbound HTTP through the agent's gateway
},
// optional: lifecycle wiring on server start/stop
onServerStart: undefined,
onServerStop: undefined,
};

createLifecycleHooks({
name: "my-plugin",
onInit: async (hostServices: HostServices) => {
// Register providers, schedule background tasks, etc.
},
});

return plugin;
};

The agent calls createPlugin(ctx) once per profile when loading plugins.

VibePluginFactory + VibePlugin

Exactly what's exported by @vibecontrols/plugin-sdk/contract:

TypePurpose
VibePluginFactory(ctx: ProfileContext) => VibePlugin
VibePluginPlugin metadata + capabilities + optional hooks
ProfileContextPer-profile context (config dir, profile name, etc.)
HostServicesServices the agent exposes to plugins (logger, storage, subprocess, telemetry, providers registry)

Capabilities

Each plugin declares what runtime features it needs:

  • storage: "ro"|"rw"|false — access to the plugin's TypedStore (per-namespace persistent store)
  • subprocess: true|false — allowed to spawn child processes
  • telemetry: true|false — allowed to emit telemetry

The agent enforces these at runtime — if a plugin tries to use a capability it didn't declare, the call is denied.

Providers

A plugin registers as a provider of a typed interface. The agent has registries for:

  • Session providers (vibe-plugin-session-tmux, vibe-plugin-session-wezterm, vibe-plugin-session-zellij)
  • Tunnel providers (vibe-plugin-tunnel-cloudflare, vibe-plugin-tunnel-vibetunnels)
  • AI providers (within the AI plugin)
createLifecycleHooks({
name: "my-session-plugin",
onInit: async (hostServices) => {
const provider = new MySessionProvider(hostServices);
hostServices.providers.register("session", provider);
},
});

Storage

Plugins get a typed key-value store namespaced to the plugin name:

const STORAGE_NS = "my-plugin";
const KEY_STATE = "state";
const store = hostServices.storage.namespace(STORAGE_NS);
await store.set(KEY_STATE, {foo: "bar"});
const value = await store.get(KEY_STATE);

Persisted on disk in the agent's data directory.

Telemetry + logging

import {TelemetryEmitter} from "@vibecontrols/plugin-sdk/telemetry";
import {BoundLogger} from "@vibecontrols/plugin-sdk/log";

const telemetry = new TelemetryEmitter("my-plugin", "0.1.0");
const log = hostServices.logger.bind({plugin: "my-plugin"});

log.info("something happened");
telemetry.emit("my-event", {duration: 42});

Next steps