Skip to content

joey727/Jobberman

Repository files navigation

Jobberman — Agentic Job Search Assistant

Tests

An autonomous, AI-powered CLI tool that scans job boards, scores relevance, tailors your resume and cover letter, best-effort auto-applies, and prepares contact outreach drafts powered by Gemini.

       _       _     _
      | |     | |   | |
      | | ___ | |__ | |__   ___ _ __ _ __ ___   __ _ _ __
  _   | |/ _ \ | '_ \| '_ \ / _ \ '__| '_ ` _ \ / _` | '_ \
 | |__| | (_) | |_) | |_) |  __/ |  | | | | | | (_| | | | |
  \____/ \___/|_.__/|_.__/ \___|_|  |_| |_| |_|\__,_|_| |_|

Features

Feature Description
Interactive Setup job-search setup guides you through API, Email, and Profile config
API Key Rotation Uses GEMINI_API_KEYS to rotate and retry on 429 rate limits
Intelligent Pre-Filtering Fast local heuristics instantly drop jobs missing your salary/location baselines before using LLM credits
Dynamic Query Rotation Derives search terms directly from candidate.json and perfectly rotates via round-robin to avoid rate-limits
Batch Scoring Scores 5+ jobs in a single Gemini call for max efficiency, strictly evaluating your requested compensation metrics
Autopilot Mode Continuously scans job boards, scores, tailors, applies, emails fallback packages, and drafts outreach
Resume & Cover Letter Tailoring AI-generates tailored content (PDF & DOCX) per job
Portfolio Coaching Suggests projects to strengthen your application
Mock Interview Prep Generates role-specific questions with feedback
LinkedIn Scan + Auto-Apply Best-effort LinkedIn search and Easy Apply via Playwright
Contact Outreach Drafts Resolves LinkedIn company pages, collects a few contacts, and drafts simple messages
Storage Management Auto-prunes packages, rotates logs for production

Quick Start

1. Install

# Clone and install globally
git clone https://github.com/joey727/Jobberman && cd Jobberman
pip install -e .

2. Interactive Setup

Run the setup wizard to configure your Gemini API keys (multi-key support), SMTP settings for notifications, and your candidate profile.

job-search setup

This will create/update your .env and candidate.json files automatically.

3. Run a Single Job Analysis

job-search full \
  -j job_description.txt \
  -r resume.txt \
  --name "Joey Khalifa" \
  --email "josh@example.com" \
  --headline "Software Engineer" \
  --years-experience 5 \
  --skills "Python, Django, Typescript, AWS"

4. Start Autopilot

job-search autopilot \
  --query "Backend Engineer" \
  --sources remoteok,remotive,linkedin \
  --interval 30 \
  --min-score 60 \
  --max-packages 25 \
  --max-log-lines 2000 \
  --max-contacts 3 \
  --storage-state storage_state.json \
  --candidate-json candidate.json \
  -r resume.txt

This will:

  1. Rotate Queries dynamically from your open_to_roles array in round-robin fashion.
  2. Scan RemoteOK, Remotive, and optionally LinkedIn every cycle for the active query.
  3. Pre-filter locally dropping any jobs mismatching your location and minimum salary bounds.
  4. Score jobs in batches against your profile and skip weak matches using the LLM.
  5. Tailor your resume and cover letter for qualified roles.
  6. Auto-apply to LinkedIn jobs when authenticated storage state is available.
  7. Fallback-email a package to you when auto-apply is not possible.
  8. Draft outreach for a few LinkedIn contacts at the target company.
  9. Prune & Recover old packages, cleanly isolating failed sessions so networking anomalies retry on the next cycle implicitly instead of failing forever.

Stop with Ctrl+C — graceful shutdown guaranteed.


Configuration

Jobberman uses a combination of environment variables, a candidate profile, and optional Playwright auth state.

API Key Rotation

Get one or more free keys at Google AI Studio. Pass them as a comma-separated list to GEMINI_API_KEYS. Jobberman will automatically rotate keys if one hits a rate limit (429).

Copy .env.example to .env and fill in your values.

Environment Variables (.env)

Variable Description
GEMINI_API_KEYS Comma-separated list of Gemini API keys.
SUPPORTED_MODELS Optional allow-list of model aliases or concrete model names.
MODEL_MAP_DEEPSEAK / MODEL_MAP_MINMAX Optional alias-to-model mappings for mixed-key setups.
LLM_CACHE_DIR Cache directory for schema-parsed LLM responses.
LLM_CACHE_TTL Cache TTL in seconds.
QUERY Default autopilot search query.
SOURCES Comma-separated sources such as remoteok,remotive,linkedin.
INTERVAL_MINUTES Minutes between autopilot scan cycles.
MIN_SCORE Minimum relevance score before tailoring/apply.
MAX_JOBS Max jobs fetched per source per cycle.
MAX_PACKAGES Max retained job packages before pruning old ones.
MAX_LOG_LINES Log rotation threshold.
MAX_CONTACTS Max LinkedIn contacts to collect per matched company.
SCRAPE_CONTACT_EMAILS true to attempt email extraction from LinkedIn profiles.
SMTP_HOST SMTP server address (e.g., smtp.gmail.com).
SMTP_PORT SMTP port (defaults to 587).
SMTP_USER SMTP authentication user.
SMTP_PASSWORD SMTP authentication password.
SMTP_FROM "From" email address.
STORAGE_STATE Playwright auth state path for LinkedIn automation.
JOBBERMAN_IMAGE Optional remote image URI for EC2 image-based deployments.
ENV_SSM_PREFIX Optional SSM prefix used by deploy/bootstrap.sh for env sync, defaults to /jobberman.
ENV_SSM_KEYS Comma-separated env keys bootstrap should fetch from SSM when missing locally.

