
    i              	          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mZmZmZ  e ej                              Z ee          Z ee          Z ej        d	          Z  ej        d
          Z!dede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 d d                      Z)e G d d                      Z*e G d d                      Z+e G d d                      Z,dede-ee.ef         dz  fd Z/ G d! d"e          Z0h d#Z1d$d%d&d'd(d)Z2d*ede-e$ef         fd+Z3d*ede-e$ef         fd,Z4d*ede-e$ef         fd-Z5d*ede-e$ef         fd.Z6e6e3e4e4e5d/Z7d0Z8d0Z9d1Z:d2Z;d3Z<dZ=d4Z>d5ed6e.de.fd7Z?e;e<fd8ed9ede-e.e.f         fd:Z@e=e>fd8ed9ede-e.e.f         fd;ZA G d< d=e0          ZBdS )>a\  
File Operations Module

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

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)BINARY_EXTENSIONS)build_write_denied_pathsbuild_write_denied_prefixesget_safe_write_rootis_write_deniedz!\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)z+'?\x07?__HERMES_FENCE_[A-Za-z0-9]+__\x07?'?textreturnc                 t   | s| S g }|                      d          D ]}d|v pd|v }t                              d|          }t                              d|          }|                    dd          }|r|                    d          dk    rr|                    |           d                    |          S )z;Strip leaked terminal fence wrappers from file read output.Tkeepends__HERMES_FENCE_z] z'
	 )
splitlines_OSC_SEQUENCE_REsub_FENCE_MARKER_REreplacestripappendjoin)r   cleaned_lineslinehad_terminal_wrappercleaneds        :/home/piyush/.hermes/hermes-agent/tools/file_operations.py_strip_terminal_fence_leaksr&   <   s     !M.. & &0D8KGtO"&&r400"&&r733//&"-- 	GMM*$=$=$C$CW%%%%77=!!!    c                      t                      S )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.
    )_shared_get_safe_write_root r'   r%   _get_safe_write_rootr+   M   s     '(((r'   pathc                      t          |           S )z.Return True if path is on the write deny list.)_shared_is_write_denied)r,   s    r%   _is_write_deniedr/   X   s    "4(((r'   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*   .0kvs      r%   
<dictcomp>z&ReadResult.to_dict.<locals>.<dictcomp>r   s(    TTTA!-AQSGG1GGGr'   __dict__itemsselfs    r%   to_dictzReadResult.to_dictq   s$    TT!4!4!6!6TTTTr'   )__name__
__module____qualname____doc__r2   str__annotations__r3   intr4   r5   boolr6   r   r7   r8   r9   r:   r;   r<   r   listr?   r   dictrM   r*   r'   r%   r1   r1   a   s#        %%GSKIsItD(3-ItHd$(NHSM(((#Ix}### $J$$$E8C=$uT:::M49:::U U U U U U Ur'   r1   c                       e Zd ZU dZdZeed<   dZeed<   dZ	e
eeef                  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_createdNlintr<   warningr   c                 H    d | j                                         D             S )Nc                     i | ]
\  }}|||S rB   r*   rC   s      r%   rG   z'WriteResult.to_dict.<locals>.<dictcomp>   s    HHHA!-1---r'   rH   rK   s    r%   rM   zWriteResult.to_dict~   s$    HH!4!4!6!6HHHHr'   )rN   rO   rP   rQ   rZ   rT   rS   r[   rU   r\   r   r	   rR   r
   r<   r]   rW   rM   r*   r'   r%   rY   rY   u   s         %%M3L$%)D(4S>
")))E8C=!GXc]!!!I I I I I I Ir'   rY   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_deletedNr\   r<   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   r\   r<   )rb   rc   rd   re   rf   r\   r<   rL   results     r%   rM   zPatchResult.to_dict   s    T\*9 	'!YF6N 	;'+':F#$ 	9&*&8F?# 	9&*&8F?#9 	'!YF6N: 	)"jF7Or'   )rN   rO   rP   rQ   rb   rU   rS   rc   rR   r   rV   rd   r   re   rf   r\   r   r	   r
   r<   rW   rM   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_numberr2   g        mtimeN)	rN   rO   rP   rQ   rR   rS   rT   rm   floatr*   r'   r%   rk   rk      sF           
