supercode-cli 0.4.15

supercode — a lightweight, fully-customizable AI coding agent CLI in Rust. Any model via OpenRouter; natively continues Claude Code and Codex sessions.
//! ORCH-6 dev/01+dev/02 acceptance at the CLI: `supercode sessions list`
//! shows the conversation nouns — where a conversation is reached, why it
//! exists, which config home it belongs to — and both filters (`--harness`,
//! `--profile`) select against them.
//!
//! Runs the built binary fully offline against the committed Hermes fixture
//! store (`crates/harness/tests/fixtures/hermes_home/state.db`), reached the
//! way a real install reaches it: `HERMES_HOME`.

use std::path::PathBuf;
use std::process::{Command, Output};

fn bin() -> PathBuf {
    PathBuf::from(env!("CARGO_BIN_EXE_supercode"))
}

fn hermes_home() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../harness/tests/fixtures/hermes_home")
}

fn run(extra: &[&str]) -> Output {
    // An empty SUPERCODE_HOME keeps supercode's own saved-session store out of
    // the picture; the discovered listing must stand on the harness alone.
    let home = std::env::temp_dir().join(format!(
        "supercode-orch6-{}-{}",
        std::process::id(),
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos()
    ));
    std::fs::create_dir_all(&home).unwrap();
    let output = Command::new(bin())
        .env("SUPERCODE_HOME", &home)
        .env("HERMES_HOME", hermes_home())
        .env("NO_COLOR", "1")
        .env_remove("OPENROUTER_API_KEY")
        .args(extra)
        .stdin(std::process::Stdio::null())
        .output()
        .expect("supercode binary runs");
    let _ = std::fs::remove_dir_all(&home);
    output
}

fn stdout(extra: &[&str]) -> String {
    let output = run(extra);
    assert!(
        output.status.success(),
        "`supercode {}` failed: {}",
        extra.join(" "),
        String::from_utf8_lossy(&output.stderr)
    );
    String::from_utf8(output.stdout).unwrap()
}

#[test]
fn sessions_list_shows_surface_trigger_and_profile_for_a_harness() {
    let table = stdout(&["sessions", "list", "--harness", "hermes"]);

    // The Telegram DM: reached on a channel, no profile.
    let dm = table
        .lines()
        .find(|line| line.contains("tg-dm-1"))
        .unwrap_or_else(|| panic!("no `tg-dm-1` row in:\n{table}"));
    assert!(dm.contains("telegram:dm:123456"), "{dm}");
    assert!(dm.contains("channel"), "{dm}");

    // The cron fire: unattended, no surface.
    let fire = table
        .lines()
        .find(|line| line.contains("cron_job42_20260902_120000"))
        .unwrap_or_else(|| panic!("no cron row in:\n{table}"));
    assert!(fire.contains("cron"), "{fire}");

    // The profiled group session: the thread stays on the surface key and the
    // config home is named.
    let coder = table
        .lines()
        .find(|line| line.contains("tg-coder-1"))
        .unwrap_or_else(|| panic!("no `tg-coder-1` row in:\n{table}"));
    assert!(coder.contains("telegram:group:-100777:55"), "{coder}");
    assert!(coder.contains("coder"), "{coder}");

    // A plain ACP session is human-triggered with no surface at all.
    let acp = table
        .lines()
        .find(|line| line.contains("cef97234-e8e8-428a-99ab-e8fff4e7e613"))
        .unwrap_or_else(|| panic!("no ACP row in:\n{table}"));
    assert!(acp.contains("human"), "{acp}");
}

#[test]
fn profile_filter_selects_only_conversations_routed_through_it() {
    let filtered = stdout(&[
        "sessions",
        "list",
        "--harness",
        "hermes",
        "--profile",
        "coder",
    ]);
    assert!(filtered.contains("tg-coder-1"), "{filtered}");
    assert!(!filtered.contains("tg-dm-1"), "{filtered}");
    assert!(!filtered.contains("cron_job42"), "{filtered}");

    // A profile nothing is routed through says so instead of falling back to
    // the unfiltered inventory.
    let empty = stdout(&[
        "sessions",
        "list",
        "--harness",
        "hermes",
        "--profile",
        "nobody",
    ]);
    assert!(!empty.contains("tg-coder-1"), "{empty}");
    assert!(empty.contains("nobody"), "{empty}");
}

#[test]
fn harness_filter_scopes_the_listing_to_that_store() {
    // The hermes store is the only home this test provides; asking for a
    // different harness must not fall through to it.
    let other = stdout(&["sessions", "list", "--harness", "openclaw"]);
    assert!(!other.contains("tg-dm-1"), "{other}");
}

#[test]
fn json_rows_carry_the_same_nouns_as_the_rpc() {
    let rendered = stdout(&[
        "sessions",
        "list",
        "--harness",
        "hermes",
        "--profile",
        "coder",
        "--json",
    ]);
    let rows: serde_json::Value = serde_json::from_str(&rendered).unwrap();
    let row = &rows[0];
    assert_eq!(row["locator"]["session_id"], "tg-coder-1");
    assert_eq!(row["trigger"], "channel");
    assert_eq!(row["profile"], "coder");
    assert_eq!(row["surface"]["platform"], "telegram");
    assert_eq!(row["surface"]["thread_id"], "55");
    assert_eq!(row["cross_surface"]["state"], "pending");
    assert_eq!(row["cross_surface"]["platform"], "discord");
    assert_eq!(row["workspace"]["kind"], "repo");
    assert_eq!(row["workspace"]["value"], "/workspace/project");
}

#[test]
fn plain_sessions_list_still_lists_supercode_saved_sessions() {
    // No `--harness`/`--profile` means the pre-ORCH-6 inventory, untouched.
    let table = stdout(&["sessions", "list"]);
    assert!(table.contains("no saved sessions yet"), "{table}");
    assert!(!table.contains("tg-dm-1"), "{table}");
}