---
title: "Plugins"
description: "Install and share bundles of skills from a repo, git URL, or local folder."
icon: "box"
---

import PluginSourceForms from "/snippets/plugin-source-forms.mdx";
import PluginGovernance from "/snippets/plugin-governance.mdx";

<Note>
  Plugins are in **beta**. Behavior and configuration may change in future releases.
</Note>

A **plugin** is a bundle of [skills](/cli/extensibility/skills/overview) you can
install from a GitHub repo, a git URL, or a local folder and reuse across
projects. Installing a plugin makes its skills available as
`/<plugin>:<skill>` slash commands, and it can pull in other plugins it depends
on automatically.

A plugin is just a source that contains:

```
my-plugin/
├── .devin-plugin/
│   └── plugin.json     # The plugin manifest
├── AGENTS.md           # Optional always-on rule
├── rules/              # Optional triggered rules
├── agents/
│   └── reviewer.md     # Optional custom subagent (reviewer/AGENT.md also works)
├── hooks.json          # Optional lifecycle hooks
├── mcp_config.json     # Optional MCP servers
└── skills/
    └── review/
        └── SKILL.md    # An ordinary skill
```

The `skills/` directory holds ordinary skills — plugins introduce no new skill
format. See [Creating Skills](/cli/extensibility/skills/creating-skills) for the
`SKILL.md` format.

Beyond skills, a plugin can ship:

