lua-cli 2.5.4 → 2.5.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.
@@ -0,0 +1,54 @@
1
+ import { HttpClient } from "../common/http.client.js";
2
+ import { ApiResponse } from "../interfaces/common.js";
3
+ /**
4
+ * Environment variables response structure from the API
5
+ * The actual environment variables are nested in the 'data' property
6
+ */
7
+ interface EnvironmentVariablesResponse {
8
+ data: Record<string, string>;
9
+ _id?: string;
10
+ agentId?: string;
11
+ createdAt?: string;
12
+ updatedAt?: string;
13
+ __v?: number;
14
+ }
15
+ /**
16
+ * Developer API calls for agent management and configuration
17
+ */
18
+ export default class DeveloperApi extends HttpClient {
19
+ private apiKey;
20
+ private agentId;
21
+ /**
22
+ * Creates an instance of DeveloperApi
23
+ * @param baseUrl - The base URL for the API
24
+ * @param apiKey - The API key for authentication
25
+ * @param agentId - The unique identifier of the agent
26
+ */
27
+ constructor(baseUrl: string, apiKey: string, agentId: string);
28
+ /**
29
+ * Retrieves all environment variables for the agent in production
30
+ * The response includes metadata (_id, agentId, timestamps) and the actual env vars in the 'data' property
31
+ * @returns Promise resolving to an ApiResponse containing environment variables and metadata
32
+ * @throws Error if the API request fails or the agent is not found
33
+ */
34
+ getEnvironmentVariables(): Promise<ApiResponse<EnvironmentVariablesResponse>>;
35
+ /**
36
+ * Updates all environment variables for the agent in production
37
+ * This operation replaces all existing environment variables with the provided set
38
+ * @param envData - Object containing environment variable key-value pairs
39
+ * @returns Promise resolving to an ApiResponse with the updated environment variables and metadata
40
+ * @throws Error if the API request fails or the agent is not found
41
+ */
42
+ updateEnvironmentVariables(envData: Record<string, string>): Promise<ApiResponse<EnvironmentVariablesResponse>>;
43
+ /**
44
+ * Deletes a specific environment variable by key
45
+ * @param key - The environment variable key to delete
46
+ * @returns Promise resolving to an ApiResponse with confirmation
47
+ * @throws Error if the API request fails, the agent is not found, or the key doesn't exist
48
+ */
49
+ deleteEnvironmentVariable(key: string): Promise<ApiResponse<{
50
+ message: string;
51
+ key: string;
52
+ }>>;
53
+ }
54
+ export {};
@@ -0,0 +1,51 @@
1
+ import { HttpClient } from "../common/http.client.js";
2
+ /**
3
+ * Developer API calls for agent management and configuration
4
+ */
5
+ export default class DeveloperApi extends HttpClient {
6
+ /**
7
+ * Creates an instance of DeveloperApi
8
+ * @param baseUrl - The base URL for the API
9
+ * @param apiKey - The API key for authentication
10
+ * @param agentId - The unique identifier of the agent
11
+ */
12
+ constructor(baseUrl, apiKey, agentId) {
13
+ super(baseUrl);
14
+ this.apiKey = apiKey;
15
+ this.agentId = agentId;
16
+ }
17
+ /**
18
+ * Retrieves all environment variables for the agent in production
19
+ * The response includes metadata (_id, agentId, timestamps) and the actual env vars in the 'data' property
20
+ * @returns Promise resolving to an ApiResponse containing environment variables and metadata
21
+ * @throws Error if the API request fails or the agent is not found
22
+ */
23
+ async getEnvironmentVariables() {
24
+ return this.httpGet(`/developer/agents/${this.agentId}/env`, {
25
+ Authorization: `Bearer ${this.apiKey}`,
26
+ });
27
+ }
28
+ /**
29
+ * Updates all environment variables for the agent in production
30
+ * This operation replaces all existing environment variables with the provided set
31
+ * @param envData - Object containing environment variable key-value pairs
32
+ * @returns Promise resolving to an ApiResponse with the updated environment variables and metadata
33
+ * @throws Error if the API request fails or the agent is not found
34
+ */
35
+ async updateEnvironmentVariables(envData) {
36
+ return this.httpPost(`/developer/agents/${this.agentId}/env`, envData, {
37
+ Authorization: `Bearer ${this.apiKey}`,
38
+ });
39
+ }
40
+ /**
41
+ * Deletes a specific environment variable by key
42
+ * @param key - The environment variable key to delete
43
+ * @returns Promise resolving to an ApiResponse with confirmation
44
+ * @throws Error if the API request fails, the agent is not found, or the key doesn't exist
45
+ */
46
+ async deleteEnvironmentVariable(key) {
47
+ return this.httpDelete(`/developer/agents/${this.agentId}/env/${key}`, {
48
+ Authorization: `Bearer ${this.apiKey}`,
49
+ });
50
+ }
51
+ }
@@ -1,6 +1,7 @@
1
1
  import { DevVersionResponse, UpdateDevVersionResponse } from "../interfaces/dev.js";
2
2
  import { HttpClient } from "../common/http.client.js";
3
3
  import { ApiResponse } from "../interfaces/common.js";
4
+ import { GetSkillsResponse, DeleteSkillResponse } from "../interfaces/skills.js";
4
5
  /**
5
6
  * Skill API calls
6
7
  */
@@ -14,6 +15,12 @@ export default class SkillApi extends HttpClient {
14
15
  * @param agentId - The unique identifier of the agent
15
16
  */
16
17
  constructor(baseUrl: string, apiKey: string, agentId: string);
18
+ /**
19
+ * Retrieves all skills for the agent
20
+ * @returns Promise resolving to an ApiResponse containing an array of skills with their versions and tools
21
+ * @throws Error if the API request fails or the agent is not found
22
+ */
23
+ getSkills(): Promise<ApiResponse<GetSkillsResponse>>;
17
24
  /**
18
25
  * Creates a new skill for the agent
19
26
  * @param skillData - The skill data including name, description, and optional context
@@ -74,4 +81,13 @@ export default class SkillApi extends HttpClient {
74
81
  activeVersionId: string;
75
82
  publishedAt: string;
76
83
  }>>;
84
+ /**
85
+ * Deletes a skill and all its versions, or deactivates it if it has versions
86
+ * @param skillId - The unique identifier of the skill to delete
87
+ * @returns Promise resolving to an ApiResponse with deletion status
88
+ * - If deleted is true: skill was successfully deleted
89
+ * - If deleted is false and deactivated is true: skill has versions and was deactivated instead
90
+ * @throws Error if the skill is not found or the delete operation fails
91
+ */
92
+ deleteSkill(skillId: string): Promise<ApiResponse<DeleteSkillResponse>>;
77
93
  }
@@ -14,6 +14,16 @@ export default class SkillApi extends HttpClient {
14
14
  this.apiKey = apiKey;
15
15
  this.agentId = agentId;
16
16
  }
17
+ /**
18
+ * Retrieves all skills for the agent
19
+ * @returns Promise resolving to an ApiResponse containing an array of skills with their versions and tools
20
+ * @throws Error if the API request fails or the agent is not found
21
+ */
22
+ async getSkills() {
23
+ return this.httpGet(`/developer/skills/${this.agentId}`, {
24
+ Authorization: `Bearer ${this.apiKey}`,
25
+ });
26
+ }
17
27
  /**
18
28
  * Creates a new skill for the agent
19
29
  * @param skillData - The skill data including name, description, and optional context
@@ -85,4 +95,17 @@ export default class SkillApi extends HttpClient {
85
95
  Authorization: `Bearer ${this.apiKey}`,
86
96
  });
87
97
  }
98
+ /**
99
+ * Deletes a skill and all its versions, or deactivates it if it has versions
100
+ * @param skillId - The unique identifier of the skill to delete
101
+ * @returns Promise resolving to an ApiResponse with deletion status
102
+ * - If deleted is true: skill was successfully deleted
103
+ * - If deleted is false and deactivated is true: skill has versions and was deactivated instead
104
+ * @throws Error if the skill is not found or the delete operation fails
105
+ */
106
+ async deleteSkill(skillId) {
107
+ return this.httpDelete(`/developer/skills/${this.agentId}/${skillId}`, {
108
+ Authorization: `Bearer ${this.apiKey}`,
109
+ });
110
+ }
88
111
  }
