
    i@                         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
mZmZmZ ddlmZmZ  ej        e          Zdddd	Z ed
          ZdadefdZdedee	eef         ef         fdZde	eef         defdZd(dedz  dee         fdZdee         fdZde
e         fdZde
e         fdZ de	eef         de	ee
f         fdZ!de	eef         de
e	eef                  fdZ"de
e	eef                  fdZ#dZ$de	eef         defdZ%de
e	eef                  de	eef         fdZ&de	eef         defdZ'd ed!efd"Z( ej)        d#          Z*d$edeee         ef         fd%Z+d&ee         defd'Z,dS ))a  Lightweight skill metadata utilities shared by prompt_builder and skills_tool.

This module intentionally avoids importing the tool registry, CLI config, or any
heavy dependency chain.  It is safe to import at module level without triggering
tool registration or provider resolution.
    N)Path)AnyDictListOptionalSetTuple)get_config_pathget_skills_dirdarwinlinuxwin32)macosr   windows)z.gitz.githubz.hubz.archivecontentc                     t           ,ddlt          dd          pj        dt          ffd}|a t          |           S )z7Parse YAML with lazy import and CSafeLoader preference.Nr   CSafeLoadervaluec                 2                         |           S )N)Loader)load)r   loaderyamls    6/home/ubuntu/.hermes/hermes-agent/agent/skill_utils.py_loadzyaml_load.<locals>._load*   s    99U69222    )_yaml_load_fnr   getattr
SafeLoaderstr)r   r   r   r   s     @@r   	yaml_loadr!   "   sl     }d33Ft	3 	3 	3 	3 	3 	3 	3 	3 !!!r   returnc                 X   i }| }|                      d          s||fS t          j        d| dd                   }|s||fS | d|                                dz            }| |                                dz   d         }	 t          |          }t          |t                    r|}n# t          $ rt |	                                
                    d          D ]I}d|vr|
                    dd          \  }}|	                                ||	                                <   JY nw xY w||fS )zParse YAML frontmatter from a markdown string.

    Uses yaml with CSafeLoader for full YAML support (nested metadata, lists)
    with a fallback to simple key:value splitting for robustness.

    Returns:
        (frontmatter_dict, remaining_body)
    z---z
\n---\s*\n   N
:   )
startswithresearchstartendr!   
isinstancedict	Exceptionstripsplit)	r   frontmatterbody	end_matchyaml_contentparsedlinekeyr   s	            r   parse_frontmatterr9   4   s[    #%KDe$$ !D  	-55I !D  1y001445L9==??Q&(()D
5<((fd## 	! K 5 5 5 &&((..t44 	5 	5D$C++JC',{{}}K		$$		5 	55 s    &B' 'A;D%$D%r2   c                 V   |                      d          }|sdS t          |t                    s|g}t          j        }|D ]h}t          |                                                                          }t                               ||          }|	                    |          r dS idS )a  Return True when the skill is compatible with the current OS.

    Skills declare platform requirements via a top-level ``platforms`` list
    in their YAML frontmatter::

        platforms: [macos]          # macOS only
        platforms: [macos, linux]   # macOS and Linux

    If the field is absent or empty the skill is compatible with **all**
    platforms (backward-compatible default).
    	platformsTF)
getr-   listsysplatformr    lowerr0   PLATFORM_MAPr(   )r2   r;   currentr?   
normalizedmappeds         r   skill_matches_platformrE   \   s     ,,I ti&&  K	lG  ]]((**0022
!!*j99f%% 	44	5r   r?   c                    t                      }|                                st                      S 	 t          |                    d                    }nA# t
          $ r4}t                              d||           t                      cY d}~S d}~ww xY wt          |t                    st                      S |
                    d          }t          |t                    st                      S ddlm} | pt          j        d          p
 |d	          }|r;|
                    d
          pi 
                    |          }|t          |          S t          |
                    d                    S )a  Read disabled skill names from config.yaml.

    Args:
        platform: Explicit platform name (e.g. ``"telegram"``).  When
            *None*, resolves from ``HERMES_PLATFORM`` or
            ``HERMES_SESSION_PLATFORM`` env vars.  Falls back to the
            global disabled list when no platform is determined.

    Reads the config file directly (no CLI config imports) to stay
    lightweight.
    utf-8encodingz"Could not read skill config %s: %sNskillsr   )get_session_envHERMES_PLATFORMHERMES_SESSION_PLATFORMplatform_disableddisabled)r
   existssetr!   	read_textr/   loggerdebugr-   r.   r<   gateway.session_contextrK   osgetenv_normalize_string_set)r?   config_pathr6   e
