Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "2.17.0",
"resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c",
"integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c"
},
"ghcr.io/devcontainers/features/powershell:2": {
"version": "2.0.2",
"resolved": "ghcr.io/devcontainers/features/powershell@sha256:82ea3880c5706ac3963f1b5e940a7f5871835393a5472f80008148b995d58d61",
"integrity": "sha256:82ea3880c5706ac3963f1b5e940a7f5871835393a5472f80008148b995d58d61"
}
}
}
25 changes: 25 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm",
"customizations": {
"vscode": {
"settings": {
"json.schemas": [
{
"fileMatch": [
"*/devcontainer-feature.json"
],
"url": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainerFeature.schema.json"
}
]
},
"extensions": []
}
},
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/powershell:2": {}
},
"updateContentCommand": {
"Update Dev Containers CLI": "npm install -g @devcontainers/cli@0.87.0"
}
}
40 changes: 40 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Release dev container features and generate documentation

on:
workflow_dispatch:

jobs:
deploy:
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
packages: write
steps:
- uses: actions/checkout@v4

- name: Publish features
uses: devcontainers/action@v1
with:
publish-features: "true"
base-path-to-features: "./src"
generate-docs: "true"
devcontainer-cli-version: "0.87.0"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Create pull request for generated documentation
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
git config --global user.email github-actions[bot]@users.noreply.github.com
git config --global user.name github-actions[bot]
git config pull.rebase false
branch="automated-documentation-update-${GITHUB_RUN_ID}"
git checkout -b "$branch"
git add src/**/README.md
git commit -m 'Automated documentation update [skip ci]' || exit 0
git push origin "$branch"
gh pr create --title 'Automated documentation update' --body 'Automated documentation update'
26 changes: 26 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Test

on:
pull_request:
push:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install Dev Containers CLI
run: npm install -g @devcontainers/cli@0.87.0

- name: Run all feature tests with Node-capable base image
run: devcontainer features test --base-image mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm .
94 changes: 93 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,95 @@
# Dev Container Features

Custom dev container Features published from this repository.
This repository contains the `pi-coding-agent` Dev Container Feature for installing and configuring Pi inside a development container.

## Features

- `pi-coding-agent`: Installs `@earendil-works/pi-coding-agent` with npm, installs bundled Pi extensions during feature setup, verifies the installation with `pi --version`, and can synchronize selected or all supported Pi configuration resources, including local Pi extensions, into the container at startup.

## Usage

Pi is installed with npm, so the target dev container must already provide `node` and `npm`, either through its base image or through another feature that you add yourself. `pi-coding-agent` does not install Node.js on your behalf and fails fast if `node` or `npm` is absent.

```jsonc
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm",
"features": {
"ghcr.io/rodribus/devcontainer-features/pi-coding-agent:0": {
"version": "latest"
}
}
}
```

To reuse Pi configuration from the host, mount a staging directory into the container and either opt into specific mappings or enable `mapAllConfigurations`. Mounting the full host `~/.pi/agent` directory is the simplest option because it already matches the expected file layout, including `extensions/` when you want to bring local Pi extensions into the container.

```jsonc
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm",
"features": {
"ghcr.io/rodribus/devcontainer-features/pi-coding-agent:0": {
"configImportRoot": "/mnt/pi-coding-agent-import",
"mapAllConfigurations": true,
"sessionPersistenceMode": "volume"
}
},
"mounts": [
{
"source": "${localEnv:HOME}/.pi/agent",
"target": "/mnt/pi-coding-agent-import",
"type": "bind"
}
]
}
```

When `mapAllConfigurations` is enabled, any individual `map*` options are ignored and the feature logs a warning. When `sessionPersistenceMode` is `volume`, the feature automatically provisions a per-devcontainer named volume for Pi sessions. When `sessionPersistenceMode` is `bind`, the feature points Pi at the bind-mounted storage path you provide instead of copying session files. Do not use the same live session storage from host Pi and container Pi at the same time.

## Repository Layout

```text
.
├── .devcontainer
├── .github/workflows
├── src/pi-coding-agent
└── test
└── pi-coding-agent
```

## Running Tests

