1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! CLI application for anyrepair
mod cli;
use clap::{Parser, Subcommand};
use std::time::Instant;
#[derive(Parser)]
#[command(name = "anyrepair")]
#[command(about = "A tool for repairing LLM responses including JSON, YAML, and Markdown")]
#[command(version)]
pub struct Cli {
/// Verbose output
#[arg(short, long)]
verbose: bool,
/// Quiet mode (suppress non-error output)
#[arg(short, long)]
quiet: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Repair content (auto-detects format, or use --format to specify)
Repair {
/// Input file path (or use --input flag, stdin if not provided)
#[arg(value_name = "FILE")]
file: Option<String>,
/// Input file (stdin if not provided)
#[arg(short, long)]
input: Option<String>,
/// Output file (stdout if not provided)
#[arg(short, long)]
output: Option<String>,
/// Show confidence score
#[arg(long)]
confidence: bool,
/// Specify format: json, yaml, markdown, xml, toml, csv, ini, diff
#[arg(short, long)]
format: Option<String>,
/// Show a diff of changes without writing output
#[arg(long)]
diff: bool,
/// Show what would be repaired without writing output
#[arg(long)]
dry_run: bool,
/// Output machine-readable JSON result to stdout (for CI)
#[arg(long)]
json: bool,
/// Minimum confidence threshold (0.0–1.0); exit with code 2 if below
#[arg(long, value_name = "FLOAT")]
min_confidence: Option<f64>,
/// Print which repair strategies were applied
#[arg(long)]
explain: bool,
/// Color output: auto, always, never
#[arg(long, value_name = "WHEN", default_value = "auto")]
color: String,
},
/// Validate content without repairing
Validate {
/// Input file (stdin if not provided)
#[arg(short, long)]
input: Option<String>,
/// Format to validate (auto-detect if not provided)
#[arg(short, long)]
format: Option<String>,
},
/// Process batch files
Batch {
/// Input directory
#[arg(short, long)]
input: String,
/// Output directory
#[arg(short, long)]
output: String,
/// File pattern to match
#[arg(short, long)]
pattern: Option<String>,
/// Recursive directory processing
#[arg(short, long)]
recursive: bool,
},
/// Generate shell completions
Completions {
/// Shell: bash, zsh, fish, elvish, powershell
#[arg(value_name = "SHELL")]
shell: String,
},
/// Stream repair for large files
Stream {
/// Input file (stdin if not provided)
#[arg(short, long)]
input: Option<String>,
/// Output file (stdout if not provided)
#[arg(short, long)]
output: Option<String>,
/// Format (auto-detect if not provided)
#[arg(short, long)]
format: Option<String>,
/// Buffer size in bytes
#[arg(long)]
buffer_size: Option<usize>,
},
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let start_time = Instant::now();
match cli.command {
Commands::Repair { file, input, output, confidence, format, diff, dry_run, json, min_confidence, explain, color } => {
let input_path = file.as_deref().or(input.as_deref());
cli::repair_cmd::handle_repair(input_path, output.as_deref(), confidence, cli.verbose, format.as_deref(), diff, dry_run, json, min_confidence, explain, &color)?;
}
Commands::Validate { input, format } => {
cli::validate_cmd::handle_validate(input.as_deref(), format.as_deref(), cli.verbose)?;
}
Commands::Batch { input, output, pattern, recursive } => {
cli::batch_cmd::handle_batch(&input, &output, pattern.as_deref(), recursive, cli.verbose)?;
}
Commands::Stream { input, output, format, buffer_size } => {
let fmt = format.as_deref().unwrap_or("auto");
cli::stream_cmd::handle_stream(input.as_deref(), output.as_deref(), fmt, buffer_size, cli.verbose)?;
}
Commands::Completions { shell } => {
cli::completions_cmd::handle_completions(&shell)?;
}
}
if cli.verbose && !cli.quiet {
let duration = start_time.elapsed();
eprintln!("Completed in {:?}", duration);
}
Ok(())
}