@@ -106,7 +106,6 @@ local function process_response(session_info, messages)
106106 end
107107 end
108108
109- -- Log errors but don't fail completely if some replacements worked
110109 if # errors > 0 then
111110 for _ , err in ipairs (errors ) do
112111 vim .notify (' Quick chat: ' .. err , vim .log .levels .WARN )
@@ -141,11 +140,13 @@ local on_done = Promise.async(function(active_session)
141140 cleanup_session (running_session , active_session .id , ' Failed to update file with quick chat response' )
142141 end
143142
144- -- @TODO: enable session deletion after testing
145- -- Always delete ephemeral session
146- -- state.api_client:delete_session(session_obj.id):catch(function(err)
147- -- vim.notify('Error deleting ephemeral session: ' .. vim.inspect(err), vim.log.levels.WARN)
148- -- end)
143+ if config .debug .quick_chat and config .debug .quick_chat .keep_session then
144+ return
145+ end
146+
147+ state .api_client :delete_session (active_session .id ):catch (function (err )
148+ vim .notify (' Error deleting ephemeral session: ' .. vim .inspect (err ), vim .log .levels .WARN )
149+ end )
149150end )
150151
151152--- @param message string | nil The message to validate
@@ -167,135 +168,66 @@ local function validate_quick_chat_prerequisites(message)
167168end
168169
169170--- Creates context configuration for quick chat
171+ --- Optimized for minimal token usage while providing essential context
170172--- @param has_range boolean Whether a range is specified
171173--- @return OpencodeContextConfig context_opts
172174local function create_context_config (has_range )
173175 return {
174176 enabled = true ,
175- current_file = { enabled = false },
176- cursor_data = { enabled = not has_range },
177- selection = { enabled = has_range },
177+ current_file = { enabled = false }, -- Disable full file content
178+ cursor_data = { enabled = not has_range , context_lines = 10 }, -- Only cursor position when no selection
179+ selection = { enabled = has_range }, -- Only selected text when range provided
178180 diagnostics = {
179181 enabled = true ,
180182 error = true ,
181183 warning = true ,
182184 info = false ,
183- only_closest = has_range ,
185+ only_closest = true , -- Only closest diagnostics, not all file diagnostics
184186 },
185- agents = { enabled = false },
186- buffer = { enabled = true },
187- git_diff = { enabled = false },
187+ agents = { enabled = false }, -- No agent context needed
188+ buffer = { enabled = false }, -- Disable full buffer content for token efficiency
189+ git_diff = { enabled = false }, -- No git context needed
188190 }
189191end
190192
191193--- Generates instructions for the LLM to follow the SEARCH/REPLACE format
194+ --- This is inspired from Aider Chat approach
192195--- @param context_instance ContextInstance Context instance
193196--- @return string[] instructions Array of instruction lines
194- local function generate_search_replace_instructions (context_instance )
197+ local generate_search_replace_instructions = Promise . async ( function (context_instance )
195198 local base_instructions = {
196- ' # ROLE' ,
197- ' You are a precise code editing assistant. Your task is to modify code based on user instructions.' ,
198- ' ' ,
199- ' # OUTPUT FORMAT' ,
200- ' You MUST output ONLY in SEARCH/REPLACE blocks. No explanations, no markdown, no additional text.' ,
201- ' ' ,
199+ ' Output ONLY SEARCH/REPLACE blocks, no explanations:' ,
202200 ' <<<<<<< SEARCH' ,
203- ' [exact lines from the original code]' ,
201+ ' [exact original code]' ,
204202 ' =======' ,
205- ' [modified version of those lines ]' ,
203+ ' [modified code ]' ,
206204 ' >>>>>>> REPLACE' ,
207205 ' ' ,
208- ' # CRITICAL RULES' ,
209- ' 1. **Exact matching**: Copy SEARCH content EXACTLY character-for-character from the provided code' ,
210- ' 2. **Context lines**: Include 1-3 unchanged surrounding lines in SEARCH for unique identification' ,
211- ' 3. **Indentation**: Preserve the exact indentation from the original code' ,
212- ' 4. **Multiple changes**: Use separate SEARCH/REPLACE blocks for each distinct change' ,
213- ' 5. **No explanations**: Output ONLY the SEARCH/REPLACE blocks, nothing else' ,
214- ' ' ,
206+ ' Rules: Copy SEARCH content exactly. Include 1-3 context lines for unique matching. Use empty SEARCH to insert at cursor. Output multiple blocks only if needed for more complex operations.' ,
215207 }
216208
217- -- Add context-specific guidance
218209 local context_guidance = {}
219210
220- if context_instance :has (' diagnostics' ) then
221- table.insert (context_guidance , ' **DIAGNOSTICS context**: Use error/warning information to guide your fixes ' )
211+ if context_instance :has (' diagnostics' ): await () then
212+ table.insert (context_guidance , ' Fix errors/warnings (if asked) ' )
222213 end
223214
224- if context_instance :has (' selection' ) then
225- table.insert (context_guidance , ' **SELECTION context**: Only modify code within the selected range' )
215+ if context_instance :has (' selection' ): await () then
216+ table.insert (context_guidance , ' Modify only selected range' )
226217 elseif context_instance :has (' cursor_data' ) then
227- table.insert (context_guidance , ' **CURSOR context**: Focus modifications near the cursor position ' )
218+ table.insert (context_guidance , ' Modify only near cursor' )
228219 end
229220
230- if context_instance :has (' git_diff' ) then
231- table.insert (context_guidance , ' **GIT_DIFF context**: For reference only - never copy git diff syntax into SEARCH ' )
221+ if context_instance :has (' git_diff' ): await () then
222+ table.insert (context_guidance , " ONLY Reference git diff (don't copy syntax) " )
232223 end
233224
234225 if # context_guidance > 0 then
235- table.insert (base_instructions , ' # CONTEXT USAGE' )
236- for _ , guidance in ipairs (context_guidance ) do
237- table.insert (base_instructions , ' - ' .. guidance )
238- end
239- table.insert (base_instructions , ' ' )
240- end
241-
242- -- Add practical examples
243- local examples = {
244- ' # EXAMPLES' ,
245- ' ' ,
246- ' **Modify a return value:**' ,
247- ' <<<<<<< SEARCH' ,
248- ' function calculate()' ,
249- ' local result = x + y' ,
250- ' return result * 2' ,
251- ' end' ,
252- ' =======' ,
253- ' function calculate()' ,
254- ' local result = x + y' ,
255- ' return result * 3 -- Changed multiplier' ,
256- ' end' ,
257- ' >>>>>>> REPLACE' ,
258- ' ' ,
259- ' **Insert a new line:**' ,
260- ' <<<<<<< SEARCH' ,
261- ' local config = {' ,
262- ' timeout = 5000,' ,
263- ' }' ,
264- ' =======' ,
265- ' local config = {' ,
266- ' timeout = 5000,' ,
267- ' retry_count = 3,' ,
268- ' }' ,
269- ' >>>>>>> REPLACE' ,
270- ' ' ,
271- ' **Remove a line:**' ,
272- ' <<<<<<< SEARCH' ,
273- ' local debug_mode = true' ,
274- ' local verbose = true' ,
275- ' local silent = false' ,
276- ' =======' ,
277- ' local debug_mode = true' ,
278- ' local silent = false' ,
279- ' >>>>>>> REPLACE' ,
280- ' ' ,
281- ' **Insert new code at cursor (empty SEARCH):**' ,
282- ' When the cursor is on an empty line or you need to insert without replacing, use an empty SEARCH section:' ,
283- ' <<<<<<< SEARCH' ,
284- ' =======' ,
285- ' local new_variable = "inserted at cursor"' ,
286- ' >>>>>>> REPLACE' ,
287- ' ' ,
288- ' # FINAL REMINDER' ,
289- ' Output ONLY the SEARCH/REPLACE blocks. The SEARCH section must match the original code exactly.' ,
290- ' Use an empty SEARCH section to insert new code at the cursor position.' ,
291- }
292-
293- for _ , line in ipairs (examples ) do
294- table.insert (base_instructions , line )
226+ table.insert (base_instructions , ' Context: ' .. table.concat (context_guidance , ' , ' ) .. ' .' )
295227 end
296228
297229 return base_instructions
298- end
230+ end )
299231
300232--- Creates message parameters for quick chat
301233--- @param message string The user message
@@ -311,7 +243,7 @@ local create_message = Promise.async(function(message, buf, range, context_insta
311243 if quick_chat_config .instructions then
312244 instructions = quick_chat_config .instructions
313245 else
314- instructions = generate_search_replace_instructions (context_instance )
246+ instructions = generate_search_replace_instructions (context_instance ): await ()
315247 end
316248
317249 local format_opts = { buf = buf }
@@ -365,14 +297,9 @@ M.quick_chat = Promise.async(function(message, options, range)
365297 local row , col = cursor_pos [1 ] - 1 , cursor_pos [2 ] -- Convert to 0-indexed
366298 local spinner = CursorSpinner .new (buf , row , col )
367299
368- -- Create context instance for diagnostics and other context
369- local context_config = vim .tbl_deep_extend (' force' , create_context_config (range ~= nil ), options .context_config or {})
370- local context_instance = context .new_instance (context_config )
371-
372- -- Check prompt guard with the current file
373300 local file_name = vim .api .nvim_buf_get_name (buf )
374301 local mentioned_files = file_name ~= ' ' and { file_name } or {}
375- local allowed , err_msg = util .check_prompt_allowed (config .values . prompt_guard , mentioned_files )
302+ local allowed , err_msg = util .check_prompt_allowed (config .prompt_guard , mentioned_files )
376303 if not allowed then
377304 spinner :stop ()
378305 return Promise .new ():reject (err_msg or ' Prompt denied by prompt_guard' )
@@ -385,8 +312,9 @@ M.quick_chat = Promise.async(function(message, options, range)
385312 return Promise .new ():reject (' Failed to create ephemeral session' )
386313 end
387314
388- -- TODO only for debug
389- state .active_session = quick_chat_session
315+ if config .debug .quick_chat and config .debug .quick_chat .set_active_session then
316+ state .active_session = quick_chat_session
317+ end
390318
391319 running_sessions [quick_chat_session .id ] = {
392320 buf = buf ,
@@ -396,6 +324,8 @@ M.quick_chat = Promise.async(function(message, options, range)
396324 timestamp = vim .uv .now (),
397325 }
398326
327+ local context_config = vim .tbl_deep_extend (' force' , create_context_config (range ~= nil ), options .context_config or {})
328+ local context_instance = context .new_instance (context_config )
399329 local params = create_message (message , buf , range , context_instance , options ):await ()
400330
401331 local success , err = pcall (function ()
0 commit comments