Ë
    áß²jŒ‹  ã            
       óZ  — U d 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mZmZmZmZ ddlmZmZmZmZ  ej,                  e«      Zdddd	œZ ed
«      Z ed«      ZdZdZdZdZ dedee!   fd„Z"dedee!df   fd„Z#dede$fd„Z%dedee!   fd„Z&ddœdee   de$fd„Z'ddœdee   de$fd„Z(da)de!fd„Z*de!deee!e
f   e!f   fd„Z+de
de$fd„Z,d ee!e
f   de$fd!„Z-i Z.ee!e$f   e/d"<   de$fd#„Z0de$fd$„Z1e0e1d%„ d&œZ2ee!eg e$f   f   e/d'<   d(e!de$fd)„Z3d ee!e
f   de$fd*„Z4d ee!e
f   de$fd+„Z5i Z6eee!e7e7e7e7f   ee!e
f   f   e/d,<   dud-„Z8d.edeee!e7e7e7e7f      fd/„Z9dee!e
f   fd0„Z:deee!e
f      fd1„Z;d2e!de
fd3„Z<d4e!defd5„Z=d6edefd7„Z> ed8h«      Z?ee/d9<   dvd:e!dz  dee!   fd;„Z@dee!   fd<„ZAdee!   fd=„ZBi ZCeee!e7e7e7e7f   ee   f   e/d><   dud?„ZDdee!   fd@„ZEdee   fdA„ZFdee   fdB„ZGde!fdC„ZHdee   fdD„ZIej”                  j—                  dEdF«      ej”                  j—                  dGdF«      fZLdHZMdvdIee   dee   fdJ„ZNdee   fdK„ZOdede$fdL„ZPdedee   fdM„ZQdNe$dee   fdO„ZRdee   fdP„ZSdeeee7f      fdQ„ZTdRZUi ZVee!e$f   e/dS<   de$fdT„ZWdUefdV„ZXdWe!de!fdX„ZYdefdY„ZZde$fdZ„Z[d ee!e
f   dee!e
f   fd[„Z\d\Z]d ee!e
f   dee!ef   fd]„Z^d ee!e
f   deee!e
f      fd^„Z_deee!e
f      fd_„Z`d`Zadaee!e
f   dbe!fdc„Zb ejÆ                  dd«      Zddee!de!fdf„Zedgeee!e
f      dee!e
f   fdh„ZfdiZgd ee!e
f   de!fdj„Zhd ee!e
f   de!fdk„Zid ee!e
f   de$fdl„Zjdedme!fdn„Zk ejÆ                  do«      Zldpe!deee!   e!f   fdq„Zmdree!   de$fds„Zndee   fdt„Zoy)wzœLightweight skill metadata utilities shared by prompt_builder and skills_tool.
Import-light by design: no tool registry, CLI config, or provider resolution.é    N)ÚPathÚPurePath)ÚAnyÚCallableÚDictÚListÚOptionalÚSetÚTuple)Úget_config_pathÚget_skills_dirÚget_subprocess_homeÚ	is_termuxÚdarwinÚlinuxÚwin32)Úmacosr   Úwindows)ú.gitz.githubz.hubz.archivez.curator_backupsz.locksz.venvÚvenvÚnode_moduleszsite-packagesÚ__pycache__z.toxz.noxz.pytest_cachez.mypy_cachez.ruff_cache)Ú
referencesÚ	templatesÚassetsÚscriptsÚ_orgz.active_orgz.org-provenance.jsonz.org-baseline.jsonÚ
skills_dirÚreturnc                 ó®   — | t         z  t        z  }	 |j                  «       r$|j                  d¬«      j	                  «       xs dS dS # t
        $ r Y yw xY w)zBThe org id whose mirror may resolve, or None (no org skills load).úutf-8©ÚencodingN)ÚORG_MIRROR_DIR_NAMEÚORG_ACTIVE_MARKERÚexistsÚ	read_textÚstripÚOSError)r   Úmarkers     ú6/home/ubuntu/.hermes/hermes-agent/agent/skill_utils.pyÚread_active_org_idr,   *   sZ   € àÔ-Ñ-Ô0AÑA€FðØGMÇ}Á}Ä�× Ñ ¨'Ð Ó2×8Ñ8Ó:ÒB¸dÐ`Ð\`Ð`øÜò Ùðús   ’3A ÁA Á	AÁA.c                 óä   — 	 t        | «      j                  «       j                  t        |«      j                  «       «      j                  }|r|d   t        k(  r|S dS # t        t
        f$ r Y yw xY w)zTPath parts of *path* relative to *skills_dir* if it is under ``_org/``, else ``()``.© r   )r   ÚresolveÚrelative_toÚpartsr)   Ú
ValueErrorr$   ©Úpathr   r1   s      r+   Ú_org_rel_partsr5   3   sl   € ðÜ�T“
×"Ñ"Ó$×0Ñ0´°jÓ1A×1IÑ1IÓ1KÓL×RÑRˆñ ˜e A™hÔ*=Ò=ˆ5ÐEÀ2ÐEøô ”ZÐ ò Ùðús   ‚A	A ÁA/Á.A/c                 ó,   — t        t        | |«      «      S )z6True when *path* is inside the org mirror (``_org/``).)Úboolr5   )r4   r   s     r+   Úis_org_mirror_pathr8   <   s   € ä”˜t ZÓ0Ó1Ð1ó    c                 óD   — t        | |«      }t        |«      dk\  r|d   S dS )z@The ``<org_id>`` segment for a path under ``_org/<org_id>/...``.é   é   N)r5   Úlenr3   s      r+   Úorg_id_of_pathr>   A   s'   € ä˜4 Ó,€EÜ˜5“z Q’ˆ5�‰8Ð0¨DÐ0r9   ©Úrootr@   c                ó€   — t        t        | «      «      j                  }t        d„ |D «       «      xs t	        | |¬«      S )z—True if *path* should be skipped by skill scanners (VCS/dependency/cache
    dirs + support packages). Apply to every SKILL.md from a direct ``rglob``.c              3   ó,   K  — | ]  }|t         v –— Œ y ­w©N)ÚEXCLUDED_SKILL_DIRS)Ú.0Úparts     r+   ú	<genexpr>z)is_excluded_skill_path.<locals>.<genexpr>K   s   è ø€ Ò=¨tˆtÔ*Ô*Ñ=ùs   ‚r?   )r   Ústrr1   ÚanyÚis_skill_support_path)r4   r@   r1   s      r+   Úis_excluded_skill_pathrK   G   s8   € ô ”S˜“YÓ×%Ñ%€EÜÑ=°uÔ=Ó=ÒgÔAVÐW[ÐbfÔAgÐgr9   c                óè   ‡‡— t        | t        «      r| nt        t        | «      «      }|j                  Š|�|j	                  «       s|n	t        «       Št        ˆˆfd„t        ‰dd «      D «       «      S )zžTrue if *path* is under a support dir sitting directly inside a skill root
    (``skills/scripts/foo`` stays discoverable: no ``SKILL.md`` above ``scripts``).Nc              3   ó€   •K  — | ]5  \  }}|d kD  r+|t         v xr ‰t        ‰d| Ž z  dz  j                  «       –— Œ7 y­w)r   NúSKILL.md)ÚSKILL_SUPPORT_DIRSr   r&   )rE   ÚidxrF   Úbaser1   s      €€r+   rG   z(is_skill_support_path.<locals>.<genexpr>U   sP   øè ø€ ò áˆC�Ø�Š7ð 	Ô"Ð"ÒX¨¬t°U¸4¸C°[Ð/AÑ(AÀJÑ(N×'VÑ'VÓ'XÓXñùs   ƒ;>éÿÿÿÿ)Ú
isinstancer   rH   r1   Úis_absoluterI   Ú	enumerate)r4   r@   Úpath_objrQ   r1   s      @@r+   rJ   rJ   N   sd   ù€ ô " $¬Ô-‰t´4¼¸D»	³?€HØ�N‰N€EØÐ#¨H×,@Ñ,@Ô,B‰4ÌË€Däô ä" 5¨¨" :Ó.ôó ð r9   Úcontentc                 ó    — t         €>ddl}ddl}|j                  |j                  t        |dd«      xs |j                  ¬«      a t        | «      S )z7Parse YAML with lazy import and CSafeLoader preference.Nr   ÚCSafeLoader)ÚLoader)Ú_yaml_load_fnÚ	functoolsÚyamlÚpartialÚloadÚgetattrÚ
SafeLoader)rW   r\   r]   s      r+   Ú	yaml_loadrb   _   sJ   € ô ÐÛÛØ!×)Ñ)¨$¯)©)¼GÀDÈ-ÐY]Ó<^Ò<qÐbf×bqÑbqÐ)ÓrˆÜ˜Ó!Ð!r9   c                 ó
  — | j                  d«      } | j                  d«      rt        j                  d| dd «      nd}|si | fS | d|j	                  «       dz    }| |j                  «       dz   d }i }	 t        |«      }t        |t        «      r|}||fS # t        $ re |j                  «       j                  d«      D ]=  }d|v sŒ|j                  dd«      \  }}|j                  «       ||j                  «       <   Œ? Y ||fS w xY w)	zêParse YAML frontmatter from markdown; returns (frontmatter_dict, body).
    Malformed YAML falls back to key:value line splitting. A leading UTF-8 BOM
    (Windows editors) is stripped first or it would defeat the ``---`` fence check.u   ï»¿z---z
