luv-cli 0.0.13__tar.gz → 0.0.16__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.13 → luv_cli-0.0.16}/PKG-INFO +3 -1
- {luv_cli-0.0.13 → luv_cli-0.0.16}/README.md +2 -0
- {luv_cli-0.0.13 → luv_cli-0.0.16}/luv/__init__.py +26 -15
- {luv_cli-0.0.13 → luv_cli-0.0.16}/pyproject.toml +1 -1
- {luv_cli-0.0.13 → luv_cli-0.0.16}/.claude/settings.json +0 -0
- {luv_cli-0.0.13 → luv_cli-0.0.16}/.failproofai/policies-config.json +0 -0
- {luv_cli-0.0.13 → luv_cli-0.0.16}/.github/workflows/publish.yml +0 -0
- {luv_cli-0.0.13 → luv_cli-0.0.16}/.gitignore +0 -0
- {luv_cli-0.0.13 → luv_cli-0.0.16}/LICENSE +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.16
|
|
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
|
|
@@ -94,6 +94,8 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
94
94
|
|------|-------------|
|
|
95
95
|
| `-n` | Navigate: open a shell instead of Claude |
|
|
96
96
|
| `-r` | Resume: resume the last Claude session |
|
|
97
|
+
| `-p` | Launch Claude in plan permission mode (default: `bypassPermissions`) |
|
|
98
|
+
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL); streams `stream-json` events to stdout |
|
|
97
99
|
| `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
|
|
98
100
|
| `-f`, `--force` | Skip safety checks (with `--clean`) |
|
|
99
101
|
|
|
@@ -71,6 +71,8 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
71
71
|
|------|-------------|
|
|
72
72
|
| `-n` | Navigate: open a shell instead of Claude |
|
|
73
73
|
| `-r` | Resume: resume the last Claude session |
|
|
74
|
+
| `-p` | Launch Claude in plan permission mode (default: `bypassPermissions`) |
|
|
75
|
+
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL); streams `stream-json` events to stdout |
|
|
74
76
|
| `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
|
|
75
77
|
| `-f`, `--force` | Skip safety checks (with `--clean`) |
|
|
76
78
|
|
|
@@ -347,7 +347,8 @@ def resume(clone_dir: Path, extra_env: dict[str, str] = {}) -> None:
|
|
|
347
347
|
"--remote-control-session-name-prefix", clone_dir.name])
|
|
348
348
|
|
|
349
349
|
|
|
350
|
-
def launch(clone_dir: Path, prompt: str | None,
|
|
350
|
+
def launch(clone_dir: Path, prompt: str | None, plan_mode: bool = False,
|
|
351
|
+
non_interactive: bool = False, extra_env: dict[str, str] = {}) -> None:
|
|
351
352
|
"""Trust, resolve claude, chdir, and exec — replacing this process."""
|
|
352
353
|
trust_project(clone_dir)
|
|
353
354
|
os.chdir(str(clone_dir))
|
|
@@ -359,12 +360,18 @@ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {})
|
|
|
359
360
|
"--effort", "max",
|
|
360
361
|
"--remote-control",
|
|
361
362
|
"--remote-control-session-name-prefix", clone_dir.name]
|
|
362
|
-
if
|
|
363
|
+
if non_interactive:
|
|
364
|
+
if not prompt:
|
|
365
|
+
die("-nit requires a prompt")
|
|
366
|
+
mode_flags = ["--output-format", "stream-json",
|
|
367
|
+
"--verbose", "--include-partial-messages"]
|
|
368
|
+
initial_args = ["-p", prompt]
|
|
369
|
+
elif plan_mode:
|
|
363
370
|
mode_flags = ["--permission-mode", "plan"]
|
|
364
|
-
initial_args = [prompt]
|
|
371
|
+
initial_args = [prompt] if prompt else [f"/color {pick_color()}"]
|
|
365
372
|
else:
|
|
366
373
|
mode_flags = ["--permission-mode", "bypassPermissions"]
|
|
367
|
-
initial_args = [f"/color {pick_color()}"]
|
|
374
|
+
initial_args = [prompt] if prompt else [f"/color {pick_color()}"]
|
|
368
375
|
|
|
369
376
|
if compose_file:
|
|
370
377
|
project = docker_project_name(clone_dir)
|
|
@@ -489,7 +496,7 @@ def find_latest_clone(repo: str) -> Path | None:
|
|
|
489
496
|
return best
|
|
490
497
|
|
|
491
498
|
|
|
492
|
-
def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool = False, resume_mode: bool = False, extra_env: dict[str, str] = {}) -> None:
|
|
499
|
+
def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool = False, resume_mode: bool = False, plan_mode: bool = False, non_interactive: bool = False, extra_env: dict[str, str] = {}) -> None:
|
|
493
500
|
"""Open an existing work folder or remote branch by number."""
|
|
494
501
|
clone_dir = PRS_DIR / f"{repo}-{number}"
|
|
495
502
|
|
|
@@ -502,7 +509,7 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
|
|
|
502
509
|
elif resume_mode:
|
|
503
510
|
resume(clone_dir, extra_env=extra_env)
|
|
504
511
|
else:
|
|
505
|
-
launch(clone_dir, prompt, extra_env=extra_env)
|
|
512
|
+
launch(clone_dir, prompt, plan_mode=plan_mode, non_interactive=non_interactive, extra_env=extra_env)
|
|
506
513
|
return # unreachable
|
|
507
514
|
|
|
508
515
|
# 2. Check remote branch luv-{number}
|
|
@@ -529,10 +536,10 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
|
|
|
529
536
|
elif resume_mode:
|
|
530
537
|
resume(clone_dir, extra_env=extra_env)
|
|
531
538
|
else:
|
|
532
|
-
launch(clone_dir, prompt, extra_env=extra_env)
|
|
539
|
+
launch(clone_dir, prompt, plan_mode=plan_mode, non_interactive=non_interactive, extra_env=extra_env)
|
|
533
540
|
|
|
534
541
|
|
|
535
|
-
def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool = False, resume_mode: bool = False, extra_env: dict[str, str] = {}) -> None:
|
|
542
|
+
def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool = False, resume_mode: bool = False, plan_mode: bool = False, non_interactive: bool = False, extra_env: dict[str, str] = {}) -> None:
|
|
536
543
|
"""Open any GitHub PR by org/repo/number, cloning if needed."""
|
|
537
544
|
clone_dir = PRS_DIR / f"{repo}-{number}"
|
|
538
545
|
|
|
@@ -544,7 +551,7 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
|
|
|
544
551
|
elif resume_mode:
|
|
545
552
|
resume(clone_dir, extra_env=extra_env)
|
|
546
553
|
else:
|
|
547
|
-
launch(clone_dir, prompt, extra_env=extra_env)
|
|
554
|
+
launch(clone_dir, prompt, plan_mode=plan_mode, non_interactive=non_interactive, extra_env=extra_env)
|
|
548
555
|
return # unreachable
|
|
549
556
|
|
|
550
557
|
# Resolve the actual branch name via GitHub API
|
|
@@ -571,7 +578,7 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
|
|
|
571
578
|
elif resume_mode:
|
|
572
579
|
resume(clone_dir, extra_env=extra_env)
|
|
573
580
|
else:
|
|
574
|
-
launch(clone_dir, prompt, extra_env=extra_env)
|
|
581
|
+
launch(clone_dir, prompt, plan_mode=plan_mode, non_interactive=non_interactive, extra_env=extra_env)
|
|
575
582
|
|
|
576
583
|
|
|
577
584
|
def main() -> None:
|
|
@@ -579,9 +586,11 @@ def main() -> None:
|
|
|
579
586
|
|
|
580
587
|
nav_mode = "-n" in args
|
|
581
588
|
resume_mode = "-r" in args
|
|
589
|
+
plan_mode = "-p" in args
|
|
590
|
+
non_interactive = "-nit" in args
|
|
582
591
|
force = "-f" in args or "--force" in args
|
|
583
592
|
env_mode = "-e" in args
|
|
584
|
-
args = [a for a in args if a not in ("-n", "-r", "-e", "-f", "--force")]
|
|
593
|
+
args = [a for a in args if a not in ("-n", "-r", "-e", "-f", "--force", "-p", "-nit")]
|
|
585
594
|
extra_env = collect_luv_env() if env_mode else {}
|
|
586
595
|
|
|
587
596
|
if not args or args[0] in ("-h", "--help"):
|
|
@@ -591,6 +600,8 @@ Usage: luv [flags] <command>
|
|
|
591
600
|
Flags:
|
|
592
601
|
-n navigate: open a shell in the work folder instead of launching Claude
|
|
593
602
|
-r resume: resume the last Claude session in the work folder
|
|
603
|
+
-p launch Claude in plan permission mode (default: bypassPermissions)
|
|
604
|
+
-nit non-interactive: run claude -p <prompt> and exit (no REPL)
|
|
594
605
|
-e env: pass LUV_* environment variables (with prefix stripped) into the session
|
|
595
606
|
-f, --force (with --clean) skip safety checks and delete all work folders
|
|
596
607
|
|
|
@@ -632,7 +643,7 @@ Docker:
|
|
|
632
643
|
die(f"cannot parse PR URL: {url}")
|
|
633
644
|
org, repo, number = m.group(1), m.group(2), int(m.group(3))
|
|
634
645
|
prompt = " ".join(args[2:]) or None
|
|
635
|
-
open_pr(org, repo, number, prompt, nav_mode, resume_mode, extra_env=extra_env)
|
|
646
|
+
open_pr(org, repo, number, prompt, nav_mode, resume_mode, plan_mode, non_interactive, extra_env=extra_env)
|
|
636
647
|
return
|
|
637
648
|
|
|
638
649
|
raw = args[0].rstrip("/")
|
|
@@ -652,14 +663,14 @@ Docker:
|
|
|
652
663
|
die(f"expected a PR number after -pr, got '{args[idx + 1]}'")
|
|
653
664
|
prompt_parts = [a for i, a in enumerate(args) if i not in (0, idx, idx + 1)]
|
|
654
665
|
prompt = " ".join(prompt_parts) or None
|
|
655
|
-
open_pr(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode, extra_env=extra_env)
|
|
666
|
+
open_pr(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode, plan_mode, non_interactive, extra_env=extra_env)
|
|
656
667
|
return
|
|
657
668
|
|
|
658
669
|
# Detect optional numeric second argument
|
|
659
670
|
if len(args) > 1 and args[1].isdigit():
|
|
660
671
|
number = int(args[1])
|
|
661
672
|
prompt = " ".join(args[2:]) or None
|
|
662
|
-
open_existing(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode, extra_env=extra_env)
|
|
673
|
+
open_existing(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode, plan_mode, non_interactive, extra_env=extra_env)
|
|
663
674
|
return
|
|
664
675
|
|
|
665
676
|
org = resolve_org(explicit_org)
|
|
@@ -728,4 +739,4 @@ Docker:
|
|
|
728
739
|
elif resume_mode:
|
|
729
740
|
resume(clone_dir, extra_env=extra_env)
|
|
730
741
|
else:
|
|
731
|
-
launch(clone_dir, prompt, extra_env=extra_env)
|
|
742
|
+
launch(clone_dir, prompt, plan_mode=plan_mode, non_interactive=non_interactive, extra_env=extra_env)
|
|
@@ -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.16"
|
|
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
|