@@ -347,7 +347,8 @@ def resume(clone_dir: Path, extra_env: dict[str, str] = {}) -> None:
347347 "--remote-control-session-name-prefix" , clone_dir .name ])
348348
349349
350- def launch (clone_dir : Path , prompt : str | None , extra_env : dict [str , 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 :
351352 """Trust, resolve claude, chdir, and exec — replacing this process."""
352353 trust_project (clone_dir )
353354 os .chdir (str (clone_dir ))
@@ -359,12 +360,17 @@ def launch(clone_dir: Path, prompt: str | None, extra_env: dict[str, str] = {})
359360 "--effort" , "max" ,
360361 "--remote-control" ,
361362 "--remote-control-session-name-prefix" , clone_dir .name ]
362- if prompt :
363+ if non_interactive :
364+ if not prompt :
365+ die ("-nit requires a prompt" )
366+ mode_flags = []
367+ initial_args = ["-p" , prompt ]
368+ elif plan_mode :
363369 mode_flags = ["--permission-mode" , "plan" ]
364- initial_args = [prompt ]
370+ initial_args = [prompt ] if prompt else [ f"/color { pick_color () } " ]
365371 else :
366372 mode_flags = ["--permission-mode" , "bypassPermissions" ]
367- initial_args = [f"/color { pick_color ()} " ]
373+ initial_args = [prompt ] if prompt else [ f"/color { pick_color ()} " ]
368374
369375 if compose_file :
370376 project = docker_project_name (clone_dir )
@@ -489,7 +495,7 @@ def find_latest_clone(repo: str) -> Path | None:
489495 return best
490496
491497
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 :
498+ 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 :
493499 """Open an existing work folder or remote branch by number."""
494500 clone_dir = PRS_DIR / f"{ repo } -{ number } "
495501
@@ -502,7 +508,7 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
502508 elif resume_mode :
503509 resume (clone_dir , extra_env = extra_env )
504510 else :
505- launch (clone_dir , prompt , extra_env = extra_env )
511+ launch (clone_dir , prompt , plan_mode = plan_mode , non_interactive = non_interactive , extra_env = extra_env )
506512 return # unreachable
507513
508514 # 2. Check remote branch luv-{number}
@@ -529,10 +535,10 @@ def open_existing(org: str, repo: str, number: int, prompt: str | None, nav_mode
529535 elif resume_mode :
530536 resume (clone_dir , extra_env = extra_env )
531537 else :
532- launch (clone_dir , prompt , extra_env = extra_env )
538+ launch (clone_dir , prompt , plan_mode = plan_mode , non_interactive = non_interactive , extra_env = extra_env )
533539
534540
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 :
541+ 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 :
536542 """Open any GitHub PR by org/repo/number, cloning if needed."""
537543 clone_dir = PRS_DIR / f"{ repo } -{ number } "
538544
@@ -544,7 +550,7 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
544550 elif resume_mode :
545551 resume (clone_dir , extra_env = extra_env )
546552 else :
547- launch (clone_dir , prompt , extra_env = extra_env )
553+ launch (clone_dir , prompt , plan_mode = plan_mode , non_interactive = non_interactive , extra_env = extra_env )
548554 return # unreachable
549555
550556 # Resolve the actual branch name via GitHub API
@@ -571,17 +577,19 @@ def open_pr(org: str, repo: str, number: int, prompt: str | None, nav_mode: bool
571577 elif resume_mode :
572578 resume (clone_dir , extra_env = extra_env )
573579 else :
574- launch (clone_dir , prompt , extra_env = extra_env )
580+ launch (clone_dir , prompt , plan_mode = plan_mode , non_interactive = non_interactive , extra_env = extra_env )
575581
576582
577583def main () -> None :
578584 args = sys .argv [1 :]
579585
580586 nav_mode = "-n" in args
581587 resume_mode = "-r" in args
588+ plan_mode = "-p" in args
589+ non_interactive = "-nit" in args
582590 force = "-f" in args or "--force" in args
583591 env_mode = "-e" in args
584- args = [a for a in args if a not in ("-n" , "-r" , "-e" , "-f" , "--force" )]
592+ args = [a for a in args if a not in ("-n" , "-r" , "-e" , "-f" , "--force" , "-p" , "-nit" )]
585593 extra_env = collect_luv_env () if env_mode else {}
586594
587595 if not args or args [0 ] in ("-h" , "--help" ):
@@ -591,6 +599,8 @@ def main() -> None:
591599Flags:
592600 -n navigate: open a shell in the work folder instead of launching Claude
593601 -r resume: resume the last Claude session in the work folder
602+ -p launch Claude in plan permission mode (default: bypassPermissions)
603+ -nit non-interactive: run claude -p <prompt> and exit (no REPL)
594604 -e env: pass LUV_* environment variables (with prefix stripped) into the session
595605 -f, --force (with --clean) skip safety checks and delete all work folders
596606
@@ -632,7 +642,7 @@ def main() -> None:
632642 die (f"cannot parse PR URL: { url } " )
633643 org , repo , number = m .group (1 ), m .group (2 ), int (m .group (3 ))
634644 prompt = " " .join (args [2 :]) or None
635- open_pr (org , repo , number , prompt , nav_mode , resume_mode , extra_env = extra_env )
645+ open_pr (org , repo , number , prompt , nav_mode , resume_mode , plan_mode , non_interactive , extra_env = extra_env )
636646 return
637647
638648 raw = args [0 ].rstrip ("/" )
@@ -652,14 +662,14 @@ def main() -> None:
652662 die (f"expected a PR number after -pr, got '{ args [idx + 1 ]} '" )
653663 prompt_parts = [a for i , a in enumerate (args ) if i not in (0 , idx , idx + 1 )]
654664 prompt = " " .join (prompt_parts ) or None
655- open_pr (resolve_org (explicit_org ), repo , number , prompt , nav_mode , resume_mode , extra_env = extra_env )
665+ open_pr (resolve_org (explicit_org ), repo , number , prompt , nav_mode , resume_mode , plan_mode , non_interactive , extra_env = extra_env )
656666 return
657667
658668 # Detect optional numeric second argument
659669 if len (args ) > 1 and args [1 ].isdigit ():
660670 number = int (args [1 ])
661671 prompt = " " .join (args [2 :]) or None
662- open_existing (resolve_org (explicit_org ), repo , number , prompt , nav_mode , resume_mode , extra_env = extra_env )
672+ open_existing (resolve_org (explicit_org ), repo , number , prompt , nav_mode , resume_mode , plan_mode , non_interactive , extra_env = extra_env )
663673 return
664674
665675 org = resolve_org (explicit_org )
@@ -728,4 +738,4 @@ def _latest(endpoint: str) -> int:
728738 elif resume_mode :
729739 resume (clone_dir , extra_env = extra_env )
730740 else :
731- launch (clone_dir , prompt , extra_env = extra_env )
741+ launch (clone_dir , prompt , plan_mode = plan_mode , non_interactive = non_interactive , extra_env = extra_env )
0 commit comments