\n---\s*\né   NÚ
ú:r<   )ÚremoveprefixÚ
startswithÚreÚsearchÚstartÚendrb   rS   ÚdictÚ	Exceptionr(   Úsplit)	rW   Ú	end_matchÚyaml_contentÚbodyÚfrontmatterÚparsedÚlineÚkeyÚvalues	            r+   Úparse_frontmatterrx   i   s  € ð ×"Ñ" 8Ó,€GØ9@×9KÑ9KÈEÔ9R”—	‘	˜-¨°°¨Ô5ÐX\€IÙØ�7ˆ{ÐØ˜1˜yŸ™Ó0°1Ñ4Ð5€LØ�9—=‘=“? QÑ&Ð(Ð)€DØ"$€Kð9Ü˜<Ó(ˆÜ�fœdÔ#Ø ˆKð ˜ÐÐøô ò 9Ø ×&Ñ&Ó(×.Ñ.¨tÓ4ò 	9ˆDØ�dŠ{Ø!ŸZ™Z¨¨QÓ/‘
��UØ+0¯;©;«=�˜CŸI™I›KÒ(ñ	9ð ˜ÐÐð9ús   Á3B Â/DÃ8DÄDÚ	platformsc                 ó  — | syt        «       }t        | t        «      r| n| gD ]g  }t        |«      j	                  «       j                  «       }t        j                  ||«      }t        j                  j                  |«      s|sŒb|dv sŒg y y)z?Return True when *platforms* is compatible with the current OS.T)r   ÚtermuxÚandroidF)r   rS   ÚlistrH   Úlowerr(   ÚPLATFORM_MAPÚgetÚsysÚplatformrh   )ry   Úrunning_in_termuxr‚   Ú
normalizedÚmappeds        r+   Úskill_matches_platform_listr†   €   s€   € áØÜ!›ÐÜ!+¨I´tÔ!<‘IÀ9À+ò ˆÜ˜“]×(Ñ(Ó*×0Ñ0Ó2ˆ
Ü×!Ñ! *¨jÓ9ˆô �<‰<×"Ñ" 6Ô*Ò/@ÀVÐOmÒEmÙðð r9   rs   c                 ó6   — t        | j                  d«      «      S )zITrue when the skill's ``platforms:`` list (absent = all) matches this OS.ry   )r†   r€   ©rs   s    r+   Úskill_matches_platformr‰   �   s   € ä& {§¡°{Ó'CÓDÐDr9   Ú_ENV_DETECT_CACHEc                  óæ   — t        j                  d«      st        j                  d«      r	 ddlm}   | «       }|ry	 ddlm} t         |«       «      S # t        $ r d}Y Œ'w xY w# t        $ r Y yw xY w)NÚHERMES_KANBAN_TASKÚHERMES_KANBAN_BOARDr   )Ú"is_dispatcher_owned_worker_contextT)Ú_profile_has_kanban_toolsetF)ÚosÚgetenvÚagent.delegation_contextrŽ   rn   Útools.kanban_toolsr�   r7   )rŽ   Úownedr�   s      r+   Ú_detect_kanbanr•   ›   su   € ô 
‡y�yÐ%Ô&¬"¯)©)Ð4IÔ*Jð	ÝSÙ6Ó8ˆEñ ØðÝBÜÑ/Ó1Ó2Ð2øô ò 	ØŠEð	ûô ò Ùðús"   ¬A ½A$ ÁA!Á A!Á$	A0Á/A0c                  ó<   — 	 ddl m}   | «       S # t        $ r Y yw xY w)Nr   ©Úis_containerF)Úhermes_constantsr˜   rn   r—   s    r+   Ú_detect_dockerrš   ®   s$   € ðÝ1Ù‹~ÐøÜò Ùðús   ‚ �	šc                  ó‚   — t         j                  j                  d«      xs t         j                  j                  d«      S )Nz/run/s6z/package/admin/s6-overlay)r�   r4   Úisdirr.   r9   r+   ú<lambda>r�   ¸   s&   € ”"—'‘'—-‘- 	Ó*ÒX¬b¯g©g¯m©mÐ<WÓ.X€ r9   )ÚkanbanÚdockerÚs6Ú_ENV_DETECTORSÚenvc                 ó„   — | dk7  r| t         v r	t         |    S t        j                  | «      }|r |«       nd}|t         | <   |S )a   True when the named runtime environment is active (unknown => True).
    Cached per process EXCEPT ``kanban``: that verdict is context-dependent
    (delegate children / in-process cron see the worker's vars), so a
    process-wide cache would leak the first asker's answer to the others.rž   T)rŠ   r¡   r€   )r¢   ÚdetectorÚresults      r+   Ú_detect_environmentr¦   ¼   sI   € ð
 ˆh‚˜3Ô"3Ñ3Ü  Ñ%Ð%Ü×!Ñ! #Ó&€HÙ#‰XŒZ¨€FØ#Ô�cÑØ€Mr9   c                 óæ   — | j                  d«      }|syt        |t        «      r|n|gD �cg c])  }t        |«      j	                  «       j                  «       ‘Œ+ }}t        d„ |D «       «      S c c}w )zzTrue when ANY declared ``environments:`` tag is active (absent = all;
    unknown tags fail open). Offer-time filter only.ÚenvironmentsTc              3   ó8   K  — | ]  }|sŒt        |«      –— Œ y ­wrC   )r¦   )rE   Útags     r+   rG   z,skill_matches_environment.<locals>.<genexpr>Ð   s   è ø€ Ò?¨Cº3Ô" 3×'Ñ?ùs   ‚Š)r€   rS   r}   rH   r~   r(   rI   )rs   r¨   r¢   Útagss       r+   Úskill_matches_environmentr¬   É   sf   € ð —?‘? >Ó2€LÙØÜAKÈLÔZ^ÔA_±ÐfrÐesÖu¨ŒC�‹H�N‰NÓ×"Ñ"Õ$Ðu€DÐuÜÑ?°4Ô?Ó?Ð?ùò vs   ¬.A.c                 ó  — | j                  d«      }|syddlm} ddlm} t        |t        «      r|n|gD ]N  }|j                  t        |«      j                  «       «      }|�|j                  € y ||«      j                  rŒN y y)a„  True when every app named in ``requires_apps:`` has a registered declaration this host satisfies.

    Names resolve through ``hermes_platform.declaration`` (registered by whoever owns the server,
    e.g. the plugin loader); the check is the same ``availability()`` the MCP check_fn uses. An
    unknown name hides the skill (fail closed). Offer-time filter, like ``environments:``.
    Úrequires_appsTr   )Údeclaration)ÚavailabilityF)r€   Úhermes_platformr¯   Ú%hermes_platform.resolver.availabilityr°   rS   r}   ÚlookuprH   r(   ÚappÚ	offerable)rs   Únamesr¯   r°   ÚnameÚdecls         r+   Úskill_matches_appsr¹   Ó   s|   € ð �O‰O˜OÓ,€EÙØÝ+ÝBä# E¬4Ô0‘°u°gò ˆØ×!Ñ!¤# d£)§/¡/Ó"3Ó4ˆØˆ<˜4Ÿ8™8Ð+ÙÙ˜DÓ!×+Ó+Ùðð r9   Ú_RAW_CONFIG_CACHEc                  ó,   — t         j                  «        y)u/   Test hook â€” drop the shared raw config cache.N)rº   Úclearr.   r9   r+   Ú_raw_config_cache_clearr½   ì   s   € ä×ÑÕr9   Úconfig_pathc                 ót   — 	 ddl m} t        | «      g || j                  «       «      ¢­S # t        $ r Y yw xY w)zT``(path, *file_signature)`` identity of config.yaml, or None when unreadable/absent.r   )Úfile_signatureN)ÚutilsrÀ   rH   Ústatr)   )r¾   rÀ   s     r+   Ú_config_cache_keyrÃ   ñ   s=   € ðÝ(Ü�KÓ ÐF¡>°+×2BÑ2BÓ2DÓ#EÑFÐFøÜò Ùðús   ‚(+ «	7¶7c                  ó†  — t        «       } | j                  «       si S t        | «      }|�t        j	                  |«      nd}|�|S 	 t        | j                  d¬«      «      }t        |t        «      si S |�t        j                  «        |t        |<   |S # t        $ r#}t        j                  d| |«       i cY d}~S d}~ww xY w)zNRead config.yaml with an mtime+size keyed cache (no hermes_cli.config import).Nr!   r"   z"Could not read skill config %s: %s)r   r&   rÃ   rº   r€   rb   r'   rn   ÚloggerÚdebugrS   rm   r¼   )r¾   Ú	cache_keyÚcachedrt   Úes        r+   Ú_load_raw_configrÊ   ú   sÀ   € ä!Ó#€KØ×ÑÔØˆ	Ü! +Ó.€IØ1:Ð1FÔ×"Ñ" 9Ô-ÈD€FØÐØˆðÜ˜;×0Ñ0¸'Ð0ÓBÓCˆô �fœdÔ#Øˆ	ØÐÜ×ÑÔ!Ø'-Ô˜)Ñ$Ø€Møô ò Ü�‰Ð9¸;ÈÔJØ�	ûðús   ÁB Â	C ÂB;Â5C Â;C c                  ó\   — t        «       j                  d«      } t        | t        «      r| S dS )zHThe ``skills:`` mapping from config.yaml, or None when absent/malformed.ÚskillsN)rÊ   r€   rS   rm   )Ú
