
    `gju                     d   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j6                  d	      Z ej6                  d
      ZdZdZdZ dZ!dZ"dZ#dZ$dede
e   fdZ%dede
e   fdZ&dede
e   fdZ'de
e   fdZ(d3d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-	 	 	 d4d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&Z0de	eef   fd'Z1d(ede
e   fd)Z2	 	 	 d5d*ed!ededz  d"ede
e   f
d+Z3d,Z4d-ede)e,e   ef   fd.Z5	 	 d6d/e,e   d!ededz  de
e)ee,e   e,e   f      fd0Z6	 d3d1e,e   dedz  de)ee,e   e,e   f   fd2Z7y)7zShared 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,}z%[IMPORTANT: The user has invoked the z(The full skill content is loaded below.]PThe 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 contentreturnc                     t        | t              sy| j                  t              s| S t        | v rt        |       S t        | v rt        |       S y)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    G/root/.hermes/venv/lib/python3.12/site-packages/agent/skill_commands.py+extract_user_instruction_from_skill_messager   :   sN     gs#67 /88w&5g>>    messagec                     | j                  t              }|dk  ry | |t        t              z   d  }|j                  t              }|dk\  r|d | }|j                         }|xs d S Nr   )rfind_SINGLE_SKILL_INSTRUCTIONlenfind_RUNTIME_NOTEstrip)r   
marker_idxinstructionruntime_idxs       r   r   r   U   sr     89JA~*s+D'EEFGK""=1Ka!,;/##%K$r   c                     | j                  t              }|dk  ry | |t        t              z   d  }|j                  t              }|dk\  r|d | }|j	                         }|xs d S r   )r"   _BUNDLE_USER_INSTRUCTIONr!   _BUNDLE_FIRST_SKILL_BLOCKr$   )r   r%   r&   first_skill_idxs       r   r   r   d   st     67JA~*s+C'DDEFK!&&'@AO!!"2?3##%K$r   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  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     r    _resolve_skill_commands_platformr5   s   sZ    9; II'( :89 	 $$  9II&789s   %- AA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 |      }t        |j                  d
      xs d      }	d}
|j                  d      }|rt        |      }
n|	r	 |t        |	      j                  z  }
||
|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). Nr   )
SKILLS_DIR
skill_view)normalize_skill_lookup_nameF)r7   
preprocesssuccessnamepath	skill_dir)r$   tools.skills_toolr:   r;   agent.skill_utilsr<   jsonloadsr3   getr   r   parent)r6   r7   raw_identifierr:   r;   r<   
normalizedloaded_skill
skill_name
skill_pathrA   abs_skill_dirs               r   _load_skill_payloadrN      s
   &,"335N
<A0@
zzz7uE
 I&\%%f-;<J\%%f-34JI
 !$$[1M'			"T*%5%<%<<I J..-  &  	I	s#   4C ?C+ 	C('C(+C98C9rJ   partsc                    	 ddl m}m}m} t	        | j                  d      xs | j                  d      xs d      }|sy ||      \  }} ||      }|sy ||      }	|	syddt                dg}
|	j                         D ]+  \  }}|rt	        |      nd	}|
j                  d
| d|        - |
j                  d       |j                  |
       y# t        $ r Y y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   r9   Nz[Skill config (from z/config.yaml):z	(not set)z  z = ])rC   rQ   rR   rS   r   rF   r   itemsappendextendr3   )rJ   rO   rQ   rR   rS   rT   frontmatter_config_varsresolvedlineskeyvaluedisplay_vals                 r   _inject_skill_configra      s    	
 	
 ,**=9^\=M=Mi=X^\^_*;7Q/<.{;+,?,A+B.QR"..* 	5JC(-#e*;KLL2cU#k]34	5 	SU s$   =C  C 
C !A0C 	CCrA   activation_noteuser_instructionruntime_note
session_idc                    ddl m} t        | j                  d      xs d      }t	               }|j                  dd      rt        |||      }|j                  dd      r,t        |j                  d	d
      xs d
      }	t        |||	      }|d|j                         g}
|r7|
j                  d       |
j                  d| d       |
j                  d       t        | |
       | j                  d      r|
j                  ddg       nh| j                  d      r|
j                  dd| d    dg       n<| j                  d      r+| j                  d      r|
j                  dd| d    dg       g }| j                  d      xs i }|j                         D ]$  }t        |t              s|j                  |       & |s|rdD ]  }||z  }|j                         st!        |j#                  d            D ]O  }|j%                         s|j'                         r%t        |j)                  |            }|j                  |       Q  |rx|rv	 t        |j)                  |            }|
j                  d       |
j                  d       |D ]  }|
j                  d| d||z           |
j                  d| d| d       |r%|
j                  d       |
j                  d|        |r&|
j                  d       |
j                  d| d       dj/                  |
      S # t*        $ r |j,                  }Y w xY w) z9Format a loaded skill into a user/system message payload.r   )r:   r   r9   template_varsTinline_shellFinline_shell_timeout
   z[Skill directory: rU   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`).r   z[Runtime note: 
)rB   r:   r   rF   _load_skills_config_substitute_template_varsint_expand_inline_shellr$   rW   ra   rX   valuesr   listexistssortedrglobis_file
is_symlinkrelative_to
ValueErrorr?   join)rJ   rA   rb   rc   rd   re   r:   r   
skills_cfgtimeoutrO   
supportingro   entriessubdirsubdir_pathfrelskill_view_targetsfs                       r   _build_skill_messager      sG    -,""9-34G
 %&J~~ot,+GY