All repository tests are run directly through the Dev Containers CLI. The authoring dev container already installs the CLI for you.

Run the full suite, including the autogenerated `test.sh` smoke test:

```bash
devcontainer features test --base-image mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm .
```

`devcontainer features test --base-image mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm .` runs the full repository test suite, including the autogenerated smoke test for `pi-coding-agent` and the hand-authored scenario coverage.

The explicit `--base-image` is needed because the autogenerated `test.sh` path installs `pi-coding-agent` in isolation. That smoke test needs a base image that already includes `node` and `npm`, but `pi-coding-agent` itself does not hard-depend on any Node feature.

Run the hand-authored scenario coverage plus the duplicate/idempotency check:

```bash
devcontainer features test --skip-autogenerated --base-image mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm .
```

`devcontainer features test --skip-autogenerated --base-image mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm .` skips every feature's autogenerated `test.sh` smoke test and still runs the scenario coverage plus duplicate-install coverage from each feature.

If you only want the scenario coverage and do not care about the duplicate-install check, you can also run:

```bash
devcontainer features test --skip-autogenerated --skip-duplicated .
```

That variant works with the repository's current scenarios because each scenario that installs `pi-coding-agent` also provisions `node` and `npm` explicitly.

The GitHub workflow currently uses the `--base-image` variant so it exercises both the autogenerated smoke test and the scenario suite.

If you are not using the authoring dev container, install the Dev Containers CLI first and then run the commands above directly.

## Publishing

The release workflow publishes features from `src/` to GHCR with `devcontainers/action` and generates feature README files. Enable GitHub Actions pull request creation in the repository settings before using the release workflow.
98 changes: 98 additions & 0 deletions src/pi-coding-agent/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
## Overview

This feature installs Pi with npm, runs `pi update --extensions` during feature setup for packages already visible during image build, verifies the installation with `pi --version`, and can import selected Pi configuration resources from a staging directory every time the container starts.

The feature does not install Node.js for the user and does not depend on the official Node feature. `node` and `npm` must already be present in the target image when installation starts, otherwise the install fails immediately.

## Import Root Layout

When configuration import is enabled, the feature expects a single staging root mounted inside the container. The following files and directories are recognized:

```text
/mnt/pi-coding-agent-import
├── auth.json
├── extensions/
├── keybindings.json
├── models.json
├── settings.json
├── prompts/
├── skills/
└── themes/
```

Mounting the host `~/.pi/agent` directory directly to the import root works well because Pi already stores the same resources there. If `mapAllConfigurations` is enabled, all supported resources from this root are imported, including `extensions/`, and any individual `map*` flags are ignored with a warning.

## Example: Reuse Host Pi Configuration

```jsonc
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm",
"features": {
"ghcr.io/rodribus/devcontainer-features/pi-coding-agent:0": {
"configImportRoot": "/mnt/pi-coding-agent-import",
"mapAllConfigurations": true
}
},
"mounts": [
{
"source": "${localEnv:HOME}/.pi/agent",
"target": "/mnt/pi-coding-agent-import",
"type": "bind"
}
]
}
```

Imports use copy-if-newer behavior. Missing staged sources only produce warnings and do not block container startup.

If `settings.json` is imported, the startup hook also runs `pi update --extensions` once per imported settings change. This preinstalls Pi packages referenced by the imported settings so the first interactive `pi` run does not need to install them again.

## Example: Persistent Session Storage

The feature treats session persistence as live storage, not as a copied import. In `volume` mode, the feature mounts a per-devcontainer named volume automatically and updates `~/.pi/agent/settings.json` so Pi uses that managed path as `sessionDir`. In `bind` mode, the user still defines the mount in `devcontainer.json`.

Bind mount example:

```jsonc
{
"features": {
"ghcr.io/rodribus/devcontainer-features/pi-coding-agent:0": {
"sessionPersistenceMode": "bind",
"sessionStoragePath": "/mnt/pi-coding-agent-sessions"
}
},
"mounts": [
{
"source": "${localEnv:HOME}/.pi/agent/sessions",
"target": "/mnt/pi-coding-agent-sessions",
"type": "bind"
}
]
}
```