skills_cfgs    r+   Ú_skills_cfgrÎ     s)   € ä!Ó#×'Ñ'¨Ó1€JÜ# J´Ô5ˆ:Ð?¸4Ð?r9   rv   c                 ó@   — t        «       }|�|j                  | «      S dS )zP``skills.<key>`` from config.yaml, or None when the section is absent/malformed.N)rÎ   r€   )rv   rÍ   s     r+   Ú_skills_cfg_getrÐ     s"   € ä“€JØ",Ð"8ˆ:�>‰>˜#ÓÐB¸dÐBr9   Úentryc                 óŒ   — t        t        j                  j                  t        j                  j	                  | «      «      «      S )z3Expand ``~`` and ``${VAR}`` in a config path entry.)r   r�   r4   Ú
expanduserÚ
expandvars)rÑ   s    r+   Ú_expand_pathrÕ     s+   € ä”—‘×"Ñ"¤2§7¡7×#5Ñ#5°eÓ#<Ó=Ó>Ð>r9   Úpc                 óF   — ddl m} | j                  «       r| S  |«       | z  S )zJAnchor a relative config path at HERMES_HOME; absolute paths pass through.r   ©Úget_hermes_home)r™   rÙ   rT   )rÖ   rÙ   s     r+   Ú_home_relativerÚ   !  s    € å0Ø—‘”ˆ1Ð:¡_Ó%6¸Ñ%:Ð:r9   zhermes-agentÚESSENTIAL_SKILLSr‚   c                 ó6  — t        «       }|€
t        «       S ddlm} | xs t	        j
                  d«      xs  |d«      }t        |j                  d«      «      }|r$|j                  d«      xs i j                  |«      nd}|�|t        |«      z  }|t        z
  S )u”   Disabled skill names from config.yaml: global list âˆª platform list
    (*platform* defaults to ``HERMES_PLATFORM`` / ``HERMES_SESSION_PLATFORM``).Nr   )Úget_session_envÚHERMES_PLATFORMÚHERMES_SESSION_PLATFORMÚdisabledÚplatform_disabled)	rÎ   ÚsetÚgateway.session_contextrÝ   r�   r‘   Ú_normalize_string_setr€   rÛ   )r‚   rÍ   rÝ   Úresolved_platformrà   rá   s         r+   Úget_disabled_skill_namesræ   ,  s—   € ô “€JØÐÜ‹uˆÝ7Ø Òn¤B§I¡IÐ.?Ó$@ÒnÁOÐTmÓDnÐÜ$ Z§^¡^°JÓ%?Ó@€HÙ^o˜Ÿ™Ð(;Ó<ÒBÀ×GÑGÐHYÔZÐuyÐØÐ$ØÔ)Ð*;Ó<Ñ<ˆØÔ&Ñ&Ð&r9   c                 ó¸  — t        | t        «      ro| j                  «       j                  d«      rM	 t	        j
                  | j                  «       «      }t        |t        «      r|D �cg c]  }t        |«      ‘Œ c}S | gS t        | t        t        t        t        f«      r| D �cg c]  }t        |«      ‘Œ c}S g S # t        t        f$ r d}Y Œzw xY wc c}w c c}w )a  Normalize a config value that may hold a JSON-array string into a list.
    ``hermes config set`` stores lists as quoted JSON/Python-literal strings;
    treating one as a single name would silently filter nothing. A scalar
    string still means one name.

    See #13026, #86661.
    ú[N)rS   rH   r(   rh   ÚastÚliteral_evalr2   ÚSyntaxErrorr}   Útuplerâ   Ú	frozenset)rw   rt   Úitems      r+   Úparse_config_string_listrï   ;  sµ   € ô �%œÔØ�;‰;‹=×#Ñ# CÔ(ðÜ×)Ñ)¨%¯+©+«-Ó8�ô ˜&¤$Ô'Ø.4Ö5 dœ˜D�	Ò5Ð5ØˆwˆÜ,6°u¼tÄUÌCÔQZÐ>[Ô,\ %Ö(˜$ŒC��IÒ(ÐdÐbdÐdøô ¤Ð,ò Ø’ðüò 6ùâ(s   ±#B; Á(CÂ$CÂ;CÃCc                 ó|   — t        | «      D �ch c]#  }|j                  «       sŒ|j                  «       ’Œ% c}S c c}w rC   )rï   r(   )Úvaluesr·   s     r+   rä   rä   O  s)   € Ü%=¸fÓ%EÖV˜TÈÏÉÍˆD�J‰J�LÒVÐVùÒVs   Ž9¤9Ú_EXTERNAL_DIRS_CACHEc                  ó@   — t         j                  «        t        «        y)u(   Test hook â€” drop the in-process cache.N)rò   r¼   r½   r.   r9   r+   Ú_external_dirs_cache_clearrô   X  s   € ä×ÑÔ ÜÕr9   c                 óŠ   — t        | t        «      r| g} t        | t        «      sg S d„ | D «       D �cg c]  }|sŒ|‘Œ	 c}S c c}w )zFA scalar-or-list config entry as a list of stripped non-empty strings.c              3   óN   K  — | ]  }t        |«      j                  «       –— Œ y ­wrC   )rH   r(   )rE   rÑ   s     r+   rG   z#_config_str_list.<locals>.<genexpr>d  s   è ø€ Ò<¨uœ˜E›
