@@ -262,12 +262,71 @@ def get_current_template_commit(project_path: Path) -> str | None:
262262 return None
263263
264264
265+ def _get_versions_from_github (include_prereleases : bool = False ) -> list [str ]:
266+ """
267+ Fetch available versions from GitHub API when not in a git repo.
268+
269+ Used when running from installed package (pip/uvx) instead of git checkout.
270+
271+ Args:
272+ include_prereleases: Include pre-release versions (rc, alpha, beta, dev)
273+
274+ Returns:
275+ List of version strings sorted by PEP 440 (newest first)
276+ """
277+ import json
278+ import urllib .request
279+ from urllib .parse import urlparse
280+
281+ github_url = GITHUB_REPO_URL
282+
283+ # Extract owner/repo from GITHUB_REPO_URL (e.g., "lbedner/aegis-stack")
284+ parsed = urlparse (github_url )
285+ repo_path = parsed .path .strip ("/" ) # "lbedner/aegis-stack"
286+
287+ api_url = f"https://api.github.com/repos/{ repo_path } /tags?per_page=100"
288+
289+ try :
290+ req = urllib .request .Request (
291+ api_url ,
292+ headers = {
293+ "Accept" : "application/vnd.github.v3+json" ,
294+ "User-Agent" : "aegis-stack-cli" ,
295+ },
296+ )
297+ with urllib .request .urlopen (req , timeout = 10 ) as response :
298+ data = json .loads (response .read ().decode ())
299+
300+ # Parse tags from API response
301+ versions = []
302+ for tag_info in data :
303+ tag_name = tag_info .get ("name" , "" )
304+ if tag_name .startswith ("v" ):
305+ version_str = tag_name [1 :] # Remove 'v' prefix
306+ try :
307+ parsed_ver = parse (version_str )
308+ if not include_prereleases and parsed_ver .is_prerelease :
309+ continue
310+ versions .append (version_str )
311+ except Exception :
312+ continue
313+
314+ # Sort by PEP 440 (newest first)
315+ versions .sort (key = parse , reverse = True )
316+ return versions
317+
318+ except Exception :
319+ return []
320+
321+
265322def get_available_versions (
266323 template_root : Path | None = None , include_prereleases : bool = False
267324) -> list [str ]:
268325 """
269326 Get list of available template versions from git tags.
270327
328+ Falls back to GitHub API when not in a git repository (installed package mode).
329+
271330 Args:
272331 template_root: Path to template repository (default: auto-detect)
273332 include_prereleases: Include pre-release versions (rc, alpha, beta, dev)
@@ -278,6 +337,11 @@ def get_available_versions(
278337 if template_root is None :
279338 template_root = get_template_root ()
280339
340+ # Check if we're in a git repository
341+ if not (template_root / ".git" ).exists ():
342+ # Fall back to GitHub API (installed package mode)
343+ return _get_versions_from_github (include_prereleases )
344+
281345 try :
282346 result = subprocess .run (
283347 ["git" , "tag" , "--list" , "v*" ],
@@ -293,20 +357,21 @@ def get_available_versions(
293357 if tag .startswith ("v" ):
294358 version_str = tag [1 :] # Remove 'v' prefix
295359 try :
296- parsed = parse (version_str ) # Validate version
360+ parsed_ver = parse (version_str ) # Validate version
297361 # Skip pre-releases (rc, alpha, beta, dev) unless explicitly requested
298- if not include_prereleases and parsed .is_prerelease :
362+ if not include_prereleases and parsed_ver .is_prerelease :
299363 continue
300364 versions .append (version_str )
301365 except Exception :
302366 continue
303367
304368 # Sort by PEP 440 (newest first)
305- versions .sort (key = lambda v : parse ( v ) , reverse = True )
369+ versions .sort (key = parse , reverse = True )
306370 return versions
307371
308372 except Exception :
309- return []
373+ # Fall back to GitHub API on any git error
374+ return _get_versions_from_github (include_prereleases )
310375
311376
312377def get_latest_version (template_root : Path | None = None ) -> str | None :
0 commit comments