Plugins are in beta. Behavior and configuration may change in future releases.
/<plugin>:<skill> slash commands, and it can pull in other plugins it depends
on automatically.
A plugin is just a source that contains:
skills/ directory holds ordinary skills — plugins introduce no new skill
format. See Creating Skills for the
SKILL.md format.
A plugin can also 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
as Windsurf rules.
Installing a plugin
A plugin source can be a GitHubowner/repo, a git URL, or a local path:
-y / --yes to skip the
prompt.
Plugins are installed at the user level and are available across all your
projects.
Managing plugins
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).
name, version, description, author
({ name, email }), homepage, repository, license, and keywords.
A dependency entry is a source — either a string shorthand or an object:
All GitHub forms for the same repo (
owner/repo, the HTTPS URL, the .git URL, the SSH form) refer to the same plugin identity.
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.
forbiddenPlugins entries are matched against plugin identities:
- An exact identity, written as
owner/repoor a git URL. All GitHub forms of the same repo (owner/repo, the HTTPS URL, the.gitURL, the SSH form) refer to the same identity. - A glob pattern — any entry containing
*. The*matches any sequence of characters, including/:acme/*matches all ofacme’s GitHub repos,*/secretsmatches a repo namedsecretsunder any owner, andhttps://gitlab.com/acme/*matches any repo under that path. - The lone
"*", which matches everything else (a full lockdown).
- Deny wins. A plugin is blocked if any active manifest or installed plugin forbids it. If nothing forbids anything, nothing is blocked.
- Self-override. A manifest’s (or plugin’s) own
requiredPluginsandoptionalPlugins— and, for a plugin, the plugin itself — are exempt from its own forbidden list, so"forbiddenPlugins": ["*"]plus"optionalPlugins": ["acme/approved"]means “allow only what this manifest lists; forbid everything else.” The carve-out covers only those direct entries, not a required plugin’s transitive dependencies — list those explicitly under a lockdown. - No cross-scope re-permitting. One manifest’s or plugin’s allow-list cannot re-permit what another forbids. A
"forbiddenPlugins": ["*"]lockdown can’t be defeated from a lower scope.
- Install time — installing a blocked plugin (or one whose required plugins can’t be satisfied, or whose name collides with an installed plugin) is refused.
- Load time — a plugin blocked after it’s already installed stays on disk, but its skills are skipped at session start with a warning naming the forbidder.
owner/repo and git-URL forms above.
