1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
local IS_DEV = false
return {
{
dir = IS_DEV and "~/Projects/research/codecompanion.nvim" or nil,
"olimorris/codecompanion.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-treesitter/nvim-treesitter",
"nvim-telescope/telescope.nvim", -- Using telescope for file and buffer picker
"jellydn/spinner.nvim", -- Show loading spinner when request is started
"ravitemer/codecompanion-history.nvim", -- History management extension
{
"echasnovski/mini.diff",
config = function()
local diff = require "mini.diff"
diff.setup { source = diff.gen_source.none() }
end,
},
{
"ravitemer/mcphub.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
},
build = "npm install -g mcp-hub@latest", -- Installs `mcp-hub` node binary globally
config = function()
require("mcphub").setup()
end,
},
},
opts = {
log_level = "TRACE", -- or "DEBUG"
adapters = {
http = {
ollama = function()
return require("codecompanion.adapters").extend("ollama", {
name = "tinyllama",
env = {
url = "http://127.0.0.1:11434",
-- api_key = "",
},
headers = {
["Content-Type"] = "application/json",
-- ["Authorization"] = "Bearer ${api_key}",
},
parameters = {
sync = true,
},
schema = {
model = {
default = "qwen2.5-coder:0.5b",
},
},
})
end,
anthropic = function()
return require("codecompanion.adapters").extend("anthropic", {
env = {
api_key = "cmd:echo $ANTHROPIC_API_KEY",
},
schema = {
model = {
default = "claude-3-opus-20240229",
},
},
})
end,
copilot = function()
return require("codecompanion.adapters").extend("copilot", {
schema = {
model = {
default = "gpt-4.1",
},
},
})
end,
gemini = function()
return require("codecompanion.adapters").extend("gemini", {
env = {
api_key = "cmd:echo $GEMINI_API_KEY",
},
})
end,
openai = function()
return require("codecompanion.adapters").extend("openai", {
env = {
api_key = "cmd:echo $OPENAI_API_KEY",
chat_url = "/v1/chat/completions",
url = "https://api.openai.com",
},
schema = {
model = {
default = function()
return "gpt-4o-mini"
end,
},
},
})
end,
openai_compatible_llama_cpp = function()
return require("codecompanion.adapters").extend("openai_compatible", {
env = {
api_key = "cmd:echo $OPENAI_API_KEY",
chat_url = "/v1/chat/completions",
url = "http://127.0.0.1:8012",
},
headers = {
["Content-Type"] = "application/json",
},
schema = {
model = {
default = function()
return "davinci"
end,
},
},
})
end,
openai_compatible_qwen3_coder = function()
return require("codecompanion.adapters").extend("openai_compatible", {
env = {
api_key = "cmd:echo $DASHSCOPE_API_KEY",
chat_url = "/v1/chat/completions",
url = "https://dashscope-intl.aliyuncs.com/compatible-mode",
},
headers = {
["Content-Type"] = "application/json",
},
schema = {
model = {
default = function()
return "qwen3-coder-plus"
end,
},
},
})
end,
xai = function()
return require("codecompanion.adapters").extend("xai", {
env = {
api_key = "cmd:op read op://personal/xAI_API/credential --no-newline",
},
})
end,
azure_openai = function()
return require("codecompanion.adapters").extend("azure_openai", {
env = {
api_key = "cmd:echo $AZ_DS_API_KEY",
endpoint = "https://DeepSeek-R1-nphob.eastus2.models.ai.azure.com",
},
schema = {
model = {
default = "DeepSeek-R1-nphob",
},
},
})
end,
},
},
display = {
diff = {
provider = "mini_diff",
},
},
strategies = {
chat = {
adapter = "openai_compatible_qwen3_coder",
-- roles = { llm = " Copilot Chat", user = "IT Man" },
},
inline = { adapter = "copilot" },
agent = { adapter = "copilot" },
cmd = { adapter = "copilot" },
},
},
config = function(_, options)
require("codecompanion").setup(options)
require("codecompanion").setup {
opts = {
language = "English",
},
extensions = {
mcphub = {
callback = "mcphub.extensions.codecompanion",
opts = {
make_vars = true,
make_slash_commands = true,
show_result_in_chat = true,
},
},
history = {
enabled = true,
opts = {
-- Keymap to open history from chat buffer (default: gh)
keymap = "gh",
-- Keymap to save the current chat manually (when auto_save is disabled)
save_chat_keymap = "sc",
-- Save all chats by default (disable to save only manually using 'sc')
auto_save = true,
-- Number of days after which chats are automatically deleted (0 to disable)
expiration_days = 0,
-- Picker interface (auto resolved to a valid picker)
picker = "telescope", -- ("telescope", "snacks", "fzf-lua", or "default")
-- Optional filter function to control which chats are shown when browsing
chat_filter = nil, -- function(chat_data) return boolean end
-- Customize picker keymaps (optional)
picker_keymaps = {
rename = { n = "r", i = "<M-r>" },
delete = { n = "d", i = "<M-d>" },
duplicate = { n = "<C-y>", i = "<C-y>" },
},
-- Automatically generate titles for new chats
auto_generate_title = true,
title_generation_opts = {
-- Adapter for generating titles (defaults to current chat adapter)
adapter = "copilot", -- Use copilot for cheaper title generation
-- Model for generating titles (defaults to current chat model)
model = "gpt-4.1", -- Use cheaper model for titles
-- Number of user prompts after which to refresh the title (0 to disable)
refresh_every_n_prompts = 3, -- Refresh after every 3rd user prompt
-- Maximum number of times to refresh the title (default: 3)
max_refreshes = 5,
format_title = function(original_title)
-- Custom formatting for titles
return original_title
end,
},
-- On exiting and entering neovim, loads the last chat on opening chat
continue_last_chat = false,
-- When chat is cleared with `gx` delete the chat from history
delete_on_clearing_chat = false,
-- Directory path to save the chats
dir_to_save = vim.fn.stdpath "data" .. "/codecompanion-history",
-- Enable detailed logging for history extension
enable_logging = false,
-- Summary system
summary = {
-- Keymap to generate summary for current chat (default: "gcs")
create_summary_keymap = "gcs",
-- Keymap to browse summaries (default: "gbs")
browse_summaries_keymap = "gbs",
generation_opts = {
adapter = "copilot", -- Use copilot for cheaper summary generation
model = "gpt-4.1", -- Use cheaper model for summaries
context_size = 90000, -- max tokens that the model supports
include_references = true, -- include slash command content
include_tool_outputs = true, -- include tool execution results
system_prompt = nil, -- custom system prompt (string or function)
format_summary = nil, -- custom function to format generated summary
},
},
-- Memory system (requires VectorCode CLI)
memory = {
-- Automatically index summaries when they are generated
auto_create_memories_on_summary_generation = false, -- Disabled by default
-- Path to the VectorCode executable
vectorcode_exe = "vectorcode",
-- Tool configuration
tool_opts = {
-- Default number of memories to retrieve
default_num = 10,
},
-- Enable notifications for indexing progress
notify = true,
-- Index all existing memories on startup
index_on_startup = false,
},
},
},
},
}
-- Show loading spinner when request is started
local spinner = require "spinner"
local group = vim.api.nvim_create_augroup("CodeCompanionHooks", {})
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "CodeCompanionRequest*",
group = group,
callback = function(request)
if request.match == "CodeCompanionRequestStarted" then
spinner.show()
end
if request.match == "CodeCompanionRequestFinished" then
spinner.hide()
end
end,
})
end,
},
}
|