@birdie_moblie/plugin-marketplace-cli 1.0.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/README.md +71 -0
- package/dist/adapters/claude.d.ts +41 -0
- package/dist/adapters/claude.js +89 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +54 -0
- package/dist/adapters/codex.js +96 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +58 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +9 -0
- package/dist/commands/build.js +37 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/index-gen.d.ts +9 -0
- package/dist/commands/index-gen.js +72 -0
- package/dist/commands/index-gen.js.map +1 -0
- package/dist/commands/init-repo.d.ts +7 -0
- package/dist/commands/init-repo.js +116 -0
- package/dist/commands/init-repo.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +101 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/release-local.d.ts +8 -0
- package/dist/commands/release-local.js +106 -0
- package/dist/commands/release-local.js.map +1 -0
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.js +150 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/schema/plugin-yaml.d.ts +59 -0
- package/dist/schema/plugin-yaml.js +140 -0
- package/dist/schema/plugin-yaml.js.map +1 -0
- package/dist/utils/helpers.d.ts +47 -0
- package/dist/utils/helpers.js +117 -0
- package/dist/utils/helpers.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* init-repo 命令 — 创建新的 plugin marketplace 仓库
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { basename, join } from 'node:path';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { resolveInvocationPath, toTitleCase } from '../utils/helpers.js';
|
|
8
|
+
const CLI_PACKAGE = '@birdie_moblie/plugin-marketplace-cli';
|
|
9
|
+
const DEFAULT_SCRIPTS = {
|
|
10
|
+
mp: 'plugin-marketplace',
|
|
11
|
+
'validate:plugins': 'plugin-marketplace validate --all',
|
|
12
|
+
'build:plugins': 'plugin-marketplace build --all',
|
|
13
|
+
'build:index': 'plugin-marketplace index',
|
|
14
|
+
'build:all': 'npm run build:plugins && npm run build:index',
|
|
15
|
+
'ci:local': 'npm run validate:plugins && npm run build:all',
|
|
16
|
+
'release:local': 'npm run ci:local && plugin-marketplace release-local',
|
|
17
|
+
};
|
|
18
|
+
function marketplaceYaml(name, organization) {
|
|
19
|
+
return `# Marketplace metadata
|
|
20
|
+
|
|
21
|
+
name: ${name}
|
|
22
|
+
description: "${toTitleCase(name)} plugin marketplace"
|
|
23
|
+
organization: "${organization}"
|
|
24
|
+
|
|
25
|
+
categories:
|
|
26
|
+
- id: tooling
|
|
27
|
+
label: "Tooling"
|
|
28
|
+
description: "Development tools and scaffolding"
|
|
29
|
+
- id: integration
|
|
30
|
+
label: "Integration"
|
|
31
|
+
description: "Third-party service connectors"
|
|
32
|
+
- id: quality
|
|
33
|
+
label: "Quality"
|
|
34
|
+
description: "Quality checks and review workflows"
|
|
35
|
+
- id: workflow
|
|
36
|
+
label: "Workflow"
|
|
37
|
+
description: "Automation workflows"
|
|
38
|
+
- id: general
|
|
39
|
+
label: "General"
|
|
40
|
+
description: "Other plugins"
|
|
41
|
+
|
|
42
|
+
platforms:
|
|
43
|
+
- name: "Claude Code"
|
|
44
|
+
manifest: ".claude-plugin/plugin.json"
|
|
45
|
+
- name: "Codex"
|
|
46
|
+
manifest: ".codex-plugin/plugin.json"
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
function readPackageJson(path) {
|
|
50
|
+
const raw = readFileSync(path, 'utf-8');
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
53
|
+
throw new Error(`package.json must contain an object: ${path}`);
|
|
54
|
+
}
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
function ensurePackageJson(rootDir, name) {
|
|
58
|
+
const packagePath = join(rootDir, 'package.json');
|
|
59
|
+
const packageJson = existsSync(packagePath)
|
|
60
|
+
? readPackageJson(packagePath)
|
|
61
|
+
: {
|
|
62
|
+
name,
|
|
63
|
+
version: '1.0.0',
|
|
64
|
+
private: true,
|
|
65
|
+
};
|
|
66
|
+
packageJson.scripts = {
|
|
67
|
+
...DEFAULT_SCRIPTS,
|
|
68
|
+
...(packageJson.scripts || {}),
|
|
69
|
+
};
|
|
70
|
+
packageJson.devDependencies = {
|
|
71
|
+
...(packageJson.devDependencies || {}),
|
|
72
|
+
};
|
|
73
|
+
if (!packageJson.devDependencies[CLI_PACKAGE]) {
|
|
74
|
+
packageJson.devDependencies[CLI_PACKAGE] = '^1.0.0';
|
|
75
|
+
}
|
|
76
|
+
writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
77
|
+
}
|
|
78
|
+
function ensureReadme(rootDir, name) {
|
|
79
|
+
const readmePath = join(rootDir, 'README.md');
|
|
80
|
+
if (existsSync(readmePath))
|
|
81
|
+
return;
|
|
82
|
+
const readme = `# ${toTitleCase(name)}
|
|
83
|
+
|
|
84
|
+
Plugin marketplace repository powered by \`${CLI_PACKAGE}\`.
|
|
85
|
+
|
|
86
|
+
## Workflow
|
|
87
|
+
|
|
88
|
+
\`\`\`bash
|
|
89
|
+
npm install
|
|
90
|
+
npm run mp -- init my-plugin
|
|
91
|
+
npm run ci:local
|
|
92
|
+
npm run release:local
|
|
93
|
+
\`\`\`
|
|
94
|
+
`;
|
|
95
|
+
writeFileSync(readmePath, readme);
|
|
96
|
+
}
|
|
97
|
+
export function runInitRepo(name, options) {
|
|
98
|
+
const rootDir = options.root ? resolveInvocationPath(options.root) : resolveInvocationPath('.');
|
|
99
|
+
const marketplaceName = name || basename(rootDir);
|
|
100
|
+
const organization = options.organization || toTitleCase(marketplaceName);
|
|
101
|
+
const marketplacePath = join(rootDir, 'marketplace.yaml');
|
|
102
|
+
if (existsSync(marketplacePath)) {
|
|
103
|
+
console.error(chalk.red(`✗ marketplace.yaml 已存在: ${marketplacePath}`));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
console.log(chalk.blue(`⟳ 初始化 plugin marketplace 仓库: ${marketplaceName}`));
|
|
107
|
+
mkdirSync(rootDir, { recursive: true });
|
|
108
|
+
mkdirSync(join(rootDir, 'plugins'), { recursive: true });
|
|
109
|
+
writeFileSync(marketplacePath, marketplaceYaml(marketplaceName, organization));
|
|
110
|
+
ensurePackageJson(rootDir, marketplaceName);
|
|
111
|
+
ensureReadme(rootDir, marketplaceName);
|
|
112
|
+
console.log(chalk.green(`✓ 仓库初始化完成: ${rootDir}`));
|
|
113
|
+
console.log(chalk.gray(' → npm install'));
|
|
114
|
+
console.log(chalk.gray(' → npm run mp -- init my-plugin'));
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=init-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-repo.js","sourceRoot":"","sources":["../../src/commands/init-repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* init 命令 — 创建新插件骨架
|
|
3
|
+
*
|
|
4
|
+
* 用法:mp init <name> [--type skill|mcp|hook]
|
|
5
|
+
*/
|
|
6
|
+
import { chmodSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { resolveRepoContext } from '../utils/helpers.js';
|
|
10
|
+
const SKILL_TEMPLATE = `---
|
|
11
|
+
name: {{SKILL_NAME}}
|
|
12
|
+
description: "TODO: 填写 skill 描述"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# {{SKILL_NAME}}
|
|
16
|
+
|
|
17
|
+
## 触发条件
|
|
18
|
+
当用户需要 TODO 时,激活此 skill。
|
|
19
|
+
|
|
20
|
+
## 指令
|
|
21
|
+
|
|
22
|
+
1. 第一步
|
|
23
|
+
2. 第二步
|
|
24
|
+
3. 第三步
|
|
25
|
+
`;
|
|
26
|
+
const PLUGIN_YAML_TEMPLATE = (name, type) => {
|
|
27
|
+
const base = `name: ${name}
|
|
28
|
+
version: "0.1.0"
|
|
29
|
+
description: "TODO: 填写插件描述"
|
|
30
|
+
author:
|
|
31
|
+
name: "Mobile Team"
|
|
32
|
+
|
|
33
|
+
category: general
|
|
34
|
+
tags: []
|
|
35
|
+
|
|
36
|
+
components:`;
|
|
37
|
+
switch (type) {
|
|
38
|
+
case 'mcp':
|
|
39
|
+
return `${base}
|
|
40
|
+
skills:
|
|
41
|
+
- name: ${name}
|
|
42
|
+
path: skills/${name}
|
|
43
|
+
description: "TODO: 填写 skill 描述"
|
|
44
|
+
mcp:
|
|
45
|
+
- name: ${name}-server
|
|
46
|
+
command: npx
|
|
47
|
+
args: ["-y", "${name}-server"]
|
|
48
|
+
description: "TODO: 填写 MCP server 描述"
|
|
49
|
+
`;
|
|
50
|
+
case 'hook':
|
|
51
|
+
return `${base}
|
|
52
|
+
hooks:
|
|
53
|
+
- event: PreToolUse
|
|
54
|
+
pattern: "Write|Edit"
|
|
55
|
+
command: "./hooks/check.sh"
|
|
56
|
+
`;
|
|
57
|
+
default: // skill
|
|
58
|
+
return `${base}
|
|
59
|
+
skills:
|
|
60
|
+
- name: ${name}
|
|
61
|
+
path: skills/${name}
|
|
62
|
+
description: "TODO: 填写 skill 描述"
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
export function runInit(name, options) {
|
|
67
|
+
const context = resolveRepoContext(options.root);
|
|
68
|
+
const pluginDir = join(context.pluginsDir, name);
|
|
69
|
+
if (existsSync(pluginDir)) {
|
|
70
|
+
console.error(chalk.red(`✗ 插件目录已存在: plugins/${name}`));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const type = options.type || 'skill';
|
|
74
|
+
console.log(chalk.blue(`⟳ 创建插件: ${name} (类型: ${type})`));
|
|
75
|
+
// 创建目录结构
|
|
76
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
77
|
+
// plugin.yaml
|
|
78
|
+
writeFileSync(join(pluginDir, 'plugin.yaml'), PLUGIN_YAML_TEMPLATE(name, type));
|
|
79
|
+
// 根据类型创建组件
|
|
80
|
+
if (type === 'skill' || type === 'mcp') {
|
|
81
|
+
const skillDir = join(pluginDir, 'skills', name);
|
|
82
|
+
mkdirSync(skillDir, { recursive: true });
|
|
83
|
+
writeFileSync(join(skillDir, 'SKILL.md'), SKILL_TEMPLATE.replaceAll('{{SKILL_NAME}}', name));
|
|
84
|
+
}
|
|
85
|
+
if (type === 'hook') {
|
|
86
|
+
const hooksDir = join(pluginDir, 'hooks');
|
|
87
|
+
const hookPath = join(hooksDir, 'check.sh');
|
|
88
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
89
|
+
writeFileSync(hookPath, '#!/bin/bash\n# TODO: 实现 hook 逻辑\nexit 0\n');
|
|
90
|
+
chmodSync(hookPath, 0o755);
|
|
91
|
+
}
|
|
92
|
+
console.log(chalk.green(`✓ 插件创建成功: plugins/${name}/`));
|
|
93
|
+
console.log(chalk.gray(` → 编辑 plugin.yaml 完善配置`));
|
|
94
|
+
if (type === 'skill' || type === 'mcp') {
|
|
95
|
+
console.log(chalk.gray(` → 编辑 skills/${name}/SKILL.md 编写指令`));
|
|
96
|
+
}
|
|
97
|
+
if (type === 'hook') {
|
|
98
|
+
console.log(chalk.gray(` → 编辑 hooks/check.sh 实现逻辑`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release-local 命令 — 组装本地发布产物
|
|
3
|
+
*
|
|
4
|
+
* 该命令假定调用前已执行完整构建流程。
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { loadAllPlugins, loadMarketplaceMetadata, resolveRepoContext, } from '../utils/helpers.js';
|
|
11
|
+
function assertExists(path, label) {
|
|
12
|
+
if (!existsSync(path)) {
|
|
13
|
+
throw new Error(`${label} 不存在,请先运行 npm run ci:local`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function copyDirectory(from, to) {
|
|
17
|
+
cpSync(from, to, {
|
|
18
|
+
recursive: true,
|
|
19
|
+
filter: (src) => !src.endsWith('.DS_Store'),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function writeReleaseReadme(releaseDir, marketplaceName, pluginCount) {
|
|
23
|
+
const readme = `# ${marketplaceName} 本地发布包
|
|
24
|
+
|
|
25
|
+
本目录由 \`plugin-marketplace release-local\` 生成。
|
|
26
|
+
|
|
27
|
+
## 内容
|
|
28
|
+
|
|
29
|
+
- \`plugins/\`: 插件源文件以及生成后的 Claude Code / Codex manifest
|
|
30
|
+
- \`.claude-plugin/marketplace.json\`: Claude Code marketplace manifest
|
|
31
|
+
- \`.agents/plugins/marketplace.json\`: Codex repo marketplace manifest
|
|
32
|
+
- \`marketplace.json\`: 根目录兼容镜像
|
|
33
|
+
- \`marketplace.yaml\`: marketplace 元数据
|
|
34
|
+
- \`release-manifest.json\`: 本次发布包的元信息
|
|
35
|
+
|
|
36
|
+
## 插件数量
|
|
37
|
+
|
|
38
|
+
${pluginCount}
|
|
39
|
+
`;
|
|
40
|
+
writeFileSync(join(releaseDir, 'README.md'), readme);
|
|
41
|
+
}
|
|
42
|
+
function createArchive(distDir, marketplaceName) {
|
|
43
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
44
|
+
const safeName = marketplaceName.replace(/[^a-zA-Z0-9._-]+/g, '-');
|
|
45
|
+
const archiveName = `${safeName}-release-${stamp}.tar.gz`;
|
|
46
|
+
const archivePath = join(distDir, archiveName);
|
|
47
|
+
const result = spawnSync('tar', ['-czf', archivePath, '-C', distDir, 'release'], {
|
|
48
|
+
stdio: 'inherit',
|
|
49
|
+
});
|
|
50
|
+
if (result.status !== 0) {
|
|
51
|
+
throw new Error(`发布包压缩失败: ${archiveName}`);
|
|
52
|
+
}
|
|
53
|
+
return archivePath;
|
|
54
|
+
}
|
|
55
|
+
export function runReleaseLocal(options = {}) {
|
|
56
|
+
console.log(chalk.blue('⟳ 组装本地发布包...\n'));
|
|
57
|
+
const context = resolveRepoContext(options.root);
|
|
58
|
+
const marketplace = loadMarketplaceMetadata(context);
|
|
59
|
+
const plugins = loadAllPlugins(context);
|
|
60
|
+
const releaseDir = join(context.distDir, 'release');
|
|
61
|
+
assertExists(join(context.rootDir, '.claude-plugin/marketplace.json'), 'Claude marketplace manifest');
|
|
62
|
+
assertExists(join(context.rootDir, '.agents/plugins/marketplace.json'), 'Codex marketplace manifest');
|
|
63
|
+
assertExists(join(context.rootDir, 'marketplace.json'), 'marketplace.json');
|
|
64
|
+
assertExists(context.marketplaceYamlPath, 'marketplace.yaml');
|
|
65
|
+
assertExists(join(context.pluginsDir, 'CATALOG.md'), 'plugins/CATALOG.md');
|
|
66
|
+
for (const { name, dir, config } of plugins) {
|
|
67
|
+
assertExists(join(dir, '.claude-plugin/plugin.json'), `${name} Claude manifest`);
|
|
68
|
+
assertExists(join(dir, '.codex-plugin/plugin.json'), `${name} Codex manifest`);
|
|
69
|
+
if (config.components.mcp?.length) {
|
|
70
|
+
assertExists(join(dir, '.mcp.json'), `${name} MCP 配置`);
|
|
71
|
+
}
|
|
72
|
+
if (config.components.hooks?.length) {
|
|
73
|
+
assertExists(join(dir, 'hooks/hooks.json'), `${name} hooks 配置`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
rmSync(releaseDir, { recursive: true, force: true });
|
|
77
|
+
mkdirSync(releaseDir, { recursive: true });
|
|
78
|
+
copyDirectory(context.pluginsDir, join(releaseDir, 'plugins'));
|
|
79
|
+
copyDirectory(join(context.rootDir, '.claude-plugin'), join(releaseDir, '.claude-plugin'));
|
|
80
|
+
copyDirectory(join(context.rootDir, '.agents'), join(releaseDir, '.agents'));
|
|
81
|
+
cpSync(join(context.rootDir, 'marketplace.json'), join(releaseDir, 'marketplace.json'));
|
|
82
|
+
cpSync(context.marketplaceYamlPath, join(releaseDir, 'marketplace.yaml'));
|
|
83
|
+
const manifest = {
|
|
84
|
+
generatedAt: new Date().toISOString(),
|
|
85
|
+
pluginCount: plugins.length,
|
|
86
|
+
plugins: plugins.map(({ name, config }) => ({
|
|
87
|
+
name,
|
|
88
|
+
version: config.version,
|
|
89
|
+
category: config.category || 'general',
|
|
90
|
+
})),
|
|
91
|
+
artifacts: {
|
|
92
|
+
plugins: 'plugins/',
|
|
93
|
+
claudeMarketplace: '.claude-plugin/marketplace.json',
|
|
94
|
+
codexMarketplace: '.agents/plugins/marketplace.json',
|
|
95
|
+
marketplaceMirror: 'marketplace.json',
|
|
96
|
+
marketplaceMetadata: 'marketplace.yaml',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
writeFileSync(join(releaseDir, 'release-manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
100
|
+
writeReleaseReadme(releaseDir, marketplace.name, plugins.length);
|
|
101
|
+
const archivePath = createArchive(context.distDir, marketplace.name);
|
|
102
|
+
console.log(chalk.green(` ✓ dist/release (${plugins.length} 个插件)`));
|
|
103
|
+
console.log(chalk.green(` ✓ ${archivePath}`));
|
|
104
|
+
console.log(chalk.green('\n✓ 本地发布包生成完成'));
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=release-local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-local.js","sourceRoot":"","sources":["../../src/commands/release-local.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate 命令 — 验证插件格式
|
|
3
|
+
*
|
|
4
|
+
* 用法:mp validate [name] 或 mp validate --all
|
|
5
|
+
*/
|
|
6
|
+
import { accessSync, constants, existsSync, statSync } from 'node:fs';
|
|
7
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { loadPluginYaml, listPluginNames, resolveRepoContext, } from '../utils/helpers.js';
|
|
10
|
+
function isInsidePlugin(pluginDir, targetPath) {
|
|
11
|
+
const rel = relative(pluginDir, targetPath);
|
|
12
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
13
|
+
}
|
|
14
|
+
function resolvePluginPath(pluginDir, configuredPath, label, errors) {
|
|
15
|
+
if (isAbsolute(configuredPath)) {
|
|
16
|
+
errors.push(`${label} 不应使用绝对路径: ${configuredPath}`);
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const resolvedPath = resolve(pluginDir, configuredPath);
|
|
20
|
+
if (!isInsidePlugin(pluginDir, resolvedPath)) {
|
|
21
|
+
errors.push(`${label} 越过插件目录: ${configuredPath}`);
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return resolvedPath;
|
|
25
|
+
}
|
|
26
|
+
function looksLikePluginLocalPath(value) {
|
|
27
|
+
return (value.startsWith('./') ||
|
|
28
|
+
value.startsWith('../') ||
|
|
29
|
+
value.startsWith('/') ||
|
|
30
|
+
value.startsWith('hooks/') ||
|
|
31
|
+
value.startsWith('mcp/') ||
|
|
32
|
+
value.startsWith('skills/'));
|
|
33
|
+
}
|
|
34
|
+
function validateExecutable(filePath, displayPath, errors) {
|
|
35
|
+
try {
|
|
36
|
+
const stat = statSync(filePath);
|
|
37
|
+
if (!stat.isFile()) {
|
|
38
|
+
errors.push(`Hook 脚本不是文件: ${displayPath}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
accessSync(filePath, constants.X_OK);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
errors.push(`Hook 脚本不可执行: ${displayPath}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function validateOne(context, name) {
|
|
48
|
+
const pluginDir = join(context.pluginsDir, name);
|
|
49
|
+
const errors = [];
|
|
50
|
+
// 1. 目录存在
|
|
51
|
+
if (!existsSync(pluginDir)) {
|
|
52
|
+
return { name, valid: false, errors: [`插件目录不存在: plugins/${name}`] };
|
|
53
|
+
}
|
|
54
|
+
// 2. plugin.yaml 存在且格式正确
|
|
55
|
+
try {
|
|
56
|
+
const config = loadPluginYaml(pluginDir);
|
|
57
|
+
// 3. 验证引用的 skill 目录存在
|
|
58
|
+
if (config.components.skills) {
|
|
59
|
+
for (const skill of config.components.skills) {
|
|
60
|
+
const skillDir = resolvePluginPath(pluginDir, skill.path, 'Skill 路径', errors);
|
|
61
|
+
if (!skillDir)
|
|
62
|
+
continue;
|
|
63
|
+
if (!existsSync(skillDir)) {
|
|
64
|
+
errors.push(`Skill 目录不存在: ${skill.path}`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!statSync(skillDir).isDirectory()) {
|
|
68
|
+
errors.push(`Skill 路径不是目录: ${skill.path}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const skillMd = join(skillDir, 'SKILL.md');
|
|
72
|
+
if (!existsSync(skillMd)) {
|
|
73
|
+
errors.push(`SKILL.md 不存在: ${skill.path}/SKILL.md`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 4. 验证 hook 脚本存在
|
|
78
|
+
if (config.components.hooks) {
|
|
79
|
+
for (const hook of config.components.hooks) {
|
|
80
|
+
const scriptPath = resolvePluginPath(pluginDir, hook.command, 'Hook 脚本路径', errors);
|
|
81
|
+
if (!scriptPath)
|
|
82
|
+
continue;
|
|
83
|
+
if (!existsSync(scriptPath)) {
|
|
84
|
+
errors.push(`Hook 脚本不存在: ${hook.command}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
validateExecutable(scriptPath, hook.command, errors);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// 5. 验证 MCP 本地文件引用存在且不越过插件目录
|
|
91
|
+
if (config.components.mcp) {
|
|
92
|
+
for (const mcp of config.components.mcp) {
|
|
93
|
+
if (looksLikePluginLocalPath(mcp.command)) {
|
|
94
|
+
const commandPath = resolvePluginPath(pluginDir, mcp.command, 'MCP command 路径', errors);
|
|
95
|
+
if (commandPath && !existsSync(commandPath)) {
|
|
96
|
+
errors.push(`MCP command 文件不存在: ${mcp.command}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const arg of mcp.args || []) {
|
|
100
|
+
if (!looksLikePluginLocalPath(arg))
|
|
101
|
+
continue;
|
|
102
|
+
const argPath = resolvePluginPath(pluginDir, arg, 'MCP args 路径', errors);
|
|
103
|
+
if (argPath && !existsSync(argPath)) {
|
|
104
|
+
errors.push(`MCP args 引用的文件不存在: ${arg}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// 6. 名称一致性检查
|
|
110
|
+
if (config.name !== name) {
|
|
111
|
+
errors.push(`plugin.yaml 中的 name (${config.name}) 与目录名 (${name}) 不一致`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
errors.push(err.message);
|
|
116
|
+
}
|
|
117
|
+
return { name, valid: errors.length === 0, errors };
|
|
118
|
+
}
|
|
119
|
+
export function runValidate(name, options) {
|
|
120
|
+
const context = resolveRepoContext(options.root);
|
|
121
|
+
const names = options.all ? listPluginNames(context) : name ? [name] : [];
|
|
122
|
+
if (names.length === 0) {
|
|
123
|
+
console.error(chalk.red('✗ 请指定插件名或使用 --all'));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
console.log(chalk.blue(`⟳ 验证 ${names.length} 个插件...\n`));
|
|
127
|
+
let allValid = true;
|
|
128
|
+
for (const n of names) {
|
|
129
|
+
const result = validateOne(context, n);
|
|
130
|
+
if (result.valid) {
|
|
131
|
+
console.log(chalk.green(` ✓ ${n}`));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
allValid = false;
|
|
135
|
+
console.log(chalk.red(` ✗ ${n}`));
|
|
136
|
+
for (const err of result.errors) {
|
|
137
|
+
console.log(chalk.red(` └─ ${err}`));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
console.log();
|
|
142
|
+
if (allValid) {
|
|
143
|
+
console.log(chalk.green(`✓ 所有插件验证通过`));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.error(chalk.red(`✗ 存在验证错误`));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugin.yaml JSON Schema 定义
|
|
3
|
+
*
|
|
4
|
+
* 这是 marketplace 的核心数据模型。所有 plugin.yaml 都必须符合此 schema。
|
|
5
|
+
* 生成双平台 manifest 时,也基于此 schema 做字段映射。
|
|
6
|
+
*/
|
|
7
|
+
import type { JSONSchemaType } from 'ajv';
|
|
8
|
+
export interface PluginSkill {
|
|
9
|
+
name: string;
|
|
10
|
+
path: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
'argument-hint'?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PluginMcp {
|
|
15
|
+
name: string;
|
|
16
|
+
command: string;
|
|
17
|
+
args?: string[];
|
|
18
|
+
env?: Record<string, string>;
|
|
19
|
+
description?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PluginHook {
|
|
22
|
+
event: string;
|
|
23
|
+
pattern?: string;
|
|
24
|
+
command: string;
|
|
25
|
+
timeout?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface PluginAuthor {
|
|
28
|
+
name: string;
|
|
29
|
+
email?: string;
|
|
30
|
+
url?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface CodexInterface {
|
|
33
|
+
displayName?: string;
|
|
34
|
+
shortDescription?: string;
|
|
35
|
+
longDescription?: string;
|
|
36
|
+
category?: string;
|
|
37
|
+
developerName?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface PlatformOverrides {
|
|
40
|
+
codex?: {
|
|
41
|
+
interface?: CodexInterface;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface PluginComponents {
|
|
45
|
+
skills?: PluginSkill[];
|
|
46
|
+
mcp?: PluginMcp[];
|
|
47
|
+
hooks?: PluginHook[];
|
|
48
|
+
}
|
|
49
|
+
export interface PluginYaml {
|
|
50
|
+
name: string;
|
|
51
|
+
version: string;
|
|
52
|
+
description: string;
|
|
53
|
+
author: PluginAuthor;
|
|
54
|
+
category?: string;
|
|
55
|
+
tags?: string[];
|
|
56
|
+
components: PluginComponents;
|
|
57
|
+
platform?: PlatformOverrides;
|
|
58
|
+
}
|
|
59
|
+
export declare const pluginYamlSchema: JSONSchemaType<PluginYaml>;
|