plugin.json Manifest Reference
The plugin.json file is the only required file in a plugin. It declares what the plugin provides and how Recursive should load it.
Minimal manifest
Section titled “Minimal manifest”{ "id": "my-plugin", "name": "My Plugin", "version": "0.1.0", "description": "What this plugin does."}Complete schema
Section titled “Complete schema”Unknown fields are ignored with a warning in the plugin’s health panel (typos
like skilss get a did-you-mean suggestion); wrong-typed fields are dropped
with a warning. Only a missing id prevents the plugin from loading.
Validate anytime with the plugin_validate tool.
Identity & metadata
Section titled “Identity & metadata”| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique kebab-case plugin identifier. |
name | string | No | Display name (falls back to id). |
version | string | No | Semver version, compared against the registry for update checks. |
description | string | No | One-line summary shown in the plugins UI and registry. |
author | string | object | No | Author attribution — a string or { name, url }. |
homepage | string | No | Project homepage or docs URL. |
color | string | No | Hex color for the icon badge. |
icon | string | No | Icon name from the Recursive icon set. |
thumbnail | string | No | Path to a thumbnail image within the plugin. |
category | string | No | Marketplace category. |
tags | string[] | No | Marketplace search tags. |
appScope | string | No | Which app the plugin applies to: recursive, financial, or shared. |
private | boolean | No | Excluded from registry publishing. |
platforms | string[] | No | OS allowlist (e.g. [“darwin”]); omit for all platforms. |
min_recursive_version | string | No | Minimum Recursive version this plugin supports; enforced at install. |
Capability paths
Section titled “Capability paths”Each points to a directory or file (relative to the plugin root) that Recursive loads. A string can be a directory (ending in /) or a single file; an array mixes both. When the field is omitted, a conventional directory of the same name is picked up automatically.
| Field | Type | Required | Description |
|---|---|---|---|
tools | string | string[] | No | MCP tool handler modules (JS/TS). |
routes | string | string[] | No | HTTP route handler modules. |
adapters | string | string[] | No | Agent CLI adapter modules (JS/TS). |
skills | string | string[] | No | Skill directories containing SKILL.md subfolders. |
rules | string | string[] | No | Agent rule markdown files. |
workflows | string | string[] | No | Workflow markdown files. |
automations | string | string[] | No | Automation definitions. |
hooks | string | string[] | No | Server lifecycle hook handler modules. |
mcp | string | string[] | No | External MCP server configuration (JSON). |
themes | string | string[] | No | Theme JSON files. |
sounds | string | string[] | No | Sound pack audio files (mp3/wav/ogg/m4a/aac/aiff). |
comms | string | string[] | No | Comms templates directory. |
Behavior blocks
Section titled “Behavior blocks”| Field | Type | Required | Description |
|---|---|---|---|
ui | object | No | UI extensions: views, panels, commands, chat renderers, components. |
settings | object | No | Settings schema and defaults rendered in the plugin settings page. |
detect | object | No | Project auto-detection: file patterns that suggest enabling this plugin. |
data | object | No | Data directory scopes the plugin persists into (global/workspace/project). |
provides | object | No | Structured provider declarations (sync_provider, data_provider). |
triggers | object | No | Declares the plugin as an event trigger source: module (exports verify/normalize), a non-empty events[] of { type, label, filters }, optional setupSkill and requiredSettings. |
lifecycle | object | No | Server lifecycle hook handlers keyed by event name. |
client_lifecycle | object | No | Client (renderer) lifecycle hook handlers keyed by event name. |
soundDefaults | Record<string, string> | No | Sound packs: notification event → sound slug mapping. |
dependencies | string[] | No | Plugin ids this plugin requires. |
bundledOptIn | boolean | No | Bundled copy is marketplace-only until explicitly installed. |
core | boolean | No | Core platform plugin — boot-synced, cannot be disabled. |
npm | object | No | npm install behavior — set { "scripts": true } to allow lifecycle scripts (disclosed at install). |
Terminal actions & onboarding
Section titled “Terminal actions & onboarding”| Field | Type | Required | Description |
|---|---|---|---|
loginCommand | string | No | Command that (re)authenticates the plugin’s CLI. |
setupCommand | string | No | First-time configuration command. |
diagnoseCommand | string | No | Health-check / repair command. |
installCommand | string | No | Command that installs the CLI itself. |
installUrl | string | No | Docs or download URL for installing the CLI. |
cli | object | No | How to probe whether the CLI is installed (bin, versionArgs). |
app | object | No | Desktop-app provider config (name, downloadUrl, healthUrl). |
setupSequence | object[] | No | Ordered getting-started steps shown after install. |
onboarding | object | No | Declarative onboarding steps (fields, verify, links). |
ui — Dashboard extensions
Section titled “ui — Dashboard extensions”The ui field is an object that declares frontend components and behaviors.
ui.views
Section titled “ui.views”Array of view definitions that add new pages to the dashboard.
{ "ui": { "views": [ { "id": "my-view", "label": "My View", "icon": "layers", "route": "my-view", "component": "./ui/MyView.svelte", "detailPanel": "./ui/MyDetailPanel.svelte" } ] }}| Field | Type | Description |
|---|---|---|
id | string | Unique view identifier. |
label | string | Display name in navigation. |
icon | string | Icon name. |
route | string | URL route segment (e.g., my-view → /my-view). |
component | string | Path to the main Svelte component. |
detailPanel | string | Optional path to a detail panel component. |
ui.nav
Section titled “ui.nav”Array of navigation entries added to the sidebar.
{ "ui": { "nav": [ { "route": "my-view", "label": "My View", "icon": "layers", "shortcut": "Mod+5", "order": 50, "surface": "sidebar.nav.my-view" } ] }}ui.commands
Section titled “ui.commands”Array of command palette entries.
{ "ui": { "commands": [ { "id": "nav-my-view", "label": "Go to My View", "icon": "layers", "section": "Navigation", "type": "navigate", "route": "my-view", "shortcut": "Mod+5", "keywords": ["custom", "view"] } ] }}ui.panels
Section titled “ui.panels”Panels added to the workspace detail area (alongside file viewer, preview, etc.).
ui.taskPanels
Section titled “ui.taskPanels”Panels added to the task detail view.
ui.chatRenderers
Section titled “ui.chatRenderers”Custom renderers for specific tool call results in the chat feed.
{ "ui": { "chatRenderers": [ { "kind": "goal_create", "component": "./ui/GoalCreateRenderer.svelte" } ] }}ui.entityTypes
Section titled “ui.entityTypes”Register custom entity types for mention resolution and search.
{ "ui": { "entityTypes": [ { "type": "goal", "storeKey": "allGoals", "nameKey": "title" } ] }}ui.sseEvents
Section titled “ui.sseEvents”SSE event channels that the plugin’s store should listen to.
Stores
Section titled “Stores”Frontend stores are discovered by convention, not declared: any
ui/*-store.ts module in a bundled plugin is auto-registered, its
load<Name>() export is called on mount, and SSE events matching the plugin
id (or declared via ui.sseEvents) re-invoke it.
ui.components
Section titled “ui.components”Map of named components that other parts of the UI can reference.
settings
Section titled “settings”Plugin configuration with defaults and a schema for the settings UI.
{ "settings": { "defaults": { "api_key": "", "enabled_features": ["basic"] }, "schema": [ { "key": "api_key", "type": "string", "label": "API Key", "description": "Your service API key.", "section": "Authentication" }, { "key": "enabled_features", "type": "multi-select", "label": "Features", "options": ["basic", "advanced", "experimental"], "section": "Features" } ] }}detect
Section titled “detect”Auto-detection rules that enable the plugin when a project matches.
{ "detect": { "files": ["package.json", "tsconfig.json"], "strategy": "all" }}| Field | Type | Default | Description |
|---|---|---|---|
files | string[] | — | File patterns to check in the project root. |
strategy | "any" | "all" | "any" | Whether any file match suffices or all must be present. |
lifecycle
Section titled “lifecycle”Server-side lifecycle event handlers. Keys are event names, values define the handler.
{ "lifecycle": { "session.complete": { "handler": "./hooks/on-session-complete.ts", "priority": 10, "type": "action" } }}client_lifecycle
Section titled “client_lifecycle”Client-side lifecycle event handlers. Same structure as lifecycle but handlers run in the browser.
{ "client_lifecycle": { "session.complete": { "handler": "./hooks/client-on-complete.ts", "priority": 10 } }}triggers
Section titled “triggers”Declares the plugin as an event trigger source for event-mode automations. See Building a Trigger Plugin for the full contract.
{ "triggers": { "module": "./triggers/index.js", "setupSkill": "connecting-my-provider", "requiredSettings": ["signing_secret"], "events": [ { "type": "issue.created", "label": "Issue created", "filters": ["project"] } ] }}| Field | Required | Description |
|---|---|---|
module | Yes | Path to the file exporting verify(rawBody, headers, connection) and normalize(payload, headers, connection). Without it the whole block is dropped (with a warning). |
events | Yes | Non-empty array of { type, label, description?, filters? }. type (non-empty string) is matched against automations’ event_type; label is shown in the automation editor. A zero-event source is skipped by the trigger catalog — the validator warns about it. |
setupSkill | No | Skill id that walks the user through connecting the provider. Should correspond to a skill this plugin ships — plugin_validate warns when skills/<name>/SKILL.md is missing. |
requiredSettings | No | Settings keys (from settings.schema) that must be non-empty for the source to show as “Connected”. Defaults to every password-type setting. |
onboarding vs setupSequence
Section titled “onboarding vs setupSequence”These two fields solve different problems — don’t mix them up:
onboardingis declarative plugin setup: asubtitleplusstepsof kindlink(open an external console),field(save a settings value, optionally secret), andverify(confirm required credentials are present), rendered in the plugin’s settings page. Use it for integrations that need credentials — trigger sources, API-backed tools.setupSequenceis adapter/CLI onboarding only: ordered terminal-action steps (install/auth/setup/diagnose, falling back toinstallCommand/loginCommand/setupCommand/diagnoseCommand) shown after installing a provider CLI plugin. Use it when the plugin wraps a CLI that must be installed and authenticated.
{ "onboarding": { "subtitle": "Connect your workspace so agents pick up events.", "steps": [ { "kind": "link", "title": "Create an app", "url": "https://example.com/apps", "actionLabel": "Open console" }, { "kind": "field", "title": "Add your Signing Secret", "field": { "key": "signing_secret", "secret": true }, "actionLabel": "Save" }, { "kind": "verify", "title": "Verify setup", "actionLabel": "Verify" } ] }}