
    iG                        d Z ddlZddlZddlZddlmZ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 ddlmZ ddlmZ  e ej                              Zd	 ej                            ed
d          ej                            ed
d          ej                            ed
d          ej                            ed
d           e e            dz            ej                            ed          ej                            ed          ej                            ed          ej                            ed          ej                            ed          ej                            ed          ej                            ed          ej                            ed          ej                            ed          dddfD             Zd ej                            ed
          ej                            ed          ej                            ed          ej                            ed          d d!ej                            ed"          ej                            ed#          ej                            ed$d%          f	D             Zd&ee         fd'Zd(ed&efd)Ze G d* d+                      Ze G d, d-                      Z e G d. d/                      Z!e G d0 d1                      Z"e G d2 d3                      Z#e G d4 d5                      Z$e G d6 d7                      Z% G d8 d9e          Z&h d:Z'd;d<d=d>d?d@Z(dAZ)dAZ*dBZ+ G dC dDe&          Z,dS )EaL  
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_home)BINARY_EXTENSIONSc                 L    h | ]!}t           j                            |          "S  )ospathrealpath.0ps     =/home/agentuser/.hermes/hermes-agent/tools/file_operations.py	<setcomp>r   -   s5       BGQ      z.sshauthorized_keysid_rsa
id_ed25519configz.envz.bashrcz.zshrcz.profilez.bash_profilez	.zprofilez.netrcz.pgpassz.npmrcz.pypircz/etc/sudoersz/etc/passwdz/etc/shadowc                 f    g | ].}t           j                            |          t           j        z   /S r   )r   r   r   sepr   s     r   
<listcomp>r   C   s<       %&BGQ"&   r   z.awsz.gnupgz.kubez/etc/sudoers.dz/etc/systemdz.dockerz.azurez.configghreturnc                      t          j        dd          } | sdS 	 t           j                            t           j                            |                     S # t
          $ r Y dS w xY w)a\  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)r   getenvr   r   
expanduser	Exception)roots    r   _get_safe_write_rootr)   R   sp     9-r22D tw 2 24 8 8999   tts   ;A 
A%$A%r   c                 f   t           j                            t           j                            t	          |                               }|t
          v rdS t          D ]}|                    |          r dS t                      }|r*||k    s$|                    |t           j	        z             sdS dS )z.Return True if path is on the write deny list.TF)
r   r   r   r&   strWRITE_DENIED_PATHSWRITE_DENIED_PREFIXES
startswithr)   r   )r   resolvedprefix	safe_roots       r   _is_write_deniedr2   c   s    w 2 23t99 = =>>H %%%t'  v&& 	44	 %&&I I%%)<)<Y=O)P)P%45r   c                   $   e Zd ZU dZdZeed<   dZeed<   dZ	eed<   dZ
eed<   d	Zee         ed
<   dZeed<   dZeed<   d	Zee         ed<   d	Zee         ed<   d	Zee         ed<   d	Zee         ed<    ee          Zee         ed<   defdZd	S )
ReadResultzResult from reading a file.r$   contentr   total_lines	file_sizeF	truncatedNhint	is_binaryis_imagebase64_content	mime_type
dimensionserrordefault_factorysimilar_filesr!   c                 H    d | j                                         D             S )Nc                 *    i | ]\  }}||g k    ||S Nr   r   kvs      r   
<dictcomp>z&ReadResult.to_dict.<locals>.<dictcomp>   s(    TTTA!-AQSGG1GGGr   __dict__itemsselfs    r   to_dictzReadResult.to_dict   s$    TT!4!4!6!6TTTTr   )__name__
__module____qualname____doc__r5   r+   __annotations__r6   intr7   r8   boolr9   r   r:   r;   r<   r=   r>   r?   r   listrB   r   dictrO   r   r   r   r4   r4   {   s#        %%GSKIsItD(3-ItHd$(NHSM(((#Ix}### $J$$$E8C=$uT:::M49:::U U U U U U Ur   r4   c                   p    e Zd ZU dZdZeed<   dZeed<   dZ	e
e         ed<   dZe
e         ed<   d	efd
ZdS )WriteResultzResult from writing a file.r   bytes_writtenFdirs_createdNr?   warningr!   c                 H    d | j                                         D             S )Nc                     i | ]
\  }}|||S rE   r   rF   s      r   rI   z'WriteResult.to_dict.<locals>.<dictcomp>   s    HHHA!-1---r   rJ   rM   s    r   rO   zWriteResult.to_dict   s$    HH!4!4!6!6HHHHr   )rP   rQ   rR   rS   r[   rU   rT   r\   rV   r?   r   r+   r]   rX   rO   r   r   r   rZ   rZ      s         %%M3L$E8C=!GXc]!!!I I I I I I Ir   rZ   c                   
   e Zd ZU dZdZeed<   dZeed<    e	e
          Zee         ed<    e	e
          Zee         ed<    e	e
          Zee         ed	<   d
Zeeeef                  ed<   d
Zee         ed<   defdZd
S )PatchResultzResult from patching a file.Fsuccessr$   diffr@   files_modifiedfiles_createdfiles_deletedNlintr?   r!   c                     d| j         i}| j        r
| j        |d<   | j        r
| j        |d<   | j        r
| j        |d<   | j        r
| j        |d<   | j        r
| j        |d<   | j        r
| j        |d<   |S )Nrb   rc   rd   re   rf   rg   r?   )rb   rc   rd   re   rf   rg   r?   rN   results     r   rO   zPatchResult.to_dict   s    T\*9 	'!YF6N 	;'+':F#$ 	9&*&8F?# 	9&*&8F?#9 	'!YF6N: 	)"jF7Or   )rP   rQ   rR   rS   rb   rV   rT   rc   r+   r   rW   rd   r   re   rf   rg   r   r	   r
   r?   rX   rO   r   r   r   ra   ra      s         &&GTD#NNN %d ; ; ;NDI;;;$uT:::M49:::$uT:::M49:::%)D(4S>
")))E8C=      r   ra   c                   @    e Zd ZU dZeed<   eed<   eed<   dZeed<   dS )SearchMatchzA single search match.r   line_numberr5   g        mtimeN)	rP   rQ   rR   rS   r+   rT   rU   rn   floatr   r   r   rl   rl      sF           
IIILLLE5r   rl   c                       e Zd ZU dZ ee          Zee         e	d<    ee          Z
ee         e	d<    ee          Zeeef         e	d<   dZee	d<   dZee	d	<   d
Zee         e	d<   defdZd
S )SearchResultzResult from searching.r@   matchesfilescountsr   total_countFr8   Nr?   r!   c                     d| j         i}| j        rd | j        D             |d<   | j        r
| j        |d<   | j        r
| j        |d<   | j        rd|d<   | j        r
| j        |d<   |S )	Nru   c                 8    g | ]}|j         |j        |j        d S ))r   liner5   r   rm   r5   )r   ms     r   r   z(SearchResult.to_dict.<locals>.<listcomp>   s8     ! ! ! 19MM! ! !r   rr   rs   rt   Tr8   r?   )ru   rr   rs   rt   r8   r?   ri   s     r   rO   zSearchResult.to_dict   s    !12< 	! !! ! !F9 : 	)"jF7O; 	+#{F8> 	'"&F;: 	)"jF7Or   )rP   rQ   rR   rS   r   rW   rr   r   rl   rT   rs   r+   rX   rt   r	   rU   ru   r8   rV   r?   r   rO   r   r   r   rq   rq      s           !&t!<!<!<GT+<<<uT222E49222"U4888FDcN888KItE8C=      r   rq   c                   X    e Zd ZU dZdZeed<   dZeed<   dZe	ed<   dZ
e	ed<   d	efd
ZdS )
LintResultzResult from linting a file.Trb   Fskippedr$   outputmessager!   c                 J    | j         r
d| j        dS | j        rdnd| j        dS )Nr}   )statusr   okr?   )r   r~   )r}   r   rb   r~   rM   s    r   rO   zLintResult.to_dict   s?    < 	B'DLAAA"l7ddk
 
 	
r   N)rP   rQ   rR   rS   rb   rV   rT   r}   r~   r+   r   rX   rO   r   r   r   r|   r|      sw         %%GTGTFCGS
 
 
 
 
 
 
r   r|   c                   0    e Zd ZU dZdZeed<   dZeed<   dS )ExecuteResultz&Result from executing a shell command.r$   stdoutr   	exit_codeN)	rP   rQ   rR   rS   r   r+   rT   r   rU   r   r   r   r   r      s8         00FCIsr   r   c                   d   e Zd ZdZed!dedededefd            Zededefd	            Z	eded
ede
fd            Ze	 d"dededededef
d            Zededefd            Zedede
fd            Zededede
fd            Ze	 	 	 d#dedededee         dededededefd             ZdS )$FileOperationsz@Abstract interface for file operations across terminal backends.     r   offsetlimitr!   c                     dS )z$Read a file with pagination support.Nr   )rN   r   r   r   s       r   	read_filezFileOperations.read_file   	     	r   c                     dS )a
  Read the complete file content as a plain string.

        No pagination, no line-number prefixes, no per-line truncation.
        Returns ReadResult with .content = full file text, .error set on
        failure. Always reads to EOF regardless of file size.
        Nr   rN   r   s     r   read_file_rawzFileOperations.read_file_raw   s	     	r   r5   c                     dS )z8Write content to a file, creating directories as needed.Nr   )rN   r   r5   s      r   
write_filezFileOperations.write_file
  r   r   F
