Skip to content
Open
29 changes: 29 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,33 @@ vim.api.nvim_create_autocmd("VimLeavePre", {
})
```

### 7. File Explorer Integrations (`integrations.lua`)

Unified interface for popular file explorers:

```lua
-- Supports nvim-tree, neo-tree, oil.nvim, and snacks.explorer
function M.get_selected_files_from_tree()
local current_ft = vim.bo.filetype

if current_ft == "NvimTree" then
return M._get_nvim_tree_selection()
elseif current_ft == "neo-tree" then
return M._get_neotree_selection()
elseif current_ft == "oil" then
return M._get_oil_selection()
elseif current_ft == "snacks_picker_list" then
return M._get_snacks_explorer_selection()
end
end
```

Key features across all integrations:

- **Visual mode support**: Select multiple files using vim visual mode
- **Directory handling**: Adds trailing slashes to directories for consistency
- **Fallback behavior**: Selected items → current item → error

## Module Structure

```
Expand All @@ -197,6 +224,8 @@ lua/claudecode/
│ ├── client.lua # Connection management
│ └── utils.lua # Pure Lua SHA-1, base64
├── tools/init.lua # MCP tool registry
├── integrations.lua # File explorer integrations
├── visual_commands.lua # Visual mode handling
├── diff.lua # Native diff support
├── selection.lua # Selection tracking
├── terminal.lua # Terminal management
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ When Anthropic released Claude Code, they only supported VS Code and JetBrains.
"<leader>as",
"<cmd>ClaudeCodeTreeAdd<cr>",
desc = "Add file",
ft = { "NvimTree", "neo-tree", "oil", "minifiles" },
ft = { "NvimTree", "neo-tree", "oil", "minifiles", "snacks_picker_list" },
},
-- Diff management
{ "<leader>aa", "<cmd>ClaudeCodeDiffAccept<cr>", desc = "Accept diff" },
Expand Down Expand Up @@ -182,7 +182,7 @@ Configure the plugin with the detected path:
1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal
2. **Send context**:
- Select text in visual mode and use `<leader>as` to send it to Claude
- In `nvim-tree`/`neo-tree`/`oil.nvim`/`mini.nvim`, press `<leader>as` on a file to add it to Claude's context
- In `nvim-tree`/`neo-tree`/`oil.nvim`/`mini.nvim`/`snacks.explorer`, press `<leader>as` on a file to add it to Claude's context
3. **Let Claude work**: Claude can now:
- See your current file and selections in real-time
- Open files in your editor
Expand Down
2 changes: 1 addition & 1 deletion dev-config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ return {
"<leader>as",
"<cmd>ClaudeCodeTreeAdd<cr>",
desc = "Add file from tree",
ft = { "NvimTree", "neo-tree", "oil", "minifiles" },
ft = { "NvimTree", "neo-tree", "oil", "minifiles", "snacks_picker_list" },
},

-- Development helpers
Expand Down
1 change: 1 addition & 0 deletions fixtures/snacks-explorer/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("config.lazy")
6 changes: 6 additions & 0 deletions fixtures/snacks-explorer/lazy-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" },
"mini.icons": { "branch": "main", "commit": "b8f6fa6f5a3fd0c56936252edcd691184e5aac0c" },
"snacks.nvim": { "branch": "main", "commit": "bc0630e43be5699bb94dadc302c0d21615421d93" },
"which-key.nvim": { "branch": "main", "commit": "370ec46f710e058c9c1646273e6b225acf47cbed" }
}
41 changes: 41 additions & 0 deletions fixtures/snacks-explorer/lua/config/lazy.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)

-- Make sure to setup `mapleader` and `maplocalleader` before
-- loading lazy.nvim so that mappings are correct.
-- This is also a good place to setup other settings (vim.opt)
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- Setup lazy.nvim
require("lazy").setup({
spec = {
-- import your plugins
{ import = "plugins" },
},
-- Configure any other settings here. See the documentation for more details.
-- colorscheme that will be used when installing plugins.
install = { colorscheme = { "habamax" } },
-- automatically check for plugin updates
checker = { enabled = true },
})

-- Add keybind for Lazy plugin manager
vim.keymap.set("n", "<leader>l", "<cmd>Lazy<cr>", { desc = "Lazy Plugin Manager" })

-- Terminal keybindings
vim.keymap.set("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode (double esc)" })
1 change: 1 addition & 0 deletions fixtures/snacks-explorer/lua/plugins/dev-claudecode.lua
33 changes: 33 additions & 0 deletions fixtures/snacks-explorer/lua/plugins/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
return {
-- Essential plugins for basic functionality
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {},
keys = {
{
"<leader>?",
function()
require("which-key").show({ global = false })
end,
desc = "Buffer Local Keymaps (which-key)",
},
},
},

