organize --help

This commit is contained in:
Chris Wanstrath 2026-02-09 20:47:17 -08:00
parent 9517f6d4b2
commit 86a91469be

View File

@ -1,6 +1,7 @@
import { program } from 'commander' import { program } from 'commander'
import color from 'kleur' import color from 'kleur'
import pkg from '../../package.json' import pkg from '../../package.json'
import { import {
cleanApp, cleanApp,
@ -41,13 +42,11 @@ program
} }
return '' return ''
}) })
.addHelpCommand(false)
.configureOutput({ .configureOutput({
writeOut: (str) => { writeOut: (str) => {
const colored = str const colored = str
.replace(/^(Usage:)/gm, color.yellow('$1')) .replace(/^([A-Z][\w ]*:)/gm, color.yellow('$1'))
.replace(/^(Commands:)/gm, color.yellow('$1'))
.replace(/^(Options:)/gm, color.yellow('$1'))
.replace(/^(Arguments:)/gm, color.yellow('$1'))
process.stdout.write(colored) process.stdout.write(colored)
}, },
}) })
@ -56,44 +55,88 @@ program
.command('version', { hidden: true }) .command('version', { hidden: true })
.action(() => console.log(program.version())) .action(() => console.log(program.version()))
program // Apps
.command('config')
.description('Show current host configuration')
.action(configShow)
program
.command('info')
.description('Show info for an app')
.argument('[name]', 'app name (uses current directory if omitted)')
.action(infoApp)
program program
.command('list') .command('list')
.helpGroup('Apps:')
.description('List all apps') .description('List all apps')
.option('-t, --tools', 'show only tools') .option('-t, --tools', 'show only tools')
.option('-a, --apps', 'show only apps (exclude tools)') .option('-a, --apps', 'show only apps (exclude tools)')
.action(listApps) .action(listApps)
program
.command('info')
.helpGroup('Apps:')
.description('Show info for an app')
.argument('[name]', 'app name (uses current directory if omitted)')
.action(infoApp)
program
.command('new')
.helpGroup('Apps:')
.description('Create a new toes app')
.argument('[name]', 'app name (uses current directory if omitted)')
.option('--ssr', 'SSR template with pages directory (default)')
.option('--bare', 'minimal template with no pages')
.option('--spa', 'single-page app with client-side rendering')
.action(newApp)
program
.command('get')
.helpGroup('Apps:')
.description('Download an app from server')
.argument('<name>', 'app name')
.action(getApp)
program
.command('open')
.helpGroup('Apps:')
.description('Open an app in browser')
.argument('[name]', 'app name (uses current directory if omitted)')
.action(openApp)
program
.command('rename')
.helpGroup('Apps:')
.description('Rename an app')
.argument('[name]', 'app name (uses current directory if omitted)')
.argument('<new-name>', 'new app name')
.action(renameApp)
program
.command('rm')
.helpGroup('Apps:')
.description('Remove an app from the server')
.argument('[name]', 'app name (uses current directory if omitted)')
.action(rmApp)
// Lifecycle
program program
.command('start') .command('start')
.helpGroup('Lifecycle:')
.description('Start an app') .description('Start an app')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.action(startApp) .action(startApp)
program program
.command('stop') .command('stop')
.helpGroup('Lifecycle:')
.description('Stop an app') .description('Stop an app')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.action(stopApp) .action(stopApp)
program program
.command('restart') .command('restart')
.helpGroup('Lifecycle:')
.description('Restart an app') .description('Restart an app')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.action(restartApp) .action(restartApp)
program program
.command('logs') .command('logs')
.helpGroup('Lifecycle:')
.description('Show logs for an app') .description('Show logs for an app')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.option('-f, --follow', 'follow log output') .option('-f, --follow', 'follow log output')
@ -113,59 +156,47 @@ program
program program
.command('stats') .command('stats')
.helpGroup('Lifecycle:')
.description('Show CPU and memory stats for apps') .description('Show CPU and memory stats for apps')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.action(statsApp) .action(statsApp)
program // Sync
.command('open')
.description('Open an app in browser')
.argument('[name]', 'app name (uses current directory if omitted)')
.action(openApp)
program
.command('get')
.description('Download an app from server')
.argument('<name>', 'app name')
.action(getApp)
program
.command('new')
.description('Create a new toes app')
.argument('[name]', 'app name (uses current directory if omitted)')
.option('--ssr', 'SSR template with pages directory (default)')
.option('--bare', 'minimal template with no pages')
.option('--spa', 'single-page app with client-side rendering')
.action(newApp)
program program
.command('push') .command('push')
.helpGroup('Sync:')
.description('Push local changes to server') .description('Push local changes to server')
.action(pushApp) .action(pushApp)
program program
.command('pull') .command('pull')
.helpGroup('Sync:')
.description('Pull changes from server') .description('Pull changes from server')
.option('-f, --force', 'overwrite local changes') .option('-f, --force', 'overwrite local changes')
.action(pullApp) .action(pullApp)
program program
.command('status') .command('status')
.helpGroup('Sync:')
.description('Show what would be pushed/pulled') .description('Show what would be pushed/pulled')
.action(statusApp) .action(statusApp)
program program
.command('diff') .command('diff')
.helpGroup('Sync:')
.description('Show diff of changed files') .description('Show diff of changed files')
.action(diffApp) .action(diffApp)
program program
.command('sync') .command('sync')
.helpGroup('Sync:')
.description('Watch and sync changes bidirectionally') .description('Watch and sync changes bidirectionally')
.action(syncApp) .action(syncApp)
program program
.command('clean') .command('clean')
.helpGroup('Sync:')
.description('Remove local files not on server') .description('Remove local files not on server')
.option('-f, --force', 'skip confirmation') .option('-f, --force', 'skip confirmation')
.option('-n, --dry-run', 'show what would be removed') .option('-n, --dry-run', 'show what would be removed')
@ -173,6 +204,7 @@ program
const stash = program const stash = program
.command('stash') .command('stash')
.helpGroup('Sync:')
.description('Stash local changes') .description('Stash local changes')
.action(stashApp) .action(stashApp)
@ -186,8 +218,17 @@ stash
.description('List all stashes') .description('List all stashes')
.action(stashListApp) .action(stashListApp)
// Config
program
.command('config')
.helpGroup('Config:')
.description('Show current host configuration')
.action(configShow)
const env = program const env = program
.command('env') .command('env')
.helpGroup('Config:')
.description('Manage environment variables') .description('Manage environment variables')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.action(envList) .action(envList)
@ -209,28 +250,17 @@ env
program program
.command('versions') .command('versions')
.helpGroup('Config:')
.description('List deployed versions') .description('List deployed versions')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.action(versionsApp) .action(versionsApp)
program program
.command('rollback') .command('rollback')
.helpGroup('Config:')
.description('Rollback to a previous version') .description('Rollback to a previous version')
.argument('[name]', 'app name (uses current directory if omitted)') .argument('[name]', 'app name (uses current directory if omitted)')
.option('-v, --version <version>', 'version to rollback to (prompts if omitted)') .option('-v, --version <version>', 'version to rollback to (prompts if omitted)')
.action((name, options) => rollbackApp(name, options.version)) .action((name, options) => rollbackApp(name, options.version))
program
.command('rm')
.description('Remove an app from the server')
.argument('[name]', 'app name (uses current directory if omitted)')
.action(rmApp)
program
.command('rename')
.description('Rename an app')
.argument('[name]', 'app name (uses current directory if omitted)')
.argument('<new-name>', 'new app name')
.action(renameApp)
export { program } export { program }