old_string
new_stringreplace_allc                     dS )z,Replace text in a file using fuzzy matching.Nr   )rN   r   r   r   r   s        r   patch_replacezFileOperations.patch_replace  s	     	r   patch_contentc                     dS )zApply a V4A format patch.Nr   )rN   r   s     r   	patch_v4azFileOperations.patch_v4a  r   r   c                     dS )z>Delete a file. Returns WriteResult with .error set on failure.Nr   r   s     r   delete_filezFileOperations.delete_file  r   r   srcdstc                     dS )zSMove/rename a file from src to dst. Returns WriteResult with .error set on failure.Nr   )rN   r   r   s      r   	move_filezFileOperations.move_file  r   r   .N2   r   patterntarget	file_globoutput_modecontextc	                     dS )zSearch for content or files.Nr   )	rN   r   r   r   r   r   r   r   r   s	            r   searchzFileOperations.search$  s	    
 	r   r   r   Fr   r5   Nr   r   r5   r   )rP   rQ   rR   rS   r   r+   rU   r4   r   r   rZ   r   rV   ra   r   r   r   r   r   rq   r   r   r   r   r   r      s       JJ c 3 3     ^ # *    ^ s S [    ^ */ # 3 C #'4?   ^
 s {    ^      ^ S s {    ^ BKOP<= c  C "3-7:IL69BN   ^  r   r   >   .bmp.gif.ico.jpg.png.jpeg.webpz python -m py_compile {file} 2>&1znode --check {file} 2>&1znpx tsc --noEmit {file} 2>&1zgo vet {file} 2>&1zrustfmt --check {file} 2>&1)z.pyz.jsz.tsz.goz.rsi  i   c                      e Zd ZdZd<defdZ	 	 d=dededededef
d	Zd
ede	fdZ
d<dedede	fdZdede	fdZd>dededefdZdedefdZdedefdZdedededefdZd?dedededefdZdedefdZdedefd Zdedefd!Zd"ed#edefd$Zdededefd%Z	 d@ded'ed(ed)e	def
d*Zd+edefd,Zdedefd-Z	 	 	 dAd1eded2ed3ee         deded4ed5edefd6Z d1ededededef
d7Z!d1ededededef
d8Z"d1eded3ee         deded4ed5edefd9Z#d1eded3ee         deded4ed5edefd:Z$d1eded3ee         deded4ed5edefd;Z%dS )BShellFileOperationsz
    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.
    Ncwdc                     || _         |p2t          |dd          p!t          t          |dd          dd          pd| _        i | _        dS )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)rN   terminal_envr   s      r   __init__zShellFileOperations.__init__J  se      
  V',t<< V7<4@@%NNVRU 	 02r   commandtimeout
stdin_datar!   c                     i }|r||d<   |||d<    | j         j        |fd|p| j        i|}t          |                    dd          |                    dd          	          S )
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   Nr   r   r~   r$   
returncoder   )r   r   )r   executer   r   get)rN   r   r   r   r   kwargsrj   s          r   _execzShellFileOperations._exec^  s      	( 'F9!#-F< !!'IIsdhI&II::h++jjq11
 
 
 	
r   cmdc                     || j         vr>|                     d| d          }|j                                        dk    | j         |<   | j         |         S )z6Check if a command exists in the environment (cached).zcommand -v z >/dev/null 2>&1 && echo 'yes'yes)r   r   r   strip)rN   r   rj   s      r   _has_commandz ShellFileOperations._has_commandr  s[    d)))ZZ Qc Q Q QRRF'-}':':'<'<'ED$"3''r   r   content_samplec                    t           j                            |          d                                         }|t          v rdS |rEt          d |dd         D                       }|t          t          |          d          z  dk    S dS )z
        Check if a file is likely binary.
        
        Uses extension check (fast) + content analysis (fallback).
        r   Tc              3   H   K   | ]}t          |          d k     |dvdV  dS )    z
	r   N)ord)r   cs     r   	<genexpr>z8ShellFileOperations._is_likely_binary.<locals>.<genexpr>  sJ        E  Ea"%a&&2++!82C2C !"2C2C2C2C E  Er   Ni  g333333?F)r   r   splitextlowerr   summinlen)rN   r   r   extnon_printables        r   _is_likely_binaryz%ShellFileOperations._is_likely_binaryy  s     gt$$Q'--//###4  	I  E  E>%4%+@  E  E  E E EM 3s>':':D#A#AADHHur   c                     t           j                            |          d                                         }|t          v S )z2Check if file is an image we can return as base64.r   )r   r   r   r   IMAGE_EXTENSIONS)rN   r   r   s      r   	_is_imagezShellFileOperations._is_image  s4    gt$$Q'--//&&&r   r   r5   
start_linec                    |                     d          }g }t          ||          D ]J\  }}t          |          t          k    r|dt                   dz   }|                    |dd|            Kd                    |          S )z7Add line numbers to content in LINE_NUM|CONTENT format.
)startNz... [truncated]6d|)split	enumerater   MAX_LINE_LENGTHappendjoin)rN   r5   r   linesnumberedirx   s          r   _add_line_numbersz%ShellFileOperations._add_line_numbers  s    d## j999 	. 	.GAt4yy?**,_,-0AAOOq,,,d,,----yy"""r   c                    |s|S |                     d          r9|                     d          }|j        dk    r|j                                        r|j                                        }|dk    r|S |                     d          r||dd         z   S |dd         }|                    d          }|dk    r
|d|         n|}|rt          j        d|          rt|                     d	|           }|j        dk    rQ|j                                        r8|j                                        }|dt          |          z   d         }	||	z   S |S )
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 $HOMEr   z~/r   Nr   z[a-zA-Z0-9._-]+zecho ~)	r.   r   r   r   r   findre	fullmatchr   )
rN   r   rj   homerest	slash_idxusernameexpand_result	user_homesuffixs
             r   _expand_pathz ShellFileOperations._expand_path  sr     	K ??3 	2ZZ--F1$$)<)<)>)>$}**,,3;;K__T** +$qrr(?* ABBx IIcNN	/8A~~4

++4 2-? J J 2 %)JJ/B/B/B$C$CM$.!338L8R8R8T8T3$1$8$>$>$@$@	!%a#h--&7&8&8!9(611r   argc                 :    d|                     dd          z   dz   S )z/Escape a string for safe use in shell commands.'z'"'"')replace)rN   r   s     r   _escape_shell_argz%ShellFileOperations._escape_shell_arg  s"     S[[i000366r   old_contentnew_contentfilenamec                     |                     d          }|                     d          }t          j        ||d| d|           }d                    |          S )z2Generate unified diff between old and new content.T)keependsza/zb/)fromfiletofiler$   )
splitlinesdifflibunified_diffr   )rN   r  r  r  	old_lines	new_linesrc   s          r   _unified_diffz!ShellFileOperations._unified_diff  sn    **D*99	**D*99	#y$(__"??
 
 

 wwt}}r   r   r   r   c           	         |                      |          }t          |t                    }d|                     |           d}|                     |          }|j        dk    r|                     |          S 	 t          |j        	                                          }n# t          $ r d}Y nw xY w|t          k    r	 |                     |          rt          dd|d          S d|                     |           d}|                     |          }|                     ||j                  rt          d|d	          S ||z   d
z
  }	d| d|	 d|                     |           }
|                     |
          }|j        dk    rt          d|j                   S d|                     |           }|                     |          }	 t          |j        	                                          }n# t          $ r d}Y nw xY w||	k    }d}|rd|	d
z    d| d|	 d| d	}t          |                     |j        |          ||||          S )a  
        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/nullr   TzImage file detected. Automatically redirected to vision_analyze tool. Use vision_analyze with this file path to inspect the image contents.)r;   r:   r7   r9   head -c 1000 zUBinary file - cannot display as text. Use appropriate tools to handle this file type.r:   r7   r?   r   zsed -n ',zp' Failed to read file: r?   zwc -l < NzUse offset=z to continue reading (showing -z of z lines))r5   r6   r7   r8   r9   )r   r   	MAX_LINESr   r   r   _suggest_similar_filesrU   r   r   
ValueErrorMAX_FILE_SIZEr   r4   r   r   )rN   r   r   r   stat_cmdstat_resultr7   
sample_cmdsample_resultend_lineread_cmdread_resultwc_cmd	wc_resultr6   r8   r9   s                    r   r   zShellFileOperations.read_file  s      && E9%% Id44T::HHHjj** A%%..t444	K.446677II 	 	 	III	 }$$ >>$ 		#\    PT%;%;D%A%AOOO


:..!!$(<== 	#m    E>A%RfRRxRRD4J4J44P4PRRjj** A%%$PK<N$P$PQQQQ ;D22488::JJv&&		i.446677KK 	 	 	KKK	  (*	 	yxAxxVxxV^xxdoxxxD**;+=vFF#
 
 
 	
s$   :&B! !B0/B0&G5 5HHc                    t           j                            |          pd}t           j                            |          }t           j                            |          d         }t           j                            |          d                                         }|                                }d|                     |           d}|                     |          }g }	|j        dk    r|j	        
                                r|j	        
                                                    d          D ]}
|
s|
                                }d}||k    rd}n:t           j                            |
          d                                         |                                k    rd}n|                    |          s|                    |          rd	}n||v rd
}n||v rt          |          dk    rd}n|rt           j                            |
          d                                         |k    r_t          |          t          |          z  }t          |          t          t          |          t          |                    dz  k    rd}|dk    r5|	                    |t           j                            ||
          f           |	                    d            d |	dd         D             }t'          d| |          S )z;Suggest similar files when the requested file is not found.r   r   r   ls -1 z 2>/dev/null | head -50r   d   Z   F   <      (   g?   c                     | d          S )Nr   r   )xs    r   <lambda>z<ShellFileOperations._suggest_similar_files.<locals>.<lambda>Y  s    1Q4% r   )keyc                     g | ]\  }}|S r   r   )r   _fps      r   r   z>ShellFileOperations._suggest_similar_files.<locals>.<listcomp>Z  s    ...%!R2...r   N   zFile not found: )r?   rB   )r   r   dirnamebasenamer   r   r   r   r   r   r   r   r.   r   setmaxr   r   sortr4   )rN   r   dir_pathr  basename_no_extr   
lower_namels_cmd	ls_resultscoredflfscorecommonsimilars                  r   r  z*ShellFileOperations._suggest_similar_files-  s   7??4((/C7##D))'**844Q7gx((+1133^^%%
 T$00::SSSJJv&&	!##	(8(>(>(@(@#%++--33D99 F F WWYY ##EEW%%a((+11337L7L7N7NNNEE]]:.. #*2G2G2K2K #EE2%%EE:%%#b''A++EE #RW--a00399;;sBB __s2ww6F6{{c#j//3r77&C&Cc&III "199MM5"',,x*C*C"DEEE(((..6"1":...+T++!
 
 
 	
r   c                    |                      |          }d|                     |           d}|                     |          }|j        dk    r|                     |          S 	 t          |j                                                  }n# t          $ r d}Y nw xY w| 	                    |          rt          dd|          S |                     d|                     |           d          }|                     ||j                  rt          d|d          S |                     d	|                     |                     }|j        dk    rt          d
|j                   S t          |j        |          S )zRead the complete file content as a plain string.

        No pagination, no line-number prefixes, no per-line truncation.
        Uses cat so the full file is returned regardless of size.
        r  r  r   T)r;   r:   r7   r  u'   Binary file — cannot display as text.r  cat r  r  )r5   r7   )r   r   r   r   r  rU   r   r   r  r   r4   r   )rN   r   r  r  r7   r  
cat_results          r   r   z!ShellFileOperations.read_file_rawa  s      &&Hd44T::HHHjj** A%%..t444	K.446677II 	 	 	III	>>$ 	RttyQQQQ

#]43I3I$3O3O#]#]#]^^!!$(<== 	)?    ZZ Et'='=d'C'C E EFF
1$$$OJ<M$O$OPPPP*"3yIIIIs   %&B BBc                 0   |                      |          }t          |          rt          d| d          S |                     d|                     |                     }|j        dk    rt          d| d|j                   S t                      S )zDelete a file via rm.zDelete denied:  is a protected pathr  zrm -f r   zFailed to delete : r   r2   rZ   r   r   r   r   )rN   r   rj   s      r   r   zShellFileOperations.delete_file}  s      &&D!! 	S%Qt%Q%Q%QRRRRCT%;%;D%A%ACCDDq  %P%P%P%P%PQQQQ}}r   r   r   c                    |                      |          }|                      |          }||fD ]'}t          |          rt          d| d          c S (|                     d|                     |           d|                     |                     }|j        dk    rt          d| d| d	|j                   S t                      S )
zMove a file via mv.zMove denied: rI  r  zmv  r   zFailed to move z -> rJ  rK  )rN   r   r   r   rj   s        r   r   zShellFileOperations.move_file  s    $$$$s 	R 	RA"" R")P)P)P)PQQQQQQRM$((--MM0F0Fs0K0KMM
 
 q  %Vs%V%V%V%Vv}%V%VWWWW}}r   c                    |                      |          }t          |          rt          d| d          S t          j                            |          }d}|r:d|                     |           }|                     |          }|j        dk    rd}d|                     |           }|                     ||	          }|j        dk    rt          d
|j	                   S d|                     |           d}	|                     |	          }
	 t          |
j	                                                  }n2# t          $ r% t          |                    d                    }Y nw xY wt          ||          S )u  
        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.r  Fz	mkdir -p r   Tzcat > )r   zFailed to write file: r  r  zutf-8)r[   r\   )r   r2   rZ   r   r   r5  r   r   r   r   rU   r   r  r   encode)rN   r   r5   parentr\   	mkdir_cmdmkdir_result	write_cmdwrite_resultr  r  r[   s               r   r   zShellFileOperations.write_file  s       && D!! 	g%et%e%e%effff && 	$DD$:$:6$B$BDDI::i00L%**# <T33D99;;	zz)z@@!Q&&%Sl>Q%S%STTTT Id44T::HHHjj**	9 2 8 8 : :;;MM 	9 	9 	9w 7 788MMM	9 '%
 
 
 	
s   &D> >,E-,E-Fr   r   r   c                    |                      |          }t          |          rt          d| d          S d|                     |           d}|                     |          }|j        dk    rt          d|           S |j        }ddlm}  |||||          \  }	}
}}|rt          |          S |
dk    rt          d	|           S | 	                    ||	          }|j
        rt          d
|j
                   S |                     ||	|          }|                     |          }t          d||g|r|                                nd          S )ai  
        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
        rO  rP  r  rF  r  r   r  )fuzzy_find_and_replacez'Could not find match for old_string in zFailed to write changes: TN)rb   rc   rd   rg   )r   r2   ra   r   r   r   r   tools.fuzzy_matchrX  r   r?   r  _check_lintrO   )rN   r   r   r   r   r   r!  r5   rX  r  match_count	_strategyr?   rV  rc   lint_results                   r   r   z!ShellFileOperations.patch_replace  s      && D!! 	g%et%e%e%effff E$0066DDDjj** A%%%CT%C%CDDDD$ 	=<<<<<5K5KZ[6
 6
