feat: Weevil v1.0.0-beta1 - FTC Project Generator

Cross-platform tool for generating clean, testable FTC robot projects
without editing the SDK installation.

Features:
- Standalone project generation with proper separation from SDK
- Per-project SDK configuration via .weevil.toml
- Local unit testing support (no robot required)
- Cross-platform build/deploy scripts (Linux/macOS/Windows)
- Project upgrade system preserving user code
- Configuration management commands
- Comprehensive test suite (11 passing tests)
- Zero-warning builds

Architecture:
- Pure Rust implementation with embedded Gradle wrapper
- Projects use deployToSDK task to copy code to FTC SDK TeamCode
- Git-ready projects with automatic initialization
- USB and WiFi deployment with auto-detection

Commands:
- weevil new <name> - Create new project
- weevil upgrade <path> - Update project infrastructure
- weevil config <path> - View/modify project configuration
- weevil sdk status/install/update - Manage SDKs

Addresses the core problem: FTC's SDK structure forces students to
edit framework internals instead of separating concerns like industry
standard practices. Weevil enables proper software engineering workflows
for robotics education.
This commit is contained in:
Eric Ratliff
2026-01-24 15:20:18 -06:00
commit 70a1acc2a1
35 changed files with 3558 additions and 0 deletions

127
src/main.rs Normal file
View File

@@ -0,0 +1,127 @@
use clap::{Parser, Subcommand};
use colored::*;
use anyhow::Result;
mod commands;
mod sdk;
mod project;
mod templates;
#[derive(Parser)]
#[command(name = "weevil")]
#[command(author = "Eric Barch <eric@intrepidfusion.com>")]
#[command(version = "1.0.0")]
#[command(about = "FTC robotics project generator - bores into complexity, emerges with clean code", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Create a new FTC robot project
New {
/// Name of the robot project
name: String,
/// Path to FTC SDK (optional, will auto-detect or download)
#[arg(long)]
ftc_sdk: Option<String>,
/// Path to Android SDK (optional, will auto-detect or download)
#[arg(long)]
android_sdk: Option<String>,
},
/// Upgrade an existing project to the latest generator version
Upgrade {
/// Path to the project directory
path: String,
},
/// Build and deploy project to Control Hub
Deploy {
/// Path to the project directory
path: String,
/// Force USB connection
#[arg(long)]
usb: bool,
/// Force WiFi connection
#[arg(long)]
wifi: bool,
/// Custom IP address
#[arg(short, long)]
ip: Option<String>,
},
/// Manage SDKs (FTC and Android)
Sdk {
#[command(subcommand)]
command: SdkCommands,
},
/// Show or update project configuration
Config {
/// Path to the project directory
path: String,
/// Set FTC SDK path for this project
#[arg(long, value_name = "PATH")]
set_sdk: Option<String>,
},
}
#[derive(Subcommand)]
enum SdkCommands {
/// Install required SDKs
Install,
/// Show SDK status and locations
Status,
/// Update SDKs to latest versions
Update,
}
fn main() -> Result<()> {
let cli = Cli::parse();
print_banner();
match cli.command {
Commands::New { name, ftc_sdk, android_sdk } => {
commands::new::create_project(&name, ftc_sdk.as_deref(), android_sdk.as_deref())
}
Commands::Upgrade { path } => {
commands::upgrade::upgrade_project(&path)
}
Commands::Deploy { path, usb, wifi, ip } => {
commands::deploy::deploy_project(&path, usb, wifi, ip.as_deref())
}
Commands::Sdk { command } => {
match command {
SdkCommands::Install => commands::sdk::install_sdks(),
SdkCommands::Status => commands::sdk::show_status(),
SdkCommands::Update => commands::sdk::update_sdks(),
}
}
Commands::Config { path, set_sdk } => {
if let Some(sdk_path) = set_sdk {
commands::config::set_sdk(&path, &sdk_path)
} else {
commands::config::show_config(&path)
}
}
}
}
fn print_banner() {
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
println!("{}", " 🪲 Weevil - FTC Project Generator v1.0.0".bright_cyan().bold());
println!("{}", " Nexus Workshops LLC".bright_cyan());
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
println!();
}