Download src/memdir/memoryScan.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 3.11 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/memdir/memoryScan.ts
- Command line
-
hf download hf://chenbhao/codev/src/memdir/memoryScan.ts
-
curl -L -o memoryScan.ts https://huggingface.co/chenbhao/codev/resolve/main/src/memdir/memoryScan.ts
3.11 kB
| /** | |
| * Memory-directory scanning primitives. Split out of findRelevantMemories.ts | |
| * so extractMemories can import the scan without pulling in sideQuery and | |
| * the API-client chain (which closed a cycle through memdir.ts — #25372). | |
| */ | |
| import { readdir } from 'fs/promises' | |
| import { basename, join } from 'path' | |
| import { parseFrontmatter } from '../utils/frontmatterParser.js' | |
| import { readFileInRange } from '../utils/readFileInRange.js' | |
| import { type MemoryType, parseMemoryType } from './memoryTypes.js' | |
| export type MemoryHeader = { | |
| filename: string | |
| filePath: string | |
| mtimeMs: number | |
| description: string | null | |
| type: MemoryType | undefined | |
| } | |
| const MAX_MEMORY_FILES = 200 | |
| const FRONTMATTER_MAX_LINES = 30 | |
| /** | |
| * Scan a memory directory for .md files, read their frontmatter, and return | |
| * a header list sorted newest-first (capped at MAX_MEMORY_FILES). Shared by | |
| * findRelevantMemories (query-time recall) and extractMemories (pre-injects | |
| * the listing so the extraction agent doesn't spend a turn on `ls`). | |
| * | |
| * Single-pass: readFileInRange stats internally and returns mtimeMs, so we | |
| * read-then-sort rather than stat-sort-read. For the common case (N ≤ 200) | |
| * this halves syscalls vs a separate stat round; for large N we read a few | |
| * extra small files but still avoid the double-stat on the surviving 200. | |
| */ | |
| export async function scanMemoryFiles( | |
| memoryDir: string, | |
| signal: AbortSignal, | |
| ): Promise<MemoryHeader[]> { | |
| try { | |
| const entries = await readdir(memoryDir, { recursive: true }) | |
| const mdFiles = entries.filter( | |
| f => f.endsWith('.md') && basename(f) !== 'MEMORY.md', | |
| ) | |
| const headerResults = await Promise.allSettled( | |
| mdFiles.map(async (relativePath): Promise<MemoryHeader> => { | |
| const filePath = join(memoryDir, relativePath) | |
| const { content, mtimeMs } = await readFileInRange( | |
| filePath, | |
| 0, | |
| FRONTMATTER_MAX_LINES, | |
| undefined, | |
| signal, | |
| ) | |
| const { frontmatter } = parseFrontmatter(content, filePath) | |
| return { | |
| filename: relativePath, | |
| filePath, | |
| mtimeMs, | |
| description: frontmatter.description || null, | |
| type: parseMemoryType(frontmatter.type), | |
| } | |
| }), | |
| ) | |
| return headerResults | |
| .filter( | |
| (r): r is PromiseFulfilledResult<MemoryHeader> => | |
| r.status === 'fulfilled', | |
| ) | |
| .map(r => r.value) | |
| .sort((a, b) => b.mtimeMs - a.mtimeMs) | |
| .slice(0, MAX_MEMORY_FILES) | |
| } catch { | |
| return [] | |
| } | |
| } | |
| /** | |
| * Format memory headers as a text manifest: one line per file with | |
| * [type] filename (timestamp): description. Used by both the recall | |
| * selector prompt and the extraction-agent prompt. | |
| */ | |
| export function formatMemoryManifest(memories: MemoryHeader[]): string { | |
| return memories | |
| .map(m => { | |
| const tag = m.type ? `[${m.type}] ` : '' | |
| const ts = new Date(m.mtimeMs).toISOString() | |
| return m.description | |
| ? `- ${tag}${m.filename} (${ts}): ${m.description}` | |
| : `- ${tag}${m.filename} (${ts})` | |
| }) | |
| .join('\n') | |
| } | |