@zapier/zapier-sdk-cli 0.13.2 → 0.13.4

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.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
- import { ZapierSdk } from '@zapier/zapier-sdk';
1
+ import { ZapierSdk, BaseEvent } from '@zapier/zapier-sdk';
2
2
 
3
3
  interface ZapierCliSdkOptions {
4
4
  debug?: boolean;
5
+ eventEmission?: {
6
+ enabled?: boolean;
7
+ transport?: {
8
+ type: "http" | "console" | "noop";
9
+ endpoint?: string;
10
+ headers?: Record<string, string>;
11
+ };
12
+ };
5
13
  }
6
14
  /**
7
15
  * Create a Zapier SDK instance configured specifically for the CLI
@@ -9,4 +17,85 @@ interface ZapierCliSdkOptions {
9
17
  */
10
18
  declare function createZapierCliSdk(options?: ZapierCliSdkOptions): ZapierSdk;
11
19
 
12
- export { type ZapierCliSdkOptions, createZapierCliSdk };
20
+ /**
21
+ * CLI-specific telemetry event definitions
22
+ */
23
+
24
+ /**
25
+ * Event emitted when a CLI command is executed
26
+ */
27
+ interface CliCommandExecutedEvent extends BaseEvent {
28
+ system_name: string;
29
+ session_id?: string | null;
30
+ cli_version?: string | null;
31
+ cli_arguments?: (string | null)[] | null;
32
+ cli_primary_command?: string | null;
33
+ os_platform?: string | null;
34
+ os_release?: string | null;
35
+ os_architecture?: string | null;
36
+ platform_versions?: Record<string, string | null> | null;
37
+ selected_api?: string | null;
38
+ app_id?: number | null;
39
+ app_version_id?: number | null;
40
+ execution_duration_ms?: number | null;
41
+ success_flag: boolean;
42
+ exit_code?: number | null;
43
+ error_message?: string | null;
44
+ command_category?: string | null;
45
+ requires_auth?: boolean | null;
46
+ is_ci_environment?: boolean | null;
47
+ ci_platform?: string | null;
48
+ package_manager?: string | null;
49
+ made_network_requests?: boolean | null;
50
+ files_modified_count?: number | null;
51
+ files_created_count?: number | null;
52
+ files_processed_size_bytes?: number | null;
53
+ peak_memory_usage_bytes?: number | null;
54
+ cpu_time_ms?: number | null;
55
+ subprocess_count?: number | null;
56
+ }
57
+
58
+ /**
59
+ * CLI-specific event builders
60
+ *
61
+ * Provides builder functions for CLI command telemetry that auto-populate
62
+ * common CLI fields and system information.
63
+ */
64
+
65
+ interface CliEventContext {
66
+ customuser_id?: number | null;
67
+ account_id?: number | null;
68
+ identity_id?: number | null;
69
+ visitor_id?: string | null;
70
+ correlation_id?: string | null;
71
+ session_id?: string | null;
72
+ selected_api?: string | null;
73
+ app_id?: number | null;
74
+ app_version_id?: number | null;
75
+ }
76
+ interface CliCommandExecutedEventData {
77
+ cli_primary_command: string;
78
+ success_flag: boolean;
79
+ execution_duration_ms?: number | null;
80
+ exit_code?: number | null;
81
+ error_message?: string | null;
82
+ command_category?: string | null;
83
+ requires_auth?: boolean | null;
84
+ cli_arguments?: (string | null)[] | null;
85
+ cli_version?: string | null;
86
+ made_network_requests?: boolean | null;
87
+ files_modified_count?: number | null;
88
+ files_created_count?: number | null;
89
+ files_processed_size_bytes?: number | null;
90
+ peak_memory_usage_bytes?: number | null;
91
+ cpu_time_ms?: number | null;
92
+ subprocess_count?: number | null;
93
+ package_manager?: string | null;
94
+ }
95
+ declare function buildCliCommandExecutedEvent({ data, context, cliVersion, }: {
96
+ data: CliCommandExecutedEventData;
97
+ context?: CliEventContext;
98
+ cliVersion?: string;
99
+ }): CliCommandExecutedEvent;
100
+
101
+ export { type CliCommandExecutedEvent, type CliCommandExecutedEventData, type CliEventContext, type ZapierCliSdkOptions, buildCliCommandExecutedEvent, createZapierCliSdk };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, createZapierSdkWithoutRegistry, registryPlugin, toSnakeCase } from '@zapier/zapier-sdk';
1
+ import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, getOsInfo, getPlatformVersions, getCiPlatform, isCi, createZapierSdkWithoutRegistry, registryPlugin, getReleaseId, getCurrentTimestamp, generateEventId, toSnakeCase } from '@zapier/zapier-sdk';
2
2
  import open from 'open';