2[)U  	,U++++!%Ut%U%UVVVV t[99 	W%UAS%U%UVVVV !!';== &&t,, 6*5?$$&&&4	
 
 
 	
r   r   c                 t    ddl m}m}  ||          \  }}|rt          d|           S  |||           }|S )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
        r   )parse_v4a_patchapply_v4a_operationszFailed to parse patch: r  )tools.patch_parserr_  r`  ra   )rN   r   r_  r`  
operationsparse_errorrj   s          r   r   zShellFileOperations.patch_v4a  sm    ( 	MLLLLLLL"1/-"@"@
K 	N%L{%L%LMMMM &%j$77r   c                 d   t           j                            |          d                                         }|t          vrt          dd| d          S t          |         }|                                d         }|                     |          st          d| d          S |                    d| 	                    |                    }| 
                    |d	
          }t          |j        dk    |j                                        r|j                                        nd          S )z
        Run syntax check on a file after editing.
        
        Args:
            path: File path to lint
        
        Returns:
            LintResult with status and any errors
        r   TzNo linter for z files)r}   r   r   z not availablez{file}r,  r   r$   )rb   r~   )r   r   r   r   LINTERSr|   r   r   r   r   r   r   r   r   )rN   r   r   
linter_cmdbase_cmdr   rj   s          r   rZ  zShellFileOperations._check_lint.  s-    gt$$Q'--//gd4PS4P4P4PQQQQ S\
##%%a(  ** 	Qdx4O4O4OPPPP   4+A+A$+G+GHHC,,$),2M,?,?,A,AI6=&&(((r
 
 
 	
r   r   r   r   r   r   r   r   r   c	           	         |                      |          }|                     d|                     |           d          }	d|	j        v rt          j                            |          pd}
t          j                            |          }d| g}|                     d|                     |
           d          }d|j        v r=|r:|                     d	|                     |
           d
          }|j        dk    r|j        	                                r|
                                }g }|j        	                                                    d          D ]q}|s|
                                }||v s!||v s|                    |dd                   r3|                    t          j                            |
|                     r|r3|                    dd                    |dd                   z              t          d                    |          d          S |dk    r|                     ||||          S |                     |||||||          S )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_foundr   zPath not found: ztest -d z && echo yes || echo nor   r%  z 2>/dev/null | head -20r   r   N   zSimilar paths: z, r4  z. r?   ru   rs   )r   r   r   r   r   r   r5  r6  r   r   r   r   r.   r   r   rq   _search_files_search_content)rN   r   r   r   r   r   r   r   r   checkrR  basename_query
hint_partsparent_checkr>  lower_q
candidatesentryles                      r   r   zShellFileOperations.searchR  s   (   && 

ed&<&<T&B&Beeeff%,&&W__T**1cFW--d33N3T334J::R411&99RRR L ++++ JJTT33F;;TTT 	 &!++	0@0F0F0H0H+,2244G!#J!*!1!7!7!9!9!?!?!E!E K K$ %$"[[]]"b==B'MMR]]7SUTUSU;=W=WM&--bgll65.I.IJJJ! "))-		*RaR.0I0II    ii
++   
 W%%gtUFCCC''y%(3W> > >r   c                 H   |                     d          sd|vr|}n|                    d          d         }|                     d          r|                     ||||          S |                     d          st	          d          S d}d	|                     |           d
| d|                     |           d|dz    d| 
}|                     |d          }|j                                        sTd	|                     |           d
| d|                     |           d||z    d|dz    
}	|                     |	d          }g }
|j                                                            d          D ]}|s|                    d
d          }t          |          dk    rJ|d         
                    dd                                          r|
                    |d                    x|
                    |           t	          |
t          |
                    S )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#installationr  z-not -path '*/.*'zfind rM  z -type f -name z6 -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +r   z | head -n r)  re   2>/dev/null | head -n z | tail -n +r   r*  r   r   r$   rs   ru   )r.   r   r   _search_files_rgrq   r   r   r   r   r   r   isdigitr   )rN   r   r   r   r   search_patternhidden_excluder   rj   
cmd_simplers   rx   partss                r   rm  z!ShellFileOperations._search_files  s    !!%(( 	4S-?-?$NN$]]3//3N
 T"" 	N((ufMMM   (( 	K    -fd,,T22 f f^ f fTXTjTjkyTzTz f fGMPQzf f^cf f C,,}""$$ 	8Z!7!7!=!= Z Z Z Z_c_u_u  wE  `F  `F Z Z16Z ZMSVWZZ ZJZZ
BZ77FM''))//55 	# 	#D JJsA&&E5zzQ58#3#3C#<#<#D#D#F#FU1X&&&&T""""E


 
 
 	
r   c                    d|vr|                     d          sd| }n|}||z   }d|                     |           d|                     |           d| }|                     |d          }d |j                                                            d	          D             }	|	s~d
|                     |           d|                     |           d| }
|                     |
d          }d |j                                                            d	          D             }	|	|||z            }t          |t          |	          t          |	          |k              S )ad  Search 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.  Results are sorted by modification time
        (most recently edited first) when rg >= 13.0 supports --sortr.
        r   *zrg --files --sortr=modified -g rM  rz  r)  re  c                     g | ]}||S r   r   r   r@  s     r   r   z8ShellFileOperations._search_files_rg.<locals>.<listcomp>  s    GGG1QGQGGGr   r   zrg --files -g c                     g | ]}||S r   r   r  s     r   r   z8ShellFileOperations._search_files_rg.<locals>.<listcomp>      KKKqKKKKr   )rs   ru   r8   )r.   r   r   r   r   r   rq   r   )rN   r   r   r   r   glob_patternfetch_limit
