§
    ÷žyj®†  ã                   ó   — U d Z ddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZ ddlmZ ddlmZ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	<    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j'        e ¦  «        dz   ¦  «        Z(e dz   Z)dZ*de+dedefd„Z,dede
e         fd„Z-d=dedede
e         fd„Z.dede
e         fd „Z/dede
e         fd!„Z0de
e         fd"„Z1d>d#ed$edz  de2e3eef         edz  ef         dz  fd%„Z4d&e3eef         de+e         ddfd'„Z5	 	 	 d?d&e3eef         d)edz  d*ed+ed,ed-edz  defd.„Z6de	ee	eef         f         fd/„Z7de	ee	eef         f         fd0„Z8de	eef         fd1„Z9d2ede
e         fd3„Z:	 	 	 d@d4ed+ed$edz  d,ede
e         f
d5„Z;d6Z<d7ede2e+e         ef         fd8„Z=	 	 dAd9e+e         d+ed$edz  de
e2ee+e         e+e         f                  fd:„Z>	 d>d;e+e         d$edz  de2ee+e         e+e         f         fd<„Z?dS )Bz Shared slash command helpers for skills.

Shared between CLI (cli.py) and gateway (gateway/run.py) so both surfaces
can invoke skills via /skill-name commands.
é    N)ÚPath)ÚAnyÚDictÚOptional)Údisplay_hermes_home)Úregister_stable_prefix)Úexpand_inline_shellÚload_skills_configÚsubstitute_template_varsÚ_skill_commandsÚ_skill_commands_platformz
[^a-z0-9-]z-{2,}z%[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	"([^"]*)"ú%úÚpartsÚinstructionÚreturnc                 ó‚   — d                      | ¦  «        dz   t          z   }|                      t          › |› �¦  «         |S )u˜  Append the instruction line to ``parts``; return the stable prefix.

    Shared by every builder that ends a static skill scaffold with the
    caller-supplied volatile instruction (single-skill invocations, cron job
    prompts). The returned prefix ends exactly at the instruction marker, so
    registering it with ``agent.prompt_cache_boundary`` lets the Anthropic
    cache planner put a breakpoint on the scaffold instead of caching the
    whole message as one atomic block (#81867). 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.
    Ú
)ÚjoinÚ_SINGLE_SKILL_INSTRUCTIONÚappend)r   r   Ústable_prefixs      ú:/home/ragecks/.hermes/hermes-agent/agent/skill_commands.pyÚappend_user_instructionr   J   sD   € ð —I’I˜eÑ$Ô$ tÑ+Õ.GÑG€MØ	‡L‚LÕ-Ð<¨{Ð<Ð<Ñ=Ô=Ð=ØÐó    Úcontentc                 óÌ   — t          | t          ¦  «        sdS |                      t          ¦  «        s| S t          | v rt          | ¦  «        S t          | v rt          | ¦  «        S dS )u(  Recover the user's instruction from a slash-skill-expanded turn.

    Returns:
        - The original string unchanged when it is NOT skill scaffolding
          (a normal user message passes straight through).
        - The extracted user instruction when the scaffolding carried one.
        - ``None`` when the content is skill scaffolding with no user
          instruction (i.e. a bare ``/skill`` invocation). Callers that feed
          memory providers should skip the turn in that case â€” there is no
          user content worth storing.
    N)Ú
isinstanceÚstrÚ
startswithÚ_SKILL_INVOCATION_PREFIXÚ_BUNDLE_MARKERÚ _extract_bundle_user_instructionÚ_SINGLE_SKILL_MARKERÚ&_extract_single_skill_user_instruction)r   s    r   Ú+extract_user_instruction_from_skill_messager&   [   sn   € õ �g�sÑ#Ô#ð Øˆtà×ÒÕ6Ñ7Ô7ð Øˆå˜Ð Ð Ý/°Ñ8Ô8Ð8å˜wÐ&Ð&Ý5°gÑ>Ô>Ð>àˆ4r   õ    â€” Ú	separatorc                 ó  — t          | t          ¦  «        r|                      t          ¦  «        sdS t                               | ¦  «        }|r|                     d¦  «        nd                     ¦   «         }|                     d¦  «        r|nd|› �}t          | ¦  «        }|rY|| urU| 	                    t          ¦  «        d         }d                     | 	                    ¦   «         ¦  «        }|r|r|› |› |› �n|S |r|ndS )u   Render a slash-skill-expanded turn the way the user typed it.

    The expanded message embeds the whole skill body, so any surface that
    summarizes a user turn from its raw content â€” session titles, sidebar
    previews, the ``/rewind`` picker â€” otherwise shows the skill's own prose
    as if the user had written it. That is how a skill's opening line ends up
    as a session title.

    Returns ``"/work â€” fix the title leak"``, or ``"/work"`` for a bare
    invocation, or ``None`` when *content* is not skill scaffolding (the
    caller should then summarize it as an ordinary message).

    *separator* joins the command and the instruction. Previews use the
    default em dash; pass ``" "`` for the literal invocation the user typed,
    which is what chat transcripts render.
    Né   Ú ú/r   Ú )r   r   r    r!   Ú_SKILL_NAME_REÚmatchÚgroupÚstripr&   ÚsplitÚSKILL_EXCERPT_JOINTr   )r   r(   r/   ÚnameÚlabelr   s         r   Údescribe_skill_invocationr6   v   s  € õ" �g�sÑ#Ô#ð ¨7×+=Ò+=Õ>VÑ+WÔ+Wð Øˆtå× Ò  Ñ)Ô)€EØ#Ð+ˆE�KŠK˜‰NŒNˆN¨×2Ò2Ñ4Ô4€Dð —O’O CÑ(Ô(Ð8ˆDˆD¨j°$¨j¨j€Eå=¸gÑFÔF€KØð O�{¨'Ð1Ð1ð "×'Ò'Õ(;Ñ<Ô<¸QÔ?ˆØ—h’h˜{×0Ò0Ñ2Ô2Ñ3Ô3ˆØð 	OØ9=ÐN�eÐ5˜YÐ5¨Ð5Ð5Ð5À;ÐNàÐ"ˆ5ˆ5˜dÐ"r   Úmessagec                 ó  — |                       t          ¦  «        }|dk     rd S | |t          t          ¦  «        z   d …         }|                     t          ¦  «        }|dk    r
