> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Slash Commands

> Reusable, parameterized prompt templates in markdown — invoked as /name on every surface, with zero-migration interop for 9 agent CLIs: Claude Code, Devin, Windsurf, Cursor, opencode, Codex, Gemini CLI, Qwen Code and GitHub Copilot.

Slash commands turn markdown files into **reusable, parameterized prompt templates**: drop `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

|                  | Skill                               | Slash command                         |
| ---------------- | ----------------------------------- | ------------------------------------- |
| What it is       | *Knowledge* — "how we do X here"    | *Action* — "do X now with these args" |
| Where it lands   | Guidance block in the system prompt | Becomes the user turn itself          |
| Lifecycle        | Sticky, ages out, re-triggers       | One-shot, consumed                    |
| Parameterization | Args attached alongside             | `$ARGUMENTS` / `$1`…`$9` interpolated |

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.

| Source             | Project                       | Global                               | Format |
| ------------------ | ----------------------------- | ------------------------------------ | ------ |
| **ChatCLI**        | `.chatcli/commands/`          | `~/.chatcli/commands/`               | md     |
| **Claude Code**    | `.claude/commands/`           | —                                    | md     |
| **Devin**          | `.devin/workflows/`           | —                                    | md     |
| **Windsurf**       | `.windsurf/workflows/`        | —                                    | md     |
| **Cursor**         | `.cursor/commands/`           | `~/.cursor/commands/`                | md     |
| **opencode**       | `.opencode/commands/`         | `~/.config/opencode/commands/`       | md     |
| **Codex (OpenAI)** | —                             | `~/.codex/prompts/` (top-level only) | md     |
| **Gemini CLI**     | `.gemini/commands/`           | `~/.gemini/commands/`                | TOML   |
| **Qwen Code**      | `.qwen/commands/`             | `~/.qwen/commands/`                  | TOML   |
| **GitHub Copilot** | `.github/prompts/*.prompt.md` | —                                    | md     |

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

```markdown theme={"system"}
---
description: Review a pull request with a given focus
argument-hint: <pr-number> [focus]
model: claude-sonnet-5
effort: high
allowed-tools: read, search
---
! gh pr view $1 --json title,body

Review PR $1 focusing on $ARGUMENTS.
Ground every finding in the diff above.
```

| Frontmatter        | Effect                                                                                                                  |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `description`      | Shown in the completer, `/menu`, ACP and MCP listings                                                                   |
| `argument-hint`    | Usage nudge shown when invoked without args                                                                             |
| `model` / `effort` | Route the expanded turn (same cross-provider hint plumbing as skills)                                                   |
| `allowed-tools`    | Restricts tools during the run the command initiates (see below)                                                        |
| `mode`             | `chat` or `coder` — where the expanded body should run (see below). Absent: inferred, `allowed-tools` present ⇒ `coder` |

**Placeholders** (every dialect's syntax works in every file):

| Placeholder  | Meaning                                                                            |
| ------------ | ---------------------------------------------------------------------------------- |
| `$ARGUMENTS` | Raw argument string, verbatim                                                      |
| `{{args}}`   | Gemini/Qwen alias for the same                                                     |
| `$1`…`$9`    | Positional arguments (`KEY=value` tokens excluded)                                 |
| `$KEY`       | Codex-style named argument: invoke with `/draftpr FILES="a b" PR_TITLE="Add hero"` |
| `$$`         | Literal `$`                                                                        |

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**:

1. Safety-immune commands (`rm -rf`, `sudo`, …) always require interactive approval — never auto-approved, not even by automode.
2. Your `/policy` rules apply (allow / ask / deny), and approval decisions can persist new rules (allow-always / deny-forever).
3. 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.

Lines inside fenced code blocks are **never** executed — documentation stays documentation.

***

## `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).

| Category               | Tools                                                                                                             |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Coder engine           | `@coder` (file ops, shell exec, git, tests — all subcommands)                                                     |
| Atomic file tools      | `@read` · `@search` · `@tree` · `@todo`                                                                           |
| User interaction       | `@ask`                                                                                                            |
| Web & research         | `@webfetch` · `@websearch` · `@wikipedia` · `@http` · `@api-explorer` · `@osv`                                    |
| Knowledge & memory     | `@context` · `@knowledge` · `@memory` · `@recall` · `@session` · `@compress` · `@registry-tags` · `@docs-flatten` |
| Agents & orchestration | `@agents` · `@moa` · `@board` · `@mail` · `@channels` · `@send` · `@park` · `@scheduler` · `@proc`                |
| Media & visuals        | `@image` · `@diagram` · `@graphview` · `@speak` · `@voice`                                                        |
| Dev & meta             | `@lsp` · `@model` · `@commands` · `@tools` · `@skill` · `@mcp-login`                                              |
| Slash-as-tool          | `@cmd:help` · `@cmd:version` · `@cmd:session-list`                                                                |

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](/features/mcp-integration) appear as `mcp_` + 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:

```markdown theme={"system"}
---
description: Open a GitHub issue for the failing tests
mode: coder
allowed-tools: @coder, @read, mcp_create_issue
---
Run the test suite, extract the failures, and open a GitHub issue
summarizing them with repro steps.
```

Any other MCP tool the model reaches for during that run escalates to an interactive ask instead of running silently.

***

## Execution mode & the chat auto-route

Chat mode is tool-less by design — a command whose body drives tools would just be refused there. The `mode:` key declares where the command is meant to run, and the chat dispatcher honors it:

* **`mode: coder`** (or inferred from a non-empty `allowed-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 and `allowed-tools` overlay 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: chat`** explicitly vetoes the inference — the command stays a plain conversational turn even if it declares `allowed-tools`.
* Absent or unrecognized values never invalidate the file: resolution falls back to the inference, then to `chat`.

Interop files from Claude Code and friends that carry `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

| Surface          | Behavior                                                                                                                                  |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| REPL chat        | `/name args` dispatches like any command; a coder-mode command auto-routes through a coder one-shot; completer + `/menu` list the catalog |
| `/coder` mid-run | Follow-ups and type-ahead expand too; `allowed-tools` re-arms the scope, `model`/`effort` are ignored mid-loop                            |
| One-shot         | `chatcli -p "/review-pr 1326"` expands before routing; coder-mode commands run through the coder engine                                   |
| Gateway          | A channel message `/deploy prod` expands before the coder engine sees it                                                                  |
| ACP (IDEs)       | Commands are advertised via `available_commands_update` with input hints, and expand in the prompt flow                                   |
| MCP server       | Served through the `prompts` primitive — `prompts/list` carries the spec `arguments` field, `prompts/get` accepts `{"args": "..."}`       |
| The model itself | The `@commands` builtin lets the agent discover (`list`) and expand (`get`) the team's playbooks mid-task                                 |

***

## Managing

```bash theme={"system"}
/config commands          # diagnostics panel (below)
/config commands reload   # force a re-scan (file edits are picked up automatically too)
/reload                   # also re-scans the catalog
```

The panel groups the catalog by source with counts and argument hints, then the failure ledgers — commands **refused** for shadowing a built-in and files **skipped** with the parse reason — and every scanned directory with a `✓`/`–` existence marker:

```
⌘ Slash Commands ─────────────────────────
  9 command(s) from 4 source(s)

  ▸ Project (.chatcli/commands) (2)
    /deploy       Deploy service          <env>
    /review-pr    Review a PR             <pr> [focus]

  ▸ Gemini CLI (1)
    /git:commit   Writes a conventional commit

  ⚠ Skipped (failed to parse — fix the file and it loads on the next scan):
    ~/.gemini/commands/broken.toml
      unterminated """ string

  Scanned directories (precedence order):
    ✓ ~/project/.chatcli/commands
    – ~/project/.claude/commands
```

The catalog is served from a stat-fingerprint cache: lookups are free until a file actually changes.

| Environment variable         | Default | Effect                                                             |
| ---------------------------- | ------- | ------------------------------------------------------------------ |
| `CHATCLI_COMMANDS`           | `true`  | Master switch for the whole feature                                |
| `CHATCLI_COMMANDS_AUTOROUTE` | `true`  | Auto-route coder-mode commands from chat into a coder one-shot run |

***

## Example: team standup

```bash theme={"system"}
mkdir -p .chatcli/commands
cat > .chatcli/commands/standup.md <<'EOF'
---
description: Standup summary from recent commits
argument-hint: [days]
---
! git log --oneline --since="$1 days ago" | head -20

Based on the commits above, write a 3-bullet standup summary.
EOF
```

Commit it — everyone on the team (and their agents, via `@commands`) now has `/standup 2`.
