Use when wrapping CLI binaries in JavaScript, automating shell workflows in TypeScript, composing multiple CLIs into scripts, or building agent tool-use. Covers convertCliToJs, $command, $validate, $spawn, script(), .text()/.lines()/.json() output parsing.
You are using cli-to-js to turn CLI binaries into callable JavaScript APIs.
convertCliToJs("binary") once, reuse the returned API.text(), .lines(), or .json() for typed output — never manually split result.stdout$validate before executing when inputs come from untrusted sources$spawn + for await for streaming — never callbacks unless specifically asked// JS option key → CLI output
// { verbose: true } → --verbose
// { verbose: false } → (omitted)
// { output: "file.txt" } → --output file.txt
// { dryRun: true } → --dry-run
// { v: true } → -v
// { include: ["a", "b"] } → --include a --include b
// { _: ["file.txt"] } → file.txt
import { convertCliToJs, script } from "cli-to-js";
const tool = await convertCliToJs("binary-name");
// Run + typed output
await tool.subcommand({ flag: "val", _: ["pos"] }).text();
await tool.subcommand({ json: true }).json<MyType>();
await tool.subcommand().lines();
// Streaming
for await (const line of tool.$spawn.subcommand({ watch: true })) {
}
// Validation (did-you-mean, choices, required flags, exclusive flags)
const errors = tool.$validate({ misspeled: true });
// Shell string without executing
tool.$command.subcommand({ flag: "val" });
// → "binary-name subcommand --flag val"
// Compose into runnable script
const deploy = script(git.$command.push(), docker.$command.build({ tag: "app", _: ["."] }));
deploy.run(); // execute with &&
console.log(`${deploy}`); // get the string
Bad:
const result = await api.status();
const lines = result.stdout.trim().split("\n");
Good:
const statusLines = await api.status().lines();
Bad:
const api1 = await convertCliToJs("git");
const api2 = await convertCliToJs("git"); // wasteful duplicate
Good:
const git = await convertCliToJs("git");
// reuse git everywhere
Bad:
await api.commit({ mesage: "fix" }); // typo silently becomes unknown flag
Good:
const errors = api.$validate({ mesage: "fix" });
// [{ kind: "unknown-flag", suggestion: "message" }]
if (errors.length > 0) throw new Error(errors[0].message);
await api.commit({ message: "fix" });
const git = await convertCliToJs("git");
const claude = await convertCliToJs("claude");
const files = await git.diff({ nameOnly: true, _: ["HEAD~1"] }).lines();
for (const file of files) {
const review = await claude({
print: true,
model: "sonnet",
_: [`Review ${file} for bugs`],
}).text();
if (review.includes("no issues")) continue;
console.log(`${file}: ${review}`);
}
| Method | Returns | Use for |
|---|---|---|
api.sub(opts) | CommandPromise | Run subcommand |
.text() | Promise<string> | Trimmed stdout |
.lines() | Promise<string[]> | Split by newlines |
.json<T>() | Promise<T> | Parse JSON output |
api.$spawn.sub(opts) | CommandProcess | for await streaming |
api.$command.sub(opts) | string | Shell string, no execution |
api.$validate(opts) | ValidationError[] | Pre-flight flag checking |
api.$validate("sub", opts) | ValidationError[] | Subcommand flag checking |
api.$schema | CliSchema | Parsed schema from --help |
api.$parse("sub") | ParsedCommand | Lazily enrich subcommand |
script(...cmds) | { run, toString } | Compose && chain |