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!["-y".into(), "@zed-industries/claude-agent-acp".into()],
30            description: "Claude Code via ACP bridge".into(),
31        },
32    );
33    m.insert(
34        "codex".into(),
35        AgentEntry {
36            command: "npx".into(),
37            args: vec!["-y".into(), "@zed-industries/codex-acp".into()],
38            description: "OpenAI Codex CLI".into(),
39        },
40    );
41    m.insert(
42        "pi".into(),
43        AgentEntry {
44            command: "npx".into(),
45            args: vec!["-y".into(), "pi-acp".into()],
46            description: "Pi Coding Agent".into(),
47        },
48    );
49    m.insert(
50        "kilocode".into(),
51        AgentEntry {
52            command: "npx".into(),
53            args: vec!["-y".into(), "@kilocode/cli".into(), "acp".into()],
54            description: "Kilocode".into(),
55        },
56    );
57    m.insert(
58        "opencode".into(),
59        AgentEntry {
60            command: "npx".into(),
61            args: vec!["-y".into(), "opencode-ai".into(), "acp".into()],
62            description: "OpenCode".into(),
63        },
64    );
65
66    // --- Agents with native ACP support ---
67    m.insert(
68        "gemini".into(),
69        AgentEntry {
70            command: "gemini".into(),
71            args: vec!["--acp".into()],
72            description: "Google Gemini CLI".into(),
73        },
74    );
75    m.insert(
76        "openclaw".into(),
77        AgentEntry {
78            command: "openclaw".into(),
79            args: vec!["acp".into()],
80            description: "OpenClaw".into(),
81        },
82    );
83    m.insert(
84        "cursor".into(),
85        AgentEntry {
86            command: "cursor-agent".into(),
87            args: vec!["acp".into()],
88            description: "Cursor".into(),
89        },
90    );
91    m.insert(
92        "copilot".into(),
93        AgentEntry {
94            command: "copilot".into(),
95            args: vec!["--acp".into(), "--stdio".into()],
96            description: "GitHub Copilot".into(),
97        },
98    );
99    m.insert(
100        "kiro".into(),
101        AgentEntry {
102            command: "kiro-cli".into(),
103            args: vec!["acp".into()],
104            description: "Kiro CLI (AWS)".into(),
105        },
106    );
107    m.insert(
108        "kimi".into(),
109        AgentEntry {
110            command: "kimi".into(),
111            args: vec!["acp".into()],
112            description: "Kimi CLI".into(),
113        },
114    );
115    m.insert(
116        "qwen".into(),
117        AgentEntry {
118            command: "qwen".into(),
119            args: vec!["--acp".into()],
120            description: "Qwen Code".into(),
121        },
122    );
123    m.insert(
124        "droid".into(),
125        AgentEntry {
126            command: "droid".into(),
127            args: vec!["exec".into(), "--output-format".into(), "acp".into()],
128            description: "Factory Droid".into(),
129        },
130    );
131    m.insert(
132        "goose".into(),
133        AgentEntry {
134            command: "goose".into(),
135            args: vec!["acp".into()],
136            description: "Goose (Block)".into(),
137        },
138    );
139
140    m
141}
142
143/// Resolves an agent name to a `(command, args)` pair.
144///
145/// Resolution order:
146/// 1. Check `overrides` (user config) first
147/// 2. Check the built-in `registry`
148/// 3. Treat the name as a raw command with no args
149pub fn resolve_agent(
150    name: &str,
151    registry: &HashMap<String, AgentEntry>,
152    overrides: &HashMap<String, AgentOverride>,
153) -> (String, Vec<String>) {
154    if let Some(ov) = overrides.get(name) {
155        return (ov.command.clone(), ov.args.clone());
156    }
157
158    if let Some(entry) = registry.get(name) {
159        return (entry.command.clone(), entry.args.clone());
160    }
161
162    // Fallback: treat the name itself as a raw command
163    (name.to_string(), Vec::new())
164}