Skip to content

Agent Setup — CLAUDE.md, MCP & Debug Config

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Bootstrap all agent-context files so Claude (and future engineers) have zero-ambiguity guidance when working in any sub-project of Avelia.

Architecture: Root CLAUDE.md gives project-wide context; each sub-project has its own CLAUDE.md with precise tech-stack, commands, conventions, and testing rules. MCP servers are declared at the project root. VS Code launch/settings files enable one-click debugging.

Tech Stack: NestJS 10, TypeScript 5, PostgreSQL 16, Prisma 5, Flutter 3, Dart 3, Riverpod 2 (code-gen), go_router, Freezed, Dio, Jest, MCP (Puppeteer, Dart).


Task 1: Root CLAUDE.md

Files:

  • Create: CLAUDE.md

Step 1: Write the file

markdown
# Avelia — Project Root

Avelia is a **privacy-first mobile companion app** for couples navigating the journey from pre-conception through early childhood (7 life stages). Each partner has their own account; every data entry is explicitly private or shared — nothing crosses that line without intent.

## Repository Structure

| Path | What lives here |
|------|----------------|
| `avelia-flutter-app/` | Flutter mobile app (iOS + Android) |
| `avelia-backend/` | NestJS REST API (auth, sync, partner linking) |
| `docs/` | Product spec, design docs, implementation plans |
| `design/` | UI/UX assets |

## Sub-project Guides

- **Backend:** `avelia-backend/CLAUDE.md`
- **Flutter app:** `avelia-flutter-app/CLAUDE.md`

## Key Reference

- `docs/avelia-product-spec.md` — Complete product specification (read this first)
- `docs/plans/` — Implementation plans (named `YYYY-MM-DD-<feature>.md`)

## MCP Servers

Configured in `.claude/settings.json`:

| Server | Purpose |
|--------|---------|
| `puppeteer` | Browser automation for integration testing |
| `dart` | Flutter/Dart tooling (pub, analysis, test runner) |

## Core Principles (from spec)

1. **Private by default** — backend enforces privacy at the service layer; partner cannot read private entries, not even their existence
2. **Stage-aware** — 7 life stages; UI adapts but no features are locked
3. **E2E encrypted sync** — backend stores opaque ciphertext; it cannot read synced data
4. **No analytics, no ads, no third-party SDKs**

## Cross-cutting Conventions

- All secrets in `.env` (never committed); see `.env.example` in each sub-project
- Prefer explicit over implicit — no magic strings, no undocumented side effects
- Every feature starts with a failing test (TDD)
- Commit messages: `type(scope): description` — e.g. `feat(auth): add refresh token rotation`

Step 2: Verify it exists

Run: ls CLAUDE.md Expected: CLAUDE.md

Step 3: Commit

bash
git add CLAUDE.md
git commit -m "docs: add root CLAUDE.md with project context"

Note: Project root has no git repo yet — skip commit step if git is not yet initialised. Init with git init first if desired.


Task 2: MCP Server Config

Files:

  • Create: .claude/settings.json

Step 1: Create the .claude directory and settings file

json
{
  "mcpServers": {
    "puppeteer": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-puppeteer"],
      "env": {
        "PUPPETEER_LAUNCH_OPTIONS": "{\"headless\": true}"
      }
    },
    "dart": {
      "command": "dart",
      "args": ["pub", "global", "run", "mcp_server"],
      "env": {}
    }
  }
}

Note on Dart MCP: The mcp_server package is a community Dart MCP server. Install it with: dart pub global activate mcp_server If the package name differs in your environment, update args accordingly. Alternatively, use flutter_mcp if available on pub.dev at time of setup.

Step 2: Verify

Run: cat .claude/settings.json Expected: valid JSON with two server entries.

Step 3: Commit

bash
git add .claude/settings.json
git commit -m "chore: add MCP server config (puppeteer, dart)"

Task 3: Backend CLAUDE.md

Files:

  • Create: avelia-backend/CLAUDE.md

Step 1: Write the file

markdown
# Avelia Backend — NestJS API

REST API serving the Avelia mobile app. Responsibilities: JWT authentication (access + refresh), partner account linking via invite codes, privacy-enforced entry sync (E2E encrypted), and push notification delivery.

