# JobOps

Self-hosted job search orchestration: discover roles from multiple sources, score fit with your profile, draft tailored resumes and cover letters, export PDFs, and track application email—**you still submit applications yourself**; JobOps prepares the work and keeps state organized.

Licensed under **AGPLv3 + Commons Clause** — see [LICENSE](LICENSE).

![Product screenshot](https://github.com/user-attachments/assets/14fdc392-0e96-43be-bc1f-cf819ab2afc4)

## What’s in this repo

| Area | Role |
|------|------|
| **`orchestrator/`** | Express API, SQLite + Drizzle, pipeline, React (Vite) UI, LLM integrations, Reactive Resume PDF flow |
| **`shared/`** | Shared TypeScript types and settings registry |
| **`docs-site/`** | Docusaurus user and developer documentation |
| **`extractors/`** | Per-source crawlers (Adzuna, Gradcracker, UK Visa Jobs, Hiring Café, Startup Jobs, JobSpy helpers, etc.) |

Root `package.json` is an npm **workspace** root; day-to-day app commands usually run under `orchestrator/`.

## Features (high level)

- **Sources**: Multiple boards and aggregators (exact list evolves; see docs and extractor packages).
- **Scoring & tailoring**: LLM compares jobs to your resume profile; optional drafts for summary, headline, skills, and project selection. The scorer enforces hard caps when deal-breakers fire (e.g. clearance/citizenship conflicts cap score to 0-15) and includes candidate name and location in the profile sent to the model.
- **Cover letters**: LLM-generated, profile-aware cover letters. The prompt uses the full job-search profile (experience level, target roles, must-have and nice-to-have skills, work arrangement, location, salary, industries, deal-breakers) and produces 4-5 paragraphs (~550 words) signed with the candidate's name.
- **Job notes**: Per-listing personal notes with debounced auto-save, available in Discovered, Ready, and detail panels.
- **PDFs**: Tailored exports via **Reactive Resume** (v4 or v5 API). Optional **local JSON resume** (`JOBOPS_LOCAL_RESUME_PATH` or Settings) as the base document for profile/tailoring; PDF export still uses RxResume when configured.
- **Pipeline**: Scheduled or manual runs (`POST /api/pipeline/run`, webhook trigger).
- **Post-application**: Optional Gmail-based inbox for interview/offer/rejection signals.
- **Job list filters** (orchestrator UI): Narrow the pipeline job list by **multiple sources** and **countries** (country is inferred from each listing’s location text). Filters sync to the URL (`source`, `sourceExclude`, `countries`, `countriesExclude`). Each source/country chip cycles **off → include → exclude** (exclude shows in red); listings marked **remote** still pass country include/exclude rules.
- **Bulk actions**: Select-all / batch actions support up to **2 500 jobs** per request (raised from 100).
- **Data**: SQLite and generated artifacts under `./data` (default in Docker).

## Requirements

- **Node.js 22** (see `package.json` / Volta pin) for local development.
- **Docker + Compose v2** for the recommended production-style run.

## Quick start (Docker)

```bash
git clone <your-repo-url>
cd Jobber

cp .env.example .env
# Edit .env: MODEL / LLM keys, Reactive Resume or local resume path, optional BASIC_AUTH, etc.

docker compose up -d --build
```

- **UI**: `http://localhost:3005` (host port mapped in `docker-compose.yml`; app listens on **3001** inside the container).
- **Health**: `GET /health` on the same origin.

Persist data by backing up the mounted **`./data`** directory.

## Local development

From the repository root:

```bash
npm install
```

Then use the orchestrator workspace (see **`orchestrator/README.md`** for API tables and scripts):

```bash
cd orchestrator
cp .env.example .env
npm run db:migrate
npm run dev
```

Typical dev URLs: API **http://localhost:3001**, Vite client **http://localhost:5173**.

## Configuration notes

- **LLM**: Configurable provider (OpenRouter default; also OpenAI, OpenAI-compatible endpoints, Gemini, Ollama, LM Studio, etc.). Keys can be set in `.env` or onboarding.
- **Reactive Resume**: v5 API key or v4 email/password; optional self-hosted `RXRESUME_URL`. Full behavior is documented in the docs site under **Reactive Resume**.
- **Local resume JSON**: Set `JOBOPS_LOCAL_RESUME_PATH` (or the Settings field) to a Reactive Resume–shaped export so you do not need a selected cloud “base resume” for profile and tailoring. PDF generation still performs a temporary import/print through RxResume when credentials are present.

## Documentation

| Resource | Contents |
|----------|----------|
| **`docs-site/`** | User-facing guides (build with `npm run docs:dev` from repo root) |
| **`orchestrator/README.md`** | Orchestrator layout, endpoints, dev commands |
| **`DEPLOY_GITEA_VM_CRON_TELEGRAM.md`** | VM/container deploy, cron, optional Telegram |
| **`AGENTS.md`** | API/logging/SSE conventions for contributors and automation |

## Deploy, cron, Telegram

See **[DEPLOY_GITEA_VM_CRON_TELEGRAM.md](./DEPLOY_GITEA_VM_CRON_TELEGRAM.md)** for production deploy, scheduling `POST /api/pipeline/run`, and optional notifications.

## Verification (CI parity)

From the repo root, before merging substantial changes:

```bash
./orchestrator/node_modules/.bin/biome ci .
npm run check:types:shared
npm --workspace orchestrator run check:types
npm --workspace orchestrator run build:client
npm --workspace orchestrator run test:run
```

(Additional workspace typechecks apply in CI; see **`AGENTS.md`**.)

## License

**AGPLv3 + Commons Clause** — you may self-host, use, and modify; you may not sell the software or offer paid hosting or support whose value substantially comes from this codebase. See [LICENSE](LICENSE).
