This commit is contained in:
Quentin Torroba 2025-12-26 17:03:09 +01:00 committed by Quentin
parent 08d8e85447
commit 4d449be276
14 changed files with 65 additions and 46 deletions

2
.vscode/launch.json vendored
View file

@ -1,5 +1,5 @@
{
"version": "1.3.2",
"version": "1.3.3",
"configurations": [
{
"name": "ACP Server",

View file

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.3.3] - 2025-12-26
### Fixed
- Fix config desyncing issues
## [1.3.2] - 2025-12-24
### Added

View file

@ -1,7 +1,7 @@
id = "mistral-vibe"
name = "Mistral Vibe"
description = "Mistral's open-source coding assistant"
version = "1.3.2"
version = "1.3.3"
schema_version = 1
authors = ["Mistral AI"]
repository = "https://github.com/mistralai/mistral-vibe"
@ -11,25 +11,25 @@ name = "Mistral Vibe"
icon = "./icons/mistral_vibe.svg"
[agent_servers.mistral-vibe.targets.darwin-aarch64]
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.2/vibe-acp-darwin-aarch64-1.3.2.zip"
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.3/vibe-acp-darwin-aarch64-1.3.3.zip"
cmd = "./vibe-acp"
[agent_servers.mistral-vibe.targets.darwin-x86_64]
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.2/vibe-acp-darwin-x86_64-1.3.2.zip"
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.3/vibe-acp-darwin-x86_64-1.3.3.zip"
cmd = "./vibe-acp"
[agent_servers.mistral-vibe.targets.linux-aarch64]
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.2/vibe-acp-linux-aarch64-1.3.2.zip"
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.3/vibe-acp-linux-aarch64-1.3.3.zip"
cmd = "./vibe-acp"
[agent_servers.mistral-vibe.targets.linux-x86_64]
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.2/vibe-acp-linux-x86_64-1.3.2.zip"
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.3/vibe-acp-linux-x86_64-1.3.3.zip"
cmd = "./vibe-acp"
[agent_servers.mistral-vibe.targets.windows-aarch64]
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.2/vibe-acp-windows-aarch64-1.3.2.zip"
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.3/vibe-acp-windows-aarch64-1.3.3.zip"
cmd = "./vibe-acp.exe"
[agent_servers.mistral-vibe.targets.windows-x86_64]
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.2/vibe-acp-windows-x86_64-1.3.2.zip"
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v1.3.3/vibe-acp-windows-x86_64-1.3.3.zip"
cmd = "./vibe-acp.exe"

View file

@ -1,6 +1,6 @@
[project]
name = "mistral-vibe"
version = "1.3.2"
version = "1.3.3"
description = "Minimal CLI coding agent by Mistral"
readme = "README.md"
requires-python = ">=3.12"

View file

@ -41,7 +41,7 @@ class TestACPInitialize:
),
)
assert response.agentInfo == Implementation(
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.2"
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.3"
)
assert response.authMethods == []
@ -63,7 +63,7 @@ class TestACPInitialize:
),
)
assert response.agentInfo == Implementation(
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.2"
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.3"
)
assert response.authMethods is not None

View file

@ -20,7 +20,7 @@ def config() -> VibeConfig:
@pytest.fixture
def skill_manager(config: VibeConfig) -> SkillManager:
return SkillManager(config)
return SkillManager(lambda: config)
class TestSkillManagerDiscovery:
@ -38,7 +38,7 @@ class TestSkillManagerDiscovery:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
assert "test-skill" in manager.available_skills
assert manager.available_skills["test-skill"].description == "A test skill"
@ -54,7 +54,7 @@ class TestSkillManagerDiscovery:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
assert len(manager.available_skills) == 3
assert "skill-one" in manager.available_skills
@ -76,7 +76,7 @@ class TestSkillManagerDiscovery:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skills = manager.available_skills
assert len(skills) == 1
@ -96,7 +96,7 @@ class TestSkillManagerDiscovery:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skills = manager.available_skills
assert len(skills) == 1
@ -121,7 +121,7 @@ class TestSkillManagerParsing:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skill = manager.get_skill("full-skill")
assert skill is not None
@ -141,7 +141,7 @@ class TestSkillManagerParsing:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skill = manager.get_skill("test-skill")
assert skill is not None
@ -163,7 +163,7 @@ class TestSkillManagerParsing:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skills = manager.available_skills
assert len(skills) == 1
@ -185,7 +185,7 @@ class TestSkillManagerParsing:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skills = manager.available_skills
assert len(skills) == 1
@ -209,7 +209,7 @@ class TestSkillManagerSearchPaths:
include_project_context=False,
skill_paths=[skills_dir_1, skills_dir_2],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skills = manager.available_skills
assert len(skills) == 2
@ -232,7 +232,7 @@ class TestSkillManagerSearchPaths:
include_project_context=False,
skill_paths=[skills_dir_1, skills_dir_2],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skills = manager.available_skills
assert len(skills) == 1
@ -249,7 +249,7 @@ class TestSkillManagerSearchPaths:
include_project_context=False,
skill_paths=[skills_dir, tmp_path / "nonexistent"],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
assert len(manager.available_skills) == 1
assert "valid-skill" in manager.available_skills
@ -265,7 +265,7 @@ class TestSkillManagerGetSkill:
include_project_context=False,
skill_paths=[skills_dir],
)
manager = SkillManager(config)
manager = SkillManager(lambda: config)
skill = manager.get_skill("test-skill")
assert skill is not None

View file

@ -22,7 +22,7 @@ def test_get_universal_system_prompt_includes_windows_prompt_on_windows(
include_model_info=False,
include_commit_signature=False,
)
tool_manager = ToolManager(config)
tool_manager = ToolManager(lambda: config)
prompt = get_universal_system_prompt(tool_manager, config)

