Skip to main content

acp_cli/agent/
registry.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// A built-in or discovered agent entry in the registry.
5#[derive(Debug, Clone)]
6pub struct AgentEntry {
7    pub command: String,
8    pub args: Vec<String>,
9    pub description: String,
10}
11
12/// User-provided override for an agent's command and args (from config file).
13#[derive(Debug, Clone, Deserialize, Serialize)]
14pub struct AgentOverride {
15    pub command: String,
16    #[serde(default)]
17    pub args: Vec<String>,
18}
19
20/// Returns the default registry of built-in agents.
21pub fn default_registry() -> HashMap<String, AgentEntry> {
22    let mut m = HashMap::new();
23
24    // --- Agents with npm ACP adapters ---
25    m.insert(
26        "claude".into(),
27        AgentEntry {
28            command: "npx".into(),
29            args: vec![
30                "-y".into(),
31                "@agentclientprotocol/claude-agent-acp@^0.24.2".into(),
32            ],
33            description: "Claude Code via ACP bridge".into(),
34        },
35    );
36    m.insert(
37        "codex".into(),
38        AgentEntry {
39            command: "npx".into(),
40            args: vec!["-y".into(), "@zed-industries/codex-acp@^0.10.0".into()],
41            description: "OpenAI Codex CLI".into(),
42        },
43    );
44    m.insert(
45        "pi".into(),
46        AgentEntry {
47            command: "npx".into(),
48            args: vec!["-y".into(), "pi-acp@^0.0.22".into()],
49            description: "Pi Coding Agent".into(),
50        },
51    );
52    m.insert(
53        "kilocode".into(),
54        AgentEntry {
55            command: "npx".into(),
56            args: vec!["-y".into(), "@kilocode/cli".into(), "acp".into()],
57            description: "Kilocode".into(),
58        },
59    );
60    m.insert(
61        "opencode".into(),
62        AgentEntry {
63            command: "npx".into(),
64            args: vec!["-y".into(), "opencode-ai".into(), "acp".into()],
65            description: "OpenCode".into(),
66        },
67    );
68
69    // --- Agents with native ACP support ---
70    m.insert(
71        "gemini".into(),
72        AgentEntry {
73            command: "gemini".into(),
74            args: vec!["--acp".into()],
75            description: "Google Gemini CLI".into(),
76        },
77    );
78    m.insert(
79        "openclaw".into(),
80        AgentEntry {
81            command: "openclaw".into(),
82            args: vec!["acp".into()],
83            description: "OpenClaw".into(),
84        },
85    );
86    m.insert(
87        "cursor".into(),
88        AgentEntry {
89            command: "cursor-agent".into(),
90            args: vec!["acp".into()],
91            description: "Cursor".into(),
92        },
93    );
94    m.insert(
95        "copilot".into(),
96        AgentEntry {
97            command: "copilot".into(),
98            args: vec!["--acp".into(), "--stdio".into()],
99            description: "GitHub Copilot".into(),
100        },
101    );
102    m.insert(
103        "kiro".into(),
104        AgentEntry {
105            command: "kiro-cli-chat".into(),
106            args: vec!["acp".into()],
107            description: "Kiro CLI (AWS)".into(),
108        },
109    );
110    m.insert(
111        "iflow".into(),
112        AgentEntry {
113            command: "iflow".into(),
114            args: vec!["--experimental-acp".into()],
115            description: "iFlow ACP agent".into(),
116        },
117    );
118    m.insert(
119        "qoder".into(),
120        AgentEntry {
121            command: "qodercli".into(),
122            args: vec!["--acp".into()],
123            description: "Qoder CLI".into(),
124        },
125    );
126    m.insert(
127        "trae".into(),
128        AgentEntry {
129            command: "traecli".into(),
130            args: vec!["acp".into(), "serve".into()],
131            description: "Trae CLI".into(),
132        },
133    );
134    m.insert(
135        "kimi".into(),
136        AgentEntry {
137            command: "kimi".into(),
138            args: vec!["acp".into()],
139            description: "Kimi CLI".into(),
140        },
141    );
142    m.insert(
143        "qwen".into(),
144        AgentEntry {
145            command: "qwen".into(),
146            args: vec!["--acp".into()],
147            description: "Qwen Code".into(),
148        },
149    );
150    m.insert(
151        "droid".into(),
152        AgentEntry {
153            command: "droid".into(),
154            args: vec!["exec".into(), "--output-format".into(), "acp".into()],
155            description: "Factory Droid".into(),
156        },
157    );
158    m.insert(
159        "goose".into(),
160        AgentEntry {
161            command: "goose".into(),
162            args: vec!["acp".into()],
163            description: "Goose (Block)".into(),
164        },
165    );
166
167    m
168}
169
170/// Resolves an agent name to a `(command, args)` pair.
171///
172/// Resolution order:
173/// 1. Check `overrides` (user config) first
174/// 2. Check the built-in `registry`
175/// 3. Treat the name as a raw command with no args
176pub fn resolve_agent(
177    name: &str,
178    registry: &HashMap<String, AgentEntry>,
179    overrides: &HashMap<String, AgentOverride>,
180) -> (String, Vec<String>) {
181    if let Some(ov) = overrides.get(name) {
182        return (ov.command.clone(), ov.args.clone());
183    }
184
185    if let Some(entry) = registry.get(name) {
186        return (entry.command.clone(), entry.args.clone());
187    }
188
189    // Fallback: treat the name itself as a raw command
190    (name.to_string(), Vec::new())
191}