memor-cli 0.1.1__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.1 → memor_cli-0.2.0}/PKG-INFO +1 -1
- memor_cli-0.2.0/memor/__init__.py +1 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/cli.py +63 -10
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/daemon.py +9 -0
- {memor_cli-0.1.1 → 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.1.1 → 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.1 → memor_cli-0.2.0}/memor/store/sqlite_store.py +106 -1
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/types.py +15 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor_cli.egg-info/PKG-INFO +1 -1
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor_cli.egg-info/SOURCES.txt +2 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/pyproject.toml +1 -1
- memor_cli-0.2.0/tests/test_service.py +25 -0
- memor_cli-0.1.1/memor/dashboard/static/index.html +0 -688
- memor_cli-0.1.1/memor/store/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/LICENSE +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/README.md +0 -0
- {memor_cli-0.1.1/memor → memor_cli-0.2.0/memor/dashboard}/__init__.py +0 -0
- {memor_cli-0.1.1/memor/dashboard → memor_cli-0.2.0/memor/distill}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/distill/distiller.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/distill/extractive.py +0 -0
- {memor_cli-0.1.1/memor/distill → memor_cli-0.2.0/memor/embed}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/embed/api.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/embed/fake.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/embed/local.py +0 -0
- {memor_cli-0.1.1/memor/embed → memor_cli-0.2.0/memor/eval}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/baselines/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/baselines/base.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/baselines/claude_mem.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/baselines/graphiti.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/dataset.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/embed_benchmark.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/judge.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/metrics.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/eval/runner.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/feedback.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/hook_cli.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/hook_server.py +0 -0
- {memor_cli-0.1.1/memor/eval → memor_cli-0.2.0/memor/ingest}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/ingest/documents.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/interfaces.py +0 -0
- {memor_cli-0.1.1/memor/ingest → memor_cli-0.2.0/memor/llm}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/llm/anthropic.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/llm/base.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/llm/openai_compat.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/project.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/recall.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/redact.py +0 -0
- {memor_cli-0.1.1/memor/llm → memor_cli-0.2.0/memor/retrieve}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/retrieve/retriever.py +0 -0
- {memor_cli-0.1.1/memor/retrieve → memor_cli-0.2.0/memor/store}/__init__.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor/tokencount.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor_cli.egg-info/dependency_links.txt +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor_cli.egg-info/entry_points.txt +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor_cli.egg-info/requires.txt +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/memor_cli.egg-info/top_level.txt +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/setup.cfg +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_cli_smoke.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_daemon.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_dashboard.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_dataset_builder.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_dimension_safety.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_distiller.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_embed.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_embed_benchmark.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_eval_ablation.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_eval_runner.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_external_baselines.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_extractive.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_feedback.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_hook.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_hook_server.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_ingest_claude_code.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_ingest_documents.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_install_hook.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_interfaces.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_judge.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_metrics.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_noise_filter.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_project_resolver.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_recall_core.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_redact.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_retriever.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_skill_recall.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_store.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_supersession.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_tokencount.py +0 -0
- {memor_cli-0.1.1 → memor_cli-0.2.0}/tests/test_types.py +0 -0
|
@@ -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
|
|
|
@@ -438,7 +448,8 @@ def install_hook():
|
|
|
438
448
|
typer.echo(" retry later with: memor setup-model")
|
|
439
449
|
typer.echo()
|
|
440
450
|
typer.echo("Next steps:")
|
|
441
|
-
typer.echo(" 1. Start the daemon: memor
|
|
451
|
+
typer.echo(" 1. Start the daemon: memor service install")
|
|
452
|
+
typer.echo(" (or foreground: memor daemon)")
|
|
442
453
|
typer.echo(" 2. Open the dashboard: memor dashboard")
|
|
443
454
|
typer.echo(" Everything works locally — no API keys needed.")
|
|
444
455
|
|
|
@@ -464,5 +475,47 @@ def dashboard(port: int = typer.Option(8420, help="Port to serve on"),
|
|
|
464
475
|
uvicorn.run(app_instance, host="127.0.0.1", port=port, log_level="warning")
|
|
465
476
|
|
|
466
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
|
+
|
|
467
520
|
if __name__ == "__main__":
|
|
468
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()
|