@@ -276,6 +276,10 @@ def remove(self, pack_id: str):
276276 def update (self , pack_id : str , updates : dict ):
277277 """Update preset metadata in registry.
278278
279+ Merges the provided updates with the existing entry, preserving any
280+ fields not specified. The installed_at timestamp is always preserved
281+ from the original entry.
282+
279283 Args:
280284 pack_id: Preset ID
281285 updates: Partial metadata to merge into existing metadata
@@ -285,7 +289,13 @@ def update(self, pack_id: str, updates: dict):
285289 """
286290 if pack_id not in self .data ["presets" ]:
287291 raise KeyError (f"Preset '{ pack_id } ' not found in registry" )
288- self .data ["presets" ][pack_id ].update (updates )
292+ existing = self .data ["presets" ][pack_id ]
293+ # Merge: existing fields preserved, new fields override
294+ merged = {** existing , ** updates }
295+ # Always preserve original installed_at
296+ if "installed_at" in existing :
297+ merged ["installed_at" ] = existing ["installed_at" ]
298+ self .data ["presets" ][pack_id ] = merged
289299 self ._save ()
290300
291301 def get (self , pack_id : str ) -> Optional [dict ]:
@@ -311,14 +321,18 @@ def list_by_priority(self) -> List[tuple]:
311321 """Get all installed presets sorted by priority.
312322
313323 Lower priority number = higher precedence (checked first).
324+ Presets with equal priority are sorted alphabetically by ID
325+ for deterministic ordering.
314326
315327 Returns:
316- List of (pack_id, metadata) tuples sorted by priority
328+ List of (pack_id, metadata_copy) tuples sorted by priority.
329+ Metadata is deep-copied to prevent accidental mutation.
317330 """
331+ import copy
318332 packs = self .data ["presets" ]
319333 return sorted (
320- packs .items (),
321- key = lambda item : item [1 ].get ("priority" , 10 ),
334+ [( pack_id , copy . deepcopy ( meta )) for pack_id , meta in packs .items ()] ,
335+ key = lambda item : ( item [1 ].get ("priority" , 10 ), item [ 0 ] ),
322336 )
323337
324338 def is_installed (self , pack_id : str ) -> bool :
@@ -697,9 +711,13 @@ def install_from_directory(
697711 Installed preset manifest
698712
699713 Raises:
700- PresetValidationError: If manifest is invalid
714+ PresetValidationError: If manifest is invalid or priority is invalid
701715 PresetCompatibilityError: If pack is incompatible
702716 """
717+ # Validate priority
718+ if priority < 1 :
719+ raise PresetValidationError ("Priority must be a positive integer (1 or higher)" )
720+
703721 manifest_path = source_dir / "preset.yml"
704722 manifest = PresetManifest (manifest_path )
705723
@@ -752,9 +770,13 @@ def install_from_zip(
752770 Installed preset manifest
753771
754772 Raises:
755- PresetValidationError: If manifest is invalid
773+ PresetValidationError: If manifest is invalid or priority is invalid
756774 PresetCompatibilityError: If pack is incompatible
757775 """
776+ # Validate priority early
777+ if priority < 1 :
778+ raise PresetValidationError ("Priority must be a positive integer (1 or higher)" )
779+
758780 with tempfile .TemporaryDirectory () as tmpdir :
759781 temp_path = Path (tmpdir )
760782
@@ -1474,7 +1496,7 @@ def resolve(
14741496 if subdir :
14751497 candidate = ext_dir / subdir / f"{ template_name } { ext } "
14761498 else :
1477- candidate = ext_dir / "templates" / f"{ template_name } { ext } "
1499+ candidate = ext_dir / f"{ template_name } { ext } "
14781500 if candidate .exists ():
14791501 return candidate
14801502
0 commit comments