Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/shobcoder/shob/llms.txt

Use this file to discover all available pages before exploring further.

Plugins are the primary extension point for Shob. A plugin is a Node.js module that exports a server-side function. When Shob starts, it calls each configured plugin and merges the returned hooks into the runtime. You can use plugins to add custom tools the AI can call, inject environment variables into shell sessions, intercept permission prompts, integrate new auth providers, and much more.

What plugins can do

A plugin can implement any combination of the following capabilities via the Hooks interface:
HookPurpose
toolRegister custom tools the AI agent can invoke during a session
authAdd authentication methods (OAuth or API key) for a provider
providerRegister a new model provider or extend an existing one
eventReact to any Shob event emitted on the bus
chat.messageInspect or log every message sent to the LLM
chat.paramsModify temperature, topP, topK, maxOutputTokens, or other parameters before each LLM call
chat.headersInject extra HTTP headers into LLM requests
shell.envInject environment variables into every shell command executed by the agent
permission.askOverride the default permission prompt — auto-allow or auto-deny based on custom logic
tool.execute.beforeIntercept a tool call before it runs and rewrite its arguments
tool.execute.afterPost-process a tool’s output before it is returned to the model
tool.definitionRewrite a tool’s description or parameter schema as seen by the model
command.execute.beforeIntercept slash-commands before they run

Plugin structure

Every plugin is a module that exports a server property conforming to the Plugin type:
// my-plugin/index.ts
import type { Plugin } from "@shob-ai/plugin"

export const server: Plugin = async (input, options) => {
  // Return a Hooks object
  return {}
}
The module may also export an optional id string that identifies the plugin in logs and the TUI:
export const id = "my-plugin"

The PluginInput object

Your plugin function receives a PluginInput as its first argument:
export type PluginInput = {
  /** A fully-configured Shob SDK client pointing at the running server */
  client: ReturnType<typeof createOpencodeClient>
  /** The current project metadata */
  project: Project
  /** The working directory for the current session */
  directory: string
  /** The root of the git worktree */
  worktree: string
  /** Workspace adapter registry (experimental) */
  experimental_workspace: {
    register(type: string, adapter: WorkspaceAdapter): void
  }
  /** The base URL of the running Shob server */
  serverUrl: URL
  /** A Bun shell instance for running shell commands */
  $: BunShell
}
The second argument options is an arbitrary Record<string, unknown> that comes from the plugin’s entry in shob.json (see Plugin options).

Adding custom tools

Use the tool helper from @shob-ai/plugin to define a tool. The args field accepts a Zod schema — use tool.schema (which is aliased to zod) to build it:
import type { Plugin } from "@shob-ai/plugin"
import { tool } from "@shob-ai/plugin"

export const server: Plugin = async (_input) => {
  return {
    tool: {
      greet: tool({
        description: "Greet a user by name",
        args: {
          name: tool.schema.string().describe("The user's name"),
        },
        async execute(args, context) {
          return `Hello, ${args.name}! Session: ${context.sessionID}`
        },
      }),
    },
  }
}
The execute function receives the validated args and a ToolContext:
type ToolContext = {
  sessionID: string
  messageID: string
  agent: string
  directory: string   // Current project directory
  worktree: string    // Git worktree root
  abort: AbortSignal
  metadata(input: { title?: string; metadata?: Record<string, any> }): void
  ask(input: AskInput): Promise<void>
}
A tool can return a plain string, or a richer object with a title, structured metadata, and file attachments:
async execute(args) {
  return {
    title: "Search results",
    output: "Found 3 matches",
    metadata: { count: 3 },
  }
}

Implementing other hooks

shell.env — inject environment variables

Add environment variables to every shell command executed during a session:
export const server: Plugin = async (_input, options) => {
  return {
    "shell.env": async (_input, output) => {
      output.env["MY_API_KEY"] = String(options?.apiKey ?? "")
      output.env["LOG_LEVEL"] = "debug"
    },
  }
}

permission.ask — customize permission behaviour

Auto-allow or auto-deny specific permissions without prompting the user:
export const server: Plugin = async () => {
  return {
    "permission.ask": async (permission, output) => {
      // Auto-allow all bash commands in /tmp
      if (permission.metadata?.path?.startsWith("/tmp")) {
        output.status = "allow"
      }
    },
  }
}

event — react to Shob events

Subscribe to any event emitted by the Shob event bus:
export const server: Plugin = async () => {
  return {
    event: async ({ event }) => {
      if (event.type === "session.created") {
        console.log("New session started:", event.properties.sessionID)
      }
    },
  }
}

chat.message — inspect the message lifecycle

Called whenever a new user message is received by the server:
export const server: Plugin = async () => {
  return {
    "chat.message": async (input, output) => {
      console.log(`[${input.sessionID}] New message:`, output.parts)
    },
  }
}

Minimal working plugin

Here is a complete, minimal plugin you can use as a starting point:
// my-shob-plugin/index.ts
import type { Plugin } from "@shob-ai/plugin"
import { tool } from "@shob-ai/plugin"

export const id = "my-shob-plugin"

export const server: Plugin = async (_input, options) => {
  const greeting = String(options?.greeting ?? "Hello")

  return {
    tool: {
      say_hello: tool({
        description: "Say hello to someone",
        args: {
          name: tool.schema.string().describe("Name of the person to greet"),
        },
        async execute({ name }) {
          return `${greeting}, ${name}!`
        },
      }),
    },

    "shell.env": async (_input, output) => {
      output.env["PLUGIN_ACTIVE"] = "1"
    },
  }
}

Registering a plugin

Use the shob plugin command (alias: shob plug) to install an npm package and automatically update your shob.json:
# Install locally (updates the project's shob.json)
shob plugin my-shob-plugin

# Install globally (updates the global config)
shob plugin my-shob-plugin --global

# Replace an already-installed version
shob plugin my-shob-plugin --force

Manually in shob.json

Add the package name to the plugin array in your shob.json:
{
  "plugin": [
    "my-shob-plugin"
  ]
}

Plugin options

Pass options to a plugin by replacing the string entry with a two-element tuple:
{
  "plugin": [
    ["my-shob-plugin", { "greeting": "Howdy", "apiKey": "secret" }]
  ]
}
Your plugin receives these options as the second argument:
export const server: Plugin = async (_input, options) => {
  console.log(options?.greeting) // "Howdy"
}

Loading a local plugin

During development you can load a plugin directly from a local file path. Place a .ts or .js file in a plugin/ or plugins/ directory at your project root — Shob automatically discovers and loads files in those directories. Alternatively, reference the file explicitly in shob.json:
{
  "plugin": [
    "./plugin/my-local-plugin.ts"
  ]
}

Publishing a plugin to npm

A Shob plugin is a standard npm package. The minimum package.json for a server-side plugin looks like this:
{
  "name": "my-shob-plugin",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.js",
  "exports": {
    ".": "./dist/index.js",
    "./server": "./dist/index.js"
  },
  "peerDependencies": {
    "@shob-ai/plugin": "*"
  }
}
Export your server entrypoint under the "./server" export condition. Shob uses this to distinguish server-side plugins from TUI plugins when both are provided by the same package.
Once published, users can install your plugin with a single command:
shob plugin my-shob-plugin
Plugins run with the same privileges as the Shob server process. Only install plugins from authors you trust, and review any plugin code before loading it in production environments.

Build docs developers (and LLMs) love