Skip to main content

agent_rules_tool/
lib.rs

1#![warn(missing_docs)]
2
3//! Lint and migrate AI agent rule files per
4//! [agent-rules-spec](https://github.com/rameshsunkara/agent-rules-spec).
5//!
6//! # Quick start
7//!
8//! ```
9//! use agent_rules_tool::{lint_string, migrate_string, LintOptions, MigrateOptions};
10//! use agent_rules_tool::format::RuleFormat;
11//!
12//! # fn example(content: &str) -> Result<(), agent_rules_tool::Error> {
13//! let report = lint_string(content, &LintOptions::default())?;
14//! assert!(report.valid);
15//!
16//! let migrated = migrate_string(
17//!     content,
18//!     &MigrateOptions {
19//!         from: RuleFormat::Cursor,
20//!         to: RuleFormat::Agents,
21//!         ..Default::default()
22//!     },
23//! )?;
24//! # let _ = migrated;
25//! # Ok(())
26//! # }
27//! ```
28//!
29//! # Modules
30//!
31//! - [`lint`] — validate rule content against schema and RFC semantics
32//! - [`migrate`] — convert between tool-native and canonical formats
33//! - [`mod@format`] — format detection and tool directory defaults
34//! - [`spec`] — vendored spec URLs, embedded schema, and constants
35//! - [`report`] — YAML lint report serialization
36
37pub mod cli;
38pub mod discover;
39pub mod error;
40pub mod format;
41pub mod io;
42pub mod lint;
43pub mod migrate;
44pub mod parse;
45pub mod report;
46pub mod schema;
47pub mod spec;
48pub mod walk;
49
50/// Error type returned by library operations.
51pub use error::Error;
52/// Rule format identifiers for migration and detection.
53pub use format::{RuleFormat, RuleFormatArg};
54/// Lint a single rule string or directory of rule files.
55pub use lint::{exceeds_threshold, lint_directory, lint_string};
56/// Migrate rule files between formats.
57pub use migrate::{
58    InputRule, MigrateOptions, MigrateResult, MigrateSummary, MigrateWarning,
59    build_inputs_from_dirs, migrate_paths, migrate_string,
60};
61
62use serde::{Deserialize, Serialize};
63use std::path::PathBuf;
64
65/// Violation severity for lint results and exit-code thresholds.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
67pub enum Severity {
68    /// Non-fatal issue; fails only when threshold is [`Severity::Warn`].
69    Warn,
70    /// Schema or RFC violation.
71    Error,
72}
73
74impl Severity {
75    /// Returns `true` when `self` is at or above `threshold`.
76    pub fn meets_threshold(self, threshold: Self) -> bool {
77        self >= threshold
78    }
79}
80
81/// A single lint finding with a citation into the spec or schema.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct Violation {
84    /// How severe this finding is.
85    pub severity: Severity,
86    /// Frontmatter field or path associated with the violation.
87    pub field: String,
88    /// Human-readable description of the problem.
89    pub message: String,
90    /// URL to the relevant RFC section or schema document.
91    pub spec_ref: &'static str,
92}
93
94/// Options controlling [`lint_string`] and [`lint_directory`].
95#[derive(Debug, Clone)]
96pub struct LintOptions {
97    /// Minimum severity that should fail a lint run (CLI exit code).
98    pub severity_threshold: Severity,
99    /// Filename stem used for semantic checks such as `name` vs filename.
100    pub filename_hint: Option<String>,
101}
102
103impl Default for LintOptions {
104    fn default() -> Self {
105        Self {
106            severity_threshold: Severity::Error,
107            filename_hint: None,
108        }
109    }
110}
111
112/// Result of linting one rule file's content.
113#[derive(Debug, Clone)]
114pub struct LintReport {
115    /// All violations found in the content.
116    pub violations: Vec<Violation>,
117    /// `true` when `violations` is empty.
118    pub valid: bool,
119}
120
121/// Lint result for one file within a directory scan.
122#[derive(Debug, Clone)]
123pub struct FileLintResult {
124    /// Path relative to the directory passed to [`lint_directory`].
125    pub path: PathBuf,
126    /// Lint outcome for this file.
127    pub report: LintReport,
128}
129
130/// Vendored [agent-rules-spec](https://github.com/rameshsunkara/agent-rules-spec) index manifest.
131#[derive(Debug, Clone, Deserialize)]
132pub struct SpecIndex {
133    /// Upstream repository URL.
134    pub repository: String,
135    /// Pinned upstream git commit.
136    pub commit: String,
137    /// Vendored file manifest entries.
138    pub files: Vec<SpecIndexEntry>,
139}
140
141/// One entry in [`SpecIndex::files`].
142#[derive(Debug, Clone, Deserialize)]
143pub struct SpecIndexEntry {
144    /// Path under `spec/` in this repository.
145    pub vendored: String,
146    /// Corresponding path in the upstream repository.
147    pub upstream: String,
148}
149
150/// Load the embedded spec index from [`spec::EMBEDDED_INDEX`].
151pub fn load_spec_index() -> Result<SpecIndex, Error> {
152    serde_saphyr::from_str(spec::EMBEDDED_INDEX).map_err(|e| Error::Yaml(e.to_string()))
153}