+
    i                     
   R t ^ RIt^ RIt^ RIt^ RIHtHt ^ RIHtH	t	 ^ RI
HtHtHtHt ^ RIHt ^ RIHt ]! ]P(                  ! 4       4      t]P,                  P/                  ]RR4      ]P,                  P/                  ]RR	4      ]P,                  P/                  ]RR
4      ]P,                  P/                  ]RR4      ]! ]! 4       R,          4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      RRR3 U u0 uF  p ]P,                  P1                  V 4      kK   	  up t]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R4      RR]P,                  P/                  ]R4      ]P,                  P/                  ]R4      ]P,                  P/                  ]R R!4      3	 U u. uF/  p ]P,                  P1                  V 4      ]P4                  ,           NK1  	  up tR" R# ltR$ R% lt] ! R& R'4      4       t] ! R( R)4      4       t] ! R* R+4      4       t ] ! R, R-4      4       t!] ! R. R/4      4       t"] ! R0 R14      4       t#] ! R2 R34      4       t$ ! R4 R5]4      t%0 R6kR7kR8kR9kR:kR;kR<kR=kR>kR?kR@kRAkRBkRCkRDkREkRFkRGkRHkRIkRJkRKkRLkRMkRNkROkRPkRQkRRkRSkRTkRUkRVkRWkRXkRYkRZkR[kR\kR]kR^kR_kR`kRakRbkRckRdkRekRfkRgkRhkRikt&0 Rwmt'RjRkRlRmRnRoRpRqRrRs/t(Rtt)Rtt*Rxt+ ! Ru Rv]%4      t,R# u up i u up i )yaL  
File Operations Module

Provides file manipulation capabilities (read, write, patch, search) that work
across all terminal backends (local, docker, singularity, ssh, modal, daytona).

The key insight is that all file operations can be expressed as shell commands,
so we wrap the terminal backend's execute() interface to provide a unified file API.

Usage:
    from tools.file_operations import ShellFileOperations
    from tools.terminal_tool import _active_environments
    
    # Get file operations for a terminal environment
    file_ops = ShellFileOperations(terminal_env)
    
    # Read a file
    result = file_ops.read_file("/path/to/file.py")
    
    # Write a file
    result = file_ops.write_file("/path/to/new.py", "print('hello')")
    
    # Search for content
    result = file_ops.search("TODO", path=".", file_glob="*.py")
N)ABCabstractmethod)	dataclassfield)OptionalListDictAny)Path)get_hermes_homez.sshauthorized_keysid_rsa
id_ed25519configz.envz.bashrcz.zshrcz.profilez.bash_profilez	.zprofilez.netrcz.pgpassz.npmrcz.pypircz/etc/sudoersz/etc/passwdz/etc/shadowz.awsz.gnupgz.kubez/etc/sudoers.dz/etc/systemdz.dockerz.azurez.configghc                :    V ^8  d   QhR\         \        ,          /#    return)r   str)formats   "2/home/ubuntu/hermes-agent/tools/file_operations.py__annotate__r   Q   s      hsm     c                     \         P                  ! RR4      p V '       g   R#  \         P                  P                  \         P                  P	                  V 4      4      #   \
         d     R# i ; i)aH  Return the resolved HERMES_WRITE_SAFE_ROOT path, or None if unset.

When set, all write_file/patch operations are constrained to this
directory tree.  Writes outside it are denied even if the target is
not on the static deny list.  Opt-in hardening for gateway/messaging
deployments that should only touch a workspace checkout.
HERMES_WRITE_SAFE_ROOT N)osgetenvpathrealpath
expanduser	Exception)roots    r   _get_safe_write_rootr$   Q   sV     99-r2Dww 2 24 899 s   ;A A.-A.c                0    V ^8  d   QhR\         R\        /# r   r   r   r   bool)r   s   "r   r   r   b   s      3 4 r   c                   \         P                  P                  \         P                  P                  \	        V 4      4      4      pV\
        9   d   R# \         F  pVP                  V4      '       g   K   R# 	  \        4       pV'       d5   W8X  g/   VP                  V\         P                  ,           4      '       g   R# R# )z.Return True if path is on the write deny list.TF)
r   r   r    r!   r   WRITE_DENIED_PATHSWRITE_DENIED_PREFIXES
startswithr$   sep)r   resolvedprefix	safe_roots   &   r   _is_write_deniedr1   b   s    ww 2 23t9 =>H %%'v&& (
 %&I%)<)<Y=O)P)Pr   c                      a  ] tR t^zt o RtRt^ t^ tRtRt	Rt
RtRtRtRtRt]! ]R7      tV 3R lR ltV 3R ltR	tV tR# )

