| name | stacks-buddy |
| description | Use when working with the Stacks CLI (buddy/bud/stacks/stx) — understanding all 50+ commands with their flags and options, adding custom commands, the make:* scaffolding commands, development server commands, build commands, deployment commands, email/mail commands, environment management, or domain/DNS commands. Covers @stacksjs/buddy and all CLI command files. |
| license | MIT |
| compatibility | Bun >= 1.3.0, TypeScript |
| allowed-tools | Read Edit Write Bash Grep Glob |
Stacks Buddy CLI
The complete CLI runtime for the Stacks framework with 50+ commands, lazy-loaded for fast cold starts.
Key Paths
- Core package:
storage/framework/core/buddy/src/
- CLI entry point:
storage/framework/core/buddy/src/cli.ts
- Commands directory:
storage/framework/core/buddy/src/commands/
- Lazy command registry:
storage/framework/core/buddy/src/lazy-commands.ts
- Config system:
storage/framework/core/buddy/src/config.ts
- Shell entry:
buddy (shell script at project root that invokes bun run ./storage/framework/core/buddy/src/cli.ts)
- Application commands:
app/Commands/
- Command registry:
app/Commands.ts
- Make templates:
storage/framework/defaults/
CLI Aliases
All invoke the same CLI: ./buddy, bud, stacks, stx
Architecture
Lazy Loading System
Commands are lazy-loaded for performance. The lazy-commands.ts file maps command names to their file paths and export names. When a command is invoked, only that command's module is loaded.
const commandRegistry: Record<string, CommandLoader> = {
'about': { path: './commands/about.ts', exportName: 'about' },
'build': { path: './commands/build.ts', exportName: 'build' },
}
Key functions:
loadCommand(name, buddy) - Load a single command lazily
loadCommands(names, buddy) - Load multiple commands with 5-second timeout per command
loadCommandGroup(groupName, buddy) - Load related commands ('development', 'database', 'scaffolding', 'deployment', 'info')
loadAllCommands(buddy) - Load all commands (for interactive mode or list command)
getCommandsToLoad(args) - Determine which commands to load based on CLI arguments
getCommandNames() - Get all command names without loading them (for autocomplete)
Command groups defined in lazy-commands.ts:
minimal: version, help
development: dev, build, test, lint
database: migrate, seed, fresh
scaffolding: make, generate
deployment: deploy, release, cloud
info: about, doctor, list
Startup Flow (cli.ts)
- Parse args to determine requested command
- Skip expensive setup for minimal commands (--version, --help, help, version)
- For full commands: load setup + key commands, check APP_KEY, load lazy commands, load user commands from
app/Commands/
- For no arguments with interactive TTY: show interactive menu with select prompt
- Parse and execute
Commands that skip APP_KEY check
build, lint, lint:fix, test, test:types, test:unit, test:feature, typecheck, types:fix, types:generate, clean, fresh, about, doctor, setup, setup:ssl, setup:oh-my-zsh, deploy
buddy.config.ts Support
Optional config file at project root. Validated with validateConfig(). Supports:
interface BuddyConfig {
theme?: 'default' | 'dracula' | 'nord' | 'solarized' | 'monokai'
emoji?: boolean
commands?: Array<(cli: CLI) => void>
defaultFlags?: { verbose?: boolean, quiet?: boolean, debug?: boolean }
aliases?: Record<string, string>
plugins?: Array<BuddyPlugin | string>
}
Config files searched in order: buddy.config.ts, buddy.config.js, .buddy.config.ts, .buddy.config.js
Plugin System
Plugins can register commands, add hooks (beforeCommand/afterCommand):
interface BuddyPlugin {
name: string
version?: string
setup: (cli: CLI) => void | Promise<void>
hooks?: {
beforeCommand?: (context: any) => void | Promise<void>
afterCommand?: (context: any) => void | Promise<void>
}
}
Interactive Mode
When buddy is invoked with no arguments on an interactive TTY, it shows a select menu:
- Start development server
- Build for production
- Run tests
- List all commands
- Run health checks
- Show system information
- Show help
- Exit
Development Commands
buddy dev [server] - Start development server(s)
buddy dev
buddy dev frontend
buddy dev api
buddy dev docs
buddy dev dashboard
buddy dev desktop
buddy dev system-tray
buddy dev -f/--frontend
buddy dev -a/--api
buddy dev -c/--components
buddy dev -d/--dashboard
buddy dev -k/--desktop
buddy dev -o/--docs
buddy dev -s/--system-tray
buddy dev -i/--interactive
buddy dev -l/--with-localhost
buddy dev -p/--project [name]
buddy dev --verbose
Sub-commands with aliases:
buddy dev:components
buddy dev:docs
buddy dev:desktop
buddy dev:api
buddy dev:frontend
buddy dev:dashboard
buddy dev:system-tray
When running buddy dev with no flags (all servers), it:
- Prints Vite-style output showing all server URLs
- Sets
STACKS_PROXY_MANAGED=1 env var
- Starts frontend, API, docs, dashboard servers in parallel
- If APP_URL has a custom domain, starts reverse proxy (rpx) for HTTPS with subdomains
- Uses environment variables:
PORT (default 3000), PORT_API (3008), PORT_DOCS (3006), PORT_ADMIN (3002)
- Cleans up child processes on SIGINT/SIGTERM via
process.kill(0, 'SIGKILL')
Reverse proxy (rpx):
- Proxies
localhost:PORT -> domain, localhost:PORT_API -> api.domain, etc.
- SSL certs stored at
~/.stacks/ssl/
- Only starts when APP_URL is set to a custom domain (not localhost)
Build Commands
buddy build [type] - Build for production
buddy build
buddy build components
buddy build vue
buddy build web-components
buddy build functions
buddy build views
buddy build docs
buddy build buddy
buddy build cli
buddy build stacks
buddy build server
Flags:
buddy build -c/--components
buddy build -w/--web-components
buddy build -f/--functions
buddy build -p/--views/--pages
buddy build -d/--docs
buddy build -b/--buddy
buddy build -s/--stacks
buddy build --server
Sub-commands with aliases:
buddy build:components
buddy build:cli
buddy build:server
buddy build:functions
buddy build:web-components
buddy build:docs
buddy build:core
buddy build:desktop
buddy build:stacks
Database Commands
buddy migrate - Run database migrations
buddy migrate
buddy migrate -d/--diff
buddy migrate -a/--auth
buddy migrate --verbose
buddy migrate:fresh
buddy migrate:fresh -s/--seed
buddy migrate:fresh -a/--auth
buddy migrate:fresh -d/--diff
buddy migrate:dns
Both migrate and migrate:fresh validate that models exist in app/Models or storage/framework/defaults/models before running.
buddy seed - Seed database
buddy seed
buddy seed --verbose
Code Generation (make:*)
buddy make [type] - Scaffolding commands
All make commands accept -n/--name [name] and --verbose flags.
buddy make:action [name]
buddy make:certificate
buddy make:command [name]
--signature [sig]
--description [desc]
--no-register
buddy make:component [name]
buddy make:database [name]
buddy make:factory [name]
buddy make:function [name]
buddy make:job [name]
-q/--queue [queue]
-c/--class
-t/--tries [n]
-b/--backoff [n]
buddy make:lang [name]
buddy make:migration [name]
buddy make:model [name]
buddy make:notification [name]# create notification
-e/--email
-c/--chat
-s/--sms
buddy make:policy [name]
-m/--model [model]
--no-register
buddy make:resource [name]
-m/--model [model]
-c/--collection
buddy make:queue-table
buddy make:stack [name]
buddy make:view [name]
Code Generation (generate)
buddy generate - Generate artifacts
buddy generate
buddy generate -t/--types
buddy generate -e/--entries
buddy generate -w/--web-types
buddy generate -c/--custom-data
buddy generate -i/--ide-helpers
buddy generate -c/--component-meta
buddy generate -p/--pantry
buddy generate -o/--openapi
buddy generate --core-symlink
buddy generate:types
buddy generate:entries
buddy generate:web-types
buddy generate:vscode-custom-data
buddy generate:ide-helpers
buddy generate:component-meta
buddy generate:pantry-config
buddy generate:openapi-spec
buddy generate:migrations
buddy generate:core-symlink
Environment Management
buddy env:* - Manage environment variables
buddy env:get [key]
-f/--file [file]
--format [json|shell|eval]
-a/--all
-p/--pretty
buddy env:set [key] [value]
-f/--file [file]
-fk/--file-keys [path]
--plain
buddy env:encrypt [key]
-f/--file [file]
-fk/--file-keys [path]
-o/--stdout
-ek/--exclude-key [key]
buddy env:decrypt [key]
-f/--file [file]
-fk/--file-keys [path]
-o/--stdout
buddy env:keypair [key]
-f/--file [file]
--format [json|shell]
buddy env:rotate [key]
-f/--file [file]
-ek/--exclude-key [key]
-o/--stdout
buddy env:check
-f/--file [file]
env:check validates: .env file exists, variable count, APP_KEY presence, encryption keys (DOTENV_PUBLIC_KEY/DOTENV_PRIVATE_KEY), .env.keys file.
Cloud & Deployment
buddy deploy - Deploy to cloud
Handles full deployment workflow: prerequisites check, pantry install, env setup, APP_KEY, AWS credentials (from .env.{env} or ~/.aws/credentials), domain setup, email DNS records (DKIM, MX, SPF, DMARC), mail user creation.
buddy cloud - Cloud management
buddy cloud --ssh
buddy cloud --diff
buddy cloud --invalidate-cache
--paths [paths]
buddy cloud:add --jump-box
buddy cloud:remove
--jump-box
--force
--yes
buddy cloud:optimize-cost
buddy cloud:cleanup
buddy cloud:invalidate-cache
--paths [paths]
buddy cloud:diff
cloud:remove uses undeployStack() from storage/framework/core/actions/deploy and handles stuck stacks by creating temporary IAM roles.
Domain & DNS
buddy domains:* - Domain management
buddy domains:purchase <domain>
--years <n>
--privacy
--auto-renew
--first-name/--last-name/etc.
--admin-*/--tech-*
--privacy-admin/--privacy-tech/--privacy-registrant
--contact-type <type>
buddy domains:add <domain>
buddy domains:remove <domain>
--yes
buddy dns [domain] - DNS query tool (uses @stacksjs/dnsx)
buddy dns [domain]
buddy dns -q/--query <query>
buddy dns -t/--type <type>
buddy dns -n/--nameserver <ns>
buddy dns --class <class>
buddy dns -U/--udp
buddy dns -T/--tcp
buddy dns -S/--tls
buddy dns -H/--https
buddy dns -1/--short
buddy dns -J/--json
Email / Mail Commands
buddy email:* - Email management (SES/S3 based)
buddy email
buddy email:verify
buddy email:test [recipient]
buddy email:list
buddy email:logs
-n/--lines <count>
buddy email:status
buddy email:inbox [mailbox]
-n/--limit <count>
--raw <id>
--bucket <name>
buddy email:reprocess
--bucket <name>
--prefix <prefix>
--domain <domain>
Email inbox reads from S3 in two strategies:
mailboxes/{domain}/{user}/inbox.json (processed inbox)
- Falls back to raw emails in
inbox/ or incoming/ prefixes
buddy mail:* - Mail server management
buddy mail:user:add <email>
--password <password>
buddy mail:user:list
buddy mail:user:delete <email>
buddy mail:proxy
--port <port>
--api <url>
buddy mail:test
buddy mail:credentials [email]
buddy mail:logs
-n/--lines <count>
-f/--follow
--filter <pattern>
buddy mail:status
buddy mail:port25:request
--provider <aws|hetzner>
--instance-id <id>
--elastic-ip <ip>
--rdns <hostname>
--use-case <text>
buddy mail:port25:status
--provider <aws|hetzner>
buddy mail:server
--port <port>
Code Quality
buddy lint - Lint codebase (uses pickier)
buddy lint
buddy lint -f/--fix
buddy lint:fix
buddy format
-w/--write
-c/--check
buddy format:check
Internal implementation: runs bunx --bun pickier run --mode lint --config ./pickier.config.ts (with --fix when requested).
buddy test - Run test suite
buddy test
buddy test -f/--feature
buddy test -u/--unit
buddy test:unit
buddy test:feature
buddy test:ui
buddy test:types
Project Management
buddy fresh
buddy install
buddy clean
buddy outdated
buddy add
buddy upgrade
-v/--version <version>
--canary
--stable
-f/--force
buddy upgrade:all
buddy upgrade:dependencies
buddy upgrade:bun
buddy upgrade:shell
buddy upgrade:binary
Maintenance Mode
buddy down
--message [msg]
--retry [seconds]
--secret [token]
--allow [ip...]
--redirect [url]
--status [code]
buddy up
buddy status
Uses @stacksjs/server module's down(), up(), isDownForMaintenance(), maintenancePayload() functions.
Other Commands
buddy about
buddy doctor
buddy tinker
-e/--eval [expr]
--print [expr]
--no-banner
--preload [modules]
buddy key:generate
buddy commit
buddy changelog
-q/--quiet
-d/--dry-run
buddy release
--dry-run
buddy route
buddy share [type]
-p/--port <port>
--server <url>
--subdomain <name>
buddy search
buddy configure
buddy create
buddy completion [shell]
buddy ports
-l/--list
-c/--check
buddy ports:list
buddy ports:check
buddy setup
--skip-aws
--skip-keygen
buddy setup:ssl
-d/--domain [domain]
--skip-hosts
--skip-trust
buddy setup:oh-my-zsh
buddy telemetry
buddy list
buddy version
buddy saas
buddy sms
buddy phone
buddy http
buddy auth
buddy queue
buddy schedule
buddy package
buddy projects
buddy prepublish
buddy stacks
Adding Custom Commands
Method 1: Commands.ts Registry (preferred)
export default {
'inspire': 'Inspire',
'deploy-hooks': { file: 'DeployHooks', enabled: true, aliases: ['dh'] },
'disabled-cmd': { file: 'Disabled', enabled: false },
}
import type { CLI } from '@stacksjs/cli'
export default function (buddy: CLI) {
buddy
.command('inspire', 'Display inspirational quote')
.option('-t, --two', 'Show two quotes')
.action(async (options: { two?: boolean }) => {
console.log(randomQuote())
if (options.two) console.log(randomQuote())
})
}
Method 2: Auto-discovery (fallback)
If app/Commands.ts does not exist, all .ts files in app/Commands/ are auto-discovered and loaded. Each must export a default function that receives the CLI instance.
Method 3: buddy.config.ts
export default {
commands: [
(cli) => {
cli.command('custom', 'My custom command').action(() => console.log('Hello'))
},
],
aliases: {
'r': 'release',
'dep': 'deploy',
},
}
Gotchas
- Application commands go in
app/Commands/, framework commands in storage/framework/core/buddy/src/commands/
- The
buddy file at root is a shell script that bootstraps pantry, ensures Bun is available, then runs bun run ./storage/framework/core/buddy/src/cli.ts
- Commands are lazy-loaded with a 5-second timeout per command to prevent startup hangs
- The
list command loads ALL commands to display them
- When no command matches, the CLI loads only
version for minimal overhead
- APP_KEY is auto-generated if not set (except for commands in the skipAppKeyCheck list)
buddy dev with custom APP_URL starts a reverse proxy (rpx) that handles SSL certificates via ~/.stacks/ssl/
dev:frontend has aliases dev:pages and dev:views
dev:dashboard has alias dev:admin
dev:system-tray has alias dev:tray
build with no flags defaults to building the Stacks framework (--stacks)
cloud:remove handles stuck CloudFormation stacks by creating temporary IAM roles with AdministratorAccess
mail:* commands load AWS credentials from .env.production and ~/.aws/credentials
share uses localtunnel and auto-starts companion dev servers (API, docs) for frontend shares
- Console listeners can be registered via
app/Listeners/Console.ts