11import { readFileSync , writeFileSync , readdirSync , unlinkSync , existsSync } from "fs"
22import { join , basename } from "path"
3- import { getMemoryDir , getMemoryEntrypoint , ENTRYPOINT_NAME } from "./paths.js"
3+ import {
4+ getMemoryDir ,
5+ getMemoryEntrypoint ,
6+ ENTRYPOINT_NAME ,
7+ validateMemoryFileName ,
8+ MAX_MEMORY_FILES ,
9+ MAX_MEMORY_FILE_BYTES ,
10+ FRONTMATTER_MAX_LINES ,
11+ } from "./paths.js"
412
513export const MEMORY_TYPES = [ "user" , "feedback" , "project" , "reference" ] as const
614export type MemoryType = ( typeof MEMORY_TYPES ) [ number ]
@@ -21,11 +29,20 @@ function parseFrontmatter(raw: string): { frontmatter: Record<string, string>; c
2129 return { frontmatter : { } , content : trimmed }
2230 }
2331
24- const endIndex = trimmed . indexOf ( "---" , 3 )
25- if ( endIndex === - 1 ) {
32+ const lines = trimmed . split ( "\n" )
33+ let closingLineIdx = - 1
34+ for ( let i = 1 ; i < Math . min ( lines . length , FRONTMATTER_MAX_LINES ) ; i ++ ) {
35+ if ( lines [ i ] . trimEnd ( ) === "---" ) {
36+ closingLineIdx = i
37+ break
38+ }
39+ }
40+ if ( closingLineIdx === - 1 ) {
2641 return { frontmatter : { } , content : trimmed }
2742 }
2843
44+ const endIndex = lines . slice ( 0 , closingLineIdx ) . join ( "\n" ) . length + 1
45+
2946 const frontmatterBlock = trimmed . slice ( 3 , endIndex ) . trim ( )
3047 const content = trimmed . slice ( endIndex + 3 ) . trim ( )
3148
@@ -61,6 +78,7 @@ export function listMemories(worktree: string): MemoryEntry[] {
6178 files = readdirSync ( memDir )
6279 . filter ( ( f ) => f . endsWith ( ".md" ) && f !== ENTRYPOINT_NAME )
6380 . sort ( )
81+ . slice ( 0 , MAX_MEMORY_FILES )
6482 } catch {
6583 return entries
6684 }
@@ -88,8 +106,9 @@ export function listMemories(worktree: string): MemoryEntry[] {
88106}
89107
90108export function readMemory ( worktree : string , fileName : string ) : MemoryEntry | null {
109+ const safeName = validateMemoryFileName ( fileName )
91110 const memDir = getMemoryDir ( worktree )
92- const filePath = join ( memDir , fileName . endsWith ( ".md" ) ? fileName : ` ${ fileName } .md` )
111+ const filePath = join ( memDir , safeName )
93112
94113 try {
95114 const rawContent = readFileSync ( filePath , "utf-8" )
@@ -116,11 +135,16 @@ export function saveMemory(
116135 type : MemoryType ,
117136 content : string ,
118137) : string {
138+ const safeName = validateMemoryFileName ( fileName )
119139 const memDir = getMemoryDir ( worktree )
120- const safeName = fileName . endsWith ( ".md" ) ? fileName : `${ fileName } .md`
121140 const filePath = join ( memDir , safeName )
122141
123142 const fileContent = `${ buildFrontmatter ( name , description , type ) } \n\n${ content . trim ( ) } \n`
143+ if ( Buffer . byteLength ( fileContent , "utf-8" ) > MAX_MEMORY_FILE_BYTES ) {
144+ throw new Error (
145+ `Memory file content exceeds the ${ MAX_MEMORY_FILE_BYTES } -byte limit` ,
146+ )
147+ }
124148 writeFileSync ( filePath , fileContent , "utf-8" )
125149
126150 updateIndex ( worktree , safeName , name , description )
@@ -129,8 +153,8 @@ export function saveMemory(
129153}
130154
131155export function deleteMemory ( worktree : string , fileName : string ) : boolean {
156+ const safeName = validateMemoryFileName ( fileName )
132157 const memDir = getMemoryDir ( worktree )
133- const safeName = fileName . endsWith ( ".md" ) ? fileName : `${ fileName } .md`
134158 const filePath = join ( memDir , safeName )
135159
136160 try {
0 commit comments