§
    ÷žyjS  ã                  ó¼   — U d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
  ej        d¦  «        Zi Zded<   d"d„Zd#d„Zd$d„Zdddœd%d„Zddœd&d„Zd'd „Zg d!¢ZdS )(u0  Workspace and project-root resolution for LSP.

Two concerns live here:

1. **Workspace gate** â€” the upper-level "is this directory a project?"
   check.  Hermes only runs LSP when the cwd (or the file being edited)
   sits inside a git worktree.  Files outside any git root never
   trigger LSP, even if a server is configured.  This keeps Telegram
   gateway users on user-home cwd's from spawning daemons.

2. **NearestRoot** â€” the per-server project-root walk.  Each language
   server cares about a different marker (``pyproject.toml`` for
   Python, ``Cargo.toml`` for Rust, ``go.mod`` for Go, etc.) and
   wants the directory containing that marker.  ``nearest_root()``
   walks up from a starting path looking for any of a list of marker
   files, optionally bailing if an exclude marker shows up first.
é    )ÚannotationsN)ÚPath)ÚIterableÚOptionalÚTuplezagent.lsp.workspaceÚdictÚ_workspace_cacheÚpathÚstrÚreturnc                óz   — t           j                             t           j                             | ¦  «        ¦  «        S )uE  Normalize a path for use as a stable map key.

    Resolves ``~``, makes absolute, and collapses ``.``/``..``.  We do
    NOT resolve symlinks here â€” symlink stability matters for some
    LSP servers (rust-analyzer cares about Cargo workspace identity)
    and we want the canonical path the user typed when possible.
    )Úosr
   ÚabspathÚ
expanduser)r
   s    ú9/home/ragecks/.hermes/hermes-agent/agent/lsp/workspace.pyÚnormalize_pathr   !   s(   € õ Œ7�?Š?�2œ7×-Ò-¨dÑ3Ô3Ñ4Ô4Ð4ó    ÚstartúOptional[str]c                ó<  — 	 t          t          | ¦  «        ¦  «        }|                     ¦   «         r|j        }n# t          t
          t          f$ r Y dS w xY wt                               t          |¦  «        ¦  «        }|�|\  }}|S |}t          d¦  «        D ]k}|dz  }	 |                     ¦   «         r,t          |¦  «        }|dft          t          |¦  «        <   |c S n# t          $ r Y  nw xY w|j        }	|	|k    r n|	}Œldt          t          |¦  «        <   dS )uF  Walk up from ``start`` looking for a ``.git`` entry (file or dir).

    Returns the directory containing ``.git``, or ``None`` if no git
    root is found before hitting the filesystem root.

    A ``.git`` *file* (not directory) means we're inside a git
    worktree set up via ``git worktree add`` â€” both forms count.
    Né@   z.gitT©NF)r   r   Úis_fileÚparentÚOSErrorÚRuntimeErrorÚ
ValueErrorr	   Úgetr   ÚrangeÚexists)
r   Ú
start_pathÚcachedÚrootÚ_is_gitÚcurÚ_Ú
git_markerÚresolvedr   s
             r   Úfind_git_worktreer)   ,   sX  € ðÝ�.¨Ñ/Ô/Ñ0Ô0ˆ
Ø×ÒÑÔð 	+Ø#Ô*ˆJøøÝ•\¥:Ð.ð ð ð ð ˆtˆtðøøøõ ×!Ò!¥# j¡/¤/Ñ2Ô2€FØÐØ‰ˆˆgØˆà
€Cõ �2‰YŒYð ð ˆØ˜6‘\ˆ
ð	Ø× Ò Ñ"Ô"ð  Ý˜s™8œ8�Ø5=¸tÐ4DÕ ¥ Z¡¤Ñ1Ø���ð øõ ð 	ð 	ð 	àˆEˆEð	øøøð ”ˆØ�SŠ=ˆ=ØˆEØˆˆà(5Õ•S˜‘_”_Ñ%Øˆ4s!   ‚7: ºAÁAÂ!=C"Ã"
C0Ã/C0Úworkspace_rootÚboolc                óÂ   — t          | ¦  «        }t          |¦  «        }||k    rdS 	 t          j                             ||g¦  «        }n# t          $ r Y dS w xY w||k    S )u[  Return True iff ``path`` is inside (or equal to) ``workspace_root``.

    Uses absolute paths but does not resolve symlinks â€” a file accessed
    via a symlink that points outside the workspace still counts as
    outside.  This is the conservative interpretation; matches LSP
    behaviour where servers reject didOpen for unrelated files.
    TF)r   r   r
   Ú
commonpathr   )r
   r*   Úpr#   Úcommons        r   Úis_inside_workspacer0   [   sz   € õ 	�tÑÔ€AÝ˜.Ñ)Ô)€DØˆD‚y€yØˆtðÝ”×#Ò# Q¨ IÑ.Ô.ˆˆøÝð ð ð àˆuˆuðøøøð �TŠ>Ðs   ¨!A
 Á

