bots-garden/mini-mepublic Fork
⑂ v1.0.0
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 4478b61 · on v1.0.0 · k33g · 12h ago
slash-commands.md · 221 lines · 20.7 KBmarkdown
Blame HistoryOpen raw

Reference: slash commands and keyboard shortcuts

Neutral description of the commands recognised at the terminal prompt of mm. They are matched on the whole trimmed line; anything else is sent to the model.

Commands

Command At the prompt During a generation
/quit Exits the program. Cancels the generation, prints Goodbye., exits.
/abort Prints ⚠️ No generation in progress. Cancels the generation, prints 🛑 Aborting...; the commands already run stay in the history.
/compact Compresses the history now, regardless of the threshold; prints the 🗜️ report, or 🗜️ nothing to compact when no turn is older than context.keepLastTurns. Sent to the model as a new message (see below).
/new Clears the history back to the system prompt, resets the loop detector, forgets the server's last token count and the conversation's plans; prints 🆕 new session: N message(s) forgotten (N counts every message except the system prompt). Treated like any other text: cancels the generation, keeps its partial history, then runs as /new on that history.
/sessions Lists the project's stored sessions, newest first: id, last update, question count, first question, then · ⚠️ N error(s) when the session recorded errors; * marks the current one. With sessions.enabled: false: ⚠️ sessions are off (set sessions.enabled: true in the YAML). Sent to the model as a new message.
/resume [id] Continues a stored session: the most recently updated one other than the current, skipping empty ones, or the named id. Prints ↩️ resumed <id> — N message(s), started with "<first question>" (plus · ⚠️ N error(s) when the session recorded errors); resets the loop detector, the server's token count and the plans; later turns are saved to that session. Refused with a ⚠️ reason when sessions are off, when there is no earlier session, when the id is unknown or is the current session. Sent to the model as a new message.
/help Prints the commands of this front end, one per line with their description, aligned. Sent to the model as a new message.
/copy [n|all] Copies the last answer (n: the n-th from the end) to the clipboard through pbcopy/wl-copy/xclip/xsel or, failing that, OSC 52 when stdout is a terminal, and prints 📋 copied N chars (<road>) or ⚠️ …. /copy all is window-mode only: here it says so. Sent to the model as a new message.
/save <file> [n] Writes the last answer (n: the n-th from the end) as it is to a new file, creating the missing directories, and prints 💾 saved the last answer to <file> (N bytes). Never overwrites: an existing file gives ⚠️ <file> already exists — nothing was written; choose another name. A file name with spaces is not supported. The model is not called. Sent to the model as a new message.
/memory <words> With sessions.enabled and sessions.searchMemory: searches the project's stored sessions and prints 🔎 N hit(s) for "<words>" in <dir>, then two lines per hit — <session id> · <date> · turn <n> · question|answer|summary and the text around the words found, plus → <answer> under a question (the answer its turn ended on) — or 🔎 no hit for "<words>" in <dir>. Case and accents do not count; words of one or two letters are left out beside longer ones; a hit holds at least one of the words; hits are ranked by the number of words held, then newest first; 10 at most. Questions, answers and /compact summaries are searched, not tool calls; the conversation in progress is left out. Without words: ⚠️ nothing to search: give one or more words. The model is not called; the history is not touched. Sent to the model as a new message.
/docs <words> With docSearch.searchDocs: searches the text files of docSearch.dirs with BM25 and prints 🔎 N passage(s) for "<words>" — F file(s) in <dirs>, then per passage a line ── <path>:<line> · <heading › subheading> and the passage as written; or 🔎 no passage for "<words>" — F file(s) in <dirs>. Case and accents do not count; words of 4+ letters also match longer words and plurals; 5 passages at most. Without words: ⚠️ nothing to search: give one or more words. The model is not called; the history is not touched. Sent to the model as a new message.
/unload [model] With unloadModels (default on): unloads a model from the server's memory — this agent's own, or the one named — and prints one line: 🧹 unloaded <model>, 🧹 <model> was not loaded: nothing to unload, or 🧹 cannot unload <model>: <reason> (a provider or a server that has no way to). The history is not touched; the next question reloads the agent's model. Sent to the model as a new message.

With sessions.enabled, /new leaves the current session's file as it is; the next question opens a new file. An empty line at the prompt is ignored.

Asking another agent

Present only when mmConfigs declares at least one agent; /help and the / picker list them. <name> is a key of mmConfigs.

Command Front ends Effect
/ask-<name> <prompt> terminal, window, editor Sends <prompt> alone to <name>.
/ask-with-context-<name> <prompt> terminal, window, editor Sends a transcript of the current conversation, then <prompt>.
/ask <name> <prompt> terminal, window Same as /ask-<name> <prompt>.
/ask-with-context <name> <prompt> terminal, window Same as /ask-with-context-<name> <prompt>.