|d |…         }|                     ¦   «         }|pd S ©Nr   )Úrfindr   ÚlenÚfindÚ_RUNTIME_NOTEr1   )r7   Ú
marker_idxr   Úruntime_idxs       r   r%   r%   �   sŠ   € ð —’Õ8Ñ9Ô9€JØ�A‚~€~Øˆtà˜*¥sÕ+DÑ'EÔ'EÑEÐFÐFÔG€KØ×"Ò"¥=Ñ1Ô1€KØ�aÒÐØ! , ; ,Ô/ˆØ×#Ò#Ñ%Ô%€KØÐ˜$Ðr   c                 ó  — |                       t          ¦  «        }|dk     rd S | |t          t          ¦  «        z   d …         }|                      t          ¦  «        }|dk    r
|d |…         }|                     ¦   «         }|pd S r9   )r<   Ú_BUNDLE_USER_INSTRUCTIONr;   Ú_BUNDLE_FIRST_SKILL_BLOCKr1   )r7   r>   r   Úfirst_skill_idxs       r   r#   r#   ¬   s�   € ð —’Õ6Ñ7Ô7€JØ�A‚~€~Øˆtà˜*¥sÕ+CÑ'DÔ'DÑDÐEÐEÔF€KØ!×&Ò&Õ'@ÑAÔA€OØ˜!ÒÐØ!Ð"2 ?Ð"2Ô3ˆØ×#Ò#Ñ%Ô%€KØÐ˜$Ðr   c                  ó    — 	 ddl m}  t          j        d¦  «        p
 | d¦  «        }n$# t          $ r t          j        d¦  «        }Y nw xY w|pdS )a  Return the current platform scope used for disabled-skill filtering.

    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).

    Resolves from (in order) ``HERMES_PLATFORM`` env var and
    ``HERMES_SESSION_PLATFORM`` from the gateway session context. Returns
    ``None`` when no platform scope is active (e.g. classic CLI, RL
    rollouts, standalone scripts).
    r   )Úget_session_envÚHERMES_PLATFORMÚHERMES_SESSION_PLATFORMN)Úgateway.session_contextrE   ÚosÚgetenvÚ	Exception)rE   Úresolved_platforms     r   Ú _resolve_skill_commands_platformrM   »   sƒ   € ð9Ø;Ð;Ð;Ð;Ð;Ð;õ ŒIÐ'Ñ(Ô(ð :ØˆÐ8Ñ9Ô9ð 	Ðøõ ð 9ð 9ð 9ÝœIÐ&7Ñ8Ô8ÐÐÐð9øøøàÐ$ Ð$s   ‚%( ¨A	ÁA	Úskill_identifierÚtask_idc                 óB  — | pd                      ¦   «         }|sdS 	 ddlm}m} ddlm}  ||¦  «        }t          j         |||d¬¦  «        ¦  «        }n# t          $ r Y dS w xY w| 	                    d¦  «        sdS t          | 	                    d	¦  «        p|¦  «        }t          | 	                    d
