claude-jacked 0.3.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-jacked
3
- Version: 0.3.1
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
@@ -82,10 +82,10 @@ Install claude-jacked for me. Use AskUserQuestion to ask me which features I wan
82
82
  - SECURITY: Everything above + auto-approve safe bash commands (fewer permission prompts)
83
83
  - ALL: Everything
84
84
  3. Install based on my choice:
85
- - BASE: pipx install claude-jacked && jacked install
86
- - SEARCH: pipx install "claude-jacked[search]" && jacked install
87
- - SECURITY: pipx install "claude-jacked[security]" && jacked install --security
88
- - ALL: pipx install "claude-jacked[all]" && jacked install --security
85
+ - BASE: pipx install claude-jacked && jacked install --force
86
+ - SEARCH: pipx install "claude-jacked[search]" && jacked install --force
87
+ - SECURITY: pipx install "claude-jacked[security]" && jacked install --force --security
88
+ - ALL: pipx install "claude-jacked[all]" && jacked install --force --security
89
89
  4. If I chose SEARCH or ALL, help me set up Qdrant Cloud credentials
90
90
  5. Verify with: jacked --help
91
91
  ```
@@ -95,26 +95,26 @@ Install claude-jacked for me. Use AskUserQuestion to ask me which features I wan
95
95
  **Core (reviewers, commands, behavioral rules):**
96
96
  ```bash
97
97
  pipx install claude-jacked
98
- jacked install
98
+ jacked install --force
99
99
  ```
100
100
 
101
101
  **Add session search (optional):**
102
102
  ```bash
103
103
  pipx install "claude-jacked[search]"
104
- jacked install
104
+ jacked install --force
105
105
  # Then set up Qdrant Cloud credentials (see below)
106
106
  ```
107
107
 
108
108
  **Add security gatekeeper (optional):**
109
109
  ```bash
110
110
  pipx install "claude-jacked[security]"
111
- jacked install --security
111
+ jacked install --force --security
112
112
  ```
113
113
 
114
114
  **Everything:**
115
115
  ```bash
116
116
  pipx install "claude-jacked[all]"
117
- jacked install --security
117
+ jacked install --force --security
118
118
  ```
119
119
 
120
120
  ---
@@ -277,7 +277,7 @@ The security gatekeeper is opt-in. To enable it:
277
277
 
278
278
  ```bash
279
279
  pip install "claude-jacked[security]"
280
- jacked install --security
280
+ jacked install --force --security
281
281
  ```
282
282
 
283
283
  To remove just the security hook:
@@ -311,7 +311,7 @@ export ANTHROPIC_API_KEY="sk-..."
311
311
  Get audio alerts so you don't have to watch the terminal:
312
312
 
313
313
  ```bash
