Skip to main content

Documentation Index

Fetch the complete documentation index at: https://doc.ambientsoul.ai/llms.txt

Use this file to discover all available pages before exploring further.

Skill Development Guide

This guide walks you through creating custom Skills for Soul Kernel from concept to deployment.

Prerequisites

  • Rust development environment
  • Soul Kernel CLI installed (soul --version)
  • Basic understanding of the Skill System

Quick Start: Hello World Skill

1. Create New Skill Project

Use the Soul CLI to scaffold a new skill:
# Create a new skill project
soul new skill hello-world
cd hello-world-skill
This creates:
hello-world-skill/
├── Cargo.toml
├── src/
│   └── lib.rs
└── tests/
    └── integration_test.rs

2. Configure Cargo.toml

The CLI generates a proper Cargo.toml:
[package]
name = "hello-world-skill"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]  # Important: Build as dynamic library

[dependencies]
soul-kernel-skill-abi = "0.1"
serde = { version = "1.0", features = ["derive"] }

3. Implement the Skill

Edit src/lib.rs:
use soul_kernel_skill_abi::{
    export_skill, Capability, Skill, SkillError, SkillInput, SkillOutput,
};

pub struct HelloWorldSkill {
    greeting: String,
}

impl HelloWorldSkill {
    pub fn new() -> Self {
        Self {
            greeting: "Hello from Soul Kernel!".to_string(),
        }
    }
}

impl Skill for HelloWorldSkill {
    fn name(&self) -> &str {
        "hello-world"
    }
    
    fn version(&self) -> &str {
        env!("CARGO_PKG_VERSION")
    }
    
    fn capabilities(&self) -> Vec<Capability> {
        vec![Capability::TextProcessing]
    }
    
    fn execute(&self, input: SkillInput) -> Result<SkillOutput, SkillError> {
        match input {
            SkillInput::Text(name) => {
                let response = if name.is_empty() {
                    self.greeting.clone()
                } else {
                    format!("{} Nice to meet you, {}!", self.greeting, name)
                };
                Ok(SkillOutput::Text(response))
            }
            _ => Err(SkillError::InvalidInput(
                "Expected text input".to_string()
            ))
        }
    }
}

// Required: Export the skill for dynamic loading
export_skill!(HelloWorldSkill);

4. Test Your Skill

Create tests/integration_test.rs:
use hello_world_skill::HelloWorldSkill;
use soul_kernel_skill_abi::{Skill, SkillInput, SkillOutput};

#[test]
fn test_greeting() {
    let skill = HelloWorldSkill::new();
    let input = SkillInput::Text("Alice".to_string());
    let output = skill.execute(input).unwrap();
    
    match output {
        SkillOutput::Text(text) => {
            assert!(text.contains("Alice"));
            assert!(text.contains("Hello"));
        }
        _ => panic!("Unexpected output type"),
    }
}

#[test]
fn test_empty_name() {
    let skill = HelloWorldSkill::new();
    let input = SkillInput::Text("".to_string());
    let output = skill.execute(input).unwrap();
    
    match output {
        SkillOutput::Text(text) => {
            assert_eq!(text, "Hello from Soul Kernel!");
        }
        _ => panic!("Unexpected output type"),
    }
}
Run tests:
# Run tests using Soul CLI
soul test skill

# Or use cargo directly
cargo test

5. Build the Skill

# Build using Soul CLI (recommended)
soul build skill --release

# Or use cargo directly
cargo build --release

# Your skill library will be at:
# - macOS: target/release/libhello_world_skill.dylib
# - Linux: target/release/libhello_world_skill.so
# - Windows: target/release/hello_world_skill.dll

6. Test Your Skill Locally

Use the Soul CLI to test your skill interactively:
# Test skill in REPL mode
soul repl --skill ./target/release/libhello_world_skill.dylib

# Or run automated tests
soul test skill --integration
In the REPL:
> load hello-world
Loaded skill: hello-world v0.1.0

> exec hello-world "Alice"
Hello from Soul Kernel! Nice to meet you, Alice!

> capabilities hello-world
- TextProcessing

7. Package and Install

# Package the skill for distribution
soul package skill
# Creates: hello-world-skill-0.1.0.skill

# Install locally for development
soul install skill ./hello-world-skill-0.1.0.skill --dev

# List installed skills
soul list skills

Advanced Skill Development

Using the Skill Template Generator

Soul CLI provides templates for common skill types:
# Create a perception skill (vision, audio)
soul new skill my-vision --template perception

# Create an action skill (motors, actuators)
soul new skill my-motor --template action

# Create a cognitive skill (reasoning, planning)
soul new skill my-planner --template cognitive

# List available templates
soul new skill --list-templates

