Skip to Content
ContributingDevelopment

Development

Prerequisites

  • Node.js 20 or newer
  • pnpm 9 (the repository pins pnpm@9.15.9)
  • git
git clone https://github.com/zschiller/screenplay.git cd screenplay pnpm install

The monorepo

PathWhat it isDev port
apps/appThe product: canvas, agent runtime, API routes3000
apps/homepageThe marketing site3001
apps/docsThis site (Nextra)3002
apps/desktopThe Tauri shell that packages apps/app as the desktop app—
packages/screenplay-knobs, packages/screenplay-stateThe npm packages apps use to declare knobs and shared state—
packages/uiShared shadcn/ui components—

apps/app/CONTEXT.md is the glossary for the product’s domain language. Design decisions are recorded as ADRs in apps/app/docs/adr/. Read both before a larger change.

Running the product

There are two ways to run apps/app locally.

The same build switches the desktop app uses let you run the full product with no external services: embedded Postgres, a local Yjs server, and each chat’s code as a git worktree. You need a coding CLI such as Claude Code installed for the agent.

cd apps/app NEXT_PUBLIC_SCREENPLAY_LOCAL=1 NEXT_PUBLIC_YJS_HOST=local NEXT_PUBLIC_BASE_PATH= \ SANDBOX_BACKEND=local SCREENPLAY_DB=pglite BLOB_STORE=local-fs AGENT_ENGINE=external \ ENCRYPTION_KEY=$(openssl rand -hex 32) TERMINAL_AUTH_SECRET=$(openssl rand -hex 32) \ pnpm dev

Open http://localhost:3000. Data lives under apps/app (.pglite/, .data/yjs/, .screenplay/blobs/), and worktrees go under ~/.screenplay/.

pnpm dev at the repository root starts every app through Turborepo. Run it inside apps/app to start just the product.

Commands

From the repository root (runs across all workspaces via Turborepo):

pnpm dev # dev servers pnpm build # production builds (apps/app runs its migrations first) pnpm lint # ESLint pnpm typecheck # tsc --noEmit pnpm format # Prettier pnpm test # Vitest

In apps/app, pnpm test:watch runs Vitest in watch mode. Database helpers:

pnpm db:generate # generate a migration from schema changes (commit it) pnpm db:migrate # apply migrations to $DATABASE_URL pnpm db:push # sync the schema without a migration (throwaway dev only) pnpm db:studio # open Drizzle Studio

See Database for the migration workflow.

Desktop app

pnpm --filter desktop build:sidecar # build apps/app as the bundled sidecar pnpm --filter desktop dev # run the Tauri app pnpm --filter desktop build # package Screenplay.app

This needs the Rust and Tauri toolchains. See Desktop app.

Releasing the desktop app

Releases are cut on a Mac with one command:

pnpm --filter desktop release patch # or minor, major, or an exact X.Y.Z

It bumps the version, builds a signed and notarized .dmg, checks it with Gatekeeper, then commits the bump, tags desktop-v<version> and publishes a GitHub Release with the .dmg attached, once under its version and once as Screenplay.dmg, the name the homepage’s Download buttons link to. Nothing is tagged or published unless the build verifies.

The Release’s notes are a short changelog for people using the app, not a list of commits. Before the build, the script collects the pull requests merged since the last release that change the app (skipping the homepage, docs, tests and CI), and asks claude -p to write them up as New, Improved and Fixed. It shows the draft so you can accept it, edit it in $EDITOR, or stop. Without Claude Code installed, the draft lists the pull request titles. To write the notes yourself, pass a Markdown file with --notes notes.md.

Releasing needs Node 23 or later, the gh CLI signed in, a clean working tree, and the Apple signing keys in apps/desktop/.env.release (copy .env.release.example).

Screenshots for design review

Design-polish tickets ship before/after screenshots on the PR. The screenshot harness boots the local build against a seeded fixture world and captures a named list of screens in light and dark:

pnpm --filter app screenshots:boot # a browsable, fully-populated local build pnpm --filter app screenshots:shots --screens canvas # named screens, both themes

The harness README covers the before/after workflow, how to add a screen, and what the local build leaves out.

Writing docs

These docs live in apps/docs/content as MDX. Run the site with pnpm --filter docs dev, then open http://localhost:3002/docs.

Screenshots are framed WebP images in apps/docs/public/screenshots, in a light and a dark variant (name.light.webp, name.dark.webp). Embed one with:

<Screenshot name="hero" alt="Describe what the screenshot shows" />

The component shows the variant that matches the reader’s theme. Put two related shots side by side (they stack on a phone) with:

<ScreenshotRow> <Screenshot name="frame-ask" caption="Just drawn" alt="…" /> <Screenshot name="frame-unanswered" caption="Skipped" alt="…" /> </ScreenshotRow>

Keep full-window shots for the Introduction and Core concepts, where the whole layout is the point. Everywhere else, crop to the part the section is about. Every screenshot is generated: run pnpm --filter docs screenshots to regenerate them after a UI change, and see Docs screenshots to add one.

Issues

Bugs and feature requests are tracked in GitHub issues.

Last updated on