Skip to content

DWE — Dev Workspace Engine

DWE — Dev Workspace Engine

A single-binary CLI for running, configuring, and maintaining containerised local development environments declaratively.

  • One descriptive tree (workspace.yml + workspace/) drives the whole project — services, pipelines, commands, info dashboard, templates, translations.
  • Deploy, run, stop, restart, reset, and snapshot share the same pipeline engine, so behaviour is consistent across operations.
  • Container orchestration sits on top of plain Docker Compose files the project already owns — no synthetic compose generation, no hidden lock-in.
  • Per-developer overrides live in tracked defaults.yml plus gitignored local.yml; the same project boots cleanly on every workstation.
  • The host bridge mounts a tiny dwe shim into dev containers, so git hooks and project commands work identically on the host and inside a devcontainer terminal.
  • Embedded docs and i18n make dwe docs and translated UIs work without network access or out-of-band assets.

DWE ships as a single static Go binary. Pick whichever channel fits.

Terminal window
brew install semsemyonoff/tap/dwe

Installs the binary plus bash, zsh, and fish completion to the standard Homebrew paths. Works on macOS (Intel + Apple Silicon) and Linux (linuxbrew).

Download the appropriate archive or package from GitHub releases:

  • dwe_<version>_macos_{x86_64,arm64}.tar.gz and dwe_<version>_linux_{x86_64,arm64}.tar.gz — extract and drop dwe into /usr/local/bin (or any directory on $PATH); the archive bundles a completions/ folder for use with dwe completion install.
  • dwe_<version>_{amd64,arm64}.debsudo dpkg -i dwe_*.deb; completion is installed automatically under /usr/share/{bash-completion,zsh/site-functions,fish/vendor_completions.d}.
  • dwe-<version>-1.{x86_64,aarch64}.rpmsudo rpm -i dwe-*.rpm; completion paths match the deb package.

File integrity can be verified against the published checksums.txt.

Terminal window
git clone https://github.com/semsemyonoff/dwe.git
cd dwe
make build

make build runs go mod tidy, syncs docs/ into internal/core/docs/embedded/, regenerates internal/core/docs/content_hashes_gen.go, compiles ./cmd/dwe, and writes bin/dwe. The binary is self-contained: docs, translations, default pipelines, and built-in steps are embedded. Drop it on $PATH:

Terminal window
install -m 0755 bin/dwe /usr/local/bin/dwe

go install github.com/semsemyonoff/dwe/cmd/dwe@latest is intentionally unsupported: the embedded docs tree (internal/core/docs/embedded/) is generated by scripts/sync-embedded-docs.sh at build time and is gitignored, so a go install build would ship with empty docs.

If the binary came from a tar.gz archive, completion is installed via a separate command:

Terminal window
dwe completion install # autodetect $SHELL
dwe completion install zsh # or specify the shell explicitly

Supported shells: bash, zsh, fish, powershell. See dwe completion install --help for details and dry-run.

docker (with docker compose), git, and a POSIX shell on the host. If they live in non-standard locations, override their paths in the user-level config at ~/.config/dwe/config via binary_<name> = <path> entries — see docs/reference/config/userconfig.md.

The repository ships an agent skill at skills/dwe/ — a thin navigator that teaches Claude Code, Codex, Cursor, OpenCode, and other compatible agents how to detect a DWE project, which dwe commands to use for inspection vs mutation, and how to look up everything else through the built-in dwe docs subsystem.

This repository is also a Claude Code plugin marketplace. Add the marketplace and install the dwe plugin (which bundles the skill) from inside Claude Code:

/plugin marketplace add semsemyonoff/dwe
/plugin install dwe@dwe

dwe@dwe is <plugin>@<marketplace> — both are named dwe. Run /plugin (no arguments) for the interactive browser instead. Once installed, the skill activates automatically whenever you work in a directory containing workspace.yml. To enable it across a team or in CI, commit the marketplace and plugin to .claude/settings.json:

{
"extraKnownMarketplaces": {
"dwe": { "source": { "source": "github", "repo": "semsemyonoff/dwe" } }
},
"enabledPlugins": { "dwe@dwe": true }
}

The vercel-labs/skills CLI installs the same skill into Claude Code, Codex, Cursor, OpenCode, and others:

Terminal window
# install into the current project (./<agent>/skills/)
npx skills add semsemyonoff/dwe --skill dwe
# or install globally for all your projects
npx skills add semsemyonoff/dwe --skill dwe -g
# target a specific agent (claude-code, codex, cursor, opencode, ...)
npx skills add semsemyonoff/dwe --skill dwe -a claude-code

Starting from scratch? Scaffold a new project with a single command:

Terminal window
dwe init my-project # interactive: prompts for name, prefix, branding
cd my-project
dwe validate # check the scaffolded config

