.claude/skills/cuyamaca-project-manifest/SKILL.md
Build the project system and manifest editor for Cuyamaca — project CRUD, the manifest data model, the parts editor UI with component picker, and pin assignment editing. Use this skill whenever the user wants to create the project system, build the manifest editor, implement the parts panel, add the component picker, set up the hardware definition workflow, or references "phase 3", "project system", "manifest", "parts editor", "component picker", "hardware definition", "pin assignment", or "add component". Also trigger when the user asks about the manifest JSON schema, component types, project file structure, or how to define board configurations. This skill assumes Phase 2 is complete (LLM abstraction layer with provider trait and model slots).
npx skillsauth add yuyanghu06/cuyamaca cuyamaca-project-manifestInstall 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 builds the project system and hardware definition workflow. Users create projects, define their board and components in a manifest, and see everything reflected in the parts panel. The manifest is the ground truth that all later phases (code generation, tool synthesis, runtime control) build from.
manifest.json, sketch.ino, tools.json, history/)cuyamaca-projects/ directory// src-tauri/src/models/manifest.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Manifest {
pub project: String,
pub board: String, // e.g., "arduino:avr:uno"
pub serial_port: String, // e.g., "/dev/cu.usbmodem14201" or "COM3"
pub baud_rate: u32, // default 115200
pub components: Vec<Component>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Component {
pub id: String, // snake_case identifier, e.g., "motor_left"
pub component_type: String, // e.g., "dc_motor", "ultrasonic", "servo"
pub pins: HashMap<String, u8>, // pin name → pin number
pub label: String, // human-readable name
#[serde(skip_serializing_if = "Option::is_none")]
pub subtype: Option<String>, // e.g., "esp32-cam"
#[serde(skip_serializing_if = "Option::is_none")]
pub connection: Option<String>, // e.g., "wifi", "i2c", "serial"
#[serde(skip_serializing_if = "Option::is_none")]
pub resolution: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
}
// src-tauri/src/models/project.rs
pub struct Project {
pub name: String,
pub path: PathBuf, // full path to the project directory
pub manifest: Manifest,
pub sketch: Option<String>, // current sketch content, if any
pub has_tools: bool, // whether tools.json exists
}
// src/types/manifest.ts
export interface Manifest {
project: string;
board: string;
serial_port: string;
baud_rate: number;
components: Component[];
}
export interface Component {
id: string;
component_type: string;
pins: Record<string, number>;
label: string;
subtype?: string;
connection?: string;
resolution?: string;
format?: string;
}
The component library is a static registry of all supported component types. Each entry defines: the type name, category (actuator / sensor / vision), required pin names, optional fields, and the expected serial output format.
Store this as a static data structure in Rust:
// src-tauri/src/models/component_library.rs
pub struct ComponentTemplate {
pub component_type: &'static str,
pub category: &'static str, // "actuator", "distance", "motion", "touch", "environmental", "vision"
pub label: &'static str, // display name, e.g., "DC Motor"
pub pins: &'static [PinTemplate],
pub optional_fields: &'static [&'static str], // "subtype", "connection", "resolution", "format"
pub serial_output: Option<&'static str>, // expected output format description
}
pub struct PinTemplate {
pub name: &'static str,
pub description: &'static str,
}
Populate with all types from the CLAUDE.md Component Library section:
Actuators: dc_motor (pwm, dir_a, dir_b), servo (signal), stepper_motor (step, direction), relay (pin), led (pin)
Distance: ultrasonic (trig, echo), ir_distance (analog), lidar_serial (rx, tx)
Motion: imu (sda, scl — I2C), magnetometer (sda, scl — I2C), encoder (pin_a, pin_b)
Touch: bump_switch (pin), line_sensor_array (pins — variable count), force_sensor (analog)
Environmental: temp_humidity (data), barometer (sda, scl — I2C), light (sda, scl — I2C), gas (analog)
Vision: camera (no physical pins — wifi connection, needs subtype, resolution, format fields)
Also expose this library to the frontend via a Tauri command so the component picker can display it.
Projects live in a cuyamaca-projects/ directory inside the user's app data directory (use Tauri's app_data_dir()).
{app_data_dir}/cuyamaca-projects/
my-robot/
manifest.json
sketch.ino (created in Phase 4)
tools.json (created in Phase 4)
history/ (created in Phase 4)
sketch_v1.ino
...
Tauri commands for project CRUD:
#[tauri::command]
pub fn create_project(name: String, board: String) -> Result<Manifest, String> {
// Validate name (alphanumeric + hyphens, no spaces)
// Create directory
// Create manifest.json with defaults (empty components, 115200 baud)
// Return the manifest
}
#[tauri::command]
pub fn list_projects() -> Result<Vec<ProjectSummary>, String> {
// Scan cuyamaca-projects/ directory
// Read each manifest.json, return name + board + component count
}
#[tauri::command]
pub fn open_project(name: String) -> Result<Project, String> {
// Read manifest.json, optionally sketch.ino, check for tools.json
// Set as the active project in app state
}
#[tauri::command]
pub fn delete_project(name: String) -> Result<(), String> {
// Remove the project directory
}
#[tauri::command]
pub fn save_manifest(state: tauri::State<'_, AppState>) -> Result<(), String> {
// Write the active project's manifest to disk
}
Add to AppState:
pub struct AppState {
pub active_project: Mutex<Option<Project>>,
pub model_manager: Mutex<ModelManager>,
// ... other state
}
All manifest mutations (add component, change board, etc.) modify the in-memory project and then persist to disk via save_manifest.
#[tauri::command]
pub fn set_board(
state: tauri::State<'_, AppState>,
board: String,
) -> Result<(), String>
#[tauri::command]
pub fn set_serial_port(
state: tauri::State<'_, AppState>,
port: String,
) -> Result<(), String>
#[tauri::command]
pub fn set_baud_rate(
state: tauri::State<'_, AppState>,
baud: u32,
) -> Result<(), String>
#[tauri::command]
pub fn add_component(
state: tauri::State<'_, AppState>,
component: Component,
) -> Result<(), String>
#[tauri::command]
pub fn update_component(
state: tauri::State<'_, AppState>,
id: String,
component: Component,
) -> Result<(), String>
#[tauri::command]
pub fn remove_component(
state: tauri::State<'_, AppState>,
id: String,
) -> Result<(), String>
#[tauri::command]
pub fn list_serial_ports() -> Result<Vec<String>, String> {
// Use the serialport crate to enumerate available ports
// Returns port names: /dev/cu.* on macOS, COM* on Windows
}
#[tauri::command]
pub fn get_component_library() -> Vec<ComponentTemplate> {
// Return the static component library for the frontend picker
}
For serial port enumeration, add to Cargo.toml:
serialport = "4"
Replace the ManifestView placeholder from Phase 1 with the real editor.
┌─────────────────────────────────────┐
│ Board Configuration │
│ ┌────────────────────────────────┐ │
│ │ Board: [arduino:avr:uno ▼] │ │
│ │ Port: [/dev/cu.usbmodem ▼] │ │
│ │ Baud: [115200 ▼] │ │
│ └────────────────────────────────┘ │
│ │
│ Components [+ Add]│
│ ┌────────────────────────────────┐ │
│ │ ≡ Left Drive Motor dc_motor │ │
│ │ pwm: 5 dir_a: 2 dir_b: 3 │ │
│ ├────────────────────────────────┤ │
│ │ ≡ Front Distance ultrasonic │ │
│ │ trig: 9 echo: 10 │ │
│ ├────────────────────────────────┤ │
│ │ ≡ Head Servo servo │ │
│ │ signal: 3 │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────┘
Three dropdowns/inputs at the top:
arduino:avr:uno, arduino:avr:mega, arduino:avr:nano, arduino:sam:arduino_due_x, esp32:esp32:esp32.list_serial_ports(). Include a refresh button to re-enumerate. Show platform-appropriate format (/dev/cu.* on macOS, COM* on Windows).Each component renders as a Glass Standard card with:
Clicking a component card expands it to show:
Opens the component picker modal.
A modal overlay (Glass Strong) that displays the component library grouped by category.
┌─────────────────────────────────────────┐
│ Add Component [✕] │
│ │
│ ACTUATORS │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ DC │ │Servo │ │Stepper│ │
│ │Motor │ │ │ │Motor │ │
│ └──────┘ └──────┘ └──────┘ │
│ ┌──────┐ ┌──────┐ │
│ │Relay │ │ LED │ │
│ └──────┘ └──────┘ │
│ │
│ DISTANCE / PROXIMITY │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Ultra- │ │ IR │ │LiDAR │ │
│ │sonic │ │Dist. │ │Serial│ │
│ └──────┘ └──────┘ └──────┘ │
│ │
│ ... more categories ... │
└─────────────────────────────────────────┘
Each tile is a Glass Standard card showing the component type name and a small icon (use inline SVGs — no icon fonts). Clicking a tile:
dc_motor_1, incrementing if duplicates exist)The right-side Parts Panel mirrors the manifest's component list but in a compact, read-only format grouped by category.
Grouped display:
ACTUATORS
Left Drive Motor pwm:5
Right Drive Motor pwm:6
Head Servo sig:3
SENSORS
Front Distance trig:9
VISION
Forward Camera wifi
Each row shows: component label + one key pin or connection info. Clicking a component in the parts panel scrolls to and expands it in the Manifest View.
When no project is loaded, show "no project loaded" with a "Create Project" button.
Update the sidebar to show the project list above the navigation items:
┌──────────┐
│ CUYAMACA │
│ │
│ PROJECTS │
│ my-robot│ ← active (cyan accent)
│ test-bot│
│ [+ New] │
│ │
│ ─────── │
│ Manifest │ ← nav items
│ Code │
│ Chat │
│ │
│ ─────── │
│ ● Ollama │
│ ● CLI │
│ ● Code │
│ ● Runtime│
└──────────┘
Clicking a project name opens it. The active project is highlighted with a cyan left border. The "+ New" button opens a small inline form (project name + board dropdown) to create a new project.
manifest.json.manifest.json.Serial port enumeration on macOS: The serialport crate may not find ports if the USB driver isn't installed. For Arduino Uno, the built-in driver works. For CH340-based boards, the user needs the CH340 driver.
Component ID collisions: Auto-generated IDs must be unique within a project. When creating a component, check existing IDs and increment a suffix if needed.
File system permissions: Tauri's app_data_dir() should always be writable, but verify on both macOS and Windows.
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).