314
- jacked install --sounds
314
+ jacked install --force --sounds
315
315
  ```
316
316
 
317
317
  - **Notification sound** — Plays when Claude needs your input
@@ -444,10 +444,10 @@ jacked delete <session_id> # Remove from index
444
444
  jacked cleardb # Delete all your data
445
445
 
446
446
  # Setup
447
- jacked install # Install agents, commands, rules
448
- jacked install --search # Also add session indexing hook
449
- jacked install --security # Also add security gatekeeper hook
450
- jacked install --sounds # Also add sound notifications
447
+ jacked install --force # Install agents, commands, rules
448
+ jacked install --force --search # Also add session indexing hook
449
+ jacked install --force --security # Also add security gatekeeper hook
450
+ jacked install --force --sounds # Also add sound notifications
451
451
  jacked uninstall # Remove from Claude Code
452
452
  jacked uninstall --sounds # Remove only sounds
453
453
  jacked uninstall --security # Remove only security hook
@@ -603,10 +603,10 @@ Options:
603
603
 
604
604
  PHASE 3 - INSTALL:
605
605
  Based on user choice:
606
- - BASE: pipx install claude-jacked && jacked install
607
- - SEARCH: pipx install "claude-jacked[search]" && jacked install
608
- - SECURITY: pipx install "claude-jacked[security]" && jacked install --security
609
- - ALL: pipx install "claude-jacked[all]" && jacked install --security
606
+ - BASE: pipx install claude-jacked && jacked install --force
607
+ - SEARCH: pipx install "claude-jacked[search]" && jacked install --force
608
+ - SECURITY: pipx install "claude-jacked[security]" && jacked install --force --security
609
+ - ALL: pipx install "claude-jacked[all]" && jacked install --force --security
610
610
 
611
611
  PHASE 4 - POST-INSTALL (if SEARCH or ALL):
612
612
  Help user set up Qdrant Cloud:
@@ -1,5 +1,5 @@
1
- jacked/__init__.py,sha256=-nf4gLXaGd9gIAXz2-AaiIdFt1gZbXGDalP0jHTTOFo,1276
2
- jacked/cli.py,sha256=Uw4Dk0GjMgVzA7vc6w6wBfNeCr3CjbcyeyR9PJvrGbg,51636
1
+ jacked/__init__.py,sha256=RHhr1IFF5FdT1ERj7GstKVdi4KpHq0K7hYDr6MgR4BY,1276
2
+ jacked/cli.py,sha256=SPGNaIhxL9VTcS9qqvWNwLrVgFrjCNq-a8S1q1kAPIw,51728
3
3
  jacked/client.py,sha256=7N4GdjcG8OXfmjUfprlUOJTkyrPyqF1OBstAsplNSa8,18151
4
4
  jacked/config.py,sha256=DeC1no5rFsvlb6JaU2PzLyyhI4DaJrxrsfqVugWuC4Q,8606
5
5
  jacked/index_write_tracker.py,sha256=E1zVqyZTQdHenp_vXL-gWPvLBo18Ar703_0MI9Cst8U,8679
@@ -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=nhiT4X-8zBhjG-i1hKgQzoXNE3NOnekTYGDlns72iXk,14083
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.1.dist-info/METADATA,sha256=dCRRqhE9GYB3Z9wl8PdUYDYr2-o5jXCTQAt1Bnuew0Q,22091
30
- claude_jacked-0.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
31
- claude_jacked-0.3.1.dist-info/entry_points.txt,sha256=52JDhetd7OsE9iJryGrQIL3PWhum_QCeLuTeQ9-CTqQ,43
32
- claude_jacked-0.3.1.dist-info/licenses/LICENSE,sha256=m8NmsH7P5G4VH3VyHWPQUTl2weuNTh1-eaH0RbUcn3k,1066
33
- claude_jacked-0.3.1.dist-info/RECORD,,
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
@@ -8,7 +8,7 @@ Install extras for additional features:
8
8
  pip install "claude-jacked[all]" — everything
9
9
  """
10
10
 
11
- __version__ = "0.3.1"
11
+ __version__ = "0.3.3"
12
12
 
13
13
 
14
14
  def _qdrant_available() -> bool:
jacked/cli.py CHANGED
@@ -807,7 +807,7 @@ def _install_behavioral_rules(claude_md_path: Path, force: bool = False):
807
807
  # Version upgrade needed
808
808
  console.print("\n[bold]Behavioral rules update available:[/bold]")
809
809
  console.print(f"[dim]{rules_text}[/dim]")
810
- if not force and not click.confirm("Update behavioral rules in CLAUDE.md?"):
810
+ if not force and sys.stdin.isatty() and not click.confirm("Update behavioral rules in CLAUDE.md?"):
811
811
  console.print("[yellow][-][/yellow] Skipped behavioral rules update")
812
812
  return
813
813
 
@@ -838,7 +838,7 @@ def _install_behavioral_rules(claude_md_path: Path, force: bool = False):
838
838
  # Fresh install - show and confirm
839
839
  console.print("\n[bold]Proposed behavioral rules for ~/.claude/CLAUDE.md:[/bold]")
840
840
  console.print(f"[dim]{rules_text}[/dim]")
841
- if not force and not click.confirm("Add these behavioral rules to your global CLAUDE.md?"):
841
+ if not force and sys.stdin.isatty() and not click.confirm("Add these behavioral rules to your global CLAUDE.md?"):
842
842
  console.print("[yellow][-][/yellow] Skipped behavioral rules")
843
843
  return
844
844
 
@@ -1142,7 +1142,7 @@ def install(sounds: bool, search: bool, security: bool, no_rules: bool, force: b
1142
1142
  skipped += 1
1143
1143
  continue # Same content, skip silently
1144
1144
  # Different content - ask before overwriting (unless --force)
1145
- if not force and not click.confirm(f"Agent '{agent_file.name}' exists with different content. Overwrite?"):
1145
+ if not force and sys.stdin.isatty() and not click.confirm(f"Agent '{agent_file.name}' exists with different content. Overwrite?"):
1146
1146
  console.print(f"[yellow][-][/yellow] Skipped {agent_file.name}")
1147
1147
  continue
1148
1148
  shutil.copy(agent_file, dst_file)
@@ -1170,7 +1170,7 @@ def install(sounds: bool, search: bool, security: bool, no_rules: bool, force: b
1170
1170
  skipped += 1
1171
1171
  continue # Same content, skip silently
1172
1172
  # Different content - ask before overwriting (unless --force)
1173
- if not force and not click.confirm(f"Command '{cmd_file.name}' exists with different content. Overwrite?"):
1173
+ if not force and sys.stdin.isatty() and not click.confirm(f"Command '{cmd_file.name}' exists with different content. Overwrite?"):
1174
1174
  console.print(f"[yellow][-][/yellow] Skipped {cmd_file.name}")
1175
1175
  continue
1176
1176
  shutil.copy(cmd_file, dst_file)
@@ -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
- if is_wildcard:
201
- if command.startswith(prefix):
202
- return True
203
- else:
204
- if command == prefix:
205
- return True
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: PASS ({elapsed:.3f}s)")
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: PASS ({elapsed:.3f}s)")
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: PASS (no response, {elapsed:.1f}s)")
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: PASS ({elapsed:.1f}s)")
422
+ log(f"DECISION: ASK USER ({elapsed:.1f}s)")
410
423
 
411
424
  sys.exit(0)
412
425