Skip to content
77 changes: 61 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# opencode-context.nvim

A Neovim plugin that enables seamless context sharing with running opencode sessions inside Tmux. Send your current buffer, all open buffers, visual selections, or diagnostics directly to opencode running in a Tmux pane for AI-assisted development.
A Neovim plugin that enables seamless context sharing with running opencode sessions inside tmux or Zellij. Send your current buffer, all open buffers, visual selections, or diagnostics directly to opencode running in a pane for AI-assisted development.

## Features

Expand All @@ -11,14 +11,14 @@ A Neovim plugin that enables seamless context sharing with running opencode sess
- 📍 `@cursor`, `@here` - Insert cursor position info
- ✂️ `@selection`, `@range` - Insert visual selection
- 🔍 `@diagnostics` - Insert LSP diagnostics
- 🖥️ **Tmux integration** - Send directly to opencode pane in current window
- 🖥️ **Tmux + Zellij integration** - Send directly to opencode pane in current window/tab
- ⚡ LazyVim compatible with lazy loading

## Requirements

- Neovim >= 0.8.0
- `tmux` - Required for sending messages to running opencode sessions
- `opencode` running in a pane within the same tmux window as Neovim
- `tmux` or `zellij` - Required for sending messages to running opencode sessions
- `opencode` running in a pane within the same tmux window or zellij tab as Neovim

## Installation

Expand All @@ -28,12 +28,22 @@ A Neovim plugin that enables seamless context sharing with running opencode sess
{
"cousine/opencode-context.nvim",
opts = {
multiplexer = "auto", -- "auto", "tmux", or "zellij"
tmux_target = nil, -- Manual override: "session:window.pane"
zellij_target = nil, -- Manual override: "terminal_3"
auto_detect_pane = true, -- Auto-detect opencode pane in current window
},
keys = {
{ "<leader>oc", "<cmd>OpencodeSend<cr>", desc = "Send prompt to opencode" },
{ "<leader>oc", "<cmd>OpencodeSend<cr>", mode = "v", desc = "Send prompt to opencode" },
{
"<leader>oc",
function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "x", false)
require("opencode-context").send_prompt(true)
end,
mode = "v",
desc = "Send prompt to opencode",
},
{ "<leader>ot", "<cmd>OpencodeSwitchMode<cr>", desc = "Toggle opencode mode" },
{ "<leader>op", "<cmd>OpencodePrompt<cr>", desc = "Open opencode persistent prompt" },
},
Expand All @@ -48,7 +58,9 @@ use {
'cousine/opencode-context.nvim',
config = function()
require('opencode-context').setup({
multiplexer = "auto",
tmux_target = nil,
zellij_target = nil,
auto_detect_pane = true,
})
end
Expand All @@ -63,13 +75,18 @@ Plug 'cousine/opencode-context.nvim'
" Configuration in init.vim or init.lua
lua << EOF
require('opencode-context').setup({
multiplexer = "auto",
tmux_target = nil,
zellij_target = nil,
auto_detect_pane = true,
})

-- Keymaps
vim.keymap.set("n", "<leader>oc", "<cmd>OpencodeSend<cr>", { desc = "Send prompt to opencode" })
vim.keymap.set("v", "<leader>oc", "<cmd>OpencodeSend<cr>", { desc = "Send prompt to opencode" })
vim.keymap.set("v", "<leader>oc", function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "x", false)
require("opencode-context").send_prompt(true)
end, { desc = "Send prompt to opencode" })
vim.keymap.set("n", "<leader>ot", "<cmd>OpencodeSwitchMode<cr>", { desc = "Toggle opencode mode" })
vim.keymap.set("n", "<leader>op", "<cmd>OpencodePrompt<cr>", { desc = "Open opencode persistent prompt" })
EOF
Expand All @@ -83,7 +100,9 @@ call dein#add('cousine/opencode-context.nvim')
" Configuration
lua << EOF
require('opencode-context').setup({
multiplexer = "auto",
tmux_target = nil,
zellij_target = nil,
auto_detect_pane = true,
})
EOF
Expand All @@ -101,9 +120,11 @@ EOF

```lua
require("opencode-context").setup({
tmux_target = nil,
auto_detect_pane = true,
})
multiplexer = "auto",
tmux_target = nil,
zellij_target = nil,
auto_detect_pane = true,
})
```

## Usage
Expand Down Expand Up @@ -143,7 +164,7 @@ Use these placeholders in your prompts to include context:

### Workflow Example

