How to talk to a coding agent from the editor
This guide shows how to point Turbo MoonBit at an agent that speaks the Agent Client Protocol, open a window onto it, and hold a conversation about the code you are editing. It assumes you already have Turbo MoonBit running in a project.
Turbo MoonBit is an ACP client. It starts the agent as a child process and talks JSON-RPC to it over its standard input and output — the same arrangement Zed uses, so an agent that works there works here.
Tell the editor about an agent
Agents are listed in acp.toml. Choose Agent ▸ Create agents file and the editor writes a starter one into .turbo-moonbit/acp.toml and opens it.
An agent is one [[agent]] block:
[[agent]]
name = "Bob (llama.cpp)"
command = "docker"
args = ["agent", "serve", "acp", ".turbo-moonbit/agent.yaml"]
env = { TELEMETRY_ENABLED = "false" }
name is what the Agent menu shows and what the window is called. command and args are how the agent is started. That is the whole of it — the file is read again every time you open a window, so you never restart the editor to try a change.
List as many as you like. Each becomes its own line in the menu, and each window you open from it is a separate process with a conversation of its own.
Put the agent's own configuration beside it
Most agents have a configuration file of their own, and .turbo-moonbit/ is a reasonable place to keep it so that it travels with the project. For docker agent, saving this as .turbo-moonbit/agent.yaml is what the args above point at:
providers:
llamacpp:
api_type: openai_chatcompletions
base_url: http://localhost:8080/v1
models:
mellum2:
provider: llamacpp
model: JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
temperature: 0.7
provider_opts:
context_size: 262144
agents:
root:
model: mellum2
description: A helpful AI assistant running on a local llama.cpp server
instruction: |
You name is Bob 🤓, you are a knowledgeable code assistant.
Be helpful, accurate, and concise in your responses.
You have access to the local filesystem and shell: use these tools
toolsets:
- type: filesystem
- type: shell
Open a window on it
Press Alt-A, or choose Agent from the menu bar, and pick the agent by name.
A window opens, split in two: the conversation above, and a box to type in below. The agent is started when the window opens and stopped when it closes.
┌ Bob (llama.cpp) ───────────────────────────────[■]┐
│ ‣ You │
│ What does buildMenus do? │
│ │
│ ‣ Shell ls -1 internal/ ✓ done │
│ golang │
│ │
│ ‣ Bob │
│ It assembles the menu bar. Here is the shape: │
│ │
│ ```moonbit │
│ func (a *App) buildMenus() *ui.MenuBar { │
│ return ui.NewMenuBar(a.allMenus()...) │
│ } │
│ ``` │
├───────────────────────────────────────────────────┤
│ > _ │
└───────────────────────────────────────────────────┘
Code the agent sends inside a fenced block is coloured by the same scanners the editor uses for files, so a MoonBit answer is coloured as MoonBit and a shell answer as shell. A fence naming a language the editor does not colour is left plain rather than guessed at.
Hold the conversation
| Key | Effect |
|---|---|
Enter |
Send what you have typed |
Alt-Enter |
Start a new line instead of sending |
Tab |
Move between the conversation and the input box |
PgUp PgDn |
Scroll the conversation a screenful at a time |
Esc |
Stop the turn in progress |
Ctrl-W |
Close the window, and stop the agent with it |
While the agent is answering, its reply appears as it is written rather than all at once, and the rule between the two panes turns a spinner beside the word thinking. Esc interrupts it — the agent is told to stop, and what it had already said stays in the window.
Use the agent's own commands
Some agents answer to commands — /compact, /web, /plan — and tell the editor which ones. Type / as the first character of the box and the list opens over the conversation: each command, what it does, and in angle brackets what it wants after its name.
Type on to narrow it, ↑ ↓ to move, then Tab to complete. A command that takes something is completed with a space after it, ready for you to type the rest; press Enter when the line is what you mean. If nothing appears when you type /, the agent announced no commands — Agent ▸ Agent status says so — and / is only a character.
Point the agent at a file
Type @ anywhere in the box and the project's files appear. Type a few letters of the file's name to narrow the list, Tab to take the highlighted one:
> explain what @internal/scanner.go does
When you press Enter, the agent is given the file, not merely its name: its text when the agent accepts embedded context, a link to it otherwise. If the file is open in the editor with unsaved changes, it is your unsaved version that goes. The line stays in the conversation as you typed it.
Several files in one prompt is several @. A word that begins with @ but is not a file — an e-mail address — is left as text.
Take something out of the conversation
Press Tab to put the cursor in the conversation. The rule changes to say what the keys now do.
| Key | Effect |
|---|---|
↑ ↓ PgUp PgDn |
Move the cursor through what was said |
Shift-↑ Shift-↓ |
Select whole lines |
| Drag with the mouse | The same, by hand |
Ctrl-C |
Copy |
Esc |
Drop the selection |
Tab |
Back to the box |
With nothing selected, Ctrl-C copies the block the cursor is on — one fenced code block, one paragraph, one tool's output — without the speaker's label above it and without the sentence after it. That is almost always what you wanted, and it saves selecting it by hand.
What is copied goes to two clipboards: this editor's, so Shift-Ins pastes it into a file you have open, and your system's, so Ctrl-V pastes it anywhere else. The indentation the conversation is drawn with is taken off, so pasted code lands flush against the margin.
The system half travels through your terminal (an escape sequence called OSC 52). Most terminals do it; a few refuse it for security, and some need it turned on. If Ctrl-V elsewhere gives you nothing, that is where to look — the editor's own clipboard has the text either way.
Answer the agent when it asks permission
An agent with a shell or a filesystem toolset asks before it uses one. A dialog names the tool and the exact command, and offers the choices the agent itself proposed — normally Allow this action, Allow and remember my choice, and Skip this action.
┌───────────── Bob (llama.cpp) wants to run ─────────────┐
│ │
│ Shell │
│ ls -1 │
│ │
│ [ Allow ] [ Allow always ] [ Skip ] │
└────────────────────────────────────────────────────────┘
Allow always is remembered by the agent, not by the editor, so what it covers and how long it lasts are the agent's business. Escape is the same answer as Skip.
Nothing runs before you answer. An agent waiting on a permission dialog is simply blocked, which is the point.
Let the agent see what you have not saved yet
The editor offers the agent its own filesystem: when the agent reads a file you have open with unsaved changes, it is given the text in the buffer, not the older text on disk. That is usually what you want — you are asking about the edit you just made.
When the agent writes a file, the change lands in the buffer and the window is marked modified, so you can read it, undo it with Ctrl-Z, or save it with F2. A file you do not have open is read from and written to disk directly.
Run several agents at once
Each window is its own process and its own conversation. Opening the same agent twice gives two independent sessions, and opening two different agents lets you put a fast local model and a slower careful one side by side — Window ▸ Tile arranges them.
Leaving the editor stops every agent.
Variants
- You want the agent to run somewhere other than the project root. Add
cwd = "backend"to its block. The path is relative to the project, and is both where the process starts and what the agent is told the working directory is. - The agent needs a credential. Put it in
env, or rely on it being in the environment you started the editor from — the agent inherits it. - The agent's commands do not appear when you type
/. Open Agent ▸ Agent status with the window in front. If it lists no commands, the agent announced none — or announced them in a shape this editor could not read, in which case the dialog names the update and the decoding error. To see exactly what went over the wire, start the editor withTURBO_ACP_TRACE=/tmp/acp.logand read the file:->is what the editor sent,<-what the agent answered. - The agent will not start. Agent ▸ Agent status lists what was read from
acp.toml, what each agent's command line came out as, and the error from anything that failed to start. Whatever the agent writes to its standard error is shown there too, which is where a misconfigured model endpoint reports itself. - You keep the same agent in every project. Put the
[[agent]]block in~/.config/turbo-moonbit/acp.tomlinstead. A project's own file is read afterwards and an agent with the samenamein it replaces yours.
See also
- Every key of the file, and exactly how much of the protocol is implemented: Agents and ACP reference
- Why an agent is a window rather than a panel, and why permissions are modal: Agent windows
- The protocol itself: agentclientprotocol.com
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 |
|