@embeddables/cli 0.7.8 → 0.7.10

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/cli.js CHANGED
@@ -109,13 +109,17 @@ program
109
109
  .description('Pull an embeddable from the cloud')
110
110
  .option('-i, --id <id>', 'Embeddable ID to pull (interactive selection if not provided)')
111
111
  .option('-o, --out <path>', 'Output json path')
112
+ .option('--version <version>', 'Version to pull (number or "latest"); if omitted, choose from 100 most recent')
112
113
  .option('-b, --branch <branch_id>', 'Embeddable branch ID')
113
114
  .option('-f, --fix', 'Fix by removing components missing required props (warn instead of error)')
114
115
  .option('-p, --preserve', 'Preserve component order in config for forward compile')
115
116
  .action(async (opts) => {
116
117
  await requireLogin('pull');
117
118
  await setSentryContextForCommand();
118
- await runPull(opts);
119
+ await runPull({
120
+ ...opts,
121
+ version: opts.version,
122
+ });
119
123
  });
120
124
  program
121
125
  .command('save')
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAwHA,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,iBAyOxE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAwHA,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,iBAgPxE"}
@@ -194,6 +194,12 @@ export async function runInit(opts) {
194
194
  else {
195
195
  stdout.print(pc.gray(' ✓ embeddables/ directory exists'));
196
196
  }
197
+ // Remove old .claudefiles folder (replaced by .cursor/ and .claude/)
198
+ const claudefilesDir = path.join(cwd, '.claudefiles');
199
+ if (fs.existsSync(claudefilesDir)) {
200
+ fs.rmSync(claudefilesDir, { recursive: true });
201
+ stdout.print(pc.green(' ✓ Removed old .claudefiles/'));
202
+ }
197
203
  // Inject .cursor/ and .claude/ from source .prompts/embeddables-cli.md (or copy if pre-built dirs exist)
198
204
  const packageRoot = path.resolve(__dirname, '..', '..');
199
205
  const promptsSource = path.join(packageRoot, '.prompts', 'embeddables-cli.md');
@@ -3,6 +3,8 @@ export type RunPullOptions = {
3
3
  out?: string;
4
4
  branch?: string;
5
5
  branchName?: string;
6
+ /** Version to pull (number or "latest"). If omitted and embeddable is known, prompt from 100 most recent. */
7
+ version?: string | number;
6
8
  /** When true, pull main and clear saved branch (e.g. when user explicitly selects "main" in branch switcher). */
7
9
  useMain?: boolean;
8
10
  fix?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAuHA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iHAAiH;IACjH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,iBAgSjD"}
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AA6HA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6GAA6G;IAC7G,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,iHAAiH;IACjH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,iBAgTjD"}
@@ -5,7 +5,7 @@ import prompts from 'prompts';
5
5
  import { reverseCompile } from '../compiler/reverse.js';
6
6
  import { getAccessToken, isLoggedIn } from '../auth/index.js';
7
7
  import { getProjectId, writeProjectConfig } from '../config/index.js';
8
- import { promptForProject, promptForEmbeddable, fetchEmbeddableMetadata } from '../prompts/index.js';
8
+ import { promptForProject, promptForEmbeddable, fetchEmbeddableMetadata, fetchRecentVersions, promptForVersion, } from '../prompts/index.js';
9
9
  import { captureException, createLogger, exit } from '../logger.js';
10
10
  import { getSentryContextFromEmbeddableConfig, getSentryContextFromProjectConfig, setSentryContext, } from '../sentry-context.js';
11
11
  import * as stdout from '../stdout.js';
@@ -156,6 +156,7 @@ export async function runPull(opts) {
156
156
  stdout.print(pc.cyan('Fetching embeddables from project...'));
157
157
  const selected = await promptForEmbeddable(projectId, {
158
158
  message: 'Select an embeddable to pull:',
159
+ prioritizeLocal: true,
159
160
  });
160
161
  if (!selected) {
161
162
  logger.error('embeddable selection failed');
@@ -175,10 +176,6 @@ export async function runPull(opts) {
175
176
  const currentFromConfig = opts.useMain || opts.branch != null ? null : getCurrentBranchFromConfig(embeddableId);
176
177
  const effectiveBranch = opts.useMain ? undefined : (opts.branch ?? currentFromConfig?.branchId);
177
178
  const effectiveBranchName = opts.useMain ? undefined : (opts.branchName ?? currentFromConfig?.branchName);
178
- let url = `https://engine.embeddables.com/${embeddableId}?version=latest`;
179
- if (effectiveBranch) {
180
- url += `&embeddable_branch=${effectiveBranch}`;
181
- }
182
179
  const outPath = opts.out || path.join('embeddables', embeddableId, '.generated', 'embeddable.json');
183
180
  const branchLabel = effectiveBranch
184
181
  ? effectiveBranchName
@@ -188,6 +185,24 @@ export async function runPull(opts) {
188
185
  if (effectiveBranch) {
189
186
  setSentryContext({ branch: { id: effectiveBranch, name: effectiveBranchName ?? null } });
190
187
  }
188
+ // Resolve version: explicit --version, or interactive choice from 100 most recent
189
+ let versionParam;
190
+ if (opts.version !== undefined && opts.version !== '') {
191
+ const v = String(opts.version).toLowerCase();
192
+ versionParam = v === 'latest' ? 'latest' : v;
193
+ }
194
+ else {
195
+ stdout.print(pc.cyan('Fetching recent versions...'));
196
+ const recentVersions = await fetchRecentVersions(embeddableId, effectiveBranch ?? null);
197
+ const selected = await promptForVersion(recentVersions, {
198
+ message: 'Select a version to pull:',
199
+ });
200
+ versionParam = selected === null ? 'latest' : String(selected);
201
+ stdout.print('');
202
+ }
203
+ let url = `https://engine.embeddables.com/${embeddableId}?version=${versionParam}`;
204
+ if (effectiveBranch)
205
+ url += `&embeddable_branch=${effectiveBranch}`;
191
206
  logger.info('pull started');
192
207
  stdout.print(pc.cyan(`Pulling branch: ${pc.bold(branchLabel)}`));
193
208
  stdout.print(`Fetching embeddable from ${url}...`);
@@ -14,6 +14,8 @@ export interface LocalEmbeddable {
14
14
  export interface PromptForEmbeddableOptions {
15
15
  /** Custom message for the prompt */
16
16
  message?: string;
17
+ /** When true, sort so embeddables already present in embeddables/ appear first */
18
+ prioritizeLocal?: boolean;
17
19
  }
18
20
  /**
19
21
  * Fetch all embeddables for a project from Supabase
@@ -1 +1 @@
1
- {"version":3,"file":"embeddables.d.ts","sourceRoot":"","sources":["../../src/prompts/embeddables.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA0B1F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA2BpC;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuCxB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAqC3E;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuCxB"}
1
+ {"version":3,"file":"embeddables.d.ts","sourceRoot":"","sources":["../../src/prompts/embeddables.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kFAAkF;IAClF,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA0B1F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA2BpC;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4ExB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAqC3E;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuCxB"}
@@ -67,29 +67,64 @@ export async function fetchEmbeddableMetadata(embeddableId) {
67
67
  * Returns null if no embeddables found or user cancels
68
68
  */
69
69
  export async function promptForEmbeddable(projectId, options = {}) {
70
- const { message = 'Select an embeddable:' } = options;
71
- const embeddables = await fetchProjectEmbeddables(projectId);
70
+ const { message = 'Select an embeddable:', prioritizeLocal = false } = options;
71
+ let embeddables = await fetchProjectEmbeddables(projectId);
72
+ const SEPARATOR_VALUE = '__separator__';
73
+ let inRepo = [];
74
+ let notInRepo = [];
75
+ if (prioritizeLocal && embeddables.length > 0) {
76
+ const local = await discoverLocalEmbeddables();
77
+ const localIds = new Set(local.map((e) => e.id));
78
+ inRepo = embeddables.filter((e) => localIds.has(e.id));
79
+ notInRepo = embeddables.filter((e) => !localIds.has(e.id));
80
+ embeddables = [...inRepo, ...notInRepo];
81
+ }
72
82
  if (embeddables.length === 0) {
73
83
  stdout.print(pc.yellow('No embeddables found in this project.'));
74
84
  return null;
75
85
  }
86
+ const hasTwoSections = prioritizeLocal && inRepo.length > 0 && notInRepo.length > 0;
87
+ const separatorChoice = {
88
+ title: pc.dim('── Other embeddables in project ──'),
89
+ value: SEPARATOR_VALUE,
90
+ disabled: true,
91
+ };
76
92
  const choices = embeddables.map((e) => ({
77
93
  title: e.title || e.id,
78
94
  description: e.id,
79
95
  value: e.id,
80
96
  }));
97
+ if (hasTwoSections) {
98
+ choices.splice(inRepo.length, 0, separatorChoice);
99
+ }
81
100
  const response = await prompts({
82
101
  type: 'autocomplete',
83
102
  name: 'id',
84
103
  message,
85
104
  choices,
86
- suggest: (input, choices) => Promise.resolve(choices.filter((c) => (c.title?.toLowerCase().includes(input.toLowerCase()) ?? false) ||
87
- String(c.value).toLowerCase().includes(input.toLowerCase()))),
105
+ suggest: (input, list) => {
106
+ const realChoices = list.filter((c) => c.value !== SEPARATOR_VALUE);
107
+ const filtered = input
108
+ ? realChoices.filter((c) => (c.title?.toLowerCase().includes(input.toLowerCase()) ?? false) ||
109
+ String(c.value).toLowerCase().includes(input.toLowerCase()))
110
+ : realChoices;
111
+ if (hasTwoSections && !input) {
112
+ return Promise.resolve([
113
+ ...filtered.slice(0, inRepo.length),
114
+ separatorChoice,
115
+ ...filtered.slice(inRepo.length),
116
+ ]);
117
+ }
118
+ return Promise.resolve(filtered);
119
+ },
88
120
  }, {
89
121
  onCancel: () => {
90
122
  process.exit(0);
91
123
  },
92
124
  });
125
+ if (response.id === SEPARATOR_VALUE) {
126
+ return promptForEmbeddable(projectId, options);
127
+ }
93
128
  return response.id || null;
94
129
  }
95
130
  /**
@@ -4,6 +4,8 @@ export { fetchProjectEmbeddables, fetchEmbeddableMetadata, promptForEmbeddable,
4
4
  export type { EmbeddableInfo, EmbeddableMetadata, LocalEmbeddable, PromptForEmbeddableOptions, } from './embeddables.js';
5
5
  export { fetchBranches, promptForBranch } from './branches.js';
6
6
  export type { BranchInfo } from './branches.js';
7
+ export { fetchRecentVersions, promptForVersion } from './versions.js';
8
+ export type { VersionInfo } from './versions.js';
7
9
  export { fetchProjectExperiments, promptForExperiment, } from './experiments.js';
8
10
  export type { ExperimentInfo, PromptForExperimentOptions } from './experiments.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEzE,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,0BAA0B,GAC3B,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EACL,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEzE,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,0BAA0B,GAC3B,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,EACL,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA"}
@@ -2,4 +2,5 @@
2
2
  export { fetchProjects, promptForProject } from './projects.js';
3
3
  export { fetchProjectEmbeddables, fetchEmbeddableMetadata, promptForEmbeddable, discoverLocalEmbeddables, promptForLocalEmbeddable, } from './embeddables.js';
4
4
  export { fetchBranches, promptForBranch } from './branches.js';
5
+ export { fetchRecentVersions, promptForVersion } from './versions.js';
5
6
  export { fetchProjectExperiments, promptForExperiment, } from './experiments.js';
@@ -0,0 +1,18 @@
1
+ export interface VersionInfo {
2
+ version_number: number;
3
+ created_at: string | null;
4
+ }
5
+ /**
6
+ * Fetch up to 100 most recent version numbers for an embeddable (flow).
7
+ * Uses flow_versions for the given flow_id and branch (null = main).
8
+ * Returns unique version numbers in descending order.
9
+ */
10
+ export declare function fetchRecentVersions(flowId: string, branchId: string | null): Promise<VersionInfo[]>;
11
+ /**
12
+ * Prompt the user to select a version from the list.
13
+ * Returns the selected version number, or null for "Latest".
14
+ */
15
+ export declare function promptForVersion(versions: VersionInfo[], options?: {
16
+ message?: string;
17
+ }): Promise<number | null>;
18
+ //# sourceMappingURL=versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/prompts/versions.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAID;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CA8CxB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkDxB"}
@@ -0,0 +1,99 @@
1
+ import pc from 'picocolors';
2
+ import prompts from 'prompts';
3
+ import { getAuthenticatedSupabaseClient } from '../auth/index.js';
4
+ import * as stdout from '../stdout.js';
5
+ import { formatDate } from '../helpers/dates.js';
6
+ const RECENT_VERSIONS_LIMIT = 100;
7
+ /**
8
+ * Fetch up to 100 most recent version numbers for an embeddable (flow).
9
+ * Uses flow_versions for the given flow_id and branch (null = main).
10
+ * Returns unique version numbers in descending order.
11
+ */
12
+ export async function fetchRecentVersions(flowId, branchId) {
13
+ const supabase = await getAuthenticatedSupabaseClient();
14
+ if (!supabase) {
15
+ return [];
16
+ }
17
+ try {
18
+ let query = supabase
19
+ .from('flow_versions')
20
+ .select('version_number, created_at')
21
+ .eq('flow_id', flowId)
22
+ .order('version_number', { ascending: false })
23
+ .limit(RECENT_VERSIONS_LIMIT * 2); // fetch extra then dedupe
24
+ if (branchId === null) {
25
+ query = query.is('branch_id', null);
26
+ }
27
+ else {
28
+ query = query.eq('branch_id', branchId);
29
+ }
30
+ const { data, error } = await query;
31
+ if (error) {
32
+ stdout.warn(pc.yellow(`Could not fetch versions: ${error.message}`));
33
+ return [];
34
+ }
35
+ // Dedupe by version_number, preserving order (most recent first)
36
+ const seen = new Set();
37
+ const versions = [];
38
+ for (const row of data || []) {
39
+ const v = row.version_number;
40
+ if (typeof v !== 'number' || seen.has(v))
41
+ continue;
42
+ seen.add(v);
43
+ versions.push({
44
+ version_number: v,
45
+ created_at: row.created_at ?? null,
46
+ });
47
+ if (versions.length >= RECENT_VERSIONS_LIMIT)
48
+ break;
49
+ }
50
+ return versions;
51
+ }
52
+ catch (err) {
53
+ stdout.warn(pc.yellow(`Could not fetch versions: ${err}`));
54
+ return [];
55
+ }
56
+ }
57
+ /**
58
+ * Prompt the user to select a version from the list.
59
+ * Returns the selected version number, or null for "Latest".
60
+ */
61
+ export async function promptForVersion(versions, options = {}) {
62
+ const choices = [];
63
+ if (versions.length === 0) {
64
+ choices.push({
65
+ title: pc.bold('Latest'),
66
+ value: 'latest',
67
+ description: 'Current published version',
68
+ });
69
+ }
70
+ else {
71
+ for (let i = 0; i < versions.length; i++) {
72
+ const v = versions[i];
73
+ const date = v.created_at ? formatDate(v.created_at) : '';
74
+ const isLatest = i === 0;
75
+ choices.push({
76
+ title: isLatest ? `Version ${v.version_number} (Latest)` : `Version ${v.version_number}`,
77
+ value: isLatest ? 'latest' : v.version_number,
78
+ description: date || undefined,
79
+ });
80
+ }
81
+ }
82
+ const response = await prompts({
83
+ type: 'autocomplete',
84
+ name: 'version',
85
+ message: options.message ?? 'Select a version to pull:',
86
+ choices,
87
+ suggest: (input, choicesList) => Promise.resolve(choicesList.filter((c) => c.value === 'latest' ||
88
+ String(c.value).includes(input) ||
89
+ (typeof c.title === 'string' && c.title.toLowerCase().includes(input.toLowerCase())))),
90
+ }, {
91
+ onCancel: () => {
92
+ process.exit(0);
93
+ },
94
+ });
95
+ if (response.version === 'latest' || response.version === undefined) {
96
+ return null;
97
+ }
98
+ return typeof response.version === 'number' ? response.version : null;
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddables/cli",
3
- "version": "0.7.8",
3
+ "version": "0.7.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "embeddables": "./bin/embeddables.mjs"