Skip to main content
Guides

Plugins

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.

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

mkdir -p ~/my-plugin/.qoder-plugin
mkdir -p ~/my-plugin/skills/hello

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:
{
  "name": "my-plugin",
  "version": "0.1.0",
  "description": "My first plugin"
}

3. Add a Skill

~/my-plugin/skills/hello/SKILL.md:
---
name: hello
description: Say hello. Use when the user says "say hello".
---

# Hello Skill

Give the user a friendly greeting.

4. Install

qoderclicn plugins install ~/my-plugin
After it outputs 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.
my-plugin/
├── .qoder-plugin/
│   └── plugin.json        # Recommended: manifest (declares metadata such as name/version)
├── commands/              # Custom commands (.md files or subdirectories)
├── agents/                # Custom subagents
├── skills/                # Custom Skills
├── hooks/
│   └── hooks.json         # Hook configuration
├── output-styles/         # Output styles
├── bin/                   # Plugin executables
└── .mcp.json              # MCP server declarations bundled with the plugin
Behavior of the convention directories:
Directory / FilePurpose
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.jsonProvides 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.jsonMCP server declarations bundled with the plugin
The 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.
FieldRequiredDescription
nameYesThe unique identifier of the plugin; cannot contain spaces, and kebab-case is recommended (such as my-plugin)
versionNoSemantic version number (such as 1.0.0)
descriptionNoA short description
authorNoAuthor information
homepageNoDocumentation or homepage URL
repositoryNoSource code repository URL
licenseNoSPDX license identifier (such as MIT)
keywordsNoAn array of tags for discovery and categorization
Advanced: The manifest also supports explicitly declaring fields such as 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:
ScopeDescriptionApplicable Scenarios
userGlobally available; takes effect for all projects of the current user (default)Personal, frequently used plugins
projectTakes 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 teamTeam-shared project-specific plugins
localTakes effect only in the current project; the configuration is written to the project-level settings.local.json, and adding it to .gitignore is recommendedLocal 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:
qoderclicn plugins install ~/my-plugin
qoderclicn plugins install ./relative/path/to/plugin
qoderclicn plugins install /abs/path/to/plugin --scope project
Parameter / OptionDescription
<plugin>The local plugin directory path (absolute path, relative path, or ~/... are all acceptable)
-s, --scope <scope>The installation scope: user (default), project, local
After installation, we recommend restarting the CLI or running /plugins reload in the TUI for the changes to take effect.

Uninstall: plugins uninstall

qoderclicn plugins uninstall my-plugin
qoderclicn plugins uninstall my-plugin --scope project --keep-data
Aliases: remove / rm.
Parameter / OptionDescription
<plugin>The name of the installed plugin
-s, --scope <scope>The scope to uninstall from: user (default), project, local
--keep-dataRetain the plugin's data directory

Enable / Disable: plugins enable / plugins disable

qoderclicn plugins enable my-plugin
qoderclicn plugins disable my-plugin
qoderclicn plugins enable my-plugin --scope project
qoderclicn plugins disable --all
Parameter / OptionDescription
<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
Enabling / disabling is implemented by modifying the enabledPlugins field in the settings.json of the corresponding scope. Disabled plugins are not loaded in new sessions.

List: plugins list

qoderclicn plugins list
qoderclicn plugins list --json
qoderclicn plugins list --plugin-dir ./local-plugins ./more-plugins
OptionDescription
--jsonOutput 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:
qoderclicn plugins validate ~/my-plugin
Validation lists the discovered commands, Skills, Hooks, and other components, and prints a hint when the directory contains no convention subdirectories at all. Note: 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:
{
  "enabledPlugins": {
    "my-plugin@local": true,
    "another-plugin@local": false
  }
}
  • A value of true: Enable this plugin
  • A value of false: Explicitly disable this plugin
The configuration key must exactly match the ID of an installed plugin (the ID of a locally installed plugin has the form 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:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"${QODER_PLUGIN_ROOT}\"/scripts/check.sh"
          }
        ]
      }
    ]
  }
}
This differs from 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 VariableDescription
QODER_PLUGIN_ROOTThe installation root directory of the current plugin
QODER_PLUGIN_DATAThe data directory of the current plugin (independent of the installation directory, making it easy to preserve state across upgrades)
For more details on writing Hooks, see Hooks.