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:
parent
6837cca9f3
commit
74e2c952f1
|
|
@ -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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const NEVER_TRANSLATE_VALUES = new Set([
|
|||
'Continue',
|
||||
'Cursor',
|
||||
'Droid',
|
||||
'Devin',
|
||||
'Gemini',
|
||||
'GitHub Copilot',
|
||||
'Goose',
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -274,7 +274,8 @@
|
|||
"760bc6883d": "Codex",
|
||||
"a5fc0cb622": "OpenClaude",
|
||||
"bf53f09bf8": "Claude Agent Teams",
|
||||
"0708ed89f1": "Claude"
|
||||
"0708ed89f1": "Claude",
|
||||
"fc80296033": "Devin"
|
||||
},
|
||||
"skill": {
|
||||
"cli": {
|
||||
|
|
|
|||
|
|
@ -274,7 +274,8 @@
|
|||
"760bc6883d": "Codex",
|
||||
"a5fc0cb622": "OpenClaude",
|
||||
"bf53f09bf8": "Claude Agent Teams",
|
||||
"0708ed89f1": "Claude"
|
||||
"0708ed89f1": "Claude",
|
||||
"fc80296033": "Devin"
|
||||
},
|
||||
"skill": {
|
||||
"cli": {
|
||||
|
|
|
|||
|
|
@ -274,7 +274,8 @@
|
|||
"760bc6883d": "Codex",
|
||||
"a5fc0cb622": "OpenClaude",
|
||||
"bf53f09bf8": "Claude Agent Teams",
|
||||
"0708ed89f1": "Claude"
|
||||
"0708ed89f1": "Claude",
|
||||
"fc80296033": "Devin"
|
||||
},
|
||||
"skill": {
|
||||
"cli": {
|
||||
|
|
|
|||
|
|
@ -274,7 +274,8 @@
|
|||
"760bc6883d": "Codex",
|
||||
"a5fc0cb622": "OpenClaude",
|
||||
"bf53f09bf8": "Claude Agent Teams",
|
||||
"0708ed89f1": "Claude"
|
||||
"0708ed89f1": "Claude",
|
||||
"fc80296033": "Devin"
|
||||
},
|
||||
"skill": {
|
||||
"cli": {
|
||||
|
|
|
|||
|
|
@ -274,7 +274,8 @@
|
|||
"760bc6883d": "Codex",
|
||||
"a5fc0cb622": "OpenClaude",
|
||||
"bf53f09bf8": "Claude Agent Teams",
|
||||
"0708ed89f1": "Claude"
|
||||
"0708ed89f1": "Claude",
|
||||
"fc80296033": "Devin"
|
||||
},
|
||||
"skill": {
|
||||
"cli": {
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export type WellKnownAgentType =
|
|||
| 'command-code'
|
||||
| 'grok'
|
||||
| 'hermes'
|
||||
| 'devin'
|
||||
| 'unknown'
|
||||
export type AgentType = WellKnownAgentType | (string & {})
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ export const AGENT_KIND_VALUES = [
|
|||
'openclaw',
|
||||
'copilot',
|
||||
'grok',
|
||||
'devin',
|
||||
'other'
|
||||
] as const
|
||||
export const agentKindSchema = z.enum(AGENT_KIND_VALUES)
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>>> = {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export const TUI_AGENT_AUTO_PICK_ORDER = [
|
|||
'qwen-code',
|
||||
'rovo',
|
||||
'hermes',
|
||||
'devin',
|
||||
'openclaw'
|
||||
] as const satisfies readonly TuiAgent[]
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue