Plugins
A plugin is a set of skills installed from a git repository. Any repo that looks like a skill collection - or is a single skill - can be installed with one command, without copying files by hand or trusting a download step. Installing a plugin never runs its code: a plugin carries instructions plus optional metadata, nothing executable.
Where they live
<project>/.jan/agent/plugins/└── release-tools/ ├── plugin.toml # optional TOML metadata ├── .claude-plugin/ │ └── plugin.json # Claude-compatible metadata ├── skills/ │ └── prepare/ │ └── SKILL.md └── ...
A plugin's payload is discovered conventionally, so a repo needs no manifest to be installable:
skills/- the same layout as project skills: folder skills (<name>/SKILL.md) and flat<name>.mdSKILL.mdat the repo root - a repo that is itself one skillcommands/- slash-invocable prompt templates (<name>.md, nested directories included)agents/- dispatchable subagent definitions (<name>.md, nested directories included)plugin.tomlor.claude-plugin/plugin.json- optional metadata:name,description,version, andrepo. The directory name is the plugin's identity; the manifest'snameis whatplugin listshows when present.
This is the same layout Claude Code and Codex plugins use, so existing plugins install and work
unchanged: anthropics/claude-plugins-official and similar marketplaces are drop-in compatible.
Installing
From the command line:
jan plugin install <git-url>jan plugin install <git-url>#<ref> # install a specific branch or tagjan plugin install <github-tree-url> # install a plugin from a repo subdirectoryjan plugin install <marketplace-name> # resolve a name through your marketplacejan plugin listjan plugin remove <name>jan plugin search [query]
Inside the interactive agent, the equivalent commands are:
/plugin install <git-url>/plugin list/plugin remove <name>/plugin search <query>
GitHub tree URLs such as
https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-code-setup
are cloned from the repository root and install only the selected subdirectory.
The plugin is cloned (--depth 1), validated to contain a plugin manifest, skills/, commands/,
agents/, .mcp.json, or a root SKILL.md, and moved into place under its final name.
Reinstalling an existing plugin is refused (remove it first). Specs with shell metacharacters are
rejected outright - a plugin name is a name, not a command.
Invoking plugin skills
Plugin skills are qualified with their plugin name: release-tools:prepare.
| You type | What happens |
|---|---|
/prepare | The plugin skill, when the name is unambiguous |
/skill:release-tools:prepare | The exact plugin skill, always |
/release-tools:prepare | Same as the explicit form |
Precedence: a project skill always wins its short name, and a plain name shared by two plugins is
ambiguous, so only the explicit forms work. The slash popup applies the same rules - it offers the
short form when unambiguous and the qualified form otherwise. Plugin skills flow through the same
invocation mechanics as project skills, including the [skills].enabled whitelist: enable a plugin
name (release-tools), one qualified skill (release-tools:prepare), or a plain name.
Plugin commands
A plugin's commands/ directory is discovered as slash commands. Each markdown file is a prompt
template: the frontmatter description feeds the slash popup, and the body is loaded into the
conversation when you run the command. $ARGUMENTS is replaced with everything you typed after the
command name, and $1..$9 with the individual whitespace-separated words. A missing positional
becomes empty, and $10-style tokens (and $ARGUMENTATION) are left literal so a body can talk
about dollars; the popup only advertises placeholders that will actually be filled:
/feature-dev add user authentication # $ARGUMENTS = "add user authentication", $1 = "add"
Precedence in the slash namespace is built-in command, then plugin command, then skill, so a plugin
command named resume never shadows the built-in /resume (the explicit /command:<plugin>:<name>
form always works). Like skills, commands honor the [skills].enabled whitelist: a command disabled
there is not offered and cannot be fired, even by typing its name directly. The transcript shows one
compact [command:<name>] row with your arguments.
Plugin agents
A plugin's agents/ directory is discovered as dispatchable subagents: the markdown frontmatter
name and description become the subagent's identity, and the body becomes its system prompt, so
a plugin can fan work out to specialized agents exactly as it does in Claude Code. Claude-runtime
metadata is handled honestly:
modelandcolorare ignored - the agent always runs on the session's modeltoolsis mapped onto Jan's tool names (Read->read,Glob->glob,Grep->grep,WebSearch->web_search,WebFetch->web_fetch,TodoWrite->todo, and so on); names with no Jan equivalent are dropped, and an agent listing only unknown tools inherits the parent's full tool policyskill_listandskill_readstay available even whentoolsmaps to a narrow set - a plugin agent can always load the skills its procedure references, matching Claude Code (see Subagents)
Plugin agents are read-only: create_subagent refuses the plugin scope, and a subagent defined in
.jan/agent/subagents/ shadows a plugin agent of the same name.
Managing
Interactive commands:
/plugin list # one compact summary line per plugin/plugin list release-tools # description and skill names for one plugin/plugin remove release-tools # uninstall/plugin search prepare # search the marketplace (name/description)
The compact list shows each plugin's name, version, and payload counts (skills, commands, agents).
Descriptions and artifact names are shown only when a plugin name is requested. Interrupted
.installing-* staging directories are omitted from listings.
The headless CLI uses the same compact table by default:
jan plugin list --project ~/code/app
Use --json when complete plugin metadata or machine-readable output is needed:
jan plugin list --project ~/code/app --json
Install and search continue to print JSON results:
jan plugin install https://github.com/acme/release-tools --project ~/code/appjan plugin search prepare --project ~/code/app
Removal is a directory delete under the same name guard as everything else in the agent: a name can never escape the plugins directory.
Marketplace
Point [plugins] marketplace in agent.toml at a JSON index to install by name and search:
[plugins]marketplace = "https://example.com/jan-plugins/index.json"
[ { "name": "release-tools", "description": "Release automation", "repo": "https://github.com/acme/release-tools" }, { "name": "triage", "description": "Issue triage helpers", "repo": "https://github.com/acme/triage", "ref": "main" }]
repo is cloned with an optional pinned ref (branch or tag). Without a configured marketplace,
install <name> and search report the missing config - direct git URLs always work.
Plugins are shared with Jan Desktop - the same project files, through the same directory. The
desktop agent exposes agent_plugin_list, agent_plugin_install, agent_plugin_remove, and
agent_plugin_search for UI integration.