|
| 1 | +import { StructuredTool } from "@langchain/core/tools"; |
| 2 | +import fsOperation from "fileSystem"; |
| 3 | +import { addedFolder } from "lib/openFolder"; |
| 4 | +import { z } from "zod"; |
| 5 | + |
| 6 | +/** |
| 7 | + * Tool for listing files and directories in a given path |
| 8 | + */ |
| 9 | +class ListDirectoryTool extends StructuredTool { |
| 10 | + name = "listDirectory"; |
| 11 | + description = "Lists files and directories in a given path"; |
| 12 | + schema = z.object({ |
| 13 | + path: z |
| 14 | + .string() |
| 15 | + .describe( |
| 16 | + "The relative path of the directory to list. This path must never be absolute. The first component of the path should always be the name of a root directory in the project (as shown in the sidebar). For example, if the root directories are 'directory1' and 'directory2', you can list the contents of 'directory1' by using the path 'directory1'. If the root directories are 'foo' and 'bar', and you want to list the contents of the directory 'foo/baz', you should use the path 'foo/baz'.", |
| 17 | + ), |
| 18 | + }); |
| 19 | + |
| 20 | + async _call({ path }) { |
| 21 | + try { |
| 22 | + // Handle special cases: ".", "", "./", "*" |
| 23 | + if (path === "." || path === "" || path === "./" || path === "*") { |
| 24 | + // List all root directories (project roots) |
| 25 | + const rootDirs = addedFolder |
| 26 | + .filter((folder) => folder && folder.title) |
| 27 | + .map((folder) => folder.title) |
| 28 | + .join("\n"); |
| 29 | + return rootDirs; |
| 30 | + } |
| 31 | + |
| 32 | + // Split the path to get project name and subpath |
| 33 | + const pathParts = path.split("/"); |
| 34 | + const projectName = pathParts[0]; |
| 35 | + const subPath = pathParts.slice(1).join("/"); |
| 36 | + |
| 37 | + // Find the project in addedFolder array |
| 38 | + const project = addedFolder.find( |
| 39 | + (folder) => folder.title === projectName, |
| 40 | + ); |
| 41 | + if (!project) { |
| 42 | + return `Error: Path '${path}' not found in opened projects`; |
| 43 | + } |
| 44 | + |
| 45 | + // Construct the full URL |
| 46 | + const dirUrl = subPath ? project.url + "/" + subPath : project.url; |
| 47 | + |
| 48 | + // List directory entries |
| 49 | + const entries = await fsOperation(dirUrl).lsDir(); |
| 50 | + if (!Array.isArray(entries)) { |
| 51 | + return `Error: Path not found: ${path}`; |
| 52 | + } |
| 53 | + |
| 54 | + // Separate folders and files |
| 55 | + const folders = []; |
| 56 | + const files = []; |
| 57 | + for (const entry of entries) { |
| 58 | + // Skip "." and ".." |
| 59 | + if (entry.name === "." || entry.name === "..") continue; |
| 60 | + const entryRelPath = subPath ? subPath + "/" + entry.name : entry.name; |
| 61 | + if (entry.isDirectory) { |
| 62 | + folders.push(`${projectName}/${entryRelPath}`); |
| 63 | + } else if (entry.isFile) { |
| 64 | + files.push(`${projectName}/${entryRelPath}`); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + let output = ""; |
| 69 | + if (folders.length > 0) { |
| 70 | + output += `# Folders:\n${folders.join("\n")}\n`; |
| 71 | + } |
| 72 | + if (files.length > 0) { |
| 73 | + output += `\n# Files:\n${files.join("\n")}\n`; |
| 74 | + } |
| 75 | + if (output.trim() === "") { |
| 76 | + output = `${path} is empty.`; |
| 77 | + } |
| 78 | + return output.trim(); |
| 79 | + } catch (error) { |
| 80 | + return `Error reading directory: ${error.message}`; |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +export const listDirectory = new ListDirectoryTool(); |
0 commit comments