Skip to main content

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>;