lorekeep 0.7.0__tar.gz → 0.7.1__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.
- lorekeep-0.7.1/.release-please-manifest.json +3 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/CHANGELOG.md +7 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/PKG-INFO +1 -1
- {lorekeep-0.7.0 → lorekeep-0.7.1}/pyproject.toml +1 -1
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/__init__.py +1 -1
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/compile/extract.py +5 -2
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/mcp_server.py +19 -4
- lorekeep-0.7.1/tests/test_fts.py +109 -0
- lorekeep-0.7.0/.release-please-manifest.json +0 -3
- lorekeep-0.7.0/tests/test_fts.py +0 -31
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.commitlintrc.json +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.github/workflows/auto-merge-release-please.yml +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.github/workflows/ci.yml +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.github/workflows/lint-commits.yml +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.github/workflows/release-please.yml +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.gitignore +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.lorekeep/config.yaml.example +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.lorekeep/schema.json +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/.pre-commit-config.yaml +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/AGENTS.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/CLAUDE.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/LICENSE +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/README.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/SECURITY.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/cover.jpeg +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/README.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/agent.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/data-model.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/evaluation.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/journal.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/overview.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/permission.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/pipeline.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/serve-mcp.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/architecture/temporal.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/backup.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/compile.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/data-home.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/getting-started.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/import.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/serve.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/guides/wiki.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/superpowers/plans/2026-06-26-lorekeep-dotdir-backup.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/docs/superpowers/specs/2026-06-26-lorekeep-dotdir-layout-design.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/release-please-config.json +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/scripts/check-conventional-commit.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/agent.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/backup.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/compile/__init__.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/compile/ingest.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/compile/providers.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/compile/resolve.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/compile/writer.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/config.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/defaults.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/eval/__init__.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/eval/construction.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/eval/gold.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/eval/retrieval.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/facts_io.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/importer/__init__.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/importer/claude.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/importer/codex.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/importer/cursor.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/importer/opencode.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/__init__.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/claude_code.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/codex.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/common.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/cursor.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/detect.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/integrations/opencode.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/journal.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/models.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/paths.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/perm/__init__.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/perm/ns.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/pipeline.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/schema_io.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/store/__init__.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/store/fts.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/store/graph.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/src/lorekeep/wiki.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/conftest.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/claude-session/memory/MEMORY.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/claude-session/memory/lorekeep-design.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/claude-session/memory/lorekeep-project-state.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/claude-session/test.jsonl +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/gold/payments.facts.jsonl +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/raw/backend/payments.md +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/retrieval/questions.json +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/schema.json +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_agent_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_agent_detect.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_backup.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_backup_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_compile_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_config.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_construction_eval.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_defaults.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_determinism.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_doctor_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_eval_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_extract.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_graph_store.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_import_claude.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_import_codex.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_import_cursor.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_import_opencode.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_ingest.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_init_chain.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_init_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_integrations.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_mcp_add_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_mcp_reload.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_mcp_server.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_mcp_write_tools.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_merge_journals.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_models.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_paths.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_perm.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_pipeline.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_providers.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_resolve.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_retrieval_eval.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_schema_io.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_scope_awareness.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_serve_cli.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_session_hook.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_smoke.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_watch_e2e.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_watch_sessions.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_wiki.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/tests/test_writer.py +0 -0
- {lorekeep-0.7.0 → lorekeep-0.7.1}/uv.lock +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.1](https://github.com/manhhailua/lorekeep/compare/v0.7.0...v0.7.1) (2026-06-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* wire FTS5 search index + include model in extraction cache key ([#74](https://github.com/manhhailua/lorekeep/issues/74)) ([e9cd60a](https://github.com/manhhailua/lorekeep/commit/e9cd60ab573ae42b172f888cf3af5647e747c3f7))
|
|
9
|
+
|
|
3
10
|
## [0.7.0](https://github.com/manhhailua/lorekeep/compare/v0.6.1...v0.7.0) (2026-06-30)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -119,10 +119,12 @@ class ExtractionCache:
|
|
|
119
119
|
if self.path.exists():
|
|
120
120
|
self._data = json.loads(self.path.read_text(encoding="utf-8"))
|
|
121
121
|
|
|
122
|
-
def key(self, chunk: DocChunk, schema_version: int) -> str:
|
|
122
|
+
def key(self, chunk: DocChunk, schema_version: int, model: str = "") -> str:
|
|
123
123
|
h = hashlib.sha256()
|
|
124
124
|
h.update(str(schema_version).encode("utf-8"))
|
|
125
125
|
h.update(b"\n")
|
|
126
|
+
h.update(model.encode("utf-8"))
|
|
127
|
+
h.update(b"\n")
|
|
126
128
|
h.update(chunk.hash.encode("utf-8"))
|
|
127
129
|
return h.hexdigest()
|
|
128
130
|
|
|
@@ -142,7 +144,8 @@ class ExtractionCache:
|
|
|
142
144
|
def extract_chunk(
|
|
143
145
|
chunk: DocChunk, schema: Schema, provider: LLMProvider, cache: ExtractionCache,
|
|
144
146
|
) -> tuple[list[Node], list[Edge], dict[str, list[str]]]:
|
|
145
|
-
|
|
147
|
+
model = getattr(provider, "model", "")
|
|
148
|
+
key = cache.key(chunk, schema.version, model)
|
|
146
149
|
raw = cache.get(key)
|
|
147
150
|
if raw is None:
|
|
148
151
|
raw = provider.extract_json(SYSTEM_PROMPT, build_prompt(chunk, schema))
|
|
@@ -19,13 +19,15 @@ from lorekeep.models import JournalEntry, Manifest, Schema
|
|
|
19
19
|
from lorekeep.perm.ns import ScopedGraph
|
|
20
20
|
from lorekeep.schema_io import load_schema
|
|
21
21
|
from lorekeep.store.graph import GraphStore, parse_date
|
|
22
|
+
from lorekeep.store.fts import FTSIndex
|
|
22
23
|
|
|
23
24
|
mcp = FastMCP("lorekeep")
|
|
24
25
|
|
|
25
|
-
_state: dict = {} # graph_dir, allowed_ns, schema_path, pending_dir, facts_mtime
|
|
26
|
+
_state: dict = {} # graph_dir, allowed_ns, schema_path, pending_dir, fts_path, facts_mtime
|
|
26
27
|
_scope: ScopedGraph | None = None
|
|
27
28
|
_schema: Schema | None = None
|
|
28
29
|
_manifest: Manifest | None = None
|
|
30
|
+
_fts: FTSIndex | None = None
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
def configure(graph_dir, allowed_ns, schema_path=None, fts_path=None, pending_dir=None) -> None:
|
|
@@ -34,12 +36,16 @@ def configure(graph_dir, allowed_ns, schema_path=None, fts_path=None, pending_di
|
|
|
34
36
|
_state["allowed_ns"] = list(allowed_ns)
|
|
35
37
|
_state["schema_path"] = Path(schema_path) if schema_path else None
|
|
36
38
|
_state["pending_dir"] = Path(pending_dir) if pending_dir else None
|
|
39
|
+
if fts_path:
|
|
40
|
+
_state["fts_path"] = Path(fts_path)
|
|
41
|
+
else:
|
|
42
|
+
_state["fts_path"] = _state["graph_dir"] / "fts.sqlite"
|
|
37
43
|
_rebuild()
|
|
38
44
|
|
|
39
45
|
|
|
40
46
|
def _rebuild() -> None:
|
|
41
|
-
"""(Re)load the graph + schema + manifest from disk into a fresh ScopedGraph."""
|
|
42
|
-
global _scope, _schema, _manifest
|
|
47
|
+
"""(Re)load the graph + schema + manifest + FTS from disk into a fresh ScopedGraph."""
|
|
48
|
+
global _scope, _schema, _manifest, _fts
|
|
43
49
|
facts = _state["graph_dir"] / "facts.jsonl"
|
|
44
50
|
store = GraphStore.from_jsonl(facts)
|
|
45
51
|
sp = _state.get("schema_path")
|
|
@@ -50,6 +56,15 @@ def _rebuild() -> None:
|
|
|
50
56
|
_manifest = Manifest.from_json(manifest_path.read_text(encoding="utf-8"))
|
|
51
57
|
else:
|
|
52
58
|
_manifest = None
|
|
59
|
+
try:
|
|
60
|
+
fts_path = _state.get("fts_path")
|
|
61
|
+
if fts_path:
|
|
62
|
+
_fts = FTSIndex(fts_path)
|
|
63
|
+
_fts.build(store.all_nodes())
|
|
64
|
+
else:
|
|
65
|
+
_fts = None
|
|
66
|
+
except Exception:
|
|
67
|
+
_fts = None
|
|
53
68
|
_state["facts_mtime"] = facts.stat().st_mtime if facts.exists() else 0
|
|
54
69
|
|
|
55
70
|
|
|
@@ -125,7 +140,7 @@ def changes(from_t: str, to_t: str) -> dict:
|
|
|
125
140
|
@mcp.tool()
|
|
126
141
|
def search(query: str, limit: int = 10) -> list:
|
|
127
142
|
"""Text search over node ids/props, scoped to the caller."""
|
|
128
|
-
return _require().search(query, limit)
|
|
143
|
+
return _require().search(query, limit, fts=_fts)
|
|
129
144
|
|
|
130
145
|
|
|
131
146
|
@mcp.tool()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from lorekeep.models import Node
|
|
3
|
+
from lorekeep.store.fts import FTSIndex, scan_search, node_text
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def nd(id, name, lang=None):
|
|
7
|
+
props = {"name": name}
|
|
8
|
+
if lang:
|
|
9
|
+
props["lang"] = lang
|
|
10
|
+
return Node(id=id, type="service", ns=("teams/backend",), props=props)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_node_text_concatenates_id_name_props():
|
|
14
|
+
t = node_text(nd("svc:x", "auth", "go"))
|
|
15
|
+
assert "svc:x" in t and "auth" in t and "go" in t
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_scan_search_substring(tmp_path: Path):
|
|
19
|
+
nodes = [nd("svc:a", "payments-api"), nd("svc:b", "auth")]
|
|
20
|
+
assert scan_search(nodes, "pay") == ["svc:a"]
|
|
21
|
+
assert scan_search(nodes, "auth") == ["svc:b"]
|
|
22
|
+
assert scan_search(nodes, "zzz") == []
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_fts_index_build_and_match(tmp_path: Path):
|
|
26
|
+
idx = FTSIndex(tmp_path / "fts.sqlite")
|
|
27
|
+
idx.build([nd("svc:a", "payments"), nd("svc:b", "auth")])
|
|
28
|
+
assert "svc:a" in idx.search("payments")
|
|
29
|
+
assert idx.search("payments") == ["svc:a"]
|
|
30
|
+
assert idx.search("nomatch*") == []
|
|
31
|
+
idx.close()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_fts_different_keys_for_different_models(tmp_path: Path):
|
|
35
|
+
"""Cache key must differ when model changes."""
|
|
36
|
+
from lorekeep.models import DocChunk
|
|
37
|
+
from lorekeep.compile.extract import ExtractionCache
|
|
38
|
+
|
|
39
|
+
cache = ExtractionCache(tmp_path / "cache.json")
|
|
40
|
+
chunk = DocChunk(
|
|
41
|
+
path="raw/test.md", start_line=1, end_line=10,
|
|
42
|
+
text="hello", namespace="test",
|
|
43
|
+
)
|
|
44
|
+
key_a = cache.key(chunk, schema_version=1, model="gpt-4o-mini")
|
|
45
|
+
key_b = cache.key(chunk, schema_version=1, model="claude-sonnet-4")
|
|
46
|
+
assert key_a != key_b
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_fts_cache_key_backward_compat(tmp_path: Path):
|
|
50
|
+
"""Old cache keys (no model) should not collide with new keys (empty model)."""
|
|
51
|
+
from lorekeep.models import DocChunk
|
|
52
|
+
from lorekeep.compile.extract import ExtractionCache
|
|
53
|
+
|
|
54
|
+
cache = ExtractionCache(tmp_path / "cache.json")
|
|
55
|
+
chunk = DocChunk(
|
|
56
|
+
path="raw/test.md", start_line=1, end_line=10,
|
|
57
|
+
text="hello", namespace="test",
|
|
58
|
+
)
|
|
59
|
+
key_default = cache.key(chunk, schema_version=1)
|
|
60
|
+
key_empty = cache.key(chunk, schema_version=1, model="")
|
|
61
|
+
assert key_default == key_empty
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_mcp_search_uses_fts(tmp_path: Path):
|
|
65
|
+
"""mcp_server search tool uses FTSIndex when configured."""
|
|
66
|
+
from lorekeep.mcp_server import configure, search
|
|
67
|
+
from lorekeep.compile.writer import write_graph
|
|
68
|
+
from lorekeep.models import Edge, Manifest
|
|
69
|
+
|
|
70
|
+
nodes = [
|
|
71
|
+
Node(id="svc:payments", type="service", ns=("backend",), props={"name": "payments", "lang": "go"}),
|
|
72
|
+
Node(id="svc:auth", type="service", ns=("backend",), props={"name": "auth"}),
|
|
73
|
+
]
|
|
74
|
+
graph = tmp_path / "graph"
|
|
75
|
+
write_graph(graph, nodes, [], Manifest(
|
|
76
|
+
schema_version=1, chunk_count=0, node_count=2, edge_count=0,
|
|
77
|
+
run_id="x", facts_hash="y",
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
configure(graph_dir=graph, allowed_ns=["backend"])
|
|
81
|
+
results = search("payments")
|
|
82
|
+
assert "svc:payments" in results
|
|
83
|
+
assert "svc:auth" not in results
|
|
84
|
+
|
|
85
|
+
results = search("auth")
|
|
86
|
+
assert "svc:auth" in results
|
|
87
|
+
|
|
88
|
+
fts_db = graph / "fts.sqlite"
|
|
89
|
+
assert fts_db.exists()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_mcp_search_fts_fallback_on_error(tmp_path: Path):
|
|
93
|
+
"""If FTS fails to build, search falls back to scan."""
|
|
94
|
+
from lorekeep.mcp_server import configure, search
|
|
95
|
+
from lorekeep.compile.writer import write_graph
|
|
96
|
+
from lorekeep.models import Manifest
|
|
97
|
+
|
|
98
|
+
nodes = [
|
|
99
|
+
Node(id="svc:payments", type="service", ns=("backend",), props={"name": "payments"}),
|
|
100
|
+
]
|
|
101
|
+
graph = tmp_path / "graph"
|
|
102
|
+
write_graph(graph, nodes, [], Manifest(
|
|
103
|
+
schema_version=1, chunk_count=0, node_count=1, edge_count=0,
|
|
104
|
+
run_id="x", facts_hash="y",
|
|
105
|
+
))
|
|
106
|
+
|
|
107
|
+
configure(graph_dir=graph, allowed_ns=["backend"], fts_path=tmp_path / "nonexistent" / "deep" / "fts.sqlite")
|
|
108
|
+
results = search("payments")
|
|
109
|
+
assert "svc:payments" in results
|
lorekeep-0.7.0/tests/test_fts.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from lorekeep.models import Node
|
|
3
|
-
from lorekeep.store.fts import FTSIndex, scan_search, node_text
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def nd(id, name, lang=None):
|
|
7
|
-
props = {"name": name}
|
|
8
|
-
if lang:
|
|
9
|
-
props["lang"] = lang
|
|
10
|
-
return Node(id=id, type="service", ns=("teams/backend",), props=props)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_node_text_concatenates_id_name_props():
|
|
14
|
-
t = node_text(nd("svc:x", "auth", "go"))
|
|
15
|
-
assert "svc:x" in t and "auth" in t and "go" in t
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_scan_search_substring(tmp_path: Path):
|
|
19
|
-
nodes = [nd("svc:a", "payments-api"), nd("svc:b", "auth")]
|
|
20
|
-
assert scan_search(nodes, "pay") == ["svc:a"]
|
|
21
|
-
assert scan_search(nodes, "auth") == ["svc:b"]
|
|
22
|
-
assert scan_search(nodes, "zzz") == []
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_fts_index_build_and_match(tmp_path: Path):
|
|
26
|
-
idx = FTSIndex(tmp_path / "fts.sqlite")
|
|
27
|
-
idx.build([nd("svc:a", "payments"), nd("svc:b", "auth")])
|
|
28
|
-
assert "svc:a" in idx.search("payments")
|
|
29
|
-
assert idx.search("payments") == ["svc:a"]
|
|
30
|
-
assert idx.search("nomatch*") == []
|
|
31
|
-
idx.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lorekeep-0.7.0 → lorekeep-0.7.1}/docs/superpowers/plans/2026-06-26-lorekeep-dotdir-backup.md
RENAMED
|
File without changes
|
{lorekeep-0.7.0 → lorekeep-0.7.1}/docs/superpowers/specs/2026-06-26-lorekeep-dotdir-layout-design.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lorekeep-0.7.0 → lorekeep-0.7.1}/tests/fixtures/claude-session/memory/lorekeep-project-state.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|