ritalin 0.1.0

Proof-carrying completion for AI coding agents. The verification layer for the prompt-request era.
use chrono::Utc;
use serde::Serialize;

use crate::cli::ObligationKind;
use crate::error::AppError;
use crate::ledger::{is_initialized, obligations, obligations::Obligation, state_dir};
use crate::output::{self, Ctx};

#[derive(Serialize)]
struct AddResult {
    id: String,
    claim: String,
    kind: String,
    critical: bool,
    proof_cmd: String,
}

pub fn run(
    ctx: Ctx,
    claim: String,
    proof: String,
    kind: ObligationKind,
    critical: bool,
) -> Result<(), AppError> {
    let cwd = std::env::current_dir()?;
    if !is_initialized(&cwd) {
        return Err(AppError::NotInitialized);
    }
    let dir = state_dir(&cwd);

    let claim = claim.trim().to_string();
    if claim.is_empty() {
        return Err(AppError::InvalidInput("claim cannot be empty".into()));
    }
    let proof = proof.trim().to_string();
    if proof.is_empty() {
        return Err(AppError::InvalidInput("proof command cannot be empty".into()));
    }

    let id = obligations::next_id(&dir)?;
    let ob = Obligation {
        id: id.clone(),
        claim: claim.clone(),
        kind,
        critical,
        proof_cmd: proof.clone(),
        created_at: Utc::now(),
    };
    obligations::append(&dir, &ob)?;

    let result = AddResult {
        id,
        claim,
        kind: kind.to_string(),
        critical,
        proof_cmd: proof,
    };

    output::print_success_or(ctx, &result, |r| {
        use owo_colors::OwoColorize;
        let crit = if r.critical {
            "[critical]".red().to_string()
        } else {
            "[advisory]".dimmed().to_string()
        };
        println!("{} {} {} {}", "+".green().bold(), r.id.bold(), crit, r.claim);
        println!("  proof: {}", r.proof_cmd.dimmed());
    });

    Ok(())
}