This guide covers development setup, architecture, and testing for the claude-vm Rust implementation.
Run the setup script to install all dependencies:
./bin/setupThis will install:
- Rust toolchain (rustc, cargo)
- Lima VM (macOS and Linux)
- Development tools (clippy, rustfmt)
- Build the project and run tests
cargo build --releaseThe binary will be at target/release/claude-vm.
Claude-vm automatically distinguishes between development and release builds to enable safe parallel usage:
Development builds include additional version information and use separate VM templates:
Version String:
- Includes git commit hash:
0.3.0-dev+a1b2c3d4 - Shows
.dirtysuffix if working tree has uncommitted changes:0.3.0-dev+a1b2c3d4.dirty - Falls back to
0.3.0-dev+unknownif git is unavailable
Template Names:
- Include
-devsuffix:claude-tpl_project_hash-dev - Isolated from release templates
Example:
cargo build
./target/debug/claude-vm --version
# Output: claude-vm 0.3.0-dev+a1b2c3d4.dirty
./target/debug/claude-vm setup --all
# Creates: claude-tpl_my-project_12345678-devRelease builds use clean version strings and standard template names:
Version String:
- Clean semver version:
0.3.0 - No git metadata
Template Names:
- Standard format:
claude-tpl_project_hash - Isolated from dev templates
Example:
cargo build --release
./target/release/claude-vm --version
# Output: claude-vm 0.3.0
./target/release/claude-vm setup --all
# Creates: claude-tpl_my-project_12345678This separation allows you to:
- Test development builds without affecting release templates
- Quickly identify which version is running
- Track which commit a binary was built from
- Run both dev and release builds in parallel without conflicts
After switching between dev and release builds, you may accumulate multiple templates:
# List all templates
claude-vm list
# Clean current project's template (dev or release depending on which binary you run)
claude-vm clean
# Clean all templates (both dev and release)
claude-vm clean-allWhen developing and testing claude-vm, you may want to skip the Claude Code agent installation to speed up template creation. This is especially useful when testing VM configuration, capabilities, or other features that don't require the agent itself.
The --no-agent-install flag is available only in debug builds (compiled with cargo build):
# Create template without installing Claude Code agent (debug builds only)
./target/debug/claude-vm setup --all --no-agent-installThis flag:
- Skips Claude Code agent installation
- Skips agent authentication
- Skips MCP server configuration
- Significantly reduces setup time for development testing
- Only available in debug builds - removed from release builds via conditional compilation
- Not shown in release build help output
- Release builds will reject the flag with an error if attempted
Note: Templates created with --no-agent-install will not have the Claude Code agent available. This flag should only be used during development when testing aspects of claude-vm that don't require the agent itself.
claude-vm-rust/
├── src/
│ ├── main.rs # Entry point
│ ├── lib.rs # Library root
│ ├── cli.rs # CLI parsing
│ ├── config.rs # Configuration system
│ ├── error.rs # Error types
│ ├── project.rs # Project detection
│ ├── vm/ # VM management
│ │ ├── limactl.rs # Lima wrapper
│ │ ├── mount.rs # Mount computation
│ │ ├── session.rs # RAII sessions
│ │ └── template.rs # Template operations
│ ├── commands/ # Command implementations
│ └── scripts/ # Embedded install scripts
└── tests/ # Integration tests
Detects the project root and generates a unique template name. Handles git worktrees by using git rev-parse --git-common-dir.
Implements configuration precedence: CLI > Env > Project > Global > Defaults. Supports TOML configuration files with validation.
RAII-based VM lifecycle management using Rust's Drop trait. Ensures cleanup even on panic or error paths.
Subprocess wrapper for all Lima operations. Provides type-safe interface for creating, cloning, starting, stopping, and deleting VMs.
Implements Docker-like entrypoint pattern for runtime scripts. Includes shell injection protection and progress indicators.
The project uses GitHub Actions for continuous integration. On every push and pull request:
- Tests run on Ubuntu and macOS
- Code is checked with clippy
- Formatting is verified with rustfmt
- Release binaries are built and uploaded as artifacts
See .github/workflows/test.yml for the full CI configuration.
Run all Rust tests:
cargo testRun integration tests:
cargo test --test integration_testsRun tests with verbose output:
cargo test -- --nocaptureRun with coverage reporting (requires cargo-tarpaulin):
cargo install cargo-tarpaulin
cargo tarpaulin --out HtmlFormat code with rustfmt:
cargo fmtCheck formatting without making changes:
cargo fmt -- --checkRun clippy for lint checks:
cargo clippy -- -D warningsRun clippy on all targets:
cargo clippy --all-targets --all-features -- -D warnings- clap: CLI parsing
- toml: Configuration file parsing
- serde: Serialization/deserialization
- anyhow: Error handling
- thiserror: Error type definitions
- md5: Project ID hashing
- which: Executable detection
- Rust 1.70+ (edition 2021)
- Lima VM installed
- macOS with Apple Silicon (for Rosetta support)
- Add tests first (TDD approach)
- Implement the feature
- Update documentation
- Run all tests and linting
- Submit a pull request
- All tests pass
- Code formatted with rustfmt
- No clippy warnings
- Documentation updated
- Error handling implemented
- Security considerations addressed
Run with --verbose to see detailed Lima logs:
claude-vm --verbose shellEnable backtrace for panics:
RUST_BACKTRACE=1 claude-vm setupFull backtrace with all frames:
RUST_BACKTRACE=full claude-vm setupCheck Lima logs directly:
limactl list
limactl shell vm-name
tail -f ~/.lima/vm-name/ha.stdout.logThe project includes an automated release script that handles version bumping, testing, and GitHub release creation.
# Show help and usage information
./bin/release --help
# Bump patch version (0.1.0 -> 0.1.1) - for bug fixes
./bin/release patch
# Bump minor version (0.1.0 -> 0.2.0) - for new features
./bin/release minor
# Bump major version (0.1.0 -> 1.0.0) - for breaking changes
./bin/release major
# Set specific version
./bin/release 0.2.0
# Interactive mode with prompts
./bin/release- Validates the version format (semantic versioning)
- Checks that the git working tree is clean
- Verifies you're on the main branch (with confirmation if not)
- Checks that the version tag doesn't already exist
- Runs all tests:
cargo test --all - Runs clippy:
cargo clippy -- -D warnings - Updates the version in
Cargo.toml - Updates
Cargo.lock - Creates a git commit with the version bump
- Creates an annotated git tag (e.g.,
v0.2.0) - Pushes the commit and tag to the remote repository
- Triggers the GitHub Actions release workflow
- patch: Increments the patch version (0.1.0 -> 0.1.1) - for bug fixes
- minor: Increments the minor version and resets patch (0.1.0 -> 0.2.0) - for new features
- major: Increments the major version and resets minor/patch (0.1.0 -> 1.0.0) - for breaking changes
When the release tag is pushed, .github/workflows/release.yml automatically:
- Builds binaries for all supported platforms:
- macOS x86_64 (Intel)
- macOS aarch64 (Apple Silicon)
- Linux x86_64
- Linux aarch64 (ARM64)
- Creates a GitHub release with the version tag
- Uploads all platform binaries as release assets
- Generates installation instructions in the release notes
- Clean git working tree (no uncommitted changes)
- All tests passing
- No clippy warnings
- Git remote configured
- Version must follow semantic versioning (e.g., 0.2.0, 1.0.0, 2.1.3)
If you need to release manually without the script:
- Update version in
Cargo.toml - Update CHANGELOG.md
- Run all tests:
cargo test --all - Run clippy:
cargo clippy -- -D warnings - Build release binary:
cargo build --release - Commit changes:
git commit -am "Release version X.Y.Z" - Tag release:
git tag -a vX.Y.Z -m "Release version X.Y.Z" - Push commit and tag:
git push origin main && git push origin vX.Y.Z