- **Rules** — an `AGENTS.md` at the plugin root is injected as an always-on
  rule in every session, alongside your project's own rules. Markdown files in
  a `rules/` folder are loaded too, with the same `trigger` frontmatter and
  [activation types](/cli/extensibility/rules#rule-activation-types)
  as [Windsurf rules](/cli/extensibility/rules#rules-from-other-tools).
- **Custom subagents** — `agents/<name>.md` or `agents/<name>/AGENT.md`
  profiles (the same
  [custom subagent format](/cli/subagents#custom-subagents) as project
  subagents), available
  as `<plugin>:<name>`. Plugin subagents currently load in local Devin agents
  only — the CLI and Devin Desktop — not in cloud Devin sessions.
- **Hooks** — a `hooks.json` at the plugin root registers
  [lifecycle hooks](/cli/extensibility/hooks/lifecycle-hooks) that run in every
  session where the plugin is installed.
- **MCP servers** — an `mcp_config.json` at the plugin root declares
  [MCP servers](/cli/extensibility/mcp/overview)
  (`"mcpServers": { "<name>": { … } }`) that start with the session. Claude
  plugins' root `.mcp.json` and manifest `mcpServers` field are also honored.

---

## Installing a plugin

A plugin source can be a GitHub `owner/repo`, a git URL, or a local path:

```bash
# From GitHub
devin plugins install acme/review-tools

# From any git host
devin plugins install https://gitlab.com/acme/review-tools.git

# From a local folder (great for authoring)
devin plugins install ./my-plugin
```

Before installing, Devin shows what the plugin adds — the skills it provides,
any required plugins that will be auto-installed, and any policy it introduces
(for example, if it forbids other plugins). Pass `-y` / `--yes` to skip the
prompt.

Plugins are installed at the **user** level and are available across all your
projects.

---

## Managing plugins

```bash
# List installed plugins, their versions, and whether any are blocked by policy
devin plugins list

# Show a plugin's skills and its required/optional/forbidden lists
devin plugins info review-tools

# Re-fetch a plugin (or all plugins) at the latest version
devin plugins update review-tools
devin plugins update

# Remove a plugin (auto-installed required plugins are left in place)
devin plugins remove review-tools
```

Local plugins are linked directly to their source folder, so edits are live:
`devin plugins install ./my-plugin` → edit `skills/<name>/SKILL.md` → changes
apply on the next session, no `update` needed.

---

## The manifest

`.devin-plugin/plugin.json` describes the plugin. Only `name` is required, and
it must be unique among installed plugins (it is the `/<name>:…` namespace).

```jsonc
{
  "name": "review-tools",
  "version": "1.0.0",
  "description": "Code-review skills for our team",
  "requiredPlugins": [
    "acme/secure-base",
    { "source": "github", "repo": "acme/audit-logging" }
  ],
  "optionalPlugins": [
    "acme/deploy-tools",
    { "source": "url", "url": "https://gitlab.com/acme/extra.git" }
  ],
  "forbiddenPlugins": ["sketchy-org/bad-plugin", "acme/*", "*"]
}
```

Supported metadata fields: `name`, `version`, `description`, `author`
(`{ name, email }`), `homepage`, `repository`, `license`, and `keywords`.

Two more optional fields control where assets load from: `skills` — a path or
array of paths to skill directories, replacing the default `skills/` — and
`mcpServers` — MCP declaration file paths or an inline server map, read in
addition to the root conventions.

A dependency entry is a **source** — either a string shorthand or an object:

<PluginSourceForms />

---

## Dependencies and governance

A plugin can declare three lists, which let a single plugin act as a curated,
governed collection of other plugins.

### `requiredPlugins`

Auto-installed (recursively) when the plugin is installed. If a required plugin
is blocked by policy, the whole install fails — there is no partial install.

### `optionalPlugins`

An **allow-list** of plugins this plugin endorses. They are **not**
auto-installed; the list only matters as a carve-out against a forbidden entry
(see below).

### `forbiddenPlugins`

A **deny-list** of plugin identities and glob patterns.

<PluginGovernance />

A forbidden identity can also be a **local path** (for plugins installed from a local folder), in addition to the `owner/repo` and git-URL forms above.

---

## Inheritance and levels

Plugins aren't declared in one place. Beyond your own installs, plugins can be
required, endorsed, or forbidden by your repo and by your organization's admin.
Each source is a **level**, and the levels are ranked by **authority**, highest
first:

1. **Enterprise** — the account-wide managed manifest, configured by an admin.
2. **Org** — an org-level managed manifest, layered below its account (an org
   can add to what its account declares, but can't overrule it). This applies
   only to **cloud Devin sessions**: the CLI authenticates at the account level
   and has no org context, so org-level requires and forbids don't reach CLI
   users. Put anything you need enforced in the CLI in the enterprise/account
   manifest.
3. **Repo** — the `requiredPlugins` / `optionalPlugins` / `forbiddenPlugins` in a
   checkout's `.devin/config.json`, discovered by walking up from your working
   directory.
4. **User** — plugins you install yourself with `devin plugins install`.

Every level declares the same three lists, and within a level they combine with
the same [deny-wins, self-override rules](#dependencies-and-governance) as a
single manifest. What the levels add on top is one rule: **higher authority
wins**.

### Higher authority wins

- A lower level can never **re-permit** what a higher level forbids.
- A lower level can never **forbid** what a higher level requires — the forbid is
  ignored and the plugin still loads.

So an admin can mandate a plugin no repo or user can opt out of, and forbid a
plugin no lower level can bring back.

### A denylist is only overridden at its own level

Because allow-lists don't cross levels, the **only** way to carve an exception
out of a denylist is at the same level that declared it. A level's `forbiddenPlugins`
is overridden only by that same manifest's own `optionalPlugins` (or
`requiredPlugins`) — never by a list at a lower level.

For example, an enterprise-level managed manifest can lock the account down to a
single approved plugin:

```jsonc
// Enterprise-level managed manifest
{
  "forbiddenPlugins": ["*"],
  "optionalPlugins": ["acme/approved"]
}
```

This means "across the whole account, allow only `acme/approved` and forbid
every other plugin." No org, repo, or user can widen that allow-list — not by
installing a plugin, and not by adding it to a lower level's `optionalPlugins`.
The carve-out also covers only the entries this manifest lists directly; a
required plugin's own transitive dependencies aren't exempt, so list those
explicitly under a lockdown.

### Conflicts and dependencies

- A require and a forbid for the same plugin at the **same level** but from
  **different manifests** (for example two separately installed user-level
  plugins) resolve to the forbid — an allow-list only exempts entries in its
  *own* manifest, so it can't rescue a plugin another manifest forbids. (Within
  a single manifest, its own required/optional stay exempt from its own forbids,
  as [above](#a-denylist-is-only-overridden-at-its-own-level).)
- A plugin blocked by governance **soft-fails**: at session start its skills are
  skipped with a warning naming the forbidder, rather than aborting the session.
- Being depended upon grants no exemption. A plugin pulled in only as a
  transitive dependency is still subject to every forbid that applies to it, and
  it inherits the highest authority level of any plugin that requires it.