×(Ñ(×*Ñ<ùs   ‚#%)rS   rH   r}   )ÚrawrÉ   s     r+   Ú_config_str_listrø   ^  s=   € ä�#”sÔØˆeˆÜ�cœ4Ô Øˆ	Ù<¸Ô<ÖB�!ÂŠAÒBÐBùÒBs
   ±A ¹A c                  ó"  — t        «       } | j                  «       sg S t        | «      }|}|�t        j	                  |«      nd}|�t        |«      S t        «       }|€g S t        «       j                  «       }g }t        |j	                  d«      «      D ]f  }t        t        |«      «      j                  «       }||k(  s||v rŒ/|j                  «       r|j                  |«       ŒQt        j                  d|«       Œh |�t        |«      t        |<   |S )z«Validated, deduplicated ``skills.external_dirs`` (existing dirs only). Entries
    are ``~``/``${VAR}`` expanded, relative to HERMES_HOME; the local skills dir is skipped.NÚexternal_dirsz0External skills dir does not exist, skipping: %s)r   r&   rÃ   rò   r€   r}   rÎ   r   r/   rø   rÚ   rÕ   Úis_dirÚappendrÅ   rÆ   )	r¾   Úfull_keyrÇ   rÈ   rÍ   Úlocal_skillsr¥   rÑ   rÖ   s	            r+   Úget_external_skills_dirsrÿ   g  s  € ô "Ó#€KØ×ÑÔØˆ	Ü  Ó-€HØ€IØ4=Ð4IÔ!×%Ñ% iÔ0Èt€FØÐÜ�F‹|ÐÜ“€JØÐØˆ	Ü!Ó#×+Ñ+Ó-€LØ€FÜ! *§.¡.°Ó"AÓBò PˆÜœ<¨Ó.Ó/×7Ñ7Ó9ˆØ�Ò  V¡ØØ�8‰8Œ:Ø�M‰M˜!Õä�L‰LÐKÈQÕOðPð ÐÜ*.¨v«,Ô˜YÑ'Ø€Mr9   c                  ód  — t        d«      } | r9t        | t        t        j                  f«      rt        | «      j                  «       nd}|syt        t        |«      «      }	 |j                  «       }	 |t        «       j                  «       k(  ry	 |S # t        $ r |}Y Œ-w xY w# t        $ r Y |S w xY w)zžConfigured ``skills.create_dir`` (need not exist yet), or None when unset;
    relative to HERMES_HOME; a value equal to the local skills dir counts as unset.Ú
create_dirÚ N)rÐ   rS   rH   r�   ÚPathLiker(   rÚ   rÕ   r/   r)   r   )r÷   rÑ   rÖ   Úresolveds       r+   Úget_skill_create_dirr  „  s°   € ô ˜,Ó
'€CÙ #¬
°3¼¼b¿k¹kÐ8JÔ(KŒC�‹H�N‰NÔÐQS€EÙØÜ”| EÓ*Ó+€AðØ—9‘9“;ˆðØ”~Ó'×/Ñ/Ó1Ò1Øð 2ð €Oøô ò ØŠðûô
 ò ØØ€Oðús$   Á!B Á2B" ÂBÂBÂ"	B/Â.B/c                  ó  — ddl m}  t        «       }|€
 | «       › d�S |j                  t	        j
                  «       «      r7d|j                  t	        j
                  «       «      j                  «       z   dz   S |j                  «       dz   S )z–User-facing path where new skills are created (``~/`` shorthand when
    possible); tool schema descriptions and prompts follow ``skills.create_dir``.r   )Údisplay_hermes_homez/skills/ú~/ú/)r™   r  r  Úis_relative_tor   Úhomer0   Úas_posix)r  r  s     r+   Údisplay_skill_create_dirr  ˜  sx   € õ 5Ü%Ó'€JØÐÙ%Ó'Ð(¨Ð1Ð1Ø× Ñ ¤§¡£Ô-Ø�j×,Ñ,¬T¯Y©Y«[Ó9×BÑBÓDÑDÀsÑJÐJØ×ÑÓ  3Ñ&Ð&r9   c                  ó¼   ‡— t        «       gŠt        «       } | �!| j                  «       r‰j                  | «       ‰j	                  ˆfd„t        «       D «       «       ‰S )zªSkill dirs: local ``~/.hermes/skills/`` first, then create_dir, then external.
    Trusted project dirs are NOT included (higher precedence; see get_project_skills_dirs).c              3   ó,   •K  — | ]  }|‰vsŒ|–— Œ y ­wrC   r.   )rE   ÚdÚdirss     €r+   rG   z&get_all_skills_dirs.<locals>.<genexpr>«  s   øè ø€ ÒG�a¸À$º”ÑGùs   ƒ	�)r   r  rû   rü   Úextendrÿ   )r  r  s    @r+   Úget_all_skills_dirsr  ¤  sP   ø€ ô ÓÐ€DÜ%Ó'€JØÐ *×"3Ñ"3Ô"5Ø�‰�JÔØ‡K�KÓGÔ3Ó5ÔGÔGØ€Kr9   z.hermesrÌ   z.agentsé@   rk   c                 ó|  — 	 | €ddl m}  |«       } t        | «      j                  «       }t        j
                  «       j                  «       }	 t        t        «      D ]?  }|dz  j                  «       r||k(  rdc S |c S |j                  |k(  r y|j                  }ŒA 	 y# t        $ r Y yw xY w# t        $ r Y yw xY w)uí  Nearest ancestor containing ``.git`` (dir or worktree file), or None.
    Without *start*, the surface's effective working directory wins over the process cwd â€” the same
    ladder every other cwd consumer reads (``resolve_agent_cwd``: session-bound cwd, then the scope's
    ``TERMINAL_CWD``, then the process cwd). The session cwd comes first because a multi-session host
    (TUI/desktop gateway) pins each session's workspace there while its terminal scope resolves a
    placeholder ``terminal.cwd`` to ``$HOME``; reading only the scope made every project skill invisible
    on those surfaces (#114359). ``TERMINAL_CWD`` is the per-surface workdir the terminal tool and cron
    jobs use (a cron job sets it from its per-job ``workdir`` without chdir'ing the scheduler process),
    which lets non-interactive surfaces inherit a prior interactive trust decision by project identity â€”
    and a surface with no workdir in a trusted repo simply resolves no project and loads nothing (#48975).
    Nr   )Úresolve_agent_cwdr   )
Úagent.runtime_cwdr  r   r/   r)   r  ÚrangeÚ_PROJECT_ROOT_MAX_DEPTHr&   Úparent)rk   r  Úcurr  Ú_s        r+   Úfind_project_rootr  º  sÃ   € ðØˆ=Ý;Ù%Ó'ˆEÜ�5‹k×!Ñ!Ó#ˆô �9‰9‹;×ÑÓ €Dð
ÜÔ.Ó/ò 	ˆAØ�f‘×$Ñ$Ô&ð  # dš{�tÒ3°Ò3Ø�z‰z˜SÒ ÙØ—*‘*‰Cñ	ð øô ò Ùðûô ò ØØðús5   ‚(B  Á-B/ Á;B/ Á?B/ ÂB/ Â 	B,Â+B,Â/	B;Â:B;c                  ó¼   — t        «       } t        t        d«      «      D ]+  }	 | j                  t	        |«      j                  «       «       Œ- | S # t        $ r Y Œ;w xY w)zKResolved set of trusted project roots from ``skills.trusted_project_dirs``.Útrusted_project_dirs)râ   rø   rÐ   ÚaddrÕ   r/   r)   )r¥   rÑ   s     r+   Ú!_project_trusted_dirs_from_configr!  Ü  s]   € ä›€FÜ!¤/Ð2HÓ"IÓJò ˆð	Ø�J‰J”| EÓ*×2Ñ2Ó4Õ5ðð
 €Møô ò 	Ùð	ús   £(AÁ	AÁAc                 óh   — 	 t        | «      j                  «       t        «       v S # t        $ r Y yw xY w)z>True when *root* is listed in ``skills.trusted_project_dirs``.F)r   r/   r!  r)   r?   s    r+   Úis_project_root_trustedr#  ç  s5   € ðÜ�D‹z×!Ñ!Ó#Ô'HÓ'JÐJÐJøÜò Ùðús   ‚"% ¥	1°1c                 ó  ‡ — t        «       j                  «       }g }ˆ fd„t        D «       D ]E  }	 |j                  «       r2|j                  «       |k7  r|j	                  |j                  «       «       ŒG |S # t
        $ r Y ŒUw xY w)z�Existing skill dirs under *root*, excluding the profile's own skills dir
    (HERMES_HOME itself may live inside a git checkout).c              3   ó(   •K  — | ]	  }‰|z  –— Œ y ­wrC   r.   )rE   Úsubr@   s     €r+   rG   z1_candidate_project_skills_dirs.<locals>.<genexpr>ô  s   øè ø€ Ò> �˜•Ñ>ùs   ƒ)r   r/   ÚPROJECT_SKILLS_SUBDIRSrû   rü   r)   )r@   rþ   r  Úcands   `   r+   Ú_candidate_project_skills_dirsr)  ï  sy   ø€ ô "Ó#×+Ñ+Ó-€LØ€DÛ>Ô'=Ô>ò ˆð	Ø�{‰{Œ} §¡£°<Ò!?Ø—‘˜DŸL™L›NÔ+øðð €Køô ò 	Ùð	ús   ¯AA5Á5	BÂ BÚtrustedc                 óZ   — t        d«      du ryt        «       }|�t        |«      | k(  r|S dS )zIcwd's project root when discovery is on and its trust state == *trusted*.Úproject_discoveryFN)rÐ   r  r#  )r*  r@   s     r+   Ú_current_project_rootr-  ý  s9   € äÐ*Ó+¨uÑ4ØÜÓ€DØÐ#Ô(?ÀÓ(EÈÒ(Pˆ4ÐZÐVZÐZr9   c                  ó8   — t        d¬«      } | �t        | «      S g S )zDTrusted project-local skill dirs for the current cwd (may be empty).T©r*  )r-  r)  r?   s    r+   Úget_project_skills_dirsr0    s"   € ä ¨Ô.€DØ37Ð3CÔ)¨$Ó/ÐKÈÐKr9   c            	      ó´   — t        d¬«      } d}| �t        | «      ndD ]"  }	 |t        d„ t        |d«      D «       «      z  }Œ$ |r| |fS dS # t        $ r Y Œ8w xY w)zP(root, skill_count) when cwd's project has skills but is NOT trusted, else None.Fr/  r   Nr.   c              3   ó    K  — | ]  }d –— Œ y­w)r<   Nr.   )rE   r  s     r+   rG   z4get_untrusted_project_skills_root.<locals>.<genexpr>  s   è ø€ ÒJ˜qœÑJùs   ‚rN   )r-  r)  ÚsumÚiter_skill_index_filesr)   )r@   Úcountr  s      r+   Ú!get_untrusted_project_skills_rootr6    sv   € ä ¨Ô/€DØ€EØ59Ð5EÔ+¨DÔ1È2ò ˆð	Ø”SÑJÔ$:¸1¸jÓ$IÔJÓJÑJ‰Eðñ
 "ˆD�%ˆ=Ð+ tÐ+øô ò 	Ùð	ús   ¢AÁ	AÁAzproject-localÚ_PROJECT_QUARANTINE_CACHEc                 óÚ  — t        | «      j                  }	 t        |j                  «       «      }|t
        v r	t
        |   S 	 ddlm} ddlm	}  |«       dz  dz  } ||t        |¬«      \  }}|j                  dk(  }|r!t        j                  d||j                  «       |t
        |<   |S # t        $ r t        |«      }Y Œ�w xY w# t        $ r t        j                  d	|d
¬«       d
}Y ŒJw xY w)uÍ   True when a project skill's scan verdict is ``dangerous``. Fail-closed: a
    scanner crash or missing scanner quarantines the skill. Scans
    unconditionally â€” non-project callers should not call this.r   )Úscan_skill_cachedrØ   ÚcacheÚproject_skill_scans)ÚsourceÚ	cache_dirÚ	dangerousu8   Project skill quarantined (verdict=dangerous): %s â€” %su<   Project skill scan failed â€” quarantining (fail closed): %sT)Úexc_info)r   r  rH   r/   r)   r7  Útools.skills_guardr9  r™   rÙ   Ú_PROJECT_SCAN_SOURCEÚverdictrÅ   ÚwarningÚsummaryrn   )	Úskill_mdÚ	skill_dirrv   r9  rÙ   r=  r¥   Ú_provÚquarantineds	            r+   Úis_quarantined_project_skillrI  +  sô   € ô �X“×%Ñ%€IðÜ�)×#Ñ#Ó%Ó&ˆð Ô'Ñ'Ü(¨Ñ-Ð-ð
Ý8Ý4Ù#Ó%¨Ñ/Ð2GÑGˆ	Ù)¨)Ô<PÐ\eÔf‰ˆ�Ø—n‘n¨Ñ3ˆÙÜ�N‰NÐUÐW`Ðbh×bpÑbpÔqð &1Ô˜cÑ"ØÐøô! ò Ü�)‹nŠðûô ò Ü�‰ÐUÐW`ÐkoˆÔpØŠðús$   —B* ÁAC Â*CÃ CÃ#C*Ã)C*Úproject_dirc              #   óF   K  — d„ t        | d«      D «       E d{  –—†  y7 Œ­w)uÌ   Yield non-quarantined SKILL.md files under a trusted project dir â€” the
    single iteration chokepoint for the project tier, so the quarantine cannot
    be bypassed by a call site forgetting the check.c              3   ó8   K  — | ]  }t        |«      rŒ|–— Œ y ­wrC   )rI  )rE   rÖ   s     r+   rG   z+iter_project_skill_files.<locals>.<genexpr>I  s   è ø€ Òr�aÔRnÐopÕRq”Ñrùs   ‚“rN   N)r4  )rJ  s    r+   Úiter_project_skill_filesrM  E  s   è ø€ ñ sÔ1°+¸zÓJÔr×rÒrús   ‚!™š!Ú
