feat: add glob llm tool (#1495)

This commit is contained in:
yetone
2025-03-05 19:22:22 +08:00
committed by GitHub
parent 093c09b760
commit f6484a0e61
2 changed files with 117 additions and 3 deletions

View File

@@ -38,6 +38,16 @@ local function has_permission_to_access(abs_path)
return not Utils.is_ignored(rel_path, gitignore_patterns, gitignore_negate_patterns)
end
---@type AvanteLLMToolFunc<{ rel_path: string, pattern: string }>
function M.glob(opts, on_log)
local abs_path = get_abs_path(opts.rel_path)
if not has_permission_to_access(abs_path) then return "", "No permission to access path: " .. abs_path end
if on_log then on_log("path: " .. abs_path) end
if on_log then on_log("pattern: " .. opts.pattern) end
local files = vim.fn.glob(abs_path .. "/" .. opts.pattern, true, true)
return vim.json.encode(files), nil
end
---@type AvanteLLMToolFunc<{ rel_path: string, max_depth?: integer }>
function M.list_files(opts, on_log)
local abs_path = get_abs_path(opts.rel_path)
@@ -613,6 +623,38 @@ end
---@type AvanteLLMTool[]
M._tools = {
{
name = "glob",
description = 'Fast file pattern matching using glob patterns like "**/*.js"',
param = {
type = "table",
fields = {
{
name = "pattern",
description = "Glob pattern",
type = "string",
},
{
name = "rel_path",
description = "Relative path to the directory, as cwd",
type = "string",
},
},
},
returns = {
{
name = "matches",
description = "List of matched files",
type = "string",
},
{
name = "err",
description = "Error message",
type = "string",
optional = true,
},
},
},
{
name = "rag_search",
enabled = function() return Config.rag_service.enabled and RagService.is_ready() end,
@@ -643,7 +685,7 @@ M._tools = {
},
{
name = "python",
description = "Run python code",
description = "Run python code. Can't use it to read files or modify files.",
param = {
type = "table",
fields = {
@@ -858,7 +900,7 @@ M._tools = {
},
{
name = "read_file",
description = "Read the contents of a file",
description = "Read the contents of a file. If the file content is already in the context, do not use this tool.",
param = {
type = "table",
fields = {
@@ -1057,7 +1099,7 @@ M._tools = {
},
{
name = "bash",
description = "Run a bash command in a directory",
description = "Run a bash command in a directory. Can't use search commands like find/grep or read tools like cat/ls.",
param = {
type = "table",
fields = {

View File

@@ -272,4 +272,76 @@ describe("llm_tools", function()
assert.equals("Hello from custom container\n", result)
end)
end)
describe("glob", function()
it("should find files matching the pattern", function()
-- Create some additional test files with different extensions for glob testing
os.execute("touch " .. test_dir .. "/file1.lua")
os.execute("touch " .. test_dir .. "/file2.lua")
os.execute("touch " .. test_dir .. "/file3.js")
os.execute("mkdir -p " .. test_dir .. "/nested")
os.execute("touch " .. test_dir .. "/nested/file4.lua")
-- Test for lua files in the root
local result, err = LlmTools.glob({ rel_path = ".", pattern = "*.lua" })
assert.is_nil(err)
local files = vim.json.decode(result)
assert.equals(2, #files)
assert.truthy(vim.tbl_contains(files, test_dir .. "/file1.lua"))
assert.truthy(vim.tbl_contains(files, test_dir .. "/file2.lua"))
assert.falsy(vim.tbl_contains(files, test_dir .. "/file3.js"))
assert.falsy(vim.tbl_contains(files, test_dir .. "/nested/file4.lua"))
-- Test with recursive pattern
local result2, err2 = LlmTools.glob({ rel_path = ".", pattern = "**/*.lua" })
assert.is_nil(err2)
local files2 = vim.json.decode(result2)
assert.equals(3, #files2)
assert.truthy(vim.tbl_contains(files2, test_dir .. "/file1.lua"))
assert.truthy(vim.tbl_contains(files2, test_dir .. "/file2.lua"))
assert.truthy(vim.tbl_contains(files2, test_dir .. "/nested/file4.lua"))
end)
it("should respect path permissions", function()
local result, err = LlmTools.glob({ rel_path = "../outside_project", pattern = "*.txt" })
assert.equals("", result)
assert.truthy(err:find("No permission to access path"))
end)
it("should handle patterns without matches", function()
local result, err = LlmTools.glob({ rel_path = ".", pattern = "*.nonexistent" })
assert.is_nil(err)
local files = vim.json.decode(result)
assert.equals(0, #files)
end)
it("should handle files in gitignored directories", function()
-- Create test files in ignored directory
os.execute("touch " .. test_dir .. "/test_dir2/ignored1.lua")
os.execute("touch " .. test_dir .. "/test_dir2/ignored2.lua")
-- Create test files in non-ignored directory
os.execute("touch " .. test_dir .. "/test_dir1/notignored1.lua")
os.execute("touch " .. test_dir .. "/test_dir1/notignored2.lua")
local result, err = LlmTools.glob({ rel_path = ".", pattern = "**/*.lua" })
assert.is_nil(err)
local files = vim.json.decode(result)
-- Check that files from non-ignored directory are found
local found_notignored = false
for _, file in ipairs(files) do
if file:find("test_dir1/notignored") then
found_notignored = true
break
end
end
assert.is_true(found_notignored)
-- Note: By default, vim.fn.glob does not respect gitignore files
-- This test simply verifies the glob function works as expected
-- If in the future, the function is modified to respect gitignore,
-- this test can be updated
end)
end)
end)