opencode-copy-message
v0.1.1
Published
Copy messages from the opencode TUI: last message, any message, transcript popup, or external editor
Maintainers
Readme
opencode-copy-message
Copy anything out of the opencode TUI — any single message, the last assistant message, or the whole session transcript — without fighting the TUI's mouse capture or the built-in "last assistant message only" copy.
Features
- Copy any message — open a picker of every message in the session and copy one in full, including multi-part responses.
- Copy the last message — replaces the stock
Copy last assistant messagecommand with a shadowed version that behaves the same way. - Open in your editor — dump the full transcript to a temp file and open it
in
nvim(or any editor you configure) for real vim-style yanking. - Built-in transcript viewer — a scrollable, mouse-selectable popup that matches opencode's own dialog styling.
- Mouse capture toggle — flip TUI mouse capture off at runtime so your terminal's native selection works; flip it back on when you're done.
Every command is available both from a keybind and from the command palette
(ctrl+p).
Requirements
- opencode 1.18.x (uses the TUI plugin API: keymap layers, dialog replacement, renderer handoff).
- A terminal with OSC 52 clipboard support for message copies (WezTerm, Ghostty, kitty, iTerm2, tmux, Zellij — most modern terminals). The popup's drag-select uses native terminal selection and always works.
Install
From npm
Fetch the package into opencode's plugin cache:
opencode plugin opencode-copy-message --globalThe command also registers the package in opencode.jsonc (the server plugin
list). This plugin is TUI-only, so remove that line again and add the spec to
your global tui.json instead:
{
"plugin": [
["opencode-copy-message", {
"editor": "nvim",
"keybinds": {
"copy-message": "<leader>o",
"copy-message.nvim": "<leader>v",
"copy-message.popup": "<leader>p",
"copy-message.mouse": "<leader>m"
}
}]
]
}Restart opencode.
From a local checkout
Clone or copy the repo, then point tui.json at the entry file:
{
"plugin": [
"file:///path/to/opencode-copy/copy-message.ts"
]
}Usage
ctrl+x is opencode's leader key (see Configuration to
rebind it).
| Key | Command | What it does |
| --- | --- | --- |
| ctrl+x y | messages.copy | Copy the last assistant message |
| ctrl+x o | copy-message | Pick any message from the session, copy its full text |
| ctrl+x v | copy-message.nvim | Open the whole transcript in your editor |
| ctrl+x p | copy-message.popup | Show the transcript in a scrollable popup |
| ctrl+x m | copy-message.mouse | Toggle TUI mouse capture on/off |
Copying with the editor
copy-message.nvim writes the transcript to
/tmp/opencode-copy-<sessionID>.md, suspends the TUI, and hands the terminal
to your editor. When you quit the editor, opencode resumes exactly where it
left off. Inside the editor, use your normal motions:
yy/Y— yank a lineV— visual line mode, theny"+y— yank to the system clipboard
Copying with the popup
copy-message.popup opens a transcript viewer styled like opencode's own
dialogs: role headers, a scrollbar, and the transcript scrolled to the end.
Drag to select text with the mouse — it copies on release — then press esc
to close.
When mouse capture gets in the way
With mouse capture on, opencode intercepts clicks (clicking a message opens its
dialog). Press ctrl+x m to disable capture, select text with your
terminal's native selection, and press ctrl+x m again to re-enable it.
Keyboard scrolling still works while capture is off (pagedown/pageup,
ctrl+alt+d/u for half-page scroll).
Configuration
Plugins take a tuple form in tui.json: [spec, options].
{
"plugin": [
["opencode-copy-message", {
"editor": "nvim",
"keybinds": {
"copy-message": "<leader>o",
"copy-message.nvim": "<leader>v",
"copy-message.popup": "<leader>p",
"copy-message.mouse": "<leader>m"
}
}]
]
}editor
The executable used by copy-message.nvim. Anything on PATH works:
| Value | Effect |
| --- | --- |
| "nvim" (default) | Modern vim with a system clipboard |
| "vim" | Classic vim |
| "code" | VS Code (the file is opened in a new window) |
| "cat" | Print the transcript and return — handy for piping |
keybinds
A map of command name to key. Keys are case-normalized, so <leader>Y and
<leader>y are the same binding. Set a command to "none" to disable its
keybind — it stays available in the command palette.
| Command | Default |
| --- | --- |
| copy-message | <leader>o |
| copy-message.nvim | <leader>v |
| copy-message.popup | <leader>p |
| copy-message.mouse | <leader>m |
messages.copy (the copy-last command) deliberately keeps opencode's built-in
<leader>y binding; rebind it in tui.json with the messages_copy key if
you want a different key.
Rebind the leader key
The leader is opencode's own setting, not this plugin's. Change it in
tui.json:
{
"keybinds": {
"leader": "ctrl+space"
}
}Development
npm install
# run the unit tests (bun is fetched via npx, no global install needed)
npx -y bun test
# type-check
npx -y -p typescript tsc --noEmit \
--module esnext --moduleResolution bundler --target esnext \
--skipLibCheck --strict --types bun-types \
copy-message.ts copy-message.test.tsLayout:
copy-message.ts— the plugin (pure helpers + thetuiplugin function)copy-message.test.ts— unit tests for the pure helperstui.jsonoptions —editor,keybinds(documented above)
License
MIT
