use std::path::{Path, PathBuf};
const SENTINEL_START: &str = "<!-- >>> burn agent (managed) >>> -->";
const SENTINEL_END: &str = "<!-- <<< burn agent (managed) <<< -->";
const BLOCK_BODY: &str = "## JavaScript execution\n\
Run ALL JavaScript/TypeScript SEALED in the burn sandbox - never invoke `node`/`npx`/`tsx` directly:\n\
`burn --sandbox node script.js`, `burn --sandbox npm test`, inline: `burn --sandbox -e '<code>'`.\n\
Sealed means no network, no filesystem, no env access: write code that conforms to that\n\
(pure computation over explicit inputs). Only when a capability is genuinely required,\n\
grant it narrowly per run, e.g. `burn --sandbox --allow-net=api.example.com node app.js`\n\
or `--allow-fs-read=./data`. Never use `--allow-all`.";
fn context_path(key: &str, user: bool) -> Option<PathBuf> {
let home = || PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| ".".to_string()));
let cwd = || std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
match (key, user) {
("claude-code", true) => Some(home().join(".claude").join("CLAUDE.md")),
("claude-code", false) => Some(cwd().join("CLAUDE.md")),
("gemini" | "antigravity", true) => Some(home().join(".gemini").join("GEMINI.md")),
("gemini" | "antigravity", false) => Some(cwd().join("GEMINI.md")),
("codex", true) => Some(home().join(".codex").join("AGENTS.md")),
("codex", false) => Some(cwd().join("AGENTS.md")),
_ => None,
}
}
fn strip_block(text: &str) -> String {
let (Some(s), Some(e)) = (text.find(SENTINEL_START), text.find(SENTINEL_END)) else {
return text.to_string();
};
if e < s {
return text.to_string();
}
let region_end = text[e..].find('\n').map_or(text.len(), |nl| e + nl + 1);
let mut out = text[..s].trim_end_matches('\n').to_string();
out.push_str(&text[region_end..]);
out
}
fn write_block(path: &Path) -> std::io::Result<String> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let existing = std::fs::read_to_string(path).unwrap_or_default();
let present = existing.contains(SENTINEL_START);
let base = strip_block(&existing);
let sep = if base.trim().is_empty() { "" } else { "\n" };
let body = format!(
"{}{sep}\n{SENTINEL_START}\n{BLOCK_BODY}\n{SENTINEL_END}\n",
base.trim_end_matches('\n')
);
std::fs::write(path, body)?;
let verb = if present { "refreshed" } else { "added" };
Ok(format!("+ {verb} instructions -> {}", path.display()))
}
pub fn install_context(key: &str, user: bool) -> std::io::Result<Option<String>> {
match context_path(key, user) {
Some(path) => write_block(&path).map(Some),
None => Ok(None),
}
}
pub fn remove_context(key: &str, user: bool) -> std::io::Result<Option<String>> {
let Some(path) = context_path(key, user) else {
return Ok(None);
};
let Ok(existing) = std::fs::read_to_string(&path) else {
return Ok(Some(format!("= nothing to remove: {}", path.display())));
};
let base = strip_block(&existing);
if base == existing {
return Ok(Some(format!("= no managed block in {}", path.display())));
}
if base.trim().is_empty() {
std::fs::remove_file(&path)?;
return Ok(Some(format!(
"- removed instructions <- {}",
path.display()
)));
}
let body = if base.ends_with('\n') {
base
} else {
base + "\n"
};
std::fs::write(&path, body)?;
Ok(Some(format!(
"- removed instructions <- {}",
path.display()
)))
}
#[must_use]
pub fn context_present(key: &str, user: bool) -> bool {
context_path(key, user)
.and_then(|p| std::fs::read_to_string(p).ok())
.is_some_and(|t| t.contains(SENTINEL_START))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_round_trip_preserves_user_notes() {
let dir = std::env::temp_dir().join(format!("burn-agent-ctx-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("CLAUDE.md");
std::fs::write(&path, "# My notes\n\nKeep these.\n").unwrap();
write_block(&path).unwrap();
let text = std::fs::read_to_string(&path).unwrap();
assert!(text.contains("# My notes"));
assert!(text.contains(SENTINEL_START));
assert!(text.contains("burn --sandbox node script.js"));
write_block(&path).unwrap();
let text = std::fs::read_to_string(&path).unwrap();
assert_eq!(text.matches(SENTINEL_START).count(), 1);
let stripped = strip_block(&text);
assert!(stripped.contains("# My notes"));
assert!(!stripped.contains(SENTINEL_START));
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn fresh_file_contains_only_the_block_and_is_deleted_on_remove() {
let dir = std::env::temp_dir().join(format!("burn-agent-ctxf-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("AGENTS.md");
write_block(&path).unwrap();
let text = std::fs::read_to_string(&path).unwrap();
assert!(text.trim_start().starts_with(SENTINEL_START));
let base = strip_block(&text);
assert!(base.trim().is_empty());
let _ = std::fs::remove_dir_all(&dir);
}
}