code-context-control 2.76.2__py3-none-any.whl → 2.76.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.76.2"
95
+ __version__ = "2.76.3"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
cli/hook_access_guard.py CHANGED
@@ -84,6 +84,15 @@ _DOUBLE_COLON_RE = re.compile(r"::(?!\$)")
84
84
  _GIT_REVSPEC_RE = re.compile(r"^(?P<rev>[^:\s]+):(?P<path>[^:\s]+)$")
85
85
  _GIT_CMD_RE = re.compile(r"^\s*(?:[\w.\-]*[/\\])?git(?:\.exe)?\b", re.IGNORECASE)
86
86
 
87
+ # Where one command ends and the next begins. Anchoring "is this git?" to the
88
+ # start of the whole string was wrong in the most ordinary way possible:
89
+ # `cd /repo && git show origin/main:pyproject.toml` starts with `cd`, so the
90
+ # revspec rewrite never fired for the shape people actually type. Caught by a
91
+ # live probe after release, not by the unit tests — which passed the command as
92
+ # `git show …` because that is how the test author writes it, not how a shell
93
+ # call arrives.
94
+ _SEGMENT_SPLIT = re.compile(r"&&|\|\||[;|\n]")
95
+
87
96
 
88
97
  def _is_network_token(tok: str) -> bool:
89
98
  """True for URLs and IPv6 literals/CIDRs — never for an ADS spelling."""
@@ -201,44 +210,58 @@ def _scan_shell(cmd: str, base: str):
201
210
  outright, as are URLs and IPv6 literals: all of them trip the ADS spelling
202
211
  check, which is exempt from existence-gating, so a token naming nothing on
