
    *"hjg                        d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m	Z	m
Z
 ddlmZmZmZ ddlmZ ddlmZmZmZ dd	lmZmZmZmZmZmZ  ej8                  e      Zdd
ZdddZ ddZ! G d d      Z"y)aV  
Skill Hub: shared skill sync via pluggable object storage.

Provides bidirectional sync between local skill directories and a shared
object store, enabling group-wide skill sharing with incremental (sha256-based)
transfers. Default pull behavior mirrors the cloud snapshot into the local
skills directory with backup and rollback safety.

Usage:
    hub = SkillHub(config)
    hub.pull_skills("/path/to/local/skills")   # mirror cloud snapshot locally
    hub.push_skills("/path/to/local/skills")   # upload new/updated skills
    hub.list_remote()                          # list skills on cloud
    )annotationsN)datetimetimezone)Any
CollectionOptional)SkillIDRegistry   )build_object_storeis_not_found_errornormalize_backend)bundle_entrypoint_bytesbundle_file_recordsbundle_has_only_entrypointbundle_tree_sha256read_skill_bundle_with_metawrite_skill_bundlec                    t         j                  j                  |       t         j                  j                  t         j                  j                  t         j                  j	                  d      dd            k(  S )N~z.hermesskills)ospathrealpathjoin
expanduser)
skills_dirs    ./root/.hermes/SkillClaw/skillclaw/skill_hub.py_is_hermes_skill_rootr   *   sN    77J'277+;+;BGGLLI[I[\_I`bkmu<v+www    c                    t        |       r(|r&|dk7  r!t        j                  j                  | ||      S t        j                  j                  | |      S )Ngeneral)r   r   r   r   )r   
skill_namecategorys      r   _skill_dir_for_rootr$   .   s?    Z(X(i:Oww||J*==77<<
J//r   c                    t        |       syt        j                  j                  ||       }|j	                  t        j
                        }t        |      dk\  rt        |d   xs d      S y)Nr!      r   )r   r   r   relpathsplitseplenstr)r   skill_md_pathrelpartss       r   _category_from_skill_pathr/   4   sU     ,
''//-
4CIIbffE
5zQ58(y))r   c                     e Zd ZdZdddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d"dZed#d       Zed$d       Zd%dZd%d	Z	d&d
Z
d&dZd'dZd(dZd)dZ	 	 	 	 	 	 d*dZd+dZd,dZd+dZd-dZed.d       Zd/dZd0dZ	 d1	 	 	 	 	 d2dZed3d       Zed4d       Zed5d       Ze	 	 	 	 	 	 	 	 	 	 d6d       Ze	 	 	 	 	 	 	 	 d7d       Zed8d9d       Z	 	 	 d:	 	 	 	 	 	 	 	 	 d;dZd<d Z d=d!Z!y)>SkillHubz@Sync skills between a local directory and a shared object store. default)regionsession_token
local_rootgroup_id
user_aliasc       
   
         t        ||||||||      | _        |	| _        |
xs  t        j                  j                  dd      | _        y )N)backendendpointbucketaccess_key_idsecret_access_keyr4   r5   r6   USER	anonymous)r   _bucket	_group_idr   environget_user_alias)selfr:   r;   r<   r=   r>   r4   r5   r6   r7   r8   s              r   __init__zSkillHub.__init__A   sM     *'/'!	
 "%L)Lr   c                   t        t        |dd      xs d      j                         j                         }t        t        |dd      xs d      j                         j                         xs |}|dk(  rddlm} |j                  |      S t        t        |dd      xs d      }t        t        |dd      xs d      }t        t        |d	d      xs d      }t        t        |d
d      xs d      }t        t        |dd      xs d      }	 | |xs |	rdn|s|rdnd||||t        t        |dd      xs d      t        t        |dd      xs d      |	t        |dd      t        |dd      
      S )Nsharing_backendr2   sharing_skill_backendnacosr
   )NacosSkillHubsharing_endpointsharing_bucketsharing_access_key_idsharing_secret_access_keysharing_local_rootlocals3osssharing_regionsharing_session_tokensharing_group_idr3   sharing_user_alias
