mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
Unknown/mistyped CLI subcommands now print 'Unknown command' and exit non-zero instead of silently routing to the detector. Closes #266. Version bump and changelog entry deferred (batching). Co-Authored-By: abdulwahabone
97 lines
3.6 KiB
JavaScript
Executable File
97 lines
3.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Impeccable CLI
|
|
*
|
|
* Usage:
|
|
* npx impeccable detect [file-or-dir-or-url...]
|
|
* npx impeccable ignores <list|add-file|add-value|remove-...>
|
|
* npx impeccable help|install|update
|
|
* npx impeccable --help
|
|
*/
|
|
|
|
import { readFileSync, existsSync } from 'node:fs';
|
|
import { join, dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const SKILL_COMMANDS = new Set(['help', 'install', 'link', 'update', 'check']);
|
|
|
|
// Is this a detect target (the `npx impeccable src/` shorthand) or a mistyped
|
|
// command? Flags, URLs, path-shaped args, and real files/dirs (e.g. an
|
|
// extension-less `Dockerfile`) are targets; anything else is an unknown command.
|
|
function looksLikeDetectTarget(arg) {
|
|
const isFlag = arg.startsWith('-');
|
|
const isUrl = /^https?:\/\//i.test(arg);
|
|
const isPathShaped = arg.includes('/') || arg.includes('\\') || arg.includes('.');
|
|
const isExistingPath = existsSync(resolve(arg));
|
|
return isFlag || isUrl || isPathShaped || isExistingPath;
|
|
}
|
|
|
|
async function main() {
|
|
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
|
|
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 <command> Legacy namespace; still supported.`);
|
|
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('../engine/detect-antipatterns.mjs');
|
|
await detectCli();
|
|
} else if (command === 'ignores' || command === 'ignore') {
|
|
const { run } = await import('./commands/ignores.mjs');
|
|
await run(args.slice(1));
|
|
} else if (command === 'skills') {
|
|
const { run } = await import('./commands/skills.mjs');
|
|
await run(args.slice(1));
|
|
} else if (SKILL_COMMANDS.has(command)) {
|
|
const { run } = await import('./commands/skills.mjs');
|
|
await run(args);
|
|
} else if (looksLikeDetectTarget(command)) {
|
|
// Default: treat as detect arguments (allow `npx impeccable src/` shorthand)
|
|
process.argv = [process.argv[0], process.argv[1], ...args];
|
|
const { detectCli } = await import('../engine/detect-antipatterns.mjs');
|
|
await detectCli();
|
|
} else {
|
|
// An unknown bareword: a mistyped command (or an old cached version run
|
|
// against newer docs). Fail loudly instead of silently statting it as a path.
|
|
console.error(`Unknown command: "${command}"\n\nTo see a list of supported commands, run:\n impeccable --help`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main().catch(error => {
|
|
if (error?.code === 'IMPECCABLE_PROMPT_ABORT') {
|
|
console.log('\nAborted.');
|
|
process.exit(130);
|
|
}
|
|
|
|
console.error(error?.message || error);
|
|
process.exit(1);
|
|
});
|