Ë
    áß²jÏ‡  ã                   ó  — 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 ddlm	Z	m
Z
mZ ddlmZ ddlmZ ddlmZmZ  ej(                  e«      Zi ae
ee
ee	f   f   ed<   daee   ed	<   daee   ed
<   daee   ed<    ej:                  «       Z ej>                  d«      Z  ej>                  d«      Z!dZ"dZ#dZ$dZ%dZ&dZ'dZ( ej>                   ejR                  e"«      dz   «      Z*e"dz   Z+dZ,dedefd„Z-de.dedefd„Z/de	dee   fd„Z0dnde	d edee   fd!„Z1d"ed#ed$edee   fd%„Z2dee   fd&„Z3defd'„Z4dee   fd(„Z5dod)ed*edz  de6e7ee	f   edz  ef   dz  fd+„Z8d,e7ee	f   de.e   ddfd-„Z9d.Z:d/Z;d,e7ee	f   dee   fd0„Z<d,e7ee	f   d1edz  de.e   fd2„Z=	 	 	 dpd,e7ee	f   d1edz  d4ed5ed6ed7edz  defd8„Z>d9e6e7ee	f   edz  ef   d4ed*edz  d:edef
d;„Z?dddd3d3d<œd=ed>e.e   d?e.e   dz  d@e.e   dz  dAe.e   dz  dBed5edefdC„Z@h dD£ZAdedee   fdE„ZBdFedAeCdGeCdHe
ee
ee	f   f   ddf
dI„ZDde
ee
ee	f   f   fdJ„ZEde
ee
ee	f   f   fdK„ZFdLe
eef   dMe
eef   de
ee	f   fdN„ZGdOe
ee
ee	f   f   de
eef   fdP„ZHde
ee	f   fdQ„ZIdRedee   fdS„ZJdRedTe
ee	f   dee   fdU„ZK	 dqdVed5ed*edz  d6edee   f
dW„ZLdXZMdYede6e.e   ef   fdZ„ZN	 drd[e.e   d5ed*edz  dee6ee.e   e.e   f      fd\„ZOdod]edz  deCfd^„ZPd_„ dd`ddaœdbe.e   d*edz  dceCdz  ddeQdeeCdz  de6e.e   e.e   e.e   e.e   f   fdf„ZR	 dsdge.e   d*edz  dheCe   dz  de6ee.e   e.e   f   fdi„ZSdodje7dz  de.e   fdk„ZT	 dtd*edz  dje7dz  dledz  de6ee.e   e.e   f   fdm„ZUy)uzRShared slash command helpers for skills (CLI and gateway both invoke /skill-name).é    N)ÚPath)ÚAnyÚDictÚOptional)Údisplay_hermes_home)Úregister_stable_prefix)Úload_skills_configÚpreprocess_skill_contentÚ_skill_commandsÚ_skill_commands_platformÚ_skill_commands_homeÚ_skill_commands_projectz[^\w-]z-{2,}ú%[IMPORTANT: The user has invoked the z(The full skill content is loaded below.]zPThe user has provided the following instruction alongside the skill invocation: z

[Runtime note:z skill bundle,z
User instruction: z

[Loaded as part of the z	"([^"]*)"ú%úÚnameÚreturnc                 óÔ   — t         j                  d| j                  «       j                  dd«      j                  dd«      «      }t        j                  d|«      j                  d«      S )z¢Normalize a skill/bundle name to a ``/command`` slug (``Foo Bar`` -> ``foo-bar``);
    strips chars (``+``, ``/``) that would make invalid Telegram command names.Ú ú ú-Ú_)Ú_SKILL_INVALID_CHARSÚsubÚlowerÚreplaceÚ_SKILL_MULTI_HYPHENÚstrip)r   Úcmds     ú9/home/ubuntu/.hermes/hermes-agent/agent/skill_commands.pyÚslugify_skill_namer!   9   sU   € ô ×
"Ñ
" 2 t§z¡z£|×';Ñ';¸CÀÓ'E×'MÑ'MÈcÐSVÓ'WÓ
X€CÜ×"Ñ" 3¨Ó,×2Ñ2°3Ó7Ð7ó    ÚpartsÚinstructionc                 ón   — dj                  | «      dz   t        z   }| j                  t        › |› �«       |S )u‡  Append the instruction line to ``parts``; return the stable prefix, which
    ends exactly at the instruction marker so (registered with
    ``agent.prompt_cache_boundary``) the cache planner can break on the scaffold.
    Single construction site guarantees the prefix is a byte-prefix of the message.

    Shared by every builder that ends a static skill scaffold with the caller-supplied volatile instruction
    (single-skill invocations, cron job prompts). Keeping construction in one place guarantees the
    registered prefix stays a byte-prefix of the built message â€” the invariant the request-time split
    depends on. See #81867.
    ú
)ÚjoinÚ_SINGLE_SKILL_INSTRUCTIONÚappend)r#   r$   Ústable_prefixs      r    Úappend_user_instructionr+   @   s:   € ð —I‘I˜eÓ$ tÑ+Ô.GÑG€MØ	‡L�LÔ-Ð.¨{¨mÐ<Ô=ØÐr"   Úcontentc                 óô   — t        | t        «      sy| j                  t        «      s| S t        | v r t        | t        t        | j                  «      S t        | v r t        | t        t        | j                  «      S y)zãRecover the user's instruction from a slash-skill-expanded turn: the
    string unchanged when it is NOT scaffolding, the extracted instruction when
    the scaffolding carried one, or ``None`` for a bare ``/skill`` invocation.N)Ú
isinstanceÚstrÚ
startswithÚ_SKILL_INVOCATION_PREFIXÚ_BUNDLE_MARKERÚ
_cut_afterÚ_BUNDLE_USER_INSTRUCTIONÚ_BUNDLE_FIRST_SKILL_BLOCKÚfindÚ_SINGLE_SKILL_MARKERr(   Ú_RUNTIME_NOTEÚrfind)r,   s    r    Ú+extract_user_instruction_from_skill_messager:   P   sh   € ô �gœsÔ#ØØ×ÑÔ6Ô7ØˆÜ˜Ñ ä˜'Ô#;Ô=VÐX_×XdÑXdÓeÐeÜ˜wÑ&ä˜'Ô#<¼mÈWÏ]É]Ó[Ð[Ør"   Ú	separatorc                 ó¢  — t        | t        «      r| j                  t        «      syt        j                  | «      }|r|j                  d«      ndj                  «       }|j                  d«      r|nd|› �}t        | «      }|rG|| urCdj                  |j                  t        «      d   j                  «       «      }|r|r|› |› |› �S |S |r|S dS )u  Render a slash-skill-expanded turn the way the user typed it:
    ``"/work â€” fix the title leak"``, ``"/work"`` for a bare invocation, or
    ``None`` when *content* is not scaffolding. ``separator=" "`` gives the
    literal invocation as typed (chat transcripts).Né   r   ú/r   r   )r.   r/   r0   r1   Ú_SKILL_NAME_REÚmatchÚgroupr   r:   r'   ÚsplitÚSKILL_EXCERPT_JOINT)r,   r;   r@   r   Úlabelr$   s         r    Údescribe_skill_invocationrE   a   sÉ   € ô
 �gœsÔ#¨7×+=Ñ+=Ô>VÔ+WØÜ× Ñ  Ó)€EÙ#ˆE�K‰K˜ŒN¨×2Ñ2Ó4€Dà—O‘O CÔ(‰D°°$°¨j€EÜ=¸gÓF€KÙ�{¨'Ñ1ð —h‘h˜{×0Ñ0Ô1DÓEÀaÑH×NÑNÓPÓQˆÙÙ9=�e�W˜Y˜K¨ }Ð5ÐNÀ;ÐNÙˆ5Ð"˜dÐ"r"   ÚmessageÚmarkerÚstop_markerc                 óŠ   —  ||«      }|dk  ry| |t        |«      z   d j                  |d«      d   j                  «       xs dS )z`Text between *marker* (located with ``find``) and *stop_marker*, stripped; None if absent/empty.r   Nr=   )ÚlenrB   r   )rF   rG   rH   r6   Ú
