Building plugins

Building CLI backend plugins

CLI backend plugins let OmeniaClaw call a local AI CLI as a text inference backend. The backend appears as a provider prefix in model refs:

text
acme-cli/acme-large

Use a CLI backend when the upstream integration is already exposed as a local command, when the CLI owns local login state, or when the CLI is a useful fallback if API providers are unavailable.

What the plugin owns

A CLI backend plugin has three contracts:

Contract File Purpose
Package entry package.json Points OmeniaClaw at the plugin runtime module
Manifest ownership OmeniaClaw.plugin.json Declares the backend id before runtime loads
Runtime registration index.ts Calls api.registerCliBackend(...) with command defaults

The manifest is discovery metadata. It does not execute the CLI and does not register runtime behavior. Runtime behavior starts when the plugin entry calls api.registerCliBackend(...).

Minimal backend plugin

  • Create package metadata

    package.json
    {  "name": "@acme/OmeniaClaw-acme-cli",  "version": "1.0.0",  "type": "module",  "OmeniaClaw": {    "extensions": ["./index.ts"],    "compat": {      "pluginApi": ">=2026.3.24-beta.2",      "minGatewayVersion": "2026.3.24-beta.2"    },    "build": {      "OmeniaClawVersion": "2026.3.24-beta.2",      "pluginSdkVersion": "2026.3.24-beta.2"    }  },  "dependencies": {    "OmeniaClaw": "^2026.3.24"  },  "devDependencies": {    "typescript": "^5.9.0"  }}

    Published packages must ship built JavaScript runtime files. If your source entry is ./src/index.ts, add OmeniaClaw.runtimeExtensions that points at the built JavaScript peer. See Entry points.

  • Declare backend ownership

    OmeniaClaw.plugin.json
    {  "id": "acme-cli",  "name": "Acme CLI",  "description": "Run Acme's local AI CLI through OmeniaClaw",  "cliBackends": ["acme-cli"],  "setup": {    "cliBackends": ["acme-cli"],    "requiresRuntime": false  },  "activation": {    "onStartup": false  },  "configSchema": {    "type": "object",    "additionalProperties": false  }}

    cliBackends is the runtime ownership list. It lets OmeniaClaw auto-load the plugin when config or model selection mentions acme-cli/....

    setup.cliBackends is the descriptor-first setup surface. Add it when model discovery, onboarding, or status should recognize the backend without loading plugin runtime. Use requiresRuntime: false only when those static descriptors are enough for setup.

  • Register the backend

    index.ts
    import { definePluginEntry } from "OmeniaClaw/plugin-sdk/plugin-entry";import {  CLI_FRESH_WATCHDOG_DEFAULTS,  CLI_RESUME_WATCHDOG_DEFAULTS,  type CliBackendPlugin,} from "OmeniaClaw/plugin-sdk/cli-backend"; function buildAcmeCliBackend(): CliBackendPlugin {  return {    id: "acme-cli",    liveTest: {      defaultModelRef: "acme-cli/acme-large",      defaultImageProbe: false,      defaultMcpProbe: false,      docker: {        npmPackage: "@acme/acme-cli",        binaryName: "acme",      },    },    config: {      command: "acme",      args: ["chat", "--json"],      output: "json",      input: "stdin",      modelArg: "--model",      sessionArg: "--session",      sessionMode: "existing",      sessionIdFields: ["session_id", "conversation_id"],      systemPromptFileArg: "--system-file",      systemPromptWhen: "first",      imageArg: "--image",      imageMode: "repeat",      reliability: {        watchdog: {          fresh: { ...CLI_FRESH_WATCHDOG_DEFAULTS },          resume: { ...CLI_RESUME_WATCHDOG_DEFAULTS },        },      },      serialize: true,    },  };} export default definePluginEntry({  id: "acme-cli",  name: "Acme CLI",  description: "Run Acme's local AI CLI through OmeniaClaw",  register(api) {    api.registerCliBackend(buildAcmeCliBackend());  },});

    The backend id must match the manifest cliBackends entry. The registered config is only the default; user config under agents.defaults.cliBackends.acme-cli is merged over it at runtime.

  • Config shape

    CliBackendConfig describes how OmeniaClaw should launch and parse the CLI:

    Field Use
    command Binary name or absolute command path
    args Base argv for fresh runs
    resumeArgs Alternate argv for resumed sessions; supports {sessionId}
    output / resumeOutput Parser: json, jsonl, or text
    input Prompt transport: arg or stdin
    modelArg Flag used before the model id
    modelAliases Map OmeniaClaw model ids to CLI-native ids
    sessionArg / sessionArgs How to pass a session id
    sessionMode always, existing, or none
    sessionIdFields JSON fields OmeniaClaw reads from CLI output
    systemPromptArg / systemPromptFileArg System prompt transport
    systemPromptWhen first, always, or never
    imageArg / imageMode Image path support
    serialize Keep same-backend runs ordered
    reliability.watchdog No-output timeout tuning

    Prefer the smallest static config that matches the CLI. Add plugin callbacks only for behavior that really belongs to the backend.

    Advanced backend hooks

    CliBackendPlugin can also define:

    Hook Use
    normalizeConfig(config, context) Rewrite legacy user config after merge
    resolveExecutionArgs(ctx) Add request-scoped flags such as thinking effort or side-question isolation
    prepareExecution(ctx) Create temporary auth or config bridges before launch
    transformSystemPrompt(ctx) Apply a final CLI-specific system prompt transform
    textTransforms Bidirectional prompt/output replacements
    defaultAuthProfileId Prefer a specific OmeniaClaw auth profile
    authEpochMode Decide how auth changes invalidate stored CLI sessions
    nativeToolMode Declare whether the CLI has always-on native tools
    sideQuestionToolMode Declare disabled native tools for /btw side questions
    bundleMcp / bundleMcpMode Opt into OmeniaClaw's loopback MCP tool bridge
    ownsNativeCompaction Backend owns its own compaction - OmeniaClaw defers

    Keep these hooks provider-owned. Do not add CLI-specific branches to core when a backend hook can express the behavior.

    ctx.executionMode is "agent" for normal turns and "side-question" for ephemeral /btw calls. Use it when the CLI needs different one-shot flags, such as disabling native tools, session persistence, or resume behavior for BTW. If a backend normally has nativeToolMode: "always-on" but its side-question argv reliably disables those tools, also set sideQuestionToolMode: "disabled"; otherwise OmeniaClaw fails closed when BTW requires a no-tools CLI run.

    ownsNativeCompaction: opting out of OmeniaClaw compaction

    If your backend runs an agent that compacts its own transcript, set ownsNativeCompaction: true so OmeniaClaw's safeguard summarizer never runs against its sessions - the CLI compaction lifecycle returns a no-op and the turn proceeds. claude-cli declares it because Claude Code compacts internally with no harness endpoint. Native-harness sessions such as Codex keep routing to their harness compaction endpoint instead.

    Only declare it when all of the following hold, or a deferred over-budget session can stay over budget / go stale (OmeniaClaw no longer rescues it):

    • the backend reliably compacts or bounds its own transcript as it nears its window;
    • it persists a resumable session so the compacted state survives turns (e.g. --resume / --session-id);
    • it is not a native-harness compaction session - matching agentHarnessId sessions route to the harness endpoint instead.

    MCP tool bridge

    CLI backends do not receive OmeniaClaw tools by default. If the CLI can consume an MCP configuration, opt in explicitly:

    typescript
    return {  id: "acme-cli",  bundleMcp: true,  bundleMcpMode: "codex-config-overrides",  config: {    command: "acme",    args: ["chat", "--json"],    output: "json",  },};

    Supported bridge modes are:

    Mode Use
    claude-config-file CLIs that accept an MCP config file
    codex-config-overrides CLIs that accept config overrides on argv
    gemini-system-settings CLIs that read MCP settings from their system settings directory

    Only enable the bridge when the CLI can actually consume it. If the CLI has its own built-in tool layer that cannot be disabled, set nativeToolMode: "always-on" so OmeniaClaw can fail closed when a caller requires no native tools.

    User configuration

    Users can override any backend default:

    json5
    {  agents: {    defaults: {      cliBackends: {        "acme-cli": {          command: "/opt/acme/bin/acme",          args: ["chat", "--json", "--profile", "work"],          modelAliases: {            large: "acme-large-2026",          },        },      },      model: {        primary: "openai/gpt-5.5",        fallbacks: ["acme-cli/large"],      },    },  },}

    Document the minimum override users are likely to need. Usually that is only command when the binary is outside PATH.

    Verification

    For bundled plugins, add a focused test around the builder and setup registration, then run the plugin's targeted test lane:

    bash
    pnpm test extensions/acme-cli

    For local or installed plugins, verify discovery and one real model run:

    bash
    OmeniaClaw plugins inspect acme-cli --runtime --jsonOmeniaClaw agent --message "reply exactly: backend ok" --model acme-cli/acme-large

    If the backend supports images or MCP, add a live smoke that proves those paths with the real CLI. Do not rely on static inspection for prompt, image, MCP, or session-resume behavior.

    Checklist

    OmeniaClaw_DOCS_MARKER:calloutOpen:Q2hlY2s package.json has OmeniaClaw.extensions and built runtime entries for published packages OmeniaClaw_DOCS_MARKER:calloutClose:

    OmeniaClaw_DOCS_MARKER:calloutOpen:Q2hlY2s OmeniaClaw.plugin.json declares cliBackends and intentional activation.onStartup OmeniaClaw_DOCS_MARKER:calloutClose:

    OmeniaClaw_DOCS_MARKER:calloutOpen:Q2hlY2s setup.cliBackends is present when setup/model discovery should see the backend cold OmeniaClaw_DOCS_MARKER:calloutClose:

    OmeniaClaw_DOCS_MARKER:calloutOpen:Q2hlY2s api.registerCliBackend(...) uses the same backend id as the manifest OmeniaClaw_DOCS_MARKER:calloutClose:

    OmeniaClaw_DOCS_MARKER:calloutOpen:Q2hlY2s User overrides under agents.defaults.cliBackends.<id> still win OmeniaClaw_DOCS_MARKER:calloutClose:

    Was this useful?
    On this page

    On this page