# Development

## Requirements

- Pi 0.99.1 with `pi` on `PATH` for the pinned integration tests;
- Node.js and pnpm;
- Git.

## Set up a checkout

```bash
git clone https://github.com/alexshpunt/pi-agent-ide.git
cd pi-agent-ide
pnpm install
```

## Run the unified extension

The root package uses the same single entrypoint as the published package:

```bash
pi --no-extensions -e .
```

`--no-extensions` prevents an already installed copy from loading at the same time. Explicit `-e` extensions still load.

Use the project config at `.pi/pi-agent-ide/extensions.json` to disable built-ins or turn on built-ins that are off by default for this checkout. A global config can be placed at `~/.pi/agent/pi-agent-ide/extensions.json`.

## Run modular mode

Modular mode loads every built-in through its independent source entrypoint:

```bash
pnpm dev:modular
```

Use this mode when working on plugin ordering, package boundaries, or failures that should be isolated to one Pi extension. The modular manifest contains the ordered entrypoint list.

The published package uses the composite entrypoint because loading dozens of Pi extensions separately is noisy for normal use.

## Test

Run unit tests after changing a covered component:

```bash
pnpm exec vitest run --config vitest.config.mjs
```

Run real-Pi integration tests after changing extension loading, tools, hooks, sessions, rendering, or filesystem behavior:

```bash
pnpm test:integration
```

Integration tests use `pi-coding-agent-test`. They start real Pi processes with scripted model responses and preserve traces and terminal artifacts under `.tmp/test-runs/`.

The root runner uses native rendering by default. The published harness's raw renderer imports private files relative to the CLI and cannot start with Pi 0.99.1's bundled CLI. Explicit `rawMode: true` still exposes that failure; it is not silently replaced. Result and filesystem assertions remain enabled.

Tests that need an unchanged conversation use `#integration/support/pi-runtime/native-pi-coding-agent-test.js`. The root `pi-coding-agent-test` alias uses the same native adapter and keeps its existing surrounding read calls. Both adapters forward explicit launch options; the native adapter also reads the system prompt from Pi 0.99's transcript system messages using the SDK.

`pnpm test:integration:shards` runs four standalone shards. Each scenario closes its Pi process instead of retaining a shared process for every fixture configuration. Each run prints its own log directory under `.tmp/integration-shards.*`, so parallel audits do not overwrite each other's logs. Set `LOG_DIR` to choose another parent directory.
CI uses the same four shards without changing test or job timeouts. Set `REPORT_DIR` to write a separate `integration-N.xml` JUnit report for each shard. CI retains all shard reports and logs, and builds the release candidate only after every shard passes.

`--no-extensions` also disables Pi's native built-ins. Native fixtures explicitly load `builtin:codemode`, `builtin:tool-search`, and `builtin:mcp`; ordinary fixtures do not. `native-host.integration.test.ts` checks the version inside the launched process, deferred discovery, nested execution, and isolation from personal MCP servers.

### Watch integration tests live

Use the project command to watch every test in one file:

```bash
pnpm test:integration:live -- \
  tests/integration/path/to/example.integration.test.ts
```

Use the explicit named-test command when you only want one test:

```bash
pnpm test:integration:live:test -- \
  tests/integration/path/to/example.integration.test.ts \
  "shows the expected result"
```

Both commands repeat the test until you press `Ctrl+C`. They use the `gpt-5.6-sol-xhigh` stream profile and wait 1000 ms between runs.

Override either default after the file or test name:

```bash
pnpm test:integration:live -- \
  tests/integration/path/to/example.integration.test.ts \
  --stream-profile gemini-3.5-flash \
  --pause-ms 500
```

Add `--once` for one run. Add `--delay-ms 17` to use a fixed delay instead of a stream profile.

VS Code provides two launch targets under **Run and Debug**:

- **Watch integration test file live** asks for a file and runs every test in it;
- **Watch named integration test live** also asks for the test name.

Both targets run the matching project command in the integrated terminal.

You can also call `pi-test` directly when you need full control over the wrapped test command:

```bash
pnpm exec pi-test live \
  --stream-profile gpt-5.6-sol-xhigh \
  --pause-ms 1000 \
  -- \
  vitest run tests/integration/path/to/example.integration.test.ts \
  -t "shows the expected result" \
  --config vitest.integration.config.mjs
```