## Tech Stack

| Layer | Technology |
|-------|-----------|
| Runtime | Node.js 20 LTS |
| Language | TypeScript 5 (strict mode) |
| Framework | NestJS 10 |
| Database | PostgreSQL 16 |
| ORM | Prisma 5 |
| Auth | JWT (access 15 min / refresh 7 days) + bcrypt |
| Validation | class-validator + class-transformer |
| Testing | Jest + Supertest + jest-mock-extended |
| Linting | ESLint (typescript-eslint/recommended-requiring-type-checking) + Prettier |

## Commands

```bash
# Development
npm run start:dev          # Dev server with hot reload (port 3000)
npm run start:debug        # Debug mode — Node inspector on port 9229

# Testing
npm run test               # Unit tests (watch off)
npm run test:watch         # Unit tests in watch mode
npm run test:e2e           # E2E tests (requires running DB)
npm run test:cov           # Coverage report (target: ≥80% statements)

# Database
npm run prisma:migrate     # Apply pending migrations (dev)
npm run prisma:migrate:deploy  # Apply migrations (production/CI)
npm run prisma:studio      # Open Prisma Studio GUI (port 5555)
npm run prisma:generate    # Regenerate Prisma client after schema change

# Build
npm run build              # Compile to dist/
npm run start:prod         # Run compiled build
npm run lint               # Lint + auto-fix
npm run format             # Prettier format

Module Architecture

src/
  auth/               # JWT strategy, guards, login/register/refresh endpoints
    auth.module.ts
    auth.service.ts
    auth.controller.ts
    strategies/
      jwt.strategy.ts
      jwt-refresh.strategy.ts
    guards/
      jwt-auth.guard.ts
  users/              # User CRUD, profile management
    users.module.ts
    users.service.ts
    users.controller.ts
  partners/           # Invite code generation, partner linking/unlinking
    partners.module.ts
    partners.service.ts
    partners.controller.ts
  entries/            # Log entries with private/shared toggle
    entries.module.ts
    entries.service.ts
    entries.controller.ts
  sync/               # Receive and serve E2E encrypted sync payloads
    sync.module.ts
    sync.service.ts
    sync.controller.ts
  common/
    decorators/       # @CurrentUser(), @Public()
    filters/          # GlobalExceptionFilter
    interceptors/     # ResponseTransformInterceptor
    pipes/            # ValidationPipe (global)
    dto/              # Shared DTOs
  prisma/
    prisma.module.ts
    prisma.service.ts  # PrismaService extends PrismaClient
  app.module.ts
  main.ts
prisma/
  schema.prisma       # Source of truth for DB schema
  migrations/         # Generated — never edit manually

Key Conventions

Privacy enforcement

  • entries.service.ts MUST filter by userId on every query — never return another user's private entries
  • Partner visibility: entry.visibility === 'SHARED' && entry.userId === partner.id only
  • Never expose userId or internal IDs in API responses — use cuid-based public IDs

DTOs and validation

  • Every controller method must have a typed DTO (CreateEntryDto, UpdateEntryDto, etc.)
  • Use @IsEnum(), @IsNotEmpty(), @IsOptional() — no raw any
  • Global ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }) in main.ts

Error handling

  • Throw HttpException subclasses only: NotFoundException, UnauthorizedException, ConflictException, etc.
  • Never throw raw Error in services — callers won't know what status code to return
  • GlobalExceptionFilter catches unknown errors and returns 500 without leaking stack traces

Auth

  • Access token: 15-minute lifespan, carried in Authorization: Bearer <token> header
  • Refresh token: 7-day lifespan, stored hashed in DB, rotated on every use
  • @Public() decorator opts a route out of the global JwtAuthGuard

Prisma

  • Always inject PrismaService (not the raw client) so the connection pool is shared
  • Use prisma.$transaction([...]) for any operation touching ≥2 tables
  • After editing schema.prisma, run prisma:generate before building

Testing Approach

  • Unit tests (*.spec.ts): mock PrismaService with jest-mock-extended; test service logic in isolation
  • E2E tests (test/*.e2e-spec.ts): spin up a real NestJS app against a test DB; reset with prisma migrate reset --force before suite
  • Coverage gate: CI fails below 80% statement coverage on src/
  • Test file mirrors source: src/auth/auth.service.tssrc/auth/auth.service.spec.ts

Environment Variables

Copy .env.example to .env. Never commit .env.

VariableExampleDescription
DATABASE_URLpostgresql://avelia:pw@localhost:5432/avelia_devPrisma connection string
JWT_SECRET<random 64-char hex>Access token signing secret
JWT_REFRESH_SECRET<random 64-char hex>Refresh token signing secret
JWT_EXPIRES_IN15mAccess token TTL
JWT_REFRESH_EXPIRES_IN7dRefresh token TTL
PORT3000HTTP server port
NODE_ENVdevelopmentdevelopment / production / test

Debugging

VS Code: open avelia-backend/ as workspace root, then use the "Debug NestJS" launch config. It starts the server in --inspect mode and attaches automatically. Breakpoints in .ts files work via source maps.

Alternatively, run npm run start:debug then use the "Attach to NestJS" config to connect to port 9229 from a running instance.


**Step 2: Verify**

Run: `ls avelia-backend/CLAUDE.md`
Expected: file exists.

**Step 3: Commit**

```bash
git add avelia-backend/CLAUDE.md
git commit -m "docs: add avelia-backend CLAUDE.md (NestJS + Prisma conventions)"

