Merge PR #3428: validate and sync every catalog project tree entry

This commit is contained in:
haelyra
2026-10-10 17:37:24 -04:00
4 changed files with 205 additions and 16 deletions
+51 -11
View File
@@ -87,6 +87,23 @@ function replaceOrThrow(content, regex, replacer, source) {
return content.replace(regex, replacer);
}
function countMarkers(content, regex) {
return [...content.matchAll(regex)].length;
}
function replaceAllOrThrow(content, regex, replacer, source, expectedCount) {
const found = countMarkers(content, regex);
if (found === 0) {
throw new Error(`${source} is missing the expected catalog marker`);
}
if (expectedCount !== undefined && found !== expectedCount) {
throw new Error(`${source} expected ${expectedCount} marker(s), found ${found}`);
}
return content.replace(regex, replacer);
}
function parseReadmeExpectations(readmeContent) {
const expectations = [];
@@ -103,17 +120,28 @@ function parseReadmeExpectations(readmeContent) {
{ category: 'commands', mode: 'exact', expected: Number(quickStartMatch[3]), source: 'README.md quick-start summary' }
);
const projectTreeAgentsMatches = [...readmeContent.matchAll(/^\|\s*--\s*agents\/\s*#\s*(\d+)\s+specialized subagents for delegation\s*$/gim)];
if (!projectTreeAgentsMatches.length) {
throw new Error('README.md project tree is missing the agents count');
}
const projectTreePatterns = [
{ category: 'agents', regex: /^\|\s*--\s*agents\/\s*#\s*(\d+)\s+specialized subagents for delegation\s*$/gim, source: 'README.md project tree (agents)' },
{ category: 'commands', regex: /^\|\s*--\s*commands\/\s*#\s*(\d+)\s+maintained slash-command shims\s*$/gim, source: 'README.md project tree (commands)' },
{ category: 'skills', regex: /^\|\s*--\s*skills\/\s*#\s*(\d+)\s+reusable workflows loaded on demand\s*$/gim, source: 'README.md project tree (skills)' }
];
for (const [index, match] of projectTreeAgentsMatches.entries()) expectations.push({
category: 'agents',
mode: 'exact',
expected: Number(match[1]),
source: `README.md project tree ${index + 1} (agents)`
});
for (const pattern of projectTreePatterns) {
const matches = [...readmeContent.matchAll(pattern.regex)];
if (matches.length === 0) {
throw new Error(`${pattern.source} is missing the ${pattern.category} count`);
}
for (const match of matches) {
expectations.push({
category: pattern.category,
mode: 'exact',
expected: Number(match[1]),
source: `${pattern.source} block ${readmeContent.slice(0, match.index).split('\n').length}`
});
}
}
const tablePatterns = [
{ category: 'agents', regex: /\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?(\d+)\s+agents\s*\|/i, source: 'README.md comparison table' },
@@ -409,12 +437,24 @@ 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 = replaceAllOrThrow(
nextContent,
/^(\|\s*--\s*agents\/\s*#\s*)(\d+)(\s+specialized subagents for delegation\s*)$/gim,
(_, prefix, __, suffix) => `${prefix}${catalog.agents.count}${suffix}`,
'README.md project tree (agents)'
);
nextContent = replaceAllOrThrow(
nextContent,
/^(\|\s*--\s*commands\/\s*#\s*)(\d+)(\s+maintained slash-command shims\s*)$/gim,
(_, prefix, __, suffix) => `${prefix}${catalog.commands.count}${suffix}`,
'README.md project tree (commands)'
);
nextContent = replaceAllOrThrow(
nextContent,
/^(\|\s*--\s*skills\/\s*#\s*)(\d+)(\s+reusable workflows loaded on demand\s*)$/gim,
(_, prefix, __, suffix) => `${prefix}${catalog.skills.count}${suffix}`,
'README.md project tree (skills)'
);
nextContent = replaceOrThrow(
nextContent,
/(\|\s*(?:\*\*)?Agents(?:\*\*)?\s*\|\s*(?:(?:PASS:|\u2705)\s*)?)(\d+)(\s+agents\s*\|)/i,