§
    ‰eîi©@  ã                   ó"  — d Z ddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
mZmZ  ej        e¦  «        Z e¦   «         Zedz  Zedz  Zdefd	„Zde
eef         fd
„Zde
eef         fd„Zdededefd„Zdedeeeef                  fd„Zdededefd„Zdedefd„Zd.dedefd„Zd.dededefd„Zedk    rÔ ed¦  «          ed¬¦  «        Z  e!e d         ¦  «        › d � e!e d!         ¦  «        › d"�e d#         › d$�gZ"e d%         r'e" #                     e!e d%         ¦  «        › d&�¦  «         e d'         r'e" #                     e!e d'         ¦  «        › d(�¦  «          ed)d* $                    e"¦  «        › d+e d,         › d-�¦  «         dS dS )/u  
Skills Sync -- Manifest-based seeding and updating of bundled skills.

Copies bundled skills from the repo's skills/ directory into ~/.hermes/skills/
and uses a manifest to track which skills have been synced and their origin hash.

Manifest format (v2): each line is "skill_name:origin_hash" where origin_hash
is the MD5 of the bundled skill at the time it was last synced to the user dir.
Old v1 manifests (plain names without hashes) are auto-migrated.

Update logic:
  - NEW skills (not in manifest): copied to user dir, origin hash recorded.
  - EXISTING skills (in manifest, present in user dir):
      * If user copy matches origin hash: user hasn't modified it â†’ safe to
        update from bundled if bundled changed. New origin hash recorded.
      * If user copy differs from origin hash: user customized it â†’ SKIP.
  - DELETED by user (in manifest, absent from user dir): respected, not re-added.
  - REMOVED from bundled (in manifest, gone from repo): cleaned from manifest.

The manifest lives at ~/.hermes/skills/.bundled_manifest.
é    N)ÚPath)Úget_hermes_home)ÚDictÚListÚTupleÚskillsz.bundled_manifestÚreturnc                  óŽ   — t          j        d¦  «        } | rt          | ¦  «        S t          t          ¦  «        j        j        dz  S )z°Locate the bundled skills/ directory.

    Checks HERMES_BUNDLED_SKILLS env var first (set by Nix wrapper),
    then falls back to the relative path from this source file.
    ÚHERMES_BUNDLED_SKILLSr   )ÚosÚgetenvr   Ú__file__Úparent)Úenv_overrides    ú3/home/div/.hermes/hermes-agent/tools/skills_sync.pyÚ_get_bundled_dirr   (   sA   € õ ”9Ð4Ñ5Ô5€LØð "Ý�LÑ!Ô!Ð!Ý•‰>Œ>Ô Ô'¨(Ñ2Ð2ó    c                  óš  — t                                ¦   «         si S 	 i } t                                d¬¦  «                             ¦   «         D ]e}|                     ¦   «         }|sŒd|v rC|                     d¦  «        \  }}}|                     ¦   «         | |                     ¦   «         <   Œ`d| |<   Œf| S # t          t          f$ r i cY S w xY w)zÑ
    Read the manifest as a dict of {skill_name: origin_hash}.

    Handles both v1 (plain names) and v2 (name:hash) formats.
    v1 entries get an empty hash string which triggers migration on next sync.
    úutf-8©Úencodingú:Ú )ÚMANIFEST_FILEÚexistsÚ	read_textÚ
splitlinesÚstripÚ	partitionÚOSErrorÚIOError)ÚresultÚlineÚnameÚ_Úhash_vals        r   Ú_read_manifestr'   4   sæ   € õ ×ÒÑ!Ô!ð Øˆ	ðØˆÝ!×+Ò+°WÐ+Ñ=Ô=×HÒHÑJÔJð 
	"ð 
	"ˆDØ—:’:‘<”<ˆDØð ØØ�dˆ{ˆ{à$(§N¢N°3Ñ$7Ô$7Ñ!��a˜Ø'/§~¢~Ñ'7Ô'7��t—z’z‘|”|Ñ$Ð$ð  "��t‘�ØˆøÝ•WÐð ð ð Øˆ	ˆ	ˆ	ðøøøs   �BB4 Â4C
