# Bun CLI Parser & Interactive System Design ## Three Implementation Approaches ### Approach 1: TypeScript-First with Zig Backend **Pros**: Familiar API, easy to extend, good DX **Cons**: JS overhead for parsing ```typescript // User-facing API const cli = Bun.CLI.create({ name: "myapp", version: "1.0.0", flags: { verbose: { type: "boolean", short: "v", default: false }, port: { type: "number", short: "p", default: 3000 }, files: { type: "array", of: "string" }, config: { type: "string", env: "MY_APP_CONFIG" } } }); const args = cli.parse(); // Interactive mode if (!args.config && cli.isTTY) { args.config = await cli.prompt.select({ message: "Choose config", choices: ["dev", "prod", "test"] }); } ``` ### Approach 2: Pure Zig with Code Generation **Pros**: Zero overhead, compile-time validation, fastest possible **Cons**: Less flexible, harder to maintain ```zig // Generated from schema at build time const MyAppCLI = generateCLI(.{ .name = "myapp", .flags = .{ .verbose = .{ .type = .boolean, .short = 'v' }, .port = .{ .type = .number, .short = 'p', .default = 3000 }, .files = .{ .type = .array, .of = .string }, }, }); // Runtime usage const args = try MyAppCLI.parse(std.os.argv); ``` ### Approach 3: Hybrid with Smart Optimization (RECOMMENDED) **Pros**: Best of both worlds, progressive enhancement **Cons**: More complex implementation ```typescript // Fast path for simple cases const args = Bun.CLI.parseSimple(); // Zero alloc for basic flags // Full featured for complex cases const cli = new Bun.CLI({ // Schema definition schema: { commands: { serve: { flags: { port: { type: "number", short: "p" }, host: { type: "string", short: "h" } } } } }, // Performance hints hints: { maxArgs: 10, // Pre-allocate buffers commonFlags: ["v", "h", "help"], // Optimize these lazyInteractive: true // Load interactive only when needed } }); ``` ## Core Implementation Plan ### Phase 1: Fast Flag Parser (Week 1) ```typescript // Core parser with zero allocations for common cases export namespace Bun.CLI { export interface ParseOptions { // Stop at first non-flag stopEarly?: boolean; // Allow unknown flags allowUnknown?: boolean; // Parse numbers/booleans autoType?: boolean; } export function parse( args?: string[], options?: ParseOptions ): Record; // Fast path for simple cases export function parseSimple(args?: string[]): { _: string[]; [key: string]: any; }; } ``` ### Phase 2: Type-Safe Schema (Week 2) ```typescript // Type-safe flag definitions export interface FlagSchema { type: "string" | "number" | "boolean" | "array" | "enum"; short?: string; long?: string; default?: any; required?: boolean; env?: string; // Environment variable fallback validate?: (value: any) => boolean | string; transform?: (value: any) => any; } // Advanced array handling export interface ArrayFlagSchema extends FlagSchema { type: "array"; of: "string" | "number"; separator?: string; // For comma-separated values accumulate?: boolean; // Multiple --flag values } ``` ### Phase 3: Interactive System (Week 3) ```typescript export namespace Bun.CLI.Interactive { // TTY detection with fallback export const isTTY: boolean; // Core prompt types export interface PromptOptions { message: string; default?: any; validate?: (input: any) => boolean | string; // Non-TTY fallback fallback?: () => any; } export async function text(options: PromptOptions): Promise; export async function confirm(options: PromptOptions): Promise; export async function select(options: SelectOptions): Promise; export async function multiselect(options: MultiSelectOptions): Promise; // Advanced: Form with multiple fields export async function form(schema: FormSchema): Promise; } ``` ### Phase 4: Performance Optimizations ```zig // Zig backend for hot paths pub const FastParser = struct { allocator: std.mem.Allocator, args_buffer: [256][]const u8, // Stack allocation for common case pub fn parse(args: []const []const u8) ParseResult { // Single pass parsing // Zero-copy string slicing // Compile-time type coercion } }; // Incremental renderer for interactive mode pub const IncrementalRenderer = struct { last_frame: []u8, dirty_regions: std.ArrayList(Region), pub fn render(content: []const u8) !void { // Diff-based updates // Batched escape sequences // Adaptive frame rate } }; ``` ## Performance Benchmarks Target ``` Simple flag parsing (10 args): - Target: < 100ns - Baseline (minimist): ~1μs Complex parsing (100 args, nested commands): - Target: < 1μs - Baseline (yargs): ~50μs Interactive prompt render: - Target: < 1ms per frame - 60fps for smooth animations Memory usage: - Zero allocations for < 16 args - Single arena for complex parsing - Reusable buffers for interactive ``` ## Edge Cases Handled 1. **No TTY**: Graceful fallback to simple prompts or defaults 2. **Piped input**: Detect and handle stdin/stdout pipes 3. **CI environment**: Auto-detect CI and disable interactive 4. **Windows Terminal**: Special handling for Windows console 5. **SSH sessions**: Detect and adapt rendering 6. **Screen readers**: Accessibility mode with plain text 7. **Partial args**: Handle incomplete flag values 8. **Unicode**: Full UTF-8 support in prompts 9. **Signals**: Proper cleanup on SIGINT/SIGTERM 10. **Large inputs**: Stream processing for huge arg lists ## API Examples ### Basic Usage ```typescript // Simple parsing const args = Bun.CLI.parse(); console.log(args.verbose, args.port); // With schema const cli = Bun.CLI.create({ flags: { verbose: { type: "boolean", short: "v" }, port: { type: "number", default: 3000 } } }); const { verbose, port } = cli.parse(); ``` ### Interactive Usage ```typescript // Auto-detect TTY and fallback const name = await Bun.CLI.prompt.text({ message: "Your name?", fallback: () => process.env.USER || "anonymous" }); // Complex form const config = await Bun.CLI.prompt.form({ fields: { database: { type: "select", choices: ["postgres", "mysql", "sqlite"] }, port: { type: "number", default: 5432 }, ssl: { type: "confirm", default: true } } }); ``` ### Advanced Subcommands ```typescript const cli = Bun.CLI.create({ commands: { serve: { handler: (args) => startServer(args), flags: { port: { type: "number" } } }, build: { handler: (args) => runBuild(args), flags: { watch: { type: "boolean" } } } } }); await cli.run(); ``` ## Testing Strategy 1. **Unit tests**: Each parser component 2. **Integration tests**: Full CLI flows 3. **Performance tests**: Benchmark suite 4. **TTY simulation**: Mock terminal tests 5. **Cross-platform**: Windows, macOS, Linux 6. **Fuzzing**: Random input generation 7. **Memory tests**: Leak detection 8. **Stress tests**: Large arg counts