A comprehensive CLI framework for creating command-line interfaces in Node.js and TypeScript
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Direct access to oclif functionality for programmatic usage, custom integrations, and embedding CLIs within applications.
Execute oclif CLI programmatically with custom arguments and options.
/**
* Execute oclif CLI programmatically
* @param argv - Command line arguments array
* @param options - Load options for CLI execution
* @returns Promise that resolves when command completes
*/
function run(argv?: string[], options?: LoadOptions): Promise<void>;
interface LoadOptions {
/** Root directory of the CLI project */
root?: string;
/** Release channel to use (stable, beta, alpha) */
channel?: string;
/** Enable development mode with enhanced debugging */
development?: boolean;
}Usage Examples:
import { run } from "oclif";
// Execute help command
await run(["help"]);
// Execute specific command with arguments
await run(["generate", "my-new-cli", "--author=John Doe"]);
// Execute with custom options
await run(["pack", "tarballs"], {
root: "/path/to/cli/project",
development: true
});
// Handle command execution in try-catch
try {
await run(["invalid-command"]);
} catch (error) {
console.error("Command failed:", error.message);
}The run function throws errors for various failure conditions:
// Command execution can throw these error types:
// - CommandNotFoundError: Command does not exist
// - ValidationError: Invalid arguments or flags
// - ExecutionError: Command execution failed
// - ConfigError: CLI configuration issues
try {
await run(["nonexistent-command"]);
} catch (error) {
if (error.code === 'EEXIT') {
// Command exited with non-zero code
console.log(`Command failed with exit code: ${error.exit}`);
} else {
// Other execution errors
console.error("Execution error:", error);
}
}Embed oclif CLI functionality within Node.js applications:
import { run } from "oclif";
class CLIService {
private projectRoot: string;
constructor(projectRoot: string) {
this.projectRoot = projectRoot;
}
async generateProject(name: string, options: GenerateOptions): Promise<void> {
const args = ["generate", name];
if (options.author) args.push(`--author=${options.author}`);
if (options.license) args.push(`--license=${options.license}`);
if (options.yes) args.push("--yes");
await run(args, { root: this.projectRoot });
}
async buildPackages(targets: string[]): Promise<void> {
await run(["pack", "tarballs", `--targets=${targets.join(",")}`], {
root: this.projectRoot
});
}
}
interface GenerateOptions {
author?: string;
license?: string;
yes?: boolean;
}Create wrapper functions for specific oclif operations:
import { run } from "oclif";
export class OclifWrapper {
static async createCLI(config: CLIConfig): Promise<void> {
const args = ["generate", config.name];
Object.entries(config).forEach(([key, value]) => {
if (key !== "name" && value !== undefined) {
args.push(`--${key}=${value}`);
}
});
if (config.skipPrompts) {
args.push("--yes");
}
await run(args);
}
static async generateCommand(name: string, force: boolean = false): Promise<void> {
const args = ["generate", "command", name];
if (force) args.push("--force");
await run(args);
}
static async updateReadme(): Promise<void> {
await run(["readme"]);
}
static async createManifest(path?: string): Promise<void> {
const args = ["manifest"];
if (path) args.push(path);
await run(args);
}
}
interface CLIConfig {
name: string;
author?: string;
description?: string;
license?: string;
bin?: string;
repository?: string;
skipPrompts?: boolean;
}Automate the build and deployment process:
import { run } from "oclif";
export class BuildPipeline {
private projectRoot: string;
constructor(projectRoot: string) {
this.projectRoot = projectRoot;
}
async buildAll(targets: string[] = ["linux-x64", "darwin-x64", "win32-x64"]): Promise<void> {
console.log("Building tarballs...");
await run(["pack", "tarballs", `--targets=${targets.join(",")}`], {
root: this.projectRoot
});
console.log("Building platform packages...");
await Promise.all([
run(["pack", "deb"], { root: this.projectRoot }),
run(["pack", "macos"], { root: this.projectRoot }),
run(["pack", "win"], { root: this.projectRoot })
]);
}
async deploy(channel: string = "stable"): Promise<void> {
console.log("Uploading packages...");
await Promise.all([
run(["upload", "tarballs"], { root: this.projectRoot }),
run(["upload", "deb"], { root: this.projectRoot }),
run(["upload", "macos"], { root: this.projectRoot }),
run(["upload", "win"], { root: this.projectRoot })
]);
console.log(`Promoting to ${channel} channel...`);
await run(["promote", `--channel=${channel}`], {
root: this.projectRoot
});
}
async fullPipeline(targets?: string[], channel: string = "beta"): Promise<void> {
await this.buildAll(targets);
await this.deploy(channel);
console.log("Pipeline completed successfully!");
}
}Use programmatic API for testing CLI functionality:
import { run } from "oclif";
import { promises as fs } from "fs";
import { tmpdir } from "os";
import { join } from "path";
describe("CLI Integration Tests", () => {
let testDir: string;
beforeEach(async () => {
testDir = await fs.mkdtemp(join(tmpdir(), "oclif-test-"));
});
afterEach(async () => {
await fs.rmdir(testDir, { recursive: true });
});
it("should generate a new CLI project", async () => {
const projectName = "test-cli";
await run([
"generate",
projectName,
"--author=Test Author",
"--license=MIT",
"--yes"
], { root: testDir });
// Verify project was created
const projectPath = join(testDir, projectName);
const packageJsonExists = await fs.access(join(projectPath, "package.json"))
.then(() => true)
.catch(() => false);
expect(packageJsonExists).toBe(true);
});
it("should generate README documentation", async () => {
// First create a CLI project
await run(["generate", "test-cli", "--yes"], { root: testDir });
const projectPath = join(testDir, "test-cli");
// Generate README
await run(["readme"], { root: projectPath });
// Verify README was updated
const readmeContent = await fs.readFile(join(projectPath, "README.md"), "utf-8");
expect(readmeContent).toContain("Usage");
expect(readmeContent).toContain("Commands");
});
});Override default configuration for programmatic usage:
import { run } from "oclif";
// Set environment variables for custom configuration
process.env.OCLIF_CLI_ROOT = "/custom/cli/path";
process.env.OCLIF_DEVELOPMENT = "true";
await run(["help"], {
development: true,
root: process.env.OCLIF_CLI_ROOT
});Capture command output for processing:
import { run } from "oclif";
// Capture stdout and stderr
const originalWrite = process.stdout.write;
const originalError = process.stderr.write;
let output = "";
let errors = "";
process.stdout.write = (chunk: any) => {
output += chunk.toString();
return true;
};
process.stderr.write = (chunk: any) => {
errors += chunk.toString();
return true;
};
try {
await run(["help"]);
console.log("Command output:", output);
} catch (error) {
console.log("Command errors:", errors);
} finally {
// Restore original functions
process.stdout.write = originalWrite;
process.stderr.write = originalError;
}Handle CLI execution in different process contexts:
import { run } from "oclif";
import { spawn } from "child_process";
// Execute in current process
await run(["generate", "my-cli"]);
// Execute in separate process for isolation
function runInSeparateProcess(args: string[]): Promise<number> {
return new Promise((resolve, reject) => {
const child = spawn("oclif", args, { stdio: "inherit" });
child.on("close", (code) => {
if (code === 0) {
resolve(code);
} else {
reject(new Error(`Process exited with code ${code}`));
}
});
child.on("error", reject);
});
}
// Usage
await runInSeparateProcess(["pack", "tarballs"]);Install with Tessl CLI
npx tessl i tessl/npm-oclif