luv-cli 0.0.2__tar.gz → 0.0.3__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.
@@ -0,0 +1,316 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook SessionStart",
9
+ "timeout": 60000,
10
+ "__failproofai_hook__": true
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "SessionEnd": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook SessionEnd",
21
+ "timeout": 60000,
22
+ "__failproofai_hook__": true
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "UserPromptSubmit": [
28
+ {
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook UserPromptSubmit",
33
+ "timeout": 60000,
34
+ "__failproofai_hook__": true
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "PreToolUse": [
40
+ {
41
+ "hooks": [
42
+ {
43
+ "type": "command",
44
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PreToolUse",
45
+ "timeout": 60000,
46
+ "__failproofai_hook__": true
47
+ }
48
+ ]
49
+ }
50
+ ],
51
+ "PermissionRequest": [
52
+ {
53
+ "hooks": [
54
+ {
55
+ "type": "command",
56
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PermissionRequest",
57
+ "timeout": 60000,
58
+ "__failproofai_hook__": true
59
+ }
60
+ ]
61
+ }
62
+ ],
63
+ "PermissionDenied": [
64
+ {
65
+ "hooks": [
66
+ {
67
+ "type": "command",
68
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PermissionDenied",
69
+ "timeout": 60000,
70
+ "__failproofai_hook__": true
71
+ }
72
+ ]
73
+ }
74
+ ],
75
+ "PostToolUse": [
76
+ {
77
+ "hooks": [
78
+ {
79
+ "type": "command",
80
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PostToolUse",
81
+ "timeout": 60000,
82
+ "__failproofai_hook__": true
83
+ }
84
+ ]
85
+ }
86
+ ],
87
+ "PostToolUseFailure": [
88
+ {
89
+ "hooks": [
90
+ {
91
+ "type": "command",
92
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PostToolUseFailure",
93
+ "timeout": 60000,
94
+ "__failproofai_hook__": true
95
+ }
96
+ ]
97
+ }
98
+ ],
99
+ "Notification": [
100
+ {
101
+ "hooks": [
102
+ {
103
+ "type": "command",
104
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook Notification",
105
+ "timeout": 60000,
106
+ "__failproofai_hook__": true
107
+ }
108
+ ]
109
+ }
110
+ ],
111
+ "SubagentStart": [
112
+ {
113
+ "hooks": [
114
+ {
115
+ "type": "command",
116
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook SubagentStart",
117
+ "timeout": 60000,
118
+ "__failproofai_hook__": true
119
+ }
120
+ ]
121
+ }
122
+ ],
123
+ "SubagentStop": [
124
+ {
125
+ "hooks": [
126
+ {
127
+ "type": "command",
128
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook SubagentStop",
129
+ "timeout": 60000,
130
+ "__failproofai_hook__": true
131
+ }
132
+ ]
133
+ }
134
+ ],
135
+ "TaskCreated": [
136
+ {
137
+ "hooks": [
138
+ {
139
+ "type": "command",
140
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook TaskCreated",
141
+ "timeout": 60000,
142
+ "__failproofai_hook__": true
143
+ }
144
+ ]
145
+ }
146
+ ],
147
+ "TaskCompleted": [
148
+ {
149
+ "hooks": [
150
+ {
151
+ "type": "command",
152
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook TaskCompleted",
153
+ "timeout": 60000,
154
+ "__failproofai_hook__": true
155
+ }
156
+ ]
157
+ }
158
+ ],
159
+ "Stop": [
160
+ {
161
+ "hooks": [
162
+ {
163
+ "type": "command",
164
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook Stop",
165
+ "timeout": 60000,
166
+ "__failproofai_hook__": true
167
+ }
168
+ ]
169
+ }
170
+ ],
171
+ "StopFailure": [
172
+ {
173
+ "hooks": [
174
+ {
175
+ "type": "command",
176
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook StopFailure",
177
+ "timeout": 60000,
178
+ "__failproofai_hook__": true
179
+ }
180
+ ]
181
+ }
182
+ ],
183
+ "TeammateIdle": [
184
+ {
185
+ "hooks": [
186
+ {
187
+ "type": "command",
188
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook TeammateIdle",
189
+ "timeout": 60000,
190
+ "__failproofai_hook__": true
191
+ }
192
+ ]
193
+ }
194
+ ],
195
+ "InstructionsLoaded": [
196
+ {
197
+ "hooks": [
198
+ {
199
+ "type": "command",
200
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook InstructionsLoaded",
201
+ "timeout": 60000,
202
+ "__failproofai_hook__": true
203
+ }
204
+ ]
205
+ }
206
+ ],
207
+ "ConfigChange": [
208
+ {
209
+ "hooks": [
210
+ {
211
+ "type": "command",
212
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook ConfigChange",
213
+ "timeout": 60000,
214
+ "__failproofai_hook__": true
215
+ }
216
+ ]
217
+ }
218
+ ],
219
+ "CwdChanged": [
220
+ {
221
+ "hooks": [
222
+ {
223
+ "type": "command",
224
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook CwdChanged",
225
+ "timeout": 60000,
226
+ "__failproofai_hook__": true
227
+ }
228
+ ]
229
+ }
230
+ ],
231
+ "FileChanged": [
232
+ {
233
+ "hooks": [
234
+ {
235
+ "type": "command",
236
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook FileChanged",
237
+ "timeout": 60000,
238
+ "__failproofai_hook__": true
239
+ }
240
+ ]
241
+ }
242
+ ],
243
+ "WorktreeCreate": [
244
+ {
245
+ "hooks": [
246
+ {
247
+ "type": "command",
248
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook WorktreeCreate",
249
+ "timeout": 60000,
250
+ "__failproofai_hook__": true
251
+ }
252
+ ]
253
+ }
254
+ ],
255
+ "WorktreeRemove": [
256
+ {
257
+ "hooks": [
258
+ {
259
+ "type": "command",
260
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook WorktreeRemove",
261
+ "timeout": 60000,
262
+ "__failproofai_hook__": true
263
+ }
264
+ ]
265
+ }
266
+ ],
267
+ "PreCompact": [
268
+ {
269
+ "hooks": [
270
+ {
271
+ "type": "command",
272
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PreCompact",
273
+ "timeout": 60000,
274
+ "__failproofai_hook__": true
275
+ }
276
+ ]
277
+ }
278
+ ],
279
+ "PostCompact": [
280
+ {
281
+ "hooks": [
282
+ {
283
+ "type": "command",
284
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook PostCompact",
285
+ "timeout": 60000,
286
+ "__failproofai_hook__": true
287
+ }
288
+ ]
289
+ }
290
+ ],
291
+ "Elicitation": [
292
+ {
293
+ "hooks": [
294
+ {
295
+ "type": "command",
296
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook Elicitation",
297
+ "timeout": 60000,
298
+ "__failproofai_hook__": true
299
+ }
300
+ ]
301
+ }
302
+ ],
303
+ "ElicitationResult": [
304
+ {
305
+ "hooks": [
306
+ {
307
+ "type": "command",
308
+ "command": "\"/home/ubuntu/.nvm/versions/node/v24.14.0/bin/failproofai\" --hook ElicitationResult",
309
+ "timeout": 60000,
310
+ "__failproofai_hook__": true
311
+ }
312
+ ]
313
+ }
314
+ ]
315
+ }
316
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "enabledPolicies": [
3
+ "sanitize-jwt",
4
+ "sanitize-api-keys",
5
+ "sanitize-connection-strings",
6
+ "sanitize-private-key-content",
7
+ "sanitize-bearer-tokens",
8
+ "protect-env-vars",
9
+ "block-env-files",
10
+ "block-read-outside-cwd",
11
+ "block-sudo",
12
+ "block-curl-pipe-sh",
13
+ "block-rm-rf",
14
+ "block-failproofai-commands",
15
+ "block-secrets-write",
16
+ "block-push-master",
17
+ "block-force-push",
18
+ "block-work-on-main",
19
+ "warn-git-amend",
20
+ "warn-git-stash-drop",
21
+ "warn-all-files-staged",
22
+ "warn-destructive-sql",
23
+ "warn-schema-alteration",
24
+ "warn-package-publish",
25
+ "warn-global-package-install",
26
+ "warn-large-file-write",
27
+ "warn-background-process",
28
+ "warn-repeated-tool-calls",
29
+ "require-commit-before-stop",
30
+ "require-push-before-stop",
31
+ "require-pr-before-stop",
32
+ "require-ci-green-before-stop"
33
+ ],
34
+ "policyParams": {
35
+ "block-force-push": {
36
+ "hint": "Create a new branch from your current HEAD (e.g. `git checkout -b <new-branch>`) and push that instead."
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,4 @@
1
+ __pycache__/
2
+ *.pyc
3
+ dist/
4
+ *.egg-info/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: luv-cli
3
- Version: 0.0.2
3
+ Version: 0.0.3
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,7 @@ 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
+ | `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
97
98
  | `-f`, `--force` | Skip safety checks (with `--clean`) |
98
99
 
99
100
  ## Docker dev environments
@@ -71,6 +71,7 @@ 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
+ | `-e` | Env: pass `LUV_*` environment variables (with prefix stripped) into the session |
74
75
  | `-f`, `--force` | Skip safety checks (with `--clean`) |
75
76
 
76
77
  ## Docker dev environments
@@ -119,6 +119,23 @@ def trust_project(path: Path) -> None:
119
119
  os.replace(tmp_path, CLAUDE_JSON)
120
120
 
121
121
 
122
+ def collect_luv_env() -> dict[str, str]:
123
+ """Collect LUV_* env vars, strip prefix, return as dict."""
124
+ result = {}
125
+ for key, value in os.environ.items():
126
+ if key.startswith("LUV_") and len(key) > 4:
127
+ result[key[4:]] = value
128
+ return result
129
+
130
+
131
+ def docker_env_flags(env_vars: dict[str, str]) -> list[str]:
132
+ """Convert env dict to docker compose exec -e flags."""
133
+ flags: list[str] = []
134
+ for key, value in env_vars.items():
135
+ flags.extend(["-e", f"{key}={value}"])
136
+ return flags
137
+
138
+
122
139
  def ensure_pr_rules() -> None:
123
140
  claude_dir = Path.home() / ".claude"
124
141
  claude_md = claude_dir / "CLAUDE.md"
@@ -229,7 +246,7 @@ def stop_docker(clone_dir: Path, compose_file: str, project: str) -> None:
229
246
  subprocess.run(base + ["down", "-v", "--remove-orphans"])
230
247
 
231
248
 
232
- def navigate(clone_dir: Path) -> None:
249
+ def navigate(clone_dir: Path, extra_env: dict[str, str] = {}) -> None:
233
250
  """Chdir into the work folder and exec a shell — replacing this process."""
234
251
  os.chdir(str(clone_dir))
235
252
  settings = load_luv_settings(clone_dir)
@@ -240,16 +257,17 @@ def navigate(clone_dir: Path) -> None:
240
257
  start_docker(clone_dir, compose_file, project)
241
258
  try:
242
259
  base = docker_compose_base(clone_dir, compose_file, project)
243
- r = subprocess.run(base + ["exec", "-it", "dev-environment", "bash"])
260
+ r = subprocess.run(base + ["exec", "-it"] + docker_env_flags(extra_env) + ["dev-environment", "bash"])
244
261
  sys.exit(r.returncode)
245
262
  finally:
246
263
  stop_docker(clone_dir, compose_file, project)
247
264
  else:
248
265
  shell = os.environ.get("SHELL", "/bin/bash")
266
+ os.environ.update(extra_env)
249
267
  os.execv(shell, [shell])
250
268
 
251
269
 
252
- def resume(clone_dir: Path) -> None:
270
+ def resume(clone_dir: Path, extra_env: dict[str, str] = {}) -> None:
253
271
  """Trust, chdir, and exec claude --resume — replacing this process."""
254
272
  trust_project(clone_dir)
255
273
  os.chdir(str(clone_dir))
@@ -261,7 +279,7 @@ def resume(clone_dir: Path) -> None:
261
279
  start_docker(clone_dir, compose_file, project)
262
280
  try:
263
281
  base = docker_compose_base(clone_dir, compose_file, project)
264
- r = subprocess.run(base + ["exec", "-it", "dev-environment",
282
+ r = subprocess.run(base + ["exec", "-it"] + docker_env_flags(extra_env) + ["dev-environment",
265
283
  "claude", "--dangerously-skip-permissions",
266
284
  "--model", "claude-opus-4-6",
267
285
  "--effort", "max", "--resume"])
@@ -272,11 +290,12 @@ def resume(clone_dir: Path) -> None:
272
290
  claude_bin = shutil.which("claude")
273
291
  if not claude_bin:
274
292
  die("'claude' not found in PATH")
293
+ os.environ.update(extra_env)
275
294
  os.execv(claude_bin, [claude_bin, "--dangerously-skip-permissions",
276
295
  "--model", "claude-opus-4-6", "--effort", "max", "--resume"])
277
296
 
278
297
 
279
- def launch(clone_dir: Path, prompt: str | None) -> None:
298
+ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {}) -> None:
280
299
  """Trust, resolve claude, chdir, and exec — replacing this process."""
281
300
  trust_project(clone_dir)
282
301
  os.chdir(str(clone_dir))
@@ -293,7 +312,7 @@ def launch(clone_dir: Path, prompt: str | None) -> None:
293
312
  "--model", "claude-opus-4-6", "--effort", "max"]
294
313
  if prompt:
295
314
  claude_cmd.append(f"/plan {prompt}")
296
- r = subprocess.run(base + ["exec", "-it", "dev-environment"] + claude_cmd)
315
+ r = subprocess.run(base + ["exec", "-it"] + docker_env_flags(extra_env) + ["dev-environment"] + claude_cmd)
297
316
  sys.exit(r.returncode)
298
317
  finally:
299
318
  stop_docker(clone_dir, compose_file, project)
@@ -304,6 +323,7 @@ def launch(clone_dir: Path, prompt: str | None) -> None:
304
323
  base_args = [claude_bin, "--dangerously-skip-permissions",
305
324
  "--permission-mode", "bypassPermissions",
306
325
  "--model", "claude-opus-4-6", "--effort", "max"]
326
+ os.environ.update(extra_env)
307
327
  if prompt:
308
328
  os.execv(claude_bin, base_args + [f"/plan {prompt}"])
309
329
  else:
@@ -415,7 +435,7 @@ def find_latest_clone(repo: str) -> Path | None:
415
435
  return best
416
436
 
417
437
 
418
- def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool = False, resume_mode: bool = False) -> None:
438
+ 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:
419
439
  """Open an existing work folder or remote branch by number."""
420
440
  clone_dir = PRS_DIR / f"{repo}-{number}"
421
441
 
@@ -424,11 +444,11 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
424
444
  print(f"luv: opening existing folder {clone_dir.name}")
425
445
  ensure_pr_rules()
426
446
  if nav_mode:
427
- navigate(clone_dir)
447
+ navigate(clone_dir, extra_env=extra_env)
428
448
  elif resume_mode:
429
- resume(clone_dir)
449
+ resume(clone_dir, extra_env=extra_env)
430
450
  else:
431
- launch(clone_dir, prompt)
451
+ launch(clone_dir, prompt, extra_env=extra_env)
432
452
  return # unreachable
433
453
 
434
454
  # 2. Check remote branch luv-{number}
@@ -451,14 +471,14 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
451
471
  print(f"luv: ready — {clone_dir.name}, branch {branch}")
452
472
  ensure_pr_rules()
453
473
  if nav_mode:
454
- navigate(clone_dir)
474
+ navigate(clone_dir, extra_env=extra_env)
455
475
  elif resume_mode:
456
- resume(clone_dir)
476
+ resume(clone_dir, extra_env=extra_env)
457
477
  else:
458
- launch(clone_dir, prompt)
478
+ launch(clone_dir, prompt, extra_env=extra_env)
459
479
 
460
480
 
461
- def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool = False, resume_mode: bool = False) -> None:
481
+ 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:
462
482
  """Open any GitHub PR by org/repo/number, cloning if needed."""
463
483
  clone_dir = PRS_DIR / f"{repo}-{number}"
464
484
 
@@ -466,11 +486,11 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
466
486
  print(f"luv: opening existing folder {clone_dir.name}")
467
487
  ensure_pr_rules()
468
488
  if nav_mode:
469
- navigate(clone_dir)
489
+ navigate(clone_dir, extra_env=extra_env)
470
490
  elif resume_mode:
471
- resume(clone_dir)
491
+ resume(clone_dir, extra_env=extra_env)
472
492
  else:
473
- launch(clone_dir, prompt)
493
+ launch(clone_dir, prompt, extra_env=extra_env)
474
494
  return # unreachable
475
495
 
476
496
  # Resolve the actual branch name via GitHub API
@@ -493,11 +513,11 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
493
513
  print(f"luv: ready — {clone_dir.name}, branch {branch}")
494
514
  ensure_pr_rules()
495
515
  if nav_mode:
496
- navigate(clone_dir)
516
+ navigate(clone_dir, extra_env=extra_env)
497
517
  elif resume_mode:
498
- resume(clone_dir)
518
+ resume(clone_dir, extra_env=extra_env)
499
519
  else:
500
- launch(clone_dir, prompt)
520
+ launch(clone_dir, prompt, extra_env=extra_env)
501
521
 
502
522
 
503
523
  def main() -> None:
@@ -506,7 +526,9 @@ def main() -> None:
506
526
  nav_mode = "-n" in args
507
527
  resume_mode = "-r" in args
508
528
  force = "-f" in args or "--force" in args
509
- args = [a for a in args if a not in ("-n", "-r", "-f", "--force")]
529
+ env_mode = "-e" in args
530
+ args = [a for a in args if a not in ("-n", "-r", "-e", "-f", "--force")]
531
+ extra_env = collect_luv_env() if env_mode else {}
510
532
 
511
533
  if not args or args[0] in ("-h", "--help"):
512
534
  print("""\
@@ -515,6 +537,7 @@ Usage: luv [flags] <command>
515
537
  Flags:
516
538
  -n navigate: open a shell in the work folder instead of launching Claude
517
539
  -r resume: resume the last Claude session in the work folder
540
+ -e env: pass LUV_* environment variables (with prefix stripped) into the session
518
541
  -f, --force (with --clean) skip safety checks and delete all work folders
519
542
 
520
543
  Commands:
@@ -555,7 +578,7 @@ Docker:
555
578
  die(f"cannot parse PR URL: {url}")
556
579
  org, repo, number = m.group(1), m.group(2), int(m.group(3))
557
580
  prompt = " ".join(args[2:]) or None
558
- open_pr(org, repo, number, prompt, nav_mode, resume_mode)
581
+ open_pr(org, repo, number, prompt, nav_mode, resume_mode, extra_env=extra_env)
559
582
  return
560
583
 
561
584
  raw = args[0].rstrip("/")
@@ -575,14 +598,14 @@ Docker:
575
598
  die(f"expected a PR number after -pr, got '{args[idx + 1]}'")
576
599
  prompt_parts = [a for i, a in enumerate(args) if i not in (0, idx, idx + 1)]
577
600
  prompt = " ".join(prompt_parts) or None
578
- open_pr(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode)
601
+ open_pr(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode, extra_env=extra_env)
579
602
  return
580
603
 
581
604
  # Detect optional numeric second argument
582
605
  if len(args) > 1 and args[1].isdigit():
583
606
  number = int(args[1])
584
607
  prompt = " ".join(args[2:]) or None
585
- open_existing(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode)
608
+ open_existing(resolve_org(explicit_org), repo, number, prompt, nav_mode, resume_mode, extra_env=extra_env)
586
609
  return
587
610
 
588
611
  org = resolve_org(explicit_org)
@@ -595,9 +618,9 @@ Docker:
595
618
  die(f"no local clones of '{repo}' found in {PRS_DIR}")
596
619
  print(f"luv: opening latest clone {clone_dir.name}")
597
620
  if nav_mode:
598
- navigate(clone_dir)
621
+ navigate(clone_dir, extra_env=extra_env)
599
622
  else:
600
- resume(clone_dir)
623
+ resume(clone_dir, extra_env=extra_env)
601
624
  return
602
625
 
603
626
  # 1. Verify repo exists
@@ -641,8 +664,8 @@ Docker:
641
664
 
642
665
  # 7. Launch claude, resume session, or open shell (replace this process)
643
666
  if nav_mode:
644
- navigate(clone_dir)
667
+ navigate(clone_dir, extra_env=extra_env)
645
668
  elif resume_mode:
646
- resume(clone_dir)
669
+ resume(clone_dir, extra_env=extra_env)
647
670
  else:
648
- launch(clone_dir, prompt)
671
+ launch(clone_dir, prompt, 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.2"
7
+ version = "0.0.3"
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