Building Process
Plan Your Skill
Define what your skill will do and what tools it needs
Create Tools
Implement tools as TypeScript classes
Define Skill
Add tools to a LuaSkill instance in index.ts
Test Locally
Test with lua chat (sandbox mode) and optionally lua test
Deploy
Push and deploy to production
Complete Example: Task Management Skill
Step 1: Plan
Goal : Build a skill to manage tasks
Tools needed :
create_task - Add new tasks
list_tasks - View all tasks
complete_task - Mark tasks as done
search_tasks - Find tasks
Create src/tools/TaskTools.ts:
import { LuaTool , Data } from "lua-cli" ;
import { z } from "zod" ;
export class CreateTaskTool implements LuaTool {
name = "create_task" ;
description = "Create a new task" ;
inputSchema = z . object ({
title: z . string (),
description: z . string (). optional (),
priority: z . enum ([ 'low' , 'medium' , 'high' ]). default ( 'medium' )
});
async execute ( input : z . infer < typeof this . inputSchema >) {
const task = await Data . create ( 'tasks' , {
... input ,
status: 'pending' ,
createdAt: new Date (). toISOString ()
}, ` ${ input . title } ${ input . description || '' } ` );
return {
taskId: task . id ,
message: `Task " ${ input . title } " created`
};
}
}
export class ListTasksTool implements LuaTool {
name = "list_tasks" ;
description = "List all tasks" ;
inputSchema = z . object ({});
async execute ( input : any ) {
const result = await Data . get ( 'tasks' , {}, 1 , 50 );
return {
tasks: result . data . map ( entry => ({
id: entry . id ,
... entry . data
})),
count: result . pagination . totalCount
};
}
}
export class CompleteTaskTool implements LuaTool {
name = "complete_task" ;
description = "Mark a task as completed" ;
inputSchema = z . object ({
taskId: z . string ()
});
async execute ( input : z . infer < typeof this . inputSchema >) {
const task = await Data . getEntry ( 'tasks' , input . taskId );
await Data . update ( 'tasks' , input . taskId , {
... task . data ,
status: 'completed' ,
completedAt: new Date (). toISOString ()
});
return {
success: true ,
message: `Task completed!`
};
}
}
Update src/index.ts to use the v3.0.0 LuaAgent pattern :
import { LuaAgent , LuaSkill } from "lua-cli" ;
import {
CreateTaskTool ,
ListTasksTool ,
CompleteTaskTool
} from "./tools/TaskTools" ;
// Create skill
const taskSkill = new LuaSkill ({
name: "task-management-skill" ,
description: "Manage tasks and to-do lists" ,
context: `
This skill helps users manage their tasks.
- Use create_task when users want to add a new task
- Use list_tasks to show all tasks
- Use complete_task when users finish a task
Always confirm task details before creating.
When listing tasks, organize by priority.
` ,
tools: [
new CreateTaskTool (),
new ListTasksTool (),
new CompleteTaskTool ()
]
});
// Configure agent (v3.0.0)
export const agent = new LuaAgent ({
name: "task-assistant" ,
persona: `You are a helpful task management assistant.
Your role:
- Help users create and organize tasks
- Keep track of todos and deadlines
- Help users stay productive
Communication style:
- Friendly and encouraging
- Clear and concise
- Confirm before creating tasks` ,
skills: [ taskSkill ]
});
New in v3.0.0: Use LuaAgent to configure your agent’s persona, welcome message, and skills together. This is now the recommended pattern.
Step 4: Test
# Test conversationally (primary)
lua chat
# Optional: Test individual tools
lua test
Select sandbox mode to test with your local skills.
Step 5: Deploy
Best Practices
Build one tool at a time:
Create basic version
Test thoroughly
Add more features
Test again
// ✅ Good
name = "create_task"
name = "search_products"
name = "send_email"
// ❌ Bad
name = "task1"
name = "do_stuff"
name = "tool"
// ✅ Good
description = "Create a new task with title, description, and priority"
// ❌ Bad
description = "Creates tasks"
async execute ( input : any ) {
try {
const result = await api . call ( input );
return result ;
} catch ( error ) {
throw new Error (
`Failed to process request: ${ error . message } `
);
}
}
// ✅ Good - Structured
return {
success: true ,
taskId: task . id ,
message: "Task created"
};
// ❌ Bad - Unstructured
return "Task created with ID 123" ;
Common Patterns
CRUD Pattern
// Create
export class CreateItemTool {
async execute ( input ) {
return await Data . create ( 'items' , input );
}
}
// Read
export class GetItemTool {
async execute ( input ) {
return await Data . getEntry ( 'items' , input . id );
}
}
// Update
export class UpdateItemTool {
async execute ( input ) {
return await Data . update ( 'items' , input . id , input . data );
}
}
// Delete
export class DeleteItemTool {
async execute ( input ) {
return await Data . delete ( 'items' , input . id );
}
}
Search Pattern
export class SearchTool {
async execute ( input : { query : string }) {
// Vector search
const results = await Data . search (
'items' ,
input . query ,
10 ,
0.7
);
return {
items: results . data ,
count: results . count
};
}
}
External API Pattern
export class ExternalApiTool {
async execute ( input : any ) {
// Get API key from environment
const apiKey = env ( 'EXTERNAL_API_KEY' );
if ( ! apiKey ) {
throw new Error ( 'API key not configured' );
}
// Call external API
const response = await fetch ( apiUrl , {
headers: {
'Authorization' : `Bearer ${ apiKey } `
}
});
return await response . json ();
}
}
Next Steps