Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ Claude Code Plugin provides seamless integration between the Claude Code AI assi
- Implementing better context synchronization
- Adding buffer-specific context management

## Multi-Instance Support
The plugin supports running multiple Claude Code instances, one per git repository root:

- Each git repository maintains its own Claude instance
- Works across multiple Neovim tabs with different projects
- Allows working on multiple projects in parallel
- Configurable via `git.multi_instance` option (defaults to `true`)
- Instances remain in their own directory context when switching between tabs
- Buffer names include the git root path for easy identification

Example configuration to disable multi-instance mode:
```lua
require('claude-code').setup({
git = {
multi_instance = false -- Use a single global Claude instance
}
})
```

## Documentation Links
- Tasks: `/home/gregg/Projects/docs-projects/neovim-ecosystem-docs/tasks/claude-code-tasks.md`
- Project Status: `/home/gregg/Projects/docs-projects/neovim-ecosystem-docs/project-status.md`
6 changes: 6 additions & 0 deletions lua/claude-code/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local M = {}
--- ClaudeCodeGit class for git integration configuration
-- @table ClaudeCodeGit
-- @field use_git_root boolean Set CWD to git root when opening Claude Code (if in git project)
-- @field multi_instance boolean Use multiple Claude instances (one per git root)

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

if type(config.git.multi_instance) ~= 'boolean' then
return false, 'git.multi_instance must be a boolean'
end

