Multi-Agent Prompt ✍️
A crash-proof, autosaving prompt editor for AI coding agent CLIs.
Chat text boxes lose long, carefully-composed prompts to a single wrong
keystroke, and give you almost no editing power while you write. multi-agent-prompt
moves composition out of the chat box entirely: write in a real Neovim, in a
split pane next to your agent session, autosaved to disk every couple of
seconds so a crash or a fat-fingered shortcut can never cost you more than a
moment of typing. When you're ready, hand it to the agent with /prompt —
which reads it, archives it, and clears the file in one step, so there's no
separate "now go clear it" to remember.
Install
pip install multi-agent-prompt
# or
uv tool install multi-agent-prompt
Requires Neovim — the autosave hook is Lua, so plain Vim isn't supported.
Usage
Open a split pane next to your agent CLI (Windows Terminal: Alt+Shift+-;
tmux: <prefix> ") in the same working directory, then run:
map
This opens .ma/prompt/current.md — resolved to the current project's git
root, so every pane in the same repo shares one scratch file — in your actual
nvim, your actual init.lua, your actual keymaps and plugins. No config
import step, no reimplemented Vim subset: it's just Neovim. You land in
insert mode already, cursor at the end of the buffer, so there's no i
to press before you start typing (--no-insert if you'd rather open in
normal mode). A buffer-local autocmd (scoped only to this one buffer — it
never touches how you edit anything else) autosaves ~2 seconds after you stop
typing, and immediately when you switch away from the pane
(FocusLost/BufLeave), so the save always beats you to the chat window.
When you're done, switch back to the agent pane and either:
- Type
/prompt(if the agent's install includes the command — see below). The host, not the model, reads the draft, splices it into your message, archives it, and clears the file — the model only ever sees the draft content, never a command or a file-operation instruction, or - Reference
@prompt.md/@.ma/prompt/current.mddirectly, if your agent supports file mentions and can see gitignored files — this is a plain read (by the model), so unlike/promptit doesn't archive or clear anything.
Pasting a long CLI transcript or diff in? Anything 6 lines or more
(bracketed paste, "+p, "*p — any paste source) auto-folds, closed, so it
doesn't bury the rest of what you're writing — za/zo/zc (standard Vim)
toggle it open.
The session is also styled like a prompt editor (again, only for the
throwaway nvim map launches, never your own): line numbers are off and a
> prompt marker follows your cursor in the gutter, a statusline footer
lists whichever keymaps are active this session, and the background is
forced true black (#000000) so it melts into a black terminal while your
colorscheme's foregrounds survive. All of it degrades to a no-op on an
older Neovim instead of blocking the session.
Also want to browse past drafts without leaving the editor? A buffer-local
keymap — <leader>ph by default — lists archived drafts (newest first) in a
split; <CR> opens one, read-only. Forgot what any of this is bound to?
Press g? — it shows exactly the keymaps active for this session
(respecting any --clear-key/--history-key overrides, and omitting
whichever ones you disabled):
map # open the scratch file (same as `map edit`)
map where # print the resolved file path
map show # print its current content to stdout (does not clear it)
map pop # print it, archive it, and clear it — what /prompt uses
map clear # archive the current draft, then empty it, without printing it
map --clean # skip your init.lua entirely (-u NONE) for faster startup
map edit --clear-key '<F5>' # rebind the in-editor clear keymap
map edit --no-clear-key # don't register it at all
map edit --history-key '<F6>' # rebind the in-editor history-browse keymap
map edit --no-history-key # don't register it at all
map edit --help-key '<F1>' # rebind the in-editor g? cheatsheet keymap
map edit --no-help-key # don't register it at all
map edit --fold-threshold 3 # auto-fold pastes of 3+ lines instead of 6
map edit --no-fold-paste # don't auto-fold pastes at all
map edit --no-insert # open in normal mode instead of insert mode
map edit --no-prompt-gutter # keep your line numbers/gutter (no '> ' marker)
map edit --no-footer # don't add the keymap footer to the statusline
map edit --no-trueblack # keep your colorscheme's own background
map clear --keep 20 # override how many archived drafts to retain
map clear --no-archive # discard instead of archiving (e.g. it had a secret in it)
map pop accepts the same --keep/--no-archive as map clear.
The archive
map pop/map clear — from the CLI or the in-editor keymap, which shells
out to map clear — never actually discard a non-empty draft; they move it
to .ma/prompt/archive/<timestamp>-<seq>.md first, then prune that archive
down to the most recent entries. Pruning is by count, not age (--keep /
MAP_ARCHIVE_KEEP env var, default 10) — simpler to reason about than a
retention window, and it doesn't depend on the clock. Use --no-archive for
the one case that shouldn't be kept anywhere: you pasted a secret and want it
actually gone.
Performance
A map session opens your entire Neovim config, which is exactly the
point — but if that config is large, startup isn't free. Two things worth
knowing, from actually measuring it (not guessing):
-
It's not a one-time thing. Whatever makes your first
mapslow will make every subsequent one about equally slow, unless something changes (lazy.nvim itself finishes installing/compiling once; LSP servers and large plugin trees generally don't get meaningfully faster after that). -
On WSL specifically, check for
vim.opt.clipboard = 'unnamedplus'(or similar) in your own init.lua. Setting that option makes Neovim probe for a clipboard provider immediately, and on WSL that probe walks everyPATHentry checkingexecutable()— including everything under/mnt/c/..., which is slow cross-filesystem interop. Measured on this project's own dev machine: ~900ms startup with that line in play, ~160ms with the WindowsPATHentries stripped, ~4ms with-u NONE. That's not multi-agent-prompt overhead; it's one line in a personal init.lua interacting badly with WSL, and fixing it (or guarding it — seeMAP_SESSIONbelow) speeds up every Neovim session, not justmap. -
map --clean(-u NONE) sidesteps all of it by skipping your config entirely — the built-in fast path when you don't need your plugins for a quick edit. -
MAP_SESSION=1is set in the environment of every nvimmaplaunches. Nothing in this package reads it — it's there so you can guard an expensive line in your own init.lua behindif not vim.env.MAP_SESSION then ... end, if you want full-config speed back without giving up fastermapstartup. That's your config to edit, not something this tool does for you. This isn't just for the clipboard line above — a scratch prompt buffer has no use for a file-tree sidebar or git tooling either, and with lazy.nvim you can skip those specific plugins the same way, withcondon the spec:return { 'nvim-neo-tree/neo-tree.nvim', cond = not vim.env.MAP_SESSION, -- ... }
Confirmed on this project's own dev config: with
neo-tree.nvimandgitsigns.nvimguarded this way,<leader>e(NeoTree's toggle) becomes a no-op in amapsession, and their entire module trees (dozens of individualrequire()calls each) simply don't load — real startup savings, not just fewer keymaps to trip over.condis evaluated on every start, so this takes effect immediately; no:Lazy sync/restart needed. -
It's specifically a startup cost, not a per-save one. The clipboard probe runs once, while your init.lua is sourced, not again on autosave, on
FocusLost, or when you switch panes and type/prompt— that last step never touches nvim at all (map pop/reading the file is a separate, plain filesystem read and write). If a session still feels slow at the hand-off moment specifically, that's terminal pane-switching or the agent's own slash-command overhead, not this tool or Neovim. -
/prompt's own overhead is small, and it isn't Neovim's. Measured on this project's own dev machine:map popruns in ~60-85ms end to end — barepython3 -c passalone is ~30ms of that, this package's own imports add maybe another ~20ms, and the rest is the file read/archive/write. If/promptstill feels slow, that latency lives in the agent host's own prompt-construction path, not in anything this package does — there's no nvim process in this path at all. Claude Code's and Grok's variants add one hook subprocess (map pop --stage, same ~60-85ms budget).
Agent integration
The design rule for /prompt: the host, not the model, does the work.
Reading the draft, splicing it into the outgoing message, archiving, and
clearing the scratch file all run locally while the prompt is being built.
The model receives the draft verbatim — a handoff is indistinguishable
from the user having typed it into the chat box — or, when nothing was
composed, a single locally-generated notice ("no prompt to hand off"). No
tags, no framing, no file-operation instructions. (Grok has no splice
primitive at all — it's the one degraded case, a staged model-side read;
see the bullet and commands/grok/README.md.)
- OpenCode: a command file whose
!`map pop`template substitution executes at send time and splices only the output into the prompt. - Claude Code: a command file that
@-includes the draft, plus aUserPromptExpansionhook that runsmap pop --stagefirst — archive + clear + stage the content where the include reads it. (Claude's own!`injection annotates the model-visible message with a● Bash(...)line, hence the different mechanism.) - Antigravity CLI (
agy): a frontmatter-only skill (so/promptexists as a slash command) plus aPreInvocationhook that pops locally and injects the prompt as a genuine user message — agy has no include or substitution primitive, but its hooks can inject trajectory steps. - Grok (xAI Build): no splice, substitution, or injection primitive
exists (verified — no
!`cmd`, no@file, noUserPromptExpansion), so/promptis a flat command file plus aUserPromptSubmithook that runsmap pop --stagebefore the model is invoked and writes the popped draft (or the empty-handoff notice) to the staged.ma/prompt/handoff.md; the command body then tells the model to read that staged copy and treat it as the message. The pop stays host-side and exactly-once; the unavoidable residue is the short read-only command body and the model's ownread_fileof the staged file.
See commands/README.md for the per-host
comparative table, templates, hook scripts, and install steps —
commands/decisions.md for the cross-cutting
design decisions, and commands/turn-end.md for
the turn-end hook surface (agent done / question / report).
skills/prompt/ is a read-only fallback for hosts without local
expansion primitives: it teaches an agent only to read
.ma/prompt/current.md — and explicitly forbids running map pop or
touching the file. Never install it where a /prompt command file exists
(a same-named skill wins in Claude Code, and its instruction body is
exactly the model-driven file-op noise the command design avoids). See
skills/README.md.
Install every host with one command
map hosts install wires /prompt into each agent CLI this machine
actually runs — it writes each host's command/skill template and hook
script into that host's own config root (on Windows:
%USERPROFILE% / %APPDATA%; elsewhere: ~ / $XDG_CONFIG_HOME), and
merges the required hook entries into each host's existing config. Run it
in every shell you use — a run in PowerShell wires the native-Windows
hosts, a run in WSL wires the WSL ones:
uv tool upgrade multi-agent-prompt # `map hosts` ships in 0.1.8+
map hosts status # what's detected / missing on this platform
map hosts install # wire opencode, claude, agy, grok
map hosts install --host claude # just one host
map hosts uninstall # remove what the installer wrote
The installer is conservative by design: re-running it is a no-op
(idempotent), it never replaces a file whose content differs from ours
unless you pass --force, and settings.json / hooks.json merges only
ever gain (or remove) the installer's own hook entry — hooks you
configured yourself are left exactly as you wrote them. --dry-run prints
exactly what would change, --host limits scope, broken JSON is reported
and left untouched, and nothing is deleted that the installer didn't write.
Why not tmux send-keys / auto-injection?
That's the obvious next step — save, close the pane, and have the tool type
/prompt into the neighboring agent pane for you — but it needs a reliable
way to identify which pane is running the agent, which varies by terminal
(tmux panes, Windows Terminal panes, and OS-level input injection like
xdotool/wtype all solve a different half of that problem). The manual
handoff above works everywhere today; auto-injection is being explored as a
follow-up, potentially building on multi-agent-registry's
chat discovery to identify a live agent session (not just a recent one) in
the same directory.
If/when that lands: prefer sending a widely-supported "submit" keystroke —
Ctrl+Enter is the closest thing to a universal convention across chat
input boxes — into the target pane after typing /prompt, over anything
input-method-specific, so the same injection code has a chance of working
across agents rather than being re-tuned per host.
Design notes
- The prompt file is per-project, not global.
.ma/prompt/current.mdresolves against the nearest.gitroot, so working on two projects in two terminal tabs never mixes up their scratch files. .ma/is a shared root, not this tool's alone. It's the umbrella directory for the whole multi-agent-* line (map's CLI alias is the sharedmaprefix ofmap/mar/maa) — one dotfolder instead of one per product. Only.ma/prompt/is this tool's; a sibling product could use.ma/registry/, etc., without colliding. Note thattask-agent's existing.task-agent/convention predates this and stays as-is — migrating an already-shipped tool's config layout is a separate, larger decision..gitignoreis managed for you. The first timemap editruns in a git repo, it appends.ma/prompt/to.gitignoreif it isn't already covered — including by a broader pre-existing.ma/entry.- Reading is non-destructive; handing off isn't, but it never discards
either.
map showand@prompt.md(file mention) just print/read — the file is untouched either way./prompt(map pop) clears it, because the whole point is not needing a separate "now go clear it" step — but it archives first, so "cleared" never means "gone".--no-archiveis the explicit opt-out for content that shouldn't be kept anywhere at all. - The editor notices when the file changes out from under it.
/promptrunsmap popfrom the agent's process, not from inside the running nvim session — so if you're still looking at that buffer when you hand a draft off, it auto-reloads (autoread+checktimeonFocusGained/BufEnter) the next time you focus it, rather than keep showing the text you already submitted. It won't discard anything you've since typed there unsaved.
Release files for multi-agent-prompt 0.1.9
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| multi_agent_prompt-0.1.9.tar.gz | 80.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| multi_agent_prompt-0.1.9-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 133.8 kB
Release files / multi_agent_prompt-0.1.9.tar.gz
| Download URL | multi_agent_prompt-0.1.9.tar.gz |
|---|---|
| Size | 80.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
29cc79787ef7b13bd513700ddd406452bea489acb37ee153e0398946b34fc4ce
|
|
BLAKE2b-256 checksum How to use checksums |
9d22679db37970d2d77312065947659d15170bc1dcc101664411f6ce5a9a20c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / multi_agent_prompt-0.1.9-py3-none-any.whl
| Download URL | multi_agent_prompt-0.1.9-py3-none-any.whl |
|---|---|
| Size | 52.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
56712c3e6a54554df81309883c7d4f7bc2d731c7bb9d1844e7679e749991a71b
|
|
BLAKE2b-256 checksum How to use checksums |
13a89df3ce80f0341c8232d28b9dc9d0141c2edc1afbf16bed5a6ab09614391c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log