CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-oclif

A comprehensive CLI framework for creating command-line interfaces in Node.js and TypeScript

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

programmatic-api.mddocs/

Programmatic API

Direct access to oclif functionality for programmatic usage, custom integrations, and embedding CLIs within applications.

Capabilities

Run Function

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);
}

Error Handling

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);
  }
}

Integration Patterns

Embedding in Applications

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;
}

Custom CLI Wrapper

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;
}

Build and Deploy Automation

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!");
  }
}

Testing Integration

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");
  });
});

Advanced Usage

Custom Configuration

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
});

Output Capture

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;
}

Process Management

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

docs

cli-management.md

code-generation.md

index.md

packaging.md

programmatic-api.md

upload-deployment.md

tile.json