While it runs:

Item Behaviour
First line 🗣 asking <name> (<provider>/<model>)…, or …, with the conversation… for the context forms.
<name>'s answer Streamed where answers appear. Its reasoning is shown as reasoning when showThinking is on.
<name>'s tool calls Shown as tool calls titled <name> · <tool>: <args>. Permission requests go through this front end: none in the line mode (allowed), permissions in the window (a/s/r, "for this session" keyed on <name>/<tool>), the editor's dialog over ACP.
/abort, Esc, Ctrl+C, session/cancel Cancel the ask; 🛑 Ask aborted — nothing kept.
/quit Cancels the ask and exits.
Any other line (line mode) ⚠️ <name> is still answering — /abort to stop it.; the line is dropped.

When it ends:

Outcome History Message
Answered Two messages appended: user [asked <name> (<provider>/<model>)[, with this conversation as context]: <prompt>], assistant [<name>'s answer] + the answer (capped at maxOutput characters, beginning and end kept). Saved with the session. 🗣 <name>'s answer is in the history for your next question
Cancelled Unchanged. 🛑 Ask aborted — nothing kept.
Failed Unchanged; recorded in the session file with where: "ask". [error: …] (terminal, window) or a JSON-RPC error (editor).

The transcript sent by the context forms holds the user's questions, the assistant's answers, one line per tool call (Assistant ran <tool>: <args>, args cut at 160 characters) and the compaction summary if any; never the system prompt, tool outputs or reasoning. Capped at maxOutput characters, newest lines kept, [… earlier conversation omitted …] marking the cut.

Error Cause
usage: /ask <agent> <prompt> — agents: … /ask without a name.
unknown agent "x" — agents: … /ask x … with a name not in mmConfigs.
nothing to ask: type the question after the command No prompt.
<name>: config file <path>: no such file or directory The file of mmConfigs.<name> does not exist.
<name> did not start: … — <last stderr line> The agent's process exited or did not answer initialize within 60 s.
<name> failed: … The agent's turn ended with an error (its own one-line explanation).
<name> gave no answer The agent ended its turn without any text.
this agent was itself asked through 2 level(s) of /ask; it cannot ask further (max 2) MM_ASK_DEPTH is 2.

@path in a question

Input Effect
@<path> at the start or after a space, bracket or quote If the path exists (relative to the start directory, absolute, or ~/…), a dim 📎 <absolute path> line is printed and [attached file: <absolute path>] (or [attached directory: …]) is appended to the message the model receives. Trailing punctuation is ignored; each path is attached once.
A @<path> that names nothing on disk, or @ glued to a word Left as typed.

Details and examples: Point the model at a file with @path.

Any other text during a generation

Input Behaviour
A non-command line Prints 🔄 New command received. Cancelling current task..., cancels the current generation, keeps its partial history, then submits the new line as the next question.

Keyboard shortcuts

Key At the prompt During a generation
Ctrl+C Prints Goodbye. and exits. Prints 🛑 Interrupted (Ctrl+C). and cancels the generation; the prompt comes back.
Ctrl+D (end of input) Exits. —

Window mode keys

In mm -tui the same commands exist, plus /shell; /abort when idle prints ⚠️ No generation in progress. as a notice, and the picker opened by / lists them — see The three pickers.

/quit, /abort and /shell take effect immediately, even while a turn is running. Every other command and every question waits for the turn to end, because it needs the history the turn is still writing. See Leaving a turn that will not stop.

/shell

Window mode only. Takes no argument: /shell <text> is sent to the model.

Step Behaviour
Start The window gives the terminal back (tcell Suspend: normal screen, cooked mode, mouse and paste modes off), prints mm: <shell> in <dir> — type exit to come back to the conversation. and runs the shell with the terminal as its input and output.
Shell $SHELL, else /bin/sh; on Windows %COMSPEC%, else cmd.exe. Started in the window's working directory, with mm's environment plus MM_SHELL=1.
Prompt Starts with (mm) , in front of the prompt your own configuration sets (see below).
Ctrl-C, Ctrl-\ Reach the shell and what it runs; mm ignores them while the shell runs.
A turn in progress Goes on, and is not cancelled. What it streams is shown on the way back. A permission it asks for waits until the window is back.
Return When the shell exits: the window is redrawn whole, and a notice says how it ended.
Notice When
🐚 Back from the shell. The shell exited with status 0.
🐚 Back from the shell (exit status N). The shell exited with status N (exit 3, or the last command's status).
⚠️ /shell: <error> The shell could not start (for example exec: "/bin/fish": no such file or directory), or the terminal could not be suspended or resumed.

The history is not touched and the model is not called.

How the (mm) prefix is added, per shell (recognised by the program's name):

Shell How Your configuration
bash --rcfile with a file of mm's that runs ~/.bashrc, then sets PS1="(mm) $PS1" ~/.bashrc runs as usual
zsh ZDOTDIR points at a directory of mm's whose .zshenv and .zshrc run yours (from your ZDOTDIR, else $HOME), then set PROMPT="(mm) $PROMPT"; ZDOTDIR is set back to yours .zshenv and .zshrc run as usual
fish -C wraps your fish_prompt function after your configuration is read config.fish runs as usual
sh, dash, ash, ksh, mksh PS1="(mm) $ " in the environment a file named by $ENV that sets PS1 replaces the prefix
cmd PROMPT="(mm) $P$G" in the environment —
any other nothing: no prefix —

mm's files are written in a temporary directory (mm-shell-*), removed when the shell exits; when they cannot be written, the shell starts without the prefix. A theme that rebuilds the prompt before each line (powerlevel10k, for instance) replaces the prefix: test MM_SHELL in its configuration instead.

The keys:

Key Action
Enter Sends the box (nothing on an empty box). With the picker open on an incomplete name, completes it instead.
Ctrl-J, Alt-Enter Inserts a newline in the box.
Esc Cancels the running turn (same as /abort); when no turn runs and the box is not empty, clears the box instead; closes the picker; rejects a pending permission.
Ctrl-C Cancels the running turn; quits when idle — and quits too when the turn has already been asked to stop and has not.
Tab Moves the focus between the box and the conversation; completes the picker's choice when it is open (/, @ or $).
↑ ↓ In the box: move between its rows — a long line wraps at spaces into several rows. From the first row, ↑ recalls the previous question (sessions on: this session, then the earlier ones, newest first); from the last row during a recall, ↓ the next one, then the text typed before. With nothing to recall, they scroll the conversation from the first/last row. On the conversation: scroll one line. In the picker: move the choice.
PgUp PgDn Scroll one page.
Home End In the box: start/end of the line. On the conversation: top / bottom (bottom resumes following).
Ctrl-L Clears the displayed conversation; the history is kept.
F1 Same as /help.
F2 Toggles mouse capture. Off (default): the terminal's own click-and-drag selection works; the status bar says F2 mouse: off. On: the wheel scrolls, Shift+drag selects, the status bar says mouse: on (Shift+drag selects).
Ctrl-Y Same as /copy: the last answer to the clipboard.
a s r n Answer a pending permission (yolo: false, or an MCP tool marked ask): allow once, allow this tool for the session, reject this call (Esc too), never allow this tool in this session.
Mouse wheel Scrolls the conversation — only while the mouse is captured (F2).
Bracketed paste Inserted as one block; its line breaks become newlines, never a send. Longer than 3 lines or 200 characters: collapsed into one [Pasted #n · L lines · C chars] token that edits as a single character; Enter sends the full text, the conversation keeps the token. @path inside a paste is not looked up.

Leaving a turn that will not stop

A turn normally ends when it is cancelled. A turn that ignores the cancellation — an engine that stopped answering, a tool blocked on something that never returns — used to trap the window: /quit and /abort waited for the turn to end, so they never ran, and Ctrl-C read "there is a turn to cancel" as "so do not quit". The only way out was to kill mm from another terminal.

The way out, in order:

You do The window does
Esc or /abort Cancels the turn, and says 🛑 Aborting….
Esc or /abort again The turn is still there: ⚠️ The turn is not answering the cancellation — press Ctrl-C (or /quit) to leave mm.
Ctrl-C Cancels, as above.
Ctrl-C again Leaves mm, turn or no turn.
/quit Leaves mm at once, at any moment.

Leaving this way does not wait for the stuck job: the session file holds everything saved up to the last completed turn.

The three pickers

A popup opens over the conversation as you type one of three triggers, and narrows as you type more. Tab completes the highlighted row, ↑/↓ move, Esc dismisses it until the text changes.

Trigger Offers Where it works What Tab writes
/ the window's commands only as the whole first word of the box — a command is the message the command, replacing the box, with a space when it takes an argument
@ files and directories under the working directory anywhere in a sentence the path with its @ kept. A directory gets a trailing / and no space, so Tab again walks down the tree; a file gets a space, so the sentence continues
$ the skills of the catalogue anywhere in a sentence $name — the $ kept — plus a space, with the cursor after it, ready for the skill's parameters

Three rules keep the popup out of the way:

  • A trigger glued to the end of a word is not a trigger: bob@example.com opens nothing, and neither does 5$.
  • @ hides dotfiles until you type the . yourself.
  • $ offers nothing when no skill matches, so echo $HOME and $PATH are left alone.

Enter on a @ or $ token completes it rather than sending: the message is not finished. Enter on a command whose name is already complete sends it.

What $name does when sent

$greetings Bob Morane appends one line to what the model reads, the same way @path appends an attachment line:

use $greetings Bob Morane
[skill: greetings — load it with read_skill and carry out its steps now; parameters: Bob Morane]

The parameters are the rest of the line. The model still makes the read_skill call itself, so the loop detector, the permission gate and the 📖 N skill(s) recap all see it — mm does not load the skill behind the model's back.

The notation works in the line mode and over ACP too, typed by hand: a $skill means the same thing wherever it is written, as @path does. A $name that matches no skill is left alone, and a $ inside a collapsed paste is not looked up.

Messages printed at the end of a turn

Message Meaning
🛑 Generation aborted. The turn was cancelled (/abort, Ctrl+C). The question and any completed tool turns stay in the history.
[error: …] The provider explained a failure in one line; a question that got no answer is removed from the history.
⚙ N command(s) Number of bash commands that actually ran in the turn.
· 📝 N file op(s) Appended when file tools ran.
· 📖 N skill(s) Appended when skills were loaded.
Numbered green list The commands and file operations of the turn, one per line, when displayCommands: true.

ACP mode

Two commands exist in ACP mode, three with unloadModels (the default): /new, /compact and /unload — plus /memory with sessions.searchMemory and /docs with docSearch.searchDocs. They are advertised to the editor through an available_commands_update notification and intercepted by session/prompt when the prompt text, trimmed, is exactly the command (/unload may be followed by a model name, /memory and /docs by their words). Each answers with one agent_message_chunk carrying the line the terminal would print, then stopReason: end_turn; the model is not consulted.

Command Over ACP
/new Same as in the terminal: history back to the system prompt, token count and this session's plans forgotten (other sessions keep theirs); the session id, cwd and allow-always grants are kept.
/compact Same as in the terminal, always forced: 🗜️ compressed …, 🗜️ nothing to compact: … or [compact: failed, history kept: …]. Waits for a running turn to finish first.
/memory <words> With sessions.enabled and sessions.searchMemory, advertised with the input hint <words>: same as in the terminal, in the session's project (sessions.dir under its cwd), as the agent's text; the history is not touched.
/docs <words> With docSearch.searchDocs, advertised with the input hint <words>: same as in the terminal, from the session's cwd, as the agent's text; the history is not touched.
/unload [model] With unloadModels (default on), advertised with the input hint [model]: same as in the terminal, the 🧹 line as the agent's text; the history is not touched.

With mmConfigs, /ask-<name> and /ask-with-context-<name> are advertised too, each with the input hint <prompt>; the generic /ask <name> is not, since an editor's menu completes a command name but not its argument. See Asking another agent.

/quit and /abort do not exist in ACP mode: the editor owns the process and ends it by closing stdin, and cancels a turn with its own stop key through session/cancel. Each skill is offered there as a command of its own — ACP has no picker for skills, so /greetings Bob in the editor's menu is the equivalent of $greetings Bob in the window, and it is rewritten into exactly that. See the ACP reference.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# Reference: slash commands and keyboard shortcuts

> Neutral description of the commands recognised at the terminal prompt of `mm`. They are matched on the whole trimmed line; anything else is sent to the model.

## Commands

| Command | At the prompt | During a generation |
|---------|---------------|---------------------|
| `/quit` | Exits the program. | Cancels the generation, prints `Goodbye.`, exits. |
| `/abort` | Prints `⚠️ No generation in progress.` | Cancels the generation, prints `🛑 Aborting...`; the commands already run stay in the history. |
| `/compact` | Compresses the history now, regardless of the threshold; prints the `🗜️` report, or `🗜️ nothing to compact` when no turn is older than `context.keepLastTurns`. | Sent to the model as a new message (see below). |
| `/new` | Clears the history back to the system prompt, resets the loop detector, forgets the server's last token count and the conversation's plans; prints `🆕 new session: N message(s) forgotten` (N counts every message except the system prompt). | Treated like any other text: cancels the generation, keeps its partial history, then runs as `/new` on that history. |
| `/sessions` | Lists the project's stored sessions, newest first: id, last update, question count, first question, then ` · ⚠️ N error(s)` when the session recorded errors; `*` marks the current one. With `sessions.enabled: false`: `⚠️ sessions are off (set sessions.enabled: true in the YAML)`. | Sent to the model as a new message. |
| `/resume [id]` | Continues a stored session: the most recently updated one other than the current, skipping empty ones, or the named id. Prints `↩️ resumed <id> — N message(s), started with "<first question>"` (plus ` · ⚠️ N error(s)` when the session recorded errors); resets the loop detector, the server's token count and the plans; later turns are saved to that session. Refused with a `⚠️` reason when sessions are off, when there is no earlier session, when the id is unknown or is the current session. | Sent to the model as a new message. |
| `/help` | Prints the commands of this front end, one per line with their description, aligned. | Sent to the model as a new message. |
| `/copy [n\|all]` | Copies the last answer (`n`: the n-th from the end) to the clipboard through `pbcopy`/`wl-copy`/`xclip`/`xsel` or, failing that, OSC 52 when stdout is a terminal, and prints `📋 copied N chars (<road>)` or `⚠️ …`. `/copy all` is window-mode only: here it says so. | Sent to the model as a new message. |
| `/save <file> [n]` | Writes the last answer (`n`: the n-th from the end) as it is to a new file, creating the missing directories, and prints `💾 saved the last answer to <file> (N bytes)`. Never overwrites: an existing file gives `⚠️ <file> already exists — nothing was written; choose another name`. A file name with spaces is not supported. The model is not called. | Sent to the model as a new message. |
| `/memory <words>` | With `sessions.enabled` and `sessions.searchMemory`: searches the project's stored sessions and prints `🔎 N hit(s) for "<words>" in <dir>`, then two lines per hit — `<session id> · <date> · turn <n> · question\|answer\|summary` and the text around the words found, plus `→ <answer>` under a question (the answer its turn ended on) — or `🔎 no hit for "<words>" in <dir>`. Case and accents do not count; words of one or two letters are left out beside longer ones; a hit holds at least one of the words; hits are ranked by the number of words held, then newest first; 10 at most. Questions, answers and `/compact` summaries are searched, not tool calls; the conversation in progress is left out. Without words: `⚠️ nothing to search: give one or more words`. The model is not called; the history is not touched. | Sent to the model as a new message. |
| `/docs <words>` | With `docSearch.searchDocs`: searches the text files of `docSearch.dirs` with BM25 and prints `🔎 N passage(s) for "<words>" — F file(s) in <dirs>`, then per passage a line `── <path>:<line> · <heading › subheading>` and the passage as written; or `🔎 no passage for "<words>" — F file(s) in <dirs>`. Case and accents do not count; words of 4+ letters also match longer words and plurals; 5 passages at most. Without words: `⚠️ nothing to search: give one or more words`. The model is not called; the history is not touched. | Sent to the model as a new message. |
| `/unload [model]` | With `unloadModels` (default on): unloads a model from the server's memory — this agent's own, or the one named — and prints one line: `🧹 unloaded <model>`, `🧹 <model> was not loaded: nothing to unload`, or `🧹 cannot unload <model>: <reason>` (a provider or a server that has no way to). The history is not touched; the next question reloads the agent's model. | Sent to the model as a new message. |

With `sessions.enabled`, `/new` leaves the current session's file as it is; the next question opens a new file. An empty line at the prompt is ignored.

## Asking another agent

Present only when `mmConfigs` declares at least one agent; `/help` and the `/` picker list them. `<name>` is a key of `mmConfigs`.

| Command | Front ends | Effect |
|---------|------------|--------|
| `/ask-<name> <prompt>` | terminal, window, editor | Sends `<prompt>` alone to `<name>`. |
| `/ask-with-context-<name> <prompt>` | terminal, window, editor | Sends a transcript of the current conversation, then `<prompt>`. |
| `/ask <name> <prompt>` | terminal, window | Same as `/ask-<name> <prompt>`. |
| `/ask-with-context <name> <prompt>` | terminal, window | Same as `/ask-with-context-<name> <prompt>`. |

While it runs:

| Item | Behaviour |
|------|-----------|
| First line | `🗣 asking <name> (<provider>/<model>)…`, or `…, with the conversation…` for the context forms. |
| `<name>`'s answer | Streamed where answers appear. Its reasoning is shown as reasoning when `showThinking` is on. |
| `<name>`'s tool calls | Shown as tool calls titled `<name> · <tool>: <args>`. Permission requests go through this front end: none in the line mode (allowed), `permissions` in the window (`a`/`s`/`r`, "for this session" keyed on `<name>/<tool>`), the editor's dialog over ACP. |
| `/abort`, `Esc`, `Ctrl+C`, `session/cancel` | Cancel the ask; `🛑 Ask aborted — nothing kept.` |
| `/quit` | Cancels the ask and exits. |
| Any other line (line mode) | `⚠️ <name> is still answering — /abort to stop it.`; the line is dropped. |

When it ends:

| Outcome | History | Message |
|---------|---------|---------|
| Answered | Two messages appended: user `[asked <name> (<provider>/<model>)[, with this conversation as context]: <prompt>]`, assistant `[<name>'s answer]` + the answer (capped at `maxOutput` characters, beginning and end kept). Saved with the session. | `🗣 <name>'s answer is in the history for your next question` |
| Cancelled | Unchanged. | `🛑 Ask aborted — nothing kept.` |
| Failed | Unchanged; recorded in the session file with `where: "ask"`. | `[error: …]` (terminal, window) or a JSON-RPC error (editor). |

The transcript sent by the context forms holds the user's questions, the assistant's answers, one line per tool call (`Assistant ran <tool>: <args>`, args cut at 160 characters) and the compaction summary if any; never the system prompt, tool outputs or reasoning. Capped at `maxOutput` characters, newest lines kept, `[… earlier conversation omitted …]` marking the cut.

| Error | Cause |
|-------|-------|
| `usage: /ask <agent> <prompt> — agents: …` | `/ask` without a name. |
| `unknown agent "x" — agents: …` | `/ask x …` with a name not in `mmConfigs`. |
| `nothing to ask: type the question after the command` | No prompt. |
| `<name>: config file <path>: no such file or directory` | The file of `mmConfigs.<name>` does not exist. |
| `<name> did not start: … — <last stderr line>` | The agent's process exited or did not answer `initialize` within 60 s. |
| `<name> failed: …` | The agent's turn ended with an error (its own one-line explanation). |
| `<name> gave no answer` | The agent ended its turn without any text. |
| `this agent was itself asked through 2 level(s) of /ask; it cannot ask further (max 2)` | `MM_ASK_DEPTH` is 2. |

## `@path` in a question

| Input | Effect |
|-------|--------|
| `@<path>` at the start or after a space, bracket or quote | If the path exists (relative to the start directory, absolute, or `~/…`), a dim `📎 <absolute path>` line is printed and `[attached file: <absolute path>]` (or `[attached directory: …]`) is appended to the message the model receives. Trailing punctuation is ignored; each path is attached once. |
| A `@<path>` that names nothing on disk, or `@` glued to a word | Left as typed. |

Details and examples: [Point the model at a file with `@path`](../how-to/attach-a-file.md).

## Any other text during a generation

| Input | Behaviour |
|-------|-----------|
| A non-command line | Prints `🔄 New command received. Cancelling current task...`, cancels the current generation, keeps its partial history, then submits the new line as the next question. |

## Keyboard shortcuts

| Key | At the prompt | During a generation |
|-----|---------------|---------------------|
| `Ctrl+C` | Prints `Goodbye.` and exits. | Prints `🛑 Interrupted (Ctrl+C).` and cancels the generation; the prompt comes back. |
| `Ctrl+D` (end of input) | Exits. | — |

## Window mode keys

In `mm -tui` the same commands exist, plus `/shell`; `/abort` when idle prints `⚠️ No generation in progress.` as a notice, and the picker opened by `/` lists them — see [The three pickers](#the-three-pickers).

`/quit`, `/abort` and `/shell` take effect **immediately**, even while a turn is running. Every other command and every question waits for the turn to end, because it needs the history the turn is still writing. See [Leaving a turn that will not stop](#leaving-a-turn-that-will-not-stop).

### `/shell`

Window mode only. Takes no argument: `/shell <text>` is sent to the model.

| Step | Behaviour |
|------|-----------|
| Start | The window gives the terminal back (tcell `Suspend`: normal screen, cooked mode, mouse and paste modes off), prints `mm: <shell> in <dir> — type exit to come back to the conversation.` and runs the shell with the terminal as its input and output. |
| Shell | `$SHELL`, else `/bin/sh`; on Windows `%COMSPEC%`, else `cmd.exe`. Started in the window's working directory, with mm's environment plus `MM_SHELL=1`. |
| Prompt | Starts with `(mm) `, in front of the prompt your own configuration sets (see below). |
| `Ctrl-C`, `Ctrl-\` | Reach the shell and what it runs; `mm` ignores them while the shell runs. |
| A turn in progress | Goes on, and is not cancelled. What it streams is shown on the way back. A permission it asks for waits until the window is back. |
| Return | When the shell exits: the window is redrawn whole, and a notice says how it ended. |

| Notice | When |
|--------|------|
| `🐚 Back from the shell.` | The shell exited with status 0. |
| `🐚 Back from the shell (exit status N).` | The shell exited with status N (`exit 3`, or the last command's status). |
| `⚠️ /shell: <error>` | The shell could not start (for example `exec: "/bin/fish": no such file or directory`), or the terminal could not be suspended or resumed. |

The history is not touched and the model is not called.

How the `(mm) ` prefix is added, per shell (recognised by the program's name):

| Shell | How | Your configuration |
|-------|-----|--------------------|
| `bash` | `--rcfile` with a file of mm's that runs `~/.bashrc`, then sets `PS1="(mm) $PS1"` | `~/.bashrc` runs as usual |
| `zsh` | `ZDOTDIR` points at a directory of mm's whose `.zshenv` and `.zshrc` run yours (from your `ZDOTDIR`, else `$HOME`), then set `PROMPT="(mm) $PROMPT"`; `ZDOTDIR` is set back to yours | `.zshenv` and `.zshrc` run as usual |
| `fish` | `-C` wraps your `fish_prompt` function after your configuration is read | `config.fish` runs as usual |
| `sh`, `dash`, `ash`, `ksh`, `mksh` | `PS1="(mm) $ "` in the environment | a file named by `$ENV` that sets `PS1` replaces the prefix |
| `cmd` | `PROMPT="(mm) $P$G"` in the environment | — |
| any other | nothing: no prefix | — |

mm's files are written in a temporary directory (`mm-shell-*`), removed when the shell exits; when they cannot be written, the shell starts without the prefix. A theme that rebuilds the prompt before each line (powerlevel10k, for instance) replaces the prefix: test `MM_SHELL` in its configuration instead.

The keys:

| Key | Action |
|-----|--------|
| `Enter` | Sends the box (nothing on an empty box). With the picker open on an incomplete name, completes it instead. |
| `Ctrl-J`, `Alt-Enter` | Inserts a newline in the box. |
| `Esc` | Cancels the running turn (same as `/abort`); when no turn runs and the box is not empty, clears the box instead; closes the picker; rejects a pending permission. |
| `Ctrl-C` | Cancels the running turn; quits when idle — and quits too when the turn has already been asked to stop and has not. |
| `Tab` | Moves the focus between the box and the conversation; completes the picker's choice when it is open (`/`, `@` or `$`). |
| `↑` `↓` | In the box: move between its rows — a long line wraps at spaces into several rows. From the first row, `↑` recalls the previous question (sessions on: this session, then the earlier ones, newest first); from the last row during a recall, `↓` the next one, then the text typed before. With nothing to recall, they scroll the conversation from the first/last row. On the conversation: scroll one line. In the picker: move the choice. |
| `PgUp` `PgDn` | Scroll one page. |
| `Home` `End` | In the box: start/end of the line. On the conversation: top / bottom (bottom resumes following). |
| `Ctrl-L` | Clears the displayed conversation; the history is kept. |
| `F1` | Same as `/help`. |
| `F2` | Toggles mouse capture. Off (default): the terminal's own click-and-drag selection works; the status bar says `F2 mouse: off`. On: the wheel scrolls, `Shift`+drag selects, the status bar says `mouse: on (Shift+drag selects)`. |
| `Ctrl-Y` | Same as `/copy`: the last answer to the clipboard. |
| `a` `s` `r` `n` | Answer a pending permission (`yolo: false`, or an MCP tool marked `ask`): allow once, allow this tool for the session, reject this call (`Esc` too), never allow this tool in this session. |
| Mouse wheel | Scrolls the conversation — only while the mouse is captured (F2). |
| Bracketed paste | Inserted as one block; its line breaks become newlines, never a send. Longer than 3 lines or 200 characters: collapsed into one `[Pasted #n · L lines · C chars]` token that edits as a single character; Enter sends the full text, the conversation keeps the token. `@path` inside a paste is not looked up. |

## Leaving a turn that will not stop

A turn normally ends when it is cancelled. A turn that ignores the cancellation — an engine that stopped answering, a tool blocked on something that never returns — used to trap the window: `/quit` and `/abort` waited for the turn to end, so they never ran, and `Ctrl-C` read "there is a turn to cancel" as "so do not quit". The only way out was to kill `mm` from another terminal.

The way out, in order:

| You do | The window does |
|--------|-----------------|
| `Esc` or `/abort` | Cancels the turn, and says `🛑 Aborting…`. |
| `Esc` or `/abort` again | The turn is still there: `⚠️ The turn is not answering the cancellation — press Ctrl-C (or /quit) to leave mm.` |
| `Ctrl-C` | Cancels, as above. |
| `Ctrl-C` again | Leaves `mm`, turn or no turn. |
| `/quit` | Leaves `mm` at once, at any moment. |

Leaving this way does not wait for the stuck job: the session file holds everything saved up to the last completed turn.

## The three pickers

A popup opens over the conversation as you type one of three triggers, and narrows as you type more. **Tab** completes the highlighted row, **↑/↓** move, **Esc** dismisses it until the text changes.

| Trigger | Offers | Where it works | What Tab writes |
|---|---|---|---|
| `/` | the window's commands | only as the whole first word of the box — a command *is* the message | the command, replacing the box, with a space when it takes an argument |
| `@` | files and directories under the working directory | anywhere in a sentence | the path with its `@` kept. A **directory** gets a trailing `/` and no space, so Tab again walks down the tree; a **file** gets a space, so the sentence continues |
| `$` | the skills of the catalogue | anywhere in a sentence | `$name` — the `$` **kept** — plus a space, with the cursor after it, ready for the skill's parameters |

Three rules keep the popup out of the way:

- A trigger glued to the end of a word is not a trigger: `bob@example.com` opens nothing, and neither does `5$`.
- `@` hides dotfiles until you type the `.` yourself.
- `$` offers nothing when no skill matches, so `echo $HOME` and `$PATH` are left alone.

`Enter` on a `@` or `$` token completes it rather than sending: the message is not finished. `Enter` on a command whose name is already complete sends it.

### What `$name` does when sent

`$greetings Bob Morane` appends one line to what the model reads, the same way `@path` appends an attachment line:

```
use $greetings Bob Morane
[skill: greetings — load it with read_skill and carry out its steps now; parameters: Bob Morane]
```

The parameters are the rest of the line. The model still makes the `read_skill` call itself, so the loop detector, the permission gate and the `📖 N skill(s)` recap all see it — `mm` does not load the skill behind the model's back.

The notation works in the line mode and over ACP too, typed by hand: a `$skill` means the same thing wherever it is written, as `@path` does. A `$name` that matches no skill is left alone, and a `$` inside a collapsed paste is not looked up.

## Messages printed at the end of a turn

| Message | Meaning |
|---------|---------|
| `🛑 Generation aborted.` | The turn was cancelled (`/abort`, `Ctrl+C`). The question and any completed tool turns stay in the history. |
| `[error: …]` | The provider explained a failure in one line; a question that got no answer is removed from the history. |
| `⚙ N command(s)` | Number of `bash` commands that actually ran in the turn. |
| `· 📝 N file op(s)` | Appended when file tools ran. |
| `· 📖 N skill(s)` | Appended when skills were loaded. |
| Numbered green list | The commands and file operations of the turn, one per line, when `displayCommands: true`. |

## ACP mode

Two commands exist in ACP mode, three with `unloadModels` (the default): `/new`, `/compact` and `/unload` — plus `/memory` with `sessions.searchMemory` and `/docs` with `docSearch.searchDocs`. They are advertised to the editor through an `available_commands_update` notification and intercepted by `session/prompt` when the prompt text, trimmed, is exactly the command (`/unload` may be followed by a model name, `/memory` and `/docs` by their words). Each answers with one `agent_message_chunk` carrying the line the terminal would print, then `stopReason: end_turn`; the model is not consulted.

| Command | Over ACP |
|---------|----------|
| `/new` | Same as in the terminal: history back to the system prompt, token count and this session's plans forgotten (other sessions keep theirs); the session id, `cwd` and allow-always grants are kept. |
| `/compact` | Same as in the terminal, always forced: `🗜️ compressed …`, `🗜️ nothing to compact: …` or `[compact: failed, history kept: …]`. Waits for a running turn to finish first. |
| `/memory <words>` | With `sessions.enabled` and `sessions.searchMemory`, advertised with the input hint `<words>`: same as in the terminal, in the session's project (`sessions.dir` under its `cwd`), as the agent's text; the history is not touched. |
| `/docs <words>` | With `docSearch.searchDocs`, advertised with the input hint `<words>`: same as in the terminal, from the session's `cwd`, as the agent's text; the history is not touched. |
| `/unload [model]` | With `unloadModels` (default on), advertised with the input hint `[model]`: same as in the terminal, the `🧹` line as the agent's text; the history is not touched. |

With `mmConfigs`, `/ask-<name>` and `/ask-with-context-<name>` are advertised too, each with the input hint `<prompt>`; the generic `/ask <name>` is not, since an editor's menu completes a command name but not its argument. See [Asking another agent](#asking-another-agent).

`/quit` and `/abort` do not exist in ACP mode: the editor owns the process and ends it by closing stdin, and cancels a turn with its own stop key through `session/cancel`. Each **skill** is offered there as a command of its own — ACP has no picker for skills, so `/greetings Bob` in the editor's menu is the equivalent of `$greetings Bob` in the window, and it is rewritten into exactly that. See the [ACP reference](acp.md#skills-as-commands).