pub use crate::prompt::tool::inject::{InjectedTool, RoutedCall, RoutedCapture, ToolInjection};
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
pub mod advance;
pub mod bundle;
pub mod config;
pub mod delete;
pub mod dispatch;
pub mod message;
pub mod new;
pub mod prelude;
pub mod prime;
pub mod prompt;
pub mod replay;
pub mod retarget;
pub mod scan;
pub mod stop;
pub mod tool;
#[cfg(test)]
mod tests;
#[derive(clap::Parser, Debug)]
#[command(name = "lernie", about = "Git-backed agent harness", version = cli_version())]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
fn cli_version() -> &'static str {
static VERSION: std::sync::OnceLock<String> = std::sync::OnceLock::new();
VERSION
.get_or_init(|| {
format!(
"{} (brazen {})",
env!("CARGO_PKG_VERSION"),
crate::prompt::brazen_pin()
)
})
.as_str()
}
#[derive(clap::Subcommand, Debug)]
pub enum Command {
New(new::Args),
Config(config::Args),
Prompt(prompt::Args),
Dispatch(dispatch::Args),
Retarget(retarget::Args),
Stop(stop::Args),
Message(message::Args),
Scan(scan::Args),
Bundle(bundle::Args),
Delete(delete::Args),
Replay(replay::Args),
Advance(advance::Args),
Tool(tool::Args),
Prime(prime::Args),
}
#[derive(Debug)]
pub enum Outcome {
Line(String),
Quiet,
Exec(std::process::Command),
Code(u8),
}
pub struct Fx<'a> {
pub driver_target: PathBuf,
pub adapter_target: Option<PathBuf>,
pub editor: &'a dyn Fn(&Path) -> std::io::Result<()>,
pub tool_stdin: &'a mut dyn std::io::Read,
pub tool_stdout: &'a mut dyn std::io::Write,
pub tool_stderr: &'a mut dyn std::io::Write,
pub stop: &'a AtomicBool,
pub tool_injection: Option<&'a dyn ToolInjection>,
}
#[derive(Debug)]
pub struct Error {
prefix: String,
message: String,
}
impl Error {
pub fn new(prefix: impl Into<String>, source: impl std::fmt::Display) -> Self {
Self {
prefix: prefix.into(),
message: source.to_string(),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "lernie {}: {}", self.prefix, self.message)
}
}
impl std::error::Error for Error {}
impl Command {
pub fn preludes(&self) -> &'static [fn()] {
match self {
Command::Prompt(_) | Command::Advance(_) => {
&[prelude::become_pgid_leader, prelude::install_stop_handler]
}
Command::Dispatch(_) => &[prelude::become_pgid_leader],
_ => &[],
}
}
pub fn run(self, fx: &mut Fx) -> Result<Outcome, Error> {
match self {
Command::New(a) => new::run(a, fx),
Command::Config(a) => config::run(a, fx),
Command::Prompt(a) => prompt::run(a, fx),
Command::Dispatch(a) => dispatch::run(a, fx),
Command::Retarget(a) => retarget::run(a, fx),
Command::Stop(a) => stop::run(a, fx),
Command::Message(a) => message::run(a, fx),
Command::Scan(a) => scan::run(a, fx),
Command::Bundle(a) => bundle::run(a, fx),
Command::Delete(a) => delete::run(a, fx),
Command::Replay(a) => replay::run(a, fx),
Command::Advance(a) => advance::run(a, fx),
Command::Tool(a) => tool::run(a, fx),
Command::Prime(a) => prime::run(a, fx),
}
}
}