
    i                        d Z ddlZddl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 ddlmZmZmZmZ ddlmZmZ ddlmZ  ej        e          Z	 ddlmZmZmZ d	Zn# e$ r d
ZY nw xY wdefdZ dedee!         fdZ"ddl#Z# e
            Z$e$dz  Z%dZ&dZ'dedefdZ(de!dee!         fdZ)dZ*dZ+ ej,        d          Z-h dZ.de!dee!         fdZ/dee!         dee!         fdZ0de!dee!         fdZ1dQde!d e!dee!         fd!Z2dRde!de!defd"Z3de!deee!ef                  fd#Z4d$e!dee!         fd%Z5ded$e!deee         ee!         f         fd&Z6dSd$ede!d(e!ddfd)Z7dRde!de!de!dee!ef         fd*Z8de!de!dee!ef         fd+Z9	 	 dTde!d,e!d-e!d$e!d.edee!ef         fd/Z:dRde!d0ee!         dee!ef         fd1Z;de!d$e!d2e!dee!ef         fd3Z<de!d$e!dee!ef         fd4Z=	 	 	 	 	 	 	 	 dUd5e!de!de!de!d$e!d2e!d,e!d-e!d.ed0e!de!fd6Z>d7d8 e             d9d:d;g d<d=d>d;d?d@d;dAd@d;dBd@d;dCd@dDdEd@d;dFd@d;dGd@d;dHd@d;dId@dJ
d5dgdKdLZ?ddMl@mAZAmBZB  eAjC        d7de?dN dOP           dS )Vu  
Skill Manager Tool -- Agent-Managed Skill Creation & Editing

Allows the agent to create, update, and delete skills, turning successful
approaches into reusable procedural knowledge. New skills are created in
~/.hermes/skills/. Existing skills (bundled, hub-installed, or user-created)
can be modified or deleted wherever they live.

Skills are the agent's procedural memory: they capture *how to do a specific
type of task* based on proven experience. General memory (MEMORY.md, USER.md) is
broad and declarative. Skills are narrow and actionable.

Actions:
  create     -- Create a new skill (SKILL.md + directory structure)
  edit       -- Replace the SKILL.md content of a user skill (full rewrite)
  patch      -- Targeted find-and-replace within SKILL.md or any supporting file
  delete     -- Remove a user skill entirely
  write_file -- Add/overwrite a supporting file (reference, template, script, asset)
  remove_file-- Remove a supporting file from a user skill

Directory layout for user skills:
    ~/.hermes/skills/
    ├── my-skill/
    │   ├── SKILL.md
    │   ├── references/
    │   ├── templates/
    │   ├── scripts/
    │   └── assets/
    └── category-name/
        └── another-skill/
            └── SKILL.md
    N)Path)get_hermes_homedisplay_hermes_home)DictAnyOptionalTuple)atomic_replaceis_truthy_value)cfg_get)
scan_skillshould_allow_installformat_scan_reportTFreturnc                      	 ddl m}   |             }t          t          |dd          d          S # t          $ r Y dS w xY w)a[  Read skills.guard_agent_created from config (default False).

    Off by default because the agent can already execute the same code
    paths via terminal() with no gate, so the scan adds friction without
    meaningful security.  Users who want belt-and-suspenders can turn it
    on via `hermes config set skills.guard_agent_created true`.
    r   )load_configskillsguard_agent_createdF)default)hermes_cli.configr   r   r   	Exception)r   cfgs     =/home/ubuntu/.hermes/hermes-agent/tools/skill_manager_tool.py_guard_agent_created_enabledr   ;   sr    111111kmmC#899
 
 
 	
    uus   /2 
A A 	skill_dirc                    t           sdS t                      sdS 	 t          | d          }t          |          \  }}|du rt	          |          }d| d| S |2t	          |          }t
                              d|           d| d| S n5# t          $ r(}t
                              d| |d	
           Y d}~nd}~ww xY wdS )zScan a skill directory after write. Returns error string if blocked, else None.

    No-op when skills.guard_agent_created is disabled (the default).
    Nzagent-created)sourceFz"Security scan blocked this skill (z):