Task 4: Backend .env.example

Files:

  • Create: avelia-backend/.env.example

Step 1: Write the file

bash
# Avelia Backend — Environment Variables
# Copy to .env and fill in values. Never commit .env.

# Database
DATABASE_URL="postgresql://avelia:changeme@localhost:5432/avelia_dev"

# JWT
JWT_SECRET="replace-with-64-char-random-hex"
JWT_REFRESH_SECRET="replace-with-different-64-char-random-hex"
JWT_EXPIRES_IN="15m"
JWT_REFRESH_EXPIRES_IN="7d"

# Server
PORT=3000
NODE_ENV=development

Step 2: Commit

bash
git add avelia-backend/.env.example
git commit -m "chore(backend): add .env.example with all required variables"

Task 5: Backend VS Code Debug Config

Files:

  • Create: avelia-backend/.vscode/launch.json
  • Create: avelia-backend/.vscode/settings.json

Step 1: Write launch.json

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug NestJS",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "start:debug"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "sourceMaps": true,
      "envFile": "${workspaceFolder}/.env"
    },
    {
      "name": "Attach to NestJS",
      "type": "node",
      "request": "attach",
      "port": 9229,
      "restart": true,
      "sourceMaps": true,
      "localRoot": "${workspaceFolder}",
      "remoteRoot": "${workspaceFolder}"
    },
    {
      "name": "Run Unit Tests (Debug)",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "test", "--", "--runInBand", "--no-coverage"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "envFile": "${workspaceFolder}/.env"
    }
  ]
}

Step 2: Write settings.json

json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "jest.jestCommandLine": "npm run test --",
  "jest.autoRun": "off"
}

Step 3: Commit

bash
git add avelia-backend/.vscode/
git commit -m "chore(backend): add VS Code debug and workspace settings"

Task 6: Flutter CLAUDE.md

Files:

  • Create: avelia-flutter-app/CLAUDE.md

Step 1: Write the file

markdown
# Avelia Flutter App

Cross-platform mobile app (iOS + Android) for Avelia. Privacy-first, offline-first: all data is stored on-device first; cloud sync is optional and E2E encrypted. The UI adapts to the user's current life stage (7 stages) without locking any feature.

## Tech Stack

| Layer | Package / Tool |
|-------|---------------|
| Language | Dart 3 (sound null safety) |
| Framework | Flutter 3.x |
| State management | Riverpod 2 (`flutter_riverpod`, `riverpod_annotation`) |
| Code generation | `build_runner` + `riverpod_generator` + `freezed` + `json_serializable` |
| Navigation | `go_router` |
| HTTP client | `dio` + `retrofit` |
| Local storage | `drift` (SQLite ORM) — mirrors Room on Android / CoreData on iOS |
| Secure storage | `flutter_secure_storage` (tokens, encryption key) |
| Biometrics | `local_auth` |
| Testing | `flutter_test`, `mocktail`, `patrol` (E2E) |
| Linting | `flutter_lints` + custom `analysis_options.yaml` |

