feat: add Devin CLI as supported TUI agent (#5282)

* feat: add Devin CLI as supported TUI agent

Register Devin across shared agent config, telemetry, desktop/mobile
catalogs, localization, and README. Uses stdin-after-start prompt
delivery with --permission-mode bypass default args.

* test: assert Devin default --permission-mode bypass in startup plans
This commit is contained in:
Yang,Zhou 2026-06-13 07:10:09 +08:00 committed by GitHub
parent 6837cca9f3
commit 74e2c952f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 84 additions and 9 deletions

View File

@ -185,6 +185,7 @@ Works with **any CLI agent** — if it runs in a terminal, it runs in Orca.
<a href="https://pi.dev"><kbd><img src="https://pi.dev/favicon.svg" alt="Pi logo" width="16" valign="middle" /> Pi</kbd></a> &nbsp;
<a href="https://omp.sh"><kbd><img src="https://omp.sh/favicon.svg" alt="oh-my-pi logo" width="16" valign="middle" /> oh-my-pi</kbd></a> &nbsp;
<a href="https://hermes-agent.nousresearch.com/docs/"><kbd><img src="https://www.google.com/s2/favicons?domain=nousresearch.com&sz=64" alt="Hermes Agent logo" width="16" valign="middle" /> Hermes Agent</kbd></a> &nbsp;
<a href="https://devin.ai/cli"><kbd><img src="https://www.google.com/s2/favicons?domain=devin.ai&sz=64" alt="Devin logo" width="16" valign="middle" /> Devin</kbd></a> &nbsp;
<a href="https://block.github.io/goose/docs/quickstart/"><kbd><img src="https://www.google.com/s2/favicons?domain=goose-docs.ai&sz=64" alt="Goose logo" width="16" valign="middle" /> Goose</kbd></a> &nbsp;
<a href="https://docs.augmentcode.com/cli/overview"><kbd><img src="https://www.google.com/s2/favicons?domain=augmentcode.com&sz=64" alt="Auggie logo" width="16" valign="middle" /> Auggie</kbd></a> &nbsp;
<a href="https://github.com/autohandai/code-cli"><kbd><img src="https://www.google.com/s2/favicons?domain=autohand.ai&sz=64" alt="Autohand Code logo" width="16" valign="middle" /> Autohand Code</kbd></a> &nbsp;

View File

@ -32,6 +32,7 @@ export const NEVER_TRANSLATE_VALUES = new Set([
'Continue',
'Cursor',
'Droid',
'Devin',
'Gemini',
'GitHub Copilot',
'Goose',

View File

@ -34,6 +34,7 @@ export const MOBILE_TUI_AGENT_AUTO_PICK_ORDER = [
'qwen-code',
'rovo',
'hermes',
'devin',
'openclaw'
] as const satisfies readonly TuiAgent[]
@ -68,6 +69,7 @@ export const MOBILE_TUI_AGENT_LABELS: Record<TuiAgent, string> = {
'qwen-code': 'Qwen Code',
rovo: 'Rovo Dev',
hermes: 'Hermes',
devin: 'Devin',
openclaw: 'OpenClaw'
}
@ -97,6 +99,7 @@ export const MOBILE_TUI_AGENT_FAVICON_DOMAINS: Partial<Record<TuiAgent, string>>
'qwen-code': 'qwenlm.github.io',
rovo: 'atlassian.com',
hermes: 'nousresearch.com',
devin: 'devin.ai',
openclaw: 'openclaw.ai'
}
@ -131,6 +134,7 @@ export const MOBILE_TUI_AGENT_LAUNCH_COMMANDS: Record<TuiAgent, string> = {
'qwen-code': 'qwen-code',
rovo: 'rovo',
hermes: 'hermes',
devin: 'devin',
openclaw: 'openclaw'
}

View File

@ -274,7 +274,8 @@
"760bc6883d": "Codex",
"a5fc0cb622": "OpenClaude",
"bf53f09bf8": "Claude Agent Teams",
"0708ed89f1": "Claude"
"0708ed89f1": "Claude",
"fc80296033": "Devin"
},
"skill": {
"cli": {

View File

@ -274,7 +274,8 @@
"760bc6883d": "Codex",
"a5fc0cb622": "OpenClaude",
"bf53f09bf8": "Claude Agent Teams",
"0708ed89f1": "Claude"
"0708ed89f1": "Claude",
"fc80296033": "Devin"
},
"skill": {
"cli": {

View File

@ -274,7 +274,8 @@
"760bc6883d": "Codex",
"a5fc0cb622": "OpenClaude",
"bf53f09bf8": "Claude Agent Teams",
"0708ed89f1": "Claude"
"0708ed89f1": "Claude",
"fc80296033": "Devin"
},
"skill": {
"cli": {

View File

@ -274,7 +274,8 @@
"760bc6883d": "Codex",
"a5fc0cb622": "OpenClaude",
"bf53f09bf8": "Claude Agent Teams",
"0708ed89f1": "Claude"
"0708ed89f1": "Claude",
"fc80296033": "Devin"
},
"skill": {
"cli": {

View File

@ -274,7 +274,8 @@
"760bc6883d": "Codex",
"a5fc0cb622": "OpenClaude",
"bf53f09bf8": "Claude Agent Teams",
"0708ed89f1": "Claude"
"0708ed89f1": "Claude",
"fc80296033": "Devin"
},
"skill": {
"cli": {

View File

@ -244,6 +244,13 @@ export const getAgentCatalog = createLocalizedCatalog((): AgentCatalogEntry[] =>
faviconDomain: 'nousresearch.com',
homepageUrl: 'https://hermes-agent.nousresearch.com/docs/'
},
{
id: 'devin',
label: translate('auto.lib.agent.catalog.fc80296033', 'Devin'),
cmd: 'devin',
faviconDomain: 'devin.ai',
homepageUrl: 'https://devin.ai/cli'
},
{
id: 'openclaw',
label: translate('auto.lib.agent.catalog.5dff448636', 'OpenClaw'),

View File

@ -126,7 +126,8 @@ const WELL_KNOWN_LABELS: Record<string, string> = {
droid: 'Droid',
'command-code': 'Command Code',
grok: 'Grok',
hermes: 'Hermes'
hermes: 'Hermes',
devin: 'Devin'
}
export function formatAgentTypeLabel(agentType: AgentType | null | undefined): string {
@ -181,7 +182,8 @@ const ICONABLE_AGENT_TYPES: Record<TuiAgent, true> = {
hermes: true,
openclaw: true,
copilot: true,
grok: true
grok: true,
devin: true
}
export function agentTypeToIconAgent(agentType: AgentType | null | undefined): TuiAgent | null {

View File

@ -4,6 +4,7 @@ import {
buildAgentStartupPlan,
isShellProcess
} from './tui-agent-startup'
import { resolveTuiAgentLaunchArgs } from '../../../shared/tui-agent-launch-defaults'
describe('buildAgentStartupPlan', () => {
it('passes Claude prompts as a positional interactive argument', () => {
@ -150,6 +151,23 @@ describe('buildAgentStartupPlan', () => {
})
})
it('launches Devin first and injects the prompt after startup', () => {
expect(
buildAgentStartupPlan({
agent: 'devin',
prompt: 'Trace the failing test',
cmdOverrides: {},
agentArgs: resolveTuiAgentLaunchArgs('devin', null),
platform: 'linux'
})
).toEqual({
agent: 'devin',
launchCommand: "devin '--permission-mode' 'bypass'",
expectedProcess: 'devin',
followupPrompt: 'Trace the failing test'
})
})
it('launches Command Code by its unambiguous binary with a positional prompt', () => {
expect(
buildAgentStartupPlan({

View File

@ -44,7 +44,8 @@ const TUI_AGENT_KIND_BY_AGENT = {
hermes: 'hermes',
openclaw: 'openclaw',
copilot: 'copilot',
grok: 'grok'
grok: 'grok',
devin: 'devin'
} satisfies Record<TuiAgent, ConcreteAgentKind>
// Why: `satisfies Record<TuiAgent, …>` makes the lookup exhaustive at compile

View File

@ -30,6 +30,7 @@ export type WellKnownAgentType =
| 'command-code'
| 'grok'
| 'hermes'
| 'devin'
| 'unknown'
export type AgentType = WellKnownAgentType | (string & {})

View File

@ -100,6 +100,7 @@ export const AGENT_KIND_VALUES = [
'openclaw',
'copilot',
'grok',
'devin',
'other'
] as const
export const agentKindSchema = z.enum(AGENT_KIND_VALUES)

View File

@ -301,6 +301,17 @@ export const TUI_AGENT_CONFIG: Record<TuiAgent, TuiAgentConfig> = {
launchCmd: 'grok',
expectedProcess: 'grok',
promptInjectionMode: 'stdin-after-start'
},
devin: {
detectCmd: 'devin',
launchCmd: 'devin',
expectedProcess: 'devin',
// Why: `devin -- <prompt>` auto-submits the prompt (the issue's claim
// that it pre-fills without submitting is incorrect per the official
// docs at docs.devin.ai/cli/reference/commands). `stdin-after-start`
// launches the REPL first, then pastes via bracketed paste so the
// user can review before submitting — same as aider, goose, amp, etc.
promptInjectionMode: 'stdin-after-start'
}
}

View File

@ -28,7 +28,8 @@ export const DEFAULT_TUI_AGENT_ARGS: Partial<Record<TuiAgent, string>> = {
rovo: '--yolo',
hermes: '--yolo',
copilot: '--yolo',
grok: '--permission-mode bypassPermissions'
grok: '--permission-mode bypassPermissions',
devin: '--permission-mode bypass'
}
export const DEFAULT_TUI_AGENT_ENV: Partial<Record<TuiAgent, Record<string, string>>> = {

View File

@ -34,6 +34,7 @@ export const TUI_AGENT_AUTO_PICK_ORDER = [
'qwen-code',
'rovo',
'hermes',
'devin',
'openclaw'
] as const satisfies readonly TuiAgent[]

View File

@ -266,4 +266,24 @@ describe('tui agent startup plans', () => {
expect(plan?.expectedProcess).toBe('omp')
expect(plan?.launchCommand).toBe('omp; unset ORCA_OMP_PREFILL')
})
it('launches Devin with stdin-after-start prompt delivery', () => {
const plan = buildAgentStartupPlan({
agent: 'devin',
prompt: 'fix the tests',
cmdOverrides: {},
agentArgs: resolveTuiAgentLaunchArgs('devin', null),
platform: 'linux'
})
expect(plan).toEqual({
agent: 'devin',
launchCommand: "devin '--permission-mode' 'bypass'",
expectedProcess: 'devin',
followupPrompt: 'fix the tests'
})
})
it('appends Devin default permission-mode bypass before stdin prompt delivery', () => {
expect(resolveTuiAgentLaunchArgs('devin', null)).toBe('--permission-mode bypass')
})
})

View File

@ -1962,6 +1962,7 @@ export type TuiAgent =
| 'openclaw' // OpenClaw
| 'copilot' // GitHub Copilot CLI
| 'grok' // xAI Grok CLI
| 'devin' // Devin CLI
export type TaskViewPresetId = 'all' | 'issues' | 'review' | 'my-issues' | 'my-prs' | 'prs'