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.
_ _ _
| | | | | |
| | ___ | |__ | |__ ___ _ __ _ __ ___ __ _ _ __
_ | |/ _ \ | '_ \| '_ \ / _ \ '__| '_ ` _ \ / _` | '_ \
| |__| | (_) | |_) | |_) | __/ | | | | | | | (_| | | | |
\____/ \___/|_.__/|_.__/ \___|_| |_| |_| |_|\__,_|_| |_|
| 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 |
# Clone and install globally
git clone https://github.com/joey727/Jobberman && cd Jobberman
pip install -e .Run the setup wizard to configure your Gemini API keys (multi-key support), SMTP settings for notifications, and your candidate profile.
job-search setupThis will create/update your .env and candidate.json files automatically.
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"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.txtThis will:
- Rotate Queries dynamically from your
open_to_rolesarray in round-robin fashion. - Scan RemoteOK, Remotive, and optionally LinkedIn every cycle for the active query.
- Pre-filter locally dropping any jobs mismatching your location and minimum salary bounds.
- Score jobs in batches against your profile and skip weak matches using the LLM.
- Tailor your resume and cover letter for qualified roles.
- Auto-apply to LinkedIn jobs when authenticated storage state is available.
- Fallback-email a package to you when auto-apply is not possible.
- Draft outreach for a few LinkedIn contacts at the target company.
- 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.
Jobberman uses a combination of environment variables, a candidate profile, and optional Playwright auth state.
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.
| 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. |
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.
candidate.json: structured candidate profileresume.txtor another readable resume path: fallback upload source for LinkedIn applystorage_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
| 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 |
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 |
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| 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 |
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:
- Put Gemini keys in SSM Parameter Store.
- Copy your candidate files into
data/profile/. - Export LinkedIn Playwright auth into
data/linkedin/state.json. - Set SMTP settings if you want email fallback.
- Run
deploy/bootstrap.shordocker compose up -d --build autopilot.
For EC2, you now have two supported config paths:
- Put secrets such as
GEMINI_API_KEYSandSMTP_*into SSM under/jobberman/..., then letdeploy/bootstrap.shpopulate missing env values. - Sync a local
.envand 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.jsonThe 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.
.dockerignoreexcludes caches, git data, and output folders to keep builds smaller.json-filelog rotation is used by default to reduce CloudWatch dependency and storage growth on free-tier setups.
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.
- 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.
MIT — see LICENSE.