lua-cli 2.5.5 → 2.5.6

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.
@@ -7,6 +7,7 @@ import { readSkillConfig } from '../utils/files.js';
7
7
  import { withErrorHandling, writeProgress, writeSuccess, writeInfo } from '../utils/cli.js';
8
8
  import { sortVersionsByDate, promptVersionSelection, confirmDeployment, validateDeployConfig, validateVersionsAvailable, promptSkillSelection, getAvailableSkills, } from '../utils/deploy-helpers.js';
9
9
  import { fetchVersions, publishVersion } from '../utils/deploy-api.js';
10
+ import { updateSkillVersionInYaml } from '../utils/push-helpers.js';
10
11
  /**
11
12
  * Main deploy command - deploys a skill version to production.
12
13
  *
@@ -82,6 +83,12 @@ export async function deployCommand() {
82
83
  // Step 7: Publish the selected version
83
84
  writeProgress("šŸ”„ Publishing version...");
84
85
  const publishResponse = await publishVersion(apiKey, config.agent.agentId, selectedSkill.skillId, selectedVersion);
85
- writeSuccess(`āœ… Version ${publishResponse.activeVersionId} of "${selectedSkill.name}" deployed successfully`);
86
+ const deployedVersion = publishResponse.activeVersionId;
87
+ writeSuccess(`āœ… Version ${deployedVersion} of "${selectedSkill.name}" deployed successfully`);
88
+ // Step 8: Update YAML with the deployed version
89
+ if (deployedVersion !== selectedSkill.version) {
90
+ writeInfo(`šŸ“ Updating YAML with deployed version: ${deployedVersion}`);
91
+ updateSkillVersionInYaml(selectedSkill.name, deployedVersion);
92
+ }
86
93
  }, "deployment");
87
94
  }
@@ -2,11 +2,13 @@
2
2
  * Init Command
3
3
  * Orchestrates the initialization of a new Lua skill project
4
4
  */
5
+ import inquirer from 'inquirer';
5
6
  import { loadApiKey, checkApiKey } from "../services/auth.js";
6
- import { withErrorHandling, writeProgress, writeSuccess, } from "../utils/cli.js";
7
+ import { withErrorHandling, writeProgress, writeSuccess, writeInfo, writeError, } from "../utils/cli.js";
7
8
  import { promptAgentChoice, promptOrganizationSelection, promptAgentSelection, promptMetadataCollection, promptFeatureConfiguration, promptBusinessConfiguration, } from "../utils/init-prompts.js";
8
- import { fetchAgentTypes, selectBaseAgentType, createNewAgent, } from "../utils/init-agent.js";
9
+ import { fetchAgentTypes, selectBaseAgentType, createNewAgent, fetchExistingAgentDetails, } from "../utils/init-agent.js";
9
10
  import { initializeProject, installDependencies, clearLinesIfNeeded, } from "../utils/init-helpers.js";
