.claude/skills/cuyamaca-installer/SKILL.md
Build the native installers (macOS .dmg, Windows .exe/.msi) and the in-app first-run dependency installation wizard for Cuyamaca, the Tauri v2 Arduino control app. Use this skill whenever the user asks to build installers, set up distribution, create a .dmg or .msi, implement the first-run experience, auto-install Ollama or arduino-cli, build a setup wizard, handle dependency detection, manage child processes for Ollama, or anything related to packaging, bundling, or distributing Cuyamaca. Also trigger for "first launch", "onboarding flow", "dependency check", "auto-install", "process lifecycle", or "app distribution". This skill covers both the Tauri bundler configuration for producing native installers AND the Rust/React code for the in-app dependency wizard that runs on first launch.
npx skillsauth add yuyanghu06/cuyamaca cuyamaca-installerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill covers two tightly coupled concerns:
.dmg (macOS) and .exe/.msi (Windows) installers.Read the project's CLAUDE.md before implementing. The architecture, process lifecycle, and platform requirements there are authoritative.
references/ci-signing.md — GitHub Actions release workflow and code signing setup for macOS and Windows. Read this when setting up CI/CD or preparing for production distribution.references/process-manager.md — Full Rust implementation for managing Ollama as a child process (start, health-check, graceful shutdown, crash recovery) and invoking arduino-cli on demand. Read this when implementing the process lifecycle.Tauri v2's built-in bundler produces platform-native installers. No electron-builder or NSIS scripts needed. Run npm run tauri build and it outputs the correct format for the current OS in src-tauri/target/release/bundle/.
You can only build for the OS you're on. Use CI (GitHub Actions) for cross-platform releases — see references/ci-signing.md.
In src-tauri/tauri.conf.json, configure the bundle section:
{
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.cuyamaca.app",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/[email protected]",
"icons/icon.icns",
"icons/icon.ico"
],
"macOS": {
"minimumSystemVersion": "10.15",
"dmg": {
"appPosition": { "x": 180, "y": 170 },
"applicationFolderPosition": { "x": 480, "y": 170 },
"windowSize": { "width": 660, "height": 400 }
},
"signingIdentity": null
},
"windows": {
"nsis": {
"installMode": "currentUser"
}
}
}
}
Key decisions: identifier is a stable reverse-domain string (changing it later breaks updates). minimumSystemVersion is "10.15" for Tauri v2. installMode: "currentUser" avoids requiring admin on Windows. signingIdentity: null for development — see references/ci-signing.md for production signing.
npm run tauri icon path/to/source-icon.png
Source image: 1024x1024+ PNG with transparency. This generates .icns, .ico, and all required PNG sizes.
macOS: npm run tauri build → outputs .dmg in src-tauri/target/release/bundle/dmg/
Windows: npm run tauri build → outputs .exe (NSIS) and .msi in src-tauri/target/release/bundle/nsis/ and msi/
App launches → Rust checks deps → all healthy? → main app
→ any missing? → wizard view
│
user clicks Install → Rust downloads + installs
│
all healthy → wizard dismisses → main app
The frontend renders progress and status. The Rust backend does all detection, downloading, installation, and validation. The frontend never downloads files or checks paths.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyStatus {
pub ollama: DepState,
pub arduino_cli: DepState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum DepState {
Missing,
Unhealthy { reason: String },
Installing { progress: f32, message: String },
Ready { version: String, path: String },
}
Detection is platform-specific. For each dependency, the Rust backend: (1) checks PATH, (2) checks common install locations, (3) validates with a version command.
Ollama:
which ollama (macOS) / where ollama (Windows)/usr/local/bin/ollama, /opt/homebrew/bin/ollama (macOS); %LOCALAPPDATA%\Programs\Ollama\ollama.exe, C:\Program Files\Ollama\ollama.exe (Windows)ollama --version + optional health check at http://localhost:11434/arduino-cli:
which arduino-cli / where arduino-cli/usr/local/bin/arduino-cli, /opt/homebrew/bin/arduino-cli (macOS); C:\Program Files\Arduino CLI\arduino-cli.exe (Windows)arduino-cli versionIf found and version check passes → DepState::Ready. If binary found but version fails → DepState::Unhealthy. If not found anywhere → DepState::Missing.
Always use direct binary downloads. Never use Homebrew, winget, or any package manager — the user may not have them.
| Dependency | macOS | Windows |
|---|---|---|
| Ollama | Download .dmg from ollama.com, mount with hdiutil, copy Ollama.app to /Applications/ | Download .exe installer from ollama.com, run silently (/S flag) |
| arduino-cli | Download tarball from arduino.cc, extract to app data directory | Download zip from arduino.cc, extract to app data directory |
arduino-cli is a single binary — no installer needed. Extract it into the Tauri app data directory (app.path().app_data_dir()) so it doesn't pollute the system.
Use reqwest streaming + Tauri Channels to report progress:
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
pub enum InstallEvent {
Progress { percent: f32, message: String },
Extracting { message: String },
Validating,
Complete { version: String, path: String },
Failed { error: String },
}
#[tauri::command]
pub async fn install_dependency(
dep: String,
on_event: Channel<InstallEvent>,
app: tauri::AppHandle,
) -> Result<(), String> {
let data_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
match dep.as_str() {
"ollama" => install_ollama(&data_dir, &on_event).await,
"arduino-cli" => install_arduino_cli(&data_dir, &on_event).await,
_ => Err(format!("Unknown dependency: {}", dep)),
}
}
The download function streams bytes, tracks progress against content_length, and sends InstallEvent::Progress through the channel. After download, send Extracting, then Validating, then Complete or Failed.
Extraction crates:
.tar.gz (macOS): flate2 + tar.zip (Windows): zip.dmg (macOS Ollama): shell out to hdiutil attach / hdiutil detach.exe (Windows Ollama): run installer with /S silent flagAfter installation, run the version command to confirm the binary works. If it fails, return InstallEvent::Failed with a descriptive error. If it succeeds, return InstallEvent::Complete with the version string and binary path.
Three commands for the wizard:
/// Check all dependencies, return their status
#[tauri::command]
pub async fn check_dependencies(app: tauri::AppHandle) -> Result<DependencyStatus, String>
/// Install a specific dependency with progress streaming
#[tauri::command]
pub async fn install_dependency(dep: String, on_event: Channel<InstallEvent>, app: tauri::AppHandle) -> Result<(), String>
/// User chose to skip — mark wizard complete, load main app with degraded indicators
#[tauri::command]
pub async fn skip_dependency_setup() -> Result<(), String>
The wizard renders when check_dependencies reports any Missing state. It follows the project's design language from CLAUDE.md.
Layout:
┌─────────────────────────────────────────┐
│ [App icon / wordmark] │
│ Welcome to Cuyamaca │
│ │
│ ┌─────────────────────────────┐ │
│ │ ✓ Ollama Ready │ │
│ │ v0.3.14 │ │
│ ├─────────────────────────────┤ │
│ │ ✕ arduino-cli Missing │ │
│ │ [Install] │ │
│ └─────────────────────────────┘ │
│ │
│ [ Skip — I'll set up manually ] │
└─────────────────────────────────────────┘
Each dependency row has five states:
check_dependencies is in flight)InstallEvent::Progress, status text, button disabledFlow:
check_dependencies. Show Checking state for each row.install_dependency with a Tauri Channel. Progress streams in.Persistence: After wizard completes (all Ready or Skip), store a flag in a config file in the app data directory so it doesn't re-appear on next launch:
{ "setup_complete": true, "ollama_path": "...", "arduino_cli_path": "..." }
On subsequent launches, still run check_dependencies silently. If a dep disappears, show degraded sidebar indicators — don't re-launch the wizard. The wizard is first-run only.
After installation/detection, the app manages Ollama as a child process (start on launch, stop on exit). See references/process-manager.md for the full implementation including the ProcessManager struct, Tauri lifecycle hooks, health polling, and crash recovery.
arduino-cli is not a long-running process. It's invoked on demand for compile + flash.
npm run tauri build produces .dmg on macOS and .exe/.msi on Windowssrc-tauri/src/
├── commands/
│ └── setup.rs # check_dependencies, install_dependency, skip_dependency_setup
├── services/
│ ├── process_manager.rs # Ollama child process lifecycle
│ └── dependency.rs # Detection + installation logic
src/
├── views/
│ └── SetupWizard.tsx # First-run wizard UI
├── components/
│ └── DependencyRow.tsx # Individual dep status/install row
development
Build the Settings view and apply final polish to Cuyamaca — model configuration UI, API key management, process health monitoring, accessibility improvements, responsive refinements, and overall UX tightening. Use this skill whenever the user wants to build the settings view, configure model providers in the UI, add API key entry, polish the app's responsiveness, improve accessibility, refine animations, add keyboard navigation, or references "phase 8", "settings", "settings view", "API key management", "model configuration", "accessibility", "polish", "keyboard navigation", or "responsive refinement". Also trigger when the user asks about WCAG compliance for glass effects, reduce transparency mode, or the settings UI for Cuyamaca. This skill assumes Phase 7 is complete (runtime agent loop, all core functionality working).
tools
Build serial communication, structured output parsing, sensor state management, and sensor visualization rendering for Cuyamaca. Use this skill whenever the user wants to implement serial port reading/writing, parse structured sensor output, build the sensor state panel, render sensor visualization images, manage the serial connection lifecycle, or references "phase 6", "serial communication", "serial port", "sensor parsing", "sensor state", "sensor visualization", "structured output", "serial monitor", or "serial reader". Also trigger when the user asks about the SENSOR_ID:VALUE protocol, concurrent serial read/write, sensor image rendering, or real-time state updates. This skill assumes Phase 5 is complete (arduino-cli integration, compile and flash working).
testing
Scaffold a Tauri v2 desktop app for the Cuyamaca project — an Arduino robotics controller with natural language control. Use this skill whenever the user wants to initialize the Cuyamaca project, set up the Tauri v2 scaffold, create the base layout and warm-white liquid glass UI theme, verify the IPC bridge, or references "phase 1", "scaffold", "project setup", "initialize Cuyamaca", "create the app skeleton", or "base layout". Also trigger when the user asks about Cuyamaca's three-panel layout, the warm-white glass design language, or setting up the Tauri project structure from scratch.
tools
Build the runtime agent loop for Cuyamaca — the agentic control loop where the runtime model reads sensor context, decides tool calls, writes serial commands, and iterates until the user stops it. Use this skill whenever the user wants to implement the runtime window, build the agent loop, assemble multimodal context for the runtime model, implement tool call dispatch via serial, add the kill button, or references "phase 7", "runtime agent", "agent loop", "runtime window", "runtime model", "tool calling", "kill button", "agentic loop", "multimodal context", or "control loop". Also trigger when the user asks about feeding sensor data to a vision model, executing tool calls as serial commands, or the observe-decide-act cycle. This skill assumes Phase 6 is complete (serial communication, sensor parsing, sensor visualization).