marker_idxs        r    r3   r3   v   sM   € á�f“€JØ�A‚~ØØ�:¤ F£Ñ+Ð,Ð-×3Ñ3°KÀÓCÀAÑF×LÑLÓNÒVÐRVÐVr"   c                  ó¢   — 	 ddl m}  t        j                  d«      xs  | d«      }|xs dS # t        $ r t        j                  d«      }Y Œ&w xY w)aŽ  Current platform scope for disabled-skill filtering, or None (CLI, RL,
    scripts). A change invalidates the scan cache so each platform sees its
    own ``skills.platform_disabled`` view.

    Used to detect when the active platform has shifted so :func:`get_skill_commands` can drop a stale cache
    that was populated for a different platform's ``skills.platform_disabled`` view (#14536).
    r   )Úget_session_envÚHERMES_PLATFORMÚHERMES_SESSION_PLATFORMN)Úgateway.session_contextrM   ÚosÚgetenvÚ	Exception)rM   Úresolved_platforms     r    Ú _resolve_skill_commands_platformrU   ~   sS   € ð9Ý;ÜŸI™IÐ&7Ó8Òf¹OÐLeÓ<fÐð Ò$ Ð$øô ò 9ÜŸI™IÐ&7Ó8Òð9ús   ‚%- ­AÁAc                  ó.   — ddl m}  t         | «       «      S )aJ  Effective Hermes home the scan is scoped to (profiles carry their own
    ``skills.external_dirs``, so a profile switch must invalidate the cache).

    A gateway session can switch between profiles that each carry their own ``skills.external_dirs`` (via
    ``set_hermes_home_override``), but the module-level scan only tracked
    ``_resolve_skill_commands_platform()``. Switching profiles without a platform change left the previous
    profile's skill list cached, so ``get_skill_commands()`` reported a cache miss for skills that only
    exist under the new profile (#88023).
    r   ©Úget_hermes_home)Úhermes_constantsrX   r/   rW   s    r    Ú_resolve_skill_commands_homerZ   Ž   s   € õ 1Ü‰Ó Ó!Ð!r"   c                  ó:   — ddl m}   | «       }|�t        |«      S dS )a  The project root the scan's project skills resolve from (None outside a repo). One multi-session
    host serves sessions in different repos; without this tag the first session's project skills stayed
    published for every other session's ``get_skill_commands`` lookup (#114359).r   )Úfind_project_rootN)Úagent.skill_utilsr\   r/   )r\   Úroots     r    Ú_resolve_skill_commands_projectr_   œ   s#   € õ 4ÙÓ€DØÐ(Œ3ˆt‹9Ð2¨dÐ2r"   Úskill_identifierÚtask_idc                 ó   — | xs dj                  «       }|sy	 ddlm}m} ddlm}  ||«      }t        j                   |||d¬«      «      }|j                  d«      syt        |j                  d	«      xs d«      }d}	|j                  d
«      rt        |d
   «      }	n |r	  |«       t        |«      j                  z  }	||	t        |j                  d«      xs |«      fS # t        $ r Y yw xY w# t        $ r d}	Y Œ=w xY w)zOLoad a skill by name/path and return (loaded_payload, skill_dir, display_name).r   Nr   )Ú_skills_dirÚ
