use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use agent_base::{PlanItem, Tool};
use serde::Serialize;
pub mod prompter;
pub mod registry;
#[cfg(feature = "prompt_skill")]
pub mod prompt_skill;
#[cfg(feature = "yaml_skill")]
pub mod yaml_skill;
#[cfg(feature = "prompt_skill")]
pub mod catalog;
#[cfg(feature = "prompt_skill")]
pub mod middleware;
#[cfg(feature = "prompt_skill")]
pub mod resolver;
#[cfg(feature = "prompt_skill")]
pub mod telemetry;
#[cfg(feature = "prompt_skill")]
pub mod tool;
pub use prompter::{FullDetailPrompter, LazySkillPrompter};
pub use registry::{SkillRegistry, SkillSummary};
#[cfg(feature = "prompt_skill")]
pub use catalog::{MAX_CATALOG_SKILLS, refresh_catalog, render_catalog, strip_catalog};
#[cfg(feature = "prompt_skill")]
pub use middleware::SkillCatalogRefreshMiddleware;
#[cfg(feature = "prompt_skill")]
pub use resolver::SkillResolver;
#[cfg(feature = "prompt_skill")]
pub use telemetry::SkillTelemetry;
#[cfg(feature = "prompt_skill")]
pub use tool::SkillTool;
#[derive(Debug, Clone, Serialize, PartialEq)]
pub enum SkillParamType {
String,
Number,
HostRef,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct SkillParam {
pub name: String,
pub description: String,
pub param_type: SkillParamType,
pub required: bool,
pub default: Option<String>,
}
pub trait Skill: Send + Sync {
fn name(&self) -> &'static str;
fn brief_description(&self) -> String;
fn detailed_description(&self) -> String;
fn tools(&self) -> Vec<Arc<dyn Tool>>;
fn plan_steps(&self, _params: &HashMap<String, String>) -> Option<Vec<PlanItem>> {
None
}
fn parameters(&self) -> &[SkillParam] {
&[]
}
fn version(&self) -> &'static str {
"0.1.0"
}
fn tags(&self) -> &[&'static str] {
&[]
}
fn author(&self) -> &'static str {
""
}
fn category(&self) -> &'static str {
""
}
fn allowed_tools(&self) -> &[String] {
&[]
}
fn disallowed_tools(&self) -> &[String] {
&[]
}
fn model_override(&self) -> Option<&str> {
None
}
fn is_user_invocable(&self) -> bool {
true
}
fn disable_model_invocation(&self) -> bool {
false
}
fn context_mode(&self) -> Option<&str> {
None
}
fn path_patterns(&self) -> &[String] {
&[]
}
fn skill_dir(&self) -> Option<&Path> {
None
}
fn source_path(&self) -> Option<&Path> {
None
}
fn read_reference(&self, _relative_path: &str) -> Result<String, String> {
Err("references not supported".into())
}
}
pub trait SkillPrompter: Send + Sync {
fn build_prompt(&self, skills: &[Arc<dyn Skill>], detail_tool_name: &str) -> String;
}
#[cfg(test)]
mod tests {
use super::*;
struct TestSkill;
impl Skill for TestSkill {
fn name(&self) -> &'static str {
"test_skill"
}
fn brief_description(&self) -> String {
"brief".to_string()
}
fn detailed_description(&self) -> String {
"detailed".to_string()
}
fn tools(&self) -> Vec<Arc<dyn Tool>> {
vec![]
}
}
#[test]
fn test_default_trait_methods() {
let skill = TestSkill;
let params: HashMap<String, String> = HashMap::new();
assert!(skill.plan_steps(¶ms).is_none());
assert!(skill.parameters().is_empty());
assert_eq!(skill.version(), "0.1.0");
assert!(skill.tags().is_empty());
assert_eq!(skill.author(), "");
assert_eq!(skill.category(), "");
assert!(skill.allowed_tools().is_empty());
assert!(skill.disallowed_tools().is_empty());
assert_eq!(skill.model_override(), None);
assert!(skill.is_user_invocable());
assert!(!skill.disable_model_invocation());
assert_eq!(skill.context_mode(), None);
assert!(skill.path_patterns().is_empty());
assert_eq!(skill.skill_dir(), None);
assert_eq!(skill.source_path(), None);
assert!(skill.read_reference("x").is_err());
}
#[test]
fn test_skill_param_types() {
assert_eq!(SkillParamType::String, SkillParamType::String);
assert_ne!(SkillParamType::Number, SkillParamType::HostRef);
let p = SkillParam {
name: "x".to_string(),
description: "d".to_string(),
param_type: SkillParamType::String,
required: true,
default: None,
};
assert_eq!(p.name, "x");
assert!(p.required);
assert_eq!(p.default, None);
}
}