memos-engine 0.2.0__tar.gz → 0.3.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 (89) hide show
  1. memos_engine-0.3.0/.github/workflows/ci.yml +47 -0
  2. memos_engine-0.3.0/.github/workflows/release.yml +39 -0
  3. {memos_engine-0.2.0 → memos_engine-0.3.0}/.gitignore +1 -1
  4. {memos_engine-0.2.0 → memos_engine-0.3.0}/AGENTS.md +32 -5
  5. {memos_engine-0.2.0 → memos_engine-0.3.0}/AGENTS_EXAMPLE.md +2 -1
  6. memos_engine-0.3.0/CHANGELOG.md +32 -0
  7. {memos_engine-0.2.0 → memos_engine-0.3.0}/PKG-INFO +29 -13
  8. {memos_engine-0.2.0 → memos_engine-0.3.0}/README.md +27 -12
  9. memos_engine-0.3.0/memos/cli/doctor.py +175 -0
  10. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/cli/main.py +222 -14
  11. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/db.py +30 -13
  12. memos_engine-0.3.0/memos/core/migrations/0005_symbol_name_file_index.sql +3 -0
  13. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/schema.sql +1 -0
  14. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/typescript.py +19 -3
  15. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/mcp/server.py +52 -20
  16. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/query/core.py +1 -1
  17. {memos_engine-0.2.0 → memos_engine-0.3.0}/pyproject.toml +10 -1
  18. memos_engine-0.3.0/scripts/benchmark_index.py +108 -0
  19. memos_engine-0.3.0/tests/fixtures/javascript_mini/src/index.js +5 -0
  20. memos_engine-0.3.0/tests/fixtures/javascript_mini/src/utils.js +9 -0
  21. memos_engine-0.3.0/tests/test_cli_doctor.py +134 -0
  22. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_cli_index.py +72 -0
  23. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_cli_query.py +37 -0
  24. memos_engine-0.3.0/tests/test_javascript_indexer.py +124 -0
  25. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_mcp.py +12 -0
  26. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_migrations.py +2 -2
  27. memos_engine-0.3.0/tests/test_packaging.py +6 -0
  28. memos_engine-0.3.0/tests/test_query_efficiency.py +91 -0
  29. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_schema.py +1 -1
  30. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_typescript_indexer.py +8 -0
  31. memos_engine-0.3.0/tests/test_watch.py +103 -0
  32. {memos_engine-0.2.0 → memos_engine-0.3.0}/uv.lock +113 -2
  33. memos_engine-0.2.0/.github/workflows/ci.yml +0 -18
  34. memos_engine-0.2.0/.github/workflows/release.yml +0 -25
  35. memos_engine-0.2.0/CHANGELOG.md +0 -19
  36. {memos_engine-0.2.0 → memos_engine-0.3.0}/.github/release.yml +0 -0
  37. {memos_engine-0.2.0 → memos_engine-0.3.0}/.python-version +0 -0
  38. {memos_engine-0.2.0 → memos_engine-0.3.0}/LICENSE +0 -0
  39. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/__init__.py +0 -0
  40. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/api/__init__.py +0 -0
  41. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/api/main.py +0 -0
  42. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/api/schemas.py +0 -0
  43. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/cli/__init__.py +0 -0
  44. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/__init__.py +0 -0
  45. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0001_init.sql +0 -0
  46. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0002_vec.sql +0 -0
  47. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0003_prompt_version.sql +0 -0
  48. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0004_memory_fts.sql +0 -0
  49. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/__init__.py +0 -0
  50. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/models.py +0 -0
  51. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/__init__.py +0 -0
  52. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/base.py +0 -0
  53. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/diff.py +0 -0
  54. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/go.py +0 -0
  55. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/python.py +0 -0
  56. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/mcp/__init__.py +0 -0
  57. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/query/__init__.py +0 -0
  58. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/__init__.py +0 -0
  59. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/base.py +0 -0
  60. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/embeddings.py +0 -0
  61. {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/sqlite_vec_store.py +0 -0
  62. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/__init__.py +0 -0
  63. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/conftest.py +0 -0
  64. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/__init__.py +0 -0
  65. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/__init__.py +0 -0
  66. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/src/main.go +0 -0
  67. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/src/types.go +0 -0
  68. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/src/utils.go +0 -0
  69. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/import_cycle/src/a.ts +0 -0
  70. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/import_cycle/src/b.ts +0 -0
  71. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/python_mini/src/main.py +0 -0
  72. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/python_mini/src/types.py +0 -0
  73. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/python_mini/src/utils.py +0 -0
  74. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/typescript_mini/src/index.ts +0 -0
  75. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/typescript_mini/src/types.ts +0 -0
  76. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/typescript_mini/src/utils.ts +0 -0
  77. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_api.py +0 -0
  78. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_crud.py +0 -0
  79. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_dependency_graph.py +0 -0
  80. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_go_indexer.py +0 -0
  81. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_hygiene.py +0 -0
  82. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_impact.py +0 -0
  83. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_llm_summary.py +0 -0
  84. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_memory_hygiene.py +0 -0
  85. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_python_indexer.py +0 -0
  86. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_query.py +0 -0
  87. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_reindex.py +0 -0
  88. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_resolver.py +0 -0
  89. {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_semantic_search.py +0 -0
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ ci:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python-version: ["3.12", "3.13"]
11
+ # TODO: verify tree-sitter/fastembed/sqlite-vec have wheels for 3.13
12
+ # If not, document limitation in README and keep only 3.12
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: ${{ matrix.python-version }}
18
+ - run: uv sync
19
+ - run: uv run ruff check .
20
+ - run: uv run pytest --cov=memos --cov-report=xml --cov-fail-under=70 -m "not slow"
21
+ - name: Upload coverage reports to Codecov
22
+ uses: codecov/codecov-action@v5
23
+ with:
24
+ token: ${{ secrets.CODECOV_TOKEN }}
25
+ files: ./coverage.xml
26
+ fail_ci_if_error: false
27
+
28
+ packaging-smoke-test:
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: astral-sh/setup-uv@v5
33
+ with:
34
+ python-version: "3.12"
35
+ - run: uv build
36
+ - name: install from built wheel in isolated venv
37
+ run: |
38
+ python -m venv /tmp/smoke-venv
39
+ /tmp/smoke-venv/bin/pip install dist/*.whl
40
+ /tmp/smoke-venv/bin/memos --help
41
+ - name: verify entry point works end-to-end
42
+ run: |
43
+ mkdir -p /tmp/smoke-project/src
44
+ echo 'def foo(): return 1' > /tmp/smoke-project/src/a.py
45
+ cd /tmp/smoke-project
46
+ /tmp/smoke-venv/bin/memos index --path .
47
+ /tmp/smoke-venv/bin/memos query symbol foo --path . | grep -q '"name": "foo"'
@@ -0,0 +1,39 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ release:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v5
13
+ with:
14
+ python-version: "3.12"
15
+ - name: verify tag matches pyproject version
16
+ run: |
17
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
18
+ PYPROJECT_VERSION=$(grep -Po '(?<=^version = ")[^"]+' pyproject.toml)
19
+ if [ "$TAG_VERSION" != "$PYPROJECT_VERSION" ]; then
20
+ echo "Tag $TAG_VERSION does not match pyproject.toml version $PYPROJECT_VERSION"
21
+ exit 1
22
+ fi
23
+ - run: uv sync
24
+ - run: uv run ruff check .
25
+ - run: uv run pytest
26
+ - run: uv build
27
+ - run: uv publish
28
+ env:
29
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
30
+ - name: extract changelog for this version
31
+ run: |
32
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
33
+ awk -v ver="$TAG_VERSION" \
34
+ '/^## / { if (found) exit; if (index($0, "## " ver) == 1) found=1; next } \
35
+ found { print }' CHANGELOG.md > /tmp/release_body.md
36
+ - uses: softprops/action-gh-release@v2
37
+ with:
38
+ files: dist/*
39
+ body_path: /tmp/release_body.md
@@ -6,7 +6,7 @@ __pycache__/
6
6
  # memos local index database
7
7
  .memos/
8
8
 
9
- MEMORY_OS_ARCHITECTURE.md
9
+ TZ.md
10
10
 
11
11
  # C extensions
12
12
  *.so
@@ -97,7 +97,7 @@ call edges, imports) с episodic memory. Инструмент доступен
97
97
  | `memory_prune_tool(older_than_days?, kind?, apply?)` | Удаление устаревших записей (dry-run по умолчанию) |
98
98
  | `reindex_file_tool(path)` | Переиндексировать один файл после правки |
99
99
 
100
- Индексируются файлы с расширениями `.ts`, `.tsx`, `.go`. Индекс лежит в
100
+ Индексируются файлы с расширениями `.ts`, `.tsx`, `.go`, `.py`, `.js`, `.jsx`. Индекс лежит в
101
101
  `.memos/memory.db` в корне проекта и обновляется через `memos index --path .`
102
102
  (инкрементально, по content hash — быстро на повторных запусках). Для
103
103
  переиндексации одного файла после правки используй `reindex_file_tool(path)`
@@ -117,6 +117,8 @@ a short descriptive title and all details in the commit body.
117
117
 
118
118
  | Command | What |
119
119
  |---------|------|
120
+ | `memos --version` | show version |
121
+ | `memos tools` | list available MCP tools |
120
122
  | `uv run pytest` | all tests |
121
123
  | `uv run pytest -v` | verbose |
122
124
  | `uv run pytest tests/test_crud.py::test_symbol_crud` | single test |
@@ -126,13 +128,21 @@ a short descriptive title and all details in the commit body.
126
128
  | `uv run memos query module <path>` | show everything for a file |
127
129
  | `uv run memos serve --path . --port 8000` | start FastAPI server |
128
130
  | `uv run memos serve-mcp --path .` | start MCP server (stdio) |
131
+ | `uv run memos doctor --path .` | Run project diagnostics |
132
+ | `uv run memos watch --path .` | Watch files and auto-reindex |
129
133
  | `uv run pytest tests/test_mcp.py` | MCP server tests |
130
134
  | `uv run pytest tests/test_llm_summary.py` | LLM summary tests |
131
135
  | `uv run pytest tests/test_impact.py` | Impact analysis tests |
132
136
  | `uv run pytest tests/test_hygiene.py` | Dead code / hygiene tests |
133
137
  | `uv run pytest tests/test_dependency_graph.py` | Dependency graph tests |
134
138
  | `uv run pytest tests/test_memory_hygiene.py` | Memory search & prune tests |
139
+ | `uv run pytest tests/test_javascript_indexer.py` | JavaScript indexer tests |
140
+ | `uv run pytest tests/test_query_efficiency.py` | Query N+1 regression guard |
141
+ | `uv run pytest tests/test_packaging.py` | Packaging smoke tests |
135
142
  | `uv run pytest tests/test_reindex.py` | Reindex tool tests |
143
+ | `uv run pytest tests/test_cli_doctor.py` | Doctor CLI tests |
144
+ | `uv run pytest tests/test_watch.py` | Watch (slow) tests |
145
+ | `uv run pytest --cov=memos --cov-report=term-missing -m "not slow"` | Tests with coverage report |
136
146
  | `curl http://localhost:8000/symbols/{id}/context` | API: get context for symbol |
137
147
  | `uv add <pkg>` | add dependency |
138
148
  | `uv add --dev <pkg>` | add dev dependency |
@@ -150,13 +160,16 @@ memos/
150
160
  0001_init.sql # authoritative DDL
151
161
  0002_vec.sql
152
162
  0003_prompt_version.sql
153
- 0004_memory_fts.sql
163
+ 0004_memory_fts.sql
164
+ 0005_symbol_name_file_index.sql
154
165
  indexer/
155
166
  base.py # LanguageIndexer ABC + ParsedSymbol/Call/Import/Result dataclasses
156
- typescript.py # TypeScriptIndexer (tree-sitter, handles .ts + .tsx)
167
+ typescript.py # TypeScriptIndexer (tree-sitter, handles .ts + .tsx + .js + .jsx via language_override)
157
168
  go.py # GoIndexer (tree-sitter, export by name case)
158
169
  python.py # PythonIndexer (tree-sitter, export by _ prefix) # NEW
159
170
  diff.py # compute_file_hash, should_reindex
171
+ scripts/
172
+ benchmark_index.py # performance benchmark harness
160
173
  query/
161
174
  core.py # find_symbol, find_calls, get_module, find_calls_by_id, semantic_search,
162
175
  # list_files, list_symbols, get_or_generate_summary, get_context,
@@ -173,15 +186,23 @@ memos/
173
186
  embeddings.py # FastEmbedEmbedding (all-MiniLM-L6-v2, 384-dim)
174
187
  sqlite_vec_store.py # SqliteVecStore (sqlite-vec vec0 table)
175
188
  cli/
176
- main.py # argparse: "memos index [--path .] [--full] [--no-embed]"
189
+ doctor.py # run_diagnostics(), DiagnosticResult pure functions for `memos doctor`
190
+ main.py # argparse: "memos [--version]"
191
+ # "memos index [--path .] [--full] [--no-embed] [--profile]"
177
192
  # "memos query (symbol|calls|module)"
178
193
  # "memos serve [--path] [--port]"
179
194
  # "memos serve-mcp [--path]"
195
+ # "memos tools"
196
+ # "memos doctor"
197
+ # "memos watch"
180
198
  tests/
181
199
  conftest.py # fixture: in-memory sqlite with migrations applied
182
200
  test_schema.py # table existence checks
183
201
  test_crud.py # CRUD + cascade delete
184
- test_migrations.py# idempotent re-run
202
+ test_migrations.py# idempotent re-run
203
+ test_javascript_indexer.py # 11 unit tests on JS parsing
204
+ test_query_efficiency.py # 1 query N+1 regression guard
205
+ test_packaging.py # 1 packaging smoke test
185
206
  test_typescript_indexer.py # 18 unit tests on TS parsing
186
207
  test_go_indexer.py # 18 unit tests on Go parsing
187
208
  test_python_indexer.py # 18 unit tests on Python parsing
@@ -204,6 +225,7 @@ tests/
204
225
  go_mini/src/ # 3 .go files for integration testing
205
226
  python_mini/src/ # 3 .py files for integration testing # NEW
206
227
  import_cycle/src/ # 2 .ts files with circular import
228
+ javascript_mini/src/ # 2 .js files for integration testing
207
229
  ```
208
230
 
209
231
  ## Dependencies
@@ -216,9 +238,11 @@ tests/
216
238
  - **fastembed** — ONNX embeddings (all-MiniLM-L6-v2, 384-dim)
217
239
  - **sqlite-vec** — vector search extension for sqlite
218
240
  - **rich** — CLI progress bars
241
+ - **watchdog** — file system watcher for `memos watch`
219
242
  - **pytest** (dev)
220
243
  - **httpx** (dev, for TestClient)
221
244
  - **pytest-anyio** (dev, for async MCP tests)
245
+ - **ruff** (dev)
222
246
  - **hatchling** (build)
223
247
 
224
248
  ## Architecture conventions
@@ -261,3 +285,6 @@ tests/
261
285
  14. ✅ Section 3: Dependency graph (graph + cycle detection, MCP, API, tests)
262
286
  15. ✅ Section 4: Memory search & hygiene (FTS5 search, prune, MCP, API, tests)
263
287
  16. ✅ Section 5: Reindex file tool (per-file reindex via MCP, tests)
288
+ 17. ✅ Section 0 (Phase 3): Packaging hardening — CI smoke-test, migration test, Python 3.13 matrix, README fix
289
+ 18. ✅ Section 1 (Phase 3): JavaScript .js/.jsx support — require()→import, language_override, fixtures, tests
290
+ 19. ✅ Section 3 (Phase 3): Performance — batch embedding, profile flag, benchmark script, N+1 fix, migration 0005
@@ -101,8 +101,9 @@ call edges, imports) с episodic memory. Инструмент доступен
101
101
  | `memory_search_tool(query, top_k?)` | Полнотекстовый поиск по memory entries |
102
102
  | `memory_prune_tool(older_than_days?, kind?, apply?)` | Удаление устаревших записей (dry-run по умолчанию) |
103
103
  | `reindex_file_tool(path)` | Переиндексировать один файл после правки |
104
+ | `usage_stats_tool()` | Показать счётчики вызовов тулов за сессию |
104
105
 
105
- Индексируются файлы с расширениями `.ts`, `.tsx`, `.go`. Индекс лежит в
106
+ Индексируются файлы с расширениями `.ts`, `.tsx`, `.go`, `.py`, `.js`, `.jsx`. Индекс лежит в
106
107
  `.memos/memory.db` в корне проекта и обновляется через `memos index --path .`
107
108
  (инкрементально, по content hash — быстро на повторных запусках). Для
108
109
  переиндексации одного файла после правки используй `reindex_file_tool(path)`
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 — 2026-07-22
4
+
5
+ - JavaScript (.js/.jsx) support: `require()` → import (not call), `module.exports` ignored
6
+ - Packaging hardening: CI smoke-test, migration presence test, Python 3.12+3.13 matrix
7
+ - Performance: batch embedding (single model, chunked by 256, rich Progress bar)
8
+ - Benchmarks: `--profile` flag, `scripts/benchmark_index.py`, query-efficiency regression test
9
+ - Migration 0005: composite index `symbols(name, file_id)` for faster resolve
10
+ - Fixed N+1 in `resolve_call_edges` (batch SELECT vs per-edge loop)
11
+ - Adoption tooling: `memos doctor`, `memos watch`, `usage_stats_tool`
12
+ - Diagnostics: `memos doctor` checks index freshness, schema, embeddings, unresolved edges
13
+ - MCP: `_tracked` decorator counts per-session tool usage via `usage_stats_tool`
14
+ - File watching: `memos watch` auto-reindexes on file changes (watchdog, 500ms debounce)
15
+
16
+ ## 0.2.0 — 2026-07-21
17
+
18
+ - Python language support (indexer, tests, fixtures)
19
+ - Self-indexed: memos-engine uses memos for its own development
20
+ - PyPI package `memos-engine` with MIT license
21
+ - CI: GitHub Actions release workflow (tag → build → publish)
22
+ - GitHub Release automation with changelog categorization
23
+
24
+ ## 0.1.0 — 2026-07-21
25
+
26
+ - Initial release
27
+ - TypeScript/Go structural index via tree-sitter
28
+ - MCP server with 20 tools
29
+ - FastAPI with 15 endpoints
30
+ - Semantic search (fastembed + sqlite-vec)
31
+ - Episodic memory (FTS5)
32
+ - Impact analysis, dead code, dependency graph, memory hygiene
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memos-engine
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Memory OS for AI coding agents — structural code index with episodic memory
5
5
  Project-URL: Repository, https://github.com/TAskMAster339/memos-engine
6
6
  Author: TAskMAster339
@@ -22,6 +22,7 @@ Requires-Dist: tree-sitter-python>=0.23.0
22
22
  Requires-Dist: tree-sitter-typescript>=0.23.2
23
23
  Requires-Dist: tree-sitter>=0.26.0
24
24
  Requires-Dist: uvicorn[standard]>=0.34.0
25
+ Requires-Dist: watchdog>=4.0.0
25
26
  Description-Content-Type: text/markdown
26
27
 
27
28
  # memos — Structural code index for AI agents
@@ -34,14 +35,17 @@ Description-Content-Type: text/markdown
34
35
  ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=fff)
35
36
  ![TSX](https://img.shields.io/badge/TSX-3178C6?logo=react&logoColor=fff)
36
37
  ![Go](https://img.shields.io/badge/Go-00ADD8?logo=go&logoColor=fff)
38
+ ![JavaScript](https://img.shields.io/badge/JavaScript-F7DF1E?logo=javascript&logoColor=000)
39
+
40
+ [![codecov](https://codecov.io/gh/TAskMAster339/memos-engine/branch/main/graph/badge.svg)](https://codecov.io/gh/TAskMAster339/memos-engine)
37
41
 
38
42
  </div>
39
43
 
40
44
  `memos` builds a **structural index** (symbols, call edges, imports) of a
41
- TypeScript / TSX / Go / Python codebase using tree-sitter and stores it in
42
- SQLite. It is the first layer of a larger *Memory OS* for AI coding agents
43
- — instead of grepping text, agents query **structure** (definitions,
44
- callers, callees).
45
+ TypeScript / TSX / Go / Python / JavaScript codebase using tree-sitter and
46
+ stores it in SQLite. It is the first layer of a larger *Memory OS* for AI
47
+ coding agents — instead of grepping text, agents query **structure**
48
+ (definitions, callers, callees).
45
49
 
46
50
  ## Integrating memos into your project
47
51
 
@@ -64,13 +68,6 @@ cd memos-engine
64
68
  uv tool install -e .
65
69
  ```
66
70
 
67
- Or install from PyPI (no clone needed):
68
-
69
- ```bash
70
- pip install memos-engine
71
- # or via uv:
72
- uv tool install memos-engine
73
- ```
74
71
 
75
72
  **2. Index your project**
76
73
 
@@ -107,11 +104,23 @@ git clone https://github.com/TAskMAster339/memos-engine.git
107
104
  cd memos-engine
108
105
  uv tool install -e .
109
106
 
107
+ # Check version
108
+ memos --version
109
+
110
110
  # Index a project
111
111
  memos index --path /path/to/your/project
112
112
 
113
+ # List all available MCP tools
114
+ memos tools
115
+
113
116
  # Start the MCP server for AI agents
114
117
  memos serve-mcp
118
+
119
+ # Run project diagnostics
120
+ memos doctor --path /path/to/your/project
121
+
122
+ # Watch files and auto-reindex
123
+ memos watch --path /path/to/your/project
115
124
  ```
116
125
 
117
126
  Or using `uv run` without global install:
@@ -120,6 +129,8 @@ Or using `uv run` without global install:
120
129
  uv sync # install deps
121
130
  uv run memos index --path . # index current project
122
131
  uv run memos index --path . --full # force reindex (ignore hashes)
132
+ uv run memos index --path . --no-embed # skip embeddings (faster)
133
+ uv run memos index --path . --profile # print phase timings
123
134
  ```
124
135
 
125
136
  ## Query
@@ -248,6 +259,7 @@ Available tools:
248
259
  | `memory_search_tool` | Full-text search over memory entries |
249
260
  | `memory_prune_tool` | Delete stale memory entries (dry-run by default) |
250
261
  | `reindex_file_tool` | Re-index a single file after editing |
262
+ | `usage_stats_tool` | Show per-session tool call counters (resets on server restart) |
251
263
 
252
264
  Each query tool accepts an optional `project` parameter to target a specific opened project (defaults to the most recently opened one).
253
265
 
@@ -289,6 +301,9 @@ Configure in `claude_desktop_config.json`:
289
301
 
290
302
  ```bash
291
303
  uv run pytest -v
304
+
305
+ # With coverage (excludes slow tests)
306
+ uv run pytest --cov=memos --cov-report=term-missing -m "not slow"
292
307
  ```
293
308
 
294
309
  ## Architecture notes
@@ -302,6 +317,7 @@ uv run pytest -v
302
317
  - The CLI (`memos index`) is a thin adapter over `indexer/` + `core/db.py` —
303
318
  no business logic.
304
319
  - Semantic search: sqlite-vec vec0 table, lazy-loaded fastembed model, cascade
305
- cleanup on reindex (`--no-embed` flag to skip).
320
+ cleanup on reindex (`--no-embed` flag to skip). Embeddings computed in
321
+ batches of 256 with rich progress bar (`--profile` for phase timings).
306
322
  - MCP server (`memos serve-mcp`) is a thin FastMCP adapter over `query/core.py`
307
323
  — same pattern as the FastAPI adapter.
@@ -8,14 +8,17 @@
8
8
  ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=fff)
9
9
  ![TSX](https://img.shields.io/badge/TSX-3178C6?logo=react&logoColor=fff)
10
10
  ![Go](https://img.shields.io/badge/Go-00ADD8?logo=go&logoColor=fff)
11
+ ![JavaScript](https://img.shields.io/badge/JavaScript-F7DF1E?logo=javascript&logoColor=000)
12
+
13
+ [![codecov](https://codecov.io/gh/TAskMAster339/memos-engine/branch/main/graph/badge.svg)](https://codecov.io/gh/TAskMAster339/memos-engine)
11
14
 
12
15
  </div>
13
16
 
14
17
  `memos` builds a **structural index** (symbols, call edges, imports) of a
15
- TypeScript / TSX / Go / Python codebase using tree-sitter and stores it in
16
- SQLite. It is the first layer of a larger *Memory OS* for AI coding agents
17
- — instead of grepping text, agents query **structure** (definitions,
18
- callers, callees).
18
+ TypeScript / TSX / Go / Python / JavaScript codebase using tree-sitter and
19
+ stores it in SQLite. It is the first layer of a larger *Memory OS* for AI
20
+ coding agents — instead of grepping text, agents query **structure**
21
+ (definitions, callers, callees).
19
22
 
20
23
  ## Integrating memos into your project
21
24
 
@@ -38,13 +41,6 @@ cd memos-engine
38
41
  uv tool install -e .
39
42
  ```
40
43
 
41
- Or install from PyPI (no clone needed):
42
-
43
- ```bash
44
- pip install memos-engine
45
- # or via uv:
46
- uv tool install memos-engine
47
- ```
48
44
 
49
45
  **2. Index your project**
50
46
 
@@ -81,11 +77,23 @@ git clone https://github.com/TAskMAster339/memos-engine.git
81
77
  cd memos-engine
82
78
  uv tool install -e .
83
79
 
80
+ # Check version
81
+ memos --version
82
+
84
83
  # Index a project
85
84
  memos index --path /path/to/your/project
86
85
 
86
+ # List all available MCP tools
87
+ memos tools
88
+
87
89
  # Start the MCP server for AI agents
88
90
  memos serve-mcp
91
+
92
+ # Run project diagnostics
93
+ memos doctor --path /path/to/your/project
94
+
95
+ # Watch files and auto-reindex
96
+ memos watch --path /path/to/your/project
89
97
  ```
90
98
 
91
99
  Or using `uv run` without global install:
@@ -94,6 +102,8 @@ Or using `uv run` without global install:
94
102
  uv sync # install deps
95
103
  uv run memos index --path . # index current project
96
104
  uv run memos index --path . --full # force reindex (ignore hashes)
105
+ uv run memos index --path . --no-embed # skip embeddings (faster)
106
+ uv run memos index --path . --profile # print phase timings
97
107
  ```
98
108
 
99
109
  ## Query
@@ -222,6 +232,7 @@ Available tools:
222
232
  | `memory_search_tool` | Full-text search over memory entries |
223
233
  | `memory_prune_tool` | Delete stale memory entries (dry-run by default) |
224
234
  | `reindex_file_tool` | Re-index a single file after editing |
235
+ | `usage_stats_tool` | Show per-session tool call counters (resets on server restart) |
225
236
 
226
237
  Each query tool accepts an optional `project` parameter to target a specific opened project (defaults to the most recently opened one).
227
238
 
@@ -263,6 +274,9 @@ Configure in `claude_desktop_config.json`:
263
274
 
264
275
  ```bash
265
276
  uv run pytest -v
277
+
278
+ # With coverage (excludes slow tests)
279
+ uv run pytest --cov=memos --cov-report=term-missing -m "not slow"
266
280
  ```
267
281
 
268
282
  ## Architecture notes
@@ -276,6 +290,7 @@ uv run pytest -v
276
290
  - The CLI (`memos index`) is a thin adapter over `indexer/` + `core/db.py` —
277
291
  no business logic.
278
292
  - Semantic search: sqlite-vec vec0 table, lazy-loaded fastembed model, cascade
279
- cleanup on reindex (`--no-embed` flag to skip).
293
+ cleanup on reindex (`--no-embed` flag to skip). Embeddings computed in
294
+ batches of 256 with rich progress bar (`--profile` for phase timings).
280
295
  - MCP server (`memos serve-mcp`) is a thin FastMCP adapter over `query/core.py`
281
296
  — same pattern as the FastAPI adapter.
@@ -0,0 +1,175 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ from memos.core.db import MIGRATIONS_DIR, get_connection
8
+ from memos.indexer.diff import compute_file_hash
9
+
10
+ Status = Literal["ok", "warn", "error"]
11
+ _WARN_PCT_UNRESOLVED = 50
12
+ _WARN_PCT_EMBEDDING = 80
13
+
14
+
15
+ @dataclass
16
+ class DiagnosticResult:
17
+ check: str
18
+ status: Status
19
+ detail: str
20
+
21
+
22
+ def _get_schema_version(conn) -> int:
23
+ cur = conn.execute(
24
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'",
25
+ )
26
+ if cur.fetchone() is None:
27
+ return 0
28
+ return conn.execute(
29
+ "SELECT COALESCE(MAX(version), 0) FROM schema_version",
30
+ ).fetchone()[0]
31
+
32
+
33
+ def run_diagnostics( # noqa: C901, PLR0912, PLR0915
34
+ conn, project, root_path: str,
35
+ ) -> list[DiagnosticResult]:
36
+ results: list[DiagnosticResult] = []
37
+
38
+ db_path = str(Path(root_path) / ".memos" / "memory.db")
39
+ if not Path(db_path).exists():
40
+ results.append(DiagnosticResult(
41
+ check="Index file exists",
42
+ status="error",
43
+ detail=f".memos/memory.db not found at {root_path}"
44
+ " — run 'memos index --path .'",
45
+ ))
46
+ else:
47
+ results.append(DiagnosticResult(
48
+ check="Index file exists",
49
+ status="ok",
50
+ detail=".memos/memory.db found",
51
+ ))
52
+
53
+ try:
54
+ test_conn = get_connection(db_path)
55
+ test_conn.close()
56
+ vec_ok = True
57
+ except Exception as e:
58
+ vec_ok = False
59
+ results.append(DiagnosticResult(
60
+ check="sqlite-vec extension",
61
+ status="error",
62
+ detail=f"sqlite_vec failed to load: {e}",
63
+ ))
64
+ if vec_ok:
65
+ results.append(DiagnosticResult(
66
+ check="sqlite-vec extension",
67
+ status="ok",
68
+ detail="sqlite_vec loaded successfully",
69
+ ))
70
+
71
+ latest_migration = sorted(MIGRATIONS_DIR.glob("*.sql"))
72
+ expected = int(latest_migration[-1].stem.split("_")[0]) if latest_migration else 0
73
+ current = _get_schema_version(conn)
74
+ if current < expected:
75
+ results.append(DiagnosticResult(
76
+ check="Schema version",
77
+ status="warn",
78
+ detail=f"schema version {current} < expected {expected}"
79
+ " — run any memos command to auto-migrate",
80
+ ))
81
+ else:
82
+ results.append(DiagnosticResult(
83
+ check="Schema version",
84
+ status="ok",
85
+ detail=f"schema version {current} (latest)",
86
+ ))
87
+
88
+ files = conn.execute(
89
+ "SELECT f.id, f.path, f.content_hash FROM files f WHERE f.project_id = ?",
90
+ (project.id,),
91
+ ).fetchall()
92
+ stale = 0
93
+ for f in files:
94
+ full_path = str(Path(root_path) / f["path"])
95
+ if not Path(full_path).exists():
96
+ stale += 1
97
+ continue
98
+ current_hash = compute_file_hash(full_path)
99
+ if current_hash != f["content_hash"]:
100
+ stale += 1
101
+ if stale > 0:
102
+ results.append(DiagnosticResult(
103
+ check="Index freshness",
104
+ status="warn",
105
+ detail=f"{stale} of {len(files)} files stale"
106
+ " — run 'memos index --path .'",
107
+ ))
108
+ else:
109
+ results.append(DiagnosticResult(
110
+ check="Index freshness",
111
+ status="ok",
112
+ detail=f"all {len(files)} files up to date",
113
+ ))
114
+
115
+ total_edges = conn.execute(
116
+ "SELECT COUNT(*) FROM call_edges ce "
117
+ "JOIN symbols s ON s.id = ce.caller_symbol_id "
118
+ "JOIN files f ON f.id = s.file_id WHERE f.project_id = ?",
119
+ (project.id,),
120
+ ).fetchone()[0]
121
+ unresolved = conn.execute(
122
+ "SELECT COUNT(*) FROM call_edges ce "
123
+ "JOIN symbols s ON s.id = ce.caller_symbol_id "
124
+ "JOIN files f ON f.id = s.file_id "
125
+ "WHERE f.project_id = ? AND ce.callee_symbol_id IS NULL",
126
+ (project.id,),
127
+ ).fetchone()[0]
128
+ if total_edges > 0:
129
+ pct = unresolved / total_edges * 100
130
+ if pct > _WARN_PCT_UNRESOLVED:
131
+ results.append(DiagnosticResult(
132
+ check="Unresolved call edges",
133
+ status="warn",
134
+ detail=f"{unresolved}/{total_edges} ({pct:.0f}%) unresolved"
135
+ " — run 'memos index' to trigger resolution",
136
+ ))
137
+ else:
138
+ results.append(DiagnosticResult(
139
+ check="Unresolved call edges",
140
+ status="ok",
141
+ detail=f"{unresolved}/{total_edges} ({pct:.0f}%) unresolved",
142
+ ))
143
+ else:
144
+ results.append(DiagnosticResult(
145
+ check="Unresolved call edges",
146
+ status="ok",
147
+ detail="no call edges",
148
+ ))
149
+
150
+ sym_count = conn.execute(
151
+ "SELECT COUNT(*) FROM symbols s "
152
+ "JOIN files f ON f.id = s.file_id WHERE f.project_id = ?",
153
+ (project.id,),
154
+ ).fetchone()[0]
155
+ vec_count = conn.execute(
156
+ "SELECT COUNT(*) FROM vec_symbols vs "
157
+ "JOIN symbols s ON s.id = vs.rowid "
158
+ "JOIN files f ON f.id = s.file_id WHERE f.project_id = ?",
159
+ (project.id,),
160
+ ).fetchone()[0]
161
+ if sym_count > 0 and vec_count / sym_count * 100 < _WARN_PCT_EMBEDDING:
162
+ results.append(DiagnosticResult(
163
+ check="Embedding coverage",
164
+ status="warn",
165
+ detail=f"{vec_count}/{sym_count} symbols have embeddings (<80%)"
166
+ " — reindex without --no-embed",
167
+ ))
168
+ else:
169
+ results.append(DiagnosticResult(
170
+ check="Embedding coverage",
171
+ status="ok",
172
+ detail=f"{vec_count}/{sym_count} symbols have embeddings",
173
+ ))
174
+
175
+ return results