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

# Plugins

`options.plugins` is used to load local plugin directories into the current query session. The SDK converts each local plugin into a `--plugin-dir <path>` startup argument; commands, agents, skills, and MCP servers included in the plugin are automatically discovered and available for the session.

<div id="loading-local-plugins" />

## Loading Local Plugins

```typescript theme={null}
import { query } from '@qoder-ai/qoder-agent-sdk';

const q = query({
  prompt: 'List the commands and agents contributed by the current plugins',
  options: {
    plugins: [
      { type: 'local', path: '/path/to/my-plugin' },
    ],
  },
});

const init = await q.initializationResult();
console.log(init.commands);
console.log(init.agents);
console.log(init.plugins);
```

You can pass multiple local plugins at once:

```typescript theme={null}
const q = query({
  options: {
    plugins: [
      { type: 'local', path: '/path/to/plugin-a' },
      { type: 'local', path: '/path/to/plugin-b' },
    ],
  },
});
```

***

<div id="plugin-directory-layout" />

## Plugin Directory Layout

A local plugin typically contains:

```
my-plugin/
  .qoder-plugin/plugin.json
  commands/
  agents/
  skills/
  .mcp.json
```

`.qoder-plugin/plugin.json` declares the plugin name, version, and description. The other directories are automatically scanned by the CLI based on file type.

***

<div id="plugin-contributed-slash-commands" />

## Plugin-contributed Slash Commands

`commands/*.md` files in the plugin will appear in the initialization result and in `supportedCommands()`.

```typescript theme={null}
const commands = await q.supportedCommands();
console.log(commands.map((cmd) => cmd.name));
```

<div id="plugin-contributed-agents" />

## Plugin-contributed Agents

`agents/*.md` files in the plugin will appear in the initialization result and in `supportedAgents()`.

```typescript theme={null}
const agents = await q.supportedAgents();
console.log(agents.map((agent) => agent.name));
```

<div id="plugin-contributed-skills" />

## Plugin-contributed Skills

`skills/*/SKILL.md` files in the plugin are registered with a plugin-qualified name (`plugin:skill`). To make them callable in the main session, they must be explicitly listed in `options.skills`; see [Skills documentation](/en/cli/sdk/skills#enabling-plugin-skills).

<div id="plugin-contributed-mcp-servers" />

## Plugin-contributed MCP Servers

`.mcp.json` in the plugin is started by the CLI and included in the MCP status.

```typescript theme={null}
const servers = await q.mcpServerStatus();
console.log(servers);
```

***

<div id="temporarily-overriding-an-installed-plugin-with-the-same-name" />

## Temporarily Overriding an Installed Plugin with the Same Name

Plugins loaded via `options.plugins` are session-scoped. During the current session, if a local plugin shares a name with an installed plugin, the local plugin takes priority in capability discovery. This is useful for plugin development, debugging, and canary testing.

```typescript theme={null}
const q = query({
  options: {
    // The local version only takes effect for this query session and does not
    // touch the user's global install state.
    plugins: [{ type: 'local', path: './my-plugin-dev' }],
  },
});
```

***

<div id="reloading-plugins-at-runtime" />

## Reloading Plugins at Runtime

If the plugin directory changes, you can call `reloadPlugins()` within the same query session to have the CLI rescan plugin resources.

```typescript theme={null}
const refreshed = await q.reloadPlugins();

console.log(refreshed.commands);
console.log(refreshed.agents);
console.log(refreshed.plugins);
console.log(refreshed.mcpServers);
console.log(refreshed.error_count);
```

Typical use cases:

* Refreshing after adding or deleting `commands/*.md` during plugin development.
* After installing or updating a local plugin without restarting the host application.
* When the host UI needs to display commands, agents, plugins, and MCP status after a reload.

***

<div id="options-reference" />

## Options Reference

| Field            | Type                                 | Description                                                                                       |
| ---------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `plugins`        | `SdkPluginConfig[]`                  | Load local plugin directories; currently the common form is `{ type: 'local', path }`             |
| `settings`       | `string \| Settings`                 | Settings passed to the CLI, which can include fields like `enabledPlugins`, `pluginConfigs`, etc. |
| `settingSources` | `('user' \| 'project' \| 'local')[]` | Controls which settings sources the CLI reads                                                     |

Enterprise policy fields like `settings.enabledPlugins`, `settings.pluginConfigs`, `settings.allowedChannelPlugins`, and `settings.strictPluginOnlyCustomization` are passed through by the SDK types, but the actual pipeline depends on the specific CLI version. Verify against your environment before use.

***

<div id="return-value-reference" />

## Return Value Reference

<div id="initializationresult" />

### `initializationResult()`

```typescript theme={null}
type SDKControlInitializeResponse = {
  commands?: Array<{ name: string; description?: string; argumentHint?: string }>;
  agents?: Array<{ name: string; description?: string; model?: string }>;
  skills?: Array<{ name: string; description?: string; source?: string }>;
  plugins?: Array<{ name: string; path: string; source?: string }>;
  // Also includes models, account, output_style and other fields
};
```

<div id="reloadplugins" />

### `reloadPlugins()`

```typescript theme={null}
type SDKControlReloadPluginsResponse = {
  commands: Array<{ name: string; description?: string; argumentHint?: string }>;
  agents: Array<{ name: string; description?: string }>;
  plugins: Array<{ name: string; path: string; source?: string }>;
  mcpServers: Array<Record<string, unknown>>;
  error_count: number;
};
```

***

<div id="best-practices" />

## Best Practices

* **Read `initializationResult()` first for the host UI**: It is the stable entry point for displaying commands, agents, skills, and plugins.
* **Use `options.plugins` during plugin development**: It only affects the current session without modifying the user's global install state.
* **Prepare user prompts before reloading**: `reloadPlugins()` triggers the CLI to rescan disk, which may briefly change the available resource list; synchronize UI updates accordingly.
* **Check `error_count` for diagnostics**: If `error_count > 0` after reload, some plugin resources failed to load; display the source to the user.

***

<div id="current-limitations" />

## Current Limitations

* In some qodercli versions, local plugin commands, agents, and MCP can appear correctly in the initialization result, but plugin skills may not appear in `initializationResult().skills` — this is a CLI-side discovery pipeline issue.
* Under the current qodercli implementation, non-existent `--plugin-dir` paths are silently ignored in SDK mode; to explicitly diagnose plugin loading failures, currently the only option is `reloadPlugins().error_count` as a fallback.
* `reloadPlugins()` is a runtime control API exposed by the SDK; if the current CLI version returns internal errors related to `this._plugins`, an upgrade to the fixed qodercli is needed.
