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 buildwithout-c release,cargo buildwithout--release), and whetherWENDY_DEBUGis wired to toggle the optimization level. This check targets hand-written Dockerfiles. For Swift apps cross-compiled viawendy run/wendy build(the swift-container-plugin path), the CLI passes-c releaseautomatically; no manualwendy.jsonor Dockerfile change is needed for that path. Stagefile-generated Dockerfiles always spell the configuration out (swift build -c releaseor-c debug,cargo buildwith or without--release), so this finding never fires for them — pass--debugtowendy build/wendy runto switch a Stagefile build to debug. - CUDA / ML — a CPU-only ML wheel (e.g.
torch==…+cpu) paired with thegpuentitlement (or a CUDA wheel without it), and x86nvidia/cudabase images on an arm64 (Jetson) target. - Architecture & image — an
amd64base 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 hygiene —
apt-get installsplit into a separateRUNfromapt-get update(a stale-package-index trap) or missing--no-install-recommends;pip installwithout--no-cache-dir;npm installused despite apackage-lock.json(considernpm ci);ADDused where a plainCOPYwould do; an unpinned or:latestFROMtag; shell-formCMD/ENTRYPOINT(breaks signal forwarding ondocker stop); andCOPY --fromthat 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 agentFlags
--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-fileADDforCOPY. Fixes are idempotent; contextual changes (multi-stage refactors, choosing the right CUDA wheel, retagging a:latestbase image, converting shell-formCMDto exec form) are left to you or the--agenticflow.npm install→npm ciis reported but never auto-fixed, even here — a drifted lockfile makesnpm cifail outright wherenpm installwould 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 towarning.--arch <arch>— override the target architecture (defaults toarm64).--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 ADD → COPY 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 behavior — npm install → npm 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.