supercode-cli 0.4.6

supercode — a lightweight, fully-customizable AI coding agent CLI in Rust. Any model via OpenRouter; natively continues Claude Code and Codex sessions.
//! Process-level proof that overdue Claude runtime work is delivered while
//! the resumed interactive CLI is idle, without modifying the source transcript.

use std::fs;
use std::io::{Read, Write};
use std::net::TcpListener;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

fn bin() -> &'static str {
    env!("CARGO_BIN_EXE_supercode")
}

fn temp_home(label: &str) -> PathBuf {
    let nonce = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    let home = std::env::temp_dir().join(format!(
        "supercode-claude-runtime-resume-{label}-{}-{nonce}",
        std::process::id(),
    ));
    fs::create_dir_all(&home).unwrap();
    home
}

fn write_overdue_wakeup(path: &PathBuf) {
    let lines = [
        serde_json::json!({
            "type": "user",
            "sessionId": "runtime-resume",
            "cwd": "/tmp",
            "timestamp": "2020-01-01T00:00:00Z",
            "message": {"role": "user", "content": "initial context"}
        }),
        serde_json::json!({
            "type": "assistant",
            "timestamp": "2020-01-01T00:00:01Z",
            "message": {"role": "assistant", "content": [{
                "type": "tool_use",
                "id": "wake-overdue",
                "name": "ScheduleWakeup",
                "input": {
                    "delaySeconds": 1,
                    "reason": "acceptance wakeup",
                    "prompt": "RUNTIME_WAKEUP_MARKER"
                }
            }]}
        }),
        serde_json::json!({
            "type": "user",
            "timestamp": "2020-01-01T00:00:02Z",
            "message": {"role": "user", "content": [{
                "type": "tool_result",
                "tool_use_id": "wake-overdue",
                "content": "Next wakeup scheduled for 00:00:02 (in 1s)."
            }]}
        }),
    ];
    let text = lines
        .iter()
        .map(serde_json::Value::to_string)
        .collect::<Vec<_>>()
        .join("\n")
        + "\n";
    fs::write(path, text).unwrap();
}

fn write_pending_queue(path: &PathBuf) {
    let lines = [
        serde_json::json!({
            "type": "user",
            "sessionId": "runtime-queue-resume",
            "cwd": "/tmp",
            "timestamp": "2020-01-01T00:00:00Z",
            "message": {"role": "user", "content": "initial context"}
        }),
        serde_json::json!({
            "type": "queue-operation",
            "operation": "enqueue",
            "timestamp": "2020-01-01T00:00:01Z",
            "content": "RUNTIME_QUEUE_MARKER"
        }),
    ];
    let text = lines
        .iter()
        .map(serde_json::Value::to_string)
        .collect::<Vec<_>>()
        .join("\n")
        + "\n";
    fs::write(path, text).unwrap();
}

fn spawn_sse_stub() -> (
    std::net::SocketAddr,
    mpsc::Receiver<String>,
    std::thread::JoinHandle<()>,
) {
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();
    let (tx, rx) = mpsc::channel();
    let handle = std::thread::spawn(move || {
        let (mut socket, _) = listener.accept().unwrap();
        socket
            .set_read_timeout(Some(Duration::from_secs(10)))
            .unwrap();
        let mut request = Vec::new();
        let mut buf = [0_u8; 4096];
        loop {
            let read = socket.read(&mut buf).unwrap();
            if read == 0 {
                break;
            }
            request.extend_from_slice(&buf[..read]);
            let Some(header_end) = request.windows(4).position(|w| w == b"\r\n\r\n") else {
                continue;
            };
            let headers = String::from_utf8_lossy(&request[..header_end]);
            let content_len = headers
                .lines()
                .find_map(|line| {
                    let (name, value) = line.split_once(':')?;
                    name.eq_ignore_ascii_case("content-length")
                        .then(|| value.trim().parse::<usize>().ok())
                        .flatten()
                })
                .unwrap_or(0);
            if request.len() >= header_end + 4 + content_len {
                break;
            }
        }
        tx.send(String::from_utf8_lossy(&request).into_owned())
            .unwrap();
        let sse = "data: {\"choices\":[{\"delta\":{\"content\":\"scheduled done\"}}]}\n\n\
                   data: [DONE]\n\n";
        let response = format!(
            "HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{sse}",
            sse.len()
        );
        socket.write_all(response.as_bytes()).unwrap();
    });
    (addr, rx, handle)
}