z4Agent-created skill blocked (dangerous findings): %szSecurity scan failed for %s: %sTexc_info)_GUARD_AVAILABLEr   r   r   r   loggerwarningr   )r   resultallowedreasonreportes         r   _security_scan_skillr(   N   s   
  t')) tWIo>>>.v66e'//FLLLFLLL? (//FNNQSYZZZLLLFLLL   W W W8)QQUVVVVVVVVW4s   =B 3B 
C B;;C r   @   i   
skill_pathc                 
   ddl m} 	 |                                 }n# t          $ r | }Y nw xY w |            D ]D}	 |                    |                                           |c S # t
          t          f$ r Y Aw xY wt          S )u   Return the skills root directory (local or external_dirs entry) that
    contains ``skill_path``.  Falls back to the local ``SKILLS_DIR`` if no
    match is found (defensive — callers should have located the skill via
    ``_find_skill`` first).
    r   get_all_skills_dirs)agent.skill_utilsr-   resolveOSErrorrelative_to
ValueError
SKILLS_DIR)r*   r-   resolvedroots       r   _containing_skills_rootr6   s   s     655555%%''    $#%%  	  000KKKG$ 	 	 	H	s    ,,(A''A;:A;namec                     	 ddl m} |                    |           }|                    d          r	d|  d|  dS n-# t          $ r  t
                              d| d	           Y nw xY wd
S )a.  Return a refusal message if *name* is pinned, else None.

    Pinned skills are off-limits to the agent's skill_manage tool.  The only
    way to modify one is for the user to unpin it via
    ``hermes curator unpin <name>`` (or edit it directly by hand).  This
    mirrors the curator's own pinned-skip behavior but extends the guard
    to tool-driven writes as well, giving users a hard fence against
    accidental agent edits.

    Best-effort: if the sidecar is unreadable we let the write through
    rather than block on a broken telemetry file.
    r   )skill_usagepinnedSkill 'z^' is pinned and cannot be modified by skill_manage. Ask the user to run `hermes curator unpin z` if they want the change.z!pinned-guard lookup failed for %sTr   N)toolsr9   
get_recordgetr   r!   debug)r7   r9   recs      r   _pinned_guardrA      s    
O%%%%%%$$T**778 	J$ J J)-J J J	  O O O8$NNNNNO4s   8< 'A&%A&i i   z^[a-z0-9][a-z0-9._-]*$>   assetsscripts	templates
referencesc                     | sdS t          |           t          k    rdt           dS t                              |           sd|  dS dS )z>Validate a skill name. Returns error message or None if valid.zSkill name is required.zSkill name exceeds  characters.zInvalid skill name 'ze'. Use lowercase letters, numbers, hyphens, dots, and underscores. Must start with a letter or digit.N)lenMAX_NAME_LENGTHVALID_NAME_REmatch)r7   s    r   _validate_namerL      sn     )((
4yy?""B_BBBBt$$ 
R4 R R R	
 4    categoryc                    | dS t          | t                    sdS |                                 } | sdS d| v sd| v rd|  dS t          |           t          k    rdt           dS t
                              |           sd|  dS dS )	zFValidate an optional category name used as a single directory segment.NzCategory must be a string./\zInvalid category 'zn'. Use lowercase letters, numbers, hyphens, dots, and underscores. Categories must be a single directory name.zCategory exceeds rG   )
isinstancestrstriprH   rI   rJ   rK   )rN   s    r   _validate_categoryrU      s    th$$ ,++~~H t
h$(**Z Z Z Z	
 8}}&&@?@@@@x(( 
Z Z Z Z	
 4rM   contentc                 j   |                                  sdS |                     d          sdS t          j        d| dd                   }|sdS | d|                                dz            }	 t          j        |          }n!# t
          j        $ r}d| cY d}~S d}~ww xY wt          |t                    sd	S d
|vrdS d|vrdS t          t          |d                             t          k    rdt           dS | |                                dz   d                                          }|sdS dS )z
    Validate that SKILL.md content has proper frontmatter with required fields.
    Returns error message or None if valid.
    zContent cannot be empty.z---zPSKILL.md must start with YAML frontmatter (---). See existing skills for format.z
