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.
- memos_engine-0.3.0/.github/workflows/ci.yml +47 -0
- memos_engine-0.3.0/.github/workflows/release.yml +39 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/.gitignore +1 -1
- {memos_engine-0.2.0 → memos_engine-0.3.0}/AGENTS.md +32 -5
- {memos_engine-0.2.0 → memos_engine-0.3.0}/AGENTS_EXAMPLE.md +2 -1
- memos_engine-0.3.0/CHANGELOG.md +32 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/PKG-INFO +29 -13
- {memos_engine-0.2.0 → memos_engine-0.3.0}/README.md +27 -12
- memos_engine-0.3.0/memos/cli/doctor.py +175 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/cli/main.py +222 -14
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/db.py +30 -13
- memos_engine-0.3.0/memos/core/migrations/0005_symbol_name_file_index.sql +3 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/schema.sql +1 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/typescript.py +19 -3
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/mcp/server.py +52 -20
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/query/core.py +1 -1
- {memos_engine-0.2.0 → memos_engine-0.3.0}/pyproject.toml +10 -1
- memos_engine-0.3.0/scripts/benchmark_index.py +108 -0
- memos_engine-0.3.0/tests/fixtures/javascript_mini/src/index.js +5 -0
- memos_engine-0.3.0/tests/fixtures/javascript_mini/src/utils.js +9 -0
- memos_engine-0.3.0/tests/test_cli_doctor.py +134 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_cli_index.py +72 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_cli_query.py +37 -0
- memos_engine-0.3.0/tests/test_javascript_indexer.py +124 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_mcp.py +12 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_migrations.py +2 -2
- memos_engine-0.3.0/tests/test_packaging.py +6 -0
- memos_engine-0.3.0/tests/test_query_efficiency.py +91 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_schema.py +1 -1
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_typescript_indexer.py +8 -0
- memos_engine-0.3.0/tests/test_watch.py +103 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/uv.lock +113 -2
- memos_engine-0.2.0/.github/workflows/ci.yml +0 -18
- memos_engine-0.2.0/.github/workflows/release.yml +0 -25
- memos_engine-0.2.0/CHANGELOG.md +0 -19
- {memos_engine-0.2.0 → memos_engine-0.3.0}/.github/release.yml +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/.python-version +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/LICENSE +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/api/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/api/main.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/api/schemas.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/cli/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0001_init.sql +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0002_vec.sql +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0003_prompt_version.sql +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/0004_memory_fts.sql +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/migrations/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/core/models.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/base.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/diff.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/go.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/indexer/python.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/mcp/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/query/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/base.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/embeddings.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/memos/search/sqlite_vec_store.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/conftest.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/__init__.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/src/main.go +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/src/types.go +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/go_mini/src/utils.go +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/import_cycle/src/a.ts +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/import_cycle/src/b.ts +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/python_mini/src/main.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/python_mini/src/types.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/python_mini/src/utils.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/typescript_mini/src/index.ts +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/typescript_mini/src/types.ts +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/fixtures/typescript_mini/src/utils.ts +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_api.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_crud.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_dependency_graph.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_go_indexer.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_hygiene.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_impact.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_llm_summary.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_memory_hygiene.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_python_indexer.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_query.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_reindex.py +0 -0
- {memos_engine-0.2.0 → memos_engine-0.3.0}/tests/test_resolver.py +0 -0
- {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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|

|
|
35
36
|

|
|
36
37
|

|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
[](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
|
|
42
|
-
SQLite. It is the first layer of a larger *Memory OS* for AI
|
|
43
|
-
— instead of grepping text, agents query **structure**
|
|
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
|

|
|
9
9
|

|
|
10
10
|

|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
[](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
|
|
16
|
-
SQLite. It is the first layer of a larger *Memory OS* for AI
|
|
17
|
-
— instead of grepping text, agents query **structure**
|
|
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
|