diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 206b558..36cecfe 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -5,30 +5,57 @@ use futures_util::TryStreamExt; use llm_ollama::{OllamaClient, OllamaOptions, types::ChatMessage}; use std::io::{self, Write}; +#[derive(clap::Subcommand, Debug)] +enum Cmd { + Read {path: String}, + Glob {root: String}, + Grep {root: String, pattern: String}, +} + #[derive(Parser, Debug)] -#[command(name = "code", version, about = "Rust code-agent (Ollama)")] +#[command(name = "code", version)] struct Args { - /// Override Ollama base URL (local or cloud) #[arg(long)] ollama_url: Option, - - /// Model name #[arg(long)] model: Option, - - /// Print response only (headless-like) + #[arg(long)] + api_key: Option, #[arg(long)] print: bool, - - /// Prompt to send #[arg()] prompt: Vec, + #[command(subcommand)] + cmd: Option, } #[tokio::main] async fn main() -> Result<()> { color_eyre::install()?; let args = Args::parse(); + let settings = load_settings(None).unwrap_or_default(); + + if let Some(cmd) = args.cmd { + match cmd { + Cmd::Read { path } => { + let s = tools_fs::read_file(&path)?; + println!("{}", s); + return Ok(()); + } + Cmd::Glob { root } => { + for p in tools_fs::glob_list(&root)? { + println!("{}", p); + } + return Ok(()); + } + Cmd::Grep { root, pattern } => { + for (path, line_number, text) in tools_fs::grep(&root, &pattern)? { + println!("{path}:{line_number}:{text}") + } + return Ok(()); + } + } + } let prompt = if args.prompt.is_empty() { "Say hello".to_string() @@ -36,11 +63,21 @@ async fn main() -> Result<()> { args.prompt.join(" ") }; - let settings = load_settings(None).unwrap_or_default(); - let base_url = args.ollama_url.unwrap_or(settings.ollama_url); let model = args.model.unwrap_or(settings.model); + let api_key = args.api_key.or(settings.api_key); - let client = OllamaClient::new(base_url); + // Use Ollama Cloud when model has "-cloud" suffix AND API key is set + let use_cloud = model.ends_with("-cloud") && api_key.is_some(); + let client = if use_cloud { + OllamaClient::with_cloud().with_api_key(api_key.unwrap()) + } else { + let base_url = args.ollama_url.unwrap_or(settings.ollama_url); + let mut client = OllamaClient::new(base_url); + if let Some(key) = api_key { + client = client.with_api_key(key); + } + client + }; let opts = OllamaOptions { model, stream: true, diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index aed4988..87f033d 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -14,6 +14,8 @@ pub struct Settings { pub model: String, #[serde(default = "default_mode")] pub mode: String, // "plan" (read-only) for now + #[serde(default)] + pub api_key: Option, // For Ollama Cloud or other API authentication } fn default_ollama_url() -> String { @@ -32,6 +34,7 @@ impl Default for Settings { ollama_url: default_ollama_url(), model: default_model(), mode: default_mode(), + api_key: None, } } } @@ -52,6 +55,8 @@ pub fn load_settings(project_root: Option<&str>) -> Result