Compare commits

..

8 Commits

Author SHA1 Message Date
pi d68674d11e chore(release): v1.5.0
Publish Docker Image / resolve-versions (push) Successful in 12s
Publish Docker Image / base-decide (push) Successful in 9s
Lint / hadolint (push) Successful in 12s
Lint / actionlint (push) Successful in 13s
Publish Docker Image / build-base (push) Successful in 43m20s
Publish Docker Image / smoke (push) Successful in 4m20s
Publish Docker Image / smoke-studio (push) Successful in 7m2s
Publish Docker Image / build-variant (push) Successful in 16m18s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 14s
Publish Docker Image / build-variant-studio (push) Successful in 17m47s
Roll the 7 unreleased commits since v1.4.0 into v1.5.0 (minor — significant
base additions: readable Neovim true-colour, broad terminal terminfo support).
Also includes: typst PDF template font fix, .claude gitignore seed, pi-studio
semver-tag pin + version label, and repo hygiene (LICENSE, THIRD_PARTY.md,
.dockerignore, hadolint lint, IDEAS backlog). No pi bump — CI resolves latest
pi from npm at build time as usual.
2026-07-13 18:56:51 +02:00
pi 8c27894cf2 base: support modern terminals (ncurses-term + xterm-ghostty alias)
Lint / hadolint (push) Successful in 8s
Lint / actionlint (push) Successful in 15s
The base only shipped ncurses-base (xterm-256color, tmux), so SSHing in from
WezTerm/Alacritty/foot/Ghostty degraded to a dumb TERM fallback. Following the
maintainer's ansible common role:

- Dockerfile.base: install ncurses-term (terminfo for wezterm, alacritty,
  foot, st, base ghostty entry, and many more).
- rootfs/.../terminfo-src/ghostty.terminfo: thin xterm-ghostty alias
  (use=ghostty) — Ghostty connects as TERM=xterm-ghostty, which no distro
  packages. Compiled into the system db with 'tic -x'; build asserts it
  resolved via infocmp.
- kitty (xterm-kitty) already covered by kitty-terminfo; iTerm2 uses
  xterm-256color (ncurses-base).
- smoke-test: assert ncurses-term emulators + xterm-ghostty alias resolve.

Validated end-to-end in a throwaway container: ncurses-term brings the
entries, the alias compiles and equals the ghostty capability set. hadolint
clean, bash -n OK. Base-affecting, rebuilds base-<hash>. No tag.
2026-07-13 18:45:38 +02:00
pi 291ae5345e repo: add LICENSE, THIRD_PARTY.md, .dockerignore, hadolint lint, IDEAS backlog
Lint / hadolint (push) Successful in 9s
Lint / actionlint (push) Successful in 16s
Repo/CI hygiene batch (none base-affecting; image contents unchanged):

- LICENSE: actual MIT file (repo previously declared MIT only in prose).
- THIRD_PARTY.md: notes bundled software + licenses (pi/pi-fork/pi-obsmem/
  pi-studio MIT, gosu Apache-2.0, Debian packages under their own terms).
- .dockerignore: trims build context to what the Dockerfiles COPY (rootfs/ +
  entrypoint*.sh); keeps .git/docs/scripts/compose out. Verified it excludes
  none of the required COPY sources.
- lint.yml: new hadolint job (pinned v2.14.0) lints both Dockerfiles;
  .hadolint.yaml grandfathers deliberate choices (DL3008/DL3016/DL4006/DL3003/
  SC2086, mirroring the shellcheck excludes), fails on anything new at warning+.
  Verified hadolint exit 0 and the repo shell-guard passes with the new job.
- IDEAS.md: parks deferred follow-ups (SHA-pin actions, trivy, buildx SBOM/
  provenance, Makefile, renovate).
- README/DOCKER_HUB License sections now link LICENSE + THIRD_PARTY.md.

No tag.
2026-07-13 18:20:44 +02:00
pi 38d8832d34 ci: record pi-studio version as image label
Lint workflows / actionlint (push) Successful in 1m16s
Adds se.jordbo.pi-devbox.pi-studio-version (e.g. v0.9.36) alongside the
existing SHA label, so 'docker inspect' shows the human-readable version.

Plumbing: resolve-versions exposes studio_tag output -> both studio build
steps pass --build-arg PI_STUDIO_VERSION -> Dockerfile.variant declares
ARG PI_STUDIO_VERSION=none and emits the LABEL. Studio-variant only.

Validated: yq parse OK, resolve run-script bash -n OK. No tag.
2026-07-13 17:59:59 +02:00
pi 32586f19e7 ci: pin pi-studio to newest semver tag, not main HEAD
Lint workflows / actionlint (push) Successful in 23s
Upstream omaclaren/pi-studio stopped publishing GitHub Releases at v0.5.55
but keeps tagging every version (v0.9.36 now) and pushing to main. Tracking
main HEAD risked baking half-finished commits that land after a tag.

resolve-versions now lists all tags via a single git ls-remote (the REST
tags API paginates at 100 and the repo has >140 tags, so a single page can
miss the newest), picks the highest X.Y.Z with sort -V (pre-releases
excluded), and peels it to a commit SHA for PI_STUDIO_REF. SHA (not moving
tag) keeps cache-busting + reproducibility; require_sha still enforced.

Studio-variant only, not base-affecting. No change to the resolved commit
today (v0.9.36 == current main HEAD). Validated: yq parse OK, bash -n OK,
live git ls-remote -> v0.9.36 -> 2ef38ef. No tag.
2026-07-13 17:54:56 +02:00
pi aaf1be0bcb base: readable Neovim colours out of the box (termguicolors + kitty-terminfo)
Lint workflows / actionlint (push) Successful in 22s
Vanilla Neovim fell back to a 256-colour palette over ssh/kitty and rendered
strings/comments in a muddy, low-contrast colour. Fix, all base-affecting:

- rootfs/etc/xdg/nvim/sysinit.vim: system vimrc enabling termguicolors. Loads
  for every user before any personal ~/.config/nvim; overridable per-user.
- Dockerfile.base: install kitty-terminfo (TERM=xterm-kitty understood; lets
  Neovim auto-detect true colour) and set ENV COLORTERM=truecolor.
