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:
acme-cli/acme-largeUse 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
{ "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
{ "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
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
agentHarnessIdsessions 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:
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:
{ 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:
pnpm test extensions/acme-cliFor local or installed plugins, verify discovery and one real model run:
OmeniaClaw plugins inspect acme-cli --runtime --jsonOmeniaClaw agent --message "reply exactly: backend ok" --model acme-cli/acme-largeIf 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: