Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Jean-Baptiste Muscat <jeanbaptiste.muscatdupuis@mistral.ai> Co-authored-by: JeroenvdV <JeroenvdV@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: Paul Cacheux <paul.cacheux@mistral.ai> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Simon Van de Kerckhove <simon.vandekerckhove@mistral.ai> Co-authored-by: Val <102326092+vdeva@users.noreply.github.com> Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com> Co-authored-by: allansimon-mistral <allan.simon@ext.mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
6.7 KiB
6.7 KiB
AGENTS.md
Conventions for AI agents and humans contributing to Mistral Vibe — a Python 3.12+ CLI coding assistant managed with uv.
Layout: vibe/core is the engine (agent loop, tools, LLM backends, config); vibe/cli is the Textual TUI; vibe/acp bridges to the Agent Client Protocol; vibe/setup runs first-run wizards. Tests live in tests/ with autouse fixtures in conftest.py and test doubles in tests/stubs/.
Commands
Always go through uv — never invoke bare python or pip.
uv run vibe/uv run vibe-acp— the two entry points.uv run pytest— full suite (parallel viapytest-xdist).uv run pyright— strict type check.uv run ruff check --fix .anduv run ruff format .— run both after every code change and report the files modified.uv run pre-commit run --all-files— full lint pass. Install once withuv tool install pre-commit && uv run pre-commit install.- Useful uv basics:
uv sync --all-extras,uv add <pkg>,uv remove <pkg>.
Project layout & module conventions
__init__.pyexposes the public API via an explicit__all__.- Private modules are prefixed with
_(e.g._settings.py,_config.py). - Pydantic models live in
models.py; configuration in_settings.py/_config.py. - Abstract interfaces use the
_port.pysuffix (hexagonal-style ports). - Tests mirror the source layout; test doubles in
tests/stubs/are namedFake*.
Python style
- Prefer
match/caseover longif/elifchains. - Use the walrus operator
:=only when it shortens code and improves clarity. - Be a never-nester: early returns and guard clauses over nested blocks.
- Modern type hints only: built-in generics (
list,dict) and|unions. Never importOptional,Union,Dict,Listfromtyping. - Use
pathlib.Path(andanyio.Pathin async paths) instead ofos.path. - Use f-strings, comprehensions, and context managers; follow PEP 8.
- Enums:
StrEnum/IntEnumwithauto()and UPPERCASE members. For type-mixing, the mix-in type comes beforeEnumin the bases. Add methods or@propertyrather than parallel lookup tables. - Write declarative, minimalist code: express intent, drop boilerplate.
- Never call a private method from outside of it's class
- Avoid comments and docstrings, except for when there's a hard to spot corner case
Typing & imports
- Pyright is strict and gates CI; fix types at the source.
- No relative imports —
ban-relative-imports = "all". Alwaysfrom vibe.core.x import …. - No inline
# type: ignoreor# noqa. Fix with refined signatures (TypeVar, Protocol),isinstanceguards,typing.castwhen control flow guarantees the type, or a small typed wrapper at the boundary.
Pydantic
- Parse external data via
model_validate,field_validator, ormodel_validator(mode="before")— never ad-hocgetattr/hasattrwalks or customfrom_sdkconstructors. - Set
ConfigDict(extra=…)explicitly. Usevalidation_alias(or field aliases) for kebab-case TOML keys. - Discriminated unions (e.g. MCP
transport): use sibling final classes plus a shared base/mixin, and compose withAnnotated[Union[...], Field(discriminator=...)]. Never narrow the discriminator field in a subclass — it violates LSP and pyright will reject it. - Document
Raises:only for exceptions the function actually raises (or that propagate from public API calls). Don't list speculative built-ins.
Async
asynciois the orchestration runtime in the agent loop and tool execution. Useasyncio.create_task+ queues for concurrent work, not blanketgather.- Use
anyio.Pathfor file I/O on async paths. - Streaming surfaces return
AsyncGenerator[Event, None], not coroutines. - HTTP via
httpx.AsyncClient; mock withrespxin tests.
Tools
- Subclass
BaseToolfromvibe/core/tools/base.pywith a Pydantic args model and aBaseToolConfiggeneric parameter. - Implement
async def run(args, ctx: InvokeContext)and yield events progressively. - Raise
ToolErrorfor user-facing failures; raiseToolPermissionErrorfor authorization failures. - Declare permission with
ToolPermission(ALWAYS/ASK/NEVER); honor it consistently.
Logging & errors
- Use
from vibe.core.logger import logger— stdlibloggingwithStructuredLogFormatter, notstructlog. - Configure via env:
LOG_LEVEL(defaultWARNING),DEBUG_MODE,LOG_MAX_BYTES. Logs land in~/.vibe/logs/vibe.log. - Pass variables as keyword args, not interpolated into the message: prefer
logger.error("Failed to fetch", url=url)overlogger.error(f"Failed to fetch {url}"). - Define module-local exception hierarchies. Always chain with
raise NewError(...) from e. Rich exceptions expose a_fmt()helper for human-readable output.
File I/O
- Prefer
vibe.core.utils.io.read_safe/read_safe_async/decode_safeover rawPath.read_text(),Path.read_bytes().decode(), oropen(). - They return
ReadSafeResult(text, encoding)and try UTF-8, then BOM detection, then locale, thencharset_normalizerlazily. - Pass
raise_on_error=Trueonly when callers must distinguish corrupt files from valid ones; the default replaces undecodable bytes with U+FFFD.
Tests
- Stack:
pytest+pytest-asyncio+pytest-textual-snapshot+respx. - Mark async tests with
@pytest.mark.asyncio. Mock outbound HTTP withrespx. - Rely on the autouse fixtures in
tests/conftest.py(config_dir,tmp_working_directory) for filesystem and home-dir isolation. - No docstrings on test functions, methods, or classes — descriptive names like
test_create_user_returns_403_when_unauthorizedcarry the intent. Pytest displays docstrings instead of node IDs when present, which hurts. - Tests are exempt from the
ANNandPLRruff rules (seeper-file-ignores).
Git
- Never use
git commit --amend,git push --force, orgit push --force-with-lease. - Always create new commits and push with a plain
git push. - If a push is rejected due to upstream changes, rebase onto the updated remote branch — never merge and never force-push.
Editor tip
In Cursor / Pyright, the "Add import" quick fix is missing — use the workspace snippets acpschema, acphelpers, vibetypes, vibeconfig to insert the import line, then rename the symbol.
Autoimprovement
- Suggest to add new rules to AGENTS.md based on user input or PR comments, when a change request could be generalized as a rule.
- Suggest updates to the README.md file according to feature changes or additions
- Keep the builtin Vibe Skill (
vibe/core/skills/builtins/vibe.py) up-to-date. It documents the CLI's features, such as args, flags, config options and persistence, commands, built-in agents, file discovery logic.