
    iKJ                        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                 h   | pd                                 }|sdS 	 ddlm}m} t	          |                                          }|                                rZ	 t          |                                	                    |                                                    }n'# t          $ r |}Y nw xY w|                    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/F)r   
preprocesssuccessnamepath	skill_dir)striptools.skills_toolr   r   r   
expanduseris_absolutestrresolverelative_tor   lstripjsonloadsgetparent)r   r   raw_identifierr   r   identifier_path
normalizedloaded_skill
skill_name
skill_pathr$   abs_skill_dirs               r   _load_skill_payloadr8   5   s   &,"3355N t<<<<<<<<~..99;;&&(( 	4, !8!8!:!:!F!FzGYGYG[G[!\!\]]

 , , ,+


, (..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..sH   =C) AB!  C) !B0-C) /B008C) )
C76C7F F,+F,r4   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 = ])agent.skill_utilsr;   r<   r=   r)   r/   r   itemsappendextendr   )r4   r9   r;   r<   r=   r>   frontmatter_config_varsresolvedlineskeyvaluedisplay_vals                 r   _inject_skill_configrM   c   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   r?   r   template_varsTinline_shellFinline_shell_timeout
   z[Skill directory: r@   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)   r/   _load_skills_config_substitute_template_varsint_expand_inline_shellr%   rC   rM   rD   values
isinstancelistexistssortedrglobis_file
is_symlinkr+   
ValueErrorr"   join)r4   r$   rN   rO   rP   rQ   r   r?   
skills_cfgtimeoutr9   
supportingr[   entriessubdirsubdir_pathfrelskill_view_targetsfs                       r   _build_skill_messagerz      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} ddlm	}m
}  |            }t                      }g }|                                 r|                    |            |                     |                       |D ]}	 ||	d          D ]}
t          d |
j        D                       r"	 |
                    d          } ||          \  }} ||          sS|                    d|
j        j                  }||v rx||v r}|                    d	d
          }|sa|                                                    d          D ]9}|                                }|r!|                    d          s|dd         } n:|                    |           |                                                    dd                              dd          }t6                              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_get_disabled_skill_names)get_external_skills_dirsiter_skill_index_fileszSKILL.mdc              3      K   | ]}|d v V  	dS ))z.gitz.githubz.hubz.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   ra   #NP    -rF   zInvoke the z skill)r"   r   skill_md_pathr$   r   ) r   r   r   r&   r   r|   r}   r~   rA   r   r   setri   rC   rD   anyr9   	read_textr/   r0   r"   r%   split
startswithaddlowerreplace_SKILL_INVALID_CHARSsub_SKILL_MULTI_HYPHENr)   r   )r   r|   r}   r~   r   r   disabled
seen_namesdirs_to_scanscan_dirskill_mdr?   rE   bodyr"   r   linecmd_names                     r   scan_skill_commandsr      s"     @AAO5wwwwwwwwwwww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+>> ! &??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K&'	P    sg   B'I8 :/I$)I8 *$I$I8 I$I8 DI$)I8 +7I$"I8 $
I2.I8 1I22I8 8
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   3  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   )rB   r,   r/   )r   out	slash_keyinfobares        r   	_snapshotz reload_skills.<locals>._snapshotb  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>u  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>x  s%    LLL&)44LLLr   )addedremoved	unchangedtotalcommands)r   r)   r   r   r   rj   r   len)	r   new_commandsadded_namesremoved_namesr   r   r   r   r   s	          @@r   reload_skillsr   B  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   rF   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|d          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   z[Failed to load skill: r"   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.])rO   rP   rQ   )r   r/   r8   tools.skill_usager   r   rz   )r   rO   r   rP   r   
skill_infoloadedr4   r$   r5   r   rN   s               r   build_skill_invocation_messager     s    "##Hg&&J t K!8'JJJF ?>F);>>>>*0'L)Z......   	S 	S 	S 	S   )!   s   A& &
A32A3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.])rQ   z

)
r   r%   r   r8   rC   r   r   r   rz   ro   )r   r   prompt_partsloaded_namesmissingseenr1   
identifierr   r4   r$   r5   r   rN   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__r-   loggingr   repathlibr   typingr   r   r   hermes_constantsr   agent.skill_preprocessingr   re   r	   rb   r
   rc   	getLogger__name__loggerr   r)   __annotations__r   compiler   r   r   tupledictr8   rh   rM   rz   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- % % % %.+/ +/# +/d
 +/eTXY\^aYaTbdhkodoqtTtNux|N| +/ +/ +/ +/\$tCH~ $d3i $D $ $ $ $V !d dsCx.dd{d d 	d
 d d
d 	d d d dN?T#tCH~"56 ? ? ? ?D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   