code-context-control 2.76.3__py3-none-any.whl → 2.77.0__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.3"
95
+ __version__ = "2.77.0"
96
96
 
97
97
 
98
98
  def _compress_file_cli(compressor, path, mode="smart", **kw):
cli/hook_access_guard.py CHANGED
@@ -10,6 +10,7 @@ Bash coverage is a best-effort, existence-gated token scan — advisory by
10
10
  design (docs/access-guard.md §3/§6); it catches an agent naming a denied
11
11
  path, not an adversary hiding one.
12
12
  """
13
+ import os
13
14
  import re
14
15
  import sys
15
16
  from pathlib import Path
@@ -93,6 +94,14 @@ _GIT_CMD_RE = re.compile(r"^\s*(?:[\w.\-]*[/\\])?git(?:\.exe)?\b", re.IGNORECASE
93
94
  # call arrives.
94
95
  _SEGMENT_SPLIT = re.compile(r"&&|\|\||[;|\n]")
95
96
 
97
+ # `cd <dir>` — the segment that moves the ground under every later one. Handles
98
+ # a quoted target, since project paths on this platform contain spaces, and the
99
+ # Windows `/d` flag.
100
+ _CD_RE = re.compile(
101
+ r"""^\s*cd\s+(?:/d\s+)?(?P<dir>"[^"]+"|'[^']+'|\S+)\s*$""",
102
+ re.IGNORECASE,
103
+ )
104
+
96
105
 
97
106
  def _is_network_token(tok: str) -> bool:
98
107
  """True for URLs and IPv6 literals/CIDRs — never for an ADS spelling."""
@@ -199,6 +208,20 @@ def _target(tool_input: dict) -> str:
199
208
  )
200
209
 
201
210
 
211
+ def _cd_target(segment: str, cwd: str) -> str | None:
212
+ """The directory a `cd` segment moves to, resolved against ``cwd``."""
213
+ m = _CD_RE.match(segment or "")
214
+ if not m:
215
+ return None
216
+ target = m.group("dir").strip("\"'")
217
+ if not target or target.startswith("-"):
218
+ return None
219
+ try:
220
+ return os.path.abspath(os.path.join(cwd, os.path.expanduser(target)))
221
+ except (OSError, ValueError):
222
+ return None
223
+
224
+
202
225
  def _scan_shell(cmd: str, base: str):
203
226
  """(denial, token) for the first confident deny-rule hit, else (None, '').
204
227
 
@@ -218,6 +241,7 @@ def _scan_shell(cmd: str, base: str):
218
241
  not have its first token reinterpreted just because a later segment is git.
219
242
  """
220
243
  budget = _MAX_TOKENS
244
+ cwd = base
221
245
  for segment in _SEGMENT_SPLIT.split(cmd or ""):
222
246
  if budget <= 0:
223
247
  break
@@ -227,7 +251,10 @@ def _scan_shell(cmd: str, base: str):
227
251
  tok = raw.strip("\"'`;,()")
228
252
  if not tok or tok.startswith("-"):
229
253
  continue
230
- if "/" not in tok and "\\" not in tok and not tok.startswith("."):
254
+ # A revspec's path half need not contain a separator — `HEAD:.env`
255
+ # has none, so it used to be dropped here before any rule saw it.
256
+ if ("/" not in tok and "\\" not in tok and not tok.startswith(".")
257
+ and not (is_git and _GIT_REVSPEC_RE.match(tok))):
231
258
  continue
232
259
  # Order matters only for readability: a URL is also syntax-free, so
233
260
  # either check alone would skip 'https://x'. Both are kept because
@@ -249,11 +276,22 @@ def _scan_shell(cmd: str, base: str):
249
276
  tok = revspec_path
250
277
  if re.match(r"^/[a-z]/", tok): # MSYS /c/foo → C:/foo
251
278
  tok = f"{tok[1]}:{tok[2:]}"
252
- denial = ag.check(tok, "read", base)
279
+ # Resolve against the cwd the command will actually run in, not the
280
+ # session root. `cd /elsewhere && cat .env` used to compute the
281
+ # denial correctly and then throw it away, because the existence
282
+ # gate looked for `.env` under the project root where it does not
283
+ # live (#82). The rule base stays `base` — policy is the project's;
284
+ # only the path being judged follows the shell.
285
+ probe = tok
286
+ if not os.path.isabs(probe):
287
+ try:
288
+ probe = os.path.join(cwd, probe)
289
+ except (OSError, ValueError):
290
+ probe = tok
291
+ denial = ag.check(probe, "read", base)
253
292
  if denial and denial.kind == "deny":
254
293
  try:
255
- p = Path(tok)
256
- exists = (p if p.is_absolute() else Path(base) / p).exists()
294
+ exists = Path(probe).exists()
257
295
  except OSError:
258
296
  exists = False
259
297
  # A revspec names a path in HISTORY, so the working tree is the
@@ -262,6 +300,11 @@ def _scan_shell(cmd: str, base: str):
262
300
  # existence-gating it would be a hole this rewrite opened.
263
301
  if exists or from_revspec or denial.rule.startswith("<"):
264
302
  return denial, tok
303
+ # AFTER the segment's own tokens: `cd x` moves the ground for what
304
+ # follows, not for its own argument.
305
+ moved = _cd_target(segment, cwd)
306
+ if moved is not None:
307
+ cwd = moved
265
308
  return None, ""
266
309
 
267
310
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-context-control
3
- Version: 2.76.3
3
+ Version: 2.77.0
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=5sN37hJx2S9IJDyiWBb-NIfgQ02dPUxWSdaE1svI3GM,368519
3
+ cli/c3.py,sha256=vepPfESCu5S1EizKddSybSgWYC0zOrQ7r73dVLB2xNQ,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=msFfKCMMYZoF_2uDvMHWstFwoED1-yIhMuXZSz9dxmU,14128
6
+ cli/hook_access_guard.py,sha256=UGXZ6-3gVNxjVasAuCV9eUOBwqDHl3YnuDc3XVTpvL0,15942
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.3.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
115
+ code_context_control-2.77.0.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.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,,
268
+ code_context_control-2.77.0.dist-info/METADATA,sha256=XFIYGme_wszePZIjWKSWokSb3m4RqUwDXfF4k3Rqwk8,25648
269
+ code_context_control-2.77.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
270
+ code_context_control-2.77.0.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
271
+ code_context_control-2.77.0.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
272
+ code_context_control-2.77.0.dist-info/RECORD,,