@primeuicom/mcp 0.1.25 → 0.1.26

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/service.js CHANGED
@@ -3,14 +3,212 @@
3
3
  // src/service.ts
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
- // src/runtime.ts
6
+ // src/cli.ts
7
+ import path2 from "path";
8
+
9
+ // src/package-metadata.ts
10
+ import { readFileSync } from "fs";
11
+ import path from "path";
12
+ import { fileURLToPath } from "url";
13
+ import { z } from "zod";
14
+ var packageMetadataSchema = z.object({
15
+ name: z.string().trim().min(1),
16
+ version: z.string().trim().min(1),
17
+ description: z.string().trim().min(1)
18
+ });
19
+ function loadPackageMetadata() {
20
+ const currentFilePath = fileURLToPath(import.meta.url);
21
+ const currentDir = path.dirname(currentFilePath);
22
+ const packageJsonPath = path.resolve(currentDir, "../package.json");
23
+ let rawPackageJson = "";
24
+ try {
25
+ rawPackageJson = readFileSync(packageJsonPath, "utf-8");
26
+ } catch (error) {
27
+ throw new Error(
28
+ `[primeui-mcp] failed to read package.json at ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`
29
+ );
30
+ }
31
+ let parsedPackageJson;
32
+ try {
33
+ parsedPackageJson = JSON.parse(rawPackageJson);
34
+ } catch (error) {
35
+ throw new Error(
36
+ `[primeui-mcp] invalid package.json at ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`
37
+ );
38
+ }
39
+ const parsed = packageMetadataSchema.safeParse(parsedPackageJson);
40
+ if (!parsed.success) {
41
+ const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
42
+ throw new Error(
43
+ `[primeui-mcp] invalid package metadata in ${packageJsonPath}: ${details}`
44
+ );
45
+ }
46
+ return parsed.data;
47
+ }
48
+ var packageMetadata = loadPackageMetadata();
49
+
50
+ // src/cli.ts
51
+ function buildCliCommand(metadata) {
52
+ return `npx ${metadata.name}@latest`;
53
+ }
54
+ function formatUsageText(metadata = packageMetadata) {
55
+ const command = buildCliCommand(metadata);
56
+ return [
57
+ "Usage:",
58
+ ` ${command}`,
59
+ ` ${command} --help`,
60
+ ` ${command} --version`,
61
+ ` ${command} --health`,
62
+ ` ${command} --health /absolute/project/path`
63
+ ].join("\n");
64
+ }
65
+ function formatHelpText(metadata = packageMetadata) {
66
+ return [
67
+ "PrimeUI MCP",
68
+ "MCP stdio server for importing PrimeUI pages into your local project.",
69
+ "",
70
+ formatUsageText(metadata),
71
+ "",
72
+ "Options:",
73
+ " -h, --help Show this help message and exit.",
74
+ " -v, --version Show package version and exit.",
75
+ " --health Run runtime diagnostics without starting the MCP server.",
76
+ "",
77
+ "Behavior:",
78
+ " Running without arguments starts the PrimeUI MCP stdio server.",
79
+ " This mode is intended to be launched by an MCP-compatible client.",
80
+ " `--health` prints runtime diagnostics and exits.",
81
+ "",
82
+ "Configuration:",
83
+ " PRIMEUI_API_KEY PrimeUI API key. Falls back to .primeui/project.json.",
84
+ " PRIMEUI_API_BASE_URL Override PrimeUI API base URL for local development.",
85
+ " PRIMEUI_PROJECT_ROOT Override project root used to resolve .primeui/project.json.",
86
+ "",
87
+ "Project config:",
88
+ " .primeui/project.json must include projectId, apiKey, and targetProjectPath."
89
+ ].join("\n");
90
+ }
91
+ function formatInvalidHealthPathError(projectRoot, metadata = packageMetadata) {
92
+ return [
93
+ `Invalid --health project path: ${projectRoot}`,
94
+ "",
95
+ "The optional `--health` project path must be an absolute path.",
96
+ "",
97
+ formatUsageText(metadata)
98
+ ].join("\n");
99
+ }
100
+ function formatUnknownArgError(arg, metadata = packageMetadata) {
101
+ return [`Unknown argument: ${arg}`, "", formatUsageText(metadata)].join("\n");
102
+ }
103
+ function formatInvalidArgsError(args, metadata = packageMetadata) {
104
+ return [
105
+ `Invalid arguments: ${args.join(" ")}`,
106
+ "",
107
+ formatUsageText(metadata)
108
+ ].join("\n");
109
+ }
110
+ function parseCliArgs(argv) {
111
+ if (argv.length === 0) {
112
+ return {
113
+ kind: "start-server"
114
+ };
115
+ }
116
+ const [arg, maybeProjectRoot] = argv;
117
+ if (arg === "--health") {
118
+ if (argv.length === 1) {
119
+ return {
120
+ kind: "run-health-check"
121
+ };
122
+ }
123
+ if (argv.length !== 2) {
124
+ return {
125
+ kind: "error",
126
+ reason: "invalid-arguments",
127
+ args: argv
128
+ };
129
+ }
130
+ if (maybeProjectRoot === "--help" || maybeProjectRoot === "-h" || maybeProjectRoot === "--version" || maybeProjectRoot === "-v") {
131
+ return {
132
+ kind: "error",
133
+ reason: "invalid-arguments",
134
+ args: argv
135
+ };
136
+ }
137
+ if (!path2.isAbsolute(maybeProjectRoot ?? "")) {
138
+ return {
139
+ kind: "error",
140
+ reason: "invalid-health-path",
141
+ args: argv
142
+ };
143
+ }
144
+ return {
145
+ kind: "run-health-check",
146
+ projectRoot: maybeProjectRoot
147
+ };
148
+ }
149
+ if (argv.length !== 1) {
150
+ return {
151
+ kind: "error",
152
+ reason: "invalid-arguments",
153
+ args: argv
154
+ };
155
+ }
156
+ if (arg === "--help" || arg === "-h") {
157
+ return {
158
+ kind: "print-help"
159
+ };
160
+ }
161
+ if (arg === "--version" || arg === "-v") {
162
+ return {
163
+ kind: "print-version"
164
+ };
165
+ }
166
+ return {
167
+ kind: "error",
168
+ reason: "unknown-argument",
169
+ args: argv
170
+ };
171
+ }
172
+ function writeLine(stream, text) {
173
+ stream.write(`${text}
174
+ `);
175
+ }
176
+ async function runCli(argv, dependencies) {
177
+ const metadata = dependencies.metadata ?? packageMetadata;
178
+ const parsed = parseCliArgs(argv);
179
+ switch (parsed.kind) {
180
+ case "start-server":
181
+ await dependencies.startServer();
182
+ return 0;
183
+ case "run-health-check":
184
+ return dependencies.runHealthCheck({
185
+ projectRoot: parsed.projectRoot
186
+ });
187
+ case "print-help":
188
+ writeLine(dependencies.stdout, formatHelpText(metadata));
189
+ return 0;
190
+ case "print-version":
191
+ writeLine(dependencies.stdout, metadata.version);
192
+ return 0;
193
+ case "error":
194
+ writeLine(
195
+ dependencies.stderr,
196
+ parsed.reason === "unknown-argument" ? formatUnknownArgError(parsed.args[0] ?? "", metadata) : parsed.reason === "invalid-health-path" ? formatInvalidHealthPathError(parsed.args[1] ?? "", metadata) : formatInvalidArgsError(parsed.args, metadata)
197
+ );
198
+ return 1;
199
+ }
200
+ }
201
+
202
+ // src/health/run-health-check.ts
203
+ import { stat as stat3 } from "fs/promises";
7
204
  import path7 from "path";
8
205
 
9
206
  // src/lib/project-link-config.ts
10
207
  import { readFile, stat } from "fs/promises";
