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.
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:
"owner/repo"→ GitHub"https://…","git@…","ssh://…"→ git URL{ "source": "github", "repo": "owner/repo" }{ "source": "url", "url": "https://gitlab.com/team/plugin.git" }
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. Each entry is one of:
- An exact plugin identity, written as
owner/repo, a git URL, or a local path. - A glob pattern — any entry containing
*. The*matches any sequence of characters, including/. Patterns are normalized into canonical-identity space first, soacme/*becomeshttps://github.com/acme/*(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 every other plugin (an un-defeatable lockdown).
- Deny wins. A plugin is blocked if any installed plugin forbids it (via its
exact identity, a matching glob, or
"*"). - Self-override. A plugin’s own
requiredPlugins,optionalPlugins, and itself are exempt from its own forbidden list. SoforbiddenPlugins: ["*"]together withoptionalPlugins: [B, C]means “allow myself, B, and C; forbid everything else.” - No cross-plugin re-permitting. One plugin’s allow-lists cannot re-permit
what another plugin forbids. An installed plugin with
forbiddenPlugins: ["*"]is an un-defeatable lockdown. - Default open. If no installed plugin forbids anything, nothing is blocked.
- 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 — if a plugin becomes blocked after install (for example, a forbidding plugin is installed later), it stays on disk but its skills are skipped at session start, with a warning naming the plugin that forbids it.

