@agile-team/wl-skills-kit 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -1
- package/README.md +104 -39
- package/bin/wl-skills.js +344 -44
- package/docs/ai/345/205/250/346/231/257/345/210/206/346/236/220.md +3 -3
- package/files/.github/copilot-instructions.md +361 -322
- package/files/.github/guides/architecture.md +6 -3
- package/files/.github/guides/usage.md +48 -13
- package/files/.github/skills/_compat/README.md +4 -2
- package/files/.github/skills/_registry.md +18 -16
- package/files/.github/skills/core/page-codegen/SKILL.md +149 -74
- package/files/.github/skills/core/page-codegen/USAGE.md +33 -12
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +80 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-FORM-ROUTE.md +183 -55
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-LIST.md +110 -21
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-MASTER-DETAIL.md +29 -9
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-RECORD-FORM.md +93 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +49 -29
- package/files/.github/skills/sync/menu-sync/SKILL.md +27 -13
- package/mcp/server.js +279 -195
- package/mcp/tools/menuSync.js +416 -96
- package/mcp/tools/projectTools.js +336 -124
- package/package.json +31 -2
package/mcp/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
"use strict";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* wl-skills MCP Server
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* 暴露工具:
|
|
9
9
|
* wls_menu_query 查询菜单树
|
|
10
10
|
* wls_menu_upsert 批量新增/更新菜单
|
|
11
|
+
* wls_menu_sync_from_report 从 SYS_MENU_INFO*.md 确定性同步菜单
|
|
11
12
|
* wls_dict_query 查询字典模块
|
|
12
13
|
* wls_dict_upsert 新增/更新字典模块及字典项
|
|
13
14
|
* wls_role_query 查询角色列表
|
|
@@ -25,10 +26,14 @@
|
|
|
25
26
|
* node node_modules/@agile-team/wl-skills-kit/mcp/server.js
|
|
26
27
|
*/
|
|
27
28
|
|
|
28
|
-
const readline = require(
|
|
29
|
-
const { loadConfig } = require(
|
|
30
|
-
const {
|
|
31
|
-
|
|
29
|
+
const readline = require("readline");
|
|
30
|
+
const { loadConfig } = require("./config");
|
|
31
|
+
const {
|
|
32
|
+
handleMenuQuery,
|
|
33
|
+
handleMenuUpsert,
|
|
34
|
+
handleMenuSyncFromReport,
|
|
35
|
+
} = require("./tools/menuSync");
|
|
36
|
+
const { handleDictQuery, handleDictUpsert } = require("./tools/dictSync");
|
|
32
37
|
const {
|
|
33
38
|
handleRoleQuery,
|
|
34
39
|
handleRoleUpsert,
|
|
@@ -36,376 +41,455 @@ const {
|
|
|
36
41
|
handleAssignableMenusQuery,
|
|
37
42
|
handleActionQuery,
|
|
38
43
|
handleActionUpsert,
|
|
39
|
-
} = require(
|
|
44
|
+
} = require("./tools/permissionSync");
|
|
40
45
|
const {
|
|
41
46
|
handleCodeScan,
|
|
47
|
+
handleValidatePage,
|
|
48
|
+
handleDoctorUi,
|
|
42
49
|
handleRouteCheck,
|
|
43
50
|
handleGitLogExtract,
|
|
44
51
|
handleAuditReportPush,
|
|
45
|
-
} = require(
|
|
52
|
+
} = require("./tools/projectTools");
|
|
46
53
|
|
|
47
|
-
const PKG = require(
|
|
54
|
+
const PKG = require("../package.json");
|
|
48
55
|
|
|
49
56
|
// ─── Tool 注册表 ────────────────────────────────────────────────────────
|
|
50
57
|
|
|
51
58
|
const TOOLS = [
|
|
52
59
|
{
|
|
53
|
-
name:
|
|
60
|
+
name: "wls_menu_query",
|
|
54
61
|
description:
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
"查询当前应用的完整菜单树。自动从 .github/skills/sync/env.local.json 读取 domainId," +
|
|
63
|
+
"无需传参。在 wls_menu_upsert 前调用,用于判断哪些菜单需要新增、哪些需要更新。",
|
|
57
64
|
inputSchema: {
|
|
58
|
-
type:
|
|
65
|
+
type: "object",
|
|
59
66
|
properties: {},
|
|
60
67
|
required: [],
|
|
61
68
|
},
|
|
62
69
|
},
|
|
63
70
|
{
|
|
64
|
-
name:
|
|
71
|
+
name: "wls_menu_upsert",
|
|
65
72
|
description:
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
"批量新增或更新菜单项。有 id 字段 → 更新;无 id 字段 → 新增。" +
|
|
74
|
+
"新增时响应自动包含服务端生成的 id,可链式用于创建子菜单。",
|
|
68
75
|
inputSchema: {
|
|
69
|
-
type:
|
|
76
|
+
type: "object",
|
|
70
77
|
properties: {
|
|
71
78
|
items: {
|
|
72
|
-
type:
|
|
79
|
+
type: "array",
|
|
73
80
|
description:
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
"MenuSaveBody 数组。每项字段:" +
|
|
82
|
+
"id(更新时传), sysAppNo, menuName, menuNameCode, parentId, " +
|
|
76
83
|
'type("M"=目录/"C"=菜单), path, icon, orderNum, ' +
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
items: { type:
|
|
84
|
+
"useCache(1), common(2), hidden(false), editMode(false), " +
|
|
85
|
+
"component(type=C时传), permission(type=C时传)",
|
|
86
|
+
items: { type: "object" },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ["items"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "wls_menu_sync_from_report",
|
|
94
|
+
description:
|
|
95
|
+
"读取 .github/reports/SYS_MENU_INFO*.md,按一级目录(type=M)优先、二级菜单(type=C)随后同步到后端菜单。" +
|
|
96
|
+
"自动查询 domain 菜单树去重,复用或更新已存在菜单,避免把二级页面全部挂到根 parentMenuId。",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: "object",
|
|
99
|
+
properties: {
|
|
100
|
+
reportPath: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description:
|
|
103
|
+
"可选。SYS_MENU_INFO*.md 路径;不传则使用 .github/reports 下最新报告。",
|
|
104
|
+
},
|
|
105
|
+
dryRun: {
|
|
106
|
+
type: "boolean",
|
|
107
|
+
description: "可选。true 时只解析和预览,不调用保存接口。",
|
|
80
108
|
},
|
|
81
109
|
},
|
|
82
|
-
required: [
|
|
110
|
+
required: [],
|
|
83
111
|
},
|
|
84
112
|
},
|
|
85
113
|
{
|
|
86
|
-
name:
|
|
114
|
+
name: "wls_dict_query",
|
|
87
115
|
description:
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
"查询当前应用的所有字典模块及字典项。在 wls_dict_upsert 前调用," +
|
|
117
|
+
"用于判断哪些模块/字典项已存在。",
|
|
90
118
|
inputSchema: {
|
|
91
|
-
type:
|
|
119
|
+
type: "object",
|
|
92
120
|
properties: {},
|
|
93
121
|
required: [],
|
|
94
122
|
},
|
|
95
123
|
},
|
|
96
124
|
{
|
|
97
|
-
name:
|
|
125
|
+
name: "wls_dict_upsert",
|
|
98
126
|
description:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
"新增或更新字典模块及其字典项。内部自动处理:" +
|
|
128
|
+
"若模块不存在则创建(data=null 后自动 re-query 获取 id)," +
|
|
129
|
+
"若已存在则直接取 id;字典项自动跳过已存在的 strSn。",
|
|
102
130
|
inputSchema: {
|
|
103
|
-
type:
|
|
131
|
+
type: "object",
|
|
104
132
|
properties: {
|
|
105
133
|
module: {
|
|
106
|
-
type:
|
|
107
|
-
description:
|
|
134
|
+
type: "object",
|
|
135
|
+
description:
|
|
136
|
+
'DictModuleSaveBody: strSn(必填), strName(必填), sortPriority("1"), strLevel(2)',
|
|
108
137
|
properties: {
|
|
109
|
-
strSn: { type:
|
|
110
|
-
strName: { type:
|
|
111
|
-
sortPriority: {
|
|
112
|
-
|
|
138
|
+
strSn: { type: "string", description: '模块标识符,如 "gender"' },
|
|
139
|
+
strName: { type: "string", description: '模块显示名,如 "性别"' },
|
|
140
|
+
sortPriority: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: '排序,字符串类型,如 "1"',
|
|
143
|
+
},
|
|
144
|
+
strLevel: { type: "number", description: "固定传 2" },
|
|
113
145
|
},
|
|
114
|
-
required: [
|
|
146
|
+
required: ["strSn", "strName"],
|
|
115
147
|
},
|
|
116
148
|
items: {
|
|
117
|
-
type:
|
|
149
|
+
type: "array",
|
|
118
150
|
description:
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
"DictItemSaveBody 数组(可选)。每项字段:" +
|
|
152
|
+
"strSn(必填), strName(必填), strLevel(2), " +
|
|
121
153
|
'dtlValue(""), dtlValueRequired(false), dtlValue2Required(false), ' +
|
|
122
|
-
|
|
123
|
-
items: { type:
|
|
154
|
+
"dtlValue3Required(false), dtlValue4Required(false)",
|
|
155
|
+
items: { type: "object" },
|
|
124
156
|
},
|
|
125
157
|
},
|
|
126
|
-
required: [
|
|
158
|
+
required: ["module"],
|
|
127
159
|
},
|
|
128
160
|
},
|
|
129
161
|
{
|
|
130
|
-
name:
|
|
162
|
+
name: "wls_role_query",
|
|
131
163
|
description:
|
|
132
|
-
|
|
164
|
+
"查询角色列表。可选参数 current/size 翻页,默认 size=100。返回精简字段:id, roleName, code, sysAppNo, roleDesc。",
|
|
133
165
|
inputSchema: {
|
|
134
|
-
type:
|
|
166
|
+
type: "object",
|
|
135
167
|
properties: {
|
|
136
|
-
current: { type:
|
|
137
|
-
size: { type:
|
|
168
|
+
current: { type: "number", description: "页码,默认 1" },
|
|
169
|
+
size: { type: "number", description: "每页数量,默认 100" },
|
|
138
170
|
},
|
|
139
171
|
required: [],
|
|
140
172
|
},
|
|
141
173
|
},
|
|
142
174
|
{
|
|
143
|
-
name:
|
|
175
|
+
name: "wls_role_upsert",
|
|
144
176
|
description:
|
|
145
|
-
|
|
146
|
-
|
|
177
|
+
"批量新增角色(按 code 字段自动去重;已存在则跳过)。每项必填 roleName 和 code,可选 configDesc。" +
|
|
178
|
+
"注意:角色仅新增不更新,因角色变更通常需要业务确认。",
|
|
147
179
|
inputSchema: {
|
|
148
|
-
type:
|
|
180
|
+
type: "object",
|
|
149
181
|
properties: {
|
|
150
182
|
items: {
|
|
151
|
-
type:
|
|
183
|
+
type: "array",
|
|
152
184
|
description:
|
|
153
|
-
|
|
154
|
-
items: { type:
|
|
185
|
+
"角色数组。字段:roleName(必填,显示名), code(必填,唯一标识), configDesc(可选,描述)",
|
|
186
|
+
items: { type: "object" },
|
|
155
187
|
},
|
|
156
188
|
},
|
|
157
|
-
required: [
|
|
189
|
+
required: ["items"],
|
|
158
190
|
},
|
|
159
191
|
},
|
|
160
192
|
{
|
|
161
|
-
name:
|
|
193
|
+
name: "wls_assignable_menus_query",
|
|
162
194
|
description:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
inputSchema: { type:
|
|
195
|
+
"查询全量可授权菜单列表(扁平结构,含菜单 id/menuName/permission)。" +
|
|
196
|
+
"在 wls_role_assign_menus 前调用,AI 据此选出要分配给角色的 menuIds。",
|
|
197
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
166
198
|
},
|
|
167
199
|
{
|
|
168
|
-
name:
|
|
200
|
+
name: "wls_role_assign_menus",
|
|
169
201
|
description:
|
|
170
|
-
|
|
171
|
-
|
|
202
|
+
"给指定角色批量分配菜单权限。menuIds 传字符串数组,内部自动拼成逗号分隔字符串提交后端。" +
|
|
203
|
+
"该接口为全量覆盖式,应包含该角色所有菜单(含已有的,否则会被移除)。",
|
|
172
204
|
inputSchema: {
|
|
173
|
-
type:
|
|
205
|
+
type: "object",
|
|
174
206
|
properties: {
|
|
175
|
-
roleId: {
|
|
207
|
+
roleId: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "角色 id(来自 wls_role_query)",
|
|
210
|
+
},
|
|
176
211
|
menuIds: {
|
|
177
|
-
type:
|
|
178
|
-
description:
|
|
179
|
-
items: { type:
|
|
212
|
+
type: "array",
|
|
213
|
+
description: "该角色应拥有的全部菜单 id 数组",
|
|
214
|
+
items: { type: "string" },
|
|
180
215
|
},
|
|
181
216
|
},
|
|
182
|
-
required: [
|
|
217
|
+
required: ["roleId", "menuIds"],
|
|
183
218
|
},
|
|
184
219
|
},
|
|
185
220
|
{
|
|
186
|
-
name:
|
|
221
|
+
name: "wls_action_query",
|
|
187
222
|
description:
|
|
188
|
-
|
|
223
|
+
"查询指定页面菜单(type=C)下的动作按钮列表(type=A)。返回 id/menuName/permission/orderNum/icon。",
|
|
189
224
|
inputSchema: {
|
|
190
|
-
type:
|
|
225
|
+
type: "object",
|
|
191
226
|
properties: {
|
|
192
|
-
menuId: { type:
|
|
227
|
+
menuId: { type: "string", description: "父菜单 id(页面菜单)" },
|
|
193
228
|
},
|
|
194
|
-
required: [
|
|
229
|
+
required: ["menuId"],
|
|
195
230
|
},
|
|
196
231
|
},
|
|
197
232
|
{
|
|
198
|
-
name:
|
|
233
|
+
name: "wls_action_upsert",
|
|
199
234
|
description:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
235
|
+
"在指定页面菜单下批量新增动作按钮(type=A),按 permission 字段自动去重。" +
|
|
236
|
+
"权限码命名规范:{资源camelCase}_{动作} 或 {模块}:{资源}:{动作}(与项目既有约定保持一致)。" +
|
|
237
|
+
"常见动作:add/edit/remove/export/import/approve。",
|
|
203
238
|
inputSchema: {
|
|
204
|
-
type:
|
|
239
|
+
type: "object",
|
|
205
240
|
properties: {
|
|
206
|
-
parentId: {
|
|
241
|
+
parentId: {
|
|
242
|
+
type: "string",
|
|
243
|
+
description: "页面菜单 id(动作挂在它下面)",
|
|
244
|
+
},
|
|
207
245
|
items: {
|
|
208
|
-
type:
|
|
246
|
+
type: "array",
|
|
209
247
|
description:
|
|
210
|
-
|
|
211
|
-
items: { type:
|
|
248
|
+
"动作数组。字段:menuName(必填,显示名), permission(必填,权限码), icon(可选,默认list), orderNum(可选,默认1), useCache(可选,默认1)",
|
|
249
|
+
items: { type: "object" },
|
|
212
250
|
},
|
|
213
251
|
},
|
|
214
|
-
required: [
|
|
252
|
+
required: ["parentId", "items"],
|
|
215
253
|
},
|
|
216
254
|
},
|
|
217
255
|
{
|
|
218
|
-
name:
|
|
256
|
+
name: "wls_code_scan",
|
|
219
257
|
description:
|
|
220
|
-
|
|
221
|
-
|
|
258
|
+
"扫描项目页面目录,返回 index.vue/data.ts/index.scss/api.md 完整性与 API_CONFIG 概览。" +
|
|
259
|
+
"默认扫描 src/views,可传 path 指定目录。适用于 convention-audit / Agent Pipeline 前置感知项目结构。",
|
|
222
260
|
inputSchema: {
|
|
223
|
-
type:
|
|
261
|
+
type: "object",
|
|
224
262
|
properties: {
|
|
225
|
-
path: {
|
|
263
|
+
path: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "相对项目根目录的扫描路径,默认 src/views",
|
|
266
|
+
},
|
|
226
267
|
},
|
|
227
268
|
required: [],
|
|
228
269
|
},
|
|
229
270
|
},
|
|
230
271
|
{
|
|
231
|
-
name:
|
|
272
|
+
name: "wls_route_check",
|
|
232
273
|
description:
|
|
233
|
-
|
|
234
|
-
|
|
274
|
+
"检查 src/views 页面目录是否能在路由文件中被发现。默认查找 vite/plugins/shared/pages.ts 等常见路由文件," +
|
|
275
|
+
"可传 path 和 routeFile 定制。用于 page-codegen/menu-sync 后闭环验证。",
|
|
235
276
|
inputSchema: {
|
|
236
|
-
type:
|
|
277
|
+
type: "object",
|
|
237
278
|
properties: {
|
|
238
|
-
path: { type:
|
|
239
|
-
routeFile: {
|
|
279
|
+
path: { type: "string", description: "页面扫描路径,默认 src/views" },
|
|
280
|
+
routeFile: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "路由文件路径,默认自动探测",
|
|
283
|
+
},
|
|
240
284
|
},
|
|
241
285
|
required: [],
|
|
242
286
|
},
|
|
243
287
|
},
|
|
244
288
|
{
|
|
245
|
-
name:
|
|
289
|
+
name: "wls_validate_page",
|
|
246
290
|
description:
|
|
247
|
-
|
|
291
|
+
"校验页面是否符合 wl-skills-kit 最新页面规范:BaseTable+AGGrid+cid、defineColumns、renderOps、mock-first、api.md 等。",
|
|
248
292
|
inputSchema: {
|
|
249
|
-
type:
|
|
293
|
+
type: "object",
|
|
250
294
|
properties: {
|
|
251
|
-
|
|
295
|
+
path: { type: "string", description: "页面或目录路径,默认 src/views" },
|
|
252
296
|
},
|
|
253
297
|
required: [],
|
|
254
298
|
},
|
|
255
299
|
},
|
|
256
300
|
{
|
|
257
|
-
name:
|
|
301
|
+
name: "wls_doctor_ui",
|
|
302
|
+
description:
|
|
303
|
+
"检查 @agile-team/wk-skills-ui 是否真正接入:依赖、tokens、styles preset、installCommonPreset、defineColumns、renderOps。",
|
|
304
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "wls_git_log_extract",
|
|
258
308
|
description:
|
|
259
|
-
|
|
309
|
+
"提取最近 N 次 git commit 摘要,用于 convention-audit 的 Git 规范检查或 changelog-gen 的数据源。",
|
|
260
310
|
inputSchema: {
|
|
261
|
-
type:
|
|
311
|
+
type: "object",
|
|
262
312
|
properties: {
|
|
263
|
-
|
|
313
|
+
n: { type: "number", description: "提取数量,默认 20,最大 100" },
|
|
264
314
|
},
|
|
265
315
|
required: [],
|
|
266
316
|
},
|
|
267
317
|
},
|
|
268
|
-
|
|
318
|
+
{
|
|
319
|
+
name: "wls_audit_report_push",
|
|
320
|
+
description:
|
|
321
|
+
"将最新审计报告推送到飞书机器人 webhook。未配置 env.local.json 的 feishu_webhook 时静默跳过,不影响其他流程。",
|
|
322
|
+
inputSchema: {
|
|
323
|
+
type: "object",
|
|
324
|
+
properties: {
|
|
325
|
+
reportPath: {
|
|
326
|
+
type: "string",
|
|
327
|
+
description:
|
|
328
|
+
"审计报告路径,不传则自动选择 .github/reports 下最新 AUDIT_*.md 或规范审查报告.md",
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
required: [],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
];
|
|
269
335
|
|
|
270
336
|
// ─── JSON-RPC 协议层 ────────────────────────────────────────────────────
|
|
271
337
|
|
|
272
338
|
function send(obj) {
|
|
273
|
-
process.stdout.write(JSON.stringify(obj) +
|
|
339
|
+
process.stdout.write(JSON.stringify(obj) + "\n");
|
|
274
340
|
}
|
|
275
341
|
|
|
276
342
|
function sendResult(id, result) {
|
|
277
|
-
send({ jsonrpc:
|
|
343
|
+
send({ jsonrpc: "2.0", id, result });
|
|
278
344
|
}
|
|
279
345
|
|
|
280
346
|
function sendError(id, code, message) {
|
|
281
|
-
send({ jsonrpc:
|
|
347
|
+
send({ jsonrpc: "2.0", id, error: { code, message } });
|
|
282
348
|
}
|
|
283
349
|
|
|
284
350
|
// ─── Tool 调度 ───────────────────────────────────────────────────────────
|
|
285
351
|
|
|
286
352
|
async function dispatchTool(id, toolName, toolArgs) {
|
|
287
|
-
let config
|
|
353
|
+
let config;
|
|
288
354
|
const needsBackendConfig = ![
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
355
|
+
"wls_code_scan",
|
|
356
|
+
"wls_route_check",
|
|
357
|
+
"wls_validate_page",
|
|
358
|
+
"wls_doctor_ui",
|
|
359
|
+
"wls_git_log_extract",
|
|
360
|
+
"wls_audit_report_push",
|
|
361
|
+
].includes(toolName);
|
|
294
362
|
if (needsBackendConfig) {
|
|
295
363
|
try {
|
|
296
|
-
config = loadConfig()
|
|
364
|
+
config = loadConfig();
|
|
297
365
|
} catch (e) {
|
|
298
366
|
// 配置加载失败:以文本形式返回给 AI(不是 JSON-RPC error,AI 能读)
|
|
299
|
-
sendResult(id, {
|
|
300
|
-
|
|
367
|
+
sendResult(id, {
|
|
368
|
+
content: [{ type: "text", text: `❌ 配置加载失败: ${e.message}` }],
|
|
369
|
+
isError: true,
|
|
370
|
+
});
|
|
371
|
+
return;
|
|
301
372
|
}
|
|
302
373
|
}
|
|
303
374
|
|
|
304
375
|
try {
|
|
305
|
-
let text
|
|
376
|
+
let text;
|
|
306
377
|
switch (toolName) {
|
|
307
|
-
case
|
|
308
|
-
text = await handleMenuQuery(config)
|
|
309
|
-
break
|
|
310
|
-
case
|
|
311
|
-
text = await handleMenuUpsert(toolArgs, config)
|
|
312
|
-
break
|
|
313
|
-
case
|
|
314
|
-
text = await
|
|
315
|
-
break
|
|
316
|
-
case
|
|
317
|
-
text = await
|
|
318
|
-
break
|
|
319
|
-
case
|
|
320
|
-
text = await
|
|
321
|
-
break
|
|
322
|
-
case
|
|
323
|
-
text = await
|
|
324
|
-
break
|
|
325
|
-
case
|
|
326
|
-
text = await
|
|
327
|
-
break
|
|
328
|
-
case
|
|
329
|
-
text = await
|
|
330
|
-
break
|
|
331
|
-
case
|
|
332
|
-
text = await
|
|
333
|
-
break
|
|
334
|
-
case
|
|
335
|
-
text = await
|
|
336
|
-
break
|
|
337
|
-
case
|
|
338
|
-
text = await
|
|
339
|
-
break
|
|
340
|
-
case
|
|
341
|
-
text = await
|
|
342
|
-
break
|
|
343
|
-
case
|
|
344
|
-
text = await
|
|
345
|
-
break
|
|
346
|
-
case
|
|
347
|
-
text = await
|
|
348
|
-
break
|
|
378
|
+
case "wls_menu_query":
|
|
379
|
+
text = await handleMenuQuery(config);
|
|
380
|
+
break;
|
|
381
|
+
case "wls_menu_upsert":
|
|
382
|
+
text = await handleMenuUpsert(toolArgs, config);
|
|
383
|
+
break;
|
|
384
|
+
case "wls_menu_sync_from_report":
|
|
385
|
+
text = await handleMenuSyncFromReport(toolArgs, config);
|
|
386
|
+
break;
|
|
387
|
+
case "wls_dict_query":
|
|
388
|
+
text = await handleDictQuery(config);
|
|
389
|
+
break;
|
|
390
|
+
case "wls_dict_upsert":
|
|
391
|
+
text = await handleDictUpsert(toolArgs, config);
|
|
392
|
+
break;
|
|
393
|
+
case "wls_role_query":
|
|
394
|
+
text = await handleRoleQuery(toolArgs, config);
|
|
395
|
+
break;
|
|
396
|
+
case "wls_role_upsert":
|
|
397
|
+
text = await handleRoleUpsert(toolArgs, config);
|
|
398
|
+
break;
|
|
399
|
+
case "wls_assignable_menus_query":
|
|
400
|
+
text = await handleAssignableMenusQuery(toolArgs, config);
|
|
401
|
+
break;
|
|
402
|
+
case "wls_role_assign_menus":
|
|
403
|
+
text = await handleRoleAssignMenus(toolArgs, config);
|
|
404
|
+
break;
|
|
405
|
+
case "wls_action_query":
|
|
406
|
+
text = await handleActionQuery(toolArgs, config);
|
|
407
|
+
break;
|
|
408
|
+
case "wls_action_upsert":
|
|
409
|
+
text = await handleActionUpsert(toolArgs, config);
|
|
410
|
+
break;
|
|
411
|
+
case "wls_code_scan":
|
|
412
|
+
text = await handleCodeScan(toolArgs);
|
|
413
|
+
break;
|
|
414
|
+
case "wls_route_check":
|
|
415
|
+
text = await handleRouteCheck(toolArgs);
|
|
416
|
+
break;
|
|
417
|
+
case "wls_validate_page":
|
|
418
|
+
text = await handleValidatePage(toolArgs);
|
|
419
|
+
break;
|
|
420
|
+
case "wls_doctor_ui":
|
|
421
|
+
text = await handleDoctorUi(toolArgs);
|
|
422
|
+
break;
|
|
423
|
+
case "wls_git_log_extract":
|
|
424
|
+
text = await handleGitLogExtract(toolArgs);
|
|
425
|
+
break;
|
|
426
|
+
case "wls_audit_report_push":
|
|
427
|
+
text = await handleAuditReportPush(toolArgs);
|
|
428
|
+
break;
|
|
349
429
|
default:
|
|
350
|
-
sendError(id, -32601, `未知工具: ${toolName}`)
|
|
351
|
-
return
|
|
430
|
+
sendError(id, -32601, `未知工具: ${toolName}`);
|
|
431
|
+
return;
|
|
352
432
|
}
|
|
353
|
-
sendResult(id, { content: [{ type:
|
|
433
|
+
sendResult(id, { content: [{ type: "text", text }] });
|
|
354
434
|
} catch (e) {
|
|
355
435
|
sendResult(id, {
|
|
356
|
-
content: [{ type:
|
|
436
|
+
content: [{ type: "text", text: `❌ 工具执行异常: ${e.message}` }],
|
|
357
437
|
isError: true,
|
|
358
|
-
})
|
|
438
|
+
});
|
|
359
439
|
}
|
|
360
440
|
}
|
|
361
441
|
|
|
362
442
|
// ─── 消息循环 ────────────────────────────────────────────────────────────
|
|
363
443
|
|
|
364
|
-
const rl = readline.createInterface({ input: process.stdin, terminal: false })
|
|
444
|
+
const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
365
445
|
|
|
366
|
-
rl.on(
|
|
367
|
-
const raw = line.trim()
|
|
368
|
-
if (!raw) return
|
|
446
|
+
rl.on("line", async (line) => {
|
|
447
|
+
const raw = line.trim();
|
|
448
|
+
if (!raw) return;
|
|
369
449
|
|
|
370
|
-
let msg
|
|
450
|
+
let msg;
|
|
371
451
|
try {
|
|
372
|
-
msg = JSON.parse(raw)
|
|
452
|
+
msg = JSON.parse(raw);
|
|
373
453
|
} catch (e) {
|
|
374
|
-
send({
|
|
375
|
-
|
|
454
|
+
send({
|
|
455
|
+
jsonrpc: "2.0",
|
|
456
|
+
id: null,
|
|
457
|
+
error: { code: -32700, message: "Parse error" },
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
376
460
|
}
|
|
377
461
|
|
|
378
|
-
const { id, method, params = {} } = msg
|
|
462
|
+
const { id, method, params = {} } = msg;
|
|
379
463
|
|
|
380
464
|
// Notifications(无 id)不需要响应
|
|
381
|
-
if (id === undefined || id === null) return
|
|
465
|
+
if (id === undefined || id === null) return;
|
|
382
466
|
|
|
383
467
|
switch (method) {
|
|
384
|
-
case
|
|
468
|
+
case "initialize":
|
|
385
469
|
sendResult(id, {
|
|
386
|
-
protocolVersion:
|
|
470
|
+
protocolVersion: "2024-11-05",
|
|
387
471
|
capabilities: { tools: {} },
|
|
388
|
-
serverInfo: { name:
|
|
389
|
-
})
|
|
390
|
-
break
|
|
472
|
+
serverInfo: { name: "wl-skills", version: PKG.version },
|
|
473
|
+
});
|
|
474
|
+
break;
|
|
391
475
|
|
|
392
|
-
case
|
|
393
|
-
sendResult(id, { tools: TOOLS })
|
|
394
|
-
break
|
|
476
|
+
case "tools/list":
|
|
477
|
+
sendResult(id, { tools: TOOLS });
|
|
478
|
+
break;
|
|
395
479
|
|
|
396
|
-
case
|
|
397
|
-
await dispatchTool(id, params.name, params.arguments || {})
|
|
398
|
-
break
|
|
480
|
+
case "tools/call":
|
|
481
|
+
await dispatchTool(id, params.name, params.arguments || {});
|
|
482
|
+
break;
|
|
399
483
|
|
|
400
|
-
case
|
|
401
|
-
sendResult(id, {})
|
|
402
|
-
break
|
|
484
|
+
case "ping":
|
|
485
|
+
sendResult(id, {});
|
|
486
|
+
break;
|
|
403
487
|
|
|
404
488
|
default:
|
|
405
|
-
sendError(id, -32601, `Method not found: ${method}`)
|
|
489
|
+
sendError(id, -32601, `Method not found: ${method}`);
|
|
406
490
|
}
|
|
407
|
-
})
|
|
491
|
+
});
|
|
408
492
|
|
|
409
|
-
rl.on(
|
|
410
|
-
process.exit(0)
|
|
411
|
-
})
|
|
493
|
+
rl.on("close", () => {
|
|
494
|
+
process.exit(0);
|
|
495
|
+
});
|