code-context-control 2.92.2__py3-none-any.whl → 2.92.3__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
@@ -92,7 +92,7 @@ console = Console() if HAS_RICH else None
92
92
  # Config
93
93
  CONFIG_DIR = ".c3"
94
94
  CONFIG_FILE = ".c3/config.json"
95
- __version__ = "2.92.2"
95
+ __version__ = "2.92.3"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.92.2
3
+ Version: 2.92.3
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=lxYVHSqK4FTiFU2avXW2hbOheMbaaZhnAZqqwqtrZHI,20862
3
3
  cli/_shell_writes.py,sha256=XzUNlUDljmg-wZxK03xHnRe36WeQxe4tpO9HKVFwP5o,5090
4
- cli/c3.py,sha256=G4_iBSVNkNBuPUgKBmyK1z3K84wm_j7n7pSgtgUqDGw,390431
4
+ cli/c3.py,sha256=_DNAo4tAgIsq8d-XPXELnqBk9bL03qQaXa-_u8A8gnU,390431
5
5
  cli/docs.html,sha256=X9M7qC7LWi8pPJNF-4yEM57jhuZ3WeTESECbdDOeNaA,143172
6
6
  cli/edits.html,sha256=UjAhoCmBmQ89cklGvJqzC6eyNP2tc8H6T-e01DVkLvE,43418
7
7
  cli/hook_access_guard.py,sha256=4SIX-A4-tlXcOpzV4k5PZvyXibRvw9buqAV1-_doJpg,17200
@@ -115,7 +115,7 @@ cli/ui/components/sessions.js,sha256=FIKtil76B8tCkAmcFV7hlj6GQ_DCJK2jCzvEmdK7NBE
115
115
  cli/ui/components/settings.js,sha256=ATbAjBlVIwCNpxq7s191b49a_INQV38iwmySqtJLYwY,79066
116
116
  cli/ui/components/sidebar.js,sha256=K2ym2kUgpbyG-EBA_wBIIiqQY8qTatsiBZwzseMWuIQ,9939
117
117
  cli/ui/components/tasks.js,sha256=vyKQ3uwoppMwvdEaHlhWXW4oWcAisx4NveqzMhsYqHo,38438
118
- code_context_control-2.92.2.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
118
+ code_context_control-2.92.3.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
119
119
  core/__init__.py,sha256=TSDCEcM4V7gcZVM3w2ykJaqEUch4Dkon-rivV17T73s,2501
120
120
  core/config.py,sha256=YmkcZwedz_lfDM0ZuI_f4xpe98ixPLcSQFcTCHgRiRg,19206
121
121
  core/ide.py,sha256=V6VVMVsFdmmcsMyxikjQp7z9xa42CWiHKS-ya-MAcG4,6172
@@ -206,7 +206,7 @@ services/error_reporting.py,sha256=HZ3ru8i5RLf8nq2R4iRnTs5sm1blUxknSbv5hdxuxs0,4
206
206
  services/file_memory.py,sha256=vUvgBBdFC_rvCp9GXfvfojQIr6iSHWTRFrzWG5e0kHs,35048
207
207
  services/git_context.py,sha256=FFdA8Y46op4DYsUnZsXLTqxIjgxb0q_d-hkFTbosC8Q,9518
208
208
  services/hub_service.py,sha256=z2MZ2Rg9NLNXxQ17e05XGF_f6qSTDWLYtoC3WpuQfGU,2634
209
- services/indexer.py,sha256=Dme1bqX7NDZjmLDWcc1pbnIeLC5jaKUVQ7k-uMJy-ls,31216
209
+ services/indexer.py,sha256=sODQ7kV1dmLAuMVJTnd9JpSaPJsq2eNPIqGziM9sr3g,32963
210
210
  services/jira_client.py,sha256=blh2LhjHrWL3OUHMS_ab7JguQqtBbx66m5UYZz7sbXQ,18935
211
211
  services/jira_cloud.py,sha256=vOMQxwEuPa-FcLgmch-to7feiPkGrXXjUf9mNgpV4VE,12656
212
212
  services/jira_credentials.py,sha256=QmODZa2ivZ1DwsxSpnmM1XhPxNBiTJCR1LrdCPh7Nuk,9124
@@ -281,8 +281,8 @@ tui/screens/search_view.py,sha256=MMHjVdlk3HZSuDBSvq8IGrqv_Mh5Us6YqXQ80bcWSMk,19
281
281
  tui/screens/session_view.py,sha256=eZ1eDwHTvPOck1wCCviixtOaCxIkBT_95ytNNNriGNA,5991
282
282
  tui/screens/stats.py,sha256=p81PjzdaIv7hllb8f45-rlVe4lJZwSdIMqu7e86_u5s,6223
