feat(web): improve suggest-files and tree route with better error handling

This commit is contained in:
kumarabhirup 2026-03-02 18:34:34 -08:00
parent f3fffec97f
commit f23b7133cb
No known key found for this signature in database
GPG Key ID: DB7CA2289CAB0167
2 changed files with 20 additions and 6 deletions

View File

@ -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));

View File

@ -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;}