ReadResultzResult from reading a file.r   FNdefault_factoryc                    < V ^8  d   QhRS[ /# r   dict)r   __classdict__s   "r   r   ReadResult.__annotate__   s     U U Ur   c                    V P                   P                  4        UUu/ uF  w  rVf   K  V. 8w  g   K  WbK  	  upp# u uppi N__dict__itemsselfkvs   &  r   to_dictReadResult.to_dict   s:    !%!4!4!6T!6!AQSG!6TTTs   >>>c                
  < V ^8  d   Qh/ S[ ;R&   S[;R&   S[;R&   S[;R&   S[S[ ,          ;R&   S[;R&   S[;R&   S[S[ ,          ;R&   S[S[ ,          ;R	&   S[S[ ,          ;R
&   S[S[ ,          ;R&   S[S[ ,          ;R&   # )r   contenttotal_lines	file_size	truncatedhint	is_binaryis_imagebase64_content	mime_type
dimensionserrorsimilar_files)r   intr(   r   r   )r   r9   s   "r   r   r:   z   s        	 
     3-      SM(  }#  $  C=  9: r    )__name__
__module____qualname____firstlineno____doc__rG   rH   rI   rJ   rK   rL   rM   rN   rO   rP   rQ   r   listrR   rD   __annotate_func____static_attributes____classdictcell__r9   s   @r   r3   r3   z   s\     %GKIIDIH$(N#I $JE$T:MU U!  r   r3   c                   R   a  ] tR t^t o Rt^ tRtRtRtV 3R lR lt	V 3R lt
RtV tR# )WriteResultzResult from writing a file.FNc                    < V ^8  d   QhRS[ /# r   r7   )r   r9   s   "r   r   WriteResult.__annotate__   s     I I Ir   c                v    V P                   P                  4        UUu/ uF  w  rVf   K  WbK  	  upp# u uppi r<   r=   r@   s   &  r   rD   WriteResult.to_dict   s1    !%!4!4!6H!6!!6HHHs   55c                j   < V ^8  d   Qh/ S[ ;R&   S[;R&   S[S[,          ;R&   S[S[,          ;R&   # )r   bytes_writtendirs_createdrQ   warning)rS   r(   r   r   )r   r9   s   "r   r   rb      sA        	 
 C=  c]! r   rT   )rU   rV   rW   rX   rY   rf   rg   rQ   rh   rD   r[   r\   r]   r^   s   @r   r`   r`      s/     %MLE!GI I  r   r`   c                      a  ] tR t^t o RtRtRt]! ]R7      t	]! ]R7      t
]! ]R7      tRtRtV 3R lR ltV 3R ltR	tV tR# )
PatchResultzResult from patching a file.Fr   r4   Nc                    < V ^8  d   QhRS[ /# r   r7   )r   r9   s   "r   r   PatchResult.__annotate__   s       r   c                   R V P                   /pV P                  '       d   V P                  VR&   V P                  '       d   V P                  VR&   V P                  '       d   V P                  VR&   V P                  '       d   V P                  VR&   V P
                  '       d   V P
                  VR&   V P                  '       d   V P                  VR&   V# successdifffiles_modifiedfiles_createdfiles_deletedlintrQ   rn   )rA   results   & r   rD   PatchResult.to_dict   s    T\\*999!YYF6N'+':':F#$&*&8&8F?#&*&8&8F?#999!YYF6N:::"jjF7Or   c                   < V ^8  d   Qh/ S[ ;R&   S[;R&   S[S[,          ;R&   S[S[,          ;R&   S[S[,          ;R&   S[S[S[S[3,          ,          ;R&   S[S[,          ;R&   # )r   ro   rp   rq   rr   rs   rt   rQ   )r(   r   r   r   r   r	   )r   r9   s   "r   r   rl      s|        N	 
 I;  9:  9:  4S>
")  C= r   rT   )rU   rV   rW   rX   rY   ro   rp   r   rZ   rq   rr   rs   rt   rQ   rD   r[   r\   r]   r^   s   @r   rj   rj      sK     &GD %d ;N$T:M$T:M%)DE   r   rj   c                   4   a  ] tR t^t o RtRtV 3R ltRtV tR# )SearchMatchzA single search match.g        c                J   < V ^8  d   Qh/ S[ ;R&   S[;R&   S[ ;R&   S[;R&   # )r   r   line_numberrG   mtime)r   rS   float)r   r9   s   "r   r   SearchMatch.__annotate__   s7      I  	 
 L   r   rT   N)	rU   rV   rW   rX   rY   r|   r[   r\   r]   r^   s   @r   ry   ry      s       E  r   ry   c                      a  ] tR t^t o Rt]! ]R7      t]! ]R7      t]! ]	R7      t
^ tRtRtV 3R lR ltV 3R ltRtV tR# )	SearchResultzResult from searching.r4   FNc                    < V ^8  d   QhRS[ /# r   r7   )r   r9   s   "r   r   SearchResult.__annotate__   s       r   c           	        R V P                   /pV P                  '       dD   V P                   Uu. uF)  pRVP                  RVP                  RVP                  /NK+  	  upVR&   V P
                  '       d   V P
                  VR&   V P                  '       d   V P                  VR&   V P                  '       d   RVR&   V P                  '       d   V P                  VR	&   V# u upi )
total_countr   linerG   matchesfilescountsTrJ   rQ   )	r   r   r   r{   rG   r   r   rJ   rQ   )rA   ru   ms   &  r   rD   SearchResult.to_dict   s    !1!12<<< !%A 	199M%!F9 :::"jjF7O;;;#{{F8>>>"&F;:::"jjF7O!s   /C c                   < V ^8  d   Qh/ S[ S[,          ;R&   S[ S[,          ;R&   S[S[S[3,          ;R&   S[;R&   S[;R&   S[S[,          ;R&   # )r   r   r   r   r   rJ   rQ   )r   ry   r   r   rS   r(   r   )r   r9   s   "r   r   r      sh      +<  92	 
 cN8      C= r   rT   )rU   rV   rW   rX   rY   r   rZ   r   r   r8   r   r   rJ   rQ   rD   r[   r\   r]   r^   s   @r   r   r      sF      !&t!<GT2E"48FKIE   r   r   c                   R   a  ] tR t^t o RtRtRtRtRtV 3R lR lt	V 3R lt
RtV tR	# )

LintResultzResult from linting a file.TFr   c                    < V ^8  d   QhRS[ /# r   r7   )r   r9   s   "r   r   LintResult.__annotate__   s     
 
 
r   c                    V P                   '       d   R RRV P                  /# R V P                  '       d   RMRRV P                  /# )statusskippedmessageokrQ   output)r   r   ro   r   )rA   s   &r   rD   LintResult.to_dict   s@    <<<iDLLAAdlllddkk
 	
r   c                J   < V ^8  d   Qh/ S[ ;R&   S[ ;R&   S[;R&   S[;R&   # )r   ro   r   r   r   )r(   r   )r   r9   s   "r   r   r      s9        	 
    r   rT   N)rU   rV   rW   rX   rY   ro   r   r   r   rD   r[   r\   r]   r^   s   @r   r   r      s-     %GGFG
 
  r   r   c                   8   a  ] tR t^t o RtRt^ tV 3R ltRtV t	R# )ExecuteResultz&Result from executing a shell command.r   c                2   < V ^8  d   Qh/ S[ ;R&   S[;R&   # )r   stdout	exit_coder   rS   )r   r9   s   "r   r   ExecuteResult.__annotate__   s        	 r   rT   N)
rU   rV   rW   rX   rY   r   r   r[   r\   r]   r^   s   @r   r   r      s     0FI	  r   r   c                      a  ] tR t^t o Rt]RV 3R lR ll4       t]V 3R lR l4       t]RV 3R lR ll4       t]V 3R lR	 l4       t	]RV 3R lR ll4       t
RtV tR
# )FileOperationsz@Abstract interface for file operations across terminal backends.c                2   < V ^8  d   QhRS[ RS[RS[RS[/# r   r   offsetlimitr   r   rS   r3   )r   r9   s   "r   r   FileOperations.__annotate__   s)      c 3 3  r   c                    R# )z$Read a file with pagination support.NrT   )rA   r   r   r   s   &&&&r   	read_fileFileOperations.read_file        	r   c                ,   < V ^8  d   QhRS[ RS[ RS[/# r   r   rG   r   r   r`   )r   r9   s   "r   r   r      s"      s S [ r   c                    R# )z8Write content to a file, creating directories as needed.NrT   )rA   r   rG   s   &&&r   
write_fileFileOperations.write_file   r   r   c          
      8   < V ^8  d   QhRS[ RS[ RS[ RS[RS[/# r   r   
old_string
new_stringreplace_allr   r   r(   rj   )r   r9   s   "r   r   r     s2      # 3 C #'4?r   c                    R# )z,Replace text in a file using fuzzy matching.NrT   )rA   r   r   r   r   s   &&&&&r   patch_replaceFileOperations.patch_replace  s     	r   c                &   < V ^8  d   QhRS[ RS[/# r   patch_contentr   r   rj   )r   r9   s   "r   r   r     s      s { r   c                    R# )zApply a V4A format patch.NrT   )rA   r   s   &&r   	patch_v4aFileOperations.patch_v4a
  r   r   Nc                `   < V ^8  d   QhRS[ RS[ RS[ RS[S[ ,          RS[RS[RS[ RS[R	S[/	# 
r   patternr   target	file_globr   r   output_modecontextr   r   r   rS   r   )r   r9   s   "r   r   r     sV      c  C "3-7:IL69BNr   c	                    R# )zSearch for content or files.NrT   )	rA   r   r   r   r   r   r   r   r   s	   &&&&&&&&&r   searchFileOperations.search  s    
 	r   rT      i  F.rG   N2       rG   r   )rU   rV   rW   rX   rY   r   r   r   r   r   r   r\   r]   r^   s   @r   r   r      sy     J       
     r   r   .png.jpg.jpeg.gif.webp.bmp.icoz.tiffz.tifz.svgz.mp3z.mp4z.wavz.aviz.movz.mkvz.flacz.oggz.webmz.zipz.tarz.gzz.bz2z.xzz.7zz.rarz.pdfz.docz.docxz.xlsz.xlsxz.pptz.pptxz.exez.dllz.soz.dylibz.oz.az.pycz.pyoz.classz.wasmz.binz.ttfz.otfz.woffz.woff2z.eotz.dbz.sqlitez.sqlite3z.pyz python -m py_compile {file} 2>&1z.jsznode --check {file} 2>&1z.tsznpx tsc --noEmit {file} 2>&1z.gozgo vet {file} 2>&1z.rszrustfmt --check {file} 2>&1i  c                     a  ] tR tRt o RtR1V 3R lR lltR2V 3R lR lltV 3R lR	 ltR1V 3R
 lR lltV 3R lR lt	R3V 3R lR llt
V 3R lR ltV 3R lR ltV 3R lR ltR4V 3R lR lltR5tV 3R lR ltV 3R lR ltV 3R lR ltR6V 3R lR lltV 3R  lR! ltV 3R" lR# ltR7V 3R$ lR% lltV 3R& lR' ltV 3R( lR) ltV 3R* lR+ ltV 3R, lR- ltV 3R. lR/ ltR0tV tR# )8ShellFileOperationsiA  z
File operations implemented via shell commands.

Works with ANY terminal backend that has execute(command, cwd) method.
This includes local, docker, singularity, ssh, modal, and daytona environments.
Nc                    < V ^8  d   QhRS[ /# )r   cwdr   )r   r9   s   "r   r    ShellFileOperations.__annotate__I  s     2 2# 2r   c                    Wn         T;'       g8    \        VRR4      ;'       g#    \        \        VRR4      RR4      ;'       g    RV n        / V n        R# )a  
Initialize file operations with a terminal environment.

Args:
    terminal_env: Any object with execute(command, cwd) method.
                 Returns {"output": str, "returncode": int}
    cwd: Working directory (defaults to env's cwd or current directory)
r   Nr   /)envgetattrr   _command_cache)rA   terminal_envr   s   &&&r   __init__ShellFileOperations.__init__I  sf      
  V V',t< V V7<4@%NV VRU 	 02r   c          
      8   < V ^8  d   QhRS[ RS[ RS[RS[ RS[/# )r   commandr   timeout
stdin_datar   )r   rS   r   )r   r9   s   "r   r   r   ]  s2     
 
S 
s 
C 

)6
r   c                    / pV'       d   W5R&   Ve   WER&   V P                   P                  ! V3RT;'       g    V P                  /VB p\        VP	                  RR4      VP	                  R^ 4      R7      # )zExecute command via terminal backend.

Args:
    stdin_data: If provided, piped to the process's stdin instead of
                embedding in the command string. Bypasses ARG_MAX.
r   r   r   r   r   
returncode)r   r   )r   executer   r   get)rA   r   r   r   r   kwargsru   s   &&&&&  r   _execShellFileOperations._exec]  ss      '9!#-< !!'IsdhhI&I::h+jjq1
 	
r   c                &   < V ^8  d   QhRS[ RS[/# )r   cmdr   r'   )r   r9   s   "r   r   r   q  s     ( ( ( (r   c                    WP                   9  d@   V P                  RV R24      pVP                  P                  4       R8H  V P                   V&   V P                   V,          # )z6Check if a command exists in the environment (cached).zcommand -v z >/dev/null 2>&1 && echo 'yes'yes)r   r   r   strip)rA   r   ru   s   && r   _has_command ShellFileOperations._has_commandq  sY    )))ZZ+cU2P QRF'-}}':':'<'ED$""3''r   c                ,   < V ^8  d   QhRS[ RS[ RS[/# )r   r   content_sampler   r'   )r   r9   s   "r   r   r   x  s"      c 3 $ r   c                   \         P                  P                  V4      ^,          P                  4       pV\        9   d   R# V'       dC   V'       g   R# \        R VR,           4       4      pV\        \        V4      R4      ,          R8  # R# )z_
Check if a file is likely binary.

Uses extension check (fast) + content analysis (fallback).
TFc              3   \   "   T F"  p\        V4      ^ 8  g   K  VR9  g   K  ^x  K$  	  R# 5i)    z
	N)ord).0cs   & r   	<genexpr>8ShellFileOperations._is_likely_binary.<locals>.<genexpr>  s2       E+@a"%a&2+ !"2382C !"+@s   ,,
,:Ni  Ni  g333333?)r   r   splitextlowerBINARY_EXTENSIONSsumminlen)rA   r   r   extnon_printables   &&&  r   _is_likely_binary%ShellFileOperations._is_likely_binaryx  sz     ggt$Q'--/## !  E>%+@  E EM 3s>':D#AADHHr   c                &   < V ^8  d   QhRS[ RS[/# r&   r'   )r   r9   s   "r   r   r     s     ' 'c 'd 'r   c                |    \         P                  P                  V4      ^,          P                  4       pV\        9   # )z2Check if file is an image we can return as base64.)r   r   r  r  IMAGE_EXTENSIONS)rA   r   r
  s   && r   	_is_imageShellFileOperations._is_image  s0    ggt$Q'--/&&&r   c                ,   < V ^8  d   QhRS[ RS[RS[ /# )r   rG   
start_liner   r   )r   r9   s   "r   r   r     s"     	# 	# 	## 	#c 	#r   c                    VP                  R4      p. p\        W2R7       F@  w  rV\        V4      \        8  d   VR\         R,           pVP	                  VR RV 24       KB  	  RP                  V4      # )z7Add line numbers to content in LINE_NUM|CONTENT format.
)startNz... [truncated]6d|)split	enumerater	  MAX_LINE_LENGTHappendjoin)rA   rG   r  linesnumberedir   s   &&&    r   _add_line_numbers%ShellFileOperations._add_line_numbers  so    d# 9GA4y?*,_-0AAOOqfAdV,-	 :
 yy""r   c                &   < V ^8  d   QhRS[ RS[ /# r&   r   )r   r9   s   "r   r   r     s     # # # #r   c                   V'       g   V# VP                  R4      '       EdW   V P                  R4      pVP                  ^ 8X  Ed4   VP                  P	                  4       '       Ed   VP                  P	                  4       pVR8X  d   V# VP                  R4      '       d   W1R,          ,           # VR,          pVP                  R4      pV^ 8  d   VRV MTpV'       d   \        P                  ! RV4      '       d}   V P                  RV 24      pVP                  ^ 8X  dX   VP                  P	                  4       '       d8   VP                  P	                  4       pV^\        V4      ,           R p	W,           # V# )	z
Expand shell-style paths like ~ and ~user to absolute paths.

This must be done BEFORE shell escaping, since ~ doesn't expand
inside single quotes.
~z
echo $HOMEz~/:r   NNr   Nz[a-zA-Z0-9._-]+zecho ~)	r,   r   r   r   r   findre	fullmatchr	  )
rA   r   ru   homerest	slash_idxusernameexpand_result	user_homesuffixs
   &&        r   _expand_path ShellFileOperations._expand_path  s5    K ??3ZZ-F1$)<)<)>)>}}**,3;K__T**r(?* Bx IIcN	/8A~4
+4-? J J %)JJz/B$CM$..!38L8L8R8R8T8T$1$8$8$>$>$@	!%a#h-&7&8!9(11r   c                &   < V ^8  d   QhRS[ RS[ /# )r   argr   r   )r   r9   s   "r   r   r     s     7 7S 7S 7r   c                B    RVP                  RR4      ,           R,           # )z/Escape a string for safe use in shell commands.'z'"'"')replace)rA   r4  s   &&r   _escape_shell_arg%ShellFileOperations._escape_shell_arg  s     S[[i00366r   c                2   < V ^8  d   QhRS[ RS[ RS[ RS[ /# )r   old_contentnew_contentfilenamer   r   )r   r9   s   "r   r   r     s*     	 	 	3 	# 	RU 	r   c                    VP                  RR7      pVP                  RR7      p\        P                  ! WERV 2RV 2R7      pRP                  V4      # )z2Generate unified diff between old and new content.T)keependsza/zb/)fromfiletofiler   )
splitlinesdifflibunified_diffr  )rA   r;  r<  r=  	old_lines	new_linesrp   s   &&&&   r   _unified_diff!ShellFileOperations._unified_diff  s]    **D*9	**D*9	##(_z?

 wwt}r   c                2   < V ^8  d   QhRS[ RS[RS[RS[/# r   r   )r   r9   s   "r   r   r     s/     W
 W
c W
3 W
3 W
 W
r   c           	     n   V P                  V4      p\        V\        4      pRV P                  V4       R2pV P	                  V4      pVP
                  ^ 8w  d   V P                  V4      #  \        VP                  P                  4       4      pV\        8  d    V P                  V4      '       d   \        RRVRR7      # RV P                  V4       R2pV P	                  V4      pV P                  WP                  4      '       d   \        RVRR7      # W#,           ^,
          p	R	V R
V	 RV P                  V4       2p
V P	                  V
4      pVP
                  ^ 8w  d   \        RVP                   2R7      # RV P                  V4       2pV P	                  V4      p \        VP                  P                  4       4      pW8  pRpV'       d   RV	^,            RV RV	 RV R2	p\        V P                  VP                  V4      VVVVR7      #   \         d    ^ p ELi ; i  \         d    ^ p Lqi ; i)a2  
Read a file with pagination, binary detection, and line numbers.

Args:
    path: File path (absolute or relative to cwd)
    offset: Line number to start from (1-indexed, default 1)
    limit: Maximum lines to return (default 500, max 2000)

Returns:
    ReadResult with content, metadata, or error info
wc -c <  2>/dev/nullTzImage file detected. Automatically redirected to vision_analyze tool. Use vision_analyze with this file path to inspect the image contents.rM   rL   rI   rK   zhead -c 1000 zUBinary file - cannot display as text. Use appropriate tools to handle this file type.)rL   rI   rQ   zsed -n ',zp' Failed to read file: rQ   zwc -l < NzUse offset=z to continue reading (showing -z of z lines))rG   rH   rI   rJ   rK   )r1  r  	MAX_LINESr8  r   r   _suggest_similar_filesrS   r   r   
ValueErrorMAX_FILE_SIZEr  r3   r  r"  )rA   r   r   r   stat_cmdstat_resultrI   
sample_cmdsample_resultend_lineread_cmdread_resultwc_cmd	wc_resultrH   rJ   rK   s   &&&&             r   r   ShellFileOperations.read_file  sb      & E9% d44T:;<Hjj*  A%..t44	K..4467I
 }$ >>$#\  %T%;%;D%A$B,O


:.!!$(<(<==#m  >A%fXQxjD4J4J44P3QRjj*  A%&;K<N<N;O$PQQ D22489:JJv&		i..4467K
  *	 A.LVHTUV^U__cdocppwxD**;+=+=vF#
 	
k  	I	X  	K	s$   +#H #H$ H! H!$H43H4c                &   < V ^8  d   QhRS[ RS[/# r&   r   r3   )r   r9   s   "r   r   r   2  s     ?
 ?
 ?

 ?
r   c                f   RV P                  V4       R2pV P                  V4      p \        VP                  P	                  4       4      pW@P                  8  d   \        RRVRVR R2R7      # RV P                  V4       R	2pV P                  V^R
7      pVP                  ^ 8w  d   \        RRVRVP                   2R7      # RpV P                  R4      '       dR   RV P                  V4       R2pV P                  V4      p	V	P                  ^ 8X  d   V	P                  P	                  4       p\        P                  P                  V4      ^,          P                  4       p
RRRRRRRRRRRRRR/pVP                  V
R4      p\        RRVVP                  VVR7      #   \
         d    ^ p ELdi ; i)z-Read an image file, returning base64 content.rK  rL  TzImage is too large to inline (rN  zJ bytes). Use vision_analyze to inspect the image, or reference it by path.rM  zbase64 z 2>/dev/null | tr -d '\n'r   zFailed to read image: )rM   rL   rI   rQ   Nidentifyzidentify -format '%wx%h' r   z	image/pngr   z
image/jpegr   r   z	image/gifr   z
image/webpr   z	image/bmpr   zimage/x-iconzapplication/octet-stream)rM   rL   rI   rN   rO   rP   )r8  r   rS   r   r   rT  MAX_IMAGE_BYTESr3   r   r   r   r   r  r  r   )rA   r   rV  rW  rI   b64_cmd
b64_resultrP   dim_cmd
dim_resultr
  
mime_typesrO   s   &&           r   _read_imageShellFileOperations._read_image2  s    d44T:;<Hjj*	K..4467I +++#4YqM BX X  D224899STZZZ4
1$#.z/@/@.AB	  
Z((1$2H2H2N1O|\GG,J##q('..446
 ggt$Q'--/KL\K\KN

 NN3(BC	%,,!
 	
c  	I	s   #F F0/F0c                &   < V ^8  d   QhRS[ RS[/# r&   ra  )r   r9   s   "r   r   r   s  s     
 
3 
: 
r   c                   \         P                  P                  V4      ;'       g    Rp\         P                  P                  V4      pRV P	                  V4       R2pV P                  V4      p. pVP                  ^ 8X  d   VP                  P                  4       '       d   VP                  P                  4       P                  R4      pV F  p\        VP                  4       4      \        VP                  4       4      ,          p	\        V	4      \        V4      R,          8  g   K\  VP                  \         P                  P                  W(4      4       K  	  \        RV 2VR,          R7      # )	z;Suggest similar files when the requested file is not found.r   zls -1 z 2>/dev/null | head -20r  g      ?zFile not found: :N   N)rQ   rR   )r   r   dirnamebasenamer8  r   r   r   r   r  setr  r	  r  r  r3   )
rA   r   dir_pathr=  ls_cmd	ls_resultsimilarr   fcommons
   &&        r   rS  *ShellFileOperations._suggest_similar_filess  s    77??4(//C77##D) $00:;;RSJJv&	!#	(8(8(>(>(@(@$$**,2248EX^^-.QWWY?v;#h-#"55NN277<<#<=	  $TF+!"+
 	
r   c                ,   < V ^8  d   QhRS[ RS[ RS[/# r   r   )r   r9   s   "r   r   r     s"     4
 4
s 4
S 4
[ 4
r   c                   V P                  V4      p\        V4      '       d   \        RV R2R7      # \        P                  P                  V4      pRpV'       d9   RV P                  V4       2pV P                  V4      pVP                  ^ 8X  d   RpRV P                  V4       2pV P                  WrR7      pVP                  ^ 8w  d   \        R	VP                   2R7      # R
V P                  V4       R2p	V P                  V	4      p
 \        V
P                  P                  4       4      p\        VVR7      #   \         d    \        TP                  R4      4      p L4i ; i)uT  
Write content to a file, creating parent directories as needed.

Pipes content through stdin to avoid OS ARG_MAX limits on large
files. The content never appears in the shell command string —
only the file path does.

Args:
    path: File path to write
    content: Content to write

Returns:
    WriteResult with bytes written or error
Write denied: '(' is a protected system/credential file.rP  Fz	mkdir -p Tzcat > )r   zFailed to write file: rK  rL  zutf-8)rf   rg   )r1  r1   r`   r   r   rp  r8  r   r   r   rS   r   rT  r	  encode)rA   r   rG   parentrg   	mkdir_cmdmkdir_result	write_cmdwrite_resultrV  rW  rf   s   &&&         r   r   ShellFileOperations.write_file  s^       & D!!tf<d%eff &#D$:$:6$B#CDI::i0L%%*# T33D9:;	zz)z@!!Q&'=l>Q>Q=R%STT d44T:;<Hjj*	9 2 2 8 8 :;M '%
 	
  	9w 78M	9s   #D; ;%E#"E#c          
      8   < V ^8  d   QhRS[ RS[ RS[ RS[RS[/# r   r   )r   r9   s   "r   r   r     s2     ;
 ;
# ;
3 ;
C ;
#';
4?;
r   c                   V P                  V4      p\        V4      '       d   \        RV R2R7      # RV P                  V4       R2pV P	                  V4      pVP
                  ^ 8w  d   \        RV 2R7      # TP                  p^ RIHp V! WrW44      w  rpV'       d   \        VR7      # V
^ 8X  d   \        RV 2R7      # V P                  W4      pVP                  '       d   \        R	VP                   2R7      # V P                  WyV4      pV P                  V4      p\        R
TV.V'       d   VP                  4       R7      # RR7      # )a!  
Replace text in a file using fuzzy matching.

Args:
    path: File path to modify
    old_string: Text to find (must be unique unless replace_all=True)
    new_string: Replacement text
    replace_all: If True, replace all occurrences

Returns:
    PatchResult with diff and lint results
r|  r}  rP  zcat rL  rO  )fuzzy_find_and_replacez'Could not find match for old_string in zFailed to write changes: TN)ro   rp   rq   rt   )r1  r1   rj   r8  r   r   r   tools.fuzzy_matchr  r   rQ   rG  _check_lintrD   )rA   r   r   r   r   r[  r\  rG   r  r<  match_countrQ   r  rp   lint_results   &&&&&          r   r   !ShellFileOperations.patch_replace  sb      & D!!tf<d%eff $0067|Djj*  A%'<TF%CDD$$ 	=*@+
'% U++!'Ntf%UVV t9'@ASAS@T%UVV !!'= &&t, 6*5$$&	
 	
 <@	
 	
r   c                &   < V ^8  d   QhRS[ RS[/# r   r   )r   r9   s   "r   r   r     s      s { r   c                h    ^ RI HpHp V! V4      w  rEV'       d   \        RV 2R7      # V! W@4      pV# )a  
Apply a V4A format patch.

V4A format:
    *** Begin Patch
    *** Update File: path/to/file.py
    @@ context hint @@
     context line
    -removed line
    +added line
    *** End Patch

Args:
    patch_content: V4A format patch string

Returns:
    PatchResult with changes made
)parse_v4a_patchapply_v4a_operationszFailed to parse patch: rP  )tools.patch_parserr  r  rj   )rA   r   r  r  
operationsparse_errorru   s   &&     r   r   ShellFileOperations.patch_v4a  s<    ( 	M"1-"@
'>{m%LMM &j7r   c                &   < V ^8  d   QhRS[ RS[/# r&   )r   r   )r   r9   s   "r   r   r   %  s     
 
 

 
r   c                R   \         P                  P                  V4      ^,          P                  4       pV\        9  d   \        RRV R2R7      # \        V,          pVP                  4       ^ ,          pV P                  V4      '       g   \        RV R2R7      # VP                  V P                  V4      R7      pV P                  V^R7      p\        VP                  ^ 8H  VP                  P                  4       '       d    VP                  P                  4       R	7      # RR	7      # )
z
Run syntax check on a file after editing.

Args:
    path: File path to lint

Returns:
    LintResult with status and any errors
TzNo linter for z files)r   r   z not available)filerc  r   )ro   r   )r   r   r  r  LINTERSr   r  r   r   r8  r   r   r   r   )rA   r   r
  
linter_cmdbase_cmdr   ru   s   &&     r   r  ShellFileOperations._check_lint%  s    ggt$Q'--/gdnSE4PQQ S\
##%a(  **dxj4OPP T%;%;D%ABC,$$),2MM,?,?,A,A6==&&(
 	
GI
 	
r   c                `   < V ^8  d   QhRS[ RS[ RS[ RS[S[ ,          RS[RS[RS[ RS[R	S[/	# r   r   )r   r9   s   "r   r   r   I  sV     "> ">c "> ">C ">"3-">7:">IL">">69">BN">r   c	           	     
   V P                  V4      pV P                  RV P                  V4       R24      p	RV	P                  9   d   \	        RV R2^ R7      # VR8X  d   V P                  WWV4      # V P                  WWEVWx4      # )a  
Search for content or files.

Args:
    pattern: Regex (for content) or glob pattern (for files)
    path: Directory/file to search (default: cwd)
    target: "content" (grep) or "files" (glob)
    file_glob: File pattern filter for content search (e.g., "*.py")
    limit: Max results (default 50)
    offset: Skip first N results
    output_mode: "content", "files_only", or "count"
    context: Lines of context around matches

Returns:
    SearchResult with matches or file list
ztest -e z! && echo exists || echo not_found	not_foundzPath not found: z3. Verify the path exists (use 'terminal' to check).rQ   r   r   )r1  r   r8  r   r   _search_files_search_content)
rA   r   r   r   r   r   r   r   r   checks
   &&&&&&&&& r   r   ShellFileOperations.searchI  s    (   & 

Xd&<&<T&B%CCdef%,,&(.ab 
 W%%gUCC''y(3> >r   c          
      8   < V ^8  d   QhRS[ RS[ RS[RS[RS[/# r   r   r   r   r   r   r   rS   r   )r   r9   s   "r   r   r   m  s1     1
 1
S 1
 1
C 1
 1
Q] 1
r   c                   VP                  R4      '       g   RV9  d   TpMVP                  R4      R,          pV P                  R4      '       d   V P                  WRW44      # V P                  R4      '       g   \	        RR7      # RpRV P                  V4       R	V R
V P                  V4       RV^,            RV 2
pV P                  V^<R7      pVP                  P                  4       '       gP   RV P                  V4       R	V R
V P                  V4       RW4,            RV^,            2
p	V P                  V	^<R7      p. p
VP                  P                  4       P                  R4       F  pV'       g   K  VP                  R	^4      p\        V4      ^8X  dH   V^ ,          P                  RR4      P                  4       '       d   V
P                  V^,          4       Kv  V
P                  V4       K  	  \	        V
\        V
4      R7      # )z-Search for files by name pattern (glob-like).z**/r   rgr'  zFile search requires 'rg' (ripgrep) or 'find'. Install ripgrep for best results: https://github.com/BurntSushi/ripgrep#installationrP  z-not -path '*/.*'zfind  z -type f -name z6 -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +z | head -n rc   2>/dev/null | head -n z | tail -n +r  r   r   r   r   )r,   r  r   _search_files_rgr   r8  r   r   r   r	  r7  isdigitr  )rA   r   r   r   r   search_patternhidden_excluder   ru   
cmd_simpler   r   partss   &&&&&        r   r  !ShellFileOperations._search_filesm  s    !!%((S-?$N$]]3/3N
 T""((uMM   ((K  -d,,T231^4DOTXTjTjkyTzS{ |GGMPQzlR]^c]df C,}}""$$ !7!7!= >a?O_c_u_u  wE  `F  _G G1160@VVWZLZJZZ
BZ7FMM'')//5DJJsA&E5zQ58#3#3C#<#D#D#F#FU1X&T" 6 E

 	
r   c          
      8   < V ^8  d   QhRS[ RS[ RS[RS[RS[/# r  r  )r   r9   s   "r   r   r     s1     
 
 
3 
s 
C 
T` 
r   c                   RV9  d   VP                  R4      '       g   RV 2pMTpW4,           pRV P                  V4       RV P                  V4       RV 2pV P                  V^<R7      pVP                  P	                  4       P                  R4       U	u. uF  q'       g   K  V	NK  	  p
p	WWC,            p\        V\        V
4      \        V
4      V8  R7      # u up	i )	zSearch for files by name using ripgrep's --files mode.

rg --files respects .gitignore and excludes hidden directories by
default, and uses parallel directory traversal for ~200x speedup
over find on wide trees.
r   *zrg --files -g r  r  rc  r  )r   r   rJ   )r,   r8  r   r   r   r  r   r	  )rA   r   r   r   r   glob_patternfetch_limitr   ru   rw  	all_filespages   &&&&&       r   r  $ShellFileOperations._search_files_rg  s     gg&8&8&=&=wi=L"LnT33LAB!%%d+, -$' 	
 C, & 3 3 5 ; ;D AG A1QQQ A	G/I)n3
 	
 Hs   	C$Cc                Z   < V ^8  d   QhRS[ RS[ RS[S[ ,          RS[RS[RS[ RS[RS[/# 	r   r   r   r   r   r   r   r   r   r   )r   r9   s   "r   r   r     sN      s # (3- ",/>ALOT`r   c           	         V P                  R4      '       d   V P                  WW4VWg4      # V P                  R4      '       d   V P                  WW4VWg4      # \        RR7      # )z,Search for content inside files (grep-like).r  grepzqContent search requires ripgrep (rg) or grep. Install ripgrep: https://github.com/BurntSushi/ripgrep#installationrP  )r   _search_with_rg_search_with_grepr   )rA   r   r   r   r   r   r   r   s   &&&&&&&&r   r  #ShellFileOperations._search_content  su     T""''y(3> >v&&))'6*5@ @  \ r   c                Z   < V ^8  d   QhRS[ RS[ RS[S[ ,          RS[RS[RS[ RS[RS[/# r  r   )r   r9   s   "r   r   r     sX     a as a# a(3- a"a,/a>AaLOaT`ar   c                <	   . ROpV^ 8  d   VP                  R\        V4      .4       V'       d#   VP                  RV P                  V4      .4       VR8X  d   VP                  R4       MVR8X  d   VP                  R4       VP                  V P                  V4      4       VP                  V P                  V4      4       V^ 8  d   WE,           ^,           MWE,           p	VP                  RRR	\        V	4      .4       R
P	                  V4      p
V P                  V
^<R7      pVP                  ^8X  dq   VP                  P                  4       '       gQ   \        VR4      '       d-   VP                  '       d   VP                  P                  4       MRp\        RV 2^ R7      # VR8X  dd   VP                  P                  4       P                  R4       Uu. uF  q'       g   K  VNK  	  pp\        V4      pWWT,            p\        VVR7      # VR8X  d   / pVP                  P                  4       P                  R4       FM  pRV9   g   K  VP                  R^4      p\        V4      ^8X  g   K0   \        V^,          4      VV^ ,          &   KO  	  \        V\#        VP%                  4       4      R7      # \&        P(                  ! R4      p\&        P(                  ! R4      p. pVP                  P                  4       P                  R4       EFH  pV'       d   VR8X  d   K  VP+                  V4      pV'       d}   TP                  \-        VP/                  ^4      ;'       g    RVP/                  ^4      ,           \        VP/                  ^4      4      VP/                  ^4      R,          R7      4       K  V^ 8  g   K  VP+                  V4      pV'       g   K  TP                  \-        VP/                  ^4      ;'       g    RVP/                  ^4      ,           \        VP/                  ^4      4      VP/                  ^4      R,          R7      4       EKK  	  \        V4      pVWUV,            p\        VVWV,           8  R7      # u upi   \          d     EKZ  i ; i)zSearch using ripgrep.-Cz--glob
files_only-lcount-cr  head-nr  rc  stderrSearch errorSearch failed: r  r  r  :r   r   ^([A-Za-z]:)?(.*?):(\d+):(.*)$^([A-Za-z]:)?(.*?)-(\d+)-(.*)$--r   Ni  Nr   r{   rG   r   r   rJ   )r  z--line-numberz--no-headingz--with-filename)extendr   r8  r  r  r   r   r   r   hasattrr  r   r  r	  rsplitrS   rT  r  valuesr(  compilematchry   grouprA   r   r   r   r   r   r   r   	cmd_partsr  r   ru   	error_msgrw  r  totalr  r   r   r  	_match_re_ctx_rer   r   s   &&&&&&&&                r   r  #ShellFileOperations._search_with_rg  s    O	 Q;dCL12 h(>(>y(IJK ,&T"G#T" 	//89//56
 /6kens*u~#vtS-=>?hhy!C, q )<)<)>)>181J1Jv}}}++-bpI	{&CQRSS ,&$*MM$7$7$9$?$?$EK$Eq$EIK	NEFN3Dd>>G#F++-33D9$; KKQ/E5zQ!/258}F58, :  v3v}};OPP 

#DEIjj!BCGG++-33D9tt| OOD)NN;ggaj..B!''!*<$'
O !
4 0$ 
  Q;d+Aq{"#''!*"2"2aggaj!@(+AGGAJ$%GGAJt$4( ' :2 LE65.1D!5.0 m L  * ! !s   	R$R1RRRc                Z   < V ^8  d   QhRS[ RS[ RS[S[ ,          RS[RS[RS[ RS[RS[/# r  r   )r   r9   s   "r   r   r   3  sX     _ _ _C _HSM _!$_.1_@C_NQ_Vb_r   c                R	   RR.pVP                  R4       V^ 8  d   VP                  R\        V4      .4       V'       d#   VP                  RV P                  V4      .4       VR8X  d   VP                  R4       MVR8X  d   VP                  R	4       VP                  V P                  V4      4       VP                  V P                  V4      4       WE,           V^ 8  d   ^M^ ,           p	VP                  R
RR\        V	4      .4       RP	                  V4      p
V P                  V
^<R7      pVP                  ^8X  dq   VP                  P                  4       '       gQ   \        VR4      '       d-   VP                  '       d   VP                  P                  4       MRp\        RV 2^ R7      # VR8X  dd   VP                  P                  4       P                  R4       Uu. uF  q'       g   K  VNK  	  pp\        V4      pWWT,            p\        VVR7      # VR8X  d   / pVP                  P                  4       P                  R4       FM  pRV9   g   K  VP                  R^4      p\        V4      ^8X  g   K0   \        V^,          4      VV^ ,          &   KO  	  \        V\#        VP%                  4       4      R7      # \&        P(                  ! R4      p\&        P(                  ! R4      p. pVP                  P                  4       P                  R4       EFH  pV'       d   VR8X  d   K  VP+                  V4      pV'       d}   TP                  \-        VP/                  ^4      ;'       g    RVP/                  ^4      ,           \        VP/                  ^4      4      VP/                  ^4      R,          R7      4       K  V^ 8  g   K  VP+                  V4      pV'       g   K  TP                  \-        VP/                  ^4      ;'       g    RVP/                  ^4      ,           \        VP/                  ^4      4      VP/                  ^4      R,          R7      4       EKK  	  \        V4      pVWUV,            p\        VVWV,           8  R7      # u upi   \          d     EKZ  i ; i)zFallback search using grep.r  z-rnHz--exclude-dir='.*'r  z	--includer  r  r  r  r  r  r  r  rc  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  )r  r  r   r8  r  r   r   r   r   r  r  r   r  r	  r  rS   rT  r  r  r(  r  r  ry   r  r  s   &&&&&&&&                r   r  %ShellFileOperations._search_with_grep3  s    V$	 	-. Q;dCL12 k4+A+A)+LMN ,&T"G#T" 	//89//56 nw{B#vtS-=>?hhy!C, q )<)<)>)>181J1Jv}}}++-bpI	{&CQRSS,&$*MM$7$7$9$?$?$EK$Eq$EIK	NEFN3Dd>>G#F++-33D9$; KKQ/E5zQ!/258}F58, :  v3v}};OPP 

#DEIjj!BCGG++-33D9tt|OOD)NN;ggaj..B!''!*<$'
O !
4 0$ 
 Q;d+Aq{"#''!*"2"2aggaj!@(+AGGAJ$%GGAJt$4( ! :. LE65.1D!5.0 g L  * ! !s   !	R/R<RR&%R&)r   r   r   r<   )NNN)r   r   i   r   r   )rU   rV   rW   rX   rY   r   r   r   r  r  r"  r1  r8  rG  r   re  rk  rS  r   r   r   r  r   r  r  r  r  r  r\   r]   r^   s   @r   r   r   A  s     2 2(
 
(( ( (' '
	# 	## #J7 7
	 	W
 W
v !O?
 ?
B
 
:4
 4
t;
 ;
z <
 
H"> ">H1
 1
f
 
> "a aF_ _r   r   >   r   r   r   r   r   r   r   i   )-rY   r   r(  rC  abcr   r   dataclassesr   r   typingr   r   r   r	   pathlibr
   hermes_constantsr   r   r*  _HOMEr   r  r    r*   r-   r+   r$   r1   r3   r`   rj   ry   r   r   r   r   r  r  r  rR  r  rU  r   )ps   0r   <module>r     se  4 
 	  # ( , ,  , 	DIIK 	UF$56
UFH-
UFL1
UFH-O&'
UI&
UH%
UJ'
UO,
UK(
UH%
UI&
UH%
UI&#""ABGGQ " 0 	UF#
UF#
UH%
UG$
UI&
UH%
UIt,
+
+QBGGQ"&&   
+ "0 U U U& I I I   6      6 
 
 
    S J
#%,.46<>EGM 
 
 
 
 #
 %+
 -3
 5<
 >D
 FM    " $) +0 28    $ &- /5 7>    $ &* ,0 28 :@ BJ      & (.  
!  !  !! ( N  
-	%	)		( 	Q. Qk,s   $O;65P 