diff --git a/cli/bin/cli.js b/cli/bin/cli.js index 16459e4e4..850911a6d 100755 --- a/cli/bin/cli.js +++ b/cli/bin/cli.js @@ -13,6 +13,7 @@ import { readFileSync, existsSync } from 'node:fs'; import { join, dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { printUsage } from './usage.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const SKILL_COMMANDS = new Set(['help', 'install', 'link', 'update', 'check']); @@ -33,23 +34,7 @@ async function main() { const command = args[0]; if (!command || command === '--help' || command === '-h') { - console.log(`Usage: impeccable [options] - -Commands: - detect [file-or-dir-or-url...] Scan for UI anti-patterns and design quality issues - ignores Manage detector ignore rules, files, and values - help List all available skills and commands - install Install impeccable skills into your project or global harness - link Symlink skills from a local checkout or submodule - update Update skills to the latest version - check Check if skill updates are available - -Options: - --help Show this help message - --version Show version number - -Compatibility: - impeccable skills Legacy namespace; still supported.`); + printUsage(); process.exit(0); } diff --git a/cli/bin/commands/skills.mjs b/cli/bin/commands/skills.mjs index db1c13633..de5e51319 100644 --- a/cli/bin/commands/skills.mjs +++ b/cli/bin/commands/skills.mjs @@ -19,6 +19,7 @@ import { createHash } from 'node:crypto'; import { tmpdir, homedir } from 'node:os'; import { unzipSync } from 'fflate'; import { getHookConsent, setHookConsent } from '../../lib/impeccable-config.mjs'; +import { printUsage } from '../usage.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const API_BASE = 'https://impeccable.style'; @@ -2362,22 +2363,9 @@ async function downloadFile(url, dest, { fetchImpl = globalThis.fetch } = {}) { } } -function printUpdateUsage() { - console.log(`Usage: impeccable update [options] - -Update installed impeccable skills to the latest version. - -Options: - --project, --user Update the project-level or user-level install - -y, --yes Skip confirmation prompts - --force Overwrite existing hook manifests - --no-hooks Refresh skills without installing or repairing hooks - -h, --help Show this help message`); -} - async function update(flags = []) { if (flags.includes('--help') || flags.includes('-h')) { - printUpdateUsage(); + printUsage(); return; } diff --git a/cli/bin/usage.mjs b/cli/bin/usage.mjs new file mode 100644 index 000000000..de8fe49ac --- /dev/null +++ b/cli/bin/usage.mjs @@ -0,0 +1,162 @@ +export function printUsage() { + console.log(`Usage: impeccable [options] + +Terminal: npx impeccable ... install, update, detect, ignores +Agent: /impeccable ... every design command +Docs: https://impeccable.style/docs + +\`impeccable polish\` in a shell is not polish. Design commands run in the agent. + + +──────────────────────────────────────────────────────── +Start with /impeccable +──────────────────────────────────────────────────────── + + 1. npx impeccable install From the project root, then reload the agent + 2. /impeccable init PRODUCT.md (and DESIGN.md when code exists) + 3. /impeccable polish the pricing page + + +──────────────────────────────────────────────────────── +Learn +──────────────────────────────────────────────────────── + +Getting started + npx impeccable install Skill + hook into this project or user home + npx impeccable update Refresh an existing install + npx impeccable check See if a newer skill bundle exists + npx impeccable link --source=.impeccable + Symlink a git checkout / submodule + --providers=claude,cursor,codex,github,gemini,grok,hermes,kiro, + opencode,pi,qoder,trae,trae-cn,rovo-dev,vibe, + veto,antigravity + --scope=project|global --project | --user + -y, --yes Skip prompts + --force Replace hook manifests / existing links + --no-hooks Skills only; do not install or repair hooks + impeccable skills Legacy namespace; still supported + /impeccable pin audit | unpin audit Standalone /audit shortcut (any command) + Reload the agent after install. Then trust the hook in the harness. + +Iterate on UI with Live Mode + /impeccable live Pick an element, three variants, accept + into source. Vite, Next, SvelteKit, + Astro, Nuxt. Next includes monorepos. + From a monorepo root, pick the app first (or --target ). + Live state lives in /.impeccable/live/ + +Critique with the visual overlay + /impeccable critique UX review in the agent + Chrome extension Same 61 rules as an overlay on any page + + +──────────────────────────────────────────────────────── +Core concepts +──────────────────────────────────────────────────────── + +Design Context + PRODUCT.md Audience, purpose, platform + DESIGN.md Visual system + .impeccable/design.json Generated sidecar + .impeccable/surfaces/*.md Per-page / per-route briefs + Platform web | ios | android | adaptive + Mode (per surface, not per repo) Persuade | Operate | Read | Experience + + More than one app + Found via package.json workspaces, pnpm-workspace.yaml, lerna.json, + or "projectRoots": ["apps/*"] in .impeccable/config.json + Child PRODUCT.md / DESIGN.md wins; missing files inherit the repo root + per file. A nested git repo does not inherit. + --target on context, live, and doctor. + In a non-monorepo repo, --target still selects a nested product. + From the repo root with no --target: pick an app, then rerun there. + +Config and ignores + npx impeccable ignores list + npx impeccable ignores add-rule + npx impeccable ignores add-file + npx impeccable ignores add-value + npx impeccable ignores remove-rule | remove-file | remove-value + npx impeccable ignores clear + --shared .impeccable/config.json (default, commit this) + --local .impeccable/config.local.json (gitignored) + --all remove/clear both + --file --reason + In-file: impeccable-disable | -line | -next-line + projectRoots in config.json (local.json can add private roots; !glob hides) + Keep .gitignore .impeccable rules unanchored so apps/web/.impeccable matches + .impeccable/live/config.json is shared; do commit it + +New work + /impeccable Describe a new surface in plain English + Worlds, direction, then build. Replacement looks go through new-work, + not polish-on-the-old-one. + + +──────────────────────────────────────────────────────── +Automation +──────────────────────────────────────────────────────── + +Detector CLI + npx impeccable detect [file|dir|url...] + --json --quiet --scope type|layout --viewport WxH + --no-config --no-inline-ignores --no-design-system --no-advisory + Workspace files use that app's DESIGN.md, else the repo root's. + Exit codes are CI-safe. Advisory findings never fail the gate. + +Design hooks + /impeccable hooks status | on | off + /impeccable hooks ignore-rule | ignore-file | ignore-value + install/update writes the manifest for Claude Code, Copilot, Codex, + Cursor, Grok Build. The harness still has to trust it. + +Doctor + /impeccable doctor Context, DESIGN drift, ignores vs live + rules, hook path, workspace table + --json --fix --target + Flags projectRoots globs that match nothing. + + +──────────────────────────────────────────────────────── +Commands (agent: /impeccable [target]) +──────────────────────────────────────────────────────── + +Create + impeccable Next-step menu, or describe the work in plain English + shape Plan UX/UI before code + +Evaluate + audit Technical quality, P0-P3 + critique UX review, scoring, personas, detector + +Refine + animate Purposeful motion + bolder Safe design, more impact + colorize Strategic color + delight Small memorable moments + layout Spacing, rhythm, composition + overdrive Shaders, physics, 60fps, cinematic + quieter Too loud, same intent + typeset Type hierarchy and fonts + +Simplify + adapt Screens, devices, platforms + clarify UX copy, labels, errors + distill Strip to essence + +Harden + harden Errors, i18n, overflow, edge cases + onboard First-run, empty states, activation + optimize UI performance + polish Last quality pass + +System + document DESIGN.md from existing UI + extract Tokens and components into the system + init PRODUCT.md + live Browser variants into source + + pin / unpin Standalone /audit (and friends) + teach Same as init + craft Deprecated new-work alias`); +} diff --git a/tests/skills-cli.test.js b/tests/skills-cli.test.js index 5887258f8..5667cf548 100644 --- a/tests/skills-cli.test.js +++ b/tests/skills-cli.test.js @@ -858,9 +858,14 @@ describe('skills install/update: local universal bundle e2e', () => { test('root help advertises top-level skills commands', () => { const output = run('--help'); - expect(output).toContain('install Install impeccable skills'); - expect(output).toContain('update Update skills to the latest version'); - expect(output).toContain('impeccable skills Legacy namespace; still supported.'); + expect(output).toContain('Start with /impeccable'); + expect(output).toContain('npx impeccable install'); + expect(output).toContain('npx impeccable update'); + expect(output).toContain('impeccable skills Legacy namespace; still supported'); + expect(output).toContain('Detector CLI'); + expect(output).toContain('Design Context'); + expect(output).not.toContain('Full session path'); + expect(output).not.toContain('Useful command pairs'); expect(output).not.toContain('skills install Install impeccable skills'); }); @@ -876,7 +881,8 @@ describe('skills install/update: local universal bundle e2e', () => { for (const args of ['update --help', 'update -h', 'skills update --help']) { const output = run(args, { cwd: tmp, env: { ...envBase, HOME: home } }); - expect(output).toContain('Usage: impeccable update'); + expect(output).toContain('Usage: impeccable [options]'); + expect(output).toContain('Start with /impeccable'); expect(output).not.toContain('Checking for updates'); expect(output).not.toContain('Updating the'); } @@ -888,7 +894,7 @@ describe('skills install/update: local universal bundle e2e', () => { const emptyTmp = mkdtempSync(join(tmpdir(), 'imp-test-update-help-empty-699-')); const emptyHome = mkdtempSync(join(tmpdir(), 'imp-home-update-help-empty-699-')); const output = run('update --help', { cwd: emptyTmp, env: { ...envBase, HOME: emptyHome } }); - expect(output).toContain('Usage: impeccable update'); + expect(output).toContain('Usage: impeccable [options]'); expect(output).not.toContain('Run `npx impeccable install` to install first.'); expect(output).not.toContain('Checking for updates');