r:   r;   r<   r=   r>   r4   r5   r6   r7   r8   )r+   getattrstriplowernacos_skill_hubrL   from_config)
clsconfigrI   r:   rL   r;   r<   r=   r>   r6   s
             r   r^   zSkillHub.from_config\   sp   gf.?DJKQQSYY[gf&=rBHbIOOQWWYl]lg6 ,,V44wv'92>D"EWV%5r:@bAGF,CRHNBO0KR P VTVW)=rBHbI
c:Fh4]b'/wv'7<BCgf.ErJPbQ!V%7Cv';R@
 	
r   c                L   t        t        |dd      xs d      j                         j                         }t        t        |dd      xs d      j                         j                         }|s|dk7  r|}|s!|dk(  rt        t        |dd      xs d      rd}t        t        |dd      xs d      }t        t        |dd      xs d      }t        t        |d	d      xs d      }t        t        |d
d      xs d      }t        t        |dd      xs d      }t	        |||      }	|	dk(  rd}	|	s|rd}	|	s|dk7  r|s|r
|rd|v rdnd}	|	sy | |	||||t        t        |dd      xs d      t        t        |dd      xs d      |t        |dd      t        |dd      
      S )a;  Build the legacy object-store hub for non-skill artifacts.

        ``sharing.skill_backend=nacos`` selects only the Skill registry.
        Sessions, validation jobs, and other non-skill artifacts must continue
        to use local/OSS/S3 object storage when that storage is explicitly
        configured.
        rI   r2   sharing_session_backendrK   rQ   rR   rM   rN   rO   rP   )r;   r6   zaliyuncs.comrT   rS   NrU   rV   rW   r3   rX   rY   )r+   rZ   r[   r\   r   )
