mermaid-diagram-generator — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited mermaid-diagram-generator (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Generates Mermaid diagrams from Arduino code to visualize state machines, timing, architecture, and workflows.
# Generate state machine diagram from Arduino code
uv run mermaid-diagram-generator/scripts/generate_diagram.py \
--input src/main.ino \
--type state-machine \
--output docs/state-machine.mmd
# Generate timing diagram for I2C communication
uv run mermaid-diagram-generator/scripts/generate_diagram.py \
--type timing \
--signals "SDA,SCL,START,DATA,STOP" \
--output docs/i2c-timing.mmd
# Interactive mode
uv run mermaid-diagram-generator/scripts/generate_diagram.py --interactiveUse this skill when:
Don't use when:
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.8"
# dependencies = ["re"]
# ///
"""Extract state machine from Arduino code."""
import re
def extract_states(code):
"""Find enum states or #define constants."""
states = []
# Pattern 1: enum StateType { STATE_A, STATE_B, ... }
enum_pattern = r'enum\s+\w+\s*\{([^}]+)\}'
enum_match = re.search(enum_pattern, code)
if enum_match:
states_str = enum_match.group(1)
states = [s.strip().split('=')[0].strip()
for s in states_str.split(',') if s.strip()]
# Pattern 2: #define STATE_A 0
define_pattern = r'#define\s+(STATE_\w+)\s+\d+'
states += re.findall(define_pattern, code)
return list(set(states))
def extract_transitions(code, states):
"""Find state transitions in code."""
transitions = []
for i, state in enumerate(states):
# Look for assignments: currentState = NEXT_STATE
pattern = rf'{state}.*?=\s*(\w+)'
matches = re.findall(pattern, code)
for next_state in matches:
if next_state in states and next_state != state:
transitions.append((state, next_state))
return transitions
# Example usage
arduino_code = '''
enum State {
STATE_IDLE,
STATE_READING,
STATE_PROCESSING,
STATE_DONE
};
State currentState = STATE_IDLE;
void loop() {
switch(currentState) {
case STATE_IDLE:
if (buttonPressed()) {
currentState = STATE_READING;
}
break;
case STATE_READING:
readSensor();
currentState = STATE_PROCESSING;
break;
case STATE_PROCESSING:
processData();
currentState = STATE_DONE;
break;
case STATE_DONE:
currentState = STATE_IDLE;
break;
}
}
'''
states = extract_states(arduino_code)
transitions = extract_transitions(arduino_code, states)
print("States:", states)
print("Transitions:", transitions)def generate_state_diagram(states, transitions):
"""Generate Mermaid state diagram."""
mermaid = ["```mermaid", "stateDiagram-v2"]
# Add initial state
mermaid.append(f" [*] --> {states[0]}")
# Add transitions
for from_state, to_state in transitions:
mermaid.append(f" {from_state} --> {to_state}")
# Add final state if applicable
if states and 'DONE' in states[-1]:
mermaid.append(f" {states[-1]} --> [*]")
mermaid.append("```")
return '\n'.join(mermaid)
# Generate diagram
diagram = generate_state_diagram(states, transitions)
print(diagram)Output:
stateDiagram-v2 [] --> STATE_IDLE STATE_IDLE --> STATE_READING STATE_READING --> STATE_PROCESSING STATE_PROCESSING --> STATE_DONE STATE_DONE --> STATE_IDLE STATE_DONE --> []
def generate_flowchart(code):
"""Generate flowchart from Arduino function."""
flowchart = ["```mermaid", "flowchart TD"]
# Parse function structure
if 'if' in code:
flowchart.append(' A["Start"] --> B{"Condition?"}')
flowchart.append(' B -->|Yes| C["Action A"]')
flowchart.append(' B -->|No| D["Action B"]')
flowchart.append(' C --> E["End"]')
flowchart.append(' D --> E')
flowchart.append("```")
return '\n'.join(flowchart)def generate_timing_diagram(signals):
"""Generate timing diagram for protocol analysis."""
timing = ["```mermaid", "sequenceDiagram"]
timing.append(" participant M as Master")
timing.append(" participant S as Slave")
timing.append("")
timing.append(" M->>S: START")
timing.append(" M->>S: ADDRESS + W")
timing.append(" S->>M: ACK")
timing.append(" M->>S: DATA")
timing.append(" S->>M: ACK")
timing.append(" M->>S: STOP")
timing.append("```")
return '\n'.join(timing)
print(generate_timing_diagram(['SDA', 'SCL']))Output:
sequenceDiagram participant M as Master participant S as Slave
M->>S: START M->>S: ADDRESS + W S->>M: ACK M->>S: DATA S->>M: ACK M->>S: STOP
def generate_task_diagram(tasks):
"""Generate FreeRTOS task communication diagram."""
diagram = ["```mermaid", "flowchart LR"]
for task in tasks:
diagram.append(f' {task}["{task}"]')
# Add queue connections
diagram.append(" TaskA -->|Queue| TaskB")
diagram.append(" TaskB -->|Semaphore| TaskC")
diagram.append("```")
return '\n'.join(diagram)# Input: Arduino code with enum states
# Expected output: Mermaid stateDiagram-v2 with all states
✅ All states extracted correctly
✅ Transitions match code logic
✅ Diagram validates with mermaid-diagram-validator# Input: Function with if/else logic
# Expected output: Flowchart with decision diamonds
✅ Decision nodes for if statements
✅ Action nodes for code blocks
✅ Proper arrow connections# Input: I2C transaction description
# Expected output: Sequence diagram with timing
✅ Participants labeled correctly
✅ Message sequence matches protocol
✅ Timing annotations present# WRONG: Missing quotes around node labels with spaces
flowchart TD
A[Start System] --> B[Read Sensor] # Error!Fix:
✅ flowchart TD
A["Start System"] --> B["Read Sensor"]# WRONG: Only finding #define, missing enum states
def extract_states(code):
return re.findall(r'#define\s+(STATE_\w+)', code)Fix:
✅ def extract_states(code):
states = []
# Check enum
enum_match = re.search(r'enum\s+\w+\s*\{([^}]+)\}', code)
if enum_match:
states = [s.strip().split('=')[0].strip()
for s in enum_match.group(1).split(',')]
# Also check #define
states += re.findall(r'#define\s+(STATE_\w+)', code)
return list(set(states))# WRONG: Creating infinite loops in diagram
STATE_A --> STATE_B
STATE_B --> STATE_A # Circular without exitFix:
✅ # Add exit condition or [*] terminal state
STATE_A --> STATE_B
STATE_B --> STATE_A
STATE_A --> [*]| Use Case | Mermaid Type | When to Use |
|---|---|---|
| State machine | stateDiagram-v2 | FSM with enum states |
| Control flow | flowchart TD | if/else, loops |
| Timing | sequenceDiagram | I2C, SPI, UART |
| Architecture | flowchart LR | FreeRTOS tasks |
| Class structure | classDiagram | OOP Arduino libs |
import glob
def analyze_project(project_dir):
"""Analyze all .ino and .cpp files in project."""
all_states = []
all_transitions = []
for file in glob.glob(f"{project_dir}/**/*.ino", recursive=True):
with open(file, 'r') as f:
code = f.read()
states = extract_states(code)
transitions = extract_transitions(code, states)
all_states.extend(states)
all_transitions.extend(transitions)
return list(set(all_states)), list(set(all_transitions))def interactive_mode():
"""Interactive Mermaid diagram generation."""
print("Mermaid Diagram Generator")
print("1. State Machine")
print("2. Flowchart")
print("3. Timing Diagram")
choice = input("Select type (1-3): ")
if choice == '1':
states_input = input("Enter states (comma-separated): ")
states = [s.strip() for s in states_input.split(',')]
# Generate state diagram...def update_readme_diagram(readme_path, diagram):
"""Insert diagram into README.md."""
with open(readme_path, 'r') as f:
content = f.read()
# Find diagram section
start_marker = "## Architecture"
if start_marker in content:
# Replace old diagram
content = re.sub(
r'(## Architecture.*?)```mermaid.*?```',
f'\\1{diagram}',
content,
flags=re.DOTALL
)
else:
# Append new section
content += f"\n\n## Architecture\n\n{diagram}\n"
with open(readme_path, 'w') as f:
f.write(content)Progression for students:
Common student questions:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.