skills_cfgrK   resolved_platformrN   s           r   get_disabled_skill_namesr]   y   s    "##K uu;00'0BBCC   9;JJJuu fd## uuH%%Jj$'' uu777777 	69&''	6?455 
  <'^^,?@@FBKK
 
 (():;;; 
!;!;<<<s   #A 
B )B	BBc                 j    | t                      S t          | t                    r| g} d | D             S )Nc                     h | ]D}t          |                                          #t          |                                          ES  )r    r0   ).0vs     r   	<setcomp>z(_normalize_string_set.<locals>.<setcomp>   s9    ===qc!ffllnn=CFFLLNN===r   )rQ   r-   r    )valuess    r   rX   rX      s=    ~uu&# ==F====r   c                     t                      } |                                 sg S 	 t          |                     d                    }n# t          $ r g cY S w xY wt          |t                    sg S |                    d          }t          |t                    sg S |                    d          }|sg S t          |t                    r|g}t          |t                    sg S ddl
m}  |            }t                                                      }t                      }g }|D ]}	t          |	                                          }	|	s't           j                            t           j                            |	                    }
t)          |
          }|                                s||z                                  }n|                                }||k    r||v r|                                r+|                    |           |                    |           t2                              d|           |S )a<  Read ``skills.external_dirs`` from config.yaml and return validated paths.

    Each entry is expanded (``~`` and ``${VAR}``) and resolved to an absolute
    path.  Only directories that actually exist are returned.  Duplicates and
    paths that resolve to the local ``~/.hermes/skills/`` are silently skipped.
    rG   rH   rJ   external_dirsr   )get_hermes_homez0External skills dir does not exist, skipping: %s)r
   rP   r!   rR   r/   r-   r.   r<   r    r=   hermes_constantsrg   r   resolverQ   r0   rV   path
expanduser
expandvarsr   is_absoluteis_diraddappendrS   rT   )rY   r6   r[   raw_dirsrg   hermes_homelocal_skillsseenresultentryexpandedps               r   get_external_skills_dirsry      sE    "##K 	;00'0BBCC   			fd## 	H%%Jj$'' 	~~o..H 	(C   :h%% 	000000!/##K!##++--LeeDF P PE

  "" 	7%%bg&8&8&?&?@@NN}} 	q))++AA		A9988:: 	PHHQKKKMM!LLKQOOOOMs   #A
 
AAc                  f    t                      g} |                     t                                 | S )u   Return all skill directories: local ``~/.hermes/skills/`` first, then external.

    The local dir is always first (and always included even if it doesn't exist
    yet — callers handle that).  External dirs follow in config order.
    )r   extendry   )dirss    r   get_all_skills_dirsr}      s0     DKK(**+++Kr   c                 d   |                      d          }t          |t                    si }|                     d          pi }t          |t                    si }|                     dg           |                     dg           |                     dg           |                     dg           dS )z>Extract conditional activation fields from parsed frontmatter.metadatahermesfallback_for_toolsetsrequires_toolsetsfallback_for_toolsrequires_tools)r   r   r   r   )r<   r-   r.   )r2   r   r   s      r   extract_skill_conditionsr      s    z**Hh%% \\(##)rFfd## !',CR!H!H#ZZ(;R@@$jj)=rBB **%5r::	  r   c                    |                      d          }t          |t                    sg S |                     d          }t          |t                    sg S |                     d          }|sg S t          |t                    r|g}t          |t                    sg S g }t	                      }|D ]3}t          |t                    st          |                     dd                                                    }|r||v rUt          |                     dd                                                    }|s||d}	|                     d          }
|
|
|	d<   |                     d
          }t          |t
                    r,|                                r|                                |	d
<   n||	d
<   |                    |           |                    |	           5|S )a   Extract config variable declarations from parsed frontmatter.

    Skills declare config.yaml settings they need via::

        metadata:
          hermes:
            config:
              - key: wiki.path
                description: Path to the LLM Wiki knowledge base directory
                default: "~/wiki"
                prompt: Wiki directory path

    Returns a list of dicts with keys: ``key``, ``description``, ``default``,
    ``prompt``.  Invalid or incomplete entries are silently skipped.
    r   r   configr8    description)r8   r   defaultNprompt)	r<   r-   r.   r=   rQ   r    r0   ro   rp   )r2   r   r   rawru   rt   itemr8   descrv   r   prompt_texts               r   extract_skill_config_varsr     s     z**Hh%% 	\\(##Ffd## 	
**X

C 	#t ec4   	#%FD  $%% 	$((5"%%&&,,.. 	cTkk488M2..//5577 	!
 !
 ((9%%&E)hhx((k3'' 	#K,=,=,?,? 	#)//11E(OO"E(OeMr   c                  r   g } t                      }t                      }t                      D ]}|                                st	          |d          D ]}	 |                    d          }t          |          \  }}n# t          $ r Y 8w xY w|                    d          p|j	        j
        }t          |          |v rot          |          st          |          }	|	D ]N}
|
d         |vrBt          |          |
d<   |                     |
           |                    |
d                    O
| S )aY  Scan all enabled skills and collect their config variable declarations.

    Walks every skills directory, parses each SKILL.md frontmatter, and returns
    a deduplicated list of config var dicts.  Each dict also includes a
    ``skill`` key with the skill name for attribution.

    Disabled and platform-incompatible skills are excluded.
    zSKILL.mdrG   rH   namer8   skill)rQ   r]   r}   rn   iter_skill_index_filesrR   r9   r/   r<   parentr   r    rE   r   rp   ro   )all_vars	seen_keysrO   
