pub mod cli;
pub mod commands;
pub mod config;
pub mod error;
pub mod fs_atomic;
pub mod hash;
pub mod manifest;
pub mod output;
pub mod templates;
use clap::Parser;
use crate::cli::{Cli, Commands};
use crate::error::CliError;
const JSON_OUTPUT_SCHEMA: &str = r#"{
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "opencode-ralph-loop-cli output",
"description": "JSON/NDJSON output schema for opencode-ralph-loop-cli v1",
"type": "object",
"required": ["status", "command", "files", "summary", "cli_version", "schema_version", "duration_ms", "plugin_version"],
"properties": {
"status": { "type": "string", "description": "ok | drift | error:<msg>" },
"command": { "type": "string" },
"cli_version": { "type": "string" },
"schema_version": { "type": "integer" },
"plugin_version": { "type": "string" },
"duration_ms": { "type": "integer" },
"files": {
"type": "array",
"items": {
"type": "object",
"required": ["path", "action", "sha256", "size_bytes"],
"properties": {
"path": { "type": "string" },
"action": { "type": "string", "enum": ["created","updated","skipped","modified","missing","removed"] },
"sha256": { "type": "string" },
"expected_sha256": { "type": ["string", "null"] },
"size_bytes": { "type": "integer" }
}
}
},
"summary": {
"type": "object",
"properties": {
"created": { "type": "integer" },
"updated": { "type": "integer" },
"skipped": { "type": "integer" },
"modified": { "type": "integer" },
"missing": { "type": "integer" },
"removed": { "type": "integer" }
}
}
}
}"#;
pub fn run() -> i32 {
init_tracing();
ctrlc::set_handler(|| {
std::process::exit(130);
})
.ok();
let cli = Cli::parse();
if cli.json_schema {
println!("{}", JSON_OUTPUT_SCHEMA);
return 0;
}
let _config = crate::config::Config::load(cli.config.as_ref());
let result = dispatch(cli);
match result {
Ok(()) => 0,
Err(e) => {
eprintln!("ERROR: {e}");
e.exit_code()
}
}
}
fn dispatch(cli: Cli) -> Result<(), CliError> {
match cli.command {
Commands::Init(args) => commands::init::run(&args, &cli.output),
Commands::Check(args) => commands::check::run(&args, &cli.output),
Commands::Uninstall(args) => commands::uninstall::run(&args, &cli.output),
Commands::List => commands::list::run(&cli.output),
Commands::Doctor => {
let path = std::env::current_dir().map_err(|e| CliError::io("current directory", e))?;
commands::doctor::run(&path, &cli.output)
}
Commands::Completions { shell } => {
commands::completions::run(shell);
Ok(())
}
}
}
fn init_tracing() {
use tracing_subscriber::{EnvFilter, fmt};
fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
}