code-context-control 2.145.1__py3-none-any.whl → 2.145.2__py3-none-any.whl

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.
cli/c3.py CHANGED
@@ -91,7 +91,7 @@ console = Console() if HAS_RICH else None
91
91
  # Config
92
92
  CONFIG_DIR = ".c3"
93
93
  CONFIG_FILE = ".c3/config.json"
94
- __version__ = "2.145.1"
94
+ __version__ = "2.145.2"
95
95
 
96
96
  # The PreToolUse matcher for native subagent calls (installer and hub migration).
97
97
  AGENT_MATCHER = "Agent|Task"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.145.1
3
+ Version: 2.145.2
4
4
  Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
5
5
  Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -1,7 +1,7 @@
1
1
  cli/__init__.py,sha256=ec66drCZGNMRU4V6ov0zVhYZph1us12Vn8OvG_LJyRY,22
2
2
  cli/_hook_utils.py,sha256=RhfS2WS5VMSi2w2Jxcts1wg5WC-UFp89JOQvcO5Jjb8,23002
3
3
  cli/_shell_writes.py,sha256=XzUNlUDljmg-wZxK03xHnRe36WeQxe4tpO9HKVFwP5o,5090
4
- cli/c3.py,sha256=C5K99R5PwQd7wQv2yzifv7gMib8fYaMeWSeROUa4WJ0,431828
4
+ cli/c3.py,sha256=Thn44zRVOuNqg22oafbPECYSXzQCBYZycF377xu1zIE,431828
5
5
  cli/docs.html,sha256=pPxcP5ieY0DcfEDvegwIcRxbz1woLZoKLVSF-4JVy0Q,146784
6
6
  cli/edits.html,sha256=UjAhoCmBmQ89cklGvJqzC6eyNP2tc8H6T-e01DVkLvE,43418
7
7
  cli/hook_access_guard.py,sha256=Mjrrt3agYCWPqSjdzk_Sw4ooMrizO8Um-nQjgGfXck4,27896
@@ -131,7 +131,7 @@ cli/ui/components/sessions.js,sha256=FIKtil76B8tCkAmcFV7hlj6GQ_DCJK2jCzvEmdK7NBE
131
131
  cli/ui/components/settings.js,sha256=FXcn-UQE2mXC6HFsLeEas-wAUFKt0OJaLySroZQMhGA,84095
132
132
  cli/ui/components/sidebar.js,sha256=K2ym2kUgpbyG-EBA_wBIIiqQY8qTatsiBZwzseMWuIQ,9939
133
133
  cli/ui/components/tasks.js,sha256=vyKQ3uwoppMwvdEaHlhWXW4oWcAisx4NveqzMhsYqHo,38438
134
- code_context_control-2.145.1.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
134
+ code_context_control-2.145.2.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
135
135
  core/__init__.py,sha256=TSDCEcM4V7gcZVM3w2ykJaqEUch4Dkon-rivV17T73s,2501
136
136
  core/codex_patch.py,sha256=CxvT_rQIwPNTRJZj9EHJmx0ZdOeSLxjiixuLI3Z7lPc,2107
137
137
  core/config.py,sha256=wCjrZKXRywUbR6WRcO7optyOirAQ2DeRNdPCU0X5zIY,23762
@@ -233,7 +233,7 @@ services/e2e_benchmark.py,sha256=5Z3rE42o08MKVbIX_769V0RX2yAzSVcApCvdAQ9YLAU,134
233
233
  services/e2e_evaluator.py,sha256=FsB4vMzcFUcmicfIwRtA8JZ_XeMpuk2_-86F2iVNEDc,17783
234
234
  services/e2e_tasks.py,sha256=Ln5VbGDIcS3NY3aml5ERbgjfjLxEslmRZ8AyaYxpWEo,34524
235
235
  services/edit_ledger.py,sha256=Tj8Q14o19VHQHpofRHdbZKKcVs17v-ctZ3LOeVTqvdQ,29362
236
- services/embedding_index.py,sha256=c85kAkekL7r-liiFEwqi7nJAQr4ZbJA36h0oMdiTurc,25156
236
+ services/embedding_index.py,sha256=wqq7odpGzAxyO-lxr6N1WusyEGInGgEQUjuEw445Bxs,27711
237
237
  services/enforcement_policy.py,sha256=znjABRB-8TSl0ELazY2ALzux2YJ5nWf8TvxLRhDsL2w,18905
238
238
  services/error_reporting.py,sha256=HZ3ru8i5RLf8nq2R4iRnTs5sm1blUxknSbv5hdxuxs0,4139
239
239
  services/file_map.py,sha256=j37DTR-UE_PMNj7vG2HiHucpwpJNaKgiD94UNUU-UXE,17091
@@ -333,8 +333,8 @@ tui/screens/search_view.py,sha256=MMHjVdlk3HZSuDBSvq8IGrqv_Mh5Us6YqXQ80bcWSMk,19
333
333
  tui/screens/session_view.py,sha256=eZ1eDwHTvPOck1wCCviixtOaCxIkBT_95ytNNNriGNA,5991
334
334
  tui/screens/stats.py,sha256=p81PjzdaIv7hllb8f45-rlVe4lJZwSdIMqu7e86_u5s,6223
335
335
  tui/screens/ui_view.py,sha256=1QJCgLh2YfgWIpvzRG1KOGXYEaOYX6ojN61Azjf2oX0,2125
