memor-cli 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {memor_cli-0.1.0/memor_cli.egg-info → memor_cli-0.2.0}/PKG-INFO +2 -2
  2. {memor_cli-0.1.0 → memor_cli-0.2.0}/README.md +1 -1
  3. memor_cli-0.2.0/memor/__init__.py +1 -0
  4. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/cli.py +74 -16
  5. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/daemon.py +9 -0
  6. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/dashboard/server.py +20 -0
  7. memor_cli-0.2.0/memor/dashboard/static/index.html +821 -0
  8. memor_cli-0.2.0/memor/hook_cli.py +90 -0
  9. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/ingest/claude_code.py +55 -1
  10. memor_cli-0.2.0/memor/service.py +187 -0
  11. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/store/sqlite_store.py +106 -1
  12. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/types.py +15 -0
  13. {memor_cli-0.1.0 → memor_cli-0.2.0/memor_cli.egg-info}/PKG-INFO +2 -2
  14. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/SOURCES.txt +3 -0
  15. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/entry_points.txt +1 -0
  16. {memor_cli-0.1.0 → memor_cli-0.2.0}/pyproject.toml +2 -1
  17. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_install_hook.py +12 -14
  18. memor_cli-0.2.0/tests/test_service.py +25 -0
  19. memor_cli-0.1.0/memor/dashboard/static/index.html +0 -688
  20. memor_cli-0.1.0/memor/store/__init__.py +0 -0
  21. {memor_cli-0.1.0 → memor_cli-0.2.0}/LICENSE +0 -0
  22. {memor_cli-0.1.0/memor → memor_cli-0.2.0/memor/dashboard}/__init__.py +0 -0
  23. {memor_cli-0.1.0/memor/dashboard → memor_cli-0.2.0/memor/distill}/__init__.py +0 -0
  24. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/distill/distiller.py +0 -0
  25. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/distill/extractive.py +0 -0
  26. {memor_cli-0.1.0/memor/distill → memor_cli-0.2.0/memor/embed}/__init__.py +0 -0
  27. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/embed/api.py +0 -0
  28. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/embed/fake.py +0 -0
  29. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/embed/local.py +0 -0
  30. {memor_cli-0.1.0/memor/embed → memor_cli-0.2.0/memor/eval}/__init__.py +0 -0
  31. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/__init__.py +0 -0
  32. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/base.py +0 -0
  33. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/claude_mem.py +0 -0
  34. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/graphiti.py +0 -0
  35. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/dataset.py +0 -0
  36. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/embed_benchmark.py +0 -0
  37. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/judge.py +0 -0
  38. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/metrics.py +0 -0
  39. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/runner.py +0 -0
  40. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/feedback.py +0 -0
  41. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/hook_server.py +0 -0
  42. {memor_cli-0.1.0/memor/eval → memor_cli-0.2.0/memor/ingest}/__init__.py +0 -0
  43. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/ingest/documents.py +0 -0
  44. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/interfaces.py +0 -0
  45. {memor_cli-0.1.0/memor/ingest → memor_cli-0.2.0/memor/llm}/__init__.py +0 -0
  46. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/llm/anthropic.py +0 -0
  47. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/llm/base.py +0 -0
  48. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/llm/openai_compat.py +0 -0
  49. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/project.py +0 -0
  50. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/recall.py +0 -0
  51. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/redact.py +0 -0
  52. {memor_cli-0.1.0/memor/llm → memor_cli-0.2.0/memor/retrieve}/__init__.py +0 -0
  53. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/retrieve/retriever.py +0 -0
  54. {memor_cli-0.1.0/memor/retrieve → memor_cli-0.2.0/memor/store}/__init__.py +0 -0
  55. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/tokencount.py +0 -0
  56. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/dependency_links.txt +0 -0
  57. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/requires.txt +0 -0
  58. {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/top_level.txt +0 -0
  59. {memor_cli-0.1.0 → memor_cli-0.2.0}/setup.cfg +0 -0
  60. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_cli_smoke.py +0 -0
  61. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_daemon.py +0 -0
  62. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_dashboard.py +0 -0
  63. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_dataset_builder.py +0 -0
  64. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_dimension_safety.py +0 -0
  65. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_distiller.py +0 -0
  66. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_embed.py +0 -0
  67. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_embed_benchmark.py +0 -0
  68. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_eval_ablation.py +0 -0
  69. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_eval_runner.py +0 -0
  70. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_external_baselines.py +0 -0
  71. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_extractive.py +0 -0
  72. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_feedback.py +0 -0
  73. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_hook.py +0 -0
  74. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_hook_server.py +0 -0
  75. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_ingest_claude_code.py +0 -0
  76. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_ingest_documents.py +0 -0
  77. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_interfaces.py +0 -0
  78. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_judge.py +0 -0
  79. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_metrics.py +0 -0
  80. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_noise_filter.py +0 -0
  81. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_project_resolver.py +0 -0
  82. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_recall_core.py +0 -0
  83. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_redact.py +0 -0
  84. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_retriever.py +0 -0
  85. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_skill_recall.py +0 -0
  86. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_store.py +0 -0
  87. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_supersession.py +0 -0
  88. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_tokencount.py +0 -0
  89. {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memor-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Measured memory for coding agents. Fire and forget — no API keys needed.
5
5
  Author-email: Nimit Bhandari <nimitbhandari17@gmail.com>
6
6
  License-Expression: MIT
@@ -216,7 +216,7 @@ memor/
216
216
  +-- judge.py LLM-as-judge evaluation
217
217
  +-- embed_benchmark.py Embedding model comparison
218
218
 
219
- bin/memor-hook.py Claude Code hook (thin client)
219
+ memor/hook_cli.py Claude Code hook entry point (thin client)
220
220
  skill/recall.py Standalone recall script
221
221
  ```
222
222
 
@@ -179,7 +179,7 @@ memor/
179
179
  +-- judge.py LLM-as-judge evaluation
180
180
  +-- embed_benchmark.py Embedding model comparison
181
181
 
182
- bin/memor-hook.py Claude Code hook (thin client)
182
+ memor/hook_cli.py Claude Code hook entry point (thin client)
183
183
  skill/recall.py Standalone recall script
184
184
  ```
185
185
 
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -9,6 +9,8 @@ from memor.types import Scope
9
9
  from memor.ingest.claude_code import parse_transcript
10
10
 
11
11
  app = typer.Typer(no_args_is_help=True)
12
+ service_app = typer.Typer(help="Manage the memor daemon as a background service.")
13
+ app.add_typer(service_app, name="service")
12
14
 
13
15
  def _db_path(db: str) -> str:
14
16
  return str(Path(db).expanduser())
@@ -42,14 +44,21 @@ HELP_TEXT = """\
42
44
  memor — measured memory for coding agents
43
45
 
44
46
  GETTING STARTED
45
- memor install-hook Install the Claude Code recall hook + download model
46
- memor daemon Start the background watcher (ingests + distills)
47
- memor dashboard Open the web dashboard at localhost:8420
47
+ memor install-hook Install the Claude Code recall hook + download model
48
+ memor service install Start the daemon as a background service
49
+ memor dashboard Open the web dashboard at localhost:8420
50
+
51
+ SERVICE MANAGEMENT
52
+ memor service install Install and start as a background service (survives reboots)
53
+ memor service status Check if the daemon is running
54
+ memor service stop Stop the service (restarts on next login)
55
+ memor service uninstall Stop and remove the service completely
56
+ memor daemon Run the daemon in the foreground (alternative to service)
48
57
 
49
58
  QUERYING
50
- memor query <text> Search memories from the command line
51
- --project <name> Scope to a specific project
52
- --k <n> Number of results (default: 8)
59
+ memor query <text> Search memories from the command line
60
+ --project <name> Scope to a specific project
61
+ --k <n> Number of results (default: 8)
53
62
 
54
63
  INGESTION
55
64
  memor ingest-cc <file> Ingest a single transcript
@@ -66,6 +75,7 @@ MAINTENANCE
66
75
  memor scan Audit DB for leaked secrets
67
76
  memor scan --purge Redact secrets in place
68
77
  memor setup-model Download/retry the embedding model (~60MB)
78
+ memor version Print the installed version
69
79
 
70
80
  EVALUATION
71
81
  memor eval <cases.json> Run eval suite
@@ -77,10 +87,10 @@ CONFIGURATION
77
87
  No configuration needed — just install, hook, and run the daemon.
78
88
 
79
89
  EXAMPLES
80
- memor install-hook && memor daemon # one-time setup
90
+ memor install-hook && memor service install # one-time setup
81
91
  memor query "auth flow" --project my-app
82
- memor reingest --project my-app -y # refresh one project
83
- memor dashboard # see stats at localhost:8420
92
+ memor reingest --project my-app -y # refresh one project
93
+ memor dashboard # see stats at localhost:8420
84
94
  """
85
95
 
86
96
 
@@ -387,7 +397,7 @@ def daemon(poll_interval: int = typer.Option(30, help="Seconds between polls"),
387
397
  run_daemon(poll_interval=poll_interval, projects_dir=d)
388
398
 
389
399
 
390
- def _install_hook_logic(settings_path: Path, hook_path: str) -> None:
400
+ def _install_hook_logic(settings_path: Path, hook_command: str) -> None:
391
401
  """Core logic for install-hook, separated for testing."""
392
402
  if settings_path.exists():
393
403
  data = json.loads(settings_path.read_text())
@@ -395,8 +405,7 @@ def _install_hook_logic(settings_path: Path, hook_path: str) -> None:
395
405
  data = {}
396
406
  hooks = data.setdefault("hooks", {})
397
407
  prompt_hooks = hooks.setdefault("UserPromptSubmit", [])
398
- python = sys.executable
399
- hook_cmd = {"type": "command", "command": f"{python} {hook_path}", "timeout": 5000}
408
+ hook_cmd = {"type": "command", "command": hook_command, "timeout": 5000}
400
409
  entry = {"matcher": "", "hooks": [hook_cmd]}
401
410
  existing_idx = None
402
411
  for i, group in enumerate(prompt_hooks):
@@ -417,10 +426,16 @@ def _install_hook_logic(settings_path: Path, hook_path: str) -> None:
417
426
  @app.command("install-hook")
418
427
  def install_hook():
419
428
  """Install the Claude Code recall hook into ~/.claude/settings.json."""
420
- hook_path = str(Path(__file__).resolve().parent.parent / "bin" / "memor-hook.py")
429
+ import shutil
430
+ hook_bin = shutil.which("memor-hook")
431
+ if not hook_bin:
432
+ typer.echo("Error: 'memor-hook' not found on PATH.", err=True)
433
+ typer.echo(" If you installed with pipx: pipx install memor-cli", err=True)
434
+ typer.echo(" If developing locally: pip install -e .", err=True)
435
+ raise typer.Exit(1)
421
436
  settings_path = Path.home() / ".claude" / "settings.json"
422
- _install_hook_logic(settings_path, hook_path)
423
- typer.echo(f"Hook installed: {hook_path}")
437
+ _install_hook_logic(settings_path, hook_bin)
438
+ typer.echo(f"Hook installed: {hook_bin}")
424
439
  typer.echo(f"Settings updated: {settings_path}")
425
440
  typer.echo()
426
441
  typer.echo("Pre-downloading embedding model...")
@@ -433,7 +448,8 @@ def install_hook():
433
448
  typer.echo(" retry later with: memor setup-model")
434
449
  typer.echo()
435
450
  typer.echo("Next steps:")
436
- typer.echo(" 1. Start the daemon: memor daemon")
451
+ typer.echo(" 1. Start the daemon: memor service install")
452
+ typer.echo(" (or foreground: memor daemon)")
437
453
  typer.echo(" 2. Open the dashboard: memor dashboard")
438
454
  typer.echo(" Everything works locally — no API keys needed.")
439
455
 
@@ -459,5 +475,47 @@ def dashboard(port: int = typer.Option(8420, help="Port to serve on"),
459
475
  uvicorn.run(app_instance, host="127.0.0.1", port=port, log_level="warning")
460
476
 
461
477
 
478
+ @app.command("version")
479
+ def version():
480
+ """Print the memor version."""
481
+ from memor import __version__
482
+ typer.echo(f"memor {__version__}")
483
+
484
+
485
+ @service_app.command("install")
486
+ def service_install():
487
+ """Install and start the daemon as a background service (survives reboots)."""
488
+ from memor.service import install
489
+ try:
490
+ typer.echo(install())
491
+ except FileNotFoundError as e:
492
+ typer.echo(str(e), err=True)
493
+ raise typer.Exit(1)
494
+ except Exception as e:
495
+ typer.echo(f"Failed to install service: {e}", err=True)
496
+ raise typer.Exit(1)
497
+
498
+
499
+ @service_app.command("uninstall")
500
+ def service_uninstall():
501
+ """Stop and remove the background service."""
502
+ from memor.service import uninstall
503
+ typer.echo(uninstall())
504
+
505
+
506
+ @service_app.command("stop")
507
+ def service_stop():
508
+ """Stop the background service (will restart on next login)."""
509
+ from memor.service import stop
510
+ typer.echo(stop())
511
+
512
+
513
+ @service_app.command("status")
514
+ def service_status():
515
+ """Show whether the daemon service is running."""
516
+ from memor.service import status
517
+ typer.echo(status())
518
+
519
+
462
520
  if __name__ == "__main__":
463
521
  app()
@@ -93,6 +93,15 @@ def ingest_file(path: Path, project: str, store: SqliteStore, embedder) -> int:
93
93
  return 0
94
94
  vecs = embedder.embed([a.text for a in arts])
95
95
  store.add_artifacts(arts, vecs)
96
+
97
+ from memor.ingest.claude_code import parse_session_usage
98
+ try:
99
+ usage = parse_session_usage(path, project)
100
+ if usage:
101
+ store.upsert_session_stats(usage)
102
+ except Exception:
103
+ pass
104
+
96
105
  return len(arts)
97
106
 
98
107
 
@@ -114,6 +114,26 @@ def create_app(db_path: str | None = None) -> FastAPI:
114
114
  })
115
115
  return result
116
116
 
117
+ @app.get("/api/session-efficiency")
118
+ def session_efficiency():
119
+ store = _store()
120
+ return store.get_session_efficiency()
121
+
122
+ @app.get("/api/recall-trend")
123
+ def recall_trend(days: int = Query(30, ge=7, le=90)):
124
+ store = _store()
125
+ rows = store.db.execute("""
126
+ SELECT date(timestamp, 'unixepoch', 'localtime') as day,
127
+ COUNT(*) as recalls,
128
+ SUM(CASE WHEN hits_count > 0 THEN 1 ELSE 0 END) as hits,
129
+ SUM(tokens_injected) as tokens,
130
+ AVG(CASE WHEN hits_count > 0 THEN top_score END) as avg_score
131
+ FROM recall_log
132
+ WHERE timestamp >= unixepoch('now', ? || ' days')
133
+ GROUP BY day ORDER BY day
134
+ """, (f"-{days}",)).fetchall()
135
+ return [dict(r) for r in rows]
136
+
117
137
  @app.get("/api/health")
118
138
  def health():
119
139
  store = _store()