¦  «        pd¦  «        }	d}
| 	                    d¦  «        }|rt          |¦  «        }
n-|	r+	 |t          |	¦  «        j        z  }
n# t          $ r d}
Y nw xY w||
|fS )zOLoad a skill by name/path and return (loaded_payload, skill_dir, display_name).r+   Nr   )Ú
SKILLS_DIRÚ
skill_view)Únormalize_skill_lookup_nameF)rO   Ú
preprocessÚsuccessr4   ÚpathÚ	skill_dir)r1   Útools.skills_toolrQ   rR   Úagent.skill_utilsrS   ÚjsonÚloadsrK   Úgetr   r   Úparent)rN   rO   Úraw_identifierrQ   rR   rS   Ú
normalizedÚloaded_skillÚ
skill_nameÚ
skill_pathrW   Úabs_skill_dirs               r   Ú_load_skill_payloadrd   Ò   s”  € à&Ð,¨"×3Ò3Ñ5Ô5€NØð Øˆtð
Ø<Ð<Ð<Ð<Ð<Ð<Ð<Ð<ØAÐAÐAÐAÐAÐAà0Ð0°Ñ@Ô@ˆ
å”zØˆJ�z¨7¸uÐEÑEÔEñ
ô 
ˆˆøõ ð ð ð Øˆtˆtðøøøð ×Ò˜IÑ&Ô&ð Øˆtå�\×%Ò% fÑ-Ô-Ð;°Ñ<Ô<€JÝ�\×%Ò% fÑ-Ô-Ð3°Ñ4Ô4€JØ€Ið
 !×$Ò$ [Ñ1Ô1€MØð Ý˜Ñ'Ô'ˆ	ˆ	Ø	ð ð	Ø"¥T¨*Ñ%5Ô%5Ô%<Ñ<ˆIˆIøÝð 	ð 	ð 	ØˆIˆIˆIð	øøøð ˜ JÐ.Ð.s#   œ9A Á
A$Á#A$Ã2D
 Ä
DÄDr`   c                 ó  — 	 ddl m}m}m} t	          |                      d¦  «        p|                      d¦  «        pd¦  «        }|sdS  ||¦  «        \  }} ||¦  «        }|sdS  ||¦  «        }	|	sdS ddt          ¦   «         › d�g}
|	                     ¦   «         D ]3\  }}|rt	          |¦  «        nd	}|
                     d
|› d|› �¦  «         Œ4|
                     d¦  «         | 	                    |
¦  «         dS # t          $ r Y dS w xY w)ae  Resolve and inject skill-declared config values into the message parts.

    If the loaded skill's frontmatter declares ``metadata.hermes.config``
    entries, their current values (from config.yaml or defaults) are appended
    as a ``[Skill config: ...]`` block so the agent knows the configured values
    without needing to read config.yaml itself.
    r   )Úextract_skill_config_varsÚparse_frontmatterÚresolve_skill_config_valuesÚraw_contentr   r+   Nz[Skill config (from z/config.yaml):z	(not set)z  z = Ú])rY   rf   rg   rh   r   r\   r   Úitemsr   ÚextendrK   )r`   r   rf   rg   rh   ri   ÚfrontmatterÚ_Úconfig_varsÚresolvedÚlinesÚkeyÚvalueÚdisplay_vals                 r   Ú_inject_skill_configru   ú   sŠ  € ðð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
õ ˜,×*Ò*¨=Ñ9Ô9Ð^¸\×=MÒ=MÈiÑ=XÔ=XÐ^Ð\^Ñ_Ô_ˆØð 	ØˆFà*Ð*¨;Ñ7Ô7‰ˆ�QØ/Ð/°Ñ<Ô<ˆØð 	ØˆFà.Ð.¨{Ñ;Ô;ˆØð 	ØˆFàÐQÕ,?Ñ,AÔ,AÐQÐQÐQÐRˆØ"Ÿ.š.Ñ*Ô*ð 	5ð 	5‰JˆC�Ø(-Ð>�#˜e™*œ*˜*°;ˆKØ�LŠLÐ3˜cÐ3Ð3 kÐ3Ð3Ñ4Ô4Ð4Ð4Ø�Š�SÑÔÐØ�Š�UÑÔÐÐÐøÝð ð ð Øˆˆðøøøs%   ‚AC= Á	C= Á&C= Á5BC= Ã=
DÄ
Dr+   rW   Úactivation_noteÚuser_instructionÚruntime_noteÚ
session_idc                 óJ  — ddl m} t          |                      d¦  «        pd¦  «        }t	          ¦   «         }|                     dd¦  «        rt          |||¦  «        }|                     dd¦  «        r6t          |                     d	d
¦  «        pd
¦  «        }	t          |||	¦  «        }|d|                     ¦   «         g}
|rC|
 	                    d¦  «         |
 	                    d|› d�¦  «         |
 	                    d¦  «         t          | |
¦  «         |                      d¦  «        r|
                     ddg¦  «         n‚|                      d¦  «        r"|
                     dd| d         › d�g¦  «         nK|                      d¦  «        r6|                      d¦  «        r!|
                     dd| d         › d�g¦  «         g }|                      d¦  «        pi }|                     ¦   «         D ],}t          |t          ¦  «        r|                     |¦  «         Œ-|s¤|r¢dD ]Ÿ}||z  }|                     ¦   «         r„t!          |                     d¦  «        ¦  «        D ]a}|                     ¦   «         rK|                     ¦   «         s7t          |                     |¦  «        ¦  «        }| 	                    |¦  «         ŒbŒ |r¦|r¤	 t          |                     |¦  «        ¦  «        }n# t*          $ r
 |j        }Y nw xY w|
 	                    d¦  «         |
 	                    d¦  «         |D ] }|
 	                    d|› d||z  › �¦  «         Œ!|
 	                    d|› d|› d�¦  «         d}|r%|
 	                    d¦  «         t/          |
|¦  «        }|r.|
 	                    d¦  «         |
 	                    d|› d�¦  «         d                     |
