use std::path::{Path, PathBuf};
use std::process::ExitCode;
use anyhow::{anyhow, Context, Result};
use crate::cli::InitArgs;
const EXAMPLE_TOML: &str = r#"# klasp.toml โ generated by `klasp init`
# Docs: https://github.com/klasp-dev/klasp
# Verify this install: run `klasp doctor`
version = 1
[gate]
# Agent surfaces that klasp intercepts. v0.3 ships three: claude_code, codex, aider.
# `klasp install --agent all` walks this list. Comment out any you don't use.
agents = ["claude_code", "codex", "aider"]
# Gate policy: "any_fail" blocks the agent if any check fails.
policy = "any_fail"
# [[checks]]
# name = "lint"
# triggers = [{ on = ["commit"] }]
# timeout_secs = 60
# [checks.source]
# type = "shell"
# command = "ruff check ." # or: cargo clippy --all-targets -- -D warnings
# [[checks]]
# name = "test"
# triggers = [{ on = ["commit"] }]
# [checks.source]
# type = "shell"
# command = "pytest -q" # or: cargo test --workspace
"#;
pub fn run(args: &InitArgs) -> ExitCode {
match try_run(args) {
Ok(path) => {
println!("wrote {}", path.display());
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("klasp init: {e:#}");
ExitCode::FAILURE
}
}
}
fn try_run(args: &InitArgs) -> Result<PathBuf> {
let repo_root = crate::cmd::install::resolve_repo_root(None).context("resolving repo root")?;
let target = repo_root.join("klasp.toml");
if target.exists() && !args.force {
return Err(anyhow!(
"klasp.toml already exists at {}; pass --force to overwrite",
target.display()
));
}
atomic_write_text(&target, EXAMPLE_TOML)
.with_context(|| format!("writing klasp.toml to {}", target.display()))?;
Ok(target)
}
fn atomic_write_text(path: &Path, contents: &str) -> std::io::Result<()> {
use std::io::Write;
let dir = path.parent().unwrap_or_else(|| Path::new("."));
let mut tf = tempfile::NamedTempFile::new_in(dir)?;
tf.write_all(contents.as_bytes())?;
tf.flush()?;
tf.persist(path).map_err(|e| e.error)?;
Ok(())
}