diff --git a/nexus/mnemosyne/cli.py b/nexus/mnemosyne/cli.py index 5ad22653..0881c6ec 100644 --- a/nexus/mnemosyne/cli.py +++ b/nexus/mnemosyne/cli.py @@ -7,7 +7,8 @@ Provides: mnemosyne ingest, mnemosyne search, mnemosyne link, mnemosyne stats, mnemosyne timeline, mnemosyne neighbors, mnemosyne path, mnemosyne touch, mnemosyne decay, mnemosyne vitality, mnemosyne fading, mnemosyne vibrant, - mnemosyne snapshot create|list|restore|diff + mnemosyne snapshot create|list|restore|diff, + mnemosyne resonance """ from __future__ import annotations @@ -19,6 +20,7 @@ import sys from nexus.mnemosyne.archive import MnemosyneArchive from nexus.mnemosyne.entry import ArchiveEntry from nexus.mnemosyne.ingest import ingest_event +from nexus.mnemosyne.snapshot import snapshot_create, snapshot_list, snapshot_restore, snapshot_diff, ingest_directory def cmd_stats(args): @@ -64,6 +66,13 @@ def cmd_ingest(args): print(f"Ingested: [{entry.id[:8]}] {entry.title} ({len(entry.links)} links)") +def cmd_ingest_dir(args): + archive = MnemosyneArchive() + ext = [e.strip() for e in args.ext.split(",")] if args.ext else None + added = ingest_directory(archive, args.path, extensions=ext) + print(f"Ingested {added} new entries from {args.path}") + + def cmd_link(args): archive = MnemosyneArchive() entry = archive.get(args.entry_id) @@ -366,6 +375,24 @@ def cmd_snapshot(args): sys.exit(1) +def cmd_resonance(args): + archive = MnemosyneArchive() + topic = args.topic if args.topic else None + pairs = archive.resonance(threshold=args.threshold, limit=args.limit, topic=topic) + if not pairs: + print("No resonant pairs found.") + return + for p in pairs: + a = p["entry_a"] + b = p["entry_b"] + print(f"Score: {p['score']:.4f}") + print(f" [{a['id'][:8]}] {a['title']}") + print(f" Topics: {', '.join(a['topics']) if a['topics'] else '(none)'}") + print(f" [{b['id'][:8]}] {b['title']}") + print(f" Topics: {', '.join(b['topics']) if b['topics'] else '(none)'}") + print() + + def cmd_vibrant(args): archive = MnemosyneArchive() results = archive.vibrant(limit=args.limit) @@ -378,24 +405,6 @@ def cmd_vibrant(args): print() - -def cmd_resonance(args): - archive = MnemosyneArchive() - results = archive.resonance( - min_similarity=args.threshold, - limit=args.limit, - topic=args.topic or None, - ) - if not results: - print("No latent connections found.") - return - print(f"Found {len(results)} latent connection(s):\n") - for r in results: - print(f"[{r['entry_a'][:8]}] {r['title_a']}") - print(f" ←→ [{r['entry_b'][:8]}] {r['title_b']}") - print(f" Similarity: {r['similarity']:.4f}") - print() - def main(): parser = argparse.ArgumentParser(prog="mnemosyne", description="The Living Holographic Archive") sub = parser.add_subparsers(dest="command") @@ -412,6 +421,10 @@ def main(): i.add_argument("--content", required=True) i.add_argument("--topics", default="", help="Comma-separated topics") + id_ = sub.add_parser("ingest-dir", help="Ingest a directory of files") + id_.add_argument("path", help="Directory to ingest") + id_.add_argument("--ext", default="", help="Comma-separated extensions (default: md,txt,json)") + l = sub.add_parser("link", help="Show linked entries") l.add_argument("entry_id", help="Entry ID (or prefix)") l.add_argument("-d", "--depth", type=int, default=1) @@ -482,6 +495,11 @@ def main(): vb = sub.add_parser("vibrant", help="Show most alive entries (highest vitality)") vb.add_argument("-n", "--limit", type=int, default=10, help="Max entries to show") + rs = sub.add_parser("resonance", help="Discover latent connections between entries") + rs.add_argument("-t", "--threshold", type=float, default=0.3, help="Minimum similarity score (default: 0.3)") + rs.add_argument("-n", "--limit", type=int, default=20, help="Max pairs to show (default: 20)") + rs.add_argument("--topic", default="", help="Restrict to entries with this topic") + sn = sub.add_parser("snapshot", help="Point-in-time backup and restore") sn_sub = sn.add_subparsers(dest="snapshot_cmd") sn_create = sn_sub.add_parser("create", help="Create a new snapshot") @@ -492,11 +510,6 @@ def main(): sn_diff = sn_sub.add_parser("diff", help="Show what changed since a snapshot") sn_diff.add_argument("snapshot_id", help="Snapshot ID to compare against") - rs = sub.add_parser("resonance", help="Find latent connections between entries") - rs.add_argument("-t", "--threshold", type=float, default=0.25, help="Min similarity score (default: 0.25)") - rs.add_argument("-n", "--limit", type=int, default=20, help="Max pairs to show (default: 20)") - rs.add_argument("--topic", help="Filter to entries with this topic") - args = parser.parse_args() if not args.command: parser.print_help() @@ -509,6 +522,7 @@ def main(): "stats": cmd_stats, "search": cmd_search, "ingest": cmd_ingest, + "ingest-dir": cmd_ingest_dir, "link": cmd_link, "topics": cmd_topics, "remove": cmd_remove, @@ -529,11 +543,27 @@ def main(): "vitality": cmd_vitality, "fading": cmd_fading, "vibrant": cmd_vibrant, - "snapshot": cmd_snapshot, + "snapshot": lambda args: _dispatch_snapshot(args), + "discover": cmd_discover, "resonance": cmd_resonance, + "resonance": cmd_resonance, + "snapshot": cmd_snapshot, } dispatch[args.command](args) if __name__ == "__main__": main() + +def _dispatch_snapshot(args): + cmd = getattr(args, "snapshot_command", None) + if cmd == "create": print("Snapshot created") + elif cmd == "list": print("Snapshots listed") + +def cmd_discover(args): + archive = MnemosyneArchive() + for r in archive.discover(count=args.count, topic=args.topic): print(f"[{r['entry_id'][:8]}] {r['title']}") + +def cmd_resonance(args): + archive = MnemosyneArchive() + for r in archive.resonance(min_similarity=args.threshold, limit=args.limit, topic=args.topic): print(f"[{r['entry_a'][:8]}] {r['title_a']} <-> {r['title_b']}")