§
    øžyj�/ ã            	       óÐ  — d Z ddlZddlZddlZddlZddlZddlZddl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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dee         fd„Z'dededefd„Z(dZ)dede*ee+f         fd„Z,dee         de+fd„Z-dede+fd„Z.e G d„ d¦  «        ¦   «         Z/e G d„ d¦  «        ¦   «         Z0e G d„ d¦  «        ¦   «         Z1e G d„ d¦  «        ¦   «         Z2e G d„ d ¦  «        ¦   «         Z3e G d!„ d"¦  «        ¦   «         Z4e G d#„ d$¦  «        ¦   «         Z5 ej#        d%¦  «        Z6d&e5de*eee         f         fd'„Z7d(ede*eef         fd)„Z8 ej#        d*¦  «        Z9d+ede*ee:ef         dz  fd,„Z; G d-„ d.e	¦  «        Z<h d/£Z=d0d1d2d3d4d5œZ> e?h d6£¦  «        Z@d7d8d9d:œZAd;ed(ede+fd<„ZBd=ede*e+ef         fd>„ZCd=ede*e+ef         fd?„ZDd=ede*e+ef         fd@„ZEd=ede*e+ef         fdA„ZFeFeCeDeDeEdBœZG e?h dC£¦  «        ZHdDZIdDZJdEZKdFZLdDZMdZNdGZOdHZPdIedJe:de:fdK„ZQeLeMfdLedMede*e:e:f         fdN„ZReNeOfdLedMede*e:e:f         fdO„ZS ej#        dP¦  «        ZTdQede+fdR„ZUdSee         de+fdT„ZVd&e3dQede3fdU„ZW G dV„ dWe<¦  «        ZXdS )Xa\  
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ÚClassVar)ÚPath)ÚBINARY_EXTENSIONS)Úbuild_write_denied_pathsÚbuild_write_denied_prefixesÚget_write_denied_errorÚ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/ragecks/.hermes/hermes-agent/tools/file_operations.pyÚ_strip_terminal_fence_leaksr'   @   sÌ   € àð Øˆà!€MØ—’¨�Ñ.Ô.ð &ð &ˆØ0°DÐ8ÐK¸GÀt¸OÐÝ"×&Ò& r¨4Ñ0Ô0ˆÝ"×&Ò& r¨7Ñ3Ô3ˆØ—/’/ &¨"Ñ-Ô-ˆØð 	 G§M¢M°*Ñ$=Ô$=ÀÒ$CÐ$CØØ×Ò˜WÑ%Ô%Ð%Ð%Ø�7Š7�=Ñ!Ô!Ð!ó    Úsamplec                 ó:   — | sdS | dd…         }d|v rdS d|v rdS dS )u"  Return the dominant line ending in ``sample`` or None if undetermined.

    Looks at the first few line breaks and picks ``\r\n`` if any are
    present (Windows / DOS), otherwise ``\n`` (Unix).  Returns ``None``
    for empty / single-line content where we can't tell.  Used to
    preserve the file's original line endings across write_file and
    patch operations â€” without this the agent's bare-LF tool args
    silently normalize Windows-line-ending files, and patch produces
    mixed endings when only a substituted region changes.
    Ni   ú
Ú
© )r)   Úheads     r&   Ú_detect_line_endingr/   Q   s?   € ð ð Øˆtà�%�4�%Œ=€DØ�€~€~ØˆvØˆt€|€|ØˆtØˆ4r(   Útargetc                 ó¢   — |                       dd¦  «                              dd¦  «        }|dk    r|S |dk    r|                      dd¦  «        S | S )a5  Convert all line endings in ``text`` to ``target`` (``\n`` or ``\r\n``).

    Idempotent: ``_normalize_line_endings(_normalize_line_endings(x, "\r\n"), "\r\n") == _normalize_line_endings(x, "\r\n")``.
    Strips lone ``\r`` characters as well, so mixed-ending content is
    homogenized in a single pass.
    r+   r,   ú)r   )r   r0   Úlf_normalizeds      r&   Ú_normalize_line_endingsr4   g   s\   € ð —L’L ¨Ñ.Ô.×6Ò6°t¸TÑBÔB€MØ�‚~€~ØÐØ�ÒÐØ×$Ò$ T¨6Ñ2Ô2Ð2Ø€Kr(   u   ï»¿c                 ó~   — | r8|                       t          ¦  «        r| t          t          ¦  «        d…         dfS | dfS )z»Return (text-without-leading-BOM, had_bom).

    Only a single leading BOM is stripped; a BOM appearing mid-content is
    left alone (it's legitimate data there, not a file marker).
    NTF)Ú
startswithÚ	_UTF8_BOMÚlen©r   s    r&   Ú
_strip_bomr:   ‡   sC   € ð ð +�—’¥	Ñ*Ô*ð +Ø•C�	‘N”N�O�OÔ$ dÐ*Ð*Ø�ˆ;Ðr(   c                 óT   — t          | ¦  «        o|                      t          ¦  «        S )z)True if ``text`` begins with a UTF-8 BOM.)Úboolr6   r7   r9   s    r&   Ú_has_bomr=   ’   s   € å�‰:Œ:Ð4˜$Ÿ/š/­)Ñ4Ô4Ð4r(   Úpathc                 ó    — t          | ¦  «        S )z.Return True if path is on the write deny list.)Ú_shared_is_write_denied©r>   s    r&   Ú_is_write_deniedrB   —   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>±   s(   € ÐTÐTÐT™˜˜A¸!¸-ÈAÐQSÊGÈG��1ÈGÈGÈGr(   ©Ú__dict__Úitems©Úselfs    r&   Úto_dictzReadResult.to_dict°   s$   € ØTÐT ¤×!4Ò!4Ñ!6Ô!6ÐTÑTÔTÐTr(   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rE   ÚstrÚ__annotations__rF   ÚintrG   rH   r<   rI   r   rJ   rK   rL   rM   rN   rO   r   ÚlistrR   r   Údictr`   r-   r(   r&   rD   rD       s#  € € € € € € à%Ð%Ø€GˆSÐÐÑØ€K�ÐÐÑØ€IˆsÐÐÑØ€IˆtÐÐÑØ€Dˆ(�3Œ-ÐÐÑØ€IˆtÐÐÑØ€HˆdÐÐÑØ$(€N�H˜S”MÐ(Ð(Ñ(Ø#€Iˆx˜Œ}Ð#Ð#Ñ#Ø $€J�˜”Ð$Ð$Ñ$Ø€Eˆ8�CŒ=ÐÐÑØ$˜u°TÐ:Ñ:Ô:€M�4˜”9Ð:Ð:Ñ:ðU˜ð Uð Uð Uð Uð Uð Ur(   rD   c                   óÎ   — 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ef                  ed<   dZe
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_createdNÚverifiedÚlintÚlsp_diagnosticsrO   Úwarningr   c                 óH   — d„ | j                              ¦   «         D ¦   «         S )Nc                 ó   — i | ]
\  }}|®||“ŒS rU   r-   rV   s      r&   rZ   z'WriteResult.to_dict.<locals>.<dictcomp>Ê   s   € ÐHÐHÐH™˜˜A¸!¸-��1¸-¸-¸-r(   r[   r^   s    r&   r`   zWriteResult.to_dictÉ   s$   € ØHÐH ¤×!4Ò!4Ñ!6Ô!6ÐHÑHÔHÐHr(   )ra   rb   rc   rd   rl   rg   rf   rm   r<   rn   r   ro   r	   re   r
   rp   rO   rq   ri   r`   r-   r(   r&   rk   rk   ´   sÏ   € € € € € € à%Ð%Ø€M�3ÐÐÑØ€L�$ÐÐÑð
  $€Hˆh�tŒnÐ#Ð#Ñ#Ø%)€Dˆ(�4˜˜S˜”>Ô
"Ð)Ð)Ñ)ð &*€O�X˜c”]Ð)Ð)Ñ)Ø€Eˆ8�CŒ=ÐÐÑØ!€GˆX�cŒ]Ð!Ð!Ñ!ðI˜ð Ið Ið Ið Ið Ið Ir(   rk   c                   óL  — 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
Zee         ed<   dZeed<   d
Zee         ed<   defd„Zd
S )ÚPatchResultzResult from patching a file.FÚsuccessr   ÚdiffrP   Úfiles_modifiedÚfiles_createdÚfiles_deletedNro   rp   rO   Ú	no_changeÚnoter   c                 ó@  — d| j         i}| j        rd|d<   | j        r
| j        |d<   | j        r
| j        |d<   | 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 )Nrv   Tr{   r|   rw   rx   ry   rz   ro   rp   rO   )
rv   r{   r|   rw   rx   ry   rz   ro   rp   rO   ©r_   Úresults     r&   r`   zPatchResult.to_dictß   sÝ   € Ø"+¨T¬\Ð!:ˆØŒ>ð 	'Ø"&ˆF�;ÑØŒ9ð 	'Ø!œYˆF�6‰NØŒ9ð 	'Ø!œYˆF�6‰NØÔð 	;Ø'+Ô':ˆFÐ#Ñ$ØÔð 	9Ø&*Ô&8ˆF�?Ñ#ØÔð 	9Ø&*Ô&8ˆF�?Ñ#ØŒ9ð 	'Ø!œYˆF�6‰NØÔð 	=Ø(,Ô(<ˆFÐ$Ñ%ØŒ:ð 	)Ø"œjˆF�7‰OØˆr(   )ra   rb   rc   rd   rv   r<   rf   rw   re   r   rh   rx   r   ry   rz   ro   r   r	   r
   rp   rO   r{   r|   ri   r`   r-   r(   r&   ru   ru   Í   s"  € € € € € € à&Ð&Ø€GˆTÐÐÑØ€Dˆ#€N€N�NØ % °dÐ ;Ñ ;Ô ;€N�D˜”IÐ;Ð;Ñ;Ø$˜u°TÐ:Ñ:Ô:€M�4˜”9Ð:Ð:Ñ:Ø$˜u°TÐ:Ñ:Ô:€M�4˜”9Ð:Ð:Ñ:Ø%)€Dˆ(�4˜˜S˜”>Ô
"Ð)Ð)Ñ)à%)€O�X˜c”]Ð)Ð)Ñ)Ø€Eˆ8�CŒ=ÐÐÑð €IˆtÐÐÑØ€Dˆ(�3Œ-ÐÐÑð˜ð ð ð ð ð ð r(   ru   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_numberrE   g        ÚmtimeN)	ra   rb   rc   rd   re   rf   rg   rƒ   Úfloatr-   r(   r&   r�   r�   ö   sF   € € € € € € à Ð Ø
€I€I�IØÐÐÑØ€L€L�LØ€Eˆ5ÐÐÑÐÐr(   r�   c                   óP  — 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
Zee         e	d<   d
Zee         e	d<   dZee         e	d<   dee         fd„Zddedefd„Zd
S )ÚSearchResultzResult from searching.rP   ÚmatchesÚfilesÚcountsr   Útotal_countFrH   NÚlimit_reasonrq   rO   é   Ú_DENSIFY_MIN_MATCHESr   c                 óL  — t          | j        ¦  «        | j        k     rdS g }d}| j        D ]e}|j        |k    r!|                     |j        ¦  «         |j        }|                     d|j        › d|j                             ¦   «         › �¦  «         Œfd                     |¦  «        S )uK  Render content-mode matches as a compact, path-grouped text block.

        The verbose form repeats the ``{"path","line","content"}`` keys and the
        full path string for every match. This groups consecutive matches by
        path (path printed once, then ``  <line>: <content>`` rows), which is
        lossless â€” every path, line number, and content byte is preserved â€” and
        readable by the model without any decode step.

        Returns ``None`` when densification is not worthwhile (too few matches),
        so the caller falls back to the verbose array.
        Nz  ú: r,   )	r8   r‡   r�   r>   r    r‚   rE   Úrstripr!   )r_   ÚlinesÚcurrent_pathÚms       r&   Ú_densify_matcheszSearchResult._densify_matches  s²   € õ ˆtŒ|ÑÔ˜tÔ8Ò8Ð8Ø�4ð ˆØ&*ˆØ”ð 	Eð 	EˆAØŒv˜Ò%Ð%Ø—’˜QœVÑ$Ô$Ð$Ø œv�ð �LŠLÐC˜aœmÐCÐC¨q¬y×/?Ò/?Ñ/AÔ/AÐCÐCÑDÔDÐDÐDØ�yŠy˜ÑÔÐr(   Údensifyc                 óZ  — d| j         i}| j        r9|r|                      ¦   «         nd }|�d|d<   ||d<   nd„ | j        D ¦   «         |d<   | j        r
| j        |d<   | j        r
| j        |d<   | j        rd	|d
<   | j        r
| j        |d<   | j        r
| j        |d<   | j        r
| j        |d<   |S )NrŠ   ztpath-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that fileÚmatches_formatÚmatches_textc                 ó8   — g | ]}|j         |j        |j        d œ‘ŒS ))r>   r#   rE   ©r>   r‚   rE   )rW   r“   s     r&   ú
<listcomp>z(SearchResult.to_dict.<locals>.<listcomp>9  s8   € ð %ð %ð %àð œV¨Q¬]ÀqÄyÐQÐQð%ð %ð %r(   r‡   rˆ   r‰   TrH   r‹   rq   rO   )	rŠ   r‡   r”   rˆ   r‰   rH   r‹   rq   rO   )r_   r•   r   Údenses       r&   r`   zSearchResult.to_dict,  s  € Ø%2°DÔ4DÐ$EˆØŒ<ð 	Ø/6Ð@�D×)Ò)Ñ+Ô+Ð+¸DˆEØÐ ðQð Ð'Ñ(ð */��~Ñ&Ð&ð%ð %à!œ\ð%ñ %ô %��yÑ!ð Œ:ð 	)Ø"œjˆF�7‰OØŒ;ð 	+Ø#œ{ˆF�8ÑØŒ>ð 	'Ø"&ˆF�;ÑØÔð 	7Ø%)Ô%6ˆF�>Ñ"ØŒ<ð 	-Ø $¤ˆF�9ÑØŒ:ð 	)Ø"œjˆF�7‰OØˆr(   ©F)ra   rb   rc   rd   r   rh   r‡   r   r�   rf   rˆ   re   ri   r‰   r	   rg   rŠ   rH   r<   r‹   r   rq   rO   r�   r   r”   r`   r-   r(   r&   r†   r†   ÿ   s=  € € € € € € à Ð Ø!& °tÐ!<Ñ!<Ô!<€GˆT�+ÔÐ<Ð<Ñ<Ø�u¨TÐ2Ñ2Ô2€Eˆ4�Œ9Ð2Ð2Ñ2Ø"˜U°4Ð8Ñ8Ô8€FˆD��c�ŒNÐ8Ð8Ñ8Ø€K�ÐÐÑØ€IˆtÐÐÑØ"&€L�(˜3”-Ð&Ð&Ñ&Ø!€GˆX�cŒ]Ð!Ð!Ñ!Ø€Eˆ8�CŒ=ÐÐÑð
 +,Ð˜( 3œ-Ð+Ð+Ñ+ð  (¨3¤-ð  ð  ð  ð  ð8ð ˜tð °ð ð ð ð ð ð r(   r†   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.Trv   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¢   ÚokrO   )r¤   r¡   r¢   )r    r¢   rv   r¡   r~   s     r&   r`   zLintResult.to_dictT  sR   € ØŒ<ð 	BØ'°D´LÐAÐAÐAØ$(¤LÐ=˜D˜D°gÈÌÐUÐUˆØŒ<ð 	-Ø $¤ˆF�9ÑØˆr(   N)ra   rb   rc   rd   rv   r<   rf   r    r¡   re   r¢   ri   r`   r-   r(   r&   rŸ   rŸ   L  sw   € € € € € € à%Ð%Ø€GˆTÐÐÑØ€GˆTÐÐÑØ€FˆCÐÐÑØ€GˆSÐÐÑð˜ð ð ð ð ð ð 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)	ra   rb   rc   rd   r¨   re   rf   r©   rg   r-   r(   r&   r§   r§   ]  s8   € € € € € € à0Ð0Ø€FˆCÐÐÑØ€IˆsÐÐÑÐÐr(   r§   z'\n?\[Command timed out after \d+s\]\s*$r   c                 ón   — | j         dk    r"t                               d| j        ¦  «        dfS | j        dfS )zIReturn stdout cleaned for parsing and a limit reason for search timeouts.é|   r   Úsearch_timeoutN)r©   Ú_SEARCH_TIMEOUT_MARKER_REr   r¨   )r   s    r&   Ú_search_stdout_and_limitr®   g  s;   € àÔ˜3ÒÐÝ(×,Ò,¨R°´Ñ?Ô?ÐAQÐQÐQØŒ=˜$ÐÐr(   r¡   c                 óö  — g }g }|                       d¦  «        D ]¶}|                     ¦   «         sŒ|                     ¦   «         }|                     d¦  «        s|                     d¦  «        r|                     |¦  «         Œk|dk    st
                               |¦  «        r|                     |¦  «         Œ¡|                     |¦  «         Œ·d                     |¦  «        d                     |¦  «        fS )uC  Separate rg/grep diagnostic lines from real match output.

    ``_exec`` runs commands with ``stderr=subprocess.STDOUT``, so error and
    warning text from ``rg``/``grep`` is interleaved with match lines in a
    single stream. Diagnostics must not be parsed as matches, and on a hard
    failure they are the error message to surface.

    Returns ``(diagnostics, payload)`` where ``payload`` contains only lines
    that look like real search output â€” a match line (``file:line:content``),
    a files-only path, a count line, or a context line/separator. Everything
    else (tool-prefixed errors, rg's multi-line ``regex parse error`` block
    with its indented carets, blank lines) is folded into ``diagnostics``.

    Classifying by *shape* rather than by error prefix is what lets the
    exit-2 guard distinguish a pure failure (no usable payload â†’ surface the
    error) from a partial failure (some files matched, one was unreadable â†’
    keep the matches). It also means error text can never be mis-parsed as a
    match, a latent bug that predates the exit-code fix.
    r,   zrg: zgrep: ú--)Úsplitr   Úlstripr6   r    Ú_SEARCH_OUTPUT_REÚmatchr!   )r¡   ÚdiagnosticsÚpayloadr#   Ústrippeds        r&   Ú_split_tool_diagnosticsr¸   n  sú   € ð(  €KØ€GØ—’˜TÑ"Ô"ð %ð %ˆØ�zŠz‰|Œ|ð 	Øð —;’;‘=”=ˆØ×Ò˜vÑ&Ô&ð 	¨(×*=Ò*=¸hÑ*GÔ*Gð 	Ø×Ò˜tÑ$Ô$Ð$Øð �4Š<ˆ<Õ,×2Ò2°4Ñ8Ô8ˆ<Ø�NŠN˜4Ñ Ô Ð Ð à×Ò˜tÑ$Ô$Ð$Ð$Ø�9Š9�[Ñ!Ô! 4§9¢9¨WÑ#5Ô#5Ð5Ð5r(   z0^([A-Za-z]:)?[^\s:][^\n]*?[:\-]\d|^[^\s:][^\s]*$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``.
    r°   Nz-(\d+)-é   )ÚreÚfinditerÚstartrg   ÚgroupÚend)r#   r´   Ú	candidater>   s       r&   Ú_parse_search_context_linerÁ   §  s�   € ð ð �4˜4’<�<Øˆtà€EÝ”[ ¨TÑ2Ô2ð ð ˆ	Øˆˆà€}Øˆtà��—’‘”�Ô€DØð Øˆtà•�U—[’[ ‘^”^Ñ$Ô$ d¨5¯9ª9©;¬;¨<¨<Ô&8Ð8Ð8r(   c                   ó°  — 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	d'dede
