@@ -2210,6 +2210,10 @@ def preset_info(
22102210 if license_val :
22112211 console .print (f" License: { license_val } " )
22122212 console .print ("\n [green]Status: installed[/green]" )
2213+ # Get priority from registry
2214+ pack_metadata = manager .registry .get (pack_id )
2215+ priority = pack_metadata .get ("priority" , 10 ) if pack_metadata else 10
2216+ console .print (f" [dim]Priority:[/dim] { priority } " )
22132217 console .print ()
22142218 return
22152219
@@ -2241,6 +2245,53 @@ def preset_info(
22412245 console .print ()
22422246
22432247
2248+ @preset_app .command ("set-priority" )
2249+ def preset_set_priority (
2250+ pack_id : str = typer .Argument (help = "Preset ID" ),
2251+ priority : int = typer .Argument (help = "New priority (lower = higher precedence)" ),
2252+ ):
2253+ """Set the resolution priority of an installed preset."""
2254+ from .presets import PresetManager
2255+
2256+ project_root = Path .cwd ()
2257+
2258+ # Check if we're in a spec-kit project
2259+ specify_dir = project_root / ".specify"
2260+ if not specify_dir .exists ():
2261+ console .print ("[red]Error:[/red] Not a spec-kit project (no .specify/ directory)" )
2262+ console .print ("Run this command from a spec-kit project root" )
2263+ raise typer .Exit (1 )
2264+
2265+ # Validate priority
2266+ if priority < 1 :
2267+ console .print ("[red]Error:[/red] Priority must be a positive integer (1 or higher)" )
2268+ raise typer .Exit (1 )
2269+
2270+ manager = PresetManager (project_root )
2271+
2272+ # Check if preset is installed
2273+ if not manager .registry .is_installed (pack_id ):
2274+ console .print (f"[red]Error:[/red] Preset '{ pack_id } ' is not installed" )
2275+ raise typer .Exit (1 )
2276+
2277+ # Get current metadata
2278+ metadata = manager .registry .get (pack_id )
2279+ if metadata is None :
2280+ console .print (f"[red]Error:[/red] Preset '{ pack_id } ' not found in registry (corrupted state)" )
2281+ raise typer .Exit (1 )
2282+
2283+ old_priority = metadata .get ("priority" , 10 )
2284+ if old_priority == priority :
2285+ console .print (f"[yellow]Preset '{ pack_id } ' already has priority { priority } [/yellow]" )
2286+ raise typer .Exit (0 )
2287+
2288+ # Update priority
2289+ manager .registry .update (pack_id , {"priority" : priority })
2290+
2291+ console .print (f"[green]✓[/green] Preset '{ pack_id } ' priority changed: { old_priority } → { priority } " )
2292+ console .print ("\n [dim]Lower priority = higher precedence in template resolution[/dim]" )
2293+
2294+
22442295# ===== Preset Catalog Commands =====
22452296
22462297
@@ -2576,8 +2627,9 @@ def extension_list(
25762627 status_color = "green" if ext ["enabled" ] else "red"
25772628
25782629 console .print (f" [{ status_color } ]{ status_icon } [/{ status_color } ] [bold]{ ext ['name' ]} [/bold] (v{ ext ['version' ]} )" )
2630+ console .print (f" [dim]{ ext ['id' ]} [/dim]" )
25792631 console .print (f" { ext ['description' ]} " )
2580- console .print (f" Commands: { ext ['command_count' ]} | Hooks: { ext ['hook_count' ]} | Status: { 'Enabled' if ext ['enabled' ] else 'Disabled' } " )
2632+ console .print (f" Commands: { ext ['command_count' ]} | Hooks: { ext ['hook_count' ]} | Priority: { ext [ 'priority' ] } | Status: { 'Enabled' if ext ['enabled' ] else 'Disabled' } " )
25812633 console .print ()
25822634
25832635 if available or all_extensions :
@@ -2765,6 +2817,7 @@ def extension_add(
27652817 extension : str = typer .Argument (help = "Extension name or path" ),
27662818 dev : bool = typer .Option (False , "--dev" , help = "Install from local directory" ),
27672819 from_url : Optional [str ] = typer .Option (None , "--from" , help = "Install from custom URL" ),
2820+ priority : int = typer .Option (10 , "--priority" , help = "Resolution priority (lower = higher precedence, default 10)" ),
27682821):
27692822 """Install an extension."""
27702823 from .extensions import ExtensionManager , ExtensionCatalog , ExtensionError , ValidationError , CompatibilityError
@@ -2794,7 +2847,7 @@ def extension_add(
27942847 console .print (f"[red]Error:[/red] No extension.yml found in { source_path } " )
27952848 raise typer .Exit (1 )
27962849
2797- manifest = manager .install_from_directory (source_path , speckit_version )
2850+ manifest = manager .install_from_directory (source_path , speckit_version , priority = priority )
27982851
27992852 elif from_url :
28002853 # Install from URL (ZIP file)
@@ -2827,7 +2880,7 @@ def extension_add(
28272880 zip_path .write_bytes (zip_data )
28282881
28292882 # Install from downloaded ZIP
2830- manifest = manager .install_from_zip (zip_path , speckit_version )
2883+ manifest = manager .install_from_zip (zip_path , speckit_version , priority = priority )
28312884 except urllib .error .URLError as e :
28322885 console .print (f"[red]Error:[/red] Failed to download from { from_url } : { e } " )
28332886 raise typer .Exit (1 )
@@ -2871,7 +2924,7 @@ def extension_add(
28712924
28722925 try :
28732926 # Install from downloaded ZIP
2874- manifest = manager .install_from_zip (zip_path , speckit_version )
2927+ manifest = manager .install_from_zip (zip_path , speckit_version , priority = priority )
28752928 finally :
28762929 # Clean up downloaded ZIP
28772930 if zip_path .exists ():
@@ -3113,6 +3166,8 @@ def extension_info(
31133166
31143167 console .print ()
31153168 console .print ("[green]✓ Installed[/green]" )
3169+ priority = metadata .get ("priority" , 10 )
3170+ console .print (f"[dim]Priority:[/dim] { priority } " )
31163171 console .print (f"\n To remove: specify extension remove { resolved_installed_id } " )
31173172 return
31183173
@@ -3206,6 +3261,9 @@ def _print_extension_info(ext_info: dict, manager):
32063261 install_allowed = ext_info .get ("_install_allowed" , True )
32073262 if is_installed :
32083263 console .print ("[green]✓ Installed[/green]" )
3264+ metadata = manager .registry .get (ext_info ['id' ])
3265+ priority = metadata .get ("priority" , 10 ) if metadata else 10
3266+ console .print (f"[dim]Priority:[/dim] { priority } " )
32093267 console .print (f"\n To remove: specify extension remove { ext_info ['id' ]} " )
32103268 elif install_allowed :
32113269 console .print ("[yellow]Not installed[/yellow]" )
@@ -3470,6 +3528,10 @@ def extension_update(
34703528 if "installed_at" in backup_registry_entry :
34713529 new_metadata ["installed_at" ] = backup_registry_entry ["installed_at" ]
34723530
3531+ # Preserve the original priority
3532+ if "priority" in backup_registry_entry :
3533+ new_metadata ["priority" ] = backup_registry_entry ["priority" ]
3534+
34733535 # If extension was disabled before update, disable it again
34743536 if not backup_registry_entry .get ("enabled" , True ):
34753537 new_metadata ["enabled" ] = False
@@ -3716,6 +3778,52 @@ def extension_disable(
37163778 console .print (f"To re-enable: specify extension enable { extension_id } " )
37173779
37183780
3781+ @extension_app .command ("set-priority" )
3782+ def extension_set_priority (
3783+ extension : str = typer .Argument (help = "Extension ID or name" ),
3784+ priority : int = typer .Argument (help = "New priority (lower = higher precedence)" ),
3785+ ):
3786+ """Set the resolution priority of an installed extension."""
3787+ from .extensions import ExtensionManager
3788+
3789+ project_root = Path .cwd ()
3790+
3791+ # Check if we're in a spec-kit project
3792+ specify_dir = project_root / ".specify"
3793+ if not specify_dir .exists ():
3794+ console .print ("[red]Error:[/red] Not a spec-kit project (no .specify/ directory)" )
3795+ console .print ("Run this command from a spec-kit project root" )
3796+ raise typer .Exit (1 )
3797+
3798+ # Validate priority
3799+ if priority < 1 :
3800+ console .print ("[red]Error:[/red] Priority must be a positive integer (1 or higher)" )
3801+ raise typer .Exit (1 )
3802+
3803+ manager = ExtensionManager (project_root )
3804+
3805+ # Resolve extension ID from argument (handles ambiguous names)
3806+ installed = manager .list_installed ()
3807+ extension_id , display_name = _resolve_installed_extension (extension , installed , "set-priority" )
3808+
3809+ # Get current metadata
3810+ metadata = manager .registry .get (extension_id )
3811+ if metadata is None :
3812+ console .print (f"[red]Error:[/red] Extension '{ extension_id } ' not found in registry (corrupted state)" )
3813+ raise typer .Exit (1 )
3814+
3815+ old_priority = metadata .get ("priority" , 10 )
3816+ if old_priority == priority :
3817+ console .print (f"[yellow]Extension '{ display_name } ' already has priority { priority } [/yellow]" )
3818+ raise typer .Exit (0 )
3819+
3820+ # Update priority
3821+ manager .registry .update (extension_id , {"priority" : priority })
3822+
3823+ console .print (f"[green]✓[/green] Extension '{ display_name } ' priority changed: { old_priority } → { priority } " )
3824+ console .print ("\n [dim]Lower priority = higher precedence in template resolution[/dim]" )
3825+
3826+
37193827def main ():
37203828 app ()
37213829
0 commit comments