This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MCS Tech Pack Registry — a public registry at techpacks.mcs-cli.dev for discovering, searching, and submitting tech packs for Claude Code. Built on Cloudflare Pages + Workers + KV (free tier).
npm run build:worker # esbuild bundles _worker.ts → public/_worker.js (required before deploy)
npm run typecheck # tsc --noEmit
npm run dev # build:worker + wrangler pages dev (local KV is empty)
npm run deploy # build:worker + wrangler pages deployCI auto-deploys on push to main via .github/workflows/deploy.yml.
_worker.ts is the sole entry point. There is no src/index.ts — do not create one.
_worker.ts → routes API traffic, falls back to env.ASSETS.fetch() for static files
src/api/packs.ts → GET /api/packs, GET /api/packs/github/:owner/:repo
src/api/submit.ts → POST /api/submit (Turnstile + honeypot + IP rate-limit)
src/api/reindex.ts → POST /api/reindex (auth required, batch GitHub GraphQL)
src/lib/github.ts → GitHub API helpers (GraphQL batch metadata, REST yaml fetch)
src/lib/validator.ts → Manual techpack.yaml validation (Ajv cannot be used in Workers)
src/lib/glob.ts → POSIX fnmatch + dir/ shortcut — mirrors mcs Sources/mcs/Core/GlobMatcher.swift
src/lib/builtinIgnore.ts→ BUILTIN_IGNORED_DIRS + BUILTIN_INFRASTRUCTURE_FILES — mirror mcs PackHeuristics.swift
src/lib/turnstile.ts → Cloudflare Turnstile verification
public/ → Static frontend (vanilla JS, no framework)
public/_worker.js → BUILD ARTIFACT (gitignored, esbuild output)
The build pipeline: esbuild _worker.ts --bundle → public/_worker.js. Cloudflare Pages picks up _worker.js from the output directory automatically. Everything in src/ is bundled at build time — nothing deploys separately.
tsc / npm run build targets src/ only and outputs to dist/ for type-checking. It is NOT used for production.
| Key | Value |
|---|---|
pack:github/<owner>/<repo> |
JSON PackEntry — one entry per pack |
index:all |
JSON string[] — sorted slug array (master index) |
rate:<ip> |
Submission count (RATE_LIMIT namespace, 1h TTL) |
The github/ prefix future-proofs for other providers. The identifier field from techpack.yaml is display-only — not used as a key.
All routing is manual string matching in _worker.ts → handleApiRoute().
| Method | Route | Auth |
|---|---|---|
| GET | /api/packs |
Public |
| GET | /api/packs/github/:owner/:repo |
Public (triggers background stale reindex if >1h old) |
| POST | /api/submit |
Turnstile token |
| POST | /api/reindex |
Authorization: Bearer <REINDEX_SECRET> |
- Ajv is banned — Workers block
new Function(). Validation is manual insrc/lib/validator.ts. The JSON Schema file exists for documentation only. glob.tsandbuiltinIgnore.tsmirror mcs verbatim — registry/CLI parity is the contract. The matcher must not gain**support and the built-in sets are exact strings (not globs). Any drift meansmcs pack validateand the registry website disagree on the same pack.public/_worker.jsis gitignored — must be built before deploy. If API routes return HTML instead of JSON, the Worker wasn't bundled.- esbuild flags matter —
--platform=browser(notneutral) and--conditions=workerd,worker,browserare required. wrangler kvdefaults to local — always pass--remotefor production KV operations.wrangler-actionversion — deploy workflow setswranglerVersion: ""to use the project's wrangler 4.x devDep, not the action's bundled 3.x.- Do NOT add
maintowrangler.toml— it makes wrangler treat the project as a Worker instead of Pages.
- Scheduled: GitHub Actions cron every 6h calls
POST /api/reindex - On-demand:
handleGetPackfires backgroundreindexSinglePackviactx.waitUntilif data is >1h stale - Smart re-fetch:
techpack.yamlonly re-fetched ifpushedAtchanged - Batch GraphQL: Up to 50 repos per GitHub API call
- Pack statuses:
active | unavailable | invalid—unavailablepacks are pruned fromindex:all(KV entry kept, filtered from listing).invalidpacks stay inindex:alland render at the bottom of the grid with a red banner; the pack modal exposes a "Report issue" button that builds a prefilled GitHub issue URL. See.claude/memories/decision_architecture_reindex_pruning_and_recovery.md.
Cloudflare (via wrangler pages secret put): TURNSTILE_SECRET_KEY, GITHUB_TOKEN, REINDEX_SECRET
GitHub Actions: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, REINDEX_SECRET, REGISTRY_URL