\n---\s*\n   NzISKILL.md frontmatter is not closed. Ensure you have a closing '---' line.zYAML frontmatter parse error: z6Frontmatter must be a YAML mapping (key: value pairs).r7   z&Frontmatter must include 'name' field.descriptionz-Frontmatter must include 'description' field.zDescription exceeds rG   zRSKILL.md must have content after the frontmatter (instructions, procedures, etc.).)rT   
startswithresearchstartyaml	safe_load	YAMLErrorrR   dictrH   rS   MAX_DESCRIPTION_LENGTHend)rV   	end_matchyaml_contentparsedr'   bodys         r   _validate_frontmatterrh      s~   
 ==?? *))e$$ baa	-55I [ZZ1Y__..223L4--> 4 4 43333333334 fd## HGGV77F"">>
3vm$%%&&)???J&<JJJJ9==??Q&''(..00D dcc4s   /B B"BB"B"SKILL.mdlabelc                 t    t          |           t          k    r| dt          |           ddt          ddS dS )zCheck that content doesn't exceed the character limit for agent writes.

    Returns an error message or None if within bounds.
    z content is ,z characters (limit: za). Consider splitting into a smaller SKILL.md with supporting files in references/ or templates/.N)rH   MAX_SKILL_CONTENT_CHARS)rV   rj   s     r   _validate_content_sizern      sY    
 7||--- - -#g,,> - -.5- - -	
 4rM   c                 4    |rt           |z  | z  S t           | z  S )zFBuild the directory path for a new skill, optionally under a category.)r3   )r7   rN   s     r   _resolve_skill_dirrp     s%     ,H$t++rM   c                     ddl m}  |            D ]L}|                                s|                    d          D ]}|j        j        | k    rd|j        ic c S  MdS )z
    Find a skill by name across all skill directories.

    Searches the local skills dir (~/.hermes/skills/) first, then any
    external dirs configured via skills.external_dirs.  Returns
    {"path": Path} or None.
    r   r,   ri   pathN)r.   r-   existsrglobparentr7   )r7   r-   
skills_dirskill_mds       r   _find_skillrx     s     655555))++ 1 1
  "" 	"((44 	1 	1H#t++000000 ,	1 4rM   	file_pathc                 :   ddl m} | sdS t          |           } ||           rdS |j        r|j        d         t          vr0d                    t          t                              }d| d|  dS t          |j                  d	k     rd
|j        d          dS dS )z
    Validate a file path for write_file/remove_file.
    Must be under an allowed subdirectory and not escape the skill dir.
    r   )has_traversal_componentzfile_path is required.z%Path traversal ('..') is not allowed.z, zFile must be under one of: z. Got: ''   z5Provide a file path, not just a directory. Example: 'z/myfile.md'N)tools.path_securityr{   r   partsALLOWED_SUBDIRSjoinsortedrH   )ry   r{   
normalizedr$   s       r   _validate_file_pathr   (  s    
 <;;;;; (''iJ y)) 766  Kz/2/II))F?3344JWJJiJJJJ :q  gzGWXYGZgggg4rM   c                 D    ddl m} | |z  } |||           }|rd|fS |dfS )zNResolve a supporting-file path and ensure it stays within the skill directory.r   )validate_within_dirN)r~   r   )r   ry   r   targeterrors        r   _resolve_skill_targetr   D  sL    777777"F	22E U{4<rM   utf-8encodingc                    | j                             dd           t          j        t	          | j                   d| j         dd          \  }}	 t          j        |d|          5 }|                    |           d	d	d	           n# 1 swxY w Y   t          ||            d	S # t          $ rE 	 t          j        |           n-# t          $ r  t                              d
