An internal tool for engineering managers. Replaces the Figma boards, spreadsheets, personal docs, and manual GitHub spelunking with a single dashboard for project health, team composition, goal tracking, 1:1s, and delivery stats.
Engineering management tooling is fragmented. Project health lives in static Figma boards with no history. 1:1 notes are scattered across personal tools with no continuity when reports change managers. Goals are tracked inconsistently. Team composition is invisible beyond org charts. Delivery data requires manually checking GitHub.
Eng Hub puts it all in one place.
| Area | What it does |
|---|---|
| Project Home Pages | Per-project hub with description, team roster, health status, milestones, goals, and links to Figma, repos, docs |
| Health Tracking | 8-dimension assessments (Growth, Margin, Longevity, Client Sat, Eng/Product/Design Vibe) with notes, full history, and at-a-glance status |
| 1:1 Meeting Notes | Rich text notes by direct report, reusable templates, management chain visibility, and handoff when reports change managers |
| Goal Management | Hierarchical milestones and quarterly goals with key results, assignees, status, and target dates |
| Budget & Billets | Per-project budget tracking and contracted headcount (billets) with department, title, seniority level, and count |
| Team Arrangements | Drag-and-drop team editor, seniority composition bars, draft arrangements before committing, org chart per project |
| Delivery Insights | GitHub stats (commits, PRs, reviews, trends) with visual dashboards and resilient sync with exponential backoff |
| Contributor Comparison | Side-by-side comparison of selected contributors — commits, PRs merged, monthly trends, and AI-generated narrative summaries (via Anthropic Claude) |
| Performance Reviews | Per-person review packets with 4 scored dimensions (Core Competency, Teamwork, Innovation, Time Management), optional comments, PDF upload, trend chart, and computed averages |
| Skill Matrices | Per-person skill tags (e.g. React, Python, Machine Learning) with a shared global skill library and autocomplete — add skills in the edit sheet, displayed on each person's profile |
| People Directory | Searchable directory with profiles, manager hierarchies, department/title taxonomy, and manager change audit trail |
| Layer | Technology |
|---|---|
| Runtime | Bun |
| Framework | Next.js 16 (App Router), React 19 |
| API | tRPC 11 with Zod validation |
| Database | PostgreSQL via Supabase + Prisma |
| Auth | Clerk with management chain-based visibility |
| UI | Tailwind CSS 4 + shadcn/ui |
| AI | Vercel AI SDK + Anthropic Claude (optional — contributor comparison summaries) |
| Quality | Biome · Knip · Lefthook · Playwright E2E · Changesets |
apps/web → Next.js frontend
packages/api → tRPC routers and GitHub integration
packages/db → Prisma schema & client
packages/ui → Shared UI components (shadcn/ui)
bun install
cp apps/web/.env.example apps/web/.env.local # fill in your keys
bun dev # start dev serverSee Environment file layout for detailed configuration.
- Copy the example env and fill in your keys:
cp apps/web/.env.example apps/web/.env.local
- Add your database URLs to
apps/web/.env.local(pooled + direct) andpackages/db/.env(same two URLs). - Generate the Prisma client and start the dev server:
cd packages/db && bun run db:generate bun dev
The app runs at http://localhost:3000 with Turbopack HMR.
For fully offline development with seeded test data:
- Start the local Supabase stack (PostgreSQL, Auth, Storage, etc.):
bunx supabase start
- Switch your environment to local:
cd apps/web && bun run env:local
- Push the Prisma schema to the local database:
cd packages/db && bun run db:push
- (Optional) Reset the local database to re-apply migrations and seed data:
cd packages/db && bun run db:reset
- Start the dev server:
bun dev
The local Supabase database runs on localhost:54322 with seed data from supabase/seed.sql. Auth still goes through Clerk cloud, so you need valid Clerk dev keys in apps/web/.env.local.local.
Note:
SKIP_ENV_VALIDATION=1is set in the local env file so the app starts even without optional services (UploadThing, QStash, GitHub).
When you open a PR, Supabase automatically creates an isolated preview database from supabase/migrations/ and applies supabase/seed.sql. Vercel deploys a preview of the app with the Supabase-provided env vars injected automatically.
To point your local dev server at a preview branch database:
- Go to Supabase dashboard → your project → the preview branch → Settings → Database → Connection string
- Copy the pooled (port 6543) and direct (port 5432) connection strings
- Paste them into
apps/web/.env.local.previewandpackages/db/.env.preview - Switch your local environment:
cd apps/web && bun run env:preview
- Start the dev server:
bun dev
The preview database has seed data (projects, people, teams) so you can develop and run E2E tests locally against real data.
To switch back to the production database:
cd apps/web && bun run env:prodThis copies apps/web/.env.local.prod → .env.local and packages/db/.env.prod → packages/db/.env.
Tip: The toggle scripts update both the web app env and the Prisma CLI env, so
prisma db pushandprisma studiowill also point at the correct database.
apps/web/
.env ← shared keys (Clerk, GitHub, etc.) — gitignored
.env.local ← DB URLs for current session — gitignored, swapped by toggle scripts
.env.local.local ← local Supabase DB URLs — committed
.env.local.prod ← production DB URLs — gitignored
.env.local.preview ← preview branch DB URLs — gitignored
.env.example ← template with all required keys — committed
packages/db/
.env ← DB URLs for Prisma CLI — gitignored, swapped by toggle scripts
.env.local-template ← local Supabase DB URLs — committed
.env.prod ← production DB URLs — gitignored
.env.preview ← preview branch DB URLs — gitignored
The project uses Supabase for PostgreSQL with Prisma as the ORM. Schema changes go through Supabase migrations (not prisma migrate), and Prisma is used only for client generation.
bunx supabase login # authenticate with Supabase
bunx supabase link --project-ref <PROJECT_REF> # link to your Supabase project
bunx supabase db pull # pull baseline migration from production- Edit
packages/db/prisma/schema.prisma - Apply to local/dev DB:
cd packages/db && bunx prisma db push - Generate migration:
cd packages/db && bun run db:migrate:new <descriptive_name> - Verify:
bunx supabase db reset(replays all migrations + seed) - Generate Prisma client:
cd packages/db && bun run db:generate - Commit both
schema.prismaand the newsupabase/migrations/<timestamp>_<name>.sql
| Command | Description |
|---|---|
cd packages/db && bun run db:generate |
Regenerate Prisma client |
cd packages/db && bun run db:migrate:new <name> |
Generate a new migration from schema diff |
cd packages/db && bun run db:reset |
Drop & recreate DB from migrations + seed |
cd packages/db && bun run db:studio |
Open Prisma Studio |
bunx supabase db pull |
Pull remote schema as a migration |
bunx supabase db push |
Push local migrations to remote |
The project uses Supabase Branching + Vercel Previews for a fully automated PR-to-production pipeline. No manual database steps are needed.
When a PR is opened:
- Supabase creates an isolated preview database, runs all migrations from
supabase/migrations/, then appliessupabase/seed.sql - Vercel deploys a preview of the app with the Supabase-provided preview DB env vars injected automatically
- CI runs lint, typecheck, and unit tests; then E2E tests run against the Vercel preview URL
When a PR is merged to main:
- Supabase deletes the preview branch database
- Supabase applies any new migration files to the production database automatically
- Vercel deploys to production (with production DB env vars)
⚠️ Important: Production migrations come from the.sqlfiles insupabase/migrations/, not from Prisma'sdb push. Thedb pushcommand does a direct schema diff and is only safe for local development — never use it against production. Always generate a migration file (bun run db:migrate:new) and commit it with your PR.
PRs with no migration files (e.g. test-only or frontend-only changes) skip the database step entirely — the production DB is unchanged.
Unit tests use bun:test and live in __tests__/ directories alongside the code they test.
cd apps/web && bun run test # run all unit tests
cd apps/web && bun run test:watch # watch mode
cd packages/api && bun test # run API package tests directlyE2E tests use Playwright with Clerk Testing Tokens for authentication bypass. Tests run against seed data in supabase/seed.sql.
Note: Goals CRUD and manager-edit tests require
E2E_CLERK_USER_IDto be set (the Clerk user ID of the E2E test account). This linksperson-alicein the seed to the test user, enabling/me/goalsand manager permission tests. Without it, those tests are skipped. Set it in.env, as a GitHub Actions secret, and as a Vercel Preview environment variable.
Running locally against a preview branch (recommended):
- Switch to the preview database:
cd apps/web && bun run env:preview - Start the dev server:
bun dev - In a separate terminal, run the tests:
cd apps/web && bun run test:e2e
Other modes:
cd apps/web && bun run test:e2e # headless (starts dev server if needed)
cd apps/web && bun run test:e2e:ui # Playwright UI mode
cd apps/web && bun run test:e2e:headed # headed browser
cd apps/web && bun run test:e2e:debug # step-through debuggerRunning against a deployed preview URL:
PLAYWRIGHT_TEST_BASE_URL=https://your-preview.vercel.app \
cd apps/web && bun run test:e2eTests live in apps/web/e2e/ and import { test, expect } from ./helpers (which injects Clerk testing tokens).
In CI, the E2E job waits for the Vercel preview deployment, then runs tests against it.
Biome handles both linting and formatting across the entire monorepo.
bun run biome:check # check for lint + format issues
bun run biome:fix # auto-fix all issues
bun run format # format only
bun run --filter web lint # lint web app via turbo
bun run --filter web lint:fixKnip detects unused exports, files, and dependencies.
bun run knipLefthook manages git hooks. Hooks are installed automatically on bun install via the prepare script.
| Hook | What runs |
|---|---|
pre-commit |
Biome check + auto-fix on staged files only |
pre-push |
TypeScript typecheck, unit tests, and Knip (in parallel) |
To reinstall hooks manually:
bunx lefthook installbun run dev # start all workspaces
bun run --filter web dev # start web app only| Command | Description |
|---|---|
bun run --filter web lint |
Lint with Biome |
bun run --filter web lint:fix |
Lint + auto-fix |
bun run --filter web typecheck |
TypeScript type-check |
bun run --filter web test |
Run unit tests |
bun run --filter web test:e2e |
Run E2E tests |
bun run --filter web ci |
Lint + typecheck + test |
bun run knip |
Check for dead code and unused dependencies |
bun run format |
Format all files with Biome |
cd apps/web && bun run env:local |
Switch local env to local Supabase |
cd apps/web && bun run env:prod |
Switch local env to production DB |
cd apps/web && bun run env:preview |
Switch local env to preview branch DB |
cd packages/api && bun run sync-git-stats <projectId> <repoPath> |
Sync contributor stats from a local git clone |
bun changeset |
Create a changeset for the current PR |
bun changeset:version |
Apply changesets and bump versions |
For projects hosted on GitLab or other providers where the API isn't accessible, you can sync contributor stats from a local clone of the repository.
cd packages/api
bun run sync-git-stats <projectId> <repoPath>Example (jeric2o):
cd packages/api
bun run sync-git-stats cmlsmp63g0000itbru5wvt294 ~/Desktop/hypergiant/jeric2o development- Runs
git log --numstaton the local repo to extract per-commit author email, date, additions, and deletions - Queries the database for team members on the project and builds an email-to-username mapping
- Matches commit authors by email to team members (unmatched commits are logged and skipped)
- Aggregates into all-time and year-to-date stats with weekly trend data
- Writes results to
contributor_stats(full replace) and updates the sync timestamp
- The repo must be cloned locally and the path passed as the second argument
- Team members must exist on the project in the app
- Each team member's Person email must match their git commit email, or they need a
gitlabUsernameset - Run while pointing at the correct database (local, preview, or prod via
bun run env:prod)
The script prints a detailed mapping table and lists unmatched commit emails with their commit counts. If contributors are missing from the output, update the Person's email in the app to match their git config email.
The Stats page lets you select 2+ contributors and open a side-by-side comparison sheet showing commits, lines changed, PRs merged, monthly trends, and recent MRs.
| Project type | Data source | Works in production? |
|---|---|---|
| GitHub URL configured | GitHub REST + GraphQL APIs (same data as stats dashboards) | Yes |
| GitLab / local repo only | git log against a local clone (via repoPath) |
No — dev/local only |
GitHub-backed projects use the same GITHUB_TOKEN already configured for stats sync. No additional setup is needed.
When ANTHROPIC_API_KEY is set, the comparison sheet shows a "Generate Summary" button that streams a narrative analysis of the comparison data using Claude. The summary covers individual profiles, monthly trajectory, work style comparison, and key takeaways.
A "Custom Prompt" option lets you add instructions (e.g., "Focus on who improved the most over the last 3 months").
| Env var | Required? | Description |
|---|---|---|
ANTHROPIC_API_KEY |
Optional | Enables AI summary generation. Get one at console.anthropic.com. |
If the key is not set, the AI summary section is hidden — all other comparison features work without it.