agent-policy 0.6.0

Schema-first generator for coding-agent repo policies and compatibility files.
Documentation
//! `agent-policy init` — write a starter `agent-policy.yaml`.

use std::fs;

use camino::Utf8Path;

use crate::error::{Error, Result};

const STARTER_YAML: &str = r#"# agent-policy.yaml
# Generated by: agent-policy init
# Schema: https://github.com/CameronBrooks11/agent-policy/agent-policy.schema.json
#
# Edit this file, then run: agent-policy generate

# Schema version — always use "1" for current releases.
schema_version: "1"

project:
  name: my-project
  summary: A short description of this repository.

# Shell commands for agents to use in this repo.
# Uncomment and fill in any that apply to this project.
# commands:
#   install: "npm install"
#   dev: "npm run dev"
#   lint: "npm run lint"
#   test: "npm test"
#   build: "npm run build"

# Classify repository paths using glob patterns
paths:
  editable:
    - src/**
    - docs/**
  protected:
    - .github/workflows/**
    - deployment.json
  generated:
    - AGENTS.md
    - CLAUDE.md
    - .cursor/rules/**

# Define named agent roles with scoped permissions (optional)
# roles:
#   docs_agent:
#     editable:
#       - docs/**
#     forbidden:
#       - src/**

# Behavioral guardrails (optional)
constraints:
  forbid_secrets: true
  require_tests_for_code_changes: false
  require_human_review_for_protected_paths: true

# Which output targets to generate.
# Valid: agents-md, claude-md, cursor-rules, gemini-md, copilot-instructions
# Omit this section to use the default (agents-md only).
outputs:
  - agents-md
  # - claude-md              # CLAUDE.md  (Anthropic Claude)
  # - cursor-rules           # .cursor/rules/  (Cursor)
  # - gemini-md              # GEMINI.md  (Google Gemini)
  # - copilot-instructions   # .github/copilot-instructions.md  (GitHub Copilot)
"#;

/// Run the `init` command.
///
/// Writes a starter `agent-policy.yaml` to the current directory.
/// Fails if the file already exists and `force` is `false`.
///
/// # Errors
///
/// Returns [`Error::Io`] if the file cannot be written, or if it already
/// exists and `force` is `false`.
pub fn run(force: bool) -> Result<()> {
    let path = Utf8Path::new("agent-policy.yaml");

    if path.exists() && !force {
        return Err(Error::Io {
            path: path.as_std_path().to_owned(),
            source: std::io::Error::new(
                std::io::ErrorKind::AlreadyExists,
                "agent-policy.yaml already exists. Use --force to overwrite.",
            ),
        });
    }

    fs::write(path, STARTER_YAML).map_err(|e| Error::Io {
        path: path.as_std_path().to_owned(),
        source: e,
    })?;

    println!("Created agent-policy.yaml");
    println!("Edit it, then run: agent-policy generate");
    Ok(())
}