0
# Programmatic API
1
2
Direct access to oclif functionality for programmatic usage, custom integrations, and embedding CLIs within applications.
3
4
## Capabilities
5
6
### Run Function
7
8
Execute oclif CLI programmatically with custom arguments and options.
9
10
```typescript { .api }
11
/**
12
* Execute oclif CLI programmatically
13
* @param argv - Command line arguments array
14
* @param options - Load options for CLI execution
15
* @returns Promise that resolves when command completes
16
*/
17
function run(argv?: string[], options?: LoadOptions): Promise<void>;
18
19
interface LoadOptions {
20
/** Root directory of the CLI project */
21
root?: string;
22
/** Release channel to use (stable, beta, alpha) */
23
channel?: string;
24
/** Enable development mode with enhanced debugging */
25
development?: boolean;
26
}
27
```
28
29
**Usage Examples:**
30
31
```typescript
32
import { run } from "oclif";
33
34
// Execute help command
35
await run(["help"]);
36
37
// Execute specific command with arguments
38
await run(["generate", "my-new-cli", "--author=John Doe"]);
39
40
// Execute with custom options
41
await run(["pack", "tarballs"], {
42
root: "/path/to/cli/project",
43
development: true
44
});
45
46
// Handle command execution in try-catch
47
try {
48
await run(["invalid-command"]);
49
} catch (error) {
50
console.error("Command failed:", error.message);
51
}
52
```
53
54
### Error Handling
55
56
The run function throws errors for various failure conditions:
57
58
```typescript { .api }
59
// Command execution can throw these error types:
60
// - CommandNotFoundError: Command does not exist
61
// - ValidationError: Invalid arguments or flags
62
// - ExecutionError: Command execution failed
63
// - ConfigError: CLI configuration issues
64
65
try {
66
await run(["nonexistent-command"]);
67
} catch (error) {
68
if (error.code === 'EEXIT') {
69
// Command exited with non-zero code
70
console.log(`Command failed with exit code: ${error.exit}`);
71
} else {
72
// Other execution errors
73
console.error("Execution error:", error);
74
}
75
}
76
```
77
78
## Integration Patterns
79
80
### Embedding in Applications
81
82
Embed oclif CLI functionality within Node.js applications:
83
84
```typescript
85
import { run } from "oclif";
86
87
class CLIService {
88
private projectRoot: string;
89
90
constructor(projectRoot: string) {
91
this.projectRoot = projectRoot;
92
}
93
94
async generateProject(name: string, options: GenerateOptions): Promise<void> {
95
const args = ["generate", name];
96
97
if (options.author) args.push(`--author=${options.author}`);
98
if (options.license) args.push(`--license=${options.license}`);
99
if (options.yes) args.push("--yes");
100
101
await run(args, { root: this.projectRoot });
102
}
103
104
async buildPackages(targets: string[]): Promise<void> {
105
await run(["pack", "tarballs", `--targets=${targets.join(",")}`], {
106
root: this.projectRoot
107
});
108
}
109
}
110
111
interface GenerateOptions {
112
author?: string;
113
license?: string;
114
yes?: boolean;
115
}
116
```
117
118
### Custom CLI Wrapper
119
120
Create wrapper functions for specific oclif operations:
121
122
```typescript
123
import { run } from "oclif";
124
125
export class OclifWrapper {
126
static async createCLI(config: CLIConfig): Promise<void> {
127
const args = ["generate", config.name];
128
129
Object.entries(config).forEach(([key, value]) => {
130
if (key !== "name" && value !== undefined) {
131
args.push(`--${key}=${value}`);
132
}
133
});
134
135
if (config.skipPrompts) {
136
args.push("--yes");
137
}
138
139
await run(args);
140
}
141
142
static async generateCommand(name: string, force: boolean = false): Promise<void> {
143
const args = ["generate", "command", name];
144
if (force) args.push("--force");
145
146
await run(args);
147
}
148
149
static async updateReadme(): Promise<void> {
150
await run(["readme"]);
151
}
152
153
static async createManifest(path?: string): Promise<void> {
154
const args = ["manifest"];
155
if (path) args.push(path);
156
157
await run(args);
158
}
159
}
160
161
interface CLIConfig {
162
name: string;
163
author?: string;
164
description?: string;
165
license?: string;
166
bin?: string;
167
repository?: string;
168
skipPrompts?: boolean;
169
}
170
```
171
172
### Build and Deploy Automation
173
174
Automate the build and deployment process:
175
176
```typescript
177
import { run } from "oclif";
178
179
export class BuildPipeline {
180
private projectRoot: string;
181
182
constructor(projectRoot: string) {
183
this.projectRoot = projectRoot;
184
}
185
186
async buildAll(targets: string[] = ["linux-x64", "darwin-x64", "win32-x64"]): Promise<void> {
187
console.log("Building tarballs...");
188
await run(["pack", "tarballs", `--targets=${targets.join(",")}`], {
189
root: this.projectRoot
190
});
191
192
console.log("Building platform packages...");
193
await Promise.all([
194
run(["pack", "deb"], { root: this.projectRoot }),
195
run(["pack", "macos"], { root: this.projectRoot }),
196
run(["pack", "win"], { root: this.projectRoot })
197
]);
198
}
199
200
async deploy(channel: string = "stable"): Promise<void> {
201
console.log("Uploading packages...");
202
await Promise.all([
203
run(["upload", "tarballs"], { root: this.projectRoot }),
204
run(["upload", "deb"], { root: this.projectRoot }),
205
run(["upload", "macos"], { root: this.projectRoot }),
206
run(["upload", "win"], { root: this.projectRoot })
207
]);
208
209
console.log(`Promoting to ${channel} channel...`);
210
await run(["promote", `--channel=${channel}`], {
211
root: this.projectRoot
212
});
213
}
214
215
async fullPipeline(targets?: string[], channel: string = "beta"): Promise<void> {
216
await this.buildAll(targets);
217
await this.deploy(channel);
218
console.log("Pipeline completed successfully!");
219
}
220
}
221
```
222
223
### Testing Integration
224
225
Use programmatic API for testing CLI functionality:
226
227
```typescript
228
import { run } from "oclif";
229
import { promises as fs } from "fs";
230
import { tmpdir } from "os";
231
import { join } from "path";
232
233
describe("CLI Integration Tests", () => {
234
let testDir: string;
235
236
beforeEach(async () => {
237
testDir = await fs.mkdtemp(join(tmpdir(), "oclif-test-"));
238
});
239
240
afterEach(async () => {
241
await fs.rmdir(testDir, { recursive: true });
242
});
243
244
it("should generate a new CLI project", async () => {
245
const projectName = "test-cli";
246
247
await run([
248
"generate",
249
projectName,
250
"--author=Test Author",
251
"--license=MIT",
252
"--yes"
253
], { root: testDir });
254
255
// Verify project was created
256
const projectPath = join(testDir, projectName);
257
const packageJsonExists = await fs.access(join(projectPath, "package.json"))
258
.then(() => true)
259
.catch(() => false);
260
261
expect(packageJsonExists).toBe(true);
262
});
263
264
it("should generate README documentation", async () => {
265
// First create a CLI project
266
await run(["generate", "test-cli", "--yes"], { root: testDir });
267
268
const projectPath = join(testDir, "test-cli");
269
270
// Generate README
271
await run(["readme"], { root: projectPath });
272
273
// Verify README was updated
274
const readmeContent = await fs.readFile(join(projectPath, "README.md"), "utf-8");
275
expect(readmeContent).toContain("Usage");
276
expect(readmeContent).toContain("Commands");
277
});
278
});
279
```
280
281
## Advanced Usage
282
283
### Custom Configuration
284
285
Override default configuration for programmatic usage:
286
287
```typescript
288
import { run } from "oclif";
289
290
// Set environment variables for custom configuration
291
process.env.OCLIF_CLI_ROOT = "/custom/cli/path";
292
process.env.OCLIF_DEVELOPMENT = "true";
293
294
await run(["help"], {
295
development: true,
296
root: process.env.OCLIF_CLI_ROOT
297
});
298
```
299
300
### Output Capture
301
302
Capture command output for processing:
303
304
```typescript
305
import { run } from "oclif";
306
307
// Capture stdout and stderr
308
const originalWrite = process.stdout.write;
309
const originalError = process.stderr.write;
310
311
let output = "";
312
let errors = "";
313
314
process.stdout.write = (chunk: any) => {
315
output += chunk.toString();
316
return true;
317
};
318
319
process.stderr.write = (chunk: any) => {
320
errors += chunk.toString();
321
return true;
322
};
323
324
try {
325
await run(["help"]);
326
console.log("Command output:", output);
327
} catch (error) {
328
console.log("Command errors:", errors);
329
} finally {
330
// Restore original functions
331
process.stdout.write = originalWrite;
332
process.stderr.write = originalError;
333
}
334
```
335
336
### Process Management
337
338
Handle CLI execution in different process contexts:
339
340
```typescript
341
import { run } from "oclif";
342
import { spawn } from "child_process";
343
344
// Execute in current process
345
await run(["generate", "my-cli"]);
346
347
// Execute in separate process for isolation
348
function runInSeparateProcess(args: string[]): Promise<number> {
349
return new Promise((resolve, reject) => {
350
const child = spawn("oclif", args, { stdio: "inherit" });
351
352
child.on("close", (code) => {
353
if (code === 0) {
354
resolve(code);
355
} else {
356
reject(new Error(`Process exited with code ${code}`));
357
}
358
});
359
360
child.on("error", reject);
361
});
362
}
363
364
// Usage
365
await runInSeparateProcess(["pack", "tarballs"]);
366
```