336
- code_context_control-2.145.1.dist-info/METADATA,sha256=yNqmbGJ2m9zH8AyXL1O1x7sGU5hGQV2KmafCGkSRGsc,28902
337
- code_context_control-2.145.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
338
- code_context_control-2.145.1.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
339
- code_context_control-2.145.1.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
340
- code_context_control-2.145.1.dist-info/RECORD,,
336
+ code_context_control-2.145.2.dist-info/METADATA,sha256=bHhi9gHWwyOe6v9kxLBSGBFgB_jEEtfWVQomsR91rss,28902
337
+ code_context_control-2.145.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
338
+ code_context_control-2.145.2.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
339
+ code_context_control-2.145.2.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
340
+ code_context_control-2.145.2.dist-info/RECORD,,
@@ -6,10 +6,13 @@ embeddings. Tracks file content hashes to only re-embed changed files.
6
6
  Falls back gracefully when Ollama or chromadb are unavailable.
7
7
  """
8
8
 
9
+ import gc
9
10
  import hashlib
10
11
  import json
11
12
  import logging
13
+ import shutil
12
14
  import threading
15
+ from datetime import datetime
13
16
  from pathlib import Path
14
17
 
15
18
  log = logging.getLogger("c3.embedding_index")
@@ -106,19 +109,7 @@ class EmbeddingIndex:
106
109
  def _init_backends(self):
107
110
  """Initialize chromadb collection and check Ollama."""
108
111
  try:
109
- import chromadb
110
- from chromadb.config import Settings
111
-
112
- persist_dir = str(self._index_dir / "chromadb")
113
- Path(persist_dir).mkdir(parents=True, exist_ok=True)
114
- self._chroma_client = chromadb.PersistentClient(
115
- path=persist_dir,
116
- settings=Settings(anonymized_telemetry=False),
117
- )
118
- self._collection = self._chroma_client.get_or_create_collection(
119
- name=COLLECTION_NAME,
120
- metadata={"hnsw:space": "cosine"},
121
- )
112
+ self._open_chroma()
122
113
  self._available = True
123
114
  self._drop_legacy_collection()
124
115
  except Exception as e:
@@ -135,6 +126,78 @@ class EmbeddingIndex:
135
126
  self._model_ok = False
136
127
  self._ollama_ok = self._ollama_up and self._model_ok
137
128
 
129
+ def _open_chroma(self, _retried: bool = False) -> None:
130
+ """Open the persistent collection, quarantining a corrupt store once.
131
+
132
+ A damaged HNSW segment does not surface at open time: the Rust backend
133
+ accepts the client and the collection handle, then faults on the first
134
+ real read. When that read happens on the background index thread it has
135
+ taken the whole MCP process down with an access violation, which the
136
+ host reports only as a dead server (seen 2026-07-17, 2026-09-06).
137
+
138
+ So probe here, on the init path, where the failure is still a catchable
139
+ Python exception, and treat a failed probe as a corrupt store: close the
140
+ client, move the directory aside, and reopen empty. The store is a
141
+ rebuildable cache, so the cost is one re-embed, not lost data.
142
+ """
143
+ import chromadb
144
+ from chromadb.config import Settings
145
+
146
+ persist_dir = self._index_dir / "chromadb"
147
+ persist_dir.mkdir(parents=True, exist_ok=True)
148
+ self._chroma_client = chromadb.PersistentClient(
149
+ path=str(persist_dir),
150
+ settings=Settings(anonymized_telemetry=False),
151
+ )
152
+ self._collection = self._chroma_client.get_or_create_collection(
153
+ name=COLLECTION_NAME,
154
+ metadata={"hnsw:space": "cosine"},
155
+ )
156
+ try:
157
+ self._collection.count()
158
+ except Exception as e:
159
+ if _retried:
160
+ raise
161
+ log.warning(
162
+ "embedding store failed its health probe (%s); "
163
+ "quarantining and rebuilding",
164
+ e,
165
+ )
166
+ self._quarantine_store(persist_dir)
167
+ self._open_chroma(_retried=True)
168
+
169
+ def _close_chroma(self) -> None:
170
+ """Drop the client so Windows releases its handle on chroma.sqlite3."""
171
+ client = self._chroma_client
172
+ self._collection = None
173
+ self._chroma_client = None
174
+ try:
175
+ close = getattr(client, "close", None)
176
+ if callable(close):
177
+ close()
178
+ except Exception:
179
+ pass
180
+ gc.collect()
181
+
182
+ def _quarantine_store(self, persist_dir: Path) -> None:
183
+ """Move a corrupt store and its hash file aside for post-mortem.
184
+
185
+ The hashes go with it: they claim vectors that the fresh store does not
186
+ have, so leaving them behind would suppress the very rebuild this is
187
+ meant to trigger.
188
+ """
189
+ self._close_chroma()
190
+ dest = self._index_dir / f"quarantine_corrupt_{datetime.now():%Y%m%d_%H%M%S}"
191
+ dest.mkdir(parents=True, exist_ok=True)
192
+ shutil.move(str(persist_dir), str(dest / persist_dir.name))
193
+ try:
194
+ if self._hash_file.exists():
195
+ shutil.move(str(self._hash_file), str(dest / self._hash_file.name))
196
+ except OSError:
197
+ self._hash_file.unlink(missing_ok=True)
198
+ self._file_hashes = {}
199
+ log.warning("quarantined corrupt embedding store to %s", dest)
200
+
138
201
  def _drop_legacy_collection(self) -> None:
139
202
  """Best-effort removal of the pre-2.108.0 collection and hash file.
140
203