Files
paseo/packages/server/CLAUDE.md
Mohamed Boudra 33a52d0ae7 Speed up cold diffs and wrapped text measurement (#4676)
* perf(app): optimize wrapped measurements and checkpoint native layout experiment

Compute full grapheme geometry only for final wrap fragments. Preserve shaped-text search behavior while bounding additive wrap probes. Keep the Android background layout experiment isolated from the normal diff renderer; native renderer integration and iOS bindings remain unfinished.

* perf(server): reduce cold diff generation work

Keep active diff reads owned across invalidation and wait for the initial watcher inventory before cold generation. Batch patch and blob reads while preserving per-file size isolation, bounded output, and daemon responsiveness. Avoid constructing fallback tokens when full-file highlighting already covers both sides.

* test(server): exercise oversized highlighted diff rejection

The single-line fixture now bypasses highlighting under the long-line guard, so it no longer reaches the structured payload limit. Use short dense lines to keep testing oversized rejection and continued socket usability.

* fix(server): queue fresh diffs for forced reads

A forced request following a repository mutation must not reuse an older active build. Invalidate that result and coalesce waiting callers onto one subsequent build. Give the cache a named object input and completed-value type.

* test(server): share deferred diff loader setup

* test(server): name completed diff cache setup

* refactor(server): use aliases for new diff imports

* fix(server): keep shipped diff imports resolvable by Node

The TypeScript build preserves path aliases without a runtime resolver. Restore relative imports for shipped code and declarations, and correct the server import guideline that prompted the regression.
2026-09-11 08:07:01 +02:00

6.8 KiB

AGENTS.md — Paseo Server Development Guide

For AI coding agents working in packages/server. Supplements CLAUDE.md at the repo root.

Project Overview

Paseo is a mobile + CLI app for monitoring and controlling local AI coding agents (Claude Code, Codex, GitHub Copilot, OpenCode, Pi). The daemon runs on your machine, manages agent processes, and streams their output over WebSocket to clients.


Build / Lint / Test Commands

Root (monorepo)

npm run dev                          # Start daemon + Expo in Tmux
npm run build:server                 # Build: highlight + relay + protocol + client + server + cli
npm run typecheck                    # Typecheck all packages
npm run test                         # Test all packages
npm run format                       # Format with Biome (in-place)

Server package (packages/server)

npm run dev                          # Start dev daemon (tsx watch)
npm run build                        # Build lib + scripts to dist/
npm run start                        # Run production daemon from dist/
npm run typecheck                    # Typecheck server source

# Run a SINGLE test file
npx vitest run src/server/agent/agent-manager.test.ts --reporter=verbose

# Run a SINGLE test by name
npx vitest run -t "returns timeout error when provider times out"

# Test categories
npm run test:unit                    # Unit tests only (excludes e2e)
npm run test:integration             # Integration tests
npm run test:integration:all         # All integration tests
npm run test:integration:real       # Real API integration tests
npm run test:integration:local       # Local integration tests
npm run test:e2e                     # End-to-end tests (excludes real/local)
npm run test:e2e:all                # All e2e tests
npm run test:watch                  # Watch mode
npm run test:ui                     # Vitest UI at localhost:51204

Other useful commands

npm run build:server                         # Rebuild server stack
npm run db:query -- "SELECT ..."             # Run arbitrary SQL
npm run cli -- ls -a -g                      # List agents
npm run cli -- daemon status                 # Check daemon status

Code Style

Biome (formatting only, no linting)

{
  "indentStyle": "space",
  "indentWidth": 2,
  "lineWidth": 100,
  "quoteStyle": "double",
  "trailingCommas": "all",
  "semicolons": "always"
}

TypeScript

  • Fully strict — no any, no implicit any
  • interface over type** when possible
  • function declarations over arrow function assignments
  • Named types — no complex inline types in public signatures
  • Object parameters — use single object param when >1 argument
  • Infer from Zod schemas — z.infer<typeof schema> instead of hand-written types
  • noUnusedLocals: true, noUnusedParameters: true, noFallthroughCasesInSwitch: true

Imports

  • Use relative .js paths for imports within shipped server code, including type imports. TypeScript preserves @server/* aliases in JavaScript and declarations, and production Node has no alias resolver. Vitest tests support the alias.
  • No barrel index.ts re-exports — they create unnecessary indirection

Naming

  • Files: kebab-case.ts named after the main export (create-tool-call.ts)
  • Tests: collocated with implementation (thing.test.ts)
  • No prefixes like RpcX, DbX, UiX — keep one canonical type per concept

Error Handling

  • Fail explicitly — throw instead of silently returning defaults
  • Typed domain errors — extend Error with structured metadata
class TimeoutError extends Error {
  constructor(
    public readonly operation: string,
    public readonly waitedMs: number,
  ) {
    super(`${operation} timed out after ${waitedMs}ms`);
    this.name = "TimeoutError";
  }
}

State Design

Discriminated unions over bags of booleans/optionals:

// Bad
interface FetchState {
  isLoading: boolean;
  error?: Error;
  data?: Data;
}

// Good
type FetchState =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "error"; error: Error }
  | { status: "success"; data: Data };

Testing Philosophy

Tests prove behavior, not structure. Every test should answer: "what user-visible or API-visible behavior does this verify?"

  • TDD: Work in vertical slices — one test, one implementation, repeat
  • Determinism first: No conditional assertions, no timing/randomness, no weak assertions
  • Real deps over mocks: Database, APIs, file system — real in tests
  • Flaky tests are a bug: Never remove a test because it's flaky; fix the variance source

Critical Rules

  1. NEVER restart the daemon on port 6767 — it kills your own process
  2. NEVER assume timeouts need a restart — they can be transient
  3. Always run npm run typecheck after changes
  4. NEVER add auth checks to tests — agent providers handle their own auth
  5. NEVER make breaking WebSocket/message schema changes — always backward-compatible

Architecture Quick Reference

packages/server/src/
├── server/
│   ├── index.ts              # Entry point
│   ├── bootstrap.ts           # Daemon initialization
│   ├── websocket-server.ts   # WS connection management
│   ├── session.ts             # Per-client session state
│   └── agent/
│       ├── agent-manager.ts  # Agent lifecycle state machine
│       └── agent-storage.ts  # File-backed JSON persistence
├── providers/                 # Claude, Codex, Copilot, OpenCode, Pi adapters
├── relay-transport.ts        # Outbound relay connection

Agent state persists to $PASEO_HOME/agents/{cwd-with-dashes}/{agent-id}.json
Daemon logs: $PASEO_HOME/daemon.log


Debugging

tail -f $PASEO_HOME/daemon.log      # Daemon logs
npm run test:ui                     # Vitest browser UI at localhost:51204
npm run cli -- inspect <agent-id>   # Detailed agent info
npm run db:query -- "SELECT * FROM agent_timeline_rows..."

Relevant Docs

File What it covers
../CLAUDE.md Repository overview, critical rules, quick start
../docs/architecture.md System design, WebSocket protocol, data flow
../docs/coding-standards.md Type hygiene, error handling, React patterns
../docs/testing.md TDD workflow, determinism, real deps over mocks
../SECURITY.md Relay threat model, E2E encryption