NeraJob is a local-first toolkit to scan job listings, build a CV, and prepare applications — with pluggable scrapers and an optional Qt desktop app.
Product: mergeos-bounties/NeraJob
- Highlights
- Desktop GUI (Qt)
- Screenshots
- Supported job sources
- Quick start
- CLI reference
- Diagrams
- Repository layout
- Data layout
- Adding a job site
- Skill aliases
- Compliance
- Development
- MergeOS bounties
- License
| Area | What you get |
|---|---|
| Scan | Query one source or all scrapers; results cached in data/jobs.json |
| Profile | Local JSON profile as CV source of truth |
| CV | Markdown + plain-text export aimed at a target role |
| Apply | Per-job package: cover note, checklist, notes for manual apply |
| Extensible | Drop-in scrapers implementing BaseScraper + registry |
| Desktop GUI | Modern PySide6 app (nerajob-gui) |
Scan jobs, browse the local cache, edit profile, build a CV, and prepare apply packages.
Layout (v0.2.14+): sidebar nav · top bar with live stats · page headers with primary actions · scan form as a 2×2 card grid · job board as table + detail split · profile as two-column identity / summary.
pip install -e ".[gui]"
nerajob-gui
# or: nerajob gui| Page | What you do |
|---|---|
| Scan | Keywords, location, source, limit → run one source or all |
| Job board | Local cache table + side detail; send row to Apply |
| Profile | Name, headline, email, skills, summary → data/profile.json |
| Build CV | Target role → generate CV files |
| Apply pack | Job id → cover note + checklist package |
Scan — form card + activity log
Job board — table + detail panel
Profile — two-column layout
Build CV
Apply package
CLI demo captures:
| Scan | Sources | Jobs cache |
|---|---|---|
![]() |
![]() |
![]() |
| Sample scan | Registered scrapers | Jobs cache |
Full catalog (API links, env vars, bounty issues): docs/SOURCES.md.
--source |
Site | Type | Network | Auth / config |
|---|---|---|---|---|
sample |
Built-in demo feed | Offline fixtures | No | None |
remoteok |
RemoteOK | Public JSON API | Yes | Polite User-Agent |
lever |
Lever public postings | Per-company JSON | Optional | NERAJOB_LEVER_BOARD (slug). Without it: offline sample |
ashby |
Ashby public board | Per-company JSON | Optional | NERAJOB_ASHBY_BOARD (board id). Without it: offline sample |
# Offline demo / CI-friendly
nerajob scan --source sample -q python -n 10
# Live remote listings
nerajob scan --source remoteok -q "python backend" -n 20
# Lever / Ashby — set board env for live company boards
$env:NERAJOB_LEVER_BOARD = "netflix"
nerajob scan --source lever -q engineer -n 20
$env:NERAJOB_ASHBY_BOARD = "openai"
nerajob scan --source ashby -q python -n 20
# Every registered scraper
nerajob scan --all -q python -l remote -n 15| Source | Notes |
|---|---|
| sample | Deterministic roles for demos and tests. No HTTP. |
| remoteok | Live adapter for RemoteOK’s public feed. On failure may fall back to sample. |
| lever | Postings API: https://api.lever.co/v0/postings/<board>?mode=json |
| ashby | https://api.ashbyhq.com/posting-api/job-board/<board_id> |
Adapters below are not in the registry yet. Contribute via open issues labeled scraper / api.
| Planned | Site | Issue |
|---|---|---|
remotive |
Remotive | #2 |
arbeitnow |
Arbeitnow | #3 |
jobicy |
Jobicy | #4 |
himalayas |
Himalayas | #5 |
findwork |
Findwork.dev | #6 |
| Planned | Site | Issue |
|---|---|---|
adzuna |
Adzuna | #7 |
usajobs |
USAJOBS | #8 |
reed |
Reed.co.uk | #9 |
themuse |
The Muse | #10 |
jooble |
Jooble | #15 |
| Planned | ATS | Issue |
|---|---|---|
greenhouse |
Greenhouse Job Board API | #11 |
smartrecruiters |
SmartRecruiters | #14 |
Lever and Ashby are shipped. Remaining ATS boards still planned.
| Planned | Notes | Issue |
|---|---|---|
topcv / vietnamworks |
Prefer official / partner APIs | #17 |
| Work | Issue |
|---|---|
| Shared HTTP client, retries, rate limit, robots-aware policy | #18 |
scan --all aggregation + dedupe |
#19 |
| Multi-source pack: 5+ live scrapers with CI mocks | #22 |
cd NeraJob
python -m venv .venv
.\.venv\Scripts\activate
pip install -e ".[dev,gui]"
nerajob version
nerajob profile init
nerajob scan --source sample -q "python backend" -n 10
nerajob-guiStack: Python 3.11+ · Typer + Rich · httpx · BeautifulSoup4 · Pydantic v2 · local JSON under data/ · optional PySide6 GUI.
| Command | Purpose |
|---|---|
nerajob version |
Package version |
nerajob profile init / show |
Local profile (CV seed) |
nerajob scan --source … |
Scan one scraper |
nerajob scan --all |
All registered scrapers |
nerajob jobs list |
List cached jobs |
nerajob jobs match |
Rank cached jobs with configurable match weights |
nerajob cv build --target "…" |
Build Markdown + text CV |
nerajob apply prepare --job-id <id> |
Apply package for one job |
nerajob gui / nerajob-gui |
Qt desktop app (needs .[gui]) |
nerajob profile show
nerajob scan --source remoteok -q python -n 20
nerajob cv build --target "Backend Engineer"
nerajob apply prepare --job-id <id>
nerajob jobs list
nerajob jobs match --skill-weight 70 --title-weight 20 --location-weight 12Match scoring defaults to a 70-point skills cap, 20-point title/headline cap, and 12-point location/remote cap. Adjust those weights when a search should favor role wording or location fit over direct skill hits.
System architecture and workflow — full width. Open the HTML files for dark/light theme and export (PNG/SVG).
Generated with archify.
src/nerajob/
cli.py # Typer CLI entry
gui/ # PySide6 desktop demo
config.py # paths + HTTP settings
models.py # Job, Profile, Application models
storage.py # JSON persistence under data/
scrapers/
base.py # BaseScraper protocol
registry.py # built-in scrapers (keep SOURCES.md in sync)
sample.py # offline sample feed
remoteok.py # RemoteOK public API
lever.py # Lever public postings
ashby.py # Ashby public job board
cv/builder.py
apply/assistant.py
docs/SOURCES.md
docs/SKILL_ALIASES.md
docs/screenshots/
docs/diagrams/
data/
profile.json # profile / CV source of truth
jobs.json # scanned jobs cache
applications/ # per-job apply packages
data/ is gitignored except for example fixtures. Do not commit real CVs, API keys, or credentials.
- Create
src/nerajob/scrapers/mysite.pyimplementingBaseScraper.search(query, location, limit) - Register it in
scrapers/registry.py - Add tests under
tests/with mocked HTTP (CI must not depend on live network) - Update docs/SOURCES.md and the tables in this README
- Prefer official APIs; document rate limits and ToS notes in the PR
See docs/BOUNTY.md for MergeOS scraper bounty acceptance.
Skill aliases live in src/nerajob/match.py as SKILL_ALIASES — a dict[str, set[str]]. Each key is a canonical skill domain, and its set contains matching keywords used for resume ↔ job matching via expand_skills().
Add a new domain entry in SKILL_ALIASES:
"new_domain": {"new_domain", "alias1", "alias2", "alias3"},Then add a test in tests/test_skill_aliases.py:
def test_expand_skills_new_domain():
out = expand_skills({"new_domain"})
assert "alias1" in out
assert "alias2" in outVerify with the CLI:
nerajob skills | grep new_domain
pytest tests/test_skill_aliases.py -qRun nerajob skills to list all domains and their aliases.
| Domain key | Covers |
|---|---|
python |
django, fastapi, flask |
javascript |
js, typescript, node, react |
devops |
docker, kubernetes, k8s, ci/cd |
ml_ai |
machine learning, deep learning, nlp, pytorch, tensorflow |
data_engineering |
etl, spark, airflow, dbt, warehouse |
cybersecurity |
soc, siem, iam, infosec |
education |
teaching, curriculum, edtech, tutor |
(full list via nerajob skills) |
Add new domains that don't overlap with existing keys. Each domain set should be self-contained — aliases only expand inward, not across domains.
NeraJob expands profile skills with SKILL_ALIASES in src/nerajob/match.py
before scoring jobs. When adding a new skill domain, keep the alias set focused
and add tests that exercise expand_skills().
Contributor guide: docs/SKILL_ALIASES.md.
See SCRAPING_POLICY.md for ethical scraping guidelines and rate limit policies.
See docs/BOUNTY.md for bounty details.
- Prefer official / public APIs over brittle HTML scrapers
- Respect robots.txt, published rate limits, and site Terms of Service
- Never commit secrets, long-lived tokens, or production
.envvalues - Degrade gracefully on network failure (
[]+ optional sample fallback) - CI uses mocks — live smoke is optional and manual
Full policy: docs/ETHICAL_SCRAPING.md — covers principles, preferred official APIs, source-specific ToS notes, rate limit configuration, exponential backoff strategy, User-Agent standard, and a compliance checklist for new scrapers.
Details: docs/SOURCES.md § Compliance.
pytest -q
ruff check src tests
python scripts/capture_gui_shots.py # refresh GUI screenshotsOptional live smoke (network required):
nerajob scan --source remoteok -q python -n 5NeraJob issues labeled bounty pay MRG via MergeOS after merge.
- Read docs/BOUNTY.md
- Pick an open issue with
reward:*-mrg(high demand: scrapers — see tables above) - Star this repo + mergeos; claim on the issue and Claim Token #1
- Open a PR to this repo (
Fixes #N) - Maintainer merges and credits
github:<you>on the MergeOS ledger (25 / 50 / 100 / 200)
Docs catalog bounty: #23.
Roadmap: docs/ROADMAP.md · Sources: docs/SOURCES.md
MIT · MergeOS / ThanhTrucSolutions







