Skip to content

Commit 6a79e2b

Browse files
authored
Merge pull request #26 from ahdzib-maya/feat/multi-instance
Add instance per git root
2 parents bfac363 + 083682e commit 6a79e2b

7 files changed

Lines changed: 283 additions & 72 deletions

File tree

CLAUDE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ Claude Code Plugin provides seamless integration between the Claude Code AI assi
2626
- Implementing better context synchronization
2727
- Adding buffer-specific context management
2828

29+
## Multi-Instance Support
30+
The plugin supports running multiple Claude Code instances, one per git repository root:
31+
32+
- Each git repository maintains its own Claude instance
33+
- Works across multiple Neovim tabs with different projects
34+
- Allows working on multiple projects in parallel
35+
- Configurable via `git.multi_instance` option (defaults to `true`)
36+
- Instances remain in their own directory context when switching between tabs
37+
- Buffer names include the git root path for easy identification
38+
39+
Example configuration to disable multi-instance mode:
40+
```lua
41+
require('claude-code').setup({
42+
git = {
43+
multi_instance = false -- Use a single global Claude instance
44+
}
45+
})
46+
```
47+
2948
## Documentation Links
3049
- Tasks: `/home/gregg/Projects/docs-projects/neovim-ecosystem-docs/tasks/claude-code-tasks.md`
3150
- Project Status: `/home/gregg/Projects/docs-projects/neovim-ecosystem-docs/project-status.md`

lua/claude-code/config.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ local M = {}
2525
--- ClaudeCodeGit class for git integration configuration
2626
-- @table ClaudeCodeGit
2727
-- @field use_git_root boolean Set CWD to git root when opening Claude Code (if in git project)
28+
-- @field multi_instance boolean Use multiple Claude instances (one per git root)
2829

