@@ -466,6 +466,19 @@ def main() -> None:
466466 default = False ,
467467 help = "Skip all tool confirmation prompts" ,
468468 )
469+ # Mutually exclusive session flags
470+ code_session_group = code_parser .add_mutually_exclusive_group ()
471+ code_session_group .add_argument (
472+ "--session" ,
473+ default = None ,
474+ help = "Connect to a specific existing session (UUID)" ,
475+ )
476+ code_session_group .add_argument (
477+ "--new" ,
478+ action = "store_true" ,
479+ default = False ,
480+ help = "Always create a new session (don't resume the latest)" ,
481+ )
469482
470483 # providers subcommand group
471484 providers_parser = subparsers .add_parser ("providers" , help = "Manage LLM providers" )
@@ -640,8 +653,113 @@ def _resolve_provider(args: argparse.Namespace) -> tuple[str, str, str]:
640653 return api_key , base_url , model
641654
642655
656+ def _probe_backend (backend_url : str ) -> bool :
657+ """Probe GET /api/system/health on the backend. Returns True if reachable and 200."""
658+ try :
659+ resp = httpx .get (f"{ backend_url } /api/system/health" , timeout = 3.0 )
660+ return resp .status_code == 200
661+ except (httpx .ConnectError , httpx .TimeoutException , httpx .ConnectTimeout ):
662+ return False
663+ except Exception :
664+ return False
665+
666+
667+ def _resolve_project_and_session (
668+ backend_url : str ,
669+ project_dir : str ,
670+ session_flag : str | None ,
671+ new_flag : bool ,
672+ ) -> tuple [str , str ]:
673+ """Resolve project_id and session_id from the backend.
674+
675+ Returns (project_id, session_id) as strings.
676+ Raises SystemExit on errors.
677+ """
678+ import uuid as _uuid
679+
680+ client = httpx .Client (base_url = backend_url , timeout = 30.0 )
681+
682+ # Get or create project by path
683+ try :
684+ resp = client .post ("/api/projects/by-path" , json = {"path" : project_dir })
685+ except httpx .ConnectError :
686+ print ("Backend not available, starting local-only session" , file = sys .stderr )
687+ raise SystemExit (None )
688+
689+ if resp .status_code not in (200 , 201 ):
690+ print (
691+ f"Warning: Failed to resolve project (HTTP { resp .status_code } ), "
692+ "starting local-only session" ,
693+ file = sys .stderr ,
694+ )
695+ raise SystemExit (None )
696+
697+ project = resp .json ()
698+ project_id = project ["id" ]
699+
700+ # Session resolution
701+ if session_flag :
702+ # Validate it looks like a UUID
703+ try :
704+ _uuid .UUID (session_flag )
705+ except ValueError :
706+ print (f"Error: Invalid session UUID: { session_flag } " , file = sys .stderr )
707+ sys .exit (1 )
708+ session_id = session_flag
709+ elif new_flag :
710+ resp = client .post (
711+ f"/api/projects/{ project_id } /sessions" ,
712+ json = {"name" : "code-session" , "engine" : "native" , "mode" : "execution" },
713+ )
714+ if resp .status_code not in (200 , 201 ):
715+ print (
716+ f"Warning: Failed to create session (HTTP { resp .status_code } ), "
717+ "starting local-only session" ,
718+ file = sys .stderr ,
719+ )
720+ raise SystemExit (None )
721+ session_id = resp .json ()["id" ]
722+ else :
723+ # List sessions, pick most recent
724+ resp = client .get (f"/api/projects/{ project_id } /sessions" )
725+ if resp .status_code == 200 :
726+ sessions = resp .json ()
727+ if sessions :
728+ # Pick the most recent by created_at
729+ most_recent = max (sessions , key = lambda s : s .get ("created_at" , "" ))
730+ session_id = most_recent ["id" ]
731+ else :
732+ # No sessions exist, create one
733+ resp = client .post (
734+ f"/api/projects/{ project_id } /sessions" ,
735+ json = {
736+ "name" : "code-session" ,
737+ "engine" : "native" ,
738+ "mode" : "execution" ,
739+ },
740+ )
741+ if resp .status_code not in (200 , 201 ):
742+ print (
743+ f"Warning: Failed to create session (HTTP { resp .status_code } ), "
744+ "starting local-only session" ,
745+ file = sys .stderr ,
746+ )
747+ raise SystemExit (None )
748+ session_id = resp .json ()["id" ]
749+ else :
750+ print (
751+ f"Warning: Failed to list sessions (HTTP { resp .status_code } ), "
752+ "starting local-only session" ,
753+ file = sys .stderr ,
754+ )
755+ raise SystemExit (None )
756+
757+ client .close ()
758+ return project_id , session_id
759+
760+
643761def _code (args : argparse .Namespace ) -> None :
644- import os
762+ import uuid as _uuid
645763
646764 from codehive .clients .terminal .code_app import CodeApp
647765
@@ -650,23 +768,59 @@ def _code(args: argparse.Namespace) -> None:
650768 print (f"Error: { project_dir } is not a directory" , file = sys .stderr )
651769 sys .exit (1 )
652770
653- api_key , base_url , model = _resolve_provider (args )
771+ backend_url = _get_base_url (args )
772+ session_flag = getattr (args , "session" , None )
773+ new_flag = getattr (args , "new" , False )
654774
655- if not api_key :
775+ # Probe backend
776+ backend_available = _probe_backend (backend_url )
777+
778+ if not backend_available :
656779 print (
657- "Error: No API key found. Set CODEHIVE_ANTHROPIC_API_KEY or "
658- "ANTHROPIC_API_KEY environment variable." ,
780+ "Backend not available, starting local-only session" ,
659781 file = sys .stderr ,
660782 )
661- sys .exit (1 )
662783
663- app = CodeApp (
664- project_dir = project_dir ,
665- model = model ,
666- api_key = api_key ,
667- base_url = base_url ,
668- auto_approve = getattr (args , "auto_approve" , False ),
669- )
784+ if backend_available :
785+ try :
786+ project_id , session_id = _resolve_project_and_session (
787+ backend_url , project_dir , session_flag , new_flag
788+ )
789+ except SystemExit :
790+ # Fall back to local mode
791+ backend_available = False
792+ project_id = None
793+ session_id = None
794+ else :
795+ project_id_uuid = _uuid .UUID (project_id )
796+ session_id_uuid = _uuid .UUID (session_id )
797+
798+ if backend_available :
799+ app = CodeApp (
800+ project_dir = project_dir ,
801+ auto_approve = getattr (args , "auto_approve" , False ),
802+ backend_url = backend_url ,
803+ project_id = project_id_uuid , # type: ignore[possibly-undefined]
804+ session_id = session_id_uuid , # type: ignore[possibly-undefined]
805+ )
806+ else :
807+ api_key , base_url , model = _resolve_provider (args )
808+
809+ if not api_key :
810+ print (
811+ "Error: No API key found. Set CODEHIVE_ANTHROPIC_API_KEY or "
812+ "ANTHROPIC_API_KEY environment variable." ,
813+ file = sys .stderr ,
814+ )
815+ sys .exit (1 )
816+
817+ app = CodeApp (
818+ project_dir = project_dir ,
819+ model = model ,
820+ api_key = api_key ,
821+ base_url = base_url ,
822+ auto_approve = getattr (args , "auto_approve" , False ),
823+ )
670824 app .run ()
671825
672826
0 commit comments