SkillKitHub is a monorepo for the universal registry of AI agent workflows (Kits) and expert instruction sets (Skills). All agents welcome — Cursor, Claude, Codex, and more. It uses Turborepo to manage multiple apps and shared packages.
apps/web— Next.js 16 frontend, runs on port 5000apps/api— Fastify REST API backend, runs on port 8080
packages/schema(@kithub/schema) — Zod schemas and kit.md parserpackages/db(@kithub/db) — Drizzle ORM + Postgres clientpackages/sdk(@kithub/sdk) — TypeScript SDK for SkillKitHub APIpackages/ui(@repo/ui) — Shared React componentspackages/cli(@kithub/cli) — CLI toolpackages/mcp-server(@kithub/mcp-server) — MCP server
- "Start application" — Runs
turbo dev --filter=webon port 5000 (webview) - "API Server" — Runs
turbo dev --filter=apion port 8080 (console)
DATABASE_URL— PostgreSQL connection string (required for database features)SUPABASE_URL— Supabase project URL used by the API auth verifierSUPABASE_SECRET_KEY— Supabase secret/service-role key used by the API auth verifierNEXT_PUBLIC_SUPABASE_URL— Public Supabase URL used by the web appNEXT_PUBLIC_SUPABASE_ANON_KEY— Public Supabase browser key used by the web appNEXT_PUBLIC_API_URL— API base URL used by the frontendWEB_URL— Frontend URL for CORS and notification links (defaults to http://localhost:5000)OPENAI_API_KEY— (optional) enables semantic search and related-kits via OpenAItext-embedding-3-small. Falls back to keyword/tag matching when unset (one-time warning logged).
- npm (with npm workspaces)
- Node.js 20 required (Next.js 16 requires >= 20.9.0)
- Turborepo for task orchestration (using
streamUI mode for Replit compatibility)
Shared packages must be built before apps:
@kithub/schemaand@kithub/db(independent)@kithub/sdk(depends on schema)apps/apiandapps/web(depend on packages above)
npx turbo run build --filter=@kithub/schema --filter=@kithub/sdk --filter=@kithub/db- Dark theme:
--bg: #030304,--accent: #00e88f(green primary) - Multi-accent palette:
--accent-2: #7c5cff(violet),--accent-3: #22d3ee(cyan) for visual variety - Gradient tokens:
--gradient-accent,--gradient-text,--gradient-text-accent,--gradient-surface - Fonts: Inter (sans), JetBrains Mono (mono)
- Ambient color washes via
body::after(green/violet/cyan radial blooms) for depth - All design tokens in
apps/web/app/globals.css(~2200 lines)
- Hero:
.status-pill(live dot + indexed count), gradient.accent-wordin title,.hero-statsstrip (Kits Indexed / Curated Stacks / Agent Targets / Kit Spec — sourced only from accurate API counts plus static facts) - Stretched-link cards:
.kit-card-stretched+.kit-card-title-link::aftercovers the whole card; sibling.kit-publisher-linkhas higher z-index; decorative chips usepointer-events: noneso the whole card area still navigates - Reduced-motion media query disables animations/transitions for
prefers-reduced-motion: reduce - Section eyebrows:
.eyebrow+.eyebrow-num+.eyebrow-barfor orientation - Pill nav:
.nav-linkrounds on hover, active state is filled green pill - Kit cards: mono
.kit-id-eyebrow(publisher/slug), gradient left-edge on hover - Steps: numbered circles colored per-step (green/cyan/violet) with halo glow + gradient connector
- Section headers use gradient text via
--gradient-text - Feature cards lift on hover; icons get blur halo via
.feature-icon-styled - Collections:
.collection-card-featuredhas gradient top accent + tinted bg +.featured-pill - Terminal blocks: gradient bg + subtle green ring/glow
Global CSS classes in globals.css organized by section:
- Layout:
.container,.page-section,.page-narrow,.page-header,.page-header-row - Cards:
.glass-panel,.kit-card,.stat-card,.stat-grid - Forms:
.input,.input-code,.input-mono,.form-group,.form-hint - Buttons:
.btn,.btn-secondary,.btn-sm,.btn-full,.btn-link,.btn-back,.btn-danger - Modal:
.modal-overlay,.modal-content,.modal-actions - Steps:
.step-indicator,.step-bar,.step-panel,.step-heading,.step-description - Alerts:
.alert,.alert-error,.alert-warning,.alert-success,.alert-info - Results:
.result-centered,.result-icon,.result-title,.result-description - Skeletons:
.skeleton,.skeleton-card,.skeleton-stat,.skeleton-text - Scores:
.score-circle,.score-badge(with.high,.medium,.lowvariants) - Layout utils:
.centered-card,.flex-between,.flex-end,.grid-2col,.item-grid
- Toast system:
apps/web/app/components/Toast.tsx— React context + provider for notifications (success/error/warning/info). Wired in root layout via<ToastProvider>. UseuseToast()hook. - Skeleton loaders:
apps/web/app/components/Skeleton.tsx—<SkeletonCard>,<SkeletonStat>,<SkeletonText>for loading states. - Analytics drawer:
apps/web/app/components/Analytics.tsx— Per-kit analytics modal with SVG bar chart (30-day installs) and target breakdown. Used on Dashboard. - Markdown preview:
apps/web/app/components/MarkdownPreview.tsx— Lightweight live-rendered markdown preview (no external deps). Used in publish wizard Step 1.
- Skip-to-content link targeting
#main-content - Global
:focus-visiblering styling (2px accent outline) - ARIA labels on nav, form inputs, score badges
aria-hiddenon decorative icons--text-tertiaryadjusted to#7a7a8afor WCAG AA contrast
/— Homepage (hero, quick start, how it works, features)/auth— Auth (centered card, register/login toggle, verification)/publish— Publish kit (3-step wizard with progress bar, live markdown preview in Step 1). Supports?edit=<slug>for editing existing kits./dashboard— User dashboard (stats grid, owned kit list with Analytics/Edit/Unpublish actions). UsesGET /api/kits/minefor publisher-owned kits./registry— Registry listing with sort selector (newest/installs/score), pagination (20/page), and "Trending Kits" section (top 3 by installs). Cards show VerifiedBadge + Stars./registry/[slug]— Kit detail with Version History, Related Kits rail, Scan diff panel (added/removed/unchanged checks across releases), Ratings & reviews block (auth-gated submission), VerifiedBadge on publisher line, dynamic OG/Twitter meta./collections— Index of curated collections (cards with emoji, kit count, total installs, average stars)./collections/[slug]— Collection detail with kit list and Install Stack sidebar (client-sideInstallStack.tsxwith copy-to-clipboard for CLI one-liner + agent prompt + collapsible install URLs)./skills— Skills directory (card grid with search/filter, category badges, install counts)/skills/[slug]— Skill detail page (emoji, category, description, tags, OG/Twitter meta)/publishers/[slug]— Publisher profile page (agent name + VerifiedBadge, kit count, total installs, avg score, kit list)- 404 —
apps/web/app/not-found.tsx(styled 404 with gradient text)
Multi-column layout: brand description, Product links, Resources links, Community links (GitHub, Discord, Twitter). Bottom bar with copyright and tagline.
- Next.js dev/start scripts updated to use
-p 5000 -H 0.0.0.0for Replit proxy compatibility - Turbo UI changed from
tuitostream(TUI mode blocks in Replit's environment) - Upgraded from Node.js 18 to Node.js 20 (required by Next.js 16)
- Fixed TypeScript strict mode errors in
packages/schema/src/index.ts - Fixed
@fastify/jwttype conflicts inapps/api/src/middleware/auth.ts - Fixed DB SSL: Replit's internal Postgres (helium) doesn't use TLS —
isReplitHeliumcheck inpackages/db/src/index.tsand seed files - Fixed SQL
ANY()array syntax for postgres.js compatibility inbatchFetchLatestReleasesandbatchFetchLatestScores - Added
allowedDevOriginstoapps/web/next.config.jsfor Replit proxy HMR support - Fixed
scripts/post-merge.sh: updated deprecateddrizzle-kit push:pg→drizzle-kit push - Set env vars:
WEB_URL,NEXT_PUBLIC_API_URL,NEXT_PUBLIC_BASE_URL,NODE_ENV,PORT - Database schema pushed and seeded with sample kits + skills
Migration files are managed via Drizzle Kit and stored in packages/db/drizzle/.
- Generate migrations after changing the schema:
cd packages/db && npm run generate - Apply migrations to the database:
cd packages/db && npm run migrate(requiresDATABASE_URL) - Push schema directly (dev only):
cd packages/db && npm run push - Migration config:
packages/db/drizzle.config.ts
GET /api/kits— Public registry listing with sort/pagination (?sort=installs|score|newest,?page=1,?limit=20)- Discovery params:
?q=<text>&mode=keyword|semantic(semantic falls back to keyword whenOPENAI_API_KEYis missing) ?related_to=<slug>&limit=N— returns related kits via embedding cosine similarity (or tag overlap fallback). Response includesmode: "embedding"|"tags"|"none".
- Discovery params:
GET /api/kits/trending— Top 3 kits by install countGET /api/kits/mine— Publisher's own kits (auth required)GET /api/kits/:slug— Kit detail with publisherName, publisherVerified, averageStars, ratingCount (404 for unpublished)GET /api/kits/:slug/versions— Version history with scan resultsGET /api/kits/:slug/scans— Full scan history withdiffs[]andversions[]. Without query params: returns adjacent diffs (newest first). With?base=<v>&head=<v>: returns a single targeted diff between any two release versions.GET /api/kits/:slug/install— Install payloadPOST /api/kits— Publish/update a kit (auth required). Triggers embedding generation ifOPENAI_API_KEYis set.DELETE /api/kits/:slug— Unpublish a kit (auth required, owner only)POST /api/kits/:slug/learnings— Submit a learningGET /api/kits/:slug/analytics— Daily installs (30d) and target breakdown (auth required, owner only)GET /api/kits/:slug/ratings— Public list of ratings + aggregatedaverageStars,ratingCountPOST /api/kits/:slug/ratings— Submit/update a 1-5 star rating with optional body (auth required, cannot rate own kit; rate-limited)GET /api/publishers/:slug— Publisher profile (agent name, verified flag, kit count, total installs, avg score, kits list)GET /api/collections— List curated collections (slug, title, description, curator, kit count, total installs, average stars, featured flag)GET /api/collections/:slug— Collection detail with hydrated kit listGET /api/collections/:slug/install— Install Stack: returnscliCommand(e.g.npx @kithub/cli install-collection <slug> --target=<t>), per-kitinstallUrls, fullinstructionsmarkdown, andsupportedTargets. Validates?target=againstSUPPORTED_TARGETS(400 on invalid).
The API uses @fastify/rate-limit with per-route configuration (global rate limiting is disabled).
Rate-limited endpoints (10 req/min per IP):
POST /api/auth/register(legacy route; 410 outside tests)POST /api/auth/login(legacy route; 410 outside tests)POST /api/kits(publish)
All API error responses follow a consistent shape:
{ "error": "Error Type", "message": "Human-readable description.", "statusCode": 400 }- ErrorBoundary component:
apps/web/app/components/ErrorBoundary.tsx— Wraps all pages in the root layout to catch unexpected React errors. - Next.js error.tsx files:
apps/web/app/dashboard/error.tsx,apps/web/app/registry/error.tsx,apps/web/app/registry/[slug]/error.tsx— Route-level error boundaries with retry buttons. - Toast notifications: API fetch failures surface user-friendly error messages via the existing toast system.
- Install and learning events trigger publisher email notifications (max 1 per kit per type per 24h window)
notification_logstable tracks sent notifications to enforce the 24h dedup window- Email delivery: logs to console in dev mode; set
SMTP_URLenv var to send real emails via nodemailer - Optional env vars:
SMTP_URL,EMAIL_FROM(defaults to noreply@kithub.dev)
- Token persistence:
~/.kithub/config.jsonstores token, email, agentName - Commands:
search,install,install-collection,publish,login,verify,whoami,logout installauto-detects target (Cursor, Claude Code, Codex) and writes files to diskinstall-collection <slug>fetches the collection bundle and installs every kit in order, with--target=<t>and--dry-runflagspublishprints a live URL after successful publish, exits with code 1 if blockedlogin/register/verifyare Supabase-native: the SDK fetches/api/auth/configfor the public Supabase URL + anon key, then callssupabase.auth.signInWithOtpandsupabase.auth.verifyOtpdirectly. The Supabase access token + refresh token are persisted to~/.kithub/config.jsonand sent as the Bearer token to the API.registerpassesagentNamevia Supabase user metadata (options.data.agentName) so the API auto-provisions the publisher profile from the first authenticated request.resolveAuthSessionauto-refreshes expired Supabase sessions via the stored refresh token; legacy single-token configs are cleared on first run.publishalso acceptsKITHUB_TOKENenv var (must be a valid Supabase access token) for non-interactive CI usage.
npm testfrom root runs all tests via Turborepo- Uses Vitest as the test framework
packages/schema— 39 unit tests covering:parseKitMd(valid/invalid kits, conformance levels, missing sections)scanKit(secrets detection, destructive patterns, model grounding, scoring)KitFrontmatterSchema/KitBodySchema(Zod validation)generateInstallPayload/isValidTarget(all 5 install targets)
apps/api— integration tests covering:- Health check endpoint
- Legacy auth flow used only in test mode
- Kit CRUD: publish → list → detail → install payload
- Error cases: 401/400/404 responses
- Discovery (
discovery.test.ts, 16 tests): keyword/semantic search modes + fallback, related-kits with tag-overlap fallback, ratings auth-gated + self-rating block + upsert + invalid stars, public ratings list, kit-detail rating aggregates, scan history, collections index/404/install bundle (CLI command + invalid-target rejection), publisher verified flag. - MCP contracts (
packages/mcp-server/src/__tests__/tools.test.ts, 11 tests): validates the input schemas ofsearch_kits,get_related_kits,list_collections,get_collection— includingmodeenum,targetenum bound toSUPPORTED_TARGETS, andlimitrange guards. Guards against silent contract drift for downstream agents.
npm test # Run all tests
cd packages/schema && npx vitest run # Schema tests only
cd apps/api && npx vitest run # API tests only- Basic seed:
npx tsx packages/db/src/seed.ts— Creates test user + 3 sample kits, marks QuantBot/OpsBot publishers as verified, seeds 3 curated collections (indie-hacker-starter,engineering-quality,ops-comms), seeds sample ratings, and backfills embeddings whenOPENAI_API_KEYis set. - JourneyKits seed:
npx tsx packages/db/src/seed-journeykits.ts— Fetches ~20 real kits from JourneyKits.ai, anonymizes authors, and inserts them under a "CommunityCurator" publisher. Idempotent (safe to re-run). - Skills seed:
npx tsx packages/db/src/seed-skills.ts— Creates 10 universal agent skills under "SkillCurator" publisher. Idempotent.
kit_ratings— 1–5 stars + optional body, unique on(kit_slug, publisher_id), self-rating blocked at API.collections+collection_kits— curator-authored bundles with display order; aggregates fetched viabatchFetchKitsBySlugs.kit_embeddings—jsonbvector storage (no pgvector dependency); cosine similarity computed in JS bysemanticSearchKitsandgetRelatedKits.publisher_profiles.verified_at— timestamp; surfaced asverifiedboolean on publisher endpoints andpublisherVerifiedon kit detail.
- Script:
scripts/post-merge.sh— Runsnpm install, rebuilds shared packages, and pushes DB schema - Configured in
.replit[postMerge]section - Runs automatically after task agent merges
- API auth expects Supabase access tokens; legacy
/api/auth/{register,verify-email,login}endpoints return 410 Gone outside of test mode (test mode keeps the legacy email-code flow alive only soapps/api/src/__tests__/auth.test.tscan exercise the JWT issuance path). - Anonymous endpoints exempted from auth: all
GET /api/{kits,publishers,skills,collections,install-targets}/*(excluding/api/kits/mine), plusPOST /api/kits/:slug/viewso unauthenticated view tracking works for the analytics rollup. Seeapps/api/src/middleware/auth.ts. - Migration
0004_discovery_trust_v1.sqlis defensive: allCREATE TABLE,ADD COLUMN, andCREATE INDEXstatements useIF NOT EXISTS; foreign-keyADD CONSTRAINTstatements are wrapped inDO $$ … EXCEPTION WHEN duplicate_object THEN null; END $$blocks so re-running the migration on an existing Discovery & Trust schema is a no-op.