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.
- {memor_cli-0.1.0/memor_cli.egg-info → memor_cli-0.2.0}/PKG-INFO +2 -2
- {memor_cli-0.1.0 → memor_cli-0.2.0}/README.md +1 -1
- memor_cli-0.2.0/memor/__init__.py +1 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/cli.py +74 -16
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/daemon.py +9 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/dashboard/server.py +20 -0
- memor_cli-0.2.0/memor/dashboard/static/index.html +821 -0
- memor_cli-0.2.0/memor/hook_cli.py +90 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/ingest/claude_code.py +55 -1
- memor_cli-0.2.0/memor/service.py +187 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/store/sqlite_store.py +106 -1
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/types.py +15 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0/memor_cli.egg-info}/PKG-INFO +2 -2
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/SOURCES.txt +3 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/entry_points.txt +1 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/pyproject.toml +2 -1
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_install_hook.py +12 -14
- memor_cli-0.2.0/tests/test_service.py +25 -0
- memor_cli-0.1.0/memor/dashboard/static/index.html +0 -688
- memor_cli-0.1.0/memor/store/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/LICENSE +0 -0
- {memor_cli-0.1.0/memor → memor_cli-0.2.0/memor/dashboard}/__init__.py +0 -0
- {memor_cli-0.1.0/memor/dashboard → memor_cli-0.2.0/memor/distill}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/distill/distiller.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/distill/extractive.py +0 -0
- {memor_cli-0.1.0/memor/distill → memor_cli-0.2.0/memor/embed}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/embed/api.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/embed/fake.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/embed/local.py +0 -0
- {memor_cli-0.1.0/memor/embed → memor_cli-0.2.0/memor/eval}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/base.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/claude_mem.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/baselines/graphiti.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/dataset.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/embed_benchmark.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/judge.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/metrics.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/eval/runner.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/feedback.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/hook_server.py +0 -0
- {memor_cli-0.1.0/memor/eval → memor_cli-0.2.0/memor/ingest}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/ingest/documents.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/interfaces.py +0 -0
- {memor_cli-0.1.0/memor/ingest → memor_cli-0.2.0/memor/llm}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/llm/anthropic.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/llm/base.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/llm/openai_compat.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/project.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/recall.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/redact.py +0 -0
- {memor_cli-0.1.0/memor/llm → memor_cli-0.2.0/memor/retrieve}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/retrieve/retriever.py +0 -0
- {memor_cli-0.1.0/memor/retrieve → memor_cli-0.2.0/memor/store}/__init__.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor/tokencount.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/dependency_links.txt +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/requires.txt +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/memor_cli.egg-info/top_level.txt +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/setup.cfg +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_cli_smoke.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_daemon.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_dashboard.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_dataset_builder.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_dimension_safety.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_distiller.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_embed.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_embed_benchmark.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_eval_ablation.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_eval_runner.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_external_baselines.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_extractive.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_feedback.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_hook.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_hook_server.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_ingest_claude_code.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_ingest_documents.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_interfaces.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_judge.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_metrics.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_noise_filter.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_project_resolver.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_recall_core.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_redact.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_retriever.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_skill_recall.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_store.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_supersession.py +0 -0
- {memor_cli-0.1.0 → memor_cli-0.2.0}/tests/test_tokencount.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
46
|
-
memor
|
|
47
|
-
memor dashboard
|
|
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>
|
|
51
|
-
--project <name>
|
|
52
|
-
--k <n>
|
|
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
|
|
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
|
|
83
|
-
memor dashboard
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
423
|
-
typer.echo(f"Hook installed: {
|
|
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
|
|
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()
|