mirror of
https://github.com/codeaashu/claude-code.git
synced 2026-04-11 07:29:54 +03:00
claude-code
This commit is contained in:
95
src/memdir/memoryScan.ts
Normal file
95
src/memdir/memoryScan.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user