cmd_sortedrj   	all_files	cmd_plainpages               r   r|  z$ShellFileOperations._search_files_rg  s    gg&8&8&=&=(w==LL"Lfn'd.D.D\.R.R ' '%%d++' '$' ' 	
 J33GG 3 3 5 5 ; ;D A AGGG	 	L+!7!7!E!E + +))$//+ +(+ + 
 ZZ	2Z66FKKFM$7$7$9$9$?$?$E$EKKKI./I)nn3
 
 
 	
r   c           	          |                      d          r|                     |||||||          S |                      d          r|                     |||||||          S t          d          S )z,Search for content inside files (grep-like).ry  grepzqContent search requires ripgrep (rg) or grep. Install ripgrep: https://github.com/BurntSushi/ripgrep#installationr  )r   _search_with_rg_search_with_greprq   )rN   r   r   r   r   r   r   r   s           r   rn  z#ShellFileOperations._search_content  s     T"" 	''y%(3W> > >v&& 	))'4E6*5w@ @ @  \   r   c                 	   g d}|dk    r$|                     dt          |          g           |r*|                     d|                     |          g           |dk    r|                    d           n|dk    r|                    d           |                    |                     |                     |                    |                     |                     |dk    r||z   d	z   n||z   }	|                     d
ddt          |	          g           d                    |          }
|                     |
d          }|j        dk    r_|j                                        sFt          |d          r |j
        r|j
                                        nd}t          d| d          S |dk    rcd |j                                                            d          D             }t          |          }||||z            }t          ||          S |dk    ri }|j                                                            d          D ]_}d|v rY|                    dd          }t          |          dk    r0	 t          |d                   ||d         <   O# t           $ r Y [w xY w`t          |t#          |                                                    S t'          j        d          }t'          j        d          }g }|j                                                            d          D ]W}|r|dk    r|                    |          }|r|                    t-          |                    d          pd|                    d          z   t          |                    d                    |                    d           d!d"         #                     |dk    r|                    |          }|r|                    t-          |                    d          pd|                    d          z   t          |                    d                    |                    d           d!d"         #                     Yt          |          }||||z            }t          |||||z   k    $          S )%zSearch using ripgrep.)ry  z--line-numberz--no-headingz--with-filenamer   -Cz--glob
files_only-lcount-c   r   head-nrM  r)  re  r*  stderrSearch errorSearch failed: rl  c                     g | ]}||S r   r   r  s     r   r   z7ShellFileOperations._search_with_rg.<locals>.<listcomp>%  r  r   r   r{  :r   rt   ru   ^([A-Za-z]:)?(.*?):(\d+):(.*)$^([A-Za-z]:)?(.*?)-(\d+)-(.*)$--r$   rk     Nr   ry   rr   ru   r8   )extendr+   r   r   r   r   r   r   r   hasattrr  rq   r   r   rsplitrU   r  r   valuesr   compilematchrl   grouprN   r   r   r   r   r   r   r   	cmd_partsr  r   rj   	error_msgr  totalr  rt   rx   r  	_match_re_ctx_rerr   rz   s                          r   r  z#ShellFileOperations._search_with_rg  s    ONN	 Q;;dCLL1222  	Lh(>(>y(I(IJKKK ,&&T""""G##T""" 	//88999//55666
 /6kkefns**uv~#vtS-=-=>???hhy!!C,, q  )<)<)>)> 181J1Jpv}p++---bpI&C	&C&CQRSSSS ,&&KKFM$7$7$9$9$?$?$E$EKKKI	NNEVFUN23Dd>>>>G##F++--33D99 ! !$;; KKQ//E5zzQ!/258}}F58,,) ! ! ! D!v3v}};O;OPPPP 
#DEEIj!BCCGG++--33D99   tt|| OOD)) NN;ggajj.B!''!**<$'

OO !

