Stop Searching.
Start Automating.
Scrapes 3 platforms. Scores every job. Tailors your resume. Applies with one click. All self-hosted, all yours.
Your Command Center
Real-time dashboard for managing your entire job search pipeline


Smart Job Table
Filter by score, platform, status. Sort and search across all discovered jobs.
Instant Search
Full-text search across all job titles, companies, and descriptions with FTS5.
Multi-Filter
Combine score, platform, status, and date filters to zero in on the best matches.
What It Does
A complete pipeline from job discovery to application submission
Multi-Platform Scraping
Scrapes Indeed, Dice, and RemoteOK with Playwright stealth automation. Handles login, pagination, and deduplication.
Smart Filtering
Configurable search queries with location, salary, and keyword filters. YAML-driven configuration, no code changes needed.
AI-Powered Scoring
Claude CLI scores each job 1-5 against your candidate profile with explainable breakdowns and weighted criteria.
Resume Tailoring
Generates role-specific resumes via Claude with anti-fabrication validation. Never invents experience or qualifications.
Web Dashboard
FastAPI + htmx real-time dashboard with Kanban board, search management, and one-click apply controls.
Analytics & Insights
Score distribution, platform breakdown, salary ranges, and application funnel metrics at a glance.
One-Click Apply
Background apply engine with SSE progress streaming and human-in-the-loop confirmation gates.
Safety First
Never auto-submits without approval. CAPTCHA detection, credential gating, and screenshot debugging built in.
How It Works
A five-phase pipeline from configuration to application
Setup
Config & credentials
Login
Platform auth
Search
Job discovery
Score
AI match scoring
Apply
One-click submit
Under the Hood
Real code from the project — protocol-driven architecture with pluggable platforms
Platform Protocol
Runtime-checkable Protocol defines the contract every platform adapter must implement.
@runtime_checkable
class BrowserPlatform(Protocol):
"""Contract for browser-automated job platforms."""
platform_name: str
def init(self, context: BrowserContext) -> None: ...
def login(self) -> bool: ...
def is_logged_in(self) -> bool: ...
def search(self, query: SearchQuery) -> list[Job]: ...
def get_job_details(self, job: Job) -> Job: ...
def apply(self, job: Job, resume_path: Path | None = None) -> bool: ... Scoring Engine
Weighted multi-factor scoring rates each job 1–5 against the candidate profile.
class JobScorer:
"""Score jobs 1-5 against candidate profile."""
def _compute(self, job: Job) -> tuple[int, ScoreBreakdown]:
w = self.weights
title_pts = self._title_score(job.title)
tech_pts, tech_matched = self._tech_score_with_keywords(job)
remote_pts = self._location_score(job.location)
salary_pts = self._salary_score(job)
raw = (
title_pts * w.title_match / 2.0
+ tech_pts * w.tech_overlap / 2.0
+ remote_pts * w.remote
+ salary_pts * w.salary
)
total = 5 if raw >= 5 else 4 if raw >= 4 else ...
return total, ScoreBreakdown(
title_points=title_pts, tech_points=tech_pts,
tech_matched=tech_matched, remote_points=remote_pts,
salary_points=salary_pts, total=total,
) Orchestrator
Five-phase pipeline with error recovery, credential validation, and run history tracking.
def run(self, platforms: list[str] | None = None) -> None:
"""Execute the five-phase pipeline."""
if platforms is None:
platforms = self.settings.enabled_platforms()
for name in platforms:
get_platform(name) # Validates registration
try:
self.phase_0_setup()
self.phase_1_login(platforms)
self.phase_2_search(platforms)
self.phase_3_score()
self.phase_4_apply() # Human-in-the-loop
finally:
webdb.record_run(
status="success" if not self._run_errors else "partial",
total_raw=self._total_raw,
total_scored=len(self.discovered_jobs),
) Anti-Fabrication Guardrails
Claude CLI with strict rules preventing the AI from inventing experience or qualifications.
SYSTEM_PROMPT = """\
ABSOLUTE RULES — VIOLATION IS UNACCEPTABLE:
1. MUST NOT add skills, technologies, or achievements
not already in the original resume.
2. MUST NOT invent certifications, awards, or credentials.
3. MUST NOT change dates, company names, or job titles.
4. MUST NOT fabricate quantified results or metrics.
"""
async def tailor_resume(
resume_text: str, job_description: str,
job_title: str, company_name: str,
) -> TailoredResume:
"""Structured output with Pydantic schema validation."""
return await cli_run(
system_prompt=SYSTEM_PROMPT,
user_message=build_prompt(resume_text, job_title, company_name),
output_model=TailoredResume,
model="sonnet",
) SSE Apply Engine
Thread-safe event bridge from Playwright to FastAPI with human confirmation gates.
async def apply(self, job: dict, mode: str, queue: asyncio.Queue):
"""Serialize applies, stream events via SSE."""
async with self._semaphore:
loop = asyncio.get_running_loop()
emit = self._make_emitter(queue, loop)
await asyncio.to_thread(self._apply_sync, job, mode, emit)
def _make_emitter(self, queue, loop):
"""Bridge sync Playwright thread -> async FastAPI."""
def emit(event: ApplyEvent) -> None:
loop.call_soon_threadsafe(queue.put_nowait, event.model_dump())
return emit
# Confirmation gate in _apply_sync:
if config.confirm_before_submit:
emit(ApplyEvent(type=ApplyEventType.AWAITING_CONFIRM))
confirmed = self._confirmations[key].wait(timeout=300)
if not confirmed:
return # User declined in dashboard Built With
The tools that power the pipeline
Built in Public
From concept to production in one sprint — these principles guide every line
Protocol-Driven
Every platform adapter implements the same contract. Adding a new job board means creating a single file — the registry validates compliance at import time.
Safety by Default
Never auto-submits without explicit approval. CAPTCHA detection, credential gating, and screenshot debugging are built into every automation flow.
No Fabrication
Resume tailoring enforces strict rules. The AI cannot invent experience, certifications, metrics, or qualifications — validated before every generation.
Tested Thoroughly
581 tests across unit, integration, and E2E browser suites. 80%+ coverage with a CI pipeline running on every commit.
Quick Start
Up and running in five steps
Clone the repository
git clone https://github.com/PatrykQuantumNomad/jobs.git && cd jobs Install dependencies
pip install -r requirements.txt && playwright install chromium Configure your profile
cp config.example.yaml config.yaml
# Edit config.yaml with your search queries, target titles, and tech keywords Run the pipeline
python orchestrator.py --platforms indeed remoteok Open the dashboard
python -m webapp.app
# Visit http://localhost:8000