DivApply: AI-assisted end-to-end job application pipeline
Project description
DivApply
DivApply is a local-first job application assistant. It can find jobs, enrich postings, score fit, tailor resumes, write cover letters, export PDFs, and optionally help with browser-based applications.
It is designed to stay factual: it works from your profile, resume, saved preferences, and optional coursework notes. It should not invent jobs, credentials, degrees, tools, dates, metrics, or experience.
What It Does
discover -> enrich -> score -> tailor -> cover -> pdf -> apply
- Finds jobs from configured searches and direct sources.
- Expands postings with full descriptions and application links.
- Scores jobs against your real experience and required criteria.
- Separates required qualifications from preferred/nice-to-have items.
- Generates one-page tailored resumes and cover letters.
- Exports safe job tracking data.
- Can run browser apply flows only when you confirm it.
Quick Start
Use Python 3.12 for the smoothest setup. The maintained Windows setup for this project currently uses Python 3.12.13. Python 3.14 may be installed on the same PC, but JobSpy-backed discovery is still expected to run from the Python 3.12 environment because of upstream dependency pins.
Recommended CLI install with pip:
python --version # expected on the maintained Windows setup: Python 3.12.13
python -m pip install --upgrade pip
python -m pip install "divapply[full]"
python -m pip install --no-deps "https://files.pythonhosted.org/packages/d5/2b/18863fcd3c544a69d81e351381a50036a33c21b61cc1c6de2a8f25931237/python_jobspy-1.1.82-py3-none-any.whl#sha256=93d638b35ffd30a714253e065907f68c5bac624e3937a3ad2ba09f618a072ee9"
python -m playwright install chromium
divapply init
divapply edit
divapply doctor
Virtualenv install, if you want the dependencies isolated:
python --version # expected on the maintained Windows setup: Python 3.12.13
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install "divapply[full]"
python -m pip install --no-deps "https://files.pythonhosted.org/packages/d5/2b/18863fcd3c544a69d81e351381a50036a33c21b61cc1c6de2a8f25931237/python_jobspy-1.1.82-py3-none-any.whl#sha256=93d638b35ffd30a714253e065907f68c5bac624e3937a3ad2ba09f618a072ee9"
python -m playwright install chromium
divapply init
divapply edit
divapply doctor
Alternative development install from the current GitHub main branch:
python --version # expected on the maintained Windows setup: Python 3.12.13
python -m pip install "divapply[full] @ git+https://github.com/InnitDivine/DivApply.git"
python -m pip install --no-deps "https://files.pythonhosted.org/packages/d5/2b/18863fcd3c544a69d81e351381a50036a33c21b61cc1c6de2a8f25931237/python_jobspy-1.1.82-py3-none-any.whl#sha256=93d638b35ffd30a714253e065907f68c5bac624e3937a3ad2ba09f618a072ee9"
Use divapply without extras for the lightest install. Use divapply[coursework] for PDF transcript import, divapply[jobspy-runtime] for the secure JobSpy runtime dependency floor, or divapply[full] for both coursework and JobSpy runtime dependencies. Because the current python-jobspy package pins markdownify<0.14.0, which conflicts with the CVE-2025-46656 fix in markdownify>=0.14.1, install DivApply's secure dependency floor first, then install the exact hash-verified JobSpy 1.1.82 wheel shown above with --no-deps. Run python -m divapply.jobspy_runtime to verify every installed JobSpy dependency bound. Until upstream relaxes that pin, pip check may still report the intentional Markdownify metadata conflict even when pip-audit and DivApply's runtime validator pass.
On Windows, DivApply installs plain divapply/divapply.cmd launchers instead of relying on a generated divapply.exe, so managed PCs that block generated console-script executables can still run divapply directly from cmd.
Auto-apply mode also needs Node.js 18+ and an agent CLI such as Codex or Claude Code. It uses DIVAPPLY_BROWSER when configured, otherwise it defaults to Playwright Chromium, which is installed by the quick-start command. Firefox is optional and only needed if you explicitly run divapply apply --browser firefox.
Clone Setup
Use this if you want the repo files locally.
git clone https://github.com/InnitDivine/DivApply.git
cd DivApply
.\install.ps1
Update an existing clone:
git pull
python -m pip install --upgrade -e ".[full]"
python -m pip install --no-deps "https://files.pythonhosted.org/packages/d5/2b/18863fcd3c544a69d81e351381a50036a33c21b61cc1c6de2a8f25931237/python_jobspy-1.1.82-py3-none-any.whl#sha256=93d638b35ffd30a714253e065907f68c5bac624e3937a3ad2ba09f618a072ee9"
First Run
divapply init
divapply edit
This creates your local app folder and setup files:
profile.json- applicant facts such as personal details, compensation, work history, education, and verified skillsresume.txt- master resume factssearches.yaml- job searches and filters.env- API keys and runtime settingsdivapply.db- local jobs database
These files are private and ignored by git.
Use divapply edit as the normal setup screen after initialization. It opens a local-only browser editor for:
- Personal/contact details
- Compensation and verified skills
- Work history, education, certifications, references, projects, and real metrics
- Search roles, schedule/work-type filters, locations, query tiers, job boards, and filters
The editor saves applicant facts to profile.json and job-search intent to searches.yaml, so most users do not need to hand-edit JSON or YAML.
Keep profile.json factual: identity, work authorization, pay answers, work history, education, verified skills, references, and reusable application answers. Do not put preferred job titles, employers, locations, reject lists, or search strategy in the profile; put those in searches.yaml so scoring is driven by the current search instead of stale profile prose.
Daily Workflow
divapply doctor
divapply run
divapply status
divapply export jobs --out jobs.csv
Useful targeted runs:
divapply run discover enrich
divapply run score tailor cover
divapply explain https://example.com/job
Use dry runs before any real browser submission:
divapply apply --dry-run
divapply apply --gen --url https://example.com/job
divapply apply --yes
Auto-apply launches a dedicated Chromium browser session for each worker, navigates to application pages, fills supported form fields from your profile and resume facts, uploads generated documents when available, answers screening questions through the selected agent backend, and shows progress in the live terminal dashboard.
The apply agent is launched without host shell access and with an exact browser-tool allowlist. CAPTCHA solving fails closed with RESULT:CAPTCHA; DivApply never puts a CAPTCHA-service key or host-command recipe in the agent prompt. Gmail automation is unavailable because the former integration is archived and its latest dependency tree has known High-severity advisories. Email-only applications and email verification therefore fail closed with RESULT:FAILED:email_required.
Cost-safe defaults are intentionally conservative: real auto-apply runs default to one job and one worker, and higher limits, multiple workers, unlimited continuous polling, or unbounded runs require --allow-expensive. Start with divapply apply --dry-run --limit 1, then use divapply apply --yes --limit 1 for the lowest-cost real run.
Saved job-site logins are local-only and belong in ~/.divapply/credentials.yaml, not profile.json or Git:
divapply credentials --username you@example.com
divapply credentials --site workdayjobs.com --username you@example.com
divapply credentials --show
Omit --password and DivApply prompts for it without echoing it. Use site-specific entries for employers that need a different login.
To avoid signing in repeatedly, open the same persistent browser profile used by auto-apply, sign in once, complete any two-factor challenge, then close the browser:
divapply browser-login --url https://www.myworkday.com/
divapply browser-login --url https://imh.wd108.myworkdayjobs.com/IntermountainCareers
Future divapply apply --workers 1 runs reuse the worker-0 profile cookies for the configured browser. Use the same --browser and --worker values for login and apply when overriding the configured browser.
DivApply creates blank, dedicated worker profiles; it never copies your personal Chrome profile or another worker's cookies. Chrome worker profiles created by older DivApply releases are intentionally refused because they may contain copied personal browser credentials. Preserve an old profile as a backup, create a fresh dedicated profile, and sign in intentionally:
Move-Item "$HOME\.divapply\chrome-workers\worker-0" "$HOME\.divapply\chrome-workers\worker-0.legacy-backup"
divapply browser-login --browser chrome --worker 0 --url https://www.myworkday.com/
The apply agent cannot execute page JavaScript or arbitrary browser code. DivApply records applied only when the agent's final three nonempty lines provide a matching submission origin, visible confirmation evidence, and exact RESULT:APPLIED; this remains a conservative model-reported check, so verify important submissions with the employer.
If Google says "This browser or app may not be secure", use real Chrome for both login and apply:
divapply browser-login --browser chrome --url https://www.myworkday.com/
divapply apply --browser chrome --backend codex --model gpt-5.4-mini --dry-run --limit 1 --workers 1
To make real Chrome the default saved-cookie browser, set DIVAPPLY_BROWSER=chrome in ~/.divapply/.env. Then both divapply browser-login and divapply apply use the Chrome worker profile unless you explicitly pass a different --browser.
Auto-apply has a bounded 45-minute per-job timeout by default, plus a 5-minute no-output timeout. Multi-step ATS flows can take several minutes; for fail-fast testing, set a shorter timeout for that session:
$env:DIVAPPLY_APPLY_TIMEOUT="900"
divapply apply --backend codex --model gpt-5.4-mini --dry-run --limit 1 --workers 1
Search Config
searches.yaml controls what DivApply looks for.
Example:
queries:
- query: "student assistant"
tier: 1
- query: "front desk"
tier: 2
- query: "customer service"
tier: 3
locations:
- location: "Exampletown, UT"
remote: false
- location: "Remote"
remote: true
country: "USA"
boards:
- indeed
- linkedin
defaults:
results_per_site: 50
hours_old: 168
Tier 1 should be your best, most specific searches. Tier 2 should be strong adjacent matches. Tier 3 should be broad fallback searches. Keep job titles in queries, places in locations, schedule intent in require_part_time, and boards in boards. DivApply derives normal location filtering from locations; manual filters such as location.accept_patterns, reject_patterns, exclude_titles, excluded_keywords, and customer-service-specific hour filters are optional escape hatches only when a board repeatedly returns results that need cleanup before scoring.
Older search configs still load, but divapply selfcheck warns on legacy aliases so you can migrate gradually:
search_terms -> queries, job_boards or sites -> boards, nearby_locations -> locations, reject_locations -> location.reject_patterns, target_titles -> include_titles, avoid_titles -> exclude_titles, and avoid_keywords -> excluded_keywords.
Resume Rules
Treat resume.txt as the source of truth.
- Keep it broad, truthful, and complete.
- Add only real work, projects, education, certifications, tools, and dates.
- Generated resumes are job-specific views of the master resume.
- Coursework can help matching, but it is not copied into resumes unless it belongs there.
Validation modes:
divapply run score tailor --validation strict
divapply run score tailor --validation normal
divapply run score tailor --validation lenient
Scoring
DivApply blends:
- Required/preferred keyword matching
- Local similarity scoring
- LLM judgment
Required gaps matter more than preferred gaps. Missing preferred certifications should not block a good job match. Missing required non-substitutable items, such as a required license, degree, or clearance, can cap the score.
Provider and transport failures do not become valid low scores: affected jobs remain retryable. LLM requests use separate network timeouts, at most three attempts, finite retry delays, and a 10-minute default total caller budget; advanced overrides are documented in docs/OPERATIONS.md and .env.example.
Inspect a job:
divapply explain https://example.com/job
Exports
divapply export jobs --out jobs.csv
divapply export jobs --out jobs.json --format json
Exports include safe fields such as title, company, source site, URLs, fit score, lifecycle dates, and redacted apply errors. They do not print your full profile, resume, transcript text, or secrets.
Answer Bank
Save reusable factual answers for application forms:
divapply answers add "How many years of Python experience do you have?" "2 years."
divapply answers list
divapply answers match "Years using Python?"
Answers are local-only and must stay supported by your profile or resume.
Coursework
Optional coursework data can improve matching without exposing transcript text.
divapply import-coursework path\to\transcript.json
divapply coursework-summary
Supported formats: JSON, CSV, text, and PDF if pypdf is installed.
Safety
- Real auto-apply requires confirmation.
- Use
--dry-runfirst on new sites. - Do not upload private files to git.
- Review generated resumes before sending them.
- Do not let the browser agent enter SSNs, bank info, payment details, ID uploads, or biometric verification.
- Treat Gmail MCP opt-in as account access: enable it only when email application/verification automation is required.
- CAPTCHA and unsupported authentication challenges stop the run instead of expanding agent permissions.
- Browser workers use dedicated profiles only; personal or legacy unmarked Chrome profiles are never launched automatically.
Privacy details: docs/PRIVACY.md
Common Commands
divapply init
divapply add-url JOB_URL
divapply edit
divapply doctor
divapply selfcheck
divapply run
divapply run -w 4
divapply credentials --username you@example.com
divapply browser-login --url https://www.myworkday.com/
divapply status
divapply track applied JOB_URL
divapply followups
divapply analytics
divapply dashboard
divapply explain JOB_URL
divapply rescore
divapply export jobs --out jobs.csv
divapply answers list
divapply backup
divapply cleanup
divapply apply --dry-run
divapply apply --yes
divapply migrate
divapply prune --dry-run
divapply sync --dry-run
divapply dashboard opens an interactive local dashboard. Applied jobs include an Archive button so you can hide submitted applications without deleting their history. Archiving also removes generated resume and cover-letter files for that job. Use divapply dashboard --static when you only want to write a standalone HTML file.
divapply cleanup previews stale local dashboard benchmark/perf HTML files and backup-style .bak files. Run divapply cleanup --yes to delete the previewed files, or add --include-backups when you also want old divapply-backup-*.zip archives removed from the local backups folder.
Apply logs use private user-only files and expire after 30 days by default; set DIVAPPLY_LOG_RETENTION_DAYS=0 to retain them. Recovery backups exclude logs and credentials by default. Add --include-logs and/or --include-secrets only when explicitly needed.
Development
git clone https://github.com/InnitDivine/DivApply.git
cd DivApply
.\install.ps1 -Dev
python --version # expected on the maintained Windows setup: Python 3.12.13
python -m pip install -e .
divapply --version
python -m divapply --version
python -m pytest -q
ruff check .
Release notes: CHANGELOG.md
Publishing notes: PUBLISHING.md
Operations notes: docs/OPERATIONS.md
AI assistance disclosure: docs/AI_ASSISTANCE.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file divapply-0.5.1.tar.gz.
File metadata
- Download URL: divapply-0.5.1.tar.gz
- Upload date:
- Size: 492.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25f9f11ed4758b2da95f389c7028abb4bad91e449d202ec7ee08a3b089575189
|
|
| MD5 |
86794c07139c6b632c5b90dfce77faff
|
|
| BLAKE2b-256 |
07a5848ecb2e57b7845abdaa191ce3d8f97fba0c9fda73fed134f4dbecf1e60d
|
Provenance
The following attestation bundles were made for divapply-0.5.1.tar.gz:
Publisher:
publish.yml on InnitDivine/DivApply
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
divapply-0.5.1.tar.gz -
Subject digest:
25f9f11ed4758b2da95f389c7028abb4bad91e449d202ec7ee08a3b089575189 - Sigstore transparency entry: 2160703605
- Sigstore integration time:
-
Permalink:
InnitDivine/DivApply@23d2b7c369ce4d50629c496be0d4c27220cc1fdf -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/InnitDivine
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@23d2b7c369ce4d50629c496be0d4c27220cc1fdf -
Trigger Event:
push
-
Statement type:
File details
Details for the file divapply-0.5.1-py3-none-any.whl.
File metadata
- Download URL: divapply-0.5.1-py3-none-any.whl
- Upload date:
- Size: 281.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25e12d1e9c9ec10d9b80877dbc599afae027c1777c6108183f075eb917aa123e
|
|
| MD5 |
4a4062fc890f90f1b76121f0883d9bff
|
|
| BLAKE2b-256 |
c986ee28e25f0d8c26f9dd44c393ab5368d29ef9abff9f4008a9bb02fa0ace6b
|
Provenance
The following attestation bundles were made for divapply-0.5.1-py3-none-any.whl:
Publisher:
publish.yml on InnitDivine/DivApply
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
divapply-0.5.1-py3-none-any.whl -
Subject digest:
25e12d1e9c9ec10d9b80877dbc599afae027c1777c6108183f075eb917aa123e - Sigstore transparency entry: 2160703736
- Sigstore integration time:
-
Permalink:
InnitDivine/DivApply@23d2b7c369ce4d50629c496be0d4c27220cc1fdf -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/InnitDivine
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@23d2b7c369ce4d50629c496be0d4c27220cc1fdf -
Trigger Event:
push
-
Statement type: