1use crate::{Context, World, error::SkillError};
8use serde::{Deserialize, Serialize};
9use std::borrow::Cow;
10use std::collections::BTreeMap;
11use std::path::PathBuf;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SkillManifest {
18 pub name: String,
19 pub description: String,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub license: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub compatibility: Option<String>,
24 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
26 pub metadata: BTreeMap<String, serde_json::Value>,
27 #[serde(
29 default,
30 skip_serializing_if = "Option::is_none",
31 rename = "allowed-tools"
32 )]
33 pub allowed_tools: Option<String>,
34}
35
36impl SkillManifest {
37 pub fn harness_ext(&self) -> Option<HarnessExt> {
39 let v = self.metadata.get("harness")?;
40 serde_json::from_value::<HarnessExt>(v.clone()).ok()
41 }
42}
43
44#[derive(Debug, Clone, Default, Serialize, Deserialize)]
47pub struct HarnessExt {
48 #[serde(default)]
49 pub kind: Option<crate::Execution>,
50 #[serde(default)]
51 pub risk: Option<crate::ToolRisk>,
52 #[serde(default)]
54 pub entrypoint: Option<String>,
55 #[serde(default)]
56 pub schema_version: Option<String>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct Resource {
62 pub kind: ResourceKind,
63 pub path: PathBuf,
64 pub summary: Option<String>,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "lowercase")]
70#[non_exhaustive]
71pub enum ResourceKind {
72 Script,
73 Reference,
74 Asset,
75}
76
77pub type SkillHandler = std::sync::Arc<
79 dyn for<'a> Fn(
80 &'a mut Context,
81 &'a mut World,
82 ) -> futures::future::BoxFuture<'a, Result<(), SkillError>>
83 + Send
84 + Sync,
85>;
86
87pub trait Skill: Send + Sync + 'static {
88 fn manifest(&self) -> &SkillManifest;
89 fn body(&self) -> Cow<'_, str>;
91 fn resources(&self) -> &[Resource] {
92 &[]
93 }
94 fn handler(&self) -> Option<SkillHandler> {
95 None
96 }
97}
98
99pub struct SkillEntry {
104 pub factory: fn() -> std::sync::Arc<dyn Skill>,
105}
106
107inventory::collect!(SkillEntry);
108
109pub fn iter_macro_skills() -> impl Iterator<Item = std::sync::Arc<dyn Skill>> {
112 inventory::iter::<SkillEntry>().map(|e| (e.factory)())
113}