Commands
commands/ holds slash commands: saved prompts a person starts by name, such as /deploy. The body is the prompt the tool sends.
Use a skill instead when the model should pick the workflow itself, or when it needs bundled files. Set outputs.<target>.emit-skills-as-commands: true to get both from one skill.
Write one
agnostic-ai new command review-changes
It creates .agnostic-ai/commands/review-changes.md. Write the prompt, run agnostic-ai sync, then type /review-changes in a supported tool.
---
name: review-changes
description: Review the current branch against main.
x-claude:
allowed-tools: [Bash(git diff:*), Read, Grep]
---
Diff the branch against `main` and report bugs with `file:line`, most severe first.
Fields
| Field | Required | Default | Description |
|---|---|---|---|
name | no | filename | Command identifier and slash name, such as /deploy. |
description | no | empty | Short description shown in slash-command pickers. |
argument-hint | no | empty | Hint shown after the command in Claude Code, Augment, and Factory. |
Other frontmatter is kept. Put keys for one tool under x-<target>, like x-claude.allowed-tools above.
Output
Each target page lists the exact directory. Gemini writes each command as a .toml file named after name. Codex without outputs.codex.commands-dir, Copilot, Zed, Windsurf, and Crush get each command as a skill (below). Other tools without command support skip it with a note.
Tools without commands
Tools with skills but no commands can receive commands as skills. The tool must support preventing the model from starting them: Codex, Copilot, Zed, Windsurf, and Crush. All but Codex qualify only while no other tool writes their skills folder, since a shared SKILL.md cannot keep the field. Cline, Amp, Warp, Antigravity, Continue, Goose, and OpenHands document no such switch, so they skip commands. The skill:
- is written at the top level of the skills folder, whatever folder the command sits in under
commands/; - sets
disable-model-invocation: true, which Codex reads asallow_implicit_invocation: falseinagents/openai.yaml, Copilot and Zed keep inSKILL.md, Windsurf writes astriggers: [user], and Crush writes withuser-invocable: true; - keeps
name,description, the target fields, andargument-hintwhere the tool reads it, and notes any other field it leaves out. A target field that decides who starts the skill,disable-model-invocation,triggers, oruser-invocable, is left out, so the skill stays user-run; - opens with one line asking the model to run it only when called, and to treat the text typed after its name as its arguments, since these tools do not fill in
$ARGUMENTSin a skill. Sync notes each$ARGUMENTSor$1line, andon-unsupported: errordoes not fail on them.
A skill spec with the same name (ignoring case), or a hand-written skill folder of that name the tool reads, wins, and sync notes the skipped command. A tool that reads the same skills folder, such as Gemini CLI reading the copy Codex writes to .agents/skills, also sees the skill, even with target-exclude; sync notes that it lists the command twice, or that its model can start it.
Claude Code body syntax
A Claude Code command body can use !`command` lines, ```! blocks, $ARGUMENTS, and $1, $2, ... (skill syntax). Sync copies the body as written. Support follows each tool's docs:
| Target | !`command` | $ARGUMENTS | $1, $2, ... |
|---|---|---|---|
| Claude Code | yes | yes | yes |
| OpenCode | yes | yes | yes |
| Codex | no | yes | yes |
| Augment, Factory | no | yes | no |
| Kiro CLI V3 | no | yes | no |
| Cursor, Gemini, Junie, Kilo, Qoder, Trae | no | no | no |
A tool that reads the syntax as plain text gets a note naming each line; on-unsupported: error fails the sync, and lint reports LINT019. Write a tool's own placeholders in a ::target fence: Gemini's argument and shell forms (custom commands), or Kiro's ${1} through ${10} and ${@}, which sync keeps as written (it does not map a bare $1).