divapply 0.4.2__tar.gz → 0.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. {divapply-0.4.2 → divapply-0.4.3}/.gitignore +19 -0
  2. {divapply-0.4.2 → divapply-0.4.3}/CHANGELOG.md +8 -0
  3. {divapply-0.4.2 → divapply-0.4.3}/PKG-INFO +185 -8
  4. divapply-0.4.3/README.md +408 -0
  5. divapply-0.4.3/docs/PRIVACY.md +36 -0
  6. {divapply-0.4.2 → divapply-0.4.3}/install.ps1 +4 -0
  7. {divapply-0.4.2 → divapply-0.4.3}/install.sh +6 -1
  8. {divapply-0.4.2 → divapply-0.4.3}/pyproject.toml +1 -1
  9. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/__init__.py +1 -1
  10. divapply-0.4.3/src/divapply/apply/answers.py +113 -0
  11. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/apply/launcher.py +83 -564
  12. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/apply/prompt.py +122 -105
  13. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/cli.py +496 -9
  14. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/config/searches.example.yaml +9 -0
  15. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/config.py +49 -0
  16. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/database.py +407 -31
  17. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/discovery/jobspy.py +140 -9
  18. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/llm.py +45 -2
  19. divapply-0.4.3/src/divapply/migrations/__init__.py +74 -0
  20. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/pipeline.py +2 -2
  21. divapply-0.4.3/src/divapply/scoring/composite.py +144 -0
  22. divapply-0.4.3/src/divapply/scoring/context.py +16 -0
  23. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/cover_letter.py +2 -7
  24. divapply-0.4.3/src/divapply/scoring/embedding.py +49 -0
  25. divapply-0.4.3/src/divapply/scoring/keywords.py +209 -0
  26. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/pdf.py +31 -20
  27. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/scorer.py +116 -33
  28. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/tailor.py +61 -37
  29. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/ultimate.py +8 -19
  30. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/validator.py +33 -9
  31. divapply-0.4.3/tests/conftest.py +12 -0
  32. divapply-0.4.3/tests/test_answer_bank.py +24 -0
  33. divapply-0.4.3/tests/test_apply_launcher.py +114 -0
  34. divapply-0.4.3/tests/test_apply_prompt.py +48 -0
  35. divapply-0.4.3/tests/test_coursework_summary.py +105 -0
  36. divapply-0.4.3/tests/test_discovery_filters.py +72 -0
  37. divapply-0.4.3/tests/test_export_redaction.py +92 -0
  38. divapply-0.4.3/tests/test_hybrid_scoring.py +181 -0
  39. divapply-0.4.3/tests/test_lifecycle_and_dedup.py +70 -0
  40. divapply-0.4.3/tests/test_llm_aliases.py +17 -0
  41. divapply-0.4.3/tests/test_migrations.py +17 -0
  42. divapply-0.4.3/tests/test_pdf_template.py +79 -0
  43. divapply-0.4.3/tests/test_scoring_parser.py +38 -0
  44. divapply-0.4.3/tests/test_tailor_trace.py +19 -0
  45. divapply-0.4.3/tests/test_ultimate_resume.py +37 -0
  46. divapply-0.4.3/tests/test_validator_modes.py +94 -0
  47. {divapply-0.4.2 → divapply-0.4.3}/tools/bootstrap.ps1 +6 -1
  48. divapply-0.4.2/README.md +0 -231
  49. {divapply-0.4.2 → divapply-0.4.3}/.env.example +0 -0
  50. {divapply-0.4.2 → divapply-0.4.3}/.github/workflows/ci.yml +0 -0
  51. {divapply-0.4.2 → divapply-0.4.3}/.github/workflows/publish.yml +0 -0
  52. {divapply-0.4.2 → divapply-0.4.3}/AI_ASSISTANCE.md +0 -0
  53. {divapply-0.4.2 → divapply-0.4.3}/CONTRIBUTING.md +0 -0
  54. {divapply-0.4.2 → divapply-0.4.3}/LICENSE +0 -0
  55. {divapply-0.4.2 → divapply-0.4.3}/MIGRATION_CHECKLIST.md +0 -0
  56. {divapply-0.4.2 → divapply-0.4.3}/PUBLISHING.md +0 -0
  57. {divapply-0.4.2 → divapply-0.4.3}/install.cmd +0 -0
  58. {divapply-0.4.2 → divapply-0.4.3}/profile.example.json +0 -0
  59. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/__main__.py +0 -0
  60. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/apply/__init__.py +0 -0
  61. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/apply/chrome.py +0 -0
  62. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/apply/dashboard.py +0 -0
  63. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/config/coursework.seed.json +0 -0
  64. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/config/employers.yaml +0 -0
  65. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/config/sites.yaml +0 -0
  66. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/discovery/__init__.py +0 -0
  67. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/discovery/smartextract.py +0 -0
  68. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/discovery/workday.py +0 -0
  69. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/enrichment/__init__.py +0 -0
  70. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/enrichment/detail.py +0 -0
  71. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/scoring/__init__.py +0 -0
  72. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/social.py +0 -0
  73. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/view.py +0 -0
  74. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/wizard/__init__.py +0 -0
  75. {divapply-0.4.2 → divapply-0.4.3}/src/divapply/wizard/init.py +0 -0
  76. {divapply-0.4.2 → divapply-0.4.3}/tests/test_migration.py +0 -0
  77. {divapply-0.4.2 → divapply-0.4.3}/tests/test_tier.py +0 -0
  78. {divapply-0.4.2 → divapply-0.4.3}/tools/extract_transcript.ps1 +0 -0
