Docs

Contributing

Help improve Emdash - contribution guidelines and development setup

Thanks for your interest in contributing. We favor small, focused PRs with clear intent. This guide covers the local development setup, the commands that matter, and the conventions contributors should follow before opening a PR.

Quick Start

Prerequisites

  • Git
  • Node.js 24.14.0 from .nvmrc
  • [email protected]
  • Optional, but useful for integration work:
    • GitHub CLI (gh)
    • At least one supported coding agent CLI, see Providers
    • Docker, when working on SSH development infrastructure

Use the pinned toolchain where possible:

nvm use
corepack enable
pnpm --version

Get The Source

Fork the repository on GitHub, then clone your fork:

git clone https://github.com/<you>/emdash.git
cd emdash

Install

From the repo root:

pnpm install

This repository is a pnpm workspace. The Electron app is in apps/emdash-desktop/, and shared workspace packages live in packages/.

Start Development

For normal app development, run the full workspace dev command from the repo root:

pnpm run dev

The root dev command now does two things:

  1. Builds all packages under packages/.
  2. Starts package watch builds and the Electron desktop app in parallel.

Use this command when you are changing code in packages/ or when you want the same startup path a fresh contributor will use.

If you are only working inside apps/emdash-desktop/, you can run the Electron dev server directly:

cd apps/emdash-desktop
pnpm run dev

From apps/emdash-desktop/, pnpm run d is a convenience command that runs pnpm install and then starts pnpm run dev for the desktop app:

cd apps/emdash-desktop
pnpm run d

Important distinction:

  • pnpm run dev from the repo root starts the workspace package watchers and the app together.
  • pnpm run dev from apps/emdash-desktop/ starts only electron-vite dev for the desktop app.
  • If app code imports changed package output, prefer the root command so package dist/ files stay current.

Renderer changes usually hot reload. Main-process changes under apps/emdash-desktop/src/main/ may require restarting the Electron dev app.

Repository Layout

This is a pnpm workspace monorepo.

  • apps/emdash-desktop/ - Electron desktop app package
  • apps/emdash-desktop/src/main/ - Electron main process, RPC controllers, services, database, PTY, SSH, Git, GitHub, updates, and integrations
  • apps/emdash-desktop/src/preload/ - typed Electron preload bridge
  • apps/emdash-desktop/src/renderer/ - React renderer app
  • apps/emdash-desktop/src/shared/ - shared app IPC, provider, event, MCP, skills, and domain types
  • apps/emdash-desktop/drizzle/ - generated Drizzle migrations and metadata
  • apps/emdash-desktop/scripts/ - release, verification, and build scripts
  • packages/core/ - transport-agnostic core runtime primitives
  • packages/shared/ - shared workspace primitives
  • packages/ui/ - shared UI components and theme system
  • packages/plugins/ - plugin interfaces and helpers
  • agents/ - architecture, workflow, convention, integration, and risk docs

Root scripts are aggregate workspace scripts. Most app-specific commands live in apps/emdash-desktop/package.json.

Common Commands

Run these from the repo root unless noted.

pnpm run dev            # build packages, watch packages, and start the Electron app
pnpm run build          # build every workspace package
pnpm run format         # format with oxfmt
pnpm run format:check   # check formatting without writing
pnpm run lint           # lint with oxlint
pnpm run typecheck      # run TypeScript checks
pnpm run test           # run workspace tests

Useful app-local commands from apps/emdash-desktop/:

pnpm run d              # install dependencies, then start the desktop app
pnpm run dev            # start electron-vite dev for the desktop app only
pnpm run dev:debug      # start with debug logging
pnpm run dev:main       # watch the Electron main process
pnpm run dev:renderer   # watch the renderer
pnpm run build          # build the Electron app
pnpm run build:main     # build main process only
pnpm run build:renderer # build renderer only
pnpm run package        # build and package desktop artifacts
pnpm run rebuild        # rebuild native Electron dependencies
pnpm run reset          # clean app dependencies and reinstall

