truth-mirror 0.15.0

Truthfulness gate and adversarial reviewer harness for AI coding agents.
Documentation
use std::process::ExitCode;

use anyhow::Result;
use clap::{CommandFactory, FromArgMatches};
use tracing_subscriber::EnvFilter;

pub mod claim;
pub mod cli;
pub mod config;
pub mod context;
pub mod debt;
pub mod enforcement;
pub mod gate;
pub mod hooks;
pub mod ledger;
pub mod memory_skill;
mod messages;
pub mod provenance;
pub mod reinject;
pub mod resolve;
pub mod reviewer;
mod shell;
pub mod skills;
pub mod state;
pub mod status;
pub mod surface;
mod time;
pub mod tui;
pub mod watcher;

pub fn init_tracing() {
    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn"));
    let _ = tracing_subscriber::fmt().with_env_filter(filter).try_init();
}

pub fn run_from_env(command_name: &'static str) -> Result<ExitCode> {
    init_tracing();
    let matches = cli::Cli::command()
        .name(command_name)
        .bin_name(command_name)
        .get_matches();
    run(cli::Cli::from_arg_matches(&matches)?)
}

pub fn run(cli: cli::Cli) -> Result<ExitCode> {
    let cli::Cli {
        state_dir,
        config: config_path,
        command,
    } = cli;

    let command = match command {
        cli::Commands::Status(args) => {
            return status::run(args, &state_dir, config_path.as_deref());
        }
        // TUI inspection remains available while disabled; actions that would
        // enqueue or spawn inference are gated inside the TUI.
        cli::Commands::Tui(args) => {
            return tui::run(args, &state_dir, config_path.as_deref());
        }
        command => command,
    };

    let config = config::TruthMirrorConfig::load_for_cli(config_path.as_deref(), &state_dir)?;

    match command {
        cli::Commands::InstallHooks(args) => {
            hooks::run(args, &state_dir, config_path.as_deref(), &config)
        }
        // `run_review_command` handles review-run subcommands (status/result/cancel)
        // regardless of `enabled` and only performs inference-backed review when enabled.
        cli::Commands::Review(args) => reviewer::run_review_command(args, &state_dir, &config),
        cli::Commands::Gate(args) => gate::run(args, &state_dir, config_path.as_deref(), &config),
        cli::Commands::Reinject(args) => reinject::run(args, &state_dir, &config),
        cli::Commands::Ledger(args) => ledger::run(args, &state_dir),
        // `run_with_config` allows the human-only waive lane even when disabled;
        // petitions are skipped while disabled.
        cli::Commands::Resolve(args) => {
            resolve::run_with_config(args, &state_dir, &config, config_path.as_deref())
        }
        cli::Commands::Debt(args) => debt::run(args, &state_dir),
        cli::Commands::Tui(_) => unreachable!("tui returns before config load"),
        // Listing materializes historical candidates, while show/render only inspect
        // existing state. Keep the latter available with the rest of the inspection
        // surface; all state-writing memory-skill operations are disabled no-ops.
        cli::Commands::MemorySkill(args)
            if !config.enabled
                && !matches!(
                    &args.command,
                    cli::MemorySkillCommand::Show { .. } | cli::MemorySkillCommand::Render { .. }
                ) =>
        {
            if config.announce_when_disabled {
                println!("truth-mirror: disabled by runtime configuration; skipped memory-skill");
            }
            Ok(ExitCode::SUCCESS)
        }
        cli::Commands::MemorySkill(args) => memory_skill::run(args, &state_dir, &config),
        cli::Commands::Watch(args) => {
            reviewer::run_watch_command(args, &state_dir, config_path.as_deref(), &config)
        }
        cli::Commands::EnsureWatcher(args) => {
            reviewer::run_ensure_watcher_command(args, &state_dir, config_path.as_deref(), &config)
        }
        // Echo is an embedded document read; installing it writes project state.
        cli::Commands::Skills(args)
            if !config.enabled && !matches!(&args.command, cli::SkillsCommand::Echo) =>
        {
            if config.announce_when_disabled {
                println!("truth-mirror: disabled by runtime configuration; skipped skills");
            }
            Ok(ExitCode::SUCCESS)
        }
        cli::Commands::Skills(args) => skills::run(args),
        cli::Commands::Status(_) => unreachable!("status returns before config load"),
        cli::Commands::Uninstall(args) => {
            hooks::run_uninstall(args, &state_dir, config_path.as_deref())
        }
        cli::Commands::HookDispatch(args) => {
            hooks::dispatch(args, &state_dir, config_path.as_deref(), &config)
        }
    }
}