luv-cli 0.0.15__tar.gz → 0.0.17__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.15 → luv_cli-0.0.17}/PKG-INFO +5 -3
- {luv_cli-0.0.15 → luv_cli-0.0.17}/README.md +4 -2
- {luv_cli-0.0.15 → luv_cli-0.0.17}/luv/__init__.py +19 -5
- {luv_cli-0.0.15 → luv_cli-0.0.17}/pyproject.toml +1 -1
- {luv_cli-0.0.15 → luv_cli-0.0.17}/.claude/settings.json +0 -0
- {luv_cli-0.0.15 → luv_cli-0.0.17}/.failproofai/policies-config.json +0 -0
- {luv_cli-0.0.15 → luv_cli-0.0.17}/.github/workflows/publish.yml +0 -0
- {luv_cli-0.0.15 → luv_cli-0.0.17}/.gitignore +0 -0
- {luv_cli-0.0.15 → luv_cli-0.0.17}/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.17
|
|
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
|
|
@@ -87,6 +87,7 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
87
87
|
| `luv [org/]<repo> -pr <number> [prompt]` | Open a PR by repo + number |
|
|
88
88
|
| `luv --clean` | Delete workspaces where the branch is fully pushed/merged |
|
|
89
89
|
| `luv --clean -f` | Force delete all workspaces |
|
|
90
|
+
| `luv --clean --safe -f` | Force delete only workspaces older than 24h |
|
|
90
91
|
|
|
91
92
|
### Flags
|
|
92
93
|
|
|
@@ -95,9 +96,10 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
95
96
|
| `-n` | Navigate: open a shell instead of Claude |
|
|
96
97
|
| `-r` | Resume: resume the last Claude session |
|
|
97
98
|
| `-p` | Launch Claude in plan permission mode (default: `bypassPermissions`) |
|
|
98
|
-
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL) |
|
|
99
|
+
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL); streams `stream-json` events to stdout |
|
|
99
100
|
| `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
|
|
100
101
|
| `-f`, `--force` | Skip safety checks (with `--clean`) |
|
|
102
|
+
| `--safe` | With `--clean -f`, only delete workspaces older than 24h (mtime) |
|
|
101
103
|
|
|
102
104
|
## Docker dev environments
|
|
103
105
|
|
|
@@ -157,7 +159,7 @@ Docker mode works with all flags: `-n` opens a bash shell in the container, `-r`
|
|
|
157
159
|
- No unpushed commits
|
|
158
160
|
- If the remote branch is gone, verifies the PR was merged and local HEAD matches
|
|
159
161
|
|
|
160
|
-
Use `luv --clean -f` to skip all safety checks and delete everything.
|
|
162
|
+
Use `luv --clean -f` to skip all safety checks and delete everything. Add `--safe` (i.e. `luv --clean --safe -f`) to restrict force-delete to workspaces whose folder mtime is older than 24 hours, leaving recently-touched workspaces alone.
|
|
161
163
|
|
|
162
164
|
## Configuration
|
|
163
165
|
|
|
@@ -64,6 +64,7 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
64
64
|
| `luv [org/]<repo> -pr <number> [prompt]` | Open a PR by repo + number |
|
|
65
65
|
| `luv --clean` | Delete workspaces where the branch is fully pushed/merged |
|
|
66
66
|
| `luv --clean -f` | Force delete all workspaces |
|
|
67
|
+
| `luv --clean --safe -f` | Force delete only workspaces older than 24h |
|
|
67
68
|
|
|
68
69
|
### Flags
|
|
69
70
|
|
|
@@ -72,9 +73,10 @@ All workspaces live under `~/prs/`. The number comes from the repo's GitHub issu
|
|
|
72
73
|
| `-n` | Navigate: open a shell instead of Claude |
|
|
73
74
|
| `-r` | Resume: resume the last Claude session |
|
|
74
75
|
| `-p` | Launch Claude in plan permission mode (default: `bypassPermissions`) |
|
|
75
|
-
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL) |
|
|
76
|
+
| `-nit` | Non-interactive: run `claude -p <prompt>` and exit (no REPL); streams `stream-json` events to stdout |
|
|
76
77
|
| `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
|
|
77
78
|
| `-f`, `--force` | Skip safety checks (with `--clean`) |
|
|
79
|
+
| `--safe` | With `--clean -f`, only delete workspaces older than 24h (mtime) |
|
|
78
80
|
|
|
79
81
|
## Docker dev environments
|
|
80
82
|
|
|
@@ -134,7 +136,7 @@ Docker mode works with all flags: `-n` opens a bash shell in the container, `-r`
|
|
|
134
136
|
- No unpushed commits
|
|
135
137
|
- If the remote branch is gone, verifies the PR was merged and local HEAD matches
|
|
136
138
|
|
|
137
|
-
Use `luv --clean -f` to skip all safety checks and delete everything.
|
|
139
|
+
Use `luv --clean -f` to skip all safety checks and delete everything. Add `--safe` (i.e. `luv --clean --safe -f`) to restrict force-delete to workspaces whose folder mtime is older than 24 hours, leaving recently-touched workspaces alone.
|
|
138
140
|
|
|
139
141
|
## Configuration
|
|
140
142
|
|
|
@@ -6,6 +6,7 @@ import shutil
|
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
8
|
import tempfile
|
|
9
|
+
import time
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
11
12
|
LUV_DIR = Path.home() / ".luv"
|
|
@@ -363,7 +364,8 @@ def launch(clone_dir: Path, prompt: str | None, plan_mode: bool = False,
|
|
|
363
364
|
if non_interactive:
|
|
364
365
|
if not prompt:
|
|
365
366
|
die("-nit requires a prompt")
|
|
366
|
-
mode_flags = [
|
|
367
|
+
mode_flags = ["--output-format", "stream-json",
|
|
368
|
+
"--verbose", "--include-partial-messages"]
|
|
367
369
|
initial_args = ["-p", prompt]
|
|
368
370
|
elif plan_mode:
|
|
369
371
|
mode_flags = ["--permission-mode", "plan"]
|
|
@@ -390,7 +392,10 @@ def launch(clone_dir: Path, prompt: str | None, plan_mode: bool = False,
|
|
|
390
392
|
os.execv(claude_bin, [claude_bin] + common_flags + mode_flags + initial_args)
|
|
391
393
|
|
|
392
394
|
|
|
393
|
-
|
|
395
|
+
SAFE_AGE_SECONDS = 24 * 3600
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def cmd_clean(force: bool = False, safe: bool = False) -> None:
|
|
394
399
|
"""Scan ~/prs/ and delete fully-pushed, clean work folders."""
|
|
395
400
|
if not PRS_DIR.exists():
|
|
396
401
|
print("luv: nothing to clean (~/prs/ does not exist)")
|
|
@@ -398,6 +403,7 @@ def cmd_clean(force: bool = False) -> None:
|
|
|
398
403
|
|
|
399
404
|
cleaned: list[str] = []
|
|
400
405
|
skipped: list[tuple[str, str]] = []
|
|
406
|
+
now = time.time()
|
|
401
407
|
|
|
402
408
|
for entry in sorted(PRS_DIR.iterdir()):
|
|
403
409
|
if not entry.is_dir():
|
|
@@ -408,6 +414,9 @@ def cmd_clean(force: bool = False) -> None:
|
|
|
408
414
|
continue # doesn't match {repo}-{number} — skip silently
|
|
409
415
|
|
|
410
416
|
if force:
|
|
417
|
+
if safe and (now - entry.stat().st_mtime) < SAFE_AGE_SECONDS:
|
|
418
|
+
skipped.append((entry.name, "younger than 24h (--safe)"))
|
|
419
|
+
continue
|
|
411
420
|
shutil.rmtree(entry)
|
|
412
421
|
cleaned.append(entry.name)
|
|
413
422
|
continue
|
|
@@ -588,8 +597,9 @@ def main() -> None:
|
|
|
588
597
|
plan_mode = "-p" in args
|
|
589
598
|
non_interactive = "-nit" in args
|
|
590
599
|
force = "-f" in args or "--force" in args
|
|
600
|
+
safe = "--safe" in args
|
|
591
601
|
env_mode = "-e" in args
|
|
592
|
-
args = [a for a in args if a not in ("-n", "-r", "-e", "-f", "--force", "-p", "-nit")]
|
|
602
|
+
args = [a for a in args if a not in ("-n", "-r", "-e", "-f", "--force", "-p", "-nit", "--safe")]
|
|
593
603
|
extra_env = collect_luv_env() if env_mode else {}
|
|
594
604
|
|
|
595
605
|
if not args or args[0] in ("-h", "--help"):
|
|
@@ -603,6 +613,7 @@ Flags:
|
|
|
603
613
|
-nit non-interactive: run claude -p <prompt> and exit (no REPL)
|
|
604
614
|
-e env: pass LUV_* environment variables (with prefix stripped) into the session
|
|
605
615
|
-f, --force (with --clean) skip safety checks and delete all work folders
|
|
616
|
+
--safe (with --clean -f) only delete folders older than 24h
|
|
606
617
|
|
|
607
618
|
Commands:
|
|
608
619
|
luv --init configure default GitHub org
|
|
@@ -612,7 +623,7 @@ Commands:
|
|
|
612
623
|
luv [org/]<repo> -pr <number> [prompt] open a GitHub PR by repo + number
|
|
613
624
|
luv [org/]<repo> -n open shell in latest local clone
|
|
614
625
|
luv [org/]<repo> -r resume Claude in latest local clone
|
|
615
|
-
luv --clean [-f]
|
|
626
|
+
luv --clean [-f] [--safe] delete fully-pushed work folders
|
|
616
627
|
|
|
617
628
|
Org resolution:
|
|
618
629
|
Explicit org/repo overrides the default. Run 'luv --init' to set a default.
|
|
@@ -624,8 +635,11 @@ Docker:
|
|
|
624
635
|
"dev-environment" service. Torn down automatically on exit.""")
|
|
625
636
|
sys.exit(0)
|
|
626
637
|
|
|
638
|
+
if safe and (args[0] != "--clean" or not force):
|
|
639
|
+
die("--safe only works with --clean -f")
|
|
640
|
+
|
|
627
641
|
if args[0] == "--clean":
|
|
628
|
-
cmd_clean(force=force)
|
|
642
|
+
cmd_clean(force=force, safe=safe)
|
|
629
643
|
return
|
|
630
644
|
|
|
631
645
|
if args[0] == "--init":
|
|
@@ -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.17"
|
|
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
|