Skip to content

Repository files navigation

Pulldog 🐕

The faithful watchdog for your pull requests. Live PR dashboard with sound alerts, SLA tracking, and light/dark mode — built with Vue 3, Tailwind CSS, and shadcn/ui.

LicenseVueTypeScriptTailwind


Features

  • Multi-repo dashboard — monitor open PRs across any number of GitHub repositories in one view
  • Live status — review state (open, approved, changes requested, draft), CI check results, assignees, and reviewers
  • SLA tracking — configurable warning and breach thresholds; rows highlight amber/red as PRs age past them
  • Comment heat — shows comment count per PR with a 🔥 when it crosses a configurable threshold
  • Granular notifications — independently toggle new PR sound, merge sound, close sound, merge confetti, and voice announcements
  • Custom per-author sounds — optionally load pr_open.mp3 / pr_merged.mp3 / pr_closed.mp3 from each author's pulldog-sounds GitHub repo
  • Voice announcements (optional) — OpenAI-powered TTS for new PR and merge events; bring your own key via Settings, or set it in the environment
  • 7-day summary bar — total open PRs, PRs opened, PRs merged, average lead time, and merge rate
  • Light / dark / system theme — persisted to localStorage, respects OS preference in system mode
  • Auto-refresh — configurable poll interval (15s to 30m); detects new, merged, and closed PRs and triggers notifications
  • Flexible filtering — by status, SLA state, repo, author, staleness (7d+), free-text search, and title-regex exclusions
  • Cleaner default views — optional "Hide Drafts" and "Hide Merged" behavior when "All" is selected
  • GitHub App OAuth — one-click "Connect with GitHub" via a Cloudflare Worker; no token copy-pasting required
  • Manual PAT fallback — direct token entry remains available when OAuth is not configured

Screenshots

Dark modeLight mode
(add your own)(add your own)

Prerequisites

  • Node.js v18 or later
  • Wrangler CLI for the auth worker (npm i -g wrangler)
  • A Cloudflare account (free tier is sufficient)
  • A GitHub App (see setup below)

GitHub App setup

A single GitHub App works for both development and production — you can register multiple callback URLs.

  1. Go to github.com → Settings → Developer settings → GitHub Apps → New GitHub App
  2. Set the following:
    • Homepage URL: https://pulldog.dev(or your domain)
    • Callback URLs: https://pulldog.dev and http://localhost:5173
    • Check "Request user authorization (OAuth) during installation"
  3. Under Permissions → Repository permissions, set:
    • Metadata: Read-only (required, auto-selected)
    • Pull requests: Read-only
  4. Note the App ID and Client ID, and generate a Client secret

Cloudflare Worker setup

The worker handles the GitHub token exchange server-side so your client secret never touches the browser.

Deploy to production

# Authenticate with Cloudflare
wrangler login
# Set production secrets (uses the GitHub App credentials)cd worker
wrangler secret put GITHUB_CLIENT_ID
wrangler secret put GITHUB_CLIENT_SECRET
# Deploy
wrangler deploy

This gives you a worker URL like https://pulldog-auth.<your-subdomain>.workers.dev.

Run locally

# Copy the example and fill in your GitHub App credentials
cp worker/.dev.vars.example worker/.dev.vars
# Start the local worker (runs at http://localhost:8787)cd worker && wrangler dev

Getting started

# 1. Clone the repository
git clone https://github.com/YOUR_USERNAME/pulldog.git
cd pulldog
# 2. Install dependencies
npm install
# 3. Configure environment variables
cp .env.example .env
# Edit .env and fill in VITE_GITHUB_CLIENT_ID (GitHub App client ID) and VITE_GITHUB_WORKER_URL# 4. Start the local worker in a separate terminal
cp worker/.dev.vars.example worker/.dev.vars
# Edit worker/.dev.vars with your GitHub App credentialscd worker && wrangler dev
# 5. Start the development server
npm run dev

Open http://localhost:5173 and click Connect with GitHub.

Configuration

Frontend — .env

Copy .env.example to get started:

# GitHub App Client IDVITE_GITHUB_CLIENT_ID=your_client_id# URL of your deployed Cloudflare WorkerVITE_GITHUB_WORKER_URL=https://pulldog-auth.<your-subdomain>.workers.dev# Override the OAuth redirect URI (defaults to window.location.origin)VITE_GITHUB_REDIRECT_URI=# Optional: bake a token directly into the bundle (skips the OAuth flow)VITE_GITHUB_TOKEN=# Hours until a PR row turns amber (SLA warning)VITE_SLA_WARNING_HOURS=24# Hours until a PR row turns red (SLA breach)VITE_SLA_BREACH_HOURS=72# Show 🔥 next to comment count when it reaches this numberVITE_COMMENT_FIRE_THRESHOLD=10# Poll interval in seconds (how often to refresh PRs, default: 60)VITE_POLL_INTERVAL_S=60# Show sound test buttons in the top bar (true | false)VITE_TEST_MODE=false# Optional: OpenAI API key for voice announcements.# Leave blank so each user supplies their own key under Settings > Authentication.# Warning: any value set here is inlined into the public JS bundle at build time.VITE_OPENAI_API_KEY=

