Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8f8373b
feat: add multi-session terminal support
Dec 17, 2025
9dee2c9
fix: cursor position when switching terminal sessions
Dec 18, 2025
c0a7705
feat: add tabbar UI with mouse support for terminal sessions
Dec 24, 2025
ccde261
fix: keep terminal window open when session exits with other sessions…
Dec 28, 2025
9c74f95
fix: preserve terminal window size across session operations
Jan 4, 2026
e28a7d9
docs: add fork features section and recommended configuration
Jan 15, 2026
837dbb8
fix: prevent orphan Claude processes on Neovim exit
snirt Jan 20, 2026
d0772d7
fix: update file reference on keyboard window navigation
snirt Jan 21, 2026
f594ffe
fix: restore terminal split width after tab switch
snirt Feb 17, 2026
5e40e58
fix: reduce noise from expected disconnection errors
snirt Feb 17, 2026
ecc3840
feat: add on_disconnect_cleanup callback to TCP server
snirt Mar 15, 2026
2ba2e8a
fix: exclude snacks_picker_list from main editor window detection (#165)
tuannvm Jan 3, 2026
4770cf6
fix: ide diagnostics without URI schema (#163)
alvarosevilla95 Jan 3, 2026
5867bcd
fix(diff): keep terminal focus for floating terminals (#178)
ThomasK33 Jan 26, 2026
cfc9d20
fix(server): sync disconnect callbacks (#176)
snirt Mar 15, 2026
2813084
fix(diff): close plugin-created split on cleanup (#175)
snirt Mar 15, 2026
2379fc1
test(terminal): add failing tests for 3-state ESC handler
snirt Mar 18, 2026
82b0823
test(terminal): add failing tests for 3-state ESC machine
snirt Mar 18, 2026
0bd5396
test(terminal): strengthen 3-state ESC machine test assertions
snirt Mar 18, 2026
14b7ba6
feat(terminal): triple-ESC to exit terminal mode, pass 1x/2x to Claud…
snirt Mar 18, 2026
7445a19
fix(terminal): restart timer on 2nd ESC to enable 2x ESC rewind path
snirt Mar 18, 2026
4c4858f
fix(terminal): allocate fresh timer in count=1 branch if first timer …
snirt Mar 18, 2026
60dd70e
docs(terminal): update comments to reflect triple-ESC behavior
snirt Mar 18, 2026
7466e64
docs: document triple-ESC behavior and migration note for double-ESC …
snirt Mar 18, 2026
54b9ea3
docs: fix stale double-tap comment in README config example
snirt Mar 18, 2026
2013286
refactor(terminal): clean up stale comments and dead nil-timer guard
snirt Mar 18, 2026
ca9e891
fix(terminal): defer jobresize until user enters terminal window
snirt Mar 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,36 @@ vim.api.nvim_create_autocmd("CursorMoved", {
-- Preserves selection context when switching to terminal
```

### 6. Terminal Integration (`terminal.lua`)
### 6. Terminal Integration (`terminal/`)

Flexible terminal management with provider pattern:
Flexible terminal management with provider pattern and centralized window management:

```lua
-- Snacks.nvim provider (preferred)
if has_snacks then
Snacks.terminal.open(cmd, {
win = { position = "right", width = 0.3 }
})
else
-- Native fallback
vim.cmd("vsplit | terminal " .. cmd)
end
-- Window manager singleton owns THE terminal window
-- Providers only create buffers, window_manager displays them
local window_manager = require("claudecode.terminal.window_manager")

-- Display a buffer in the managed window (preserves user resizing)
window_manager.display_buffer(bufnr, focus)

-- Snacks.nvim provider creates buffer, delegates window to manager
local term = Snacks.terminal.open(cmd, opts)
vim.api.nvim_win_close(term.win, false) -- Close snacks' window
window_manager.display_buffer(term.buf, true) -- Use our window

-- Native provider creates buffer without window
local bufnr = vim.api.nvim_create_buf(false, true)
vim.fn.termopen(cmd, { env = env })
window_manager.display_buffer(bufnr, true)
```

Key features:

- **Single window**: All sessions share one terminal window
- **Buffer switching**: `nvim_win_set_buf()` preserves window size
- **Session management**: Multiple Claude sessions with tab-like switching
- **Window preservation**: User resizing persists across session switches

## Key Implementation Patterns

### Thread Safety
Expand Down Expand Up @@ -199,7 +213,15 @@ lua/claudecode/
├── tools/init.lua # MCP tool registry
├── diff.lua # Native diff support
├── selection.lua # Selection tracking
├── terminal.lua # Terminal management
├── session.lua # Multi-session state management
├── terminal.lua # Terminal orchestration
├── terminal/ # Terminal providers
│ ├── window_manager.lua # Singleton window management
│ ├── snacks.lua # Snacks.nvim provider
│ ├── native.lua # Native Neovim terminal
│ ├── external.lua # External terminal apps
│ ├── tabbar.lua # Session tab bar UI
│ └── osc_handler.lua # Terminal title detection
└── lockfile.lua # Discovery files
```

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

### Features

- Multi-session terminal support with `:ClaudeCodeNew`, `:ClaudeCodeSessions`, `:ClaudeCodeSwitch`, `:ClaudeCodeCloseSession` commands
- Tab bar UI with mouse support for session management (`terminal.tabs.*` configuration)
- Smart ESC handling - double-tap ESC to exit terminal mode (`esc_timeout`, `keymaps.exit_terminal`)
- External terminal provider to run Claude in a separate terminal ([#102](https://github.com/coder/claudecode.nvim/pull/102))
- Terminal provider APIs: implement `ensure_visible` for reliability ([#103](https://github.com/coder/claudecode.nvim/pull/103))
- Working directory control for Claude terminal ([#117](https://github.com/coder/claudecode.nvim/pull/117))
Expand Down Expand Up @@ -32,6 +35,10 @@

### Bug Fixes

- Preserve terminal window size across session operations
- Keep terminal window open when session exits with other sessions available
- Fix cursor position when switching terminal sessions
- Send selection updates on BufEnter event
- Wrap ERROR/WARN logging in `vim.schedule` to avoid fast-event context errors ([#54](https://github.com/coder/claudecode.nvim/pull/54))
- Native terminal: do not wipe Claude buffer on window close ([#60](https://github.com/coder/claudecode.nvim/pull/60))
- Native terminal: respect `auto_close` behavior ([#63](https://github.com/coder/claudecode.nvim/pull/63))
Expand Down
17 changes: 11 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,22 +288,27 @@ require("claudecode").setup({

The `diff_opts` configuration allows you to customize diff behavior:

- `layout` ("vertical"|"horizontal", default: `"vertical"`) - Whether the diff panes open in a vertical or horizontal split.
- `keep_terminal_focus` (boolean, default: `false`) - When enabled, keeps focus in the Claude Code terminal when a diff opens instead of moving focus to the diff buffer. This allows you to continue using terminal keybindings like `<CR>` for accepting/rejecting diffs without accidentally triggering other mappings.
- `open_in_new_tab` (boolean, default: `false`) - Open diffs in a new tab instead of the current tab.
- `hide_terminal_in_new_tab` (boolean, default: `false`) - When opening diffs in a new tab, do not show the Claude terminal split in that new tab. The terminal remains in the original tab, giving maximum screen estate for reviewing the diff.
- `on_new_file_reject` ("keep_empty"|"close_window", default: `"keep_empty"`) - Behavior when rejecting a diff for a new file (where the old file did not exist).
- Legacy aliases (still supported): `vertical_split` (maps to `layout`) and `open_in_current_tab` (inverse of `open_in_new_tab`).

**Example use case**: If you frequently use `<CR>` or arrow keys in the Claude Code terminal to accept/reject diffs, enable this option to prevent focus from moving to the diff buffer where `<CR>` might trigger unintended actions.

```lua
require("claudecode").setup({
diff_opts = {
keep_terminal_focus = true, -- If true, moves focus back to terminal after diff opens
open_in_new_tab = true, -- Open diff in a separate tab
layout = "vertical", -- "vertical" or "horizontal"
keep_terminal_focus = true, -- If true, moves focus back to terminal after diff opens
open_in_new_tab = true, -- Open diff in a separate tab
hide_terminal_in_new_tab = true, -- In the new tab, do not show Claude terminal
auto_close_on_accept = true,
show_diff_stats = true,
vertical_split = true,
open_in_current_tab = true,
on_new_file_reject = "keep_empty", -- "keep_empty" or "close_window"

-- Legacy aliases (still supported):
-- vertical_split = true,
-- open_in_current_tab = true,
},
})
```
Expand Down
3 changes: 3 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ vv netrw # Start Neovim with built-in netrw configuration

# List available configurations
list-configs

# Minimal repro environment (copies fixtures/repro/example into /tmp)
repro
```

**Example fixture structure** (`fixtures/my-integration/`):
Expand Down
Loading