¦  «        }|�D|                     |¦  «        r/t5          |¦  «        t5          |¦  «        k    rt7          |¦  «         |S ) z9Format a loaded skill into a user/system message payload.r   )rQ   r   r+   Útemplate_varsTÚinline_shellFÚinline_shell_timeouté
   z[Skill directory: rj   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.Úsetup_skippedz‹[Skill setup note: Required environment setup was skipped. Continue loading the skill and explain any reduced functionality if it matters.]Úgateway_setup_hintz[Skill setup note: Úsetup_neededÚ
setup_noteÚlinked_files)Ú
referencesÚ	templatesÚscriptsÚassetsÚ*z"[This skill has supporting files:]z- 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   )rX   rQ   r   r\   Ú_load_skills_configÚ_substitute_template_varsÚintÚ_expand_inline_shellr1   r   ru   rl   Úvaluesr   ÚlistÚexistsÚsortedÚrglobÚis_fileÚ
is_symlinkÚrelative_toÚ
ValueErrorr4   r   r   r    r;   r   )r`   rW   rv   rw   rx   ry   rQ   r   Ú
skills_cfgÚtimeoutr   Ú
supportingrƒ   ÚentriesÚsubdirÚsubdir_pathÚfÚrelÚskill_view_targetÚsfr   r7   s                         r   Ú_build_skill_messager    !  så  € ð -Ð,Ð,Ð,Ð,Ð,å�,×"Ò" 9Ñ-Ô-Ð3°Ñ4Ô4€Gõ
 %Ñ&Ô&€JØ‡~‚~�o tÑ,Ô,ð LÝ+¨G°YÀ
ÑKÔKˆØ‡~‚~�n eÑ,Ô,ð DÝ�j—n’nÐ%;¸RÑ@Ô@ÐFÀBÑGÔGˆÝ& w°	¸7ÑCÔCˆà˜b '§-¢-¡/¤/Ð2€Eð ð 
Ø�Š�RÑÔÐØ�ŠÐ6¨)Ð6Ð6Ð6Ñ7Ô7Ð7Ø�Šð>ñ	
ô 	
ð 	
õ ˜ uÑ-Ô-Ð-à×Ò˜Ñ(Ô(ð 
Ø�Šàð ^ðñ	
ô 	
ð 	
ð 	
ð 
×	Ò	Ð.Ñ	/Ô	/ð 
Ø�ŠàØK lÐ3GÔ&HÐKÐKÐKðñ	
ô 	
ð 	
ð 	
ð 
×	Ò	˜.Ñ	)Ô	)ð 
¨l×.>Ò.>¸|Ñ.LÔ.Lð 
Ø�ŠàØC l°<Ô&@ÐCÐCÐCðñ	
ô 	
ð 	
ð €JØ×#Ò# NÑ3Ô3Ð9°r€LØ×&Ò&Ñ(Ô(ð 'ð 'ˆÝ�g�tÑ$Ô$ð 	'Ø×Ò˜gÑ&Ô&Ð&øàð /˜)ð /ØFð 	/ð 	/ˆFØ# fÑ,ˆKØ×!Ò!Ñ#Ô#ð /Ý × 1Ò 1°#Ñ 6Ô 6Ñ7Ô7ð /ð /�AØ—y’y‘{”{ð /¨1¯<ª<©>¬>ð /Ý! !§-¢-°	Ñ":Ô":Ñ;Ô;˜Ø"×)Ò)¨#Ñ.Ô.Ð.øøàð 
�ið 
ð	/Ý # I×$9Ò$9¸*Ñ$EÔ$EÑ FÔ FÐÐøÝð 	/ð 	/ð 	/à )¤ÐÐÐð	/øøøð 	�Š�RÑÔÐØ�ŠÐ9Ñ:Ô:Ð:Øð 	:ð 	:ˆBØ�LŠLÐ8˜bÐ8Ð8¨	°B©Ð8Ð8Ñ9Ô9Ð9Ð9Ø�Šð9Ð9Jð 9ð 9à$ð9ð 9ð 9ñ	
ô 	
ð 	
ð €MØð IØ�Š�RÑÔÐõ 0°Ð7GÑHÔHˆàð 8Ø�Š�RÑÔÐØ�ŠÐ6 |Ð6Ð6Ð6Ñ7Ô7Ð7à�iŠi˜ÑÔ€GØÐ  W×%7Ò%7¸Ñ%FÔ%FÐ Í3ÈwÉ<Ì<ÕZ]Ð^kÑZlÔZlÒKlÐKlÝ˜}Ñ-Ô-Ð-Ø€Ns   Ë"K/ Ë/LÌLc            	      ó  — t          ¦   «         ai a	 ddlm} m}m}m}m} ddl	m
}m} ddlm}  |¦   «         }t          ¦   «         }	g }
|                      ¦   «         r|
                     | ¦  «         |
                      |¦   «         ¦  «         |
D �]^} ||d¦  «        D �]M}t%          d„ |j        D ¦   «         ¦  «        rŒ"	 |                     d¬¦  «        } ||¦  «        \  }} ||¦  «        sŒS ||¦  «        sŒ_|                     d	|j        j        ¦  «        }||	v rŒ„||v rŒ‰|                     d
d¦  «        }|sa|                     ¦   «                              d¦  «        D ]9}|                     ¦   «         }|r!|                     d¦  «        s|dd…         } nŒ:|	                     |¦  «         |                     ¦   «                              dd¦  «                             dd¦  «        }t<                               d|¦  «        }t@                               d|¦  «                             d¦  «        }|s�Œ  ||¦  «        �tB           "                    d|||¦  «         �ŒÊd|› �}|t          v r0tB           "                    d||t          |         d	         ¦  «         �Œ||pd|› d�tG          |¦  «        tG          |j        ¦  «        dœt          |<   �Œ># tH          $ r Y �ŒKw xY w�Œ`n# tH          $ r Y nw xY wt          S )z®Scan ~/.hermes/skills/ and return a mapping of /command -> skill info.

    Returns:
        Dict mapping "/skill-name" to {name, description, skill_md_path, skill_dir}.
    r   )rQ   Ú_parse_frontmatterÚskill_matches_platformÚskill_matches_environmentÚ_get_disabled_skill_names)Úget_external_skills_dirsÚiter_skill_index_files)Úresolve_commandzSKILL.mdc              3   ó   K  — | ]}|d v V — Œ	dS )>   ú.gitú.hubú.githubú.archiveN© )Ú.0Úparts     r   ú	<genexpr>z&scan_skill_commands.<locals>.<genexpr>ª  s(   è è € ÐbÐbÈ4�tÐFÐFÐbÐbÐbÐbÐbÐbr   zutf-8)Úencodingr4   Údescriptionr+   r   ú#NéP   r-   Ú-rn   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)r4   r³   Úskill_md_pathrW   )%rM   r   r   rX   rQ   r¢   r£   r¤   r¥   rY   r¦   r§   Úhermes_cli.commandsr¨   Úsetr�   r   rl   Úanyr   Ú	read_textr\   r]   r4   r1   r2   r    ÚaddÚlowerÚreplaceÚ_SKILL_INVALID_CHARSÚsubÚ_SKILL_MULTI_HYPHENÚloggerÚwarningr   rK   )rQ   r¢   r£   r¤   r¥   r¦   r§   r¨   ÚdisabledÚ
