Code Skills Tutorial
Step-by-step guide to creating code skills: from specifications to execution.
Prerequisites
Before starting this tutorial, ensure you have:
- Node.js 18+ installed
- Basic understanding of JavaScript/ESM
- Familiarity with Achilles Agents concepts
- Access to an LLM provider (for code generation)
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)
.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"
- The
cskill.mdfile 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"
- 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"
- The
RecursiveSkilledAgentautomatically discovers skills in.AchillesSkillsdirectories - When it finds a
cskill.md, it automatically invokes the generate-code-skill (hardcoded generic skill for code generation) - The
generate-code-skillcompares timestamps: newest spec file vs oldest generated file - Code is generated via single-step LLM call combining all specs and
cskill.mdsections - 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:
- Specification files must contain ONLY natural language - no JavaScript code, only descriptions of what the code should do
- The
cskill.mdsections (Input Format, Output Format, Constraints, Examples) are automatically included in the generation prompt - All specification files are combined in a single LLM prompt, so ensure consistency across files
- Ensure all specification files are properly formatted with clear headings
- Verify that examples are complete and accurate
- Check that all required fields and their validation rules are documented
- If using diagrams (Mermaid), make sure they are syntactically correct
Issue: Missing index.mjs in generated code
Solution: Ensure your specs/index.js.md clearly specifies the main entrypoint and exports the action function.
- Verify the file exists in the
specs/folder - Check that it contains proper specification format
- Ensure it references the correct function name (
action)
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.
- Check that the skill was properly prepared
- Verify the
src/folder exists - Ensure
src/index.mjshas the correct exports
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:
- Code Skills Reference - Complete API documentation
- Skill System Overview - Architecture and design patterns
- Other Tutorials - Learn about other skill types
- Agent Runtime - Understand how agents execute skills
Experiment with different specification patterns and build complex skills by combining multiple modules!