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.
Files changed (61) hide show
  1. package/dist/api/job.api.service.d.ts +16 -7
  2. package/dist/api/job.api.service.js +21 -5
  3. package/dist/api/postprocessor.api.service.d.ts +61 -1
  4. package/dist/api/postprocessor.api.service.js +35 -0
  5. package/dist/api/preprocessor.api.service.d.ts +61 -1
  6. package/dist/api/preprocessor.api.service.js +35 -0
  7. package/dist/api-exports.d.ts +26 -6
  8. package/dist/api-exports.js +42 -29
  9. package/dist/cli/command-definitions.js +13 -6
  10. package/dist/commands/chat.js +32 -5
  11. package/dist/commands/compile.js +16 -2
  12. package/dist/commands/dev.js +23 -2
  13. package/dist/commands/push.d.ts +6 -2
  14. package/dist/commands/push.js +412 -6
  15. package/dist/commands/test.js +18 -2
  16. package/dist/common/job.instance.d.ts +3 -0
  17. package/dist/common/job.instance.js +8 -0
  18. package/dist/config/constants.d.ts +6 -5
  19. package/dist/config/constants.js +12 -10
  20. package/dist/interfaces/chat.d.ts +30 -1
  21. package/dist/interfaces/jobs.d.ts +21 -0
  22. package/dist/types/skill.d.ts +75 -56
  23. package/dist/types/skill.js +53 -59
  24. package/dist/utils/bundling.d.ts +13 -4
  25. package/dist/utils/bundling.js +83 -26
  26. package/dist/utils/compile.js +27 -6
  27. package/dist/utils/dev-api.d.ts +42 -2
  28. package/dist/utils/dev-api.js +177 -4
  29. package/dist/utils/dev-server.d.ts +1 -1
  30. package/dist/utils/dev-server.js +4 -4
  31. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  32. package/dist/utils/dynamic-job-bundler.js +143 -0
  33. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  34. package/dist/utils/pre-bundle-jobs.js +176 -0
  35. package/dist/utils/sandbox-storage.d.ts +48 -0
  36. package/dist/utils/sandbox-storage.js +114 -0
  37. package/dist/utils/sandbox.d.ts +2 -2
  38. package/dist/utils/sandbox.js +23 -7
  39. package/package.json +1 -1
  40. package/template/lua.skill.yaml +47 -0
  41. package/template/package-lock.json +10505 -0
  42. package/template/package.json +2 -1
  43. package/template/src/index.ts +65 -3
  44. package/template/src/tools/CreateInlineJob.ts +42 -0
  45. package/API_REFERENCE.md +0 -1408
  46. package/CHANGELOG.md +0 -236
  47. package/CLI_REFERENCE.md +0 -908
  48. package/GETTING_STARTED.md +0 -1040
  49. package/INSTANCE_TYPES.md +0 -1158
  50. package/README.md +0 -865
  51. package/TEMPLATE_GUIDE.md +0 -1398
  52. package/USER_DATA_INSTANCE.md +0 -621
  53. package/template/AGENT_CONFIGURATION.md +0 -251
  54. package/template/COMPLEX_JOB_EXAMPLES.md +0 -795
  55. package/template/DYNAMIC_JOB_CREATION.md +0 -371
  56. package/template/TOOL_EXAMPLES.md +0 -655
  57. package/template/WEBHOOKS_JOBS_QUICKSTART.md +0 -318
  58. package/template/WEBHOOK_JOB_EXAMPLES.md +0 -817
  59. package/template/src/index-agent-example.ts +0 -201
  60. package/template/src/postprocessors/ResponseFormatter.ts +0 -151
  61. 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}&current_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
-