agent_config/lib.rs
1//! `agent-config` installs hooks, prompt rules, MCP servers, and skills into AI coding harnesses.
2//!
3//! The library knows where each harness keeps its configuration and what shape
4//! that configuration takes. Callers supply a [`HookSpec`], [`McpSpec`], or
5//! [`SkillSpec`]. The library handles atomic writes, backups, ownership ledgers,
6//! and idempotent edits.
7//!
8//! # Production usage
9//!
10//! In production code, prefer [`try_build()`](HookSpecBuilder::try_build) over
11//! the panicking [`build()`](HookSpecBuilder::build) so that invalid specs
12//! propagate as [`Result`] errors instead of panics:
13//!
14//! ```no_run
15//! use agent_config::{by_id, HookSpec, Matcher, Event, Scope};
16//!
17//! fn install_my_hook() -> agent_config::Result<()> {
18//! let spec = HookSpec::builder("myapp")
19//! .command_program("myapp", ["hook", "claude"])
20//! .matcher(Matcher::Bash)
21//! .event(Event::PreToolUse)
22//! .try_build()?;
23//!
24//! let claude = by_id("claude").expect("claude integration registered");
25//! claude.install(&Scope::Global, &spec)?;
26//! Ok(())
27//! }
28//! ```
29//!
30//! [`build()`](HookSpecBuilder::build) is also available as a convenience for
31//! tests and examples where a panic on misconfiguration is acceptable.
32//!
33//! # Quick start
34//!
35//! ```no_run
36//! use agent_config::{by_id, HookSpec, Matcher, Event, Scope};
37//!
38//! let spec = HookSpec::builder("myapp")
39//! .command_program("myapp", ["hook", "claude"])
40//! .matcher(Matcher::Bash)
41//! .event(Event::PreToolUse)
42//! .build();
43//!
44//! let claude = by_id("claude").expect("claude integration registered");
45//! claude.install(&Scope::Global, &spec).unwrap();
46//! ```
47//!
48//! # MCP servers
49//!
50//! ```no_run
51//! use agent_config::{mcp_by_id, McpSpec, Scope};
52//!
53//! let spec = McpSpec::builder("github")
54//! .owner("myapp")
55//! .stdio("npx", ["-y", "@modelcontextprotocol/server-github"])
56//! .build();
57//!
58//! let codex = mcp_by_id("codex").expect("codex MCP support registered");
59//! codex.install_mcp(&Scope::Global, &spec).unwrap();
60//! ```
61//!
62//! # Skills
63//!
64//! ```no_run
65//! use agent_config::{skill_by_id, Scope, SkillSpec};
66//!
67//! let spec = SkillSpec::builder("my-skill")
68//! .owner("myapp")
69//! .description("Use when my app needs custom repository context.")
70//! .body("# My Skill\n\nFollow the local project conventions.")
71//! .build();
72//!
73//! let claude = skill_by_id("claude").expect("claude skill support registered");
74//! claude.install_skill(&Scope::Global, &spec).unwrap();
75//! ```
76//!
77//! # Discovery and uninstall
78//!
79//! ```no_run
80//! use agent_config::{all, by_id, Scope};
81//!
82//! for integration in all() {
83//! if integration.supported_scopes().contains(&Scope::Global.kind())
84//! && integration.is_installed(&Scope::Global, "myapp").unwrap_or(false)
85//! {
86//! println!("{} has myapp installed", integration.display_name());
87//! }
88//! }
89//!
90//! let claude = by_id("claude").expect("claude integration registered");
91//! claude.uninstall(&Scope::Global, "myapp").unwrap();
92//! ```
93//!
94//! # Safety guarantees
95//!
96//! - Atomic writes (write-to-temp + rename).
97//! - First-touch `.bak` backups of any pre-existing file we modify.
98//! - Idempotent installs: repeating `install` with the same `tag` yields the same state.
99//! - Reversible: `uninstall` removes only the tagged content.
100//!
101//! # Concrete agent types
102//!
103//! When the target harness is known at compile time, construct the agent
104//! directly for type-safe, discoverable usage:
105//!
106//! ```no_run
107//! use agent_config::{ClaudeAgent, Integration, HookSpec, Matcher, Event, Scope};
108//!
109//! fn main() -> agent_config::Result<()> {
110//! let claude = ClaudeAgent::new();
111//! let spec = HookSpec::builder("myapp")
112//! .command_program("myapp", ["hook", "claude"])
113//! .matcher(Matcher::Bash)
114//! .event(Event::PreToolUse)
115//! .try_build()?;
116//!
117//! claude.install(&Scope::Global, &spec)?;
118//! Ok(())
119//! }
120//! ```
121//!
122//! The registry API (`by_id`, `mcp_by_id`, `skill_by_id`) remains the
123//! recommended path for CLIs and tools that accept integration IDs from user
124//! input at runtime. Concrete types are stable convenience handles for
125//! compile-time usage.
126
127#![warn(missing_docs)]
128#![warn(clippy::missing_errors_doc)]
129#![forbid(unsafe_code)]
130#![cfg_attr(test, allow(unused_must_use))]
131
132pub mod error;
133pub mod integration;
134pub mod paths;
135pub mod plan;
136pub mod registry;
137pub mod schema;
138pub mod scope;
139pub mod spec;
140pub mod status;
141pub mod validation;
142
143mod agents;
144mod util;
145
146pub use error::AgentConfigError;
147pub use integration::{
148 InstallReport, InstructionSurface, Integration, McpSurface, MigrationReport, SkillSurface,
149 UninstallReport,
150};
151pub use plan::{
152 InstallPlan, PlanStatus, PlanTarget, PlanWarning, PlannedChange, RefusalReason, UninstallPlan,
153};
154pub use registry::{
155 all, by_id, instruction_by_id, instruction_capable, mcp_by_id, mcp_capable, skill_by_id,
156 skill_capable,
157};
158pub use scope::{Scope, ScopeKind};
159pub use spec::{
160 Event, HookCommand, HookSpec, HookSpecBuilder, InstructionPlacement, InstructionSpec,
161 InstructionSpecBuilder, Matcher, McpSpec, McpSpecBuilder, McpTransport, RulesBlock,
162 ScriptTemplate, SecretPolicy, SkillAsset, SkillFrontmatter, SkillSpec, SkillSpecBuilder,
163};
164pub use status::{
165 DriftIssue, InstallStatus, PathStatus, PlanTarget as StatusPlanTarget, StatusReport,
166 StatusWarning,
167};
168pub use validation::{SuggestedAction, ValidationReport};
169
170pub use agents::{
171 AmpAgent, AntigravityAgent, ClaudeAgent, ClineAgent, CodeBuddyAgent, CodexAgent, CopilotAgent,
172 CursorAgent, ForgeAgent, GeminiAgent, HermesAgent, IFlowAgent, JunieAgent, KiloCodeAgent,
173 OpenClawAgent, OpenCodeAgent, QoderCliAgent, QwenAgent, RooAgent, TabnineAgent, TraeAgent,
174 WindsurfAgent,
175};
176
177/// Result alias used throughout the crate's public API.
178pub type Result<T> = std::result::Result<T, AgentConfigError>;