IIILLLE5r'   rk   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_countFr5   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 )	Nrt   c                 8    g | ]}|j         |j        |j        d S ))r,   r"   r2   r,   rl   r2   )rD   ms     r%   
<listcomp>z(SearchResult.to_dict.<locals>.<listcomp>   s8     ! ! ! 19MM! ! !r'   rq   rr   rs   Tr5   r<   )rt   rq   rr   rs   r5   r<   rh   s     r%   rM   zSearchResult.to_dict   s    !12< 	! !! ! !F9 : 	)"jF7O; 	+#{F8> 	'"&F;: 	)"jF7Or'   )rN   rO   rP   rQ   r   rV   rq   r   rk   rS   rr   rR   rW   rs   r	   rT   rt   r5   rU   r<   r   rM   r*   r'   r%   rp   rp      s           !&t!<!<!<GT+<<<uT222E49222"U4888FDcN888KItE8C=      r'   rp   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                 p    | j         r
d| j        dS | j        rdnd| j        d}| j        r
| j        |d<   |S )Nr|   )statusr~   okr<   )r   r}   r~   )r|   r~   rb   r}   rh   s     r%   rM   zLintResult.to_dict   sR    < 	B'DLAAA$(L=DDgUU< 	- $F9r'   N)rN   rO   rP   rQ   rb   rU   rS   r|   r}   rR   r~   rW   rM   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)	rN   rO   rP   rQ   r   rR   rS   r   rT   r*   r'   r%   r   r      s8         00FCIsr'   r   r"   c                    | r| dk    rdS d}t          j        d|           D ]}|}|dS | d|                                         }|sdS |t          |                    d                    | |                                d         fS )a]  Parse grep/rg context output in ``path-line-content`` format.

    Context lines are ambiguous because filenames may legitimately contain
    ``-<digits>-`` segments. Prefer the rightmost numeric separator so a path
    like ``dir/file-12-name.py-8-context`` resolves to
    ``dir/file-12-name.py`` line ``8`` instead of truncating at ``file``.
    --Nz-(\d+)-   )refinditerstartrT   groupend)r"   match	candidater,   s       r%   _parse_search_context_liner      s      44<<tE[T22  	}tD tU[[^^$$d599;;<<&888r'   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     r,   offsetlimitr   c                     dS )z$Read a file with pagination support.Nr*   )rL   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*   rL   r,   s     r%   read_file_rawzFileOperations.read_file_raw   s	     	r'   r2   c                     dS )z8Write content to a file, creating directories as needed.Nr*   )rL   r,   r2   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*   )rL   r,   r   r   r   s        r%   patch_replacezFileOperations.patch_replace  s	     	r'   patch_contentc                     dS )zApply a V4A format patch.Nr*   )rL   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*   )rL   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*   )	rL   r   r,   r   r   r   r   r   r   s	            r%   searchzFileOperations.search$  s	    
 	r'   r   r   Fr   r2   Nr   r   r2   r   )rN   rO   rP   rQ   r   rR   rT   r1   r   r   rY   r   rU   ra   r   r   r   r   r   rp   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).pyz.jsz.tsz.goz.rsr2   c           	          ddl }	 |                    |            dS # |j        $ r'}dd|j         d|j         d|j         dfcY d}~S d}~wt          $ r%}dt          |          j         d	| fcY d}~S d}~ww xY w)
z;In-process JSON syntax check.  Returns (ok, error_message).r   NTr   FzJSONDecodeError:  (line 	, column ): )	jsonloadsJSONDecodeErrormsglinenocolno	ExceptiontyperN   )r2   _jsones      r%   _lint_json_inprocr   ?  s    1Gx  V V VU!%UUUU17UUUUUUUUUU 1 1 1a)00Q0000000001s,    
A;A	A;	A;A60A;6A;c                     	 ddl }n# t          $ r Y dS w xY w	 |                    |            dS # |j        $ r}dd| fcY d}~S d}~wt          $ r%}dt          |          j         d| fcY d}~S d}~ww xY w)u   In-process YAML syntax check.  Returns (ok, error_message).

    Skipped gracefully if PyYAML isn't installed — YAML parsing is optional.
    r   NT__SKIP__r   FzYAMLError: r   )yamlImportError	safe_load	YAMLErrorr   r   rN   )r2   _yamlr   s      r%   _lint_yaml_inprocr   K  s    
        1   x? ( ( ('A''''''''' 1 1 1a)00Q0000000001s8    
0 
A8A A8A8A3-A83A8c                     	 ddl }n(# t          $ r 	 ddl}n# t          $ r Y Y dS w xY wY nw xY w	 |                    |            dS # t          $ r%}dt          |          j         d| fcY d}~S d}~ww xY w)z<In-process TOML syntax check (stdlib tomllib, Python 3.11+).r   Nr   r   Fr   )tomllibr   tomlir   r   r   rN   )r2   _tomlr   s      r%   _lint_toml_inprocr   ^  s    $ $ $ $	$!!!!! 	$ 	$ 	$####	$ "!$1Gx 1 1 1a)00Q0000000001sA    
,,
&,&,,A 
A6A1+A61A6c                 >   ddl }	 |                    |            dS # t          $ rH}|j        rd|j         d|j         dnd}dt          |          j         d	|j         | fcY d}~S d}~wt          $ r%}dt          |          j         d	| fcY d}~S d}~ww xY w)
u   In-process Python syntax check via ast.parse.

    Catches SyntaxError, IndentationError, and everything else the
    ast module rejects — matching py_compile's scope but with no
    subprocess overhead and no dependency on a ``python`` in PATH.
    r   Nr   r   r   r   r   Fr   )	astparseSyntaxErrorr   r   r   rN   r   r   )r2   _astr   locs       r%   _lint_python_inprocr   o  s     1