Useful package-local commands from a package under packages/:

pnpm run dev            # watch-build that package with tsdown
pnpm run build          # build that package with tsdown
pnpm run test
pnpm run typecheck

Local Validation

Before opening or merging a PR, run the local merge gate:

pnpm run format
pnpm run lint
pnpm run typecheck
pnpm run test

There are no pre-commit hooks. CI currently enforces:

pnpm run format:check
pnpm run typecheck
pnpm run lint

Tests are still expected locally even when a specific CI workflow does not run the full test suite.

Development Workflow

  1. Create a feature branch:
git checkout -b feat/<short-slug>
  1. Keep PRs small and focused.

Update docs when behavior changes. Include screenshots or short recordings for UI changes where they help reviewers understand the result.

  1. Run validation locally.

Use the full merge gate above for broad changes. For narrow work, it is fine to run focused tests while iterating, then run the full gate before opening or merging the PR.

  1. Commit using Conventional Commits:
fix(opencode): change initialPromptFlag from -p to --prompt for TUI
feat(docs): add changelog tab with GitHub releases integration
  1. Open a pull request.

Describe the change, the reason for it, and the validation you ran. Link related issues when relevant.

Code Style

  • Use TypeScript strict mode.
  • Use top-level import statements, not require().
  • Do not introduce npm or yarn lockfiles.
  • Use pnpm.
  • Format with oxfmt.
  • Lint with oxlint.
  • Keep lines near the configured printWidth of 100 characters.
  • Use 2 spaces, semicolons, single quotes in TypeScript, double quotes in JSX, LF endings, and trailing commas where valid in ES5.
  • Avoid any. If a boundary requires it, keep the escape local and document why.
  • Do not re-export as a shortcut. Import from the original source.

App Architecture Conventions

The app follows this high-level flow:

Renderer -> typed RPC client -> preload bridge -> Electron main -> controllers -> services

Main process:

  • RPC handlers live in src/main/core/*/controller.ts.
  • Controllers should delegate to imported operation or service functions.
  • Expected failures should use the Result<T, E> pattern from src/main/lib/result.ts.
  • Prefer execFile over exec.
  • Treat shell escaping, PTY spawning, SSH commands, and worktree paths as security-sensitive.
  • Preserve secret redaction in logging and telemetry code.

Renderer:

  • Feature UI lives under src/renderer/features/<feature>/.
  • Shared renderer primitives, stores, hooks, commands, PTY, Monaco, modal infrastructure, and UI live under src/renderer/lib/.
  • Renderer RPC calls go through rpc from src/renderer/lib/ipc.ts.
  • New modals must be registered in src/renderer/app/modal-registry.ts.
  • New views must be registered in src/renderer/app/view-registry.ts.
  • New commands should use src/renderer/lib/commands/registry.ts and view-level commandProvider hooks when possible.
  • Components use PascalCase; hooks use useX camelCase or an existing local pattern.

State and stores:

  • Access task managers through getTaskManagerStore(projectId), not project.taskManager.
  • Access mounted projects through asMounted(getProjectStore(id)).
  • Never use asProvisioned(...)! or asMounted(...)!; use explicit null checks.
  • State guards should check kind !== 'ready' rather than enumerate non-ready states.
  • Task selectors live in src/renderer/features/tasks/stores/task-selectors.ts.
  • Project selectors live in src/renderer/features/projects/stores/project-selectors.ts.

Database And Migrations

Development database paths use Electron app.getPath('userData').

  • macOS: ~/Library/Application Support/emdash-dev/emdash4.db
  • Linux: ~/.config/emdash-dev/emdash4.db
  • Windows: %APPDATA%\emdash-dev\emdash4.db

Use an isolated scratch database when working on schema or migration changes. From the repo root:

EMDASH_DB_FILE=/tmp/emdash-scratch.db pnpm run dev

For app-only development, change into apps/emdash-desktop/ first so this starts only electron-vite dev:

cd apps/emdash-desktop
EMDASH_DB_FILE=/tmp/emdash-scratch.db pnpm run dev

Reset dev databases from apps/emdash-desktop/:

pnpm run db:reset

Database rules:

  • Do not hand-edit numbered Drizzle migrations or drizzle/meta/.
  • Use pnpm run db:generate for new migrations.
  • Update fixtures and migration tests when schema behavior changes.
  • Run focused database validation from apps/emdash-desktop/ when relevant:
pnpm run db:setup
pnpm run db:fixtures
pnpm run test:migrations

Read agents/risky-areas/database.md before changing database internals.

Worktrees, PTY, SSH, And Providers

Emdash orchestrates coding agents in Git worktrees and PTY sessions. These areas are high impact.

  • Do not delete worktree folders manually unless you know the matching Git state. Prefer in-app cleanup or git worktree prune from the main repository.
  • Do not weaken shell quoting, spawn behavior, environment allowlists, or secret redaction.
  • PTY environment passthrough must use the allowlist in src/main/core/pty/pty-env.ts.
  • Provider changes may need updates to shared provider metadata, dependency detection, PTY behavior, hooks/plugins, renderer assumptions, and tests.

Read the relevant risk or integration doc before touching these areas:

  • agents/risky-areas/pty.md
  • agents/risky-areas/ssh.md
  • agents/integrations/providers.md
  • agents/integrations/mcp.md

Testing Notes

  • Unit tests use Vitest.
  • Main database integration tests run in the main-db Vitest project.
  • Migration tests run in the migrations project.
  • Fixture generation runs in the fixtures project.
  • Renderer browser tests use Playwright-backed @vitest/browser-playwright.
  • Main-process tests are colocated under src/main/core/**/*.test.ts.
  • Renderer unit tests live under src/renderer/tests/.
  • Renderer browser tests live under src/renderer/tests/browser/.
  • Integration-style tests create temporary repos and worktrees in os.tmpdir().

