caliban_plugins/loaded.rs
1//! `LoadedPlugin` and friends — the in-memory representation handed to the
2//! caliban binary after discovery.
3
4use std::path::PathBuf;
5
6use crate::manifest::{PluginManifest, ResolvedComponents};
7
8/// Where a plugin was discovered. Determines override semantics.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum PluginSource {
11 /// `<workspace>/.caliban/plugins/<name>/`
12 Project,
13 /// `$XDG_DATA_HOME/caliban/plugins/<name>/`
14 User,
15 /// `/etc/caliban/plugins/<name>/` (Linux), platform analogues elsewhere.
16 Managed,
17}
18
19impl PluginSource {
20 /// Stable lower-case label for logs / overlays.
21 #[must_use]
22 pub fn as_str(self) -> &'static str {
23 match self {
24 Self::Project => "project",
25 Self::User => "user",
26 Self::Managed => "managed",
27 }
28 }
29}
30
31/// A namespaced item produced by a plugin (`pluginA:skill-foo`).
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct NamespacedItem {
34 /// `<plugin>:<bare-name>` (e.g. `superpowers:brainstorming`).
35 pub namespaced: String,
36 /// The bare item name (e.g. `brainstorming`).
37 pub bare: String,
38 /// The plugin namespace (e.g. `superpowers`).
39 pub plugin: String,
40}
41
42impl NamespacedItem {
43 /// Construct from a plugin name + bare item name.
44 #[must_use]
45 pub fn new(plugin: &str, bare: &str) -> Self {
46 Self {
47 namespaced: format!("{plugin}:{bare}"),
48 bare: bare.to_string(),
49 plugin: plugin.to_string(),
50 }
51 }
52}
53
54/// A plugin that successfully passed manifest validation and platform/version
55/// gating. `components` is already resolved to absolute paths under `root_dir`.
56#[derive(Debug, Clone)]
57pub struct LoadedPlugin {
58 /// Parsed manifest.
59 pub manifest: PluginManifest,
60 /// Absolute path to the plugin directory.
61 pub root_dir: PathBuf,
62 /// Namespace string — always == `manifest.name`. Kept separately for
63 /// quick access by overlays.
64 pub namespace: String,
65 /// Source root the plugin was discovered in.
66 pub source: PluginSource,
67 /// Component paths resolved against `root_dir`.
68 pub components: ResolvedComponents,
69}
70
71impl LoadedPlugin {
72 /// Compose `<namespace>:<bare>` for a discovered item.
73 #[must_use]
74 pub fn namespace_item(&self, bare: &str) -> String {
75 format!("{}:{bare}", self.namespace)
76 }
77}