#![allow(dead_code)]
use std::io;
use std::path::Path;
use crate::volume;
const BOOTLOADER_CONTENT: &str = "\
# CLAUDE.md
> Bootloader — loads the distro's agent briefing.
@.omne/dist/AGENTS.md
";
const DOCS_INDEX_BASELINE: &str = "\
# Volume docs
Index for this volume's knowledge base. Populate `raw/` with verbatim
source material, `inter/` with intermediate synthesis, and `wiki/`
with curated long-form notes.
";
const GITIGNORE_TEMPLATE: &str = include_str!("../templates/gitignore-template");
pub fn create_volume_dirs(root: &Path) -> io::Result<()> {
std::fs::create_dir_all(volume::cfg_dir(root))?;
let docs = volume::docs_dir(root);
std::fs::create_dir_all(docs.join("raw"))?;
std::fs::create_dir_all(docs.join("inter"))?;
std::fs::create_dir_all(docs.join("wiki"))?;
std::fs::create_dir_all(volume::var_dir(root))?;
std::fs::create_dir_all(volume::wt_dir(root))?;
Ok(())
}
pub fn write_docs_baseline(root: &Path) -> io::Result<()> {
let index = volume::docs_baseline(root);
if !index.exists() {
std::fs::write(&index, DOCS_INDEX_BASELINE)?;
}
for subdir in ["raw", "inter", "wiki"] {
let keep = volume::docs_dir(root).join(subdir).join(".gitkeep");
if !keep.exists() {
std::fs::write(&keep, "")?;
}
}
Ok(())
}
pub fn write_bootloader(root: &Path) -> io::Result<()> {
std::fs::write(root.join("CLAUDE.md"), BOOTLOADER_CONTENT)
}
pub fn write_omne_readme(root: &Path, content: &str) -> io::Result<()> {
std::fs::write(root.join(".omne").join("omne.md"), content)
}
pub fn write_gitignore(root: &Path) -> io::Result<()> {
let path = root.join(".gitignore");
if !path.exists() {
return std::fs::write(&path, GITIGNORE_TEMPLATE);
}
let existing = std::fs::read_to_string(&path)?;
let required: Vec<&str> = GITIGNORE_TEMPLATE
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect();
let missing: Vec<&str> = required
.into_iter()
.filter(|needle| !existing.lines().any(|l| l.trim() == *needle))
.collect();
if missing.is_empty() {
return Ok(());
}
let mut out = existing;
if !out.ends_with('\n') {
out.push('\n');
}
if !out.contains("# omne") {
out.push_str("\n# omne\n");
}
for line in missing {
out.push_str(line);
out.push('\n');
}
std::fs::write(&path, out)
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn creates_lib_and_runtime_dirs() {
let tmp = TempDir::new().unwrap();
create_volume_dirs(tmp.path()).unwrap();
let omne = tmp.path().join(".omne");
assert!(omne.join("lib").join("cfg").is_dir());
assert!(omne.join("lib").join("docs").join("raw").is_dir());
assert!(omne.join("lib").join("docs").join("inter").is_dir());
assert!(omne.join("lib").join("docs").join("wiki").is_dir());
assert!(omne.join("var").is_dir());
assert!(omne.join("wt").is_dir());
}
#[test]
fn does_not_create_legacy_cfg_or_log_at_root() {
let tmp = TempDir::new().unwrap();
create_volume_dirs(tmp.path()).unwrap();
assert!(!tmp.path().join(".omne/cfg").exists());
assert!(!tmp.path().join(".omne/log").exists());
}
#[test]
fn idempotent() {
let tmp = TempDir::new().unwrap();
create_volume_dirs(tmp.path()).unwrap();
create_volume_dirs(tmp.path()).unwrap();
assert!(tmp.path().join(".omne/var").is_dir());
}
#[test]
fn stamps_index_and_gitkeeps() {
let tmp = TempDir::new().unwrap();
create_volume_dirs(tmp.path()).unwrap();
write_docs_baseline(tmp.path()).unwrap();
let docs = tmp.path().join(".omne/lib/docs");
assert!(docs.join("index.md").is_file());
assert!(docs.join("raw/.gitkeep").is_file());
assert!(docs.join("inter/.gitkeep").is_file());
assert!(docs.join("wiki/.gitkeep").is_file());
}
#[test]
fn docs_baseline_preserves_existing_index() {
let tmp = TempDir::new().unwrap();
create_volume_dirs(tmp.path()).unwrap();
let index = tmp.path().join(".omne/lib/docs/index.md");
std::fs::write(&index, "user authored\n").unwrap();
write_docs_baseline(tmp.path()).unwrap();
assert_eq!(std::fs::read_to_string(&index).unwrap(), "user authored\n");
}
#[test]
fn bootloader_imports_dist_agents() {
let tmp = TempDir::new().unwrap();
write_bootloader(tmp.path()).unwrap();
let content = std::fs::read_to_string(tmp.path().join("CLAUDE.md")).unwrap();
assert!(
content.contains("@.omne/dist/AGENTS.md"),
"bootloader must import dist/AGENTS.md: {content}"
);
assert!(
!content.contains("MANIFEST.md"),
"bootloader must not retain legacy MANIFEST.md hop: {content}"
);
}
#[test]
fn writes_omne_readme_file() {
let tmp = TempDir::new().unwrap();
std::fs::create_dir_all(tmp.path().join(".omne")).unwrap();
write_omne_readme(tmp.path(), "test content").unwrap();
let path = tmp.path().join(".omne").join("omne.md");
assert!(path.is_file());
assert_eq!(std::fs::read_to_string(path).unwrap(), "test content");
}
#[test]
fn writes_fresh_gitignore_when_absent() {
let tmp = TempDir::new().unwrap();
write_gitignore(tmp.path()).unwrap();
let content = std::fs::read_to_string(tmp.path().join(".gitignore")).unwrap();
assert!(content.contains(".omne/wt/"));
assert!(content.contains(".omne/var/runs/*/nodes/"));
assert!(content.contains(".omne/var/.ulid-last"));
}
#[test]
fn appends_missing_entries_to_existing_gitignore() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join(".gitignore");
std::fs::write(&path, "target/\nnode_modules/\n").unwrap();
write_gitignore(tmp.path()).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("target/"), "existing entries preserved");
assert!(content.contains(".omne/wt/"), "new entries appended");
assert!(content.contains("# omne"), "section marker added");
}
#[test]
fn gitignore_is_idempotent() {
let tmp = TempDir::new().unwrap();
write_gitignore(tmp.path()).unwrap();
let first = std::fs::read_to_string(tmp.path().join(".gitignore")).unwrap();
write_gitignore(tmp.path()).unwrap();
let second = std::fs::read_to_string(tmp.path().join(".gitignore")).unwrap();
assert_eq!(first, second, "repeat invocations must not mutate");
}
}