Skip to content

feat(packaging): record Devin's native plugin layout - #156

Merged
jothimani-rajendran merged 1 commit into
mainfrom
claude/devin-packaging-record
Sep 21, 2026
Merged

jothimani-rajendran merged 1 commit into
mainfrom
claude/devin-packaging-record

Conversation

@jothimani-rajendran

@jothimani-rajendran jothimani-rajendran commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

What this changes

Moves devin out of packaging-limits.json's unrecorded map and into packaging.json, in the existing entries' exact shape, so chock's emitter (chock-devin-plugins) can source Devin's plugin layout from agentseam instead of hand-writing it. No version bump; no core or consumer changes.

Field-by-field basis (vendor-docs, page text read 2026-09-21)

Sources: https://docs.devin.ai/cli/extensibility/plugins/overview, https://docs.devin.ai/cli/changelog/stable.

  • unit: "plugin" — "the plugin is the unit of installation."
  • manifest: ".devin-plugin/plugin.json" — the native layout only. Devin's own precedence chain is native → .claude-plugin/plugin.json (Claude fallback) → a root plugin.json in the open Agent Plugins 1.0.0 spec. Per the plan's guidance, the current schema has no home for a fallback chain, so the entry records the native layout and carries the chain in notes, the way codex_cli's notes carry its Legacy-vs-AgentPlugin story.
  • parts.skill: "skills/{name}/SKILL.md" — matches the documented tree (skills/review/SKILL.md) and is byte-identical to every other recorded plugin format's skill path (now claude_code, codex_cli, copilot, cursor, devin, gemini_cli).
  • parts.subagent: "agents/{name}.md" — matches the documented tree (agents/reviewer.md); the docs note reviewer/AGENT.md also works, recorded in notes since the flat form matches the shape every other agent here uses.
  • parts.hooks: "hooks.json" — at the plugin root, not the nested hooks/hooks.json every other recorded format shares. The CLI changelog's "hooks are also loaded from hooks/hooks.json" (under "Broader Claude plugin compatibility") is the Claude-fallback layout's path, kept distinct here on purpose.
  • parts.mcp: ".mcp.json" — "Optional MCP servers" in the documented tree.
  • parts.command: null, parts.executable: null — no slash-command location and no bundled-executable/scripts folder is documented anywhere for the native or Claude-fallback layouts; only the Agent Plugins 1.0.0 manifest's ./-prefixed MCP server command resolves against the plugin root, and that convention is stated to apply "only to plugins whose manifest is the root plugin.json" — not this one. PART_LIMITS reasons added for both, matching the codex_cli/copilot/cursor pattern.
  • plugin_root: [] — hook commands do receive a DEVIN_PLUGIN_ROOT environment variable (CLI changelog), but whether ${DEVIN_PLUGIN_ROOT} expands inside a hooks.json command string is not documented, so no expansion token is claimed.
  • notes also carries: the requiredPlugins/optionalPlugins/forbiddenPlugins marketplace convention standing in for an index file (a git-subdir source pointing at plugins/<name>/, merge-to-default-branch as the release); the vendor's own words that plugin hooks are "currently best effort and fail open ... don't rely on them for crucial guardrails yet," documented for "local Devin sessions (the CLI and Devin Desktop)" only (cloud-session and SessionStart/SessionEnd firing not established here); and that the schema has no part for rules/ or the optional always-on AGENTS.md.
  • verified: page text read 2026-09-21, both URLs cited.

Vendors / matrix re-check

Checked src/agentseam/data/vendors/devin.json and matrix.json against the same vendor pages (plus the hook-surface pages: /cli/extensibility/hooks/overview, /lifecycle-hooks). No contradiction found — nothing changed. Specifically:

  • config_path: ".devin/hooks.v1.json" matches "standalone, recommended."
  • matrix-notes.json's "reads .claude/settings.json for hooks by default" agrees with the fuller .claude/settings.json / .claude/settings.local.json / user-level fallback list now read (read_config_from.claude, default enabled) — an elaboration, not a correction.
  • Exit-code/fail-open semantics (0 success, 2 block, other non-zero logged-not-blocking) agree with matrix.json's fail_mode: "open" and devin.json's bare_allow: "unverified".
  • Output shapes (hookSpecificOutput.updatedInput at PreToolUse only, additionalContext at PostToolUse/SessionStart/UserPromptSubmit) agree with devin.json's gates and context_events.
  • claims.accept_names already lists PermissionRequest and PostCompaction as Devin-only markers; the docs now corroborate both are real Devin hook events Claude Code never sends.