skill_view)Únormalize_skill_lookup_nameF)ra   Ú
preprocessÚsuccessÚpathÚ	skill_dirr   )r   Útools.skills_toolrc   rd   r]   re   ÚjsonÚloadsrS   Úgetr/   r   Úparent)
r`   ra   Úraw_identifierrc   rd   re   Ú
normalizedÚloaded_skillÚ
skill_pathri   s
             r    Ú_load_skill_payloadrs   ¥   s  € à&Ò,¨"×3Ñ3Ó5€NÙØðß=ÝAÙ0°Ó@ˆ
Ü—z‘z¡*¨ZÀÐUZÔ"[Ó\ˆð ×Ñ˜IÔ&ØÜ�\×%Ñ% fÓ-Ò3°Ó4€JØ€Ið ×Ñ˜Ô$Ü˜ kÑ2Ó3‰	Ù	ð	Ù#›¬¨ZÓ(8×(?Ñ(?Ñ?ˆIð ˜¤C¨×(8Ñ(8¸Ó(@Ò(NÀJÓ$OÐOÐOøô ò Ùðûô ò 	ØŠIð	ús#   ™4C  Â"C/ Ã 	C,Ã+C,Ã/C=Ã<C=rq   c                 óœ  — 	 ddl m}m}m} t	        | j                  d«      xs | j                  d«      xs d«      } ||«      \  }} | ||«      «      }|sy|j                  d«       |j                  dt        «       › d�«       |j                  d	„ |j                  «       D «       «       |j                  d
«       y# t        $ r Y yw xY w)z¬Append a ``[Skill config: ...]`` block with resolved ``metadata.hermes.config``
    values so the agent needn't read config.yaml. Any failure leaves the message without it.r   )Úextract_skill_config_varsÚparse_frontmatterÚresolve_skill_config_valuesÚraw_contentr,   r   Nz[Skill config (from z/config.yaml):c              3   óL   K  — | ]  \  }}d |› d|rt        |«      nd› �–— Œ y­w)z  z = z	(not set)N)r/   )Ú.0ÚkeyÚvalues      r    ú	<genexpr>z'_inject_skill_config.<locals>.<genexpr>Í   s+   è ø€ ÒlÉZÈSÐRW�r˜#˜˜c±¤# e¤*¸;Ð!GÔHÑlùs   ‚"$ú])r]   ru   rv   rw   r/   rm   r)   r   ÚextendÚitemsrS   )	rq   r#   ru   rv   rw   rx   Úfrontmatterr   Úresolveds	            r    Ú_inject_skill_configrƒ   Á   s½   € ðßoÑoÜ˜,×*Ñ*¨=Ó9Ò^¸\×=MÑ=MÈiÓ=XÒ^Ð\^Ó_ˆÙ*¨;Ó7‰ˆ�QÙ.Ñ/HÈÓ/UÓVˆÙØØ�‰�RÔØ�‰Ð+Ô,?Ó,AÐ+BÀ.ÐQÔRØ�‰ÑlÐ[c×[iÑ[iÓ[kÔlÔlØ�‰�SÕøÜò Ùðús   ‚AB? ÁA%B? Â?	CÃ
Cz¯Resolve any relative paths in this skill (e.g. `scripts/foo.js`, `templates/config.yaml`) against that directory, then run them with the terminal tool using the absolute path.zwRequired environment setup was skipped. Continue loading the skill and explain any reduced functionality if it matters.c                 ó¦   — | j                  d«      rt        S | j                  d«      xs( | j                  d«      r| j                  d«      nd xs d S )NÚsetup_skippedÚgateway_setup_hintÚsetup_neededÚ
setup_note)rm   Ú_SETUP_SKIPPED_NOTE)rq   s    r    Ú_setup_noterŠ   Þ   sU   € Ø×Ñ˜Ô(Ü"Ð"Ø×ÑÐ0Ó1ò Ø*6×*:Ñ*:¸>Ô*Jˆ×Ñ˜Ô&ÐPTòà	ðr"   ri   c           
      óž  — | j                  d«      xs i j                  «       }|D ��cg c]  }t        |t        «      sŒ|D ]  }|‘Œ Œ }}}|ss|rqdD ]l  }t	        ||z  j                  d«      «      }||D �cg c]>  }|j                  «       sŒ|j                  «       rŒ%t        |j                  |«      «      ‘Œ@ c}z  }Œn |S c c}}w c c}w )zHSkill-relative support file paths: from ``linked_files`` or a disk walk.Úlinked_files)Ú
referencesÚ	templatesÚscriptsÚassetsÚ*)
rm   Úvaluesr.   ÚlistÚsortedÚrglobÚis_fileÚ
is_symlinkr/   Úrelative_to)	rq   ri   ÚlinkedÚentriesÚentryÚ
supportingÚsubdirÚfilesÚfs	            r    Ú_supporting_filesr    æ   sÄ   € à×Ñ˜~Ó.Ò4°"×<Ñ<Ó>€FØ'-×`˜G´¸GÄTÕ1JÐX_Ò`Èu’%Ð`�%Ð`€JÑ`Ù™)ØFò 	oˆFÜ˜I¨Ñ.×5Ñ5°cÓ:Ó;ˆEØÀ%Ön¸QÈ1Ï9É9Í;Ð_`×_kÑ_kÕ_mœ3˜qŸ}™}¨YÓ7Õ8ÒnÑn‰Jð	oð Ðùó aùò os   ©C¿CÁ8C
ÂC
ÂC
r   Úactivation_noteÚuser_instructionÚruntime_noteÚ
session_idc                 óü  — ddl m} t        t        | j	                  d«      xs d«      ||t        «       ¬«      }|d|j                  «       g}|r|dd|› d�t        gz  }t        | |«       t        | «      }	|	r|dd|	› d�gz  }t        | |«      }
|
rW|rU	 t        |j                   |«       «      «      }|dd	gz  }||
D �cg c]  }d
|› �‘Œ	 c}z  }|j                  d|› d|› d�«       d}|r|j                  d«       t        ||«      }|r|dd|› d�gz  }dj!                  |«      }|�3|j#                  |«      r"t%        |«      t%        |«      kD  rt'        |«       |S # t        $ r |j                  }Y ŒÂw xY wc c}w )z9Format a loaded skill into a user/system message payload.r   )rc   r,   r   )Ú
skills_cfgz[Skill directory: r~   z[Skill setup note: zP[This skill has supporting files (paths relative to the skill directory above):]z- z)
Load any of these with skill_view(name="zM", file_path="<path>"), or run scripts directly by absolute path (e.g. `node z/scripts/foo.js`).Nz[Runtime note: r&   )rj   rc   r
   r/   rm   Ú_load_skills_configr   Ú_SKILL_DIR_NOTErƒ   rŠ   r    r˜   Ú
ValueErrorr   r)   r+   r'   r0   rJ   r   )rq   ri   r¡   r¢   r£   r¤   rc   r,   r#   rˆ   rœ   Úskill_view_targetÚsfr*   rF   s                  r    Ú_build_skill_messager¬   ñ   s·  € õ .ä&ÜˆL×Ñ˜YÓ'Ò-¨2Ó.°	¸:ÔReÓRgô€Gð ˜b '§-¡-£/Ð2€EáØ�"Ð*¨9¨+°QÐ7¼ÐIÑIˆÜ˜ uÔ-Ü˜\Ó*€JÙØ�"Ð+¨J¨<°qÐ9Ð:Ñ:ˆÜ" <°Ó;€JÙ‘ið	/Ü # I×$9Ñ$9¹+»-Ó$HÓ IÐð 	�"ÐhÐiÑiˆØ jÖ1 �B�r�d’)Ò1Ñ1ˆØ�‰Ø8Ð9JÐ8Kð Là$˜+Ð%7ð9ô	
ð
 €MÙØ�‰�RÔô 0°Ð7GÓHˆÙØ�"˜¨ ~°QÐ7Ð8Ñ8ˆØ�i‰i˜Ó€GØÐ  W×%7Ñ%7¸Ô%FÌ3ÈwË<ÔZ]Ð^kÓZlÒKlÜ˜}Ô-Ø€Nøô1 ò 	/Ø )§¡Òð	/üò 2s   ÂE Â?E9ÅE6Å5E6ÚloadedÚmessage_kwargsc                 ón   — | \  }}}	 ddl m}  |||¬«       t        |||fd|i|¤ŽS # t        $ r Y Œw xY w)z[Bump Curator usage tracking (never fatal) and build the message block for one loaded skill.r   )Úbump_use©ra   r¤   )Útools.skill_usager°   rS   r¬   )r­   r¡   ra   r®   rq   ri   Ú
skill_namer°   s           r    Ú_render_skill_blockr´   &  sU   € ð +1Ñ'€L�)˜Zðõ 	/Ù� WÕ-ô   ¨i¸ÑoÐU\ÐoÐ`nÑoÐoøô ò Ùðús   ˆ( ¨	4³4)Ú
lead_linesÚmissingÚdisabledÚextra_instructionr¢   ÚsubjectÚloaded_namesrµ   r¶   r·   r¸   c                óD  — d| › dt        |«      › d�dg|xs g ¢ddj                  |«      › �‘}|r#|j                  ddj                  |«      › �«       |r#|j                  ddj                  |«      › �«       |r
|dd	|› �gz  }|r
|dd
|› �gz  }dj                  |«      S )z–Header for multi-skill messages (bundles and stacked invocations).
    ``subject`` must end in " skill bundle" so the bundle-format extractor applies.r   z
