Skip to content

Run AI-powered workflows from your terminal.

Prerequisites

  1. Clone the repository and install dependencies:

    bash
    git clone https://github.com/Cadence-Intelligence/zen
    cd zen
    bun install
  2. Make CLI globally available (recommended):

    bash
    cd packages/cli
    bun link

    This creates an zen command available from anywhere.

  3. Authenticate with Claude:

    bash
    claude /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

bash
# 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.

bash
zen chat "What does the orchestrator do?"

setup

Interactive setup wizard for credentials and configuration.

bash
zen setup
zen setup --spawn  # Open in a new terminal window

Flags:

FlagEffect
--spawnOpen setup wizard in a new terminal window

workflow list

List workflows available in target directory.

bash
zen workflow list --cwd /path/to/repo

# Machine-readable output for scripting
zen workflow list --cwd /path/to/repo --json

Discovers workflows from .zen/workflows/ (recursive), ~/.zen/workflows/ (global), and bundled defaults. See Global Workflows.

Flags:

FlagEffect
--cwd <path>Target directory (required for most use cases)
--jsonOutput 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.

bash
# 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:

FlagEffect
--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-worktreeOpt out of isolation; run directly in live checkout
--resumeResume from last failed run at the working path (skips completed nodes)
--allow-env-keysGrant 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, -qSuppress all progress output to stderr
--verbose, -vAlso 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 --branch and --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):

  1. Exact match - morning-brief matches morning-brief
  2. Case-insensitive - Morning-Brief matches morning-brief
  3. Suffix match - brief matches morning-brief (looks for -brief suffix)
  4. Substring match - brief matches zen-morning-brief

If multiple workflows match at the same tier, an error lists the candidates:

Ambiguous workflow 'review'. Did you mean:
  - zen-review
  - custom-review

workflow status

Show all running workflow runs across all worktrees.

bash
zen workflow status
zen workflow status --json

workflow resume

Resume a failed workflow run. Re-executes the workflow, automatically skipping nodes that completed in the prior run.

bash
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.

bash
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.

bash
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.

bash
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.

bash
zen workflow cleanup        # Default: 7 days
zen workflow cleanup 30     # Custom threshold

workflow event emit

Emit a workflow event directly to the database. Primarily used inside workflow loop prompts to record story-level lifecycle events.

bash
zen workflow event emit --run-id <uuid> --type <event-type> [--data <json>]

Flags:

FlagRequiredDescription
--run-idYesUUID of the workflow run
--typeYesEvent type (e.g., ralph_story_started, node_completed)
--dataNoJSON 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.

bash
zen isolation list

Groups by codebase, shows branch, workflow type, platform, and days since activity.

isolation cleanup [days]

Remove stale environments.

bash
# 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-closed

Merge 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).

bash
zen validate workflows                 # Validate all workflows
zen validate workflows my-workflow     # Validate a single workflow
zen validate workflows my-workflow --json  # Machine-readable JSON output

Checks: 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/.

bash
zen validate commands                  # Validate all commands
zen validate commands my-command       # Validate a single command

Checks: 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.

bash
zen complete feature-auth
zen complete feature-auth --force  # bypass uncommitted-changes check

Flags:

FlagEffect
--forceSkip 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.

bash
# 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-only

Flags:

FlagEffect
--port <port>Override server port (default: 3090, range: 1-65535)
--download-onlyDownload 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.

bash
zen version

Global Options

OptionEffect
--cwd <path>Override working directory (default: current directory)
--quiet, -qReduce log verbosity to warnings and errors only
--verbose, -vShow debug-level output
--jsonOutput machine-readable JSON (for workflow list, workflow status)
--help, -hShow help message

Working Directory

The CLI determines where to run based on:

  1. --cwd flag (if provided)
  2. 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:

  1. Loads ~/.zen/.env with override: true (Z.E.N.'s config wins over CWD vars)
  2. 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

bash
# 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

AI that follows a recipe, not a conversation.