Completed runs leave terminal records under `.tmp/test-runs/`. Replay a recorded case without running its tools again:

```bash
pnpm exec pi-test replay ".tmp/test-runs/path/to/test/case"
```

Use the exact artifact directory reported by the test run. Add `--play` to play the recorded terminal frames.

Check package paths before committing:

```bash
pnpm check:paths
```

## Build the public package

Build and validate the single publishable tarball:

```bash
pnpm package:public
```

The result is written to `.agents/tmp/public-package/`. The tarball contains the umbrella extension and its private workspace packages as bundled dependencies. Tests, development files, hooks, temporary files, and private package metadata are excluded.

Only the `pi-agent-ide` tarball is intended for registry publication. The bundled package names are implementation details and are not published separately.

The release runs compiled JavaScript from `dist/`. Its public entrypoints share generated chunks, so the extension and plugins use the same runtime modules. Source files remain for TypeScript consumers and module-relative resources. Development still loads `src/pi-agent-ide.ts` directly. PDF, browser, and HTML extraction dependencies load on first use instead of during startup.

To check an installed tarball, create a clean directory outside the checkout and install it with production dependencies. Pass that directory (the parent of `node_modules`) to the package integration tests:

```bash
PI_AGENT_IDE_TEST_INSTALLATION=/path/to/installation \
  pnpm exec vitest run --config vitest.integration.config.mjs \
  tests/integration/composite/release-runtime.integration.test.ts
```

These checks exercise first-use resources, browser fallback against a local page, external plugins, reload, and prompt parity. They need a system Chrome or Chromium executable. The package-specific suite is skipped when no installation is supplied.

Compare baseline and candidate installations using the same Node and Pi versions:

```bash
node scripts/measure-startup.ts \
  /path/to/baseline/extension-entry \
  /path/to/candidate/extension-entry \
  .agents/tmp/startup-comparison
```

Use the entry declared in each installed package's `pi.extensions`. The script records an initial run separately, then five interleaved fresh-process runs for each variant and Pi alone. It measures process-to-RPC-ready time, not TUI rendering or import time alone. `PI_COMMAND` can select the Pi executable. Startup Doctor hints run after session readiness and are cancelled when their session ends.

## Try development builds

For local changes, use the `develop` checkout. Run `pnpm install --frozen-lockfile` before loading it as a local Pi package. This checkout uses pnpm workspaces, so do not install it through `pi install git`.

To try a verified build without a checkout, download the `nightly-package` artifact from a successful [Develop nightly](nightly.md) run. The archive contains the tested `develop` commit and is retained for 30 days. The nightly guide explains how to install it in a disposable Pi environment. Nightlies do not publish to npm.

Public `main` carries released changes; install published versions from npm. See [Releases](releases.md) for the release flow.

## Project tasks

The Product Owner decides scope, priority, labels, status, and cycle plans. An agent assigned a tracked task may update its status while working and mark it complete after the accepted work is verified. Other project-management changes need the Product Owner's approval. The Project Manager gathers requirements, maintains task records, and plans only the next cycle; it does not inspect or change product code. Linear authentication and the optional local `.linear-project` binding are not needed to build or test the public repository.

## Repository layout

```text
packages/            shared Resource, text, image, PDF, and interception libraries
src/extensions/      read, search, and text-editor cores and plugins
src/plugins/         AST, LSP, formatter, lint, and Git-aware IDE plugins
src/composite/       unified built-in registry and configuration loader
tests/integration/   umbrella real-Pi behavior tests
```

## Dependency rules

Packages inside this monorepo use `workspace:^`. pnpm links them to local source and release packaging converts the ranges.

Packages from other repositories use normal semver ranges. Local development mappings belong in `pnpm-workspace.yaml`, not in published manifests.

Do not commit machine-specific checkout paths or `file:` dependencies.

## Adding a built-in

A new built-in normally needs:

1. an independent extension entrypoint;
2. a public protocol contribution instead of a direct core dependency;
3. an entry in `src/composite/builtin-extensions.ts` with a stable ID and dependencies;
4. an entry in the modular manifest in the required load order;
5. its ID in [configuration](./configuration.md);
6. behavior coverage at the narrowest useful level.

If a feature can live as an external extension, it does not need to become a bundled built-in.