@@ -1,19 +1,38 @@
1
1
  # User data (NEVER commit)
2
+ .divapply/
3
+ .applypilot/
2
4
  profile.json
3
5
  resume.txt
4
6
  resume.pdf
7
+ answers.yaml
5
8
  *.env
6
9
  .env.*
7
10
  !.env.example
11
+ coursework/
12
+ transcripts/
13
+ # Local search config can carry personal targeting rules; the example
14
+ # version (src/divapply/config/searches.example.yaml) stays tracked.
15
+ /searches.yaml
16
+ searches.local.yaml
8
17
 
9
18
  # Runtime artifacts
10
19
  *.db
20
+ *.db-wal
21
+ *.db-shm
22
+ *.sqlite
23
+ *.sqlite3
24
+ divapply.db*
25
+ applypilot.db*
11
26
  tailored_resumes/
12
27
  cover_letters/
13
28
  chrome-workers/
14
29
  apply-workers/
15
30
  apply_logs/
16
31
  logs/
32
+ *.log
33
+ screenshots/
34
+ social_screenshots/
35
+ .mcp-apply-*.json
17
36
  .venv/
18
37
  .vendor/
19
38
  academic_progress.txt
@@ -23,6 +23,14 @@ All notable changes to DivApply will be documented here.
23
23
  - Reworked the README around the simple pip-first install path.
24
24
  - Kept the GitHub clone installers as fallback/development options.
25
25
 
26
+ ## 0.4.3
27
+
28
+ - Fixed neutral job scoring edge cases for preferred qualifications and hard requirement gaps.
29
+ - Kept company and source/job-board fields separate in scoring, apply prompts, logs, traces, and exports.
30
+ - Fixed ultimate resume job selection to prioritize highest fit scores before recency.
31
+ - Hardened resume and cover letter HTML escaping for generated PDFs.
32
+ - Added regression coverage for scoring, apply prompt context, PDF template output, and safe exports.
33
+
26
34
  ## Unreleased
27
35
 
28
36
  - Future work will continue under the DivApply branding.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: divapply
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: DivApply: AI-assisted end-to-end job application pipeline
5
5
  Project-URL: Homepage, https://github.com/InnitDivine/DivApply
6
6
  Project-URL: Repository, https://github.com/InnitDivine/DivApply
@@ -75,35 +75,62 @@ DivApply runs in stages:
75
75
 
76
76
  ## Install
77
77
 