e         defd„Ze	 d'dedede
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d(d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-   )r_   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-   ©r_   r>   s     r&   Úread_file_rawzFileOperations.read_file_rawÌ  s	   € ð 	ˆr(   NÚ	max_bytesc                 ó"   — t          d¬¦  «        S )zCRead complete binary content as base64 across the backend boundary.z1Binary reads are not implemented for this backend©rO   ©rD   )r_   r>   rÍ   s      r&   Úread_file_byteszFileOperations.read_file_bytesÖ  s   € åÐ SÐTÑTÔTÐTr(   rE   Úpre_contentc                 ó   — dS )z8Write content to a file, creating directories as needed.Nr-   )r_   r>   rE   rÒ   s       r&   Ú
write_filezFileOperations.write_fileÚ  ó	   € ð 	ˆr(   FÚ
old_stringÚ
new_stringÚreplace_allc                 ó   — dS )z,Replace text in a file using fuzzy matching.Nr-   )r_   r>   rÖ   r×   rØ   s        r&   Úpatch_replacezFileOperations.patch_replaceà  rÕ   r(   Úpatch_contentc                 ó   — dS )zApply a V4A format patch.Nr-   )r_   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(   Ú	recursivec                 óP   — |rt          d¬¦  «        S |                      |¦  «        S )züCross-platform delete that handles files and (with recursive=True)
        directory trees. Default implementation delegates to ``delete_file``
        for the non-recursive case; backends with native recursive support
        should override.
        z1Recursive delete not implemented for this backendrÏ   )rk   rß   ©r_   r>   rà   s      r&   Údelete_pathzFileOperations.delete_pathð  s3   € ð ð 	ZÝÐ%XÐYÑYÔYÐYØ×Ò Ñ%Ô%Ð%r(   ÚsrcÚdstc                 ó   — dS )zSMove/rename a file from src to dst. Returns WriteResult with .error set on failure.Nr-   )r_   rä   rå   s      r&   Ú	move_filezFileOperations.move_fileú  rÉ   r(   ú.é2   r   Úpatternr0   Ú	file_globÚoutput_modeÚcontextc	                 ó   — dS )zSearch for content or files.Nr-   )	r_   rê   r>   r0   rë   rÆ   rÅ   rì   rí   s	            r&   ÚsearchzFileOperations.searchÿ  s	   € ð
 	ˆr(   ©rº   rÄ   rU   r�   ©rè   rE   Nré   r   rE   r   )ra   rb   rc   rd   r   re   rg   rD   rÈ   rÌ   r   rÑ   rk   rÔ   r<   ru   rÚ   rÝ   rß   rã   rç   r†   rï   r-   r(   r&   rÃ   rÃ   Ä  sŽ  € € € € € ØJÐJàðð ˜cð ¨3ð ¸3ð È*ð ð ð ñ „^ðð ð #ð ¨*ð ð ð ñ „^ððUð U Cð U°H¸S´Mð UÈZð Uð Uð Uð Uð à04ðð ˜sð ¨Sð Ø (¨¤ðØ9Dðð ð ñ „^ðð
 à*/ðð  #ð °3ð ÀCð Ø#'ðØ4?ðð ð ñ „^ðð
 ð sð ¨{ð ð ð ñ „^ðð ð ð ¨ð ð ð ñ „^ðð&ð & ð &°ð &Àð &ð &ð &ð &ð ð˜Sð  sð ¨{ð ð ð ñ „^ðð ØBKØOPØ<=ðð ˜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.jsú.tsú.goú.rs>   rû   rü   rú   )z/this is not the tsc command you are looking forz%could not determine executable to runznot found in npm registry)zno input filename givenzerror: not a workspace)zcannot find packagezgo: cannot find main module)ÚnpxÚrustfmtÚgoÚbase_cmdc                 óž   ‡— t                                | ¦  «        }|sdS |                     ¦   «         Št          ˆfd„|D ¦   «         ¦  «        S )aU  Return True iff ``output`` from ``base_cmd`` indicates the linter
    itself couldn't run (a tooling gap), as opposed to a real lint error
    in the file being checked.

    ``base_cmd`` is the first word of the linter command line (``npx``,
    ``rustfmt``, ``go``, ...).  ``output`` is the stdout/stderr captured
    from running it.
    Fc              3   ó    •K  — | ]}|‰v V — Œ	d S rU   r-   )rW   ÚpÚlowers     €r&   ú	<genexpr>z._looks_like_linter_unusable.<locals>.<genexpr>n  s'   øè è € Ð,Ð,˜aˆq�EˆzÐ,Ð,Ð,Ð,Ð,Ð,r(   )Ú_LINTER_UNUSABLE_PATTERNSÚgetr  Úany)r   r¡   Úpatternsr  s      @r&   Ú_looks_like_linter_unusabler
  a  sU   ø€ õ )×,Ò,¨XÑ6Ô6€HØð ØˆuØ�LŠL‰NŒN€EÝÐ,Ð,Ð,Ð, 8Ð,Ñ,Ô,Ñ,Ô,Ð,r(   rE   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 ú)r�   )	ÚjsonÚloadsÚJSONDecodeErrorÚmsgÚlinenoÚcolnoÚ	ExceptionÚtypera   )rE   Ú_jsonÚes      r&   Ú_lint_json_inprocr  q  sÒ   € àÐÐÐð1Ø�Š�GÑÔÐØˆxøØÔ ð Vð Vð VØÐU¨!¬%ÐUÐU¸¼ÐUÐUÈ1Ì7ÐUÐUÐUÐUÐUÐUÐUÐUÐUÐUøøøøÝð 1ð 1ð 1Ø�˜a™œÔ)Ð0Ð0¨QÐ0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs,   † �
A;§A	ÁA;Á	A;ÁA6Á0A;Á6A;c                 óü   — 	 ddl }n# t          $ r Y dS w xY w	 |                     | ¦  «        D ]}Œ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)uw  In-process YAML syntax check.  Returns (ok, error_message).

    Skipped gracefully if PyYAML isn't installed â€” YAML parsing is optional.

    Deliberately a *syntax-only* scan (``yaml.parse``), not ``safe_load``:
    loading rejects perfectly valid YAML that merely isn't a single plain
    document â€” multi-document streams (``---``-separated Kubernetes
    manifests raise ``ComposerError``) and application-defined tags
    (CloudFormation ``!Sub``/``!Ref``, Ansible ``!vault`` raise
    ``ConstructorError``).  Those are content conventions for whatever
    consumes the file, not syntax errors, and this linter's verdict is
    used as a fail-closed WRITE gate in ``write_file`` â€” a false positive
    here refuses a legitimate write outright.  ``yaml.parse`` still
    catches real scanner/parser failures (unclosed quotes, bad
    indentation, tab-mangled block maps).
    r   N)TÚ__SKIP__r  FzYAMLError: r�   )ÚyamlÚImportErrorÚparseÚ	YAMLErrorr  r  ra   )rE   Ú_yamlÚ_eventr  s       r&   Ú_lint_yaml_inprocr#  }  sí   € ð" ØÐÐÐÐøÝð  ð  ð  àÐÐð øøøð1Ø—k’k 'Ñ*Ô*ð 	ð 	ˆFØØˆxøØŒ?ð (ð (ð (ØÐ' AÐ'Ð'Ð'Ð'Ð'Ð'Ð'Ð'Ð'øøøøÝð 1ð 1ð 1Ø�˜a™œÔ)Ð0Ð0¨QÐ0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs8   ‚ ‡
”™3 ³
A;½A	ÁA;Á	A;ÁA6Á0A;Á6A;c                 óž   — ddl }	 |                     | ¦  «         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  Fr�   )Útomllibr  r  r  ra   )rE   Ú_tomlr  s      r&   Ú_lint_toml_inprocr'  �  sz   € àÐÐÐð1Ø�Š�GÑÔÐØˆxøÝð 1ð 1ð 1Ø�˜a™œÔ)Ð0Ð0¨QÐ0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs   † �
A§AÁAÁA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�   )	Úastr  ÚSyntaxErrorr  rÅ   r  ra   r  r  )rE   Ú_astr  Úlocs       r&   Ú_lint_python_inprocr-  ¨  só   € ð ÐÐÐð1Ø�
Š
�7ÑÔÐØˆxøÝð :ð :ð :Ø:;¼(ÐJÐ6˜œÐ6Ð6¨1¬8Ð6Ð6Ð6Ð6ÈˆØ�˜a™œÔ)Ð9Ð9¨Q¬UÐ9°CÐ9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9øøøøÝð 1ð 1ð 1Ø�˜a™œÔ)Ð0Ð0¨QÐ0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0øøøøð1øøøs,   † �
B§=A*Á$BÁ*BÁ7BÂBÂB)rù   ú.jsonú.yamlú.ymlú.toml>   r0  r.  r1  r/  rÄ   i È  rº   ré   Ú__hermes_not_regular__ÚvalueÚdefaultc                 óT   — 	 t          | ¦  «        S # t          t          f$ r |cY S w xY w)z8Best-effort integer coercion for tool pagination inputs.)rg   Ú	TypeErrorÚ
ValueError)r3  r4  s     r&   Ú_coerce_intr8  ä  s<   € ðÝ�5‰zŒzÐøÝ•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:  Úmaxr8  ÚDEFAULT_READ_OFFSETÚDEFAULT_READ_LIMITÚmin)rÅ   rÆ   r:  Ú	max_linesÚnormalized_offsetÚnormalized_limits         r&   Únormalize_read_paginationrC  ì  st   € ð 7Ð6Ð6Ð6Ð6Ð6Ø�‘”€IÝ˜A�{¨6Õ3FÑGÔGÑHÔHÐÝ" 5Õ*<Ñ=Ô=ÐÝ˜1�cÐ"2°IÑ>Ô>Ñ?Ô?ÐØÐ.Ð.Ð.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<  r8  ÚDEFAULT_SEARCH_OFFSETÚDEFAULT_SEARCH_LIMIT)rÅ   rÆ   rA  rB  s       r&   Únormalize_search_paginationrG  ÿ  sF   € õ ˜A�{¨6Õ3HÑIÔIÑJÔJÐÝ˜1�k¨%Õ1EÑFÔFÑGÔGÐØÐ.Ð.Ð.r(   z(?<!\\)(?:\\\\)*\\nrê   c                 óX   — d| v p&t          t                               | ¦  «        ¦  «        S )aÆ  Return True when a content-search regex tries to match a newline.

    ``search_files`` runs rg/grep in line-oriented mode, not rg
    ``-U``/``--multiline`` mode, so newline regexes cannot match across
    lines.  Detect both a literal newline already decoded into the tool
    argument and a regex ``
`` escape (odd number of backslashes before
    ``n``).  Even backslashes, e.g. ``\n``, mean a literal backslash+n
    search and should not warn.
    r,   )r<   Ú_REGEX_NEWLINE_ESCAPE_RErï   )rê   s    r&   Ú_pattern_has_regex_newlinerJ  
  s*   € ð �7ˆ?ÐL�dÕ#;×#BÒ#BÀ7Ñ#KÔ#KÑLÔLÐLr(   rO   c                 ó   — | sdS d| v od| v S )z@Return True for rg's hard error when multiline mode is required.Fzliteral "\n" is not allowedú--multiliner-   rÏ   s    r&   Ú_is_line_oriented_newline_errorrM    s$   € àð ØˆuØ+¨uÐ4ÐO¸È%Ð9OÐOr(   c                 ó”   — | j         dk    st          |¦  «        s| S | j        rt          | j        ¦  «        s| S d| _        d| _        | S )zHAttach a newline-regex warning only when search found no usable results.r   Na  0 results found. Note: search_files content search is line-oriented and does not run ripgrep with -U/--multiline, so `\n` in the regex does not match line breaks. Use context=N to inspect neighboring lines, or escape as `\\n` when searching for a literal backslash+n.)rŠ   rJ  rO   rM  rq   )r   rê   s     r&   Ú)_maybe_warn_line_oriented_newline_patternrO    s_   € àÔ˜QÒÐÕ&@ÀÑ&IÔ&IÐØˆØ„|ð Õ;¸F¼LÑIÔIð ØˆØ€F„Lð	Pð „Nð €Mr(   c                   óü  — e Zd ZdZdYdefd„Z	 	 dZdededededef
