WendyOS Docs
AdvancedClientsWendy cliCommandsProject

Optimize

wendy project optimize statically analyzes your project's build configuration — its Dockerfile(s), requirements.txt, and wendy.json — and reports missed build-speed and runtime opt

wendy project optimize statically analyzes your project's build configuration — its Dockerfile(s), requirements.txt, and wendy.json — and reports missed build-speed and runtime optimizations.

It runs locally and is read-only by default. It works on a single Dockerfile, a multi-service / Compose project (findings are grouped by service), and native Swift (Package.swift) or Brewfile projects.

What it checks

  • Build caches — compiled-language build/install steps (cargo, go, swift, npm/yarn/pnpm, pip) that run without a BuildKit --mount=type=cache, and so re-download or re-compile dependencies on every build.
  • Release vs. debug — debug builds shipped to production (swift build without -c release, cargo build without --release), and whether WENDY_DEBUG is wired to toggle the optimization level. This check targets hand-written Dockerfiles. For Swift apps cross-compiled via wendy run/wendy build (the swift-container-plugin path), the CLI passes -c release automatically; no manual wendy.json or Dockerfile change is needed for that path. Stagefile-generated Dockerfiles always spell the configuration out (swift build -c release or -c debug, cargo build with or without --release), so this finding never fires for them — pass --debug to wendy build/wendy run to switch a Stagefile build to debug.
  • CUDA / ML — a CPU-only ML wheel (e.g. torch==…+cpu) paired with the gpu entitlement (or a CUDA wheel without it), and x86 nvidia/cuda base images on an arm64 (Jetson) target.
  • Architecture & image — an amd64 base image on an arm64 device (which runs under slow QEMU emulation or fails), a missing .dockerignore, and single-stage builds that ship their full build toolchain.
  • Dockerfile hygieneapt-get install split into a separate RUN from apt-get update (a stale-package-index trap) or missing --no-install-recommends; pip install without --no-cache-dir; npm install used despite a package-lock.json (consider npm ci); ADD used where a plain COPY would do; an unpinned or :latest FROM tag; shell-form CMD/ENTRYPOINT (breaks signal forwarding on docker stop); and COPY --from that drags in an entire build stage instead of just the artifact it needs.

Usage

wendy project optimize            # report findings (colorized in a terminal, JSON in CI)
wendy project optimize --json     # machine-readable findings
wendy project optimize --fix      # apply the safe, deterministic fixes
wendy project optimize --agentic  # emit a context bundle for an AI agent

Flags

  • --fix — apply the safe fixes only: add a build-cache mount, add the release flag (swift/cargo), create a default .dockerignore, add --no-install-recommends/--no-cache-dir, and swap a plain-file ADD for COPY. Fixes are idempotent; contextual changes (multi-stage refactors, choosing the right CUDA wheel, retagging a :latest base image, converting shell-form CMD to exec form) are left to you or the --agentic flow. npm installnpm ci is reported but never auto-fixed, even here — a drifted lockfile makes npm ci fail outright where npm install would have quietly updated it, so it's a suggestion, not a fix.
  • --agentic — instead of a report, emit a JSON bundle (static findings plus the verbatim project files and a prompt) designed to be piped into Claude Code or the Wendy MCP server.
  • --severity <info|warning|error> — the minimum severity that causes a non-zero exit. Defaults to warning.
  • --arch <arch> — override the target architecture (defaults to arm64).
  • --json — emit findings as JSON (also the default when output is not a terminal, e.g. in CI).

Exit codes

  • 0 — no findings at or above the severity threshold.
  • 1 — findings at or above the threshold (use this to gate CI).
  • 2 — execution error (no project found, parse failure).

At build time

Every time wendy run / wendy build builds a plain Dockerfile or Containerfile (not a Stagefile), it first runs a subset of this scan and, if any purely additive fix applies — a build-cache mount, --no-install-recommends, --no-cache-dir, or a plain-file ADDCOPY swap — applies it in memory and builds that instead. Your real Dockerfile on disk is never touched; nothing is written unless a fix actually applies, and even then only a sibling Dockerfile.generated is created. This runs unconditionally (interactive or not, every build) since it's pure static analysis over an already-in-memory file — sub-millisecond overhead, nothing you'd notice next to the build itself.

Stagefile projects skip this scan entirely — a Stagefile is compiled, not patched, so the optimizations are the compiler's job. They write into the same Dockerfile.generated* namespace: build.stagefile.yaml compiles to Dockerfile.generated (the same name the auto-fix path uses, which is why only one of the two can apply to a given build), and each variant compiles to its own Dockerfile.generated.<variant>prod.stagefile.yaml to Dockerfile.generated.prod. See Stagefile naming.

Fixes that change build outcome or behaviornpm installnpm ci (can turn a passing build into a failing one on a drifted lockfile) and the swift/cargo release flag (changes the shipped binary's runtime behavior) — are deliberately excluded from this silent path. They still show up as findings, and the release flag is still available via an explicit --fix.

Separately: after a slow incremental build (one that reused cached layers and still took more than ~50s), wendy run / wendy build also runs the full scan interactively, shows every finding, and offers to apply the remaining safe fixes to disk for your next build. This part never runs in CI or non-interactive shells.

Sample projects

The repository used to ship deliberately un-optimized sample projects under Examples/project-optimize-samples/, each triggering a specific finding. Those four have since been converted to build.stagefile.yaml, and this command does not analyse Stagefiles — so they no longer reproduce anything. Each one's EXPECTED.txt now records what its findings were and which of them a Stagefile makes structurally impossible, which is a useful read if you are choosing between the two formats.

To see the analyzer and --fix in action, run it against a project that still has a Dockerfile — Examples/ClaudeOnDevice and Examples/HelloAudio are the two left in this repository — or against your own.

A note on --agentic and secrets

The --agentic bundle includes the verbatim contents of your Dockerfile(s), requirements.txt, and wendy.json so the agent has full context. These files can contain secrets (ARG/ENV tokens, private registry URLs). The command prints a reminder to stderr; review the bundle before sending it to an external agent.

On this page