Skip to content
Crystal CodeDocs
ENEnglish

Plugins

Operators add in-process extensions as plugins. A plugin is one directory, one plugin.json, and one assembly in its own load context. It can contribute tools, a protocol client, approval classifiers, slash commands, hooks, raw hooks, and prompt placeholders. After the host has loaded plugins, external tools, and skills, it calls Attach with a read-only snapshot of those catalogs, then AttachSession with the live session and review model. A plugin that implements IPluginModelClient also receives clients for those models. A plugin with an admitted status field then receives AttachStatus.

The host owns catalog registration, approval, workspace fencing, output truncation, and timeouts. Plugin code does not bypass ToolInvocationPolicy.

Mechanism Role
PluginRegistry First-party in-process contributions (built-in tools and the six wire protocols).
External tool sets Model-callable tools under tools/. They do not implement this contract and have no hooks. See external-tools.md.
Skills Markdown instructions loaded through the skill tool.
MCP Deferred. It is not a plugin runner.

The plugin has no JSON identity field. The plugin is its directory under plugins/.

~/.crystal/plugins/<directory>/plugin.json
<workspace>/.crystal/plugins/<directory>/plugin.json

Runtime data lives separately under data/plugins/<directory>/ in the same two trees; see Data directories.

Overlay key: the directory name. A project directory replaces the home plugin of the same name as a whole. Directory names are 1–64 characters, start with a letter, then A–Z a–z 0–9 _ . -.

IPlugin.Name is the name shown by /plugins. It must be non-empty. Two loaded plugins cannot share it.

config.json field plugins enables discovery (default true). When true, the field is omitted from the written file. When false, manifests are not scanned. crystal run --plugins on|off overrides that switch for one process and is not written back.

plugin.json field enabled turns one plugin on or off (default true). "enabled": false omits that plugin after overlay, with no operator note. A missing, unreadable, or invalid manifest skips that plugin and records an English note. The session still starts.

/plugins lists loaded plugins and any directory that is not loaded. /plugins on, /plugins off, and /plugins reload persist the discovery switch when it changes and reload catalogs. /plugins enable <directory>, /plugins disable <directory>, and /plugins show <directory> change or show one manifest. Prefix home or project to choose that tree. Without a prefix, a project directory wins over Home. The command reloads catalogs after a change. It does not create a directory. A broken manifest is left unchanged.

crystal plugins list, show, enable, and disable do the same outside a session. --source home|project chooses the tree. --workspace chooses the project root. --home chooses the data directory. The default output is a Spectre.Console table. --format text prints the same fields as aligned plain text. These commands write enabled in plugin.json and do not write config.json. Turning one plugin on does not turn discovery on. crystal run --plugins remains a process-only override.

plugin.json is JSON. Unknown fields are ignored.

{
"enabled": true,
"assembly": "Acme.Plugin.dll",
"type": "Acme.AcmePlugin"
}

assembly is a path inside the plugin directory. type is the public entry type. It implements CrystalCode.Plugins.IPlugin and has a public parameterless constructor.