diff --git a/crates/starforge-plugin-sdk/Cargo.toml b/crates/starforge-plugin-sdk/Cargo.toml new file mode 100644 index 00000000..7c83b921 --- /dev/null +++ b/crates/starforge-plugin-sdk/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "starforge-plugin-sdk" +version = "0.1.0" +edition = "2021" +description = "SDK for building StarForge CLI plugins" +license = "MIT" + +[lib] +name = "starforge_plugin_sdk" diff --git a/crates/starforge-plugin-sdk/src/lib.rs b/crates/starforge-plugin-sdk/src/lib.rs new file mode 100644 index 00000000..67111fa3 --- /dev/null +++ b/crates/starforge-plugin-sdk/src/lib.rs @@ -0,0 +1,49 @@ +//! StarForge Plugin SDK +//! +//! Implement the [`StarForgePlugin`] trait and use [`export_plugin!`] to +//! expose your plugin to the StarForge CLI loader. + +/// Metadata every plugin must provide. +pub struct PluginMeta { + pub name: &'static str, + pub version: &'static str, + pub description: &'static str, +} + +/// Core trait all StarForge plugins must implement. +pub trait StarForgePlugin { + fn meta(&self) -> PluginMeta; + fn run(&self, args: &[String]) -> Result<(), String>; +} + +/// Exports a plugin so the StarForge CLI can load it via `libloading`. +/// +/// # Example +/// ```rust,ignore +/// use starforge_plugin_sdk::{export_plugin, PluginMeta, StarForgePlugin}; +/// +/// struct MyPlugin; +/// +/// impl StarForgePlugin for MyPlugin { +/// fn meta(&self) -> PluginMeta { +/// PluginMeta { name: "my-plugin", version: "0.1.0", description: "Does something cool" } +/// } +/// fn run(&self, args: &[String]) -> Result<(), String> { +/// println!("my-plugin args: {:?}", args); +/// Ok(()) +/// } +/// } +/// +/// export_plugin!(MyPlugin); +/// ``` +#[macro_export] +macro_rules! export_plugin { + ($plugin_type:ty) => { + #[no_mangle] + pub extern "C" fn _starforge_plugin_create( + ) -> *mut dyn $crate::StarForgePlugin { + let plugin = <$plugin_type>::default(); + Box::into_raw(Box::new(plugin)) + } + }; +} diff --git a/examples/plugin_example.md b/examples/plugin_example.md new file mode 100644 index 00000000..1cbe0b51 --- /dev/null +++ b/examples/plugin_example.md @@ -0,0 +1,61 @@ +# StarForge Plugin Example + +Build a plugin using the `starforge-plugin-sdk` crate. + +## Setup + +Create a new crate: + +```bash +cargo new --lib my-starforge-plugin +cd my-starforge-plugin +``` + +Add to `Cargo.toml`: + +```toml +[lib] +crate-type = ["cdylib"] + +[dependencies] +starforge-plugin-sdk = { path = "../crates/starforge-plugin-sdk" } +``` + +## Implement the plugin + +```rust +use starforge_plugin_sdk::{export_plugin, PluginMeta, StarForgePlugin}; + +#[derive(Default)] +struct HelloPlugin; + +impl StarForgePlugin for HelloPlugin { + fn meta(&self) -> PluginMeta { + PluginMeta { + name: "hello", + version: "0.1.0", + description: "Prints a greeting", + } + } + + fn run(&self, args: &[String]) -> Result<(), String> { + let name = args.first().map(|s| s.as_str()).unwrap_or("world"); + println!("Hello, {}!", name); + Ok(()) + } +} + +export_plugin!(HelloPlugin); +``` + +## Build + +```bash +cargo build --release +# Output: target/release/libmy_starforge_plugin.dylib (macOS) +# target/release/libmy_starforge_plugin.so (Linux) +``` + +## Load + +Place the compiled `.so` / `.dylib` in `~/.starforge/plugins/` and StarForge will discover it on startup.