u2up/pi-env
GitHub: u2up/pi-env
pi-env 为 Pi AI 编码智能体提供 Bubblewrap 沙箱隔离、可选 Nix 可复现运行时以及基于 Git 的多智能体协调机制,解决智能体开发中的安全隔离、环境一致性和工作流治理问题。
Stars: 4 | Forks: 0
# pi-env
`pi-env` runs Pi Coding Agent safely against one selected project, with optional
guaranteed-reproducible tooling and optional managed agentic coordination.
It is built around three layers:
1. **Sandboxed project isolation with Bubblewrap**
Every run is confined to a mandatory Bubblewrap sandbox where the selected
project is mounted read-write at `/workspace`. The agent does not receive
wholesale access to your home directory, credentials, shell configuration,
Docker socket, or unrelated projects.
**Problem addressed:** reducing the blast radius of agentic coding tools
that can inspect files, run commands, and edit code.
2. **Optional guaranteed-reproducible runtime with Nix**
When selected, the Nix runtime provides pinned tools and dependencies so
teams can run the agent with the same command-line environment across
machines. Direct host-runtime mode remains available for convenience, but is
intentionally unpinned.
**Problem addressed:** avoiding “works on my machine” drift in agent runs,
checks, and development tooling.
3. **Optional coordination repository for managed agentic development**
`pi-env` includes Git-backed coordination helpers and role workflows for
teams that want structured multi-agent or multi-role development. This acts
as a reference implementation of the
[coordination repository pattern](https://github.com/u2up/coordination-repository-pattern):
requirements, decisions, ownership, handoffs, and validation can be tracked
outside the working project.
**Problem addressed:** preventing ad-hoc agent work from becoming hard to
audit, reproduce, assign, or hand off.
In short:
Without pi-env:
Pi -> host environment
-> home directory, SSH keys, cloud credentials, Docker socket,
shell config, unrelated projects
With pi-env:
Pi -> Bubblewrap sandbox
-> selected repo at /workspace, isolated HOME,
selected auth/config only
Most users start with one of two workflows:
1. **Direct use**: run this checkout's `pi-env` launcher from any project.
2. **Flake integration**: add `pi-env` as an input to a project's own flake so
the team shares the same pinned runtime.
## 60-second example
Assuming Linux, Git, and a configured host `pi` command are already available,
try pi-env on an existing public repository:
git clone https://github.com/spog/evm.git
cd evm
git clone https://github.com/u2up/pi-env.git ~/src/pi-env
~/src/pi-env/pi-env \
"Summarize this repository and suggest safe first checks."
That direct checkout command starts in the default **host runtime** mode: Pi
runs inside the Bubblewrap sandbox, but runtime tools are unpinned host tools.
You can make the selection explicit or opt in to the reproducible Nix runtime:
~/src/pi-env/pi-env --runtime host "Inspect this repo with host tools."
~/src/pi-env/pi-env --runtime nix "Inspect this repo with pinned Nix tools."
You can also use the Nix flake app directly when you want the pinned runtime
without cloning first:
nix run github:u2up/pi-env -- \
"Summarize this repository and suggest safe first checks."
All of these run Pi against the cloned repository with that repository mounted
read-write at `/workspace` inside the Bubblewrap sandbox. They are intended for
inspection. If you ask Pi to build or test a project, supply the project's
build tools with the host runtime policy or declare them in a devshell for the
Nix runtime as described below.
### Optional: enable local coordination for the example
For tracked role-based agent work, enter the pi-env shell:
cd evm
nix develop github:u2up/pi-env
Then bootstrap a local coordination repository for the checkout and run Pi:
bootstrap-coordination \
--project-root "$PWD" \
--project evm \
--project-key EVM
agent-coord-status
pi-env "Inspect this repository and review its state."
This creates local coordination state under `.pi-env/` for agent issue, TODO,
and synchronization tracking. `.pi-env/` is operational state and should
normally stay untracked.
Implementation repositories may commit a small root-level attachment hint named
`.pi-env-coordination.yaml` so helpers can find their shared coordination domain:
version: 1
coordination_domain: my-product
coordination_remote: git@example.com:org/my-product-coordination.git
repo_id: backend-api
The file is read from the implementation repository root, not from inside the
coordination checkout. Explicit command options and environment variables still
win: repo id resolution is `--repo-id`, `PI_COORD_REPO_ID`,
`.pi-env-coordination.yaml`, then Git remote-name inference; coordination remote
resolution is explicit `--remote`, `PI_COORD_REMOTE_URL`, then
`.pi-env-coordination.yaml`. No legacy implementation attachment filename is
read as a fallback. The coordination repository registry remains authoritative
for canonical and active repo ids when `repositories.yaml` or the
`repos//REPO.md` registry is present.
Domain-wide generated files that are committed to implementation repositories
are declared in the owning repo's coordination manifest, not in the per-repo
attachment hint:
# repos/backend-api/REPO.md
---
repo_id: backend-api
status: active
domain_generated_files:
- REQUIREMENTS.md
- REQUIREMENTS_COVERAGE.md
---
The `repo_id` must be canonical and active, and paths are relative to that
implementation repository root. Agents should regenerate and commit those paths
only in implementation repos whose `REPO.md` lists them. More than one active
repo may list the same domain-wide generated path when the domain intentionally
keeps committed copies in several implementation repositories.
`agent-coord-lint` validates repo manifests and all repo-scoped issue structure
under `repos//issues/`. Item-matched issue tests are expected
only for the current implementation repo resolved from `--repo-id`,
`PI_COORD_REPO_ID`, `.pi-env-coordination.yaml`, or Git remote registry data.
`--all-repos` keeps structural validation across every registered repo but does
not require tests from unavailable implementation checkouts unless `--repo-id`
selects that repo explicitly. Fresh `agent-coord-init` scaffolds the initial
implementation namespace at
`repos//issues/{open,blocked,done,closed}` and writes the sole registry
record at `repos//REPO.md`; no root `REPOS.md` index is generated.
Existing `REPOS.md` files from older coordination repositories are ignored by
tooling and may be deleted manually. Existing root-layout domains can move
tracked root issue files with `agent-coord-repo migrate-root-issues `;
the command creates or validates the target repo manifest, uses `git mv` for
tracked files, and refuses target overwrites or duplicate issue ids. Root
`issues/` paths are migration-compatible by default with warnings; set
`PI_COORD_LINT_ROOT_ISSUES=fail` to reject them.
### Simple coordination workflow
A typical human-and-agent workflow with a coordination repository is:
1. **Bootstrap or clone coordination state** for the project. For local-only
experiments this is `.pi-env/coordination`; for team use, point it at a
shared Git remote.
2. **Capture project knowledge there**: requirements, decisions, notes, issues,
TODOs, ownership, and validation expectations that should survive beyond one
chat session.
3. **Pick or create a work item** before starting implementation. Pull the
coordination repo, inspect open items, and claim or assign one when work
begins.
4. **Run Pi with the project and coordination state mounted**. The agent can use
the coordination repository as shared memory and should record meaningful
events, decisions, handoffs, and validation results there.
5. **Review two diffs separately**: project-source changes in the project repo,
and coordination changes in the coordination repo. Commit/push each to its
own repository when accepted.
6. **Repeat from coordination state**, not from chat history. The next human or
agent pulls the coordination repo and sees the current requirements,
decisions, item status, and handoff notes.
In short, the project repository remains the source of product code, while the
coordination repository is the source of shared process memory and work state.
For a serial automation loop that applies this pattern across developer,
reviewer, and tester roles, see
[Serial role automation](#serial-role-automation).
## 1. Host prerequisites
Install or configure these on the host before using this repository.
### Required host dependencies
- **Linux** with unprivileged user namespaces/Bubblewrap support.
- **`pi-coding-agent`** installed on the host and available as `pi` on `PATH`.
`pi-env` does not pin or install Pi itself.
- **Model credentials** for Pi, either in Pi's normal auth files under
`~/.pi/agent` or as provider environment variables such as
`ANTHROPIC_API_KEY` / `OPENAI_API_KEY`.
- **Git** or another way to fetch this repository.
### Optional Nix dependency
Install **Nix** with flakes enabled for Nix runtime workflows (`--runtime nix`,
`nix run`, `nix develop`, profile installs, or project flake integration). You
can either enable the `nix-command` and `flakes` experimental features globally
or pass them when running Nix. Direct checkout use defaults to the host runtime
and does not require Nix.
Quick checks:
pi --version
# Needed only for Nix runtime workflows:
nix --version
If Pi is not installed yet, install it using the upstream package. A common npm
installation is:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent@latest
pi --version
Node/npm are needed on the host for this Pi installation or upgrade step and
for host-runtime launches that use the npm-installed Pi launcher. In host
runtime mode, `node` for an npm-installed Pi launcher must resolve under
`/usr/local/bin`, `/usr/bin`, or `/bin`; `PI_BWRAP_HOST_EXTRA_PATH` does not
admit that launcher interpreter. The pi-env Nix shell provides Node and the
other runtime tools when you select the Nix runtime.
### Provided by pi-env
When you enter the devshell, select `--runtime nix`, run `nix run`, or consume
pi-env as a flake, Nix supplies the pinned runtime tools used by the wrappers:
bash bubblewrap cacert coreutils fd findutils gawk git gnugrep gnused
gnutar gzip jq nodejs ripgrep which
The development shell also includes review and patch utilities for contributor
workflows:
diff diff3 patch
Verify the devshell tools with:
nix develop --command bash -lc 'command -v diff diff3 patch'
You normally do not need to install these separately for pi-env itself.
## 2. Install pi-env
Clone this repository for direct host-runtime use:
git clone https://github.com/u2up/pi-env.git ~/src/pi-env
Enter the devshell when you want Nix-pinned pi-env commands and helper tools:
cd ~/src/pi-env
nix develop
If flakes are not enabled globally, use:
nix --extra-experimental-features 'nix-command flakes' develop
Verify the commands are available:
pi-env --help
pi-bwrap --help
### Non-Nix host-runtime installation
For a conventional host-runtime install, use the non-Nix installer from a
release/archive checkout:
./scripts/install-non-nix --prefix "$HOME/.local" --check-deps
export PATH="$HOME/.local/bin:$PATH"
pi-env --runtime host --help
The installer does not invoke Nix and does not install a pinned runtime
toolchain. It copies pi-env commands to `$PREFIX/bin`, support files to
`$PREFIX/share/pi-env`, and writes wrappers that resolve coordination support,
coordination templates, and the role-manager package from that installed
prefix. Host tools such as Bash, Bubblewrap, Git, jq, Node, the `pi` launcher,
and standard POSIX text/file utilities must already be available. Use the Nix
flake, devshell, or profile packages when you need the reproducible pinned
runtime.
Release artifacts can contain only the pi-env payload directories (`scripts/`,
`role-manager/`, and `pi-skill-templates/`); end users do not need a Git clone
for installation. Prefer tagged releases or release artifact URLs for stable
installs, for example `--ref v0.1.0` or `--artifact-url URL`.
For latest/development testing, the installer can bootstrap itself from a
GitHub branch archive when no local payload is present:
curl -fsSL https://raw.githubusercontent.com/u2up/pi-env/main/scripts/install-non-nix \
| bash -s -- --ref main --prefix "$HOME/.local" --check-deps
`--ref main` is mutable and non-reproducible; treat it as a development/latest
channel, not the recommended stable install path. Use `--repo OWNER/REPO` for
forks, or `--artifact-url URL` to install from a specific archive URL.
Re-run the same command to upgrade or repair an install. To
remove installed files later, use the manifest-backed uninstall command:
pi-env-uninstall
# or, without PATH setup:
"$HOME/.local/bin/pi-env-uninstall"
Inside `nix develop` the prompt is prefixed with `(nix-dev)`. The shell exports
`PI_ENV_ROLE_MANAGER_PACKAGE` to the Nix-built role-manager package path and
prints a short reminder unless `PI_ENV_QUIET` is set.
### Optional profile installation
For the smallest profile that can launch Pi in the sandbox, install the core
runtime package. It puts `pi-env`, `pi-start`, `pi-bwrap`, and the runtime tools
on `PATH` without the Git-backed coordination helper commands:
nix profile install ~/src/pi-env#pi-core
If you also use coordination helpers, either install them separately or keep the
compatibility runtime bundle:
nix profile install ~/src/pi-env#pi-env-coordination
# or, for the compatibility bundle used by older docs/automation:
nix profile install ~/src/pi-env#pi-runtime
`pi-runtime` continues to include the core runtime plus coordination helpers.
None of these packages install `pi-coding-agent`; the host `pi` command must
already exist.
## 3. Use pi-env directly from any project
Use direct mode for local, ad hoc, or internal runs where selecting a pi-env
checkout is enough and the target project does not need to pin pi-env in its
own `flake.lock`. Direct checkout startup defaults to **host runtime** mode:
`pi-env` still enters the Bubblewrap sandbox, but command-line tools are the
unpinned host tools admitted by pi-env's conservative mount policy.
From the target project directory, run this checkout's launcher:
cd /path/to/project
~/src/pi-env/pi-env
~/src/pi-env/pi-env "Inspect this repo"
~/src/pi-env/pi-env --raw -- --model anthropic/claude-sonnet-4-5 "Inspect this repo"
If you installed `#pi-core` or `#pi-runtime` into a profile, you can run the
shorter command:
cd /path/to/project
pi-env
pi-env "Inspect this repo"
The checkout launcher defaults to host runtime mode. It preserves the current
project as the detected project root; inside the sandbox that project is
mounted read-write at `/workspace`. The pi-env checkout is only the source of
launcher code and runtime policy.
Select a runtime explicitly with `--runtime` or `PI_ENV_RUNTIME`:
~/src/pi-env/pi-env --runtime host "Inspect this repo"
~/src/pi-env/pi-env --runtime nix "Inspect this repo with pinned tools"
PI_ENV_RUNTIME=nix ~/src/pi-env/pi-env
Use `--runtime host` for direct startup with unpinned host tools. Use
`--runtime nix`, `nix run`, `nix develop`, profile packages, or project flake
integration when you need the reproducible/pinned Nix runtime. `--runtime auto`
keeps compatibility with environments that already provide pi-env commands and
falls back to the Nix runtime when needed.
Use `--raw --` when you want to pass arguments directly to Pi through
`pi-bwrap` instead of using the `pi-start` defaults:
pi-env --raw -- --model anthropic/claude-sonnet-4-5 "Inspect this repo"
Select another pi-env flake reference with either form:
PI_ENV_FLAKE=github:u2up/pi-env ~/src/pi-env/pi-env
~/src/pi-env/pi-env --flake github:u2up/pi-env
## 4. Use pi-env through a project flake
Use project-integrated mode when a repository should:
- pin pi-env for the team in the repository's `flake.lock`;
- share the same pi-env revision across machines and CI jobs;
- combine pi-env with project-specific Nix dependencies; or
- expose one committed `nix develop` entrypoint for both project tools and Pi.
After integration, the usual workflow is:
cd /path/to/project
nix develop
pi-env
pi-env "Inspect this repo"
pi-env --raw -- --model anthropic/claude-sonnet-4-5 "Inspect this repo"
Both direct and flake-integrated modes keep the selected project root as the
single project mounted at `/workspace`. Direct use gets pi-env from a checkout
or profile; flake integration lets the project pin pi-env in its own
`flake.lock` and combine it with project-specific tools. Neither mode turns
pi-env into a separate workspace-env repository or multi-project manager.
### New project flake
For a project that does not yet have a flake, use this as a starting
`flake.nix`. Replace the `pi-env.url` with either a local checkout or a Git
reference that your team can access.
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils";
# Local checkout example. A shared repo could use github:u2up/pi-env.
pi-env.url = "git+file:///home/me/src/pi-env";
pi-env.inputs.nixpkgs.follows = "nixpkgs";
pi-env.inputs.flake-utils.follows = "flake-utils";
};
outputs = { nixpkgs, flake-utils, pi-env, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in {
devShells.default = pi-env.lib.mkPiShell {
inherit pkgs;
# Smallest project shell: omit agent-coord* helper commands unless
# this project uses Git-backed coordination.
includeCoordinationHelpers = false;
extraPackages = with pkgs; [
# project-specific tools, for example:
# nodejs
# python3
];
shellHook = ''
echo "project shell loaded"
'';
};
});
}
Then run:
nix develop
pi-env
`mkPiShell` defaults `includeCoordinationHelpers` to `true` so existing
consumers keep `bootstrap-coordination` and `agent-coord*` commands on `PATH`.
Set it to `false` for core-only project shells.
### Existing project flake
If the project already has a `flake.nix`, keep its existing structure and add
only the pi-env pieces.
Add the input:
inputs = {
# existing inputs...
pi-env.url = "git+file:///home/me/src/pi-env";
# or: pi-env.url = "github:u2up/pi-env";
pi-env.inputs.nixpkgs.follows = "nixpkgs";
pi-env.inputs.flake-utils.follows = "flake-utils";
};
Include `pi-env` in the outputs arguments:
outputs = { self, nixpkgs, flake-utils, pi-env, ... }:
# existing outputs...
Then choose one integration style.
#### Wrap the devshell with `mkPiShell`
Use this when you want pi-env to own the shell composition and add your project
tools through `extraPackages`:
devShells.default = pi-env.lib.mkPiShell {
inherit pkgs;
# Keep this false for a core-only runtime shell. Omit the option or set it
# to true if the project uses bootstrap-coordination or agent-coord* helpers.
includeCoordinationHelpers = false;
extraPackages = with pkgs; [
# existing project tools
];
shellHook = ''
# existing shell hook
'';
};
### Project-specific build and test tools
pi-env keeps its default runtime intentionally small. Host runtime mode uses
unpinned host tools from the conservative sandbox `PATH`; Nix runtime mode uses
the pinned tools provided by this flake. Neither mode bundles every compiler or
build system a target repository might need. Add host-runtime tools explicitly
with `PI_BWRAP_HOST_EXTRA_PATH`, or declare reproducible project tools in the
consuming project's Nix flake. Bubblewrap remains the isolation boundary in both
modes.
For builds or tests, declare tools in the consuming project's flake:
devShells.default = pi-env.lib.mkPiShell {
inherit pkgs;
includeCoordinationHelpers = false;
extraPackages = with pkgs; [
gnumake
gcc
pkg-config
];
};
`mkPiShell` turns the `extraPackages` `bin` outputs into `PI_BWRAP_EXTRA_PATH`.
In Nix runtime mode, `pi-bwrap` validates those entries before starting the
sandbox, accepts only canonical `/nix/store` directories, and then appends them
after the core pi-env runtime path. Since `/nix/store` is already mounted
read-only, no host `/bin`, host `/usr/bin`, project-writable directory, or scan
of the whole store is needed. pi-env does not infer tools from a repository
automatically.
Advanced Nix-runtime users may set `PI_BWRAP_EXTRA_PATH` directly to a
colon-separated list of command directories, but entries must be absolute
existing directories that canonicalize under `/nix/store`; unsafe entries such
as `/tmp/bin`, `$HOME/bin`, `./bin`, `/usr/bin`, or `/bin` are rejected before
Pi starts. Host-runtime users should use `PI_BWRAP_HOST_EXTRA_PATH` instead;
those entries are canonicalized, must exist, are mounted read-only, and are
rejected under host `$HOME`.
#### Add pi-env to an existing devshell
Use this when the project already has a custom `mkShell` and you only want to
add the pi-env commands:
packages = existingPackages ++ [
pi-env.packages.${system}.pi-core
];
If your shell uses `nativeBuildInputs` or `buildInputs`, add the same package
there instead. Use `pi-env.packages.${system}.pi-env-coordination` when you want
only the optional coordination helpers, or `pi-env.packages.${system}.pi-runtime`
when older automation expects the compatibility bundle containing both the core
runtime and coordination helpers.
Update a consuming project's pinned input with:
nix flake update pi-env
## 5. Command reference
### `pi-env`
Start Pi with pi-env defaults:
pi-env
Direct checkout `pi-env` defaults to host runtime mode. In all runtime modes,
`pi-env` delegates to `pi-start`, which runs the sandbox with the default tool
allowlist, `--continue`, and the default role-manager package when available:
pi-bwrap --tools read,bash,edit,write,grep,find,ls --continue -e "$PI_ENV_ROLE_MANAGER_PACKAGE"
Select the runtime with `--runtime host|nix|auto` or
`PI_ENV_RUNTIME=host|nix|auto`; the command-line option wins. Host runtime is
unpinned and uses admitted host tools. Nix runtime is reproducible and pinned
by the selected pi-env flake, entering `nix develop` when needed.
For custom Pi arguments, use raw mode:
pi-env --raw -- --model anthropic/claude-sonnet-4-5 "Inspect this repo"
pi-env --runtime nix --raw -- --model anthropic/claude-sonnet-4-5 "Inspect this repo"
### `pi-start`
`pi-start` is the default startup wrapper. It chooses the default tool list from
`PI_BWRAP_DEFAULT_TOOLS` when set, otherwise uses Pi's built-in tools:
read,bash,edit,write,grep,find,ls
By default, `pi-start` loads the packaged role-manager extension when the
package path exists. The role manager is inactive until you select a role,
restore one from session state, or request one through supported environment
variables. Set `PI_ENV_ROLE_MANAGER_AUTO=0` to omit the automatic per-run
extension argument, especially if you prefer an installed-package workflow.
### `pi-bwrap`
`pi-bwrap` runs `pi-coding-agent` inside the Bubblewrap sandbox. Use it directly
when you want full control over the Pi arguments or when running Pi subcommands:
pi-bwrap -- --help
pi-bwrap -- config
### Running `pi config`
Pi's `config` subcommand enables or disables extensions, skills, prompt
templates, and themes.
To edit the **sandboxed pi-env config**, run it through Bubblewrap:
pi-bwrap -- config
# or
pi-bwrap config
Inside the sandbox, Pi uses `/home/pi/.pi/agent/settings.json`, backed by
pi-env's per-project state directory. Project-local config remains the mounted
repo's `.pi/settings.json` under `/workspace`.
By default, pi-env copies the host `settings.json` into sandbox state on each
run when global extensions/packages are imported. If you want sandbox edits made
by `pi-bwrap -- config` to persist instead of being refreshed from the host
copy, use:
PI_BWRAP_EXTENSIONS_SYNC=missing pi-bwrap -- config
To edit your **real host/global Pi config**, run `pi config` directly after
entering the Nix devshell:
nix develop
pi config
This uses the Nix-provided runtime/tools on `PATH`, but does **not** enter the
Bubblewrap sandbox. It modifies the host Pi agent config, normally
`~/.pi/agent/settings.json` unless `PI_CODING_AGENT_DIR` points elsewhere.
## 6. Runtime and sandbox behavior
A pi-env invocation operates on one selected project root. The launcher detects
or receives that root, and the Bubblewrap layer mounts it read-write at the
fixed in-sandbox path `/workspace`. Complex layouts such as monorepos,
submodules, worktrees, or integration checkouts remain project-owned policy;
pi-env only chooses which root to expose for this run.
`pi-bwrap`:
- mounts the detected project root read-write at `/workspace`;
- mounts `/nix/store` read-only so declared devshell tools can be exposed
through validated extra command paths;
- constructs the sandbox `PATH` from allowlisted host command directories
(`/usr/local/bin`, `/usr/bin`, and `/bin`) instead of inheriting the caller's
full host `PATH`;
- mounts `/usr/local/bin` and the global Pi npm package read-only when present,
so a global npm-installed `pi` works;
- uses isolated `$HOME=/home/pi`;
- stores sandbox Pi state outside the project by default under
`$XDG_STATE_HOME/pi-env/` or
`$HOME/.local/state/pi-env/`;
- imports common Pi rules/skills/prompts/roles from the host Pi agent directory
by default (`$PI_CODING_AGENT_DIR`, else `~/.pi/agent`), limited to
`AGENTS.md`, `CLAUDE.md`, `SYSTEM.md`, `APPEND_SYSTEM.md`, `skills/`,
`prompts/`, and `roles/`;
- exposes global Pi extensions and installed package directories from the host
Pi agent directory by default (`extensions/`, `npm/`, `git/`) and copies
`settings.json`, while project-local `.pi/extensions` and `.pi/settings.json`
are available through `/workspace`;
- copies host Git config into the sandbox by default (`~/.gitconfig` and
`$XDG_CONFIG_HOME/git/config` / `~/.config/git/config`), but not Git
credentials or SSH keys;
- copies host Pi model auth files (`auth.json`, `models.json`) from
`~/.pi/agent` into sandbox state by default;
- bind-mounts only the host Pi session directory for the current working
directory into the sandbox by default (disabled for ephemeral homes), so
`/resume` and `--continue` can access sessions for the directory/project
without exposing all sessions;
- passes `PI_COORD_ROOT`, `PI_COORD_REMOTE_URL`, `PI_COORD_PROJECT`,
`PI_COORD_AGENT_ID`, `PI_COORD_PROJECT_KEY`, `PI_COORD_ROLE`, and
coordination directory context, mapping project-local coordination paths to
`/workspace/...`, binding explicit external `PI_COORD_ROOT` paths at
`/agent-remotes`, and explicitly mounting an external coordination clone
with `PI_BWRAP_COORDINATION_DIR`;
- accepts additional host-runtime command directories only through
`PI_BWRAP_HOST_EXTRA_PATH`; entries must be absolute, existing directories,
are canonicalized, are mounted read-only, and are rejected under host `$HOME`;
- does **not** mount host `$HOME`, `~/.ssh`, cloud credential directories, or
Docker sockets;
- clears the environment, then passes only terminal basics and selected LLM
provider variables;
- shares the host network by default so Pi can reach model providers.
In host runtime mode, pi-env also adds conservative read-only support mounts
for system runtime files: `/lib`, `/lib64`, `/usr/lib`, `/usr/lib64`, `/bin`,
`/usr/bin`, and certificate-related `/etc` paths when they exist. These support
mounts let admitted host binaries run inside Bubblewrap; they are not a general
host filesystem, `/usr/share`, locale, alternatives, or home-directory mount.
If your `pi` command or language-manager shims live under host `$HOME` (for
example `~/.local/bin`, `~/.nvm`, `~/.asdf`, or a per-user npm prefix), host
runtime rejects them by default because host `$HOME` is not mounted. Prefer a
system/global install under `/usr/local/bin`, `/usr/bin`, or `/bin`, move a
custom `pi` launcher or other needed command directory outside `$HOME` and opt
it in with `PI_BWRAP_HOST_EXTRA_PATH`, or use `--runtime nix` for pinned tools.
Custom host tool directories admitted with `PI_BWRAP_HOST_EXTRA_PATH` are
mounted read-only and only after validation.
When an admitted host `pi` launcher uses `#!/usr/bin/env node`, `node` itself
must resolve under `/usr/local/bin`, `/usr/bin`, or `/bin`; the launcher check
does not admit `node` from `PI_BWRAP_HOST_EXTRA_PATH`. Use a system/global Node
install or `--runtime nix` when Node only exists in another custom directory.
Important: with the `bash`/`read` tools enabled, auth copied into the sandbox
and project sessions bind-mounted into the sandbox can be read by commands or
tools inside the sandbox. This is still safer than mounting your whole home, but
use least-privilege API keys or a provider proxy when possible.
## 7. Configuration reference
Common environment knobs:
PI_BWRAP_PROJECT_ROOT=/path/to/repo # default: git root, else $PWD
PI_BWRAP_USE_GIT_ROOT=0 # bind only $PWD
PI_BWRAP_STATE_DIR=/path/to/state # persistent sandbox home/config; .pi-env/state is opt-in
PI_BWRAP_EPHEMERAL_HOME=1 # temporary home/config for this run
PI_BWRAP_IMPORT_AUTH=0 # do not import host ~/.pi/agent auth files
PI_BWRAP_AUTH_SYNC=missing # copy auth only if sandbox copy is absent; default is always
PI_BWRAP_IMPORT_SESSIONS=0 # do not bind host sessions for the current working directory
PI_BWRAP_HOST_AGENT_DIR=/path/to/agent # default: $PI_CODING_AGENT_DIR or ~/.pi/agent
PI_BWRAP_COMMON_AGENT_DIR=/path/to/dir # common rules/skills/roles dir; default: host Pi agent dir
PI_BWRAP_IMPORT_COMMON=0 # do not import common AGENTS/SYSTEM files, skills, prompts, or roles
PI_BWRAP_COMMON_SYNC=missing # copy common files only if sandbox copy is absent; default is always
PI_BWRAP_IMPORT_EXTENSIONS=0 # do not expose global Pi extensions/packages from host agent dir
PI_BWRAP_EXTENSIONS_SYNC=missing # copy settings.json only if sandbox copy is absent; default is always
PI_BWRAP_IMPORT_GIT_CONFIG=0 # do not import host ~/.gitconfig and XDG git config
PI_BWRAP_GIT_CONFIG_SYNC=missing # copy git config only if sandbox copy is absent; default is always
PI_BWRAP_HOST_GITCONFIG=/path # host global git config; default: ~/.gitconfig
PI_BWRAP_HOST_XDG_GIT_CONFIG=/path # host XDG git config; default: $XDG_CONFIG_HOME/git/config or ~/.config/git/config
PI_BWRAP_COORDINATION_DIR=/path/to/coordination # bind external coordination clone at /coordination
PI_COORD_ROOT=.pi-env/agent-remotes # explicit bare remotes; project paths map to /workspace, external paths to /agent-remotes
PI_COORD_REMOTE_URL=git@example:repo.git # optional Git-server coordination remote URL; no local remotes mount required
PI_COORD_PROJECT=pi-env # coordination project/domain name
PI_COORD_PROJECT_KEY=PIENV # optional generated item ID prefix
PI_COORD_ROLE=architect # active coordination role for helper commits/events
PI_BWRAP_DEFAULT_TOOLS="read,bash,..." # override pi-start/pi-bwrap default tools
PI_BWRAP_EXTRA_PATH=/nix/store/.../bin # Nix runtime: validated /nix/store command dirs
PI_BWRAP_HOST_EXTRA_PATH=/opt/tools/bin # host runtime: validated read-only host command dirs
PI_BWRAP_NET=0 # disable network sharing
PI_BWRAP_PASS_ENV="HTTP_PROXY,NO_PROXY" # pass extra env vars by name
Common per-project overrides can be set before running `pi-start` / `pi-bwrap`,
or exported in the project's shell hook:
PI_BWRAP_PROJECT_ROOT=/path/to/repo pi-start # mount this repo at /workspace
PI_BWRAP_USE_GIT_ROOT=0 pi-start # use $PWD instead of git root
PI_BWRAP_EPHEMERAL_HOME=1 pi-start # throw away sandbox home after the run
PI_BWRAP_STATE_DIR=$PWD/.pi-env/state pi-start # opt in to project-local sandbox state
PI_BWRAP_IMPORT_AUTH=0 pi-start # do not copy host Pi auth into sandbox state
PI_BWRAP_NET=0 pi-start # disable network access
Inside the sandbox, the selected project root is mounted read-write at
`/workspace`, while the sandbox home and Pi config live separately from the
host home. The default state location intentionally stays outside `.pi-env/`
because it can contain copied auth, settings, sessions, and caches; use
`PI_BWRAP_STATE_DIR=$PWD/.pi-env/state` only when you explicitly want that
project-local operational state.
## 8. Common vs project-specific Pi resources
`pi-env` keeps the runtime separate from user-specific agent behavior. It does
not ship common rules, skills, prompts, or custom roles itself. Instead,
`pi-bwrap` imports common Pi resources from an external directory into the
sandbox Pi agent directory.
By default, the common directory is the user's normal Pi agent directory:
$PI_CODING_AGENT_DIR # if set
~/.pi/agent # otherwise
From that directory, `pi-bwrap` imports only common agent resources:
AGENTS.md
CLAUDE.md
SYSTEM.md
APPEND_SYSTEM.md
skills/
prompts/
roles/
It does not import the whole host home, and auth/session handling remains
controlled separately by `PI_BWRAP_IMPORT_AUTH` and
`PI_BWRAP_IMPORT_SESSIONS`. Global extension/package exposure is controlled
separately by `PI_BWRAP_IMPORT_EXTENSIONS`.
To keep common rules, skills, prompts, or roles in a separate repo or directory,
point `PI_BWRAP_COMMON_AGENT_DIR` at it:
PI_BWRAP_COMMON_AGENT_DIR=~/CODE/my-pi-common pi-start
Expected layout:
my-pi-common/
AGENTS.md
skills/
common-skill/
SKILL.md
prompts/
review.md
roles/
domain-architect.md
Disable common resource import entirely with:
PI_BWRAP_IMPORT_COMMON=0 pi-start
Project-specific rules, skills, roles, and extensions should live in the
project repository so they are versioned with the project:
project/
AGENTS.md
.pi/
extensions/
project-extension.ts
skills/
project-skill/
SKILL.md
prompts/
roles/
release-builder.md
settings.json
Pi loads the common/global resources from `/home/pi/.pi/agent` and also
discovers project resources from `/workspace`, giving a clean split:
- common rules/skills/roles and global extensions/packages: user-owned,
reusable across projects;
- project-specific rules/skills/roles/extensions/packages: committed with the
project;
- `pi-env`: neutral runtime and isolation layer only.
## 9. Git config and credentials
`pi-bwrap` imports the user's host Git config into the isolated sandbox home by
default:
~/.gitconfig
$XDG_CONFIG_HOME/git/config, or ~/.config/git/config
Inside the sandbox these become:
/home/pi/.gitconfig
/home/pi/.config/git/config
This lets Git commands run by Pi use the user's normal identity, aliases,
default branch settings, diff settings, and other non-secret Git preferences
while still avoiding a host `$HOME` mount.
Disable this with:
PI_BWRAP_IMPORT_GIT_CONFIG=0 pi-start
Use a different config source with:
PI_BWRAP_HOST_GITCONFIG=/path/to/gitconfig pi-start
PI_BWRAP_HOST_XDG_GIT_CONFIG=/path/to/xdg-git-config pi-start
By default the sandbox copy is refreshed on each run. Preserve an existing
sandbox copy with:
PI_BWRAP_GIT_CONFIG_SYNC=missing pi-start
Git credentials, SSH keys, signing keys, credential helpers' backing stores,
and other files referenced from Git config are not imported automatically.
## 10. Role-manager package
`pi-env` ships a Pi role-manager package for agent roles such as architect,
developer, builder, tester, and reviewer. The package contains a Pi extension
plus Markdown role definitions under `role-manager/`. `pi-start` loads it by
default with Pi's per-run extension/package flag when the package path exists;
this does not modify global or project `settings.json`.
Inside `nix develop`, the shell exports `PI_ENV_ROLE_MANAGER_PACKAGE` to the
Nix-built role-manager package path. To opt out of default loading for one run:
PI_ENV_ROLE_MANAGER_AUTO=0 pi-start
You can still install it into project-local Pi settings if you want Pi to load
it normally without the per-run flag. In that workflow, use the opt-out variable
if you want to avoid loading the same package through both mechanisms:
pi-bwrap install -l "$PI_ENV_ROLE_MANAGER_PACKAGE"
PI_ENV_ROLE_MANAGER_AUTO=0 pi-start
The role-manager package can also be built directly:
nix build /path/to/pi-env#pi-role-manager
pi-bwrap install -l "$(readlink -f result)"
### Role files and precedence
Base roles are bundled with the package:
| Role | Purpose | Default tools |
|------|---------|---------------|
| `architect` | Design, trade-offs, decisions, and plans. | `read`, `grep`, `find`, `ls` |
| `developer` | Focused source changes. | `read`, `grep`, `find`, `ls`, `edit`, `write`, `bash` |
| `builder` | Build, packaging, integration, and release prep. | `read`, `grep`, `find`, `ls`, `bash`, `edit` |
| `tester` | Reproduction, tests, verification, and coverage gaps. | `read`, `grep`, `find`, `ls`, `bash`, `edit`, `write` |
| `reviewer` | Diff, risk, security, and maintainability review. | `read`, `grep`, `find`, `ls`, `bash` |
Role definitions are Markdown files with frontmatter. Project roles live in
`.pi/roles/*.md` beside other project Pi resources. Common roles can live in the
host/common agent resource directory as `roles/*.md`; `pi-bwrap` imports that
`roles/` directory with common `skills/` and `prompts/` when common import is
enabled. A mounted coordination clone may also provide roles for that project
coordination domain.
Roles are merged by `name`; later sources override earlier ones:
1. bundled base package roles;
2. global/common agent roles imported into `/home/pi/.pi/agent/roles`;
3. common roles from `PI_BWRAP_COMMON_AGENT_DIR/roles` when directly visible;
4. coordination-domain roles from `$PI_COORD_DIR/roles`;
5. project roles from `.pi/roles`.
See `role-manager/ROLE_FILE_SCHEMA.md` for the full schema. See
`examples/project-role-override/.pi/roles/domain-architect.md` for a minimal
project-specific role that adds a role without changing the base package.
### Role commands and tools
The extension registers these slash commands:
/role select a role interactively
/role switch the current session to a role
/role-clear clear the role and restore prior settings
/role-cycle run one bounded role cycle in this session
/role-new start a fresh session and run one role cycle
When a role is active, only that role's instructions are injected into the
system prompt for each turn. Role frontmatter may request a thinking level,
model/provider, and tool allowlist. Unknown requested tools are warned and
ignored. The default `pi-start` allowlist includes every built-in tool used by
the bundled roles. `/role-cycle` includes the role's one-cycle checklist in the
kickoff prompt, enables the package's `role_cycle_done` tool for that cycle,
and instructs the model to call it as the final action so Pi can terminate the
cycle without an extra follow-up turn. If that tool is unavailable, the prompt
asks for a normal prose final report rather than JSON. `/role-new` requests
that Pi preserve the existing UI screen while switching to the fresh session.
When the role-manager extension has an active role, it sets `PI_COORD_ROLE` for
Pi subprocesses to the role's `coordCommitter` value, or to the role name when
`coordCommitter` is omitted. Coordination helper commands use that value only
for coordination item event actors and per-command coordination Git identity;
project repository commits keep the normal imported Git identity unless the
user explicitly changes it.
See `designs/role-manager.md` for the architecture.
## 11. Agent coordination helpers
`pi-env` includes opt-in helpers for one Git-backed coordination domain per
selected project. A domain can cover multiple implementation repositories, but
each pi-env invocation mounts and works in one implementation repository at
`/workspace`. They are plain Git/text-file tooling and are separate from
`pi-start`. Install `#pi-env-coordination`, use the compatibility `#pi-runtime`
bundle, or leave `includeCoordinationHelpers` enabled in `mkPiShell` when you
want these commands. Projects usually use the project-local
`.pi-env/coordination` clone as their attachment to the shared domain.
Guided setup with inferred, project-specific defaults:
bootstrap-coordination
# inspect another project root from this devshell
bootstrap-coordination --project-root /path/to/project --print-only
# or only print the suggested PI_COORD_* environment and init command
bootstrap-coordination --print-only
Manual minimal setup with a local bare remote:
export PI_COORD_ROOT=/workspace/.pi-env/agent-remotes
export PI_COORD_PROJECT=pi-env
export PI_COORD_PROJECT_KEY=PIENV
export PI_COORD_DIR=/workspace/.pi-env/coordination
export PI_COORD_AGENT_ID=agent-a
agent-coord-init
To use a remote hosted by a Git server, pass it explicitly or set
`PI_COORD_REMOTE_URL`:
agent-coord-init --project pi-env --remote git@example.com:org/pi-env-coordination.git
agent-coord-clone --remote git@example.com:org/pi-env-coordination.git
bootstrap-coordination --remote git@example.com:org/pi-env-coordination.git --print-only
`bootstrap-coordination` is a thin wrapper around `agent-coord-init`: it prints
the inferred root, clone dir, remote, agent ID, project, and project key, then
initializes with those explicit values. Remote selection uses this precedence:
explicit `--remote`, then `PI_COORD_REMOTE_URL`, then the local bare remote
under `PI_COORD_ROOT`. If the local coordination clone already exists but the
planned local bare remote is missing or empty, it recreates that remote from
the clone's committed Git history and repairs `origin` when it is absent or
points to a missing local path.
Without a configured remote URL, this creates a bare remote at:
$PI_COORD_ROOT/$PI_COORD_PROJECT-coordination.git
If `PI_COORD_ROOT` is unset, helpers default to the project-local
`.pi-env/agent-remotes` directory. Inside the pi-env sandbox, that is normally
`/workspace/.pi-env/agent-remotes`, available through the standard project
bind mount rather than a separate remotes mount.
If `PI_COORD_ROOT` is set to a project-local path, `pi-bwrap` rewrites it to the
matching `/workspace/...` path. If it is set to an existing local path outside
the project, `pi-bwrap` bind-mounts that directory read-write at
`/agent-remotes` and rewrites `PI_COORD_ROOT=/agent-remotes` inside the
sandbox. Without explicit overrides, the sandbox launcher only recognizes
project-local `.pi-env/coordination` and `.pi-env/agent-remotes`; root-level
`coordination/` and `agent-remotes/` directories are not selected or mounted
automatically.
When `--remote` or `PI_COORD_REMOTE_URL` is set, helpers use that URL directly
and do not create a local bare remote. The remote repository must already exist
and be accessible to Git. Provide SSH keys, tokens, or credential helpers
through narrowly-scoped sandbox/project configuration as needed; pi-env does
not import the host `~/.ssh` directory or all host Git credentials wholesale.
It then clones/scaffolds `$PI_COORD_DIR` with `AGENTS.md`, domain
`PROJECT.md` metadata, a repo namespace at
`repos//issues/{open,blocked,done,closed}`, shared `requirements/`,
`todos/`, `decisions/`, and `notes/` directories, protocol docs, item-format
docs, and `.pi/skills/agent-coordination/SKILL.md`. The repo manifest at
`repos//REPO.md` is the authoritative registry record for that
implementation repo. When `PI_COORD_DIR` is unset, fresh projects use
`.pi-env/coordination`.
Each `repos//REPO.md` manifest can also declare the domain-wide
generated outputs committed by that implementation repo, with paths relative to
the implementation repo root, for example:
domain_generated_files:
- REQUIREMENTS.md
- REQUIREMENTS_COVERAGE.md
Clone the same coordination domain elsewhere with:
agent-coord-clone
Create a type-coded timestamp-ID issue in the current repo namespace with:
agent-coord-new --repo-id pi-env --type issue --category bug \
"Document pi config behavior"
agent-coord-push -m "Add PIENV documentation item"
The resulting issue path is
`repos/pi-env/issues/open/.yaml`. Functional requirements, decisions,
and notes remain common domain records at the coordination root.
Issue items can use optional categories such as `bug`, `feature-request`,
`task`, `question`, or `improvement`; use `--type issue
--category task` for task-category work. Use `agent-coord-list --category
bug issues open` to filter, or `agent-coord-list --group-by-category issues`
to sort grouped issue output.
When top-level `PROJECT.md` exists, omit `--project` for domain-common items;
`PI_COORD_PROJECT` can remain set for coordination-domain selection. For
issues, pass `--repo-id` or set `PI_COORD_REPO_ID`; helpers may also resolve it
from the implementation repo's `.pi-env-coordination.yaml` or registry remote
metadata.
Generated item IDs use a project item key prefix, a type code, a UTC timestamp,
and a three-digit collision/order suffix that starts at `001`:
---
For example, an issue can be created as
`PIENV-ISS-20260607-204155-001.yaml`; a functional requirement can be created as
`PIENV-FRQ-20260607-204155-001.yaml`. Use
`agent-coord-init --project-key PIENV` to set the initial project's stored key
during scaffolding. Project-root keys are stored in top-level `PROJECT.md` as `item_key`. Agents
should use stored keys instead of inventing new ones.
Key resolution for `agent-coord-new` is:
1. `--project-key KEY`;
2. stored root project `item_key`;
3. `PI_COORD_PROJECT_KEY` when no stored key exists;
4. derived `--project` / `PI_COORD_PROJECT` for project items;
5. derived coordination clone directory name when no project name is set.
Derived keys are uppercased and all delimiters, whitespace, pipes, slashes,
backslashes, and other non-alphanumeric characters are removed. For example,
`pi-env_test` becomes `PIENVTEST`. `--id ID` overrides the whole item ID.
Built-in type codes are `ISS` for `issue`, `FRQ` for `functional-requirement`,
`QRQ` for `quality-requirement`, `CRQ` for `constraint-requirement`, `TODO` for
`todo`, `DEC` for `decision`, and `NOTE` for `note`.
Lifecycle helpers are also available:
bootstrap-coordination
infer defaults and initialize via agent-coord-init
agent-coord-status show sync status and open/blocked/done items
agent-coord-list list issues, todos, notes, decisions, requirements, or
classes by status
agent-coord-cat print one resolved item's YAML or repo-relative path
agent-coord-pull run git pull --rebase --autostash
agent-coord-push commit and push coordination changes
agent-coord-new create a templated item
agent-coord-claim claim an item, commit, and push
agent-coord-done mark developer work done, commit, and push
agent-coord-review mark review pass/fail, commit, and push
agent-coord-verify mark verification pass/fail, commit, and push
agent-coord-close final-close reviewed+verified done items
agent-coord-lint lint item IDs, status, and item-matched tests
agent-coord-upgrade-rules --preview
preview/apply bundled rule template updates
pi-serial-roles serially run one developer/reviewer/tester Pi job at a time
Items are YAML files with chronological `events` and linked Markdown messages.
Issue state group names are developer-centric: `open` means developer work is
needed, `blocked` means developer work cannot proceed, `done` means the
developer believes implementation is complete, and `closed` means final
accepted after review and verification.
Issue items live under `repos//issues//`, and each issue
belongs to exactly one implementation repo by that path. Functional, quality,
and constraint requirements use the root-level `requirements/` directory while
preserving FRQ, QRQ, and CRQ item-ID type codes. TODO items use `todos/` and
single top-level `body: |-` records without issue history. The `agent-coord-list requirements` command reports functional,
quality, and constraint requirement items; use `functional`, `quality`, or
`constraint` for class-specific listings. Done issue listings append
review and verification sub-status after the title. Imported requirement items
record traceability in a top-level `source_refs` list using stable strings such
as old requirement IDs, `REQUIREMENTS.md#heading`, and `USE_CASES.md#section`;
lint checks imported FRQ/QRQ/CRQ items for non-empty source references plus the
standard `testable` metadata.
Decision, note, and other non-issue item types live under their semantic type
directories shared by the coordination domain. Use `agent-coord-list notes` or
`agent-coord-list todos` to list those root-level groups, optionally filtered
by their YAML `status` values. The
accepted TODO type spellings are `todo` and `todos`; `tdo` is
not a supported alias. Stored implementation refs are structured objects with
`repo`,
`branch`, and full `commit` fields. For cross-repo implementation work, create
one issue per affected repo and link them with stable item IDs in `related:` or
messages rather than path-only references; repo renames preserve aliases and
should not require reference rewrites. `agent-coord-done --implementation-ref
pi-env:main@` accepts the compact CLI form and writes the structured
YAML form. `agent-coord-close` finalizes only items that are done, reviewed, and
verified unless forced.
Commands that create item events or coordination commits accept `--role ROLE`
and read `PI_COORD_ROLE`. Item events store actor ID/role metadata explicitly;
helper commits use per-command Git identity overrides such as `pi/architect
`. These overrides are scoped to the helper's
coordination-repository `git commit`; normal project repository commits keep the
user's imported Git identity unless the user explicitly opts in to another
identity.
Existing coordination repositories are not silently overwritten. Rule upgrades
are explicit and diffable:
agent-coord-upgrade-rules --preview
agent-coord-upgrade-rules
The helpers do not make `pi-start` create, claim, mark done, review, verify,
close, commit, or push coordination state automatically. If a coordination clone
is under the mounted project, `pi-bwrap` only exposes it as normal project files
and sets `PI_COORD_DIR` to the sandbox path. For a coordination clone outside
the project, opt in explicitly:
PI_BWRAP_COORDINATION_DIR=/path/to/coordination pi-start
That clone is mounted read-write at `/coordination` and `PI_COORD_DIR` is set
to `/coordination` inside the sandbox.
When the role-manager extension has an active role, it sets `PI_COORD_ROLE` for
Pi subprocesses to the role's `coordCommitter` value, or to the role name when
`coordCommitter` is omitted. Bash-invoked `agent-coord-*` commands inherit the
active role without changing project Git identity.
### Serial role automation
`pi-serial-roles` is the first, deliberately serial automation mode for
coordination-backed role work. Use it when you want one long-lived shell to
process developer, reviewer, and tester issue jobs over one project clone and
one coordination clone, without concurrent source edits or competing Git
operations in that clone. It is useful for initial small automation and prompt
shake-out before investing in parallel workers.
Serial mode prerequisites:
- run from a clean Git project root, or pass `--project-root DIR`;
- provide a writable coordination checkout. Projects default to
`.pi-env/coordination`; use `PI_COORD_DIR` or `--coord-dir DIR` for an
explicit override path;
- run from the pi-env devshell/profile so `pi-env`, `agent-coord-*` helpers,
and `PI_ENV_ROLE_MANAGER_PACKAGE` are available, or pass explicit `--pi-env`
and `--role-manager` paths;
- configure Pi model credentials on the host the same way you do for normal
`pi-env` runs, for example host Pi auth files or provider environment
variables; and
- allow the orchestrator to mount the selected coordination clone into each raw
sandbox job. It passes `PI_BWRAP_COORDINATION_DIR`, `PI_COORD_DIR`,
`PI_COORD_AGENT_ID`, and role context for the job, and exposes packaged
lifecycle helpers through `PI_BWRAP_EXTRA_PATH` when they live in the Nix
store.
Start the loop from the project root:
cd /path/to/project
pi-serial-roles --sleep 30
Stop it with `Ctrl-C`, or use bounded modes when you want it to exit on its own:
pi-serial-roles --once
pi-serial-roles --max-jobs 3
pi-serial-roles --max-idle-polls 1 --sleep 5
pi-serial-roles --dry-run
pi-serial-roles --ui interactive --once
pi-serial-roles --ui json --once
pi-serial-roles --ui none --once
Each poll holds `.pi-env/locks/pi-serial-roles.lock` and creates
`.pi-env/locks` as needed. It requires a clean project working tree. A dirty
coordination checkout before selection is treated as busy: the loop skips
pulling, selecting, and claiming, then sleeps or exits according to the bounded
idle options. Clean coordination is still required before a pull, before a Pi
job, and after each job completes. Serial automation logs and future local
diagnostics default under
`.pi-env/logs`. Work priority is:
1. tester: done issues with `reviewed: true` and `verified: false`;
2. reviewer: done issues with `reviewed: false`;
3. developer: open issues that are unowned or already owned by the agent.
Developer items are claimed with `agent-coord-claim` before Pi is invoked.
Reviewer and tester prompts name only the selected done item and instruct the
role to use `agent-coord-review` or `agent-coord-verify`. If no issue is
eligible, the orchestrator sleeps and polls again without invoking Pi.
Every issue job starts a fresh raw Pi session with `pi-env --raw --` and does
not pass `--continue`. The default `--ui interactive` mode launches the normal
Pi TUI with the selected item prompt, active role environment, coordination
mount, and tool allowlist, but without `--mode json`, `--print`, or `-p`. It
also passes the role-manager extension flag that requests graceful TUI shutdown
after `role_cycle_done`, so the orchestrator can continue after the bounded
role cycle finishes.
Use `--ui json` for structured automation. It adds `--mode json` for JSONL
output, which is useful for unattended loops, CI-like supervision, or when you
want to parse the final `role_cycle_done` details from the corresponding
`tool_execution_end` event alongside other structured tool, usage, compaction,
and error events.
Use `--ui none` for non-interactive prompt/response output. It adds `--print`,
prints the generated role report, and exits without a TUI or JSON event stream.
The previous hold-open `interactive` behavior has intentionally been removed
and is not available under another `--ui` value or compatibility alias. If you
need to inspect a completed session, use normal logs/output or run Pi directly
instead of keeping `pi-serial-roles` blocked after each item. Coordination state
and Git history are the memory shared between jobs; a fresh conversation avoids
stale context from a previous issue influencing item selection, review,
verification, or lifecycle helper use.
The command fails closed around role execution. Dirty project trees stop the
loop before polling. A dirty coordination tree during idle pre-selection is
treated as a busy checkout: the loop does not pull, inspect, select, claim,
reset, discard, or stash, and it retries after the normal sleep. Dirty project
or coordination trees after a role job remain fatal. A failed coordination
pull/rebase stops with the helper's error so you can resolve the conflict and
rerun. A non-zero Pi job also stops the loop instead of moving on to another
issue; inspect the terminal output and clean up any project or coordination
changes before restarting.
Serial mode does not require tmux, per-role clones, worktrees, or
reviewer/tester leases, and the local lock prevents two serial loops from
sharing one clone.
Those pieces are future parallel-worker concerns. Parallel mode should use
separate clones/worktrees and additional lease rules before multiple roles edit
or mutate coordination concurrently.
See `designs/serial-role-automation.md` for the full design.
See `designs/agent-coordination.md` for the full design.
## 12. Upgrading
`pi-env` does not pin or install `pi-coding-agent` through Nix. The wrappers
expect a `pi` executable to already exist on the host `PATH`, then `pi-bwrap`
bind-mounts the host/global Pi installation read-only into the sandbox.
When a new Pi version is available, upgrade Pi on the host, outside `pi-start` /
`pi-bwrap`:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent@latest
pi --version
Then continue using `pi-env` normally:
nix develop
pi-start
Do not run Pi self-updates from inside the Bubblewrap sandbox: `/usr/local/bin`
and the global Pi npm package are mounted read-only there.
If your current global Pi supports self-update and your user has permission to
update the global install, this can also be run on the host:
pi update --self
That updates Pi itself. It is separate from updating a project's `pi-env` flake
input. If another project consumes this repository as a flake input and pi-env
changed, update that input in the consuming project with:
nix flake update pi-env
## 13. Development and tests
Run the whole project test suite with:
tests/run.sh
Coordination helper smoke tests:
tests/agent-coord-blackbox.sh
tests/agent-coord-concurrency.sh
tests/agent-coord-lint.sh
tests/coordination-items-closed-or-done.sh
Role-manager package, schema/template, loader, and command smoke tests:
tests/role-manager-package.sh
tests/role-manager-schema.sh
tests/role-manager-loader.sh
tests/role-manager-commands.sh
Item-matched tests live in the owning implementation repository under
`tests/items/` and match the item ID by filename stem. Issue items belong to a
single repo namespace under `repos/{repo_id}/issues/{status}`, but project item
tests mirror only the root item type; they do not mirror repo namespaces or
issue lifecycle status directories:
.pi-env/coordination/repos/pi-env/issues/closed/PIENV-ISS-20260607-204155-001.yaml
tests/items/issues/PIENV-ISS-20260607-204155-001.sh
.pi-env/coordination/requirements/PIENV-FRQ-20260607-204155-001.yaml
tests/items/requirements/PIENV-FRQ-20260607-204155-001.sh
## 14. Notes
- Pi's built-in tool list is `read,bash,edit,write,grep,find,ls`.
`pi-start` allowlists those by default. If you need extension/custom tools
too, include them in `PI_BWRAP_DEFAULT_TOOLS` or call `pi-bwrap` with your own
`--tools` list.
- Global Pi extensions and globally installed Pi packages are exposed read-only
from the host agent directory by default. Disable this with
`PI_BWRAP_IMPORT_EXTENSIONS=0`. Project-local extensions/packages under
`.pi/` are available because the project is mounted at `/workspace`.
- Use `git` through the `bash` tool unless you install/register a separate Git
tool extension.
- Bubblewrap limits filesystem/environment exposure. It does not provide
domain-level network allowlists. For tighter network policy, disable network
with `PI_BWRAP_NET=0`, use an external firewall/proxy, or add Pi's sandbox
extension as an additional layer for `bash` commands.
标签:AI编程助手, Bubblewrap, MITM代理, Nix, SOC Prime, 可复现环境, 多智能体协作, 应用安全, 开发工具, 沙箱隔离, 环境隔离, 网络安全研究