clawdie-ai/setup/agent-cli-check.ts
Clawdie AI fc0abfc517 feat(setup): require at least one agent CLI before onboarding
Mirrors Paperclip's per-adapter ensureCommandResolvable pattern as a single
fail-fast gate at the top of `setup onboard`. Probes {claude, codex, gemini,
pi} via commandExists; throws NoAgentCliError if none resolve. Also surfaces
all four in `setup verify` alongside the existing CODEX check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---
Build: pass | Tests: FAIL — Tests  1 failed | 846 passed (847)
2026-04-08 09:14:56 +00:00

55 lines
1.6 KiB
TypeScript

/**
* setup/agent-cli-check.ts — Detect agent CLIs available on PATH.
*
* Clawdie's runtime drives one of several local agent CLIs (claude, codex,
* gemini, pi). At least one must be resolvable on PATH for onboarding to
* succeed. This mirrors Paperclip's per-adapter `ensureCommandResolvable`
* pattern, but as a single fail-fast check at the top of `setup onboard`.
*
* No "hello probe" yet — `command -v` is enough; all four were validated
* end-to-end in doc/AGENT-CLI-VALIDATION.md.
*/
import { commandExists } from './platform.js';
export interface AgentCli {
name: string;
command: string;
present: boolean;
}
export const AGENT_CLIS: ReadonlyArray<{ name: string; command: string }> = [
{ name: 'claude', command: 'claude' },
{ name: 'codex', command: 'codex' },
{ name: 'gemini', command: 'gemini' },
{ name: 'pi', command: 'pi' },
];
export function detectAgentClis(): AgentCli[] {
return AGENT_CLIS.map(({ name, command }) => ({
name,
command,
present: commandExists(command),
}));
}
export class NoAgentCliError extends Error {
constructor() {
super(
[
'No agent CLI found on PATH.',
'Clawdie needs at least one of: claude, codex, gemini, pi.',
'Install via the ISO npm-globals bundle (claude/gemini/pi) or `pkg install codex`.',
'See doc/AGENT-CLI-VALIDATION.md for the validated install paths.',
].join(' '),
);
this.name = 'NoAgentCliError';
}
}
export function requireAtLeastOneAgentCli(): AgentCli[] {
const clis = detectAgentClis();
if (!clis.some((c) => c.present)) {
throw new NoAgentCliError();
}
return clis;
}