mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-11 15:17:24 +08:00
fix: sync skill frontmatter and catalog counts
Adds missing skill frontmatter, normalizes strict YAML metadata, syncs README catalog counts, and extends catalog validation for README/plugin/marketplace count drift.
This commit is contained in:
@@ -448,7 +448,7 @@ everything-claude-code/
|
||||
| |-- plugin.json # Plugin metadata and component paths
|
||||
| |-- marketplace.json # Marketplace catalog for /plugin marketplace add
|
||||
|
|
||||
|-- agents/ # 36 specialized subagents for delegation
|
||||
|-- agents/ # 48 specialized subagents for delegation
|
||||
| |-- planner.md # Feature implementation planning
|
||||
| |-- architect.md # System design decisions
|
||||
| |-- tdd-guide.md # Test-driven development
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
name: skill-stocktake
|
||||
description: "用于审计Claude技能和命令的质量。支持快速扫描(仅变更技能)和全面盘点模式,采用顺序子代理批量评估。"
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
@@ -21,6 +21,8 @@ const AGENTS_PATH = path.join(ROOT, 'AGENTS.md');
|
||||
const README_ZH_CN_PATH = path.join(ROOT, 'README.zh-CN.md');
|
||||
const DOCS_ZH_CN_README_PATH = path.join(ROOT, 'docs', 'zh-CN', 'README.md');
|
||||
const DOCS_ZH_CN_AGENTS_PATH = path.join(ROOT, 'docs', 'zh-CN', 'AGENTS.md');
|
||||
const PLUGIN_JSON_PATH = path.join(ROOT, '.claude-plugin', 'plugin.json');
|
||||
const MARKETPLACE_JSON_PATH = path.join(ROOT, '.claude-plugin', 'marketplace.json');
|
||||
const WRITE_MODE = process.argv.includes('--write');
|
||||
|
||||
const OUTPUT_MODE = process.argv.includes('--md')
|
||||
@@ -99,6 +101,18 @@ function parseReadmeExpectations(readmeContent) {
|
||||
{ category: 'commands', mode: 'exact', expected: Number(quickStartMatch[3]), source: 'README.md quick-start summary' }
|
||||
);
|
||||
|
||||
const projectTreeAgentsMatch = readmeContent.match(/^\|\s*--\s*agents\/\s*#\s*(\d+)\s+specialized subagents for delegation\s*$/im);
|
||||
if (!projectTreeAgentsMatch) {
|
||||
throw new Error('README.md project tree is missing the agents count');
|
||||
}
|
||||
|
||||
expectations.push({
|
||||
category: 'agents',
|
||||
mode: 'exact',
|
||||
expected: Number(projectTreeAgentsMatch[1]),
|
||||
source: 'README.md project tree (agents)'
|
||||
});
|
||||
|
||||
const tablePatterns = [
|
||||
{ category: 'agents', regex: /\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+agents\s*\|/i, source: 'README.md comparison table' },
|
||||
{ category: 'commands', regex: /\|\s*(?:\*\*)?Commands(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+commands\s*\|/i, source: 'README.md comparison table' },
|
||||
@@ -346,6 +360,31 @@ function parseZhAgentsDocExpectations(agentsContent) {
|
||||
return expectations;
|
||||
}
|
||||
|
||||
function parseCatalogDescriptionExpectations(content, source, getDescription) {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(content);
|
||||
} catch (error) {
|
||||
throw new Error(`${source} is not valid JSON: ${error.message}`);
|
||||
}
|
||||
|
||||
const description = getDescription(parsed);
|
||||
if (typeof description !== 'string') {
|
||||
throw new Error(`${source} is missing the catalog count description`);
|
||||
}
|
||||
|
||||
const match = description.match(/(\d+)\s+agents,\s+(\d+)\s+skills,\s+(\d+)\s+legacy command shims?/i);
|
||||
if (!match) {
|
||||
throw new Error(`${source} is missing the catalog count description`);
|
||||
}
|
||||
|
||||
return [
|
||||
{ category: 'agents', mode: 'exact', expected: Number(match[1]), source },
|
||||
{ category: 'skills', mode: 'exact', expected: Number(match[2]), source },
|
||||
{ category: 'commands', mode: 'exact', expected: Number(match[3]), source },
|
||||
];
|
||||
}
|
||||
|
||||
function evaluateExpectations(catalog, expectations) {
|
||||
return expectations.map(expectation => {
|
||||
const actual = catalog[expectation.category].count;
|
||||
@@ -376,6 +415,12 @@ function syncEnglishReadme(content, catalog) {
|
||||
`${prefix}${catalog.agents.count}${agentsSuffix}${catalog.skills.count}${skillsSuffix}${catalog.commands.count} legacy command shims`,
|
||||
'README.md quick-start summary'
|
||||
);
|
||||
nextContent = replaceOrThrow(
|
||||
nextContent,
|
||||
/^(\|\s*--\s*agents\/\s*#\s*)(\d+)(\s+specialized subagents for delegation\s*)$/im,
|
||||
(_, prefix, __, suffix) => `${prefix}${catalog.agents.count}${suffix}`,
|
||||
'README.md project tree (agents)'
|
||||
);
|
||||
nextContent = replaceOrThrow(
|
||||
nextContent,
|
||||
/(\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?)(\d+)(\s+agents\s*\|)/i,
|
||||
@@ -540,6 +585,31 @@ function syncZhAgents(content, catalog) {
|
||||
return nextContent;
|
||||
}
|
||||
|
||||
function syncCatalogDescription(content, catalog, source, getDescription, setDescription) {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(content);
|
||||
} catch (error) {
|
||||
throw new Error(`${source} is not valid JSON: ${error.message}`);
|
||||
}
|
||||
|
||||
const description = getDescription(parsed);
|
||||
if (typeof description !== 'string') {
|
||||
throw new Error(`${source} is missing the catalog count description`);
|
||||
}
|
||||
|
||||
const nextDescription = replaceOrThrow(
|
||||
description,
|
||||
/(\d+)(\s+agents,\s+)(\d+)(\s+skills,\s+)(\d+)(\s+legacy command shims?)/i,
|
||||
(_, __, agentsSuffix, ___, skillsSuffix, ____, commandsSuffix) =>
|
||||
`${catalog.agents.count}${agentsSuffix}${catalog.skills.count}${skillsSuffix}${catalog.commands.count}${commandsSuffix}`,
|
||||
source
|
||||
);
|
||||
|
||||
setDescription(parsed, nextDescription);
|
||||
return `${JSON.stringify(parsed, null, 2)}\n`;
|
||||
}
|
||||
|
||||
function createDocumentSpecs(paths = {}) {
|
||||
const {
|
||||
readmePath = README_PATH,
|
||||
@@ -547,6 +617,8 @@ function createDocumentSpecs(paths = {}) {
|
||||
zhRootReadmePath = README_ZH_CN_PATH,
|
||||
zhDocsReadmePath = DOCS_ZH_CN_README_PATH,
|
||||
zhDocsAgentsPath = DOCS_ZH_CN_AGENTS_PATH,
|
||||
pluginJsonPath = PLUGIN_JSON_PATH,
|
||||
marketplaceJsonPath = MARKETPLACE_JSON_PATH,
|
||||
} = paths;
|
||||
|
||||
return [
|
||||
@@ -575,6 +647,36 @@ function createDocumentSpecs(paths = {}) {
|
||||
parseExpectations: parseZhAgentsDocExpectations,
|
||||
syncContent: syncZhAgents,
|
||||
},
|
||||
{
|
||||
filePath: pluginJsonPath,
|
||||
parseExpectations: content => parseCatalogDescriptionExpectations(
|
||||
content,
|
||||
'.claude-plugin/plugin.json description',
|
||||
parsed => parsed.description
|
||||
),
|
||||
syncContent: (content, catalog) => syncCatalogDescription(
|
||||
content,
|
||||
catalog,
|
||||
'.claude-plugin/plugin.json description',
|
||||
parsed => parsed.description,
|
||||
(parsed, description) => { parsed.description = description; }
|
||||
),
|
||||
},
|
||||
{
|
||||
filePath: marketplaceJsonPath,
|
||||
parseExpectations: content => parseCatalogDescriptionExpectations(
|
||||
content,
|
||||
'.claude-plugin/marketplace.json plugin description',
|
||||
parsed => parsed.plugins?.[0]?.description
|
||||
),
|
||||
syncContent: (content, catalog) => syncCatalogDescription(
|
||||
content,
|
||||
catalog,
|
||||
'.claude-plugin/marketplace.json plugin description',
|
||||
parsed => parsed.plugins?.[0]?.description,
|
||||
(parsed, description) => { parsed.plugins[0].description = description; }
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -585,6 +687,8 @@ function createDocumentSpecsForRoot(root) {
|
||||
zhRootReadmePath: path.join(root, 'README.zh-CN.md'),
|
||||
zhDocsReadmePath: path.join(root, 'docs', 'zh-CN', 'README.md'),
|
||||
zhDocsAgentsPath: path.join(root, 'docs', 'zh-CN', 'AGENTS.md'),
|
||||
pluginJsonPath: path.join(root, '.claude-plugin', 'plugin.json'),
|
||||
marketplaceJsonPath: path.join(root, '.claude-plugin', 'marketplace.json'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -689,11 +793,13 @@ module.exports = {
|
||||
formatExpectation,
|
||||
main,
|
||||
parseAgentsDocExpectations,
|
||||
parseCatalogDescriptionExpectations,
|
||||
parseReadmeExpectations,
|
||||
parseZhAgentsDocExpectations,
|
||||
parseZhDocsReadmeExpectations,
|
||||
parseZhRootReadmeExpectations,
|
||||
runCatalogCheck,
|
||||
syncCatalogDescription,
|
||||
syncEnglishAgents,
|
||||
syncEnglishReadme,
|
||||
syncZhAgents,
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
---
|
||||
name: openclaw-persona-forge
|
||||
description: |-
|
||||
为 OpenClaw AI Agent 锻造完整的龙虾灵魂方案。根据用户偏好或随机抽卡,
|
||||
输出身份定位、灵魂描述(SOUL.md)、角色化底线规则、名字和头像生图提示词。
|
||||
如当前环境提供已审核的生图 skill,可自动生成统一风格头像图片。
|
||||
当用户需要创建、设计或定制 OpenClaw 龙虾灵魂时使用。
|
||||
不适用于:微调已有 SOUL.md、非 OpenClaw 平台的角色设计、纯工具型无性格 Agent。
|
||||
触发词:龙虾灵魂、虾魂、OpenClaw 灵魂、养虾灵魂、龙虾角色、龙虾定位、
|
||||
龙虾剧本杀角色、龙虾游戏角色、龙虾 NPC、龙虾性格、龙虾背景故事、
|
||||
lobster soul、lobster character、抽卡、随机龙虾、龙虾 SOUL、gacha。
|
||||
description: "为 OpenClaw AI Agent 锻造完整的龙虾灵魂方案。根据用户偏好或随机抽卡, 输出身份定位、灵魂描述(SOUL.md)、角色化底线规则、名字和头像生图提示词。 如当前环境提供已审核的生图 skill,可自动生成统一风格头像图片。 当用户需要创建、设计或定制 OpenClaw 龙虾灵魂时使用。 不适用于:微调已有 SOUL.md、非 OpenClaw 平台的角色设计、纯工具型无性格 Agent。 触发词:龙虾灵魂、虾魂、OpenClaw 灵魂、养虾灵魂、龙虾角色、龙虾定位、 龙虾剧本杀角色、龙虾游戏角色、龙虾 NPC、龙虾性格、龙虾背景故事、 lobster soul、lobster character、抽卡、随机龙虾、龙虾 SOUL、gacha。"
|
||||
origin: community
|
||||
---
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
name: skill-stocktake
|
||||
description: "Use when auditing Claude skills and commands for quality. Supports Quick Scan (changed skills only) and Full Stocktake modes with sequential subagent batch evaluation."
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
@@ -44,6 +44,7 @@ function writeEnglishReadme(root, counts, options = {}) {
|
||||
const unrelatedSkillsCount = options.unrelatedSkillsCount || 16;
|
||||
|
||||
fs.writeFileSync(path.join(root, 'README.md'), `Access to ${counts.agents} agents, ${counts.skills} skills, and ${counts.commands} commands.
|
||||
|-- agents/ # ${counts.agents} specialized subagents for delegation
|
||||
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Agents | PASS: ${tableCounts.agents} agents |
|
||||
@@ -64,6 +65,22 @@ function writeEnglishReadme(root, counts, options = {}) {
|
||||
`);
|
||||
}
|
||||
|
||||
function writePluginMetadata(root, counts) {
|
||||
const pluginDir = path.join(root, '.claude-plugin');
|
||||
fs.mkdirSync(pluginDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(path.join(pluginDir, 'plugin.json'), JSON.stringify({
|
||||
name: 'ecc',
|
||||
description: `Fixture plugin — ${counts.agents} agents, ${counts.skills} skills, ${counts.commands} legacy command shims`,
|
||||
}, null, 2));
|
||||
fs.writeFileSync(path.join(pluginDir, 'marketplace.json'), JSON.stringify({
|
||||
plugins: [{
|
||||
name: 'ecc',
|
||||
description: `Fixture marketplace plugin — ${counts.agents} agents, ${counts.skills} skills, ${counts.commands} legacy command shims`,
|
||||
}],
|
||||
}, null, 2));
|
||||
}
|
||||
|
||||
function writeEnglishAgents(root, counts, options = {}) {
|
||||
const plus = options.skillsMinimum ? '+' : '';
|
||||
|
||||
@@ -143,6 +160,7 @@ function writeCatalogFixture(root, options = {}) {
|
||||
writeZhRootReadme(root, documentedCounts);
|
||||
writeZhDocsReadme(root, documentedCounts, { unrelatedSkillsCount });
|
||||
writeZhAgents(root, documentedCounts, { skillsMinimum });
|
||||
writePluginMetadata(root, documentedCounts);
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
@@ -203,7 +221,10 @@ function runTests() {
|
||||
.join('\n');
|
||||
|
||||
assert.ok(formatted.includes('README.md quick-start summary'));
|
||||
assert.ok(formatted.includes('README.md project tree'));
|
||||
assert.ok(formatted.includes('AGENTS.md summary'));
|
||||
assert.ok(formatted.includes('.claude-plugin/plugin.json description'));
|
||||
assert.ok(formatted.includes('.claude-plugin/marketplace.json plugin description'));
|
||||
assert.ok(formatted.includes('README.zh-CN.md quick-start summary'));
|
||||
assert.ok(formatted.includes('docs/zh-CN/README.md parity table'));
|
||||
assert.ok(formatted.includes('docs/zh-CN/AGENTS.md project structure'));
|
||||
@@ -230,14 +251,19 @@ function runTests() {
|
||||
const agentsDoc = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf8');
|
||||
const zhReadme = fs.readFileSync(path.join(testDir, 'docs', 'zh-CN', 'README.md'), 'utf8');
|
||||
const zhAgentsDoc = fs.readFileSync(path.join(testDir, 'docs', 'zh-CN', 'AGENTS.md'), 'utf8');
|
||||
const pluginJson = fs.readFileSync(path.join(testDir, '.claude-plugin', 'plugin.json'), 'utf8');
|
||||
const marketplaceJson = fs.readFileSync(path.join(testDir, '.claude-plugin', 'marketplace.json'), 'utf8');
|
||||
|
||||
assert.ok(readme.includes('Access to 1 agents, 1 skills, and 1 legacy command shims'));
|
||||
assert.ok(readme.includes('|-- agents/ # 1 specialized subagents for delegation'));
|
||||
assert.ok(readme.includes('| Skills | 42 | .agents/skills/ |'));
|
||||
assert.ok(agentsDoc.includes('providing 1 specialized agents, 1+ skills, 1 commands'));
|
||||
assert.ok(agentsDoc.includes('skills/ - 1+ workflow skills and domain knowledge'));
|
||||
assert.ok(zhReadme.includes('| 技能 | 42 | .agents/skills/ |'));
|
||||
assert.ok(zhAgentsDoc.includes('提供 1 个专业代理、1+ 项技能、1 条命令'));
|
||||
assert.ok(zhAgentsDoc.includes('skills/ - 1+ 个工作流技能和领域知识'));
|
||||
assert.ok(pluginJson.includes('1 agents, 1 skills, 1 legacy command shims'));
|
||||
assert.ok(marketplaceJson.includes('1 agents, 1 skills, 1 legacy command shims'));
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
|
||||
@@ -169,6 +169,8 @@ function runCatalogValidator(overrides = {}) {
|
||||
README_ZH_CN_PATH: path.join(repoRoot, 'README.zh-CN.md'),
|
||||
DOCS_ZH_CN_README_PATH: path.join(repoRoot, 'docs', 'zh-CN', 'README.md'),
|
||||
DOCS_ZH_CN_AGENTS_PATH: path.join(repoRoot, 'docs', 'zh-CN', 'AGENTS.md'),
|
||||
PLUGIN_JSON_PATH: path.join(repoRoot, '.claude-plugin', 'plugin.json'),
|
||||
MARKETPLACE_JSON_PATH: path.join(repoRoot, '.claude-plugin', 'marketplace.json'),
|
||||
...overrides,
|
||||
};
|
||||
|
||||
@@ -225,6 +227,7 @@ function runSkillsValidator(testDir, argv = [], envOverrides = {}) {
|
||||
function writeCatalogFixture(testDir, options = {}) {
|
||||
const {
|
||||
readmeCounts = { agents: 1, skills: 1, commands: 1 },
|
||||
readmeProjectTreeAgents = readmeCounts.agents,
|
||||
readmeTableCounts = readmeCounts,
|
||||
readmeParityCounts = readmeCounts,
|
||||
readmeUnrelatedSkillsCount = 16,
|
||||
@@ -245,6 +248,8 @@ function writeCatalogFixture(testDir, options = {}) {
|
||||
'skills/ — 1 个工作流技能和领域知识',
|
||||
'commands/ — 1 个斜杠命令',
|
||||
],
|
||||
pluginCounts = { agents: 1, skills: 1, commands: 1 },
|
||||
marketplaceCounts = { agents: 1, skills: 1, commands: 1 },
|
||||
} = options;
|
||||
|
||||
const readmePath = path.join(testDir, 'README.md');
|
||||
@@ -252,23 +257,36 @@ function writeCatalogFixture(testDir, options = {}) {
|
||||
const zhRootReadmePath = path.join(testDir, 'README.zh-CN.md');
|
||||
const zhDocsReadmePath = path.join(testDir, 'docs', 'zh-CN', 'README.md');
|
||||
const zhAgentsPath = path.join(testDir, 'docs', 'zh-CN', 'AGENTS.md');
|
||||
const pluginJsonPath = path.join(testDir, '.claude-plugin', 'plugin.json');
|
||||
const marketplaceJsonPath = path.join(testDir, '.claude-plugin', 'marketplace.json');
|
||||
|
||||
fs.mkdirSync(path.join(testDir, 'agents'), { recursive: true });
|
||||
fs.mkdirSync(path.join(testDir, 'commands'), { recursive: true });
|
||||
fs.mkdirSync(path.join(testDir, 'skills', 'demo-skill'), { recursive: true });
|
||||
fs.mkdirSync(path.join(testDir, 'docs', 'zh-CN'), { recursive: true });
|
||||
fs.mkdirSync(path.join(testDir, '.claude-plugin'), { recursive: true });
|
||||
|
||||
fs.writeFileSync(path.join(testDir, 'agents', 'planner.md'), '---\nmodel: sonnet\ntools: Read\n---\n# Planner');
|
||||
fs.writeFileSync(path.join(testDir, 'commands', 'plan.md'), '---\ndescription: Plan\n---\n# Plan');
|
||||
fs.writeFileSync(path.join(testDir, 'skills', 'demo-skill', 'SKILL.md'), '---\nname: demo-skill\ndescription: Demo skill\norigin: ECC\n---\n# Demo Skill');
|
||||
|
||||
fs.writeFileSync(readmePath, `Access to ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} commands.\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| Agents | PASS: ${readmeTableCounts.agents} agents | Shared | Shared | 1 |\n| Commands | PASS: ${readmeTableCounts.commands} commands | Shared | Shared | 1 |\n| Skills | PASS: ${readmeTableCounts.skills} skills | Shared | Shared | 1 |\n\n| Feature | Count | Format |\n|-----------|-------|---------|\n| Skills | ${readmeUnrelatedSkillsCount} | .agents/skills/ |\n\n## Cross-Tool Feature Parity\n\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| **Agents** | ${readmeParityCounts.agents} | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |\n| **Commands** | ${readmeParityCounts.commands} | Shared | Instruction-based | 31 |\n| **Skills** | ${readmeParityCounts.skills} | Shared | 10 (native format) | 37 |\n`);
|
||||
fs.writeFileSync(readmePath, `Access to ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} commands.\n|-- agents/ # ${readmeProjectTreeAgents} specialized subagents for delegation\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| Agents | PASS: ${readmeTableCounts.agents} agents | Shared | Shared | 1 |\n| Commands | PASS: ${readmeTableCounts.commands} commands | Shared | Shared | 1 |\n| Skills | PASS: ${readmeTableCounts.skills} skills | Shared | Shared | 1 |\n\n| Feature | Count | Format |\n|-----------|-------|---------|\n| Skills | ${readmeUnrelatedSkillsCount} | .agents/skills/ |\n\n## Cross-Tool Feature Parity\n\n| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| **Agents** | ${readmeParityCounts.agents} | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |\n| **Commands** | ${readmeParityCounts.commands} | Shared | Instruction-based | 31 |\n| **Skills** | ${readmeParityCounts.skills} | Shared | 10 (native format) | 37 |\n`);
|
||||
fs.writeFileSync(agentsPath, `This is a **production-ready AI coding plugin** providing ${summaryCounts.agents} specialized agents, ${summaryCounts.skills} skills, ${summaryCounts.commands} commands, and automated hook workflows for software development.\n\n\`\`\`\n${structureLines.join('\n')}\n\`\`\`\n`);
|
||||
fs.writeFileSync(zhRootReadmePath, `**完成!** 你现在可以使用 ${zhRootReadmeCounts.agents} 个代理、${zhRootReadmeCounts.skills} 个技能和 ${zhRootReadmeCounts.commands} 个命令。\n`);
|
||||
fs.writeFileSync(zhDocsReadmePath, `**搞定!** 你现在可以使用 ${zhDocsReadmeCounts.agents} 个智能体、${zhDocsReadmeCounts.skills} 项技能和 ${zhDocsReadmeCounts.commands} 个命令了。\n| 功能特性 | Claude Code | OpenCode | 状态 |\n|---------|-------------|----------|--------|\n| 智能体 | \u2705 ${zhDocsTableCounts.agents} 个 | \u2705 12 个 | **Claude Code 领先** |\n| 命令 | \u2705 ${zhDocsTableCounts.commands} 个 | \u2705 31 个 | **Claude Code 领先** |\n| 技能 | \u2705 ${zhDocsTableCounts.skills} 项 | \u2705 37 项 | **Claude Code 领先** |\n\n| 功能特性 | 数量 | 格式 |\n|-----------|-------|---------|\n| 技能 | ${zhDocsUnrelatedSkillsCount} | .agents/skills/ |\n\n## 跨工具功能对等\n\n| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |\n|---------|------------|------------|-----------|----------|\n| **智能体** | ${zhDocsParityCounts.agents} | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |\n| **命令** | ${zhDocsParityCounts.commands} | 共享 | 基于指令 | 31 |\n| **技能** | ${zhDocsParityCounts.skills} | 共享 | 10 (原生格式) | 37 |\n`);
|
||||
fs.writeFileSync(zhAgentsPath, `这是一个**生产就绪的 AI 编码插件**,提供 ${zhAgentsSummaryCounts.agents} 个专业代理、${zhAgentsSummaryCounts.skills} 项技能、${zhAgentsSummaryCounts.commands} 条命令以及自动化钩子工作流,用于软件开发。\n\n\`\`\`\n${zhAgentsStructureLines.join('\n')}\n\`\`\`\n`);
|
||||
fs.writeFileSync(pluginJsonPath, JSON.stringify({
|
||||
name: 'ecc',
|
||||
description: `Battle-tested plugin — ${pluginCounts.agents} agents, ${pluginCounts.skills} skills, ${pluginCounts.commands} legacy command shims`,
|
||||
}, null, 2));
|
||||
fs.writeFileSync(marketplaceJsonPath, JSON.stringify({
|
||||
plugins: [{
|
||||
name: 'ecc',
|
||||
description: `Marketplace plugin — ${marketplaceCounts.agents} agents, ${marketplaceCounts.skills} skills, ${marketplaceCounts.commands} legacy command shims`,
|
||||
}],
|
||||
}, null, 2));
|
||||
|
||||
return { readmePath, agentsPath, zhRootReadmePath, zhDocsReadmePath, zhAgentsPath };
|
||||
return { readmePath, agentsPath, zhRootReadmePath, zhDocsReadmePath, zhAgentsPath, pluginJsonPath, marketplaceJsonPath };
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
@@ -417,6 +435,8 @@ function runTests() {
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
zhAgentsPath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir, {
|
||||
readmeCounts: { agents: 99, skills: 99, commands: 99 },
|
||||
readmeTableCounts: { agents: 99, skills: 99, commands: 99 },
|
||||
@@ -446,6 +466,8 @@ function runTests() {
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: zhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 1, 'Should fail when catalog counts drift');
|
||||
@@ -461,6 +483,8 @@ function runTests() {
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
zhAgentsPath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir, {
|
||||
readmeCounts: { agents: 1, skills: 1, commands: 1 },
|
||||
readmeTableCounts: { agents: 1, skills: 1, commands: 1 },
|
||||
@@ -475,6 +499,8 @@ function runTests() {
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: zhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 1, 'Should fail when README parity table drifts');
|
||||
@@ -492,6 +518,8 @@ function runTests() {
|
||||
agentsPath,
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir);
|
||||
const missingZhAgentsPath = path.join(testDir, 'docs', 'zh-CN', 'AGENTS.md');
|
||||
fs.rmSync(missingZhAgentsPath);
|
||||
@@ -503,6 +531,8 @@ function runTests() {
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: missingZhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 1, 'Should fail when a tracked doc is missing');
|
||||
@@ -521,6 +551,8 @@ function runTests() {
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
zhAgentsPath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir, {
|
||||
readmeCounts: { agents: 9, skills: 9, commands: 9 },
|
||||
readmeTableCounts: { agents: 8, skills: 8, commands: 8 },
|
||||
@@ -531,6 +563,8 @@ function runTests() {
|
||||
zhDocsTableCounts: { agents: 12, skills: 12, commands: 12 },
|
||||
zhDocsParityCounts: { agents: 13, skills: 13, commands: 13 },
|
||||
zhAgentsSummaryCounts: { agents: 14, skills: 14, commands: 14 },
|
||||
pluginCounts: { agents: 18, skills: 18, commands: 18 },
|
||||
marketplaceCounts: { agents: 19, skills: 19, commands: 19 },
|
||||
zhAgentsStructureLines: [
|
||||
'agents/ — 15 个专业子代理',
|
||||
'skills/ — 16 个工作流技能和领域知识',
|
||||
@@ -546,6 +580,8 @@ function runTests() {
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: zhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 0, `Should sync and pass, got stderr: ${result.stderr}`);
|
||||
@@ -555,8 +591,11 @@ function runTests() {
|
||||
const zhRootReadme = fs.readFileSync(zhRootReadmePath, 'utf8');
|
||||
const zhDocsReadme = fs.readFileSync(zhDocsReadmePath, 'utf8');
|
||||
const zhAgentsDoc = fs.readFileSync(zhAgentsPath, 'utf8');
|
||||
const pluginJson = fs.readFileSync(pluginJsonPath, 'utf8');
|
||||
const marketplaceJson = fs.readFileSync(marketplaceJsonPath, 'utf8');
|
||||
|
||||
assert.ok(readme.includes('Access to 1 agents, 1 skills, and 1 legacy command shims'), 'Should sync README quick-start summary');
|
||||
assert.ok(readme.includes('|-- agents/ # 1 specialized subagents for delegation'), 'Should sync README project tree agents count');
|
||||
assert.ok(readme.includes('| Agents | PASS: 1 agents |'), 'Should sync README comparison table');
|
||||
assert.ok(readme.includes('| Skills | 16 | .agents/skills/ |'), 'Should not rewrite unrelated README tables');
|
||||
assert.ok(readme.includes('| **Agents** | 1 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |'), 'Should sync README parity table');
|
||||
@@ -569,6 +608,8 @@ function runTests() {
|
||||
assert.ok(zhDocsReadme.includes('| **智能体** | 1 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |'), 'Should sync docs/zh-CN/README parity table');
|
||||
assert.ok(zhAgentsDoc.includes('提供 1 个专业代理、1 项技能、1 条命令'), 'Should sync docs/zh-CN/AGENTS summary');
|
||||
assert.ok(zhAgentsDoc.includes('commands/ — 1 个斜杠命令'), 'Should sync docs/zh-CN/AGENTS structure');
|
||||
assert.ok(pluginJson.includes('1 agents, 1 skills, 1 legacy command shims'), 'Should sync plugin manifest catalog description');
|
||||
assert.ok(marketplaceJson.includes('1 agents, 1 skills, 1 legacy command shims'), 'Should sync marketplace plugin catalog description');
|
||||
|
||||
cleanupTestDir(testDir);
|
||||
})) passed++; else failed++;
|
||||
@@ -581,6 +622,8 @@ function runTests() {
|
||||
zhRootReadmePath,
|
||||
zhDocsReadmePath,
|
||||
zhAgentsPath,
|
||||
pluginJsonPath,
|
||||
marketplaceJsonPath,
|
||||
} = writeCatalogFixture(testDir, {
|
||||
structureLines: [
|
||||
' agents/ - 1 specialized subagents ',
|
||||
@@ -601,6 +644,8 @@ function runTests() {
|
||||
README_ZH_CN_PATH: zhRootReadmePath,
|
||||
DOCS_ZH_CN_README_PATH: zhDocsReadmePath,
|
||||
DOCS_ZH_CN_AGENTS_PATH: zhAgentsPath,
|
||||
PLUGIN_JSON_PATH: pluginJsonPath,
|
||||
MARKETPLACE_JSON_PATH: marketplaceJsonPath,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 0, `Should accept formatting variations, got stderr: ${result.stderr}`);
|
||||
|
||||
Reference in New Issue
Block a user