@l4yercak3/cli 1.2.14 → 1.2.16

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.
@@ -19,7 +19,9 @@
19
19
  "Bash(npm run build:*)",
20
20
  "Bash(npm version:*)",
21
21
  "Bash(npm run lint:fix:*)",
22
- "Bash(cat:*)"
22
+ "Bash(cat:*)",
23
+ "Bash(node:*)",
24
+ "Bash(node -c:*)"
23
25
  ]
24
26
  }
25
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@l4yercak3/cli",
3
- "version": "1.2.14",
3
+ "version": "1.2.16",
4
4
  "description": "Icing on the L4yercak3 - The sweet finishing touch for your Layer Cake integration",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -10,6 +10,7 @@ const chalk = require('chalk');
10
10
  const inquirer = require('inquirer');
11
11
  const projectDetector = require('../detectors');
12
12
  const { showLogo } = require('../logo');
13
+ const { showPostLoginMenu, executeMenuAction } = require('../utils/prompt-utils');
13
14
 
14
15
  /**
15
16
  * Generate retro Windows 95 style HTML page
@@ -262,7 +263,9 @@ async function promptSetupWizard() {
262
263
  ]);
263
264
 
264
265
  if (!reconfigure) {
265
- console.log(chalk.gray('\n Run "l4yercak3 spread" anytime to reconfigure.\n'));
266
+ console.log('');
267
+ const action = await showPostLoginMenu({ isInProject: true, hasExistingConfig: true });
268
+ await executeMenuAction(action);
266
269
  return;
267
270
  }
268
271
  } else {
@@ -276,7 +279,9 @@ async function promptSetupWizard() {
276
279
  ]);
277
280
 
278
281
  if (!runWizard) {
279
- console.log(chalk.gray('\n Run "l4yercak3 spread" anytime to set up your project.\n'));
282
+ console.log('');
283
+ const action = await showPostLoginMenu({ isInProject: true, hasExistingConfig: false });
284
+ await executeMenuAction(action);
280
285
  return;
281
286
  }
282
287
  }
@@ -13,6 +13,7 @@ const { generateProjectPathHash } = require('../utils/file-utils');
13
13
  const inquirer = require('inquirer');
14
14
  const chalk = require('chalk');
15
15
  const pkg = require('../../package.json');
16
+ const { showMainMenu, executeMenuAction } = require('../utils/prompt-utils');
16
17
 
17
18
  /**
18
19
  * Helper function to create an organization
@@ -108,7 +109,9 @@ async function handleSpread() {
108
109
 
109
110
  if (!continueAnyway) {
110
111
  console.log(chalk.gray('\n Setup cancelled.\n'));
111
- process.exit(0);
112
+ const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
113
+ await executeMenuAction(action);
114
+ return;
112
115
  }
113
116
  }
114
117
 
@@ -284,7 +287,9 @@ async function handleSpread() {
284
287
  console.log(chalk.cyan('\n To continue, either:'));
285
288
  console.log(chalk.gray(' • Delete an existing key at https://app.l4yercak3.com?openWindow=integrations&panel=api-keys'));
286
289
  console.log(chalk.gray(' • Upgrade your plan at https://app.l4yercak3.com?openWindow=store\n'));
287
- process.exit(0);
290
+ const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
291
+ await executeMenuAction(action);
292
+ return;
288
293
  } else if (existingKeys && existingKeys.keys && existingKeys.keys.length > 0) {
289
294
  // Has existing keys on backend - offer to reuse or generate new
290
295
  const activeKeys = existingKeys.keys.filter(k => k.status === 'active');
@@ -350,7 +355,9 @@ async function handleSpread() {
350
355
  console.log(chalk.cyan('\n To continue, either:'));
351
356
  console.log(chalk.gray(' • Delete an existing key at https://app.l4yercak3.com?openWindow=integrations&panel=api-keys'));
352
357
  console.log(chalk.gray(' • Upgrade your plan at https://app.l4yercak3.com?openWindow=store\n'));
353
- process.exit(0);
358
+ const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
359
+ await executeMenuAction(action);
360
+ return;
354
361
  } else if (error.code === 'API_KEY_ALREADY_LINKED') {
355
362
  console.log(chalk.yellow(`\n ⚠️ ${error.message}`));
356
363
  if (error.suggestion) {
@@ -372,7 +379,9 @@ async function handleSpread() {
372
379
  if (generateNew) {
373
380
  apiKey = await generateNewApiKey(organizationId);
374
381
  } else {
375
- process.exit(0);
382
+ const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
383
+ await executeMenuAction(action);
384
+ return;
376
385
  }
377
386
  } else if (error.code === 'SESSION_EXPIRED' || error.code === 'INVALID_SESSION') {
378
387
  console.log(chalk.red(`\n ❌ Session expired. Please run "l4yercak3 login" again.\n`));
@@ -73,7 +73,7 @@ Returns an API key for the application to use.`,
73
73
  required: ['name', 'features'],
74
74
  },
75
75
  requiresAuth: true,
76
- requiredPermissions: ['manage_applications'],
76
+ requiredPermissions: ['applications:write'],
77
77
  handler: async (params, authContext) => {
78
78
  // Generate project path hash for tracking
79
79
  const projectPathHash = params.projectPath
@@ -293,7 +293,7 @@ Returns an API key for the application to use.`,
293
293
  required: ['applicationId'],
294
294
  },
295
295
  requiresAuth: true,
296
- requiredPermissions: ['manage_applications'],
296
+ requiredPermissions: ['applications:write'],
297
297
  handler: async (params, authContext) => {
298
298
  const { applicationId, ...updates } = params;
299
299
 
@@ -339,7 +339,7 @@ Triggers a sync based on configured model mappings.`,
339
339
  required: ['applicationId'],
340
340
  },
341
341
  requiresAuth: true,
342
- requiredPermissions: ['manage_applications'],
342
+ requiredPermissions: ['applications:write'],
343
343
  handler: async (params, authContext) => {
344
344
  const response = await backendClient.syncApplication(params.applicationId, {
345
345
  direction: params.direction || 'bidirectional',
@@ -403,7 +403,7 @@ Model mappings define how local models sync with L4YERCAK3 types.`,
403
403
  required: ['applicationId', 'localModel', 'layerCakeType'],
404
404
  },
405
405
  requiresAuth: true,
406
- requiredPermissions: ['manage_applications'],
406
+ requiredPermissions: ['applications:write'],
407
407
  handler: async (params, authContext) => {
408
408
  // Get current application
409
409
  const appResponse = await backendClient.getApplication(params.applicationId);
@@ -49,7 +49,7 @@ Returns contacts with their details including name, email, phone, and custom fie
49
49
  },
50
50
  },
51
51
  requiresAuth: true,
52
- requiredPermissions: ['view_crm'],
52
+ requiredPermissions: ['contacts:read'],
53
53
  handler: async (params, authContext) => {
54
54
  // Build query params
55
55
  const queryParams = new URLSearchParams();
@@ -130,7 +130,7 @@ Use this to add a customer, lead, or prospect to the user's CRM.`,
130
130
  required: ['firstName', 'lastName', 'email'],
131
131
  },
132
132
  requiresAuth: true,
133
- requiredPermissions: ['manage_crm'],
133
+ requiredPermissions: ['contacts:write'],
134
134
  handler: async (params, authContext) => {
135
135
  const response = await backendClient.request('POST', '/api/v1/crm/contacts', {
136
136
  organizationId: authContext.organizationId,
@@ -177,7 +177,7 @@ Use this to retrieve full details including activities and notes.`,
177
177
  required: ['contactId'],
178
178
  },
179
179
  requiresAuth: true,
180
- requiredPermissions: ['view_crm'],
180
+ requiredPermissions: ['contacts:read'],
181
181
  handler: async (params, authContext) => {
182
182
  const queryParams = new URLSearchParams();
183
183
  if (params.includeActivities) queryParams.set('includeActivities', 'true');
@@ -242,7 +242,7 @@ Use this to modify contact information.`,
242
242
  required: ['contactId'],
243
243
  },
244
244
  requiresAuth: true,
245
- requiredPermissions: ['manage_crm'],
245
+ requiredPermissions: ['contacts:write'],
246
246
  handler: async (params, authContext) => {
247
247
  const { contactId, ...updates } = params;
248
248
 
@@ -273,7 +273,7 @@ This performs a soft delete - the contact can be restored.`,
273
273
  required: ['contactId'],
274
274
  },
275
275
  requiresAuth: true,
276
- requiredPermissions: ['manage_crm'],
276
+ requiredPermissions: ['contacts:write'],
277
277
  handler: async (params, authContext) => {
278
278
  await backendClient.request('DELETE', `/api/v1/crm/contacts/${params.contactId}`);
279
279
 
@@ -311,7 +311,7 @@ These are customer companies, not L4YERCAK3 platform organizations.`,
311
311
  },
312
312
  },
313
313
  requiresAuth: true,
314
- requiredPermissions: ['view_crm'],
314
+ requiredPermissions: ['contacts:read'],
315
315
  handler: async (params, authContext) => {
316
316
  const queryParams = new URLSearchParams();
317
317
  queryParams.set('organizationId', authContext.organizationId);
@@ -386,7 +386,7 @@ Use this to track a customer company in the CRM.`,
386
386
  required: ['name'],
387
387
  },
388
388
  requiresAuth: true,
389
- requiredPermissions: ['manage_crm'],
389
+ requiredPermissions: ['contacts:write'],
390
390
  handler: async (params, authContext) => {
391
391
  const response = await backendClient.request('POST', '/api/v1/crm/organizations', {
392
392
  organizationId: authContext.organizationId,
@@ -426,7 +426,7 @@ Use this to track a customer company in the CRM.`,
426
426
  required: ['crmOrganizationId'],
427
427
  },
428
428
  requiresAuth: true,
429
- requiredPermissions: ['view_crm'],
429
+ requiredPermissions: ['contacts:read'],
430
430
  handler: async (params, authContext) => {
431
431
  const queryParams = new URLSearchParams();
432
432
  if (params.includeContacts) queryParams.set('includeContacts', 'true');
@@ -488,7 +488,7 @@ Use this to associate a contact with a company they work for.`,
488
488
  required: ['contactId', 'crmOrganizationId'],
489
489
  },
490
490
  requiresAuth: true,
491
- requiredPermissions: ['manage_crm'],
491
+ requiredPermissions: ['contacts:write'],
492
492
  handler: async (params, authContext) => {
493
493
  await backendClient.request('POST', '/api/v1/crm/contact-organization-links', {
494
494
  contactId: params.contactId,
@@ -527,7 +527,7 @@ Use this to record information or interactions with a contact.`,
527
527
  required: ['contactId', 'content'],
528
528
  },
529
529
  requiresAuth: true,
530
- requiredPermissions: ['manage_crm'],
530
+ requiredPermissions: ['contacts:write'],
531
531
  handler: async (params, authContext) => {
532
532
  await backendClient.request('POST', `/api/v1/crm/contacts/${params.contactId}/notes`, {
533
533
  content: params.content,
@@ -572,7 +572,7 @@ Use this to track interactions with contacts.`,
572
572
  required: ['contactId', 'type', 'summary'],
573
573
  },
574
574
  requiresAuth: true,
575
- requiredPermissions: ['manage_crm'],
575
+ requiredPermissions: ['contacts:write'],
576
576
  handler: async (params, authContext) => {
577
577
  await backendClient.request('POST', `/api/v1/crm/contacts/${params.contactId}/activities`, {
578
578
  type: params.type,
@@ -51,7 +51,7 @@ Returns events with their basic details, status, and dates.`,
51
51
  },
52
52
  },
53
53
  requiresAuth: true,
54
- requiredPermissions: ['view_events'],
54
+ requiredPermissions: ['events:read'],
55
55
  handler: async (params, authContext) => {
56
56
  const queryParams = new URLSearchParams();
57
57
  queryParams.set('organizationId', authContext.organizationId);
@@ -128,7 +128,7 @@ Events start in 'draft' status and can be published when ready.`,
128
128
  required: ['name', 'startDate', 'endDate', 'location'],
129
129
  },
130
130
  requiresAuth: true,
131
- requiredPermissions: ['manage_events'],
131
+ requiredPermissions: ['events:write'],
132
132
  handler: async (params, authContext) => {
133
133
  // Convert ISO dates to timestamps
134
134
  const startDate = new Date(params.startDate).getTime();
@@ -184,7 +184,7 @@ Includes agenda, products, and optionally sponsors.`,
184
184
  required: ['eventId'],
185
185
  },
186
186
  requiresAuth: true,
187
- requiredPermissions: ['view_events'],
187
+ requiredPermissions: ['events:read'],
188
188
  handler: async (params, authContext) => {
189
189
  const queryParams = new URLSearchParams();
190
190
  if (params.includeProducts !== false) queryParams.set('includeProducts', 'true');
@@ -245,7 +245,7 @@ Can update name, dates, location, and other properties.`,
245
245
  required: ['eventId'],
246
246
  },
247
247
  requiresAuth: true,
248
- requiredPermissions: ['manage_events'],
248
+ requiredPermissions: ['events:write'],
249
249
  handler: async (params, authContext) => {
250
250
  const { eventId, startDate, endDate, ...updates } = params;
251
251
 
@@ -278,7 +278,7 @@ Changes status from 'draft' to 'published'.`,
278
278
  required: ['eventId'],
279
279
  },
280
280
  requiresAuth: true,
281
- requiredPermissions: ['manage_events'],
281
+ requiredPermissions: ['events:write'],
282
282
  handler: async (params, authContext) => {
283
283
  await backendClient.request('POST', `/api/v1/events/${params.eventId}/publish`);
284
284
 
@@ -306,7 +306,7 @@ Sets status to 'cancelled'. This is a soft delete.`,
306
306
  required: ['eventId'],
307
307
  },
308
308
  requiresAuth: true,
309
- requiredPermissions: ['manage_events'],
309
+ requiredPermissions: ['events:write'],
310
310
  handler: async (params, authContext) => {
311
311
  await backendClient.request('POST', `/api/v1/events/${params.eventId}/cancel`);
312
312
 
@@ -371,7 +371,7 @@ Replace the entire agenda with a new list of agenda items.`,
371
371
  required: ['eventId', 'agenda'],
372
372
  },
373
373
  requiresAuth: true,
374
- requiredPermissions: ['manage_events'],
374
+ requiredPermissions: ['events:write'],
375
375
  handler: async (params, authContext) => {
376
376
  await backendClient.request('PATCH', `/api/v1/events/${params.eventId}/agenda`, {
377
377
  agenda: params.agenda,
@@ -403,7 +403,7 @@ Replace the entire agenda with a new list of agenda items.`,
403
403
  required: ['eventId'],
404
404
  },
405
405
  requiresAuth: true,
406
- requiredPermissions: ['view_events'],
406
+ requiredPermissions: ['events:read'],
407
407
  handler: async (params, authContext) => {
408
408
  const response = await backendClient.request(
409
409
  'GET',
@@ -470,7 +470,7 @@ Products can be tickets, merchandise, or add-ons.`,
470
470
  required: ['eventId', 'name', 'priceInCents'],
471
471
  },
472
472
  requiresAuth: true,
473
- requiredPermissions: ['manage_events'],
473
+ requiredPermissions: ['events:write'],
474
474
  handler: async (params, authContext) => {
475
475
  const response = await backendClient.request('POST', '/api/v1/products', {
476
476
  organizationId: authContext.organizationId,
@@ -519,7 +519,7 @@ Returns people who have purchased tickets.`,
519
519
  required: ['eventId'],
520
520
  },
521
521
  requiresAuth: true,
522
- requiredPermissions: ['view_events'],
522
+ requiredPermissions: ['events:read'],
523
523
  handler: async (params, authContext) => {
524
524
  const queryParams = new URLSearchParams();
525
525
  if (params.status) queryParams.set('status', params.status);
@@ -571,7 +571,7 @@ Sponsors are CRM organizations linked to the event.`,
571
571
  required: ['eventId'],
572
572
  },
573
573
  requiresAuth: true,
574
- requiredPermissions: ['view_events'],
574
+ requiredPermissions: ['events:read'],
575
575
  handler: async (params, authContext) => {
576
576
  const queryParams = new URLSearchParams();
577
577
  if (params.sponsorLevel) queryParams.set('sponsorLevel', params.sponsorLevel);
@@ -629,7 +629,7 @@ Sponsors are CRM organizations linked to the event.`,
629
629
  required: ['eventId', 'crmOrganizationId'],
630
630
  },
631
631
  requiresAuth: true,
632
- requiredPermissions: ['manage_events'],
632
+ requiredPermissions: ['events:write'],
633
633
  handler: async (params, authContext) => {
634
634
  await backendClient.request('POST', `/api/v1/events/${params.eventId}/sponsors`, {
635
635
  crmOrganizationId: params.crmOrganizationId,
@@ -43,7 +43,7 @@ Returns forms with their type, status, and submission counts.`,
43
43
  },
44
44
  },
45
45
  requiresAuth: true,
46
- requiredPermissions: ['view_forms'],
46
+ requiredPermissions: ['forms:read'],
47
47
  handler: async (params, authContext) => {
48
48
  const queryParams = new URLSearchParams();
49
49
  queryParams.set('organizationId', authContext.organizationId);
@@ -197,7 +197,7 @@ Start with basic info, then add fields.`,
197
197
  required: ['name'],
198
198
  },
199
199
  requiresAuth: true,
200
- requiredPermissions: ['manage_forms'],
200
+ requiredPermissions: ['forms:write'],
201
201
  handler: async (params, authContext) => {
202
202
  // Build form schema
203
203
  const formSchema = {
@@ -259,7 +259,7 @@ Start with basic info, then add fields.`,
259
259
  required: ['formId'],
260
260
  },
261
261
  requiresAuth: true,
262
- requiredPermissions: ['view_forms'],
262
+ requiredPermissions: ['forms:read'],
263
263
  handler: async (params, authContext) => {
264
264
  const response = await backendClient.request('GET', `/api/v1/forms/${params.formId}`);
265
265
 
@@ -312,7 +312,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
312
312
  required: ['formId'],
313
313
  },
314
314
  requiresAuth: true,
315
- requiredPermissions: ['manage_forms'],
315
+ requiredPermissions: ['forms:write'],
316
316
  handler: async (params, authContext) => {
317
317
  const { formId, ...updates } = params;
318
318
 
@@ -395,7 +395,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
395
395
  required: ['formId', 'field'],
396
396
  },
397
397
  requiresAuth: true,
398
- requiredPermissions: ['manage_forms'],
398
+ requiredPermissions: ['forms:write'],
399
399
  handler: async (params, authContext) => {
400
400
  // Get current form to add field
401
401
  const formResponse = await backendClient.request('GET', `/api/v1/forms/${params.formId}`);
@@ -439,7 +439,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
439
439
  required: ['formId'],
440
440
  },
441
441
  requiresAuth: true,
442
- requiredPermissions: ['manage_forms'],
442
+ requiredPermissions: ['forms:write'],
443
443
  handler: async (params, authContext) => {
444
444
  await backendClient.request('POST', `/api/v1/forms/${params.formId}/publish`);
445
445
 
@@ -466,7 +466,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
466
466
  required: ['formId'],
467
467
  },
468
468
  requiresAuth: true,
469
- requiredPermissions: ['manage_forms'],
469
+ requiredPermissions: ['forms:write'],
470
470
  handler: async (params, authContext) => {
471
471
  await backendClient.request('POST', `/api/v1/forms/${params.formId}/unpublish`);
472
472
 
@@ -493,7 +493,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
493
493
  required: ['formId'],
494
494
  },
495
495
  requiresAuth: true,
496
- requiredPermissions: ['manage_forms'],
496
+ requiredPermissions: ['forms:write'],
497
497
  handler: async (params, authContext) => {
498
498
  await backendClient.request('DELETE', `/api/v1/forms/${params.formId}`);
499
499
 
@@ -518,7 +518,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
518
518
  required: ['formId'],
519
519
  },
520
520
  requiresAuth: true,
521
- requiredPermissions: ['manage_forms'],
521
+ requiredPermissions: ['forms:write'],
522
522
  handler: async (params, authContext) => {
523
523
  const response = await backendClient.request(
524
524
  'POST',
@@ -563,7 +563,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
563
563
  required: ['formId'],
564
564
  },
565
565
  requiresAuth: true,
566
- requiredPermissions: ['view_forms'],
566
+ requiredPermissions: ['forms:read'],
567
567
  handler: async (params, authContext) => {
568
568
  const queryParams = new URLSearchParams();
569
569
  if (params.status) queryParams.set('status', params.status);
@@ -602,7 +602,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
602
602
  required: ['responseId'],
603
603
  },
604
604
  requiresAuth: true,
605
- requiredPermissions: ['view_forms'],
605
+ requiredPermissions: ['forms:read'],
606
606
  handler: async (params, authContext) => {
607
607
  const response = await backendClient.request(
608
608
  'GET',
@@ -646,7 +646,7 @@ To update fields, use l4yercak3_forms_add_field or l4yercak3_forms_update_fields
646
646
  required: ['formId'],
647
647
  },
648
648
  requiresAuth: true,
649
- requiredPermissions: ['view_forms'],
649
+ requiredPermissions: ['forms:read'],
650
650
  handler: async (params, authContext) => {
651
651
  const format = params.format || 'json';
652
652
 
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Prompt Utilities
3
+ * Shared prompt helpers for consistent CLI UX
4
+ * Auto-generated by @l4yercak3/cli
5
+ */
6
+
7
+ const inquirer = require('inquirer');
8
+ const chalk = require('chalk');
9
+
10
+ /**
11
+ * Show a menu with options and handle selection
12
+ * Always includes an Exit option at the end
13
+ *
14
+ * @param {Object} options - Menu configuration
15
+ * @param {string} options.message - Menu header message
16
+ * @param {Array<{name: string, value: string}>} options.choices - Menu choices
17
+ * @returns {Promise<string>} The selected action value
18
+ */
19
+ async function showActionMenu(options) {
20
+ const { message = 'What would you like to do?', choices = [] } = options;
21
+
22
+ // Always include exit option at the end
23
+ const menuChoices = [
24
+ ...choices,
25
+ new inquirer.Separator(),
26
+ { name: 'Exit', value: 'exit' },
27
+ ];
28
+
29
+ const { action } = await inquirer.prompt([
30
+ {
31
+ type: 'list',
32
+ name: 'action',
33
+ message,
34
+ choices: menuChoices,
35
+ },
36
+ ]);
37
+
38
+ // Handle exit
39
+ if (action === 'exit') {
40
+ console.log(chalk.gray('\n Goodbye!\n'));
41
+ process.exit(0);
42
+ }
43
+
44
+ return action;
45
+ }
46
+
47
+ /**
48
+ * Get the full list of available CLI commands as menu choices
49
+ * @param {Object} context - Context for filtering options
50
+ * @param {boolean} context.isLoggedIn - Whether user is logged in
51
+ * @param {boolean} context.isInProject - Whether CLI is running in a project directory
52
+ * @param {boolean} context.hasExistingConfig - Whether project already has L4YERCAK3 config
53
+ */
54
+ function getCommandChoices(context = {}) {
55
+ const { isLoggedIn = true, isInProject = false, hasExistingConfig = false } = context;
56
+
57
+ const choices = [];
58
+
59
+ // Project setup commands (when in a project)
60
+ if (isInProject) {
61
+ if (hasExistingConfig) {
62
+ choices.push({
63
+ name: 'Reconfigure project l4yercak3 spread',
64
+ value: 'spread',
65
+ });
66
+ } else {
67
+ choices.push({
68
+ name: 'Set up L4YERCAK3 l4yercak3 spread',
69
+ value: 'spread',
70
+ });
71
+ }
72
+ } else {
73
+ choices.push({
74
+ name: 'Initialize project l4yercak3 spread',
75
+ value: 'spread',
76
+ });
77
+ }
78
+
79
+ // Always available commands (when logged in)
80
+ if (isLoggedIn) {
81
+ choices.push(
82
+ { name: 'View account status l4yercak3 status', value: 'status' },
83
+ { name: 'Manage API keys l4yercak3 api-keys', value: 'api-keys' },
84
+ { name: 'Set up MCP server l4yercak3 mcp-setup', value: 'mcp-setup' },
85
+ { name: 'Check for updates l4yercak3 upgrade', value: 'upgrade' },
86
+ new inquirer.Separator(),
87
+ { name: 'Log out l4yercak3 logout', value: 'logout' },
88
+ );
89
+ } else {
90
+ choices.push(
91
+ { name: 'Log in l4yercak3 login', value: 'login' },
92
+ { name: 'Check for updates l4yercak3 upgrade', value: 'upgrade' },
93
+ );
94
+ }
95
+
96
+ return choices;
97
+ }
98
+
99
+ /**
100
+ * Show main menu with all available commands
101
+ * @param {Object} context - Context for menu options
102
+ * @param {boolean} context.isLoggedIn - Whether user is logged in
103
+ * @param {boolean} context.isInProject - Whether CLI is running in a project directory
104
+ * @param {boolean} context.hasExistingConfig - Whether project already has L4YERCAK3 config
105
+ * @returns {Promise<string>} The selected action value
106
+ */
107
+ async function showMainMenu(context = {}) {
108
+ const choices = getCommandChoices(context);
109
+
110
+ return await showActionMenu({
111
+ message: 'What would you like to do?',
112
+ choices,
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Show post-login menu with common actions
118
+ * @param {Object} context - Context for menu options
119
+ * @param {boolean} context.isInProject - Whether CLI is running in a project directory
120
+ * @param {boolean} context.hasExistingConfig - Whether project already has L4YERCAK3 config
121
+ */
122
+ async function showPostLoginMenu(context = {}) {
123
+ return await showMainMenu({ ...context, isLoggedIn: true });
124
+ }
125
+
126
+ /**
127
+ * Execute a menu action by calling the appropriate command handler
128
+ * @param {string} action - The action to execute
129
+ * @returns {Promise<void>}
130
+ */
131
+ async function executeMenuAction(action) {
132
+ console.log('');
133
+
134
+ switch (action) {
135
+ case 'spread': {
136
+ const { handler } = require('../commands/spread');
137
+ await handler();
138
+ break;
139
+ }
140
+ case 'status': {
141
+ const { handler } = require('../commands/status');
142
+ await handler();
143
+ break;
144
+ }
145
+ case 'api-keys': {
146
+ const { handler } = require('../commands/api-keys');
147
+ await handler();
148
+ break;
149
+ }
150
+ case 'logout': {
151
+ const { handler } = require('../commands/logout');
152
+ await handler();
153
+ break;
154
+ }
155
+ case 'login': {
156
+ const { handler } = require('../commands/login');
157
+ await handler();
158
+ break;
159
+ }
160
+ case 'mcp-setup': {
161
+ const { handler } = require('../commands/mcp-setup');
162
+ await handler();
163
+ break;
164
+ }
165
+ case 'upgrade': {
166
+ const { handler } = require('../commands/upgrade');
167
+ await handler();
168
+ break;
169
+ }
170
+ default:
171
+ // Unknown action, just return
172
+ break;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Show menu and execute selected action
178
+ * Convenience function that combines showMainMenu + executeMenuAction
179
+ * @param {Object} context - Context for menu options
180
+ * @returns {Promise<string>} The selected action value
181
+ */
182
+ async function showMenuAndExecute(context = {}) {
183
+ const action = await showMainMenu(context);
184
+ await executeMenuAction(action);
185
+ return action;
186
+ }
187
+
188
+ module.exports = {
189
+ showActionMenu,
190
+ showMainMenu,
191
+ showPostLoginMenu,
192
+ getCommandChoices,
193
+ executeMenuAction,
194
+ showMenuAndExecute,
195
+ };