A powerful command-line interface framework for building interactive CLI applications, featuring command parsing, prompts, spinners, and beautiful output formatting.
Installation
bunadd@stacksjs/cli
Basic Usage
import{CLI,Command,log,prompt,spin}from'@stacksjs/cli'// Create a simple commandconstcli=newCLI('myapp').command('greet','Greet a user').option('-n, --name <name>','Name to greet').action((options)=>{log.info(`Hello, ${options.name || 'World'}!`)})cli.run()
Creating Commands
Basic Command
import{Command}from'@stacksjs/cli'constcommand=newCommand('deploy').description('Deploy the application').action(()=>{console.log('Deploying...')})
Command with Options
constcommand=newCommand('build').description('Build the application').option('-e, --env <environment>','Target environment','production').option('-m, --minify','Minify output',false).option('-w, --watch','Watch for changes').action((options)=>{console.log(`Building for ${options.env}`)if(options.minify)console.log('Minification enabled')if(options.watch)console.log('Watch mode enabled')})
Command with Arguments
constcommand=newCommand('generate').description('Generate a resource').argument('<type>','Resource type (model, controller, action)').argument('<name>','Resource name').argument('[path]','Optional path').action((type,name,path,options)=>{console.log(`Generating ${type}: ${name}`)if(path)console.log(`At path: ${path}`)})
Subcommands
constcli=newCLI('buddy')cli.command('make').description('Generate resources').command('model').description('Generate a model').argument('<name>').action((name)=>{console.log(`Creating model: ${name}`)}).command('controller').description('Generate a controller').argument('<name>').action((name)=>{console.log(`Creating controller: ${name}`)})
Prompts
Text Input
import{prompt}from'@stacksjs/cli'constname=awaitprompt.text({message:'What is your name?',placeholder:'Enter your name',defaultValue:'Anonymous',validate:(value)=>{if(value.length<2)return'Name must be at least 2 characters'}})
Password Input
constpassword=awaitprompt.password({message:'Enter your password:',mask:'*',validate:(value)=>{if(value.length<8)return'Password must be at least 8 characters'}})
Confirm
constconfirmed=awaitprompt.confirm({message:'Are you sure you want to continue?',initialValue:false})if(confirmed){// Proceed}
Select (Single Choice)
constframework=awaitprompt.select({message:'Choose a framework:',options:[{value:'stx',label:'STX',hint:'Recommended'},{value:'react',label:'React'},{value:'svelte',label:'Svelte'},],initialValue:'stx'})
Multi-Select
constfeatures=awaitprompt.multiselect({message:'Select features to install:',options:[{value:'auth',label:'Authentication'},{value:'api',label:'API Routes'},{value:'queue',label:'Queue System'},{value:'cache',label:'Caching'},],required:true,initialValues:['auth']})
Autocomplete
constproject=awaitprompt.autocomplete({message:'Select a project:',options:async(input)=>{constprojects=awaitfetchProjects(input)returnprojects.map(p=>({value:p.id,label:p.name}))},placeholder:'Type to search...'})
constfile=awaitprompt.path({message:'Select a file:',type:'file',validate:(path)=>{if(!path.endsWith('.ts'))return'Must be a TypeScript file'}})constdirectory=awaitprompt.path({message:'Select output directory:',type:'directory'})
Output and Logging
Log Levels
import{log}from'@stacksjs/cli'log.info('Information message')log.success('Operation completed successfully')log.warn('Warning: Something might be wrong')log.error('Error: Something went wrong')log.debug('Debug information')
import{runCommand,exec}from'@stacksjs/cli'// Run a command and get resultconstresult=awaitrunCommand('npm install')if(result.isOk){console.log('Success:',result.value)}else{console.error('Error:',result.error)}// Execute with optionsconstoutput=awaitexec('ls -la',{cwd:'/path/to/dir',env:{NODE_ENV:'production'}})
Streaming Output
import{exec}from'@stacksjs/cli'constproc=exec('npm run build',{stdout:'pipe',stderr:'pipe'})forawait(constchunkofproc.stdout){process.stdout.write(chunk)}
import{dump,dd}from'@stacksjs/cli'// Dump variables for debuggingdump(someObject)dump(anotherObject,'Label')// Dump and exitdd(object)// Logs and exits with code 1
Echo
import{echo}from'@stacksjs/cli'echo('Simple output message')echo(object)// Pretty prints objects
CLI Configuration
Global Options
constcli=newCLI('myapp').version('1.0.0').description('My awesome CLI application').option('-v, --verbose','Enable verbose output').option('-c, --config <path>','Config file path')// Global options are available to all commandscli.command('build').action((options)=>{if(options.verbose)log.info('Verbose mode enabled')})
Help Generation
constcli=newCLI('myapp').command('serve').description('Start the development server').option('-p, --port <number>','Port to listen on','3000').option('-h, --host <hostname>','Host to bind to','localhost').example('myapp serve --port 8080').example('myapp serve -h 0.0.0.0')
Error Handling
cli.command('deploy').action(async()=>{try{awaitdeploy()}catch(error){log.error('Deployment failed:',error.message)process.exit(1)}})// Global error handlercli.catch((error)=>{log.error('An unexpected error occurred:',error)process.exit(1)})
Edge Cases
Handling Cancellation
constname=awaitprompt.text({message:'Enter name:'})// User pressed Ctrl+Cif(prompt.isCancel(name)){log.warn('Operation cancelled')process.exit(0)}
Handling Empty Input
constvalue=awaitprompt.text({message:'Enter value:',validate:(v)=>{if(!v||v.trim()===''){return'Value cannot be empty'}}})
Terminal Size
import{getTerminalSize}from'@stacksjs/cli'const{columns,rows}=getTerminalSize()if(columns<80){log.warn('Terminal is too narrow for optimal display')}
Non-Interactive Mode
import{isInteractive}from'@stacksjs/cli'if(!isInteractive()){// Running in CI or pipedlog.info('Running in non-interactive mode')// Use default values instead of prompts}else{constname=awaitprompt.text({message:'Name:'})}