
    #jN                        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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e
e         fdZd#dededz  dee eef         edz  ef         dz  fdZ!de eef         de"e         ddfdZ#	 	 	 d$de eef         dedz  dededededz  defdZ$de	ee	eef         f         fdZ%de	ee	eef         f         fdZ&de	eef         fdZ'dede
e         fdZ(	 	 	 d%dedededz  dede
e         f
d Z)	 d#d!e"e         dedz  deee"e         e"e         f         fd"Z*dS )&zShared 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)expand_inline_shellload_skills_configsubstitute_template_vars_skill_commands_skill_commands_platformz
[^a-z0-9-]z-{2,}returnc                      	 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_contextr   osgetenv	Exception)r   resolved_platforms     9/home/ubuntu/.hermes/hermes-agent/agent/skill_commands.py _resolve_skill_commands_platformr      s    9;;;;;; I'(( :899 	  9 9 9I&7889$$s   %( A	A	skill_identifiertask_idc                 T   | pd                                 }|sdS 	 ddlm}m} ddlm} t          |                                          }|                                rd}|g}	 |	                     |                       n# t          $ r Y nw xY w|D ]6}		 t          |                    |	                    } n# t          $ r Y 3w xY w|Z	 t          |                                                    |                                                    }n(# t          $ r |}Y nw xY wn|                    d          }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). Nr   )
SKILLS_DIR
skill_view)get_external_skills_dirs/F)r   
preprocesssuccessnamepath	skill_dir)striptools.skills_toolr   r   agent.skill_utilsr   r   
expanduseris_absoluteextendr   strrelative_to
ValueErrorresolvelstripjsonloadsgetparent)r   r   raw_identifierr   r   r   identifier_path
normalizedtrusted_rootsrootloaded_skill
skill_name
skill_pathr%   abs_skill_dirs                  r   _load_skill_payloadr>   5   s   &,"3355N t&<<<<<<<<>>>>>>~..99;;&&(( 	4J'LM$$%=%=%?%?@@@@    &  !$_%@%@%F%F!G!GJE!   H !0!$_%<%<%>%>%J%J:K]K]K_K_%`%`!a!aJJ  0 0 0!/JJJ0 " (..s33JzJz7uEEE
 
    tt I&& t\%%f--;<<J\%%f--344JI
 !$$[11M ''			 	"T*%5%5%<<II 	 	 	III	 J..s   AE %B E 
BE BE "B<:E <
C	E C		E AD E D%"E $D%%9E 
E-,E-;H H"!H"r:   partsc                    	 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_contentcontentr   Nz[Skill config (from z/config.yaml):z	(not set)z  z = ])r(   rA   rB   rC   r,   r3   r   itemsappendr+   r   )r:   r?   rA   rB   rC   rD   frontmatter_config_varsresolvedlineskeyvaluedisplay_vals                 r   _inject_skill_configrQ   y   s   	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 ,**=99^\=M=Mi=X=X^\^__ 	F**;77Q//<< 	F..{;; 	FQ,?,A,AQQQR"..** 	5 	5JC(->#e***;KLL3c33k334444SU   s%   AC= 	C= &C= 5BC= =
D
Dr   r%   activation_noteuser_instructionruntime_note
session_idc                    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|rdD ]}||z  }|                                rt!          |                    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           |r-|
	                    d           |
	                    d|            |r.|
	                    d           |
	                    d| d           d                    |
          S ) z9Format a loaded skill into a user/system message payload.r   )r   rE   r   template_varsTinline_shellFinline_shell_timeout
   z[Skill directory: rF   zResolve 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`).zPThe user has provided the following instruction alongside the skill invocation: z[Runtime note: 
)r'   r   r,   r3   _load_skills_config_substitute_template_varsint_expand_inline_shellr&   rH   rQ   r+   values
isinstancelistexistssortedrglobis_file
is_symlinkr-   r.   r#   join)r:   r%   rR   rS   rT   rU   r   rE   
skills_cfgtimeoutr?   
supportingr_   entriessubdirsubdir_pathfrelskill_view_targetsfs                       r   _build_skill_messager}      s    -,,,,,,""9--344G
 %&&J~~ot,, L+GY
KK~~ne,, Djnn%;R@@FBGG&w	7CCb'--//2E  
R6)666777>	
 	
 	
 u---(( 
 ^	
 	
 	
 	
 
		.	/	/ 
Kl3G&HKKK	
 	
 	
 	
 
		.	)	) 
l.>.>|.L.L 
Cl<&@CCC	
 	
 	
 J##N339rL&&(( ' 'gt$$ 	'g&&& /) /F 	/ 	/F#f,K!!## / 1 1# 6 677 / /Ayy{{ /1<<>> /!!--	":":;;"))#... 
i 
	/ #I$9$9*$E$E F F 	/ 	/ 	/ )	/ 	R9::: 	: 	:BLL8b88	B88999999J 9 9$9 9 9	
 	
 	
  |Rzhxzz{{{ 8R6|66677799Us   "K/ /LLc                  :   t                      ai a	 ddlm} m}m}m}m} ddl	m
}m}  |            }t                      }g }	|                                 r|	                    |            |	                     |                       |	D ]}
 ||
d          D ]}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          }t8                              d
|          }t<                              d|                              d          }|s||pd| dt?          |          t?          |j                  dt          d| <   # t@          $ r Y w xY wn# t@          $ r Y nw xY wt          S )zScan ~/.hermes/skills/ and return a mapping of /command -> skill info.

    Returns:
        Dict mapping "/skill-name" to {name, description, skill_md_path, skill_dir}.
    r   )r   _parse_frontmatterskill_matches_platformskill_matches_environment_get_disabled_skill_names)r   iter_skill_index_filesz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b4tFFbbbbbb    zutf-8)encodingr#   descriptionr   re   #NP    -rJ   zInvoke the z skill)r#   r   skill_md_pathr%   r    )!r   r   r   r'   r   r   r   r   r   r(   r   r   setrm   rH   r+   anyr?   	read_textr3   r4   r#   r&   split
startswithaddlowerreplace_SKILL_INVALID_CHARSsub_SKILL_MULTI_HYPHENr,   r   )r   r   r   r   r   r   r   disabled
seen_namesdirs_to_scanscan_dirskill_mdrE   rI   bodyr#   r   linecmd_names                      r   scan_skill_commandsr     sj     @AAO9 	S  	S  	S  	S  	S  	S  	S  	S  	S  	S  	S  	S  	S  	SVVVVVVVV,,..%%
  	,
+++4466777$ +	 +	H228ZHH * *bbS[Sabbbbb '&00'0BBG(:(:7(C(C%K11+>> !  54[AA ! &??68?3GHHDz)) x'' "-//-"D"DK& &$(JJLL$6$6t$<$< & &D#'::<<D# &DOOC,@,@ &.23B3i %NN4(((  $zz||33C==EEc3OOH377HEEH266sHEEKKCPPH# !  $'2'P6PD6P6P6P),X%(%9%9	7 7ONNN33 !   HS*+	X    ss   B)J </I2+J ,I27J 8$I2J I2!J "DI27J 97I20J 2
J <J ?J  J 
JJ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   r   r   r   r   r   get_skill_commandsr   M  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 — the same string the system prompt renders
        as ``    - name: description`` for pre-existing skills.
    cmdsr   c                     i }|                                  D ]6\  }}|                    d          }|pi                     d          pd||<   7|S )Nr    r   r   )rG   r0   r3   )r   out	slash_keyinfobares        r   	_snapshotz reload_skills.<locals>._snapshot|  s\     #zz|| 	> 	>OIt##C((D((77=2CII
r   c                 &    g | ]}||         d S )r#   r   r   )r   nafters     r   
<listcomp>z!reload_skills.<locals>.<listcomp>  s%    GGGaaa11GGGr   c                 &    g | ]}||         d S r   r   )r   r   befores     r   r   z!reload_skills.<locals>.<listcomp>  s%    LLL&)44LLLr   )addedremoved	unchangedtotalcommands)r   r,   r   r   r   rn   r   len)	r   new_commandsadded_namesremoved_namesr   r   r   r   r   s	          @@r   reload_skillsr   \  s+   @S$sCx.01 d38n     Y''F '((LIl##EUc&kk122K3v;;U344Ms5zzCKK/00IGGGG;GGGE MLLLmLLLG 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    rJ   r   )r   r   )r   cmd_keys     r   resolve_skill_command_keyr     sG      t-'//#s++--G!3!5!555774?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.
    Nr%   r   r   bump_usez&[IMPORTANT: The user has invoked the "zf" skill, indicating they want you to follow its instructions. The full skill content is loaded below.])rS   rT   rU   )r   r3   r>   tools.skill_usager   r   r}   )r   rS   r   rT   r   
skill_infoloadedr:   r%   r;   r   rR   s               r   build_skill_invocation_messager     s     "##Hg&&J t K!8'JJJF t*0'L)Z......   	S 	S 	S 	S   )!   s   
A 
A)(A)skill_identifiersc           	         g }g }g }t                      }| D ]}|pd                                }|r||v r|                    |           t          ||          }|s|                    |           ]|\  }	}
}	 ddlm}  ||           n# t          $ r Y nw xY wd| d}|                    t          |	|
||                     |                    |           d	                    |          ||fS )	zLoad one or more skills for session-wide CLI preloading.

    Returns (prompt_text, loaded_skill_names, missing_identifiers).
    r   r   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.])rU   z

)
r   r&   r   r>   rH   r   r   r   r}   rr   )r   r   prompt_partsloaded_namesmissingseenr5   
identifierr   r:   r%   r;   r   rR   s                 r   build_preloaded_skills_promptr     sr    !L LGUUD+ !( !($*1133
 	Z4//$ZAAA 	NN:&&&.4+i	222222HZ     	 	 	D	7
 7 7 7 	
 	 "	  	
 	
 	
 	J'''';;|$$lG;;s   ;B
BB)N)r   r   N)r   Nr   )+__doc__r1   loggingr   repathlibr   typingr   r   r   hermes_constantsr   agent.skill_preprocessingr   ri   r	   rf   r
   rg   	getLogger__name__loggerr   r,   __annotations__r   compiler   r   r   tupledictr>   rl   rQ   r}   r   r   r   r   r   r   r   r   r   <module>r      s       				 				       & & & & & & & & & & 0 0 0 0 0 0          
	8	$	$-/c4S>)* / / /*. (3- . . .!rz-00  bj** %(3- % % % %.A/ A/# A/d
 A/eTXY\^aYaTbdhkodoqtTtNux|N| A/ A/ A/ A/H$tCH~ $d3i $D $ $ $ $V !d dsCx.dd{d d 	d
 d d
d 	d d d dNCT#tCH~"56 C C C CLDd38n!45    >tCH~ > > > >B@s @x} @ @ @ @* 	, ,,, 4Z, 	,
 c], , , ,b 0< 0<Cy0<4Z0< 3S	49$%0< 0< 0< 0< 0< 0<r   