Project Structure Walkthrough

Monorepo layout: apps/web (React frontend), apps/server (Express API), packages/ (shared types and utilities). Key config files and what lives where.

Project Structure Walkthrough

What you'll learn

The Paperclip monorepo layout, what each directory contains, and where to look when you want to change or extend specific behavior.


Top-level structure

paperclip/
├── apps/
│   ├── server/          ← Express API + heartbeat scheduler
│   └── web/             ← React 19 + Vite frontend
├── packages/
│   └── shared/          ← TypeScript types shared between server and web
├── .env.example         ← environment variable template
├── .env                 ← your local config (git-ignored)
├── docker-compose.yml   ← container definitions for Docker path
├── package.json         ← workspace root (pnpm workspaces)
└── pnpm-workspace.yaml  ← declares apps/* and packages/* as workspaces

apps/server — the API

apps/server/
├── src/
│   ├── index.ts            ← entry point: creates Express app, starts scheduler
│   ├── routes/             ← one router file per entity
│   │   ├── companies.ts    ← GET/POST/PATCH/DELETE /api/companies
│   │   ├── agents.ts       ← /api/agents
│   │   ├── issues.ts       ← /api/issues
│   │   ├── heartbeat.ts    ← /api/heartbeat/trigger, /api/heartbeat/runs
│   │   ├── approvals.ts    ← /api/approvals
│   │   └── activity.ts     ← /api/activity
│   ├── services/
│   │   ├── heartbeat.service.ts   ← core scheduler: selects agents, invokes adapters
│   │   ├── cost.service.ts        ← records cost_events, checks budgets
│   │   └── approval.service.ts    ← creates and resolves approval gates
│   ├── adapters/
│   │   ├── base.adapter.ts        ← the Adapter interface definition
│   │   ├── claude.adapter.ts      ← Claude Code CLI adapter
│   │   ├── openai.adapter.ts      ← OpenAI chat completions adapter
│   │   ├── http.adapter.ts        ← HTTP webhook adapter
│   │   └── shell.adapter.ts       ← shell process adapter
│   └── db/
│       ├── schema.ts              ← Drizzle ORM schema (all 7 tables)
│       ├── migrate.ts             ← runs migrations on startup
│       └── migrations/            ← SQL migration files (auto-generated)
├── package.json
└── tsconfig.json

The most important file is heartbeat.service.ts — it contains the core execution loop that the entire platform runs on.


apps/web — the frontend

apps/web/
├── src/
│   ├── main.tsx            ← React entry point
│   ├── App.tsx             ← router setup (React Router v6)
│   ├── pages/
│   │   ├── Dashboard.tsx   ← company overview, agent cards
│   │   ├── Agents.tsx      ← agent list, hire/edit/pause
│   │   ├── Issues.tsx      ← issue board (Kanban)
│   │   ├── Runs.tsx        ← heartbeat run history + costs
│   │   └── Approvals.tsx   ← pending approvals inbox
│   ├── components/         ← reusable UI components
│   │   ├── AgentCard.tsx
│   │   ├── IssueRow.tsx
│   │   ├── CostChart.tsx
│   │   └── ApprovalGate.tsx
│   └── api/
│       └── client.ts       ← typed fetch wrapper for every endpoint
├── package.json
└── vite.config.ts

packages/shared — shared types

packages/shared/
└── src/
    ├── types/
    │   ├── company.types.ts
    │   ├── agent.types.ts
    │   ├── issue.types.ts
    │   └── heartbeat.types.ts
    └── index.ts            ← re-exports everything

Both apps/server and apps/web import from @paperclip/shared. This ensures the TypeScript types for API request/response shapes are identical on both ends — no silent type drift.


Where to look for common tasks

Task File to edit
Add a new API endpoint apps/server/src/routes/
Change task selection logic apps/server/src/services/heartbeat.service.ts
Add a new adapter type apps/server/src/adapters/
Change the DB schema apps/server/src/db/schema.ts + run pnpm db:generate
Add a new dashboard page apps/web/src/pages/ + register in App.tsx
Add a shared type packages/shared/src/types/

Checkpoint ✓

Sign in for progress tracking & Q&A →