Ã	C
Úentriesc                 óJ  — ddl }t          j                             dd¬¦  «         d                     d„ t          |                      ¦   «         ¦  «        D ¦   «         ¦  «        dz   }	 |                     t          t          j        ¦  «        dd¬	¦  «        \  }}	 t          j
        |d
d¬¦  «        5 }|                     |¦  «         |                     ¦   «          t          j        |                     ¦   «         ¦  «         ddd¦  «         n# 1 swxY w Y   t          j        |t          ¦  «         dS # t           $ r( 	 t          j        |¦  «         n# t$          $ r Y nw xY w‚ w xY w# t&          $ r.}t(                               dt          |d¬¦  «         Y d}~dS d}~ww xY w)z²Write the manifest file atomically in v2 format (name:hash).

    Uses a temp file + os.replace() to avoid corruption if the process
    crashes or is interrupted mid-write.
    r   NT©ÚparentsÚexist_okú
c              3   ó*   K  — | ]\  }}|› d |› �V — ŒdS )r   N© )Ú.0r$   r&   s      r   ú	<genexpr>z"_write_manifest.<locals>.<genexpr>X   s4   è è € ÐXÐX©n¨d°H˜Ð)Ð)˜xÐ)Ð)ÐXÐXÐXÐXÐXÐXr   z.bundled_manifest_z.tmp)ÚdirÚprefixÚsuffixÚwr   r   z&Failed to write skills manifest %s: %s)Úexc_info)Útempfiler   r   ÚmkdirÚjoinÚsortedÚitemsÚmkstempÚstrr   ÚfdopenÚwriteÚflushÚfsyncÚfilenoÚreplaceÚBaseExceptionÚunlinkr    Ú	ExceptionÚloggerÚdebug)r(   r7   ÚdataÚfdÚtmp_pathÚfÚes          r   Ú_write_manifestrN   O   sü  € ð €O€O€OåÔ×Ò t°dÐÑ;Ô;Ð;Ø�9Š9ÐXÐXÅÀwÇ}Â}ÁÄÑ@WÔ@WÐXÑXÔXÑXÔXÐ[_Ñ_€Dð`Ø×'Ò'Ý•MÔ(Ñ)Ô)Ø'Øð (ñ 
ô 
‰ˆˆHð
	Ý”˜2˜s¨WÐ5Ñ5Ô5ð %¸Ø—’˜‘”�Ø—’‘	”	�	Ý”˜Ÿš™œÑ$Ô$Ð$ð%ð %ð %ñ %ô %ð %ð %ð %ð %ð %ð %øøøð %ð %ð %ð %õ ŒJ�x¥Ñ/Ô/Ð/Ð/Ð/øÝð 	ð 	ð 	ðÝ”	˜(Ñ#Ô#Ð#Ð#øÝð ð ð Ø�ðøøøàð	øøøøõ ð `ð `ð `Ý�ŠÐ=½}ÈaÐZ^ˆÑ_Ô_Ð_Ð_Ð_Ð_Ð_Ð_Ð_øøøøð`øøøss   Á(2E* ÂD5 Â2ADÄD5 ÄDÄD5 ÄDÄD5 Ä5
E'Å EÅE'Å
E"ÅE'Å!E"Å"E'Å'E* Å*
F"Å4#FÆF"Úskill_mdÚfallbackc                 óž  — 	 |                       dd¬¦  «        dd…         }n# t          $ r |cY S w xY wd}|                     d¦  «        D ]�}|                     ¦   «         }|dk    r|r nbd	}Œ#|r\|                     d
¦  «        rG|                     dd¦  «        d                              ¦   «                              d¦  «        }|r|c S Œ‚|S )zORead the name field from SKILL.md YAML frontmatter, falling back to *fallback*.r   rC   )r   ÚerrorsNi   Fr-   z---Tzname:r   é   z"')r   r    Úsplitr   Ú
startswith)rO   rP   ÚcontentÚin_frontmatterr#   ÚstrippedÚvalues          r   Ú_read_skill_namerZ   p   s  € ðØ×$Ò$¨g¸iÐ$ÑHÔHÈÈ$ÈÔOˆˆøÝð ð ð Øˆˆˆðøøøà€NØ—’˜dÑ#Ô#ð 
ð 
ˆØ—:’:‘<”<ˆØ�uÒÐØð Ø�Ø!ˆNØØð 	˜h×1Ò1°'Ñ:Ô:ð 	Ø—N’N 3¨Ñ*Ô*¨1Ô-×3Ò3Ñ5Ô5×;Ò;¸EÑBÔBˆEØð Ø���øØ€Os   ‚" ¢1°1Úbundled_dirc                 ó  — g }|                       ¦   «         s|S |                      d¦  «        D ]Q}t          |¦  «        }d|v sd|v sd|v rŒ|j        }t	          ||j        ¦  «        }|                     ||f¦  «         ŒR|S )zz
    Find all SKILL.md files in the bundled directory.
    Returns list of (skill_name, skill_directory_path) tuples.
    zSKILL.mdz/.git/z	/.github/z/.hub/)r   Úrglobr=   r   rZ   r$   Úappend)r[   r   rO   Úpath_strÚ	skill_dirÚ
skill_names         r   Ú_discover_bundled_skillsrb   …   s£   € ð
 €FØ×ÒÑÔð Øˆà×%Ò% jÑ1Ô1ð /ð /ˆÝ�x‘=”=ˆØ�xÐÐ ;°(Ð#:Ð#:¸hÈ(Ð>RÐ>RØØ”Oˆ	Ý% h°	´Ñ?Ô?ˆ
Ø�Š�z 9Ð-Ñ.Ô.Ð.Ð.à€Mr   r`   c                 ó@   — |                       |¦  «        }t          |z  S )z 
    Compute the destination path in SKILLS_DIR preserving the category structure.
    e.g., bundled/skills/mlops/axolotl -> ~/.hermes/skills/mlops/axolotl
    )Úrelative_toÚ
SKILLS_DIR)r`   r[   Úrels      r   Ú_compute_relative_destrg   ™   s"   € ð
 ×
Ò
 Ñ
,Ô
,€CÝ˜ÑÐr   Ú	directoryc                 óÖ  — t          j        ¦   «         }	 t          |                      d¦  «        ¦  «        D ]‡}|                     ¦   «         rq|                     | ¦  «        }|                     t          |¦  «                             d¦  «        ¦  «         |                     | 	                    ¦   «         ¦  «         Œˆn# t          t          f$ r Y nw xY w|                     ¦   «         S )zHCompute a hash of all file contents in a directory for change detection.Ú*r   )ÚhashlibÚmd5r:   r]   Úis_filerd   Úupdater=   ÚencodeÚ
read_bytesr    r!   Ú	hexdigest)rh   ÚhasherÚfpathrf   s       r   Ú	_dir_hashrt   ¢   sÙ   € åŒ[‰]Œ]€FðÝ˜IŸOšO¨CÑ0Ô0Ñ1Ô1ð 	2ð 	2ˆEØ�}Š}‰Œð 2Ø×'Ò'¨	Ñ2Ô2�Ø—’�c #™hœhŸošo¨gÑ6Ô6Ñ7Ô7Ð7Ø—’˜e×.Ò.Ñ0Ô0Ñ1Ô1Ð1øð		2øõ
 •WÐð ð ð Øˆðøøøà×ÒÑÔÐs   •B*C  Ã CÃCFÚquietc                 óú  — t          ¦   «         }|                     ¦   «         s	g g dg g ddœS t                               dd¬¦  «         t	          ¦   «         }t          |¦  «        }d„ |D ¦   «         }g }g }g }d}|D �]ê\  }	}
t          |
|¦  «        }t          |
¦  «        }|	|vrâ	 |                     ¦   «         r7|dz  }t          |¦  «        |k    r|||	<   nx| st          d|	› d|	› d	�¦  «         n_|j	                             dd¬¦  «         t          j        |
|¦  «         |                     |	¦  «         |||	<   | st          d
|	› �¦  «         ŒÕ# t          t          f$ r"}| st          d|	› d|› �¦  «         Y d}~�Œd}~ww xY w|                     ¦   «         �rÅ|                     |	d¦  «        }t          |¦  «        }|s|||	<   ||k    r|dz  }n|dz  }�Œ_||k    r,|                     |	¦  «         | st          d|	› d�¦  «         �Œ‘||k    �rF	 |                     d¦  «        }t          j        t%          |¦  «        t%          |¦  «        ¦  «         	 t          j        |
|¦  «         |||	<   |                     |	¦  «         | st          d|	› d�¦  «         t          j        |d¬¦  «         nm# t          t          f$ rY |                     ¦   «         rC|                     ¦   «         s/t          j        t%          |¦  «        t%          |¦  «        ¦  «         ‚ w xY w�Œ¨# t          t          f$ r"}| st          d|	› d|› �¦  «         Y d}~�ŒÖd}~ww xY w|dz  }�Œå|dz  }�Œìt)          t+          |                     ¦   «         ¦  «        |z
  ¦  «        }|D ]}||= Œ|                     d¦  «        D ]¢}|                     |¦  «        }t          |z  }|                     ¦   «         sm	 |j	                             dd¬¦  «         t          j        ||¦  «         Œh# t          t          f$ r&}t4                               d||¦  «         Y d}~Œšd}~ww xY wŒ£t9          |¦  «         |||||t;          |¦  «        dœS )zî
    Sync bundled skills into ~/.hermes/skills/ using the manifest.

    Returns:
        dict with keys: copied (list), updated (list), skipped (int),
                        user_modified (list), cleaned (list), total_bundled (int)
    r   )ÚcopiedÚupdatedÚskippedÚuser_modifiedÚcleanedÚtotal_bundledTr*   c                 ó   — h | ]\  }}|’ŒS r/   r/   )r0   r$   r%   s      r   ú	<setcomp>zsync_skills.<locals>.<setcomp>Â   s   € Ð8Ð8Ð8™g˜d A�TÐ8Ð8Ð8r   rS   u     âš  uw   : bundled version shipped but you already have a local skill by this name â€” yours was kept. Run `hermes skills reset z)` to replace it with the bundled version.z  + z  ! Failed to copy ú: Nr   z  ~ z (user-modified, skipping)z.baku     â†‘ z
 (updated))Úignore_errorsz  ! Failed to update zDESCRIPTION.mdzCould not copy %s: %s)r   r   re   r8   r'   rb   rg   rt   Úprintr   ÚshutilÚcopytreer^   r    r!   ÚgetÚwith_suffixÚmover=   Úrmtreer:   ÚsetÚkeysr]   rd   Úcopy2rG   rH   rN   Úlen)ru   r[   ÚmanifestÚbundled_skillsÚbundled_namesrw   rx   rz   ry   ra   Ú	skill_srcÚdestÚbundled_hashrM   Úorigin_hashÚ	user_hashÚbackupr{   r$   Údesc_mdrf   Ú	dest_descs                         r   Úsync_skillsr—   °   s¨  € õ #Ñ$Ô$€KØ×ÒÑÔð 