, loading zL skills together. Treat every skill below as active guidance for this turn.]r   zSkills loaded: z, zSkills missing (skipped): z-Skills disabled for this platform (skipped): zBundle instruction: zUser instruction: r&   )rJ   r'   r)   )r¹   rº   rµ   r¶   r·   r¸   r¢   Úliness           r    Ú_scaffold_headerr½   6  sæ   € ð 0°¨yð 9Ü�|Ó$Ð%ð &-ð	-ð 	ð	ð
 Ò
˜ðð ˜$Ÿ)™) LÓ1Ð2Ð3ð€Eñ Ø�‰Ð1°$·)±)¸GÓ2DÐ1EÐFÔGÙØ�‰ÐDÀTÇYÁYÈxÓEXÐDYÐZÔ[ÙØ�"Ð,Ð->Ð,?Ð@ÐAÑAˆÙØ�"Ð*Ð+;Ð*<Ð=Ð>Ñ>ˆØ�9‰9�UÓÐr"   >   ú.gitú.hubú.locksú.githubú.archivec                 óJ   — ddl m} t        | «      }|r ||«      €yd|› d| › �S )uÅ  User-facing note when *name*'s slash slug is a core command (name or alias), else None.

    The single source of the collision predicate: ``scan_skill_commands`` uses it to skip
    auto-registration (the shadowing guard from 370ebf2d3 â€” the skill map is consulted before
    built-in handlers), and the ``/skills`` listing plus the command palette render the note so
    the skipped skill is explained where the user looks, not only in the log.
    r   )Úresolve_commandNzslash command /u4    unavailable â€” name taken by built-in; use /skill )Úhermes_cli.commandsrÄ   r!   )r   rÄ   Úcmd_names      r    Úskill_command_collision_noterÇ   S  s7   € õ 4Ü! $Ó'€HÙ‘ xÓ0Ð8ØØ˜X˜JÐ&ZÐ[_ÐZ`ÐaÐar"   Úskill_mdÚ
seen_namesÚcommandsc                 óÖ  — ddl m}m}m}m} t        d„ | j                  D «       «      ry || j                  d¬«      «      \  }}	 ||«      r ||«      r ||«      sy|j                  d| j                  j                  «      }
|
|v s|
|v ry|j                  dd	«      xs0 t        d
„ |	j                  «       j                  d«      D «       d	«      }|j                  |
«       t        |
«      }|syt!        |
«      �t"        j%                  d|
||
«       yd|› �}||v rt"        j%                  d|
|||   d   «       y|
|xs d|
› d�t'        | «      t'        | j                  «      dœ||<   y)zGRegister one SKILL.md in *commands* (no-op when filtered or colliding).r   )Ú_parse_frontmatterÚskill_matches_appsÚskill_matches_platformÚskill_matches_environmentc              3   ó,   K  — | ]  }|t         v –— Œ y ­w©N)Ú_SCAN_SKIP_PARTS)rz   Úparts     r    r}   z!_scan_skill_md.<locals>.<genexpr>e  s   è ø€ Ò
?¨ˆ4Ô#Ô#Ñ
?ùs   ‚Nzutf-8)Úencodingr   Údescriptionr   c              3   ó¤   K  — | ]H  }|j                  «       sŒ|j                  «       j                  d «      rŒ4|j                  «       dd –— ŒJ y­w)ú#NéP   )r   r0   )rz   Úlines     r    r}   z!_scan_skill_md.<locals>.<genexpr>o  s=   è ø€ Òu˜tÀ4Ç:Á:Å<ÐX\×XbÑXbÓXd×XoÑXoÐpsÕXtˆ�‰‹�c�rÔ	Ñuùs   ‚A˜A¸Ar&   z†Skill %r generates slash command '/%s' which collides with a core Hermes command; skipping auto-registration. Use '/skill %s' instead.r>   zaSkill %r maps to slash command %s already claimed by %r; keeping the first and skipping this one.zInvoke the z skill)r   rÕ   Úskill_md_pathri   )rj   rÌ   rÍ   rÎ   rÏ   Úanyr#   Ú	read_textrm   rn   r   Únextr   rB   Úaddr!   rÇ   ÚloggerÚwarningr/   )rÈ   r·   rÉ   rÊ   rÌ   rÍ   rÎ   rÏ   r�   Úbodyr   rÕ   rÆ   Úcmd_keys                 r    Ú_scan_skill_mdrã   b  s{  € ç{Ó{Ü
Ñ
?°·±Ô
?Ô?ØÙ*¨8×+=Ñ+=ÀwÐ+=Ó+OÓPÑ€K�á! +Ô.Ñ6OÐP[Ô6\Ñdvð  xCô  eDØØ�?‰?˜6 8§?¡?×#7Ñ#7Ó8€DØˆzÑ˜T XÑ-ØØ—/‘/ -°Ó4ò ¼Ùu t§z¡z£|×'9Ñ'9¸$Ó'?ÔuØ
ó9€Kð ‡N�N�4ÔÜ! $Ó'€HÙØô $ DÓ)Ð5Ü�‰ð NØOSÐU]Ð_cô	eàð �(�ˆn€GØ�(ÑÜ�‰ÐzØ˜W h¨wÑ&7¸Ñ&?ô	AàØ!%°kÒ6_À{ÐSWÐRXÐX^ÐE_Ü*-¨h«-ÄcÈ(Ï/É/ÓFZñ\€HˆWÒr"   c                  ó  — t        «       } t        «       }t        «       }i }	 ddlm}m} ddlm}m}m	}m
}	  |«       }
t        «       } |«       } |«       D �cg c]
  } ||«      ‘Œ }}|j                  «       r|gng }|| |«       z   D �cg c]  } |	|d«      ‘Œ c}z  }|D ]  }|D ]  }	 t        ||
