mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-18 17:16:46 +03:00
Add build-time count validation, fix all stale count references
Build system now computes authoritative counts from source (22 commands, 24 detection rules) and warns about mismatches in HTML, README, plugin configs. Generates public/js/generated/counts.js for frontend use. Fixed 15 stale references across index.html, cheatsheet.html, README.md, NOTICE.md, AGENTS.md, plugin.json, and marketplace.json. Changed all "20 commands" to "22", "25 rules" to "24". Fixed v1.6.0 changelog date (was March 24, after v2.0.0's March 20; now March 18). Changelog entries are excluded from validation since historical counts were correct at time of release. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
99bc9d48ad
commit
0d4d6b4f56
@@ -22,6 +22,86 @@ import { createTransformer, PROVIDERS } from './lib/transformers/index.js';
|
||||
import { createAllZips } from './lib/zip.js';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
/**
|
||||
* Generate authoritative counts from source data and write to public/js/generated/counts.js.
|
||||
* Also validates that key HTML files reference the correct numbers.
|
||||
*/
|
||||
function generateCounts(rootDir, skills, buildDir) {
|
||||
// Count active (non-deprecated) user-invocable commands
|
||||
const activeCommands = skills.filter(s => {
|
||||
if (!s.userInvocable) return false;
|
||||
const content = fs.readFileSync(s.filePath, 'utf-8');
|
||||
return !content.includes('DEPRECATED');
|
||||
});
|
||||
const commandCount = activeCommands.length;
|
||||
|
||||
// Count detection rules from source
|
||||
const detectorSrc = fs.readFileSync(
|
||||
path.join(rootDir, 'source/skills/critique/scripts/detect-antipatterns.mjs'), 'utf-8'
|
||||
);
|
||||
const ruleIds = new Set();
|
||||
for (const match of detectorSrc.matchAll(/^\s+id: '([^']+)'/gm)) {
|
||||
ruleIds.add(match[1]);
|
||||
}
|
||||
const detectionCount = ruleIds.size;
|
||||
|
||||
// Write generated counts module
|
||||
const genDir = path.join(rootDir, 'public/js/generated');
|
||||
fs.mkdirSync(genDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(genDir, 'counts.js'),
|
||||
`// GENERATED by build.js — do not edit\n` +
|
||||
`export const COMMAND_COUNT = ${commandCount};\n` +
|
||||
`export const DETECTION_COUNT = ${detectionCount};\n`
|
||||
);
|
||||
|
||||
// Validate counts in key files
|
||||
const filesToCheck = [
|
||||
'public/index.html',
|
||||
'public/cheatsheet.html',
|
||||
'README.md',
|
||||
'NOTICE.md',
|
||||
'AGENTS.md',
|
||||
'.claude-plugin/plugin.json',
|
||||
'.claude-plugin/marketplace.json',
|
||||
];
|
||||
|
||||
let warnings = 0;
|
||||
for (const relPath of filesToCheck) {
|
||||
const absPath = path.join(rootDir, relPath);
|
||||
if (!fs.existsSync(absPath)) continue;
|
||||
const content = fs.readFileSync(absPath, 'utf-8');
|
||||
|
||||
// Check for stale command counts (look for "N commands" or "N skills" patterns)
|
||||
// Strip changelog list content to avoid flagging historical counts
|
||||
const strippedContent = content.replace(/<ul class="changelog-items">[\s\S]*?<\/ul>/g, '');
|
||||
const countPattern = /\b(\d+)\s+(design\s+)?(commands|skills|steering commands)/gi;
|
||||
for (const match of strippedContent.matchAll(countPattern)) {
|
||||
const num = parseInt(match[1]);
|
||||
// Allow 1 (for "1 skill") and the correct count
|
||||
if (num !== commandCount && num !== 1) {
|
||||
console.warn(` ⚠️ ${relPath}: found "${match[0]}" but active command count is ${commandCount}`);
|
||||
warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for stale detection counts
|
||||
const detectPattern = /\b(\d+)\s+(deterministic\s+)?(checks|patterns|rules|detections)/gi;
|
||||
for (const match of content.matchAll(detectPattern)) {
|
||||
const num = parseInt(match[1]);
|
||||
if (num !== detectionCount && num > 10) { // ignore small numbers like "3 patterns"
|
||||
console.warn(` ⚠️ ${relPath}: found "${match[0]}" but detection count is ${detectionCount}`);
|
||||
warnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings > 0) {
|
||||
console.warn(`\n⚠️ ${warnings} stale count reference(s) found. Update them to match source of truth.`);
|
||||
}
|
||||
|
||||
console.log(`✓ Generated counts: ${commandCount} commands, ${detectionCount} detection rules`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy directory recursively
|
||||
*/
|
||||
@@ -361,6 +441,9 @@ async function build() {
|
||||
console.log(`📋 Synced skills to: ${syncConfigs.map(p => p.configDir).join(', ')}`);
|
||||
|
||||
|
||||
// Generate authoritative counts and validate references
|
||||
generateCounts(ROOT_DIR, skills, buildDir);
|
||||
|
||||
// Generate browser anti-pattern detector (after skill sync so it doesn't get overwritten)
|
||||
try {
|
||||
execSync('node scripts/build-browser-detector.js', { cwd: ROOT_DIR, stdio: 'inherit' });
|
||||
|
||||
Reference in New Issue
Block a user