7x : : ::;(J666186666a)99QU9C999999999 1 1 1a)00Q0000000001s,    
B=A*$B*B7BBB)r   z.jsonz.yamlz.ymlz.tomli  i   r   r   r   valuedefaultc                 T    	 t          |           S # t          t          f$ r |cY S w xY w)z8Best-effort integer coercion for tool pagination inputs.)rT   	TypeError
ValueError)r   r   s     r%   _coerce_intr     s<    5zzz"   s    ''r   r   c                     ddl m}  |            }t          dt          | t                              }t          |t
                    }t          dt          ||                    }||fS )a  Return safe read_file pagination bounds.

    Tool schemas declare minimum/maximum values, but not every caller or
    provider enforces schemas before dispatch. Clamp here so invalid values
    cannot leak into sed ranges like ``0,-1p``.

    The upper bound on ``limit`` comes from ``tool_output.max_lines`` in
    config.yaml (defaults to the module-level ``MAX_LINES`` constant).
    r   )get_max_linesr   )tools.tool_output_limitsr   maxr   DEFAULT_READ_OFFSETDEFAULT_READ_LIMITmin)r   r   r   	max_linesnormalized_offsetnormalized_limits         r%   normalize_read_paginationr     st     766666IA{63FGGHH"5*<==1c"2I>>??...r'   c                     t          dt          | t                              }t          dt          |t                              }||fS )zCReturn safe search pagination bounds for shell head/tail pipelines.r   r   )r   r   DEFAULT_SEARCH_OFFSETDEFAULT_SEARCH_LIMIT)r   r   r   r   s       r%   normalize_search_paginationr     sF     A{63HIIJJ1k%1EFFGG...r'   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dAd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dBd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	 dCded'ed(ed)e	def
d*Zd+edefd,Zd?dedee         defd-Z	 d?ded.ee         d/ee         defd0Z	 	 	 dDd4eded5ed6ee         deded7ed8ede fd9Z!d4edededede f
d:Z"d4edededede f
d;Z#d4eded6ee         deded7ed8ede fd<Z$d4eded6ee         deded7ed8ede fd=Z%d4eded6ee         deded7ed8ede fd>Z&dS )E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 )u  
        Initialize file operations with a terminal environment.

        Args:
            terminal_env: Any object with execute(command, cwd) method.
                         Returns {"output": str, "returncode": int}
            cwd: Optional explicit fallback cwd when the terminal env has
                 no cwd attribute (rare — most backends track cwd live).

        Note:
            Every _exec() call prefers the LIVE ``terminal_env.cwd`` over
            ``self.cwd`` so ``cd`` commands run via the terminal tool are
            picked up immediately.  ``self.cwd`` is only used as a fallback
            when the env has no cwd at all — it is NOT the authoritative
            cwd, despite being settable at init time.

            Historical bug (fixed): prior versions of this class used the
            init-time cwd for every _exec() call, which caused relative
            paths passed to patch/read/write to target the wrong directory
            after the user ran ``cd`` in the terminal.  Patches would
            claim success and return a plausible diff but land in the
            original directory, producing apparent silent failures.
        r   Nconfig/)envgetattrr   _command_cache)rL   terminal_envr   s      r%   __init__zShellFileOperations.__init__  se    0  
  V',t<< V7<4@@%NNVRU 	 02r'   commandtimeout
stdin_datar   c                     i }|r||d<   |||d<   |pt          | j        dd          p| j        } | j        j        |fd|i|}t	          |                    dd          |                    dd          	          S )
u  Execute 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.

        Cwd resolution order (critical — see class docstring):
          1. Explicit ``cwd`` arg (if provided)
          2. Live ``self.env.cwd`` (tracks ``cd`` commands run via terminal)
          3. Init-time ``self.cwd`` (fallback when env has no cwd attribute)

        This ordering ensures relative paths in file operations follow the
        terminal's current directory — not the directory this file_ops was
        originally created in.  See test_file_ops_cwd_tracking.py.
        r  Nr  r   r}   r   
