review-pr.md into .chatcli/commands/ and /review-pr 1326 security becomes a full, expanded prompt — in the REPL, inside a running /coder session, in one-shot -p scripts, through the messaging gateway, and over ACP and MCP. Expansion happens before the request is built, so commands work identically with every provider ChatCLI supports. And if your team already keeps commands for Claude Code, Devin, Windsurf, Cursor, opencode, Codex, Gemini CLI, Qwen Code or GitHub Copilot, those files work here unchanged — zero migration.
Commands vs. skills
Both appear in the completer and in the
/menu palette.
Where commands live
Scanned in precedence order — first hit for a name wins. Native ChatCLI dirs come first, then the interop matrix: if your team already uses any of these agent CLIs, their command files work in ChatCLI unchanged.
Subdirectories become namespaces:
frontend/deploy.md → /frontend:deploy (Gemini’s git/commit.toml → /git:commit). Foreign frontmatter keys (agent, subtask, auto_execute_steps) are tolerated; opencode’s model maps to ChatCLI’s model hint, and mode is a first-class ChatCLI key (see the execution-mode section below). Frontmatter that isn’t even valid YAML — like Codex’s own documented argument-hint: [FILES=<paths>] shape — falls back to a line-wise read instead of dropping the file. A command can never shadow a built-in command — files named session.md, config.md etc. are refused and reported in /config commands.
Anatomy of a command
Placeholders (every dialect’s syntax works in every file):
Unknown
$WORDS pass through untouched.
Pre-execution lines (!)
A line starting with ! runs a shell command and embeds its output into the expanded prompt. The inline dialects work too — Gemini’s !{cmd} and opencode’s !`cmd` substitute in place mid-sentence. Every occurrence, whole-line or inline, goes through the same security gate as coder tools:
- Safety-immune commands (
rm -rf,sudo, …) always require interactive approval — never auto-approved, not even by automode. - Your
/policyrules apply (allow / ask / deny), and approval decisions can persist new rules (allow-always / deny-forever). - On unattended surfaces (gateway, MCP, ACP, scheduler) an “ask” resolves through
/policy automode— or fails safe to deny. A denied line is replaced by an explicit marker so the model knows the output is missing, never silently empty.
allowed-tools
When set, the run initiated by the command gets an ephemeral overlay on the security gate: a tool outside the list escalates from allow to ask — the human (or policy automode) arbitrates the exception. It never silently widens permissions and never silently denies.
Matching is case-insensitive and the leading @ is optional (coder ≡ @coder). Names are matched exactly against the tool names below — a name outside the catalog never matches, which means every tool call in that run escalates to ask. In particular, Claude Code’s tool names (Read, Write, Edit, Bash) do not exist in ChatCLI — don’t copy them into a command’s frontmatter.
Available tool names
The coder engine is a single tool,@coder — read, write, patch, multipatch, search, tree, exec, git-status, git-diff, git-log, test, rollback and friends are its subcommands, not standalone tools. Allowing @coder allows all of them (each execution still goes through /policy and the interactive gate).
External plugins from
~/.chatcli/plugins participate under their own names (e.g. @docker-ps). @model is only registered while the model-routing tool is enabled (CHATCLI_AGENT_MODEL_TOOL).
MCP tools
Tools from connected MCP servers appear asmcp_ + the tool’s own name exactly as the server declares it — no server prefix. A server named github declaring create_issue becomes mcp_create_issue. Example:
Execution mode & the chat auto-route
Chat mode is tool-less by design — a command whose body drives tools would just be refused there. Themode: key declares where the command is meant to run, and the chat dispatcher honors it:
mode: coder(or inferred from a non-emptyallowed-tools): invoked in REPL chat, the command prints a notice (⚡ /deploy runs with tools…) and auto-routes through a coder one-shot run — same engine, security gate andallowed-toolsoverlay as a manual/coder, returning to the chat prompt when the loop reaches its final answer.chatcli -p "/deploy prod"routes the same way (notice on stderr).mode: chatexplicitly vetoes the inference — the command stays a plain conversational turn even if it declaresallowed-tools.- Absent or unrecognized values never invalidate the file: resolution falls back to the inference, then to
chat.
allowed-tools therefore run correctly with zero edits. Routed commands are marked [coder] in the completer, /menu and /config commands.
Headless surfaces (gateway, ACP, MCP, scheduler) keep their existing behavior — the auto-route only fires on the interactive REPL and the -p one-shot. Opt out globally with CHATCLI_COMMANDS_AUTOROUTE=off.
Surfaces
Managing
✓/– existence marker:
Example: team standup
@commands) now has /standup 2.