283
283
  tui/screens/ui_view.py,sha256=1QJCgLh2YfgWIpvzRG1KOGXYEaOYX6ojN61Azjf2oX0,2125
284
- code_context_control-2.92.2.dist-info/METADATA,sha256=c9FOYVgbXn3kqLd4hyYYcFs5oz_ZaLYIhL8MHBZQvNc,26368
285
- code_context_control-2.92.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
286
- code_context_control-2.92.2.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
287
- code_context_control-2.92.2.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
288
- code_context_control-2.92.2.dist-info/RECORD,,
284
+ code_context_control-2.92.3.dist-info/METADATA,sha256=69oubv8g3Xd1SiRr5Dcqn5RFP_ot3WvUF1FucgTOwp4,26368
285
+ code_context_control-2.92.3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
286
+ code_context_control-2.92.3.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
287
+ code_context_control-2.92.3.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
288
+ code_context_control-2.92.3.dist-info/RECORD,,
services/indexer.py CHANGED
@@ -37,6 +37,20 @@ def _masked_content(fpath, project_path):
37
37
  return None
38
38
 
39
39
 
40
+ # Co-occurrence is O(unique_tokens^2) per chunk, so one minified vendor
41
+ # bundle can dominate a whole index build. Both bounds below are deliberate
42
+ # and their effect is REPORTED in build_index()'s stats rather than applied
43
+ # silently — a capped build must never look like a complete one.
44
+ #
45
+ # 200: chunks above this are minified/bundled/data blobs, not code. The
46
+ # pruning step keeps only the top-5 co-occurring terms per token with at
47
+ # least 3 hits, so such a chunk contributes noise that is discarded anyway.
48
+ _COOC_MAX_CHUNK_TOKENS = 200
49
+ # 20M: whole-pass ceiling on pair updates, so no repository shape can make
50
+ # this step unbounded. ~10s of CPU on the boxes this runs on.
51
+ _COOC_MAX_PAIR_UPDATES = 20_000_000
52
+
53
+
40
54
  class CodeIndex:
41
55
  """TF-IDF based code search index with structural awareness."""
42
56
 
@@ -240,6 +254,7 @@ class CodeIndex:
240
254
  "entries_scanned": scan_stats["entries"],
241
255
  "files_capped": files_capped,
242
256
  "max_files": max_files,
257
+ "cooccurrence": getattr(self, "_cooccurrence_stats", {}),
243
258
  }
244
259
 
245
260
  def _chunk_file(self, content: str, ext: str, doc_id: str) -> list:
@@ -625,16 +640,39 @@ class CodeIndex:
625
640
  self.chunk_tfidf[chunk_id][term] = normalized_tf * self.idf.get(term, 0)
626
641
 
627
642
  def _build_cooccurrence(self):
628
- """Build lightweight co-occurrence map from indexed chunks for auto-synonyms."""
643
+ """Build lightweight co-occurrence map from indexed chunks for auto-synonyms.
644
+
645
+ Bounded on purpose: the pair loop is quadratic in a chunk's unique
646
+ token count, so a single minified bundle (798 unique tokens x 4091
647
+ chunks = 1.9e9 pair updates, measured on a real project) can hang a
648
+ build that has nothing else wrong with it. Skipped chunks and an
649
+ exhausted budget are recorded in ``self._cooccurrence_stats`` and
650
+ returned by :meth:`build_index`.
651
+ """
629
652
  self._cooccurrence = {}
653
+ skipped = 0
654
+ updates = 0
655
+ budget_hit = False
630
656
  for chunk in self.chunks.values():
631
657
  tokens = set(self._tokenize(chunk["content"]))
658
+ if len(tokens) > _COOC_MAX_CHUNK_TOKENS:
659
+ skipped += 1
660
+ continue
661
+ if updates + len(tokens) * (len(tokens) - 1) > _COOC_MAX_PAIR_UPDATES:
662
+ budget_hit = True
663
+ break
632
664
  for t in tokens:
633
665
  if t not in self._cooccurrence:
634
666
  self._cooccurrence[t] = Counter()
635
667
  for t2 in tokens:
636
668
  if t != t2:
637
669
  self._cooccurrence[t][t2] += 1
670
+ updates += len(tokens) * (len(tokens) - 1)
671
+ self._cooccurrence_stats = {
672
+ "chunks_skipped": skipped,
673
+ "pair_updates": updates,
674
+ "budget_exhausted": budget_hit,
675
+ }
638
676
  # Prune: keep only top-5 co-occurring terms per token (minimum 3 co-occurrences)
639
677
  pruned = {}
640
678
  for term, counts in self._cooccurrence.items():