@@ -12,6 +12,8 @@
12
12
  * 4. Extracts execute code and schemas from bundled tools
13
13
  * 5. Bundles the main index file
14
14
  * 6. Creates deployment data in both new and legacy formats
15
+ * 7. Syncs lua.skill.yaml with deploy.json (removes deleted skills from YAML)
16
+ * 8. Syncs server with YAML (deletes/deactivates skills not in YAML)
15
17
  *
16
18
  * Output:
17
19
  * - dist/deployment.json - New deployment format with tool references
@@ -19,6 +21,11 @@
19
21
  * - dist/index.js - Main skill entry point
20
22
  * - .lua/deploy.json - Legacy deployment format with compressed tools
21
23
  * - .lua/*.js - Individual uncompressed tool files for debugging
24
+ * - lua.skill.yaml - Updated to match compiled skills
25
+ *
26
+ * Server Sync:
27
+ * - Skills deleted from code are removed from YAML
28
+ * - Skills not in YAML are deleted from server (or deactivated if they have versions)
22
29
  *
23
30
  * @returns Promise that resolves when compilation is complete
24
31
  */
@@ -10,6 +10,8 @@ import { findIndexFile } from '../utils/compile.js';
10
10
  import { detectTools } from '../utils/tool-detection.js';
11
11
  import { bundleTool, bundleMainIndex, extractExecuteCode } from '../utils/bundling.js';
12
12
  import { createDeploymentData, createLegacyDeploymentData } from '../utils/deployment.js';
13
+ import { syncYamlWithDeployJson, syncServerSkillsWithYaml } from '../utils/skill-management.js';
14
+ import { readSkillConfig } from '../utils/files.js';
13
15
  import { COMPILE_DIRS, COMPILE_FILES } from '../config/compile.constants.js';
14
16
  /**
15
17
  * Main compile command - orchestrates the entire skill compilation process.
@@ -21,6 +23,8 @@ import { COMPILE_DIRS, COMPILE_FILES } from '../config/compile.constants.js';
21
23
  * 4. Extracts execute code and schemas from bundled tools
22
24
  * 5. Bundles the main index file
23
25
  * 6. Creates deployment data in both new and legacy formats
26
+ * 7. Syncs lua.skill.yaml with deploy.json (removes deleted skills from YAML)
27
+ * 8. Syncs server with YAML (deletes/deactivates skills not in YAML)
24
28
  *
25
29
  * Output:
26
30
  * - dist/deployment.json - New deployment format with tool references
@@ -28,6 +32,11 @@ import { COMPILE_DIRS, COMPILE_FILES } from '../config/compile.constants.js';
28
32
  * - dist/index.js - Main skill entry point
29
33
  * - .lua/deploy.json - Legacy deployment format with compressed tools
30
34
  * - .lua/*.js - Individual uncompressed tool files for debugging
35
+ * - lua.skill.yaml - Updated to match compiled skills
36
+ *
37
+ * Server Sync:
38
+ * - Skills deleted from code are removed from YAML
39
+ * - Skills not in YAML are deleted from server (or deactivated if they have versions)
31
40
  *
32
41
  * @returns Promise that resolves when compilation is complete
33
42
  */
@@ -52,6 +61,15 @@ export async function compileCommand() {
52
61
  // Step 5: Create deployment data in both formats
53
62
  await createDeploymentData(tools, distDir);
54
63
  await createLegacyDeploymentData(tools, luaDir, indexFile);
64
+ // Step 6: Sync YAML with deploy.json
65
+ writeProgress("🔄 Syncing YAML with deploy.json...");
66
+ const deployJsonPath = path.join(luaDir, COMPILE_FILES.DEPLOY_JSON);
67
+ const config = readSkillConfig();
68
+ await syncYamlWithDeployJson(deployJsonPath, config);
69
+ // Step 7: Sync server with YAML (delete skills not in YAML)
70
+ writeProgress("🔄 Syncing server with YAML...");
71
+ const updatedConfig = readSkillConfig(); // Re-read config after YAML sync
72
+ await syncServerSkillsWithYaml(updatedConfig);
55
73
  writeSuccess(`✅ Skill compiled successfully - ${tools.length} tools bundled`);
56
74
  }, "compilation");
