From 0af28f11057c671e0258773dba5887f86ca37a83 Mon Sep 17 00:00:00 2001 From: Ayush Raj Date: Thu, 11 Jun 2026 14:11:49 +0530 Subject: [PATCH 1/4] style: refine landing page and add dedicated manifesto docs page --- package-lock.json | 29 ++- src/app/docs/docs.css | 83 ++++++++ src/app/docs/page.tsx | 260 +++++++++++++++++++++++++ src/components/landing/LandingPage.tsx | 8 +- 4 files changed, 369 insertions(+), 11 deletions(-) create mode 100644 src/app/docs/docs.css create mode 100644 src/app/docs/page.tsx diff --git a/package-lock.json b/package-lock.json index 815631c0..f3604728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2082,6 +2082,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.6.tgz", "integrity": "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", @@ -2310,6 +2311,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -2422,6 +2424,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -4522,13 +4525,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", - "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", - "dev": true, - "license": "MIT" - }, "node_modules/@stablelib/base64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", @@ -4627,6 +4623,7 @@ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.105.4.tgz", "integrity": "sha512-cEnx+k49knU+qdIP7rXwR6fqEXPHZs+74xFK1R0S8MgQ7v9tbePVdGxvO03n3bPympMdJWVLadARBfU4TgNHCQ==", "license": "MIT", + "peer": true, "dependencies": { "@supabase/auth-js": "2.105.4", "@supabase/functions-js": "2.105.4", @@ -4980,6 +4977,7 @@ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "pg-protocol": "*", @@ -5008,6 +5006,7 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -5019,6 +5018,7 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -5866,6 +5866,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6403,6 +6404,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -7741,6 +7743,7 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7926,6 +7929,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -9921,6 +9925,7 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -10699,6 +10704,7 @@ "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", "license": "MIT", + "peer": true, "dependencies": { "@next/env": "16.2.6", "@swc/helpers": "0.5.15", @@ -11276,6 +11282,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -11446,6 +11453,7 @@ "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.9.tgz", "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==", "license": "Unlicense", + "peer": true, "engines": { "node": ">=12" }, @@ -11509,6 +11517,7 @@ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11677,6 +11686,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -11689,6 +11699,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -13179,6 +13190,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -13832,6 +13844,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14080,6 +14093,7 @@ "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -14643,6 +14657,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", diff --git a/src/app/docs/docs.css b/src/app/docs/docs.css new file mode 100644 index 00000000..25c3c8ab --- /dev/null +++ b/src/app/docs/docs.css @@ -0,0 +1,83 @@ +/* Scoped to .landing-root .docs-container */ +.landing-root .docs-container { + max-width: 720px; + width: 100%; + margin: 0 auto; + padding: 160px 24px 100px; + position: relative; + z-index: 1; +} + +.landing-root .manifesto-badge { + color: var(--neon); + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.15em; + text-transform: uppercase; + margin-bottom: 20px; + display: inline-block; + font-family: var(--font-inter), 'Inter', sans-serif; +} + +.landing-root .docs-h1 { + font-size: clamp(2.5rem, 6vw, 4.2rem); + font-weight: 800; + letter-spacing: -0.04em; + line-height: 1.05; + margin-bottom: 24px; + font-family: var(--font-outfit), 'Outfit', sans-serif; + color: #fff; +} + +.landing-root .docs-sub { + font-size: clamp(1.05rem, 2vw, 1.2rem); + color: var(--text-secondary); + line-height: 1.65; + margin-bottom: 48px; +} + +.landing-root .docs-body { + font-family: var(--font-inter), 'Inter', sans-serif; + font-size: 1.02rem; + line-height: 1.8; + color: #b3b3b6; +} + +.landing-root .docs-body p { + margin-bottom: 28px; +} + +/* Styled Drop-Cap T */ +.landing-root .docs-body p:first-of-type::first-letter { + float: left; + font-size: 4.2rem; + line-height: 0.85; + font-family: var(--font-outfit), 'Outfit', sans-serif; + font-weight: 800; + margin-right: 12px; + margin-top: 4px; + color: #fff; +} + +/* Styled Quote Block with top/bottom neon green borders */ +.landing-root .docs-quote { + border-top: 1px solid rgba(0, 255, 135, 0.3); + border-bottom: 1px solid rgba(0, 255, 135, 0.3); + padding: 24px 0; + margin: 40px 0; + font-size: clamp(1.1rem, 2.2vw, 1.25rem); + line-height: 1.6; + font-style: italic; + color: var(--neon); + font-weight: 500; +} + +.landing-root .docs-h2 { + font-size: clamp(1.5rem, 4vw, 2rem); + font-weight: 700; + letter-spacing: -0.02em; + margin-top: 48px; + margin-bottom: 20px; + font-family: var(--font-outfit), 'Outfit', sans-serif; + color: #fff; +} diff --git a/src/app/docs/page.tsx b/src/app/docs/page.tsx new file mode 100644 index 00000000..97932f74 --- /dev/null +++ b/src/app/docs/page.tsx @@ -0,0 +1,260 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { Menu, X, ArrowRight } from 'lucide-react'; +import { getBrowserSupabase } from '@/lib/supabase/browser'; +import '@/app/landing.css'; +import './docs.css'; + +type NavUser = { name: string | null; email: string | null }; + +function isLocalSupabase(): boolean { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL ?? ''; + return url.includes('127.0.0.1') || url.includes('localhost'); +} + +export default function DocsPage() { + const [scrolled, setScrolled] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + const [user, setUser] = useState(null); + const [configured, setConfigured] = useState(true); + const localDev = isLocalSupabase(); + + useEffect(() => { + const fn = () => setScrolled(window.scrollY > 20); + fn(); + window.addEventListener('scroll', fn, { passive: true }); + return () => window.removeEventListener('scroll', fn); + }, []); + + useEffect(() => { + document.body.style.overflow = menuOpen ? 'hidden' : ''; + return () => { + document.body.style.overflow = ''; + }; + }, [menuOpen]); + + useEffect(() => { + const sb = getBrowserSupabase(); + if (!sb) { + setConfigured(false); + return; + } + sb.auth.getUser().then(({ data }) => { + if (!data.user) return setUser(null); + const u = data.user; + const meta = (u.user_metadata ?? {}) as Record; + const name = + (meta['name'] as string | undefined) ?? + (meta['user_name'] as string | undefined) ?? + null; + setUser({ name, email: u.email ?? null }); + }); + }, []); + + const handleLogin = () => { + const sb = getBrowserSupabase(); + if (!sb) return; + void sb.auth.signInWithOAuth({ + provider: 'github', + options: { + redirectTo: `${window.location.origin}/api/auth/callback?next=/dashboard`, + }, + }); + }; + + const handleLogout = async () => { + const sb = getBrowserSupabase(); + if (!sb) return; + await sb.auth.signOut(); + setUser(null); + }; + + const PrimaryCTA = ({ label, className = 'btn-neon' }: { label: string; className?: string }) => { + if (user) { + return ( + + {label} + + ); + } + if (localDev) { + return ( + + {label} + + ); + } + return ( + + ); + }; + + return ( +
+ {/* ambient glow behind content */} +
+ + {/* ════════ NAVBAR ════════════════════════════════════════════════════ */} + + + {/* mobile menu */} + {menuOpen && ( + <> +
setMenuOpen(false)} /> +
+ setMenuOpen(false)}>Platform + setMenuOpen(false)}>Features + setMenuOpen(false)}>Docs + setMenuOpen(false)}>Pricing +
+ {!configured ? ( + + Sign-in coming soon + + ) : user ? ( + <> + setMenuOpen(false)}> + Dashboard + + + + ) : ( + <> + {localDev ? ( + setMenuOpen(false)}>Login + ) : ( + + )} + + + )} +
+ + )} + + {/* ════════ MANIFESTO CONTENT ════════════════════════════════════════ */} +
+ Manifesto + +