K~~ne,jnn%;R@FBG&w	7Cb'--/2E R))A67>	
 u-( ^	
 
		.	/%l3G&H%IK	
 
		.	)l.>.>|.L%l<&@%AC	
 J##N39rL&&( 'gt$g&' )F 	/F#f,K!!# 1 1# 67 /Ayy{1<<>!!--	":;"))#./	/ i	/ #I$9$9*$E F 	R9: 	:BLL2bT	B'789	:89J8K L$+%79	
 Rghxgyz{R|nA6799U+  	/ )	/s   L& &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 }
| j                         r|
j!                  |        |
j#                   |              |
D ]  } ||d      D ]  }t%        d |j&                  D              r!	 |j)                  d      } ||      \  }} ||      sH ||      sQ|j+                  d	|j,                  j.                        }||	v r|||v r|j+                  d
d      }|sO|j1                         j3                  d      D ]-  }|j1                         }|s|j5                  d      r(|dd } n |	j7                  |       |j9                         j;                  dd      j;                  dd      }t<        j?                  d|      }t@        j?                  d|      j1                  d      }|sd ||      tB        jE                  d|||       d| }|t        v r$tB        jE                  d||t        |   d	          ||xs d| dtG        |      tG        |j,                        dt        |<     	 t        S # tH        $ r Y w xY w# tH        $ r Y t        S w xY w)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)get_external_skills_dirsiter_skill_index_files)resolve_commandzSKILL.mdc              3   $   K   | ]  }|d v  
 yw)>   .git.hub.github.archiveN ).0parts     r   	<genexpr>z&scan_skill_commands.<locals>.<genexpr>X  s     b4tFFbs   zutf-8)encodingr?   descriptionr9   ru   #NP    -rZ   zSkill %r generates slash command '/%s' which collides with a core Hermes command; skipping auto-registration. Use '/skill %s' instead./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_pathrA   )%r5   r   r   rB   r:   r   r   r   r   rC   r   r   hermes_cli.commandsr   setr|   rW   rX   anyrO   	read_textrF   rG   r?   r$   splitr   addlowerreplace_SKILL_INVALID_CHARSsub_SKILL_MULTI_HYPHENloggerwarningr   r3   )r:   r   r   r   r   r   r   r   disabled
seen_namesdirs_to_scanscan_dirskill_mdr   rY   bodyr?   r   linecmd_namecmd_keys                        r   scan_skill_commandsr   @  s     @AOS 	S  	SV7,.%
 
+467$ D	H28ZH CbS[SaSabb@&00'0BG(:7(C%K1+>  5[A &??68??3G3GHDz) x' "-//-"DK&$(JJL$6$6t$< &D#'::<D#DOOC,@.23Bi %	&
 NN4(  $zz|33C=EEc3OH377HEH266sHEKKCPH#  'x0<J !(D	 !
 !"(nG/1N '?7+CF+K
 ! $'2'PD66P),X%(%9	0OG,yCD	N 	 !  s   BJ	 %%I9
J	 I9J	 *I9>J	 ?I9J	 AI9I9BI9%J	 ' I9J	 	/I98J	 :2I9,J	 9	JJ	 JJ	 		JJc                  P    t         rt        t               k7  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   r5   r   r   r   r   get_skill_commandsr     s     #'G'IIr   c                     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        }|D cg c]
  }|||   d }}|D cg c]
  }|||   d }	}||	|t        |      t        |      dS c c}w c c}w )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 }| j                         D ]2  \  }}|j                  d      }|xs i j                  d      xs d||<   4 |S )Nr   r   r9   )rV   lstriprF   )r   out	slash_keyinfobares        r   	_snapshotz reload_skills.<locals>._snapshot  sT     #zz| 	>OIt##C(D((7=2CI	> 
r   )r?   r   )addedremoved	unchangedtotalcommands)r   r   r   r   r   r}   r   r!   )
r   beforenew_commandsafteradded_namesremoved_namesr   nr   r   s
             r   reload_skillsr     s    @S$sCx.01 d38n  'F '(Ll#EUc&k12K3v;U34Ms5zCK/0I;FGaaa1GEG ?LL&)4LGL U%  H Ms   :C:C?commandc                 R    | syd| j                  dd       }|t               v r|S 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   rZ   r   )r   r   )r   r   s     r   resolve_skill_command_keyr     s8     '//#s+,-G!3!557?4?r   r   c                     t               }|j                  |       }|syt        |d   |      }|sy|\  }}}		 ddlm}
  |
|	       d|	 d}t        ||||||      S # t
        $ r Y "w xY w)	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.
    NrA   r7   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.])rc   rd   re   )r   rF   rN   tools.skill_usager   r3   r   )r   rc   r7   rd   r   
