@agile-team/wl-skills-kit 2.7.1 → 2.7.3

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/mcp/api/client.js CHANGED
@@ -1,76 +1,83 @@
1
- 'use strict'
2
-
3
- const https = require('https')
4
- const http = require('http')
5
-
6
- /**
7
- * 带鉴权的 HTTP 客户端(兼容 Node 16+,无额外依赖)
8
- *
9
- * @param {string} urlPath - 接口路径(以 / 开头),拼接到 config.gatewayPath 后
10
- * @param {{ method?: string, body?: unknown }} options
11
- * @param {{ gatewayPath: string, token: string }} config
12
- * @returns {Promise<{ ok: boolean, data: any, error?: string, code?: number }>}
13
- */
14
- function wlsFetch(urlPath, options, config) {
15
- const fullUrl = config.gatewayPath + urlPath
16
- const isHttps = fullUrl.startsWith('https')
17
- const lib = isHttps ? https : http
18
- const bodyStr = options.body ? JSON.stringify(options.body) : null
19
-
20
- let urlObj
21
- try {
22
- urlObj = new URL(fullUrl)
23
- } catch (e) {
24
- return Promise.reject(new Error(`无效的 URL: ${fullUrl}`))
25
- }
26
-
27
- const reqOptions = {
28
- hostname: urlObj.hostname,
29
- port: urlObj.port || (isHttps ? 443 : 80),
30
- path: urlObj.pathname + urlObj.search,
31
- method: options.method || 'GET',
32
- headers: {
33
- 'Content-Type': 'application/json',
34
- 'Authorization': `Bearer ${config.token}`,
35
- },
36
- }
37
-
38
- if (bodyStr) {
39
- reqOptions.headers['Content-Length'] = Buffer.byteLength(bodyStr)
40
- }
41
-
42
- return new Promise((resolve, reject) => {
43
- const req = lib.request(reqOptions, (res) => {
44
- let data = ''
45
- res.on('data', (chunk) => { data += chunk })
46
- res.on('end', () => {
47
- try {
48
- const json = JSON.parse(data)
49
- if (json.code === 2000) {
50
- resolve({ ok: true, data: json.data, code: json.code })
51
- } else {
52
- resolve({
53
- ok: false,
54
- data: null,
55
- error: json.message || `服务端返回 code=${json.code}`,
56
- code: json.code,
57
- })
58
- }
59
- } catch (e) {
60
- reject(new Error(`响应解析失败: ${e.message},原始内容: ${data.slice(0, 200)}`))
61
- }
62
- })
63
- })
64
-
65
- req.on('error', (e) => reject(new Error(`请求失败: ${e.message}`)))
66
- req.setTimeout(15000, () => {
67
- req.destroy()
68
- reject(new Error('请求超时(15s)'))
69
- })
70
-
71
- if (bodyStr) req.write(bodyStr)
72
- req.end()
73
- })
74
- }
75
-
76
- module.exports = { wlsFetch }
1
+ 'use strict'
2
+
3
+ const https = require('https')
4
+ const http = require('http')
5
+
6
+ /**
7
+ * 带鉴权的 HTTP 客户端(兼容 Node 16+,无额外依赖)
8
+ *
9
+ * @param {string} urlPath - 接口路径(以 / 开头),拼接到 config.gatewayPath 后
10
+ * @param {{ method?: string, body?: unknown }} options
11
+ * @param {{ gatewayPath: string, token: string }} config
12
+ * @returns {Promise<{ ok: boolean, data: any, error?: string, code?: number }>}
13
+ */
14
+ function wlsFetch(urlPath, options, config) {
15
+ const fullUrl = config.gatewayPath + urlPath
16
+ const isHttps = fullUrl.startsWith('https')
17
+ const lib = isHttps ? https : http
18
+ const bodyStr = options.body ? JSON.stringify(options.body) : null
19
+
20
+ let urlObj
21
+ try {
22
+ urlObj = new URL(fullUrl)
23
+ } catch (e) {
24
+ return Promise.reject(new Error(`无效的 URL: ${fullUrl}`))
25
+ }
26
+
27
+ const reqOptions = {
28
+ hostname: urlObj.hostname,
29
+ port: urlObj.port || (isHttps ? 443 : 80),
30
+ path: urlObj.pathname + urlObj.search,
31
+ method: options.method || 'GET',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ 'Authorization': `Bearer ${config.token}`,
35
+ },
36
+ }
37
+
38
+ if (bodyStr) {
39
+ reqOptions.headers['Content-Length'] = Buffer.byteLength(bodyStr)
40
+ }
41
+
42
+ return new Promise((resolve, reject) => {
43
+ const req = lib.request(reqOptions, (res) => {
44
+ let data = ''
45
+ res.on('data', (chunk) => { data += chunk })
46
+ res.on('end', () => {
47
+ try {
48
+ const json = JSON.parse(data)
49
+ if (json.code === 2000) {
50
+ resolve({ ok: true, data: json.data, code: json.code })
51
+ } else {
52
+ // 友好提示:401/4004 等典型错误附带排查指引
53
+ let hint = ''
54
+ if (json.code === 401 || /token|未登录|鉴权/i.test(json.message || '')) {
55
+ hint = '(Token 可能已过期,请重新登录后更新 env.local.json token 字段,仅填纯 JWT 不含 bearer 前缀)'
56
+ } else if (json.code === 4004 || /url:|not\s*found/i.test(json.message || '')) {
57
+ hint = '(接口未找到。可能是 gatewayPath 配置缺失前缀或后端环境差异;请检查 env.local.json 的 gatewayPath。不要让 AI 绕开 MCP 自己拼 HTTP)'
58
+ }
59
+ resolve({
60
+ ok: false,
61
+ data: null,
62
+ error: (json.message || `服务端返回 code=${json.code}`) + hint,
63
+ code: json.code,
64
+ })
65
+ }
66
+ } catch (e) {
67
+ reject(new Error(`响应解析失败: ${e.message},原始内容: ${data.slice(0, 200)}`))
68
+ }
69
+ })
70
+ })
71
+
72
+ req.on('error', (e) => reject(new Error(`请求失败: ${e.message}`)))
73
+ req.setTimeout(15000, () => {
74
+ req.destroy()
75
+ reject(new Error('请求超时(15s)'))
76
+ })
77
+
78
+ if (bodyStr) req.write(bodyStr)
79
+ req.end()
80
+ })
81
+ }
82
+
83
+ module.exports = { wlsFetch }
package/mcp/server.js CHANGED
@@ -122,7 +122,28 @@ function startServer() {
122
122
 
123
123
  // 仅在直接执行时启动监听;被 require(如测试)时不启动
124
124
  if (require.main === module) {
125
+ printBanner();
125
126
  startServer();
126
127
  }
127
128
 
129
+ /**
130
+ * 启动 banner — 写入 stderr,不污染 stdout 的 JSON-RPC 通道
131
+ * 让用户在编辑器 MCP 输出面板能直观看到:版本、工具数量、项目根
132
+ */
133
+ function printBanner() {
134
+ const projectRoot = process.env.WL_PROJECT_ROOT || process.cwd();
135
+ const lines = [
136
+ "",
137
+ "═══════════════════════════════════════════════════",
138
+ ` wl-skills MCP Server v${PKG.version}`,
139
+ "═══════════════════════════════════════════════════",
140
+ ` 项目根 (WL_PROJECT_ROOT): ${projectRoot}`,
141
+ ` 已注册工具 (${TOOLS.length}):`,
142
+ ...TOOLS.map((t) => ` • ${t.name}`),
143
+ "═══════════════════════════════════════════════════",
144
+ "",
145
+ ];
146
+ process.stderr.write(lines.join("\n") + "\n");
147
+ }
148
+
128
149
  module.exports = { TOOLS, HANDLERS, dispatchTool, startServer };
@@ -170,4 +170,9 @@ async function handleDictUpsert(args, config) {
170
170
  return output
171
171
  }
172
172
 
173
- module.exports = { handleDictQuery, handleDictUpsert }
173
+ module.exports = {
174
+ handleDictQuery,
175
+ handleDictUpsert,
176
+ // 导出纯工具函数供单测覆盖
177
+ _internal: { extractModules },
178
+ }
@@ -413,4 +413,15 @@ module.exports = {
413
413
  handleMenuQuery,
414
414
  handleMenuUpsert,
415
415
  handleMenuSyncFromReport,
416
+ // 导出纯工具函数供单测覆盖
417
+ _internal: {
418
+ cleanCell,
419
+ splitMarkdownRow,
420
+ isDividerRow,
421
+ parseBoolean,
422
+ normalizeTree,
423
+ flattenMenus,
424
+ findExisting,
425
+ parseReport,
426
+ },
416
427
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-kit",
3
- "version": "2.7.1",
4
- "description": "AI Skill 模板包 v2.7.1 — 13 条编码规范 + 10 个 AI Skill + 17 个 MCP Tool,一条命令导入 Vue 3 项目",
3
+ "version": "2.7.3",
4
+ "description": "AI Skill 模板包 v2.7.3 — 13 条编码规范 + 10 个 AI Skill + 17 个 MCP Tool,一条命令导入 Vue 3 项目",
5
5
  "main": "./bin/wl-skills.js",
6
6
  "bin": {
7
7
  "wl-skills": "bin/wl-skills.js"
@@ -43,9 +43,10 @@
43
43
  "prepare": "husky",
44
44
  "cz": "git-cz",
45
45
  "lint": "eslint .",
46
+ "lint:skills": "node scripts/lint-skills.js",
46
47
  "test": "vitest run",
47
48
  "version:verify": "node scripts/verify-version.js",
48
- "prepublishOnly": "node scripts/verify-version.js && vitest run"
49
+ "prepublishOnly": "node scripts/verify-version.js && node scripts/lint-skills.js && vitest run"
49
50
  },
50
51
  "dependencies": {
51
52
  "xlsx": "^0.18.5"
@@ -74,4 +75,4 @@
74
75
  "eslint --fix --no-cache"
75
76
  ]
76
77
  }
77
- }
78
+ }