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.
Files changed (183) hide show
  1. memstem-0.17.0/.github/ISSUE_TEMPLATE/adapter_request.md +31 -0
  2. memstem-0.17.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  3. memstem-0.17.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. memstem-0.17.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  5. memstem-0.17.0/.github/dependabot.yml +23 -0
  6. memstem-0.17.0/.github/pull_request_template.md +27 -0
  7. memstem-0.17.0/.github/workflows/ci.yml +72 -0
  8. memstem-0.17.0/.github/workflows/release.yml +85 -0
  9. memstem-0.17.0/.gitignore +86 -0
  10. memstem-0.17.0/.pre-commit-config.yaml +36 -0
  11. memstem-0.17.0/.python-version +1 -0
  12. memstem-0.17.0/ARCHITECTURE.md +175 -0
  13. memstem-0.17.0/CHANGELOG.md +2220 -0
  14. memstem-0.17.0/CLAUDE.md +70 -0
  15. memstem-0.17.0/CODE_OF_CONDUCT.md +84 -0
  16. memstem-0.17.0/CONTRIBUTING.md +57 -0
  17. memstem-0.17.0/LICENSE +21 -0
  18. memstem-0.17.0/PKG-INFO +505 -0
  19. memstem-0.17.0/README.md +464 -0
  20. memstem-0.17.0/ROADMAP.md +82 -0
  21. memstem-0.17.0/SECURITY.md +29 -0
  22. memstem-0.17.0/clients/codex/AGENTS.md.example +62 -0
  23. memstem-0.17.0/clients/codex/README.md +144 -0
  24. memstem-0.17.0/clients/codex/config.toml.fragment +10 -0
  25. memstem-0.17.0/clients/skills/memstem-search/SKILL.md +114 -0
  26. memstem-0.17.0/docs/decisions/0001-greenfield-not-fork.md +31 -0
  27. memstem-0.17.0/docs/decisions/0002-markdown-canonical.md +31 -0
  28. memstem-0.17.0/docs/decisions/0003-sqlite-with-fts5-and-vec.md +34 -0
  29. memstem-0.17.0/docs/decisions/0004-mit-license.md +39 -0
  30. memstem-0.17.0/docs/decisions/0005-pull-based-ingestion.md +36 -0
  31. memstem-0.17.0/docs/decisions/0006-anthropic-memory-tool-adapter.md +32 -0
  32. memstem-0.17.0/docs/decisions/0007-remote-ingestion.md +114 -0
  33. memstem-0.17.0/docs/decisions/0008-tiered-memory.md +360 -0
  34. memstem-0.17.0/docs/decisions/0009-pluggable-embedders-and-queue.md +199 -0
  35. memstem-0.17.0/docs/decisions/0010-obsidian-plugin.md +20 -0
  36. memstem-0.17.0/docs/decisions/0011-noise-filter-and-fact-extraction.md +311 -0
  37. memstem-0.17.0/docs/decisions/0012-llm-judge-dedup.md +439 -0
  38. memstem-0.17.0/docs/decisions/0013-workspace-extra-files.md +90 -0
  39. memstem-0.17.0/docs/decisions/0014-cli-daemon-delegation-and-migration-discipline.md +253 -0
  40. memstem-0.17.0/docs/decisions/0015-eval-harness.md +190 -0
  41. memstem-0.17.0/docs/decisions/0016-mmr-diversification.md +191 -0
  42. memstem-0.17.0/docs/decisions/0017-cross-encoder-rerank.md +390 -0
  43. memstem-0.17.0/docs/decisions/0018-hyde-query-expansion.md +394 -0
  44. memstem-0.17.0/docs/decisions/0019-no-skill-authoring.md +201 -0
  45. memstem-0.17.0/docs/decisions/0020-session-distillation-writer.md +325 -0
  46. memstem-0.17.0/docs/decisions/0021-project-records.md +313 -0
  47. memstem-0.17.0/docs/decisions/0022-codex-adapter.md +203 -0
  48. memstem-0.17.0/docs/decisions/0023-in-daemon-hygiene-loop.md +140 -0
  49. memstem-0.17.0/docs/decisions/0024-incremental-startup-reconcile.md +100 -0
  50. memstem-0.17.0/docs/decisions/0025-multimodal-embeddings.md +123 -0
  51. memstem-0.17.0/docs/dedupe-audit.md +404 -0
  52. memstem-0.17.0/docs/distillation-verification.md +250 -0
  53. memstem-0.17.0/docs/frontmatter-spec.md +104 -0
  54. memstem-0.17.0/docs/images/hero.png +0 -0
  55. memstem-0.17.0/docs/images/hybrid-search.png +0 -0
  56. memstem-0.17.0/docs/install.md +101 -0
  57. memstem-0.17.0/docs/mcp-api.md +128 -0
  58. memstem-0.17.0/docs/operations.md +434 -0
  59. memstem-0.17.0/docs/recall-eval-results.md +161 -0
  60. memstem-0.17.0/docs/recall-models.md +322 -0
  61. memstem-0.17.0/docs/secrets.md +173 -0
  62. memstem-0.17.0/eval/queries.yaml +151 -0
  63. memstem-0.17.0/examples/example_memory.md +23 -0
  64. memstem-0.17.0/examples/example_skill.md +37 -0
  65. memstem-0.17.0/pyproject.toml +145 -0
  66. memstem-0.17.0/scripts/dedupe_audit_report.py +792 -0
  67. memstem-0.17.0/scripts/dedupe_phase1_apply.py +477 -0
  68. memstem-0.17.0/scripts/dedupe_phase1_select.py +428 -0
  69. memstem-0.17.0/scripts/e2e-smoke.sh +326 -0
  70. memstem-0.17.0/scripts/install.sh +495 -0
  71. memstem-0.17.0/scripts/migrate-from-flipclaw.py +18 -0
  72. memstem-0.17.0/scripts/run_eval.py +139 -0
  73. memstem-0.17.0/scripts/smoke_0_7_0.sh +234 -0
  74. memstem-0.17.0/src/memstem/__init__.py +3 -0
  75. memstem-0.17.0/src/memstem/__main__.py +6 -0
  76. memstem-0.17.0/src/memstem/adapters/__init__.py +1 -0
  77. memstem-0.17.0/src/memstem/adapters/base.py +64 -0
  78. memstem-0.17.0/src/memstem/adapters/claude_code.py +351 -0
  79. memstem-0.17.0/src/memstem/adapters/codex.py +508 -0
  80. memstem-0.17.0/src/memstem/adapters/openclaw.py +664 -0
  81. memstem-0.17.0/src/memstem/auth.py +124 -0
  82. memstem-0.17.0/src/memstem/cli.py +2723 -0
  83. memstem-0.17.0/src/memstem/client.py +280 -0
  84. memstem-0.17.0/src/memstem/config.py +532 -0
  85. memstem-0.17.0/src/memstem/core/__init__.py +1 -0
  86. memstem-0.17.0/src/memstem/core/dedup.py +117 -0
  87. memstem-0.17.0/src/memstem/core/embed_worker.py +426 -0
  88. memstem-0.17.0/src/memstem/core/embeddings.py +712 -0
  89. memstem-0.17.0/src/memstem/core/extraction.py +395 -0
  90. memstem-0.17.0/src/memstem/core/frontmatter.py +224 -0
  91. memstem-0.17.0/src/memstem/core/hyde.py +504 -0
  92. memstem-0.17.0/src/memstem/core/importance_seed.py +183 -0
  93. memstem-0.17.0/src/memstem/core/index.py +1351 -0
  94. memstem-0.17.0/src/memstem/core/media.py +102 -0
  95. memstem-0.17.0/src/memstem/core/mmr.py +167 -0
  96. memstem-0.17.0/src/memstem/core/pipeline.py +309 -0
  97. memstem-0.17.0/src/memstem/core/rerank.py +672 -0
  98. memstem-0.17.0/src/memstem/core/retrieval_log.py +193 -0
  99. memstem-0.17.0/src/memstem/core/search.py +602 -0
  100. memstem-0.17.0/src/memstem/core/storage.py +190 -0
  101. memstem-0.17.0/src/memstem/core/summarizer.py +480 -0
  102. memstem-0.17.0/src/memstem/discovery.py +227 -0
  103. memstem-0.17.0/src/memstem/eval/__init__.py +35 -0
  104. memstem-0.17.0/src/memstem/eval/harness.py +314 -0
  105. memstem-0.17.0/src/memstem/hygiene/__init__.py +1 -0
  106. memstem-0.17.0/src/memstem/hygiene/cleanup_retro.py +633 -0
  107. memstem-0.17.0/src/memstem/hygiene/dedup_candidates.py +334 -0
  108. memstem-0.17.0/src/memstem/hygiene/dedup_judge.py +555 -0
  109. memstem-0.17.0/src/memstem/hygiene/distillation.py +241 -0
  110. memstem-0.17.0/src/memstem/hygiene/importance.py +357 -0
  111. memstem-0.17.0/src/memstem/hygiene/loop.py +398 -0
  112. memstem-0.17.0/src/memstem/hygiene/project_records.py +702 -0
  113. memstem-0.17.0/src/memstem/hygiene/session_distill.py +808 -0
  114. memstem-0.17.0/src/memstem/hygiene/state.py +232 -0
  115. memstem-0.17.0/src/memstem/hygiene/verify.py +293 -0
  116. memstem-0.17.0/src/memstem/integration.py +783 -0
  117. memstem-0.17.0/src/memstem/migrate.py +247 -0
  118. memstem-0.17.0/src/memstem/progress.py +164 -0
  119. memstem-0.17.0/src/memstem/prompts/__init__.py +6 -0
  120. memstem-0.17.0/src/memstem/prompts/dedup_judge.txt +41 -0
  121. memstem-0.17.0/src/memstem/prompts/distill_project.txt +89 -0
  122. memstem-0.17.0/src/memstem/prompts/distill_session.txt +79 -0
  123. memstem-0.17.0/src/memstem/prompts/hyde.txt +27 -0
  124. memstem-0.17.0/src/memstem/prompts/rerank.txt +38 -0
  125. memstem-0.17.0/src/memstem/servers/__init__.py +1 -0
  126. memstem-0.17.0/src/memstem/servers/http_server.py +436 -0
  127. memstem-0.17.0/src/memstem/servers/mcp_server.py +539 -0
  128. memstem-0.17.0/src/memstem/servers/request_limits.py +47 -0
  129. memstem-0.17.0/src/memstem/star_nudge.py +65 -0
  130. memstem-0.17.0/tests/__init__.py +0 -0
  131. memstem-0.17.0/tests/adapters/__init__.py +0 -0
  132. memstem-0.17.0/tests/adapters/test_claude_code.py +363 -0
  133. memstem-0.17.0/tests/adapters/test_codex.py +473 -0
  134. memstem-0.17.0/tests/adapters/test_openclaw.py +880 -0
  135. memstem-0.17.0/tests/conftest.py +32 -0
  136. memstem-0.17.0/tests/servers/__init__.py +0 -0
  137. memstem-0.17.0/tests/servers/test_http_server.py +428 -0
  138. memstem-0.17.0/tests/servers/test_request_limits.py +38 -0
  139. memstem-0.17.0/tests/test_auth.py +189 -0
  140. memstem-0.17.0/tests/test_cli.py +1303 -0
  141. memstem-0.17.0/tests/test_client.py +515 -0
  142. memstem-0.17.0/tests/test_config_rerank.py +44 -0
  143. memstem-0.17.0/tests/test_config_validation.py +112 -0
  144. memstem-0.17.0/tests/test_daemon_reconcile.py +238 -0
  145. memstem-0.17.0/tests/test_dedup.py +239 -0
  146. memstem-0.17.0/tests/test_dedupe_audit_report.py +304 -0
  147. memstem-0.17.0/tests/test_dedupe_phase1_apply.py +494 -0
  148. memstem-0.17.0/tests/test_dedupe_phase1_select.py +351 -0
  149. memstem-0.17.0/tests/test_discovery.py +248 -0
  150. memstem-0.17.0/tests/test_embed_worker.py +924 -0
  151. memstem-0.17.0/tests/test_embeddings.py +819 -0
  152. memstem-0.17.0/tests/test_eval_harness.py +433 -0
  153. memstem-0.17.0/tests/test_extraction.py +576 -0
  154. memstem-0.17.0/tests/test_frontmatter.py +191 -0
  155. memstem-0.17.0/tests/test_hyde.py +360 -0
  156. memstem-0.17.0/tests/test_hygiene_cleanup_retro.py +378 -0
  157. memstem-0.17.0/tests/test_hygiene_dedup_candidates.py +543 -0
  158. memstem-0.17.0/tests/test_hygiene_dedup_judge.py +673 -0
  159. memstem-0.17.0/tests/test_hygiene_distillation.py +343 -0
  160. memstem-0.17.0/tests/test_hygiene_importance.py +405 -0
  161. memstem-0.17.0/tests/test_hygiene_loop.py +262 -0
  162. memstem-0.17.0/tests/test_hygiene_project_records.py +711 -0
  163. memstem-0.17.0/tests/test_hygiene_session_distill.py +692 -0
  164. memstem-0.17.0/tests/test_hygiene_state.py +243 -0
  165. memstem-0.17.0/tests/test_hygiene_verify.py +248 -0
  166. memstem-0.17.0/tests/test_importance_seed.py +222 -0
  167. memstem-0.17.0/tests/test_index.py +1287 -0
  168. memstem-0.17.0/tests/test_install_sh.py +142 -0
  169. memstem-0.17.0/tests/test_integration.py +835 -0
  170. memstem-0.17.0/tests/test_mcp_server.py +859 -0
  171. memstem-0.17.0/tests/test_media.py +82 -0
  172. memstem-0.17.0/tests/test_migrate.py +161 -0
  173. memstem-0.17.0/tests/test_mmr.py +236 -0
  174. memstem-0.17.0/tests/test_pipeline.py +469 -0
  175. memstem-0.17.0/tests/test_progress.py +382 -0
  176. memstem-0.17.0/tests/test_rerank.py +797 -0
  177. memstem-0.17.0/tests/test_retrieval_log.py +337 -0
  178. memstem-0.17.0/tests/test_search.py +1130 -0
  179. memstem-0.17.0/tests/test_smoke.py +35 -0
  180. memstem-0.17.0/tests/test_star_nudge.py +96 -0
  181. memstem-0.17.0/tests/test_storage.py +241 -0
  182. memstem-0.17.0/tests/test_summarizer.py +459 -0
  183. 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,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Discussions
4
+ url: https://github.com/memstem/memstem/discussions
5
+ about: Ask a question or discuss an idea
@@ -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**.