+ The Maintainer's
Burden. +

+ +

+ Why we built MergeShip, and why the current state of continuous integration is fundamentally broken for high-velocity teams. +

+ +
+

+ There is a silent tax levied on every engineering team as they scale. It isn't paid in dollars, but in cycles. It is the insidious creeping slowness of the integration pipeline. What begins as a snappy, instantaneous feedback loop degenerates into a sluggish, fragile monster that demands constant feeding and watering by dedicated DevOps engineers. +

+ +

+ We accept this as normal. We tell ourselves that scale implies friction. But this is a failure of imagination. The tools we use to orchestrate our builds were designed for a different era—an era before serverless architectures, distributed caching, and deterministic execution environments became ubiquitous. +

+ +
+ "The most expensive resource in any software company is not compute. It is engineer attention. Every minute spent waiting for a build to pass is a minute where context is lost and momentum dies." +
+ +

+ Consider the lifecycle of a typical pull request in a mature codebase. The developer writes code, commits, and pushes. Then, they wait. They switch tasks, perhaps answering Slack messages or reviewing another PR. By the time the CI pipeline inevitably fails—often due to a flaky integration test or an outdated cache—the developer's mental context has completely shifted. The cost of context switching is monumental, yet it rarely appears on any balance sheet. +

+ +