r_   r`   rI   r:   r;   r<   r=   r>   r6   resolveds
             r   object_storage_from_configz#SkillHub.object_storage_from_configv   s    gf.?DJKQQSYY[gf&?DJKQQSYY[?g5%G?g5#gfNbdf>g>mkm:nGwv'92>D"EWV%5r:@bAGF,CRHNBO0KR P VTVW)=rBHbI
$WxJWwHJHOw6Fh (^x-GuTH'/wv'7<BCgf.ErJPbQ!V%7Cv';R@
 	
r   c                     | j                    dS )N/)rB   rF   s    r   _prefixzSkillHub._prefix   s    ..!##r   c                (    | j                          dS )Nzmanifest.jsonlrh   rg   s    r   _manifest_keyzSkillHub._manifest_key   s    ,,.!00r   c                .    | j                          d| dS )Nskills/z	/SKILL.mdrj   rF   r"   s     r   
_skill_keyzSkillHub._skill_key   s    ,,.!I>>r   c                .    | j                          d| dS )Nrm   z/files/rj   rn   s     r   _skill_files_prefixzSkillHub._skill_files_prefix   s    ,,.!G<<r   c                    t        |xs d      j                         j                  dd      }|dk(  r| j                  |      S | j	                  |       | S )Nr2   \rf   SKILL.md)r+   r[   replacero   rq   )rF   r"   rel_pathcleans       r   _skill_bundle_keyzSkillHub._skill_bundle_key   sX    HN#))+33D#>J??:..**:67w??r   c                :    | j                   j                  |      S )N)prefix)rA   iter_objects)rF   rz   s     r   _iter_remote_keyszSkillHub._iter_remote_keys   s    ||(((77r   c                   |D ch c]  }|dk7  s	| j                  ||       }}| j                  | j                  |            D ]?  }t        t	        |dd      xs d      }|s ||vs%| j
                  j                  |       A y c c}w )Nrt   keyr2   )rx   r|   rq   r+   rZ   rA   delete_object)rF   r"   
keep_pathsrv   	keep_keysobjr~   s          r   _delete_remote_bundle_extrasz%SkillHub._delete_remote_bundle_extras   s    R\wh`hlv`vT++JAw	w))$*B*B:*NO 	0Cgc5"-34Cs)+**3/	0 xs
   
B	B	c                   i }|j                  d      }t        |t              r|r|D ]  }t        |xs i j                  d      xs d      j	                         j                  dd      }|sF| j                  ||      }| j                  j                  |      j                         ||<    |S | j                  j                  | j                  |            j                         |d<   |S )Nfilesr   r2   rs   rf   rt   )rD   
isinstancelistr+   r[   ru   rx   rA   
get_objectreadro   )rF   r"   recordbundlefile_entriesitemrv   r~   s           r   _download_skill_bundlezSkillHub._download_skill_bundle   s    
 $&zz'*lD)l$ G
//7=2>DDFNNtUXY,,ZB#'<<#:#:3#?#D#D#Fx G  "&!8!89T!U!Z!Z!\F:r   c                b    | j                          d| dt        dt        |xs d             dS )Nrm   z/versions/vr
   rf   )rh   maxintrF   r"   versions      r   _skill_version_prefixzSkillHub._skill_version_prefix   s5    ,,.!KAs7<VWGX@Y?ZZ[\\r   c                    t        |xs d      j                         j                  dd      }|dk(  r| j                  ||       dS | j                  ||       d| S )Nr2   rs   rf   rt   files/)r+   r[   ru   r   )rF   r"   r   rv   rw   s        r   _skill_version_bundle_keyz"SkillHub._skill_version_bundle_key   sh    HN#))+33D#>J00WEFhOO,,ZAB&PPr   c                ,    | j                  ||       dS )Nzbundle.json)r   r   s      r   _skill_version_record_keyz"SkillHub._skill_version_record_key   s    ,,ZAB+NNr   c                |   ddt        |      t        |      d}t               }t        |j	                               D ]E  \  }}| j                  |||      }|j                  |       | j                  j                  ||       G | j                  | j                  ||       d      D ]?  }	t        t        |	dd      xs d      }|s ||vs%| j                  j                  |       A | j                  j                  | j                  ||      t        j                   |dd	      j#                  d
             |S )N	bundle_v1rt   format
entrypointtree_sha256r   r   r~   r2   F   )ensure_asciiindentutf-8)r   r   setsorteditemsr   addrA   
put_objectr|   r   r+   rZ   r   r   jsondumpsencode)
rF   r"   r   bundle_filesr   r   rv   datar~   r   s
             r   _save_version_bundlezSkillHub._save_version_bundle   s*   !$-l;(6	
 "e	$\%7%7%9: 	/NHd00WhOCMM#LL##C.	/ ))T-G-G
T[-\,]]c*de 	0Cgc5"-34Cs)+**3/	0 	**:w?JJvE!<CCGL	
 r   c                h   t        |       \  }}}|sy|j                  d      dk(  r!t        |j                  d      xs d      |k(  S 	 t        |      }t        j                  |      j                         }t        |      xr! t        |j                  d      xs d      |k(  S # t        $ r Y yw xY w)NFr   r   r   r2   sha256)	r   rD   r+   r   	Exceptionhashlibr   	hexdigestr   )	skill_dirr   r   _recordstree_shaskill_md	skill_shas          r   _local_bundle_matches_recordz%SkillHub._local_bundle_matches_record   s    %@%K"(::h;.vzz-06B78CC	.v6H NN8,668	)&1bc&**X:N:TRT6UYb6bb  		s   	B% %	B10B1c                   | j                         }	 | j                  j                  |      }|j                         j	                  d      }i }|j                         j                         D ]B  }|j                         s	 t        j                  |      }|j                  dd      }|r|||<   D |S # t
        $ r4}t        |      ri cY d}~S t        j                  d|       i cY d}~S d}~ww xY w# t        j                  $ r Y w xY w)z?Load manifest.jsonl from storage. Returns {skill_name: record}.r   Nz&[SkillHub] failed to load manifest: %snamer2   )rk   rA   r   r   decoder   r   loggerwarningr[   
splitlinesr   loadsrD   JSONDecodeError)	rF   r~   resultcontentemanifestlinerecr   s	            r   _load_remote_manifestzSkillHub._load_remote_manifest
  s      "	\\,,S1Fkkm**73G /1MMO..0 		D::<jj&wwvr*%(HTN		 #  	!!$	NNCQGI		 '' s;   :B3 .C33	C0<C+C0C+%C0+C03D	D	c                   |j                         D cg c]  }t        j                  |d       }}|rdj                  |      dz   nd}| j                  j                  | j                         |j                  d             yc c}w )z(Write the full manifest back to storage.F)r   
r2   r   N)valuesr   r   r   rA   r   rk   r   )rF   r   r   linesr   s        r   _save_remote_manifestzSkillHub._save_remote_manifest#  sj    @H@QRCe4RR-2$))E"T) 2 2 4gnnW6MN Ss   BNc                   t        |      r@t        t        j                  t        j                  j                  |dd      d            }n=t        t        j                  t        j                  j                  |dd                  }|st        j                  d       dddddS | j                         }t               }|j                  | j                  | j                                d}d}d}|xs i j                  d	i       }	|xs i j                  d
d      }
|xs i j                  dd      }|du}|D ]  }t        j                  j                  t        j                  j                  |            }t        j                  j                  |      }|rV||	v rR|	|   }|j                  dd      }|j                  dd      }||
k\  r$||k  rt        j                  d||||       |dz  }t!        |      \  }}}t#        |      }t%        j&                  |      j)                         }|j                  |      }|r| j+                  ||      r|dz  } | j                  j-                  | j/                  |      |       t        |j1                               D ]7  \  }}|dk(  r| j                  j-                  | j3                  ||      |       9 | j5                  ||j7                                dd||d}|j9                  ||d|      }| j;                  |||       i |xs i ||j=                  |      |||dd|| j>                  tA        jB                  tD        jF                        jI                         d
||<   | jK                  ||   |       ||   jM                  dtO        ||             |dz  }t        j                  d|        |dkD  r;| jQ                  |       |jS                  | j                  | j                                t        j                  d|||tU        |             |||tU        |      dS )u  Upload new/changed skills from local directory to shared storage.

        Parameters
        ----------
        skills_dir:
            Path to the local skills directory.
        skill_filter:
            Optional quality gate. When provided, must contain:
              - ``"stats"``: dict mapping skill_name → stats record
              - ``"min_injections"``: int — skills with fewer injections are
                in a "probation" period and are not uploaded yet
              - ``"min_effectiveness"``: float — skills below this threshold
                after the probation period are blocked from upload

            Skills that have *never* been injected (no stats entry) are
            treated as brand-new and are allowed through (they need cloud
            exposure to gather data from other group members).

        Returns {"uploaded": N, "skipped": M, "filtered": F, "total_local": T}.
        **rt   T	recursive*z"[SkillHub] no local skills to pushr   )uploadedskippedfilteredtotal_localstatsmin_injectionsmin_effectivenessg        Ninject_counteffectivenessg      ?zK[SkillHub] filtered out skill %s (effectiveness=%.2f < %.2f, injections=%d)r
   r   r   push)actionbundle_record)
r   skill_idr   r   r   r   r   r   uploaded_byuploaded_atr#   z[SkillHub] pushed skill: %szH[SkillHub] push complete: %d uploaded, %d skipped, %d filtered, %d total)+r   r   globr   r   r   r   infor   r	   load_from_ossrA   rh   rD   basenamedirnamer   r   r   r   r   r   r   ro   r   rx   r   keysrecord_updater   get_or_createrE   r   nowr   utc	isoformat_enrich_manifest_entry
setdefaultr/   r   save_to_ossr*   )rF   r   skill_filterpathsr   registryr   r   r   r   min_injmin_eff
use_filterr   r"   r   entryinjeffr   bundle_recordsr   r   	local_sha
remote_recrv   r   r   r   s                                r   push_skillszSkillHub.push_skills-  s   2 !,499RWW\\*dJ%O[_`aE499RWW\\*c:%NOPEKK<= !aQqQQ--/"$t||T\\^<#(("5%2**+;Q?%2**+>D!-
 A	CD))"''//$*?@J-IjE1j)ii2ii5'>cGmKKe" MH5PQZ5[2L.(.|<Hx0::<I!j1Jd??	:V1LL##DOOJ$?J"(););)="> \$z)''(>(>z8(TVZ[\ --j,:K:K:MN &(''	M ,,+	 - G %%j'<H$#$"$22:>"#'%('#//'||HLL9CCE$HZ  ''(<dCZ ++J8QR\^b8cdMHKK5zBCA	CF a<&&x0  t||~>VJ	
 %h_bch_ijjr   c                .   	 t        |d      5 }|j                         }ddd       j                  d      sy|j	                  dd      }|dk(  ry	 ddl}|j                  |d| j                               xs i }t        |t              sy|j                  d	      }|rt        |      j                         | d	<   |j                  d
      }t        |t              r|xs i j                  di       ni }	t        |	t              r1|	j                  d      r t        |	d         j                         | d<   y|j                  d      r t        |d         j                         | d<   yy# 1 sw Y   KxY w# t        $ r Y yw xY w# t        $ r i }Y w xY w)zIParse SKILL.md YAML frontmatter to fill description/category in manifest.r   )encodingNz---z
