agent-config 0.3.3

Install hooks/integrations into AI coding harnesses (Claude Code, Cursor, Gemini CLI, OpenCode, Codex CLI, Cline, Windsurf, ...) without learning each one's filesystem layout.
Documentation
//! `agent-config` installs hooks, prompt rules, MCP servers, and skills into AI coding harnesses.
//!
//! The library knows where each harness keeps its configuration and what shape
//! that configuration takes. Callers supply a [`HookSpec`], [`McpSpec`], or
//! [`SkillSpec`]. The library handles atomic writes, backups, ownership ledgers,
//! and idempotent edits.
//!
//! # Production usage
//!
//! In production code, prefer [`try_build()`](HookSpecBuilder::try_build) over
//! the panicking [`build()`](HookSpecBuilder::build) so that invalid specs
//! propagate as [`Result`] errors instead of panics:
//!
//! ```no_run
//! use agent_config::{by_id, HookSpec, Matcher, Event, Scope};
//!
//! fn install_my_hook() -> agent_config::Result<()> {
//!     let spec = HookSpec::builder("myapp")
//!         .command_program("myapp", ["hook", "claude"])
//!         .matcher(Matcher::Bash)
//!         .event(Event::PreToolUse)
//!         .try_build()?;
//!
//!     let claude = by_id("claude").expect("claude integration registered");
//!     claude.install(&Scope::Global, &spec)?;
//!     Ok(())
//! }
//! ```
//!
//! [`build()`](HookSpecBuilder::build) is also available as a convenience for
//! tests and examples where a panic on misconfiguration is acceptable.
//!
//! # Quick start
//!
//! ```no_run
//! use agent_config::{by_id, HookSpec, Matcher, Event, Scope};
//!
//! let spec = HookSpec::builder("myapp")
//!     .command_program("myapp", ["hook", "claude"])
//!     .matcher(Matcher::Bash)
//!     .event(Event::PreToolUse)
//!     .build();
//!
//! let claude = by_id("claude").expect("claude integration registered");
//! claude.install(&Scope::Global, &spec).unwrap();
//! ```
//!
//! # MCP servers
//!
//! ```no_run
//! use agent_config::{mcp_by_id, McpSpec, Scope};
//!
//! let spec = McpSpec::builder("github")
//!     .owner("myapp")
//!     .stdio("npx", ["-y", "@modelcontextprotocol/server-github"])
//!     .build();
//!
//! let codex = mcp_by_id("codex").expect("codex MCP support registered");
//! codex.install_mcp(&Scope::Global, &spec).unwrap();
//! ```
//!
//! # Skills
//!
//! ```no_run
//! use agent_config::{skill_by_id, Scope, SkillSpec};
//!
//! let spec = SkillSpec::builder("my-skill")
//!     .owner("myapp")
//!     .description("Use when my app needs custom repository context.")
//!     .body("# My Skill\n\nFollow the local project conventions.")
//!     .build();
//!
//! let claude = skill_by_id("claude").expect("claude skill support registered");
//! claude.install_skill(&Scope::Global, &spec).unwrap();
//! ```
//!
//! # Discovery and uninstall
//!
//! ```no_run
//! use agent_config::{all, by_id, Scope};
//!
//! for integration in all() {
//!     if integration.supported_scopes().contains(&Scope::Global.kind())
//!         && integration.is_installed(&Scope::Global, "myapp").unwrap_or(false)
//!     {
//!         println!("{} has myapp installed", integration.display_name());
//!     }
//! }
//!
//! let claude = by_id("claude").expect("claude integration registered");
//! claude.uninstall(&Scope::Global, "myapp").unwrap();
//! ```
//!
//! # Safety guarantees
//!
//! - Atomic writes (write-to-temp + rename).
//! - First-touch `.bak` backups of any pre-existing file we modify.
//! - Idempotent installs: repeating `install` with the same `tag` yields the same state.
//! - Reversible: `uninstall` removes only the tagged content.
//!
//! # Concrete agent types
//!
//! When the target harness is known at compile time, construct the agent
//! directly for type-safe, discoverable usage:
//!
//! ```no_run
//! use agent_config::{ClaudeAgent, Integration, HookSpec, Matcher, Event, Scope};
//!
//! fn main() -> agent_config::Result<()> {
//!     let claude = ClaudeAgent::new();
//!     let spec = HookSpec::builder("myapp")
//!         .command_program("myapp", ["hook", "claude"])
//!         .matcher(Matcher::Bash)
//!         .event(Event::PreToolUse)
//!         .try_build()?;
//!
//!     claude.install(&Scope::Global, &spec)?;
//!     Ok(())
//! }
//! ```
//!
//! The registry API (`by_id`, `mcp_by_id`, `skill_by_id`) remains the
//! recommended path for CLIs and tools that accept integration IDs from user
//! input at runtime. Concrete types are stable convenience handles for
//! compile-time usage.

#![warn(missing_docs)]
#![warn(clippy::missing_errors_doc)]
#![forbid(unsafe_code)]
#![cfg_attr(test, allow(unused_must_use))]

pub mod error;
pub mod integration;
pub mod paths;
pub mod plan;
pub mod registry;
pub mod schema;
pub mod scope;
pub mod spec;
pub mod status;
pub mod validation;

mod agents;
mod util;

pub use error::AgentConfigError;
pub use integration::{
    InstallReport, InstructionSurface, Integration, McpSurface, MigrationReport, SkillSurface,
    UninstallReport,
};
pub use plan::{
    InstallPlan, PlanStatus, PlanTarget, PlanWarning, PlannedChange, RefusalReason, UninstallPlan,
};
pub use registry::{
    all, by_id, instruction_by_id, instruction_capable, mcp_by_id, mcp_capable, skill_by_id,
    skill_capable,
};
pub use scope::{Scope, ScopeKind};
pub use spec::{
    Event, HookCommand, HookSpec, HookSpecBuilder, InstructionPlacement, InstructionSpec,
    InstructionSpecBuilder, Matcher, McpSpec, McpSpecBuilder, McpTransport, RulesBlock,
    ScriptTemplate, SecretPolicy, SkillAsset, SkillContext, SkillEffort, SkillFrontmatter,
    SkillShell, SkillSpec, SkillSpecBuilder,
};
pub use status::{
    DriftIssue, InstallStatus, PathStatus, PlanTarget as StatusPlanTarget, StatusReport,
    StatusWarning,
};
pub use validation::{SuggestedAction, ValidationReport};

pub use agents::{
    AmpAgent, AntigravityAgent, ClaudeAgent, ClineAgent, CodeBuddyAgent, CodexAgent, CopilotAgent,
    CursorAgent, ForgeAgent, GeminiAgent, HermesAgent, IFlowAgent, JunieAgent, KiloCodeAgent,
    OpenClawAgent, OpenCodeAgent, QoderCliAgent, QwenAgent, RooAgent, TabnineAgent, TraeAgent,
    WindsurfAgent,
};

/// Result alias used throughout the crate's public API.
pub type Result<T> = std::result::Result<T, AgentConfigError>;