opencode-ralph-loop-cli 0.1.0

Scaffolder CLI for OpenCode Ralph Loop plugin — one command setup
Documentation
use assert_cmd::Command;
use tempfile::TempDir;

fn cmd() -> Command {
    Command::cargo_bin("opencode-ralph-loop-cli").unwrap()
}

fn init_dir(dir: &TempDir) {
    cmd()
        .args(["init", "--path", dir.path().to_str().unwrap()])
        .assert()
        .success();
}

#[test]
fn scenario_9_uninstall_removes_only_manifest_files() {
    let dir = TempDir::new().unwrap();
    init_dir(&dir);

    let opencode = dir.path().join(".opencode");
    let custom_file = opencode.join("meu-arquivo-custom.md");
    std::fs::write(&custom_file, b"custom file").unwrap();

    cmd()
        .args(["uninstall", "--path", dir.path().to_str().unwrap()])
        .assert()
        .success();

    assert!(
        !opencode.join("plugins/ralph.ts").exists(),
        "ralph.ts should have been removed"
    );
    assert!(
        custom_file.exists(),
        "custom file should not have been removed"
    );
}

#[test]
fn scenario_keep_state_preserves_ralph_loop_local() {
    let dir = TempDir::new().unwrap();
    init_dir(&dir);

    let opencode = dir.path().join(".opencode");
    let state_file = dir.path().join("ralph-loop.local.md");
    std::fs::write(&state_file, b"loop active").unwrap();

    let manifest_path = opencode.join(".manifest.json");
    let mut manifest: serde_json::Value =
        serde_json::from_str(&std::fs::read_to_string(&manifest_path).unwrap()).unwrap();
    manifest["files"]
        .as_array_mut()
        .unwrap()
        .push(serde_json::json!({
            "path": "ralph-loop.local.md",
            "sha256": "abc",
            "size_bytes": 10,
            "canonical": false,
            "action": "created"
        }));
    std::fs::write(
        &manifest_path,
        serde_json::to_string_pretty(&manifest).unwrap(),
    )
    .unwrap();

    cmd()
        .args([
            "uninstall",
            "--path",
            dir.path().to_str().unwrap(),
            "--keep-state",
        ])
        .assert()
        .success();

    assert!(
        state_file.exists(),
        "ralph-loop.local.md should have been preserved with --keep-state"
    );
}

#[test]
fn scenario_uninstall_dry_run_does_not_remove() {
    let dir = TempDir::new().unwrap();
    init_dir(&dir);

    let opencode = dir.path().join(".opencode");

    cmd()
        .args([
            "uninstall",
            "--path",
            dir.path().to_str().unwrap(),
            "--dry-run",
        ])
        .assert()
        .success();

    assert!(
        opencode.join("plugins/ralph.ts").exists(),
        "dry-run should not remove files"
    );
    assert!(
        opencode.join(".manifest.json").exists(),
        "manifest should not be removed on dry-run"
    );
}