skills_dir
skill_filer   r2   _
skill_nameconfig_varsvars              r   discover_all_skill_config_varsr   H  si    &(HUUI'))H)++ . .
  "" 	0ZHH 	. 	.J **G*<<!23!7!7QQ    %00JJ4E4JJ:(**)+66 3K@@K" . .u:Y..#&z??CLOOC(((MM#e*---	.	.( Os   (B
BBzskills.configr   
dotted_keyc                     |                     d          }| }|D ]&}t          |t                    r||v r	||         }$ dS |S )zPWalk a nested dict following a dotted key.  Returns None if any part is missing..N)r1   r-   r.   )r   r   partsrB   parts        r   _resolve_dotpathr   u  sZ    S!!EG  gt$$ 	dmGG44Nr   r   c                 z   t                      }i }|                                rL	 t          |                    d                    }t	          |t
                    r|}n# t          $ r Y nw xY wi }| D ]}|d         }t           d| }t          ||          }|)t	          |t                    r*|
                                s|                    dd          }t	          |t                    rDd|v sd	|v r<t          j                            t          j                            |                    }|||<   |S )
aV  Resolve current values for skill config vars from config.yaml.

    Skill config is stored under ``skills.config.<key>`` in config.yaml.
    Returns a dict mapping **logical** keys (as declared by skills) to their
    current values (or the declared default if the key isn't set).
    Path values are expanded via ``os.path.expanduser``.
    rG   rH   r8   r   Nr   r   ~z${)r
   rP   r!   rR   r-   r.   r/   SKILL_CONFIG_PREFIXr   r    r0   r<   rV   rj   rk   rl   )	r   rY   r   r6   resolvedr   logical_keystorage_keyr   s	            r   resolve_skill_config_valuesr     sR    "##KF 	{44g4FFGGF&$''   	 	 	D	  "H & &%j,<<{<< 55=Zs33=EKKMM=GGIr**E eS!! 	Bse||tu}}G&&rw'9'9%'@'@AAE %Os   :A! !
A.-A.c                     |                      dd          }|sdS t          |                                                              d          }t          |          dk    r|dd         dz   S |S )z8Extract a truncated description from parsed frontmatter.r   r   z'"<   N9   z...)r<   r    r0   len)r2   raw_descr   s      r   extract_skill_descriptionr     sn    }b11H rx==  &&u--D
4yy2~~CRCy5  Kr   r   filenamec              #       K   g }t          j         d          D ]@\  }}}d |D             |dd<   ||v r%|                    t          |          |z             At	          | fd          D ]}|V  dS )zWalk skills_dir yielding sorted paths matching *filename*.

    Excludes ``.git``, ``.github``, ``.hub``, ``.archive`` directories.
    T)followlinksc                 $    g | ]}|t           v|S r`   )EXCLUDED_SKILL_DIRS)ra   ds     r   
<listcomp>z*iter_skill_index_files.<locals>.<listcomp>  s#    CCCa/B&B&B1&B&B&Br   Nc                 H    t          |                                         S N)r    relative_to)rx   r   s    r   <lambda>z(iter_skill_index_files.<locals>.<lambda>  s    c!--
2K2K.L.L r   )r8   )rV   walkrp   r   sorted)r   r   matchesrootr|   filesrj   s   `      r   r   r     s      
 GWZTBBB 2 2dECCdCCCQQQuNN4::0111w$L$L$L$LMMM  



 r   z^[a-zA-Z0-9_-]+$r   c                 X    d| vrd| fS t          |                     dd                    S )z~Split ``'namespace:skill-name'`` into ``(namespace, bare_name)``.

    Returns ``(None, name)`` when there is no ``':'``.
    r&   Nr'   )tupler1   )r   s    r   parse_qualified_namer     s3    
 $TzC##$$$r   	candidatec                 X    | sdS t          t                              |                     S )zDCheck whether *candidate* is a valid namespace (``[a-zA-Z0-9_-]+``).F)bool_NAMESPACE_REmatch)r   s    r   is_valid_namespacer     s+     u##I..///r   r   )-__doc__loggingrV   r)   r>   pathlibr   typingr   r   r   r   r   r	   rh   r
   r   	getLogger__name__rS   rA   	frozensetr   r   r    r!   r9   r   rE   r]   rX   ry   r}   r   r   r   r   r   r   r   r   compiler   r   r   r`   r   r   <module>r      s     				 				 



       8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 < < < < < < < <		8	$	$
    i GHH  "s " " " "$"s "uT#s(^S-@'A " " " "PS#X 4    :'= '=sTz '=SX '= '= '= '=T>SX > > > >:$t* : : : :zT$Z    $sCx. T#t)_    (84S> 8d4S>>R 8 8 8 8v$T#s(^(< $ $ $ $T & 	T#s(^ 	 	 	 	 	#d38n%#	#s(^# # # #R4S> c    t s      
.//%s %uXc]C-?'@ % % % %0(3- 0D 0 0 0 0 0 0r   