
Plugin Creator
- 2 installs
- 1 repo stars
- Updated July 28, 2026
- evanfang0054/cc-system-creator-scripts
Packages skills, agents, commands, hooks, and MCP configs into a standard, distributable Claude Code plugin structure.
About
Organizes scattered Claude Code components (skills, agents, commands, hooks, MCP servers) into a spec-compliant plugin, validating and structuring them for sharing. A developer uses it to convert existing configs into a distributable plugin.
- Collects and validates skills, agents, commands, hooks, and MCP configs
- Produces a standard plugin structure for team or community distribution
Plugin Creator by the numbers
- 2 all-time installs (skills.sh)
- Ranked #609 of 782 Skill Development skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/evanfang0054/cc-system-creator-scripts --skill plugin-creatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 1 |
| Last updated | July 28, 2026 |
| Repository | evanfang0054/cc-system-creator-scripts ↗ |
What it does
Packages skills, agents, commands, hooks, and MCP configs into a standard, distributable Claude Code plugin structure.
Files
Plugin Creator
将分散的 Claude Code 组件整合为标准的可分发插件。
概述
此技能帮助用户将现有的 skills、agents、commands、hooks、MCP 服务器配置等组件,按照 Claude Code 插件规范组织成完整的插件结构,便于团队共享和社区分发。
工作流程
用户提供组件 → 分析与验证 → 规划结构 → 创建插件 → 验证完整性步骤 1:收集用户组件
首先明确用户提供的组件类型和位置:
| 组件类型 | 标识 | 来源位置 |
|---|---|---|
| Skills | skills/ | 包含 SKILL.md 的目录 |
| Agents | agents/*.md | YAML frontmatter 配置文件 |
| Commands | commands/*.md | 斜杠命令定义 |
| Hooks | hooks.json 或 settings.json | 钩子配置 |
| MCP | .mcp.json | MCP 服务器配置 |
| LSP | .lsp.json | LSP 服务器配置 |
| Settings | settings.json | 默认设置 |
询问用户: 1. 插件名称和描述 2. 各组件的源路径 3. 是否需要创建新的组件
步骤 2:规划插件结构
基于收集的组件,规划目标结构:
plugin-name/
├── .claude-plugin/
│ └── plugin.json # 必需:插件元数据
├── commands/ # 可选:斜杠命令
├── agents/ # 可选:代理定义
├── skills/ # 可选:技能
│ └── skill-name/
│ └── SKILL.md
├── hooks/ # 可选:钩子
│ └── hooks.json
├── .mcp.json # 可选:MCP 配置
├── .lsp.json # 可选:LSP 配置
├── settings.json # 可选:默认设置
└── README.md # 推荐:使用文档步骤 3:创建插件清单
生成 plugin.json:
{
"name": "plugin-name",
"version": "1.0.0",
"description": "插件功能描述",
"author": "作者名称"
}命名规范:
- 使用小写字母和连字符
- 简洁且具有描述性
- 避免与现有插件冲突
步骤 4:迁移组件
按以下规则迁移各组件:
Skills 迁移
- 复制整个 skill 目录到
skills/ - 验证 SKILL.md 包含有效的 frontmatter
- 确保
name和description字段完整
Agents 迁移
- 复制
.md文件到agents/ - 验证 YAML frontmatter 格式
- 确认
tools和model配置正确
Commands 迁移
- 复制
.md文件到commands/ - 命令文件无需 frontmatter
Hooks 迁移
从 settings.json 提取钩子配置,转换为 hooks/hooks.json:
{
"hooks": {
"PreToolUse": [...],
"PostToolUse": [...],
"Notification": [...]
}
}MCP/LSP 迁移
- 直接复制
.mcp.json和.lsp.json到插件根目录 - 检查路径引用是否需要调整
步骤 5:生成文档
创建 README.md,包含:
# Plugin Name
简短描述插件功能。
## 安装
claude /plugin install <plugin-path>
## 组件
### Skills
- `skill-name`: 技能描述
### Commands
- `/command-name`: 命令描述
### Agents
- `agent-name`: 代理描述
## 使用示例
具体使用示例...
## 配置
可选配置说明...步骤 6:验证插件
运行验证检查:
# 检查插件结构
claude --plugin-dir ./plugin-name --validate验证清单:
- [ ] plugin.json 存在且格式正确
- [ ] 所有 SKILL.md 包含有效 frontmatter
- [ ] agents/*.md 格式正确
- [ ] hooks.json 语法有效
- [ ] MCP/LSP 配置路径正确
- [ ] README.md 包含基本使用说明
组件格式规范
详细格式规范参见 references/plugin-spec.md。
SKILL.md 格式
---
name: skill-name
description: 触发条件和使用场景描述
---
技能指令内容...Agent 格式
---
name: agent-name
description: 代理用途描述
tools: Read, Write, Edit, Grep, Glob, Bash
model: inherit
---
系统提示内容...Hooks 格式
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": ["命令"]
}
]
}
}常见问题
| 问题 | 解决方案 |
|---|---|
| 技能命名冲突 | 使用插件命名空间:/plugin-name:skill-name |
| 路径引用错误 | 使用相对于插件根目录的路径 |
| 钩子不触发 | 检查 matcher 配置和命令可执行性 |
| MCP 连接失败 | 验证命令路径和环境变量 |
输出
完成后输出: 1. 插件目录结构概览 2. 包含的组件清单 3. 安装和测试命令 4. 后续分发建议
Claude Code 插件开发规范
基于 Claude Code 官方文档 整理的完整插件开发指南。
目录
---
何时使用插件 vs 独立配置
Claude Code 支持两种方式添加自定义技能、代理和钩子:
| 方式 | 技能名称 | 适用场景 |
|---|---|---|
独立配置 (.claude/ 目录) | /hello | 个人工作流、项目特定定制、快速实验 |
插件 (带 .claude-plugin/plugin.json 的目录) | /plugin-name:hello | 团队共享、社区分发、版本发布、跨项目复用 |
使用独立配置的场景
- 为单个项目定制 Claude Code
- 配置是个人使用的,无需共享
- 在打包前实验技能或钩子
- 需要简短的技能名称如
/hello或/review
使用插件的场景
- 与团队或社区共享功能
- 跨多个项目使用相同的技能/代理
- 需要版本控制和便捷更新
- 通过市场分发
- 接受命名空间技能如
/my-plugin:hello(命名空间防止插件间冲突)
---
插件目录结构
目录说明
| 目录 | 位置 | 用途 |
|---|---|---|
.claude-plugin/ | 插件根目录 | 包含 plugin.json 清单(组件使用默认位置时可选) |
commands/ | 插件根目录 | 斜杠命令(Markdown 文件) |
agents/ | 插件根目录 | 自定义代理定义 |
skills/ | 插件根目录 | 代理技能(包含 SKILL.md 文件) |
hooks/ | 插件根目录 | hooks.json 中的事件处理器 |
.mcp.json | 插件根目录 | MCP 服务器配置 |
.lsp.json | 插件根目录 | 代码智能的 LSP 服务器配置 |
settings.json | 插件根目录 | 插件启用时应用的默认设置 |
完整插件结构示例
plugin-name/
├── .claude-plugin/
│ └── plugin.json # 插件元数据清单
├── commands/ # 斜杠命令(可选)
│ └── review.md # 命令定义
├── agents/ # 专业代理(可选)
│ └── security-reviewer.md # 代理配置
├── skills/ # 代理技能(可选)
│ └── code-review/
│ └── SKILL.md # 技能定义
├── hooks/ # 事件处理器(可选)
│ └── hooks.json # 钩子配置
├── .mcp.json # MCP 服务器配置(可选)
├── .lsp.json # LSP 服务器配置(可选)
├── settings.json # 默认设置(可选)
└── README.md # 插件文档---
配置文件详解
插件清单 (plugin.json)
{
"name": "my-plugin",
"version": "1.0.0",
"description": "插件描述",
"author": "作者名称"
}技能定义 (SKILL.md)
每个 SKILL.md 需要包含 name 和 description 的 frontmatter,后跟指令:
---
name: code-review
description: 审查代码的最佳实践和潜在问题。用于审查代码、检查 PR 或分析代码质量时使用。
---
审查代码时检查:
1. 代码组织和结构
2. 错误处理
3. 安全问题
4. 测试覆盖率代理配置 (agents/*.md)
---
name: security-reviewer
description: 安全审查代理
tools: Read, Grep, Glob
model: inherit
---
系统提示内容...可用工具列表:
Read- 读取文件Write- 写入文件Edit- 编辑文件Glob- 文件模式匹配Grep- 内容搜索Bash- 执行命令
模型选择:
inherit- 继承父代理模型sonnet- Claude Sonnetopus- Claude Opushaiku- Claude Haiku
钩子配置 (hooks/hooks.json)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": ["echo '执行命令前'"]
}
],
"PostToolUse": [
{
"matcher": "Write",
"hooks": ["echo '文件写入后'"]
}
]
}
}支持的钩子事件:
PreToolUse- 工具执行前PostToolUse- 工具执行后Notification- 通知事件
MCP 服务器配置 (.mcp.json)
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["./server.js"],
"env": {
"API_KEY": "your-key"
}
}
}
}LSP 服务器配置 (.lsp.json)
{
"go": {
"command": "gopls",
"args": ["serve"],
"extensionToLanguage": {
".go": "go"
}
}
}默认设置 (settings.json)
{
"agent": "security-reviewer"
}设置 agent 会激活插件的自定义代理作为主线程,应用其系统提示、工具限制和模型。
---
开发与测试
本地测试插件
# 使用 --plugin-dir 标志测试插件
claude --plugin-dir ./my-plugin测试清单
- [ ] 使用
/plugin-name:skill-name测试技能 - [ ] 检查代理是否出现在
/agents中 - [ ] 验证钩子按预期工作
- [ ] 测试 MCP 服务器连接
- [ ] 验证 LSP 服务器功能
调试插件问题
1. 检查结构:确保目录位于插件根目录,不在 .claude-plugin/ 内 2. 单独测试组件:分别检查每个命令、代理和钩子 3. 使用验证工具:参考调试和开发工具的 CLI 命令 4. 查看日志:检查 Claude Code 的输出日志
常见问题
| 问题 | 解决方案 |
|---|---|
| 技能无法加载 | 检查 SKILL.md 的 frontmatter 格式 |
| 代理不显示 | 确认文件位于 agents/ 目录 |
| 钩子不触发 | 验证 hooks.json 的 matcher 配置 |
| MCP 连接失败 | 检查命令路径和环境变量 |
---
发布与分发
发布前检查
1. 添加文档:包含 README.md,说明安装和使用方法 2. 版本控制:在 plugin.json 中使用语义化版本 3. 创建或使用市场:通过插件市场分发 4. 团队测试:在更广泛分发前让团队成员测试
版本规范
使用语义化版本号(Semantic Versioning):
1.0.0- 初始稳定版本1.1.0- 新增功能1.1.1- Bug 修复2.0.0- 重大变更
提交到官方市场
使用以下表单提交到 Anthropic 官方市场:
- Claude.ai: claude.ai/settings/plugins/submit
- Console: platform.claude.com/plugins/submit
---
迁移指南
从独立配置迁移到插件
独立配置 (.claude/) | 插件 |
|---|---|
| 仅在一个项目中可用 | 可通过市场共享 |
文件在 .claude/commands/ | 文件在 plugin-name/commands/ |
钩子在 settings.json | 钩子在 hooks/hooks.json |
| 必须手动复制共享 | 使用 /plugin install 安装 |
迁移步骤
1. 创建插件目录结构 2. 复制命令文件到 commands/ 3. 复制代理文件到 agents/ 4. 复制技能文件夹到 skills/ 5. 转换钩子配置到 hooks/hooks.json 6. 创建 plugin.json 清单 7. 添加 README.md 文档 8. 测试插件功能
---
参考资料
/**
* 插件模板生成脚本
* 快速创建标准 Claude Code 插件目录结构
*/
import * as fs from 'fs';
import * as path from 'path';
interface PluginOptions {
name: string;
version?: string;
description?: string;
author?: string;
includeSkills?: boolean;
includeAgents?: boolean;
includeCommands?: boolean;
includeHooks?: boolean;
includeMcp?: boolean;
includeLsp?: boolean;
includeSettings?: boolean;
}
/**
* 创建插件目录结构
*/
export function createPlugin(outputPath: string, options: PluginOptions): void {
const pluginDir = path.join(outputPath, options.name);
// 检查目录是否已存在
if (fs.existsSync(pluginDir)) {
throw new Error(`目录已存在: ${pluginDir}`);
}
// 创建主目录
fs.mkdirSync(pluginDir, { recursive: true });
// 1. 创建 plugin.json
const pluginJsonDir = path.join(pluginDir, '.claude-plugin');
fs.mkdirSync(pluginJsonDir, { recursive: true });
const pluginJson = {
name: options.name,
version: options.version || '1.0.0',
description: options.description || `${options.name} 插件`,
author: options.author || '',
};
fs.writeFileSync(
path.join(pluginJsonDir, 'plugin.json'),
JSON.stringify(pluginJson, null, 2)
);
// 2. 创建 skills 目录
if (options.includeSkills) {
const skillsDir = path.join(pluginDir, 'skills', 'example-skill');
fs.mkdirSync(skillsDir, { recursive: true });
const skillMd = `---
name: example-skill
description: 示例技能描述。说明何时使用此技能的触发条件和使用场景。
---
# Example Skill
技能的具体指令和使用说明。
## 使用方法
1. 步骤一
2. 步骤二
3. 步骤三
## 示例
\`\`\`typescript
// 示例代码
console.log('Hello from ${options.name}!');
\`\`\`
`;
fs.writeFileSync(path.join(skillsDir, 'SKILL.md'), skillMd);
}
// 3. 创建 agents 目录
if (options.includeAgents) {
const agentsDir = path.join(pluginDir, 'agents');
fs.mkdirSync(agentsDir, { recursive: true });
const agentMd = `---
name: example-agent
description: 示例代理描述
tools: Read, Write, Edit, Grep, Glob
model: inherit
---
# Example Agent
你是一个专业的示例代理。
## 职责
- 职责一
- 职责二
- 职责三
## 工作流程
1. 分析任务
2. 执行操作
3. 验证结果
`;
fs.writeFileSync(path.join(agentsDir, 'example-agent.md'), agentMd);
}
// 4. 创建 commands 目录
if (options.includeCommands) {
const commandsDir = path.join(pluginDir, 'commands');
fs.mkdirSync(commandsDir, { recursive: true });
const commandMd = `# Example Command
这是一个示例命令。
## 使用方法
\`\`\`
/${options.name}:example
\`\`\`
## 功能说明
执行示例命令时,Claude 会按照以下步骤操作:
1. 步骤一
2. 步骤二
3. 步骤三
`;
fs.writeFileSync(path.join(commandsDir, 'example.md'), commandMd);
}
// 5. 创建 hooks 目录
if (options.includeHooks) {
const hooksDir = path.join(pluginDir, 'hooks');
fs.mkdirSync(hooksDir, { recursive: true });
const hooksJson = {
hooks: {
PreToolUse: [
{
matcher: 'Bash',
hooks: ['echo "即将执行命令"'],
},
],
PostToolUse: [
{
matcher: 'Write',
hooks: ['echo "文件已写入"'],
},
],
},
};
fs.writeFileSync(
path.join(hooksDir, 'hooks.json'),
JSON.stringify(hooksJson, null, 2)
);
}
// 6. 创建 .mcp.json
if (options.includeMcp) {
const mcpJson = {
mcpServers: {
'example-server': {
command: 'node',
args: ['./server.js'],
env: {
API_KEY: 'your-api-key',
},
},
},
};
fs.writeFileSync(
path.join(pluginDir, '.mcp.json'),
JSON.stringify(mcpJson, null, 2)
);
}
// 7. 创建 .lsp.json
if (options.includeLsp) {
const lspJson = {
typescript: {
command: 'typescript-language-server',
args: ['--stdio'],
extensionToLanguage: {
'.ts': 'typescript',
'.tsx': 'typescriptreact',
},
},
};
fs.writeFileSync(
path.join(pluginDir, '.lsp.json'),
JSON.stringify(lspJson, null, 2)
);
}
// 8. 创建 settings.json
if (options.includeSettings) {
const settingsJson = {
// 默认设置
};
fs.writeFileSync(
path.join(pluginDir, 'settings.json'),
JSON.stringify(settingsJson, null, 2)
);
}
// 9. 创建 README.md
const readme = `# ${options.name}
${options.description || `${options.name} 插件`}
## 安装
\`\`\`bash
claude /plugin install <plugin-path>
\`\`\`
## 组件
${options.includeSkills ? `### Skills\n- \`example-skill\`: 示例技能描述\n\n` : ''}${options.includeAgents ? `### Agents\n- \`example-agent\`: 示例代理描述\n\n` : ''}${options.includeCommands ? `### Commands\n- \`/${options.name}:example\`: 示例命令\n\n` : ''}${options.includeHooks ? `### Hooks\n- PreToolUse: Bash 命令执行前\n- PostToolUse: Write 文件写入后\n\n` : ''}${options.includeMcp ? `### MCP\n- \`example-server\`: 示例 MCP 服务器\n\n` : ''}
## 使用示例
\`\`\`
/${options.name}:example
\`\`\`
## 配置
参见各配置文件了解详细配置选项。
## 版本历史
- \`1.0.0\` - 初始版本
`;
fs.writeFileSync(path.join(pluginDir, 'README.md'), readme);
console.log(`✅ 插件已创建: ${pluginDir}`);
console.log('\n目录结构:');
printTree(pluginDir, '', true);
}
/**
* 打印目录树
*/
function printTree(dir: string, prefix: string, isLast: boolean): void {
const name = path.basename(dir);
const stats = fs.statSync(dir);
if (stats.isDirectory()) {
console.log(`${prefix}${isLast ? '└── ' : '├── '}${name}/`);
const children = fs.readdirSync(dir);
const newPrefix = prefix + (isLast ? ' ' : '│ ');
children.forEach((child, index) => {
const childPath = path.join(dir, child);
const childIsLast = index === children.length - 1;
printTree(childPath, newPrefix, childIsLast);
});
} else {
console.log(`${prefix}${isLast ? '└── ' : '├── '}${name}`);
}
}
// CLI 入口
if (require.main === module) {
const args = process.argv.slice(2);
if (args.length < 2) {
console.log('用法: npx ts-node create_plugin.ts <output-path> <plugin-name> [options]');
console.log('');
console.log('选项:');
console.log(' --description <desc> 插件描述');
console.log(' --author <name> 作者名称');
console.log(' --with-skills 包含示例技能');
console.log(' --with-agents 包含示例代理');
console.log(' --with-commands 包含示例命令');
console.log(' --with-hooks 包含示例钩子');
console.log(' --with-mcp 包含 MCP 配置');
console.log(' --with-lsp 包含 LSP 配置');
console.log(' --with-settings 包含设置文件');
console.log(' --full 包含所有组件');
console.log('');
console.log('示例:');
console.log(' npx ts-node create_plugin.ts ./plugins my-plugin --full');
console.log(' npx ts-node create_plugin.ts ./plugins my-plugin --with-skills --with-commands');
process.exit(1);
}
const outputPath = args[0];
const pluginName = args[1];
const options: PluginOptions = {
name: pluginName,
};
for (let i = 2; i < args.length; i++) {
switch (args[i]) {
case '--description':
options.description = args[++i];
break;
case '--author':
options.author = args[++i];
break;
case '--with-skills':
options.includeSkills = true;
break;
case '--with-agents':
options.includeAgents = true;
break;
case '--with-commands':
options.includeCommands = true;
break;
case '--with-hooks':
options.includeHooks = true;
break;
case '--with-mcp':
options.includeMcp = true;
break;
case '--with-lsp':
options.includeLsp = true;
break;
case '--with-settings':
options.includeSettings = true;
break;
case '--full':
options.includeSkills = true;
options.includeAgents = true;
options.includeCommands = true;
options.includeHooks = true;
options.includeMcp = true;
options.includeLsp = true;
options.includeSettings = true;
break;
}
}
try {
createPlugin(outputPath, options);
} catch (error) {
console.error(`❌ 错误: ${(error as Error).message}`);
process.exit(1);
}
}
/**
* 插件验证脚本
* 用于检查 Claude Code 插件结构的完整性
*/
import * as fs from 'fs';
import * as path from 'path';
interface ValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
components: {
skills: string[];
agents: string[];
commands: string[];
hooks: boolean;
mcp: boolean;
lsp: boolean;
settings: boolean;
};
}
/**
* 验证插件目录结构
*/
export function validatePlugin(pluginPath: string): ValidationResult {
const result: ValidationResult = {
valid: true,
errors: [],
warnings: [],
components: {
skills: [],
agents: [],
commands: [],
hooks: false,
mcp: false,
lsp: false,
settings: false,
},
};
// 检查目录是否存在
if (!fs.existsSync(pluginPath)) {
result.valid = false;
result.errors.push(`插件目录不存在: ${pluginPath}`);
return result;
}
// 1. 检查 plugin.json
const pluginJsonPath = path.join(pluginPath, '.claude-plugin', 'plugin.json');
if (fs.existsSync(pluginJsonPath)) {
try {
const content = fs.readFileSync(pluginJsonPath, 'utf-8');
const pluginJson = JSON.parse(content);
if (!pluginJson.name) {
result.errors.push('plugin.json 缺少 name 字段');
result.valid = false;
}
if (!pluginJson.version) {
result.warnings.push('plugin.json 建议包含 version 字段');
}
if (!pluginJson.description) {
result.warnings.push('plugin.json 建议包含 description 字段');
}
} catch (e) {
result.errors.push(`plugin.json 解析失败: ${(e as Error).message}`);
result.valid = false;
}
} else {
result.warnings.push('未找到 .claude-plugin/plugin.json(可选但推荐)');
}
// 2. 检查 skills
const skillsPath = path.join(pluginPath, 'skills');
if (fs.existsSync(skillsPath)) {
const skillDirs = fs.readdirSync(skillsPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
for (const skillName of skillDirs) {
const skillMdPath = path.join(skillsPath, skillName, 'SKILL.md');
if (fs.existsSync(skillMdPath)) {
const content = fs.readFileSync(skillMdPath, 'utf-8');
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatterMatch) {
result.errors.push(`skills/${skillName}/SKILL.md 缺少 frontmatter`);
result.valid = false;
} else {
const frontmatter = frontmatterMatch[1];
if (!frontmatter.includes('name:')) {
result.errors.push(`skills/${skillName}/SKILL.md frontmatter 缺少 name 字段`);
result.valid = false;
}
if (!frontmatter.includes('description:')) {
result.errors.push(`skills/${skillName}/SKILL.md frontmatter 缺少 description 字段`);
result.valid = false;
}
}
result.components.skills.push(skillName);
} else {
result.warnings.push(`skills/${skillName} 目录缺少 SKILL.md 文件`);
}
}
}
// 3. 检查 agents
const agentsPath = path.join(pluginPath, 'agents');
if (fs.existsSync(agentsPath)) {
const agentFiles = fs.readdirSync(agentsPath)
.filter(file => file.endsWith('.md'));
for (const agentFile of agentFiles) {
const agentPath = path.join(agentsPath, agentFile);
const content = fs.readFileSync(agentPath, 'utf-8');
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatterMatch) {
result.errors.push(`agents/${agentFile} 缺少 frontmatter`);
result.valid = false;
} else {
const frontmatter = frontmatterMatch[1];
if (!frontmatter.includes('name:')) {
result.errors.push(`agents/${agentFile} frontmatter 缺少 name 字段`);
result.valid = false;
}
if (!frontmatter.includes('description:')) {
result.errors.push(`agents/${agentFile} frontmatter 缺少 description 字段`);
result.valid = false;
}
}
result.components.agents.push(agentFile.replace('.md', ''));
}
}
// 4. 检查 commands
const commandsPath = path.join(pluginPath, 'commands');
if (fs.existsSync(commandsPath)) {
const commandFiles = fs.readdirSync(commandsPath)
.filter(file => file.endsWith('.md'));
for (const commandFile of commandFiles) {
result.components.commands.push(commandFile.replace('.md', ''));
}
}
// 5. 检查 hooks
const hooksPath = path.join(pluginPath, 'hooks', 'hooks.json');
if (fs.existsSync(hooksPath)) {
try {
const content = fs.readFileSync(hooksPath, 'utf-8');
const hooksJson = JSON.parse(content);
if (!hooksJson.hooks) {
result.warnings.push('hooks/hooks.json 建议包含 hooks 字段');
}
result.components.hooks = true;
} catch (e) {
result.errors.push(`hooks/hooks.json 解析失败: ${(e as Error).message}`);
result.valid = false;
}
}
// 6. 检查 MCP 配置
const mcpPath = path.join(pluginPath, '.mcp.json');
if (fs.existsSync(mcpPath)) {
try {
const content = fs.readFileSync(mcpPath, 'utf-8');
const mcpJson = JSON.parse(content);
if (!mcpJson.mcpServers || Object.keys(mcpJson.mcpServers).length === 0) {
result.warnings.push('.mcp.json 建议包含 mcpServers 配置');
}
result.components.mcp = true;
} catch (e) {
result.errors.push(`.mcp.json 解析失败: ${(e as Error).message}`);
result.valid = false;
}
}
// 7. 检查 LSP 配置
const lspPath = path.join(pluginPath, '.lsp.json');
if (fs.existsSync(lspPath)) {
try {
fs.readFileSync(lspPath, 'utf-8');
JSON.parse(fs.readFileSync(lspPath, 'utf-8'));
result.components.lsp = true;
} catch (e) {
result.errors.push(`.lsp.json 解析失败: ${(e as Error).message}`);
result.valid = false;
}
}
// 8. 检查 settings
const settingsPath = path.join(pluginPath, 'settings.json');
if (fs.existsSync(settingsPath)) {
try {
fs.readFileSync(settingsPath, 'utf-8');
JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
result.components.settings = true;
} catch (e) {
result.errors.push(`settings.json 解析失败: ${(e as Error).message}`);
result.valid = false;
}
}
// 9. 检查 README
const readmePath = path.join(pluginPath, 'README.md');
if (!fs.existsSync(readmePath)) {
result.warnings.push('建议添加 README.md 文档');
}
return result;
}
/**
* 打印验证结果
*/
export function printValidationResult(result: ValidationResult): void {
console.log('\n=== 插件验证结果 ===\n');
if (result.valid) {
console.log('✅ 插件结构有效\n');
} else {
console.log('❌ 插件结构存在问题\n');
}
if (result.errors.length > 0) {
console.log('错误:');
result.errors.forEach(err => console.log(` ❌ ${err}`));
console.log('');
}
if (result.warnings.length > 0) {
console.log('警告:');
result.warnings.forEach(warn => console.log(` ⚠️ ${warn}`));
console.log('');
}
console.log('组件统计:');
console.log(` 📦 Skills: ${result.components.skills.length > 0 ? result.components.skills.join(', ') : '无'}`);
console.log(` 🤖 Agents: ${result.components.agents.length > 0 ? result.components.agents.join(', ') : '无'}`);
console.log(` ⚡ Commands: ${result.components.commands.length > 0 ? result.components.commands.join(', ') : '无'}`);
console.log(` 🪝 Hooks: ${result.components.hooks ? '已配置' : '未配置'}`);
console.log(` 🔌 MCP: ${result.components.mcp ? '已配置' : '未配置'}`);
console.log(` 📝 LSP: ${result.components.lsp ? '已配置' : '未配置'}`);
console.log(` ⚙️ Settings: ${result.components.settings ? '已配置' : '未配置'}`);
}
// CLI 入口
if (require.main === module) {
const pluginPath = process.argv[2];
if (!pluginPath) {
console.error('用法: npx ts-node validate_plugin.ts <plugin-path>');
process.exit(1);
}
const result = validatePlugin(pluginPath);
printValidationResult(result);
process.exit(result.valid ? 0 : 1);
}