remem-ai 0.6.93

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use std::{fs, path::Component, path::Path, process::Command};

use serde_json::Value;
use sha2::{Digest, Sha256};

const PRODUCTION_PATHSPEC_CONTRACT: &str = "eval/production-input-pathspec-v1.json";

fn main() {
    let (contract, paths) = production_pathspec();
    for path in &paths {
        println!("cargo:rerun-if-changed={path}");
    }
    emit_git_rerun_paths();

    if let Some(commit) = build_git_stdout(&["rev-parse", "--verify", "HEAD^{commit}"]) {
        println!("cargo:rustc-env=REMEM_BUILD_GIT_SHA={commit}");
    }
    if let Some(dirty) = git_dirty() {
        println!("cargo:rustc-env=REMEM_BUILD_SOURCE_DIRTY={dirty}");
    }
    println!("cargo:rustc-env=REMEM_BUILD_PRODUCTION_PATHSPEC_JSON={contract}");
    println!(
        "cargo:rustc-env=REMEM_BUILD_PRODUCTION_PATHSPEC_SHA256={:x}",
        Sha256::digest(contract.as_bytes())
    );
    if let Some(tree) = production_input_tree_sha256(&paths) {
        println!("cargo:rustc-env=REMEM_BUILD_PRODUCTION_INPUT_TREE_SHA256={tree}");
    }
}

fn production_pathspec() -> (String, Vec<String>) {
    let bytes = fs::read(PRODUCTION_PATHSPEC_CONTRACT)
        .unwrap_or_else(|error| panic!("read {PRODUCTION_PATHSPEC_CONTRACT}: {error}"));
    let value: Value = serde_json::from_slice(&bytes)
        .unwrap_or_else(|error| panic!("parse {PRODUCTION_PATHSPEC_CONTRACT}: {error}"));
    assert_eq!(
        value["schema_version"], 1,
        "unsupported production pathspec"
    );
    assert_eq!(
        value["contract_id"], "remem-production-input-pathspec-v1",
        "unexpected production pathspec contract"
    );
    let paths = value["paths"]
        .as_array()
        .expect("production pathspec paths must be an array")
        .iter()
        .map(|path| {
            path.as_str()
                .filter(|path| !path.is_empty())
                .expect("production pathspec entries must be non-empty strings")
                .to_string()
        })
        .collect::<Vec<_>>();
    assert!(!paths.is_empty(), "production pathspec must not be empty");
    assert!(
        paths.iter().all(|path| {
            let path = Path::new(path);
            !path.is_absolute()
                && !path
                    .components()
                    .any(|component| matches!(component, Component::ParentDir))
        }),
        "production pathspec entries must be repository-relative"
    );
    assert!(
        paths
            .iter()
            .any(|path| path == PRODUCTION_PATHSPEC_CONTRACT),
        "production pathspec must include its own contract"
    );
    let mut unique = paths.clone();
    unique.sort();
    unique.dedup();
    assert_eq!(
        unique.len(),
        paths.len(),
        "production pathspec has duplicates"
    );
    (
        serde_json::to_string(&value).expect("serialize production pathspec"),
        paths,
    )
}

fn emit_git_rerun_paths() {
    for args in [
        &["rev-parse", "--git-path", "HEAD"][..],
        &["rev-parse", "--git-path", "index"][..],
    ] {
        if let Some(path) = build_git_stdout(args) {
            println!("cargo:rerun-if-changed={path}");
        }
    }
    if let Some(reference) = build_git_stdout(&["symbolic-ref", "-q", "HEAD"]) {
        if let Some(path) = build_git_stdout(&["rev-parse", "--git-path", &reference]) {
            println!("cargo:rerun-if-changed={path}");
        }
    }
}

fn build_git_stdout(args: &[&str]) -> Option<String> {
    let output = Command::new("git").args(args).output().ok()?;
    if !output.status.success() {
        return None;
    }
    let value = String::from_utf8_lossy(&output.stdout).trim().to_string();
    (!value.is_empty()).then_some(value)
}

fn git_dirty() -> Option<bool> {
    let output = Command::new("git")
        .args(["status", "--porcelain", "--untracked-files=normal"])
        .output()
        .ok()?;
    output.status.success().then_some(!output.stdout.is_empty())
}

fn production_input_tree_sha256(paths: &[String]) -> Option<String> {
    let mut args = vec!["ls-files", "-s", "--"];
    args.extend(paths.iter().map(String::as_str));
    let output = Command::new("git").args(args).output().ok()?;
    if !output.status.success() || output.stdout.is_empty() {
        return None;
    }
    Some(format!("{:x}", Sha256::digest(&output.stdout)))
}