# @portel/cli

CLI toolkit for building beautiful terminal applications. Provides formatting, progress indicators, fuzzy matching, and logging utilities.

## Part of the Portel Ecosystem

```
┌─────────────────────────────────────────────────────────────────┐
│                     @portel/cli (this package)                  │
│            CLI utilities: formatting, progress, logging         │
└─────────────────────────────────────────────────────────────────┘
                              ▲
                              │
┌─────────────────────────────────────────────────────────────────┐
│                        @portel/mcp                              │
│              MCP protocol: client, transport, config            │
└─────────────────────────────────────────────────────────────────┘
                              ▲
                              │
┌─────────────────────────────────────────────────────────────────┐
│                     @portel/photon-core                         │
│         Core library: schema extraction, generators, UI         │
└─────────────────────────────────────────────────────────────────┘
                              ▲
                              │
        ┌─────────────────────┼─────────────────────┐
        │                     │                     │
┌───────┴───────┐    ┌───────┴───────┐    ┌───────┴───────┐
│ @portel/photon│    │    lumina     │    │ @portel/ncp   │
│  CLI + BEAM   │    │  REST runtime │    │ MCP orchestr. │
└───────────────┘    └───────────────┘    └───────────────┘
```

**Use this package if:** You're building CLI tools and need beautiful output formatting, spinners, or fuzzy matching.

## Installation

```bash
npm install @portel/cli
```

## Features

### Output Formatting

Format data as tables, trees, lists, or cards with automatic detection.

```typescript
import { formatOutput, renderTable, renderTree } from '@portel/cli';

// Auto-detect format based on data shape
formatOutput([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);

// Explicit formats
formatOutput(data, 'table');  // Bordered table
formatOutput(data, 'tree');   // Indented tree
formatOutput(data, 'list');   // Bullet list
formatOutput(data, 'json');   // Pretty JSON
```

### Progress Indicators

Show spinners and progress bars for long-running operations.

```typescript
import { startSpinner, stopProgress, showProgress } from '@portel/cli';

// Simple spinner
startSpinner('Loading...');
await doWork();
stopProgress();

// With progress updates
showProgress('Processing files', 0, 100);
for (let i = 0; i <= 100; i++) {
  await processFile(i);
  showProgress('Processing files', i, 100);
}
stopProgress();
```

### Status Messages

Print colored status messages.

```typescript
import { printSuccess, printError, printInfo, printWarning } from '@portel/cli';

printSuccess('Operation completed');  // ✓ green
printError('Something failed');       // ✗ red
printInfo('FYI...');                  // ℹ blue
printWarning('Be careful');           // ⚠ yellow
```

### Fuzzy Matching

Find similar strings for "did you mean?" suggestions.

```typescript
import { FuzzyMatcher, findBestMatch } from '@portel/cli';

const matcher = new FuzzyMatcher();
const suggestions = matcher.findSuggestions('helo', ['hello', 'help', 'world']);
// => ['hello', 'help']

// Or simple best match
const best = findBestMatch('get-usrs', ['get-users', 'get-posts', 'delete-user']);
// => 'get-users'
```

### Text Utilities

Word wrapping, truncation, and formatting helpers.

```typescript
import { TextUtils } from '@portel/cli';

TextUtils.wrap('Long text...', { width: 80 });
TextUtils.truncate('Very long string', 20);
TextUtils.padRight('Name', 10);
```

### Execution Context

AsyncLocalStorage-based context for passing data through async call chains. Used by the Photon runtime to propagate caller identity and output handlers.

```typescript
import { executionContext, runWithContext, getContext } from '@portel/cli';
import type { CallerInfo, ExecutionContext } from '@portel/cli';

// Run a function with caller context (used by runtimes)
runWithContext({ caller: { id: 'user_123', name: 'Alice', anonymous: false } }, () => {
  const ctx = getContext();
  console.log(ctx?.caller?.id); // 'user_123'
});
```

**CallerInfo** — authenticated caller identity from MCP OAuth:

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | Stable user ID (JWT `sub` claim) |
| `name` | `string?` | Display name from OIDC profile |
| `anonymous` | `boolean` | `true` if no valid JWT was provided |
| `scope` | `string?` | OAuth scopes granted |
| `claims` | `Record<string, unknown>?` | Raw JWT claims |

### Logging

Structured logging with levels and formatting.

```typescript
import { createLogger } from '@portel/cli';

const logger = createLogger({ level: 'debug' });
logger.debug('Detailed info');
logger.info('General info');
logger.warn('Warning');
logger.error('Error occurred');
```

## API Reference

### Formatting Functions

| Function | Description |
|----------|-------------|
| `formatOutput(data, hint?)` | Auto-format and print data |
| `detectFormat(data)` | Detect best format for data |
| `renderTable(data)` | Render bordered table |
| `renderTree(data)` | Render indented tree |
| `renderList(items)` | Render bullet list |
| `renderCard(data)` | Render key-value card |

### Progress Functions

| Function | Description |
|----------|-------------|
| `startSpinner(message)` | Start a spinner |
| `showProgress(label, current, total)` | Show progress bar |
| `updateProgressMessage(message)` | Update spinner text |
| `stopProgress()` | Stop spinner/progress |
| `isProgressActive()` | Check if progress is active |

### Status Functions

| Function | Description |
|----------|-------------|
| `printSuccess(message)` | Print success (green ✓) |
| `printError(message)` | Print error (red ✗) |
| `printInfo(message)` | Print info (blue ℹ) |
| `printWarning(message)` | Print warning (yellow ⚠) |
| `printHeader(text)` | Print section header |

### Execution Context Functions

| Function | Description |
|----------|-------------|
| `executionContext` | `AsyncLocalStorage<ExecutionContext>` instance |
| `runWithContext(ctx, fn)` | Run function with a specific context |
| `getContext()` | Get current execution context |

### Types

| Type | Description |
|------|-------------|
| `ExecutionContext` | Context with `outputHandler?` and `caller?` |
| `CallerInfo` | Authenticated caller identity from MCP OAuth |

## License

MIT
