# OpenClaude CLI Startup Sequence: From Shell Command to Interactive REPL

> Discover the OpenClaude CLI startup sequence from shell command to interactive REPL. Explore the initialization flow via bin openclaude, src commands ts, and src main ts.

- Repository: [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude)
- Tags: internals
- Published: 2026-09-02

---

**The OpenClaude CLI initialization flow progresses through the `bin/openclaude` wrapper, the [`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts) orchestrator, and finally mounts the React-Ink application via [`src/main.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/main.tsx) before rendering the interactive REPL.**

The startup sequence for the OpenClaude CLI—that ships out of the [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude) repository—follows a carefully staged boot process. Understanding this flow helps developers debug initialization issues, optimize cold-start performance, and extend the tool's functionality.

## Entry Point: The Binary Wrapper

The OpenClaude CLI begins execution at **`bin/openclaude`**, a thin Node.js wrapper responsible for launching the TypeScript application.

```javascript
#!/usr/bin/env node
import('../src/main.tsx').catch(err => {
  console.error('OpenClaude failed to start:', err);
  process.exit(1);
});

```

This pattern—dynamic import with error handling—ensures graceful failure reporting if the main module fails to load, while keeping the wrapper itself minimal and fast to parse.

## Command Orchestration in [`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts)

The dynamic import resolves to **[`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts)**, the central orchestration module. This file performs three critical tasks during startup:

- **Initialize profiling** via `profileCheckpoint('main_tsx_entry')` from [`src/utils/startupProfiler.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/startupProfiler.ts)
- **Parse command-line flags** using `@commander-js/extra-typings`
- **Route execution** between fast-path commands and full interactive mode

### Flag Parsing Implementation

```typescript
// From src/commands.ts
import { Command } from '@commander-js/extra-typings';
const cli = new Command('openclaude');

cli
  .option('--remote', 'Run in headless remote mode')
  .option('--name <title>', 'Give the session a custom name')
  .option('--file <path>', 'Load conversation from file')
  // ... additional options
  .action(async (opts) => {
    // Fast-path: skip UI for non-interactive commands
    if (opts.remote) {
      await runHeadlessJob(opts);
      return;
    }
    // Continue to interactive startup
    await startInteractiveSession(opts);
  });

await cli.parseAsync(process.argv);

```

The [`src/cli/handlers/util.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/handlers/util.tsx) module contains the underlying flag definitions and validation logic used by this parser.

## Fast-Path Handling for Non-Interactive Commands

Certain subcommands—`openclaude doctor`, `openclaude version`, and `openclaude --remote`—bypass the full UI initialization. This fast-path block (around line 4200 in [`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts)) exits early after completing their specific task, avoiding the overhead of React-Ink mounting and background service startup.

## Profile Loading and Provider Validation

For interactive sessions, the CLI loads user configuration through **[`src/utils/settings/settingsCache.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/settings/settingsCache.ts)**. This stage:

1. Reads API keys and provider selections from disk
2. Caches frequently accessed settings in memory
3. Validates provider configurations via **[`src/utils/providerValidation.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerValidation.ts)**

Failed validation here triggers early exit with actionable error messages, preventing the UI from launching into a broken state.

## Mounting the React-Ink Application: [`src/main.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/main.tsx)

With configuration validated, control passes to **[`src/main.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/main.tsx)**, the React-Ink entry point:

```tsx
// src/main.tsx
import React from 'react';
import { render } from 'ink';
import { App } from './components/App';
import { profileCheckpoint } from './utils/startupProfiler';

profileCheckpoint('main_tsx_entry');  // Mark UI boot start
render(<App />);                      // Mount interactive interface

```

The `profileCheckpoint` call records timing data to **[`src/utils/startupProfiler.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/startupProfiler.ts)**, which aggregates metrics and eventually reports `tengu_startup_perf` events to Statsig for performance monitoring.

## Background Service Initialization

While the UI renders, several background services initialize asynchronously:

| Service | Source File | Purpose |
|---------|-------------|---------|
| **MCP Connector** | [`src/services/mcpServerApproval.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/services/mcpServerApproval.tsx) | Manages Model-Control-Plane server connections |
| **Error Reporting** | [`src/utils/sentry.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/sentry.ts) | Lazy-loaded Sentry integration for crash tracking |
| **Session Management** | [`src/utils/sessionStart.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/sessionStart.ts) | Initializes conversation state and history |

The Sentry integration specifically uses lazy loading to avoid blocking the critical path—errors during early startup are queued and reported once the client initializes.

## REPL Rendering and Query Engine Binding

The root `App` component renders **[`src/screens/REPL.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/screens/REPL.tsx)**, which implements the core interactive loop:

```tsx
// src/screens/REPL.tsx
import { useAppState } from '../hooks/useAppState';
import { QueryEngine } from '../QueryEngine';

export const REPL = () => {
  const { state, dispatch } = useAppState();

  const handleUserMessage = async (input: string) => {
    dispatch({ type: 'ADD_USER_MESSAGE', payload: input });
    
    const result = await QueryEngine.run(input, {
      provider: state.activeProvider,
      tokenBudget: getTokenBudget(state),
      // ... additional context
    });
    
    dispatch({ type: 'ADD_ASSISTANT_MESSAGE', payload: result });
  };

  return (
    <Box flexDirection="column">
      <ChatHistory messages={state.messages} />
      <InputPrompt onSubmit={handleUserMessage} />
    </Box>
  );
};

```

## The Complete Query Lifecycle

Each user message triggers the **`QueryEngine`** execution flow defined in **[`src/QueryEngine.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/QueryEngine.ts)**:

1. **Provider resolution** via [`src/utils/providerProfiles.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerProfiles.ts) determines which model handles the request
2. **Token budgeting** from [`src/query/tokenBudget.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/query/tokenBudget.ts) enforces usage limits
3. **Streaming response** pipes through the Ink component tree in real-time

## Startup Profiling and Telemetry

The **[`src/utils/startupProfiler.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/startupProfiler.ts)** module tracks timing across all initialization phases:

```typescript
// Conceptual structure from startupProfiler.ts
export function profileCheckpoint(phase: string): void {
  checkpoints[phase] = performance.now();
}

export function reportStartupMetrics(): void {
  const duration = checkpoints['main_tsx_entry'] - checkpoints['commands_ts_entry'];
  statsig.logEvent({ eventName: 'tengu_startup_perf', value: duration });
}

```

This data enables the maintainers to identify regression in cold-start performance across releases.

## Summary

- **`bin/openclaude`** launches the Node process with error handling
- **[`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts)** parses flags, profiles startup, and routes to fast-path or interactive flow
- **[`src/utils/settings/settingsCache.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/settings/settingsCache.ts)** loads and validates user configuration
- **[`src/main.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/main.tsx)** mounts the React-Ink application root
- **[`src/services/mcpServerApproval.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/services/mcpServerApproval.tsx)**, **[`src/utils/sentry.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/sentry.ts)**, and **[`src/utils/sessionStart.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/sessionStart.ts)** initialize background services
- **[`src/screens/REPL.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/screens/REPL.tsx)** renders the interactive interface bound to **[`src/QueryEngine.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/QueryEngine.ts)**
- **[`src/utils/startupProfiler.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/startupProfiler.ts)** records and reports performance metrics to Statsig

## Frequently Asked Questions

### What file actually parses the OpenClaude CLI arguments?

The **[`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts)** module contains the argument parsing logic, delegating to **@commander-js/extra-typings** for type-safe flag definitions. The underlying handler utilities reside in **[`src/cli/handlers/util.tsx`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/handlers/util.tsx)**.

### How does OpenClaude avoid slow startup for simple commands?

A **fast-path block** in [`src/commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/commands.ts) (approximately line 4200) detects non-interactive commands like `--remote`, `doctor`, or `version` and exits immediately after completion—skipping React-Ink mounting, profile loading, and background service initialization.

### Where is startup performance measured?

The **[`src/utils/startupProfiler.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/startupProfiler.ts)** module records phase timings starting from `profileCheckpoint('main_tsx_entry')` and reports aggregated metrics as Statsig `tengu_startup_perf` events. Checkpoints are placed at module boundaries including [`commands.ts`](https://github.com/Gitlawb/openclaude/blob/main/commands.ts) entry and [`main.tsx`](https://github.com/Gitlawb/openclaude/blob/main/main.tsx) mount.

### What determines which AI provider handles a query?

**[`src/utils/providerProfiles.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/providerProfiles.ts)** resolves the active provider at session startup based on user settings from [`src/utils/settings/settingsCache.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/utils/settings/settingsCache.ts). The `QueryEngine` consults this resolution for each request, enabling per-message provider overrides through explicit flags.