- smoke-test.sh: assert xterm-kitty terminfo present and nvim tgc default on.
- CHANGELOG (Unreleased/Added) + README editor note.

No tag — lands on the next base-<hash> rebuild.
2026-07-12 23:19:19 +02:00
pi 92212fa447 base: seed global gitignore with .claude/settings.local.json
Lint workflows / actionlint (push) Successful in 23s
Claude Code's per-machine local settings can carry credentials and should never
be committed. Add the pattern to the seeded ~/.gitignore_global so fresh
containers match a host global that already ignores it. Existing containers are
unaffected (seed copied only when absent). Base-affecting; rebuilds base-<hash>.
2026-07-11 22:29:28 +02:00
pi 3d46c6615e fix(base): default pandoc typst template font so PDF export works without -V mainfont
Lint workflows / actionlint (push) Successful in 21s
pandoc's bundled typst template defaults the document font to an empty
tuple (font: ()), so a naked `pandoc --pdf-engine=typst` fails with
"font fallback list must not be empty". Patch the template default to
Libertinus Serif (typst's own bundled default) at build time so PDF
export works out of the box. Document usage in README and note the fix
in CHANGELOG (Unreleased). Base-affecting.
2026-07-11 20:55:50 +02:00
16 changed files with 468 additions and 10 deletions
+35
View File
@@ -0,0 +1,35 @@
# Keep the Docker build context minimal and prevent stray files (notably
# `.git`) from ever being pulled in by a future broad COPY. Both Dockerfiles
# only COPY `rootfs/` and `entrypoint*.sh`, so everything below is safe to
# exclude from the context.
#
# DO NOT add `rootfs/`, `entrypoint.sh`, `entrypoint-user.sh`, or the
# Dockerfiles here — they are required to build the image.
# VCS / CI metadata
.git
.gitea
.gitignore
.dockerignore
# Lint / editor config
.hadolint.yaml
.editorconfig
# Docs & project meta
README.md
DOCKER_HUB.md
CHANGELOG.md
AGENTS.md
IDEAS.md
LICENSE
THIRD_PARTY.md
docs
# Local orchestration & examples (compose runs the image; not a build input)
docker-compose.yml
docker-compose.mempalace.yml
.env.example
# Repo tooling / tests (run from a checkout, not baked into the image)
scripts
+32 -4
View File
@@ -135,6 +135,7 @@ jobs:
toolkit_ref: ${{ steps.resolve.outputs.toolkit_ref }} toolkit_ref: ${{ steps.resolve.outputs.toolkit_ref }}
extensions_ref: ${{ steps.resolve.outputs.extensions_ref }} extensions_ref: ${{ steps.resolve.outputs.extensions_ref }}
studio_ref: ${{ steps.resolve.outputs.studio_ref }} studio_ref: ${{ steps.resolve.outputs.studio_ref }}
studio_tag: ${{ steps.resolve.outputs.studio_tag }}
mempalace_toolkit_ref: ${{ steps.resolve.outputs.mempalace_toolkit_ref }} mempalace_toolkit_ref: ${{ steps.resolve.outputs.mempalace_toolkit_ref }}
steps: steps:
- name: Resolve pi version + companion refs - name: Resolve pi version + companion refs
@@ -199,16 +200,40 @@ jobs:
require_sha MEMPALACE_TOOLKIT_REF "$MEMPALACE_TOOLKIT_REF" require_sha MEMPALACE_TOOLKIT_REF "$MEMPALACE_TOOLKIT_REF"
echo "mempalace_toolkit_ref=${MEMPALACE_TOOLKIT_REF}" >> "$GITHUB_OUTPUT" echo "mempalace_toolkit_ref=${MEMPALACE_TOOLKIT_REF}" >> "$GITHUB_OUTPUT"
# pi-studio (omaclaren/pi-studio) → commit SHA for :latest-studio. # pi-studio (omaclaren/pi-studio) → newest SEMVER TAG's commit SHA
STUDIO_REF=$(curl -sf -H "Accept: application/vnd.github.sha" \ # for the :*-studio images. Upstream stopped publishing GitHub
"https://api.github.com/repos/omaclaren/pi-studio/commits/main" || true) # *Releases* at v0.5.55 but keeps tagging every version (vX.Y.Z) and
# pushing to main, so pinning main HEAD risked baking half-finished
# commits that land after a tag. Take the newest stable tag instead.
# List ALL tags in one `git ls-remote` call — the REST tags API
# paginates at 100 and this repo already has >140 tags, so page 1 is
# NOT guaranteed to hold the newest — pick the highest X.Y.Z with
# `sort -V` (pre-releases like -rc1 excluded by the strict filter),
# then resolve its commit SHA (a SHA, not a moving tag, preserves
# cache-busting + reproducibility and is what require_sha demands).
STUDIO_TAGS=$(git ls-remote --tags "https://github.com/omaclaren/pi-studio.git" || true)
STUDIO_TAG=$(printf '%s\n' "$STUDIO_TAGS" | awk '{print $2}' \
| sed -n 's#^refs/tags/##p' \
| grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' \
| sort -V | tail -n1 || true)
if [ -z "${STUDIO_TAG:-}" ]; then
echo "::error::Could not resolve a pi-studio semver tag (git ls-remote empty/unreachable). Refusing to fall back to a floating ref."
exit 1
fi
# Prefer the peeled ^{} line (annotated tags); fall back to the
# direct ref (lightweight tags, which pi-studio currently uses).
STUDIO_REF=$(printf '%s\n' "$STUDIO_TAGS" | awk -v t="refs/tags/${STUDIO_TAG}^{}" '$2==t{print $1}')
if [ -z "$STUDIO_REF" ]; then
STUDIO_REF=$(printf '%s\n' "$STUDIO_TAGS" | awk -v t="refs/tags/${STUDIO_TAG}" '$2==t{print $1}')
fi
require_sha PI_STUDIO_REF "$STUDIO_REF" require_sha PI_STUDIO_REF "$STUDIO_REF"
echo "studio_ref=${STUDIO_REF}" >> "$GITHUB_OUTPUT" echo "studio_ref=${STUDIO_REF}" >> "$GITHUB_OUTPUT"
echo "studio_tag=${STUDIO_TAG}" >> "$GITHUB_OUTPUT"
echo "Resolved PI_VERSION=${PI_VERSION}" echo "Resolved PI_VERSION=${PI_VERSION}"
echo "Resolved PI_FORK_REF=${FORK_REF}, PI_OBSMEM_REF=${OBSMEM_REF}" echo "Resolved PI_FORK_REF=${FORK_REF}, PI_OBSMEM_REF=${OBSMEM_REF}"
echo "Resolved PI_TOOLKIT_REF=${TOOLKIT_REF}, PI_EXTENSIONS_REF=${EXTENSIONS_REF}" echo "Resolved PI_TOOLKIT_REF=${TOOLKIT_REF}, PI_EXTENSIONS_REF=${EXTENSIONS_REF}"
echo "Resolved PI_STUDIO_REF=${STUDIO_REF}" echo "Resolved PI_STUDIO_REF=${STUDIO_REF} (pi-studio ${STUDIO_TAG})"
echo "Resolved MEMPALACE_TOOLKIT_REF=${MEMPALACE_TOOLKIT_REF}" echo "Resolved MEMPALACE_TOOLKIT_REF=${MEMPALACE_TOOLKIT_REF}"
# ── Phase 2: build & push base (multi-arch), only when needed ────── # ── Phase 2: build & push base (multi-arch), only when needed ──────
@@ -390,6 +415,7 @@ jobs:
PI_EXTENSIONS_REF=${{ needs.resolve-versions.outputs.extensions_ref }} PI_EXTENSIONS_REF=${{ needs.resolve-versions.outputs.extensions_ref }}
INSTALL_STUDIO=true INSTALL_STUDIO=true
PI_STUDIO_REF=${{ needs.resolve-versions.outputs.studio_ref }} PI_STUDIO_REF=${{ needs.resolve-versions.outputs.studio_ref }}
PI_STUDIO_VERSION=${{ needs.resolve-versions.outputs.studio_tag }}
MEMPALACE_TOOLKIT_REF=${{ needs.resolve-versions.outputs.mempalace_toolkit_ref }} MEMPALACE_TOOLKIT_REF=${{ needs.resolve-versions.outputs.mempalace_toolkit_ref }}
RELEASE_TAG=smoke-studio RELEASE_TAG=smoke-studio
SOURCE_REVISION=${{ github.sha }} SOURCE_REVISION=${{ github.sha }}
@@ -531,6 +557,7 @@ jobs:
TOOLKIT_REF: ${{ needs.resolve-versions.outputs.toolkit_ref }} TOOLKIT_REF: ${{ needs.resolve-versions.outputs.toolkit_ref }}
EXTENSIONS_REF: ${{ needs.resolve-versions.outputs.extensions_ref }} EXTENSIONS_REF: ${{ needs.resolve-versions.outputs.extensions_ref }}
STUDIO_REF: ${{ needs.resolve-versions.outputs.studio_ref }} STUDIO_REF: ${{ needs.resolve-versions.outputs.studio_ref }}
STUDIO_TAG: ${{ needs.resolve-versions.outputs.studio_tag }}
MEMPALACE_TOOLKIT_REF: ${{ needs.resolve-versions.outputs.mempalace_toolkit_ref }} MEMPALACE_TOOLKIT_REF: ${{ needs.resolve-versions.outputs.mempalace_toolkit_ref }}
run: | run: |
set -euo pipefail set -euo pipefail
@@ -553,6 +580,7 @@ jobs:
--build-arg "MEMPALACE_TOOLKIT_REF=${MEMPALACE_TOOLKIT_REF}" \ --build-arg "MEMPALACE_TOOLKIT_REF=${MEMPALACE_TOOLKIT_REF}" \
--build-arg "INSTALL_STUDIO=true" \ --build-arg "INSTALL_STUDIO=true" \
--build-arg "PI_STUDIO_REF=${STUDIO_REF}" \ --build-arg "PI_STUDIO_REF=${STUDIO_REF}" \
--build-arg "PI_STUDIO_VERSION=${STUDIO_TAG}" \
--build-arg "RELEASE_TAG=${RELEASE_TAG}" \ --build-arg "RELEASE_TAG=${RELEASE_TAG}" \
--build-arg "BUILD_DATE=${BUILD_DATE}" \ --build-arg "BUILD_DATE=${BUILD_DATE}" \
--build-arg "SOURCE_REVISION=${GITHUB_SHA:-}" \ --build-arg "SOURCE_REVISION=${GITHUB_SHA:-}" \
+25 -1
View File
@@ -1,4 +1,4 @@
name: Lint workflows name: Lint
# Durable guard against CI-workflow bugs — most importantly the recurring # Durable guard against CI-workflow bugs — most importantly the recurring
# "bash-only syntax under the default `sh`/dash shell" footgun that broke # "bash-only syntax under the default `sh`/dash shell" footgun that broke
@@ -67,3 +67,27 @@ jobs:
# ("no project was found"). Globbing the workflow files is the # ("no project was found"). Globbing the workflow files is the
# supported way to lint a non-GitHub layout. # supported way to lint a non-GitHub layout.
run: actionlint -color .gitea/workflows/*.yml run: actionlint -color .gitea/workflows/*.yml
hadolint:
# Lint the two Dockerfiles that ARE the project (the shell/actions linting
# above never looked at them). Config — ignored rules + failure threshold
# — lives in .hadolint.yaml, which hadolint reads automatically, so a local
# `hadolint Dockerfile.base` reproduces CI exactly.
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- uses: actions/checkout@v4
- name: Install hadolint (pinned)
env:
HADOLINT_VERSION: 2.14.0
run: |
curl -fsSL \
"https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VERSION}/hadolint-Linux-x86_64" \
-o /usr/local/bin/hadolint
chmod +x /usr/local/bin/hadolint
hadolint --version
- name: Run hadolint
run: hadolint Dockerfile.base Dockerfile.variant
+27
View File
@@ -0,0 +1,27 @@
# hadolint configuration for pi-devbox.
#
# Both Dockerfiles are linted in CI (.gitea/workflows/lint.yml → `hadolint`
# job). hadolint reads this file automatically, so a local
# `hadolint Dockerfile.base` reproduces CI exactly.
#
# The ignores below are DELIBERATE project choices — they mirror the
# philosophy of the shellcheck excludes already applied to `run:` steps
# (SHELLCHECK_OPTS in lint.yml). Anything NOT listed here still fails the
# build at `warning` and above, so new Dockerfile smells are caught going
# forward.
ignored:
- DL3008 # "pin apt versions" — intentionally unpinned: the base tracks
# Debian stable and runs `apt-get upgrade`, so pinning point
# versions would rot and fight security updates.
- DL3016 # "pin npm versions" — pi's version IS pinned, but via the
# PI_VERSION build-arg (CI-resolved from npm), not the npm CLI.
- DL4006 # "set -o pipefail before a pipe" — the piped RUNs are
# download|extract steps with their own retries / `set -e`.
# Switching the global SHELL to bash is a larger, base-affecting
# change — tracked in IDEAS.md.
- DL3003 # "use WORKDIR, not cd" — cosmetic in the few `cd` RUNs here.
- SC2086 # "double-quote to prevent word-splitting" — the same code is
# excluded for shell `run:` steps in lint.yml; splitting is
# intentional in these contexts.
failure-threshold: warning
+95
View File
@@ -11,6 +11,101 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
--- ---
## Unreleased
_Nothing yet — next release's changes will accrue here._
## v1.5.0 — 2026-07-13
### Added
- **Seeded global gitignore now ignores `**/.claude/settings.local.json`.** Claude
Code's per-machine local settings file holds machine-specific permissions and
can carry credentials, so it should never be committed. The seed
(`rootfs/home/developer/.gitignore_global`, baked to `/etc/skel-devbox/`) gains
the pattern so fresh containers match a host global that already ignores it.
Existing containers are unaffected (the seed is copied only when
`~/.gitignore_global` is absent); their file can be updated by hand. Base-
affecting (`Dockerfile.base` COPY of the seed), rebuilds `base-<hash>`.
- **Readable Neovim colours out of the box.** The base now ships a system-wide
Neovim config (`/etc/xdg/nvim/sysinit.vim`) that enables `termguicolors`,
plus the `kitty-terminfo` package. Vanilla Neovim otherwise fell back to a
256-colour palette over ssh/kitty and rendered strings and comments in a
muddy, low-contrast dark colour. `sysinit.vim` is Neovim's system vimrc: it
loads for every user before any personal `~/.config/nvim` and can still be
overridden per-user (`:set notermguicolors`, or your own init). Base-affecting
(`Dockerfile.base` apt package + COPY), rebuilds `base-<hash>`.
- **Terminal support beyond kitty: `ncurses-term` + a compiled `xterm-ghostty`
alias.** The base previously shipped only `ncurses-base` (xterm-256color,
tmux), so SSHing in from a modern emulator degraded to a dumb fallback. The
base now installs `ncurses-term` (terminfo for WezTerm, Alacritty, foot, st,
and the base `ghostty` entry, among many others) and compiles an
`xterm-ghostty` alias with `tic -x` (`use=ghostty`) — Ghostty connects as
`TERM=xterm-ghostty` and no distro packages that name. Combined with
`kitty-terminfo` (xterm-kitty) and xterm-256color (iTerm2's default, already
in ncurses-base), the common modern terminals now resolve their TERM. The
approach mirrors the maintainer's ansible `common` role. Base-affecting
(`Dockerfile.base` apt + COPY + `tic` RUN, plus a new
`rootfs/usr/local/share/terminfo-src/ghostty.terminfo`), rebuilds `base-<hash>`.
- **Repository hygiene: `LICENSE`, `THIRD_PARTY.md`, and `.dockerignore`.** The
repo declared MIT only in prose; it now ships an actual `LICENSE` file (MIT,
© Joakim Persson) plus `THIRD_PARTY.md` recording that the published images
bundle third-party software under its own terms (pi, pi-fork,
pi-observational-memory, pi-studio — all MIT; gosu Apache-2.0; Debian packages
under their respective licenses). A new `.dockerignore` trims the build
context to what the Dockerfiles actually `COPY` (`rootfs/` + `entrypoint*.sh`),
keeping `.git`, docs, `scripts/`, and compose files out — cheaper context and
no risk of a future broad `COPY` pulling in `.git`. Not base-affecting (the
base hash covers only `Dockerfile.base` + `rootfs/` + `entrypoint*.sh`);
image contents are byte-identical.
- **Dockerfile linting (`hadolint`) in CI, plus an `IDEAS.md` backlog.** The
lint workflow already ran actionlint + shellcheck on `run:` steps but never
looked at the two Dockerfiles that are the heart of the project. A new
`hadolint` job (pinned v2.14.0, same download-pin pattern as actionlint) lints
`Dockerfile.base` and `Dockerfile.variant`; `.hadolint.yaml` grandfathers the
deliberate choices (unpinned apt/npm, `cd`-in-`RUN`, `SC2086` — mirroring the
existing shellcheck excludes) and fails on anything new at `warning`+.
`IDEAS.md` parks the vetted-but-unscheduled follow-ups (SHA-pin CI actions,
trivy scanning, buildx SBOM/provenance attestations, a local `Makefile`,
renovate). Repo/CI only — not baked into the image.
### Changed
- **`-studio` images now pin pi-studio to its newest *semver tag* instead of
`main` HEAD.** Upstream `omaclaren/pi-studio` abandoned GitHub *Releases* at
v0.5.55 but keeps tagging every version (currently `v0.9.36`) and pushing to
`main`; tracking `main` HEAD risked baking half-finished commits that land
after a tag. CI (`resolve-versions`) now lists every tag via a single
`git ls-remote` (the REST tags API paginates at 100 and the repo already has
>140 tags), selects the highest `X.Y.Z` with `sort -V` (pre-releases
excluded by a strict filter), and pins that tag's commit SHA into
`PI_STUDIO_REF`. Pinning the SHA (not the moving tag) preserves cache-busting
and reproducibility, is what `require_sha` demands, and is recorded in the
`se.jordbo.pi-devbox.pi-studio-ref` image label. The human-readable tag (e.g.
`v0.9.36`) is now also recorded in a new `se.jordbo.pi-devbox.pi-studio-version`
label for at-a-glance identification (`docker inspect`). Studio-variant only —
not base-affecting; takes effect on the next `-studio` build. No change to the
resolved commit today (`v0.9.36` == current `main` HEAD).
### Fixed
- **`pandoc --pdf-engine=typst` now works without `-V mainfont`.** pandoc's
bundled typst template (`/usr/share/pandoc/data/templates/template.typst`)
defaults the document font to an empty tuple (`font: ()`), so a naked
`pandoc --pdf-engine=typst` (and `studio_export_pdf` in some cases) failed
with `error: font fallback list must not be empty` unless the caller passed
`-V mainfont="..."`. The base now patches that template default to
`Libertinus Serif` (typst's own bundled default font) at build time, so PDF
export works out of the box. Base-affecting (`Dockerfile.base` RUN), rebuilds
`base-<hash>`. README gains a "Generating a PDF with pandoc + typst" section
with the working command and how to override the font via `-V mainfont`.
---
## v1.4.0 — 2026-07-11 ## v1.4.0 — 2026-07-11
Minor release. Headline: **PDF export works out of the box** — the base now Minor release. Headline: **PDF export works out of the box** — the base now
+2 -1
View File
@@ -156,4 +156,5 @@ Optional volumes for MemPalace (commented out by default — uncomment in `docke
## License ## License
MIT (the image; pi and the bundled tools each carry their own licenses). MIT (the image; pi and the bundled tools each carry their own licenses). See
`LICENSE` and `THIRD_PARTY.md` in the [source repo](https://gitea.jordbo.se/joakimp/pi-devbox).
+48 -2
View File
@@ -14,7 +14,7 @@
# content-addressed over this file, so any byte change invalidates the # content-addressed over this file, so any byte change invalidates the
# cache. Recommended cadence: once per release for security updates. # cache. Recommended cadence: once per release for security updates.
# #
# BASE_REBUILD_DATE: 2026-07-11 (Unreleased — typst PDF engine + xz-utils) # BASE_REBUILD_DATE: 2026-07-11 (Unreleased — typst PDF engine + xz-utils + pandoc typst-template default-font patch)
# #
# ── Lineage note ───────────────────────────────────────────────────── # ── Lineage note ─────────────────────────────────────────────────────
# Adapted from opencode-devbox/Dockerfile.base (commit before v1.16.2). # Adapted from opencode-devbox/Dockerfile.base (commit before v1.16.2).
@@ -70,6 +70,19 @@ ENV DEBIAN_FRONTEND=noninteractive
# htop/tmux, so it adds no extra packages. Companion to nvim # htop/tmux, so it adds no extra packages. Companion to nvim
# and the `micro` binary installed further down. EDITOR stays # and the `micro` binary installed further down. EDITOR stays
# nvim; users opt in via `export EDITOR=nano`. # nvim; users opt in via `export EDITOR=nano`.
# kitty-terminfo — terminfo entry for the kitty terminal (TERM=xterm-kitty).
# ~77 KB, terminfo file only (no kitty binary). Without it,
# ncurses apps fall back and Neovim can't reliably detect
# true-colour from kitty over ssh; installing it makes
# TERM=xterm-kitty understood. Pairs with the system-wide
# Neovim termguicolors default (etc/xdg/nvim/sysinit.vim).
# ncurses-term — broad terminfo bundle (wezterm, alacritty, foot, st, the
# base `ghostty` entry, and many more) so SSHing in from a
# modern emulator resolves its TERM instead of degrading to a
# dumb fallback. xterm-kitty is NOT in it (hence kitty-terminfo
# above); TERM=xterm-ghostty is compiled from an alias further
# down (ncurses ships `ghostty`, not `xterm-ghostty`). iTerm2
# defaults to xterm-256color (ncurses-base), so needs nothing.
RUN apt-get update && \ RUN apt-get update && \
apt-get upgrade -y --no-install-recommends && \ apt-get upgrade -y --no-install-recommends && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
@@ -107,6 +120,8 @@ RUN apt-get update && \
imagemagick \ imagemagick \
socat \ socat \
nano \ nano \
kitty-terminfo \
ncurses-term \
&& ln -s /usr/bin/fdfind /usr/local/bin/fd \ && ln -s /usr/bin/fdfind /usr/local/bin/fd \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -429,6 +444,11 @@ ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8
ENV EDITOR=nvim ENV EDITOR=nvim
# Advertise 24-bit colour so colour-aware tools (Neovim's own auto-detect, bat,
# delta, ...) use true colour instead of a 256-colour fallback. Safe for the
# modern terminals this devbox targets; override by exporting `COLORTERM=`
# (empty) from a terminal that lacks true-colour support.
ENV COLORTERM=truecolor
ENV PATH="/home/developer/.local/bin:/home/developer/.cargo/bin:${PATH}" ENV PATH="/home/developer/.local/bin:/home/developer/.cargo/bin:${PATH}"
# ── Node.js (required for pi + MCP servers + tldr) ── # ── Node.js (required for pi + MCP servers + tldr) ──
@@ -465,6 +485,13 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
# typst ships a `.tar.xz` asset (hence xz-utils in the apt layer above) # typst ships a `.tar.xz` asset (hence xz-utils in the apt layer above)
# that extracts to typst-<arch>-unknown-linux-musl/typst. Pin a specific # that extracts to typst-<arch>-unknown-linux-musl/typst. Pin a specific
# tag with --build-arg TYPST_VERSION=vX.Y.Z. # tag with --build-arg TYPST_VERSION=vX.Y.Z.
#
# We also patch pandoc's bundled typst template
# (/usr/share/pandoc/data/templates/template.typst): its conf() defaults the
# document font to an empty tuple (`font: ()`), so a naked
# `pandoc --pdf-engine=typst` fails with "font fallback list must not be empty"
# unless the caller passes `-V mainfont=...`. We default it to Libertinus Serif
# (typst's own bundled default font) so PDF export works out-of-the-box.
ARG TYPST_VERSION=latest ARG TYPST_VERSION=latest
RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64" ;; *) echo "x86_64" ;; esac) && \ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64" ;; *) echo "x86_64" ;; esac) && \
V="${TYPST_VERSION}" && \ V="${TYPST_VERSION}" && \
@@ -476,7 +503,9 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/typst/typst/releases/download/v${V}/typst-${ARCH}-unknown-linux-musl.tar.xz" | tar -xJ -C /tmp && \ curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/typst/typst/releases/download/v${V}/typst-${ARCH}-unknown-linux-musl.tar.xz" | tar -xJ -C /tmp && \
install /tmp/typst-${ARCH}-unknown-linux-musl/typst /usr/local/bin/typst && \ install /tmp/typst-${ARCH}-unknown-linux-musl/typst /usr/local/bin/typst && \
rm -rf /tmp/typst-${ARCH}-unknown-linux-musl && \ rm -rf /tmp/typst-${ARCH}-unknown-linux-musl && \
typst --version typst --version && \
sed -i 's/^ font: (),$/ font: ("Libertinus Serif",),/' /usr/share/pandoc/data/templates/template.typst && \
grep -q 'font: ("Libertinus Serif",),' /usr/share/pandoc/data/templates/template.typst
# ── yq (mikefarah) — YAML processor, jq's companion for YAML ───────── # ── yq (mikefarah) — YAML processor, jq's companion for YAML ─────────
# Installed as the mikefarah Go binary — NOT Debian's `yq` apt package, which # Installed as the mikefarah Go binary — NOT Debian's `yq` apt package, which
@@ -549,6 +578,23 @@ COPY rootfs/home/developer/.bash_aliases /etc/skel-devbox/.bash_aliases
COPY rootfs/home/developer/.inputrc /etc/skel-devbox/.inputrc COPY rootfs/home/developer/.inputrc /etc/skel-devbox/.inputrc
COPY rootfs/home/developer/.gitignore_global /etc/skel-devbox/.gitignore_global COPY rootfs/home/developer/.gitignore_global /etc/skel-devbox/.gitignore_global
# ── Editor defaults: system-wide Neovim true-colour ──────────────────
# /etc/xdg/nvim/sysinit.vim is Neovim's system vimrc: it loads for every user
# (before any personal ~/.config/nvim) and can still be overridden per-user.
# Enables termguicolors so the default theme renders in 24-bit colour instead
# of a muddy 256-colour fallback. Pairs with kitty-terminfo (installed above).
COPY rootfs/etc/xdg/nvim/sysinit.vim /etc/xdg/nvim/sysinit.vim
# ── Terminal support: xterm-ghostty terminfo alias ──────────────────
# ncurses-term (installed above) covers wezterm/alacritty/foot/st and the base
# `ghostty` entry, but Ghostty connects with TERM=xterm-ghostty, for which no
# distro packages an entry. Ship a thin alias (use=ghostty) and compile it into
# the system terminfo db with `tic -x`, so it inherits the maintained ghostty
# capability set. The `infocmp` check fails the build if the entry didn't land.
COPY rootfs/usr/local/share/terminfo-src/ghostty.terminfo /usr/local/share/terminfo-src/ghostty.terminfo
RUN tic -x -o /usr/share/terminfo /usr/local/share/terminfo-src/ghostty.terminfo && \
infocmp -x xterm-ghostty >/dev/null
# ── Entrypoint ──────────────────────────────────────────────────────── # ── Entrypoint ────────────────────────────────────────────────────────
COPY rootfs/usr/local/lib/pi-devbox/ /usr/local/lib/pi-devbox/ COPY rootfs/usr/local/lib/pi-devbox/ /usr/local/lib/pi-devbox/
# Image-baked skills + the global-AGENTS append snippet. Under /usr/local so a # Image-baked skills + the global-AGENTS append snippet. Under /usr/local so a
+6 -1
View File
@@ -158,6 +158,10 @@ RUN if [ -f /opt/pi-toolkit/pi-global-AGENTS.md ] && \
ARG INSTALL_STUDIO=false ARG INSTALL_STUDIO=false
ARG PI_STUDIO_REPO=https://github.com/omaclaren/pi-studio.git ARG PI_STUDIO_REPO=https://github.com/omaclaren/pi-studio.git
ARG PI_STUDIO_REF=main ARG PI_STUDIO_REF=main
# PI_STUDIO_VERSION is the human-readable tag (e.g. v0.9.36) that PI_STUDIO_REF
# was resolved from; recorded as a label below for at-a-glance identification.
# Only meaningful for the studio variant (default `none` otherwise).
ARG PI_STUDIO_VERSION=none
RUN if [ "${INSTALL_STUDIO}" = "true" ]; then \ RUN if [ "${INSTALL_STUDIO}" = "true" ]; then \
set -e; \ set -e; \
rm -rf /opt/pi-studio && mkdir -p /opt/pi-studio && \ rm -rf /opt/pi-studio && mkdir -p /opt/pi-studio && \
@@ -225,7 +229,8 @@ LABEL org.opencontainers.image.version="${RELEASE_TAG}" \
se.jordbo.pi-devbox.pi-fork-ref="${PI_FORK_REF}" \ se.jordbo.pi-devbox.pi-fork-ref="${PI_FORK_REF}" \
se.jordbo.pi-devbox.pi-obsmem-ref="${PI_OBSMEM_REF}" \ se.jordbo.pi-devbox.pi-obsmem-ref="${PI_OBSMEM_REF}" \
se.jordbo.pi-devbox.mempalace-toolkit-ref="${MEMPALACE_TOOLKIT_REF}" \ se.jordbo.pi-devbox.mempalace-toolkit-ref="${MEMPALACE_TOOLKIT_REF}" \
se.jordbo.pi-devbox.pi-studio-ref="${PI_STUDIO_REF}" se.jordbo.pi-devbox.pi-studio-ref="${PI_STUDIO_REF}" \
se.jordbo.pi-devbox.pi-studio-version="${PI_STUDIO_VERSION}"
# The manifest is written from GROUND TRUTH — the actual checked-out HEAD # The manifest is written from GROUND TRUTH — the actual checked-out HEAD
# of each /opt clone and the live `pi --version` — not merely the intended # of each /opt clone and the live `pi --version` — not merely the intended
+60
View File
@@ -0,0 +1,60 @@
# Ideas & backlog
A living list of potential improvements for pi-devbox that are **not yet
scheduled**. This is intentionally lightweight — a place to park ideas so they
aren't lost between sessions. When an item ships, describe it in
[`CHANGELOG.md`](CHANGELOG.md) and remove it from here.
Rough effort tags: 🟢 small · 🟡 medium · 🔴 large. Status: `idea` (unvetted) ·
`planned` (agreed, not started).
---
## Supply-chain hardening
- 🟡 `planned`**Pin CI actions to commit SHAs.** The workflows use floating
major tags (`actions/checkout@v4`, `docker/build-push-action@v7`,
`docker/setup-buildx-action@v4`, `docker/login-action@v3`,
`docker/setup-qemu-action@v3`). This is inconsistent with the project's own
philosophy of SHA-pinning *content* refs (pi, pi-studio, pi-fork, …) to defeat
floating refs. Pin each action to a SHA with a trailing `# vX.Y.Z` comment.
Pairs naturally with the renovate item below to keep the pins fresh.
- 🟡 `planned`**Vulnerability scanning in CI.** No CVE scan runs on the
published images today. Add a `trivy image` (or grype) job to
`docker-publish.yml` after `smoke`. Start non-blocking (report only), then
tighten to fail on `HIGH`/`CRITICAL` with an available fix.
- 🟢🟡 `planned`**Standardize build provenance → buildx SBOM + attestations.**
The image already carries hand-rolled provenance (OCI labels +
`build-manifest`). `docker/build-push-action` can emit a standard SBOM and
SLSA provenance attestation nearly for free (`provenance: mode=max`,
`sbom: true`). Makes provenance machine-consumable and pairs well with the
trivy item (scan the SBOM).
## Dockerfile hardening
- 🟡 `idea`**Address hadolint DL4006 properly.** Currently ignored in
`.hadolint.yaml`. The clean fix is `SHELL ["/bin/bash", "-o", "pipefail",
"-c"]` so piped `RUN`s fail on the first non-zero stage. This changes the
default `RUN` shell from `sh` to `bash` for all subsequent layers, so it is
base-affecting and needs a careful pass over existing `RUN`s before removing
the ignore.
## Developer experience
- 🟢 `idea`**`Makefile`/`justfile` for local iteration.** Reproducing a CI
build locally means hand-assembling many `--build-arg`s. Thin targets
(`make build-base`, `make build-variant`, `make smoke`, `make lint`) would
make local testing painless and document the canonical invocations.
- 🟡 `idea`**Dependency-update automation (renovate).** With CI actions
SHA-pinned (above), a `renovate.json` keeps those pins — plus the pinned tool
versions (`ACTIONLINT_VERSION`, `HADOLINT_VERSION`, gosu, etc.) — current via
automated PRs. Requires a renovate runner against the Gitea instance.
## Housekeeping
- 🟢 `idea`**Registry retention for `base-<hash>` tags.** The base-hash
caching scheme accumulates `base-<hash>` tags over time. Confirm whether the
registry prunes old ones, and add a retention/cleanup step if not.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Joakim Persson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+30 -1
View File
@@ -59,6 +59,12 @@ comfort level — if you'd rather not use a vi-style editor, `nano` and `micro`
are both non-modal. Set your preference with `export EDITOR=micro` (or `nano`) are both non-modal. Set your preference with `export EDITOR=micro` (or `nano`)
in your shell profile, and/or `git config --global core.editor micro`. in your shell profile, and/or `git config --global core.editor micro`.
Neovim ships with a system-wide default (`/etc/xdg/nvim/sysinit.vim`) that turns
on `termguicolors`, so its colours render in 24-bit instead of a muddy
256-colour fallback over ssh/kitty. The `kitty-terminfo` entry is also bundled
so `TERM=xterm-kitty` is understood. Override either in your own
`~/.config/nvim`.
### Document and image tooling ### Document and image tooling
- `pandoc` — universal Markdown↔HTML/Org/RST/etc. converter - `pandoc` — universal Markdown↔HTML/Org/RST/etc. converter
@@ -292,6 +298,26 @@ Assuming the compose file publishes `127.0.0.1:8765:8765` (see method B):
> LaTeX-exact output you can install `texlive-xetex` on demand as a heavier > LaTeX-exact output you can install `texlive-xetex` on demand as a heavier
> fallback. HTML export, KaTeX, Mermaid, and all REPL features work regardless. > fallback. HTML export, KaTeX, Mermaid, and all REPL features work regardless.
### Generating a PDF with pandoc + typst
The base ships `pandoc` (front-end) and `typst` (PDF engine), so Markdown → PDF
works out of the box:
```bash
pandoc doc.md --pdf-engine=typst -o doc.pdf
```
The base patches pandoc's bundled typst template so it defaults to the
**Libertinus Serif** font. Without that patch a naked `--pdf-engine=typst`
fails with `error: font fallback list must not be empty`, because the upstream
template leaves the font unset. To pick a different face, pass one of the fonts
typst can see (`typst fonts` lists them — DejaVu Serif/Sans/Mono, Libertinus
Serif, New Computer Modern):
```bash
pandoc doc.md --pdf-engine=typst -V mainfont="New Computer Modern" -o doc.pdf
```
### Graphviz diagrams in Studio: `dot-watch` ### Graphviz diagrams in Studio: `dot-watch`
pi-studio renders **Mermaid** natively but has **no Graphviz/DOT renderer**. pi-studio renders **Mermaid** natively but has **no Graphviz/DOT renderer**.
@@ -757,4 +783,7 @@ The pi coding-agent itself is [@earendil-works/pi-coding-agent](https://www.npmj
## License ## License
MIT MIT — see [`LICENSE`](LICENSE). This covers the repository's own contents
(Dockerfiles, entrypoint scripts, `rootfs/` seeds, CI, docs). The published
images bundle third-party software under their own licenses; see
[`THIRD_PARTY.md`](THIRD_PARTY.md).
+46
View File
@@ -0,0 +1,46 @@
# Third-party notices
pi-devbox is distributed under the MIT License (see [`LICENSE`](LICENSE)), which
covers **this repository's own contents** — the Dockerfiles, entrypoint scripts,
`rootfs/` seeds, CI workflows, and docs.
The **published container images** (`joakimp/pi-devbox:*`) additionally *bundle*
third-party software, each of which remains under its own license. This file is
a good-faith summary; the authoritative sources are the upstream projects and,
for OS packages, the per-package copyright files inside the image at
`/usr/share/doc/<package>/copyright`.
## pi and its extensions (installed in the variant layer)
| Component | Upstream | License |
| --- | --- | --- |
| pi (`@earendil-works/pi-coding-agent`) | npm | MIT |
| pi-fork | github.com/elpapi42/pi-fork | MIT |
| pi-observational-memory | github.com/elpapi42/pi-observational-memory | MIT |
| pi-studio *(`-studio` variant only)* | github.com/omaclaren/pi-studio | MIT |
| pi-toolkit, pi-extensions, mempalace-toolkit | authored by the maintainer (Joakim Persson) | MIT |
## Tooling baked into the base image
| Component | Upstream | License (best effort) |
| --- | --- | --- |
| gosu | github.com/tianon/gosu | Apache-2.0 |
| Node.js | nodejs.org | MIT (bundles components under their own licenses) |
| uv | github.com/astral-sh/uv | Apache-2.0 OR MIT |
| Neovim | neovim.io | Apache-2.0 + Vim license |
| Pandoc | pandoc.org | GPL-2.0-or-later |
| Typst | github.com/typst/typst | Apache-2.0 |
| ripgrep / fd / micro / tealdeer / yq (mikefarah) | respective repos | MIT / Apache-2.0 / Unlicense (varies) |
## Base OS
The image is built `FROM` a Debian base and installs packages via `apt`. Debian
and its packages are distributed under their respective licenses (GPL, LGPL,
MIT, BSD, and others). See each package's copyright file in the image under
`/usr/share/doc/<package>/copyright`.
---
*Licenses marked "best effort" are widely known but were not each verified at
the exact bundled version; consult the upstream project for authoritative
terms. Corrections welcome.*
+18
View File
@@ -0,0 +1,18 @@
" pi-devbox — system-wide Neovim defaults.
"
" This is Neovim's *system vimrc*: it loads for every user before any personal
" ~/.config/nvim, and personal configs can still override it.
"
" Enable 24-bit ("true") colour. Without it, Neovim's default theme is squeezed
" into a 256-colour palette where strings/comments become a muddy, low-contrast
" dark colour — a common complaint over ssh/kitty where COLORTERM often isn't
" propagated into the container. Modern terminals (kitty, WezTerm, iTerm2,
" Alacritty, ...) all support true colour; the bundled kitty-terminfo also lets
" Neovim auto-detect it, but forcing it here guarantees readable colour
" regardless of how the terminal type / COLORTERM reach the container.
"
" Opt out for a session: :set notermguicolors
" Override permanently: set your own value in ~/.config/nvim/init.lua
if has('termguicolors')
set termguicolors
endif
+3
View File
@@ -9,3 +9,6 @@
*.orig *.orig
*.swp *.swp
*.tmp *.tmp
# AI/LLM tool local settings — machine-specific perms + credentials, never commit
**/.claude/settings.local.json
@@ -0,0 +1,14 @@
# xterm-ghostty — alias of the maintained ncurses `ghostty` terminfo entry.
#
# Ghostty sets TERM=xterm-ghostty by default, but the ncurses terminfo
# database (Debian: ncurses-term) ships the entry under the name `ghostty`
# only — there is no `xterm-ghostty` alias, and no distro packages one. This
# thin alias makes xterm-ghostty resolve to the same upstream-maintained
# capability set, so SSH sessions from a Ghostty terminal work without
# vendoring Ghostty's full (Zig-generated) terminfo here.
#
# `use=ghostty` is resolved by `tic` at compile time against the base
# `ghostty` entry from ncurses-term (installed in Dockerfile.base before the
# compile step). Compiled with `tic -x`.
xterm-ghostty|Ghostty terminal emulator (xterm-ghostty alias),
use=ghostty,
+6
View File
@@ -8,6 +8,8 @@
# - new v1.0.0 base additions (pandoc, graphviz, imagemagick, yq, tealdeer) # - new v1.0.0 base additions (pandoc, graphviz, imagemagick, yq, tealdeer)
# - typst PDF engine for pandoc (Unreleased) — `pandoc --pdf-engine=typst` # - typst PDF engine for pandoc (Unreleased) — `pandoc --pdf-engine=typst`
# - non-modal editors nano + micro (alongside nvim) # - non-modal editors nano + micro (alongside nvim)
# - terminfo for modern emulators: xterm-kitty, xterm-ghostty, wezterm,
# alacritty, foot (kitty-terminfo + ncurses-term + compiled ghostty alias)
# - tmux 0-indexing baked in /etc/tmux.conf (required for pi-studio variants) # - tmux 0-indexing baked in /etc/tmux.conf (required for pi-studio variants)
# - pi-toolkit cloned at /opt/pi-toolkit # - pi-toolkit cloned at /opt/pi-toolkit
# - pi-extensions cloned at /opt/pi-extensions # - pi-extensions cloned at /opt/pi-extensions
@@ -76,6 +78,10 @@ run "uv" "uv --version"
run "nvim" "nvim --version" run "nvim" "nvim --version"
run "nano" "nano --version" run "nano" "nano --version"
run "micro" "micro --version" run "micro" "micro --version"
run "kitty-terminfo" "infocmp -x xterm-kitty >/dev/null 2>&1"
run "terminfo: modern emulators (ncurses-term)" 'for t in wezterm alacritty foot ghostty st-256color; do infocmp -x "$t" >/dev/null 2>&1 || exit 1; done'
run "terminfo: xterm-ghostty alias (tic)" "infocmp -x xterm-ghostty >/dev/null 2>&1"
run "nvim true-colour default (sysinit.vim)" "nvim --headless -c 'lua os.exit(vim.o.termguicolors and 0 or 1)'"
run "mempalace-mcp" "mempalace-mcp --help" run "mempalace-mcp" "mempalace-mcp --help"
# v1.0.0 base additions — verify presence and basic functionality. # v1.0.0 base additions — verify presence and basic functionality.
run "pandoc" "pandoc --version" run "pandoc" "pandoc --version"