Skip to main content

Beyond the Basics

This guide covers advanced patterns for capability plugins beyond the basics in Creating Capabilities. These patterns handle real-world complexity: dependencies, long-running operations, state management, and multi-tool plugins.

Plugin Dependencies

Add a package.json next to your SKILL.md to declare npm dependencies. The cerebellum installs them lazily on first load:
Dependencies are installed with npm install --production in the capability folder. The cerebellum runs this once and caches the result. Delete node_modules/ to force a reinstall.

Skill Dependencies

Use the requires field in SKILL.md frontmatter to declare that your capability depends on other capabilities being loaded:
If shell isn’t loaded, cerebellum logs a warning and skips your capability.

Long-Running Tools with Cancellation

Use the signal (AbortSignal) from context to support cancellation. The motor cortex passes this when the user clicks Stop or a task timeout fires:
Always check signal.aborted inside loops and before expensive operations. The motor cortex retries on failure 3x — but a cancellation should not be retried.

Background Processes

Plugins can spawn detached child processes for dev servers, watchers, or other long-running programs. Return the PID so the agent can reference it later:

File I/O Patterns

Use context.workspaceRoot to read/write workspace files and context.pluginDir for plugin-local configuration:
Always use context.workspaceRoot — never hardcode paths. Users can move their workspace anywhere.

Multi-Tool Plugins

A single plugin can export multiple tools. Route by toolName in execute():

Error Handling

Always return a ToolResult with success: false and an error message on failure. Never throw — the motor cortex catches exceptions but retries 3x, which wastes tokens if the error is deterministic:
The motor cortex retries failed tool calls up to 3 times with exponential backoff (2s, 6s, 18s). Return clear error messages so the LLM can adjust its approach on the next attempt.

Stateful Plugins

init() runs once on load, destroy() on shutdown. Store state in closures or module scope:

Tool Result Formatting

The output string is shown to the LLM for its next turn. Make it informative but concise — the LLM has limited context:
Truncate long outputs. The LLM doesn’t need every detail — give it enough to make a decision. A good rule: keep tool output under 4000 characters.

Inter-Plugin Communication

Plugins cannot call other plugins directly. If your plugin needs another capability’s output, emit the need as part of your result and let the LLM orchestrate:
The LLM reads this output and decides to call file_read next. This keeps the architecture clean — the LLM is the orchestrator, plugins are tools.

Security Model

Plugins run with full Node.js access in the main Electron process. They can do anything: file system, network, child processes, native modules. The amygdala is the only gate — it decides whether the LLM is allowed to invoke a tool based on danger patterns.
Only install plugins you trust. A malicious plugin has the same access as Wolffish itself. Review plugin code before adding it to your workspace, especially community plugins.

Testing Plugins

Two approaches for testing:
Create a mock context and call execute() directly:

Real-World Example: Paginated API Client

A complete plugin that makes HTTP API calls, handles pagination, and returns structured results: