rho-coding-agent 1.17.0

A lightweight agent harness inspired by Pi
Documentation
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;