Skip to content
Draft
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: 15 additions & 4 deletions lua/claudecode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -390,12 +390,22 @@ end

---Start the Claude Code integration
---@param show_startup_notification? boolean Whether to show a notification upon successful startup (defaults to true)
---@param force_new_terminal? boolean If true, creates a new terminal instance even if server is running
---@return boolean success Whether the operation was successful
---@return number|string port_or_error The WebSocket port if successful, or error message if failed
function M.start(show_startup_notification)
function M.start(show_startup_notification, force_new_terminal)
if show_startup_notification == nil then
show_startup_notification = true
end

-- If server is already running and we're forcing a new terminal, just create it
if M.state.server and force_new_terminal then
logger.info("init", "Creating new Claude Code terminal on existing port " .. tostring(M.state.port))
local terminal = require("claudecode.terminal")
terminal.create_new_instance()
return true, M.state.port
end

if M.state.server then
local msg = "Claude Code integration is already running on port " .. tostring(M.state.port)
logger.warn("init", msg)
Expand Down Expand Up @@ -525,10 +535,11 @@ end
---Set up user commands
---@private
function M._create_commands()
vim.api.nvim_create_user_command("ClaudeCodeStart", function()
M.start()
vim.api.nvim_create_user_command("ClaudeCodeStart", function(opts)
M.start(nil, opts.bang)
end, {
desc = "Start Claude Code integration",
bang = true,
desc = "Start Claude Code integration (use ! to create multiple instances)",
})

vim.api.nvim_create_user_command("ClaudeCodeStop", function()
Expand Down
17 changes: 17 additions & 0 deletions lua/claudecode/terminal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -569,4 +569,21 @@ function M._get_managed_terminal_for_test()
return nil
end

---Creates a new Claude Code terminal instance.
---This always creates a new terminal, regardless of existing ones.
---@param opts_override table? Overrides for terminal appearance (split_side, split_width_percentage).
---@param cmd_args string? Arguments to append to the claude command.
function M.create_new_instance(opts_override, cmd_args)
local effective_config = build_config(opts_override)
local cmd_string, claude_env_table = get_claude_command_and_env(cmd_args)

local provider = get_provider()
if provider.create_new_instance then
provider.create_new_instance(cmd_string, claude_env_table, effective_config)
else
-- Fallback: just call open, which may reuse existing terminal
provider.open(cmd_string, claude_env_table, effective_config)
end
end

return M
28 changes: 28 additions & 0 deletions lua/claudecode/terminal/native.lua
Original file line number Diff line number Diff line change
Expand Up @@ -435,5 +435,33 @@ function M.is_available()
return true -- Native provider is always available
end

---Creates a new terminal instance, ignoring any existing terminals
---@param cmd_string string
---@param env_table table
---@param effective_config table
function M.create_new_instance(cmd_string, env_table, effective_config)
-- Temporarily save the current state
local old_bufnr = bufnr
local old_winid = winid
local old_jobid = jobid

-- Clear state to force creation of new terminal
bufnr = nil
winid = nil
jobid = nil

-- Create new terminal
local success = open_terminal(cmd_string, env_table, effective_config, true)

-- If creation failed, restore old state
if not success then
bufnr = old_bufnr
winid = old_winid
jobid = old_jobid
end

return success
end

--- @type ClaudeCodeTerminalProvider
return M
31 changes: 31 additions & 0 deletions lua/claudecode/terminal/snacks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -272,5 +272,36 @@ function M._get_terminal_for_test()
return terminal
end

---Creates a new terminal instance, ignoring any existing terminals
---@param cmd_string string
---@param env_table table
---@param config table
function M.create_new_instance(cmd_string, env_table, config)
if not is_available() then
vim.notify("Snacks.nvim terminal provider selected but Snacks.terminal not available.", vim.log.levels.ERROR)
return
end

-- Save existing terminal
local old_terminal = terminal

-- Clear terminal state to force creation
terminal = nil

-- Create new terminal (M.open will create a new one since terminal is nil)
local opts = build_opts(config, env_table, true)
local term_instance = Snacks.terminal.open(cmd_string, opts)

if term_instance and term_instance:buf_valid() then
setup_terminal_events(term_instance, config)
-- Don't set terminal = term_instance, keep it nil or use a separate tracking if needed
-- This allows the new terminal to exist independently
else
terminal = old_terminal -- Restore if creation failed
local logger = require("claudecode.logger")
logger.error("terminal", "Failed to create new terminal instance")
end
end

---@type ClaudeCodeTerminalProvider
return M