d	„Zd
ede	fd„Z
d[dedefd„Zedede	fd„¦   «         ZdYd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dfd„ZdYdedee         dee         fd„ZdYdedee         de	fd „Zd!ed"ed#edefd$„Zdedefd%„Zededefd&„¦   «         Zd]ded(ed)edefd*„Zdedee         fd+„Zdedefd,„Zdedefd-„ZdYded.ee         defd/„Z dede!fd0„Z"d^ded2e	de!fd3„Z#ded2e	de!fd4„Z$d5ed6ede!fd7„Z%	 dYdededee         de!fd8„Z&	 d^ded9ed:ed;e	de'f
d<„Z(d=ede'fd>„Z)dYdedee         de*fd?„Z+	 dYdedee         d@ee         de*fdA„Z,de	fdB„Z-dCede	fdD„Z.dede	fdE„Z/deddfdF„Z0dddGœdedee         d@ee         defdH„Z1	 	 	 d_dLededMedNee         d)ed(edOedPede2fdQ„Z3dLededMedNee         d)ed(edOedPedee2         fdR„Z4dLededNee         dee         fdS„Z5dLeded)ed(ede2f
dT„Z6dLeded)ed(ede2f
dU„Z7dLededNee         d)ed(edOedPede2fdV„Z8dLededNee         d)ed(edOedPede2fdW„Z9dLededNee         d)ed(edOedPede2fdX„Z:dS )`Ú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.
        rR  NÚconfigú/)ÚenvÚgetattrrR  Ú_command_cache)r_   Úterminal_envrR  s      r&   Ú__init__zShellFileOperations.__init__6  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|¤Ž}|                     dd¦  «        }|                     d¦  «        r|dk    rd}t          |                     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]  rR  Ú
returncoder   Ústdin_errorrº   r¡   r   )r¨   r©   )rW  rV  rR  Úexecuter  r§   )	r_   r[  rR  r\  r]  ÚkwargsÚeffective_cwdr   r©   s	            r&   Ú_execzShellFileOperations._execY  sÎ   € ð" ˆØð 	(Ø 'ˆF�9ÑØÐ!Ø#-ˆF�<Ñ ð ÐI�w t¤x°¸Ñ=Ô=ÐIÀÄˆØ!�”Ô! 'ÐGÐG¨}ÐGÀÐGÐGˆØ—J’J˜|¨QÑ/Ô/ˆ	ð
 �:Š:�mÑ$Ô$ð 	¨°aª¨ØˆIÝØ—:’:˜h¨Ñ+Ô+Øð
ñ 
ô 
ð 	
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)rX  rd  r¨   r   )r_   re  r   s      r&   Ú_has_commandz ShellFileOperations._has_command€  s[   € à�dÔ)Ð)Ð)Ø—Z’ZÐ Q¨cÐ QÐ QÐ QÑRÔRˆFØ'-¤}×':Ò':Ñ'<Ô'<ÀÒ'EˆDÔ Ñ$ØÔ" 3Ô'Ð'r(   éè  r>   Úlengthc                 óŽ  — |                       d|› d|                      |¦  «        › d�¦  «        }|j        dk    rdS t          |j        ¦  «        }d                     |                     ¦   «         ¦  «        }|sdS t          j        d|¦  «        sdS 	 t          j
        |d	¬
¦  «        S # t          j        t          f$ r Y dS w xY w)u-  Fetch the first ``length`` raw bytes of a file through the terminal.

        File operations run through a terminal backend (possibly remote), so
        raw bytes cannot cross the transport directly â€” the terminal decodes
        stdout with ``errors="replace"`` and manufactures U+FFFD at every
        byte it cannot decode, including a multibyte character cut in half by
        ``head -c``. Wrapping the sample in base64 lets the original bytes
        survive the transport, so binary detection can happen at the byte
        layer where it is well-defined (#80308 and friends).

        Returns the sample bytes, or ``None`` when the transport could not
        produce clean base64 (exotic shells without ``base64``); callers fall
        back to the legacy text-sample heuristic in that case.
        zhead -c Ú z 2>/dev/null | base64r   Nr   r(   z[A-Za-z0-9+/]+={0,2}T©Úvalidate)rd  Ú_escape_shell_argr©   r'   r¨   r!   r±   r»   Ú	fullmatchÚbase64Ú	b64decodeÚbinasciiÚErrorr7  )r_   r>   rj  r   Úencodeds        r&   Ú_sample_file_bytesz&ShellFileOperations._sample_file_bytes‡  sã   € ð —’ØS�vÐSÐS × 6Ò 6°tÑ <Ô <ÐSÐSÐSñ
ô 
ˆð Ô˜qÒ Ð Ø�4Ý-¨f¬mÑ<Ô<ˆØ—'’'˜'Ÿ-š-™/œ/Ñ*Ô*ˆØð 	Ø�3ÝŒ|Ð3°WÑ=Ô=ð 	Ø�4ð	ÝÔ# G°dÐ;Ñ;Ô;Ð;øÝ”¥
Ð+ð 	ð 	ð 	Ø�4�4ð	øøøs   ÂB* Â*CÃCr)   c                 ó  — | sdS d| v rdS 	 |                       d¦  «         dS # t          $ r_}|j        t          | ¦  «        dz
  k    r9	 | d|j        …                               d¦  «         Y d}~dS # t          $ r Y nw xY wY d}~dS d}~ww xY w)u  Byte-layer binary detection (the boundary for the #80308 class).

        Contract: a file is text when its sample is valid UTF-8, allowing one
        incomplete multibyte sequence at the very end (an artifact of cutting
        the sample at a byte boundary, not a property of the file). Anything
        else â€” NUL bytes, mid-stream invalid UTF-8 such as latin-1 or true
        binaries â€” stays read-only, preserving the anti-mojibake guarantee
        the old U+FFFD check existed for: a readâ†’editâ†’write round-trip must
        never rewrite undecodable bytes with replacement characters.

        A file that legitimately *contains* U+FFFD (EF BF BD â€” e.g. logs of
        lossy output) is valid UTF-8 and reads as text; the old text-layer
        check misclassified it because it could not tell a stored replacement
        character from a transport-manufactured one.
        Fó    Túutf-8é   N)ÚdecodeÚUnicodeDecodeErrorr½   r8   )r)   Úexcs     r&   Ú_is_likely_binary_bytesz+ShellFileOperations._is_likely_binary_bytes¦  sÓ   € ð" ð 	Ø�5Ø�fÐÐØ�4ð	Ø�MŠM˜'Ñ"Ô"Ð"Ø�5øÝ!ð 		ð 		ð 		ð Œy�C ™KœK¨!™OÒ+Ð+ðØ˜;˜SœY˜;Ô'×.Ò.¨wÑ7Ô7Ð7Ø ˜5˜5˜5˜5˜5øÝ)ð ð ð Ø�Dðøøøà�4�4�4�4�4øøøøð		øøøs2   Œ# £
B­BÁ	"A1Á1
A>Á;BÁ=A>Á>BÂBÚcontent_samplec                 ó4  — t           j                             |¦  «        d                              ¦   «         }|t          v rdS |rSd|dd…         v rdS t          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º   Tu   ï¿½Nri  c              3   óH   K  — | ]}t          |¦  «        d k     ¯|dv¯dV — ŒdS )é    z
	rº   N)Úord)rW   Úcs     r&   r  z8ShellFileOperations._is_likely_binary.<locals>.<genexpr>à  sJ   è è € ð  Eð  E aÝ"% a¡&¤&¨2¢+ +°!¸8Ð2CÐ2Cð !"Ø2CÐ2CÐ2CÐ2Cð Eð  Er(   g333333Ó?F)Úosr>   Úsplitextr  r   Úsumr?  r8   )r_   r>   r  ÚextÚnon_printables        r&   Ú_is_likely_binaryz%ShellFileOperations._is_likely_binaryÉ  sÂ   € õ Œg×Ò˜tÑ$Ô$ QÔ'×-Ò-Ñ/Ô/ˆØÕ#Ð#Ð#Ø�4ð ð 	Ið ˜>¨%¨4¨%Ô0Ð0Ð0Ø�tÝð  Eð  E¨>¸%¸4¸%Ô+@ð  Eñ  Eô  Eñ Eô EˆMà ¥3¥s¨>Ñ':Ô':¸DÑ#AÔ#AÑAÀDÒ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)r_   r>   rˆ  s      r&   Ú	_is_imagezShellFileOperations._is_imageæ  s4   € åŒg×Ò˜tÑ$Ô$ QÔ'×-Ò-Ñ/Ô/ˆØÕ&Ð&Ð&r(   rº   rE   Ú
start_linec                 ó  — ddl m}  |¦   «         }|                     d¦  «        }g }t          ||¬¦  «        D ]?\  }}t	          |¦  «        |k    r|d|…         dz   }|                     |› d|› �¦  «         Œ@d                     |¦  «        S )uJ  Add line numbers to content in ``LINE_NUM|CONTENT`` format.

        The gutter uses a compact ``<n>|`` prefix (e.g. ``34|foo``) rather
        than a fixed-width zero/space-padded one (``    34|foo``). The
        padding was pure token overhead: on dense source the padded gutter
        cost ~48% more tokens than the bare content and ~16% more than the
        compact form, because the leading spaces + zero-padding tokenize
        into extra tokens on every single line. An A/B (Sonnet 4.6, 2
        passes) showed the compact gutter matches the padded gutter on
        line-reference / patch / value-lookup / structure tasks (4/4 both),
        while dropping line numbers entirely regressed line-referencing
        (the model hand-counted and was off-by-one, 3/4) â€” so we keep the
        numbers, just not the padding.
        r   ©Úget_max_line_lengthr,   )r½   Nz... [truncated]Ú|)r;  r‘  r±   Ú	enumerater8   r    r!   )	r_   rE   rŽ  r‘  Úmax_line_lengthr‘   ÚnumberedÚir#   s	            r&   Ú_add_line_numbersz%ShellFileOperations._add_line_numbersë  s³   € ð 	AÐ@Ð@Ð@Ð@Ð@Ø-Ð-Ñ/Ô/ˆØ—’˜dÑ#Ô#ˆØˆÝ  ¨jÐ9Ñ9Ô9ð 	+ð 	+‰GˆAˆtå�4‰yŒy˜?Ò*Ð*ØÐ,˜_Ð,Ô-Ð0AÑA�Ø�OŠO˜q˜M˜M 4˜M˜MÑ*Ô*Ð*Ð*Ø�yŠy˜Ñ"Ô"Ð"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|}|r‰t          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º   NrU  z[a-zA-Z0-9._-]+zecho ~)	r6   rd  r©   r¨   r   Úfindr»   rp  r8   )
r_   r>   r   ÚhomeÚrestÚ	slash_idxÚusernameÚexpand_resultÚ	user_homeÚsuffixs
             r&   Ú_expand_pathz ShellFileOperations._expand_path  sr  € ð ð 	ØˆKð �?Š?˜3ÑÔñ 	2à—Z’Z Ñ-Ô-ˆFØÔ 1Ò$Ñ$¨¬×)<Ò)<Ñ)>Ô)>Ð$Ø”}×*Ò*Ñ,Ô,�Ø˜3’;�;Ø�KØ—_’_ TÑ*Ô*ð +Ø $ q r r¤(™?Ð*ð ˜A˜B˜B”x�Ø ŸIšI c™NœN�	Ø/8¸Aª~¨~˜4 
  
Ô+Ð+À4�Øð 2¥¤Ð-?ÀÑ JÔ Jð 2ð %)§J¢JÐ/B¸Ð/BÐ/BÑ$CÔ$C�MØ$Ô.°!Ò3Ð3¸Ô8L×8RÒ8RÑ8TÔ8TÐ3Ø$1Ô$8×$>Ò$>Ñ$@Ô$@˜	Ø!% a­#¨h©-¬-Ñ&7Ð&8Ð&8Ô!9˜Ø(¨6Ñ1Ð1àˆr(   Úargc                 ó\   — ddl m}  ||¦  «        }d|                     dd¦  «        z   dz   S )a4  Escape a string for safe use in shell commands.

        On Windows native drive paths (``C:\Users\x`` / ``C:/Users/x``)
        and mixed MSYS leftovers (``/c/Users\x``) are rewritten to the
        Git Bash ``/c/Users/x`` form via ``_bash_safe_path``: bash eats
        backslashes and MSYS otherwise mangles drive paths into the
        ``Directory \drivers\etc does not exist`` failure class. Reuses
        the env-layer translator so shell file ops and the terminal ``cd``
        agree on the path form. No-op off Windows and for plain POSIX paths.
        r   )Ú_bash_safe_pathú'z'"'"')Útools.environments.localr¥  r   )r_   r£  r¥  s      r&   ro  z%ShellFileOperations._escape_shell_arg*  sC   € ð 	=Ð<Ð<Ð<Ð<Ð<àˆo˜cÑ"Ô"ˆà�S—[’[  iÑ0Ô0Ñ0°3Ñ6Ð6r(   r§   c                 ó  — |                       |¦  «        }t          j                             |¦  «        pd}|                       |¦  «        }|                       d¦  «        }d|› d|› d�|z   dz   }|                      ||¬¦  «        S )a­  Write ``content`` to ``path`` atomically via temp-file + rename.

        Streams ``content`` over stdin into a temp file in the SAME
        directory as ``path`` (so the final ``mv`` is a real rename on the
        same filesystem, not a non-atomic cross-device copy), preserves the
        existing file's mode if it exists, then renames over the target.
        On any failure the temp file is removed so we never leak a partial
        ``.hermes-tmp`` file next to the user's data, and the original file
        is left untouched. Content rides stdin so there is no ARG_MAX limit.

        ``mkdir -p`` for the parent directory is folded into this script
        (one fewer subprocess vs. a separate ``mkdir -p`` call).

        Returns an :class:`ExecuteResult`; ``exit_code == 0`` means the file
        was swapped into place atomically. A non-zero exit means nothing was
        renamed and the original (if any) is intact.
        rè   z.hermes-tmp.XXXXXXz
set -e; d=z; t=z¾; if [ -L "$t" ]; then rt="$(readlink -f "$t" 2>/dev/null || realpath "$t" 2>/dev/null || true)"; [ -n "$rt" ] && { t="$rt"; d="$(dirname "$t")"; }; fi; mkdir -p "$d"; tmp="$(mktemp -p "$d" aó   2>/dev/null || mktemp "$d/.hermes-tmp.$$.XXXXXX" 2>/dev/null || { tmp="$d/.hermes-tmp.$$"; : > "$tmp" && echo "$tmp"; })"; [ -n "$tmp" ] || { echo "atomic write: could not create temp file" >&2; exit 1; }; trap 'rm -f \"$tmp\"' EXIT; if [ -e "$t" ]; then m="$(stat -c%a "$t" 2>/dev/null || stat -f%Lp "$t" 2>/dev/null || true)"; [ -n "$m" ] && chmod "$m" "$tmp" 2>/dev/null || true; fi; cat > "$tmp"; if [ ! -e "$t" ]; then chmod "=rw" "$tmp" 2>/dev/null || true; fi; mv -f "$tmp" "$t"; trap - EXIT)r]  )ro  r…  r>   Údirnamerd  )r_   r>   rE   Úq_pathÚparentÚq_parentÚtmplÚscripts           r&   Ú_atomic_writez!ShellFileOperations._atomic_write;  s®   € ð$ ×'Ò'¨Ñ-Ô-ˆÝ”—’ Ñ&Ô&Ð-¨#ˆØ×)Ò)¨&Ñ1Ô1ˆð ×%Ò%Ð&:Ñ;Ô;ˆð6%Øð%ð %Ø%ð%ð %ð %ð" (,ñ#,ð"/ñ#!ð 	ðH �zŠz˜&¨WˆzÑ5Ô5Ð5r(   rÒ   c                 óÐ   — |rt          |¦  «        S d|                      |¦  «        › d�}|                      |¦  «        }|j        dk    s|j        sdS t          |j        ¦  «        S )uØ  Detect the dominant line ending of a file on disk.

        If ``pre_content`` is already available (we just read the file
        for lint/LSP purposes), inspect that â€” zero extra exec calls.
        Otherwise issue a tiny ``head -c 4096`` to sample the first 4KB.

        Returns ``"\r\n"`` for CRLF (Windows), ``"\n"`` for LF (Unix),
        or ``None`` if undetermined (new file, empty file, single-line
        file with no line break in the first chunk).
        zhead -c 4096 ú 2>/dev/nullr   N)r/   ro  rd  r©   r¨   ©r_   r>   rÒ   Úhead_cmdÚhead_results        r&   Ú_detect_file_line_endingz,ShellFileOperations._detect_file_line_ending“  sx   € ð ð 	4Ý& {Ñ3Ô3Ð3ð N 4×#9Ò#9¸$Ñ#?Ô#?ÐMÐMÐMˆØ—j’j Ñ*Ô*ˆØÔ  AÒ%Ð%¨[Ô-?Ð%Ø�4Ý" ;Ô#5Ñ6Ô6Ð6r(   c                 ó®   — d|                       |¦  «        › d�}|                      |¦  «        }|j        dk    s|j        sdS t	          |j        ¦  «        S )u0  Whether the file on disk starts with a UTF-8 BOM.

        Always probes the first 3 bytes on disk â€” do NOT trust
        ``pre_content`` for BOM detection because the most common
        provider (``read_file_raw``) deliberately strips BOMs so the
        agent never sees U+FEFF glyphs.  Passing BOM-stripped content
        through ``pre_content`` would cause a false-negative and
        silently remove the marker on rewrite.

        A missing/empty file returns False (new writes get no BOM
        unless the caller explicitly includes one).
        z
head -c 3 r±  r   F)ro  rd  r©   r¨   r=   r²  s        r&   Ú_file_has_bomz!ShellFileOperations._file_has_bom¨  s`   € ð K × 6Ò 6°tÑ <Ô <ÐJÐJÐJˆØ—j’j Ñ*Ô*ˆØÔ  AÒ%Ð%¨[Ô-?Ð%Ø�5Ý˜Ô*Ñ+Ô+Ð+r(   Ú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!   )r_   r¸  r¹  rº  Ú	old_linesÚ	new_linesrw   s          r&   Ú_unified_diffz!ShellFileOperations._unified_diff¼  sn   € à×*Ò*°DÐ*Ñ9Ô9ˆ	Ø×*Ò*°DÐ*Ñ9Ô9ˆ	ÝÔ#Ø�yØ$˜(�_�_Ø"˜�?�?ð
ñ 
ô 
ˆð
 �wŠw�t‰}Œ}Ðr(   c           	      óT   — |                       |¦  «        }d|› d|› d|› dt          › d�	S )u‹  Byte size of a regular file, without opening one that never ends.

        ``wc -c < path`` opens the path. On a FIFO with no writer, a socket,
        or a character device like /dev/zero that never reaches EOF, that
        read blocks forever â€” and the read helpers pass no timeout to
        :meth:`_exec`, so the turn wedges until the process is killed. The
        device blocklist in ``tools/file_tools.py`` cannot cover this: it
        matches literal ``/dev/*`` names, while a FIFO is a file *type* and
        can sit at any path.

        ``[ -f ]`` is a stat, not an open â€” it answers exactly the question
        the size probe needs (regular file, symlinks followed) without
        touching the contents. Non-regular paths that exist report the
        sentinel so callers can say so instead of claiming the file is
        missing; a genuinely absent path still exits non-zero.
        zif [ -f z ]; then wc -c < z 2>/dev/null; elif [ -e z ]; then echo z; else exit 1; fi)ro  ÚNOT_REGULAR_SENTINEL)r_   r>   r£  s      r&   Ú_size_probe_cmdz#ShellFileOperations._size_probe_cmdË  sX   € ð" ×$Ò$ TÑ*Ô*ˆð�sð ð ¨Sð ð Øðð Ý,@ðð ð ð	
r(   c                 ó*   — t          d| › d�¬¦  «        S )z5Error for a path that exists but would block if read.zCannot read 'z`': not a regular file (directory, FIFO, socket, or device). Reading it could block indefinitely.rÏ   rÐ   rA   s    r&   Ú_not_regular_errorz&ShellFileOperations._not_regular_errorã  s5   € õ ðK ð Kð Kð Kð
ñ 
ô 
ð 	
r(   rÄ   rÅ   rÆ   c                 óì  — |                       |¦  «        }t          ||¦  «        \  }}|                      |                      |¦  «        ¦  «        }|j        dk    ri|                      |¦  «        }|�=|                      |||¬¦  «        }d|› d|› d�}|j        r|› d|j        › �n||_        |S |                      |¦  «        S t          |j
        ¦  «        }|                     ¦   «         t          k    r|                      |¦  «        S 	 t          |                     ¦   «         ¦  «        }	n# t          $ r d}	Y nw xY w|	t           k    r	 |                      |¦  «        rt%          dd|	d	¬
¦  «        S |                      |¦  «        }
|
�Vt(          j                             |¦  «        d                              ¦   «         t0          v }|p|                      |
¦  «        }nXd|                      |¦  «        › d�}|                      |¦  «        }t          |j
        ¦  «        }|                      ||¦  «        }|rt%          d|	d¬¦  «        S ddlm} d |¦   «         z  dz   }||z   dz
  }d|› d|› d|                      |¦  «        › d|› �}|                      |¦  «        }|j        dk    rt%          d|j
        › �¬¦  «        S t          |j
        ¦  «        }|dk    rt=          |¦  «        \  }}d|                      |¦  «        › �}|                      |¦  «        }t          |j
        ¦  «        }	 t          |                     ¦   «         ¦  «        }n# t          $ r d}Y nw xY w||k    }d}|rd|dz   › d|› d|› d|› d�	}|s„|                     d¦  «        rod|                      |¦  «        › d �}|                      |¦  «        }t          |j
        ¦  «        }|j        dk    r"|                     ¦   «         d!k    r
|dd"…         }|	dk    rt%          d#ddd$¬%¦  «        S ||cxk    rdk    r n nt%          d#||	d&|› d'|› d(|› d)�¬%¦  «        S 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
        r   N)rÅ   rÆ   zNote: 'zD' not found byte-for-byte; resolved to the unicode-equivalent file 'zM' (invisible encoding difference: NFC/NFD or special space/quote characters).rl  Tz‹Image file detected. Automatically redirected to vision_analyze tool. Use vision_analyze with this file path to inspect the image contents.)rK   rJ   rG   rI   rº   úhead -c 1000 r±  zUBinary file - cannot display as text. Use appropriate tools to handle this file type.©rJ   rG   rO   r�  é   zsed -n 'Ú,zp' z | cut -b1-úFailed to read file: rÏ   zwc -l < zUse offset=z to continue reading (showing Ú-z of z lines)r,   z
tail -c 1 z | wc -lÚ0éÿÿÿÿr   zFile is empty (0 bytes).)rE   rF   rG   rI   zNote: offset z  is beyond the end of the file (z$ lines total). Retry with offset <= rè   )rE   rF   rG   rH   rI   )!r¢  rC  rd  rÅ  r©   Ú_unicode_variant_matchrÈ   rI   Ú_suggest_similar_filesr'   r¨   r   rÄ  rÇ  rg   r7  ÚMAX_FILE_SIZEr�  rD   rv  r…  r>   r†  r  r   r~  ro  rŠ  r;  r‘  r:   Úendswithr—  ) r_   r>   rÅ   rÆ   Ústat_resultÚvariantr   r|   Ústat_outputrG   Úsample_bytesÚ
ext_binaryrJ   Ú
sample_cmdÚsample_resultÚsample_outputr‘  Úline_clamp_bytesÚend_lineÚread_cmdÚread_resultÚread_outputÚ_Úwc_cmdÚ	wc_resultÚ	wc_outputrF   rH   rI   Útail_cmdÚtail_resultÚtail_outputs                                    r&   rÈ   zShellFileOperations.read_fileí  s½  € ð × Ò  Ñ&Ô&ˆå1°&¸%Ñ@Ô@‰ˆ�ð —j’j ×!5Ò!5°dÑ!;Ô!;Ñ<Ô<ˆàÔ  AÒ%Ð%ð ×1Ò1°$Ñ7Ô7ˆGØÐ"ØŸš¨¸Àe˜ÑLÔL�ð#˜dð #ð #Ø4;ð#ð #ð #ð ð :@¼ÐN Ð5Ð5¨¬Ð5Ð5Ð5È$�”Ø�à×.Ò.¨tÑ4Ô4Ð4å1°+Ô2DÑEÔEˆØ×ÒÑÔÕ"6Ò6Ð6Ø×*Ò*¨4Ñ0Ô0Ð0ð	Ý˜K×-Ò-Ñ/Ô/Ñ0Ô0ˆIˆIøÝð 	ð 	ð 	ØˆIˆIˆIð	øøøð •}Ò$Ð$àð �>Š>˜$ÑÔð 		ÝØØØ#ð\ðñ ô ð ð ×.Ò.¨tÑ4Ô4ˆØÐ#Ýœ×)Ò)¨$Ñ/Ô/°Ô2×8Ò8Ñ:Ô:Õ>OÐOˆJØ"ÐP d×&BÒ&BÀ<Ñ&PÔ&PˆIˆIàS¨×)?Ò)?ÀÑ)EÔ)EÐSÐSÐSˆJØ ŸJšJ zÑ2Ô2ˆMÝ7¸Ô8LÑMÔMˆMØ×.Ò.¨t°]ÑCÔCˆIàð 	ÝØØ#Ømðñ ô ð ð< 	AÐ@Ð@Ð@Ð@Ð@ØÐ2Ð2Ñ4Ô4Ñ4°qÑ8ÐØ˜E‘> AÑ%ˆð-�vð -ð - ð -ð -¨T×-CÒ-CÀDÑ-IÔ-Ið -ð -Ø*ð-ð -ð 	ð —j’j Ñ*Ô*ˆàÔ  AÒ%Ð%ÝÐ$P¸KÔ<NÐ$PÐ$PÐQÑQÔQÐQÝ1°+Ô2DÑEÔEˆð �QŠ;ˆ;Ý'¨Ñ4Ô4‰NˆK˜ð ;˜D×2Ò2°4Ñ8Ô8Ð:Ð:ˆØ—J’J˜vÑ&Ô&ˆ	Ý/°	Ô0@ÑAÔAˆ	ð	Ý˜iŸošoÑ/Ô/Ñ0Ô0ˆKˆKøÝð 	ð 	ð 	ØˆKˆKˆKð	øøøð   (Ò*ˆ	ØˆØð 	yØx ¨A¡ÐxÐxÈVÐxÐxÐV^ÐxÐxÐdoÐxÐxÐxˆDð ð 	/˜[×1Ò1°$Ñ7Ô7ð 	/ØJ D×$:Ò$:¸4Ñ$@Ô$@ÐJÐJÐJˆHØŸ*š* XÑ.Ô.ˆKÝ5°kÔ6HÑIÔIˆKØÔ$¨Ò)Ð)¨k×.?Ò.?Ñ.AÔ.AÀSÒ.HÐ.HØ)¨#¨2¨#Ô.�ð ˜Š>ˆ>ÝØØØØ/ð	ñ ô ð ð �KÐ#Ð#Ò#Ð# !Ò#Ð#Ð#Ð#Ð#ÝØØ'Ø#ð& Fð &ð &Ø#ð&ð &à"ð&ð &ð &ð	ñ 	ô 	ð 	õ Ø×*Ò*¨;¸Ñ?Ô?Ø#ØØØð
ñ 
ô 
ð 	
s$   Ä!D. Ä.D=Ä<D=Ì2!M ÍM#Í"M#c                 ót  ‡‡‡— t           j                             |¦  «        pd}t           j                             |¦  «        Š‰sdS dt          dt          fd„Š ‰‰¦  «        Šd|                      |¦  «        › d�}|                      |¦  «        }|j        dk    s|j         	                    ¦   «         sdS ˆˆˆfd	„t          |j        ¦  «                             ¦   «         D ¦   «         }t          |¦  «        d
k    r8|dk    sd|v r&t           j                             ||d         ¦  «        n|d         S dS )u»  Find an existing file whose name is unicode-equivalent to ``path``.

        macOS names screenshots with a NARROW NO-BREAK SPACE (U+202F) before
        AM/PM, stores names NFD-decomposed, and Finder renames turn ' into
        â€™ â€” all invisible in rendered text. Compare directory entries
        under a normalization that erases exactly those differences and
        return the on-disk spelling when exactly one entry matches.
        rè   NÚnamer   c                 ól   — t          j        d| ¦  «        }dD ]\  }}|                     ||¦  «        }Œ|S )NÚNFC))u   â€¯rl  )õ   Â rl  )u   â€™r¦  )u   â€˜r¦  )ÚunicodedataÚ	normalizer   )rê  Úouträ   rå   s       r&   Ú_canonz:ShellFileOperations._unicode_variant_match.<locals>._canon²  sG   € õ Ô'¨¨tÑ4Ô4ˆCðð ,ð ,‘��Sð —k’k # sÑ+Ô+��ØˆJr(   úls -1 r±  r   c                 óB   •— g | ]}|r|‰k    ¯
 ‰|¦  «        ‰k    ¯|‘ŒS r-   r-   )rW   Úentryrñ  rº  r0   s     €€€r&   r›   z>ShellFileOperations._unicode_variant_match.<locals>.<listcomp>Ä  sJ   ø€ ð 
ð 
ð 
àØð
ð  (Ò*Ð*¨v¨v°e©}¬}ÀÒ/FÐ/Fð à/FÐ/FÐ/Fr(   rº   rU  )r…  r>   r©  Úbasenamere   ro  rd  r©   r¨   r   r'   r   r8   r!   )	r_   r>   Údir_pathÚls_cmdÚ	ls_resultÚ
candidatesrñ  rº  r0   s	         @@@r&   rÑ  z*ShellFileOperations._unicode_variant_match¤  sb  øøø€ õ ”7—?’? 4Ñ(Ô(Ð/¨CˆÝ”7×#Ò# DÑ)Ô)ˆØð 	Ø�4ð	�ð 	¥ð 	ð 	ð 	ð 	ð �˜Ñ!Ô!ˆØH˜$×0Ò0°Ñ:Ô:ÐHÐHÐHˆØ—J’J˜vÑ&Ô&ˆ	ØÔ !Ò#Ð#¨9Ô+;×+AÒ+AÑ+CÔ+CÐ#Ø�4ð
ð 
ð 
ð 
ð 
ð 
å4°YÔ5EÑFÔF×QÒQÑSÔSð
ñ 
ô 
ˆ
õ ˆz‰?Œ?˜aÒÐØ<DÈºO¸OÈsÐVZÈ{È{•2”7—<’< ¨*°Q¬-Ñ8Ô8Ð8Ð`jÐklÔ`mÐmØˆtr(   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œ|ršt           j                             |
¦  «        d                              ¦   «         |k    r_t          |¦  «        t          |¦  «        z  }t          |¦  «        t          t          |¦  «        t          |¦  «        ¦  «        dz  k    rd}|dk    r.t!          j        d||¦  «                             ¦   «         d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º   rò  ú 2>/dev/null | head -50r,   éd   éZ   éF   é<   é   é(   gš™™™™™Ù?é   Ngš™™™™™é?ré   c                 ó   — | d          S )Nr   r-   )Úxs    r&   ú<lambda>z<ShellFileOperations._suggest_similar_files.<locals>.<lambda>  s   €  1 Q¤4 %€ r(   )Úkeyc                 ó   — g | ]\  }}|‘ŒS r-   r-   )rW   râ  Úfps      r&   r›   z>ShellFileOperations._suggest_similar_files.<locals>.<listcomp>  s   € Ð.Ð.Ð.™%˜!˜R�2Ð.Ð.Ð.r(   rŒ   úFile not found: )rO   rR   )r…  r>   r©  rõ  r†  r  ro  rd  r©   r¨   r   r±   r6   r8   Úsetr<  r¾  ÚSequenceMatcherÚratior    r!   ÚsortrD   )r_   r>   rö  rº  Úbasename_no_extrˆ  Ú
lower_namer÷  rø  ÚscoredÚfÚlfÚscoreÚcommonÚsimilars                  r&   rÒ  z*ShellFileOperations._suggest_similar_filesÐ  s  € å”7—?’? 4Ñ(Ô(Ð/¨CˆÝ”7×#Ò# DÑ)Ô)ˆÝœ'×*Ò*¨8Ñ4Ô4°QÔ7ˆÝŒg×Ò˜xÑ(Ô(¨Ô+×1Ò1Ñ3Ô3ˆØ—^’^Ñ%Ô%ˆ
ð T˜$×0Ò0°Ñ:Ô:ÐSÐSÐSˆØ—J’J˜vÑ&Ô&ˆ	àˆØÔ !Ò#Ñ#¨	Ô(8×(>Ò(>Ñ(@Ô(@Ñ#ØÔ%×+Ò+Ñ-Ô-×3Ò3°DÑ9Ô9ð #Fñ #F�Øð ØØ—W’W‘Y”Y�Ø�ð ˜Ò#Ð#Ø�E‘Eå”W×%Ò% aÑ(Ô(¨Ô+×1Ò1Ñ3Ô3°×7LÒ7LÑ7NÔ7NÒNÐNØ�E�Eà—]’] :Ñ.Ô.ð #°*×2GÒ2GÈÑ2KÔ2Kð #Ø�E�Eà 2Ð%Ð%Ø�E�Eà˜:Ð%Ð%­#¨b©'¬'°Aª+¨+Ø�E�Eàð #�RœW×-Ò-¨aÑ0Ô0°Ô3×9Ò9Ñ;Ô;¸sÒBÐBÝ  ™_œ_­s°2©w¬wÑ6�FÝ˜6‘{”{¥c­#¨j©/¬/½3¸r¹7¼7Ñ&CÔ&CÀcÑ&IÒIÐIØ "˜ð ˜A’:�:¥'Ô"9Ø˜* bñ#ô #ç’%‘'”'˜Sò#!ð #!ð �Eà˜1’9�9Ø—M’M 5­"¬'¯,ª,°xÀÑ*CÔ*CÐ"DÑEÔEÐEùà�Š˜˜ˆÑ(Ô(Ð(Ø.Ð. 6¨"¨1¨"¤:Ð.Ñ.Ô.ˆåØ+ TÐ+Ð+Ø!ð
ñ 
ô 
ð 	
r(   c                 ó´  — |                       |¦  «        }|                      |                      |¦  «        ¦  «        }|j        dk    r|                      |¦  «        S t          |j        ¦  «        }|                     ¦   «         t          k    r|  	                    |¦  «        S 	 t          |                     ¦   «         ¦  «        }n# t          $ r d}Y nw xY w|                      |¦  «        rt          dd|¬¦  «        S |                      |¦  «        }|�Vt          j                             |¦  «        d                              ¦   «         t&          v }|p|                      |¦  «        }nV|                      d|                      |¦  «        › d�¦  «        }t          |j        ¦  «        }	|                      ||	¦  «        }|rt          d|d¬	¦  «        S |                      d
|                      |¦  «        › �¦  «        }
|
j        dk    rt          d|
j        › �¬¦  «        S t/          t          |
j        ¦  «        ¦  «        \  }}t          ||¬¦  «        S )zÅRead 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   T)rK   rJ   rG   Nrº   rÉ  r±  u'   Binary file â€” cannot display as text.rÊ  úcat rÍ  rÏ   )rE   rG   )r¢  rd  rÅ  r©   rÒ  r'   r¨   r   rÄ  rÇ  rg   r7  r�  rD   rv  r…  r>   r†  r  r   r~  ro  rŠ  r:   )r_   r>   rÕ  r×  rG   rØ  rÙ  rJ   rÛ  rÜ  Ú
cat_resultÚraw_contentrâ  s                r&   rÌ   z!ShellFileOperations.read_file_raw  si  € ð × Ò  Ñ&Ô&ˆØ—j’j ×!5Ò!5°dÑ!;Ô!;Ñ<Ô<ˆØÔ  AÒ%Ð%Ø×.Ò.¨tÑ4Ô4Ð4Ý1°+Ô2DÑEÔEˆØ×ÒÑÔÕ"6Ò6Ð6Ø×*Ò*¨4Ñ0Ô0Ð0ð	Ý˜K×-Ò-Ñ/Ô/Ñ0Ô0ˆIˆIøÝð 	ð 	ð 	ØˆIˆIˆIð	øøøà�>Š>˜$ÑÔð 	RÝ t°tÀyÐQÑQÔQÐQØ×.Ò.¨tÑ4Ô4ˆØÐ#Ýœ×)Ò)¨$Ñ/Ô/°Ô2×8Ò8Ñ:Ô:Õ>OÐOˆJØ"ÐP d×&BÒ&BÀ<Ñ&PÔ&PˆIˆIà ŸJšJÐ'a°t×7MÒ7MÈdÑ7SÔ7SÐ'aÐ'aÐ'aÑbÔbˆMÝ7¸Ô8LÑMÔMˆMØ×.Ò.¨t°]ÑCÔCˆIØð 	ÝØ¨)Ø?ðñ ô ð ð —Z’ZÐ E t×'=Ò'=¸dÑ'CÔ'CÐ EÐ EÑFÔFˆ
ØÔ 1Ò$Ð$ÝÐ$O¸JÔ<MÐ$OÐ$OÐPÑPÔPÐPõ $Õ$?À
Ô@QÑ$RÔ$RÑSÔS‰ˆ�QÝØØð
ñ 
ô 
ð 	
s   Â%!C ÃCÃCrÍ   c                 óÞ  — |                       |¦  «        }|                      |                      |¦  «        ¦  «        }|j        dk    rt	          d|› �¬¦  «        S t          |j        ¦  «        }|                     ¦   «         t          k    r|  	                    |¦  «        S 	 t          |                     ¦   «         ¦  «        }n## t          $ r t	          d|› �¬¦  «        cY S w xY w|� ||k    rt	          |d|d›d|d›d	�¬
¦  «        S |                      d|                      |¦  «        › �¦  «        }|j        dk    rt	          d|j        › �¬¦  «        S d                     t          |j        ¦  «                             ¦   «         ¦  «        }	 t          j        |d¬¦  «         n4# t          t          j        j        f$ r t	          d|› �¬¦  «        cY S w xY wt	          ||d¬¦  «        S )z9Read binary-safe bytes from any shell-backed environment.r   r	  rÏ   zCould not determine file size: NzFile is too large (rÌ  z bytes, limit is r  )rG   rO   z	base64 < zFailed to read binary file: r   Trm  z*Backend returned invalid binary data for: )rL   rG   rJ   )r¢  rd  rÅ  r©   rD   r'   r¨   r   rÄ  rÇ  rg   r7  ro  r!   r±   rq  rr  rs  rt  )r_   r>   rÍ   rÕ  r×  rG   ru  Úcompacts           r&   rÑ   z#ShellFileOperations.read_file_bytes9  sC  € à× Ò  Ñ&Ô&ˆØ—j’j ×!5Ò!5°dÑ!;Ô!;Ñ<Ô<ˆØÔ  AÒ%Ð%ÝÐ$=°tÐ$=Ð$=Ð>Ñ>Ô>Ð>Ý1°+Ô2DÑEÔEˆØ×ÒÑÔÕ"6Ò6Ð6Ø×*Ò*¨4Ñ0Ô0Ð0ð	NÝ˜K×-Ò-Ñ/Ô/Ñ0Ô0ˆIˆIøÝð 	Nð 	Nð 	NÝÐ$LÀdÐ$LÐ$LÐMÑMÔMÐMÐMÐMð	NøøøàÐ  Y°Ò%:Ð%:ÝØ#ØX¨IÐXÐXÐXÈ)ÐXÐXÐXÐXðñ ô ð ð
 —*’*ÐG¨×)?Ò)?ÀÑ)EÔ)EÐGÐGÑHÔHˆØÔ Ò!Ð!ÝÐ$SÀ7Ä>Ð$SÐ$SÐTÑTÔTÐTØ—'’'Õ5°g´nÑEÔE×KÒKÑMÔMÑNÔNˆð	YÝÔ˜W¨tÐ4Ñ4Ô4Ð4Ð4øÝ�FœOÔ1Ð2ð 	Yð 	Yð 	YÝÐ$WÐQUÐ$WÐ$WÐXÑXÔXÐXÐXÐXð	YøøøåØ"ØØð
ñ 
ô 
ð 	
s$   Â#!C ÃC%Ã$C%ÆF) Æ).GÇGc                 ó0   — |                       |d¬¦  «        S )u(  Delete a single file.

        Cross-platform: runs via ``python -c`` against the terminal env's
        Python so it works on Windows shells (``cmd.exe``/PowerShell) that
        don't ship ``rm``. Directories are rejected here â€” use
        ``delete_path(recursive=True)`` for trees.
        F©rà   ©Ú_python_deleterË   s     r&   rß   zShellFileOperations.delete_fileZ  s   € ð ×"Ò" 4°5Ð"Ñ9Ô9Ð9r(   Frà   c                 ó0   — |                       ||¬¦  «        S )a  Cross-platform delete that handles files and (with recursive=True)
        directory trees. Always preferred over emitting ``rm -rf`` /
        ``Remove-Item -Recurse`` directly so the same tool call works on
        every backend (local / docker / ssh / Windows).
        r  r  râ   s      r&   rã   zShellFileOperations.delete_pathd  s   € ð ×"Ò" 4°9Ð"Ñ=Ô=Ð=r(   c                 ó
  — |                       |¦  «        }t          |d¬¦  «        }|rt          |¬¦  «        S d|›dt          |¦  «        ›d�}|                      d|                      |¦  «        › �¦  «        }|j        dk    r6d	|j        pd
v r+|                      d|                      |¦  «        › �¦  «        }|j        dk    r1t          d|› d|j        pd
                     ¦   «         pd› �¬¦  «        S t          ¦   «         S )NÚDelete©ÚverbrÏ   z-import shutil, pathlib, sys
p = pathlib.Path(z)
recursive = aI  
try:
    if p.is_dir() and not p.is_symlink():
        if recursive:
            shutil.rmtree(p)
        else:
            print('is a directory: ' + str(p), file=sys.stderr); sys.exit(2)
    else:
        p.unlink()
except FileNotFoundError:
    pass
except Exception as exc:
    print(str(exc), file=sys.stderr); sys.exit(1)
zpython3 -c r   Úpython3r   z
python -c zFailed to delete r�   zunknown error)	r¢  r   rk   r<   rd  ro  r©   r¨   r   )r_   r>   rà   ÚdeniedÚsnippetr   s         r&   r  z"ShellFileOperations._python_deletel  s<  € Ø× Ò  Ñ&Ô&ˆÝ'¨°8Ð<Ñ<Ô<ˆØð 	-Ý VÐ,Ñ,Ô,Ð,ðBØ $ðBð Bå 	™?œ?ðBð Bð Bð 	ð, —’ÐK¨$×*@Ò*@ÀÑ*IÔ*IÐKÐKÑLÔLˆð Ô˜qÒ Ð  Y°6´=Ð3FÀBÐ%GÐ%GØ—Z’ZÐ N¨T×-CÒ-CÀGÑ-LÔ-LÐ NÐ NÑOÔOˆFàÔ˜qÒ Ð ÝÐ%s¸Ð%sÐ%sÀ&Ä-ÐBUÐSU×A\ÒA\ÑA^ÔA^ÐAqÐbqÐ%sÐ%sÐtÑtÔtÐtå‰}Œ}Ðr(   rä   rå   c                 óž  — |                       |¦  «        }|                       |¦  «        }||fD ]'}t          |d¬¦  «        }|rt          |¬¦  «        c S Œ(|                      d|                      |¦  «        › d|                      |¦  «        › �¦  «        }|j        dk    rt          d|› d|› d	|j        › �¬¦  «        S t          ¦   «         S )
zMove a file via mv.ÚMover#  rÏ   zmv rl  r   zFailed to move z -> r�   )r¢  r   rk   rd  ro  r©   r¨   )r_   rä   rå   r  r&  r   s         r&   rç   zShellFileOperations.move_file˜  sù   € à×Ò Ñ$Ô$ˆØ×Ò Ñ$Ô$ˆØ�s�ð 	1ð 	1ˆAÝ+¨A°FÐ;Ñ;Ô;ˆFØð 1Ý"¨Ð0Ñ0Ô0Ð0Ð0Ð0ð1à—’ØM�$×(Ò(¨Ñ-Ô-ÐMÐM°×0FÒ0FÀsÑ0KÔ0KÐMÐMñ
ô 
ˆð Ô˜qÒ Ð ÝÐ%V°sÐ%VÐ%VÀÐ%VÐ%VÀvÄ}Ð%VÐ%VÐWÑWÔWÐWÝ‰}Œ}Ðr(   c           	      óÊ  — |                       |¦  «        }t          |¦  «        }|rt          |¬¦  «        S t          j        d|¦  «        }|r*t          d|› d|                     d¦  «        ›d�¬¦  «        S t          j                             |¦  «        d          	                    ¦   «         }|t          v rt                               |¦  «        nd}|�0 ||¦  «        \  }}	|s |	d	k    rt          d|› d
|› d|	› d�¬¦  «        S |t          v p|                      |¦  «        }
|
rJ|�nGd|                      |¦  «        › d�}|                      |¦  «        }|j        dk    r|j        r|j        }|                      ||¦  «        }|dk    rt'          |d¦  «        }|                      ||¦  «        rt+          |¦  «        s
t,          |z   }|                      |¦  «         t          j                             |¦  «        }t3          |¦  «        }	 |                     dd¦  «        }n.# t6          $ r!}t          d|› d|› d�¬¦  «        cY d}~S d}~ww xY w|                      ||¦  «        }|j        dk    rt          d|j        › �¬¦  «        S t;          |¦  «        }d}	 d|                      |¦  «        › d�}|                      |¦  «        }|j        dk    rŒ|j                             ¦   «         rs|j                             ¦   «                              ¦   «         d         }tA          j!        |¦  «         "                    ¦   «         }||k    }|st          d|› d�¬¦  «        S n# tF          $ r d}Y nw xY w|  $                    |||¬¦  «        }d}|j%        s|j&        r|  '                    |||¬¦  «        }|r|}t          ||||r| (                    ¦   «         nd|¬¦  «        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.

        Before anything touches disk, a fail-closed syntax gate runs
        against the CANDIDATE content: if ``path``'s extension is in
        ``_FAIL_CLOSED_INPROC_EXTS`` (JSON/YAML/TOML â€” structured data
        formats where a parse failure always means corruption) and the
        candidate content doesn't parse, the write is refused outright.
        No temp file, no rename, nothing on disk changes.

        After a write that clears the gate, 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 gate didn't already catch (i.e. errors from a linter
        outside ``_FAIL_CLOSED_INPROC_EXTS``, such as Python), 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
            pre_content: Pre-edit file content if the caller already has it
                (e.g. patch_replace read the file for fuzzy matching).
                When provided, skips a redundant ``cat`` subprocess to
                re-read the file for lint baseline / line-ending
                detection. BOM detection always probes disk (the most
                common provider â€” ``read_file_raw`` â€” strips BOMs, so
                trusting ``pre_content`` for BOM would cause false
                negatives and silent marker loss on rewrite). When
                None, reads from disk as before.

        Returns:
            WriteResult with bytes written, lint summary, or error.
        rÏ   z[\ud800-\udc7f\udd00-\udfff]zRefusing to write 'z0': content contains a lone surrogate character (r   zH) that cannot be encoded as UTF-8. The file was NOT created or modified.rº   Nr  z': candidate content fails z syntax validation (zC). The file was NOT created or modified. Fix the content and retry.r  r±  r+   ry  ÚsurrogateescapezFailed to write file: z
sha256sum ú#Post-write verification failed for u{   : on-disk content hash differs from the intended write. The write did not persist correctly â€” re-read the file and retry.©rÒ   Úpost_content)rl   rm   rn   ro   rp   ))r¢  r   rk   r»   rï   r¾   r…  r>   r†  r  Ú_FAIL_CLOSED_INPROC_EXTSÚLINTERS_INPROCr  Ú_lsp_handles_extensionro  rd  r©   r¨   rµ  r4   r·  r=   r7   Ú_snapshot_lsp_baseliner©  r<   ÚencodeÚUnicodeEncodeErrorr¯  r8   r   r±   ÚhashlibÚsha256Ú	hexdigestr  Ú_check_lint_deltarv   r    Ú_maybe_lsp_diagnosticsr`   )r_   r>   rE   rÒ   r&  r“   rˆ  Úinproc_linterÚ_okÚ	_lint_errÚwant_prerß  rà  Úoriginal_endingr«  rm   Úcontent_bytesr}  Úwrite_resultrl   Úcontent_verifiedÚhash_cmdÚhash_resultÚdisk_shaÚexpected_shaÚlint_resultrp   Úblocks                               r&   rÔ   zShellFileOperations.write_file«  s  € ðT × Ò  Ñ&Ô&ˆõ (¨Ñ-Ô-ˆØð 	-Ý VÐ,Ñ,Ô,Ð,õ ŒIÐ5°wÑ?Ô?ˆØð 	ÝðN¨$ð Nð NØ,-¯GªG°A©J¬JðNð Nð Nðñ ô ð õB Œg×Ò˜tÑ$Ô$ QÔ'×-Ò-Ñ/Ô/ˆØ36Õ:RÐ3RÐ3R�×*Ò*¨3Ñ/Ô/Ð/ÐX\ˆØÐ$Ø*˜]¨7Ñ3Ô3‰NˆC�Øð ˜9¨
Ò2Ð2Ý"ðN¨dð Nð NØðNð NØ4=ðNð Nð Nðñ ô ð ð. �.Ð(ÐL¨D×,GÒ,GÈÑ,LÔ,LˆØð 	5ØÐ&ð ð M $×"8Ò"8¸Ñ">Ô">ÐLÐLÐL�Ø"Ÿjšj¨Ñ2Ô2�ØÔ(¨AÒ-Ð-°+Ô2DÐ-Ø"-Ô"4�Kð ×7Ò7¸¸kÑJÔJˆØ˜fÒ$Ð$Ý-¨g°vÑ>Ô>ˆGð ×Ò˜d KÑ0Ô0ð 	*½À'Ñ9JÔ9Jð 	*Ý 'Ñ)ˆGð 	×#Ò# DÑ)Ô)Ð)õ ”—’ Ñ&Ô&ˆÝ˜F‘|”|ˆð,		Ø#ŸNšN¨7Ð4EÑFÔFˆMˆMøÝ!ð 	ð 	ð 	ÝðC¨$ð Cð CØ,/ðCð Cð Cðñ ô ð ð ð ð ð ð øøøøð	øøøð ×)Ò)¨$°Ñ8Ô8ˆàÔ! QÒ&Ð&ÝÐ%S¸lÔ>QÐ%SÐ%SÐTÑTÔTÐTõ ˜MÑ*Ô*ˆð ,0Ðð	$ØN D×$:Ò$:¸4Ñ$@Ô$@ÐNÐNÐNˆHØŸ*š* XÑ.Ô.ˆKØÔ$¨Ò)Ð)¨kÔ.@×.FÒ.FÑ.HÔ.HÐ)Ø&Ô-×3Ò3Ñ5Ô5×;Ò;Ñ=Ô=¸aÔ@�Ý&œ~¨mÑ<Ô<×FÒFÑHÔH�Ø#+¨|Ò#;Ð Ø'ð Ý&ð)À$ð )ð )ð )ðñ ô ð øøõ ð 	$ð 	$ð 	$Ø#ÐÐÐð	$øøøð ×,Ò,¨T¸{ÐY`Ð,ÑaÔaˆð *.ˆØÔð 	( +Ô"5ð 	(Ø×/Ò/Ø +¸Gð 0ñ ô ˆEð ð (Ø"'�åØ'Ø%Ø%Ø*5Ð?�×$Ò$Ñ&Ô&Ð&¸4Ø+ð
ñ 
ô 
ð 	
s1   ÈH& È&
IÈ0IÉIÉIÊCM% Í%M4Í3M4rÖ   r×   rØ   c           
      ó   — |                       |¦  «        }t          |¦  «        }|rt          |¬¦  «        S d|                      |¦  «        › d�}|                      |¦  «        }|j        dk    rt          d|› �¬¦  «        S |j        }|}	t          |¦  «        \  }}
ddlm	}  |||||¦  «        \  }}}}|s|dk    riddlm
}  ||||¦  «        rt          ddd	|› d
�¬¦  «        S |pd|› �}	 ddlm} | |||||¦  «        z  }n# t          $ r Y nw xY wt          |¬¦  «        S t          |¦  «        }|rt          ||¦  «        }|                      |||	¬¦  «        }|j        rt          d|j        › �¬¦  «        S d|                      |¦  «        › d�}|                      |¦  «        }|j        dk    rt          d|› �¬¦  «        S t          |j        ¦  «        \  }}
|                     dd¦  «                             dd¦  «        }|                     dd¦  «                             dd¦  «        }||k    r4t          d|› dt%          |¦  «        › dt%          |¦  «        › d�¬¦  «        S |                      |||¦  «        }|                      |||¬¦  «        }t          d||g|r|                     ¦   «         nd|j        ¬¦  «        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  r±  r   rÍ  )Úfuzzy_find_and_replace)Úis_already_appliedTuT   File already contains the target text â€” the edit appears to be already applied to z0. No write performed; do not re-send this patch.)rv   r{   r|   z'Could not find match for old_string in )Úformat_no_match_hint)rÒ   zFailed to write changes: z2Post-write verification failed: could not re-read r+   r,   r2   r,  z5: on-disk content differs from intended write (wrote z chars, read back zb chars after normalizing line endings). The patch did not persist. Re-read the file and try again.r-  N)rv   rw   rx   ro   rp   )r¢  r   ru   ro  rd  r©   r¨   r:   Útools.fuzzy_matchrI  rJ  rK  r  r/   r4   rÔ   rO   r   r8   rÂ  r8  r`   rp   )r_   r>   rÖ   r×   rØ   r&  rß  rà  rE   r  râ  rI  r¹  Úmatch_countÚ	_strategyrO   rJ  Úerr_msgrK  Úfile_endingr@  Ú
verify_cmdÚverify_resultÚ_verify_bomlessÚ_verify_stdout_normalizedÚ_new_content_normalizedrw   rF  s                               r&   rÚ   z!ShellFileOperations.patch_replace¼  s  € ð × Ò  Ñ&Ô&ˆõ (¨Ñ-Ô-ˆØð 	-Ý VÐ,Ñ,Ô,Ð,ð E˜$×0Ò0°Ñ6Ô6ÐDÐDÐDˆØ—j’j Ñ*Ô*ˆàÔ  AÒ%Ð%ÝÐ%C¸TÐ%CÐ%CÐDÑDÔDÐDàÔ$ˆð ˆõ   Ñ(Ô(‰
ˆ�ð 	=Ð<Ð<Ð<Ð<Ð<à5KÐ5KØ�Z ¨[ñ6
ô 6
Ñ2ˆ�[ )¨Uð ð 	.�K 1Ò$Ð$ð =Ð<Ð<Ð<Ð<Ð<Ø!Ð! '¨:°zÑBÔBð 	Ý"Ø Ø"ð@Ø<@ð@ð @ð @ð	ñ ô ð ð ÐOÐOÈÐOÐOˆGðØBÐBÐBÐBÐBÐBØÐ/Ð/°¸ÀjÐRYÑZÔZÑZ��øÝð ð ð Ø�ðøøøå WÐ-Ñ-Ô-Ð-õ *¨'Ñ2Ô2ˆØð 	LÝ1°+¸{ÑKÔKˆKð
 —’ t¨[Ø3>ð 'ñ @ô @ˆàÔð 	WÝÐ%UÀÔASÐ%UÐ%UÐVÑVÔVÐVð G˜D×2Ò2°4Ñ8Ô8ÐFÐFÐFˆ
ØŸ
š
 :Ñ.Ô.ˆØÔ" aÒ'Ð'ÝÐ%`ÐZ^Ð%`Ð%`ÐaÑaÔaÐaõ (¨Ô(<Ñ=Ô=Ñˆ˜Ø$3×$;Ò$;¸FÀDÑ$IÔ$I×$QÒ$QÐRVÐX\Ñ$]Ô$]Ð!Ø"-×"5Ò"5°f¸dÑ"CÔ"C×"KÒ"KÈDÐRVÑ"WÔ"WÐØ$Ð(?Ò?Ð?ÝðM°dð Mð MåÐ5Ñ6Ô6ðMð Mõ Ð0Ñ1Ô1ðMð Mð Mðñ ô ð ð ×!Ò! '¨;¸Ñ=Ô=ˆð
 ×,Ò,¨T¸wÐU`Ð,ÑaÔaˆåØØØ ˜6Ø*5Ð?�×$Ò$Ñ&Ô&Ð&¸4ð )Ô8ð
ñ 
ô 
ð 	
s   Ã0D Ä
DÄD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_parserrW  rX  ru   )r_   rÛ   rW  rX  Ú
operationsÚparse_errorr   s          r&   rÝ   zShellFileOperations.patch_v4aO  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          v r*|                      |¦  «        rt          dd|› d�¬	¦  «        S t          |         }	|	                     ¦   «         d         }
|                      |
¦  «        st          d|
› d�¬	¦  «        S |	                     d|                      |¦  «        ¦  «        }|                      |d¬¦  «        }|j        dk    r�t#          |
|j
        ¦  «        rzddlm}  ||j
        ¦  «                             ¦   «         }t+          d„ |                     ¦   «         D ¦   «         |dd…         ¦  «        }t          d|
› d|dd…         › �¬	¦  «        S 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  r±  r   TzFailed to read z	 for lint)r    r¢   r  zNo linter available for z (missing dependency)r   ©rv   r¡   zNo linter for z fileszLSP server handles u    â€” shell linter skippedz not availablez{file}r  ©r\  )Ú
strip_ansic              3   óf   K  — | ],}|                      ¦   «         ¯|                      ¦   «         V — Œ-d S rU   ©r   ©rW   Úlns     r&   r  z2ShellFileOperations._check_lint.<locals>.<genexpr>¸  s7   è è € ÐIÐI ¸b¿hºh¹j¼jÐI�—’‘”ÐIÐIÐIÐIÐIÐIr(   éx   z not usable: éÈ   )r…  r>   r†  r  r0  r  ro  rd  r©   rŸ   r¨   ÚLINTERSÚ_SHELL_LINTER_LSP_REDUNDANTÚ_lsp_will_handler±   rh  r   r
  Útools.ansi_stripr_  r   Únextr   )r_   r>   rE   rˆ  Úinprocrß  rà  r¥   ÚerrÚ
linter_cmdr   re  r   r_  r%   Ú
first_lines                   r&   Ú_check_lintzShellFileOperations._check_lintm  s  € õ( Œg×Ò˜tÑ$Ô$ QÔ'×-Ò-Ñ/Ô/ˆõ  ×#Ò# CÑ(Ô(ˆØÐàˆØL $×"8Ò"8¸Ñ">Ô">ÐLÐLÐL�Ø"Ÿjšj¨Ñ2Ô2�ØÔ(¨AÒ-Ð-Ý%¨dÐ<]ÈdÐ<]Ð<]Ð<]Ð^Ñ^Ô^Ð^Ø%Ô,�Ø�f˜W‘o”o‰GˆB�Ø�jÒ Ð Ý!¨$Ð8mÐSVÐ8mÐ8mÐ8mÐnÑnÔnÐnÝ b°rÐ1B°°¸sÐCÑCÔCÐCð •gÐÐÝ dÐ4PÀSÐ4PÐ4PÐ4PÐQÑQÔQÐQð Õ-Ð-Ð-°$×2GÒ2GÈÑ2MÔ2MÐ-ÝØØL¨cÐLÐLÐLðñ ô ð õ
 ˜S”\ˆ
à×#Ò#Ñ%Ô% aÔ(ˆà× Ò  Ñ*Ô*ð 	QÝ d°xÐ4OÐ4OÐ4OÐPÑPÔPÐPð × Ò  ¨4×+AÒ+AÀ$Ñ+GÔ+GÑHÔHˆØ—’˜C¨�Ñ,Ô,ˆàÔ˜qÒ Ð Õ%@ÀÈ6Ì=Ñ%YÔ%YÐ ð 4Ð3Ð3Ð3Ð3Ð3Ø �j ¤Ñ/Ô/×5Ò5Ñ7Ô7ˆGåØIÐI g×&8Ò&8Ñ&:Ô&:ÐIÑIÔIØ˜˜˜”ñô ˆJõ ØØ#ÐDÐD°*¸T¸c¸TÔ2BÐDÐDðñ ô ð õ
 ØÔ$¨Ò)Ø,2¬M×,?Ò,?Ñ,AÔ,AÐI�6”=×&Ò&Ñ(Ô(Ð(À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 syntax lint with pre-write baseline comparison.

        Two-tier strategy:

        1. **Syntax check** (in-process or shell-based, microseconds).
           Catches the bug class that motivated this layer: corrupt
           writes, mashed quotes, truncated output.  Hot path.

        2. **Delta refinement against pre-write content** when the
           syntax tier reports errors.  Filter out errors that already
           existed pre-edit so the agent isn't distracted by inherited
           state.

        Semantic diagnostics from the LSP layer are fetched separately
        via :meth:`_maybe_lsp_diagnostics` and surfaced in the
        ``lsp_diagnostics`` field on :class:`WriteResult` /
        :class:`PatchResult`.  Keeping the two channels separate lets
        the agent (and any downstream parsers) read syntax errors and
        semantic errors as independent signals.

        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).
        )rE   Nc                 ó^   — h | ]*}|                      ¦   «         ¯|                      ¦   «         ’Œ+S r-   ra  rb  s     r&   ú	<setcomp>z8ShellFileOperations._check_lint_delta.<locals>.<setcomp>	  s-   € ÐPÐPÐP BÀRÇXÂXÁZÄZÐP�R—X’X‘Z”ZÐPÐPÐPr(   c                 óh   •— g | ].}|                      ¦   «         ¯|                      ¦   «         ‰v¯,|‘Œ/S r-   ra  )rW   rc  Ú	pre_liness     €r&   r›   z9ShellFileOperations._check_lint_delta.<locals>.<listcomp>	  s9   ø€ ÐjÐjÐj˜R¸r¿xºx¹z¼zÐjÈbÏhÊhÉjÌjÐ`iÐNiÐNi�bÐNiÐNiÐNir(   Fu^   Pre-existing lint errors â€” this edit didn't introduce new ones but the file is still broken.)rv   r¡   r¢   zLNew lint errors introduced by this edit (pre-existing errors filtered out):
r,   r]  )ro  rv   r    r¡   r   rŸ   r!   )r_   r>   rÒ   r.  ÚpostÚpreÚ
post_linesrt  s          @r&   r8  z%ShellFileOperations._check_lint_deltaÅ  s-  ø€ ðL ×Ò ¨lÐÑ;Ô;ˆð Œ<ð 	˜4œ<ð 	ØˆKð ÐØˆKà×Ò˜t¨[ÐÑ9Ô9ˆØŒ;ð 	˜#œ+ð 	¨S¬Zð 	ð ˆKð QÐP¨#¬*×*?Ò*?Ñ*AÔ*AÐPÑPÔPˆ	ØjÐjÐjÐj 4¤;×#9Ò#9Ñ#;Ô#;ÐjÑjÔjˆ
àð 	õ ØØ”{Øxðñ ô ð õ Øð8Ø:>¿)º)ÀJÑ:OÔ:OñPð
ñ 
ô 
ð 	
r(   c                 ó~   — t          | dd¦  «        }|€dS 	 ddlm} n# t          $ r Y dS w xY wt	          ||¦  «        S )u}  Return True iff this FileOperations is wired to a local backend.

        LSP servers run on the host process â€” they need access to the
        files they're linting.  Remote/sandboxed backends (Docker,
        Modal, SSH, Daytona) keep files inside the sandbox where the
        host-side LSP server can't reach them, so we skip the LSP
        path for those entirely.
        rV  NFr   )ÚLocalEnvironment)rW  r§  ry  r  Ú
isinstance)r_   rV  ry  s      r&   Ú_lsp_local_onlyz#ShellFileOperations._lsp_local_only	  sn   € õ �d˜E 4Ñ(Ô(ˆØˆ;ð �5ð	ØAÐAÐAÐAÐAÐAÐAøÝð 	ð 	ð 	Ø�5�5ð	øøøå˜#Ð/Ñ0Ô0Ð0s   — ž
,«,rˆ  c                 óŠ   — |sdS 	 ddl m} n# t          $ r Y dS w xY w|                     ¦   «         }|D ]}||j        v r dS ŒdS )u¹  Return True iff some registered LSP server claims this extension.

        Used to decide whether to capture pre-write content for the
        line-shift map.  Capturing is cheap (one ``cat`` on the host)
        but pointless if no LSP would ever look at the file.

        Safe to call on remote backends â€” the registry is purely
        in-process metadata; we still gate the actual LSP path on
        :meth:`_lsp_local_only`.
        Fr   )ÚSERVERST)Úagent.lsp.serversr}  r  r  Ú
extensions)r_   rˆ  r}  Ú	ext_lowerÚsrvs        r&   r1  z*ShellFileOperations._lsp_handles_extension/	  s‡   € ð ð 	Ø�5ð	Ø1Ð1Ð1Ð1Ð1Ð1Ð1øÝð 	ð 	ð 	Ø�5�5ð	øøøà—I’I‘K”Kˆ	Øð 	ð 	ˆCØ˜CœNÐ*Ð*Ø�t�tð +àˆus   † �
šc                 ó
  — |                       ¦   «         sdS 	 ddlm} n# t          $ r Y dS w xY w	  |¦   «         }n# t          $ r Y dS w xY w|€dS 	 t	          |                     |¦  «        ¦  «        S # t          $ r Y dS w xY w)u!  Return True iff the LSP service is active AND will lint this file.

        Stronger than :meth:`_lsp_handles_extension` â€” that one only checks
        the static server registry.  This one additionally requires the
        LSP service to be configured/enabled and the file to pass
        :meth:`agent.lsp.manager.LSPService.enabled_for` (which gates on
        workspace detection, disabled-server set, and the broken-pair
        short-circuit).

        Used by :meth:`_check_lint` to decide whether to skip the per-file
        shell linter for extensions in ``_SHELL_LINTER_LSP_REDUNDANT``.

        Best-effort: any failure path returns False so the shell linter
        runs as before â€” never suppress lint based on an LSP probe that
        couldn't actually answer the question.
        Fr   ©Úget_service)r{  Ú	agent.lspr„  r  r<   Úenabled_for©r_   r>   r„  Úsvcs       r&   rh  z$ShellFileOperations._lsp_will_handleF	  sØ   € ð" ×#Ò#Ñ%Ô%ð 	Ø�5ð	Ø-Ð-Ð-Ð-Ð-Ð-Ð-øÝð 	ð 	ð 	Ø�5�5ð	øøøð	Ø�+‘-”-ˆCˆCøÝð 	ð 	ð 	Ø�5�5ð	øøøàˆ;Ø�5ð	Ý˜Ÿš¨Ñ-Ô-Ñ.Ô.Ð.øÝð 	ð 	ð 	Ø�5�5ð	øøøs-   ˜ Ÿ
-¬-±
< ¼
A
Á	A
Á!A4 Á4
BÂBc                 óÎ   — |                       ¦   «         sdS 	 ddlm}  |¦   «         }n# t          $ r Y dS w xY w|€dS 	 |                     |¦  «         dS # t          $ r Y dS w xY w)uL  Capture pre-edit LSP diagnostics so the post-write delta is correct.

        Best-effort.  Silent on every failure path â€” LSP is an
        enrichment layer and must never break a write.

        Skipped entirely on non-local backends (Docker, Modal, SSH,
        etc.) â€” the server can't see files inside the sandbox.
        Nr   rƒ  )r{  r…  r„  r  Úsnapshot_baseliner‡  s       r&   r2  z*ShellFileOperations._snapshot_lsp_baselineh	  s®   € ð ×#Ò#Ñ%Ô%ð 	ØˆFð	Ø-Ð-Ð-Ð-Ð-Ð-Ø�+‘-”-ˆCˆCøÝð 	ð 	ð 	ØˆFˆFð	øøøàˆ;ØˆFð	Ø×!Ò! $Ñ'Ô'Ð'Ð'Ð'øÝð 	ð 	ð 	ØˆDˆDð	øøøs   ˜) ©
7¶7¿A Á
A$Á#A$r-  c                óþ  — |                       ¦   «         sdS 	 ddlm} n# t          $ r Y dS w xY w	  |¦   «         }n# t          $ r Y dS w xY w|�|                     |¦  «        sdS d}|�.|�,||k    r&	 ddlm}  |||¦  «        }n# t          $ r d}Y nw xY w	 |                     |d|¬¦  «        }n# t          $ r Y dS w xY w|sdS 	 ddlm	}	m
}
  |	||¦  «        }|sdS  |
d	|z   ¦  «        S # t          $ r Y dS w xY w)
uÛ  Best-effort LSP semantic diagnostics for ``path``.

        Returns a formatted ``<diagnostics>`` block, or empty string
        when LSP is unavailable / disabled / produced no errors.

        When both ``pre_content`` and ``post_content`` are provided,
        a line-shift map is built and passed to the LSPService so
        baseline diagnostics are remapped into post-edit coordinates
        before the set-difference.  Without this, edits that delete
        or insert lines surface every pre-existing diagnostic below
        the edit point as "introduced by this edit".

        Wraps everything in a try/except so a misbehaving LSP server
        can't break a write.  This intentionally swallows all errors
        â€” the calling tier already returned a clean syntax result, so
        ``""`` here just means "no extra info to add".

        Skipped entirely on non-local backends (Docker, Modal, SSH,
        etc.) â€” same reasoning as ``_snapshot_lsp_baseline``.
        r   r   rƒ  N)Úbuild_line_shiftT)ÚdeltaÚ
line_shift)Úreport_for_fileÚtruncatez)LSP diagnostics introduced by this edit:
)r{  r…  r„  r  r†  Úagent.lsp.range_shiftrŒ  Úget_diagnostics_syncÚagent.lsp.reporterr�  r�  )r_   r>   rÒ   r.  r„  rˆ  rŽ  rŒ  rµ   r�  r�  rG  s               r&   r9  z*ShellFileOperations._maybe_lsp_diagnostics	  s×  € ð6 ×#Ò#Ñ%Ô%ð 	Ø�2ð	Ø-Ð-Ð-Ð-Ð-Ð-Ð-øÝð 	ð 	ð 	Ø�2�2ð	øøøð	Ø�+‘-”-ˆCˆCøÝð 	ð 	ð 	Ø�2�2ð	øøøàˆ;˜cŸošo¨dÑ3Ô3ˆ;Ø�2ð
 ˆ
ØÐ" |Ð'?ÀKÐS_ÒD_ÐD_ð"ØBÐBÐBÐBÐBÐBØ-Ð-¨k¸<ÑHÔH�
�
øÝð "ð "ð "Ø!�
�
�
ð"øøøð	Ø×2Ò2°4¸tÐPZÐ2Ñ[Ô[ˆKˆKøÝð 	ð 	ð 	Ø�2�2ð	øøøàð 	Ø�2ð	ØDÐDÐDÐDÐDÐDÐDÐDØ#�O D¨+Ñ6Ô6ˆEØð Ø�rØ�8ÐHÈ5ÑPÑQÔQÐQøÝð 	ð 	ð 	Ø�2�2ð	øøøsW   ˜ Ÿ
-¬-±
< ¼
A
Á	A
Á3B ÂBÂBÂB2 Â2
C Â?C ÃC. Ã C. Ã.
C<Ã;C<rè   ré   r   rê   r0   rë   rì   rí   c	           
      ó*  — t          ||¦  «        \  }}|                      |¦  «        }|                      d|                      |¦  «        › d�¦  «        }	d|	j        v �rý|                      ||||||||¦  «        }
|
�|
S t          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
        útest -e ú! && echo exists || echo not_foundÚ	not_foundNrè   zPath not found: ztest -d z && echo yes || echo norg  rò  z 2>/dev/null | head -20r   r,   rz  zSimilar paths: ú, rŒ   z. ©rO   rŠ   rˆ   )rG  r¢  rd  ro  r¨   Ú_try_multi_path_searchr…  r>   r©  rõ  r©   r   r  r±   r6   r    r!   r†   Ú_search_filesÚ_search_content)r_   rê   r>   r0   rë   rÆ   rÅ   rì   rí   ÚcheckÚmultir«  Úbasename_queryÚ
hint_partsÚparent_checkrø  Úlower_qrù  rô  Úles                       r&   rï   zShellFileOperations.searchÅ	  sÑ  € õ& 4°F¸EÑBÔB‰ˆ�ð × Ò  Ñ&Ô&ˆð —
’
Ðe d×&<Ò&<¸TÑ&BÔ&BÐeÐeÐeÑfÔfˆØ˜%œ,Ð&Ñ&ð
 ×/Ò/Ø˜˜v y°%¸ÀÈgñô ˆEð Ð Ø�å”W—_’_ TÑ*Ô*Ð1¨cˆFÝœW×-Ò-¨dÑ3Ô3ˆNØ3¨TÐ3Ð3Ð4ˆJàŸ:š:ØR˜4×1Ò1°&Ñ9Ô9ÐRÐRÐRñô ˆLð ˜Ô+Ð+Ñ+°Ñ+Ø ŸJšJØT˜T×3Ò3°FÑ;Ô;ÐTÐTÐTñô �	ð Ô&¨!Ò+Ñ+°	Ô0@×0FÒ0FÑ0HÔ0HÐ+Ø,×2Ò2Ñ4Ô4�GØ!#�JØ!*Ô!1×!7Ò!7Ñ!9Ô!9×!?Ò!?ÀÑ!EÔ!Eð Kð K˜Ø$ð %Ø$Ø"Ÿ[š[™]œ]˜Ø" b˜=˜=¨B°'¨M¨M¸R¿]º]È7ÐSUÐTUÐSUÌ;Ñ=WÔ=W¨MØ&×-Ò-­b¬g¯lªl¸6À5Ñ.IÔ.IÑJÔJÐJøØ!ð Ø"×)Ò)Ø-°·	²	¸*ÀRÀaÀR¼.Ñ0IÔ0IÑIñô ð õ  Ø—i’i 
Ñ+Ô+Øðñ ô ð ð
 �WÒÐØ×%Ò% g¨t°U¸FÑCÔCÐCà×'Ò'¨°°yÀ%ÈØ(3°Wñ>ô >ð >r(   c	           
      óD  — d„ |                      d¦  «        D ¦   «         }	t          |	¦  «        dk     rdS g g }}
|	D ]c}|                      |¦  «        }|                      d|                      |¦  «        › d�¦  «        }d|j        v r|
n|                     |¦  «         Œd|
sdS t          ¦   «         }|
D ]É}|dk    r|                      ||||¦  «        }n|  	                    |||||||¦  «        }|j
        rŒD|j                             |j        ¦  «         |j                             |j        ¦  «         |j                             |j        ¦  «         |xj        |j        z  c_        |j        p|j        |_        ŒÊ|j        d|…         |_        |j        d|…         |_        d	t          |	¦  «        › d
t          |
¦  «        › d�}|rO|dd                     |dd…         ¦  «        z   z  }t          |¦  «        dk    r|dt          |¦  «        dz
  › d�z  }||_        |S )aÇ  Recover a not-found ``path`` that is really several paths in one string.

        Production trajectories show models passing "dir1 dir2 dir3" (or
        comma-separated lists) as ``path``. Split on whitespace/commas; when
        at least one candidate exists and at least two candidates were given,
        search every existing path, merge results, and note skipped parts.
        Returns None when this doesn't look like a multi-path string.
        c                 óh   — g | ]/}|                      ¦   «         D ]}|                     ¦   «         ¯|‘ŒŒ0S r-   )r±   r   )rW   Úchunkr  s      r&   r›   z>ShellFileOperations._try_multi_path_search.<locals>.<listcomp>
  s:   € ÐTÐTÐT�u¸¿º¹¼ÐTÐT°AÈ!Ï'Ê'É)Ì)ÐT�ÐTÐTÐTÐTr(   rÌ  r   Nr•  r–  Úexistsrˆ   zpath contained z entries; searched z that existz; skipped missing: r˜  rz  z (+z more))r±   r8   r¢  rd  ro  r¨   r    r†   r›  rœ  rO   r‡   Úextendrˆ   r‰   ÚupdaterŠ   rH   r!   rq   )r_   rê   r>   r0   rë   rÆ   rÅ   rì   rí   ÚpartsÚexistingÚmissingr  ÚexpandedÚchkÚmergedr   r|   s                     r&   rš  z*ShellFileOperations._try_multi_path_search
  s`  € ð UÐT §
¢
¨3¡¤ÐTÑTÔTˆÝˆu‰:Œ:˜Š>ˆ>Ø�4Ø �'ˆØð 	Oð 	OˆAØ×(Ò(¨Ñ+Ô+ˆHØ—*’*Ø^˜4×1Ò1°(Ñ;Ô;Ð^Ð^Ð^ñô ˆCð " S¤ZÐ/Ð/ˆXˆX°W×DÒDÀXÑNÔNÐNÐNØð 	Ø�4å‘”ˆØð 	Að 	AˆAØ˜Ò Ð Ø×(Ò(¨°!°U¸FÑCÔC��à×*Ò*¨7°A°yÀ%ÈØ+6¸ñAô A�àŒyð ØØŒN×!Ò! #¤+Ñ.Ô.Ð.ØŒL×Ò ¤	Ñ*Ô*Ð*ØŒM× Ò  ¤Ñ,Ô,Ð,ØÐÔ #¤/Ñ1ÐÔØ%Ô/Ð@°3´=ˆFÔÐàœ¨¨¨Ô/ˆŒØ”| F U FÔ+ˆŒØZ¥ U¡¤ÐZÐZÅÀHÁÄÐZÐZÐZˆØð 	7ØÐ)¨D¯IªI°g¸b¸q¸b´kÑ,BÔ,BÑBÑBˆDÝ�7‰|Œ|˜aÒÐØÐ6�c '™lœl¨QÑ.Ð6Ð6Ð6Ñ6�ØˆŒØˆr(   c           
      óü  — |                       d¦  «        sdS |rd|                      |¦  «        › �nd}|                      d|› d|                      |¦  «        › d|                      |¦  «        › d�d¬	¦  «        }d
}d
}|j        pd                     ¦   «                              ¦   «         D ]F}|                     d¦  «        \  }	}
}|                     ¦   «         r|t          |¦  «        z  }|dz  }ŒG|d
k    r	d|› d|› d�S |                      d|› d|                      |¦  «        › d|                      |¦  «        › d�d¬	¦  «        }d
}d
}|j        pd                     ¦   «                              ¦   «         D ]F}|                     d¦  «        \  }	}
}|                     ¦   «         r|t          |¦  «        z  }|dz  }ŒG|d
k    r	d|› d|› d�S t          j
        d|¦  «        r—|                      d|› d|                      |¦  «        › d|                      |¦  «        › d�d¬	¦  «        }t          d„ |j        pd                     ¦   «                              ¦   «         D ¦   «         ¦  «        }|d
k    rd|› d�S dS )aŠ  Return a hint for a 0-match content search, or None.

        13.9% of production content searches return zero matches and give
        the model nothing to steer by. Run ONE cheap case-insensitive count
        probe; if it hits, say so. If the pattern contains regex
        metacharacters, also probe it as a fixed string. Bounded: two rg
        invocations max, count-only output.
        ÚrgNz --glob r   zrg -i --count-matchesrl  rû  r  r^  r   ú:rº   z0 exact matches, but z case-insensitive match(es) in u/    file(s) â€” the pattern's casing may be wrong.z'rg --hidden --no-ignore --count-matchesz 0 matches in visible files, but z match(es) in us    hidden or gitignored file(s) â€” these are excluded by default. Search the hidden path explicitly to include them.z[.\[\](){}?*+^$\\|]zrg -F --count-matchesc              3   óÀ   K  — | ]Y}|                      d ¦  «        d                              ¦   «         ¯/t          |                      d ¦  «        d         ¦  «        V — ŒZdS )r²  r   N)Ú
rpartitionÚisdigitrg   )rW   r#   s     r&   r  z8ShellFileOperations._zero_match_probe.<locals>.<genexpr>{
  sr   è è € ð ð àØ—?’? 3Ñ'Ô'¨Ô*×2Ò2Ñ4Ô4ðÝ�D—O’O CÑ(Ô(¨Ô+Ñ,Ô,ðð ð ð ð ð r(   z0 regex matches, but uy    literal match(es) â€” the pattern contains regex metacharacters that likely need escaping (or pass a simpler substring).)rh  ro  rd  r¨   r   r   r´  rµ  rg   r»   rï   r‡  )r_   rê   r>   rë   Ú	glob_exprÚprobeÚci_totalÚci_filesr#   Ú_pÚ_sepÚnÚhiddenÚh_totalÚh_filesÚfixedÚf_totals                    r&   Ú_zero_match_probez%ShellFileOperations._zero_match_probe>
  sw  € ð × Ò  Ñ&Ô&ð 	Ø�4ØFOÐWÐB˜t×5Ò5°iÑ@Ô@ÐBÐBÐBÐUWˆ	Ø—
’
ð& Ið &ð &Ø×%Ò% gÑ.Ô.ð&ð &Ø15×1GÒ1GÈÑ1MÔ1Mð&ð &ð &ð ð	 ñ 
ô 
ˆð ˆØˆØ”\Ð' R×.Ò.Ñ0Ô0×;Ò;Ñ=Ô=ð 	ð 	ˆDØŸ/š/¨#Ñ.Ô.‰KˆB��aØ�yŠy‰{Œ{ð Ø�C ™FœFÑ"�Ø˜A‘�øØ�aŠ<ˆ<ðP¨ð Pð PØðPð Pð Pðð —’ð&°ið &ð &Ø×%Ò% gÑ.Ô.ð&ð &Ø15×1GÒ1GÈÑ1MÔ1Mð&ð &ð &ð ð	 ñ 
ô 
ˆð ˆØˆØ”]Ð( b×/Ò/Ñ1Ô1×<Ò<Ñ>Ô>ð 	ð 	ˆDØŸ/š/¨#Ñ.Ô.‰KˆB��aØ�yŠy‰{Œ{ð Ø�3˜q™6œ6Ñ!�Ø˜1‘�øØ�QŠ;ˆ;ðQ°7ð Qð QØðQð Qð Qðõ
 Œ9Ð+¨WÑ5Ô5ð 	Ø—J’Jð*¨	ð *ð *Ø×)Ò)¨'Ñ2Ô2ð*ð *Ø59×5KÒ5KÈDÑ5QÔ5Qð*ð *ð *ð ð	 ñ ô ˆEõ ð ð à"œ\Ð/¨R×6Ò6Ñ8Ô8×CÒCÑEÔEðñ ô ñ ô ˆGð
 ˜Š{ˆ{ð>¨Gð >ð >ð >ðð
 ˆtr(   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¬¦  «        }t          |¦  «        \  }}|                     ¦   «         s^|s\d|                      |¦  «        › |	› d|                      |¦  «        › d|
› �}|  	                    |d¬¦  «        }t          |¦  «        \  }}g }|                     ¦   «                              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          |¦  «        t'          |¦  «        |¬¦  «        S )z-Search for files by name pattern (glob-like).z**/rU  rÐ  c              3   óH   K  — | ]}|d vo|                      d¦  «        V — ŒdS ©>   ú..rè   rè   N©r6   ©rW   Úparts     r&   r  z4ShellFileOperations._search_files.<locals>.<genexpr>‘
  sL   è è € ð '
