[codex] Add design-aware detector rules (#252)

* Add design-aware detector rules

* Fix design-aware detector noise

* Unify CLI and hook detector ignores

* Fix remaining design-system review findings

* Add detector ignore CLI

* Fix design detector review findings

* Fix design color source false positives

* Fix core test suite registration

* Add design-aware detector docs

* Fix font priority design-system parsing

* Fix color ignore value matching
This commit is contained in:
Paul Bakaus
2026-06-15 21:06:17 -07:00
committed by GitHub
parent c27a75ad41
commit 51d01e3a5f
80 changed files with 5372 additions and 527 deletions
+31 -7
View File
@@ -1,9 +1,15 @@
import fs from 'node:fs';
import path from 'node:path';
import { loadDesignSystemForCwd } from '../design-system.mjs';
import { createBrowserDetector, detectUrl } from '../engines/browser/detect-url.mjs';
import { detectHtml } from '../engines/static-html/detect-html.mjs';
import { detectText } from '../engines/regex/detect-text.mjs';
import {
filterDetectionFindings,
readDetectionConfig,
shouldIgnoreDetectionFile,
} from '../../lib/impeccable-config.mjs';
import {
HTML_EXTENSIONS,
buildImportGraph,
@@ -79,10 +85,17 @@ function printUsage() {
Scan files or URLs for UI anti-patterns and design quality issues.
Options:
--json Output results as JSON
--gpt Also report GPT-specific provider tells (off by default)
--gemini Also report Gemini-specific provider tells (off by default)
--help Show this help message
--json Output results as JSON
--gpt Also report GPT-specific provider tells (off by default)
--gemini Also report Gemini-specific provider tells (off by default)
--no-config Do not apply project config, detector ignores, or DESIGN.md
--no-design-system Do not load local DESIGN.md / .impeccable/design.json context
--help Show this help message
Project config:
Respects .impeccable/config.json and .impeccable/config.local.json detector
settings: detector.ignoreRules, detector.ignoreFiles, detector.ignoreValues,
and detector.designSystem.enabled.
Detection modes:
HTML files Static HTML/CSS analysis (default, catches linked CSS)
@@ -93,7 +106,8 @@ Examples:
impeccable detect src/
impeccable detect index.html
impeccable detect https://example.com
impeccable detect --json .`);
impeccable detect --json .
impeccable detect --no-config src/`);
}
async function detectCli() {
@@ -114,10 +128,16 @@ async function detectCli() {
'Note: --fast is deprecated and ignored. The full scan is fast now and runs every rule.\n',
);
}
const configEnabled = !args.includes('--no-config');
const detectionConfig = configEnabled
? readDetectionConfig(process.cwd())
: { ignoreRules: [], ignoreFiles: [], ignoreValues: [] };
const providers = [];
if (args.includes('--gpt')) providers.push('gpt');
if (args.includes('--gemini')) providers.push('gemini');
const scanOptions = { providers };
const designSystemEnabled = configEnabled && !args.includes('--no-design-system') && detectionConfig.designSystem?.enabled !== false;
const designSystem = designSystemEnabled ? loadDesignSystemForCwd(process.cwd()) : null;
const scanOptions = designSystem ? { providers, designSystem } : { providers };
const targets = args.filter(a => !a.startsWith('--'));
if (helpMode) { printUsage(); process.exit(0); }
@@ -175,7 +195,8 @@ async function detectCli() {
}
}
const files = walkDir(resolved);
const files = walkDir(resolved)
.filter(file => !shouldIgnoreDetectionFile(file, process.cwd(), detectionConfig));
const htmlCount = files.filter(f => HTML_EXTENSIONS.has(path.extname(f).toLowerCase())).length;
// Warn and confirm if scanning many files (static HTML/CSS processes each HTML file)
@@ -219,6 +240,7 @@ async function detectCli() {
allFindings.push(...fileFindings);
}
} else if (stat.isFile()) {
if (shouldIgnoreDetectionFile(resolved, process.cwd(), detectionConfig)) continue;
const ext = path.extname(resolved).toLowerCase();
if (HTML_EXTENSIONS.has(ext)) {
allFindings.push(...await detectHtml(resolved, scanOptions));
@@ -232,6 +254,8 @@ async function detectCli() {
}
}
allFindings = filterDetectionFindings(allFindings, detectionConfig);
if (allFindings.length > 0) {
if (jsonMode) process.stdout.write(formatFindings(allFindings, true) + '\n');
else process.stderr.write(formatFindings(allFindings, false) + '\n');