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