lowfat 0.2.1

CLI binary for lowfat
use anyhow::Result;
use lowfat_core::config::RunfConfig;
use lowfat_plugin::discovery::discover_plugins;
use std::collections::BTreeSet;

/// Commands with native compiled filters — always wrapped.
const BUILTIN_COMMANDS: &[&str] = &["docker", "git", "ls"];

/// Collect all wrappable commands from 3 sources:
/// 1. Native compiled filters (git, docker, ls)
/// 2. Installed plugins (~/.lowfat/plugins/)
/// 3. Pipeline declarations in .lowfat config (pipeline.cargo = ...)
fn collect_commands() -> Vec<String> {
    let mut cmds: BTreeSet<String> = BUILTIN_COMMANDS.iter().map(|s| s.to_string()).collect();

    let config = RunfConfig::resolve();

    // Plugins
    let plugins = discover_plugins(&config.plugin_dir);
    for plugin in &plugins {
        for cmd in &plugin.manifest.plugin.commands {
            cmds.insert(cmd.clone());
        }
    }

    // Pipeline declarations (e.g., pipeline.cargo = strip-ansi,truncate:50)
    for cmd in config.pipelines.keys() {
        cmds.insert(cmd.clone());
    }

    cmds.into_iter().collect()
}

/// Print shell init script for eval.
/// Usage: eval "$(lowfat shell-init zsh)"
pub fn run(shell: &str) -> Result<()> {
    match shell {
        "zsh" | "bash" => print_posix_init(),
        "fish" => print_fish_init(),
        _ => {
            eprintln!("lowfat: unsupported shell: {shell} (supported: bash, zsh, fish)");
        }
    }
    Ok(())
}

fn print_posix_init() {
    let commands = collect_commands();

    println!("# lowfat shell init — auto-wrap commands for LLM token savings");
    println!("# Usage: eval \"$(lowfat shell-init zsh)\"");
    println!();

    // Only auto-wrap inside LLM environments
    println!("if [[ \"$CLAUDECODE\" == \"1\" ]] || [[ -n \"$CODEX_ENV\" ]] || [[ \"$LOWFAT_AUTO\" == \"1\" ]]; then");

    for cmd in &commands {
        println!("  {cmd}() {{ command lowfat \"${{FUNCNAME[0]:-{cmd}}}\" \"$@\"; }}");
    }

    println!("fi");
    println!();

    // Session summary on exit
    println!("if [[ \"${{LOWFAT_SESSION:-1}}\" == \"1\" ]]; then");
    println!("  _lowfat_session_exit() {{");
    println!("    command lowfat status 2>/dev/null || true");
    println!("  }}");
    println!("  trap '_lowfat_session_exit' EXIT");
    println!("fi");
}

fn print_fish_init() {
    let commands = collect_commands();

    println!("# lowfat shell init for fish");
    println!();

    println!("if test \"$CLAUDECODE\" = 1; or test -n \"$CODEX_ENV\"; or test \"$LOWFAT_AUTO\" = 1");
    for cmd in &commands {
        println!("  function {cmd}; command lowfat {cmd} $argv; end");
    }
    println!("end");
}