3
3
  import crypto from 'crypto';
4
4
  import express from 'express';
@@ -230,7 +230,66 @@ var LoginSchema = z.object({
230
230
  timeout: z.string().optional().describe("Login timeout in seconds (default: 300)")
231
231
  }).describe("Log in to Zapier to access your account");
232
232
 
233
+ // package.json
234
+ var package_default = {
235
+ version: "0.13.4"};
236
+
237
+ // src/telemetry/builders.ts
238
+ function createCliBaseEvent(context = {}) {
239
+ return {
240
+ event_id: generateEventId(),
241
+ timestamp_ms: getCurrentTimestamp(),
242
+ release_id: getReleaseId(),
243
+ customuser_id: context.customuser_id ?? null,
244
+ account_id: context.account_id ?? null,
245
+ identity_id: context.identity_id ?? null,
246
+ visitor_id: context.visitor_id ?? null,
247
+ correlation_id: context.correlation_id ?? null
248
+ };
249
+ }
250
+ function buildCliCommandExecutedEvent({
251
+ data,
252
+ context = {},
253
+ cliVersion = package_default.version
254
+ }) {
255
+ const osInfo = getOsInfo();
256
+ const platformVersions = getPlatformVersions();
257
+ return {
258
+ ...createCliBaseEvent(context),
259
+ system_name: "zapier-sdk-cli",
260
+ session_id: context.session_id ?? null,
261
+ cli_version: data.cli_version ?? cliVersion,
262
+ cli_arguments: data.cli_arguments ?? null,
263
+ cli_primary_command: data.cli_primary_command,
264
+ os_platform: osInfo.platform,
265
+ os_release: osInfo.release,
266
+ os_architecture: osInfo.architecture,
267
+ platform_versions: platformVersions,
268
+ selected_api: context.selected_api ?? null,
269
+ app_id: context.app_id ?? null,
270
+ app_version_id: context.app_version_id ?? null,
271
+ execution_duration_ms: data.execution_duration_ms ?? null,
272
+ success_flag: data.success_flag,
273
+ exit_code: data.exit_code ?? (data.success_flag ? 0 : 1),
274
+ error_message: data.error_message ?? null,
275
+ command_category: data.command_category ?? null,
276
+ requires_auth: data.requires_auth ?? null,
277
+ is_ci_environment: isCi(),
278
+ ci_platform: getCiPlatform(),
279
+ package_manager: data.package_manager ?? "pnpm",
280
+ // Default based on project setup
281
+ made_network_requests: data.made_network_requests ?? null,
282
+ files_modified_count: data.files_modified_count ?? null,
283
+ files_created_count: data.files_created_count ?? null,
284
+ files_processed_size_bytes: data.files_processed_size_bytes ?? null,
285
+ peak_memory_usage_bytes: data.peak_memory_usage_bytes ?? null,
286
+ cpu_time_ms: data.cpu_time_ms ?? null,
287
+ subprocess_count: data.subprocess_count ?? null
288
+ };
289
+ }
290
+
233
291
  // src/plugins/login/index.ts
