Quick Start
The following example creates a minimal plugin containing a single Skill and installs it from a local directory.1. Create the plugin directory
2. Write the manifest
Declaring aplugin.json is recommended for every plugin so it has stable metadata; at minimum include name (see Manifest Fields below):
~/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 start using the Skill.
Plugin Directory Layout
.qoder-plugin/plugin.json is the recommended location for the manifest. When omitted, the CLI still loads the directory by convention and uses the directory name as the plugin name. Convention directories are auto-discovered when present, otherwise ignored.
| Directory / File | Purpose |
|---|---|
commands/ | Register custom slash commands; same structure as ~/.qoder/commands/ |
agents/ | Register custom sub-agents |
skills/ | Register Skills; same structure as ~/.qoder/skills/ |
hooks/hooks.json | Hook configuration; uses the { "hooks": ... } wrapper, where the inner hooks value matches the hooks field in settings.json |
output-styles/ | Custom output styles |
bin/ | Plugin executables |
.mcp.json | MCP server declarations bundled with the plugin |
Manifest Fields
Onlyname is required in plugin.json; other fields are optional.
| Field | Required | Description |
|---|---|---|
name | Yes | Unique plugin identifier; cannot contain spaces; kebab-case recommended (e.g., my-plugin) |
version | No | Semantic version (e.g., 1.0.0) |
description | No | Brief description |
author | No | Author information |
homepage | No | Documentation or homepage URL |
repository | No | Source repository URL |
license | No | SPDX license identifier (e.g., MIT) |
keywords | No | Tags for discovery and categorization |
Advanced: the manifest can also explicitly declarecommands/agents/skills/hooks/outputStylesto override the default directory conventions or use inline content (note the manifest field uses camelCaseoutputStyles, while the convention directory remainsoutput-styles/). When not declared, the CLI auto-discovers via the conventions above.
Installation Scope
Plugins can be installed at three scopes:| Scope | Description | Use case |
|---|---|---|
user | Globally available; applies to all of the current user’s projects (default) | Personal frequently-used plugins |
project | Applies only to the current project; written to project-level settings.json, can be committed to git for team sharing | Team-shared, project-specific plugins |
local | Applies only to the current project; written to project-local settings.local.json, recommended to add to .gitignore | Local experimental plugins |
Commands
Plugin commands live under theqodercli plugins subcommand group, aliased as plugin.
Install: plugins install
Install a plugin from a local directory path:
| Argument / Option | Description |
|---|---|
<plugin> | Local plugin directory path (absolute, relative, or ~/...) |
-s, --scope <scope> | Installation scope: user (default), project, local |
/plugins reload in the TUI to apply changes.
Uninstall: plugins uninstall
remove / rm.
| Argument / Option | Description |
|---|---|
<plugin> | Installed plugin name |
-s, --scope <scope> | Scope to uninstall from: user (default), project, local |
--keep-data | Preserve the plugin’s data directory |
Enable / Disable: plugins enable / plugins disable
| Argument / Option | Description |
|---|---|
<plugin> | Installed plugin name |
-s, --scope <scope> | Scope to write to; auto-detected if omitted |
-a, --all | (disable only) Disable all enabled plugins in the chosen scope |
enabledPlugins field in the corresponding settings.json. Disabled plugins are not loaded in new sessions.
List: plugins list
| Option | Description |
|---|---|
--json | Output as JSON |
-o, --output-format <format> | text or json (equivalent to --json) |
--plugin-dir <dirs...> | Additional directories to scan and merge into the listing (does not install) |
Validate: plugins validate
Validate that a local plugin directory matches the conventions; useful during development:
validate does not fail in that case — but plugins install for a local plugin requires at least one recognizable component or resource (either a convention directory or a resource explicitly declared in the manifest).
Recommended: always declarename,version, etc. in.qoder-plugin/plugin.json. This is the recommended way to organize a Qoder plugin — without it, the plugin can only be identified by its directory name inplugins list,enabledPlugins, and other places, which is fragile across environments.
The enabledPlugins Setting
Enable / disable state is stored in the enabledPlugins field of settings.json:
true: enables the pluginfalse: explicitly disables the plugin
The configuration key must match the installed plugin ID exactly (locally installed plugins have IDs of the formPrefername@local). Runplugins listto see each plugin’s identifier.
plugins enable / plugins disable over hand-editing this field — the commands handle scope selection, dependency resolution, and other details for you.
Writing Plugin Hooks
A plugin may declare its own Hooks inhooks/hooks.json. The file uses a wrapped shape: a top-level object with a hooks field whose value matches the hooks field in settings.json:
Note this differs fromWhen executed, plugin Hooks receive two extra environment variables:settings.json:settings.jsonuses the barehooksfield, while a plugin’shooks/hooks.jsonrequires the extra{ "hooks": ... }wrapper.
| Variable | Description |
|---|---|
QODER_PLUGIN_ROOT | Installation root of the current plugin |
QODER_PLUGIN_DATA | Data directory of the current plugin (separate from the install dir to preserve state across upgrades) |