## Commands

```bash
# Development
flutter run                          # Run on connected device/emulator (debug)
flutter run --flavor staging         # Run staging flavor
flutter run -d chrome                # Run in Chrome (web, dev only)

# Code generation (run after editing annotated files)
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch          # Continuous watch mode

# Testing
flutter test                         # All unit + widget tests
flutter test --coverage              # With coverage (output: coverage/lcov.info)
flutter test test/features/auth/     # Single feature
dart run patrol test                 # E2E/integration tests (requires device)

# Build
flutter build apk --release          # Android APK
flutter build ios --release          # iOS (requires macOS + Xcode)
flutter build appbundle --release    # Android App Bundle (Play Store)

# Analysis
flutter analyze                      # Static analysis
dart fix --apply                     # Auto-fix lint issues
dart format .                        # Format all Dart files

Architecture

Feature-first folder structure. Each feature is self-contained.

lib/
  features/
    auth/
      data/
        auth_repository.dart        # implements AuthRepository (abstract)
        auth_api.dart               # Retrofit API interface
        dtos/
          login_request.dto.dart
          auth_response.dto.dart
      domain/
        auth_repository.dart        # Abstract interface
        models/
          user.dart                 # @freezed data class
      presentation/
        screens/
          login_screen.dart
        widgets/
          auth_form.dart
        providers/
          auth_provider.dart        # @riverpod providers
    entries/
      ...                           # Same structure
    home/
    journal/
    guide/
    us/                             # Partner shared view
    settings/
  core/
    router/
      app_router.dart               # go_router configuration
    network/
      dio_client.dart               # Dio setup (interceptors, base URL)
      api_client.dart               # Retrofit client
    storage/
      database.dart                 # Drift database
      secure_storage.dart           # flutter_secure_storage wrapper
    auth/
      auth_interceptor.dart         # Attaches JWT, handles 401 refresh
    theme/
      app_theme.dart
      app_colors.dart
      app_text_styles.dart
  shared/
    widgets/                        # Reusable widgets (buttons, inputs, etc.)
    extensions/                     # Dart extension methods
    utils/
  main.dart
  bootstrap.dart                    # ProviderScope + runApp
test/
  features/                         # Mirrors lib/features/
  helpers/
    test_providers.dart             # Override providers in tests
integration_test/
  app_test.dart                     # Patrol E2E tests

Key Conventions

Riverpod

  • Use @riverpod annotation (code-gen) — no manual Provider() declarations
  • Keep providers in presentation/providers/ for screen-level state; domain/ for business logic
  • Use ref.watch in build methods; ref.read in callbacks/event handlers only
  • Override providers in tests via ProviderContainer with overrides: [...]

Models

  • All domain models use @freezed — gives copyWith, equality, pattern matching for free
  • DTOs (API in/out) use @JsonSerializable — kept separate from domain models
  • Conversion: DTO → domain model in the repository layer; never pass DTOs to the UI
  • All routes defined in core/router/app_router.dart
  • Use context.go('/path') (not Navigator.push)
  • Guards (redirect) check auth state via ref.read(authStateProvider)

Privacy

  • Every Entry model has a visibility field (Visibility.private | Visibility.shared)
  • UI shows private entries only to the owner; shared entries visible to linked partner
  • The private/shared toggle must be present on every log entry UI — no implicit defaults in UI

Error handling

  • Repositories return AsyncValue<T> or Either<Failure, T> — never throw from repositories
  • UI uses .when(data:, loading:, error:) on AsyncValue providers
  • Show user-facing error messages via ScaffoldMessenger — no raw exception messages exposed

Theming

  • All colours from AppColors — no hardcoded hex values in widgets
  • All text styles from AppTextStyles — no hardcoded TextStyle(...) in widgets
  • Support light and dark mode from day 1

Testing Approach

  • Unit tests (test/): test services, repositories, providers in isolation using mocktail mocks
  • Widget tests (test/): use ProviderScope with overridden providers; never hit real network
  • E2E tests (integration_test/): patrol against a real device/emulator with a test backend
  • Test file mirrors source: lib/features/auth/domain/auth_repository.darttest/features/auth/domain/auth_repository_test.dart

Debugging

VS Code: use the "Avelia (Debug)" launch config. Ensure a device/emulator is running first (flutter devices).

For hot reload: press r in the terminal; hot restart: R.

Flutter DevTools (performance, widget inspector, network):

bash
dart devtools

Then open the printed URL in Chrome.


**Step 2: Verify**

Run: `ls avelia-flutter-app/CLAUDE.md`
Expected: file exists.

**Step 3: Commit**

```bash
git add avelia-flutter-app/CLAUDE.md
git commit -m "docs: add avelia-flutter-app CLAUDE.md (Riverpod, Drift, go_router conventions)"