From apps/emdash-desktop/, the app test command is:

pnpm run test

It runs the app Vitest projects:

node, main-db, migrations, browser, scripts

Native Dependencies

After native dependency changes, rebuild Electron native modules from apps/emdash-desktop/:

pnpm run rebuild

This is especially relevant for better-sqlite3 and node-pty.

Docker SSH Development

When working on Docker-backed SSH development infrastructure, start it from apps/emdash-desktop/:

pnpm run run:docker-ssh

Read agents/workflows/remote-development.md and agents/risky-areas/ssh.md before making SSH behavior changes.

Issue Reports And Feature Requests

Use GitHub Issues. Include:

  • Operating system
  • Emdash version or commit SHA
  • Node and pnpm versions, if development-related
  • Steps to reproduce
  • Expected behavior
  • Actual behavior
  • Relevant logs, terminal output, or screenshots

Do not include secrets, tokens, private keys, local app databases, or private repository content in public issues.

Release Process For Maintainers

Do not dispatch release workflows, publish packages, or upload artifacts unless you are explicitly doing release work.

The app version lives in apps/emdash-desktop/package.json. For release version bumps, run these from apps/emdash-desktop/:

pnpm version patch
pnpm version minor
pnpm version major

This updates package.json and pnpm-lock.yaml, creates a version commit, and creates a tag.

Production releases are dispatched through GitHub Actions:

gh workflow run release-prod.yml --ref main -f arch=both

Canary releases are dispatched through:

gh workflow run release-canary.yml --ref main -f arch=both

Production releases publish artifacts to GitHub Releases as the primary update feed and Cloudflare R2 as fallback. Canary releases currently publish to R2 only.

Further Reading

  • agents/README.md
  • agents/quickstart.md
  • agents/architecture/overview.md
  • agents/architecture/main-process.md
  • agents/architecture/renderer.md
  • agents/conventions/ipc.md
  • agents/conventions/main-patterns.md
  • agents/conventions/renderer-patterns.md
  • agents/conventions/typescript.md
  • agents/workflows/testing.md
  • agents/workflows/worktrees.md
Last updated on June 19, 2026
Â