use crate::config;
use crate::paths::Paths;
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
const SEED_PLAYBOOK: &str = include_str!("seed/PLAYBOOK.md");
const SEED_GOAL_SETUP: &str = include_str!("seed/setup.md");
const SEED_GOAL_PLAYBOOK_DAILY: &str = include_str!("seed/playbook-daily.md");
const SEED_SENSOR_TODAY: &str = include_str!("seed/today.sh");
const GITIGNORE: &str = "\
snapshots/
prompts/
runs/
claims/
reports/
.PLAYBOOK.approved
.lock/
.last-tick-hash
.next-interval
.tmux-surfaced
attention.md
tick.log
events.jsonl
cost.jsonl
paused
# worker scratch that can land in the data dir
.ruff_cache/
.pytest_cache/
.mypy_cache/
__pycache__/
";
pub fn ensure_dirs(paths: &Paths) -> Result<()> {
for d in [
paths
.config
.parent()
.unwrap_or(Path::new("."))
.to_path_buf(),
paths.sensors_dir(),
paths.snapshots_dir(),
paths.claims_dir(),
paths.reports_dir(),
paths.runs_dir(),
paths.goals_dir().join("archive"),
paths.prompts_dir(),
] {
fs::create_dir_all(&d).with_context(|| format!("mkdir -p {}", d.display()))?;
}
config::ensure_config(paths)?;
if !paths.playbook().is_file() {
seed_data(paths)?;
}
let gi = paths.data_dir.join(".gitignore");
if !gi.is_file() {
fs::write(&gi, GITIGNORE).with_context(|| format!("writing {}", gi.display()))?;
}
Ok(())
}
fn seed_data(paths: &Paths) -> Result<()> {
fs::write(paths.playbook(), SEED_PLAYBOOK)?;
fs::write(paths.goals_dir().join("setup.md"), SEED_GOAL_SETUP)?;
fs::write(
paths.goals_dir().join("playbook-daily.md"),
SEED_GOAL_PLAYBOOK_DAILY,
)?;
let sensor = paths.sensors_dir().join("today.sh");
fs::write(&sensor, SEED_SENSOR_TODAY)?;
make_executable(&sensor)?;
Ok(())
}
#[cfg(unix)]
fn make_executable(p: &Path) -> Result<()> {
use std::os::unix::fs::PermissionsExt;
let mut perm = fs::metadata(p)?.permissions();
perm.set_mode(0o755);
fs::set_permissions(p, perm)?;
Ok(())
}
#[cfg(not(unix))]
fn make_executable(_p: &Path) -> Result<()> {
Ok(())
}