---r&   r   descriptionmetadata	skillclawr#   )openr   OSError
startswithfindyaml	safe_loadr[   r   r   dictrD   r+   )
r   
skill_pathfrawend_idxr  fmdescr  sc_metas
             r   r   zSkillHub._enrich_manifest_entry  ss   	j73 qffh
 ~~e$((7A&b=	Ag 4 4 67=2B "d#vvm$#&t9??#4E- 66*%;EhPT;U8>r&&{B7[]gt$Z)@ #GJ$7 8 > > @E*VVJ #BzN 3 9 9 ;E*  ;  		  	B	s9   E6 E)E6 *F )E3.E6 6	FFFFc           	         i }t         j                  j                  |       s|S t        |       rt	        t        j
                  t         j                  j                  | dd      d            D ]a  }t         j                  j                  |      }t         j                  j                  |      }|j                  |g       j                  |       c |S t        j                  |       D ]  }|j                         st         j                  j                  |j                  d      }t         j                  j                  |      s^|j                  |j                  g       j                  |j                          |S )zJReturn {skill_name: [skill_dir, ...]} for local `<name>/SKILL.md` entries.r   rt   Tr   )r   r   isdirr   r   r   r   r   r   r   appendscandiris_dirisfiler   )r   outr   r   r   r   r   s          r   _list_local_skill_dirszSkillHub._list_local_skill_dirs  s    %'ww}}Z(J ,tyyj$
)S_cde ;GGOOD1	ww''	2tR(//	:; JZZ
+ 	BE<<>ww||EJJ
;Hww~~h'uzz2.55ejjA	B 
r   c                    | j                  |      }|j                         D ci c]  \  }}|s	||d    c}}S c c}}w )zCReturn {skill_name: skill_dir} for local `<name>/SKILL.md` entries.r   )r  r   )r_   r   groupedr   dirss        r   _list_local_skillszSkillHub._list_local_skills  s<     ,,Z818I:4Dd2hIIIs   
<<c           	     :   t        | ||      }t        |       s|S |j                  |      xs g }|s|S t        |xs d      j	                         dk(  rl|D cg c]N  }t        t        j                  j                  ||       j                  t        j                              dk\  sM|P }}t        |      dk(  r|d   S t        j                  j                  |      }|D cg c]!  }t        j                  j                  |      # }	}||	v r|S |S c c}w c c}w )Nr!   r   r
   r   )r$   r   rD   r+   r[   r*   r   r   r'   r(   r)   r   )
r   r"   r#   local_dirs_by_nametargetexisting_dirsr   nestedtarget_realexisting_reals
             r   _resolve_pull_target_dirz!SkillHub._resolve_pull_target_dir  s    %ZXF$Z0M*..z:@bMx$9%++-:'4rtBGGOODR\<]<c<cdfdjdj<k8lpq8qdrFr6{aay gg&&v.<IJD))$/JJ-'M s
 Ks   AD'D#&Dc                R   t         j                  j                  |      }|j                  |       xs g D ]p  }t         j                  j                  |      |k(  r&t         j                  j	                  |      sFt        j                  |       t        j                  d|       r y )Nz0[SkillHub] removed duplicate local skill dir: %s)	r   r   r   rD   r  shutilrmtreer   r   )r"   keep_dirr  	keep_realr   s        r   "_remove_duplicate_local_skill_dirsz+SkillHub._remove_duplicate_local_skill_dirs  s     GG$$X.	+//
;Ar 	WIww	*i777==+MM)$KKJIV	Wr   c                &   	 t        fdt        j                  |       D              }|dkD  r|d|  n|}|D ]8  }t        j                  j                  | |      }	 t        j                  |       : y# t        $ r Y yw xY w# t        $ r Y Vw xY w)z7Keep only newest `keep` backups for current skills dir.c              3  F   K   | ]  }|j                        s|  y wN)r  ).0nrz   s     r   	<genexpr>z*SkillHub._prune_backups.<locals>.<genexpr>  s     VfAU1Vs   !!Nr   )r   r   listdirr   r   r   r&  r'  )backup_rootrz   keepnames	to_deleter   r   s    `     r   _prune_backupszSkillHub._prune_backups  s    	Vbjj&=VVE &*AXE&D5M5	 	D77<<T2Dd#	  		  s#   'A5 B5	B B	BBc           	     
  &'() t        j                  |d       | j                  |      }|j                         D ci c]  \  }}|s	||d    }}}| j	                         }	|xs g D ch c]=  }t        |xs d      j                         s!t        |xs d      j                         ? }
}|xs g D ch c]=  }t        |xs d      j                         s!t        |xs d      j                         ? c}&&r|rd}&r(|	j                         D ci c]  \  }}|&v s|| }	}}t        &t        |	      z
        (t        &      )t        |	      '	 	 	 	 	 	 	 	 	 	 	 	 	 	 d&&'()fd}|	sO&r*t        j                  ddj                  (      xs d	       nt        j                  d
        |dddddd      S d}d}d}d}|s|	j                         D ]D  \  }}t        |j                  dd      xs d      }| j                  ||||      }t         j                   j                  |d      }||