4C4 0$ $ $   
  Q;;d++A {"#''!**"2aggajj!@(+AGGAJJ$%GGAJJtt$4( ( (    LLE6&5.01D!&5.0   s   >J
J*)J*c                 	   ddg}|                     d           |dk    r$|                    dt          |          g           |r*|                    d|                     |          g           |dk    r|                     d           n|d	k    r|                     d
           |                     |                     |                     |                     |                     |                     ||z   |dk    rdndz   }	|                    dddt          |	          g           d                    |          }
|                     |
d          }|j        dk    r_|j                                        sFt          |d          r |j
        r|j
                                        nd}t          d| d          S |dk    rcd |j                                                            d          D             }t          |          }||||z            }t          ||          S |d	k    ri }|j                                                            d          D ]_}d|v rY|                    dd          }t          |          dk    r0	 t          |d                   ||d         <   O# t           $ r Y [w xY w`t          |t#          |                                                    S t'          j        d          }t'          j        d          }g }|j                                                            d          D ]W}|r|dk    r|                    |          }|r|                     t-          |                    d          pd |                    d          z   t          |                    d!                    |                    d"          d#d$         %                     |dk    r|                    |          }|r|                     t-          |                    d          pd |                    d          z   t          |                    d!                    |                    d"          d#d$         %                     Yt          |          }||||z            }t          |||||z   k    &          S )'zFallback search using grep.r  z-rnHz--exclude-dir='.*'r   r  z	--includer  r  r  r  r  r   r  r  rM  r)  re  r*  r  r  r  rl  c                     g | ]}||S r   r   r  s     r   r   z9ShellFileOperations._search_with_grep.<locals>.<listcomp>  r  r   r   r{  r  r   r  r  r  r  r$   rk  r  Nr   ry   r  )r   r  r+   r   r   r   r   r   r   r  r  rq   r   r   r  rU   r  r   r  r   r  r  rl   r  r  s                          r   r  z%ShellFileOperations._search_with_grepa  s    V$	 	-... Q;;dCLL1222  	Ok4+A+A)+L+LMNNN ,&&T""""G##T""" 	//88999//55666 fnw{{B#vtS-=-=>???hhy!!C,, q  )<)<)>)> 181J1Jpv}p++---bpI&C	&C&CQRSSSS,&&KKFM$7$7$9$9$?$?$E$EKKKI	NNEVFUN23Dd>>>>G##F++--33D99 ! !$;; KKQ//E5zzQ!/258}}F58,,) ! ! ! D!v3v}};O;OPPPP 
#DEEIj!BCCGG++--33D99   tt||OOD)) NN;ggajj.B!''!**<$'

OO !

4C4 0$ $ $   
 Q;;d++A {"#''!**"2aggajj!@(+AGGAJJ$%GGAJJtt$4( ( (    LLE6&5.01D!&5.0   s   J//
J<;J<rE   )NNN)r   r   r   r   )&rP   rQ   rR   rS   r+   r   rU   r   r   rV   r   r   r   r   r   r   r  r4   r   r  r   rZ   r   r   r   ra   r   r   r|   rZ  r   rq   r   rm  r|  rn  r  r  r   r   r   r   r   B  s         2 2# 2 2 2 2( CG $
 
S 
s 
C 

)6
 
 
 
(( ( ( ( ( ( c 3 $    $'c 'd ' ' ' '
	# 	# 	## 	#c 	# 	# 	# 	## # # # # #J7S 7S 7 7 7 7
	 	3 	# 	RU 	 	 	 	W
 W
c W
3 W
3 W
 W
 W
 W
 W
r2
3 2
: 2
 2
 2
 2
hJ# J* J J J J8     S s {    $4
s 4
S 4
[ 4
 4
 4
 4
v +0;
 ;
# ;
3 ;
C ;
#';
4?;
 ;
 ;
 ;
zs {    <
 

 
 
 
 
H CLOP<=;> ;>c ;> ;>C ;>"3-;>7:;>IL;>;>69;>BN;> ;> ;> ;>z1
S 1
 1
C 1
 1
Q] 1
 1
 1
 1
f)
 )
3 )
s )
C )
T` )
 )
 )
 )
Vs # (3- ",/>ALOT`   "as a# a(3- a"a,/a>AaLOaT`a a a aF_ _C _HSM _!$_.1_@C_NQ_Vb_ _ _ _ _ _r   r   )-rS   r   r   r	  abcr   r   dataclassesr   r   typingr   r   r	   r
   pathlibr   hermes_constantsr   tools.binary_extensionsr   r+   r   _HOMEr   r   r,   r-   r)   rV   r2   r4   rZ   ra   rl   rq   r|   r   r   r   rf  r  r   r  r   r   r   r   <module>r     s$   4 
			 				  # # # # # # # # ( ( ( ( ( ( ( ( , , , , , , , , , , , ,       , , , , , , 5 5 5 5 5 5 	IDIKK 
UF$566
UFH--
UFL11
UFH--OO&''
UI&&
UH%%
UJ''
UO,,
UK((
UH%%
UI&&
UH%%
UI&&#"   , 
UF##
UF##
UH%%
UG$$
UI&&
UH%%
UIt,,
+   hsm    "3 4    0 U U U U U U U U& I I I I I I I I        6                6 
 
 
 
 
 
 
 
         1 1 1 1 1S 1 1 1r NMM  .%)(  	~ ~ ~ ~ ~. ~ ~ ~ ~ ~r   