Files
ClawSkills/openclaw-plugin-dev/docs/entry-point.md
zhi 5a8f490cc2 feat: add openclaw-plugin-dev skill
Plugin development reference and workflows based on real development
experience (Dirigent, ContractorAgent, PrismFacet).

Docs: structure, entry-point, hooks, tools, state, config, debugging
Workflows: create-plugin, add-hook

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 17:25:07 +00:00

1.3 KiB

Plugin Entry Point

Format

import type { OpenClawPluginApi } from "openclaw/plugin-sdk";

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

export default {
  id: "my-plugin",
  name: "My Plugin",
  register(api: OpenClawPluginApi) {
    const config = normalizeConfig(api);

    // Gateway lifecycle: only once
    if (!_G[LIFECYCLE_KEY]) {
      _G[LIFECYCLE_KEY] = true;
      // Start sidecars, init global resources
      api.on("gateway_stop", () => { _G[LIFECYCLE_KEY] = false; });
    }

    // Agent session hooks: every register() call (dedup inside handler)
    registerMyHook(api, config);

    // Tools
    registerMyTools(api, config);

    api.logger.info("my-plugin: registered");
  },
};

Why globalThis?

OpenClaw may hot-reload plugins. Module-level variables reset on reload, but globalThis persists. All mutable state must be on globalThis:

  • Startup flags → prevent double initialization
  • Dedup sets → prevent double hook execution
  • Runtime references → keep connections alive across reloads
  • Shared registries → preserve cross-plugin state

Naming Convention

_<pluginId>PluginXxx     # Internal state (e.g., _prismFacetRouters)
__<pluginId>             # Cross-plugin public API (e.g., __yonexusClient)