203
212
  disk would otherwise hard-deny (#50).
213
+
214
+ Scanning is per **command segment**, not per whole string. A segment is what
215
+ sits between `&&`, `||`, `;`, `|` or a newline, and each one answers "am I a
216
+ git command?" for itself — so the revspec rewrite applies to the tokens of
217
+ the git segment and to no others. `cat notes.txt:hidden && git status` must
218
+ not have its first token reinterpreted just because a later segment is git.
204
219
  """
205
- for raw in _TOKEN_SPLIT.split(cmd)[:_MAX_TOKENS]:
206
- tok = raw.strip("\"'`;,()")
207
- if not tok or tok.startswith("-"):
208
- continue
209
- if "/" not in tok and "\\" not in tok and not tok.startswith("."):
210
- continue
211
- # Order matters only for readability: a URL is also syntax-free, so
212
- # either check alone would skip 'https://x'. Both are kept because they
213
- # answer different questions "is this a network literal" and "is this
214
- # a path at all" — and the second is the one that generalizes.
215
- if not _looks_like_a_path(tok) or _is_network_token(tok):
216
- continue
217
- # `tests/x.py::TestThing` is a node id, not a stream spelling.
218
- if _is_scope_token(tok):
219
- continue
220
- # `git show <rev>:<path>`judge the PATH, not the whole token. This
221
- # is the one place the scan rewrites what it checks rather than skipping
222
- # it, because skipping would let `git show HEAD:.env` through.
223
- revspec_path = _git_revspec_path(cmd, tok)
224
- from_revspec = revspec_path is not None
225
- if from_revspec:
226
- tok = revspec_path
227
- if re.match(r"^/[a-z]/", tok): # MSYS /c/foo → C:/foo
228
- tok = f"{tok[1]}:{tok[2:]}"
229
- denial = ag.check(tok, "read", base)
230
- if denial and denial.kind == "deny":
231
- try:
232
- p = Path(tok)
233
- exists = (p if p.is_absolute() else Path(base) / p).exists()
234
- except OSError:
235
- exists = False
236
- # A revspec names a path in HISTORY, so the working tree is the
237
- # wrong place to ask whether it is real. `git show HEAD~5:.env`
238
- # reads a denied file whether or not that file exists today, and
239
- # existence-gating it would be a hole this rewrite opened.
240
- if exists or from_revspec or denial.rule.startswith("<"):
241
- return denial, tok
220
+ budget = _MAX_TOKENS
221
+ for segment in _SEGMENT_SPLIT.split(cmd or ""):
222
+ if budget <= 0:
223
+ break
224
+ is_git = bool(_GIT_CMD_RE.match(segment))
225
+ for raw in _TOKEN_SPLIT.split(segment)[:budget]:
226
+ budget -= 1
227
+ tok = raw.strip("\"'`;,()")
228
+ if not tok or tok.startswith("-"):
229
+ continue
230
+ if "/" not in tok and "\\" not in tok and not tok.startswith("."):
231
+ continue
232
+ # Order matters only for readability: a URL is also syntax-free, so
233
+ # either check alone would skip 'https://x'. Both are kept because
234
+ # they answer different questions — "is this a network literal" and
235
+ # "is this a path at all" and the second is the one that
236
+ # generalizes.
237
+ if not _looks_like_a_path(tok) or _is_network_token(tok):
238
+ continue
239
+ # `tests/x.py::TestThing` is a node id, not a stream spelling.
240
+ if _is_scope_token(tok):
241
+ continue
242
+ # `git show <rev>:<path>` — judge the PATH, not the whole token.
243
+ # This is the one place the scan rewrites what it checks rather than
244
+ # skipping it, because skipping would let `git show HEAD:.env`
245
+ # through.
246
+ revspec_path = _git_revspec_path(segment, tok) if is_git else None
247
+ from_revspec = revspec_path is not None
248
+ if from_revspec:
249
+ tok = revspec_path
250
+ if re.match(r"^/[a-z]/", tok): # MSYS /c/foo → C:/foo
251
+ tok = f"{tok[1]}:{tok[2:]}"
252
+ denial = ag.check(tok, "read", base)
253
+ if denial and denial.kind == "deny":
254
+ try:
255
+ p = Path(tok)
256
+ exists = (p if p.is_absolute() else Path(base) / p).exists()
257
+ except OSError:
258
+ exists = False
259
+ # A revspec names a path in HISTORY, so the working tree is the
260
+ # wrong place to ask whether it is real. `git show HEAD~5:.env`
261
+ # reads a denied file whether or not that file exists today, and
262
+ # existence-gating it would be a hole this rewrite opened.
263
+ if exists or from_revspec or denial.rule.startswith("<"):
264
+ return denial, tok
242
265
  return None, ""
243
266
 
244
267
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.76.2
3
+ Version: 2.76.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,9 +1,9 @@
1
1
  cli/__init__.py,sha256=ec66drCZGNMRU4V6ov0zVhYZph1us12Vn8OvG_LJyRY,22
2
2
  cli/_hook_utils.py,sha256=03fDyLvfzF832M7L5B2DgR8KMWXIObpbYxSC3LbhQ04,17229
3
- cli/c3.py,sha256=H7frUbcXyG6sIUrnKfVVpvFhH6t5SRi7J0EKwZVAErU,368519
3
+ cli/c3.py,sha256=5sN37hJx2S9IJDyiWBb-NIfgQ02dPUxWSdaE1svI3GM,368519
4
4
  cli/docs.html,sha256=bNymSz7LatnWHjxSXL92QSUtGvB3jBzNHbOV-bRpAOo,142507
5
5
  cli/edits.html,sha256=UjAhoCmBmQ89cklGvJqzC6eyNP2tc8H6T-e01DVkLvE,43418
6
- cli/hook_access_guard.py,sha256=f7LywupKKDkCNdyZd5s4C8inGvOc0qp68HyLNLo301w,12800
6
+ cli/hook_access_guard.py,sha256=msFfKCMMYZoF_2uDvMHWstFwoED1-yIhMuXZSz9dxmU,14128
7
7
  cli/hook_artifact.py,sha256=Se1CNBfoBFyvJQlRmYdNtdRXIkQp9zaF0O6ndRMo7ts,2198
8
8
  cli/hook_auto_snapshot.py,sha256=yGFqRANtw8qNqRNr-oCgpuyaUyTkSzIOFxXt5kauq4I,5271
9
9
  cli/hook_c3_signal.py,sha256=FPvs-9nD9ZiKA8r2tmusgj79yT9w9F173cRDKn-PYeI,2149
@@ -112,7 +112,7 @@ cli/ui/components/sessions.js,sha256=FIKtil76B8tCkAmcFV7hlj6GQ_DCJK2jCzvEmdK7NBE
112
112
  cli/ui/components/settings.js,sha256=ATbAjBlVIwCNpxq7s191b49a_INQV38iwmySqtJLYwY,79066
113
113
  cli/ui/components/sidebar.js,sha256=K2ym2kUgpbyG-EBA_wBIIiqQY8qTatsiBZwzseMWuIQ,9939
114
114
  cli/ui/components/tasks.js,sha256=vyKQ3uwoppMwvdEaHlhWXW4oWcAisx4NveqzMhsYqHo,38438
115
- code_context_control-2.76.2.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
115
+ code_context_control-2.76.3.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
116
116
  core/__init__.py,sha256=TSDCEcM4V7gcZVM3w2ykJaqEUch4Dkon-rivV17T73s,2501
117
117
  core/config.py,sha256=YmkcZwedz_lfDM0ZuI_f4xpe98ixPLcSQFcTCHgRiRg,19206
118
118
  core/ide.py,sha256=V6VVMVsFdmmcsMyxikjQp7z9xa42CWiHKS-ya-MAcG4,6172
@@ -265,8 +265,8 @@ tui/screens/search_view.py,sha256=MMHjVdlk3HZSuDBSvq8IGrqv_Mh5Us6YqXQ80bcWSMk,19
265
265
  tui/screens/session_view.py,sha256=eZ1eDwHTvPOck1wCCviixtOaCxIkBT_95ytNNNriGNA,5991
266
266
  tui/screens/stats.py,sha256=p81PjzdaIv7hllb8f45-rlVe4lJZwSdIMqu7e86_u5s,6223
267
267
  tui/screens/ui_view.py,sha256=1QJCgLh2YfgWIpvzRG1KOGXYEaOYX6ojN61Azjf2oX0,2125
268
- code_context_control-2.76.2.dist-info/METADATA,sha256=XL1W9TMmDxlqeZvl4NFBBtejLyZ_IJDNGIIPtRv5B94,25648
269
- code_context_control-2.76.2.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
270
- code_context_control-2.76.2.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
271
- code_context_control-2.76.2.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
272
- code_context_control-2.76.2.dist-info/RECORD,,
268
+ code_context_control-2.76.3.dist-info/METADATA,sha256=P1LnXiW2UONXBzI6PQOEfWdZe3nk4NOMYSoYqOHSgxg,25648
269
+ code_context_control-2.76.3.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
270
+ code_context_control-2.76.3.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
271
+ code_context_control-2.76.3.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
272
+ code_context_control-2.76.3.dist-info/RECORD,,