Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Google Gemini Variables
GOOGLE_API_KEY=""
# Default is usually gemini-2.5-flash
MODEL_NAME="gemini-2.5-flash"
# OpenCode Zen Variables (OpenAI-compatible API)
OPENCODE_API_KEY=""
# Optional base URL override; defaults to https://opencode.ai/zen/v1
OPENCODE_BASE_URL=""
# big-pickle is a reasoning model: reasoning tokens are charged against max_tokens,
# so keep output budgets well above ~1024 or responses come back empty.
MODEL_NAME="big-pickle"
AI_TIMEOUT_MS="30000"
PDF_PARSE_TIMEOUT_MS="12000"
COVER_LETTER_ROUTE_TIMEOUT_MS="35000"
Expand All @@ -18,9 +21,28 @@ NEXT_PUBLIC_STACK_PROJECT_ID=""
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=""
STACK_SECRET_SERVER_KEY=""

# Optional
# Optional — also used as the base for "tailor my resume" links in alert emails
NEXT_PUBLIC_APP_URL="http://localhost:3000"

# Optional JavaScript career-page rendering fallback, using Firecrawl's scrape API.
# Example: https://firecrawl.adityamer.dev or https://api.firecrawl.dev
# Leave empty to use provider APIs and ordinary HTML only.
FIRECRAWL_API_BASE_URL=""
# Server-only; required when your rendering service enforces API authentication.
FIRECRAWL_API_KEY=""

# Job alert emails (Resend). Both are required before any alert is sent;
# without them the cron still scans and stores new listings, and mails them on
# the first run after the keys are added.
RESEND_API_KEY=""
# Must use a domain verified in Resend, e.g. "ATS Job Alerts <alerts@yourdomain.com>"
EMAIL_FROM=""

# Shared secret for /api/cron/scan-watchlist. Vercel Cron sends it as
# "Authorization: Bearer <CRON_SECRET>". The endpoint rejects every request
# when this is unset.
CRON_SECRET=""

# Rate limiting backend (required in production unless ALLOW_IN_MEMORY_RATE_LIMIT=true)
UPSTASH_REDIS_REST_URL=""
UPSTASH_REDIS_REST_TOKEN=""
Expand Down
18 changes: 11 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
bun-version: latest
node-version: "24"
cache: true

- name: Install dependencies
run: bun install --frozen-lockfile
run: vp install --frozen-lockfile

- name: Format and lint
run: vp check

- name: Lint
run: bun run lint
run: vp run lint

- name: Typecheck
run: bun run typecheck
run: vp run typecheck

- name: Test (Coverage)
run: bun run test:coverage
run: vp run test:coverage --maxWorkers=2 --testTimeout=15000
1 change: 1 addition & 0 deletions .vite-hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vp staged
88 changes: 88 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Application Tracking System

This is a job seeker's resume preparation and job discovery app built with Next.js App Router, React, TypeScript, Tailwind CSS, Stack Auth, and Convex. Vite+ manages tooling; Next.js serves and builds the application.

## Major Features

| Feature | Entry point | Main responsibilities |
| --------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Dashboard | `src/app/dashboard/page.tsx` | Resume, analysis, and cover-letter counts, average match score, and quick actions. |
| Resume library | `src/app/dashboard/upload/` | PDF upload, text extraction, stored resumes, selection, and deletion. |
| Applications | `src/app/dashboard/applications/` | Persistent application pipeline, contacts, notes, follow-up queue, filtered CSV export, and watchlist imports. |
| Interview prep | `src/app/dashboard/applications/[id]/interview/` | AI practice questions, STAR outlines, saved answer notes, explicit regeneration, and revision conflict protection on each application. |
| Resume analysis | `src/app/dashboard/analysis/` | Job match scoring, skills extraction, strengths, weaknesses, recommendations, and saved results. |
| Cover letters | `src/app/dashboard/cover-letter/` | Generation with tone and length preferences, draft restoration, saved letters, and document export. |
| Resume builder | `src/app/dashboard/resume-builder/` | AI resumes from notes or saved content, optional job tailoring, seven LaTeX templates, editing, PDF rendering, and saved versions. |
| History | `src/app/dashboard/history/` | Search and filtering across saved analyses, cover letters, and tailored resumes. |
| Job alerts | `src/app/dashboard/watchlist/` | Career-page trackers, role/location/exclusion filters, editable email preferences, manual scans, and pending-alert retries. |
| Authentication | `src/stack/server.ts`, `src/app/handler/` | Stack Auth sessions and account flows. |
| Public pages | `src/app/` | Landing page, how it works, contact, privacy, and terms. |

Treat this table as the existing product scope. When adding a feature, connect its navigation, authenticated API, storage, loading/error/empty states, and relevant history or dashboard integration.

## Development Commands

Use `package.json` and the lockfile as the command and dependency sources of truth. This repository declares pnpm and uses `pnpm-lock.yaml`.

| Task | Command |
| ------------------------------------ | ---------------------- |
| Install dependencies | `vp install` |
| Start Next.js | `vp run dev` |
| Start the Convex development backend | `vp exec convex dev` |
| Build Next.js | `vp run build` |
| Serve the production build | `vp run start` |
| Format and lint checks | `vp check` |
| Strict lint script | `vp run lint` |
| TypeScript checks | `vp run typecheck` |
| Tests, single run | `vp run test` |
| Tests with coverage | `vp run test:coverage` |

