@@ -124,7 +124,7 @@ def cmd_split(args):
124124 import sys
125125
126126 # Rebuild argv for split_mega_files argparse
127- argv = [args .dir ]
127+ argv = ["--source" , args .dir ]
128128 if args .output_dir :
129129 argv += ["--output-dir" , args .output_dir ]
130130 if args .dry_run :
@@ -147,6 +147,77 @@ def cmd_status(args):
147147 status (palace_path = palace_path )
148148
149149
150+ def cmd_repair (args ):
151+ """Rebuild palace vector index from SQLite metadata."""
152+ import chromadb
153+ import shutil
154+
155+ palace_path = os .path .expanduser (args .palace ) if args .palace else MempalaceConfig ().palace_path
156+
157+ if not os .path .isdir (palace_path ):
158+ print (f"\n No palace found at { palace_path } " )
159+ return
160+
161+ print (f"\n { '=' * 55 } " )
162+ print (" MemPalace Repair" )
163+ print (f"{ '=' * 55 } \n " )
164+ print (f" Palace: { palace_path } " )
165+
166+ # Try to read existing drawers
167+ try :
168+ client = chromadb .PersistentClient (path = palace_path )
169+ col = client .get_collection ("mempalace_drawers" )
170+ total = col .count ()
171+ print (f" Drawers found: { total } " )
172+ except Exception as e :
173+ print (f" Error reading palace: { e } " )
174+ print (" Cannot recover — palace may need to be re-mined from source files." )
175+ return
176+
177+ if total == 0 :
178+ print (" Nothing to repair." )
179+ return
180+
181+ # Extract all drawers in batches
182+ print ("\n Extracting drawers..." )
183+ batch_size = 5000
184+ all_ids = []
185+ all_docs = []
186+ all_metas = []
187+ offset = 0
188+ while offset < total :
189+ batch = col .get (limit = batch_size , offset = offset , include = ["documents" , "metadatas" ])
190+ all_ids .extend (batch ["ids" ])
191+ all_docs .extend (batch ["documents" ])
192+ all_metas .extend (batch ["metadatas" ])
193+ offset += batch_size
194+ print (f" Extracted { len (all_ids )} drawers" )
195+
196+ # Backup and rebuild
197+ backup_path = palace_path + ".backup"
198+ if os .path .exists (backup_path ):
199+ shutil .rmtree (backup_path )
200+ print (f" Backing up to { backup_path } ..." )
201+ shutil .copytree (palace_path , backup_path )
202+
203+ print (" Rebuilding collection..." )
204+ client .delete_collection ("mempalace_drawers" )
205+ new_col = client .create_collection ("mempalace_drawers" )
206+
207+ filed = 0
208+ for i in range (0 , len (all_ids ), batch_size ):
209+ batch_ids = all_ids [i : i + batch_size ]
210+ batch_docs = all_docs [i : i + batch_size ]
211+ batch_metas = all_metas [i : i + batch_size ]
212+ new_col .add (documents = batch_docs , ids = batch_ids , metadatas = batch_metas )
213+ filed += len (batch_ids )
214+ print (f" Re-filed { filed } /{ len (all_ids )} drawers..." )
215+
216+ print (f"\n Repair complete. { filed } drawers rebuilt." )
217+ print (f" Backup saved at { backup_path } " )
218+ print (f"\n { '=' * 55 } \n " )
219+
220+
150221def cmd_compress (args ):
151222 """Compress drawers in a wing using AAAK Dialect."""
152223 import chromadb
@@ -350,6 +421,12 @@ def main():
350421 help = "Only split files containing at least N sessions (default: 2)" ,
351422 )
352423
424+ # repair
425+ sub .add_parser (
426+ "repair" ,
427+ help = "Rebuild palace vector index from stored data (fixes segfaults after corruption)" ,
428+ )
429+
353430 # status
354431 sub .add_parser ("status" , help = "Show what's been filed" )
355432
@@ -366,6 +443,7 @@ def main():
366443 "search" : cmd_search ,
367444 "compress" : cmd_compress ,
368445 "wake-up" : cmd_wakeup ,
446+ "repair" : cmd_repair ,
369447 "status" : cmd_status ,
370448 }
371449 dispatch [args .command ](args )
0 commit comments