@portel/cli
v1.1.0
Published
CLI toolkit for building terminal applications - formatting, progress, fuzzy matching, logging
Maintainers
Readme
@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
npm install @portel/cliFeatures
Output Formatting
Format data as tables, trees, lists, or cards with automatic detection.
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 JSONProgress Indicators
Show spinners and progress bars for long-running operations.
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.
import { printSuccess, printError, printInfo, printWarning } from '@portel/cli';
printSuccess('Operation completed'); // ✓ green
printError('Something failed'); // ✗ red
printInfo('FYI...'); // ℹ blue
printWarning('Be careful'); // ⚠ yellowFuzzy Matching
Find similar strings for "did you mean?" suggestions.
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.
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.
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.
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
