Command System Overview
Overview
Xec provides a powerful and extensible command system that supports both built-in commands and custom user-defined commands. This section covers the complete command reference for the Xec CLI.
The CLI entry point lives in apps/xec/src/main.ts. Built-in commands are the modules in apps/xec/src/commands/, each registering itself with the commander program. Dynamic commands are loaded by loadDynamicCommands() from apps/xec/src/utils/cli-command-manager.ts, and the BaseCommand class custom commands extend is defined in apps/xec/src/utils/command-base.ts.
Command Types
Xec supports three types of commands:
1. Built-in Commands
Core commands that are included with Xec:
- config - Manage Xec configuration (aliases:
conf,cfg) - copy - Copy files between targets (alias:
cp) - docker - Manage Docker containers, images, networks, and Compose via the fluent API (alias:
d) - forward - Forward ports between local and remote systems (alias:
fwd) - in - Execute commands inside Docker containers or Kubernetes pods
- inspect - Inspect configuration and targets
- logs - View and stream logs from various sources
- new - Create new Xec artifacts (scripts, configs, tasks)
- on - Execute commands on SSH hosts
- run - Run Xec scripts or tasks
- secrets - Manage secrets and credentials
- watch - Watch files for changes and execute commands
2. Dynamic Commands
Commands loaded from .xec/commands/ directory that extend Xec's functionality.
3. Script and Task Execution
Direct execution of JavaScript/TypeScript files as Xec scripts (xec ./script.ts), and execution of tasks defined in configuration by name (xec deploy).
Command Structure
All Xec commands follow a consistent structure:
xec [global-options] <command> [command-options] [arguments]
Global Options
Options that apply to all commands:
-v, --verbose- Enable verbose output-q, --quiet- Suppress non-error output--cwd <path>- Set current working directory--no-color- Disable colored output (setsNO_COLOR)-e, --eval <code>- Evaluate code directly--repl- Start interactive REPL
Options such as --dry-run, --profile, or config-file selection are per-subcommand, not global — check xec <command> --help for what each command supports. The configuration file location is controlled by the XEC_CONFIG environment variable rather than a global flag.
Command Resolution
When you run xec <something>, the CLI resolves it in the following order:
-e/--evaland--repl- Handled first, before any command lookup- Script files - If the first argument ends in
.js,.ts, or.mjs, or is an existing file, it runs as a script - Tasks - If the argument is not a registered command but matches a task in configuration, the task runs (task parameters are passed as
--param=valueor--param value) - Direct command execution - Target-prefixed direct execution (e.g. running a command on a configured target)
- Built-in and dynamic commands - Resolved by commander, including aliases
- Unknown command - Prints an error with "did you mean" typo suggestions
Target Selection
Many commands operate on targets (local, SSH, Docker, Kubernetes). The target selection pattern is:
xec <command> <target-pattern> [options]
Target patterns can be:
local- Local machinehosts.<name>- SSH hostcontainers.<name>- Docker containerpods.<name>- Kubernetes podhosts.*- All SSH hosts (wildcard){hosts.web1,hosts.web2}- Multiple targets (brace expansion)
Configuration Integration
Commands respect configuration from:
.xec/config.yaml- Project configuration- Command-specific defaults in configuration
- Environment variables (
XEC_*) - Command-line options (highest priority)
Error Handling
Errors are handled by handleError() from @xec-sh/ops, which prints an actionable message and exits with a code based on the error type:
Exit Codes
| Code | Error Type |
|---|---|
| 0 | Success |
| 1 | Generic / unhandled error |
| 2 | ValidationError |
| 3 | ConfigurationError |
| 4 | ModuleError |
| 5 | TaskError |
| 6 | RecipeError |
| 7 | NetworkError |
| 8 | FileSystemError |
| 9 | TimeoutError |
| 10 | File not found (ENOENT) |
| 11 | Permission denied (EACCES) |
| 12 | Not a directory (ENOTDIR) |
| 13 | Is a directory (EISDIR) |
Error Features
- Clear error messages with context
- Stack traces with
--verboseflag - Suggestions for common issues
- Colored output for error types
Command Development
You can create custom commands by:
- Creating a
.xec/commands/directory - Adding JavaScript/TypeScript files that extend
BaseCommand(fromapps/xec/src/utils/command-base.ts) - Commands are automatically loaded via
loadDynamicCommands()
Implementation Details:
- Commands must extend the
BaseCommandclass - Must implement
execute(args: any[]): Promise<void> - Command metadata (name, description, options, aliases, examples) is passed to the
BaseCommandconstructor - Throw specific error types for proper exit codes
See Creating Custom Commands for detailed guide.
Common Patterns
Dry Run Mode
Several commands support --dry-run to preview actions:
xec copy --dry-run source.txt hosts.* /tmp/
Parallel Execution
Commands that operate on multiple targets execute in parallel:
xec on "hosts.*" "uptime" # Runs on all hosts in parallel
SSH connections are reused via the connection pool, and output streams are merged in real time.
Streaming Output
Commands that produce continuous output support streaming:
xec logs containers.app --follow
Interactive Mode
Some commands provide interactive prompts when options are missing:
xec new # Prompts for artifact type
Next Steps
- Built-in Commands Reference - Detailed documentation for each command
- Custom Commands - Create your own commands
- CLI Reference - Complete CLI reference