@@ -141,11 +141,13 @@ local on_done = Promise.async(function(active_session)
141141 cleanup_session (running_session , active_session .id , ' Failed to update file with quick chat response' )
142142 end
143143
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)
144+ if config .debug .quick_chat and config .debug .quick_chat .keep_session then
145+ return
146+ end
147+
148+ state .api_client :delete_session (running_session .id ):catch (function (err )
149+ vim .notify (' Error deleting ephemeral session: ' .. vim .inspect (err ), vim .log .levels .WARN )
150+ end )
149151end )
150152
151153--- @param message string | nil The message to validate
@@ -167,135 +169,66 @@ local function validate_quick_chat_prerequisites(message)
167169end
168170
169171--- Creates context configuration for quick chat
172+ --- Optimized for minimal token usage while providing essential context
170173--- @param has_range boolean Whether a range is specified
171174--- @return OpencodeContextConfig context_opts
172175local function create_context_config (has_range )
173176 return {
174177 enabled = true ,
175- current_file = { enabled = false },
176- cursor_data = { enabled = not has_range },
177- selection = { enabled = has_range },
178+ current_file = { enabled = false }, -- Disable full file content
179+ cursor_data = { enabled = not has_range , context_lines = 10 }, -- Only cursor position when no selection
180+ selection = { enabled = has_range }, -- Only selected text when range provided
178181 diagnostics = {
179182 enabled = true ,
180183 error = true ,
181184 warning = true ,
182185 info = false ,
183- only_closest = has_range ,
186+ only_closest = true , -- Only closest diagnostics, not all file diagnostics
184187 },
185- agents = { enabled = false },
186- buffer = { enabled = true },
187- git_diff = { enabled = false },
188+ agents = { enabled = false }, -- No agent context needed
189+ buffer = { enabled = false }, -- Disable full buffer content for token efficiency
190+ git_diff = { enabled = false }, -- No git context needed
188191 }
189192end
190193
191194--- Generates instructions for the LLM to follow the SEARCH/REPLACE format
195+ --- This is inspired from Aider Chat approach
192196--- @param context_instance ContextInstance Context instance
193197--- @return string[] instructions Array of instruction lines
194- local function generate_search_replace_instructions (context_instance )
198+ local generate_search_replace_instructions = Promise . async ( function (context_instance )
195199 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- ' ' ,
200+ ' Output ONLY SEARCH/REPLACE blocks, no explanations:' ,
202201 ' <<<<<<< SEARCH' ,
203- ' [exact lines from the original code]' ,
202+ ' [exact original code]' ,
204203 ' =======' ,
205- ' [modified version of those lines ]' ,
204+ ' [modified code ]' ,
206205 ' >>>>>>> REPLACE' ,
207206 ' ' ,
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- ' ' ,
207+ ' 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.' ,
215208 }
216209
217- -- Add context-specific guidance
218210 local context_guidance = {}
219211
220- if context_instance :has (' diagnostics' ) then
221- table.insert (context_guidance , ' **DIAGNOSTICS context**: Use error/warning information to guide your fixes ' )
212+ if context_instance :has (' diagnostics' ): await () then
213+ table.insert (context_guidance , ' Fix errors/warnings (if asked) ' )
222214 end
223215
224- if context_instance :has (' selection' ) then
225- table.insert (context_guidance , ' **SELECTION context**: Only modify code within the selected range' )
216+ if context_instance :has (' selection' ): await () then
217+ table.insert (context_guidance , ' Modify only selected range' )
226218 elseif context_instance :has (' cursor_data' ) then
227- table.insert (context_guidance , ' **CURSOR context**: Focus modifications near the cursor position ' )
219+ table.insert (context_guidance , ' Modify only near cursor' )
228220 end
229221
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 ' )
222+ if context_instance :has (' git_diff' ): await () then
223+ table.insert (context_guidance , " ONLY Reference git diff (don't copy syntax) " )
232224 end
233225
234226 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 )
227+ table.insert (base_instructions , ' Context: ' .. table.concat (context_guidance , ' , ' ) .. ' .' )
295228 end
296229
297230 return base_instructions
298- end
231+ end )
299232
300233--- Creates message parameters for quick chat
301234--- @param message string The user message
@@ -311,7 +244,7 @@ local create_message = Promise.async(function(message, buf, range, context_insta
311244 if quick_chat_config .instructions then
312245 instructions = quick_chat_config .instructions
313246 else
314- instructions = generate_search_replace_instructions (context_instance )
247+ instructions = generate_search_replace_instructions (context_instance ): await ()
315248 end
316249
317250 local format_opts = { buf = buf }
@@ -365,11 +298,6 @@ M.quick_chat = Promise.async(function(message, options, range)
365298 local row , col = cursor_pos [1 ] - 1 , cursor_pos [2 ] -- Convert to 0-indexed
366299 local spinner = CursorSpinner .new (buf , row , col )
367300
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
373301 local file_name = vim .api .nvim_buf_get_name (buf )
374302 local mentioned_files = file_name ~= ' ' and { file_name } or {}
375303 local allowed , err_msg = util .check_prompt_allowed (config .values .prompt_guard , mentioned_files )
@@ -385,8 +313,9 @@ M.quick_chat = Promise.async(function(message, options, range)
385313 return Promise .new ():reject (' Failed to create ephemeral session' )
386314 end
387315
388- -- TODO only for debug
389- state .active_session = quick_chat_session
316+ if config .debug .quick_chat and config .debug .quick_chat .set_active_session then
317+ state .active_session = quick_chat_session
318+ end
390319
391320 running_sessions [quick_chat_session .id ] = {
392321 buf = buf ,
@@ -396,6 +325,8 @@ M.quick_chat = Promise.async(function(message, options, range)
396325 timestamp = vim .uv .now (),
397326 }
398327
328+ local context_config = vim .tbl_deep_extend (' force' , create_context_config (range ~= nil ), options .context_config or {})
329+ local context_instance = context .new_instance (context_config )
399330 local params = create_message (message , buf , range , context_instance , options ):await ()
400331
401332 local success , err = pcall (function ()
0 commit comments