78
+ ### Python Version
79
+
80
+ Use Python 3.12 for the full DivApply setup.
81
+
82
+ DivApply core supports Python 3.11+, but full job-board discovery uses `python-jobspy`, and JobSpy currently pins `numpy==1.26.3`. That NumPy pin can fail on newer Python releases such as Python 3.13 and Python 3.14. If you want JobSpy-backed discovery, use Python 3.11 or 3.12; Python 3.12 is recommended.
83
+
84
+ Avoid Python 3.13/3.14 for the full setup until JobSpy updates its dependency pins.
85
+
86
+ Windows example:
87
+
88
+ ```bat
89
+ py -3.12 -m venv .venv
90
+ .venv\Scripts\activate
91
+ ```
92
+
93
+ macOS/Linux example:
94
+
95
+ ```bash
96
+ python3.12 -m venv .venv
97
+ source .venv/bin/activate
98
+ ```
99
+
78
100
  ### Quick Install
79
101
 
80
102
  Once DivApply is published to PyPI:
81
103
 
82
104
  ```bash
83
105
  pip install divapply
84
- pip install --no-deps python-jobspy && pip install pydantic tls-client requests markdownify regex
106
+ pip install --no-deps python-jobspy
107
+ pip install "numpy==1.26.3" "pandas>=2.1,<3.0" "markdownify>=0.13.1,<0.14.0" "regex>=2024.4.28,<2025.0.0" pydantic tls-client requests
85
108
  python -m playwright install chromium firefox
86
109
  divapply init # one-time setup: resume, profile, preferences, API keys
87
110
  divapply doctor # verify setup and show what's missing
88
111
  divapply run # discover > enrich > score > tailor > cover letters
89
112
  divapply run -w 4 # same but parallel
90
- divapply apply # autonomous browser-driven submission
113
+ divapply apply --dry-run # browser test without final submit
114
+ divapply apply --yes # confirmed autonomous browser-driven submission
91
115
  divapply apply -w 3 # parallel apply workers
92
- divapply apply --dry-run # fill forms without submitting
116
+ divapply selfcheck # offline local sanity check
93
117
  ```
94
118
 
95
119
  Before the first PyPI release is published, install directly from GitHub:
96
120
 
97
121
  ```bash
98
122
  pip install git+https://github.com/InnitDivine/DivApply.git
99
- pip install --no-deps python-jobspy && pip install pydantic tls-client requests markdownify regex
123
+ pip install --no-deps python-jobspy
124
+ pip install "numpy==1.26.3" "pandas>=2.1,<3.0" "markdownify>=0.13.1,<0.14.0" "regex>=2024.4.28,<2025.0.0" pydantic tls-client requests
100
125
  python -m playwright install chromium firefox
101
126
  ```
102
127
 
103
- Why the second install command? `python-jobspy` pins an exact NumPy version in its metadata that can fight pip's resolver. Installing JobSpy with `--no-deps`, then installing its real runtime dependencies separately, avoids that resolver trap.
128
+ Why the separate JobSpy install commands? `python-jobspy` pins an exact NumPy version in its metadata. Installing JobSpy with `--no-deps`, then installing the compatible runtime pins separately, avoids resolver conflicts and makes Python-version problems obvious.
104
129
 
105
130
  `divapply apply` also needs Node.js 18+ for `npx` and either the Codex CLI or Claude Code CLI.
106
131
 
132
+ Privacy details live in [docs/PRIVACY.md](docs/PRIVACY.md).
133
+
107
134
  ### Clone Install
108
135
 
109
136
  Use this if you want a local checkout, development setup, or the all-in-one installer.
@@ -167,20 +194,23 @@ bash ./install.sh --recreate
167
194
  bash ./install.sh --skip-browsers
168
195
  bash ./install.sh --skip-jobspy
169
196
  bash ./install.sh --browsers all
170
- bash ./install.sh --python /path/to/python3.11
197
+ bash ./install.sh --python /path/to/python3.12
171
198
  ```
172
199
 
200
+ The installer accepts Python 3.11+ for core DivApply. If JobSpy is enabled, it requires Python 3.11 or 3.12 and will stop early on newer Python versions with a clear message.
201
+
173
202
  ### Publishing To PyPI
174
203
 
175
204
  This repo includes a GitHub Actions workflow for PyPI Trusted Publishing. See [PUBLISHING.md](PUBLISHING.md) for the one-time PyPI setup and release commands.
176
205
 
177
206
  ### Manual Local Install
178
207
 
179
- If you already have a working Python 3.11+ environment:
208
+ If you already have a working Python 3.12 environment:
180
209
 
181
210
  ```powershell
