.claude/skills/cuyamaca-code-generation/SKILL.md
Build the code generation and diff review workflow for Cuyamaca — the code model generates Arduino sketches from manifests, displays them with highlighted diffs, and synthesizes tool definitions from the sketch. Use this skill whenever the user wants to implement sketch generation, build the code view with diffs, add the approve/reject workflow, implement tool synthesis, add sketch upload support, or references "phase 4", "code generation", "sketch generation", "code view", "diff view", "approve flash", "tool synthesis", "code model integration", or "sketch upload". Also trigger when the user asks about prompting the LLM for Arduino code, structured diff display, or generating tools.json from a sketch. This skill assumes Phase 3 is complete (project system, manifest editor, component library).
npx skillsauth add yuyanghu06/cuyamaca cuyamaca-code-generationInstall 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 wires up the code model to generate and modify Arduino sketches from the manifest, displays the results in a code view with highlighted diffs, and synthesizes tool definitions from the generated sketch. This is the first phase where the LLM is actively used to produce output.
.ino filetools.jsonThe code model receives the manifest and a system prompt that encodes all sketch generation rules from the CLAUDE.md. This prompt is critical — it's the contract between the manifest and the generated code.
Build the system prompt in Rust as a formatted string:
You are an Arduino code generator. You produce complete, compilable .ino sketches.
Rules you must follow:
1. Always include Serial.begin({baud_rate}) in setup().
2. Always include a command dispatch loop in loop() that reads Serial.readStringUntil('\n'), parses the CMD: prefix, and dispatches to handler functions.
3. All Serial.print output must follow the structured format: SENSOR_ID:VALUE
4. Print sensor state at fixed intervals using millis(), not delay(). Default interval: 100ms.
5. Always include an emergency stop command (CMD:stop) that halts all actuators immediately.
6. Pin assignments must use #define or const int declarations matching the manifest exactly.
7. Do not include freeform debug strings. All serial output is structured.
The hardware manifest:
{manifest_json}
Component pin reference:
{formatted_pin_summary}
Generate a complete .ino sketch that:
- Initializes all components in setup()
- Reads and prints all sensor values at 100ms intervals in loop()
- Includes a CMD dispatch loop for controlling all actuators
- Includes CMD:stop as an emergency halt
Generate a complete Arduino sketch for this hardware configuration. Include all sensor reading, actuator control, and the serial command dispatch loop.
Here is the current sketch:
{current_sketch}
Modify it to: {user_instruction}
Return the complete modified sketch. Do not omit any existing functionality unless explicitly asked to remove it.
Create the code generation service:
// src-tauri/src/services/code_gen.rs
pub struct CodeGenService;
impl CodeGenService {
pub async fn generate_sketch(
provider: &dyn ModelProvider,
manifest: &Manifest,
) -> Result<GeneratedSketch, String> {
let system_prompt = build_system_prompt(manifest);
let user_prompt = "Generate a complete Arduino sketch for this hardware configuration.";
let request = CompletionRequest {
messages: vec![ChatMessage {
role: "user".to_string(),
content: MessageContent::Text(user_prompt.to_string()),
}],
system_prompt: Some(system_prompt),
temperature: Some(0.2), // low temperature for code generation
max_tokens: Some(4096),
tools: None,
};
let response = provider.complete(request).await?;
let sketch = extract_code_block(&response.content)?;
Ok(GeneratedSketch {
code: sketch,
diff: None, // no diff for initial generation
})
}
pub async fn modify_sketch(
provider: &dyn ModelProvider,
manifest: &Manifest,
current_sketch: &str,
instruction: &str,
conversation_history: &[ChatMessage],
) -> Result<GeneratedSketch, String> {
// Build messages with conversation context
// Include the current sketch and modification instruction
// Parse response, extract code, compute diff against current
}
pub async fn synthesize_tools(
provider: &dyn ModelProvider,
manifest: &Manifest,
sketch: &str,
) -> Result<Vec<ToolDefinition>, String> {
// Prompt the code model to read the sketch and produce tool definitions
// The prompt should specify the exact JSON schema from CLAUDE.md
}
}
pub struct GeneratedSketch {
pub code: String,
pub diff: Option<SketchDiff>,
}
pub struct SketchDiff {
pub added_lines: Vec<usize>,
pub removed_lines: Vec<(usize, String)>,
pub unified_diff: String,
}
The model's response will contain the sketch wrapped in markdown code fences. Extract it:
fn extract_code_block(response: &str) -> Result<String, String> {
// Find ```cpp or ```arduino or ```ino or plain ``` blocks
// Extract the content between the fences
// If no code fence found, treat the entire response as code (fallback)
}
Use a diff library to compute the difference between the old and new sketch:
# Cargo.toml
similar = "2"
The similar crate provides unified diff output. Compute line-level diffs and track which lines were added, removed, or modified.
Users can upload their own .ino file instead of generating from the manifest. The uploaded sketch replaces the current sketch in the project.
#[tauri::command]
pub async fn upload_sketch(
state: tauri::State<'_, AppState>,
sketch_content: String,
) -> Result<(), String> {
// Set as the active sketch
// Save to project directory as sketch.ino
// Trigger tool synthesis on the uploaded sketch
}
On the frontend, provide a file picker or drag-and-drop zone in the Code View for .ino files. Read the file content in TypeScript and pass it via the Tauri command.
When a sketch is uploaded, the code model should still be used to:
Show the user what changes the model would make as a diff before applying.
After a sketch is generated or uploaded, the code model reads it and produces tools.json — the tool definitions that the runtime model will use to control the hardware.
Read this Arduino sketch and produce a JSON array of tool definitions.
Each tool represents a serial command the sketch can receive. For each dispatchable command in the CMD: handler, create a tool with:
- name: snake_case matching the function name
- description: plain English explanation of what this tool does, written for someone who has never seen the sketch
- parameters: object mapping parameter names to {type, range, default, required}
- serial_command: the exact CMD string template with {param} placeholders
Also include these lifecycle tools (not serial commands, managed by the app):
- read_sensor_state: returns current parsed sensor values
- wait_milliseconds: pauses for a specified duration
- end_session: terminates the control loop
The sketch:
{sketch}
Respond with ONLY the JSON array, no explanation.
Parse the response as Vec<ToolDefinition> and save as tools.json in the project directory.
Every time a sketch is modified (not on initial generation), save the previous version:
fn save_sketch_version(project_path: &Path, sketch: &str) -> Result<(), String> {
let history_dir = project_path.join("history");
std::fs::create_dir_all(&history_dir).map_err(|e| e.to_string())?;
let version = count_versions(&history_dir) + 1;
let filename = format!("sketch_v{}.ino", version);
std::fs::write(history_dir.join(filename), sketch).map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
pub async fn generate_sketch(
state: tauri::State<'_, AppState>,
) -> Result<GeneratedSketchResponse, String> {
// Get the active project's manifest
// Get the code model provider
// Call CodeGenService::generate_sketch
// Return the sketch code (don't save yet — user must approve)
}
#[tauri::command]
pub async fn modify_sketch(
state: tauri::State<'_, AppState>,
instruction: String,
) -> Result<GeneratedSketchResponse, String> {
// Get current sketch + manifest
// Call CodeGenService::modify_sketch
// Compute diff against current
// Return sketch code + diff (don't save yet)
}
#[tauri::command]
pub async fn approve_sketch(
state: tauri::State<'_, AppState>,
sketch_code: String,
) -> Result<(), String> {
// Save current sketch to version history
// Write new sketch to sketch.ino
// Run tool synthesis
// Save tools.json
// Update project state
}
#[tauri::command]
pub async fn reject_sketch(
state: tauri::State<'_, AppState>,
) -> Result<(), String> {
// Discard the pending sketch, keep the current one
}
#[tauri::command]
pub async fn get_sketch(
state: tauri::State<'_, AppState>,
) -> Result<Option<String>, String> {
// Return the current sketch content
}
#[tauri::command]
pub async fn get_tools(
state: tauri::State<'_, AppState>,
) -> Result<Option<Vec<ToolDefinition>>, String> {
// Return the current tools.json content
}
Replace the CodeView placeholder with the real code display.
┌─────────────────────────────────────────┐
│ [Approve & Flash] [Reject] │
│ ─── only visible when pending diff ─── │
├─────────────────────────────────────────┤
│ │
│ 1 #include <Servo.h> │
│ 2 │
│ 3 // Pin definitions │
│ 4+ const int MOTOR_L_PWM = 5; │ ← added (cyan highlight)
│ 5+ const int MOTOR_L_DIR_A = 2; │ ← added
│ 6 const int MOTOR_L_DIR_B = 3; │
│ 7- const int OLD_PIN = 4; │ ← removed (red highlight)
│ 8 │
│ ... (scrollable) │
│ │
└─────────────────────────────────────────┘
Use a lightweight syntax highlighting approach. Options:
Apply highlighting classes:
void, int, const, if, while, for, return): cyan#include, #define): purpleWhen a pending sketch modification exists, overlay the diff:
var(--cyan-bg))var(--red-bg)), shown with strikethroughLine numbers in monospace, dimmed. The code content in monospace at 13px.
A floating bar at the top of the Code View, visible only when there's a pending diff:
approve_sketch and then triggers flashing (Phase 5).reject_sketch and reverts the display.When no pending diff exists, the bar is hidden and the code is displayed read-only.
When no sketch exists yet, show:
generate_sketch using the code model.ino filesThe Chat View (from the sidebar nav) becomes a conversational interface with the code model. This is where users ask for sketch modifications in natural language.
The flow:
modify_sketch with the instructionThe chat maintains conversation history so the code model has context from previous exchanges. Store this history in the Rust backend state, scoped to the active project.
Same layout as Sierra's chat (message bubbles, input capsule) but with the dark industrial styling:
The chat is NOT streaming for code generation — the full response is shown at once after the model finishes. Show a loading state (pulsing input capsule border + "Generating..." text in a temporary AI bubble) while the code model works.
sketch.ino, tools.json is generated.ino file — the code model analyzes it and suggests modifications if neededtools.json — it should contain tool definitions matching the sketch's CMD handlersCode model produces incomplete sketches: Lower the temperature (0.1-0.2) and increase max_tokens. Include explicit instructions to produce the COMPLETE sketch, not just the changed parts.
Diff is wrong or misaligned: Use the similar crate's line-level diff, not character-level. Normalize line endings before comparing.
Tool synthesis returns invalid JSON: The model may wrap JSON in markdown fences or add explanation text. Strip everything outside the JSON array. Validate against the schema before saving.
Large sketches exceed context window: For very complex hardware setups, the sketch + manifest + conversation history may exceed the model's context. Add truncation logic that keeps the manifest and most recent sketch in full but trims older conversation turns.
complete method.tools.json must always be in sync with the current sketch.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).