||«       Œ Œ 	 t        5  |a| a|a|addd«       |S c c}w c c}w # t        $ r Y ŒEw xY w# t        $ r Y Œ=w xY w# 1 sw Y   |S xY w)a:  Scan skill dirs and return {"/skill-name": {name, description, skill_md_path, skill_dir}}.
    Builds a local map and publishes once at the end: writing straight into the
    global exposed partial results to overlapping scans, which then logged
    bogus "already claimed" collisions against their own incumbents.r   )rc   Ú_get_disabled_skill_names)Úget_external_skills_dirsÚget_project_skills_dirsÚiter_project_skill_filesÚiter_skill_index_fileszSKILL.mdN)rU   rZ   r_   rj   rc   rå   r]   ræ   rç   rè   ré   ÚsetÚexistsrã   rS   Ú_publish_lockr   r   r   r   )ÚplatformÚhomeÚprojectrÊ   rc   rå   ræ   rç   rè   ré   r·   rÉ   Ú
skills_dirÚdÚitersÚlocalÚ_iterrÈ   s                     r    Úscan_skill_commandsrõ   ‡  s@  € ô 0Ó1€HÜ'Ó)€DÜ-Ó/€Gð +-€HðßL÷	
ó 	
ñ -Ó.ˆÜ›%ˆ
ñ
 !“]ˆ
Ù6MÓ6OÖP°Ñ)¨!Õ,ÐPˆÐPØ *× 1Ñ 1Ô 3�‘¸ˆØÀÑIaÓIcÑAcÖd¸AÑ(¨¨JÕ7ÒdÑdˆØò 	ˆEØ!ò �ðÜ" 8¨X°zÀ8ÕLññ	ô 
ñ 	*ð #ˆØ#+Ð Ø#ÐØ")Ð÷	*ð €Oùò1 Qùâdøô
 !ò Ùðûäò Ùðú÷
	*ð €Oús_   ¢5C/ ÁCÁ&$C/ Â
CÂC/ Â*C Â8C/ Ã	C>Ã
C/ Ã 	C,Ã)C/ Ã+C,Ã,C/ Ã/	C;Ã:C;Ã>Dc                  óÜ   — t        «       t        «       t        «       f} t        5  t        }t        |«      xr t        t        t        f| k(  }ddd«       rS t        «       S # 1 sw Y   ŒxY w)aŒ  Return the current skill commands mapping (scan first if empty). Rescans
    when the platform scope (one gateway serving Telegram and Discord) or the
    active profile's home (Desktop profile switch) or the session's project root (two sessions in two
    repos) changes, so each sees its own ``platform_disabled`` / ``external_dirs`` / project-skill view.

    See #14536, #88023, #114359.
    N)
rU   rZ   r_   rì   r   Úboolr   r   r   rõ   )ÚcurrentrÊ   Úis_freshs      r    Úget_skill_commandsrú   ½  sm   € ô 0Ó1Ô3OÓ3QÔSrÓStÐu€GÜ	ñ {Ü"ˆÜ˜“>ÒzÔ'?ÔAUÔWnÐ&oÐszÑ&zˆ÷{ñ
  ˆ8Ð:Ô%8Ó%:Ð:÷{ð {ús   ¤(A"Á"A+ÚbeforeÚafterc                 ó:  — t        t        |«      t        | «      z
  «      D �cg c]
  }|||   dœ‘Œ c}t        t        | «      t        |«      z
  «      D �cg c]
  }|| |   dœ‘Œ c}t        t        |«      t        | «      z  «      t        |«      dœS c c}w c c}w )z—Diff two {name: description} snapshots into added/removed/unchanged/total.
    Removed entries carry the pre-rescan description (the file may be gone).)r   rÕ   )ÚaddedÚremovedÚ	unchangedÚtotal)r”   rê   rJ   )rû   rü   Úns      r    Údiff_command_snapshotsr  Î  sŠ   € ô AGÄsÈ5ÃzÔTWÐX^ÓT_ÑG_Ó@`Öa¸1˜1¨U°1©XÓ6ÒaÜCIÌ#ÈfË+ÔX[Ð\aÓXbÑJbÓCcÖd¸a˜Q¨v°a©yÓ9ÒdÜœC ›J¬¨V«Ñ4Ó5Ü�U“ñ	ð ùÚaùÚds   £BÁBÚcmdsc                 ó¢   — | j                  «       D ��ci c].  \  }}|j                  d«      |xs i j                  d«      xs d“Œ0 c}}S c c}}w )zL``{"/slug": info}`` -> ``{"slug": description}`` for diff_command_snapshots.r>   rÕ   r   )r€   Úlstriprm   )r  r{   Úinfos      r    Úcommand_snapshotr  Ù  sD   € àTX×T^ÑT^ÓT`×aÁyÀsÈDˆC�J‰J�s‹O˜dšj b×-Ñ-¨mÓ<ÒBÀÑBÓaÐaùÓas   ”3Ac                  ó~   — t        t        «      } t        «       }t        | t        |«      «      }t	        |«      |d<   |S )a7  Re-scan skill dirs and return a diff of the slash-command map (``added``
    / ``removed`` / ``unchanged`` / ``total`` / ``commands``; descriptions are the
    full frontmatter field). Does NOT invalidate the skills system-prompt cache:
    skills are called by name, so ``/reload-skills`` costs no cache reset.rÊ   )r  r   rõ   r  rJ   )rû   Únew_commandsÚresults      r    Úreload_skillsr  Þ  s<   € ô
 œoÓ.€FÜ&Ó(€LÜ# FÔ,<¸\Ó,JÓK€FÜ˜\Ó*€Fˆ:ÑØ€Mr"   Úcommandc                 ó*   — t        | t        «       «      S )u¨   Resolve a user-typed /command to its canonical ``/slug`` key, or None.
    ``_`` â‰¡ ``-``: Telegram disallows hyphens, so ``/claude-code`` arrives as ``/claude_code``.)Úresolve_slash_keyrú   )r  s    r    Úresolve_skill_command_keyr  ê  s   € ô ˜WÔ&8Ó&:Ó;Ð;r"   Útablec                 óB   — | syd| j                  dd«      › �}||v r|S dS )zZ``command`` -> ``"/slug"`` when present in *table* (``_`` normalized to ``-``), else None.Nr>   r   r   )r   )r  r  râ   s      r    r  r  ð  s2   € áØØ�'—/‘/ # sÓ+Ð,Ð-€GØ Ñ&ˆ7Ð0¨DÐ0r"   râ   c                 ó”   — t        «       j                  | «      }|rt        |d   |¬«      nd}|syd|d   › d�}t        |||||¬«      S )zGBuild the user message for a skill slash command, or None if not found.ri   r±   Nz&[IMPORTANT: The user has invoked the "é   zf" skill, indicating they want you to follow its instructions. The full skill content is loaded below.])r¢   r£   )rú   rm   rs   r´   )râ   r¢   ra   r£   Ú
