@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/README.md CHANGED
@@ -43,7 +43,7 @@ SSE: http://localhost:<vite-port>/__mcp/sse
43
43
  Streamable HTTP: http://localhost:<vite-port>/__mcp/mcp
44
44
  ```
45
45
 
46
- 启动 Vite dev server 后,插件会按项目中已经存在的客户端入口自动写入项目级 MCP 配置,服务名默认是 `vue-mcp-next`。自动配置只会在缺少同名 server 条目时新增配置;如果用户已经配置了 `vue-mcp-next`,插件不会重复写入、覆盖原配置或改写用户自定义字段。
46
+ 启动 Vite dev server 后,插件会按项目中已经存在的客户端入口自动写入项目级 MCP 配置,服务名默认是 `vite-mcp-next`。自动配置只会在缺少同名 server 条目时新增配置;如果用户已经配置了历史默认名 `vue-mcp-next`,插件会把它迁移为 `vite-mcp-next` 并保留原配置内容。
47
47
 
48
48
  默认自动探测规则如下:
49
49
 
@@ -67,7 +67,7 @@ Cursor、Claude Code、Trae 等 JSON 配置客户端可以使用:
67
67
  ```json
68
68
  {
69
69
  "mcpServers": {
70
- "vue-mcp-next": {
70
+ "vite-mcp-next": {
71
71
  "type": "sse",
72
72
  "url": "http://localhost:5173/__mcp/sse"
73
73
  }
@@ -78,12 +78,44 @@ Cursor、Claude Code、Trae 等 JSON 配置客户端可以使用:
78
78
  Codex 使用 TOML 配置:
79
79
 
80
80
  ```toml
81
- [mcp_servers.vue-mcp-next]
81
+ [mcp_servers.vite-mcp-next]
82
82
  url = "http://localhost:5173/__mcp/mcp"
83
83
  ```
84
84
 
85
85
  `5173` 是示例端口。若 Vite 使用了其他端口,请替换为启动日志中打印的 MCP 地址。
86
86
 
87
+ ### 自动复制 AI 使用指南
88
+
89
+ 插件随 npm 包发布一份通用 Skill 文件:`skills/vite-mcp-next/SKILL.md`。Vite dev server 启动时,插件会在检测到项目中已经存在对应 AI 工具入口后,把这份静态文件复制到对应工具目录。插件不会在运行时拼接或生成 Skill 正文,包内 `skills/vite-mcp-next/SKILL.md` 是唯一内容来源。
90
+
91
+ | 客户端 | 探测入口 | 自动复制目标 | 说明 |
92
+ | ----------- | ---------- | ---------------------------------------------- | ------------------------------------- |
93
+ | Codex | `.codex/` | `.codex/skills/vite-mcp-next/SKILL.md` | 从包内 `skills/vite-mcp-next/SKILL.md` 复制 |
94
+ | Claude Code | `.claude/` | `.claude/skills/vite-mcp-next/SKILL.md` | 从包内 `skills/vite-mcp-next/SKILL.md` 复制 |
95
+ | Cursor | `.cursor/` | `.cursor/rules/vite-mcp-next.mdc` | 从同一份 Skill 文件复制,作为项目规则参考 |
96
+ | Trae | `.trae/` | 不自动复制 | 当前只自动写 MCP 配置,Rule 路径未作为首版稳定能力 |
97
+
98
+ 使用指南覆盖以下工具顺序:
99
+
100
+ 1. `list_pages`:先确认页面、runtime 和 CDP target
101
+ 2. `get_dom_tree` / `query_dom`:检查 DOM 结构
102
+ 3. `take_screenshot`:做视觉验证
103
+ 4. `get_console_logs`:排查 Console 报错
104
+ 5. `get_network_requests` / `get_network_request_detail`:排查接口请求
105
+ 6. `get_component_tree` / `get_component_state` / `get_router_info` / `get_pinia_tree` / `get_pinia_state`:检查 Vue 语义状态
106
+
107
+ 如果不希望插件复制任何 AI 使用指南,可以关闭:
108
+
109
+ ```ts
110
+ vueMcpNext({
111
+ skill: {
112
+ autoConfig: false
113
+ }
114
+ })
115
+ ```
116
+
117
+ 自动复制只会更新带有插件生成标记的文件;如果目标文件已经存在但不是插件生成的内容,插件会跳过并保留用户文件。没有使用某个 AI 工具时,对应入口目录不存在,插件不会创建该工具目录。
118
+
87
119
  ## 完整配置
88
120
 
89
121
  `mcpClients` 中的布尔值保留为开关语义:默认解析后为 `true`,但写入阶段会先做项目入口自动探测;用户在配置中显式写出 `true` 时表示强制创建对应客户端配置。
@@ -99,7 +131,10 @@ vueMcpNext({
99
131
  codex: true,
100
132
  claudeCode: true,
101
133
  trae: true,
102
- serverName: 'vue-mcp-next'
134
+ serverName: 'vite-mcp-next'
135
+ },
136
+ skill: {
137
+ autoConfig: true
103
138
  },
104
139
  appendTo: undefined,
105
140
  runtime: {
@@ -155,6 +190,7 @@ vueMcpNext({
155
190
  | `printUrl` | `boolean` | `true` | 是否在 Vite 启动日志中打印 MCP SSE 地址 |
156
191
  | `mcpClients` | `{ cursor?: boolean; codex?: boolean; claudeCode?: boolean; trae?: boolean; serverName?: string }` | 自动探测 | 是否写入 Cursor、Codex、Claude Code、Trae 的项目级 MCP 配置;默认只处理项目中已有入口,显式 `true` 强制创建 |
157
192
  | `updateCursorMcpJson` | `boolean \| { enabled: boolean; serverName?: string }` | 自动探测 | 兼容旧配置;默认只在 `.cursor` 已存在时写入,建议新项目使用 `mcpClients` |
193
+ | `skill` | `{ autoConfig?: boolean }` | `{ autoConfig: true }` | 是否在检测到 Codex、Claude Code、Cursor 项目入口时自动复制包内 AI 使用指南 |
158
194
  | `appendTo` | `string \| RegExp` | `undefined` | 非 HTML 入口注入点。配置后会在匹配入口模块前追加 runtime import |
159
195
  | `screenshot` | `ScreenshotOptions` | CDP 优先,snapdom 降级 | 页面截图配置,控制真截图、DOM 降级截图、体积上限和 snapdom 扩展 |
160
196
  | `screenshot.type` | `'path' \| 'base64'` | `'path'` | 项目级控制截图返回文件路径还是 base64 数据 |
package/dist/index.cjs CHANGED
@@ -81,7 +81,8 @@ var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-
81
81
  var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
82
82
  var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
83
83
  var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
84
- var DEFAULT_MCP_CLIENT_SERVER_NAME = "vue-mcp-next";
84
+ var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
85
+ var LEGACY_MCP_CLIENT_SERVER_NAMES = ["vue-mcp-next"];
85
86
  var RUNTIME_PAGE_RECONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-reconnected";
86
87
  var DEFAULT_OPTIONS = {
87
88
  mcpPath: DEFAULT_MCP_PATH,
@@ -98,6 +99,9 @@ var DEFAULT_OPTIONS = {
98
99
  trae: true,
99
100
  serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
100
101
  },
102
+ skill: {
103
+ autoConfig: true
104
+ },
101
105
  runtime: {
102
106
  mode: "auto",
103
107
  evaluate: {
@@ -155,6 +159,10 @@ function mergeOptions(options = {}) {
155
159
  ...options,
156
160
  updateCursorMcpJson: cursorConfig,
157
161
  mcpClients,
162
+ skill: {
163
+ ...DEFAULT_OPTIONS.skill,
164
+ ...options.skill
165
+ },
158
166
  runtime: {
159
167
  ...DEFAULT_OPTIONS.runtime,
160
168
  ...options.runtime,
@@ -1183,13 +1191,13 @@ function registerVueTools(server, ctx) {
1183
1191
  valueType: import_zod7.z.enum(["string", "number", "boolean", "object", "array"])
1184
1192
  }
1185
1193
  },
1186
- ({ componentName, path: path6, value, valueType }) => {
1194
+ ({ componentName, path: path8, value, valueType }) => {
1187
1195
  if (!ctx.rpcServer) {
1188
1196
  return vueBridgeUnavailable();
1189
1197
  }
1190
1198
  void ctx.rpcServer.editComponentState({
1191
1199
  componentName,
1192
- path: path6,
1200
+ path: path8,
1193
1201
  value,
1194
1202
  valueType
1195
1203
  });
@@ -1791,10 +1799,18 @@ async function updateCodexMcpClientConfig(options) {
1791
1799
  }
1792
1800
  function replaceOrAppendOwnedBlock(current, options) {
1793
1801
  const block = createCodexServerBlock(options);
1794
- const matcher = createOwnedBlockMatcher(options.serverName);
1802
+ const matcher = createServerTableMatcher(options.serverName);
1795
1803
  if (matcher.test(current)) {
1796
1804
  return ensureTrailingNewline(current);
1797
1805
  }
1806
+ const legacyServerName = options.legacyServerNames?.find(
1807
+ (serverName) => createServerTableMatcher(serverName).test(current)
1808
+ );
1809
+ if (legacyServerName) {
1810
+ return ensureTrailingNewline(
1811
+ renameServerTableHeaders(current, legacyServerName, options.serverName)
1812
+ );
1813
+ }
1798
1814
  const separator = current.trim() ? "\n\n" : "";
1799
1815
  return `${trimEndNewline(current)}${separator}${block}`;
1800
1816
  }
@@ -1803,25 +1819,47 @@ function createCodexServerBlock(options) {
1803
1819
  url = ${quoteTomlString(options.mcpUrl)}
1804
1820
  `;
1805
1821
  }
1806
- function createOwnedBlockMatcher(serverName) {
1822
+ function createServerTableMatcher(serverName) {
1807
1823
  const plainHeader = escapeRegExp(`[mcp_servers.${serverName}]`);
1824
+ const plainChildHeader = escapeRegExp(`[mcp_servers.${serverName}.`);
1808
1825
  const quotedHeader = escapeRegExp(
1809
1826
  `[mcp_servers.${quoteTomlKey(serverName)}]`
1810
1827
  );
1828
+ const quotedChildHeader = escapeRegExp(
1829
+ `[mcp_servers.${quoteTomlKey(serverName)}.`
1830
+ );
1811
1831
  return new RegExp(
1812
- `(?:^|\\n)(?:${plainHeader}|${quotedHeader})\\n[\\s\\S]*?(?=\\n\\[|$)`
1832
+ `(?:^|\\n)(?:${plainHeader}|${plainChildHeader}|${quotedHeader}|${quotedChildHeader})`
1813
1833
  );
1814
1834
  }
1815
1835
  function createTableHeader(serverName) {
1816
- const key = /^[A-Za-z0-9_-]+$/.test(serverName) ? serverName : quoteTomlKey(serverName);
1836
+ const key = createTableKey(serverName);
1817
1837
  return `[mcp_servers.${key}]`;
1818
1838
  }
1839
+ function createTableKey(serverName) {
1840
+ return /^[A-Za-z0-9_-]+$/.test(serverName) ? serverName : quoteTomlKey(serverName);
1841
+ }
1819
1842
  function quoteTomlKey(value) {
1820
1843
  return quoteTomlString(value);
1821
1844
  }
1822
1845
  function quoteTomlString(value) {
1823
1846
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
1824
1847
  }
1848
+ function renameServerTableHeaders(current, fromServerName, toServerName) {
1849
+ return current.split("\n").map((line) => renameServerTableHeader(line, fromServerName, toServerName)).join("\n");
1850
+ }
1851
+ function renameServerTableHeader(line, fromServerName, toServerName) {
1852
+ const toKey = createTableKey(toServerName);
1853
+ const fromKeys = [fromServerName, quoteTomlKey(fromServerName)];
1854
+ for (const fromKey of fromKeys) {
1855
+ const prefix = `[mcp_servers.${fromKey}`;
1856
+ const nextChar = line[prefix.length];
1857
+ if (line.startsWith(prefix) && (nextChar === "]" || nextChar === ".")) {
1858
+ return `[mcp_servers.${toKey}${line.slice(prefix.length)}`;
1859
+ }
1860
+ }
1861
+ return line;
1862
+ }
1825
1863
  async function readOptionalTextFile(filePath) {
1826
1864
  try {
1827
1865
  return await import_promises2.default.readFile(filePath, "utf-8");
@@ -1863,18 +1901,38 @@ async function updateJsonMcpClientConfig(options) {
1863
1901
  if (Object.hasOwn(mcpServers, options.serverName)) {
1864
1902
  return;
1865
1903
  }
1904
+ const migratedMcpServers = renameLegacyServer(mcpServers, options);
1905
+ if (migratedMcpServers) {
1906
+ config.mcpServers = migratedMcpServers;
1907
+ await writeJsonConfig(options.configPath, config);
1908
+ return;
1909
+ }
1866
1910
  mcpServers[options.serverName] = { type: "sse", url: options.mcpUrl };
1867
1911
  config.mcpServers = mcpServers;
1868
- await import_promises3.default.mkdir(import_node_path5.default.dirname(options.configPath), { recursive: true });
1869
- await import_promises3.default.writeFile(
1870
- options.configPath,
1871
- `${JSON.stringify(config, null, 2)}
1872
- `
1873
- );
1912
+ await writeJsonConfig(options.configPath, config);
1874
1913
  } catch (error) {
1875
1914
  warnConfigFailure(options, formatError2(error));
1876
1915
  }
1877
1916
  }
1917
+ function renameLegacyServer(mcpServers, options) {
1918
+ const legacyServerName = options.legacyServerNames?.find(
1919
+ (name) => Object.hasOwn(mcpServers, name)
1920
+ );
1921
+ if (!legacyServerName) {
1922
+ return void 0;
1923
+ }
1924
+ return Object.fromEntries(
1925
+ Object.entries(mcpServers).map(([serverName, serverConfig]) => [
1926
+ serverName === legacyServerName ? options.serverName : serverName,
1927
+ serverConfig
1928
+ ])
1929
+ );
1930
+ }
1931
+ async function writeJsonConfig(configPath, config) {
1932
+ await import_promises3.default.mkdir(import_node_path5.default.dirname(configPath), { recursive: true });
1933
+ await import_promises3.default.writeFile(configPath, `${JSON.stringify(config, null, 2)}
1934
+ `);
1935
+ }
1878
1936
  async function readJsonConfig(configPath) {
1879
1937
  const raw = await readOptionalTextFile2(configPath);
1880
1938
  if (!raw.trim()) {
@@ -1910,76 +1968,83 @@ function isNodeError2(error) {
1910
1968
  // src/plugin/mcpClientConfig/index.ts
1911
1969
  async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options, userOptions = {}) {
1912
1970
  const serverName = options.serverName;
1913
- const jobs = [];
1914
- if (await shouldUpdateClientConfig({
1915
- root,
1916
- clientName: "cursor",
1917
- enabled: options.cursor,
1918
- entryPath: import_node_path6.default.join(root, ".cursor"),
1919
- entryKind: "directory",
1920
- userOptions
1921
- })) {
1922
- jobs.push(
1923
- updateJsonMcpClientConfig({
1971
+ const legacyServerNames = getLegacyServerNames(serverName);
1972
+ const descriptors = [
1973
+ {
1974
+ root,
1975
+ clientName: "cursor",
1976
+ enabled: options.cursor,
1977
+ entryPath: import_node_path6.default.join(root, ".cursor"),
1978
+ entryKind: "directory",
1979
+ userOptions,
1980
+ createJob: () => updateJsonMcpClientConfig({
1924
1981
  clientName: "Cursor",
1925
1982
  configPath: import_node_path6.default.join(root, ".cursor", "mcp.json"),
1926
1983
  mcpUrl: sseUrl,
1927
- serverName
1984
+ serverName,
1985
+ legacyServerNames
1928
1986
  })
1929
- );
1930
- }
1931
- if (await shouldUpdateClientConfig({
1932
- root,
1933
- clientName: "codex",
1934
- enabled: options.codex,
1935
- entryPath: import_node_path6.default.join(root, ".codex"),
1936
- entryKind: "directory",
1937
- userOptions
1938
- })) {
1939
- jobs.push(
1940
- updateCodexMcpClientConfig({
1987
+ },
1988
+ {
1989
+ root,
1990
+ clientName: "codex",
1991
+ enabled: options.codex,
1992
+ entryPath: import_node_path6.default.join(root, ".codex"),
1993
+ entryKind: "directory",
1994
+ userOptions,
1995
+ createJob: () => updateCodexMcpClientConfig({
1941
1996
  configPath: import_node_path6.default.join(root, ".codex", "config.toml"),
1942
1997
  mcpUrl: streamableHttpUrl,
1943
- serverName
1998
+ serverName,
1999
+ legacyServerNames
1944
2000
  })
1945
- );
1946
- }
1947
- if (await shouldUpdateClientConfig({
1948
- root,
1949
- clientName: "claudeCode",
1950
- enabled: options.claudeCode,
1951
- entryPath: import_node_path6.default.join(root, ".mcp.json"),
1952
- entryKind: "file",
1953
- userOptions
1954
- })) {
1955
- jobs.push(
1956
- updateJsonMcpClientConfig({
2001
+ },
2002
+ {
2003
+ root,
2004
+ clientName: "claudeCode",
2005
+ enabled: options.claudeCode,
2006
+ entryPath: import_node_path6.default.join(root, ".mcp.json"),
2007
+ entryKind: "file",
2008
+ userOptions,
2009
+ createJob: () => updateJsonMcpClientConfig({
1957
2010
  clientName: "Claude Code",
1958
2011
  configPath: import_node_path6.default.join(root, ".mcp.json"),
1959
2012
  mcpUrl: sseUrl,
1960
- serverName
2013
+ serverName,
2014
+ legacyServerNames
1961
2015
  })
1962
- );
1963
- }
1964
- if (await shouldUpdateClientConfig({
1965
- root,
1966
- clientName: "trae",
1967
- enabled: options.trae,
1968
- entryPath: import_node_path6.default.join(root, ".trae"),
1969
- entryKind: "directory",
1970
- userOptions
1971
- })) {
1972
- jobs.push(
1973
- updateJsonMcpClientConfig({
2016
+ },
2017
+ {
2018
+ root,
2019
+ clientName: "trae",
2020
+ enabled: options.trae,
2021
+ entryPath: import_node_path6.default.join(root, ".trae"),
2022
+ entryKind: "directory",
2023
+ userOptions,
2024
+ createJob: () => updateJsonMcpClientConfig({
1974
2025
  clientName: "Trae",
1975
2026
  configPath: import_node_path6.default.join(root, ".trae", "mcp.json"),
1976
2027
  mcpUrl: sseUrl,
1977
- serverName
2028
+ serverName,
2029
+ legacyServerNames
1978
2030
  })
1979
- );
1980
- }
2031
+ }
2032
+ ];
2033
+ const jobs = await createClientConfigJobs(descriptors);
1981
2034
  await Promise.all(jobs);
1982
2035
  }
2036
+ async function createClientConfigJobs(descriptors) {
2037
+ const jobs = [];
2038
+ for (const descriptor of descriptors) {
2039
+ if (await shouldUpdateClientConfig(descriptor)) {
2040
+ jobs.push(descriptor.createJob());
2041
+ }
2042
+ }
2043
+ return jobs;
2044
+ }
2045
+ function getLegacyServerNames(serverName) {
2046
+ return serverName === DEFAULT_MCP_CLIENT_SERVER_NAME ? LEGACY_MCP_CLIENT_SERVER_NAMES : [];
2047
+ }
1983
2048
  async function shouldUpdateClientConfig(options) {
1984
2049
  if (!options.enabled) {
1985
2050
  return false;
@@ -2016,6 +2081,157 @@ function isNodeError3(error) {
2016
2081
  return error instanceof Error && "code" in error;
2017
2082
  }
2018
2083
 
2084
+ // src/plugin/skillConfig/index.ts
2085
+ var import_promises6 = __toESM(require("fs/promises"), 1);
2086
+ var import_node_path8 = __toESM(require("path"), 1);
2087
+
2088
+ // src/plugin/skillConfig/writers.ts
2089
+ var import_promises5 = __toESM(require("fs/promises"), 1);
2090
+ var import_node_path7 = __toESM(require("path"), 1);
2091
+ var GENERATED_SKILL_CONFIG_MARKER = "<!-- Generated by vite-plugin-vue-mcp-next. Safe to edit, but automatic updates only apply while this marker remains. -->";
2092
+ async function writeGeneratedTextFile(options) {
2093
+ try {
2094
+ const current = await readOptionalTextFile3(options.filePath);
2095
+ if (current && !current.includes(GENERATED_SKILL_CONFIG_MARKER)) {
2096
+ console.warn(
2097
+ `[vite-plugin-vue-mcp-next] Skipped ${options.targetName} at ${options.filePath}: file is not generated by this plugin`
2098
+ );
2099
+ return;
2100
+ }
2101
+ await import_promises5.default.mkdir(import_node_path7.default.dirname(options.filePath), { recursive: true });
2102
+ await import_promises5.default.writeFile(options.filePath, options.content);
2103
+ } catch (error) {
2104
+ console.warn(
2105
+ `[vite-plugin-vue-mcp-next] Failed to update ${options.targetName} at ${options.filePath}: ${formatError4(error)}`
2106
+ );
2107
+ }
2108
+ }
2109
+ async function readOptionalTextFile3(filePath) {
2110
+ try {
2111
+ return await import_promises5.default.readFile(filePath, "utf-8");
2112
+ } catch (error) {
2113
+ if (isNodeError4(error) && error.code === "ENOENT") {
2114
+ return "";
2115
+ }
2116
+ throw error;
2117
+ }
2118
+ }
2119
+ function formatError4(error) {
2120
+ return error instanceof Error ? error.message : String(error);
2121
+ }
2122
+ function isNodeError4(error) {
2123
+ return error instanceof Error && "code" in error;
2124
+ }
2125
+
2126
+ // src/plugin/skillConfig/index.ts
2127
+ var PACKAGE_NAME = "@xiaou66/vite-plugin-vue-mcp-next";
2128
+ var PACKAGED_SKILL_PATH = import_node_path8.default.join("skills", "vite-mcp-next", "SKILL.md");
2129
+ async function updateSkillConfigs(root, options) {
2130
+ if (!options.autoConfig) {
2131
+ return;
2132
+ }
2133
+ const descriptors = createSkillConfigDescriptors(root);
2134
+ const skillContent = await safelyReadPackagedSkillContent(root);
2135
+ if (!skillContent) {
2136
+ return;
2137
+ }
2138
+ const jobs = await createSkillConfigJobs(descriptors, skillContent);
2139
+ await Promise.all(jobs);
2140
+ }
2141
+ function createSkillConfigDescriptors(root) {
2142
+ return [
2143
+ {
2144
+ entryPath: import_node_path8.default.join(root, ".codex"),
2145
+ filePath: import_node_path8.default.join(root, ".codex", "skills", "vite-mcp-next", "SKILL.md"),
2146
+ targetName: "Codex skill"
2147
+ },
2148
+ {
2149
+ entryPath: import_node_path8.default.join(root, ".claude"),
2150
+ filePath: import_node_path8.default.join(
2151
+ root,
2152
+ ".claude",
2153
+ "skills",
2154
+ "vite-mcp-next",
2155
+ "SKILL.md"
2156
+ ),
2157
+ targetName: "Claude Code skill"
2158
+ },
2159
+ {
2160
+ entryPath: import_node_path8.default.join(root, ".cursor"),
2161
+ filePath: import_node_path8.default.join(root, ".cursor", "rules", "vite-mcp-next.mdc"),
2162
+ targetName: "Cursor rule"
2163
+ }
2164
+ ];
2165
+ }
2166
+ async function createSkillConfigJobs(descriptors, content) {
2167
+ const jobs = [];
2168
+ for (const descriptor of descriptors) {
2169
+ if (await hasDirectoryEntry(descriptor.entryPath)) {
2170
+ jobs.push(
2171
+ writeGeneratedTextFile({
2172
+ filePath: descriptor.filePath,
2173
+ content,
2174
+ targetName: descriptor.targetName
2175
+ })
2176
+ );
2177
+ }
2178
+ }
2179
+ return jobs;
2180
+ }
2181
+ async function readPackagedSkillContent(root) {
2182
+ const candidates = getPackagedSkillCandidates(root);
2183
+ for (const candidate of candidates) {
2184
+ try {
2185
+ return await import_promises6.default.readFile(candidate, "utf-8");
2186
+ } catch (error) {
2187
+ if (isNodeError5(error) && error.code === "ENOENT") {
2188
+ continue;
2189
+ }
2190
+ throw error;
2191
+ }
2192
+ }
2193
+ throw new Error(
2194
+ `Cannot find packaged vite-mcp-next skill at ${candidates.join(", ")}`
2195
+ );
2196
+ }
2197
+ async function safelyReadPackagedSkillContent(root) {
2198
+ try {
2199
+ return await readPackagedSkillContent(root);
2200
+ } catch (error) {
2201
+ console.warn(
2202
+ `[vite-plugin-vue-mcp-next] Failed to read packaged AI skill: ${formatError5(error)}`
2203
+ );
2204
+ return void 0;
2205
+ }
2206
+ }
2207
+ function getPackagedSkillCandidates(root) {
2208
+ return [
2209
+ import_node_path8.default.resolve(root, "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
2210
+ import_node_path8.default.resolve(process.cwd(), "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
2211
+ import_node_path8.default.resolve(process.cwd(), PACKAGED_SKILL_PATH)
2212
+ ];
2213
+ }
2214
+ async function hasDirectoryEntry(entryPath) {
2215
+ try {
2216
+ const stat = await import_promises6.default.stat(entryPath);
2217
+ return stat.isDirectory();
2218
+ } catch (error) {
2219
+ if (isNodeError5(error) && error.code === "ENOENT") {
2220
+ return false;
2221
+ }
2222
+ console.warn(
2223
+ `[vite-plugin-vue-mcp-next] Failed to inspect skill config entry at ${entryPath}: ${formatError5(error)}`
2224
+ );
2225
+ return false;
2226
+ }
2227
+ }
2228
+ function formatError5(error) {
2229
+ return error instanceof Error ? error.message : String(error);
2230
+ }
2231
+ function isNodeError5(error) {
2232
+ return error instanceof Error && "code" in error;
2233
+ }
2234
+
2019
2235
  // src/plugin/createPlugin.ts
2020
2236
  var import_vite_dev_rpc = require("vite-dev-rpc");
2021
2237
  function vueMcpNext(userOptions = {}) {
@@ -2087,6 +2303,7 @@ function vueMcpNext(userOptions = {}) {
2087
2303
  options.mcpClients,
2088
2304
  userOptions
2089
2305
  );
2306
+ await updateSkillConfigs(root, options.skill);
2090
2307
  if (options.printUrl) {
2091
2308
  setTimeout(() => {
2092
2309
  console.log(` \u279C MCP: SSE server is running at ${mcpSseUrl}`);