identifierc                 óž  — | xs dj                  «       }|s|S t        |«      j                  «       }|j                  «       s|j	                  d«      S 	 ddlm} |j                  «       }|g}t        t        fD ]  }	 |j                   |«       «       Œ |D ]/  }|j                  |«      sŒt        |j                  |«      «      c S  	 t        |j!                  «       j                  |j!                  «       «      «      S # t        $ r t        «       }Y Œ«w xY w# t        $ r Y Œ«w xY w# t        $ r t"        j%                  d|«       |cY S w xY w)z�Translate a trusted absolute skill path (slash commands / cron may store
    them) to the relative form ``skill_view()`` accepts.r  r	  r   )Úskills_toolu~   Skill identifier %r is an absolute path outside trusted skills roots â€” passing through unchanged (skill_view will reject it))r(   r   rÓ   rT   ÚlstripÚtoolsrP  Ú_skills_dirrn   r   r0  rÿ   r  r
  rH   r0   r/   rÅ   rÆ   )rN  Úraw_identifierÚidentifier_pathÚ_skills_toolÚprimary_rootÚtrusted_rootsÚgetterr@   s           r+   Únormalize_skill_lookup_namerZ  L  sS  € ð !Ò& B×-Ñ-Ó/€NÙØÐÜ˜>Ó*×5Ñ5Ó7€OØ×&Ñ&Ô(Ø×$Ñ$ SÓ)Ð)ð
(å5Ø#×/Ñ/Ó1ˆð "�N€MÜ*Ô,DÐEò ˆð	Ø× Ñ ¡£Õ*ðð ò :ˆØ×)Ñ)¨$Õ/Ü�×2Ñ2°4Ó8Ó9Ò9ð:ðÜ�?×*Ñ*Ó,×8Ñ8¸×9MÑ9MÓ9OÓPÓQÐQøô ò (Ü%Ó'Šð(ûô ò 	Ùð	ûô ò Ü�‰ð WØXfô	hàÒðús6   ÁD  Á=DÃ
5D( Ä DÄDÄ	D%Ä$D%Ä(!EÅEc                 óò   — t        | t        «      r| nt        t        | «      «      }	 |j                  «       j	                  «       S # t
        t        f$ r! |j                  «       j                  «       cY S w xY wrC   )rS   r   rH   rÓ   r/   r)   ÚRuntimeErrorÚabsolute)r4   rV   s     r+   Ú_resolve_for_skill_ownershipr^  s  sc   € Ü! $¬Ô-‰t´4¼¸D»	³?€Hð0Ø×"Ñ"Ó$×,Ñ,Ó.Ð.øÜ”\Ð"ò 0Ø×"Ñ"Ó$×-Ñ-Ó/Ò/ð0ús   ¨A Á-A6Á5A6c                 óº   ‡— t        | «      Št        t        «       «      }	 |j                  t	        «       «       t        ˆfd„|D «       «      S # t
        $ r Y Œw xY w)z×True when ``path`` lives under an external or trusted project skills dir.
    Those are externally owned: autonomous lifecycle maintenance treats them as
    read-only (user-directed tool calls may still edit them).c              3   óR   •K  — | ]  }‰j                  t        |«      «      –— Œ  y ­wrC   )r
  r^  )rE   r@   Ú	candidates     €r+   rG   z)is_external_skill_path.<locals>.<genexpr>…  s#   øè ø€ Ò^ÐPTˆy×'Ñ'Ô(DÀTÓ(J×KÑ^ùs   ƒ$')r^  r}   rÿ   r  r0  rn   rI   )r4   Úrootsra  s     @r+   Úis_external_skill_pathrc  {  sZ   ø€ ô -¨TÓ2€IÜÔ5Ó7Ó8€EðØ�‰Ô,Ó.Ô/ô Ó^ÐX]Ô^Ó^Ð^øô ò Ùðús   ¡A Á	AÁAc                 ó’   — | j                  d«      }t        |t        «      r|j                  d«      nd}t        |t        «      r|S i S )zG``metadata.hermes`` mapping from frontmatter, or ``{}`` when malformed.ÚmetadataÚhermesN)r€   rS   rm   )rs   re  rf  s      r+   Ú_hermes_metadatarg  ˆ  s=   € à�‰˜zÓ*€HÜ'1°(¼DÔ'AˆX�\‰\˜(Ô#Àt€FÜ ¬Ô-ˆ6Ð5°2Ð5r9   )Úfallback_for_toolsetsÚrequires_toolsetsÚfallback_for_toolsÚrequires_toolsÚsession_platformsc                 ól   — t        | «      }t        D �ci c]  }||j                  |g «      “Œ c}S c c}w )zPExtract conditional activation fields from parsed frontmatter (absent = ``[]``).)rg  Ú_CONDITION_KEYSr€   )rs   rf  rv   s      r+   Úextract_skill_conditionsro  ”  s0   € ä˜kÓ*€FÜ0?Ö@¨ˆC�—‘˜C Ó$Ñ$Ò@Ð@ùÒ@s   ”1c                 ór  — t        | «      j                  d«      }t        |t        «      r|g}|rt        |t        «      sg S i }|D ]×  }t        |t        «      sŒt        |j                  dd«      «      j                  «       }t        |j                  dd«      «      j                  «       }|r||v s|sŒo||dœ}|j                  d«      �|d   |d<   |j                  d«      }t        |t
        «      r |j                  «       r|j                  «       n||d<   |||<   ŒÙ t	        |j                  «       «      S )zµExtract ``metadata.hermes.config`` declarations (key/description/default/prompt).
    Entries missing ``key`` or ``description`` are skipped; ``prompt`` defaults to the description.Úconfigrv   r  Údescription)rv   rr  ÚdefaultÚprompt)rg  r€   rS   rm   r}   rH   r(   rñ   )rs   r÷   r¥   rî   rv   ÚdescrÑ   Úprompt_texts           r+   Úextract_skill_config_varsrw  š  s!  € ô ˜;Ó
