Feat add autocomplete (#18)
Co-authored-by: Shubham Naik <shub@memgpt.ai>
This commit is contained in:
@@ -201,11 +201,13 @@ export function onChunk(
|
||||
// TODO remove once SDK v1 has proper typing for in-stream errors
|
||||
// Check for streaming error objects (not typed in SDK but emitted by backend)
|
||||
// These are emitted when LLM errors occur during streaming (rate limits, timeouts, etc.)
|
||||
const chunkAny = chunk as any;
|
||||
if (chunkAny.error && !chunk.messageType) {
|
||||
const chunkWithError = chunk as typeof chunk & {
|
||||
error?: { message?: string; detail?: string };
|
||||
};
|
||||
if (chunkWithError.error && !chunk.messageType) {
|
||||
const errorId = `err-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const errorMsg = chunkAny.error.message || "An error occurred";
|
||||
const errorDetail = chunkAny.error.detail || "";
|
||||
const errorMsg = chunkWithError.error.message || "An error occurred";
|
||||
const errorDetail = chunkWithError.error.detail || "";
|
||||
const fullErrorText = errorDetail
|
||||
? `${errorMsg}: ${errorDetail}`
|
||||
: errorMsg;
|
||||
|
||||
176
src/cli/helpers/fileSearch.ts
Normal file
176
src/cli/helpers/fileSearch.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import { readdirSync, statSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
interface FileMatch {
|
||||
path: string;
|
||||
type: "file" | "dir" | "url";
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively search a directory for files matching a pattern
|
||||
*/
|
||||
function searchDirectoryRecursive(
|
||||
dir: string,
|
||||
pattern: string,
|
||||
maxDepth: number = 3,
|
||||
currentDepth: number = 0,
|
||||
maxResults: number = 100,
|
||||
results: FileMatch[] = [],
|
||||
): FileMatch[] {
|
||||
if (currentDepth > maxDepth || results.length >= maxResults) {
|
||||
return results;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = readdirSync(dir);
|
||||
|
||||
for (const entry of entries) {
|
||||
// Skip hidden files and common ignore patterns
|
||||
if (
|
||||
entry.startsWith(".") ||
|
||||
entry === "node_modules" ||
|
||||
entry === "dist" ||
|
||||
entry === "build"
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const fullPath = join(dir, entry);
|
||||
const stats = statSync(fullPath);
|
||||
|
||||
// Check if entry matches the pattern
|
||||
const matches =
|
||||
pattern.length === 0 ||
|
||||
entry.toLowerCase().includes(pattern.toLowerCase());
|
||||
|
||||
if (matches) {
|
||||
const relativePath = fullPath.startsWith(process.cwd())
|
||||
? fullPath.slice(process.cwd().length + 1)
|
||||
: fullPath;
|
||||
|
||||
results.push({
|
||||
path: relativePath,
|
||||
type: stats.isDirectory() ? "dir" : "file",
|
||||
});
|
||||
|
||||
if (results.length >= maxResults) {
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively search subdirectories
|
||||
if (stats.isDirectory()) {
|
||||
searchDirectoryRecursive(
|
||||
fullPath,
|
||||
pattern,
|
||||
maxDepth,
|
||||
currentDepth + 1,
|
||||
maxResults,
|
||||
results,
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
} catch {
|
||||
// Can't read directory, skip
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for files and directories matching the query
|
||||
* @param query - The search query (partial file path)
|
||||
* @param deep - Whether to search recursively through subdirectories
|
||||
* @returns Array of matching files and directories
|
||||
*/
|
||||
export async function searchFiles(
|
||||
query: string,
|
||||
deep: boolean = false,
|
||||
): Promise<FileMatch[]> {
|
||||
const results: FileMatch[] = [];
|
||||
|
||||
try {
|
||||
// Determine the directory to search in
|
||||
let searchDir = process.cwd();
|
||||
let searchPattern = query;
|
||||
|
||||
// Handle relative paths like "./src" or "../test"
|
||||
if (query.includes("/")) {
|
||||
const lastSlashIndex = query.lastIndexOf("/");
|
||||
const dirPart = query.slice(0, lastSlashIndex);
|
||||
searchPattern = query.slice(lastSlashIndex + 1);
|
||||
|
||||
// Resolve the directory path
|
||||
try {
|
||||
searchDir = resolve(process.cwd(), dirPart);
|
||||
} catch {
|
||||
// If path doesn't exist, return empty results
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (deep) {
|
||||
// Deep search: recursively search subdirectories
|
||||
const deepResults = searchDirectoryRecursive(
|
||||
searchDir,
|
||||
searchPattern,
|
||||
3, // Max depth of 3 levels
|
||||
0,
|
||||
100, // Max 100 results
|
||||
);
|
||||
results.push(...deepResults);
|
||||
} else {
|
||||
// Shallow search: only current directory
|
||||
let entries: string[] = [];
|
||||
try {
|
||||
entries = readdirSync(searchDir);
|
||||
} catch {
|
||||
// Directory doesn't exist or can't be read
|
||||
return [];
|
||||
}
|
||||
|
||||
// Filter entries matching the search pattern
|
||||
// If pattern is empty, show all entries (for when user just types "@")
|
||||
const matchingEntries =
|
||||
searchPattern.length === 0
|
||||
? entries
|
||||
: entries.filter((entry) =>
|
||||
entry.toLowerCase().includes(searchPattern.toLowerCase()),
|
||||
);
|
||||
|
||||
// Get stats for each matching entry
|
||||
for (const entry of matchingEntries.slice(0, 50)) {
|
||||
// Limit to 50 results
|
||||
try {
|
||||
const fullPath = join(searchDir, entry);
|
||||
const stats = statSync(fullPath);
|
||||
|
||||
// Make path relative to cwd if possible
|
||||
const relativePath = fullPath.startsWith(process.cwd())
|
||||
? fullPath.slice(process.cwd().length + 1)
|
||||
: fullPath;
|
||||
|
||||
results.push({
|
||||
path: relativePath,
|
||||
type: stats.isDirectory() ? "dir" : "file",
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort: directories first, then files, alphabetically within each group
|
||||
results.sort((a, b) => {
|
||||
if (a.type === "dir" && b.type !== "dir") return -1;
|
||||
if (a.type !== "dir" && b.type === "dir") return 1;
|
||||
return a.path.localeCompare(b.path);
|
||||
});
|
||||
} catch (error) {
|
||||
// Return empty array on any error
|
||||
console.error("File search error:", error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
Reference in New Issue
Block a user