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