use std::env; use std::path::{Path, PathBuf}; use commands::handle_slash_command; use compat_harness::{extract_manifest, UpstreamPaths}; use runtime::{load_system_prompt, BootstrapPlan, CompactionConfig, Session}; fn main() { let args: Vec = env::args().skip(1).collect(); match parse_args(&args) { Ok(CliAction::DumpManifests) => dump_manifests(), Ok(CliAction::BootstrapPlan) => print_bootstrap_plan(), Ok(CliAction::PrintSystemPrompt { cwd, date }) => print_system_prompt(cwd, date), Ok(CliAction::ResumeSession { session_path, command, }) => resume_session(&session_path, command), Ok(CliAction::Help) => print_help(), Err(error) => { eprintln!("{error}"); print_help(); std::process::exit(2); } } } #[derive(Debug, Clone, PartialEq, Eq)] enum CliAction { DumpManifests, BootstrapPlan, PrintSystemPrompt { cwd: PathBuf, date: String, }, ResumeSession { session_path: PathBuf, command: Option, }, Help, } fn parse_args(args: &[String]) -> Result { if args.is_empty() { return Ok(CliAction::Help); } if matches!(args.first().map(String::as_str), Some("--help" | "-h")) { return Ok(CliAction::Help); } if args.first().map(String::as_str) == Some("--resume") { return parse_resume_args(&args[1..]); } match args[0].as_str() { "dump-manifests" => Ok(CliAction::DumpManifests), "bootstrap-plan" => Ok(CliAction::BootstrapPlan), "system-prompt" => parse_system_prompt_args(&args[1..]), other => Err(format!("unknown subcommand: {other}")), } } fn parse_system_prompt_args(args: &[String]) -> Result { let mut cwd = env::current_dir().map_err(|error| error.to_string())?; let mut date = "2026-03-31".to_string(); let mut index = 0; while index < args.len() { match args[index].as_str() { "--cwd" => { let value = args .get(index + 1) .ok_or_else(|| "missing value for --cwd".to_string())?; cwd = PathBuf::from(value); index += 2; } "--date" => { let value = args .get(index + 1) .ok_or_else(|| "missing value for --date".to_string())?; date.clone_from(value); index += 2; } other => return Err(format!("unknown system-prompt option: {other}")), } } Ok(CliAction::PrintSystemPrompt { cwd, date }) } fn parse_resume_args(args: &[String]) -> Result { let session_path = args .first() .ok_or_else(|| "missing session path for --resume".to_string()) .map(PathBuf::from)?; let command = args.get(1).cloned(); if args.len() > 2 { return Err("--resume accepts at most one trailing slash command".to_string()); } Ok(CliAction::ResumeSession { session_path, command, }) } fn dump_manifests() { let workspace_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); let paths = UpstreamPaths::from_workspace_dir(&workspace_dir); match extract_manifest(&paths) { Ok(manifest) => { println!("commands: {}", manifest.commands.entries().len()); println!("tools: {}", manifest.tools.entries().len()); println!("bootstrap phases: {}", manifest.bootstrap.phases().len()); } Err(error) => { eprintln!("failed to extract manifests: {error}"); std::process::exit(1); } } } fn print_bootstrap_plan() { for phase in BootstrapPlan::claude_code_default().phases() { println!("- {phase:?}"); } } fn print_system_prompt(cwd: PathBuf, date: String) { match load_system_prompt(cwd, date, env::consts::OS, "unknown") { Ok(sections) => println!("{}", sections.join("\n\n")), Err(error) => { eprintln!("failed to build system prompt: {error}"); std::process::exit(1); } } } fn resume_session(session_path: &Path, command: Option) { let session = match Session::load_from_path(session_path) { Ok(session) => session, Err(error) => { eprintln!("failed to restore session: {error}"); std::process::exit(1); } }; match command { Some(command) if command.starts_with('/') => { let Some(result) = handle_slash_command( &command, &session, CompactionConfig { max_estimated_tokens: 0, ..CompactionConfig::default() }, ) else { eprintln!("unknown slash command: {command}"); std::process::exit(2); }; if let Err(error) = result.session.save_to_path(session_path) { eprintln!("failed to persist resumed session: {error}"); std::process::exit(1); } println!("{}", result.message); } Some(other) => { eprintln!("unsupported resumed command: {other}"); std::process::exit(2); } None => { println!( "Restored session from {} ({} messages).", session_path.display(), session.messages.len() ); } } } fn print_help() { println!("rusty-claude-cli"); println!(); println!("Current scaffold commands:"); println!( " dump-manifests Read upstream TS sources and print extracted counts" ); println!(" bootstrap-plan Print the current bootstrap phase skeleton"); println!(" system-prompt [--cwd PATH] [--date YYYY-MM-DD]"); println!(" Build a Claude-style system prompt from CLAUDE.md and config files"); println!(" --resume SESSION.json [/compact] Restore a saved session and optionally run a slash command"); } #[cfg(test)] mod tests { use super::{parse_args, CliAction}; use std::path::PathBuf; #[test] fn parses_system_prompt_options() { let args = vec![ "system-prompt".to_string(), "--cwd".to_string(), "/tmp/project".to_string(), "--date".to_string(), "2026-04-01".to_string(), ]; assert_eq!( parse_args(&args).expect("args should parse"), CliAction::PrintSystemPrompt { cwd: PathBuf::from("/tmp/project"), date: "2026-04-01".to_string(), } ); } #[test] fn parses_resume_flag_with_slash_command() { let args = vec![ "--resume".to_string(), "session.json".to_string(), "/compact".to_string(), ]; assert_eq!( parse_args(&args).expect("args should parse"), CliAction::ResumeSession { session_path: PathBuf::from("session.json"), command: Some("/compact".to_string()), } ); } }