Comment on tmux pane output and feed it back to the agent.
Select, annotate, stack. Flush when you're done — scroll position never moves.
You're watching a claude or codex agent produce a long reply in a tmux pane. You want to scroll up, pick out specific passages, push back on each one, and send the whole annotated reply as your next prompt — without losing your place in the scrollback every time you touch the keyboard. tmcmt gives you exactly that loop: commented chunks accumulate in a per-pane draft file on disk, and nothing touches the agent's prompt until you explicitly flush.
- 📎 Chunks stack into a draft — select a passage, type a comment, repeat. The draft grows on disk, not in the pane.
- 🧭 Scroll position is preserved — the pane's PTY is never written to during the add loop, so copy mode stays put.
- ✍️ nvim popup for every edit — your keybindings, your config, your muscle memory. No custom TUI to learn.
- 🔁 Review pass before flush — open the whole accumulated draft in nvim, reorder, polish, or delete chunks before sending.
- 📬 Bracketed-paste injection — the whole draft arrives as one coherent message, not N fragments to stitch together.
- 💾 Recoverable drafts — survive accidental popup closes, tmux kills, restarts. Just hit
Cagain. - 🧹 Stale-draft GC — drafts for dead panes are cleaned up automatically on every add/flush.
- 🔧 Scriptable primitive —
tmcmt sendpipes stdin straight into any pane, for automation and future multicast.
┌─ agent pane (copy mode, scrolled up) ───┐
│ the agent's reply… │
│ cursor at line 147 of scrollback │ never moves during the loop
└──────────────────────────────────────────┘
▲ ▲
│ c │ c
│ │
▼ ▼
┌─ popup: nvim ─┐ ┌─ popup: nvim ─┐
│ type comment │ │ type comment │
│ :wq │ │ :wq │
└───────────────┘ └───────────────┘
│ │
▼ ▼
append → ~/.local/state/tmcmt/drafts/<pane>.md ← append
│
(later)
│ C = flush
▼
┌─ popup: nvim ─┐
│ review draft │
│ reorder/edit │
│ :wq │
└───────────────┘
│
▼
paste whole draft into pane (bracketed paste, no Enter)
cin copy mode → nvim popup, type comment,:wq. Chunk appended to draft. Pane untouched.Cin copy mode → nvim popup opens on the accumulated draft for a final review.:wqpastes into the pane.
You hit Enter yourself when the prompt looks right.
Requires Go 1.24+, tmux 3.2+, and nvim (or any $EDITOR, but nvim/vim get insert-mode-on-open).
git clone <repo-url> tmcmt
cd tmcmt
make install # builds and copies to ~/bin/tmcmtMake sure ~/bin is on your PATH.
Add to ~/.tmux.conf and reload (tmux source-file ~/.tmux.conf):
# c = append a commented selection to this pane's draft (no paste)
bind-key -T copy-mode-vi c send-keys -X copy-pipe-no-clear \
"tmcmt draft add --pane '#{pane_id}'"
# C = flush the draft: review in nvim popup, paste into pane, clear
bind-key -T copy-mode-vi C send-keys -X copy-pipe-no-clear \
"tmcmt draft flush --pane '#{pane_id}'"Both bindings use copy-pipe-no-clear so copy mode stays active and scroll position is preserved.
Inside a tmux pane running claude or codex:
M-w(or whatever opens your copy mode) and scroll up to something the agent said.vselect a passage.c— nvim popup opens with an empty comment area on top and your selection rendered as a commented-out preview below aTMCMT-SELECTION-BELOWseparator.- Type your comment,
:wq. Status bar flashestmcmt: 1 chunk in draft — C to flush. - Scroll somewhere else, select,
cagain. Counter ticks up. - When the whole reply is assembled,
C. nvim popup opens on the full draft. Reorder, edit, or delete chunks freely. :wq— draft pastes into the agent's prompt via bracketed paste. No auto-Enter.- Review in the live prompt and hit Enter yourself when it looks right.
If you exit nvim without changes (:q! or :wq on an unmodified file), the chunk is treated as cancelled — nothing is appended.
tmcmt draft add --pane <id> # append a commented chunk (bound to `c`)
tmcmt draft flush --pane <id> # review + paste draft (bound to `C`)
tmcmt draft show --pane <id> # print current draft to stdout
tmcmt draft clear --pane <id> # discard current draft
tmcmt draft list # list all drafts with pane status
tmcmt send --pane <id> # paste stdin → pane (scriptable)
tmcmt --version--pane defaults to the current pane, so from a shell inside the agent pane you can just run tmcmt draft show without arguments.
| Flag | Description |
|---|---|
--no-review |
Skip the nvim review pass, paste the draft as-is |
--send |
Press Enter after pasting (auto-submit) |
--dry-run |
Print the final payload to stdout instead of pasting |
| Flag | Description |
|---|---|
--pane <id> |
Target pane id (default: current pane) |
--enter |
Press Enter after pasting |
Draft files live at ~/.local/state/tmcmt/drafts/<pane-id>.md (pane id with the % stripped — e.g. %42 → 42.md). Each chunk is appended as:
<your comment>
```
<selected terminal text>
```
(blank line)
Chunks just accumulate — no separators, no metadata. When flushed, the raw file content is the payload.
The obvious design is "select → comment → paste immediately, re-enter copy mode" — and it's wrong. The paste bumps the pane back to the prompt, claude-code redraws, and even if you re-enter copy mode you land at the bottom of the scrollback, not where you were reading. You lose your place every cycle.
Draft accumulation fixes this structurally:
- The pane's PTY is never written to during the
cloop. Copy mode stays active, scroll position is preserved, selections clear automatically so you can make the next one without pressing Escape. - Bonus wins: you see the whole reply together before sending, you can reorder/polish/delete, and the draft survives a crash or accidental popup close.
- Cost paid: one extra keystroke (
Cto flush) and one extra nvim popup for the review. Both feel cheap.
- Cancellation —
:q!or:wqwithout edits → chunk cancelled, nothing appended. - Empty comment — allowed. You get a selection-only chunk.
- Stale drafts — every
add/flushwalks the drafts dir and deletes files whose pane id no longer exists intmux list-panes -a. - ANSI in selections — CSI (color) and OSC (hyperlinks, titles) sequences are stripped before the selection hits your draft.
- Multi-line content — pasted with bracketed paste (
-p), so claude-code treats it as one message instead of submitting each line.
tmcmt send is a straight-up "pipe text into a pane's prompt" primitive:
# dump a log into a running claude pane
tail -100 build.log | tmcmt send --pane %42
# with auto-enter
echo "rerun the last test" | tmcmt send --pane %42 --enter
# primitive for future multicast wrappers:
for pane in %42 %47; do
echo "explain this" | tmcmt send --pane $pane
donetmcmt/
├── main.go
├── cmd/ # cobra subcommands (draft_add, draft_flush, send, ...)
├── internal/
│ ├── tmux/ # tmux CLI wrappers (run, paste, popup, display-message)
│ ├── draft/ # per-pane draft file CRUD + GC
│ ├── chunk/ # nvim compose template build/parse
│ └── sanitize/ # ANSI CSI + OSC strip
└── Makefile
Personal tool built for my own workflow — I live inside tmux panes running claude/codex all day and got tired of losing scroll position. Feel free to fork and adapt.