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:
Mathias Gesbert 2026-05-19 11:56:25 +02:00 committed by GitHub
parent 626f905186
commit 228f3c65a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
158 changed files with 7235 additions and 916 deletions

389
tests/core/test_add_dir.py Normal file
View 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]