Fix: point install's next step at the agent chat, not the terminal (#472) (#532)

The install completion message said to run /impeccable init "in your AI
harness", and users pasted it into their shell instead (bash: /impeccable:
No such file or directory). Say the command is typed in the AI coding
agent's chat, and give `npx impeccable init` a pointed redirect instead of
the generic unknown-command error. A real path named `init` still routes
to detect as before.

Prepared with AI assistance (Cursor agent), directed by @abdulwahabone.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Abdul Wahab
2026-08-08 18:40:26 -07:00
committed by GitHub
co-authored by Cursor
parent 477484aaee
commit 628aac5a40
3 changed files with 65 additions and 1 deletions
+6
View File
@@ -77,6 +77,12 @@ Compatibility:
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.
+1 -1
View File
@@ -1944,7 +1944,7 @@ async function install(flags) {
reportProviderAgents(agentResults);
if (hookTargets.length > 0) console.log(`Installed hooks into: ${hookTargets.join(', ')}`);
console.log('\nDone! Run /impeccable init in your AI harness to set up design context.\n');
console.log('\nDone! Now type /impeccable init in your AI coding agent\'s chat (not in this terminal) to set up design context.\n');
}
// ─── skills update ────────────────────────────────────────────────────────────
+58
View File
@@ -719,6 +719,64 @@ describe('skills install/update: local universal bundle e2e', () => {
rmSync(home, { recursive: true, force: true });
}, 15000);
test('install completion says /impeccable init runs in the agent chat, not the terminal (#472)', () => {
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-install-472-msg-'));
const home = mkdtempSync(join(tmpdir(), 'imp-home-install-472-msg-'));
execSync('git init', { cwd: tmp });
const bundleRoot = createFakeUniversalBundle(tmp, ['.claude']);
const output = run('install -y --providers=claude --no-hooks', {
cwd: tmp,
env: { ...process.env, HOME: home, IMPECCABLE_BUNDLE_PATH: bundleRoot },
});
expect(output).toContain("type /impeccable init in your AI coding agent's chat (not in this terminal)");
rmSync(tmp, { recursive: true, force: true });
rmSync(home, { recursive: true, force: true });
}, 15000);
test('`impeccable init` in the shell points at the agent chat instead of "Unknown command" (#472)', () => {
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-init-472-'));
let error;
try {
run('init', { cwd: tmp, stdio: 'pipe' });
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.status).toBe(1);
const stderr = String(error.stderr);
expect(stderr).toContain("Type /impeccable init in your AI coding agent's chat");
expect(stderr).not.toContain('Unknown command');
rmSync(tmp, { recursive: true, force: true });
}, 15000);
test('a real path named init still routes to detect, not the #472 guidance', () => {
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-init-path-472-'));
mkdirSync(join(tmp, 'init'), { recursive: true });
writeFileSync(join(tmp, 'init', 'page.html'), '<!doctype html><html><head><title>t</title></head><body><p>hello</p></body></html>\n');
// Detect exits 0 on a clean scan and 2 when findings surface; either way it
// must be the detector answering, not the init redirect. --json makes that
// positive: the detector always prints a JSON findings array.
let output = '';
try {
output = run('init --json', { cwd: tmp, stdio: 'pipe' });
} catch (e) {
output = `${e.stdout || ''}${e.stderr || ''}`;
}
expect(output.trim().startsWith('[')).toBe(true);
expect(output).not.toContain('is not a CLI command');
expect(output).not.toContain('Unknown command');
rmSync(tmp, { recursive: true, force: true });
}, 60000);
test('formats detected harnesses as concise source-to-target rows', () => {
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-detect-lines-'));
const home = mkdtempSync(join(tmpdir(), 'imp-home-detect-lines-'));