AÁA)ÚexcludesÚceilingÚmarkersúIterable[str]r1   úOptional[Iterable[str]]r2   c               ó|  — t          t          | ¦  «        ¦  «        }	 |                     ¦   «         r|j        }n# t          t
          t          f$ r Y dS w xY w|rt          t          |¦  «        ¦  «        nd}t          |¦  «        }|rt          |¦  «        ng }|}t          d¦  «        D ]�}	|D ].}
	 ||
z   	                    ¦   «         r  dS Œ# t          $ r Y Œ+w xY w|D ]=}	 ||z   	                    ¦   «         rt          |¦  «        c c S Œ.# t          $ r Y Œ:w xY w|�	||k    r dS |j        }||k    r dS |}Œ‘dS )u+  Walk up from ``start`` looking for any of the given marker files.

    Returns the **directory containing** the first matched marker, or
    ``None`` if no marker is found before hitting ``ceiling`` (or the
    filesystem root if no ceiling).

    If ``excludes`` is provided and an exclude marker matches *first*
    in the upward walk, returns ``None`` â€” the server is gated off
    for that file.  Mirrors OpenCode's NearestRoot exclude semantics
    (e.g. typescript skips deno projects when ``deno.json`` is found
    before ``package.json``).
    Nr   )r   r   r   r   r   r   r   Úlistr   r    r   )r   r3   r1   r2   r!   Úceiling_pathÚmarkers_listÚexcludes_listr%   r&   ÚexcÚmarkerr   s                r   Únearest_rootr=   q   s¼  € õ& •n UÑ+Ô+Ñ,Ô,€JðØ×ÒÑÔð 	+Ø#Ô*ˆJøøÝ•\¥:Ð.ð ð ð Øˆtˆtðøøøà4;ÐE•4� wÑ/Ô/Ñ0Ô0Ð0À€Lå˜‘=”=€LØ&.Ð6•D˜‘N”N�N°B€Mà
€Cõ �2‰YŒYð ð ˆð !ð 	ð 	ˆCðØ˜#‘I×%Ò%Ñ'Ô'ð  Ø˜4˜4˜4ð øåð ð ð Ø�ðøøøð #ð 	ð 	ˆFðØ˜&‘L×(Ò(Ñ*Ô*ð $Ý˜s™8œ8�O�O�O�O�Oð$øåð ð ð Ø�ðøøøð Ð#¨¨|Ò(;Ð(;Ø�4�4Ø”ˆØ�SŠ=ˆ=Ø�4�4ØˆˆØˆ4s3   ž: ºAÁAÂ2CÃ
CÃCÃ#%DÄ
DÄD)ÚcwdÚ	file_pathr>   úTuple[Optional[str], bool]c               ó¤   — |pt          j        ¦   «         }t          |¦  «        }|�t          | |¦  «        r|dfS t          | ¦  «        }|�|dfS dS )uz  Resolve the workspace root for a file.

    Returns ``(workspace_root, gated_in)`` where ``gated_in`` is True
    iff LSP should run for this file at all.  Currently the gate is
    "file is inside a git worktree found by walking up from cwd OR
    from the file itself".

    The cwd path takes precedence â€” if the agent was launched in a
    git project, that worktree is the workspace, and any edit inside
    it (regardless of where the file lives) is in-scope.  If the cwd
    isn't in a git worktree, we try the file's own location as a
    fallback.

    Returns ``(None, False)`` when neither path is in a git worktree.
    NTr   )r   Úgetcwdr)   r0   )r?   r>   Úcwd_rootÚ	file_roots       r   Úresolve_workspace_for_filerE   ­   sg   € ð( Ð
•”‘”€CÝ  Ñ%Ô%€HØÐÝ˜y¨(Ñ3Ô3ð 	"Ø˜T�>Ð!õ " )Ñ,Ô,€IØÐØ˜$ˆÐØˆ;r   ÚNonec                 ó8   — t                                ¦   «          dS )z�Clear the workspace-resolution cache.

    Called on service shutdown so a subsequent re-init doesn't pick
    up stale results from a previous session.
    N)r	   Úclear© r   r   Úclear_cacherJ   Ï   s   € õ ×ÒÑÔÐÐÐr   )r)   r0   r=   r   rE   rJ   )r
   r   r   r   )r   r   r   r   )r
   r   r*   r   r   r+   )
r   r   r3   r4   r1   r5   r2   r   r   r   )r?   r   r>   r   r   r@   )r   rF   )Ú__doc__Ú
__future__r   Úloggingr   Úpathlibr   Útypingr   r   r   Ú	getLoggerÚloggerr	   Ú__annotations__r   r)   r0   r=   rE   rJ   Ú__all__rI   r   r   ú<module>rT      sB  ððð ð ð" #Ð "Ð "Ð "Ð "Ð "à €€€Ø 	€	€	€	Ø Ð Ð Ð Ð Ð Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,à	ˆÔ	Ð0Ñ	1Ô	1€ð
 Ð Ð Ð Ð Ñ ð5ð 5ð 5ð 5ð,ð ,ð ,ð ,ð^ð ð ð ð4 )-Ø!ð9ð 9ð 9ð 9ð 9ð 9ð~ ðð ð ð ð ð ðDð ð ð ðð ð €€€r   