@primeuicom/mcp 0.1.30 → 1.1.0

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
@@ -85,7 +85,9 @@ function formatHelpText(metadata = packageMetadata) {
85
85
  " PRIMEUI_PROJECT_ROOT Override project root used to resolve .primeui/project.json.",
86
86
  "",
87
87
  "Project config:",
88
- " .primeui/project.json must include projectId, apiKey, and targetProjectPath."
88
+ " .primeui/project.json must include projectId and apiKey.",
89
+ " targetProjectPath is required for file-oriented import/export flows.",
90
+ " targetProjectPath is optional for the additional atomic external API tools."
89
91
  ].join("\n");
90
92
  }
91
93
  function formatInvalidHealthPathError(projectRoot, metadata = packageMetadata) {
@@ -227,8 +229,17 @@ function buildProjectRootHint() {
227
229
  var primeUiProjectConfigSchema = z2.object({
228
230
  projectId: z2.string().trim().min(1),
229
231
  apiKey: z2.string().trim().min(1),
230
- targetProjectPath: z2.string().trim().regex(/^\.\/.*$/)
232
+ targetProjectPath: z2.string().trim().regex(/^\.\/.*$/).optional()
231
233
  }).passthrough();
234
+ var primeUiProjectConfigWithTargetPathSchema = primeUiProjectConfigSchema.superRefine((value, ctx) => {
235
+ if (!value.targetProjectPath) {
236
+ ctx.addIssue({
237
+ code: z2.ZodIssueCode.custom,
238
+ path: ["targetProjectPath"],
239
+ message: "Required"
240
+ });
241
+ }
242
+ });
232
243
  async function fileExists(filePath) {
233
244
  try {
234
245
  const fileStats = await stat(filePath);
@@ -263,7 +274,10 @@ async function findPrimeUiProjectConfigPath(startPath) {
263
274
  }
264
275
  return void 0;
265
276
  }
266
- async function readPrimeUiProjectConfig(projectConfigPath) {
277
+ async function readPrimeUiProjectConfig(projectConfigPath, options = {
278
+ requireTargetProjectPath: true
279
+ }) {
280
+ const requireTargetProjectPath = options.requireTargetProjectPath !== false;
267
281
  let projectConfigRaw;
268
282
  try {
269
283
  projectConfigRaw = await readFile(projectConfigPath, "utf-8");
@@ -290,7 +304,7 @@ async function readPrimeUiProjectConfig(projectConfigPath) {
290
304
  hint: buildProjectRootHint()
291
305
  });
292
306
  }
293
- const parsedConfig = primeUiProjectConfigSchema.safeParse(projectConfigJson);
307
+ const parsedConfig = (requireTargetProjectPath ? primeUiProjectConfigWithTargetPathSchema : primeUiProjectConfigSchema).safeParse(projectConfigJson);
294
308
  if (!parsedConfig.success) {
295
309
  const details = parsedConfig.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
296
310
  throw new PrimeUiProjectConfigError({
@@ -320,10 +334,13 @@ function withConfigSourceDetails(error, context) {
320
334
  }
321
335
  });
322
336
  }
323
- async function loadResolvedPrimeUiProjectConfig(context) {
337
+ async function loadResolvedPrimeUiProjectConfig(context, options) {
324
338
  try {
325
339
  const projectConfig = await readPrimeUiProjectConfig(
326
- context.projectConfigPath
340
+ context.projectConfigPath,
341
+ {
342
+ requireTargetProjectPath: options.requireTargetProjectPath
343
+ }
327
344
  );
328
345
  return {
329
346
  projectRoot: context.projectRoot,
@@ -349,7 +366,9 @@ function toUniqueResolvedDirs(directories) {
349
366
  function buildPrimeUiProjectSearchRoots(options) {
350
367
  return toUniqueResolvedDirs([...options.fallbackCwds ?? [], options.cwd]);
351
368
  }
352
- async function resolvePrimeUiProjectConfig(options) {
369
+ async function resolvePrimeUiProjectConfig(options, configOptions = {
370
+ requireTargetProjectPath: true
371
+ }) {
353
372
  const toolProjectRoot = options.projectRootFromTool?.trim();
354
373
  if (toolProjectRoot) {
355
374
  const resolvedToolRoot = path3.resolve(toolProjectRoot);
@@ -370,11 +389,14 @@ async function resolvePrimeUiProjectConfig(options) {
370
389
  hint: buildProjectRootHint()
371
390
  });
372
391
  }
373
- return loadResolvedPrimeUiProjectConfig({
374
- source: "tool",
375
- projectRoot: resolvedToolRoot,
376
- projectConfigPath: toolProjectConfigPath
377
- });
392
+ return loadResolvedPrimeUiProjectConfig(
393
+ {
394
+ source: "tool",
395
+ projectRoot: resolvedToolRoot,
396
+ projectConfigPath: toolProjectConfigPath
397
+ },
398
+ configOptions
399
+ );
378
400
  }
379
401
  const stickyProjectRoot = options.projectRootFromSticky?.trim();
380
402
  if (stickyProjectRoot) {
@@ -384,11 +406,14 @@ async function resolvePrimeUiProjectConfig(options) {
384
406
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
385
407
  );
386
408
  if (await fileExists(stickyProjectConfigPath)) {
387
- return loadResolvedPrimeUiProjectConfig({
388
- source: "sticky",
389
- projectRoot: resolvedStickyRoot,
390
- projectConfigPath: stickyProjectConfigPath
391
- });
409
+ return loadResolvedPrimeUiProjectConfig(
410
+ {
411
+ source: "sticky",
412
+ projectRoot: resolvedStickyRoot,
413
+ projectConfigPath: stickyProjectConfigPath
414
+ },
415
+ configOptions
416
+ );
392
417
  }
393
418
  }
394
419
  const envProjectRoot = options.projectRootFromEnv?.trim();
@@ -412,11 +437,14 @@ async function resolvePrimeUiProjectConfig(options) {
412
437
  hint: buildProjectRootHint()
413
438
  });
414
439
  }
415
- return loadResolvedPrimeUiProjectConfig({
416
- source: "env",
417
- projectRoot: resolvedEnvRoot,
418
- projectConfigPath: envProjectConfigPath
419
- });
440
+ return loadResolvedPrimeUiProjectConfig(
441
+ {
442
+ source: "env",
443
+ projectRoot: resolvedEnvRoot,
444
+ projectConfigPath: envProjectConfigPath
445
+ },
446
+ configOptions
447
+ );
420
448
  }
421
449
  const searchRoots = buildPrimeUiProjectSearchRoots({
422
450
  cwd: options.cwd,
@@ -445,11 +473,14 @@ async function resolvePrimeUiProjectConfig(options) {
445
473
  });
446
474
  }
447
475
  const projectRoot = path3.dirname(path3.dirname(projectConfigPath));
448
- return loadResolvedPrimeUiProjectConfig({
449
- source: "search",
450
- projectRoot,
451
- projectConfigPath
452
- });
476
+ return loadResolvedPrimeUiProjectConfig(
477
+ {
478
+ source: "search",
479
+ projectRoot,
480
+ projectConfigPath
481
+ },
482
+ configOptions
483
+ );
453
484
  }
454
485
  function resolvePrimeUiApiKeyDetails(options) {
455
486
  const envApiKey = options.apiKeyFromEnv?.trim();
@@ -465,6 +496,16 @@ function resolvePrimeUiApiKeyDetails(options) {
465
496
  };
466
497
  }
467
498
  function resolvePrimeUiTargetProjectRoot(projectRoot, projectConfig) {
499
+ if (!projectConfig.targetProjectPath) {
500
+ throw new PrimeUiProjectConfigError({
501
+ code: "PROJECT_CONFIG_INVALID_FORMAT",
502
+ message: `[primeui-mcp] invalid ${PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH} format: targetProjectPath: Required`,
503
+ details: {
504
+ projectRoot
505
+ },
506
+ hint: buildProjectRootHint()
507
+ });
508
+ }
468
509
  return path3.resolve(projectRoot, projectConfig.targetProjectPath);
469
510
  }
470
511
  async function resolvePrimeUiApiKey(options) {
@@ -562,6 +603,57 @@ var primeUiProjectPageDetailsSchema = z3.object({
562
603
  }).nullable(),
563
604
  components: z3.array(primeUiProjectPageComponentSchema).nullable()
564
605
  });
606
+ var primeUiProjectDescriptionSchema = z3.object({
607
+ projectDescription: z3.string()
608
+ });
609
+ var primeUiPageGenerationTypeSchema = z3.enum([
610
+ "landing",
611
+ "pricing",
612
+ "blogIndex",
613
+ "blogPost",
614
+ "contact-us",
615
+ "docs",
616
+ "legal"
617
+ ]);
618
+ var primeUiProjectApiWireframeStatusSchema = z3.enum(["pending", "generating", "ready", "error"]);
619
+ var primeUiProjectApiVariantSummarySchema = z3.object({
620
+ id: z3.string(),
621
+ name: z3.string(),
622
+ isActive: z3.boolean(),
623
+ isGenerating: z3.boolean()
624
+ });
625
+ var primeUiProjectApiPageResourceSchema = z3.object({
626
+ id: z3.string(),
627
+ title: z3.string(),
628
+ slug: z3.string(),
629
+ pageType: primeUiPageGenerationTypeSchema,
630
+ isReadyToExport: z3.boolean(),
631
+ wireframeStatus: primeUiProjectApiWireframeStatusSchema,
632
+ variants: z3.array(primeUiProjectApiVariantSummarySchema)
633
+ });
634
+ var primeUiProjectApiPagesResponseSchema = z3.object({
635
+ pages: z3.array(primeUiProjectApiPageResourceSchema)
636
+ });
637
+ var primeUiProjectApiVariantsResponseSchema = z3.object({
638
+ variants: z3.array(primeUiProjectApiVariantSummarySchema)
639
+ });
640
+ var primeUiProjectApiVariantResourceSchema = z3.object({
641
+ id: z3.string(),
642
+ name: z3.string(),
643
+ isActive: z3.boolean(),
644
+ isGenerating: z3.boolean(),
645
+ components: z3.array(primeUiProjectPageComponentSchema)
646
+ });
647
+ var primeUiProjectApiVariantSummaryResponseSchema = z3.object({
648
+ variant: primeUiProjectApiVariantSummarySchema
649
+ });
650
+ var primeUiProjectApiVariantResponseSchema = z3.object({
651
+ variant: primeUiProjectApiVariantResourceSchema
652
+ });
653
+ var primeUiIssueReportSubmitResponseSchema = z3.object({
654
+ accepted: z3.literal(true),
655
+ ticketId: z3.string()
656
+ });
565
657
  var primeUiExportsResponseSchema = z3.object({
566
658
  exports: z3.array(
567
659
  z3.object({
@@ -721,6 +813,75 @@ var ApiProjectDataProvider = class {
721
813
  async getProjectInfo() {
722
814
  return this.requestJson("project", primeUiProjectInfoSchema);
723
815
  }
816
+ async getProjectDescription() {
817
+ return this.requestJson(
818
+ "project/description",
819
+ primeUiProjectDescriptionSchema
820
+ );
821
+ }
822
+ async upsertProjectDescription(projectDescription) {
823
+ return this.requestJson(
824
+ "project/description",
825
+ primeUiProjectDescriptionSchema,
826
+ this.buildJsonRequestInit({
827
+ projectDescription
828
+ })
829
+ );
830
+ }
831
+ async listProjectPages() {
832
+ return this.requestJson(
833
+ "project/pages",
834
+ primeUiProjectApiPagesResponseSchema
835
+ );
836
+ }
837
+ async getProjectPage(pageId) {
838
+ return this.requestJson(
839
+ `project/pages/${encodeURIComponent(pageId)}`,
840
+ primeUiProjectApiPagesResponseSchema
841
+ );
842
+ }
843
+ async createProjectPage(input) {
844
+ return this.requestJson(
845
+ "project/pages",
846
+ primeUiProjectApiPagesResponseSchema,
847
+ this.buildJsonRequestInit(input)
848
+ );
849
+ }
850
+ async listProjectPageVariants(pageId) {
851
+ return this.requestJson(
852
+ `project/pages/${encodeURIComponent(pageId)}/variants`,
853
+ primeUiProjectApiVariantsResponseSchema
854
+ );
855
+ }
856
+ async getProjectPageVariant(pageId, variantId) {
857
+ return this.requestJson(
858
+ `project/pages/${encodeURIComponent(pageId)}/variants/${encodeURIComponent(variantId)}`,
859
+ primeUiProjectApiVariantResponseSchema
860
+ );
861
+ }
862
+ async createProjectPageVariant(pageId, input) {
863
+ return this.requestJson(
864
+ `project/pages/${encodeURIComponent(pageId)}/variants`,
865
+ primeUiProjectApiVariantSummaryResponseSchema,
866
+ this.buildJsonRequestInit(input)
867
+ );
868
+ }
869
+ async setProjectPageActiveVariant(pageId, variantId) {
870
+ return this.requestJson(
871
+ `project/pages/${encodeURIComponent(pageId)}/active-variant`,
872
+ primeUiProjectApiPagesResponseSchema,
873
+ this.buildJsonRequestInit({
874
+ variantId
875
+ })
876
+ );
877
+ }
878
+ async submitIssueReport(input) {
879
+ return this.requestJson(
880
+ "issue-reports",
881
+ primeUiIssueReportSubmitResponseSchema,
882
+ this.buildJsonRequestInit(input)
883
+ );
884
+ }
724
885
  async getProjectPageBySlug(slug) {
725
886
  const encodedSlug = encodeURIComponent(slug);
726
887
  return this.requestJson(
@@ -790,6 +951,15 @@ var ApiProjectDataProvider = class {
790
951
  buildUrl(endpoint) {
791
952
  return buildPrimeUiApiUrl(this.apiRoot, endpoint);
792
953
  }
954
+ buildJsonRequestInit(payload) {
955
+ return {
956
+ method: "POST",
957
+ headers: {
958
+ "Content-Type": "application/json"
959
+ },
960
+ body: JSON.stringify(payload)
961
+ };
962
+ }
793
963
  async requestJson(endpoint, schema, requestInit = {}) {
794
964
  const apiKey = this.requireApiKey();
795
965
  const method = requestInit.method ?? "GET";
@@ -1083,9 +1253,19 @@ async function findNestedPrimeUiCandidates(rootPath) {
1083
1253
  }
1084
1254
 
1085
1255
  // src/health/report.ts
1086
- function renderValue(value, fallback = "missing") {
1256
+ function renderValue(value, fallback = "not set") {
1087
1257
  return value?.trim() ? value : fallback;
1088
1258
  }
1259
+ function renderPresenceState(value) {
1260
+ switch (value) {
1261
+ case "set":
1262
+ return "set";
1263
+ case "missing":
1264
+ return "not set";
1265
+ case "unknown":
1266
+ return "unknown";
1267
+ }
1268
+ }
1089
1269
  function renderProjectRootComparison(comparison) {
1090
1270
  switch (comparison) {
1091
1271
  case "same":
@@ -1144,7 +1324,7 @@ function formatHealthReport(report) {
1144
1324
  `health input path: ${renderValue(report.runtimeContext.inputProjectRoot)}`,
1145
1325
  `PRIMEUI_PROJECT_ROOT: ${renderValue(report.runtimeContext.envProjectRoot)}`,
1146
1326
  `PRIMEUI_API_BASE_URL: ${renderValue(report.runtimeContext.envApiBaseUrl)}`,
1147
- `PRIMEUI_API_KEY: ${report.runtimeContext.envApiKeySet ? "set" : "missing"}`
1327
+ `PRIMEUI_API_KEY: ${report.runtimeContext.envApiKeySet ? "set" : "not set"}`
1148
1328
  ]),
1149
1329
  "",
1150
1330
  formatSection("Source Resolution", [
@@ -1154,8 +1334,8 @@ function formatHealthReport(report) {
1154
1334
  report.sourceResolution.projectRoot.comparison
1155
1335
  )}`,
1156
1336
  `effective project root source: ${report.sourceResolution.projectRoot.effectiveSource}`,
1157
- `env API key: ${report.sourceResolution.apiKey.envState}`,
1158
- `config API key: ${report.sourceResolution.apiKey.configState}`,
1337
+ `env API key: ${renderPresenceState(report.sourceResolution.apiKey.envState)}`,
1338
+ `config API key: ${renderPresenceState(report.sourceResolution.apiKey.configState)}`,
1159
1339
  `API key comparison: ${renderApiKeyComparison(
1160
1340
  report.sourceResolution.apiKey.comparison
1161
1341
  )}`,
@@ -1545,12 +1725,12 @@ async function runHealthCheck(options) {
1545
1725
  }
1546
1726
 
1547
1727
  // src/services/project-sync-service.ts
1548
- import path10 from "path";
1549
- import { readFile as readFile4 } from "fs/promises";
1728
+ import path13 from "path";
1729
+ import { readFile as readFile7 } from "fs/promises";
1550
1730
 
1551
1731
  // src/services/page-copy-service.ts
1552
- import { readFile as readFile3, readdir as readdir2, stat as stat5, writeFile as writeFile2 } from "fs/promises";
1553
- import path9 from "path";
1732
+ import { readFile as readFile4, stat as stat6, writeFile as writeFile2 } from "fs/promises";
1733
+ import path10 from "path";
1554
1734
 
1555
1735
  // src/lib/import-graph.ts
1556
1736
  import { readFile as readFile2, stat as stat4 } from "fs/promises";
@@ -1830,20 +2010,14 @@ function buildUnifiedDiff(input) {
1830
2010
  ... [diff truncated]`;
1831
2011
  }
1832
2012
 
1833
- // src/services/page-copy-service.ts
2013
+ // src/services/copy-service-shared.ts
2014
+ import { readFile as readFile3, readdir as readdir2, stat as stat5 } from "fs/promises";
2015
+ import path9 from "path";
1834
2016
  var DEPENDENCY_SECTIONS = [
1835
2017
  "dependencies",
1836
2018
  "devDependencies",
1837
2019
  "peerDependencies"
1838
2020
  ];
1839
- function normalizeSlug(slug) {
1840
- const trimmed = slug.trim();
1841
- if (!trimmed || trimmed === "/") {
1842
- return "/";
1843
- }
1844
- const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
1845
- return withLeadingSlash.replace(/\/+$/, "") || "/";
1846
- }
1847
2021
  function toPosixPath(value) {
1848
2022
  return value.split(path9.sep).join("/");
1849
2023
  }
@@ -1883,11 +2057,12 @@ function parseManifest(value) {
1883
2057
  }
1884
2058
  async function resolveManifestCandidateFiles(input) {
1885
2059
  const result = /* @__PURE__ */ new Set();
2060
+ const requireExisting = input.requireExisting ?? true;
1886
2061
  for (const rawPath of input.files) {
1887
2062
  const trimmed = rawPath.trim();
1888
2063
  if (!trimmed) {
1889
2064
  throw new Error(
1890
- `Export manifest for page "${input.pageSlug}" contains an empty file path.`
2065
+ `Export manifest for ${input.manifestOwnerLabel} contains an empty file path.`
1891
2066
  );
1892
2067
  }
1893
2068
  const normalizedRelative = toPosixPath(trimmed).replace(/^\.\/+/, "");
@@ -1895,14 +2070,16 @@ async function resolveManifestCandidateFiles(input) {
1895
2070
  const relative = path9.relative(input.exportPath, absolutePath);
1896
2071
  if (relative.startsWith("..") || path9.isAbsolute(relative)) {
1897
2072
  throw new Error(
1898
- `Export manifest for page "${input.pageSlug}" contains path outside export root: ${trimmed}`
2073
+ `Export manifest for ${input.manifestOwnerLabel} contains path outside export root: ${trimmed}`
1899
2074
  );
1900
2075
  }
1901
- const stats = await stat5(absolutePath).catch(() => null);
1902
- if (!stats?.isFile()) {
1903
- throw new Error(
1904
- `Export manifest file is missing for page "${input.pageSlug}": ${normalizedRelative}`
1905
- );
2076
+ if (requireExisting) {
2077
+ const stats = await stat5(absolutePath).catch(() => null);
2078
+ if (!stats?.isFile()) {
2079
+ throw new Error(
2080
+ `Export manifest file is missing for ${input.manifestOwnerLabel}: ${normalizedRelative}`
2081
+ );
2082
+ }
1906
2083
  }
1907
2084
  result.add(absolutePath);
1908
2085
  }
@@ -1977,6 +2154,86 @@ function sortDependencySections(packageJson) {
1977
2154
  packageJson[section] = sorted;
1978
2155
  }
1979
2156
  }
2157
+ async function resolveDependencyRequirements(input) {
2158
+ const exportPackageJson = await readJsonFile(
2159
+ input.exportPackageJsonPath
2160
+ );
2161
+ const userPackageJson = await readJsonFile(
2162
+ input.userPackageJsonPath
2163
+ );
2164
+ const missedDependencies = [];
2165
+ const dependenciesVersionConflicts = [];
2166
+ for (const packageName of new Set(input.requiredPackageNames)) {
2167
+ const exportDependency = getDependencyVersion(
2168
+ exportPackageJson,
2169
+ packageName
2170
+ );
2171
+ if (!exportDependency) {
2172
+ continue;
2173
+ }
2174
+ const userDependency = getDependencyVersion(userPackageJson, packageName);
2175
+ if (!userDependency) {
2176
+ missedDependencies.push({
2177
+ packageName,
2178
+ version: exportDependency.version,
2179
+ section: exportDependency.section
2180
+ });
2181
+ continue;
2182
+ }
2183
+ if (userDependency.version !== exportDependency.version) {
2184
+ dependenciesVersionConflicts.push({
2185
+ packageName,
2186
+ section: exportDependency.section,
2187
+ exportVersion: exportDependency.version,
2188
+ userVersion: userDependency.version
2189
+ });
2190
+ }
2191
+ }
2192
+ return {
2193
+ missedDependencies: missedDependencies.sort(
2194
+ (a, b) => a.packageName.localeCompare(b.packageName)
2195
+ ),
2196
+ dependenciesVersionConflicts: dependenciesVersionConflicts.sort(
2197
+ (a, b) => a.packageName.localeCompare(b.packageName)
2198
+ )
2199
+ };
2200
+ }
2201
+
2202
+ // src/services/runtime-guidance.ts
2203
+ var EXPORT_MANIFEST_DESCRIPTION = "Local sidecar export manifest. This is the authoritative file-level contract for the selected transfer item. Files listed per page or exportable are functionality-critical dependencies for that item.";
2204
+ var REFERENCE_EXPORT_DESCRIPTION = "Downloaded export project. Use this as the golden example for working behavior, file relationships, and integration details when the local result diverges or implementation is unclear.";
2205
+ var CONFLICT_REPORT_DESCRIPTION = "Local diff report for files that were not copied because target files already differ. These conflicts represent required imported functionality and must be reviewed before the transfer is complete.";
2206
+ function buildExportManifestArtifact(manifestPath) {
2207
+ return {
2208
+ kind: "export_manifest",
2209
+ path: manifestPath,
2210
+ description: EXPORT_MANIFEST_DESCRIPTION
2211
+ };
2212
+ }
2213
+ function buildReferenceExportArtifact(exportPath) {
2214
+ return {
2215
+ kind: "reference_export",
2216
+ path: exportPath,
2217
+ description: REFERENCE_EXPORT_DESCRIPTION
2218
+ };
2219
+ }
2220
+ function buildConflictReportArtifact(reportPath) {
2221
+ return {
2222
+ kind: "conflict_report",
2223
+ path: reportPath,
2224
+ description: CONFLICT_REPORT_DESCRIPTION
2225
+ };
2226
+ }
2227
+
2228
+ // src/services/page-copy-service.ts
2229
+ function normalizeSlug(slug) {
2230
+ const trimmed = slug.trim();
2231
+ if (!trimmed || trimmed === "/") {
2232
+ return "/";
2233
+ }
2234
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
2235
+ return withLeadingSlash.replace(/\/+$/, "") || "/";
2236
+ }
1980
2237
  function resolveTargetFilePath(input) {
1981
2238
  const {
1982
2239
  sourceFilePath,
@@ -1990,26 +2247,46 @@ function resolveTargetFilePath(input) {
1990
2247
  if (sourceFilePath === sourcePagePath) {
1991
2248
  return targetPagePath;
1992
2249
  }
1993
- const componentsPrefix = `${sourceComponentsPath}${path9.sep}`;
2250
+ const componentsPrefix = `${sourceComponentsPath}${path10.sep}`;
1994
2251
  if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
1995
- const relativeToComponents = path9.relative(
2252
+ const relativeToComponents = path10.relative(
1996
2253
  sourceComponentsPath,
1997
2254
  sourceFilePath
1998
2255
  );
1999
- return path9.join(targetComponentsPath, relativeToComponents);
2256
+ return path10.join(targetComponentsPath, relativeToComponents);
2000
2257
  }
2001
- const relativeToExport = path9.relative(exportRoot, sourceFilePath);
2002
- if (relativeToExport.startsWith("..") || path9.isAbsolute(relativeToExport)) {
2258
+ const relativeToExport = path10.relative(exportRoot, sourceFilePath);
2259
+ if (relativeToExport.startsWith("..") || path10.isAbsolute(relativeToExport)) {
2003
2260
  throw new Error(`Source file is outside export root: ${sourceFilePath}`);
2004
2261
  }
2005
- return path9.join(projectRoot, relativeToExport);
2262
+ return path10.join(projectRoot, relativeToExport);
2263
+ }
2264
+ function buildPageCopyGuidance(input) {
2265
+ const artifacts = [
2266
+ buildExportManifestArtifact(input.manifestPath),
2267
+ buildReferenceExportArtifact(input.exportPath)
2268
+ ];
2269
+ if (input.hasFileConflicts) {
2270
+ artifacts.push(buildConflictReportArtifact(input.reportPath));
2271
+ }
2272
+ const installDependenciesPrefix = input.addedDependencies.length > 0 ? "Install any newly added dependencies first. " : "";
2273
+ let requiredActions = "Follow repository instructions for verification first. If repository rules are absent, inspect package.json scripts and run the most relevant minimal checks. Compare against the reference_export artifact only if behavior or integration looks wrong.";
2274
+ if (input.status === "needs_review" && input.hasFileConflicts) {
2275
+ requiredActions = "Review every file listed in the conflict_report artifact. Those conflicting files are an inseparable part of the imported page functionality and must be resolved deliberately before the transfer is complete. After resolving them, run repository-required verification and re-check after any local adaptation or refactor.";
2276
+ } else if (input.status === "needs_review") {
2277
+ requiredActions = "Resolve dependency version conflicts or other review blockers first, then run repository-required verification. If repository rules are absent, inspect package.json scripts and run the most relevant minimal checks. Use the reference_export artifact only when behavior diverges or local adaptation is unclear.";
2278
+ }
2279
+ return {
2280
+ artifacts,
2281
+ requiredActions: `${installDependenciesPrefix}${requiredActions}`
2282
+ };
2006
2283
  }
2007
2284
  async function copyPageFromExport(input) {
2008
2285
  const normalizedOriginSlug = normalizeSlug(input.originPageSlug);
2009
2286
  const { exportId, exportPath } = await resolveSingleExportDirectory(
2010
2287
  input.exportsRoot
2011
2288
  );
2012
- const manifestPath = path9.join(
2289
+ const manifestPath = path10.join(
2013
2290
  input.exportsRoot,
2014
2291
  `${exportId}.manifest.json`
2015
2292
  );
@@ -2032,13 +2309,13 @@ async function copyPageFromExport(input) {
2032
2309
  `Page not found in manifest for slug: ${normalizedOriginSlug}`
2033
2310
  );
2034
2311
  }
2035
- const sourcePagePath = path9.join(exportPath, page.pagePath);
2036
- const sourceComponentsPath = path9.join(exportPath, page.componentsPath);
2037
- const sourcePageStats = await stat5(sourcePagePath).catch(() => null);
2312
+ const sourcePagePath = path10.join(exportPath, page.pagePath);
2313
+ const sourceComponentsPath = path10.join(exportPath, page.componentsPath);
2314
+ const sourcePageStats = await stat6(sourcePagePath).catch(() => null);
2038
2315
  if (!sourcePageStats?.isFile()) {
2039
2316
  throw new Error(`Source page file not found: ${sourcePagePath}`);
2040
2317
  }
2041
- const sourceComponentsStats = await stat5(sourceComponentsPath).catch(
2318
+ const sourceComponentsStats = await stat6(sourceComponentsPath).catch(
2042
2319
  () => null
2043
2320
  );
2044
2321
  if (!sourceComponentsStats?.isDirectory()) {
@@ -2046,8 +2323,8 @@ async function copyPageFromExport(input) {
2046
2323
  `Source components folder not found: ${sourceComponentsPath}`
2047
2324
  );
2048
2325
  }
2049
- const targetPagePath = path9.join(input.projectRoot, page.pagePath);
2050
- const targetComponentsPath = path9.join(
2326
+ const targetPagePath = path10.join(input.projectRoot, page.pagePath);
2327
+ const targetComponentsPath = path10.join(
2051
2328
  input.projectRoot,
2052
2329
  page.componentsPath
2053
2330
  );
@@ -2055,7 +2332,7 @@ async function copyPageFromExport(input) {
2055
2332
  ensureSafeTargetPath(input.projectRoot, targetComponentsPath);
2056
2333
  const candidateFiles = await resolveManifestCandidateFiles({
2057
2334
  exportPath,
2058
- pageSlug: normalizedOriginSlug,
2335
+ manifestOwnerLabel: `page "${normalizedOriginSlug}"`,
2059
2336
  files: page.manifest.files
2060
2337
  });
2061
2338
  const dependencyGraph = await buildImportGraph({
@@ -2078,18 +2355,18 @@ async function copyPageFromExport(input) {
2078
2355
  projectRoot: input.projectRoot
2079
2356
  });
2080
2357
  ensureSafeTargetPath(input.projectRoot, targetFilePath);
2081
- const sourceBuffer = await readFile3(sourceFilePath);
2358
+ const sourceBuffer = await readFile4(sourceFilePath);
2082
2359
  const plannedSourceBuffer = sourceBuffer;
2083
2360
  let targetBuffer = null;
2084
2361
  try {
2085
- targetBuffer = await readFile3(targetFilePath);
2362
+ targetBuffer = await readFile4(targetFilePath);
2086
2363
  } catch {
2087
2364
  targetBuffer = null;
2088
2365
  }
2089
2366
  const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
2090
2367
  const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
2091
2368
  if (!targetBuffer) {
2092
- await ensureDir(path9.dirname(targetFilePath));
2369
+ await ensureDir(path10.dirname(targetFilePath));
2093
2370
  await writeFile2(targetFilePath, plannedSourceBuffer);
2094
2371
  newFiles.push(targetRelative);
2095
2372
  continue;
@@ -2112,8 +2389,8 @@ async function copyPageFromExport(input) {
2112
2389
  isBinary
2113
2390
  });
2114
2391
  }
2115
- const exportPackageJsonPath = path9.join(exportPath, "package.json");
2116
- const userPackageJsonPath = path9.join(input.projectRoot, "package.json");
2392
+ const exportPackageJsonPath = path10.join(exportPath, "package.json");
2393
+ const userPackageJsonPath = path10.join(input.projectRoot, "package.json");
2117
2394
  if (!await fileExists2(exportPackageJsonPath)) {
2118
2395
  throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
2119
2396
  }
@@ -2182,7 +2459,7 @@ async function copyPageFromExport(input) {
2182
2459
  });
2183
2460
  }
2184
2461
  const copyId = createCopyId();
2185
- const reportPath = path9.join(
2462
+ const reportPath = path10.join(
2186
2463
  input.exportsRoot,
2187
2464
  `${exportId}.copy-report-${copyId}.json`
2188
2465
  );
@@ -2220,6 +2497,14 @@ async function copyPageFromExport(input) {
2220
2497
  "Missing dependencies were added to package.json but are NOT installed yet. Run dependency installation manually with the package manager used by the target project."
2221
2498
  );
2222
2499
  }
2500
+ const guidance = buildPageCopyGuidance({
2501
+ status,
2502
+ exportPath,
2503
+ manifestPath,
2504
+ reportPath,
2505
+ hasFileConflicts,
2506
+ addedDependencies: sortedAddedDependencies
2507
+ });
2223
2508
  return {
2224
2509
  status,
2225
2510
  message: messageParts.join(" "),
@@ -2241,7 +2526,8 @@ async function copyPageFromExport(input) {
2241
2526
  conflictFiles: conflictFiles.length,
2242
2527
  addedDependencies: addedDependencies.length,
2243
2528
  dependenciesVersionConflicts: dependenciesVersionConflicts.length
2244
- }
2529
+ },
2530
+ guidance
2245
2531
  };
2246
2532
  }
2247
2533
 
@@ -2335,198 +2621,1215 @@ function buildInspectPageReport(input) {
2335
2621
  };
2336
2622
  }
2337
2623
 
2338
- // src/services/project-sync-service.ts
2339
- function buildManifestPath(exportsRoot, exportId) {
2340
- return path10.join(exportsRoot, `${exportId}.manifest.json`);
2624
+ // src/services/exportable-copy-service.ts
2625
+ import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
2626
+ import path11 from "path";
2627
+ var PACKAGE_JSON_RELATIVE_PATH = "package.json";
2628
+ var SUPPLEMENTARY_DEPENDENCY_PACKAGES = {
2629
+ cookieBanner: ["@types/js-cookie"]
2630
+ };
2631
+ function normalizeExportableKey(value) {
2632
+ const trimmed = value.trim();
2633
+ if (!trimmed) {
2634
+ throw new Error("exportableKey must not be empty.");
2635
+ }
2636
+ return trimmed;
2341
2637
  }
2342
- var ProjectSyncService = class {
2343
- provider;
2344
- projectRoot;
2345
- targetProjectRoot;
2346
- primeUiRoot;
2347
- tempRoot;
2348
- exportsRoot;
2349
- constructor(options) {
2350
- this.projectRoot = options.projectRoot;
2351
- this.targetProjectRoot = options.targetProjectRoot;
2352
- this.provider = options.provider;
2353
- this.primeUiRoot = path10.join(this.projectRoot, ".primeui");
2354
- this.tempRoot = path10.join(this.primeUiRoot, "temp");
2355
- this.exportsRoot = path10.join(this.tempRoot, "exports");
2638
+ function createNotAvailableResult(input) {
2639
+ const skippedFiles = input.skippedFiles ?? [];
2640
+ const availabilityHint = input.availabilityHint ?? (input.isReadyToExport ? "Open the PrimeUI project and regenerate the export after the shared exportable produces files successfully." : "Enable or configure this shared exportable in PrimeUI project settings first, then create and download a fresh export.");
2641
+ return {
2642
+ status: "not_available",
2643
+ message: `Shared exportable "${input.exportableKey}" is not available for copy. ${availabilityHint} Details: ${input.manifestMessage}`,
2644
+ reportPath: null,
2645
+ exportId: input.exportId,
2646
+ exportPath: input.exportPath,
2647
+ exportableKey: input.exportableKey,
2648
+ isReadyToExport: input.isReadyToExport,
2649
+ manifestSuccess: input.manifestSuccess,
2650
+ manifestMessage: input.manifestMessage,
2651
+ manifestFiles: input.manifestFiles,
2652
+ newFiles: [],
2653
+ identicalFiles: [],
2654
+ conflictFiles: [],
2655
+ skippedFiles,
2656
+ missedDependencies: [],
2657
+ dependenciesVersionConflicts: [],
2658
+ summary: {
2659
+ totalManifestFiles: input.manifestFiles.length,
2660
+ totalCandidateFiles: 0,
2661
+ copiedFiles: 0,
2662
+ identicalFiles: 0,
2663
+ conflictFiles: 0,
2664
+ skippedFiles: skippedFiles.length,
2665
+ missedDependencies: 0,
2666
+ dependenciesVersionConflicts: 0
2667
+ }
2668
+ };
2669
+ }
2670
+ function resolveSupplementaryDependencyPackages(exportableKey) {
2671
+ return SUPPLEMENTARY_DEPENDENCY_PACKAGES[exportableKey] ?? [];
2672
+ }
2673
+ async function copyExportableFromExport(input) {
2674
+ const normalizedExportableKey = normalizeExportableKey(input.exportableKey);
2675
+ const { exportId, exportPath } = await resolveSingleExportDirectory(
2676
+ input.exportsRoot
2677
+ );
2678
+ const manifestPath = path11.join(
2679
+ input.exportsRoot,
2680
+ `${exportId}.manifest.json`
2681
+ );
2682
+ if (!await fileExists2(manifestPath)) {
2683
+ throw new Error(
2684
+ `Export manifest is missing at ${manifestPath}. Run create_export and download_export to regenerate it.`
2685
+ );
2356
2686
  }
2357
- async getProjectInfo(_context) {
2358
- await this.ensureTempLayout();
2359
- return this.provider.getProjectInfo();
2687
+ const manifest = parseManifest(await readJsonFile(manifestPath));
2688
+ if (manifest.export.id !== exportId) {
2689
+ throw new Error(
2690
+ `Manifest export id mismatch: expected ${exportId}, got ${manifest.export.id}. Re-run create_export and download_export.`
2691
+ );
2360
2692
  }
2361
- async listExports(_context) {
2362
- await this.ensureTempLayout();
2363
- return this.provider.listExports();
2693
+ const exportable = manifest.export.exportables.find(
2694
+ (item) => item.key === normalizedExportableKey
2695
+ );
2696
+ if (!exportable) {
2697
+ throw new Error(
2698
+ `Shared exportable not found in manifest for key: ${normalizedExportableKey}`
2699
+ );
2364
2700
  }
2365
- async createExport(_context) {
2366
- await this.ensureTempLayout();
2367
- const result = await this.provider.createExport();
2368
- const manifestPath = buildManifestPath(this.exportsRoot, result.export.id);
2369
- await writeUtf8(manifestPath, `${JSON.stringify(result, null, 2)}
2370
- `);
2371
- return result;
2701
+ const manifestFiles = exportable.manifest.files.map(
2702
+ (filePath) => toPosixPath(filePath).replace(/^\.\/+/, "")
2703
+ );
2704
+ if (!exportable.isReadyToExport || !exportable.manifest.success || manifestFiles.length === 0) {
2705
+ return createNotAvailableResult({
2706
+ exportId,
2707
+ exportPath,
2708
+ exportableKey: normalizedExportableKey,
2709
+ isReadyToExport: exportable.isReadyToExport,
2710
+ manifestSuccess: exportable.manifest.success,
2711
+ manifestMessage: exportable.manifest.message,
2712
+ manifestFiles
2713
+ });
2372
2714
  }
2373
- async downloadExportById(id, _context) {
2374
- await this.ensureTempLayout();
2375
- const exportsList = await this.provider.listExports();
2376
- const selected = exportsList.find((item) => item.id === id);
2377
- if (!selected) {
2378
- throw new Error(`Export not found: ${id}`);
2379
- }
2380
- const targetZipPath = path10.join(this.exportsRoot, `${id}.zip`);
2381
- const targetProjectPath = path10.join(this.exportsRoot, id);
2382
- const manifestPath = buildManifestPath(this.exportsRoot, id);
2383
- let manifest;
2384
- try {
2385
- manifest = parsePrimeUiExportManifest(
2386
- JSON.parse(await readFile4(manifestPath, "utf-8"))
2387
- );
2388
- } catch (error) {
2389
- const reason = error instanceof Error ? error.message : String(error);
2390
- throw new Error(
2391
- `Export manifest is required for download id "${id}". Run create_export for this export before downloading. Details: ${reason}`
2392
- );
2393
- }
2394
- if (manifest.export.id !== id) {
2395
- throw new Error(
2396
- `Export manifest mismatch for id "${id}". Run create_export and retry download.`
2397
- );
2715
+ const skippedFiles = [];
2716
+ const copyManagedManifestFiles = manifestFiles.filter((relativePath) => {
2717
+ if (relativePath !== PACKAGE_JSON_RELATIVE_PATH) {
2718
+ return true;
2398
2719
  }
2399
- await ensureDir(this.exportsRoot);
2400
- await this.provider.downloadExportArchive(id, targetZipPath);
2401
- await resetDir(targetProjectPath);
2402
- await extractZip(targetZipPath, targetProjectPath);
2403
- return {
2404
- exportId: id,
2405
- projectPath: targetProjectPath,
2406
- manifestPath
2407
- };
2720
+ skippedFiles.push({
2721
+ targetPath: relativePath,
2722
+ reason: "package_json_managed_as_dependencies"
2723
+ });
2724
+ return false;
2725
+ });
2726
+ if (copyManagedManifestFiles.length === 0) {
2727
+ return createNotAvailableResult({
2728
+ exportId,
2729
+ exportPath,
2730
+ exportableKey: normalizedExportableKey,
2731
+ isReadyToExport: exportable.isReadyToExport,
2732
+ manifestSuccess: exportable.manifest.success,
2733
+ manifestMessage: exportable.manifest.message,
2734
+ manifestFiles,
2735
+ skippedFiles,
2736
+ availabilityHint: "No copyable files remain after filtering non-copyable manifest entries such as package.json. Adjust the PrimeUI exportable configuration and create a fresh export before retrying."
2737
+ });
2408
2738
  }
2409
- async inspectPage(slug, _context) {
2410
- await this.ensureTempLayout();
2411
- const pageDetails = await this.provider.getProjectPageBySlug(slug);
2412
- const reportPayload = buildInspectPageReport(pageDetails);
2413
- return {
2414
- ...pageDetails,
2415
- report: reportPayload.report,
2416
- reportRows: reportPayload.reportRows
2417
- };
2739
+ const candidateFiles = await resolveManifestCandidateFiles({
2740
+ exportPath,
2741
+ manifestOwnerLabel: `exportable "${normalizedExportableKey}"`,
2742
+ files: copyManagedManifestFiles
2743
+ });
2744
+ const dependencyGraph = await buildImportGraph({
2745
+ projectRoot: exportPath,
2746
+ entryFiles: candidateFiles,
2747
+ followInternalImports: false
2748
+ });
2749
+ const newFiles = [];
2750
+ const identicalFiles = [];
2751
+ const conflictFiles = [];
2752
+ const conflictReportEntries = [];
2753
+ for (const sourceFilePath of candidateFiles) {
2754
+ const relativeToExport = path11.relative(exportPath, sourceFilePath);
2755
+ if (relativeToExport.startsWith("..") || path11.isAbsolute(relativeToExport)) {
2756
+ throw new Error(`Source file is outside export root: ${sourceFilePath}`);
2757
+ }
2758
+ const targetFilePath = path11.join(input.projectRoot, relativeToExport);
2759
+ ensureSafeTargetPath(input.projectRoot, targetFilePath);
2760
+ const sourceBuffer = await readFile5(sourceFilePath);
2761
+ let targetBuffer = null;
2762
+ try {
2763
+ targetBuffer = await readFile5(targetFilePath);
2764
+ } catch {
2765
+ targetBuffer = null;
2766
+ }
2767
+ const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
2768
+ const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
2769
+ if (!targetBuffer) {
2770
+ await ensureDir(path11.dirname(targetFilePath));
2771
+ await writeFile3(targetFilePath, sourceBuffer);
2772
+ newFiles.push(targetRelative);
2773
+ continue;
2774
+ }
2775
+ if (sourceBuffer.equals(targetBuffer)) {
2776
+ identicalFiles.push(targetRelative);
2777
+ continue;
2778
+ }
2779
+ const isBinary = isBinaryBuffer(sourceBuffer) || isBinaryBuffer(targetBuffer);
2780
+ const diff = isBinary ? "Binary files differ." : buildUnifiedDiff({
2781
+ oldText: targetBuffer.toString("utf-8"),
2782
+ newText: sourceBuffer.toString("utf-8"),
2783
+ oldLabel: `user/${targetRelative}`,
2784
+ newLabel: `export/${sourceRelative}`
2785
+ });
2786
+ conflictReportEntries.push({
2787
+ sourcePath: sourceRelative,
2788
+ targetPath: targetRelative,
2789
+ diff,
2790
+ isBinary
2791
+ });
2418
2792
  }
2419
- async copyPage(originPageSlug, _context) {
2420
- await this.ensureTempLayout();
2421
- return copyPageFromExport({
2422
- projectRoot: this.targetProjectRoot,
2423
- exportsRoot: this.exportsRoot,
2424
- originPageSlug
2793
+ const exportPackageJsonPath = path11.join(
2794
+ exportPath,
2795
+ PACKAGE_JSON_RELATIVE_PATH
2796
+ );
2797
+ const userPackageJsonPath = path11.join(
2798
+ input.projectRoot,
2799
+ PACKAGE_JSON_RELATIVE_PATH
2800
+ );
2801
+ if (!await fileExists2(exportPackageJsonPath)) {
2802
+ throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
2803
+ }
2804
+ if (!await fileExists2(userPackageJsonPath)) {
2805
+ throw new Error(`User package.json not found: ${userPackageJsonPath}`);
2806
+ }
2807
+ const requiredPackageNames = [
2808
+ ...dependencyGraph.externalPackages,
2809
+ ...resolveSupplementaryDependencyPackages(normalizedExportableKey)
2810
+ ];
2811
+ const { missedDependencies, dependenciesVersionConflicts } = await resolveDependencyRequirements({
2812
+ exportPackageJsonPath,
2813
+ userPackageJsonPath,
2814
+ requiredPackageNames
2815
+ });
2816
+ const sortedConflictReportEntries = conflictReportEntries.sort(
2817
+ (a, b) => a.targetPath.localeCompare(b.targetPath)
2818
+ );
2819
+ for (const entry of sortedConflictReportEntries) {
2820
+ conflictFiles.push({
2821
+ targetPath: entry.targetPath,
2822
+ isBinary: entry.isBinary
2425
2823
  });
2426
2824
  }
2427
- async clearTemp(_context) {
2428
- await resetDir(this.tempRoot);
2429
- await ensureDir(this.exportsRoot);
2825
+ const copyId = createCopyId();
2826
+ const reportPath = path11.join(
2827
+ input.exportsRoot,
2828
+ `${exportId}.copy-exportable-report-${copyId}.json`
2829
+ );
2830
+ const report = {
2831
+ reportPath,
2832
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2833
+ exportId,
2834
+ exportableKey: normalizedExportableKey,
2835
+ conflicts: sortedConflictReportEntries
2836
+ };
2837
+ await writeFile3(reportPath, `${JSON.stringify(report, null, 2)}
2838
+ `, "utf-8");
2839
+ const hasFileConflicts = conflictFiles.length > 0;
2840
+ const hasDependencyVersionConflicts = dependenciesVersionConflicts.length > 0;
2841
+ const needsReview = hasFileConflicts || hasDependencyVersionConflicts;
2842
+ const status = needsReview ? "needs_review" : "completed";
2843
+ const messageParts = [];
2844
+ if (hasFileConflicts) {
2845
+ messageParts.push(
2846
+ `Conflicts detected while copying shared exportable "${normalizedExportableKey}". Manual resolution is required before integration is complete. Review ${reportPath} for full diffs and resolve conflicts in the context of the target project behavior.`
2847
+ );
2848
+ } else if (hasDependencyVersionConflicts) {
2849
+ messageParts.push(
2850
+ `Dependency version conflicts detected for shared exportable "${normalizedExportableKey}". Manual resolution is required before integration is complete. Review ${reportPath} and dependenciesVersionConflicts in this response.`
2851
+ );
2852
+ } else {
2853
+ messageParts.push(
2854
+ `Shared exportable "${normalizedExportableKey}" copied with no file conflicts or dependency version conflicts. Review ${reportPath} for full local details if needed.`
2855
+ );
2430
2856
  }
2431
- async ensureTempLayout() {
2432
- await ensureDir(this.primeUiRoot);
2433
- await ensureDir(this.tempRoot);
2434
- await ensureDir(this.exportsRoot);
2857
+ if (missedDependencies.length > 0) {
2858
+ messageParts.push(
2859
+ "missedDependencies lists required packages that are a substantial and inseparable part of this exportable's functionality. They were not added or installed automatically and must be handled explicitly by the agent or user."
2860
+ );
2435
2861
  }
2436
- };
2437
-
2438
- // src/runtime.ts
2439
- function defaultCreateProvider(options) {
2440
- return new ApiProjectDataProvider(options);
2862
+ if (skippedFiles.length > 0) {
2863
+ messageParts.push(
2864
+ "package.json is never copied automatically for shared exportables. Dependency follow-up must come from missedDependencies and dependenciesVersionConflicts."
2865
+ );
2866
+ }
2867
+ return {
2868
+ status,
2869
+ message: messageParts.join(" "),
2870
+ reportPath,
2871
+ exportId,
2872
+ exportPath,
2873
+ exportableKey: normalizedExportableKey,
2874
+ isReadyToExport: exportable.isReadyToExport,
2875
+ manifestSuccess: exportable.manifest.success,
2876
+ manifestMessage: exportable.manifest.message,
2877
+ manifestFiles,
2878
+ newFiles,
2879
+ identicalFiles,
2880
+ conflictFiles,
2881
+ skippedFiles,
2882
+ missedDependencies,
2883
+ dependenciesVersionConflicts,
2884
+ summary: {
2885
+ totalManifestFiles: manifestFiles.length,
2886
+ totalCandidateFiles: candidateFiles.length,
2887
+ copiedFiles: newFiles.length,
2888
+ identicalFiles: identicalFiles.length,
2889
+ conflictFiles: conflictFiles.length,
2890
+ skippedFiles: skippedFiles.length,
2891
+ missedDependencies: missedDependencies.length,
2892
+ dependenciesVersionConflicts: dependenciesVersionConflicts.length
2893
+ }
2894
+ };
2441
2895
  }
2442
- var LazyProjectSyncSource = class {
2443
- env;
2444
- getCwd;
2445
- createProvider;
2446
- stickyProjectRoot;
2447
- constructor(options = {}) {
2448
- this.env = options.env ?? process.env;
2449
- this.getCwd = options.getCwd ?? (() => process.cwd());
2450
- this.createProvider = options.createProvider ?? defaultCreateProvider;
2896
+
2897
+ // src/services/component-copy-service.ts
2898
+ import { readFile as readFile6, stat as stat7, writeFile as writeFile4 } from "fs/promises";
2899
+ import path12 from "path";
2900
+ function normalizeSlug2(slug) {
2901
+ const trimmed = slug.trim();
2902
+ if (!trimmed || trimmed === "/") {
2903
+ return "/";
2451
2904
  }
2452
- async withService(context, operation) {
2453
- const service = await this.createProjectSyncService(context);
2454
- return operation(service);
2905
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
2906
+ return withLeadingSlash.replace(/\/+$/, "") || "/";
2907
+ }
2908
+ function normalizeRequiredValue(value, label) {
2909
+ const trimmed = value.trim();
2910
+ if (!trimmed) {
2911
+ throw new Error(`${label} must not be empty.`);
2455
2912
  }
2456
- async withMutationService(context, operation) {
2457
- const service = await this.createProjectSyncService(context);
2458
- return operation(service);
2913
+ return trimmed;
2914
+ }
2915
+ function resolveTargetFilePath2(input) {
2916
+ const {
2917
+ sourceFilePath,
2918
+ sourcePagePath,
2919
+ sourceComponentsPath,
2920
+ targetPagePath,
2921
+ targetComponentsPath,
2922
+ exportRoot,
2923
+ projectRoot
2924
+ } = input;
2925
+ if (sourceFilePath === sourcePagePath) {
2926
+ return targetPagePath;
2459
2927
  }
2460
- async createProjectSyncService(context) {
2461
- const resolvedProjectConfig = await resolvePrimeUiProjectConfig({
2462
- cwd: this.getCwd(),
2463
- projectRootFromTool: context?.projectRoot,
2464
- projectRootFromSticky: this.stickyProjectRoot,
2465
- projectRootFromEnv: this.env.PRIMEUI_PROJECT_ROOT
2466
- });
2467
- const projectRoot = resolvedProjectConfig.projectRoot;
2468
- const targetProjectRoot = resolvePrimeUiTargetProjectRoot(
2469
- projectRoot,
2470
- resolvedProjectConfig.projectConfig
2928
+ const componentsPrefix = `${sourceComponentsPath}${path12.sep}`;
2929
+ if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
2930
+ const relativeToComponents = path12.relative(
2931
+ sourceComponentsPath,
2932
+ sourceFilePath
2471
2933
  );
2472
- const apiKey = await resolvePrimeUiApiKey({
2473
- projectConfig: resolvedProjectConfig.projectConfig,
2474
- apiKeyFromEnv: this.env.PRIMEUI_API_KEY
2475
- });
2476
- this.stickyProjectRoot = projectRoot;
2477
- const provider = this.createProvider({
2478
- apiKey,
2479
- baseUrl: this.env.PRIMEUI_API_BASE_URL
2480
- });
2481
- return new ProjectSyncService({
2482
- projectRoot,
2483
- targetProjectRoot,
2484
- provider
2485
- });
2934
+ return path12.join(targetComponentsPath, relativeToComponents);
2486
2935
  }
2487
- async getProjectInfo(context) {
2488
- return this.withService(
2489
- context,
2490
- (service) => service.getProjectInfo(context)
2936
+ const relativeToExport = path12.relative(exportRoot, sourceFilePath);
2937
+ if (relativeToExport.startsWith("..") || path12.isAbsolute(relativeToExport)) {
2938
+ throw new Error(`Source file is outside export root: ${sourceFilePath}`);
2939
+ }
2940
+ return path12.join(projectRoot, relativeToExport);
2941
+ }
2942
+ function buildContentKey(componentId, occurrenceIndex) {
2943
+ return occurrenceIndex === 1 ? componentId : `${componentId}-${occurrenceIndex}`;
2944
+ }
2945
+ function resolveSelectedBlock(input) {
2946
+ const { components } = input.pageDetails;
2947
+ if (components === null) {
2948
+ throw new Error(
2949
+ `Components are unavailable for page "${input.pageDetails.page.slug}" because no active variant is set.`
2491
2950
  );
2492
2951
  }
2493
- async listExports(context) {
2494
- return this.withService(context, (service) => service.listExports(context));
2952
+ let occurrenceIndex = 0;
2953
+ for (const component of components) {
2954
+ if (component.componentId !== input.componentId) {
2955
+ continue;
2956
+ }
2957
+ occurrenceIndex += 1;
2958
+ if (component.blockId !== input.blockId) {
2959
+ continue;
2960
+ }
2961
+ return {
2962
+ blockId: component.blockId,
2963
+ componentId: component.componentId,
2964
+ slot: component.slot,
2965
+ props: component.props,
2966
+ occurrenceIndex,
2967
+ contentKey: buildContentKey(component.componentId, occurrenceIndex)
2968
+ };
2495
2969
  }
2496
- async createExport(context) {
2497
- return this.withService(
2498
- context,
2499
- (service) => service.createExport(context)
2500
- );
2970
+ throw new Error(
2971
+ `Block not found for page "${input.pageDetails.page.slug}" using blockId "${input.blockId}" and componentId "${input.componentId}". Run inspect_page and confirm the selected block instance.`
2972
+ );
2973
+ }
2974
+ async function createConflictReportEntry(input) {
2975
+ const sourceBuffer = await readFile6(input.sourceFilePath);
2976
+ let targetBuffer = null;
2977
+ try {
2978
+ targetBuffer = await readFile6(input.targetFilePath);
2979
+ } catch {
2980
+ targetBuffer = null;
2501
2981
  }
2502
- async downloadExportById(id, context) {
2503
- return this.withService(
2504
- context,
2505
- (service) => service.downloadExportById(id, context)
2506
- );
2982
+ if (!targetBuffer || sourceBuffer.equals(targetBuffer)) {
2983
+ return null;
2507
2984
  }
2508
- async inspectPage(slug, context) {
2509
- return this.withService(
2510
- context,
2511
- (service) => service.inspectPage(slug, context)
2512
- );
2985
+ const sourceRelative = toProjectRelative(
2986
+ input.exportPath,
2987
+ input.sourceFilePath
2988
+ );
2989
+ const targetRelative = toProjectRelative(
2990
+ input.projectRoot,
2991
+ input.targetFilePath
2992
+ );
2993
+ const isBinary = isBinaryBuffer(sourceBuffer) || isBinaryBuffer(targetBuffer);
2994
+ return {
2995
+ sourcePath: sourceRelative,
2996
+ targetPath: targetRelative,
2997
+ diff: isBinary ? "Binary files differ." : buildUnifiedDiff({
2998
+ oldText: targetBuffer.toString("utf-8"),
2999
+ newText: sourceBuffer.toString("utf-8"),
3000
+ oldLabel: `user/${targetRelative}`,
3001
+ newLabel: `export/${sourceRelative}`
3002
+ }),
3003
+ isBinary
3004
+ };
3005
+ }
3006
+ async function applyDependencyRequirements(input) {
3007
+ const exportPackageJsonPath = path12.join(input.exportPath, "package.json");
3008
+ const userPackageJsonPath = path12.join(input.projectRoot, "package.json");
3009
+ if (!await fileExists2(exportPackageJsonPath)) {
3010
+ throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
2513
3011
  }
2514
- async copyPage(originPageSlug, context) {
2515
- return this.withMutationService(
2516
- context,
2517
- (service) => service.copyPage(originPageSlug, context)
2518
- );
3012
+ if (!await fileExists2(userPackageJsonPath)) {
3013
+ throw new Error(`User package.json not found: ${userPackageJsonPath}`);
2519
3014
  }
2520
- async clearTemp(context) {
2521
- return this.withMutationService(
2522
- context,
2523
- (service) => service.clearTemp(context)
3015
+ const exportPackageJson = await readJsonFile(
3016
+ exportPackageJsonPath
3017
+ );
3018
+ const userPackageJson = await readJsonFile(userPackageJsonPath);
3019
+ const addedDependencies = [];
3020
+ const dependenciesVersionConflicts = [];
3021
+ for (const packageName of new Set(input.requiredPackageNames)) {
3022
+ const exportDependency = getDependencyVersion(
3023
+ exportPackageJson,
3024
+ packageName
2524
3025
  );
2525
- }
2526
- };
2527
-
2528
- // src/server.ts
3026
+ if (!exportDependency) {
3027
+ continue;
3028
+ }
3029
+ const userDependency = getDependencyVersion(userPackageJson, packageName);
3030
+ if (!userDependency) {
3031
+ const sectionData = getOrCreateDependencySection(
3032
+ userPackageJson,
3033
+ exportDependency.section
3034
+ );
3035
+ sectionData[packageName] = exportDependency.version;
3036
+ addedDependencies.push({
3037
+ packageName,
3038
+ version: exportDependency.version,
3039
+ section: exportDependency.section
3040
+ });
3041
+ continue;
3042
+ }
3043
+ if (userDependency.version !== exportDependency.version) {
3044
+ dependenciesVersionConflicts.push({
3045
+ packageName,
3046
+ section: exportDependency.section,
3047
+ exportVersion: exportDependency.version,
3048
+ userVersion: userDependency.version
3049
+ });
3050
+ }
3051
+ }
3052
+ if (addedDependencies.length > 0) {
3053
+ sortDependencySections(userPackageJson);
3054
+ await writeFile4(
3055
+ userPackageJsonPath,
3056
+ `${JSON.stringify(userPackageJson, null, 2)}
3057
+ `,
3058
+ "utf-8"
3059
+ );
3060
+ }
3061
+ return {
3062
+ addedDependencies: addedDependencies.sort(
3063
+ (a, b) => a.packageName.localeCompare(b.packageName)
3064
+ ),
3065
+ dependenciesVersionConflicts: dependenciesVersionConflicts.sort(
3066
+ (a, b) => a.packageName.localeCompare(b.packageName)
3067
+ )
3068
+ };
3069
+ }
3070
+ function buildGuidance(input) {
3071
+ const requiredActions = [
3072
+ `Use selectedBlock.props as the exact prop payload for block "${input.selectedBlock.blockId}" and contentKey "${input.selectedBlock.contentKey}".`,
3073
+ `Review manualInsertionTargets and wire the selected component instance into: ${input.manualInsertionTargets.join(", ")}.`,
3074
+ input.potentialDependencyFiles.length > 0 ? "Review potentialDependencyFiles first if the imported component is incomplete, because they are the most likely supporting files." : "If the imported component is incomplete, compare the page file against the downloaded export before touching unrelated files.",
3075
+ input.primaryComponentResolved ? "If behavior is incorrect, compare the local files against the downloaded reference export using exportPath." : "Resolve the canonical component file manually from the downloaded reference export before continuing integration."
3076
+ ];
3077
+ if (input.addedDependencies.length > 0) {
3078
+ requiredActions.push(
3079
+ "Install the newly added dependencies before running the copied component locally."
3080
+ );
3081
+ }
3082
+ if (input.dependenciesVersionConflicts.length > 0) {
3083
+ requiredActions.push(
3084
+ "Resolve the reported dependency version conflicts before treating the transfer as complete."
3085
+ );
3086
+ }
3087
+ if (input.missingManifestFiles.length > 0) {
3088
+ requiredActions.push(
3089
+ "Restore the missing export files referenced by the manifest before treating the transfer as complete."
3090
+ );
3091
+ }
3092
+ const notes = [
3093
+ "copy_component copies only the canonical component file when it can resolve that file reliably.",
3094
+ "potentialDependencyFiles and potentialConflictFiles are hints, not mandatory full-page merge instructions."
3095
+ ];
3096
+ if (input.potentialConflictFiles.length > 0) {
3097
+ notes.push(
3098
+ "potentialConflictFiles mark related files that already differ locally and may matter only if behavior diverges after integration."
3099
+ );
3100
+ }
3101
+ return {
3102
+ requiredActions,
3103
+ notes
3104
+ };
3105
+ }
3106
+ async function copyComponentFromExport(input) {
3107
+ const normalizedOriginSlug = normalizeSlug2(input.originPageSlug);
3108
+ const normalizedBlockId = normalizeRequiredValue(input.blockId, "blockId");
3109
+ const normalizedComponentId = normalizeRequiredValue(
3110
+ input.componentId,
3111
+ "componentId"
3112
+ );
3113
+ const { exportId, exportPath } = await resolveSingleExportDirectory(
3114
+ input.exportsRoot
3115
+ );
3116
+ const manifestPath = path12.join(
3117
+ input.exportsRoot,
3118
+ `${exportId}.manifest.json`
3119
+ );
3120
+ if (!await fileExists2(manifestPath)) {
3121
+ throw new Error(
3122
+ `Export manifest is missing at ${manifestPath}. Run create_export and download_export to regenerate it.`
3123
+ );
3124
+ }
3125
+ const manifest = parseManifest(await readJsonFile(manifestPath));
3126
+ if (manifest.export.id !== exportId) {
3127
+ throw new Error(
3128
+ `Manifest export id mismatch: expected ${exportId}, got ${manifest.export.id}. Re-run create_export and download_export.`
3129
+ );
3130
+ }
3131
+ const page = manifest.pages.find(
3132
+ (item) => normalizeSlug2(item.slug) === normalizedOriginSlug
3133
+ );
3134
+ if (!page) {
3135
+ throw new Error(
3136
+ `Page not found in manifest for slug: ${normalizedOriginSlug}`
3137
+ );
3138
+ }
3139
+ const candidateFiles = await resolveManifestCandidateFiles({
3140
+ exportPath,
3141
+ manifestOwnerLabel: `page "${normalizedOriginSlug}"`,
3142
+ files: page.manifest.files,
3143
+ requireExisting: false
3144
+ });
3145
+ const selectedBlock = resolveSelectedBlock({
3146
+ pageDetails: input.pageDetails,
3147
+ blockId: normalizedBlockId,
3148
+ componentId: normalizedComponentId
3149
+ });
3150
+ const sourcePagePath = path12.join(exportPath, page.pagePath);
3151
+ const sourceComponentsPath = path12.join(exportPath, page.componentsPath);
3152
+ const targetPagePath = path12.join(input.projectRoot, page.pagePath);
3153
+ const targetComponentsPath = path12.join(
3154
+ input.projectRoot,
3155
+ page.componentsPath
3156
+ );
3157
+ ensureSafeTargetPath(input.projectRoot, targetPagePath);
3158
+ ensureSafeTargetPath(input.projectRoot, targetComponentsPath);
3159
+ const manualInsertionTargets = [
3160
+ toProjectRelative(input.projectRoot, targetPagePath)
3161
+ ];
3162
+ const primarySourceCandidates = [
3163
+ toPosixPath(path12.join(page.componentsPath, `${normalizedComponentId}.tsx`)),
3164
+ toPosixPath(
3165
+ path12.join(page.componentsPath, normalizedComponentId, "index.tsx")
3166
+ )
3167
+ ];
3168
+ const primaryCandidateTargetPaths = /* @__PURE__ */ new Set();
3169
+ let primaryComponentFile = null;
3170
+ let resolvedPrimarySourcePath = null;
3171
+ for (const sourceRelativePath of primarySourceCandidates) {
3172
+ const sourceCandidatePath = path12.join(exportPath, sourceRelativePath);
3173
+ const targetCandidatePath = resolveTargetFilePath2({
3174
+ sourceFilePath: sourceCandidatePath,
3175
+ sourcePagePath,
3176
+ sourceComponentsPath,
3177
+ targetPagePath,
3178
+ targetComponentsPath,
3179
+ exportRoot: exportPath,
3180
+ projectRoot: input.projectRoot
3181
+ });
3182
+ ensureSafeTargetPath(input.projectRoot, targetCandidatePath);
3183
+ primaryCandidateTargetPaths.add(
3184
+ toProjectRelative(input.projectRoot, targetCandidatePath)
3185
+ );
3186
+ const stats = await stat7(sourceCandidatePath).catch(() => null);
3187
+ if (!stats?.isFile()) {
3188
+ continue;
3189
+ }
3190
+ if (!resolvedPrimarySourcePath) {
3191
+ resolvedPrimarySourcePath = sourceCandidatePath;
3192
+ primaryComponentFile = toProjectRelative(
3193
+ input.projectRoot,
3194
+ targetCandidatePath
3195
+ );
3196
+ }
3197
+ }
3198
+ const newFiles = [];
3199
+ const identicalFiles = [];
3200
+ const conflictFiles = [];
3201
+ const actualConflicts = [];
3202
+ const missingManifestFiles = /* @__PURE__ */ new Set();
3203
+ if (resolvedPrimarySourcePath && primaryComponentFile) {
3204
+ const targetPrimaryPath = path12.join(
3205
+ input.projectRoot,
3206
+ primaryComponentFile
3207
+ );
3208
+ ensureSafeTargetPath(input.projectRoot, targetPrimaryPath);
3209
+ const sourceBuffer = await readFile6(resolvedPrimarySourcePath);
3210
+ let targetBuffer = null;
3211
+ try {
3212
+ targetBuffer = await readFile6(targetPrimaryPath);
3213
+ } catch {
3214
+ targetBuffer = null;
3215
+ }
3216
+ if (!targetBuffer) {
3217
+ await ensureDir(path12.dirname(targetPrimaryPath));
3218
+ await writeFile4(targetPrimaryPath, sourceBuffer);
3219
+ newFiles.push(primaryComponentFile);
3220
+ } else if (sourceBuffer.equals(targetBuffer)) {
3221
+ identicalFiles.push(primaryComponentFile);
3222
+ } else {
3223
+ const entry = await createConflictReportEntry({
3224
+ sourceFilePath: resolvedPrimarySourcePath,
3225
+ targetFilePath: targetPrimaryPath,
3226
+ exportPath,
3227
+ projectRoot: input.projectRoot
3228
+ });
3229
+ if (entry) {
3230
+ actualConflicts.push(entry);
3231
+ }
3232
+ }
3233
+ }
3234
+ const dependencyGraph = resolvedPrimarySourcePath ? await buildImportGraph({
3235
+ projectRoot: exportPath,
3236
+ entryFiles: [resolvedPrimarySourcePath]
3237
+ }) : { externalPackages: [] };
3238
+ const { addedDependencies, dependenciesVersionConflicts } = resolvedPrimarySourcePath ? await applyDependencyRequirements({
3239
+ exportPath,
3240
+ projectRoot: input.projectRoot,
3241
+ requiredPackageNames: dependencyGraph.externalPackages
3242
+ }) : {
3243
+ addedDependencies: [],
3244
+ dependenciesVersionConflicts: []
3245
+ };
3246
+ const potentialDependencyTargets = /* @__PURE__ */ new Set();
3247
+ const potentialConflictEntries = /* @__PURE__ */ new Map();
3248
+ for (const sourceFilePath of candidateFiles) {
3249
+ const targetFilePath = resolveTargetFilePath2({
3250
+ sourceFilePath,
3251
+ sourcePagePath,
3252
+ sourceComponentsPath,
3253
+ targetPagePath,
3254
+ targetComponentsPath,
3255
+ exportRoot: exportPath,
3256
+ projectRoot: input.projectRoot
3257
+ });
3258
+ ensureSafeTargetPath(input.projectRoot, targetFilePath);
3259
+ const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
3260
+ const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
3261
+ const sourceExists = await fileExists2(sourceFilePath);
3262
+ if (!sourceExists) {
3263
+ missingManifestFiles.add(sourceRelative);
3264
+ continue;
3265
+ }
3266
+ if (manualInsertionTargets.includes(targetRelative) || primaryCandidateTargetPaths.has(targetRelative)) {
3267
+ continue;
3268
+ }
3269
+ potentialDependencyTargets.add(targetRelative);
3270
+ const targetExists = await fileExists2(targetFilePath);
3271
+ if (!targetExists) {
3272
+ continue;
3273
+ }
3274
+ const entry = await createConflictReportEntry({
3275
+ sourceFilePath,
3276
+ targetFilePath,
3277
+ exportPath,
3278
+ projectRoot: input.projectRoot
3279
+ });
3280
+ if (entry) {
3281
+ potentialConflictEntries.set(targetRelative, entry);
3282
+ }
3283
+ }
3284
+ const sortedActualConflicts = actualConflicts.sort(
3285
+ (a, b) => a.targetPath.localeCompare(b.targetPath)
3286
+ );
3287
+ for (const entry of sortedActualConflicts) {
3288
+ conflictFiles.push({
3289
+ targetPath: entry.targetPath,
3290
+ isBinary: entry.isBinary
3291
+ });
3292
+ }
3293
+ const sortedPotentialConflictEntries = [
3294
+ ...potentialConflictEntries.values()
3295
+ ].sort((a, b) => a.targetPath.localeCompare(b.targetPath));
3296
+ const potentialDependencyFiles = [...potentialDependencyTargets].sort(
3297
+ (a, b) => a.localeCompare(b)
3298
+ );
3299
+ const potentialConflictFiles = sortedPotentialConflictEntries.map(
3300
+ (entry) => entry.targetPath
3301
+ );
3302
+ const copyId = createCopyId();
3303
+ const reportPath = path12.join(
3304
+ input.exportsRoot,
3305
+ `${exportId}.copy-component-report-${copyId}.json`
3306
+ );
3307
+ const report = {
3308
+ reportPath,
3309
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3310
+ exportId,
3311
+ originPageSlug: normalizedOriginSlug,
3312
+ blockId: normalizedBlockId,
3313
+ componentId: normalizedComponentId,
3314
+ primaryComponentCandidates: primarySourceCandidates,
3315
+ primaryComponentFile,
3316
+ manualInsertionTargets,
3317
+ potentialDependencyFiles,
3318
+ actualConflicts: sortedActualConflicts,
3319
+ potentialConflicts: sortedPotentialConflictEntries,
3320
+ selectedBlock
3321
+ };
3322
+ await writeFile4(reportPath, `${JSON.stringify(report, null, 2)}
3323
+ `, "utf-8");
3324
+ const needsReview = !primaryComponentFile || conflictFiles.length > 0 || dependenciesVersionConflicts.length > 0 || missingManifestFiles.size > 0;
3325
+ const missingManifestFileList = [...missingManifestFiles].sort(
3326
+ (a, b) => a.localeCompare(b)
3327
+ );
3328
+ const guidance = buildGuidance({
3329
+ selectedBlock,
3330
+ manualInsertionTargets,
3331
+ potentialDependencyFiles,
3332
+ potentialConflictFiles,
3333
+ primaryComponentResolved: Boolean(primaryComponentFile),
3334
+ addedDependencies,
3335
+ dependenciesVersionConflicts,
3336
+ missingManifestFiles: missingManifestFileList
3337
+ });
3338
+ const messageParts = [];
3339
+ if (!primaryComponentFile) {
3340
+ messageParts.push(
3341
+ `Primary component file could not be resolved for block "${normalizedBlockId}" (${normalizedComponentId}). Review ${reportPath}, inspect the downloaded export at ${exportPath}, and resolve the component file manually before continuing.`
3342
+ );
3343
+ } else if (conflictFiles.length > 0) {
3344
+ messageParts.push(
3345
+ `Primary component file conflicts with an existing local file. Review ${reportPath} for the exact diff before completing block-level transfer.`
3346
+ );
3347
+ } else if (newFiles.length > 0) {
3348
+ messageParts.push(
3349
+ `Primary component file copied successfully with no direct conflicts. Review ${reportPath} for local details if needed.`
3350
+ );
3351
+ } else {
3352
+ messageParts.push(
3353
+ `Primary component file already exists and is byte-identical. Review ${reportPath} for local details if needed.`
3354
+ );
3355
+ }
3356
+ if (missingManifestFileList.length > 0) {
3357
+ messageParts.push(
3358
+ `Referenced export files are missing: ${missingManifestFileList.join(", ")}. Review ${reportPath} and restore the missing files before treating this transfer as complete.`
3359
+ );
3360
+ }
3361
+ if (addedDependencies.length > 0) {
3362
+ messageParts.push(
3363
+ "Missing dependencies were added to package.json but are NOT installed yet. Run dependency installation manually with the package manager used by the target project."
3364
+ );
3365
+ }
3366
+ if (dependenciesVersionConflicts.length > 0) {
3367
+ messageParts.push(
3368
+ `Dependency version conflicts detected. Manual resolution is required before continuing block-level integration. Review ${reportPath} for full local details.`
3369
+ );
3370
+ }
3371
+ messageParts.push(
3372
+ `Use selectedBlock.props with contentKey "${selectedBlock.contentKey}" for the chosen block instance.`
3373
+ );
3374
+ messageParts.push(
3375
+ `manualInsertionTargets identifies where this component usually needs to be wired: ${manualInsertionTargets.join(", ")}.`
3376
+ );
3377
+ if (potentialDependencyFiles.length > 0) {
3378
+ messageParts.push(
3379
+ "potentialDependencyFiles are prioritized hints, not mandatory full-page merge instructions. Review them first if the imported component behavior is incomplete."
3380
+ );
3381
+ }
3382
+ if (potentialConflictFiles.length > 0) {
3383
+ messageParts.push(
3384
+ "potentialConflictFiles already differ locally. Compare them against the downloaded export only if the imported component behavior diverges."
3385
+ );
3386
+ }
3387
+ return {
3388
+ status: needsReview ? "needs_review" : "completed",
3389
+ message: messageParts.join(" "),
3390
+ reportPath,
3391
+ exportId,
3392
+ exportPath,
3393
+ manifestPath,
3394
+ originPageSlug: normalizedOriginSlug,
3395
+ blockId: normalizedBlockId,
3396
+ componentId: normalizedComponentId,
3397
+ selectedBlock,
3398
+ primaryComponentFile,
3399
+ manualInsertionTargets,
3400
+ newFiles,
3401
+ identicalFiles,
3402
+ conflictFiles,
3403
+ potentialDependencyFiles,
3404
+ potentialConflictFiles,
3405
+ addedDependencies,
3406
+ dependenciesVersionConflicts,
3407
+ guidance
3408
+ };
3409
+ }
3410
+
3411
+ // src/services/project-sync-service.ts
3412
+ function buildManifestPath(exportsRoot, exportId) {
3413
+ return path13.join(exportsRoot, `${exportId}.manifest.json`);
3414
+ }
3415
+ function normalizeSlug3(slug) {
3416
+ const trimmed = slug.trim();
3417
+ if (!trimmed || trimmed === "/") {
3418
+ return "/";
3419
+ }
3420
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
3421
+ return withLeadingSlash.replace(/\/+$/, "") || "/";
3422
+ }
3423
+ var ProjectSyncService = class {
3424
+ provider;
3425
+ projectRoot;
3426
+ targetProjectRoot;
3427
+ primeUiRoot;
3428
+ tempRoot;
3429
+ exportsRoot;
3430
+ pageDetailsBySlug;
3431
+ constructor(options) {
3432
+ this.projectRoot = options.projectRoot;
3433
+ this.targetProjectRoot = options.targetProjectRoot;
3434
+ this.provider = options.provider;
3435
+ this.pageDetailsBySlug = options.pageDetailsBySlug ?? /* @__PURE__ */ new Map();
3436
+ this.primeUiRoot = path13.join(this.projectRoot, ".primeui");
3437
+ this.tempRoot = path13.join(this.primeUiRoot, "temp");
3438
+ this.exportsRoot = path13.join(this.tempRoot, "exports");
3439
+ }
3440
+ async getProjectInfo(_context) {
3441
+ await this.ensureTempLayout();
3442
+ return this.provider.getProjectInfo();
3443
+ }
3444
+ async getProjectDescription(_context) {
3445
+ return this.provider.getProjectDescription();
3446
+ }
3447
+ async upsertProjectDescription(projectDescription, _context) {
3448
+ return this.provider.upsertProjectDescription(projectDescription);
3449
+ }
3450
+ async listProjectPages(_context) {
3451
+ return this.provider.listProjectPages();
3452
+ }
3453
+ async getProjectPage(pageId, _context) {
3454
+ return this.provider.getProjectPage(pageId);
3455
+ }
3456
+ async createProjectPage(input, _context) {
3457
+ return this.provider.createProjectPage(input);
3458
+ }
3459
+ async listProjectPageVariants(pageId, _context) {
3460
+ return this.provider.listProjectPageVariants(pageId);
3461
+ }
3462
+ async getProjectPageVariant(pageId, variantId, _context) {
3463
+ return this.provider.getProjectPageVariant(pageId, variantId);
3464
+ }
3465
+ async createProjectPageVariant(pageId, input, _context) {
3466
+ return this.provider.createProjectPageVariant(pageId, input);
3467
+ }
3468
+ async setProjectPageActiveVariant(pageId, variantId, _context) {
3469
+ return this.provider.setProjectPageActiveVariant(pageId, variantId);
3470
+ }
3471
+ async submitIssueReport(input, _context) {
3472
+ return this.provider.submitIssueReport(input);
3473
+ }
3474
+ async listExports(_context) {
3475
+ await this.ensureTempLayout();
3476
+ return this.provider.listExports();
3477
+ }
3478
+ async createExport(_context) {
3479
+ await this.ensureTempLayout();
3480
+ const result = await this.provider.createExport();
3481
+ const manifestPath = buildManifestPath(this.exportsRoot, result.export.id);
3482
+ await writeUtf8(manifestPath, `${JSON.stringify(result, null, 2)}
3483
+ `);
3484
+ return {
3485
+ ...result,
3486
+ guidance: {
3487
+ artifacts: [buildExportManifestArtifact(manifestPath)],
3488
+ requiredActions: `Call download_export with id "${result.export.id}" next before attempting any file transfer.`
3489
+ }
3490
+ };
3491
+ }
3492
+ async downloadExportById(id, _context) {
3493
+ await this.ensureTempLayout();
3494
+ const exportsList = await this.provider.listExports();
3495
+ const selected = exportsList.find((item) => item.id === id);
3496
+ if (!selected) {
3497
+ throw new Error(`Export not found: ${id}`);
3498
+ }
3499
+ const targetZipPath = path13.join(this.exportsRoot, `${id}.zip`);
3500
+ const targetProjectPath = path13.join(this.exportsRoot, id);
3501
+ const manifestPath = buildManifestPath(this.exportsRoot, id);
3502
+ let manifest;
3503
+ try {
3504
+ manifest = parsePrimeUiExportManifest(
3505
+ JSON.parse(await readFile7(manifestPath, "utf-8"))
3506
+ );
3507
+ } catch (error) {
3508
+ const reason = error instanceof Error ? error.message : String(error);
3509
+ throw new Error(
3510
+ `Export manifest is required for download id "${id}". Run create_export for this export before downloading. Details: ${reason}`
3511
+ );
3512
+ }
3513
+ if (manifest.export.id !== id) {
3514
+ throw new Error(
3515
+ `Export manifest mismatch for id "${id}". Run create_export and retry download.`
3516
+ );
3517
+ }
3518
+ await ensureDir(this.exportsRoot);
3519
+ await this.provider.downloadExportArchive(id, targetZipPath);
3520
+ await resetDir(targetProjectPath);
3521
+ await extractZip(targetZipPath, targetProjectPath);
3522
+ return {
3523
+ exportId: id,
3524
+ projectPath: targetProjectPath,
3525
+ manifestPath,
3526
+ guidance: {
3527
+ artifacts: [
3528
+ buildReferenceExportArtifact(targetProjectPath),
3529
+ buildExportManifestArtifact(manifestPath)
3530
+ ],
3531
+ requiredActions: "Use the export_manifest artifact as the source of truth for which files belong to the selected page, component, or exportable. Use the reference_export artifact as the golden example only when behavior diverges or integration is unclear. Then choose copy_page for full pages, copy_component for one selected block instance, or copy_exportable for shared exportables based on user intent."
3532
+ }
3533
+ };
3534
+ }
3535
+ async inspectPage(slug, _context) {
3536
+ await this.ensureTempLayout();
3537
+ const pageDetails = await this.provider.getProjectPageBySlug(slug);
3538
+ const reportPayload = buildInspectPageReport(pageDetails);
3539
+ this.pageDetailsBySlug.set(normalizeSlug3(slug), pageDetails);
3540
+ return {
3541
+ ...pageDetails,
3542
+ report: reportPayload.report,
3543
+ reportRows: reportPayload.reportRows
3544
+ };
3545
+ }
3546
+ async copyPage(originPageSlug, _context) {
3547
+ await this.ensureTempLayout();
3548
+ return copyPageFromExport({
3549
+ projectRoot: this.targetProjectRoot,
3550
+ exportsRoot: this.exportsRoot,
3551
+ originPageSlug
3552
+ });
3553
+ }
3554
+ async copyComponent(originPageSlug, blockId, componentId, _context) {
3555
+ await this.ensureTempLayout();
3556
+ const normalizedOriginSlug = normalizeSlug3(originPageSlug);
3557
+ const cachedPageDetails = this.pageDetailsBySlug.get(normalizedOriginSlug);
3558
+ const pageDetails = cachedPageDetails ?? await this.provider.getProjectPageBySlug(originPageSlug);
3559
+ if (!cachedPageDetails) {
3560
+ this.pageDetailsBySlug.set(normalizedOriginSlug, pageDetails);
3561
+ }
3562
+ return copyComponentFromExport({
3563
+ projectRoot: this.targetProjectRoot,
3564
+ exportsRoot: this.exportsRoot,
3565
+ originPageSlug,
3566
+ blockId,
3567
+ componentId,
3568
+ pageDetails
3569
+ });
3570
+ }
3571
+ async copyExportable(exportableKey, _context) {
3572
+ await this.ensureTempLayout();
3573
+ return copyExportableFromExport({
3574
+ projectRoot: this.targetProjectRoot,
3575
+ exportsRoot: this.exportsRoot,
3576
+ exportableKey
3577
+ });
3578
+ }
3579
+ async clearTemp(_context) {
3580
+ await resetDir(this.tempRoot);
3581
+ await ensureDir(this.exportsRoot);
3582
+ this.pageDetailsBySlug.clear();
3583
+ }
3584
+ async ensureTempLayout() {
3585
+ await ensureDir(this.primeUiRoot);
3586
+ await ensureDir(this.tempRoot);
3587
+ await ensureDir(this.exportsRoot);
3588
+ }
3589
+ };
3590
+
3591
+ // src/runtime.ts
3592
+ function defaultCreateProvider(options) {
3593
+ return new ApiProjectDataProvider(options);
3594
+ }
3595
+ function normalizeSlug4(slug) {
3596
+ const trimmed = slug.trim();
3597
+ if (!trimmed || trimmed === "/") {
3598
+ return "/";
3599
+ }
3600
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
3601
+ return withLeadingSlash.replace(/\/+$/, "") || "/";
3602
+ }
3603
+ var LazyProjectSyncSource = class {
3604
+ env;
3605
+ getCwd;
3606
+ createProvider;
3607
+ stickyProjectRoot;
3608
+ pageDetailsBySlug = /* @__PURE__ */ new Map();
3609
+ constructor(options = {}) {
3610
+ this.env = options.env ?? process.env;
3611
+ this.getCwd = options.getCwd ?? (() => process.cwd());
3612
+ this.createProvider = options.createProvider ?? defaultCreateProvider;
3613
+ }
3614
+ async withService(context, operation, options) {
3615
+ const service = await this.createProjectSyncService(context, options);
3616
+ return operation(service);
3617
+ }
3618
+ async withMutationService(context, operation, options) {
3619
+ const service = await this.createProjectSyncService(context, options);
3620
+ return operation(service);
3621
+ }
3622
+ async createProjectSyncService(context, options) {
3623
+ const requireTargetProjectRoot = options?.requireTargetProjectRoot ?? true;
3624
+ const resolvedProjectConfig = await resolvePrimeUiProjectConfig(
3625
+ {
3626
+ cwd: this.getCwd(),
3627
+ projectRootFromTool: context?.projectRoot,
3628
+ projectRootFromSticky: this.stickyProjectRoot,
3629
+ projectRootFromEnv: this.env.PRIMEUI_PROJECT_ROOT
3630
+ },
3631
+ {
3632
+ requireTargetProjectPath: requireTargetProjectRoot
3633
+ }
3634
+ );
3635
+ const projectRoot = resolvedProjectConfig.projectRoot;
3636
+ const targetProjectRoot = requireTargetProjectRoot ? resolvePrimeUiTargetProjectRoot(
3637
+ projectRoot,
3638
+ resolvedProjectConfig.projectConfig
3639
+ ) : projectRoot;
3640
+ const apiKey = await resolvePrimeUiApiKey({
3641
+ projectConfig: resolvedProjectConfig.projectConfig,
3642
+ apiKeyFromEnv: this.env.PRIMEUI_API_KEY
3643
+ });
3644
+ this.stickyProjectRoot = projectRoot;
3645
+ const provider = this.createProvider({
3646
+ apiKey,
3647
+ baseUrl: this.env.PRIMEUI_API_BASE_URL
3648
+ });
3649
+ return new ProjectSyncService({
3650
+ projectRoot,
3651
+ targetProjectRoot,
3652
+ provider,
3653
+ pageDetailsBySlug: this.pageDetailsBySlug
3654
+ });
3655
+ }
3656
+ async getProjectInfo(context) {
3657
+ return this.withService(
3658
+ context,
3659
+ (service) => service.getProjectInfo(context)
3660
+ );
3661
+ }
3662
+ async getProjectDescription(context) {
3663
+ return this.withService(
3664
+ context,
3665
+ (service) => service.getProjectDescription(context),
3666
+ {
3667
+ requireTargetProjectRoot: false
3668
+ }
3669
+ );
3670
+ }
3671
+ async upsertProjectDescription(projectDescription, context) {
3672
+ return this.withMutationService(
3673
+ context,
3674
+ async (service) => {
3675
+ const result = await service.upsertProjectDescription(
3676
+ projectDescription,
3677
+ context
3678
+ );
3679
+ this.pageDetailsBySlug.clear();
3680
+ return result;
3681
+ },
3682
+ {
3683
+ requireTargetProjectRoot: false
3684
+ }
3685
+ );
3686
+ }
3687
+ async listProjectPages(context) {
3688
+ return this.withService(
3689
+ context,
3690
+ (service) => service.listProjectPages(context),
3691
+ {
3692
+ requireTargetProjectRoot: false
3693
+ }
3694
+ );
3695
+ }
3696
+ async getProjectPage(pageId, context) {
3697
+ return this.withService(
3698
+ context,
3699
+ (service) => service.getProjectPage(pageId, context),
3700
+ {
3701
+ requireTargetProjectRoot: false
3702
+ }
3703
+ );
3704
+ }
3705
+ async createProjectPage(input, context) {
3706
+ return this.withMutationService(
3707
+ context,
3708
+ async (service) => {
3709
+ const result = await service.createProjectPage(input, context);
3710
+ this.pageDetailsBySlug.clear();
3711
+ return result;
3712
+ },
3713
+ {
3714
+ requireTargetProjectRoot: false
3715
+ }
3716
+ );
3717
+ }
3718
+ async listProjectPageVariants(pageId, context) {
3719
+ return this.withService(
3720
+ context,
3721
+ (service) => service.listProjectPageVariants(pageId, context),
3722
+ {
3723
+ requireTargetProjectRoot: false
3724
+ }
3725
+ );
3726
+ }
3727
+ async getProjectPageVariant(pageId, variantId, context) {
3728
+ return this.withService(
3729
+ context,
3730
+ (service) => service.getProjectPageVariant(pageId, variantId, context),
3731
+ {
3732
+ requireTargetProjectRoot: false
3733
+ }
3734
+ );
3735
+ }
3736
+ async createProjectPageVariant(pageId, input, context) {
3737
+ return this.withMutationService(
3738
+ context,
3739
+ async (service) => {
3740
+ const result = await service.createProjectPageVariant(
3741
+ pageId,
3742
+ input,
3743
+ context
3744
+ );
3745
+ this.pageDetailsBySlug.clear();
3746
+ return result;
3747
+ },
3748
+ {
3749
+ requireTargetProjectRoot: false
3750
+ }
3751
+ );
3752
+ }
3753
+ async setProjectPageActiveVariant(pageId, variantId, context) {
3754
+ return this.withMutationService(
3755
+ context,
3756
+ async (service) => {
3757
+ const result = await service.setProjectPageActiveVariant(
3758
+ pageId,
3759
+ variantId,
3760
+ context
3761
+ );
3762
+ this.pageDetailsBySlug.clear();
3763
+ return result;
3764
+ },
3765
+ {
3766
+ requireTargetProjectRoot: false
3767
+ }
3768
+ );
3769
+ }
3770
+ async submitIssueReport(input, context) {
3771
+ return this.withMutationService(
3772
+ context,
3773
+ (service) => service.submitIssueReport(input, context),
3774
+ {
3775
+ requireTargetProjectRoot: false
3776
+ }
3777
+ );
3778
+ }
3779
+ async listExports(context) {
3780
+ return this.withService(context, (service) => service.listExports(context));
3781
+ }
3782
+ async createExport(context) {
3783
+ return this.withService(
3784
+ context,
3785
+ (service) => service.createExport(context)
3786
+ );
3787
+ }
3788
+ async downloadExportById(id, context) {
3789
+ return this.withService(
3790
+ context,
3791
+ (service) => service.downloadExportById(id, context)
3792
+ );
3793
+ }
3794
+ async inspectPage(slug, context) {
3795
+ const result = await this.withService(
3796
+ context,
3797
+ (service) => service.inspectPage(slug, context)
3798
+ );
3799
+ this.pageDetailsBySlug.set(normalizeSlug4(slug), result);
3800
+ return result;
3801
+ }
3802
+ async copyPage(originPageSlug, context) {
3803
+ return this.withMutationService(
3804
+ context,
3805
+ (service) => service.copyPage(originPageSlug, context)
3806
+ );
3807
+ }
3808
+ async copyComponent(originPageSlug, blockId, componentId, context) {
3809
+ return this.withMutationService(
3810
+ context,
3811
+ (service) => service.copyComponent(originPageSlug, blockId, componentId, context)
3812
+ );
3813
+ }
3814
+ async copyExportable(exportableKey, context) {
3815
+ return this.withMutationService(
3816
+ context,
3817
+ (service) => service.copyExportable(exportableKey, context)
3818
+ );
3819
+ }
3820
+ async clearTemp(context) {
3821
+ const result = await this.withMutationService(
3822
+ context,
3823
+ (service) => service.clearTemp(context)
3824
+ );
3825
+ this.pageDetailsBySlug.clear();
3826
+ return result;
3827
+ }
3828
+ };
3829
+
3830
+ // src/server.ts
2529
3831
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3832
+ import { ZodError } from "zod/v3";
2530
3833
 
2531
3834
  // src/instructions.ts
2532
3835
  import { z as z4 } from "zod/v3";
@@ -2632,6 +3935,45 @@ var conflictFileSchema = z4.object({
2632
3935
  targetPath: z4.string().describe("Relative conflict target path inside user project root."),
2633
3936
  isBinary: z4.boolean().describe("True if conflict comparison was treated as binary data.")
2634
3937
  });
3938
+ var skippedCopyFileSchema = z4.object({
3939
+ targetPath: z4.string().describe(
3940
+ "Relative target path intentionally not copied into user project root."
3941
+ ),
3942
+ reason: z4.enum(["package_json_managed_as_dependencies"]).describe(
3943
+ "Machine-readable reason for skipping this file. package_json_managed_as_dependencies means package.json was handled as dependency guidance instead of direct file copy."
3944
+ )
3945
+ });
3946
+ var runtimeGuidanceArtifactSchema = z4.object({
3947
+ kind: z4.enum(["export_manifest", "reference_export", "conflict_report"]).describe(
3948
+ "Artifact kind. export_manifest is the local sidecar manifest and authoritative file-level contract. reference_export is the downloaded export project used as the golden example when behavior diverges or integration is unclear. conflict_report is the saved local diff report for conflicting files that represent required imported functionality."
3949
+ ),
3950
+ path: z4.string().describe("Absolute local path to the generated or referenced artifact."),
3951
+ description: z4.string().describe("Why this artifact matters for the current tool result.")
3952
+ });
3953
+ var runtimeGuidanceSchema = z4.object({
3954
+ artifacts: z4.array(runtimeGuidanceArtifactSchema).describe(
3955
+ "Concrete local artifacts produced or referenced by the tool result. Review these before continuing the import flow."
3956
+ ),
3957
+ requiredActions: z4.string().describe(
3958
+ "Mandatory next-step instruction derived from the actual tool result. Follow this text before continuing the flow."
3959
+ )
3960
+ });
3961
+ var copyComponentSelectedBlockSchema = z4.object({
3962
+ blockId: z4.string().describe("Selected block instance identifier."),
3963
+ componentId: z4.string().describe("Canonical component file identity from block.key."),
3964
+ slot: z4.string().nullable().describe("Optional slot value for the selected block instance."),
3965
+ props: z4.record(z4.unknown()).nullable().describe("Exact props payload for the selected block instance."),
3966
+ occurrenceIndex: z4.number().describe(
3967
+ "1-based position of this block among components with the same componentId."
3968
+ ),
3969
+ contentKey: z4.string().describe(
3970
+ "Content binding key for this selected instance. First instance uses componentId, later duplicates append -2, -3, and so on."
3971
+ )
3972
+ });
3973
+ var copyComponentGuidanceSchema = z4.object({
3974
+ requiredActions: z4.array(z4.string()).describe("Concrete next actions for finishing component transfer."),
3975
+ notes: z4.array(z4.string()).describe("Important semantics and caveats for this transfer result.")
3976
+ });
2635
3977
  var pageDetailsSchema = pageSchema.extend({
2636
3978
  pageInstruction: z4.string().nullable().describe(
2637
3979
  "Current instruction text for the active page variant. Null when no active variant is set."
@@ -2677,164 +4019,768 @@ var TRIGGER_PHRASES = [
2677
4019
  var WORKFLOW_SUMMARY = `
2678
4020
  WORKFLOW ORDER (always follow this sequence):
2679
4021
  1. clear_temp -> cleanup stale temp files from previous runs before starting new import flow
2680
- 2. get_project_info -> discover PrimeUI pages
4022
+ 2. get_project_info -> discover PrimeUI pages and shared exportables
2681
4023
  3. Reconcile PrimeUI pages with local project pages/routes/paths, then confirm target pages with user
2682
4024
  4. inspect_page -> inspect selected page components and produce a structured comparison table
2683
4025
  5. create_export -> generate export snapshot and local manifest with page file lists + shared exportables
2684
4026
  6. download_export -> download to temp directory
2685
- 7. copy_page -> copy one page safely (repeat for each selected page)
2686
- 8. Reconcile integration points and resolve reported conflicts, if any
4027
+ 7. copy_page, copy_component, or copy_exportable -> transfer one selected page, one selected block-level component, or one selected shared exportable safely
4028
+ 8. Reconcile integration points, dependency follow-up, and reported conflicts
2687
4029
  `.trim();
2688
4030
  var initialInstructions = `
2689
4031
  PrimeUI MCP enables importing pages from PrimeUI Studio into your local project.
2690
4032
 
2691
- TRIGGER EXAMPLES (semantic intent, not exact phrase matching): ${TRIGGER_PHRASES}
4033
+ TRIGGER EXAMPLES (semantic intent, not exact phrase matching): ${TRIGGER_PHRASES}
4034
+
4035
+ ${WORKFLOW_SUMMARY}
4036
+
4037
+ ADDITIONAL CAPABILITIES:
4038
+ - PrimeUI MCP also exposes atomic external API tools for project description, project pages, page variants, active-variant selection, and issue reports.
4039
+ - These atomic tools are OPTIONAL additional capabilities for extended scenarios.
4040
+ - They are NOT part of the primary import/export workflow above.
4041
+ - Do not replace get_project_info/create_export/download_export/copy_* with the additional API tools when the user wants the standard page import/export flow.
4042
+
4043
+ CRITICAL RULES FOR PAGE IMPORT:
4044
+ - Import is PAGE-BY-PAGE only. Never import the entire project at once.
4045
+ - Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
4046
+ - After get_project_info, always compare PrimeUI pages against the user's existing local project pages before proposing imports.
4047
+ - Reconciliation report MUST include: page presence match, slug/pagePath/componentsPath path match, and proposed action per page.
4048
+ - Decision matrix:
4049
+ - PrimeUI + local page exists -> candidate for update/review.
4050
+ - PrimeUI exists, local missing -> import candidate.
4051
+ - Local exists, PrimeUI missing -> custom local page, no action unless user asks.
4052
+ - Both exist but path mismatch -> ambiguous, export + compare before deciding.
4053
+ - For targeted component-level analysis on a specific page, call inspect_page before export/copy.
4054
+ - After inspect_page returns report table, supplement that table with local project page state:
4055
+ mark what already exists, what is missing, and what differs.
4056
+ - Before creating export, ask user which pages to add/update (specific pages or all missing pages) and keep that choice in thread context.
4057
+ - create_export response includes:
4058
+ - page-level manifest files per page ('pages[].manifest.files'),
4059
+ - shared exportables list ('export.exportables').
4060
+ Mention both to user after create_export, because this is the source of truth for copy decisions.
4061
+ - The downloaded export in .primeui/temp/ contains a full standalone Next.js project.
4062
+ Do NOT copy it wholesale.
4063
+ - After download_export, choose the copy tool based on what the user wants to transfer:
4064
+ - copy_page for page manifests,
4065
+ - copy_component for one selected block-level component instance,
4066
+ - copy_exportable for shared exportables.
4067
+ - Always call copy_page, copy_component, or copy_exportable instead of manual file copy.
4068
+ - copy_page performs safe copy only:
4069
+ - new files are copied,
4070
+ - identical files are reported,
4071
+ - conflicting files are NEVER overwritten and are returned with diff.
4072
+ - copy_page reads and validates files only from 'pages[].manifest.files' in local sidecar manifest.
4073
+ - copy_page may add only missing dependencies to user's package.json.
4074
+ It never upgrades existing dependency versions and never runs dependency installation.
4075
+ - copy_component uses inspect_page block data plus downloaded export artifacts.
4076
+ It copies only the canonical component file that can be resolved reliably for the selected componentId.
4077
+ - copy_component returns exact selected block props, occurrenceIndex, and contentKey for the chosen blockId.
4078
+ - copy_component treats manualInsertionTargets, potentialDependencyFiles, and potentialConflictFiles as review hints, not as mandatory full-page merge instructions.
4079
+ - copy_exportable reads and validates files only from 'export.exportables[].manifest.files' in local sidecar manifest.
4080
+ - copy_exportable returns status 'not_available' when the shared exportable is disabled, not configured, or currently has no copyable export files.
4081
+ - copy_exportable never copies package.json automatically.
4082
+ Dependency follow-up is returned via 'missedDependencies' and 'dependenciesVersionConflicts' and must be handled explicitly by the agent or user.
4083
+ - After page copy operations, inspect integration points (navigation configs, link menus, route registries, page indexes).
4084
+ - If integration pattern is obvious, apply it and explicitly mark it in the report with "\u26A0 integration update".
4085
+ - If integration pattern is unclear, ask the user before editing integration files.
4086
+ `.trim();
4087
+ var toolGetProjectInfo = {
4088
+ title: "PrimeUI Project Info",
4089
+ description: `ENTRY POINT for all PrimeUI import operations. Always start here. Returns project metadata plus a lightweight inventory of pages and shared exportables you can reason about before export.
4090
+
4091
+ WHEN TO USE: Call this FIRST when user wants to import, add, sync, pull, or transfer a page from PrimeUI. Intent examples (semantic intent, not exact phrase matching): ${TRIGGER_PHRASES}.
4092
+
4093
+ AFTER CALLING:
4094
+ - Scan the user's local project to discover existing pages/routes and related page component folders.
4095
+ - Build a reconciliation report between PrimeUI and local project with action labels:
4096
+ - Page exists in both PrimeUI and local project.
4097
+ - Page exists in PrimeUI but not in local project -> import candidate.
4098
+ - Page exists in local project but not in PrimeUI -> custom local page, no action by default.
4099
+ - Page exists in both but slug/pagePath/componentsPath mapping does not match -> ambiguous, requires export-based comparison before decision.
4100
+ - If user already requested specific pages, first verify those pages exist in PrimeUI and are isReadyToExport: true.
4101
+ - If user already requested specific shared exportables, verify those exportables exist in the lightweight exportables inventory and note their readiness before create_export.
4102
+ - If any requested page is not found or not ready, report this immediately and ask for replacement page choices.
4103
+ - Ask the user which pages or shared exportables to add or update now and keep this decision in thread context.
4104
+ - Import works one page or one shared exportable at a time. Multiple transfers are allowed, but each item is handled explicitly.
4105
+ - Only after user decision is clear, proceed to create_export.
4106
+
4107
+ ${WORKFLOW_SUMMARY}`,
4108
+ inputSchema: optionalProjectRootInputObjectSchema,
4109
+ outputSchema: z4.object({
4110
+ projectId: z4.string().describe(
4111
+ "PrimeUI project identifier associated with the API key and current import session context."
4112
+ ),
4113
+ projectName: z4.string().describe(
4114
+ "Human-readable PrimeUI project name for confirmations in chat."
4115
+ ),
4116
+ metadata: z4.record(z4.unknown()).describe(
4117
+ "Additional project metadata from PrimeUI. May include project-description and other context fields."
4118
+ ),
4119
+ exportables: z4.array(projectExportableSchema).describe(
4120
+ "Lightweight inventory of shared exportables (for example logo, cookie banner, announcement banner, theme, image assets). Use description + isReadyToExport to plan imports before create_export."
4121
+ ),
4122
+ pages: z4.array(pageSchema).describe(
4123
+ "All pages visible for this project context. Filter by user request and isReadyToExport before creating export."
4124
+ )
4125
+ }),
4126
+ annotations: {
4127
+ readOnlyHint: true,
4128
+ destructiveHint: false,
4129
+ idempotentHint: true,
4130
+ openWorldHint: true
4131
+ }
4132
+ };
4133
+ var toolInspectPage = {
4134
+ title: "PrimeUI Inspect Page",
4135
+ description: `Targeted inspection tool for one PrimeUI page. Returns page details, active variant (if present), raw components payload, and a formatted report table.
4136
+
4137
+ WHEN TO USE:
4138
+ - Call this after get_project_info when user wants component-level analysis for a specific page.
4139
+ - Use this before export/copy when user asks to compare or selectively update page sections.
4140
+
4141
+ AFTER CALLING:
4142
+ - Use report + reportRows to present the component table:
4143
+ Number (1-based), ComponentGroup (ComponentId), Title/Description, blockId.
4144
+ - Then supplement this table with the CURRENT local project page state:
4145
+ what already exists, what is missing, and what differs.
4146
+ - Do not assume componentId is unique. Use blockId as the primary component instance identifier.
4147
+
4148
+ If page has no active variant, components is null (not empty array), and report explains why.
4149
+
4150
+ ${WORKFLOW_SUMMARY}`,
4151
+ inputSchema: {
4152
+ projectRoot: projectRootInputSchema,
4153
+ pageSlug: z4.string().describe(
4154
+ "PrimeUI page slug to inspect, for example '/', '/pricing', '/docs/getting-started'."
4155
+ )
4156
+ },
4157
+ outputSchema: z4.object({
4158
+ page: pageDetailsSchema.describe(
4159
+ "Single PrimeUI page details resolved by slug with pageInstruction."
4160
+ ),
4161
+ variant: pageVariantSchema,
4162
+ components: z4.array(pageComponentSchema).nullable().describe(
4163
+ "Raw component instances from active variant. Null when page has no active variant."
4164
+ ),
4165
+ report: z4.string().describe(
4166
+ "Formatted report table generated by MCP from component payload (includes extracted title/description summary)."
4167
+ ),
4168
+ reportRows: z4.array(inspectPageReportRowSchema).describe(
4169
+ "Structured report rows with 1-based numbering and extracted title/description fields."
4170
+ )
4171
+ }),
4172
+ annotations: {
4173
+ readOnlyHint: true,
4174
+ destructiveHint: false,
4175
+ idempotentHint: true,
4176
+ openWorldHint: true
4177
+ }
4178
+ };
4179
+ var toolListExports = {
4180
+ title: "PrimeUI Export List",
4181
+ description: `Auxiliary tool to list previously created exports with their IDs, statuses, and creation dates. This is NOT the starting point for page import - start with get_project_info instead.
4182
+
4183
+ WHEN TO USE: This is an OPTIONAL helper tool, NOT part of the main import flow. Use it only when:
4184
+ - Something went wrong with an export and you need to check its status.
4185
+ - The user explicitly references a previous export by date or context.
4186
+ - You need to verify whether an export initiated via PrimeUI Studio UI has completed.
2692
4187
 
2693
- ${WORKFLOW_SUMMARY}
4188
+ Note: exports can be created both through create_export AND by the user directly in PrimeUI Studio UI. This tool shows all of them.
2694
4189
 
2695
- CRITICAL RULES FOR PAGE IMPORT:
2696
- - Import is PAGE-BY-PAGE only. Never import the entire project at once.
2697
- - Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
2698
- - After get_project_info, always compare PrimeUI pages against the user's existing local project pages before proposing imports.
2699
- - Reconciliation report MUST include: page presence match, slug/pagePath/componentsPath path match, and proposed action per page.
2700
- - Decision matrix:
2701
- - PrimeUI + local page exists -> candidate for update/review.
2702
- - PrimeUI exists, local missing -> import candidate.
2703
- - Local exists, PrimeUI missing -> custom local page, no action unless user asks.
2704
- - Both exist but path mismatch -> ambiguous, export + compare before deciding.
2705
- - For targeted component-level analysis on a specific page, call inspect_page before export/copy.
2706
- - After inspect_page returns report table, supplement that table with local project page state:
2707
- mark what already exists, what is missing, and what differs.
2708
- - Before creating export, ask user which pages to add/update (specific pages or all missing pages) and keep that choice in thread context.
2709
- - create_export response includes:
2710
- - page-level manifest files per page ('pages[].manifest.files'),
2711
- - shared exportables list ('export.exportables').
2712
- Mention both to user after create_export, because this is the source of truth for copy decisions.
2713
- - The downloaded export in .primeui/temp/ contains a full standalone Next.js project.
2714
- Do NOT copy it wholesale.
2715
- - Always call copy_page for each confirmed page instead of manual file copy.
2716
- - copy_page performs safe copy only:
2717
- - new files are copied,
2718
- - identical files are reported,
2719
- - conflicting files are NEVER overwritten and are returned with diff.
2720
- - copy_page reads and validates files only from 'pages[].manifest.files' in local sidecar manifest.
2721
- - copy_page may add only missing dependencies to user's package.json.
2722
- It never upgrades existing dependency versions and never runs dependency installation.
2723
- - After page copy operations, inspect integration points (navigation configs, link menus, route registries, page indexes).
2724
- - If integration pattern is obvious, apply it and explicitly mark it in the report with "\u26A0 integration update".
2725
- - If integration pattern is unclear, ask the user before editing integration files.
2726
- `.trim();
2727
- var toolGetProjectInfo = {
2728
- title: "PrimeUI Project Info",
2729
- description: `ENTRY POINT for all PrimeUI import operations. Always start here. Returns project metadata plus a lightweight inventory of pages and shared exportables you can reason about before export.
4190
+ Do NOT use this tool as a substitute for create_export. Always create a fresh export unless the user explicitly asks to use a previous one.
2730
4191
 
2731
- WHEN TO USE: Call this FIRST when user wants to import, add, sync, pull, or transfer a page from PrimeUI. Intent examples (semantic intent, not exact phrase matching): ${TRIGGER_PHRASES}.
4192
+ ${WORKFLOW_SUMMARY}`,
4193
+ inputSchema: optionalProjectRootInputObjectSchema,
4194
+ outputSchema: z4.object({
4195
+ exports: z4.array(exportItemSchema).describe(
4196
+ "Available exports sorted by API policy. Use item.id to download a specific prior export if user asks."
4197
+ )
4198
+ }),
4199
+ annotations: {
4200
+ readOnlyHint: true,
4201
+ destructiveHint: false,
4202
+ idempotentHint: true,
4203
+ openWorldHint: true
4204
+ }
4205
+ };
4206
+ var toolCreateExport = {
4207
+ title: "PrimeUI Create Export",
4208
+ description: `Create a new export snapshot from the current PrimeUI project state and wait for completion. Returns strict export manifest payload from API, including page-level file manifests and shared exportables.
4209
+
4210
+ WHEN TO USE: Call this AFTER the user has confirmed which pages they want to import via get_project_info. This tool is REQUIRED before download_export, unless the user explicitly and directly asked to download a specific previous export.
2732
4211
 
2733
4212
  AFTER CALLING:
2734
- - Scan the user's local project to discover existing pages/routes and related page component folders.
2735
- - Build a reconciliation report between PrimeUI and local project with action labels:
2736
- - Page exists in both PrimeUI and local project.
2737
- - Page exists in PrimeUI but not in local project -> import candidate.
2738
- - Page exists in local project but not in PrimeUI -> custom local page, no action by default.
2739
- - Page exists in both but slug/pagePath/componentsPath mapping does not match -> ambiguous, requires export-based comparison before decision.
2740
- - If user already requested specific pages, first verify those pages exist in PrimeUI and are isReadyToExport: true.
2741
- - If any requested page is not found or not ready, report this immediately and ask for replacement page choices.
2742
- - Ask the user which pages to add or update now (specific page list or all missing pages) and keep this decision in thread context.
2743
- - Import works per page. Multiple pages are allowed, but each page transfer is handled explicitly.
2744
- - Only after user decision is clear, proceed to create_export.
4213
+ - Verify that all user-selected pages are present in the export result AND have isReadyToExport: true.
4214
+ - Verify that any user-selected shared exportables are present in export.exportables.
4215
+ - If path mismatch pages were flagged during project-info reconciliation, ensure they are included for deeper comparison after download.
4216
+ - If any required page is missing or not ready, report and pause for user decision before proceeding.
4217
+ - Mention that pages can later be transferred via copy_page or copy_component, and shared exportables can be transferred separately via copy_exportable.
4218
+ - Local sidecar manifest is saved to .primeui/temp/exports/[exportId].manifest.json from this API payload.
4219
+ - Use the returned export ID to call download_export.
4220
+ - If guidance is present in the result, read guidance.artifacts and follow guidance.requiredActions before continuing.
2745
4221
 
2746
4222
  ${WORKFLOW_SUMMARY}`,
2747
4223
  inputSchema: optionalProjectRootInputObjectSchema,
2748
4224
  outputSchema: z4.object({
2749
- projectId: z4.string().describe(
2750
- "PrimeUI project identifier associated with the API key and current import session context."
4225
+ export: z4.object({
4226
+ id: z4.string().describe(
4227
+ "Freshly created export identifier. Use this as download_export input.id."
4228
+ ),
4229
+ status: exportStatusSchema,
4230
+ createdAt: z4.string().describe("Export creation timestamp in ISO-8601 UTC format."),
4231
+ expiresAt: z4.string().nullable().describe(
4232
+ "Export expiration timestamp in ISO-8601 UTC format, or null when export has no explicit expiration."
4233
+ ),
4234
+ summary: exportSummarySchema.describe(
4235
+ "Export run summary with total/successful/failed counters."
4236
+ ),
4237
+ exportables: z4.array(exportableSchema).describe(
4238
+ "Shared exportable manifest. These entries are independent from page-level manifests."
4239
+ )
4240
+ }).describe("Created export summary."),
4241
+ pages: z4.array(exportPageSchema).describe(
4242
+ "Strict page payload associated with this export, including per-page manifest files list."
4243
+ ),
4244
+ guidance: runtimeGuidanceSchema.optional().describe(
4245
+ "Mandatory runtime guidance for local artifacts created during export. Present only on successful tool results."
4246
+ )
4247
+ }),
4248
+ annotations: {
4249
+ readOnlyHint: false,
4250
+ destructiveHint: false,
4251
+ idempotentHint: false,
4252
+ openWorldHint: true
4253
+ }
4254
+ };
4255
+ var toolDownloadExport = {
4256
+ title: "PrimeUI Download Export",
4257
+ description: `Download a completed export into .primeui/temp/exports/[exportId]. Downloads and extracts the full export as a standalone Next.js project into the temp directory. This does NOT modify the user's project - files land only in .primeui/temp/.
4258
+
4259
+ Do NOT call this as the first step. Requires export ID from create_export. Start with get_project_info instead.
4260
+
4261
+ WHEN TO USE: Call this AFTER create_export has returned a completed export. Requires export ID from create_export so MCP can use the matching local sidecar manifest.
4262
+
4263
+ NOTE:
4264
+ - Download of an export ID without local sidecar manifest will fail.
4265
+ - For previous exports, create a fresh export first when possible.
4266
+
4267
+ AFTER DOWNLOAD:
4268
+ - Use manifestPath from this response as the contract for page slug/path mapping and manifest file lists in copy operations.
4269
+ - For each selected page, call copy_page with:
4270
+ - originPageSlug (required).
4271
+ - For each selected block-level component instance, call copy_component with:
4272
+ - originPageSlug (required),
4273
+ - blockId (required),
4274
+ - componentId (required).
4275
+ - For each selected shared exportable, call copy_exportable with:
4276
+ - exportableKey (required).
4277
+ - Treat conflicts as normal and expected. If copy_page returns needs_review, stop follow-up integration and resolve manually first.
4278
+ - If copy_component returns needs_review, stop and resolve the primary-file or direct-conflict issue before continuing block-level integration.
4279
+ - If copy_exportable returns not_available, stop and tell the user the shared exportable must be enabled or configured in PrimeUI first.
4280
+ - Do not force cleanup at flow end; keep downloaded export files available for validation and follow-up checks.
4281
+ - If guidance is present in the result, read guidance.artifacts and follow guidance.requiredActions before choosing the next copy tool.
4282
+
4283
+ ${WORKFLOW_SUMMARY}`,
4284
+ inputSchema: {
4285
+ projectRoot: projectRootInputSchema,
4286
+ id: z4.string().describe(
4287
+ "Export identifier to download. Prefer export.id from create_export; use list_exports only on explicit user request."
4288
+ )
4289
+ },
4290
+ outputSchema: z4.object({
4291
+ exportId: z4.string().describe(
4292
+ "Downloaded export identifier. Should match the requested input.id for traceability."
4293
+ ),
4294
+ projectPath: z4.string().describe(
4295
+ "Absolute local path to extracted export root in .primeui/temp/exports/[exportId]. Read files from here only."
4296
+ ),
4297
+ manifestPath: z4.string().describe(
4298
+ "Absolute path to sidecar export manifest file (.primeui/temp/exports/[exportId].manifest.json) saved by create_export and required by copy_page and copy_component."
4299
+ ),
4300
+ guidance: runtimeGuidanceSchema.optional().describe(
4301
+ "Mandatory runtime guidance for the downloaded export and manifest artifacts. Present only on successful tool results."
4302
+ )
4303
+ }),
4304
+ annotations: {
4305
+ readOnlyHint: false,
4306
+ destructiveHint: false,
4307
+ idempotentHint: true,
4308
+ openWorldHint: true
4309
+ }
4310
+ };
4311
+ var toolCopyPage = {
4312
+ title: "PrimeUI Copy Page",
4313
+ description: `Safely copy one page from downloaded export into the user's local project. Works only with local files in .primeui/temp/exports and does NOT call PrimeUI API.
4314
+
4315
+ WHEN TO USE:
4316
+ - Call this AFTER download_export.
4317
+ - Call once per selected page.
4318
+
4319
+ BEHAVIOR:
4320
+ - Reads export sidecar manifest to resolve originPageSlug -> pagePath/componentsPath.
4321
+ - Copies and validates files strictly from pages[].manifest.files for the selected page.
4322
+ - Never overwrites existing conflicting files.
4323
+ - Writes full conflict diffs to a local report file in .primeui/temp/exports/[exportId].copy-report-[copyId].json.
4324
+ - Reports:
4325
+ a) copied new files (relative target paths),
4326
+ b) identical existing files (relative target paths),
4327
+ c) conflicting files as lightweight metadata (relative target paths + binary flag, no inline diff),
4328
+ d) missing dependencies added to package.json,
4329
+ e) dependency version conflicts (report-only, no auto upgrade).
4330
+ - Adds only missing dependencies to package.json (dependencies/devDependencies/peerDependencies).
4331
+ - Never updates existing dependency versions and never runs install commands.
4332
+ - If status is needs_review, page import is NOT complete and manual review is required before continuing. This can be caused by file conflicts or dependency version conflicts.
4333
+ - If guidance is present in the result, read guidance.artifacts and follow guidance.requiredActions before continuing integration.
4334
+
4335
+ ${WORKFLOW_SUMMARY}`,
4336
+ inputSchema: {
4337
+ projectRoot: projectRootInputSchema,
4338
+ originPageSlug: z4.string().describe(
4339
+ "Source page slug from PrimeUI project pages list, for example '/', '/pricing', '/docs'."
4340
+ )
4341
+ },
4342
+ outputSchema: z4.object({
4343
+ status: z4.enum(["completed", "needs_review"]).describe(
4344
+ "Decision state for next step. needs_review means manual review is required before follow-up integration because of file conflicts or dependency version conflicts."
4345
+ ),
4346
+ message: z4.string().describe(
4347
+ "Decision-oriented summary that includes resolved reportPath and manual follow-up instructions when needed."
4348
+ ),
4349
+ reportPath: z4.string().describe(
4350
+ "Absolute path to saved local report with full conflict diffs and details."
4351
+ ),
4352
+ exportId: z4.string().describe("Resolved local export identifier used for copy operation."),
4353
+ exportPath: z4.string().describe("Absolute path to local extracted export project root."),
4354
+ originPageSlug: z4.string().describe("Normalized source page slug requested for copy."),
4355
+ sourcePagePath: z4.string().describe("Source page path relative to export root."),
4356
+ sourceComponentsPath: z4.string().describe("Source components directory relative to export root."),
4357
+ newFiles: z4.array(z4.string()).describe(
4358
+ "Relative target paths for new files copied into user project."
4359
+ ),
4360
+ identicalFiles: z4.array(z4.string()).describe("Relative target paths for existing byte-identical files."),
4361
+ conflictFiles: z4.array(conflictFileSchema).describe(
4362
+ "Files that already exist and differ from export version. Never overwritten."
4363
+ ),
4364
+ addedDependencies: z4.array(dependencyToAddSchema).describe(
4365
+ "Dependencies that were missing and have been added to user's package.json."
4366
+ ),
4367
+ dependenciesVersionConflicts: z4.array(dependencyVersionConflictSchema).describe(
4368
+ "Dependencies with version mismatch between export and user project. Report only."
4369
+ ),
4370
+ summary: z4.object({
4371
+ totalCandidateFiles: z4.number().describe(
4372
+ "Total number of candidate files considered for page copy from pages[].manifest.files."
4373
+ ),
4374
+ copiedFiles: z4.number().describe("Number of files copied as new."),
4375
+ identicalFiles: z4.number().describe("Number of files detected as identical."),
4376
+ conflictFiles: z4.number().describe("Number of conflicting files not copied."),
4377
+ addedDependencies: z4.number().describe("Count of dependencies added to package.json."),
4378
+ dependenciesVersionConflicts: z4.number().describe("Count of dependency version mismatches reported.")
4379
+ }),
4380
+ guidance: runtimeGuidanceSchema.optional().describe(
4381
+ "Mandatory runtime guidance for export artifacts and next steps after this page copy result. Present only on successful tool results."
4382
+ )
4383
+ }),
4384
+ annotations: {
4385
+ readOnlyHint: false,
4386
+ destructiveHint: false,
4387
+ idempotentHint: false,
4388
+ openWorldHint: false
4389
+ }
4390
+ };
4391
+ var toolCopyComponent = {
4392
+ title: "PrimeUI Copy Component",
4393
+ description: `Safely copy one block-level component from downloaded export into the user's local project. Works only with local files in .primeui/temp/exports and uses inspect_page semantics for block selection.
4394
+
4395
+ WHEN TO USE:
4396
+ - Call this AFTER download_export.
4397
+ - Call this when the user wants one selected block instance, not a full page transfer.
4398
+ - Pass both blockId and componentId so the tool can bind exact props for the selected instance while still resolving the canonical component file identity.
4399
+
4400
+ BEHAVIOR:
4401
+ - Reads export sidecar manifest to resolve originPageSlug -> pagePath/componentsPath.
4402
+ - Matches the selected block from inspect_page-compatible page details using blockId + componentId.
4403
+ - Computes selectedBlock.occurrenceIndex and selectedBlock.contentKey for duplicate component types on the same page.
4404
+ - Reuses the most recent inspect_page snapshot for selected block metadata when available instead of re-fetching live page details at copy time.
4405
+ - Resolves the primary component file from downloaded export using:
4406
+ - [componentsPath]/[componentId].tsx
4407
+ - [componentsPath]/[componentId]/index.tsx
4408
+ - Copies only the reliably resolved primary component file.
4409
+ - Scans the selected component tree for external package imports, adds missing dependencies to package.json, and reports version conflicts.
4410
+ - Never overwrites an existing conflicting primary component file.
4411
+ - Writes full direct and potential conflict details to a local report file in .primeui/temp/exports/[exportId].copy-component-report-[copyId].json.
4412
+ - Reports:
4413
+ a) selectedBlock with exact props for the chosen instance,
4414
+ b) primaryComponentFile when resolved,
4415
+ c) copied new or identical primary files,
4416
+ d) conflictFiles only for files actually attempted during copy,
4417
+ e) addedDependencies / dependenciesVersionConflicts for dependency follow-up,
4418
+ f) manualInsertionTargets for likely integration points,
4419
+ g) potentialDependencyFiles / potentialConflictFiles as prioritized hints for manual review.
4420
+ - If status is needs_review, stop and resolve the reported primary-file, missing-manifest-file, or dependency-version issue before treating the component transfer as complete.
4421
+
4422
+ ${WORKFLOW_SUMMARY}`,
4423
+ inputSchema: {
4424
+ projectRoot: projectRootInputSchema,
4425
+ originPageSlug: z4.string().describe(
4426
+ "Source page slug from PrimeUI project pages list, for example '/', '/pricing', '/docs'."
4427
+ ),
4428
+ blockId: z4.string().describe(
4429
+ "Selected block instance identifier from inspect_page output. Required when the same componentId appears multiple times on one page."
4430
+ ),
4431
+ componentId: z4.string().describe(
4432
+ "Selected component identity from inspect_page output (block.key). This defines the canonical component file identity."
4433
+ )
4434
+ },
4435
+ outputSchema: z4.object({
4436
+ status: z4.enum(["completed", "needs_review"]).describe(
4437
+ "Decision state for next step. needs_review means the primary component file could not be resolved safely or directly copied without conflict."
2751
4438
  ),
2752
- projectName: z4.string().describe(
2753
- "Human-readable PrimeUI project name for confirmations in chat."
4439
+ message: z4.string().describe(
4440
+ "Decision-oriented summary with reportPath and the next manual actions for completing component transfer."
2754
4441
  ),
2755
- metadata: z4.record(z4.unknown()).describe(
2756
- "Additional project metadata from PrimeUI. May include project-description and other context fields."
4442
+ reportPath: z4.string().describe(
4443
+ "Absolute path to saved local report with direct conflicts, potential conflicts, and selected block metadata."
2757
4444
  ),
2758
- exportables: z4.array(projectExportableSchema).describe(
2759
- "Lightweight inventory of shared exportables (for example logo, cookie banner, announcement banner, theme, image assets). Use description + isReadyToExport to plan imports before create_export."
4445
+ exportId: z4.string().describe("Resolved local export identifier used for component copy."),
4446
+ exportPath: z4.string().describe("Absolute path to local extracted export project root."),
4447
+ manifestPath: z4.string().describe(
4448
+ "Absolute path to the local sidecar export manifest used during component copy."
2760
4449
  ),
2761
- pages: z4.array(pageSchema).describe(
2762
- "All pages visible for this project context. Filter by user request and isReadyToExport before creating export."
4450
+ originPageSlug: z4.string().describe("Normalized source page slug requested for copy."),
4451
+ blockId: z4.string().describe("Normalized selected block instance identifier."),
4452
+ componentId: z4.string().describe("Normalized component file identity requested for copy."),
4453
+ selectedBlock: copyComponentSelectedBlockSchema.describe(
4454
+ "Resolved selected block instance with exact props and duplicate-instance metadata."
4455
+ ),
4456
+ primaryComponentFile: z4.string().nullable().describe(
4457
+ "Relative target path for the canonical component file when resolved, otherwise null."
4458
+ ),
4459
+ manualInsertionTargets: z4.array(z4.string()).describe(
4460
+ "Relative target paths that usually need manual wiring for this component instance, typically the page file."
4461
+ ),
4462
+ newFiles: z4.array(z4.string()).describe(
4463
+ "Relative target paths for new files copied into user project. For copy_component this usually contains only the primary component file."
4464
+ ),
4465
+ identicalFiles: z4.array(z4.string()).describe(
4466
+ "Relative target paths for existing byte-identical copied files."
4467
+ ),
4468
+ conflictFiles: z4.array(conflictFileSchema).describe(
4469
+ "Files that were actually attempted during copy and already exist with different content. Never overwritten."
4470
+ ),
4471
+ addedDependencies: z4.array(dependencyToAddSchema).describe(
4472
+ "Dependencies that were missing and have been added to user's package.json."
4473
+ ),
4474
+ dependenciesVersionConflicts: z4.array(dependencyVersionConflictSchema).describe(
4475
+ "Dependencies with version mismatch between export and user project. Report only."
4476
+ ),
4477
+ potentialDependencyFiles: z4.array(z4.string()).describe(
4478
+ "Prioritized hint files related to the selected page manifest. Review them first if the copied component is incomplete."
4479
+ ),
4480
+ potentialConflictFiles: z4.array(z4.string()).describe(
4481
+ "Potential dependency files that already differ locally. These are hints only and are not treated as direct copy conflicts."
4482
+ ),
4483
+ guidance: copyComponentGuidanceSchema.describe(
4484
+ "Structured follow-up guidance for completing block-level transfer with the selected props and related files."
2763
4485
  )
2764
4486
  }),
2765
4487
  annotations: {
2766
- readOnlyHint: true,
4488
+ readOnlyHint: false,
2767
4489
  destructiveHint: false,
2768
- idempotentHint: true,
2769
- openWorldHint: true
4490
+ idempotentHint: false,
4491
+ openWorldHint: false
2770
4492
  }
2771
4493
  };
2772
- var toolInspectPage = {
2773
- title: "PrimeUI Inspect Page",
2774
- description: `Targeted inspection tool for one PrimeUI page. Returns page details, active variant (if present), raw components payload, and a formatted report table.
4494
+ var toolCopyExportable = {
4495
+ title: "PrimeUI Copy Exportable",
4496
+ description: `Safely copy one shared exportable from downloaded export into the user's local project. Works only with local files in .primeui/temp/exports and does NOT call PrimeUI API.
2775
4497
 
2776
4498
  WHEN TO USE:
2777
- - Call this after get_project_info when user wants component-level analysis for a specific page.
2778
- - Use this before export/copy when user asks to compare or selectively update page sections.
2779
-
2780
- AFTER CALLING:
2781
- - Use report + reportRows to present the component table:
2782
- Number (1-based), ComponentGroup (ComponentId), Title/Description, blockId.
2783
- - Then supplement this table with the CURRENT local project page state:
2784
- what already exists, what is missing, and what differs.
2785
- - Do not assume componentId is unique. Use blockId as the primary component instance identifier.
4499
+ - Call this AFTER download_export.
4500
+ - Call once per selected shared exportable.
2786
4501
 
2787
- If page has no active variant, components is null (not empty array), and report explains why.
4502
+ BEHAVIOR:
4503
+ - Reads export sidecar manifest to resolve exportableKey -> export.exportables[] entry.
4504
+ - Copies and validates files strictly from export.exportables[].manifest.files for the selected shared exportable.
4505
+ - Never copies package.json automatically. package.json is treated as dependency guidance only.
4506
+ - Returns status not_available when the shared exportable is disabled, not configured, or currently has no copyable export files.
4507
+ - Never overwrites existing conflicting files.
4508
+ - Writes full conflict diffs to a local report file in .primeui/temp/exports/[exportId].copy-exportable-report-[copyId].json.
4509
+ - Reports:
4510
+ a) copied new files (relative target paths),
4511
+ b) identical existing files (relative target paths),
4512
+ c) conflicting files as lightweight metadata (relative target paths + binary flag, no inline diff),
4513
+ d) skipped files such as package.json,
4514
+ e) missedDependencies that must be handled explicitly,
4515
+ f) dependency version conflicts (report-only, no auto upgrade).
4516
+ - missedDependencies are a substantial and inseparable part of the shared exportable functionality. They are never added or installed automatically.
4517
+ - If status is needs_review, shared exportable transfer is NOT complete and manual review is required before continuing.
2788
4518
 
2789
4519
  ${WORKFLOW_SUMMARY}`,
2790
4520
  inputSchema: {
2791
4521
  projectRoot: projectRootInputSchema,
2792
- pageSlug: z4.string().describe(
2793
- "PrimeUI page slug to inspect, for example '/', '/pricing', '/docs/getting-started'."
4522
+ exportableKey: z4.string().describe(
4523
+ "Shared exportable key from PrimeUI export payload, for example 'logo', 'theme', 'cookieBanner', 'announcementBanner', or 'imageAssets'."
2794
4524
  )
2795
4525
  },
2796
4526
  outputSchema: z4.object({
2797
- page: pageDetailsSchema.describe(
2798
- "Single PrimeUI page details resolved by slug with pageInstruction."
4527
+ status: z4.enum(["completed", "needs_review", "not_available"]).describe(
4528
+ "Decision state for next step. not_available means the shared exportable must be enabled or configured in PrimeUI first. needs_review means file conflicts or dependency version conflicts require manual resolution."
2799
4529
  ),
2800
- variant: pageVariantSchema,
2801
- components: z4.array(pageComponentSchema).nullable().describe(
2802
- "Raw component instances from active variant. Null when page has no active variant."
4530
+ message: z4.string().describe(
4531
+ "Decision-oriented summary with resolved reportPath and mandatory follow-up guidance for availability, conflicts, or dependency handling."
2803
4532
  ),
2804
- report: z4.string().describe(
2805
- "Formatted report table generated by MCP from component payload (includes extracted title/description summary)."
4533
+ reportPath: z4.string().nullable().describe(
4534
+ "Absolute path to saved local report with full conflict diffs and details, or null when the exportable is not available for copy."
2806
4535
  ),
2807
- reportRows: z4.array(inspectPageReportRowSchema).describe(
2808
- "Structured report rows with 1-based numbering and extracted title/description fields."
2809
- )
4536
+ exportId: z4.string().describe("Resolved local export identifier used for copy operation."),
4537
+ exportPath: z4.string().describe("Absolute path to local extracted export project root."),
4538
+ exportableKey: z4.string().describe("Normalized shared exportable key requested for copy."),
4539
+ isReadyToExport: z4.boolean().describe(
4540
+ "Readiness flag from export manifest for the selected shared exportable."
4541
+ ),
4542
+ manifestSuccess: z4.boolean().describe(
4543
+ "Success flag from export manifest for the selected shared exportable."
4544
+ ),
4545
+ manifestMessage: z4.string().describe(
4546
+ "Manifest message from export payload for the selected shared exportable."
4547
+ ),
4548
+ manifestFiles: z4.array(z4.string()).describe(
4549
+ "Authoritative list of manifest files for this shared exportable before any copy-time filtering."
4550
+ ),
4551
+ newFiles: z4.array(z4.string()).describe(
4552
+ "Relative target paths for new files copied into user project."
4553
+ ),
4554
+ identicalFiles: z4.array(z4.string()).describe("Relative target paths for existing byte-identical files."),
4555
+ conflictFiles: z4.array(conflictFileSchema).describe(
4556
+ "Files that already exist and differ from export version. Never overwritten."
4557
+ ),
4558
+ skippedFiles: z4.array(skippedCopyFileSchema).describe(
4559
+ "Manifest files intentionally not copied, for example package.json which is handled via dependency guidance."
4560
+ ),
4561
+ missedDependencies: z4.array(dependencyToAddSchema).describe(
4562
+ "Required packages missing in user's package.json. These are never added or installed automatically."
4563
+ ),
4564
+ dependenciesVersionConflicts: z4.array(dependencyVersionConflictSchema).describe(
4565
+ "Dependencies with version mismatch between export and user project. Report only."
4566
+ ),
4567
+ summary: z4.object({
4568
+ totalManifestFiles: z4.number().describe(
4569
+ "Total number of manifest files listed for this shared exportable before copy-time filtering."
4570
+ ),
4571
+ totalCandidateFiles: z4.number().describe(
4572
+ "Total number of candidate files considered for direct copy after filtering skipped files such as package.json."
4573
+ ),
4574
+ copiedFiles: z4.number().describe("Number of files copied as new."),
4575
+ identicalFiles: z4.number().describe("Number of files detected as identical."),
4576
+ conflictFiles: z4.number().describe("Number of conflicting files not copied."),
4577
+ skippedFiles: z4.number().describe("Number of manifest files intentionally skipped."),
4578
+ missedDependencies: z4.number().describe("Count of missing dependency packages reported."),
4579
+ dependenciesVersionConflicts: z4.number().describe("Count of dependency version mismatches reported.")
4580
+ })
2810
4581
  }),
2811
4582
  annotations: {
2812
- readOnlyHint: true,
4583
+ readOnlyHint: false,
2813
4584
  destructiveHint: false,
2814
- idempotentHint: true,
2815
- openWorldHint: true
4585
+ idempotentHint: false,
4586
+ openWorldHint: false
2816
4587
  }
2817
4588
  };
2818
- var toolListExports = {
2819
- title: "PrimeUI Export List",
2820
- description: `Auxiliary tool to list previously created exports with their IDs, statuses, and creation dates. This is NOT the starting point for page import - start with get_project_info instead.
4589
+ var toolClearTemp = {
4590
+ title: "PrimeUI Clear Temp",
4591
+ description: `Delete all files in .primeui/temp/ and recreate empty temp structure.
2821
4592
 
2822
- WHEN TO USE: This is an OPTIONAL helper tool, NOT part of the main import flow. Use it only when:
2823
- - Something went wrong with an export and you need to check its status.
2824
- - The user explicitly references a previous export by date or context.
2825
- - You need to verify whether an export initiated via PrimeUI Studio UI has completed.
4593
+ Call this at the START of a new import flow, before get_project_info/create_export, to reset stale temp state from previous runs.
2826
4594
 
2827
- Note: exports can be created both through create_export AND by the user directly in PrimeUI Studio UI. This tool shows all of them.
4595
+ WHEN TO USE: Call once at the beginning of each new import flow. Do not call this between page imports in the same flow.
2828
4596
 
2829
- Do NOT use this tool as a substitute for create_export. Always create a fresh export unless the user explicitly asks to use a previous one.
4597
+ Safe to call multiple times. Only affects .primeui/temp/ directory - never touches the user's project files.
2830
4598
 
2831
4599
  ${WORKFLOW_SUMMARY}`,
2832
4600
  inputSchema: optionalProjectRootInputObjectSchema,
2833
4601
  outputSchema: z4.object({
2834
- exports: z4.array(exportItemSchema).describe(
2835
- "Available exports sorted by API policy. Use item.id to download a specific prior export if user asks."
4602
+ success: z4.boolean().describe(
4603
+ "True when temp cleanup completed and baseline temp structure was recreated."
2836
4604
  )
2837
4605
  }),
4606
+ annotations: {
4607
+ readOnlyHint: false,
4608
+ destructiveHint: false,
4609
+ idempotentHint: true,
4610
+ openWorldHint: false
4611
+ }
4612
+ };
4613
+ var projectDescriptionOutputSchema = z4.object({
4614
+ projectDescription: z4.string().describe(
4615
+ "Current shared project description string. Empty string means the description is not set."
4616
+ )
4617
+ });
4618
+ var projectApiWireframeStatusSchema = z4.enum(["pending", "generating", "ready", "error"]).describe(
4619
+ "Shared page readiness status from the external API. ready means export-selected content exists; generating means background AI work is still running."
4620
+ );
4621
+ var projectApiVariantSummarySchema = z4.object({
4622
+ id: z4.string().describe("Stable variant identifier within the scoped PrimeUI project."),
4623
+ name: z4.string().describe("Human-readable variant name."),
4624
+ isActive: z4.boolean().describe(
4625
+ "True when this variant is currently selected as the page active variant for export."
4626
+ ),
4627
+ isGenerating: z4.boolean().describe(
4628
+ "True when background AI generation is still running for this variant."
4629
+ )
4630
+ });
4631
+ var pageGenerationTypeSchema = z4.enum([
4632
+ "landing",
4633
+ "pricing",
4634
+ "blogIndex",
4635
+ "blogPost",
4636
+ "contact-us",
4637
+ "docs",
4638
+ "legal"
4639
+ ]).describe(
4640
+ "Supported PrimeUI page type. Must match the external API page type enum."
4641
+ );
4642
+ var projectApiPageResourceSchema = z4.object({
4643
+ id: z4.string().describe("Stable PrimeUI page identifier."),
4644
+ title: z4.string().describe("Human-readable page title."),
4645
+ slug: z4.string().describe("PrimeUI route slug for the page, for example '/', '/pricing'."),
4646
+ pageType: pageGenerationTypeSchema,
4647
+ isReadyToExport: z4.boolean().describe(
4648
+ "True when the page currently has an export-selected active variant."
4649
+ ),
4650
+ wireframeStatus: projectApiWireframeStatusSchema,
4651
+ variants: z4.array(projectApiVariantSummarySchema).describe("Lightweight variant summaries attached to the page resource.")
4652
+ });
4653
+ var projectApiPagesOutputSchema = z4.object({
4654
+ pages: z4.array(projectApiPageResourceSchema).describe(
4655
+ "Project pages payload returned by the external API. GET list may include many entries; create/get/set-active responses usually return one entry."
4656
+ )
4657
+ });
4658
+ var projectApiVariantsOutputSchema = z4.object({
4659
+ variants: z4.array(projectApiVariantSummarySchema).describe("Variant summaries for one selected page.")
4660
+ });
4661
+ var projectApiVariantSummaryResultSchema = z4.object({
4662
+ variant: projectApiVariantSummarySchema.describe(
4663
+ "Variant summary returned immediately after creation."
4664
+ )
4665
+ });
4666
+ var projectApiVariantOutputSchema = z4.object({
4667
+ variant: z4.object({
4668
+ id: z4.string().describe("Stable variant identifier within the selected page."),
4669
+ name: z4.string().describe("Human-readable variant name."),
4670
+ isActive: z4.boolean().describe("True when this variant is currently active for export."),
4671
+ isGenerating: z4.boolean().describe(
4672
+ "True while background AI generation for this variant is still running."
4673
+ ),
4674
+ components: z4.array(pageComponentSchema).describe("Resolved component payload for the selected variant.")
4675
+ })
4676
+ });
4677
+ var issueReportSubmitOutputSchema = z4.object({
4678
+ accepted: z4.literal(true).describe("Always true when the issue report was accepted by the API."),
4679
+ ticketId: z4.string().describe("Generated issue report identifier returned by the API.")
4680
+ });
4681
+ var createContentModeSchema = z4.enum(["ai", "empty", "components"]).describe(
4682
+ "Content creation mode. empty creates an empty resource, components uses ordered componentIds, ai queues generation from prompt."
4683
+ );
4684
+ var createPageOrVariantSchema = (shape) => z4.object(shape).superRefine((value, ctx) => {
4685
+ const contentMode = value.contentMode;
4686
+ const componentIds = value.componentIds;
4687
+ const prompt = value.prompt;
4688
+ if (contentMode === "ai") {
4689
+ if (Array.isArray(componentIds) && componentIds.length > 0) {
4690
+ ctx.addIssue({
4691
+ code: z4.ZodIssueCode.custom,
4692
+ message: "componentIds are not allowed in ai mode.",
4693
+ path: ["componentIds"]
4694
+ });
4695
+ }
4696
+ return;
4697
+ }
4698
+ if (contentMode === "empty") {
4699
+ if (Array.isArray(componentIds) && componentIds.length > 0) {
4700
+ ctx.addIssue({
4701
+ code: z4.ZodIssueCode.custom,
4702
+ message: "componentIds are not allowed in empty mode.",
4703
+ path: ["componentIds"]
4704
+ });
4705
+ }
4706
+ if (typeof prompt === "string" && prompt.trim()) {
4707
+ ctx.addIssue({
4708
+ code: z4.ZodIssueCode.custom,
4709
+ message: "prompt is not allowed in empty mode.",
4710
+ path: ["prompt"]
4711
+ });
4712
+ }
4713
+ return;
4714
+ }
4715
+ if (!Array.isArray(componentIds) || componentIds.length === 0) {
4716
+ ctx.addIssue({
4717
+ code: z4.ZodIssueCode.custom,
4718
+ message: "componentIds are required in components mode.",
4719
+ path: ["componentIds"]
4720
+ });
4721
+ }
4722
+ if (typeof prompt === "string" && prompt.trim()) {
4723
+ ctx.addIssue({
4724
+ code: z4.ZodIssueCode.custom,
4725
+ message: "prompt is not allowed in components mode.",
4726
+ path: ["prompt"]
4727
+ });
4728
+ }
4729
+ });
4730
+ var projectDescriptionUpsertInputSchema = z4.object({
4731
+ projectRoot: projectRootInputSchema,
4732
+ projectDescription: z4.string().describe(
4733
+ "Project description to store. Empty or whitespace-only input clears the saved description."
4734
+ )
4735
+ });
4736
+ var projectPageCreateBaseInputSchema = z4.object({
4737
+ projectRoot: projectRootInputSchema,
4738
+ slug: z4.string().describe("Route slug for the new page, for example '/pricing'."),
4739
+ title: z4.string().describe("Human-readable page title."),
4740
+ pageType: pageGenerationTypeSchema,
4741
+ contentMode: createContentModeSchema,
4742
+ componentIds: z4.array(z4.string()).optional().describe(
4743
+ "Ordered component IDs for components mode. Omit for empty or ai mode."
4744
+ ),
4745
+ prompt: z4.string().optional().describe("Optional AI prompt used only in ai mode.")
4746
+ });
4747
+ var projectPageCreateValidationSchema = createPageOrVariantSchema(
4748
+ projectPageCreateBaseInputSchema.shape
4749
+ );
4750
+ var projectPageVariantCreateBaseInputSchema = z4.object({
4751
+ projectRoot: projectRootInputSchema,
4752
+ pageId: z4.string().describe("Target page identifier for the new variant."),
4753
+ name: z4.string().optional().describe(
4754
+ "Optional explicit variant name. When omitted, Studio assigns one."
4755
+ ),
4756
+ contentMode: createContentModeSchema,
4757
+ componentIds: z4.array(z4.string()).optional().describe(
4758
+ "Ordered component IDs for components mode. Omit for empty or ai mode."
4759
+ ),
4760
+ prompt: z4.string().optional().describe("Optional AI prompt used only in ai mode.")
4761
+ });
4762
+ var projectPageVariantCreateValidationSchema = createPageOrVariantSchema(projectPageVariantCreateBaseInputSchema.shape);
4763
+ var projectIssueReportSubmitInputSchema = z4.object({
4764
+ projectRoot: projectRootInputSchema,
4765
+ reportText: z4.string().describe("Issue report text to submit for the scoped project."),
4766
+ tags: z4.array(z4.string()).optional().describe(
4767
+ "Optional issue report tags. Empty values are trimmed and dropped by the API."
4768
+ )
4769
+ });
4770
+ var toolProjectDescriptionGet = {
4771
+ title: "PrimeUI Project Description Get",
4772
+ description: `Atomic external API helper. Returns the current shared project description for the scoped PrimeUI project.
4773
+
4774
+ WHEN TO USE:
4775
+ - Use this when the caller needs only the project description text.
4776
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4777
+ - Do not treat this as part of the page download or export flow.
4778
+
4779
+ AFTER CALLING:
4780
+ - Use the returned projectDescription directly.
4781
+ - Empty string means the project currently has no saved description.`,
4782
+ inputSchema: optionalProjectRootInputObjectSchema,
4783
+ outputSchema: projectDescriptionOutputSchema,
2838
4784
  annotations: {
2839
4785
  readOnlyHint: true,
2840
4786
  destructiveHint: false,
@@ -2842,196 +4788,198 @@ ${WORKFLOW_SUMMARY}`,
2842
4788
  openWorldHint: true
2843
4789
  }
2844
4790
  };
2845
- var toolCreateExport = {
2846
- title: "PrimeUI Create Export",
2847
- description: `Create a new export snapshot from the current PrimeUI project state and wait for completion. Returns strict export manifest payload from API, including page-level file manifests and shared exportables.
4791
+ var toolProjectDescriptionUpsert = {
4792
+ title: "PrimeUI Project Description Upsert",
4793
+ description: `Atomic external API helper. Creates, updates, or clears the shared project description for the scoped PrimeUI project.
2848
4794
 
2849
- WHEN TO USE: Call this AFTER the user has confirmed which pages they want to import via get_project_info. This tool is REQUIRED before download_export, unless the user explicitly and directly asked to download a specific previous export.
4795
+ WHEN TO USE:
4796
+ - Use this only when the caller explicitly wants to change the shared project description.
4797
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4798
+ - Whitespace-only input is valid and clears the saved description.
2850
4799
 
2851
4800
  AFTER CALLING:
2852
- - Verify that all user-selected pages are present in the export result AND have isReadyToExport: true.
2853
- - If path mismatch pages were flagged during project-info reconciliation, ensure they are included for deeper comparison after download.
2854
- - If any required page is missing or not ready, report and pause for user decision before proceeding.
2855
- - Mention that shared exportables are available in export.exportables and can be transferred separately if needed.
2856
- - Local sidecar manifest is saved to .primeui/temp/exports/[exportId].manifest.json from this API payload.
2857
- - Use the returned export ID to call download_export.
4801
+ - Treat the returned projectDescription as the source of truth after persistence.`,
4802
+ inputSchema: projectDescriptionUpsertInputSchema,
4803
+ outputSchema: projectDescriptionOutputSchema,
4804
+ annotations: {
4805
+ readOnlyHint: false,
4806
+ destructiveHint: false,
4807
+ idempotentHint: false,
4808
+ openWorldHint: true
4809
+ }
4810
+ };
4811
+ var toolProjectPagesList = {
4812
+ title: "PrimeUI Project Pages List",
4813
+ description: `Atomic external API helper. Returns the current page inventory with page-level readiness and lightweight variant summaries.
2858
4814
 
2859
- ${WORKFLOW_SUMMARY}`,
4815
+ WHEN TO USE:
4816
+ - Use this when the caller needs page inventory from the external API itself.
4817
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4818
+ - Do not replace get_project_info with this tool when the goal is the standard import/export flow; get_project_info remains the primary entry point there.
4819
+
4820
+ AFTER CALLING:
4821
+ - Use the returned page resources as-is.
4822
+ - If the caller needs one specific page by ID, use project_page_get.`,
2860
4823
  inputSchema: optionalProjectRootInputObjectSchema,
2861
- outputSchema: z4.object({
2862
- export: z4.object({
2863
- id: z4.string().describe(
2864
- "Freshly created export identifier. Use this as download_export input.id."
2865
- ),
2866
- status: exportStatusSchema,
2867
- createdAt: z4.string().describe("Export creation timestamp in ISO-8601 UTC format."),
2868
- expiresAt: z4.string().nullable().describe(
2869
- "Export expiration timestamp in ISO-8601 UTC format, or null when export has no explicit expiration."
2870
- ),
2871
- summary: exportSummarySchema.describe(
2872
- "Export run summary with total/successful/failed counters."
2873
- ),
2874
- exportables: z4.array(exportableSchema).describe(
2875
- "Shared exportable manifest. These entries are independent from page-level manifests."
2876
- )
2877
- }).describe("Created export summary."),
2878
- pages: z4.array(exportPageSchema).describe(
2879
- "Strict page payload associated with this export, including per-page manifest files list."
2880
- )
2881
- }),
4824
+ outputSchema: projectApiPagesOutputSchema,
4825
+ annotations: {
4826
+ readOnlyHint: true,
4827
+ destructiveHint: false,
4828
+ idempotentHint: true,
4829
+ openWorldHint: true
4830
+ }
4831
+ };
4832
+ var toolProjectPageGet = {
4833
+ title: "PrimeUI Project Page Get",
4834
+ description: `Atomic external API helper. Returns one page by pageId using the same shape as project_pages_list.
4835
+
4836
+ WHEN TO USE:
4837
+ - Use this when the caller already knows the target pageId.
4838
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4839
+
4840
+ AFTER CALLING:
4841
+ - The response stays endpoint-aligned and returns a pages array envelope from the external API.`,
4842
+ inputSchema: {
4843
+ projectRoot: projectRootInputSchema,
4844
+ pageId: z4.string().describe("PrimeUI page identifier to fetch from the external API.")
4845
+ },
4846
+ outputSchema: projectApiPagesOutputSchema,
4847
+ annotations: {
4848
+ readOnlyHint: true,
4849
+ destructiveHint: false,
4850
+ idempotentHint: true,
4851
+ openWorldHint: true
4852
+ }
4853
+ };
4854
+ var toolProjectPageCreate = {
4855
+ title: "PrimeUI Project Page Create",
4856
+ description: `Atomic external API helper. Creates a new page and its initial variant.
4857
+
4858
+ WHEN TO USE:
4859
+ - Use this only when the caller explicitly wants to create a page through the external API.
4860
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4861
+ - Do not encode follow-up chains here. Creating more variants later is a possible next step, not a requirement.
4862
+
4863
+ AFTER CALLING:
4864
+ - The returned pages payload reflects the immediate API result for the created page.
4865
+ - If contentMode is ai, the initial variant may still be generating.`,
4866
+ inputSchema: projectPageCreateBaseInputSchema,
4867
+ outputSchema: projectApiPagesOutputSchema,
4868
+ annotations: {
4869
+ readOnlyHint: false,
4870
+ destructiveHint: false,
4871
+ idempotentHint: false,
4872
+ openWorldHint: true
4873
+ }
4874
+ };
4875
+ var toolProjectPageVariantsList = {
4876
+ title: "PrimeUI Project Page Variants List",
4877
+ description: `Atomic external API helper. Returns lightweight variant summaries for one page.
4878
+
4879
+ WHEN TO USE:
4880
+ - Use this when the caller needs the variant inventory for an existing pageId.
4881
+ - This is an ADDITIONAL capability outside the primary import/export workflow.`,
4882
+ inputSchema: {
4883
+ projectRoot: projectRootInputSchema,
4884
+ pageId: z4.string().describe("PrimeUI page identifier whose variants should be listed.")
4885
+ },
4886
+ outputSchema: projectApiVariantsOutputSchema,
2882
4887
  annotations: {
2883
- readOnlyHint: false,
4888
+ readOnlyHint: true,
2884
4889
  destructiveHint: false,
2885
- idempotentHint: false,
4890
+ idempotentHint: true,
2886
4891
  openWorldHint: true
2887
4892
  }
2888
4893
  };
2889
- var toolDownloadExport = {
2890
- title: "PrimeUI Download Export",
2891
- description: `Download a completed export into .primeui/temp/exports/[exportId]. Downloads and extracts the full export as a standalone Next.js project into the temp directory. This does NOT modify the user's project - files land only in .primeui/temp/.
2892
-
2893
- Do NOT call this as the first step. Requires export ID from create_export. Start with get_project_info instead.
2894
-
2895
- WHEN TO USE: Call this AFTER create_export has returned a completed export. Requires export ID from create_export so MCP can use the matching local sidecar manifest.
2896
-
2897
- NOTE:
2898
- - Download of an export ID without local sidecar manifest will fail.
2899
- - For previous exports, create a fresh export first when possible.
2900
-
2901
- AFTER DOWNLOAD:
2902
- - Use manifestPath from this response as the contract for page slug/path mapping and manifest file lists in copy operations.
2903
- - For each selected page, call copy_page with:
2904
- - originPageSlug (required).
2905
- - Treat conflicts as normal and expected. If copy_page returns needs_review, stop follow-up integration and resolve manually first.
2906
- - Do not force cleanup at flow end; keep downloaded export files available for validation and follow-up checks.
4894
+ var toolProjectPageVariantGet = {
4895
+ title: "PrimeUI Project Page Variant Get",
4896
+ description: `Atomic external API helper. Returns one variant with its component payload.
2907
4897
 
2908
- ${WORKFLOW_SUMMARY}`,
4898
+ WHEN TO USE:
4899
+ - Use this when the caller already knows both pageId and variantId.
4900
+ - This is an ADDITIONAL capability outside the primary import/export workflow.`,
2909
4901
  inputSchema: {
2910
4902
  projectRoot: projectRootInputSchema,
2911
- id: z4.string().describe(
2912
- "Export identifier to download. Prefer export.id from create_export; use list_exports only on explicit user request."
2913
- )
4903
+ pageId: z4.string().describe("PrimeUI page identifier that owns the variant."),
4904
+ variantId: z4.string().describe("PrimeUI variant identifier to fetch.")
2914
4905
  },
2915
- outputSchema: z4.object({
2916
- exportId: z4.string().describe(
2917
- "Downloaded export identifier. Should match the requested input.id for traceability."
2918
- ),
2919
- projectPath: z4.string().describe(
2920
- "Absolute local path to extracted export root in .primeui/temp/exports/[exportId]. Read files from here only."
2921
- ),
2922
- manifestPath: z4.string().describe(
2923
- "Absolute path to sidecar export manifest file (.primeui/temp/exports/[exportId].manifest.json) saved by create_export and required by copy_page."
2924
- )
2925
- }),
4906
+ outputSchema: projectApiVariantOutputSchema,
2926
4907
  annotations: {
2927
- readOnlyHint: false,
4908
+ readOnlyHint: true,
2928
4909
  destructiveHint: false,
2929
4910
  idempotentHint: true,
2930
4911
  openWorldHint: true
2931
4912
  }
2932
4913
  };
2933
- var toolCopyPage = {
2934
- title: "PrimeUI Copy Page",
2935
- description: `Safely copy one page from downloaded export into the user's local project. Works only with local files in .primeui/temp/exports and does NOT call PrimeUI API.
4914
+ var toolProjectPageVariantCreate = {
4915
+ title: "PrimeUI Project Page Variant Create",
4916
+ description: `Atomic external API helper. Creates one new variant for an existing page.
2936
4917
 
2937
4918
  WHEN TO USE:
2938
- - Call this AFTER download_export.
2939
- - Call once per selected page.
4919
+ - Use this only when the caller explicitly wants a new variant on an existing page.
4920
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4921
+ - The new variant is created independently. Becoming active is a separate decision handled by project_page_active_variant_set.
2940
4922
 
2941
- BEHAVIOR:
2942
- - Reads export sidecar manifest to resolve originPageSlug -> pagePath/componentsPath.
2943
- - Copies and validates files strictly from pages[].manifest.files for the selected page.
2944
- - Never overwrites existing conflicting files.
2945
- - Writes full conflict diffs to a local report file in .primeui/temp/exports/[exportId].copy-report-[copyId].json.
2946
- - Reports:
2947
- a) copied new files (relative target paths),
2948
- b) identical existing files (relative target paths),
2949
- c) conflicting files as lightweight metadata (relative target paths + binary flag, no inline diff),
2950
- d) missing dependencies added to package.json,
2951
- e) dependency version conflicts (report-only, no auto upgrade).
2952
- - Adds only missing dependencies to package.json (dependencies/devDependencies/peerDependencies).
2953
- - Never updates existing dependency versions and never runs install commands.
2954
- - If status is needs_review, page import is NOT complete and manual review is required before continuing. This can be caused by file conflicts or dependency version conflicts.
4923
+ AFTER CALLING:
4924
+ - Treat the returned variant payload as the immediate API result.
4925
+ - The create endpoint returns a lightweight variant summary only, not the full component payload.
4926
+ - If contentMode is ai, the variant may still be generating.`,
4927
+ inputSchema: projectPageVariantCreateBaseInputSchema,
4928
+ outputSchema: projectApiVariantSummaryResultSchema,
4929
+ annotations: {
4930
+ readOnlyHint: false,
4931
+ destructiveHint: false,
4932
+ idempotentHint: false,
4933
+ openWorldHint: true
4934
+ }
4935
+ };
4936
+ var toolProjectPageActiveVariantSet = {
4937
+ title: "PrimeUI Project Page Active Variant Set",
4938
+ description: `Atomic external API helper. Sets the export-selected active variant for one page.
2955
4939
 
2956
- ${WORKFLOW_SUMMARY}`,
4940
+ WHEN TO USE:
4941
+ - Use this only when the caller explicitly wants to switch the active variant.
4942
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4943
+ - This tool performs exactly one operation: set the active variant. It does not create variants and does not export anything.`,
2957
4944
  inputSchema: {
2958
4945
  projectRoot: projectRootInputSchema,
2959
- originPageSlug: z4.string().describe(
2960
- "Source page slug from PrimeUI project pages list, for example '/', '/pricing', '/docs'."
2961
- )
4946
+ pageId: z4.string().describe("PrimeUI page identifier whose active variant should change."),
4947
+ variantId: z4.string().describe("Variant identifier that should become active for export.")
2962
4948
  },
2963
- outputSchema: z4.object({
2964
- status: z4.enum(["completed", "needs_review"]).describe(
2965
- "Decision state for next step. needs_review means manual review is required before follow-up integration because of file conflicts or dependency version conflicts."
2966
- ),
2967
- message: z4.string().describe(
2968
- "Decision-oriented summary that includes resolved reportPath and manual follow-up instructions when needed."
2969
- ),
2970
- reportPath: z4.string().describe(
2971
- "Absolute path to saved local report with full conflict diffs and details."
2972
- ),
2973
- exportId: z4.string().describe("Resolved local export identifier used for copy operation."),
2974
- exportPath: z4.string().describe("Absolute path to local extracted export project root."),
2975
- originPageSlug: z4.string().describe("Normalized source page slug requested for copy."),
2976
- sourcePagePath: z4.string().describe("Source page path relative to export root."),
2977
- sourceComponentsPath: z4.string().describe("Source components directory relative to export root."),
2978
- newFiles: z4.array(z4.string()).describe("Relative target paths for new files copied into user project."),
2979
- identicalFiles: z4.array(z4.string()).describe("Relative target paths for existing byte-identical files."),
2980
- conflictFiles: z4.array(conflictFileSchema).describe(
2981
- "Files that already exist and differ from export version. Never overwritten."
2982
- ),
2983
- addedDependencies: z4.array(dependencyToAddSchema).describe(
2984
- "Dependencies that were missing and have been added to user's package.json."
2985
- ),
2986
- dependenciesVersionConflicts: z4.array(dependencyVersionConflictSchema).describe(
2987
- "Dependencies with version mismatch between export and user project. Report only."
2988
- ),
2989
- summary: z4.object({
2990
- totalCandidateFiles: z4.number().describe(
2991
- "Total number of candidate files considered for page copy from pages[].manifest.files."
2992
- ),
2993
- copiedFiles: z4.number().describe("Number of files copied as new."),
2994
- identicalFiles: z4.number().describe("Number of files detected as identical."),
2995
- conflictFiles: z4.number().describe("Number of conflicting files not copied."),
2996
- addedDependencies: z4.number().describe("Count of dependencies added to package.json."),
2997
- dependenciesVersionConflicts: z4.number().describe("Count of dependency version mismatches reported.")
2998
- })
2999
- }),
4949
+ outputSchema: projectApiPagesOutputSchema,
3000
4950
  annotations: {
3001
4951
  readOnlyHint: false,
3002
4952
  destructiveHint: false,
3003
4953
  idempotentHint: false,
3004
- openWorldHint: false
4954
+ openWorldHint: true
3005
4955
  }
3006
4956
  };
3007
- var toolClearTemp = {
3008
- title: "PrimeUI Clear Temp",
3009
- description: `Delete all files in .primeui/temp/ and recreate empty temp structure.
3010
-
3011
- Call this at the START of a new import flow, before get_project_info/create_export, to reset stale temp state from previous runs.
3012
-
3013
- WHEN TO USE: Call once at the beginning of each new import flow. Do not call this between page imports in the same flow.
4957
+ var toolProjectIssueReportSubmit = {
4958
+ title: "PrimeUI Project Issue Report Submit",
4959
+ description: `Atomic external API helper. Submits an issue report for the scoped PrimeUI project.
3014
4960
 
3015
- Safe to call multiple times. Only affects .primeui/temp/ directory - never touches the user's project files.
4961
+ WHEN TO USE:
4962
+ - Use this when the caller wants to send a focused issue report through the external API.
4963
+ - This is an ADDITIONAL capability outside the primary import/export workflow.
4964
+ - This tool is independent from export, page, and variant flows.
3016
4965
 
3017
- ${WORKFLOW_SUMMARY}`,
3018
- inputSchema: optionalProjectRootInputObjectSchema,
3019
- outputSchema: z4.object({
3020
- success: z4.boolean().describe(
3021
- "True when temp cleanup completed and baseline temp structure was recreated."
3022
- )
3023
- }),
4966
+ AFTER CALLING:
4967
+ - Use ticketId for user-facing confirmation or follow-up references.`,
4968
+ inputSchema: projectIssueReportSubmitInputSchema,
4969
+ outputSchema: issueReportSubmitOutputSchema,
3024
4970
  annotations: {
3025
4971
  readOnlyHint: false,
3026
4972
  destructiveHint: false,
3027
- idempotentHint: true,
3028
- openWorldHint: false
4973
+ idempotentHint: false,
4974
+ openWorldHint: true
3029
4975
  }
3030
4976
  };
3031
4977
 
3032
4978
  // src/server.ts
3033
4979
  var ZERO_ARG_PROJECT_ROOT_TOOLS = /* @__PURE__ */ new Set([
3034
4980
  "get_project_info",
4981
+ "project_description_get",
4982
+ "project_pages_list",
3035
4983
  "list_exports",
3036
4984
  "create_export",
3037
4985
  "clear_temp"
@@ -3070,6 +5018,18 @@ Hint: ${error.hint}` : `primeui mcp error [${error.code}]: ${error.message}`;
3070
5018
  content: [{ type: "text", text }]
3071
5019
  };
3072
5020
  }
5021
+ if (error instanceof ZodError) {
5022
+ const details = error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
5023
+ return {
5024
+ isError: true,
5025
+ content: [
5026
+ {
5027
+ type: "text",
5028
+ text: `primeui mcp error [INVALID_INPUT]: ${details}`
5029
+ }
5030
+ ]
5031
+ };
5032
+ }
3073
5033
  const message = error instanceof Error ? error.message : String(error);
3074
5034
  return {
3075
5035
  isError: true,
@@ -3111,6 +5071,150 @@ function createPrimeUiMcpServer(source) {
3111
5071
  }
3112
5072
  }
3113
5073
  );
5074
+ server.registerTool(
5075
+ "project_description_get",
5076
+ toolProjectDescriptionGet,
5077
+ async (args) => {
5078
+ const { projectRoot } = args ?? {};
5079
+ try {
5080
+ const result = await source.getProjectDescription({
5081
+ projectRoot
5082
+ });
5083
+ return okResult("project_description_get", result);
5084
+ } catch (error) {
5085
+ return errorResult(error);
5086
+ }
5087
+ }
5088
+ );
5089
+ server.registerTool(
5090
+ "project_description_upsert",
5091
+ toolProjectDescriptionUpsert,
5092
+ async ({ projectDescription, projectRoot }) => {
5093
+ try {
5094
+ const result = await source.upsertProjectDescription(projectDescription, {
5095
+ projectRoot
5096
+ });
5097
+ return okResult("project_description_upsert", result);
5098
+ } catch (error) {
5099
+ return errorResult(error);
5100
+ }
5101
+ }
5102
+ );
5103
+ server.registerTool(
5104
+ "project_pages_list",
5105
+ toolProjectPagesList,
5106
+ async (args) => {
5107
+ const { projectRoot } = args ?? {};
5108
+ try {
5109
+ const result = await source.listProjectPages({
5110
+ projectRoot
5111
+ });
5112
+ return okResult("project_pages_list", result);
5113
+ } catch (error) {
5114
+ return errorResult(error);
5115
+ }
5116
+ }
5117
+ );
5118
+ server.registerTool(
5119
+ "project_page_get",
5120
+ toolProjectPageGet,
5121
+ async ({ pageId, projectRoot }) => {
5122
+ try {
5123
+ const result = await source.getProjectPage(pageId, { projectRoot });
5124
+ return okResult("project_page_get", result);
5125
+ } catch (error) {
5126
+ return errorResult(error);
5127
+ }
5128
+ }
5129
+ );
5130
+ server.registerTool(
5131
+ "project_page_create",
5132
+ toolProjectPageCreate,
5133
+ async (args) => {
5134
+ try {
5135
+ const { projectRoot, ...input } = projectPageCreateValidationSchema.parse(args);
5136
+ const result = await source.createProjectPage(input, { projectRoot });
5137
+ return okResult("project_page_create", result);
5138
+ } catch (error) {
5139
+ return errorResult(error);
5140
+ }
5141
+ }
5142
+ );
5143
+ server.registerTool(
5144
+ "project_page_variants_list",
5145
+ toolProjectPageVariantsList,
5146
+ async ({ pageId, projectRoot }) => {
5147
+ try {
5148
+ const result = await source.listProjectPageVariants(pageId, {
5149
+ projectRoot
5150
+ });
5151
+ return okResult("project_page_variants_list", result);
5152
+ } catch (error) {
5153
+ return errorResult(error);
5154
+ }
5155
+ }
5156
+ );
5157
+ server.registerTool(
5158
+ "project_page_variant_get",
5159
+ toolProjectPageVariantGet,
5160
+ async ({ pageId, variantId, projectRoot }) => {
5161
+ try {
5162
+ const result = await source.getProjectPageVariant(pageId, variantId, {
5163
+ projectRoot
5164
+ });
5165
+ return okResult("project_page_variant_get", result);
5166
+ } catch (error) {
5167
+ return errorResult(error);
5168
+ }
5169
+ }
5170
+ );
5171
+ server.registerTool(
5172
+ "project_page_variant_create",
5173
+ toolProjectPageVariantCreate,
5174
+ async (args) => {
5175
+ try {
5176
+ const { pageId, projectRoot, ...input } = projectPageVariantCreateValidationSchema.parse(args);
5177
+ const result = await source.createProjectPageVariant(pageId, input, {
5178
+ projectRoot
5179
+ });
5180
+ return okResult("project_page_variant_create", result);
5181
+ } catch (error) {
5182
+ return errorResult(error);
5183
+ }
5184
+ }
5185
+ );
5186
+ server.registerTool(
5187
+ "project_page_active_variant_set",
5188
+ toolProjectPageActiveVariantSet,
5189
+ async ({ pageId, variantId, projectRoot }) => {
5190
+ try {
5191
+ const result = await source.setProjectPageActiveVariant(pageId, variantId, {
5192
+ projectRoot
5193
+ });
5194
+ return okResult("project_page_active_variant_set", result);
5195
+ } catch (error) {
5196
+ return errorResult(error);
5197
+ }
5198
+ }
5199
+ );
5200
+ server.registerTool(
5201
+ "project_issue_report_submit",
5202
+ toolProjectIssueReportSubmit,
5203
+ async ({ reportText, tags, projectRoot }) => {
5204
+ try {
5205
+ const result = await source.submitIssueReport(
5206
+ {
5207
+ reportText,
5208
+ tags
5209
+ },
5210
+ { projectRoot }
5211
+ );
5212
+ return okResult("project_issue_report_submit", result);
5213
+ } catch (error) {
5214
+ return errorResult(error);
5215
+ }
5216
+ }
5217
+ );
3114
5218
  server.registerTool(
3115
5219
  "inspect_page",
3116
5220
  toolInspectPage,
@@ -3187,16 +5291,54 @@ function createPrimeUiMcpServer(source) {
3187
5291
  server.registerTool(
3188
5292
  "copy_page",
3189
5293
  toolCopyPage,
5294
+ async ({ originPageSlug, projectRoot }) => {
5295
+ try {
5296
+ const result = await source.copyPage(
5297
+ originPageSlug,
5298
+ { projectRoot }
5299
+ );
5300
+ return okResult("copy_page", result);
5301
+ } catch (error) {
5302
+ return errorResult(error);
5303
+ }
5304
+ }
5305
+ );
5306
+ server.registerTool(
5307
+ "copy_component",
5308
+ toolCopyComponent,
3190
5309
  async ({
3191
5310
  originPageSlug,
5311
+ blockId,
5312
+ componentId,
3192
5313
  projectRoot
3193
5314
  }) => {
3194
5315
  try {
3195
- const result = await source.copyPage(
5316
+ const result = await source.copyComponent(
3196
5317
  originPageSlug,
3197
- { projectRoot }
5318
+ blockId,
5319
+ componentId,
5320
+ {
5321
+ projectRoot
5322
+ }
3198
5323
  );
3199
- return okResult("copy_page", result);
5324
+ return okResult("copy_component", result);
5325
+ } catch (error) {
5326
+ return errorResult(error);
5327
+ }
5328
+ }
5329
+ );
5330
+ server.registerTool(
5331
+ "copy_exportable",
5332
+ toolCopyExportable,
5333
+ async ({ exportableKey, projectRoot }) => {
5334
+ try {
5335
+ const result = await source.copyExportable(
5336
+ exportableKey,
5337
+ {
5338
+ projectRoot
5339
+ }
5340
+ );
5341
+ return okResult("copy_exportable", result);
3200
5342
  } catch (error) {
3201
5343
  return errorResult(error);
3202
5344
  }