Plugins
Use and build shareable Droid packages for skills, commands, droids, hooks, and MCP servers.
Plugins package reusable Droid extensions so a team can
install the same skills, commands, droids, hooks, and MCP servers across projects. Use
standalone .factory/ configuration for personal or project-local experiments, then
turn the pieces into a plugin when they should be shared, versioned, or governed.
For org-approved catalogs and managed marketplace sources, see Internal Plugin Marketplaces.
What plugins contain
| Component | Location in a plugin | Loaded as | Use for |
|---|---|---|---|
| Skills | skills/<name>/SKILL.md | Model-invoked skill | Reusable procedures and domain knowledge. |
| Slash commands | commands/<name>.md or executable command files | /name command | User-invoked workflows. |
| Droids | droids/<name>.md | Task-callable subagent | Specialized agents with scoped prompts, tools, and models. |
| Hooks | hooks/hooks.json plus scripts | Lifecycle hooks | Validation, policy, formatting, logging, and context injection. |
| MCP servers | mcp.json | MCP tool configuration | External tools and data sources made available when the plugin is active. |
Native Droid plugins use .factory-plugin/plugin.json. Claude Code plugin layouts are also supported: .claude-plugin/ is translated to .factory-plugin/, agents/ is translated to droids/, and .mcp.json is translated to mcp.json when Droid copies the plugin into its cache.
Manage plugins
Use the interactive UI for browsing and one-off work:
/plugins| Tab | Purpose |
|---|---|
| Browse | View and install plugins from registered marketplaces. |
| Installed | Update, uninstall, or inspect installed plugins. |
| Marketplaces | Add, update, or remove marketplaces. |
Use CLI commands for scripts and onboarding:
# Marketplace management
droid plugin marketplace add <url-or-local-path>
droid plugin marketplace list
droid plugin marketplace update [name]
droid plugin marketplace remove <name>
# Plugin management
droid plugin install <plugin@marketplace> --scope user
droid plugin install <plugin@marketplace> --scope project
droid plugin list --scope user
droid plugin update [plugin@marketplace] --scope project
droid plugin uninstall <plugin@marketplace> --scope projectPlugin IDs use pluginName@marketplaceName. Scoped npm package names are supported because Droid splits on the first @ after the first character, so @scope/plugin@marketplace is valid.
Build a plugin
A minimal native plugin looks like this:
my-plugin/
├── .factory-plugin/
│ └── plugin.json
├── commands/
│ └── hello.md
├── skills/
│ └── code-review/
│ └── SKILL.md
├── droids/
│ └── reviewer.md
├── hooks/
│ ├── hooks.json
│ └── check.sh
├── mcp.json
└── README.mdKeep commands/, skills/, droids/, hooks/, and mcp.json at the plugin root. Do not put them inside .factory-plugin/; that directory is for plugin metadata.
Plugin manifest
Create .factory-plugin/plugin.json:
{
"name": "my-plugin",
"description": "A helpful plugin description",
"version": "1.0.0",
"author": {
"name": "Your Team"
},
"homepage": "https://github.com/your-org/my-plugin",
"repository": "https://github.com/your-org/my-plugin",
"license": "MIT",
"keywords": ["review", "security"]
}| Field | Source-validated behavior |
|---|---|
name | Plugin identity. Include it for native Droid and Claude Code compatibility. |
description | Shown in browsing and installation surfaces when available. |
version | Documentation metadata for the plugin. Git-based installation still tracks the installed commit hash. |
author, homepage, repository, license, keywords | Optional metadata for attribution and discovery. |
Commands
A Markdown command at commands/review-pr.md becomes /review-pr:
---
description: Review the current PR for issues
---
Review the current pull request. Focus on: $ARGUMENTSSkills
A skill lives at skills/<name>/SKILL.md:
---
name: code-review
description: Reviews code for correctness, safety, and maintainability. Use when reviewing code, checking PRs, or analyzing code quality.
---
Check for logic errors, security issues, missing tests, and unclear ownership. Return specific, actionable findings.Droids
A droid lives at droids/<name>.md:
---
name: reviewer
description: Specialized code reviewer subagent
model: inherit
tools: ["Read", "Grep", "Glob"]
---
You are a senior code reviewer. Report correctness, security, and test coverage issues with severity and evidence.See Custom Droids for the full droid configuration surface.
Hooks
Plugin hooks live at hooks/hooks.json and may reference scripts in the plugin:
{
"PostToolUse": [
{
"matcher": "Create|Edit|ApplyPatch",
"hooks": [
{
"type": "command",
"command": "${DROID_PLUGIN_ROOT}/hooks/check.sh",
"timeout": 30
}
]
}
]
}Droid expands ${DROID_PLUGIN_ROOT}, $DROID_PLUGIN_ROOT, ${CLAUDE_PLUGIN_ROOT}, and $CLAUDE_PLUGIN_ROOT to the installed plugin cache path when the plugin loads. See Hooks for hook events, input, and output behavior.
MCP servers
Plugin MCP servers use mcp.json at the plugin root:
{
"mcpServers": {
"my-api": {
"command": "npx",
"args": ["-y", "@example/mcp-server"],
"env": {
"API_KEY": "${MY_API_KEY}"
}
}
}
}Test locally
During development, add the plugin directory as a local marketplace and install from it:
mkdir -p my-plugin/.factory-plugin
# create my-plugin/.factory-plugin/plugin.json and any plugin components
droid plugin marketplace add ./my-plugin
droid plugin install my-plugin@my-plugin --scope userBefore sharing, verify:
- The plugin installs from a clean checkout.
- Commands work with and without
$ARGUMENTS. - Skills and droids have clear names and routing descriptions.
- Hook scripts use absolute paths or plugin-root variables.
- MCP servers do not embed secrets directly in
mcp.json. - The README explains what the plugin does, when to use it, and prerequisites.
Marketplaces
A marketplace is a catalog of installable plugins. Droid reads .factory-plugin/marketplace.json first and falls back to .claude-plugin/marketplace.json for Claude Code compatibility.
your-marketplace/
├── .factory-plugin/
│ └── marketplace.json
├── plugin-one/
│ └── .factory-plugin/
│ └── plugin.json
└── plugin-two/
└── .factory-plugin/
└── plugin.json{
"name": "your-marketplace",
"description": "A collection of team plugins",
"owner": {
"name": "Platform Team"
},
"plugins": [
{
"name": "plugin-one",
"description": "Description of plugin one",
"source": "./plugin-one"
}
]
}| Marketplace field | Required | Description |
|---|---|---|
name | Yes | Marketplace identifier. |
description | No | Shown when browsing marketplaces. |
owner | No | Contact metadata. |
plugins[].name | Yes | Plugin identifier inside this marketplace. |
plugins[].source | Yes | Relative path string or source object. |
plugins[].description, category, homepage, tags | No | Browsing metadata. |
Plugin sources
Each marketplace plugin entry has a source field.
| Source type | Shape | Use when | Pinning |
|---|---|---|---|
| Relative path | "./plugin-one" | Plugin lives inside the marketplace repository. Absolute paths and paths that escape the marketplace directory are rejected. | Pin the marketplace source. |
github | { "source": "github", "repo": "org/repo", "ref": "v1.0.0" } | Plugin or marketplace is in a GitHub repository. | ref branch/tag or full 40-character sha. |
url | { "source": "url", "url": "https://gitlab.com/org/repo.git" } | Plugin or marketplace is on another Git host. | ref branch/tag or full 40-character sha. |
git-subdir | { "source": "git-subdir", "url": "https://github.com/org/repo", "path": "plugins/foo" } | Plugin lives in a subdirectory of a larger repository. | ref branch/tag or full 40-character sha. |
npm | { "source": "npm", "package": "@org/plugin", "version": "^2.0.0" } | Plugin is published to npm or a private npm-compatible registry. | version semver, range, or dist-tag. |
npm is valid only inside a marketplace's plugins[].source. droid plugin marketplace add npm:<package> is intentionally rejected. To distribute one npm plugin, create a small wrapper marketplace that lists the npm source.
Add and pin marketplaces
droid plugin marketplace add accepts a GitHub URL, any other Git URL, or a local path. For Git URLs, append #<ref> to follow a branch or tag, or @<40-character-sha> to pin an exact commit:
droid plugin marketplace add https://github.com/Factory-AI/factory-plugins
droid plugin marketplace add 'https://github.com/your-org/plugins#v1.2.0'
droid plugin marketplace add https://github.com/your-org/plugins@1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b
droid plugin marketplace add ./your-local-marketplaceLocal marketplace paths are resolved to absolute paths and cannot carry a ref or SHA pin.
npm source details
| Field | Required | Description |
|---|---|---|
package | Yes | npm package name. Scoped packages such as @scope/name are supported. |
version | No | Version, range, or dist-tag. Defaults to latest. URL, path, file:, git+..., and npm: alias specs are rejected. |
registry | No | HTTPS registry URL with no embedded credentials, query string, or fragment. |
authTokenEnvVar | No | Environment variable name containing the private registry token. Requires registry to have an effect. |
Droid installs npm-source plugins in a per-plugin scratch directory with npm install --ignore-scripts --no-save --no-audit --no-fund, then copies the resolved package root into the plugin cache. Lifecycle scripts do not run, global npm configuration is not mutated, and the package must ship ready-to-use files.
Team and enterprise distribution
Register marketplaces and enable plugins through settings when you want teams to get them automatically:
{
"extraKnownMarketplaces": {
"your-org-internal-plugins": {
"source": {
"source": "github",
"repo": "your-org/internal-plugins",
"ref": "v1.2.0"
}
}
},
"enabledPlugins": {
"code-standards@your-org-internal-plugins": true
}
}The installation scope follows where the setting is defined: org-managed settings install as org scope, user settings install as user scope, and project settings install as project scope.
Use strictKnownMarketplaces in managed settings when an organization wants to restrict marketplace sources to an approved list. See Internal Plugin Marketplaces for centralized governance.
Versioning and updates
| Source | Tracked version | Update behavior |
|---|---|---|
| Relative path inside a Git marketplace | Marketplace commit hash | Update the marketplace to move the plugin. |
github, url, git-subdir plugin source | Plugin source commit hash | Update fetches the latest allowed commit unless pinned by sha. |
npm plugin source | Resolved npm package version and metadata | Update follows the version spec, such as latest or ^2.0.0. |
Plugin manifest version | Metadata only | Useful for humans and release notes, but Git-based installs are tracked by commit. |
Discover plugins
Factory maintains an official marketplace at Factory-AI/factory-plugins:
droid plugin marketplace add https://github.com/Factory-AI/factory-pluginsCommon official plugins include:
| Plugin | Purpose |
|---|---|
| droid-control | Terminal, browser, and computer automation for demos, QA, and verification. |
| droid-evolved | Skills for session navigation, writing, skill creation, design, frontend work, and browser automation. |
| security-engineer | Security review, threat modeling, commit scanning, and vulnerability validation. |
Droid can also install compatible Claude Code plugins. Claude Code layouts are translated during cache copy, not by mutating the source repository.
Best practices
| Practice | Why it matters |
|---|---|
| Keep plugins focused | Small plugins are easier to review, compose, and retire. |
| Document capabilities and boundaries | Users need to know when to use the plugin, prerequisites, and what data or tools it touches. |
| Avoid hardcoded machine paths | Use plugin-root variables for plugin files and environment variables for secrets. |
| Test on a clean machine | Plugin installs should not depend on uncommitted local files or global state. |
| Ship prebuilt npm packages | --ignore-scripts means npm plugin sources cannot rely on postinstall builds. |
| Govern high-trust plugins | Use managed settings and Internal Plugin Marketplaces for approved enterprise distribution. |