> ## 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`, `mode`, `auto_execute_steps`) are tolerated; opencode's `model` maps to ChatCLI's model hint. 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)      |

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

***

## Surfaces

| Surface          | Behavior                                                                                                                            |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| REPL chat        | `/name args` dispatches like any command; 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                                                                               |
| 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 |

***

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