Skip to content

Commit 7e2c8a9

Browse files
NiveditJainclaude
andauthored
[luv-12] Default to bypassPermissions in ~/.claude/settings.json (#11)
Add ensure_default_permission_mode() that merges permissions.defaultMode = bypassPermissions into the user's global Claude Code settings on each luv run, so new sessions start in bypass mode instead of the default. Atomic-write, idempotent, no-op on unreadable/non-dict JSON. Bump version to 0.0.5. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent add795f commit 7e2c8a9

2 files changed

Lines changed: 43 additions & 2 deletions

File tree

luv/__init__.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
CONFIG_FILE = LUV_DIR / "config.json"
1212
PRS_DIR = Path.home() / "prs"
1313
CLAUDE_JSON = Path.home() / ".claude.json"
14+
CLAUDE_SETTINGS_JSON = Path.home() / ".claude" / "settings.json"
1415

1516
PR_RULES = """
1617
# Pull Request Management
@@ -146,6 +147,45 @@ def ensure_pr_rules() -> None:
146147
f.write(PR_RULES)
147148

148149

150+
def ensure_default_permission_mode() -> None:
151+
"""Set permissions.defaultMode = bypassPermissions in ~/.claude/settings.json.
152+
153+
Merges into existing JSON without clobbering other keys. No-op if the file
154+
exists but is unreadable/invalid, or if the value is already set.
155+
"""
156+
data: dict[str, object] = {}
157+
if CLAUDE_SETTINGS_JSON.exists():
158+
try:
159+
with CLAUDE_SETTINGS_JSON.open("r", encoding="utf-8") as f:
160+
loaded = json.load(f)
161+
if not isinstance(loaded, dict):
162+
return
163+
data = loaded
164+
except (json.JSONDecodeError, OSError):
165+
return
166+
167+
permissions = data.get("permissions")
168+
if not isinstance(permissions, dict):
169+
permissions = {}
170+
data["permissions"] = permissions
171+
172+
if permissions.get("defaultMode") == "bypassPermissions":
173+
return
174+
permissions["defaultMode"] = "bypassPermissions"
175+
176+
CLAUDE_SETTINGS_JSON.parent.mkdir(parents=True, exist_ok=True)
177+
with tempfile.NamedTemporaryFile(
178+
"w",
179+
encoding="utf-8",
180+
dir=str(CLAUDE_SETTINGS_JSON.parent),
181+
delete=False,
182+
) as tmp:
183+
json.dump(data, tmp, indent=2)
184+
tmp.write("\n")
185+
tmp_path = Path(tmp.name)
186+
os.replace(tmp_path, CLAUDE_SETTINGS_JSON)
187+
188+
149189
def cmd_init() -> None:
150190
"""Interactive setup: choose a default GitHub org."""
151191
if not sys.stdin.isatty():
@@ -657,8 +697,9 @@ def main() -> None:
657697
if r.returncode != 0:
658698
die(f"git checkout -b failed (exit {r.returncode})")
659699

660-
# 6. Ensure PR rules in ~/.claude/CLAUDE.md
700+
# 6. Ensure PR rules in ~/.claude/CLAUDE.md and bypass-permissions default
661701
ensure_pr_rules()
702+
ensure_default_permission_mode()
662703

663704
print(f"luv: ready — {clone_dir.name}, branch {branch}")
664705

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "luv-cli"
7-
version = "0.0.4"
7+
version = "0.0.5"
88
description = "Launch Claude Code agents on GitHub repos with isolated workspaces and optional Docker dev environments"
99
requires-python = ">=3.10"
1010
license = "MIT"

0 commit comments

Comments
 (0)