AGENTS.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Emu is a lazygit-inspired TUI for managing Android emulators and iOS simulators, built with Rust. Async-first architecture with trait-based platform abstraction (DeviceManager), centralized state (AppState with Arc<Mutex<>>), and ratatui-based UI.
See docs/ARCHITECTURE.md for full architecture details.
Development Commands
cargo build # Build
cargo run # Run (default: emu binary)
cargo run -- --check # Verify local environment without opening TUI
cargo run -- --debug # Run with debug logging
cargo check # Type check (fast)
cargo clippy --all-targets --all-features -- -D warnings # Lint (CI-level)
cargo fmt # Format
cargo test --bins --tests # Run tests (recommended)
cargo test --features test-utils # Run all tests including integration
Key Files
| File | Role |
|---|---|
src/app/mod.rs | Main event loop shell and app coordination |
src/app/state/mod.rs | AppState, ApiLevelManagementState::is_busy() |
src/managers/android/mod.rs | Android AVD facade and runtime orchestration |
src/managers/android/*.rs | Android helper modules (create/details/discovery/install/lifecycle/parser/sdk/version) |
src/managers/ios/mod.rs | iOS simulator facade and orchestration (macOS only) |
src/managers/ios/*.rs | iOS helper modules (details/discovery/lifecycle/tests) |
src/managers/common.rs | DeviceManager trait |
src/app/api_levels.rs | API level management mode handling and install/uninstall orchestration |
src/app/tests.rs | App orchestration characterization tests and startup fixtures |
src/app/state/ui.rs | UI-facing state enums and confirmation dialog structs |
src/app/state/logs.rs | Log entry state and log-related AppState methods |
src/managers/android/tests.rs | AndroidManager facade and helper module regression tests |
src/managers/ios/tests.rs | IosManager facade and helper module regression tests |
src/ui/render.rs | Three-panel layout rendering and panel composition |
src/ui/dialogs/mod.rs | Modal dialog entrypoint and dialog composition |
src/ui/dialogs/*.rs | Dialog-specific renderers (create/confirm/api-level/notifications) |
src/ui/panels/mod.rs | Panel rendering entrypoint and panel composition helpers |
src/ui/panels/*.rs | Panel-specific renderers (device_lists/details/logs/commands) |
src/models/device_info/mod.rs | Dynamic device info entrypoint, cache access, and public surface |
src/models/device_info/*.rs | Device info helpers (priority/parsing/tests) |
src/constants/ | All constants (NO hardcoded values in source) |
src/models/ | Core data structures |
Code Conventions
String Formatting — CRITICAL
Always use inline variable syntax in format! and all logging macros:
// ✅ Correct
format!("Device {name} created")
log::info!("Starting {identifier}")
// ❌ Wrong — clippy::uninlined_format_args error in CI
format!("Device {} created", name)
log::info!("Starting {}", identifier)
This applies to format!, println!, eprintln!, bail!, anyhow!, log::*, and test assertions.
Constants
All hardcoded values must be defined in src/constants/. Never use magic numbers or strings in source.
Error Handling
anyhowfor propagation,thiserrorfor custom types- Never
.unwrap()or.expect()in user-facing code
Async
- Use
impl Future + Sendfor trait methods Arc<Mutex<>>for shared state; preferArc<AtomicBool>for simple flags- Never use
std::sync::Mutexin async contexts — usetokio::sync::Mutex
Testing
All tests use MockCommandExecutor and MockDeviceManager — no real Android SDK or Xcode needed.
cargo test --features test-utils # Full suite
cargo test -- --nocapture # With output
RUST_TEST_THREADS=1 cargo test ... # Serial (avoids env var races)
Test Infrastructure
tests/support/— shared test foundation (device factories, TestStateBuilder, assertions, contract tests)src/inline#[cfg(test)]— pure logic tests (state, models, validation, constants)tests/integration/— multi-component integration teststests/root — standalone test binaries (android, ios, app, ui)
See docs/TESTING.md for full testing guide.
Current Status
Completed
- ✅ Android AVD full lifecycle (create/start/stop/delete/wipe)
- ✅ iOS simulator basic operations (macOS)
- ✅ Three-panel UI with device details
- ✅ Real-time logcat streaming with filtering
- ✅ API level management with install progress UI
- ✅
ApiLevelManagementState::is_busy()— centralized busy-check - ✅ Install progress: 100% shown on completion, stale callbacks prevented
- ✅ Ultra-responsive input (8ms polling, no debouncing)
- ✅ Background loading, smart caching, incremental refresh
- ✅ Comprehensive test suite (720+ tests, CI/CD ready)
- ✅ PostToolUse hook (
.claude/settings.json):cargo checkafter.rsedits
Known Issues
- Android state detection: occasional inaccuracy in AVD-to-emulator-serial mapping
- iOS device details: limited info compared to Android
- Performance tests: thresholds are calibrated for CI runners, but environment-sensitive benchmarks should still be treated as signal rather than strict micro-benchmarks
Hooks & CI
- pre-commit:
cargo clippy --all-targets --all-features -- -D warnings+cargo fmt - pre-push:
RUST_TEST_THREADS=1 cargo test --bins --tests --features test-utils - PostToolUse (
.claude/settings.json):cargo checkafter.rsfile edits - CI: Build → Check & Lint → Test (ubuntu + macos)