Plugin Tutorial
This tutorial walks through building a real SwarmClaw plugin from scratch, enabling it, testing it, and shipping it in a way that still behaves well under autonomous agent load.
What You'll Build
A plugin that:
- Adds one custom tool (
release_guard) - Rewrites tool input with
beforeToolExec - Exposes plugin settings in Settings → Plugins
- Demonstrates safe install, test, update, and recovery flows
Prerequisites
- SwarmClaw running locally
- Access to
data/plugins/ - Basic JavaScript familiarity
Plugin File Format
SwarmClaw supports both:
- CommonJS:
module.exports = { ... } - ESM:
export default { ... }in a.mjsfile
For a first plugin, CommonJS is the easiest path.
Step 1: Create the Plugin File
Create data/plugins/release-guard.js:
module.exports = {
name: "release-guard",
version: "1.0.0",
description: "Adds a guarded release checklist tool and traces risky shell usage.",
hooks: {
getCapabilityDescription() {
return "I can run guarded release checks with `release_guard` and annotate risky shell calls."
},
getOperatingGuidance() {
return [
"Prefer dry-run or preview modes before write actions.",
"If a release tool would publish externally, confirm the target first.",
]
},
beforeToolExec({ toolName, input }) {
if (toolName !== "shell" || !input || typeof input !== "object") return input
if (typeof input.command !== "string") return input
if (!/npm publish|git push|docker push/i.test(input.command)) return input
return {
...input,
command: `echo "[release-guard] traced command"; ${input.command}`,
}
},
afterToolExec({ toolName, output }) {
if (toolName === "shell") {
console.log("[release-guard] shell completed", String(output).slice(0, 160))
}
},
},
tools: [
{
name: "release_guard",
description: "Return a compact release checklist for a package, app, or deployment target.",
parameters: {
type: "object",
properties: {
target: { type: "string" },
environment: { type: "string", enum: ["dev", "staging", "prod"] },
},
required: ["target"],
},
async execute(args, ctx) {
return {
ok: true,
target: args.target,
environment: args.environment || "prod",
cwd: ctx.session.cwd,
checklist: [
"Verify tests or smoke checks are green",
"Confirm release target and credentials",
"Review rollback path before publish",
],
}
},
},
],
ui: {
settingsFields: [
{
key: "defaultEnvironment",
label: "Default Environment",
type: "select",
options: [
{ label: "Development", value: "dev" },
{ label: "Staging", value: "staging" },
{ label: "Production", value: "prod" },
],
defaultValue: "staging",
},
{
key: "releaseToken",
label: "Release Token",
type: "secret",
},
],
},
}
Step 2: Understand the Important Parts
hooks.beforeToolExeccan return a replacement input object. Use that only for deliberate input shaping, not silent policy surprises.hooks.getCapabilityDescriptionhelps the agent understand what the plugin adds.hooks.getOperatingGuidanceadds concise runtime hints.tools[].execute(args, ctx)receives normalized JSON input plus the current session context.ui.settingsFieldsrenders a settings form in the Plugins sheet. Fields withtype: "secret"are encrypted at rest.
Step 3: Enable the Plugin
- Open Settings → Plugins
- Confirm
release-guardappears in the installed list - Toggle it on
- Open the plugin detail sheet and save any settings you declared
SwarmClaw hot-reloads plugin files, so a restart is usually unnecessary after editing code under data/plugins/.
Step 4: Test the Plugin Tool
Ask an agent to call:
- Tool:
release_guard - Input:
{ "target": "npm package", "environment": "staging" }
You should get a JSON result with:
ok: truetargetenvironmentchecklist
Step 5: Test Hook Behavior
Ask an agent to run a shell command that includes something risky, for example a publish or push command. Because beforeToolExec returns a replacement input object, the tool input is rewritten before execution.
Good uses for beforeToolExec:
- Adding tracing or dry-run flags
- Injecting plugin-controlled defaults
- Enforcing a small number of explicit safety wrappers
Bad uses:
- Silently changing the user’s intent
- Hiding destructive behavior inside a plugin
Step 6: Read and Write Plugin Settings
The plugin sheet uses:
GET /api/plugins/settings?pluginId=<pluginId>PUT /api/plugins/settings?pluginId=<pluginId>
Use settings for values that should not live in source code, such as:
- API base URLs
- Project defaults
- Tokens or credentials
Unknown keys are ignored on write, and secret values are encrypted before storage.
Step 7: Install From URL and Update Safely
Once the plugin works locally, you can host it on HTTPS and install it from Settings → Plugins → Install from URL.
SwarmClaw records normalized source metadata for URL-installed plugins, which enables future updates from the Plugins manager. Built-in plugins update with the app release; external plugins update from their recorded source URL.
Step 8: Add Dependencies When You Need Them
If your plugin needs third-party npm packages, give it a package.json manifest and let SwarmClaw manage a per-plugin workspace.
Typical flow:
- Attach a
package.jsonmanifest to the plugin - Open the plugin detail sheet in Settings → Plugins
- Use Install / Refresh in the Dependencies card
The same operation is available over the API:
curl -X POST http://localhost:3456/api/plugins/dependencies \
-H "Content-Type: application/json" \
-d '{"filename":"release-guard.js","packageManager":"npm"}'
SwarmClaw records dependency status, install errors, package manager, and last install time in plugin metadata so operators can see whether the workspace is ready.
Step 9: Failure Recovery
To test stability guardrails, temporarily throw an error from one of your hooks:
beforeToolExec() {
throw new Error("intentional failure")
}
SwarmClaw tracks consecutive failures across plugin load, hooks, and tool execution. If a plugin keeps failing, it can be auto-disabled based on SWARMCLAW_PLUGIN_FAILURE_THRESHOLD.
Recovery flow:
- Fix the plugin code
- Re-enable the plugin in Settings → Plugins
- Re-run a safe test tool call
Failure metadata is stored in data/plugin-failures.json.
Common Patterns
- Build one plugin around one responsibility
- Keep tool outputs structured when possible
- Use plugin settings instead of hardcoding secrets
- Use hooks for guidance or light shaping, not heavy hidden control flow
- Prefer a dedicated tool over overloading a lifecycle hook when the agent needs explicit intent
Common Mistakes
- Returning prose when the agent really needs JSON
- Doing expensive work in every hook invocation
- Storing credentials in source instead of
settingsFields - Installing unreviewed remote plugin code on production agents
- Forgetting that external plugins execute as server-side code with app-level access
Next Steps
- Use Plugins for the full hook and API reference
- Add UI extensions (
sidebarItems,headerWidgets,chatInputActions) after your tool contract is stable - Combine plugin tools with the built-in
document,extract,table, andcrawlfamilies when you want structured autonomous workflows without writing shell scripts