Merge pull request #1104 from arnoldwender/fix/encoding-non-ascii-sweep

fix(encoding): replace non-ASCII symbols in CLI output (#1034)
This commit is contained in:
Igor Lins e Silva 2026-08-11 07:06:24 -03:00 committed by GitHub
commit b4345e84a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 75 additions and 75 deletions

View File

@ -166,7 +166,7 @@ def _run_pass_zero(project_dir, palace_dir, llm_provider) -> dict:
samples = _gather_origin_samples(project_dir)
if not samples:
print(" Skipping corpus-origin detection no readable samples.")
print(" Skipping corpus-origin detection -- no readable samples.")
return None
# Tier 1 — always runs. Cheap regex grep, no API.
@ -431,7 +431,7 @@ def cmd_init(args):
registry_path = add_to_known_entities(confirmed, wing=wing)
print(f" Registry updated: {registry_path}")
else:
print(" No entities detected proceeding with directory-based rooms.")
print(" No entities detected -- proceeding with directory-based rooms.")
# Pass 2: detect rooms from folder structure
detect_rooms_local(project_dir=args.dir, yes=getattr(args, "yes", False))
@ -878,7 +878,7 @@ def cmd_sync(args):
project_dirs = project_dirs or None
print(f"\n{'=' * 55}")
print(" MemPalace Sync Gitignore-aware drawer prune")
print(" MemPalace Sync -- Gitignore-aware drawer prune")
print(f"{'=' * 55}")
print(f" Palace: {palace_path}")
if args.wing:
@ -1183,7 +1183,7 @@ def cmd_hallways(args):
config=MempalaceConfig(palace_path=palace_path),
)
if not rows:
print("No hallways yet they are built from drawer entities when you mine.")
print("No hallways yet -- they are built from drawer entities when you mine.")
return
rows.sort(key=lambda h: h.get("co_occurrence_count", 0), reverse=True)
print(f" {len(rows)} hallway(s):")
@ -1651,7 +1651,7 @@ def cmd_repair(args):
# withholds success until FTS5 rebuild, VACUUM, and quick_check are
# clean. Its exception already includes the retained destination
# and archive/source recovery paths.
print("\n Rebuild cleanup failed see recovery details above.")
print("\n Rebuild cleanup failed -- see recovery details above.")
sys.exit(1)
# An empty counts dict is rebuild_from_sqlite's documented signal
# for a validation refusal (missing source, existing dest,
@ -1985,12 +1985,12 @@ def cmd_serve(args):
print(f" palace : {palace_path}")
print(f" backend : {(backend or 'default').strip().lower() if backend else 'default'}")
print(f" bind : {host}:{port} ({'loopback' if loopback else 'network-exposed'})")
print(f" tls : {'on' if tls_cert else 'off (plaintext terminate TLS at a proxy)'}")
print(f" tls : {'on' if tls_cert else 'off (plaintext -- terminate TLS at a proxy)'}")
print(f" read-only: {'yes' if args.read_only else 'no'}")
if token_created:
print("\n A new bearer token was generated and stored 0600 at:")
print(f" {_server_token_path(palace_path)}")
print(" Store it securely clients need it to connect:")
print(" Store it securely -- clients need it to connect:")
print(f" {token}")
print("\nConnect a client:")
if token:

View File

