feat: Add list_files_tool for directory listing with optional recursion

This commit is contained in:
Slipstream 2025-05-28 15:52:04 -06:00
parent 9b82460bbf
commit cec4c6c60b
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
2 changed files with 78 additions and 1 deletions

View File

@ -1672,6 +1672,28 @@ def create_tools_list():
)
)
# --- List Files Tool ---
tool_declarations.append(
FunctionDeclaration(
name="list_files",
description="Lists files and directories within a specified path relative to the bot's working directory. Can list recursively.",
parameters={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The path to the directory to list contents for (e.g., '.', 'gurt/utils', '../some_other_project')."
},
"recursive": {
"type": "boolean",
"description": "Optional: Whether to list files recursively. Defaults to false (top-level only)."
}
},
"required": ["path"]
}
)
)
# --- Get User Highest Role Color ---
tool_declarations.append(
FunctionDeclaration(

View File

@ -2950,6 +2950,60 @@ TOOL_MAPPING = {
"get_user_highest_role_color": get_user_highest_role_color,
}
# --- List Files Tool ---
async def list_files_tool(cog: commands.Cog, path: str, recursive: bool = False) -> Dict[str, Any]:
"""Lists files and directories within a specified path."""
print(f"Executing list_files_tool: Path='{path}', Recursive={recursive}")
try:
# Normalize path relative to CWD
base_path = os.path.abspath(os.getcwd())
target_path = os.path.abspath(os.path.join(base_path, path))
print(f"Listing files for absolute path: {target_path}")
if not os.path.exists(target_path):
return {"error": f"Path not found: {target_path}", "path_requested": path}
if not os.path.isdir(target_path):
return {"error": f"Path is not a directory: {target_path}", "path_requested": path}
items = []
if recursive:
for root, dirs, files in os.walk(target_path):
# Add directories
for d_name in dirs:
full_d_path = os.path.join(root, d_name)
relative_d_path = os.path.relpath(full_d_path, base_path)
items.append({"name": d_name, "path": relative_d_path.replace('\\\\', '/'), "type": "directory"})
# Add files
for f_name in files:
full_f_path = os.path.join(root, f_name)
relative_f_path = os.path.relpath(full_f_path, base_path)
items.append({"name": f_name, "path": relative_f_path.replace('\\\\', '/'), "type": "file"})
else:
for item_name in os.listdir(target_path):
full_item_path = os.path.join(target_path, item_name)
relative_item_path = os.path.relpath(full_item_path, base_path)
item_type = "directory" if os.path.isdir(full_item_path) else "file"
items.append({"name": item_name, "path": relative_item_path.replace('\\\\', '/'), "type": item_type})
# Sort items for consistent output
items.sort(key=lambda x: (x["type"], x["path"]))
return {
"status": "success",
"path_listed": target_path,
"path_requested": path,
"recursive": recursive,
"items": items,
"count": len(items),
"timestamp": datetime.datetime.now().isoformat()
}
except PermissionError:
return {"error": f"Permission denied for path: {target_path}", "path_requested": path}
except Exception as e:
error_message = f"Unexpected error listing files for path '{path}': {str(e)}"
print(error_message); traceback.print_exc()
return {"error": error_message, "path_requested": path}
# --- Tenor GIF Search Tool Implementation ---
async def tool_search_tenor_gifs(cog: commands.Cog, query: str, limit: int = 8) -> Dict[str, Any]:
"""Searches Tenor for GIFs and returns a list of URLs."""
@ -3194,6 +3248,7 @@ async def send_tenor_gif(cog: commands.Cog, query: str, limit: int = 8) -> Dict[
"timestamp": datetime.datetime.now().isoformat()
}
# Update TOOL_MAPPING to include the new Tenor GIF tool
# Update TOOL_MAPPING to include the new Tenor GIF tool and list_files_tool
TOOL_MAPPING["search_tenor_gifs"] = tool_search_tenor_gifs
TOOL_MAPPING["send_tenor_gif"] = send_tenor_gif
TOOL_MAPPING["list_files"] = list_files_tool