#[test]
fn active_resume_delivers_overdue_wakeup_while_idle_and_keeps_source_unchanged() {
    let home = temp_home("wakeup");
    let source = home.join("session.jsonl");
    write_overdue_wakeup(&source);
    let before = fs::read(&source).unwrap();
    let (addr, request_rx, server) = spawn_sse_stub();

    let mut child = Command::new(bin())
        .env("HOME", &home)
        .env("SUPERCODE_HOME", home.join("supercode-home"))
        .env_remove("OPENROUTER_API_KEY")
        .args([
            "--quiet",
            "--api-key",
            "x",
            "--base-url",
            &format!("http://{addr}"),
            "--max-iterations",
            "1",
            "resume",
            source.to_str().unwrap(),
        ])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap();
    let stdin = child.stdin.take().unwrap();
    let request = request_rx
        .recv_timeout(Duration::from_secs(20))
        .expect("scheduler should issue a provider request while stdin remains idle");
    assert!(request.contains("RUNTIME_WAKEUP_MARKER"), "{request}");
    drop(stdin); // EOF lets the interactive loop exit after the scheduled turn.

    let output = child.wait_with_output().unwrap();
    assert!(
        output.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(String::from_utf8_lossy(&output.stdout).contains("scheduled done"));
    assert_eq!(fs::read(&source).unwrap(), before);

    // Unreduced controls only the provider view; it must still create a
    // separate, durable continuation family.  The imported Claude source is
    // byte-identical above while Supercode owns the new sidecar/transcript and
    // acknowledged runtime cursor below.
    let sessions_dir = home.join("supercode-home").join("sessions");
    let files = fs::read_dir(&sessions_dir)
        .unwrap()
        .map(|entry| entry.unwrap().path())
        .collect::<Vec<_>>();
    let sidecar = files
        .iter()
        .find(|path| path.to_string_lossy().ends_with(".sidecar.jsonl"))
        .expect("unreduced imported resume should own a lossless sidecar");
    let transcript = files
        .iter()
        .find(|path| {
            path.extension().and_then(|ext| ext.to_str()) == Some("jsonl")
                && !path.to_string_lossy().ends_with(".sidecar.jsonl")
        })
        .expect("unreduced imported resume should persist a working transcript");
    let runtime = files
        .iter()
        .find(|path| path.to_string_lossy().ends_with(".claude-runtime.json"))
        .expect("unreduced imported resume should persist Claude runtime state");
    let metadata = files
        .iter()
        .find(|path| path.to_string_lossy().ends_with(".meta.json"))
        .expect("unreduced imported resume should persist session metadata");
    assert!(fs::read_to_string(sidecar)
        .unwrap()
        .contains("scheduled done"));
    assert!(fs::read_to_string(transcript)
        .unwrap()
        .contains("scheduled done"));
    let runtime: serde_json::Value =
        serde_json::from_str(&fs::read_to_string(runtime).unwrap()).unwrap();
    let metadata: serde_json::Value =
        serde_json::from_str(&fs::read_to_string(metadata).unwrap()).unwrap();
    assert_eq!(
        metadata.get("reduced").and_then(serde_json::Value::as_bool),
        Some(false)
    );
    assert_eq!(
        runtime
            .pointer("/scheduler/deliveries")
            .and_then(serde_json::Value::as_array)
            .map(Vec::len),
        Some(0),
        "successful scheduled turn should durably acknowledge its delivery"
    );
    server.join().unwrap();
    fs::remove_dir_all(home).ok();
}

#[test]
fn queue_only_active_resume_warns_and_delivers_while_idle() {
    let home = temp_home("queue");
    let source = home.join("queue-session.jsonl");
    write_pending_queue(&source);
    let before = fs::read(&source).unwrap();
    let (addr, request_rx, server) = spawn_sse_stub();

    let mut child = Command::new(bin())
        .env("HOME", &home)
        .env("SUPERCODE_HOME", home.join("supercode-home"))
        .env_remove("OPENROUTER_API_KEY")
        .args([
            "--api-key",
            "x",
            "--base-url",
            &format!("http://{addr}"),
            "--max-iterations",
            "1",
            "resume",
            source.to_str().unwrap(),
        ])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap();
    let stdin = child.stdin.take().unwrap();
    let request = request_rx
        .recv_timeout(Duration::from_secs(20))
        .expect("pending queue should issue a provider request while stdin remains idle");
    assert!(request.contains("RUNTIME_QUEUE_MARKER"), "{request}");
    drop(stdin);

    let output = child.wait_with_output().unwrap();
    assert!(output.status.success(), "{:?}", output.status);
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(stderr.contains("same scheduled or queued work"), "{stderr}");
    assert!(stderr.contains("--paused"), "{stderr}");
    assert_eq!(fs::read(&source).unwrap(), before);

    server.join().unwrap();
    fs::remove_dir_all(home).ok();
}