skill_infor­   Únotes          r    Úbuild_skill_invocation_messager  ø  sc   € ô $Ó%×)Ñ)¨'Ó2€JÙNXÔ  ¨KÑ!8À'ÕJÐ^b€FÙØØ4°V¸A±Y°Kð @Wð W€Dä˜v t¨WÐGWÐfrÔsÐsr"   é   Úrestc                 óp  — g }| xs d}t        |«      t        dz
  k  rˆ|j                  «       }|j                  d«      snf|j	                  dd«      dgz   dd \  }}t        |j                  d«      «      }|�||v rn)|j                  |«       |}t        |«      t        dz
  k  rŒˆ||j                  «       fS )zïConsume further leading ``/skill`` tokens from *rest* (text after the first
    matched command); stops at the first non-skill (or repeated) token, which
    starts the user instruction. Returns ``(extra_cmd_keys, remaining_instruction)``.r   r=   r>   Nr  )rJ   Ú_MAX_STACKED_SKILLSr  r0   rB   r  r)   r   )r  ÚkeysÚ	remainingÚstrippedÚtokenÚtailrâ   s          r    Úsplit_stacked_skill_commandsr!    s¿   € ð €DØ’
˜€IÜ
ˆd‹)Ô)¨AÑ-Ò
-Ø×#Ñ#Ó%ˆØ×"Ñ" 3Ô'ØØ—~‘~ d¨AÓ.°"°Ñ5°r¸Ð:‰ˆˆtÜ+¨E¯L©L¸Ó,=Ó>ˆØˆ?˜g¨™oØØ�‰�GÔØˆ	ô ˆd‹)Ô)¨AÑ-Ó
-ð �—‘Ó"Ð"Ð"r"   Úcmd_keysc                 óú   ‡‡— t        «       Š| D �cg c]  }|sŒ|‘Œ	 }}t        |ˆˆfd„d„ ‰d„ ¬«      \  }}}}|sydj                  |«      }	t        d|	› d�|||¬	«      }
d
j                  |
g|¢«      ||fS c c}w )z Build the user message for a stacked multi-skill slash invocation:
    ``(message, loaded_skill_names, missing_skill_names)``, or ``None`` when no skill loaded.c                 ó6   •— | ‰v rt        ‰|    d   ‰¬«      S d S )Nri   r±   ©rs   )râ   rÊ   ra   s    €€r    ú<lambda>z8build_stacked_skill_invocation_message.<locals>.<lambda>'  s(   ø€ Ð`gÐksÑ`sÔ+¨H°WÑ,=¸kÑ,JÐT[Ô\€ Ðy}€ r"   c                 ó   — d| › d�S )Nz1[Loaded as part of the stacked skill invocation "z".]© ©r   s    r    r&  z8build_stacked_skill_invocation_message.<locals>.<lambda>(  s   € ÐHÈÈÈcÐR€ r"   c                 ó$   — | j                  d«      S )Nr>   )r  )Úks    r    r&  z8build_stacked_skill_invocation_message.<locals>.<lambda>)  s   € ¨¯©°#«€ r"   )Úmissing_labelNr   ú"z" stacked skill bundle)r¶   r¢   ú

)rú   Ú_load_skill_blocksr'   r½   )r"  r¢   ra   r+  r  rº   r¶   Ú	_disabledÚskill_blocksÚtypedÚheaderrÊ   s     `        @r    Ú&build_stacked_skill_invocation_messager4    sŸ   ù€ ô
 "Ó#€HØÖ%�!¢1ŠAÐ%€DÐ%Ü5GØÜ}ÙRØÑ6ô	6Ñ2€L�'˜9 lñ ØØ�H‰H�T‹N€EÜ  % Ð(>Ð?ÀÐW^ð  rBô  C€FØ�K‰K˜Ð/ ,Ð/Ó0°,ÀÐHÐHùò &s
   ‘A8™A8rí   c                 óT   — 	 ddl m}  || ¬«      S # t        $ r t        «       cY S w xY w)zDOperator-disabled skill names (empty set when config is unreadable).r   )Úget_disabled_skill_names)rí   )r]   r6  rS   rê   )rí   r6  s     r    Ú_disabled_skill_namesr7  2  s+   € ðÝ>Ù'°Ô:Ð:øÜò Ü‹uŠðús   ‚ ‘'¦'c                 ó   — | S rÑ   r(  )Úidents    r    r&  r&  =  s   €  € r"   F)r,  Údisabled_namesÚdisabled_as_missingÚalready_loadedÚidentifiersr:  r;  r<  c          	      ó¼  — g }g }	g }
