From 8d0e9de26dd46e546fe55c25394014d297f1bddc Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Mon, 30 Mar 2026 12:17:59 -0700 Subject: [PATCH] Prepare CLI for npm: v2.0.1, Node compat, npm-specific README - Rename bin/impeccable.mjs to bin/impeccable (npm rejects .mjs in bin) - Shebang: #!/usr/bin/env node (works without Bun) - Add README.npm.md with CLI-focused docs, swapped in during publish - Build browser script to source/ dir so URL scanning works in npm pkg - Include browser script in files field - Move website-only deps (archiver, motion, playwright) to devDependencies - jsdom as dependency, puppeteer as optionalDependency - Bump version to 2.0.1 across package.json, plugin.json, marketplace.json Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .gitignore | 1 + CLAUDE.md | 6 +-- README.npm.md | 67 ++++++++++++++++++++++++++++++ bin/{impeccable.mjs => impeccable} | 14 +++++-- package.json | 45 ++++++++++++++------ scripts/build-browser-detector.js | 3 ++ 8 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 README.npm.md rename bin/{impeccable.mjs => impeccable} (72%) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0e7a2822f..1faf3e741 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,7 +12,7 @@ { "name": "impeccable", "description": "Design vocabulary and skills for frontend development. Includes 20 commands (/polish, /distill, /audit, /typeset, /overdrive, etc.) and an enhanced frontend-design skill with curated anti-patterns.", - "version": "2.0.0", + "version": "2.0.1", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index eddc378d8..7bceb8c98 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "impeccable", "description": "Design vocabulary and skills for frontend development. Includes 21 skills (20 user-invocable: /polish, /distill, /audit, /typeset, /overdrive, etc.) and an enhanced frontend-design skill with curated anti-patterns.", - "version": "2.0.0", + "version": "2.0.1", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/.gitignore b/.gitignore index 850a2eb2c..588f02736 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ build/ # Build artifacts *.log +source/skills/critique/scripts/detect-antipatterns-browser.js # OS files .DS_Store diff --git a/CLAUDE.md b/CLAUDE.md index 55547ade8..c262aabe8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,9 +55,9 @@ Unit tests (build, detector logic) run via `bun test`. Fixture tests (jsdom-base Impeccable includes a CLI for running anti-pattern detection outside of AI harnesses: ```bash -bun bin/impeccable.mjs detect [file-or-dir-or-url...] # detect anti-patterns -bun bin/impeccable.mjs detect --fast --json src/ # regex-only, JSON output -bun bin/impeccable.mjs --help # show help +bun bin/impeccable detect [file-or-dir-or-url...] # detect anti-patterns +bun bin/impeccable detect --fast --json src/ # regex-only, JSON output +bun bin/impeccable --help # show help ``` The detection script is at `source/skills/critique/scripts/detect-antipatterns.mjs`. It auto-detects browser vs Node and works as both: diff --git a/README.npm.md b/README.npm.md new file mode 100644 index 000000000..5bdef8817 --- /dev/null +++ b/README.npm.md @@ -0,0 +1,67 @@ +# Impeccable CLI + +Detect UI anti-patterns and design quality issues from the command line. Scans HTML, CSS, JSX, TSX, Vue, and Svelte files for 25 specific patterns including AI-generated UI tells, accessibility violations, and general design quality problems. + +## Quick Start + +```bash +# Scan files or directories +npx impeccable detect src/ + +# Scan a live URL (requires Puppeteer) +npx impeccable detect https://example.com + +# JSON output for CI/tooling +npx impeccable detect --json src/ + +# Regex-only mode (faster, no jsdom) +npx impeccable detect --fast src/ +``` + +## What It Detects + +**AI Slop Tells** -- patterns that scream "AI generated this": +- Side-tab accent borders, gradient text on headings +- Purple/violet gradients and cyan-on-dark palettes +- Dark mode with glowing accents, border + border-radius clashes + +**Typography Issues** -- overused fonts (Inter, Roboto), flat type hierarchy, single font families + +**Color & Contrast** -- WCAG AA violations, gray text on colored backgrounds, pure black/white + +**Layout & Composition** -- nested cards, monotonous spacing, everything-centered layouts + +**Motion** -- bounce/elastic easing, layout property transitions + +**Quality** -- tiny body text, cramped padding, long line lengths, small touch targets + +25 detections in total. See the full list at [impeccable.style](https://impeccable.style). + +## Exit Codes + +- `0` -- no issues found +- `2` -- anti-patterns detected + +## Options + +``` +impeccable detect [options] [file-or-dir-or-url...] + + --fast Regex-only mode (skip jsdom, faster but less accurate) + --json Output findings as JSON + --help Show help +``` + +## Requirements + +- Node.js 18+ +- `jsdom` (included as dependency, used for HTML scanning) +- `puppeteer` (optional, only needed for URL scanning) + +## Part of Impeccable + +This CLI is part of [Impeccable](https://impeccable.style), a cross-provider design skill pack for AI-powered development tools. The full suite includes 20 steering commands for Claude, Cursor, Gemini, Codex, and more. + +## License + +See [LICENSE](https://github.com/pbakaus/impeccable/blob/main/LICENSE) file. diff --git a/bin/impeccable.mjs b/bin/impeccable similarity index 72% rename from bin/impeccable.mjs rename to bin/impeccable index 5703363eb..f76390dfe 100755 --- a/bin/impeccable.mjs +++ b/bin/impeccable @@ -1,4 +1,4 @@ -#!/usr/bin/env bun +#!/usr/bin/env node /** * Impeccable CLI @@ -9,6 +9,11 @@ * npx impeccable --help */ +import { readFileSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); const args = process.argv.slice(2); const command = args[0]; @@ -19,15 +24,16 @@ Commands: detect [file-or-dir-or-url...] Scan for UI anti-patterns and design quality issues Options: - --help Show this help message + --help Show this help message + --version Show version number Run 'impeccable detect --help' for detection-specific options.`); process.exit(0); } if (command === '--version' || command === '-v') { - const pkg = await import('../package.json', { with: { type: 'json' } }); - console.log(pkg.default.version); + const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')); + console.log(pkg.version); process.exit(0); } diff --git a/package.json b/package.json index 080f3b714..d21405f4b 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,45 @@ { "name": "impeccable", - "version": "2.0.0", + "version": "2.0.1", "author": "Paul Bakaus", "bin": { - "impeccable": "./bin/impeccable.mjs" + "impeccable": "./bin/impeccable" }, + "files": [ + "bin/", + "source/skills/critique/scripts/detect-antipatterns.mjs", + "source/skills/critique/scripts/detect-antipatterns-browser.js", + "LICENSE" + ], "dependencies": { - "archiver": "^7.0.1", - "motion": "^12.38.0", - "playwright": "^1.58.2" + "jsdom": "^29.0.0" }, - "description": "Cross-provider design skills and commands for LLM-powered development tools", + "optionalDependencies": { + "puppeteer": "^24.39.1" + }, + "description": "Detect UI anti-patterns and design quality issues from the command line", "keywords": [ - "cursor", - "claude", - "gemini", - "codex", "design", "frontend", - "ux" + "ux", + "anti-patterns", + "lint", + "accessibility", + "css", + "html", + "cli" ], "license": "SEE LICENSE FILE", + "repository": { + "type": "git", + "url": "git+https://github.com/pbakaus/impeccable.git" + }, + "engines": { + "node": ">=18" + }, "scripts": { + "prepublishOnly": "cp README.md README.bak.md && cp README.npm.md README.md", + "postpublish": "mv README.bak.md README.md", "build": "bun run scripts/build.js", "clean": "rm -rf dist build", "rebuild": "bun run clean && bun run build", @@ -34,8 +52,9 @@ }, "type": "module", "devDependencies": { - "jsdom": "^29.0.0", - "puppeteer": "^24.39.1", + "archiver": "^7.0.1", + "motion": "^12.38.0", + "playwright": "^1.58.2", "wrangler": "^4.75.0" } } diff --git a/scripts/build-browser-detector.js b/scripts/build-browser-detector.js index d0b779612..46cc0d690 100644 --- a/scripts/build-browser-detector.js +++ b/scripts/build-browser-detector.js @@ -16,6 +16,8 @@ const ROOT = path.resolve(__dirname, '..'); const SOURCE = path.join(ROOT, 'source/skills/critique/scripts/detect-antipatterns.mjs'); const OUTPUT = path.join(ROOT, '.claude/skills/critique/scripts/detect-antipatterns-browser.js'); +// Also output next to source so the CLI's URL scanner can find it +const OUTPUT_CLI = path.join(ROOT, 'source/skills/critique/scripts/detect-antipatterns-browser.js'); let code = fs.readFileSync(SOURCE, 'utf-8'); @@ -42,4 +44,5 @@ ${code} fs.mkdirSync(path.dirname(OUTPUT), { recursive: true }); fs.writeFileSync(OUTPUT, output); +fs.writeFileSync(OUTPUT_CLI, output); console.log(`\u2713 Generated ${path.relative(ROOT, OUTPUT)} (${(output.length / 1024).toFixed(1)} KB)`);