292
+ var CLI_COMMAND_EXECUTED_EVENT_SUBJECT = "platform.sdk.CliCommandExecutedEvent";
234
293
  var loginWithSdk = createFunction(async function loginWithSdk2(options) {
235
294
  const timeoutSeconds = options.timeout ? parseInt(options.timeout, 10) : 300;
236
295
  if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
@@ -241,17 +300,56 @@ var loginWithSdk = createFunction(async function loginWithSdk2(options) {
241
300
  console.log(`\u2705 Successfully logged in as ${user.email}`);
242
301
  setTimeout(() => process.exit(0), 100);
243
302
  }, LoginSchema);
244
- var loginPlugin = () => ({
245
- login: loginWithSdk,
246
- context: {
247
- meta: {
248
- login: {
249
- categories: ["account"],
250
- inputSchema: LoginSchema
303
+ var loginPlugin = ({ context }) => {
304
+ const loginWithTelemetry = async (options) => {
305
+ const startTime = Date.now();
306
+ let success = false;
307
+ let errorMessage = null;
308
+ try {
309
+ await loginWithSdk(options);
310
+ success = true;
311
+ } catch (error) {
312
+ success = false;
313
+ errorMessage = error instanceof Error ? error.message : "Login failed";
314
+ throw error;
315
+ } finally {
316
+ const event = buildCliCommandExecutedEvent({
317
+ data: {
318
+ cli_primary_command: "login",
319
+ success_flag: success,
320
+ execution_duration_ms: Date.now() - startTime,
321
+ exit_code: success ? 0 : 1,
322
+ error_message: errorMessage,
323
+ command_category: "authentication",
324
+ requires_auth: false,
325
+ cli_arguments: [
326
+ "login",
327
+ options.timeout ? `--timeout=${options.timeout}` : null
328
+ ].filter(Boolean)
329
+ },
330
+ context: {
331
+ session_id: context.session_id,
332
+ selected_api: context.selected_api,
333
+ app_id: context.app_id,
334
+ app_version_id: context.app_version_id
335
+ },
336
+ cliVersion: package_default.version
337
+ });
338
+ context.eventEmission.emit(CLI_COMMAND_EXECUTED_EVENT_SUBJECT, event);
339
+ }
340
+ };
341
+ return {
342
+ login: loginWithTelemetry,
343
+ context: {
344
+ meta: {
345
+ login: {
346
+ categories: ["account"],
347
+ inputSchema: LoginSchema
348
+ }
251
349
  }
252
350
  }
253
- }
254
- });
351
+ };
352
+ };
255
353
  var LogoutSchema = z.object({}).describe("Log out of your Zapier account");
256
354
 
257
355
  // src/plugins/logout/index.ts
@@ -1106,7 +1204,8 @@ var addPlugin = ({ sdk, context }) => {
1106
1204
  // src/sdk.ts
1107
1205
  function createZapierCliSdk(options = {}) {
1108
1206
  let sdk = createZapierSdkWithoutRegistry({
1109
- debug: options.debug
1207
+ debug: options.debug,
1208
+ eventEmission: options.eventEmission
1110
1209
  });
1111
1210
  sdk = sdk.addPlugin(bundleCodePlugin);
1112
1211
  sdk = sdk.addPlugin(getLoginConfigPathPlugin);
@@ -1118,4 +1217,4 @@ function createZapierCliSdk(options = {}) {
1118
1217
  return finalSdk;
1119
1218
  }
1120
1219
 
1121
- export { createZapierCliSdk };
1220
+ export { buildCliCommandExecutedEvent, createZapierCliSdk };
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -1 +1,4 @@
1
1
  export { createZapierCliSdk, type ZapierCliSdkOptions } from "./sdk";
2
+ export type { CliEventContext, CliCommandExecutedEventData, } from "./telemetry/builders";
3
+ export { buildCliCommandExecutedEvent } from "./telemetry/builders";
4
+ export type { CliCommandExecutedEvent } from "./telemetry/events";
package/dist/src/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  // Main exports for the CLI package
2
2
  export { createZapierCliSdk } from "./sdk";
3
+ export { buildCliCommandExecutedEvent } from "./telemetry/builders";
3
4
  // All CLI functionality is now schema-driven via generateCLICommands
@@ -1,5 +1,12 @@
1
1
  import type { Plugin } from "@zapier/zapier-sdk";
2
+ import type { EventEmissionContext } from "@zapier/zapier-sdk";
2
3
  import { LoginSchema, type LoginOptions } from "./schemas";
4
+ interface CliContext {
5
+ session_id?: string | null;
6
+ selected_api?: string | null;
7
+ app_id?: number | null;
8
+ app_version_id?: number | null;
9
+ }
3
10
  interface LoginPluginProvides {
4
11
  login: (options: LoginOptions) => Promise<void>;
5
12
  context: {
@@ -11,5 +18,5 @@ interface LoginPluginProvides {
11
18
  };
12
19
  };
13
20
  }
14
- export declare const loginPlugin: Plugin<{}, {}, LoginPluginProvides>;
21
+ export declare const loginPlugin: Plugin<{}, EventEmissionContext & CliContext, LoginPluginProvides>;
15
22
  export type { LoginPluginProvides };
@@ -2,6 +2,9 @@ import { createFunction } from "@zapier/zapier-sdk";
2
2
  import login from "../../utils/auth/login";
3
3
  import { getLoggedInUser } from "@zapier/zapier-sdk-cli-login";
4
4
  import { LoginSchema } from "./schemas";
5
+ import { buildCliCommandExecutedEvent } from "../../telemetry/builders";
6
+ import cliPackageJson from "../../../package.json";
7
+ const CLI_COMMAND_EXECUTED_EVENT_SUBJECT = "platform.sdk.CliCommandExecutedEvent";
5
8
  const loginWithSdk = createFunction(async function loginWithSdk(options) {
6
9
  const timeoutSeconds = options.timeout ? parseInt(options.timeout, 10) : 300;
7
10
  if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
@@ -13,14 +16,57 @@ const loginWithSdk = createFunction(async function loginWithSdk(options) {
13
16
  // Force immediate exit to prevent hanging (especially in development with tsx)
14
17
  setTimeout(() => process.exit(0), 100);
15
18
  }, LoginSchema);
16
- export const loginPlugin = () => ({
17
- login: loginWithSdk,
18
- context: {
19
- meta: {
20
- login: {
21
- categories: ["account"],
22
- inputSchema: LoginSchema,
19
+ export const loginPlugin = ({ context }) => {
20
+ // Wrap the login function to emit telemetry events
21
+ const loginWithTelemetry = async (options) => {
22
+ const startTime = Date.now();
23
+ let success = false;
24
+ let errorMessage = null;
25
+ try {
26
+ await loginWithSdk(options);
27
+ success = true;
28
+ }
29
+ catch (error) {
30
+ success = false;
31
+ errorMessage = error instanceof Error ? error.message : "Login failed";
32
+ throw error;
33
+ }
34
+ finally {
35
+ // Emit CLI command executed event if event emission is available
36
+ const event = buildCliCommandExecutedEvent({
37
+ data: {
38
+ cli_primary_command: "login",
39
+ success_flag: success,
40
+ execution_duration_ms: Date.now() - startTime,
41
+ exit_code: success ? 0 : 1,
42
+ error_message: errorMessage,
43
+ command_category: "authentication",
44
+ requires_auth: false,
45
+ cli_arguments: [
46
+ "login",
47
+ options.timeout ? `--timeout=${options.timeout}` : null,
48
+ ].filter(Boolean),
49
+ },
50
+ context: {
51
+ session_id: context.session_id,
52
+ selected_api: context.selected_api,
53
+ app_id: context.app_id,
54
+ app_version_id: context.app_version_id,
55
+ },
56
+ cliVersion: cliPackageJson.version,
57
+ });
58
+ context.eventEmission.emit(CLI_COMMAND_EXECUTED_EVENT_SUBJECT, event);
59
+ }
60
+ };
61
+ return {
62
+ login: loginWithTelemetry,
63
+ context: {
64
+ meta: {
65
+ login: {
66
+ categories: ["account"],
67
+ inputSchema: LoginSchema,
68
+ },
23
69
  },
24
70
  },
25
- },
26
- });
71
+ };
72
+ };
package/dist/src/sdk.d.ts CHANGED
@@ -1,6 +1,14 @@
1
1
  import { type ZapierSdk } from "@zapier/zapier-sdk";
2
2
  export interface ZapierCliSdkOptions {
3
3
  debug?: boolean;
4
+ eventEmission?: {
5
+ enabled?: boolean;
6
+ transport?: {
7
+ type: "http" | "console" | "noop";
8
+ endpoint?: string;
9
+ headers?: Record<string, string>;
10
+ };
11
+ };
4
12
  }
5
13
  /**
6
14
  * Create a Zapier SDK instance configured specifically for the CLI
package/dist/src/sdk.js CHANGED
@@ -8,6 +8,7 @@ export function createZapierCliSdk(options = {}) {
8
8
  // Create SDK instance without registry
9
9
  let sdk = createZapierSdkWithoutRegistry({
10
10
  debug: options.debug,
11
+ eventEmission: options.eventEmission,
11
12
  });
12
13
  // Add CLI-specific plugins before registry
13
14
  sdk = sdk.addPlugin(bundleCodePlugin);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * CLI-specific event builders
3
+ *
4
+ * Provides builder functions for CLI command telemetry that auto-populate
5
+ * common CLI fields and system information.
6
+ */
7
+ import type { CliCommandExecutedEvent } from "./events";
8
+ export interface CliEventContext {
9
+ customuser_id?: number | null;
10
+ account_id?: number | null;
11
+ identity_id?: number | null;
12
+ visitor_id?: string | null;
13
+ correlation_id?: string | null;
14
+ session_id?: string | null;
15
+ selected_api?: string | null;
16
+ app_id?: number | null;
17
+ app_version_id?: number | null;
18
+ }
19
+ export interface CliCommandExecutedEventData {
20
+ cli_primary_command: string;
21
+ success_flag: boolean;
22
+ execution_duration_ms?: number | null;
23
+ exit_code?: number | null;
24
+ error_message?: string | null;
25
+ command_category?: string | null;
26
+ requires_auth?: boolean | null;
27
+ cli_arguments?: (string | null)[] | null;
28
+ cli_version?: string | null;
29
+ made_network_requests?: boolean | null;
30
+ files_modified_count?: number | null;
31
+ files_created_count?: number | null;
32
+ files_processed_size_bytes?: number | null;
33
+ peak_memory_usage_bytes?: number | null;
34
+ cpu_time_ms?: number | null;
35
+ subprocess_count?: number | null;
36
+ package_manager?: string | null;
37
+ }
38
+ export declare function buildCliCommandExecutedEvent({ data, context, cliVersion, }: {
39
+ data: CliCommandExecutedEventData;
40
+ context?: CliEventContext;
41
+ cliVersion?: string;
42
+ }): CliCommandExecutedEvent;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * CLI-specific event builders
3
+ *
4
+ * Provides builder functions for CLI command telemetry that auto-populate
5
+ * common CLI fields and system information.
6
+ */
7
+ import { generateEventId, getCurrentTimestamp, getReleaseId, getOsInfo, getPlatformVersions, isCi, getCiPlatform, } from "@zapier/zapier-sdk";
8
+ import cliPackageJson from "../../package.json";
9
+ // Create base event for CLI events
10
+ function createCliBaseEvent(context = {}) {
11
+ return {
12
+ event_id: generateEventId(),
13
+ timestamp_ms: getCurrentTimestamp(),
14
+ release_id: getReleaseId(),
15
+ customuser_id: context.customuser_id ?? null,
16
+ account_id: context.account_id ?? null,
17
+ identity_id: context.identity_id ?? null,
18
+ visitor_id: context.visitor_id ?? null,
19
+ correlation_id: context.correlation_id ?? null,
20
+ };
21
+ }
22
+ export function buildCliCommandExecutedEvent({ data, context = {}, cliVersion = cliPackageJson.version, }) {
23
+ const osInfo = getOsInfo();
24
+ const platformVersions = getPlatformVersions();
25
+ return {
26
+ ...createCliBaseEvent(context),
27
+ system_name: "zapier-sdk-cli",
28
+ session_id: context.session_id ?? null,
29
+ cli_version: data.cli_version ?? cliVersion,
30
+ cli_arguments: data.cli_arguments ?? null,
31
+ cli_primary_command: data.cli_primary_command,
32
+ os_platform: osInfo.platform,
33
+ os_release: osInfo.release,
34
+ os_architecture: osInfo.architecture,
35
+ platform_versions: platformVersions,
36
+ selected_api: context.selected_api ?? null,
37
+ app_id: context.app_id ?? null,
38
+ app_version_id: context.app_version_id ?? null,
39
+ execution_duration_ms: data.execution_duration_ms ?? null,
40
+ success_flag: data.success_flag,
41
+ exit_code: data.exit_code ?? (data.success_flag ? 0 : 1),
42
+ error_message: data.error_message ?? null,
43
+ command_category: data.command_category ?? null,
44
+ requires_auth: data.requires_auth ?? null,
45
+ is_ci_environment: isCi(),
46
+ ci_platform: getCiPlatform(),
47
+ package_manager: data.package_manager ?? "pnpm", // Default based on project setup
48
+ made_network_requests: data.made_network_requests ?? null,
49
+ files_modified_count: data.files_modified_count ?? null,
50
+ files_created_count: data.files_created_count ?? null,
51
+ files_processed_size_bytes: data.files_processed_size_bytes ?? null,
52
+ peak_memory_usage_bytes: data.peak_memory_usage_bytes ?? null,
53
+ cpu_time_ms: data.cpu_time_ms ?? null,
54
+ subprocess_count: data.subprocess_count ?? null,
55
+ };
56
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * CLI-specific telemetry event definitions
3
+ */
4
+ import type { BaseEvent } from "@zapier/zapier-sdk";
5
+ /**
6
+ * Event emitted when a CLI command is executed
7
+ */
8
+ export interface CliCommandExecutedEvent extends BaseEvent {
9
+ system_name: string;
10
+ session_id?: string | null;
11
+ cli_version?: string | null;
12
+ cli_arguments?: (string | null)[] | null;
13
+ cli_primary_command?: string | null;
14
+ os_platform?: string | null;
15
+ os_release?: string | null;
16
+ os_architecture?: string | null;
17
+ platform_versions?: Record<string, string | null> | null;
18
+ selected_api?: string | null;
19
+ app_id?: number | null;
20
+ app_version_id?: number | null;
21
+ execution_duration_ms?: number | null;
22
+ success_flag: boolean;
23
+ exit_code?: number | null;
24
+ error_message?: string | null;
25
+ command_category?: string | null;
26
+ requires_auth?: boolean | null;
27
+ is_ci_environment?: boolean | null;
28
+ ci_platform?: string | null;
29
+ package_manager?: string | null;
30
+ made_network_requests?: boolean | null;
31
+ files_modified_count?: number | null;
32
+ files_created_count?: number | null;
33
+ files_processed_size_bytes?: number | null;
34
+ peak_memory_usage_bytes?: number | null;
35
+ cpu_time_ms?: number | null;
36
+ subprocess_count?: number | null;
37
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * CLI-specific telemetry event definitions
3
+ */
4
+ export {};
@@ -46,15 +46,21 @@ export function formatItemsFromSchema(functionInfo, items, startingNumber = 0) {
46
46
  function formatSingleItem(formatted, itemNumber) {
47
47
  // Build the main title line with optional subtitle
48
48
  let titleLine = `${chalk.gray(`${itemNumber + 1}.`)} ${chalk.cyan(formatted.title)}`;
49
- // Generate subtitle from id or key
50
- if (formatted.id) {
51
- titleLine += ` ${chalk.gray(`(ID: ${formatted.id})`)}`;
52
- }
53
- else if (formatted.keys) {
54
- titleLine += ` ${chalk.gray(`(${formatted.keys.join(", ")})`)}`;
49
+ // Generate subtitle - always show key if available, then ID if available
50
+ const subtitleParts = [];
51
+ if (formatted.keys) {
52
+ subtitleParts.push(...formatted.keys);
55
53
  }
56
54
  else if (formatted.key) {
57
- titleLine += ` ${chalk.gray(`(${formatted.key})`)}`;
55
+ subtitleParts.push(formatted.key);
56
+ }
57
+ if (formatted.id) {
58
+ subtitleParts.push(formatted.id);
59
+ }
60
+ // Remove duplicates while preserving order
61
+ const uniqueParts = [...new Set(subtitleParts)];
62
+ if (uniqueParts.length > 0) {
63
+ titleLine += ` ${chalk.gray(`(${uniqueParts.join(", ")})`)}`;
58
64
  }
59
65
  console.log(titleLine);
60
66
  // Show description if available