Joining an existing project? Enter a project directory containing workspace.yml and run any command — DWE walks upward from the working directory to locate the project root.

Validate the project before the first deploy:

Terminal window
cd my-project
dwe validate

Build and start the stack:

Terminal window
dwe deploy run # idempotent: install, configure, migrate, bring up
dwe info # rendered URLs, hosts, command groups

Drive the runtime lifecycle:

Terminal window
dwe run # before-hooks → docker up → after-hooks → ready
dwe stop # before-stop → docker down → after-stop
dwe restart # stop + run
dwe status # services, ports, hosts, git, env

Plan-only previews are read-only:

Terminal window
dwe deploy plan # resolved phase/step tree, no execution
dwe validate # readiness checks

The deploy journal lives at .dwe/deploy/state.yml. Repeat runs skip steps whose action_hash and inputs are unchanged. Deploy logs are written to .dwe/logs/deploy.log by default (suppress with log: false); lifecycle logs (run/stop/reset) are written only when log: true is set.

DWE sits between the developer and a Dockerized stack: it reads a project’s YAML tree, renders a small amount of generated state next to it, and drives docker compose to actually run the containers. The developer never types docker compose directly.

flowchart LR
  Dev["Developer"] -->|dwe| CLI["dwe CLI"]
  Project["DWE project config<br/>+ compose config"] --> CLI
  CLI -->|docker compose| Engine["Docker engine"]
  Engine --> Containers["containers"]
  Dev -.->|http / tcp| Containers
  • DWE owns the project model, the ordered compose file list, env rendering, lifecycle orchestration, locks, and the state journal under .dwe/.
  • Docker owns containers, networks, volumes, image layers, and health reporting. The only handshake is the argv DWE passes to docker compose and the exit code it returns.
  • Every invocation is short-lived and stateless: no plugin loader, no network on the normal path, and no resident process except the per-project host-bridge daemon that serves dev containers while the stack is up. Uninstalling DWE leaves the compose files under compose/ as valid standalone docker compose input.

Full write-up: docs/reference/concepts/architecture.md.

A typical project keeps its declarative tree under workspace/, Docker Compose overlays under compose/, and runtime data under .dwe/ / snapshots/ / backups/.

my-project/
├── workspace.yml # project identity
├── workspace/ # tracked config tree
│ ├── defaults.yml # versioned defaults (optional)
│ ├── local.yml # per-developer overrides (gitignored, optional)
│ ├── services/<name>/ # per-service folders (service.yml + optional pipelines)
│ ├── commands/ # declarative user commands (optional)
│ ├── templates/ # template packs for `dwe render` (optional)
│ ├── deploy.yml # top-level deploy orchestrator (optional)
│ ├── lifecycle.yml # run / stop / restart (optional)
│ ├── reset.yml # reset pipeline (optional)
│ ├── info.yml # info dashboard (optional)
│ ├── validate.yml # readiness checks (optional)
│ ├── tests/ # integration-test scenarios for `dwe test` (optional)
│ └── docker.yml # compose file list + topology (optional)
├── compose/ # tracked Docker Compose overlays (per service)
├── images/ # tracked image builds (<service>/Dockerfile)
├── services/ # gitignored service sources (<hub>/src/)
├── snapshots/ # gitignored snapshot stash
├── backups/ # gitignored DB/other dumps
└── .dwe/ # gitignored CLI runtime data (state, locks, logs)

Full write-up: docs/reference/concepts/project-layout.md.

📖 Browse the full documentation online at semsemyonoff.github.io/dwe.

Reference documentation lives under docs/reference/ and is also embedded in the binary. Browse it offline with dwe docs (interactive TUI) or dwe docs show <topic> (plain text).

  • Concepts — high-level orientation: getting started, architecture, project layout, Docker integration, Git integration, pipelines, state and locks.
  • Configuration — field-level reference for workspace.yml, services, commands, deploy/reset/lifecycle pipelines, snapshot, info, validate, setup, styles, UI, state, i18n, notifications, docker.
  • Render packsdwe render env / ide / ai / git / config — manifest schema, collision policies, local overrides.
  • Documentation subsystem — the dwe docs browser, non-interactive subcommands, translations, content-hash staleness.
  • Templates — the shared template engine: {{ ... }} vs ${ ... }, sprout registries, render context per site.
  • Guides — task-oriented recipes and integrations (e.g. Starship prompt).

Run dwe --help (or any subcommand with --help) for the live CLI surface.

Useful one-liners:

Terminal window
dwe docs # interactive browser
dwe docs list # enumerate every topic
dwe docs show <topic> # render one page
dwe docs search <term> # cross-tree search
dwe docs llms-txt # compact AI-agent project index

Released under the MIT License.