@ -889,7 +889,7 @@ def _mine_convos_impl(
files = scan_convos(convo_dir)
print(f"\n{'=' * 55}")
print(" MemPalace Mine Conversations")
print(" MemPalace Mine -- Conversations")
print(f"{'=' * 55}")
print(f" Wing: {wing}")
print(f" Source: {convo_path}")
@ -897,7 +897,7 @@ def _mine_convos_impl(
print(f" Files: {len(files)}{limit_suffix}")
print(f" Palace: {palace_path}")
if dry_run:
print(" DRY RUN nothing will be filed")
print(" DRY RUN -- nothing will be filed")
print(f"{'-' * 55}\n")
collection = _open_convo_collection(
@ -1018,9 +1018,9 @@ def _mine_convos_impl(
type_counts = Counter(c.get("memory_type", "general") for c in chunks)
types_str = ", ".join(f"{t}:{n}" for t, n in type_counts.most_common())
print(f" [DRY RUN] {filepath.name} {len(chunks)} memories ({types_str})")
print(f" [DRY RUN] {filepath.name} -> {len(chunks)} memories ({types_str})")
else:
print(f" [DRY RUN] {filepath.name} room:{room} ({len(chunks)} drawers)")
print(f" [DRY RUN] {filepath.name} -> room:{room} ({len(chunks)} drawers)")
total_drawers += len(chunks)
# Track room counts
if extract_mode == "general":

View File

@ -200,7 +200,7 @@ def dedup_palace(
print(f" Drawers: {col.count():,}")
print(f" Threshold: {threshold}")
print(f" Mode: {'DRY RUN' if dry_run else 'LIVE'}")
print(f"{'' * 55}")
print(f"{'-' * 55}")
if wing:
print(f" Wing: {wing}")
@ -227,7 +227,7 @@ def dedup_palace(
elapsed = time.time() - t0
print(f"\n{'' * 55}")
print(f"\n{'-' * 55}")
print(f" Done in {elapsed:.1f}s")
print(
f" Drawers: {total_kept + total_deleted:,}{total_kept:,} (-{total_deleted:,} removed)"

View File

@ -1063,7 +1063,7 @@ if __name__ == "__main__":
print("=== COMPRESSION STATS ===")
print(f"JSON: ~{stats['original_tokens_est']:,} tokens (est)")
print(f"AAAK: ~{stats['summary_tokens_est']:,} tokens (est)")
print(f"Ratio: {stats['size_ratio']}x (lossy information is lost)")
print(f"Ratio: {stats['size_ratio']}x (lossy -- information is lost)")
print()
print("=== AAAK DIALECT OUTPUT ===")
print(encoded)

View File

@ -89,7 +89,7 @@ def _resolve_providers(device: str) -> tuple[list, str]:
requested = _PROVIDER_MAP.get(device)
if requested is None:
if device not in _WARNED:
logger.warning("Unknown embedding_device %r falling back to cpu", device)
logger.warning("Unknown embedding_device %r -- falling back to cpu", device)
_WARNED.add(device)
return (["CPUExecutionProvider"], "cpu")

View File

@ -748,7 +748,7 @@ def _print_entity_list(entities: list, label: str):
print(" (none detected)")
return
for i, e in enumerate(entities):
confidence_bar = "" * int(e["confidence"] * 5) + "" * (5 - int(e["confidence"] * 5))
confidence_bar = "#" * int(e["confidence"] * 5) + "." * (5 - int(e["confidence"] * 5))
signals_str = ", ".join(e["signals"][:2]) if e["signals"] else ""
print(f" {i + 1:2}. {e['name']:20} [{confidence_bar}] {signals_str}")
@ -768,7 +768,7 @@ def confirm_entities(detected: dict, yes: bool = False) -> dict:
Pass yes=True to auto-accept all detected entities without prompting.
"""
print(f"\n{'=' * 58}")
print(" MemPalace Entity Detection")
print(" MemPalace -- Entity Detection")
print(f"{'=' * 58}")
print("\n Scanned your files. Here's what we found:\n")
@ -798,7 +798,7 @@ def confirm_entities(detected: dict, yes: bool = False) -> dict:
"topics": confirmed_topics,
}
print(f"\n{'' * 58}")
print(f"\n{'-' * 58}")
print(" Options:")
print(" [enter] Accept all")
print(" [edit] Remove wrong entries or reclassify uncertain")
@ -813,9 +813,9 @@ def confirm_entities(detected: dict, yes: bool = False) -> dict:
if choice == "edit":
# Handle uncertain first
if detected["uncertain"]:
print("\n Uncertain entities classify each:")
print("\n Uncertain entities -- classify each:")
for e in detected["uncertain"]:
ans = input(f" {e['name']} (p)erson, (r)project, or (s)kip? ").strip().lower()
ans = input(f" {e['name']} -- (p)erson, (r)project, or (s)kip? ").strip().lower()
if ans == "p":
confirmed_people.append(e["name"])
elif ans == "r":

View File

@ -84,7 +84,7 @@ def export_palace(palace_path: str, output_dir: str, format: str = "markdown") -
total = col.count()
if total == 0:
print(" Palace is empty nothing to export.")
print(" Palace is empty -- nothing to export.")
return {"wings": 0, "rooms": 0, "drawers": 0}
_reject_symlink(output_dir, "output_dir")

View File

@ -362,7 +362,7 @@ def extract_text(
logger.info("skip:unreadable (file gone after scan) %s", p)
return None, ExtractionStatus.SKIP_UNREADABLE
except OSError as exc:
logger.info("skip:unreadable %s %s", p, exc)
logger.info("skip:unreadable %s -- %s", p, exc)
return None, ExtractionStatus.SKIP_UNREADABLE
# Fringe Case 5 — empty file. Skip silently.
@ -433,9 +433,9 @@ def extract_text(
# Fringe Case 4 vs Case 10: encrypted vs generic crash, by message.
msg = str(exc)
if _ENCRYPTED_PATTERNS.search(msg):
logger.info("skip:encrypted %s %s", p, msg[:120])
logger.info("skip:encrypted %s -- %s", p, msg[:120])
return None, ExtractionStatus.SKIP_ENCRYPTED
logger.warning("skip:extraction_error %s %s: %s", p, type(exc).__name__, msg[:200])
logger.warning("skip:extraction_error %s -- %s: %s", p, type(exc).__name__, msg[:200])
return None, ExtractionStatus.SKIP_EXTRACTION_ERROR
# Either transformer can legitimately return None / empty (malformed
@ -443,7 +443,7 @@ def extract_text(
# so the caller knows to skip rather than file an empty drawer.
if not text:
transformer = "striprtf" if is_rtf else "markitdown"
logger.info("skip:extraction_error %s %s returned None/empty", p, transformer)
logger.info("skip:extraction_error %s -- %s returned None/empty", p, transformer)
return None, ExtractionStatus.SKIP_EXTRACTION_ERROR
return text, ExtractionStatus.OK
@ -803,7 +803,7 @@ def mine_formats(
files = scan_formats(format_path)
print(f"\n{'=' * 55}")
print(" MemPalace Mine Format extraction")
print(" MemPalace Mine -- Format extraction")
print(f"{'=' * 55}")
print(f" Wing: {wing}")
print(f" Source: {format_path}")
@ -811,7 +811,7 @@ def mine_formats(
print(f" Files: {len(files)}{limit_suffix}")
print(f" Palace: {palace_path}")
if dry_run:
print(" DRY RUN nothing will be filed")
print(" DRY RUN -- nothing will be filed")
print(f"{'-' * 55}\n")
collection = get_collection(palace_path) if not dry_run else None
@ -876,7 +876,7 @@ def mine_formats(
files_with_text += 1
if dry_run:
print(f" [DRY RUN] {filepath.name} {len(chunks)} drawers")
print(f" [DRY RUN] {filepath.name} -> {len(chunks)} drawers")
total_drawers += len(chunks)
files_mined += 1
if limit > 0 and files_mined >= limit:

View File

@ -469,7 +469,7 @@ if __name__ == "__main__":
import json
def usage():
print("layers.py 4-Layer Memory Stack")
print("layers.py -- 4-Layer Memory Stack")
print()
print("Usage:")
print(" python layers.py wake-up Show L0 + L1")

View File

@ -2950,7 +2950,7 @@ def tool_add_drawer(
"The palace index may be stale; run reconnect or repair."
)
_metadata_cache = None
logger.info(f"Filed drawer: {drawer_id} {wing}/{room}")
logger.info(f"Filed drawer: {drawer_id} -> {wing}/{room}")
return {
"success": True,
"drawer_id": drawer_id,
@ -2985,7 +2985,7 @@ def tool_add_drawer(
"The palace index may be stale; run reconnect or repair."
)
_metadata_cache = None
logger.info(f"Filed drawer: {drawer_id} {wing}/{room} ({len(chunk_ids)} chunks)")
logger.info(f"Filed drawer: {drawer_id} -> {wing}/{room} ({len(chunk_ids)} chunks)")
return {
"success": True,
"drawer_id": drawer_id,
@ -3883,7 +3883,7 @@ def tool_diary_write(agent_name: str, entry: str, topic: str = "general", wing:
documents=[entry],
metadatas=[{**base_metadata, "chunk_index": 0}],
)
logger.info(f"Diary entry: {entry_id} {wing}/diary/{topic}")
logger.info(f"Diary entry: {entry_id} -> {wing}/diary/{topic}")
return {
"success": True,
"entry_id": entry_id,
@ -3931,7 +3931,7 @@ def tool_diary_write(agent_name: str, entry: str, topic: str = "general", wing:
}
)
col.add(ids=chunk_ids, documents=chunk_docs, metadatas=chunk_metas)
logger.info(f"Diary entry: {entry_id} {wing}/diary/{topic} ({len(chunk_ids)} chunks)")
logger.info(f"Diary entry: {entry_id} -> {wing}/diary/{topic} ({len(chunk_ids)} chunks)")
return {
"success": True,
"entry_id": entry_id,
@ -7294,10 +7294,10 @@ def _run_stdio_loop() -> None:
# clean EOF — same meaning: the client is gone. Never loop on
# it: an orphaned stdio server holding the mine_palace flock
# blocked all palace writes for hours (2026-07-10 outage).
logger.info("stdin read failed (%s) client disconnected, shutting down", exc)
logger.info("stdin read failed (%s) -- client disconnected, shutting down", exc)
break
if not line:
logger.info("stdin EOF client disconnected, shutting down")
logger.info("stdin EOF -- client disconnected, shutting down")
break
line = line.strip()
@ -7327,7 +7327,7 @@ def _run_stdio_loop() -> None:
# The client's read end is gone; every future response write
# would fail the same way, so treat it like stdin EOF and
# shut down instead of swallowing it in the generic handler.
logger.info("stdout write failed (%s) client disconnected, shutting down", exc)
logger.info("stdout write failed (%s) -- client disconnected, shutting down", exc)
_drop_broken_stdout()
break

View File

@ -305,7 +305,7 @@ def migrate(palace_path: str, dry_run: bool = False, confirm: bool = False):
print(f" ROOM: {room:30} {count:5}")
if dry_run:
print("\n DRY RUN no changes made.")
print("\n DRY RUN -- no changes made.")
print(f" Would migrate {len(drawers)} drawers.")
return True
@ -570,7 +570,7 @@ def migrate_wing_names(palace_path: str, dry_run: bool = False, confirm: bool =
topic_renames = _plan_topics_by_wing_renames()
if not d_updates and not c_updates and not topic_renames:
print(" All wing names are already normalized nothing to migrate.")
print(" All wing names are already normalized -- nothing to migrate.")
return False
print("\n Wing-name migration plan:")
@ -586,7 +586,7 @@ def migrate_wing_names(palace_path: str, dry_run: bool = False, confirm: bool =
print(f" topics_by_wing: {len(topic_renames)} key(s) re-keyed")
if dry_run:
print("\n DRY RUN no changes made.\n")
print("\n DRY RUN -- no changes made.\n")
return True
if not confirm:

View File

@ -1843,7 +1843,7 @@ def _mine_impl(
print(f" Palace: {palace_path}")
print(f" Device: {describe_device()}")
if dry_run:
print(" DRY RUN nothing will be filed")
print(" DRY RUN -- nothing will be filed")
if not respect_gitignore:
print(" .gitignore: DISABLED")
if include_ignored:
@ -2183,7 +2183,7 @@ def status(palace_path: str):
def _print_status(total: int, wing_rooms: dict[str, dict[str, int]]) -> None:
"""Render the wing/room histogram shared by both status code paths."""
print(f"\n{'=' * 55}")
print(f" MemPalace Status {total} drawers")
print(f" MemPalace Status -- {total} drawers")
print(f"{'=' * 55}\n")
for wing, rooms in sorted(wing_rooms.items()):
print(f" WING: {wing}")

View File

@ -57,7 +57,7 @@ DEFAULT_WINGS = {
def _hr():
print(f"\n{'' * 58}")
print(f"\n{'-' * 58}")
def _header(text):
@ -121,9 +121,9 @@ def _ask_mode() -> str:
""")
print(" How are you using MemPalace?")
print()
print(" [1] Work notes, projects, clients, colleagues, decisions")
print(" [2] Personal diary, family, health, relationships, reflections")
print(" [3] Both personal and professional mixed")
print(" [1] Work -- notes, projects, clients, colleagues, decisions")
print(" [2] Personal -- diary, family, health, relationships, reflections")
print(" [3] Both -- personal and professional mixed")
print()
while True:
@ -430,7 +430,7 @@ def run_onboarding(
print()
if _yn(" Add any of these to your registry?"):
for e in detected:
ans = input(f" {e['name']} (p)erson, (s)kip? ").strip().lower()
ans = input(f" {e['name']} -- (p)erson, (s)kip? ").strip().lower()
if ans == "p":
rel = input(f" Relationship/role for {e['name']}? ").strip()
ctx = (
@ -439,7 +439,7 @@ def run_onboarding(
else (
"work"
if mode == "work"
else input(" Context (p)ersonal or (w)ork? ")
else input(" Context -- (p)ersonal or (w)ork? ")
.strip()
.lower()
.replace("w", "work")

View File

@ -531,7 +531,7 @@ def prune_corrupt(palace_path=None, confirm=False, collection_name: Optional[str
bad_file = os.path.join(palace_path, "corrupt_ids.txt")
if not os.path.exists(bad_file):
print(" No corrupt_ids.txt found run scan first.")
print(" No corrupt_ids.txt found -- run scan first.")
return
with open(bad_file) as f:
@ -539,7 +539,7 @@ def prune_corrupt(palace_path=None, confirm=False, collection_name: Optional[str
print(f" {len(bad_ids):,} corrupt IDs queued for deletion")
if not confirm:
print("\n DRY RUN no deletions performed.")
print("\n DRY RUN -- no deletions performed.")
print(" Re-run with --confirm to actually delete.")
return
@ -577,7 +577,7 @@ def prune_corrupt(palace_path=None, confirm=False, collection_name: Optional[str
after = col.count()
print(f"\n Deleted: {deleted:,}")
print(f" Failed: {failed:,}")
print(f" Collection size: {before:,} {after:,}")
print(f" Collection size: {before:,} -> {after:,}")
# ChromaDB's ``collection.get()`` enforces an internal default ``limit``
@ -1130,7 +1130,7 @@ def rebuild_index(
return
progress(f"\n{'=' * 55}")
progress(" MemPalace Repair Index Rebuild")
progress(" MemPalace Repair -- Index Rebuild")
progress(f"{'=' * 55}\n")
progress(f" Palace: {palace_path}")
@ -1670,7 +1670,7 @@ def rebuild_from_sqlite(
in_place = source_palace == dest_palace
print(f"\n{'=' * 55}")
print(" MemPalace Repair Rebuild from SQLite")
print(" MemPalace Repair -- Rebuild from SQLite")
print(f"{'=' * 55}\n")
print(f" Source: {source_palace}")
print(f" Dest: {dest_palace}")
@ -1765,7 +1765,7 @@ def _preview_rebuild_from_sqlite(
Never archives, locks, or writes. Returns ``{}`` if SQLite counts are
unreadable so a broken preview cannot look like a successful zero-row plan.
"""
print("\n DRY RUN no changes will be made.")
print("\n DRY RUN -- no changes will be made.")
if in_place:
print(
f" Would archive {dest_palace}"
@ -1811,7 +1811,7 @@ def _preview_legacy_repair(
Returns ``{}`` when the count is unreadable so a broken preview cannot look
like a valid plan (#1654, #2095, #2133).
"""
print("\n DRY RUN no changes will be made.")
print("\n DRY RUN -- no changes will be made.")
n = sqlite_drawer_count(palace_path, collection_name)
if n is None:
_print_unreadable_count_refusal(collection_name=collection_name, palace_path=palace_path)
@ -1846,8 +1846,8 @@ def _preview_legacy_repair(
f" returns fewer than {n} (#1208 truncation guard). It would then, in order:"
)
if os.path.exists(backup_path):
print(f" 1. DELETE the existing backup at {backup_path} or refuse outright")
print(" if it is not a palace and copy the live palace in its place")
print(f" 1. DELETE the existing backup at {backup_path} -- or refuse outright")
print(" if it is not a palace -- and copy the live palace in its place")
else:
print(f" 1. copy the palace directory to {backup_path}")
print(f" 2. DELETE the live '{collection_name}' collection and re-file the extracted rows")
@ -1919,7 +1919,7 @@ def _rebuild_from_sqlite_locked(
if in_place:
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
archive_path = f"{dest_palace}.pre-rebuild-{ts}"
print(f" Archiving {dest_palace} {archive_path}")
print(f" Archiving {dest_palace} -> {archive_path}")
# os.rename, NOT shutil.move. When any file inside the palace is
# held open by another process (MCP server, a running mine, another
# harness), renaming the directory fails atomically UP FRONT on
@ -2048,7 +2048,7 @@ def status(palace_path=None, collection_name: Optional[str] = None) -> dict:
palace_path = palace_path or _get_palace_path()
collection_name = collection_name or _drawers_collection_name()
print(f"\n{'=' * 55}")
print(" MemPalace Repair Status")
print(" MemPalace Repair -- Status")
print(f"{'=' * 55}\n")
print(f" Palace: {palace_path}")
@ -2265,7 +2265,7 @@ def repair_max_seq_id(
}
print(f"\n{'=' * 55}")
print(" MemPalace Repair max_seq_id Un-poison")
print(" MemPalace Repair -- max_seq_id Un-poison")
print(f"{'=' * 55}\n")
print(f" Palace: {palace_path}")
if segment:
@ -2316,10 +2316,10 @@ def repair_max_seq_id(
source = "sidecar" if from_sidecar else "heuristic (collection MAX)"
print(f" clean-value source {source}")
for seg_id, old_val, new_val in plan:
print(f" {seg_id} {old_val} {new_val}")
print(f" {seg_id} {old_val} -> {new_val}")
if dry_run:
print("\n DRY RUN no rows modified.\n" + "=" * 55 + "\n")
print("\n DRY RUN -- no rows modified.\n" + "=" * 55 + "\n")
return result
if not plan:

View File

@ -232,14 +232,14 @@ def detect_rooms_from_files(project_dir: str) -> list:
def print_proposed_structure(project_name: str, rooms: list, total_files: int, source: str):
print(f"\n{'=' * 55}")
print(" MemPalace Init Local setup")
print(" MemPalace Init -- Local setup")
print(f"{'=' * 55}")
print(f"\n WING: {project_name}")
print(f" ({total_files} files found, rooms detected from {source})\n")
for room in rooms:
print(f" ROOM: {room['name']}")
print(f" {room['description']}")
print(f"\n{'' * 55}")
print(f"\n{'-' * 55}")
def get_user_approval(rooms: list) -> list:
@ -259,7 +259,7 @@ def get_user_approval(rooms: list) -> list:
if choice == "edit":
print("\n Current rooms:")
for i, room in enumerate(rooms):
print(f" {i + 1}. {room['name']} {room['description']}")
print(f" {i + 1}. {room['name']} -- {room['description']}")
remove = input("\n Room numbers to REMOVE (comma-separated, or enter to skip): ").strip()
if remove:
to_remove = {int(x.strip()) - 1 for x in remove.split(",") if x.strip().isdigit()}

View File

@ -585,7 +585,7 @@ def _print_search_results_bm25_only(
for line in (hit.get("text", "") or "").strip().split("\n"):
print(f" {line}")
print()
print(f" {'' * 56}")
print(f" {'-' * 56}")
print()
@ -692,7 +692,7 @@ def search(query: str, palace_path: str, wing: str = None, room: str = None, n_r
for line in hit["text"].strip().split("\n"):
print(f" {line}")
print()
print(f" {'' * 56}")
print(f" {'-' * 56}")
print()

View File

@ -306,7 +306,7 @@ def run_sync(payload: dict[str, Any]) -> dict[str, Any]:
dry_run = bool(payload.get("dry_run", True))
print(f"\n{'=' * 55}")
print(" MemPalace Sync Gitignore-aware drawer prune")
print(" MemPalace Sync -- Gitignore-aware drawer prune")
print(f"{'=' * 55}")
print(f" Palace: {palace_path}")
if payload.get("wing"):

View File

@ -285,7 +285,7 @@ def main():
return
print(f"\n{'=' * 60}")
print(f" Mega-file splitter {'DRY RUN' if args.dry_run else 'SPLITTING'}")
print(f" Mega-file splitter -- {'DRY RUN' if args.dry_run else 'SPLITTING'}")
print(f"{'=' * 60}")
print(f" Source: {src_dir}")
print(f" Output: {output_dir or 'same dir as source'}")
@ -307,9 +307,9 @@ def main():
print(f"{'-' * 60}")
if args.dry_run:
print(f" DRY RUN would create {total_written} files from {len(mega_files)} mega-files")
print(f" DRY RUN -- would create {total_written} files from {len(mega_files)} mega-files")
else:
print(f" Done created {total_written} files from {len(mega_files)} mega-files")
print(f" Done -- created {total_written} files from {len(mega_files)} mega-files")
print()

View File

@ -1246,7 +1246,7 @@ def test_cmd_repair_default_mode_dry_run_writes_nothing(mock_config_cls, tmp_pat
cmd_repair(args)
out = capsys.readouterr().out
assert "DRY RUN no changes will be made." in out
assert "DRY RUN -- no changes will be made." in out
assert "holds 2 rows" in out
assert "Repair complete" not in out
# The count comes from read-only SQLite, never from a chromadb client:

View File

@ -1093,7 +1093,7 @@ def test_status_does_not_cold_load_vector_index(palace_path, seeded_collection,
sentinel.assert_not_called()
out = capsys.readouterr().out
assert "MemPalace Status 4 drawers" in out
assert "MemPalace Status -- 4 drawers" in out
assert "WING: project" in out
assert "WING: notes" in out
@ -1128,7 +1128,7 @@ def test_status_falls_back_to_chroma_when_sqlite_unreadable(palace_path, seeded_
status(palace_path)
out = capsys.readouterr().out
assert "MemPalace Status 4 drawers" in out
assert "MemPalace Status -- 4 drawers" in out
assert "WING: project" in out

View File

@ -210,7 +210,7 @@ def test_generate_aaak_bootstrap_no_relationship(tmp_path):
def test_hr_prints_line(capsys):
_hr()
out = capsys.readouterr().out
assert "" in out
assert "-" in out
def test_header_prints_banner(capsys):

View File

@ -2481,7 +2481,7 @@ def test_cmd_repair_dry_run_leaves_a_real_palace_byte_identical(tmp_path, capsys
out = capsys.readouterr().out
assert snapshot() == before
assert not (tmp_path / "palace.backup").exists()
assert "DRY RUN no changes will be made." in out
assert "DRY RUN -- no changes will be made." in out
assert "holds 4 rows" in out
assert "Repair complete" not in out