|d           Y nw xY w w xY w)au  
    Atomically write text content to a file.
    
    Uses a temporary file in the same directory and os.replace() to ensure
    the target file is never left in a partially-written state if the process
    crashes or is interrupted.
    
    Args:
        file_path: Target file path
        content: Content to write
        encoding: Text encoding (default: utf-8)
    Tparentsexist_ok.z.tmp. )dirprefixsuffixwr   Nz6Failed to remove temporary file %s during atomic writer   )ru   mkdirtempfilemkstemprS   r7   osfdopenwriter
   r   unlinkr0   r!   r   )ry   rV   r   fd	temp_pathfs         r   _atomic_write_textr   O  si    4$777$	 !!(9>(((  MB	

Yr3222 	aGGG	 	 	 	 	 	 	 	 	 	 	 	 	 	 	y),,,,,   	mIi     	m 	m 	mLLQS\gkLlllll	msT   B) *B B) BB) BB) )
C84C	C8	'C30C82C33C8c                    t          |           }|rd|dS t          |          }|rd|dS t          |          }|rd|dS t          |          }|rd|dS t	          |           }|rdd|  d|d          ddS t          | |          }|                    dd           |d	z  }t          ||           t          |          }|rt          j
        |d
           d|dS dd|  dt          |                    t                              t          |          d}|r||d<   d                    |           |d<   |S )z.Create a new user skill with SKILL.md content.Fsuccessr   zA skill named 'z' already exists at rr   r   Tr   ri   )ignore_errorsr;   z
' created.)r   messagerr   rw   rN   zTo add reference files, templates, or scripts, use skill_manage(action='write_file', name='{}', file_path='references/example.md', file_content='...')hint)rL   rU   rh   rn   rx   rp   r   r   r(   shutilrmtreerS   r1   r3   format)	r7   rV   rN   errexistingr   rw   
scan_errorr#   s	            r   _create_skillr   s  s    

C
 0 3///
X
&
&C
 0 3///  
(
(C
 0 3///
 
)
)C
 0 3/// 4  H 
TtTT&AQTTT
 
 	
 #422IOOD4O000 :%Hx))) &i00J 7it4444 :666 -T---I))*5566MM	 F  &%z	nntntuynznz 6N MrM   c                    t          |          }|rd|dS t          |          }|rd|dS t          |           }|s	dd|  ddS t          |           }|rd|dS |d         dz  }|                                r|                    d          nd	}t          ||           t          |d                   }|r|t          ||           d|dS d
d|  dt          |d                   dS )z:Replace the SKILL.md of any existing skill (full rewrite).Fr   r;   z7' not found. Use skills_list() to see available skills.rr   ri   r   r   NTz
' updated.r   r   rr   )	rh   rn   rx   rA   rs   	read_textr   r(   rS   )r7   rV   r   r   
pinned_errrw   original_contentr   s           r   _edit_skillr     sY   

(
(C
 0 3///
 
)
)C
 0 3///4  H t +rT+r+r+rssst$$J 7 :666*,H?G?P?PZx))7);;;VZx))) &hv&677J 7'x)9::: :666 -T---HV$%%  rM   
old_string
new_stringreplace_allc           
         |sdddS |dddS t          |           }|s	dd|  ddS t          |           }|rd|dS |d         }|r1t          |          }|rd|dS t          ||          \  }	}|rd|dS n|d	z  }	|	                                sdd
|	                    |           dS |	                    d          }
ddlm}  ||
|||          \  }}}}|rS|
dd         t          |
          dk    rdndz   }|}	 ddlm
} | |||||
          z  }n# t          $ r Y nw xY wd||dS |sd	n|}t          ||          }|rd|dS |st          |          }|rdd| dS |
}t          |	|           t          |          }|rt          |	|           d|dS dd|sd	n| d|  d| d|dk    rdnd d	dS )zTargeted find-and-replace within a skill file.

    Defaults to SKILL.md. Use file_path to patch a supporting file instead.
    Requires a unique match unless replace_all is True.
    Fz#old_string is required for 'patch'.r   NzOnew_string is required for 'patch'. Use an empty string to delete matched text.r;   ' not found.rr   ri   zFile not found: r   r   r   )fuzzy_find_and_replacei  z...r   )format_no_match_hint)r   r   file_previewrj   z&Patch would break SKILL.md structure: TzPatched z in skill 'z' (z replacement   sz).r   r   )rx   rA   r   r   rs   r1   r   tools.fuzzy_matchr   rH   r   r   rn   rh   r   r(   )r7   r   r   ry   r   r   r   r   r   r   rV   r   new_contentmatch_count	_strategymatch_errorpreviewerr_msgr   target_labelr   r   s                         r   _patch_skillr     si     R +PQQQ +|}}}4  H I +GT+G+G+GHHHt$$J 7 :666 I 
