This file provides guidance to AI coding agents and human contributors when working on this repository. It is intentionally public so contributors and AI tools can share a common operating manual.
PocketResume is a Chrome Extension (Manifest V3) built with vanilla HTML/CSS/JS. No bundler / build pipeline for app code; the only build step is esbuild for the cloud-sync bundle. Edit source files, reload the unpacked extension in Chrome.
Key runtime entrypoints (declared in manifest.json):
background.js — pipeline orchestration + AI API callscontent.js — extracts page text from the active tabpopup.html + popup.js — user actions + PDF generation for PocketResume layoutsoptions.html + options.js — API keys + multiple resumes + togglesresume-renderers.js — Jake, Deedy, Academic CV PDF layoutssrc/cloud-sync.js (source) → cloud-sync.js (bundle, gitignored)convex/ — auth config + resume schema/functionsThe extension runs without Node at runtime, but the repo uses npm to manage dependencies (and to bundle cloud-sync.js + update the vendored jsPDF build).
npm ci
# or
npm install
src/cloud-sync.js is bundled by esbuild into cloud-sync.js at the repo root (gitignored). Required env vars (CLERK_PUBLISHABLE_KEY, CONVEX_URL) are injected at build time. See .env.example and package.json → scripts/build-clerk.mjs.
cp .env.example .env.local
# fill in your own Clerk + Convex values
npm run build:clerk
popup.html loads jsPDF from libs/jspdf.umd.min.js (vendored). If you bump jspdf, copy the built artifact into libs/.
npm install jspdf@latest
cp node_modules/jspdf/dist/jspdf.umd.min.js libs/jspdf.umd.min.js
No dev server.
chrome://extensions → Developer mode → Load unpacked → select repo rootDebugging:
npx convex dev
Requires CONVEX_DEPLOYMENT in .env.local. Generated code goes to convex/_generated/ (gitignored).
None configured. No test runner or linter. Validate by hand: load unpacked, generate a resume with a real API key, inspect the PDF + the service worker console.
popup.js).chrome.runtime.sendMessage({ type: 'START_GENERATION', payload: { tabId, resumeStyle, resumeId } }).background.js) handles START_GENERATION:
chrome.storage.local (API key per provider, resumes, cover letter toggle).GET_PAGE_CONTENT. Falls back to injecting content.js via chrome.scripting.executeScript(...).{ status: 'success', data: <resumeJsonString>, coverLetterData: <coverLetterJsonString|null> }.JSON.parse(...) the model output.generatePDF(...) in popup.jswindow.ResumeRenderers.generateResumePDF(...) in resume-renderers.jsgenerateCoverLetterPDF(...) in popup.jsoptions.js).REFINE_RESUME to background with source text.*ResumeRefinement(...) function — rewrites source into a cross-style master resume (no job-description tailoring).EXTRACT_RESUME_JSON to background with source text.*ResumeExtraction(...) function — extracts structured JSON profile from raw text.jsonContent on the resume entry and persisted. Used as jsonContent in the generation pipeline.Configured by getResumeStyleConfig(...) in background.js:
| UI Style | promptStyle | layout | PDF Renderer |
|---|---|---|---|
| basic | basic | pocketresume | popup.js → generatePDF |
| professional | professional | pocketresume | popup.js → generatePDF |
| faang | faang | pocketresume | popup.js → generatePDF |
| jake | faang | jake | resume-renderers.js → renderJakeLayout |
| deedy | faang | deedy | resume-renderers.js → renderDeedyLayout |
| academic-cv | academic-cv | academic-cv | resume-renderers.js → renderAcademicCvLayout |
Settings are stored in chrome.storage.local, managed in options.js.
Important keys:
apiProvider: "google" | "openrouter" | "openai" | "anthropic"geminiApiKey / openrouterApiKey / openaiApiKey / anthropicApiKey: stringresumes: array of { id, label, content, jsonContent, lastRefineBackup, lastRefineAppliedAt } (up to 3)selectedResumeId: which resume is active in the popupresumeType: "basic" | "professional" | "faang" | "jake" | "deedy" | "academic-cv"coverLetterEnabled: booleancloudSyncStatus: "idle" | "syncing" | "synced" | "error" (cloud-sync feature only)Legacy migration: userProfile → resumes[0].content
Four providers supported, selected via apiProvider:
gemini-2.5-flash, API key from Google AI Studiogpt-4o-mini, API key from OpenAI Platformclaude-3-5-haiku-20241022, API key from Anthropic Consoleopenai/gpt-oss-120b:free, API key from OpenRouterEach provider has matching callGemini* / callOpenAI* / callAnthropic* / callOpenRouter* variants for all 4 pipelines: generation, cover letter, extraction, refinement. Naming is historical — the function is selected at runtime based on apiProvider.
When enabled, resumes sync across devices via Clerk (auth) + Convex (backend).
Architecture:
src/cloud-sync.js — IIFE source, bundled by esbuild → cloud-sync.js (gitignored)convex/auth.config.ts — Clerk → Convex auth wiring; requires CLERK_FRONTEND_API_URL env varconvex/schema.ts — resumes table shapeconvex/resumes.ts — list, upsert, remove queries/mutationsCredentials are never hardcoded. The build step injects CLERK_PUBLISHABLE_KEY and CONVEX_URL from .env.local into the bundle. Contributors must set up their own Clerk + Convex accounts.
PocketResume/
├── manifest.json # Manifest V3 entrypoint wiring
├── background.js # Service worker: pipeline + AI calls
├── content.js # Content script: page text extraction
├── popup.html / popup.js # Popup UI + PocketResume PDF generation
├── options.html / options.js# Settings: API keys, resumes, toggles
├── resume-renderers.js # Jake / Deedy / Academic CV PDF layouts
├── src/cloud-sync.js # Cloud sync source (bundled → cloud-sync.js)
├── cloud-sync.js # [generated, gitignored] esbuild bundle
├── convex/ # Convex backend
│ ├── auth.config.ts
│ ├── schema.ts
│ ├── resumes.ts
│ └── _generated/ # [generated, gitignored]
├── libs/jspdf.umd.min.js # Vendored jsPDF
├── scripts/build-clerk.mjs # Build script for cloud-sync bundle
├── .env.example # Template for .env.local
├── AGENTS.md # This file
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── SECURITY.md
├── LICENSE
└── package.json # Build scripts only (no runtime deps)
background.js (callGemini* / callOpenAI* / callAnthropic* / callOpenRouter*). Update the style config table above if the schema or layout mapping changes.content.js (extractPageText) and the truncation logic in background.js.popup.js (generatePDF / generateCoverLetterPDF).resume-renderers.js (renderJakeLayout / renderDeedyLayout / renderAcademicCvLayout).options.js / options.html.popup.html / popup.js.manifest.json.src/cloud-sync.js (then npm run build:clerk).convex/schema.ts, convex/resumes.ts, convex/auth.config.ts (then npx convex dev).background.js).convex/ backend.chrome.storage.local for persistence; do not introduce new global state.key field is intentionally absent. Chrome assigns a fresh extension ID on first load. Do not re-add it (it would lock all contributors to one ID).host_permissions in manifest.json includes the Clerk + Convex domains contributors will need to override. Update both the manifest and this file if you add a new provider.chrome.storage.local each time.content.js. The page’s CSP applies.cloud-sync.js (build artifact), convex/_generated/, or anything from .env.local. See .gitignore.PocketResume is privacy-first by default. See privacy-policy.md for the full policy. The Chrome extension:
chrome.storage.local unless the user explicitly enables cloud sync