# Plugins

How nikcli installs plugins, patches server/TUI config, loads built-ins, and exposes plugin hooks.

## Overview

Plugins extend server-side agent behavior and TUI behavior. Server plugin loading is in `packages/nikcli/src/plugin/index.ts`. Plugin installation and config patching are in `packages/nikcli/src/plugin/installer.ts`. TUI plugin runtime lives under `packages/tui/src/plugin`.

Runtime

### Server hooks

Provider auth, tool hooks, event hooks, notifications, and session behavior load in the server plugin service.

UI

### TUI hooks

TUI plugins can register commands, routes, dialogs, keybinds, and sidebar features.

CLI

### Installer

`nikcli plugin` installs a package, reads its manifest, and patches local or global config.

Default

### Built-ins

Internal auth and notification plugins always load; external built-ins load unless disabled.

## Install a plugin

Terminal

bash 

```
nikcli plugin @scope/my-plugin
nikcli plugin @scope/my-plugin --global
nikcli plugin @scope/my-plugin@1.2.3 --force
```

$ shell · paste into your terminal

The install task performs four steps:

1.  Resolve and install the package target.
2.  Read plugin metadata from `package.json`.
3.  Detect server and/or TUI targets from manifest or exports.
4.  Patch `nikcli.json` and/or `tui.json` in the selected scope.

| Flag | Behavior |
| --- | --- |
| `--global` | Patch global config under `Global.Path.config` instead of project config. |
| `--force` | Replace an existing plugin entry for the same package. |
| version suffix | Installer preserves the requested package spec and version. |

## Config patching

`patchPluginConfig` uses `jsonc-parser` edits instead of string hacks. It can patch server config `plugin` and TUI config depending on detected targets. Duplicate detection compares package names, not only the full versioned spec.

| Mode | When used |
| --- | --- |
| `add` | No existing plugin entry for the package. |
| `noop` | Same plugin spec is already configured and force is false. |
| `replace` | Existing package entry differs and `--force` is set. |

server plugin config

json 

1

2

3

4

5

```
{
  "plugin": [
    "@scope/my-plugin"
  ]
}
```

## Server runtime

Server plugin loading starts with internal plugins that are imported directly. Then configured plugins plus external built-ins are loaded. If a plugin path is not `file://`, nikcli installs the package before importing it.

1.  Build plugin input, including SDK client and runtime helpers.
2.  Load internal auth/notification plugins.
3.  Copy configured plugin specifiers from config.
4.  Add external built-ins unless `NIKCLI_DISABLE_DEFAULT_PLUGINS` is set.
5.  Ignore old Codex/Copilot auth plugin specs that are now first-party.
6.  Install and import package specifiers.
7.  Load v1 `server` export when present, otherwise invoke exported plugin functions.
8.  Deduplicate duplicate function references from named and default exports.

## TUI runtime

TUI plugin APIs are built in `packages/tui/src/plugin/api.tsx` and loaded by `plugin/runtime.ts`. Plugins can add routes, commands, dialogs, keybinds, and UI surfaces while the TUI app is running.

| API area | Capability |
| --- | --- |
| Route | Navigate home, session, changes, tree, graph, GitHub, or plugin routes. |
| Command | Register command palette entries and key-triggered commands. |
| Dialog | Open, clear, size, and inspect dialog stack. |
| Keybind | Match, print, and create plugin keybind definitions. |
| Slots/features | Register sidebar or system plugin manager UI where supported. |
| Storage | `api.storage.memory(key, { initial })` keeps per-plugin state in memory across hot reloads; `api.storage.store(...)` persists it to disk and syncs it across TUI instances. |

A plugin that throws while rendering is contained: the slot or route renders nothing and the crash surfaces as a single error toast instead of taking the TUI down.

## TUI plugin discovery and hot reload