Two gaps observed, left unrecorded as out of this PR's scope (packaging, not the hook/dispatch primitive): PostCompaction has no family-event slot in this codebase (only pre_compact/PreCompact exists) so devin.json's events map has no key for it; and the Aug 21 changelog's PreToolUse tool_provenance field and Stop's last_assistant_message addition aren't modeled in fields/verdicts. Neither is a false claim by an existing row — just unmodeled — so nothing was changed.

Tests

  • Removed devin from the "unlocated" tuple in test_proven_but_unlocated_parts_are_distinguished_from_absent_ones.
  • test_every_matrix_agent_is_accounted_for and test_an_agent_is_never_both_recorded_and_unrecorded pass unedited.
  • Updated the two same_path_for assertions in test_a_skill_is_literally_the_same_file_in_every_bundle_format — devin now shares the skill and subagent paths, which the generic tests caught immediately.
  • Added test_devin_native_hooks_path_differs_from_the_claude_fallback_one (native hooks path, distinctness from the shared hooks/hooks.json, executable unrecorded, empty plugin_root) and test_devin_plan_renders_the_native_layout_and_drops_commands (manifest render, skill/subagent paths, command dropped with its own reason) — in the shape of test_codex_declares_its_components_and_only_one_format_carries_hooks.
  • Regenerated examples/generated/devin.md (python3 examples/generate.py) since its packaging section was stale.
  • Added a ## [Unreleased] / ### Added CHANGELOG.md entry, matching house convention.

Verification

$ python -m pytest -q
1844 passed, 4 skipped, 1041 warnings in 35.52s

$ python -m ruff check .
All checks passed!

$ python -m ruff format --check .
161 files already formatted

Checks

  • No capability claim is widened without a mechanism behind it
  • Any new/changed MATRIX row carries a verified record (version, date, method) — n/a, matrix.json unchanged; the new packaging.json row carries its own verified record
  • Payload shapes come from a primary source (vendor doc) — https://docs.devin.ai/cli/extensibility/plugins/overview, https://docs.devin.ai/cli/changelog/stable
  • pytest -q passes
  • ruff check . and ruff format --check . pass
  • Runtime path is still stdlib-only (no new imports outside the standard library)
  • Commits are signed off (git commit -s)

Notes for the reviewer

Left command and executable as null rather than guessing — no location is documented for either in the native layout. plugin_root is an empty tuple for the same reason: the docs give DEVIN_PLUGIN_ROOT as an environment variable, not a ${...}-style string token, and only for MCP server configs in the other two fallback layouts, not this one. Happy to widen either the moment a vendor page documents them.

🤖 Generated with Claude Code

Moves devin out of packaging-limits.json's unrecorded map and into
packaging.json, in the existing entries' shape. The native layout
(.devin-plugin/plugin.json > .claude-plugin/plugin.json > root
plugin.json precedence) gives skills/{name}/SKILL.md, agents/{name}.md,
and a root-level hooks.json -- deliberately distinct from the shared
hooks/hooks.json every other recorded format uses here, since that path
belongs to the Claude-fallback layout one rung down the chain. command
and executable stay null: neither location is documented for this
layout, and plugin_root stays empty since no plugin-root expansion
token is documented inside a hooks.json command string (only a
same-named environment variable handed to hook processes). The
precedence chain, the marketplace convention, and the vendor's
best-effort/fail-open wording for plugin hooks live in the entry's
notes rather than new fields, matching codex_cli's Legacy-vs-AgentPlugin
story.

Regenerates examples/generated/devin.md and adds a devin-specific test
alongside the two shared-path assertions it now joins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
@jothimani-rajendran
jothimani-rajendran marked this pull request as ready for review September 21, 2026 22:40
@jothimani-rajendran
jothimani-rajendran merged commit 558640e into main Sep 21, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant