lua-cli 1.3.2-alpha.0 → 1.3.2-alpha.2

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.
@@ -17,6 +17,11 @@ function decompressCode(compressedCode) {
17
17
  export async function compileCommand() {
18
18
  return withErrorHandling(async () => {
19
19
  writeProgress("🔨 Compiling Lua skill...");
20
+ // Clean up old .lua directory
21
+ const oldLuaDir = path.join(process.cwd(), ".lua");
22
+ if (fs.existsSync(oldLuaDir)) {
23
+ fs.rmSync(oldLuaDir, { recursive: true, force: true });
24
+ }
20
25
  // Read package.json to get version and name
21
26
  const packageJsonPath = path.join(process.cwd(), "package.json");
22
27
  if (!fs.existsSync(packageJsonPath)) {
@@ -79,21 +84,31 @@ async function extractSkillInfo(indexContent, indexFilePath) {
79
84
  const sourceFile = ts.createSourceFile(indexFilePath, indexContent, ts.ScriptTarget.Latest, true);
80
85
  // Traverse the AST to find skill.addTool() calls
81
86
  function visit(node) {
82
- // Check for skill.addTool() calls
87
+ // Check for skill.addTool() and skill.addTools() calls
83
88
  if (ts.isCallExpression(node) &&
84
89
  ts.isPropertyAccessExpression(node.expression) &&
85
90
  ts.isIdentifier(node.expression.expression) &&
86
91
  node.expression.expression.text === 'skill' &&
87
92
  ts.isIdentifier(node.expression.name) &&
88
- node.expression.name.text === 'addTool') {
93
+ (node.expression.name.text === 'addTool' || node.expression.name.text === 'addTools')) {
89
94
  // Check if this call is commented out
90
95
  if (isNodeCommentedOut(node, sourceFile)) {
91
96
  return;
92
97
  }
93
- // Collect the tool argument for later processing
94
- if (node.arguments.length > 0) {
95
- const toolArgument = node.arguments[0];
96
- toolArguments.push(toolArgument);
98
+ // Handle both addTool and addTools calls
99
+ if (node.expression.name.text === 'addTool') {
100
+ // Single tool call
101
+ if (node.arguments.length > 0) {
102
+ toolArguments.push(node.arguments[0]);
103
+ }
104
+ }
105
+ else if (node.expression.name.text === 'addTools') {
106
+ // Multiple tools call - add each tool from the array
107
+ if (node.arguments.length > 0 && ts.isArrayLiteralExpression(node.arguments[0])) {
108
+ for (const element of node.arguments[0].elements) {
109
+ toolArguments.push(element);
110
+ }
111
+ }
97
112
  }
98
113
  }
99
114
  // Continue traversing
@@ -106,9 +121,8 @@ async function extractSkillInfo(indexContent, indexFilePath) {
106
121
  }
107
122
  }
108
123
  catch (error) {
109
- console.warn('Warning: Could not parse TypeScript AST, falling back to regex approach:', error);
110
- // Fallback to the old regex approach if AST parsing fails
111
- return await extractSkillInfoRegex(indexContent, indexFilePath);
124
+ console.warn('Warning: Could not parse TypeScript AST:', error);
125
+ return [];
112
126
  }
113
127
  return tools;
114
128
  }
@@ -152,9 +166,6 @@ async function processInlineTool(objectNode, sourceFile, indexContent, tools) {
152
166
  else if (key === 'inputSchema' && ts.isIdentifier(value)) {
153
167
  toolProperties.inputSchemaVar = value.text;
154
168
  }
155
- else if (key === 'outputSchema' && ts.isIdentifier(value)) {
156
- toolProperties.outputSchemaVar = value.text;
157
- }
158
169
  else if (key === 'execute' && ts.isArrowFunction(value)) {
159
170
  // Extract the function body
160
171
  if (ts.isBlock(value.body)) {
@@ -168,16 +179,14 @@ async function processInlineTool(objectNode, sourceFile, indexContent, tools) {
168
179
  if (toolProperties.name &&
169
180
  toolProperties.description &&
170
181
  toolProperties.inputSchemaVar &&
171
- toolProperties.outputSchemaVar &&
172
182
  toolProperties.executeBody) {
173
- const inputSchema = convertSchemaToJSON(toolProperties.inputSchemaVar, indexContent);
174
- const outputSchema = convertSchemaToJSON(toolProperties.outputSchemaVar, indexContent);
183
+ // For inline tools, we need to find the schema definitions in the index file
184
+ const inputSchema = extractSchemaFromIndex(toolProperties.inputSchemaVar, indexContent);
175
185
  const selfContainedExecute = await createSelfContainedExecute(toolProperties.executeBody, indexContent);
176
186
  tools.push({
177
187
  name: toolProperties.name,
178
188
  description: toolProperties.description,
179
- inputSchema,
180
- outputSchema,
189
+ inputSchema: inputSchema || { type: "object" },
181
190
  execute: selfContainedExecute
182
191
  });
183
192
  }
@@ -203,142 +212,149 @@ function extractFunctionBody(blockStatement, sourceFile) {
203
212
  }
204
213
  return fullText;
205
214
  }
206
- // Fallback regex-based extraction (renamed from the original function)
207
- async function extractSkillInfoRegex(indexContent, indexFilePath) {
208
- const tools = [];
209
- // Find all addTool calls by searching for the pattern and manually parsing
210
- let searchIndex = 0;
211
- while (true) {
212
- const addToolIndex = indexContent.indexOf('skill.addTool(', searchIndex);
213
- if (addToolIndex === -1)
214
- break;
215
- // Check if this line is commented out
216
- const lineStart = indexContent.lastIndexOf('\n', addToolIndex) + 1;
217
- const lineContent = indexContent.substring(lineStart, addToolIndex).trim();
218
- if (lineContent.startsWith('//') || lineContent.startsWith('*') || lineContent.startsWith('#')) {
219
- searchIndex = addToolIndex + 1;
220
- continue;
221
- }
222
- // Find the matching closing parenthesis
223
- let parenCount = 0;
224
- let braceCount = 0;
225
- let callStart = addToolIndex + 'skill.addTool('.length;
226
- let callEnd = callStart;
227
- let found = false;
228
- for (let i = callStart; i < indexContent.length; i++) {
229
- const char = indexContent[i];
230
- if (char === '(')
231
- parenCount++;
232
- else if (char === ')') {
233
- if (parenCount === 0) {
234
- callEnd = i;
235
- found = true;
236
- break;
237
- }
238
- parenCount--;
239
- }
240
- else if (char === '{')
241
- braceCount++;
242
- else if (char === '}')
243
- braceCount--;
244
- }
245
- if (!found) {
246
- searchIndex = addToolIndex + 1;
247
- continue;
248
- }
249
- const toolArgument = indexContent.substring(callStart, callEnd).trim();
250
- // Check if it's an inline tool definition: skill.addTool({...})
251
- if (toolArgument.startsWith('{')) {
252
- // Extract tool properties
253
- const nameMatch = toolArgument.match(/name:\s*["']([^"']+)["']/);
254
- const descriptionMatch = toolArgument.match(/description:\s*["']([^"']+)["']/);
255
- const inputSchemaMatch = toolArgument.match(/inputSchema:\s*(\w+)/);
256
- const outputSchemaMatch = toolArgument.match(/outputSchema:\s*(\w+)/);
257
- const executeMatch = toolArgument.match(/execute:\s*async\s*\([^)]*\)\s*=>\s*\{([\s\S]*?)\}/);
258
- if (nameMatch && descriptionMatch && inputSchemaMatch && outputSchemaMatch && executeMatch) {
259
- const toolName = nameMatch[1];
260
- const toolDescription = descriptionMatch[1];
261
- const inputSchemaVar = inputSchemaMatch[1];
262
- const outputSchemaVar = outputSchemaMatch[1];
263
- const executeBody = executeMatch[1];
264
- // Convert schemas to JSON Schema format
265
- const inputSchema = convertSchemaToJSON(inputSchemaVar, indexContent);
266
- const outputSchema = convertSchemaToJSON(outputSchemaVar, indexContent);
267
- // Create self-contained execute function
268
- const selfContainedExecute = await createSelfContainedExecute(executeBody, indexContent);
269
- tools.push({
270
- name: toolName,
271
- description: toolDescription,
272
- inputSchema,
273
- outputSchema,
274
- execute: selfContainedExecute
275
- });
276
- }
277
- }
278
- // Check if it's a class-based tool: skill.addTool(new SomeTool())
279
- else if (toolArgument.startsWith('new ')) {
280
- const classMatch = toolArgument.match(/new\s+(\w+)\(\)/);
281
- if (classMatch) {
282
- const className = classMatch[1];
283
- // Find the tool class definition
284
- const toolInfo = await extractToolFromClass(className, indexContent, indexFilePath);
285
- if (toolInfo) {
286
- tools.push(toolInfo);
215
+ function convertZodTypeToJSONSchema(zodType) {
216
+ switch (zodType) {
217
+ case 'string':
218
+ return { type: 'string' };
219
+ case 'number':
220
+ return { type: 'number' };
221
+ case 'boolean':
222
+ return { type: 'boolean' };
223
+ default:
224
+ return { type: 'string' };
225
+ }
226
+ }
227
+ function extractZodSchemaFromAST(node) {
228
+ // Handle z.object({...}) calls
229
+ if (ts.isCallExpression(node) &&
230
+ ts.isPropertyAccessExpression(node.expression) &&
231
+ ts.isIdentifier(node.expression.expression) &&
232
+ node.expression.expression.text === 'z' &&
233
+ ts.isIdentifier(node.expression.name) &&
234
+ node.expression.name.text === 'object') {
235
+ const objectLiteral = node.arguments[0];
236
+ if (ts.isObjectLiteralExpression(objectLiteral)) {
237
+ const properties = {};
238
+ const required = [];
239
+ for (const property of objectLiteral.properties) {
240
+ if (ts.isPropertyAssignment(property) &&
241
+ ts.isIdentifier(property.name) &&
242
+ property.initializer) {
243
+ const propertyName = property.name.text;
244
+ const zodType = extractZodTypeFromAST(property.initializer);
245
+ properties[propertyName] = zodType;
246
+ // Only add to required if it's not optional
247
+ if (!isOptionalZodType(property.initializer)) {
248
+ required.push(propertyName);
249
+ }
287
250
  }
288
251
  }
252
+ return {
253
+ type: "object",
254
+ properties,
255
+ required
256
+ };
289
257
  }
290
- searchIndex = callEnd + 1;
291
- }
292
- return tools;
293
- }
294
- function convertSchemaToJSON(schemaVar, indexContent) {
295
- // Find the schema definition
296
- const schemaRegex = new RegExp(`const\\s+${schemaVar}\\s*=\\s*z\\.object\\(\\{([\\s\\S]*?)\\}\\\);`, 'g');
297
- const match = schemaRegex.exec(indexContent);
298
- if (match) {
299
- const schemaContent = match[1];
300
- // Convert Zod schema to JSON Schema format
301
- return {
302
- type: "object",
303
- properties: parseZodProperties(schemaContent),
304
- required: extractRequiredFields(schemaContent)
305
- };
306
258
  }
307
- // If no match found, return empty schema
308
- return { type: "object", properties: {} };
259
+ return { type: "object" };
309
260
  }
310
- function parseZodProperties(schemaContent) {
311
- const properties = {};
312
- // Simple regex to find z.string(), z.number(), etc.
313
- const fieldRegex = /(\w+):\s*z\.(\w+)\(\)/g;
314
- let match;
315
- while ((match = fieldRegex.exec(schemaContent)) !== null) {
316
- const fieldName = match[1];
317
- const fieldType = match[2];
318
- switch (fieldType) {
261
+ function extractZodTypeFromAST(node) {
262
+ // Handle z.string(), z.number(), z.boolean(), etc.
263
+ if (ts.isCallExpression(node) &&
264
+ ts.isPropertyAccessExpression(node.expression) &&
265
+ ts.isIdentifier(node.expression.expression) &&
266
+ node.expression.expression.text === 'z') {
267
+ const zodType = node.expression.name.text;
268
+ switch (zodType) {
319
269
  case 'string':
320
- properties[fieldName] = { type: 'string' };
321
- break;
270
+ return { type: 'string' };
322
271
  case 'number':
323
- properties[fieldName] = { type: 'number' };
324
- break;
272
+ return { type: 'number' };
325
273
  case 'boolean':
326
- properties[fieldName] = { type: 'boolean' };
327
- break;
274
+ return { type: 'boolean' };
275
+ case 'array':
276
+ // Handle z.array(z.string()) etc.
277
+ if (node.arguments.length > 0) {
278
+ const elementType = extractZodTypeFromAST(node.arguments[0]);
279
+ return { type: 'array', items: elementType };
280
+ }
281
+ return { type: 'array' };
282
+ case 'object':
283
+ // Handle nested z.object({...}) calls
284
+ if (node.arguments.length > 0) {
285
+ return extractZodSchemaFromAST(node);
286
+ }
287
+ return { type: 'object' };
288
+ case 'enum':
289
+ // Handle z.enum(['a', 'b', 'c'])
290
+ if (node.arguments.length > 0 && ts.isArrayLiteralExpression(node.arguments[0])) {
291
+ const enumValues = [];
292
+ for (const element of node.arguments[0].elements) {
293
+ if (ts.isStringLiteral(element)) {
294
+ enumValues.push(element.text);
295
+ }
296
+ }
297
+ return { type: 'string', enum: enumValues };
298
+ }
299
+ return { type: 'string' };
328
300
  default:
329
- properties[fieldName] = { type: 'string' };
301
+ return { type: 'string' };
302
+ }
303
+ }
304
+ // Handle method chaining like z.string().optional()
305
+ if (ts.isCallExpression(node) &&
306
+ ts.isPropertyAccessExpression(node.expression) &&
307
+ ts.isCallExpression(node.expression.expression)) {
308
+ const baseType = extractZodTypeFromAST(node.expression.expression);
309
+ const method = node.expression.name.text;
310
+ if (method === 'optional') {
311
+ // Optional fields are handled by not including them in required array
312
+ return baseType;
330
313
  }
314
+ return baseType;
331
315
  }
332
- return properties;
316
+ return { type: 'string' };
333
317
  }
334
- function extractRequiredFields(schemaContent) {
335
- const required = [];
336
- const fieldRegex = /(\w+):\s*z\.(\w+)\(\)/g;
337
- let match;
338
- while ((match = fieldRegex.exec(schemaContent)) !== null) {
339
- required.push(match[1]);
340
- }
341
- return required;
318
+ function isOptionalZodType(node) {
319
+ // Check if this is a chained call ending with .optional()
320
+ if (ts.isCallExpression(node) &&
321
+ ts.isPropertyAccessExpression(node.expression) &&
322
+ ts.isIdentifier(node.expression.name) &&
323
+ node.expression.name.text === 'optional') {
324
+ return true;
325
+ }
326
+ return false;
327
+ }
328
+ function extractSchemaFromIndex(schemaVar, indexContent) {
329
+ // Create a source file from the index content to parse schemas using AST
330
+ const sourceFile = ts.createSourceFile('index.ts', indexContent, ts.ScriptTarget.Latest, true);
331
+ let schema = null;
332
+ function findSchema(node) {
333
+ // Look for const schemaVar = z.object({...}) declarations
334
+ if (ts.isVariableDeclaration(node) &&
335
+ ts.isIdentifier(node.name) &&
336
+ node.name.text === schemaVar &&
337
+ node.initializer) {
338
+ schema = extractZodSchemaFromAST(node.initializer);
339
+ return;
340
+ }
341
+ ts.forEachChild(node, findSchema);
342
+ }
343
+ findSchema(sourceFile);
344
+ return schema;
345
+ }
346
+ function extractEnvironmentVariables() {
347
+ const config = readSkillConfig();
348
+ const envVars = [];
349
+ if (config?.skill?.env) {
350
+ for (const [key, value] of Object.entries(config.skill.env)) {
351
+ envVars.push({
352
+ name: key,
353
+ value: value
354
+ });
355
+ }
356
+ }
357
+ return envVars;
342
358
  }
343
359
  async function createSelfContainedExecute(executeBody, indexContent) {
344
360
  const dependencies = [];
@@ -741,14 +757,34 @@ const axios = {
741
757
  `;
742
758
  }
743
759
  async function extractToolFromClass(className, indexContent, indexFilePath) {
744
- // Find the import statement for this class
745
- const importRegex = new RegExp(`import\\s+${className}\\s+from\\s+["']([^"']+)["']`, 'g');
746
- const importMatch = importRegex.exec(indexContent);
747
- if (!importMatch) {
760
+ // Find the import statement for this class using AST
761
+ const sourceFile = ts.createSourceFile(indexFilePath, indexContent, ts.ScriptTarget.Latest, true);
762
+ let importPath = null;
763
+ function findImport(node) {
764
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
765
+ const moduleSpecifier = node.moduleSpecifier;
766
+ // Check if this import has the class we're looking for
767
+ if (node.importClause && node.importClause.name && node.importClause.name.text === className) {
768
+ importPath = moduleSpecifier.text;
769
+ return;
770
+ }
771
+ // Check named imports
772
+ if (node.importClause && node.importClause.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
773
+ for (const element of node.importClause.namedBindings.elements) {
774
+ if (element.name.text === className) {
775
+ importPath = moduleSpecifier.text;
776
+ return;
777
+ }
778
+ }
779
+ }
780
+ }
781
+ ts.forEachChild(node, findImport);
782
+ }
783
+ findImport(sourceFile);
784
+ if (!importPath) {
748
785
  console.warn(`Warning: Could not find import for class ${className}`);
749
786
  return null;
750
787
  }
751
- const importPath = importMatch[1];
752
788
  // Read the tool file - handle both relative and absolute paths
753
789
  let toolFilePath;
754
790
  if (importPath.startsWith('./') || importPath.startsWith('../')) {
@@ -765,32 +801,59 @@ async function extractToolFromClass(className, indexContent, indexFilePath) {
765
801
  return null;
766
802
  }
767
803
  const toolContent = fs.readFileSync(toolFilePath, 'utf8');
768
- // Extract tool properties from the class
769
- const nameMatch = toolContent.match(/this\.name\s*=\s*["']([^"']+)["']/);
770
- const descriptionMatch = toolContent.match(/this\.description\s*=\s*["']([^"']+)["']/);
771
- if (!nameMatch || !descriptionMatch) {
804
+ const toolSourceFile = ts.createSourceFile(toolFilePath, toolContent, ts.ScriptTarget.Latest, true);
805
+ // Extract tool properties using AST
806
+ let toolName = null;
807
+ let toolDescription = null;
808
+ let inputSchema = null;
809
+ let executeMethod = null;
810
+ function extractToolInfo(node) {
811
+ // Look for class declaration
812
+ if (ts.isClassDeclaration(node) && node.name && node.name.text === className) {
813
+ // Extract class members
814
+ for (const member of node.members) {
815
+ if (ts.isPropertyDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
816
+ const propertyName = member.name.text;
817
+ // Extract name property
818
+ if (propertyName === 'name' && member.initializer && ts.isStringLiteral(member.initializer)) {
819
+ toolName = member.initializer.text;
820
+ }
821
+ // Extract description property
822
+ if (propertyName === 'description' && member.initializer && ts.isStringLiteral(member.initializer)) {
823
+ toolDescription = member.initializer.text;
824
+ }
825
+ // Extract inputSchema property
826
+ if (propertyName === 'inputSchema' && member.initializer) {
827
+ inputSchema = extractZodSchemaFromAST(member.initializer);
828
+ }
829
+ }
830
+ // Extract execute method
831
+ if (ts.isMethodDeclaration(member) && member.name && ts.isIdentifier(member.name) && member.name.text === 'execute') {
832
+ executeMethod = member;
833
+ }
834
+ }
835
+ }
836
+ ts.forEachChild(node, extractToolInfo);
837
+ }
838
+ extractToolInfo(toolSourceFile);
839
+ if (!toolName || !toolDescription) {
772
840
  console.warn(`Warning: Could not extract name or description from ${className}`);
773
841
  return null;
774
842
  }
775
- const toolName = nameMatch[1];
776
- const toolDescription = descriptionMatch[1];
777
- // Extract schemas - look for multiple schema definitions
778
- const inputSchemaMatch = toolContent.match(/const\s+(\w+)\s*=\s*z\.object\(/);
779
- const outputSchemaMatch = toolContent.match(/const\s+(\w+)\s*=\s*z\.object\(/);
780
- if (!inputSchemaMatch) {
843
+ if (!inputSchema) {
781
844
  console.warn(`Warning: Could not find input schema in ${className}`);
782
845
  return null;
783
846
  }
784
- // Convert schemas to JSON Schema format
785
- const inputSchema = convertSchemaToJSON(inputSchemaMatch[1], toolContent);
786
- const outputSchema = outputSchemaMatch ? convertSchemaToJSON(outputSchemaMatch[1], toolContent) : { type: "object" };
787
- // Extract execute method
788
- const executeMatch = toolContent.match(/async\s+execute\s*\([^)]*\)\s*\{([\s\S]*?)\}/);
789
- if (!executeMatch) {
847
+ if (!executeMethod) {
790
848
  console.warn(`Warning: Could not find execute method in ${className}`);
791
849
  return null;
792
850
  }
793
- const executeBody = executeMatch[1];
851
+ // Extract execute method body
852
+ if (!executeMethod.body) {
853
+ console.warn(`Warning: Execute method has no body in ${className}`);
854
+ return null;
855
+ }
856
+ const executeBody = extractFunctionBody(executeMethod.body, toolSourceFile);
794
857
  // For class-based tools, we need to create a self-contained function that includes:
795
858
  // 1. The service classes
796
859
  // 2. Class instantiation
@@ -799,8 +862,7 @@ async function extractToolFromClass(className, indexContent, indexFilePath) {
799
862
  return {
800
863
  name: toolName,
801
864
  description: toolDescription,
802
- inputSchema,
803
- outputSchema,
865
+ inputSchema: inputSchema || { type: "object" },
804
866
  execute: selfContainedExecute
805
867
  };
806
868
  }