e22d123009
- Converted readdirSync, readFileSync, and statSync to async readdir, readFile, stat across skills and agent discovery. - Made scanDirectoryForSkills async and refactored custom directory scanning to use Promise.all for concurrent processing. - Updated agent discovery to use fs/promises for async file reading and refactored helper patterns. - Added AgentParsingError exception class for better error handling during agent parsing. - Added filesystem error type guards (isEnoent, isEacces, isPerm, etc.) to pi-utils for safe error checking. - Added color manipulation utilities to pi-utils for accessibility features. - Added color-blind mode setting to settings manager. - Migrated plugins, settings, and config modules from sync to async file operations. - Updated error handling to use new pi-utils type guards for type-safe checking.
90 lines
2.1 KiB
TypeScript
90 lines
2.1 KiB
TypeScript
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
|
|
const contentCache = new Map<string, string | null>();
|
|
const dirCache = new Map<string, fs.Dirent[]>();
|
|
|
|
function resolvePath(filePath: string): string {
|
|
return path.resolve(filePath);
|
|
}
|
|
|
|
export async function readFile(filePath: string): Promise<string | null> {
|
|
const abs = resolvePath(filePath);
|
|
if (contentCache.has(abs)) {
|
|
return contentCache.get(abs) ?? null;
|
|
}
|
|
|
|
try {
|
|
const content = await Bun.file(abs).text();
|
|
contentCache.set(abs, content);
|
|
return content;
|
|
} catch {
|
|
contentCache.set(abs, null);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function readDirEntries(dirPath: string): Promise<fs.Dirent[]> {
|
|
const abs = resolvePath(dirPath);
|
|
if (dirCache.has(abs)) {
|
|
return dirCache.get(abs) ?? [];
|
|
}
|
|
|
|
try {
|
|
const entries = await fs.promises.readdir(abs, { withFileTypes: true });
|
|
dirCache.set(abs, entries);
|
|
return entries;
|
|
} catch {
|
|
dirCache.set(abs, []);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function readDir(dirPath: string): Promise<string[]> {
|
|
const entries = await readDirEntries(dirPath);
|
|
return entries.map((entry) => entry.name);
|
|
}
|
|
|
|
export async function walkUp(
|
|
startDir: string,
|
|
name: string,
|
|
opts: { file?: boolean; dir?: boolean } = {},
|
|
): Promise<string | null> {
|
|
const { file = true, dir = true } = opts;
|
|
let current = resolvePath(startDir);
|
|
|
|
while (true) {
|
|
const entries = await readDirEntries(current);
|
|
const entry = entries.find((e) => e.name === name);
|
|
if (entry) {
|
|
if (file && entry.isFile()) return path.join(current, name);
|
|
if (dir && entry.isDirectory()) return path.join(current, name);
|
|
}
|
|
const parent = path.dirname(current);
|
|
if (parent === current) return null;
|
|
current = parent;
|
|
}
|
|
}
|
|
|
|
export function cacheStats(): { content: number; dir: number } {
|
|
return {
|
|
content: contentCache.size,
|
|
dir: dirCache.size,
|
|
};
|
|
}
|
|
|
|
export function clearCache(): void {
|
|
contentCache.clear();
|
|
dirCache.clear();
|
|
}
|
|
|
|
export function invalidate(filePath: string): void {
|
|
const abs = resolvePath(filePath);
|
|
contentCache.delete(abs);
|
|
dirCache.delete(abs);
|
|
const parent = path.dirname(abs);
|
|
if (parent !== abs) {
|
|
dirCache.delete(parent);
|
|
}
|
|
}
|