Run AI-powered workflows from your terminal.
Prerequisites
Clone the repository and install dependencies:
bashgit clone https://github.com/Cadence-Intelligence/zen cd zen bun installMake CLI globally available (recommended):
bashcd packages/cli bun linkThis creates an
zencommand available from anywhere.Authenticate with Claude:
bashclaude /login
Note: Examples below use zen (after bun link). If you skip step 2, use bun run cli from the repo directory instead.
Quick Start
# List available workflows (requires git repository)
zen workflow list --cwd /path/to/repo
# Run a workflow (opts into a worktree if the workflow declares worktree.enabled: true)
zen workflow run morning-brief --cwd /path/to/repo "Explain the authentication flow"
# Explicit branch name for the worktree
zen workflow run zen-architect --cwd /path/to/repo --branch feature-auth "Add OAuth support"
# Opt out of isolation (run in live checkout)
zen workflow run morning-brief --cwd /path/to/repo --no-worktree "Quick question"Note: Workflow and isolation commands require running from within a git repository. Running from subdirectories automatically resolves to the repo root. The version, help, chat, setup, and serve commands work anywhere.
Commands
chat <message>
Send a message to the orchestrator for a one-off AI interaction.
zen chat "What does the orchestrator do?"setup
Interactive setup wizard for credentials and configuration.
zen setup
zen setup --spawn # Open in a new terminal windowFlags:
| Flag | Effect |
|---|---|
--spawn | Open setup wizard in a new terminal window |
workflow list
List workflows available in target directory.
zen workflow list --cwd /path/to/repo
# Machine-readable output for scripting
zen workflow list --cwd /path/to/repo --jsonDiscovers workflows from .zen/workflows/ (recursive), ~/.zen/workflows/ (global), and bundled defaults. See Global Workflows.
Flags:
| Flag | Effect |
|---|---|
--cwd <path> | Target directory (required for most use cases) |
--json | Output machine-readable JSON instead of formatted text |
With --json, outputs { "workflows": [...], "errors": [...] }. Optional fields (provider, model, modelReasoningEffort, webSearchMode) are omitted when not set on a workflow.
workflow run <name> [message]
Run a workflow with an optional user message.
# Basic usage
zen workflow run morning-brief --cwd /path/to/repo "What does this function do?"
# With isolation
zen workflow run zen-architect --cwd /path/to/repo --branch feature-x "Add caching"Progress events (node start/complete/fail/skip, approval gates) are written to stderr during execution.
Flags:
| Flag | Effect |
|---|---|
--cwd <path> | Target directory (required for most use cases) |
--branch <name> | Explicit branch name for the worktree |
--from <branch>, --from-branch <branch> | Override base branch (start-point for worktree) |
--no-worktree | Opt out of isolation; run directly in live checkout |
--resume | Resume from last failed run at the working path (skips completed nodes) |
--allow-env-keys | Grant env-leak-gate consent during auto-registration (bypasses the gate for this codebase). Audit-logged as env_leak_consent_granted with actor: 'user-cli'. See security.md. |
--quiet, -q | Suppress all progress output to stderr |
--verbose, -v | Also show tool-level events (tool name and duration) |
Default (no flags):
- If the workflow declares
worktree.enabled: true, creates a worktree with auto-generated branch (zen/task-<workflow>-<timestamp>) - Auto-registers codebase if in a git repo
With --branch:
- Creates/reuses worktree at
~/.zen/workspaces/<owner>/<repo>/worktrees/<branch>/ - Reuses existing worktree if healthy
With --no-worktree:
- Runs in target directory directly (no isolation)
- Mutually exclusive with
--branchand--from
Name Matching:
Workflow names are resolved using a 4-tier fallback hierarchy. This applies consistently across the CLI and all chat platforms (Slack, Telegram, Web, GitHub):
- Exact match -
morning-briefmatchesmorning-brief - Case-insensitive -
Morning-Briefmatchesmorning-brief - Suffix match -
briefmatchesmorning-brief(looks for-briefsuffix) - Substring match -
briefmatcheszen-morning-brief
If multiple workflows match at the same tier, an error lists the candidates:
Ambiguous workflow 'review'. Did you mean:
- zen-review
- custom-reviewworkflow status
Show all running workflow runs across all worktrees.
zen workflow status
zen workflow status --jsonworkflow resume
Resume a failed workflow run. Re-executes the workflow, automatically skipping nodes that completed in the prior run.
zen workflow resume <run-id>workflow abandon
Discard a workflow run (marks it as failed). Use this to unblock a worktree when you don't want to resume.
zen workflow abandon <run-id>workflow approve
Approve a paused workflow run at an interactive approval gate. Optionally provide a comment that is available to the workflow via $LOOP_USER_INPUT.
zen workflow approve <run-id>
zen workflow approve <run-id> "Looks good, proceed"
zen workflow approve <run-id> --comment "Looks good, proceed"workflow reject
Reject a paused workflow run at an approval gate. Optionally provide a reason that is available to the workflow via $REJECTION_REASON.
zen workflow reject <run-id>
zen workflow reject <run-id> --reason "Needs more tests"workflow cleanup
Delete old terminal workflow run records from the database.
zen workflow cleanup # Default: 7 days
zen workflow cleanup 30 # Custom thresholdworkflow event emit
Emit a workflow event directly to the database. Primarily used inside workflow loop prompts to record story-level lifecycle events.
zen workflow event emit --run-id <uuid> --type <event-type> [--data <json>]Flags:
| Flag | Required | Description |
|---|---|---|
--run-id | Yes | UUID of the workflow run |
--type | Yes | Event type (e.g., ralph_story_started, node_completed) |
--data | No | JSON string attached to the event. Invalid JSON prints a warning and is ignored. |
Exit code: 0 on success, 1 when --run-id, --type is missing, or --type is not a valid event type. Event persistence is best-effort (non-throwing); check server logs if events appear missing.
isolation list
Show all active worktree environments.
zen isolation listGroups by codebase, shows branch, workflow type, platform, and days since activity.
isolation cleanup [days]
Remove stale environments.
# Default: 7 days
zen isolation cleanup
# Custom threshold
zen isolation cleanup 14
# Remove environments with branches merged into main (also deletes remote branches)
zen isolation cleanup --merged
# Also remove environments whose PRs were closed without merging
zen isolation cleanup --merged --include-closedMerge detection uses three signals in order: git branch ancestry (fast-forward / merge commit), patch equivalence (squash-merge via git cherry), and GitHub PR state via the gh CLI. The gh CLI is optional; if absent, only git signals are used.
By default, branches with a CLOSED PR are skipped. Pass --include-closed to clean those up as well. Branches with an OPEN PR are always skipped.
validate workflows [name]
Validate workflow YAML definitions and their referenced resources (command files, MCP configs, skill directories).
zen validate workflows # Validate all workflows
zen validate workflows my-workflow # Validate a single workflow
zen validate workflows my-workflow --json # Machine-readable JSON outputChecks: YAML syntax, DAG structure (cycles, dependency refs), command file existence, MCP config files, skill directories, provider compatibility. Returns actionable error messages with "did you mean?" suggestions for typos.
Exit code: 0 = all valid, 1 = errors found.
validate commands [name]
Validate command files (.md) in .zen/commands/.
zen validate commands # Validate all commands
zen validate commands my-command # Validate a single commandChecks: file exists, non-empty, valid name.
Exit code: 0 = all valid, 1 = errors found.
complete <branch> [branch2 ...]
Remove a branch's worktree, local branch, and remote branch, and mark its isolation environment as destroyed.
zen complete feature-auth
zen complete feature-auth --force # bypass uncommitted-changes checkFlags:
| Flag | Effect |
|---|---|
--force | Skip uncommitted-changes guard |
Use this after a PR is merged and you no longer need the worktree or branches. Accepts multiple branch names in one call.
serve
Start the web UI server. On first run, downloads a pre-built web UI tarball from the matching GitHub release, verifies the SHA-256 checksum, and extracts it. Subsequent runs use the cached copy.
Binary installs only; in development, use bun run dev instead.
# Start web UI server (downloads on first run)
zen serve
# Override the default port
zen serve --port 4000
# Download the web UI without starting the server
zen serve --download-onlyFlags:
| Flag | Effect |
|---|---|
--port <port> | Override server port (default: 3090, range: 1-65535) |
--download-only | Download and cache the web UI, then exit without starting the server |
The cached web UI is stored at ~/.zen/web-dist/<version>/. Each version is cached independently, so upgrading the binary automatically downloads the matching web UI.
version
Show version, build type, and database info.
zen versionGlobal Options
| Option | Effect |
|---|---|
--cwd <path> | Override working directory (default: current directory) |
--quiet, -q | Reduce log verbosity to warnings and errors only |
--verbose, -v | Show debug-level output |
--json | Output machine-readable JSON (for workflow list, workflow status) |
--help, -h | Show help message |
Working Directory
The CLI determines where to run based on:
--cwdflag (if provided)- Current directory (default)
Running from a subdirectory (e.g., /repo/packages/cli) automatically resolves to the git repository root (e.g., /repo).
When using --branch, workflows run inside the worktree directory.
Commands and workflows are loaded from the working directory at runtime. The CLI reads directly from disk, so it picks up uncommitted changes immediately. This is different from the server (Telegram/Slack/GitHub), which reads from the workspace clone at
~/.zen/workspaces/; that clone only syncs from the remote before worktree creation, so changes must be pushed to take effect there.
Environment
The CLI loads ~/.zen/.env with override: true, so Z.E.N.'s own config always wins over any env vars Bun auto-loads from the current working directory. Target repo env vars remain in process.env but cannot reach AI subprocesses; SUBPROCESS_ENV_ALLOWLIST blocks all non-whitelisted keys.
On startup, the CLI:
- Loads
~/.zen/.envwithoverride: true(Z.E.N.'s config wins over CWD vars) - Auto-enables global Claude auth if no explicit tokens are set
Database
- Without
DATABASE_URL(default): Uses SQLite at~/.zen/zen.db; zero setup, auto-initialized on first run - With
DATABASE_URL: Uses PostgreSQL (optional, for cloud/advanced deployments)
Both work transparently. Most users never need to configure a database.
Examples
# One-off AI chat
zen chat "How does error handling work in this codebase?"
# Interactive setup wizard
zen setup
# Quick question (auto-isolated in zen/task-assist-<timestamp>)
zen workflow run morning-brief --cwd ~/projects/my-app "How does error handling work here?"
# Quick question without isolation
zen workflow run morning-brief --cwd ~/projects/my-app --no-worktree "How does error handling work here?"
# Plan a feature (auto-isolated)
zen workflow run zen-architect --cwd ~/projects/my-app "Add rate limiting to the API"
# Implement with explicit branch name
zen workflow run zen-piv-loop --cwd ~/projects/my-app --branch feature-rate-limit "Add rate limiting"
# Branch from a specific source branch instead of auto-detected default
zen workflow run zen-piv-loop --cwd ~/projects/my-app --branch test-adapters --from feature/extract-adapters "Test adapter changes"
# Approve or reject a paused workflow
zen workflow approve <run-id> "Ship it"
zen workflow reject <run-id> --reason "Missing test coverage"
# Check worktrees after work session
zen isolation list
# Clean up old worktrees
zen isolation cleanup