Coverage for src / lilbee / runtime / launcher.py: 100%

35 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-06-28 01:01 +0000

1"""Thin CLI entry point: shows splash animation while heavy deps load. 

2 

3This module imports only ``splash`` (which uses only stdlib + subprocess), 

4launches the animation process, then performs the heavy 

5``from lilbee.cli import app`` import while the bee animates on stderr. 

6""" 

7 

8from __future__ import annotations 

9 

10import contextlib 

11import io 

12import os 

13import sys 

14 

15# Shells fetch tab completions by running the console script with 

16# ``_<PROG>_COMPLETE=...`` set (and an empty argv); click answers those itself. 

17_COMPLETION_ENV_SUFFIX = "_COMPLETE" 

18 

19 

20def _shell_completion_active() -> bool: 

21 """True when this process was spawned by a shell's tab-completion.""" 

22 return any( 

23 name.startswith("_") and name.endswith(_COMPLETION_ENV_SUFFIX) for name in os.environ 

24 ) 

25 

26 

27def _force_utf8_stdio() -> None: 

28 """Make stdio UTF-8 so a no-locale (GUI-spawned) launch can't crash on non-ASCII output.""" 

29 for stream in (sys.stdout, sys.stderr): 

30 if isinstance(stream, io.TextIOWrapper): 

31 # Swallow on an already-detached stream; non-TextIOWrapper streams 

32 # (StringIO redirects, capture shims) need no reconfiguration. 

33 with contextlib.suppress(ValueError, OSError): 

34 stream.reconfigure(encoding="utf-8", errors="backslashreplace") 

35 

36 

37def main() -> None: 

38 """Entry point for the ``lilbee`` console script.""" 

39 _force_utf8_stdio() 

40 args = sys.argv[1:] 

41 is_interactive = (not args or args[0] in ("chat", "")) and not _shell_completion_active() 

42 

43 if not is_interactive: 

44 from lilbee.cli import app 

45 

46 app() 

47 return 

48 

49 from lilbee.runtime.splash import start, stop 

50 

51 handle = start() 

52 

53 try: 

54 from lilbee.cli import app 

55 except BaseException: 

56 stop(handle) 

57 raise 

58 else: 

59 # Stop the splash BEFORE the TUI takes over the terminal so the 

60 # subprocess's final writes don't land on Textual's alt-screen. 

61 stop(handle) 

62 

63 try: 

64 app() 

65 except KeyboardInterrupt: 

66 sys.stderr.write("\033[?25h") 

67 sys.stderr.flush() 

68 raise SystemExit(130) from None