An evidence-grounded writing assistant that turns a resume and job description into an editable cover letter.
This section starts the app from a fresh checkout. The project uses two services:
- API server — receives resume and job-description input on port
8080 - Web app — serves the Draftwell interface on port
22475
Install:
- Node.js 24 or newer
- pnpm
- PostgreSQL, only if you plan to use the database tooling
Check your versions:
node --version
pnpm --versiongit clone https://github.com/peshotan/ConversationalCoverLetterGenerator.git
cd ConversationalCoverLetterGenerator
pnpm installThe server uses the following environment variable names for OpenAI access. Export them in the same terminal where you start the API server:
export AI_INTEGRATIONS_OPENAI_API_KEY="your-openai-api-key"
export AI_INTEGRATIONS_OPENAI_BASE_URL="https://api.openai.com/v1"Use your real key only in your local shell, a password manager, or your hosting provider's secret manager. Do not paste it into this README, commit it to Git, or put it in a checked-in .env file. This project does not automatically load .env files.
The Use AI-generated content option is enabled from the web form. The deterministic no-AI draft still uses pasted resume text, while AI mode supports pasted text and direct PDF input.
If you are running the database commands, also set your PostgreSQL connection string:
export DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/DATABASE"The basic cover-letter writing flow does not require DATABASE_URL. Generated letters can also be saved to the browser's native IndexedDB history, but this is local to the current browser origin and device: there are no accounts, server-side drafts, or cross-device sync.
If you are running this inside Replit, provision the OpenAI AI integration or add these same variables through Secrets. Replit may provide the values automatically; never print or commit the secret value.
Open a terminal at the repository root and run:
export PORT=8080
pnpm --filter @workspace/api-server run devLeave this terminal running. A healthy server responds at:
http://localhost:8080/api/healthz
Open a second terminal at the same repository root:
export PORT=22475
export BASE_PATH=/
pnpm --filter @workspace/conversational-cover-letter-generator run devOpen http://localhost:22475 in your browser.
When running in Replit, use the existing web preview. Replit's artifact routing connects the web app's /api requests to the API service. A plain local Vite server does not provide that cross-port /api routing by itself, so use a local reverse proxy or equivalent path router if the web page cannot reach http://localhost:8080/api/healthz.
- Paste at least 80 characters of resume text.
- Paste at least 80 characters of job-description text.
- Optionally enter the company, role, recipient, tone, and length.
- Leave Use AI-generated content unchecked for the deterministic evidence-based draft.
- Turn it on to use OpenAI. You can then upload a PDF, which is sent directly to the AI path rather than parsed into the resume text box.
- Select Draft my letter.
- Review the evidence links and warnings, edit the letter, then copy or download it.
# Run tests
pnpm test
# Typecheck the workspace
pnpm run typecheck
# Build the API package
pnpm --filter @workspace/api-server run build
# Build the web package
PORT=22475 BASE_PATH=/ pnpm --filter @workspace/conversational-cover-letter-generator run build
# Regenerate typed API clients after editing the OpenAPI contract
pnpm --filter @workspace/api-spec run codegen- pnpm workspaces, Node.js 24, TypeScript 5.9
- API: Express 5
- DB: PostgreSQL + Drizzle ORM
- Validation: Zod (
zod/v4),drizzle-zod - API codegen: Orval (from OpenAPI spec)
- Build: esbuild (CJS bundle)
artifacts/conversational-cover-letter-generator/src/pages/home.tsx— main input, generation, editing, and export experienceartifacts/conversational-cover-letter-generator/src/index.css— shared visual theme and motionartifacts/api-server/src/routes/cover-letter.ts— resume/job-description generation endpointlib/api-spec/openapi.yaml— source of truth for the generation contractDESIGN.md— product decisions, requirements, and build sequence
- Resume text, job descriptions, uploaded PDFs, and credentials are kept out of local history. Generated letters and safe display metadata are saved in versioned browser-local IndexedDB records.
- Local history is scoped to the browser origin and device. It is not an account backup, is not sent to the server, and may be unavailable when browser storage is blocked or disabled. The app remains usable without it.
- PDF resumes are kept in session memory and sent directly to the AI path only when the user enables AI-generated content.
- The default generator is deterministic and evidence-based so the product remains usable without a model call.
- The API contract is generated from OpenAPI so the frontend and server share the same request and response types.
- Paste a resume and job description or upload a PDF/text resume.
- Set optional company, role, recipient, tone, and length details.
- Generate an editable cover letter with evidence links and missing-evidence warnings.
- Reopen, edit, delete, or clear generated drafts from the local History panel without regenerating them.
- Copy, download, or reset the current draft.
- Keep secrets and confidential data out of repositories.
- Use feature branches and open pull requests; never push directly to the default branch.
- The generation endpoint requires at least 80 characters for both resume and job description.
- Artifact workflows provide
PORTandBASE_PATH; do not hardcode them in the app. - Browser history storage is best-effort. If IndexedDB is unavailable, generation and exports still work, but drafts cannot be saved.
- See the
pnpm-workspaceskill for workspace structure, TypeScript setup, and package details