claude-jacked 0.3.2__py3-none-any.whl → 0.3.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.
- {claude_jacked-0.3.2.dist-info → claude_jacked-0.3.3.dist-info}/METADATA +1 -1
- {claude_jacked-0.3.2.dist-info → claude_jacked-0.3.3.dist-info}/RECORD +7 -7
- jacked/__init__.py +1 -1
- jacked/data/hooks/security_gatekeeper.py +25 -12
- {claude_jacked-0.3.2.dist-info → claude_jacked-0.3.3.dist-info}/WHEEL +0 -0
- {claude_jacked-0.3.2.dist-info → claude_jacked-0.3.3.dist-info}/entry_points.txt +0 -0
- {claude_jacked-0.3.2.dist-info → claude_jacked-0.3.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-jacked
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Smart reviewers, commands, and session search for Claude Code
|
|
5
5
|
Project-URL: Homepage, https://github.com/jackneil/claude-jacked
|
|
6
6
|
Project-URL: Repository, https://github.com/jackneil/claude-jacked
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
jacked/__init__.py,sha256=
|
|
1
|
+
jacked/__init__.py,sha256=RHhr1IFF5FdT1ERj7GstKVdi4KpHq0K7hYDr6MgR4BY,1276
|
|
2
2
|
jacked/cli.py,sha256=SPGNaIhxL9VTcS9qqvWNwLrVgFrjCNq-a8S1q1kAPIw,51728
|
|
3
3
|
jacked/client.py,sha256=7N4GdjcG8OXfmjUfprlUOJTkyrPyqF1OBstAsplNSa8,18151
|
|
4
4
|
jacked/config.py,sha256=DeC1no5rFsvlb6JaU2PzLyyhI4DaJrxrsfqVugWuC4Q,8606
|
|
@@ -23,11 +23,11 @@ jacked/data/commands/learn.md,sha256=FoD-qKsQSpEayFNP997dyUfV4UJgs8LiojYIWnzOSvA
|
|
|
23
23
|
jacked/data/commands/pr.md,sha256=1orTbDnL4ky2bk5VXm5j66hGybTrNWVTyAPxx_uMggU,193
|
|
24
24
|
jacked/data/commands/redo.md,sha256=4NnCKKi10ybEOHdFObN1aVNaFGOt63eoluwDJUzUFxg,4242
|
|
25
25
|
jacked/data/commands/techdebt.md,sha256=jSdfMN1Kvz6vmsnWw9sCMFpKA8r87e9nDHrE-qsMwG8,3861
|
|
26
|
-
jacked/data/hooks/security_gatekeeper.py,sha256=
|
|
26
|
+
jacked/data/hooks/security_gatekeeper.py,sha256=zINhsDmyhYzULdym_U5DL0Uc5mnV4ot61NQ_FCnxF5I,14667
|
|
27
27
|
jacked/data/rules/jacked_behaviors.md,sha256=x1qzdj3DLmkhv5TVAdNyIpAdjBGz7IUdXSrko94LOOw,1449
|
|
28
28
|
jacked/data/skills/jacked/SKILL.md,sha256=IEMp-PfWlu5rmS1R5HHblUX1CGwzno2gtO4yQ2cMAg4,5488
|
|
29
|
-
claude_jacked-0.3.
|
|
30
|
-
claude_jacked-0.3.
|
|
31
|
-
claude_jacked-0.3.
|
|
32
|
-
claude_jacked-0.3.
|
|
33
|
-
claude_jacked-0.3.
|
|
29
|
+
claude_jacked-0.3.3.dist-info/METADATA,sha256=xLS9TKTUNakcyeA6SnM6NB3prpgL3XCf3SC0CknoCQc,22204
|
|
30
|
+
claude_jacked-0.3.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
31
|
+
claude_jacked-0.3.3.dist-info/entry_points.txt,sha256=52JDhetd7OsE9iJryGrQIL3PWhum_QCeLuTeQ9-CTqQ,43
|
|
32
|
+
claude_jacked-0.3.3.dist-info/licenses/LICENSE,sha256=m8NmsH7P5G4VH3VyHWPQUTl2weuNTh1-eaH0RbUcn3k,1066
|
|
33
|
+
claude_jacked-0.3.3.dist-info/RECORD,,
|
jacked/__init__.py
CHANGED
|
@@ -64,6 +64,9 @@ SAFE_EXACT = {
|
|
|
64
64
|
# e.g., C:/Users/jack/.conda/envs/krac_llm/python.exe → python
|
|
65
65
|
PATH_STRIP_RE = re.compile(r'^(?:.*[/\\])?([^/\\]+?)(?:\.exe)?(?:\s|$)', re.IGNORECASE)
|
|
66
66
|
|
|
67
|
+
# Strip leading env var assignments: HOME=/x PATH="/y:$PATH" cmd → cmd
|
|
68
|
+
ENV_ASSIGN_RE = re.compile(r"""^(?:\w+=(?:"[^"]*"|'[^']*'|\S+)\s+)+""")
|
|
69
|
+
|
|
67
70
|
# Universal safe: any command that just asks for version or help
|
|
68
71
|
VERSION_HELP_RE = re.compile(r'^\S+\s+(-[Vv]|--version|-h|--help)\s*$')
|
|
69
72
|
|
|
@@ -195,14 +198,18 @@ def check_permissions(command: str, cwd: str) -> bool:
|
|
|
195
198
|
patterns.extend(_load_permissions(project_dir / ".claude" / "settings.json"))
|
|
196
199
|
patterns.extend(_load_permissions(project_dir / ".claude" / "settings.local.json"))
|
|
197
200
|
|
|
201
|
+
cmd_core = _strip_env_prefix(command)
|
|
202
|
+
candidates = [command, cmd_core] if cmd_core != command else [command]
|
|
203
|
+
|
|
198
204
|
for pat in patterns:
|
|
199
205
|
prefix, is_wildcard = _parse_bash_pattern(pat)
|
|
200
|
-
|
|
201
|
-
if
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
for cmd in candidates:
|
|
207
|
+
if is_wildcard:
|
|
208
|
+
if cmd.startswith(prefix):
|
|
209
|
+
return True
|
|
210
|
+
else:
|
|
211
|
+
if cmd == prefix:
|
|
212
|
+
return True
|
|
206
213
|
|
|
207
214
|
return False
|
|
208
215
|
|
|
@@ -223,9 +230,14 @@ def _get_base_command(command: str) -> str:
|
|
|
223
230
|
return stripped
|
|
224
231
|
|
|
225
232
|
|
|
233
|
+
def _strip_env_prefix(cmd: str) -> str:
|
|
234
|
+
"""Strip leading env var assignments: HOME=/x PATH="/y" cmd → cmd"""
|
|
235
|
+
return ENV_ASSIGN_RE.sub('', cmd).strip()
|
|
236
|
+
|
|
237
|
+
|
|
226
238
|
def local_evaluate(command: str) -> str | None:
|
|
227
239
|
"""Evaluate command locally. Returns 'YES', 'NO', or None (ambiguous)."""
|
|
228
|
-
cmd = command.strip()
|
|
240
|
+
cmd = _strip_env_prefix(command.strip())
|
|
229
241
|
base = _get_base_command(cmd)
|
|
230
242
|
|
|
231
243
|
# Check deny patterns first (on original command, not stripped)
|
|
@@ -353,11 +365,12 @@ def main():
|
|
|
353
365
|
|
|
354
366
|
# Tier 0: Deny check FIRST — security always wins over permissions
|
|
355
367
|
cmd_stripped = command.strip()
|
|
368
|
+
cmd_core = _strip_env_prefix(cmd_stripped)
|
|
356
369
|
for pattern in DENY_PATTERNS:
|
|
357
|
-
if pattern.search(cmd_stripped):
|
|
370
|
+
if pattern.search(cmd_stripped) or pattern.search(cmd_core):
|
|
358
371
|
elapsed = time.time() - start
|
|
359
372
|
log(f"DENY MATCH ({elapsed:.3f}s)")
|
|
360
|
-
log(f"DECISION:
|
|
373
|
+
log(f"DECISION: ASK USER ({elapsed:.3f}s)")
|
|
361
374
|
sys.exit(0)
|
|
362
375
|
|
|
363
376
|
# Tier 1: Check Claude's own permission rules
|
|
@@ -380,7 +393,7 @@ def main():
|
|
|
380
393
|
# Shouldn't hit this since deny checked above, but just in case
|
|
381
394
|
elapsed = time.time() - start
|
|
382
395
|
log(f"LOCAL SAID: NO ({elapsed:.3f}s)")
|
|
383
|
-
log(f"DECISION:
|
|
396
|
+
log(f"DECISION: ASK USER ({elapsed:.3f}s)")
|
|
384
397
|
sys.exit(0)
|
|
385
398
|
|
|
386
399
|
# Tier 3+4: API then CLI for ambiguous commands
|
|
@@ -396,7 +409,7 @@ def main():
|
|
|
396
409
|
elapsed = time.time() - start
|
|
397
410
|
|
|
398
411
|
if response is None:
|
|
399
|
-
log(f"DECISION:
|
|
412
|
+
log(f"DECISION: ASK USER (no response, {elapsed:.1f}s)")
|
|
400
413
|
sys.exit(0)
|
|
401
414
|
|
|
402
415
|
response_upper = response.upper()
|
|
@@ -406,7 +419,7 @@ def main():
|
|
|
406
419
|
log(f"DECISION: ALLOW ({elapsed:.1f}s)")
|
|
407
420
|
emit_allow()
|
|
408
421
|
else:
|
|
409
|
-
log(f"DECISION:
|
|
422
|
+
log(f"DECISION: ASK USER ({elapsed:.1f}s)")
|
|
410
423
|
|
|
411
424
|
sys.exit(0)
|
|
412
425
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|