feat: add glob llm tool (#1495)
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user