opencode_ralph_loop_cli/
lib.rs1pub mod cli;
2pub mod commands;
3pub mod config;
4pub mod error;
5pub mod fs_atomic;
6pub mod hash;
7pub mod manifest;
8pub mod output;
9pub mod templates;
10
11use clap::Parser;
12
13use crate::cli::{Cli, Commands};
14use crate::error::CliError;
15
16const JSON_OUTPUT_SCHEMA: &str = r#"{
17 "$schema": "https://json-schema.org/draft-07/schema#",
18 "title": "opencode-ralph-loop-cli output",
19 "description": "JSON/NDJSON output schema for opencode-ralph-loop-cli v1",
20 "type": "object",
21 "required": ["status", "command", "files", "summary", "cli_version", "schema_version", "duration_ms", "plugin_version"],
22 "properties": {
23 "status": { "type": "string", "description": "ok | drift | error:<msg>" },
24 "command": { "type": "string" },
25 "cli_version": { "type": "string" },
26 "schema_version": { "type": "integer" },
27 "plugin_version": { "type": "string" },
28 "duration_ms": { "type": "integer" },
29 "files": {
30 "type": "array",
31 "items": {
32 "type": "object",
33 "required": ["path", "action", "sha256", "size_bytes"],
34 "properties": {
35 "path": { "type": "string" },
36 "action": { "type": "string", "enum": ["created","updated","skipped","modified","missing","removed"] },
37 "sha256": { "type": "string" },
38 "expected_sha256": { "type": ["string", "null"] },
39 "size_bytes": { "type": "integer" }
40 }
41 }
42 },
43 "summary": {
44 "type": "object",
45 "properties": {
46 "created": { "type": "integer" },
47 "updated": { "type": "integer" },
48 "skipped": { "type": "integer" },
49 "modified": { "type": "integer" },
50 "missing": { "type": "integer" },
51 "removed": { "type": "integer" }
52 }
53 }
54 }
55}"#;
56
57pub fn run() -> i32 {
58 init_tracing();
59
60 ctrlc::set_handler(|| {
62 std::process::exit(130);
63 })
64 .ok();
65
66 let cli = Cli::parse();
67
68 if cli.json_schema {
70 println!("{}", JSON_OUTPUT_SCHEMA);
71 return 0;
72 }
73
74 let _config = crate::config::Config::load(cli.config.as_ref());
76
77 let result = dispatch(cli);
78
79 match result {
80 Ok(()) => 0,
81 Err(e) => {
82 eprintln!("ERROR: {e}");
83 e.exit_code()
84 }
85 }
86}
87
88fn dispatch(cli: Cli) -> Result<(), CliError> {
89 match cli.command {
90 Commands::Init(args) => commands::init::run(&args, &cli.output),
91 Commands::Check(args) => commands::check::run(&args, &cli.output),
92 Commands::Uninstall(args) => commands::uninstall::run(&args, &cli.output),
93 Commands::List => commands::list::run(&cli.output),
94 Commands::Doctor => {
95 let path = std::env::current_dir().map_err(|e| CliError::io("current directory", e))?;
96 commands::doctor::run(&path, &cli.output)
97 }
98 Commands::Completions { shell } => {
99 commands::completions::run(shell);
100 Ok(())
101 }
102 }
103}
104
105fn init_tracing() {
106 use tracing_subscriber::{EnvFilter, fmt};
107 fmt()
108 .with_env_filter(EnvFilter::from_default_env())
109 .with_writer(std::io::stderr)
110 .init();
111}