seen_namesÚdirs_to_scanÚscan_dirÚskill_mdr   rm   Úbodyr4   r³   ÚlineÚcmd_nameÚcmd_keys                        r   Úscan_skill_commandsrÍ   ’  s  € õ  @ÑAÔAÐØ€OðSð 	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	Sð  	SØVÐVÐVÐVÐVÐVÐVÐVØ7Ð7Ð7Ð7Ð7Ð7Ø,Ð,Ñ.Ô.ˆÝ™%œ%ˆ
ð ˆØ×ÒÑÔð 	,Ø×Ò 
Ñ+Ô+Ð+Ø×ÒÐ4Ð4Ñ6Ô6Ñ7Ô7Ð7à$ð D	ñ D	ˆHØ2Ð2°8¸ZÑHÔHð Cñ C�ÝÐbÐbÐS[ÔSaÐbÑbÔbÑbÔbð Øð@Ø&×0Ò0¸'Ð0ÑBÔB�GØ(:Ð(:¸7Ñ(CÔ(CÑ%�K à1Ð1°+Ñ>Ô>ð !Ø ð 5Ð4°[ÑAÔAð !Ø Ø&Ÿ?š?¨6°8´?Ô3GÑHÔH�DØ˜zÐ)Ð)Ø à˜xÐ'Ð'Ø Ø"-§/¢/°-ÀÑ"DÔ"D�KØ&ð &Ø$(§J¢J¡L¤L×$6Ò$6°tÑ$<Ô$<ð &ð &˜DØ#'§:¢:¡<¤<˜DØ#ð &¨D¯OªO¸CÑ,@Ô,@ð &Ø.2°3°B°3¬i Ø % øØ—N’N 4Ñ(Ô(Ð(ð  $Ÿzšz™|œ|×3Ò3°C¸Ñ=Ô=×EÒEÀcÈ3ÑOÔO�HÝ3×7Ò7¸¸HÑEÔE�HÝ2×6Ò6°s¸HÑEÔE×KÒKÈCÑPÔP�HØ#ð !Ù ð '� xÑ0Ô0Ð<ÝŸšðJð ! (¨Dñ	ô ð ñ !ð
 - (˜n˜n�GØ¥/Ð1Ð1ÝŸšðNà  '­?¸7Ô+CÀFÔ+Kñô ð ñ
 !à $Ø'2Ð'PÐ6PÀDÐ6PÐ6PÐ6PÝ),¨X©¬Ý%(¨¬Ñ%9Ô%9ð	0ð 0•O GÑ,Ñ,øõ !ð ð ð Ø‘HðøøøñECðD	øõJ ð ð ð ØˆðøøøåÐs‹   ’B/K1 Ã/KÃ1K1 Ã2KÃ=K1 Ã>$KÄ"K1 Ä#KÄ'K1 Ä(DKÈ=K1 È?(KÉ'K1 É)<KÊ%K1 Ê'4KËK1 Ë
K+Ë'K1 Ë*K+Ë+K1 Ë1
K>Ë=K>c                  óh   — t           rt          t          ¦   «         k    rt          ¦   «          t           S )a  Return the current skill commands mapping (scan first if empty).

    Rescans when the active platform scope changes (e.g. a gateway
    process serving Telegram and Discord concurrently) so each platform
    sees its own ``skills.platform_disabled`` view (#14536).
    )r   r   rM   rÍ   r®   r   r   Úget_skill_commandsrÏ   ò  s0   € õ ðå#Õ'GÑ'IÔ'IÒIÐIåÑÔÐÝÐr   c                  ól  ‡‡— dt           t          t           t          t          f         f         dt           t          t          f         fd„}  | t          ¦  «        Št	          ¦   «         } | |¦  «        Št          t          ‰¦  «        t          ‰¦  «        z
  ¦  «        }t          t          ‰¦  «        t          ‰¦  «        z
  ¦  «        }t          t          ‰¦  «        t          ‰¦  «        z  ¦  «        }ˆfd„|D ¦   «         }ˆfd„|D ¦   «         }|||t          ‰¦  «        t          |¦  «        dœS )uÞ  Re-scan the skills directory and return a diff of what changed.

    Rescans ``~/.hermes/skills/`` and any ``skills.external_dirs`` so the
    slash-command map (``agent.skill_commands._skill_commands``) reflects
    skills added or removed on disk.

    This does NOT invalidate the skills system-prompt cache. Skills are
    called by name via ``/skill-name``, ``skills_list``, or ``skill_view``
    â€” they don't need to be in the system prompt for the model to use them.
    Keeping the prompt cache intact preserves prefix caching across the
    reload, so a user invoking ``/reload-skills`` pays no cache-reset cost.

    Returns:
        Dict with keys::

            {
              "added":      [{"name": str, "description": str}, ...],
              "removed":    [{"name": str, "description": str}, ...],
              "unchanged":  [skill names present before and after],
              "total":      total skill count after rescan,
              "commands":   total /slash-skill count after rescan,
            }

        ``description`` is the skill's full SKILL.md frontmatter
        ``description:`` field. Note: the system prompt skill index
        truncates this to the first 57 chars; see ``extract_skill_description``.
    Úcmdsr   c                 ó    — i }|                       ¦   «         D ]6\  }}|                     d¦  «        }|pi                      d¦  «        pd||<   Œ7|S )Nr,   r³   r+   )rk   Úlstripr\   )rÑ   ÚoutÚ	slash_keyÚinfoÚbares        r   Ú	_snapshotz reload_skills.<locals>._snapshot!  s\   € Ø ˆØ#Ÿzšz™|œ|ð 	>ð 	>‰OˆI�tØ×#Ò# CÑ(Ô(ˆDØ˜ ×(Ò(¨Ñ7Ô7Ð=¸2ˆC�‰IˆIØˆ
