v2.10.0 (#697)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Corentin André <corentin.andre@mistral.ai> Co-authored-by: Guillaume LE GOFF <guillaume.lgf@gmail.com> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Maxime Dolores <maxime.dolores@ext.mistral.ai> Co-authored-by: Nelson PROIA <144663685+Nelson-PROIA@users.noreply.github.com> Co-authored-by: Peter Evers <pevers90@gmail.com> 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: MichisGitIsKing <MichisGitIsKing@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
626f905186
commit
228f3c65a9
158 changed files with 7235 additions and 916 deletions
389
tests/core/test_add_dir.py
Normal file
389
tests/core/test_add_dir.py
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from vibe.core.config.harness_files import (
|
||||
HarnessFilesManager,
|
||||
get_harness_files_manager,
|
||||
init_harness_files_manager,
|
||||
reset_harness_files_manager,
|
||||
)
|
||||
from vibe.core.paths import AGENTS_MD_FILENAME
|
||||
from vibe.core.tools.utils import is_path_within_workdir
|
||||
from vibe.core.trusted_folders import trusted_folders_manager
|
||||
|
||||
|
||||
class TestHarnessFilesManagerAdditionalDirs:
|
||||
def test_default_additional_dirs_empty(self) -> None:
|
||||
mgr = HarnessFilesManager(sources=("user",))
|
||||
assert mgr._additional_dirs == ()
|
||||
|
||||
def test_init_with_additional_dirs(self, tmp_path: Path) -> None:
|
||||
d1 = tmp_path / "extra1"
|
||||
d1.mkdir()
|
||||
d2 = tmp_path / "extra2"
|
||||
d2.mkdir()
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project", additional_dirs=[d1, d2])
|
||||
mgr = get_harness_files_manager()
|
||||
# init normalizes to resolved paths so the symlink/relative-vs-absolute
|
||||
# spellings of the same dir compare equal on reinit.
|
||||
assert mgr._additional_dirs == (d1.resolve(), d2.resolve())
|
||||
|
||||
def test_reinit_idempotent_when_paths_resolve_equal(self, tmp_path: Path) -> None:
|
||||
target = tmp_path / "target"
|
||||
target.mkdir()
|
||||
link = tmp_path / "link"
|
||||
link.symlink_to(target, target_is_directory=True)
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project", additional_dirs=[target])
|
||||
# Second call with the symlink spelling must NOT raise — both resolve
|
||||
# to the same dir.
|
||||
init_harness_files_manager("user", "project", additional_dirs=[link])
|
||||
|
||||
def test_init_without_additional_dirs(self) -> None:
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project")
|
||||
mgr = get_harness_files_manager()
|
||||
assert mgr._additional_dirs == ()
|
||||
|
||||
def test_reinit_with_same_additional_dirs_is_idempotent(
|
||||
self, tmp_path: Path
|
||||
) -> None:
|
||||
d = tmp_path / "extra"
|
||||
d.mkdir()
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project", additional_dirs=[d])
|
||||
# Same sources + same additional_dirs: no-op, no raise.
|
||||
init_harness_files_manager("user", "project", additional_dirs=[d])
|
||||
|
||||
def test_reinit_with_different_additional_dirs_raises(self, tmp_path: Path) -> None:
|
||||
d1 = tmp_path / "extra1"
|
||||
d1.mkdir()
|
||||
d2 = tmp_path / "extra2"
|
||||
d2.mkdir()
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project", additional_dirs=[d1])
|
||||
with pytest.raises(RuntimeError, match="different configuration"):
|
||||
init_harness_files_manager("user", "project", additional_dirs=[d2])
|
||||
|
||||
|
||||
class TestAdditionalDirsDiscovery:
|
||||
def test_discovers_tools_from_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
tools_dir = extra / ".vibe" / "tools"
|
||||
tools_dir.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert tools_dir in mgr.project_tools_dirs
|
||||
|
||||
def test_discovers_skills_from_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
skills_dir = extra / ".vibe" / "skills"
|
||||
skills_dir.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert skills_dir in mgr.project_skills_dirs
|
||||
|
||||
def test_discovers_agents_from_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
agents_dir = extra / ".vibe" / "agents"
|
||||
agents_dir.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert agents_dir in mgr.project_agents_dirs
|
||||
|
||||
def test_discovers_agents_skills_from_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
skills_dir = extra / ".agents" / "skills"
|
||||
skills_dir.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert skills_dir in mgr.project_skills_dirs
|
||||
|
||||
def test_discovers_prompts_from_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
prompts_dir = extra / ".vibe" / "prompts"
|
||||
prompts_dir.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert prompts_dir in mgr.project_prompts_dirs
|
||||
|
||||
def test_no_dirs_when_additional_dir_has_no_vibe(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "bare_project"
|
||||
extra.mkdir()
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert mgr.project_tools_dirs == []
|
||||
assert mgr.project_skills_dirs == []
|
||||
assert mgr.project_agents_dirs == []
|
||||
|
||||
|
||||
class TestAdditionalDirsAgentsMd:
|
||||
def test_load_project_docs_includes_additional_dir_agents_md(
|
||||
self, tmp_path: Path
|
||||
) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
extra.mkdir()
|
||||
agents_md = extra / AGENTS_MD_FILENAME
|
||||
agents_md.write_text("Extra project instructions", encoding="utf-8")
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
docs = mgr.load_project_docs()
|
||||
assert any(d == extra and "Extra project" in content for d, content in docs)
|
||||
|
||||
def test_load_project_docs_skips_missing_agents_md(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "no_agents_md"
|
||||
extra.mkdir()
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
docs = mgr.load_project_docs()
|
||||
assert not any(d == extra for d, _ in docs)
|
||||
|
||||
def test_load_project_docs_skips_empty_agents_md(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "empty_agents_md"
|
||||
extra.mkdir()
|
||||
(extra / AGENTS_MD_FILENAME).write_text(" \n", encoding="utf-8")
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
docs = mgr.load_project_docs()
|
||||
assert not any(d == extra for d, _ in docs)
|
||||
|
||||
def test_find_subdirectory_agents_md_in_additional_dir(
|
||||
self, tmp_path: Path
|
||||
) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
sub = extra / "sub"
|
||||
sub.mkdir(parents=True)
|
||||
agents_md = sub / AGENTS_MD_FILENAME
|
||||
agents_md.write_text("Sub instructions", encoding="utf-8")
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
file_in_sub = sub / "main.py"
|
||||
docs = mgr.find_subdirectory_agents_md(file_in_sub)
|
||||
assert any("Sub instructions" in content for _, content in docs)
|
||||
|
||||
def test_find_subdirectory_agents_md_resolves_additional_dir(
|
||||
self, tmp_path: Path
|
||||
) -> None:
|
||||
extra = tmp_path / "extra_project"
|
||||
sub = extra / "sub"
|
||||
sub.mkdir(parents=True)
|
||||
link = tmp_path / "extra_link"
|
||||
link.symlink_to(extra, target_is_directory=True)
|
||||
agents_md = sub / AGENTS_MD_FILENAME
|
||||
agents_md.write_text("Sub instructions", encoding="utf-8")
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(link,))
|
||||
docs = mgr.find_subdirectory_agents_md(sub / "main.py")
|
||||
assert any("Sub instructions" in content for _, content in docs)
|
||||
|
||||
def test_find_subdirectory_agents_md_outside_all_dirs(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra"
|
||||
extra.mkdir()
|
||||
outside = tmp_path / "outside"
|
||||
outside.mkdir()
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
docs = mgr.find_subdirectory_agents_md(outside / "file.py")
|
||||
assert docs == []
|
||||
|
||||
|
||||
class TestFilePermissionsAdditionalDirs:
|
||||
def test_is_within_workdir_for_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra"
|
||||
extra.mkdir()
|
||||
file_in_extra = extra / "some_file.py"
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project", additional_dirs=[extra])
|
||||
|
||||
assert is_path_within_workdir(str(file_in_extra))
|
||||
|
||||
def test_is_within_workdir_resolves_additional_dir(self, tmp_path: Path) -> None:
|
||||
extra = tmp_path / "extra"
|
||||
extra.mkdir()
|
||||
link = tmp_path / "extra_link"
|
||||
link.symlink_to(extra, target_is_directory=True)
|
||||
file_in_extra = extra / "some_file.py"
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project", additional_dirs=[link])
|
||||
|
||||
assert is_path_within_workdir(str(file_in_extra))
|
||||
|
||||
def test_is_within_workdir_for_cwd(self, tmp_working_directory: Path) -> None:
|
||||
file_in_cwd = tmp_working_directory / "file.py"
|
||||
assert is_path_within_workdir(str(file_in_cwd))
|
||||
|
||||
def test_is_not_within_workdir_for_outside_path(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
outside = tmp_path / "outside"
|
||||
outside.mkdir()
|
||||
cwd = tmp_path / "cwd"
|
||||
cwd.mkdir()
|
||||
monkeypatch.chdir(cwd)
|
||||
|
||||
reset_harness_files_manager()
|
||||
init_harness_files_manager("user", "project")
|
||||
|
||||
assert not is_path_within_workdir(str(outside / "file.py"))
|
||||
|
||||
def test_is_path_within_workdir_when_manager_uninitialized(
|
||||
self, tmp_working_directory: Path
|
||||
) -> None:
|
||||
reset_harness_files_manager()
|
||||
# Without a manager, only cwd counts.
|
||||
assert is_path_within_workdir(str(tmp_working_directory / "file.py"))
|
||||
|
||||
|
||||
class TestAdditionalDirsAreImplicitlyTrusted:
|
||||
def test_load_project_docs_does_not_require_trust_for_additional_dir(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
# Untrusted cwd: primary workdir contributes nothing.
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: False)
|
||||
|
||||
extra = tmp_path / "extra_project"
|
||||
extra.mkdir()
|
||||
(extra / AGENTS_MD_FILENAME).write_text(
|
||||
"Trusted via --add-dir", encoding="utf-8"
|
||||
)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(extra,)
|
||||
)
|
||||
docs = mgr.load_project_docs()
|
||||
assert any("Trusted via --add-dir" in content for _, content in docs)
|
||||
|
||||
def test_project_tools_dirs_skip_trust_check_for_additional_dir(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setattr(trusted_folders_manager, "is_trusted", lambda _: False)
|
||||
|
||||
extra = tmp_path / "extra_project"
|
||||
tools_dir = extra / ".vibe" / "tools"
|
||||
tools_dir.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(extra,)
|
||||
)
|
||||
assert tools_dir in mgr.project_tools_dirs
|
||||
|
||||
|
||||
class TestLoadProjectDocsDedupe:
|
||||
def test_dedupes_when_cwd_under_additional_dir(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
extra = (tmp_path / "extra_project").resolve()
|
||||
extra.mkdir()
|
||||
sub = extra / "sub"
|
||||
sub.mkdir()
|
||||
(extra / AGENTS_MD_FILENAME).write_text("Root instructions", encoding="utf-8")
|
||||
(sub / AGENTS_MD_FILENAME).write_text("Sub instructions", encoding="utf-8")
|
||||
monkeypatch.chdir(sub)
|
||||
|
||||
trusted_folders_manager.trust_for_session(extra)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(extra,)
|
||||
)
|
||||
docs = mgr.load_project_docs()
|
||||
|
||||
emitted_dirs = [d for d, _ in docs]
|
||||
assert emitted_dirs.count(extra) == 1
|
||||
assert sub.resolve() in emitted_dirs
|
||||
|
||||
|
||||
class TestHookFilesAdditionalDirs:
|
||||
def test_hook_files_includes_additional_dir_hooks_toml(
|
||||
self, tmp_path: Path
|
||||
) -> None:
|
||||
extra = tmp_path / "extra"
|
||||
extra.mkdir()
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(extra,))
|
||||
assert extra / ".vibe" / "hooks.toml" in mgr.hook_files
|
||||
|
||||
def test_hook_files_deduplicates_workdir_and_additional_dir(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
workdir = tmp_path / "project"
|
||||
workdir.mkdir()
|
||||
monkeypatch.chdir(workdir)
|
||||
trusted_folders_manager.trust_for_session(workdir)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(workdir.resolve(),)
|
||||
)
|
||||
hook_file = workdir / ".vibe" / "hooks.toml"
|
||||
|
||||
assert mgr.hook_files.count(hook_file.resolve()) == 1
|
||||
|
||||
|
||||
class TestProjectPromptsDirsAdditionalDirs:
|
||||
def test_project_prompts_dirs_deduplicates_workdir_and_additional_dir(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
workdir = tmp_path / "project"
|
||||
(workdir / ".vibe" / "prompts").mkdir(parents=True)
|
||||
monkeypatch.chdir(workdir)
|
||||
trusted_folders_manager.trust_for_session(workdir)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(workdir.resolve(),)
|
||||
)
|
||||
|
||||
assert len(mgr.project_prompts_dirs) == 1
|
||||
|
||||
|
||||
class TestProjectRootsNestedDedup:
|
||||
def test_nested_add_dirs_collapse(self, tmp_path: Path) -> None:
|
||||
outer = (tmp_path / "outer").resolve()
|
||||
inner = outer / "inner"
|
||||
inner.mkdir(parents=True)
|
||||
|
||||
mgr = HarnessFilesManager(sources=("user",), _additional_dirs=(outer, inner))
|
||||
assert mgr.project_roots == [outer]
|
||||
|
||||
def test_add_dir_containing_cwd_keeps_both(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
outer = (tmp_path / "outer").resolve()
|
||||
inner = outer / "inner"
|
||||
inner.mkdir(parents=True)
|
||||
(outer / AGENTS_MD_FILENAME).write_text("Outer instructions", encoding="utf-8")
|
||||
(inner / AGENTS_MD_FILENAME).write_text("Inner instructions", encoding="utf-8")
|
||||
monkeypatch.chdir(inner)
|
||||
trusted_folders_manager.trust_for_session(inner)
|
||||
trusted_folders_manager.trust_for_session(outer)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(outer,)
|
||||
)
|
||||
# cwd survives so its walk-up semantics still work; the add-dir keeps
|
||||
# its own root-level discovery.
|
||||
assert mgr.project_roots == [inner, outer]
|
||||
docs = mgr.load_project_docs()
|
||||
assert any("Outer" in c for _, c in docs)
|
||||
assert any("Inner" in c for _, c in docs)
|
||||
|
||||
def test_add_dir_nested_under_trusted_workdir(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
outer = (tmp_path / "outer").resolve()
|
||||
inner = outer / "inner"
|
||||
inner.mkdir(parents=True)
|
||||
monkeypatch.chdir(outer)
|
||||
trusted_folders_manager.trust_for_session(outer)
|
||||
|
||||
mgr = HarnessFilesManager(
|
||||
sources=("user", "project"), _additional_dirs=(inner,)
|
||||
)
|
||||
assert mgr.project_roots == [outer]
|
||||
Loading…
Add table
Add a link
Reference in a new issue