luv-cli 0.0.22__tar.gz → 0.0.23__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.22 → luv_cli-0.0.23}/PKG-INFO +6 -1
- {luv_cli-0.0.22 → luv_cli-0.0.23}/README.md +5 -0
- {luv_cli-0.0.22 → luv_cli-0.0.23}/luv/__init__.py +51 -5
- {luv_cli-0.0.22 → luv_cli-0.0.23}/pyproject.toml +1 -1
- {luv_cli-0.0.22 → luv_cli-0.0.23}/.claude/settings.json +0 -0
- {luv_cli-0.0.22 → luv_cli-0.0.23}/.failproofai/policies-config.json +0 -0
- {luv_cli-0.0.22 → luv_cli-0.0.23}/.github/workflows/publish.yml +0 -0
- {luv_cli-0.0.22 → luv_cli-0.0.23}/.gitignore +0 -0
- {luv_cli-0.0.22 → luv_cli-0.0.23}/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.23
|
|
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
|
|
@@ -48,6 +48,9 @@ luv --init
|
|
|
48
48
|
# Create a new workspace and launch Claude
|
|
49
49
|
luv my-repo "add user authentication"
|
|
50
50
|
|
|
51
|
+
# Base a new workspace off a non-default branch
|
|
52
|
+
luv my-repo -b develop "add user authentication"
|
|
53
|
+
|
|
51
54
|
# Use a different org inline
|
|
52
55
|
luv other-org/my-repo "fix the bug"
|
|
53
56
|
|
|
@@ -82,6 +85,7 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
82
85
|
|---------|-------------|
|
|
83
86
|
| `luv --init` | Configure default GitHub org |
|
|
84
87
|
| `luv [org/]<repo> [prompt...]` | Create a new workspace and launch Claude |
|
|
88
|
+
| `luv [org/]<repo> -b <branch> [prompt...]` | Create a workspace based off `<branch>` instead of the default |
|
|
85
89
|
| `luv [org/]<repo> <number> [prompt]` | Reopen an existing workspace |
|
|
86
90
|
| `luv -l <PR URL> [prompt]` | Open any GitHub PR by URL |
|
|
87
91
|
| `luv [org/]<repo> -pr <number> [prompt]` | Open a PR by repo + number |
|
|
@@ -98,6 +102,7 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
98
102
|
| `-p` | Launch Claude in plan permission mode (default: `bypassPermissions`) |
|
|
99
103
|
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL); streams `stream-json` events to stdout |
|
|
100
104
|
| `-m MODEL` | Claude model to use (default: `claude-opus-4-8`); passed through to `claude --model`, so aliases like `opus`/`sonnet`/`haiku` work |
|
|
105
|
+
| `-b BRANCH` | Base a new workspace off `BRANCH` (clone + branch from it); recorded in `git config luv.base` so the PR can target it |
|
|
101
106
|
| `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
|
|
102
107
|
| `-f`, `--force` | Skip safety checks (with `--clean`) |
|
|
103
108
|
| `--safe` | With `--clean -f`, only delete workspaces older than 24h (mtime) |
|
|
@@ -25,6 +25,9 @@ luv --init
|
|
|
25
25
|
# Create a new workspace and launch Claude
|
|
26
26
|
luv my-repo "add user authentication"
|
|
27
27
|
|
|
28
|
+
# Base a new workspace off a non-default branch
|
|
29
|
+
luv my-repo -b develop "add user authentication"
|
|
30
|
+
|
|
28
31
|
# Use a different org inline
|
|
29
32
|
luv other-org/my-repo "fix the bug"
|
|
30
33
|
|
|
@@ -59,6 +62,7 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
59
62
|
|---------|-------------|
|
|
60
63
|
| `luv --init` | Configure default GitHub org |
|
|
61
64
|
| `luv [org/]<repo> [prompt...]` | Create a new workspace and launch Claude |
|
|
65
|
+
| `luv [org/]<repo> -b <branch> [prompt...]` | Create a workspace based off `<branch>` instead of the default |
|
|
62
66
|
| `luv [org/]<repo> <number> [prompt]` | Reopen an existing workspace |
|
|
63
67
|
| `luv -l <PR URL> [prompt]` | Open any GitHub PR by URL |
|
|
64
68
|
| `luv [org/]<repo> -pr <number> [prompt]` | Open a PR by repo + number |
|
|
@@ -75,6 +79,7 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
75
79
|
| `-p` | Launch Claude in plan permission mode (default: `bypassPermissions`) |
|
|
76
80
|
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL); streams `stream-json` events to stdout |
|
|
77
81
|
| `-m MODEL` | Claude model to use (default: `claude-opus-4-8`); passed through to `claude --model`, so aliases like `opus`/`sonnet`/`haiku` work |
|
|
82
|
+
| `-b BRANCH` | Base a new workspace off `BRANCH` (clone + branch from it); recorded in `git config luv.base` so the PR can target it |
|
|
78
83
|
| `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
|
|
79
84
|
| `-f`, `--force` | Skip safety checks (with `--clean`) |
|
|
80
85
|
| `--safe` | With `--clean -f`, only delete workspaces older than 24h (mtime) |
|
|
@@ -663,6 +663,19 @@ def main() -> None:
|
|
|
663
663
|
die("-m requires a model name")
|
|
664
664
|
args = args[:idx] + args[idx + 2:]
|
|
665
665
|
|
|
666
|
+
# -b takes a value (base branch); extract before the boolean-flag strip below.
|
|
667
|
+
base = None
|
|
668
|
+
if args.count("-b") > 1:
|
|
669
|
+
die("-b may only be provided once")
|
|
670
|
+
if "-b" in args:
|
|
671
|
+
idx = args.index("-b")
|
|
672
|
+
if idx + 1 >= len(args):
|
|
673
|
+
die("-b requires a branch name")
|
|
674
|
+
base = args[idx + 1].strip()
|
|
675
|
+
if not base or base.startswith("-"):
|
|
676
|
+
die("-b requires a branch name")
|
|
677
|
+
args = args[:idx] + args[idx + 2:]
|
|
678
|
+
|
|
666
679
|
args = [a for a in args if a not in ("-n", "-r", "-e", "-f", "--force", "-p", "-nit", "--safe")]
|
|
667
680
|
extra_env = collect_luv_env() if env_mode else {}
|
|
668
681
|
|
|
@@ -676,6 +689,7 @@ Flags:
|
|
|
676
689
|
-p launch Claude in plan permission mode (default: bypassPermissions)
|
|
677
690
|
-nit non-interactive: run claude -p <prompt> and exit (no REPL)
|
|
678
691
|
-m MODEL claude model to use (default: claude-opus-4-8)
|
|
692
|
+
-b BRANCH base a new workspace off BRANCH (clone + branch from it; recorded in git config luv.base)
|
|
679
693
|
-e env: pass LUV_* environment variables (with prefix stripped) into the session
|
|
680
694
|
-f, --force (with --clean) skip safety checks and delete all work folders
|
|
681
695
|
--safe (with --clean -f) only delete folders older than 24h
|
|
@@ -683,6 +697,7 @@ Flags:
|
|
|
683
697
|
Commands:
|
|
684
698
|
luv --init configure default GitHub org
|
|
685
699
|
luv [org/]<repo> [prompt...] create a new PR workspace
|
|
700
|
+
luv [org/]<repo> -b <branch> [prompt] create a workspace based off <branch>
|
|
686
701
|
luv [org/]<repo> <number> [prompt] reopen an existing work folder by number
|
|
687
702
|
luv -l <PR URL> [prompt] open any GitHub PR by URL
|
|
688
703
|
luv [org/]<repo> -pr <number> [prompt] open a GitHub PR by repo + number
|
|
@@ -703,6 +718,19 @@ Docker:
|
|
|
703
718
|
if safe and (args[0] != "--clean" or not force):
|
|
704
719
|
die("--safe only works with --clean -f")
|
|
705
720
|
|
|
721
|
+
# -b only makes sense when creating a NEW workspace; reject it on every path
|
|
722
|
+
# that early-returns without cloning+branching from scratch.
|
|
723
|
+
if base is not None:
|
|
724
|
+
is_reopen_by_number = len(args) > 1 and args[1].isdigit()
|
|
725
|
+
opens_latest_local = (nav_mode or resume_mode) and len(args) == 1
|
|
726
|
+
if (args[0] in ("--clean", "--init", "-l")
|
|
727
|
+
or "-pr" in args
|
|
728
|
+
or is_reopen_by_number
|
|
729
|
+
or opens_latest_local):
|
|
730
|
+
die("-b only applies when creating a new workspace "
|
|
731
|
+
"(luv [org/]<repo> [prompt...]); it cannot be combined with "
|
|
732
|
+
"--clean, --init, -l, -pr, reopen-by-number, or bare -n/-r")
|
|
733
|
+
|
|
706
734
|
if args[0] == "--clean":
|
|
707
735
|
cmd_clean(force=force, safe=safe)
|
|
708
736
|
return
|
|
@@ -771,6 +799,14 @@ Docker:
|
|
|
771
799
|
if r.returncode != 0:
|
|
772
800
|
die(f"repo '{org}/{repo}' not found or gh auth failed.\n{r.stderr.strip()}")
|
|
773
801
|
|
|
802
|
+
clone_url = f"https://github.com/{org}/{repo}"
|
|
803
|
+
|
|
804
|
+
# 1b. If a base branch was requested, verify it exists before cloning.
|
|
805
|
+
if base is not None:
|
|
806
|
+
r = run(["git", "ls-remote", "--heads", clone_url, base])
|
|
807
|
+
if r.returncode != 0 or f"refs/heads/{base}" not in r.stdout:
|
|
808
|
+
die(f"base branch '{base}' not found on {org}/{repo}")
|
|
809
|
+
|
|
774
810
|
# 2. Get latest issue/PR number (shared counter on GitHub).
|
|
775
811
|
# /issues is documented to include PRs but in practice returns [] for repos
|
|
776
812
|
# with no plain issues, so query both endpoints and take the max.
|
|
@@ -791,20 +827,30 @@ Docker:
|
|
|
791
827
|
candidate += 1
|
|
792
828
|
clone_dir = PRS_DIR / f"{repo}-{candidate}"
|
|
793
829
|
|
|
794
|
-
# 4. Clone
|
|
795
|
-
clone_url
|
|
796
|
-
|
|
797
|
-
|
|
830
|
+
# 4. Clone (off the base branch when -b was given)
|
|
831
|
+
print(f"luv: cloning {clone_url} -> {clone_dir}" + (f" (base {base})" if base else ""))
|
|
832
|
+
clone_cmd = ["git", "clone"]
|
|
833
|
+
if base is not None:
|
|
834
|
+
clone_cmd += ["--branch", base]
|
|
835
|
+
clone_cmd += [clone_url, str(clone_dir)]
|
|
836
|
+
r = subprocess.run(clone_cmd)
|
|
798
837
|
if r.returncode != 0:
|
|
799
838
|
die(f"git clone failed (exit {r.returncode})")
|
|
800
839
|
|
|
801
|
-
# 5. Create branch
|
|
840
|
+
# 5. Create branch off the cloned HEAD (= base when -b was given, else default)
|
|
802
841
|
branch = f"luv-{candidate}"
|
|
803
842
|
print(f"luv: creating branch {branch}")
|
|
804
843
|
r = subprocess.run(["git", "checkout", "-b", branch], cwd=str(clone_dir))
|
|
805
844
|
if r.returncode != 0:
|
|
806
845
|
die(f"git checkout -b failed (exit {r.returncode})")
|
|
807
846
|
|
|
847
|
+
# 5b. Record the base so the eventual PR can target it (local .git/config only).
|
|
848
|
+
if base is not None:
|
|
849
|
+
r = run(["git", "config", "luv.base", base], cwd=str(clone_dir))
|
|
850
|
+
if r.returncode != 0:
|
|
851
|
+
print(f"luv: warning: could not record base branch ({r.stderr.strip()})",
|
|
852
|
+
file=sys.stderr)
|
|
853
|
+
|
|
808
854
|
# 6. Ensure PR rules in ~/.claude/CLAUDE.md and bypass-permissions default
|
|
809
855
|
ensure_pr_rules()
|
|
810
856
|
ensure_default_permission_mode()
|
|
@@ -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.23"
|
|
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
|