g }t        «       }| D ]À  }|r||v rŒ
|j                  |«        ||«      }|s|	j                   ||«      «       Œ=|d   }|r2||v s||v r*|r|	j                  |«       n|
j                  |xs |«       Œv|r||v r|j                  |«       ŒŽ|j                  t        | ||«      |«      «       |j                  |«       ŒÂ ||	|
|fS )uÎ  Load each distinct identifier via *load* and render its block; returns
    ``(loaded_names, missing, disabled, blocks)``. With *disabled_names*, members
    whose canonical (LOADED â€” identifiers may be paths) name or identifier is
    disabled go to ``disabled`` (or ``missing`` when *disabled_as_missing*).
    Canonical names in *already_loaded* (e.g. skills.auto_load) count as resolved
    but render no block, so one skill never lands in the prompt twice.r  )rê   rÞ   r)   r´   )r=  Úloadr¡   ra   r,  r:  r;  r<  rº   r¶   r·   ÚblocksÚseenÚ
identifierr­   r³   s                   r    r/  r/  ;  sú   € ð !€LØ€GØ€HØ€FÜ“U€DØ!ò (ˆ
Ù˜Z¨4Ñ/ØØ�‰�ÔÙ�jÓ!ˆÙØ�N‰N™=¨Ó4Ô5ØØ˜A‘Yˆ
Ù˜z¨^Ñ;¸zÈ^Ñ?[Ù"Ø—‘˜zÕ*à—‘ 
Ò 8¨jÔ9ØÙ˜j¨NÑ:Ø×Ñ 
Ô+ØØ�‰Ô)¨&±/À*Ó2MÈwÓWÔXØ×Ñ˜JÕ'ð'(ð( ˜ (¨FÐ2Ð2r"   Úskill_identifiersÚexcluded_loaded_namesc           	      ó¼   ‡— t        | D �cg c]  }|xs dj                  «       ‘Œ c}ˆfd„d„ ‰t        «       d|¬«      \  }}}}dj                  |«      ||fS c c}w )u§  Load skills for session-wide CLI/TUI preloading; returns (prompt_text,
    loaded_skill_names, missing_identifiers). Disabled skills count as missing:
    this path bypasses the scan-time filter, and ``hermes -s <skill>`` must not
    force-load an operator-disabled skill. *excluded_loaded_names* are canonical
    names the session already carries (skills.auto_load): they resolve as loaded
    but are not rendered again.

    Disabled skills are treated the same as missing ones: this loads via a raw identifier straight into
    ``_load_skill_payload``, bypassing ``get_skill_commands()``'s scan-time disabled filter â€” mirrors the
    bundle-invocation gate (#59156).
    r   c                 ó   •— t        | ‰¬«      S ©Nr±   r%  ©rB  ra   s    €r    r&  z/build_preloaded_skills_prompt.<locals>.<lambda>r  s   ø€ Ô.¨zÀ7ÔK€ r"   c                 ó   — d| › d�S )Nz9[IMPORTANT: The user launched this CLI session with the "z~" skill preloaded. Treat its instructions as active guidance for the duration of this session unless the user overrides them.]r(  r)  s    r    r&  z/build_preloaded_skills_prompt.<locals>.<lambda>s  s   € ÐQÐRVÐQWð XAð A€ r"   T)r:  r;  r<  r.  )r/  r   r7  r'   )rC  ra   rD  Úrawrº   r¶   r0  Úprompt_partss    `      r    Úbuild_preloaded_skills_promptrL  b  sj   ø€ ô 6HØ(9Ö: ˆ#Š)�×	Ñ	Õ	Ò:ÛKñ	Bð 	Ô 5Ó 7ÈTØ,ô6Ñ2€L�'˜9 lð �;‰;�|Ó$ l°GÐ;Ð;ùò 	;s   ‹AÚuser_configc                 ó¸  — | €	 ddl m}  |«       } t        | t        «      r| j                  d«      nd}t        |t        «      r|j                  d«      nd}t        |t        «      sg S |D �cg c]4  }t        |t        «      sŒ|j                  «       sŒ%|j                  «       ‘Œ6 }}t        t        j                  |«      «      S # t        $ r g cY S w xY wc c}w )z•``skills.auto_load`` from *user_config* (else the active profile config), deduplicated;
    empty when unset, malformed, or the config is unreadable.Nr   )Úload_config_readonlyÚskillsÚ	auto_load)
