@xiaou66/vite-plugin-vue-mcp-next 1.0.1 → 1.0.2

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/index.d.cts CHANGED
@@ -33,6 +33,8 @@ interface VueMcpNextOptions {
33
33
  readonly updateCursorMcpJson?: boolean | CursorMcpConfig;
34
34
  /** 是否写入常见 AI 客户端的项目级 MCP 配置;默认按项目已有客户端入口自动探测。 */
35
35
  readonly mcpClients?: McpClientConfigOptions;
36
+ /** AI 使用指南自动安装配置,用于让 AI 客户端知道何时以及如何使用本 MCP。 */
37
+ readonly skill?: SkillConfigOptions;
36
38
  /** 非 HTML 入口的运行时脚本注入点,用于兼容不直接使用 `index.html` 的项目。 */
37
39
  readonly appendTo?: string | RegExp;
38
40
  /** 通用 DevTools 能力配置;Vue 专属能力不受该配置影响,始终走 Vue Runtime Bridge。 */
@@ -77,6 +79,16 @@ interface McpClientConfigOptions {
77
79
  /** MCP 客户端中展示的服务名,同一项目存在多个 MCP 服务时用于避免冲突。 */
78
80
  readonly serverName?: string;
79
81
  }
82
+ /**
83
+ * AI 使用指南自动安装配置。
84
+ *
85
+ * 该配置只影响项目级 skill/rule 文件写入,不影响 MCP 服务是否启动;
86
+ * 独立开关可以让不希望插件修改 AI 客户端上下文的项目完全关闭自动安装。
87
+ */
88
+ interface SkillConfigOptions {
89
+ /** 是否在 Vite dev server 启动时自动安装 AI 使用指南,默认开启。 */
90
+ readonly autoConfig?: boolean;
91
+ }
80
92
  /**
81
93
  * 通用 DevTools 能力的运行模式。
82
94
  *
@@ -320,6 +332,8 @@ interface ResolvedVueMcpNextOptions {
320
332
  readonly updateCursorMcpJson: Required<CursorMcpConfig>;
321
333
  /** 已规范化的多客户端 MCP 配置写入策略,内部写入器只读取该对象。 */
322
334
  readonly mcpClients: Required<McpClientConfigOptions>;
335
+ /** 已补齐默认值的 AI 使用指南自动安装配置。 */
336
+ readonly skill: Required<SkillConfigOptions>;
323
337
  /** 非 HTML 入口注入点,未配置时 HTML 注入路径生效。 */
324
338
  readonly appendTo?: string | RegExp;
325
339
  /** 已补齐默认值的通用 DevTools 配置。 */
@@ -569,4 +583,4 @@ interface VueRuntimeRpc {
569
583
  */
570
584
  declare function vueMcpNext(userOptions?: VueMcpNextOptions): Plugin;
571
585
 
572
- export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
586
+ export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type SkillConfigOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
package/dist/index.d.ts CHANGED
@@ -33,6 +33,8 @@ interface VueMcpNextOptions {
33
33
  readonly updateCursorMcpJson?: boolean | CursorMcpConfig;
34
34
  /** 是否写入常见 AI 客户端的项目级 MCP 配置;默认按项目已有客户端入口自动探测。 */
35
35
  readonly mcpClients?: McpClientConfigOptions;
36
+ /** AI 使用指南自动安装配置,用于让 AI 客户端知道何时以及如何使用本 MCP。 */
37
+ readonly skill?: SkillConfigOptions;
36
38
  /** 非 HTML 入口的运行时脚本注入点,用于兼容不直接使用 `index.html` 的项目。 */
37
39
  readonly appendTo?: string | RegExp;
38
40
  /** 通用 DevTools 能力配置;Vue 专属能力不受该配置影响,始终走 Vue Runtime Bridge。 */
@@ -77,6 +79,16 @@ interface McpClientConfigOptions {
77
79
  /** MCP 客户端中展示的服务名,同一项目存在多个 MCP 服务时用于避免冲突。 */
78
80
  readonly serverName?: string;
79
81
  }
82
+ /**
83
+ * AI 使用指南自动安装配置。
84
+ *
85
+ * 该配置只影响项目级 skill/rule 文件写入,不影响 MCP 服务是否启动;
86
+ * 独立开关可以让不希望插件修改 AI 客户端上下文的项目完全关闭自动安装。
87
+ */
88
+ interface SkillConfigOptions {
89
+ /** 是否在 Vite dev server 启动时自动安装 AI 使用指南,默认开启。 */
90
+ readonly autoConfig?: boolean;
91
+ }
80
92
  /**
81
93
  * 通用 DevTools 能力的运行模式。
82
94
  *
@@ -320,6 +332,8 @@ interface ResolvedVueMcpNextOptions {
320
332
  readonly updateCursorMcpJson: Required<CursorMcpConfig>;
321
333
  /** 已规范化的多客户端 MCP 配置写入策略,内部写入器只读取该对象。 */
322
334
  readonly mcpClients: Required<McpClientConfigOptions>;
335
+ /** 已补齐默认值的 AI 使用指南自动安装配置。 */
336
+ readonly skill: Required<SkillConfigOptions>;
323
337
  /** 非 HTML 入口注入点,未配置时 HTML 注入路径生效。 */
324
338
  readonly appendTo?: string | RegExp;
325
339
  /** 已补齐默认值的通用 DevTools 配置。 */
@@ -569,4 +583,4 @@ interface VueRuntimeRpc {
569
583
  */
570
584
  declare function vueMcpNext(userOptions?: VueMcpNextOptions): Plugin;
571
585
 
572
- export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
586
+ export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type SkillConfigOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
package/dist/index.js CHANGED
@@ -44,7 +44,8 @@ var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-
44
44
  var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
45
45
  var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
46
46
  var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
47
- var DEFAULT_MCP_CLIENT_SERVER_NAME = "vue-mcp-next";
47
+ var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
48
+ var LEGACY_MCP_CLIENT_SERVER_NAMES = ["vue-mcp-next"];
48
49
  var RUNTIME_PAGE_RECONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-reconnected";
49
50
  var DEFAULT_OPTIONS = {
50
51
  mcpPath: DEFAULT_MCP_PATH,
@@ -61,6 +62,9 @@ var DEFAULT_OPTIONS = {
61
62
  trae: true,
62
63
  serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
63
64
  },
65
+ skill: {
66
+ autoConfig: true
67
+ },
64
68
  runtime: {
65
69
  mode: "auto",
66
70
  evaluate: {
@@ -118,6 +122,10 @@ function mergeOptions(options = {}) {
118
122
  ...options,
119
123
  updateCursorMcpJson: cursorConfig,
120
124
  mcpClients,
125
+ skill: {
126
+ ...DEFAULT_OPTIONS.skill,
127
+ ...options.skill
128
+ },
121
129
  runtime: {
122
130
  ...DEFAULT_OPTIONS.runtime,
123
131
  ...options.runtime,
@@ -1146,13 +1154,13 @@ function registerVueTools(server, ctx) {
1146
1154
  valueType: z7.enum(["string", "number", "boolean", "object", "array"])
1147
1155
  }
1148
1156
  },
1149
- ({ componentName, path: path6, value, valueType }) => {
1157
+ ({ componentName, path: path8, value, valueType }) => {
1150
1158
  if (!ctx.rpcServer) {
1151
1159
  return vueBridgeUnavailable();
1152
1160
  }
1153
1161
  void ctx.rpcServer.editComponentState({
1154
1162
  componentName,
1155
- path: path6,
1163
+ path: path8,
1156
1164
  value,
1157
1165
  valueType
1158
1166
  });
@@ -1754,10 +1762,18 @@ async function updateCodexMcpClientConfig(options) {
1754
1762
  }
1755
1763
  function replaceOrAppendOwnedBlock(current, options) {
1756
1764
  const block = createCodexServerBlock(options);
1757
- const matcher = createOwnedBlockMatcher(options.serverName);
1765
+ const matcher = createServerTableMatcher(options.serverName);
1758
1766
  if (matcher.test(current)) {
1759
1767
  return ensureTrailingNewline(current);
1760
1768
  }
1769
+ const legacyServerName = options.legacyServerNames?.find(
1770
+ (serverName) => createServerTableMatcher(serverName).test(current)
1771
+ );
1772
+ if (legacyServerName) {
1773
+ return ensureTrailingNewline(
1774
+ renameServerTableHeaders(current, legacyServerName, options.serverName)
1775
+ );
1776
+ }
1761
1777
  const separator = current.trim() ? "\n\n" : "";
1762
1778
  return `${trimEndNewline(current)}${separator}${block}`;
1763
1779
  }
@@ -1766,25 +1782,47 @@ function createCodexServerBlock(options) {
1766
1782
  url = ${quoteTomlString(options.mcpUrl)}
1767
1783
  `;
1768
1784
  }
1769
- function createOwnedBlockMatcher(serverName) {
1785
+ function createServerTableMatcher(serverName) {
1770
1786
  const plainHeader = escapeRegExp(`[mcp_servers.${serverName}]`);
1787
+ const plainChildHeader = escapeRegExp(`[mcp_servers.${serverName}.`);
1771
1788
  const quotedHeader = escapeRegExp(
1772
1789
  `[mcp_servers.${quoteTomlKey(serverName)}]`
1773
1790
  );
1791
+ const quotedChildHeader = escapeRegExp(
1792
+ `[mcp_servers.${quoteTomlKey(serverName)}.`
1793
+ );
1774
1794
  return new RegExp(
1775
- `(?:^|\\n)(?:${plainHeader}|${quotedHeader})\\n[\\s\\S]*?(?=\\n\\[|$)`
1795
+ `(?:^|\\n)(?:${plainHeader}|${plainChildHeader}|${quotedHeader}|${quotedChildHeader})`
1776
1796
  );
1777
1797
  }
1778
1798
  function createTableHeader(serverName) {
1779
- const key = /^[A-Za-z0-9_-]+$/.test(serverName) ? serverName : quoteTomlKey(serverName);
1799
+ const key = createTableKey(serverName);
1780
1800
  return `[mcp_servers.${key}]`;
1781
1801
  }
1802
+ function createTableKey(serverName) {
1803
+ return /^[A-Za-z0-9_-]+$/.test(serverName) ? serverName : quoteTomlKey(serverName);
1804
+ }
1782
1805
  function quoteTomlKey(value) {
1783
1806
  return quoteTomlString(value);
1784
1807
  }
1785
1808
  function quoteTomlString(value) {
1786
1809
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
1787
1810
  }
1811
+ function renameServerTableHeaders(current, fromServerName, toServerName) {
1812
+ return current.split("\n").map((line) => renameServerTableHeader(line, fromServerName, toServerName)).join("\n");
1813
+ }
1814
+ function renameServerTableHeader(line, fromServerName, toServerName) {
1815
+ const toKey = createTableKey(toServerName);
1816
+ const fromKeys = [fromServerName, quoteTomlKey(fromServerName)];
1817
+ for (const fromKey of fromKeys) {
1818
+ const prefix = `[mcp_servers.${fromKey}`;
1819
+ const nextChar = line[prefix.length];
1820
+ if (line.startsWith(prefix) && (nextChar === "]" || nextChar === ".")) {
1821
+ return `[mcp_servers.${toKey}${line.slice(prefix.length)}`;
1822
+ }
1823
+ }
1824
+ return line;
1825
+ }
1788
1826
  async function readOptionalTextFile(filePath) {
1789
1827
  try {
1790
1828
  return await fs2.readFile(filePath, "utf-8");
@@ -1826,18 +1864,38 @@ async function updateJsonMcpClientConfig(options) {
1826
1864
  if (Object.hasOwn(mcpServers, options.serverName)) {
1827
1865
  return;
1828
1866
  }
1867
+ const migratedMcpServers = renameLegacyServer(mcpServers, options);
1868
+ if (migratedMcpServers) {
1869
+ config.mcpServers = migratedMcpServers;
1870
+ await writeJsonConfig(options.configPath, config);
1871
+ return;
1872
+ }
1829
1873
  mcpServers[options.serverName] = { type: "sse", url: options.mcpUrl };
1830
1874
  config.mcpServers = mcpServers;
1831
- await fs3.mkdir(path4.dirname(options.configPath), { recursive: true });
1832
- await fs3.writeFile(
1833
- options.configPath,
1834
- `${JSON.stringify(config, null, 2)}
1835
- `
1836
- );
1875
+ await writeJsonConfig(options.configPath, config);
1837
1876
  } catch (error) {
1838
1877
  warnConfigFailure(options, formatError2(error));
1839
1878
  }
1840
1879
  }
1880
+ function renameLegacyServer(mcpServers, options) {
1881
+ const legacyServerName = options.legacyServerNames?.find(
1882
+ (name) => Object.hasOwn(mcpServers, name)
1883
+ );
1884
+ if (!legacyServerName) {
1885
+ return void 0;
1886
+ }
1887
+ return Object.fromEntries(
1888
+ Object.entries(mcpServers).map(([serverName, serverConfig]) => [
1889
+ serverName === legacyServerName ? options.serverName : serverName,
1890
+ serverConfig
1891
+ ])
1892
+ );
1893
+ }
1894
+ async function writeJsonConfig(configPath, config) {
1895
+ await fs3.mkdir(path4.dirname(configPath), { recursive: true });
1896
+ await fs3.writeFile(configPath, `${JSON.stringify(config, null, 2)}
1897
+ `);
1898
+ }
1841
1899
  async function readJsonConfig(configPath) {
1842
1900
  const raw = await readOptionalTextFile2(configPath);
1843
1901
  if (!raw.trim()) {
@@ -1873,76 +1931,83 @@ function isNodeError2(error) {
1873
1931
  // src/plugin/mcpClientConfig/index.ts
1874
1932
  async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options, userOptions = {}) {
1875
1933
  const serverName = options.serverName;
1876
- const jobs = [];
1877
- if (await shouldUpdateClientConfig({
1878
- root,
1879
- clientName: "cursor",
1880
- enabled: options.cursor,
1881
- entryPath: path5.join(root, ".cursor"),
1882
- entryKind: "directory",
1883
- userOptions
1884
- })) {
1885
- jobs.push(
1886
- updateJsonMcpClientConfig({
1934
+ const legacyServerNames = getLegacyServerNames(serverName);
1935
+ const descriptors = [
1936
+ {
1937
+ root,
1938
+ clientName: "cursor",
1939
+ enabled: options.cursor,
1940
+ entryPath: path5.join(root, ".cursor"),
1941
+ entryKind: "directory",
1942
+ userOptions,
1943
+ createJob: () => updateJsonMcpClientConfig({
1887
1944
  clientName: "Cursor",
1888
1945
  configPath: path5.join(root, ".cursor", "mcp.json"),
1889
1946
  mcpUrl: sseUrl,
1890
- serverName
1947
+ serverName,
1948
+ legacyServerNames
1891
1949
  })
1892
- );
1893
- }
1894
- if (await shouldUpdateClientConfig({
1895
- root,
1896
- clientName: "codex",
1897
- enabled: options.codex,
1898
- entryPath: path5.join(root, ".codex"),
1899
- entryKind: "directory",
1900
- userOptions
1901
- })) {
1902
- jobs.push(
1903
- updateCodexMcpClientConfig({
1950
+ },
1951
+ {
1952
+ root,
1953
+ clientName: "codex",
1954
+ enabled: options.codex,
1955
+ entryPath: path5.join(root, ".codex"),
1956
+ entryKind: "directory",
1957
+ userOptions,
1958
+ createJob: () => updateCodexMcpClientConfig({
1904
1959
  configPath: path5.join(root, ".codex", "config.toml"),
1905
1960
  mcpUrl: streamableHttpUrl,
1906
- serverName
1961
+ serverName,
1962
+ legacyServerNames
1907
1963
  })
1908
- );
1909
- }
1910
- if (await shouldUpdateClientConfig({
1911
- root,
1912
- clientName: "claudeCode",
1913
- enabled: options.claudeCode,
1914
- entryPath: path5.join(root, ".mcp.json"),
1915
- entryKind: "file",
1916
- userOptions
1917
- })) {
1918
- jobs.push(
1919
- updateJsonMcpClientConfig({
1964
+ },
1965
+ {
1966
+ root,
1967
+ clientName: "claudeCode",
1968
+ enabled: options.claudeCode,
1969
+ entryPath: path5.join(root, ".mcp.json"),
1970
+ entryKind: "file",
1971
+ userOptions,
1972
+ createJob: () => updateJsonMcpClientConfig({
1920
1973
  clientName: "Claude Code",
1921
1974
  configPath: path5.join(root, ".mcp.json"),
1922
1975
  mcpUrl: sseUrl,
1923
- serverName
1976
+ serverName,
1977
+ legacyServerNames
1924
1978
  })
1925
- );
1926
- }
1927
- if (await shouldUpdateClientConfig({
1928
- root,
1929
- clientName: "trae",
1930
- enabled: options.trae,
1931
- entryPath: path5.join(root, ".trae"),
1932
- entryKind: "directory",
1933
- userOptions
1934
- })) {
1935
- jobs.push(
1936
- updateJsonMcpClientConfig({
1979
+ },
1980
+ {
1981
+ root,
1982
+ clientName: "trae",
1983
+ enabled: options.trae,
1984
+ entryPath: path5.join(root, ".trae"),
1985
+ entryKind: "directory",
1986
+ userOptions,
1987
+ createJob: () => updateJsonMcpClientConfig({
1937
1988
  clientName: "Trae",
1938
1989
  configPath: path5.join(root, ".trae", "mcp.json"),
1939
1990
  mcpUrl: sseUrl,
1940
- serverName
1991
+ serverName,
1992
+ legacyServerNames
1941
1993
  })
1942
- );
1943
- }
1994
+ }
1995
+ ];
1996
+ const jobs = await createClientConfigJobs(descriptors);
1944
1997
  await Promise.all(jobs);
1945
1998
  }
1999
+ async function createClientConfigJobs(descriptors) {
2000
+ const jobs = [];
2001
+ for (const descriptor of descriptors) {
2002
+ if (await shouldUpdateClientConfig(descriptor)) {
2003
+ jobs.push(descriptor.createJob());
2004
+ }
2005
+ }
2006
+ return jobs;
2007
+ }
2008
+ function getLegacyServerNames(serverName) {
2009
+ return serverName === DEFAULT_MCP_CLIENT_SERVER_NAME ? LEGACY_MCP_CLIENT_SERVER_NAMES : [];
2010
+ }
1946
2011
  async function shouldUpdateClientConfig(options) {
1947
2012
  if (!options.enabled) {
1948
2013
  return false;
@@ -1979,6 +2044,157 @@ function isNodeError3(error) {
1979
2044
  return error instanceof Error && "code" in error;
1980
2045
  }
1981
2046
 
2047
+ // src/plugin/skillConfig/index.ts
2048
+ import fs6 from "fs/promises";
2049
+ import path7 from "path";
2050
+
2051
+ // src/plugin/skillConfig/writers.ts
2052
+ import fs5 from "fs/promises";
2053
+ import path6 from "path";
2054
+ var GENERATED_SKILL_CONFIG_MARKER = "<!-- Generated by vite-plugin-vue-mcp-next. Safe to edit, but automatic updates only apply while this marker remains. -->";
2055
+ async function writeGeneratedTextFile(options) {
2056
+ try {
2057
+ const current = await readOptionalTextFile3(options.filePath);
2058
+ if (current && !current.includes(GENERATED_SKILL_CONFIG_MARKER)) {
2059
+ console.warn(
2060
+ `[vite-plugin-vue-mcp-next] Skipped ${options.targetName} at ${options.filePath}: file is not generated by this plugin`
2061
+ );
2062
+ return;
2063
+ }
2064
+ await fs5.mkdir(path6.dirname(options.filePath), { recursive: true });
2065
+ await fs5.writeFile(options.filePath, options.content);
2066
+ } catch (error) {
2067
+ console.warn(
2068
+ `[vite-plugin-vue-mcp-next] Failed to update ${options.targetName} at ${options.filePath}: ${formatError4(error)}`
2069
+ );
2070
+ }
2071
+ }
2072
+ async function readOptionalTextFile3(filePath) {
2073
+ try {
2074
+ return await fs5.readFile(filePath, "utf-8");
2075
+ } catch (error) {
2076
+ if (isNodeError4(error) && error.code === "ENOENT") {
2077
+ return "";
2078
+ }
2079
+ throw error;
2080
+ }
2081
+ }
2082
+ function formatError4(error) {
2083
+ return error instanceof Error ? error.message : String(error);
2084
+ }
2085
+ function isNodeError4(error) {
2086
+ return error instanceof Error && "code" in error;
2087
+ }
2088
+
2089
+ // src/plugin/skillConfig/index.ts
2090
+ var PACKAGE_NAME = "@xiaou66/vite-plugin-vue-mcp-next";
2091
+ var PACKAGED_SKILL_PATH = path7.join("skills", "vite-mcp-next", "SKILL.md");
2092
+ async function updateSkillConfigs(root, options) {
2093
+ if (!options.autoConfig) {
2094
+ return;
2095
+ }
2096
+ const descriptors = createSkillConfigDescriptors(root);
2097
+ const skillContent = await safelyReadPackagedSkillContent(root);
2098
+ if (!skillContent) {
2099
+ return;
2100
+ }
2101
+ const jobs = await createSkillConfigJobs(descriptors, skillContent);
2102
+ await Promise.all(jobs);
2103
+ }
2104
+ function createSkillConfigDescriptors(root) {
2105
+ return [
2106
+ {
2107
+ entryPath: path7.join(root, ".codex"),
2108
+ filePath: path7.join(root, ".codex", "skills", "vite-mcp-next", "SKILL.md"),
2109
+ targetName: "Codex skill"
2110
+ },
2111
+ {
2112
+ entryPath: path7.join(root, ".claude"),
2113
+ filePath: path7.join(
2114
+ root,
2115
+ ".claude",
2116
+ "skills",
2117
+ "vite-mcp-next",
2118
+ "SKILL.md"
2119
+ ),
2120
+ targetName: "Claude Code skill"
2121
+ },
2122
+ {
2123
+ entryPath: path7.join(root, ".cursor"),
2124
+ filePath: path7.join(root, ".cursor", "rules", "vite-mcp-next.mdc"),
2125
+ targetName: "Cursor rule"
2126
+ }
2127
+ ];
2128
+ }
2129
+ async function createSkillConfigJobs(descriptors, content) {
2130
+ const jobs = [];
2131
+ for (const descriptor of descriptors) {
2132
+ if (await hasDirectoryEntry(descriptor.entryPath)) {
2133
+ jobs.push(
2134
+ writeGeneratedTextFile({
2135
+ filePath: descriptor.filePath,
2136
+ content,
2137
+ targetName: descriptor.targetName
2138
+ })
2139
+ );
2140
+ }
2141
+ }
2142
+ return jobs;
2143
+ }
2144
+ async function readPackagedSkillContent(root) {
2145
+ const candidates = getPackagedSkillCandidates(root);
2146
+ for (const candidate of candidates) {
2147
+ try {
2148
+ return await fs6.readFile(candidate, "utf-8");
2149
+ } catch (error) {
2150
+ if (isNodeError5(error) && error.code === "ENOENT") {
2151
+ continue;
2152
+ }
2153
+ throw error;
2154
+ }
2155
+ }
2156
+ throw new Error(
2157
+ `Cannot find packaged vite-mcp-next skill at ${candidates.join(", ")}`
2158
+ );
2159
+ }
2160
+ async function safelyReadPackagedSkillContent(root) {
2161
+ try {
2162
+ return await readPackagedSkillContent(root);
2163
+ } catch (error) {
2164
+ console.warn(
2165
+ `[vite-plugin-vue-mcp-next] Failed to read packaged AI skill: ${formatError5(error)}`
2166
+ );
2167
+ return void 0;
2168
+ }
2169
+ }
2170
+ function getPackagedSkillCandidates(root) {
2171
+ return [
2172
+ path7.resolve(root, "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
2173
+ path7.resolve(process.cwd(), "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
2174
+ path7.resolve(process.cwd(), PACKAGED_SKILL_PATH)
2175
+ ];
2176
+ }
2177
+ async function hasDirectoryEntry(entryPath) {
2178
+ try {
2179
+ const stat = await fs6.stat(entryPath);
2180
+ return stat.isDirectory();
2181
+ } catch (error) {
2182
+ if (isNodeError5(error) && error.code === "ENOENT") {
2183
+ return false;
2184
+ }
2185
+ console.warn(
2186
+ `[vite-plugin-vue-mcp-next] Failed to inspect skill config entry at ${entryPath}: ${formatError5(error)}`
2187
+ );
2188
+ return false;
2189
+ }
2190
+ }
2191
+ function formatError5(error) {
2192
+ return error instanceof Error ? error.message : String(error);
2193
+ }
2194
+ function isNodeError5(error) {
2195
+ return error instanceof Error && "code" in error;
2196
+ }
2197
+
1982
2198
  // src/plugin/createPlugin.ts
1983
2199
  import { createRPCServer } from "vite-dev-rpc";
1984
2200
  function vueMcpNext(userOptions = {}) {
@@ -2050,6 +2266,7 @@ function vueMcpNext(userOptions = {}) {
2050
2266
  options.mcpClients,
2051
2267
  userOptions
2052
2268
  );
2269
+ await updateSkillConfigs(root, options.skill);
2053
2270
  if (options.printUrl) {
2054
2271
  setTimeout(() => {
2055
2272
  console.log(` \u279C MCP: SSE server is running at ${mcpSseUrl}`);