@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/README.md +63 -14
- package/dist/service.js +2632 -490
- package/dist/service.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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 = "
|
|
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" : "
|
|
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
|
|
1549
|
-
import { readFile as
|
|
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
|
|
1553
|
-
import
|
|
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/
|
|
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
|
|
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
|
|
2073
|
+
`Export manifest for ${input.manifestOwnerLabel} contains path outside export root: ${trimmed}`
|
|
1899
2074
|
);
|
|
1900
2075
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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}${
|
|
2250
|
+
const componentsPrefix = `${sourceComponentsPath}${path10.sep}`;
|
|
1994
2251
|
if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
|
|
1995
|
-
const relativeToComponents =
|
|
2252
|
+
const relativeToComponents = path10.relative(
|
|
1996
2253
|
sourceComponentsPath,
|
|
1997
2254
|
sourceFilePath
|
|
1998
2255
|
);
|
|
1999
|
-
return
|
|
2256
|
+
return path10.join(targetComponentsPath, relativeToComponents);
|
|
2000
2257
|
}
|
|
2001
|
-
const relativeToExport =
|
|
2002
|
-
if (relativeToExport.startsWith("..") ||
|
|
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
|
|
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 =
|
|
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 =
|
|
2036
|
-
const sourceComponentsPath =
|
|
2037
|
-
const sourcePageStats = await
|
|
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
|
|
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 =
|
|
2050
|
-
const targetComponentsPath =
|
|
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
|
-
|
|
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
|
|
2358
|
+
const sourceBuffer = await readFile4(sourceFilePath);
|
|
2082
2359
|
const plannedSourceBuffer = sourceBuffer;
|
|
2083
2360
|
let targetBuffer = null;
|
|
2084
2361
|
try {
|
|
2085
|
-
targetBuffer = await
|
|
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(
|
|
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 =
|
|
2116
|
-
const userPackageJsonPath =
|
|
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 =
|
|
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/
|
|
2339
|
-
|
|
2340
|
-
|
|
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
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
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
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
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
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
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
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
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
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
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
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
return
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
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
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
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
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
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
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
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
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
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
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
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
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
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
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
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
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
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
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
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
|
-
|
|
2494
|
-
|
|
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
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
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
|
-
|
|
2503
|
-
return
|
|
2504
|
-
context,
|
|
2505
|
-
(service) => service.downloadExportById(id, context)
|
|
2506
|
-
);
|
|
2982
|
+
if (!targetBuffer || sourceBuffer.equals(targetBuffer)) {
|
|
2983
|
+
return null;
|
|
2507
2984
|
}
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
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
|
-
|
|
2515
|
-
|
|
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
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
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
|
-
|
|
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 ->
|
|
2686
|
-
8. Reconcile integration points and
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
2735
|
-
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
-
|
|
2741
|
-
- If
|
|
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
|
-
|
|
2750
|
-
|
|
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
|
-
|
|
2753
|
-
"
|
|
4439
|
+
message: z4.string().describe(
|
|
4440
|
+
"Decision-oriented summary with reportPath and the next manual actions for completing component transfer."
|
|
2754
4441
|
),
|
|
2755
|
-
|
|
2756
|
-
"
|
|
4442
|
+
reportPath: z4.string().describe(
|
|
4443
|
+
"Absolute path to saved local report with direct conflicts, potential conflicts, and selected block metadata."
|
|
2757
4444
|
),
|
|
2758
|
-
|
|
2759
|
-
|
|
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
|
-
|
|
2762
|
-
|
|
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:
|
|
4488
|
+
readOnlyHint: false,
|
|
2767
4489
|
destructiveHint: false,
|
|
2768
|
-
idempotentHint:
|
|
2769
|
-
openWorldHint:
|
|
4490
|
+
idempotentHint: false,
|
|
4491
|
+
openWorldHint: false
|
|
2770
4492
|
}
|
|
2771
4493
|
};
|
|
2772
|
-
var
|
|
2773
|
-
title: "PrimeUI
|
|
2774
|
-
description: `
|
|
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
|
|
2778
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
2793
|
-
"
|
|
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
|
-
|
|
2798
|
-
"
|
|
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
|
-
|
|
2801
|
-
|
|
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
|
-
|
|
2805
|
-
"
|
|
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
|
-
|
|
2808
|
-
|
|
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:
|
|
4583
|
+
readOnlyHint: false,
|
|
2813
4584
|
destructiveHint: false,
|
|
2814
|
-
idempotentHint:
|
|
2815
|
-
openWorldHint:
|
|
4585
|
+
idempotentHint: false,
|
|
4586
|
+
openWorldHint: false
|
|
2816
4587
|
}
|
|
2817
4588
|
};
|
|
2818
|
-
var
|
|
2819
|
-
title: "PrimeUI
|
|
2820
|
-
description: `
|
|
4589
|
+
var toolClearTemp = {
|
|
4590
|
+
title: "PrimeUI Clear Temp",
|
|
4591
|
+
description: `Delete all files in .primeui/temp/ and recreate empty temp structure.
|
|
2821
4592
|
|
|
2822
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2835
|
-
"
|
|
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
|
|
2846
|
-
title: "PrimeUI
|
|
2847
|
-
description: `
|
|
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:
|
|
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
|
-
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
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
|
-
|
|
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:
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
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:
|
|
4888
|
+
readOnlyHint: true,
|
|
2884
4889
|
destructiveHint: false,
|
|
2885
|
-
idempotentHint:
|
|
4890
|
+
idempotentHint: true,
|
|
2886
4891
|
openWorldHint: true
|
|
2887
4892
|
}
|
|
2888
4893
|
};
|
|
2889
|
-
var
|
|
2890
|
-
title: "PrimeUI
|
|
2891
|
-
description: `
|
|
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
|
-
|
|
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
|
-
|
|
2912
|
-
|
|
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:
|
|
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:
|
|
4908
|
+
readOnlyHint: true,
|
|
2928
4909
|
destructiveHint: false,
|
|
2929
4910
|
idempotentHint: true,
|
|
2930
4911
|
openWorldHint: true
|
|
2931
4912
|
}
|
|
2932
4913
|
};
|
|
2933
|
-
var
|
|
2934
|
-
title: "PrimeUI
|
|
2935
|
-
description: `
|
|
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
|
-
-
|
|
2939
|
-
-
|
|
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
|
-
|
|
2942
|
-
-
|
|
2943
|
-
-
|
|
2944
|
-
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2960
|
-
|
|
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:
|
|
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:
|
|
4954
|
+
openWorldHint: true
|
|
3005
4955
|
}
|
|
3006
4956
|
};
|
|
3007
|
-
var
|
|
3008
|
-
title: "PrimeUI
|
|
3009
|
-
description: `
|
|
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
|
-
|
|
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
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
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:
|
|
3028
|
-
openWorldHint:
|
|
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.
|
|
5316
|
+
const result = await source.copyComponent(
|
|
3196
5317
|
originPageSlug,
|
|
3197
|
-
|
|
5318
|
+
blockId,
|
|
5319
|
+
componentId,
|
|
5320
|
+
{
|
|
5321
|
+
projectRoot
|
|
5322
|
+
}
|
|
3198
5323
|
);
|
|
3199
|
-
return okResult("
|
|
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
|
}
|