Welcome to the Cortex Plugin System! This guide provides comprehensive documentation for developing plugins that extend Cortex functionality.
- Introduction
- Plugin Architecture
- Plugin Manifest
- Capabilities and Permissions
- WASM Runtime
- Host Functions
- Development Workflow
- Related Documentation
The Cortex plugin system allows developers to extend the CLI with custom functionality including:
- Custom Commands: Add new slash commands
- Hook Handlers: Intercept and modify tool execution, chat messages, UI rendering, and more
- Custom Widgets: Add UI elements to the TUI
- Keyboard Bindings: Register custom key combinations
- Event Handling: React to system events
Plugins are written in Rust and compiled to WebAssembly (WASM), providing cross-platform compatibility and sandboxed execution.
| Type | Description | Location |
|---|---|---|
| WASM | WebAssembly-based plugins for cross-platform compatibility | ~/.cortex/plugins/ or .cortex/plugins/ |
| Native | Dynamic libraries (.so, .dylib, .dll) for maximum performance | Same as above |
| Script | Directory-based plugins with hooks.json and scripts | Same as above |
Plugins are discovered from two locations:
- User Plugins:
~/.cortex/plugins/- Available across all projects - Project Plugins:
.cortex/plugins/- Project-specific plugins
┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌────────────┐
│ Discovery │ ──▶ │ Loading │ ──▶ │ Registration │ ──▶ │ Initialize │
└─────────────┘ └─────────────┘ └──────────────┘ └────────────┘
│
▼
┌─────────────┐ ┌─────────────┐ ┌────────────┐
│ Shutdown │ ◀── │ Unloading │ ◀────────────────────────│ Active │
└─────────────┘ └─────────────┘ └────────────┘
- Discovery: Plugins are found in plugin directories
- Loading: Manifest is loaded and validated
- Registration: Plugin is registered with the manager
- Initialize: Plugin's
init()function is called - Active: Plugin receives hook calls
- Shutdown: Plugin's
shutdown()function is called on exit
Every plugin requires a plugin.toml manifest file. Here's a complete reference:
# ============================================================================
# Plugin Metadata
# ============================================================================
[plugin]
id = "my-awesome-plugin" # Unique identifier (alphanumeric, hyphens, underscores)
name = "My Awesome Plugin" # Human-readable name
version = "1.0.0" # Semantic version (required)
description = "Plugin description" # What your plugin does
authors = ["Your Name <email>"] # List of authors
homepage = "https://github.com/..." # Plugin homepage/repository
license = "MIT" # License identifier
min_cortex_version = "1.0.0" # Minimum Cortex version required
keywords = ["utility", "tools"] # Keywords for discovery
# ============================================================================
# Capabilities (what your plugin can do)
# ============================================================================
capabilities = [
"commands", # Provide custom commands
"hooks", # Register hooks
"events", # Handle events
"tools", # Provide tools (MCP-style)
"formatters", # Provide formatters
"themes", # Provide custom themes
"config", # Access configuration
"filesystem", # Access file system (with permissions)
"shell", # Execute shell commands (with permissions)
"network" # Make network requests (with permissions)
]
# ============================================================================
# Permissions (what resources your plugin needs)
# ============================================================================
permissions = [
{ read_file = { paths = ["**/*.rs", "**/*.toml"] } },
{ write_file = { paths = [".cortex/plugins/my-plugin/**"] } },
{ execute = { commands = ["cargo", "rustc"] } },
{ network = { domains = ["api.example.com", "registry.npmjs.org"] } },
{ environment = { vars = ["HOME", "PATH"] } },
{ config = { keys = ["theme", "model"] } },
"clipboard", # Access clipboard
"notifications" # Show notifications
]
# ============================================================================
# Commands
# ============================================================================
[[commands]]
name = "my-command" # Command name (without leading /)
aliases = ["mc", "myc"] # Alternative names
description = "Does something" # Command description
usage = "/my-command [options]" # Usage example
category = "utility" # Category for grouping
hidden = false # Hide from help listing
[[commands.args]]
name = "option" # Argument name
description = "An option" # Argument description
required = false # Is it required?
default = "default_value" # Default value
arg_type = "string" # Type: string, number, boolean
# ============================================================================
# Hooks
# ============================================================================
[[hooks]]
hook_type = "tool_execute_before" # Hook type (see HOOKS.md)
priority = 50 # Lower = runs first (default: 100)
pattern = "*.rs" # Optional pattern filter
function = "my_hook_handler" # WASM function name (optional)
[[hooks]]
hook_type = "session_start"
priority = 100
# ============================================================================
# Configuration Schema
# ============================================================================
[config]
api_key = { description = "API key", type = "string", required = true }
max_items = { description = "Max items", type = "number", default = 10 }
enabled = { description = "Enable feature", type = "boolean", default = true }
[config.advanced]
description = "Advanced settings"
type = "object"
required = false
# ============================================================================
# WASM Settings
# ============================================================================
[wasm]
memory_pages = 256 # Memory limit in 64KB pages (256 = 16MB)
timeout_ms = 30000 # Execution timeout (30 seconds default)
wasi_enabled = true # Enable WASI preview1
wasi_caps = [ # WASI capabilities
"stdin",
"stdout",
"stderr",
"env",
"random",
"clocks"
]Capabilities declare what your plugin can do:
| Capability | Description |
|---|---|
commands |
Provide custom slash commands |
hooks |
Register hook handlers |
events |
Handle system events |
tools |
Provide MCP-style tools |
formatters |
Provide code formatters |
themes |
Provide custom themes |
config |
Access Cortex configuration |
filesystem |
Access file system (requires permissions) |
shell |
Execute shell commands (requires permissions) |
network |
Make network requests (requires permissions) |
Permissions request access to specific resources:
| Permission | Description | Risk Level |
|---|---|---|
read_file |
Read files from specified paths | Medium |
write_file |
Write files to specified paths | High |
execute |
Execute specified shell commands | High |
network |
Access specified network domains | Medium |
environment |
Access environment variables | Low |
config |
Access configuration keys | Low |
clipboard |
Access clipboard | Medium |
notifications |
Show notifications | Low |
The WASM runtime enforces strict resource limits for security:
| Resource | Limit | Description |
|---|---|---|
| Memory | 16 MB | Maximum memory per plugin instance |
| CPU | 10M operations | Fuel-based CPU limiting |
| Timeout | 30 seconds | Default execution timeout |
| Tables | 10,000 elements | Maximum table size |
| Instances | 10 | Maximum instances per plugin |
| Memories | 1 | One linear memory per instance |
Memory is allocated in 64KB pages:
[wasm]
memory_pages = 256 # 256 × 64KB = 16MB (maximum)The runtime uses wasmtime's fuel mechanism to limit CPU usage. Each WASM operation consumes fuel. When fuel is exhausted, execution stops with an error.
Default fuel limit: 10,000,000 operations (approximately 10 million basic operations).
Plugins can call host functions to interact with Cortex. These are exposed through the cortex WASM import module.
| Function | Signature | Description |
|---|---|---|
log |
(level: i32, msg_ptr: i32, msg_len: i32) |
Log a message |
get_context |
() -> i64 |
Get execution context |
register_widget |
(region: i32, type_ptr: i32, type_len: i32) -> i32 |
Register a UI widget |
register_keybinding |
(key_ptr: i32, key_len: i32, action_ptr: i32, action_len: i32) -> i32 |
Register a keyboard binding |
show_toast |
(level: i32, msg_ptr: i32, msg_len: i32, duration_ms: i32) -> i32 |
Show a toast notification |
emit_event |
(name_ptr: i32, name_len: i32, data_ptr: i32, data_len: i32) -> i32 |
Emit a custom event |
| Level | Value | Description |
|---|---|---|
| Trace | 0 | Detailed debugging |
| Debug | 1 | Debug information |
| Info | 2 | General information |
| Warn | 3 | Warning messages |
| Error | 4 | Error messages |
| Region | Value | Description |
|---|---|---|
| Header | 0 | Top header area |
| Footer | 1 | Bottom footer area |
| SidebarLeft | 2 | Left sidebar |
| SidebarRight | 3 | Right sidebar |
| MainContent | 4 | Main content area |
| InputArea | 5 | User input area |
| Overlay | 6 | Overlay/modal area |
| StatusBar | 7 | Status bar |
| ToolOutput | 8 | Tool output area |
| MessageArea | 9 | Chat message area |
| Level | Value | Description |
|---|---|---|
| Info | 0 | Informational |
| Success | 1 | Success message |
| Warning | 2 | Warning message |
| Error | 3 | Error message |
#[link(wasm_import_module = "cortex")]
extern "C" {
fn log(level: i32, msg_ptr: i32, msg_len: i32);
fn register_widget(region: i32, type_ptr: i32, type_len: i32) -> i32;
fn show_toast(level: i32, msg_ptr: i32, msg_len: i32, duration_ms: i32) -> i32;
}
fn log_info(msg: &str) {
unsafe {
log(2, msg.as_ptr() as i32, msg.len() as i32);
}
}
fn register_status_widget(widget_type: &str) -> bool {
unsafe {
register_widget(7, widget_type.as_ptr() as i32, widget_type.len() as i32) == 0
}
}
fn show_success_toast(msg: &str, duration_ms: i32) {
unsafe {
show_toast(1, msg.as_ptr() as i32, msg.len() as i32, duration_ms);
}
}# Create plugin directory
mkdir -p ~/.cortex/plugins/my-plugin
cd ~/.cortex/plugins/my-plugin
# Initialize Rust project
cargo init --lib[package]
name = "my-plugin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wee_alloc = "0.4"
[profile.release]
opt-level = "s"
lto = trueSee the Plugin Manifest section above.
# Add WASM target
rustup target add wasm32-wasi
# Build
cargo build --target wasm32-wasi --release
# Copy WASM file
cp target/wasm32-wasi/release/my_plugin.wasm plugin.wasm# Validate your plugin
cortex plugin validate .
# Test in development mode
cortex plugin dev --watch# Plugin is automatically discovered from ~/.cortex/plugins/my-plugin/
cortex plugin list- Getting Started - Step-by-step tutorial for creating your first plugin
- Hooks Reference - Complete reference for all hook types
- Security Model - Security features and best practices
Plugins can be configured in your main Cortex config file:
[[plugins]]
name = "my-plugin"
path = "~/.cortex/plugins/my-plugin"
enabled = true
priority = 0
granted_permissions = ["read_files", "network"]
[plugins.config]
api_key = "your-api-key"
max_items = 20