ð '
àð ˜Ð#Ð<¨¯ª¸Ñ(<Ô(<ð'
ð '
ð '
ð '
ð '
ð '
r(   r±  rš  zƒFile search requires 'rg' (ripgrep) or 'find'. Install ripgrep for best results: https://github.com/BurntSushi/ripgrep#installationrÏ   z-not -path '*/.*'r   rl  z | tail -n +rº   z | head -n zfind z -type f -name z* -printf '%T@ %p\n' 2>/dev/null | sort -rnrÿ  r^  z 2>/dev/null | sort -rnr,   r   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   è è € Ð^Ð^ÈD�t ;Ð.ÐG°4·?²?À3Ñ3GÔ3GÐ^Ð^Ð^Ð^Ð^Ð^r(   ©rˆ   rŠ   rH   r‹   )r6   r±   r   r  rª  rh  Ú_search_files_rgr†   ro  rd  r®   r   r8   r   rµ  r    ÚresolveÚrelative_tor7  r<   )r_   rê   r>   rÆ   rÅ   Úsearch_patternÚsearch_rootÚhas_hidden_path_ancestorÚhidden_excludeÚhidden_filter_exprÚpagination_exprre  r   r¨   r‹   Ú
cmd_simplerˆ   r#   rª  Únormalized_rootÚfiltered_filesÚ	file_pathÚ	rel_partss                          r&   r›  z!ShellFileOperations._search_filesˆ
  s  € ð ×!Ò! %Ñ(Ô(ð 	4¨S¸Ð-?Ð-?Ø$ˆNˆNà$Ÿ]š]¨3Ñ/Ô/°Ô3ˆNå˜4‘j”jˆÝ#&ð '
ð '
à#Ô)ð'
ñ '
ô '
ñ $
ô $
Ð ð ×Ò˜TÑ"Ô"ð 	NØ×(Ò(¨¸¸uÀfÑMÔMÐMð × Ò  Ñ(Ô(ð 	ÝðKðñ ô ð ð 5MÐTÐ,Ð,ÐRTˆØ5CÐKÐ1 Ð1Ð1Ð1ÈÐð
 ˆØ'ð 	LØK¨V°a©ZÐKÐKÀEÐKÐKˆOðM�d×,Ò,¨TÑ2Ô2ð MÐ4Fð Mð MÐW[×WmÒWmÐn|ÑW}ÔW}ð Mð MØ;JðMð Mˆð —’˜C¨�Ñ,Ô,ˆÝ7¸Ñ?Ô?Ñˆ�à�|Š|‰~Œ~ð 	D lð 	DðC ×!7Ò!7¸Ñ!=Ô!=ð CÐ?Qð Cð CÐbf×bxÒbxð  zHñ  cIô  cIð Cð CØ1@ðCð CˆJà—Z’Z 
°B�ZÑ7Ô7ˆFÝ#;¸FÑ#CÔ#CÑ ˆF�LàˆØ—L’L‘N”N×(Ò(¨Ñ.Ô.ð 	#ð 	#ˆDØð ØØ—J’J˜s AÑ&Ô&ˆEÝ�5‰zŒz˜QŠˆ 5¨¤8×#3Ò#3°C¸Ñ#<Ô#<×#DÒ#DÑ#FÔ#FˆØ—’˜U 1œXÑ&Ô&Ð&Ð&à—’˜TÑ"Ô"Ð"Ð"ð
 $ð 	:Ø)×1Ò1Ñ3Ô3ˆOØˆNØ"ð 1ð 1�	ð6Ý $ Y¡¤× 7Ò 7Ñ 9Ô 9× EÒ EÀoÑ VÔ VÔ \�I�IøÝ!ð 6ð 6ð 6Ý $ Y¡¤Ô 5�I�I�Ið6øøøåÐ^Ð^ÐT]Ð^Ñ^Ô^Ñ^Ô^ð ØØ×%Ò% iÑ0Ô0Ð0Ð0Ø" 6¨&°5©.Ð#8Ô9ˆEõ ØÝ˜E™
œ
Ý˜<Ñ(Ô(Ø%ð	
ñ 
ô 
ð 	
s   É79J1Ê1KËKc                 ó  — d|vr|                      d¦  «        sd|› �}n|}||z   }d|                      |¦  «        › d|                      |¦  «        › d|› �}|                      |d¬¦  «        }t          |¦  «        \  }	}
d„ |	                     ¦   «                              d	¦  «        D ¦   «         }|s�|
s‹d
|                      |¦  «        › d|                      |¦  «        › d|› �}|                      |d¬¦  «        }t          |¦  «        \  }	}
d„ |	                     ¦   «                              d	¦  «        D ¦   «         }||||z   …         }t          |t          |¦  «        t          |¦  «        |k    pt          |
¦  «        |
¬¦  «        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.
        rU  Ú*zrg --files --sortr=modified -g rl  z 2>/dev/null | head -n rÿ  r^  c                 ó   — g | ]}|¯|‘ŒS r-   r-   ©rW   r  s     r&   r›   z8ShellFileOperations._search_files_rg.<locals>.<listcomp>ö
  s   € Ð@Ð@Ð@˜1¸aÐ@�QÐ@Ð@Ð@r(   r,   zrg --files -g c                 ó   — g | ]}|¯|‘ŒS r-   r-   rÝ  s     r&   r›   z8ShellFileOperations._search_files_rg.<locals>.<listcomp>  ó   € ÐDÐDÐD˜qÀ!ÐD˜ÐDÐDÐDr(   rË  )	r6   ro  rd  r®   r   r±   r†   r8   r<   )r_   rê   r>   rÆ   rÅ   Úglob_patternÚfetch_limitÚ
