From e8ceccdab8e4a7fea96f4d1f5485f915af8ffb63 Mon Sep 17 00:00:00 2001 From: cteyton Date: Wed, 17 Jun 2026 17:03:08 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=94=A7=20chore(dev):=20add=20infra-on?= =?UTF-8?q?ly=20compose=20and=20pinned=20toolchain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add docker-compose.dev.yml running only Postgres + Redis under an isolated Compose project "packmind-dev", so app processes no longer run in containers over a bind mount - Add mise.toml pinning Node 24.15.0 and pnpm 11.5.0 via corepack - Add .env.example documenting optional local overrides - Add LOCAL_DEV_REVAMP_PLAN.md describing the migration Co-Authored-By: Claude Opus 4.8 --- .env.example | 19 ++++++ LOCAL_DEV_REVAMP_PLAN.md | 140 +++++++++++++++++++++++++++++++++++++++ docker-compose.dev.yml | 71 ++++++++++++++++++++ mise.toml | 9 +++ 4 files changed, 239 insertions(+) create mode 100644 .env.example create mode 100644 LOCAL_DEV_REVAMP_PLAN.md create mode 100644 docker-compose.dev.yml create mode 100644 mise.toml diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..9f2b06779 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# Local development OVERRIDES — optional. +# +# You normally do NOT need a .env file: `pnpm dev` bakes in the deterministic +# localhost wiring (see scripts/dev-serve.sh). Copy this file to `.env` ONLY to +# override a default or to supply a secret. Values set here take precedence. + +# --- Secrets (optional; enable AI features) --- +# OPENAI_API_KEY= + +# --- Override examples (uncomment + edit only if needed) --- +# Point the apps at a different Postgres/Redis (e.g. the proprietary stack on 5433/6380): +# DATABASE_URL=postgres://postgres:postgres@localhost:5432/packmind +# REDIS_URI=redis://localhost:6379 + +# Frontend → api/mcp proxy targets (defaults: localhost:3000 / localhost:3001): +# API_HOSTNAME=localhost +# API_PORT=3000 +# MCP_HOSTNAME=localhost +# MCP_PORT=3001 diff --git a/LOCAL_DEV_REVAMP_PLAN.md b/LOCAL_DEV_REVAMP_PLAN.md new file mode 100644 index 000000000..410e85e5e --- /dev/null +++ b/LOCAL_DEV_REVAMP_PLAN.md @@ -0,0 +1,140 @@ +# Local Dev Environment Revamp — Migration Plan + +**Status:** Plan only. No files changed yet. +**Goal:** Two commands to a working stack, minimal per-developer install, fast HMR, works on Linux + macOS. +**Date:** 2026-06-17 + +--- + +## 1. Problem statement + +The current `docker-compose.yml` runs the **watched Node apps** (frontend, api, mcp-server) _inside containers_ over a **bind-mounted source tree** (`.:/packmind`) plus a named `node_modules` volume. This combination is the root of a year of friction: + +- **File-system events don't cross the container/VM boundary** (especially on macOS). To compensate, the setup forces polling everywhere: `NX_WATCHER: polling`, `CHOKIDAR_USEPOLLING=1`, `WATCHPACK_POLLING=true`, polling intervals of 200–1000ms. Polling = laggy HMR + constant CPU burn. +- **`node_modules` lives in a named volume** (`dev-node_modules`), invisible to the host, prone to drift, and requiring a dedicated `install-dependencies` container on every boot. +- **The Nx daemon runs in its own container** sharing a socket via the `dev-nx-sock` volume. It's fragile: every app service carries a "Daemon OK / Daemon NOT reachable → fall back" branch that exists _only_ because the design is brittle. + +**Insight:** Docker is excellent for **stateful infra** (Postgres, Redis) and bad for **hot-reloading Node processes**. We currently use it for both. Splitting the two removes ~80% of the pain regardless of which toolchain manager we pick. + +--- + +## 2. Chosen approach — Hybrid (infra in Docker, apps native) + `mise` + +- **Infra (Postgres, Redis) stays in Docker.** Already pinned by image tag, stateful, Docker's sweet spot. Optionally keep pgAdmin. +- **Apps run natively on the host** via `nx run-many` → real fs events, fast HMR, no daemon/socket/polling. +- **Toolchain pinned by [`mise`](https://mise.jdx.dev)** reading `.nvmrc` (Node 24.15.0) + `package.json` `packageManager` (pnpm 11.5.0 via corepack). Per-dev install = Docker (usually already present) + `mise` (one curl/brew). That is the "minimal install." + +### Why this over Nix/devenv + +Nix/devenv gives maximal reproducibility (system libs, exact Postgres binary, hermetic CI) but costs team ramp-up. For a pure-JS stack whose infra is already pinned by Docker image tags, mise delivers ~95% of the reproducibility at ~5% of the cost. Nix remains the better choice **only if** hermetic, system-level reproducibility is ranked above onboarding simplicity. See Appendix A for the devenv variant if that priority changes. + +### Target developer experience + +One-time: + +```bash +mise install # installs pinned Node + pnpm +pnpm install +``` + +Daily: + +```bash +pnpm dev # infra up (pg+redis) → migrations → nx run-many serve (api, frontend, mcp) +``` + +--- + +## 3. Configuration delta: Docker service-names → localhost + +Today apps reach each other by Docker **service name**. Running natively, every reference becomes `localhost`. This is the single most error-prone part of the migration. + +| Variable | Today (in-container) | Native value | +| ------------------------- | ----------------------------------------------------- | ------------------------------------------------------ | +| `DATABASE_URL` | `postgres://postgres:postgres@postgres:5432/packmind` | `postgres://postgres:postgres@localhost:5432/packmind` | +| `REDIS_URI` | `redis://redis:6379` | `redis://localhost:6379` | +| `API_HOSTNAME` (frontend) | `backend` | `localhost` | +| `MCP_HOSTNAME` (frontend) | `mcp-server` | `localhost` | +| `API_PORT` / `MCP_PORT` | `3000` / `3001` | unchanged | +| `APP_WEB_URL` | `http://localhost:4200` | unchanged | +| `COOKIE_SECURE` | `false` | unchanged | +| `JS_PLAYGROUND_PATH` | `packages/linter/js-playground-local` | unchanged | + +These belong in a committed `.env.example` (and a gitignored `.env`, sourced by the existing `withEnv` script pattern already in `package.json`). + +Note: `packages/migrations/datasource.ts` **already** points at `localhost:5432` with `postgres/postgres/packmind` — so native migrations need no new datasource; use `datasource.ts`, not `datasourceDocker.ts`. + +--- + +## 4. Required pre-serve steps (don't lose these) + +The current compose entrypoints do two things beyond `nx serve` that must be preserved as explicit setup steps: + +1. **tsconfig selection** — `node scripts/select-tsconfig.mjs` (reads `PACKMIND_EDITION`, must be `oss`). Run once after install / on edition change. +2. **JS playground copy** — api and mcp-server copy `packages/linter/js-playground` → `packages/linter/js-playground-local` if absent. Fold into a `predev` / setup script. + +--- + +## 5. Files to add / change + +**Add:** + +- `mise.toml` — pins `node = "24.15.0"`, enables corepack/pnpm; optional `[env]` to auto-load `.env`. +- `docker-compose.dev.yml` — **infra only**: `postgres` (17-alpine), `redis` (7.2.4), optional `pgadmin`. Named volumes for data. No app services, no nx-daemon, no install-deps, no socket volume, no polling env. +- `.env.example` — the native env values from §3. +- Root `package.json` scripts: + - `dev:infra` → `docker compose -f docker-compose.dev.yml up -d` + - `dev:setup` → `node scripts/select-tsconfig.mjs && ` + - `migrate` → `cd packages/migrations && pnpm typeorm migration:run -d datasource.ts` + - `dev` → `pnpm dev:infra && pnpm migrate && nx run-many -t serve -p api frontend mcp-server` (serve targets: `api:serve:development`, `frontend:dev`, `mcp-server:serve:development` — confirm config names during build). +- `CONTRIBUTING.md` (or README dev section) — the two-command flow + troubleshooting. + +**Change:** + +- `CLAUDE.md` — update the "Local Development Environment" section to describe the hybrid flow. +- Memory note: the recorded start command (`PACKMIND_EDITION=oss docker compose --profile dev up -d`) will be superseded by `pnpm dev`. + +**Keep, untouched (transition safety):** + +- `docker-compose.yml` (full in-container stack) — leave during transition; delete only after the team validates the hybrid flow. +- `docker-local.sh` + `dockerfile/` — still used for prod-like image builds. Out of scope. + +**Eventually delete (phase 4, after validation):** from `docker-compose.yml` — `nx-daemon`, `install-dependencies`, `run-migrations`, `frontend`, `backend`, `mcp-server`, `nginx` services; the `dev-nx-sock`, `dev-node_modules`, `dev-dist`, `dev-tmp`, `dev-corepack-cache`, `dev-pnpm-store` volumes; all `CHOKIDAR_*` / `WATCHPACK_*` / `NX_WATCHER*` / `NX_DAEMON*` env. ~250 lines. + +--- + +## 6. Phased rollout (each phase = its own commit) + +1. **Add infra-only compose + mise + .env.example.** Devs can run `docker compose -f docker-compose.dev.yml up -d` and serve apps manually. Old compose still present. _Validation:_ infra healthy, apps serve natively against it. +2. **Add `pnpm dev` orchestration + setup scripts.** Collapse to two commands. _Validation:_ clean clone → `mise install && pnpm install` → `pnpm dev` → frontend at :4200, api at :3000, mcp at :3001, login works. +3. **Docs + CLAUDE.md update.** Team switches over. Gather feedback for ~1 week. +4. **Remove app/daemon services from `docker-compose.yml`** (or delete the file if `docker-local.sh` fully covers prod-like testing). Drop dead env/volumes. + +--- + +## 7. Risks & mitigations + +- **Process orchestration / logs.** `nx run-many -t serve` interleaves logs; Ctrl-C should stop all. If output is messy, add a lightweight runner (`mprocs` / `concurrently`) — but try plain `nx run-many` first (zero extra dep). +- **Migration ordering.** `pnpm dev` must run migrations _after_ Postgres is healthy. Add a readiness wait (`pg_isready` loop) or `docker compose ... up -d --wait` (Compose v2 `--wait` blocks until healthy). +- **Port conflicts** with the old full stack. Don't run both compose files at once; `docker-compose.dev.yml` reuses 5432/6379, so `down` the old stack first. +- **macOS native Postgres/Redis not needed** — they stay in Docker, so no host DB install. Bind-mount perf problem is gone because _source_ is no longer mounted into a container. +- **`frontend:dev` vs `frontend:serve` target name.** Compose uses `frontend:dev`; project.json shows `serve`. Verify the exact runnable target when wiring `pnpm dev`. +- **CI parity.** CI currently sets `CI=...` to disable the daemon. CI is unaffected (it builds images / runs e2e via the existing paths). Confirm e2e (`run-e2e-tests` profile) still works against either stack. + +--- + +## 8. Rollback + +All Phase 1–3 changes are **additive** (new files + new scripts). If the hybrid flow misbehaves, developers fall back to the untouched `docker-compose.yml` immediately. No destructive change occurs until Phase 4, which is gated on team sign-off. + +--- + +## Appendix A — devenv.sh (Nix) variant + +If hermetic reproducibility later outranks onboarding simplicity, swap `mise` + `docker-compose.dev.yml` for a single `devenv.nix`: + +- `languages.javascript` + `pnpm` (pinned), `services.postgres` (initial DB `packmind`), `services.redis` — all **native processes**, no Docker daemon at all. +- `processes.{api,frontend,mcp}` with `process-compose` `depends_on` to order migrations first. +- Per-dev install: **Nix only**. Commands: `nix develop` (or `direnv allow`) then `devenv up`. + +Trade-off: strongest reproducibility (system libs + exact Postgres binary, identical in CI) at the cost of team Nix ramp-up. Everything else in this plan (the localhost env delta, pre-serve steps, phased rollout, rollback) applies unchanged. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..de0eec245 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,71 @@ +# Local development infra ONLY — Postgres + Redis (+ optional pgAdmin). +# The apps (api, frontend, mcp-server) run natively on the host via `pnpm dev`, +# NOT in containers. This avoids bind-mount fs-event loss, file-watch polling, +# and the shared Nx-daemon socket — the sources of the old in-container setup's pain. +# +# Start: docker compose -f docker-compose.dev.yml up -d +# With UI: docker compose -f docker-compose.dev.yml --profile tools up -d +# Stop: docker compose -f docker-compose.dev.yml down +# Wipe: docker compose -f docker-compose.dev.yml down -v +# +# NOTE: reuses ports 5432/6379. Do not run this alongside the legacy +# docker-compose.yml dev stack — `down` the old one first. +# +# Dedicated Compose project name isolates containers/volumes/network from the +# legacy stack (which defaults to project "packmind"), so the two never collide. +name: packmind-dev + +services: + postgres: + image: postgres:17-alpine + container_name: packmind-dev-postgres + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: packmind + ports: + - '5432:5432' + volumes: + - packmind-dev-postgres:/var/lib/postgresql/data + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U postgres'] + interval: 2s + timeout: 2s + retries: 15 + start_period: 2s + + redis: + image: redis:7.2.4 + container_name: packmind-dev-redis + ports: + - '6379:6379' + volumes: + - packmind-dev-redis:/data + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + interval: 2s + timeout: 2s + retries: 15 + start_period: 1s + + pgadmin: + profiles: + - tools + image: dpage/pgadmin4:latest + container_name: packmind-dev-pgadmin + environment: + PGADMIN_DEFAULT_EMAIL: admin@pgadmin.com + PGADMIN_DEFAULT_PASSWORD: password + PGADMIN_LISTEN_PORT: 80 + ports: + - '2345:80' + volumes: + - packmind-dev-pgadmin:/var/lib/pgadmin + depends_on: + postgres: + condition: service_healthy + +volumes: + packmind-dev-postgres: + packmind-dev-redis: + packmind-dev-pgadmin: diff --git a/mise.toml b/mise.toml new file mode 100644 index 000000000..f8e0c2773 --- /dev/null +++ b/mise.toml @@ -0,0 +1,9 @@ +# Per-developer toolchain, pinned. Install once with `mise install`. +# Node version mirrors .nvmrc; pnpm is provisioned via corepack to match +# package.json "packageManager" exactly (no version drift vs the lockfile). +[tools] +node = { version = "24.15.0", postinstall = "corepack enable && corepack prepare pnpm@11.5.0 --activate" } + +[env] +# Default edition for local development (override per-shell if needed). +PACKMIND_EDITION = "oss" From 3e309c7feff2ffa17f9c371032e4e8314c6365df Mon Sep 17 00:00:00 2001 From: cteyton Date: Wed, 17 Jun 2026 17:03:26 +0200 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20feat(dev):=20add=20native=20"pn?= =?UTF-8?q?pm=20dev"=20stack=20orchestration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add pnpm dev / dev:infra / dev:infra:down / dev:reset / dev:setup / migrate scripts to package.json - Add scripts/dev-serve.sh: bake deterministic localhost wiring (an optional .env overrides it) and serve api, mcp-server and frontend natively via nx run-many - Add scripts/prepare-local-dev.mjs: select the effective tsconfig and seed the local JS playground directory (idempotent) - Native processes restore real fs events: no Nx daemon socket, no file-watch polling Co-Authored-By: Claude Opus 4.8 --- package.json | 6 ++++++ scripts/dev-serve.sh | 28 ++++++++++++++++++++++++++++ scripts/prepare-local-dev.mjs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100755 scripts/dev-serve.sh create mode 100644 scripts/prepare-local-dev.mjs diff --git a/package.json b/package.json index 117f4b734..5c6d9b7ed 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,12 @@ "packageManager": "pnpm@11.5.0", "scripts": { "build": "NODE_OPTIONS='--max-old-space-size=16384' nx run-many -t build", + "dev": "pnpm dev:infra && pnpm dev:setup && pnpm migrate && bash scripts/dev-serve.sh", + "dev:infra": "docker compose -f docker-compose.dev.yml up -d --wait", + "dev:infra:down": "docker compose -f docker-compose.dev.yml down", + "dev:reset": "docker compose -f docker-compose.dev.yml down -v", + "dev:setup": "node scripts/prepare-local-dev.mjs", + "migrate": "cd packages/migrations && pnpm typeorm migration:run --dataSource=datasource.ts --transaction each", "chakra:typegen": "pnpm --filter ./packages/ui exec chakra typegen ./src/lib/theme/theme.ts", "e2e": "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=false pnpm exec playwright test --config apps/e2e-tests/playwright.config.ts", "husky": "husky", diff --git a/scripts/dev-serve.sh b/scripts/dev-serve.sh new file mode 100755 index 000000000..a3b2b2847 --- /dev/null +++ b/scripts/dev-serve.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Serve the three apps natively (api, mcp-server, frontend) against the infra +# from docker-compose.dev.yml. Run by `pnpm dev`. +# +# Wiring strategy: the local-dev connection values are deterministic (apps run +# natively → everything is localhost) and are NOT secrets, so they are baked in +# here as defaults. An optional `.env` is sourced FIRST, so any value a developer +# sets there takes precedence (e.g. point DATABASE_URL at a different port). +set -euo pipefail +cd "$(dirname "$0")/.." + +set -a +# 1) developer overrides / secrets (optional) +[ -f .env ] && . ./.env +# 2) deterministic local-dev defaults — only applied when not already set above +: "${PACKMIND_EDITION:=oss}" +: "${DATABASE_URL:=postgres://postgres:postgres@localhost:5432/packmind}" +: "${REDIS_URI:=redis://localhost:6379}" +: "${API_HOSTNAME:=localhost}" +: "${API_PORT:=3000}" +: "${MCP_HOSTNAME:=localhost}" +: "${MCP_PORT:=3001}" +: "${APP_WEB_URL:=http://localhost:4200}" +: "${COOKIE_SECURE:=false}" +: "${JS_PLAYGROUND_PATH:=packages/linter/js-playground-local}" +set +a + +exec ./node_modules/.bin/nx run-many -t serve -p api mcp-server frontend diff --git a/scripts/prepare-local-dev.mjs b/scripts/prepare-local-dev.mjs new file mode 100644 index 000000000..aa4718377 --- /dev/null +++ b/scripts/prepare-local-dev.mjs @@ -0,0 +1,30 @@ +// scripts/prepare-local-dev.mjs +// One-time-ish setup for native local development (run by `pnpm dev:setup`). +// Idempotent: safe to run on every `pnpm dev`. +// 1. Select the effective tsconfig for the current PACKMIND_EDITION. +// 2. Seed the local JS playground directory the api/mcp-server expect. +// Note: no .env is created — the localhost wiring is baked into +// scripts/dev-serve.sh. A .env is only for optional overrides/secrets. +import fs from 'node:fs'; +import path from 'node:path'; +import { execFileSync } from 'node:child_process'; + +const ROOT = process.cwd(); +process.env.PACKMIND_EDITION = process.env.PACKMIND_EDITION || 'oss'; + +// 1. tsconfig selection +execFileSync('node', ['scripts/select-tsconfig.mjs'], { stdio: 'inherit' }); + +// 2. JS playground seed (api/mcp-server read JS_PLAYGROUND_PATH) +const playgroundSrc = path.join(ROOT, 'packages/linter/js-playground'); +const playgroundDst = path.join(ROOT, 'packages/linter/js-playground-local'); +if (fs.existsSync(playgroundSrc) && !fs.existsSync(playgroundDst)) { + fs.cpSync(playgroundSrc, playgroundDst, { recursive: true }); + console.log('[prepare] seeded packages/linter/js-playground-local'); +} else { + console.log( + '[prepare] js-playground-local present or source missing, skipping', + ); +} + +console.log('[prepare] local dev setup complete'); From 24d0b6111df77773c1359b6f5b96f92345cd4a4d Mon Sep 17 00:00:00 2001 From: cteyton Date: Wed, 17 Jun 2026 17:04:36 +0200 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9D=20docs(dev):=20document=20nati?= =?UTF-8?q?ve=20pnpm=20dev=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rewrite CLAUDE.md and CONTRIBUTING.md "Starting the stack" sections for the hybrid setup: infra in Docker, apps native via pnpm dev - Recommend mise for toolchain pinning; keep nvm + corepack as alternative - Note the legacy in-container stack as a transitional fallback and the port conflict between the two Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 24 ++++++++++++++++++++++-- CONTRIBUTING.md | 36 +++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index abfe43af9..1dd036029 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,8 +19,28 @@ This is an Nx monorepo containing applications and reusable packages. ## Local Development Environment -Local development uses Docker Compose to run all services (API, frontend, database, Redis, mcp-Server, Postgresq). -This starts the entire development environmentDocker Compose automatically provisions PostgreSQL and Redis - no manual setup required. +Apps (api, frontend, mcp-server) run **natively on the host**; only the stateful +infra (PostgreSQL, Redis) runs in Docker via `docker-compose.dev.yml`. This gives +real file-watch events and fast HMR — no Nx daemon socket, no file-watch polling. + +Per-developer install (minimal): Docker + [`mise`](https://mise.jdx.dev) (pins +Node + pnpm from `mise.toml`). + +```bash +mise install && pnpm install # one-time +pnpm dev # daily: infra up → migrations → serve all 3 apps native +``` + +`pnpm dev` bakes in the deterministic localhost wiring (see `scripts/dev-serve.sh`); +an optional `.env` overrides it (see `.env.example`). Other scripts: `dev:infra`, +`dev:infra:down`, `dev:reset` (wipe volumes), `dev:setup`, `migrate`. + +Apps serve at: frontend `:4200`, api `:3000`, mcp-server `:3001`. + +> Legacy full-in-container stack (`docker-compose.yml`, +> `PACKMIND_EDITION=oss docker compose --profile dev up -d`) remains during the +> transition as a fallback. Do not run it alongside `docker-compose.dev.yml` +> (port conflicts). `docker-local.sh` + `dockerfile/` still build prod-like images. ## Working with Nx The following commands apply for both NX apps and packages (use `./node_modules/.bin/nx show projects` to list actual apps and packages.) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 28a5c4fa9..04aec1849 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,21 +2,43 @@ ## Starting the stack: -You will need node 24.15.0 and docker to start the development stack. This repo -uses **pnpm 11** (pinned via `packageManager` in `package.json`); enable it through -corepack — no global install needed. pnpm settings (overrides, `allowBuilds`, -hoisting) live in `pnpm-workspace.yaml`, not `.npmrc` or the `package.json` `pnpm` -field, as required since pnpm 11: +The apps (api, frontend, mcp-server) run **natively on your host**; only Postgres +and Redis run in Docker. This gives real file-watch events and fast HMR. You need +**Docker** and **Node 24.15.0 + pnpm 11**. + +Recommended — pin the toolchain with [`mise`](https://mise.jdx.dev) (reads +`mise.toml`), then two commands: + +```shell +mise install # installs the pinned Node + pnpm (via corepack) +pnpm install +pnpm dev # Postgres + Redis up → run migrations → serve all 3 apps +``` + +Without mise, provide Node/pnpm yourself (corepack — no global install needed): ```shell nvm use corepack enable pnpm install --frozen-lockfile -PACKMIND_EDITION=oss node scripts/select-tsconfig.mjs -docker compose --profile=dev up +pnpm dev ``` The app should be available at [http://localhost:4200](http://localhost:4200) +(api on `:3000`, mcp-server on `:3001`). + +`pnpm dev` bakes in the local connection wiring; create a `.env` (see +`.env.example`) only to override a default or supply a secret. Useful scripts: +`pnpm dev:infra` (infra only), `pnpm dev:infra:down`, `pnpm dev:reset` (wipe data +volumes), `pnpm migrate`. + +pnpm settings (overrides, `allowBuilds`, hoisting) live in `pnpm-workspace.yaml`, +not `.npmrc` or the `package.json` `pnpm` field, as required since pnpm 11. + +> **Legacy stack (transitional):** the old full-in-container setup +> (`PACKMIND_EDITION=oss docker compose --profile=dev up`) still works as a +> fallback but is being retired. Do not run it at the same time as `pnpm dev` / +> `docker-compose.dev.yml` — they share ports 5432/6379. ## Migrating an existing checkout from npm to pnpm From ca54d181b873a7361d6069e0eac660dd9ef676c9 Mon Sep 17 00:00:00 2001 From: cteyton Date: Wed, 17 Jun 2026 17:24:47 +0200 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf(api):=20use=20nat?= =?UTF-8?q?ive=20file=20watching=20for=20local=20dev=20serve?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Gate nodemon's --legacy-watch/--polling-interval behind WATCH_POLLING, expanded only when the var is set, so native `pnpm dev` uses real fs events (~3s reload) instead of 500ms polling - Remove "legacyWatch": true from apps/api/nodemon.json (default native) - Set WATCH_POLLING=1 on the legacy docker-compose.yml backend service, which still needs polling over the bind mount Co-Authored-By: Claude Opus 4.8 --- apps/api/nodemon.json | 1 - apps/api/project.json | 2 +- docker-compose.yml | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/nodemon.json b/apps/api/nodemon.json index 4203d6895..be4efa4e6 100644 --- a/apps/api/nodemon.json +++ b/apps/api/nodemon.json @@ -2,7 +2,6 @@ "watch": ["dist/apps/api"], "ext": "js,json", "exec": "node dist/apps/api/main.js", - "legacyWatch": true, "delay": "200ms", "signal": "SIGTERM", "ignore": ["dist/apps/api/**/*.map"], diff --git a/apps/api/project.json b/apps/api/project.json index 62bda5dac..10c487516 100644 --- a/apps/api/project.json +++ b/apps/api/project.json @@ -47,7 +47,7 @@ "parallel": true, "commands": [ "nx run api:build:development --skip-nx-cache", - "sh -lc 'mkdir -p dist/apps/api; while [ ! -f dist/apps/api/main.js ]; do sleep 0.2; done; npx nodemon --config apps/api/nodemon.json --legacy-watch --polling-interval 500'" + "sh -lc 'mkdir -p dist/apps/api; while [ ! -f dist/apps/api/main.js ]; do sleep 0.2; done; npx nodemon --config apps/api/nodemon.json ${WATCH_POLLING:+--legacy-watch --polling-interval 500}'" ] }, "configurations": { diff --git a/docker-compose.yml b/docker-compose.yml index 9cc7dd22d..81b5e98b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -227,6 +227,7 @@ services: CHOKIDAR_USEPOLLING: '1' CHOKIDAR_INTERVAL: '200' WATCHPACK_POLLING: 'true' + WATCH_POLLING: '1' JS_PLAYGROUND_PATH: 'packages/linter/js-playground-local' APP_WEB_URL: ${APP_WEB_URL:-http://localhost:4200}