CLI tool
earendil-works/pi avatar
earendil-works/pi

Pi ships a coding agent CLI with no permission system of its own

Pi combines a provider-neutral LLM API, agent loop, terminal interface, and coding CLI in a TypeScript toolkit.

110,348 stars14,033 forksTypeScriptMIT

At a glance

What is it?
Pi is a TypeScript agent harness combining a provider-neutral LLM API, an agent loop, a terminal interface and a coding CLI in one monorepo. Its supply-chain discipline is unusually explicit, and its permission model is absent by design, which is the decision a user has to make first.
Who is it for?
Pi is worth adopting if you will containerize it and you want the model provider boundary in one place, because the isolation patterns and the dependency pinning are documented well enough to reason about. It is the wrong choice for a shared host where an interactive session could read another user's files, because nothing in the tool will stop that and the boundary has to be built around it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

Pi ships no permission system, so the sandbox is yours to build

Pi does not include a built-in permission system for restricting filesystem, process, network, or credential access, and by default it runs with the permissions of the user and process that launched it. That is the most consequential line in the repository for anyone running it on a machine they care about. An interactive coding session can read the files the launching user can read, start the processes that user can start, and reach whatever credentials that user's environment already holds.

The project does not leave you without answers, it leaves you without a default. `packages/coding-agent/docs/containerization.md` gives three patterns. The Gondolin extension keeps `pi` and provider auth on the host while routing built-in tools and `!` commands into a local Linux micro-VM, which keeps your model credentials outside the boundary while the things that touch your files stay inside it. Plain Docker puts the whole `pi` process in a local container, which is the simplest option and the one that also puts your provider auth inside. OpenShell runs the whole process in a policy-controlled sandbox.

The consequence is that there is no middle setting. Anything short of one of those three runs unrestricted, and a coding agent pointed at untrusted input is exactly the workload where that distinction decides whether a mistake is a typo or an incident.

min-release-age=2 keeps a same-day security fix out of your install

`.npmrc` sets `save-exact=true` and `min-release-age=2`, and the second setting carries a cost that is easy to miss. It makes npm resolution ignore any dependency release published less than two days ago, which exists so a compromised or retracted package cannot enter a build the moment it appears. The same protection applies to you when you are the one who needs the fix. When a transitive dependency ships a security patch today, an install started today will not see it, and a lockfile already pinned to the older version keeps resolving to it until somebody updates on purpose.

The rest of the posture is more conventional and more thorough. Direct external dependencies are pinned to exact versions while internal workspace packages stay version-ranged, `package-lock.json` is treated as the dependency ground truth, and `npm run check` verifies pinned direct deps, native TypeScript import compatibility, and the generated coding-agent shrinkwrap. A scheduled GitHub workflow runs `npm audit --omit=dev` and `npm audit signatures --omit=dev`, and CI installs with `npm ci --ignore-scripts`.

So the mechanism is a two-day window on every new release anywhere in the tree. Decide deliberately whether your threat model wants that window, because it is not something you can vary per project without editing `.npmrc` yourself.

npm run build walks thirteen workspaces in dependency order

`npm run build` is not one compiler pass. The script chains thirteen package builds in a fixed order, starting at `packages/chord`, then `tui`, `telemetry`, `codemode`, `mcp`, `ai`, `durable`, `agent`, `session-backends/sqlite-node`, `protocol`, `client`, `server`, and finally `coding-agent`. Each stage depends on the artefacts of the one before it, so an edit in `packages/ai` cannot be exercised in the CLI until the middle of that chain has run.

The default path also refreshes model data from provider catalogues before it builds, which puts a network dependency in front of an ordinary compile.

bash
npm install --ignore-scripts  # Install all dependencies without running lifecycle scripts
npm run build         # Refresh model data, then build all packages
npm run build:offline # Rebuild using existing model data without network access
npm run check         # Lint, format, and type check
./test.sh            # Run tests (skips LLM-dependent tests without API keys)
./pi-test.sh         # Run pi from sources (can be run from any directory)

`npm run build:offline` exists for the no-network case, and it differs from the default in one visible place: the `ai` package builds with `build:offline` instead of its network build, while every other stage runs identically. `./pi-test.sh` then runs pi from sources and works from any directory, which is the shortest route from a fresh clone to a working agent.

New contributor issues and pull requests arrive already closed

New issues and pull requests from new contributors are auto-closed by default, and maintainers review auto-closed issues daily. The reasoning is defensible for a repository taking inbound patches on a coding agent, but the effect on a user is worth stating plainly: opening an issue is not a request for attention, it is a request for a daily review pass.

That changes what an issue is worth. If you are blocked on a defect, the default state of your report is closed, and the only thing that reopens the question is a maintainer picking it up on a schedule you do not control. `CONTRIBUTING.md` carries that process, the repository keeps `AGENTS.md` for project-specific rules aimed at both humans and agents, and longer term plans live in RFCs at rfc.earendil.com.

The practical consequence is to keep your reproduction independent of the tracker. Nothing in the repository promises a response time, so any plan that depends on an upstream fix landing carries an unbounded delay.

The lockfile is reviewed output, and npm users get a different one