cmd_sortedr   r¨   r‹   Ú	all_filesÚ	cmd_plainÚpages                 r&   rÌ  z$ShellFileOperations._search_files_rgÞ
  sÞ  € ð �gÐÐ g×&8Ò&8¸Ñ&=Ô&=ÐØ(˜w˜=˜=ˆLˆLà"ˆLà˜f‘nˆð'¨d×.DÒ.DÀ\Ñ.RÔ.Rð 'ð 'Ø×%Ò% dÑ+Ô+ð'ð 'à$ð'ð 'ð 	ð
 —’˜J°�Ñ3Ô3ˆÝ7¸Ñ?Ô?Ñˆ�Ø@Ð@ §¢¡¤× 4Ò 4°TÑ :Ô :Ð@Ñ@Ô@ˆ	àð 		E ð 		Eð+ ×!7Ò!7¸Ñ!EÔ!Eð +ð +Ø×)Ò)¨$Ñ/Ô/ð+ð +à(ð+ð +ð ð
 —Z’Z 	°2�ZÑ6Ô6ˆFÝ#;¸FÑ#CÔ#CÑ ˆF�LØDÐD F§L¢L¡N¤N×$8Ò$8¸Ñ$>Ô$>ÐDÑDÔDˆIà˜ ¨¡Ð.Ô/ˆåØÝ˜I™œÝ˜)‘n”n¨Ò3ÐIµt¸LÑ7IÔ7IØ%ð	
ñ 
ô 
ð 	
r(   c           	      óò  — d}|                       d¦  «        rd}|                      |||||||¦  «        }	nA|                       d¦  «        r|                      |||||||¦  «        }	nt          d¬¦  «        S |	j        sg|	j        dk    r\|	j        sU|	j        sN|	j        sG	 |  	                    |||¦  «        }
n# t          $ r d}
Y nw xY w|
r|	j        s|
n|	j        › d	|
› �|	_        |r|	S t          |	|¦  «        S )
z,Search for content inside files (grep-like).Fr±  TÚgrepzqContent search requires ripgrep (rg) or grep. Install ripgrep: https://github.com/BurntSushi/ripgrep#installationrÏ   r   Nrl  )rh  Ú_search_with_rgÚ_search_with_grepr†   rO   rŠ   r‡   rˆ   r‰   rÂ  r  rq   rO  )r_   rê   r>   rë   rÆ   rÅ   rì   rí   Úused_rgr   rI   s              r&   rœ  z#ShellFileOperations._search_content  sn  € ð ˆØ×Ò˜TÑ"Ô"ð 	ØˆGØ×)Ò)¨'°4¸ÀEÈ6Ø*5°wñ@ô @ˆFˆFà×Ò˜vÑ&Ô&ð 	Ø×+Ò+¨G°T¸9ÀeÈVØ,7¸ñBô BˆFˆFõ  ð\ðñ ô ð ð ”ð 	\ Ô!3°qÒ!8Ð!8Øœð "9Ø/5¬|ð "9ØDJÄMð "9ðØ×-Ò-¨g°t¸YÑGÔG��øÝð ð ð Ø���ðøøøàð \Ø-3¬^Ð![  ÀFÄNÐA[ÐA[ÐUYÐA[ÐA[�”ð ð 	ØˆMÝ8¸ÀÑIÔIÐIs   ÂB7 Â7CÃCc                 ó”	  — g d¢}t          |¦  «        }	|	r|                     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                     |¦  «        z   }|                      |d¬¦  «        }t          |¦  «        \  }}t          |¦  «        \  }}|j	        dk    rW| 
                    ¦   «         sC| 
                    ¦   «         p|j         
                    ¦   «         pd}t          d|› �d¬¦  «        S |}|	rdnd}|dk    rnd„ | 
                    ¦   «                              d¦  «        D ¦   «         }t          |¦  «        }||||z   …         }t          ||t          |¦  «        ||¬¦  «        S |dk    rÈi }| 
                    ¦   «                              d¦  «        D ]_}d|v rY|                     dd¦  «        }t          |¦  «        dk    r0	 t#          |d         ¦  «        ||d         <   ŒO# t$          $ r Y Œ[w xY wŒ`t          |t'          |                     ¦   «         ¦  «        t          |¦  «        |¬¦  «        S t+          j        d¦  «        }g }| 
                    ¦   «                              d¦  «        D �]}|r|dk    rŒ|                     |¦  «        }|rŒ|                     t1          |                     d¦  «        pd |                     d¦  «        z   t#          |                     d!¦  «        ¦  «        |                     d"¦  «        dd#…         ¬$¦  «        ¦  «         Œ¯|dk    rPt5          |¦  «        }|r?|                     t1          |d         |d         |d         dd#…         ¬$¦  «        ¦  «         �Œt          |¦  «        }||||z   …         }t          |||||z   k    pt          |¦  «        ||¬%¦  «        S )&zSearch using ripgrep.)r±  z--line-numberz--no-headingz--with-filenamerL  r   ú-Cz--globÚ