v rNt         j                   j#                  |      r/|dz  }| j%                  |||       t        j                  d|       t         j                   j'                  |      r+| j)                  ||      r|dz  }| j%                  |||       	 | j+                  ||      }t/        ||d       |dz  }| j%                  |||       t        j                  d|       G t        j                  d||t        |	              |||dt        |	      dd      S t         j                   j1                  t         j                   j3                  |            }t         j                   j5                  t         j                   j3                  |            }t7        j8                  t:        j<                        j?                  d      }t         j                   j                  |d      }t        j                  |d       | d}t         j                   j                  || |       }t         j                   j                  |d| d|       }	 tA        jB                  ||       t        j                  |d       i }	 |	j                         D ]  \  }}t        |j                  dd      xs d      }| j                  ||||      }|||<   t         j                   j                  |d      }t         j                   j                  |t         j                   jE                  ||            }||
v rst         j                   j#                  |      rT|dz  }t         j                   j'                  |      rtA        jB                  ||d       t        j                  d|       t         j                   j'                  |      r1| j)                  ||      r|dz  }tA        jB                  ||d       d	 | j+                  ||      }t/        ||d       |dz  }t        j                  d|        t        |	jI                               } t        |jI                               }!t        |!| z
        D ]!  }"tA        jJ                  ||"   d       |dz  }# t        |       D ]*  }|	j                  |i       }t        |j                  dd      xs d      }|j                  |      xs | j                  ||||      }#t         j                   j                  |t         j                   jE                  |#|            }$t         j                   j'                  |#      rtA        jJ                  |#       t        j                  t         j                   j1                  |#      d       tA        jL                  |$|#       | j%                  ||#|       - 	 t         j                   j'                  |      rtA        jJ                  |d       	 t        j                  d#|||t        |	             | jQ                  ||d$%        ||||t        |	      d|      S c c}}w c c}w c c}w c c}}w # t,        $ r"}t        j                  d||       Y d}~d}~ww xY w# t,        $ r7}t        j                  d|        |dddt        |	      dd      cY d}~S d}~ww xY w# t,        $ r}tG        d| d|       |d}~ww xY w# t,        $ r}t        j                  d |       	 t         j                   j'                  |      rtA        jJ                  |       tA        jB                  ||       d}t        j                  d!|       n,# t,        $ r }%t        jO                  d"|%       Y d}%~%nd}%~%ww xY w |dddt        |	      ||      cY d}~t         j                   j'                  |      rtA        jJ                  |d       S S d}~ww xY w# t         j                   j'                  |      rtA        jJ                  |d       w w xY w)'a  Mirror cloud skills to local directory with backup + rollback safety.

        Parameters
        ----------
        skills_dir:
            Local skills directory.
        mirror:
            When ``True`` (default), do full mirror: local stale skill folders
            not present in remote manifest are deleted. When ``False``, perform
            incremental pull only (download/update remote skills, never delete
            local extras).
        skip_names:
            Skill names to preserve from local disk during this pull.
        include_names:
            Optional subset of remote skill names to download. When provided,
            pull is forced into incremental mode to avoid deleting unrelated
            local skills.

        Returns:
            {
                "downloaded": N,
                "skipped": M,
                "deleted": D,
                "total_remote": T,
                "restored_from_backup": bool,
                "backup_dir": "...",
            }
        T)exist_okr   r2   Fc                \    | |||||d}r|j                  
