Skip to main content

oy_code_cli/
lib.rs

1use clap::Parser;
2use oy_agent::infrastructure::tools::edit::EditTool;
3use oy_agent::infrastructure::tools::read::ReadTool;
4use oy_agent::infrastructure::tools::write::WriteTool;
5use oy_agent::infrastructure::tools::{ToolRegistry, bash::BashTool};
6use oy_ai::AiConfig;
7use serde::Deserialize;
8use std::env;
9
10/// CLI arguments for oy-agent
11#[derive(Parser, Debug)]
12#[command(author, version, about)]
13pub struct CliArgs {
14    /// Prompt to send to the agent (if omitted, launches the TUI)
15    #[arg(short = 'p', long)]
16    pub prompt: Option<String>,
17
18    #[arg(short = 'm', long)]
19    pub model: Option<String>,
20}
21
22/// Configuration loaded from ~/.oy-ai-agent/config.toml
23#[derive(Debug, Deserialize, Default)]
24pub struct CliConfig {
25    pub api_key: Option<String>,
26    pub base_url: Option<String>,
27    pub model: Option<String>,
28}
29
30impl CliConfig {
31    /// Load config from ~/.oy-ai-agent/config.toml, returning defaults for missing fields.
32    pub fn load() -> Self {
33        let home = match dirs::home_dir() {
34            Some(h) => h,
35            None => return Self::default(),
36        };
37        let config_path = home.join(".oy-ai-agent").join("config.toml");
38        if !config_path.exists() {
39            return Self::default();
40        }
41        match std::fs::read_to_string(&config_path) {
42            Ok(content) => toml::from_str(&content).unwrap_or_default(),
43            Err(_) => Self::default(),
44        }
45    }
46}
47
48/// Build an `AiConfig` by merging CLI args, config file, env vars, and defaults.
49///
50/// Priority (highest first):
51///   1. CLI argument (`--model`)
52///   2. Config file (`~/.oy-ai-agent/config.toml`)
53///   3. Environment variable (`OPENROUTER_*`)
54///   4. Hardcoded default
55///
56/// `api_key` is required: if none of the sources provide it, the process exits.
57pub fn build_provider_config(cli_config: &CliConfig, cli_args: &CliArgs) -> AiConfig {
58    let api_key = cli_config
59        .api_key
60        .clone()
61        .or_else(|| env::var("OPENROUTER_API_KEY").ok())
62        .unwrap_or_else(|| {
63            eprintln!(
64                "OPENROUTER_API_KEY is not set. Set it in ~/.oy-ai-agent/config.toml \
65                 or the OPENROUTER_API_KEY environment variable."
66            );
67            std::process::exit(1);
68        });
69
70    let base_url = cli_config
71        .base_url
72        .clone()
73        .or_else(|| env::var("OPENROUTER_BASE_URL").ok())
74        .unwrap_or_else(|| "https://openrouter.ai/api/v1".to_string());
75
76    let model = cli_args
77        .model
78        .clone()
79        .or_else(|| cli_config.model.clone())
80        .or_else(|| env::var("OPENROUTER_MODEL").ok())
81        .unwrap_or_else(|| "anthropic/claude-haiku-4.5".to_string());
82
83    AiConfig::new(base_url, api_key, model)
84}
85
86/// Register the default set of tools (Read, Write, Bash).
87pub fn register_default_tools(registry: &mut ToolRegistry) {
88    registry.register(ReadTool);
89    registry.register(WriteTool);
90    registry.register(EditTool);
91    registry.register(BashTool);
92}
93
94/// Run the agent with the given CLI arguments, or launch the TUI if no prompt is given.
95pub async fn run(args: CliArgs) -> Result<(), anyhow::Error> {
96    // No prompt → launch TUI
97    if args.prompt.is_none() {
98        oy_tui::run_tui()
99            .await
100            .map_err(|e| anyhow::Error::msg(format!("{}", e)))?;
101        return Ok(());
102    }
103
104    Ok(())
105}