vectr 1.0.0__tar.gz → 1.0.2__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 (107) hide show
  1. vectr-1.0.2/PKG-INFO +373 -0
  2. {vectr-1.0.0 → vectr-1.0.2}/README.md +2 -2
  3. {vectr-1.0.0 → vectr-1.0.2}/api.py +1 -1
  4. {vectr-1.0.0 → vectr-1.0.2}/app/models.py +0 -4
  5. {vectr-1.0.0 → vectr-1.0.2}/app/routes.py +0 -5
  6. {vectr-1.0.0 → vectr-1.0.2}/pyproject.toml +11 -2
  7. vectr-1.0.2/vectr.egg-info/PKG-INFO +373 -0
  8. {vectr-1.0.0 → vectr-1.0.2}/vectr.egg-info/SOURCES.txt +0 -1
  9. vectr-1.0.0/PKG-INFO +0 -37
  10. vectr-1.0.0/agent/llm_client.py +0 -8
  11. vectr-1.0.0/vectr.egg-info/PKG-INFO +0 -37
  12. {vectr-1.0.0 → vectr-1.0.2}/LICENSE +0 -0
  13. {vectr-1.0.0 → vectr-1.0.2}/MANIFEST.in +0 -0
  14. {vectr-1.0.0 → vectr-1.0.2}/agent/__init__.py +0 -0
  15. {vectr-1.0.0 → vectr-1.0.2}/agent/cartographer.py +0 -0
  16. {vectr-1.0.0 → vectr-1.0.2}/agent/chunk_quality.py +0 -0
  17. {vectr-1.0.0 → vectr-1.0.2}/agent/config.py +0 -0
  18. {vectr-1.0.0 → vectr-1.0.2}/agent/config.yaml +0 -0
  19. {vectr-1.0.0 → vectr-1.0.2}/agent/eviction_advisor.py +0 -0
  20. {vectr-1.0.0 → vectr-1.0.2}/agent/identifier_hint.py +0 -0
  21. {vectr-1.0.0 → vectr-1.0.2}/agent/indexer/__init__.py +0 -0
  22. {vectr-1.0.0 → vectr-1.0.2}/agent/indexer/_chunking.py +0 -0
  23. {vectr-1.0.0 → vectr-1.0.2}/agent/indexer/_constants.py +0 -0
  24. {vectr-1.0.0 → vectr-1.0.2}/agent/indexer/_core.py +0 -0
  25. {vectr-1.0.0 → vectr-1.0.2}/agent/indexer/_types.py +0 -0
  26. {vectr-1.0.0 → vectr-1.0.2}/agent/instance_registry.py +0 -0
  27. {vectr-1.0.0 → vectr-1.0.2}/agent/model_cache.py +0 -0
  28. {vectr-1.0.0 → vectr-1.0.2}/agent/prompt_templates.py +0 -0
  29. {vectr-1.0.0 → vectr-1.0.2}/agent/searcher.py +0 -0
  30. {vectr-1.0.0 → vectr-1.0.2}/agent/strategy_selector.py +0 -0
  31. {vectr-1.0.0 → vectr-1.0.2}/agent/symbol_graph/__init__.py +0 -0
  32. {vectr-1.0.0 → vectr-1.0.2}/agent/symbol_graph/_constants.py +0 -0
  33. {vectr-1.0.0 → vectr-1.0.2}/agent/symbol_graph/_extraction.py +0 -0
  34. {vectr-1.0.0 → vectr-1.0.2}/agent/symbol_graph/_graph.py +0 -0
  35. {vectr-1.0.0 → vectr-1.0.2}/agent/symbol_graph/_types.py +0 -0
  36. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/claude_md.md +0 -0
  37. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/claude_md_search_only.md +0 -0
  38. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/cursor_mcp.json.template +0 -0
  39. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/cursor_rules_header.txt +0 -0
  40. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/hook_no_double_recall.txt +0 -0
  41. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/mcp.json.template +0 -0
  42. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/session_start_guidance_default.txt +0 -0
  43. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/session_start_guidance_hooks_aware.txt +0 -0
  44. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/tool_loading_guidance_claude.txt +0 -0
  45. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/tool_loading_guidance_claude_search_only.txt +0 -0
  46. {vectr-1.0.0 → vectr-1.0.2}/agent/templates/vscode_mcp.json.template +0 -0
  47. {vectr-1.0.0 → vectr-1.0.2}/agent/tool_necessity_probe.py +0 -0
  48. {vectr-1.0.0 → vectr-1.0.2}/agent/version_stamp.py +0 -0
  49. {vectr-1.0.0 → vectr-1.0.2}/agent/watcher.py +0 -0
  50. {vectr-1.0.0 → vectr-1.0.2}/agent/working_context_store/__init__.py +0 -0
  51. {vectr-1.0.0 → vectr-1.0.2}/agent/working_context_store/_audit.py +0 -0
  52. {vectr-1.0.0 → vectr-1.0.2}/agent/working_context_store/_encryption.py +0 -0
  53. {vectr-1.0.0 → vectr-1.0.2}/agent/working_context_store/_store.py +0 -0
  54. {vectr-1.0.0 → vectr-1.0.2}/agent/working_context_store/_types.py +0 -0
  55. {vectr-1.0.0 → vectr-1.0.2}/app/__init__.py +0 -0
  56. {vectr-1.0.0 → vectr-1.0.2}/app/service.py +0 -0
  57. {vectr-1.0.0 → vectr-1.0.2}/integrations/__init__.py +0 -0
  58. {vectr-1.0.0 → vectr-1.0.2}/integrations/mcp_server/__init__.py +0 -0
  59. {vectr-1.0.0 → vectr-1.0.2}/integrations/mcp_server/_dispatch.py +0 -0
  60. {vectr-1.0.0 → vectr-1.0.2}/integrations/mcp_server/_schemas.py +0 -0
  61. {vectr-1.0.0 → vectr-1.0.2}/integrations/mcp_server/_session.py +0 -0
  62. {vectr-1.0.0 → vectr-1.0.2}/integrations/vscode_bridge.py +0 -0
  63. {vectr-1.0.0 → vectr-1.0.2}/integrations/workspace_detect.py +0 -0
  64. {vectr-1.0.0 → vectr-1.0.2}/main.py +0 -0
  65. {vectr-1.0.0 → vectr-1.0.2}/setup.cfg +0 -0
  66. {vectr-1.0.0 → vectr-1.0.2}/tests/test_agent.py +0 -0
  67. {vectr-1.0.0 → vectr-1.0.2}/tests/test_api.py +0 -0
  68. {vectr-1.0.0 → vectr-1.0.2}/tests/test_api_memory.py +0 -0
  69. {vectr-1.0.0 → vectr-1.0.2}/tests/test_cartographer.py +0 -0
  70. {vectr-1.0.0 → vectr-1.0.2}/tests/test_chunk_quality.py +0 -0
  71. {vectr-1.0.0 → vectr-1.0.2}/tests/test_config_loader.py +0 -0
  72. {vectr-1.0.0 → vectr-1.0.2}/tests/test_eviction_advisor.py +0 -0
  73. {vectr-1.0.0 → vectr-1.0.2}/tests/test_eviction_session_scope.py +0 -0
  74. {vectr-1.0.0 → vectr-1.0.2}/tests/test_fetch_route.py +0 -0
  75. {vectr-1.0.0 → vectr-1.0.2}/tests/test_grammar_availability.py +0 -0
  76. {vectr-1.0.0 → vectr-1.0.2}/tests/test_hint_language_live.py +0 -0
  77. {vectr-1.0.0 → vectr-1.0.2}/tests/test_hook_injection_observability.py +0 -0
  78. {vectr-1.0.0 → vectr-1.0.2}/tests/test_identifier_hint.py +0 -0
  79. {vectr-1.0.0 → vectr-1.0.2}/tests/test_indexer_searcher.py +0 -0
  80. {vectr-1.0.0 → vectr-1.0.2}/tests/test_instance_registry.py +0 -0
  81. {vectr-1.0.0 → vectr-1.0.2}/tests/test_integration.py +0 -0
  82. {vectr-1.0.0 → vectr-1.0.2}/tests/test_main.py +0 -0
  83. {vectr-1.0.0 → vectr-1.0.2}/tests/test_mcp_jsonrpc.py +0 -0
  84. {vectr-1.0.0 → vectr-1.0.2}/tests/test_mcp_server.py +0 -0
  85. {vectr-1.0.0 → vectr-1.0.2}/tests/test_mcp_session_handshake.py +0 -0
  86. {vectr-1.0.0 → vectr-1.0.2}/tests/test_memory.py +0 -0
  87. {vectr-1.0.0 → vectr-1.0.2}/tests/test_memory_only_mode.py +0 -0
  88. {vectr-1.0.0 → vectr-1.0.2}/tests/test_model_cache.py +0 -0
  89. {vectr-1.0.0 → vectr-1.0.2}/tests/test_prompt_templates.py +0 -0
  90. {vectr-1.0.0 → vectr-1.0.2}/tests/test_query_prompt.py +0 -0
  91. {vectr-1.0.0 → vectr-1.0.2}/tests/test_ragas_eval.py +0 -0
  92. {vectr-1.0.0 → vectr-1.0.2}/tests/test_recall_hierarchy.py +0 -0
  93. {vectr-1.0.0 → vectr-1.0.2}/tests/test_remember_banner_fatigue.py +0 -0
  94. {vectr-1.0.0 → vectr-1.0.2}/tests/test_search_only_mode.py +0 -0
  95. {vectr-1.0.0 → vectr-1.0.2}/tests/test_service.py +0 -0
  96. {vectr-1.0.0 → vectr-1.0.2}/tests/test_strategy.py +0 -0
  97. {vectr-1.0.0 → vectr-1.0.2}/tests/test_subagent_memory.py +0 -0
  98. {vectr-1.0.0 → vectr-1.0.2}/tests/test_symbol_graph.py +0 -0
  99. {vectr-1.0.0 → vectr-1.0.2}/tests/test_task_note_recency.py +0 -0
  100. {vectr-1.0.0 → vectr-1.0.2}/tests/test_tool_necessity_probe.py +0 -0
  101. {vectr-1.0.0 → vectr-1.0.2}/tests/test_version_stamp.py +0 -0
  102. {vectr-1.0.0 → vectr-1.0.2}/tests/test_watcher.py +0 -0
  103. {vectr-1.0.0 → vectr-1.0.2}/tests/test_workspace_detect.py +0 -0
  104. {vectr-1.0.0 → vectr-1.0.2}/vectr.egg-info/dependency_links.txt +0 -0
  105. {vectr-1.0.0 → vectr-1.0.2}/vectr.egg-info/entry_points.txt +0 -0
  106. {vectr-1.0.0 → vectr-1.0.2}/vectr.egg-info/requires.txt +0 -0
  107. {vectr-1.0.0 → vectr-1.0.2}/vectr.egg-info/top_level.txt +0 -0
