@@ -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