From 4ffec5a03948a8eb6de41f0b513d61e747c5a136 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Sun, 9 Feb 2025 01:39:00 +0800
Subject: [PATCH 01/39] fix: search/replace bad case (#1223)

---
 lua/avante/sidebar.lua | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index c8c5a3c..c372a9f 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -225,11 +225,17 @@ local function transform_result_content(selected_files, result_content, prev_fil
       if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
       search_start = i + 1
       last_search_tag_start_line = i
-    elseif line_content == "</SEARCH>" then
+    elseif line_content:match("</SEARCH>") then
       is_searching = false
 
       local search_end = i
 
+      -- Handle case where </SEARCH> is a suffix
+      if not line_content:match("^%s*</SEARCH>%s*$") then
+        local search_end_line = line_content:match("^(.+)</SEARCH>")
+        result_lines[i] = search_end_line
+      end
+
       local prev_line = result_lines[i - 1]
       if prev_line and prev_line:match("^%s*```$") then search_end = i - 1 end
 
@@ -317,8 +323,13 @@ local function transform_result_content(selected_files, result_content, prev_fil
       if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
       last_replace_tag_start_line = i
       goto continue
-    elseif line_content == "</REPLACE>" then
+    elseif line_content:match("</REPLACE>") then
       is_replacing = false
+      -- Handle case where </REPLACE> is a suffix
+      if not line_content:match("^%s*</REPLACE>%s*$") then
+        local replace_end_line = line_content:match("^(.+)</REPLACE>")
+        if replace_end_line and replace_end_line ~= "" then table.insert(transformed_lines, replace_end_line) end
+      end
       local prev_line = result_lines[i - 1]
       if not (prev_line and prev_line:match("^%s*```$")) then table.insert(transformed_lines, "```") end
       goto continue

From 987275c64a95a50ac9fa0946ffc8de00c452281f Mon Sep 17 00:00:00 2001
From: Daniel Tabuenca <dtabuenc@gmail.com>
Date: Sat, 8 Feb 2025 11:40:20 -0600
Subject: [PATCH 02/39] feat: bedrock temporary credentials support (#1207)

---
 README.md                        | 4 +++-
 lua/avante/providers/bedrock.lua | 3 +++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 18ab5c1..a9979de 100644
--- a/README.md
+++ b/README.md
@@ -471,8 +471,10 @@ Given its early stage, `avante.nvim` currently supports the following basic func
 > For Amazon Bedrock:
 >
 > ```sh
-> export BEDROCK_KEYS=aws_access_key_id,aws_secret_access_key,aws_region
+> export BEDROCK_KEYS=aws_access_key_id,aws_secret_access_key,aws_region[,aws_session_token]
+>
 > ```
+> Note: The aws_session_token is optional and only needed when using temporary AWS credentials
 
 1. Open a code file in Neovim.
 2. Use the `:AvanteAsk` command to query the AI about the code.
diff --git a/lua/avante/providers/bedrock.lua b/lua/avante/providers/bedrock.lua
index 2b0cd7e..49720f3 100644
--- a/lua/avante/providers/bedrock.lua
+++ b/lua/avante/providers/bedrock.lua
@@ -64,6 +64,7 @@ M.parse_curl_args = function(provider, prompt_opts)
   local aws_access_key_id = parts[1]
   local aws_secret_access_key = parts[2]
   local aws_region = parts[3]
+  local aws_session_token = parts[4]
 
   local endpoint = string.format(
     "https://bedrock-runtime.%s.amazonaws.com/model/%s/invoke-with-response-stream",
@@ -75,6 +76,8 @@ M.parse_curl_args = function(provider, prompt_opts)
     ["Content-Type"] = "application/json",
   }
 
+  if aws_session_token and aws_session_token ~= "" then headers["x-amz-security-token"] = aws_session_token end
+
   local body_payload = M.build_bedrock_payload(prompt_opts, body_opts)
 
   local rawArgs = {

From 26e40e5483db90c593bf09466bc6cbe5a7711381 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Sun, 9 Feb 2025 01:56:59 +0800
Subject: [PATCH 03/39] fix: apply non-existent file (#1224)

---
 lua/avante/sidebar.lua | 38 +++++++++++++++++++++-----------------
 1 file changed, 21 insertions(+), 17 deletions(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index c372a9f..d56c223 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -254,24 +254,28 @@ local function transform_result_content(selected_files, result_content, prev_fil
 
       if not the_matched_file then
         if not PPath:new(filepath):exists() then
-          Utils.warn("File not found: " .. filepath)
-          goto continue
+          the_matched_file = {
+            filepath = filepath,
+            content = "",
+            file_type = nil,
+          }
+        else
+          if not PPath:new(filepath):is_file() then
+            Utils.warn("Not a file: " .. filepath)
+            goto continue
+          end
+          local lines = Utils.read_file_from_buf_or_disk(filepath)
+          if lines == nil then
+            Utils.warn("Failed to read file: " .. filepath)
+            goto continue
+          end
+          local content = table.concat(lines, "\n")
+          the_matched_file = {
+            filepath = filepath,
+            content = content,
+            file_type = nil,
+          }
         end
-        if not PPath:new(filepath):is_file() then
-          Utils.warn("Not a file: " .. filepath)
-          goto continue
-        end
-        local lines = Utils.read_file_from_buf_or_disk(filepath)
-        if lines == nil then
-          Utils.warn("Failed to read file: " .. filepath)
-          goto continue
-        end
-        local content = table.concat(lines, "\n")
-        the_matched_file = {
-          filepath = filepath,
-          content = content,
-          file_type = nil,
-        }
       end
 
       local file_content = vim.split(the_matched_file.content, "\n")

From 59fdbd72a450536195b4fd2eca6a1fd96bc86387 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Mon, 10 Feb 2025 02:08:58 +0800
Subject: [PATCH 04/39] fix: get abs path (#1232)

---
 lua/avante/llm_tools.lua | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lua/avante/llm_tools.lua b/lua/avante/llm_tools.lua
index 9d75346..b2b0dce 100644
--- a/lua/avante/llm_tools.lua
+++ b/lua/avante/llm_tools.lua
@@ -7,6 +7,7 @@ local M = {}
 ---@param rel_path string
 ---@return string
 local function get_abs_path(rel_path)
+  if Path:new(rel_path):is_absolute() then return rel_path end
   local project_root = Utils.get_project_root()
   return Path:new(project_root):joinpath(rel_path):absolute()
 end

From c07d396e30f1705bd28404bfd733a2313a998471 Mon Sep 17 00:00:00 2001
From: Amin Roosta <2920178+aminroosta@users.noreply.github.com>
Date: Mon, 10 Feb 2025 00:21:55 -0500
Subject: [PATCH 05/39] feat(web_search_engine): adds google as a provider
 (#1226)

---
 README.md                |  7 ++++---
 lua/avante/config.lua    | 27 ++++++++++++++++++++++++++-
 lua/avante/llm_tools.lua | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index a9979de..f9d79ba 100644
--- a/README.md
+++ b/README.md
@@ -550,15 +550,16 @@ For more information, see [Custom Providers](https://github.com/yetone/avante.nv
 
 ## Web Search Engines
 
-Avante's tools include some web search engines, currently support [tavily](https://tavily.com/) and [serpapi](https://serpapi.com/). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
+Avante's tools include some web search engines, currently support [tavily](https://tavily.com/), [serpapi](https://serpapi.com/) and google's [programmable search engine](https://developers.google.com/custom-search/v1/overview). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
 
 ```lua
 web_search_engine = {
-  provider = "tavily", -- tavily or serpapi
+  provider = "tavily", -- tavily, serpapi or google
 }
 ```
 
-You need to set the environment variable `TAVILY_API_KEY` or `SERPAPI_API_KEY` to use tavily or serpapi.
+You need to set the environment variable `TAVILY_API_KEY` , `SERPAPI_API_KEY` to use tavily or serpapi.
+To use google, set the `GOOGLE_SEARCH_API_KEY` as the [API key](https://developers.google.com/custom-search/v1/overview), and `GOOGLE_SEARCH_ENGINE_ID` as the [search engine](https://programmablesearchengine.google.com) ID.
 
 ## Disable Tools
 
diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 123ac11..0364d02 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -55,8 +55,33 @@ M._defaults = {
                   }
                 end
               )
+              :take(5)
+              :totable()
+            return vim.json.encode(jsn), nil
+          end
+          return "", nil
+        end,
+      },
+      google = {
+        api_key_name = "GOOGLE_SEARCH_API_KEY",
+        engine_id_name = "GOOGLE_SEARCH_ENGINE_ID",
+        extra_request_body = {},
+        ---@type WebSearchEngineProviderResponseBodyFormatter
+        format_response_body = function(body)
+          if body.items ~= nil then
+            local jsn = vim
+              .iter(body.items)
+              :map(
+                function(result)
+                  return {
+                    title = result.title,
+                    link = result.link,
+                    snippet = result.snippet,
+                  }
+                end
+              )
+              :take(5)
               :totable()
-            if #jsn > 5 then jsn = vim.list_slice(jsn, 1, 5) end
             return vim.json.encode(jsn), nil
           end
           return "", nil
diff --git a/lua/avante/llm_tools.lua b/lua/avante/llm_tools.lua
index b2b0dce..5117a05 100644
--- a/lua/avante/llm_tools.lua
+++ b/lua/avante/llm_tools.lua
@@ -327,6 +327,28 @@ function M.web_search(opts, on_log)
     if resp.status ~= 200 then return nil, "Error: " .. resp.body end
     local jsn = vim.json.decode(resp.body)
     return search_engine.format_response_body(jsn)
+  elseif provider_type == "google" then
+    local engine_id = os.getenv(search_engine.engine_id_name)
+    if engine_id == nil or engine_id == "" then
+      return nil, "Environment variable " .. search_engine.engine_id_namee .. " is not set"
+    end
+    local query_params = vim.tbl_deep_extend("force", {
+      key = api_key,
+      cx = engine_id,
+      q = opts.query,
+    }, search_engine.extra_request_body)
+    local query_string = ""
+    for key, value in pairs(query_params) do
+      query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
+    end
+    local resp = curl.get("https://www.googleapis.com/customsearch/v1?" .. query_string, {
+      headers = {
+        ["Content-Type"] = "application/json",
+      },
+    })
+    if resp.status ~= 200 then return nil, "Error: " .. resp.body end
+    local jsn = vim.json.decode(resp.body)
+    return search_engine.format_response_body(jsn)
   end
 end
 

From 6e18616c1606a8372164bee844129e3666f8947c Mon Sep 17 00:00:00 2001
From: Omar Crespo <crespomerchano@gmail.com>
Date: Mon, 10 Feb 2025 12:40:49 -0500
Subject: [PATCH 06/39] fix: dual boost only in planning mode (#1215)

---
 lua/avante/llm.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lua/avante/llm.lua b/lua/avante/llm.lua
index 522d54b..c657c74 100644
--- a/lua/avante/llm.lua
+++ b/lua/avante/llm.lua
@@ -449,7 +449,7 @@ M.stream = function(opts)
       return original_on_stop(stop_opts)
     end)
   end
-  if Config.dual_boost.enabled then
+  if Config.dual_boost.enabled and opts.mode == "planning" then
     M._dual_boost_stream(opts, P[Config.dual_boost.first_provider], P[Config.dual_boost.second_provider])
   else
     M._stream(opts)

From cc463861300c593f3ff45ddcf26cbdc4ba791881 Mon Sep 17 00:00:00 2001
From: Oliver Lorton <oliverlorton@gmail.com>
Date: Mon, 10 Feb 2025 17:43:01 +0000
Subject: [PATCH 07/39] fix: when dev icons absent (#1214)

* Add helper function to check for dev icon availability

* Add function to display dev icon or nothing if icon plugins unavailable

* Fix existing use of icons

* Reformat with stylua
---
 lua/avante/health.lua     |  4 +---
 lua/avante/sidebar.lua    | 23 ++++++++++++++---------
 lua/avante/utils/init.lua | 18 ++++++++++++++++++
 3 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/lua/avante/health.lua b/lua/avante/health.lua
index 357db84..78804ac 100644
--- a/lua/avante/health.lua
+++ b/lua/avante/health.lua
@@ -23,9 +23,7 @@ M.check = function()
   end
 
   -- Optional dependencies
-  local has_devicons = Utils.has("nvim-web-devicons")
-  local has_mini_icons = Utils.has("mini.icons") or Utils.has("mini.nvim")
-  if has_devicons or has_mini_icons then
+  if Utils.icons_enabled() then
     H.ok("Found icons plugin (nvim-web-devicons or mini.icons)")
   else
     H.warn("No icons plugin found (nvim-web-devicons or mini.icons). Icons will not be displayed")
diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index d56c223..815529e 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -412,8 +412,8 @@ local function get_searching_hint()
 end
 
 local thinking_spinner_chars = {
-  "🤯",
-  "🙄",
+  Utils.icon("🤯", "?"),
+  Utils.icon("🙄", "¿"),
 }
 local thinking_spinner_index = 1
 
@@ -452,8 +452,10 @@ local function generate_display_content(replacement)
         return string.format("  > %s", line)
       end)
       :totable()
-    local result_lines =
-      vim.list_extend(vim.list_slice(lines, 1, replacement.last_search_tag_start_line), { "🤔 Thought content:" })
+    local result_lines = vim.list_extend(
+      vim.list_slice(lines, 1, replacement.last_search_tag_start_line),
+      { Utils.icon("🤔 ") .. "Thought content:" }
+    )
     result_lines = vim.list_extend(result_lines, formatted_thinking_content_lines)
     result_lines = vim.list_extend(result_lines, vim.list_slice(lines, last_think_tag_end_line + 1))
     return table.concat(result_lines, "\n")
@@ -860,7 +862,7 @@ function Sidebar:render_result()
   then
     return
   end
-  local header_text = "ó°­» Avante"
+  local header_text = Utils.icon("ó°­» ") .. "Avante"
   self:render_header(
     self.result_container.winid,
     self.result_container.bufnr,
@@ -882,13 +884,15 @@ function Sidebar:render_input(ask)
   end
 
   local header_text = string.format(
-    "󱜸 %s (" .. Config.mappings.sidebar.switch_windows .. ": switch focus)",
+    "%s%s (" .. Config.mappings.sidebar.switch_windows .. ": switch focus)",
+    Utils.icon("󱜸 "),
     ask and "Ask" or "Chat with"
   )
 
   if self.code.selection ~= nil then
     header_text = string.format(
-      "󱜸 %s (%d:%d) (<Tab>: switch focus)",
+      "%s%s (%d:%d) (<Tab>: switch focus)",
+      Utils.icon("󱜸 "),
       ask and "Ask" or "Chat with",
       self.code.selection.range.start.lnum,
       self.code.selection.range.finish.lnum
@@ -921,7 +925,8 @@ function Sidebar:render_selected_code()
     selected_code_lines_count = #selected_code_lines
   end
 
-  local header_text = "îž– Selected Code"
+  local header_text = Utils.icon("îž– ")
+    .. "Selected Code"
     .. (
       selected_code_lines_count > selected_code_max_lines_count
         and " (Show only the first " .. tostring(selected_code_max_lines_count) .. " lines)"
@@ -2329,7 +2334,7 @@ function Sidebar:create_selected_files_container()
     self:render_header(
       self.selected_files_container.winid,
       selected_files_buf,
-      " Selected Files",
+      Utils.icon(" ") .. "Selected Files",
       Highlights.SUBTITLE,
       Highlights.REVERSED_SUBTITLE
     )
diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua
index 8921c05..ca783cb 100644
--- a/lua/avante/utils/init.lua
+++ b/lua/avante/utils/init.lua
@@ -918,4 +918,22 @@ function M.read_file_from_buf_or_disk(file_path)
   end
 end
 
+---Check if an icon plugin is installed
+---@return boolean
+M.icons_enabled = function() return M.has("nvim-web-devicons") or M.has("mini.icons") or M.has("mini.nvim") end
+
+---Display an string with icon, if an icon plugin is available.
+---Dev icons are an optional install for avante, this function prevents ugly chars
+---being displayed by displaying fallback options or nothing at all.
+---@param string_with_icon string
+---@param utf8_fallback string|nil
+---@return string
+M.icon = function(string_with_icon, utf8_fallback)
+  if M.icons_enabled() then
+    return string_with_icon
+  else
+    return utf8_fallback or ""
+  end
+end
+
 return M

From 63136fd92f2f2e9cf91b231dc19ac2c95e3897ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20M=C3=BCller?= <tom@94.me>
Date: Mon, 10 Feb 2025 19:08:58 +0100
Subject: [PATCH 08/39] fix(suggestions): make history more faithful to future
 input (#1210)

* fix(suggestions): make history more faithful to future input

* fix(suggestions): put cursor to the end of line in suggestion history
---
 lua/avante/suggestion.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lua/avante/suggestion.lua b/lua/avante/suggestion.lua
index 37c521b..10451f3 100644
--- a/lua/avante/suggestion.lua
+++ b/lua/avante/suggestion.lua
@@ -84,8 +84,8 @@ function Suggestion:suggest()
 L1: def fib
 L2:
 L3: if __name__ == "__main__":
-L4:    # just pass
-L5:    pass
+L4:     # just pass
+L5:     pass
 </code>
       ]],
     },
@@ -95,7 +95,7 @@ L5:    pass
     },
     {
       role = "user",
-      content = '<question>{ "indentSize": 4, "position": { "row": 1, "col": 2 } }</question>',
+      content = '<question>{"insertSpaces":true,"tabSize":4,"indentSize":4,"position":{"row":1,"col":7}}</question>',
     },
     {
       role = "assistant",

From 8d82abe04358515d4bc8385acc6c5847befb717e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20M=C3=BCller?= <git@tom94.net>
Date: Tue, 11 Feb 2025 04:35:28 +0100
Subject: [PATCH 09/39] feat: expose whether the llm is currently generating
 content (#1181)

---
 lua/avante/sidebar.lua | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 815529e..38a5a85 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -57,6 +57,7 @@ function Sidebar:new(id)
     selected_files_container = nil,
     input_container = nil,
     file_selector = FileSelector:new(id),
+    is_generating = false,
   }, { __index = self })
 end
 
@@ -1836,6 +1837,8 @@ function Sidebar:create_input_container(opts)
 
     ---@type AvanteLLMChunkCallback
     local on_chunk = function(chunk)
+      self.is_generating = true
+
       original_response = original_response .. chunk
 
       local selected_files = self.file_selector:get_selected_files_contents()
@@ -1870,6 +1873,8 @@ function Sidebar:create_input_container(opts)
 
     ---@type AvanteLLMStopCallback
     local on_stop = function(stop_opts)
+      self.is_generating = false
+
       pcall(function()
         ---remove keymaps
         vim.keymap.del("n", "j", { buffer = self.result_container.bufnr })

From a6ae8ad4b7fb142759820a57b08bc49087e2da38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20M=C3=BCller?= <git@tom94.net>
Date: Tue, 11 Feb 2025 04:36:19 +0100
Subject: [PATCH 10/39] fix(sidebar): reset cursor to beginning of prompt on
 submit (#1213)

This fix is required for users with vim.o.virtualedit="all". Otherwise,
the cursor remains at the end of the prompt and has to be manually moved
back before entering the next prompt.
---
 lua/avante/sidebar.lua | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 38a5a85..a450dac 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -1988,6 +1988,7 @@ function Sidebar:create_input_container(opts)
     local request = table.concat(lines, "\n")
     if request == "" then return end
     api.nvim_buf_set_lines(self.input_container.bufnr, 0, -1, false, {})
+    api.nvim_win_set_cursor(self.input_container.winid, { 1, 0 })
     handle_submit(request)
   end
 

From 3f8b6d07ba086fa6a2aa6832d73933b934b92ee0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20M=C3=BCller?= <git@tom94.net>
Date: Tue, 11 Feb 2025 04:36:38 +0100
Subject: [PATCH 11/39] feat(sidebar): add member function to check whether the
 sidebar is focused (#1216)

---
 lua/avante/sidebar.lua | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index a450dac..f70f6d2 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -1257,6 +1257,20 @@ function Sidebar:initialize()
   return self
 end
 
+function Sidebar:is_focused()
+  if not self:is_open() then return false end
+
+  local current_winid = api.nvim_get_current_win()
+  if self.winids.result_container and self.winids.result_container == current_winid then return true end
+  if self.winids.selected_files_container and self.winids.selected_files_container == current_winid then
+    return true
+  end
+  if self.winids.selected_code_container and self.winids.selected_code_container == current_winid then return true end
+  if self.winids.input_container and self.winids.input_container == current_winid then return true end
+
+  return false
+end
+
 function Sidebar:is_focused_on_result()
   return self:is_open() and self.result_container and self.result_container.winid == api.nvim_get_current_win()
 end

From 62c29b3d0c313d61d08da45c73c59c8ebf242027 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Tue, 11 Feb 2025 12:11:16 +0800
Subject: [PATCH 12/39] fix: incorrect replace end tag (#1240)

---
 lua/avante/sidebar.lua | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index f70f6d2..c16750b 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -227,6 +227,10 @@ local function transform_result_content(selected_files, result_content, prev_fil
       search_start = i + 1
       last_search_tag_start_line = i
     elseif line_content:match("</SEARCH>") then
+      if is_replacing then
+        result_lines[i] = "</REPLACE>"
+        goto continue_without_increment
+      end
       is_searching = false
 
       local search_end = i
@@ -348,6 +352,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
     table.insert(transformed_lines, line_content)
     ::continue::
     i = i + 1
+    ::continue_without_increment::
   end
 
   return {

From 0bfbd39ebc1815eecc1b9b6ac062dd3278609dc3 Mon Sep 17 00:00:00 2001
From: synic <arolsen@gmail.com>
Date: Mon, 10 Feb 2025 22:42:23 -0700
Subject: [PATCH 13/39] fix(sidebar): fix various focus errors (#1237)

This fixes a bunch of errors I was getting when switching away from the
avante sidebar and then back. Sometimes the sidebar would lose track of
the various windows, and you'd get errors like this:

```
|| stack traceback:
|| 	...local/share/nvim/lazy/avante.nvim/lua/avante/sidebar.lua:2350: in function <...local/share/nvim/lazy/avante.nvim/lua/avante/sidebar.lua:2349>
|| Error detected while processing BufLeave Autocommands for "<buffer=294>":
|| Error executing lua callback: ...local/share/nvim/lazy/avante.nvim/lua/avante/sidebar.lua:2398: attempt to index field 'selected_files_container' (a nil value)
```

Or this one:

```
|| stack traceback:
|| 	...local/share/nvim/lazy/avante.nvim/lua/avante/sidebar.lua:2350: in function <...local/share/nvim/lazy/avante.nvim/lua/avante/sidebar.lua:2349>
|| Error detected while processing BufLeave Autocommands for "<buffer=294>":
|| Error executing lua callback: ...local/share/nvim/lazy/avante.nvim/lua/avante/sidebar.lua:2398: attempt to index field 'selected_files_container' (a nil value)
```

This change does the following:

1. Proper order of cleanup in `reset()`
2. Proper cleanup in `create_selected_files_container()`
---
 lua/avante/sidebar.lua | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index c16750b..f7e4565 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -67,9 +67,24 @@ function Sidebar:delete_autocmds()
 end
 
 function Sidebar:reset()
+  -- clean up event handlers
+  if self.augroup then
+    api.nvim_del_augroup_by_id(self.augroup)
+    self.augroup = nil
+  end
+
+  -- clean up keymaps
   self:unbind_apply_key()
   self:unbind_sidebar_keys()
-  self:delete_autocmds()
+
+  -- clean up file selector events
+  if self.file_selector then self.file_selector:off("update") end
+
+  if self.result_container then self.result_container:unmount() end
+  if self.selected_code_container then self.selected_code_container:unmount() end
+  if self.selected_files_container then self.selected_files_container:unmount() end
+  if self.input_container then self.input_container:unmount() end
+
   self.code = { bufnr = 0, winid = 0, selection = nil }
   self.winids =
     { result_container = 0, selected_files_container = 0, selected_code_container = 0, input_container = 0 }

From 6d116fac36f7e82190c7dcf9a1d0b7199306ace5 Mon Sep 17 00:00:00 2001
From: guanghechen <42513619+guanghechen@users.noreply.github.com>
Date: Tue, 11 Feb 2025 13:49:00 +0800
Subject: [PATCH 14/39] feat(file_selector): support customized file selector
 method (#1204)

---
 lua/avante/config.lua        | 7 ++++++-
 lua/avante/file_selector.lua | 5 +++++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 0364d02..85e0771 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -5,6 +5,11 @@
 
 local Utils = require("avante.utils")
 
+---@class avante.file_selector.IParams
+---@field public title      string
+---@field public filepaths  string[]
+---@field public handler    fun(filepaths: string[]|nil): nil
+
 ---@class avante.CoreConfig: avante.Config
 local M = {}
 ---@class avante.Config
@@ -327,7 +332,7 @@ M._defaults = {
   },
   --- @class AvanteFileSelectorConfig
   file_selector = {
-    --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string
+    --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams): nil
     provider = "native",
     -- Options override for custom providers
     provider_opts = {},
diff --git a/lua/avante/file_selector.lua b/lua/avante/file_selector.lua
index 4af6ac6..2b703d1 100644
--- a/lua/avante/file_selector.lua
+++ b/lua/avante/file_selector.lua
@@ -330,6 +330,11 @@ function FileSelector:show_select_ui()
       self:snacks_picker_ui(handler)
     elseif Config.file_selector.provider == "telescope" then
       self:telescope_ui(handler)
+    elseif type(Config.file_selector.provider) == "function" then
+      local title = string.format("%s:", PROMPT_TITLE) ---@type string
+      local filepaths = self:get_filepaths() ---@type string[]
+      local params = { title = title, filepaths = filepaths, handler = handler } ---@type avante.file_selector.IParams
+      Config.file_selector.provider(params)
     else
       Utils.error("Unknown file selector provider: " .. Config.file_selector.provider)
     end

From cdacfce90134f02a9e63faaa63736ae05223d1b0 Mon Sep 17 00:00:00 2001
From: b9Joker108 <147242971+b9Joker108@users.noreply.github.com>
Date: Tue, 11 Feb 2025 16:50:26 +1100
Subject: [PATCH 15/39] Update Cargo.toml with mlua dependency and features for
 successful build as per my manual build on an aarch64 proot-distro alias of
 Debian in an unrooted Termux host. NOTE: This is soon to be my first official
 OS commit and soon to be my first official pull request. (#898)

---
 Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index b442446..0334b54 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@ minijinja = { version = "2.4.0", features = [
   "custom_syntax",
   "loop_controls",
 ] }
-mlua = { version = "0.10.0", features = ["module", "serialize"] }
+mlua = { version = "0.10.0", features = ["lua54", "module", "serialize"], default-features = false }
 tiktoken-rs = { version = "0.6.0" }
 tokenizers = { version = "0.20.0", features = [
   "esaxx_fast",

From b73b3d7888695052a99c0a50daa1ff07cfdedb1b Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Tue, 11 Feb 2025 15:54:57 +0800
Subject: [PATCH 16/39] fix: file not found (#1243)

---
 lua/avante/sidebar.lua | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index f7e4565..c72df95 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -740,8 +740,7 @@ function Sidebar:minimize_snippets(snippets_map)
 
   if vim.tbl_count(snippets_map) > 0 then
     local filepaths = vim.tbl_keys(snippets_map)
-    local original_lines_, _, err = Utils.read_file_from_buf_or_disk(filepaths[1])
-    if err ~= nil then return {} end
+    local original_lines_ = Utils.read_file_from_buf_or_disk(filepaths[1])
     if original_lines_ then original_lines = original_lines_ end
   end
 

From 3305bdb76911f4388a8157f9988b02d26336ab4d Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Tue, 11 Feb 2025 15:56:11 +0800
Subject: [PATCH 17/39] =?UTF-8?q?Revert=20"Update=20Cargo.toml=20with=20ml?=
 =?UTF-8?q?ua=20dependency=20and=20features=20for=20successful=20bu?=
 =?UTF-8?q?=E2=80=A6"=20(#1245)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit cdacfce90134f02a9e63faaa63736ae05223d1b0.
---
 Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index 0334b54..b442446 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@ minijinja = { version = "2.4.0", features = [
   "custom_syntax",
   "loop_controls",
 ] }
-mlua = { version = "0.10.0", features = ["lua54", "module", "serialize"], default-features = false }
+mlua = { version = "0.10.0", features = ["module", "serialize"] }
 tiktoken-rs = { version = "0.6.0" }
 tokenizers = { version = "0.20.0", features = [
   "esaxx_fast",

From 31c170166038824c1b77eafa03df410bf99d807d Mon Sep 17 00:00:00 2001
From: guanghechen <42513619+guanghechen@users.noreply.github.com>
Date: Tue, 11 Feb 2025 15:57:15 +0800
Subject: [PATCH 18/39] :memo: docs(README): update customized file_selector
 example (#1242)

* :memo: docs(README): update customized file_selector example

* fix types
---
 README.md             | 34 +++++++++++++++++++++++++++++++++-
 lua/avante/config.lua |  2 +-
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index f9d79ba..364f066 100644
--- a/README.md
+++ b/README.md
@@ -380,12 +380,44 @@ This is achieved by emulating nvim-cmp using blink.compat
 
 ```lua
       file_selector = {
-        --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string
+        --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
         provider = "fzf",
         -- Options override for custom providers
         provider_opts = {},
       }
 ```
+
+To create a customized file_selector, you can specify a customized function to launch a picker to select items and pass the selected items to the `handler` callback.
+
+```lua
+      file_selector = {
+        ---@param params avante.file_selector.IParams
+        provider = function(params)
+          local filepaths = params.filepaths ---@type string[]
+          local title = params.title ---@type string
+          local handler = params.handler ---@type fun(selected_filepaths: string[]|nil): nil
+
+          -- Launch your customized picker with the items built from `filepaths`, then in the `on_confirm` callback,
+          -- pass the selected items (convert back to file paths) to the `handler` function.
+
+          local items = __your_items_formatter__(filepaths)
+          __your_picker__({
+            items = items,
+            on_cancel = function()
+              handler(nil)
+            end,
+            on_confirm = function(selected_items)
+              local selected_filepaths = {}
+              for _, item in ipairs(selected_items) do
+                table.insert(selected_filepaths, item.filepath)
+              end
+              handler(selected_filepaths)
+            end
+          })
+        end,
+      }
+```
+
 Choose a selector other that native, the default as that currently has an issue
 For lazyvim users copy the full config for blink.cmp from the website or extend the options
 ```lua
diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 85e0771..b498966 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -332,7 +332,7 @@ M._defaults = {
   },
   --- @class AvanteFileSelectorConfig
   file_selector = {
-    --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams): nil
+    --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
     provider = "native",
     -- Options override for custom providers
     provider_opts = {},

From f660350cdc326d14290585b0d0335eb8d857cbd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20M=C3=BCller?= <git@tom94.net>
Date: Tue, 11 Feb 2025 08:57:38 +0100
Subject: [PATCH 19/39] fix(tavily): remove time restriction of 1 day (#1241)

---
 lua/avante/config.lua | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index b498966..8791469 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -33,7 +33,6 @@ M._defaults = {
       tavily = {
         api_key_name = "TAVILY_API_KEY",
         extra_request_body = {
-          time_range = "d",
           include_answer = "basic",
         },
         ---@type WebSearchEngineProviderResponseBodyFormatter

From 72edea97cbd2429ff1df39343f592cccd4d692ba Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Tue, 11 Feb 2025 23:11:03 +0800
Subject: [PATCH 20/39] optimize: prompts (#1247)

---
 lua/avante/templates/base.avanterules | 13 +++++++++----
 lua/avante/utils/init.lua             |  2 +-
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/lua/avante/templates/base.avanterules b/lua/avante/templates/base.avanterules
index 06a2efe..9598c92 100644
--- a/lua/avante/templates/base.avanterules
+++ b/lua/avante/templates/base.avanterules
@@ -10,10 +10,15 @@
 Act as an expert software developer.
 Always use best practices when coding.
 Respect and use existing conventions, libraries, etc that are already present in the code base.
-You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.
-If you encounter a URL, prioritize using the fetch tool to obtain its content.
-If you have information that you don't know, please proactively use the tools provided by users! Especially the web search tool.
-When available tools cannot meet the requirements, please try to use the `run_command` tool to solve the problem whenever possible.
+
+Tools Usage Guide:
+  - You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.
+  - If you encounter a URL, prioritize using the fetch tool to obtain its content.
+  - If you have information that you don't know, please proactively use the tools provided by users! Especially the web search tool.
+  - When available tools cannot meet the requirements, please try to use the `run_command` tool to solve the problem whenever possible.
+  - When attempting to modify a file that is not in the context, please first use the `list_files` tool and `search_files` tool to check if the file you want to modify exists, then use the `read_file` tool to read the file content. Don't modify blindly!
+  - When generating files, first use `list_files` tool to read the directory structure, don't generate blindly!
+  - When creating files, first check if the directory exists. If it doesn't exist, create the directory before creating the file.
 
 {% if system_info -%}
 Use the appropriate shell based on the user's system info:
diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua
index ca783cb..7c1bf2d 100644
--- a/lua/avante/utils/init.lua
+++ b/lua/avante/utils/init.lua
@@ -913,7 +913,7 @@ function M.read_file_from_buf_or_disk(file_path)
     local file_type = vim.filetype.match({ filename = file_path, contents = { content } }) or "unknown"
     return vim.split(content, "\n"), file_type, nil
   else
-    M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
+    -- M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
     return {}, nil, open_err
   end
 end

From 456f7ccdab6fa977fe10458e02d6226c7f8f33c0 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Tue, 11 Feb 2025 23:49:37 +0800
Subject: [PATCH 21/39] fix: search/replace tag bad cases (#1248)

---
 lua/avante/sidebar.lua | 52 ++++++++++++++++++++++++++++++++----------
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index c72df95..d0c2dfc 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -216,7 +216,8 @@ local function transform_result_content(selected_files, result_content, prev_fil
   local current_filepath
 
   local i = 1
-  while i <= #result_lines do
+  while true do
+    if i > #result_lines then break end
     local line_content = result_lines[i]
     if line_content:match("<FILEPATH>.+</FILEPATH>") then
       local filepath = line_content:match("<FILEPATH>(.+)</FILEPATH>")
@@ -226,8 +227,16 @@ local function transform_result_content(selected_files, result_content, prev_fil
         goto continue
       end
     end
-    if line_content == "<SEARCH>" then
+    if line_content:match("^%s*<SEARCH>") then
       is_searching = true
+
+      if not line_content:match("^%s*<SEARCH>%s*$") then
+        local search_start_line = line_content:match("<SEARCH>(.+)$")
+        line_content = "<SEARCH>"
+        result_lines[i] = line_content
+        if search_start_line and search_start_line ~= "" then table.insert(result_lines, i + 1, search_start_line) end
+      end
+
       local prev_line = result_lines[i - 1]
       if
         prev_line
@@ -241,21 +250,27 @@ local function transform_result_content(selected_files, result_content, prev_fil
       if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
       search_start = i + 1
       last_search_tag_start_line = i
-    elseif line_content:match("</SEARCH>") then
+    elseif line_content:match("</SEARCH>%s*$") then
       if is_replacing then
-        result_lines[i] = "</REPLACE>"
+        result_lines[i] = line_content:gsub("</SEARCH>", "</REPLACE>")
         goto continue_without_increment
       end
-      is_searching = false
-
-      local search_end = i
 
       -- Handle case where </SEARCH> is a suffix
       if not line_content:match("^%s*</SEARCH>%s*$") then
         local search_end_line = line_content:match("^(.+)</SEARCH>")
-        result_lines[i] = search_end_line
+        line_content = "</SEARCH>"
+        result_lines[i] = line_content
+        if search_end_line and search_end_line ~= "" then
+          table.insert(result_lines, i, search_end_line)
+          goto continue_without_increment
+        end
       end
 
+      is_searching = false
+
+      local search_end = i
+
       local prev_line = result_lines[i - 1]
       if prev_line and prev_line:match("^%s*```$") then search_end = i - 1 end
 
@@ -341,19 +356,32 @@ local function transform_result_content(selected_files, result_content, prev_fil
         string.format("```%s", match_filetype),
       })
       goto continue
-    elseif line_content == "<REPLACE>" then
+    elseif line_content:match("^%s*<REPLACE>") then
       is_replacing = true
+      if not line_content:match("^%s*<REPLACE>%s*$") then
+        local replace_first_line = line_content:match("<REPLACE>(.+)$")
+        line_content = "<REPLACE>"
+        result_lines[i] = line_content
+        if replace_first_line and replace_first_line ~= "" then
+          table.insert(result_lines, i + 1, replace_first_line)
+        end
+      end
       local next_line = result_lines[i + 1]
       if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
       last_replace_tag_start_line = i
       goto continue
-    elseif line_content:match("</REPLACE>") then
-      is_replacing = false
+    elseif line_content:match("</REPLACE>%s*$") then
       -- Handle case where </REPLACE> is a suffix
       if not line_content:match("^%s*</REPLACE>%s*$") then
         local replace_end_line = line_content:match("^(.+)</REPLACE>")
-        if replace_end_line and replace_end_line ~= "" then table.insert(transformed_lines, replace_end_line) end
+        line_content = "</REPLACE>"
+        result_lines[i] = line_content
+        if replace_end_line and replace_end_line ~= "" then
+          table.insert(result_lines, i, replace_end_line)
+          goto continue_without_increment
+        end
       end
+      is_replacing = false
       local prev_line = result_lines[i - 1]
       if not (prev_line and prev_line:match("^%s*```$")) then table.insert(transformed_lines, "```") end
       goto continue

From c89741e98b62b9317c6511216b64ce1915e30f4c Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Wed, 12 Feb 2025 21:46:11 +0800
Subject: [PATCH 22/39] fix: base prompts (#1255)

---
 lua/avante/templates/base.avanterules | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lua/avante/templates/base.avanterules b/lua/avante/templates/base.avanterules
index 9598c92..2c361e4 100644
--- a/lua/avante/templates/base.avanterules
+++ b/lua/avante/templates/base.avanterules
@@ -10,6 +10,7 @@
 Act as an expert software developer.
 Always use best practices when coding.
 Respect and use existing conventions, libraries, etc that are already present in the code base.
+Don't directly search for code context in historical messages. Instead, prioritize using tools to obtain context first, then use context from historical messages as a secondary source, since context from historical messages is often not up to date.
 
 Tools Usage Guide:
   - You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.

From 4f0bf40a9d19008efe61619cba17851bc50394dd Mon Sep 17 00:00:00 2001
From: Giuseppe Capasso <46749018+alarmfox@users.noreply.github.com>
Date: Wed, 12 Feb 2025 14:52:38 +0100
Subject: [PATCH 23/39] Fix: musl build error (#1249)

---
 .cargo/config.toml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.cargo/config.toml b/.cargo/config.toml
index af95132..83d9435 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -3,3 +3,6 @@ rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
 
 [target.aarch64-apple-darwin]
 rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
+
+[target.x86_64-unknown-linux-musl]
+rustflags = ["-C", "target-feature=-crt-static"]

From 3a7277386feada184b34e6c66d3664b0917b1047 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Wed, 12 Feb 2025 22:02:12 +0800
Subject: [PATCH 24/39] fix: mkdir for new file (#1257)

---
 lua/avante/sidebar.lua | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index d0c2dfc..1835d3c 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -820,6 +820,8 @@ function Sidebar:apply(current_cursor)
     api.nvim_set_current_win(self.code.winid)
     for filepath, snippets in pairs(selected_snippets_map) do
       local bufnr = Utils.get_or_create_buffer_with_filepath(filepath)
+      local path_ = PPath:new(filepath)
+      path_:parent():mkdir({ parents = true, exists_ok = true })
       insert_conflict_contents(bufnr, snippets)
       local process = function(winid)
         api.nvim_set_current_win(winid)

From f8636315a5629cbd6606fb770824c2da8fa2ec4a Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Wed, 12 Feb 2025 22:19:55 +0800
Subject: [PATCH 25/39] fix: get filetype (#1258)

---
 lua/avante/file_selector.lua |  4 ++--
 lua/avante/repo_map.lua      | 17 +++--------------
 lua/avante/sidebar.lua       |  3 +--
 lua/avante/utils/init.lua    | 21 ++++++++++++++-------
 4 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/lua/avante/file_selector.lua b/lua/avante/file_selector.lua
index 2b703d1..6eece15 100644
--- a/lua/avante/file_selector.lua
+++ b/lua/avante/file_selector.lua
@@ -368,9 +368,9 @@ end
 function FileSelector:get_selected_files_contents()
   local contents = {}
   for _, file_path in ipairs(self.selected_filepaths) do
-    local lines, filetype, error = Utils.read_file_from_buf_or_disk(file_path)
+    local lines, error = Utils.read_file_from_buf_or_disk(file_path)
     lines = lines or {}
-    filetype = filetype or "unknown"
+    local filetype = Utils.get_filetype(file_path)
     if error ~= nil then
       Utils.error("error reading file: " .. error)
     else
diff --git a/lua/avante/repo_map.lua b/lua/avante/repo_map.lua
index c639922..42334d8 100644
--- a/lua/avante/repo_map.lua
+++ b/lua/avante/repo_map.lua
@@ -29,21 +29,10 @@ end
 function RepoMap.setup() vim.defer_fn(RepoMap._init_repo_map_lib, 1000) end
 
 function RepoMap.get_ts_lang(filepath)
-  local filetype = RepoMap.get_filetype(filepath)
+  local filetype = Utils.get_filetype(filepath)
   return filetype_map[filetype] or filetype
 end
 
-function RepoMap.get_filetype(filepath)
-  -- Some files are sometimes not detected correctly when buffer is not included
-  -- https://github.com/neovim/neovim/issues/27265
-
-  local buf = vim.api.nvim_create_buf(false, true)
-  local filetype = vim.filetype.match({ filename = filepath, buf = buf })
-  vim.api.nvim_buf_delete(buf, { force = true })
-
-  return filetype
-end
-
 function RepoMap._build_repo_map(project_root, file_ext)
   local output = {}
   local gitignore_path = project_root .. "/.gitignore"
@@ -70,7 +59,7 @@ function RepoMap._build_repo_map(project_root, file_ext)
     if definitions == "" then return end
     table.insert(output, {
       path = Utils.relative_path(filepath),
-      lang = RepoMap.get_filetype(filepath),
+      lang = Utils.get_filetype(filepath),
       defs = definitions,
     })
   end)
@@ -142,7 +131,7 @@ function RepoMap._get_repo_map(file_ext)
       if not found then
         table.insert(repo_map, {
           path = Utils.relative_path(abs_filepath),
-          lang = RepoMap.get_filetype(abs_filepath),
+          lang = Utils.get_filetype(abs_filepath),
           defs = definitions,
         })
       end
diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 1835d3c..6c63040 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -337,8 +337,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
       -- can happen if the llm tries to edit or create a file outside of it's context.
       if not match_filetype then
         local snippet_file_path = current_filepath or prev_filepath
-        local snippet_file_type = vim.filetype.match({ filename = snippet_file_path }) or "unknown"
-        match_filetype = snippet_file_type
+        match_filetype = Utils.get_filetype(snippet_file_path)
       end
 
       local search_start_tag_idx_in_transformed_lines = 0
diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua
index 7c1bf2d..d20c2cb 100644
--- a/lua/avante/utils/init.lua
+++ b/lua/avante/utils/init.lua
@@ -890,9 +890,19 @@ function M.is_same_file(filepath_a, filepath_b) return M.uniform_path(filepath_a
 
 function M.trim_think_content(content) return content:gsub("^<think>.-</think>", "", 1) end
 
+function M.get_filetype(filepath)
+  -- Some files are sometimes not detected correctly when buffer is not included
+  -- https://github.com/neovim/neovim/issues/27265
+
+  local buf = vim.api.nvim_create_buf(false, true)
+  local filetype = vim.filetype.match({ filename = filepath, buf = buf })
+  vim.api.nvim_buf_delete(buf, { force = true })
+
+  return filetype
+end
+
 ---@param file_path string
 ---@return string[]|nil lines
----@return string|nil file_type
 ---@return string|nil error
 function M.read_file_from_buf_or_disk(file_path)
   --- Lookup if the file is loaded in a buffer
@@ -900,8 +910,7 @@ function M.read_file_from_buf_or_disk(file_path)
   if bufnr ~= -1 and vim.api.nvim_buf_is_loaded(bufnr) then
     -- If buffer exists and is loaded, get buffer content
     local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
-    local file_type = vim.api.nvim_get_option_value("filetype", { buf = bufnr })
-    return lines, file_type, nil
+    return lines, nil
   end
 
   -- Fallback: read file from disk
@@ -909,12 +918,10 @@ function M.read_file_from_buf_or_disk(file_path)
   if file then
     local content = file:read("*all")
     file:close()
-    -- Detect the file type using the specific file's content
-    local file_type = vim.filetype.match({ filename = file_path, contents = { content } }) or "unknown"
-    return vim.split(content, "\n"), file_type, nil
+    return vim.split(content, "\n"), nil
   else
     -- M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
-    return {}, nil, open_err
+    return {}, open_err
   end
 end
 

From 9bad591e8a63f53058471f691497b04eea5dddbf Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Wed, 12 Feb 2025 22:59:15 +0800
Subject: [PATCH 26/39] fix: lowercase filepath bad case (#1259)

---
 lua/avante/sidebar.lua | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 6c63040..36194ea 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -227,6 +227,14 @@ local function transform_result_content(selected_files, result_content, prev_fil
         goto continue
       end
     end
+    if line_content:match("<filepath>.+</filepath>") then
+      local filepath = line_content:match("<filepath>(.+)</filepath>")
+      if filepath then
+        current_filepath = filepath
+        table.insert(transformed_lines, string.format("Filepath: %s", filepath))
+        goto continue
+      end
+    end
     if line_content:match("^%s*<SEARCH>") then
       is_searching = true
 
@@ -243,6 +251,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
         and prev_filepath
         and not prev_line:match("Filepath:.+")
         and not prev_line:match("<FILEPATH>.+</FILEPATH>")
+        and not prev_line:match("<filepath>.+</filepath>")
       then
         table.insert(transformed_lines, string.format("Filepath: %s", prev_filepath))
       end

From ce55d7ac9ec80105410353527c2970b544598655 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Thu, 13 Feb 2025 01:39:02 +0800
Subject: [PATCH 27/39] refactor: better value name (#1261)

---
 lua/avante/llm.lua               |  4 ++--
 lua/avante/providers/azure.lua   | 23 +++++++++++++----------
 lua/avante/providers/bedrock.lua | 22 +++++++++-------------
 lua/avante/providers/claude.lua  | 14 +++++++-------
 lua/avante/providers/cohere.lua  | 14 +++++++-------
 lua/avante/providers/copilot.lua | 14 +++++++-------
 lua/avante/providers/gemini.lua  | 23 +++++++++++++----------
 lua/avante/providers/init.lua    |  4 ++--
 lua/avante/providers/openai.lua  | 32 ++++++++++++++++----------------
 lua/avante/providers/vertex.lua  | 22 +++++++++++-----------
 10 files changed, 87 insertions(+), 85 deletions(-)

diff --git a/lua/avante/llm.lua b/lua/avante/llm.lua
index c657c74..064cb10 100644
--- a/lua/avante/llm.lua
+++ b/lua/avante/llm.lua
@@ -25,8 +25,8 @@ M.generate_prompts = function(opts)
   local Provider = opts.provider or P[Config.provider]
   local mode = opts.mode or "planning"
   ---@type AvanteProviderFunctor | AvanteBedrockProviderFunctor
-  local _, body_opts = P.parse_config(Provider)
-  local max_tokens = body_opts.max_tokens or 4096
+  local _, request_body = P.parse_config(Provider)
+  local max_tokens = request_body.max_tokens or 4096
 
   -- Check if the instructions contains an image path
   local image_paths = {}
diff --git a/lua/avante/providers/azure.lua b/lua/avante/providers/azure.lua
index 83ca617..270e1a2 100644
--- a/lua/avante/providers/azure.lua
+++ b/lua/avante/providers/azure.lua
@@ -18,31 +18,34 @@ M.parse_response = O.parse_response
 M.parse_response_without_stream = O.parse_response_without_stream
 
 M.parse_curl_args = function(provider, prompt_opts)
-  local base, body_opts = P.parse_config(provider)
+  local provider_conf, request_body = P.parse_config(provider)
 
   local headers = {
     ["Content-Type"] = "application/json",
   }
-  if P.env.require_api_key(base) then headers["api-key"] = provider.parse_api_key() end
+  if P.env.require_api_key(provider_conf) then headers["api-key"] = provider.parse_api_key() end
 
   -- NOTE: When using "o" series set the supported parameters only
-  if O.is_o_series_model(base.model) then
-    body_opts.max_tokens = nil
-    body_opts.temperature = 1
+  if O.is_o_series_model(provider_conf.model) then
+    request_body.max_tokens = nil
+    request_body.temperature = 1
   end
 
   return {
     url = Utils.url_join(
-      base.endpoint,
-      "/openai/deployments/" .. base.deployment .. "/chat/completions?api-version=" .. base.api_version
+      provider_conf.endpoint,
+      "/openai/deployments/"
+        .. provider_conf.deployment
+        .. "/chat/completions?api-version="
+        .. provider_conf.api_version
     ),
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
     headers = headers,
     body = vim.tbl_deep_extend("force", {
       messages = M.parse_messages(prompt_opts),
       stream = true,
-    }, body_opts),
+    }, request_body),
   }
 end
 
diff --git a/lua/avante/providers/bedrock.lua b/lua/avante/providers/bedrock.lua
index 49720f3..07ef4f0 100644
--- a/lua/avante/providers/bedrock.lua
+++ b/lua/avante/providers/bedrock.lua
@@ -1,5 +1,4 @@
 local Utils = require("avante.utils")
-local Clipboard = require("avante.clipboard")
 local P = require("avante.providers")
 
 ---@alias AvanteBedrockPayloadBuilder fun(prompt_opts: AvantePromptOptions, body_opts: table<string, any>): table<string, any>
@@ -17,17 +16,14 @@ M.api_key_name = "BEDROCK_KEYS"
 M.use_xml_format = true
 
 M.load_model_handler = function()
-  local base, _ = P.parse_config(P["bedrock"])
-  local bedrock_model = base.model
-  if base.model:match("anthropic") then bedrock_model = "claude" end
+  local provider_conf, _ = P.parse_config(P["bedrock"])
+  local bedrock_model = provider_conf.model
+  if provider_conf.model:match("anthropic") then bedrock_model = "claude" end
 
   local ok, model_module = pcall(require, "avante.providers.bedrock." .. bedrock_model)
-  if ok then
-    return model_module
-  else
-    local error_msg = "Bedrock model handler not found: " .. bedrock_model
-    Utils.error(error_msg, { once = true, title = "Avante" })
-  end
+  if ok then return model_module end
+  local error_msg = "Bedrock model handler not found: " .. bedrock_model
+  error(error_msg)
 end
 
 M.parse_response = function(ctx, data_stream, event_state, opts)
@@ -46,8 +42,8 @@ M.parse_stream_data = function(data, opts)
   -- The `type` field in the decoded JSON determines how the response is handled.
   local bedrock_match = data:gmatch("event(%b{})")
   for bedrock_data_match in bedrock_match do
-    local data = vim.json.decode(bedrock_data_match)
-    local data_stream = vim.base64.decode(data.bytes)
+    local jsn = vim.json.decode(bedrock_data_match)
+    local data_stream = vim.base64.decode(jsn.bytes)
     local json = vim.json.decode(data_stream)
     M.parse_response({}, data_stream, json.type, opts)
   end
@@ -60,6 +56,7 @@ M.parse_curl_args = function(provider, prompt_opts)
   local base, body_opts = P.parse_config(provider)
 
   local api_key = provider.parse_api_key()
+  if api_key == nil then error("Cannot get the bedrock api key!") end
   local parts = vim.split(api_key, ",")
   local aws_access_key_id = parts[1]
   local aws_secret_access_key = parts[2]
@@ -108,7 +105,6 @@ M.on_error = function(result)
   end
 
   local error_msg = body.error.message
-  local error_type = body.error.type
 
   Utils.error(error_msg, { once = true, title = "Avante" })
 end
diff --git a/lua/avante/providers/claude.lua b/lua/avante/providers/claude.lua
index ecc97dd..bf64fa3 100644
--- a/lua/avante/providers/claude.lua
+++ b/lua/avante/providers/claude.lua
@@ -226,7 +226,7 @@ end
 ---@param prompt_opts AvantePromptOptions
 ---@return table
 M.parse_curl_args = function(provider, prompt_opts)
-  local base, body_opts = P.parse_config(provider)
+  local provider_conf, request_body = P.parse_config(provider)
 
   local headers = {
     ["Content-Type"] = "application/json",
@@ -234,7 +234,7 @@ M.parse_curl_args = function(provider, prompt_opts)
     ["anthropic-beta"] = "prompt-caching-2024-07-31",
   }
 
-  if P.env.require_api_key(base) then headers["x-api-key"] = provider.parse_api_key() end
+  if P.env.require_api_key(provider_conf) then headers["x-api-key"] = provider.parse_api_key() end
 
   local messages = M.parse_messages(prompt_opts)
 
@@ -246,12 +246,12 @@ M.parse_curl_args = function(provider, prompt_opts)
   end
 
   return {
-    url = Utils.url_join(base.endpoint, "/v1/messages"),
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
+    url = Utils.url_join(provider_conf.endpoint, "/v1/messages"),
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
     headers = headers,
     body = vim.tbl_deep_extend("force", {
-      model = base.model,
+      model = provider_conf.model,
       system = {
         {
           type = "text",
@@ -262,7 +262,7 @@ M.parse_curl_args = function(provider, prompt_opts)
       messages = messages,
       tools = tools,
       stream = true,
-    }, body_opts),
+    }, request_body),
   }
 end
 
diff --git a/lua/avante/providers/cohere.lua b/lua/avante/providers/cohere.lua
index 059e23e..f6c7953 100644
--- a/lua/avante/providers/cohere.lua
+++ b/lua/avante/providers/cohere.lua
@@ -70,7 +70,7 @@ M.parse_stream_data = function(data, opts)
 end
 
 M.parse_curl_args = function(provider, prompt_opts)
-  local base, body_opts = P.parse_config(provider)
+  local provider_conf, request_body = P.parse_config(provider)
 
   local headers = {
     ["Accept"] = "application/json",
@@ -82,17 +82,17 @@ M.parse_curl_args = function(provider, prompt_opts)
       .. "."
       .. vim.version().patch,
   }
-  if P.env.require_api_key(base) then headers["Authorization"] = "Bearer " .. provider.parse_api_key() end
+  if P.env.require_api_key(provider_conf) then headers["Authorization"] = "Bearer " .. provider.parse_api_key() end
 
   return {
-    url = Utils.url_join(base.endpoint, "/chat"),
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
+    url = Utils.url_join(provider_conf.endpoint, "/chat"),
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
     headers = headers,
     body = vim.tbl_deep_extend("force", {
-      model = base.model,
+      model = provider_conf.model,
       stream = true,
-    }, M.parse_messages(prompt_opts), body_opts),
+    }, M.parse_messages(prompt_opts), request_body),
   }
 end
 
diff --git a/lua/avante/providers/copilot.lua b/lua/avante/providers/copilot.lua
index 47ec4b4..050b934 100644
--- a/lua/avante/providers/copilot.lua
+++ b/lua/avante/providers/copilot.lua
@@ -249,7 +249,7 @@ M.parse_curl_args = function(provider, prompt_opts)
   -- (this should rarely happen, as we refresh the token in the background)
   H.refresh_token(false, false)
 
-  local base, body_opts = P.parse_config(provider)
+  local provider_conf, request_body = P.parse_config(provider)
 
   local tools = {}
   if prompt_opts.tools then
@@ -259,10 +259,10 @@ M.parse_curl_args = function(provider, prompt_opts)
   end
 
   return {
-    url = H.chat_completion_url(base.endpoint),
-    timeout = base.timeout,
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
+    url = H.chat_completion_url(provider_conf.endpoint),
+    timeout = provider_conf.timeout,
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
     headers = {
       ["Content-Type"] = "application/json",
       ["Authorization"] = "Bearer " .. M.state.github_token.token,
@@ -270,11 +270,11 @@ M.parse_curl_args = function(provider, prompt_opts)
       ["Editor-Version"] = ("Neovim/%s.%s.%s"):format(vim.version().major, vim.version().minor, vim.version().patch),
     },
     body = vim.tbl_deep_extend("force", {
-      model = base.model,
+      model = provider_conf.model,
       messages = M.parse_messages(prompt_opts),
       stream = true,
       tools = tools,
-    }, body_opts),
+    }, request_body),
   }
 end
 
diff --git a/lua/avante/providers/gemini.lua b/lua/avante/providers/gemini.lua
index 1528b87..9a13346 100644
--- a/lua/avante/providers/gemini.lua
+++ b/lua/avante/providers/gemini.lua
@@ -82,26 +82,29 @@ M.parse_response = function(ctx, data_stream, _, opts)
 end
 
 M.parse_curl_args = function(provider, prompt_opts)
-  local base, body_opts = P.parse_config(provider)
+  local provider_conf, request_body = P.parse_config(provider)
 
-  body_opts = vim.tbl_deep_extend("force", body_opts, {
+  request_body = vim.tbl_deep_extend("force", request_body, {
     generationConfig = {
-      temperature = body_opts.temperature,
-      maxOutputTokens = body_opts.max_tokens,
+      temperature = request_body.temperature,
+      maxOutputTokens = request_body.max_tokens,
     },
   })
-  body_opts.temperature = nil
-  body_opts.max_tokens = nil
+  request_body.temperature = nil
+  request_body.max_tokens = nil
 
   local api_key = provider.parse_api_key()
   if api_key == nil then error("Cannot get the gemini api key!") end
 
   return {
-    url = Utils.url_join(base.endpoint, base.model .. ":streamGenerateContent?alt=sse&key=" .. api_key),
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
+    url = Utils.url_join(
+      provider_conf.endpoint,
+      provider_conf.model .. ":streamGenerateContent?alt=sse&key=" .. api_key
+    ),
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
     headers = { ["Content-Type"] = "application/json" },
-    body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), body_opts),
+    body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), request_body),
   }
 end
 
diff --git a/lua/avante/providers/init.lua b/lua/avante/providers/init.lua
index fdb1cd3..3fa7915 100644
--- a/lua/avante/providers/init.lua
+++ b/lua/avante/providers/init.lua
@@ -346,9 +346,9 @@ M = setmetatable(M, {
     if t[k].has == nil then t[k].has = function() return E.parse_envvar(t[k]) ~= nil end end
 
     if t[k].setup == nil then
-      local base = M.parse_config(t[k])
+      local provider_conf = M.parse_config(t[k])
       t[k].setup = function()
-        if E.require_api_key(base) then t[k].parse_api_key() end
+        if E.require_api_key(provider_conf) then t[k].parse_api_key() end
         require("avante.tokenizers").setup(t[k].tokenizer_id)
       end
     end
diff --git a/lua/avante/providers/openai.lua b/lua/avante/providers/openai.lua
index 0543046..d03541b 100644
--- a/lua/avante/providers/openai.lua
+++ b/lua/avante/providers/openai.lua
@@ -275,14 +275,14 @@ M.parse_response_without_stream = function(data, _, opts)
 end
 
 M.parse_curl_args = function(provider, prompt_opts)
-  local base, body_opts = P.parse_config(provider)
-  local disable_tools = base.disable_tools or false
+  local provider_conf, request_body = P.parse_config(provider)
+  local disable_tools = provider_conf.disable_tools or false
 
   local headers = {
     ["Content-Type"] = "application/json",
   }
 
-  if P.env.require_api_key(base) then
+  if P.env.require_api_key(provider_conf) then
     local api_key = provider.parse_api_key()
     if api_key == nil then
       error(Config.provider .. " API key is not set, please set it in your environment variable or config file")
@@ -290,18 +290,18 @@ M.parse_curl_args = function(provider, prompt_opts)
     headers["Authorization"] = "Bearer " .. api_key
   end
 
-  if M.is_openrouter(base.endpoint) then
+  if M.is_openrouter(provider_conf.endpoint) then
     headers["HTTP-Referer"] = "https://github.com/yetone/avante.nvim"
     headers["X-Title"] = "Avante.nvim"
-    body_opts.include_reasoning = true
+    request_body.include_reasoning = true
   end
 
   -- NOTE: When using "o" series set the supported parameters only
   local stream = true
-  if M.is_o_series_model(base.model) then
-    body_opts.max_completion_tokens = body_opts.max_tokens
-    body_opts.max_tokens = nil
-    body_opts.temperature = 1
+  if M.is_o_series_model(provider_conf.model) then
+    request_body.max_completion_tokens = request_body.max_tokens
+    request_body.max_tokens = nil
+    request_body.temperature = 1
   end
 
   local tools = nil
@@ -312,20 +312,20 @@ M.parse_curl_args = function(provider, prompt_opts)
     end
   end
 
-  Utils.debug("endpoint", base.endpoint)
-  Utils.debug("model", base.model)
+  Utils.debug("endpoint", provider_conf.endpoint)
+  Utils.debug("model", provider_conf.model)
 
   return {
-    url = Utils.url_join(base.endpoint, "/chat/completions"),
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
+    url = Utils.url_join(provider_conf.endpoint, "/chat/completions"),
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
     headers = headers,
     body = vim.tbl_deep_extend("force", {
-      model = base.model,
+      model = provider_conf.model,
       messages = M.parse_messages(prompt_opts),
       stream = stream,
       tools = tools,
-    }, body_opts),
+    }, request_body),
   }
 end
 
diff --git a/lua/avante/providers/vertex.lua b/lua/avante/providers/vertex.lua
index f1ca9ee..5273483 100644
--- a/lua/avante/providers/vertex.lua
+++ b/lua/avante/providers/vertex.lua
@@ -32,22 +32,22 @@ M.parse_api_key = function()
 end
 
 M.parse_curl_args = function(provider, prompt_opts)
-  local base, body_opts = P.parse_config(provider)
+  local provider_conf, request_body = P.parse_config(provider)
   local location = vim.fn.getenv("LOCATION") or "default-location"
   local project_id = vim.fn.getenv("PROJECT_ID") or "default-project-id"
-  local model_id = base.model or "default-model-id"
-  local url = base.endpoint:gsub("LOCATION", location):gsub("PROJECT_ID", project_id)
+  local model_id = provider_conf.model or "default-model-id"
+  local url = provider_conf.endpoint:gsub("LOCATION", location):gsub("PROJECT_ID", project_id)
 
   url = string.format("%s/%s:streamGenerateContent?alt=sse", url, model_id)
 
-  body_opts = vim.tbl_deep_extend("force", body_opts, {
+  request_body = vim.tbl_deep_extend("force", request_body, {
     generationConfig = {
-      temperature = body_opts.temperature,
-      maxOutputTokens = body_opts.max_tokens,
+      temperature = request_body.temperature,
+      maxOutputTokens = request_body.max_tokens,
     },
   })
-  body_opts.temperature = nil
-  body_opts.max_tokens = nil
+  request_body.temperature = nil
+  request_body.max_tokens = nil
   local bearer_token = M.parse_api_key()
 
   return {
@@ -56,9 +56,9 @@ M.parse_curl_args = function(provider, prompt_opts)
       ["Authorization"] = "Bearer " .. bearer_token,
       ["Content-Type"] = "application/json; charset=utf-8",
     },
-    proxy = base.proxy,
-    insecure = base.allow_insecure,
-    body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), body_opts),
+    proxy = provider_conf.proxy,
+    insecure = provider_conf.allow_insecure,
+    body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), request_body),
   }
 end
 

From 76c06ed27798842a037e682dbfd3a1e2f5726635 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Thu, 13 Feb 2025 11:54:54 +0800
Subject: [PATCH 28/39] fix: tools return json (#1263)

---
 lua/avante/llm_tools.lua | 41 ++++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/lua/avante/llm_tools.lua b/lua/avante/llm_tools.lua
index 5117a05..715244d 100644
--- a/lua/avante/llm_tools.lua
+++ b/lua/avante/llm_tools.lua
@@ -42,13 +42,12 @@ function M.list_files(opts, on_log)
     add_dirs = true,
     depth = opts.depth,
   })
-  local result = ""
+  local filepaths = {}
   for _, file in ipairs(files) do
     local uniform_path = Utils.uniform_path(file)
-    result = result .. uniform_path .. "\n"
+    table.insert(filepaths, uniform_path)
   end
-  result = result:gsub("\n$", "")
-  return result, nil
+  return vim.json.encode(filepaths), nil
 end
 
 ---@param opts { rel_path: string, keyword: string }
@@ -63,12 +62,11 @@ function M.search_files(opts, on_log)
   local files = Utils.scan_directory_respect_gitignore({
     directory = abs_path,
   })
-  local result = ""
+  local filepaths = {}
   for _, file in ipairs(files) do
-    if file:find(opts.keyword) then result = result .. file .. "\n" end
+    if file:find(opts.keyword) then table.insert(filepaths, file) end
   end
-  result = result:gsub("\n$", "")
-  return result, nil
+  return vim.json.encode(filepaths), nil
 end
 
 ---@param opts { rel_path: string, keyword: string }
@@ -105,7 +103,9 @@ function M.search(opts, on_log)
   if on_log then on_log("Running command: " .. cmd) end
   local result = vim.fn.system(cmd)
 
-  return result or "", nil
+  local filepaths = vim.split(result, "\n")
+
+  return vim.json.encode(filepaths), nil
 end
 
 ---@param opts { rel_path: string }
@@ -184,9 +184,10 @@ function M.rename_file(opts, on_log)
 end
 
 ---@param opts { rel_path: string, new_rel_path: string }
+---@param on_log? fun(log: string): nil
 ---@return boolean success
 ---@return string|nil error
-function M.copy_file(opts)
+function M.copy_file(opts, on_log)
   local abs_path = get_abs_path(opts.rel_path)
   if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
   if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
@@ -194,38 +195,47 @@ function M.copy_file(opts)
   local new_abs_path = get_abs_path(opts.new_rel_path)
   if not has_permission_to_access(new_abs_path) then return false, "No permission to access path: " .. new_abs_path end
   if Path:new(new_abs_path):exists() then return false, "File already exists: " .. new_abs_path end
+  if on_log then on_log("Copying file: " .. abs_path .. " to " .. new_abs_path) end
   Path:new(new_abs_path):write(Path:new(abs_path):read())
   return true, nil
 end
 
 ---@param opts { rel_path: string }
+---@param on_log? fun(log: string): nil
 ---@return boolean success
 ---@return string|nil error
-function M.delete_file(opts)
+function M.delete_file(opts, on_log)
   local abs_path = get_abs_path(opts.rel_path)
   if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
   if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
   if not Path:new(abs_path):is_file() then return false, "Path is not a file: " .. abs_path end
   if not M.confirm("Are you sure you want to delete the file: " .. abs_path) then return false, "User canceled" end
+  if on_log then on_log("Deleting file: " .. abs_path) end
   os.remove(abs_path)
   return true, nil
 end
 
 ---@param opts { rel_path: string }
+---@param on_log? fun(log: string): nil
 ---@return boolean success
 ---@return string|nil error
-function M.create_dir(opts)
+function M.create_dir(opts, on_log)
   local abs_path = get_abs_path(opts.rel_path)
   if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
   if Path:new(abs_path):exists() then return false, "Directory already exists: " .. abs_path end
+  if not M.confirm("Are you sure you want to create the directory: " .. abs_path) then
+    return false, "User canceled"
+  end
+  if on_log then on_log("Creating directory: " .. abs_path) end
   Path:new(abs_path):mkdir({ parents = true })
   return true, nil
 end
 
 ---@param opts { rel_path: string, new_rel_path: string }
+---@param on_log? fun(log: string): nil
 ---@return boolean success
 ---@return string|nil error
-function M.rename_dir(opts)
+function M.rename_dir(opts, on_log)
   local abs_path = get_abs_path(opts.rel_path)
   if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
   if not Path:new(abs_path):exists() then return false, "Directory not found: " .. abs_path end
@@ -236,14 +246,16 @@ function M.rename_dir(opts)
   if not M.confirm("Are you sure you want to rename directory " .. abs_path .. " to " .. new_abs_path .. "?") then
     return false, "User canceled"
   end
+  if on_log then on_log("Renaming directory: " .. abs_path .. " to " .. new_abs_path) end
   os.rename(abs_path, new_abs_path)
   return true, nil
 end
 
 ---@param opts { rel_path: string }
+---@param on_log? fun(log: string): nil
 ---@return boolean success
 ---@return string|nil error
-function M.delete_dir(opts)
+function M.delete_dir(opts, on_log)
   local abs_path = get_abs_path(opts.rel_path)
   if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
   if not Path:new(abs_path):exists() then return false, "Directory not found: " .. abs_path end
@@ -251,6 +263,7 @@ function M.delete_dir(opts)
   if not M.confirm("Are you sure you want to delete the directory: " .. abs_path) then
     return false, "User canceled"
   end
+  if on_log then on_log("Deleting directory: " .. abs_path) end
   os.remove(abs_path)
   return true, nil
 end

From 763dbe064d848544f9996d83ac688449bde6d246 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Thu, 13 Feb 2025 13:23:28 +0800
Subject: [PATCH 29/39] fix: line content (#1264)

---
 lua/avante/sidebar.lua | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 36194ea..6630b47 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -244,6 +244,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
         result_lines[i] = line_content
         if search_start_line and search_start_line ~= "" then table.insert(result_lines, i + 1, search_start_line) end
       end
+      line_content = "<SEARCH>"
 
       local prev_line = result_lines[i - 1]
       if

From 25111c6df31e0e7562f388d4d7dedcb570e323e1 Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Thu, 13 Feb 2025 13:39:24 +0800
Subject: [PATCH 30/39] fix: incorrect minimize snippets (#1265)

---
 lua/avante/sidebar.lua | 28 +++++++++++-----------------
 1 file changed, 11 insertions(+), 17 deletions(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 6630b47..8305f9f 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -770,27 +770,22 @@ local function minimize_snippet(original_lines, snippet)
   return new_snippets
 end
 
----@param snippets_map table<string, AvanteCodeSnippet[]>
+---@param filepath string
+---@param snippets AvanteCodeSnippet[]
 ---@return table<string, AvanteCodeSnippet[]>
-function Sidebar:minimize_snippets(snippets_map)
+function Sidebar:minimize_snippets(filepath, snippets)
   local original_lines = {}
 
-  if vim.tbl_count(snippets_map) > 0 then
-    local filepaths = vim.tbl_keys(snippets_map)
-    local original_lines_ = Utils.read_file_from_buf_or_disk(filepaths[1])
-    if original_lines_ then original_lines = original_lines_ end
-  end
+  local original_lines_ = Utils.read_file_from_buf_or_disk(filepath)
+  if original_lines_ then original_lines = original_lines_ end
 
   local results = {}
 
-  for filepath, snippets in pairs(snippets_map) do
-    for _, snippet in ipairs(snippets) do
-      local new_snippets = minimize_snippet(original_lines, snippet)
-      if new_snippets then
-        results[filepath] = results[filepath] or {}
-        for _, new_snippet in ipairs(new_snippets) do
-          table.insert(results[filepath], new_snippet)
-        end
+  for _, snippet in ipairs(snippets) do
+    local new_snippets = minimize_snippet(original_lines, snippet)
+    if new_snippets then
+      for _, new_snippet in ipairs(new_snippets) do
+        table.insert(results, new_snippet)
       end
     end
   end
@@ -823,11 +818,10 @@ function Sidebar:apply(current_cursor)
     selected_snippets_map = all_snippets_map
   end
 
-  if Config.behaviour.minimize_diff then selected_snippets_map = self:minimize_snippets(selected_snippets_map) end
-
   vim.defer_fn(function()
     api.nvim_set_current_win(self.code.winid)
     for filepath, snippets in pairs(selected_snippets_map) do
+      if Config.behaviour.minimize_diff then snippets = self:minimize_snippets(filepath, snippets) end
       local bufnr = Utils.get_or_create_buffer_with_filepath(filepath)
       local path_ = PPath:new(filepath)
       path_:parent():mkdir({ parents = true, exists_ok = true })

From 8f325129498ead873d7da7abfd5e0eef72d5ef2f Mon Sep 17 00:00:00 2001
From: Alexandre Balon-Perin <alexbp13@gmail.com>
Date: Sun, 2 Feb 2025 00:09:27 +0900
Subject: [PATCH 31/39] Fix Makefile shell usage

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 40d6374..c46f285 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,7 @@ else
 endif
 else
 $1:
-	LUA_VERSION=$1 bash ./build.sh
+	$(shell LUA_VERSION=$1 bash ./build.sh)
 endif
 endef
 

From 16bcbc0229493056dc989238e39821cab591eca0 Mon Sep 17 00:00:00 2001
From: Hanchin Hsieh <me@yuchanns.xyz>
Date: Thu, 13 Feb 2025 15:29:32 +0800
Subject: [PATCH 32/39] Revert "Fix Makefile shell usage"

This reverts commit 8f325129498ead873d7da7abfd5e0eef72d5ef2f.
---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index c46f285..40d6374 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,7 @@ else
 endif
 else
 $1:
-	$(shell LUA_VERSION=$1 bash ./build.sh)
+	LUA_VERSION=$1 bash ./build.sh
 endif
 endef
 

From 1a4f2575d663cc3e0c5c9d17383f1479dd4fd28f Mon Sep 17 00:00:00 2001
From: Hanchin Hsieh <me@yuchanns.xyz>
Date: Thu, 13 Feb 2025 16:55:10 +0800
Subject: [PATCH 33/39] fix(utils): get non-nil filetype

---
 lua/avante/utils/init.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua
index d20c2cb..7e97297 100644
--- a/lua/avante/utils/init.lua
+++ b/lua/avante/utils/init.lua
@@ -895,7 +895,7 @@ function M.get_filetype(filepath)
   -- https://github.com/neovim/neovim/issues/27265
 
   local buf = vim.api.nvim_create_buf(false, true)
-  local filetype = vim.filetype.match({ filename = filepath, buf = buf })
+  local filetype = vim.filetype.match({ filename = filepath, buf = buf }) or ""
   vim.api.nvim_buf_delete(buf, { force = true })
 
   return filetype

From c60dc6c316414c2adf5bb61624719d76accab22f Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Fri, 14 Feb 2025 12:28:39 +0800
Subject: [PATCH 34/39] fix: case insensitive (#1275)

---
 lua/avante/sidebar.lua | 41 ++++++++++++++++-------------------------
 1 file changed, 16 insertions(+), 25 deletions(-)

diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua
index 8305f9f..02a25d3 100644
--- a/lua/avante/sidebar.lua
+++ b/lua/avante/sidebar.lua
@@ -219,27 +219,19 @@ local function transform_result_content(selected_files, result_content, prev_fil
   while true do
     if i > #result_lines then break end
     local line_content = result_lines[i]
-    if line_content:match("<FILEPATH>.+</FILEPATH>") then
-      local filepath = line_content:match("<FILEPATH>(.+)</FILEPATH>")
+    if line_content:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>.+</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>") then
+      local filepath = line_content:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>(.+)</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>")
       if filepath then
         current_filepath = filepath
         table.insert(transformed_lines, string.format("Filepath: %s", filepath))
         goto continue
       end
     end
-    if line_content:match("<filepath>.+</filepath>") then
-      local filepath = line_content:match("<filepath>(.+)</filepath>")
-      if filepath then
-        current_filepath = filepath
-        table.insert(transformed_lines, string.format("Filepath: %s", filepath))
-        goto continue
-      end
-    end
-    if line_content:match("^%s*<SEARCH>") then
+    if line_content:match("^%s*<[Ss][Ee][Aa][Rr][Cc][Hh]>") then
       is_searching = true
 
-      if not line_content:match("^%s*<SEARCH>%s*$") then
-        local search_start_line = line_content:match("<SEARCH>(.+)$")
+      if not line_content:match("^%s*<[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
+        local search_start_line = line_content:match("<[Ss][Ee][Aa][Rr][Cc][Hh]>(.+)$")
         line_content = "<SEARCH>"
         result_lines[i] = line_content
         if search_start_line and search_start_line ~= "" then table.insert(result_lines, i + 1, search_start_line) end
@@ -251,8 +243,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
         prev_line
         and prev_filepath
         and not prev_line:match("Filepath:.+")
-        and not prev_line:match("<FILEPATH>.+</FILEPATH>")
-        and not prev_line:match("<filepath>.+</filepath>")
+        and not prev_line:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>.+</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>")
       then
         table.insert(transformed_lines, string.format("Filepath: %s", prev_filepath))
       end
@@ -260,15 +251,15 @@ local function transform_result_content(selected_files, result_content, prev_fil
       if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
       search_start = i + 1
       last_search_tag_start_line = i
-    elseif line_content:match("</SEARCH>%s*$") then
+    elseif line_content:match("</[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
       if is_replacing then
-        result_lines[i] = line_content:gsub("</SEARCH>", "</REPLACE>")
+        result_lines[i] = line_content:gsub("</[Ss][Ee][Aa][Rr][Cc][Hh]>", "</REPLACE>")
         goto continue_without_increment
       end
 
       -- Handle case where </SEARCH> is a suffix
-      if not line_content:match("^%s*</SEARCH>%s*$") then
-        local search_end_line = line_content:match("^(.+)</SEARCH>")
+      if not line_content:match("^%s*</[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
+        local search_end_line = line_content:match("^(.+)</[Ss][Ee][Aa][Rr][Cc][Hh]>")
         line_content = "</SEARCH>"
         result_lines[i] = line_content
         if search_end_line and search_end_line ~= "" then
@@ -365,10 +356,10 @@ local function transform_result_content(selected_files, result_content, prev_fil
         string.format("```%s", match_filetype),
       })
       goto continue
-    elseif line_content:match("^%s*<REPLACE>") then
+    elseif line_content:match("^%s*<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>") then
       is_replacing = true
-      if not line_content:match("^%s*<REPLACE>%s*$") then
-        local replace_first_line = line_content:match("<REPLACE>(.+)$")
+      if not line_content:match("^%s*<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
+        local replace_first_line = line_content:match("<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>(.+)$")
         line_content = "<REPLACE>"
         result_lines[i] = line_content
         if replace_first_line and replace_first_line ~= "" then
@@ -379,10 +370,10 @@ local function transform_result_content(selected_files, result_content, prev_fil
       if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
       last_replace_tag_start_line = i
       goto continue
-    elseif line_content:match("</REPLACE>%s*$") then
+    elseif line_content:match("</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
       -- Handle case where </REPLACE> is a suffix
-      if not line_content:match("^%s*</REPLACE>%s*$") then
-        local replace_end_line = line_content:match("^(.+)</REPLACE>")
+      if not line_content:match("^%s*</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
+        local replace_end_line = line_content:match("^(.+)</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>")
         line_content = "</REPLACE>"
         result_lines[i] = line_content
         if replace_end_line and replace_end_line ~= "" then

From 7fa7b0fa3b2b846aab759633d45bd37edb03868e Mon Sep 17 00:00:00 2001
From: yetone <yetoneful@gmail.com>
Date: Mon, 17 Feb 2025 00:36:00 +0800
Subject: [PATCH 35/39] feat: support searchapi (#1284)

---
 README.md                             |  6 ++---
 lua/avante/config.lua                 | 33 +++++++++++++++++++++++++--
 lua/avante/llm_tools.lua              | 17 ++++++++++++++
 lua/avante/templates/base.avanterules |  1 +
 4 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 364f066..03e3ff3 100644
--- a/README.md
+++ b/README.md
@@ -582,15 +582,15 @@ For more information, see [Custom Providers](https://github.com/yetone/avante.nv
 
 ## Web Search Engines
 
-Avante's tools include some web search engines, currently support [tavily](https://tavily.com/), [serpapi](https://serpapi.com/) and google's [programmable search engine](https://developers.google.com/custom-search/v1/overview). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
+Avante's tools include some web search engines, currently support [tavily](https://tavily.com/), [serpapi](https://serpapi.com/), [searchapi](https://www.searchapi.io/) and google's [programmable search engine](https://developers.google.com/custom-search/v1/overview). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
 
 ```lua
 web_search_engine = {
-  provider = "tavily", -- tavily, serpapi or google
+  provider = "tavily", -- tavily, serpapi, searchapi or google
 }
 ```
 
-You need to set the environment variable `TAVILY_API_KEY` , `SERPAPI_API_KEY` to use tavily or serpapi.
+You need to set the environment variable `TAVILY_API_KEY` , `SERPAPI_API_KEY`, `SEARCHAPI_API_KEY` to use tavily or serpapi or searchapi.
 To use google, set the `GOOGLE_SEARCH_API_KEY` as the [API key](https://developers.google.com/custom-search/v1/overview), and `GOOGLE_SEARCH_ENGINE_ID` as the [search engine](https://programmablesearchengine.google.com) ID.
 
 ## Disable Tools
diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 8791469..504f8fb 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -56,10 +56,39 @@ M._defaults = {
                     title = result.title,
                     link = result.link,
                     snippet = result.snippet,
+                    date = result.date,
                   }
                 end
               )
-              :take(5)
+              :take(10)
+              :totable()
+            return vim.json.encode(jsn), nil
+          end
+          return "", nil
+        end,
+      },
+      searchapi = {
+        api_key_name = "SEARCHAPI_API_KEY",
+        extra_request_body = {
+          engine = "google",
+        },
+        ---@type WebSearchEngineProviderResponseBodyFormatter
+        format_response_body = function(body)
+          if body.answer_box ~= nil then return body.answer_box.result, nil end
+          if body.organic_results ~= nil then
+            local jsn = vim
+              .iter(body.organic_results)
+              :map(
+                function(result)
+                  return {
+                    title = result.title,
+                    link = result.link,
+                    snippet = result.snippet,
+                    date = result.date,
+                  }
+                end
+              )
+              :take(10)
               :totable()
             return vim.json.encode(jsn), nil
           end
@@ -84,7 +113,7 @@ M._defaults = {
                   }
                 end
               )
-              :take(5)
+              :take(10)
               :totable()
             return vim.json.encode(jsn), nil
           end
diff --git a/lua/avante/llm_tools.lua b/lua/avante/llm_tools.lua
index 715244d..5dbec4d 100644
--- a/lua/avante/llm_tools.lua
+++ b/lua/avante/llm_tools.lua
@@ -340,6 +340,23 @@ function M.web_search(opts, on_log)
     if resp.status ~= 200 then return nil, "Error: " .. resp.body end
     local jsn = vim.json.decode(resp.body)
     return search_engine.format_response_body(jsn)
+  elseif provider_type == "searchapi" then
+    local query_params = vim.tbl_deep_extend("force", {
+      api_key = api_key,
+      q = opts.query,
+    }, search_engine.extra_request_body)
+    local query_string = ""
+    for key, value in pairs(query_params) do
+      query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
+    end
+    local resp = curl.get("https://searchapi.io/api/v1/search?" .. query_string, {
+      headers = {
+        ["Content-Type"] = "application/json",
+      },
+    })
+    if resp.status ~= 200 then return nil, "Error: " .. resp.body end
+    local jsn = vim.json.decode(resp.body)
+    return search_engine.format_response_body(jsn)
   elseif provider_type == "google" then
     local engine_id = os.getenv(search_engine.engine_id_name)
     if engine_id == nil or engine_id == "" then
diff --git a/lua/avante/templates/base.avanterules b/lua/avante/templates/base.avanterules
index 2c361e4..c96886e 100644
--- a/lua/avante/templates/base.avanterules
+++ b/lua/avante/templates/base.avanterules
@@ -20,6 +20,7 @@ Tools Usage Guide:
   - When attempting to modify a file that is not in the context, please first use the `list_files` tool and `search_files` tool to check if the file you want to modify exists, then use the `read_file` tool to read the file content. Don't modify blindly!
   - When generating files, first use `list_files` tool to read the directory structure, don't generate blindly!
   - When creating files, first check if the directory exists. If it doesn't exist, create the directory before creating the file.
+  - After `web_search`, if you don't get detailed enough information, do not continue use `web_search`, just continue using the `fetch` tool to get more information you need from the links in the search results.
 
 {% if system_info -%}
 Use the appropriate shell based on the user's system info:

From 0a146dc63a16710e768d0fa5d1ba00f99322911d Mon Sep 17 00:00:00 2001
From: Enes Kutay SEZEN <eneskutaysezen@gmail.com>
Date: Sun, 16 Feb 2025 23:52:22 -0500
Subject: [PATCH 36/39] docs: add reasoning_effort docs (#1286)

---
 README.md                     | 1 +
 lua/avante/providers/init.lua | 1 +
 2 files changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 03e3ff3..c7f3363 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,7 @@ For building binary if you wish to build from source, then `cargo` is required.
       timeout = 30000, -- timeout in milliseconds
       temperature = 0, -- adjust if needed
       max_tokens = 4096,
+      reasoning_effort = "high" -- only supported for "o" models
     },
   },
   -- if you want to build from source then do `make BUILD_FROM_SOURCE=true`
diff --git a/lua/avante/providers/init.lua b/lua/avante/providers/init.lua
index 3fa7915..91aa37d 100644
--- a/lua/avante/providers/init.lua
+++ b/lua/avante/providers/init.lua
@@ -64,6 +64,7 @@ local DressingState = { winid = nil, input_winid = nil, input_bufnr = nil }
 ---@field __inherited_from? string
 ---@field temperature? number
 ---@field max_tokens? number
+---@field reasoning_effort? string
 ---
 ---@class AvanteLLMUsage
 ---@field input_tokens number

From 6fd82f24dd63e4561066a9b27b461d9b4743ee21 Mon Sep 17 00:00:00 2001
From: kyrisu <krystian.paszkiewicz@gmail.com>
Date: Mon, 17 Feb 2025 04:53:15 +0000
Subject: [PATCH 37/39] fix: correct typo in answer field name in tavily
 format_response_body function (#1287)

---
 lua/avante/config.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 504f8fb..2bcbcc6 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -36,7 +36,7 @@ M._defaults = {
           include_answer = "basic",
         },
         ---@type WebSearchEngineProviderResponseBodyFormatter
-        format_response_body = function(body) return body.anwser, nil end,
+        format_response_body = function(body) return body.answer, nil end,
       },
       serpapi = {
         api_key_name = "SERPAPI_API_KEY",

From f70eb1040c24267a8f260183804cf7e01a7b1f17 Mon Sep 17 00:00:00 2001
From: 8uff3r <8uff3r@gmail.com>
Date: Mon, 17 Feb 2025 08:24:58 +0330
Subject: [PATCH 38/39] fix(providers/openai): check for vim.NIl on tool_calls
 (#1283)

---
 lua/avante/providers/openai.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lua/avante/providers/openai.lua b/lua/avante/providers/openai.lua
index d03541b..8e95a7a 100644
--- a/lua/avante/providers/openai.lua
+++ b/lua/avante/providers/openai.lua
@@ -227,7 +227,7 @@ M.parse_response = function(ctx, data_stream, _, opts)
         end
         ctx.last_think_content = choice.delta.reasoning
         opts.on_chunk(choice.delta.reasoning)
-      elseif choice.delta.tool_calls then
+      elseif choice.delta.tool_calls and choice.delta.tool_calls ~= vim.NIL then
         local tool_call = choice.delta.tool_calls[1]
         if not ctx.tool_use_list then ctx.tool_use_list = {} end
         if not ctx.tool_use_list[tool_call.index + 1] then

From b6ae4dfe7fe443362f5f31d71797173ec12c2598 Mon Sep 17 00:00:00 2001
From: phyer <phyer@sina.com>
Date: Mon, 17 Feb 2025 12:56:09 +0800
Subject: [PATCH 39/39] fix: use absolute paths to handle gitignore

Co-authored-by: zhangkun9038@dingtalk.com <zhangkun9038@dingtalk.com>
---
 lua/avante/utils/init.lua | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua
index 7e97297..fef7648 100644
--- a/lua/avante/utils/init.lua
+++ b/lua/avante/utils/init.lua
@@ -665,6 +665,19 @@ function M.scan_directory_respect_gitignore(options)
   local directory = options.directory
   local gitignore_path = directory .. "/.gitignore"
   local gitignore_patterns, gitignore_negate_patterns = M.parse_gitignore(gitignore_path)
+
+  -- Convert relative paths in gitignore to absolute paths based on project root
+  local project_root = M.get_project_root()
+  local function to_absolute_path(pattern)
+    -- Skip if already absolute path
+    if pattern:sub(1, 1) == "/" then return pattern end
+    -- Convert relative path to absolute
+    return Path:new(project_root, pattern):absolute()
+  end
+
+  gitignore_patterns = vim.tbl_map(to_absolute_path, gitignore_patterns)
+  gitignore_negate_patterns = vim.tbl_map(to_absolute_path, gitignore_negate_patterns)
+
   return M.scan_directory({
     directory = directory,
     gitignore_patterns = gitignore_patterns,