From bf80431b2e490255d94b9cdbbb1ccd5f4109e3f1 Mon Sep 17 00:00:00 2001 From: Chris Phillipson Date: Sat, 8 Aug 2026 10:41:56 -0700 Subject: [PATCH 1/2] feat: dev container setup for Codespaces (maintainer + consumer configs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two dev container configurations so users can experiment with agentic-kit on Codespaces or any dev-container-compatible tool: - .devcontainer/devcontainer.json — contributor environment; pnpm install + npm link so `ak`/`agentic-kit` resolve to the live checkout. - .devcontainer/consumer/devcontainer.json — reuses docker/Dockerfile to install the published @pacphi/agentic-kit release into a disposable container, seeding a ~/sandbox project outside the mounted repo. CI (.github/workflows/devcontainers.yml) builds and smoke-tests both configs with the devcontainers/ci action on relevant PR changes (a paths-filter job scopes each config to only the files it depends on) and monthly, to catch base-image or published-package drift independent of any change in this repo. docs/DEVCONTAINERS.md documents both configs, the CI job, and how the dashboard's port forwarding differs from the docker/ compose bridge; linked from README and cross-linked from docker/USER-GUIDE.md. --- .devcontainer/consumer/devcontainer.json | 24 ++++ .devcontainer/consumer/postCreate.sh | 35 ++++++ .devcontainer/devcontainer.json | 25 ++++ .github/workflows/devcontainers.yml | 110 +++++++++++++++++ README.md | 8 ++ docker/USER-GUIDE.md | 6 + docs/DEVCONTAINERS.md | 145 +++++++++++++++++++++++ 7 files changed, 353 insertions(+) create mode 100644 .devcontainer/consumer/devcontainer.json create mode 100755 .devcontainer/consumer/postCreate.sh create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/devcontainers.yml create mode 100644 docs/DEVCONTAINERS.md diff --git a/.devcontainer/consumer/devcontainer.json b/.devcontainer/consumer/devcontainer.json new file mode 100644 index 0000000..763d181 --- /dev/null +++ b/.devcontainer/consumer/devcontainer.json @@ -0,0 +1,24 @@ +{ + // Reuses docker/Dockerfile — the same image the "first-use" docker-compose + // environment (see docker/USER-GUIDE.md) builds — so both paths install the + // published package the same way, on the same base. This config does NOT + // build agentic-kit from this checkout; use the top-level + // .devcontainer/devcontainer.json for that. + "name": "agentic-kit (try the published release)", + "build": { + "dockerfile": "../../docker/Dockerfile", + "context": "../../docker" + }, + "remoteUser": "tester", + "remoteEnv": { + "AK_DIST_TAG": "next" + }, + "postCreateCommand": "bash ${containerWorkspaceFolder}/.devcontainer/consumer/postCreate.sh", + "forwardPorts": [7431], + "portsAttributes": { + "7431": { + "label": "ak dashboard", + "onAutoForward": "notify" + } + } +} diff --git a/.devcontainer/consumer/postCreate.sh b/.devcontainer/consumer/postCreate.sh new file mode 100755 index 0000000..cb2ee60 --- /dev/null +++ b/.devcontainer/consumer/postCreate.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Installs the PUBLISHED @pacphi/agentic-kit — not this checkout — and preps a +# scratch git repo outside the mounted workspace so `ak setup` (project scope) +# never touches the agentic-kit repo you're browsing in the editor. +set -euo pipefail + +AK_DIST_TAG="${AK_DIST_TAG:-next}" +echo "installing @pacphi/agentic-kit@${AK_DIST_TAG} (published release)" +npm install -g "@pacphi/agentic-kit@${AK_DIST_TAG}" +ak --version + +SANDBOX="$HOME/sandbox" +mkdir -p "$SANDBOX" +cd "$SANDBOX" +if [ ! -d .git ]; then + git init -q + git config user.email "tester@codespace.invalid" + git config user.name "Codespace Tester" + echo "# sandbox — try agentic-kit here" > README.md + git add README.md + git commit -qm "init sandbox" +fi + +cat <&2; exit 1 ;; + esac + pnpm run test:surface + + consumer: + name: consumer devcontainer (published release) + needs: changes + if: needs.changes.outputs.consumer == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + # `up` runs postCreate.sh (npm install -g the published package, seeds + # ~/sandbox); runCmd checks the CLI is reachable and the sandbox exists. + - name: Build + smoke test + uses: devcontainers/ci@v0.3 + with: + configFile: .devcontainer/consumer/devcontainer.json + push: never + runCmd: | + set -euo pipefail + which ak + ak --version + ak --help --all > /dev/null + test -d "$HOME/sandbox/.git" + echo "sandbox present at $HOME/sandbox" diff --git a/README.md b/README.md index bea2882..a68a4c7 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,11 @@ Git revisions, and contributor links have different package footprints while [Installation and scope](docs/INSTALLATION.md) before choosing a non-global method or deploying on a shared machine. +Want to try it before installing anything locally? Open this repo in GitHub +Codespaces (or any [dev container](https://containers.dev)-compatible tool) — +a "try the published release" configuration installs `ak` into a disposable +container for you. See [docs/DEVCONTAINERS.md](docs/DEVCONTAINERS.md). + **What you get:** - **One command** installs + heals + *proves* ruflo & agentic-qe — native SQLite, memory, security, statusline (past npm's `allow-scripts` gate). @@ -259,6 +264,9 @@ tarball, Git, and source-checkout installs, including user/machine/project impac [docs/HOST-SUPPORT.md](docs/HOST-SUPPORT.md) — Claude, Codex, and OpenCode support across Ruflo, AQE, and RuvNet Brain, with limitations and current upstream risks. +[docs/DEVCONTAINERS.md](docs/DEVCONTAINERS.md) — Codespaces/dev container setup, +both for contributing to this repo and for trying the published release. + [docs/DASHBOARD.md](docs/DASHBOARD.md) — dashboard navigation, deep links, keyboard behavior, and the meaning of each primary and secondary view. diff --git a/docker/USER-GUIDE.md b/docker/USER-GUIDE.md index eca8b31..4031084 100644 --- a/docker/USER-GUIDE.md +++ b/docker/USER-GUIDE.md @@ -12,6 +12,12 @@ containers, and the containers cannot alter your host install. Persistent state lives only in Docker-managed named volumes; the single bind mount is `./artifacts` inside this directory. +Prefer an editor-attached environment (Codespaces, VS Code Dev Containers) over +a bare `docker compose` shell? The consumer dev container in +[`.devcontainer/consumer/`](../.devcontainer/consumer/devcontainer.json) builds +this same Dockerfile and installs the same published package — see +[docs/DEVCONTAINERS.md](../docs/DEVCONTAINERS.md) for the tradeoffs. + ## Prerequisites - Docker Desktop (macOS/Windows) or Docker Engine + Compose v2 (Linux). diff --git a/docs/DEVCONTAINERS.md b/docs/DEVCONTAINERS.md new file mode 100644 index 0000000..a90656e --- /dev/null +++ b/docs/DEVCONTAINERS.md @@ -0,0 +1,145 @@ +# Dev containers (Codespaces and compatible tools) + +Two dev container configurations ship in `.devcontainer/`, for two different +audiences. Both work in GitHub Codespaces and in any tool that reads the +[dev container spec](https://containers.dev) (VS Code's Dev Containers +extension, JetBrains Gateway, `devcontainer` CLI). + +| Config | Audience | What it gives you | +| --- | --- | --- | +| `.devcontainer/devcontainer.json` | Contributors to this repo | Node + pnpm, `pnpm install`, `ak`/`agentic-kit` linked to **this checkout** via `npm link` — edit source, run `ak` immediately, no reinstall | +| `.devcontainer/consumer/devcontainer.json` | Anyone who wants to try `ak` without cloning | Installs the **published** `@pacphi/agentic-kit` npm package into a disposable container, same image [docker/](../docker/) uses | + +The top-level config is the default: "Create codespace on main" from the +GitHub UI uses it. To get the consumer config instead, use the "..." menu → +"New with options" → pick "agentic-kit (try the published release)" (or, with +the `devcontainer` CLI, pass `--config .devcontainer/consumer/devcontainer.json`). + +## CI + +[.github/workflows/devcontainers.yml](../.github/workflows/devcontainers.yml) +builds and smoke-tests both configs with the +[`devcontainers/ci`](https://github.com/devcontainers/ci) action — the same +`devcontainer` CLI Codespaces uses, not a hand-rolled approximation. A +`changes` job (via `dorny/paths-filter`) narrows each config's job to the +files it actually depends on, so, for example, a `docker/Dockerfile` edit +only rebuilds the consumer config, not the maintainer one: + +| Job | Runs when | Proves | +| --- | --- | --- | +| `maintainer` | `.devcontainer/devcontainer.json`, `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `bin/**`, or `src/**` changes | `npm link` resolves `ak` to the checked-out source (version string match), then `pnpm run test:surface` | +| `consumer` | `.devcontainer/consumer/**` or `docker/Dockerfile` changes | The published package installs and `~/sandbox` is seeded | + +Both jobs also run monthly (`23 6 1 * *`, no path filter — that's the point: +catch a base-image tag disappearing or a published-package install breaking +even when nothing here changed) and on demand via `workflow_dispatch`. + +## Maintainer container + +```bash +# GitHub UI: Code -> Codespaces -> Create codespace on main +# VS Code: Dev Containers: Reopen in Container +``` + +`postCreateCommand` runs `corepack enable && pnpm install && npm link`. +`packageManager` in `package.json` pins the pnpm version, so corepack +resolves it without a separate install step. `npm link` registers the +`ak`/`agentic-kit` bins globally, pointing at `bin/agentic-kit.mjs` in the +container's copy of your checkout — so `ak status` (or any `ak` command) +exercises your live edits, not a published version. + +Base image: `mcr.microsoft.com/devcontainers/typescript-node:22-bookworm` +(Node 22, the `engines` floor in `package.json`; CI additionally matrixes +Node 24 and 26 — see [.github/workflows/ci.yml](../.github/workflows/ci.yml)). +Features: GitHub CLI (`gh`) and Docker-outside-of-Docker, so you can drive +`gh pr`/`gh issue` and run the [docker/](../docker/) first-use environment +from inside the codespace. + +Standard checks, unchanged from local development: + +```bash +pnpm test # unit suite + statusline/dashboard/admin tests +pnpm run check # typecheck + lint + markdown lint + build + test +``` + +The `claude` CLI (required for `ak setup`) is deliberately **not** +auto-installed — it needs an interactive device-flow login tied to your own +Anthropic account, which doesn't belong in `postCreateCommand`. Install it +yourself when you want to exercise setup end to end: + +```bash +npm install -g @anthropic-ai/claude-code +claude # complete the login flow, then: +ak setup +``` + +## Consumer container ("try the published release") + +Reuses [docker/Dockerfile](../docker/Dockerfile) — the same image the +[first-use docker-compose environment](../docker/USER-GUIDE.md) builds — so a +Codespace and a local `docker compose up` install the kit the same way, on +the same base. It does **not** build agentic-kit from source; the repo +checkout is present in the editor for reference only. + +`postCreateCommand` runs +[`.devcontainer/consumer/postCreate.sh`](../.devcontainer/consumer/postCreate.sh): +it installs `@pacphi/agentic-kit@next` globally, then creates +`~/sandbox` — a throwaway git repo outside the mounted workspace — so +`ak setup`'s project-scope work (`ruflo init --full --force`, statusline, +project config) never touches the agentic-kit repo you're browsing. +When it finishes, the terminal shows the next commands: + +```bash +cd ~/sandbox +ak setup --yes # drop --yes to see the interactive prompts +ak status +ak dashboard --no-open --port 7431 # open the printed #token URL via the Ports tab +``` + +Pin a specific release instead of `next` by editing `AK_DIST_TAG` in +`.devcontainer/consumer/devcontainer.json`'s `remoteEnv` before creating the +container, or after creation: + +```bash +npm install -g @pacphi/agentic-kit@4.0.0-alpha.41 && ak sync +``` + +### The dashboard port + +`ak dashboard` binds `127.0.0.1` only, by design +([ADR-0014](adr/0014-dashboard-auth-and-remediation.md)). Unlike a plain +`docker compose up` on your host — which needs the `socat` bridge documented +in [docker/MAINTAINER-GUIDE.md](../docker/MAINTAINER-GUIDE.md) to reach a +container-loopback listener — a dev container's port forwarding runs from +*inside* the container (VS Code Server attaches there directly), so +forwarding container port 7431 works without a bridge. Open the "Ports" tab +and use the forwarded URL, appending the `#token=...` fragment printed by +`ak dashboard`. + +## Choosing between a dev container and `docker compose` + +Both routes end up running the published package in a disposable container; +they exist for different workflows: + +- **This directory's consumer config** — editor-attached (files, terminal, + extensions, port forwarding through the VS Code UI), one container per + Codespace/window, `ak setup` run by hand so you see the real prompts. +- **[docker/](../docker/)** — a scripted, fully non-interactive + (`--yes`) environment meant for release smoke-testing, bisecting a + regression across dist-tags, and upgrade-path testing; no editor + attachment. See [docker/USER-GUIDE.md](../docker/USER-GUIDE.md) and + [docker/MAINTAINER-GUIDE.md](../docker/MAINTAINER-GUIDE.md). + +## Limitations + +- `ak setup --codex --opencode` (the flag set a maintainer might reach for) + offers the ~2 GB RuvNet Brain download; on a metered or slow Codespaces + network, pass `--no-ruvnet-brain` or accept the default prompt to skip it. +- Codespaces machine types with less than the default disk/CPU may time out + during `pnpm install` (maintainer container) or during `ak setup` + (consumer container, if you opt into RuvNet Brain); pick a larger machine + type from the Codespaces creation options if you hit this. +- Docker-outside-of-Docker in the maintainer container shares the host + Codespace's Docker daemon; containers you start from `docker/` there are + visible to (and stoppable from) the Codespace host, same as any + Docker-outside-of-Docker setup. From 218b57738cf18d9f88ad4369847c633d93e494ba Mon Sep 17 00:00:00 2001 From: Chris Phillipson Date: Sat, 8 Aug 2026 10:44:56 -0700 Subject: [PATCH 2/2] fix: sudo corepack enable / npm link in maintainer devcontainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI (devcontainers.yml) caught this: postCreateCommand runs as the non-root `node` user, but corepack's shim install and npm link both write into the base image's root-owned /usr/local/bin, so both failed with EACCES. The devcontainers base image grants node passwordless sudo for exactly this. pnpm install stays unprivileged — it only touches the (UID-synced) mounted repo. --- .devcontainer/devcontainer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d5bae75..16b01c1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,11 @@ // pnpm is pinned via package.json's "packageManager" field; corepack reads // that automatically. `npm link` exposes `ak`/`agentic-kit` globally, // pointing at THIS checkout — edits are live, no reinstall needed. - "postCreateCommand": "corepack enable && pnpm install && npm link && ak --version", + // corepack/npm link write into the image's root-owned global bin dir + // (/usr/local/bin), so those two steps need sudo; `node` has passwordless + // sudo in this base image. `pnpm install` targets the (UID-synced, + // node-writable) mounted repo and stays unprivileged. + "postCreateCommand": "sudo corepack enable && pnpm install && sudo npm link && ak --version", "forwardPorts": [7431], "portsAttributes": { "7431": {