Code Skills Tutorial

Step-by-step guide to creating code skills: from specifications to execution.

Prerequisites

Before starting this tutorial, ensure you have:

Step 1: Create Skill Structure

Start by creating the basic skill structure in your project repository:

mkdir -p .AchillesSkills/user-formatter/specs
cd .AchillesSkills/user-formatter

This creates the following structure:

user-formatter/
├── specs/              # Will contain our specification files
└── cskill.md           # Main skill descriptor (to be created)
Important: The .AchillesSkills directory must be at your project root. Each skill has its own folder with a descriptive name (using hyphens, not spaces).

Step 2: Create Skill Descriptor

Create cskill.md with the skill definition. This file serves as the main interface definition:

# User Data Formatter

## Summary
Formats user data into a standardized string representation based on detailed specifications.

## Input Format
- **user**: Object containing user information
  - `firstName` (string, required): User's first name (2-50 characters, letters only)
  - `lastName` (string, required): User's last name (2-50 characters, letters and hyphens)
  - `age` (number, required): User's age in years (0-120)
  - `email` (string, optional): User's email address (valid format)

## Output Format
- **Type**: `string`
- **Success Format**: "Name: {fullName}, Age: {age}, Status: {status}[, Email: {email}]"
- **Error Format**: "Error: {specific_error_message}"
- **Status**: "Adult" if age ≥ 18, otherwise "Minor"

## Constraints
- Validate all inputs according to specified rules
- Handle missing optional fields gracefully
- Return specific error messages for each validation failure
- Maintain consistent output format
- Use only built-in Node.js modules (no external dependencies)

## Examples
### Example 1 (Complete valid data)
- **Input**: `{ "user": { "firstName": "John", "lastName": "Doe", "age": 30, "email": "john@example.com" } }`
- **Output**: "Name: John Doe, Age: 30, Status: Adult, Email: john@example.com"
Key Points:
  • The cskill.md file defines the interface (inputs, outputs, constraints)
  • Detailed implementation specifications go in the specs/ folder
  • Include one comprehensive example covering the main use case
  • Be specific about validation rules and data formats

Step 3: Create Specification Files

Create specification files in the specs/ folder. These files describe the implementation logic using natural language and diagrams.

Create specs/index.js.md as the main entrypoint specification:

# Specification for index.js

## Function: action(args)

### Description
Main entry point for user data formatting skill. This function orchestrates the validation and formatting process.

### Input
- args: Object containing user data
  - user: Object (required) - The user object to format
    - firstName: string (required, 2-50 chars, letters only)
    - lastName: string (required, 2-50 chars, letters and hyphens)
    - age: number (required, 0-120)
    - email: string (optional, valid format)

### Processing Logic

```mermaid
flowchart TD
    A[Start] --> B[Validate Input]
    B -->|Valid| C[Format User Data]
    B -->|Invalid| D[Return Error]
    C --> E[Return Formatted Result]
    D --> E
```

#### 1. Validate Input
- Check that all required fields are present and valid
- Validate firstName: 2-50 letters only
- Validate lastName: 2-50 letters or hyphens
- Validate age: integer between 0 and 120
- Validate email format if provided

#### 2. Format User Data
- Construct fullName: "{firstName} {lastName}"
- Determine status: "Adult" if age ≥ 18, else "Minor"
- Build result string: "Name: {fullName}, Age: {age}, Status: {status}"
- Append ", Email: {email}" if email is provided

### Error Handling
- Return specific error messages for validation failures
- Handle unexpected errors gracefully with generic error message

### Example
- **Input**: `{ "user": { "firstName": "John", "lastName": "Doe", "age": 30, "email": "john@example.com" } }`
- **Output**: "Name: John Doe, Age: 30, Status: Adult, Email: john@example.com"
Important:
  • Specification files describe what the code should do, not how
  • Use natural language and diagrams (Mermaid) - NO JavaScript code
  • Include flowcharts, sequence diagrams, or state diagrams where helpful
  • Provide one clear example of expected input and output
  • Never include actual implementation code in specification files

Step 4: Using the Skill

Once you've created the skill structure and specifications, the RecursiveSkilledAgent will automatically discover and prepare your skill. You don't need to manually prepare or execute the skill.

Simply use the agent to execute your skill with natural language prompts:

import { RecursiveSkilledAgent } from 'achillesAgentLib';
import { LLMAgent } from 'achillesAgentLib';

// Initialize the agent with your LLM provider
const llmAgent = new LLMAgent({
  provider: 'anthropic', // or 'openai', etc.
  model: 'claude-3-opus-20240229',
  apiKey: process.env.ANTHROPIC_API_KEY
});

const agent = new RecursiveSkilledAgent({
  llmAgent,
  additionalSkillRoots: ['./.AchillesSkills']
});

// Execute the skill using natural language
const result = await agent.executePrompt(
  'Format user data for John Doe, age 30, email john@example.com',
  { skillName: 'user-formatter' }
);

console.log('Result:', result.result);
// Output: "Name: John Doe, Age: 30, Status: Adult, Email: john@example.com"
Automatic Preparation and Execution:
  • The RecursiveSkilledAgent automatically discovers skills in .AchillesSkills directories
  • When it finds a cskill.md, it automatically invokes the generate-code-skill (hardcoded generic skill for code generation)
  • The generate-code-skill compares timestamps: newest spec file vs oldest generated file
  • Code is generated via single-step LLM call combining all specs and cskill.md sections
  • Code regeneration happens automatically when spec files are newer than generated files (timestamp-based)
  • Generated code is cached and reused until specs change, making subsequent executions very fast
  • Results from primitive types (strings, numbers, etc.) are automatically wrapped in { result: value } format

Debugging Tips

Common issues and solutions:

Issue: Code not regenerating when specs change

Solution: Delete the src/ folder to force regeneration:

rm -rf .AchillesSkills/user-formatter/src
# Next prepareSkill will regenerate all code

Issue: Generated code has errors

Solution: Check your specification files for clarity and completeness. Remember:

Issue: Missing index.mjs in generated code

Solution: Ensure your specs/index.js.md clearly specifies the main entrypoint and exports the action function.

Issue: Execution fails with "Main entrypoint not found"

Solution: Verify that src/index.mjs exists and contains a valid action function. If missing, delete src/ and regenerate.

Issue: Cannot access result (undefined property)

Solution: If your skill returns a primitive value (string, number, boolean), it's automatically wrapped in an object:

// If your action function returns: "Hello, world!"
// The result will be: { result: "Hello, world!" }

const result = await agent.executePrompt(prompt, { skillName: 'my-skill' });
console.log(result.result);  // ✅ Correct: "Hello, world!"
console.log(result);          // ❌ Wrong: { result: "Hello, world!" }

This wrapping ensures consistent return types across the system and prevents issues when spreading primitive values.

Next Steps

Now that you've completed this tutorial, explore:

Experiment with different specification patterns and build complex skills by combining multiple modules!