main
John Lauer Publish 0.1.0 55a9481 2d ago
//! The project's flow folder (`<project>/<flow>/`): state.json plus run.jsonl, one line per command
//! with the time, the calling AI thread and what it did. Never rewrite earlier lines.
use serde_json::{json, Value};
use std::io::Write;
use std::path::{Path, PathBuf};

pub fn now() -> String {
    let out = std::process::Command::new("date").args(["-u", "+%Y-%m-%dT%H:%M:%SZ"]).output();
    out.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()).unwrap_or_default()
}

#[allow(dead_code)]
pub struct Project { pub dir: PathBuf, pub flow_dir: PathBuf, pub state: Value }

impl Project {
    /// The nearest folder at or above `start` that holds `<flow>/state.json`.
    pub fn find(start: &Path, flow: &str) -> Option<Project> {
        let mut d = Some(start.to_path_buf());
        while let Some(p) = d {
            let s = p.join(flow).join("state.json");
            if s.is_file() {
                let state = std::fs::read_to_string(&s).ok().and_then(|t| serde_json::from_str(&t).ok()).unwrap_or(json!({}));
                return Some(Project { flow_dir: p.join(flow), dir: p, state });
            }
            d = p.parent().map(Path::to_path_buf);
        }
        None
    }

    pub fn create(dir: &Path, flow: &str, state: Value) -> std::io::Result<Project> {
        let flow_dir = dir.join(flow);
        std::fs::create_dir_all(&flow_dir)?;
        let p = Project { dir: dir.to_path_buf(), flow_dir, state };
        p.save()?;
        Ok(p)
    }

    pub fn save(&self) -> std::io::Result<()> {
        std::fs::write(self.flow_dir.join("state.json"), serde_json::to_string_pretty(&self.state)? + "\n")
    }

    pub fn path(&self, rel: &str) -> PathBuf { self.flow_dir.join(rel) }

    pub fn log(&self, thread: &str, ev: &str, data: Value) {
        let line = json!({"t": now(), "aiThread": thread, "ev": ev, "data": data});
        if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(self.flow_dir.join("run.jsonl")) {
            let _ = writeln!(f, "{line}");
        }
    }
}