57
75
  }
@@ -10,6 +10,7 @@ import { readSkillConfig } from '../utils/files.js';
10
10
  import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
11
11
  import { BASE_URLS } from '../config/constants.js';
12
12
  import { validateConfig, validateAgentConfig, } from '../utils/dev-helpers.js';
13
+ import DeveloperApi from '../api/developer.api.service.js';
13
14
  /**
14
15
  * Main env command - manages environment variables
15
16
  *
@@ -57,6 +58,7 @@ export async function envCommand() {
57
58
  }
58
59
  await checkApiKey(apiKey);
59
60
  context.apiKey = apiKey;
61
+ context.developerApi = new DeveloperApi(BASE_URLS.API, apiKey, agentId);
60
62
  writeProgress("✅ Authenticated");
61
63
  }
62
64
  // Step 4: Start management loop
@@ -160,19 +162,22 @@ function loadSandboxEnvVariables() {
160
162
  */
161
163
  async function loadProductionEnvVariables(context) {
162
164
  try {
163
- const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/env`, {
164
- method: 'GET',
165
- headers: {
166
- 'accept': 'application/json',
167
- 'Authorization': `Bearer ${context.apiKey}`
168
- }
169
- });
170
- if (!response.ok) {
171
- throw new Error(`HTTP error! status: ${response.status}`);
165
+ if (!context.developerApi) {
166
+ throw new Error('Developer API not initialized');
167
+ }
168
+ const response = await context.developerApi.getEnvironmentVariables();
169
+ if (!response.success) {
170
+ throw new Error(response.error?.message || 'Failed to load environment variables');
172
171
  }
173
- const data = await response.json();
174
- if (data.data && typeof data.data === 'object') {
175
- return Object.entries(data.data).map(([key, value]) => ({
172
+ // The API returns environment variables nested in a 'data' property
173
+ // Response structure: { success: true, data: { data: { KEY: "value", ... }, _id: "...", ... } }
174
+ const envData = response.data?.data || response.data;
175
+ if (envData && typeof envData === 'object') {
176
+ // Filter out metadata fields like _id, agentId, createdAt, updatedAt, __v
177
+ const metadataKeys = ['_id', 'agentId', 'data', 'createdAt', 'updatedAt', '__v'];
178
+ return Object.entries(envData)
179
+ .filter(([key]) => !metadataKeys.includes(key))
180
+ .map(([key, value]) => ({
176
181
  key,
177
182
  value: String(value)
178
183
  }));
@@ -231,23 +236,18 @@ function saveSandboxEnvVariables(variables) {
231
236
  */
232
237
  async function saveProductionEnvVariables(context, variables) {
233
238
  try {
239
+ if (!context.developerApi) {
240
+ throw new Error('Developer API not initialized');
241
+ }
234
242
  const envData = {};
235
243
  variables.forEach(v => {
236
244
  if (v.key.trim()) {
237
245
  envData[v.key.trim()] = v.value;
238
246
  }
239
247
  });
240
- const response = await fetch(`${BASE_URLS.API}/developer/agents/${context.agentId}/env`, {
241
- method: 'POST',
242
- headers: {
243
- 'accept': 'application/json',
244
- 'Authorization': `Bearer ${context.apiKey}`,
245
- 'Content-Type': 'application/json'
246
- },
247
- body: JSON.stringify(envData)
248
- });
249
- if (!response.ok) {
250
- throw new Error(`HTTP error! status: ${response.status}`);
248
+ const response = await context.developerApi.updateEnvironmentVariables(envData);
249
+ if (!response.success) {
250
+ throw new Error(response.error?.message || 'Failed to save environment variables');
251
251
  }
252
252
  return true;
253
253
  }
@@ -383,14 +383,30 @@ async function deleteVariable(context, currentVariables) {
383
383
  console.log("\nℹ️ Deletion cancelled.\n");
384
384
  return;
385
385
  }
386
- const updatedVariables = currentVariables.filter(v => v.key !== selectedKey);
387
- writeProgress("🔄 Saving...");
386
+ writeProgress("🔄 Deleting...");
388
387
  let success = false;
389
388
  if (context.environment === 'sandbox') {
389
+ const updatedVariables = currentVariables.filter(v => v.key !== selectedKey);
390
390
  success = saveSandboxEnvVariables(updatedVariables);
391
391
  }
392
392
  else {
393
- success = await saveProductionEnvVariables(context, updatedVariables);
393
+ // Use the dedicated delete API endpoint for production
394
+ if (!context.developerApi) {
395
+ console.error("❌ Developer API not initialized");
396
+ return;
397
+ }
398
+ try {
399
+ const response = await context.developerApi.deleteEnvironmentVariable(selectedKey);
400
+ if (response.success) {
401
+ success = true;
402
+ }
403
+ else {
404
+ console.error(`❌ ${response.error?.message || 'Failed to delete variable'}`);
405
+ }
406
+ }
407
+ catch (error) {
408
+ console.error('❌ Error deleting production env variable:', error);
409
+ }
394
410
  }
395
411
  if (success) {
396
412
  writeSuccess(`✅ Variable "${selectedKey}" deleted successfully`);
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Skills Interfaces
3
+ * Structures for skill management
4
+ */
5
+ /**
6
+ * Tool definition within a skill version
7
+ */
8
+ export interface SkillTool {
9
+ name: string;
10
+ description: string;
11
+ }
12
+ /**
13
+ * Skill version information
14
+ */
15
+ export interface SkillVersion {
16
+ id: string;
17
+ version: string;
18
+ description: string;
19
+ active: boolean;
20
+ context: string;
21
+ tools: SkillTool[];
22
+ }
23
+ /**
24
+ * Skill information
25
+ */
26
+ export interface Skill {
27
+ id: string;
28
+ name: string;
29
+ description: string;
30
+ public: boolean;
31
+ active: boolean;
32
+ createdAt: string;
33
+ updatedAt: string;
34
+ versions: SkillVersion[];
35
+ }
36
+ /**
37
+ * Response from get all skills API endpoint
38
+ */
39
+ export interface GetSkillsResponse {
40
+ skills: Skill[];
41
+ }
42
+ /**
43
+ * Response from delete skill API endpoint
44
+ * If deleted is false and deactivated is true, the skill has versions
45
+ * and cannot be deleted, so it was deactivated instead
46
+ */
47
+ export interface DeleteSkillResponse {
48
+ message: string;
49
+ deleted: boolean;
50
+ deactivated: boolean;
51
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Skills Interfaces
3
+ * Structures for skill management
4
+ */
5
+ export {};
@@ -12,3 +12,23 @@ import { SkillConfig } from '../types/compile.types.js';
12
12
  * @returns Updated skills array with valid skill IDs
13
13
  */
14
14
  export declare function ensureSkillsExistInYaml(skillsArray: any[], config: SkillConfig): Promise<any[]>;
15
+ /**
16
+ * Updates the YAML file with skills from the deploy.json file.
17
+ * This ensures the YAML reflects what's actually in the compiled code.
18
+ * Only updates skill entries that already exist in YAML (preserves skillId).
19
+ * Removes skills from YAML that are no longer in deploy.json.
20
+ *
21
+ * @param deployJsonPath - Path to the deploy.json file
22
+ * @param config - Current skill configuration
23
+ */
24
+ export declare function syncYamlWithDeployJson(deployJsonPath: string, config: SkillConfig): Promise<void>;
25
+ /**
26
+ * Syncs the server skills with the YAML configuration.
27
+ * Performs a two-way sync:
28
+ * 1. Deletes/deactivates skills from server that aren't in YAML
29
+ * 2. Updates YAML with active version numbers from server
30
+ *
31
+ * @param config - The skill configuration from lua.skill.yaml
32
+ * @returns Array of messages about sync operations
33
+ */
34
+ export declare function syncServerSkillsWithYaml(config: SkillConfig): Promise<string[]>;
@@ -146,3 +146,162 @@ async function updateYamlWithSkills(skills, config) {
146
146
  });
147
147
  fs.writeFileSync(yamlPath, yamlContent);
148
148
  }
149
+ /**
150
+ * Updates the YAML file with skills from the deploy.json file.
151
+ * This ensures the YAML reflects what's actually in the compiled code.
152
+ * Only updates skill entries that already exist in YAML (preserves skillId).
153
+ * Removes skills from YAML that are no longer in deploy.json.
154
+ *
155
+ * @param deployJsonPath - Path to the deploy.json file
156
+ * @param config - Current skill configuration
157
+ */
158
+ export async function syncYamlWithDeployJson(deployJsonPath, config) {
159
+ try {
160
+ // Read deploy.json
161
+ if (!fs.existsSync(deployJsonPath)) {
162
+ console.warn("⚠️ deploy.json not found. Skipping YAML sync.");
163
+ return;
164
+ }
165
+ const deployData = JSON.parse(fs.readFileSync(deployJsonPath, 'utf8'));
166
+ const deploySkills = deployData.skills || [];
167
+ // Create map of deploy skills by name
168
+ const deploySkillsMap = new Map();
169
+ deploySkills.forEach((skill) => {
170
+ deploySkillsMap.set(skill.name, skill);
171
+ });
172
+ // Get existing YAML skills
173
+ const existingYamlSkills = config?.skills || [];
174
+ // Update YAML skills to match deploy.json
175
+ const updatedSkills = existingYamlSkills
176
+ .filter((yamlSkill) => {
177
+ // Keep only skills that exist in deploy.json
178
+ return deploySkillsMap.has(yamlSkill.name);
179
+ })
180
+ .map((yamlSkill) => {
181
+ // Update version and description from deploy.json if available
182
+ const deploySkill = deploySkillsMap.get(yamlSkill.name);
183
+ return {
184
+ name: yamlSkill.name,
185
+ version: deploySkill?.version || yamlSkill.version || SKILL_DEFAULTS.VERSION,
186
+ skillId: yamlSkill.skillId || ''
187
+ };
188
+ });
189
+ // Add new skills from deploy.json that aren't in YAML yet
190
+ deploySkills.forEach((deploySkill) => {
191
+ const existsInYaml = updatedSkills.some((s) => s.name === deploySkill.name);
192
+ if (!existsInYaml) {
193
+ updatedSkills.push({
194
+ name: deploySkill.name,
195
+ version: deploySkill.version || SKILL_DEFAULTS.VERSION,
196
+ skillId: '' // Will be created via API later
197
+ });
198
+ }
199
+ });
200
+ // Update YAML file
201
+ await updateYamlWithSkills(updatedSkills, config);
202
+ console.log("✅ YAML synced with deploy.json");
203
+ }
204
+ catch (error) {
205
+ console.error("❌ Error syncing YAML with deploy.json:", error);
206
+ }
207
+ }
208
+ /**
209
+ * Syncs the server skills with the YAML configuration.
210
+ * Performs a two-way sync:
211
+ * 1. Deletes/deactivates skills from server that aren't in YAML
212
+ * 2. Updates YAML with active version numbers from server
213
+ *
214
+ * @param config - The skill configuration from lua.skill.yaml
215
+ * @returns Array of messages about sync operations
216
+ */
217
+ export async function syncServerSkillsWithYaml(config) {
218
+ const messages = [];
219
+ let yamlNeedsUpdate = false;
220
+ try {
221
+ // Validate prerequisites
222
+ const apiKey = await loadApiKey();
223
+ if (!apiKey) {
224
+ console.warn("⚠️ No API key found. Skipping server sync.");
225
+ return messages;
226
+ }
227
+ const agentId = config?.agent?.agentId;
228
+ if (!agentId) {
229
+ console.warn("⚠️ No agent ID found in lua.skill.yaml. Skipping server sync.");
230
+ return messages;
231
+ }
232
+ // Get skills from server
233
+ const skillApi = new SkillApi(BASE_URLS.API, apiKey, agentId);
234
+ const serverSkillsResponse = await skillApi.getSkills();
235
+ if (!serverSkillsResponse.success || !serverSkillsResponse.data?.skills) {
236
+ console.warn("⚠️ Could not retrieve server skills. Skipping server sync.");
237
+ return messages;
238
+ }
239
+ const serverSkills = serverSkillsResponse.data.skills;
240
+ const yamlSkills = config?.skills || [];
241
+ // Create maps for efficient lookup
242
+ const yamlSkillsMap = new Map(yamlSkills
243
+ .filter((skill) => skill.skillId)
244
+ .map((skill) => [skill.skillId, skill]));
245
+ const serverSkillsMap = new Map(serverSkills.map(skill => [skill.id, skill]));
246
+ // Part 1: Delete skills from server that aren't in YAML
247
+ const skillsToDelete = serverSkills.filter(serverSkill => !yamlSkillsMap.has(serverSkill.id));
248
+ for (const skill of skillsToDelete) {
249
+ try {
250
+ const deleteResponse = await skillApi.deleteSkill(skill.id);
251
+ if (deleteResponse.success && deleteResponse.data) {
252
+ if (deleteResponse.data.deleted) {
253
+ const msg = `✅ Deleted skill "${skill.name}" from server`;
254
+ messages.push(msg);
255
+ console.log(msg);
256
+ }
257
+ else if (deleteResponse.data.deactivated) {
258
+ const msg = `⚠️ Skill "${skill.name}" has versions and cannot be deleted. It has been deactivated instead.`;
259
+ messages.push(msg);
260
+ console.warn(msg);
261
+ }
262
+ }
263
+ else {
264
+ const msg = `❌ Failed to delete skill "${skill.name}": ${deleteResponse.error?.message || 'Unknown error'}`;
265
+ messages.push(msg);
266
+ console.error(msg);
267
+ }
268
+ }
269
+ catch (error) {
270
+ const msg = `❌ Error deleting skill "${skill.name}": ${error}`;
271
+ messages.push(msg);
272
+ console.error(msg);
273
+ }
274
+ }
275
+ // Part 2: Sync version numbers from server to YAML
276
+ const updatedYamlSkills = yamlSkills.map((yamlSkill) => {
277
+ const serverSkill = serverSkillsMap.get(yamlSkill.skillId);
278
+ if (serverSkill && serverSkill.versions && serverSkill.versions.length > 0) {
279
+ // Find the active version on the server
280
+ const activeVersion = serverSkill.versions.find((v) => v.active);
281
+ if (activeVersion && activeVersion.version !== yamlSkill.version) {
282
+ const msg = `📝 Updated "${yamlSkill.name}" version in YAML: ${yamlSkill.version} → ${activeVersion.version}`;
283
+ messages.push(msg);
284
+ console.log(msg);
285
+ yamlNeedsUpdate = true;
286
+ return {
287
+ ...yamlSkill,
288
+ version: activeVersion.version
289
+ };
290
+ }
291
+ }
292
+ return yamlSkill;
293
+ });
294
+ // Update YAML file if versions changed
295
+ if (yamlNeedsUpdate) {
296
+ await updateYamlWithSkills(updatedYamlSkills, config);
297
+ console.log("✅ YAML versions synced with server");
298
+ }
299
+ if (skillsToDelete.length === 0 && !yamlNeedsUpdate) {
300
+ console.log("✅ Server and YAML are fully in sync");
301
+ }
302
+ }
303
+ catch (error) {
304
+ console.error("❌ Error syncing server skills:", error);
305
+ }
306
+ return messages;
307
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lua-cli",
3
- "version": "2.5.4",
3
+ "version": "2.5.5",
4
4
  "description": "Command-line interface for Lua AI platform - develop, test, and deploy LuaSkills with custom tools",
5
5
  "readmeFilename": "README.md",
6
6
  "main": "dist/api-exports.js",
@@ -19,7 +19,7 @@
19
19
  "axios": "^1.6.0",
20
20
  "inquirer": "^12.9.6",
21
21
  "js-yaml": "^4.1.0",
22
- "lua-cli": "^2.5.3",
22
+ "lua-cli": "^2.5.5",
23
23
  "openai": "^5.23.0",
24
24
  "uuid": "^13.0.0",
25
25
  "zod": "^3.24.1"