Task 7: Flutter VS Code Debug Config

Files:

  • Create: avelia-flutter-app/.vscode/launch.json
  • Create: avelia-flutter-app/.vscode/settings.json

Step 1: Write launch.json

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Avelia (Debug)",
      "request": "launch",
      "type": "dart",
      "flutterMode": "debug",
      "program": "lib/main.dart",
      "args": ["--dart-define=ENV=development"]
    },
    {
      "name": "Avelia (Profile)",
      "request": "launch",
      "type": "dart",
      "flutterMode": "profile",
      "program": "lib/main.dart"
    },
    {
      "name": "Avelia (Release)",
      "request": "launch",
      "type": "dart",
      "flutterMode": "release",
      "program": "lib/main.dart"
    },
    {
      "name": "Flutter Tests (Debug)",
      "request": "launch",
      "type": "dart",
      "program": "test/",
      "flutterMode": "debug"
    }
  ]
}

Step 2: Write settings.json

json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "Dart-Code.dart-code",
  "editor.rulers": [100],
  "dart.lineLength": 100,
  "dart.flutterSdkPath": null,
  "dart.runPubGetOnPubspecChanges": "always",
  "[dart]": {
    "editor.formatOnSave": true,
    "editor.selectionHighlight": false,
    "editor.suggest.snippetsPreventQuickSuggestions": false,
    "editor.suggestSelection": "first",
    "editor.tabCompletion": "onlySnippets",
    "editor.wordBasedSuggestions": "off"
  }
}

Step 3: Commit

bash
git add avelia-flutter-app/.vscode/
git commit -m "chore(flutter): add VS Code debug and Dart workspace settings"

Task 8: Save Memory

Files:

  • Create: /Users/antonanders/.claude/projects/-Volumes-exSSD-dev-baby-parents-app/memory/MEMORY.md

Step 1: Write MEMORY.md

markdown
# Avelia — Project Memory

## Project Overview
Privacy-first mobile app for couples (fertility → early childhood, 7 life stages).
Product spec: `docs/avelia-product-spec.md`

## Sub-projects
- `avelia-backend/` — NestJS 10 + PostgreSQL + Prisma 5 + JWT auth
- `avelia-flutter-app/` — Flutter 3 + Riverpod 2 (code-gen) + Drift + go_router

## Key Conventions
- Privacy enforced at service layer: partner never sees private entries
- E2E encrypted sync: backend stores ciphertext only
- Backend: DTOs + class-validator, HttpException hierarchy, Prisma.$transaction for multi-table ops
- Flutter: @freezed models, @riverpod code-gen, repositories return AsyncValue/Either (no throws)
- Commit style: `type(scope): description`

## MCP Servers
`.claude/settings.json` — Puppeteer + Dart MCP (dart pub global run mcp_server)

## Important Paths
- Product spec: `docs/avelia-product-spec.md`
- Plans: `docs/plans/`
- Backend env: `avelia-backend/.env.example`
- Backend debug: `avelia-backend/.vscode/launch.json`
- Flutter debug: `avelia-flutter-app/.vscode/launch.json`

Step 2: Verify

Run: ls /Users/antonanders/.claude/projects/-Volumes-exSSD-dev-baby-parents-app/memory/MEMORY.md


Execution Summary

8 tasks total. All are file creation with no dependencies between tasks — can be run sequentially or in parallel.

No tests required for this plan (it is configuration/documentation only — no runnable code).

If a git repo doesn't yet exist in the project root, initialise with git init && git add -A && git commit -m "chore: initial project scaffold" before the per-task commits.

Private by design.