use std::path::Path;
use tokf::baseline;
use tokf::config;
use tokf::filter;
use tokf::history;
use tokf::history::OutputConfig;
use tokf::hook;
use tokf::rewrite;
use tokf::runner;
use tokf::skill;
use tokf::telemetry;
use crate::resolve;
use crate::{Cli, HookFormat, HookTool};
pub fn or_exit(r: anyhow::Result<i32>) -> i32 {
r.unwrap_or_else(|e| {
eprintln!("[tokf] error: {e:#}");
1
})
}
#[allow(clippy::too_many_lines)]
pub fn cmd_run(
command_args: &[String],
baseline_pipe: Option<&str>,
prefer_less: bool,
cli: &Cli,
reporter: &dyn telemetry::TelemetryReporter,
) -> anyhow::Result<i32> {
let filter_match = if cli.no_filter {
None
} else {
resolve::find_filter(command_args, cli.verbose, cli.no_cache)?
};
let words_consumed = filter_match.as_ref().map_or(0, |m| m.words_consumed);
let remaining_args: Vec<String> = if words_consumed > 0 {
command_args[words_consumed..].to_vec()
} else if command_args.len() > 1 {
command_args[1..].to_vec()
} else {
vec![]
};
let passthrough = filter_match
.as_ref()
.is_some_and(|m| m.config.should_passthrough(&remaining_args));
if passthrough && cli.verbose {
eprintln!("[tokf] passthrough: user args match passthrough_args, skipping filter");
}
let filter_cfg = if passthrough {
None
} else {
filter_match.as_ref().map(|m| &m.config)
};
let cmd_result =
resolve::run_command(filter_cfg, words_consumed, command_args, &remaining_args)?;
let filter_match = if passthrough { None } else { filter_match };
let Some(filter_match) = filter_match else {
if prefer_less && cli.verbose {
eprintln!("[tokf] --prefer-less has no effect: no matching filter found");
}
let raw_len = cmd_result.combined.len();
let input_bytes = match baseline_pipe {
Some(pipe_cmd) => baseline::compute(&cmd_result.combined, pipe_cmd),
None => raw_len,
};
let mask = !cli.no_mask_exit_code && cmd_result.exit_code != 0;
if mask {
println!("Error: Exit code {}", cmd_result.exit_code);
}
if !cmd_result.combined.is_empty() {
println!("{}", cmd_result.combined);
}
resolve::record_run(
command_args,
None,
None,
input_bytes,
raw_len,
raw_len,
0,
cmd_result.exit_code,
false,
);
resolve::try_auto_sync();
reporter.report(&telemetry::TelemetryEvent::new(
None,
command_args.join(" "),
input_bytes,
raw_len,
raw_len,
&cmd_result.combined,
&cmd_result.combined,
std::time::Duration::ZERO,
cmd_result.exit_code,
));
if cli.no_mask_exit_code {
return Ok(cmd_result.exit_code);
}
return Ok(0);
};
let (cfg, filter_hash) =
resolve::resolve_phase_b(filter_match, &cmd_result.combined, cli.verbose);
let (input_bytes, piped_text) = match baseline_pipe {
Some(pipe_cmd) if prefer_less => {
let text = baseline::compute_output(&cmd_result.combined, pipe_cmd);
let bytes = text.as_ref().map_or(cmd_result.combined.len(), String::len);
(bytes, text)
}
Some(pipe_cmd) => (baseline::compute(&cmd_result.combined, pipe_cmd), None),
None => (cmd_result.combined.len(), None),
};
let start = std::time::Instant::now();
let filter_opts = filter::FilterOptions {
preserve_color: cli.preserve_color,
};
let filtered = filter::apply(&cfg, &cmd_result, &remaining_args, &filter_opts);
let elapsed = start.elapsed();
if cli.timing {
eprintln!("[tokf] filter took {:.1}ms", elapsed.as_secs_f64() * 1000.0);
}
let (final_output, output_bytes, pipe_override) =
if let Some(piped) = piped_text.filter(|t| t.len() < filtered.output.len()) {
if cli.verbose {
eprintln!(
"[tokf] prefer-less: pipe output ({} bytes) < filtered ({} bytes), using pipe",
piped.len(),
filtered.output.len()
);
}
let len = piped.len();
(piped, len, true)
} else {
let len = filtered.output.len();
(filtered.output, len, false)
};
let filter_name = cfg.command.first();
let command_str = command_args.join(" ");
let raw_bytes = cmd_result.combined.len();
if cli.verbose {
eprintln!(
"[tokf] accounting: raw={raw_bytes}B baseline={input_bytes}B filtered={output_bytes}B"
);
}
resolve::record_run(
command_args,
Some(filter_name),
Some(&filter_hash),
input_bytes,
output_bytes,
raw_bytes,
elapsed.as_millis(),
cmd_result.exit_code,
pipe_override,
);
resolve::try_auto_sync();
let show_hint = cfg.show_history_hint || history::try_was_recently_run(&command_str);
let history_id = history::try_record(
&command_str,
filter_name,
&cmd_result.combined,
&final_output,
cmd_result.exit_code,
);
let output_cfg = {
let cwd = std::env::current_dir().unwrap_or_default();
let project_root = history::project_root_for(&cwd);
OutputConfig::load(Some(&project_root))
};
let mask = !cli.no_mask_exit_code && cmd_result.exit_code != 0;
if mask {
println!("Error: Exit code {}", cmd_result.exit_code);
}
if !final_output.is_empty() {
if output_cfg.show_indicator {
println!("🗜️ {final_output}");
} else {
println!("{final_output}");
}
}
if show_hint && let Some(id) = history_id {
println!("🗜️ compressed — run `tokf raw {id}` for full output");
}
reporter.report(&telemetry::TelemetryEvent::new(
Some(filter_name.to_string()),
command_str,
input_bytes,
output_bytes,
raw_bytes,
&cmd_result.combined,
&final_output,
elapsed,
cmd_result.exit_code,
));
if cli.no_mask_exit_code {
Ok(cmd_result.exit_code)
} else {
Ok(0)
}
}
pub fn cmd_check(filter_path: &Path) -> i32 {
match config::try_load_filter(filter_path) {
Ok(Some(cfg)) => {
eprintln!(
"[tokf] {} is valid (command: \"{}\")",
filter_path.display(),
cfg.command.first()
);
0
}
Ok(None) => {
eprintln!("[tokf] file not found: {}", filter_path.display());
1
}
Err(e) => {
eprintln!("[tokf] error: {e:#}");
1
}
}
}
pub fn cmd_test(
filter_path: &Path,
fixture_path: &Path,
exit_code: i32,
cli: &Cli,
) -> anyhow::Result<i32> {
let cfg = config::try_load_filter(filter_path)?
.ok_or_else(|| anyhow::anyhow!("filter not found: {}", filter_path.display()))?;
let fixture = std::fs::read_to_string(fixture_path)
.map_err(|e| anyhow::anyhow!("failed to read fixture: {}: {e}", fixture_path.display()))?;
let combined = fixture.trim_end().to_string();
let cmd_result = runner::CommandResult {
stdout: String::new(),
stderr: String::new(),
exit_code,
combined,
};
let start = std::time::Instant::now();
let filter_opts = filter::FilterOptions {
preserve_color: cli.preserve_color,
};
let filtered = filter::apply(&cfg, &cmd_result, &[], &filter_opts);
let elapsed = start.elapsed();
if cli.timing {
eprintln!("[tokf] filter took {:.1}ms", elapsed.as_secs_f64() * 1000.0);
}
if !filtered.output.is_empty() {
println!("{}", filtered.output);
}
Ok(0)
}
pub fn cmd_ls(verbose: bool) -> i32 {
let Ok(filters) = resolve::discover_filters(false) else {
eprintln!("[tokf] error: failed to discover filters");
return 1;
};
for filter in &filters {
let display_name = filter
.relative_path
.with_extension("")
.display()
.to_string();
let desc_suffix = filter
.config
.description
.as_deref()
.map_or(String::new(), |d| format!(" ({d})"));
println!(
"{display_name} \u{2192} {}{desc_suffix}",
filter.config.command.first()
);
if verbose {
eprintln!(
"[tokf] source: {} [{}]",
filter.source_path.display(),
filter.priority_label()
);
let patterns = filter.config.command.patterns();
if patterns.len() > 1 {
for p in patterns {
eprintln!("[tokf] pattern: \"{p}\"");
}
}
}
}
0
}
pub fn cmd_which(command: &str, verbose: bool) -> i32 {
let Ok(filters) = resolve::discover_filters(false) else {
eprintln!("[tokf] error: failed to discover filters");
return 1;
};
let words: Vec<&str> = command.split_whitespace().collect();
let cwd = std::env::current_dir().unwrap_or_default();
for filter in &filters {
if filter.matches(&words).is_some() {
let display_name = filter
.relative_path
.with_extension("")
.display()
.to_string();
let variant_info = if filter.config.variant.is_empty() {
String::new()
} else {
let res =
config::variant::resolve_variants(&filter.config, &filters, &cwd, verbose);
let resolved = res.config.command.first().to_string();
if resolved != filter.config.command.first() {
format!(" -> variant: \"{resolved}\"")
} else if res.output_variants.is_empty() {
format!(
" ({} variant(s), none matched by file)",
filter.config.variant.len()
)
} else {
let names: Vec<&str> = res
.output_variants
.iter()
.map(|v| v.name.as_str())
.collect();
format!(
" ({} variant(s), {} deferred to output-pattern: {})",
filter.config.variant.len(),
res.output_variants.len(),
names.join(", ")
)
}
};
println!(
"{display_name} [{}] command: \"{}\"{variant_info}",
filter.priority_label(),
filter.config.command.first()
);
if verbose {
eprintln!("[tokf] source: {}", filter.source_path.display());
}
return 0;
}
}
eprintln!("[tokf] no filter found for \"{command}\"");
1
}
pub fn cmd_rewrite(command: &str, verbose: bool) -> i32 {
let result = rewrite::rewrite(command, verbose);
println!("{result}");
0
}
pub fn cmd_skill_install(global: bool) -> i32 {
match skill::install(global) {
Ok(()) => 0,
Err(e) => {
eprintln!("[tokf] error: {e:#}");
1
}
}
}
pub fn cmd_hook_handle(format: &HookFormat) -> i32 {
match format {
HookFormat::ClaudeCode => {
hook::handle();
}
HookFormat::Gemini => {
hook::handle_gemini();
}
HookFormat::Cursor => {
hook::handle_cursor();
}
}
0
}
pub fn cmd_hook_install(
global: bool,
tool: &HookTool,
path: Option<&Path>,
install_context: bool,
) -> i32 {
let tokf_bin = path.map_or_else(|| "tokf".to_string(), |p| p.display().to_string());
let result = match tool {
HookTool::ClaudeCode => hook::install(global, &tokf_bin, install_context),
HookTool::OpenCode => hook::opencode::install(global, &tokf_bin),
HookTool::Codex => hook::codex::install(global),
HookTool::GeminiCli => hook::gemini::install(global, &tokf_bin, install_context),
HookTool::Cursor => hook::cursor::install(global, &tokf_bin, install_context),
HookTool::Cline => hook::cline::install(global),
HookTool::Windsurf => hook::windsurf::install(global),
HookTool::Copilot => hook::copilot::install(global),
HookTool::Aider => hook::aider::install(global),
};
match result {
Ok(()) => 0,
Err(e) => {
eprintln!("[tokf] hook install failed: {e:#}");
1
}
}
}