files_onlyú-lÚcountú-cre  r’  r.   ú-núset -o pipefail; rl  rÿ  r^  r   úSearch errorúSearch failed: r™  ut   Pattern contains \n â€” multiline mode (-U) was enabled automatically so the regex can match across line boundaries.Nc                 ó   — g | ]}|¯|‘ŒS r-   r-   rÝ  s     r&   r›   z7ShellFileOperations._search_with_rg.<locals>.<listcomp>w  rß  r(   r,   )rˆ   rŠ   rH   r‹   rq   r²  rº   ©r‰   rŠ   rH   r‹   ú^([A-Za-z]:)?(.*?):(\d+):(.*)$r°   r   rz  rË  éô  rš   )r‡   rŠ   rH   r‹   rq   )rJ  r    r¨  re   ro  r!   rd  r®   r¸   r©   r   r¨   r†   r±   r8   r<   Úrsplitrg   r7  r‡  Úvaluesr»   Úcompiler´   r�   r¾   rÁ   )r_   rê   r>   rë   rÆ   rÅ   rì   rí   Ú	cmd_partsÚ	multilinerá  re  r   r¨   r‹   rµ   r¶   Ú	error_msgÚ_ml_noterã  Útotalrå  r‰   r#   rª  Ú	_match_rer‡   r“   Úparseds                                r&   rè  z#ShellFileOperations._search_with_rg2  s@  € ð OÐNÐNˆ	õ /¨wÑ7Ô7ˆ	Øð 	,Ø×Ò˜]Ñ+Ô+Ð+ð �QŠ;ˆ;Ø×Ò˜d¥C¨¡L¤LÐ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¸ªk¨k�e˜f‘n sÑ*Ð*¸uÀv¹~ˆØ×Ò˜#˜v t­S°Ñ-=Ô-=Ð>Ñ?Ô?Ð?ð " C§H¢H¨YÑ$7Ô$7Ñ7ˆØ—’˜C¨�Ñ,Ô,ˆÝ7¸Ñ?Ô?Ñˆ�õ  7°vÑ>Ô>Ñˆ�Wð Ô˜qÒ Ð ¨¯ª©¬Ð Ø#×)Ò)Ñ+Ô+ÐV¨v¬}×/BÒ/BÑ/DÔ/DÐVÈˆIÝÐ&C¸	Ð&CÐ&CÐQRÐSÑSÔSÐSð ˆð ð!ð=ð =à ð 	ð
 ˜,Ò&Ð&ØDÐD F§L¢L¡N¤N×$8Ò$8¸Ñ$>Ô$>ÐDÑDÔDˆIÝ˜	‘N”NˆEØ˜V F¨U¡NÐ2Ô3ˆDÝØØ!Ý˜|Ñ,Ô,Ø)Ø ðñ ô ð ð ˜GÒ#Ð#ØˆFØŸš™œ×,Ò,¨TÑ2Ô2ð !ð !�Ø˜$�;�;Ø ŸKšK¨¨QÑ/Ô/�EÝ˜5‘z”z Q’�ð!Ý/2°5¸´8©}¬}˜F 5¨¤8Ñ,Ð,øÝ)ð !ð !ð !Ø ˜Dð!øøøøåØÝ §¢¡¤Ñ0Ô0Ý˜|Ñ,Ô,Ø)ð	ñ ô ð õ œ