r   c                 ó&   •— g | ]}|‰|         d œ‘ŒS ©)r4   r³   r®   )r¯   ÚnÚafters     €r   ú
<listcomp>z!reload_skills.<locals>.<listcomp>4  s%   ø€ ÐGÐGÐG°a�a¨¨a¬Ð1Ð1ÐGÐGÐGr   c                 ó&   •— g | ]}|‰|         d œ‘ŒS rÚ   r®   )r¯   rÛ   Úbefores     €r   rÝ   z!reload_skills.<locals>.<listcomp>7  s%   ø€ ÐLÐLÐL¸˜¨&°¬)Ð4Ð4ÐLÐLÐLr   )ÚaddedÚremovedÚ	unchangedÚtotalÚcommands)r   r   r   r   rÍ   r�   r¹   r;   )	rØ   Únew_commandsÚadded_namesÚremoved_namesrâ   rà   rá   rÜ   rß   s	          @@r   Úreload_skillsrè     s+  øø€ ð@��S¥$¥s­C x¤.Ð0Ô1ð µd½3Å¸8´nð ð ð ð ð ˆY•Ñ'Ô'€Fõ 'Ñ(Ô(€LàˆI�lÑ#Ô#€Eå�˜U™œ¥c¨&¡k¤kÑ1Ñ2Ô2€KÝ�3˜v™;œ;­¨U©¬Ñ3Ñ4Ô4€MÝ•s˜5‘z”z¥C¨¡K¤KÑ/Ñ0Ô0€IàGÐGÐGÐG¸;ÐGÑGÔG€Eð MÐLÐLÐL¸mÐLÑLÔL€Gð ØØÝ�U‘”Ý˜Ñ%Ô%ðð ð r   Úcommandc                 ód   — | sdS d|                       dd¦  «        › �}|t          ¦   «         v r|ndS )uV  Resolve a user-typed /command to its canonical skill_cmds key.

    Skills are always stored with hyphens â€” ``scan_skill_commands`` normalizes
    spaces and underscores to hyphens when building the key. Hyphens and
    underscores are treated interchangeably in user input: this matches
    ``_check_unavailable_skill`` and accommodates Telegram bot-command names
    (which disallow hyphens, so ``/claude-code`` is registered as
    ``/claude_code`` and comes back in the underscored form).

    Returns the matching ``/slug`` key from ``get_skill_commands()`` or
    ``None`` if no match.
    Nr,   rn   r¶   )r¾   rÏ   )ré   rÌ   s     r   Úresolve_skill_command_keyrë   B  sG   € ð ð ØˆtØ-�'—/’/ # sÑ+Ô+Ð-Ð-€GØÕ!3Ñ!5Ô!5Ð5Ð5ˆ7ˆ7¸4Ð?r   rÌ   c                 ó  — t          ¦   «         }|                     | ¦  «        }|sdS t          |d         |¬¦  «        }|sdS |\  }}}		 ddlm}
  |
|	|¬¦  «         n# t
          $ r Y nw xY wd|	› d�}t          ||||||¬¦  «        S )	aE  Build the user message content for a skill slash command invocation.

    Args:
        cmd_key: The command key including leading slash (e.g., "/gif-search").
        user_instruction: Optional text the user typed after the command.

    Returns:
        The formatted message string, or None if the skill wasn't found.
    NrW   ©rO   r   ©Úbump_useú&[IMPORTANT: The user has invoked the "zf" skill, indicating they want you to follow its instructions. The full skill content is loaded below.])rw   rx   ry   )rÏ   r\   rd   Útools.skill_usagerï   rK   r    )rÌ   rw   rO   rx   rä   Ú
