@@ -1137,14 +1137,16 @@ def fetch_catalog(self, force_refresh: bool = False) -> Dict[str, Any]:
11371137 Raises:
11381138 PresetError: If catalog cannot be fetched
11391139 """
1140+ catalog_url = self .get_catalog_url ()
1141+
11401142 if not force_refresh and self .is_cache_valid ():
11411143 try :
1142- return json .loads (self .cache_file .read_text ())
1143- except json .JSONDecodeError :
1144+ metadata = json .loads (self .cache_metadata_file .read_text ())
1145+ if metadata .get ("catalog_url" ) == catalog_url :
1146+ return json .loads (self .cache_file .read_text ())
1147+ except (json .JSONDecodeError , OSError ):
11441148 pass
11451149
1146- catalog_url = self .get_catalog_url ()
1147-
11481150 try :
11491151 import urllib .request
11501152 import urllib .error
@@ -1186,6 +1188,9 @@ def search(
11861188 ) -> List [Dict [str , Any ]]:
11871189 """Search catalog for presets.
11881190
1191+ Searches across all active catalogs (merged by priority) so that
1192+ community and custom catalogs are included in results.
1193+
11891194 Args:
11901195 query: Search query (searches name, description, tags)
11911196 tag: Filter by specific tag
@@ -1195,12 +1200,11 @@ def search(
11951200 List of matching preset metadata
11961201 """
11971202 try :
1198- catalog_data = self .fetch_catalog ()
1203+ packs = self ._get_merged_packs ()
11991204 except PresetError :
12001205 return []
12011206
12021207 results = []
1203- packs = catalog_data .get ("presets" , {})
12041208
12051209 for pack_id , pack_data in packs .items ():
12061210 if author and pack_data .get ("author" , "" ).lower () != author .lower ():
@@ -1234,18 +1238,19 @@ def get_pack_info(
12341238 ) -> Optional [Dict [str , Any ]]:
12351239 """Get detailed information about a specific preset.
12361240
1241+ Searches across all active catalogs (merged by priority).
1242+
12371243 Args:
12381244 pack_id: ID of the preset
12391245
12401246 Returns:
12411247 Pack metadata or None if not found
12421248 """
12431249 try :
1244- catalog_data = self .fetch_catalog ()
1250+ packs = self ._get_merged_packs ()
12451251 except PresetError :
12461252 return None
12471253
1248- packs = catalog_data .get ("presets" , {})
12491254 if pack_id in packs :
12501255 return {** packs [pack_id ], "id" : pack_id }
12511256 return None
@@ -1369,16 +1374,18 @@ def resolve(
13691374 else :
13701375 subdirs = ["" ]
13711376
1377+ # Determine file extension based on template type
1378+ ext = ".md"
1379+ if template_type == "script" :
1380+ ext = ".sh" # scripts use .sh; callers can also check .ps1
1381+
13721382 # Priority 1: Project-local overrides
1373- for subdir in subdirs :
1374- if template_type == "script" :
1375- override = self .overrides_dir / "scripts" / f"{ template_name } .sh"
1376- elif subdir :
1377- override = self .overrides_dir / f"{ template_name } .md"
1378- else :
1379- override = self .overrides_dir / f"{ template_name } .md"
1380- if override .exists ():
1381- return override
1383+ if template_type == "script" :
1384+ override = self .overrides_dir / "scripts" / f"{ template_name } { ext } "
1385+ else :
1386+ override = self .overrides_dir / f"{ template_name } { ext } "
1387+ if override .exists ():
1388+ return override
13821389
13831390 # Priority 2: Installed presets (sorted by priority — lower number wins)
13841391 if self .presets_dir .exists ():
@@ -1387,11 +1394,9 @@ def resolve(
13871394 pack_dir = self .presets_dir / pack_id
13881395 for subdir in subdirs :
13891396 if subdir :
1390- candidate = (
1391- pack_dir / subdir / f"{ template_name } .md"
1392- )
1397+ candidate = pack_dir / subdir / f"{ template_name } { ext } "
13931398 else :
1394- candidate = pack_dir / f"{ template_name } .md "
1399+ candidate = pack_dir / f"{ template_name } { ext } "
13951400 if candidate .exists ():
13961401 return candidate
13971402
@@ -1402,13 +1407,9 @@ def resolve(
14021407 continue
14031408 for subdir in subdirs :
14041409 if subdir :
1405- candidate = (
1406- ext_dir / "templates" / f"{ template_name } .md"
1407- )
1410+ candidate = ext_dir / subdir / f"{ template_name } { ext } "
14081411 else :
1409- candidate = (
1410- ext_dir / "templates" / f"{ template_name } .md"
1411- )
1412+ candidate = ext_dir / "templates" / f"{ template_name } { ext } "
14121413 if candidate .exists ():
14131414 return candidate
14141415
@@ -1421,6 +1422,10 @@ def resolve(
14211422 core = self .templates_dir / "commands" / f"{ template_name } .md"
14221423 if core .exists ():
14231424 return core
1425+ elif template_type == "script" :
1426+ core = self .templates_dir / "scripts" / f"{ template_name } { ext } "
1427+ if core .exists ():
1428+ return core
14241429
14251430 return None
14261431
@@ -1438,52 +1443,34 @@ def resolve_with_source(
14381443 Returns:
14391444 Dictionary with 'path' and 'source' keys, or None if not found
14401445 """
1441- # Priority 1: Project-local overrides
1442- override = self .overrides_dir / f" { template_name } .md"
1443- if override . exists () :
1444- return { "path" : str ( override ), "source" : "project override" }
1446+ # Delegate to resolve() for the actual lookup, then determine source
1447+ resolved = self .resolve ( template_name , template_type )
1448+ if resolved is None :
1449+ return None
14451450
1446- # Priority 2: Installed presets (sorted by priority — lower number wins)
1447- if self .presets_dir .exists ():
1451+ resolved_str = str (resolved )
1452+
1453+ # Determine source attribution
1454+ if str (self .overrides_dir ) in resolved_str :
1455+ return {"path" : resolved_str , "source" : "project override" }
1456+
1457+ if str (self .presets_dir ) in resolved_str and self .presets_dir .exists ():
14481458 registry = PresetRegistry (self .presets_dir )
14491459 for pack_id , _metadata in registry .list_by_priority ():
1450- pack_dir = self .presets_dir / pack_id
1451- # Check templates/ subdirectory first, then root
1452- for subdir in ["templates" , "commands" , "scripts" , "" ]:
1453- if subdir :
1454- candidate = (
1455- pack_dir / subdir / f"{ template_name } .md"
1456- )
1457- else :
1458- candidate = pack_dir / f"{ template_name } .md"
1459- if candidate .exists ():
1460- meta = registry .get (pack_id )
1461- version = meta .get ("version" , "?" ) if meta else "?"
1462- return {
1463- "path" : str (candidate ),
1464- "source" : f"{ pack_id } v{ version } " ,
1465- }
1460+ if f"/{ pack_id } /" in resolved_str :
1461+ meta = registry .get (pack_id )
1462+ version = meta .get ("version" , "?" ) if meta else "?"
1463+ return {
1464+ "path" : resolved_str ,
1465+ "source" : f"{ pack_id } v{ version } " ,
1466+ }
14661467
1467- # Priority 3: Extension-provided templates
1468- if self .extensions_dir .exists ():
1468+ if str (self .extensions_dir ) in resolved_str and self .extensions_dir .exists ():
14691469 for ext_dir in sorted (self .extensions_dir .iterdir ()):
1470- if not ext_dir .is_dir () or ext_dir .name .startswith ("." ):
1471- continue
1472- candidate = ext_dir / "templates" / f"{ template_name } .md"
1473- if candidate .exists ():
1470+ if ext_dir .is_dir () and f"/{ ext_dir .name } /" in resolved_str :
14741471 return {
1475- "path" : str ( candidate ) ,
1472+ "path" : resolved_str ,
14761473 "source" : f"extension:{ ext_dir .name } " ,
14771474 }
14781475
1479- # Priority 4: Core templates
1480- core = self .templates_dir / f"{ template_name } .md"
1481- if core .exists ():
1482- return {"path" : str (core ), "source" : "core" }
1483-
1484- # Also check commands subdirectory for core
1485- core_cmd = self .templates_dir / "commands" / f"{ template_name } .md"
1486- if core_cmd .exists ():
1487- return {"path" : str (core_cmd ), "source" : "core" }
1488-
1489- return None
1476+ return {"path" : resolved_str , "source" : "core" }
0 commit comments