Ð#DÑEÔEˆIØˆGØŸš™œ×,Ò,¨TÑ2Ô2ð ñ �Øð ˜t tš|˜|Øð —O’O DÑ)Ô)�Øð Ø—N’N¥;ØŸgšg a™jœjÐ.¨B°!·'²'¸!±*´*Ñ<Ý$'¨¯ª°©
¬
¡O¤OØ !§¢¨¡
¤
¨4¨C¨4Ô 0ð$ñ $ô $ñ ô ð ð
 ð ˜Q’;�;Ý7¸Ñ=Ô=�FØð ØŸš¥{Ø!'¨¤Ø(.¨q¬	Ø$*¨1¤I¨d¨s¨d¤Oð(ñ (ô (ñ ô ð ùõ ˜‘L”LˆEØ˜6 &¨5¡.Ð0Ô1ˆDÝØØ!Ø &¨5¡.Ò0ÐFµD¸Ñ4FÔ4FØ)Ø ðñ ô ð s   ËK0Ë0
K=Ë<K=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                     |¦  «        z   }
|                      |
d¬¦  «        }t          |¦  «        \  }}t          |¦  «        \  }}|j        dk    rW| 	                    ¦   «         sC| 	                    ¦   «         p|j
         	                    ¦   «         pd}t          d|› �d¬¦  «        S |}|dk    rmd„ | 	                    ¦   «                              d¦  «        D ¦   «         }t          |¦  «        }||||z   …         }t          ||t          |¦  «        |¬¦  «        S |d	k    rÈi }| 	                    ¦   «                              d¦  «        D ]_}d|v rY|                     dd¦  «        }t          |¦  «        dk    r0	 t!          |d         ¦  «        ||d         <   ŒO# t"          $ r Y Œ[w xY wŒ`t          |t%          |                     ¦   «         ¦  «        t          |¦  «        |¬¦  «        S t)          j        d¦  «        }g }| 	                    ¦   «                              d¦  «        D �]}|r|dk    rŒ|                     |¦  «        }|rŒ|                      t/          |                     d¦  «        pd|                     d¦  «        z   t!          |                     d ¦  «        ¦  «        |                     d!¦  «        d"d#…         ¬$¦  «        ¦  «         Œ¯|dk    rPt3          |¦  «        }|r?|                      t/          |d         |d         |d         d"d#…         ¬$¦  «        ¦  «         �Œt          |¦  «        }||||z   …         }t          |||||z   k    pt          |¦  «        |¬%¦  «        S )&zFallback search using grep.rç  z-rnHEz--exclude-dir='.*'r   rì  z	--includerí  rî  rï  rð  re  r’  r.   rñ  rò  rl  rÿ  r^  r   ró  rô  r™  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   rz  rË  Nrø  rš   )r‡   rŠ   rH   r‹   )r    r¨  re   ro  r!   rd  r®   r¸   r©   r   r¨   r†   r±   r8   r<   rù  rg   r7  r‡  rú  r»   rû  r´   r�   r¾   rÁ   )r_   rê   r>   rë   rÆ   rÅ   rì   rí   rü  rá  re  r   r¨   r‹   rµ   r¶   rþ  rã  r   rå  r‰   r#   rª  r  r‡   r“   r  s                              r&   ré  z%ShellFileOperations._search_with_grep¿  sû  € ð ˜WÐ%ˆ	ð 	×ÒÐ-Ñ.Ô.Ð.ð �QŠ;ˆ;Ø×Ò˜d¥C¨¡L¤LÐ1Ñ2Ô2Ð2ð ð 	OØ×Ò˜k¨4×+AÒ+AÀ)Ñ+LÔ+LÐMÑNÔNÐNð ˜,Ò&Ð&Ø×Ò˜TÑ"Ô"Ð"Ð"Ø˜GÒ#Ð#Ø×Ò˜TÑ"Ô"Ð"ð 	×Ò˜×/Ò/°Ñ8Ô8Ñ9Ô9Ð9Ø×Ò˜×/Ò/°Ñ5Ô5Ñ6Ô6Ð6ð ˜f‘n¨w¸ª{¨{¨¨ÀÑBˆØ×Ò˜#˜v t­S°Ñ-=Ô-=Ð>Ñ?Ô?Ð?ð " C§H¢H¨YÑ$7Ô$7Ñ7ˆØ—’˜C¨�Ñ,Ô,ˆÝ7¸Ñ?Ô?Ñˆ�õ  7°vÑ>Ô>Ñˆ�Wð Ô˜qÒ Ð ¨¯ª©¬Ð Ø#×)Ò)Ñ+Ô+ÐV¨v¬}×/BÒ/BÑ/DÔ/DÐVÈˆIÝÐ&C¸	Ð&CÐ&CÐQRÐSÑSÔSÐSàˆØ˜,Ò&Ð&ØDÐD F§L¢L¡N¤N×$8Ò$8¸Ñ$>Ô$>ÐDÑDÔDˆIÝ˜	‘N”NˆEØ˜V F¨U¡NÐ2Ô3ˆDÝØØ!Ý˜|Ñ,Ô,Ø)ð	ñ ô ð ð ˜GÒ#Ð#ØˆFØŸš™œ×,Ò,¨TÑ2Ô2ð !ð !�Ø˜$�;�;Ø ŸKšK¨¨QÑ/Ô/�EÝ˜5‘z”z Q’�ð!Ý/2°5¸´8©}¬}˜F 5¨¤8Ñ,Ð,øÝ)ð !ð !ð !Ø ˜Dð!øøøøåØÝ §¢¡¤Ñ0Ô0Ý˜|Ñ,Ô,Ø)ð	ñ ô ð õ œ