returncoder   )r   r   )r  r  r   executer   get)rL   r  r   r  r  kwargseffective_cwdri   s           r%   _execzShellFileOperations._exec  s    "  	( 'F9!#-F<  Iwtx==I!!'GG}GGG::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   r   )rL   r  ri   s      r%   _has_commandz ShellFileOperations._has_command  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)rD   cs     r%   	<genexpr>z8ShellFileOperations._is_likely_binary.<locals>.<genexpr>  sJ        E  Ea"%a&&2++!82C2C !"2C2C2C2C E  Er'   Ni  g333333?F)osr,   splitextlowerr   sumr   len)rL   r,   r  extnon_printables        r%   _is_likely_binaryz%ShellFileOperations._is_likely_binary  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)rL   r,   r   s      r%   	_is_imagezShellFileOperations._is_image  s4    gt$$Q'--//&&&r'   r   r2   
start_linec                    ddl m}  |            }|                    d          }g }t          ||          D ]@\  }}t	          |          |k    r|d|         dz   }|                    |dd|            Ad                    |          S )	z7Add line numbers to content in LINE_NUM|CONTENT format.r   )get_max_line_length
)r   Nz... [truncated]6d|)r   r(  split	enumerater  r   r    )	rL   r2   r&  r(  max_line_lengthlinesnumberedir"   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 ~)	
startswithr  r   r   r   findr   	fullmatchr  )
rL   r,   ri   homerest	slash_idxusernameexpand_result	user_homesuffixs
             r%   _expand_pathz ShellFileOperations._expand_path1  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'"'"')r   )rL   r@  s     r%   _escape_shell_argz%ShellFileOperations._escape_shell_argV  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.Tr   za/zb/)fromfiletofiler   )r   difflibunified_diffr    )rL   rD  rE  rF  	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          ||          \  }}d|                     |           d}|                     |          }|j        dk    r|                     |          S t          |j                  }	 t          |	                                          }n# t          $ r d}Y nw xY w|t          k    r	 |                     |          rt          dd|d          S d|                     |           d}|                     |          }	t          |	j                  }
|                     ||
          rt          d|d	          S ||z   d
z
  }d| d| d|                     |           }|                     |          }|j        dk    rt          d|j                   S t          |j                  }d|                     |           }|                     |          }t          |j                  }	 t          |	                                          }n# t          $ r d}Y nw xY w||k    }d}|rd|d
z    d| d| d| d	}t          |                     ||          ||||          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.)r8   r7   r4   r6   head -c 1000 zUBinary file - cannot display as text. Use appropriate tools to handle this file type.r7   r4   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))r2   r3   r4   r5   r6   )r?  r   rC  r  r   _suggest_similar_filesr&   r   rT   r   r   MAX_FILE_SIZEr%  r1   r"  r2  )rL   r,   r   r   stat_cmdstat_resultstat_outputr4   
sample_cmdsample_resultsample_outputend_lineread_cmdread_resultread_outputwc_cmd	wc_result	wc_outputr3   r5   r6   s                        r%   r   zShellFileOperations.read_filej  s      &&1&%@@ Id44T::HHHjj** A%%..t4441+2DEE	K--//00II 	 	 	III	 }$$ >>$ 		#\    PT%;%;D%A%AOOO


:..3M4HII!!$66 	#m    E>A%RfRRxRRD4J4J44P4PRRjj** A%%$PK<N$P$PQQQQ1+2DEE ;D22488::JJv&&	/	0@AA		ioo//00KK 	 	 	KKK	  (*	 	yxAxxVxxV^xxdoxxxD**;??#
 
 
 	
s$   !B. .B=<B=!H4 4II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>  s    1Q4% r'   )keyc                     g | ]\  }}|S r*   r*   )rD   _fps      r%   ry   z>ShellFileOperations._suggest_similar_files.<locals>.<listcomp>  s    ...%!R2...r'   N   zFile not found: )r<   r?   )r  r,   dirnamebasenamer  r  rC  r  r   r   r   r,  r5  r  setr   r   r    sortr1   )rL   r,   dir_pathrF  basename_no_extr   
lower_namels_cmd	ls_resultscoredflfscorecommonsimilars                  r%   rX  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                 Z   |                      |          }d|                     |           d}|                     |          }|j        dk    r|                     |          S t          |j                  }	 t          |                                          }n# t          $ r d}Y nw xY w| 
                    |          rt          dd|          S |                     d|                     |           d          }t          |j                  }|                     ||          rt          d|d          S |                     d	|                     |                     }|j        dk    rt          d