11
- import path from "path";
12
- import { z } from "zod";
13
- var PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH = ".primeui/project.json";
208
+ import path3 from "path";
209
+ import { z as z2 } from "zod";
210
+ var PRIMEUI_PROJECT_DIR_NAME = ".primeui";
211
+ var PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH = `${PRIMEUI_PROJECT_DIR_NAME}/project.json`;
14
212
  var PrimeUiProjectConfigError = class extends Error {
15
213
  code;
16
214
  hint;
@@ -26,10 +224,10 @@ var PrimeUiProjectConfigError = class extends Error {
26
224
  function buildProjectRootHint() {
27
225
  return "Likely MCP project-path bug: when Codex VSCode runs MCP from global config, server can start in HOME without project context. Fix: set tool input parameter `projectRoot` to the ABSOLUTE path of the current project root (for example: /path/to/current/project).";
28
226
  }
29
- var primeUiProjectConfigSchema = z.object({
30
- projectId: z.string().trim().min(1),
31
- apiKey: z.string().trim().min(1),
32
- targetProjectPath: z.string().trim().regex(/^\.\/.*$/)
227
+ var primeUiProjectConfigSchema = z2.object({
228
+ projectId: z2.string().trim().min(1),
229
+ apiKey: z2.string().trim().min(1),
230
+ targetProjectPath: z2.string().trim().regex(/^\.\/.*$/)
33
231
  }).passthrough();
34
232
  async function fileExists(filePath) {
35
233
  try {
@@ -40,12 +238,12 @@ async function fileExists(filePath) {
40
238
  }
41
239
  }
42
240
  function ancestors(startPath) {
43
- const resolvedStartPath = path.resolve(startPath);
241
+ const resolvedStartPath = path3.resolve(startPath);
44
242
  const directories = [];
45
243
  let currentPath = resolvedStartPath;
46
244
  while (true) {
47
245
  directories.push(currentPath);
48
- const parentPath = path.dirname(currentPath);
246
+ const parentPath = path3.dirname(currentPath);
49
247
  if (parentPath === currentPath) {
50
248
  break;
51
249
  }
@@ -55,7 +253,7 @@ function ancestors(startPath) {
55
253
  }
56
254
  async function findPrimeUiProjectConfigPath(startPath) {
57
255
  for (const currentPath of ancestors(startPath)) {
58
- const candidatePath = path.join(
256
+ const candidatePath = path3.join(
59
257
  currentPath,
60
258
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
61
259
  );
@@ -106,6 +304,37 @@ async function readPrimeUiProjectConfig(projectConfigPath) {
106
304
  }
107
305
  return parsedConfig.data;
108
306
  }
307
+ function withConfigSourceDetails(error, context) {
308
+ if (!(error instanceof PrimeUiProjectConfigError)) {
309
+ throw error;
310
+ }
311
+ throw new PrimeUiProjectConfigError({
312
+ code: error.code,
313
+ message: error.message,
314
+ hint: error.hint,
315
+ details: {
316
+ ...error.details,
317
+ source: context.source,
318
+ projectRoot: context.projectRoot,
319
+ expectedConfigPath: context.projectConfigPath
320
+ }
321
+ });
322
+ }
323
+ async function loadResolvedPrimeUiProjectConfig(context) {
324
+ try {
325
+ const projectConfig = await readPrimeUiProjectConfig(
326
+ context.projectConfigPath
327
+ );
328
+ return {
329
+ projectRoot: context.projectRoot,
330
+ projectConfigPath: context.projectConfigPath,
331
+ projectConfig,
332
+ source: context.source
333
+ };
334
+ } catch (error) {
335
+ withConfigSourceDetails(error, context);
336
+ }
337
+ }
109
338
  function toUniqueResolvedDirs(directories) {
110
339
  const resolved = /* @__PURE__ */ new Set();
111
340
  for (const directory of directories) {
@@ -113,7 +342,7 @@ function toUniqueResolvedDirs(directories) {
113
342
  if (!trimmed) {
114
343
  continue;
115
344
  }
116
- resolved.add(path.resolve(trimmed));
345
+ resolved.add(path3.resolve(trimmed));
117
346
  }
118
347
  return [...resolved];
119
348
  }
@@ -123,8 +352,8 @@ function buildPrimeUiProjectSearchRoots(options) {
123
352
  async function resolvePrimeUiProjectConfig(options) {
124
353
  const toolProjectRoot = options.projectRootFromTool?.trim();
125
354
  if (toolProjectRoot) {
126
- const resolvedToolRoot = path.resolve(toolProjectRoot);
127
- const toolProjectConfigPath = path.join(
355
+ const resolvedToolRoot = path3.resolve(toolProjectRoot);
356
+ const toolProjectConfigPath = path3.join(
128
357
  resolvedToolRoot,
129
358
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
130
359
  );
@@ -141,35 +370,31 @@ async function resolvePrimeUiProjectConfig(options) {
141
370
  hint: buildProjectRootHint()
142
371
  });
143
372
  }
144
- const projectConfig2 = await readPrimeUiProjectConfig(toolProjectConfigPath);
145
- return {
373
+ return loadResolvedPrimeUiProjectConfig({
374
+ source: "tool",
146
375
  projectRoot: resolvedToolRoot,
147
- projectConfigPath: toolProjectConfigPath,
148
- projectConfig: projectConfig2
149
- };
376
+ projectConfigPath: toolProjectConfigPath
377
+ });
150
378
  }
151
379
  const stickyProjectRoot = options.projectRootFromSticky?.trim();
152
380
  if (stickyProjectRoot) {
153
- const resolvedStickyRoot = path.resolve(stickyProjectRoot);
154
- const stickyProjectConfigPath = path.join(
381
+ const resolvedStickyRoot = path3.resolve(stickyProjectRoot);
382
+ const stickyProjectConfigPath = path3.join(
155
383
  resolvedStickyRoot,
156
384
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
157
385
  );
158
386
  if (await fileExists(stickyProjectConfigPath)) {
159
- const projectConfig2 = await readPrimeUiProjectConfig(
160
- stickyProjectConfigPath
161
- );
162
- return {
387
+ return loadResolvedPrimeUiProjectConfig({
388
+ source: "sticky",
163
389
  projectRoot: resolvedStickyRoot,
164
- projectConfigPath: stickyProjectConfigPath,
165
- projectConfig: projectConfig2
166
- };
390
+ projectConfigPath: stickyProjectConfigPath
391
+ });
167
392
  }
168
393
  }
169
394
  const envProjectRoot = options.projectRootFromEnv?.trim();
170
395
  if (envProjectRoot) {
171
- const resolvedEnvRoot = path.resolve(envProjectRoot);
172
- const envProjectConfigPath = path.join(
396
+ const resolvedEnvRoot = path3.resolve(envProjectRoot);
397
+ const envProjectConfigPath = path3.join(
173
398
  resolvedEnvRoot,
174
399
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
175
400
  );
@@ -187,12 +412,11 @@ async function resolvePrimeUiProjectConfig(options) {
187
412
  hint: buildProjectRootHint()
188
413
  });
189
414
  }
190
- const projectConfig2 = await readPrimeUiProjectConfig(envProjectConfigPath);
191
- return {
415
+ return loadResolvedPrimeUiProjectConfig({
416
+ source: "env",
192
417
  projectRoot: resolvedEnvRoot,
193
- projectConfigPath: envProjectConfigPath,
194
- projectConfig: projectConfig2
195
- };
418
+ projectConfigPath: envProjectConfigPath
419
+ });
196
420
  }
197
421
  const searchRoots = buildPrimeUiProjectSearchRoots({
198
422
  cwd: options.cwd,
@@ -220,37 +444,52 @@ async function resolvePrimeUiProjectConfig(options) {
220
444
  hint: buildProjectRootHint()
221
445
  });
222
446
  }
223
- const projectRoot = path.dirname(path.dirname(projectConfigPath));
224
- const projectConfig = await readPrimeUiProjectConfig(projectConfigPath);
225
- return {
447
+ const projectRoot = path3.dirname(path3.dirname(projectConfigPath));
448
+ return loadResolvedPrimeUiProjectConfig({
449
+ source: "search",
226
450
  projectRoot,
227
- projectConfigPath,
228
- projectConfig
451
+ projectConfigPath
452
+ });
453
+ }
454
+ function resolvePrimeUiApiKeyDetails(options) {
455
+ const envApiKey = options.apiKeyFromEnv?.trim();
456
+ const configApiKey = options.projectConfig?.apiKey.trim();
457
+ const envProvided = Boolean(envApiKey);
458
+ const configProvided = Boolean(configApiKey);
459
+ return {
460
+ apiKey: envApiKey ?? configApiKey,
461
+ source: envProvided ? "env" : configProvided ? "config" : "missing",
462
+ envProvided,
463
+ configProvided,
464
+ valuesMatch: envProvided && configProvided ? envApiKey === configApiKey : void 0
229
465
  };
230
466
  }
467
+ function resolvePrimeUiTargetProjectRoot(projectRoot, projectConfig) {
468
+ return path3.resolve(projectRoot, projectConfig.targetProjectPath);
469
+ }
231
470
  async function resolvePrimeUiApiKey(options) {
232
- const envApiKey = options.apiKeyFromEnv?.trim();
233
- if (envApiKey) {
234
- return envApiKey;
235
- }
236
- const projectConfigApiKey = options.projectConfig.apiKey.trim();
237
- if (!projectConfigApiKey) {
238
- throw new PrimeUiProjectConfigError({
239
- code: "PROJECT_API_KEY_MISSING",
240
- message: "[primeui-mcp] PRIMEUI_API_KEY is missing in env and .primeui/project.json",
241
- hint: "Set PRIMEUI_API_KEY or ensure apiKey is present in .primeui/project.json."
242
- });
243
- }
244
- return projectConfigApiKey;
471
+ const resolved = resolvePrimeUiApiKeyDetails(options);
472
+ if (resolved.apiKey) {
473
+ return resolved.apiKey;
474
+ }
475
+ throw new PrimeUiProjectConfigError({
476
+ code: "PROJECT_API_KEY_MISSING",
477
+ message: "[primeui-mcp] PRIMEUI_API_KEY is missing in env and .primeui/project.json",
478
+ hint: "Set PRIMEUI_API_KEY or ensure apiKey is present in .primeui/project.json."
479
+ });
245
480
  }
246
481
 
247
- // src/services/project-sync-service.ts
482
+ // src/sources/api-provider.ts
483
+ import { createWriteStream } from "fs";
484
+ import { unlink } from "fs/promises";
248
485
  import path5 from "path";
249
- import { readFile as readFile4 } from "fs/promises";
486
+ import { Readable, Transform } from "stream";
487
+ import { pipeline } from "stream/promises";
488
+ import { z as z3 } from "zod";
250
489
 
251
490
  // src/lib/fs.ts
252
491
  import { mkdir, rm, writeFile } from "fs/promises";
253
- import path2 from "path";
492
+ import path4 from "path";
254
493
  import extractZipArchive from "extract-zip";
255
494
  async function ensureDir(dirPath) {
256
495
  await mkdir(dirPath, { recursive: true });
@@ -260,7 +499,7 @@ async function resetDir(dirPath) {
260
499
  await mkdir(dirPath, { recursive: true });
261
500
  }
262
501
  async function writeUtf8(filePath, content) {
263
- await ensureDir(path2.dirname(filePath));
502
+ await ensureDir(path4.dirname(filePath));
264
503
  await writeFile(filePath, content, "utf-8");
265
504
  }
266
505
  async function extractZip(zipPath, targetDir) {
@@ -273,14 +512,1010 @@ async function extractZip(zipPath, targetDir) {
273
512
  }
274
513
  }
275
514
 
515
+ // src/sources/api-provider.ts
516
+ var DEFAULT_API_BASE_URL = "https://app.primeui.com/";
517
+ var ZIP_CONTENT_TYPES = [
518
+ "application/zip",
519
+ "application/octet-stream",
520
+ "application/x-zip-compressed"
521
+ ];
522
+ var exportStatusSchema = z3.enum(["in_progress", "completed", "failed"]);
523
+ var projectPageObjectSchema = z3.object({
524
+ id: z3.string(),
525
+ title: z3.string(),
526
+ slug: z3.string(),
527
+ pageType: z3.string(),
528
+ isReadyToExport: z3.boolean(),
529
+ pagePath: z3.string(),
530
+ componentsPath: z3.string()
531
+ });
532
+ var projectPageSchema = projectPageObjectSchema;
533
+ var exportSummarySchema = z3.object({
534
+ total: z3.number(),
535
+ successful: z3.number(),
536
+ failed: z3.number()
537
+ });
538
+ var exportedComponentSchema = z3.object({
539
+ componentKey: z3.string(),
540
+ enabled: z3.boolean(),
541
+ files: z3.array(z3.string()),
542
+ message: z3.string()
543
+ });
544
+ var exportPageManifestSchema = z3.object({
545
+ success: z3.boolean(),
546
+ message: z3.string(),
547
+ files: z3.array(z3.string())
548
+ });
549
+ var exportPageSchema = z3.object({
550
+ id: z3.string(),
551
+ title: z3.string().optional(),
552
+ slug: z3.string(),
553
+ pageType: z3.string(),
554
+ isReadyToExport: z3.literal(true),
555
+ pagePath: z3.string(),
556
+ componentsPath: z3.string(),
557
+ manifest: exportPageManifestSchema
558
+ });
559
+ var projectInfoSchema = z3.object({
560
+ projectId: z3.string(),
561
+ projectName: z3.string(),
562
+ metadata: z3.record(z3.unknown()),
563
+ pages: z3.array(projectPageSchema)
564
+ });
565
+ var projectPageComponentSchema = z3.object({
566
+ blockId: z3.string(),
567
+ componentId: z3.string(),
568
+ componentGroup: z3.string(),
569
+ slot: z3.string().nullable(),
570
+ props: z3.record(z3.unknown()).nullable()
571
+ });
572
+ var projectPageDetailsSchema = z3.object({
573
+ page: projectPageObjectSchema.extend({
574
+ pageInstruction: z3.string().nullable()
575
+ }),
576
+ variant: z3.object({
577
+ id: z3.string(),
578
+ name: z3.string()
579
+ }).nullable(),
580
+ components: z3.array(projectPageComponentSchema).nullable()
581
+ });
582
+ var exportsResponseSchema = z3.object({
583
+ exports: z3.array(
584
+ z3.object({
585
+ id: z3.string(),
586
+ status: exportStatusSchema,
587
+ createdAt: z3.string().datetime({ offset: true })
588
+ })
589
+ )
590
+ });
591
+ var createExportResponseSchema = z3.object({
592
+ export: z3.object({
593
+ id: z3.string(),
594
+ status: exportStatusSchema,
595
+ createdAt: z3.string().datetime({ offset: true }),
596
+ expiresAt: z3.string().datetime({ offset: true }).nullable(),
597
+ summary: exportSummarySchema,
598
+ components: z3.array(exportedComponentSchema)
599
+ }),
600
+ pages: z3.array(exportPageSchema)
601
+ });
602
+ var PrimeUiApiContractError = class extends Error {
603
+ constructor(endpoint, details) {
604
+ super(`PrimeUI API contract mismatch for "${endpoint}": ${details}`);
605
+ this.name = "PrimeUiApiContractError";
606
+ }
607
+ };
608
+ var normalizePrimeUiApiRoot = (rawBaseUrl) => {
609
+ const normalizedBase = rawBaseUrl?.trim() || DEFAULT_API_BASE_URL;
610
+ const parsed = new URL(normalizedBase);
611
+ const normalizedPath = parsed.pathname.replace(/\/+$/, "");
612
+ if (normalizedPath.endsWith("/api/v1")) {
613
+ parsed.pathname = `${normalizedPath}/`;
614
+ return parsed.toString();
615
+ }
616
+ parsed.pathname = `${normalizedPath}/api/v1/`.replace(
617
+ "//api/v1/",
618
+ "/api/v1/"
619
+ );
620
+ return parsed.toString();
621
+ };
622
+ var buildPrimeUiApiUrl = (apiRoot, endpoint) => new URL(endpoint, apiRoot).toString();
623
+ var isJsonContentType = (contentType) => contentType.includes("application/json") || contentType.includes("+json");
624
+ var isZipContentType = (contentType) => ZIP_CONTENT_TYPES.some((allowedType) => contentType.includes(allowedType));
625
+ var looksLikeZipArchive = (buffer) => {
626
+ if (buffer.length < 4) {
627
+ return false;
628
+ }
629
+ if (buffer[0] !== 80 || buffer[1] !== 75) {
630
+ return false;
631
+ }
632
+ const signature = `${buffer[2]}:${buffer[3]}`;
633
+ return signature === "3:4" || signature === "5:6" || signature === "7:8";
634
+ };
635
+ var createZipSignatureGuard = (endpoint) => {
636
+ let signature = Buffer.alloc(0);
637
+ let validated = false;
638
+ return new Transform({
639
+ transform(chunk, _encoding, callback) {
640
+ const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
641
+ if (!validated) {
642
+ const requiredBytes = Math.max(0, 4 - signature.length);
643
+ if (requiredBytes > 0) {
644
+ signature = Buffer.concat([
645
+ signature,
646
+ chunkBuffer.subarray(0, requiredBytes)
647
+ ]);
648
+ }
649
+ if (signature.length >= 4) {
650
+ if (!looksLikeZipArchive(signature)) {
651
+ callback(
652
+ new PrimeUiApiContractError(
653
+ endpoint,
654
+ "response body is not a valid zip archive"
655
+ )
656
+ );
657
+ return;
658
+ }
659
+ validated = true;
660
+ }
661
+ }
662
+ callback(null, chunkBuffer);
663
+ },
664
+ flush(callback) {
665
+ if (!validated) {
666
+ callback(
667
+ new PrimeUiApiContractError(
668
+ endpoint,
669
+ "response body is not a valid zip archive"
670
+ )
671
+ );
672
+ return;
673
+ }
674
+ callback();
675
+ }
676
+ });
677
+ };
678
+ var ApiProjectDataProvider = class {
679
+ apiKey;
680
+ apiRoot;
681
+ constructor(options) {
682
+ this.apiKey = options.apiKey;
683
+ this.apiRoot = normalizePrimeUiApiRoot(options.baseUrl);
684
+ }
685
+ async getProjectInfo() {
686
+ return this.requestJson("project", projectInfoSchema);
687
+ }
688
+ async getProjectPageBySlug(slug) {
689
+ const encodedSlug = encodeURIComponent(slug);
690
+ return this.requestJson(
691
+ `project/page?slug=${encodedSlug}`,
692
+ projectPageDetailsSchema
693
+ );
694
+ }
695
+ async listExports() {
696
+ const response = await this.requestJson(
697
+ "project/exports",
698
+ exportsResponseSchema
699
+ );
700
+ return response.exports;
701
+ }
702
+ async createExport() {
703
+ return this.requestJson("project/exports", createExportResponseSchema, {
704
+ method: "POST"
705
+ });
706
+ }
707
+ /**
708
+ * Consumer-side runtime contract for GET /api/v1/project/exports/:exportId/download.
709
+ * Producer source of truth: apps/studio/src/app/api/v1/project/exports/[exportId]/download/route.ts
710
+ * Keep content-type and archive format checks synchronized with the producer response.
711
+ */
712
+ async downloadExportArchive(exportId, destinationPath) {
713
+ const endpoint = `project/exports/${encodeURIComponent(exportId)}/download`;
714
+ const apiKey = this.requireApiKey();
715
+ const response = await fetch(this.buildUrl(endpoint), {
716
+ method: "GET",
717
+ headers: {
718
+ Authorization: `Bearer ${apiKey}`
719
+ }
720
+ });
721
+ if (!response.ok) {
722
+ const details = await this.readError(response);
723
+ throw new Error(
724
+ `PrimeUI API request failed (${response.status}) while downloading export "${exportId}": ${details}`
725
+ );
726
+ }
727
+ const contentType = response.headers.get("content-type") ?? "";
728
+ if (!isZipContentType(contentType)) {
729
+ throw new PrimeUiApiContractError(
730
+ endpoint,
731
+ `expected zip content-type but got "${contentType || "unknown"}"`
732
+ );
733
+ }
734
+ if (!response.body) {
735
+ throw new PrimeUiApiContractError(endpoint, "response body is empty");
736
+ }
737
+ await ensureDir(path5.dirname(destinationPath));
738
+ const zipStream = Readable.fromWeb(
739
+ response.body
740
+ );
741
+ const signatureGuard = createZipSignatureGuard(endpoint);
742
+ const fileStream = createWriteStream(destinationPath);
743
+ try {
744
+ await pipeline(zipStream, signatureGuard, fileStream);
745
+ } catch (error) {
746
+ await unlink(destinationPath).catch(() => void 0);
747
+ throw error;
748
+ }
749
+ }
750
+ buildUrl(endpoint) {
751
+ return buildPrimeUiApiUrl(this.apiRoot, endpoint);
752
+ }
753
+ async requestJson(endpoint, schema, requestInit = {}) {
754
+ const apiKey = this.requireApiKey();
755
+ const method = requestInit.method ?? "GET";
756
+ const response = await fetch(this.buildUrl(endpoint), {
757
+ ...requestInit,
758
+ method,
759
+ headers: {
760
+ ...requestInit.headers ?? {},
761
+ Authorization: `Bearer ${apiKey}`
762
+ }
763
+ });
764
+ if (!response.ok) {
765
+ const details = await this.readError(response);
766
+ throw new Error(
767
+ `PrimeUI API request failed (${response.status}) for "${endpoint}": ${details}`
768
+ );
769
+ }
770
+ const contentType = response.headers.get("content-type") ?? "";
771
+ if (!isJsonContentType(contentType)) {
772
+ throw new PrimeUiApiContractError(
773
+ endpoint,
774
+ `expected JSON content-type but got "${contentType || "unknown"}"`
775
+ );
776
+ }
777
+ let payload;
778
+ try {
779
+ payload = await response.json();
780
+ } catch {
781
+ throw new PrimeUiApiContractError(
782
+ endpoint,
783
+ "response body is not valid JSON"
784
+ );
785
+ }
786
+ const parsed = schema.safeParse(payload);
787
+ if (!parsed.success) {
788
+ const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
789
+ throw new PrimeUiApiContractError(endpoint, details);
790
+ }
791
+ return parsed.data;
792
+ }
793
+ async readError(response) {
794
+ const contentType = response.headers.get("content-type") ?? "";
795
+ try {
796
+ if (isJsonContentType(contentType)) {
797
+ const body = await response.json();
798
+ if (body?.message && body?.code) {
799
+ return `${body.code}: ${body.message}`;
800
+ }
801
+ if (body?.message) {
802
+ return body.message;
803
+ }
804
+ } else {
805
+ const bodyText = await response.text();
806
+ if (bodyText.trim()) {
807
+ return bodyText.trim();
808
+ }
809
+ }
810
+ } catch {
811
+ }
812
+ return response.statusText || "Unknown error";
813
+ }
814
+ requireApiKey() {
815
+ const apiKey = this.apiKey?.trim();
816
+ if (!apiKey) {
817
+ throw new Error("PRIMEUI_API_KEY is required to call PrimeUI API tools.");
818
+ }
819
+ return apiKey;
820
+ }
821
+ };
822
+
823
+ // src/health/hints.ts
824
+ function addHint(target, seen, hint) {
825
+ if (!seen.has(hint)) {
826
+ seen.add(hint);
827
+ target.push(hint);
828
+ }
829
+ }
830
+ function buildHealthHints(options) {
831
+ const { report, connectApiKey } = options;
832
+ const hints = [];
833
+ const seen = /* @__PURE__ */ new Set();
834
+ const validNestedRoots = report.nestedSearch.candidates.filter((candidate) => candidate.configStatus === "valid").map((candidate) => candidate.projectRoot);
835
+ if (!report.runtimeContext.inputProjectRoot && report.projectResolution.configStatus === "unresolved" && report.projectResolution.errorSource === "search") {
836
+ addHint(
837
+ hints,
838
+ seen,
839
+ "Config was not found from the current working directory. Rerun `--health /absolute/project/path`, pass the same absolute path via MCP tool `projectRoot`, or set `PRIMEUI_PROJECT_ROOT` if your MCP client starts outside the project."
840
+ );
841
+ }
842
+ if (report.runtimeContext.inputProjectRoot && report.projectResolution.primeUiDirStatus === "missing") {
843
+ if (report.nestedSearch.candidates.length > 0) {
844
+ addHint(
845
+ hints,
846
+ seen,
847
+ validNestedRoots.length > 0 ? `No direct \`.primeui\` directory was found under the provided path. Rerun health with one of the exact nested project roots that passed config validation, for example: ${validNestedRoots.join(", ")}.` : "No direct `.primeui` directory was found under the provided path. Nested `.primeui` directories were found below it, so rerun health with the exact intended project root before using MCP tools."
848
+ );
849
+ if (validNestedRoots.length > 0) {
850
+ addHint(
851
+ hints,
852
+ seen,
853
+ `When calling MCP tools, pass \`projectRoot\` only for a candidate whose nested health check passes, for example: ${validNestedRoots[0]}.`
854
+ );
855
+ }
856
+ } else {
857
+ const connectCommand = `npx @primeuicom/cli ${connectApiKey ?? "<api-key>"} connect`;
858
+ addHint(
859
+ hints,
860
+ seen,
861
+ `No \`.primeui\` directory was found under the provided path. From the intended target folder, run \`${connectCommand}\` to connect the project to PrimeUI.`
862
+ );
863
+ if (!connectApiKey) {
864
+ addHint(
865
+ hints,
866
+ seen,
867
+ "If you do not already have a valid PrimeUI API key, get it from the appropriate PrimeUI project on `primeui.com`."
868
+ );
869
+ }
870
+ }
871
+ }
872
+ if (report.projectResolution.primeUiDirStatus === "found" && report.projectResolution.configStatus === "missing") {
873
+ addHint(
874
+ hints,
875
+ seen,
876
+ "`.primeui/project.json` is missing. It was likely corrupted or deleted; restore it by reconnecting the project or by making a fresh export via the PrimeUI CLI tool."
877
+ );
878
+ }
879
+ if (report.projectResolution.primeUiDirStatus === "found" && (report.projectResolution.configStatus === "invalid-json" || report.projectResolution.configStatus === "invalid-format" || report.projectResolution.configStatus === "read-failed")) {
880
+ addHint(
881
+ hints,
882
+ seen,
883
+ "`.primeui/project.json` looks corrupted or incomplete. Restore it by reconnecting the project or by making a fresh export via the PrimeUI CLI tool."
884
+ );
885
+ }
886
+ if (report.runtimeContext.envProjectRoot && report.sourceResolution.projectRoot.comparison === "different") {
887
+ addHint(
888
+ hints,
889
+ seen,
890
+ report.projectResolution.inputProjectRootValidated ? `\`PRIMEUI_PROJECT_ROOT\` points to a different location than the validated explicit input path. Fix or unset \`PRIMEUI_PROJECT_ROOT\`, and use the successful path via MCP tool \`projectRoot\`: ${report.runtimeContext.inputProjectRoot}.` : "`PRIMEUI_PROJECT_ROOT` points to a different location than the explicit input path. Correct the project root first, then rerun health before using MCP tool `projectRoot`."
891
+ );
892
+ }
893
+ if (report.sourceResolution.apiKey.effectiveSource === "missing" && !(report.projectResolution.primeUiDirStatus === "found" && report.projectResolution.configStatus !== "valid")) {
894
+ addHint(
895
+ hints,
896
+ seen,
897
+ "No usable API key was found. Set `PRIMEUI_API_KEY`, restore `apiKey` in `.primeui/project.json`, or get a valid key from the appropriate PrimeUI project on `primeui.com`."
898
+ );
899
+ }
900
+ if (report.apiCheck.status === "skipped" && report.apiCheck.errorKind === "configuration" && report.runtimeContext.envApiBaseUrl) {
901
+ addHint(
902
+ hints,
903
+ seen,
904
+ "Set `PRIMEUI_API_BASE_URL` to a valid absolute URL such as `http://localhost:3020`, or remove it to fall back to the standard production API."
905
+ );
906
+ }
907
+ if (report.apiCheck.status === "failed" && report.apiCheck.errorKind === "auth") {
908
+ if (report.sourceResolution.apiKey.envState === "set" && report.sourceResolution.apiKey.configState === "set") {
909
+ addHint(
910
+ hints,
911
+ seen,
912
+ "Both env and config API keys are present. Verify that `PRIMEUI_API_KEY` is not unintentionally overriding a valid config key."
913
+ );
914
+ }
915
+ if (report.apiCheck.nonStandardBaseUrl) {
916
+ addHint(
917
+ hints,
918
+ seen,
919
+ "Authentication failed while using a non-standard `PRIMEUI_API_BASE_URL`. If you did not intend to target a custom API host, remove that env var and retry."
920
+ );
921
+ }
922
+ addHint(
923
+ hints,
924
+ seen,
925
+ "The current API key was rejected by `project info`. Rotate or regenerate the key, then rerun health."
926
+ );
927
+ }
928
+ if (report.apiCheck.status === "failed" && report.apiCheck.errorKind === "network") {
929
+ addHint(
930
+ hints,
931
+ seen,
932
+ report.apiCheck.nonStandardBaseUrl ? "The `project info` request failed while using a non-standard `PRIMEUI_API_BASE_URL`. Verify that host is reachable, or remove the env var if you did not intend to target a custom API." : "The `project info` request could not reach the API successfully. Verify network reachability and retry."
933
+ );
934
+ }
935
+ return hints;
936
+ }
937
+
938
+ // src/health/nested-primeui-search.ts
939
+ import { readdir, stat as stat2 } from "fs/promises";
940
+ import path6 from "path";
941
+ var SCAN_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
942
+ ".git",
943
+ "node_modules",
944
+ ".next",
945
+ "dist",
946
+ "build",
947
+ "coverage"
948
+ ]);
949
+ async function isFile(targetPath) {
950
+ try {
951
+ const targetStat = await stat2(targetPath);
952
+ return targetStat.isFile();
953
+ } catch {
954
+ return false;
955
+ }
956
+ }
957
+ function mapConfigStatus(error) {
958
+ if (!(error instanceof PrimeUiProjectConfigError)) {
959
+ return "read-failed";
960
+ }
961
+ switch (error.code) {
962
+ case "PROJECT_CONFIG_INVALID_JSON":
963
+ return "invalid-json";
964
+ case "PROJECT_CONFIG_INVALID_FORMAT":
965
+ return "invalid-format";
966
+ case "PROJECT_CONFIG_READ_FAILED":
967
+ return "read-failed";
968
+ case "PROJECT_CONFIG_NOT_FOUND":
969
+ return "missing";
970
+ default:
971
+ return "read-failed";
972
+ }
973
+ }
974
+ async function inspectCandidate(projectRoot, primeUiPath) {
975
+ const configPath = path6.join(primeUiPath, "project.json");
976
+ if (!await isFile(configPath)) {
977
+ return {
978
+ projectRoot,
979
+ primeUiPath,
980
+ configPath,
981
+ hasProjectConfig: false,
982
+ configStatus: "missing"
983
+ };
984
+ }
985
+ try {
986
+ const projectConfig = await readPrimeUiProjectConfig(configPath);
987
+ return {
988
+ projectRoot,
989
+ primeUiPath,
990
+ configPath,
991
+ hasProjectConfig: true,
992
+ configStatus: "valid",
993
+ targetProjectPath: projectConfig.targetProjectPath,
994
+ targetProjectRoot: resolvePrimeUiTargetProjectRoot(
995
+ projectRoot,
996
+ projectConfig
997
+ )
998
+ };
999
+ } catch (error) {
1000
+ return {
1001
+ projectRoot,
1002
+ primeUiPath,
1003
+ configPath,
1004
+ hasProjectConfig: true,
1005
+ configStatus: mapConfigStatus(error)
1006
+ };
1007
+ }
1008
+ }
1009
+ async function findNestedPrimeUiCandidates(rootPath) {
1010
+ const queue = [path6.resolve(rootPath)];
1011
+ const seen = /* @__PURE__ */ new Set();
1012
+ const candidates = [];
1013
+ while (queue.length > 0) {
1014
+ const currentDir = queue.shift();
1015
+ if (!currentDir || seen.has(currentDir)) {
1016
+ continue;
1017
+ }
1018
+ seen.add(currentDir);
1019
+ let entries;
1020
+ try {
1021
+ entries = await readdir(currentDir, { withFileTypes: true });
1022
+ } catch {
1023
+ continue;
1024
+ }
1025
+ for (const entry of entries) {
1026
+ if (!entry.isDirectory()) {
1027
+ continue;
1028
+ }
1029
+ const entryPath = path6.join(currentDir, entry.name);
1030
+ if (entry.name === PRIMEUI_PROJECT_DIR_NAME) {
1031
+ candidates.push(await inspectCandidate(currentDir, entryPath));
1032
+ continue;
1033
+ }
1034
+ if (SCAN_EXCLUDED_DIRS.has(entry.name)) {
1035
+ continue;
1036
+ }
1037
+ queue.push(entryPath);
1038
+ }
1039
+ }
1040
+ return candidates.sort(
1041
+ (left, right) => left.primeUiPath.localeCompare(right.primeUiPath)
1042
+ );
1043
+ }
1044
+
1045
+ // src/health/report.ts
1046
+ function renderValue(value, fallback = "missing") {
1047
+ return value?.trim() ? value : fallback;
1048
+ }
1049
+ function renderProjectRootComparison(comparison) {
1050
+ switch (comparison) {
1051
+ case "same":
1052
+ return "same";
1053
+ case "different":
1054
+ return "different";
1055
+ case "input-only":
1056
+ return "only explicit input path is set";
1057
+ case "env-only":
1058
+ return "only PRIMEUI_PROJECT_ROOT is set";
1059
+ case "neither":
1060
+ return "neither explicit input path nor PRIMEUI_PROJECT_ROOT is set";
1061
+ }
1062
+ }
1063
+ function renderApiKeyComparison(comparison) {
1064
+ switch (comparison) {
1065
+ case "same":
1066
+ return "same";
1067
+ case "different":
1068
+ return "different";
1069
+ case "not-comparable":
1070
+ return "not comparable";
1071
+ }
1072
+ }
1073
+ function renderConfigStatus(status) {
1074
+ switch (status) {
1075
+ case "valid":
1076
+ return "valid";
1077
+ case "missing":
1078
+ return "missing";
1079
+ case "invalid-json":
1080
+ return "invalid JSON";
1081
+ case "invalid-format":
1082
+ return "invalid format";
1083
+ case "read-failed":
1084
+ return "read failed";
1085
+ case "unresolved":
1086
+ return "unresolved";
1087
+ }
1088
+ }
1089
+ function formatSection(title, lines) {
1090
+ return [title, ...lines.map((line) => `- ${line}`)].join("\n");
1091
+ }
1092
+ function formatHealthReport(report) {
1093
+ const sections = [
1094
+ "PrimeUI MCP Health",
1095
+ "",
1096
+ formatSection("Version", [
1097
+ `current: ${report.version.currentVersion}`,
1098
+ `latest: ${renderValue(report.version.latestVersion, "unavailable")}`,
1099
+ `update status: ${report.version.updateStatus}${report.version.detail ? ` (${report.version.detail})` : ""}`
1100
+ ]),
1101
+ "",
1102
+ formatSection("Runtime Context", [
1103
+ `cwd: ${report.runtimeContext.cwd}`,
1104
+ `health input path: ${renderValue(report.runtimeContext.inputProjectRoot)}`,
1105
+ `PRIMEUI_PROJECT_ROOT: ${renderValue(report.runtimeContext.envProjectRoot)}`,
1106
+ `PRIMEUI_API_BASE_URL: ${renderValue(report.runtimeContext.envApiBaseUrl)}`,
1107
+ `PRIMEUI_API_KEY: ${report.runtimeContext.envApiKeySet ? "set" : "missing"}`
1108
+ ]),
1109
+ "",
1110
+ formatSection("Source Resolution", [
1111
+ `input project root: ${renderValue(report.sourceResolution.projectRoot.inputPath)}`,
1112
+ `env project root: ${renderValue(report.sourceResolution.projectRoot.envPath)}`,
1113
+ `project root comparison: ${renderProjectRootComparison(
1114
+ report.sourceResolution.projectRoot.comparison
1115
+ )}`,
1116
+ `effective project root source: ${report.sourceResolution.projectRoot.effectiveSource}`,
1117
+ `env API key: ${report.sourceResolution.apiKey.envState}`,
1118
+ `config API key: ${report.sourceResolution.apiKey.configState}`,
1119
+ `API key comparison: ${renderApiKeyComparison(
1120
+ report.sourceResolution.apiKey.comparison
1121
+ )}`,
1122
+ `effective API key source: ${report.sourceResolution.apiKey.effectiveSource}`
1123
+ ]),
1124
+ "",
1125
+ formatSection("Project Resolution", [
1126
+ `resolved project root: ${renderValue(
1127
+ report.projectResolution.resolvedProjectRoot,
1128
+ "unresolved"
1129
+ )}`,
1130
+ `effective project root source: ${report.projectResolution.effectiveSource}`,
1131
+ `project root validation via explicit input: ${report.projectResolution.inputProjectRootValidated ? "passed" : "not used or failed"}`,
1132
+ `.primeui directory: ${report.projectResolution.primeUiDirStatus === "found" ? `found at ${report.projectResolution.primeUiDirPath}` : report.projectResolution.primeUiDirStatus}`,
1133
+ `config path: ${report.projectResolution.configPath ? `${report.projectResolution.configPath} (${renderConfigStatus(
1134
+ report.projectResolution.configStatus
1135
+ )})` : renderConfigStatus(report.projectResolution.configStatus)}`,
1136
+ `targetProjectPath: ${report.projectResolution.targetProjectPath ?? "unresolved"}`,
1137
+ `resolved target project root: ${renderValue(
1138
+ report.projectResolution.targetProjectRoot,
1139
+ "unresolved"
1140
+ )}`,
1141
+ `project resolution detail: ${renderValue(
1142
+ report.projectResolution.errorMessage,
1143
+ "none"
1144
+ )}`
1145
+ ]),
1146
+ "",
1147
+ formatSection("API Check", [
1148
+ `API root: ${renderValue(report.apiCheck.apiRoot, "unresolved")}`,
1149
+ `project info URL: ${renderValue(report.apiCheck.projectInfoUrl, "unresolved")}`,
1150
+ `PRIMEUI_API_BASE_URL source: ${report.apiCheck.apiBaseUrlSource}`,
1151
+ `non-standard API base URL: ${report.apiCheck.nonStandardBaseUrl ? "yes" : "no"}`,
1152
+ `project info request: ${report.apiCheck.status}`,
1153
+ `request detail: ${report.apiCheck.message}`
1154
+ ])
1155
+ ];
1156
+ if (report.nestedSearch.performed) {
1157
+ const candidateLines = report.nestedSearch.candidates.length > 0 ? report.nestedSearch.candidates.map((candidate) => {
1158
+ const parts = [
1159
+ candidate.primeUiPath,
1160
+ `project root: ${candidate.projectRoot}`,
1161
+ `config: ${renderConfigStatus(candidate.configStatus)}`
1162
+ ];
1163
+ if (candidate.targetProjectPath) {
1164
+ parts.push(`targetProjectPath: ${candidate.targetProjectPath}`);
1165
+ }
1166
+ return parts.join("; ");
1167
+ }) : ["none found"];
1168
+ sections.splice(
1169
+ sections.length - 2,
1170
+ 0,
1171
+ "",
1172
+ formatSection("Nested PrimeUI Candidates", [
1173
+ `search root: ${renderValue(report.nestedSearch.rootPath, "unresolved")}`,
1174
+ ...candidateLines
1175
+ ])
1176
+ );
1177
+ }
1178
+ sections.push("");
1179
+ if (report.hints.length === 0) {
1180
+ sections.push("Hints\n- none");
1181
+ } else {
1182
+ sections.push(
1183
+ [
1184
+ "Hints",
1185
+ ...report.hints.map((hint, index) => `${index + 1}. ${hint}`)
1186
+ ].join("\n")
1187
+ );
1188
+ }
1189
+ return sections.join("\n");
1190
+ }
1191
+
1192
+ // src/health/update-check.ts
1193
+ function parseVersion(input) {
1194
+ const [coreVersion, prerelease] = input.split("-", 2);
1195
+ return {
1196
+ parts: coreVersion.split(".").map((segment) => {
1197
+ const parsed = Number.parseInt(segment, 10);
1198
+ return Number.isFinite(parsed) ? parsed : 0;
1199
+ }),
1200
+ prerelease
1201
+ };
1202
+ }
1203
+ function compareVersions(left, right) {
1204
+ const leftVersion = parseVersion(left);
1205
+ const rightVersion = parseVersion(right);
1206
+ const length = Math.max(leftVersion.parts.length, rightVersion.parts.length);
1207
+ for (let index = 0; index < length; index += 1) {
1208
+ const leftPart = leftVersion.parts[index] ?? 0;
1209
+ const rightPart = rightVersion.parts[index] ?? 0;
1210
+ if (leftPart !== rightPart) {
1211
+ return leftPart > rightPart ? 1 : -1;
1212
+ }
1213
+ }
1214
+ if (!leftVersion.prerelease && !rightVersion.prerelease) {
1215
+ return 0;
1216
+ }
1217
+ if (!leftVersion.prerelease) {
1218
+ return 1;
1219
+ }
1220
+ if (!rightVersion.prerelease) {
1221
+ return -1;
1222
+ }
1223
+ return leftVersion.prerelease.localeCompare(rightVersion.prerelease);
1224
+ }
1225
+ async function checkLatestPackageVersion(packageName, currentVersion) {
1226
+ const controller = new AbortController();
1227
+ const timeoutHandle = setTimeout(() => controller.abort(), 3e3);
1228
+ try {
1229
+ const response = await fetch(
1230
+ `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`,
1231
+ {
1232
+ signal: controller.signal
1233
+ }
1234
+ );
1235
+ if (!response.ok) {
1236
+ return {
1237
+ status: "unavailable",
1238
+ detail: `npm registry request failed with status ${response.status}`
1239
+ };
1240
+ }
1241
+ const payload = await response.json();
1242
+ const latestVersion = payload.version?.trim();
1243
+ if (!latestVersion) {
1244
+ return {
1245
+ status: "unavailable",
1246
+ detail: "npm registry response did not include a latest version"
1247
+ };
1248
+ }
1249
+ const comparison = compareVersions(currentVersion, latestVersion);
1250
+ return {
1251
+ latestVersion,
1252
+ status: comparison < 0 ? "update-available" : "up-to-date",
1253
+ detail: comparison > 0 ? "installed version is newer than the npm latest tag" : void 0
1254
+ };
1255
+ } catch (error) {
1256
+ return {
1257
+ status: "unavailable",
1258
+ detail: error instanceof Error ? error.message : String(error)
1259
+ };
1260
+ } finally {
1261
+ clearTimeout(timeoutHandle);
1262
+ }
1263
+ }
1264
+
1265
+ // src/health/run-health-check.ts
1266
+ function mapProjectSource(source) {
1267
+ switch (source) {
1268
+ case "tool":
1269
+ return "input";
1270
+ case "env":
1271
+ return "env";
1272
+ case "search":
1273
+ return "search";
1274
+ default:
1275
+ return "unresolved";
1276
+ }
1277
+ }
1278
+ function mapProjectErrorSource(source) {
1279
+ switch (source) {
1280
+ case "tool":
1281
+ return "input";
1282
+ case "env":
1283
+ return "env";
1284
+ case "search":
1285
+ return "search";
1286
+ default:
1287
+ return void 0;
1288
+ }
1289
+ }
1290
+ function compareProjectRoots(inputPath, envPath) {
1291
+ if (inputPath && envPath) {
1292
+ return inputPath === envPath ? "same" : "different";
1293
+ }
1294
+ if (inputPath) {
1295
+ return "input-only";
1296
+ }
1297
+ if (envPath) {
1298
+ return "env-only";
1299
+ }
1300
+ return "neither";
1301
+ }
1302
+ function compareApiKeys(valuesMatch, envProvided, configProvided) {
1303
+ if (envProvided && configProvided) {
1304
+ return valuesMatch ? "same" : "different";
1305
+ }
1306
+ return "not-comparable";
1307
+ }
1308
+ function mapConfigStatus2(error) {
1309
+ if (!error) {
1310
+ return "valid";
1311
+ }
1312
+ switch (error.code) {
1313
+ case "PROJECT_CONFIG_NOT_FOUND":
1314
+ return typeof error.details.expectedConfigPath === "string" ? "missing" : "unresolved";
1315
+ case "PROJECT_CONFIG_INVALID_JSON":
1316
+ return "invalid-json";
1317
+ case "PROJECT_CONFIG_INVALID_FORMAT":
1318
+ return "invalid-format";
1319
+ case "PROJECT_CONFIG_READ_FAILED":
1320
+ return "read-failed";
1321
+ default:
1322
+ return "unresolved";
1323
+ }
1324
+ }
1325
+ async function directoryExists(targetPath) {
1326
+ try {
1327
+ const targetStat = await stat3(targetPath);
1328
+ return targetStat.isDirectory();
1329
+ } catch {
1330
+ return false;
1331
+ }
1332
+ }
1333
+ function classifyApiError(message) {
1334
+ return /PrimeUI API request failed \((401|403)\)/.test(message) ? "auth" : "network";
1335
+ }
1336
+ async function runHealthCheck(options) {
1337
+ const metadata = options.metadata ?? packageMetadata;
1338
+ const env = options.env ?? process.env;
1339
+ const cwd = path7.resolve(options.cwd ?? process.cwd());
1340
+ const inputProjectRoot = options.projectRoot?.trim() ? path7.resolve(options.projectRoot.trim()) : void 0;
1341
+ const envProjectRoot = env.PRIMEUI_PROJECT_ROOT?.trim() ? path7.resolve(env.PRIMEUI_PROJECT_ROOT.trim()) : void 0;
1342
+ const versionResult = await (options.checkLatestVersion ?? checkLatestPackageVersion)(metadata.name, metadata.version);
1343
+ let resolvedProjectConfig;
1344
+ let projectConfigError;
1345
+ try {
1346
+ resolvedProjectConfig = await resolvePrimeUiProjectConfig({
1347
+ cwd,
1348
+ projectRootFromTool: inputProjectRoot,
1349
+ projectRootFromEnv: envProjectRoot
1350
+ });
1351
+ } catch (error) {
1352
+ if (!(error instanceof PrimeUiProjectConfigError)) {
1353
+ throw error;
1354
+ }
1355
+ projectConfigError = error;
1356
+ }
1357
+ const resolvedProjectRoot = resolvedProjectConfig?.projectRoot ?? (typeof projectConfigError?.details.projectRoot === "string" ? path7.resolve(projectConfigError.details.projectRoot) : void 0);
1358
+ const primeUiDirPath = resolvedProjectRoot ? path7.join(resolvedProjectRoot, PRIMEUI_PROJECT_DIR_NAME) : inputProjectRoot ? path7.join(inputProjectRoot, PRIMEUI_PROJECT_DIR_NAME) : envProjectRoot && mapProjectErrorSource(
1359
+ projectConfigError?.details.source
1360
+ ) === "env" ? path7.join(envProjectRoot, PRIMEUI_PROJECT_DIR_NAME) : void 0;
1361
+ const primeUiDirStatus = primeUiDirPath ? await directoryExists(primeUiDirPath) ? "found" : "missing" : "unresolved";
1362
+ const configStatus = resolvedProjectConfig ? "valid" : mapConfigStatus2(projectConfigError);
1363
+ const configPath = resolvedProjectConfig?.projectConfigPath ?? (typeof projectConfigError?.details.expectedConfigPath === "string" ? path7.resolve(projectConfigError.details.expectedConfigPath) : primeUiDirPath ? path7.join(primeUiDirPath, "project.json") : void 0);
1364
+ const nestedSearchPerformed = Boolean(inputProjectRoot) && primeUiDirStatus === "missing";
1365
+ const nestedCandidates = nestedSearchPerformed ? await findNestedPrimeUiCandidates(inputProjectRoot) : [];
1366
+ const apiKeyDetails = resolvePrimeUiApiKeyDetails({
1367
+ projectConfig: resolvedProjectConfig?.projectConfig,
1368
+ apiKeyFromEnv: env.PRIMEUI_API_KEY
1369
+ });
1370
+ const configApiKeyState = resolvedProjectConfig ? "set" : configStatus === "missing" || configStatus === "unresolved" ? "missing" : "unknown";
1371
+ const apiBaseUrlSource = env.PRIMEUI_API_BASE_URL?.trim() ? "env" : "default";
1372
+ const defaultApiRoot = normalizePrimeUiApiRoot(DEFAULT_API_BASE_URL);
1373
+ let apiRoot;
1374
+ let projectInfoUrl;
1375
+ let apiCheck;
1376
+ try {
1377
+ apiRoot = normalizePrimeUiApiRoot(env.PRIMEUI_API_BASE_URL);
1378
+ projectInfoUrl = buildPrimeUiApiUrl(apiRoot, "project");
1379
+ } catch (error) {
1380
+ apiCheck = {
1381
+ status: "skipped",
1382
+ apiBaseUrlSource,
1383
+ nonStandardBaseUrl: Boolean(env.PRIMEUI_API_BASE_URL?.trim()),
1384
+ message: error instanceof Error ? error.message : String(error),
1385
+ errorKind: "configuration"
1386
+ };
1387
+ }
1388
+ if (!apiCheck) {
1389
+ if (!apiKeyDetails.apiKey) {
1390
+ apiCheck = {
1391
+ status: "skipped",
1392
+ apiRoot,
1393
+ projectInfoUrl,
1394
+ apiBaseUrlSource,
1395
+ nonStandardBaseUrl: apiRoot !== defaultApiRoot,
1396
+ message: "No usable API key was available, so project info was not requested."
1397
+ };
1398
+ } else {
1399
+ const provider = options.createProvider?.({
1400
+ apiKey: apiKeyDetails.apiKey,
1401
+ baseUrl: env.PRIMEUI_API_BASE_URL
1402
+ }) ?? new ApiProjectDataProvider({
1403
+ apiKey: apiKeyDetails.apiKey,
1404
+ baseUrl: env.PRIMEUI_API_BASE_URL
1405
+ });
1406
+ try {
1407
+ await provider.getProjectInfo();
1408
+ apiCheck = {
1409
+ status: "passed",
1410
+ apiRoot,
1411
+ projectInfoUrl,
1412
+ apiBaseUrlSource,
1413
+ nonStandardBaseUrl: apiRoot !== defaultApiRoot,
1414
+ message: "Project info request succeeded with the current API key."
1415
+ };
1416
+ } catch (error) {
1417
+ const message = error instanceof Error ? error.message : String(error);
1418
+ apiCheck = {
1419
+ status: "failed",
1420
+ apiRoot,
1421
+ projectInfoUrl,
1422
+ apiBaseUrlSource,
1423
+ nonStandardBaseUrl: apiRoot !== defaultApiRoot,
1424
+ message,
1425
+ errorKind: classifyApiError(message)
1426
+ };
1427
+ }
1428
+ }
1429
+ }
1430
+ if (!apiCheck) {
1431
+ throw new Error(
1432
+ "PrimeUI health check did not produce an API check result."
1433
+ );
1434
+ }
1435
+ const report = {
1436
+ version: {
1437
+ currentVersion: metadata.version,
1438
+ latestVersion: versionResult.latestVersion,
1439
+ updateStatus: versionResult.status,
1440
+ detail: versionResult.detail
1441
+ },
1442
+ runtimeContext: {
1443
+ cwd,
1444
+ inputProjectRoot,
1445
+ envProjectRoot,
1446
+ envApiBaseUrl: env.PRIMEUI_API_BASE_URL?.trim() || void 0,
1447
+ envApiKeySet: Boolean(env.PRIMEUI_API_KEY?.trim())
1448
+ },
1449
+ sourceResolution: {
1450
+ projectRoot: {
1451
+ inputPath: inputProjectRoot,
1452
+ envPath: envProjectRoot,
1453
+ comparison: compareProjectRoots(inputProjectRoot, envProjectRoot),
1454
+ effectiveSource: mapProjectSource(
1455
+ resolvedProjectConfig?.source ?? projectConfigError?.details.source
1456
+ )
1457
+ },
1458
+ apiKey: {
1459
+ envState: apiKeyDetails.envProvided ? "set" : "missing",
1460
+ configState: configApiKeyState,
1461
+ comparison: compareApiKeys(
1462
+ apiKeyDetails.valuesMatch,
1463
+ apiKeyDetails.envProvided,
1464
+ apiKeyDetails.configProvided
1465
+ ),
1466
+ effectiveSource: apiKeyDetails.source
1467
+ }
1468
+ },
1469
+ projectResolution: {
1470
+ resolvedProjectRoot,
1471
+ effectiveSource: mapProjectSource(
1472
+ resolvedProjectConfig?.source ?? projectConfigError?.details.source
1473
+ ),
1474
+ errorSource: mapProjectErrorSource(
1475
+ projectConfigError?.details.source
1476
+ ),
1477
+ primeUiDirPath,
1478
+ primeUiDirStatus,
1479
+ configPath,
1480
+ configStatus,
1481
+ targetProjectPath: resolvedProjectConfig?.projectConfig.targetProjectPath,
1482
+ targetProjectRoot: resolvedProjectConfig ? resolvePrimeUiTargetProjectRoot(
1483
+ resolvedProjectConfig.projectRoot,
1484
+ resolvedProjectConfig.projectConfig
1485
+ ) : void 0,
1486
+ errorCode: projectConfigError?.code,
1487
+ errorMessage: projectConfigError?.message,
1488
+ inputProjectRootValidated: Boolean(inputProjectRoot) && resolvedProjectConfig?.source === "tool"
1489
+ },
1490
+ nestedSearch: {
1491
+ performed: nestedSearchPerformed,
1492
+ rootPath: nestedSearchPerformed ? inputProjectRoot : void 0,
1493
+ candidates: nestedCandidates
1494
+ },
1495
+ apiCheck,
1496
+ hints: []
1497
+ };
1498
+ report.hints = buildHealthHints({
1499
+ report,
1500
+ connectApiKey: nestedSearchPerformed && nestedCandidates.length === 0 && apiCheck.status === "passed" ? apiKeyDetails.apiKey : void 0
1501
+ });
1502
+ options.stdout.write(`${formatHealthReport(report)}
1503
+ `);
1504
+ return 0;
1505
+ }
1506
+
1507
+ // src/services/project-sync-service.ts
1508
+ import path10 from "path";
1509
+ import { readFile as readFile4 } from "fs/promises";
1510
+
276
1511
  // src/services/page-copy-service.ts
277
- import { readFile as readFile3, readdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
278
- import path4 from "path";
1512
+ import { readFile as readFile3, readdir as readdir2, stat as stat5, writeFile as writeFile2 } from "fs/promises";
1513
+ import path9 from "path";
279
1514
 
280
1515
  // src/lib/import-graph.ts
281
- import { readFile as readFile2, stat as stat2 } from "fs/promises";
1516
+ import { readFile as readFile2, stat as stat4 } from "fs/promises";
282
1517
  import { builtinModules } from "module";
283
- import path3 from "path";
1518
+ import path8 from "path";
284
1519
  var INTERNAL_EXTENSIONS = [
285
1520
  ".ts",
286
1521
  ".tsx",
@@ -349,14 +1584,14 @@ function getExternalPackageName(specifier) {
349
1584
  }
350
1585
  async function pathExists(filePath) {
351
1586
  try {
352
- await stat2(filePath);
1587
+ await stat4(filePath);
353
1588
  return true;
354
1589
  } catch {
355
1590
  return false;
356
1591
  }
357
1592
  }
358
1593
  async function resolveFileCandidate(candidateBase) {
359
- const ext = path3.extname(candidateBase);
1594
+ const ext = path8.extname(candidateBase);
360
1595
  if (ext) {
361
1596
  if (await pathExists(candidateBase)) {
362
1597
  return candidateBase;
@@ -370,13 +1605,13 @@ async function resolveFileCandidate(candidateBase) {
370
1605
  }
371
1606
  }
372
1607
  if (await pathExists(candidateBase)) {
373
- const stats = await stat2(candidateBase);
1608
+ const stats = await stat4(candidateBase);
374
1609
  if (stats.isFile()) {
375
1610
  return candidateBase;
376
1611
  }
377
1612
  if (stats.isDirectory()) {
378
1613
  for (const extension of INTERNAL_EXTENSIONS) {
379
- const indexCandidate = path3.join(candidateBase, `index${extension}`);
1614
+ const indexCandidate = path8.join(candidateBase, `index${extension}`);
380
1615
  if (await pathExists(indexCandidate)) {
381
1616
  return indexCandidate;
382
1617
  }
@@ -398,13 +1633,13 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
398
1633
  }
399
1634
  let candidateBase = null;
400
1635
  if (specifier.startsWith("@/")) {
401
- candidateBase = path3.join(projectRoot, "src", specifier.slice(2));
1636
+ candidateBase = path8.join(projectRoot, "src", specifier.slice(2));
402
1637
  } else if (specifier.startsWith("@root/")) {
403
- candidateBase = path3.join(projectRoot, specifier.slice(6));
1638
+ candidateBase = path8.join(projectRoot, specifier.slice(6));
404
1639
  } else if (specifier.startsWith(".")) {
405
- candidateBase = path3.resolve(path3.dirname(importerFilePath), specifier);
1640
+ candidateBase = path8.resolve(path8.dirname(importerFilePath), specifier);
406
1641
  } else if (specifier.startsWith("/")) {
407
- candidateBase = path3.join(projectRoot, specifier.slice(1));
1642
+ candidateBase = path8.join(projectRoot, specifier.slice(1));
408
1643
  } else {
409
1644
  return { kind: "unknown" };
410
1645
  }
@@ -418,12 +1653,12 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
418
1653
  };
419
1654
  }
420
1655
  function shouldParseFile(filePath) {
421
- return PARSEABLE_EXTENSIONS.has(path3.extname(filePath).toLowerCase());
1656
+ return PARSEABLE_EXTENSIONS.has(path8.extname(filePath).toLowerCase());
422
1657
  }
423
1658
  async function buildImportGraph(input) {
424
1659
  const shouldFollowInternalImports = input.followInternalImports ?? true;
425
- const projectRoot = path3.resolve(input.projectRoot);
426
- const queue = input.entryFiles.map((filePath) => path3.resolve(filePath));
1660
+ const projectRoot = path8.resolve(input.projectRoot);
1661
+ const queue = input.entryFiles.map((filePath) => path8.resolve(filePath));
427
1662
  const visited = /* @__PURE__ */ new Set();
428
1663
  const internalFiles = /* @__PURE__ */ new Set();
429
1664
  const externalPackages = /* @__PURE__ */ new Set();
@@ -676,10 +1911,10 @@ function normalizeSlug2(slug) {
676
1911
  return withLeadingSlash.replace(/\/+$/, "") || "/";
677
1912
  }
678
1913
  function toPosixPath(value) {
679
- return value.split(path4.sep).join("/");
1914
+ return value.split(path9.sep).join("/");
680
1915
  }
681
1916
  function toProjectRelative(rootPath, absolutePath) {
682
- return toPosixPath(path4.relative(rootPath, absolutePath));
1917
+ return toPosixPath(path9.relative(rootPath, absolutePath));
683
1918
  }
684
1919
  function escapeRegExp(value) {
685
1920
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -736,7 +1971,7 @@ function buildPlannedSourceBuffer(sourceBuffer, sourceFilePath, importRewritePla
736
1971
  if (!importRewritePlan) {
737
1972
  return sourceBuffer;
738
1973
  }
739
- const extension = path4.extname(sourceFilePath).toLowerCase();
1974
+ const extension = path9.extname(sourceFilePath).toLowerCase();
740
1975
  if (!REWRITABLE_IMPORT_EXTENSIONS.has(extension)) {
741
1976
  return sourceBuffer;
742
1977
  }
@@ -759,7 +1994,7 @@ async function readJsonFile(filePath) {
759
1994
  }
760
1995
  async function fileExists2(filePath) {
761
1996
  try {
762
- await stat3(filePath);
1997
+ await stat5(filePath);
763
1998
  return true;
764
1999
  } catch {
765
2000
  return false;
@@ -838,14 +2073,14 @@ async function resolveManifestCandidateFiles(input) {
838
2073
  );
839
2074
  }
840
2075
  const normalizedRelative = toPosixPath(trimmed).replace(/^\.\/+/, "");
841
- const absolutePath = path4.resolve(input.exportPath, normalizedRelative);
842
- const relative = path4.relative(input.exportPath, absolutePath);
843
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
2076
+ const absolutePath = path9.resolve(input.exportPath, normalizedRelative);
2077
+ const relative = path9.relative(input.exportPath, absolutePath);
2078
+ if (relative.startsWith("..") || path9.isAbsolute(relative)) {
844
2079
  throw new Error(
845
2080
  `Export manifest for page "${input.pageSlug}" contains path outside export root: ${trimmed}`
846
2081
  );
847
2082
  }
848
- const stats = await stat3(absolutePath).catch(() => null);
2083
+ const stats = await stat5(absolutePath).catch(() => null);
849
2084
  if (!stats?.isFile()) {
850
2085
  throw new Error(
851
2086
  `Export manifest file is missing for page "${input.pageSlug}": ${normalizedRelative}`
@@ -856,7 +2091,7 @@ async function resolveManifestCandidateFiles(input) {
856
2091
  return [...result].sort((a, b) => a.localeCompare(b));
857
2092
  }
858
2093
  async function resolveSingleExportDirectory(exportsRoot) {
859
- const entries = await readdir(exportsRoot, { withFileTypes: true });
2094
+ const entries = await readdir2(exportsRoot, { withFileTypes: true });
860
2095
  const exportDirectories = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
861
2096
  if (exportDirectories.length === 0) {
862
2097
  throw new Error(
@@ -871,12 +2106,12 @@ async function resolveSingleExportDirectory(exportsRoot) {
871
2106
  const exportId = exportDirectories[0] ?? "";
872
2107
  return {
873
2108
  exportId,
874
- exportPath: path4.join(exportsRoot, exportId)
2109
+ exportPath: path9.join(exportsRoot, exportId)
875
2110
  };
876
2111
  }
877
2112
  function ensureSafeTargetPath(projectRoot, targetPath) {
878
- const relative = path4.relative(projectRoot, targetPath);
879
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
2113
+ const relative = path9.relative(projectRoot, targetPath);
2114
+ if (relative.startsWith("..") || path9.isAbsolute(relative)) {
880
2115
  throw new Error(
881
2116
  `Refusing to write outside project root. Computed target: ${targetPath}`
882
2117
  );
@@ -934,19 +2169,19 @@ function resolveTargetFilePath(input) {
934
2169
  if (sourceFilePath === sourcePagePath) {
935
2170
  return targetPagePath;
936
2171
  }
937
- const componentsPrefix = `${sourceComponentsPath}${path4.sep}`;
2172
+ const componentsPrefix = `${sourceComponentsPath}${path9.sep}`;
938
2173
  if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
939
- const relativeToComponents = path4.relative(
2174
+ const relativeToComponents = path9.relative(
940
2175
  sourceComponentsPath,
941
2176
  sourceFilePath
942
2177
  );
943
- return path4.join(targetComponentsPath, relativeToComponents);
2178
+ return path9.join(targetComponentsPath, relativeToComponents);
944
2179
  }
945
- const relativeToExport = path4.relative(exportRoot, sourceFilePath);
946
- if (relativeToExport.startsWith("..") || path4.isAbsolute(relativeToExport)) {
2180
+ const relativeToExport = path9.relative(exportRoot, sourceFilePath);
2181
+ if (relativeToExport.startsWith("..") || path9.isAbsolute(relativeToExport)) {
947
2182
  throw new Error(`Source file is outside export root: ${sourceFilePath}`);
948
2183
  }
949
- return path4.join(projectRoot, relativeToExport);
2184
+ return path9.join(projectRoot, relativeToExport);
950
2185
  }
951
2186
  async function copyPageFromExport(input) {
952
2187
  const normalizedOriginSlug = normalizeSlug2(input.originPageSlug);
@@ -957,7 +2192,7 @@ async function copyPageFromExport(input) {
957
2192
  const { exportId, exportPath } = await resolveSingleExportDirectory(
958
2193
  input.exportsRoot
959
2194
  );
960
- const manifestPath = path4.join(
2195
+ const manifestPath = path9.join(
961
2196
  input.exportsRoot,
962
2197
  `${exportId}.manifest.json`
963
2198
  );
@@ -980,13 +2215,13 @@ async function copyPageFromExport(input) {
980
2215
  `Page not found in manifest for slug: ${normalizedOriginSlug}`
981
2216
  );
982
2217
  }
983
- const sourcePagePath = path4.join(exportPath, page.pagePath);
984
- const sourceComponentsPath = path4.join(exportPath, page.componentsPath);
985
- const sourcePageStats = await stat3(sourcePagePath).catch(() => null);
2218
+ const sourcePagePath = path9.join(exportPath, page.pagePath);
2219
+ const sourceComponentsPath = path9.join(exportPath, page.componentsPath);
2220
+ const sourcePageStats = await stat5(sourcePagePath).catch(() => null);
986
2221
  if (!sourcePageStats?.isFile()) {
987
2222
  throw new Error(`Source page file not found: ${sourcePagePath}`);
988
2223
  }
989
- const sourceComponentsStats = await stat3(sourceComponentsPath).catch(
2224
+ const sourceComponentsStats = await stat5(sourceComponentsPath).catch(
990
2225
  () => null
991
2226
  );
992
2227
  if (!sourceComponentsStats?.isDirectory()) {
@@ -1001,8 +2236,8 @@ async function copyPageFromExport(input) {
1001
2236
  pageType: page.pageType,
1002
2237
  slug: normalizedActualSlug
1003
2238
  });
1004
- const targetPagePath = path4.join(input.projectRoot, targetPaths.pagePath);
1005
- const targetComponentsPath = path4.join(
2239
+ const targetPagePath = path9.join(input.projectRoot, targetPaths.pagePath);
2240
+ const targetComponentsPath = path9.join(
1006
2241
  input.projectRoot,
1007
2242
  targetPaths.componentsPath
1008
2243
  );
@@ -1048,7 +2283,7 @@ async function copyPageFromExport(input) {
1048
2283
  const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
1049
2284
  const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
1050
2285
  if (!targetBuffer) {
1051
- await ensureDir(path4.dirname(targetFilePath));
2286
+ await ensureDir(path9.dirname(targetFilePath));
1052
2287
  await writeFile2(targetFilePath, plannedSourceBuffer);
1053
2288
  newFiles.push({
1054
2289
  sourcePath: sourceRelative,
@@ -1077,8 +2312,8 @@ async function copyPageFromExport(input) {
1077
2312
  isBinary
1078
2313
  });
1079
2314
  }
1080
- const exportPackageJsonPath = path4.join(exportPath, "package.json");
1081
- const userPackageJsonPath = path4.join(input.projectRoot, "package.json");
2315
+ const exportPackageJsonPath = path9.join(exportPath, "package.json");
2316
+ const userPackageJsonPath = path9.join(input.projectRoot, "package.json");
1082
2317
  if (!await fileExists2(exportPackageJsonPath)) {
1083
2318
  throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
1084
2319
  }
@@ -1284,7 +2519,7 @@ function isExportPage(value) {
1284
2519
  return typeof maybe.id === "string" && (typeof title === "string" || typeof title === "undefined") && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && maybe.isReadyToExport === true && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string" && isExportPageManifest(maybe.manifest);
1285
2520
  }
1286
2521
  function buildManifestPath(exportsRoot, exportId) {
1287
- return path5.join(exportsRoot, `${exportId}.manifest.json`);
2522
+ return path10.join(exportsRoot, `${exportId}.manifest.json`);
1288
2523
  }
1289
2524
  function parseExportManifest(value) {
1290
2525
  if (!value || typeof value !== "object") {
@@ -1328,9 +2563,9 @@ var ProjectSyncService = class {
1328
2563
  this.projectRoot = options.projectRoot;
1329
2564
  this.targetProjectRoot = options.targetProjectRoot;
1330
2565
  this.provider = options.provider;
1331
- this.primeUiRoot = path5.join(this.projectRoot, ".primeui");
1332
- this.tempRoot = path5.join(this.primeUiRoot, "temp");
1333
- this.exportsRoot = path5.join(this.tempRoot, "exports");
2566
+ this.primeUiRoot = path10.join(this.projectRoot, ".primeui");
2567
+ this.tempRoot = path10.join(this.primeUiRoot, "temp");
2568
+ this.exportsRoot = path10.join(this.tempRoot, "exports");
1334
2569
  }
1335
2570
  async getProjectInfo(_context) {
1336
2571
  await this.ensureTempLayout();
@@ -1355,8 +2590,8 @@ var ProjectSyncService = class {
1355
2590
  if (!selected) {
1356
2591
  throw new Error(`Export not found: ${id}`);
1357
2592
  }
1358
- const targetZipPath = path5.join(this.exportsRoot, `${id}.zip`);
1359
- const targetProjectPath = path5.join(this.exportsRoot, id);
2593
+ const targetZipPath = path10.join(this.exportsRoot, `${id}.zip`);
2594
+ const targetProjectPath = path10.join(this.exportsRoot, id);
1360
2595
  const manifestPath = buildManifestPath(this.exportsRoot, id);
1361
2596
  let manifest;
1362
2597
  try {
@@ -1416,319 +2651,6 @@ var ProjectSyncService = class {
1416
2651
  }
1417
2652
  };
1418
2653
 
1419
- // src/sources/api-provider.ts
1420
- import { createWriteStream } from "fs";
1421
- import { unlink } from "fs/promises";
1422
- import path6 from "path";
1423
- import { Readable, Transform } from "stream";
1424
- import { pipeline } from "stream/promises";
1425
- import { z as z2 } from "zod";
1426
- var DEFAULT_API_BASE_URL = "https://app.primeui.com/";
1427
- var ZIP_CONTENT_TYPES = [
1428
- "application/zip",
1429
- "application/octet-stream",
1430
- "application/x-zip-compressed"
1431
- ];
1432
- var exportStatusSchema = z2.enum(["in_progress", "completed", "failed"]);
1433
- var projectPageObjectSchema = z2.object({
1434
- id: z2.string(),
1435
- title: z2.string(),
1436
- slug: z2.string(),
1437
- pageType: z2.string(),
1438
- isReadyToExport: z2.boolean(),
1439
- pagePath: z2.string(),
1440
- componentsPath: z2.string()
1441
- });
1442
- var projectPageSchema = projectPageObjectSchema;
1443
- var exportSummarySchema = z2.object({
1444
- total: z2.number(),
1445
- successful: z2.number(),
1446
- failed: z2.number()
1447
- });
1448
- var exportedComponentSchema = z2.object({
1449
- componentKey: z2.string(),
1450
- enabled: z2.boolean(),
1451
- files: z2.array(z2.string()),
1452
- message: z2.string()
1453
- });
1454
- var exportPageManifestSchema = z2.object({
1455
- success: z2.boolean(),
1456
- message: z2.string(),
1457
- files: z2.array(z2.string())
1458
- });
1459
- var exportPageSchema = z2.object({
1460
- id: z2.string(),
1461
- title: z2.string().optional(),
1462
- slug: z2.string(),
1463
- pageType: z2.string(),
1464
- isReadyToExport: z2.literal(true),
1465
- pagePath: z2.string(),
1466
- componentsPath: z2.string(),
1467
- manifest: exportPageManifestSchema
1468
- });
1469
- var projectInfoSchema = z2.object({
1470
- projectId: z2.string(),
1471
- projectName: z2.string(),
1472
- metadata: z2.record(z2.unknown()),
1473
- pages: z2.array(projectPageSchema)
1474
- });
1475
- var projectPageComponentSchema = z2.object({
1476
- blockId: z2.string(),
1477
- componentId: z2.string(),
1478
- componentGroup: z2.string(),
1479
- slot: z2.string().nullable(),
1480
- props: z2.record(z2.unknown()).nullable()
1481
- });
1482
- var projectPageDetailsSchema = z2.object({
1483
- page: projectPageObjectSchema.extend({
1484
- pageInstruction: z2.string().nullable()
1485
- }),
1486
- variant: z2.object({
1487
- id: z2.string(),
1488
- name: z2.string()
1489
- }).nullable(),
1490
- components: z2.array(projectPageComponentSchema).nullable()
1491
- });
1492
- var exportsResponseSchema = z2.object({
1493
- exports: z2.array(
1494
- z2.object({
1495
- id: z2.string(),
1496
- status: exportStatusSchema,
1497
- createdAt: z2.string().datetime({ offset: true })
1498
- })
1499
- )
1500
- });
1501
- var createExportResponseSchema = z2.object({
1502
- export: z2.object({
1503
- id: z2.string(),
1504
- status: exportStatusSchema,
1505
- createdAt: z2.string().datetime({ offset: true }),
1506
- expiresAt: z2.string().datetime({ offset: true }).nullable(),
1507
- summary: exportSummarySchema,
1508
- components: z2.array(exportedComponentSchema)
1509
- }),
1510
- pages: z2.array(exportPageSchema)
1511
- });
1512
- var PrimeUiApiContractError = class extends Error {
1513
- constructor(endpoint, details) {
1514
- super(`PrimeUI API contract mismatch for "${endpoint}": ${details}`);
1515
- this.name = "PrimeUiApiContractError";
1516
- }
1517
- };
1518
- var normalizeApiRoot = (rawBaseUrl) => {
1519
- const normalizedBase = rawBaseUrl.trim() || DEFAULT_API_BASE_URL;
1520
- const parsed = new URL(normalizedBase);
1521
- const normalizedPath = parsed.pathname.replace(/\/+$/, "");
1522
- if (normalizedPath.endsWith("/api/v1")) {
1523
- parsed.pathname = `${normalizedPath}/`;
1524
- return parsed.toString();
1525
- }
1526
- parsed.pathname = `${normalizedPath}/api/v1/`.replace(
1527
- "//api/v1/",
1528
- "/api/v1/"
1529
- );
1530
- return parsed.toString();
1531
- };
1532
- var isJsonContentType = (contentType) => contentType.includes("application/json") || contentType.includes("+json");
1533
- var isZipContentType = (contentType) => ZIP_CONTENT_TYPES.some((allowedType) => contentType.includes(allowedType));
1534
- var looksLikeZipArchive = (buffer) => {
1535
- if (buffer.length < 4) {
1536
- return false;
1537
- }
1538
- if (buffer[0] !== 80 || buffer[1] !== 75) {
1539
- return false;
1540
- }
1541
- const signature = `${buffer[2]}:${buffer[3]}`;
1542
- return signature === "3:4" || signature === "5:6" || signature === "7:8";
1543
- };
1544
- var createZipSignatureGuard = (endpoint) => {
1545
- let signature = Buffer.alloc(0);
1546
- let validated = false;
1547
- return new Transform({
1548
- transform(chunk, _encoding, callback) {
1549
- const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1550
- if (!validated) {
1551
- const requiredBytes = Math.max(0, 4 - signature.length);
1552
- if (requiredBytes > 0) {
1553
- signature = Buffer.concat([
1554
- signature,
1555
- chunkBuffer.subarray(0, requiredBytes)
1556
- ]);
1557
- }
1558
- if (signature.length >= 4) {
1559
- if (!looksLikeZipArchive(signature)) {
1560
- callback(
1561
- new PrimeUiApiContractError(
1562
- endpoint,
1563
- "response body is not a valid zip archive"
1564
- )
1565
- );
1566
- return;
1567
- }
1568
- validated = true;
1569
- }
1570
- }
1571
- callback(null, chunkBuffer);
1572
- },
1573
- flush(callback) {
1574
- if (!validated) {
1575
- callback(
1576
- new PrimeUiApiContractError(
1577
- endpoint,
1578
- "response body is not a valid zip archive"
1579
- )
1580
- );
1581
- return;
1582
- }
1583
- callback();
1584
- }
1585
- });
1586
- };
1587
- var ApiProjectDataProvider = class {
1588
- apiKey;
1589
- apiRoot;
1590
- constructor(options) {
1591
- this.apiKey = options.apiKey;
1592
- this.apiRoot = normalizeApiRoot(options.baseUrl ?? DEFAULT_API_BASE_URL);
1593
- }
1594
- async getProjectInfo() {
1595
- return this.requestJson("project", projectInfoSchema);
1596
- }
1597
- async getProjectPageBySlug(slug) {
1598
- const encodedSlug = encodeURIComponent(slug);
1599
- return this.requestJson(
1600
- `project/page?slug=${encodedSlug}`,
1601
- projectPageDetailsSchema
1602
- );
1603
- }
1604
- async listExports() {
1605
- const response = await this.requestJson(
1606
- "project/exports",
1607
- exportsResponseSchema
1608
- );
1609
- return response.exports;
1610
- }
1611
- async createExport() {
1612
- return this.requestJson("project/exports", createExportResponseSchema, {
1613
- method: "POST"
1614
- });
1615
- }
1616
- /**
1617
- * Consumer-side runtime contract for GET /api/v1/project/exports/:exportId/download.
1618
- * Producer source of truth: apps/studio/src/app/api/v1/project/exports/[exportId]/download/route.ts
1619
- * Keep content-type and archive format checks synchronized with the producer response.
1620
- */
1621
- async downloadExportArchive(exportId, destinationPath) {
1622
- const endpoint = `project/exports/${encodeURIComponent(exportId)}/download`;
1623
- const apiKey = this.requireApiKey();
1624
- const response = await fetch(this.buildUrl(endpoint), {
1625
- method: "GET",
1626
- headers: {
1627
- Authorization: `Bearer ${apiKey}`
1628
- }
1629
- });
1630
- if (!response.ok) {
1631
- const details = await this.readError(response);
1632
- throw new Error(
1633
- `PrimeUI API request failed (${response.status}) while downloading export "${exportId}": ${details}`
1634
- );
1635
- }
1636
- const contentType = response.headers.get("content-type") ?? "";
1637
- if (!isZipContentType(contentType)) {
1638
- throw new PrimeUiApiContractError(
1639
- endpoint,
1640
- `expected zip content-type but got "${contentType || "unknown"}"`
1641
- );
1642
- }
1643
- if (!response.body) {
1644
- throw new PrimeUiApiContractError(endpoint, "response body is empty");
1645
- }
1646
- await ensureDir(path6.dirname(destinationPath));
1647
- const zipStream = Readable.fromWeb(
1648
- response.body
1649
- );
1650
- const signatureGuard = createZipSignatureGuard(endpoint);
1651
- const fileStream = createWriteStream(destinationPath);
1652
- try {
1653
- await pipeline(zipStream, signatureGuard, fileStream);
1654
- } catch (error) {
1655
- await unlink(destinationPath).catch(() => void 0);
1656
- throw error;
1657
- }
1658
- }
1659
- buildUrl(endpoint) {
1660
- return new URL(endpoint, this.apiRoot).toString();
1661
- }
1662
- async requestJson(endpoint, schema, requestInit = {}) {
1663
- const apiKey = this.requireApiKey();
1664
- const method = requestInit.method ?? "GET";
1665
- const response = await fetch(this.buildUrl(endpoint), {
1666
- ...requestInit,
1667
- method,
1668
- headers: {
1669
- ...requestInit.headers ?? {},
1670
- Authorization: `Bearer ${apiKey}`
1671
- }
1672
- });
1673
- if (!response.ok) {
1674
- const details = await this.readError(response);
1675
- throw new Error(
1676
- `PrimeUI API request failed (${response.status}) for "${endpoint}": ${details}`
1677
- );
1678
- }
1679
- const contentType = response.headers.get("content-type") ?? "";
1680
- if (!isJsonContentType(contentType)) {
1681
- throw new PrimeUiApiContractError(
1682
- endpoint,
1683
- `expected JSON content-type but got "${contentType || "unknown"}"`
1684
- );
1685
- }
1686
- let payload;
1687
- try {
1688
- payload = await response.json();
1689
- } catch {
1690
- throw new PrimeUiApiContractError(
1691
- endpoint,
1692
- "response body is not valid JSON"
1693
- );
1694
- }
1695
- const parsed = schema.safeParse(payload);
1696
- if (!parsed.success) {
1697
- const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
1698
- throw new PrimeUiApiContractError(endpoint, details);
1699
- }
1700
- return parsed.data;
1701
- }
1702
- async readError(response) {
1703
- const contentType = response.headers.get("content-type") ?? "";
1704
- try {
1705
- if (isJsonContentType(contentType)) {
1706
- const body = await response.json();
1707
- if (body?.message && body?.code) {
1708
- return `${body.code}: ${body.message}`;
1709
- }
1710
- if (body?.message) {
1711
- return body.message;
1712
- }
1713
- } else {
1714
- const bodyText = await response.text();
1715
- if (bodyText.trim()) {
1716
- return bodyText.trim();
1717
- }
1718
- }
1719
- } catch {
1720
- }
1721
- return response.statusText || "Unknown error";
1722
- }
1723
- requireApiKey() {
1724
- const apiKey = this.apiKey?.trim();
1725
- if (!apiKey) {
1726
- throw new Error("PRIMEUI_API_KEY is required to call PrimeUI API tools.");
1727
- }
1728
- return apiKey;
1729
- }
1730
- };
1731
-
1732
2654
  // src/runtime.ts
1733
2655
  function defaultCreateProvider(options) {
1734
2656
  return new ApiProjectDataProvider(options);
@@ -1759,9 +2681,9 @@ var LazyProjectSyncSource = class {
1759
2681
  projectRootFromEnv: this.env.PRIMEUI_PROJECT_ROOT
1760
2682
  });
1761
2683
  const projectRoot = resolvedProjectConfig.projectRoot;
1762
- const targetProjectRoot = path7.resolve(
2684
+ const targetProjectRoot = resolvePrimeUiTargetProjectRoot(
1763
2685
  projectRoot,
1764
- resolvedProjectConfig.projectConfig.targetProjectPath
2686
+ resolvedProjectConfig.projectConfig
1765
2687
  );
1766
2688
  const apiKey = await resolvePrimeUiApiKey({
1767
2689
  projectConfig: resolvedProjectConfig.projectConfig,
@@ -1823,132 +2745,132 @@ var LazyProjectSyncSource = class {
1823
2745
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1824
2746
 
1825
2747
  // src/instructions.ts
1826
- import { z as z3 } from "zod/v3";
1827
- var pageSchema = z3.object({
1828
- id: z3.string().describe(
2748
+ import { z as z4 } from "zod/v3";
2749
+ var pageSchema = z4.object({
2750
+ id: z4.string().describe(
1829
2751
  "Stable PrimeUI page identifier. Use this to match the same page across tool responses."
1830
2752
  ),
1831
- title: z3.string().describe(
2753
+ title: z4.string().describe(
1832
2754
  "Human-readable page title. Show this to the user when confirming which pages to import."
1833
2755
  ),
1834
- slug: z3.string().describe(
2756
+ slug: z4.string().describe(
1835
2757
  "PrimeUI route slug, for example '/', '/pricing', '/docs/getting-started'. Use to match user intent."
1836
2758
  ),
1837
- pageType: z3.string().describe(
2759
+ pageType: z4.string().describe(
1838
2760
  "PrimeUI page type classification (for example landing, docs, pricing, blogIndex, legal)."
1839
2761
  ),
1840
- isReadyToExport: z3.boolean().describe(
2762
+ isReadyToExport: z4.boolean().describe(
1841
2763
  "True when this page has an active export-ready variant. Only ready pages are expected in export output."
1842
2764
  ),
1843
- pagePath: z3.string().describe(
2765
+ pagePath: z4.string().describe(
1844
2766
  "Relative file path to the page source inside downloaded export root. Copy page code from this exact path."
1845
2767
  ),
1846
- componentsPath: z3.string().describe(
2768
+ componentsPath: z4.string().describe(
1847
2769
  "Relative directory path inside downloaded export root where page-level components live. Use as dependency copy start."
1848
2770
  )
1849
2771
  }).describe(
1850
2772
  "PrimeUI page descriptor used by project and export tools. Paths are always relative to export project root."
1851
2773
  );
1852
- var exportStatusSchema2 = z3.enum(["in_progress", "completed", "failed"]).describe(
2774
+ var exportStatusSchema2 = z4.enum(["in_progress", "completed", "failed"]).describe(
1853
2775
  "Export lifecycle state. Use 'completed' before calling download_export."
1854
2776
  );
1855
- var exportItemSchema = z3.object({
1856
- id: z3.string().describe(
2777
+ var exportItemSchema = z4.object({
2778
+ id: z4.string().describe(
1857
2779
  "Export identifier. Pass this value to download_export input.id."
1858
2780
  ),
1859
2781
  status: exportStatusSchema2,
1860
- createdAt: z3.string().describe("Export creation timestamp in ISO-8601 UTC format.")
2782
+ createdAt: z4.string().describe("Export creation timestamp in ISO-8601 UTC format.")
1861
2783
  }).describe("Single export record from PrimeUI export history.");
1862
- var exportSummarySchema2 = z3.object({
1863
- total: z3.number().describe("Total pages processed in this export run."),
1864
- successful: z3.number().describe("Number of pages exported successfully."),
1865
- failed: z3.number().describe("Number of pages that failed export.")
2784
+ var exportSummarySchema2 = z4.object({
2785
+ total: z4.number().describe("Total pages processed in this export run."),
2786
+ successful: z4.number().describe("Number of pages exported successfully."),
2787
+ failed: z4.number().describe("Number of pages that failed export.")
1866
2788
  });
1867
- var exportComponentSchema = z3.object({
1868
- componentKey: z3.string().describe("Global component key from export manifest."),
1869
- enabled: z3.boolean().describe("True when this global component was exported."),
1870
- files: z3.array(z3.string()).describe("Files associated with this global component."),
1871
- message: z3.string().describe("Export status message for this global component.")
2789
+ var exportComponentSchema = z4.object({
2790
+ componentKey: z4.string().describe("Global component key from export manifest."),
2791
+ enabled: z4.boolean().describe("True when this global component was exported."),
2792
+ files: z4.array(z4.string()).describe("Files associated with this global component."),
2793
+ message: z4.string().describe("Export status message for this global component.")
1872
2794
  });
1873
- var exportPageManifestSchema2 = z3.object({
1874
- success: z3.boolean().describe("True when page export succeeded in this export run."),
1875
- message: z3.string().describe("Export status message for this page."),
1876
- files: z3.array(z3.string()).describe(
2795
+ var exportPageManifestSchema2 = z4.object({
2796
+ success: z4.boolean().describe("True when page export succeeded in this export run."),
2797
+ message: z4.string().describe("Export status message for this page."),
2798
+ files: z4.array(z4.string()).describe(
1877
2799
  "Authoritative list of page-related files from PrimeUI export manifest, including shared project files required by this page."
1878
2800
  )
1879
2801
  });
1880
- var exportPageSchema2 = z3.object({
1881
- id: z3.string().describe("Stable PrimeUI page identifier for this export entry."),
1882
- title: z3.string().optional().describe(
2802
+ var exportPageSchema2 = z4.object({
2803
+ id: z4.string().describe("Stable PrimeUI page identifier for this export entry."),
2804
+ title: z4.string().optional().describe(
1883
2805
  "Optional page title from export manifest. Can be undefined for some legacy records."
1884
2806
  ),
1885
- slug: z3.string().describe("PrimeUI page slug included in this export."),
1886
- pageType: z3.string().describe("PrimeUI page type for routing/path resolution."),
1887
- isReadyToExport: z3.literal(true).describe("Always true for pages included in export payload."),
1888
- pagePath: z3.string().describe("Page source file path relative to export root."),
1889
- componentsPath: z3.string().describe("Page components directory path relative to export root."),
2807
+ slug: z4.string().describe("PrimeUI page slug included in this export."),
2808
+ pageType: z4.string().describe("PrimeUI page type for routing/path resolution."),
2809
+ isReadyToExport: z4.literal(true).describe("Always true for pages included in export payload."),
2810
+ pagePath: z4.string().describe("Page source file path relative to export root."),
2811
+ componentsPath: z4.string().describe("Page components directory path relative to export root."),
1890
2812
  manifest: exportPageManifestSchema2
1891
2813
  });
1892
- var fileTransferSchema = z3.object({
1893
- sourcePath: z3.string().describe("Relative source file path inside downloaded export root."),
1894
- targetPath: z3.string().describe("Relative target file path inside user project root.")
2814
+ var fileTransferSchema = z4.object({
2815
+ sourcePath: z4.string().describe("Relative source file path inside downloaded export root."),
2816
+ targetPath: z4.string().describe("Relative target file path inside user project root.")
1895
2817
  });
1896
2818
  var conflictFileSchema = fileTransferSchema.extend({
1897
- diff: z3.string().describe(
2819
+ diff: z4.string().describe(
1898
2820
  "Unified diff between user file and export file. For binary files the diff contains a plain message."
1899
2821
  ),
1900
- isBinary: z3.boolean().describe("True if conflict comparison was treated as binary data.")
2822
+ isBinary: z4.boolean().describe("True if conflict comparison was treated as binary data.")
1901
2823
  });
1902
- var dependencySectionSchema = z3.enum([
2824
+ var dependencySectionSchema = z4.enum([
1903
2825
  "dependencies",
1904
2826
  "devDependencies",
1905
2827
  "peerDependencies"
1906
2828
  ]);
1907
- var dependencyToAddSchema = z3.object({
1908
- packageName: z3.string().describe("Dependency package name that was missing in user package.json."),
1909
- version: z3.string().describe("Version from exported project's package.json."),
2829
+ var dependencyToAddSchema = z4.object({
2830
+ packageName: z4.string().describe("Dependency package name that was missing in user package.json."),
2831
+ version: z4.string().describe("Version from exported project's package.json."),
1910
2832
  section: dependencySectionSchema.describe(
1911
2833
  "package.json section where dependency should be added."
1912
2834
  )
1913
2835
  });
1914
- var dependencyVersionConflictSchema = z3.object({
1915
- packageName: z3.string().describe("Dependency package name with version mismatch."),
2836
+ var dependencyVersionConflictSchema = z4.object({
2837
+ packageName: z4.string().describe("Dependency package name with version mismatch."),
1916
2838
  section: dependencySectionSchema.describe(
1917
2839
  "Section where export expects this dependency."
1918
2840
  ),
1919
- exportVersion: z3.string().describe("Version found in exported project's package.json."),
1920
- userVersion: z3.string().describe("Version currently present in user's package.json.")
2841
+ exportVersion: z4.string().describe("Version found in exported project's package.json."),
2842
+ userVersion: z4.string().describe("Version currently present in user's package.json.")
1921
2843
  });
1922
2844
  var pageDetailsSchema = pageSchema.extend({
1923
- pageInstruction: z3.string().nullable().describe(
2845
+ pageInstruction: z4.string().nullable().describe(
1924
2846
  "Current instruction text for the active page variant. Null when no active variant is set."
1925
2847
  )
1926
2848
  });
1927
- var pageVariantSchema = z3.object({
1928
- id: z3.string().describe("Active variant identifier."),
1929
- name: z3.string().describe("Active variant display name.")
2849
+ var pageVariantSchema = z4.object({
2850
+ id: z4.string().describe("Active variant identifier."),
2851
+ name: z4.string().describe("Active variant display name.")
1930
2852
  }).nullable().describe(
1931
2853
  "Active page variant. Null when page has no active variant and components are unavailable."
1932
2854
  );
1933
- var pageComponentSchema = z3.object({
1934
- blockId: z3.string().describe("Stable block identifier. Use as primary component instance ID."),
1935
- componentId: z3.string().describe("Component key from variant blocks payload (block.key)."),
1936
- componentGroup: z3.string().describe("Component family/group normalized from component key."),
1937
- slot: z3.string().nullable().describe("Optional slot value from block payload."),
1938
- props: z3.record(z3.unknown()).nullable().describe("Raw block props payload from PrimeUI.")
2855
+ var pageComponentSchema = z4.object({
2856
+ blockId: z4.string().describe("Stable block identifier. Use as primary component instance ID."),
2857
+ componentId: z4.string().describe("Component key from variant blocks payload (block.key)."),
2858
+ componentGroup: z4.string().describe("Component family/group normalized from component key."),
2859
+ slot: z4.string().nullable().describe("Optional slot value from block payload."),
2860
+ props: z4.record(z4.unknown()).nullable().describe("Raw block props payload from PrimeUI.")
1939
2861
  });
1940
- var inspectPageReportRowSchema = z3.object({
1941
- number: z3.number().describe("1-based row number matching report order."),
1942
- componentGroup: z3.string().describe("Component family/group label."),
1943
- componentId: z3.string().describe("Component key (block key)."),
1944
- blockId: z3.string().describe("Primary component instance identifier for future operations."),
1945
- title: z3.string().nullable().describe("Title extracted by MCP from component props."),
1946
- description: z3.string().nullable().describe("Description extracted by MCP from component props.")
2862
+ var inspectPageReportRowSchema = z4.object({
2863
+ number: z4.number().describe("1-based row number matching report order."),
2864
+ componentGroup: z4.string().describe("Component family/group label."),
2865
+ componentId: z4.string().describe("Component key (block key)."),
2866
+ blockId: z4.string().describe("Primary component instance identifier for future operations."),
2867
+ title: z4.string().nullable().describe("Title extracted by MCP from component props."),
2868
+ description: z4.string().nullable().describe("Description extracted by MCP from component props.")
1947
2869
  });
1948
- var projectRootInputSchema = z3.string().optional().describe(
2870
+ var projectRootInputSchema = z4.string().optional().describe(
1949
2871
  "Optional absolute project root path. Use when .primeui/project.json cannot be auto-resolved from current working directory."
1950
2872
  );
1951
- var optionalProjectRootInputObjectSchema = z3.object({
2873
+ var optionalProjectRootInputObjectSchema = z4.object({
1952
2874
  projectRoot: projectRootInputSchema
1953
2875
  }).optional();
1954
2876
  var TRIGGER_PHRASES = [
@@ -2032,17 +2954,17 @@ AFTER CALLING:
2032
2954
 
2033
2955
  ${WORKFLOW_SUMMARY}`,
2034
2956
  inputSchema: optionalProjectRootInputObjectSchema,
2035
- outputSchema: z3.object({
2036
- projectId: z3.string().describe(
2957
+ outputSchema: z4.object({
2958
+ projectId: z4.string().describe(
2037
2959
  "PrimeUI project identifier associated with the API key and current import session context."
2038
2960
  ),
2039
- projectName: z3.string().describe(
2961
+ projectName: z4.string().describe(
2040
2962
  "Human-readable PrimeUI project name for confirmations in chat."
2041
2963
  ),
2042
- metadata: z3.record(z3.unknown()).describe(
2964
+ metadata: z4.record(z4.unknown()).describe(
2043
2965
  "Additional project metadata from PrimeUI. May include project-description and other context fields."
2044
2966
  ),
2045
- pages: z3.array(pageSchema).describe(
2967
+ pages: z4.array(pageSchema).describe(
2046
2968
  "All pages visible for this project context. Filter by user request and isReadyToExport before creating export."
2047
2969
  )
2048
2970
  }),
@@ -2073,22 +2995,22 @@ If page has no active variant, components is null (not empty array), and report
2073
2995
  ${WORKFLOW_SUMMARY}`,
2074
2996
  inputSchema: {
2075
2997
  projectRoot: projectRootInputSchema,
2076
- pageSlug: z3.string().describe(
2998
+ pageSlug: z4.string().describe(
2077
2999
  "PrimeUI page slug to inspect, for example '/', '/pricing', '/docs/getting-started'."
2078
3000
  )
2079
3001
  },
2080
- outputSchema: z3.object({
3002
+ outputSchema: z4.object({
2081
3003
  page: pageDetailsSchema.describe(
2082
3004
  "Single PrimeUI page details resolved by slug with pageInstruction."
2083
3005
  ),
2084
3006
  variant: pageVariantSchema,
2085
- components: z3.array(pageComponentSchema).nullable().describe(
3007
+ components: z4.array(pageComponentSchema).nullable().describe(
2086
3008
  "Raw component instances from active variant. Null when page has no active variant."
2087
3009
  ),
2088
- report: z3.string().describe(
3010
+ report: z4.string().describe(
2089
3011
  "Formatted report table generated by MCP from component payload (includes extracted title/description summary)."
2090
3012
  ),
2091
- reportRows: z3.array(inspectPageReportRowSchema).describe(
3013
+ reportRows: z4.array(inspectPageReportRowSchema).describe(
2092
3014
  "Structured report rows with 1-based numbering and extracted title/description fields."
2093
3015
  )
2094
3016
  }),
@@ -2114,8 +3036,8 @@ Do NOT use this tool as a substitute for create_export. Always create a fresh ex
2114
3036
 
2115
3037
  ${WORKFLOW_SUMMARY}`,
2116
3038
  inputSchema: optionalProjectRootInputObjectSchema,
2117
- outputSchema: z3.object({
2118
- exports: z3.array(exportItemSchema).describe(
3039
+ outputSchema: z4.object({
3040
+ exports: z4.array(exportItemSchema).describe(
2119
3041
  "Available exports sorted by API policy. Use item.id to download a specific prior export if user asks."
2120
3042
  )
2121
3043
  }),
@@ -2142,24 +3064,24 @@ AFTER CALLING:
2142
3064
 
2143
3065
  ${WORKFLOW_SUMMARY}`,
2144
3066
  inputSchema: optionalProjectRootInputObjectSchema,
2145
- outputSchema: z3.object({
2146
- export: z3.object({
2147
- id: z3.string().describe(
3067
+ outputSchema: z4.object({
3068
+ export: z4.object({
3069
+ id: z4.string().describe(
2148
3070
  "Freshly created export identifier. Use this as download_export input.id."
2149
3071
  ),
2150
3072
  status: exportStatusSchema2,
2151
- createdAt: z3.string().describe("Export creation timestamp in ISO-8601 UTC format."),
2152
- expiresAt: z3.string().nullable().describe(
3073
+ createdAt: z4.string().describe("Export creation timestamp in ISO-8601 UTC format."),
3074
+ expiresAt: z4.string().nullable().describe(
2153
3075
  "Export expiration timestamp in ISO-8601 UTC format, or null when export has no explicit expiration."
2154
3076
  ),
2155
3077
  summary: exportSummarySchema2.describe(
2156
3078
  "Export run summary with total/successful/failed counters."
2157
3079
  ),
2158
- components: z3.array(exportComponentSchema).describe(
3080
+ components: z4.array(exportComponentSchema).describe(
2159
3081
  "Global component export manifest. These components are independent from page-level manifests."
2160
3082
  )
2161
3083
  }).describe("Created export summary."),
2162
- pages: z3.array(exportPageSchema2).describe(
3084
+ pages: z4.array(exportPageSchema2).describe(
2163
3085
  "Strict page payload associated with this export, including per-page manifest files list."
2164
3086
  )
2165
3087
  }),
@@ -2193,21 +3115,21 @@ AFTER DOWNLOAD:
2193
3115
  ${WORKFLOW_SUMMARY}`,
2194
3116
  inputSchema: {
2195
3117
  projectRoot: projectRootInputSchema,
2196
- id: z3.string().describe(
3118
+ id: z4.string().describe(
2197
3119
  "Export identifier to download. Prefer export.id from create_export; use list_exports only on explicit user request."
2198
3120
  )
2199
3121
  },
2200
- outputSchema: z3.object({
2201
- exportId: z3.string().describe(
3122
+ outputSchema: z4.object({
3123
+ exportId: z4.string().describe(
2202
3124
  "Downloaded export identifier. Should match the requested input.id for traceability."
2203
3125
  ),
2204
- projectPath: z3.string().describe(
3126
+ projectPath: z4.string().describe(
2205
3127
  "Absolute local path to extracted export root in .primeui/temp/exports/[exportId]. Read files from here only."
2206
3128
  ),
2207
- manifestPath: z3.string().describe(
3129
+ manifestPath: z4.string().describe(
2208
3130
  "Absolute path to sidecar export manifest file (.primeui/temp/exports/[exportId].manifest.json) saved by create_export and required by copy_page."
2209
3131
  ),
2210
- pages: z3.array(exportPageSchema2).describe(
3132
+ pages: z4.array(exportPageSchema2).describe(
2211
3133
  "Page descriptors loaded from local sidecar export manifest for import decisions."
2212
3134
  )
2213
3135
  }),
@@ -2243,46 +3165,46 @@ BEHAVIOR:
2243
3165
  ${WORKFLOW_SUMMARY}`,
2244
3166
  inputSchema: {
2245
3167
  projectRoot: projectRootInputSchema,
2246
- originPageSlug: z3.string().describe(
3168
+ originPageSlug: z4.string().describe(
2247
3169
  "Source page slug from PrimeUI project pages list, for example '/', '/pricing', '/docs'."
2248
3170
  ),
2249
- actualPageSlug: z3.string().optional().describe(
3171
+ actualPageSlug: z4.string().optional().describe(
2250
3172
  "Optional destination slug in user's project. If omitted, originPageSlug is used."
2251
3173
  )
2252
3174
  },
2253
- outputSchema: z3.object({
2254
- exportId: z3.string().describe("Resolved local export identifier used for copy operation."),
2255
- exportPath: z3.string().describe("Absolute path to local extracted export project root."),
2256
- originPageSlug: z3.string().describe("Normalized source page slug requested for copy."),
2257
- actualPageSlug: z3.string().describe("Normalized destination slug used for target route paths."),
2258
- sourcePagePath: z3.string().describe("Source page path relative to export root."),
2259
- targetPagePath: z3.string().describe("Destination page path relative to user project root."),
2260
- sourceComponentsPath: z3.string().describe("Source components directory relative to export root."),
2261
- targetComponentsPath: z3.string().describe(
3175
+ outputSchema: z4.object({
3176
+ exportId: z4.string().describe("Resolved local export identifier used for copy operation."),
3177
+ exportPath: z4.string().describe("Absolute path to local extracted export project root."),
3178
+ originPageSlug: z4.string().describe("Normalized source page slug requested for copy."),
3179
+ actualPageSlug: z4.string().describe("Normalized destination slug used for target route paths."),
3180
+ sourcePagePath: z4.string().describe("Source page path relative to export root."),
3181
+ targetPagePath: z4.string().describe("Destination page path relative to user project root."),
3182
+ sourceComponentsPath: z4.string().describe("Source components directory relative to export root."),
3183
+ targetComponentsPath: z4.string().describe(
2262
3184
  "Destination components directory relative to user project root."
2263
3185
  ),
2264
- newFiles: z3.array(fileTransferSchema).describe("New files copied into user project without conflicts."),
2265
- identicalFiles: z3.array(fileTransferSchema).describe(
3186
+ newFiles: z4.array(fileTransferSchema).describe("New files copied into user project without conflicts."),
3187
+ identicalFiles: z4.array(fileTransferSchema).describe(
2266
3188
  "Existing files in user project that are byte-identical to export files."
2267
3189
  ),
2268
- conflictFiles: z3.array(conflictFileSchema).describe(
3190
+ conflictFiles: z4.array(conflictFileSchema).describe(
2269
3191
  "Files that already exist and differ from export version. Never overwritten."
2270
3192
  ),
2271
- addedDependencies: z3.array(dependencyToAddSchema).describe(
3193
+ addedDependencies: z4.array(dependencyToAddSchema).describe(
2272
3194
  "Dependencies that were missing and have been added to user's package.json."
2273
3195
  ),
2274
- dependenciesVersionConflicts: z3.array(dependencyVersionConflictSchema).describe(
3196
+ dependenciesVersionConflicts: z4.array(dependencyVersionConflictSchema).describe(
2275
3197
  "Dependencies with version mismatch between export and user project. Report only."
2276
3198
  ),
2277
- summary: z3.object({
2278
- totalCandidateFiles: z3.number().describe(
3199
+ summary: z4.object({
3200
+ totalCandidateFiles: z4.number().describe(
2279
3201
  "Total number of candidate files considered for page copy from pages[].manifest.files."
2280
3202
  ),
2281
- copiedFiles: z3.number().describe("Number of files copied as new."),
2282
- identicalFiles: z3.number().describe("Number of files detected as identical."),
2283
- conflictFiles: z3.number().describe("Number of conflicting files not copied."),
2284
- addedDependencies: z3.number().describe("Count of dependencies added to package.json."),
2285
- dependenciesVersionConflicts: z3.number().describe("Count of dependency version mismatches reported.")
3203
+ copiedFiles: z4.number().describe("Number of files copied as new."),
3204
+ identicalFiles: z4.number().describe("Number of files detected as identical."),
3205
+ conflictFiles: z4.number().describe("Number of conflicting files not copied."),
3206
+ addedDependencies: z4.number().describe("Count of dependencies added to package.json."),
3207
+ dependenciesVersionConflicts: z4.number().describe("Count of dependency version mismatches reported.")
2286
3208
  })
2287
3209
  }),
2288
3210
  annotations: {
@@ -2304,8 +3226,8 @@ Safe to call multiple times. Only affects .primeui/temp/ directory - never touch
2304
3226
 
2305
3227
  ${WORKFLOW_SUMMARY}`,
2306
3228
  inputSchema: optionalProjectRootInputObjectSchema,
2307
- outputSchema: z3.object({
2308
- success: z3.boolean().describe(
3229
+ outputSchema: z4.object({
3230
+ success: z4.boolean().describe(
2309
3231
  "True when temp cleanup completed and baseline temp structure was recreated."
2310
3232
  )
2311
3233
  }),
@@ -2364,7 +3286,7 @@ function createPrimeUiMcpServer(source) {
2364
3286
  const server = new McpServer(
2365
3287
  {
2366
3288
  name: "primeui-mcp-server",
2367
- version: "0.1.0"
3289
+ version: packageMetadata.version
2368
3290
  },
2369
3291
  {
2370
3292
  instructions: initialInstructions
@@ -2482,12 +3404,28 @@ function createPrimeUiMcpServer(source) {
2482
3404
  }
2483
3405
 
2484
3406
  // src/service.ts
2485
- async function main() {
3407
+ async function startPrimeUiMcpServer() {
2486
3408
  const source = new LazyProjectSyncSource();
2487
3409
  const server = createPrimeUiMcpServer(source);
2488
3410
  const transport = new StdioServerTransport();
2489
3411
  await server.connect(transport);
2490
3412
  }
3413
+ async function main() {
3414
+ const exitCode = await runCli(process.argv.slice(2), {
3415
+ stdout: process.stdout,
3416
+ stderr: process.stderr,
3417
+ startServer: startPrimeUiMcpServer,
3418
+ runHealthCheck: ({ projectRoot }) => runHealthCheck({
3419
+ projectRoot,
3420
+ stdout: process.stdout,
3421
+ env: process.env,
3422
+ cwd: process.cwd()
3423
+ })
3424
+ });
3425
+ if (exitCode !== 0) {
3426
+ process.exitCode = exitCode;
3427
+ }
3428
+ }
2491
3429
  main().catch((error) => {
2492
3430
  console.error("[primeui-mcp] failed to start", error);
2493
3431
  process.exit(1);