Files
Abdul WahabandCursor e01659095d Fix: print the docs map on CLI --help (#699)
`impeccable --help` and `update --help` now share one catalog aligned to the docs site, and still return without downloading or writing files.

AI assistance: prepared with Cursor Grok under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 12:43:52 +05:00

88 lines
3.3 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';
import { printUsage } from './usage.mjs';
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') {
printUsage();
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 if (command === 'init') {
// The follow-up mistake from issue #472: `/impeccable init` belongs in an AI
// coding agent's chat, and a user who typed it into their shell is likely to
// retry it here as `npx impeccable init`.
console.error(`"init" is not a CLI command. Type /impeccable init in your AI coding agent's chat (Claude Code, Cursor, Codex, ...), not in this terminal.`);
process.exit(1);
} 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);
});