For local development, override VITE_GITHUB_WORKER_URL to point at the local worker:

# .env.local (git-ignored, overrides .env in dev)VITE_GITHUB_CLIENT_ID=your_client_idVITE_GITHUB_WORKER_URL=http://localhost:8787

Note: Vite bakes VITE_* variables into the bundle at build time. Restart npm run dev after any change.

Worker — worker/.dev.vars

Copy worker/.dev.vars.example and fill in your GitHub App credentials:

GITHUB_CLIENT_ID=your_dev_client_idGITHUB_CLIENT_SECRET=your_dev_client_secret

Production secrets are stored in Cloudflare (via wrangler secret put) and never committed.

Custom sounds (quick guide)

Pulldog can play per-author custom sounds by loading audio files from GitHub.
When enabled, it looks for files in:

  • https://raw.githubusercontent.com/<github-username>/pulldog-sounds/main/pr_open.mp3
  • https://raw.githubusercontent.com/<github-username>/pulldog-sounds/main/pr_merged.mp3
  • https://raw.githubusercontent.com/<github-username>/pulldog-sounds/main/pr_closed.mp3

1) Create your sound repo

For each author you want custom sounds for, create a public repo named pulldog-sounds under that same GitHub account, then add:

  • pr_open.mp3 (played on new PR)
  • pr_merged.mp3 (played on merge)
  • pr_closed.mp3 (played when a PR is closed without merging)

2) Enable in Pulldog

Open Settings → Notifications and turn on:

  • New PR → Custom sound
  • Merge → Custom sound
  • Closed → Custom sound

3) Test

Set VITE_TEST_MODE=true in .env, restart npm run dev, and use the top test bar to trigger sample events.

If a custom file is missing or cannot be loaded, Pulldog automatically falls back to the default built-in sound.

Project structure

pulldog/
├── worker/
│ ├── index.js # Cloudflare Worker — GitHub token exchange
│ ├── wrangler.toml # Worker-only Cloudflare config
│ └── .dev.vars.example # Worker local secrets template
├── src/
│ ├── components/
│ │ ├── ui/ # shadcn-style primitive components
│ │ ├── SetupScreen.vue # OAuth + manual token onboarding flow
│ │ ├── SettingsDialog.vue
│ │ ├── SlaLegend.vue
│ │ ├── SummaryBar.vue
│ │ └── Topbar.vue
│ ├── composables/
│ │ ├── useAudio.ts # Web Audio API sounds
│ │ ├── useGithub.ts # GitHub REST API calls
│ │ ├── useGithubOAuth.ts # GitHub OAuth flow + callback handling
│ │ ├── usePersistedRepos.ts
│ │ ├── usePersistedToken.ts
│ │ ├── useSla.ts # SLA threshold logic
│ │ └── useTheme.ts # Light/dark/system theme
│ ├── types.ts
│ ├── App.vue
│ ├── base.css
│ ├── main.ts
│ └── vite-env.d.ts
├── .env.example # Frontend env template
├── vite.config.ts
└── package.json

Available scripts

CommandDescription
npm run devStart development server with hot-reload
npm run buildBuild for production (outputs to dist/)
npm run previewPreview the production build locally
npm run type-checkRun TypeScript type checking via vue-tsc
cd worker && wrangler devRun the auth worker locally on port 8787
cd worker && wrangler deployDeploy the worker to Cloudflare

Building for production

npm run build

The dist/ directory is a fully static build. Deploy it to Cloudflare Pages, Vercel, Netlify, or any static host.

Contributing

Contributions are welcome! Here's how to get involved:

  1. Fork the repository and create a branch from main
    git checkout -b feat/your-feature-name
  2. Make your changes and ensure the project builds and type-checks cleanly
    npm run type-check && npm run build
  3. Commit using a descriptive message
    git commit -m "feat: add X"
  4. Push your branch and open a Pull Request against main

Reporting issues

Open an issue and include:

  • What you expected to happen
  • What actually happened
  • Steps to reproduce
  • Browser and Node.js version

License

Distributed under the MIT License. See LICENSE for full text.

Kiosk / TV mode

Pulldog has a built-in fullscreen toggle (⛶ button in the top-right corner) that uses the browser Fullscreen API to hide all browser chrome — address bar, tabs, bookmarks — leaving only the dashboard.

Press Esc or click the button again to exit.

For a permanent TV kiosk setup, launch Chrome or Chromium directly into kiosk mode:

# Linux / Raspberry Pi
chromium-browser --kiosk --noerrdialogs --disable-infobars https://pulldog.dev
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--kiosk --noerrdialogs https://pulldog.dev
# Windows"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--kiosk --noerrdialogs https://pulldog.dev

Set VITE_GITHUB_TOKEN in .env before building to pre-seed a token so the dashboard connects automatically on boot without any interaction.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages