@@ -152,12 +152,50 @@ def short_location(provider: Provider) -> str:
152152
153153@app .command ("add" )
154154async def add_agent (
155- location : typing .Annotated [str , typer .Argument (help = "Agent location (public docker image or github url)" )],
155+ location : typing .Annotated [
156+ str | None , typer .Argument (help = "Agent location (public docker image or github url)" )
157+ ] = None ,
156158 dockerfile : typing .Annotated [str | None , typer .Option (help = "Use custom dockerfile path" )] = None ,
157159 verbose : typing .Annotated [bool , typer .Option ("-v" , "--verbose" , help = "Show verbose output" )] = False ,
158160 yes : typing .Annotated [bool , typer .Option ("--yes" , "-y" , help = "Skip confirmation prompts." )] = False ,
159161) -> None :
160162 """Add a docker image or GitHub repository [aliases: install]"""
163+ if location is None :
164+ repo_input = (
165+ await inquirer .text ( # pyright: ignore[reportPrivateImportUsage]
166+ message = "Enter GitHub repository (owner/repo or full URL):" ,
167+ ).execute_async ()
168+ or ""
169+ )
170+
171+ match = re .search (r"^(?:(?:https?://)?(?:www\.)?github\.com/)?([^/]+)/([^/?&]+)" , repo_input )
172+ if not match :
173+ raise ValueError (f"Invalid GitHub URL format: { repo_input } . Expected 'owner/repo' or a full GitHub URL." )
174+
175+ owner , repo = match .group (1 ), match .group (2 ).removesuffix (".git" )
176+
177+ async with httpx .AsyncClient () as client :
178+ response = await client .get (
179+ f"https://api.github.com/repos/{ owner } /{ repo } /tags" ,
180+ headers = {"Accept" : "application/vnd.github.v3+json" },
181+ )
182+ tags = [tag ["name" ] for tag in response .json ()] if response .status_code == 200 else []
183+
184+ if tags :
185+ selected_tag = await inquirer .fuzzy ( # pyright: ignore[reportPrivateImportUsage]
186+ message = "Select a tag to use:" ,
187+ choices = tags ,
188+ ).execute_async ()
189+ else :
190+ selected_tag = (
191+ await inquirer .text ( # pyright: ignore[reportPrivateImportUsage]
192+ message = "Enter tag to use:" ,
193+ ).execute_async ()
194+ or "main"
195+ )
196+
197+ location = f"https://github.com/{ owner } /{ repo } @{ selected_tag } "
198+
161199 url = announce_server_action (f"Installing agent '{ location } ' for" )
162200 await confirm_server_action ("Proceed with installing this agent on" , url = url , yes = yes )
163201 with verbosity (verbose ):
@@ -770,24 +808,41 @@ def handler() -> str:
770808@app .command ("run" )
771809async def run_agent (
772810 search_path : typing .Annotated [
773- str , typer .Argument (..., help = "Short ID, agent name or part of the provider location" )
774- ],
811+ str | None ,
812+ typer .Argument (
813+ help = "Short ID, agent name or part of the provider location" ,
814+ ),
815+ ] = None ,
775816 input : typing .Annotated [
776817 str | None ,
777818 typer .Argument (
778- default_factory = lambda : None if sys .stdin .isatty () else sys .stdin .read (),
779819 help = "Agent input as text or JSON" ,
780820 ),
781- ],
821+ ] = None ,
782822 dump_files : typing .Annotated [
783823 Path | None , typer .Option (help = "Folder path to save any files returned by the agent" )
784824 ] = None ,
785825) -> None :
786826 """Run an agent."""
787- announce_server_action (f"Running agent '{ search_path } ' on" )
827+ if search_path is not None and input is None and sys .stdin .isatty ():
828+ input = sys .stdin .read ()
788829 async with configuration .use_platform_client ():
789830 providers = await Provider .list ()
790831 await ensure_llm_provider ()
832+
833+ if search_path is None :
834+ if not providers :
835+ err_console .error ("No agents found. Add an agent first using 'agentstack agent add'." )
836+ sys .exit (1 )
837+ search_path = await inquirer .fuzzy ( # pyright: ignore[reportPrivateImportUsage]
838+ message = "Select an agent to run:" ,
839+ choices = [provider .agent_card .name for provider in providers ],
840+ ).execute_async ()
841+ if search_path is None :
842+ err_console .error ("No agent selected. Exiting." )
843+ sys .exit (1 )
844+
845+ announce_server_action (f"Running agent '{ search_path } ' on" )
791846 provider = select_provider (search_path , providers = providers )
792847
793848 context = await Context .create (
0 commit comments