Files
pbakaus_impeccable/bin/cli.js
T
Paul BakausandClaude Opus 4.6 5bad08723d Replace WebSocket with SSE, move live scripts into skill (self-contained)
Two architectural changes that make the live variant mode self-contained:

1. SSE replaces WebSocket: the server now uses Server-Sent Events for
   server→browser push and regular fetch POST for browser→server
   events. This eliminates the ws npm dependency entirely. The live
   server is now zero-dependency pure Node.js (http, crypto, fs, net).

   Browser: EventSource replaces WebSocket. sendEvent() uses fetch POST.
   Server: GET /events returns SSE stream, POST /events receives browser
   events. All other endpoints (poll, source, health, stop) unchanged.

2. Scripts moved to source/skills/impeccable/scripts/: live-server.mjs,
   live-poll.mjs, live-wrap.mjs, live-browser.js are now part of the
   skill itself. Users who install the skill via npx skills get the live
   mode without needing npm install impeccable separately.

   The skill reference uses {{scripts_path}}/live-server.mjs etc.
   The CLI (bin/cli.js) delegates to the skill scripts as a convenience.

   Removed ws from package.json dependencies.

The old src/live/ files remain as the development copy. The build system
syncs source/skills/ to all harness dirs (11 providers).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 19:19:36 -07:00

76 lines
3.0 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Impeccable CLI
*
* Usage:
* npx impeccable detect [file-or-dir-or-url...]
* npx impeccable live [--port=PORT]
* npx impeccable live stop
* npx impeccable skills help|install|update
* 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];
if (!command || command === '--help' || command === '-h') {
console.log(`Usage: impeccable <command> [options]
Commands:
detect [file-or-dir-or-url...] Scan for UI anti-patterns and design quality issues
live [--port=PORT] Start live variant server (element picker + variant cycling)
live stop Stop a running live server
poll Wait for a browser event from the live server
poll --reply <id> <status> Reply to a pending event (done, error)
wrap --id ID --count N --query Q Find element in source and create variant wrapper
skills help List all available skills and commands
skills install Install impeccable skills into your project
skills update Update skills to the latest version
skills check Check if skill updates are available
Options:
--help Show this help message
--version Show version number
Run 'impeccable <command> --help' for command-specific options.`);
process.exit(0);
}
if (command === '--version' || command === '-v') {
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
console.log(pkg.version);
process.exit(0);
}
if (command === 'detect') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { detectCli } = await import('../src/detect-antipatterns.mjs');
await detectCli();
} else if (command === 'live') {
// Delegate to the self-contained skill script (also works via node scripts_path/live-server.mjs)
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
await import('../source/skills/impeccable/scripts/live-server.mjs');
} else if (command === 'poll') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { pollCli } = await import('../source/skills/impeccable/scripts/live-poll.mjs');
await pollCli();
} else if (command === 'wrap') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { wrapCli } = await import('../source/skills/impeccable/scripts/live-wrap.mjs');
await wrapCli();
} else if (command === 'skills') {
const { run } = await import('./commands/skills.mjs');
await run(args.slice(1));
} else {
// Default: treat as detect arguments (allow `npx impeccable src/` shorthand)
process.argv = [process.argv[0], process.argv[1], ...args];
const { detectCli } = await import('../src/detect-antipatterns.mjs');
await detectCli();
}