soft3/tru/cli/main.rs

//! `tru` โ€” the convergence VM command line.
//!
//! Reads `.cyb` containers (`.graph` / `.vocab` / `.model`) and runs the
//! focusing engine over a graph: ฯ†*, cyberank, syntropy, telemetry.

use std::io::{self, IsTerminal};
use std::path::{Path, PathBuf};

use anyhow::Result;
use clap::{Parser, Subcommand};

use tru::focusing::{self, FocusingGraph, FocusingParams, Link};
use tru::graph::frontmatter;
use tru::vocab::Vocab;
use tru::{Graph, Model};

// โ”€โ”€ color โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// ANSI only when stdout is a terminal โ€” piped output stays clean.

fn tty() -> bool {
    io::stdout().is_terminal()
}

fn paint(code: &str, s: &str) -> String {
    if tty() {
        format!("\x1b[{code}m{s}\x1b[0m")
    } else {
        s.to_string()
    }
}

fn dim(s: &str) -> String {
    paint("90", s)
}
fn cyan(s: &str) -> String {
    paint("36", s)
}
fn green(s: &str) -> String {
    paint("32", s)
}
fn yellow(s: &str) -> String {
    paint("33", s)
}
fn bold(s: &str) -> String {
    paint("1", s)
}

/// The rainbow ANSI-Shadow wordmark โ€” printed only on a terminal.
const LOGO: &str = "\
\x1b[31mโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—   โ–ˆโ–ˆโ•—\x1b[0m
\x1b[33mโ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•‘\x1b[0m
\x1b[32m   โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•‘\x1b[0m
\x1b[36m   โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•‘\x1b[0m
\x1b[34m   โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•\x1b[0m
\x1b[35m   โ•šโ•โ•   โ•šโ•โ•  โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• \x1b[0m";

/// Banner: wordmark + tagline + the field/kernel parameters, hemera-style.
fn banner() -> String {
    if !tty() {
        return String::new();
    }
    format!(
        "{LOGO}\n{tag}\n{params}\n",
        tag = paint("37", "    the convergence engine"),
        params = dim("\n    Goldilocks field ยท p = 2^64 - 2^32 + 1\n    \
             tri-kernel ยท diffusion + springs + heat\n    \
             coupled iteration ยท fixed-point ยท converges to ฯ†*\n"),
    )
}

fn help() {
    println!(
        "{}\n  {}   {}\n  {}   {}\n  {}   {}\n  {}   {}\n  {}   {}\n  {}   {}\n\n  {}",
        dim("commands"),
        bold("inspect <file> "),
        dim("summarize any .cyb: type, name, sections"),
        bold("focus   <graph>"),
        dim("run the tri-kernel: cyberank ยท syntropy ยท telemetry"),
        bold("impulse <graph>"),
        dim("ฮ”ฯ†* of a new link: the directed syntropy gain ฮ”ฯ†โบ"),
        bold("compile <graph>"),
        dim("CT-0: derive a .model's architecture from the graph"),
        bold("vocab   <file> "),
        dim("a .vocab dictionary + self-consistency"),
        bold("model   <file> "),
        dim("a .model checkpoint: tensors ยท config ยท particle"),
        dim("focus is the heart โ€” it converges a graph to ฯ†* in the Goldilocks field."),
    );
}

// โ”€โ”€ cli โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

#[derive(Parser)]
#[command(name = "tru", version, about = "convergence VM: .graph โ†’ ฯ†* โ†’ .model")]
struct Cli {
    #[command(subcommand)]
    cmd: Cmd,
}

#[derive(Subcommand)]
enum Cmd {
    /// Summarize any `.cyb` container: type, name, sections.
    Inspect { path: PathBuf },
    /// Run the tri-kernel over a `.graph`: cyberank, syntropy, telemetry.
    /// With no path, reads the local snapshot ($TRU_GRAPH or ~/cyb/my.graph).
    Focus {
        path: Option<PathBuf>,
        #[arg(short, long, default_value_t = 20)]
        top: usize,
    },
    /// Compute the impulse ฮ”ฯ†* of one new link on a `.graph`: the directed
    /// syntropy gain ฮ”ฯ†โบ (the reward primitive) and the sparse focus shift.
    Impulse {
        path: PathBuf,
        /// Source particle, by hex prefix (matched against the graph).
        #[arg(long)]
        from: String,
        /// Target particle, by hex prefix.
        #[arg(long)]
        to: String,
        /// Stake on the new link (smallest units).
        #[arg(long, default_value_t = 1000)]
        stake: u128,
        #[arg(short, long, default_value_t = 10)]
        top: usize,
    },
    /// Compile a `.graph` into a `.model` (CT-0): derive the architecture from
    /// the graph spectrum, package config/vocab/norms.
    Compile {
        path: PathBuf,
        #[arg(short, long, default_value = "out.model")]
        out: PathBuf,
    },
    /// Summarize a `.vocab` dictionary and check its self-consistency.
    Vocab { path: PathBuf },
    /// Summarize a `.model` checkpoint: tensors, config, particle.
    Model { path: PathBuf },
}

fn main() -> Result<()> {
    let args: Vec<String> = std::env::args().skip(1).collect();
    if args.is_empty() || matches!(args[0].as_str(), "help" | "--help" | "-h") {
        print!("{}", banner());
        help();
        return Ok(());
    }
    match Cli::parse().cmd {
        Cmd::Inspect { path } => inspect(&path),
        Cmd::Focus { path, top } => focus(path, top),
        Cmd::Impulse {
            path,
            from,
            to,
            stake,
            top,
        } => impulse(&path, &from, &to, stake, top),
        Cmd::Compile { path, out } => compile(&path, &out),
        Cmd::Vocab { path } => vocab(&path),
        Cmd::Model { path } => model(&path),
    }
}

fn kv(key: &str, val: &str) -> String {
    format!("{} {}", dim(key), val)
}

/// The default local cybergraph snapshot: `$TRU_GRAPH`, else `~/cyb/my.graph`.
/// `~/cyb` is visible, not dotfile-hidden โ€” nothing about a neuron's own
/// graph needs hiding from its owner.
fn default_graph() -> PathBuf {
    if let Ok(p) = std::env::var("TRU_GRAPH") {
        return PathBuf::from(p);
    }
    let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
    Path::new(&home).join("cyb").join("my.graph")
}

/// Resolve the graph path: the one given, or the local default. Errors with
/// guidance if the default is missing.
fn resolve_graph(path: Option<PathBuf>) -> Result<PathBuf> {
    match path {
        Some(p) => Ok(p),
        None => {
            let p = default_graph();
            if p.exists() {
                Ok(p)
            } else {
                anyhow::bail!(
                    "no graph given and none at {}\n  {}\n  {}",
                    p.display(),
                    "pass a path:      tru focus <file.graph>",
                    "or set the default: export TRU_GRAPH=/path/to/snapshot.graph"
                )
            }
        }
    }
}

fn inspect(path: &Path) -> Result<()> {
    let bytes = std::fs::read(path)?;
    let (fm_str, body) = frontmatter::split(&bytes)?;
    let fm = frontmatter::parse(fm_str)?;
    let sections = frontmatter::index_sections(&bytes, body, &fm.files)?;

    println!(
        "{}  {}",
        bold(&fm.cyb.name),
        dim(&format!("[{}]", fm.cyb.types.join(", ")))
    );
    println!(
        "{}",
        dim(&format!(
            "{} bytes ยท {} sections",
            bytes.len(),
            fm.files.len()
        ))
    );
    for f in &fm.files {
        let sz = sections.get(&f.name).map(|&(s, e)| e - s).unwrap_or(0);
        println!(
            "  {} {}  {}",
            cyan(&format!("{:<11}", f.name)),
            dim(&format!("{:<8}", f.format)),
            yellow(&format!("{sz} B"))
        );
    }
    Ok(())
}

fn focus(path: Option<PathBuf>, top: usize) -> Result<()> {
    let path = resolve_graph(path)?;
    let g = Graph::open(&path)?;
    let n_links = g.cyberlinks()?.count();
    // Preserve the real neuron and valence from each record so karma wiring is a
    // one-line change; karma and ICBS price arrive from bbg, absent here, so this
    // pass runs stake-only (Context::none(), neutral price = 1).
    let links = g.cyberlinks()?.map(|cl| Link {
        neuron: cl.neuron,
        from: cl.from,
        to: cl.to,
        amount: cl.amount,
        valence: cl.valence,
        price: tru::arithmetic::Fx::ONE,
    });
    let fg = FocusingGraph::build(links, &focusing::Context::none());
    let params = FocusingParams::default();
    let result = tru::compute_focusing(&fg, &params);
    let tel = focusing::telemetry(&fg, &result, &params);

    println!("{} {}", green("focus"), bold(g.name()));
    let sep = dim(" ยท ");
    println!(
        "  {}{sep}{}",
        kv("particles", &yellow(&fg.n().to_string())),
        kv("cyberlinks", &yellow(&n_links.to_string()))
    );
    println!(
        "  {}{sep}{}",
        kv(
            "syntropy J",
            &yellow(&format!("{:.4}", tel.syntropy.to_f64()))
        ),
        kv(
            "entropy H",
            &yellow(&format!("{:.4}", tel.entropy.to_f64()))
        )
    );
    println!(
        "  {}{sep}{}{sep}{}",
        kv("ฮบ", &yellow(&format!("{:.3}", tel.kappa.to_f64()))),
        kv("ฮปโ‚‚", &yellow(&format!("{:.3}", tel.lambda_2.to_f64()))),
        kv("T(ฮต)", &yellow(&tel.steps.to_string()))
    );

    // Spectral embedding โ€” the (x, y) mir reads for layout (Fiedler + ฮปโ‚ƒ).
    let emb = fg.embedding(2, 200);
    let has_xy = emb.k >= 2;

    let mut ranked: Vec<(usize, f64)> = result
        .focus
        .iter()
        .map(|x| x.to_f64())
        .enumerate()
        .collect();
    ranked.sort_by(|a, b| b.1.total_cmp(&a.1));
    let head = if has_xy {
        "cyberank ฯ†*(p) ยท position (x,y)"
    } else {
        "cyberank ฯ†*(p)"
    };
    println!(
        "\n{}",
        dim(&format!("{head} โ€” top {}", top.min(ranked.len())))
    );
    for (idx, phi) in ranked.iter().take(top) {
        let pos = if has_xy {
            dim(&format!(
                "  ({:+.4}, {:+.4})",
                emb.coords[*idx][0].to_f64(),
                emb.coords[*idx][1].to_f64()
            ))
        } else {
            String::new()
        };
        println!(
            "  {}  {}{}",
            cyan(&hex8(fg.node_id(*idx))),
            yellow(&format!("{phi:.6}")),
            pos
        );
    }
    Ok(())
}

/// Read a graph's cyberlinks as focusing `Link`s (stake-only: neutral price,
/// no karma source wired yet).
fn read_links(g: &Graph) -> Result<Vec<Link>> {
    Ok(g.cyberlinks()?
        .map(|cl| Link {
            neuron: cl.neuron,
            from: cl.from,
            to: cl.to,
            amount: cl.amount,
            valence: cl.valence,
            price: tru::arithmetic::Fx::ONE,
        })
        .collect())
}

/// Resolve a hex prefix to a unique particle among `parts`.
fn resolve_particle(parts: &span>; 32, prefix: &str) -> Result<[u8; 32]> {
    let p = prefix.trim().trim_end_matches('โ€ฆ').to_lowercase();
    let hits: Vec<[u8; 32]> = parts
        .iter()
        .filter(|h| hex(*h).starts_with(&p))
        .copied()
        .collect();
    match hits.as_slice() {
        [one] => Ok(*one),
        [] => anyhow::bail!("no particle matches prefix '{prefix}'"),
        many => anyhow::bail!(
            "prefix '{prefix}' is ambiguous โ€” {} particles match",
            many.len()
        ),
    }
}

fn impulse(path: &Path, from: &str, to: &str, stake: u128, top: usize) -> Result<()> {
    let g = Graph::open(path)?;
    let base = read_links(&g)?;

    // Particle universe: every endpoint in the base graph.
    let mut parts: Vec<[u8; 32]> = Vec::new();
    for l in &base {
        for h in [l.from, l.to] {
            if !parts.contains(&h) {
                parts.push(h);
            }
        }
    }
    let from_h = resolve_particle(&parts, from)?;
    let to_h = resolve_particle(&parts, to)?;

    // The signal: one new link, authored by the source, at neutral market price.
    let batch = vec![Link {
        neuron: from_h,
        from: from_h,
        to: to_h,
        amount: stake,
        valence: 1,
        price: tru::arithmetic::Fx::ONE,
    }];
    let params = FocusingParams::default();
    let imp = tru::impulse(
        &base,
        &batch,
        &focusing::Context::none(),
        &params,
        params.epsilon,
    );

    println!(
        "{} {} {} {}",
        green("impulse"),
        cyan(&hex8(&from_h)),
        dim("โ†’"),
        cyan(&hex8(&to_h))
    );
    let sep = dim(" ยท ");
    println!("  {}", kv("stake", &yellow(&stake.to_string())));
    println!(
        "  {}{sep}{}",
        kv(
            "ฮ”ฯ†โบ reward",
            &yellow(&format!("{:.6}", imp.directed.to_f64()))
        ),
        kv("ฮ”J", &yellow(&format!("{:+.6}", imp.delta_j.to_f64())))
    );
    println!(
        "  {}{sep}{}{sep}{}",
        kv(
            "entropy drop",
            &yellow(&format!("{:+.6}", imp.entropy_drop.to_f64()))
        ),
        kv(
            "discovery",
            &yellow(&format!("{:+.6}", imp.discovery.to_f64()))
        ),
        kv("โ€–ฮ”ฯ†*โ€–โ‚", &yellow(&format!("{:.6}", imp.norm_l1.to_f64())))
    );

    let mut d = imp.delta.clone();
    d.sort_by(|a, b| b.1.to_f64().abs().total_cmp(&a.1.to_f64().abs()));
    println!(
        "\n{}",
        dim(&format!("ฮ”ฯ†*(p) โ€” top {} by |shift|", top.min(d.len())))
    );
    for (pid, dv) in d.iter().take(top) {
        let v = dv.to_f64();
        let arrow = if v >= 0.0 {
            green("โ–ฒ")
        } else {
            paint("31", "โ–ผ")
        };
        println!(
            "  {} {}  {}",
            cyan(&hex8(pid)),
            arrow,
            yellow(&format!("{v:+.6}"))
        );
    }
    Ok(())
}

fn compile(path: &Path, out: &Path) -> Result<()> {
    let g = Graph::open(path)?;
    let model = tru::pass::compile(&g)?;
    model.write(out)?;

    let sep = dim(" ยท ");
    println!(
        "{} {} {} {}",
        green("compile"),
        bold(g.name()),
        dim("โ†’"),
        bold(&model.name)
    );
    // Surface the derived architecture from the config section.
    let field = |key: &str| -> String {
        model
            .config
            .lines()
            .find(|l| l.trim_start().starts_with(key))
            .and_then(|l| l.split('=').nth(1))
            .map(|v| v.trim().to_string())
            .unwrap_or_default()
    };
    println!(
        "  {}{sep}{}{sep}{}",
        kv("d", &yellow(&field("hidden_size"))),
        kv("h", &yellow(&field("num_attention_heads"))),
        kv("L", &yellow(&field("num_hidden_layers"))),
    );
    println!(
        "  {}{sep}{}",
        kv("particles", &yellow(&field("vocab_size"))),
        kv("params", &yellow(&field("parameters"))),
    );
    println!(
        "  {}{sep}{}",
        kv("tensors", &yellow(&model.tensors.len().to_string())),
        kv("particle", &cyan(&hex8(&model.particle())))
    );
    println!("  {} {}", dim("wrote"), out.display());
    Ok(())
}

fn vocab(path: &Path) -> Result<()> {
    let v = Vocab::read(path)?;
    let inline = v.entries.iter().filter(|e| !e.data.is_empty()).count();
    println!("{} {}", green("vocab"), bold(&v.name));
    println!(
        "  {}",
        kv(
            "entries",
            &format!(
                "{} {}",
                yellow(&v.entries.len().to_string()),
                dim(&format!("({inline} with data)"))
            )
        )
    );
    println!("  {}", kv("particle", &cyan(&hex(&v.particle()))));
    match v.verify() {
        Ok(()) => println!("  {} {}", dim("self-consistency"), green("OK")),
        Err(e) => println!(
            "  {} {}",
            dim("self-consistency"),
            paint("31", &format!("FAIL: {e}"))
        ),
    }
    Ok(())
}

fn model(path: &Path) -> Result<()> {
    let m = Model::read(path)?;
    println!("{} {}", green("model"), bold(&m.name));
    println!("  {}", kv("particle", &cyan(&hex(&m.particle()))));
    println!("  {}", kv("tensors", &yellow(&m.tensors.len().to_string())));
    for t in &m.tensors {
        println!(
            "    {} {} {} {}",
            cyan(&format!("{:<34}", t.name)),
            dim(&format!("{:?}", t.shape)),
            yellow(&format!("{:?}", t.encoding)),
            dim(&format!("{} values", t.data.len()))
        );
    }
    if !m.config.is_empty() {
        println!("  {}", dim("config"));
        for line in m.config.lines().take(8) {
            println!("    {}", dim(line));
        }
    }
    Ok(())
}

fn hex(b: &[u8]) -> String {
    b.iter().map(|x| format!("{x:02x}")).collect()
}

fn hex8(b: &[u8; 32]) -> String {
    format!(
        "{}โ€ฆ",
        b[..8]
            .iter()
            .map(|x| format!("{x:02x}"))
            .collect::<String>()
    )
}

Homonyms

cyberia/src/main.rs
cyb/src-tauri/src/main.rs
neural/rune/cli/main.rs
cyb/cyb-boot/src/main.rs
soft3/glia/import/main.rs
warriors/trisha/cli/main.rs
neural/trident/src/main.rs
cyb/optica/src/main.rs
soft3/nox/cli/main.rs
neural/rs/link/src/main.rs
neural/rs/macho-linker/src/main.rs
soft3/zheng/cli/src/main.rs
cyb/cyb/cyb-portal/src/main.rs
neural/rs/pure-rust-check/src/main.rs
cyb/cyb/cyb-ui/src/main.rs
soft3/foculus/src/bin/main.rs
neural/eidos/cli/src/main.rs
soft3/glia/run/cli/main.rs
soft3/radio/iroh-relay/src/main.rs
cyb/cyb/cyb-shell/src/main.rs
neural/rs/rsc/src/main.rs
soft3/cybergraph/cli/src/main.rs
soft3/bbg/cli/src/main.rs
soft3/radio/radio-cli/src/main.rs
soft3/radio/particle/src/main.rs
soft3/hemera/cli/src/main.rs
soft3/radio/iroh-dns-server/src/main.rs
soft3/strata/trop/cli/src/main.rs
cyb/honeycrisp/rane/src/probe/main.rs
soft3/strata/kuro/cli/src/main.rs
soft3/strata/genies/cli/src/main.rs
bootloader/go-cyber/mcp/rust/src/main.rs
cyb/wysm/crates/cli/src/main.rs
soft3/strata/nebu/cli/src/main.rs
cyb/honeycrisp/acpu/src/probe/main.rs
neural/inf/rs/cli/src/main.rs
soft3/strata/jali/cli/src/main.rs
cyb/honeycrisp/unimem/experiments/hyp_probe/src/main.rs
cyb/honeycrisp/unimem/experiments/iosurface_probe/src/main.rs
cyb/honeycrisp/unimem/experiments/dext_iosurface_pa/client/src/main.rs
cyb/honeycrisp/unimem/experiments/dext_contiguous_alloc/client/src/main.rs

Graph