182
211
  pip install ".[full]"
183
212
  pip install --no-deps python-jobspy
213
+ pip install "numpy==1.26.3" "pandas>=2.1,<3.0" "markdownify>=0.13.1,<0.14.0" "regex>=2024.4.28,<2025.0.0"
184
214
  python -m playwright install chromium firefox
185
215
  ```
186
216
 
@@ -189,6 +219,7 @@ For editable development:
189
219
  ```powershell
190
220
  pip install -e ".[dev,full]"
191
221
  pip install --no-deps python-jobspy
222
+ pip install "numpy==1.26.3" "pandas>=2.1,<3.0" "markdownify>=0.13.1,<0.14.0" "regex>=2024.4.28,<2025.0.0"
192
223
  python -m playwright install chromium firefox
193
224
  ```
194
225
 
@@ -227,9 +258,73 @@ divapply apply --dry-run
227
258
  divapply status
228
259
  divapply dashboard
229
260
  divapply import-coursework path\to\transcript.json
261
+ divapply coursework-summary
262
+ divapply export jobs --out jobs.csv
263
+ divapply explain https://example.com/job
264
+ divapply track screening https://example.com/job --follow-up 2026-05-15
265
+ divapply followups
266
+ divapply answers add "Years of Python?" "2 years."
267
+ divapply selfcheck
230
268
  divapply migrate
231
269
  ```
232
270
 
271
+ ## Master Resume Workflow
272
+
273
+ Treat `~/.divapply/resume.txt` as the master resume. Keep it broad, truthful, and complete enough to cover real work history, projects, skills, education, and certifications. DivApply never treats tailored resumes as new facts; each generated resume is a job-specific view of the master resume plus profile data and hidden coursework summaries.
274
+
275
+ Recommended loop:
276
+
277
+ 1. Update `resume.txt` and `profile.json` with only verified facts.
278
+ 2. Run `divapply run score tailor --validation strict` for high-risk roles or `--validation normal` for daily use.
279
+ 3. Review generated resumes and reports in `~/.divapply/tailored_resumes/`.
280
+ 4. Convert/export only after factuality and coherence checks pass.
281
+
282
+ Validation modes:
283
+
284
+ - `strict`: banned wording, structure issues, fabrication, and judge failures block output.
285
+ - `normal`: banned wording warns; fabrication and structure issues block output.
286
+ - `lenient`: skips style strictness and LLM judge, but keeps core fabrication checks.
287
+ - `none`: skips validation; use only for debugging.
288
+
289
+ Tailoring rules:
290
+
291
+ - Never invent jobs, employers, credentials, licenses, degrees, skills, coursework, tools, dates, or metrics.
292
+ - Rephrase and emphasize only truthful material already in `resume.txt`, `profile.json`, or hidden coursework summaries.
293
+ - Coursework can influence matching/tailoring decisions, but it is not copied into resumes unless it already belongs in the resume.
294
+
295
+ ## Scoring Transparency
296
+
297
+ Scoring is hybrid: keyword hit-rate + local hashed embedding similarity + the existing LLM judgment. Default weights are 30% keyword, 30% embedding, 40% LLM.
298
+
299
+ Scoring stores safe, human-readable fields:
300
+
301
+ - `fit_score`
302
+ - `llm_score`
303
+ - `keyword_score`
304
+ - `embedding_score`
305
+ - `composite_score`
306
+ - `score_breakdown`
307
+ - `score_reasoning`
308
+ - `matched_skills`
309
+ - `missing_skills`
310
+ - `keyword_hits`
311
+ - `risk_flags`
312
+ - `apply_or_skip_reason`
313
+
314
+ Use `divapply explain JOB_URL` to view the breakdown for one job. Existing legacy rows show missing hybrid fields until you run `divapply rescore`. Tailoring reads score gaps as targets, but only addresses a gap when the master resume/profile already supports it.
315
+
316
+ Status/export commands do not print full private resume, profile, transcript, or database text.
317
+
318
+ Stage-specific LLM aliases are supported:
319
+
320
+ ```bash
321
+ DIVAPPLY_LLM_SCORER=openai:gpt-5.4-mini
322
+ DIVAPPLY_LLM_TAILOR=ollama:qwen2.5:14b
323
+ DIVAPPLY_LLM_JUDGE=gemini:gemini-2.0-flash
324
+ ```
325
+
326
+ Old `LLM_MODEL_SCORE`, `LLM_MODEL_TAILOR`, and `LLM_MODEL_COVER` variables still work.
327
+
233
328
  ## Configuration
