Skip to content

Contributing

Applies to: Dispatch v1.0.0, last verified 2026-04-05

Dispatch is a fair-code project maintained by The Pixel Labs. Contributions are welcome — bug reports, documentation improvements, and pull requests.

  • Read the How It Works and Architecture pages to understand the system before making changes.
  • Search open issues and open PRs before filing a new one.
  • For significant changes, open an issue first to discuss the approach. This avoids wasted effort on PRs that won’t be merged.
  • Bug reports and feature requests: GitHub Issues
  • Questions and design discussions: open an issue with the question label, or use the Discussions tab if available on the repository
  • Security vulnerabilities: do not open a public issue. See SECURITY.md for responsible disclosure instructions.

Open an issue on GitHub with:

  1. Dispatch version (npm list | grep dispatch or the version in package.json)
  2. Node.js version (node --version)
  3. OS and shell
  4. Steps to reproduce (exact commands)
  5. What you expected vs. what happened
  6. Relevant log output (run the server with LOG_LEVEL=debug npm start to increase verbosity)

Never include credentials, API keys, or .env contents in an issue. Scrub any paths that reveal personal information.

Use the following prefixes:

feat/short-description
fix/what-is-broken
docs/what-changed
refactor/what-changed

This is not just a convention — Dispatch uses branch names to link git sessions to epics. When an agent’s branch name matches an epic slug (e.g. feat/user-auth links to the user-auth epic), the Sessions panel shows that relationship automatically. Consistent branch naming keeps the dashboard useful.

Terminal window
git clone https://github.com/thepixelabs/dispatch.git
cd dispatch
npm install
Terminal window
npm run dev

This starts the Express server and the Vite dev server concurrently. The dashboard is at http://localhost:5173 with hot reload. Server restarts on TypeScript changes via tsx watch.

The codebase has three main areas:

AreaPathLanguage
Backend serverserver/index.tsTypeScript
Frontend dashboardsrc/TypeScript + React
CLIcli.jsNode.js (plain JS)
Docs sitedocs-site/src/Astro + MDX
Terminal window
npm test # full suite (vitest)
npm run test:unit # parser + state-builder + activity-feed + reducer only

All tests must pass before you open a PR. If you changed server parsing logic, run the full suite. If your change is frontend-only, test:unit is usually enough locally — CI runs everything.

Terminal window
npm run build

This runs tsc on the server and vite build on the frontend. Fix any TypeScript errors before opening a PR.

  • Register a project with node cli.js add /some/path --name "Test"
  • Confirm the dashboard shows it at http://localhost:4242 (production) or http://localhost:5173 (dev)
  • Keep PRs focused — one logical change per PR
  • Write a clear description: what changed, why, and how to verify it
  • Reference the GitHub issue number if one exists (Fixes #123)
  • All CI checks must pass before merge

The docs site lives in docs-site/. It uses Astro Starlight.

Terminal window
cd docs-site
npm install
npm run dev

The docs dev server is at http://localhost:4321. Pages are in docs-site/src/content/docs/. Edit .md or .mdx files there.

When adding a new page, also add it to docs-site/astro.config.mjs under the sidebar array so it appears in the navigation.

Writing style:

  • State the concrete behavior, not the intent. “The server polls every 2 seconds” not “the server tries to poll frequently.”
  • Use specific values. “30 seconds” not “a short time.”
  • Lead with what the user needs to know. Put implementation detail at the end or in a collapsible.
  • Do not use passive voice when you can name the subject.

Agent definitions live in agents/. Each file is a Markdown document with a YAML frontmatter name: field. The name must be unique across all agents and must match [a-zA-Z0-9_-]+.

If you add an agent, add it to the table in docs-site/src/content/docs/agents/catalog.md.

Dispatch watches the .tasks/ directory for coordination data written by agents. If you are contributing changes to how Dispatch reads or renders this data, or if you are writing agent personas that participate in multi-agent coordination, you need to understand the protocol.

The full specification is in .tasks/ Protocol Reference. The short version:

  • Each epic lives in .tasks/<epic-name>/ with a plan.md (YAML frontmatter for machine state, prose body for context) and an append-only execution-log.md.
  • Phases in plan.md have id, title, persona, and status (TODO, IN_PROGRESS, DONE, BLOCKED).
  • Agents claim phases by editing the frontmatter. Only one agent should claim a phase at a time.
  • Completed epics are moved to .tasks/.archive/ to keep the active view clean.

When testing changes to the server’s file watcher or state builder, register the sample-project/ directory included in the repo for a realistic .tasks/ directory without needing live agents.

Dispatch uses an adapter pattern for CLI agent execution. Each provider (Claude, Codex, Gemini, Copilot) has an adapter in server/adapters/ that implements the AgentAdapter interface from server/adapters/types.ts:

interface AgentAdapter {
readonly tool: AgentTool
readonly supportsSpawn: boolean
execute(ctx: SpawnContext): Promise<SpawnResult>
stream(ctx: SpawnContext, onChunk: (c: StreamChunk) => void): Promise<SpawnResult>
checkEnvironment(): Promise<{ ok: boolean; error?: string }>
}

To add a new provider:

  1. Add the tool type to the AgentTool union in server/types.ts.
  2. Add the binary to ALLOWED_CLI_BINARIES in server/adapters/constants.ts. This is a compile-time constant and the only source of binary names for spawn calls.
  3. Create the adapter at server/adapters/<provider>.ts. Implement execute() and stream() to spawn the CLI binary and parse its stdout. Set supportsSpawn = false if the provider has no CLI spawn interface.
  4. Register the adapter in server/adapters/index.ts by adding it to the ADAPTERS record.
  5. Add a session scanner if the provider stores session data in a custom format (see server/copilot-session-scanner.ts for an example). Register the scanner in server/multi-session-poller.ts.
  6. Write tests in server/__tests__/adapters/<provider>.test.ts. Mock child_process.spawn — never shell out to real CLI binaries in tests.

Key rules:

  • The adapter does not sanitize prompts. agent-spawner.ts applies sandboxedContent() before calling adapters (ADR-010).
  • Binary names come from ALLOWED_CLI_BINARIES only — never from user input or config (ADR-009).
  • DISPATCH_API_URL in the spawned env is always http://127.0.0.1:<PORT> — never from user config.
  • TypeScript: the project uses strict mode. No any unless it is unavoidable and documented.
  • React: functional components and hooks only. No class components.
  • Formatting: Prettier is not yet enforced but follow the existing style.
  • bash: use [[ ]] not [ ]. Quote all variables. Error early with set -e or explicit exit checks.

Follow Conventional Commits:

feat(server): add DISPATCH_PORT env var
fix(cli): default port not applied when PORT is unset
docs(installation): add pm2 and systemd persistence examples

Types: feat, fix, docs, refactor, test, chore.

By submitting a pull request you agree that your contribution is licensed under the PolyForm Shield 1.0.0 License that covers this project. at covers this project. covers this project. at covers this project. e 2.0 that covers this project. at covers this project. covers this project. at covers this project. project. at covers this project. covers this project. at covers this project.