Local TUI plugins are discovered from `plugin/tui/` and `plugins/tui/` inside every config root — the project `.nikcli` directories, the global config dir, `NIKCLI_CONFIG_DIR`, and the managed dir. Anything else is declared in `tui.json`. The extra `tui` level matters: `{plugin,plugins}/*.{ts,js}` is the _server_ plugin loader's territory, so a TUI plugin placed directly there is loaded as a server plugin and fails.

project layout

bash 

```
.nikcli/
  tui.json            # optional: declare plugins with options
  plugin/tui/hud.tsx  # discovered automatically
```

$ shell · paste into your terminal

Editing a local plugin swaps that one plugin in the running TUI, typically within ~100 ms. Other plugins — including built-ins — are never touched, in-memory state kept in `api.storage.memory` survives, and a broken save keeps the last working version alive with one error toast until the file is fixed. npm package plugins do not hot reload: their entrypoints resolve into the package cache and are immutable for the session.

| Change on disk | Effect on the running TUI |
| --- | --- |
| Edit a local plugin file | That plugin is disposed and re-initialized from the fresh module. |
| Add a file to `plugin/tui/` | The plugin is loaded and activated. |
| Add or remove a `tui.json` entry | The plugin is loaded, or torn down. |
| Change plugin options | That plugin restarts with the new options. |
| Flip `plugin_enabled` | The plugin is disposed or initialized. Plugin-manager toggles are stored in KV and still win. |

Note

Hot reload is on by default. Set `NIKCLI_DISABLE_PLUGIN_RELOAD=1` to load plugins once at startup; `--pure` / `NIKCLI_PURE` skips external TUI plugins entirely.

## Built-in plugins

Internal built-ins are directly imported and do not depend on package installation. They include Codex auth, GitHub Copilot auth, xAI auth, Cursor auth, Cloudflare Workers/Gateway auth, and the notification plugin. External built-ins are appended from the built-in list unless disabled.

| Built-in | Purpose |
| --- | --- |
| Codex auth | First-party Codex authentication support. |
| GitHub Copilot auth | Copilot and enterprise provider auth. |
| xAI auth | xAI provider auth integration. |
| Cursor auth | Cursor ACP/provider auth integration. |
| Cloudflare auth | Cloudflare Workers and AI Gateway auth flows. |
| Notify plugin | Session idle/error, permission, question, Slack, Discord, and macOS notifications. |

Terminal

bash 

```
NIKCLI_DISABLE_DEFAULT_PLUGINS=1 nikcli
```

$ shell · paste into your terminal

### Internal TUI feature plugins

Beyond the auth and notification plugins, the TUI ships a set of internal feature plugins that ship inside `packages/tui/src/feature-plugins/`. They run in-process and don't need to be installed:

| Plugin | Capability |
| --- | --- |
| `home` | Rotating tip line in the `home.bottom` slot. |
| `sidebar` | Six slot plugins: context/cost, MCP, LSP, todo, modified files, footer. |
| `system` | Plugin manager (list / toggle / install) and OpenRouter Fusion presets. |
| `background` | Paints a real image behind the TUI; owns `/background`. |
| `loops` | [Loops](/docs/loops) manager, creation wizard and live sidebar. |
| `mission` | [Missions](/docs/missions) manager, planning wizard and live sidebar. |
| `brain` | Arms the Brain consolidation scheduler; `/brain` runs a pass on demand. |
| `browser` | Browser-use control surface for the `browser` tool. |
| `chatbot` | ChatBot control panel for Discord / Slack / Teams / GChat / Linear / GitHub webhooks. |
| `computer` | Probes the host for the `computer` tool's screenshot and input capabilities. |
| `connectors` | First-party connector health, cache refresh, and credential removal. |
| `observability` | Live OpenTelemetry spans and OTLP endpoint dialogs. |
| `island` | Toggles the macOS Island notch integration. Registered only under `--island`. |

Each one — its commands, slash aliases, KV keys and internals — is documented in [TUI feature plugins](/docs/tui-plugins), which doubles as the reference for the plugin API since the internal plugins use exactly the API an npm plugin gets.