234
329
 
235
330
  DivApply reads local user data from your app directory:
@@ -248,6 +343,7 @@ You can add coursework with:
248
343
 
249
344
  ```bash
250
345
  divapply import-coursework path\to\file.json
346
+ divapply coursework-summary
251
347
  ```
252
348
 
253
349
  Supported import formats:
@@ -257,6 +353,87 @@ Supported import formats:
257
353
  - plain text
258
354
  - PDF transcripts if `pypdf` is installed
259
355
 
356
+ `divapply coursework-summary` shows only row count, schools, subject areas, inferred skills, and import sources. It never dumps transcript text.
357
+
358
+ ## Search Filters
359
+
360
+ `searches.yaml` supports safe filters:
361
+
362
+ ```yaml
363
+ filters:
364
+ company_blacklist:
365
+ - "Example Staffing"
366
+ title_blacklist:
367
+ - "intern"
368
+ required_keywords:
369
+ - "python"
370
+ excluded_keywords:
371
+ - "clearance"
372
+ remote_preference: "remote" # any, remote, hybrid, onsite
373
+ ```
374
+
375
+ Older keys still work: `exclude_titles`, `title_blacklist`, `company_blacklist`, `required_keywords`, `excluded_keywords`, and `remote_preference`.
376
+
377
+ ## Export And Tracking
378
+
379
+ Export safe job tracking fields:
380
+
381
+ ```bash
382
+ divapply export jobs --out jobs.csv
383
+ divapply export jobs --out jobs.json --format json
384
+ ```
385
+
386
+ Exported columns: title, site, url, application_url, fit_score, apply_status, discovered_at, scored_at, tailored_at, applied_at, and a redacted apply_error snippet.
387
+
388
+ Track lifecycle events after applying:
389
+
390
+ ```bash
391
+ divapply track applied https://example.com/job
392
+ divapply track screening https://example.com/job --follow-up 2026-05-15 --notes "Recruiter screen"
393
+ divapply track interview https://example.com/job
394
+ divapply track offer https://example.com/job
395
+ divapply track rejection https://example.com/job
396
+ divapply followups
397
+ divapply analytics
398
+ ```
399
+
400
+ Lifecycle data is append-only in `application_events`. The current state is mirrored to the jobs table for status/dashboard compatibility.
401
+
402
+ ## Answer Bank
403
+
404
+ `~/.divapply/answers.yaml` stores reusable answers for employer form questions. It is local-only and ignored by git.
405
+
406
+ ```bash
407
+ divapply answers add "How many years of Python experience do you have?" "2 years."
408
+ divapply answers list
409
+ divapply answers match "Years using Python?"
410
+ ```
411
+
412
+ The apply agent receives the answer bank in its prompt and reuses close matches for differently worded questions. Answers must stay factual and supported by the profile/resume.
413
+
414
+ ## Auto-Apply Safety
415
+
416
+ No real submissions are run unless you confirm them.
417
+
418
+ ```bash
419
+ divapply apply --dry-run
420
+ divapply apply --gen --url https://example.com/job
421
+ divapply apply --mark-applied https://example.com/job
422
+ divapply apply --mark-failed https://example.com/job --fail-reason "manual review"
423
+ divapply apply --reset-failed
424
+ divapply apply --yes
425
+ ```
426
+
427
+ Use `--dry-run` first on new sites. `--gen --url ...` writes a prompt for manual review. `--yes` confirms real browser submission mode.
428
+
429
+ ## Offline Selfcheck
430
+
431
+ ```bash
432
+ divapply selfcheck
433
+ ```
434
+
435
+ Selfcheck verifies imports, Python version, JobSpy import, config paths, DB init, coursework count, and local directories. It does not call job boards, LLMs, browsers, apply agents, or external sites.
436
+
260
437
  ## Build
261
438
 
262
439
  ```bash