Ð#DÑEÔEˆIØˆGØŸš™œ×,Ò,¨TÑ2Ô2ð ñ �Øð ˜t tš|˜|Øà—O’O DÑ)Ô)�Øð Ø—N’N¥;ØŸgšg a™jœjÐ.¨B°!·'²'¸!±*´*Ñ<Ý$'¨¯ª°©
¬
¡O¤OØ !§¢¨¡
¤
¨4¨C¨4Ô 0ð$ñ $ô $ñ ô ð ð
 à˜Q’;�;Ý7¸Ñ=Ô=�FØð ØŸš¥{Ø!'¨¤Ø(.¨q¬	Ø$*¨1¤I¨d¨s¨d¤Oð(ñ (ô (ñ ô ð ùõ ˜‘L”LˆEØ˜6 &¨5¡.Ð0Ô1ˆDÝØØ!Ø &¨5¡.Ò0ÐFµD¸Ñ4FÔ4FØ)ð	ñ ô ð s   Ê6KË
K"Ë!K"rU   )NNN)ri  )rº   rð   r�   rñ   );ra   rb   rc   rd   re   rZ  rg   r§   rd  r<   rh  rv  ÚstaticmethodÚbytesr~  rŠ  r�  r—  r¢  ro  r¯  r   rµ  r·  rÂ  rÅ  rD   rÇ  rÈ   rÑ  rÒ  rÌ   rÑ   rk   rß   rã   r  rç   rÔ   ru   rÚ   rÝ   rŸ   ro  r8  r{  r1  rh  r2  r9  r†   rï   rš  rÂ  r›  rÌ  rœ  rè  ré  r-   r(   r&   rQ  rQ  .  sL	  € € € € € ðð ð!2ð !2¨#ð !2ð !2ð !2ð !2ðF CGØ $ð%
ð %
˜Sð %
 sð %
¸Cð %
Øð%
Ø)6ð%
ð %
ð %
ð %
ðN( ð (¨ð (ð (ð (ð (ðð  sð °Cð ð ð ð ð> ð ¨ð  °$ð  ð  ð  ñ „\ð ðDð  cð ¸3ð È$ð ð ð ð ð:'˜cð ' dð 'ð 'ð 'ð 'ð
#ð #¨ð #¸#ð #Àcð #ð #ð #ð #ð4# ð #¨ð #ð #ð #ð #ðJ7 Sð 7¨Sð 7ð 7ð 7ð 7ð"V6 #ð V6°ð V6¸ð V6ð V6ð V6ð V6ðp7ð 7¨Sð 7¸xÈ¼}ð 7ÐX`ÐadÔXeð 7ð 7ð 7ð 7ð*,ð , #ð ,°H¸S´Mð ,ÈTð ,ð ,ð ,ð ,ð(	¨ð 	¸3ð 	È#ð 	ÐRUð 	ð 	ð 	ð 	ð
 Cð 
¨Cð 
ð 
ð 
ð 
ð0 ð
 ð 
¨ð 
ð 
ð 
ñ „\ð
ðu
ð u
˜cð u
¨3ð u
¸3ð u
È*ð u
ð u
ð u
ð u
ðn*¨3ð *°8¸C´=ð *ð *ð *ð *ðX9
¨3ð 9
°:ð 9
ð 9
ð 9
ð 9
ðv,
 #ð ,
¨*ð ,
ð ,
ð ,
ð ,
ð\
ð 
 Cð 
°H¸S´Mð 
ÈZð 
ð 
ð 
ð 
ðB: ð :¨ð :ð :ð :ð :ð>ð > ð >°ð >Àð >ð >ð >ð >ð* 3ð *°4ð *¸Kð *ð *ð *ð *ðX˜Sð  sð ¨{ð ð ð ð ð( 15ðK
ð K
˜sð K
¨Sð K
Ø (¨¤ðK
Ø9DðK
ð K
ð K
ð K
ðd +0ðQ
ð Q
 #ð Q
°3ð Q
ÀCð Q
Ø#'ðQ
Ø4?ðQ
ð Q
ð Q
ð Q
ðf sð ¨{ð ð ð ð ð<V
ð V
 ð V
¨h°s¬mð V
Àzð V
ð V
ð V
ð V
ðr 9=ðS
ð S
 cð S
¸À¼ð S
Ø(0°¬ðS
ØAKðS
ð S
ð S
ð S
ðj1 ð 1ð 1ð 1ð 1ð*¨#ð °$ð ð ð ð ð.  Sð  ¨Tð  ð  ð  ð  ðD¨3ð °4ð ð ð ð ð6 &*Ø&*ð@ð @ð @àð@ð ˜c”]ð	@ð
 ˜s”mð@ð 
ð@ð @ð @ð @ðL CLØOPØ<=ðF>ð F>˜cð F>¨ð F>¸Cð F>Ø" 3œ-ðF>Ø7:ðF>ØILðF>àðF>à69ðF>àBNðF>ð F>ð F>ð F>ðP/¨cð /¸ð /Àcð /Ø*2°3¬-ð/Ø@Cð/ØMPð/à,/ð/à:=ð/àBJÈ<ÔBXð/ð /ð /ð /ðbH¨ð H°Cð HØ%-¨c¤]ðHØ7?À´}ðHð Hð Hð HðTT
 Sð T
°ð T
¸Cð T
Èð T
ÐQ]ð T
ð T
ð T
ð T
ðl,
¨ð ,
°3ð ,
¸sð ,
ÈCð ,
ÐT`ð ,
ð ,
ð ,
ð ,
ð\$J sð $J°#ð $JÀ(È3Ä-ð $JØ"ð$JØ,/ð$JØ>Að$JØLOð$JØT`ð$Jð $Jð $Jð $JðLK sð K°#ð KÀ(È3Ä-ð KØ"ðKØ,/ðKØ>AðKØLOðKØT`ðKð Kð Kð KðZy¨ð y°Cð yÀHÈSÄMð yØ!$ðyØ.1ðyØ@CðyØNQðyØVbðyð yð yð yð yð yr(   rQ  )Yrd   rq  rs  r…  r»   r¾  r5  rî  Úabcr   r   Údataclassesr   r   Útypingr   r   r	   r
   r   Úpathlibr   Útools.binary_extensionsr   Úagent.file_safetyr   r   r   r   r@   re   r›  Ú_HOMEÚWRITE_DENIED_PATHSÚWRITE_DENIED_PREFIXESrû  r   r   r'   r/   r4   r7   Útupler<   r:   r=   rB   rD   rk   ru   r�   r†   rŸ   r§   r­   r®   r¸   r³   rg   rÁ   rÃ   rŒ  rf  Ú	frozensetrg  r  r
  r  r#  r'  r-  r0  r/  Ú	MAX_LINESÚMAX_LINE_LENGTHrÓ  r=  r>  rE  rF  rÄ  r8  rC  rG  rI  rJ  rM  rO  rQ  r-   r(   r&   ú<module>r     sç  ððð ð4 €€€Ø €€€Ø 	€	€	€	Ø 	€	€	€	Ø €€€Ø €€€Ø Ð Ð Ð Ø #Ð #Ð #Ð #Ð #Ð #Ð #Ð #Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ø Ð Ð Ð Ð Ð Ø 5Ð 5Ð 5Ð 5Ð 5Ð 5ðð ð ð ð ð ð ð ð ð ð ð ð 	ˆˆIˆDŒI‰KŒKÑÔ€à-Ð-¨eÑ4Ô4Ð à3Ð3°EÑ:Ô:Ð ð �2”:ÐBÑCÔCÐ Ø�2”:ÐLÑMÔMÐ ð" cð "¨cð "ð "ð "ð "ð" ð ¨°¬ð ð ð ð ð, #ð ¨sð °sð ð ð ð ð: €	ð�Sð ˜U 3¨ 9Ô-ð ð ð ð ð5�8˜C”=ð 5 Tð 5ð 5ð 5ð 5ð
)˜3ð ) 4ð )ð )ð )ð )ð ðUð Uð Uð Uð Uñ Uô Uñ „ðUð& ðIð Ið Ið Ið Iñ Iô Iñ „ðIð0 ð%ð %ð %ð %ð %ñ %ô %ñ „ð%ðP ðð ð ð ð ñ ô ñ „ðð ðIð Ið Ið Ið Iñ Iô Iñ „ðIðX ðð ð ð ð ñ ô ñ „ðð  ðð ð ð ð ñ ô ñ „ðð '˜BœJÐ'QÑRÔRÐ ð ]ð °u¸SÀ(È3Ä-Ð=OÔ7Pð ð ð ð ð.6 Cð .6¨E°#°s°(¬Oð .6ð .6ð .6ð .6ðl �B”JÐRÑSÔSÐ ð9 Sð 9¨U°3¸¸S°=Ô-AÀDÑ-Hð 9ð 9ð 9ð 9ð:@ð @ð @ð @ð @�Sñ @ô @ð @ðP NÐMÐMÐ ð .Ø%Ø)ØØ(ðð €ðX (˜iÐ(=Ð(=Ð(=Ñ>Ô>Ð ððð
ðð Ð ð.-¨#ð -°sð -¸tð -ð -ð -ð -ð 	1˜sð 	1 u¨T°3¨YÔ'7ð 	1ð 	1ð 	1ð 	1ð1˜sð 1 u¨T°3¨YÔ'7ð 1ð 1ð 1ð 1ð@1˜sð 1 u¨T°3¨YÔ'7ð 1ð 1ð 1ð 1ð1 ð 1¨¨t°S¨yÔ)9ð 1ð 1ð 1ð 1ð0 ØØØØðð €ð* %˜9Ð%HÐ%HÐ%HÑIÔIÐ ð €	Ø€Ø€ØÐ ØÐ ØÐ ØÐ ð 0Ð ð�sð  Sð ¨Sð ð ð ð ð -@Ø+=ð/ð / cð /Ø%(ð/ØBGÈÈSÈÄ/ð/ð /ð /ð /ð& /DØ-Að/ð /¨ð /Ø'*ð/ØFKÈCÐQTÈHÄoð/ð /ð /ð /ð &˜2œ:Ð&<Ñ=Ô=Ð ð
M¨ð 
M°ð 
Mð 
Mð 
Mð 
MðP¨8°C¬=ð P¸Tð Pð Pð Pð Pð°lð ÈSð ÐUað ð ð ð ð J$ð J$ð J$ð J$ð J$˜.ñ J$ô J$ð J$ð J$ð J$r(   