-- Icon support for file explorers
{
"echasnovski/mini.icons",
opts = {},
lazy = true,
specs = {
{ "nvim-tree/nvim-web-devicons", enabled = false, optional = true },
},
init = function()
package.preload["nvim-web-devicons"] = function()
require("mini.icons").mock_nvim_web_devicons()
return package.loaded["nvim-web-devicons"]
end
end,
},
}
183 changes: 183 additions & 0 deletions fixtures/snacks-explorer/lua/plugins/snacks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
return {
"folke/snacks.nvim",
priority = 1000,
lazy = false,
opts = {
-- Enable the explorer module
explorer = {
enabled = true,
replace_netrw = true, -- Replace netrw with snacks explorer
},
-- Enable other useful modules for testing
bigfile = { enabled = true },
notifier = { enabled = true },
quickfile = { enabled = true },
statuscolumn = { enabled = true },
words = { enabled = true },
},
keys = {
-- Main explorer keybindings
{
"<leader>e",
function()
require("snacks").explorer()
end,
desc = "Explorer",
},
{
"<leader>E",
function()
require("snacks").explorer.open()
end,
desc = "Explorer (open)",
},
{
"<leader>fe",
function()
require("snacks").explorer.reveal()
end,
desc = "Explorer (reveal current file)",
},

-- Alternative keybindings for testing
{
"-",
function()
require("snacks").explorer()
end,
desc = "Open parent directory",
},
{
"<C-n>",
function()
require("snacks").explorer()
end,
desc = "File Explorer",
},

-- Snacks utility keybindings for testing
{
"<leader>un",
function()
require("snacks").notifier.dismiss()
end,
desc = "Dismiss All Notifications",
},
{
"<leader>bd",
function()
require("snacks").bufdelete()
end,
desc = "Delete Buffer",
},
{
"<leader>gg",
function()
require("snacks").lazygit()
end,
desc = "Lazygit",
},
{
"<leader>gb",
function()
require("snacks").git.blame_line()
end,
desc = "Git Blame Line",
},
{
"<leader>gB",
function()
require("snacks").gitbrowse()
end,
desc = "Git Browse",
},
{
"<leader>gf",
function()
require("snacks").lazygit.log_file()
end,
desc = "Lazygit Current File History",
},
{
"<leader>gl",
function()
require("snacks").lazygit.log()
end,
desc = "Lazygit Log (cwd)",
},
{
"<leader>cR",
function()
require("snacks").rename.rename_file()
end,
desc = "Rename File",
},
{
"<c-/>",
function()
require("snacks").terminal()
end,
desc = "Toggle Terminal",
},
{
"<c-_>",
function()
require("snacks").terminal()
end,
desc = "which_key_ignore",
},
},
init = function()
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
callback = function()
-- Setup some globals for easier testing
_G.Snacks = require("snacks")
_G.lazygit = _G.Snacks.lazygit
_G.explorer = _G.Snacks.explorer
end,
})
end,
config = function(_, opts)
require("snacks").setup(opts)

-- Additional explorer-specific keybindings that activate after setup
vim.api.nvim_create_autocmd("FileType", {
pattern = "snacks_picker_list", -- This is the filetype for snacks explorer
callback = function(event)
local buf = event.buf
-- Custom keybindings specifically for snacks explorer buffers
vim.keymap.set("n", "<C-v>", function()
-- Toggle visual mode for multi-selection (this is what the PR adds support for)
vim.cmd("normal! V")
end, { buffer = buf, desc = "Toggle visual selection" })

vim.keymap.set("n", "v", function()
vim.cmd("normal! v")
end, { buffer = buf, desc = "Visual mode" })

vim.keymap.set("n", "V", function()
vim.cmd("normal! V")
end, { buffer = buf, desc = "Visual line mode" })

-- Additional testing keybindings
vim.keymap.set("n", "?", function()
require("which-key").show({ buffer = buf })
end, { buffer = buf, desc = "Show keybindings" })
end,
})

-- Set up some helpful defaults for testing
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.signcolumn = "yes"
vim.opt.wrap = false

-- Print helpful message when starting
vim.defer_fn(function()
print("🍿 Snacks Explorer fixture loaded!")
print("Press <leader>e to open explorer, <leader>? for help")
print("Use visual modes (v/V/<C-v>) in explorer for multi-file selection")
end, 500)
end,
}
1 change: 1 addition & 0 deletions lua/claudecode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ function M._create_commands()
local is_tree_buffer = current_ft == "NvimTree"
or current_ft == "neo-tree"
or current_ft == "oil"
or current_ft == "snacks_picker_list"
or current_ft == "minifiles"
or string.match(current_bufname, "neo%-tree")
or string.match(current_bufname, "NvimTree")
Expand Down
Loading
Loading