skill_infoÚloadedr`   rW   ra   rï   rv   s               r   Úbuild_skill_invocation_messagerô   U  sö   € õ "Ñ#Ô#€HØ—’˜gÑ&Ô&€JØð Øˆtå  ¨KÔ!8À'ÐJÑJÔJ€FØð Øˆtà*0Ñ'€L�)˜ZðØ.Ð.Ð.Ð.Ð.Ð.Øˆ� WÐ-Ñ-Ô-Ð-Ð-øÝð ð ð Øˆðøøøð	S°ð 	Sð 	Sð 	Sð õ  ØØØØ)Ø!Øðñ ô ð s   Á
A Á
A+Á*A+é   Úrestc                 óð  — g }| pd}t          |¦  «        t          dz
  k     rÀ|                     ¦   «         }|                     d¦  «        sn–|                     dd¦  «        }|d         }t          |¦  «        dk    r|d         nd}t          |                     d¦  «        ¦  «        }|�||v rn2|                     |¦  «         |}t          |¦  «        t          dz
  k     °À||                     ¦   «         fS )uÓ  Consume additional leading ``/skill`` tokens from *rest*.

    *rest* is the text that follows the FIRST matched skill command (the
    caller has already resolved that one). Leading whitespace-delimited
    tokens that start with ``/`` and resolve to installed skill commands are
    consumed, up to ``_MAX_STACKED_SKILLS`` total leading skills (i.e. at
    most ``_MAX_STACKED_SKILLS - 1`` extra keys here). Parsing stops at the
    first token that is not a resolvable skill command â€” that token and
    everything after it become the user instruction.

    Returns:
        ``(extra_cmd_keys, remaining_instruction)`` where ``extra_cmd_keys``
        are canonical ``/slug`` keys from :func:`get_skill_commands`.
    r+   r*   r,   Nr   )r;   Ú_MAX_STACKED_SKILLSrÓ   r    r2   rë   r   r1   )rö   ÚkeysÚ	remainingÚstrippedr   ÚtokenÚtailrÌ   s           r   Úsplit_stacked_skill_commandsrþ   •  sú   € ð €DØ�
˜€IÝ
ˆd‰)Œ)Õ)¨AÑ-Ò
-Ð
-Ø×#Ò#Ñ%Ô%ˆØ×"Ò" 3Ñ'Ô'ð 	ØØ—’˜t QÑ'Ô'ˆØ�a”ˆÝ˜u™:œ:¨š>˜>ˆu�QŒxˆx¨rˆÝ+¨E¯LªL¸Ñ,=Ô,=Ñ>Ô>ˆØˆ?˜g¨˜o˜oØØ�Š�GÑÔÐØˆ	õ ˆd‰)Œ)Õ)¨AÑ-Ò
-Ð
-ð �—’Ñ"Ô"Ð"Ð"r   Úcmd_keysc           	      óþ  — t          ¦   «         }g }g }g }t          ¦   «         }| D �]}|r||v rŒ
|                     |¦  «         |                     |¦  «        }	|	s)|                     |                     d¦  «        ¦  «         Œ_t          |	d         |¬¦  «        }
|
s)|                     |                     d¦  «        ¦  «         Œ¡|
\  }}}	 ddlm}  |||¬¦  «         n# t          $ r Y nw xY wd|› d�}|                     t          ||||¬¦  «        ¦  «         |                     |¦  «         �Œ|sd	S d
                     d„ | D ¦   «         ¦  «        }d|› dt          |¦  «        › d�ddd                     |¦  «        › �g}|r+|                     dd                     |¦  «        › �¦  «         |r|                     dd|› �g¦  «         d                     |¦  «        }d                     |g|¢¦  «        ||fS )ar  Build the user message for a stacked multi-skill slash invocation.

    Args:
        cmd_keys: Canonical ``/slug`` keys, in the order the user typed them.
        user_instruction: Text remaining after the leading skill commands.

    Returns:
        ``(message, loaded_skill_names, missing_skill_names)`` or ``None``
        when no skill could be loaded at all.
    r,   rW   rí   r   rî   z1[Loaded as part of the stacked skill invocation "z".]©ry   Nr-   c              3   ó   K  — | ]}|¯|V — Œ	d S ©Nr®   )r¯   Úks     r   r±   z9build_stacked_skill_invocation_message.<locals>.<genexpr>ö  s'   è è € Ð.Ð.˜1¨AÐ.�QÐ.Ð.Ð.Ð.Ð.Ð.r   rð   z " stacked skill bundle, loading zL skills together. Treat every skill below as active guidance for this turn.]r+   zSkills loaded: z, zSkills missing (skipped): zUser instruction: r   ú

)rÏ   r¹   r¼   r\   r   rÓ   rd   rñ   rï   rK   r    r   r;   rl   )rÿ   rw   rO   rä   Úloaded_namesÚmissingÚskill_blocksÚseenrÌ   rò   ró   r`   rW   ra   rï   rv   ÚtypedÚheader_linesÚheaders                      r   Ú&build_stacked_skill_invocation_messager  µ  s¦  € õ "Ñ#Ô#€Hà €LØ€GØ €LÝ‘U”U€Dàð $(ñ $(ˆØð 	˜' T˜/˜/ØØ�Š�ÑÔÐà—\’\ 'Ñ*Ô*ˆ
Øð 	Ø�NŠN˜7Ÿ>š>¨#Ñ.Ô.Ñ/Ô/Ð/Øå$ Z°Ô%<ÀgÐNÑNÔNˆØð 	Ø�NŠN˜7Ÿ>š>¨#Ñ.Ô.Ñ/Ô/Ð/ØØ.4Ñ+ˆ�i ð	Ø2Ð2Ð2Ð2Ð2Ð2ØˆH�Z¨Ð1Ñ1Ô1Ð1Ð1øÝð 	ð 	ð 	ØˆDð	øøøð PÀ
ÐOÐOÐOð 	ð 	×ÒÝ ØØØØ"ð	ñ ô ñ	
ô 	
ð 	
ð 	×Ò˜JÑ'Ô'Ð'Ñ'àð Øˆtð �HŠHÐ.Ð. Ð.Ñ.Ô.Ñ.Ô.€Eð	-°ð 	-ð 	-Ý�|Ñ$Ô$ð	-ð 	-ð 	-ð 	Ø3˜$Ÿ)š) LÑ1Ô1Ð3Ð3ð€Lð ð OØ×ÒÐM¸¿ºÀ7Ñ9KÔ9KÐMÐMÑNÔNÐNØð KØ×Ò˜RÐ!HÐ6FÐ!HÐ!HÐIÑJÔJÐJà�YŠY�|Ñ$Ô$€FØ�KŠK˜Ð/ ,Ð/Ñ0Ô0°,ÀÐHÐHs   ÃC!Ã!
C.Ã-C.Úskill_identifiersc           	      óŽ  — g }g }g }	 ddl m}  |¦   «         }n# t          $ r t          ¦   «         }Y nw xY wt          ¦   «         }| D ]ç}|pd                     ¦   «         }	|	r|	|v rŒ|                     |	¦  «         t          |	|¬¦  «        }
|
s|                     |	¦  «         Œ]|
\  }}}||v s|	|v r|                     |	¦  «         Œ�	 ddlm	}  |||¬¦  «         n# t          $ r Y nw xY wd|› d�}|                     t          ||||¬¦  «        ¦  «         |                     |¦  «         Œèd	                     |¦  «        ||fS )
uD  Load one or more skills for session-wide CLI/TUI preloading.

    Returns (prompt_text, loaded_skill_names, missing_identifiers).

    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). Without this, ``hermes -s <skill>`` or
    a deployment's ``HERMES_TUI_SKILLS`` env var could force-load a skill an
    operator disabled via ``skills.disabled``/``skills.platform_disabled``.
    r   )Úget_disabled_skill_namesr+   rí   rî   z9[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  )rY   r  rK   r¹   r1   r¼   rd   r   rñ   rï   r    r   )r  rO   Úprompt_partsr  r  r  Údisabled_namesr	  r^   Ú
