
    *"hj                        d Z ddlmZ ddlZddlZddlZddlmZ ddlmZm	Z	 ddl
mZmZ  ej                  e      Z G d d      Zy)	u   
Persistent skill-name → skill-id registry with version tracking.

IDs are the first 12 hex characters of SHA-256(name), consistent with the
convention used in ``SkillManager._parse_skill_md``.  The registry is
persisted to shared storage as ``{prefix}evolve_skill_registry.json`` so that IDs
remain stable across restarts.

Each entry also tracks:
  - ``version``: incremented on every content-changing update
  - ``content_sha``: SHA-256 of the latest SKILL.md content
  - ``history``: list of ``{version, content_sha, timestamp, action}``
    )annotationsN)deepcopy)datetimetimezone)AnyOptionalc                      e Zd ZdZddZddZddZedd       ZddZ	ddZ
ddZdd	Z	 dd
d	 	 	 	 	 	 	 	 	 ddZddZddZy
)SkillIDRegistryzGMaintains a persistent mapping  skill_name -> {skill_id, version, ...}.c                    i | _         y N)_mapselfs    </root/.hermes/SkillClaw/evolve_server/core/skill_registry.py__init__zSkillIDRegistry.__init__   s	    /1	    c                |   | d}	 |j                  |      j                         j                  d      }t        j                  |      }t        |t              r| j                  |      | _        t        j                  dt        | j                               y # t        $ r t        j                  d       Y y w xY w)Nevolve_skill_registry.jsonutf-8z0[SkillIDRegistry] loaded %d entries from storageuD   [SkillIDRegistry] no existing registry in storage — starting fresh)
get_objectreaddecodejsonloads
isinstancedict
_normaliser   loggerinfolen	Exception)r   bucketprefixkeydataraws         r   load_from_osszSkillIDRegistry.load_from_oss#   s    23	`$$S)..077@D**T"C#t$ OOC0	KKJCPTPYPYN[ 	`KK^_	`s   BB B;:B;c                    | d}t        j                  | j                  dd      }|j                  ||j	                  d             t
        j                  dt        | j                               y )Nr   F   )ensure_asciiindentr   z-[SkillIDRegistry] saved %d entries to storage)r   dumpsr   
put_objectencoder   r   r    )r   r"   r#   r$   contents        r   save_to_osszSkillIDRegistry.save_to_oss.   sU    23**TYYU1E#w~~g67CS^Tr   c                   i }| j                         D ]r  \  }}t        |t              r|ddg d||<   !t        |t              r|||<   7t	        j
                  |j                               j                         dd ddg d||<   t |S )zEAccept both old ``{name: id_str}`` and new ``{name: {...}}`` formats.    skill_idversioncontent_shahistoryN   )itemsr   strr   hashlibsha256r.   	hexdigest)r&   outnamevals       r   r   zSkillIDRegistry._normalise6   s     *, 	ID##s# # #%!	D	 C&D	 !(t{{} = G G I#2 N #%!	D		" 
r   c                    | j                   j                  |      }|r|d   S t        j                  |j	                               j                         dd }|ddg d| j                   |<   |S )z5Return the stable skill_id (create entry if missing).r5   Nr9   r   r3   r4   )r   getr<   r=   r.   r>   )r   
skill_nameentrysids       r   get_or_createzSkillIDRegistry.get_or_createO   sl    		j)$$nnZ..01;;=crB	!
		* 
r   c                J    | j                   j                  |      }|r|d   S d S Nr5   r   rC   r   rD   rE   s      r   rC   zSkillIDRegistry.get]   s&    		j)$)uZ 3t3r   c                d    | j                   j                  |      }|r|j                  dd      S dS )Nr6   r   rJ   rK   s      r   get_versionzSkillIDRegistry.get_versiona   s,    		j)*/uyyA&6Q6r   c                d    | j                   j                  |      }|r|j                  dd      S dS )Nr7   r3   rJ   rK   s      r   get_content_shazSkillIDRegistry.get_content_shae   s,    		j)/4uyy+<"<r   N)bundle_recordc                  | j                   j                  |      }|s | j                  |       | j                   |   }|j                  dd      dz   }||d<   ||d<   t        |t              rPdD ]  }|j                  |      s||   ||<    |j                  d      }t        |t
              rt        |      |d<   |j                  dg       }	||t        j                  t        j                        j                         |d}
t        |t              rPdD ]  }|j                  |      s||   |
|<    |j                  d      }t        |t
              rt        |      |
d<   |	j                  |
       t        |	      d	kD  r|	d
d |d<   |S )zRecord a content-changing update. Returns the new version number.

        Increments ``version``, updates ``content_sha``, and appends to
        ``history`` (capped at 20 entries to bound storage).
        r6   r   r2   r7   )format
entrypointtree_sha256filesr8   )r6   r7   	timestampaction   iN)r   rC   rG   r   r   listr   
setdefaultr   nowr   utc	isoformatappendr    )r   rD   r7   rW   rP   rE   new_versionr$   rU   r8   history_entrys              r   record_updatezSkillIDRegistry.record_updatei   s    		j)z*IIj)Eii	1-1&i*mmT*> 4 $$S)!.s!3E#J4 "%%g.E%&!)%g((B7"&!hll3==?	)
 mT*> < $$S))6s);M#&< "%%g.E%&)1%g&}%w<"&st}E)r   c                p    | j                   j                         D ci c]  \  }}||d    c}}S c c}}w rI   )r   r:   )r   r@   rE   s      r   all_idszSkillIDRegistry.all_ids   s/    ;?99??;LMKD%eJ''MMMs   2c                ,    t        | j                        S r   )r   r   r   s    r   all_entrieszSkillIDRegistry.all_entries   s    DIIr   )returnNone)r#   r;   rf   rg   )r&   r   rf   dict[str, dict[str, Any]])rD   r;   rf   r;   )rD   r;   rf   zOptional[str])rD   r;   rf   int)create)
rD   r;   r7   r;   rW   r;   rP   zOptional[dict[str, Any]]rf   ri   )rf   zdict[str, str])rf   rh   )__name__
__module____qualname____doc__r   r'   r0   staticmethodr   rG   rC   rM   rO   ra   rc   re    r   r   r
   r
      s    Q2
	`U  047= 	. 37.. . 	. 0. 
.`Nr   r
   )rn   
__future__r   r<   r   loggingcopyr   r   r   typingr   r   	getLoggerrk   r   r
   rp   r   r   <module>rv      s?    #     '  			8	$B Br   