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(context)

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

### Input
- promptText: Natural language instruction containing the user data

### 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
- Parse required fields from promptText
- 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**: "Format user data for John 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 shared mirror-code-generator (generateMirrorCode)
  • The generator compares the newest spec file against the oldest expected target (mirroring specs/ paths); missing targets always trigger regeneration
  • Code is generated via a single-step LLM call combining all specs; cskill.md sections are not required for the prompt
  • Generated files are written directly under the skill directory matching specs/ relative paths (no src/ folder)
  • Regeneration happens automatically when specs change, making subsequent executions 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: Regeneration is automatic when specs are newer than generated files or any target is missing. If you need to force it, delete the generated targets that mirror specs/ (no src/ folder).

rm -f .AchillesSkills/user-formatter/index.mjs \
      .AchillesSkills/user-formatter/core.js \
      .AchillesSkills/user-formatter/utils.js
# 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!