t        	      	d       |S )N
downloadedr   deletedtotal_remoterestored_from_backup
backup_dir)	requestedmatched_remotemissingmissing_names)updater*   )r;  r   r<  r=  r>  r?  payloadinclude_setrA  rC  requested_counts          r   _resultz%SkillHub.pull_skills.<locals>._resultV  sL     )"" ,(<('G %4*8#&}#5)6	 Nr   zG[SkillHub] none of the requested remote skills matched the manifest: %sz, z(empty request)zU[SkillHub] remote manifest empty; skip mirror pull (downloaded=0 skipped=0 deleted=0)r   r:  r#   r!   rt   r
   z0[SkillHub] preserved local skill during pull: %sz*[SkillHub] failed to download skill %s: %sN)rw   z[SkillHub] pulled skill: %szP[SkillHub] incremental pull complete: %d downloaded, %d skipped, %d total remotez%Y%m%dT%H%M%S%fZz.skillclaw_backups_z.skillclaw_pull_stage_z([SkillHub] backup before pull failed: %s)dirs_exist_okzfailed to download skill z: )ignore_errorsz3[SkillHub] mirror pull failed, restoring backup: %sz0[SkillHub] local skills restored from backup: %sz$[SkillHub] backup restore failed: %szP[SkillHub] pull complete: %d downloaded, %d skipped, %d deleted, %d total remoter&   )r3  )r;  r   r   r   r<  r   r=  r   r>  boolr?  r+   returndict[str, Any]))r   makedirsr  r   r   r+   r[   r   r   r*   r   r   r   r   rD   r$  r   existsr*  r  r   r   r   r   r   abspathr   r   r   r   r   strftimer&  copytreer'   RuntimeErrorr   r'  moveerrorr6  )*rF   r   mirror