11
+ import { readSkillYaml, updateYamlAgent } from "../utils/files.js";
10
12
  /**
11
13
  * Main init command - initializes a new Lua skill project.
12
14
  *
@@ -40,40 +42,114 @@ export async function initCommand() {
40
42
  }
41
43
  const userData = await checkApiKey(apiKey);
42
44
  writeProgress("āœ… Authenticated");
43
- // Step 2: Choose between existing or new agent
45
+ // Step 2: Check for existing YAML file
46
+ const existingYaml = readSkillYaml();
47
+ const yamlExists = existingYaml !== null;
48
+ if (yamlExists) {
49
+ const existingAgentId = existingYaml?.agent?.agentId;
50
+ if (existingAgentId) {
51
+ // Check if user has access to this agent
52
+ const hasAccess = checkUserHasAccessToAgent(userData, existingAgentId);
53
+ if (!hasAccess) {
54
+ writeError("\nāš ļø You don't have access to the agent in this project");
55
+ writeInfo(` Agent ID: ${existingAgentId}\n`);
56
+ const { action } = await inquirer.prompt([
57
+ {
58
+ type: 'list',
59
+ name: 'action',
60
+ message: 'What would you like to do?',
61
+ choices: [
62
+ { name: 'šŸ“§ Contact the agent admin to request access', value: 'contact' },
63
+ { name: 'šŸ”„ Switch to a different agent', value: 'switch' },
64
+ { name: 'āŒ Cancel', value: 'cancel' }
65
+ ]
66
+ }
67
+ ]);
68
+ if (action === 'contact') {
69
+ writeInfo("\nšŸ’” Please contact the agent administrator to grant you access.");
70
+ writeInfo(` Agent ID: ${existingAgentId}\n`);
71
+ process.exit(0);
72
+ }
73
+ if (action === 'cancel') {
74
+ writeInfo("\nāŒ Initialization cancelled.\n");
75
+ process.exit(0);
76
+ }
77
+ // User chose to switch agent - update existing YAML only
78
+ await handleAgentSwitch(userData, apiKey, existingYaml);
79
+ return;
80
+ }
81
+ else {
82
+ // User has access - ask if they want to switch anyway
83
+ writeInfo("\nšŸ“‹ Found existing project configuration");
84
+ writeInfo(` Current Agent ID: ${existingAgentId}\n`);
85
+ const { wantSwitch } = await inquirer.prompt([
86
+ {
87
+ type: 'confirm',
88
+ name: 'wantSwitch',
89
+ message: 'Do you want to switch this project to a different agent?',
90
+ default: false
91
+ }
92
+ ]);
93
+ if (wantSwitch) {
94
+ // User wants to switch agent - update existing YAML only
95
+ await handleAgentSwitch(userData, apiKey, existingYaml);
96
+ return;
97
+ }
98
+ else {
99
+ writeInfo("\nāœ… Keeping current agent configuration\n");
100
+ process.exit(0);
101
+ }
102
+ }
103
+ }
104
+ }
105
+ // Step 3: Choose between existing or new agent
44
106
  const agentChoice = await promptAgentChoice();
45
107
  let selectedAgent;
46
108
  let selectedOrg;
47
109
  let persona;
48
110
  let welcomeMessage;
111
+ let skipProjectInit = false;
49
112
  if (agentChoice === "existing") {
50
- // Step 3a: Select existing agent
51
- const result = await selectExistingAgent(userData);
113
+ // Step 4a: Select existing agent
114
+ const result = await selectExistingAgent(userData, apiKey);
52
115
  selectedAgent = result.agent;
53
116
  selectedOrg = result.org;
117
+ persona = result.persona;
118
+ welcomeMessage = result.welcomeMessage;
54
119
  }
55
120
  else {
56
- // Step 3b: Create new agent
121
+ // Step 4b: Create new agent
57
122
  const result = await createNewAgentFlow(apiKey);
58
123
  selectedAgent = result.agent;
59
124
  selectedOrg = result.org;
60
125
  persona = result.persona;
61
126
  welcomeMessage = result.welcomeMessage;
62
127
  }
63
- // Step 4: Initialize project
64
- const currentDir = initializeProject(selectedAgent.agentId, selectedOrg.id, persona, welcomeMessage);
65
- // Step 5: Install dependencies
66
- await installDependencies(currentDir);
67
- writeSuccess("āœ… Lua skill project initialized successfully!");
128
+ // Step 5: Initialize or update project
129
+ if (yamlExists) {
130
+ // Update existing YAML only
131
+ writeInfo("šŸ“ Updating existing lua.skill.yaml with new agent...");
132
+ updateYamlAgent(selectedAgent.agentId, selectedOrg.id, persona, welcomeMessage);
133
+ writeSuccess("āœ… lua.skill.yaml updated successfully!");
134
+ }
135
+ else {
136
+ // Full project initialization
137
+ const currentDir = initializeProject(selectedAgent.agentId, selectedOrg.id, persona, welcomeMessage);
138
+ // Step 6: Install dependencies
139
+ await installDependencies(currentDir);
140
+ writeSuccess("āœ… Lua skill project initialized successfully!");
141
+ }
68
142
  }, "initialization");
69
143
  }
70
144
  /**
71
145
  * Handles the flow for selecting an existing agent.
146
+ * Fetches the agent details from the server to get persona and welcomeMessage.
72
147
  *
73
148
  * @param userData - User's data
74
- * @returns Selected agent and organization
149
+ * @param apiKey - User's API key
150
+ * @returns Selected agent, organization, and optional persona/welcome message
75
151
  */
