v2.7.5 (#589)
Co-authored-by: Bastien <bastien.baret@gmail.com> Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai> Co-authored-by: Julien Legrand <72564015+JulienLGRD@users.noreply.github.com> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Quentin <quentin.torroba@mistral.ai> Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
e9a9217cc8
commit
e1a25caa52
85 changed files with 2830 additions and 594 deletions
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
from vibe.core.paths._local_config_walk import (
|
||||
WALK_MAX_DEPTH,
|
||||
has_config_dirs_nearby,
|
||||
walk_local_config_dirs_all,
|
||||
ConfigWalkResult,
|
||||
walk_local_config_dirs,
|
||||
)
|
||||
from vibe.core.paths._vibe_home import (
|
||||
DEFAULT_TOOL_DIR,
|
||||
|
|
@ -31,7 +31,7 @@ __all__ = [
|
|||
"TRUSTED_FOLDERS_FILE",
|
||||
"VIBE_HOME",
|
||||
"WALK_MAX_DEPTH",
|
||||
"ConfigWalkResult",
|
||||
"GlobalPath",
|
||||
"has_config_dirs_nearby",
|
||||
"walk_local_config_dirs_all",
|
||||
"walk_local_config_dirs",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections import deque
|
||||
from dataclasses import dataclass, field
|
||||
from functools import cache
|
||||
import logging
|
||||
import os
|
||||
|
|
@ -21,24 +22,59 @@ WALK_MAX_DEPTH = 4
|
|||
_MAX_DIRS = 2000
|
||||
|
||||
|
||||
def _collect_config_dirs_at(
|
||||
path: Path,
|
||||
entry_names: set[str],
|
||||
tools: list[Path],
|
||||
skills: list[Path],
|
||||
agents: list[Path],
|
||||
@dataclass(frozen=True)
|
||||
class ConfigWalkResult:
|
||||
"""Aggregated results of a config directory walk."""
|
||||
|
||||
config_dirs: tuple[Path, ...] = ()
|
||||
tools: tuple[Path, ...] = ()
|
||||
skills: tuple[Path, ...] = ()
|
||||
agents: tuple[Path, ...] = ()
|
||||
|
||||
|
||||
@dataclass
|
||||
class _ConfigWalkCollector:
|
||||
"""Mutable accumulator used during BFS, frozen into ConfigWalkResult at the end."""
|
||||
|
||||
config_dirs: list[Path] = field(default_factory=list)
|
||||
tools: list[Path] = field(default_factory=list)
|
||||
skills: list[Path] = field(default_factory=list)
|
||||
agents: list[Path] = field(default_factory=list)
|
||||
|
||||
def freeze(self) -> ConfigWalkResult:
|
||||
return ConfigWalkResult(
|
||||
config_dirs=tuple(self.config_dirs),
|
||||
tools=tuple(self.tools),
|
||||
skills=tuple(self.skills),
|
||||
agents=tuple(self.agents),
|
||||
)
|
||||
|
||||
|
||||
def _collect_at(
|
||||
path: Path, entry_names: set[str], collector: _ConfigWalkCollector
|
||||
) -> None:
|
||||
"""Check a single directory for .vibe/ and .agents/ config subdirs."""
|
||||
if _VIBE_DIR in entry_names:
|
||||
if _VIBE_DIR in entry_names and (vibe_dir := path / _VIBE_DIR).is_dir():
|
||||
has_content = False
|
||||
if (candidate := path / _TOOLS_SUBDIR).is_dir():
|
||||
tools.append(candidate)
|
||||
collector.tools.append(candidate)
|
||||
has_content = True
|
||||
if (candidate := path / _VIBE_SKILLS_SUBDIR).is_dir():
|
||||
skills.append(candidate)
|
||||
collector.skills.append(candidate)
|
||||
has_content = True
|
||||
if (candidate := path / _AGENTS_SUBDIR).is_dir():
|
||||
agents.append(candidate)
|
||||
if _AGENTS_DIR in entry_names:
|
||||
collector.agents.append(candidate)
|
||||
has_content = True
|
||||
if (
|
||||
has_content
|
||||
or (vibe_dir / "prompts").is_dir()
|
||||
or (vibe_dir / "config.toml").is_file()
|
||||
):
|
||||
collector.config_dirs.append(vibe_dir)
|
||||
if _AGENTS_DIR in entry_names and (agents_dir := path / _AGENTS_DIR).is_dir():
|
||||
if (candidate := path / _AGENTS_SKILLS_SUBDIR).is_dir():
|
||||
skills.append(candidate)
|
||||
collector.skills.append(candidate)
|
||||
collector.config_dirs.append(agents_dir)
|
||||
|
||||
|
||||
def _scandir_entries(path: Path) -> tuple[set[str], list[Path]]:
|
||||
|
|
@ -68,60 +104,22 @@ def _scandir_entries(path: Path) -> tuple[set[str], list[Path]]:
|
|||
|
||||
|
||||
@cache
|
||||
def walk_local_config_dirs_all(
|
||||
root: Path,
|
||||
) -> tuple[tuple[Path, ...], tuple[Path, ...], tuple[Path, ...]]:
|
||||
def walk_local_config_dirs(
|
||||
root: Path, *, max_depth: int = WALK_MAX_DEPTH, max_dirs: int = _MAX_DIRS
|
||||
) -> ConfigWalkResult:
|
||||
"""Discover .vibe/ and .agents/ config directories under *root*.
|
||||
|
||||
Uses breadth-first search bounded by ``WALK_MAX_DEPTH`` and ``_MAX_DIRS``
|
||||
Uses breadth-first search bounded by *max_depth* and *max_dirs*
|
||||
to avoid unbounded traversal in large repositories.
|
||||
"""
|
||||
tools_dirs: list[Path] = []
|
||||
skills_dirs: list[Path] = []
|
||||
agents_dirs: list[Path] = []
|
||||
|
||||
Returns a ``ConfigWalkResult`` containing both the parent config dirs
|
||||
(for trust decisions) and the categorised subdirs (for loading).
|
||||
"""
|
||||
collector = _ConfigWalkCollector()
|
||||
resolved_root = root.resolve()
|
||||
queue: deque[tuple[Path, int]] = deque([(resolved_root, 0)])
|
||||
visited = 0
|
||||
|
||||
while queue and visited < _MAX_DIRS:
|
||||
current, depth = queue.popleft()
|
||||
visited += 1
|
||||
|
||||
entry_names, children = _scandir_entries(current)
|
||||
if not entry_names:
|
||||
continue
|
||||
|
||||
_collect_config_dirs_at(
|
||||
current, entry_names, tools_dirs, skills_dirs, agents_dirs
|
||||
)
|
||||
|
||||
if depth < WALK_MAX_DEPTH:
|
||||
queue.extend((child, depth + 1) for child in children)
|
||||
|
||||
if visited >= _MAX_DIRS:
|
||||
logger.warning(
|
||||
"Config directory scan reached directory limit (%d dirs) at %s",
|
||||
_MAX_DIRS,
|
||||
resolved_root,
|
||||
)
|
||||
|
||||
return (tuple(tools_dirs), tuple(skills_dirs), tuple(agents_dirs))
|
||||
|
||||
|
||||
def has_config_dirs_nearby(
|
||||
root: Path, *, max_depth: int = WALK_MAX_DEPTH, max_dirs: int = 200
|
||||
) -> bool:
|
||||
"""Quick check for .vibe/ or .agents/ config dirs in the near subtree.
|
||||
|
||||
Returns ``True`` as soon as any config directory is found, without
|
||||
enumerating all of them.
|
||||
"""
|
||||
resolved = root.resolve()
|
||||
queue: deque[tuple[Path, int]] = deque([(resolved, 0)])
|
||||
visited = 0
|
||||
found: list[Path] = []
|
||||
|
||||
while queue and visited < max_dirs:
|
||||
current, depth = queue.popleft()
|
||||
visited += 1
|
||||
|
|
@ -130,11 +128,16 @@ def has_config_dirs_nearby(
|
|||
if not entry_names:
|
||||
continue
|
||||
|
||||
_collect_config_dirs_at(current, entry_names, found, found, found)
|
||||
if found:
|
||||
return True
|
||||
_collect_at(current, entry_names, collector)
|
||||
|
||||
if depth < max_depth:
|
||||
queue.extend((child, depth + 1) for child in children)
|
||||
|
||||
return False
|
||||
if visited >= max_dirs:
|
||||
logger.warning(
|
||||
"Config directory scan reached directory limit (%d dirs) at %s",
|
||||
max_dirs,
|
||||
resolved_root,
|
||||
)
|
||||
|
||||
return collector.freeze()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue