diff --git a/mempalace/cli.py b/mempalace/cli.py index 3244c8b..6acfcb4 100644 --- a/mempalace/cli.py +++ b/mempalace/cli.py @@ -170,7 +170,7 @@ def cmd_repair(args): """Rebuild palace vector index from SQLite metadata.""" import chromadb import shutil - from .migrate import confirm_destructive_action, has_palace_database + from .migrate import confirm_destructive_action, contains_palace_database palace_path = os.path.abspath( os.path.expanduser(args.palace) if args.palace else MempalaceConfig().palace_path @@ -180,7 +180,7 @@ def cmd_repair(args): if not os.path.isdir(palace_path): print(f"\n No palace found at {palace_path}") return - if not has_palace_database(palace_path): + if not contains_palace_database(palace_path): print(f"\n No palace database found at {db_path}") return @@ -226,8 +226,11 @@ def cmd_repair(args): palace_path = palace_path.rstrip(os.sep) backup_path = palace_path + ".backup" if os.path.exists(backup_path): - if not has_palace_database(backup_path): - print(f" Refusing to delete non-palace backup path: {backup_path}") + if not contains_palace_database(backup_path): + print( + " Cannot proceed: backup path exists but is not a valid palace database. " + f"Please remove or rename: {backup_path}" + ) return shutil.rmtree(backup_path) print(f" Backing up to {backup_path}...") diff --git a/mempalace/migrate.py b/mempalace/migrate.py index 715511d..6410801 100644 --- a/mempalace/migrate.py +++ b/mempalace/migrate.py @@ -104,7 +104,7 @@ def detect_chromadb_version(db_path: str) -> str: conn.close() -def has_palace_database(path: str) -> bool: +def contains_palace_database(path: str) -> bool: """Return True when path looks like a MemPalace ChromaDB directory.""" return os.path.isfile(os.path.join(path, "chroma.sqlite3")) @@ -135,7 +135,7 @@ def migrate(palace_path: str, dry_run: bool = False, confirm: bool = False): palace_path = os.path.abspath(os.path.expanduser(palace_path)) db_path = os.path.join(palace_path, "chroma.sqlite3") - if not os.path.isdir(palace_path) or not has_palace_database(palace_path): + if not os.path.isdir(palace_path) or not contains_palace_database(palace_path): print(f"\n No palace database found at {db_path}") return False diff --git a/tests/test_query_sanitizer.py b/tests/test_query_sanitizer.py index 2a5c94b..015a8a1 100644 --- a/tests/test_query_sanitizer.py +++ b/tests/test_query_sanitizer.py @@ -102,6 +102,20 @@ class TestTailSentence: assert result["was_sanitized"] is True assert "MemPalace" in result["clean_query"] or "ChromaDB" in result["clean_query"] + def test_long_candidate_uses_last_sentence_fragment(self): + query = ("Prompt sentence. " * 30) + "Final search intent for architecture migration" + result = sanitize_query(query) + assert result["method"] == "tail_sentence" + assert result["clean_query"] == "Final search intent for architecture migration" + + def test_long_candidate_strips_wrapping_quotes(self): + query = ("Prefix text " * 30) + '\n"' + ("x" * 260) + '"' + result = sanitize_query(query) + assert result["method"] == "tail_sentence" + assert not result["clean_query"].startswith('"') + assert not result["clean_query"].endswith('"') + assert len(result["clean_query"]) <= MAX_QUERY_LENGTH + class TestTailTruncation: """Step 4: Fallback — take the last MAX_QUERY_LENGTH characters.""" @@ -119,6 +133,12 @@ class TestTailTruncation: result = sanitize_query(filler) assert "IMPORTANT_QUERY_CONTENT" in result["clean_query"] + def test_tail_sentence_fallback_preserves_tail_without_delimiters(self): + filler = ("x" * 260) + "IMPORTANT_QUERY_CONTENT" + result = sanitize_query(filler) + assert result["method"] == "tail_sentence" + assert "IMPORTANT_QUERY_CONTENT" in result["clean_query"] + class TestLengthGuards: """Verify output length constraints."""