76
- async function selectExistingAgent(userData) {
152
+ async function selectExistingAgent(userData, apiKey) {
77
153
  // Extract organizations
78
154
  const orgs = userData.admin.orgs;
79
155
  if (!orgs || orgs.length === 0) {
@@ -83,7 +159,14 @@ async function selectExistingAgent(userData) {
83
159
  const selectedOrg = await promptOrganizationSelection(orgs);
84
160
  // Select agent from organization
85
161
  const selectedAgent = await promptAgentSelection(selectedOrg);
86
- return { agent: selectedAgent, org: selectedOrg };
162
+ // Fetch agent details to get persona and welcomeMessage
163
+ const agentDetails = await fetchExistingAgentDetails(apiKey, selectedAgent.agentId);
164
+ return {
165
+ agent: selectedAgent,
166
+ org: selectedOrg,
167
+ persona: agentDetails.persona,
168
+ welcomeMessage: agentDetails.welcomeMessage
169
+ };
87
170
  }
88
171
  /**
89
172
  * Handles the flow for creating a new agent.
@@ -115,3 +198,103 @@ async function createNewAgentFlow(apiKey) {
115
198
  const result = await createNewAgent(apiKey, selectedAgentType, businessConfig, metadata, features);
116
199
  return result;
117
200
  }
201
+ /**
202
+ * Check if user has access to a specific agent
203
+ */
204
+ function checkUserHasAccessToAgent(userData, agentId) {
205
+ const orgs = userData.admin.orgs;
206
+ for (const org of orgs) {
207
+ const agent = org.agents.find((a) => a.agentId === agentId);
208
+ if (agent) {
209
+ return true;
210
+ }
211
+ }
212
+ return false;
213
+ }
214
+ /**
215
+ * Handle switching to a different agent
216
+ */
217
+ async function handleAgentSwitch(userData, apiKey, existingYaml) {
218
+ writeInfo("\nšŸ”„ Let's switch to a different agent...\n");
219
+ // Prompt for agent choice
220
+ const agentChoice = await promptAgentChoice();
221
+ let selectedAgent;
222
+ let selectedOrg;
223
+ let persona;
224
+ let welcomeMessage;
225
+ if (agentChoice === "existing") {
226
+ // Select existing agent
227
+ const result = await selectExistingAgent(userData, apiKey);
228
+ selectedAgent = result.agent;
229
+ selectedOrg = result.org;
230
+ persona = result.persona;
231
+ welcomeMessage = result.welcomeMessage;
232
+ }
233
+ else {
234
+ // Create new agent
235
+ const result = await createNewAgentFlow(apiKey);
236
+ selectedAgent = result.agent;
237
+ selectedOrg = result.org;
238
+ persona = result.persona;
239
+ welcomeMessage = result.welcomeMessage;
240
+ }
241
+ // Ask about persona and welcome message
242
+ const finalPersona = await promptPersonaReplacement(existingYaml, persona);
243
+ const finalWelcomeMessage = await promptWelcomeMessageReplacement(existingYaml, welcomeMessage);
244
+ // Update existing YAML file with new agent
245
+ writeInfo("\nšŸ“ Updating lua.skill.yaml with new agent...");
246
+ updateYamlAgent(selectedAgent.agentId, selectedOrg.id, finalPersona, finalWelcomeMessage);
247
+ writeSuccess("āœ… lua.skill.yaml updated successfully!");
248
+ writeInfo("\nšŸ’” Your project now uses the new agent. Run 'lua compile' to update your skills.\n");
249
+ }
250
+ /**
251
+ * Prompt user about replacing existing persona
252
+ */
253
+ async function promptPersonaReplacement(existingYaml, newPersona) {
254
+ const existingPersona = existingYaml?.agent?.persona;
255
+ // If no existing persona, use new one
256
+ if (!existingPersona) {
257
+ return newPersona;
258
+ }
259
+ // If no new persona, keep existing
260
+ if (!newPersona) {
261
+ return existingPersona;
262
+ }
263
+ // Both exist - ask user
264
+ writeInfo("\nšŸ“ Persona Configuration:");
265
+ writeInfo(" Existing persona found in project");
266
+ const { replacePersona } = await inquirer.prompt([
267
+ {
268
+ type: 'confirm',
269
+ name: 'replacePersona',
270
+ message: 'Replace existing persona with the new agent\'s persona?',
271
+ default: false
272
+ }
273
+ ]);
274
+ return replacePersona ? newPersona : existingPersona;
275
+ }
276
+ /**
277
+ * Prompt user about replacing existing welcome message
278
+ */
279
+ async function promptWelcomeMessageReplacement(existingYaml, newWelcomeMessage) {
280
+ const existingWelcomeMessage = existingYaml?.agent?.welcomeMessage;
281
+ // If no existing welcome message, use new one
282
+ if (!existingWelcomeMessage) {
283
+ return newWelcomeMessage;
284
+ }
285
+ // If no new welcome message, keep existing
286
+ if (!newWelcomeMessage) {
287
+ return existingWelcomeMessage;
288
+ }
289
+ // Both exist - ask user
290
+ writeInfo(" Existing welcome message found in project");
291
+ const { replaceWelcomeMessage } = await inquirer.prompt([
292
+ {
293
+ type: 'confirm',
294
+ name: 'replaceWelcomeMessage',
295
+ message: 'Replace existing welcome message with the new agent\'s welcome message?',
296
+ default: false
297
+ }
298
+ ]);
299
+ return replaceWelcomeMessage ? newWelcomeMessage : existingWelcomeMessage;
300
+ }
@@ -160,7 +160,9 @@ function displayLogs(logs, pagination, title) {
160
160
  writeInfo("No logs found.");
161
161
  return;
162
162
  }
163
- logs.forEach((log, index) => {
163
+ // Reverse logs so latest appears at the bottom (oldest first, newest last)
164
+ const reversedLogs = [...logs].reverse();
165
+ reversedLogs.forEach((log, index) => {
164
166
  const timestamp = new Date(log.timestamp).toLocaleString();
165
167
  const icon = getLogIcon(log.subType);
166
168
  const color = getLogColor(log.subType);
@@ -179,7 +181,7 @@ function displayLogs(logs, pagination, title) {
179
181
  ? log.message.substring(0, 200) + '...'
180
182
  : log.message;
181
183
  console.log(` ${message}`);
182
- if (index < logs.length - 1) {
184
+ if (index < reversedLogs.length - 1) {
183
185
  console.log(chalk.gray(` ${'-'.repeat(78)}`));
184
186
  }
185
187
  });
@@ -85,7 +85,14 @@ export async function pushCommand() {
85
85
  writeProgress("šŸ”„ Pushing version to server...");
86
86
  const result = await pushVersion(apiKey, agentId, skillId, skillDeployData);
87
87
  if (result.success && result.data) {
88
- writeSuccess(`āœ… Version ${result.data.version} of "${selectedSkill.name}" pushed successfully`);
88
+ const pushedVersion = result.data.version;
89
+ writeSuccess(`āœ… Version ${pushedVersion} of "${selectedSkill.name}" pushed successfully`);
90
+ // Update YAML with the version returned from server (in case it's different)
91
+ if (pushedVersion !== selectedSkill.version) {
92
+ writeInfo(`šŸ“ Updating YAML with server version: ${pushedVersion}`);
93
+ updateSkillVersionInYaml(selectedSkill.name, pushedVersion);
94
+ selectedSkill.version = pushedVersion;
95
+ }
89
96
  // Ask if user wants to deploy now
90
97
  const deployAnswer = await safePrompt([
91
98
  {
@@ -96,7 +103,7 @@ export async function pushCommand() {
96
103
  }
97
104
  ]);
98
105
  if (deployAnswer && deployAnswer.deployNow) {
99
- await deployVersionAfterPush(apiKey, config.agent.agentId, selectedSkill, result.data.version);
106
+ await deployVersionAfterPush(apiKey, config.agent.agentId, selectedSkill, pushedVersion);
100
107
  }
101
108
  }
102
109
  else if (result.error) {
@@ -145,6 +152,9 @@ async function deployVersionAfterPush(apiKey, agentId, selectedSkill, pushedVers
145
152
  writeProgress("šŸ”„ Publishing version...");
146
153
  const publishResponse = await publishVersion(apiKey, agentId, selectedSkill.skillId, pushedVersion);
147
154
  writeSuccess(`\nāœ… Version ${pushedVersion} of "${selectedSkill.name}" deployed successfully to production\n`);
155
+ // Update YAML with deployed version (should already be updated from push, but ensure consistency)
156
+ writeInfo(`šŸ“ Ensuring YAML is updated with deployed version: ${pushedVersion}`);
157
+ updateSkillVersionInYaml(selectedSkill.name, pushedVersion);
148
158
  }
149
159
  catch (error) {
150
160
  console.error('\nāŒ Error deploying version:', error);
@@ -2,4 +2,8 @@ export declare function copyTemplateFiles(templateDir: string, targetDir: string
2
2
  export declare function createSkillYaml(agentId: string, orgId: string, skillName?: string, skillId?: string, persona?: string, welcomeMessage?: string): void;
3
3
  export declare function readSkillYaml(): any;
4
4
  export declare function readSkillConfig(): any;
5
+ /**
6
+ * Update only the agent information in an existing YAML file
7
+ */
8
+ export declare function updateYamlAgent(agentId: string, orgId: string, persona?: string, welcomeMessage?: string): void;
5
9
  export declare function updateSkillYamlPersona(persona: string, welcomeMessage?: string): void;
@@ -78,6 +78,33 @@ export function readSkillConfig() {
78
78
  // Read YAML config file
79
79
  return readSkillYaml();
80
80
  }
81
+ /**
82
+ * Update only the agent information in an existing YAML file
83
+ */
84
+ export function updateYamlAgent(agentId, orgId, persona, welcomeMessage) {
85
+ const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
86
+ if (!fs.existsSync(yamlPath)) {
87
+ throw new Error('lua.skill.yaml not found');
88
+ }
89
+ const config = readSkillYaml();
90
+ // Update agent information
91
+ config.agent = config.agent || {};
92
+ config.agent.agentId = agentId;
93
+ config.agent.orgId = orgId;
94
+ if (persona) {
95
+ config.agent.persona = persona;
96
+ }
97
+ if (welcomeMessage) {
98
+ config.agent.welcomeMessage = welcomeMessage;
99
+ }
100
+ // Write back to file
101
+ const yamlContent = dump(config, {
102
+ indent: 2,
103
+ lineWidth: -1,
104
+ noRefs: true
105
+ });
106
+ fs.writeFileSync(yamlPath, yamlContent);
107
+ }
81
108
  export function updateSkillYamlPersona(persona, welcomeMessage) {
82
109
  const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
83
110
  if (!fs.existsSync(yamlPath)) {
@@ -32,3 +32,16 @@ export declare function selectBaseAgentType(agentTypes: AgentType[]): AgentType;
32
32
  * @throws Error if agent creation fails
33
33
  */
34
34
  export declare function createNewAgent(apiKey: string, agentType: AgentType, businessConfig: BusinessConfig, metadata: Record<string, any>, features: Record<string, boolean>): Promise<AgentCreationResult>;
35
+ /**
36
+ * Fetches detailed agent information for an existing agent.
37
+ * Used when selecting an existing agent during init.
38
+ *
39
+ * @param apiKey - User's API key
40
+ * @param agentId - Agent ID to fetch
41
+ * @returns Agent details including persona and welcome message
42
+ * @throws Error if fetch fails
43
+ */
44
+ export declare function fetchExistingAgentDetails(apiKey: string, agentId: string): Promise<{
45
+ persona?: string;
46
+ welcomeMessage?: string;
47
+ }>;
@@ -127,3 +127,16 @@ async function fetchAgentDetails(agentApi, agentId) {
127
127
  welcomeMessage: agentDetailsResult.data.welcomeMessage
128
128
  };
129
129
  }
130
+ /**
131
+ * Fetches detailed agent information for an existing agent.
132
+ * Used when selecting an existing agent during init.
133
+ *
134
+ * @param apiKey - User's API key
135
+ * @param agentId - Agent ID to fetch
136
+ * @returns Agent details including persona and welcome message
137
+ * @throws Error if fetch fails
138
+ */
139
+ export async function fetchExistingAgentDetails(apiKey, agentId) {
140
+ const agentApi = new AgentApi(BASE_URLS.API, apiKey);
141
+ return fetchAgentDetails(agentApi, agentId);
142
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lua-cli",
3
- "version": "2.5.5",
3
+ "version": "2.5.6",
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.5",
22
+ "lua-cli": "^2.5.6",
23
23
  "openai": "^5.23.0",
24
24
  "uuid": "^13.0.0",
25
25
  "zod": "^3.24.1"