Compare commits

...

7 Commits

Author SHA1 Message Date
pi 046a5aeb3b release: v2.8.0 — sonnet-5 defaults, opencode 1.17.20, docs backfill
Publish Docker Image / resolve-versions (push) Successful in 13s
Lint workflows / actionlint (push) Successful in 14s
Lint workflows / hadolint (push) Successful in 8s
Publish Docker Image / base-decide (push) Successful in 16s
Publish Docker Image / build-base (push) Successful in 46m27s
Publish Docker Image / smoke-omos (push) Successful in 5m16s
Publish Docker Image / smoke-base (push) Successful in 12m28s
Publish Docker Image / build-variant-omos (push) Successful in 19m47s
Publish Docker Image / build-variant-base (push) Successful in 14m30s
Publish Docker Image / update-description (push) Successful in 8s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Promote CHANGELOG Unreleased -> v2.8.0. Contents:
- Default models -> claude-sonnet-5 (changed-but-compatible; new containers only)
- opencode 1.17.15 -> 1.17.20
- bg-subagents removal-trigger markers refreshed 1.17.20/omos 2.2.0 (base-affecting
  -> base image rebuilds this release)
- README backfill for v2.4.0-v2.7.0 features + README/AGENTS polish

Minor per the repo versioning policy (tool bump paired with changed-compatible
default behaviour -> feature wins -> minor).
2026-07-14 00:17:38 +02:00
pi fac14e2c56 docs+chore: refresh bg-subagents markers to 1.17.20/omos 2.2.0, link THIRD_PARTY, guard build-args table
Lint workflows / hadolint (push) Successful in 17s
Lint workflows / actionlint (push) Successful in 22s
Validate / base-change-warning (push) Successful in 8s
Validate / docs-check (push) Successful in 11s
Validate / validate-base (push) Has been skipped
Validate / validate-omos (push) Has been skipped
- Dockerfile.base + AGENTS.md: bg-subagents removal-trigger last-checked markers
  1.17.15/omos 2.1.0 -> 1.17.20/omos 2.2.0 (re-verified; trigger still not fired).
  The Dockerfile.base comment edit changes the base-content hash, so base-<hash>
  advances and the base image rebuilds on the next release.
- README: refresh stale OPENCODE_VERSION build-arg examples (1.5.0, 1.17.8 -> 1.17.20);
  link THIRD_PARTY.md from the License section.
- AGENTS.md: adding a new floated *_VERSION ARG must also update the README Build Args
  table (closes the coupling gap that hid MICRO/TEALDEER/TYPST for 3 releases).
- CHANGELOG Unreleased: record both under Changed + Documentation.
2026-07-14 00:09:53 +02:00
pi cc890a90e7 docs(readme): backfill user docs for v2.4.0-v2.7.0 features
Lint workflows / hadolint (push) Successful in 9s
Lint workflows / actionlint (push) Successful in 20s
Features that shipped without README prose are now documented:
- PDF export (pandoc --pdf-engine=typst) — new Document conversion section
- Terminal TERM/terminfo support (ncurses-term, kitty-terminfo, xterm-ghostty)
- Neovim 24-bit colour default (termguicolors via system sysinit.vim)
- first-shell host SSH reachability check (Shell defaults list)
- baked global gitignore (patterns + core.excludesFile wiring)
- Build Args table: add MICRO_VERSION, TEALDEER_VERSION, TYPST_VERSION

Docs-only; noted under CHANGELOG Unreleased. Hub template and .env.example
were already accurate. Drift found via a v2.4.0+ CHANGELOG-vs-docs audit.
2026-07-13 23:57:57 +02:00
pi 1295c56930 chore: bump opencode 1.17.15 -> 1.17.20 + pre-flight version-check guidance
Lint workflows / hadolint (push) Successful in 8s
Lint workflows / actionlint (push) Successful in 15s
Validate / base-change-warning (push) Successful in 6s
Validate / docs-check (push) Successful in 18s
Validate / validate-base (push) Successful in 3m46s
Validate / validate-omos (push) Successful in 19m9s
- Dockerfile.variant: OPENCODE_VERSION 1.17.15 -> 1.17.20 (latest stable on npm,
  verified via npm view). Variant-only layer; base image untouched.