'×
+Ñ
+¨HÓ
5€CÜ�#”tÔØˆeˆÙ”j ¤dÔ+Øˆ	Ø(*€FØò ˆÜ˜$¤Ô%ØÜ�$—(‘(˜5 "Ó%Ó&×,Ñ,Ó.ˆÜ�4—8‘8˜M¨2Ó.Ó/×5Ñ5Ó7ˆÙ�c˜V‘m©4ØØ(+¸DÑ AˆØ�8‰8�IÓÐ*Ø# I™ˆE�)ÑØ—h‘h˜xÓ(ˆÜ1;¸KÌÔ1MÐR]×RcÑRcÔRe˜+×+Ñ+Ô-Ðkoˆˆh‰ØˆˆsŠðô �—‘“Ó Ð r9   c                  óØ  — i } t        «       }t        «       D ]ª  }|j                  «       sŒt        |d«      D ]ˆ  }	 t	        |j                  d¬«      «      \  }}t        |j                  d«      xs |j                  j                  «      }||v st        |«      sŒdt        |«      D ]  }|d   | vsŒ||d<   || |d   <   Œ ŒŠ Œ¬ t        | j                  «       «      S # t        $ r Y Œ±w xY w)z�Config var declarations across all enabled, platform-compatible skills,
    deduplicated by key; each dict carries a ``skill`` attribution key.rN   r!   r"   r·   rv   Úskill)ræ   r  rû   r4  rx   r'   rn   rH   r€   r  r·   r‰   rw  r}   rñ   )Úall_varsrà   r   Ú
skill_filers   r  Ú
skill_nameÚvars           r+   Údiscover_all_skill_config_varsr~  ³  s  € ð +-€HÜ'Ó)€HÜ)Ó+ò /ˆ
Ø× Ñ Ô"ØÜ0°¸ZÓHò 	/ˆJðÜ!2°:×3GÑ3GÐQXÐ3GÓ3YÓ!Z‘�˜Qô ˜[Ÿ_™_¨VÓ4ÒN¸
×8IÑ8I×8NÑ8NÓOˆJØ˜XÑ%Ô-CÀKÔ-PØÜ0°Ó=ò /�Ø�u‘: XÒ-Ø#-�C˜‘LØ+.�H˜S ™ZÒ(ñ/ñ	/ð/ô �—‘Ó!Ó"Ð"øô ò Ùðús   »CÃ	C)Ã(C)zskills.configrq  Ú
dotted_keyc                 ól   — | }|j                  d«      D ]  }t        |t        «      r||vr y||   }Œ |S )zGWalk a nested dict following a dotted key; None if any part is missing.ú.N)ro   rS   rm   )rq  r  ÚcurrentrF   s       r+   Ú_resolve_dotpathrƒ  Î  sE   € à€GØ× Ñ  Ó%ò  ˆÜ˜'¤4Ô(¨D¸Ñ,?ÙØ˜$‘-‰ð ð €Nr9   z\$(?:\{HOME\}|HOME)(?=$|[/\\])rw   c                 ó  ‡— t        «       Š‰r7| dk(  s| j                  d«      r‰| dd z   } t        j                  ˆfd„| «      } t        j
                  j                  t        j
                  j                  | «      «      S )al  Expand ``~`` / ``$HOME`` against the HOME Hermes injects into tool subprocesses.

    Skill config defaults describe paths the agent hands to tools, so in a container where the
    control process HOME (``/opt/data``) differs from the tool HOME (``{HERMES_HOME}/home``) a
    plain ``expanduser`` pointed the prompt at a path no tool would ever read (#12260).
    Ú~)r  z~\r<   Nc                 ó   •— ‰S rC   r.   )Ú_mÚsubprocess_homes    €r+   r�   z+_expand_skill_config_path.<locals>.<lambda>è  s   ø€ ¨O€ r9   )r   rh   Ú_HOME_VAR_REr&  r�   r4   rÓ   rÔ   )rw   rˆ  s    @r+   Ú_expand_skill_config_pathrŠ  Û  sm   ø€ ô *Ó+€OÙØ�CŠ<˜5×+Ñ+¨MÔ:Ø# e¨A¨B iÑ/ˆEô × Ñ Ó!;¸UÓCˆÜ�7‰7×ÑœbŸg™g×0Ñ0°Ó7Ó8Ð8r9   Úconfig_varsc                 ó  — t        «       }i }| D ]y  }t        |t        › d|d   › �«      }|� t        |t        «      r"|j                  «       s|j                  dd«      }t        |t        «      rd|v sd|v rt        |«      }|||d   <   Œ{ |S )z¢Map logical skill config keys to current values (or declared defaults);
    path-like string values are ``~``/``$HOME``/``${VAR}`` expanded against the tool HOME.r�  rv   rs  r  r…  ú$)rÊ   rƒ  ÚSKILL_CONFIG_PREFIXrS   rH   r(   r€   rŠ  )r‹  rq  r  r}  rw   s        r+   Úresolve_skill_config_valuesr�  ì  s˜   € ô Ó€FØ!€HØò %ˆÜ  Ô,?Ð+@ÀÀ#ÀeÁ*ÀÐ)NÓOˆØˆ=œZ¨¬sÔ3¸E¿K¹K¼MØ—G‘G˜I rÓ*ˆEÜ�eœSÔ! s¨e¡|°s¸e±|Ü-¨eÓ4ˆEØ$ˆ��U‘Òð%ð €Or9   é<   c                 ó~   — | j                  dd«      }|r(t        |«      j                  «       j                  d«      S dS )z@Normalize a skill's description field for comparison/truncation.rr  r  z'")r€   rH   r(   )rs   Úraw_descs     r+   Ú_normalize_skill_descriptionr“  þ  s7   € à�‰˜}¨bÓ1€HÙ19Œ3ˆx‹=×ÑÓ ×&Ñ& uÓ-ÐA¸rÐAr9   c                 ó^   — t        | «      }t        |«      t        kD  r|dt        dz
   dz   S |S )zCExtract a system-prompt-length description from parsed frontmatter.Nrd   z...)r“  r=   ÚSKILL_PROMPT_DESC_LIMIT)rs   ru  s     r+   Úextract_skill_descriptionr–    s7   € ä'¨Ó4€DÜ9<¸T»ÔE\Ò9\ˆ4Ð,Ô(¨1Ñ,Ð-°Ñ5ÐfÐbfÐfr9   c                 ó8   — t        t        | «      «      t        kD  S )zMTrue when the description will be truncated in the system prompt skill index.)r=   r“  r•  rˆ   s    r+   Ú)is_skill_description_truncated_for_promptr˜  
  s   € äÔ+¨KÓ8Ó9Ô<SÑSÐSr9   Úfilenamec              #   ób  K  — t        | «      }t        | «      }t        j                  j	                  |t
        «      }g }t        j                  |d¬«      D ]¨  \  }}}d|v }	||k(  r t
        |v r|€|j                  t
        «       n||k(  r|D �
cg c]
  }
|
|k(  sŒ	|
‘Œ c}
|dd |D �
cg c]  }
|
t        vsŒ|	r	|
t        v rŒ|
‘Œ c}
|dd ||v sŒz|j                  t        j                  j	                  ||«      «       Œª t        t        t        |«      «      E d{  –—†  yc c}
w c c}
w 7 Œ­w)a  Walk skills_dir yielding sorted paths matching *filename*; prunes
    EXCLUDED_SKILL_DIRS and support dirs of skill roots. Org mirrors are
    TOKEN-GATED: only the active org's subdir is walked, so leaving an org
    stops its skills resolving without manual cleanup.T)ÚfollowlinksrN   N)rH   r,   r�   r4   Újoinr$   ÚwalkÚremoverD   rO   rü   Úmapr   Úsorted)r   r™  Úskills_dir_strÚ
