Skip to main content

walrus_cli/cmd/
mod.rs

1//! CLI argument parsing and command dispatch.
2
3use crate::runner::gateway::GatewayRunner;
4use anyhow::{Context, Result};
5use clap::{Parser, Subcommand};
6use compact_str::CompactString;
7use std::path::PathBuf;
8
9pub mod agent;
10pub mod chat;
11pub mod config;
12pub mod download;
13pub mod memory;
14pub mod send;
15
16/// Walrus CLI client — connects to walrusd via Unix domain socket.
17#[derive(Parser, Debug)]
18#[command(name = "walrus", about = "Walrus CLI client")]
19pub struct Cli {
20    /// Subcommand to execute.
21    #[command(subcommand)]
22    pub command: Command,
23
24    /// Agent name override.
25    #[arg(long, global = true)]
26    pub agent: Option<CompactString>,
27
28    /// Path to the walrusd socket.
29    #[arg(long, global = true)]
30    pub socket: Option<PathBuf>,
31}
32
33impl Cli {
34    /// Resolve the agent name from CLI flags or fall back to "assistant".
35    pub fn resolve_agent(&self) -> CompactString {
36        self.agent.clone().unwrap_or_else(|| "assistant".into())
37    }
38
39    /// Resolve the socket path from CLI flag or default.
40    fn resolve_socket(&self) -> PathBuf {
41        self.socket.clone().unwrap_or_else(|| {
42            dirs::home_dir()
43                .expect("no home directory")
44                .join(".walrus")
45                .join("walrus.sock")
46        })
47    }
48
49    /// Parse and dispatch the CLI command.
50    pub async fn run(self) -> Result<()> {
51        let agent = self.resolve_agent();
52        let socket_path = self.resolve_socket();
53        match self.command {
54            Command::Config(cmd) => cmd.run(),
55            Command::Chat(cmd) => {
56                let runner = connect(&socket_path).await?;
57                cmd.run(runner, agent).await
58            }
59            Command::Send(cmd) => {
60                let mut runner = connect(&socket_path).await?;
61                cmd.run(&mut runner, &agent).await
62            }
63            Command::Agent(cmd) => {
64                let mut runner = connect(&socket_path).await?;
65                cmd.run(&mut runner).await
66            }
67            Command::Memory(cmd) => {
68                let mut runner = connect(&socket_path).await?;
69                cmd.run(&mut runner).await
70            }
71            Command::Download(cmd) => {
72                let mut runner = connect(&socket_path).await?;
73                cmd.run(&mut runner).await
74            }
75        }
76    }
77}
78
79/// Top-level subcommands.
80#[derive(Subcommand, Debug)]
81pub enum Command {
82    /// Start an interactive chat REPL.
83    Chat(chat::Chat),
84    /// Send a one-shot message to an agent.
85    Send(send::Send),
86    /// Manage agents.
87    #[command(subcommand)]
88    Agent(agent::AgentCommand),
89    /// Manage memory entries.
90    #[command(subcommand)]
91    Memory(memory::MemoryCommand),
92    /// Download a model from HuggingFace.
93    Download(download::Download),
94    /// Manage configuration.
95    #[command(subcommand)]
96    Config(config::ConfigCommand),
97}
98
99/// Connect to walrusd, returning a helpful error if not running.
100async fn connect(socket_path: &std::path::Path) -> Result<GatewayRunner> {
101    GatewayRunner::connect(socket_path).await.with_context(|| {
102        format!(
103            "failed to connect to walrusd at {}. Is walrusd running?",
104            socket_path.display()
105        )
106    })
107}