A structured web UI wrapping GitHub/GitLab as the source of truth: chat →
AI-drafted ticket → poller-computed kanban. Describe a feature or bug in a
chat UI, file it as an issue with one click, and claude-code-action builds
it on a runner and opens a PR. A poller turns issue/PR/check state into a live
board — spec → queued → building → ready to test → shipped — optionally
mirrored to a Slack channel, so you can test the preview and ship to
production without leaving one browser tab (or Slack). Dispatch
stores almost nothing itself: delete the local SQLite cache and it rebuilds
the whole board from the provider on the next poll.
- Frontend: React 18 + Vite + Tailwind v3 (dark),
http://localhost:5173 - Backend: Express on Node 20,
http://127.0.0.1:3001(localhost-only) - Storage: SQLite (
./data/dispatch.db) — disposable cache; deletable - Providers: GitHub (Octokit) and GitLab (gitbeaker), behind one adapter seam
- Repos — Dispatch lists every repo your token(s) can access; click
Track (zero typing) or paste a path/URL. Tracked repos cache their
description,
CLAUDE.md, README excerpt, and a depth-2 file tree (refreshed on demand, ≤6h). - Spec chat — converse with Claude (scoped to a repo, context injected) to refine an issue spec, then Generate ticket → edit → File ticket.
- Board — six columns derived from provider state: Spec, Queued, Building, Ready to test, Shipped, Blocked. A poller reconciles every 20s (active) / 5min (idle); columns, PR linkage, and checks are derived, never stored.
- Test — open the PR preview and per-check statuses from the card; Steer
by commenting
@claudeto re-trigger the build. - Ship — one-click merge (gated on green + mergeable), confirmation modal, then the issue auto-closes and the card reaches Shipped.
cp .env.example .env # fill in the keys below
npm install
npm run dev # starts backend (:3001) + Vite (:5173) togetherOpen http://localhost:5173. With a valid .env you'll get an empty board and a
working health check (footer shows DB + rate-limit status).
This runs Dispatch as a single local process with no auth — fine for one
operator on localhost. To deploy it for real (authenticated, on Cloud Run),
see DEPLOY.md.
| Key | Required | Purpose |
|---|---|---|
ANTHROPIC_API_KEY |
for spec chat | Anthropic Messages API (spec refinement + ticket JSON) |
ANTHROPIC_MODEL |
optional | Override the model (default claude-sonnet-4-6) |
DISPATCH_DAILY_BUDGET_USD |
optional | Cap Anthropic spend per UTC day. Unset = no cap. Once the day's spend reaches the cap, chat and ticket generation return 429 until 00:00 UTC. Requires ANTHROPIC_MODEL to be priced in server/anthropic/pricing.ts. |
GITHUB_TOKEN |
for GitHub repos | Fine-grained PAT (scopes below) |
GITLAB_TOKEN |
for GitLab repos | PAT with api scope |
GITLAB_HOST |
self-hosted GitLab | Base URL (defaults to https://gitlab.com) |
PORT |
optional | Backend port (default 3001) |
HOST / ALLOW_NONLOCAL |
optional | Bind host; non-local requires ALLOW_NONLOCAL=1 |
DISPATCH_PASSWORD |
optional | Shared-password gate (HTTP Basic Auth) for internet-reachable deploys |
SLACK_WEBHOOK_URL |
optional | Slack Incoming Webhook; mirrors the activity feed into a channel |
Keys are loaded server-side only, never sent to the browser, and redacted from logs and error messages.
Set SLACK_WEBHOOK_URL to a Slack Incoming Webhook and Dispatch posts each
activity event (issue filed, column changes, PR opened, steered, merged, skill
runs) to that webhook's channel. Create one at
https://api.slack.com/messaging/webhooks (new app → Incoming Webhooks →
add to a channel). It's one-way and best-effort — a Slack outage never blocks
Dispatch. Any existing hooks.slack.com/services/… webhook works; reusing one
just routes notifications to that same channel.
- Install
anthropics/claude-code-action@v1: run/install-github-appfrom Claude Code in the repo — this configures the GitHub App and theANTHROPIC_API_KEYsecret. - The workflow should trigger on
issues: [opened]andissue_comment: [created]filtered to@claude, with scoped permissions (contents: write,pull-requests: write,issues: write) and atimeout-minutes(default 30). - Create a fine-grained PAT with Issues (RW), Pull requests (RW), Contents
(R), Actions (R) on the target repos → set as
GITHUB_TOKEN.
Verify current docs at code.claude.com/docs/en/gitlab-ci-cd.
- Add the Claude job to
.gitlab-ci.ymlper the official setup, withANTHROPIC_API_KEYstored as a masked CI/CD variable.@claudementions in issues/MR threads trigger the job, which commits results back via MRs. - Create a PAT with
apiscope →GITLAB_TOKEN. For self-hosted instances, also setGITLAB_HOST.
- Configure PR/MR preview deployments on your deploy provider (recommended).
Dispatch consumes preview URLs and production deploy status — it never
creates environments. Set a per-repo preview-URL pattern (e.g.
https://myapp-pr-{n}.vercel.app) when tracking a repo; Dispatch prefers a live URL from deployment statuses when available.
| Command | What it does |
|---|---|
npm run dev |
Backend + Vite together |
npm run typecheck |
Type-check server and web |
npm run check:seam |
Assert no provider SDK imports outside server/providers/ |
npm run verify |
typecheck + seam guard |
The Git provider is the source of truth. Deleting data/dispatch.db and
restarting rebuilds all non-draft cards from the provider on the first poll —
only local drafts (unsent spec chats) are lost.
