From ee57d0af4cb6c5428b006c674edc3d13994b8f52 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 20:07:12 +0000 Subject: [PATCH 1/2] Add levh remove-demo CLI command Demo data cleanup was only reachable via the /api/onboarding/remove-demo HTTP endpoint. Add a matching CLI command mirroring seed-demo so demo data can be stripped out of an existing instance without curl. --- server/cli.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/server/cli.py b/server/cli.py index bca1e33..e6089c7 100644 --- a/server/cli.py +++ b/server/cli.py @@ -1229,6 +1229,29 @@ async def _run() -> dict: return 0 +def cmd_remove_demo(args: argparse.Namespace) -> int: + """Remove all demo-tagged memories, leaving real data untouched.""" + import asyncio + + from server.core import engine_provider + + async def _run() -> dict: + engine = engine_provider.get_engine() + await engine.initialize() + try: + return await engine.remove_demo_data() + finally: + await engine.shutdown() + + result = asyncio.run(_run()) + removed = result.get("removed", 0) + if removed == 0: + print(" No demo data found — nothing to remove.") + else: + print(f" Removed {removed} demo memories.") + return 0 + + # ── mcp config ─────────────────────────────────────────────────── def cmd_mcp_config(args: argparse.Namespace) -> int: @@ -1576,6 +1599,12 @@ def main() -> int: help="Seed even if the store already has memories", ) + # remove-demo (onboarding: strip the demo corpus back out) + sub.add_parser( + "remove-demo", + help="Remove demo-tagged memories, leaving real data untouched", + ) + # entities (persistent entity knowledge graph) ent_p = sub.add_parser("entities", help="Persistent entity knowledge graph") ent_sub = ent_p.add_subparsers(dest="entities_command") @@ -1654,6 +1683,8 @@ def main() -> int: return cmd_purge(args) elif args.command == "seed-demo": return cmd_seed_demo(args) + elif args.command == "remove-demo": + return cmd_remove_demo(args) elif args.command == "entities": if args.entities_command in ("reindex", "list", "about"): return cmd_entities(args) From 15afabaf59a0f20384801bc287cce871fe983dd3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 21:31:22 +0000 Subject: [PATCH 2/2] Fix intermittent client-side exceptions on the dashboard - Guard memory.hscore against undefined (not just null) before .toFixed() - Fall back to a placeholder when person/org name is empty before .slice() - Handle non-JSON/empty responses in fetchApi instead of throwing raw SyntaxError - Add app/error.tsx so uncaught render errors show the real message instead of the generic "client-side exception" overlay --- frontend/src/app/error.tsx | 30 ++++++++++++++++++++++++++++++ frontend/src/app/page.tsx | 6 +++--- frontend/src/lib/api.ts | 6 +++++- 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 frontend/src/app/error.tsx diff --git a/frontend/src/app/error.tsx b/frontend/src/app/error.tsx new file mode 100644 index 0000000..6275525 --- /dev/null +++ b/frontend/src/app/error.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { useEffect } from "react"; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+

Something went wrong

+

+ {error.message || "An unexpected client-side error occurred."} +

+ +
+ ); +} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 39acc92..402d60e 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -200,7 +200,7 @@ export default function Dashboard() { {memory.pinned && } - {memory.hscore === null ? "—" : memory.hscore.toFixed(2)} + {memory.hscore == null ? "—" : memory.hscore.toFixed(2)} ))} @@ -233,7 +233,7 @@ export default function Dashboard() {
{people.map((person, index) => ( - {person.name.slice(0, 2).toUpperCase()} + {(person.name || "?").slice(0, 2).toUpperCase()} {person.name} {person.memory_count} memories @@ -242,7 +242,7 @@ export default function Dashboard() { ))} {people.length === 0 && organizations.slice(0, 4).map((org, index) => ( - {org.name.slice(0, 2).toUpperCase()} + {(org.name || "?").slice(0, 2).toUpperCase()} {org.name} {org.memory_count} memories diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 114f269..3d3f9d4 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -52,7 +52,11 @@ async function fetchApi(path: string, options?: RequestInit): Promise { } catch {} throw new Error(`API error ${detail}`); } - return res.json(); + try { + return (await res.json()) as T; + } catch { + throw new Error(`API error: invalid response from ${path}`); + } } export function wsUrl(): string {