skill_infoloadedrJ   rA   rK   r   rb   s               r   build_skill_invocation_messager     s     "#Hg&J K!8'JF*0'L)Z.
 1 =S 	S   )!   s   A 	A*)A*   restc                    g }| xs d}t        |      t        dz
  k  r|j                         }|j                  d      snv|j	                  dd      }|d   }t        |      dkD  r|d   nd}t        |j                  d            }|||v rn)|j                  |       |}t        |      t        dz
  k  r||j                         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`.
    r9      r   Nr   )r!   _MAX_STACKED_SKILLSr   r   r   r   rW   r$   )r   keys	remainingstrippedrO   tokentailr   s           r   split_stacked_skill_commandsr   C  s     D
I
d))A-
-##%""3'tQ'au:>uQxr+ELL,=>?goG	 d))A-
- """r   cmd_keysc           	      B   t               }g }g }g }t               }| D ]  }|r||v r
|j                  |       |j                  |      }	|	s!|j	                  |j                  d             Ot        |	d   |      }
|
s!|j	                  |j                  d             |
\  }}}	 ddlm}  ||       d| d}|j	                  t        ||||             |j	                  |        |sy	d
j                  d | D              }d| dt        |       ddddj                  |       g}|r#|j	                  ddj                  |              |r|j                  dd| g       dj                  |      }dj                  |g|      ||fS # t        $ r Y w xY w)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   rA   r   r   r   z1[Loaded as part of the stacked skill invocation "z".]re   Nr   c              3   &   K   | ]	  }|s|  y wNr   )r   ks     r   r   z9build_stacked_skill_invocation_message.<locals>.<genexpr>  s     .1AQ.s   r   z " stacked skill bundle, loading zL skills together. Treat every skill below as active guidance for this turn.]r9   zSkills loaded: z, zSkills missing (skipped): zUser instruction: ru   

)r   r   r   rF   rW   r   rN   r   r   r3   r   r   r!   rX   )r   rc   r7   r   loaded_namesmissingskill_blocksseenr   r   r   rJ   rA   rK   r   rb   typedheader_linesheaders                      r   &build_stacked_skill_invocation_messager   c  s    "#H LG LUD $('T/\\'*
NN7>>#./$Z%<gNNN7>>#./.4+i	2Z  @
|3O 	 	 "		
 	J'I$(L  HH...E
0 8|$% &-	- 	
$))L123L 879K8LMNR#56F5G!HIJYY|$FKK/,/0,HHK  		s   &F	FFskill_identifiersc           	      &   g }g }g }	 ddl m}  |       }t               }| D ]  }|xs dj	                         }	|	r|	|v r|j                  |	       t        |	|      }
|
s|j                  |	       P|
\  }}}||v s|	|v r|j                  |	       p	 ddlm	}  ||       d| d}|j                  t        ||||             |j                  |        d	j                  |      ||fS # t        $ r t               }Y w xY w# t        $ r Y ow xY w)
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_namesr9   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   )rC   r   r3   r   r$   r   rN   rW   r   r   r   r   )r   r7   prompt_partsr   r   r   disabled_namesr   rH   
identifierr   rJ   rA   rK   r   rb   s                   r   build_preloaded_skills_promptr    sS    !L LG>13 UD+ %($*113
Z4/$ZANN:&.4+i':+GNN:&	2Z 
 H
| T7 7 	
 	 "		
 	J'K%(N ;;|$lG;;W  2  		s#   C+ D+D D	DDr   )r9   r9   N)r9   Nr9   )r9   N)8__doc__rD   loggingr1   repathlibr   typingr   r   r   hermes_constantsr   agent.skill_preprocessingr   ry   r	   rv   r
   rw   	getLogger__name__r   r   r   __annotations__r   compiler   r   r   r   r    r#   r   r)   r*   r   r   r   r5   tupledictrN   r{   ra   r   r   r   r   r   r   r   r   r   r  r   r   r   <module>r     sj     	 	  & & 0  
		8	$-/c4S>)* /*. (3- .!rzz-0  bjj* ( C A V  %!1 9  # 6C HSM c hsm %(3- %.%/# %/d
 %/eTXY\^aYaTbdhkodoqtTtNux|N| %/P$tCH~ $d3i $D $V !dsCx.dd{d d 	d
 d d
d 	dN]T#tCH~"56 ]@Dd38n!45 >tCH~ >B@s @x} @* 	,,, 4Z, 	,
 c],z  #s #uT#Y^/D #D OI3iOIOI 4ZOI eCcDI-./	OIh A<CyA<4ZA< 3S	49$%A<r   