Thanks for your interest in contributing! We favor small, focused PRs and clear intent over big bangs. This guide explains how to get set up, the workflow we use, and a few project‑specific conventions.
Prerequisites
- Node.js 24.0.0+ (recommended: 24.14.0), pnpm 10.28.0+, and Git
- Optional (recommended for end‑to‑end testing):
- GitHub CLI (
brew install gh; thengh auth login) - At least one supported coding agent CLI (see docs for list)
- GitHub CLI (
Setup
# Fork this repo, then clone your fork
git clone https://github.com/<you>/emdash.git
cd emdash
# Use the correct Node.js version (if using nvm)
nvm use
# Quick start: install dependencies and run dev server
pnpm run d
# Or run separately:
pnpm install
pnpm run dev
# Format, lint, type check, and test
pnpm run format
pnpm run lint
pnpm run typecheck
pnpm run testTip: During development, the renderer hot‑reloads. Changes to the Electron main process (files in src/main) require a restart of the dev app.
src/main/– Electron main process, IPC handlers, services (Git, worktrees, PTY manager, DB, etc.)src/renderer/– React UI (Vite), hooks, components- Local database – SQLite file created under the OS userData folder (see "Local DB" below)
- Worktrees – Git worktrees are created outside your repo root in a sibling
worktrees/folder - Logs – Agent terminal output and app logs are written to the OS userData folder (not inside repos)
- Create a feature branch
git checkout -b feat/<short-slug>
- Make changes and keep PRs small and focused
- Prefer a series of small PRs over one large one.
- Include UI screenshots/GIFs when modifying the interface.
- Update docs (README or inline help) when behavior changes.
- Run checks locally
pnpm run format # Format code with Prettier (required)
pnpm run lint # ESLint
pnpm run typecheck # TypeScript type checking
pnpm run test # Vitest test suite
Pre-commit hooks run automatically via Husky + lint-staged. On each commit, staged files are auto-formatted with Prettier and linted with ESLint. Run the full local gate before opening or merging a PR.
If you need to skip the hook for a work-in-progress commit, use git commit --no-verify. The checks will still run in CI when you open a PR.
- Commit using Conventional Commits
feat:– new user‑facing capabilityfix:– bug fixchore:,refactor:,docs:,perf:,test:etc.
Examples
fix(opencode): change initialPromptFlag from -p to --prompt for TUI
feat(docs): add changelog tab with GitHub releases integration
- Open a Pull Request
- Describe the change, rationale, and testing steps.
- Link related Issues.
- Keep the PR title in Conventional Commit format if possible.
TypeScript + ESLint + Prettier
Pre-commit hooks handle formatting and linting automatically on staged files. For full-project checks you can run them manually:
pnpm run format-- format all files with Prettierpnpm run lint-- ESLint across all filespnpm run typecheck-- TypeScript type checking (whole project)pnpm run test-- run the test suite
Electron main (Node side)
- Prefer
execFileoverexecto avoid shell quoting issues. - Never write logs into Git worktrees. All logs belong in the Electron
userDatafolder. - Be conservative with console logging; noisy logs reduce signal. Use clear prefixes.
Git and worktrees
- The app creates worktrees in a sibling
../worktrees/folder. - Do not delete worktree folders from Finder/Explorer; if you need cleanup, use:
git worktree prune(from the main repo)- or the in‑app workspace removal
Renderer (React)
- Components live under
src/renderer/components; hooks undersrc/renderer/hooks. - Agent CLIs are embedded via terminal emulation (xterm.js) - each agent runs in its own PTY.
- Use existing UI primitives and Tailwind utility classes for consistency.
- Aim for accessible elements (labels,
aria-*where appropriate).
Local DB (SQLite)
- Location (Electron
app.getPath('userData')):- macOS:
~/Library/Application Support/emdash/emdash.db - Linux:
~/.config/emdash/emdash.db - Windows:
%APPDATA%\emdash\emdash.db
- macOS:
- Reset: quit the app, delete the file, relaunch (the schema is recreated).
- Use GitHub Issues. Include:
- OS, Node version
- Steps to reproduce
- Relevant logs (renderer console, terminal output)
- Screenshots/GIFs for UI issues
Use pnpm's built-in versioning to ensure consistency:
# For bug fixes (0.2.9 → 0.2.10)
pnpm version patch
# For new features (0.2.9 → 0.3.0)
pnpm version minor
# For breaking changes (0.2.9 → 1.0.0)
pnpm version majorThis automatically:
- Updates
package.jsonandpnpm-lock.yaml - Creates a git commit with the version number (e.g.,
"0.2.10") - Creates a git tag (e.g.,
v0.2.10)
Then push the commit and tag. Production release builds are dispatched from GitHub Actions.
The release pipeline is split across these GitHub Actions workflows:
Production Release (.github/workflows/release-prod.yml):
- Builds Linux, Windows, and macOS packages
- Signs Windows builds when Azure Trusted Signing secrets are configured
- Signs, verifies, notarizes, and staples macOS DMGs and ZIPs
- Uploads release artifacts to Cloudflare R2
Linux/Nix Build (.github/workflows/nix-build.yml):
- Computes the correct dependency hash from
pnpm-lock.yaml - Builds the x86_64-linux package via Nix flake
- Pushes build artifacts to Cachix and uploads the Nix artifact when available
Canary Release (.github/workflows/release-canary.yml):
- Builds Linux, Windows, and macOS packages with the canary config
- Publishes artifacts to the
v1-canaryR2 channel