CLI Reference
The Engineer is operated through the engineer CLI — daily-use commands (start, stop, status, logs), diagnostics (doctor), and per-task commands (why, retry, cancel).
Installing the CLI
pnpm run setuppnpm run setup installs dependencies, builds to dist/, and links the engineer command globally. It confirms before acting and is safe to re-run. If pnpm's own global bin directory has never been configured, the script offers to run pnpm setup for you, then tells you to restart your terminal.
After setup, engineer works from any directory. Re-run pnpm run setup (or just pnpm run build) after changing code.
Dev mode: use
pnpm dev <command>to run the CLI without building or linking.
Development Resets
scripts/reset.sh rebuilds from scratch for fast iteration — it stops the daemon, rebuilds, relinks the CLI, clears the data directory, and starts fresh.
./scripts/reset.sh # Full wipe, then interactive setup
./scripts/reset.sh <seed-dir> # Full wipe, then seeded setup from <seed-dir>
./scripts/reset.sh --persist-data # Keep the database, workspaces, and .env
./scripts/reset.sh --persist-data <seed-dir> # Same, seeded from <seed-dir>The seed directory is optional. With no seed, engineer start runs its interactive first-run setup; with a seed, setup is read from the directory's YAML files with no prompts (see First Run). A seed argument that does not exist or cannot be read is a hard error.
Seed-directory convention: seed-example/ is tracked as a generic reference for the layout. Personal seeds live in gitignored seed-example-<name>/ directories — copy seed-example/, fill in real values, and pass the directory to reset.sh or engineer start --seed.
ENGINEER_HOME
Every command reads and writes to a single data directory. Resolution order:
--home <path>flag (highest priority)ENGINEER_HOMEenvironment variable~/.engineer(default)
Directory structure (created automatically on first engineer start):
~/.engineer/
.env # Secrets (KEY=VALUE, 0o600 permissions)
config/ # YAML config files
plugins/ # Plugin-specific configs
data/ # SQLite database (engineer.db)
logs/ # Daemon log files (rolling JSON)
run/ # PID file
traces/ # agent prompt/response blobs (content-addressable)
workspaces/ # Git worktrees for task isolation
example-templates/ # Fully documented config referencesSource: src/cli/home.ts
Global Options
| Option | Description |
|---|---|
--home <path> | Override ENGINEER_HOME |
--config-dir <path> | Override config directory (default: $ENGINEER_HOME/config) |
--verbose | Debug-level logging |
--json | Output in JSON format |
--version | Print version |
--help | Print help for any command |
First Run
engineer startThat's it. On first run, start detects there's no config and launches guided setup:
- Environment detection — scans PATH for coding agent CLIs (
claude,opencode,gemini), checks env vars - Plugin selection — one prompt per adapter type (agent, trigger, communication, git hosting), grouped by category, with detection status shown
- Per-plugin config — prompts for required fields (repos to watch, etc.)
- People Directory — configures the owner (the single person The Engineer reaches) and optional additional people. For each person: name, identifier, roles, and a handle for each selected communication channel (derived from plugin manifests). Generates
people.yamlwith real values instead of placeholders. - Secret collection — prompts for token values with masked input (e.g.,
GITHUB_TOKEN). Tokens already set in your shell are captured and persisted to~/.engineer/.envautomatically. Permissions:0o600. - Confirmation — summary of selections, Y/n
- Config written — YAML files to
~/.engineer/config/, secrets to~/.engineer/.env - Daemon starts — pre-flight checks, bootstrap, tick loop begins
Non-interactive setup (CI, automation, teams):
engineer start --seed ./seed-example/Seeds both plugin configs and core configs from the provided directory. No prompts. The seed directory structure:
seed-example/
plugins/ # Plugin YAML configs (required)
configs/ # Core YAML configs: people.yaml, daemon.yaml, etc. (optional — falls back to templates)Validates that plugins/ exists and has .yaml files. If configs/ is present, uses those files; otherwise generates core configs from templates. Verifies all ${VAR} references resolve after setup — fails loudly if tokens are missing.
Dry run (see what would happen without writing):
engineer start --dry-runCommands
start
The single entry point. Handles first-run setup, config loading, pre-flight checks, and daemon startup.
engineer start # Foreground (Ctrl+C to stop)
engineer start --daemon # Background (detached process)
engineer start --verbose # Debug logging
engineer start --dry-run # Show what would happen, don't start
engineer start --seed ./seed-example/ # Non-interactive setup from seed directoryStartup sequence: first-run detection → setup if needed → load .env → capture shell env vars to .env → load config → pre-flight doctor checks (the no-config subset) → bootstrap all components → start tick loop → launch dashboard (React SPA on port 3847).
Signal handling: SIGTERM and SIGINT trigger graceful shutdown — active tasks transition to queued, plugins shut down, PID file cleaned up.
Source: src/cli/commands/start/start.ts, src/cli/commands/start/bootstrap.ts
stop
Graceful shutdown. Sends SIGTERM to the daemon and waits for clean exit.
engineer stop # Default 30s timeout
engineer stop --timeout 1m # Custom timeoutSource: src/cli/commands/stop.ts
status
Shows whether the daemon is running and lists non-terminal tasks with their 8-character ID prefix, state, title, and age. Use the printed prefix with why or retry. Reads SQLite directly (read-only, no IPC).
engineer status # Non-terminal tasks (active, queued, blocked, etc.)
engineer status --all # Include completed, failed, and cancelled tasksExample output:
The Engineer: running (PID 25725)
Tasks: 1 active, 1 queued
active 01HXYZ12 Add OAuth scope toggle 3m ago
queued 01HXYW98 Fix README link rot 7m agoSource: src/cli/commands/status.ts
logs
Tails the daemon log file.
engineer logs # Last 50 lines, pretty-printed
engineer logs --raw # Raw JSON (no pretty-printing)
engineer logs --lines 100 # Last 100 lines
engineer logs --follow # Stream new entries (like tail -f)Source: src/cli/commands/logs.ts
doctor
Runs independent health check categories. No daemon required — works standalone. For symptom-to-fix help when a check fails, see Troubleshooting.
engineer doctorExit codes: 0 = all pass, 1 = failures found, 2 = warnings only.
| Category | What it checks |
|---|---|
| Node.js Runtime | process.version >= 22.0.0 |
| Data Directory | ENGINEER_HOME exists, writable, subdirs present |
| Config Files | Every core YAML config parses and passes Zod validation |
| Required Secrets | All ${ENV_VAR} references in configs resolve + .env file permissions |
| Database | SQLite file accessible |
| Plugins | Plugin config files parse correctly |
| Workspace | Git binary available, workspace dir exists |
| External Dependencies | agent CLIs on PATH |
| People Directory | An owner is configured, the single-user constraint is respected, and the owner's channels have an installed communication plugin (needs loaded config) |
| Risky Config Warnings | Warnings for auto-merge enabled, missing cost limits, high concurrency (needs loaded config) |
| Telemetry | When telemetry is enabled, probes the configured OTLP endpoint (local by default, but a remote endpoint is probed wherever it points) with a short timeout — WARN-only and informational; when telemetry is disabled it is a clean pass with no probe |
The People Directory and Risky Config Warnings categories need a loaded config bundle, so they run only when config loads. On engineer start, a pre-flight subset runs automatically before the daemon boots — every category above except Workspace, People Directory, Risky Config Warnings, and Telemetry. Telemetry runs only under the full engineer doctor command: it makes an async network probe and is appended after the synchronous checks, so it is never part of the startup pre-flight subset.
Source: src/cli/commands/doctor.ts
why
Displays a timeline of significant events for a task — state transitions, events, journal entries, and cost. Opens the database read-only; no daemon required. Accepts a full task ID or a unique prefix (e.g., the 8-char prefix from engineer status).
engineer why <task-id> # Human-readable timeline
engineer why 01HXYZ12 # Works with ID prefix
engineer why <task-id> --json # Machine-readable JSON outputSource: src/cli/commands/why.ts
retry
Re-queues a blocked or failed task so the daemon picks it up on the next scheduling cycle. Resets both per-category retry counters (crash, agent-unavailable) and clears not_before so the retry cycle starts fresh. Direct database access — usable even when the daemon is stopped. Accepts a full task ID or a unique prefix.
engineer retry <task-id> # Re-queue a blocked or failed task
engineer retry 01HXYZ12 # Works with ID prefix
engineer retry <task-id> --json # Machine-readable JSON outputUse this when:
- A task is
blockedbecause external input is now available (e.g., you answered a clarifying question outside the chat). - A task is
failedbecause the retry policy exhausted its automatic budget, or because the hard cap on total active time triggered. Address the root cause first, then retry.
Source: src/cli/commands/retry.ts
cancel
Cancels an unfinished task — transitions it to the terminal cancelled state. A guarded, versioned write that joins the daemon's optimistic-concurrency check, so cancelling a running task serializes against a concurrent state change (exactly one wins). If the task is mid-flight, the daemon detects the cancellation on its next tick and stops the agent; the workspace reaper then closes any open pull request and deletes the abandoned branch. Direct database access — usable even when the daemon is stopped. Accepts a full task ID or a unique prefix.
engineer cancel <task-id> # Cancel an unfinished task
engineer cancel 01HXYZ12 # Works with ID prefix
engineer cancel <task-id> --json # Machine-readable JSON outputCancellable states are requirements_gathering, queued, active, and blocked. A task that has already finished (completed, failed, or cancelled) cannot be cancelled — a failed task is retryable instead (see retry). Cancel is for abandoning work; retry is for resuming it.
Source: src/cli/commands/cancel.ts
Configuration
Config files live in ~/.engineer/config/. Generated on first run with conservative defaults. Edit YAML directly — most changes take effect on next daemon restart.
Core configs:
daemon.yaml— tick interval, concurrency, plugin health settingsorchestrator.yaml— review lenses, observability (live agent activity)safety.yaml— cost limits, autonomy level, merge policyworkspace.yaml— worktree root, branch prefix, PR defaults, branch retentionpeople.yaml— the owner and their contact channels for communication
Plugin configs (config/plugins/):
- One YAML file per enabled plugin (e.g.,
github-trigger.yaml,claude-code-agent.yaml) - A plugin is enabled if and only if its config YAML exists
- Secrets use
${ENV_VAR}references — actual values live in.env
Secrets (~/.engineer/.env):
- Collected during first-run setup with masked input, or add manually
- Format:
KEY=VALUE(one per line,#for comments) - Auto-persistence: On every
engineer start, env vars from your shell that match${VAR}references in configs are automatically captured and written to.env. This ensures tokens survive across terminal sessions and background daemon restarts. - Environment variables already set in your shell take precedence over
.envvalues - File permissions:
0o600(owner read/write only) —engineer doctorwarns if too open - Plugin configs reference secrets as
${GITHUB_TOKEN}— resolved at startup from.envor env vars
Example templates (example-templates/):
- Fully documented reference for every config field
- Written on first run — always safe to regenerate
To reconfigure: engineer stop, edit YAML files, engineer start.
Upgrading
Pull and rebuild — all state lives in ~/.engineer, not the repo, so an upgrade never touches your config, secrets, or history:
git pull
pnpm run setupThe Engineer is pre-v1 in spirit. Additive database migrations apply automatically at startup, but a breaking schema change ships without a data migration — when one lands, you reset the database (below). Config files have no migration machinery at all: when a release reshapes one, you update the YAML by hand to match the new shape. Automated config-schema migration is on the roadmap — see Config Schema Versioning.
Surgical reset (keep config and secrets): drop only the database when a schema mismatch breaks startup. Your config, .env, and worktrees survive; the database rebuilds empty on next start, so you lose task history and the audit trail.
engineer stop
rm -f ~/.engineer/data/engineer.db
engineer startThis is the inverse of ./scripts/reset.sh --persist-data, which keeps the database and wipes config.
Full reset (start from scratch): destroys everything — config, .env secrets, database, logs, and worktrees — then runs first-run setup again.
rm -rf ~/.engineer && engineer startDuring development, ./scripts/reset.sh does the equivalent full wipe and also rebuilds and relinks the CLI (see Development Resets).
Uninstall
Remove The Engineer and all of its state cleanly:
engineer stop # Stop the daemon
pnpm rm --global the-engineer # Unlink the global `engineer` bin
rm -rf ~/.engineer # Delete config, .env secrets, the SQLite database, logs, and worktreesThen delete the cloned repo. Nothing is written outside the repo and ~/.engineer — unless you pointed workspace_root (in workspace.yaml) somewhere else, in which case delete that directory too.