Skip to content

Plugin Manifest

Plugins can include a plugin.yaml manifest to declare metadata, dependencies, capabilities, and exports. While simple plugins work without a manifest, adding one enables:

  • Dependency management
  • Capability-based permissions
  • Plugin enable/disable
  • Version tracking

The manifest file goes in the plugin directory root:

plugins/my-plugin/
├── plugin.yaml # Manifest
├── init.lua # Main entry point
└── lib/ # Additional files

Accepted filenames: plugin.yaml, plugin.yml, manifest.yaml, manifest.yml

name: my-plugin
version: "1.0.0"

Only name and version are required. Everything else has sensible defaults.

name: task-manager
version: "2.1.0"
description: Task management with TASKS.md format
author: Your Name <you@example.com>
license: MIT
main: lua/init.lua
init: setup
capabilities:
- filesystem
- shell
- kiln
dependencies:
- name: core-utils
version: ">=1.0"
- name: markdown-parser
optional: true
exports:
tools:
- tasks_list
- tasks_add
- tasks_complete
commands:
- /tasks
views:
- task-board
auto_discover: false
config:
properties:
default_file:
type: string
description: Default TASKS.md location
default: "TASKS.md"
auto_archive:
type: boolean
default: true
enabled: true
FieldTypeDescription
namestringPlugin identifier (lowercase, hyphens allowed)
versionstringSemantic version (e.g., “1.0.0”, “2.1.0-beta”)
FieldTypeDefaultDescription
descriptionstring""Brief description
authorstring""Author name and email
licensestringnullLicense identifier (MIT, Apache-2.0, etc.)
FieldTypeDefaultDescription
mainstring”init.lua”Main file path relative to plugin dir
initstringnullOptional init function name to call after load

Capabilities declare what resources the plugin needs access to:

capabilities:
- filesystem # Read/write files outside kiln
- network # Make HTTP requests
- shell # Execute shell commands
- kiln # Access knowledge kiln
- agent # Interact with AI agents
- ui # Create custom UI views
- config # Access user configuration
- system # Access system information

Plugins without declared capabilities run in a restricted sandbox. Users may be prompted to grant capabilities on first use.

Declare dependencies on other plugins:

dependencies:
- name: core-utils
version: ">=1.0" # Version constraint (optional)
- name: optional-dep
optional: true # Won't block load if missing

Version constraints support:

  • Exact: "1.0.0"
  • Minimum: ">=1.0"
  • Range: ">=1.0 <2.0"

Plugins load in dependency order automatically.

Explicitly declare what the plugin provides:

exports:
tools:
- my_tool_1
- my_tool_2
commands:
- /my-command
views:
- my-view
handlers:
- my_handler
auto_discover: true # Also scan files for @tool/@command annotations

If auto_discover is true (default), Crucible also scans plugin files for annotated functions. Set to false to only export explicitly listed items.

Define plugin-specific configuration:

config:
properties:
api_key:
type: string
description: API key for external service
max_results:
type: number
default: 10
enabled_features:
type: array

Supported types: string, number, boolean, array, object

enabled: false # Plugin won't load (default: true)

Use this to temporarily disable a plugin without removing it.

  1. Discovery: Crucible scans plugin directories for manifests
  2. Validation: Manifest is parsed and validated
  3. Dependency Resolution: Load order is determined
  4. Loading: Main file is executed
  5. Initialization: Optional init function is called
  6. Export Discovery: Tools, commands, views are registered
-- In Lua, access plugin manager (cru.* is canonical, crucible.* is an alias)
local plugins = cru.plugins
-- List loaded plugins
for name, plugin in pairs(plugins.list()) do
print(name, plugin.version)
end
-- Check capabilities
if plugins.has_capability("my-plugin", "shell") then
-- Plugin can run shell commands
end

Tools, commands, views, and handlers can be registered programmatically without annotations. This is useful for:

  • Dynamic tool creation at runtime
  • Temporary handlers that auto-remove
  • Testing and mocking
  • Plugin-generated tools
use crucible_lua::{PluginManager, ToolBuilder, HandlerBuilder};
let mut manager = PluginManager::new();
// Register a tool using the builder
let tool = ToolBuilder::new("my_search")
.description("Search notes by query")
.param("query", "string")
.param_optional("limit", "number")
.returns("SearchResult[]")
.build();
let handle = manager.register_tool(tool, None); // No owner
// Later: remove it
manager.unregister(handle);

Associate registrations with an owner for bulk removal:

// Register multiple items owned by a workflow
let tool = ToolBuilder::new("workflow_tool").build();
let handler = HandlerBuilder::new("workflow_handler", "tool:after").build();
manager.register_tool(tool, Some("my_workflow"));
manager.register_handler(handler, Some("my_workflow"));
// Later: remove all items owned by this workflow
let removed_count = manager.unregister_by_owner("my_workflow");
BuilderCreatesKey Methods
ToolBuilderDiscoveredToolparam(), param_optional(), returns()
CommandBuilderDiscoveredCommandhint(), handler_fn(), param()
HandlerBuilderDiscoveredHandlerpattern(), priority(), handler_fn()
ViewBuilderDiscoveredViewhandler_fn(), view_fn()
MethodReturnsDescription
register_tool(tool, owner)RegistrationHandleRegister a tool (owner: Option<&str>)
register_command(cmd, owner)RegistrationHandleRegister a command
register_view(view, owner)RegistrationHandleRegister a view
register_handler(handler, owner)RegistrationHandleRegister a handler
unregister(handle)boolRemove by handle
unregister_by_owner(owner)usizeRemove all with owner
  • Lowercase letters, numbers, hyphens, underscores
  • Must start with a letter
  • Cannot end with hyphen or underscore
  • Maximum 64 characters
  • Semver format: MAJOR.MINOR.PATCH
  • Optional prerelease: 1.0.0-beta, 1.0.0-rc.1