+
    iHàjx$  ã                  ó†  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
HtHtHtHt ^ RIt^ RIHt ^ RIHt ^ RIHtHtHtHt ]P6                  ! ]4      t/ sR]R	&   Rs R
]R&   R R lt!R R lt"R R lt#R R lt$R R lt%R R lt&R R lt'R R lt(R R lt)R(R R llt*R  R! lt+R)R" R# llt,R$ R% lt-R& R' lt.R# )*u‹  Skill bundles â€” aliases that load multiple skills under one slash command.

YAML files in ``<HERMES_HOME>/skill-bundles/`` (``name``, ``description``,
``skills: [...]``, optional ``instruction``; file stem = fallback name).
``/<bundle>`` loads every member skill into one user message. If a bundle and a
skill share a slug, the bundle wins â€” slash dispatch checks bundles first, on purpose.
)ÚannotationsN)ÚPath)ÚAnyÚDictÚListÚOptionalÚTuple)Úget_hermes_home)Úatomic_write_text)Úcommand_snapshotÚdiff_command_snapshotsÚresolve_slash_keyÚslugify_skill_nameúdict[str, dict[str, Any]]Ú_bundles_cachezOptional[tuple]Ú_bundles_cache_mtimec               ó   € V ^8„  d   QhRR/# )é   Úreturnr   © )Úformats   "Ú8/home/ubuntu/.hermes/hermes-agent/agent/skill_bundles.pyÚ__annotate__r      s   € ÷ \ñ \�dñ \ó    c                 ó¤   € \         P                  P                  R4      p V '       d   \        V 4      P	                  4       # \        4       R,          # )z^Bundles directory: ``HERMES_BUNDLES_DIR`` override (tests) or ``<HERMES_HOME>/skill-bundles``.ÚHERMES_BUNDLES_DIRzskill-bundles)ÚosÚenvironÚgetr   Ú
expanduserr	   )Úoverrides    r   Ú_bundles_dirr!      s7   € ä�z‰z�~‰~Ð2Ó3€Hß*2Œ4�‹>×$Ñ$Ó&Ð[¼Ó8IÈOÕ8[Ð[r   c               ó   € V ^8„  d   QhRR/# )r   r   ú
list[Path]r   )r   s   "r   r   r   #   s   € ÷ gñ g˜Jñ gr   c            	     óº   € \        4       p V P                  4       '       d5   R UUu. uF%  p\        V P                  V4      4       F  q"NK  	  K'  	  upp# . # u uppi )ú*.yaml)r%   z*.yml)r!   ÚexistsÚsortedÚglob)ÚbaseÚextÚfs      r   Ú_iter_bundle_filesr,   #   sN   € Ü‹>€DØQU×Q\ÑQ\×Q^ÒQ^Ñ,ÔMÑ,�#´f¸T¿Y¹YÀs»^Ö6L°ŠAÑ6L‰AÑ,ÒMÐfÐdfÐfùÓMs   ¦+Ac               ó    € V ^8„  d   QhRRRR/# )r   Úfilesr#   r   Útupler   )r   s   "r   r   r   (   s   € ÷ 
"ñ 
"˜
ð 
" uñ 
"r   c                óþ   € . pV  FP  p VP                  \        V4      \        P                  ! VP	                  4       4      P                  4       34       KR  	  \        \        V4      4      #   \         d     Kv  i ; i)a  Sorted ``(path, content digest)`` of the bundle files. Content, not timestamps: a same-size
rewrite within one coarse mtime/ctime tick, or under host/filesystem clock skew, still moves it.
Bundles are a handful of small YAML files, so reading them per check is cheap.)	ÚappendÚstrÚhashlibÚsha256Ú
read_bytesÚ	hexdigestÚOSErrorr/   r'   )r.   Úentriesr+   s   &  r   Ú_bundles_sigr9   (   sj   € ð €GÛˆð	Ø�N‰NœC ›F¤G§N¢N°1·<±<³>Ó$B×$LÑ$LÓ$NÐOÖPñ ô
 ”˜“Ó!Ð!øô ô 	Úð	ús   ‰AA-Á-A<Á;A<c               ó    € V ^8„  d   QhRRRR/# )r   Úpathr   r   úOptional[dict[str, Any]]r   )r   s   "r   r   r   5   s   € ÷ ñ ˜Dð Ð%=ñ r   c                ó’  a €  \         P                  ! S P                  RR7      4      pR T 3R llp\        T\        4      '       g	   T! R4      # \        TP                  R	4      ;'       g    S P                  4      P                  4       pT'       g	   T! R
4      # TP                  R4      ;'       g    . p\        T\        4      '       d	   T'       g	   T! R4      # T Uu. uF=  p\        T4      P                  4       '       g   K$  \        T4      P                  4       NK?  	  ppT'       g	   T! R4      # \        T4      pT'       g	   T! R4      # R	TRTRTR\        S 4      R\        TP                  R4      ;'       g    R4      P                  4       ;'       g    R\        T4       R2R\        TP                  R4      ;'       g    R4      P                  4       /#   \         d#   p\        P                  RS T4        Rp?R# Rp?i\         P                   d#   p\        P                  RS T4        Rp?R# Rp?ii ; iu upi )z_Parse one bundle YAML; ``None`` (logged) on any error so a broken bundle can't break discovery.z	utf-8-sig)ÚencodingzCould not read bundle %s: %sNzInvalid YAML in bundle %s: %sc               ó    € V ^8„  d   QhRRRR/# )r   Úreasonr2   r   ÚNoner   )r   s   "r   r   Ú'_load_bundle_file.<locals>.__annotate__?   s   € ÷ ?ñ ?�cð ?˜dñ ?r   c                ó6   <€ \         P                  R SV 4       R# )zBundle %s %s; skippingN)ÚloggerÚwarning)r@   r;   s   &€r   Ú_skipÚ _load_bundle_file.<locals>._skip?   s   ø€ Ü�‰Ð/°°vÖ>r   zis not a mappingÚnamezhas no nameÚskillszhas no skills listzhas empty skills listzyielded empty slugÚslugr;   ÚdescriptionÚ zLoad z skills as a bundleÚinstruction)ÚyamlÚ	safe_loadÚ	read_textr7   rD   rE   Ú	YAMLErrorÚ
isinstanceÚdictr2   r   ÚstemÚstripÚlistÚ_slugifyÚlen)	r;   ÚdataÚexcrF   rH   Ú
raw_skillsÚsrI   rJ   s	   f        r   Ú_load_bundle_filer]   5   sÈ  ø€ ðÜ�~Š~˜dŸn™n°k˜nÓBÓCˆ÷?ð ?ä�dœD×!Ò!ÙÐ'Ó(Ð(Üˆt�x‰x˜Ó×,Ð, 4§9¡9Ó-×3Ñ3Ó5€DßÙ�]Ó#Ð#Ø—‘˜(Ó#×)Ð) r€JÜ�j¤$×'Ò'¯zÙÐ)Ó*Ð*Ù&0ÓC¡j ´C¸³F·L±L·NŒnŒc�!‹f�l‰lŽn¡j€FÐCßÙÐ,Ó-Ð-Ü�D‹>€DßÙÐ)Ó*Ð*à��f˜d H¨f°f¼cÀ$»iØ”s˜4Ÿ8™8 MÓ2×8Ð8°bÓ9×?Ñ?ÓA×mÐmÀuÌSÐQWË[ÈMÐYlÐEmØ”s˜4Ÿ8™8 MÓ2×8Ð8°bÓ9×?Ñ?ÓAðð øô- ô Ü�‰Ð5°t¸SÔAÝûÜ�>‰>ô Ü�‰Ð6¸¸cÔBÝûðüò Ds5   ƒ&G Ã!!IÄIÇIÇ*HÈIÈIÈH<È<Ic               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r   V   s   € ÷ ñ Ð/ñ r   c                 óð   € \        4       p \        V 4      p/ pV  FT  p\        V4      pV'       g   K  RVR,           2pWR9   d'   \        P	                  RWSW%,          R,          4       KP  WBV&   KV  	  VsVsV# )z\Rebuild the ``"/slug"`` -> bundle info cache; duplicate slugs keep the first (alphabetical).Ú/rJ   z,Duplicate bundle slug %s from %s; keeping %sr;   )r,   r9   r]   rD   rE   r   r   )r.   ÚsigÚoutr+   ÚinfoÚkeys         r   Úscan_bundlesrf   V   s�   € ô Ó €Eä
�uÓ
€CØ%'€CÛˆÜ  Ó#ˆßÙØ�$�v•,�Ð ˆØŒ:Ü�N‰NÐIÈ3ÐSVÕS[Ð\bÕScÔdÙØˆC‹ñ ð €NØÐØ€Jr   c               ó   € V ^8„  d   QhRR/# r_   r   )r   s   "r   r   r   k   s   € ÷ ñ Ð4ñ r   c                 óv   € \        \        4       4      p \        '       d   \        V 8w  d   \	        4        \        # )z[Current bundle mapping; rescans only when the set of bundle files or their content changed.)r9   r,   r   r   rf   )Úcurrent_mtimes    r   Úget_skill_bundlesrj   k   s(   € ä Ô!3Ó!5Ó6€Mß‹>Ô1°]ÔBÜŒÜÐr   c               ó    € V ^8„  d   QhRRRR/# )r   Úcommandr2   r   zOptional[str]r   )r   s   "r   r   r   s   s   € ÷ ;ñ ;¨ð ;°ñ ;r   c                ó*   € \        V \        4       4      # )ub   Resolve a user-typed command to its ``/slug`` key (``_`` â‰¡ ``-``, as Telegram rewrites hyphens).)r   rj   )rl   s   &r   Úresolve_bundle_command_keyrn   s   s   € ä˜WÔ&7Ó&9Ó:Ð:r   c               ó   € V ^8„  d   QhRR/# )r   r   zdict[str, Any]r   )r   s   "r   r   r   x   s   € ÷ Lñ L˜ñ Lr   c                 óZ   € \        \        4      p \        V \        \        4       4      4      # )zgRe-scan and return an ``added``/``removed``/``unchanged``/``total`` diff (same shape as reload_skills).)r   r   r   rf   )Úbefores    r   Úreload_bundlesrr   x   s!   € äœnÓ-€FÜ! &Ô*:¼<»>Ó*JÓKÐKr   c               ó   € V ^8„  d   QhRR/# )r   r   zlist[dict[str, Any]]r   )r   s   "r   r   r   ~   s   € ÷ Iñ IÐ*ñ Ir   c                 óJ   € \        \        4       P                  4       R R7      # )z6Return a sorted list of bundle info dicts for display.c                ó   € V R ,          # )rJ   r   )Úbs   &r   Ú<lambda>Úlist_bundles.<locals>.<lambda>€   s   € ¸aÀ¾ir   )re   )r'   rj   Úvaluesr   r   r   Úlist_bundlesrz   ~   s   € äÔ#Ó%×,Ñ,Ó.Ñ4GÔHÐHr   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   Úcmd_keyr2   Úuser_instructionÚtask_idz
str | NoneÚplatformr   z*Optional[tuple[str, list[str], list[str]]]r   )r   s   "r   r   r   ƒ   s;   € ÷ #Iñ #IØð#IØ$'ð#IØ7Að#IØT^ð#Ià/ñ#Ir   c           
     óº  aaa€ \        4       P                  V 4      pV'       g   R# ^ RIHpHpHoHp VR,          oT! VR,           Uu. uF  qˆ;'       g    RP                  4       NK  	  upVV3R lV3R lSV! V4      R7      w  ršr¼V'       g   R# T! R	S R
