(params: {
fs: CodebuffFileSystem
logger: Logger
/** Optional home directory override for testing */
homeDir?: string
})
| 347 | * @internal Exported for testing |
| 348 | */ |
| 349 | export async function loadUserKnowledgeFiles(params: { |
| 350 | fs: CodebuffFileSystem |
| 351 | logger: Logger |
| 352 | /** Optional home directory override for testing */ |
| 353 | homeDir?: string |
| 354 | }): Promise<Record<string, string>> { |
| 355 | const { fs, logger } = params |
| 356 | const homeDir = params.homeDir ?? os.homedir() |
| 357 | const userKnowledgeFiles: Record<string, string> = {} |
| 358 | |
| 359 | // List home directory to find knowledge files case-insensitively |
| 360 | let entries: string[] |
| 361 | try { |
| 362 | entries = await fs.readdir(homeDir) |
| 363 | } catch (error) { |
| 364 | logger.debug?.( |
| 365 | { homeDir, error: getErrorObject(error) }, |
| 366 | 'Failed to read home directory', |
| 367 | ) |
| 368 | return userKnowledgeFiles |
| 369 | } |
| 370 | |
| 371 | // Find hidden files that match our knowledge file patterns (case-insensitive) |
| 372 | // Build a map of lowercase name -> actual filename for priority selection |
| 373 | const candidates = new Map<string, string>() |
| 374 | for (const entry of entries) { |
| 375 | if (!entry.startsWith('.')) continue |
| 376 | const nameWithoutDot = entry.slice(1) // Remove leading dot |
| 377 | const lowerName = nameWithoutDot.toLowerCase() |
| 378 | if (KNOWLEDGE_FILE_NAMES_LOWERCASE.includes(lowerName)) { |
| 379 | candidates.set(lowerName, entry) |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | // Select highest priority file (priority: knowledge.md > AGENTS.md > CLAUDE.md) |
| 384 | for (const priorityName of KNOWLEDGE_FILE_NAMES_LOWERCASE) { |
| 385 | const actualFileName = candidates.get(priorityName) |
| 386 | if (actualFileName) { |
| 387 | const filePath = path.join(homeDir, actualFileName) |
| 388 | try { |
| 389 | const content = await fs.readFile(filePath, 'utf8') |
| 390 | // Use tilde notation with the actual filename (preserving case) |
| 391 | const tildeKey = `~/${actualFileName}` |
| 392 | userKnowledgeFiles[tildeKey] = content |
| 393 | // Only use the first file found (highest priority) |
| 394 | break |
| 395 | } catch (error) { |
| 396 | logger.debug?.( |
| 397 | { filePath, error: getErrorObject(error) }, |
| 398 | 'Failed to read user knowledge file', |
| 399 | ) |
| 400 | } |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | return userKnowledgeFiles |
| 405 | } |
| 406 |
no test coverage detected