active_orgÚorg_rootÚmatchesr@   r  ÚfilesÚhas_skill_mdr  s              r+   r4  r4    s  è ø€ ô
 ˜“_€NÜ# JÓ/€JÜ�w‰w�|‰|˜NÔ,?Ó@€HØ€GÜŸW™W ^ÀÔFò 9Ñˆˆd�EØ! UÐ*ˆØ�>Ò!Ô&9¸TÑ&AÀjÐFXØ�K‰KÔ+Õ,Ø�XÒØ"&Ö:˜Q¨!¨z«/’qÒ:ˆD‘ˆGØ"Öv˜ aÔ/BÒ&BÉLÐ]^ÔbtÒ]t’1Òvˆ‰QˆØ�uÒØ�N‰Nœ2Ÿ7™7Ÿ<™<¨¨hÓ7Õ8ð9ô ”4œ ›Ó)×)Ñ)ùò	 ;ùÚvð *úsC   ‚BD/Â
D#ÂD#Â
D/Â(D(Â6
D(ÃD(Ã
D/ÃAD/ÄD-ÄD/z^[a-zA-Z0-9_-]+$r·   c                 ó@   — | j                  d«      \  }}}|r||fS d| fS )zcSplit ``'namespace:skill-name'`` into ``(namespace, bare_name)``; ``(None, name)`` without ``':'``.rf   N)Ú	partition)r·   Ú	namespaceÚsepÚbares       r+   Úparse_qualified_namer¬  (  s-   € àŸ>™>¨#Ó.Ñ€Iˆs�DÙ #ˆI�tÐÐ5¨$°¨Ð5r9   ra  c                 óX   — t        | «      xr t        t        j                  | «      «      S )zDCheck whether *candidate* is a valid namespace (``[a-zA-Z0-9_-]+``).)r7   Ú_NAMESPACE_REÚmatch)ra  s    r+   Úis_valid_namespacer°  .  s!   € ä�	‹?ÒCœt¤M×$7Ñ$7¸	Ó$BÓCÐCr9   c                  ó^   — t        t        «       «      } | j                  t        «       «       | S )uÃ   All skill dirs in precedence order: project â†’ local â†’ external.

    First-wins name deduplication over this order gives project skills
    priority over profile-local and external ones.
    )r}   r0  r  r  )r  s    r+   Úget_scan_ordered_skills_dirsr²  8  s'   € ô Ô'Ó)Ó*€DØ‡K�KÔ#Ó%Ô&Ø€Kr9   )r   NrC   )pÚ__doc__ré   Úloggingr�   ri   r�   Úpathlibr   r   Útypingr   r   r   r   r	   r
   r   r™   r   r   r   r   Ú	getLoggerÚ__name__rÅ   r   rí   rD   rO   r$   r%   ÚORG_PROVENANCE_FILEÚORG_BASELINE_FILErH   r,   r5   r7   r8   r>   rK   rJ   r[   rb   rx   r†   r‰   rŠ   Ú__annotations__r•   rš   r¡   r¦   r¬   r¹   rº   Úintr½   rÃ   rÊ   rÎ   rÐ   rÕ   rÚ   rÛ   ræ   rï   rä   rò   rô   rø   rÿ   r  r  r  r4   rœ  r'  r  r  r!  r#  r)  r-  r0  r6  rA  r7  rI  rM  rZ  r^  rc  rg  rn  ro  rw  r~  rŽ  rƒ  Úcompiler‰  rŠ  r�  r•  r“  r–  r˜  r4  r®  r¬  r°  r²  r.   r9   r+   ú<module>r¾     s×  ðòQó Û Û 	Û 	Û 
ß "ß B× BÑ B÷ó ð 
ˆ×	Ñ	˜8Ó	$€à!¨GÀÑH€áð !ó Ð ñ ÐOÓPÐ ð
 Ð Ø!Ð Ø,Ð Ø(Ð ð 4ð ¨H°S©Mó ðF Tð F¨e°C¸°H©oó Fð2¨ð 2°$ó 2ð
1 Tð 1¨h°s©mó 1ð <@ò h¨(°4©.ð hÀDó hð ;?ò ¨°$©ð À4ó ð €ð"�só "ð˜sð  u¨T°#°s°(©^¸SÐ-@Ñ'Aó ð.¨3ð °4ó ðE¨¨S°#¨X©ð E¸4ó Eð &(Ð �4˜˜T˜	‘?Ó 'ð˜ó ð&˜ó ð ¨Ù
Xñ1€��S˜( 2 t 8Ñ,Ð,Ñ-ó ð
˜Sð 
 Tó 
ð@¨4°°S°©>ð @¸dó @ð D¨¨c¨¡Nð °tó ð, KMÐ �4˜˜c 3¨¨S°#Ð5Ñ6¸¸SÀ#¸X¹ÐFÑGÓ Lóð
 4ð ¨H°U¸3ÀÀSÈ#ÈsÐ;RÑ5SÑ,Tó ð˜$˜s C˜x™.ó ð,@�X˜d 3¨ 8™nÑ-ó @ðC˜ð C ó Cð?˜ð ? ó ?ð
;�dð ;˜tó ;ñ (¨Ð(8Ó9Ð �)Ó 9ñ' s¨T¡zð '¸SÀ¹Xó 'ðe t¨C¡yó eð(W S¨¡Xó Wð JLÐ �d˜5  c¨3°°SÐ!8Ñ9¸4À¹:ÐEÑFÓ KóðC˜T #™Yó Cð $ t¡*ó ð:˜h t™nó ð(	' #ó 	'ð˜T $™Zó ð" Ÿ'™'Ÿ,™, y°(Ó;¸R¿W¹W¿\¹\È)ÐU]Ó=^Ð_Ð àÐ ñ˜X d™^ð °xÀ±~ó ðD¨3¨t©9ó ð $ð ¨4ó ð¨ð °$°t±*ó ð[ 4ð [¨H°T©Nó [ðL  d¡ó Lð	,¨8°E¸$À¸)Ñ4DÑ+Eó 	,ð8 'Ð Ø-/Ð ˜4  T 	™?Ó /ð¨dó ð4s¨$ó sð$¨Cð $°Có $ðN0¨$ó 0ð
_ Dó 
_ð6 $ s¨C x¡.ð 6°T¸#¸s¸(±^ó 6ð ~€ðA¨$¨s°C¨x©.ð A¸TÀ#ÀtÀ)¹_ó Að!¨4°°S°©>ð !¸dÀ4ÈÈSÈÁ>Ñ>Ró !ð2#¨¨T°#°s°(©^Ñ(<ó #ð0 &Ð ð˜T # s (™^ð ¸ó ð ˆr�z‰zÐ;Ó<€ð9 Sð 9¨Só 9ð"¨T°$°s¸C°x±.Ñ-Að ÀdÈ3ÐPSÈ8Ánó ð Ð ðB¨d°3¸°8©nð BÀó Bðg¨4°°S°©>ð g¸có gðT¸4ÀÀSÀ¹>ð TÈdó Tð
* tð *°só *ð, �—
‘
Ð.Ó/€ð6˜sð 6 u¨X°c©]¸CÐ-?Ñ'@ó 6ðD (¨3¡-ð D°Dó Dð d¨4¡jô r9   