Files
ClawSkills/development/workflows/openclaw-plugin-development.md
zhi 238574ffd2 feat: add development and hf-hangman-lab skills
development: absorbed openclaw-plugin-dev as a workflow, with
reference docs for plugin structure, hooks, tools, state, config.

hf-hangman-lab: hf-wakeup workflow — full agent wakeup lifecycle:
set busy → check due slots → select & defer → identify task →
plan work → create work channel → execute. All branches end with
status reset to idle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 13:31:27 +00:00

3.4 KiB

OpenClaw Plugin Development

When creating or modifying an OpenClaw plugin.

Reference docs in {baseDir}/docs/ cover each topic in detail.

Process

1. Scaffold

Create the project structure:

my-plugin/
  plugin/
    index.ts                 # export default { id, name, register }
    openclaw.plugin.json     # config schema (additionalProperties: false)
    package.json             # name, version, type: module
    hooks/                   # one file per hook handler
    tools/                   # tool registrations
    core/                    # pure logic (no plugin-sdk imports)
  scripts/
    install.mjs              # --install / --uninstall
  package.json               # dev dependencies
  tsconfig.plugin.json
  .gitignore

See {baseDir}/docs/structure.md for conventions.

2. Entry Point

Follow the globalThis lifecycle pattern:

const _G = globalThis as Record<string, unknown>;
const LIFECYCLE_KEY = "_myPluginLifecycleRegistered";

export default {
  id: "my-plugin",
  name: "My Plugin",
  register(api) {
    if (!_G[LIFECYCLE_KEY]) {
      _G[LIFECYCLE_KEY] = true;
      // gateway-level init (once)
      api.on("gateway_stop", () => { _G[LIFECYCLE_KEY] = false; });
    }
    // agent-level hooks (every register call, dedup inside)
    registerMyHook(api);
    registerMyTools(api);
  },
};

See {baseDir}/docs/entry-point.md for details.

3. Hooks

Each hook in its own file under hooks/. Must use dedup on globalThis:

  • before_model_resolve, before_prompt_build → WeakSet on event object
  • agent_end → Set on runId with size cap 500
  • gateway_start/stop → globalThis flag

If returning prependSystemContext/appendSystemContext, set allowPromptInjection: true in config.

See {baseDir}/docs/hooks.md.

4. Tools

Interface is inputSchema + execute (not parameters + handler):

api.registerTool({
  name: "my-tool",
  inputSchema: { type: "object", properties: { ... }, required: [...] },
  execute: async (toolCallId, params) => { return { result: "ok" }; },
});

For agent context access, use factory form: api.registerTool((ctx) => ({ ... })).

See {baseDir}/docs/tools.md.

5. State Management

All mutable state on globalThis, not module-level variables (hot reload resets modules).

  • Business state, dedup sets, lifecycle flags → globalThis
  • Cross-plugin API → globalThis.__pluginId
  • Pure functions → module-level is fine

See {baseDir}/docs/state.md.

6. Config Schema

openclaw.plugin.json must have additionalProperties: false. Every config field in install script must exist in schema. Never set sensitive fields (tokens) in install script.

See {baseDir}/docs/config.md.

7. Install Script

node scripts/install.mjs --install    # build, copy to ~/.openclaw/plugins/<id>, update config
node scripts/install.mjs --uninstall  # remove from config and filesystem

See {baseDir}/docs/config.md.

8. Build & Test

npm run build
node scripts/install.mjs --install
openclaw gateway restart
openclaw logs --follow

9. Checklist Before Deploy

  • Hook handlers have dedup on globalThis
  • Gateway lifecycle protected by globalThis flag
  • Business state on globalThis
  • Schema matches actual config fields
  • Install script uses setIfMissing, no sensitive fields
  • Clean dist before copy (rmSync)

See {baseDir}/docs/debugging.md for full checklist.