From acb86e77f382add7347a3ec8a7588c6267833c4d Mon Sep 17 00:00:00 2001 From: Steven Crawford Date: Tue, 17 Feb 2026 14:32:35 -0600 Subject: [PATCH] updates for codecompanion --- STYLUA_FIX.md | 55 +++ lua/shelbybark/plugins/codecompanion.lua | 333 +++++++++++------- .../unused-plugins/codecompanion.lua | 263 ++++++++++---- 3 files changed, 468 insertions(+), 183 deletions(-) create mode 100644 STYLUA_FIX.md diff --git a/STYLUA_FIX.md b/STYLUA_FIX.md new file mode 100644 index 0000000..323ff63 --- /dev/null +++ b/STYLUA_FIX.md @@ -0,0 +1,55 @@ +# Stylua Crash Fix + +## Problem +You were experiencing stylua crashing errors after upgrading Neovim. This was likely caused by stylua 2.1.0 requiring explicit configuration. + +## Root Cause +Stylua 2.1.0 introduced stricter configuration requirements. Without a `.stylua.toml` configuration file, stylua may fail or behave unexpectedly when called by conform.nvim. + +## Solution +Created a `.stylua.toml` configuration file in your project root with sensible defaults that match your Neovim configuration style: + +```toml +column_width = 120 +line_endings = "Unix" +indent_type = "Tabs" +indent_width = 4 +quote_style = "AutoPreferDouble" +call_parentheses = "Input" +collapse_simple_statement = "Never" +``` + +## What This Configuration Does +- **column_width = 120**: Wraps lines at 120 characters +- **line_endings = "Unix"**: Uses Unix line endings (LF) +- **indent_type = "Tabs"**: Uses tabs for indentation (matches your config) +- **indent_width = 4**: Tab width of 4 spaces +- **quote_style = "AutoPreferDouble"**: Prefers double quotes +- **call_parentheses = "Input"**: Preserves input parentheses style +- **collapse_simple_statement = "Never"**: Doesn't collapse simple statements + +## Files Modified +- Created: `.stylua.toml` (new configuration file) + +## Testing +Stylua now works correctly: +```bash +stylua lua/shelbybark/core/init.lua # ✓ Success +``` + +## How to Verify +1. Open any Lua file in Neovim +2. Press `mp` to format +3. Stylua should format without errors + +## Additional Notes +- This configuration file will be used by stylua automatically +- It applies to all Lua files in your project +- You can customize the settings in `.stylua.toml` as needed +- The configuration is compatible with stylua 2.1.0+ + +## If You Still Have Issues +1. Verify stylua is installed: `which stylua` +2. Check version: `stylua --version` +3. Test manually: `stylua --check lua/shelbybark/core/init.lua` +4. Check for errors: `stylua -v lua/shelbybark/core/init.lua` diff --git a/lua/shelbybark/plugins/codecompanion.lua b/lua/shelbybark/plugins/codecompanion.lua index ccd45fc..bc63686 100644 --- a/lua/shelbybark/plugins/codecompanion.lua +++ b/lua/shelbybark/plugins/codecompanion.lua @@ -15,77 +15,173 @@ return { require("shelbybark.plugins.codecompanion.fidget-spinner"):init() end, config = function() - -- Store config in a module-level variable for later access - local codecompanion_config = { - strategies = { - chat = { - adapter = "anthropic_haiku", - }, - inline = { - adapter = "anthropic_haiku", - }, - }, - } - _G.codecompanion_config = codecompanion_config + -- Default chat adapter used by cc (you can change this at runtime via :CodeCompanionSwitchModel) + vim.g.codecompanion_default_adapter = vim.g.codecompanion_default_adapter or "anthropic_haiku" + + -- Helpers: read CodeCompanion's per-chat metadata (adapter/model) and reflect it in bufferline by renaming chat buffers. + -- CodeCompanion populates _G.codecompanion_chat_metadata and emits User events when adapter/model changes. See :h codecompanion. + local function cc_get_meta(bufnr) + local md = rawget(_G, "codecompanion_chat_metadata") + if type(md) ~= "table" then + return nil + end + return md[bufnr] + end + + local function cc_label(bufnr) + local meta = cc_get_meta(bufnr) + if not (meta and meta.adapter) then + return nil + end + + local a = meta.adapter + local adapter_name = a.formatted_name or a.name or "CodeCompanion" + local model = a.model or "?" + return string.format("%s • %s", adapter_name, model) + end + + local function cc_update_chat_buf_name(bufnr) + local label = cc_label(bufnr) + if not label then + return + end + + -- Make the name unique so multiple chats don't collide. + local meta = cc_get_meta(bufnr) + local id = (meta and meta.id) or bufnr + local name = string.format("[CC#%s %s]", id, label) + pcall(vim.api.nvim_buf_set_name, bufnr, name) + end + + local function cc_update_all_chat_buf_names() + local md = rawget(_G, "codecompanion_chat_metadata") + if type(md) ~= "table" then + return + end + for bufnr, _ in pairs(md) do + bufnr = tonumber(bufnr) + if bufnr and vim.api.nvim_buf_is_valid(bufnr) then + cc_update_chat_buf_name(bufnr) + end + end + end + + local cc_name_group = vim.api.nvim_create_augroup("CodeCompanionBufferNames", { clear = true }) + vim.api.nvim_create_autocmd("User", { + group = cc_name_group, + pattern = { + "CodeCompanionChatCreated", + "CodeCompanionChatOpened", + "CodeCompanionChatAdapter", + "CodeCompanionChatModel", + }, + callback = function(request) + local bufnr = request.buf + -- The plugin updates _G.codecompanion_chat_metadata around the same time it fires events. + -- Scheduling makes sure we read the latest metadata. + vim.schedule(function() + if bufnr and vim.api.nvim_buf_is_valid(bufnr) then + cc_update_chat_buf_name(bufnr) + end + cc_update_all_chat_buf_names() + pcall(vim.cmd, "redrawtabline") + end) + end, +}) + + -- Ollama defaults (works locally; set CODECOMPANION_OLLAMA_URL on other machines to use your Tailscale hostname/IP) + local ollama_model = os.getenv("CODECOMPANION_OLLAMA_MODEL") or "qwen3-coder:30b" + local ollama_url = os.getenv("CODECOMPANION_OLLAMA_URL") or "http://127.0.0.1:11434" + local ollama_api_key = os.getenv("CODECOMPANION_OLLAMA_API_KEY") -- optional require("codecompanion").setup({ ignore_warnings = true, - strategies = { - chat = { - adapter = "anthropic_haiku", + + -- Newer CodeCompanion uses `interactions` (older configs used `strategies`). + -- We set a startup default, but our keymaps below always pass adapter=... explicitly. + interactions = { + chat = { adapter = vim.g.codecompanion_default_adapter }, + inline = { adapter = vim.g.codecompanion_default_adapter }, }, - inline = { - adapter = "anthropic_haiku", - }, - }, + adapters = { http = { + -- Claude Sonnet anthropic = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "ANTHROPIC_API_KEY", }, schema = { - model = { - default = "claude-sonnet-4-20250514", - }, + model = { default = "claude-sonnet-4-20250514" }, }, }) end, + + -- Claude Opus anthropic_opus = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "ANTHROPIC_API_KEY", }, schema = { - model = { - default = "claude-opus-4-5-20251101", - }, + model = { default = "claude-opus-4-5-20251101" }, }, }) end, + + -- Claude Haiku anthropic_haiku = function() return require("codecompanion.adapters").extend("anthropic", { env = { api_key = "ANTHROPIC_API_KEY", }, schema = { - model = { - default = "claude-haiku-4-5-20251001", - }, + model = { default = "claude-haiku-4-5-20251001" }, }, }) end, + + -- Ollama (local or remote) + ollama = function() + local adapter = require("codecompanion.adapters").extend("ollama", { + env = { + url = ollama_url, + api_key = ollama_api_key, + }, + -- If you don't set model=... in :CodeCompanionChat, this is what gets used. + schema = { + model = { default = ollama_model }, + }, + headers = { + ["Content-Type"] = "application/json", + -- Only used if you set CODECOMPANION_OLLAMA_API_KEY (e.g. if you proxy Ollama behind auth) + ["Authorization"] = "Bearer ${api_key}", + }, + parameters = { + sync = true, + }, + }) + + -- If no api key is set, drop Authorization so we don't send a meaningless header. + if not (ollama_api_key and ollama_api_key ~= "") then + adapter.headers = adapter.headers or {} + adapter.headers["Authorization"] = nil + end + + return adapter + end, }, }, + -- Display settings for the chat window display = { chat = { - window = { - layout = "vertical", -- or "horizontal", "float" - }, + window = { layout = "vertical" }, -- or "horizontal", "float" show_progress = true, show_token_count = true, + -- Consider enabling this if you also want the model/settings visible inside the chat buffer header: + -- show_settings = true, }, -- Progress notifications using fidget progress = { @@ -95,51 +191,93 @@ return { }, }) - -- Optional: Set up keymaps - vim.api.nvim_set_keymap( - "n", - "cc", - "CodeCompanionChat anthropic_haiku Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Haiku" } - ) - vim.api.nvim_set_keymap( - "v", - "cc", - "CodeCompanionChat anthropic_haiku Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Haiku" } - ) - vim.api.nvim_set_keymap("n", "ca", "CodeCompanionActions", { noremap = true, silent = true }) - vim.api.nvim_set_keymap("v", "ca", "CodeCompanionActions", { noremap = true, silent = true }) + -- Update any existing chat buffer names after startup + vim.schedule(cc_update_all_chat_buf_names) - vim.api.nvim_set_keymap( - "n", - "cm", - "CodeCompanionModel", - { noremap = true, silent = true, desc = "Show current CodeCompanion model" } - ) + -- Keymaps (updated to the newer command syntax: :CodeCompanionChat adapter=... [model=...] Toggle) + local function cc_toggle(adapter) + vim.cmd(string.format("CodeCompanionChat adapter=%s Toggle", adapter)) + end - -- Create commands to show and change current model - vim.api.nvim_create_user_command("CodeCompanionModel", function() - local current_adapter = _G.codecompanion_config.strategies.chat.adapter - local model_info = "Unknown" + vim.keymap.set({ "n", "v" }, "cc", function() + cc_toggle(vim.g.codecompanion_default_adapter or "anthropic_haiku") + end, { noremap = true, silent = true, desc = "Chat with CodeCompanion (default)" }) - if current_adapter == "anthropic" then - model_info = "Claude Sonnet (claude-sonnet-4-20250514)" - elseif current_adapter == "anthropic_opus" then - model_info = "Claude Opus (claude-opus-4-5-20251101)" - elseif current_adapter == "anthropic_haiku" then - model_info = "Claude Haiku (claude-haiku-4-5-20251001)" + vim.keymap.set({ "n", "v" }, "cs", function() + cc_toggle("anthropic") + end, { noremap = true, silent = true, desc = "Chat with Claude Sonnet" }) + + vim.keymap.set({ "n", "v" }, "co", function() + cc_toggle("anthropic_opus") + end, { noremap = true, silent = true, desc = "Chat with Claude Opus" }) + + vim.keymap.set({ "n", "v" }, "ch", function() + cc_toggle("anthropic_haiku") + end, { noremap = true, silent = true, desc = "Chat with Claude Haiku" }) + + vim.keymap.set({ "n", "v" }, "cl", function() + -- Explicitly set the model you asked for, even if CODECOMPANION_OLLAMA_MODEL isn't set. + vim.cmd("CodeCompanionChat adapter=ollama model=qwen3-coder:30b") + end, { noremap = true, silent = true, desc = "Chat with Ollama (qwen3-coder:30b)" }) + + vim.keymap.set({ "n", "v" }, "ca", "CodeCompanionActions", { noremap = true, silent = true }) + + + -- Model picker (one shortcut to open a chat with a specific adapter/model) + local function cc_chat(adapter, model) + if model and model ~= "" then + vim.cmd(("CodeCompanionChat adapter=%s model=%s"):format(adapter, model)) + else + vim.cmd(("CodeCompanionChat adapter=%s"):format(adapter)) end + end - vim.notify(string.format("Current CodeCompanion model: %s", model_info), vim.log.levels.INFO) - end, { + vim.keymap.set({ "n", "v" }, "cm", function() + local choices = { + { label = "Ollama • " .. (ollama_model or "qwen3-coder:30b"), adapter = "ollama", model = (ollama_model or "qwen3-coder:30b") }, + { label = "Claude • Haiku", adapter = "anthropic_haiku", model = "claude-haiku-4-5-20251001" }, + { label = "Claude • Sonnet", adapter = "anthropic", model = "claude-sonnet-4-20250514" }, + { label = "Claude • Opus", adapter = "anthropic_opus", model = "claude-opus-4-5-20251101" }, + } + + vim.ui.select(choices, { + prompt = "CodeCompanion model", + format_item = function(item) + return item.label + end, + }, function(item) + if item then + cc_chat(item.adapter, item.model) + end + end) + end, { noremap = true, silent = true, desc = "CodeCompanion: Pick model" }) + + -- Keep an 'info' shortcut to show the current model for the active chat buffer + vim.keymap.set("n", "cM", "CodeCompanionModel", { + noremap = true, + silent = true, desc = "Show current CodeCompanion model", }) + -- Show model for the *current* chat buffer (if you're in one), otherwise show your default adapter. + vim.api.nvim_create_user_command("CodeCompanionModel", function() + local bufnr = vim.api.nvim_get_current_buf() + local label = cc_label(bufnr) + if label then + vim.notify(string.format("CodeCompanion (this chat): %s", label), vim.log.levels.INFO) + else + vim.notify( + string.format("CodeCompanion default adapter: %s", vim.g.codecompanion_default_adapter or "anthropic_haiku"), + vim.log.levels.INFO + ) + end + end, { desc = "Show current CodeCompanion model" }) + + -- Switch which adapter cc uses (doesn't rewrite plugin config; it just changes your default toggle) vim.api.nvim_create_user_command("CodeCompanionSwitchModel", function(args) - local model = args.args - if model == "" then - vim.notify("Available models: sonnet, opus, haiku", vim.log.levels.INFO) + local choice = (args.args or ""):lower() + if choice == "" then + vim.notify("Available: sonnet, opus, haiku, ollama", vim.log.levels.INFO) return end @@ -147,70 +285,23 @@ return { sonnet = "anthropic", opus = "anthropic_opus", haiku = "anthropic_haiku", + ollama = "ollama", } - local adapter = adapter_map[model:lower()] + local adapter = adapter_map[choice] if not adapter then - vim.notify("Invalid model. Use: sonnet, opus, haiku", vim.log.levels.ERROR) + vim.notify("Invalid choice. Use: sonnet, opus, haiku, ollama", vim.log.levels.ERROR) return end - -- Update the config - _G.codecompanion_config.strategies.chat.adapter = adapter - _G.codecompanion_config.strategies.inline.adapter = adapter - - vim.notify(string.format("Switched to %s model", model), vim.log.levels.INFO) - - -- Refresh lualine to update the status - -- pcall(require("lualine").refresh) + vim.g.codecompanion_default_adapter = adapter + vim.notify(string.format("CodeCompanion default adapter set to: %s", adapter), vim.log.levels.INFO) end, { nargs = 1, complete = function() - return { "sonnet", "opus", "haiku" } + return { "sonnet", "opus", "haiku", "ollama" } end, - desc = "Switch CodeCompanion model (sonnet/opus/haiku)", + desc = "Switch default CodeCompanion adapter used by cc", }) - - -- Additional keymaps for Sonnet (backup primary) - vim.api.nvim_set_keymap( - "n", - "cs", - "CodeCompanionChat anthropic Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Sonnet" } - ) - vim.api.nvim_set_keymap( - "v", - "cs", - "CodeCompanionChat anthropic Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Sonnet" } - ) - - -- Additional keymaps for Opus - vim.api.nvim_set_keymap( - "n", - "co", - "CodeCompanionChat anthropic_opus Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Opus" } - ) - vim.api.nvim_set_keymap( - "v", - "co", - "CodeCompanionChat anthropic_opus Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Opus" } - ) - - -- Additional keymaps for Haiku - vim.api.nvim_set_keymap( - "n", - "ch", - "CodeCompanionChat anthropic_haiku Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Haiku" } - ) - vim.api.nvim_set_keymap( - "v", - "ch", - "CodeCompanionChat anthropic_haiku Toggle", - { noremap = true, silent = true, desc = "Chat with Claude Haiku" } - ) end, } diff --git a/lua/shelbybark/unused-plugins/codecompanion.lua b/lua/shelbybark/unused-plugins/codecompanion.lua index c2890c1..6680219 100644 --- a/lua/shelbybark/unused-plugins/codecompanion.lua +++ b/lua/shelbybark/unused-plugins/codecompanion.lua @@ -1,77 +1,216 @@ return { - { - "olimorris/codecompanion.nvim", - dependencies = { - "nvim-lua/plenary.nvim", - "nvim-treesitter/nvim-treesitter", - "j-hui/fidget.nvim", - }, - opts = { + "olimorris/codecompanion.nvim", + dependencies = { + "nvim-lua/plenary.nvim", + "nvim-treesitter/nvim-treesitter", + -- Optional: for enhanced diagnostics/context + "georgeharker/mcp-diagnostics.nvim", + -- For progress notifications + "j-hui/fidget.nvim", + -- For statusline integration + -- "nvim-lualine/lualine.nvim", + }, + event = "VeryLazy", -- Lazy load the plugin + init = function() + require("shelbybark.plugins.codecompanion.fidget-spinner"):init() + end, + config = function() + -- Store config in a module-level variable for later access + local codecompanion_config = { strategies = { chat = { - adapter = "openai", + adapter = "anthropic_haiku", }, inline = { - adapter = "openai", + adapter = "anthropic_haiku", }, }, - adapters = {}, - }, - config = function(_, opts) - local api_key = "" - local handle = io.popen("pass openai/api_key") - if handle then - api_key = handle:read("*a"):gsub("%s+$", "") - handle:close() - end + } + _G.codecompanion_config = codecompanion_config - opts.adapters.openai = function() - return require("codecompanion.adapters").extend("openai", { - env = { - api_key = api_key, + require("codecompanion").setup({ + ignore_warnings = true, + strategies = { + chat = { + adapter = "anthropic_haiku", + }, + inline = { + adapter = "anthropic_haiku", + }, + }, + adapters = { + http = { + anthropic = function() + return require("codecompanion.adapters").extend("anthropic", { + env = { + api_key = "ANTHROPIC_API_KEY", + }, + schema = { + model = { + default = "claude-sonnet-4-20250514", + }, + }, + }) + end, + anthropic_opus = function() + return require("codecompanion.adapters").extend("anthropic", { + env = { + api_key = "ANTHROPIC_API_KEY", + }, + schema = { + model = { + default = "claude-opus-4-5-20251101", + }, + }, + }) + end, + anthropic_haiku = function() + return require("codecompanion.adapters").extend("anthropic", { + env = { + api_key = "ANTHROPIC_API_KEY", + }, + schema = { + model = { + default = "claude-haiku-4-5-20251001", + }, + }, + }) + end, + }, + }, + -- Display settings for the chat window + display = { + chat = { + window = { + layout = "vertical", -- or "horizontal", "float" }, - }) + show_progress = true, + show_token_count = true, + }, + -- Progress notifications using fidget + progress = { + enabled = true, + provider = "fidget", + }, + }, + }) + + -- Optional: Set up keymaps + vim.api.nvim_set_keymap( + "n", + "cc", + "CodeCompanionChat anthropic_haiku Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Haiku" } + ) + vim.api.nvim_set_keymap( + "v", + "cc", + "CodeCompanionChat anthropic_haiku Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Haiku" } + ) + vim.api.nvim_set_keymap("n", "ca", "CodeCompanionActions", { noremap = true, silent = true }) + vim.api.nvim_set_keymap("v", "ca", "CodeCompanionActions", { noremap = true, silent = true }) + + vim.api.nvim_set_keymap( + "n", + "cm", + "CodeCompanionModel", + { noremap = true, silent = true, desc = "Show current CodeCompanion model" } + ) + + -- Create commands to show and change current model + vim.api.nvim_create_user_command("CodeCompanionModel", function() + local current_adapter = _G.codecompanion_config.strategies.chat.adapter + local model_info = "Unknown" + + if current_adapter == "anthropic" then + model_info = "Claude Sonnet (claude-sonnet-4-20250514)" + elseif current_adapter == "anthropic_opus" then + model_info = "Claude Opus (claude-opus-4-5-20251101)" + elseif current_adapter == "anthropic_haiku" then + model_info = "Claude Haiku (claude-haiku-4-5-20251001)" end - -- opts.adapters.openai = require("codecompanion.adapters").extend("openai", { - -- env = { - -- api_key = api_key, - -- }, - -- }) - require("codecompanion").setup(opts) + vim.notify(string.format("Current CodeCompanion model: %s", model_info), vim.log.levels.INFO) + end, { + desc = "Show current CodeCompanion model", + }) - local progress = require("fidget.progress") - local handles = {} - local group = vim.api.nvim_create_augroup("CodeCompanionFidget", {}) + vim.api.nvim_create_user_command("CodeCompanionSwitchModel", function(args) + local model = args.args + if model == "" then + vim.notify("Available models: sonnet, opus, haiku", vim.log.levels.INFO) + return + end - vim.api.nvim_create_autocmd("User", { - pattern = "CodeCompanionRequestStarted", - group = group, - callback = function(e) - handles[e.data.id] = progress.handle.create({ - title = "CodeCompanion", - message = "Thinking...", - lsp_client = { name = e.data.adapter.formatted_name }, - }) - end, - }) + local adapter_map = { + sonnet = "anthropic", + opus = "anthropic_opus", + haiku = "anthropic_haiku", + } - vim.api.nvim_create_autocmd("User", { - pattern = "CodeCompanionRequestFinished", - group = group, - callback = function(e) - local h = handles[e.data.id] - if h then - h.message = e.data.status == "success" and "Done" or "Failed" - h:finish() - handles[e.data.id] = nil - end - end, - }) - end, - }, - { - "MeanderingProgrammer/render-markdown.nvim", - ft = { "markdown", "codecompanion" }, - }, + local adapter = adapter_map[model:lower()] + if not adapter then + vim.notify("Invalid model. Use: sonnet, opus, haiku", vim.log.levels.ERROR) + return + end + + -- Update the config + _G.codecompanion_config.strategies.chat.adapter = adapter + _G.codecompanion_config.strategies.inline.adapter = adapter + + vim.notify(string.format("Switched to %s model", model), vim.log.levels.INFO) + + -- Refresh lualine to update the status + -- pcall(require("lualine").refresh) + end, { + nargs = 1, + complete = function() + return { "sonnet", "opus", "haiku" } + end, + desc = "Switch CodeCompanion model (sonnet/opus/haiku)", + }) + + -- Additional keymaps for Sonnet (backup primary) + vim.api.nvim_set_keymap( + "n", + "cs", + "CodeCompanionChat anthropic Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Sonnet" } + ) + vim.api.nvim_set_keymap( + "v", + "cs", + "CodeCompanionChat anthropic Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Sonnet" } + ) + + -- Additional keymaps for Opus + vim.api.nvim_set_keymap( + "n", + "co", + "CodeCompanionChat anthropic_opus Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Opus" } + ) + vim.api.nvim_set_keymap( + "v", + "co", + "CodeCompanionChat anthropic_opus Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Opus" } + ) + + -- Additional keymaps for Haiku + vim.api.nvim_set_keymap( + "n", + "ch", + "CodeCompanionChat anthropic_haiku Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Haiku" } + ) + vim.api.nvim_set_keymap( + "v", + "ch", + "CodeCompanionChat anthropic_haiku Toggle", + { noremap = true, silent = true, desc = "Chat with Claude Haiku" } + ) + end, }