Multi-Format Skills

Skills can handle multiple input/output formats:
pub struct VersatileSkill;

impl Skill for VersatileSkill {
    fn capabilities(&self) -> Vec<Capability> {
        vec![
            Capability::TextProcessing,
            Capability::ImageProcessing,
            Capability::AudioProcessing,
        ]
    }
    
    fn execute(&self, input: SkillInput) -> Result<SkillOutput, SkillError> {
        match input {
            SkillInput::Text(text) => {
                // Process text
                Ok(SkillOutput::Text(text.to_uppercase()))
            }
            SkillInput::Image(data) => {
                // Process image
                Ok(SkillOutput::Json(json!({
                    "size": data.len(),
                    "format": "processed"
                })))
            }
            SkillInput::Audio(samples) => {
                // Process audio
                let avg_amplitude = samples.iter().sum::<f32>() / samples.len() as f32;
                Ok(SkillOutput::Json(json!({
                    "average_amplitude": avg_amplitude
                })))
            }
            _ => Err(SkillError::NotImplemented(
                "Format not supported".to_string()
            ))
        }
    }
}

Stateful Skills

Skills can maintain state between executions:
use std::sync::Mutex;

pub struct CounterSkill {
    count: Mutex<u32>,
}

impl CounterSkill {
    pub fn new() -> Self {
        Self {
            count: Mutex::new(0),
        }
    }
}

impl Skill for CounterSkill {
    fn execute(&self, input: SkillInput) -> Result<SkillOutput, SkillError> {
        let mut count = self.count.lock().unwrap();
        *count += 1;
        
        Ok(SkillOutput::Json(json!({
            "count": *count,
            "input": format!("{:?}", input)
        })))
    }
}

Resource Management

Use initialize() and shutdown() for resource management:
pub struct DatabaseSkill {
    connection: Option<Connection>,
}

impl Skill for DatabaseSkill {
    fn initialize(&mut self) -> Result<(), SkillError> {
        self.connection = Some(
            Connection::open("soul.db")
                .map_err(|e| SkillError::ExecutionError(e.to_string()))?
        );
        Ok(())
    }
    
    fn shutdown(&mut self) -> Result<(), SkillError> {
        if let Some(conn) = self.connection.take() {
            conn.close()
                .map_err(|e| SkillError::ExecutionError(e.to_string()))?;
        }
        Ok(())
    }
}

Action Commands for Physical AI

Skills can control actuators through action commands:
pub struct MotorSkill;

impl Skill for MotorSkill {
    fn capabilities(&self) -> Vec<Capability> {
        vec![Capability::MotorControl]
    }
    
    fn execute(&self, input: SkillInput) -> Result<SkillOutput, SkillError> {
        match input {
            SkillInput::Text(command) => {
                match command.as_str() {
                    "wave" => Ok(SkillOutput::Action(ActionCommand {
                        target: "arm".to_string(),
                        action: "wave".to_string(),
                        parameters: json!({
                            "duration": 2.0,
                            "amplitude": 0.5,
                            "frequency": 2.0
                        }),
                    })),
                    "move_forward" => Ok(SkillOutput::Action(ActionCommand {
                        target: "wheels".to_string(),
                        action: "move".to_string(),
                        parameters: json!({
                            "direction": "forward",
                            "speed": 0.5,
                            "distance": 1.0
                        }),
                    })),
                    _ => Err(SkillError::InvalidInput(
                        format!("Unknown command: {}", command)
                    ))
                }
            }
            _ => Err(SkillError::InvalidInput("Expected text command".to_string()))
        }
    }
}

Error Handling Best Practices

Create custom error types for better error handling:
use thiserror::Error;

#[derive(Debug, Error)]
pub enum VisionError {
    #[error("Camera not available")]
    CameraUnavailable,
    
    #[error("Image processing failed: {0}")]
    ProcessingError(String),
    
    #[error("Invalid image format")]
    InvalidFormat,
}

impl From<VisionError> for SkillError {
    fn from(err: VisionError) -> Self {
        SkillError::ExecutionError(err.to_string())
    }
}

pub struct VisionSkill;

impl Skill for VisionSkill {
    fn execute(&self, input: SkillInput) -> Result<SkillOutput, SkillError> {
        match input {
            SkillInput::Image(data) => {
                if data.is_empty() {
                    return Err(VisionError::InvalidFormat.into());
                }
                // Process image...
                Ok(SkillOutput::Text("Image processed".to_string()))
            }
            _ => Err(SkillError::InvalidInput("Expected image input".to_string()))
        }
    }
}