Úhermes_cli.configrO  rS   r.   Údictrm   r“   r/   r   Úfromkeys)rM  rO  Úskills_blockrQ  r›   Únamess         r    Úresolve_auto_load_skillsrW  |  s¾   € ð Ðð	Ý>Ù.Ó0ˆKô 1;¸;ÌÔ0M�;—?‘? 8Ô,ÐSW€LÜ1;¸LÌ$Ô1O�× Ñ  Ô-ÐUY€IÜ�i¤Ô&Øˆ	Ø(1Ö^˜u´ZÀÄsÕ5KÐPU×P[ÑP[ÕP]ˆU�[‰[�]Ð^€EÐ^Ü”—‘˜eÓ$Ó%Ð%øô ò 	ØŠIð	üò _s#   „C Á-CÂCÂCÃCÃCÚhome_overridec                 ó  ‡ — ddl m}m} |� |t        |«      «      nd}	 t	        |«      }|sdg g f|�	 ||«       S S t        |ˆ fd„d„ ‰ t        «       d¬«      \  }}}	}
d	j                  |
«      ||f|�	 ||«       S S # |�	 ||«       w w xY w)
aT  Render ``skills.auto_load`` as fully loaded skill blocks for a new session; returns
    ``(prompt_text, loaded_names, missing)``. Missing and operator-disabled names are reported,
    never raised: a typo in config must not block session start on any surface.

    *home_override* makes home resolution EXPLICIT (same seam as ``build_skills_system_prompt``): the config,
    the disabled list and the ``<home>/skills`` lookup all resolve under that home, so a gateway build thread
    that lost the HERMES_HOME ContextVar cannot pin the launch profile's skills into another profile's prompt.
    r   )Úreset_hermes_home_overrideÚset_hermes_home_overrideNr   c                 ó   •— t        | ‰¬«      S rG  r%  rH  s    €r    r&  z(build_auto_load_prompt.<locals>.<lambda>   s   ø€ Ô2°:ÀwÔO€ r"   c                 ó   — d| › d�S )Nz[IMPORTANT: The "z¡" skill is auto-loaded via config (skills.auto_load). Treat its instructions as active guidance for the duration of this session unless the user overrides them.]r(  r)  s    r    r&  z(build_auto_load_prompt.<locals>.<lambda>¡  s   € Ð-¨d¨Vð 46ð 6€ r"   T)r:  r;  r.  )rY   rZ  r[  r/   rW  r/  r7  r'   )ra   rM  rX  rZ  r[  Ú
home_tokenÚauto_skillsrº   r¶   r0  rK  s   `          r    Úbuild_auto_load_promptr`  �  s¼   ø€ ÷ VØANÐAZÑ)¬#¨mÓ*<Ô=Ð`d€Jð3Ü.¨{Ó;ˆÙØ�r˜2�:ð Ð!Ù& zÕ2ð "ô :LØÛOñ7ð Ô$9Ó$;ÐQUô:
Ñ6ˆ�g˜y¨,ð �{‰{˜<Ó(¨,¸Ð?àÐ!Ù& zÕ2ð "øˆ:Ð!Ù& zÕ2ð "ús    A> ½5A> Á>B)u    â€” rÑ   )r   r   N)r   Nr   )r   N)NN)NNN)VÚ__doc__rk   ÚloggingrQ   ÚreÚ	threadingÚpathlibr   Útypingr   r   r   rY   r   Úagent.prompt_cache_boundaryr   Úagent.skill_preprocessingr	   r§   r
   Ú	getLoggerÚ__name__rß   r   r/   Ú__annotations__r   r   r   ÚLockrì   Úcompiler   r   r1   r7   r(   r8   r2   r4   r5   Úescaper?   ÚSKILL_SCAFFOLD_SQL_LIKErC   r!   r“   r+   r:   rE   r3   rU   rZ   r_   ÚtuplerS  rs   rƒ   r¨   r‰   rŠ   r    r¬   r´   r½   rÒ   rÇ   rê   rã   rõ   rú   r  r  r  r  r  r  r  r!  r4  r7  r÷   r/  rL  rW  r`  r(  r"   r    ú<module>rq     s3  ðÚ Xã Û Û 	Û 	Û Ý ß &Ñ &å 0Ý >ß ià	ˆ×	Ñ	˜8Ó	$€à-/€��c˜4  S ™>Ð)Ñ*Ó /Ø*.Ð ˜( 3™-Ó .Ø&*Ð �h˜s‘mÓ *Ø)-Ð ˜ #™Ó -ð �	—‘Ó €ð "�r—z‘z )Ó,Ð Ø �b—j‘j Ó*Ð ð CÐ ØAÐ ØnÐ Ø$€Ø!€Ø1Ð Ø9Ð ð �—‘˜I˜BŸI™IÐ&>Ó?À,ÑNÓO€ð 3°SÑ8Ð ð Ð ð8˜Sð 8 Só 8ð 4ð °cð ¸có ð ¸ð ÀÈ#Áó ñ"# sð #°sð #ÈÐRUÉó #ð*W˜ð W Sð W°sð WÀXÈcÁ]ó Wð%¨(°3©-ó %ð " có "ð3¨°#©ó 3ñP¨#ð P¸¸d¹
ð PÈeÐTXÐY\Ð^aÐYaÑTbÐdhÐkoÑdoÐqtÐTtÑNuÐx|ÑN|ó Pð8 t¨C°¨H¡~ð ¸dÀ3¹ið ÈDó ð&6ð ð;ð ð˜d 3¨ 8™nð °¸#±ó ð D¨¨c¨¡Nð ¸tÀd¹{ð ÈtÐTWÉyó ð ØØ!ñ2Ø�s˜C�x‘.ð2à�d‰{ð2ð ð2ð ð	2ð
 ð2ð �d‘
ð2ð 	ó2ðjpØ�$�s˜C�x‘. $¨¡+¨sÐ2Ñ3ðpØFIðpØTWÐZ^ÑT^ðpØruðpàópð" NRØ $À4Ø¸òØðØ $ S¡	ðØ:>¸s¹)ÀdÑ:Jðà�#‰Y˜Ñðà04°S±	¸DÑ0@ðð ðð 47ðð 	ó	ò4 EÐ ðb sð b¨x¸©}ó bð"\˜Tð "\¨Sð "\¸cð "\ÈTÐRUÐW[Ð\_ÐadÐ\dÑWeÐReÑMfð "\Ðkoó "\ðJ3˜T # t¨C°¨H¡~Ð"5Ñ6ó 3ðl;˜D  d¨3°¨8¡nÐ!4Ñ5ó ;ð" 4¨¨S¨¡>ð ¸$¸sÀC¸x¹.ð ÈTÐRUÐWZÐRZÉ^ó ðb˜4  T¨#¨s¨(¡^Ð 3Ñ4ð b¸¸cÀ3¸h¹ó bð
	�t˜C ˜H‘~ó 	ð< sð <¨x¸©}ó <ð1˜sð 1¨4°°S°©>ð 1¸hÀs¹mó 1ð _añ
tØð
tØ$'ð
tØ7:¸T±zð
tØX[ð
tàˆc�]ó
tð  Ð ð# sð #¨u°T¸#±YÀ°^Ñ/Dó #ð( LPñIØ�3‰iðIØ+.ðIØ>AÀD¹jðIàˆe�C˜˜c™ D¨¡IÐ-Ñ.Ñ/óIñ( C¨$¡Jð ¸#ó ñ &ÀDÐfkØ!%ò$3Ø�c‘ð$3Ø<?À$¹Jð$3à7:¸T±zð$3à_cð$3ð ˜$‘Jð$3ð ˆ4�‰9�d˜3‘i  c¡¨D°©IÐ5Ñ6ó	$3ðP hlñ<Ø˜C‘yð<Ø+.°©:ð<ØUXÐY\ÑU]Ð`dÑUdð<à
ˆ3��S‘	˜4 ™9Ð$Ñ%ó<ñ4&¨$°©+ð &ÀÀcÁó &ð$ _cñ3Ø�4‰Zð3Ø-1°D©[ð3ØPTÐW[ÑP[ð3à
ˆ3��S‘	˜4 ™9Ð$Ñ%ô3r"   