identifierró   r`   rW   ra   rï   rv   s                   r   Úbuild_preloaded_skills_promptr    sò  € ð !€LØ €LØ€GðØ>Ð>Ð>Ð>Ð>Ð>Ø1Ð1Ñ3Ô3ˆˆøÝð ð ð Ý™œˆˆˆðøøøõ ‘U”U€DØ+ð %(ð %(ˆØ$Ð*¨×1Ò1Ñ3Ô3ˆ
Øð 	˜Z¨4Ð/Ð/ØØ�Š�ÑÔÐå$ Z¸ÐAÑAÔAˆØð 	Ø�NŠN˜:Ñ&Ô&Ð&Øà.4Ñ+ˆ�i à˜Ð'Ð'¨:¸Ð+GÐ+GØ�NŠN˜:Ñ&Ô&Ð&Øð	Ø2Ð2Ð2Ð2Ð2Ð2ØˆH�Z¨Ð1Ñ1Ô1Ð1Ð1øÝð 	ð 	ð 	ØˆDð	øøøð7È
ð 7ð 7ð 7ð 	ð
 	×ÒÝ ØØØØ"ð	ñ ô ñ	
ô 	
ð 	
ð 	×Ò˜JÑ'Ô'Ð'Ð'à�;Š;�|Ñ$Ô$ l°GÐ;Ð;s   ˆ ™4³4Ã	CÃ
C*Ã)C*)r'   r  )r+   r+   N)r+   Nr+   )r+   N)@Ú__doc__rZ   ÚloggingrI   ÚreÚpathlibr   Útypingr   r   r   Úhermes_constantsr   Úagent.prompt_cache_boundaryr   Úagent.skill_preprocessingr	   rŒ   r
   r‰   r   rŠ   Ú	getLoggerÚ__name__rÂ   r   r   Ú__annotations__r   Úcompiler¿   rÁ   r!   r$   r   r=   r"   rA   rB   Úescaper.   ÚSKILL_SCAFFOLD_SQL_LIKEr3   rŽ   r   r&   r6   r%   r#   rM   ÚtupleÚdictrd   ru   r    rÍ   rÏ   rè   rë   rô   rø   rþ   r  r  r®   r   r   ú<module>r%     s½  ððð ð ð €€€Ø €€€Ø 	€	€	€	Ø 	€	€	€	Ø Ð Ð Ð Ð Ð Ø &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &à 0Ð 0Ð 0Ð 0Ð 0Ð 0Ø >Ð >Ð >Ð >Ð >Ð >ðð ð ð ð ð ð ð ð ð ð 
ˆÔ	˜8Ñ	$Ô	$€à-/€��c˜4  S œ>Ð)Ô*Ð /Ð /Ñ /Ø*.Ð ˜( 3œ-Ð .Ð .Ñ .à!�r”z -Ñ0Ô0Ð Ø �b”j Ñ*Ô*Ð ð( CÐ ØAÐ àVð ð %€Ø!€Ø1Ð Ø9Ð ð �”˜I˜BœIÐ&>Ñ?Ô?À,ÑNÑOÔO€ð
 3°SÑ8Ð ð Ð ð 4ð °cð ¸cð ð ð ð ð"¸ð ÀÈ#Äð ð ð ð ð6$#ð $# sð $#°sð $#ÈÐRUÌð $#ð $#ð $#ð $#ðN°Cð ¸HÀS¼Mð ð ð ð ð¨cð °h¸s´mð ð ð ð ð%¨(°3¬-ð %ð %ð %ð %ð.%/ð %/¨#ð %/¸¸d¹
ð %/ÈeÐTXÐY\Ð^aÐYaÔTbÐdhÐkoÑdoÐqtÐTtÔNuÐx|ÑN|ð %/ð %/ð %/ð %/ðP$ t¨C°¨H¤~ð $¸dÀ3¼ið $ÈDð $ð $ð $ð $ðV ØØ!ðnð nØ�s˜C�x”.ðnà�d‰{ðnð ðnð ð	nð
 ðnð �d‘
ðnð 	ðnð nð nð nðb]˜T # t¨C°¨H¤~Ð"5Ô6ð ]ð ]ð ]ð ]ð@˜D  d¨3°¨8¤nÐ!4Ô5ð ð ð ð ð>�t˜C ˜H”~ð >ð >ð >ð >ðB@ sð @¨x¸¬}ð @ð @ð @ð @ð* ØØð	,ð ,Øð,àð,ð �4‰Zð,ð ð	,ð
 ˆc„]ð,ð ,ð ,ð ,ðz Ð ð# sð #¨u°T¸#´YÀ°^Ô/Dð #ð #ð #ð #ðD ØðOIð OIØ�3ŒiðOIàðOIð �4‰ZðOIð ˆe�C˜˜cœ D¨¤IÐ-Ô.Ô/ð	OIð OIð OIð OIðh ðA<ð A<Ø˜C”yðA<à�4‰ZðA<ð ˆ3��S”	˜4 œ9Ð$Ô%ðA<ð A<ð A<ð A<ð A<ð A<r   