Named volume example:

```jsonc
{
"features": {
"ghcr.io/rodribus/devcontainer-features/pi-coding-agent:0": {
"sessionPersistenceMode": "volume"
}
}
}
```

Volume mode stores sessions in a feature-managed named volume mounted at `/mnt/pi-coding-agent-managed-sessions`. Do not add your own mount for that path.

If imported `settings.json` already defines `sessionDir`, the feature logs a warning and overrides that value while session persistence is enabled. If `sessionStoragePath` is set while `sessionPersistenceMode=volume`, the feature ignores it and uses the managed volume path instead.

Using the same live session storage from host Pi and container Pi at the same time can corrupt sessions.

## Tested Base Image

The repository's scenario-style tests run the feature against `mcr.microsoft.com/devcontainers/base:ubuntu`.

The repository test suite also includes a duplicate-install assertion via `test/pi-coding-agent/duplicate.sh`, so `devcontainer features test` exercises the feature twice with distinct option sets to catch idempotency regressions.

The duplicate/idempotency test needs a Node-capable test base image because `pi-coding-agent` intentionally fails when `node` or `npm` is absent. CI and local full-suite runs should use `mcr.microsoft.com/devcontainers/javascript-node:4.0.10-24-bookworm` or another base image that already includes `node` and `npm`.
42 changes: 42 additions & 0 deletions src/pi-coding-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Pi Coding Agent (pi-coding-agent)

Installs Pi with npm and optionally synchronizes selected Pi configuration into the dev container at startup.

When `settings.json` is imported, the startup hook also runs `pi update --extensions` once per imported settings change so Pi packages referenced there are installed before the first interactive run.

This feature currently supports Debian and Ubuntu based images. Node.js, npm, and bash must already be present in the dev container image or provided by other Features.

## Example Usage

```json
"features": {
"ghcr.io/devcontainers/features/node:2": {
"version": "lts"
},
"ghcr.io/rodribus/devcontainer-features/pi-coding-agent:0": {
"version": "latest"
}
}
```

## Options

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | Version of @earendil-works/pi-coding-agent to install. | string | latest |
| configImportRoot | In-container staging root from which selected Pi configuration files are copied at startup. | string | /mnt/pi-coding-agent-import |
| mapAllConfigurations | Import all supported Pi configuration files and directories from configImportRoot. When enabled, individual map* options are ignored and a warning is logged if any are also set. | boolean | false |
| mapAuth | Copy auth.json from the import root into ~/.pi/agent/auth.json if the source exists and is newer. | boolean | false |
| mapSettings | Copy settings.json from the import root into ~/.pi/agent/settings.json if the source exists and is newer. | boolean | false |
| mapKeybindings | Copy keybindings.json from the import root into ~/.pi/agent/keybindings.json if the source exists and is newer. | boolean | false |
| mapSkills | Copy the skills directory from the import root into ~/.pi/agent/skills using copy-if-newer behavior. | boolean | false |
| mapPrompts | Copy the prompts directory from the import root into ~/.pi/agent/prompts using copy-if-newer behavior. | boolean | false |
| mapThemes | Copy the themes directory from the import root into ~/.pi/agent/themes using copy-if-newer behavior. | boolean | false |
| mapExtensions | Copy the extensions directory from the import root into ~/.pi/agent/extensions using copy-if-newer behavior. | boolean | false |
| mapModels | Copy models.json from the import root into ~/.pi/agent/models.json if the source exists and is newer. | boolean | false |
| sessionPersistenceMode | Use a user-managed bind mount or a feature-managed named volume as Pi session storage. Volume mode mounts a per-devcontainer named volume automatically. | string | none |
| sessionStoragePath | Mounted in-container path to use as live Pi session storage when sessionPersistenceMode is bind. Ignored when sessionPersistenceMode is volume. | string | /mnt/pi-coding-agent-sessions |

Comment on lines +24 to +39
## Notes

See [NOTES.md](https://github.com/RodriBus/devcontainer-features/blob/main/src/pi-coding-agent/NOTES.md) for staging-root layout, session persistence examples, and test coverage details.
Loading
Loading