-- Validate command settings
if type(config.command) ~= 'string' then
return false, 'command must be a string'
Expand Down
3 changes: 2 additions & 1 deletion lua/claude-code/file_refresh.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ function M.setup(claude_code, config)
config.refresh.timer_interval,
vim.schedule_wrap(function()
-- Only check time if there's an active Claude Code terminal
local bufnr = claude_code.claude_code.bufnr
local current_instance = claude_code.claude_code.current_instance
local bufnr = current_instance and claude_code.claude_code.instances[current_instance]
if bufnr and vim.api.nvim_buf_is_valid(bufnr) and #vim.fn.win_findbuf(bufnr) > 0 then
vim.cmd 'silent! checktime'
end
Expand Down
17 changes: 15 additions & 2 deletions lua/claude-code/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,25 @@ function M.force_insert_mode()
terminal.force_insert_mode(M, M.config)
end

--- Get the current active buffer number
--- @return number|nil bufnr Current Claude instance buffer number or nil
local function get_current_buffer_number()
-- Get current instance from the instances table
local current_instance = M.claude_code.current_instance
if current_instance and type(M.claude_code.instances) == 'table' then
return M.claude_code.instances[current_instance]
end
return nil
end

--- Toggle the Claude Code terminal window
--- This is a public function used by commands
function M.toggle()
terminal.toggle(M, M.config, git)

-- Set up terminal navigation keymaps after toggling
if M.claude_code.bufnr and vim.api.nvim_buf_is_valid(M.claude_code.bufnr) then
local bufnr = get_current_buffer_number()
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
keymaps.setup_terminal_navigation(M, M.config)
end
end
Expand All @@ -73,7 +85,8 @@ function M.toggle_with_variant(variant_name)
terminal.toggle(M, M.config, git)

-- Set up terminal navigation keymaps after toggling
if M.claude_code.bufnr and vim.api.nvim_buf_is_valid(M.claude_code.bufnr) then
local bufnr = get_current_buffer_number()
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
keymaps.setup_terminal_navigation(M, M.config)
end

Expand Down
6 changes: 4 additions & 2 deletions lua/claude-code/keymaps.lua
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ end
--- @param claude_code table The main plugin module
--- @param config table The plugin configuration
function M.setup_terminal_navigation(claude_code, config)
local buf = claude_code.claude_code.bufnr
-- Get current active Claude instance buffer
local current_instance = claude_code.claude_code.current_instance
local buf = current_instance and claude_code.claude_code.instances[current_instance]
if buf and vim.api.nvim_buf_is_valid(buf) then
-- Create autocommand to enter insert mode when the terminal window gets focus
local augroup = vim.api.nvim_create_augroup('ClaudeCodeTerminalFocus', { clear = true })
local augroup = vim.api.nvim_create_augroup('ClaudeCodeTerminalFocus_' .. buf, { clear = true })

-- Set up multiple events for more reliable focus detection
vim.api.nvim_create_autocmd(
Expand Down
77 changes: 66 additions & 11 deletions lua/claude-code/terminal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,28 @@ local M = {}

--- Terminal buffer and window management
-- @table ClaudeCodeTerminal
-- @field bufnr number|nil Buffer number of the Claude Code terminal
-- @field instances table Key-value store of git root to buffer number
-- @field saved_updatetime number|nil Original updatetime before Claude Code was opened
-- @field current_instance string|nil Current git root path for active instance
M.terminal = {
bufnr = nil,
instances = {},
saved_updatetime = nil,
current_instance = nil,
}

--- Get the current git root or a fallback identifier
--- @param git table The git module
--- @return string identifier Git root path or fallback identifier
local function get_instance_identifier(git)
local git_root = git.get_git_root()
if git_root then
return git_root
else
-- Fallback to current working directory if not in a git repo
return vim.fn.getcwd()
end
end

--- Create a split window according to the specified position configuration
--- @param position string Window position configuration
--- @param config table Plugin configuration containing window settings
Expand Down Expand Up @@ -49,8 +64,21 @@ end
--- @param claude_code table The main plugin module
--- @param config table The plugin configuration
function M.force_insert_mode(claude_code, config)
local bufnr = claude_code.claude_code.bufnr
if bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.fn.bufnr '%' == bufnr then
local current_bufnr = vim.fn.bufnr('%')

-- Check if current buffer is any of our Claude instances
local is_claude_instance = false
for _, bufnr in pairs(claude_code.claude_code.instances) do
if bufnr
and bufnr == current_bufnr
and vim.api.nvim_buf_is_valid(bufnr)
then
is_claude_instance = true
break
end
end

if is_claude_instance then
-- Only enter insert mode if we're in the terminal buffer and not already in insert mode
-- and not configured to stay in normal mode
if config.window.start_in_normal_mode then
Expand All @@ -72,10 +100,25 @@ end
--- @param config table The plugin configuration
--- @param git table The git module
function M.toggle(claude_code, config, git)
-- Check if Claude Code is already running
local bufnr = claude_code.claude_code.bufnr
-- Determine instance ID based on config
local instance_id
if config.git.multi_instance then
if config.git.use_git_root then
instance_id = get_instance_identifier(git)
else
instance_id = vim.fn.getcwd()
end
else
-- Use a fixed ID for single instance mode
instance_id = "global"
end

claude_code.claude_code.current_instance = instance_id

-- Check if this Claude Code instance is already running
local bufnr = claude_code.claude_code.instances[instance_id]
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
-- Check if there's a window displaying Claude Code buffer
-- Check if there's a window displaying this Claude Code buffer
local win_ids = vim.fn.win_findbuf(bufnr)
if #win_ids > 0 then
-- Claude Code is visible, close the window
Expand All @@ -93,7 +136,11 @@ function M.toggle(claude_code, config, git)
end
end
else
-- Claude Code is not running, start it in a new split
-- Prune invalid buffer entries
if bufnr and not vim.api.nvim_buf_is_valid(bufnr) then
claude_code.claude_code.instances[instance_id] = nil
end
-- This Claude Code instance is not running, start it in a new split
create_split(config.window.position, config)

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

vim.cmd(cmd)
vim.cmd 'setlocal bufhidden=hide'
vim.cmd 'file claude-code'

-- Create a unique buffer name (or a standard one in single instance mode)
local buffer_name
if config.git.multi_instance then
buffer_name = 'claude-code-' .. instance_id:gsub('[^%w%-_]', '-')
else
buffer_name = 'claude-code'
end
vim.cmd('file ' .. buffer_name)

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

-- Store buffer number for future reference
claude_code.claude_code.bufnr = vim.fn.bufnr '%'
-- Store buffer number for this instance
claude_code.claude_code.instances[instance_id] = vim.fn.bufnr('%')

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