mirocode 0.0.0-stage → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +371 -2
- package/README.zh.md +372 -0
- package/dist/cli.js +2310 -0
- package/dist/codemode-worker.js +470 -0
- package/dist/quickjs-r1pq2107.wasm +0 -0
- package/package.json +60 -4
package/README.md
CHANGED
|
@@ -1,3 +1,372 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.png" alt="Miro" width="120" height="120">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">Miro</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
A terminal client for ACP-compatible coding agents.<br>
|
|
9
|
+
Interactive TUI and one-shot print mode, built with Bun, React, and Ink.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="./README.zh.md">中文</a>
|
|
14
|
+
·
|
|
15
|
+
English
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
**miro-cli** includes a built-in agent that calls Chat Completions, OpenAI Responses, or Anthropic Messages directly with its own tool loop. Use `--acp <provider-id>` only when you want to start an [Agent Client Protocol](https://agentclientprotocol.com/) provider as a child process over stdio JSON-RPC.
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- [Bun](https://bun.sh/) **>= 1.4.3**, or Node **>= 22.7** for the npm package
|
|
25
|
+
- An API endpoint for Miro's built-in agent; an ACP binary on `PATH` is needed only for `--acp` (see [ACP providers](#acp-providers))
|
|
26
|
+
|
|
27
|
+
## Install and run
|
|
28
|
+
|
|
29
|
+
Install the published package; the `miro` command runs on Node **>= 22.7** and needs no Bun:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g mirocode
|
|
33
|
+
npx mirocode
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
From a checkout:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bun install
|
|
40
|
+
bun start
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`npm publish` ships the Node bundle `dist/cli.js` plus the codemode worker and QuickJS WASM; `prepack` rebuilds them from the current source.
|
|
44
|
+
|
|
45
|
+
Build a standalone binary for the host platform:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bun run build
|
|
49
|
+
./dist/miro
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`bun run build:js` emits an inspectable ESM bundle instead of a compiled binary.
|
|
53
|
+
|
|
54
|
+
Release tags (`v*`) publish Linux and macOS binaries (x64 and arm64). Local cross-compiles use the same flags as CI:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bun run scripts/build.ts --target bun-linux-x64 --outfile dist/miro-linux-amd64
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
miro
|
|
64
|
+
miro --version
|
|
65
|
+
miro -p "Analyze this project"
|
|
66
|
+
echo "Summarize this input" | miro -p
|
|
67
|
+
miro -p --output-format json "Fix the tests"
|
|
68
|
+
miro --model hy3
|
|
69
|
+
miro --effort high
|
|
70
|
+
miro --acp claude
|
|
71
|
+
miro --continue
|
|
72
|
+
miro --resume <session-id>
|
|
73
|
+
./dist/miro -c <session-id>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
From source, pass flags after `--`:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
bun start -- -c <session-id>
|
|
80
|
+
bun start -- -p "Analyze this project"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Options
|
|
84
|
+
|
|
85
|
+
| Flag | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `-p`, `--print` | Run one non-interactive request and exit |
|
|
88
|
+
| `--output-format <text\|json>` | Print-mode output (requires `--print`) |
|
|
89
|
+
| `--acp <provider-id>` | Start this available ACP provider by id; otherwise use Miro's built-in agent |
|
|
90
|
+
| `--model <model>` | Model by value or display name (interactive and print modes) |
|
|
91
|
+
| `--effort <level>` | Effort by value or display name (interactive and print modes; not saved) |
|
|
92
|
+
| `-c`, `--continue [id]` | Resume a session; omit `id` to use the latest for this project |
|
|
93
|
+
| `--resume <id>` | Resume the given session id |
|
|
94
|
+
| `--permission-mode <m>` | miro permission mode: `auto` (default) or `manual` |
|
|
95
|
+
| `--mode <mode>` | Interaction mode: `default` or `plan` |
|
|
96
|
+
| `-h`, `--help` | Print help |
|
|
97
|
+
|
|
98
|
+
`--output-format` is only valid with `-p` / `--print`; `--acp`, `--model`, and `--effort` also work when starting the interactive TUI and apply only to this run. `--acp` accepts only an available ACP provider id; the removed `--provider` flag reports a migration error. A bare prompt without `--print` is an error. `-c` without an id continues the latest session for the selected mode (and ACP id); `--resume` always requires an id.
|
|
99
|
+
|
|
100
|
+
## Commands
|
|
101
|
+
|
|
102
|
+
Type these in the interactive composer:
|
|
103
|
+
|
|
104
|
+
| Command | Description |
|
|
105
|
+
|---------|-------------|
|
|
106
|
+
| `/init [extra]` | Analyze the codebase and create or improve `AGENTS.md` |
|
|
107
|
+
| `/model [model]` | Select or switch model |
|
|
108
|
+
| `/effort [level]` | Set the current model's effort level |
|
|
109
|
+
| `/config [id] [val]` | View or change ACP session config options |
|
|
110
|
+
| `/thinking [mode]` | Set thinking display to `compact` (default), `full`, or `hidden`; no mode opens a picker |
|
|
111
|
+
| `/permissions [mode]` | miro permission mode: `auto` (default) or `manual`; no mode opens a picker |
|
|
112
|
+
| `/plan [on\|off\|status]` | Enter, leave, or inspect Plan Mode (miro only) |
|
|
113
|
+
| `/statusline [reset]` | Configure status-line items, order, and colors |
|
|
114
|
+
| `/review [text]` | Review code changes; with no argument opens a picker (uncommitted, branch, commit, or custom instructions) |
|
|
115
|
+
| `/goal [text]` | Work toward an objective across as many turns as it takes until the model reports it done or blocked; while a response is running, a new objective safely interrupts it and takes over. No text shows the current goal; `replace`, `status`, `pause`, `resume`, and `cancel` manage it (miro only). |
|
|
116
|
+
| `/simplify [text]` | Simplify code while preserving behavior; accepts paths or free-form instructions |
|
|
117
|
+
| `/commit [text]` | Create one commit from the current changes; optional text guides the message |
|
|
118
|
+
| `/commit-push-pr` | Create one commit, push the branch to `origin`, and open a pull request with the CLI for that host (`gh`, `glab`, or `tea`) |
|
|
119
|
+
| `/resume [id]` | Resume a saved session; no id opens the picker |
|
|
120
|
+
| `/continue [id]` | Alias for `/resume` |
|
|
121
|
+
| `/new` | New session without clearing the terminal |
|
|
122
|
+
| `/sessions` | List saved sessions for the current project |
|
|
123
|
+
| `/clear` | New ACP session and clear the terminal display and visible transcript |
|
|
124
|
+
| `/compact [text]` | Summarize earlier context while keeping recent history; optional text guides the summary |
|
|
125
|
+
| `/export [file]` | Export the conversation (clipboard or `.txt` file) |
|
|
126
|
+
| `/help` | Show help |
|
|
127
|
+
| `/exit` | Exit (`exit`, `/quit`, `quit`) and print the session usage summary |
|
|
128
|
+
| `!<command>` | Run a local shell command; output is attached to the next prompt |
|
|
129
|
+
|
|
130
|
+
Provider-specific slash commands advertised by ACP are forwarded as-is.
|
|
131
|
+
|
|
132
|
+
`/commit-push-pr` reads the `origin` remote and picks the matching CLI: `gh` on GitHub, `glab` on GitLab, `tea` on Gitea or Forgejo. When that CLI is missing, unauthenticated, or the host has none at all (Bitbucket, an unrecognised self-hosted instance), it still commits and pushes, then reports the URL for opening the pull request by hand instead of inventing one. A repository with no `origin` remote stops after the local commit.
|
|
133
|
+
|
|
134
|
+
While a turn is running, Enter steers the built-in Miro agent and Tab queues a prompt for the next turn. Steering waits for the current model response and tools to finish, then continues the same task; pending steering stays visible until it enters the model history. Normal, Plan, and Goal conversations support steering. ACP, isolated workflows such as `/review` and `/commit`, and standalone compaction fall back to FIFO queuing. Unapplied steering after interruption, failure, or session recovery pauses sending: open Ctrl+Q and press `r` to resume, or edit/delete the messages. Slash commands keep their existing dispatch rules. `!command` is refused while busy.
|
|
135
|
+
|
|
136
|
+
## Shortcuts
|
|
137
|
+
|
|
138
|
+
| Key | Action |
|
|
139
|
+
|-----|--------|
|
|
140
|
+
| Enter | Send the prompt; steer the current task while supported |
|
|
141
|
+
| Tab | Queue a prompt while running; completion takes priority |
|
|
142
|
+
| Shift+Enter | Insert a newline (Miro enables Kitty and xterm extended-key reporting; unsupported terminals still cannot distinguish it from Enter) |
|
|
143
|
+
| `?` | Shortcut cheatsheet (empty composer) |
|
|
144
|
+
| ↑ / ↓ | Input history or completion |
|
|
145
|
+
| Ctrl+M | Select or switch model (needs a terminal that supports the kitty keyboard protocol, otherwise the key is the same as Enter) |
|
|
146
|
+
| Shift+Tab | Cycle interaction mode (`Default` / `Plan`) |
|
|
147
|
+
| Ctrl+O | Open the full-screen Review window: live and retained thinking, shell output, and tool details |
|
|
148
|
+
| Ctrl+Q | Review pending steering and edit, reorder, or remove queued messages; `r` resumes paused sending |
|
|
149
|
+
| Ctrl+L | Clear the terminal display (not the ACP session) |
|
|
150
|
+
| Ctrl+C | Clear the input draft; on an empty input, cancel the turn and press again within 2 seconds to exit. Any normal exit — this one, `/exit`, or a bare `exit`/`quit` — prints a one-line session usage summary, e.g. `Stat ↑12.4k ↓2.1k R84.3k W6.2k CH81.9% $0.018`: ↑/↓ are the session totals of input and output tokens, `R`/`W` the cache read and write tokens (the segment is added once the session has a cache reading; the direction with no reading is shown as 0), `CH` the cache hit rate (read / (uncached input + read + write)), and the trailing amount the session cost (three decimals below one unit, four below a tenth of a cent — miro adds main-loop requests and compaction summaries, ACP uses the agent's cumulative `usage_update` cost). The line is omitted entirely when the provider never reported usage or cost |
|
|
151
|
+
| Esc | Close shortcut help, or interrupt a running turn |
|
|
152
|
+
|
|
153
|
+
`@` completes file paths (`git ls-files`, with a filesystem fallback). Multiline pastes stay a single submission: they are collapsed while editing and restored to their original text in the transcript after submission.
|
|
154
|
+
|
|
155
|
+
## Rendering
|
|
156
|
+
|
|
157
|
+
Agent replies are rendered as Markdown in the terminal: headings, lists, tables, fenced code blocks, and inline code are styled, and `$...$` / `$$...$$` math is rendered with Unicode glyphs. Unterminated math delimiters in a stream are passed through verbatim until the turn ends, and unsupported LaTeX is left as plain text. This is display-only — your message text is never altered.
|
|
158
|
+
|
|
159
|
+
Thinking uses three disclosure levels. While a block is streaming, miro extracts a leading `**bold title**` when available and keeps the activity line stable. That live line also carries the timing and, once the provider has reported usage, the token counts, e.g. `Thinking… (12s · ↑5 ↓2.9k)`: input and output from the provider's latest reading — each miro LLM call, or the ACP `PromptResponse` usage at the end of a turn. Providers that never report usage leave the hint out. Finalized blocks follow the persisted `thinkingDisplay` setting: `compact` keeps a title-and-duration summary, `full` also prints the Markdown body, and `hidden` leaves it out of the visible transcript. `/thinking` opens the picker; `/thinking compact|full|hidden` applies a mode directly. Ctrl+O opens a full-screen review browser for the live thinking block, the live shell output, and everything retained in the transcript: the list selects an item with ↑/↓ and opens its detail with Enter, and Left/Right inside a detail moves to the previous or next item of that list.
|
|
160
|
+
|
|
161
|
+
Visible thinking blocks retain only metadata; Miro model-context checkpoints also omit raw reasoning and thinking blocks. Raw `agent_thought_chunk` messages are also omitted from ACP traffic logs by default; set `"recordRawThinking": true` in `~/.miro/settings.json` only when those sensitive details are intentionally needed for debugging.
|
|
162
|
+
|
|
163
|
+
## ACP providers
|
|
164
|
+
|
|
165
|
+
Miro uses its built-in agent by default. Start an ACP provider explicitly with `miro --acp <id>`; its binary must be on `PATH`. Add or override ACP definitions in `~/.miro/settings.json` under `providers.<id>` = `{ command | bin, args, name }`. Miro stores an ACP provider's last `/model` and `/effort` choice as `model` / `effort` in that same entry.
|
|
166
|
+
|
|
167
|
+
| Id | Name | Binary |
|
|
168
|
+
|----|------|--------|
|
|
169
|
+
| `pi` | Pi | `pi-acp` |
|
|
170
|
+
| `cursor` | Cursor | `cursor-agent` |
|
|
171
|
+
| `claude` | Claude | `claude-agent-acp` |
|
|
172
|
+
| `codex` | Codex | `codex-acp` |
|
|
173
|
+
|
|
174
|
+
### Built-in agent
|
|
175
|
+
|
|
176
|
+
Miro's built-in agent does not start a child process or speak ACP: it calls an LLM API directly, runs its own tool loop (`read_file`, `write_file`, `edit_file`, `terminal`, `grep`, `glob`, `spawn_agent`, `update_tasks`), and emits the same events as ACP clients. It supports OpenAI Chat Completions (the default), OpenAI Responses, and Anthropic Messages. It is always available, with no binary to install. Long conversations are compacted automatically; when that happens, both the transcript and the model see a `Context compacted` notice.
|
|
177
|
+
|
|
178
|
+
Commands whose output is a report — `/review`, `/simplify`, `/commit`, `/commit-push-pr`, and `/init` — run in a **separate context**. Their prompt (rubric, pre-read git context, your instructions) and every file the model reads while working stay in a history built for that one run; only a one-line record of the command (added when the run starts) and its final answer are appended to the session. A review that read fifty files therefore stops crowding out the conversation, while the answer still lands in the transcript, so a follow-up like "fix the second finding" still has something to point at. `Esc` cancels the run: the command's record and whatever was already streamed stay in the session, matching what the transcript shows, so a resumed session sees the same history. This is miro-only: an ACP provider keeps its conversation history inside the provider process, where miro can neither isolate nor trim it, so the same commands run as ordinary turns there and stay in the conversation.
|
|
179
|
+
|
|
180
|
+
Use `/compact` while idle, or `/compact preserve the migration decisions` to guide the summary. It uses the current model, keeps the visible transcript, and saves the compacted model context for session resume. It works even when automatic compaction is disabled. Press `Esc` to cancel without replacing history; insufficient history is a no-op. Pause an active goal first. Print mode also supports `miro -c <session-id> -p "/compact"`, with progress on stderr. For ACP, the command is forwarded only when the provider advertises support.
|
|
181
|
+
|
|
182
|
+
List miro LLM endpoints in `~/.miro/models.json`. `/model` reloads this file, so edits apply without restarting:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"providers": {
|
|
187
|
+
"ollama": {
|
|
188
|
+
"baseUrl": "http://localhost:11434/v1",
|
|
189
|
+
"api": "openai-completions",
|
|
190
|
+
"apiKey": "ollama",
|
|
191
|
+
"models": [
|
|
192
|
+
{ "id": "llama3.1:8b" },
|
|
193
|
+
{ "id": "qwen2.5-coder:7b" }
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`api` may be `openai-completions`, `openai-responses`, or `anthropic-messages` (at provider or model level). `apiKey` accepts a literal, `$ENV_VAR` / `${ENV_VAR}`, or `!command` (resolved when a request is sent). Selecting a model also selects that provider's `baseUrl` / `api` / `apiKey`. The picker value and the saved top-level `model` preference are always `provider/id` (for example `ollama/llama3.1:8b`). `/model llama3.1:8b` and `--model llama3.1:8b` still match when that id is unique.
|
|
201
|
+
|
|
202
|
+
For reasoning models, set `"reasoning": true`. `thinkingLevelMap`: a string maps miro's standard effort level to the provider value, `null` hides an unsupported level, omitted levels use the default mapping through `high`, and omitted `xhigh` / `max` are unsupported. `/effort` only shows levels supported by the current model.
|
|
203
|
+
|
|
204
|
+
If `models.json` is missing or has no usable models, miro falls back to the `miro` object in `~/.miro/settings.json`:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"miro": {
|
|
209
|
+
"baseUrl": "https://api.openai.com/v1",
|
|
210
|
+
"models": [
|
|
211
|
+
{ "id": "gpt-4o-mini", "name": "GPT-4o mini", "contextWindow": 128000 }
|
|
212
|
+
],
|
|
213
|
+
"model": "gpt-4o-mini",
|
|
214
|
+
"protocol": "chat-completions",
|
|
215
|
+
"effort": "medium",
|
|
216
|
+
"thinking": true,
|
|
217
|
+
"sandbox": { "enabled": false },
|
|
218
|
+
"codemode": { "enabled": false }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| Field | Default | Notes |
|
|
224
|
+
|-------|---------|-------|
|
|
225
|
+
| `protocol` | `chat-completions` | Fallback when `models.json` is unused: `chat-completions`, `openai-responses`, or `anthropic-messages`. Also configurable with `$MIRO_PROTOCOL`. Catalog models carry their own `api` instead. |
|
|
226
|
+
| `baseUrl` | Protocol-specific default | Chat Completions / Responses default to `https://api.openai.com/v1`; Anthropic Messages defaults to `https://api.anthropic.com`. `$MIRO_BASE_URL` overrides it. Catalog `baseUrl` wins for models listed in `models.json`. |
|
|
227
|
+
| `apiKey` | `$MIRO_API_KEY`, then protocol-specific key | Uses `$ANTHROPIC_API_KEY` for Anthropic Messages and `$OPENAI_API_KEY` for the two OpenAI protocols. In `models.json`, `apiKey` may also be `$ENV` or `!command`. |
|
|
228
|
+
| `models` | `gpt-4o-mini`, `gpt-4o`, `deepseek-chat` | Used only when `~/.miro/models.json` has no usable entries. Strings, or `{ id, name, contextWindow }` entries. |
|
|
229
|
+
| `model` / `effort` / `thinking` | first model / `medium` / `true` | Fallback defaults; the top-level `model` / `effort` preference takes precedence when it matches an available option. Same `/model`, `/effort`, and `/config` flows as ACP providers. |
|
|
230
|
+
| `maxToolRounds` | unlimited | Maximum tool-loop rounds per turn. There is no default cap because auto compaction already bounds the context; set a number to stop a runaway turn. |
|
|
231
|
+
| `temperature` | unset | Sampling temperature passed to the API. |
|
|
232
|
+
| `permissionMode` | `auto` | `auto` never asks the user; only Terminal calls with `risk_level: high` or `sandbox: false` receive an isolated model review, while all other tools run directly. `manual` confirms writes and commands. Unknown values and legacy `ask`, `plan`, or `yolo` fall back to `auto`. |
|
|
233
|
+
| `sandbox.enabled` | `false` | When `true`, the `terminal` tool runs commands inside the `@anthropic-ai/sandbox-runtime` network sandbox: no network unless `allowedDomains` grants it per call. The tool keeps its name in both modes and only gains the sandbox-only parameters. Filesystem access still uses host permissions. Requires the platform prerequisites of `@anthropic-ai/sandbox-runtime`; it never falls back to an unrestricted shell. |
|
|
234
|
+
|
|
235
|
+
Use `/config sandbox on` or `/config sandbox off` to update this setting and switch the command tool's sandboxing in the current session.
|
|
236
|
+
|
|
237
|
+
`/model` and `/effort` choices are remembered per provider. Miro uses top-level `model` / `effort` in `settings.json`, and records the model as `provider/id`; an ACP provider uses its own `providers.<id>` entry. The `model` / `effort` entries inside `miro` are fallback defaults when the top-level preference is absent or unavailable. Miro's `Default` / `Plan` interaction mode is independent from its `Auto` / `Manual` permission mode; `Shift+Tab` cycles the interaction mode.
|
|
238
|
+
|
|
239
|
+
### Codemode (experimental)
|
|
240
|
+
|
|
241
|
+
Codemode is off by default and available only to the built-in Miro agent. Use `/config codemode on` to enable it or `/config codemode off` to disable it; both update `miro.codemode.enabled` in `settings.json`. Set it to `true` before starting a print-mode run. Normal tools remain available.
|
|
242
|
+
|
|
243
|
+
The model can compose reads, searches, paging and aggregation in one JavaScript script inside an isolated QuickJS Worker, returning only selected output to its context. Scripts can call `read_file`, `grep`, `glob` and configured MCP bridges. They have no host `process`, `fetch`, file writes, Terminal or subagent access. Each actual MCP call still follows the normal permission policy, including denial in non-interactive Manual mode, and internal calls remain visible in the transcript.
|
|
244
|
+
|
|
245
|
+
Each script has a 60-second VM limit, a 64 MiB QuickJS heap, at most 64 tool calls and eight concurrent reads, with separate tool-data and output budgets. Scripts must await their calls. Cancellation and timeout wait for host cleanup but cannot undo remote effects; failed scripts are never automatically replayed. See the [tool reference](./docs/tools/codemode.md) for the exact API and limits. This is separate from the Terminal sandbox and does not change ACP providers.
|
|
246
|
+
|
|
247
|
+
### MCP
|
|
248
|
+
|
|
249
|
+
Configure trusted servers in the top-level `mcpServers` object in `~/.miro/settings.json`:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"mcpServers": {
|
|
254
|
+
"local-tools": {
|
|
255
|
+
"command": "node",
|
|
256
|
+
"args": ["/absolute/path/server.js"],
|
|
257
|
+
"env": { "SERVICE_TOKEN": "your-token" }
|
|
258
|
+
},
|
|
259
|
+
"remote-tools": {
|
|
260
|
+
"type": "http",
|
|
261
|
+
"url": "https://example.com/mcp",
|
|
262
|
+
"headers": { "Authorization": "Bearer your-token" },
|
|
263
|
+
"connectTimeoutMs": 10000,
|
|
264
|
+
"timeoutMs": 60000
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Replace the sample paths, URLs and credentials with your server's values. Stdio launches `command` directly without a shell. HTTP uses Streamable HTTP, not legacy SSE. Use `${NAME}` to read credentials from environment variables in command, args, env, URL or headers. Add `"enabled": false` to skip a server. Timeouts are positive integer milliseconds: 10 seconds for connecting and 60 seconds for a request by default. These settings apply to the built-in agent only.
|
|
271
|
+
|
|
272
|
+
Project servers go in `<cwd>/.miro/mcp.json` under the same `mcpServers` key and override global servers with the same name. Run `miro mcp trust` in a terminal, or `/mcp trust` in the TUI, after reviewing the file; editing it revokes trust. `miro mcp add <name> -- <command> [args...]`, `miro mcp add <name> --url <url>`, `miro mcp remove <name>` and `miro mcp list` manage servers; `--local` targets the project file. Restart or use `/mcp` to reload after changes. `/mcp` also shows server status, tools and sign-in actions.
|
|
273
|
+
|
|
274
|
+
The built-in agent exposes only two MCP tools: `mcp_list_tools` discovers tools and their parameter schemas, and `mcp_call` invokes them. Remote tools are always discovered on demand, never declared as additional model tools. Connections are reused across turns and closed with the session. Tool-list notifications refresh cached schemas. Text, embedded text resources and structured JSON returned by tools use the normal output budget; resource links remain metadata only, and image, audio and binary blocks are represented by notices. Resource listing, URI templates, resource reading, Prompts and task-based execution are not supported.
|
|
275
|
+
|
|
276
|
+
HTTP servers without an Authorization header can use OAuth. Run `miro mcp login <name>` or select Sign in under `/mcp`; credentials are stored in `~/.miro/mcp-auth.json`. Use `miro mcp logout <name>` or the TUI to remove them. The sign-in browser must reach the local callback port (default 8765; configurable with `oauth.callbackPort`). Press Esc or Ctrl+C in the TUI sign-in panel to cancel. Closing the session also cancels sign-in and releases the callback port; the complete OAuth flow has a three-minute timeout.
|
|
277
|
+
|
|
278
|
+
Sign-in follows the server's `WWW-Authenticate` resource metadata URL. Scopes come from the authentication challenge or resource metadata (`scopes_supported`, with `resource_scopes` supported for compatible gateways), falling back to the saved client's registered scope. To override the requested scopes during sign-in, set a non-empty, space-separated string such as `"oauth": { "scope": "openid profile offline_access" }` on the HTTP server entry; use only scopes supported by that server. Empty or null token-response scopes are treated as absent, and an omitted scope preserves the requested scopes. Sign-in validates the callback's `iss` when present or required by the authorization server. OAuth failures identify the stage and error code without printing token responses.
|
|
279
|
+
|
|
280
|
+
**Only configure servers you trust.** Discovery may start local processes even in Manual mode. Actual MCP invocations require Manual approval scoped to the server, tool and complete arguments; non-interactive Manual runs deny them. Auto runs MCP calls without prompts or Terminal safety review. MCP is outside the Terminal sandbox. Cancelling or timing out does not guarantee that remote effects were undone; calls are never automatically replayed. Keep `settings.json` private; tool arguments and results are still session data.
|
|
281
|
+
|
|
282
|
+
ACP sessions receive the trusted configured servers, but the external agent owns connections, permissions and OAuth. HTTP entries are skipped with a diagnostic unless the agent advertises support. Miro's ACP traffic log redacts MCP connection configuration; the external agent still receives the credentials and may keep its own logs.
|
|
283
|
+
|
|
284
|
+
### Skills
|
|
285
|
+
|
|
286
|
+
Miro supports the `SKILL.md` convention (Agent Skills). Skills are discovered at startup from `<cwd>/.miro/skills`, `<cwd>/.agents/skills`, `~/.miro/skills`, and `~/.agents/skills`, in that priority order. Extra directories go in a top-level `skills` array in `~/.miro/settings.json`; `"skills": false` turns the feature off. A directory containing `SKILL.md` is one skill and is not scanned any deeper, while a bare `*.md` file counts only when its frontmatter has a `description`. When two skills share a name, the higher priority root wins (explicit paths take precedence over default roots), and the shadowed file is reported in the client's diagnostics.
|
|
287
|
+
|
|
288
|
+
Frontmatter uses YAML core syntax and must be a mapping with a non-empty string `description`. Quote values that look like numbers or booleans if you intend them as text. Unicode escapes, block scalars, and bounded aliases are supported; custom tags and merge-key expansion are not. Malformed YAML, duplicate keys, excessive alias expansion, or frontmatter exceeding 64 KiB cause the skill to be skipped with a diagnostic.
|
|
289
|
+
|
|
290
|
+
Only each skill's name, description, and file path go into the system prompt, inside `<available_skills>`; the body is loaded on demand with `read_file`. `/skill:<name> [args]` injects that skill's body into the message instead, which works in `-p` runs too:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
/skill:pdf extract the tables from invoice.pdf
|
|
294
|
+
miro -p "/skill:pdf extract the tables from invoice.pdf"
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`disable-model-invocation: true` keeps a skill out of the model-visible catalog while leaving `/skill:` available for hand-picked use. Skills only add instructions — they never add capabilities: Manual approval and Auto's Terminal review policy remain unchanged.
|
|
298
|
+
|
|
299
|
+
This is miro-only. ACP providers own their skills, and any command they advertise (for example `skill:<name>`) reaches miro as a normal provider command.
|
|
300
|
+
|
|
301
|
+
## Sessions and context
|
|
302
|
+
|
|
303
|
+
Miro saves model-context checkpoints alongside the visible transcript, including compaction summaries and complete tool-call/result pairs. Resume uses the latest valid checkpoint rather than replaying the pre-compaction transcript; sessions without checkpoints fall back to visible messages. Checkpoints contain conversation and tool data, so keep session files private.
|
|
304
|
+
|
|
305
|
+
Visible transcripts are stored at `~/.miro/sessions/<flattened-cwd>/<acp|miro>/<sessionId>.jsonl` and can be resumed with `/resume`, `--continue`, or `--resume`. Raw ACP traffic is isolated under `<flattened-cwd>/acp/raw/`, so it cannot collide with the resumable transcript for the same session ID. The project directory name is the cwd with every non-alphanumeric character collapsed to `-` (no hash, so it stays readable). Raw thinking chunks are omitted unless `recordRawThinking` is explicitly enabled. Persistence errors are non-fatal; older transcript layouts are not read. A visible session is identified by its runtime **and** its session ID. `/resume`, `/sessions`, and `--continue` show only sessions for the current startup mode; to resume an ACP transcript, restart with that transcript's `--acp <id>`. Empty conversations leave nothing behind: the transcript file (and its runtime directory) is created only when the first user or assistant message is recorded, and zero-message transcripts are not listed by `/resume`, `/sessions`, or `--continue` (raw ACP traffic logs are exempt — they still start as soon as the session opens).
|
|
306
|
+
|
|
307
|
+
`~/.miro/AGENTS.md` and `./AGENTS.md` are loaded at startup (blank or unreadable files skipped; identical text deduplicated) and sent with the **first ordinary prompt**. Resumed sessions skip AGENTS.md reinjection. A failed first request restores injection so you can retry.
|
|
308
|
+
|
|
309
|
+
Composer history is independent of the transcript, at `~/.miro/history.jsonl`.
|
|
310
|
+
|
|
311
|
+
## Configuration
|
|
312
|
+
|
|
313
|
+
Model, effort, prompt-language, status-line, and custom ACP-provider preferences live in `~/.miro/settings.json`. Its legacy top-level `provider` value is ignored. Miro LLM endpoints and models are imported only from `~/.miro/models.json`. `/config` changes both the live ACP options advertised as `configOptions` and miro's own `language` option.
|
|
314
|
+
|
|
315
|
+
### Permission mode
|
|
316
|
+
|
|
317
|
+
Miro offers `Auto` and `Manual`, with `Auto` as the default. `/permissions` selects a mode directly or opens a picker; this setting is independent from Plan Mode.
|
|
318
|
+
|
|
319
|
+
| Mode | Behavior |
|
|
320
|
+
|------|----------|
|
|
321
|
+
| `auto` | Default. Never opens a user approval prompt. Non-Terminal tools run directly; Terminal is auto-reviewed only for `risk_level: high` or `sandbox: false` |
|
|
322
|
+
| `manual` | Writes, edits, deletes, moves, and commands need approval; read, search, and task updates run directly |
|
|
323
|
+
|
|
324
|
+
`Auto` does not parse Terminal command text, so `rm`, `curl`, redirection, interpreters, and heredocs have no special lexical rules; `low` and `medium` run directly. For `high` or `sandbox: false`, it projects user intent, earlier tool calls, and the proposed action (including the model's optional `risk_reason`, treated as an untrusted self-report) to an isolated classifier with no tools, never tool results. Only an explicit approval runs the command. A block, error, or uncertain result is returned directly to the next model round without asking the user. The verdict shows on the tool row, and a block spells out the reviewer's reason there.
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
miro --permission-mode auto
|
|
328
|
+
miro --permission-mode manual
|
|
329
|
+
/permissions manual
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`/permissions` and CLI overrides affect only the current run; set `miro.permissionMode` to persist a startup preference. Legacy `ask`, `plan`, `yolo`, and unknown settings fall back to `auto`, but invalid explicit `--permission-mode` values are errors.
|
|
333
|
+
|
|
334
|
+
When configured, the `permission-mode` status-line item shows `AUTO` or `MANUAL`. Headless miro runs print the effective policy to stderr while keeping stdout clean for results.
|
|
335
|
+
|
|
336
|
+
### Plan mode
|
|
337
|
+
|
|
338
|
+
Plan Mode separates investigation and design from implementation. Enter it with `/plan`, `/plan on`, or Shift+Tab; the model can also request entry, which requires confirmation. It keeps the normal Terminal, file-write, subagent, and local-command capabilities; planning rather than implementation is a behavioral instruction, not a read-only runtime restriction. The canonical plan is stored under `~/.miro/sessions/<project>/miro/plans/<session>/<plan-id>.md`.
|
|
339
|
+
|
|
340
|
+
In either interactive mode, the agent can ask up to four structured questions with single-select or multi-select answers, optional Markdown previews, custom text, and notes. In Plan Mode it then submits the canonical file for review. Approving freezes that exact text, returns to Default Mode, and starts a fresh implementation turn from the frozen snapshot. Requesting changes keeps Plan Mode active; rejecting exits without implementation. Plan state is persisted with the session. Plan Mode cannot start while a goal is actively running. Non-interactive runs and subagents never expose the question UI.
|
|
341
|
+
|
|
342
|
+
`miro -p --mode plan "Design this change"` is intentionally non-interactive: it prints a plan and exits without asking questions, opening approval UI, or implementing the result. ACP providers keep their own mode behavior.
|
|
343
|
+
|
|
344
|
+
### Spinner verbs
|
|
345
|
+
|
|
346
|
+
The waiting-verb animation on the status line is customizable via `spinnerVerbs` in `~/.miro/settings.json`:
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"spinnerVerbs": { "mode": "append", "verbs": ["thinking", "planning", "searching"] }
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`mode` is `append` (add to the built-in verb list) or `replace` (use only your list). A blank or invalid `verbs` falls back to the defaults.
|
|
355
|
+
|
|
356
|
+
### Prompt language
|
|
357
|
+
|
|
358
|
+
`/config language English` (or `en`, `en-us`), `/config language Chinese` (or `zh`, `zh-cn`, `中文`) or `/config language Cantonese` (or `yue`, `zh-hk`, `zh-yue`, `粤语`, `粵語`) sets the language of miro's own built-in workflow prompts (`/init`, `/review`, `/simplify`, `/commit`, and `/commit-push-pr`) and saves it as `language` in `~/.miro/settings.json`. English is the default, and unknown values fall back to it. The ACP session config and your own messages are untouched; the prompt text itself lives under `src/commands/prompts/<language>/`, so adding a language means adding a directory there.
|
|
359
|
+
|
|
360
|
+
### Status line
|
|
361
|
+
|
|
362
|
+
`/statusline` picks items, order, and colors interactively. `/statusline reset` restores the defaults. Confirming writes `statusLine` and `statusLineUseColors` together.
|
|
363
|
+
|
|
364
|
+
Default items: `["model-with-reasoning", "current-dir"]`. An empty array hides the status line. Unknown ids are ignored (one-time warning); items without data are skipped.
|
|
365
|
+
|
|
366
|
+
While a goal exists, the right end of the same line shows `◎ /goal active (4s)`: the goal's status plus how long it has actually been running (paused time and time after it stopped are not counted, matching the budget). It is not a configurable item — it appears and disappears with the goal — and the ACP provider, which has no goal mode, never shows it. An empty status line hides it along with the rest of the bar.
|
|
367
|
+
|
|
368
|
+
Available ids include `model`, `model-with-reasoning`, `reasoning`, `mode`, `permission-mode`, `current-dir`, `project-name`, `hostname`, `git-branch`, `run-state`, `provider`, `session-id`, `session-title`, `miro-version`, `context-used`, `context-remaining`, `context-window-size`, `task-progress`, `goal`, `used-tokens`, `input-tokens`, `output-tokens`, `thought-tokens`, and `session-cost`. Context, token, cost, and session-title items need the provider to report them. `used-tokens`, `input-tokens`, `output-tokens`, `thought-tokens`, and `session-cost` are session totals — miro adds each main-loop LLM request and compaction summary (subagent runs are not counted), ACP uses the agent's cumulative `PromptResponse` snapshot (and its cumulative `usage_update` cost) — so they can exceed the latest-reading hint on the activity line. `permission-mode` shows `AUTO` or `MANUAL` for the miro provider, and stays hidden when no miro permission mode is available. `goal` shows the active goal's status and progress, and stays hidden without a goal — the ACP provider has no goal mode, so it is always hidden there. These ids also accept short aliases: `model-name` (`model`), `permissions` (`permission-mode`), `project` / `project-root` (`project-name`), `status` (`run-state`), `thread-id` / `thread-title` (`session-id`), `version` (`miro-version`), `context-usage` (`context-used`), `goal-status` (`goal`).
|
|
369
|
+
|
|
370
|
+
## License
|
|
371
|
+
|
|
372
|
+
ISC
|