mod activate;
mod discover;
mod error;
mod parse;
mod skill;
mod skip;
mod source;
mod why;
pub use activate::{
DEFAULT_ACTIVATE_HINT, FormatOptions, ListFormat, ProgressiveBudgets, filter_skills,
format_available_skills_xml, format_catalog, format_load_message, format_package_envelope,
parse_list_format, progressive_budgets, rank_skills_for_catalog, skill_relevance_score,
trigger_matches, unknown_list_format,
};
pub use discover::{
CURSOR_VENDOR_DENYLIST, DiscoveryOptions, SkillMiss, UNKNOWN_SKILL_KIND, ValidationReport,
discover, find_skill_by_name, format_watch_dirs, unknown_or_skipped_skill,
unknown_or_skipped_skill_message, validate_path, validate_path_with_options,
walk_cwd_to_git_root, watch_dirs, with_home_override,
};
pub use error::{Error, ParseError, sanitize_error_token};
pub use parse::{
normalize_skill_name, parse_skill, skill_name_is_ascii_policy, skill_name_matches_directory,
skill_names_equal, validate_skill_name,
};
pub use skill::{
SKILL_BODY_LINE_SOFT_WARN, SKILL_COMPATIBILITY_MAX_CHARS, SKILL_DESCRIPTION_MAX_CHARS,
SKILL_MD_MAX_BYTES, SKILL_NAME_MAX_CHARS, Skill,
};
pub use skip::{DiscoveryReport, SkillSkip, SkipKind, format_list_tsv, format_skip_tsv};
pub use source::SkillSource;
pub use why::{
ActivationDecision, ActivationReason, SkillSummary, WhyReport, format_why_text, why,
};
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
#[cfg(test)]
mod tests {
#[test]
fn version_is_nonzero() {
assert!(!super::version().is_empty());
}
#[test]
fn crate_root_docs_name_implicit_roots_default() {
let docs: String = include_str!("lib.rs")
.lines()
.filter(|line| line.starts_with("//!"))
.collect::<Vec<_>>()
.join("\n");
assert!(
docs.contains("[`DiscoveryOptions::default`] sets `implicit_roots: true`"),
"crate-root rustdoc must say Default sets implicit_roots: true (not an inverted sentence): {docs}"
);
assert!(
docs.contains("cwd-to-git `.agents`"),
"crate-root rustdoc must attach .agents to cwd-to-git, not walk the whole tree: {docs}"
);
assert!(
docs.contains("`$HOME/.agents`"),
"crate-root rustdoc must name implicit HOME .agents, not user_skills_dir: {docs}"
);
assert!(
docs.contains("CLI `--no-implicit-roots` and MCP `implicit_roots: false`"),
"crate-root rustdoc must map CLI --no-implicit-roots and MCP implicit_roots: false (not an inverted sentence): {docs}"
);
assert!(
docs.contains("`user_skills_dir` still load"),
"crate-root rustdoc must say user_skills_dir still loads when implicit_roots is off (not an inverted sentence): {docs}"
);
assert!(
super::DiscoveryOptions::default().implicit_roots,
"documented default must stay true"
);
}
#[test]
fn crate_root_docs_name_skill_summary_siblings() {
let docs: String = include_str!("lib.rs")
.lines()
.filter(|line| line.starts_with("//!"))
.collect::<Vec<_>>()
.join("\n");
assert!(
docs.contains("List JSON, why JSON, and list XML share [`SkillSummary`]"),
"crate-root rustdoc must say list/why JSON + list XML share SkillSummary: {docs}"
);
assert!(
docs.contains("skill_summary_json_keys_have_list_xml_siblings"),
"crate-root rustdoc must name the sibling-lock test: {docs}"
);
assert!(
docs.contains("Catalog stays cheap"),
"crate-root rustdoc must say catalog stays cheap (not SkillSummary JSON): {docs}"
);
assert!(
docs.contains("text envelope"),
"crate-root rustdoc must say load is the text envelope: {docs}"
);
}
#[test]
fn crate_root_docs_name_skill_miss() {
let docs: String = include_str!("lib.rs")
.lines()
.filter(|line| line.starts_with("//!"))
.collect::<Vec<_>>()
.join("\n");
assert!(
docs.contains("[`SkillMiss`]"),
"crate-root rustdoc must name SkillMiss so a leftover-only host does not scrape Display: {docs}"
);
assert!(
docs.contains("error_kind"),
"crate-root rustdoc must name SkillMiss.error_kind: {docs}"
);
assert!(
docs.contains("leftover-only"),
"crate-root rustdoc must address leftover-only hosts: {docs}"
);
assert!(
docs.contains("scraping Display"),
"crate-root rustdoc must say branch without scraping Display: {docs}"
);
assert!(
docs.contains("unknown_skill") && docs.contains("omits `path`"),
"crate-root rustdoc must say unknown_skill omits path (do not invent one): {docs}"
);
assert!(
docs.contains("winner_path") && docs.contains("name_collision"),
"crate-root rustdoc must name SkillMiss.winner_path on name_collision: {docs}"
);
let unknown = super::unknown_or_skipped_skill("no-such-skill", &[]);
assert_eq!(unknown.error_kind, super::UNKNOWN_SKILL_KIND);
assert!(
unknown.path.is_none(),
"documented unknown_skill miss must omit path"
);
}
#[test]
fn crate_root_docs_name_validation_report() {
let docs: String = include_str!("lib.rs")
.lines()
.filter(|line| line.starts_with("//!"))
.collect::<Vec<_>>()
.join("\n");
assert!(
docs.contains("[`ValidationReport`]"),
"crate-root rustdoc must name ValidationReport like MCP skills_validate: {docs}"
);
assert!(
docs.contains("package directory") && docs.contains("SKILL.md"),
"crate-root rustdoc must name package dir like CLI validate --help: {docs}"
);
assert!(
docs.contains("no `error_kind`") || docs.contains("no error_kind"),
"crate-root rustdoc must say success has no error_kind: {docs}"
);
assert!(
docs.contains("validate --json") && docs.contains("skills_validate"),
"crate-root rustdoc must map CLI validate --json and MCP skills_validate: {docs}"
);
}
}