Candidate Profile (candidate.json)

Managed via job-search setup, this file stores your professional headline, experience, and skills to ensure consistent scoring. If you prefer to fill it manually, start from deploy/candidate.template.json.

Required Local Files For Autopilot

  • candidate.json: structured candidate profile
  • resume.txt or another readable resume path: fallback upload source for LinkedIn apply
  • storage_state.json: Playwright-authenticated LinkedIn state if you want LinkedIn scanning or auto-apply
  • SMTP settings: required if you want email fallback notifications from EC2

All CLI Commands

Command Description
setup Interactive wizard for API keys, email, and profile
full Single job: parse JD → tailor resume → portfolio → interview
autopilot Continuous bot: scan → score → tailor → apply/email fallback → outreach draft
run Batch scan from a source + tailor per job
apply LinkedIn Easy Apply from generated packages
apply-and-reach-out Apply + resolve company + collect contacts + outreach
export-auth Export Playwright authenticated session

Tests

All 36 tests pass with no API key required (Gemini calls are fully mocked):

pytest tests/ -v
Test Component Coverage
LLM Manager Rotation & 429 retry logic
Batch Scoring Multi-job relevance evaluation
Agents JD Parsing, Resume Tailoring, Coaching
Bot Lifecycle, deduplication, LinkedIn source support, tailored auto-apply
Sources Remotive query encoding and source adapters

Project Structure

job_search_agent/
├── agents/          # Gemini agents (parser, tailor, portfolio, interview)
├── automation/      # Playwright (LinkedIn apply, contacts, email)
├── sources/         # Scanners (RemoteOK, Remotive, WeWorkRemotely)
├── llm_client.py    # Multi-API key rotation & retry manager
├── bot.py           # Autonomous daemon loop (Batch processing)
├── scoring.py       # Batch relevance scorer
├── models.py        # Pydantic data models
├── orchestrator.py  # Multi-agent coordinator
├── cli.py           # CLI & Setup logic
└── terminal_ui.py   # Rich terminal rendering

Storage Management Flags

Flag Default Description
--max-packages 50 Auto-delete oldest packages beyond this limit
--max-log-lines 5000 Rotate log file when exceeding this line count
--max-contacts 3 Collect at most this many LinkedIn contacts per matched job in autopilot
--disable-outreach off Skip company resolution, contact collection, and outreach draft generation
--linkedin-headed off Force headed browser mode instead of EC2-friendly headless mode

Deployment

For a small EC2 instance, the simplest supported path is the single autopilot service in docker-compose.yml. It keeps CPU and memory flatter than running three independent source containers and keeps logs bounded with local rotation.

Recommended flow:

  1. Put Gemini keys in SSM Parameter Store.
  2. Copy your candidate files into data/profile/.
  3. Export LinkedIn Playwright auth into data/linkedin/state.json.
  4. Set SMTP settings if you want email fallback.
  5. Run deploy/bootstrap.sh or docker compose up -d --build autopilot.

For EC2, you now have two supported config paths:

  1. Put secrets such as GEMINI_API_KEYS and SMTP_* into SSM under /jobberman/..., then let deploy/bootstrap.sh populate missing env values.
  2. Sync a local .env and runtime files directly to a running instance:
bash deploy/push-instance-config.sh \
  --instance-id i-xxxxxxxxxxxxxxxxx \
  --include-source \
  --env-file .env \
  --candidate-json candidate.json \
  --resume-file resume.txt \
  --cover-letter-file cover_letter.txt \
  --storage-state storage_state.json

The sync helper normalizes .env files that use export KEY=..., can include the current local repo checkout for private-repo deployments, uploads the bundle temporarily through S3, expands it into /home/ubuntu/jobberman, and reruns bootstrap on the instance.

For an image-first EC2 flow, publish the image to ECR and inject it into the remote env:

IMAGE_URI="$(bash deploy/publish-image.sh --region us-east-1)"
bash deploy/push-instance-config.sh \
  --instance-id i-xxxxxxxxxxxxxxxxx \
  --env-file .env \
  --resume-file resume.txt \
  --set-env "JOBBERMAN_IMAGE=${IMAGE_URI}" \
  --set-env "AWS_REGION=us-east-1"

Container/runtime notes:

  • The compose service uses headless browser automation by default for EC2.
  • Screenshots, drafts, contacts, and tailored files are stored inside each package so pruning cleans them up.
  • .dockerignore excludes caches, git data, and output folders to keep builds smaller.
  • json-file log rotation is used by default to reduce CloudWatch dependency and storage growth on free-tier setups.

Disclaimer

Jobberman automates job search workflows including optional LinkedIn browsing and Easy Apply. Use at your own risk. LinkedIn and other sites may prohibit automated access in their terms of service. Respect rate limits, do not scrape aggressively, and always review tailored documents and outreach before sending. The authors provide no warranty and are not responsible for account restrictions or compliance issues arising from use of this tool.

Limitations

  • macOS Permissions: If reading files from iCloud/Mobile Documents, ensure your terminal has Full Disk Access or copy files to a local directory first.
  • LinkedIn DOM: Selectors change frequently; automation is provided as-is.
  • Authenticated Automation: LinkedIn scan/apply/outreach requires a valid storage_state.json.
  • SMTP Fallback: Manual-apply email fallback requires SMTP configuration.
  • Manual Review: Always review AI-generated resumes, cover letters, and outreach drafts before final submission.

License

MIT — see LICENSE.

Releases

Packages

Contributors

Languages