> ## Documentation Index
> Fetch the complete documentation index at: https://herm.liftaris.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Herm plugin API reference: HermPlugin and HermPluginApi

> Reference the current Herm bundled plugin API, including slots, routes, commands, events, UI helpers, KV state, themes, gateway access, rasterizers, and lifecycle cleanup.

Every Herm plugin exports a `HermPlugin` object. Herm calls `tui(api)` when the plugin activates and cleans up registered resources when it deactivates.

## HermPlugin

```ts theme={null}
export type HermPlugin = {
  id: string
  enabled?: boolean
  tui(api: HermPluginApi): void | Promise<void>
}
```

Use a stable, globally unique `id`, such as `acme.hello`.

## Slots

```ts theme={null}
api.slots.register(p: {
  order?: number
  slots: Partial<{
    [K in keyof Slots]: (ctx: SlotCtx, props: Slots[K]) => ReactNode
  }>
}): () => void
```

The type surface defines these slots:

| Slot              | Props                     | Current host status                 |
| ----------------- | ------------------------- | ----------------------------------- |
| `app_bottom`      | `{ sid, tab, streaming }` | Mounted below the composer          |
| `sidebar_content` | `{ sid }`                 | Type-defined, not currently mounted |
| `sidebar_footer`  | `{ sid }`                 | Type-defined, not currently mounted |
| `prompt_right`    | `{ sid }`                 | Type-defined, not currently mounted |
| `splash_footer`   | `{}`                      | Type-defined, not currently mounted |

`app_bottom` currently uses `single_winner` composition. Lower `order` values win.

## Routes

```ts theme={null}
api.route.register(defs: ReadonlyArray<{
  name: string
  description?: string
  render: () => ReactNode
}>): () => void

api.route.navigate(name: string, sub?: number): void
api.route.current: string | undefined
```

Routes append top-level tabs after Herm's built-in tabs. `navigate()` also understands built-in slash route names such as `memory` and `marketplace`.

## Commands

```ts theme={null}
api.command.register(cmds: ReadonlyArray<{
  title: string
  value: string
  description?: string
  category?: string
  onSelect: () => void
}>): () => void
```

Commands appear in the `Ctrl+K` command palette. Use stable `value` strings so command identity survives title changes.

## Events

```ts theme={null}
api.event.on(fn: (ev: GatewayEvent) => void): () => void
```

The return value is a disposer. Herm also scope-tracks it, so plugin deactivation removes the listener.

## UI helpers

```ts theme={null}
api.ui.toast(opts: {
  variant?: "info" | "error" | "warning" | "success"
  title?: string
  message: string
}): void

api.ui.confirm(opts: { title: string; body: string; danger?: boolean }): Promise<boolean>
api.ui.prompt(opts: { title: string; label?: string; initial?: string }): Promise<string | null>
api.ui.alert(title: string, body: string): void
api.ui.select(opts: { title: string; options: ReadonlyArray<SelectOption>; placeholder?: string }): Promise<SelectOption | null>
```

Use these helpers instead of building ad hoc dialogs when a standard prompt, alert, confirmation, toast, or select list is enough.

## KV state

```ts theme={null}
api.kv.get<T>(key: string, fallback: T): T
api.kv.set(key: string, value: unknown): void
```

KV data persists in Herm preferences. Runtime wrapping prefixes keys with the plugin id, so plugin code should pass local key names.

## Theme

```ts theme={null}
api.theme.current: Theme
api.theme.name: string
api.theme.mode: "dark" | "light"
api.theme.set(name: string): boolean
api.theme.setMode(mode: "dark" | "light"): void
api.theme.has(name: string): boolean
```

Read colors from `api.theme.current`. Raw hex values do not update when the user changes theme.

## Keybindings

```ts theme={null}
api.keys.match(id: ActionId, key: ParsedKey): boolean
api.keys.print(id: ActionId): string
```

`api.keys` is read-only. Plugins cannot register new keybindings today.

## Gateway client

```ts theme={null}
api.client
```

`api.client` is Herm's gateway client. Use it for request/response calls that must go through the live Hermes gateway. Keep method names aligned with gateway source. Do not invent RPC names in plugins or docs.

## Eikon rasterizers

```ts theme={null}
api.eikon.rasterizer.register(r: Rasterizer): () => void
```

A rasterizer contributes an image-to-text backend to Eikon Studio. The Studio handles spatial preprocessing and passes a prepared render window to the rasterizer.

## Lifecycle

```ts theme={null}
api.lifecycle.signal: AbortSignal
api.lifecycle.onDispose(fn: () => void | Promise<void>): () => void
```

Use the abort signal for long-running async work. Use `onDispose()` for intervals, file watchers, subprocesses, or other resources not registered through Herm's scoped APIs.

## Rendering constraints

* Return OpenTUI React nodes.
* Use current theme tokens for colors.
* Keep slot renderers small. Heavy work should happen outside render.
* Do not assume type-defined slots are mounted. Check current host support first.

## Current limitations

* Plugins are bundled at build time.
* User-installed plugin directories are not loaded yet.
* Plugin keybinding registration is not available yet.
* `app_bottom` is the current mounted slot. Other slots are type-defined for host growth.

## Related docs

* [Plugin overview](/plugins/overview)
* [Plugin examples](/plugins/examples)
* [Eikon overview](/eikon/overview)
