Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tracking, and an analytics dashboard to monitor and compare agents side-by-side.

- ⚡ **Zero config** — no setup required; `vp run <agent>` just works. Optional YAML for custom configuration
- 🐳 **Isolated agents** — each agent runs in its own Docker container
- 🔀 **Unified interface** — one CLI for Claude, Gemini, Codex, Devstral, Copilot, Auggie & more
- 🔀 **Unified interface** — one CLI for Claude, Gemini, Codex, Devstral/Vibe, Copilot, Auggie & more
- 📊 **Local analytics dashboard** — track usage and HTTP traffic per agent, plus token metrics
- ⚖️ **Agent comparison** — benchmark multiple agents against each other in the dashboard
- 🔒 **Privacy-first** — all metrics collected and stored locally, never sent to the cloud
Expand All @@ -42,8 +42,23 @@ vp run <agent>
# examples:
vp run claude
vp run codex
vp run vibe # alias of devstral
```

## IKWID Mode (`--ikwid`)

Use `--ikwid` to append each agent's auto-approval / permission-skip flag when supported.

| Agent | `--ikwid` appended args |
|---|---|
| `claude` | `--dangerously-skip-permissions` |
| `gemini` | `--approval-mode=yolo` |
| `devstral` (`vibe`) | `--auto-approve` |
| `copilot` | `--yolo` |
| `codex` | `--dangerously-bypass-approvals-and-sandbox` |
| `opencode` | Not supported |
| `auggie` | Not supported |

![VibePod CLI preview](docs/assets/preview.png)

## Tool Thumbnails
Expand Down Expand Up @@ -96,7 +111,7 @@ Current defaults:
- `claude` -> `vibepod/claude:latest`
- `gemini` -> `vibepod/gemini:latest`
- `opencode` -> `vibepod/opencode:latest`
- `devstral` -> `vibepod/devstral:latest`
- `devstral` (alias: `vibe`) -> `vibepod/devstral:latest`
- `auggie` -> `vibepod/auggie:latest`
- `copilot` -> `vibepod/copilot:latest`
- `codex` -> `vibepod/codex:latest`
Expand All @@ -112,6 +127,7 @@ VP_IMAGE_CLAUDE=vibepod/claude:latest vp run claude
VP_IMAGE_GEMINI=vibepod/gemini:latest vp run gemini
VP_IMAGE_OPENCODE=vibepod/opencode:latest vp run opencode
VP_IMAGE_DEVSTRAL=vibepod/devstral:latest vp run devstral
VP_IMAGE_DEVSTRAL=vibepod/devstral:latest vp run vibe # same agent/image as devstral
VP_IMAGE_AUGGIE=vibepod/auggie:latest vp run auggie
VP_IMAGE_COPILOT=vibepod/copilot:latest vp run copilot
VP_IMAGE_CODEX=vibepod/codex:latest vp run codex
Expand Down
29 changes: 26 additions & 3 deletions docs/agents/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ VibePod manages each agent as a Docker container. Credentials and config are per
| `claude` | Anthropic | `vp c` | `vibepod/claude:latest` |
| `gemini` | Google | `vp g` | `vibepod/gemini:latest` |
| `opencode` | OpenAI | `vp o` | `vibepod/opencode:latest` |
| `devstral` | Mistral | `vp d` | `vibepod/devstral:latest` |
| `devstral` (alias: `vibe`) | Mistral | `vp d` | `vibepod/devstral:latest` |
| `auggie` | Augment Code | `vp a` | `vibepod/auggie:latest` |
| `copilot` | GitHub | `vp p` | `vibepod/copilot:latest` |
| `codex` | OpenAI | `vp x` | `vibepod/codex:latest` |

Alias note: `vp run vibe` resolves to `vp run devstral`.

## First run & authentication

Start any agent for the first time with `vp run <agent>`. The container will prompt you to authenticate (browser OAuth, API key entry, or device flow depending on the provider). Once authenticated, credentials are written to the persisted config directory and reused on subsequent runs.
Expand Down Expand Up @@ -71,7 +73,7 @@ agents:

## Image customization workflows

VibePod has a fixed set of supported agent IDs (`claude`, `gemini`, `opencode`, `devstral`, `auggie`, `copilot`, `codex`). Image customization means changing the image used for one of those IDs.
VibePod has a fixed set of supported agent IDs (`claude`, `gemini`, `opencode`, `devstral`, `auggie`, `copilot`, `codex`). The CLI also supports the alias `vibe`, which resolves to `devstral`. Image customization means changing the image used for one of those IDs.

### 1. Extend an existing image for an agent

Expand Down Expand Up @@ -165,6 +167,26 @@ agents:

The `init` commands run on every `vp run` for that agent and must be idempotent.

## IKWID mode (`--ikwid`)

Use `--ikwid` to enable each agent's built-in auto-approval / permission-skip mode when supported.

| Agent | `--ikwid` appended args |
|-------|--------------------------|
| `claude` | `--dangerously-skip-permissions` |
| `gemini` | `--approval-mode=yolo` |
| `devstral` | `--auto-approve` |
| `copilot` | `--yolo` |
| `codex` | `--dangerously-bypass-approvals-and-sandbox` |
| `opencode` | Not supported |
| `auggie` | Not supported |

Example:

```bash
vp run codex --ikwid
```

## Detached mode

Use `-d` / `--detach` to start an agent container in the background without attaching your terminal. The agent process starts immediately inside the container — `-d` only controls whether VibePod attaches your terminal to it.
Expand Down Expand Up @@ -251,10 +273,11 @@ vp run gemini # or: vp g
vp run opencode # or: vp o
```

### Devstral (Mistral)
### Devstral / Vibe (Mistral)

