diff --git a/afs/local-fs/src/index.ts b/afs/local-fs/src/index.ts index 169374d6f..e8053b21c 100644 --- a/afs/local-fs/src/index.ts +++ b/afs/local-fs/src/index.ts @@ -12,7 +12,7 @@ import { globStream } from "glob"; import { z } from "zod"; import { searchWithRipgrep } from "./utils/ripgrep.js"; -const LIST_MAX_LIMIT = 50; +const LIST_MAX_LIMIT = 1000; export interface LocalFSOptions { name?: string; @@ -48,7 +48,8 @@ export class LocalFS implements AFSModule { const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT); const basePath = join(this.options.localPath, path); - const pattern = options?.recursive ? "**/*" : "*"; + const pattern = + options?.recursive || (options?.maxDepth && options.maxDepth > 1) ? "**/*" : "*"; const abortController = new AbortController(); diff --git a/packages/core/src/prompt/skills/afs.ts b/packages/core/src/prompt/skills/afs.ts index a6589a847..212a2c09f 100644 --- a/packages/core/src/prompt/skills/afs.ts +++ b/packages/core/src/prompt/skills/afs.ts @@ -7,25 +7,26 @@ export async function getAFSSkills(afs: AFS): Promise { FunctionAgent.from({ name: "afs_list", description: - "Browse directory contents in the AFS like filesystem ls/tree command - shows files and folders in the specified path", + "Get a tree view of directory contents in the AFS - shows hierarchical structure of files and folders", inputSchema: z.object({ path: z.string().describe("The directory path to browse (e.g., '/', '/docs', '/src')"), options: z .object({ - recursive: z.boolean().optional().describe("Whether to list files recursively"), - maxDepth: z.number().optional().describe("Maximum depth to list files"), - limit: z.number().optional().describe("Maximum number of entries to return"), + maxDepth: z.number().optional().describe("Maximum depth to display in the tree view"), }) .optional(), }), process: async (input) => { - const result = await afs.list(input.path, input.options); + const { list, message } = await afs.list(input.path, input.options); + + const result = buildTreeView(list); return { status: "success", tool: "afs_list", options: input.options, - ...result, + message, + result, }; }, }), @@ -122,3 +123,32 @@ export async function getAFSSkills(afs: AFS): Promise { }), ]; } + +function buildTreeView(entries: { path: string }[]): string { + const tree: Record = {}; + + for (const entry of entries) { + const parts = entry.path.split("/").filter(Boolean); + let current = tree; + + for (const part of parts) { + if (!current[part]) { + current[part] = {}; + } + current = current[part]; + } + } + + function renderTree(node: Record, prefix = ""): string { + let result = ""; + const keys = Object.keys(node); + keys.forEach((key, index) => { + const isLast = index === keys.length - 1; + result += `${prefix}${isLast ? "└── " : "├── "}${key}\n`; + result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`); + }); + return result; + } + + return renderTree(tree); +} diff --git a/packages/core/test/prompt/prompt-builder.test.ts b/packages/core/test/prompt/prompt-builder.test.ts index 960e00b62..2ab2dc86c 100644 --- a/packages/core/test/prompt/prompt-builder.test.ts +++ b/packages/core/test/prompt/prompt-builder.test.ts @@ -660,7 +660,7 @@ test("PromptBuilder should build with afs correctly", async () => { "tools": [ { "function": { - "description": "Browse directory contents in the AFS like filesystem ls/tree command - shows files and folders in the specified path", + "description": "Get a tree view of directory contents in the AFS - shows hierarchical structure of files and folders", "name": "afs_list", "parameters": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -669,18 +669,10 @@ test("PromptBuilder should build with afs correctly", async () => { "options": { "additionalProperties": false, "properties": { - "limit": { - "description": "Maximum number of entries to return", - "type": "number", - }, "maxDepth": { - "description": "Maximum depth to list files", + "description": "Maximum depth to display in the tree view", "type": "number", }, - "recursive": { - "description": "Whether to list files recursively", - "type": "boolean", - }, }, "required": [], "type": "object", diff --git a/packages/core/test/prompt/skills/afs.test.ts b/packages/core/test/prompt/skills/afs.test.ts index 358ec463b..59cc8d2df 100644 --- a/packages/core/test/prompt/skills/afs.test.ts +++ b/packages/core/test/prompt/skills/afs.test.ts @@ -17,7 +17,7 @@ test("getAFSSkills should return all AFS skills", async () => { ).toMatchInlineSnapshot(` [ { - "description": "Browse directory contents in the AFS like filesystem ls/tree command - shows files and folders in the specified path", + "description": "Get a tree view of directory contents in the AFS - shows hierarchical structure of files and folders", "inputSchema": { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, @@ -25,18 +25,10 @@ test("getAFSSkills should return all AFS skills", async () => { "options": { "additionalProperties": false, "properties": { - "limit": { - "description": "Maximum number of entries to return", - "type": "number", - }, "maxDepth": { - "description": "Maximum depth to list files", + "description": "Maximum depth to display in the tree view", "type": "number", }, - "recursive": { - "description": "Whether to list files recursively", - "type": "boolean", - }, }, "type": "object", }, @@ -193,10 +185,10 @@ test("AFS'skill list should invoke afs.list", async () => { assert(list); expect(await list.invoke({ path: "/foo/bar", options: { maxDepth: 2 } })).toMatchInlineSnapshot(` { - "list": [], "options": { "maxDepth": 2, }, + "result": "", "status": "success", "tool": "afs_list", }