|j                   S t          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.
        rP  rQ  r   T)r8   r7   r4   rR  u'   Binary file — cannot display as text.rS  cat rU  rV  )r2   r4   )r?  rC  r  r   rX  r&   r   rT   r   r   r%  r1   r"  )	rL   r,   rZ  r[  r\  r4   r^  r_  
cat_results	            r%   r   z!ShellFileOperations.read_file_raw  s      &&Hd44T::HHHjj** A%%..t4441+2DEE	K--//00II 	 	 	III	>>$ 	RttyQQQQ

#]43I3I$3O3O#]#]#]^^3M4HII!!$66 	)?    ZZ Et'='=d'C'C E EFF
1$$$OJ<M$O$OPPPP/
0ABB
 
 
 	
s   9!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 pathrV  zrm -f r   zFailed to delete r   r?  r/   rY   r  rC  r   r   )rL   r,   ri   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: r  rV  zmv  r   zFailed to move z -> r   r  )rL   r   r   pri   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                 r   |                      |          }t          |          rt          d| d          S t          j                            |          d                                         }d}|t          v rGd|                     |           d}| 	                    |          }|j
        dk    r|j        r|j        }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          |||r|                                nd          S )uO  
        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.

        After the write, runs a post-first / pre-lazy lint check via
        ``_check_lint_delta()``.  If the new content is clean, the lint
        call is O(one parse).  If the new content has errors, the pre-write
        content is linted too and only errors newly introduced by this
        write are surfaced — pre-existing problems are filtered out so
        the agent isn't distracted chasing them.

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

        Returns:
            WriteResult with bytes written, lint summary, or error.
        Write denied: '(' is a protected system/credential file.rV  r   Nr  rQ  r   Fz	mkdir -p Tzcat > )r  zFailed to write file: rP  zutf-8pre_contentpost_content)rZ   r[   r\   )r?  r/   rY   r  r,   r  r  LINTERS_INPROCrC  r  r   r   rx  rT   r   r   r  encode_check_lint_deltarM   )rL   r,   r2   r   r  ra  rb  parentr[   	mkdir_cmdmkdir_result	write_cmdwrite_resultrZ  r[  rZ   lint_results                    r%   r   zShellFileOperations.write_file7  sm   .   && D!! 	g%et%e%e%effff gt$$Q'--//%).   Id44T::HHHH**X..K$))k.@))0 && 	$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 ,,T{Y`,aa'%*5?$$&&&4
 
 
 	
s    &G ,G65G6Fr   r   r   c           
         |                      |          }t          |          rt          d| d          S d|                     |           d}|                     |          }|j        dk    rt          d|           S |j        }ddlm}  |||||          \  }	}
}}|s|
dk    r@|pd	| }	 dd
lm	} | |||
||          z  }n# t          $ r Y nw xY wt          |          S |                     ||	          }|j        rt          d|j                   S d|                     |           d}|                     |          }|j        dk    rt          d|           S |j        |	k    r9t          d| dt          |	           dt          |j                   d          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
        r  r  rV  r  rQ  r   rU  )fuzzy_find_and_replacez'Could not find match for old_string in )format_no_match_hintzFailed to write changes: z2Post-write verification failed: could not re-read z#Post-write verification failed for z5: on-disk content differs from intended write (wrote z chars, read back z=). The patch did not persist. Re-read the file and try again.r  TN)rb   rc   rd   r\   )r?  r/   ra   rC  r  r   r   tools.fuzzy_matchr  r  r   r   r<   r  rN  r  rM   )rL   r,   r   r   r   ra  rb  r2   r  rE  match_count	_strategyr<   err_msgr  r  
verify_cmdverify_resultrc   r  s                       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  	.K1$$OOOOGBBBBBB//jRYZZZ   W----t[99 	W%UAS%U%UVVVV GD22488FFF


