jevsume

September 21, 2026 · View on GitHub

ATS-friendly resume review powered by Jev (TypeSafe System One). The frontend extracts resume text the way a parser would, then a Cloudflare Worker runs typed JEV questions and composes a JevScore.

Two modes:

  • General review — wording, conciseness, structure, metrics, ATS parseability.
  • Per-job review — job description + tags become a stored job persona (JEV-native state + questions). One resume is scored against one persona.

Jev does not generate prose. Scores, verdicts, and probabilities come from System One; the Worker turns them into UI copy.

MCP + Agent Skill

Any MCP client can run the same Jev review without the studio UI. Tools return compact JSON (score, findings, suggestions, gaps) — not the full review payload.

Hosted (no API key)

Streamable HTTP at /mcp on the Worker. No auth yet (OAuth can wrap this later). review_resume shares the platform IP rate limit (10 / minute) with POST /api/reviews.

With pnpm dev, the endpoint is http://localhost:5173/mcp. After deploy, it is https://<your-worker>/mcp on the same host as the app.

{
  "mcpServers": {
    "jevsume": {
      "url": "http://localhost:5173/mcp"
    }
  }
}

Local npx (your TypeSafe key)

Runs Jev with your key. Does not use the hosted rate limit.

Setup for Claude, Claude Code, Codex, Codex Chat, and Cursor: docs/mcp-local.md.

npx -y github:unownone/jevsume -- --api-key $TYPESAFE_API_KEY

From a clone: pnpm mcp -- --api-key $TYPESAFE_API_KEY. --mock uses the deterministic provider (tests/dev only).

Tools

ToolUse
list_job_lensesBaked-in catalog (default + presets). Optional track / query. Ids, titles, tags, blurb — no job text.
get_job_lensFull listing by id. Call only when you must quote the JD.
suggest_job_lensOne lens id from resumeText.
review_resumeresumeText plus optional jobLensId and/or jobText / jobTitle / company / jobUrl.

jobLensId accepts default, a preset id (swe-staff), or preset:swe-staff. Pasted jobText wins over a lens id. Never send PDF bytes — extract text first.

Skill

skills/jevsume-resume/SKILL.md — pick a lens, review, rewrite the weak bullets, re-review. Do not invent Jev scores.

Stack

  • React + Vite SPA via @cloudflare/vite-plugin
  • Hono API on a Cloudflare Worker (/api/*)
  • D1 for personas, resumes, and evaluation runs (in-memory fallback when the binding is absent)
  • KV for the unique visitor counter (edge reads + HTTP cache headers on GET /api/visitors)
  • Workers Analytics Engine (ANALYTICS → dataset website_events) for pageviews and UI clicks
  • Isolated JEV module: packages/jev — see docs/architecture-jev.md

Every JEV call stores resume, input state, prompt/questions, and raw + transformed output so you can look up runs later and score prompt changes.

Setup

pnpm install
cp .dev.vars.example .dev.vars
# optional live Jev:
# put your key in .dev.vars as TYPESAFE_API_KEY=...
pnpm dev

Open the Vite URL (usually http://localhost:5173). Without TYPESAFE_API_KEY, the Worker uses a deterministic mock so the app still runs. pnpm dev applies local D1 migrations first. If D1 is bound but empty (first run or a preview that has not been migrated), the Worker also creates the schema on the first /api/* request.

pnpm test
pnpm typecheck
pnpm cf-typegen   # wrangler types --env-interface CloudflareBindings
pnpm db:migrate:local   # apply D1 migrations to the local SQLite file

Deploy

pnpm build
npx wrangler d1 migrations apply jevsume --remote   # after first deploy creates the DB
npx wrangler secret put TYPESAFE_API_KEY
npx wrangler deploy

Git deploys (Workers Builds) auto-provision the D1 database named jevsume because database_id is omitted. Apply migrations once after the first successful deploy.

wrangler.jsonc uses Workers static assets + SPA fallback, run_worker_first: ["/api/*", "/mcp"], compatibility_date: 2026-09-17, nodejs_compat, observability, a D1 binding DB (jevsume), a KV binding VISITORS for the visitor counter, and an Analytics Engine dataset website_events (ANALYTICS). Schema lives in migrations/0001_init.sql.

Website events are fire-and-forget writeDataPoint() calls. Each point is blobs: [event_type, page, country], doubles: [1], indexes: [event_id]. The Worker records a pageview for every /api/* request and accepts client events at POST /api/events (pageview on load, click on review / upload / demo / persona). Query with the Analytics Engine SQL API against website_events.

VISITORS is bound to the existing jevsume-visitors KV namespace (id in wrangler.jsonc). Omitting id makes Workers Builds try to create a second namespace with that title and fail. The unique count is a single KV key (count) plus per-visitor keys (vid:<id>). GET /api/visitors is cacheable (Cache-Control + CDN-Cache-Control); POST /api/visitors is no-store so uniqueness writes are not cached.

Lookup APIs (summaries on list, full prompt/input/output on get):

MethodPathUse
GET/api/resumes?q=&source=Find stored resumes
GET/api/resumes/:idFull resume text
GET/api/personas?q=&tag=Find personas
GET/api/evals?kind=&resumeId=&personaId=&provider=&promptHash=&minScore=&maxScore=Find evaluation runs
GET/api/evals/:idFull input, prompt, output, review

Secrets

Official TypeSafe env name is TYPESAFE_API_KEY (SDK ENV). Never commit .dev.vars. HTTP API: POST https://api.typesafe.ai/v1/systemone (API).

Docs