claude-total-memory 11.2.2__py3-none-any.whl
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.
- claude_total_memory/__init__.py +3 -0
- claude_total_memory/lookup.py +183 -0
- claude_total_memory/server.py +25 -0
- claude_total_memory-11.2.2.dist-info/METADATA +1309 -0
- claude_total_memory-11.2.2.dist-info/RECORD +179 -0
- claude_total_memory-11.2.2.dist-info/WHEEL +5 -0
- claude_total_memory-11.2.2.dist-info/entry_points.txt +4 -0
- claude_total_memory-11.2.2.dist-info/licenses/LICENSE +21 -0
- claude_total_memory-11.2.2.dist-info/top_level.txt +2 -0
- src/__init__.py +0 -0
- src/active_context.py +205 -0
- src/ai_layer/__init__.py +77 -0
- src/ai_layer/answerability.py +386 -0
- src/ai_layer/contradiction_detector.py +37 -0
- src/ai_layer/coref_resolver.py +21 -0
- src/ai_layer/enrichment_jobs.py +129 -0
- src/ai_layer/enrichment_worker.py +44 -0
- src/ai_layer/iterative_retriever.py +469 -0
- src/ai_layer/keyword_extractor.py +51 -0
- src/ai_layer/quality_gate.py +23 -0
- src/ai_layer/query_rewriter.py +22 -0
- src/ai_layer/question_generator.py +53 -0
- src/ai_layer/reflection.py +30 -0
- src/ai_layer/relation_extractor.py +64 -0
- src/ai_layer/reranker.py +28 -0
- src/ai_layer/self_improve.py +30 -0
- src/ai_layer/summarizer.py +39 -0
- src/ai_layer/verifier.py +506 -0
- src/analogy.py +157 -0
- src/associative/__init__.py +0 -0
- src/associative/activation.py +244 -0
- src/associative/composition.py +338 -0
- src/associative/recall.py +383 -0
- src/ast_ingest/__init__.py +5 -0
- src/ast_ingest/ingester.py +401 -0
- src/auto_episode_capture.py +417 -0
- src/auto_extract_active.py +217 -0
- src/auto_self_improve.py +314 -0
- src/auto_session_save.py +101 -0
- src/autofilter.py +191 -0
- src/cache.py +237 -0
- src/cache_layer.py +528 -0
- src/canonical_tags.py +390 -0
- src/choose_embed.py +201 -0
- src/cognitive/__init__.py +1 -0
- src/cognitive/engine.py +979 -0
- src/config.py +896 -0
- src/content_filter.py +270 -0
- src/context_expander.py +179 -0
- src/contradiction_detector.py +476 -0
- src/coref_resolver.py +302 -0
- src/dashboard.py +4557 -0
- src/dashboard_v6.py +1417 -0
- src/decisions.py +320 -0
- src/deep_enricher.py +286 -0
- src/deep_enrichment_queue.py +169 -0
- src/embed_provider.py +441 -0
- src/enrichment_filter.py +108 -0
- src/enrichment_worker.py +483 -0
- src/entity_dedup.py +354 -0
- src/episodic.py +328 -0
- src/error_capture.py +248 -0
- src/eval_harness.py +234 -0
- src/extract_transcript.py +408 -0
- src/fact_index.py +510 -0
- src/fact_merger.py +245 -0
- src/fact_synthesizer.py +375 -0
- src/file_context.py +338 -0
- src/fusion.py +138 -0
- src/graph/__init__.py +8 -0
- src/graph/auto_link.py +81 -0
- src/graph/enricher.py +505 -0
- src/graph/indexer.py +872 -0
- src/graph/query.py +470 -0
- src/graph/store.py +731 -0
- src/graph_expander.py +393 -0
- src/ingestion/__init__.py +13 -0
- src/ingestion/chunker.py +271 -0
- src/ingestion/enricher.py +249 -0
- src/ingestion/extractor.py +541 -0
- src/ingestion/file_watcher.py +320 -0
- src/ingestion/gateway.py +345 -0
- src/ingestion/ocr.py +264 -0
- src/intents.py +194 -0
- src/llm_provider.py +404 -0
- src/memory_core/__init__.py +33 -0
- src/memory_core/answer_router.py +359 -0
- src/memory_core/cache.py +123 -0
- src/memory_core/calibration.py +401 -0
- src/memory_core/chunker.py +438 -0
- src/memory_core/classifier.py +406 -0
- src/memory_core/dedup.py +87 -0
- src/memory_core/embedding_cache.py +232 -0
- src/memory_core/embedding_spaces.py +127 -0
- src/memory_core/embeddings.py +173 -0
- src/memory_core/entity_resolver.py +601 -0
- src/memory_core/episodes/__init__.py +27 -0
- src/memory_core/episodes/extractor.py +469 -0
- src/memory_core/episodes/retriever.py +383 -0
- src/memory_core/episodes/schema.py +90 -0
- src/memory_core/graph_links.py +131 -0
- src/memory_core/health.py +77 -0
- src/memory_core/idk_router.py +214 -0
- src/memory_core/negative_retrieval.py +510 -0
- src/memory_core/storage.py +64 -0
- src/memory_core/telemetry.py +91 -0
- src/memory_core/temporal/__init__.py +80 -0
- src/memory_core/temporal/allen.py +406 -0
- src/memory_core/temporal/arithmetic.py +281 -0
- src/memory_core/temporal/normalizer.py +581 -0
- src/memory_core/vector_store.py +254 -0
- src/memory_systems/__init__.py +0 -0
- src/memory_systems/episode_store.py +278 -0
- src/memory_systems/self_model.py +476 -0
- src/memory_systems/signals.py +246 -0
- src/memory_systems/skill_store.py +391 -0
- src/metrics/__init__.py +0 -0
- src/migrate_knowledge_to_graph.py +335 -0
- src/models.py +1296 -0
- src/multi_repr_search.py +334 -0
- src/multi_repr_store.py +202 -0
- src/outbox.py +325 -0
- src/privacy_filter.py +19 -0
- src/procedural.py +356 -0
- src/project_wiki.py +434 -0
- src/quality_gate.py +460 -0
- src/query_rewriter.py +300 -0
- src/query_router.py +285 -0
- src/recall_modes.py +278 -0
- src/reembed.py +307 -0
- src/reflection/__init__.py +1 -0
- src/reflection/agent.py +688 -0
- src/reflection/digest.py +484 -0
- src/reflection/scheduler.py +2202 -0
- src/reflection/synthesize.py +463 -0
- src/representations.py +231 -0
- src/representations_queue.py +204 -0
- src/reranker.py +672 -0
- src/server.py +6799 -0
- src/session_continuity.py +413 -0
- src/subject_predicate_retriever.py +184 -0
- src/task_classifier.py +181 -0
- src/task_phases.py +299 -0
- src/temporal_filter.py +199 -0
- src/temporal_index.py +368 -0
- src/temporal_kg.py +348 -0
- src/tools/__init__.py +0 -0
- src/tools/analyze_project.py +396 -0
- src/tools/backfill_orphan_edges.py +159 -0
- src/tools/backfill_v6.py +82 -0
- src/tools/benchmark.py +940 -0
- src/tools/brain_autonomy.py +614 -0
- src/tools/brain_health.py +485 -0
- src/tools/check_updates.py +166 -0
- src/tools/context_layers.py +472 -0
- src/tools/cross_project.py +1021 -0
- src/tools/dependency_monitor.py +285 -0
- src/tools/export_knowledge.py +314 -0
- src/tools/git_observer.py +968 -0
- src/tools/graph_enrichment.py +1100 -0
- src/tools/idea_engine.py +1215 -0
- src/tools/import_projects_now.py +312 -0
- src/tools/improve_search.py +877 -0
- src/tools/llm_router.py +397 -0
- src/tools/merge_duplicate_nodes.py +491 -0
- src/tools/obsidian_sync.py +399 -0
- src/tools/predictive.py +1176 -0
- src/tools/run_reflection.py +191 -0
- src/tools/task_manager.py +816 -0
- src/tools/tech_radar.py +275 -0
- src/tools/version_status.py +141 -0
- src/triple_extraction_queue.py +208 -0
- src/v11_handlers.py +236 -0
- src/validator.py +235 -0
- src/verbosity.py +49 -0
- src/version.py +10 -0
- src/workers/__init__.py +34 -0
- src/workers/consolidation_daemon.py +849 -0
- src/workers/project_activity.py +193 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""v9.0 D9 — `ctm-lookup` CLI: bash-friendly memory search for sub-agents.
|
|
2
|
+
|
|
3
|
+
Replaces the legacy `~/claude-memory-server/ollama/lookup_memory.sh` script
|
|
4
|
+
that was *only* distributed through manual setup. This module ships in the
|
|
5
|
+
pip package so any client that did `pip install claude-total-memory` (or
|
|
6
|
+
`uv pip install …`) gets a working `ctm-lookup` binary on PATH.
|
|
7
|
+
|
|
8
|
+
Design goals:
|
|
9
|
+
* **Zero extra deps** — uses only what claude_total_memory.server already
|
|
10
|
+
pulls in (mcp[cli], chromadb, sentence-transformers, sqlite3 stdlib).
|
|
11
|
+
* **No Ollama / RAG / LLM call** — pure retrieval. Sub-agents that need
|
|
12
|
+
a chat model wrap us with their own.
|
|
13
|
+
* **Same DB the running MCP server uses** — reads `$CLAUDE_MEMORY_DIR/memory.db`,
|
|
14
|
+
so results match what the parent agent sees through MCP tools.
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
ctm-lookup "query"
|
|
18
|
+
ctm-lookup --project myproj "query"
|
|
19
|
+
ctm-lookup --limit 5 --json "query"
|
|
20
|
+
ctm-lookup --tag reusable --type solution "query"
|
|
21
|
+
|
|
22
|
+
Output: human-readable bullets by default; `--json` for structured stdout
|
|
23
|
+
(stable schema for machine consumption by agents).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import argparse
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
import sqlite3
|
|
32
|
+
import sys
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
DEFAULT_LIMIT = 8
|
|
36
|
+
DEFAULT_PROJECT_FILTER = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _resolve_db_path() -> Path:
|
|
40
|
+
"""Honor CLAUDE_MEMORY_DIR (same env the server reads) → fall back to ~/.claude-memory."""
|
|
41
|
+
base = os.environ.get("CLAUDE_MEMORY_DIR") or os.path.expanduser("~/.claude-memory")
|
|
42
|
+
p = Path(base) / "memory.db"
|
|
43
|
+
return p
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _open_db(db_path: Path) -> sqlite3.Connection:
|
|
47
|
+
if not db_path.exists():
|
|
48
|
+
sys.stderr.write(
|
|
49
|
+
f"[ctm-lookup] memory.db not found at {db_path}\n"
|
|
50
|
+
" Run the MCP server once (or set CLAUDE_MEMORY_DIR) so the DB initialises.\n"
|
|
51
|
+
)
|
|
52
|
+
sys.exit(2)
|
|
53
|
+
conn = sqlite3.connect(str(db_path))
|
|
54
|
+
conn.row_factory = sqlite3.Row
|
|
55
|
+
return conn
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _has_fts(conn: sqlite3.Connection) -> bool:
|
|
59
|
+
"""v8+ ships an FTS5 virtual table `knowledge_fts`. Fall back to LIKE if absent."""
|
|
60
|
+
try:
|
|
61
|
+
conn.execute("SELECT 1 FROM knowledge_fts LIMIT 1")
|
|
62
|
+
return True
|
|
63
|
+
except sqlite3.OperationalError:
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def search(
|
|
68
|
+
conn: sqlite3.Connection,
|
|
69
|
+
query: str,
|
|
70
|
+
*,
|
|
71
|
+
project: str | None = None,
|
|
72
|
+
types: list[str] | None = None,
|
|
73
|
+
tags: list[str] | None = None,
|
|
74
|
+
limit: int = DEFAULT_LIMIT,
|
|
75
|
+
) -> list[dict]:
|
|
76
|
+
"""Hybrid BM25 (FTS5) + LIKE fallback. Filters: project, type, tags."""
|
|
77
|
+
use_fts = _has_fts(conn)
|
|
78
|
+
|
|
79
|
+
where: list[str] = ["k.status = 'active'"]
|
|
80
|
+
params: list = []
|
|
81
|
+
|
|
82
|
+
if project:
|
|
83
|
+
where.append("k.project = ?")
|
|
84
|
+
params.append(project)
|
|
85
|
+
if types:
|
|
86
|
+
placeholders = ",".join("?" * len(types))
|
|
87
|
+
where.append(f"k.type IN ({placeholders})")
|
|
88
|
+
params.extend(types)
|
|
89
|
+
if tags:
|
|
90
|
+
# tags is JSON in DB; LIKE on substring is sufficient for the lookup CLI.
|
|
91
|
+
for t in tags:
|
|
92
|
+
where.append("k.tags LIKE ?")
|
|
93
|
+
params.append(f'%"{t}"%')
|
|
94
|
+
|
|
95
|
+
if use_fts:
|
|
96
|
+
# FTS5 BM25 ranking — uses the virtual table on `content`.
|
|
97
|
+
sql = (
|
|
98
|
+
"SELECT k.id, k.project, k.type, k.content, k.tags, k.created_at, "
|
|
99
|
+
" bm25(knowledge_fts) AS score "
|
|
100
|
+
"FROM knowledge_fts "
|
|
101
|
+
"JOIN knowledge k ON k.id = knowledge_fts.rowid "
|
|
102
|
+
"WHERE knowledge_fts MATCH ? "
|
|
103
|
+
f"AND {' AND '.join(where)} "
|
|
104
|
+
"ORDER BY score LIMIT ?"
|
|
105
|
+
)
|
|
106
|
+
rows = conn.execute(sql, (_fts_query(query), *params, limit)).fetchall()
|
|
107
|
+
else:
|
|
108
|
+
sql = (
|
|
109
|
+
"SELECT k.id, k.project, k.type, k.content, k.tags, k.created_at, "
|
|
110
|
+
" 0.0 AS score "
|
|
111
|
+
"FROM knowledge k "
|
|
112
|
+
f"WHERE {' AND '.join(where)} AND k.content LIKE ? "
|
|
113
|
+
"ORDER BY k.created_at DESC LIMIT ?"
|
|
114
|
+
)
|
|
115
|
+
rows = conn.execute(sql, (*params, f"%{query}%", limit)).fetchall()
|
|
116
|
+
|
|
117
|
+
return [dict(r) for r in rows]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _fts_query(q: str) -> str:
|
|
121
|
+
"""Quote bare tokens for FTS5 so punctuation in user input doesn't break parsing."""
|
|
122
|
+
parts = []
|
|
123
|
+
for tok in q.split():
|
|
124
|
+
tok = tok.replace('"', '""')
|
|
125
|
+
parts.append(f'"{tok}"')
|
|
126
|
+
return " ".join(parts) or '""'
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def render_human(rows: list[dict]) -> str:
|
|
130
|
+
if not rows:
|
|
131
|
+
return "(no matches)"
|
|
132
|
+
out: list[str] = []
|
|
133
|
+
for i, r in enumerate(rows, 1):
|
|
134
|
+
content = (r.get("content") or "").strip()
|
|
135
|
+
snippet = content if len(content) <= 280 else content[:277] + "..."
|
|
136
|
+
meta = f"[{r.get('type', '?')}|{r.get('project', '?')}]"
|
|
137
|
+
out.append(f"{i}. {meta} {snippet}")
|
|
138
|
+
return "\n".join(out)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def main(argv: list[str] | None = None) -> int:
|
|
142
|
+
p = argparse.ArgumentParser(
|
|
143
|
+
prog="ctm-lookup",
|
|
144
|
+
description="Search claude-total-memory DB from the shell.",
|
|
145
|
+
)
|
|
146
|
+
p.add_argument("query", nargs="+", help="Search query (free text).")
|
|
147
|
+
p.add_argument("-p", "--project", default=DEFAULT_PROJECT_FILTER,
|
|
148
|
+
help="Restrict to a single project.")
|
|
149
|
+
p.add_argument("-l", "--limit", type=int, default=DEFAULT_LIMIT,
|
|
150
|
+
help=f"Max results (default {DEFAULT_LIMIT}).")
|
|
151
|
+
p.add_argument("-t", "--type", action="append",
|
|
152
|
+
help="Filter by knowledge type. Repeat for multiple.")
|
|
153
|
+
p.add_argument("--tag", action="append",
|
|
154
|
+
help="Require this tag. Repeat for multiple (AND).")
|
|
155
|
+
p.add_argument("--json", dest="json_out", action="store_true",
|
|
156
|
+
help="Emit machine-readable JSON instead of bullets.")
|
|
157
|
+
p.add_argument("--db", type=Path, default=None,
|
|
158
|
+
help="Path to memory.db (default: $CLAUDE_MEMORY_DIR/memory.db).")
|
|
159
|
+
args = p.parse_args(argv)
|
|
160
|
+
|
|
161
|
+
db_path = args.db or _resolve_db_path()
|
|
162
|
+
conn = _open_db(db_path)
|
|
163
|
+
try:
|
|
164
|
+
rows = search(
|
|
165
|
+
conn,
|
|
166
|
+
" ".join(args.query),
|
|
167
|
+
project=args.project,
|
|
168
|
+
types=args.type,
|
|
169
|
+
tags=args.tag,
|
|
170
|
+
limit=args.limit,
|
|
171
|
+
)
|
|
172
|
+
finally:
|
|
173
|
+
conn.close()
|
|
174
|
+
|
|
175
|
+
if args.json_out:
|
|
176
|
+
print(json.dumps(rows, ensure_ascii=False, indent=2, default=str))
|
|
177
|
+
else:
|
|
178
|
+
print(render_human(rows))
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Entry point for pip-installed package. Re-exports from src/server.py."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
# Add parent directory to path so we can import the actual server
|
|
8
|
+
_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
9
|
+
_server_path = os.path.join(_root, "src", "server.py")
|
|
10
|
+
|
|
11
|
+
if os.path.exists(_server_path):
|
|
12
|
+
import importlib.util
|
|
13
|
+
spec = importlib.util.spec_from_file_location("_server", _server_path)
|
|
14
|
+
_mod = importlib.util.module_from_spec(spec)
|
|
15
|
+
spec.loader.exec_module(_mod)
|
|
16
|
+
main = _mod.main
|
|
17
|
+
else:
|
|
18
|
+
async def main():
|
|
19
|
+
print("Error: server.py not found", file=sys.stderr)
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main_sync():
|
|
24
|
+
"""Synchronous entry point for console_scripts."""
|
|
25
|
+
asyncio.run(main())
|