patchloom 0.25.0

Structured file editing library and CLI for AI agents: parser-backed JSON/YAML/TOML edits, AST-aware code operations, multi-file batching, markdown operations, and MCP server
Documentation
//! Agent-facing instruction generator (`patchloom agent-rules`).
//!
//! # Inventory vs policy (do not collapse these)
//!
//! | Kind | Source of truth | Module |
//! |------|-----------------|--------|
//! | Operation catalogue (names, short descriptions, schema examples) | [`crate::schema::OPERATION_REGISTRY`] | [`inventory`] → `schema::agent_operations_catalogue()` |
//! | MCP core tool list, canonical name map, explore rule, YAML style honesty | Hand-written packaging | [`crate::cmd::agent_packaging`] (composed in [`opening`]) |
//! | Host honesty, peels, fuzzy refuse, when-to-use, exit/`error_kind` narrative | Hand-written policy | [`policy`], [`surfaces`], [`reference`] |
//! | Shell workflow recipes | Hand-written examples | [`workflows`] |
//!
//! **Do not** generate long honesty essays from JSON Schema / `OpMeta` just to
//! shrink this tree. Schema answers “what fields exist”; agent-rules answers
//! “how agents must behave.” Inventory generation is already schema-backed;
//! policy stays curated.
//!
//! Submodules are navigation only (one product domain). See AGENTS.md
//! module-size policy (#1408).

mod inventory;
mod opening;
mod policy;
mod reference;
mod surfaces;
mod troubleshooting;
mod workflows;

#[cfg(test)]
mod tests;

use clap::{Args, ValueEnum};

use crate::exit;

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum AgentMode {
    /// Include both CLI and MCP instructions (default).
    All,
    /// CLI-only: omit MCP section.
    Cli,
    /// MCP-only: omit CLI shell examples, lead with MCP tools.
    Mcp,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum AgentPlatform {
    /// Include both Linux and Windows examples where they differ (default).
    All,
    /// Linux/macOS only: heredocs, single-quote shell syntax.
    Linux,
    /// Windows only: file arguments, double-quote escaping.
    Windows,
}

#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
pub enum AgentRulesSurface {
    /// Full agent-rules document (default).
    Full,
    /// Short core pack for system prompts (#2070).
    Core,
}

#[derive(Debug, Args)]
#[command(after_help = "\
EXAMPLES:
  patchloom agent-rules >> AGENTS.md
  patchloom agent-rules --mode mcp
  patchloom agent-rules --platform linux
  patchloom agent-rules --surface core")]
pub struct AgentRulesArgs {
    /// Which integration mode to generate instructions for.
    #[arg(long, value_enum, default_value = "all")]
    pub mode: AgentMode,
    /// Which platform to generate shell examples for.
    #[arg(long, value_enum, default_value = "all")]
    pub platform: AgentPlatform,
    /// Full document or short core pack for system prompts (#2070).
    #[arg(long, value_enum, default_value = "full")]
    pub surface: AgentRulesSurface,
}

pub(crate) const AGENT_RULES_GENERATED_MARKER: &str = "<!-- Generated by patchloom v";

/// Build the full agent-rules markdown for the given mode/platform/surface.
pub(crate) fn generate_agent_rules(args: &AgentRulesArgs) -> String {
    let version = env!("CARGO_PKG_VERSION");
    if args.surface == AgentRulesSurface::Core {
        return crate::cmd::agent_packaging::agent_rules_core_body(version);
    }
    let show_cli = matches!(args.mode, AgentMode::All | AgentMode::Cli);
    let show_mcp = matches!(args.mode, AgentMode::All | AgentMode::Mcp);
    let show_linux = matches!(args.platform, AgentPlatform::All | AgentPlatform::Linux);
    let show_windows = matches!(args.platform, AgentPlatform::All | AgentPlatform::Windows);

    let mut out = String::new();

    opening::append_opening(&mut out, version, show_cli, show_mcp);
    policy::append_policy(&mut out, show_cli, show_mcp);
    surfaces::append_surfaces(&mut out, show_cli, show_mcp, show_linux, show_windows);
    workflows::append_workflows(&mut out, show_cli, show_linux, show_windows);
    reference::append_reference(&mut out, show_cli);
    inventory::append_operations_catalogue(&mut out);
    troubleshooting::append_troubleshooting(&mut out);

    out
}

/// CLI entry: print agent-rules markdown (or JSON envelope with `--json`).
pub fn run(args: AgentRulesArgs, global: &crate::cli::global::GlobalFlags) -> anyhow::Result<u8> {
    let output = generate_agent_rules(&args);
    if !global.emit_json(&serde_json::json!({
        "ok": true,
        "format": "markdown",
        "content": output,
    }))? {
        print!("{output}");
    }
    Ok(exit::SUCCESS)
}