1. In tmux, split your window: `Ctrl-b %` or `Ctrl-b "`
1. In your multiplexer, split your layout (`tmux`: `Ctrl-b %` / `Ctrl-b "`, `zellij`: `Alt n` by default)
2. Start opencode in one pane: `opencode`
3. Open Neovim in the other pane: `nvim`
4. From Neovim, press `<leader>oc` to open the prompt input
Expand All @@ -154,40 +175,54 @@ Use these placeholders in your prompts to include context:

```lua
require("opencode-context").setup({
-- Multiplexer settings
multiplexer = "auto", -- "auto", "tmux", or "zellij"

-- Tmux settings
tmux_target = nil, -- Manual override: "main:1.0"
auto_detect_pane = true, -- Auto-find opencode pane in current window (default: true)
auto_detect_pane = true, -- Auto-find opencode pane in current tmux window/zellij tab (default: true)

-- Zellij settings
zellij_target = nil, -- Manual override: "terminal_3"
})
```

## How It Works

The plugin integrates directly with tmux to send messages to your running opencode session:
The plugin integrates directly with tmux or zellij to send messages to your running opencode session:

1. **Auto-detects** running opencode pane in the current tmux window
2. **Sends keystrokes directly** to the opencode pane using `tmux send-keys`
1. **Auto-detects** running opencode pane in the current tmux window or zellij tab
2. **Sends keystrokes directly** to the opencode pane using `tmux send-keys` or `zellij action`
3. **You see the conversation** in real-time in your opencode interface
4. **No new processes** - uses your existing opencode session

### Detection Strategy

The plugin searches for opencode panes **only in the current tmux window** using:
For tmux, the plugin searches for opencode panes **only in the current tmux window** using:

- Current command is `opencode`
- Pane title contains "opencode"
- Recent command history contains opencode

For zellij, the plugin searches terminal panes in the **current active tab** and matches panes by command/title containing `opencode`.

This ensures it finds the opencode instance you're actively working with, not some other session.

## Troubleshooting

### "No opencode pane found in current window"
### "No opencode pane found in current tmux window"

- **Same window**: Ensure opencode is running in a pane in the **same tmux window** as Neovim
- **Split window**: Use `Ctrl-b %` or `Ctrl-b "` to split and run opencode in one pane
- **Manual target**: Set `tmux_target = "session:window.pane"` in config to override detection
- **Verify opencode is running**: Check that opencode is actually running in the current window

### "No opencode pane found in current zellij tab"

- **Same tab**: Ensure opencode is running in a pane in the **same zellij tab** as Neovim
- **Manual target**: Set `zellij_target = "terminal_3"` in config to override detection
- **Verify opencode is running**: Check pane command/title includes opencode

### "Failed to send to opencode pane"

- **Permissions**: Check tmux pane permissions
Expand All @@ -205,6 +240,16 @@ tmux send-keys -t session:window.pane "test message" Enter

# List all panes in current window
tmux list-panes -F '#{session_name}:#{window_index}.#{pane_index} #{pane_current_command}'

# Check zellij panes in current session
zellij action list-panes --json

# Check current zellij tab info
zellij action current-tab-info --json

# Test manual zellij send
zellij action write-chars --pane-id terminal_3 "test message"
zellij action send-keys --pane-id terminal_3 Enter
```

## Contributing
Expand Down
18 changes: 16 additions & 2 deletions lazy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,27 @@ return {
"opencode-context.nvim",
dev = true,
opts = {
-- Multiplexer settings
multiplexer = "auto", -- "auto", "tmux", or "zellij"

-- Tmux settings
tmux_target = nil, -- Manual override: "session:window.pane"
auto_detect_pane = true, -- Auto-detect opencode pane in current window

-- Zellij settings
zellij_target = nil, -- Manual override: "terminal_3"
},
keys = {
{ "<leader>oc", "<cmd>OpencodeSend<cr>", desc = "Send prompt to opencode" },
{ "<leader>oc", "<cmd>OpencodeSend<cr>", mode = "v", desc = "Send prompt to opencode" },
{
"<leader>oc",
function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "x", false)
require("opencode-context").send_prompt(true)
end,
mode = "v",
desc = "Send prompt to opencode",
},
{ "<leader>om", "<cmd>OpencodeSwitchMode<cr>", desc = "Toggle opencode mode" },
},
cmd = {
Expand All @@ -18,4 +32,4 @@ return {
config = function(_, opts)
require("opencode-context").setup(opts)
end,
}
}
Loading