:.."a''%`Z^%`%`aaaa;..Md M Mk**M M>A-BV>W>WM M M    !!';==
 ,,TwU`,aa 6*5?$$&&&4	
 
 
 	
s   5C 
CC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: rV  )tools.patch_parserr  r  ra   )rL   r   r  r  
operationsparse_errorri   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                    t           j                            |          d                                         }t                              |          }||Ud|                     |           d}|                     |          }|j        dk    rt          dd| d	          S |j
        } ||          \  }}|d
k    rt          dd| d	          S t          ||rdn|          S |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 )u8  
        Run syntax check on a file after editing.

        Prefers the in-process linter for structured formats (JSON, YAML,
        TOML) when possible — those parse via the Python stdlib in
        microseconds and don't require a subprocess.  Falls back to the
        shell linter table for compiled/type-checked languages
        (py_compile, node --check, tsc, go vet, rustfmt).

        Args:
            path: File path (used to select the linter + for shell invocation).
            content: Optional file content.  If provided AND an in-process
                     linter matches the extension, we lint the content
                     directly without re-reading the file from disk.  Ignored
                     for shell linters.

        Returns:
            LintResult with status and any errors.
        r   Nr  rQ  r   TzFailed to read z	 for lint)r|   r~   r   zNo linter available for z (missing dependency)r   rb   r}   zNo linter for z filesz not availablez{file}ro  r  )r  r,   r  r  r  r  rC  r  r   r{   r   LINTERSr,  r  r   r   )rL   r,   r2   r   inprocra  rb  r   err
linter_cmdbase_cmdr  ri   s                r%   _check_lintzShellFileOperations._check_lint  s	   ( gt$$Q'--//  ##C((L$"8"8">">LLL"jj22(A--%d<]d<]<]<]^^^^%,fWooGBj  !$8mSV8m8m8mnnnnbr1BsCCCC 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  c                    |                      ||          }|j        s|j        r|S ||S |                      ||          }|j        s|j        s|j        s|S d |j                                        D             fd|j                                        D             }|st          d|j        d          S t          ddd	                    |          z   
          S )u/  
        Run post-write lint with pre-write baseline comparison.

        Strategy (post-first, pre-lazy):
        1. Lint the post-write state.  If clean → return clean immediately.
           This is the hot path and matches _check_lint() in cost.
        2. If post-lint found errors AND we have pre-write content, lint
           that too.  If the pre-write file was already broken, return only
           the *new* errors introduced by this edit — errors that existed
           before aren't the agent's problem to chase right now.
        3. If pre_content is None (new file or unavailable), skip the delta
           step and return all post-write errors.

        This mirrors Cline's and OpenCode's post-edit LSP pattern: surface
        only the errors this specific edit introduced, so the agent doesn't
        get distracted by pre-existing problems.

        Args:
            path: File path (for linter selection).
            pre_content: File content BEFORE the write.  Pass None for new
                         files or when the pre-state isn't available — the
                         delta refinement is skipped and all post errors
                         are returned.
            post_content: File content AFTER the write.  Optional; if None,
                          the shell linter reads from disk (same as
                          _check_lint).

        Returns:
            LintResult.  ``output`` contains either the full post-lint
            errors (no pre-state) or just the new-error lines (delta
            refinement applied).
        )r2   Nc                 ^    h | ]*}|                                 |                                 +S r*   r   )rD   lns     r%   	<setcomp>z8ShellFileOperations._check_lint_delta.<locals>.<setcomp>s  s-    PPPBRXXZZPRXXZZPPPr'   c                 h    g | ].}|                                 |                                 v,|/S r*   r  )rD   r  	pre_liness     r%   ry   z9ShellFileOperations._check_lint_delta.<locals>.<listcomp>t  s9    jjjRrxxzzjbhhjj`iNiNibNiNiNir'   Fu^   Pre-existing lint errors — this edit didn't introduce new ones but the file is still broken.)rb   r}   r~   zLNew lint errors introduced by this edit (pre-existing errors filtered out):
r)  r  )r  rb   r|   r}   r   r{   r    )rL   r,   r  r  postpre
post_linesr  s          @r%   r  z%ShellFileOperations._check_lint_delta7  s-   D l;; < 	4< 	K Kt[99; 	#+ 	SZ 	 K QP#**?*?*A*APPP	jjjj4;#9#9#;#;jjj
 	 {x    8:>))J:O:OP
 
 
 	
r'   r   r   r   r   r   r   r   r   c	           	         t          ||          \  }}|                     |          }|                     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  rh  z 2>/dev/null | head -20r   r)  N   zSimilar paths: z, rw  z. r<   rt   rr   )r   r?  r  rC  r   r  r,   rx  ry  r   r   r  r,  r5  r   r    rp   _search_files_search_content)rL   r   r,   r   r   r   r   r   r   checkr  basename_query
hint_partsparent_checkr  lower_q
candidatesentryles                      r%   r   zShellFileOperations.search  s   & 4FEBB   && 

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                 (   |                     d          sd|vr|}n|                    d          d         }t          |          }t          d |j        D                       }|                     d          r|                     ||||          S |                     d          st          d          S |sd	nd
}|rd| nd
}	d
}
|sd|dz    d| }
d|                     |           |	 d|                     |           d|
 }| 	                    |d          }|j
                                        sJd|                     |           |	 d|                     |           d|
 }| 	                    |d          }g }|j
                                                            d          D ]}|s|                    dd          }t          |          dk    rJ|d                             dd
                                          r|                    |d                    x|                    |           |r|                                }g }|D ]}	 t          |                                                              |          j        }n$# t$          $ r t          |          j        }Y nw xY wt          d |D                       r{|                    |           ||||z            }t          |t          |                    S )z-Search for files by name pattern (glob-like).z**/r   c              3   H   K   | ]}|d vo|                     d          V  dS )r   z..r   Nr5  rD   parts     r%   r  z4ShellFileOperations._search_files.<locals>.<genexpr>  sL       '
 '
 #<(<(<'
 '
 '
 '
 '
 '
