npx claudepluginhub greenpolo/cc-multi-cli-plugin --plugin multiThis skill uses the workspace's default tool permissions.
cc-multi-cli-plugin is a **multi-plugin marketplace**. Adding a new CLI means adding a new plugin to the marketplace plus wiring a new adapter into the shared companion runtime in the `multi` plugin.
Customizes cc-multi-cli-plugin: rewires CLIs to roles, adds/disables subagents/commands, restricts CLIs, hardcodes models, or troubleshoots quirks via env vars/config files.
Guides building Claude Code plugins: architecture, directory structure, manifests, components (commands, agents, skills, hooks, MCP servers), and marketplace distribution.
Provides expert guidance on Anthropic's Claude Code CLI including setup, flags, CLAUDE.md optimization, hooks, MCPs, sub-agents, workflows, permissions, and troubleshooting.
Share bugs, ideas, or general feedback.
cc-multi-cli-plugin is a multi-plugin marketplace. Adding a new CLI means adding a new plugin to the marketplace plus wiring a new adapter into the shared companion runtime in the multi plugin.
Before writing any code, pull everything you need to know about the CLI so you don't have to guess or ask the user later. Extract:
cursor-agent? agent? qwen? aider?)auto if offered). Hardcoding a wrong model string causes 400 errors at runtime./research, /review, /plan, /debug, /ask, or whatever the CLI exposes. These determine which roles you'll map to slash-command prefixes in buildPrompt().--help actually use?Pick sources proportional to the question. Do NOT run every source for every fact. Start cheap and authoritative; escalate only if unclear.
Before doing anything else, search exa for an existing implementation. The community has wired up many CLIs already (Cursor's ACP adapter, the Gemini plugin port, etc.) — finding one collapses days of trial-and-error into "read their adapter, adapt to our marketplace structure."
Useful queries (try in this order, stop when you find a working example):
<cli-name> claude code plugin
<cli-name> ACP adapter
<cli-name> claude-code marketplace
<cli-name> agent client protocol
<cli-name> @anthropic-ai claude
A reference implementation reveals things probes don't:
shell: true on Windows? A specific env var to init?-preview suffix trap is the canonical example.)If you find one:
initialize call, custom auth methods, etc.).If you don't find one (or it's stale / not maintained), proceed with the verification ladder below. Looking is cheap; not looking is how you spend 4 hours debugging an ACP handshake that needed one specific param.
<cli> --help, <cli> models, <cli> about — fastest, no network, authoritative for "what does this binary accept right now."<cli> -p "List the exact <thing> strings this CLI accepts. One per line."
resolve-library-id → query-docs. Good for canonical names and deprecation context.config/models.ts constants. Slowest; use only when 1–4 disagree or come up empty.For a yes/no question ("does this CLI have ACP?") use source 1 or 3 and stop. For a canonical ID that gets hardcoded into files, use 1 plus 3 or 4 to cross-check — two sources is enough.
Hard rules:
-preview, -beta, -exp). Don't hardcode the unsuffixed variant — it will 404 at runtime. Gemini 3.x IDs all end in -preview.plugins/multi/scripts/lib/adapters/ as reference templates for the transport pattern you'll reuse.Proceed without asking the user to confirm facts you can verify yourself.
Check in this order:
ACP is a cross-vendor standard. Check the CLI's --help output for:
--acp flag (used by Gemini, Copilot)acp subcommand (used by Cursor)--stdio or --server mode that outputs NDJSON/JSON-RPCIf yes, this is the easy path. Skip to "ACP integration" below.
ASP is OpenAI's flavor — HTTP + SSE, as used by Codex (codex --app-server). If the CLI has a similar flag, you can reuse much of the Codex adapter pattern.
Some CLIs have -p or --print modes with --output-format=json or similar. Not as rich as ACP/ASP but workable — wrap subprocess invocation and parse the JSON stream.
The CLI is probably not a good fit yet. Suggest filing a feature request upstream for ACP support.
Use the CLI's brand name, lowercased. E.g., qwen, opencode, aider. This becomes the --cli <name> flag value and the slash-command namespace.
multiCopy the canonical Cursor adapter as a template:
cp plugins/multi/scripts/lib/adapters/cursor.mjs plugins/multi/scripts/lib/adapters/<new-cli>.mjs
Edit the new file:
*Cursor to *<NewCli> (e.g., runAcpPromptCursor → runAcpPromptQwen).buildPrompt(role, userTask) — map role names to slash-command prefixes the CLI understands.args: ["acp"]. ACP flag: args: ["--acp"] or ["--acp", "--stdio"].adapter export:
export const adapter = {
name: "<new-cli>",
isAvailable: get<NewCli>Availability,
isAuthenticated: get<NewCli>AuthStatus,
invoke: runAcpPrompt<NewCli>,
cancel: interruptAcpPrompt<NewCli>,
getSession: undefined,
};
node --check plugins/multi/scripts/lib/adapters/<new-cli>.mjs.Edit plugins/multi/scripts/multi-cli-companion.mjs:
import * as <newCli> from "./lib/adapters/<new-cli>.mjs";
ADAPTERS:
const ADAPTERS = { codex, gemini, cursor, copilot, <newCli> };
executeTaskRun's dispatch with an else if (cli === "<new-cli>") branch mirroring the cursor branch exactly, substituting <newCli>.adapter.invoke(...).buildTaskRunMetadata's label map so jobs get a CLI-specific title.node --check plugins/multi/scripts/multi-cli-companion.mjs.ACP is a standard, but every CLI implements it slightly differently. The contract a CLI advertises in --help doesn't always match what its acp mode actually does. Before declaring the integration done, run a real prompt that exercises the tools the user will rely on (shell exec, file writes, MCP, web search) and watch the wire.
Set ACP_TRACE=1 and check a real run end-to-end:
ACP_TRACE=1 node plugins/multi/scripts/multi-cli-companion.mjs task \
--cli <new-cli> --role <role> --write \
"Run \`echo hello\`, then write a file, then call an MCP tool."
Then look at stderr for incoming JSON-RPC traffic from the agent. The [acp-trace] <- REQ <method> lines tell you which client-side ACP methods the agent expects you to provide; <- NOTIF session/update[<kind>] lines show streaming progress.
Things to verify (don't have to be in order — just check whichever is relevant for what the user wants this CLI to do):
session/request_permission over ACP, or does it gate tool use through some out-of-band mechanism (a config file, a flag, a pre-approval list)? If you see tool_call_update go to in_progress and never completed with no incoming REQs, it's almost always an out-of-band gate. (Cursor, for example, uses ~/.cursor/cli-config.json's permissions.allow array — see ensureCursorAllowlist in cursor.mjs.)terminal/* RPCs themselves (just declare clientCapabilities.terminal=true in the handshake and acp-client.mjs's buildAutoApproveRequestHandler services them). Others run terminals internally and skip ACP entirely. Watching for <- REQ terminal/create tells you which.session/new accepts an mcpServers array, but some agents silently ignore it in ACP mode (Cursor staff has confirmed this for agent acp). If the agent reports your MCP tools as missing, see if the CLI has a per-CLI MCP config file (e.g. ~/.cursor/mcp.json) you should populate instead.session/set_mode semantics vary: for Cursor, "agent" gives full tool access while "plan"/"ask" restrict it; for Gemini, the equivalent is approvalMode: "yolo" for max permissions. Try setting and not setting it during testing.--yolo / --force / --approve-mcps flags often don't apply to ACP mode — they're for the interactive REPL or -p print mode. Don't assume they're a fix; verify on the wire.The Cursor adapter is the worked example for everything above. When in doubt, read plugins/multi/scripts/lib/adapters/cursor.mjs end-to-end — it shows the full set of workarounds that actually shipped: handshake capability declaration, mode-setting per role, allowlist file injection, version-specific warnings, and spawn flags. Adapt the patterns that apply to the new CLI; not all of them will.
For straightforward CLIs (clean ACP impl, no out-of-band gates), most of the above will be no-ops and the basic adapter scaffold from Step 2 will just work. Don't add guards for problems the CLI doesn't have.
Create one subagent file per role in plugins/multi/agents/<new-cli>-<role>.md:
---
name: <new-cli>-<role>
description: Use when the user asks for <role-appropriate tasks> via <NewCli>.
model: sonnet
tools: Bash
---
You are a thin forwarding wrapper around the cc-multi-cli-plugin companion runtime for <NewCli>.
Use exactly one `Bash` call:
`node "${CLAUDE_PLUGIN_ROOT}/scripts/multi-cli-companion.mjs" task --cli <new-cli> --role <role> ...`
Preserve task text verbatim. Return stdout exactly. No commentary.
Subagent names are <cli>-<role> (e.g., qwen-writer). Invocation path: subagent_type: "multi:<cli>-<role>".
mkdir -p plugins/<new-cli>/.claude-plugin plugins/<new-cli>/commands
Write plugins/<new-cli>/.claude-plugin/plugin.json:
{
"name": "<new-cli>",
"description": "Delegate <roles> to <NewCli> CLI. Part of cc-multi-cli-plugin. Requires the `multi` plugin.",
"version": "2.0.0",
"author": { "name": "greenpolo", "url": "https://github.com/greenpolo" },
"license": "Apache-2.0",
"keywords": ["claude-code", "<new-cli>", "<role>", "acp"]
}
One markdown per role in plugins/<new-cli>/commands/<role>.md:
---
description: <what this does>
argument-hint: "[--model <model>] <what to do>"
allowed-tools: Bash(node:*), AskUserQuestion, Agent
---
Dispatch to the `multi:<new-cli>-<role>` subagent via the `Agent` tool.
Raw user request:
$ARGUMENTS
Return the subagent's output verbatim.
Command filename becomes the part after the colon in the slash: plugins/qwen/commands/write.md → /qwen:write.
Edit .claude-plugin/marketplace.json (at the repo root) and add a new entry to the plugins array:
{
"name": "<new-cli>",
"description": "Adds /<new-cli>:<roles>. Requires multi.",
"version": "2.0.0",
"author": { "name": "greenpolo" },
"source": "./plugins/<new-cli>"
}
Validate: claude plugin validate <repo-root> should pass.
claude plugin marketplace update cc-multi-cli-plugin
claude plugin install <new-cli>@cc-multi-cli-plugin
claude plugin install multi@cc-multi-cli-plugin --force # pick up new subagent files
Restart Claude Code. Try /<new-cli>:<role> <prompt>.
ASP requires a different transport (HTTP + SSE vs stdio JSON-RPC). If the new CLI uses ASP-style servers:
plugins/multi/scripts/lib/app-server.mjs and plugins/multi/scripts/lib/adapters/codex.mjs.codex.mjs instead of cursor.mjs.ADAPTERS registration and plugin scaffolding are protocol-agnostic.This is more code than ACP integration — only take this path if the new CLI genuinely requires it.
If the CLI lacks ACP/ASP but has a headless JSON-output mode, write an adapter that:
-p --output-format=json (or equivalent).{ sessionId, text, fileChanges, error }.The adapter export interface (name, isAvailable, invoke, etc.) stays identical — only the transport differs.
OpenCode has been tested successfully via ACP. Qwen and Aider have similar ACP support and should work the same way. Any CLI that speaks a structured protocol is a candidate.
plugins/multi/scripts/lib/acp-client.mjs, job-control.mjs, state.mjs, render.mjs — shared infrastructure.plugins/multi/scripts/lib/adapters/codex.mjs, gemini.mjs, cursor.mjs, copilot.mjs — existing adapters.plugins/multi/hooks/hooks.json — unless the new CLI specifically needs a hook.After adding a new CLI, consider contributing the adapter back upstream. The plugin welcomes new CLIs that demonstrate working adapters — it's part of why the architecture is modular.