Development Setup
Set up the pair development environment from scratch, prerequisites, install, dev commands, testing, and quality gates.
Step-by-step instructions to get the pair monorepo running locally.
Prerequisites
| Tool | Version | Install |
|---|---|---|
| Node.js | 18+ (LTS recommended) | nodejs.org |
| pnpm | 10.15.0+ | npm install -g pnpm@10.15.0 |
| Git | 2.x+ | git-scm.com |
Clone and install
pnpm install also sets up Husky git hooks automatically.
Running the dev server
Use pnpm --filter to run commands in a specific package:
Running tests
Tests use Vitest, orchestrated by Turborepo:
Testing conventions
- In-memory test doubles over mocks (e.g.,
InMemoryFileSystemServiceinstead of mockingfs). - 1:1 mapping between source modules and test files.
- TDD discipline: RED (failing test) → GREEN (minimal implementation) → REFACTOR.
Quality gate
Before committing, always run:
This runs (in order): ts:check, test, lint, format:check (prettier + markdownlint, check mode), gate:composition, hygiene:check, docs:staleness, skills:conformance, dup:check.
The gate never formats. It is the pre-push hook, where the commits already exist: a write-mode formatter would rewrite the working tree without touching what is being pushed, so it only pollutes the next diff. On a format:check failure, run pnpm format and commit the result.
Exit 2 instead of 1 means a formatter wrapper itself failed (a broken install, not drift), read its output rather than running pnpm format.
Two-step remedy: if pnpm format touched packages/knowledge-hub/dataset/**, re-sync the generated .claude/skills/** and .pair/knowledge/** copies (pnpm mirrors:regenerate) in the same commit, or a mirror guard fails later in the same gate, the dataset copy is inside format scope, its generated twin is not (.claude/ and root .pair/ are not workspace members), and the mirror guards assert each twin equals the OUTPUT of the real pair-cli update transform, never the dataset source itself, which the corpus is transformed away from.
gate:composition guards the gate against a write-mode step (formatter or eslint autofix) creeping back in. pnpm mirrors:regenerate regenerates from the working tree's own dataset, offline; pair-cli update installs the latest PUBLISHED knowledge base and is not the remedy for local drift.
See ADL the pre-push gate is check-only.
Gate registry
| Order | Gate | Command | Required |
|---|---|---|---|
| 1 | Quality Gate | pnpm quality-gate | Yes |
| 2 | Smoke Tests | pnpm smoke-tests | Yes |
| 3 | E2E Tests | pnpm --filter @pair/website e2e | Yes |
Git hooks (Husky)
Husky is configured to run checks automatically:
- Pre-commit: Type checking (
pnpm ts:check). - Pre-push: The full quality gate (
pnpm quality-gate), formatting included in check mode.
Troubleshooting hooks
| Problem | Solution |
|---|---|
| Hooks not running | Run pnpm install after cloning. Verify .husky/ exists. |
| Permission errors | chmod +x .husky/* |
| Pre-commit fails | Run the hook command manually (pnpm ts:check) to debug. |
| Pre-push fails on formatting | Run pnpm format, commit the result, push again: the gate never formats for you. |
| Pre-push fails on a mirror guard | Run pnpm mirrors:regenerate and commit the regenerated files, never hand-edit a mirror. |
Common commands
Dependency management
Shared dependency versions are managed via the pnpm catalog in pnpm-workspace.yaml:
When adding a new shared dependency, add it to the catalog first.
Changesets
We use @changesets/cli for version management. Every PR carries a changeset (or the no-changeset label):
Select the affected package(s) and bump type (patch/minor/major). Name it .changeset/<story-id>.md and mirror the PR description. Releases are cut with pnpm release <pre|rc|final>.
See the Release Process for the full workflow.
Platform notes
- macOS / Linux: Fully supported. All commands work as documented.
- Windows: Use WSL2 or Git Bash. Native Windows may have issues with symlinks (skills distribution falls back to copy mode).