Skip to content

Commit 3f11c12

Browse files
committed
refactor: remove Claude OAuth integration from Neovim config
- Strip custom Anthropic adapter with OAuth token handling from codecompanion.lua - Remove Claude token setup from xvim wrapper script - Update codecompanion and direnv-nvim plugin dependencies - Update flake dependencies (base-nixpkgs, git-hooks, nixpkgs-lib, stable/unstable channels)
1 parent c866b6c commit 3f11c12

File tree

3 files changed

+60
-375
lines changed

3 files changed

+60
-375
lines changed

config/lua/plugins/codecompanion.lua

Lines changed: 0 additions & 307 deletions
Original file line numberDiff line numberDiff line change
@@ -3,314 +3,7 @@ return {
33
"olimorris/codecompanion.nvim",
44
event = "VeryLazy",
55
config = function()
6-
-- Configuration constants
7-
local config = {
8-
anthropic_version = "2023-06-01",
9-
anthropic_beta = "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,prompt-caching-2024-07-31,token-efficient-tools-2025-02-19",
10-
claude_code_system_message = "You are Claude Code, Anthropic's official CLI for Claude.",
11-
allowed_message_fields = { "content", "role", "reasoning", "tools" },
12-
}
13-
14-
-- Validate OAuth token setup (only warn in interactive sessions)
15-
local oauth_token = vim.env.CLAUDE_CODE_OAUTH_TOKEN
16-
if not oauth_token or oauth_token == "" then
17-
if vim.fn.has("gui_running") == 1 or vim.env.TERM then
18-
vim.notify("No Claude OAuth token found - ensure CLAUDE_CODE_OAUTH_TOKEN is set", vim.log.levels.WARN)
19-
end
20-
end
21-
22-
-- Helper function to keep only allowed message fields
23-
local function keep_allowed_message_fields(message, allowed_fields)
24-
local cleaned_message = {}
25-
for key, value in pairs(message) do
26-
if vim.tbl_contains(allowed_fields, key) then
27-
cleaned_message[key] = value
28-
end
29-
end
30-
return cleaned_message
31-
end
32-
33-
-- Add Claude Code system message at the beginning
34-
local function add_claude_code_system_message(system)
35-
table.insert(system, 1, {
36-
type = "text",
37-
text = config.claude_code_system_message,
38-
cache_control = { type = "ephemeral" },
39-
})
40-
return system
41-
end
42-
43-
-- Process image content for vision models
44-
local function process_image_content(message, self)
45-
if message.opts and message.opts.tag == "image" and message.opts.mimetype then
46-
if self.opts and self.opts.vision then
47-
message.content = {
48-
{
49-
type = "image",
50-
source = {
51-
type = "base64",
52-
media_type = message.opts.mimetype,
53-
data = message.content,
54-
},
55-
},
56-
}
57-
else
58-
return nil
59-
end
60-
end
61-
return message
62-
end
63-
64-
-- Apply caching to messages and system prompts
65-
local function apply_message_caching(messages, system, self, tokens)
66-
local breakpoints_used = 0
67-
68-
for i = #messages, 1, -1 do
69-
local msgs = messages[i]
70-
if msgs.role == self.roles.user and msgs.content and type(msgs.content) == "table" then
71-
for _, msg in ipairs(msgs.content) do
72-
if msg.type == "text" and msg.text and msg.text ~= "" then
73-
if
74-
tokens.calculate(msg.text) >= (self.opts.cache_over or 2048)
75-
and breakpoints_used < (self.opts.cache_breakpoints or 3)
76-
then
77-
msg.cache_control = { type = "ephemeral" }
78-
breakpoints_used = breakpoints_used + 1
79-
end
80-
end
81-
end
82-
end
83-
end
84-
85-
if system and breakpoints_used < (self.opts.cache_breakpoints or 3) then
86-
for _, prompt in ipairs(system) do
87-
if breakpoints_used < (self.opts.cache_breakpoints or 3) then
88-
prompt.cache_control = { type = "ephemeral" }
89-
breakpoints_used = breakpoints_used + 1
90-
end
91-
end
92-
end
93-
end
94-
95-
-- Consolidate tool results
96-
local function consolidate_tool_results(messages, self)
97-
for _, m in ipairs(messages) do
98-
if m.role == self.roles.user and m.content and m.content ~= "" then
99-
if type(m.content) == "table" and m.content.type then
100-
m.content = { m.content }
101-
end
102-
103-
if type(m.content) == "table" and vim.islist(m.content) then
104-
local consolidated = {}
105-
for _, block in ipairs(m.content) do
106-
if block.type == "tool_result" then
107-
local prev = consolidated[#consolidated]
108-
if prev and prev.type == "tool_result" and prev.tool_use_id == block.tool_use_id then
109-
prev.content = prev.content .. block.content
110-
else
111-
table.insert(consolidated, block)
112-
end
113-
else
114-
table.insert(consolidated, block)
115-
end
116-
end
117-
m.content = consolidated
118-
end
119-
end
120-
end
121-
end
122-
123-
local utils = require("codecompanion.utils.adapters")
124-
local tokens = require("codecompanion.utils.tokens")
125-
1266
require("codecompanion").setup({
127-
adapters = {
128-
http = {
129-
anthropic = function()
130-
return require("codecompanion.adapters").extend("anthropic", {
131-
env = {
132-
api_key = function()
133-
return vim.env.CLAUDE_CODE_OAUTH_TOKEN
134-
end,
135-
},
136-
schema = {
137-
extended_thinking = {
138-
default = false,
139-
},
140-
},
141-
headers = {
142-
["content-type"] = "application/json",
143-
["authorization"] = "Bearer ${api_key}",
144-
["anthropic-version"] = config.anthropic_version,
145-
["anthropic-beta"] = config.anthropic_beta,
146-
},
147-
handlers = {
148-
setup = function(self)
149-
-- Remove x-api-key header (we use Bearer token instead)
150-
if self.headers and self.headers["x-api-key"] then
151-
self.headers["x-api-key"] = nil
152-
end
153-
154-
if self.opts and self.opts.stream then
155-
self.parameters.stream = true
156-
end
157-
158-
local model = self.schema and self.schema.model and self.schema.model.default
159-
if model then
160-
local model_opts = self.schema.model.choices and self.schema.model.choices[model]
161-
if model_opts and model_opts.opts then
162-
self.opts = vim.tbl_deep_extend("force", self.opts or {}, model_opts.opts)
163-
if not model_opts.opts.has_vision then
164-
self.opts.vision = false
165-
end
166-
end
167-
end
168-
169-
return true
170-
end,
171-
172-
form_messages = function(self, messages)
173-
local has_tools = false
174-
175-
-- Extract and format system messages
176-
local system = vim
177-
.iter(messages)
178-
:filter(function(msg)
179-
return msg.role == "system"
180-
end)
181-
:map(function(msg)
182-
return {
183-
type = "text",
184-
text = msg.content,
185-
cache_control = nil,
186-
}
187-
end)
188-
:totable()
189-
190-
-- Add Claude Code system message
191-
system = add_claude_code_system_message(system)
192-
if #system == 0 then
193-
system = {}
194-
end
195-
196-
-- Filter out system messages from main message list
197-
messages = vim
198-
.iter(messages)
199-
:filter(function(msg)
200-
return msg.role ~= "system"
201-
end)
202-
:totable()
203-
204-
-- Process each message
205-
messages = vim.tbl_map(function(m)
206-
m = process_image_content(m, self)
207-
if m == nil then
208-
return nil
209-
end
210-
211-
m = keep_allowed_message_fields(m, config.allowed_message_fields)
212-
213-
-- Ensure content is properly formatted
214-
if m.role == self.roles.user or m.role == self.roles.llm then
215-
if m.role == self.roles.user and (m.content == "" or m.content == nil) then
216-
m.content = "<prompt></prompt>"
217-
end
218-
219-
if type(m.content) == "string" then
220-
m.content = {
221-
{ type = "text", text = m.content },
222-
}
223-
end
224-
end
225-
226-
-- Track tools usage (new format uses m.tools.calls)
227-
if m.tools and m.tools.calls and vim.tbl_count(m.tools.calls) > 0 then
228-
has_tools = true
229-
end
230-
231-
-- Handle tool role - convert tool results to Anthropic format
232-
if m.role == "tool" then
233-
m.role = self.roles.user
234-
if m.tools and m.tools.type == "tool_result" then
235-
if type(m.content) == "table" and m.content.type == "tool_result" then
236-
m.content = { m.content }
237-
else
238-
m.content = {
239-
{
240-
type = "tool_result",
241-
tool_use_id = m.tools.call_id,
242-
content = m.content,
243-
is_error = m.tools.is_error or false,
244-
},
245-
}
246-
end
247-
m.tools = nil
248-
end
249-
end
250-
251-
-- Handle tool calls in LLM messages (new format)
252-
if has_tools and m.role == self.roles.llm and m.tools and m.tools.calls then
253-
m.content = m.content or {}
254-
for _, call in ipairs(m.tools.calls) do
255-
local args = call["function"].arguments
256-
table.insert(m.content --[[@as table]], {
257-
type = "tool_use",
258-
id = call.id,
259-
name = call["function"].name,
260-
input = args ~= "" and vim.json.decode(args) or vim.empty_dict(),
261-
})
262-
end
263-
m.tools = nil
264-
end
265-
266-
-- Handle reasoning/thinking content
267-
if m.reasoning and type(m.content) == "table" then
268-
table.insert(m.content --[[@as table]], 1, {
269-
type = "thinking",
270-
thinking = m.reasoning.content,
271-
signature = m.reasoning._data and m.reasoning._data.signature,
272-
})
273-
end
274-
275-
return m
276-
end, messages)
277-
278-
messages = vim.tbl_filter(function(msg)
279-
return msg ~= nil
280-
end, messages)
281-
282-
messages = utils.merge_messages(messages)
283-
284-
if has_tools then
285-
consolidate_tool_results(messages, self)
286-
end
287-
288-
apply_message_caching(messages, system, self, tokens)
289-
290-
return { system = system, messages = messages }
291-
end,
292-
},
293-
})
294-
end,
295-
},
296-
},
297-
interactions = {
298-
chat = {
299-
adapter = "anthropic",
300-
},
301-
},
302-
display = {
303-
action_palette = {
304-
width = 95,
305-
height = 10,
306-
prompt = "Prompt ",
307-
provider = "default",
308-
opts = {
309-
show_default_actions = true,
310-
show_default_prompt_library = true,
311-
},
312-
},
313-
},
3147
extensions = {
3158
mcphub = {
3169
callback = "mcphub.extensions.codecompanion",

0 commit comments

Comments
 (0)