**Project-specific override:** use `vp run dev` and `vp run build` for this app. The generic Vite+ commands described below invoke Vite rather than the Next.js scripts. `vite.config.ts` currently disables lint type checking, so run the explicit typecheck script as well as `vp check` for code changes. Test configuration lives in `vitest.config.ts`, and tests live under `test/`.

For environment setup, read `.env.example`. For deployment, career-page providers, email/cron setup, or observability, read the relevant section of `README.md`. Some README and CI commands still reference Bun; reconcile toolchain changes with `package.json` and the current lockfile.

## Architecture and Conventions

- Pages and API route handlers live in `src/app/`; reusable UI lives in `src/components/`, with primitives in `src/components/ui/`. Reuse the Tailwind tokens in `src/app/globals.css` and the existing toast and navigation components.
- Keep interactive browser state in client components; keep credentials, AI calls, PDF parsing, and database access on the server. The `@/` import alias resolves to `src/`.
- Validate API inputs with the contracts in `src/lib/contracts/api.ts`. Follow the route's existing response contract; shared error helpers in `src/lib/api-response.ts` include a code, message, and request ID.
- Resolve the authenticated user through `src/lib/auth.ts` and scope every user-owned read or write to that user. Preserve rate limiting on expensive routes and existing ownership checks in Convex domain modules.
- Convex tables and indexes live in `convex/schema.ts`; domain operations live in `convex/*.ts`. Server adapters in `src/lib/convex-server.ts` call `functions:*` paths, so preserve or update the compatibility exports in `convex/functions.ts` when adding operations. Regenerate Convex artifacts through its CLI when required.
- Shared API and domain types live in `src/types/`. Update the request contract, server adapter, stored data, and consuming UI together when changing a payload.

## Feature Rules and Operational Limits

- **AI generation:** `src/lib/gemini.ts` currently calls OpenCode Zen through an OpenAI-compatible API. Use `OPENCODE_API_KEY`, `OPENCODE_BASE_URL`, and `MODEL_NAME` from `.env.example`; the filename is historical. Preserve timeouts, retries, normalized results, user-scoped caches, and regeneration behavior when changing generation flows.
- **Drafts and duplicate requests:** inspect `src/hooks/useGenerationFlow.ts`, `src/lib/server-drafts.ts`, and `src/lib/idempotency.ts` before changing restoration or retry behavior. Server drafts and idempotency responses are currently process-local stores, so they are not durable across restarts or shared between instances.
- **Document output:** use the existing PDF parsing, LaTeX generation/rendering, and cover-letter export helpers. Keep upload limits, parsing timeouts, render failures, and downloadable output consistent with the existing flows.
- **Job discovery:** provider adapters live in `src/lib/job-sources/`. Use `src/lib/safe-fetch.ts` for user-supplied URLs and redirects. The initial scan records a baseline without alerting; later scans deduplicate postings and send new-role alerts. Preserve pending notifications when email delivery fails or is unconfigured.
- **Scheduled scans:** `/api/cron/scan-watchlist` requires `CRON_SECRET` and scans a bounded batch. Scheduling lives in `vercel.json`. Keep authentication and batching intact when changing the scan flow.
- **Configuration and logs:** keep secrets server-only and document new variables in `.env.example`. Use the observability helpers for route events and request IDs; keep resume text, job descriptions, and credentials out of logs. Production rate limiting uses Upstash; the memory fallback is local unless explicitly enabled.

## Completing a Change

1. Inspect the current working tree and relevant feature files before editing; preserve existing work.
2. Implement the feature through its existing UI, route, and storage patterns. Include accessible controls and useful loading, error, and empty states.
3. For code changes, run the checks above and the relevant tests; use coverage or a production build when the affected behavior requires them. For documentation-only changes, check formatting and confirm referenced paths and commands against the repository.
4. Report the result, checks performed, and any failures or required environment setup. Update this feature map when adding a major capability.

<!--VITE PLUS START-->

# Using Vite+, the Unified Toolchain for the Web

This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, and it invokes Vite through `vp dev` and `vp build`. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.

Docs are local at `node_modules/vite-plus/docs` or online at https://viteplus.dev/guide/.

## Built-in Commands vs Scripts

`vp <name>` runs a built-in command. `vp run <name>` runs a `package.json` script or a `vite.config.ts` task. Scripts cannot overwrite built-ins, so `vp dev` and `vp run dev` may do different things. Check `package.json` and `vite.config.ts` first, and run `vp run <name>` when the project defines a script or task with that name.

## Review Checklist

- [ ] Run `vp install` after pulling remote changes and before getting started.
- [ ] Run `vp check` and `vp test` to format, lint, type check and test changes.
- [ ] Check if there are `vite.config.ts` tasks or `package.json` scripts necessary for validation, run via `vp run <script>`.
- [ ] If setup, runtime, or package-manager behavior looks wrong, run `vp env doctor` and include its output when asking for help.

<!--VITE PLUS END-->
Loading
Loading