vectr-1.0.2/PKG-INFO ADDED
@@ -0,0 +1,373 @@
1
+ Metadata-Version: 2.4
2
+ Name: vectr
3
+ Version: 1.0.2
4
+ Summary: Semantic codebase search + persistent working memory for AI code editors. Local, zero-config, MCP. No API key.
5
+ Author-email: Swapnanil Saha <swapnanilsaha26@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://swapnanilsaha.com/tools/vectr
8
+ Project-URL: Repository, https://github.com/swapnanil/vectr
9
+ Project-URL: Changelog, https://github.com/swapnanil/vectr/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/swapnanil/vectr/issues
11
+ Requires-Python: >=3.14
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: fastapi==0.136.3
15
+ Requires-Dist: uvicorn==0.47.0
16
+ Requires-Dist: pydantic==2.13.4
17
+ Requires-Dist: python-dotenv==1.2.2
18
+ Requires-Dist: httpx==0.28.1
19
+ Requires-Dist: sentence-transformers==5.5.1
20
+ Requires-Dist: chromadb==1.5.9
21
+ Requires-Dist: tree-sitter==0.25.2
22
+ Requires-Dist: tree-sitter-python==0.25.0
23
+ Requires-Dist: tree-sitter-javascript==0.25.0
24
+ Requires-Dist: tree-sitter-typescript==0.23.2
25
+ Requires-Dist: tree-sitter-go==0.25.0
26
+ Requires-Dist: tree-sitter-rust==0.24.2
27
+ Requires-Dist: tree-sitter-java==0.23.5
28
+ Requires-Dist: tree-sitter-zig==1.1.2
29
+ Requires-Dist: tree-sitter-c==0.24.2
30
+ Requires-Dist: tree-sitter-cpp==0.23.4
31
+ Requires-Dist: watchdog==6.0.0
32
+ Requires-Dist: rank-bm25==0.2.2
33
+ Requires-Dist: pyyaml==6.0.3
34
+ Provides-Extra: openai
35
+ Requires-Dist: openai==2.38.0; extra == "openai"
36
+ Provides-Extra: encryption
37
+ Requires-Dist: cryptography>=43.0; extra == "encryption"
38
+ Provides-Extra: ragas
39
+ Requires-Dist: ragas>=0.1; extra == "ragas"
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest==9.0.3; extra == "dev"
42
+ Requires-Dist: pytest-asyncio==1.3.0; extra == "dev"
43
+ Requires-Dist: anyio==4.13.0; extra == "dev"
44
+ Dynamic: license-file
45
+
46
+ # Vectr
47
+
48
+ > **Semantic search and persistent memory for AI code editors.**
49
+
50
+ [![CI](https://github.com/swapnanil/vectr/actions/workflows/ci.yml/badge.svg)](https://github.com/swapnanil/vectr/actions/workflows/ci.yml)
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
52
+ [![Python 3.14+](https://img.shields.io/badge/python-3.14%2B-blue.svg)](https://www.python.org/downloads/)
53
+ [![Version 1.0.0](https://img.shields.io/badge/version-1.0.0-blue.svg)](CHANGELOG.md)
54
+ [![MCP: 14 tools](https://img.shields.io/badge/MCP-14%20tools-blue.svg)](#14-mcp-tools)
55
+
56
+ Version 1.0.0 · Last updated 2026-07-08 · [CHANGELOG](CHANGELOG.md)
57
+
58
+ Vectr gives AI code editors two things they lack: **semantic codebase search** and **persistent working memory** — both served over MCP with zero configuration.
59
+
60
+ Your AI editor forgets everything. Vectr doesn't.
61
+
62
+ ---
63
+
64
+ ## The problem
65
+
66
+ Every time an AI code editor starts a task, it re-reads the same files it read yesterday. On an unfamiliar codebase it runs ripgrep, reads entire files hunting for the right function, and fills its context window with noise. In a long session it loses findings from turn 1 by turn 40. Across sessions it starts over from zero.
67
+
68
+ Vectr breaks the re-discovery loop:
69
+
70
+ - **One index** → semantic search over your whole codebase in <20ms
71
+ - **One recall call** → structured notes from any prior session, verbatim, in <50ms
72
+ - **Survives `/compact`** → notes are persisted to disk, not stored in context
73
+
74
+ **Measured, not hypothetical:** recalling 3 stored notes with `vectr_recall` costs 360 tokens in one tool call. Re-deriving the same three facts with grep + Read costs ~2,060 tokens across six tool calls on the same 182-file Python repo — **~5.7× fewer tokens, 6× fewer tool calls**, in under 50ms (chars/4 tokenization; full breakdown in [Measured costs, honestly](#measured-costs-honestly)). Across a 6-task CPython sprint measuring real Read+Bash calls, that recall discipline cut re-discovery by **39% overall**, with per-task reductions ranging **0%–85%** depending on how unfamiliar the code was to the model (the 0% task is one the model could already navigate from training — see [When vectr can hurt](#when-vectr-can-hurt)).
75
+
76
+ Notes are persisted to disk, not held in the conversation — they survive `/compact` and a fresh session equally; the session boundary doesn't matter.
77
+
78
+ **No API key required.** The embedding model runs locally.
79
+
80
+ ---
81
+
82
+ ## Benchmarks — CPython internals sprint (6 tasks, 2 agents)
83
+
84
+ The benchmark simulates a week of feature work on an unfamiliar C codebase (CPython internals). One research session stores findings with `vectr_remember`; six isolated implementation sessions each open cold and call `vectr_recall`.
85
+
86
+ **Implementation sessions only — 6 tasks combined:**
87
+
88
+ | Metric | Vanilla | Vectr | Delta |
89
+ |---|---|---|---|
90
+ | Cost | $2.50 | $1.97 | **−21%** |
91
+ | Wall time | 17.6 min | 13.5 min | **−24%** |
92
+ | Turns | 123 | 94 | **−24%** |
93
+ | Read + Bash calls | 102 | 62 | **−39%** |
94
+
95
+ **Per-task re-discovery (Read+Bash before first write):**
96
+
97
+ | Task | Vanilla | Vectr | Delta |
98
+ |---|---|---|---|
99
+ | `debug_gc_finalizer` | 16 | 6 | −62% |
100
+ | `feature_dict_pop_last` | 13 | 3 | −77% |
101
+ | `cross_session_set_cartesian` | 23 | 9 | −61% |
102
+ | `debug_descriptor_priority` | 6 | 6 | 0% |
103
+ | `cross_session_bytes_find_all` | 13 | 2 | −85% |
104
+ | `cross_session_list_rotate` | 21 | 16 | −24% |
105
+
106
+ **Research vs implementation cost breakdown:**
107
+
108
+ The research phase (paid once to build notes) costs more for vectr (+94%) because it stores rich code stubs and function signatures via `vectr_remember`. The implementation phases (which repeat every task) cost less because `vectr_recall` replaces file re-discovery. The research overhead breaks even after ~8 tasks of note reuse.
109
+
110
+ | Phase | Vanilla | Vectr | Why |
111
+ |---|---|---|---|
112
+ | Research (1 session, paid once) | $1.36 | $2.63 | Vectr stores notes — more output tokens |
113
+ | Impl (6 sessions, repeating) | $2.50 | $1.97 | Notes replace re-discovery |
114
+ | Total sprint | $3.86 | $4.60 | Inverts to net gain after ~8 tasks |
115
+
116
+ Earlier runs on Apache Camel (Java, 5,856 files): **−58% impl cost · −72% impl tool calls · −39% wall time.**
117
+
118
+ Full results: [`benchmarks/`](https://github.com/swapnanil/vectr/tree/main/benchmarks)
119
+
120
+ ---
121
+
122
+ ## Measured costs, honestly
123
+
124
+ Per-call token cost (median, 182-file Python repo, chars/4 tokenization):
125
+
126
+ | Tool | Median tokens | Range |
127
+ |---|---:|---|
128
+ | `vectr_search` | ~2,320 | 1,437–3,091 (n=8) |
129
+ | `vectr_locate` | ~192 | — |
130
+ | `vectr_trace` | ~720 | — |
131
+ | `vectr_recall` (index tier) | ~180 | — |
132
+
133
+ The trade-off, stated plainly: for a single pointed lookup on a small, already-familiar repo, grep is cheaper — vectr's median cost across 5 single-fact tasks was **+60% more tokens** — and faster, since a `vectr_search` round-trip takes 1.7–3.6s against ~28ms for grep. Vectr doesn't win on per-call cost; it wins on tool-call count (one round-trip instead of several), answer completeness (a whole symbol back, not a partial file read), and everything in working memory — the 5.7× recall refund from the opening section compounds with every task you resume.
134
+
135
+ Fine print: the automatic eviction/reminder banners riding along on tool responses cost tokens too — an always-on re-fetch footer runs ~27 tokens, a light nudge ~89 tokens, and the escalated action-required banner (fires only after both the chunk and token thresholds are crossed without a save) scales from ~480 to ~535 tokens before it plateaus.
136
+
137
+ **When it pays off:** unfamiliar or large codebases, work you resume (later this session, after `/compact`, or in a new session), and long sessions with many turns. **When it doesn't:** a one-off grep on code you already know cold — reach for grep instead.
138
+
139
+ ---
140
+
141
+ ## Quick start
142
+
143
+ **Local (recommended)**
144
+
145
+ ```bash
146
+ python3.14 -m venv ~/.vectr-env
147
+ source ~/.vectr-env/bin/activate # Windows: ~/.vectr-env/Scripts/activate
148
+ pip install vectr
149
+ cd /path/to/your/project
150
+ vectr start
151
+ ```
152
+
153
+ **Requires Python 3.14+.** To install:
154
+ - macOS: `brew install python@3.14`
155
+ - Ubuntu/Debian: `sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt install python3.14 python3.14-venv`
156
+ - Windows: [python.org/downloads](https://www.python.org/downloads/)
157
+
158
+ `vectr start` returns immediately. Indexing runs in the background — run `vectr status` to check progress. On first run the embedding model downloads once (~440 MB). Restart your AI code editor once to pick up the new MCP config.
159
+
160
+ **Docker (CI/servers)**
161
+
162
+ ```bash
163
+ git clone https://github.com/swapnanil/vectr
164
+ cd vectr
165
+ docker-compose up api
166
+ ```
167
+
168
+ Exposes port 8765. Docker does not auto-write IDE config files — use local install for IDE integration.
169
+
170
+ ---
171
+
172
+ ## Connect to your AI code editor
173
+
174
+ `vectr start` writes the MCP config for your editor automatically. Restart your editor once.
175
+
176
+ | Editor | Config | Status |
177
+ |---|---|---|
178
+ | Claude Code | Auto — `.claude/settings.json`, guidance file, and session hooks (memory auto-injected at session start, per prompt, and before file read/edit) | **Verified** |
179
+ | Cursor | Auto — `.cursor/mcp.json` | Experimental |
180
+ | VS Code / GitHub Copilot | Auto — `.vscode/mcp.json` | Experimental |
181
+ | Windsurf | Manual — see below | Experimental |
182
+ | Cline | Manual — see below | Experimental |
183
+ | Continue | Manual — see below | Experimental |
184
+ | Codex CLI | — | Planned (post-v1) |
185
+
186
+ "Verified" means the full integration (config, guidance, and hooks) has been exercised end to end. "Experimental" means the MCP config is written and works, but the integration hasn't been run through the same verification pass. "Planned" means no support yet.
187
+
188
+ <details>
189
+ <summary>Manual setup</summary>
190
+
191
+ **Claude Code** — `.claude/settings.json`:
192
+ ```json
193
+ { "mcpServers": { "vectr": { "type": "http", "url": "http://localhost:8765/mcp" } } }
194
+ ```
195
+
196
+ **Cursor** — `.cursor/mcp.json`:
197
+ ```json
198
+ { "mcpServers": { "vectr": { "url": "http://localhost:8765/mcp" } } }
199
+ ```
200
+
201
+ **VS Code / GitHub Copilot** (1.99+) — `.vscode/mcp.json`:
202
+ ```json
203
+ { "servers": { "vectr": { "type": "http", "url": "http://localhost:8765/mcp" } } }
204
+ ```
205
+
206
+ **Windsurf** — `~/.codeium/windsurf/mcp_settings.json`:
207
+ ```json
208
+ { "mcpServers": { "vectr": { "serverUrl": "http://localhost:8765/mcp" } } }
209
+ ```
210
+
211
+ **Continue.dev** — `.continue/config.json`:
212
+ ```json
213
+ { "mcpServers": [{ "name": "vectr", "transport": { "type": "http", "url": "http://localhost:8765/mcp" } }] }
214
+ ```
215
+
216
+ </details>
217
+
218
+ ---
219
+
220
+ ## How it works
221
+
222
+ 1. **AST-aware chunking** — tree-sitter parses each file and splits at function/class/method boundaries. No chunk breaks mid-logic.
223
+ 2. **Code embeddings** — `ibm-granite/granite-embedding-english-r2` (local, CPU-fast, overridable) maps natural-language queries to code symbols ("JWT validation" → `verify_jwt_token`). BM25 handles exact symbol names.
224
+ 3. **Hybrid search** — vector similarity + BM25 combined, weighted by codebase characteristics (large/unfamiliar → semantic-heavy; small/well-documented → BM25-heavy).
225
+ 4. **Symbol graph** — call edges, import chains, and HTTP routes (Flask/FastAPI/Express/Spring) are extracted and stored. `vectr_locate` uses 5 fallback strategies: exact match → suffix → same-module → unique-name → import-chain → fuzzy (edit distance ≤ 2).
226
+ 5. **Working memory** — `vectr_remember` stores structured notes to SQLite + ChromaDB. `vectr_recall` does semantic search over notes — not SQL LIKE — so multi-word queries always find relevant context.
227
+ 6. **MCP protocol** — 14 tools served over HTTP. Any MCP-compatible AI code editor connects without plugins.
228
+
229
+ ---
230
+
231
+ ## 14 MCP tools
232
+
233
+ `vectr start` writes a `CLAUDE.md` into your workspace with this table and usage guidance — your AI code editor knows which tool to reach for without being prompted.
234
+
235
+ **Search tools** — retrieve code from the index:
236
+
237
+ | Situation | Tool |
238
+ |---|---|
239
+ | You know a concept or behaviour, not a name | `vectr_search("description")` |
240
+ | You know a symbol name, not its file | `vectr_locate("SymbolName")` — 5 fallback strategies, optional `caller_file` |
241
+ | You need callers / callees of a symbol | `vectr_trace("symbol_name")` |
242
+ | You need an architectural overview | `vectr_map()` |
243
+ | You want to save a synthesised map summary | `vectr_map_save(summary)` |
244
+ | You have runtime call data to inject | `vectr_ingest_traces([{caller, callee}])` |
245
+ | You need index health / note count | `vectr_status()` |
246
+
247
+ **Memory tools** — store and recall across sessions:
248
+
249
+ | Situation | Tool |
250
+ |---|---|
251
+ | Notes exist from a prior session | `vectr_recall(query)` — semantic vector search, not substring match; two-tier (crisp index by default, expand one note with `note_id=N` or all bodies with `detail='full'`) |
252
+ | You found something worth preserving | `vectr_remember(content, tags, priority, kind, title, agent)` — `kind` controls injection: `directive` fires unconditionally every session, `task` carries current-work state, `gotcha` resurfaces when its file is touched, `finding` (default) is relevance-ranked, `reference` is a pointer; `title` labels the note in index output; `agent` attributes it to a subagent/orchestrator |
253
+ | Context is filling up | `vectr_evict_hint()` — identifies chunks vectr can re-retrieve, with the exact re-fetch ids |
254
+ | A chunk shown earlier has left your context | `vectr_fetch(ids=[...])` — deterministic, byte-verbatim re-fetch by id; no re-search, no file re-read; flags a truncation warning if the index itself stored a capped chunk |
255
+ | End of a long session, want a checkpoint | `vectr_snapshot("label")` |
256
+ | Looking for a prior checkpoint | `vectr_snapshot_list()` |
257
+ | Notes are stale after a large refactor | `vectr_forget(note_id=N)` per note, or `vectr_forget(all=true)` to clear |
258
+
259
+ Workspace-scoped notes double as a shared bus for multi-agent workflows: an orchestrator and its subagents all read and write the same note store, so a subagent can call `vectr_remember(..., agent="coder-2")` with its findings before finishing, and the orchestrator recalls them instead of re-reading the subagent's full transcript. The `agent` param is never inferred — it's explicit attribution, and it shows up as a tag in `vectr_recall` index output.
260
+
261
+ On editors with session hooks (see the [host-support matrix](#connect-to-your-ai-code-editor) for which ones), recall is injected automatically — directives and high-priority tasks at session start, semantic recall keyed to each prompt, and file-anchored gotchas before a read or edit — with observability via a `Hook injections` line in `vectr status`.
262
+
263
+ ---
264
+
265
+ ## CLI reference
266
+
267
+ ```bash
268
+ vectr start # index + start daemon for current dir
269
+ vectr start /project/api # positional workspace: a directory or .code-workspace file
270
+ vectr start --path /project/api # specific workspace (repeatable, multi-root)
271
+ vectr start --memory-only # working memory + hooks only — no code index, no watcher
272
+ vectr status # index health, chunk count, notes count
273
+ vectr status --all # all running instances
274
+ vectr stop /project/api # stop one instance (same positional as start)
275
+ vectr stop --path /project/api # stop one instance (equivalent --path form)
276
+ vectr stop --all # stop all instances
277
+ vectr index --path . # re-index without restarting daemon
278
+ vectr fetch src/auth.py:10-42 # re-fetch a chunk by exact id, verbatim
279
+ vectr init --path . # write CLAUDE.md + MCP config without starting
280
+ vectr init --exclude vendor # exclude directories from indexing
281
+ vectr forget --path . # delete all working-memory notes
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Excluding paths
287
+
288
+ Create `.vectrignore` in your project root (same syntax as `.gitignore`):
289
+
290
+ ```
291
+ vendor/
292
+ node_modules/
293
+ *.pb.go
294
+ dist/
295
+ ```
296
+
297
+ Or pass `--exclude` at init time:
298
+
299
+ ```bash
300
+ vectr init --exclude vendor --exclude dist
301
+ ```
302
+
303
+ Exclusions apply to both the initial index walk **and** the live file watcher, so
304
+ adding a directory to `.vectrignore` stops a running instance from re-indexing it.
305
+ The next index also **prunes** any chunks already stored for now-excluded (or
306
+ deleted) files — you don't have to rebuild from scratch. If you ever need a clean
307
+ rebuild (e.g. after changing the embedding model), force one:
308
+
309
+ ```bash
310
+ vectr index --path . --force # ignore the incremental cache, re-embed everything
311
+ ```
312
+
313
+ ---
314
+
315
+ ## Supported languages
316
+
317
+ | Language | Chunking | Symbol graph |
318
+ |---|---|---|
319
+ | Python | AST (functions, classes) | ✓ |
320
+ | JavaScript | AST (functions, classes, arrow fns) | ✓ |
321
+ | TypeScript | AST | ✓ |
322
+ | Go | AST | ✓ |
323
+ | Rust | AST | ✓ |
324
+ | Java | AST | ✓ |
325
+ | C | AST | ✓ |
326
+ | C++ | AST | ✓ |
327
+ | Zig | AST | ✓ |
328
+ | All others | 200-line windows, 50-line overlap | — |
329
+
330
+ HTTP routes (Flask/FastAPI decorators, Express `app.get()`, Spring `@GetMapping`) are extracted as symbols and searchable via `vectr_locate("GET /api/users")`.
331
+
332
+ ---
333
+
334
+ ## Cost
335
+
336
+ | | Cost |
337
+ |---|---|
338
+ | Embedding model | $0.00 — one-time ~440 MB download, cached at `~/.cache/vectr/` |
339
+ | Re-index (10k files, first run) | ~10 min on CPU; <5 sec on subsequent runs (mtime cache) |
340
+ | Incremental re-index per changed file | ~0.5 sec |
341
+ | vectr_search / vectr_recall | $0.00 — local inference only |
342
+
343
+ ---
344
+
345
+ ## Security
346
+
347
+ Vectr v1 is designed for a **solo developer on a personal machine**.
348
+
349
+ - MCP server binds to `127.0.0.1` only — not reachable from other hosts
350
+ - CORS restricted to localhost origins
351
+ - Each workspace gets its own isolated DB directory, port, and process
352
+ - No API key authentication in v1 — any local process can query
353
+ - Index and notes persist locally in `~/.cache/vectr/`
354
+
355
+ Multi-user, authentication, and encryption at rest are out of scope for v1.
356
+
357
+ ---
358
+
359
+ ## When vectr can hurt
360
+
361
+ **Stale notes after codebase churn** — notes store file paths at write time. After a large refactor, `vectr_recall` will flag changed referenced files with `[STALE]`. Re-verify before acting, delete the stale note with `vectr_forget(note_id=N)`, or clear everything with `vectr_forget(all=true)`.
362
+
363
+ **Over-retrieval on a well-known API** — if the model already knows a framework deeply from training (React hooks, Django ORM), vectr's research overhead may exceed savings. The benchmark shows 0% improvement on `debug_descriptor_priority` — a task where the model's training knowledge was sufficient to navigate without notes.
364
+
365
+ ---
366
+
367
+ ## Built with
368
+
369
+ Python 3.14 · FastAPI · sentence-transformers · tree-sitter · ChromaDB · BM25 · Docker
370
+
371
+ ## Author
372
+
373
+ Swapnanil Saha · [swapnanilsaha.com](https://swapnanilsaha.com)
@@ -70,7 +70,7 @@ The research phase (paid once to build notes) costs more for vectr (+94%) becaus
70
70
 
71
71
  Earlier runs on Apache Camel (Java, 5,856 files): **−58% impl cost · −72% impl tool calls · −39% wall time.**
72
72
 
73
- Full results: [`benchmarks/`](benchmarks/)
73
+ Full results: [`benchmarks/`](https://github.com/swapnanil/vectr/tree/main/benchmarks)
74
74
 
75
75
  ---
76
76
 
@@ -100,7 +100,7 @@ Fine print: the automatic eviction/reminder banners riding along on tool respons
100
100
  ```bash
101
101
  python3.14 -m venv ~/.vectr-env
102
102
  source ~/.vectr-env/bin/activate # Windows: ~/.vectr-env/Scripts/activate
103
- pip install git+https://github.com/swapnanil/vectr
103
+ pip install vectr
104
104
  cd /path/to/your/project
105
105
  vectr start
106
106
  ```
@@ -56,7 +56,7 @@ app = FastAPI(
56
56
  "(search/locate/trace/map) plus a working-memory store (remember/recall/"
57
57
  "snapshot) that survives context compaction and session boundaries."
58
58
  ),
59
- version="1.0.0",
59
+ version="1.0.2",
60
60
  lifespan=lifespan,
61
61
  )
62
62
 
@@ -58,7 +58,6 @@ class SearchResponse(BaseModel):
58
58
  query_time_ms: int
59
59
  chunks_searched: int
60
60
  processing_ms: int
61
- model: str
62
61
  # UPG-NOTFOUND-FLOOR (F46/F52): true when the query names a concept that
63
62
  # has no lexical anchor anywhere in the indexed corpus — at least
64
63
  # `ranking.notfound_floor.min_zero_df_tokens` of its content words have
@@ -75,7 +74,6 @@ class IndexResponse(BaseModel):
75
74
  indexed_files: int
76
75
  total_chunks: int
77
76
  processing_ms: int
78
- model: str
79
77
 
80
78
 
81
79
  class FetchRequest(BaseModel):
@@ -131,7 +129,6 @@ class StatusResponse(BaseModel):
131
129
  symbol_graph_complete: bool = False
132
130
  symbol_graph_failed_files: int = 0
133
131
  processing_ms: int
134
- model: str
135
132
  # Adaptive retrieval strategy. Always populated by VectrService.status()
136
133
  # (UPG-8.2): the config-declared defaults before the first index-time
137
134
  # fingerprint, the fingerprint-derived values after. Optional here only
@@ -174,7 +171,6 @@ class StatusResponse(BaseModel):
174
171
 
175
172
  class HealthResponse(BaseModel):
176
173
  status: str
177
- model: str
178
174
  embed_model: str
179
175
  # Sourced from the same VectrService.last_indexed property as
180
176
  # /v1/status so the two endpoints never disagree on freshness (UPG-8.2).
@@ -6,7 +6,6 @@ import uuid
6
6
 
7
7
  from fastapi import APIRouter, Body, HTTPException, Request, Response
8
8
 
9
- from agent.llm_client import get_model
10
9
  from app.models import (
11
10
  FetchEntry,
12
11
  FetchRequest,
@@ -55,7 +54,6 @@ async def health(request: Request) -> HealthResponse:
55
54
  svc = _service(request)
56
55
  return HealthResponse(
57
56
  status="ok",
58
- model=get_model(),
59
57
  embed_model=svc._embed_model,
60
58
  last_indexed=svc.last_indexed,
61
59
  )
@@ -75,7 +73,6 @@ async def index(body: IndexRequest, request: Request) -> IndexResponse:
75
73
  indexed_files=files,
76
74
  total_chunks=chunks,
77
75
  processing_ms=elapsed,
78
- model=get_model(),
79
76
  )
80
77
 
81
78
 
@@ -109,7 +106,6 @@ async def search(body: SearchRequest, request: Request) -> SearchResponse:
109
106
  query_time_ms=query_ms,
110
107
  chunks_searched=svc.total_chunks,
111
108
  processing_ms=int((time.monotonic() - t0) * 1000),
112
- model=get_model(),
113
109
  low_confidence=getattr(results, "low_confidence", False),
114
110
  )
115
111
 
@@ -158,7 +154,6 @@ async def status(request: Request) -> StatusResponse:
158
154
  return StatusResponse(
159
155
  **data,
160
156
  processing_ms=int((time.monotonic() - t0) * 1000),
161
- model=get_model(),
162
157
  )
163
158
 
164
159
 
@@ -15,8 +15,11 @@ agent = ["config.yaml", "data/*.txt", "templates/*"]
15
15
 
16
16
  [project]
17
17
  name = "vectr"
18
- version = "1.0.0"
19
- description = "Zero-config local semantic codebase indexer with MCP protocol"
18
+ version = "1.0.2"
19
+ description = "Semantic codebase search + persistent working memory for AI code editors. Local, zero-config, MCP. No API key."
20
+ readme = "README.md"
21
+ license = { text = "MIT" }
22
+ authors = [{ name = "Swapnanil Saha", email = "swapnanilsaha26@gmail.com" }]
20
23
  requires-python = ">=3.14"
21
24
  dependencies = [
22
25
  "fastapi==0.136.3",
@@ -57,5 +60,11 @@ dev = [
57
60
  "anyio==4.13.0",
58
61
  ]
59
62
 
63
+ [project.urls]
64
+ Homepage = "https://swapnanilsaha.com/tools/vectr"
65
+ Repository = "https://github.com/swapnanil/vectr"
66
+ Changelog = "https://github.com/swapnanil/vectr/blob/main/CHANGELOG.md"
67
+ Issues = "https://github.com/swapnanil/vectr/issues"
68
+
60
69
  [project.scripts]
61
70
  vectr = "main:main"