r'   rgr6  zFile search requires 'rg' (ripgrep) or 'find'. Install ripgrep for best results: https://github.com/BurntSushi/ripgrep#installationrV  z-not -path '*/.*'r   r  z | tail -n +r   z | head -n zfind z -type f -name z* -printf '%T@ %p\n' 2>/dev/null | sort -rnrl  r  z 2>/dev/null | sort -rnr)  rm  r   r   c              3   H   K   | ]}|d vo|                     d          V  dS r  r  r  s     r%   r  z4ShellFileOperations._search_files.<locals>.<genexpr>  s8      ^^Dt;.G4??33G3G^^^^^^r'   rr   rt   )r5  r,  r   anypartsr  _search_files_rgrp   rC  r  r   r   r  r   isdigitr   resolverelative_tor   )rL   r   r,   r   r   search_patternsearch_roothas_hidden_path_ancestorhidden_excludehidden_filter_exprpagination_exprr  ri   
cmd_simplerr   r"   r  normalized_rootfiltered_files	file_path	rel_partss                        r%   r  z!ShellFileOperations._search_files  s    !!%(( 	4S-?-?$NN$]]3//3N4jj#& '
 '
#)'
 '
 '
 $
 $
  T"" 	N((ufMMM   (( 	K    5MT,,RT5CK1111
 ' 	LKVaZKKEKKOMd,,T22 M4F M MW[WmWmn|W}W} M M;JM M C,,}""$$ 	8C!7!7!=!= C?Q C Cbfbxbx  zH  cI  cI C C1@C CJZZ
BZ77FM''))//55 	# 	#D JJsA&&E5zzQ58#3#3C#<#<#D#D#F#FU1X&&&&T""""
 $ 	:)1133ON" 1 1	6 $Y 7 7 9 9 E Eo V V \II! 6 6 6 $Y 5III6^^T]^^^^^ %%i0000"6&5.#89E E


 
 
 	
s   9JJ65J6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 r  z 2>/dev/null | head -n rl  r  c                     g | ]}||S r*   r*   rD   r  s     r%   ry   z8ShellFileOperations._search_files_rg.<locals>.<listcomp>4  s    GGG1QGQGGGr'   r)  zrg --files -g c                     g | ]}||S r*   r*   r  s     r%   ry   z8ShellFileOperations._search_files_rg.<locals>.<listcomp>>      KKKqKKKKr'   )rr   rt   r5   )r5  rC  r  r   r   r,  rp   r  )rL   r   r,   r   r   glob_patternfetch_limit
cmd_sortedri   	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).r  grepzqContent search requires ripgrep (rg) or grep. Install ripgrep: https://github.com/BurntSushi/ripgrep#installationrV  )r  _search_with_rg_search_with_greprp   )rL   r   r,   r   r   r   r   r   s           r%   r  z#ShellFileOperations._search_contentH  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          }g }|j                                                            d          D ]}|r|dk    r|                    |          }|r|                    t-          |                    d          pd|                    d          z   t          |                    d                    |                    d          d d!         "                     |dk    rPt1          |          }|r?|                    t-          |d         |d         |d         d d!         "                     t          |          }||||z            }t          |||||z   k    #          S )$zSearch using ripgrep.)r  z--line-numberz--no-headingz--with-filenamer   -Cz--glob
files_only-lcount-c   r+  head-nr  rl  r  rm  stderrSearch errorSearch failed: r  c                     g | ]}||S r*   r*   r  s     r%   ry   z7ShellFileOperations._search_with_rg.<locals>.<listcomp>  r  r'   r)  r  :r   rs   rt   ^([A-Za-z]:)?(.*?):(\d+):(.*)$r   r   r     Nr   rw   rq   rt   r5   )extendrR   rC  r   r    r  r   r   r   hasattrr  rp   r,  r  rsplitrT   r   r  valuesr   compiler   rk   r   r   rL   r   r,   r   r   r   r   r   	cmd_partsr  r  ri   	error_msgr  totalr  rs   r"   r  	_match_rerq   rx   parseds                          r%   r  z#ShellFileOperations._search_with_rgY  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G++--33D99   tt|| OOD)) NN;ggajj.B!''!**<$'

