mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-15 17:02:12 +08:00
feat: add command registry and coverage checks (#1906)
Salvages the useful parts of #1897 without generated .caliber state or stale counts. - adds a deterministic command registry generator and drift check - commits the current command registry for 75 commands - validates the rc.1 README catalog summary against live counts - adds a single Ubuntu Node 20 coverage job instead of running coverage in every matrix cell Co-authored-by: jodunk <jodunk@users.noreply.github.com>
This commit is contained in:
@@ -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.
|
||||
- **Public surface synced to the live repo** - metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: ${counts.agents} agents, ${counts.skills} skills, and ${counts.commands} legacy command shims.
|
||||
|-- agents/ # ${counts.agents} specialized subagents for delegation
|
||||
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@@ -221,6 +222,7 @@ function runTests() {
|
||||
.join('\n');
|
||||
|
||||
assert.ok(formatted.includes('README.md quick-start summary'));
|
||||
assert.ok(formatted.includes('README.md rc.1 release-note 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'));
|
||||
@@ -255,6 +257,7 @@ function runTests() {
|
||||
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('actual OSS surface: 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'));
|
||||
|
||||
176
tests/ci/command-registry.test.js
Normal file
176
tests/ci/command-registry.test.js
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Direct coverage for scripts/ci/generate-command-registry.js.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const {
|
||||
checkRegistry,
|
||||
formatRegistry,
|
||||
generateRegistry,
|
||||
parseArgs,
|
||||
run,
|
||||
writeRegistry,
|
||||
} = require('../../scripts/ci/generate-command-registry');
|
||||
|
||||
function createTestDir() {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-command-registry-'));
|
||||
}
|
||||
|
||||
function cleanupTestDir(testDir) {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function writeFixture(root) {
|
||||
fs.mkdirSync(path.join(root, 'commands'), { recursive: true });
|
||||
fs.mkdirSync(path.join(root, 'agents'), { recursive: true });
|
||||
fs.mkdirSync(path.join(root, 'skills', 'tdd-workflow'), { recursive: true });
|
||||
fs.mkdirSync(path.join(root, 'skills', 'security-review'), { recursive: true });
|
||||
|
||||
fs.writeFileSync(path.join(root, 'agents', 'code-reviewer.md'), '---\nmodel: sonnet\ntools: Read\n---\n');
|
||||
fs.writeFileSync(path.join(root, 'agents', 'test-writer.md'), '---\nmodel: sonnet\ntools: Read\n---\n');
|
||||
fs.writeFileSync(path.join(root, 'skills', 'tdd-workflow', 'SKILL.md'), '# TDD workflow\n');
|
||||
fs.writeFileSync(path.join(root, 'skills', 'security-review', 'SKILL.md'), '# Security review\n');
|
||||
|
||||
fs.writeFileSync(path.join(root, 'commands', 'review.md'), `---
|
||||
description: Review changes
|
||||
---
|
||||
# Review
|
||||
|
||||
Use @code-reviewer and skill: security-review.
|
||||
`);
|
||||
|
||||
fs.writeFileSync(path.join(root, 'commands', 'tdd.md'), `---
|
||||
description: "Write tests first"
|
||||
---
|
||||
# TDD
|
||||
|
||||
Call subagent_type: test-writer and skills/tdd-workflow/SKILL.md.
|
||||
`);
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` PASS ${name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` FAIL ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing command registry generation ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
if (test('generates deterministic command metadata and usage statistics', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeFixture(testDir);
|
||||
|
||||
const registry = generateRegistry({ root: testDir });
|
||||
|
||||
assert.strictEqual(registry.schemaVersion, 1);
|
||||
assert.strictEqual(registry.totalCommands, 2);
|
||||
assert.deepStrictEqual(
|
||||
registry.commands.map(command => command.command),
|
||||
['review', 'tdd']
|
||||
);
|
||||
assert.deepStrictEqual(registry.commands[0].allAgents, ['code-reviewer']);
|
||||
assert.deepStrictEqual(registry.commands[0].skills, ['security-review']);
|
||||
assert.deepStrictEqual(registry.commands[1].allAgents, ['test-writer']);
|
||||
assert.deepStrictEqual(registry.commands[1].skills, ['tdd-workflow']);
|
||||
assert.deepStrictEqual(registry.statistics.byType, { review: 1, testing: 1 });
|
||||
assert.deepStrictEqual(registry.statistics.topAgents[0], { agent: 'code-reviewer', count: 1 });
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('write and check modes use stable JSON without timestamps', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeFixture(testDir);
|
||||
const outputPath = path.join(testDir, 'docs', 'COMMAND-REGISTRY.json');
|
||||
const registry = generateRegistry({ root: testDir });
|
||||
|
||||
writeRegistry(registry, outputPath);
|
||||
const firstWrite = fs.readFileSync(outputPath, 'utf8');
|
||||
writeRegistry(registry, outputPath);
|
||||
const secondWrite = fs.readFileSync(outputPath, 'utf8');
|
||||
|
||||
assert.strictEqual(firstWrite, secondWrite);
|
||||
assert.ok(!firstWrite.includes('generated'));
|
||||
assert.doesNotThrow(() => checkRegistry(registry, outputPath));
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('check mode fails when the registry file is stale', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeFixture(testDir);
|
||||
const outputPath = path.join(testDir, 'docs', 'COMMAND-REGISTRY.json');
|
||||
const registry = generateRegistry({ root: testDir });
|
||||
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, `${formatRegistry(registry).trimEnd()}\n \n`);
|
||||
|
||||
assert.throws(
|
||||
() => checkRegistry(registry, outputPath),
|
||||
/out of date/
|
||||
);
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('CLI reports unknown arguments and supports check output', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeFixture(testDir);
|
||||
const outputPath = path.join(testDir, 'docs', 'COMMAND-REGISTRY.json');
|
||||
const registry = generateRegistry({ root: testDir });
|
||||
writeRegistry(registry, outputPath);
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
const streams = {
|
||||
stdout: { write: chunk => { stdout += chunk; } },
|
||||
stderr: { write: chunk => { stderr += chunk; } },
|
||||
};
|
||||
|
||||
assert.deepStrictEqual(parseArgs(['--json', '--write']), {
|
||||
json: true,
|
||||
write: true,
|
||||
check: false,
|
||||
});
|
||||
assert.strictEqual(run(['--check'], { root: testDir, outputPath, ...streams }), 0);
|
||||
assert.ok(stdout.includes('up to date'));
|
||||
assert.strictEqual(stderr, '');
|
||||
|
||||
stdout = '';
|
||||
stderr = '';
|
||||
assert.strictEqual(run(['--bogus'], { root: testDir, outputPath, ...streams }), 1);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.ok(stderr.includes('Unknown argument'));
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
@@ -270,7 +270,7 @@ function writeCatalogFixture(testDir, options = {}) {
|
||||
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|-- 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(readmePath, `Access to ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} commands.\n- **Public surface synced to the live repo** - metadata, catalog counts, plugin manifests, and install-facing docs now match the actual OSS surface: ${readmeCounts.agents} agents, ${readmeCounts.skills} skills, and ${readmeCounts.commands} legacy command shims.\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`);
|
||||
@@ -595,6 +595,7 @@ function runTests() {
|
||||
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('actual OSS surface: 1 agents, 1 skills, and 1 legacy command shims'), 'Should sync README release-note 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');
|
||||
|
||||
Reference in New Issue
Block a user