(!),, 	4$s333+IyAA 	4$s333	4 Z'==?? _ +]f>P>PQZ>[>[+]+]^^^00G 9888887M7MZ8 84Ki  
$3$-CLL3,>,>55BG	>>>>>>++KjRYZZZGG 	 	 	D	 #
 
 	
 &/=::IL
 L
A
A
AC
 0 3///  #K00 	 G#GG  
 v{+++ &i00J 76#3444 :666  Z	Hjjy  Z  ZUY  Z  Z^i  Z  Z  J  MN  N  Nwzwz  TV  Z  Z  Z  s   D 
D)(D)absorbed_intoc                    t          |           }|s	dd|  ddS t          |           }|rd|dS |ft          |t                    rQ|                                r=|                                }|| k    r	dd| ddS t          |          }|s	dd| ddS |d	         }t          |          }t          j        |           |j        }||k    rI|	                                r5t          |                                          s|                                 d|  d
}	|Dt          |t                    r/|                                r|	d|                                 dz  }	d|	dS )u<  Delete a skill.

    ``absorbed_into`` declares intent:
      - ``None`` / missing  → caller didn't declare (legacy / non-curator path);
        accepted for backward compat but logs a warning because the curator
        classification pipeline can't tell consolidation from pruning without it.
      - ``""`` (empty)      → explicit "truly pruned, no forwarding target".
      - ``"<skill-name>"``  → content was absorbed into that umbrella; the
        target must exist on disk. Validated here so the model can't claim an
        umbrella that doesn't exist.
    Fr;   r   r   Nzabsorbed_into='z'' cannot equal the skill being deleted.zR' does not exist. Create or patch the umbrella skill first, then retry the delete.rr   z
' deleted.z Content absorbed into ''.Tr   )rx   rA   rR   rS   rT   r6   r   r   ru   rs   anyiterdirrmdir)
r7   r   r   r   target_namer   r   skills_rootru   r   s
             r   _delete_skillr   3  s    4  H I +GT+G+G+GHHHt$$J 7 :666  Zs%C%C H[H[H]H] #))++$ _;___   [)) 	 Xk X X X    I))44K
M) FV^^=M=M9N9N((((G Zs%C%C H[H[H]H] Gm.A.A.C.CGGGG   rM   file_contentc                    t          |          }|rd|dS |s|dk    rdddS t          |                    d                    }|t          k    rdd|ddt          dd	dS t	          ||
          }|rd|dS t          |           }|s	dd|  ddS t          |           }|rd|dS t          |d         |          \  }}|rd|dS |j        	                    dd           |
                                r|                    d          nd}t          ||           t          |d                   }	|	r.|t          ||           n|                    d           d|	dS dd| d|  dt          |          dS )z>Add or overwrite a supporting file within any skill directory.Fr   r   zfile_content is required.r   zFile content is rl   z bytes (limit: z7 bytes / 1 MiB). Consider splitting into smaller files.r   r;   z2' not found. Create it first with action='create'.rr   Tr   r   N)
missing_okFile 'z' written to skill 'r   r   )r   rH   encodeMAX_SKILL_FILE_BYTESrn   rx   rA   r   ru   r   rs   r   r   r(   r   rS   )
r7   ry   r   r   content_bytesr   r   r   r   r   s
             r   _write_filer   l  s6   
