Skip to main content

rho_coding_agent/
cli.rs

1use std::{num::NonZeroUsize, path::PathBuf, time::Duration};
2
3use clap::{Parser, Subcommand, ValueEnum};
4
5use rho_providers::{credentials::CredentialStoreBackend, reasoning::ReasoningLevel};
6
7use crate::app::automation_protocol::parse_duration;
8
9fn parse_credential_store_backend(value: &str) -> Result<CredentialStoreBackend, String> {
10    CredentialStoreBackend::parse(value).map_err(|error| error.to_string())
11}
12
13/// Output contract used by a non-interactive `rho run` invocation.
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
15pub enum OutputFormat {
16    /// Print only the authoritative final assistant answer.
17    #[default]
18    Text,
19    /// Stream independently versioned JSON Lines events.
20    Jsonl,
21}
22
23#[derive(Parser, Debug)]
24#[command(name = "rho")]
25pub struct Cli {
26    #[arg(long)]
27    pub provider: Option<String>,
28    #[arg(long)]
29    pub model: Option<String>,
30    #[arg(long)]
31    pub config: Option<PathBuf>,
32    #[arg(long, value_parser = ["api-key", "codex", "anthropic-api-key", "google-api-key", "github-copilot", "xai-api-key", "xai-oauth", "moonshot-api-key", "poolside-api-key", "openrouter-api-key", "openrouter-oauth", "kimi-oauth"])]
33    pub auth: Option<String>,
34    /// Do not send rho's system prompt, including AGENTS.md and skill context.
35    #[arg(long)]
36    pub no_system_prompt: bool,
37    /// Do not expose any tools to the model.
38    #[arg(long)]
39    pub no_tools: bool,
40    /// Do not expose the delegated-agent tools (agent/agents) to the model.
41    #[arg(long, global = true)]
42    pub no_subagents: bool,
43    /// Select the agent definition used for this session or automation run.
44    #[arg(long, global = true, value_name = "ID")]
45    pub agent: Option<String>,
46    /// Override reasoning level: off, minimal, low, medium, high, xhigh, or max.
47    #[arg(long)]
48    pub reasoning: Option<ReasoningLevel>,
49    /// Resume an existing session by UUID or UUID prefix. Omit the ID to choose from a picker.
50    #[arg(short = 'R', long, value_name = "ID", num_args = 0..=1)]
51    pub resume: Option<Option<String>>,
52    #[command(subcommand)]
53    pub command: Option<Command>,
54}
55
56#[derive(Subcommand, Debug)]
57pub enum Command {
58    /// Run one non-interactive automation prompt and print the final answer.
59    Run {
60        /// Read additional prompt text from stdin.
61        #[arg(long)]
62        stdin: bool,
63        /// Stream progress to stdout and write a structured status/result
64        /// file (JSON) that is updated during the run and finalized on exit.
65        #[arg(long, value_name = "PATH")]
66        output_file: Option<PathBuf>,
67        /// Select plain final-answer output or a JSON Lines event stream.
68        #[arg(long, value_enum, default_value_t)]
69        output: OutputFormat,
70        /// Override the model-step budget for this run.
71        #[arg(long, value_name = "N")]
72        max_steps: Option<NonZeroUsize>,
73        /// Cancel the run after this wall-clock duration.
74        #[arg(long, value_name = "DURATION", value_parser = parse_duration)]
75        timeout: Option<Duration>,
76        /// Prompt text to send to the agent.
77        #[arg(value_name = "PROMPT", num_args = 0..)]
78        prompt: Vec<String>,
79    },
80    /// Watch a delegated agent run in a read-only TUI.
81    Attach {
82        /// Delegated run ID shown when the agent was started.
83        #[arg(value_name = "ID")]
84        id: String,
85    },
86    /// Log in to a provider from a browser or device-code flow.
87    Login {
88        /// Provider to authenticate, for example openai-codex or github-copilot.
89        #[arg(value_name = "PROVIDER")]
90        provider: String,
91        /// Use device-code login instead of opening a local browser callback.
92        #[arg(long)]
93        device_auth: bool,
94    },
95    /// Configure or probe provider credential storage.
96    CredentialStore {
97        #[command(subcommand)]
98        command: CredentialStoreCommand,
99    },
100    /// Update rho using the detected installation method.
101    Update,
102    /// List or delete saved sessions.
103    Sessions {
104        #[command(subcommand)]
105        command: SessionsCommand,
106    },
107}
108
109#[derive(Subcommand, Debug)]
110pub enum SessionsCommand {
111    /// List saved sessions for the current workspace (or all projects).
112    List {
113        /// Include sessions from every workspace, not only the current directory.
114        #[arg(long)]
115        all_projects: bool,
116    },
117    /// Delete one or more sessions by UUID or UUID prefix.
118    Rm {
119        /// Session UUID or unique prefix. May be repeated.
120        #[arg(value_name = "ID", required = true, num_args = 1..)]
121        ids: Vec<String>,
122        /// Delete even when a parent-linked run is still non-terminal.
123        ///
124        /// Use only for stale Starting/Running artifacts left after a crash.
125        #[arg(long)]
126        force: bool,
127        /// Skip the confirmation prompt for cross-project deletes.
128        #[arg(short = 'y', long)]
129        yes: bool,
130    },
131}
132
133#[derive(Subcommand, Debug)]
134pub enum CredentialStoreCommand {
135    /// Test a credential backend by writing and deleting a temporary secret.
136    Probe {
137        /// Backend to test: os or file (`auto` is accepted as an alias for os).
138        #[arg(
139            value_name = "BACKEND",
140            default_value = "os",
141            value_parser = parse_credential_store_backend
142        )]
143        backend: CredentialStoreBackend,
144    },
145    /// Show the configured credential backend (unset, os, or file).
146    Status,
147    /// Save the credential backend used by future rho processes.
148    Set {
149        /// Backend to use: os or file (`auto` is accepted as an alias for os).
150        #[arg(value_name = "BACKEND", value_parser = parse_credential_store_backend)]
151        backend: CredentialStoreBackend,
152    },
153}
154
155#[cfg(test)]
156#[path = "cli_tests.rs"]
157mod tests;