View file

@ -18,7 +18,7 @@ def config():
@pytest.fixture
def tool_manager(config):
return ToolManager(config)
return ToolManager(lambda: config)
def test_returns_default_config_when_no_overrides(tool_manager):
@ -39,7 +39,7 @@ def test_merges_user_overrides_with_defaults():
include_project_context=False,
tools={"bash": BaseToolConfig(permission=ToolPermission.ALWAYS)},
)
manager = ToolManager(vibe_config)
manager = ToolManager(lambda: vibe_config)
config = manager.get_tool_config("bash")
@ -58,7 +58,7 @@ def test_preserves_tool_specific_fields_from_overrides():
tools={"bash": BaseToolConfig(permission=ToolPermission.ASK)},
)
vibe_config.tools["bash"].__pydantic_extra__ = {"default_timeout": 600}
manager = ToolManager(vibe_config)
manager = ToolManager(lambda: vibe_config)
config = manager.get_tool_config("bash")
@ -80,7 +80,7 @@ def test_applies_workdir_from_vibe_config(tmp_path):
include_project_context=False,
workdir=tmp_path,
)
manager = ToolManager(vibe_config)
manager = ToolManager(lambda: vibe_config)
config = manager.get_tool_config("bash")

2
uv.lock generated
View file

@ -661,7 +661,7 @@ wheels = [
[[package]]
name = "mistral-vibe"
version = "1.3.2"
version = "1.3.3"
source = { editable = "." }
dependencies = [
{ name = "agent-client-protocol" },

View file

@ -3,4 +3,4 @@ from __future__ import annotations
from pathlib import Path
VIBE_ROOT = Path(__file__).parent
__version__ = "1.3.2"
__version__ = "1.3.3"

View file

@ -74,7 +74,7 @@ class BottomApp(StrEnum):
Input = auto()
class VibeApp(App):
class VibeApp(App): # noqa: PLR0904
ENABLE_COMMAND_PALETTE = False
CSS_PATH = "app.tcss"
@ -104,7 +104,7 @@ class VibeApp(App):
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
self.config = config
self._config = config
self._current_agent_mode = initial_mode
self.enable_streaming = enable_streaming
self.agent: Agent | None = None
@ -147,6 +147,10 @@ class VibeApp(App):
self._last_escape_time: float | None = None
self._terminal_theme = capture_terminal_theme()
@property
def config(self) -> VibeConfig:
return self.agent.config if self.agent else self._config
def compose(self) -> ComposeResult:
with VerticalScroll(id="chat"):
yield WelcomeBanner(self.config)
@ -704,8 +708,8 @@ class VibeApp(App):
if self.agent:
await self.agent.reload_with_initial_messages(config=new_config)
self.config = new_config
else:
self._config = new_config
if self._context_progress:
if self.config.auto_compact_threshold > 0:
current_tokens = (

View file

@ -104,8 +104,8 @@ class Agent:
self._max_turns = max_turns
self._max_price = max_price
self.tool_manager = ToolManager(config)
self.skill_manager = SkillManager(config)
self.tool_manager = ToolManager(lambda: self.config)
self.skill_manager = SkillManager(lambda: self.config)
self.format_handler = APIToolFormatHandler()
self.backend_factory = lambda: backend or self._select_backend()
@ -870,8 +870,8 @@ class Agent:
if max_price is not None:
self._max_price = max_price
self.tool_manager = ToolManager(self.config)
self.skill_manager = SkillManager(self.config)
self.tool_manager = ToolManager(lambda: self.config)
self.skill_manager = SkillManager(lambda: self.config)
new_system_prompt = get_universal_system_prompt(
self.tool_manager, self.config, self.skill_manager

View file

@ -1,5 +1,6 @@
from __future__ import annotations
from collections.abc import Callable
from logging import getLogger
from pathlib import Path
from typing import TYPE_CHECKING
@ -16,9 +17,9 @@ logger = getLogger("vibe")
class SkillManager:
def __init__(self, config: VibeConfig) -> None:
self._config = config
self._search_paths = self._compute_search_paths(config)
def __init__(self, config_getter: Callable[[], VibeConfig]) -> None:
self._config_getter = config_getter
self._search_paths = self._compute_search_paths(self._config)
self.available_skills = self._discover_skills()
if self.available_skills:
@ -28,6 +29,10 @@ class SkillManager:
len(self._search_paths),
)
@property
def _config(self) -> VibeConfig:
return self._config_getter()
@staticmethod
def _compute_search_paths(config: VibeConfig) -> list[Path]:
paths: list[Path] = []

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from collections.abc import Iterator
from collections.abc import Callable, Iterator
import importlib.util
import inspect
from logging import getLogger
@ -38,16 +38,20 @@ class ToolManager:
should have its own ToolManager instance.
"""
def __init__(self, config: VibeConfig) -> None:
self._config = config
def __init__(self, config_getter: Callable[[], VibeConfig]) -> None:
self._config_getter = config_getter
self._instances: dict[str, BaseTool] = {}
self._search_paths: list[Path] = self._compute_search_paths(config)
self._search_paths: list[Path] = self._compute_search_paths(self._config)
self._available: dict[str, type[BaseTool]] = {
cls.get_name(): cls for cls in self._iter_tool_classes(self._search_paths)
}
self._integrate_mcp()
@property
def _config(self) -> VibeConfig:
return self._config_getter()
@staticmethod
def _compute_search_paths(config: VibeConfig) -> list[Path]:
paths: list[Path] = [DEFAULT_TOOL_DIR.path]