opencode-ralph-loop-cli 0.1.0

Scaffolder CLI for OpenCode Ralph Loop plugin — one command setup
Documentation
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Action {
    Created,
    Updated,
    Skipped,
    Modified,
    Missing,
    Removed,
}

impl Action {
    pub fn as_str(&self) -> &'static str {
        match self {
            Action::Created => "CREATED",
            Action::Updated => "UPDATED",
            Action::Skipped => "SKIPPED",
            Action::Modified => "MODIFIED",
            Action::Missing => "MISSING",
            Action::Removed => "REMOVED",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileEntry {
    pub path: String,
    pub action: Action,
    pub sha256: String,
    pub expected_sha256: Option<String>,
    pub size_bytes: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Summary {
    pub created: usize,
    pub updated: usize,
    pub skipped: usize,
    pub modified: usize,
    pub missing: usize,
    pub removed: usize,
}

impl Summary {
    pub fn from_entries(entries: &[FileEntry]) -> Self {
        let mut s = Self {
            created: 0,
            updated: 0,
            skipped: 0,
            modified: 0,
            missing: 0,
            removed: 0,
        };
        for e in entries {
            match e.action {
                Action::Created => s.created += 1,
                Action::Updated => s.updated += 1,
                Action::Skipped => s.skipped += 1,
                Action::Modified => s.modified += 1,
                Action::Missing => s.missing += 1,
                Action::Removed => s.removed += 1,
            }
        }
        s
    }

    pub fn has_drift(&self) -> bool {
        self.modified > 0 || self.missing > 0
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Report {
    pub status: String,
    pub command: String,
    pub files: Vec<FileEntry>,
    pub summary: Summary,
    pub cli_version: String,
    pub schema_version: u32,
    pub duration_ms: u64,
    pub plugin_version: String,
}

impl Report {
    pub fn new(command: impl Into<String>, plugin_version: impl Into<String>) -> Self {
        Self {
            status: "ok".to_string(),
            command: command.into(),
            files: Vec::new(),
            summary: Summary {
                created: 0,
                updated: 0,
                skipped: 0,
                modified: 0,
                missing: 0,
                removed: 0,
            },
            cli_version: env!("CARGO_PKG_VERSION").to_string(),
            schema_version: 1,
            duration_ms: 0,
            plugin_version: plugin_version.into(),
        }
    }

    pub fn finalize(&mut self) {
        self.summary = Summary::from_entries(&self.files);
        if self.summary.has_drift() && self.status == "ok" {
            self.status = "drift".to_string();
        }
    }

    pub fn set_error(&mut self, msg: impl Into<String>) {
        self.status = format!("error: {}", msg.into());
    }
}

pub mod json;
pub mod ndjson;
pub mod text;