From f23b7133cbb4724143a58b4828dc58aea604630d Mon Sep 17 00:00:00 2001 From: kumarabhirup Date: Mon, 2 Mar 2026 18:34:34 -0800 Subject: [PATCH] feat(web): improve suggest-files and tree route with better error handling --- .../app/api/workspace/suggest-files/route.ts | 23 ++++++++++++++----- apps/web/app/api/workspace/tree/route.ts | 3 +++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/web/app/api/workspace/suggest-files/route.ts b/apps/web/app/api/workspace/suggest-files/route.ts index 57bd1dfd45b..8a39e029299 100644 --- a/apps/web/app/api/workspace/suggest-files/route.ts +++ b/apps/web/app/api/workspace/suggest-files/route.ts @@ -34,7 +34,11 @@ const SKIP_DIRS = new Set([ ]); /** List entries in a directory, sorted folders-first then alphabetically. */ -function listDir(absDir: string, filter?: string): SuggestItem[] { +function listDir( + absDir: string, + filter?: string, + workspaceRoot?: string, +): SuggestItem[] { let entries: Dirent[]; try { entries = readdirSync(absDir, { withFileTypes: true }); @@ -43,9 +47,13 @@ function listDir(absDir: string, filter?: string): SuggestItem[] { } const lowerFilter = filter?.toLowerCase(); + const hideRootIdentity = + typeof workspaceRoot === "string" && + resolve(absDir) === resolve(workspaceRoot); const sorted = entries .filter((e) => !e.name.startsWith(".")) + .filter((e) => !(hideRootIdentity && e.name === "IDENTITY.md")) .filter((e) => !(e.isDirectory() && SKIP_DIRS.has(e.name))) .filter((e) => !lowerFilter || e.name.toLowerCase().includes(lowerFilter)) .toSorted((a, b) => { @@ -82,6 +90,7 @@ function searchFiles( query: string, results: SuggestItem[], maxResults: number, + workspaceRoot: string, depth = 0, ): void { if (depth > 6 || results.length >= maxResults) {return;} @@ -94,10 +103,12 @@ function searchFiles( } const lowerQuery = query.toLowerCase(); + const hideRootIdentity = resolve(absDir) === resolve(workspaceRoot); for (const entry of entries) { if (results.length >= maxResults) {return;} if (entry.name.startsWith(".")) {continue;} + if (hideRootIdentity && entry.name === "IDENTITY.md") {continue;} if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) {continue;} const absPath = join(absDir, entry.name); @@ -120,7 +131,7 @@ function searchFiles( } if (entry.isDirectory()) { - searchFiles(absPath, query, results, maxResults, depth + 1); + searchFiles(absPath, query, results, maxResults, workspaceRoot, depth + 1); } } } @@ -393,7 +404,7 @@ export async function GET(req: Request) { if (searchQuery) { // File search: workspace only (skip expensive home dir traversal) const fileResults: SuggestItem[] = []; - searchFiles(workspaceRoot, searchQuery, fileResults, 15); + searchFiles(workspaceRoot, searchQuery, fileResults, 15, workspaceRoot); // DuckDB search: objects and entries (sequential to avoid lock contention) const objectResults = await searchObjects(searchQuery, workspaceRoot, 10); @@ -415,15 +426,15 @@ export async function GET(req: Request) { const resolved = resolvePath(pathQuery, workspaceRoot); if (!resolved) { const results: SuggestItem[] = []; - searchFiles(workspaceRoot, pathQuery, results, 20); + searchFiles(workspaceRoot, pathQuery, results, 20, workspaceRoot); return Response.json({ items: results }); } - const items = listDir(resolved.dir, resolved.filter); + const items = listDir(resolved.dir, resolved.filter, workspaceRoot); return Response.json({ items }); } // Default: list workspace root + all objects - const fileItems = listDir(workspaceRoot); + const fileItems = listDir(workspaceRoot, undefined, workspaceRoot); const objectItems = await searchObjects("", workspaceRoot, 20); // Deduplicate: if an object also appears as a folder, keep the object version const objectNames = new Set(objectItems.map((o) => o.name)); diff --git a/apps/web/app/api/workspace/tree/route.ts b/apps/web/app/api/workspace/tree/route.ts index c603d38b4eb..c28fdb3525b 100644 --- a/apps/web/app/api/workspace/tree/route.ts +++ b/apps/web/app/api/workspace/tree/route.ts @@ -115,6 +115,8 @@ function buildTree( for (const entry of sorted) { // .object.yaml is consumed for metadata; only show it as a visible node when revealing hidden files if (entry.name === ".object.yaml" && !showHidden) {continue;} + // Keep the root identity system file out of the workspace sidebar tree. + if (!relativeBase && entry.name === "IDENTITY.md") {continue;} const absPath = join(absDir, entry.name); const relPath = relativeBase @@ -200,6 +202,7 @@ function buildSkillsVirtualFolder(): TreeNode | null { const entries = readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (!entry.isDirectory() || seen.has(entry.name)) {continue;} + if (entry.name === "dench") {continue;} const skillMdPath = join(dir, entry.name, "SKILL.md"); if (!existsSync(skillMdPath)) {continue;}