@primeuicom/mcp 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/service.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/service.ts
4
- import path7 from "path";
4
+ import path8 from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
 
@@ -86,6 +86,9 @@ function toUniqueResolvedDirs(directories) {
86
86
  }
87
87
  return [...resolved];
88
88
  }
89
+ function buildPrimeUiProjectSearchRoots(options) {
90
+ return toUniqueResolvedDirs([options.cwd, ...options.fallbackCwds ?? []]);
91
+ }
89
92
  async function resolvePrimeUiProjectConfig(options) {
90
93
  const envProjectRoot = options.projectRootFromEnv?.trim();
91
94
  let projectConfigPath;
@@ -95,10 +98,10 @@ async function resolvePrimeUiProjectConfig(options) {
95
98
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
96
99
  );
97
100
  } else {
98
- const searchRoots = toUniqueResolvedDirs([
99
- options.cwd,
100
- ...options.fallbackCwds ?? []
101
- ]);
101
+ const searchRoots = buildPrimeUiProjectSearchRoots({
102
+ cwd: options.cwd,
103
+ fallbackCwds: options.fallbackCwds
104
+ });
102
105
  for (const searchRoot of searchRoots) {
103
106
  const foundProjectConfigPath = await findPrimeUiProjectConfigPath(searchRoot);
104
107
  if (foundProjectConfigPath) {
@@ -265,6 +268,33 @@ var inspectPageReportRowSchema = z2.object({
265
268
  title: z2.string().nullable().describe("Title extracted by MCP from component props."),
266
269
  description: z2.string().nullable().describe("Description extracted by MCP from component props.")
267
270
  });
271
+ var healthOptionSchema = z2.object({
272
+ key: z2.string().describe("Environment variable or diagnostic option name."),
273
+ description: z2.string().describe("Human-readable explanation of what this option controls."),
274
+ currentValue: z2.string().nullable().describe(
275
+ "Current effective value for this option. Null when not set or intentionally hidden."
276
+ )
277
+ });
278
+ var healthRuntimeSchema = z2.object({
279
+ cwd: z2.string().describe("process.cwd() observed by the MCP server process."),
280
+ initCwd: z2.string().nullable().describe("INIT_CWD environment value, if provided by the client runtime."),
281
+ pwd: z2.string().nullable().describe("PWD environment value, if provided by the client runtime."),
282
+ searchRoots: z2.array(z2.string()).describe(
283
+ "Resolved directories used when searching for .primeui/project.json."
284
+ ),
285
+ rootsListFallbackCwds: z2.array(z2.string()).describe(
286
+ "File-system roots derived from MCP roots/list and used as search fallbacks."
287
+ )
288
+ });
289
+ var healthConfigSchema = z2.object({
290
+ found: z2.boolean().describe("True when .primeui/project.json was found and parsed successfully."),
291
+ projectRoot: z2.string().nullable().describe("Resolved PrimeUI project root directory (parent of .primeui)."),
292
+ projectConfigPath: z2.string().nullable().describe("Absolute path to .primeui/project.json when found."),
293
+ targetProjectPath: z2.string().nullable().describe("targetProjectPath value loaded from project config."),
294
+ targetProjectRoot: z2.string().nullable().describe("Absolute target project root derived from targetProjectPath."),
295
+ source: z2.enum(["env.PRIMEUI_PROJECT_ROOT", "search"]).describe("How project config lookup was resolved."),
296
+ error: z2.string().nullable().describe("Human-readable resolution error when config is not available.")
297
+ });
268
298
  var TRIGGER_PHRASES = [
269
299
  "import page from PrimeUI",
270
300
  "add page from PrimeUI",
@@ -295,6 +325,7 @@ ${WORKFLOW_SUMMARY}
295
325
 
296
326
  CRITICAL RULES FOR PAGE IMPORT:
297
327
  - Import is PAGE-BY-PAGE only. Never import the entire project at once.
328
+ - Use health_check to inspect runtime path/config resolution when setup issues occur.
298
329
  - Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
299
330
  - After get_project_info, always compare PrimeUI pages against the user's existing local project pages before proposing imports.
300
331
  - Reconciliation report MUST include: page presence match, slug/pagePath/componentsPath path match, and proposed action per page.
@@ -325,6 +356,34 @@ CRITICAL RULES FOR PAGE IMPORT:
325
356
  - If integration pattern is obvious, apply it and explicitly mark it in the report with "\u26A0 integration update".
326
357
  - If integration pattern is unclear, ask the user before editing integration files.
327
358
  `.trim();
359
+ var toolHealthCheck = {
360
+ title: "PrimeUI Health Check",
361
+ description: `Diagnostic tool for MCP runtime setup. This tool MUST NOT fail: it always returns structured diagnostics even when project config is missing.
362
+
363
+ WHEN TO USE:
364
+ - Use this first when PrimeUI MCP tools fail due to project path/config issues.
365
+ - Use this to verify cwd/env/roots behavior in different MCP clients (Codex, Claude Code, Inspector).
366
+
367
+ RETURNS:
368
+ - Runtime paths (cwd, INIT_CWD, PWD, search roots),
369
+ - roots/list fallback paths from client (if available),
370
+ - Whether .primeui/project.json was found and parsed,
371
+ - Effective resolved project root/config path (when available),
372
+ - Current option values and fallback guidance.`,
373
+ inputSchema: {},
374
+ outputSchema: {
375
+ status: z2.enum(["ok", "degraded"]).describe("Overall setup health. 'degraded' means config is unresolved."),
376
+ runtime: healthRuntimeSchema,
377
+ config: healthConfigSchema,
378
+ options: z2.array(healthOptionSchema).describe("Environment and runtime options relevant for configuration.")
379
+ },
380
+ annotations: {
381
+ readOnlyHint: true,
382
+ destructiveHint: false,
383
+ idempotentHint: true,
384
+ openWorldHint: false
385
+ }
386
+ };
328
387
  var toolGetProjectInfo = {
329
388
  title: "PrimeUI Project Info",
330
389
  description: `ENTRY POINT for all PrimeUI import operations. Always start here. Returns project metadata and the full list of available pages.
@@ -652,6 +711,38 @@ function createPrimeUiMcpServer(source) {
652
711
  instructions: initialInstructions
653
712
  }
654
713
  );
714
+ server.registerTool(
715
+ "health_check",
716
+ toolHealthCheck,
717
+ async () => {
718
+ try {
719
+ const health = await source.healthCheck();
720
+ return okResult("health_check", health);
721
+ } catch (error) {
722
+ const message = error instanceof Error ? error.message : String(error);
723
+ return okResult("health_check", {
724
+ status: "degraded",
725
+ runtime: {
726
+ cwd: process.cwd(),
727
+ initCwd: process.env.INIT_CWD?.trim() || null,
728
+ pwd: process.env.PWD?.trim() || null,
729
+ searchRoots: [process.cwd()],
730
+ rootsListFallbackCwds: []
731
+ },
732
+ config: {
733
+ found: false,
734
+ projectRoot: null,
735
+ projectConfigPath: null,
736
+ targetProjectPath: null,
737
+ targetProjectRoot: null,
738
+ source: "search",
739
+ error: `health_check fallback: ${message}`
740
+ },
741
+ options: []
742
+ });
743
+ }
744
+ }
745
+ );
655
746
  server.registerTool(
656
747
  "get_project_info",
657
748
  toolGetProjectInfo,
@@ -742,13 +833,118 @@ function createPrimeUiMcpServer(source) {
742
833
  return server;
743
834
  }
744
835
 
836
+ // src/services/health-check-service.ts
837
+ import path2 from "path";
838
+ function normalizeOptional(value) {
839
+ const trimmed = value?.trim();
840
+ return trimmed ? trimmed : null;
841
+ }
842
+ function buildHealthOptions(input) {
843
+ return [
844
+ {
845
+ key: "PRIMEUI_PROJECT_ROOT",
846
+ description: "Optional absolute root containing .primeui/project.json. Use as explicit override.",
847
+ currentValue: input.projectRootFromEnv
848
+ },
849
+ {
850
+ key: "PRIMEUI_API_BASE_URL",
851
+ description: "Optional PrimeUI API base URL override.",
852
+ currentValue: input.apiBaseUrlFromEnv
853
+ },
854
+ {
855
+ key: "PRIMEUI_API_KEY",
856
+ description: "Optional API key override. If missing, MCP uses apiKey from .primeui/project.json.",
857
+ currentValue: input.apiKeyFromEnv ? "set" : null
858
+ },
859
+ {
860
+ key: "Fallback search",
861
+ description: "When PRIMEUI_PROJECT_ROOT is not set, MCP searches from roots/list file roots, INIT_CWD, PWD, then process.cwd().",
862
+ currentValue: null
863
+ }
864
+ ];
865
+ }
866
+ async function runPrimeUiHealthCheck(input) {
867
+ const projectRootFromEnv = normalizeOptional(input.projectRootFromEnv);
868
+ const apiBaseUrlFromEnv = normalizeOptional(input.apiBaseUrlFromEnv);
869
+ const apiKeyFromEnv = normalizeOptional(input.apiKeyFromEnv);
870
+ const initCwd = normalizeOptional(input.initCwd);
871
+ const pwd = normalizeOptional(input.pwd);
872
+ const rootsListFallbackCwds = (input.rootsListFallbackCwds ?? []).map((value) => value.trim()).filter(Boolean).map((value) => path2.resolve(value));
873
+ const fallbackCwds = (input.fallbackCwds ?? []).map((value) => value.trim()).filter(Boolean);
874
+ const searchRoots = buildPrimeUiProjectSearchRoots({
875
+ cwd: input.cwd,
876
+ fallbackCwds
877
+ });
878
+ try {
879
+ const resolvedProjectConfig = await resolvePrimeUiProjectConfig({
880
+ cwd: input.cwd,
881
+ projectRootFromEnv: projectRootFromEnv ?? void 0,
882
+ fallbackCwds
883
+ });
884
+ const targetProjectPath = resolvedProjectConfig.projectConfig.targetProjectPath;
885
+ return {
886
+ status: "ok",
887
+ runtime: {
888
+ cwd: path2.resolve(input.cwd),
889
+ initCwd,
890
+ pwd,
891
+ searchRoots,
892
+ rootsListFallbackCwds
893
+ },
894
+ config: {
895
+ found: true,
896
+ projectRoot: resolvedProjectConfig.projectRoot,
897
+ projectConfigPath: resolvedProjectConfig.projectConfigPath,
898
+ targetProjectPath,
899
+ targetProjectRoot: path2.resolve(
900
+ resolvedProjectConfig.projectRoot,
901
+ targetProjectPath
902
+ ),
903
+ source: projectRootFromEnv ? "env.PRIMEUI_PROJECT_ROOT" : "search",
904
+ error: null
905
+ },
906
+ options: buildHealthOptions({
907
+ projectRootFromEnv,
908
+ apiBaseUrlFromEnv,
909
+ apiKeyFromEnv
910
+ })
911
+ };
912
+ } catch (error) {
913
+ const message = error instanceof Error ? error.message : String(error);
914
+ return {
915
+ status: "degraded",
916
+ runtime: {
917
+ cwd: path2.resolve(input.cwd),
918
+ initCwd,
919
+ pwd,
920
+ searchRoots,
921
+ rootsListFallbackCwds
922
+ },
923
+ config: {
924
+ found: false,
925
+ projectRoot: null,
926
+ projectConfigPath: null,
927
+ targetProjectPath: null,
928
+ targetProjectRoot: null,
929
+ source: projectRootFromEnv ? "env.PRIMEUI_PROJECT_ROOT" : "search",
930
+ error: message
931
+ },
932
+ options: buildHealthOptions({
933
+ projectRootFromEnv,
934
+ apiBaseUrlFromEnv,
935
+ apiKeyFromEnv
936
+ })
937
+ };
938
+ }
939
+ }
940
+
745
941
  // src/services/project-sync-service.ts
746
- import path5 from "path";
942
+ import path6 from "path";
747
943
  import { readFile as readFile4 } from "fs/promises";
748
944
 
749
945
  // src/lib/fs.ts
750
946
  import { mkdir, rm, writeFile } from "fs/promises";
751
- import path2 from "path";
947
+ import path3 from "path";
752
948
  import extractZipArchive from "extract-zip";
753
949
  async function ensureDir(dirPath) {
754
950
  await mkdir(dirPath, { recursive: true });
@@ -758,7 +954,7 @@ async function resetDir(dirPath) {
758
954
  await mkdir(dirPath, { recursive: true });
759
955
  }
760
956
  async function writeUtf8(filePath, content) {
761
- await ensureDir(path2.dirname(filePath));
957
+ await ensureDir(path3.dirname(filePath));
762
958
  await writeFile(filePath, content, "utf-8");
763
959
  }
764
960
  async function extractZip(zipPath, targetDir) {
@@ -773,12 +969,12 @@ async function extractZip(zipPath, targetDir) {
773
969
 
774
970
  // src/services/page-copy-service.ts
775
971
  import { readFile as readFile3, readdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
776
- import path4 from "path";
972
+ import path5 from "path";
777
973
 
778
974
  // src/lib/import-graph.ts
779
975
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
780
976
  import { builtinModules } from "module";
781
- import path3 from "path";
977
+ import path4 from "path";
782
978
  var INTERNAL_EXTENSIONS = [
783
979
  ".ts",
784
980
  ".tsx",
@@ -854,7 +1050,7 @@ async function pathExists(filePath) {
854
1050
  }
855
1051
  }
856
1052
  async function resolveFileCandidate(candidateBase) {
857
- const ext = path3.extname(candidateBase);
1053
+ const ext = path4.extname(candidateBase);
858
1054
  if (ext) {
859
1055
  if (await pathExists(candidateBase)) {
860
1056
  return candidateBase;
@@ -874,7 +1070,7 @@ async function resolveFileCandidate(candidateBase) {
874
1070
  }
875
1071
  if (stats.isDirectory()) {
876
1072
  for (const extension of INTERNAL_EXTENSIONS) {
877
- const indexCandidate = path3.join(candidateBase, `index${extension}`);
1073
+ const indexCandidate = path4.join(candidateBase, `index${extension}`);
878
1074
  if (await pathExists(indexCandidate)) {
879
1075
  return indexCandidate;
880
1076
  }
@@ -896,13 +1092,13 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
896
1092
  }
897
1093
  let candidateBase = null;
898
1094
  if (specifier.startsWith("@/")) {
899
- candidateBase = path3.join(projectRoot, "src", specifier.slice(2));
1095
+ candidateBase = path4.join(projectRoot, "src", specifier.slice(2));
900
1096
  } else if (specifier.startsWith("@root/")) {
901
- candidateBase = path3.join(projectRoot, specifier.slice(6));
1097
+ candidateBase = path4.join(projectRoot, specifier.slice(6));
902
1098
  } else if (specifier.startsWith(".")) {
903
- candidateBase = path3.resolve(path3.dirname(importerFilePath), specifier);
1099
+ candidateBase = path4.resolve(path4.dirname(importerFilePath), specifier);
904
1100
  } else if (specifier.startsWith("/")) {
905
- candidateBase = path3.join(projectRoot, specifier.slice(1));
1101
+ candidateBase = path4.join(projectRoot, specifier.slice(1));
906
1102
  } else {
907
1103
  return { kind: "unknown" };
908
1104
  }
@@ -916,12 +1112,12 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
916
1112
  };
917
1113
  }
918
1114
  function shouldParseFile(filePath) {
919
- return PARSEABLE_EXTENSIONS.has(path3.extname(filePath).toLowerCase());
1115
+ return PARSEABLE_EXTENSIONS.has(path4.extname(filePath).toLowerCase());
920
1116
  }
921
1117
  async function buildImportGraph(input) {
922
1118
  const shouldFollowInternalImports = input.followInternalImports ?? true;
923
- const projectRoot = path3.resolve(input.projectRoot);
924
- const queue = input.entryFiles.map((filePath) => path3.resolve(filePath));
1119
+ const projectRoot = path4.resolve(input.projectRoot);
1120
+ const queue = input.entryFiles.map((filePath) => path4.resolve(filePath));
925
1121
  const visited = /* @__PURE__ */ new Set();
926
1122
  const internalFiles = /* @__PURE__ */ new Set();
927
1123
  const externalPackages = /* @__PURE__ */ new Set();
@@ -1174,10 +1370,10 @@ function normalizeSlug2(slug) {
1174
1370
  return withLeadingSlash.replace(/\/+$/, "") || "/";
1175
1371
  }
1176
1372
  function toPosixPath(value) {
1177
- return value.split(path4.sep).join("/");
1373
+ return value.split(path5.sep).join("/");
1178
1374
  }
1179
1375
  function toProjectRelative(rootPath, absolutePath) {
1180
- return toPosixPath(path4.relative(rootPath, absolutePath));
1376
+ return toPosixPath(path5.relative(rootPath, absolutePath));
1181
1377
  }
1182
1378
  function escapeRegExp(value) {
1183
1379
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1234,7 +1430,7 @@ function buildPlannedSourceBuffer(sourceBuffer, sourceFilePath, importRewritePla
1234
1430
  if (!importRewritePlan) {
1235
1431
  return sourceBuffer;
1236
1432
  }
1237
- const extension = path4.extname(sourceFilePath).toLowerCase();
1433
+ const extension = path5.extname(sourceFilePath).toLowerCase();
1238
1434
  if (!REWRITABLE_IMPORT_EXTENSIONS.has(extension)) {
1239
1435
  return sourceBuffer;
1240
1436
  }
@@ -1336,9 +1532,9 @@ async function resolveManifestCandidateFiles(input) {
1336
1532
  );
1337
1533
  }
1338
1534
  const normalizedRelative = toPosixPath(trimmed).replace(/^\.\/+/, "");
1339
- const absolutePath = path4.resolve(input.exportPath, normalizedRelative);
1340
- const relative = path4.relative(input.exportPath, absolutePath);
1341
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
1535
+ const absolutePath = path5.resolve(input.exportPath, normalizedRelative);
1536
+ const relative = path5.relative(input.exportPath, absolutePath);
1537
+ if (relative.startsWith("..") || path5.isAbsolute(relative)) {
1342
1538
  throw new Error(
1343
1539
  `Export manifest for page "${input.pageSlug}" contains path outside export root: ${trimmed}`
1344
1540
  );
@@ -1369,12 +1565,12 @@ async function resolveSingleExportDirectory(exportsRoot) {
1369
1565
  const exportId = exportDirectories[0] ?? "";
1370
1566
  return {
1371
1567
  exportId,
1372
- exportPath: path4.join(exportsRoot, exportId)
1568
+ exportPath: path5.join(exportsRoot, exportId)
1373
1569
  };
1374
1570
  }
1375
1571
  function ensureSafeTargetPath(projectRoot, targetPath) {
1376
- const relative = path4.relative(projectRoot, targetPath);
1377
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
1572
+ const relative = path5.relative(projectRoot, targetPath);
1573
+ if (relative.startsWith("..") || path5.isAbsolute(relative)) {
1378
1574
  throw new Error(
1379
1575
  `Refusing to write outside project root. Computed target: ${targetPath}`
1380
1576
  );
@@ -1432,19 +1628,19 @@ function resolveTargetFilePath(input) {
1432
1628
  if (sourceFilePath === sourcePagePath) {
1433
1629
  return targetPagePath;
1434
1630
  }
1435
- const componentsPrefix = `${sourceComponentsPath}${path4.sep}`;
1631
+ const componentsPrefix = `${sourceComponentsPath}${path5.sep}`;
1436
1632
  if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
1437
- const relativeToComponents = path4.relative(
1633
+ const relativeToComponents = path5.relative(
1438
1634
  sourceComponentsPath,
1439
1635
  sourceFilePath
1440
1636
  );
1441
- return path4.join(targetComponentsPath, relativeToComponents);
1637
+ return path5.join(targetComponentsPath, relativeToComponents);
1442
1638
  }
1443
- const relativeToExport = path4.relative(exportRoot, sourceFilePath);
1444
- if (relativeToExport.startsWith("..") || path4.isAbsolute(relativeToExport)) {
1639
+ const relativeToExport = path5.relative(exportRoot, sourceFilePath);
1640
+ if (relativeToExport.startsWith("..") || path5.isAbsolute(relativeToExport)) {
1445
1641
  throw new Error(`Source file is outside export root: ${sourceFilePath}`);
1446
1642
  }
1447
- return path4.join(projectRoot, relativeToExport);
1643
+ return path5.join(projectRoot, relativeToExport);
1448
1644
  }
1449
1645
  async function copyPageFromExport(input) {
1450
1646
  const normalizedOriginSlug = normalizeSlug2(input.originPageSlug);
@@ -1455,7 +1651,7 @@ async function copyPageFromExport(input) {
1455
1651
  const { exportId, exportPath } = await resolveSingleExportDirectory(
1456
1652
  input.exportsRoot
1457
1653
  );
1458
- const manifestPath = path4.join(
1654
+ const manifestPath = path5.join(
1459
1655
  input.exportsRoot,
1460
1656
  `${exportId}.manifest.json`
1461
1657
  );
@@ -1478,8 +1674,8 @@ async function copyPageFromExport(input) {
1478
1674
  `Page not found in manifest for slug: ${normalizedOriginSlug}`
1479
1675
  );
1480
1676
  }
1481
- const sourcePagePath = path4.join(exportPath, page.pagePath);
1482
- const sourceComponentsPath = path4.join(exportPath, page.componentsPath);
1677
+ const sourcePagePath = path5.join(exportPath, page.pagePath);
1678
+ const sourceComponentsPath = path5.join(exportPath, page.componentsPath);
1483
1679
  const sourcePageStats = await stat3(sourcePagePath).catch(() => null);
1484
1680
  if (!sourcePageStats?.isFile()) {
1485
1681
  throw new Error(`Source page file not found: ${sourcePagePath}`);
@@ -1499,8 +1695,8 @@ async function copyPageFromExport(input) {
1499
1695
  pageType: page.pageType,
1500
1696
  slug: normalizedActualSlug
1501
1697
  });
1502
- const targetPagePath = path4.join(input.projectRoot, targetPaths.pagePath);
1503
- const targetComponentsPath = path4.join(
1698
+ const targetPagePath = path5.join(input.projectRoot, targetPaths.pagePath);
1699
+ const targetComponentsPath = path5.join(
1504
1700
  input.projectRoot,
1505
1701
  targetPaths.componentsPath
1506
1702
  );
@@ -1546,7 +1742,7 @@ async function copyPageFromExport(input) {
1546
1742
  const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
1547
1743
  const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
1548
1744
  if (!targetBuffer) {
1549
- await ensureDir(path4.dirname(targetFilePath));
1745
+ await ensureDir(path5.dirname(targetFilePath));
1550
1746
  await writeFile2(targetFilePath, plannedSourceBuffer);
1551
1747
  newFiles.push({
1552
1748
  sourcePath: sourceRelative,
@@ -1575,8 +1771,8 @@ async function copyPageFromExport(input) {
1575
1771
  isBinary
1576
1772
  });
1577
1773
  }
1578
- const exportPackageJsonPath = path4.join(exportPath, "package.json");
1579
- const userPackageJsonPath = path4.join(input.projectRoot, "package.json");
1774
+ const exportPackageJsonPath = path5.join(exportPath, "package.json");
1775
+ const userPackageJsonPath = path5.join(input.projectRoot, "package.json");
1580
1776
  if (!await fileExists2(exportPackageJsonPath)) {
1581
1777
  throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
1582
1778
  }
@@ -1782,7 +1978,7 @@ function isExportPage(value) {
1782
1978
  return typeof maybe.id === "string" && (typeof title === "string" || typeof title === "undefined") && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && maybe.isReadyToExport === true && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string" && isExportPageManifest(maybe.manifest);
1783
1979
  }
1784
1980
  function buildManifestPath(exportsRoot, exportId) {
1785
- return path5.join(exportsRoot, `${exportId}.manifest.json`);
1981
+ return path6.join(exportsRoot, `${exportId}.manifest.json`);
1786
1982
  }
1787
1983
  function parseExportManifest(value) {
1788
1984
  if (!value || typeof value !== "object") {
@@ -1826,9 +2022,9 @@ var ProjectSyncService = class {
1826
2022
  this.projectRoot = options.projectRoot;
1827
2023
  this.targetProjectRoot = options.targetProjectRoot;
1828
2024
  this.provider = options.provider;
1829
- this.primeUiRoot = path5.join(this.projectRoot, ".primeui");
1830
- this.tempRoot = path5.join(this.primeUiRoot, "temp");
1831
- this.exportsRoot = path5.join(this.tempRoot, "exports");
2025
+ this.primeUiRoot = path6.join(this.projectRoot, ".primeui");
2026
+ this.tempRoot = path6.join(this.primeUiRoot, "temp");
2027
+ this.exportsRoot = path6.join(this.tempRoot, "exports");
1832
2028
  }
1833
2029
  async getProjectInfo() {
1834
2030
  await this.ensureTempLayout();
@@ -1853,8 +2049,8 @@ var ProjectSyncService = class {
1853
2049
  if (!selected) {
1854
2050
  throw new Error(`Export not found: ${id}`);
1855
2051
  }
1856
- const targetZipPath = path5.join(this.exportsRoot, `${id}.zip`);
1857
- const targetProjectPath = path5.join(this.exportsRoot, id);
2052
+ const targetZipPath = path6.join(this.exportsRoot, `${id}.zip`);
2053
+ const targetProjectPath = path6.join(this.exportsRoot, id);
1858
2054
  const manifestPath = buildManifestPath(this.exportsRoot, id);
1859
2055
  let manifest;
1860
2056
  try {
@@ -1907,6 +2103,47 @@ var ProjectSyncService = class {
1907
2103
  await resetDir(this.tempRoot);
1908
2104
  await ensureDir(this.exportsRoot);
1909
2105
  }
2106
+ async healthCheck() {
2107
+ return {
2108
+ status: "ok",
2109
+ runtime: {
2110
+ cwd: process.cwd(),
2111
+ initCwd: process.env.INIT_CWD?.trim() || null,
2112
+ pwd: process.env.PWD?.trim() || null,
2113
+ searchRoots: [process.cwd()],
2114
+ rootsListFallbackCwds: []
2115
+ },
2116
+ config: {
2117
+ found: true,
2118
+ projectRoot: this.projectRoot,
2119
+ projectConfigPath: path6.join(this.projectRoot, ".primeui", "project.json"),
2120
+ targetProjectPath: path6.relative(
2121
+ this.projectRoot,
2122
+ this.targetProjectRoot
2123
+ ).startsWith(".") ? "./" : `./${path6.relative(this.projectRoot, this.targetProjectRoot)}`,
2124
+ targetProjectRoot: this.targetProjectRoot,
2125
+ source: process.env.PRIMEUI_PROJECT_ROOT?.trim() ? "env.PRIMEUI_PROJECT_ROOT" : "search",
2126
+ error: null
2127
+ },
2128
+ options: [
2129
+ {
2130
+ key: "PRIMEUI_PROJECT_ROOT",
2131
+ description: "Optional absolute root containing .primeui/project.json. Use as explicit override.",
2132
+ currentValue: process.env.PRIMEUI_PROJECT_ROOT?.trim() || null
2133
+ },
2134
+ {
2135
+ key: "PRIMEUI_API_BASE_URL",
2136
+ description: "Optional PrimeUI API base URL override.",
2137
+ currentValue: process.env.PRIMEUI_API_BASE_URL?.trim() || null
2138
+ },
2139
+ {
2140
+ key: "PRIMEUI_API_KEY",
2141
+ description: "Optional API key override. If missing, MCP uses apiKey from .primeui/project.json.",
2142
+ currentValue: process.env.PRIMEUI_API_KEY?.trim() ? "set" : null
2143
+ }
2144
+ ]
2145
+ };
2146
+ }
1910
2147
  async ensureTempLayout() {
1911
2148
  await ensureDir(this.primeUiRoot);
1912
2149
  await ensureDir(this.tempRoot);
@@ -1917,7 +2154,7 @@ var ProjectSyncService = class {
1917
2154
  // src/sources/api-provider.ts
1918
2155
  import { createWriteStream } from "fs";
1919
2156
  import { unlink } from "fs/promises";
1920
- import path6 from "path";
2157
+ import path7 from "path";
1921
2158
  import { Readable, Transform } from "stream";
1922
2159
  import { pipeline } from "stream/promises";
1923
2160
  import { z as z3 } from "zod";
@@ -2141,7 +2378,7 @@ var ApiProjectDataProvider = class {
2141
2378
  if (!response.body) {
2142
2379
  throw new PrimeUiApiContractError(endpoint, "response body is empty");
2143
2380
  }
2144
- await ensureDir(path6.dirname(destinationPath));
2381
+ await ensureDir(path7.dirname(destinationPath));
2145
2382
  const zipStream = Readable.fromWeb(
2146
2383
  response.body
2147
2384
  );
@@ -2238,7 +2475,7 @@ async function createProjectSyncService(rootsFallbackCwds) {
2238
2475
  fallbackCwds: [...getProjectConfigFallbackCwds(), ...rootsFallbackCwds]
2239
2476
  });
2240
2477
  const projectRoot = resolvedProjectConfig.projectRoot;
2241
- const targetProjectRoot = path7.resolve(
2478
+ const targetProjectRoot = path8.resolve(
2242
2479
  projectRoot,
2243
2480
  resolvedProjectConfig.projectConfig.targetProjectPath
2244
2481
  );
@@ -2292,6 +2529,29 @@ var LazyProjectSyncSource = class {
2292
2529
  async clearTemp() {
2293
2530
  return this.withService((service) => service.clearTemp());
2294
2531
  }
2532
+ async healthCheck() {
2533
+ const rootsFallbackCwds = this.getRootsFallbackCwds();
2534
+ const result = await runPrimeUiHealthCheck({
2535
+ cwd: process.cwd(),
2536
+ initCwd: process.env.INIT_CWD,
2537
+ pwd: process.env.PWD,
2538
+ projectRootFromEnv: process.env.PRIMEUI_PROJECT_ROOT,
2539
+ apiBaseUrlFromEnv: process.env.PRIMEUI_API_BASE_URL,
2540
+ apiKeyFromEnv: process.env.PRIMEUI_API_KEY,
2541
+ fallbackCwds: [...getProjectConfigFallbackCwds(), ...rootsFallbackCwds],
2542
+ rootsListFallbackCwds: rootsFallbackCwds
2543
+ });
2544
+ if (result.config.found) {
2545
+ console.error("[primeui-mcp] RESOLVED_PROJECT_ROOT:", result.config.projectRoot);
2546
+ console.error(
2547
+ "[primeui-mcp] RESOLVED_PROJECT_CONFIG:",
2548
+ result.config.projectConfigPath
2549
+ );
2550
+ } else {
2551
+ console.error("[primeui-mcp] RESOLUTION_FAILED:", result.config.error);
2552
+ }
2553
+ return result;
2554
+ }
2295
2555
  };
2296
2556
  function toFileRootPath(rootUri) {
2297
2557
  try {