1use std::path::PathBuf;
4
5use anyhow::Result;
6use clap::{Args, Parser, Subcommand};
7use clap_complete::Shell;
8
9use crate::config::Config;
10
11#[derive(Parser, Debug)]
12#[command(
13 name = "zagens",
14 author,
15 version,
16 about = "Zagens headless CLI for DeepSeek agent runtime",
17 long_about = "Scriptable CLI for the Zagens agent runtime.\n\nRun `zagens exec '…'` for one-shot tasks, `zagens doctor` for diagnostics, or `zagens serve --http` for the local API.\n\nNot affiliated with DeepSeek Inc."
18)]
19pub struct Cli {
20 #[command(subcommand)]
22 pub command: Option<Commands>,
23
24 #[command(flatten)]
25 pub feature_toggles: FeatureToggles,
26
27 #[arg(short, long)]
29 pub prompt: Option<String>,
30
31 #[arg(long)]
33 pub yolo: bool,
34
35 #[arg(long)]
37 pub max_subagents: Option<usize>,
38
39 #[arg(long, global = true)]
41 pub config: Option<PathBuf>,
42
43 #[arg(short, long, global = true)]
45 pub verbose: bool,
46
47 #[arg(long, global = true)]
49 pub profile: Option<String>,
50
51 #[arg(short, long, global = true)]
53 pub workspace: Option<PathBuf>,
54
55 #[arg(short, long)]
57 pub resume: Option<String>,
58
59 #[arg(short = 'c', long = "continue")]
61 pub continue_session: bool,
62
63 #[arg(long = "no-alt-screen")]
65 pub no_alt_screen: bool,
66
67 #[arg(long = "mouse-capture", conflicts_with = "no_mouse_capture")]
70 pub mouse_capture: bool,
71
72 #[arg(long = "no-mouse-capture", conflicts_with = "mouse_capture")]
74 pub no_mouse_capture: bool,
75
76 #[arg(long)]
78 pub skip_onboarding: bool,
79
80 #[arg(long = "fresh")]
82 pub fresh: bool,
83
84 #[arg(long = "no-project-config", global = true)]
86 pub no_project_config: bool,
87}
88
89#[derive(Subcommand, Debug, Clone)]
90#[allow(clippy::large_enum_variant)]
91pub enum Commands {
92 Doctor(DoctorArgs),
94 Setup(SetupArgs),
96 Completions {
98 #[arg(value_enum)]
100 shell: Shell,
101 },
102 Sessions {
104 #[arg(short, long, default_value = "20")]
106 limit: usize,
107 #[arg(short, long)]
109 search: Option<String>,
110 },
111 Init,
113 Login {
115 #[arg(long)]
117 api_key: Option<String>,
118 },
119 Logout,
121 Models(ModelsArgs),
123 Exec(ExecArgs),
125 Review(ReviewArgs),
127 Pr {
129 #[arg(value_name = "NUMBER")]
131 number: u32,
132 #[arg(short = 'R', long)]
135 repo: Option<String>,
136 #[arg(long, default_value_t = false)]
140 checkout: bool,
141 },
142 Apply(ApplyArgs),
144 Eval(EvalArgs),
146 Mcp {
148 #[command(subcommand)]
149 command: McpCommand,
150 },
151 Execpolicy(ExecpolicyCommand),
153 Features(FeaturesCli),
155 Sandbox(SandboxArgs),
157 Serve(ServeArgs),
159 Resume {
161 #[arg(value_name = "SESSION_ID")]
163 session_id: Option<String>,
164 #[arg(long = "last", default_value_t = false, conflicts_with = "session_id")]
166 last: bool,
167 },
168 Fork {
170 #[arg(value_name = "SESSION_ID")]
172 session_id: Option<String>,
173 #[arg(long = "last", default_value_t = false, conflicts_with = "session_id")]
175 last: bool,
176 },
177}
178
179#[derive(Args, Debug, Clone)]
180pub struct ExecArgs {
181 pub prompt: String,
183 #[arg(long)]
185 pub model: Option<String>,
186 #[arg(long, default_value_t = false)]
188 pub auto: bool,
189 #[arg(long, default_value_t = false)]
191 pub json: bool,
192}
193
194#[derive(Args, Debug, Clone, Default)]
195pub struct SetupArgs {
196 #[arg(long, default_value_t = false)]
198 pub mcp: bool,
199 #[arg(long, default_value_t = false)]
201 pub skills: bool,
202 #[arg(long, default_value_t = false)]
204 pub tools: bool,
205 #[arg(long, default_value_t = false)]
207 pub plugins: bool,
208 #[arg(long, default_value_t = false)]
210 pub all: bool,
211 #[arg(long, default_value_t = false)]
213 pub local: bool,
214 #[arg(long, default_value_t = false)]
216 pub force: bool,
217 #[arg(long, default_value_t = false, conflicts_with_all = ["mcp", "skills", "tools", "plugins", "all", "local", "clean"])]
219 pub status: bool,
220 #[arg(long, default_value_t = false, conflicts_with_all = ["mcp", "skills", "tools", "plugins", "all", "local", "status"])]
222 pub clean: bool,
223}
224
225#[derive(Args, Debug, Clone, Default)]
226pub struct DoctorArgs {
227 #[arg(long, default_value_t = false)]
229 pub json: bool,
230}
231
232#[derive(Args, Debug, Clone)]
233pub struct EvalArgs {
234 #[arg(long, value_name = "STEP")]
236 pub fail_step: Option<String>,
237 #[arg(long, default_value = "printf eval-harness")]
239 pub shell_command: String,
240 #[arg(long, default_value = "eval-harness")]
242 pub shell_expect_token: String,
243 #[arg(long, default_value_t = 240)]
245 pub max_output_chars: usize,
246 #[arg(long, default_value_t = false)]
248 pub json: bool,
249 #[arg(long, value_name = "DIR")]
252 pub record: Option<PathBuf>,
253}
254
255#[derive(Args, Debug, Clone, Default)]
256pub struct ModelsArgs {
257 #[arg(long, default_value_t = false)]
259 pub json: bool,
260}
261
262#[derive(Args, Debug, Default, Clone)]
263pub struct FeatureToggles {
264 #[arg(long = "enable", value_name = "FEATURE", action = clap::ArgAction::Append, global = true)]
266 pub enable: Vec<String>,
267
268 #[arg(long = "disable", value_name = "FEATURE", action = clap::ArgAction::Append, global = true)]
270 pub disable: Vec<String>,
271}
272
273impl FeatureToggles {
274 pub fn apply(&self, config: &mut Config) -> Result<()> {
275 for feature in &self.enable {
276 config.set_feature(feature, true)?;
277 }
278 for feature in &self.disable {
279 config.set_feature(feature, false)?;
280 }
281 Ok(())
282 }
283}
284
285#[derive(Args, Debug, Clone)]
286pub struct ReviewArgs {
287 #[arg(long, conflicts_with = "base")]
289 pub staged: bool,
290 #[arg(long)]
292 pub base: Option<String>,
293 #[arg(long)]
295 pub path: Option<PathBuf>,
296 #[arg(long)]
298 pub model: Option<String>,
299 #[arg(long, default_value_t = 200_000)]
301 pub max_chars: usize,
302 #[arg(long, default_value_t = false)]
304 pub json: bool,
305}
306
307#[derive(Args, Debug, Clone)]
308pub struct ApplyArgs {
309 #[arg(value_name = "PATCH_FILE")]
311 pub patch_file: Option<PathBuf>,
312}
313
314#[derive(Args, Debug, Clone)]
315pub struct ServeArgs {
316 #[arg(long)]
318 pub mcp: bool,
319 #[arg(long)]
321 pub http: bool,
322 #[arg(long)]
324 pub acp: bool,
325 #[arg(long, default_value = "127.0.0.1")]
327 pub host: String,
328 #[arg(long, default_value_t = 7878)]
330 pub port: u16,
331 #[arg(long, default_value_t = 8)]
333 pub workers: usize,
334 #[arg(long = "cors-origin", value_name = "URL")]
339 pub cors_origin: Vec<String>,
340 #[arg(long = "auth-token", value_name = "TOKEN")]
343 pub auth_token: Option<String>,
344}
345
346#[derive(Subcommand, Debug, Clone)]
347pub enum McpCommand {
348 List,
350 Init {
352 #[arg(long, default_value_t = false)]
354 force: bool,
355 },
356 Connect {
358 #[arg(value_name = "SERVER")]
360 server: Option<String>,
361 },
362 Tools {
364 #[arg(value_name = "SERVER")]
366 server: Option<String>,
367 },
368 Add {
370 name: String,
372 #[arg(long, conflicts_with = "url")]
374 command: Option<String>,
375 #[arg(long, conflicts_with = "command")]
377 url: Option<String>,
378 #[arg(long = "arg")]
380 args: Vec<String>,
381 },
382 Remove {
384 name: String,
386 },
387 Enable {
389 name: String,
391 },
392 Disable {
394 name: String,
396 },
397 Validate,
399 #[command(
404 name = "add-self",
405 long_about = "Register this DeepSeek binary as a local MCP stdio server.\n\nAdds a config entry to ~/.deepseek/mcp.json that launches `deepseek serve --mcp`\nvia the stdio transport. Other DeepSeek sessions (or any MCP client) can then\ndiscover and call tools exposed by this server.\n\nUse `deepseek serve --http` instead if you need the HTTP/SSE runtime API."
406 )]
407 AddSelf {
408 #[arg(long, default_value = "deepseek")]
410 name: String,
411 #[arg(long)]
413 workspace: Option<String>,
414 },
415}
416
417#[derive(Args, Debug, Clone)]
418pub struct ExecpolicyCommand {
419 #[command(subcommand)]
420 pub command: ExecpolicySubcommand,
421}
422
423#[derive(Subcommand, Debug, Clone)]
424pub enum ExecpolicySubcommand {
425 Check(crate::execpolicy::ExecPolicyCheckCommand),
427}
428
429#[derive(Args, Debug, Clone)]
430pub struct FeaturesCli {
431 #[command(subcommand)]
432 pub command: FeaturesSubcommand,
433}
434
435#[derive(Subcommand, Debug, Clone)]
436pub enum FeaturesSubcommand {
437 List,
439}
440
441#[derive(Args, Debug, Clone)]
442pub struct SandboxArgs {
443 #[command(subcommand)]
444 pub command: SandboxCommand,
445}
446
447#[derive(Subcommand, Debug, Clone)]
448pub enum SandboxCommand {
449 Run {
451 #[arg(long, default_value = "workspace-write")]
453 policy: String,
454 #[arg(long)]
456 network: bool,
457 #[arg(long, value_name = "PATH")]
459 writable_root: Vec<PathBuf>,
460 #[arg(long)]
462 exclude_tmpdir: bool,
463 #[arg(long)]
465 exclude_slash_tmp: bool,
466 #[arg(long)]
468 cwd: Option<PathBuf>,
469 #[arg(long, default_value_t = 60_000)]
471 timeout_ms: u64,
472 #[arg(required = true, trailing_var_arg = true)]
474 command: Vec<String>,
475 },
476}