@42ailab/42plugin 0.1.25 → 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.

Potentially problematic release.


This version of @42ailab/42plugin might be problematic. Click here for more details.

package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@42ailab/42plugin",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "活水插件",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/api.ts CHANGED
@@ -527,7 +527,7 @@ class ApiClient {
527
527
  version?: string;
528
528
  content_hash?: string;
529
529
  visibility?: 'self' | 'public';
530
- }>(`/v1/plugins/me/${name}/status`);
530
+ }>(`/v1/plugins/status/${name}`);
531
531
 
532
532
  return {
533
533
  exists: result.exists,
@@ -29,7 +29,6 @@ const SKIP_AUTH_COMMANDS = new Set([
29
29
  'auth',
30
30
  'list',
31
31
  'check',
32
- 'completion',
33
32
  'setup',
34
33
  'version',
35
34
  ]);
@@ -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, mcp)')
17
+ .option('-t, --type <type>', '筛选类型 (skill, agent, command, hook)')
18
18
  .option('--json', '输出 JSON 格式')
19
19
  .action(async (options) => {
20
20
  try {
@@ -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', 'mcp'];
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|mcp)')
76
+ .option('-t, --type <type>', '指定插件类型 (skill|agent|command|hook)')
84
77
  .action(async (pluginPath, options) => {
85
78
  try {
86
79
  // 验证 --type 参数
@@ -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, mcp)')
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))
@@ -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('请手动运行: 42plugin completion <bash|zsh|fish>'));
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 completion');
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
- eval "$(42plugin completion bash)"
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
- eval "$(42plugin completion zsh)"
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
- 42plugin completion fish | source
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/db.ts CHANGED
@@ -581,24 +581,49 @@ export async function downloadAndExtract(
581
581
  }
582
582
 
583
583
  // 解压
584
- await tar.extract({ file: tempFile, cwd: targetDir, strip: 0 });
584
+ if (isWindows) {
585
+ // Windows: 使用 tar.Parser 手动提取,解决 Bun 编译的 tar 包无法正确写入文件的问题
586
+ const fileBuffer = await fs.readFile(tempFile);
587
+ const { Parser } = await import('tar');
588
+ const parser = new Parser();
589
+ const extractPromises: Promise<void>[] = [];
590
+
591
+ parser.on('entry', (entry) => {
592
+ const entryPath = path.join(targetDir, entry.path);
593
+ if (entry.type === 'Directory') {
594
+ extractPromises.push(fs.mkdir(entryPath, { recursive: true }));
595
+ } else if (entry.type === 'File') {
596
+ const chunks: Buffer[] = [];
597
+ entry.on('data', (chunk: Buffer) => chunks.push(chunk));
598
+ entry.on('end', () => {
599
+ extractPromises.push(
600
+ (async () => {
601
+ await fs.mkdir(path.dirname(entryPath), { recursive: true });
602
+ await fs.writeFile(entryPath, Buffer.concat(chunks));
603
+ })()
604
+ );
605
+ });
606
+ }
607
+ entry.resume();
608
+ });
585
609
 
586
- if (config.debug) {
587
- const entries = await fs.readdir(targetDir);
588
- console.log(`[DEBUG] 解压完成,目录内容: ${entries.join(', ')}`);
610
+ await new Promise<void>((resolve, reject) => {
611
+ parser.on('end', resolve);
612
+ parser.on('error', reject);
613
+ parser.write(fileBuffer);
614
+ parser.end();
615
+ });
616
+
617
+ await Promise.all(extractPromises);
618
+ } else {
619
+ await tar.extract({ file: tempFile, cwd: targetDir, strip: 0 });
589
620
  }
590
621
 
591
622
  // 清理
592
623
  await fs.unlink(tempFile).catch(() => {});
593
624
 
594
625
  // 返回最终路径(如果只有一个子目录则进入)
595
- const finalPath = await resolveFinalPath(targetDir, pluginType);
596
- if (config.debug) {
597
- console.log(`[DEBUG] 最终缓存路径: ${finalPath}`);
598
- const finalEntries = await fs.readdir(finalPath).catch(() => []);
599
- console.log(`[DEBUG] 最终路径内容: ${finalEntries.join(', ') || '(空)'}`);
600
- }
601
- return finalPath;
626
+ return resolveFinalPath(targetDir, pluginType);
602
627
  } catch (error) {
603
628
  await fs.rm(targetDir, { recursive: true, force: true }).catch(() => {});
604
629
  throw error;
@@ -765,17 +790,6 @@ export async function createLink(
765
790
  const targetDir = path.dirname(normalizedTarget);
766
791
  await fs.mkdir(targetDir, { recursive: true });
767
792
 
768
- if (config.debug) {
769
- console.log(`[DEBUG] createLink: ${sourcePath} -> ${normalizedTarget}`);
770
- }
771
-
772
- // 验证源路径存在
773
- try {
774
- await fs.access(sourcePath);
775
- } catch {
776
- throw new Error(`源路径不存在: ${sourcePath}`);
777
- }
778
-
779
793
  // 移除已存在的链接
780
794
  try {
781
795
  const stat = await fs.lstat(normalizedTarget);
@@ -790,11 +804,6 @@ export async function createLink(
790
804
  const sourceStat = await fs.stat(sourcePath);
791
805
  const isDirectory = sourceStat.isDirectory();
792
806
 
793
- if (config.debug && isDirectory) {
794
- const entries = await fs.readdir(sourcePath);
795
- console.log(`[DEBUG] 源目录内容 (${entries.length} 项): ${entries.join(', ')}`);
796
- }
797
-
798
807
  // 尝试创建符号链接
799
808
  try {
800
809
  const linkType = isDirectory ? 'dir' : 'file';
@@ -815,36 +824,19 @@ export async function createLink(
815
824
  // 回退方案 1: 对于目录,尝试使用 Junction
816
825
  if (isDirectory) {
817
826
  try {
818
- if (config.debug) {
819
- console.log(`[DEBUG] 尝试创建 Junction: ${normalizedTarget} -> ${sourcePath}`);
820
- }
821
827
  await createJunction(sourcePath, normalizedTarget);
822
- if (config.debug) {
823
- console.log(`[DEBUG] Junction 创建成功`);
824
- }
825
828
  return;
826
- } catch (junctionError) {
827
- if (config.debug) {
828
- console.log(`[DEBUG] Junction 创建失败: ${(junctionError as Error).message}`);
829
- }
829
+ } catch {
830
830
  // Junction 也失败,继续尝试复制
831
831
  }
832
832
  }
833
833
 
834
834
  // 回退方案 2: 直接复制文件/目录
835
- // 这是最后的保底方案,虽然会占用更多磁盘空间
836
835
  console.warn(
837
836
  `[Windows] 符号链接创建失败,回退到文件复制模式\n` +
838
837
  ` 提示: 启用 Windows 开发者模式可使用符号链接,节省磁盘空间`
839
838
  );
840
- if (config.debug) {
841
- console.log(`[DEBUG] 开始复制: ${sourcePath} -> ${normalizedTarget}`);
842
- }
843
839
  await copyRecursive(sourcePath, normalizedTarget);
844
- if (config.debug) {
845
- const destEntries = await fs.readdir(normalizedTarget).catch(() => []);
846
- console.log(`[DEBUG] 复制完成,目标目录内容: ${destEntries.join(', ') || '(空)'}`);
847
- }
848
840
  }
849
841
  }
850
842
 
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * 插件清单解析服务
3
3
  *
4
- * 从插件目录读取 SKILL.md / AGENT.md / COMMAND.md / HOOK.md / MCP.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' | 'mcp';
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 → MCP.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 (钩子插件)\n` +
101
- ` - MCP.md (MCP 插件)`
99
+ ` - HOOK.md (钩子插件)`
102
100
  );
103
101
  }
104
102
 
@@ -16,7 +16,6 @@ import type { PackageResult } from '../types';
16
16
 
17
17
  // 需要保留的功能性隐藏文件
18
18
  const ALLOWED_HIDDEN_FILES = [
19
- '.mcp.json',
20
19
  '.tool.json',
21
20
  '.plugin.json',
22
21
  '.claude',
@@ -268,7 +268,6 @@ export class Publisher {
268
268
  agent: ['AGENT.md', 'agent.md'],
269
269
  command: ['COMMAND.md', 'command.md'],
270
270
  hook: ['HOOK.md', 'hook.md'],
271
- mcp: ['MCP.md', 'mcp.md'],
272
271
  };
273
272
 
274
273
  const fallbackNames = ['README.md', 'readme.md'];
package/src/types.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  // 基础类型
9
9
  // ============================================================================
10
10
 
11
- export type PluginType = 'skill' | 'agent' | 'command' | 'hook' | 'mcp';
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', 'mcp'];
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
  // ==========================================================================