Pre-commit blocks accidental lockfile commits unless `PI_ALLOW_LOCKFILE_CHANGE=1` is set, which treats `package-lock.json` as reviewed output rather than a side effect of running a command. The published CLI ships `packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile, to pin transitive dependencies for npm users who never see this repository's lockfile at all.

The gap between those two files is where behaviour drifts. Inside the monorepo, internal workspace packages stay version-ranged, so a local checkout resolves sibling packages by range. Someone installing the published CLI resolves them through the generated shrinkwrap instead. A change that passes every check in this repository is therefore not automatically the change a consumer receives.

Release verification is built around the same idea: `npm run release:local` builds, packs, and creates isolated npm and Bun installs outside the repository before a release is tagged, and local release installs, documented npm installs, and `pi update --self` all use `--ignore-scripts` where supported. One guardrail sits behind that flag. Shrinkwrap generation keeps an explicit allowlist for dependency lifecycle scripts, and a new dependency needing one fails checks until a human reviews it, which is also why a user who installs with scripts disabled should expect any dependency that relies on an install step to be left unset.

./test.sh reports success without touching a model

`./test.sh` skips LLM-dependent tests when API keys are absent, which is the sensible default for a repository whose suite otherwise runs offline. The consequence is that a clean local run tells you the harness, the terminal interface and the state management hold together, and tells you nothing at all about the provider paths.

For a project whose whole purpose is presenting one API over OpenAI, Anthropic and Google, that leaves a large part of the surface unexercised, and the uncovered part is where a provider difference in streaming or structured output would show up. The fast path for closing the gap is `./pi-test.sh`, which runs pi from sources from any directory once you have keys configured.

Read a green `./test.sh` on a keyless machine as a statement about the build, not about the agent.

Seven packages, plus five example extensions that show how they compose

The repository is a monorepo named `pi-monorepo`, marked private, with `packages/*` and `packages/session-backends/*` as its workspaces. Seven packages carry the load. `pi-coding-agent` is the interactive coding agent CLI, `pi-agent-core` is the agent runtime with tool calling and state management, `pi-ai` is the unified multi-provider LLM API over OpenAI, Anthropic and Google, `pi-tui` is a terminal UI library with differential rendering, `pi-telemetry` holds vendor-neutral telemetry contracts with a reference adapter, conformance tests and typed schemas, `pi-durable` is a durable conversation, task and document runtime, and `pi-chord` is a standalone application-composition runtime for services, replicated state, RPC and plugins.

The workspace list also includes five example extensions, which is the fastest way to read how the pieces fit together: `with-deps`, `custom-provider-anthropic`, `custom-provider-gitlab-duo`, `sandbox` and `gondolin`. The last two correspond exactly to the isolation options in `packages/coding-agent/docs/containerization.md`, which tells you the sandboxing story is an extension rather than a core capability. Slack and chat automation live in a separate repository, `earendil-works/pi-chat`.

Standalone binaries come from the release archive, not a clone

GitHub releases include a versioned source archive covered by the release's `SHA256SUMS` file, and the same build script used for the official standalone binaries ships inside that archive. The archive carries release model data and native prebuilds, so the build does not need to reach provider catalogues, which is what `--offline-model-data` selects.

bash
VERSION="<release-version>"
tar -xzf "pi-${VERSION}-source.tar.gz"
cd "pi-${VERSION}"
./scripts/build-binaries.sh --offline-model-data --platform linux-x64 --out "$PWD/out"

`--platform` names a single target rather than a matrix, so a multi-platform build is one invocation per platform. `--skip-install` covers the case where dependencies are already provided, which is the path to take in CI where the dependency step is separate from the build. Worth noticing is what the two routes do not share: the published package is `@earendil-works/pi-coding-agent` on npm, so installing from the registry and building a binary from the archive are different paths to the same CLI, and only the npm route carries the generated shrinkwrap.

The last push was on 2026-09-29 and v0.99.1 shipped the same day, so the value you put in `VERSION` is worth pinning on purpose.

Editorial conclusion

Pi is worth adopting if you will containerize it and you want the model provider boundary in one place, because the isolation patterns and the dependency pinning are documented well enough to reason about. It is the wrong choice for a shared host where an interactive session could read another user's files, because nothing in the tool will stop that and the boundary has to be built around it. Before you start, check which containerization pattern you will use and whether the two-day min-release-age window in .npmrc is acceptable, because that decision is made before the first agent run and is easy to forget afterwards.

Frequently asked questions

What is the Pi agent harness?

Pi is a TypeScript toolkit that combines a provider-neutral LLM API, an agent loop, a terminal interface and a coding CLI in one monorepo called pi-monorepo. Its seven packages include pi-ai for the multi-provider API, pi-agent-core for tool calling and state management, pi-tui for terminal rendering and pi-coding-agent for the interactive CLI.

Does Pi restrict what the coding agent can access?

No. Pi does not include a built-in permission system for restricting filesystem, process, network or credential access, and by default it runs with the permissions of the user and process that launched it. Stronger boundaries come from containerizing it, with three patterns described in packages/coding-agent/docs/containerization.md: a Gondolin extension, plain Docker, and OpenShell.

How do I build Pi from source?

Run npm install --ignore-scripts and then npm run build, which refreshes model data and builds all thirteen packages in dependency order. Use npm run build:offline to rebuild from existing model data without network access, and ./pi-test.sh to run pi from sources from any directory.

Which npm package do I install to use the Pi coding agent?

The interactive coding agent CLI is published as @earendil-works/pi-coding-agent. That package ships a generated npm-shrinkwrap.json so transitive dependencies stay pinned for npm users, and the related runtime packages are @earendil-works/pi-agent-core and @earendil-works/pi-ai.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
For maintainers

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/earendil-works-pi.svg)](https://hysenlabs.com/projects/earendil-works-pi)
Community notes

Community notes