## Plugin manifest

The installer reads package metadata with `readPluginPackage`. Target detection accepts explicit plugin target metadata and also detects packages that expose `./server` and/or `./tui` exports. A package with only `main` or string `exports` is treated as a server plugin.

package.json

json 

1

2

3

4

5

6

7

8

9

```
{
  "name": "@scope/my-plugin",
  "version": "1.0.0",
  "exports": {
    "./server": "./dist/server.js",
    "./tui": "./dist/tui.js"
  },
  "oc-plugin": ["server", "tui"]
}
```

Tuple targets

Target entries may also be tuples with target options, such as a TUI target plus plugin options. The patcher preserves target options when adding config.

## Skills

Skills are related but not the same as plugins. Skills are prompt/context assets discovered by `skill/skill.ts`. The skill glob scans `skill/**/SKILL.md` and `skills/**/SKILL.md` under config directories, plus external sources unless disabled.

| Feature | Plugin | Skill |
| --- | --- | --- |
| Runtime code | Yes, imported and executed. | No, prompt/context instructions and assets. |
| Config field | `plugin` | Discovered from directories and external skill locations. |
| TUI manager | System plugin manager and plugin APIs. | Skills dialog and skill install/search UI. |
| Disable flag | `NIKCLI_DISABLE_DEFAULT_PLUGINS` for default external plugins. | `NIKCLI_DISABLE_EXTERNAL_SKILLS` for external skills. |

## Notifications

Notifications are implemented as an internal plugin created by `createNotifyPlugin`. It reads `notifications.notify` and `notifications.todo`, applies rate limits, retry/backoff, circuit breaker behavior, quiet hours, and connector-backed Slack or Discord delivery.

notify config

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

```
{
  "notifications": {
    "notify": {
      "enabled": true,
      "events": {
        "sessionIdle": true,
        "sessionError": true,
        "permissionAsked": true,
        "questionAsked": true
      },
      "slack": { "enabled": true, "connector": "slack", "channel": "#builds" }
    }
  }
}
```

## Failure modes

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Install failed | Package resolution, registry auth, or unavailable version. | Check the error detail from `Process.RunFailedError` and npm registry settings. |
| No plugin targets found | Package lacks recognized manifest targets and exports. | Add `oc-plugin`, `./server`, `./tui`, `main`, or string exports. |
| Invalid JSON while patching | Target config file is not valid JSONC. | Fix reported line/column and rerun install. |
| Plugin loads twice | Package exports same function as named and default export. | Runtime deduplicates identical function references; check distinct functions if duplicates remain. |
| Built-in plugin install fails | External built-in package could not be installed. | Server publishes a session error and continues without that built-in. |
| TUI plugin route missing | Plugin route id was not registered or runtime disposed. | Check TUI plugin runtime status and route registration. |

## Source anchors

| Need | File |
| --- | --- |
| Server plugin runtime | `packages/nikcli/src/plugin/index.ts` |
| Plugin install and patching | `packages/nikcli/src/plugin/installer.ts` |
| Plugin package parsing | `@nikcli-ai/util/plugin-shared` |
| CLI command | `packages/nikcli/src/cli/cmd/plug.ts` |
| TUI plugin API | `packages/tui/src/plugin/api.tsx` |
| TUI plugin runtime | `packages/tui/src/plugin/runtime.ts` |
| System plugin manager | `packages/tui/src/feature-plugins/system` |
| Home, sidebar, background, loop, mission, brain, browser, chatbot, computer, connectors and observability plugins | `packages/tui/src/feature-plugins/` |
| Internal plugin registry | `packages/tui/src/plugin/internal.ts` |
| Island file bridge | `@nikcli-ai/util/island-bridge` |
| Island TUI plugin | `packages/tui/src/feature-plugins/island/index.tsx` |
| Plugin contracts package | `packages/plugin` |
| Skills | `packages/nikcli/src/skill/skill.ts` |

---

Source: https://nikcli-ai.dev/docs/plugins
