memstem 0.17.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.
- memstem-0.17.0/.github/ISSUE_TEMPLATE/adapter_request.md +31 -0
- memstem-0.17.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- memstem-0.17.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- memstem-0.17.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- memstem-0.17.0/.github/dependabot.yml +23 -0
- memstem-0.17.0/.github/pull_request_template.md +27 -0
- memstem-0.17.0/.github/workflows/ci.yml +72 -0
- memstem-0.17.0/.github/workflows/release.yml +85 -0
- memstem-0.17.0/.gitignore +86 -0
- memstem-0.17.0/.pre-commit-config.yaml +36 -0
- memstem-0.17.0/.python-version +1 -0
- memstem-0.17.0/ARCHITECTURE.md +175 -0
- memstem-0.17.0/CHANGELOG.md +2220 -0
- memstem-0.17.0/CLAUDE.md +70 -0
- memstem-0.17.0/CODE_OF_CONDUCT.md +84 -0
- memstem-0.17.0/CONTRIBUTING.md +57 -0
- memstem-0.17.0/LICENSE +21 -0
- memstem-0.17.0/PKG-INFO +505 -0
- memstem-0.17.0/README.md +464 -0
- memstem-0.17.0/ROADMAP.md +82 -0
- memstem-0.17.0/SECURITY.md +29 -0
- memstem-0.17.0/clients/codex/AGENTS.md.example +62 -0
- memstem-0.17.0/clients/codex/README.md +144 -0
- memstem-0.17.0/clients/codex/config.toml.fragment +10 -0
- memstem-0.17.0/clients/skills/memstem-search/SKILL.md +114 -0
- memstem-0.17.0/docs/decisions/0001-greenfield-not-fork.md +31 -0
- memstem-0.17.0/docs/decisions/0002-markdown-canonical.md +31 -0
- memstem-0.17.0/docs/decisions/0003-sqlite-with-fts5-and-vec.md +34 -0
- memstem-0.17.0/docs/decisions/0004-mit-license.md +39 -0
- memstem-0.17.0/docs/decisions/0005-pull-based-ingestion.md +36 -0
- memstem-0.17.0/docs/decisions/0006-anthropic-memory-tool-adapter.md +32 -0
- memstem-0.17.0/docs/decisions/0007-remote-ingestion.md +114 -0
- memstem-0.17.0/docs/decisions/0008-tiered-memory.md +360 -0
- memstem-0.17.0/docs/decisions/0009-pluggable-embedders-and-queue.md +199 -0
- memstem-0.17.0/docs/decisions/0010-obsidian-plugin.md +20 -0
- memstem-0.17.0/docs/decisions/0011-noise-filter-and-fact-extraction.md +311 -0
- memstem-0.17.0/docs/decisions/0012-llm-judge-dedup.md +439 -0
- memstem-0.17.0/docs/decisions/0013-workspace-extra-files.md +90 -0
- memstem-0.17.0/docs/decisions/0014-cli-daemon-delegation-and-migration-discipline.md +253 -0
- memstem-0.17.0/docs/decisions/0015-eval-harness.md +190 -0
- memstem-0.17.0/docs/decisions/0016-mmr-diversification.md +191 -0
- memstem-0.17.0/docs/decisions/0017-cross-encoder-rerank.md +390 -0
- memstem-0.17.0/docs/decisions/0018-hyde-query-expansion.md +394 -0
- memstem-0.17.0/docs/decisions/0019-no-skill-authoring.md +201 -0
- memstem-0.17.0/docs/decisions/0020-session-distillation-writer.md +325 -0
- memstem-0.17.0/docs/decisions/0021-project-records.md +313 -0
- memstem-0.17.0/docs/decisions/0022-codex-adapter.md +203 -0
- memstem-0.17.0/docs/decisions/0023-in-daemon-hygiene-loop.md +140 -0
- memstem-0.17.0/docs/decisions/0024-incremental-startup-reconcile.md +100 -0
- memstem-0.17.0/docs/decisions/0025-multimodal-embeddings.md +123 -0
- memstem-0.17.0/docs/dedupe-audit.md +404 -0
- memstem-0.17.0/docs/distillation-verification.md +250 -0
- memstem-0.17.0/docs/frontmatter-spec.md +104 -0
- memstem-0.17.0/docs/images/hero.png +0 -0
- memstem-0.17.0/docs/images/hybrid-search.png +0 -0
- memstem-0.17.0/docs/install.md +101 -0
- memstem-0.17.0/docs/mcp-api.md +128 -0
- memstem-0.17.0/docs/operations.md +434 -0
- memstem-0.17.0/docs/recall-eval-results.md +161 -0
- memstem-0.17.0/docs/recall-models.md +322 -0
- memstem-0.17.0/docs/secrets.md +173 -0
- memstem-0.17.0/eval/queries.yaml +151 -0
- memstem-0.17.0/examples/example_memory.md +23 -0
- memstem-0.17.0/examples/example_skill.md +37 -0
- memstem-0.17.0/pyproject.toml +145 -0
- memstem-0.17.0/scripts/dedupe_audit_report.py +792 -0
- memstem-0.17.0/scripts/dedupe_phase1_apply.py +477 -0
- memstem-0.17.0/scripts/dedupe_phase1_select.py +428 -0
- memstem-0.17.0/scripts/e2e-smoke.sh +326 -0
- memstem-0.17.0/scripts/install.sh +495 -0
- memstem-0.17.0/scripts/migrate-from-flipclaw.py +18 -0
- memstem-0.17.0/scripts/run_eval.py +139 -0
- memstem-0.17.0/scripts/smoke_0_7_0.sh +234 -0
- memstem-0.17.0/src/memstem/__init__.py +3 -0
- memstem-0.17.0/src/memstem/__main__.py +6 -0
- memstem-0.17.0/src/memstem/adapters/__init__.py +1 -0
- memstem-0.17.0/src/memstem/adapters/base.py +64 -0
- memstem-0.17.0/src/memstem/adapters/claude_code.py +351 -0
- memstem-0.17.0/src/memstem/adapters/codex.py +508 -0
- memstem-0.17.0/src/memstem/adapters/openclaw.py +664 -0
- memstem-0.17.0/src/memstem/auth.py +124 -0
- memstem-0.17.0/src/memstem/cli.py +2723 -0
- memstem-0.17.0/src/memstem/client.py +280 -0
- memstem-0.17.0/src/memstem/config.py +532 -0
- memstem-0.17.0/src/memstem/core/__init__.py +1 -0
- memstem-0.17.0/src/memstem/core/dedup.py +117 -0
- memstem-0.17.0/src/memstem/core/embed_worker.py +426 -0
- memstem-0.17.0/src/memstem/core/embeddings.py +712 -0
- memstem-0.17.0/src/memstem/core/extraction.py +395 -0
- memstem-0.17.0/src/memstem/core/frontmatter.py +224 -0
- memstem-0.17.0/src/memstem/core/hyde.py +504 -0
- memstem-0.17.0/src/memstem/core/importance_seed.py +183 -0
- memstem-0.17.0/src/memstem/core/index.py +1351 -0
- memstem-0.17.0/src/memstem/core/media.py +102 -0
- memstem-0.17.0/src/memstem/core/mmr.py +167 -0
- memstem-0.17.0/src/memstem/core/pipeline.py +309 -0
- memstem-0.17.0/src/memstem/core/rerank.py +672 -0
- memstem-0.17.0/src/memstem/core/retrieval_log.py +193 -0
- memstem-0.17.0/src/memstem/core/search.py +602 -0
- memstem-0.17.0/src/memstem/core/storage.py +190 -0
- memstem-0.17.0/src/memstem/core/summarizer.py +480 -0
- memstem-0.17.0/src/memstem/discovery.py +227 -0
- memstem-0.17.0/src/memstem/eval/__init__.py +35 -0
- memstem-0.17.0/src/memstem/eval/harness.py +314 -0
- memstem-0.17.0/src/memstem/hygiene/__init__.py +1 -0
- memstem-0.17.0/src/memstem/hygiene/cleanup_retro.py +633 -0
- memstem-0.17.0/src/memstem/hygiene/dedup_candidates.py +334 -0
- memstem-0.17.0/src/memstem/hygiene/dedup_judge.py +555 -0
- memstem-0.17.0/src/memstem/hygiene/distillation.py +241 -0
- memstem-0.17.0/src/memstem/hygiene/importance.py +357 -0
- memstem-0.17.0/src/memstem/hygiene/loop.py +398 -0
- memstem-0.17.0/src/memstem/hygiene/project_records.py +702 -0
- memstem-0.17.0/src/memstem/hygiene/session_distill.py +808 -0
- memstem-0.17.0/src/memstem/hygiene/state.py +232 -0
- memstem-0.17.0/src/memstem/hygiene/verify.py +293 -0
- memstem-0.17.0/src/memstem/integration.py +783 -0
- memstem-0.17.0/src/memstem/migrate.py +247 -0
- memstem-0.17.0/src/memstem/progress.py +164 -0
- memstem-0.17.0/src/memstem/prompts/__init__.py +6 -0
- memstem-0.17.0/src/memstem/prompts/dedup_judge.txt +41 -0
- memstem-0.17.0/src/memstem/prompts/distill_project.txt +89 -0
- memstem-0.17.0/src/memstem/prompts/distill_session.txt +79 -0
- memstem-0.17.0/src/memstem/prompts/hyde.txt +27 -0
- memstem-0.17.0/src/memstem/prompts/rerank.txt +38 -0
- memstem-0.17.0/src/memstem/servers/__init__.py +1 -0
- memstem-0.17.0/src/memstem/servers/http_server.py +436 -0
- memstem-0.17.0/src/memstem/servers/mcp_server.py +539 -0
- memstem-0.17.0/src/memstem/servers/request_limits.py +47 -0
- memstem-0.17.0/src/memstem/star_nudge.py +65 -0
- memstem-0.17.0/tests/__init__.py +0 -0
- memstem-0.17.0/tests/adapters/__init__.py +0 -0
- memstem-0.17.0/tests/adapters/test_claude_code.py +363 -0
- memstem-0.17.0/tests/adapters/test_codex.py +473 -0
- memstem-0.17.0/tests/adapters/test_openclaw.py +880 -0
- memstem-0.17.0/tests/conftest.py +32 -0
- memstem-0.17.0/tests/servers/__init__.py +0 -0
- memstem-0.17.0/tests/servers/test_http_server.py +428 -0
- memstem-0.17.0/tests/servers/test_request_limits.py +38 -0
- memstem-0.17.0/tests/test_auth.py +189 -0
- memstem-0.17.0/tests/test_cli.py +1303 -0
- memstem-0.17.0/tests/test_client.py +515 -0
- memstem-0.17.0/tests/test_config_rerank.py +44 -0
- memstem-0.17.0/tests/test_config_validation.py +112 -0
- memstem-0.17.0/tests/test_daemon_reconcile.py +238 -0
- memstem-0.17.0/tests/test_dedup.py +239 -0
- memstem-0.17.0/tests/test_dedupe_audit_report.py +304 -0
- memstem-0.17.0/tests/test_dedupe_phase1_apply.py +494 -0
- memstem-0.17.0/tests/test_dedupe_phase1_select.py +351 -0
- memstem-0.17.0/tests/test_discovery.py +248 -0
- memstem-0.17.0/tests/test_embed_worker.py +924 -0
- memstem-0.17.0/tests/test_embeddings.py +819 -0
- memstem-0.17.0/tests/test_eval_harness.py +433 -0
- memstem-0.17.0/tests/test_extraction.py +576 -0
- memstem-0.17.0/tests/test_frontmatter.py +191 -0
- memstem-0.17.0/tests/test_hyde.py +360 -0
- memstem-0.17.0/tests/test_hygiene_cleanup_retro.py +378 -0
- memstem-0.17.0/tests/test_hygiene_dedup_candidates.py +543 -0
- memstem-0.17.0/tests/test_hygiene_dedup_judge.py +673 -0
- memstem-0.17.0/tests/test_hygiene_distillation.py +343 -0
- memstem-0.17.0/tests/test_hygiene_importance.py +405 -0
- memstem-0.17.0/tests/test_hygiene_loop.py +262 -0
- memstem-0.17.0/tests/test_hygiene_project_records.py +711 -0
- memstem-0.17.0/tests/test_hygiene_session_distill.py +692 -0
- memstem-0.17.0/tests/test_hygiene_state.py +243 -0
- memstem-0.17.0/tests/test_hygiene_verify.py +248 -0
- memstem-0.17.0/tests/test_importance_seed.py +222 -0
- memstem-0.17.0/tests/test_index.py +1287 -0
- memstem-0.17.0/tests/test_install_sh.py +142 -0
- memstem-0.17.0/tests/test_integration.py +835 -0
- memstem-0.17.0/tests/test_mcp_server.py +859 -0
- memstem-0.17.0/tests/test_media.py +82 -0
- memstem-0.17.0/tests/test_migrate.py +161 -0
- memstem-0.17.0/tests/test_mmr.py +236 -0
- memstem-0.17.0/tests/test_pipeline.py +469 -0
- memstem-0.17.0/tests/test_progress.py +382 -0
- memstem-0.17.0/tests/test_rerank.py +797 -0
- memstem-0.17.0/tests/test_retrieval_log.py +337 -0
- memstem-0.17.0/tests/test_search.py +1130 -0
- memstem-0.17.0/tests/test_smoke.py +35 -0
- memstem-0.17.0/tests/test_star_nudge.py +96 -0
- memstem-0.17.0/tests/test_storage.py +241 -0
- memstem-0.17.0/tests/test_summarizer.py +459 -0
- memstem-0.17.0/tests/test_vault_delete_prune.py +123 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Adapter request
|
|
3
|
+
about: Request an adapter for a new AI system
|
|
4
|
+
title: '[ADAPTER] '
|
|
5
|
+
labels: adapter
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Target system
|
|
10
|
+
|
|
11
|
+
Which AI system / tool?
|
|
12
|
+
|
|
13
|
+
## Memory storage location
|
|
14
|
+
|
|
15
|
+
Where does it store sessions / memories on disk? (path patterns, file formats)
|
|
16
|
+
|
|
17
|
+
## File format
|
|
18
|
+
|
|
19
|
+
JSONL, markdown, sqlite, other?
|
|
20
|
+
|
|
21
|
+
## Update frequency
|
|
22
|
+
|
|
23
|
+
How often does the system write?
|
|
24
|
+
|
|
25
|
+
## Existing schema (if any)
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
paste a sample
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Why this matters to you
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something isn't working
|
|
4
|
+
title: '[BUG] '
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
What happened? What did you expect to happen?
|
|
12
|
+
|
|
13
|
+
## Steps to reproduce
|
|
14
|
+
|
|
15
|
+
1. ...
|
|
16
|
+
2. ...
|
|
17
|
+
3. ...
|
|
18
|
+
|
|
19
|
+
## Environment
|
|
20
|
+
|
|
21
|
+
- Memstem version:
|
|
22
|
+
- Python version:
|
|
23
|
+
- OS:
|
|
24
|
+
- AI clients connected (Claude Code / OpenClaw / other):
|
|
25
|
+
|
|
26
|
+
## Logs
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
paste relevant log output here
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Additional context
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest a new capability
|
|
4
|
+
title: '[FEAT] '
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
What pain are you trying to solve?
|
|
12
|
+
|
|
13
|
+
## Proposed solution
|
|
14
|
+
|
|
15
|
+
What would you like to see?
|
|
16
|
+
|
|
17
|
+
## Alternatives considered
|
|
18
|
+
|
|
19
|
+
## Additional context
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "pip"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
open-pull-requests-limit: 5
|
|
8
|
+
labels:
|
|
9
|
+
- "dependencies"
|
|
10
|
+
commit-message:
|
|
11
|
+
prefix: "deps"
|
|
12
|
+
include: "scope"
|
|
13
|
+
|
|
14
|
+
- package-ecosystem: "github-actions"
|
|
15
|
+
directory: "/"
|
|
16
|
+
schedule:
|
|
17
|
+
interval: "monthly"
|
|
18
|
+
labels:
|
|
19
|
+
- "dependencies"
|
|
20
|
+
- "ci"
|
|
21
|
+
commit-message:
|
|
22
|
+
prefix: "ci"
|
|
23
|
+
include: "scope"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
What does this PR do?
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
What problem does it solve?
|
|
8
|
+
|
|
9
|
+
## Changes
|
|
10
|
+
|
|
11
|
+
- Change 1
|
|
12
|
+
- Change 2
|
|
13
|
+
|
|
14
|
+
## Testing
|
|
15
|
+
|
|
16
|
+
How did you verify this works?
|
|
17
|
+
|
|
18
|
+
- [ ] Tests added / updated
|
|
19
|
+
- [ ] Lint passes (`ruff check`, `ruff format --check`)
|
|
20
|
+
- [ ] Type check passes (`mypy src/`)
|
|
21
|
+
- [ ] Documentation updated (if user-facing change)
|
|
22
|
+
|
|
23
|
+
## Architecture decision record
|
|
24
|
+
|
|
25
|
+
If this changes storage layout, search ranking, the adapter interface, or any cross-cutting design — link or include the ADR.
|
|
26
|
+
|
|
27
|
+
## Notes for reviewer
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
- uses: actions/setup-python@v6
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.11"
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
run: |
|
|
19
|
+
pip install -e ".[dev]"
|
|
20
|
+
- name: Ruff check
|
|
21
|
+
run: ruff check .
|
|
22
|
+
- name: Ruff format check
|
|
23
|
+
run: ruff format --check .
|
|
24
|
+
- name: Mypy
|
|
25
|
+
run: mypy src/ tests/ scripts/
|
|
26
|
+
|
|
27
|
+
test:
|
|
28
|
+
runs-on: ${{ matrix.os }}
|
|
29
|
+
continue-on-error: ${{ matrix.experimental }}
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: false
|
|
32
|
+
matrix:
|
|
33
|
+
os: [ubuntu-latest]
|
|
34
|
+
python-version: ["3.11", "3.12"]
|
|
35
|
+
experimental: [false]
|
|
36
|
+
include:
|
|
37
|
+
# macOS is supported for v0.1 manual installs but not gated in CI:
|
|
38
|
+
# actions/setup-python's macOS builds ship Python without
|
|
39
|
+
# `enable_load_extension`, which sqlite-vec requires. The hosted
|
|
40
|
+
# runner image is the constraint here, not Memstem itself — users
|
|
41
|
+
# with `brew install python@3.11` are fine. Marked experimental so
|
|
42
|
+
# failures surface in CI without blocking merge until the runner
|
|
43
|
+
# image catches up.
|
|
44
|
+
- os: macos-latest
|
|
45
|
+
python-version: "3.11"
|
|
46
|
+
experimental: true
|
|
47
|
+
- os: macos-latest
|
|
48
|
+
python-version: "3.12"
|
|
49
|
+
experimental: true
|
|
50
|
+
# Windows isn't supported in v0.1 (use WSL2). We run it in CI anyway
|
|
51
|
+
# for visibility, marked experimental so failures don't block merge.
|
|
52
|
+
- os: windows-latest
|
|
53
|
+
python-version: "3.11"
|
|
54
|
+
experimental: true
|
|
55
|
+
- os: windows-latest
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
experimental: true
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v6
|
|
60
|
+
- uses: actions/setup-python@v6
|
|
61
|
+
with:
|
|
62
|
+
python-version: ${{ matrix.python-version }}
|
|
63
|
+
- name: Install dependencies
|
|
64
|
+
run: |
|
|
65
|
+
pip install -e ".[dev]"
|
|
66
|
+
- name: Run tests
|
|
67
|
+
run: pytest --cov-report=xml
|
|
68
|
+
- name: Upload coverage
|
|
69
|
+
uses: codecov/codecov-action@v6
|
|
70
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
|
|
71
|
+
with:
|
|
72
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
# Manual publishing escape hatch — used for the first PyPI upload (the
|
|
8
|
+
# version was already tagged before the pypi job existed) and for re-runs
|
|
9
|
+
# after a transient publish failure. Skips the GitHub-release job.
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
release:
|
|
17
|
+
name: Publish GitHub release from CHANGELOG
|
|
18
|
+
if: github.event_name == 'push'
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Extract CHANGELOG section
|
|
24
|
+
run: |
|
|
25
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
26
|
+
awk -v ver="$VERSION" \
|
|
27
|
+
'$0 ~ "^## \\["ver"\\]" {p=1; next} /^## \[/ {p=0} p' \
|
|
28
|
+
CHANGELOG.md > release-notes.md
|
|
29
|
+
if ! grep -q '[^[:space:]]' release-notes.md; then
|
|
30
|
+
echo "::error::No CHANGELOG.md section found for ${VERSION} — add one before tagging." >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
- name: Build release title
|
|
35
|
+
run: |
|
|
36
|
+
# Title is "vX.Y.Z — <summary paragraph>" when the section opens with
|
|
37
|
+
# prose before the first ### heading; bare "vX.Y.Z" otherwise.
|
|
38
|
+
SUMMARY=$(awk '/^#/{exit} NF{printf "%s ", $0}' release-notes.md \
|
|
39
|
+
| sed 's/ $//' | cut -c1-120)
|
|
40
|
+
if [ -n "$SUMMARY" ]; then
|
|
41
|
+
echo "RELEASE_TITLE=${GITHUB_REF_NAME} — ${SUMMARY}" >> "$GITHUB_ENV"
|
|
42
|
+
else
|
|
43
|
+
echo "RELEASE_TITLE=${GITHUB_REF_NAME}" >> "$GITHUB_ENV"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- name: Create release
|
|
47
|
+
env:
|
|
48
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
run: |
|
|
50
|
+
gh release create "$GITHUB_REF_NAME" \
|
|
51
|
+
--title "$RELEASE_TITLE" \
|
|
52
|
+
--notes-file release-notes.md \
|
|
53
|
+
--verify-tag
|
|
54
|
+
|
|
55
|
+
pypi:
|
|
56
|
+
name: Publish to PyPI
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment: pypi
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write # OIDC for PyPI Trusted Publishing — no stored token
|
|
61
|
+
contents: read
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- uses: actions/setup-python@v5
|
|
66
|
+
with:
|
|
67
|
+
python-version: "3.12"
|
|
68
|
+
|
|
69
|
+
- name: Verify tag matches pyproject version
|
|
70
|
+
if: github.event_name == 'push'
|
|
71
|
+
run: |
|
|
72
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
73
|
+
PY_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
74
|
+
if [ "$TAG_VERSION" != "$PY_VERSION" ]; then
|
|
75
|
+
echo "::error::Tag ${GITHUB_REF_NAME} does not match pyproject.toml version ${PY_VERSION}" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
- name: Build sdist and wheel
|
|
80
|
+
run: |
|
|
81
|
+
python -m pip install --quiet build
|
|
82
|
+
python -m build
|
|
83
|
+
|
|
84
|
+
- name: Publish
|
|
85
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
share/python-wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
MANIFEST
|
|
24
|
+
|
|
25
|
+
# Virtual environments
|
|
26
|
+
.env
|
|
27
|
+
.venv
|
|
28
|
+
env/
|
|
29
|
+
venv/
|
|
30
|
+
ENV/
|
|
31
|
+
env.bak/
|
|
32
|
+
venv.bak/
|
|
33
|
+
|
|
34
|
+
# Testing
|
|
35
|
+
htmlcov/
|
|
36
|
+
.tox/
|
|
37
|
+
.nox/
|
|
38
|
+
.coverage
|
|
39
|
+
.coverage.*
|
|
40
|
+
.cache
|
|
41
|
+
nosetests.xml
|
|
42
|
+
coverage.xml
|
|
43
|
+
*.cover
|
|
44
|
+
*.py,cover
|
|
45
|
+
.hypothesis/
|
|
46
|
+
.pytest_cache/
|
|
47
|
+
cover/
|
|
48
|
+
|
|
49
|
+
# Tools
|
|
50
|
+
.mypy_cache/
|
|
51
|
+
.ruff_cache/
|
|
52
|
+
.pyre/
|
|
53
|
+
.pytype/
|
|
54
|
+
.claude/
|
|
55
|
+
|
|
56
|
+
# Memstem-specific
|
|
57
|
+
*.db
|
|
58
|
+
*.db-journal
|
|
59
|
+
index.db
|
|
60
|
+
embeddings/
|
|
61
|
+
.memstem/
|
|
62
|
+
test-vaults/
|
|
63
|
+
test-data/
|
|
64
|
+
|
|
65
|
+
# IDE
|
|
66
|
+
.vscode/
|
|
67
|
+
.idea/
|
|
68
|
+
*.swp
|
|
69
|
+
*.swo
|
|
70
|
+
*~
|
|
71
|
+
.DS_Store
|
|
72
|
+
|
|
73
|
+
# Logs
|
|
74
|
+
*.log
|
|
75
|
+
logs/
|
|
76
|
+
|
|
77
|
+
# Local secrets
|
|
78
|
+
.env.local
|
|
79
|
+
.env.*.local
|
|
80
|
+
|
|
81
|
+
# Local working docs (session handoffs — never published)
|
|
82
|
+
HANDOFF.md
|
|
83
|
+
|
|
84
|
+
# Internal planning docs (kept locally, not published)
|
|
85
|
+
PLAN.md
|
|
86
|
+
RECALL-PLAN.md
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
args: ['--maxkb=500']
|
|
10
|
+
- id: check-merge-conflict
|
|
11
|
+
- id: detect-private-key
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.15.12
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--fix]
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
21
|
+
rev: v1.20.2
|
|
22
|
+
hooks:
|
|
23
|
+
- id: mypy
|
|
24
|
+
additional_dependencies:
|
|
25
|
+
- pydantic>=2.0.0
|
|
26
|
+
- pytest>=8.0.0
|
|
27
|
+
- watchdog>=4.0.0
|
|
28
|
+
- sqlite-vec>=0.1.0
|
|
29
|
+
- python-frontmatter>=1.0.0
|
|
30
|
+
- httpx>=0.27.0
|
|
31
|
+
- typer>=0.12.0
|
|
32
|
+
- mcp>=1.0.0
|
|
33
|
+
- pyyaml>=6.0
|
|
34
|
+
- aiofiles>=23.0.0
|
|
35
|
+
- fastapi>=0.110.0
|
|
36
|
+
- uvicorn>=0.30.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Goals
|
|
4
|
+
|
|
5
|
+
1. **Single canonical store** for memories and skills shared across multiple AI clients
|
|
6
|
+
2. **Immunity to upgrade churn** in any individual AI client
|
|
7
|
+
3. **Sub-second ingestion** of new content from any source
|
|
8
|
+
4. **Hybrid search** — keyword + semantic — with no remote API dependencies (optional)
|
|
9
|
+
5. **Human-readable** canonical layer (markdown + frontmatter)
|
|
10
|
+
6. **MCP-native** integration
|
|
11
|
+
|
|
12
|
+
## Layered design
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌──────────────────────────────────────────────────────────┐
|
|
16
|
+
│ AI Clients │
|
|
17
|
+
│ Claude Code │ OpenClaw │ Codex │ Cursor │ ... │
|
|
18
|
+
└──────────────────────────────────────────────────────────┘
|
|
19
|
+
▲ MCP / HTTP
|
|
20
|
+
│
|
|
21
|
+
┌──────────────────────────────────────────────────────────┐
|
|
22
|
+
│ Memstem Daemon │
|
|
23
|
+
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
|
|
24
|
+
│ │ Servers │ │ Hygiene │ │ Adapters │ │
|
|
25
|
+
│ │ MCP / HTTP │ │ Worker (bg) │ │ Watchers (bg) │ │
|
|
26
|
+
│ └─────────────┘ └──────────────┘ └────────────────┘ │
|
|
27
|
+
│ ▲ ▲ ▲ │
|
|
28
|
+
│ └────────────────┼──────────────────┘ │
|
|
29
|
+
│ │ │
|
|
30
|
+
│ ┌─────────────────────────────────────────────────┐ │
|
|
31
|
+
│ │ Index (SQLite + FTS5 + vec) │ │
|
|
32
|
+
│ └─────────────────────────────────────────────────┘ │
|
|
33
|
+
│ ▲ │
|
|
34
|
+
│ ┌─────────────────────────────────────────────────┐ │
|
|
35
|
+
│ │ Canonical Storage (markdown vault) │ │
|
|
36
|
+
│ │ memories/ skills/ sessions/ daily/ │ │
|
|
37
|
+
│ └─────────────────────────────────────────────────┘ │
|
|
38
|
+
└──────────────────────────────────────────────────────────┘
|
|
39
|
+
▲
|
|
40
|
+
│ inotify watches
|
|
41
|
+
┌──────────────────────────────────────────────────────────┐
|
|
42
|
+
│ External AI Filesystems │
|
|
43
|
+
│ ~/.claude/projects/ ~/<agent>/memory/ ~/.codex/sessions/ │
|
|
44
|
+
└──────────────────────────────────────────────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Two-layer storage
|
|
48
|
+
|
|
49
|
+
### Canonical: markdown files in a structured tree
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
~/memstem-vault/
|
|
53
|
+
├── memories/
|
|
54
|
+
│ ├── people/
|
|
55
|
+
│ │ └── brad-besner.md
|
|
56
|
+
│ ├── decisions/
|
|
57
|
+
│ │ └── 2026-04-25-deploy-via-cloudflare.md
|
|
58
|
+
│ └── facts/
|
|
59
|
+
│ └── port-assignments.md
|
|
60
|
+
├── skills/
|
|
61
|
+
│ ├── deploy-to-kinsta.md
|
|
62
|
+
│ └── send-telegram.md
|
|
63
|
+
├── sessions/
|
|
64
|
+
│ └── 2026-04-25/
|
|
65
|
+
│ ├── claude-code-abc123.md
|
|
66
|
+
│ └── openclaw-def456.md
|
|
67
|
+
├── daily/
|
|
68
|
+
│ └── 2026-04-25.md
|
|
69
|
+
└── _meta/
|
|
70
|
+
├── taxonomy.md
|
|
71
|
+
└── config.yaml
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Every file has YAML frontmatter (see [frontmatter-spec.md](./docs/frontmatter-spec.md)). Files survive any version of any tool, are diffable, git-friendly, and openable in any markdown editor.
|
|
75
|
+
|
|
76
|
+
### Index: SQLite with FTS5 + sqlite-vec
|
|
77
|
+
|
|
78
|
+
A single `index.db` file rebuilt from the canonical store at any time. FTS5 provides BM25 keyword retrieval; sqlite-vec provides cosine similarity over embeddings. Hybrid queries use Reciprocal Rank Fusion (RRF) to merge.
|
|
79
|
+
|
|
80
|
+
If the index is ever corrupted, lost, or becomes incompatible: `memstem reindex` rebuilds it from the canonical files. The truth is never at risk.
|
|
81
|
+
|
|
82
|
+
## Pull-based ingestion
|
|
83
|
+
|
|
84
|
+
Each adapter is a small module that knows how to read one external AI's filesystem.
|
|
85
|
+
|
|
86
|
+
- **Claude Code adapter**: watches `~/.claude/projects/*/sessions/*.jsonl` with `inotify`, extracts user/assistant turns, dedupes, writes a clean memory record into `memories/sessions/`.
|
|
87
|
+
- **OpenClaw adapter**: watches each configured agent workspace’s `memory/`, `skills/`, and daily logs. Mirrors directly into the canonical vault.
|
|
88
|
+
- **Codex adapter** (planned): watches `~/.codex/sessions/`.
|
|
89
|
+
- **Cursor adapter** (planned): watches Cursor's local memory store.
|
|
90
|
+
- **Generic file adapter** (planned): watches an arbitrary directory provided by the user.
|
|
91
|
+
|
|
92
|
+
Adapters emit normalized memory records. Storage and indexing are downstream — adapters never touch the index directly.
|
|
93
|
+
|
|
94
|
+
A 5-minute reconciliation pass runs alongside `inotify` to catch anything missed (crashes, race conditions, files moved out-of-band).
|
|
95
|
+
|
|
96
|
+
## Hybrid search
|
|
97
|
+
|
|
98
|
+
Every query goes through three stages:
|
|
99
|
+
|
|
100
|
+
1. **Embed the query** using the configured embedding backend. Memstem ships four pluggable implementations: `OllamaEmbedder` (default, local, `nomic-embed-text` 768d), `OpenAIEmbedder` (with `base_url` knob for OpenAI-compatible providers), `GeminiEmbedder` (`gemini-embedding-2-preview` default with Matryoshka support, so 768d Ollama indexes can switch over without reindexing), and `VoyageEmbedder` (Anthropic's recommended partner). Backend and dimensions are configured in `_meta/config.yaml`; see ADR 0009.
|
|
101
|
+
2. **Run two retrievals in parallel**:
|
|
102
|
+
- FTS5 BM25 over the markdown body + frontmatter tags
|
|
103
|
+
- sqlite-vec cosine similarity over chunk embeddings
|
|
104
|
+
3. **Merge with RRF** (k=60 default): combined ranking is the normalized inverse-rank sum from both retrievers.
|
|
105
|
+
|
|
106
|
+
Optional third signal (planned): entity-link retrieval over wikilinks (`[[Entity]]`) extracted at ingest time.
|
|
107
|
+
|
|
108
|
+
Optional fourth signal (planned): recency + importance score from the hygiene worker.
|
|
109
|
+
|
|
110
|
+
## Hygiene worker
|
|
111
|
+
|
|
112
|
+
Runs in a background thread, processing the canonical store at a low priority:
|
|
113
|
+
|
|
114
|
+
- **Dedup**: pairs with cosine similarity > 0.95 are merged; the higher-importance record wins, the duplicate becomes a redirect.
|
|
115
|
+
- **Decay**: importance score decays over time; bursts of recall raise it.
|
|
116
|
+
- **Bi-temporal validity** (planned): when a fact contradicts an existing one, the old one gets `valid_to: <date>` rather than being deleted.
|
|
117
|
+
|
|
118
|
+
The hygiene package also hosts two CLI-driven derived-record writers
|
|
119
|
+
(see ADR 0020 and ADR 0021) that turn raw transcripts and project
|
|
120
|
+
session sets into retrieval-shaped records:
|
|
121
|
+
|
|
122
|
+
- **Session distillation** (`memstem hygiene distill-sessions`): one
|
|
123
|
+
`type: distillation` per meaningful session, link-back via
|
|
124
|
+
frontmatter. Importance seeded above raw sessions so the existing
|
|
125
|
+
multiplier surfaces them on close ties.
|
|
126
|
+
- **Project records** (`memstem hygiene project-records`): one
|
|
127
|
+
`type: project` per Claude Code project tag with ≥2 sessions,
|
|
128
|
+
preferring linked distillations as input. Hand-edited records
|
|
129
|
+
carrying `manual: true` are protected from regeneration.
|
|
130
|
+
|
|
131
|
+
Both run via the same pluggable `Summarizer` abstraction
|
|
132
|
+
(`core/summarizer.py`) — NoOp default, OpenAI / Ollama opt-in. Their
|
|
133
|
+
outputs are **derivative artifacts with mandatory provenance**, which
|
|
134
|
+
is the boundary that keeps them inside the read-files-from-disk
|
|
135
|
+
architecture.
|
|
136
|
+
|
|
137
|
+
> Note: MemStem does **not** author skills. Each AI generates skills its
|
|
138
|
+
> own way (Claude Code, Codex, Hermes, OpenClaw all have their own
|
|
139
|
+
> conventions); MemStem reads `SKILL.md` files from disk and indexes
|
|
140
|
+
> them. Distillations and project records are explicitly different —
|
|
141
|
+
> they summarize source records with mandatory link-back rather than
|
|
142
|
+
> producing standalone knowledge claims. See
|
|
143
|
+
> [ADR 0019](decisions/0019-no-skill-authoring.md) for the boundary
|
|
144
|
+
> rule and [ADR 0020](decisions/0020-session-distillation-writer.md) /
|
|
145
|
+
> [ADR 0021](decisions/0021-project-records.md) for the
|
|
146
|
+
> distillation / project-record designs.
|
|
147
|
+
|
|
148
|
+
## API surface
|
|
149
|
+
|
|
150
|
+
**MCP tools** (primary):
|
|
151
|
+
|
|
152
|
+
- `memstem_search(query, limit=10, types=[...]) -> [Result]`
|
|
153
|
+
- `memstem_get(id_or_path) -> Memory`
|
|
154
|
+
- `memstem_list_skills(scope=...) -> [Skill]`
|
|
155
|
+
- `memstem_get_skill(name) -> Skill`
|
|
156
|
+
- `memstem_upsert(content, frontmatter) -> Memory` (write path)
|
|
157
|
+
|
|
158
|
+
**HTTP API** (secondary): same shape under `/api/v1/`.
|
|
159
|
+
|
|
160
|
+
**Anthropic memory-tool adapter** (planned flagship feature): implements `BetaAbstractMemoryTool` so Claude Code's official memory tool routes natively into Memstem.
|
|
161
|
+
|
|
162
|
+
See [docs/mcp-api.md](./docs/mcp-api.md) for full tool definitions.
|
|
163
|
+
|
|
164
|
+
## Configuration
|
|
165
|
+
|
|
166
|
+
A single `~/memstem-vault/_meta/config.yaml` file controls the daemon. See [docs/configuration.md](./docs/configuration.md) for the full schema (forthcoming).
|
|
167
|
+
|
|
168
|
+
## What Memstem is not
|
|
169
|
+
|
|
170
|
+
- Not a chat memory layer (that's mem0's space)
|
|
171
|
+
- Not a graph database (Graphiti, Letta)
|
|
172
|
+
- Not a managed cloud service (yet — see [ROADMAP.md](./ROADMAP.md))
|
|
173
|
+
- Not a wiki engine or note-taking app
|
|
174
|
+
|
|
175
|
+
Memstem is **infrastructure for AI agents to share knowledge without coupling to each other**.
|