@42ailab/42plugin 0.1.26 → 0.1.27
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/package.json +1 -1
- package/src/api.ts +1 -1
- package/src/auth-middleware.ts +0 -1
- package/src/commands/list.ts +1 -1
- package/src/commands/publish.ts +2 -9
- package/src/commands/search.ts +1 -1
- package/src/commands/setup.ts +211 -5
- package/src/services/manifest.ts +4 -6
- package/src/services/packager.ts +0 -1
- package/src/services/publisher.ts +0 -1
- package/src/types.ts +1 -16
- package/src/utils.ts +0 -4
- package/src/validators/plugin-validator.ts +1 -76
package/package.json
CHANGED
package/src/api.ts
CHANGED
package/src/auth-middleware.ts
CHANGED
package/src/commands/list.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const listCommand = new Command('list')
|
|
|
14
14
|
.description('查看已安装的插件')
|
|
15
15
|
.option('-g, --global', '显示全局安装的插件 (~/.claude/)')
|
|
16
16
|
.option('-a, --all', '显示所有项目的插件(包括全局)')
|
|
17
|
-
.option('-t, --type <type>', '筛选类型 (skill, agent, command, hook
|
|
17
|
+
.option('-t, --type <type>', '筛选类型 (skill, agent, command, hook)')
|
|
18
18
|
.option('--json', '输出 JSON 格式')
|
|
19
19
|
.action(async (options) => {
|
|
20
20
|
try {
|
package/src/commands/publish.ts
CHANGED
|
@@ -13,14 +13,13 @@ import { ValidationError, UploadError, AuthRequiredError } from '../errors';
|
|
|
13
13
|
import { getTypeIcon } from '../utils';
|
|
14
14
|
import type { PluginType } from '../types';
|
|
15
15
|
|
|
16
|
-
const VALID_TYPES: PluginType[] = ['skill', 'agent', 'command', 'hook'
|
|
16
|
+
const VALID_TYPES: PluginType[] = ['skill', 'agent', 'command', 'hook'];
|
|
17
17
|
|
|
18
18
|
const TYPE_DESCRIPTIONS: Record<PluginType, string> = {
|
|
19
19
|
skill: 'skill(技能) - 包含 SKILL.md 的目录,为 Claude 提供特定能力',
|
|
20
20
|
agent: 'agent(代理) - 单个 .md 文件,定义 AI 角色和行为',
|
|
21
21
|
command: 'command(命令) - 单个 .md 文件,定义可执行的斜杠命令',
|
|
22
22
|
hook: 'hook(钩子) - 包含 hooks.json 的目录,响应事件触发',
|
|
23
|
-
mcp: 'mcp(MCP 服务) - 包含 mcp.json 的目录,Model Context Protocol 服务',
|
|
24
23
|
};
|
|
25
24
|
|
|
26
25
|
/**
|
|
@@ -43,9 +42,6 @@ async function suggestType(pluginPath: string): Promise<PluginType> {
|
|
|
43
42
|
if (normalizedPath.includes('/.claude/hooks/') || normalizedPath.includes('.claude/hooks/')) {
|
|
44
43
|
return 'hook';
|
|
45
44
|
}
|
|
46
|
-
if (normalizedPath.includes('/.claude/mcp/') || normalizedPath.includes('.claude/mcp/')) {
|
|
47
|
-
return 'mcp';
|
|
48
|
-
}
|
|
49
45
|
|
|
50
46
|
// 2. 检查是否是目录,如果是则检查特征文件
|
|
51
47
|
try {
|
|
@@ -54,9 +50,6 @@ async function suggestType(pluginPath: string): Promise<PluginType> {
|
|
|
54
50
|
const files = await fs.readdir(absPath);
|
|
55
51
|
const lowerFiles = files.map((f) => f.toLowerCase());
|
|
56
52
|
|
|
57
|
-
if (lowerFiles.includes('mcp.json') || lowerFiles.includes('.mcp.json')) {
|
|
58
|
-
return 'mcp';
|
|
59
|
-
}
|
|
60
53
|
if (lowerFiles.includes('hooks.json') || lowerFiles.includes('hook.json')) {
|
|
61
54
|
return 'hook';
|
|
62
55
|
}
|
|
@@ -80,7 +73,7 @@ export const publishCommand = new Command('publish')
|
|
|
80
73
|
.option('-f, --force', '强制发布(即使内容未变化)')
|
|
81
74
|
.option('--public', '公开发布(默认仅自己可见)')
|
|
82
75
|
.option('-n, --name <name>', '覆盖插件名称')
|
|
83
|
-
.option('-t, --type <type>', '指定插件类型 (skill|agent|command|hook
|
|
76
|
+
.option('-t, --type <type>', '指定插件类型 (skill|agent|command|hook)')
|
|
84
77
|
.action(async (pluginPath, options) => {
|
|
85
78
|
try {
|
|
86
79
|
// 验证 --type 参数
|
package/src/commands/search.ts
CHANGED
|
@@ -54,7 +54,7 @@ function displayKitResults(items: KitSearchResult[]): void {
|
|
|
54
54
|
export const searchCommand = new Command('search')
|
|
55
55
|
.description('搜索插件和套包')
|
|
56
56
|
.argument('<keyword>', '搜索关键词')
|
|
57
|
-
.option('-t, --type <type>', '筛选插件类型 (skill, agent, command, hook
|
|
57
|
+
.option('-t, --type <type>', '筛选插件类型 (skill, agent, command, hook)')
|
|
58
58
|
.option('-k, --kit', '只搜索套包')
|
|
59
59
|
.option('-p, --plugin', '只搜索插件')
|
|
60
60
|
.option('-l, --limit <n>', '每页数量', String(DEFAULT_PAGE_SIZE))
|
package/src/commands/setup.ts
CHANGED
|
@@ -39,7 +39,8 @@ async function setupCompletion(): Promise<void> {
|
|
|
39
39
|
|
|
40
40
|
if (!shell) {
|
|
41
41
|
console.log(chalk.yellow('无法检测当前 Shell 类型'));
|
|
42
|
-
console.log(chalk.gray('
|
|
42
|
+
console.log(chalk.gray('支持的 Shell: bash, zsh, fish'));
|
|
43
|
+
console.log(chalk.gray('请确保 $SHELL 环境变量正确设置'));
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -110,7 +111,7 @@ function getShellConfigFile(shell: string): string | null {
|
|
|
110
111
|
async function checkAlreadyConfigured(configFile: string): Promise<boolean> {
|
|
111
112
|
try {
|
|
112
113
|
const content = await fs.readFile(configFile, 'utf-8');
|
|
113
|
-
return content.includes('42plugin
|
|
114
|
+
return content.includes('# 42plugin CLI 补全');
|
|
114
115
|
} catch {
|
|
115
116
|
return false;
|
|
116
117
|
}
|
|
@@ -136,22 +137,227 @@ async function installCompletion(shell: string, configFile: string): Promise<voi
|
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
function getCompletionInitScript(shell: string): string {
|
|
140
|
+
// 42plugin 支持的命令和选项
|
|
141
|
+
const commands = ['auth', 'search', 'install', 'list', 'uninstall', 'publish', 'check', 'setup', 'version'];
|
|
142
|
+
const globalOptions = ['--help', '--version', '--verbose', '--no-color'];
|
|
143
|
+
|
|
139
144
|
switch (shell) {
|
|
140
145
|
case 'bash':
|
|
141
146
|
return `
|
|
142
147
|
# 42plugin CLI 补全
|
|
143
|
-
|
|
148
|
+
_42plugin_installed_plugins() {
|
|
149
|
+
42plugin list --all --json 2>/dev/null | grep -o '"fullName": "[^"]*"' | cut -d'"' -f4
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
_42plugin_completions() {
|
|
153
|
+
local cur prev pprev commands
|
|
154
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
155
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
156
|
+
pprev="\${COMP_WORDS[COMP_CWORD-2]:-}"
|
|
157
|
+
commands="${commands.join(' ')}"
|
|
158
|
+
|
|
159
|
+
# 处理 --type 后的类型补全
|
|
160
|
+
if [[ "\${prev}" == "--type" ]] || [[ "\${prev}" == "-t" ]]; then
|
|
161
|
+
COMPREPLY=( $(compgen -W "skill agent command hook" -- "\${cur}") )
|
|
162
|
+
return 0
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
case "\${prev}" in
|
|
166
|
+
42plugin)
|
|
167
|
+
COMPREPLY=( $(compgen -W "\${commands} ${globalOptions.join(' ')}" -- "\${cur}") )
|
|
168
|
+
return 0
|
|
169
|
+
;;
|
|
170
|
+
uninstall|rm)
|
|
171
|
+
# 从本地数据库获取已安装插件
|
|
172
|
+
local plugins=$(_42plugin_installed_plugins)
|
|
173
|
+
COMPREPLY=( $(compgen -W "\${plugins}" -- "\${cur}") )
|
|
174
|
+
return 0
|
|
175
|
+
;;
|
|
176
|
+
install)
|
|
177
|
+
return 0
|
|
178
|
+
;;
|
|
179
|
+
search)
|
|
180
|
+
return 0
|
|
181
|
+
;;
|
|
182
|
+
publish|pub)
|
|
183
|
+
# 路径补全 + 选项补全
|
|
184
|
+
if [[ "\${cur}" == -* ]]; then
|
|
185
|
+
COMPREPLY=( $(compgen -W "--dry-run --force --public --name --type -f -n -t" -- "\${cur}") )
|
|
186
|
+
else
|
|
187
|
+
COMPREPLY=( $(compgen -f -d -- "\${cur}") )
|
|
188
|
+
fi
|
|
189
|
+
return 0
|
|
190
|
+
;;
|
|
191
|
+
check)
|
|
192
|
+
# 路径补全
|
|
193
|
+
COMPREPLY=( $(compgen -f -d -- "\${cur}") )
|
|
194
|
+
return 0
|
|
195
|
+
;;
|
|
196
|
+
*)
|
|
197
|
+
# 检查是否在 publish/check 子命令中
|
|
198
|
+
for word in "\${COMP_WORDS[@]}"; do
|
|
199
|
+
if [[ "\$word" == "publish" ]] || [[ "\$word" == "pub" ]] || [[ "\$word" == "check" ]]; then
|
|
200
|
+
if [[ "\${cur}" == -* ]]; then
|
|
201
|
+
COMPREPLY=( $(compgen -W "--dry-run --force --public --name --type -f -n -t --help" -- "\${cur}") )
|
|
202
|
+
else
|
|
203
|
+
COMPREPLY=( $(compgen -f -d -- "\${cur}") )
|
|
204
|
+
fi
|
|
205
|
+
return 0
|
|
206
|
+
fi
|
|
207
|
+
if [[ "\$word" == "uninstall" ]] || [[ "\$word" == "rm" ]]; then
|
|
208
|
+
local plugins=$(_42plugin_installed_plugins)
|
|
209
|
+
COMPREPLY=( $(compgen -W "\${plugins}" -- "\${cur}") )
|
|
210
|
+
return 0
|
|
211
|
+
fi
|
|
212
|
+
done
|
|
213
|
+
COMPREPLY=( $(compgen -W "--help" -- "\${cur}") )
|
|
214
|
+
return 0
|
|
215
|
+
;;
|
|
216
|
+
esac
|
|
217
|
+
}
|
|
218
|
+
complete -F _42plugin_completions 42plugin
|
|
144
219
|
`;
|
|
220
|
+
|
|
145
221
|
case 'zsh':
|
|
146
222
|
return `
|
|
147
223
|
# 42plugin CLI 补全
|
|
148
|
-
|
|
224
|
+
_42plugin_installed_plugins() {
|
|
225
|
+
local -a plugins
|
|
226
|
+
plugins=(\${(f)"$(42plugin list --all --json 2>/dev/null | grep -o '"fullName": "[^"]*"' | cut -d'"' -f4)"})
|
|
227
|
+
_describe 'installed plugin' plugins
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_42plugin() {
|
|
231
|
+
local -a commands
|
|
232
|
+
commands=(
|
|
233
|
+
'auth:登录或查看认证状态'
|
|
234
|
+
'search:搜索插件'
|
|
235
|
+
'install:安装插件'
|
|
236
|
+
'list:列出已安装插件'
|
|
237
|
+
'uninstall:卸载插件'
|
|
238
|
+
'publish:发布插件'
|
|
239
|
+
'check:检查插件更新'
|
|
240
|
+
'setup:配置 CLI 环境'
|
|
241
|
+
'version:显示版本信息'
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
_arguments -C \\
|
|
245
|
+
"1: :{_describe 'command' commands}" \\
|
|
246
|
+
"*::arg:->args"
|
|
247
|
+
|
|
248
|
+
case "\$state" in
|
|
249
|
+
args)
|
|
250
|
+
case \$words[1] in
|
|
251
|
+
publish|pub)
|
|
252
|
+
_arguments \\
|
|
253
|
+
'--dry-run[仅验证不发布]' \\
|
|
254
|
+
'-f[强制发布]' '--force[强制发布]' \\
|
|
255
|
+
'--public[公开发布]' \\
|
|
256
|
+
'-n[指定名称]:name:' '--name[指定名称]:name:' \\
|
|
257
|
+
'-t[指定类型]:type:(skill agent command hook)' '--type[指定类型]:type:(skill agent command hook)' \\
|
|
258
|
+
'*:path:_files -/'
|
|
259
|
+
;;
|
|
260
|
+
check)
|
|
261
|
+
_arguments \\
|
|
262
|
+
'--fix[自动修复问题]' \\
|
|
263
|
+
'*:path:_files -/'
|
|
264
|
+
;;
|
|
265
|
+
install)
|
|
266
|
+
_arguments \\
|
|
267
|
+
'-g[全局安装]' '--global[全局安装]' \\
|
|
268
|
+
'*:plugin:'
|
|
269
|
+
;;
|
|
270
|
+
uninstall|rm)
|
|
271
|
+
_arguments \\
|
|
272
|
+
'-g[卸载全局插件]' '--global[卸载全局插件]' \\
|
|
273
|
+
'*:plugin:_42plugin_installed_plugins'
|
|
274
|
+
;;
|
|
275
|
+
list|ls)
|
|
276
|
+
_arguments \\
|
|
277
|
+
'-g[显示全局插件]' '--global[显示全局插件]' \\
|
|
278
|
+
'-a[显示所有项目]' '--all[显示所有项目]' \\
|
|
279
|
+
'-t[筛选类型]:type:(skill agent command hook)' '--type[筛选类型]:type:(skill agent command hook)' \\
|
|
280
|
+
'--json[输出 JSON]'
|
|
281
|
+
;;
|
|
282
|
+
search)
|
|
283
|
+
_arguments \\
|
|
284
|
+
'-t[筛选类型]:type:(skill agent command hook kit)' '--type[筛选类型]:type:(skill agent command hook kit)' \\
|
|
285
|
+
'*:query:'
|
|
286
|
+
;;
|
|
287
|
+
auth)
|
|
288
|
+
_arguments \\
|
|
289
|
+
'--logout[登出]' \\
|
|
290
|
+
'--status[查看状态]'
|
|
291
|
+
;;
|
|
292
|
+
*)
|
|
293
|
+
_arguments '*: :_files'
|
|
294
|
+
;;
|
|
295
|
+
esac
|
|
296
|
+
;;
|
|
297
|
+
esac
|
|
298
|
+
}
|
|
299
|
+
compdef _42plugin 42plugin
|
|
149
300
|
`;
|
|
301
|
+
|
|
150
302
|
case 'fish':
|
|
151
303
|
return `
|
|
152
304
|
# 42plugin CLI 补全
|
|
153
|
-
|
|
305
|
+
|
|
306
|
+
# 获取已安装插件列表
|
|
307
|
+
function __42plugin_installed_plugins
|
|
308
|
+
42plugin list --all --json 2>/dev/null | string match -r '"fullName": "[^"]*"' | string replace -r '"fullName": "([^"]*)"' '$1'
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# 主命令
|
|
312
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "auth" -d "登录或查看认证状态"
|
|
313
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "search" -d "搜索插件"
|
|
314
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "install" -d "安装插件"
|
|
315
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "list" -d "列出已安装插件"
|
|
316
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "uninstall" -d "卸载插件"
|
|
317
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "publish" -d "发布插件"
|
|
318
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "check" -d "检查插件更新"
|
|
319
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "setup" -d "配置 CLI 环境"
|
|
320
|
+
complete -c 42plugin -n "__fish_use_subcommand" -a "version" -d "显示版本信息"
|
|
321
|
+
|
|
322
|
+
# auth 选项
|
|
323
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from auth" -l logout -d "登出"
|
|
324
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from auth" -l status -d "查看状态"
|
|
325
|
+
|
|
326
|
+
# publish 选项 + 路径补全
|
|
327
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from publish pub" -l dry-run -d "仅验证不发布"
|
|
328
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from publish pub" -s f -l force -d "强制发布"
|
|
329
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from publish pub" -l public -d "公开发布"
|
|
330
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from publish pub" -s n -l name -d "指定名称" -r
|
|
331
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from publish pub" -s t -l type -d "指定类型" -ra "skill agent command hook"
|
|
332
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from publish pub" -a "(__fish_complete_path)" -d "路径"
|
|
333
|
+
|
|
334
|
+
# check 选项 + 路径补全
|
|
335
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from check" -l fix -d "自动修复"
|
|
336
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from check" -a "(__fish_complete_path)" -d "路径"
|
|
337
|
+
|
|
338
|
+
# install 选项
|
|
339
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from install" -s g -l global -d "全局安装"
|
|
340
|
+
|
|
341
|
+
# uninstall 选项 + 已安装插件补全
|
|
342
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from uninstall rm" -s g -l global -d "卸载全局插件"
|
|
343
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from uninstall rm" -a "(__42plugin_installed_plugins)" -d "已安装插件"
|
|
344
|
+
|
|
345
|
+
# list 选项
|
|
346
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from list ls" -s g -l global -d "显示全局插件"
|
|
347
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from list ls" -s a -l all -d "显示所有项目"
|
|
348
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from list ls" -s t -l type -d "筛选类型" -ra "skill agent command hook"
|
|
349
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from list ls" -l json -d "输出 JSON"
|
|
350
|
+
|
|
351
|
+
# search 选项
|
|
352
|
+
complete -c 42plugin -n "__fish_seen_subcommand_from search" -s t -l type -d "筛选类型" -ra "skill agent command hook kit"
|
|
353
|
+
|
|
354
|
+
# 全局选项
|
|
355
|
+
complete -c 42plugin -l help -d "显示帮助"
|
|
356
|
+
complete -c 42plugin -s v -l version -d "显示版本"
|
|
357
|
+
complete -c 42plugin -l verbose -d "详细输出"
|
|
358
|
+
complete -c 42plugin -l no-color -d "禁用颜色"
|
|
154
359
|
`;
|
|
360
|
+
|
|
155
361
|
default:
|
|
156
362
|
return '';
|
|
157
363
|
}
|
package/src/services/manifest.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 插件清单解析服务
|
|
3
3
|
*
|
|
4
|
-
* 从插件目录读取 SKILL.md / AGENT.md / COMMAND.md / HOOK.md
|
|
4
|
+
* 从插件目录读取 SKILL.md / AGENT.md / COMMAND.md / HOOK.md
|
|
5
5
|
* 解析 frontmatter 获取插件元数据
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'fs/promises';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import matter from 'gray-matter';
|
|
10
10
|
|
|
11
|
-
export type CapabilityType = 'skill' | 'agent' | 'command' | 'hook'
|
|
11
|
+
export type CapabilityType = 'skill' | 'agent' | 'command' | 'hook';
|
|
12
12
|
|
|
13
13
|
export interface PluginManifest {
|
|
14
14
|
name: string; // 必填,插件名称
|
|
@@ -28,13 +28,12 @@ const TYPE_FILES: Record<string, CapabilityType> = {
|
|
|
28
28
|
'AGENT.md': 'agent',
|
|
29
29
|
'COMMAND.md': 'command',
|
|
30
30
|
'HOOK.md': 'hook',
|
|
31
|
-
'MCP.md': 'mcp',
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
/**
|
|
35
34
|
* 从目录读取插件清单
|
|
36
35
|
*
|
|
37
|
-
* 扫描顺序:SKILL.md → AGENT.md → COMMAND.md → HOOK.md
|
|
36
|
+
* 扫描顺序:SKILL.md → AGENT.md → COMMAND.md → HOOK.md
|
|
38
37
|
* 返回第一个找到的清单
|
|
39
38
|
*/
|
|
40
39
|
export async function readManifest(pluginPath: string): Promise<PluginManifest> {
|
|
@@ -97,8 +96,7 @@ export async function readManifest(pluginPath: string): Promise<PluginManifest>
|
|
|
97
96
|
` - SKILL.md (技能插件)\n` +
|
|
98
97
|
` - AGENT.md (代理插件)\n` +
|
|
99
98
|
` - COMMAND.md (命令插件)\n` +
|
|
100
|
-
` - HOOK.md (钩子插件)
|
|
101
|
-
` - MCP.md (MCP 插件)`
|
|
99
|
+
` - HOOK.md (钩子插件)`
|
|
102
100
|
);
|
|
103
101
|
}
|
|
104
102
|
|
package/src/services/packager.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// 基础类型
|
|
9
9
|
// ============================================================================
|
|
10
10
|
|
|
11
|
-
export type PluginType = 'skill' | 'agent' | 'command' | 'hook'
|
|
11
|
+
export type PluginType = 'skill' | 'agent' | 'command' | 'hook';
|
|
12
12
|
export type PriceTier = 'free' | 'vip' | 'premium';
|
|
13
13
|
|
|
14
14
|
// ============================================================================
|
|
@@ -393,18 +393,3 @@ export interface HookConfig {
|
|
|
393
393
|
};
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
// MCP 配置
|
|
397
|
-
export interface McpConfig {
|
|
398
|
-
name?: string;
|
|
399
|
-
protocol_version?: string;
|
|
400
|
-
capabilities?: {
|
|
401
|
-
resources?: boolean;
|
|
402
|
-
tools?: boolean;
|
|
403
|
-
sampling?: boolean;
|
|
404
|
-
};
|
|
405
|
-
tools?: Array<{
|
|
406
|
-
name: string;
|
|
407
|
-
description?: string;
|
|
408
|
-
inputSchema?: Record<string, unknown>;
|
|
409
|
-
}>;
|
|
410
|
-
}
|
package/src/utils.ts
CHANGED
|
@@ -104,8 +104,6 @@ export function getInstallPath(type: PluginType, name: string): string {
|
|
|
104
104
|
return `.claude/commands/${name}.md`;
|
|
105
105
|
case 'hook':
|
|
106
106
|
return `.claude/hooks/${name}`;
|
|
107
|
-
case 'mcp':
|
|
108
|
-
return `.claude/mcp/${name}/`;
|
|
109
107
|
default:
|
|
110
108
|
return `.claude/${type}/${name}/`;
|
|
111
109
|
}
|
|
@@ -157,7 +155,6 @@ const TYPE_LABELS: Record<PluginType, string> = {
|
|
|
157
155
|
agent: 'Agent',
|
|
158
156
|
command: 'Command',
|
|
159
157
|
hook: 'Hook',
|
|
160
|
-
mcp: 'MCP',
|
|
161
158
|
};
|
|
162
159
|
|
|
163
160
|
const TYPE_ICONS: Record<PluginType, string> = {
|
|
@@ -165,7 +162,6 @@ const TYPE_ICONS: Record<PluginType, string> = {
|
|
|
165
162
|
agent: '🤖',
|
|
166
163
|
command: '⌘',
|
|
167
164
|
hook: '🪝',
|
|
168
|
-
mcp: '🔌',
|
|
169
165
|
};
|
|
170
166
|
|
|
171
167
|
export function getTypeLabel(type: PluginType): string {
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* - agent: Agent 插件(仅单文件)
|
|
7
7
|
* - command: 命令插件(目录或单文件)
|
|
8
8
|
* - hook: Hook 插件(仅目录,需要 hooks.json)
|
|
9
|
-
* - mcp: MCP 服务器插件(仅目录,需要 mcp.json)
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
import fs from 'fs/promises';
|
|
@@ -19,7 +18,6 @@ import type {
|
|
|
19
18
|
ValidationResult,
|
|
20
19
|
ValidationIssue,
|
|
21
20
|
HookConfig,
|
|
22
|
-
McpConfig,
|
|
23
21
|
} from '../types';
|
|
24
22
|
|
|
25
23
|
// ============================================================================
|
|
@@ -33,7 +31,7 @@ const MIN_DESCRIPTION_LENGTH = 20;
|
|
|
33
31
|
const MAX_TAG_LENGTH = 32;
|
|
34
32
|
const MAX_AGENT_FILE_SIZE = 50 * 1024; // 50KB
|
|
35
33
|
|
|
36
|
-
const VALID_TYPES: PluginType[] = ['skill', 'agent', 'command', 'hook'
|
|
34
|
+
const VALID_TYPES: PluginType[] = ['skill', 'agent', 'command', 'hook'];
|
|
37
35
|
|
|
38
36
|
// ============================================================================
|
|
39
37
|
// PluginValidator 类
|
|
@@ -349,9 +347,6 @@ export class PluginValidator {
|
|
|
349
347
|
case 'hook':
|
|
350
348
|
await this.validateHook(absPath, isDirectory, result);
|
|
351
349
|
break;
|
|
352
|
-
case 'mcp':
|
|
353
|
-
await this.validateMcp(absPath, isDirectory, result);
|
|
354
|
-
break;
|
|
355
350
|
case 'skill':
|
|
356
351
|
case 'command':
|
|
357
352
|
// skill 和 command 支持单文件和目录,无特殊限制
|
|
@@ -471,76 +466,6 @@ export class PluginValidator {
|
|
|
471
466
|
}
|
|
472
467
|
}
|
|
473
468
|
|
|
474
|
-
/**
|
|
475
|
-
* MCP 类型验证
|
|
476
|
-
*/
|
|
477
|
-
private async validateMcp(
|
|
478
|
-
absPath: string,
|
|
479
|
-
isDirectory: boolean,
|
|
480
|
-
result: ValidationResult
|
|
481
|
-
): Promise<void> {
|
|
482
|
-
// MCP 必须是目录
|
|
483
|
-
if (!isDirectory) {
|
|
484
|
-
result.errors.push({
|
|
485
|
-
code: 'E050',
|
|
486
|
-
field: 'type',
|
|
487
|
-
message: 'MCP 类型必须是目录格式',
|
|
488
|
-
suggestion: '请创建包含 mcp.json 的目录',
|
|
489
|
-
});
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// 检查 mcp.json 是否存在
|
|
494
|
-
const mcpJsonPath = path.join(absPath, 'mcp.json');
|
|
495
|
-
const dotMcpJsonPath = path.join(absPath, '.mcp.json');
|
|
496
|
-
|
|
497
|
-
let configPath: string | null = null;
|
|
498
|
-
if (await this.exists(mcpJsonPath)) {
|
|
499
|
-
configPath = mcpJsonPath;
|
|
500
|
-
} else if (await this.exists(dotMcpJsonPath)) {
|
|
501
|
-
configPath = dotMcpJsonPath;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (!configPath) {
|
|
505
|
-
result.errors.push({
|
|
506
|
-
code: 'E051',
|
|
507
|
-
message: '缺少 mcp.json 配置文件',
|
|
508
|
-
suggestion: '创建 mcp.json 文件定义 MCP 服务器配置',
|
|
509
|
-
});
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// 验证 mcp.json 内容
|
|
514
|
-
try {
|
|
515
|
-
const content = await fs.readFile(configPath, 'utf-8');
|
|
516
|
-
const config = JSON.parse(content) as McpConfig;
|
|
517
|
-
|
|
518
|
-
// 建议指定 protocol_version
|
|
519
|
-
if (!config.protocol_version) {
|
|
520
|
-
result.warnings.push({
|
|
521
|
-
code: 'W050',
|
|
522
|
-
message: '建议指定 protocol_version',
|
|
523
|
-
suggestion: '添加 "protocol_version": "2024-11" 到 mcp.json',
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// 建议声明 capabilities
|
|
528
|
-
if (!config.capabilities) {
|
|
529
|
-
result.warnings.push({
|
|
530
|
-
code: 'W051',
|
|
531
|
-
message: '建议声明 capabilities',
|
|
532
|
-
suggestion: '添加 capabilities 对象声明支持的功能',
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
} catch (e) {
|
|
536
|
-
result.errors.push({
|
|
537
|
-
code: 'E051',
|
|
538
|
-
message: `mcp.json 解析失败: ${(e as Error).message}`,
|
|
539
|
-
suggestion: '请检查 JSON 格式是否正确',
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
469
|
// ==========================================================================
|
|
545
470
|
// 引用检查
|
|
546
471
|
// ==========================================================================
|