```bash
vp run devstral # or: vp d
vp run vibe # alias of devstral
```

!!! note
Expand Down
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Run `vp config path` to print the exact paths in use, and `vp config show` to pr
version: 1

# Agent to run when no argument is given to `vp run`
# Alias `vibe` resolves to `devstral`.
default_agent: claude

# Pull the latest image before every run (default: true)
Expand Down Expand Up @@ -112,7 +113,7 @@ These variables override the corresponding config keys without editing any file:

| Variable | Config key | Example |
|---|---|---|
| `VP_DEFAULT_AGENT` | `default_agent` | `VP_DEFAULT_AGENT=gemini` |
| `VP_DEFAULT_AGENT` | `default_agent` | `VP_DEFAULT_AGENT=vibe` |
| `VP_AUTO_PULL` | `auto_pull` | `VP_AUTO_PULL=true` |
| `VP_LOG_LEVEL` | `log_level` | `VP_LOG_LEVEL=debug` |
| `VP_NO_COLOR` | `no_color` | `VP_NO_COLOR=true` |
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ VibePod (`vp`) lets you run any supported AI coding agent in an isolated Docker
| `claude` | Anthropic | `vp c` |
| `gemini` | Google | `vp g` |
| `opencode` | OpenAI | `vp o` |
| `devstral` | Mistral | `vp d` |
| `devstral` (alias: `vibe`) | Mistral | `vp d` |
| `auggie` | Augment Code | `vp a` |
| `copilot` | GitHub | `vp p` |
| `codex` | OpenAI | `vp x` |
Expand Down
6 changes: 6 additions & 0 deletions src/vibepod/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ def run(

if ikwid:
if spec.ikwid_args:
if command is None:
try:
command = manager.resolve_launch_command(image=image, command=spec.command)
except DockerClientError as exc:
error(str(exc))
raise typer.Exit(1) from exc
info(f"IKWID mode: appending {spec.ikwid_args} to {selected_agent} command")
command = list(command or []) + spec.ikwid_args
else:
Expand Down
4 changes: 4 additions & 0 deletions src/vibepod/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
"x": "codex",
}

AGENT_ALIASES: dict[str, str] = {
"vibe": "devstral",
}

IMAGE_OVERRIDE_ENV_KEYS: tuple[str, ...] = (
"VP_IMAGE_NAMESPACE",
"VP_IMAGE_CLAUDE",
Expand Down
7 changes: 5 additions & 2 deletions src/vibepod/core/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pathlib import Path
from typing import Any

from vibepod.constants import AGENT_SHORTCUTS, DEFAULT_IMAGES, SUPPORTED_AGENTS
from vibepod.constants import AGENT_ALIASES, AGENT_SHORTCUTS, DEFAULT_IMAGES, SUPPORTED_AGENTS
from vibepod.core.config import get_config_root


Expand Down Expand Up @@ -58,6 +58,7 @@ class AgentSpec:
["env", "HOME=/config", "node", "/usr/local/bin/gemini"],
"/config",
{"HOME": "/config"},
ikwid_args=["--approval-mode=yolo"],
),
"opencode": AgentSpec(
"opencode",
Expand All @@ -78,6 +79,7 @@ class AgentSpec:
{"HOME": "/config", "WORKSPACE_PATH": "/workspace"},
platform="linux/amd64",
run_as_host_user=True,
ikwid_args=["--auto-approve"],
),
"auggie": AgentSpec(
"auggie",
Expand All @@ -96,6 +98,7 @@ class AgentSpec:
["copilot"],
"/config",
{"HOME": "/config"},
ikwid_args=["--yolo"],
),
"codex": AgentSpec(
"codex",
Expand Down Expand Up @@ -124,7 +127,7 @@ def resolve_agent_name(agent: str) -> str | None:
normalized = agent.strip().lower()
if normalized in SUPPORTED_AGENTS:
return normalized
return AGENT_SHORTCUTS.get(normalized)
return AGENT_SHORTCUTS.get(normalized) or AGENT_ALIASES.get(normalized)


def get_agent_shortcut(agent: str) -> str | None:
Expand Down
19 changes: 18 additions & 1 deletion tests/test_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def test_resolve_agent_name_accepts_short_and_full_forms() -> None:
for agent in SUPPORTED_AGENTS:
assert resolve_agent_name(agent) == agent
assert resolve_agent_name(f" {agent.upper()} ") == agent
assert resolve_agent_name("vibe") == "devstral"
assert resolve_agent_name("VIBE") == "devstral"
assert resolve_agent_name("unknown") is None


Expand All @@ -64,13 +66,28 @@ def test_codex_spec_has_ikwid_args() -> None:
assert spec.ikwid_args == ["--dangerously-bypass-approvals-and-sandbox"]


def test_gemini_spec_has_ikwid_args() -> None:
spec = get_agent_spec("gemini")
assert spec.ikwid_args == ["--approval-mode=yolo"]


def test_copilot_spec_has_ikwid_args() -> None:
spec = get_agent_spec("copilot")
assert spec.ikwid_args == ["--yolo"]


def test_devstral_spec_has_ikwid_args() -> None:
spec = get_agent_spec("devstral")
assert spec.ikwid_args == ["--auto-approve"]


def test_gemini_spec_runs_via_node_wrapper() -> None:
spec = get_agent_spec("gemini")
assert spec.command == ["env", "HOME=/config", "node", "/usr/local/bin/gemini"]


def test_unsupported_agents_have_no_ikwid_args() -> None:
for agent in ("gemini", "opencode", "devstral", "auggie", "copilot"):
for agent in ("opencode", "auggie"):
spec = get_agent_spec(agent)
assert spec.ikwid_args is None, f"{agent} should not have ikwid_args"

Expand Down
Loading
Loading