i
(
(C
 0 3/// HLB.. +FGGG ++G4455M+++:=; : :/D: : :
 
 	
 !Y
?
?
?C
 0 3///4  H o +mT+m+m+mnnnt$$J 7 :666'(8)DDKFC
 0 3///
Mt444=C]]__Vv'''999RVv|,,, &hv&677J 7'v'78888MMTM*** :666 CICC4CCCF  rM   c           	         t          |          }|rd|dS t          |           }|s	dd|  ddS t          |           }|rd|dS |d         }t          ||          \  }}|rd|dS |                                sg }t
          D ]|}||z  }	|	                                ra|	                    d          D ]K}
|
                                r5|                    t          |

                    |                               L}dd| d|  d	|r|nd
dS |                                 |j        }||k    rI|                                r5t          |                                          s|                                 dd| d|  d	dS )z2Remove a supporting file from any skill directory.Fr   r;   r   rr   *r   z' not found in skill 'r   N)r   r   available_filesTz' removed from skill 'r   )r   rx   rA   r   rs   r   rt   is_fileappendrS   r1   r   ru   r   r   r   )r7   ry   r   r   r   r   r   	availablesubdirdr   ru   s               r   _remove_filer     s   
i
(
(C
 0 3///4  H I +GT+G+G+GHHHt$$J 7 :666 I'	9==KFC
 0 3///==?? 
	% 	H 	HFF"Axxzz H H HAyy{{ H!((Q]]9-E-E)F)FGGGGiGGtGGG,5?yy4
 
 	
 MMOOO ]Fv}}s6>>;K;K7L7L EIEETEEE  rM   actionc
                 R   | dk    r%|st          dd          S t          |||          }
n| dk    r$|st          dd          S t          ||          }
n| dk    r:|st          dd          S |t          d
d          S t          |||||          }
n| dk    rt	          ||	          }
nq| dk    r8|st          dd          S |t          dd          S t          |||          }
n3| dk    r$|st          dd          S t          ||          }
n	dd|  dd}
|
                    d          r_	 ddlm	}  |d           n# t          $ r Y nw xY w	 ddlm}m} | dv r ||           n| dk    r ||           n# t          $ r Y nw xY wt          j        |
d          S )zz
    Manage user-created skills. Dispatches to the appropriate action handler.

    Returns JSON string with results.
    createzVcontent is required for 'create'. Provide the full SKILL.md text (frontmatter + body).F)r   editzGcontent is required for 'edit'. Provide the full updated SKILL.md text.patchz=old_string is required for 'patch'. Provide the text to find.NzLnew_string is required for 'patch'. Use empty string to delete matched text.delete)r   
write_filezJfile_path is required for 'write_file'. Example: 'references/api-guide.md'z*file_content is required for 'write_file'.remove_filez(file_path is required for 'remove_file'.zUnknown action 'z<'. Use: create, edit, patch, delete, write_file, remove_filer   r   r   ) clear_skills_system_prompt_cacheT)clear_snapshot)
bump_patchforget)r   r   r   r   )ensure_ascii)
tool_errorr   r   r   r   r   r   r>   agent.prompt_builderr   r   tools.skill_usager   r   jsondumps)r   r7   rV   rN   ry   r   r   r   r   r   r#   r   r   r   s                 r   skill_manager     s   "  	Gv  AF  G  G  G  GtWh77	6		 	xgqvwwwwT7++	7		 	n]glmmmmlv{||||dJ
I{SS	8		t=AAA	<		 	{jtyzzzzJTYZZZZT9l;;	=	 	  	YHRWXXXXdI.. #  .E  .E  .E  .E  F  Fzz) 	MMMMMM,,DAAAAA 	 	 	D	
	<<<<<<<<GGG
4    8##t 	 	 	D	 :f51111s$   5E 
EE)F 
FFr   u   Manage skills (create, update, delete). Skills are your procedural memory — reusable approaches for recurring task types. New skills go to u  /skills/; existing skills can be modified wherever they live.

Actions: create (full SKILL.md + optional category), patch (old_string/new_string — preferred for fixes), edit (full SKILL.md rewrite — major overhauls only), delete, write_file, remove_file.

On delete, pass `absorbed_into=<umbrella>` when you're merging this skill's content into another one, or `absorbed_into=""` when you're pruning it with no forwarding target. This lets the curator tell consolidation from pruning without guessing, so downstream consumers (cron jobs that reference the old skill name, etc.) get updated correctly. The target you name in `absorbed_into` must already exist — create/patch the umbrella first, then delete.

Create when: complex task succeeded (5+ calls), errors overcome, user-corrected approach worked, non-trivial workflow discovered, or user asks you to remember a procedure.
Update when: instructions stale/wrong, OS-specific failures, missing steps or pitfalls found during use. If you used a skill and hit issues not covered by it, patch it immediately.

After difficult/iterative tasks, offer to save as a skill. Skip for simple one-offs. Confirm with user before creating/deleting.

Good skills: trigger conditions, numbered steps with exact commands, pitfalls section, verification steps. Use skill_view() to see format examples.

Pinned skills are off-limits — all write actions refuse with a message pointing the user to `hermes curator unpin <name>`. Don't try to route around this by renaming or recreating.objectstring)r   r   r   r   r   r   zThe action to perform.)typeenumrY   zSkill name (lowercase, hyphens/underscores, max 64 chars). Must match an existing skill for patch/edit/delete/write_file/remove_file.)r   rY   zFull SKILL.md content (YAML frontmatter + markdown body). Required for 'create' and 'edit'. For 'edit', read the skill first with skill_view() and provide the complete updated text.zText to find in the file (required for 'patch'). Must be unique unless replace_all=true. Include enough surrounding context to ensure uniqueness.zXReplacement text (required for 'patch'). Can be empty string to delete the matched text.booleanzZFor 'patch': replace all occurrences instead of requiring a unique match (default: false).zOptional category/domain for organizing the skill (e.g., 'devops', 'data-science', 'mlops'). Creates a subdirectory grouping. Only used with 'create'.zPath to a supporting file within the skill directory. For 'write_file'/'remove_file': required, must be under references/, templates/, scripts/, or assets/. For 'patch': optional, defaults to SKILL.md if omitted.z0Content for the file. Required for 'write_file'.u  For 'delete' only — declares intent so the curator can tell consolidation from pruning without guessing. Pass the umbrella skill name when this skill's content was merged into another (the target must already exist). Pass an empty string when the skill is truly stale and being pruned with no forwarding target. Omitting the arg on delete is supported for backward compatibility but downstream tooling (e.g. cron-job skill reference rewriting) will have to guess at intent.)
r   r7   rV   r   r   r   rN   ry   r   r   )r   
propertiesrequired)r7   rY   
parameters)registryr   c                    t          |                     dd          |                     dd          |                     d          |                     d          |                     d          |                     d          |                     d          |                     d	          |                     d
d          |                     d          
  
        S )Nr   r   r7   rV   rN   ry   r   r   r   r   Fr   )
r   r7   rV   rN   ry   r   r   r   r   r   )r   r>   )argskws     r   <lambda>r    s    |xx"%%XXfb!!##*%%((;''XXn--88L))88L))HH]E22hh//
 1 
 1 
 1 rM   u   📝)r7   toolsetschemahandleremoji)ri   )N)r   )NF)NNNNNNFN)D__doc__r   loggingr   r[   r   r   pathlibr   hermes_constantsr   r   typingr   r   r   r	   utilsr
   r   r   r   	getLogger__name__r!   tools.skills_guardr   r   r   r    ImportErrorboolr   rS   r(   r^   HERMES_HOMEr3   rI   rb   r6   rA   rm   r   compilerJ   r   rL   rU   rh   rn   rp   rx   r   r   r   r   r   r   r   r   r   r   SKILL_MANAGE_SCHEMAtools.registryr   r   register rM   r   <module>r     sd   B   				 				         A A A A A A A A - - - - - - - - - - - - 1 1 1 1 1 1 1 1 % % % % % %		8	$	$WWWWWWWWWW   d    &D Xc]    4  o8#
      ,     6 "    
455 CBB #    # 8C=    2$3 $8C= $ $ $ $N C  Xc]     S C 4    c htCH~6    $3 8C=    8T c eHTNT\]`TaDa>b     $   RV    H6 6 6c 6S 6DcN 6 6 6 6r"c "C "DcN " " " "R _ _
__ _ 	_
 _ 
#s(^_ _ _ _D6 6 6HSM 6T#s(^ 6 6 6 6r5c 5c 5 5c3h 5 5 5 5p,s ,s ,tCH~ , , , ,l F2 F2F2
F2 F2 	F2
 F2 F2 F2 F2 F2 F2 	F2 F2 F2 F2\ 	1//11	1 	1 	1:  !ZZZ7  !a  !U  !)  !2  "{ 
 !/  !N  !Q 
 !? {K
 K
X v&]O O?o o f 0 / / / / / / /  	
1 
1      s   A! !A+*A+