# Technology Stack **Analysis Date:** 2026-03-30 **Project Name:** Paperclip (package name `paperclip`, npm org `@paperclipai`) --- ## Languages **Primary:** - TypeScript 5.7.x — all source code across every package, compiled to ESM - JavaScript (ESM) — build scripts, generated output **Secondary:** - Bash — release scripts, smoke tests, DB backup (`scripts/`, `tests/`) --- ## Runtime **Environment:** - Node.js >=20 (enforced via `engines` in root `package.json`) - ESM-first: all packages set `"type": "module"` - `tsx` (^4.19.2) used as TS runner during development and for CLI execution **Package Manager:** - pnpm 9.15.4 (pinned via `packageManager` field) - Lockfile: `pnpm-lock.yaml` — present and committed - Patched dependency: `embedded-postgres@18.1.0-beta.16` (patch in `patches/`) --- ## Monorepo Structure **Workspace layout** (`pnpm-workspace.yaml`): ``` packages/* packages/adapters/* packages/plugins/* packages/plugins/examples/* server ui cli ``` **Packages:** | Package | Name | Purpose | |---------|------|---------| | `server/` | `@paperclipai/server` | Express HTTP server + WebSocket + plugin host | | `ui/` | `@paperclipai/ui` | React SPA (board UI) | | `cli/` | `paperclipai` | CLI binary (Node.js, esbuild-bundled) | | `packages/db/` | `@paperclipai/db` | Drizzle ORM schema, migrations, embedded-postgres helpers | | `packages/shared/` | `@paperclipai/shared` | Shared Zod schemas and TypeScript types | | `packages/adapter-utils/` | `@paperclipai/adapter-utils` | Shared utilities across AI adapters | | `packages/adapters/claude-local/` | `@paperclipai/adapter-claude-local` | Adapter for Anthropic Claude Code CLI | | `packages/adapters/codex-local/` | `@paperclipai/adapter-codex-local` | Adapter for OpenAI Codex CLI | | `packages/adapters/cursor-local/` | `@paperclipai/adapter-cursor-local` | Adapter for Cursor editor agent | | `packages/adapters/gemini-local/` | `@paperclipai/adapter-gemini-local` | Adapter for Google Gemini CLI | | `packages/adapters/openclaw-gateway/` | `@paperclipai/adapter-openclaw-gateway` | Gateway adapter (WebSocket, external agent relay) | | `packages/adapters/opencode-local/` | `@paperclipai/adapter-opencode-local` | Adapter for opencode-ai CLI | | `packages/adapters/pi-local/` | `@paperclipai/adapter-pi-local` | Adapter for pi.ai | | `packages/plugins/sdk/` | `@paperclipai/plugin-sdk` | Public plugin API (worker-side + UI bridge hooks) | --- ## Backend Framework **HTTP Server:** - Express 5.1.0 (`express`) — REST API, static file serving, middleware chain - `@types/express` 5.0.0 **WebSocket (Realtime):** - `ws` ^8.18.0 — native WebSocket server at `server/src/realtime/live-events-ws.ts` - Used for live event streaming to the board UI **Authentication:** - `better-auth` 1.4.18 — pluggable auth library with Drizzle adapter - Session handling at `server/src/auth/better-auth.ts` - JWT used for agent-to-server auth (`server/src/agent-auth-jwt.ts`) **Validation:** - `zod` ^3.24.2 — schema validation throughout server and shared packages - `ajv` ^8.18.0 + `ajv-formats` ^3.0.1 — JSON Schema validation (plugin manifests) **Logging:** - `pino` ^9.6.0 — structured JSON logging - `pino-http` ^10.4.0 — HTTP request logging middleware - `pino-pretty` ^13.1.3 — dev-mode pretty-print **File Handling:** - `multer` ^2.0.2 — multipart/form-data upload handling - `sharp` ^0.34.5 — server-side image processing **HTML Sanitization:** - `dompurify` ^3.3.2 + `jsdom` ^28.1.0 — server-side sanitization of rich text --- ## Database **ORM:** - `drizzle-orm` ^0.38.4 — TypeScript ORM, query builder - `drizzle-kit` ^0.31.9 — migration generation (`drizzle-kit generate`) **Driver:** - `postgres` ^3.4.5 — native PostgreSQL client **Database Server:** - PostgreSQL 17 (Docker: `postgres:17-alpine`) - `embedded-postgres` ^18.1.0-beta.16 — bundled PostgreSQL for local/single-binary deployments (patched) **Schema location:** `packages/db/src/schema/` — 50+ individual table files **Migrations:** `packages/db/src/migrations/` **Client factory:** `packages/db/src/client.ts` (`createDb(url)`) **Database modes:** - `embedded-postgres` — default for local CLI use (no external DB required) - `postgres` — external PostgreSQL for Docker/production deployments --- ## Frontend Framework **Framework:** - React 19.0.0 (`react`, `react-dom`) - React Router DOM 7.1.5 (`react-router-dom`) — SPA routing - React Query / TanStack Query 5.x (`@tanstack/react-query`) — server state, data fetching **Build Tool:** - Vite 6.1.0 — dev server (port 5173) and production bundler - `@vitejs/plugin-react` ^4.3.4 — JSX/Fast Refresh - In dev mode, Vite runs as Express middleware via `vite.middlewares` integration **Styling:** - Tailwind CSS 4.0.7 — utility-first CSS - `@tailwindcss/vite` ^4.0.7 — Vite plugin - `@tailwindcss/typography` ^0.5.19 — prose styles - `tailwind-merge` ^3.0+ — conditional class merging - `class-variance-authority` ^0.7.1 — component variant management - `clsx` ^2.1.1 — conditional class names **UI Components:** - `radix-ui` ^1.4.3 — unstyled accessible primitives - `@radix-ui/react-slot` ^1.2.4 - Component files in `ui/src/components/ui/`: button, card, dialog, input, badge, tabs, tooltip, etc. (shadcn-style pattern) - `lucide-react` ^0.574.0 — icon library - `cmdk` ^1.1.1 — command palette **Rich Text / Markdown:** - `@mdxeditor/editor` ^3.52.4 — rich markdown editor component - `lexical` 0.35.0 + `@lexical/link` — editor framework (peer dep of MDXEditor) - `react-markdown` ^10.1.0 — Markdown rendering - `remark-gfm` ^4.0.1 — GitHub-flavored Markdown - `mermaid` ^11.12.0 — diagram rendering **Drag-and-Drop:** - `@dnd-kit/core` ^6.3.1, `@dnd-kit/sortable` ^10.0.0, `@dnd-kit/utilities` ^3.2.2 **Path Alias:** `@/` maps to `ui/src/` (configured in `vite.config.ts`) --- ## CLI **Framework:** - `commander` ^13.1.0 — command parsing - `@clack/prompts` ^0.10.0 — interactive terminal prompts - `picocolors` ^1.1.1 — terminal color output **Build:** - `esbuild` ^0.27.3 — bundles CLI to single `dist/index.js` (config: `cli/esbuild.config.mjs`) --- ## Storage **Providers (runtime-selectable):** - Local disk — `server/src/storage/local-disk-provider.ts` — default, stores under `PAPERCLIP_HOME` - AWS S3 — `server/src/storage/s3-provider.ts` — via `@aws-sdk/client-s3` ^3.888.0 **Secrets:** - Local encrypted provider — `server/src/secrets/local-encrypted-provider.ts` - External stub providers — `server/src/secrets/external-stub-providers.ts` --- ## Testing Frameworks **Unit / Integration:** - Vitest ^3.0.5 — test runner (configured in root `vitest.config.ts` as multi-project) - Projects under test: `packages/db`, `packages/adapters/opencode-local`, `server`, `ui`, `cli` - Server tests: `server/src/__tests__/` (~95 test files) - Server vitest config: `server/vitest.config.ts` (env: `node`) - `supertest` ^7.0.0 — HTTP integration testing for Express routes **E2E:** - Playwright ^1.58.2 — browser E2E tests - Config: `tests/e2e/playwright.config.ts` - Browser: Chromium only - `tests/e2e/` — feature specs, `tests/release-smoke/` — release smoke tests **Evaluations:** - `promptfoo` 0.103.3 — LLM prompt evaluation (`evals/promptfoo/`) --- ## Build and Tooling **TypeScript:** - TypeScript 5.7.3 across all packages - Base config: `tsconfig.base.json` — target ES2023, `NodeNext` module resolution, strict mode - Each package extends base or defines its own `tsconfig.json` **Dev Runner:** - `scripts/dev-runner.mjs` — coordinates parallel dev processes (server + UI) - `chokidar` ^4.0.3 — file watching in server dev mode **CLI published as:** `paperclipai` on npm (binary: `dist/index.js`) **Server published as:** `@paperclipai/server` on npm **Plugin SDK published as:** `@paperclipai/plugin-sdk` on npm --- ## Docker / Deployment **Base image:** `node:lts-trixie-slim` (Debian-based) **Multi-stage Dockerfile:** 1. `base` — Node + pnpm + ca-certificates + curl + git 2. `deps` — install all dependencies with frozen lockfile 3. `build` — build UI, plugin-sdk, server in order 4. `production` — copy build output; globally install `@anthropic-ai/claude-code`, `@openai/codex`, `opencode-ai` **Port:** 3100 (configurable via `PORT` env var) **Data volume:** `/paperclip` — all persistent state (DB, uploads, config) **Compose variants:** - `docker-compose.yml` — full stack with external Postgres 17 - `docker-compose.quickstart.yml` — single container, embedded Postgres - `docker-compose.untrusted-review.yml` — special security sandbox mode **Key env vars:** - `DATABASE_URL` — external PostgreSQL URL (omit for embedded mode) - `BETTER_AUTH_SECRET` — required auth secret - `PAPERCLIP_DEPLOYMENT_MODE` — `authenticated` | `unauthenticated` - `PAPERCLIP_DEPLOYMENT_EXPOSURE` — `private` | `public` - `PAPERCLIP_PUBLIC_URL` — public-facing URL - `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` — AI provider keys - `PAPERCLIP_HOME` — data root directory (default `/paperclip` in Docker) --- ## AI Agent Integrations (Adapters) Each adapter follows a consistent three-export pattern: `./server`, `./ui`, `./cli`. | Adapter | Target Agent CLI | |---------|-----------------| | `adapter-claude-local` | `@anthropic-ai/claude-code` | | `adapter-codex-local` | `@openai/codex` | | `adapter-cursor-local` | Cursor editor | | `adapter-gemini-local` | Gemini CLI | | `adapter-openclaw-gateway` | Remote agent relay (WebSocket, `ws`) | | `adapter-opencode-local` | `opencode-ai` | | `adapter-pi-local` | pi.ai | The `hermes-paperclip-adapter` 0.1.1 is an additional server-side dependency (third-party adapter protocol). --- *Stack analysis: 2026-03-30*