Testing Strategies

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_skill_metadata() {
        let skill = MySkill::new();
        assert_eq!(skill.name(), "my-skill");
        assert!(!skill.version().is_empty());
        assert!(!skill.capabilities().is_empty());
    }
    
    #[test]
    fn test_invalid_input() {
        let skill = MySkill::new();
        let result = skill.execute(SkillInput::Binary(vec![]));
        assert!(result.is_err());
    }
}

Integration Tests with Soul CLI

# Run integration tests
soul test skill --integration

# Test with specific input files
soul test skill --input test_data/

# Benchmark performance
soul bench skill

Testing with Mock Soul

#[test]
fn test_skill_with_mock_soul() {
    // Soul CLI provides test utilities
    use soul_kernel_test::MockSoul;
    
    let mut soul = MockSoul::new();
    soul.load_skill("./target/release/libmy_skill.dylib").unwrap();
    
    let response = soul.ask("test question").unwrap();
    assert!(!response.is_empty());
}

Performance Optimization

Profiling with Soul CLI

# Profile skill performance
soul profile skill ./my-skill.skill

# Generate flamegraph
soul profile skill --flamegraph

# Memory usage analysis
soul analyze skill --memory

Lazy Initialization

use once_cell::sync::Lazy;

static MODEL: Lazy<Model> = Lazy::new(|| {
    Model::load("model.onnx").expect("Failed to load model")
});

pub struct MLSkill;

impl Skill for MLSkill {
    fn execute(&self, input: SkillInput) -> Result<SkillOutput, SkillError> {
        // MODEL is initialized only on first use
        let result = MODEL.predict(&input)?;
        Ok(SkillOutput::Json(result))
    }
}

Caching Results

use lru::LruCache;
use std::sync::Mutex;

pub struct CachedSkill {
    cache: Mutex<LruCache<String, String>>,
}

impl CachedSkill {
    pub fn new() -> Self {
        Self {
            cache: Mutex::new(LruCache::new(100)),
        }
    }
}

Deployment

Publishing to Soul Registry

# Login to Soul Registry
soul login

# Publish your skill
soul publish skill
# Validates, packages, and uploads to registry

# Publish with specific visibility
soul publish skill --visibility public
soul publish skill --visibility private
soul publish skill --visibility unlisted

Installing from Registry

# Install a skill from registry
soul install skill weather-skill

# Install specific version
soul install skill weather-skill@1.2.0

# Install with dependencies
soul install skill vision-skill --with-deps

Building for Different Platforms

# Build for multiple targets
soul build skill --target all

# Build for specific platforms
soul build skill --target macos,linux,windows

# Cross-compile for edge devices
soul build skill --target aarch64-unknown-linux-gnu

Size Optimization

# In Cargo.toml
[profile.release]
opt-level = "z"     # Optimize for size
lto = true          # Link-time optimization
codegen-units = 1   # Single codegen unit
strip = true        # Strip symbols
Or use Soul CLI:
# Build with size optimization
soul build skill --optimize size

Soul CLI Commands Reference

Development Commands

# Create new skill
soul new skill <name> [--template <template>]

# Build skill
soul build skill [--release] [--target <target>]

# Test skill
soul test skill [--integration] [--bench]

# Run skill in REPL
soul repl --skill <path>

# Package skill
soul package skill [--sign]

# Profile skill
soul profile skill <path> [--flamegraph]

Management Commands

# List installed skills
soul list skills [--verbose]

# Install skill
soul install skill <name|path> [--dev]

# Uninstall skill
soul uninstall skill <name>

# Update skill
soul update skill <name>

# Show skill info
soul info skill <name>

Registry Commands

# Login to registry
soul login [--token <token>]

# Publish skill
soul publish skill [--visibility <level>]

# Search registry
soul search skill <query>

# Download skill
soul download skill <name> [--version <version>]

Best Practices Checklist

  • Use soul new skill to start projects
  • Test with soul test skill before publishing
  • Profile performance with soul profile skill
  • Document skill with soul doc skill
  • Sign releases with soul package skill --sign
  • Use semantic versioning
  • Include comprehensive tests
  • Handle errors gracefully
  • Validate all inputs
  • Clean up resources properly

Example Skills

Check out the example skills in the Soul Kernel repository:

Next Steps

Change Log

  • 2025-06-12: Complete rewrite based on actual Skill ABI V1 implementation
    • Updated all code examples to match real API
    • Added export_skill! macro usage
    • Included ActionCommand examples for Physical AI
    • Added proper error handling patterns
    • Updated build instructions for dynamic libraries
    • Added registry integration examples
  • 2025-06-12: Added comprehensive Soul CLI integration
    • Added CLI commands throughout the guide
    • Included REPL testing examples
    • Added registry publishing workflow
    • Included profiling and optimization commands