Skip to main content

SkillRegistry

Struct SkillRegistry 

Source
pub struct SkillRegistry { /* private fields */ }
Expand description

Skill registry: holds a collection of skills, responsible for lookup and disclosure by name.

Internally an ordered name → skill map guarded by a read-write lock (read-heavy, O(1) lookup by name, registration order preserved); add / remove take &self, so the application can hold the registry handle and hot-swap — add/remove does not depend on construction-time ordering, and the next read (menu / lookup) takes effect immediately.

Cloning is a deep copy: each registry holds its own independent skill collection, so add/remove do not affect each other.

§Panics

If the internal read-write lock gets poisoned (a method panics while still holding it), subsequent calls panic. Normal operation (no public method enters a panic path) does not trigger this.

§Example

use molo::skill::{Skill, SkillRegistry};

let registry = SkillRegistry::new();
let skill = Skill::parse("---\nname: greet\ndescription: Say hello\n---\nHello!")?;
registry.add(skill);

assert_eq!(registry.menu(), "- greet: Say hello");
assert_eq!(
    registry.get("greet").map(|skill| skill.body().to_string()),
    Some("Hello!".to_string())
);
// re-registering the same name: replaces the original skill, position
// unchanged
let v2 = Skill::parse("---\nname: greet\ndescription: Say hello\n---\nGood morning!")?;
registry.add(v2);
assert_eq!(
    registry.get("greet").map(|skill| skill.body().to_string()),
    Some("Good morning!".to_string())
);
assert_eq!(registry.skills().len(), 1);

Implementations§

Source§

impl SkillRegistry

Source

pub fn new() -> SkillRegistry

Create an empty registry.

Source

pub fn add(&self, skill: Skill) -> &SkillRegistry

Register a skill; a same-named skill replaces the original (position unchanged), returning self for chaining.

Registration is an explicit operation: a skill must first pass validation via Skill::parse / Skill::from_dir; invalid skills cannot enter the registry.

Source

pub fn remove(&self, name: &str) -> bool

Remove a skill (by name); returns true when removed, false when the skill does not exist.

This is the developer’s physical management interface (upgrading / retiring skills); session-level “invisibility” filtering uses SkillLayer::with_enabled_skills, and metadata can stay in the registry.

Source

pub fn get(&self, name: &str) -> Option<Skill>

Get a skill by name (cloned, so the lock-held reference does not escape; O(1) lookup by name); returns None when missing.

Source

pub async fn from_dir(path: &Path) -> Result<SkillRegistry, SkillError>

Scan a directory and discover all skill directories within it (reading SKILL.md from each subdirectory).

Lenient discovery: bad skills (parse failure / directory name mismatch / no SKILL.md) are skipped with the reason logged via tracing::warn, without taking down the whole registry; only an unreadable root directory itself returns an error.

§Errors

An unreadable root directory → SkillError::Io.

Source

pub async fn from_dirs<P>(paths: &[P]) -> SkillRegistry
where P: AsRef<Path>,

Discover skills from multiple directories and merge (multi-source loading).

Typical scenario: user-level and project-level skill directories as two sources (concrete paths are the caller’s decision; this library does not hardcode directory conventions). Directories are scanned in argument order, and later-loaded skills with the same name override earlier ones (argument order is priority: put the project level last so it overrides the user level).

Lenient discovery: missing directories, unreadable directories and bad skills are all skipped with the reason logged via tracing::warn, without taking down other sources; if all sources are unusable, an empty registry is returned.

§Example
use molo::skill::SkillRegistry;

// neither source exists: skipped leniently, resulting in an empty
// registry
let skills =
    SkillRegistry::from_dirs(&["molo-nonexistent-a", "molo-nonexistent-b"]).await;
assert!(skills.skills().is_empty());
Source

pub fn menu(&self) -> String

Disclosure block: one line - {name}: {description} per skill, in registration order.

This is the first step of progressive disclosure — the model uses it to decide whether to load a skill by name; one line per skill keeps the resident system-prompt cost fixed and negligible.

Source

pub fn skills(&self) -> Vec<Skill>

A cloned snapshot of all skills (in registration order).

For static assembly scenarios, take the snapshot and build the system prompt yourself, bypassing the disclosure flow.

Trait Implementations§

Source§

impl Clone for SkillRegistry

Source§

fn clone(&self) -> SkillRegistry

std RwLock has no Clone: copy the contents under the lock and rebuild.

1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SkillRegistry

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for SkillRegistry

Source§

fn default() -> SkillRegistry

Returns the “default value” for a type. Read more
Source§

impl Extend<Skill> for SkillRegistry

Source§

fn extend<I>(&mut self, iter: I)
where I: IntoIterator<Item = Skill>,

Extends a collection with the contents of an iterator. Read more
Source§

fn extend_one(&mut self, item: T)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
Source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Source§

impl FromIterator<Skill> for SkillRegistry

Source§

fn from_iter<I>(iter: I) -> SkillRegistry
where I: IntoIterator<Item = Skill>,

Creates a value from an iterator. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more