OO !

4C4 0$ $ $   
  Q;;7==F {!'(.q	$*1IdsdO( ( (    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          }g }|j                                                            d          D ]}|r|dk    r|                    |          }|r|                     t-          |                    d          pd|                    d          z   t          |                    d                     |                    d!          d"d#         $                     |dk    rPt1          |          }|r?|                     t-          |d         |d         |d         d"d#         $                     t          |          }||||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  r  rl  r  rm  r  r  r  r  c                     g | ]}||S r*   r*   r  s     r%   ry   z9ShellFileOperations._search_with_grep.<locals>.<listcomp>  r  r'   r)  r  r  r   r  r	  r   r   r  r
  Nr   rw   r  )r   r  rR   rC  r    r  r   r   r   r  r  rp   r,  r  r  rT   r   r  r  r   r  r   rk   r   r   r  s                          r%   r  z%ShellFileOperations._search_with_grep  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G++--33D99   tt||OOD)) NN;ggajj.B!''!**<$'

OO !

4C4 0$ $ $   
 Q;;7==F {!'(.q	$*1IdsdO( ( (    LLE6&5.01D!&5.0   s   J//
J<;J<rB   )NNN)r   r   r   r   )'rN   rO   rP   rQ   rR   r  rT   r   r  rU   r  r"  r%  r2  r?  rC  rN  r1   r   rX  r   rY   r   r   r   ra   r   r   r   r{   r  r  rp   r   r  r  r  r  r  r*   r'   r%   r   r     s        !2 !2# !2 !2 !2 !2F CG $
 
S 
s 
C 

)6
 
 
 
@( ( ( ( ( ( c 3 $    $'c 'd ' ' ' '
# # ## #c # # # ## # # # # #J7S 7S 7 7 7 7
	 	3 	# 	RU 	 	 	 	Z
 Z
c Z
3 Z
3 Z
 Z
 Z
 Z
 Z
x2
3 2
: 2
 2
 2
 2
h
# 
* 
 
 
 
B     S s {    $Q
s Q
S Q
[ Q
 Q
 Q
 Q
p +0P
 P
# P
3 P
C P
#'P
4?P
 P
 P
 P
ds {    <7
 7
 7
hsm 7
z 7
 7
 7
 7
t 9=O
 O
c O
 O
(0O
AKO
 O
 O
 O
j CLOP<==> =>c => =>C =>"3-=>7:=>IL=>=>69=>BN=> => => =>~P
S P
 P
C P
 P
Q] P
 P
 P
 P
d)
 )
3 )
s )
C )
T` )
 )
 )
 )
Vs # (3- ",/>ALOT`   "`s `# `(3- `"`,/`>A`LO`T`` ` ` `D^ ^C ^HSM ^!$^.1^@C^NQ^Vb^ ^ ^ ^ ^ ^r'   r   )CrQ   r  r   rJ  abcr   r   dataclassesr   r   typingr   r   r	   r
   pathlibr   tools.binary_extensionsr   agent.file_safetyr   r   r   r)   r   r.   rR   r8  _HOMEWRITE_DENIED_PATHSWRITE_DENIED_PREFIXESr  r   r   r&   r+   rU   r/   r1   rY   ra   rk   rp   r{   r   tuplerT   r   r   r$  r  r   r   r   r   r  	MAX_LINESMAX_LINE_LENGTHrY  r   r   r   r   r   r   r   r   r*   r'   r%   <module>r%     s   4 
			 				  # # # # # # # # ( ( ( ( ( ( ( ( , , , , , , , , , , , ,       5 5 5 5 5 5            	IDIKK--e44 33E::  2:BCC 2:LMM "c "c " " " "")hsm ) ) ) ))3 )4 ) ) ) ) U U U U U U U U& 	I 	I 	I 	I 	I 	I 	I 	I        6                6                 9S 9U3S=-AD-H 9 9 9 9:1 1 1 1 1S 1 1 1r NMM  .%)( 	1s 	1uT3Y'7 	1 	1 	1 	11s 1uT3Y'7 1 1 1 1&1s 1uT3Y'7 1 1 1 1"1 1tSy)9 1 1 1 10   	    s S S     -@+=/ /c /%(/BGS// / / /& /D-A/ / /'*/FKCQTHo/ / / /^ ^ ^ ^ ^. ^ ^ ^ ^ ^r'   