2930
--- ClaudeCodeKeymapsToggle class for toggle keymap configuration
3031
-- @table ClaudeCodeKeymapsToggle
@@ -78,6 +79,7 @@ M.default_config = {
7879
-- Git integration settings
7980
git = {
8081
use_git_root = true, -- Set CWD to git root when opening Claude Code (if in git project)
82+
multi_instance = true, -- Use multiple Claude instances (one per git root)
8183
},
8284
-- Command settings
8385
command = 'claude', -- Command used to launch Claude Code
@@ -173,6 +175,10 @@ local function validate_config(config)
173175
return false, 'git.use_git_root must be a boolean'
174176
end
175177

178+
if type(config.git.multi_instance) ~= 'boolean' then
179+
return false, 'git.multi_instance must be a boolean'
180+
end
181+
176182
-- Validate command settings
177183
if type(config.command) ~= 'string' then
178184
return false, 'command must be a string'

lua/claude-code/file_refresh.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ function M.setup(claude_code, config)
5757
config.refresh.timer_interval,
5858
vim.schedule_wrap(function()
5959
-- Only check time if there's an active Claude Code terminal
60-
local bufnr = claude_code.claude_code.bufnr
60+
local current_instance = claude_code.claude_code.current_instance
61+
local bufnr = current_instance and claude_code.claude_code.instances[current_instance]
6162
if bufnr and vim.api.nvim_buf_is_valid(bufnr) and #vim.fn.win_findbuf(bufnr) > 0 then
6263
vim.cmd 'silent! checktime'
6364
end

lua/claude-code/init.lua

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,25 @@ function M.force_insert_mode()
4444
terminal.force_insert_mode(M, M.config)
4545
end
4646

47+
--- Get the current active buffer number
48+
--- @return number|nil bufnr Current Claude instance buffer number or nil
49+
local function get_current_buffer_number()
50+
-- Get current instance from the instances table
51+
local current_instance = M.claude_code.current_instance
52+
if current_instance and type(M.claude_code.instances) == 'table' then
53+
return M.claude_code.instances[current_instance]
54+
end
55+
return nil
56+
end
57+
4758
--- Toggle the Claude Code terminal window
4859
--- This is a public function used by commands
4960
function M.toggle()
5061
terminal.toggle(M, M.config, git)
5162

5263
-- Set up terminal navigation keymaps after toggling
53-
if M.claude_code.bufnr and vim.api.nvim_buf_is_valid(M.claude_code.bufnr) then
64+
local bufnr = get_current_buffer_number()
65+
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
5466
keymaps.setup_terminal_navigation(M, M.config)
5567
end
5668
end
@@ -73,7 +85,8 @@ function M.toggle_with_variant(variant_name)
7385
terminal.toggle(M, M.config, git)
7486

7587
-- Set up terminal navigation keymaps after toggling
76-
if M.claude_code.bufnr and vim.api.nvim_buf_is_valid(M.claude_code.bufnr) then
88+
local bufnr = get_current_buffer_number()
89+
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
7790
keymaps.setup_terminal_navigation(M, M.config)
7891
end
7992

lua/claude-code/keymaps.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ end
8989
--- @param claude_code table The main plugin module
9090
--- @param config table The plugin configuration
9191
function M.setup_terminal_navigation(claude_code, config)
92-
local buf = claude_code.claude_code.bufnr
92+
-- Get current active Claude instance buffer
93+
local current_instance = claude_code.claude_code.current_instance
94+
local buf = current_instance and claude_code.claude_code.instances[current_instance]
9395
if buf and vim.api.nvim_buf_is_valid(buf) then
9496
-- Create autocommand to enter insert mode when the terminal window gets focus
95-
local augroup = vim.api.nvim_create_augroup('ClaudeCodeTerminalFocus', { clear = true })
97+
local augroup = vim.api.nvim_create_augroup('ClaudeCodeTerminalFocus_' .. buf, { clear = true })
9698

9799
-- Set up multiple events for more reliable focus detection
98100
vim.api.nvim_create_autocmd(

lua/claude-code/terminal.lua

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,28 @@ local M = {}
88

99
--- Terminal buffer and window management
1010
-- @table ClaudeCodeTerminal
11-
-- @field bufnr number|nil Buffer number of the Claude Code terminal
11+
-- @field instances table Key-value store of git root to buffer number
1212
-- @field saved_updatetime number|nil Original updatetime before Claude Code was opened
13+
-- @field current_instance string|nil Current git root path for active instance
1314
M.terminal = {
14-
bufnr = nil,
15+
instances = {},
1516
saved_updatetime = nil,
17+
current_instance = nil,
1618
}
1719

20+
--- Get the current git root or a fallback identifier
21+
--- @param git table The git module
22+
--- @return string identifier Git root path or fallback identifier
23+
local function get_instance_identifier(git)
24+
local git_root = git.get_git_root()
25+
if git_root then
26+
return git_root
27+
else
28+
-- Fallback to current working directory if not in a git repo
29+
return vim.fn.getcwd()
30+
end
31+
end
32+
1833
--- Create a split window according to the specified position configuration
1934
--- @param position string Window position configuration
2035
--- @param config table Plugin configuration containing window settings
@@ -49,8 +64,21 @@ end
4964
--- @param claude_code table The main plugin module
5065
--- @param config table The plugin configuration
5166
function M.force_insert_mode(claude_code, config)
52-
local bufnr = claude_code.claude_code.bufnr
53-
if bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.fn.bufnr '%' == bufnr then
67+
local current_bufnr = vim.fn.bufnr('%')
68+
69+
-- Check if current buffer is any of our Claude instances
70+
local is_claude_instance = false
71+
for _, bufnr in pairs(claude_code.claude_code.instances) do
72+
if bufnr
73+
and bufnr == current_bufnr
74+
and vim.api.nvim_buf_is_valid(bufnr)
75+
then
76+
is_claude_instance = true
77+
break
78+
end
79+
end
80+
81+
if is_claude_instance then
5482
-- Only enter insert mode if we're in the terminal buffer and not already in insert mode
5583
-- and not configured to stay in normal mode
5684
if config.window.start_in_normal_mode then
@@ -72,10 +100,25 @@ end
72100
--- @param config table The plugin configuration
73101
--- @param git table The git module
74102
function M.toggle(claude_code, config, git)
75-
-- Check if Claude Code is already running
76-
local bufnr = claude_code.claude_code.bufnr
103+
-- Determine instance ID based on config
104+
local instance_id
105+
if config.git.multi_instance then
106+
if config.git.use_git_root then
107+
instance_id = get_instance_identifier(git)
108+
else
109+
instance_id = vim.fn.getcwd()
110+
end
111+
else
112+
-- Use a fixed ID for single instance mode
113+
instance_id = "global"
114+
end
115+
116+
claude_code.claude_code.current_instance = instance_id
117+
118+
-- Check if this Claude Code instance is already running
119+
local bufnr = claude_code.claude_code.instances[instance_id]
77120
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
78-
-- Check if there's a window displaying Claude Code buffer
121+
-- Check if there's a window displaying this Claude Code buffer
79122
local win_ids = vim.fn.win_findbuf(bufnr)
80123
if #win_ids > 0 then
81124
-- Claude Code is visible, close the window
@@ -93,7 +136,11 @@ function M.toggle(claude_code, config, git)
93136
end
94137
end
95138
else
96-
-- Claude Code is not running, start it in a new split
139+
-- Prune invalid buffer entries
140+
if bufnr and not vim.api.nvim_buf_is_valid(bufnr) then
141+
claude_code.claude_code.instances[instance_id] = nil
142+
end
143+
-- This Claude Code instance is not running, start it in a new split
97144
create_split(config.window.position, config)
98145

99146
-- Determine if we should use the git root directory
@@ -108,7 +155,15 @@ function M.toggle(claude_code, config, git)
108155

109156
vim.cmd(cmd)
110157
vim.cmd 'setlocal bufhidden=hide'
111-
vim.cmd 'file claude-code'
158+
159+
-- Create a unique buffer name (or a standard one in single instance mode)
160+
local buffer_name
161+
if config.git.multi_instance then
162+
buffer_name = 'claude-code-' .. instance_id:gsub('[^%w%-_]', '-')
163+
else
164+
buffer_name = 'claude-code'
165+
end
166+
vim.cmd('file ' .. buffer_name)
112167

113168
if config.window.hide_numbers then
114169
vim.cmd 'setlocal nonumber norelativenumber'
@@ -118,8 +173,8 @@ function M.toggle(claude_code, config, git)
118173
vim.cmd 'setlocal signcolumn=no'
119174
end
120175

121-
-- Store buffer number for future reference
122-
claude_code.claude_code.bufnr = vim.fn.bufnr '%'
176+
-- Store buffer number for this instance
177+
claude_code.claude_code.instances[instance_id] = vim.fn.bufnr('%')
123178

124179
-- Automatically enter insert mode in terminal unless configured to start in normal mode
125180
if config.window.enter_insert and not config.window.start_in_normal_mode then

0 commit comments

Comments
 (0)