à R°AØ¨BÀð
ð 
ð 	
õ
 ×Ò˜T¨DÐÑ1Ô1Ð1ÝÑÔ€HÝ-¨kÑ:Ô:€NØ8Ð8¨Ð8Ñ8Ô8€Mà€FØ€GØ€MØ€Gà!/ð Yñ YÑˆ
�IÝ% i°Ñ=Ô=ˆÝ  Ñ+Ô+ˆà˜XÐ%Ð%ðCØ—;’;‘=”=ð 3ð ˜q‘L�GÝ  ‘”¨,Ò6Ð6Ø/;˜ Ñ,Ð,Ø"ð ÝðG Zð Gð GàBLðGð Gð Gñô ð øð ”K×%Ò%¨d¸TÐ%ÑBÔBÐBÝ”O I¨tÑ4Ô4Ð4Ø—M’M *Ñ-Ô-Ð-Ø+7�H˜ZÑ(Ø ð 3ÝÐ1 ZÐ1Ð1Ñ2Ô2Ð2øøÝ�WÐ%ð Cð Cð CØð CÝÐA°
ÐAÐA¸aÐAÐAÑBÔBÐBøøøøùøøøøðCøøøð
 �[Š[‰]Œ]ñ 2	à"Ÿ,š, z°2Ñ6Ô6ˆKÝ! $™œˆIàð 	ð (1�˜Ñ$Ø Ò,Ð,Ø˜q‘L�G�Gð ˜q‘L�GÙà˜KÒ'Ð'à×$Ò$ ZÑ0Ô0Ð0Øð IÝÐG ÐGÐGÐGÑHÔHÐHÙð ˜{Ò*Ñ*ðIà!×-Ò-¨fÑ5Ô5�FÝ”K¥ D¡	¤	­3¨v©;¬;Ñ7Ô7Ð7ðÝœ¨	°4Ñ8Ô8Ð8Ø/;˜ Ñ,ØŸš zÑ2Ô2Ð2Ø$ð CÝ!Ð"A¨:Ð"AÐ"AÐ"AÑBÔBÐBåœ f¸DÐAÑAÔAÐAÐAøÝ#¥WÐ-ð ð ð à!Ÿ=š=™?œ?ð @°4·;²;±=´=ð @Ý"œK­¨F©¬µS¸±Y´YÑ?Ô?Ð?Øð	øøøñ Bøõ  ¥Ð)ð Ið Ið IØ ð IÝÐG°jÐGÐGÀAÐGÐGÑHÔHÐHøøøøùøøøøðIøøøð ˜1‘�‘ð �q‰LˆG‰Gõ •S˜Ÿš™œÑ)Ô)¨MÑ9Ñ:Ô:€GØð ð ˆØ�TˆNˆNð ×$Ò$Ð%5Ñ6Ô6ð Bð BˆØ×!Ò! +Ñ.Ô.ˆÝ Ñ$ˆ	Ø×ÒÑ!Ô!ð 	BðBØÔ ×&Ò&¨t¸dÐ&ÑCÔCÐCÝ”˜W iÑ0Ô0Ð0Ð0øÝ�WÐ%ð Bð Bð BÝ—’Ð4°g¸qÑAÔAÐAÐAÐAÐAÐAÐAøøøøðBøøøð		Bõ �HÑÔÐð ØØØ&ØÝ˜^Ñ,Ô,ðð ð sd   Â%B*EÅFÅ!E>Å>FÈAL#ÉAJ4Ê3L#Ê4A*LÌL#Ì#MÌ4MÍMÏ+1PÐQÐ.QÑQr$   Úrestorec           	      ó6  — t          ¦   «         }t          ¦   «         }t          |¦  «        }d„ |D ¦   «         }| |v }| |v }|s|sddd| › d�ddœS |r|| = t          |¦  «         d}|r}|sddd| › d	�ddœS t	          ||          |¦  «        }	|	                     ¦   «         rF	 t          j        |	¦  «         d
}n.# t          t          f$ r}
ddd| › d|	› d|
› �ddœcY d}
~
S d}
~
ww xY wt          d
¬¦  «        }|r|r	d}d| › d�}n|r	d}d| › d�}nd}d| › d�}d
|||dœS )u;  
    Reset a bundled skill's manifest tracking so future syncs work normally.

    When a user edits a bundled skill, subsequent syncs mark it as
    ``user_modified`` and skip it forever â€” even if the user later copies
    the bundled version back into place, because the manifest still holds
    the *old* origin hash. This function breaks that loop.

    Args:
        name: The skill name (matches the manifest key / skill frontmatter name).
        restore: If True, also delete the user's copy in SKILLS_DIR and let
                 the next sync re-copy the current bundled version. If False
                 (default), only clear the manifest entry â€” the user's
                 current copy is preserved but future updates work again.

    Returns:
        dict with keys:
          - ok: bool, whether the reset succeeded
          - action: one of "manifest_cleared", "restored", "not_in_manifest",
                    "bundled_missing"
          - message: human-readable description
          - synced: dict from sync_skills() if a sync was triggered, else None
    c                 ó   — i | ]\  }}||“Œ	S r/   r/   )r0   ra   r`   s      r   ú
<dictcomp>z'reset_bundled_skill.<locals>.<dictcomp>[  s   € ÐYÐYÐYÑ1F°¸Y�z 9ÐYÐYÐYr   FÚnot_in_manifestú'zi' is not a tracked bundled skill. Nothing to reset. (Hub-installed skills use `hermes skills uninstall`.)N)ÚokÚactionÚmessageÚsyncedÚbundled_missingup   ' has no bundled source â€” manifest entry cleared but cannot restore from bundled (skill was removed upstream).TÚmanifest_clearedzCleared manifest entry for 'z$' but could not delete user copy at r   ©ru   Úrestoredz
Restored 'z' from bundled source.z/' (no prior user copy, re-copied from bundled).zf'. Future `hermes update` runs will re-baseline against your current copy and accept upstream changes.)r'   r   rb   rN   rg   r   r‚   r‡   r    r!   r—   )r$   r˜   rŒ   r[   r�   Úbundled_by_nameÚin_manifestÚ
is_bundledÚdeleted_user_copyr�   rM   r¡   rŸ   r    s                 r   Úreset_bundled_skillrª   @  sR  € õ0 ÑÔ€HÝ"Ñ$Ô$€KÝ-¨kÑ:Ô:€NØYÐYÈ.ÐYÑYÔY€Oà˜(Ð"€KØ˜Ð(€Jàð 	
˜zð 	
àØ'ðI�Dð Ið Ið Ið ð
ð 
ð 	
ð ð "Ø�TˆNÝ˜Ñ!Ô!Ð!ð ÐØð Øð 		àØ+ðU˜ð Uð Uð Uð ðð ð õ & o°dÔ&;¸[ÑIÔIˆØ�;Š;‰=Œ=ð 	ðÝ”˜dÑ#Ô#Ð#Ø$(Ð!Ð!øÝ�WÐ%ð 	ð 	ð 	àØ0ð;°tð ;ð ;Ø/3ð;ð ;Ø78ð;ð ;ð #ðð ð ð ð ð ð ð øøøøð	øøøõ ˜tÐ$Ñ$Ô$€Fàð 
Ð$ð 
ØˆØ;˜tÐ;Ð;Ð;ˆˆØ	ð 	
àˆØT˜tÐTÐTÐTˆˆà#ˆðW¨4ð Wð Wð Wð 	ð
  &°WÈÐOÐOÐOs   ÂB6 Â6C!ÃCÃC!ÃC!Ú__main__z1Syncing bundled skills into ~/.hermes/skills/ ...r¤   rw   z newrx   z updatedry   z
 unchangedrz   z user-modified (kept)r{   z cleaned from manifestz
Done: z, z. r|   z total bundled.)F)%Ú__doc__rk   Úloggingr   r‚   Úpathlibr   Úhermes_constantsr   Útypingr   r   r   Ú	getLoggerÚ__name__rG   ÚHERMES_HOMEre   r   r   r=   r'   rN   rZ   rb   rg   rt   ÚboolÚdictr—   rª   r�   r"   r‹   Úpartsr^   r9   r/   r   r   ú<module>r·      sY  ððð ð, €€€Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ð Ð Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø $Ð $Ð $Ð $Ð $Ð $Ð $Ð $Ð $Ð $à	ˆÔ	˜8Ñ	$Ô	$€ð ˆoÑÔ€Ø˜8Ñ#€
ØÐ0Ñ0€ð	3˜$ð 	3ð 	3ð 	3ð 	3ð˜˜S #˜Xœð ð ð ð ð6`˜T # s (œ^ð `ð `ð `ð `ðB˜tð ¨sð °sð ð ð ð ð*¨$ð °4¸¸cÀ4¸iÔ8HÔ3Ið ð ð ð ð( dð ¸ð À$ð ð ð ð ð˜ð  #ð ð ð ð ðMð M�tð M¨ð Mð Mð Mð Mð`_Pð _P˜cð _P¨Dð _P¸Tð _Pð _Pð _Pð _PðD ˆzÒÐØ	€EÐ
=Ñ>Ô>Ð>Øˆ[˜uÐ%Ñ%Ô%€Fàˆ3ˆv�hÔÑ Ô Ð&Ð&Ð&Øˆ3ˆv�iÔ Ñ!Ô!Ð+Ð+Ð+Ø�)ÔÐ(Ð(Ð(ð€Eð
 ˆoÔð MØ�Š˜˜˜F ?Ô3Ñ4Ô4ÐKÐKÐKÑLÔLÐLØˆiÔð HØ�Š˜˜˜F 9Ô-Ñ.Ô.ÐFÐFÐFÑGÔGÐGØ	€EÐ
Q�T—Y’Y˜uÑ%Ô%Ð
QÐ
Q¨°Ô)@Ð
QÐ
QÐ
QÑRÔRÐRÐRÐRð Ðr   