Imported from finbarr/yolobox (
AGENTS.md). Install upstream withnpx skills add finbarr/yolobox. Copyright stays with the author.
AGENTS.md
This file is the working agreement for changes in this repository.
Workflow
Default workflow for this repo:
- Make the requested code or docs change directly.
- Verify it thoroughly.
- Commit the change before finishing.
Whenever behavior, UX, defaults, flags, docs, or built-in agent guidance change, audit the matching user-facing surfaces in this repo and update them too:
- README.md
- the docs site under docs/
- the bundled skills under skills/
- the setup command/wizard in cmd/yolobox/main.go
Do not stop at unit tests when behavior can be exercised for real. If a change affects runtime behavior, flags, mounts, image builds, config loading, or release automation, run the actual path and verify the output.
If full end-to-end verification is blocked by the environment, state exactly what was run, what was not run, and why.
Any hard-earned lesson that changes how future work should be done belongs in AGENTS.md.
Build Commands
make build # Build the yolobox binary
make test # Run unit tests
make lint # Run go vet (and golangci-lint if installed)
make image # Build the Docker base image
make smoke-test # Run smoke tests on container tools
make install # Build and install to ~/.local/bin
make clean # Remove built binary
Verification Standard
For non-trivial code changes, start here:
make clean && make build && make test
make lint
./yolobox version
./yolobox help
./yolobox config
Before any release or tag push, update CHANGELOG.md with the release's user-facing changes. Release tags must be created only from a clean, up-to-date master branch; do not tag feature branches, and push only master plus the specific release tag. make lint is also mandatory and must include the same golangci-lint coverage that CI runs. If local make lint reports golangci-lint not installed, skipping, install/run golangci-lint locally or run the CI linter equivalent before shipping.
Then run the feature you changed end-to-end. Examples:
- Runtime or mount behavior:
./yolobox run ... - Docker integration:
./yolobox run --docker docker version - Custom image flow:
./yolobox run --packages cowsay /usr/games/cowsay hello - Config loading:
./yolobox config
For Dockerfile or image changes, rebuild the image and run container smoke tests:
make image
./yolobox run echo hello
./yolobox run whoami
./yolobox run pwd
For docs-only changes, inspect the diff carefully. Tests are optional unless the docs describe behavior that also changed.
Code Map
Most logic lives in cmd/yolobox/main.go.
Areas to check when adding flags or config:
ConfigparseBaseFlagsmergeConfigprintConfigsaveGlobalConfigrunSetup- the runtime path that consumes the option
Also update README.md, the docs site under docs/, and the bundled skills under skills/ when user-facing behavior or agent behavior changes.
Hard Learnings
- Named volumes shadow image contents. Anything baked into
/home/yolodisappears behindyolobox-homefor existing users. - On macOS, Docker socket source paths are resolved inside the Docker VM. Use
/var/run/docker.sockas the mount source, not host-side paths like~/.colima/default/docker.sock. - On macOS,
SSH_AUTH_SOCKfrom the host is not directly mountable into Docker. Docker Desktop uses/run/host-services/ssh-auth.sock; Colima requiresforwardAgent: trueand a VM-side socket path fromcolima ssh -- printenv SSH_AUTH_SOCK. - On macOS, resolve VM-local socket mounts against the selected container runtime. Docker Desktop, Colima, and Podman machine use different VMs; never choose an agent socket by probing an unrelated installed runtime.
- Claude config is split across
~/.claude/and~/.claude.json, and the config directory must be writable inside the container. - Claude OAuth creds on macOS live in Keychain, not just on disk.
ghtokens on macOS may also live in Keychain;gh auth tokenis the reliable extraction path.- Colima often defaults to 2GB RAM, which is too small for heavier agent workflows. 4GB+ is the practical floor.
- Global npm installs as
yoloneed a user-writable prefix such as/home/yolo/.npm-global. - If a Docker build gets SIGKILL while equivalent runtime commands succeed, split heavy installers into a separate stage to reduce layer memory pressure.
- Never
chmodbind-mounted host sockets from inside the container. Fix access by matching the socket's group inside the container instead of mutating host permissions. - Setup defaults must come from global config only. Never seed a global-writing flow from merged project config, or repo-local settings will leak into every future run.
yolobox upgrademust not perform host-wide Docker cleanup. Pull the image you own; do not prune unrelated user images or caches as a side effect.- Version comparisons must be semantic, not lexical. Also stamp source-built binaries with a real version string, or update checks and support output become misleading.
install.shruns underset -euo pipefail, so any best-effort network probe must explicitly tolerate failure. Otherwise the release lookup exits the script before the source-build fallback can run.- Help text for auto-forwarded env vars must be generated from
autoPassthroughEnvVars. Hardcoded copies drift and create auth debugging noise. - Allocating a container TTY (
-t) merges stdout and stderr at the PTY boundary. Only enable TTY for genuinely interactive commands, or host-side redirection and piping will behave incorrectly. - Host-side stdin pipes need
docker run -i/podman run -ieven when no TTY is allocated. Keep-iseparate from-tso piped data works without merging stdout and stderr. - Codex trust is separate from execution mode.
--ask-for-approval neverplus--sandbox danger-full-accessstill shows the trust prompt for a new directory, so verify trusted-project startup separately when changing Codex launch flags. - Any
sudore-exec path in the entrypoint must preservePATH(for example--preserve-env=PATH) or/opt/yolobox/binwrappers get bypassed and AI CLIs lose pinned yolo flags. - Avoid parallel Git commands in this repo while another Git operation is active. We have repeatedly hit misleading
.git/index.lockfailures from overlapping status/checkout/rebase calls. - Parallel
yolobox runsessions against the same repo can also race on the entrypoint'sgit config --global --add safe.directory ...update and trip/home/yolo/.gitconfiglock errors. Run end-to-end runtime verifications sequentially when they share the same persistent home volume. - GitHub Pages deployments that use a custom Actions workflow should set the custom domain in the repository Pages settings. A checked-in
CNAMEfile is ignored in that flow and only adds confusion. - For the VitePress docs site, stop the live dev container before running
npm run docs:build. The dev server and build both writedocs/.vitepress/dist, and the shared bind mount causes flaky build conflicts if both are active. - Social-card support is not just
og:image. Ship a realrobots.txt, generate a sitemap, and include explicitog:image:*plustwitter:image:altmetadata so crawlers and card parsers have stable image hints. - For VitePress SEO, do not hardcode canonical URLs or
og:/twitter:titles in globalhead. Generate them per page frompageData, or every doc page will look like the homepage to crawlers. - Docker file bind mounts targeting a path inside an already bind-mounted project tree degrade into directories. Project file filtering must use a staged readonly project view rather than nested file mounts.
- When yolobox itself runs inside another yolobox, temp mount sources must live under an existing host-visible bind mount like the project path. Inner-container
/tmpis not visible to the outer Docker daemon. - On macOS/Colima, host temp paths under
/var/foldersmay mount into containers as empty directories because the Docker VM cannot see them. Put temp mount sources under a Docker-visible path such as~/.yolobox/tmpwhen verifying bind-mounted config directories. - Brand and social assets must not depend on runtime font rendering for the ASCII wordmark. Generate committed image assets from deterministic shapes, or social-card rasterization can drop the text and the site logo can drift from the share image.
- Rootless Podman named volumes need
:Ualongside:Zwhen using--userns=keep-id, or older subordinate-ID ownership onyolobox-homereappears in-container as uid/gid 999 and breaks/home/yolo. - If the entrypoint remaps
yoloto the host project UID/GID, it must also re-own/output; otherwise--readonly-projectleaves the writable output volume stuck at the image's original 1000:1000 ownership. - Built-in skills live under
skills/as standard Agent Skills packages. When editing them, keepSKILL.mdspec-compliant and validate withuvx --from git+https://github.com/agentskills/agentskills#subdirectory=skills-ref skills-ref validate ./skills/<name>. - VitePress docs links cannot point from
docs/to repo-root paths such as../skills; use GitHub source links or add real docs pages, or the Pages build fails dead-link checking. - When a built-in skill is renamed, the entrypoint must remove the old skill directory from
/home/yolo/.codex/skillsduring startup. The named home volume preserves stale skill folders across image upgrades. - Built-in agent guidance should be injected into
CLAUDE.mdandAGENTS.mdas a managed block. Do not replace user instruction files outright just to teach agents about yolobox behavior. - The built-in yolobox skill must not infer readonly project state from
/outputexisting./outputis present in the base image; check the manifest and actual project access instead. - A zero-byte
/home/yolo/.codex/auth.jsonmakes recent Codex CLIs abort withEOF while parsing a value. Repair that stale persistent-home state during entrypoint startup instead of requiring users to reset the volume. - Host Codex config import must preserve a valid existing container
/home/yolo/.codex/auth.jsonwhen the host config has no usable auth file. Do notrm -rf /home/yolo/.codexbefore importing host Codex config. - Codex auth can report
No space left on devicewhen the Docker/Podman storage backing/home/yoloor/tmpis full after image pulls or builds. Warn clearly, but do not auto-prune unrelated runtime storage. - Codex resume ordering is sensitive to session file mtimes. Host Codex config sync should live-mount
~/.codex/sessionsinstead of copying or touching session files, or old host sessions can appear freshly updated in resume lists. - Host Codex config can be large enough that full-tree
cp -aand broadchown -R /home/yolo/.codexmake every startup painfully slow, especially when volatile Codex log/state databases, caches, temp files, and sessions change on the host. Keep Codex config sync incremental, mount sessions live, skip other volatile paths, and keep ownership fixes targeted. Do not rw-mount the whole host~/.codexonto/home/yolo/.codex, because the entrypoint writes managedAGENTS.mdand built-in skills there. UseYOLOBOX_TIMING=1when diagnosing startup latency. - Do not create yolobox runtime metadata directories inside the project checkout. For small container handoff data like the context manifest, pass it through the entrypoint and materialize it in-container instead of bind-mounting a host temp directory from the repo.
- Local
make lintskipsgolangci-lintwhen it is not installed, but CI runsgolangci-lint-actionwith the latest linter. A skipped local linter is not release-ready; before release pushes, install/rungolangci-lintlocally or run the CI linter equivalent. - npm's
min-release-agerequires a new enough npm. When enforcing npm package age in the base image, first upgrade npm with the older--beforedate filter, then scopeNPM_CONFIG_MIN_RELEASE_AGEto the specific DockerfileRUNinstalls that need it. Do not persist it in global npm config or imageENV, or runtime CLI self-updates can get stuck behind the age gate. - Bundled AI CLI versions are base-image seeds, not startup pins. Do not overwrite valid user-upgraded launchers or npm-global installs in
/home/yolo; only seed or repair missing/broken launchers. Release-image layers that download live upstream installers, such as Claude Code, must include an explicit release-specific cache key so new images do not seed stale bundled CLIs. - When an upstream native installer owns a launcher under
~/.local/bin, do not seed that path with a cross-prefix symlink to/usr/local/bin; it can prevent updates from activating and disable version cleanup. Let the installer create its native launcher layout, and migrate only exact legacy symlinks that yolobox previously owned. - Antigravity CLI's installer runs
agy installeven with--dir, and that subcommand appends PATH exports to shell profiles. In Docker builds, run the installer with a throwawayHOMEso only/usr/local/bin/agysurvives. - Release tags must only be created from a clean, up-to-date
masterbranch. Pushmasterand the specific release tag together, for examplegit push origin master refs/tags/v0.1.2; avoid broadgit push --tagsfrom feature branches. - Kimi Code prompt mode (
kimi --prompt/-p) and explicit--automode cannot be combined with--yolo. Its wrapper must bypass the injected YOLO flag for those modes. The native installer also puts the binary under its install root while Kimi state lives in~/.kimi-code, so image seeds and persistent updates must keep binary paths separate from synced config, credentials, and sessions. - Claude OAuth refresh credentials must not be cloned into independent host and container stores unless the user explicitly accepts the shared-login behavior.
--no-claude-authconfig sync must skip Keychain extraction, exclude.credentials.jsonand refresh locks, preserve the box's account identity, and suppress automatic host OAuth-token passthrough. - Host Claude config can be large enough that full-tree copies and recursive ownership walks make every startup painfully slow. Keep Claude config sync incremental, live-mount
~/.claude/projectsfor resume continuity, skip volatile debug data, and keep ownership fixes targeted. Preserve--no-claude-authcredential isolation when optimizing the host-side staging path. - Startup helpers inherit the agent session's TTY even when their output is captured. Any first-run tool setup must explicitly suppress interactive prompts rather than assuming command substitution makes it non-interactive; keep consent choices available later from the interactive tool itself.