luv-cli 0.0.4__tar.gz → 0.0.6__tar.gz
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.
- {luv_cli-0.0.4 → luv_cli-0.0.6}/PKG-INFO +1 -1
- {luv_cli-0.0.4 → luv_cli-0.0.6}/luv/__init__.py +56 -8
- {luv_cli-0.0.4 → luv_cli-0.0.6}/pyproject.toml +1 -1
- {luv_cli-0.0.4 → luv_cli-0.0.6}/.claude/settings.json +0 -0
- {luv_cli-0.0.4 → luv_cli-0.0.6}/.failproofai/policies-config.json +0 -0
- {luv_cli-0.0.4 → luv_cli-0.0.6}/.github/workflows/publish.yml +0 -0
- {luv_cli-0.0.4 → luv_cli-0.0.6}/.gitignore +0 -0
- {luv_cli-0.0.4 → luv_cli-0.0.6}/LICENSE +0 -0
- {luv_cli-0.0.4 → luv_cli-0.0.6}/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: luv-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Launch Claude Code agents on GitHub repos with isolated workspaces and optional Docker dev environments
|
|
5
5
|
Project-URL: Homepage, https://github.com/exospherehost/luv
|
|
6
6
|
Project-URL: Repository, https://github.com/exospherehost/luv
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
import random
|
|
3
4
|
import re
|
|
4
5
|
import shutil
|
|
5
6
|
import subprocess
|
|
@@ -11,6 +12,14 @@ LUV_DIR = Path.home() / ".luv"
|
|
|
11
12
|
CONFIG_FILE = LUV_DIR / "config.json"
|
|
12
13
|
PRS_DIR = Path.home() / "prs"
|
|
13
14
|
CLAUDE_JSON = Path.home() / ".claude.json"
|
|
15
|
+
CLAUDE_SETTINGS_JSON = Path.home() / ".claude" / "settings.json"
|
|
16
|
+
|
|
17
|
+
COLORS = ["red", "blue", "green", "yellow", "purple", "orange", "pink", "cyan", "default"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def pick_color() -> str:
|
|
21
|
+
"""Pick a random /color value so each luv session is visually distinct."""
|
|
22
|
+
return random.choice(COLORS)
|
|
14
23
|
|
|
15
24
|
PR_RULES = """
|
|
16
25
|
# Pull Request Management
|
|
@@ -146,6 +155,45 @@ def ensure_pr_rules() -> None:
|
|
|
146
155
|
f.write(PR_RULES)
|
|
147
156
|
|
|
148
157
|
|
|
158
|
+
def ensure_default_permission_mode() -> None:
|
|
159
|
+
"""Set permissions.defaultMode = bypassPermissions in ~/.claude/settings.json.
|
|
160
|
+
|
|
161
|
+
Merges into existing JSON without clobbering other keys. No-op if the file
|
|
162
|
+
exists but is unreadable/invalid, or if the value is already set.
|
|
163
|
+
"""
|
|
164
|
+
data: dict[str, object] = {}
|
|
165
|
+
if CLAUDE_SETTINGS_JSON.exists():
|
|
166
|
+
try:
|
|
167
|
+
with CLAUDE_SETTINGS_JSON.open("r", encoding="utf-8") as f:
|
|
168
|
+
loaded = json.load(f)
|
|
169
|
+
if not isinstance(loaded, dict):
|
|
170
|
+
return
|
|
171
|
+
data = loaded
|
|
172
|
+
except (json.JSONDecodeError, OSError):
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
permissions = data.get("permissions")
|
|
176
|
+
if not isinstance(permissions, dict):
|
|
177
|
+
permissions = {}
|
|
178
|
+
data["permissions"] = permissions
|
|
179
|
+
|
|
180
|
+
if permissions.get("defaultMode") == "bypassPermissions":
|
|
181
|
+
return
|
|
182
|
+
permissions["defaultMode"] = "bypassPermissions"
|
|
183
|
+
|
|
184
|
+
CLAUDE_SETTINGS_JSON.parent.mkdir(parents=True, exist_ok=True)
|
|
185
|
+
with tempfile.NamedTemporaryFile(
|
|
186
|
+
"w",
|
|
187
|
+
encoding="utf-8",
|
|
188
|
+
dir=str(CLAUDE_SETTINGS_JSON.parent),
|
|
189
|
+
delete=False,
|
|
190
|
+
) as tmp:
|
|
191
|
+
json.dump(data, tmp, indent=2)
|
|
192
|
+
tmp.write("\n")
|
|
193
|
+
tmp_path = Path(tmp.name)
|
|
194
|
+
os.replace(tmp_path, CLAUDE_SETTINGS_JSON)
|
|
195
|
+
|
|
196
|
+
|
|
149
197
|
def cmd_init() -> None:
|
|
150
198
|
"""Interactive setup: choose a default GitHub org."""
|
|
151
199
|
if not sys.stdin.isatty():
|
|
@@ -302,6 +350,9 @@ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {})
|
|
|
302
350
|
settings = load_luv_settings(clone_dir)
|
|
303
351
|
compose_file = (settings or {}).get("compose_file")
|
|
304
352
|
|
|
353
|
+
color_cmd = f"/color {pick_color()}"
|
|
354
|
+
initial = f"{color_cmd}\n/plan {prompt}" if prompt else color_cmd
|
|
355
|
+
|
|
305
356
|
if compose_file:
|
|
306
357
|
project = docker_project_name(clone_dir)
|
|
307
358
|
start_docker(clone_dir, compose_file, project)
|
|
@@ -309,9 +360,8 @@ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {})
|
|
|
309
360
|
base = docker_compose_base(clone_dir, compose_file, project)
|
|
310
361
|
claude_cmd = ["claude", "--dangerously-skip-permissions",
|
|
311
362
|
"--permission-mode", "bypassPermissions",
|
|
312
|
-
"--model", "claude-opus-4-7", "--effort", "max"
|
|
313
|
-
|
|
314
|
-
claude_cmd.append(f"/plan {prompt}")
|
|
363
|
+
"--model", "claude-opus-4-7", "--effort", "max",
|
|
364
|
+
initial]
|
|
315
365
|
r = subprocess.run(base + ["exec", "-it"] + docker_env_flags(extra_env) + ["dev-environment"] + claude_cmd)
|
|
316
366
|
sys.exit(r.returncode)
|
|
317
367
|
finally:
|
|
@@ -324,10 +374,7 @@ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {})
|
|
|
324
374
|
"--permission-mode", "bypassPermissions",
|
|
325
375
|
"--model", "claude-opus-4-7", "--effort", "max"]
|
|
326
376
|
os.environ.update(extra_env)
|
|
327
|
-
|
|
328
|
-
os.execv(claude_bin, base_args + [f"/plan {prompt}"])
|
|
329
|
-
else:
|
|
330
|
-
os.execv(claude_bin, base_args)
|
|
377
|
+
os.execv(claude_bin, base_args + [initial])
|
|
331
378
|
|
|
332
379
|
|
|
333
380
|
def cmd_clean(force: bool = False) -> None:
|
|
@@ -657,8 +704,9 @@ Docker:
|
|
|
657
704
|
if r.returncode != 0:
|
|
658
705
|
die(f"git checkout -b failed (exit {r.returncode})")
|
|
659
706
|
|
|
660
|
-
# 6. Ensure PR rules in ~/.claude/CLAUDE.md
|
|
707
|
+
# 6. Ensure PR rules in ~/.claude/CLAUDE.md and bypass-permissions default
|
|
661
708
|
ensure_pr_rules()
|
|
709
|
+
ensure_default_permission_mode()
|
|
662
710
|
|
|
663
711
|
print(f"luv: ready — {clone_dir.name}, branch {branch}")
|
|
664
712
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "luv-cli"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.6"
|
|
8
8
|
description = "Launch Claude Code agents on GitHub repos with isolated workspaces and optional Docker dev environments"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
license = "MIT"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|