skip_namesinclude_namesr  r   r  local_skillsr   skip_setr   rH  r;  r   r<  r>  r#   	local_dir
local_pathr   r   
parent_dir	base_namestampr2  backup_prefixr?  staging_dirresolved_targets
target_dir
staged_dirremote_nameslocal_namesstaledst_dirsrc_dirrestore_errrF  rA  rC  rG  s*                                         @@@@r   pull_skillszSkillHub.pull_skills%  sI	   F 	J.!88D9K9Q9Q9S\:4W[d2h\\--/9C9IrfcRVR\Z\oNcNcNeC
O))+ff<I<ORl4TWX\Xb`bTcTiTiTks4:2,,.l6F3;>>3C[idCt{GZc	[H[{S]:;k*X		 	 		
 	 #'	 	 	 	8 ]IIm,A0A
 vw%*  
$%^^- A	cswwz9=JK 99&		  WW\\)Z@
8#z(BqLG;;D)M_`KK RTXY77==+0Q0QR[]`0aqLG;;D)M_`!88sCF
 #9fDAa
77iI[\94@=A@ KKbH	 % ]%*  WW__RWW__Z%@A
GG$$RWW__Z%@A	X\\*334FGggll:/CD
K$/$+QWW\\+-/HI
ggll:1G	{RSTYSZ/[\	OOJ
3 	K$/+-M	?%^^- A	cswwz9=JK!::&	
 *4 &WW\\*jA
WW\\+rwwzS]7^_
8#z(BqLGww}}Z0
JdSKK RTXY77==,1R1RS]_b1cqLGOOJ
$OW!88sCF #:vTBa
94@?AB x}}/Ll//12Kl :; l51G1 |, [ll4,swwz9=JK*..t4 8U8U&	9 '',,{BGGOOGZ4XY77==)MM'*BGGOOG4tDGW-77gGYZ[H ww}}[)k>^M	
 	KQ?!X!&!
 	
 ]fl \` ! NN#OQUWXYF  		NNEqI ]%* 		R ! W&)B4&1#'NOUVVW:  	NNPRSTR77==,MM*-
J7'+$NPZ[ RC[QQR  ]%9%  ww}}[)k> *)	( ww}}[)k> *s    
bb."bb:"b b b%'b%b+c  E4d? 5dG	d? +	c4cc	d",ddd	d<%d77d<<d? ?	iiA"gi	g+g&!i&g++iii	 ii	 	9jc                T    | j                         }t        |j                               S )z?Return a list of skill metadata dicts from the remote manifest.)r   r   r   )rF   r   s     r   list_remotezSkillHub.list_remote6  s"    --/HOO%&&r   c                T    | j                  |d      }| j                  |      }||dS )z*Bidirectional sync: pull first, then push.F)rW  )pullr   )rl  r   )rF   r   pull_resultpush_results       r   sync_skillszSkillHub.sync_skills?  s4     &&z%&@&&z2#[99r   )r:   r+   r;   r+   r<   r+   r=   r+   r>   r+   r4   r+   r5   r+   r6   r+   r7   r+   r8   r+   )rM  z
'SkillHub')rM  zOptional['SkillHub'])rM  r+   )r"   r+   rM  r+   )r"   r+   rv   r+   rM  r+   )rz   r+   )r"   r+   r   zCollection[str]rM  None)r"   r+   r   rN  rM  dict[str, bytes])r"   r+   r   r   rM  r+   )r"   r+   r   r   rv   r+   rM  r+   )r"   r+   r   r   r   ru  rM  rN  )r   r+   r   rN  rM  rL  )rM  dict[str, dict[str, Any]])r   rv  rM  rt  r-  )r   r+   r   zOptional[dict[str, Any]]rM  zdict[str, int])r   r	  r
  r+   rM  rt  )r   r+   rM  dict[str, list[str]])r   r+   rM  zdict[str, str])
r   r+   r"   r+   r#   r+   r  rw  rM  r+   )r"   r+   r(  r+   r  rw  rM  rt  )r&   )r2  r+   rz   r+   r3  r   rM  rt  )TNN)
r   r+   rW  rL  rX  Optional[Collection[str]]rY  rx  rM  rN  )rM  zlist[dict[str, Any]])r   r+   rM  rv  )"__name__
__module____qualname____doc__rG   classmethodr^   rd   rh   rk   ro   rq   rx   r|   r   r   r   r   r   r   staticmethodr   r   r   r   r   r  r  r$  r*  r6  rl  rn  rs   r   r   r1   r1   >   s   J !M M 	M
 M M M M M M M M6 
 
2 *
 *
`$1?=@80  
	$]QO, c c$2O 26{k{k /{k 
	{kz !< !<N  & J J
   1	
 
 4 WWW 1W 
	W W  " 0437K
K
 K
 .	K

 1K
 
K
b':r   r1   )r   r+   rM  rL  )r!   )r   r+   r"   r+   r#   r+   rM  r+   )r   r+   r,   r+   rM  r+   )#r|  
__future__r   r   r   r   loggingr   r&  r   r   typingr   r   r   !evolve_server.core.skill_registryr	   object_storer   r   r   skill_bundler   r   r   r   r   r   	getLoggerry  r   r   r$   r/   r1   r  r   r   <module>r     sm    #     	  ' , , = S S  
		8	$x0G: G:r   