2T	RS 2.T
Y´P                  R4      ;'       g    RVR7      pRP                  V.VO4      Wš3# u upi )u�  Build the user message for a bundle invocation: ``(message,
loaded_skill_names, missing_skill_names)`` or ``None`` if the bundle wasn't
found. Uninstalled members are skipped with a note; disabled ones too, since
``_load_skill_payload`` bypasses the scan-time filter (``platform`` scopes
that check â€” gateway passes it, None resolves from env).

Disabled skills are also skipped: bundles load members via ``_load_skill_payload`` directly, bypassing
the scan-time disabled filter in ``get_skill_commands()``, so the disabled list must be re-applied here.
``platform`` scopes the check to a specific platform's ``skills.platform_disabled`` config (gateway
dispatch passes it explicitly because the gateway handles multiple platforms in one process); when
*None*, the platform resolves from session env vars and the global disabled list still applies. Mirrors
the stacked-skill gate in gateway dispatch (#58888).
N)Ú_disabled_skill_namesÚ_load_skill_blocksÚ_load_skill_payloadÚ_scaffold_headerrH   rI   rL   c                ó   <€ S! V SR 7      # ))r~   r   )Ú
identifierrƒ   r~   s   &€€r   rw   Ú1build_bundle_invocation_message.<locals>.<lambda>›   s   ø€ Ñ.¨zÀ7ÕKr   c                ó   <€ R S R2# )z[Loaded as part of the "z" skill bundle.]r   )Ú_nameÚbundle_names   &€r   rw   r‡   œ   s   ø€ Ð0°°Ð=MÑNr   )Údisabled_namesÚ"z" skill bundlezBundle: rM   )Ú
lead_linesÚmissingÚdisabledÚextra_instructionr}   z

)	rj   r   Úagent.skill_commandsr�   r‚   rƒ   r„   rU   Újoin)r|   r}   r~   r   rd   r�   r‚   r„   Úskill_idÚloaded_namesrŽ   r�   Úskill_blocksÚheaderrƒ   rŠ   s   &&f&          @@r   Úbuild_bundle_invocation_messager—   ƒ   sÚ   ú€ ô  Ó×"Ñ" 7Ó+€DßÙçuÓuØ�v•,€KÙ4FØ26°x¶.ÓA±. h�.ˆ.�b×	Ñ	Ö	!±.ÑAÝKÜNØÙ,¨XÓ6ô5Ñ1€L˜8÷ ÙÙØ
ˆKˆ=˜Ð'¨ÀXÈkÈ]ÐC[ÐB\ÐfmØ¯X©X°mÓ-D×-JÐ-JÈÐ]mô€Fð �K‰K˜Ð/ ,Ð/Ó0°,ÐHÐHùò 	Bs   Á	CÁCc               ó    € V ^8„  d   QhRRRR/# ©r   rH   r2   r   r   r   )r   s   "r   r   r   ¬   s   € ÷ +ñ +˜#ð + $ñ +r   c                óp   € \        V 4      pV'       g   \        RV : R24      h\        4       V R2,          # )z7Return the canonical filesystem path for a bundle name.zBundle name z normalizes to an empty slugz.yaml)rW   Ú
ValueErrorr!   )rH   rJ   s   & r   Úbundle_path_forrœ   ¬   s6   € ä�D‹>€DßÜ˜<¨¡xÐ/KÐLÓMÐMÜ‹>˜t˜f E˜NÕ*Ð*r   c               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   rH   r2   rI   z	list[str]rK   rM   Ú	overwriteÚboolr   r   r   )r   s   "r   r   r   ´   s9   € ÷ ñ �cð  9ð ¸3ð ÐRUð Ðgkð Ðx|ñ r   c           	     óª  € T ;'       g    RP                  4       p V '       g   \        R4      hV Uu. uF=  p\        V4      P                  4       '       g   K$  \        V4      P                  4       NK?  	  ppV'       g   \        R4      h\        V 4      pVP	                  4       '       d   V'       g   \        RV 24      hVP                  P                  RRR7       RV RV/pTP                  R	V3R
V33 U	U
u/ uF  w  ršV
'       g   K  WšbK  	  up
p	4       \        V\        P                  ! VRRR7      RR7       \        4        V# u upi u up
p	i )z˜Write a bundle to disk and refresh the cache. Raises ``FileExistsError``
if the target exists and not ``overwrite``; ``ValueError`` for unusable inputs.rL   zBundle name is requiredz(Bundle must reference at least one skillzBundle already exists at T)ÚparentsÚexist_okrH   rI   rK   rM   F)Ú	sort_keysÚallow_unicode)Úpreserve_mode)rU   r›   r2   rœ   r&   ÚFileExistsErrorÚparentÚmkdirÚupdater
   rN   Ú	safe_dumprf   )rH   rI   rK   rM   rž   r\   Úcleaned_skillsr;   ÚpayloadÚkÚvs   &&&&&      r   Úsave_bundler¯   ´   s  € ð �JˆJ�B×ÑÓ€DßÜÐ2Ó3Ð3Ù.4ÓG©f¨¼¸A»¿¹¿”n”c˜!“f—l‘l–n©f€NÐGßÜÐCÓDÐDÜ˜4Ó €DØ‡{�{‡}‚}ŸYÜÐ 9¸$¸Ð@ÓAÐAØ‡K�K×Ñ˜d¨TÐÔ2Ø% t¨X°~ÐF€GØ‡N�N }°kÐ&BÀ]ÐT_ÐD`Ñ%aÔgÑ%a™T˜QÔef”D�A’DÑ%aÒgÔhä�dœDŸNšN¨7¸eÐSWÔXÐhlÕmÜ„NØ€Kùò Hùó hs   ²!E
ÁE
Ã:E
ÄE
c               ó    € V ^8„  d   QhRRRR/# r™   r   )r   s   "r   r   r   É   s   € ÷ ñ ˜ð  ñ r   c                ó˜   € \        V 4      pVP                  4       '       g   \        RV 24      hVP                  4        \	        4        V# )zMDelete a bundle by name and return its path; ``FileNotFoundError`` if absent.zNo bundle at )rœ   r&   ÚFileNotFoundErrorÚunlinkrf   )rH   r;   s   & r   Údelete_bundler´   É   s<   € ä˜4Ó €DØ�;‰;�=Š=Ü -°¨vÐ 6Ó7Ð7Ø‡K�K„MÜ„NØ€Kr   c               ó    € V ^8„  d   QhRRRR/# )r   rH   r2   r   r<   r   )r   s   "r   r   r   Ó   s   € ÷ 9ñ 9�Sð 9Ð5ñ 9r   c                óL   € \        4       P                  R\        V 4       24      # )z+Look up a bundle by name (slug-normalized).ra   )rj   r   rW   )rH   s   &r   Ú
get_bundler·   Ó   s"   € äÓ×"Ñ" Q¤x°£~Ð&6Ð#7Ó8Ð8r   )rL   NN)rL   rL   F)/Ú__conditional_annotations__Ú__doc__Ú
__future__r   r3   Úloggingr   Úpathlibr   Útypingr   r   r   r   r   Úhermes_yamlrN   Úhermes_constantsr	   Úutilsr
   r‘   r   r   r   r   rW   Ú	getLoggerÚ__name__rD   r   Ú__annotations__r   r!   r,   r9   r]   rf   rj   rn   rr   rz   r—   rœ   r¯   r´   r·   )r¸   s   @r   Ú<module>rÄ      s¤   øðôõ #ã Û Û 	Ý ß 3Õ 3ã å ,Ý #ß |Ó |à	×	Ò	˜8Ó	$€à,.€Ð)Ó .Ø(,Ð �oÓ ,õ\õgõ

"õõBõ*õ;õ
LõI÷
#IõR+÷õ*÷9r   