A plugin is a mechanism in Qoder CN CLI for packaging extension capabilities such as commands, subagents, Skills, Hooks, and MCP servers into a single directory, making them easy to install, enable, and share. A plugin directory can contain one or more extension resources, and after installation these resources are automatically discovered and loaded by the CLI.
The following creates a minimal plugin containing only a single Skill and installs it as a local directory.
We recommend that every plugin declare a
After it outputs
Behavior of the convention directories:
The
In
Advanced: The manifest also supports explicitly declaring fields such as
Plugins can be installed to three scopes:
Plugin-related commands are located under the
Install a plugin from a local directory path:
After installation, we recommend restarting the CLI or running
Aliases:
Enabling / disabling is implemented by modifying the
Validate whether the structure of a local plugin directory conforms to the conventions; commonly used during development:
Validation lists the discovered commands, Skills, Hooks, and other components, and prints a hint when the directory contains no convention subdirectories at all. Note:
The enabled / disabled state is saved in the
A plugin can declare its own Hooks in
This differs from
For more details on writing Hooks, see Hooks.
Quick Start
The following creates a minimal plugin containing only a single Skill and installs it as a local directory.
1. Create the Plugin Directory
2. Write the Manifest
We recommend that every plugin declare a plugin.json containing at least the name field to obtain stable metadata (see the manifest fields section later):
~/my-plugin/.qoder-plugin/plugin.json:
3. Add a Skill
~/my-plugin/skills/hello/SKILL.md:
4. Install
Plugin "my-plugin@local" installed successfully. Run /plugins reload to apply., restart the CLI or run /plugins reload in the TUI to use the Skill provided by the plugin.
Plugin Directory Structure
.qoder-plugin/plugin.json is the recommended manifest location for Qoder CN; if it is not declared, the CLI loads by convention and uses the directory name as the plugin name. Convention directories are loaded if present and ignored if absent.
| Directory / File | Purpose |
|---|---|
commands/ | Registers custom slash commands; the structure is consistent with ~/.qoder-cn/commands/ |
agents/ | Registers custom subagents |
skills/ | Registers Skills; the structure is consistent with ~/.qoder-cn/skills/ |
hooks/hooks.json | Provides Hook configuration; uses the { "hooks": ... } wrapper format, where the value of the hooks field is consistent with the hooks field in settings.json |
output-styles/ | Provides custom output styles |
bin/ | Executables provided by the plugin |
.mcp.json | MCP server declarations bundled with the plugin |
agents/ directory in a plugin can also declare isolation: worktree, which is suitable for publishing implementation-type Subagents that need isolated execution.
Manifest Fields
In plugin.json, only name is required; all other fields can be omitted.
| Field | Required | Description |
|---|---|---|
name | Yes | The unique identifier of the plugin; cannot contain spaces, and kebab-case is recommended (such as my-plugin) |
version | No | Semantic version number (such as 1.0.0) |
description | No | A short description |
author | No | Author information |
homepage | No | Documentation or homepage URL |
repository | No | Source code repository URL |
license | No | SPDX license identifier (such as MIT) |
keywords | No | An array of tags for discovery and categorization |
commands / agents / skills / hooks / outputStyles, which are used to override the default directory conventions or to use inline content (note that the manifest field uses camelCase outputStyles, while the corresponding convention directory is still output-styles/). If not explicitly declared, the CLI automatically discovers content according to the directory conventions in the previous section.
Installation Scopes
Plugins can be installed to three scopes:
| Scope | Description | Applicable Scenarios |
|---|---|---|
user | Globally available; takes effect for all projects of the current user (default) | Personal, frequently used plugins |
project | Takes effect only in the current project; the configuration is written to the project-level settings.json and can be committed to git and shared with the team | Team-shared project-specific plugins |
local | Takes effect only in the current project; the configuration is written to the project-level settings.local.json, and adding it to .gitignore is recommended | Local experimental plugins |
Commands
Plugin-related commands are located under the qoderclicn plugins subcommand group, with the alias plugin.
Install: plugins install
Install a plugin from a local directory path:
| Parameter / Option | Description |
|---|---|
<plugin> | The local plugin directory path (absolute path, relative path, or ~/... are all acceptable) |
-s, --scope <scope> | The installation scope: user (default), project, local |
/plugins reload in the TUI for the changes to take effect.
Uninstall: plugins uninstall
remove / rm.
| Parameter / Option | Description |
|---|---|
<plugin> | The name of the installed plugin |
-s, --scope <scope> | The scope to uninstall from: user (default), project, local |
--keep-data | Retain the plugin's data directory |
Enable / Disable: plugins enable / plugins disable
| Parameter / Option | Description |
|---|---|
<plugin> | The name of the installed plugin |
-s, --scope <scope> | Which scope to write to; if not specified, it is selected automatically |
-a, --all | (disable only) Disable all enabled plugins in the scope at once |
enabledPlugins field in the settings.json of the corresponding scope. Disabled plugins are not loaded in new sessions.
List: plugins list
| Option | Description |
|---|---|
--json | Output in JSON |
-o, --output-format <format> | text or json (equivalent to --json) |
--plugin-dir <dirs...> | Additionally scan plugins in these directories and merge them into the list (does not install automatically) |
Validate: plugins validate
Validate whether the structure of a local plugin directory conforms to the conventions; commonly used during development:
validate itself does not fail because of this, but plugins install requires that a local plugin contains at least one recognizable component or resource (either a convention directory or a resource explicitly declared in the manifest is acceptable) when installing it.
Recommendation: Always declare metadata such as name and version in .qoder-plugin/plugin.json. This is the recommended way for Qoder CN to organize plugins—otherwise the plugin can only use the directory name as its identifier in places such as plugins list and enabledPlugins configuration, which is not conducive to cross-environment identification and sharing.
Configuring enabledPlugins
The enabled / disabled state is saved in the enabledPlugins field of settings.json:
- A value of
true: Enable this plugin - A value of
false: Explicitly disable this plugin
name@local). You can view each plugin's identifier through plugins list.
Modifying this field through plugins enable / plugins disable is more reliable than editing it manually, because the commands handle details such as scope selection and dependencies.
Writing Plugin Hooks
A plugin can declare its own Hooks in hooks/hooks.json. The file uses a wrapper format: the top level is an object whose hooks field value is consistent with the hooks field in settings.json:
settings.json: settings.json writes the hooks field directly, while a plugin's hooks/hooks.json needs an extra outer { "hooks": ... } wrapper.
Hooks provided by a plugin receive two additional environment variables at execution time:
| Environment Variable | Description |
|---|---|
QODER_PLUGIN_ROOT | The installation root directory of the current plugin |
QODER_PLUGIN_DATA | The data directory of the current plugin (independent of the installation directory, making it easy to preserve state across upgrades) |