The Illusion of Speed

+ +

+ Many modern CI providers boast about container startup times or raw compute power. But raw compute is only part of the equation. True velocity comes from intelligent orchestration—understanding the dependency graph of a monorepo so deeply that only the absolutely necessary artifacts are rebuilt. +

+ +

+ We built MergeShip because we were tired of paying the maintainer's tax. We wanted a system that felt less like a distant server running bash scripts, and more like a local compiler running directly on our development machines. A system that is deterministic, fiercely aggressive with caching, and totally transparent in its execution. +

+
+
+ + {/* ════════ FOOTER ═════════════════════════════════════════════════= */} + +
+ ); +} diff --git a/src/components/landing/LandingPage.tsx b/src/components/landing/LandingPage.tsx index fb175c08..5e395de8 100644 --- a/src/components/landing/LandingPage.tsx +++ b/src/components/landing/LandingPage.tsx @@ -206,7 +206,7 @@ export default function LandingPage() { @@ -249,7 +249,7 @@ export default function LandingPage() {
setMenuOpen(false)}>Platform setMenuOpen(false)}>Features - setMenuOpen(false)}>Docs + setMenuOpen(false)}>Docs setMenuOpen(false)}>Pricing
{!configured ? ( @@ -298,9 +298,9 @@ export default function LandingPage() { From 20af93dbee2abf5956cd48e17dcfd0aa40cdb27f Mon Sep 17 00:00:00 2001 From: Ayush Patel Date: Thu, 11 Jun 2026 18:59:56 +0530 Subject: [PATCH 2/4] fix(middleware): allow unauthenticated access to docs and onboarding routes --- src/middleware.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/middleware.ts b/src/middleware.ts index f741963b..a252cb2a 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -17,6 +17,8 @@ import { readSupabaseEnv } from '@/lib/supabase/env'; const GATE_BYPASS_PREFIXES = [ '/install', + '/onboarding', + '/docs', '/api/auth', '/api/webhooks', '/api/inngest', From 1ad1a97983052e6daec464c8f66a2b4020cf40a0 Mon Sep 17 00:00:00 2001 From: Ayush Raj Date: Tue, 23 Jun 2026 22:41:30 +0530 Subject: [PATCH 3/4] fix: default production LLM provider and resolve issue #393 --- src/app/(app)/issues/page.tsx | 21 +++++++----- src/app/(app)/leaderboard/page.tsx | 8 +++-- src/app/(app)/maintainer/community/page.tsx | 8 +++-- src/app/(app)/maintainer/issues/page.tsx | 10 +++--- src/app/(app)/maintainer/page.tsx | 34 ++++++++++--------- src/app/[handle]/page.tsx | 10 +++--- src/components/landing/LandingPage.tsx | 4 --- src/inngest/functions/mentor-post-comment.ts | 21 ++++++++---- src/inngest/functions/process-review-event.ts | 6 +++- src/lib/llm/router.test.ts | 12 ++++++- src/lib/llm/router.ts | 21 +++++++++++- 11 files changed, 103 insertions(+), 52 deletions(-) diff --git a/src/app/(app)/issues/page.tsx b/src/app/(app)/issues/page.tsx index eda5d2ce..9d4d5791 100644 --- a/src/app/(app)/issues/page.tsx +++ b/src/app/(app)/issues/page.tsx @@ -16,7 +16,12 @@ type SearchParams = { page?: string; }; -export default async function IssuesPage({ searchParams }: { searchParams: SearchParams }) { +export default async function IssuesPage({ + searchParams, +}: { + searchParams: Promise; +}) { + const resolvedSearchParams = await searchParams; const sb = await getServerSupabase(); if (!sb) return ( @@ -29,14 +34,14 @@ export default async function IssuesPage({ searchParams }: { searchParams: Searc if (!user) redirect('/'); const filters = { - search: searchParams.q, - state: (searchParams.state === 'closed' ? 'closed' : 'open') as 'open' | 'closed', - difficulty: (['E', 'M', 'H'].includes(searchParams.difficulty ?? '') - ? searchParams.difficulty + search: resolvedSearchParams.q, + state: (resolvedSearchParams.state === 'closed' ? 'closed' : 'open') as 'open' | 'closed', + difficulty: (['E', 'M', 'H'].includes(resolvedSearchParams.difficulty ?? '') + ? resolvedSearchParams.difficulty : undefined) as 'E' | 'M' | 'H' | undefined, - repo: searchParams.repo, - showClaimed: searchParams.claimed === 'true', - page: Math.max(1, parseInt(searchParams.page ?? '1') || 1), + repo: resolvedSearchParams.repo, + showClaimed: resolvedSearchParams.claimed === 'true', + page: Math.max(1, parseInt(resolvedSearchParams.page ?? '1') || 1), }; const service = getServiceSupabase(); diff --git a/src/app/(app)/leaderboard/page.tsx b/src/app/(app)/leaderboard/page.tsx index 2fa5a439..6384cd2f 100644 --- a/src/app/(app)/leaderboard/page.tsx +++ b/src/app/(app)/leaderboard/page.tsx @@ -7,10 +7,12 @@ export const dynamic = 'force-dynamic'; export default async function LeaderboardPage({ searchParams, }: { - searchParams: { scope?: string; id?: string }; + searchParams: Promise<{ scope?: string; id?: string }>; }) { - const scope = (searchParams.scope as 'global' | 'cohort' | 'language' | 'tag') ?? 'global'; - const scopeId = searchParams.id ?? null; + const resolvedSearchParams = await searchParams; + const scope = + (resolvedSearchParams.scope as 'global' | 'cohort' | 'language' | 'tag') ?? 'global'; + const scopeId = resolvedSearchParams.id ?? null; const result = await getLeaderboard(scope, scopeId, 50); return ( diff --git a/src/app/(app)/maintainer/community/page.tsx b/src/app/(app)/maintainer/community/page.tsx index 7ea56b86..72541705 100644 --- a/src/app/(app)/maintainer/community/page.tsx +++ b/src/app/(app)/maintainer/community/page.tsx @@ -16,8 +16,9 @@ export const dynamic = 'force-dynamic'; export default async function CommunityPage({ searchParams, }: { - searchParams: { install?: string }; + searchParams: Promise<{ install?: string }>; }) { + const resolvedSearchParams = await searchParams; const sb = await getServerSupabase(); if (!sb) return null; const { @@ -31,8 +32,9 @@ export default async function CommunityPage({ if (installs.length === 0) redirect('/maintainer'); const installId = - searchParams.install && installs.find((i) => i.installationId === Number(searchParams.install)) - ? Number(searchParams.install) + resolvedSearchParams.install && + installs.find((i) => i.installationId === Number(resolvedSearchParams.install)) + ? Number(resolvedSearchParams.install) : installs[0]!.installationId; const linksRes = await getCommunityLinks(installId); diff --git a/src/app/(app)/maintainer/issues/page.tsx b/src/app/(app)/maintainer/issues/page.tsx index 70751297..baaee3d0 100644 --- a/src/app/(app)/maintainer/issues/page.tsx +++ b/src/app/(app)/maintainer/issues/page.tsx @@ -32,8 +32,9 @@ const BUCKET_COLOR: Record = { export default async function MaintainerIssuesPage({ searchParams, }: { - searchParams: { install?: string; bucket?: string }; + searchParams: Promise<{ install?: string; bucket?: string }>; }) { + const resolvedSearchParams = await searchParams; const sb = await getServerSupabase(); if (!sb) return ; const { @@ -52,13 +53,14 @@ export default async function MaintainerIssuesPage({ } const activeInstallId = - searchParams.install && installs.find((i) => i.installationId === Number(searchParams.install)) - ? Number(searchParams.install) + resolvedSearchParams.install && + installs.find((i) => i.installationId === Number(resolvedSearchParams.install)) + ? Number(resolvedSearchParams.install) : installs[0]!.installationId; const activeInstall = installs.find((i) => i.installationId === activeInstallId)!; - const requestedBuckets = (searchParams.bucket ?? '') + const requestedBuckets = (resolvedSearchParams.bucket ?? '') .split(',') .filter((b): b is IssueTriageBucket => ALL_BUCKETS.includes(b as IssueTriageBucket)); const buckets: IssueTriageBucket[] = diff --git a/src/app/(app)/maintainer/page.tsx b/src/app/(app)/maintainer/page.tsx index f1be2861..ef2ca2c8 100644 --- a/src/app/(app)/maintainer/page.tsx +++ b/src/app/(app)/maintainer/page.tsx @@ -36,8 +36,9 @@ const TIER_LABEL: Record<'open' | 'closed' | 'merged', string> = { export default async function MaintainerPage({ searchParams, }: { - searchParams: { install?: string; state?: string; verified?: string }; + searchParams: Promise<{ install?: string; state?: string; verified?: string }>; }) { + const resolvedSearchParams = await searchParams; const sb = await getServerSupabase(); if (!sb) { return ; @@ -58,21 +59,22 @@ export default async function MaintainerPage({ } const activeInstallId = - searchParams.install && installs.find((i) => i.installationId === Number(searchParams.install)) - ? Number(searchParams.install) + resolvedSearchParams.install && + installs.find((i) => i.installationId === Number(resolvedSearchParams.install)) + ? Number(resolvedSearchParams.install) : installs[0]!.installationId; const activeInstall = installs.find((i) => i.installationId === activeInstallId)!; const filters: { state?: ('open' | 'closed' | 'merged')[]; mentorVerified?: 'yes' | 'no' } = {}; - if (searchParams.state) { - const parts = searchParams.state + if (resolvedSearchParams.state) { + const parts = resolvedSearchParams.state .split(',') .filter((s) => ['open', 'closed', 'merged'].includes(s)) as ('open' | 'closed' | 'merged')[]; if (parts.length > 0) filters.state = parts; } - if (searchParams.verified === 'yes' || searchParams.verified === 'no') { - filters.mentorVerified = searchParams.verified; + if (resolvedSearchParams.verified === 'yes' || resolvedSearchParams.verified === 'no') { + filters.mentorVerified = resolvedSearchParams.verified; } if (!filters.state) filters.state = ['open']; // default @@ -128,34 +130,34 @@ export default async function MaintainerPage({
|
diff --git a/src/app/[handle]/page.tsx b/src/app/[handle]/page.tsx index 55863f19..b2271808 100644 --- a/src/app/[handle]/page.tsx +++ b/src/app/[handle]/page.tsx @@ -348,8 +348,9 @@ const EVENT_DOT: Record = { const DIFFICULTY_LABEL: Record = { E: 'L1', M: 'L2', H: 'L3' }; -export async function generateMetadata({ params }: { params: { handle: string } }) { - const handle = decodeURIComponent(params.handle).replace(/^@/, ''); +export async function generateMetadata({ params }: { params: Promise<{ handle: string }> }) { + const resolvedParams = await params; + const handle = decodeURIComponent(resolvedParams.handle).replace(/^@/, ''); const profile = await loadProfileData(handle); @@ -371,8 +372,9 @@ export async function generateMetadata({ params }: { params: { handle: string } }; } -export default async function PublicProfile({ params }: { params: { handle: string } }) { - const handle = decodeURIComponent(params.handle).replace(/^@/, ''); +export default async function PublicProfile({ params }: { params: Promise<{ handle: string }> }) { + const resolvedParams = await params; + const handle = decodeURIComponent(resolvedParams.handle).replace(/^@/, ''); const profile = await loadProfileData(handle); if (!profile) notFound(); diff --git a/src/components/landing/LandingPage.tsx b/src/components/landing/LandingPage.tsx index 5e395de8..35c35dd2 100644 --- a/src/components/landing/LandingPage.tsx +++ b/src/components/landing/LandingPage.tsx @@ -283,10 +283,6 @@ export default function LandingPage() { {/* ════════ HERO ═════════════════════════════════════════════════════ */}
-
- NOW IN OPEN BETA -
-

Open source,
done right.

diff --git a/src/inngest/functions/mentor-post-comment.ts b/src/inngest/functions/mentor-post-comment.ts index 208bb7ce..7f0f6177 100644 --- a/src/inngest/functions/mentor-post-comment.ts +++ b/src/inngest/functions/mentor-post-comment.ts @@ -17,14 +17,20 @@ import { buildMentorCommentBody, decideMentorCommentAction } from '@/lib/maintai * - Skips entirely on closed/merged/draft PRs */ -type Event = { data: { prId: number; reviewerId: string } }; +type Event = { + data: { + prId: number; + reviewerId: string; + previousReviewerId?: string | null; + }; +}; export const mentorPostComment = inngest.createFunction( { id: 'mentor-post-comment', concurrency: { key: 'event.data.prId', limit: 1 } }, { event: 'mentor/post-comment' }, async ({ event, step }) => { return await step.run('post-or-update', async () => { - const { prId, reviewerId } = (event as Event).data; + const { prId, reviewerId, previousReviewerId } = (event as Event).data; const sb = getServiceSupabase(); if (!sb) return { skipped: true, reason: 'no_service_role' }; @@ -43,15 +49,16 @@ export const mentorPostComment = inngest.createFunction( .maybeSingle(); if (!reviewer) return { skipped: true, reason: 'reviewer_not_found' }; - // Look up the existing mentor level (whoever is on the row right now — - // could be the same reviewer if this is a replay, or an older one if a - // higher-level reviewer just landed). + // Look up the existing mentor level. We prefer the previousReviewerId carried + // in the event payload to avoid reading the already-overwritten row. + const oldMentorId = + previousReviewerId !== undefined ? previousReviewerId : pr.mentor_reviewer_id; let existingMentorLevel: number | null = null; - if (pr.mentor_reviewer_id) { + if (oldMentorId) { const { data: m } = await sb .from('profiles') .select('level') - .eq('id', pr.mentor_reviewer_id) + .eq('id', oldMentorId) .maybeSingle(); existingMentorLevel = m?.level ?? null; } diff --git a/src/inngest/functions/process-review-event.ts b/src/inngest/functions/process-review-event.ts index a8dec0c5..8afd2306 100644 --- a/src/inngest/functions/process-review-event.ts +++ b/src/inngest/functions/process-review-event.ts @@ -227,7 +227,11 @@ async function upsertReviewRow(payload: ReviewPayload): Promise { // here can't roll back the verified flag we just set. await inngest.send({ name: 'mentor/post-comment', - data: { prId: prRow.id, reviewerId: reviewer.id }, + data: { + prId: prRow.id, + reviewerId: reviewer.id, + previousReviewerId: prRow.mentor_reviewer_id, + }, }); } } diff --git a/src/lib/llm/router.test.ts b/src/lib/llm/router.test.ts index 91904a9f..c6faaf4a 100644 --- a/src/lib/llm/router.test.ts +++ b/src/lib/llm/router.test.ts @@ -6,7 +6,10 @@ beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2026-05-12T00:00:00Z')); }); -afterEach(() => vi.useRealTimers()); +afterEach(() => { + __setLlmProvider(null); + vi.useRealTimers(); +}); const schema = z.object({ ok: z.boolean(), answer: z.string() }); @@ -69,4 +72,11 @@ describe('llmCall', () => { expect(r.ok).toBe(true); if (r.ok) expect(r.data.answer).toBe('yo'); }); + + it('falls back to default provider or returns unavailable when override is cleared', async () => { + __setLlmProvider(null); + const r = await llmCall({ prompt: 'noop', schema }); + expect(r.ok).toBe(false); + if (!r.ok) expect(r.error.code).toBe('llm_unavailable'); + }); }); diff --git a/src/lib/llm/router.ts b/src/lib/llm/router.ts index 33cbcaf6..ab6137be 100644 --- a/src/lib/llm/router.ts +++ b/src/lib/llm/router.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import Groq from 'groq-sdk'; import type { Result } from '../result'; import { ok, err } from '../result'; @@ -25,6 +26,24 @@ type LlmCallArgs = { schema: z.ZodType; }; +const groqApiKey = process.env.GROQ_API_KEY; + +const groqProvider: LlmProvider = { + name: 'groq', + complete: async (prompt: string) => { + if (!groqApiKey) { + throw new Error('GROQ_API_KEY is not set'); + } + const groq = new Groq({ apiKey: groqApiKey }); + const completion = await groq.chat.completions.create({ + messages: [{ role: 'user', content: prompt }], + model: 'llama3-8b-8192', + }); + return completion.choices[0]?.message?.content ?? ''; + }, + isHealthy: () => !!groqApiKey, +}; + let providerOverride: LlmProvider | null = null; export function __setLlmProvider(p: LlmProvider | null): void { @@ -32,7 +51,7 @@ export function __setLlmProvider(p: LlmProvider | null): void { } function pickProvider(): LlmProvider | null { - return providerOverride; + return providerOverride || (groqApiKey ? groqProvider : null); } function extractJson(raw: string): string { From b9ab596b703f4924b7763fac11cfdf246a38b606 Mon Sep 17 00:00:00 2001 From: Ayush Raj Date: Wed, 24 Jun 2026 00:41:19 +0530 Subject: [PATCH 4/4] fix(landing): restore beta-pill and clean peer true noise in lockfile --- package-lock.json | 22 ---------------------- src/components/landing/LandingPage.tsx | 4 ++++ 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41e817a2..6dbf226b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2078,7 +2078,6 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.6.tgz", "integrity": "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", @@ -2307,7 +2306,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -2421,7 +2419,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -4643,7 +4640,6 @@ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.105.4.tgz", "integrity": "sha512-cEnx+k49knU+qdIP7rXwR6fqEXPHZs+74xFK1R0S8MgQ7v9tbePVdGxvO03n3bPympMdJWVLadARBfU4TgNHCQ==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/auth-js": "2.105.4", "@supabase/functions-js": "2.105.4", @@ -4997,7 +4993,6 @@ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "pg-protocol": "*", @@ -5026,7 +5021,6 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -5038,7 +5032,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -5884,7 +5877,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6421,7 +6413,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -7706,7 +7697,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7892,7 +7882,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -9883,7 +9872,6 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -10670,7 +10658,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "16.2.6", "@swc/helpers": "0.5.15", @@ -11245,7 +11232,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -11416,7 +11402,6 @@ "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.9.tgz", "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==", "license": "Unlicense", - "peer": true, "engines": { "node": ">=12" }, @@ -11480,7 +11465,6 @@ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11648,7 +11632,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -11661,7 +11644,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -13183,7 +13165,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13404,7 +13385,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13653,7 +13633,6 @@ "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -13783,7 +13762,6 @@ "integrity": "sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.6", diff --git a/src/components/landing/LandingPage.tsx b/src/components/landing/LandingPage.tsx index d7ff34c0..003ead75 100644 --- a/src/components/landing/LandingPage.tsx +++ b/src/components/landing/LandingPage.tsx @@ -286,6 +286,10 @@ export default function LandingPage() { {/* ════════ HERO ═════════════════════════════════════════════════════ */}
+
+ NOW IN OPEN BETA +
+

Open source,
done right.