docs/plugins/skill-workshop.md
Skill Workshop is experimental. It is disabled by default, its capture heuristics and reviewer prompts may change between releases, and automatic writes should be used only in trusted workspaces after reviewing pending-mode output first.
Skill Workshop is procedural memory for workspace skills. It lets an agent turn
reusable workflows, user corrections, hard-won fixes, and recurring pitfalls
into SKILL.md files under:
<workspace>/skills/<skill-name>/SKILL.md
This is different from long-term memory:
Skill Workshop is useful when the agent learns a procedure such as:
It is not intended for:
The bundled plugin is experimental and disabled by default unless it is
explicitly enabled in plugins.entries.skill-workshop.
The plugin manifest does not set enabledByDefault: true. The enabled: true
default inside the plugin config schema applies only after the plugin entry has
already been selected and loaded.
Experimental means:
Minimal safe config:
{
plugins: {
entries: {
"skill-workshop": {
enabled: true,
config: {
autoCapture: true,
approvalPolicy: "pending",
reviewMode: "hybrid",
},
},
},
},
}
With this config:
skill_workshop tool is availableUse automatic writes only in trusted workspaces:
{
plugins: {
entries: {
"skill-workshop": {
enabled: true,
config: {
autoCapture: true,
approvalPolicy: "auto",
reviewMode: "hybrid",
},
},
},
},
}
approvalPolicy: "auto" still uses the same scanner and quarantine path. It
does not apply proposals with critical findings.
| Key | Default | Range / values | Meaning |
|---|---|---|---|
enabled | true | boolean | Enables the plugin after the plugin entry is loaded. |
autoCapture | true | boolean | Enables post-turn capture/review on successful agent turns. |
approvalPolicy | "pending" | "pending", "auto" | Queue proposals or write safe proposals automatically. |
reviewMode | "hybrid" | "off", "heuristic", "llm", "hybrid" | Chooses explicit correction capture, LLM reviewer, both, or neither. |
reviewInterval | 15 | 1..200 | Run reviewer after this many successful turns. |
reviewMinToolCalls | 8 | 1..500 | Run reviewer after this many observed tool calls. |
reviewTimeoutMs | 45000 | 5000..180000 | Timeout for the embedded reviewer run. |
maxPending | 50 | 1..200 | Max pending/quarantined proposals kept per workspace. |
maxSkillBytes | 40000 | 1024..200000 | Max generated skill/support file size. |
Recommended profiles:
// Conservative: explicit tool use only, no automatic capture.
{
autoCapture: false,
approvalPolicy: "pending",
reviewMode: "off",
}
// Review-first: capture automatically, but require approval.
{
autoCapture: true,
approvalPolicy: "pending",
reviewMode: "hybrid",
}
// Trusted automation: write safe proposals immediately.
{
autoCapture: true,
approvalPolicy: "auto",
reviewMode: "hybrid",
}
// Low-cost: no reviewer LLM call, only explicit correction phrases.
{
autoCapture: true,
approvalPolicy: "pending",
reviewMode: "heuristic",
}
Skill Workshop has three capture paths.
The model can call skill_workshop directly when it sees a reusable procedure
or when the user asks it to save/update a skill.
This is the most explicit path and works even with autoCapture: false.
When autoCapture is enabled and reviewMode is heuristic or hybrid, the
plugin scans successful turns for explicit user correction phrases:
next timefrom now onremember tomake sure toalways ... use/check/verify/record/save/preferprefer ... when/for/instead/usewhen askedThe heuristic creates a proposal from the latest matching user instruction. It uses topic hints to choose skill names for common workflows:
animated-gif-workflowscreenshot-asset-workflowqa-scenario-workflowgithub-pr-workflowlearned-workflowsHeuristic capture is intentionally narrow. It is for clear corrections and repeatable process notes, not for general transcript summarization.
When autoCapture is enabled and reviewMode is llm or hybrid, the plugin
runs a compact embedded reviewer after thresholds are reached.
The reviewer receives:
The reviewer has no tools:
disableTools: truetoolsAllow: []disableMessageTool: trueThe reviewer returns either { "action": "none" } or one proposal. The action field is create, append, or replace — prefer append/replace when a relevant skill already exists; use create only when no existing skill fits.
Example create:
{
"action": "create",
"skillName": "media-asset-qa",
"title": "Media Asset QA",
"reason": "Reusable animated media acceptance workflow",
"description": "Validate externally sourced animated media before product use.",
"body": "## Workflow\n\n- Verify true animation.\n- Record attribution.\n- Store a local approved copy.\n- Verify in product UI before final reply."
}
append adds section + body. replace swaps oldText for newText in the named skill.
Every generated update becomes a proposal with:
idcreatedAtupdatedAtworkspaceDiragentIdsessionIdskillNametitlereasonsource: tool, agent_end, or reviewerstatuschangescanFindingsquarantineReasonProposal statuses:
pending - waiting for approvalapplied - written to <workspace>/skillsrejected - rejected by operator/modelquarantined - blocked by critical scanner findingsState is stored per workspace under the Gateway state directory:
<stateDir>/skill-workshop/<workspace-hash>.json
Pending and quarantined proposals are deduplicated by skill name and change
payload. The store keeps the newest pending/quarantined proposals up to
maxPending.
The plugin registers one agent tool:
skill_workshop
statusCount proposals by state for the active workspace.
{ "action": "status" }
Result shape:
{
"workspaceDir": "/path/to/workspace",
"pending": 1,
"quarantined": 0,
"applied": 3,
"rejected": 0
}
list_pendingList pending proposals.
{ "action": "list_pending" }
To list another status:
{ "action": "list_pending", "status": "applied" }
Valid status values:
pendingappliedrejectedquarantinedlist_quarantineList quarantined proposals.
{ "action": "list_quarantine" }
Use this when automatic capture appears to do nothing and the logs mention
skill-workshop: quarantined <skill>.
inspectFetch a proposal by id.
{
"action": "inspect",
"id": "proposal-id"
}
suggestCreate a proposal. With approvalPolicy: "pending" (default), this queues instead of writing.
{
"action": "suggest",
"skillName": "animated-gif-workflow",
"title": "Animated GIF Workflow",
"reason": "User established reusable GIF validation rules.",
"description": "Validate animated GIF assets before using them.",
"body": "## Workflow\n\n- Verify the URL resolves to image/gif.\n- Confirm it has multiple frames.\n- Record attribution and license.\n- Avoid hotlinking when a local asset is needed."
}
{
"action": "suggest",
"apply": true,
"skillName": "animated-gif-workflow",
"description": "Validate animated GIF assets before using them.",
"body": "## Workflow\n\n- Verify true animation.\n- Record attribution."
}
{
"action": "suggest",
"apply": false,
"skillName": "screenshot-asset-workflow",
"description": "Screenshot replacement workflow.",
"body": "## Workflow\n\n- Verify dimensions.\n- Optimize the PNG.\n- Run the relevant gate."
}
{
"action": "suggest",
"skillName": "qa-scenario-workflow",
"section": "Workflow",
"description": "QA scenario workflow.",
"body": "- For media QA, verify generated assets render and pass final assertions."
}
{
"action": "suggest",
"skillName": "github-pr-workflow",
"oldText": "- Check the PR.",
"newText": "- Check unresolved review threads, CI status, linked issues, and changed files before deciding."
}
applyApply a pending proposal.
{
"action": "apply",
"id": "proposal-id"
}
apply refuses quarantined proposals:
quarantined proposal cannot be applied
rejectMark a proposal rejected.
{
"action": "reject",
"id": "proposal-id"
}
write_support_fileWrite a supporting file inside an existing or proposed skill directory.
Allowed top-level support directories:
references/templates/scripts/assets/Example:
{
"action": "write_support_file",
"skillName": "release-workflow",
"relativePath": "references/checklist.md",
"body": "# Release Checklist\n\n- Run release docs.\n- Verify changelog.\n"
}
Support files are workspace-scoped, path-checked, byte-limited by
maxSkillBytes, scanned, and written atomically.
Skill Workshop writes only under:
<workspace>/skills/<normalized-skill-name>/
Skill names are normalized:
[a-z0-9_-] runs become -[a-z0-9][a-z0-9_-]{1,79}For create:
SKILL.md## WorkflowFor append:
For replace:
oldText must be present exactlyAll writes are atomic and refresh the in-memory skills snapshot immediately, so the new or updated skill can become visible without a Gateway restart.
Skill Workshop has a safety scanner on generated SKILL.md content and support
files.
Critical findings quarantine proposals:
| Rule id | Blocks content that... |
|---|---|
prompt-injection-ignore-instructions | tells the agent to ignore prior/higher instructions |
prompt-injection-system | references system prompts, developer messages, or hidden instructions |
prompt-injection-tool | encourages bypassing tool permission/approval |
shell-pipe-to-shell | includes curl/wget piped into sh, bash, or zsh |
secret-exfiltration | appears to send env/process env data over the network |
Warn findings are retained but do not block by themselves:
| Rule id | Warns on... |
|---|---|
destructive-delete | broad rm -rf style commands |
unsafe-permissions | chmod 777 style permission use |
Quarantined proposals:
scanFindingsquarantineReasonlist_quarantineapplyTo recover from a quarantined proposal, create a new safe proposal with the unsafe content removed. Do not edit the store JSON by hand.
When enabled, Skill Workshop injects a short prompt section that tells the agent
to use skill_workshop for durable procedural memory.
The guidance emphasizes:
The write mode text changes with approvalPolicy:
Heuristic capture does not call a model.
LLM review uses an embedded run on the active/default agent model. It is threshold-based so it does not run on every turn by default.
The reviewer:
reviewTimeoutMsIf the reviewer fails, times out, or returns invalid JSON, the plugin logs a warning/debug message and skips that review pass.
Use Skill Workshop when the user says:
Good skill text:
## Workflow
- Verify the GIF URL resolves to `image/gif`.
- Confirm the file has multiple frames.
- Record source URL, license, and attribution.
- Store a local copy when the asset will ship with the product.
- Verify the local asset renders in the target UI before final reply.
Poor skill text:
The user asked about a GIF and I searched two websites. Then one was blocked by
Cloudflare. The final answer said to check attribution.
Reasons the poor version should not be saved:
Check whether the plugin is loaded:
openclaw plugins list --enabled
Check proposal counts from an agent/tool context:
{ "action": "status" }
Inspect pending proposals:
{ "action": "list_pending" }
Inspect quarantined proposals:
{ "action": "list_quarantine" }
Common symptoms:
| Symptom | Likely cause | Check |
|---|---|---|
| Tool is unavailable | Plugin entry is not enabled | plugins.entries.skill-workshop.enabled and openclaw plugins list |
| No automatic proposal appears | autoCapture: false, reviewMode: "off", or thresholds not met | Config, proposal status, Gateway logs |
| Heuristic did not capture | User wording did not match correction patterns | Use explicit skill_workshop.suggest or enable LLM reviewer |
| Reviewer did not create a proposal | Reviewer returned none, invalid JSON, or timed out | Gateway logs, reviewTimeoutMs, thresholds |
| Proposal is not applied | approvalPolicy: "pending" | list_pending, then apply |
| Proposal disappeared from pending | Duplicate proposal reused, max pending pruning, or was applied/rejected/quarantined | status, list_pending with status filters, list_quarantine |
| Skill file exists but model misses it | Skill snapshot not refreshed or skill gating excludes it | openclaw skills status and workspace skill eligibility |
Relevant logs:
skill-workshop: queued <skill>skill-workshop: applied <skill>skill-workshop: quarantined <skill>skill-workshop: heuristic capture skipped: ...skill-workshop: reviewer skipped: ...skill-workshop: reviewer found no updateRepo-backed QA scenarios:
qa/scenarios/plugins/skill-workshop-animated-gif-autocreate.mdqa/scenarios/plugins/skill-workshop-pending-approval.mdqa/scenarios/plugins/skill-workshop-reviewer-autonomous.mdRun the deterministic coverage:
pnpm openclaw qa suite \
--scenario skill-workshop-animated-gif-autocreate \
--scenario skill-workshop-pending-approval \
--concurrency 1
Run reviewer coverage:
pnpm openclaw qa suite \
--scenario skill-workshop-reviewer-autonomous \
--concurrency 1
The reviewer scenario is intentionally separate because it enables
reviewMode: "llm" and exercises the embedded reviewer pass.
Avoid approvalPolicy: "auto" when:
Use pending mode first. Switch to auto mode only after reviewing the kind of skills the agent proposes in that workspace.