Add Devin CLI as a supported coding agent - #873
uhlhosting wants to merge 5 commits into
Conversation
Devin CLI reads hooks from the "hooks" key of ~/.config/devin/config.json in Claude's shape, minus the Notification event and with snake_case tool names in matchers. The integration mirrors Claude's: SessionStart / UserPromptSubmit / PreToolUse drive busy, PostToolUse drives idle, the ask_user_question|exit_plan_mode PreToolUse matcher drives awaiting_input, and PermissionRequest stands in for Claude's permission Notification (awaiting_input plus a fixed notify, since its payload carries no displayable text). Stop emits idle and forwards last_assistant_message as the notify body. Also route Cmd+V image paste to Devin's Ctrl+V chord, the same native paste translation Claude gets, and install both bundled skills under ~/.config/devin/skills.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
installState compared managed hook commands as a Set<String>, collapsing identical command strings across slots. Payloads that legitimately reuse one command — Devin's busy under both UserPromptSubmit and the catch-all PreToolUse group — then read as installed after a user deleted a single occurrence, so repair was never offered. Compare occurrences keyed by (event, matcher, command) as a multiset instead. The stricter slot-aware compare also catches a managed command parked under the wrong event and duplicated groups, for every agent on the shared installer.
|
Addressed the duplicate-hooks review finding in 167c8ca.
The fix lives in the shared |
installState compared managed hook occurrences as an unordered multiset, so reordering groups within an event read as installed. Hook groups execute in array order, and Devin's payload depends on it: the catch-all PreToolUse busy matcher must precede the ask_user_question|exit_plan_mode awaiting-input matcher for the later emit to win. Reversed groups left the badge stuck on busy while Settings showed the integration current. Compare the ordered sequence of managed groups per event — matcher plus managed commands in array order — instead. Only managed groups count, so a user-authored group interleaved between ours still reads installed.
|
Addressed the reordered-hooks finding in 01eea53.
The sequence is scoped to managed groups only: a user-authored group interleaved between ours shifts absolute indices but not managed execution order, so it still reports New coverage: reorder → outdated and interleaved user group → installed cases in |
installState still filtered managed hooks by command string and compared only matcher + command list, so a change to `type`, `timeout`, or `env` read as installed. Grok's env passthrough needed a separate additional closure for that reason. Change ManagedGroupOccurrence to keep the full managed hook JSONValue objects in array order. That catches metadata drift alongside command, occurrence-count, and ordering drift. Grok's env validation is now handled by the same comparison, so the additionalOutdatedIfInstalled hook and its closure are removed.
|
Addressed the hook-metadata drift finding in 6204b5b.
New coverage:
|
The hook payload assumptions are now explicitly sourced from the local Devin CLI documentation: event names, hook format, matcher semantics, per-event stdin fields, Stop's last_assistant_message, and Ctrl+V image paste. Add a test asserting that the Stop notify body is extracted from last_assistant_message, matching the documented contract.
|
Verified the Devin contract assumptions and documented them in 0b89866. Sources checked against the local Devin CLI stable docs (2026-06):
The Existing tests already cover:
|
Closes #602
Summary
devintoSkillAgent(display name "Devin CLI", config dir~/.config/devin,devin-markasset) so it appears in the Coding Agents settings panel like every other agent.DevinSettingsInstallermerges the Supacode hook map into the"hooks"key of~/.config/devin/config.json, reusing the shared prune-and-replaceAgentHookSettingsFileInstallerso user settings and user-authored hooks survive install/uninstall.DevinHookSettingspayload mirrorsClaudeHooksPayloadwith the deltas documented in the issue: noNotificationevent, and thePreToolUseawaiting-input matcher uses snake_case tool names (ask_user_question|exit_plan_mode).PermissionRequest(which Devin does support) stands in for Claude's permissionNotification: awaiting-input badge plus a fixed notification, since the payload carries no displayable text for the stdin-sourced notify.Stopemitsidleand forwardslast_assistant_messageas the notify body.PostCompactionis deliberately unmapped (fires after compaction, can't drive the compacting badge).supacode-cli,supacode-deeplinks) under~/.config/devin/skills/— Devin's optional SKILL.md frontmatter means the shared markdown works unmodified.devin-mark.svg(from the issue) with template rendering like the other monochrome marks.Type of change
ready)How was this tested?
DevinHookSettingsTests(event coverage, matcher ordering/casing, ownership sentinel, variable allowlist, OSC emit-to-parse round-trip) andDevinSettingsInstallerTests(install/uninstall state, siblingconfig.jsonkeys and user-authored hooks preserved).SkillAgentTestsupdated for the new case (display ordering, capabilities, relocatable custom-folder install).GhosttySurfaceViewTestscovers the Devin Cmd+V routing.AgentIntegrationFactory/DevinSettingsInstalleragainst a temp home: install writes~/.config/devin/config.jsonhooks + skills, preserves sibling config keys and user hooks, uninstall removes only managed commands, and every generated hook command parses undersh -n.Environment note: this checkout was verified with
make check(format + lint) and the standalone compile/test above; the full app build needs an Xcode 26.x toolchain, which this machine doesn't have installed.make checkpasses (format + lint)make testpassesAI tool disclosure (optional)
Checklist
Closes #above.ready.