Files
mistral-vibe/vibe/cli/entrypoint.py
Mathias Gesbert eb580209d4 v2.6.0 (#524)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai>
Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai>
Co-authored-by: Gauthier Guinet <43207538+Gguinet@users.noreply.github.com>
Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai>
Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com>
Co-authored-by: Quentin <torroba.q@gmail.com>
Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>
Co-authored-by: Simon Van de Kerckhove <simon.vandekerckhove@mistral.ai>
Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com>
Co-authored-by: angelapopopo <angele.lenglemetz@mistral.ai>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
2026-03-23 18:45:21 +01:00

167 lines
5.0 KiB
Python

from __future__ import annotations
import argparse
import os
from pathlib import Path
import sys
from rich import print as rprint
from vibe import __version__
from vibe.core.agents.models import BuiltinAgentName
from vibe.core.config.harness_files import init_harness_files_manager
from vibe.core.trusted_folders import has_trustable_content, trusted_folders_manager
from vibe.setup.trusted_folders.trust_folder_dialog import (
TrustDialogQuitException,
ask_trust_folder,
)
def parse_arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Run the Mistral Vibe interactive CLI")
parser.add_argument(
"-v", "--version", action="version", version=f"%(prog)s {__version__}"
)
parser.add_argument(
"initial_prompt",
nargs="?",
metavar="PROMPT",
help="Initial prompt to start the interactive session with.",
)
parser.add_argument(
"-p",
"--prompt",
nargs="?",
const="",
metavar="TEXT",
help="Run in programmatic mode: send prompt, auto-approve all tools, "
"output response, and exit.",
)
parser.add_argument(
"--max-turns",
type=int,
metavar="N",
help="Maximum number of assistant turns "
"(only applies in programmatic mode with -p).",
)
parser.add_argument(
"--max-price",
type=float,
metavar="DOLLARS",
help="Maximum cost in dollars (only applies in programmatic mode with -p). "
"Session will be interrupted if cost exceeds this limit.",
)
parser.add_argument(
"--enabled-tools",
action="append",
metavar="TOOL",
help="Enable specific tools. In programmatic mode (-p), this disables "
"all other tools. "
"Can use exact names, glob patterns (e.g., 'bash*'), or "
"regex with 're:' prefix. Can be specified multiple times.",
)
parser.add_argument(
"--output",
type=str,
choices=["text", "json", "streaming"],
default="text",
help="Output format for programmatic mode (-p): 'text' "
"for human-readable (default), 'json' for all messages at end, "
"'streaming' for newline-delimited JSON per message.",
)
parser.add_argument(
"--agent",
metavar="NAME",
default=BuiltinAgentName.DEFAULT,
help="Agent to use (builtin: default, plan, accept-edits, auto-approve, "
"or custom from ~/.vibe/agents/NAME.toml)",
)
parser.add_argument("--setup", action="store_true", help="Setup API key and exit")
parser.add_argument(
"--workdir",
type=Path,
metavar="DIR",
help="Change to this directory before running",
)
# Feature flag for teleport, not exposed to the user yet
parser.add_argument("--teleport", action="store_true", help=argparse.SUPPRESS)
continuation_group = parser.add_mutually_exclusive_group()
continuation_group.add_argument(
"-c",
"--continue",
action="store_true",
dest="continue_session",
help="Continue from the most recent saved session",
)
continuation_group.add_argument(
"--resume",
nargs="?",
const=True,
default=None,
metavar="SESSION_ID",
help="Resume a session. Without SESSION_ID, shows an interactive picker.",
)
return parser.parse_args()
def check_and_resolve_trusted_folder() -> None:
try:
cwd = Path.cwd()
except FileNotFoundError:
rprint(
"[red]Error: Current working directory no longer exists.[/]\n"
"[yellow]The directory you started vibe from has been deleted. "
"Please change to an existing directory and try again, "
"or use --workdir to specify a working directory.[/]"
)
sys.exit(1)
if not has_trustable_content(cwd) or cwd.resolve() == Path.home().resolve():
return
is_folder_trusted = trusted_folders_manager.is_trusted(cwd)
if is_folder_trusted is not None:
return
try:
is_folder_trusted = ask_trust_folder(cwd)
except (KeyboardInterrupt, EOFError, TrustDialogQuitException):
sys.exit(0)
except Exception as e:
rprint(f"[yellow]Error showing trust dialog: {e}[/]")
return
if is_folder_trusted is True:
trusted_folders_manager.add_trusted(cwd)
elif is_folder_trusted is False:
trusted_folders_manager.add_untrusted(cwd)
def main() -> None:
args = parse_arguments()
if args.workdir:
workdir = args.workdir.expanduser().resolve()
if not workdir.is_dir():
rprint(
f"[red]Error: --workdir does not exist or is not a directory: {workdir}[/]"
)
sys.exit(1)
os.chdir(workdir)
is_interactive = args.prompt is None
if is_interactive:
check_and_resolve_trusted_folder()
init_harness_files_manager("user", "project")
from vibe.cli.cli import run_cli
run_cli(args)
if __name__ == "__main__":
main()