fix(ci): validate cross-harness catalog counts

This commit is contained in:
dajiaohuang
2026-09-10 18:48:12 +08:00
parent cc91c24f9a
commit 214c6a1e6f
4 changed files with 65 additions and 2 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ This file provides Gemini CLI with the baseline ECC workflow, review standards,
## Overview
Everything Claude Code (ECC) is a cross-harness coding system with 36 specialized agents, 142 skills, and 68 commands.
Everything Claude Code (ECC) is a cross-harness coding system with 68 specialized agents, 286 skills, and 94 commands.
Gemini support is currently focused on a strong project-local instruction layer via `.gemini/GEMINI.md`, plus the shared MCP catalog and package-manager setup assets shipped by the installer.
+1 -1
View File
@@ -1,7 +1,7 @@
# Soul
## Core Identity
Everything Claude Code (ECC) is a production-ready AI coding plugin with 30 specialized agents, 135 skills, 60 commands, and automated hook workflows for software development.
Everything Claude Code (ECC) is a production-ready AI coding plugin with 68 specialized agents, 286 skills, 94 commands, and automated hook workflows for software development.
## Core Principles
1. **Agent-First** — route work to the right specialist as early as possible.
+42
View File
@@ -18,6 +18,8 @@ const path = require('path');
const ROOT = path.join(__dirname, '../..');
const README_PATH = path.join(ROOT, 'README.md');
const AGENTS_PATH = path.join(ROOT, 'AGENTS.md');
const SOUL_PATH = path.join(ROOT, 'SOUL.md');
const GEMINI_PATH = path.join(ROOT, '.gemini', 'GEMINI.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');
@@ -273,6 +275,30 @@ function parseAgentsDocExpectations(agentsContent) {
return expectations;
}
function parseCrossHarnessIdentityExpectations(content, source) {
const match = content.match(/with\s+(\d+)\s+specialized agents,\s+(\d+)\s+skills,\s+(?:and\s+)?(\d+)\s+commands/i);
if (!match) {
throw new Error(`${source} is missing the catalog summary line`);
}
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 syncCrossHarnessIdentity(content, catalog, source) {
return replaceOrThrow(
content,
/(with\s+)(\d+)(\s+specialized agents,\s+)(\d+)(\s+skills,\s+(?:and\s+)?)(\d+)(\s+commands)/i,
(_, prefix, __, agentsSuffix, ___, skillsSuffix, ____, commandsSuffix) => (
`${prefix}${catalog.agents.count}${agentsSuffix}${catalog.skills.count}${skillsSuffix}${catalog.commands.count}${commandsSuffix}`
),
source
);
}
function parseZhAgentsDocExpectations(agentsContent) {
const summaryMatch = agentsContent.match(/提供\s+(\d+)\s+个专业代理、\s*(\d+)(\+)?\s*项技能、\s*(\d+)\s+条命令/i);
if (!summaryMatch) {
@@ -563,6 +589,8 @@ function createDocumentSpecs(paths = {}) {
const {
readmePath = README_PATH,
agentsPath = AGENTS_PATH,
soulPath = SOUL_PATH,
geminiPath = GEMINI_PATH,
zhRootReadmePath = README_ZH_CN_PATH,
zhDocsReadmePath = DOCS_ZH_CN_README_PATH,
zhDocsAgentsPath = DOCS_ZH_CN_AGENTS_PATH,
@@ -581,6 +609,16 @@ function createDocumentSpecs(paths = {}) {
parseExpectations: parseAgentsDocExpectations,
syncContent: syncEnglishAgents,
},
{
filePath: soulPath,
parseExpectations: content => parseCrossHarnessIdentityExpectations(content, 'SOUL.md'),
syncContent: (content, catalog) => syncCrossHarnessIdentity(content, catalog, 'SOUL.md'),
},
{
filePath: geminiPath,
parseExpectations: content => parseCrossHarnessIdentityExpectations(content, '.gemini/GEMINI.md'),
syncContent: (content, catalog) => syncCrossHarnessIdentity(content, catalog, '.gemini/GEMINI.md'),
},
{
filePath: zhRootReadmePath,
parseExpectations: parseZhRootReadmeExpectations,
@@ -633,6 +671,8 @@ function createDocumentSpecsForRoot(root) {
return createDocumentSpecs({
readmePath: path.join(root, 'README.md'),
agentsPath: path.join(root, 'AGENTS.md'),
soulPath: path.join(root, 'SOUL.md'),
geminiPath: path.join(root, '.gemini', 'GEMINI.md'),
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'),
@@ -742,6 +782,7 @@ module.exports = {
formatExpectation,
main,
parseAgentsDocExpectations,
parseCrossHarnessIdentityExpectations,
parseCatalogDescriptionExpectations,
parseReadmeExpectations,
parseZhAgentsDocExpectations,
@@ -751,6 +792,7 @@ module.exports = {
syncCatalogDescription,
syncEnglishAgents,
syncEnglishReadme,
syncCrossHarnessIdentity,
syncZhAgents,
syncZhDocsReadme,
syncZhRootReadme,
+21
View File
@@ -95,6 +95,20 @@ commands/ - ${counts.commands} slash commands
`);
}
function writeCrossHarnessIdentityDocs(root, counts) {
fs.writeFileSync(
path.join(root, 'SOUL.md'),
`Everything Claude Code (ECC) is a production-ready AI coding plugin with ${counts.agents} specialized agents, ${counts.skills} skills, ${counts.commands} commands, and automated hook workflows.\n`
);
const geminiDir = path.join(root, '.gemini');
fs.mkdirSync(geminiDir, { recursive: true });
fs.writeFileSync(
path.join(geminiDir, 'GEMINI.md'),
`Everything Claude Code (ECC) is a cross-harness coding system with ${counts.agents} specialized agents, ${counts.skills} skills, and ${counts.commands} commands.\n`
);
}
function writeZhRootReadme(root, counts) {
fs.writeFileSync(path.join(root, 'README.zh-CN.md'), `你现在可以使用 ${counts.agents} 个代理、${counts.skills} 个技能和 ${counts.commands} 个命令。\n`);
}
@@ -158,6 +172,7 @@ function writeCatalogFixture(root, options = {}) {
writeEnglishReadme(root, documentedCounts, { unrelatedSkillsCount });
writeEnglishAgents(root, documentedCounts, { skillsMinimum });
writeCrossHarnessIdentityDocs(root, documentedCounts);
writeZhRootReadme(root, documentedCounts);
writeZhDocsReadme(root, documentedCounts, { unrelatedSkillsCount });
writeZhAgents(root, documentedCounts, { skillsMinimum });
@@ -224,6 +239,8 @@ function runTests() {
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('SOUL.md'));
assert.ok(formatted.includes('.gemini/GEMINI.md'));
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'));
@@ -250,6 +267,8 @@ function runTests() {
const readme = fs.readFileSync(path.join(testDir, 'README.md'), 'utf8');
const agentsDoc = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf8');
const soulDoc = fs.readFileSync(path.join(testDir, 'SOUL.md'), 'utf8');
const geminiDoc = fs.readFileSync(path.join(testDir, '.gemini', 'GEMINI.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');
@@ -261,6 +280,8 @@ function runTests() {
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(soulDoc.includes('with 1 specialized agents, 1 skills, 1 commands'));
assert.ok(geminiDoc.includes('with 1 specialized agents, 1 skills, and 1 commands'));
assert.ok(zhReadme.includes('| 技能 | 42 | .agents/skills/ |'));
assert.ok(zhAgentsDoc.includes('提供 1 个专业代理、1+ 项技能、1 条命令'));
assert.ok(zhAgentsDoc.includes('skills/ - 1+ 个工作流技能和领域知识'));