use anyhow::Result;
use clap::Args;
use octomind::config::Config;
use octomind::session;
use std::io::{self, IsTerminal, Read};
#[derive(Args, Debug)]
pub struct RunArgs {
#[arg(value_name = "TAG")]
pub tag: Option<String>,
#[arg(long, short = 'n', value_name = "NAME")]
pub name: Option<String>,
#[arg(long, short = 'r', value_name = "SESSION")]
pub resume: Option<String>,
#[arg(long)]
pub resume_recent: bool,
#[arg(long = "format")]
pub format: Option<String>,
#[arg(long, short = 'm', value_name = "MODEL")]
pub model: Option<String>,
#[arg(long)]
pub daemon: bool,
#[arg(long)]
pub sandbox: bool,
#[arg(long = "hook", value_name = "NAME")]
pub hooks: Vec<String>,
}
pub async fn execute(args: &RunArgs, config: &Config) -> Result<()> {
let is_interactive_session = args.format.is_none() && std::io::stdin().is_terminal();
let piped_input = if !is_interactive_session {
if args.daemon && std::io::stdin().is_terminal() {
Some(String::new())
} else {
Some(read_input()?)
}
} else {
None
};
let (run_config, role) =
octomind::agent::resolver::resolve_config_and_role(args.tag.as_deref(), config, None)
.await?;
let session_args = octomind::session::chat::session::GenericSessionArgs {
role: role.clone(),
mode: args.format.clone().unwrap_or_else(|| "plain".to_string()),
name: args.name.clone(),
resume: args.resume.clone(),
resume_recent: args.resume_recent,
model: args.model.clone(),
daemon: args.daemon,
hooks: args.hooks.clone(),
..Default::default()
};
if is_interactive_session {
session::chat::run_interactive_session(&session_args, &run_config).await
} else {
session::chat::run_interactive_session_with_input(
&session_args,
&run_config,
&piped_input.unwrap(),
)
.await
}
}
fn read_input() -> Result<String> {
if !std::io::stdin().is_terminal() {
let mut buf = String::new();
io::stdin().read_to_string(&mut buf)?;
let input = buf.trim().to_string();
if input.is_empty() {
anyhow::bail!("No input provided via stdin");
}
Ok(input)
} else {
anyhow::bail!("--format requires input via stdin or piped data")
}
}