Internal guide for engineers contributing to VibeCody. Covers build procedures, testing, debugging, code organization, and common development workflows.
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Rust | stable (1.77+) | Backend, shared crates, CLI |
| Node.js | LTS 18+ | VibeUI frontend |
| pnpm/npm | latest | Frontend package management |
| Tauri CLI | 2.x | Desktop app builds |
| Docker | 20+ | Container sandbox, on-prem deployment |
| sqlite3 | 3.x | Database panel (optional) |
# Verify toolchain
rustup show && node --version && cargo tauri --version
Repository Structure
vibecody/
├── Cargo.toml # Workspace root (9 members)
├── vibecli/vibecli-cli/ # VibeCLI binary crate
│ ├── src/main.rs # CLI entry, REPL loop, 126 slash commands
│ ├── src/tool_executor.rs # Agent tool execution (file I/O, bash, search)
│ ├── src/project_init.rs # Smart project auto-detection
│ ├── src/gateway.rs # 18-platform messaging gateway + channel daemon
│ ├── src/channel_daemon.rs # Always-on daemon with automation routing
│ ├── src/branch_agent.rs # Agent-per-branch workflow
│ ├── src/spec_pipeline.rs # EARS spec-driven development
│ ├── src/vm_orchestrator.rs # Parallel VM agent orchestration
│ └── skills/ # 599 skill files
├── vibeui/
│ ├── src/ # React + TypeScript frontend
│ │ ├── App.tsx # Root component, keyboard shortcuts
│ │ └── components/ # 235+ panel components (plus 39 composites)
│ ├── src-tauri/src/
│ │ ├── lib.rs # Tauri command registration (1,045+ commands)
│ │ ├── commands.rs # All Tauri command implementations
│ │ └── agent_executor.rs # Agent tool execution for VibeUI
│ └── crates/
│ ├── vibe-ai/ # AIProvider trait, agent loop, 22 providers
│ ├── vibe-core/ # Text buffer, filesystem, git, search
│ ├── vibe-lsp/ # LSP client
│ ├── vibe-extensions/ # WASM extension system
│ └── vibe-collab/ # CRDT collaboration
├── docs/ # Jekyll documentation site
├── jetbrains-plugin/ # IntelliJ/WebStorm plugin
└── neovim-plugin/ # Neovim integration
Build Commands
Quick Reference
# Check everything compiles (fastest feedback loop)
cargo check --workspace --exclude vibe-collab
# Build CLI binary (release)
cargo build --release -p vibecli
# Run all tests
cargo test --workspace --exclude vibe-collab
# Run tests for a specific crate
cargo test -p vibecli
cargo test -p vibe-ai
cargo test -p vibe-core
# Run tests matching a pattern
cargo test -p vibecli -- project_init
cargo test -p vibecli -- channel_daemon::tests
# VibeUI development mode
cd vibeui && npm install && npm run tauri:dev
# Clippy (linting)
cargo clippy --workspace --exclude vibe-collab -- -W clippy::all
# Format check
cargo fmt --all -- --check
Linux Notes
On Linux, the npm rustup package can shadow the system cargo binary. If you see unexpected build failures:
# Check which cargo is being used
which cargo
# Should be ~/.cargo/bin/cargo, NOT node_modules/.bin/cargo
See linux-dev-setup.md for full Linux environment setup.
VibeUI Development
cd vibeui
npm install # Install frontend dependencies
npm run tauri:dev # Start Tauri dev server (use tauri:dev not tauri dev on Linux)
npm run lint # ESLint
npm run typecheck # TypeScript type checking
Testing
Test Organization
| Crate | Tests | Focus |
|---|---|---|
| vibecli | ~5,600+ | CLI commands, tool executor, providers, security, all feature modules |
| vibe-ai | ~1,020+ | Provider implementations, agent loop, circuit breaker, tracing |
| vibe-core | ~290+ | Text buffer, filesystem, git, search, embeddings |
| vibe-ui | ~230+ | Tauri commands, agent executor, panel components |
| vibe-extensions | ~46 | WASM extension loading, manifest parsing |
| vibe-lsp | ~34 | LSP client protocol |
Running Tests
# Full workspace (~10,535 tests)
cargo test --workspace --exclude vibe-collab
# Single crate with output
cargo test -p vibecli -- --nocapture
# Single test function
cargo test -p vibecli -- project_init::tests::scan_rust_project --nocapture
# Tests matching a keyword
cargo test -p vibecli -- security
cargo test -p vibe-core -- safe_command
Writing Tests
Tests live in #[cfg(test)] mod tests { } blocks at the bottom of each module. Follow these conventions:
#[cfg(test)]
mod tests {
use super::*;
// Helper functions at the top
fn test_fixture() -> MyStruct {
MyStruct::default()
}
// Test names: {function_under_test}_{scenario}
#[test]
fn parse_query_empty_string_returns_none() {
assert!(parse_query("").is_none());
}
// Async tests use #[tokio::test]
#[tokio::test]
async fn fetch_url_blocks_internal_ips() {
let result = fetch("http://169.254.169.254").await;
assert!(result.is_err());
}
}
Architecture Patterns
AI Provider Trait
All 22 AI providers implement AIProvider (in vibe-ai/src/provider.rs):
#[async_trait]
pub trait AIProvider: Send + Sync {
fn name(&self) -> &str;
fn is_available(&self) -> bool;
async fn chat(&self, messages: &[Message], context: Option<String>) -> Result<String>;
async fn stream_chat(&self, messages: &[Message]) -> Result<CompletionStream>;
async fn complete(&self, context: &CodeContext) -> Result<CompletionResponse>;
async fn stream_complete(&self, context: &CodeContext) -> Result<CompletionStream>;
}
To add a new provider:
- Create
vibeui/crates/vibe-ai/src/providers/my_provider.rs - Register in
providers.rs:pub mod my_provider;+pub use my_provider::MyProvider; - Add config handling in
vibecli/vibecli-cli/src/main.rscreate_provider()function - Add to
Configstruct invibecli/vibecli-cli/src/config.rs
Agent Loop
The agent loop (vibe-ai/src/agent.rs) follows a plan-act-observe cycle:
User task → System prompt (tools + context) → LLM stream
→ Parse tool calls → Check hooks/approval → Execute tool
→ Feed result back → Repeat until task_complete
Key components:
- CircuitBreaker — Detects stalls, spins, degradation (4 health states)
- Hooks — Pre/post tool execution interception (JSON stdin/stdout)
- Context pruning — Keeps within 80K token budget
- Think tool — Free reasoning step (no side effects, no step count)
Tool Execution
Two executor implementations:
vibecli/src/tool_executor.rs— CLI executor with sandbox support, SSRF validation, command blocklistvibeui/src-tauri/src/agent_executor.rs— Tauri executor with workspace-boundary path validation, command blocklist, 120s timeout
Tauri Commands
VibeUI exposes 1,045+ Tauri commands. Each is a #[tauri::command] function in commands.rs:
#[tauri::command]
pub async fn my_command(param: String) -> Result<ResponseType, String> {
// Implementation
}
Register in lib.rs:
tauri::generate_handler![
commands::my_command,
// ...
]
Security Checklist
Before submitting code that touches these areas, verify:
File I/O
- All user-supplied paths go through
safe_resolve_path()orTauriToolExecutor::resolve() - No absolute paths outside workspace are allowed
- Symlink resolution via
canonicalize()prevents traversal
Shell Execution
- Commands pass through the blocklist (
is_blocked_command/is_safe_command) - Timeout applied (120s default for agent bash, 300s for scripts)
- User-visible commands from AI responses are filtered before execution
Network Requests
- URLs validated with
validate_url_for_ssrf()— blocks loopback, RFC 1918, link-local, metadata - Only
http://andhttps://schemes allowed - No
file:///access
Secrets
- API keys stored with
chmod 0o600file permissions - Trace files run through
redact_secrets()before writing - Error messages don’t expose API keys or internal paths
- Test fixtures use clearly fake keys (e.g.,
sk-abcdefghij1234567890)
SQL
- SQLite dot-commands blocked (
.shell,.system,.import,.load) ATTACH DATABASEblocked- Path validation before opening database files
Dependencies
- No
unsafeblocks (none in the codebase currently) - No
danger_accept_invalid_certs(true) - Cryptographic operations use
hmac/sha2crates, not hand-rolled implementations
Adding a REPL Command
- Add the command string to
vibecli/vibecli-cli/src/repl.rsCOMMANDSarray - Add the match arm in
main.rs(search for the_ =>fallthrough near line 6100+) - Implement the handler (typically calling into a dedicated module)
Example:
// In repl.rs COMMANDS array:
"/mycommand",
// In main.rs match block:
"/mycommand" => {
let sub = args.trim().split_whitespace().next().unwrap_or("help");
match sub {
"list" => { /* implementation */ }
_ => println!("Usage: /mycommand [list|create|delete]\n"),
}
}
Adding a VibeUI Panel
- Create
vibeui/src/components/MyPanel.tsx - Follow the tab-bar pattern used by other panels:
export function MyPanel({ workspacePath }: { workspacePath?: string | null }) {
const [tab, setTab] = useState<"overview" | "detail">("overview");
// ... panel implementation
}
- Import and add to the AI panel tab list in
App.tsx - If the panel needs Rust data, add a
#[tauri::command]and register it
Debugging
VibeCLI
# Verbose logging
RUST_LOG=debug vibecli --provider ollama
# Trace agent steps
vibecli --agent "task" --json 2>&1 | jq .
# Inspect trace files
ls ~/.vibecli/traces/
cat ~/.vibecli/traces/<session-id>.jsonl | jq .
VibeUI
# Open with DevTools
cd vibeui && npm run tauri:dev
# Press F12 or Cmd+Opt+I for WebView DevTools
# Rust backend logs
RUST_LOG=debug npm run tauri:dev
Common Issues
| Issue | Fix |
|---|---|
cargo check hangs |
Check for build directory lock: ls -la target/.cargo-lock |
| Tauri build fails on Linux | Install system deps: sudo apt install libwebkit2gtk-4.1-dev libappindicator3-dev |
vibe-collab won’t compile |
Exclude it: --exclude vibe-collab (requires specific CRDT dependencies) |
| Tests fail with “provider not available” | These are integration tests needing API keys — unit tests should all pass |
npm rustup shadows cargo |
Use ~/.cargo/bin/cargo directly or uninstall the npm rustup package |
Release Process
# 1. Update version in Cargo.toml
# 2. Update CHANGELOG.md
# 3. Build release binaries
cargo build --release -p vibecli
# 4. Cross-platform builds via CI
# See .github/workflows/release.yml
# 5. Docker image
docker build -t vibecody/vibecli:latest .
# 6. Install script verification
./install.sh # SHA-256 verified download
Key Module Reference
| Module | Lines | Tests | Purpose |
|---|---|---|---|
main.rs |
~7,800 | — | CLI entry, REPL loop, 126 slash command handlers |
tool_executor.rs |
~1,800 | 28+ | Agent tool execution with sandbox |
agent.rs |
~1,500 | 40+ | Agent loop, circuit breaker, system prompt |
tools.rs |
~500 | 20+ | Tool definitions, XML parsing, think tool |
project_init.rs |
~1,300 | 17 | Smart project auto-detection and caching |
gateway.rs |
~2,200 | 18+ | 18-platform gateway + channel daemon |
channel_daemon.rs |
~1,300 | 47 | Always-on daemon, event routing, sessions |
branch_agent.rs |
~1,500 | 56 | Agent-per-branch, auto-PR |
spec_pipeline.rs |
~1,700 | 64 | EARS spec-driven development |
vm_orchestrator.rs |
~1,300 | 59 | Parallel VM agent execution |
commands.rs |
~30,000 | 227+ | All Tauri command implementations |
agent_executor.rs |
~350 | 12 | VibeUI agent tool executor |
Performance Notes
agent.rs:String::with_capacity(8192)pre-allocates for LLM responsesembeddings.rs: O(n) update, fused cosine similarity, shared HTTP client viaOnceLocksearch.rs: CachedWalkDirmetadata for fast file enumerationtool_executor.rs: Async I/O viatokio::process::Command, O(n) HTML entity decode- Context pruning keeps agent within 80K tokens (configurable via
with_context_limit()) - Release profile: LTO enabled, symbols stripped,
panic=abort,opt-level=s