luv-cli 0.0.8__tar.gz → 0.0.10__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: luv-cli
3
- Version: 0.0.8
3
+ Version: 0.0.10
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
@@ -145,6 +145,34 @@ def docker_env_flags(env_vars: dict[str, str]) -> list[str]:
145
145
  return flags
146
146
 
147
147
 
148
+ def ensure_failproofai_installed() -> None:
149
+ """Install failproofai globally via npm — skip if already on PATH."""
150
+ if shutil.which("failproofai") is not None:
151
+ return
152
+ if shutil.which("npm") is None:
153
+ die("'npm' not found in PATH; install Node.js to use failproofai")
154
+ print("luv: installing failproofai globally...")
155
+ r = subprocess.run(["npm", "install", "-g", "failproofai"])
156
+ if r.returncode != 0:
157
+ die("'npm install -g failproofai' failed")
158
+
159
+
160
+ def run_failproofai_project(clone_dir: Path) -> None:
161
+ """Run failproofai project-scope initialization in the cloned repo.
162
+
163
+ On failure, remove the clone_dir so the next run re-clones and retries
164
+ cleanly instead of short-circuiting to a half-initialized folder.
165
+ """
166
+ print(f"luv: running failproofai project setup in {clone_dir.name}...")
167
+ r = subprocess.run(
168
+ ["npx", "-y", "failproofai", "p", "-i", "all", "--scope", "project"],
169
+ cwd=str(clone_dir),
170
+ )
171
+ if r.returncode != 0:
172
+ shutil.rmtree(clone_dir, ignore_errors=True)
173
+ die("'npx -y failproofai p -i all --scope project' failed")
174
+
175
+
148
176
  def ensure_pr_rules() -> None:
149
177
  claude_dir = Path.home() / ".claude"
150
178
  claude_md = claude_dir / "CLAUDE.md"
@@ -330,7 +358,9 @@ def resume(clone_dir: Path, extra_env: dict[str, str] = {}) -> None:
330
358
  r = subprocess.run(base + ["exec", "-it"] + docker_env_flags(extra_env) + ["dev-environment",
331
359
  "claude", "--dangerously-skip-permissions",
332
360
  "--model", "claude-opus-4-7",
333
- "--effort", "max", "--resume"])
361
+ "--effort", "max", "--resume",
362
+ "--remote-control",
363
+ "--remote-control-session-name-prefix", clone_dir.name])
334
364
  sys.exit(r.returncode)
335
365
  finally:
336
366
  stop_docker(clone_dir, compose_file, project)
@@ -340,7 +370,9 @@ def resume(clone_dir: Path, extra_env: dict[str, str] = {}) -> None:
340
370
  die("'claude' not found in PATH")
341
371
  os.environ.update(extra_env)
342
372
  os.execv(claude_bin, [claude_bin, "--dangerously-skip-permissions",
343
- "--model", "claude-opus-4-7", "--effort", "max", "--resume"])
373
+ "--model", "claude-opus-4-7", "--effort", "max", "--resume",
374
+ "--remote-control",
375
+ "--remote-control-session-name-prefix", clone_dir.name])
344
376
 
345
377
 
346
378
  def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {}) -> None:
@@ -353,7 +385,8 @@ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {})
353
385
  common_flags = ["--dangerously-skip-permissions",
354
386
  "--model", "claude-opus-4-7",
355
387
  "--effort", "max",
356
- "--remote-control"]
388
+ "--remote-control",
389
+ "--remote-control-session-name-prefix", clone_dir.name]
357
390
  if prompt:
358
391
  mode_flags = ["--permission-mode", "plan"]
359
392
  initial_args = [prompt]
@@ -517,6 +550,8 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
517
550
  if r.returncode != 0:
518
551
  die(f"git checkout {branch} failed (exit {r.returncode})")
519
552
 
553
+ run_failproofai_project(clone_dir)
554
+
520
555
  print(f"luv: ready — {clone_dir.name}, branch {branch}")
521
556
  ensure_pr_rules()
522
557
  if nav_mode:
@@ -559,6 +594,8 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
559
594
  if r.returncode != 0:
560
595
  die(f"git checkout {branch} failed (exit {r.returncode})")
561
596
 
597
+ run_failproofai_project(clone_dir)
598
+
562
599
  print(f"luv: ready — {clone_dir.name}, branch {branch}")
563
600
  ensure_pr_rules()
564
601
  if nav_mode:
@@ -617,6 +654,8 @@ Docker:
617
654
  cmd_init()
618
655
  return
619
656
 
657
+ ensure_failproofai_installed()
658
+
620
659
  # luv -l <PR URL>
621
660
  if args[0] == "-l":
622
661
  if len(args) < 2:
@@ -706,13 +745,16 @@ Docker:
706
745
  if r.returncode != 0:
707
746
  die(f"git checkout -b failed (exit {r.returncode})")
708
747
 
709
- # 6. Ensure PR rules in ~/.claude/CLAUDE.md and bypass-permissions default
748
+ # 6. Run failproofai project setup in the fresh clone
749
+ run_failproofai_project(clone_dir)
750
+
751
+ # 7. Ensure PR rules in ~/.claude/CLAUDE.md and bypass-permissions default
710
752
  ensure_pr_rules()
711
753
  ensure_default_permission_mode()
712
754
 
713
755
  print(f"luv: ready — {clone_dir.name}, branch {branch}")
714
756
 
715
- # 7. Launch claude, resume session, or open shell (replace this process)
757
+ # 8. Launch claude, resume session, or open shell (replace this process)
716
758
  if nav_mode:
717
759
  navigate(clone_dir, extra_env=extra_env)
718
760
  elif resume_mode:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "luv-cli"
7
- version = "0.0.8"
7
+ version = "0.0.10"
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