- AGENTS.md: Pre-flight check now requires checking whether opencode is behind
  and ASKING THE USER before bumping ahead of a release CI build; links the
  bg-subagents removal-trigger re-check.
- CHANGELOG Unreleased: record the bump; 1.17.16-1.17.20 are TUI/cosmetic +
  internal fixes; bg-subagents removal-trigger re-checked, not fired.
2026-07-13 23:43:19 +02:00
pi 0cbcea83dc chore(config): bump default amazon-bedrock model to claude-sonnet-5
Lint workflows / hadolint (push) Successful in 8s
Validate / docs-check (push) Successful in 9s
Lint workflows / actionlint (push) Successful in 15s
Validate / base-change-warning (push) Successful in 8s
Validate / validate-base (push) Has been skipped
Validate / validate-omos (push) Has been skipped
DEFAULT_MODELS[amazon-bedrock] -> amazon-bedrock/global.anthropic.claude-sonnet-5
(was global.anthropic.claude-sonnet-4-5-20250929-v1:0). Verified invokable via
pi --list-models. Folded into the existing Unreleased changelog entry; openai
(gpt-5.4) default unchanged.
2026-07-13 23:16:15 +02:00
pi 4024c4d87b chore(config): bump default anthropic model to claude-sonnet-5
Validate / docs-check (push) Successful in 7s
Lint workflows / hadolint (push) Successful in 13s
Lint workflows / actionlint (push) Successful in 18s
Validate / base-change-warning (push) Successful in 6s
Validate / validate-base (push) Has been skipped
Validate / validate-omos (push) Has been skipped
DEFAULT_MODELS[anthropic] (and thus FALLBACK_MODEL) -> anthropic/claude-sonnet-5,
matching the .env.example bump. openai/amazon-bedrock defaults unchanged.
Documented under CHANGELOG Unreleased (no release cut yet).
2026-07-13 23:13:24 +02:00
pi 4499bcf6c3 docs(env): bump example OPENCODE_MODEL to claude-sonnet-5
Lint workflows / hadolint (push) Successful in 8s
Validate / base-change-warning (push) Successful in 6s
Validate / docs-check (push) Successful in 14s
Lint workflows / actionlint (push) Successful in 22s
Validate / validate-base (push) Successful in 3m43s
Validate / validate-omos (push) Successful in 19m31s
2026-07-13 23:03:01 +02:00
7 changed files with 64 additions and 16 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
OPENCODE_PROVIDER=anthropic
# Model override (optional, defaults per provider)
# OPENCODE_MODEL=anthropic/claude-sonnet-4-6
# OPENCODE_MODEL=anthropic/claude-sonnet-5
# ── API Keys (set the one matching your provider) ────────────────────
# ANTHROPIC_API_KEY=
+3 -4
View File
@@ -51,8 +51,7 @@ repo, which decoupled from the pi tool version at its own `v1.0.0`.
- **PATCH** — opencode/tool version bumps and small fixes that don't change the
contract. When a release pairs a tool bump with a feature, the feature wins
and it's a minor.
- **Pre-flight check** — whenever an opencode bump is part of the release,
verify it is real before claiming it in the CHANGELOG:
- **Pre-flight check** — **before triggering any release CI build, check whether opencode is behind and offer to bump it.** Compare the pinned `OPENCODE_VERSION` in `Dockerfile.variant` against the latest npm release (`./check-versions.sh Dockerfile.variant` flags `→ X available` for opencode — note the base-only pins like `GOSU_VERSION` live in `Dockerfile.base`, so run it against that too for a full sweep). **If the latest stable `opencode-ai` is newer than the pin, ASK THE USER whether to bump before the build — never bump silently;** a version bump is a functional change they may want to hold or schedule. When a bump *is* part of the release, re-check the `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` removal-trigger against the new version's release notes (see the bg-subagents note under *Critical conventions*), then verify the pin is real before claiming it in the CHANGELOG:
```bash
npm view opencode-ai version # must equal the X.Y.Z you pin in Dockerfile.variant
```
@@ -101,14 +100,14 @@ curl -s https://api.github.com/repos/anomalyco/opencode/releases/tags/v1.15.10 |
Release-day checklist: README → (regenerate DOCKER_HUB.md only if HUB_TEMPLATE changed) → promote CHANGELOG Unreleased → grep AGENTS.md for stale counts → commit → tag → push tag.
**Between releases the same coupling applies.** Doc drift is not just a release-day concern — a workflow tweak, entrypoint change, or `generate-config.py` refactor can leave any of these four files lying. Before committing a non-release change, grep the docs for references to what you touched: `git diff --name-only HEAD | xargs -I{} grep -l 'thing-you-changed' README.md AGENTS.md DOCKER_HUB.md .gitea/README.md .env.example`. If a doc says "four variants" / "two phases" / "runs on amd64 only" and your change made that no longer true, fix it in the same commit.
- **GitHub/Gitea-sourced binaries float by default** — gosu, fzf, git-lfs, gitleaks, nvim, bat, eza, zoxide, uv, gitea-mcp, Go, oh-my-opencode-slim all default to `latest`. Each build-time install step reads the `/releases/latest` Location redirect (or the go.dev JSON feed for Go) and derives the concrete version. Use the same `ARCH` case-switch pattern for multi-arch support (amd64/arm64) — mind project-specific arch-name deviations (gitleaks uses `x64`, bat/eza/zoxide use `x86_64`/`aarch64`, gosu uses `amd64`/`arm64`). Intentional pins: `OPENCODE_VERSION` (drives the image tag), `NODE_VERSION=22` (major pin), `DEBIAN_VERSION=trixie-slim` (OS base). Adding a new upstream tool: follow the existing floated-version pattern, don't hardcode a specific tag.
- **GitHub/Gitea-sourced binaries float by default** — gosu, fzf, git-lfs, gitleaks, nvim, bat, eza, zoxide, uv, gitea-mcp, Go, oh-my-opencode-slim all default to `latest`. Each build-time install step reads the `/releases/latest` Location redirect (or the go.dev JSON feed for Go) and derives the concrete version. Use the same `ARCH` case-switch pattern for multi-arch support (amd64/arm64) — mind project-specific arch-name deviations (gitleaks uses `x64`, bat/eza/zoxide use `x86_64`/`aarch64`, gosu uses `amd64`/`arm64`). Intentional pins: `OPENCODE_VERSION` (drives the image tag), `NODE_VERSION=22` (major pin), `DEBIAN_VERSION=trixie-slim` (OS base). Adding a new upstream tool: follow the existing floated-version pattern, don't hardcode a specific tag — **and add its `*_VERSION` ARG to the README *Build Args* table** (the floated-`latest` row alongside `GOSU_VERSION, FZF_VERSION, …`). That table is a *separate* exhaustive list from the env-var table and has silently drifted before: `MICRO_VERSION`, `TEALDEER_VERSION`, and `TYPST_VERSION` were missing from it for three releases (v2.4.0v2.7.0), caught only by the 2026-07-13 doc-drift audit.
- **Resolved versions are logged by the smoke test** — `scripts/smoke-test.sh` prints a "Resolved component versions" table as its first step. CI logs always capture what got baked into a given image even when ARGs default to `latest`.
- **`OMOS_VERSION` MUST be passed by CI as a concrete version**, not left at the `latest` default. The npm install step in `Dockerfile.variant` (`oh-my-opencode-slim@${OMOS_VERSION}`) produces an identical layer-hash when the ARG value is byte-identical across builds; combined with the registry buildcache (`base-buildcache`) the layer gets reused even when `latest` would have resolved to a newer upstream. This is the same class of bug that bit pi-devbox v0.74.0 → v0.75.5 (silent same-bytes-across-releases regression discovered 2026-05-23, fixed in pi-devbox v0.75.5b). It is currently *masked* in opencode-devbox by `OPENCODE_VERSION` being a hard-coded ARG that bumps every release — that bump invalidates the parent-chain cache key for the downstream omos layer — but the masking would fail the moment a `vN.N.Nb` opencode-version-unchanged release ships that only bumps omos. Preventative fix: `.gitea/workflows/docker-publish-split.yml` has a `resolve-versions` job that runs `npm view oh-my-opencode-slim version`, exposing the concrete value as an output that the omos smoke + build jobs consume via build-args. Smoke tests assert via the `EXPECTED_OMOS_VERSION` env var — would catch the regression on the next release rather than several releases later. **If you change the variant build-args list, the resolve-versions job, or the smoke EXPECTED_*_VERSION wiring, audit all affected jobs in lockstep.**
- **`resolve-versions` also pins `mempalace-toolkit` to a SHA** — `resolve-versions` resolves the `mempalace-toolkit` `main` HEAD to a commit SHA (`mempalace_toolkit_ref` output) via the gitea commits API (`/api/v1/repos/joakimp/mempalace-toolkit/commits?limit=1&sha=main`; gitea allows **unauthenticated** public-repo listing, so no secret is required). Unlike every other dependency, `mempalace-toolkit` is cloned in **`Dockerfile.base`**, not the variant — so the resolve→build-arg→variant plumbing bypasses it. To make a moved toolkit actually land, the resolved SHA is **folded into the `base-decide` hash** (so `base_tag` changes → base rebuilds) AND passed to `build-base` as `--build-arg MEMPALACE_TOOLKIT_REF`. Consequently **`base-decide` now depends on `resolve-versions`** (they no longer run in parallel), and the base clone uses a SHA-capable `git fetch <ref> + checkout FETCH_HEAD` (a `git clone --branch <40-char-SHA>` would fail). Trade-off: `base_tag` now reflects a live gitea lookup — on an API blip it falls back to `main`, triggering one *extra* base rebuild, never a *missed* one. If you touch `resolve-versions`, `base-decide`'s hash inputs, or the `build-base` build-args, audit `.gitea/README.md` Step 1 in lockstep.
- **Registry buildkit cache-export is currently disabled** — do NOT re-add `cache-from`/`cache-to` to the `build-base` step in `.gitea/workflows/docker-publish-split.yml` without first verifying that buildkit's `mode=max` cache-export to `registry-1.docker.io` no longer returns HTTP 400 from the Hub CDN edge. The regression surfaced ~2026-05-23 and broke five consecutive opencode-devbox publish attempts (runs #332/333/334/336 + a rerun); root-caused on 2026-05-28 by a manual host-side publish that reproduced the same 400 only on `--cache-to` while image push worked fine. Failure shape is stable (`Offset:0` in the `_state` token, HTML response body = CDN-tier rejection, not registry backend), repo-specific (we're the only repo writing `:base-buildcache` mode=max), and explains why pinning `setup-buildx-action@v4.0.0` didn't help (action pin doesn't change the bundled buildkit version on the catthehacker runner image). Trade-off: dockerfile.base changes pay a full ~3 min rebuild instead of pulling cached layers; unchanged bases short-circuit at the Hub-probe step in `base-decide` and never re-build anyway. Variants don't use registry cache so they're unaffected. Re-enable condition: upstream moby/buildkit fix lands AND a low-risk test run succeeds without 400s. See CHANGELOG v1.15.12 `Unreleased` block for the full diagnostic chain. Manual escape-hatch publish procedure: `docs/manual-host-publish.md`.
- **Push steps wrap `docker buildx build --push` in a 3-attempt retry loop** (15s, 30s backoff) for transient `registry-1.docker.io` blips — rate limits, brief 5xx, CDN flap. Implemented as inline `shell: bash` steps with `docker buildx build` raw rather than `docker/build-push-action@v7` so the loop is visible and tweakable. Affects the 1 base + 5 variant push steps in `.gitea/workflows/docker-publish-split.yml`; smoke-test builds (`load: true`, no push) are untouched. **This does NOT mask deterministic failures** — a true regression (like the cache-export 400 of 2026-05-23..28) fails all 3 attempts identically and the job still fails. Orthogonal to the cache-export disablement above: cache-export was about a deterministic protocol mismatch, retry is about absorbing genuine transients. Both are belt-and-braces with the `ci-release-watcher` skill's transient-rerun heuristic. If you change the matrix of push steps, keep the retry wrapper consistent across them — the pattern is duplicated rather than factored out because Gitea Actions doesn't support reusable composite shell steps cleanly.
- **Shell scripts use `set -euo pipefail`** — both entrypoints are strict. Errors in volume chown or SSH permission operations are intentionally suppressed with `|| true`.
- **Background subagents flag baked ON — experimental, watch for promotion** — `Dockerfile.base` sets `ENV OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`. opencode gates native background subagents behind this flag (`packages/opencode/src/tool/task.ts` fails with `Background subagents require OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` when unset); `oh-my-opencode-slim` V2+ makes background orchestration its **default** workflow, so the omos variant is effectively degraded without it. It's a base ENV (applies to both variants; harmless on plain opencode — only *enables* a capability) and stays runtime-overridable (`-e …=false`). It's counted in the base hash, so editing that line advances `base-latest`. **REMOVAL TRIGGER:** when opencode promotes background subagents out of `EXPERIMENTAL_` (flag renamed or made default), drop the ENV. No upstream roadmap date as of opencode 1.17.15 / omos 2.1.0 (2026-07). Documented in lockstep in README env table, `.env.example`, and asserted by `scripts/smoke-test.sh` (`bg-subagents env baked`).
- **Background subagents flag baked ON — experimental, watch for promotion** — `Dockerfile.base` sets `ENV OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`. opencode gates native background subagents behind this flag (`packages/opencode/src/tool/task.ts` fails with `Background subagents require OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` when unset); `oh-my-opencode-slim` V2+ makes background orchestration its **default** workflow, so the omos variant is effectively degraded without it. It's a base ENV (applies to both variants; harmless on plain opencode — only *enables* a capability) and stays runtime-overridable (`-e …=false`). It's counted in the base hash, so editing that line advances `base-latest`. **REMOVAL TRIGGER:** when opencode promotes background subagents out of `EXPERIMENTAL_` (flag renamed or made default), drop the ENV. No upstream roadmap date as of opencode 1.17.20 / omos 2.2.0 (2026-07). Documented in lockstep in README env table, `.env.example`, and asserted by `scripts/smoke-test.sh` (`bg-subagents env baked`).
- **MemPalace `diary_write` anyOf workaround — upstream watch target** — `Dockerfile.base` carries a perl RUN block that strips a root-level `anyOf` from `mempalace_diary_write`'s advertised `inputSchema`. Mempalace 3.3.x/3.4.0 advertise `anyOf: [{required:[entry]},{required:[content]}]`, which Anthropic's tools API (and Codex) reject at session start (`input_schema does not support oneOf, allOf, or anyOf at the top level`), making the whole MCP server fail to load. The workaround is idempotent and self-deactivating: when upstream ships the real fix the regex stops matching and the build prints `WARN: ... upstream may have changed shape` — **that WARN is the signal to delete the RUN block.** Upstream status (last checked **2026-06-14**): issue **#1728 is still OPEN**; PR **#1735 is CLOSED UNMERGED (2026-06-11) — do NOT watch it, it is dead**; PR **#1717 is the current live fix candidate**; mempalace PyPI latest is **3.4.0 (== our pin)**, so **no release contains the fix yet** and the workaround must stay. **Removal trigger:** a mempalace release **> 3.4.0** that actually strips the root-level `anyOf` lands on PyPI — then bump `MEMPALACE_VERSION` (in lockstep with pi-devbox) and drop the RUN block. NOTE: `MEMPALACE_VERSION` (the pip pin) and `MEMPALACE_TOOLKIT_REF` (the git ref for the `mempalace-toolkit` clone) are unrelated despite the shared prefix; do not conflate them.
- **MemPalace install path** — installed via `uv tool install` into `/opt/uv-tools/mempalace/`. Both the `mempalace` CLI and the `mempalace-mcp` MCP server binary are shipped as entry points by the mempalace package itself and placed on PATH by uv as shims whose shebangs point at the venv's Python. No hand-rolled wrapper is needed. Do not use `pip install --break-system-packages` — that was the previous approach and has been removed. Do not use `["python3", "-m", "mempalace.mcp_server"]` in `opencode.jsonc` — system Python can't import from the uv venv.
- **generate-config.py idempotency** — the script MUST never overwrite an existing `opencode.jsonc` or legacy `opencode.json`. Config persists in the `devbox-opencode-config` named volume; accidentally clobbering that file would destroy hand-edits. The smoke test asserts this.
+30
View File
@@ -6,6 +6,36 @@ Tags follow **independent semver** (since `v2.0.0`) — they version *this image
---
## v2.8.0 — 2026-07-13
Minor release. Headline: **default models move to `claude-sonnet-5`** and opencode bumps `1.17.15 → 1.17.20`. Rounds out with a large user-docs backfill — five v2.4.0v2.7.0 base features (typst PDF export, terminal terminfo, Neovim 24-bit colour, the first-shell host SSH reachability check, the baked global gitignore) that had shipped without README prose — plus the bg-subagents removal-trigger "last-checked" markers refreshed to current versions. The marker refresh edits `Dockerfile.base`, so `base-<hash>` advances and the **base image rebuilds** this release (not just the variants).
### Changed
- **Default models bumped to `claude-sonnet-5`.** In
`rootfs/usr/local/lib/opencode-devbox/generate-config.py`,
`DEFAULT_MODELS["anthropic"]``anthropic/claude-sonnet-5` (was
`claude-sonnet-4-6`; also feeds `FALLBACK_MODEL`) and
`DEFAULT_MODELS["amazon-bedrock"]`
`amazon-bedrock/global.anthropic.claude-sonnet-5` (was
`...claude-sonnet-4-5-20250929-v1:0`). Also updates the commented
`OPENCODE_MODEL` example in `.env.example`. Takes effect for new containers
when no explicit `OPENCODE_MODEL` override is set. The `openai` default
(`gpt-5.4`) is unchanged.
- **opencode bumped `1.17.15``1.17.20`** (`Dockerfile.variant`
`OPENCODE_VERSION`; latest stable on npm, verified via `npm view
opencode-ai version`). 1.17.161.17.20 are TUI/cosmetic and internal fixes
(command-palette flash, sub-agent task-row styling, an OpenAI Responses
workaround removal) — no config-affecting or breaking changes. The
`OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` removal-trigger was re-checked
against these release notes and has **not** fired; the flag stays baked ON.
- **Refreshed the bg-subagents removal-trigger "last-checked" markers** (`Dockerfile.base`, `AGENTS.md`) from `opencode 1.17.15 / omos 2.1.0` to `opencode 1.17.20 / omos 2.2.0`. Comment-only and the trigger has still **not** fired — but the `Dockerfile.base` edit changes the base-content hash, so `base-<hash>` advances and **the base image rebuilds** on the next release (not just the variant).
### Documentation
- **README backfill for v2.4.0v2.7.0 features that shipped without user-facing docs:** PDF export via `pandoc --pdf-engine=typst` (new *Document conversion* section), terminal `TERM`/terminfo support (`ncurses-term` + `kitty-terminfo` + `xterm-ghostty`), Neovim 24-bit colour default (`termguicolors` via system `sysinit.vim`), the first-shell host SSH reachability check, and the baked global gitignore. Also adds the three floated build args missing from the Build Args table (`MICRO_VERSION`, `TEALDEER_VERSION`, `TYPST_VERSION`). Docs-only — no image change.
- **README + AGENTS.md polish:** refreshed stale `OPENCODE_VERSION=` build-arg examples (`1.5.0`, `1.17.8` → the current `1.17.20`), linked the new `THIRD_PARTY.md` from the *License* section, and added an AGENTS.md reminder to add any new floated `*_VERSION` ARG to the README *Build Args* table (the coupling gap that hid `MICRO`/`TEALDEER`/`TYPST` for three releases).
## v2.7.0 — 2026-07-13
Minor release. Headline: **further parity with the sibling `pi-devbox` image**
+1 -1
View File
@@ -449,7 +449,7 @@ ENV PATH="/home/developer/.local/bin:/home/developer/.cargo/bin:${PATH}"
# *enables* a capability). Overridable at runtime: -e OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=false.
# REMOVAL TRIGGER: when opencode promotes background subagents out of experimental
# (flag becomes default / renamed), drop this ENV. No upstream roadmap date as of
# opencode 1.17.15 / omos 2.1.0 (2026-07).
# opencode 1.17.20 / omos 2.2.0 (2026-07).
ENV OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
# ── Node.js (required for opencode/pi/omos at variant build + MCP servers) ──
+1 -1
View File
@@ -39,7 +39,7 @@ ARG USER_NAME=developer
# edit, so the cache-hit class of bug that bit pi-devbox v0.74.0..
# v0.75.5 cannot apply here.
ARG INSTALL_OPENCODE=true
ARG OPENCODE_VERSION=1.17.15
ARG OPENCODE_VERSION=1.17.20
RUN if [ "${INSTALL_OPENCODE}" = "true" ]; then \
NPM_CONFIG_PREFIX=/usr npm install -g opencode-ai@${OPENCODE_VERSION} && \
opencode --version ; \
+26 -5
View File
@@ -264,11 +264,23 @@ volumes:
- ~/.config/nvim:/home/developer/.config/nvim:ro
```
24-bit colour is on by default — a system-wide `sysinit.vim` (in `/etc/xdg/nvim/`) enables `termguicolors` so strings and comments render in full colour over SSH instead of a muddy 256-colour palette. It loads before any personal config and stays overridable per-user (`:set notermguicolors`, or your own `~/.config/nvim/init`).
> **Not a vi person?** The image also ships two non-modal editors alongside nvim:
> **nano** (classic, minimal) and **micro** (modern — desktop-style `Ctrl+S`/`Ctrl+Q`
> keys, mouse, syntax highlighting). `EDITOR` stays `nvim`; opt in per-shell with
> `export EDITOR=nano` (or `micro`), or for git with `git config --global core.editor micro`.
### Document conversion (Markdown → PDF)
The image ships [`pandoc`](https://pandoc.org/) with [`typst`](https://typst.app/) as the PDF engine, so Markdown → PDF works out of the box with no LaTeX install:
```bash
pandoc README.md -o readme.pdf --pdf-engine=typst
```
The bundled pandoc typst template defaults the font to `Libertinus Serif`, so a bare `--pdf-engine=typst` renders without needing `-V mainfont`. For higher-fidelity or complex layouts, install TeX Live on demand and use `--pdf-engine=xelatex` instead.
### Python development with uv
The image includes Python 3.13 (from Debian Trixie) and [uv](https://docs.astral.sh/uv/), a fast Python package manager that replaces pip, venv, and pyenv:
@@ -440,7 +452,7 @@ Enable optional language runtimes, pin a specific opencode version, or lock any
```bash
docker compose build --build-arg INSTALL_GO=true
docker compose build --build-arg OPENCODE_VERSION=1.5.0
docker compose build --build-arg OPENCODE_VERSION=1.17.20
docker compose build --build-arg NVIM_VERSION=0.12.1 # pin to a specific version
```
@@ -453,7 +465,7 @@ docker compose build --build-arg NVIM_VERSION=0.12.1 # pin to a specific versi
| `INSTALL_OPENCODE` | `true` | Install opencode. Set `false` to build a base with no harness (still includes Bun if `INSTALL_OMOS=true`). |
| `OPENCODE_VERSION` | *(pinned per release)* | opencode npm version. Drives the image tag and is intentionally not floated. |
| `NODE_VERSION` | `22` | Node.js major version. Pinned to protect against upstream breaking changes across majors. |
| `GOSU_VERSION`, `FZF_VERSION`, `GIT_LFS_VERSION`, `NVIM_VERSION`, `BAT_VERSION`, `EZA_VERSION`, `ZOXIDE_VERSION`, `UV_VERSION`, `GITEA_MCP_VERSION`, `GO_VERSION`, `OMOS_VERSION` | `latest` | All GitHub/Gitea/go.dev-hosted binaries resolve to the newest upstream release at build time. Override with a specific version to pin. Resolved versions are logged in CI output. |
| `GOSU_VERSION`, `FZF_VERSION`, `GIT_LFS_VERSION`, `NVIM_VERSION`, `BAT_VERSION`, `EZA_VERSION`, `ZOXIDE_VERSION`, `UV_VERSION`, `GITEA_MCP_VERSION`, `GO_VERSION`, `OMOS_VERSION`, `MICRO_VERSION`, `TEALDEER_VERSION`, `TYPST_VERSION` | `latest` | All GitHub/Gitea/go.dev-hosted binaries resolve to the newest upstream release at build time. Override with a specific version to pin. Resolved versions are logged in CI output. |
> **Reproducibility note:** With `latest` defaults, two builds of the same `v{opencode}` tag may embed different tool versions if upstream releases have happened in between. This is intentional — it means every rebuild picks up upstream CVE fixes automatically. If you need a bit-for-bit reproducible build, pass explicit `*_VERSION` args. The CI smoke test logs the resolved versions for every release build.
@@ -671,7 +683,7 @@ docker build -f Dockerfile.base -t myorg/opencode-devbox:base-dev \
# then the variant FROM that base
docker build -f Dockerfile.variant -t myorg/opencode-devbox:dev \
--build-arg BASE_IMAGE=myorg/opencode-devbox:base-dev \
--build-arg OPENCODE_VERSION=1.17.8 .
--build-arg OPENCODE_VERSION=1.17.20 .
```
Note: mempalace-toolkit clones anonymously (no token needed). Only the `resolve-versions` CI job calls the gitea API (which needs a token for public repos). A plain `docker build` like the above skips that job entirely, so no credentials are required.
@@ -761,6 +773,7 @@ Defaults you get out of the box:
- **Aliases** — `ls`/`ll`/`la` use `eza`, `cat` uses `bat`, `gs`/`gd`/`gl` for git, safe `rm`/`mv`/`cp`.
- **Integrations** — `zoxide` (`z <fragment>` to jump), `fzf` Ctrl-R / Ctrl-T key bindings.
- **Prompt marker** — `[devbox]` prefix so it's always obvious you're inside the container.
- **Host SSH reachability check** — on the first shell of a freshly (re)created container, a one-time probe checks whether the Docker host is reachable over SSH. If it isn't (host Remote Login off, or the container's `devbox_jump` key not yet authorized), it prints a clear two-step fix with the container's public key inline; it stays silent once SSH works and is skipped on later shells.
### Overriding the defaults
@@ -794,6 +807,14 @@ cp /etc/skel-devbox/.bash_aliases ~/.bash_aliases
rm ~/.bash_aliases
```
## Terminal compatibility
The base ships `ncurses-term` and `kitty-terminfo` on top of the default `ncurses-base`, plus a compiled `xterm-ghostty` alias, so modern terminal emulators resolve their `TERM` correctly over SSH instead of degrading to a dumb fallback. Covered out of the box: WezTerm, Alacritty, foot, st, kitty (`xterm-kitty`), Ghostty (`xterm-ghostty`), and iTerm2 / xterm (`xterm-256color`).
## Global gitignore
The image bakes a `~/.gitignore_global` and wires it via `git config --global core.excludesFile`, so personal/tooling artifacts are ignored across every repo in the container without per-repo `.gitignore` entries. Seeded patterns include `*.bak`, `*.bak.*`, `*~`, `*.orig`, `*.swp`, `*.tmp`, and `**/.claude/settings.local.json` (Claude Code's per-machine settings, which can carry credentials). It is seeded only if absent — edit it freely, and your version survives recreate — and the `core.excludesFile` wiring is skipped if you already set one.
## Secret Scanning
A [gitleaks](https://github.com/gitleaks/gitleaks) pre-commit hook prevents accidentally committing API keys, passwords, or other secrets.
@@ -837,7 +858,7 @@ Container (Debian trixie)
├── AWS CLI v2 (SSO + Bedrock auth)
├── neovim 0.12, nano, micro, tmux, htop, bat, eza, zoxide, uv, rustup, make, gcc, g++, rsync
├── git, git-crypt, age, gitleaks, ssh, ripgrep, fd, fzf, jq, yq, curl, tree
├── pandoc (Markdown↔HTML/PDF/etc. conversion)
├── pandoc + typst (Markdown→PDF via --pdf-engine=typst; HTML/etc.)
├── graphviz (dot diagram rendering)
├── tldr (tealdeer — quick command examples)
├── dot-watch (auto-render .dot to PNG on save)
@@ -869,4 +890,4 @@ Container (Debian trixie)
## License
MIT
MIT — see [`LICENSE`](LICENSE). The MIT license covers this repository's own contents (Dockerfiles, entrypoint scripts, `rootfs/` seeds, CI workflows, docs). The **published container images** additionally *bundle* third-party software, each under its own terms — see [`THIRD_PARTY.md`](THIRD_PARTY.md) for the summary.
@@ -41,11 +41,9 @@ from pathlib import Path
# Default model per provider. Update here when upstream changes.
DEFAULT_MODELS: dict[str, str] = {
"anthropic": "anthropic/claude-sonnet-4-6",
"anthropic": "anthropic/claude-sonnet-5",
"openai": "openai/gpt-5.4",
"amazon-bedrock": (
"amazon-bedrock/global.anthropic.claude-sonnet-4-5-20250929-v1:0"
),
"amazon-bedrock": "amazon-bedrock/global.anthropic.claude-sonnet-5",
}
# Fallback when OPENCODE_PROVIDER is set but not recognized.