lua-cli 3.0.0-alpha.1 → 3.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/job.api.service.d.ts +16 -7
- package/dist/api/job.api.service.js +21 -5
- package/dist/api/postprocessor.api.service.d.ts +61 -1
- package/dist/api/postprocessor.api.service.js +35 -0
- package/dist/api/preprocessor.api.service.d.ts +61 -1
- package/dist/api/preprocessor.api.service.js +35 -0
- package/dist/api-exports.d.ts +26 -6
- package/dist/api-exports.js +42 -29
- package/dist/cli/command-definitions.js +13 -6
- package/dist/commands/chat.js +32 -5
- package/dist/commands/compile.js +16 -2
- package/dist/commands/dev.js +23 -2
- package/dist/commands/push.d.ts +6 -2
- package/dist/commands/push.js +412 -6
- package/dist/commands/test.js +18 -2
- package/dist/common/job.instance.d.ts +3 -0
- package/dist/common/job.instance.js +8 -0
- package/dist/config/constants.d.ts +6 -5
- package/dist/config/constants.js +12 -10
- package/dist/interfaces/chat.d.ts +30 -1
- package/dist/interfaces/jobs.d.ts +21 -0
- package/dist/types/skill.d.ts +75 -56
- package/dist/types/skill.js +53 -59
- package/dist/utils/bundling.d.ts +13 -4
- package/dist/utils/bundling.js +83 -26
- package/dist/utils/compile.js +27 -6
- package/dist/utils/dev-api.d.ts +42 -2
- package/dist/utils/dev-api.js +177 -4
- package/dist/utils/dev-server.d.ts +1 -1
- package/dist/utils/dev-server.js +4 -4
- package/dist/utils/dynamic-job-bundler.d.ts +17 -0
- package/dist/utils/dynamic-job-bundler.js +143 -0
- package/dist/utils/pre-bundle-jobs.d.ts +26 -0
- package/dist/utils/pre-bundle-jobs.js +176 -0
- package/dist/utils/sandbox-storage.d.ts +48 -0
- package/dist/utils/sandbox-storage.js +114 -0
- package/dist/utils/sandbox.d.ts +2 -2
- package/dist/utils/sandbox.js +23 -7
- package/package.json +1 -1
- package/template/lua.skill.yaml +47 -0
- package/template/package-lock.json +10505 -0
- package/template/package.json +2 -1
- package/template/src/index.ts +65 -3
- package/template/src/tools/CreateInlineJob.ts +42 -0
- package/API_REFERENCE.md +0 -1408
- package/CHANGELOG.md +0 -236
- package/CLI_REFERENCE.md +0 -908
- package/GETTING_STARTED.md +0 -1040
- package/INSTANCE_TYPES.md +0 -1158
- package/README.md +0 -865
- package/TEMPLATE_GUIDE.md +0 -1398
- package/USER_DATA_INSTANCE.md +0 -621
- package/template/AGENT_CONFIGURATION.md +0 -251
- package/template/COMPLEX_JOB_EXAMPLES.md +0 -795
- package/template/DYNAMIC_JOB_CREATION.md +0 -371
- package/template/TOOL_EXAMPLES.md +0 -655
- package/template/WEBHOOKS_JOBS_QUICKSTART.md +0 -318
- package/template/WEBHOOK_JOB_EXAMPLES.md +0 -817
- package/template/src/index-agent-example.ts +0 -201
- package/template/src/postprocessors/ResponseFormatter.ts +0 -151
- package/template/src/preprocessors/MessageFilter.ts +0 -91
package/TEMPLATE_GUIDE.md
DELETED
|
@@ -1,1398 +0,0 @@
|
|
|
1
|
-
# Lua CLI - Template Project Guide
|
|
2
|
-
|
|
3
|
-
Complete guide to understanding and building skills using the template project.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 📋 Table of Contents
|
|
8
|
-
|
|
9
|
-
- [Overview](#overview)
|
|
10
|
-
- [Project Structure](#project-structure)
|
|
11
|
-
- [Understanding the Template](#understanding-the-template)
|
|
12
|
-
- [Building Your First Skill](#building-your-first-skill)
|
|
13
|
-
- [Tool Examples](#tool-examples)
|
|
14
|
-
- [Multi-Skill Projects](#multi-skill-projects)
|
|
15
|
-
- [Best Practices](#best-practices)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Overview
|
|
20
|
-
|
|
21
|
-
When you run `lua init`, a complete example project is created with:
|
|
22
|
-
- ✅ Multiple skill examples
|
|
23
|
-
- ✅ 30+ tool examples
|
|
24
|
-
- ✅ Integration with all Lua platform APIs
|
|
25
|
-
- ✅ TypeScript configuration
|
|
26
|
-
- ✅ Ready to customize and deploy
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Project Structure
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
your-skill/
|
|
34
|
-
├── src/
|
|
35
|
-
│ ├── index.ts # Main skill definitions (YOUR STARTING POINT)
|
|
36
|
-
│ ├── tools/ # Tool implementations
|
|
37
|
-
│ │ ├── GetWeatherTool.ts # External API example
|
|
38
|
-
│ │ ├── UserDataTool.ts # User API example
|
|
39
|
-
│ │ ├── ProductsTool.ts # Products API example
|
|
40
|
-
│ │ ├── BasketTool.ts # Baskets API example
|
|
41
|
-
│ │ ├── OrderTool.ts # Orders API example
|
|
42
|
-
│ │ ├── CustomDataTool.ts # Custom Data API example
|
|
43
|
-
│ │ ├── PaymentTool.ts # Payment integration example
|
|
44
|
-
│ │ └── CreatePostTool.ts # Simple tool example
|
|
45
|
-
│ └── services/ # Helper services (optional)
|
|
46
|
-
│ ├── ApiService.ts
|
|
47
|
-
│ └── GetWeather.ts
|
|
48
|
-
├── lua.skill.yaml # Configuration (auto-managed)
|
|
49
|
-
├── package.json # Dependencies
|
|
50
|
-
├── tsconfig.json # TypeScript config
|
|
51
|
-
├── .env.example # Environment variables template
|
|
52
|
-
└── README.md # Project documentation
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## Understanding the Template
|
|
58
|
-
|
|
59
|
-
### `src/index.ts` - The Heart of Your Project
|
|
60
|
-
|
|
61
|
-
This is where you define your skills and add tools to them.
|
|
62
|
-
|
|
63
|
-
**Template Structure:**
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
import { LuaSkill } from "lua-cli";
|
|
67
|
-
import GetWeatherTool from "./tools/GetWeatherTool";
|
|
68
|
-
// ... more imports
|
|
69
|
-
|
|
70
|
-
// Define your skills
|
|
71
|
-
const generalSkill = new LuaSkill({
|
|
72
|
-
name: "general-skill",
|
|
73
|
-
version: "0.0.2",
|
|
74
|
-
description: "A comprehensive skill with weather and utilities",
|
|
75
|
-
context: "Use get_weather for weather info, create_post for posts...",
|
|
76
|
-
tools: [
|
|
77
|
-
new GetWeatherTool(),
|
|
78
|
-
new CreatePostTool()
|
|
79
|
-
]
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const productSkill = new LuaSkill({
|
|
83
|
-
name: "product-skill",
|
|
84
|
-
version: "0.0.1",
|
|
85
|
-
description: "Product catalog management",
|
|
86
|
-
context: "Use search_products to find items, create_product to add new items...",
|
|
87
|
-
tools: [
|
|
88
|
-
new SearchProductsTool(),
|
|
89
|
-
new CreateProductTool(),
|
|
90
|
-
// ... more tools
|
|
91
|
-
]
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Key Points:**
|
|
96
|
-
- Each skill is independent
|
|
97
|
-
- Skills can have different versions
|
|
98
|
-
- Tools can only belong to one skill
|
|
99
|
-
- Context guides the AI on when to use tools
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
### `src/tools/` - Tool Implementations
|
|
104
|
-
|
|
105
|
-
Each file contains one or more related tools.
|
|
106
|
-
|
|
107
|
-
**File Organization:**
|
|
108
|
-
- `GetWeatherTool.ts` - Single standalone tool
|
|
109
|
-
- `ProductsTool.ts` - Multiple related tools (CRUD operations)
|
|
110
|
-
- `UserDataTool.ts` - Multiple related tools (Get + Update)
|
|
111
|
-
|
|
112
|
-
**Pattern: Single Tool per File**
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// GetWeatherTool.ts
|
|
116
|
-
import { LuaTool } from "lua-cli";
|
|
117
|
-
import { z } from "zod";
|
|
118
|
-
|
|
119
|
-
export default class GetWeatherTool implements LuaTool {
|
|
120
|
-
name = "get_weather";
|
|
121
|
-
description = "Get weather for a city";
|
|
122
|
-
inputSchema = z.object({
|
|
123
|
-
city: z.string()
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
127
|
-
// Implementation
|
|
128
|
-
return { weather: "sunny", temperature: 72 };
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Pattern: Multiple Related Tools per File**
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
// ProductsTool.ts
|
|
137
|
-
import { LuaTool, Products } from "lua-cli";
|
|
138
|
-
import { z } from "zod";
|
|
139
|
-
|
|
140
|
-
export class SearchProductsTool implements LuaTool {
|
|
141
|
-
name = "search_products";
|
|
142
|
-
description = "Search products by name or description";
|
|
143
|
-
inputSchema = z.object({ query: z.string() });
|
|
144
|
-
|
|
145
|
-
async execute(input: any) {
|
|
146
|
-
return Products.search(input.query);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export class CreateProductTool implements LuaTool {
|
|
151
|
-
name = "create_product";
|
|
152
|
-
description = "Create a new product";
|
|
153
|
-
inputSchema = z.object({
|
|
154
|
-
name: z.string(),
|
|
155
|
-
price: z.number()
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
async execute(input: any) {
|
|
159
|
-
return Products.create(input);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Export multiple tools
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## Building Your First Skill
|
|
169
|
-
|
|
170
|
-
### Step 1: Start with the Template
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
mkdir my-weather-skill
|
|
174
|
-
cd my-weather-skill
|
|
175
|
-
lua init
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Step 2: Clean Up Unused Tools
|
|
179
|
-
|
|
180
|
-
The template includes many examples. Remove what you don't need:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
# Delete unused tool files
|
|
184
|
-
rm src/tools/ProductsTool.ts
|
|
185
|
-
rm src/tools/BasketTool.ts
|
|
186
|
-
rm src/tools/OrderTool.ts
|
|
187
|
-
# Keep only what you need
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Step 3: Modify `src/index.ts`
|
|
191
|
-
|
|
192
|
-
Simplify to your use case:
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
import { LuaSkill } from "lua-cli";
|
|
196
|
-
import GetWeatherTool from "./tools/GetWeatherTool";
|
|
197
|
-
|
|
198
|
-
const weatherSkill = new LuaSkill({
|
|
199
|
-
name: "weather-skill",
|
|
200
|
-
version: "1.0.0",
|
|
201
|
-
description: "Provides weather information for cities worldwide",
|
|
202
|
-
context: "Use the get_weather tool when users ask about weather, temperature, or conditions in any city. The tool requires a city name and returns current weather data including temperature, wind speed, and conditions.",
|
|
203
|
-
tools: [
|
|
204
|
-
new GetWeatherTool()
|
|
205
|
-
]
|
|
206
|
-
});
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Step 4: Customize or Create Tools
|
|
210
|
-
|
|
211
|
-
Edit `src/tools/GetWeatherTool.ts` or create new ones:
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
import { LuaTool, env } from "lua-cli";
|
|
215
|
-
import { z } from "zod";
|
|
216
|
-
|
|
217
|
-
export default class MyCustomTool implements LuaTool {
|
|
218
|
-
name = "my_custom_tool";
|
|
219
|
-
description = "Does something specific";
|
|
220
|
-
|
|
221
|
-
inputSchema = z.object({
|
|
222
|
-
param1: z.string(),
|
|
223
|
-
param2: z.number().optional()
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
227
|
-
// Your logic here
|
|
228
|
-
return {
|
|
229
|
-
result: "success",
|
|
230
|
-
data: {...}
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Step 5: Test Locally
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
lua test
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### Step 6: Start Development
|
|
243
|
-
|
|
244
|
-
```bash
|
|
245
|
-
lua dev
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
Test in the chat interface, iterate on your tools.
|
|
249
|
-
|
|
250
|
-
### Step 7: Deploy
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
lua push # Upload version
|
|
254
|
-
lua deploy # Deploy to production
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Tool Examples
|
|
260
|
-
|
|
261
|
-
### Example 1: External API Call
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
// File: src/tools/GetWeatherTool.ts
|
|
265
|
-
import { LuaTool } from "lua-cli";
|
|
266
|
-
import { z } from "zod";
|
|
267
|
-
|
|
268
|
-
export default class GetWeatherTool implements LuaTool {
|
|
269
|
-
name = "get_weather";
|
|
270
|
-
description = "Get current weather for any city";
|
|
271
|
-
|
|
272
|
-
inputSchema = z.object({
|
|
273
|
-
city: z.string().describe("City name")
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
277
|
-
// Call Open-Meteo API (free, no API key required)
|
|
278
|
-
const geoUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(input.city)}&count=1`;
|
|
279
|
-
const geoRes = await fetch(geoUrl);
|
|
280
|
-
const geoData = await geoRes.json();
|
|
281
|
-
|
|
282
|
-
if (!geoData.results?.[0]) {
|
|
283
|
-
throw new Error(`City not found: ${input.city}`);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const { latitude, longitude, name } = geoData.results[0];
|
|
287
|
-
|
|
288
|
-
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true`;
|
|
289
|
-
const weatherRes = await fetch(weatherUrl);
|
|
290
|
-
const weatherData = await weatherRes.json();
|
|
291
|
-
|
|
292
|
-
return {
|
|
293
|
-
city: name,
|
|
294
|
-
temperature: weatherData.current_weather.temperature,
|
|
295
|
-
windSpeed: weatherData.current_weather.windspeed
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
**Usage in `index.ts`:**
|
|
302
|
-
```typescript
|
|
303
|
-
import GetWeatherTool from "./tools/GetWeatherTool";
|
|
304
|
-
|
|
305
|
-
const skill = new LuaSkill({
|
|
306
|
-
description: "Weather information skill",
|
|
307
|
-
context: "Use get_weather when users ask about weather or temperature in any city.",
|
|
308
|
-
tools: [new GetWeatherTool()]
|
|
309
|
-
});
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
### Example 2: User Data Management
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
// File: src/tools/UserDataTool.ts
|
|
318
|
-
import { LuaTool, User } from "lua-cli";
|
|
319
|
-
import { z } from "zod";
|
|
320
|
-
|
|
321
|
-
export class GetUserDataTool implements LuaTool {
|
|
322
|
-
name = "get_user_data";
|
|
323
|
-
description = "Retrieve current user's profile information";
|
|
324
|
-
inputSchema = z.object({});
|
|
325
|
-
|
|
326
|
-
async execute(input: any) {
|
|
327
|
-
const user = await User.get();
|
|
328
|
-
return {
|
|
329
|
-
name: user.name,
|
|
330
|
-
email: user.email,
|
|
331
|
-
// Return relevant user data
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export class UpdateUserDataTool implements LuaTool {
|
|
337
|
-
name = "update_user_data";
|
|
338
|
-
description = "Update user's profile information";
|
|
339
|
-
|
|
340
|
-
inputSchema = z.object({
|
|
341
|
-
data: z.object({
|
|
342
|
-
name: z.string().optional(),
|
|
343
|
-
age: z.number().optional(),
|
|
344
|
-
preferences: z.record(z.any()).optional()
|
|
345
|
-
})
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
349
|
-
const user = await User.get();
|
|
350
|
-
const updated = await user.update(input.data);
|
|
351
|
-
return {
|
|
352
|
-
success: true,
|
|
353
|
-
message: "Profile updated",
|
|
354
|
-
user: updated
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
---
|
|
361
|
-
|
|
362
|
-
### Example 3: Product Management
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
// File: src/tools/ProductsTool.ts
|
|
366
|
-
import { LuaTool, Products } from "lua-cli";
|
|
367
|
-
import { z } from "zod";
|
|
368
|
-
|
|
369
|
-
export class SearchProductsTool implements LuaTool {
|
|
370
|
-
name = "search_products";
|
|
371
|
-
description = "Search for products by name or description";
|
|
372
|
-
|
|
373
|
-
inputSchema = z.object({
|
|
374
|
-
query: z.string().describe("Search query")
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
378
|
-
const results = await Products.search(input.query);
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
products: results.data.map(p => ({
|
|
382
|
-
id: p.id,
|
|
383
|
-
name: p.name,
|
|
384
|
-
price: p.price,
|
|
385
|
-
inStock: p.inStock
|
|
386
|
-
})),
|
|
387
|
-
count: results.data.length
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
export class CreateProductTool implements LuaTool {
|
|
393
|
-
name = "create_product";
|
|
394
|
-
description = "Add a new product to the catalog";
|
|
395
|
-
|
|
396
|
-
inputSchema = z.object({
|
|
397
|
-
name: z.string(),
|
|
398
|
-
price: z.number().positive(),
|
|
399
|
-
category: z.string().optional(),
|
|
400
|
-
sku: z.string().optional(),
|
|
401
|
-
inStock: z.boolean().default(true)
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
405
|
-
const product = await Products.create(input);
|
|
406
|
-
|
|
407
|
-
return {
|
|
408
|
-
success: true,
|
|
409
|
-
productId: product.product.id,
|
|
410
|
-
message: `Product "${input.name}" created`
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
### Example 4: Shopping Basket Flow
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
// File: src/tools/BasketTool.ts
|
|
422
|
-
import { LuaTool, Baskets, Products, BasketStatus } from "lua-cli";
|
|
423
|
-
import { z } from "zod";
|
|
424
|
-
|
|
425
|
-
export class CreateBasketTool implements LuaTool {
|
|
426
|
-
name = "create_basket";
|
|
427
|
-
description = "Create a new shopping basket";
|
|
428
|
-
|
|
429
|
-
inputSchema = z.object({
|
|
430
|
-
currency: z.string().default('USD')
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
434
|
-
const basket = await Baskets.create({
|
|
435
|
-
currency: input.currency,
|
|
436
|
-
metadata: { createdBy: 'chat' }
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
return {
|
|
440
|
-
basketId: basket.id,
|
|
441
|
-
message: "New basket created"
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
export class AddItemToBasketTool implements LuaTool {
|
|
447
|
-
name = "add_to_basket";
|
|
448
|
-
description = "Add a product to the shopping basket";
|
|
449
|
-
|
|
450
|
-
inputSchema = z.object({
|
|
451
|
-
basketId: z.string(),
|
|
452
|
-
productId: z.string(),
|
|
453
|
-
quantity: z.number().min(1).default(1)
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
457
|
-
// Get product to get price
|
|
458
|
-
const product = await Products.getById(input.productId);
|
|
459
|
-
|
|
460
|
-
// Add to basket
|
|
461
|
-
const updated = await Baskets.addItem(input.basketId, {
|
|
462
|
-
id: input.productId,
|
|
463
|
-
price: product.price,
|
|
464
|
-
quantity: input.quantity,
|
|
465
|
-
SKU: product.sku
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
return {
|
|
469
|
-
basketId: updated.id,
|
|
470
|
-
itemCount: updated.common.itemCount,
|
|
471
|
-
total: updated.common.totalAmount,
|
|
472
|
-
message: `Added ${input.quantity}x ${product.name} to basket`
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
export class CheckoutBasketTool implements LuaTool {
|
|
478
|
-
name = "checkout_basket";
|
|
479
|
-
description = "Convert basket to order";
|
|
480
|
-
|
|
481
|
-
inputSchema = z.object({
|
|
482
|
-
basketId: z.string(),
|
|
483
|
-
shippingAddress: z.object({
|
|
484
|
-
street: z.string(),
|
|
485
|
-
city: z.string(),
|
|
486
|
-
zip: z.string()
|
|
487
|
-
}),
|
|
488
|
-
paymentMethod: z.string().default('stripe')
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
492
|
-
const order = await Baskets.placeOrder({
|
|
493
|
-
shippingAddress: input.shippingAddress,
|
|
494
|
-
paymentMethod: input.paymentMethod
|
|
495
|
-
}, input.basketId);
|
|
496
|
-
|
|
497
|
-
return {
|
|
498
|
-
orderId: order.id,
|
|
499
|
-
status: order.common.status,
|
|
500
|
-
total: order.common.totalAmount,
|
|
501
|
-
message: "Order created successfully"
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
---
|
|
508
|
-
|
|
509
|
-
### Example 5: Custom Data with Vector Search
|
|
510
|
-
|
|
511
|
-
```typescript
|
|
512
|
-
// File: src/tools/CustomDataTool.ts
|
|
513
|
-
import { LuaTool, Data } from "lua-cli";
|
|
514
|
-
import { z } from "zod";
|
|
515
|
-
|
|
516
|
-
export class CreateMovieTool implements LuaTool {
|
|
517
|
-
name = "create_movie";
|
|
518
|
-
description = "Add a new movie to the database";
|
|
519
|
-
|
|
520
|
-
inputSchema = z.object({
|
|
521
|
-
title: z.string(),
|
|
522
|
-
year: z.number(),
|
|
523
|
-
director: z.string(),
|
|
524
|
-
genre: z.string(),
|
|
525
|
-
description: z.string().optional()
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
529
|
-
// Create searchable text for vector search
|
|
530
|
-
const searchText = [
|
|
531
|
-
input.title,
|
|
532
|
-
input.director,
|
|
533
|
-
input.genre,
|
|
534
|
-
input.description
|
|
535
|
-
].filter(Boolean).join(' ');
|
|
536
|
-
|
|
537
|
-
const movie = await Data.create('movies', input, searchText);
|
|
538
|
-
|
|
539
|
-
return {
|
|
540
|
-
id: movie.id,
|
|
541
|
-
message: `Added "${input.title}" to database`
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
export class SearchMoviesTool implements LuaTool {
|
|
547
|
-
name = "search_movies";
|
|
548
|
-
description = "Search movies by title, director, genre, or description";
|
|
549
|
-
|
|
550
|
-
inputSchema = z.object({
|
|
551
|
-
query: z.string().describe("Search query")
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
555
|
-
// Vector search with similarity threshold
|
|
556
|
-
const results = await Data.search('movies', input.query, 10, 0.7);
|
|
557
|
-
|
|
558
|
-
return {
|
|
559
|
-
movies: results.data.map(entry => ({
|
|
560
|
-
id: entry.id,
|
|
561
|
-
title: entry.data.title,
|
|
562
|
-
year: entry.data.year,
|
|
563
|
-
director: entry.data.director,
|
|
564
|
-
relevance: Math.round(entry.score * 100) + '%'
|
|
565
|
-
})),
|
|
566
|
-
count: results.count
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
export class GetMovieByIdTool implements LuaTool {
|
|
572
|
-
name = "get_movie";
|
|
573
|
-
description = "Get detailed information about a specific movie";
|
|
574
|
-
|
|
575
|
-
inputSchema = z.object({
|
|
576
|
-
id: z.string()
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
580
|
-
const movie = await Data.getEntry('movies', input.id);
|
|
581
|
-
return movie.data;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
## Building Your First Skill
|
|
589
|
-
|
|
590
|
-
### Scenario: Build a Task Management Skill
|
|
591
|
-
|
|
592
|
-
**Goal**: Create a skill that manages tasks (create, list, complete, delete).
|
|
593
|
-
|
|
594
|
-
---
|
|
595
|
-
|
|
596
|
-
#### Step 1: Initialize Project
|
|
597
|
-
|
|
598
|
-
```bash
|
|
599
|
-
mkdir task-management-skill
|
|
600
|
-
cd task-management-skill
|
|
601
|
-
lua init
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
---
|
|
605
|
-
|
|
606
|
-
#### Step 2: Clean Up Template
|
|
607
|
-
|
|
608
|
-
Remove unnecessary tools:
|
|
609
|
-
|
|
610
|
-
```bash
|
|
611
|
-
rm src/tools/ProductsTool.ts
|
|
612
|
-
rm src/tools/BasketTool.ts
|
|
613
|
-
rm src/tools/OrderTool.ts
|
|
614
|
-
rm src/tools/PaymentTool.ts
|
|
615
|
-
rm src/tools/GetWeatherTool.ts
|
|
616
|
-
# Keep CustomDataTool.ts as reference
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
---
|
|
620
|
-
|
|
621
|
-
#### Step 3: Create Task Tool
|
|
622
|
-
|
|
623
|
-
Create `src/tools/TaskTool.ts`:
|
|
624
|
-
|
|
625
|
-
```typescript
|
|
626
|
-
import { LuaTool, Data } from "lua-cli";
|
|
627
|
-
import { z } from "zod";
|
|
628
|
-
|
|
629
|
-
export class CreateTaskTool implements LuaTool {
|
|
630
|
-
name = "create_task";
|
|
631
|
-
description = "Create a new task";
|
|
632
|
-
|
|
633
|
-
inputSchema = z.object({
|
|
634
|
-
title: z.string().describe("Task title"),
|
|
635
|
-
description: z.string().optional(),
|
|
636
|
-
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
637
|
-
dueDate: z.string().optional()
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
641
|
-
const task = await Data.create('tasks', {
|
|
642
|
-
...input,
|
|
643
|
-
status: 'pending',
|
|
644
|
-
createdAt: new Date().toISOString()
|
|
645
|
-
}, input.title + ' ' + (input.description || ''));
|
|
646
|
-
|
|
647
|
-
return {
|
|
648
|
-
taskId: task.id,
|
|
649
|
-
message: `Task "${input.title}" created`
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
export class ListTasksTool implements LuaTool {
|
|
655
|
-
name = "list_tasks";
|
|
656
|
-
description = "List all tasks, optionally filtered by status";
|
|
657
|
-
|
|
658
|
-
inputSchema = z.object({
|
|
659
|
-
status: z.enum(['pending', 'in_progress', 'completed']).optional()
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
663
|
-
const filter = input.status ? { status: input.status } : {};
|
|
664
|
-
const result = await Data.get('tasks', filter, 1, 50);
|
|
665
|
-
|
|
666
|
-
return {
|
|
667
|
-
tasks: result.data.map(entry => ({
|
|
668
|
-
id: entry.id,
|
|
669
|
-
...entry.data
|
|
670
|
-
})),
|
|
671
|
-
count: result.pagination.totalCount
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
export class CompleteTaskTool implements LuaTool {
|
|
677
|
-
name = "complete_task";
|
|
678
|
-
description = "Mark a task as completed";
|
|
679
|
-
|
|
680
|
-
inputSchema = z.object({
|
|
681
|
-
taskId: z.string()
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
685
|
-
const task = await Data.getEntry('tasks', input.taskId);
|
|
686
|
-
|
|
687
|
-
await Data.update('tasks', input.taskId, {
|
|
688
|
-
...task.data,
|
|
689
|
-
status: 'completed',
|
|
690
|
-
completedAt: new Date().toISOString()
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
return {
|
|
694
|
-
success: true,
|
|
695
|
-
message: `Task "${task.data.title}" marked as completed`
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
export class SearchTasksTool implements LuaTool {
|
|
701
|
-
name = "search_tasks";
|
|
702
|
-
description = "Search tasks by content";
|
|
703
|
-
|
|
704
|
-
inputSchema = z.object({
|
|
705
|
-
query: z.string()
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
709
|
-
const results = await Data.search('tasks', input.query, 20, 0.6);
|
|
710
|
-
|
|
711
|
-
return {
|
|
712
|
-
tasks: results.data.map(entry => ({
|
|
713
|
-
id: entry.id,
|
|
714
|
-
...entry.data,
|
|
715
|
-
relevance: entry.score
|
|
716
|
-
}))
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
---
|
|
723
|
-
|
|
724
|
-
#### Step 4: Update `src/index.ts`
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
import { LuaSkill } from "lua-cli";
|
|
728
|
-
import {
|
|
729
|
-
CreateTaskTool,
|
|
730
|
-
ListTasksTool,
|
|
731
|
-
CompleteTaskTool,
|
|
732
|
-
SearchTasksTool
|
|
733
|
-
} from "./tools/TaskTool";
|
|
734
|
-
|
|
735
|
-
const taskSkill = new LuaSkill({
|
|
736
|
-
name: "task-management-skill",
|
|
737
|
-
version: "1.0.0",
|
|
738
|
-
description: "Manage tasks and to-do lists with creation, listing, completion, and search",
|
|
739
|
-
context: `
|
|
740
|
-
This skill helps users manage their tasks.
|
|
741
|
-
|
|
742
|
-
- Use create_task when users want to add a new task
|
|
743
|
-
- Use list_tasks to show all tasks or filter by status
|
|
744
|
-
- Use complete_task when users finish a task
|
|
745
|
-
- Use search_tasks to find tasks by content
|
|
746
|
-
|
|
747
|
-
Always confirm task details before creating.
|
|
748
|
-
When listing tasks, organize by priority and due date.
|
|
749
|
-
`,
|
|
750
|
-
tools: [
|
|
751
|
-
new CreateTaskTool(),
|
|
752
|
-
new ListTasksTool(),
|
|
753
|
-
new CompleteTaskTool(),
|
|
754
|
-
new SearchTasksTool()
|
|
755
|
-
]
|
|
756
|
-
});
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
---
|
|
760
|
-
|
|
761
|
-
#### Step 5: Test and Deploy
|
|
762
|
-
|
|
763
|
-
```bash
|
|
764
|
-
# Test individual tools
|
|
765
|
-
lua test
|
|
766
|
-
|
|
767
|
-
# Test conversationally
|
|
768
|
-
lua dev
|
|
769
|
-
|
|
770
|
-
# In the chat:
|
|
771
|
-
# "Create a task to buy milk"
|
|
772
|
-
# "Show me my pending tasks"
|
|
773
|
-
# "Mark the milk task as done"
|
|
774
|
-
|
|
775
|
-
# Deploy
|
|
776
|
-
lua push
|
|
777
|
-
lua deploy
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
---
|
|
781
|
-
|
|
782
|
-
## Multi-Skill Projects
|
|
783
|
-
|
|
784
|
-
You can organize related tools into multiple skills.
|
|
785
|
-
|
|
786
|
-
### When to Use Multiple Skills
|
|
787
|
-
|
|
788
|
-
**Use separate skills when:**
|
|
789
|
-
- ✅ Tools serve different purposes (e.g., "HR Skill" vs "Sales Skill")
|
|
790
|
-
- ✅ Different teams own different skills
|
|
791
|
-
- ✅ Skills have different deployment schedules
|
|
792
|
-
- ✅ Skills need different permissions
|
|
793
|
-
|
|
794
|
-
**Use single skill when:**
|
|
795
|
-
- ✅ Tools work together closely
|
|
796
|
-
- ✅ Small to medium number of tools (< 20)
|
|
797
|
-
- ✅ All tools deploy together
|
|
798
|
-
|
|
799
|
-
### Example: Multi-Skill E-commerce
|
|
800
|
-
|
|
801
|
-
```typescript
|
|
802
|
-
import { LuaSkill } from "lua-cli";
|
|
803
|
-
import { SearchProductsTool, GetProductTool } from "./tools/ProductsTool";
|
|
804
|
-
import { CreateBasketTool, AddItemTool } from "./tools/BasketTool";
|
|
805
|
-
import { CreateOrderTool, TrackOrderTool } from "./tools/OrderTool";
|
|
806
|
-
|
|
807
|
-
// Product browsing skill
|
|
808
|
-
const catalogSkill = new LuaSkill({
|
|
809
|
-
name: "product-catalog-skill",
|
|
810
|
-
version: "1.0.0",
|
|
811
|
-
description: "Product browsing and search",
|
|
812
|
-
context: "Use search_products to find items. Use get_product for details.",
|
|
813
|
-
tools: [
|
|
814
|
-
new SearchProductsTool(),
|
|
815
|
-
new GetProductTool()
|
|
816
|
-
]
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
// Shopping skill
|
|
820
|
-
const shoppingSkill = new LuaSkill({
|
|
821
|
-
name: "shopping-skill",
|
|
822
|
-
version: "1.0.0",
|
|
823
|
-
description: "Shopping cart management",
|
|
824
|
-
context: "Use create_basket to start shopping. Use add_item to add products.",
|
|
825
|
-
tools: [
|
|
826
|
-
new CreateBasketTool(),
|
|
827
|
-
new AddItemTool()
|
|
828
|
-
]
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
// Order fulfillment skill
|
|
832
|
-
const orderSkill = new LuaSkill({
|
|
833
|
-
name: "order-skill",
|
|
834
|
-
version: "1.0.0",
|
|
835
|
-
description: "Order creation and tracking",
|
|
836
|
-
context: "Use create_order to finalize purchase. Use track_order for status.",
|
|
837
|
-
tools: [
|
|
838
|
-
new CreateOrderTool(),
|
|
839
|
-
new TrackOrderTool()
|
|
840
|
-
]
|
|
841
|
-
});
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
**Deployment:**
|
|
845
|
-
- Each skill gets its own `skillId` in `lua.skill.yaml`
|
|
846
|
-
- All skills deploy together when you run `lua push`
|
|
847
|
-
- Can activate/deactivate individual skills
|
|
848
|
-
|
|
849
|
-
---
|
|
850
|
-
|
|
851
|
-
## Best Practices
|
|
852
|
-
|
|
853
|
-
### Tool Design Principles
|
|
854
|
-
|
|
855
|
-
#### 1. Single Responsibility
|
|
856
|
-
|
|
857
|
-
**✅ Good:**
|
|
858
|
-
```typescript
|
|
859
|
-
// One focused task
|
|
860
|
-
class GetWeatherTool { ... }
|
|
861
|
-
class CreateProductTool { ... }
|
|
862
|
-
```
|
|
863
|
-
|
|
864
|
-
**❌ Bad:**
|
|
865
|
-
```typescript
|
|
866
|
-
// Too many responsibilities
|
|
867
|
-
class DoEverythingTool {
|
|
868
|
-
// Gets weather, creates products, sends emails...
|
|
869
|
-
}
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
---
|
|
873
|
-
|
|
874
|
-
#### 2. Clear Input/Output
|
|
875
|
-
|
|
876
|
-
**✅ Good:**
|
|
877
|
-
```typescript
|
|
878
|
-
inputSchema = z.object({
|
|
879
|
-
city: z.string().describe("City name (e.g., 'London', 'Tokyo')"),
|
|
880
|
-
units: z.enum(['metric', 'imperial']).describe("Temperature units")
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
// Returns structured data
|
|
884
|
-
return {
|
|
885
|
-
city: "London",
|
|
886
|
-
temperature: 15.2,
|
|
887
|
-
condition: "cloudy"
|
|
888
|
-
};
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
**❌ Bad:**
|
|
892
|
-
```typescript
|
|
893
|
-
inputSchema = z.object({
|
|
894
|
-
input: z.string() // Unclear what this is
|
|
895
|
-
});
|
|
896
|
-
|
|
897
|
-
// Returns unstructured string
|
|
898
|
-
return "The weather in London is 15.2 degrees and cloudy";
|
|
899
|
-
```
|
|
900
|
-
|
|
901
|
-
---
|
|
902
|
-
|
|
903
|
-
#### 3. Error Messages
|
|
904
|
-
|
|
905
|
-
**✅ Good:**
|
|
906
|
-
```typescript
|
|
907
|
-
if (!city) {
|
|
908
|
-
throw new Error("City parameter is required");
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
if (results.length === 0) {
|
|
912
|
-
throw new Error(`No products found matching "${query}"`);
|
|
913
|
-
}
|
|
914
|
-
```
|
|
915
|
-
|
|
916
|
-
**❌ Bad:**
|
|
917
|
-
```typescript
|
|
918
|
-
throw new Error("Error");
|
|
919
|
-
throw new Error("Invalid input");
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
---
|
|
923
|
-
|
|
924
|
-
### Context Writing Tips
|
|
925
|
-
|
|
926
|
-
The `context` field guides the AI. Write it well!
|
|
927
|
-
|
|
928
|
-
**Structure:**
|
|
929
|
-
```typescript
|
|
930
|
-
context: `
|
|
931
|
-
[Brief overview of what the skill does]
|
|
932
|
-
|
|
933
|
-
Tool Usage:
|
|
934
|
-
- tool_1: When to use it, what it does
|
|
935
|
-
- tool_2: When to use it, what it does
|
|
936
|
-
|
|
937
|
-
Important Notes:
|
|
938
|
-
- Special considerations
|
|
939
|
-
- Edge cases
|
|
940
|
-
- Limitations
|
|
941
|
-
`
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
**Example:**
|
|
945
|
-
```typescript
|
|
946
|
-
context: `
|
|
947
|
-
This skill manages a product catalog and shopping experience.
|
|
948
|
-
|
|
949
|
-
Tool Usage:
|
|
950
|
-
- search_products: Use when users want to find or browse items
|
|
951
|
-
- create_product: Use when adding new items to catalog (admin only)
|
|
952
|
-
- create_basket: Use when starting a new shopping session
|
|
953
|
-
- add_to_basket: Use when users want to add items to cart
|
|
954
|
-
- checkout_basket: Use when users are ready to complete purchase
|
|
955
|
-
|
|
956
|
-
Important Notes:
|
|
957
|
-
- Always show prices with currency symbols
|
|
958
|
-
- Confirm order details before checkout
|
|
959
|
-
- Check product availability before adding to basket
|
|
960
|
-
- Baskets expire after 24 hours of inactivity
|
|
961
|
-
`
|
|
962
|
-
```
|
|
963
|
-
|
|
964
|
-
---
|
|
965
|
-
|
|
966
|
-
## Environment Variables
|
|
967
|
-
|
|
968
|
-
### Setup
|
|
969
|
-
|
|
970
|
-
**Option 1: `.env` file** (recommended for local development)
|
|
971
|
-
```bash
|
|
972
|
-
# .env
|
|
973
|
-
STRIPE_API_KEY=sk_test_abc123
|
|
974
|
-
SENDGRID_API_KEY=SG.xyz789
|
|
975
|
-
EXTERNAL_API_URL=https://api.example.com
|
|
976
|
-
```
|
|
977
|
-
|
|
978
|
-
**Option 2: `lua.skill.yaml`** (for deployed environments)
|
|
979
|
-
```yaml
|
|
980
|
-
skill:
|
|
981
|
-
env:
|
|
982
|
-
STRIPE_API_KEY: sk_live_abc123
|
|
983
|
-
SENDGRID_API_KEY: SG.xyz789
|
|
984
|
-
EXTERNAL_API_URL: https://api.example.com
|
|
985
|
-
```
|
|
986
|
-
|
|
987
|
-
### Using in Tools
|
|
988
|
-
|
|
989
|
-
```typescript
|
|
990
|
-
import { env } from 'lua-cli';
|
|
991
|
-
|
|
992
|
-
export class PaymentTool implements LuaTool {
|
|
993
|
-
async execute(input: any) {
|
|
994
|
-
const stripeKey = env('STRIPE_API_KEY');
|
|
995
|
-
|
|
996
|
-
if (!stripeKey) {
|
|
997
|
-
throw new Error('Payment system not configured. Please set STRIPE_API_KEY.');
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
// Use the API key...
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
```
|
|
1004
|
-
|
|
1005
|
-
### Security
|
|
1006
|
-
|
|
1007
|
-
**✅ Do:**
|
|
1008
|
-
- Store API keys in environment variables
|
|
1009
|
-
- Use `.env` file for local development
|
|
1010
|
-
- Add `.env` to `.gitignore`
|
|
1011
|
-
- Use `lua.skill.yaml` for deployed secrets
|
|
1012
|
-
|
|
1013
|
-
**❌ Don't:**
|
|
1014
|
-
- Hardcode API keys in code
|
|
1015
|
-
- Commit `.env` to git
|
|
1016
|
-
- Share API keys in documentation
|
|
1017
|
-
|
|
1018
|
-
---
|
|
1019
|
-
|
|
1020
|
-
## File Organization Tips
|
|
1021
|
-
|
|
1022
|
-
### Small Project (< 5 tools)
|
|
1023
|
-
|
|
1024
|
-
```
|
|
1025
|
-
src/
|
|
1026
|
-
├── index.ts
|
|
1027
|
-
└── tools/
|
|
1028
|
-
└── MyTools.ts # All tools in one file
|
|
1029
|
-
```
|
|
1030
|
-
|
|
1031
|
-
### Medium Project (5-15 tools)
|
|
1032
|
-
|
|
1033
|
-
```
|
|
1034
|
-
src/
|
|
1035
|
-
├── index.ts
|
|
1036
|
-
└── tools/
|
|
1037
|
-
├── WeatherTools.ts # Weather-related tools
|
|
1038
|
-
├── UserTools.ts # User-related tools
|
|
1039
|
-
└── ProductTools.ts # Product-related tools
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
### Large Project (> 15 tools)
|
|
1043
|
-
|
|
1044
|
-
```
|
|
1045
|
-
src/
|
|
1046
|
-
├── index.ts
|
|
1047
|
-
├── tools/
|
|
1048
|
-
│ ├── weather/
|
|
1049
|
-
│ │ ├── GetWeather.ts
|
|
1050
|
-
│ │ └── GetForecast.ts
|
|
1051
|
-
│ ├── products/
|
|
1052
|
-
│ │ ├── Search.ts
|
|
1053
|
-
│ │ ├── Create.ts
|
|
1054
|
-
│ │ └── Update.ts
|
|
1055
|
-
│ └── orders/
|
|
1056
|
-
│ ├── Create.ts
|
|
1057
|
-
│ └── Track.ts
|
|
1058
|
-
└── services/ # Shared utilities
|
|
1059
|
-
├── WeatherApi.ts
|
|
1060
|
-
└── PaymentApi.ts
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
---
|
|
1064
|
-
|
|
1065
|
-
## Testing Strategies
|
|
1066
|
-
|
|
1067
|
-
### 1. Unit Testing Tools
|
|
1068
|
-
|
|
1069
|
-
```typescript
|
|
1070
|
-
import { describe, it, expect } from '@jest/globals';
|
|
1071
|
-
import GetWeatherTool from './tools/GetWeatherTool';
|
|
1072
|
-
|
|
1073
|
-
describe('GetWeatherTool', () => {
|
|
1074
|
-
it('should return weather data for valid city', async () => {
|
|
1075
|
-
const tool = new GetWeatherTool();
|
|
1076
|
-
const result = await tool.execute({ city: 'London' });
|
|
1077
|
-
|
|
1078
|
-
expect(result).toHaveProperty('temperature');
|
|
1079
|
-
expect(result).toHaveProperty('city');
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
it('should throw error for invalid city', async () => {
|
|
1083
|
-
const tool = new GetWeatherTool();
|
|
1084
|
-
|
|
1085
|
-
await expect(
|
|
1086
|
-
tool.execute({ city: 'InvalidCityXYZ123' })
|
|
1087
|
-
).rejects.toThrow('City not found');
|
|
1088
|
-
});
|
|
1089
|
-
});
|
|
1090
|
-
```
|
|
1091
|
-
|
|
1092
|
-
---
|
|
1093
|
-
|
|
1094
|
-
### 2. Interactive Testing
|
|
1095
|
-
|
|
1096
|
-
```bash
|
|
1097
|
-
lua test
|
|
1098
|
-
```
|
|
1099
|
-
Select tool → Enter inputs → See results
|
|
1100
|
-
|
|
1101
|
-
---
|
|
1102
|
-
|
|
1103
|
-
### 3. Conversational Testing
|
|
1104
|
-
|
|
1105
|
-
```bash
|
|
1106
|
-
lua dev
|
|
1107
|
-
```
|
|
1108
|
-
Open http://localhost:3000 → Chat with AI → Test natural language interaction
|
|
1109
|
-
|
|
1110
|
-
---
|
|
1111
|
-
|
|
1112
|
-
## Common Patterns
|
|
1113
|
-
|
|
1114
|
-
### Pattern: Pagination Helper
|
|
1115
|
-
|
|
1116
|
-
```typescript
|
|
1117
|
-
async execute(input: any) {
|
|
1118
|
-
const page = input.page || 1;
|
|
1119
|
-
const limit = input.limit || 20;
|
|
1120
|
-
|
|
1121
|
-
const results = await Data.get('collection', {}, page, limit);
|
|
1122
|
-
|
|
1123
|
-
return {
|
|
1124
|
-
items: results.data,
|
|
1125
|
-
pagination: {
|
|
1126
|
-
current: results.pagination.currentPage,
|
|
1127
|
-
total: results.pagination.totalPages,
|
|
1128
|
-
hasMore: results.pagination.hasNextPage
|
|
1129
|
-
}
|
|
1130
|
-
};
|
|
1131
|
-
}
|
|
1132
|
-
```
|
|
1133
|
-
|
|
1134
|
-
---
|
|
1135
|
-
|
|
1136
|
-
### Pattern: Input Validation
|
|
1137
|
-
|
|
1138
|
-
```typescript
|
|
1139
|
-
async execute(input: any) {
|
|
1140
|
-
// Zod handles schema validation
|
|
1141
|
-
// Add business logic validation
|
|
1142
|
-
|
|
1143
|
-
if (input.amount > 10000) {
|
|
1144
|
-
throw new Error('Amount exceeds maximum limit of $10,000');
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
if (input.email && !await this.isValidEmail(input.email)) {
|
|
1148
|
-
throw new Error('Email domain not allowed');
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
// Proceed with logic...
|
|
1152
|
-
}
|
|
1153
|
-
```
|
|
1154
|
-
|
|
1155
|
-
---
|
|
1156
|
-
|
|
1157
|
-
### Pattern: Error Recovery
|
|
1158
|
-
|
|
1159
|
-
```typescript
|
|
1160
|
-
async execute(input: any) {
|
|
1161
|
-
try {
|
|
1162
|
-
// Try primary method
|
|
1163
|
-
return await primaryService.call(input);
|
|
1164
|
-
} catch (error) {
|
|
1165
|
-
console.warn('Primary service failed, trying fallback');
|
|
1166
|
-
|
|
1167
|
-
try {
|
|
1168
|
-
// Try fallback
|
|
1169
|
-
return await fallbackService.call(input);
|
|
1170
|
-
} catch (fallbackError) {
|
|
1171
|
-
// Both failed
|
|
1172
|
-
throw new Error('Service unavailable. Please try again later.');
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
```
|
|
1177
|
-
|
|
1178
|
-
---
|
|
1179
|
-
|
|
1180
|
-
### Pattern: Response Formatting
|
|
1181
|
-
|
|
1182
|
-
```typescript
|
|
1183
|
-
async execute(input: any) {
|
|
1184
|
-
const products = await Products.search(input.query);
|
|
1185
|
-
|
|
1186
|
-
// Format for AI consumption
|
|
1187
|
-
return {
|
|
1188
|
-
summary: `Found ${products.data.length} products`,
|
|
1189
|
-
products: products.data.map(p => ({
|
|
1190
|
-
name: p.name,
|
|
1191
|
-
price: `$${p.price.toFixed(2)}`,
|
|
1192
|
-
availability: p.inStock ? '✅ In Stock' : '❌ Out of Stock',
|
|
1193
|
-
link: `https://shop.example.com/products/${p.id}`
|
|
1194
|
-
}))
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
```
|
|
1198
|
-
|
|
1199
|
-
---
|
|
1200
|
-
|
|
1201
|
-
## Deployment Checklist
|
|
1202
|
-
|
|
1203
|
-
Before deploying your skill:
|
|
1204
|
-
|
|
1205
|
-
- [ ] All tools have clear names and descriptions
|
|
1206
|
-
- [ ] Input schemas use `.describe()` for fields
|
|
1207
|
-
- [ ] Error messages are helpful and specific
|
|
1208
|
-
- [ ] Context provides clear usage guidelines
|
|
1209
|
-
- [ ] Environment variables are configured
|
|
1210
|
-
- [ ] Tools tested with `lua test`
|
|
1211
|
-
- [ ] Conversational flow tested with `lua dev`
|
|
1212
|
-
- [ ] No hardcoded secrets or API keys
|
|
1213
|
-
- [ ] Version number updated in `lua.skill.yaml`
|
|
1214
|
-
|
|
1215
|
-
---
|
|
1216
|
-
|
|
1217
|
-
## Migration from Template
|
|
1218
|
-
|
|
1219
|
-
### Customize for Your Use Case
|
|
1220
|
-
|
|
1221
|
-
1. **Identify needed APIs**: User, Products, Baskets, Orders, Custom Data?
|
|
1222
|
-
2. **Remove unused tools**: Delete files you don't need
|
|
1223
|
-
3. **Customize remaining tools**: Modify for your domain
|
|
1224
|
-
4. **Update descriptions**: Change to match your business
|
|
1225
|
-
5. **Test thoroughly**: Use `lua test` and `lua dev`
|
|
1226
|
-
|
|
1227
|
-
### Template to Production
|
|
1228
|
-
|
|
1229
|
-
**Template Example:**
|
|
1230
|
-
```typescript
|
|
1231
|
-
description = "A comprehensive skill with weather and utilities"
|
|
1232
|
-
```
|
|
1233
|
-
|
|
1234
|
-
**Your Production Version:**
|
|
1235
|
-
```typescript
|
|
1236
|
-
description = "Coffee shop assistant with menu, ordering, and loyalty features"
|
|
1237
|
-
```
|
|
1238
|
-
|
|
1239
|
-
**Template Example:**
|
|
1240
|
-
```typescript
|
|
1241
|
-
context = "Use get_weather for weather info..."
|
|
1242
|
-
```
|
|
1243
|
-
|
|
1244
|
-
**Your Production Version:**
|
|
1245
|
-
```typescript
|
|
1246
|
-
context = `
|
|
1247
|
-
This skill helps customers of Java Junction Coffee Shop.
|
|
1248
|
-
|
|
1249
|
-
- Use search_menu to show available drinks and food
|
|
1250
|
-
- Use create_order to take customer orders
|
|
1251
|
-
- Use check_loyalty_points to show rewards balance
|
|
1252
|
-
- Use redeem_reward to apply loyalty discounts
|
|
1253
|
-
|
|
1254
|
-
Always mention daily specials.
|
|
1255
|
-
Ask about size preferences for drinks.
|
|
1256
|
-
Suggest food pairings with drinks.
|
|
1257
|
-
`
|
|
1258
|
-
```
|
|
1259
|
-
|
|
1260
|
-
---
|
|
1261
|
-
|
|
1262
|
-
## Advanced Topics
|
|
1263
|
-
|
|
1264
|
-
### Custom Services
|
|
1265
|
-
|
|
1266
|
-
Create helper services in `src/services/`:
|
|
1267
|
-
|
|
1268
|
-
```typescript
|
|
1269
|
-
// src/services/PaymentService.ts
|
|
1270
|
-
import { env } from 'lua-cli';
|
|
1271
|
-
|
|
1272
|
-
export class PaymentService {
|
|
1273
|
-
private apiKey: string;
|
|
1274
|
-
|
|
1275
|
-
constructor() {
|
|
1276
|
-
this.apiKey = env('STRIPE_API_KEY') || '';
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
async createPaymentIntent(amount: number) {
|
|
1280
|
-
// Stripe integration...
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
async refund(paymentId: string) {
|
|
1284
|
-
// Refund logic...
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
```
|
|
1288
|
-
|
|
1289
|
-
Use in tools:
|
|
1290
|
-
```typescript
|
|
1291
|
-
import { PaymentService } from '../services/PaymentService';
|
|
1292
|
-
|
|
1293
|
-
export class ProcessPaymentTool implements LuaTool {
|
|
1294
|
-
private paymentService = new PaymentService();
|
|
1295
|
-
|
|
1296
|
-
async execute(input: any) {
|
|
1297
|
-
return await this.paymentService.createPaymentIntent(input.amount);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
```
|
|
1301
|
-
|
|
1302
|
-
---
|
|
1303
|
-
|
|
1304
|
-
### Shared Utilities
|
|
1305
|
-
|
|
1306
|
-
Create utilities in `src/utils/`:
|
|
1307
|
-
|
|
1308
|
-
```typescript
|
|
1309
|
-
// src/utils/validation.ts
|
|
1310
|
-
export function isValidEmail(email: string): boolean {
|
|
1311
|
-
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
export function formatCurrency(amount: number, currency: string = 'USD'): string {
|
|
1315
|
-
return new Intl.NumberFormat('en-US', {
|
|
1316
|
-
style: 'currency',
|
|
1317
|
-
currency
|
|
1318
|
-
}).format(amount);
|
|
1319
|
-
}
|
|
1320
|
-
```
|
|
1321
|
-
|
|
1322
|
-
---
|
|
1323
|
-
|
|
1324
|
-
## Troubleshooting
|
|
1325
|
-
|
|
1326
|
-
### "Tool name invalid"
|
|
1327
|
-
```
|
|
1328
|
-
Error: Tool names can only contain alphanumeric characters, hyphens (-), and underscores (_).
|
|
1329
|
-
```
|
|
1330
|
-
**Fix:** Rename tool to use only allowed characters.
|
|
1331
|
-
|
|
1332
|
-
### "Cannot find module 'lua-cli'"
|
|
1333
|
-
```
|
|
1334
|
-
Error: Cannot find module 'lua-cli'
|
|
1335
|
-
```
|
|
1336
|
-
**Fix:** Run `npm install` in your project directory.
|
|
1337
|
-
|
|
1338
|
-
### "No API key found"
|
|
1339
|
-
```
|
|
1340
|
-
Error: No API key found. Please run "lua auth configure" first.
|
|
1341
|
-
```
|
|
1342
|
-
**Fix:** Tools using platform APIs need authentication. Run `lua auth configure`.
|
|
1343
|
-
|
|
1344
|
-
---
|
|
1345
|
-
|
|
1346
|
-
## Resources
|
|
1347
|
-
|
|
1348
|
-
- **CLI Commands**: `CLI_REFERENCE.md`
|
|
1349
|
-
- **Developer Guide**: `DEVELOPER_GUIDE.md`
|
|
1350
|
-
- **Example Tools**: `template/src/tools/`
|
|
1351
|
-
- **Platform Docs**: https://docs.heylua.ai
|
|
1352
|
-
|
|
1353
|
-
---
|
|
1354
|
-
|
|
1355
|
-
## Quick Reference
|
|
1356
|
-
|
|
1357
|
-
### Essential Imports
|
|
1358
|
-
|
|
1359
|
-
```typescript
|
|
1360
|
-
import { LuaSkill, LuaTool, env } from 'lua-cli';
|
|
1361
|
-
import { User, Data, Products, Baskets, Orders } from 'lua-cli';
|
|
1362
|
-
import { BasketStatus, OrderStatus } from 'lua-cli';
|
|
1363
|
-
import { z } from 'zod';
|
|
1364
|
-
```
|
|
1365
|
-
|
|
1366
|
-
### Basic Tool Template
|
|
1367
|
-
|
|
1368
|
-
```typescript
|
|
1369
|
-
export default class MyTool implements LuaTool {
|
|
1370
|
-
name = "my_tool";
|
|
1371
|
-
description = "What it does";
|
|
1372
|
-
inputSchema = z.object({
|
|
1373
|
-
param: z.string()
|
|
1374
|
-
});
|
|
1375
|
-
|
|
1376
|
-
async execute(input: z.infer<typeof this.inputSchema>) {
|
|
1377
|
-
// Logic here
|
|
1378
|
-
return { result: "success" };
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
```
|
|
1382
|
-
|
|
1383
|
-
### Basic Skill Template
|
|
1384
|
-
|
|
1385
|
-
```typescript
|
|
1386
|
-
const skill = new LuaSkill({
|
|
1387
|
-
name: "my-skill",
|
|
1388
|
-
version: "1.0.0",
|
|
1389
|
-
description: "Brief description",
|
|
1390
|
-
context: "Detailed usage instructions for AI",
|
|
1391
|
-
tools: [new MyTool()]
|
|
1392
|
-
});
|
|
1393
|
-
```
|
|
1394
|
-
|
|
1395
|
-
---
|
|
1396
|
-
|
|
1397
|
-
**Happy building! 🚀**
|
|
1398
|
-
|