@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 +40 -4
- package/dist/index.cjs +283 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +283 -66
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/skills/vite-mcp-next/SKILL.md +71 -0
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 配置,服务名默认是 `
|
|
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
|
-
"
|
|
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.
|
|
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: '
|
|
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 = "
|
|
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:
|
|
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:
|
|
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 =
|
|
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
|
|
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}
|
|
1832
|
+
`(?:^|\\n)(?:${plainHeader}|${plainChildHeader}|${quotedHeader}|${quotedChildHeader})`
|
|
1813
1833
|
);
|
|
1814
1834
|
}
|
|
1815
1835
|
function createTableHeader(serverName) {
|
|
1816
|
-
const key =
|
|
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
|
|
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
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
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
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
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
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
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
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
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}`);
|