Plugins
Use and build shareable Droid packages for skills, commands, droids, output styles, hooks, and MCP servers.
Plugins package reusable Droid extensions so a team can
install the same skills, commands, droids, output styles, 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. |
| Output styles | output-styles/<name>.md | Settings picker option | Shared instructions for structuring interactive responses. |
| 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 plugin layouts use a .factory-plugin/ directory. 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 |
|---|---|
| Available | Install plugins from registered marketplaces. Plugin IDs installed in any scope do not appear here. |
| Installed | Enable or disable a plugin with Space where policy allows, and open its details or available actions. Org-scoped installs expose only their details. |
| Marketplaces | Add or update a marketplace, and toggle its auto-update. Only user-added marketplaces can be deleted here. Remove org or project entries from the settings that declare them. |
Use CLI commands for scripts and onboarding:
# Marketplace management
droid plugin marketplace add <source>
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 projectThere is no droid plugin enable or disable command. Enablement is stored in the enabledPlugins setting, and organization-managed activation cannot be changed locally.
Plugin 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.
When Droid derives a marketplace name from a pinned source, it appends the pin to that name. your-org/plugins#v1.2.0 registers as plugins@v1.2.0, and a plugin in it installs as code-standards@plugins@v1.2.0. A SHA pin uses the first 8 characters. Marketplaces declared in settings keep their extraKnownMarketplaces key instead. Run droid plugin marketplace list to read the registered name back.
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
├── output-styles/
│ └── review-notes.md
├── hooks/
│ ├── hooks.json
│ └── check.sh
├── mcp.json
└── README.mdKeep commands/, skills/, droids/, output-styles/, 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 | Purpose |
|---|---|
name | Source metadata for ecosystem compatibility. Installed identity comes from the marketplace entry. |
description | Source-level summary for anyone reading the plugin. |
version | Release metadata. Git-based installation still tracks the installed commit hash. |
author, homepage, repository, license, keywords | Optional source metadata for attribution and compatibility. |
Droid does not require or read the fields inside plugin.json. It identifies an installed plugin from its marketplace entry name and registered marketplace name. A .factory-plugin/ directory identifies the native layout; .claude-plugin/, agents/, or .mcp.json identifies a Claude Code layout for translation. The description and category shown while browsing come from the marketplace entry.
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.
Output styles
An output style lives at output-styles/<name>.md and becomes an option under
/settings → Output style:
---
name: Review Summary
description: Format review results for the team
---
Group findings by severity and include file references.The frontmatter is optional. Output style bodies also support the plugin-root variables described under Hooks.
Output styles apply only to interactive Droid CLI sessions. See Output styles for validation, trust, and fallback behavior.
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
marketplace add takes a marketplace, not a plugin, so a plugin directory on its own is rejected with "This doesn't appear to be a valid marketplace." Wrap the plugin in a local marketplace and install from that:
my-marketplace/
├── .factory-plugin/
│ └── marketplace.json # lists my-plugin with "source": "./my-plugin"
└── my-plugin/
└── .factory-plugin/
└── plugin.jsondroid plugin marketplace add ./my-marketplace
droid plugin install my-plugin@my-marketplace --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.
- Output styles appear in
/settings, and/diagnosticsreports no style errors. - 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 | Manifest metadata. The registered name is derived from the source or set by the extraKnownMarketplaces key. |
description | No | Optional manifest metadata. |
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. |
url | { "source": "url", "url": "https://github.com/org/plugin.git" } | Plugin has its own Git repository. | 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. |
The github source object registers a marketplace. For an external repository in plugins[].source, use url, or use git-subdir when the plugin occupies a subdirectory.
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 owner/repo shorthand, a GitHub URL, any other Git URL, or a local path. Shorthand and plain http(s)://github.com/owner/repo[.git] URLs become a github source; other non-path inputs become a url source. Append #<ref> to follow a branch or tag, or @<40-character-sha> to pin an exact commit:
droid plugin marketplace add Factory-AI/factory-plugins
droid plugin marketplace add 'your-org/plugins#v1.2.0'
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. The Marketplaces tab in /plugins and the plugin settings in the Factory App accept the same forms.
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. |
url or git-subdir plugin source | Marketplace checkout commit hash only. The external commit is not recorded. | Updating the plugin clones the external source again at its configured ref or sha, or its default branch. |
npm plugin source | Resolved npm package version and metadata | Each plugin update resolves the version spec again. |
Plugin manifest version | Metadata only | Useful for humans and release notes, but Git-based installs are tracked by commit. |
Automatic synchronization can skip an unchanged project context and a recently checked marketplace for up to six hours. An explicit droid plugin marketplace update requests an immediate update.
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. |