use agent_config::{
by_id, mcp_by_id, Event, HookSpec, Matcher, McpSpec, PlanStatus, PlannedChange, Result, Scope,
};
fn main() -> Result<()> {
let project = tempfile::tempdir().expect("create tempdir");
let scope = Scope::Local(project.path().to_path_buf());
let hook_spec = HookSpec::builder("myapp")
.command_program("myapp", ["hook"])
.matcher(Matcher::Bash)
.event(Event::PreToolUse)
.rules("Use myapp.")
.try_build()?;
let claude = by_id("claude").expect("claude is registered");
let plan = claude.plan_install(&scope, &hook_spec)?;
println!("hook plan_install");
println!(" status : {:?}", plan.status);
summarise_changes(&plan.changes);
assert!(matches!(plan.status, PlanStatus::WillChange));
assert!(
!project
.path()
.join(".claude")
.join("settings.json")
.exists(),
"plan must not mutate the filesystem"
);
let _ = claude.install(&scope, &hook_spec)?;
let plan_again = claude.plan_install(&scope, &hook_spec)?;
println!("\nhook plan_install (after apply)");
println!(" status : {:?}", plan_again.status);
assert!(matches!(plan_again.status, PlanStatus::NoOp));
let mcp = mcp_by_id("claude").expect("claude supports MCP");
let bad_spec = McpSpec::builder("github")
.owner("myapp")
.stdio("npx", ["-y", "@modelcontextprotocol/server-github"])
.env("GITHUB_TOKEN", "ghp_redacted")
.try_build()?;
let bad_plan = mcp.plan_install_mcp(&scope, &bad_spec)?;
println!("\nmcp plan_install (inline secret)");
println!(" status : {:?}", bad_plan.status);
summarise_changes(&bad_plan.changes);
assert!(matches!(bad_plan.status, PlanStatus::Refused));
let uninstall_plan = claude.plan_uninstall(&scope, "never-installed")?;
println!("\nhook plan_uninstall (absent)");
println!(" status : {:?}", uninstall_plan.status);
assert!(matches!(uninstall_plan.status, PlanStatus::NoOp));
let _ = claude.uninstall(&scope, "myapp")?;
Ok(())
}
fn summarise_changes(changes: &[PlannedChange]) {
for change in changes {
match change {
PlannedChange::CreateFile { path } => {
println!(" create : {}", path.display());
}
PlannedChange::PatchFile { path } => {
println!(" patch : {}", path.display());
}
PlannedChange::CreateDir { path } => {
println!(" mkdir : {}", path.display());
}
PlannedChange::WriteLedger { path, key, owner } => {
println!(" ledger : {} <- {key}={owner}", path.display());
}
PlannedChange::CreateBackup { backup, target } => {
println!(" backup : {} <- {}", backup.display(), target.display());
}
PlannedChange::SetPermissions { path, mode } => {
println!(" chmod : {} {:o}", path.display(), mode);
}
PlannedChange::Refuse { reason, path } => {
println!(" refuse : {reason:?} ({path:?})");
}
PlannedChange::NoOp { path, reason } => {
println!(" noop : {} ({reason})", path.display());
}
other => println!(" other : {other:?}"),
}
}
}