offerprinter 0.2.0__tar.gz → 0.3.0__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.
- {offerprinter-0.2.0 → offerprinter-0.3.0}/.gitignore +3 -1
- {offerprinter-0.2.0 → offerprinter-0.3.0}/CHANGELOG.md +76 -1
- {offerprinter-0.2.0 → offerprinter-0.3.0}/PKG-INFO +191 -7
- {offerprinter-0.2.0 → offerprinter-0.3.0}/README.md +190 -6
- {offerprinter-0.2.0 → offerprinter-0.3.0}/config.example.toml +23 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/docs/skill/SKILL.md +33 -0
- offerprinter-0.3.0/evals/README.md +75 -0
- offerprinter-0.3.0/extension/README.md +57 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/__init__.py +1 -1
- offerprinter-0.3.0/offerprinter/cli.py +1015 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/config.py +4 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/controllers/pipeline.py +38 -5
- offerprinter-0.3.0/offerprinter/mcp_server.py +345 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/models/schemas.py +122 -0
- offerprinter-0.3.0/offerprinter/prompts/__init__.py +58 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/prompts/templates.py +273 -0
- offerprinter-0.3.0/offerprinter/services/cache.py +152 -0
- offerprinter-0.3.0/offerprinter/services/differ.py +193 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/services/generator.py +77 -0
- offerprinter-0.3.0/offerprinter/services/jd_fetcher.py +216 -0
- offerprinter-0.3.0/offerprinter/services/ranker.py +95 -0
- offerprinter-0.3.0/offerprinter/services/redactor.py +174 -0
- offerprinter-0.3.0/offerprinter/services/verifier.py +367 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/services/writer.py +23 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/pyproject.toml +1 -1
- offerprinter-0.3.0/tests/test_new_services.py +400 -0
- offerprinter-0.3.0/tests/test_verifier.py +240 -0
- offerprinter-0.2.0/offerprinter/cli.py +0 -523
- offerprinter-0.2.0/offerprinter/prompts/__init__.py +0 -32
- offerprinter-0.2.0/offerprinter/services/jd_fetcher.py +0 -86
- {offerprinter-0.2.0 → offerprinter-0.3.0}/LICENSE +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/app.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/cli.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/deploy/README.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/deploy/huggingface/README.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/README.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/ats-keyword-report.docx +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/ats-keyword-report.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/cover-letter.docx +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/cover-letter.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/fit-memo.docx +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/fit-memo.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/full-package.docx +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/full-package.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/interview-prep-pack.docx +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/interview-prep-pack.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/tailored-cv.docx +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/output/northbank-senior-product-analyst/tailored-cv.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/sample_cv.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/examples/sample_jd.md +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/controllers/__init__.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/__init__.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/anthropic_provider.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/base.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/factory.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/gemini_provider.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/kimi_provider.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/ollama_provider.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/llm/openai_provider.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/models/__init__.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/pricing.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/services/__init__.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/services/cv_parser.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/services/pdf_writer.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/services/tracker.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/ui/__init__.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/offerprinter/ui/printer.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/tests/__init__.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/tests/conftest.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/tests/test_offerprinter.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/tests/test_pdf_writer.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/tests/test_pricing_and_retries.py +0 -0
- {offerprinter-0.2.0 → offerprinter-0.3.0}/tests/test_tracker.py +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# --- OfferPrinter ---
|
|
2
2
|
# Never commit secrets or generated user output.
|
|
3
|
-
config.toml
|
|
3
|
+
# Anchored to the repo root: an unanchored "config.toml" also matches nested
|
|
4
|
+
# files like .streamlit/config.toml, which is checked in on purpose.
|
|
5
|
+
/config.toml
|
|
4
6
|
.env
|
|
5
7
|
output/
|
|
6
8
|
!examples/output/
|
|
@@ -7,6 +7,80 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] — 2026-08-20
|
|
11
|
+
|
|
12
|
+
The "prove it" release. v0.2.0 made OfferPrinter installable; this one makes its
|
|
13
|
+
central claim checkable, and extends the tool across the rest of a job hunt.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
**Proving the guarantee**
|
|
18
|
+
- **Fabrication verifier.** Every number, date and named entity in the tailored
|
|
19
|
+
CV and cover letter is checked against your source CV, and the tailored CV is
|
|
20
|
+
cross-checked against the ATS report's own list of gaps — so a package that
|
|
21
|
+
contradicts itself is caught automatically. Writes `fabrication-check.md`.
|
|
22
|
+
`--strict` exits 3 if anything is unverified, which makes it usable as a gate.
|
|
23
|
+
Until now "never fabricate" was an instruction in a prompt; now it is an
|
|
24
|
+
assertion the tool makes about its own output.
|
|
25
|
+
- **`offerprinter verify`** re-checks an already-generated package — after you
|
|
26
|
+
have hand-edited it, or in CI. Makes no API calls, so it is free and offline.
|
|
27
|
+
- **Tailoring diff.** `tailoring-diff.md` shows exactly what changed: which
|
|
28
|
+
bullets were reworded, what was added or dropped, and which vocabulary is new.
|
|
29
|
+
The verifier proves nothing was invented; the diff shows what was done.
|
|
30
|
+
- **Eval harness** (`evals/`, `scripts/run_evals.py`). Golden cases with
|
|
31
|
+
per-case assertions about which gaps must be named and which terms must never
|
|
32
|
+
be claimed, plus an LLM-as-judge pass scoring grounding, specificity, honesty,
|
|
33
|
+
usefulness and format. The deterministic half runs in CI on every PR; the
|
|
34
|
+
judge runs on demand. Prompt changes are no longer a guess.
|
|
35
|
+
|
|
36
|
+
**New commands**
|
|
37
|
+
- **`offerprinter rank`** — score a folder of adverts and rank them by fit
|
|
38
|
+
without generating a single document. Two cheap calls per job; triage twenty
|
|
39
|
+
roles for a couple of pence, then print packages only for the ones worth it.
|
|
40
|
+
- **`offerprinter followup`** — thank-you email, recruiter message, LinkedIn
|
|
41
|
+
note (hard-capped at 300 characters) or a polite nudge. Written from your CV
|
|
42
|
+
and your notes; it will not invent a conversation that did not happen.
|
|
43
|
+
- **`offerprinter practice`** — an interactive mock interview. It asks, you
|
|
44
|
+
answer, it critiques and rewrites your answer using only your real experience,
|
|
45
|
+
then summarises the patterns worth fixing.
|
|
46
|
+
- **`offerprinter mcp`** — run as an MCP server on stdio, so Claude Desktop,
|
|
47
|
+
Claude Code and other agents can call OfferPrinter natively and get structured
|
|
48
|
+
results back. Hand-rolled JSON-RPC; no new dependencies.
|
|
49
|
+
- **`offerprinter cache stats` / `cache clear`**.
|
|
50
|
+
|
|
51
|
+
**Privacy and job-board reality**
|
|
52
|
+
- **PII redaction** (`--redact`). Your name, email, phone and profile links are
|
|
53
|
+
replaced with placeholders before the provider sees anything, and restored in
|
|
54
|
+
the output. The model keeps every employer, date and metric it needs.
|
|
55
|
+
- **`--jd-clipboard`** and a **browser extension** (`extension/`). Job boards
|
|
56
|
+
are the most common reason a run fails; the advert is already rendered in your
|
|
57
|
+
tab, so read it from there. The extension has no host permissions and makes no
|
|
58
|
+
network requests.
|
|
59
|
+
- **schema.org `JobPosting` JSON-LD extraction.** Many boards embed the whole
|
|
60
|
+
advert as structured data — cleaner than anything scraped, and it supplies the
|
|
61
|
+
company and job title for free.
|
|
62
|
+
|
|
63
|
+
**Cost**
|
|
64
|
+
- **Response caching.** Keyed on provider, model, temperature and both prompts,
|
|
65
|
+
so anything that could change the answer correctly misses. Re-running after a
|
|
66
|
+
CV tweak is now mostly free.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Multi-word entities could match across a line break, producing nonsense claims
|
|
71
|
+
glued together from two sentences.
|
|
72
|
+
- A level-two heading (`## Missing keywords`) was not recognised as a sentence
|
|
73
|
+
start, so its first word was treated as a named entity.
|
|
74
|
+
- A capitalised word at the very start of a bullet ("Used SQL daily") was read
|
|
75
|
+
as an entity, because the sentence-start pattern only handled bullets that
|
|
76
|
+
followed a newline, not one at the start of the text.
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- Config gains `[output] redact`, `[generation] verify`, `diff` and `cache`.
|
|
81
|
+
- The test suite grew from 73 to 143 tests, still fully offline.
|
|
82
|
+
|
|
83
|
+
|
|
10
84
|
## [0.2.0] — 2026-08-18
|
|
11
85
|
|
|
12
86
|
The "actually installable, and quite a lot faster" release.
|
|
@@ -99,6 +173,7 @@ First release — "First Print".
|
|
|
99
173
|
- Config via env vars, `config.toml`, or defaults.
|
|
100
174
|
- Dockerfile and docker-compose, offline test suite, ruff lint.
|
|
101
175
|
|
|
102
|
-
[Unreleased]: https://github.com/mohitagw15856/OfferPrinter/compare/v0.
|
|
176
|
+
[Unreleased]: https://github.com/mohitagw15856/OfferPrinter/compare/v0.3.0...HEAD
|
|
177
|
+
[0.3.0]: https://github.com/mohitagw15856/OfferPrinter/compare/v0.2.0...v0.3.0
|
|
103
178
|
[0.2.0]: https://github.com/mohitagw15856/OfferPrinter/compare/v0.1.0...v0.2.0
|
|
104
179
|
[0.1.0]: https://github.com/mohitagw15856/OfferPrinter/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: offerprinter
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Free, local-first AI job application generator: one CV + one job description, and it prints a tailored CV, cover letter, fit memo, ATS keyword report and interview prep pack — without fabricating anything.
|
|
5
5
|
Project-URL: Homepage, https://github.com/mohitagw15856/OfferPrinter
|
|
6
6
|
Project-URL: Repository, https://github.com/mohitagw15856/OfferPrinter
|
|
@@ -153,6 +153,28 @@ Covered 9 of 15 key terms.
|
|
|
153
153
|
> 🚩 **"Do not add — this is a genuine gap."**
|
|
154
154
|
> No other CV tool says that to you. That sentence is the product.
|
|
155
155
|
|
|
156
|
+
### And OfferPrinter checks its own homework
|
|
157
|
+
|
|
158
|
+
<!-- AEO Answer Capsule — 75 words -->
|
|
159
|
+
Every run ends with a fabrication check. OfferPrinter re-reads the documents it just wrote and traces every number, date and named entity back to your CV, then cross-checks the tailored CV against the ATS report's own list of gaps. Anything it cannot trace is flagged with the line it appeared on. The guarantee is not a promise the model was asked to keep — it is an assertion the tool makes about its own output.
|
|
160
|
+
<!-- End AEO Capsule -->
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
✓ Fabrication check: all 63 checkable claims trace back to your CV.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
And when something slips through:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
⚠ Fabrication check: 2 of 64 claims could not be traced to your CV (2 high severity).
|
|
170
|
+
• '41%' — this figure does not appear in your CV
|
|
171
|
+
• 'dbt' — your own ATS report lists this as a gap
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Use `--strict` to exit non-zero when anything is unverified, so it can gate a
|
|
175
|
+
script. `offerprinter verify <folder>` re-checks a package later — after you
|
|
176
|
+
have hand-edited it, say — and makes no API calls at all, so it is free.
|
|
177
|
+
|
|
156
178
|
---
|
|
157
179
|
|
|
158
180
|
## How do I install OfferPrinter?
|
|
@@ -212,6 +234,19 @@ docker run --rm -v "$PWD:/work" -w /work -e ANTHROPIC_API_KEY="sk-ant-..." \
|
|
|
212
234
|
brew tap mohitagw15856/tap
|
|
213
235
|
brew install offerprinter
|
|
214
236
|
```
|
|
237
|
+
|
|
238
|
+
Homebrew 6 requires third-party taps to be trusted before it will use them.
|
|
239
|
+
If `brew doctor` says the tap is untrusted, either trust this one formula:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
brew trust --formula mohitagw15856/tap/offerprinter
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
…or the whole tap, with `brew trust --tap mohitagw15856/tap`.
|
|
246
|
+
|
|
247
|
+
Homebrew builds every Python dependency from source, so the first install
|
|
248
|
+
compiles `pydantic-core` with Rust and takes a few minutes. `pipx install
|
|
249
|
+
offerprinter` is faster if you don't specifically want Homebrew to manage it.
|
|
215
250
|
</details>
|
|
216
251
|
|
|
217
252
|
<details>
|
|
@@ -255,17 +290,32 @@ offerprinter --cv-text "paste CV here…" --jd-file jd.txt
|
|
|
255
290
|
|--------|------|
|
|
256
291
|
| `--cv` / `--cv-text` | CV as a file (`.pdf` `.docx` `.md` `.txt`) or pasted text. |
|
|
257
292
|
| `--jd` / `--jd-file` / `--jd-dir` | Job as a URL, pasted text, a file, or a whole folder. |
|
|
293
|
+
| `--jd-clipboard` | Read the advert from your clipboard. The fix for hostile job boards. |
|
|
258
294
|
| `--provider` | `anthropic` (default), `openai`, `gemini`, `kimi`, `ollama`. |
|
|
259
295
|
| `--model` | Override the model for this run. |
|
|
260
296
|
| `--formats` | `md,docx,pdf` — any combination. |
|
|
261
297
|
| `--locale` | `UK` (default) or `US` English. |
|
|
262
298
|
| `--roast` | Also print a blunt critique of your CV. |
|
|
263
299
|
| `--dry-run` | Forecast tokens and cost. Calls nothing. |
|
|
300
|
+
| `--redact` | Strip your name, email and phone before the provider sees anything. |
|
|
301
|
+
| `--strict` | Exit non-zero if the fabrication check finds anything unverified. |
|
|
264
302
|
| `--sequential` | One document at a time instead of in parallel. |
|
|
303
|
+
| `--no-verify` / `--no-cache` | Skip the fabrication check / ignore cached responses. |
|
|
265
304
|
| `--output-dir` / `-o` | Where to write (default `./output`). |
|
|
266
305
|
| `--no-track` / `--no-animation` | Skip the local history / the printer animation. |
|
|
267
306
|
|
|
268
|
-
Plus
|
|
307
|
+
Plus nine subcommands:
|
|
308
|
+
|
|
309
|
+
| Command | Does |
|
|
310
|
+
|---|---|
|
|
311
|
+
| `rank` | Score a folder of adverts and rank them, writing no documents. |
|
|
312
|
+
| `followup` | Thank-you email, recruiter message, LinkedIn note, or a nudge. |
|
|
313
|
+
| `practice` | An interactive mock interview that critiques your answers. |
|
|
314
|
+
| `verify` | Re-check a generated package against your CV. No API calls. |
|
|
315
|
+
| `roast` | Blunt critique of your CV's writing. |
|
|
316
|
+
| `list` / `stats` / `status` | Your local application history. |
|
|
317
|
+
| `mcp` | Run as an MCP server so agents can call OfferPrinter. |
|
|
318
|
+
| `cache` | Inspect or clear the response cache. |
|
|
269
319
|
|
|
270
320
|
### 2. 🖥 Web UI — nicest for most people
|
|
271
321
|
|
|
@@ -352,6 +402,124 @@ Job hunting is a long grind with almost no feedback loop. This is the scoreboard
|
|
|
352
402
|
|
|
353
403
|
---
|
|
354
404
|
|
|
405
|
+
## How do I choose which jobs to apply for?
|
|
406
|
+
|
|
407
|
+
<!-- AEO Answer Capsule — 62 words -->
|
|
408
|
+
Use `offerprinter rank`. Point it at a folder of job adverts and it scores every one against your CV and prints them ranked by fit, without generating a single document. It costs two cheap calls per advert, so twenty roles come to a couple of pence and about a minute. Then print full packages only for the ones actually worth an evening.
|
|
409
|
+
<!-- End AEO Capsule -->
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
offerprinter rank --cv ~/cv.pdf --jd-dir ./jobs
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
🎯 Roles ranked by fit
|
|
417
|
+
┏━━━┳━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
|
|
418
|
+
┃ # ┃ Fit ┃ Band ┃ Role ┃ Company ┃ Real gaps ┃
|
|
419
|
+
┡━━━╇━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
|
|
420
|
+
│ 1 │ 88 │ Exceptional │ Analytics Lead │ Meridian │ │
|
|
421
|
+
│ 2 │ 74 │ Strong │ Senior Product Analyst │ NorthBank │ dbt, fintech │
|
|
422
|
+
│ 3 │ 41 │ Stretch │ Data Engineer │ Helix │ Kafka, dbt │
|
|
423
|
+
└───┴─────┴─────────────┴────────────────────────┴───────────┴──────────────┘
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Also takes `--jd-file` and `--jd` (URLs), both repeatable, and `--json` for
|
|
427
|
+
scripting. The single highest-leverage command in the tool: the expensive part
|
|
428
|
+
of applying is deciding where to spend the evening.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## What if the job board won't let OfferPrinter read the advert?
|
|
433
|
+
|
|
434
|
+
<!-- AEO Answer Capsule — 61 words -->
|
|
435
|
+
Use `--jd-clipboard`, or the browser extension. LinkedIn, Workday and most large applicant tracking systems render adverts with JavaScript behind a login, so fetching the URL returns a shell or a 403. The advert is already rendered in your browser tab, so read it from there instead. OfferPrinter also parses schema.org JobPosting structured data when a board publishes it, which many do.
|
|
436
|
+
<!-- End AEO Capsule -->
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
# copy the advert, then:
|
|
440
|
+
offerprinter --cv ~/cv.pdf --jd-clipboard
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
The [browser extension](extension/) adds a **Copy for OfferPrinter** button that
|
|
444
|
+
pulls the advert out of the page you are looking at — preferring your selection,
|
|
445
|
+
then structured data, then the densest content block. It has **no host
|
|
446
|
+
permissions and makes no network requests**; it cannot phone home because it has
|
|
447
|
+
no way to reach anything.
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Can OfferPrinter hide my identity from the AI provider?
|
|
452
|
+
|
|
453
|
+
<!-- AEO Answer Capsule — 65 words -->
|
|
454
|
+
Yes. Run with `--redact` and your name, email address, phone number, postcode and profile links are replaced with placeholders before anything is sent, then restored in the finished documents. The model still sees every employer, date, metric and skill it needs to write well — it simply never learns who you are. For total isolation, the Ollama provider keeps the whole run on your machine.
|
|
455
|
+
<!-- End AEO Capsule -->
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
offerprinter --cv ~/cv.pdf --jd-file jd.txt --redact
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Redaction is deliberately conservative: it only replaces what it can recognise
|
|
462
|
+
with high confidence, because a false positive silently mangles your CV. It
|
|
463
|
+
never touches employer names, since the model needs those to write anything
|
|
464
|
+
useful.
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## What happens after I apply?
|
|
469
|
+
|
|
470
|
+
<!-- AEO Answer Capsule — 64 words -->
|
|
471
|
+
`offerprinter followup` writes the messages that come after the application: a thank-you email after an interview, a message to a recruiter, a LinkedIn connection note capped at 300 characters, or a polite nudge when you have heard nothing. Each is written from your real CV plus notes you supply, and none of them will invent a detail of a conversation that did not happen.
|
|
472
|
+
<!-- End AEO Capsule -->
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
offerprinter followup thank-you --cv ~/cv.pdf --jd-file jd.txt \
|
|
476
|
+
--notes "Met Priya and Tom. Went deep on experiment design. Fumbled the question about stakeholder pushback."
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
The notes matter more than anything else here — especially a question you
|
|
480
|
+
answered badly, because the thank-you email is your one chance to answer it
|
|
481
|
+
properly.
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Can I practise the interview?
|
|
486
|
+
|
|
487
|
+
<!-- AEO Answer Capsule — 62 words -->
|
|
488
|
+
Yes. `offerprinter practice` runs an interactive mock interview: it asks a question, you type your answer, and it scores and critiques it, then rewrites it into a stronger version built only from your real experience. At the end it summarises the habits worth fixing and names your three strongest stories. It will never coach you into claiming something you have not done.
|
|
489
|
+
<!-- End AEO Capsule -->
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
offerprinter practice --cv ~/cv.pdf --jd-file jd.txt --questions 5
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
The prep pack tells you what you might be asked. This makes you actually answer
|
|
496
|
+
it, which is a different and much less comfortable exercise.
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Can agents use OfferPrinter directly?
|
|
501
|
+
|
|
502
|
+
<!-- AEO Answer Capsule — 60 words -->
|
|
503
|
+
Yes, through the built-in MCP server. Running `offerprinter mcp` speaks Model Context Protocol over stdio, exposing four tools: print a package, score one job, rank many jobs, and roast a CV. Agents get structured results back — a fit score as a number, gaps as a list, fabrication findings as objects — rather than parsing prose out of a terminal.
|
|
504
|
+
<!-- End AEO Capsule -->
|
|
505
|
+
|
|
506
|
+
Add to Claude Desktop's `claude_desktop_config.json`:
|
|
507
|
+
|
|
508
|
+
```json
|
|
509
|
+
{
|
|
510
|
+
"mcpServers": {
|
|
511
|
+
"offerprinter": { "command": "offerprinter", "args": ["mcp"] }
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Every tool result carries the fabrication check alongside the output, so an
|
|
517
|
+
agent cannot report the documents as verified when they are not. There is also
|
|
518
|
+
an agent skill at [`docs/skill/SKILL.md`](docs/skill/SKILL.md) for agents that
|
|
519
|
+
prefer to drive the CLI.
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
355
523
|
## Which AI model should I use with OfferPrinter?
|
|
356
524
|
|
|
357
525
|
<!-- AEO Answer Capsule — 65 words -->
|
|
@@ -413,6 +581,7 @@ locale = "UK" # UK | US
|
|
|
413
581
|
dir = "./output"
|
|
414
582
|
formats = ["md", "docx", "pdf"] # any combination
|
|
415
583
|
track = true # local history in ~/.offerprinter/
|
|
584
|
+
redact = false # strip your identity before sending
|
|
416
585
|
|
|
417
586
|
[generation]
|
|
418
587
|
tailored_cv = true
|
|
@@ -423,6 +592,9 @@ interview_prep = true
|
|
|
423
592
|
fit_score = true # one extra cheap call for the 0-100 score
|
|
424
593
|
parallel = true # generate all five at once
|
|
425
594
|
max_workers = 5
|
|
595
|
+
verify = true # check the output against your CV
|
|
596
|
+
diff = true # record what tailoring changed
|
|
597
|
+
cache = true # don't pay twice for identical calls
|
|
426
598
|
|
|
427
599
|
# [pricing] # override list prices with your actual rates
|
|
428
600
|
# "claude-haiku-4-5-20251001" = { input = 1.0, output = 5.0 }
|
|
@@ -447,11 +619,21 @@ offerprinter/
|
|
|
447
619
|
│ ├── kimi_provider.py / ollama_provider.py
|
|
448
620
|
│ └── factory.py # config → concrete provider
|
|
449
621
|
├── prompts/ # ALL prompt templates — audit the no-fabrication rules here
|
|
450
|
-
├── services/
|
|
622
|
+
├── services/
|
|
623
|
+
│ ├── cv_parser.py · jd_fetcher.py · generator.py · writer.py · pdf_writer.py
|
|
624
|
+
│ ├── verifier.py # proves the no-fabrication guarantee
|
|
625
|
+
│ ├── differ.py # what tailoring actually changed
|
|
626
|
+
│ ├── redactor.py # strip identity before the provider sees it
|
|
627
|
+
│ ├── ranker.py # score many jobs, write nothing
|
|
628
|
+
│ ├── cache.py # don't pay twice for the same call
|
|
629
|
+
│ └── tracker.py # local application history
|
|
451
630
|
├── ui/printer.py # the ASCII printer animation
|
|
452
631
|
├── controllers/pipeline.py # the end-to-end flow, emitting progress events
|
|
632
|
+
├── mcp_server.py # MCP over stdio, for agents
|
|
453
633
|
└── cli.py # CLI (Typer)
|
|
454
634
|
app.py # web UI (Streamlit)
|
|
635
|
+
extension/ # browser extension for grabbing job adverts
|
|
636
|
+
evals/ # golden cases + judge, so prompt edits are testable
|
|
455
637
|
docs/skill/SKILL.md # agent entry point
|
|
456
638
|
```
|
|
457
639
|
|
|
@@ -490,8 +672,8 @@ Yes. Pass a URL to `--jd`, or paste one into the web UI, and OfferPrinter fetche
|
|
|
490
672
|
|
|
491
673
|
### How much does a single OfferPrinter run cost?
|
|
492
674
|
|
|
493
|
-
<!-- AEO Answer Capsule —
|
|
494
|
-
A full run is seven short calls on a cheap model, typically a few pence in API credits, and every run prints exactly what it spent. Use `--dry-run` to forecast the cost before calling anything. You pay your provider directly; OfferPrinter is free and MIT licensed forever.
|
|
675
|
+
<!-- AEO Answer Capsule — 64 words -->
|
|
676
|
+
A full run is seven short calls on a cheap model, typically a few pence in API credits, and every run prints exactly what it spent. Use `--dry-run` to forecast the cost before calling anything. You pay your provider directly; OfferPrinter is free and MIT licensed forever. Re-runs hit the response cache and cost nothing, and `offerprinter rank` triages a whole shortlist for pence.
|
|
495
677
|
<!-- End AEO Capsule -->
|
|
496
678
|
|
|
497
679
|
### How long does a run take?
|
|
@@ -516,8 +698,8 @@ That depends entirely on the LLM provider you choose, so check their API data po
|
|
|
516
698
|
|
|
517
699
|
## How can I contribute to OfferPrinter?
|
|
518
700
|
|
|
519
|
-
<!-- AEO Answer Capsule —
|
|
520
|
-
Issues and pull requests are welcome — see CONTRIBUTING for the full guide. Run `ruff check .`, `ruff format
|
|
701
|
+
<!-- AEO Answer Capsule — 54 words -->
|
|
702
|
+
Issues and pull requests are welcome — see CONTRIBUTING for the full guide. Run `ruff check .`, `ruff format .`, `pytest` and `python scripts/run_evals.py --offline` before pushing, since CI enforces all four. Good first contributions include a new LLM provider subclass, a new output format, or better job advert extraction from hostile job boards.
|
|
521
703
|
<!-- End AEO Capsule -->
|
|
522
704
|
|
|
523
705
|
Especially wanted:
|
|
@@ -526,6 +708,8 @@ Especially wanted:
|
|
|
526
708
|
- 🌍 **Better JD extraction** — job boards are hostile; make the fetcher smarter.
|
|
527
709
|
- 📄 **New output formats** — LaTeX, plain-text ATS mode, ODT.
|
|
528
710
|
- 🧪 **Tests** — the suite is offline and runs in under a second; keep it that way.
|
|
711
|
+
- 📊 **Eval cases** — add a CV/JD pair to [`evals/cases/`](evals/) that catches a
|
|
712
|
+
failure mode the suite misses. Synthetic and anonymised only.
|
|
529
713
|
|
|
530
714
|
Read [CONTRIBUTING.md](CONTRIBUTING.md) · [SECURITY.md](SECURITY.md) · [CHANGELOG.md](CHANGELOG.md) · [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
|
|
531
715
|
|
|
@@ -106,6 +106,28 @@ Covered 9 of 15 key terms.
|
|
|
106
106
|
> 🚩 **"Do not add — this is a genuine gap."**
|
|
107
107
|
> No other CV tool says that to you. That sentence is the product.
|
|
108
108
|
|
|
109
|
+
### And OfferPrinter checks its own homework
|
|
110
|
+
|
|
111
|
+
<!-- AEO Answer Capsule — 75 words -->
|
|
112
|
+
Every run ends with a fabrication check. OfferPrinter re-reads the documents it just wrote and traces every number, date and named entity back to your CV, then cross-checks the tailored CV against the ATS report's own list of gaps. Anything it cannot trace is flagged with the line it appeared on. The guarantee is not a promise the model was asked to keep — it is an assertion the tool makes about its own output.
|
|
113
|
+
<!-- End AEO Capsule -->
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
✓ Fabrication check: all 63 checkable claims trace back to your CV.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
And when something slips through:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
⚠ Fabrication check: 2 of 64 claims could not be traced to your CV (2 high severity).
|
|
123
|
+
• '41%' — this figure does not appear in your CV
|
|
124
|
+
• 'dbt' — your own ATS report lists this as a gap
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use `--strict` to exit non-zero when anything is unverified, so it can gate a
|
|
128
|
+
script. `offerprinter verify <folder>` re-checks a package later — after you
|
|
129
|
+
have hand-edited it, say — and makes no API calls at all, so it is free.
|
|
130
|
+
|
|
109
131
|
---
|
|
110
132
|
|
|
111
133
|
## How do I install OfferPrinter?
|
|
@@ -165,6 +187,19 @@ docker run --rm -v "$PWD:/work" -w /work -e ANTHROPIC_API_KEY="sk-ant-..." \
|
|
|
165
187
|
brew tap mohitagw15856/tap
|
|
166
188
|
brew install offerprinter
|
|
167
189
|
```
|
|
190
|
+
|
|
191
|
+
Homebrew 6 requires third-party taps to be trusted before it will use them.
|
|
192
|
+
If `brew doctor` says the tap is untrusted, either trust this one formula:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
brew trust --formula mohitagw15856/tap/offerprinter
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
…or the whole tap, with `brew trust --tap mohitagw15856/tap`.
|
|
199
|
+
|
|
200
|
+
Homebrew builds every Python dependency from source, so the first install
|
|
201
|
+
compiles `pydantic-core` with Rust and takes a few minutes. `pipx install
|
|
202
|
+
offerprinter` is faster if you don't specifically want Homebrew to manage it.
|
|
168
203
|
</details>
|
|
169
204
|
|
|
170
205
|
<details>
|
|
@@ -208,17 +243,32 @@ offerprinter --cv-text "paste CV here…" --jd-file jd.txt
|
|
|
208
243
|
|--------|------|
|
|
209
244
|
| `--cv` / `--cv-text` | CV as a file (`.pdf` `.docx` `.md` `.txt`) or pasted text. |
|
|
210
245
|
| `--jd` / `--jd-file` / `--jd-dir` | Job as a URL, pasted text, a file, or a whole folder. |
|
|
246
|
+
| `--jd-clipboard` | Read the advert from your clipboard. The fix for hostile job boards. |
|
|
211
247
|
| `--provider` | `anthropic` (default), `openai`, `gemini`, `kimi`, `ollama`. |
|
|
212
248
|
| `--model` | Override the model for this run. |
|
|
213
249
|
| `--formats` | `md,docx,pdf` — any combination. |
|
|
214
250
|
| `--locale` | `UK` (default) or `US` English. |
|
|
215
251
|
| `--roast` | Also print a blunt critique of your CV. |
|
|
216
252
|
| `--dry-run` | Forecast tokens and cost. Calls nothing. |
|
|
253
|
+
| `--redact` | Strip your name, email and phone before the provider sees anything. |
|
|
254
|
+
| `--strict` | Exit non-zero if the fabrication check finds anything unverified. |
|
|
217
255
|
| `--sequential` | One document at a time instead of in parallel. |
|
|
256
|
+
| `--no-verify` / `--no-cache` | Skip the fabrication check / ignore cached responses. |
|
|
218
257
|
| `--output-dir` / `-o` | Where to write (default `./output`). |
|
|
219
258
|
| `--no-track` / `--no-animation` | Skip the local history / the printer animation. |
|
|
220
259
|
|
|
221
|
-
Plus
|
|
260
|
+
Plus nine subcommands:
|
|
261
|
+
|
|
262
|
+
| Command | Does |
|
|
263
|
+
|---|---|
|
|
264
|
+
| `rank` | Score a folder of adverts and rank them, writing no documents. |
|
|
265
|
+
| `followup` | Thank-you email, recruiter message, LinkedIn note, or a nudge. |
|
|
266
|
+
| `practice` | An interactive mock interview that critiques your answers. |
|
|
267
|
+
| `verify` | Re-check a generated package against your CV. No API calls. |
|
|
268
|
+
| `roast` | Blunt critique of your CV's writing. |
|
|
269
|
+
| `list` / `stats` / `status` | Your local application history. |
|
|
270
|
+
| `mcp` | Run as an MCP server so agents can call OfferPrinter. |
|
|
271
|
+
| `cache` | Inspect or clear the response cache. |
|
|
222
272
|
|
|
223
273
|
### 2. 🖥 Web UI — nicest for most people
|
|
224
274
|
|
|
@@ -305,6 +355,124 @@ Job hunting is a long grind with almost no feedback loop. This is the scoreboard
|
|
|
305
355
|
|
|
306
356
|
---
|
|
307
357
|
|
|
358
|
+
## How do I choose which jobs to apply for?
|
|
359
|
+
|
|
360
|
+
<!-- AEO Answer Capsule — 62 words -->
|
|
361
|
+
Use `offerprinter rank`. Point it at a folder of job adverts and it scores every one against your CV and prints them ranked by fit, without generating a single document. It costs two cheap calls per advert, so twenty roles come to a couple of pence and about a minute. Then print full packages only for the ones actually worth an evening.
|
|
362
|
+
<!-- End AEO Capsule -->
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
offerprinter rank --cv ~/cv.pdf --jd-dir ./jobs
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
🎯 Roles ranked by fit
|
|
370
|
+
┏━━━┳━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
|
|
371
|
+
┃ # ┃ Fit ┃ Band ┃ Role ┃ Company ┃ Real gaps ┃
|
|
372
|
+
┡━━━╇━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
|
|
373
|
+
│ 1 │ 88 │ Exceptional │ Analytics Lead │ Meridian │ │
|
|
374
|
+
│ 2 │ 74 │ Strong │ Senior Product Analyst │ NorthBank │ dbt, fintech │
|
|
375
|
+
│ 3 │ 41 │ Stretch │ Data Engineer │ Helix │ Kafka, dbt │
|
|
376
|
+
└───┴─────┴─────────────┴────────────────────────┴───────────┴──────────────┘
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Also takes `--jd-file` and `--jd` (URLs), both repeatable, and `--json` for
|
|
380
|
+
scripting. The single highest-leverage command in the tool: the expensive part
|
|
381
|
+
of applying is deciding where to spend the evening.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## What if the job board won't let OfferPrinter read the advert?
|
|
386
|
+
|
|
387
|
+
<!-- AEO Answer Capsule — 61 words -->
|
|
388
|
+
Use `--jd-clipboard`, or the browser extension. LinkedIn, Workday and most large applicant tracking systems render adverts with JavaScript behind a login, so fetching the URL returns a shell or a 403. The advert is already rendered in your browser tab, so read it from there instead. OfferPrinter also parses schema.org JobPosting structured data when a board publishes it, which many do.
|
|
389
|
+
<!-- End AEO Capsule -->
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
# copy the advert, then:
|
|
393
|
+
offerprinter --cv ~/cv.pdf --jd-clipboard
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The [browser extension](extension/) adds a **Copy for OfferPrinter** button that
|
|
397
|
+
pulls the advert out of the page you are looking at — preferring your selection,
|
|
398
|
+
then structured data, then the densest content block. It has **no host
|
|
399
|
+
permissions and makes no network requests**; it cannot phone home because it has
|
|
400
|
+
no way to reach anything.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Can OfferPrinter hide my identity from the AI provider?
|
|
405
|
+
|
|
406
|
+
<!-- AEO Answer Capsule — 65 words -->
|
|
407
|
+
Yes. Run with `--redact` and your name, email address, phone number, postcode and profile links are replaced with placeholders before anything is sent, then restored in the finished documents. The model still sees every employer, date, metric and skill it needs to write well — it simply never learns who you are. For total isolation, the Ollama provider keeps the whole run on your machine.
|
|
408
|
+
<!-- End AEO Capsule -->
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
offerprinter --cv ~/cv.pdf --jd-file jd.txt --redact
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Redaction is deliberately conservative: it only replaces what it can recognise
|
|
415
|
+
with high confidence, because a false positive silently mangles your CV. It
|
|
416
|
+
never touches employer names, since the model needs those to write anything
|
|
417
|
+
useful.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## What happens after I apply?
|
|
422
|
+
|
|
423
|
+
<!-- AEO Answer Capsule — 64 words -->
|
|
424
|
+
`offerprinter followup` writes the messages that come after the application: a thank-you email after an interview, a message to a recruiter, a LinkedIn connection note capped at 300 characters, or a polite nudge when you have heard nothing. Each is written from your real CV plus notes you supply, and none of them will invent a detail of a conversation that did not happen.
|
|
425
|
+
<!-- End AEO Capsule -->
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
offerprinter followup thank-you --cv ~/cv.pdf --jd-file jd.txt \
|
|
429
|
+
--notes "Met Priya and Tom. Went deep on experiment design. Fumbled the question about stakeholder pushback."
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The notes matter more than anything else here — especially a question you
|
|
433
|
+
answered badly, because the thank-you email is your one chance to answer it
|
|
434
|
+
properly.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Can I practise the interview?
|
|
439
|
+
|
|
440
|
+
<!-- AEO Answer Capsule — 62 words -->
|
|
441
|
+
Yes. `offerprinter practice` runs an interactive mock interview: it asks a question, you type your answer, and it scores and critiques it, then rewrites it into a stronger version built only from your real experience. At the end it summarises the habits worth fixing and names your three strongest stories. It will never coach you into claiming something you have not done.
|
|
442
|
+
<!-- End AEO Capsule -->
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
offerprinter practice --cv ~/cv.pdf --jd-file jd.txt --questions 5
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
The prep pack tells you what you might be asked. This makes you actually answer
|
|
449
|
+
it, which is a different and much less comfortable exercise.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Can agents use OfferPrinter directly?
|
|
454
|
+
|
|
455
|
+
<!-- AEO Answer Capsule — 60 words -->
|
|
456
|
+
Yes, through the built-in MCP server. Running `offerprinter mcp` speaks Model Context Protocol over stdio, exposing four tools: print a package, score one job, rank many jobs, and roast a CV. Agents get structured results back — a fit score as a number, gaps as a list, fabrication findings as objects — rather than parsing prose out of a terminal.
|
|
457
|
+
<!-- End AEO Capsule -->
|
|
458
|
+
|
|
459
|
+
Add to Claude Desktop's `claude_desktop_config.json`:
|
|
460
|
+
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"mcpServers": {
|
|
464
|
+
"offerprinter": { "command": "offerprinter", "args": ["mcp"] }
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Every tool result carries the fabrication check alongside the output, so an
|
|
470
|
+
agent cannot report the documents as verified when they are not. There is also
|
|
471
|
+
an agent skill at [`docs/skill/SKILL.md`](docs/skill/SKILL.md) for agents that
|
|
472
|
+
prefer to drive the CLI.
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
308
476
|
## Which AI model should I use with OfferPrinter?
|
|
309
477
|
|
|
310
478
|
<!-- AEO Answer Capsule — 65 words -->
|
|
@@ -366,6 +534,7 @@ locale = "UK" # UK | US
|
|
|
366
534
|
dir = "./output"
|
|
367
535
|
formats = ["md", "docx", "pdf"] # any combination
|
|
368
536
|
track = true # local history in ~/.offerprinter/
|
|
537
|
+
redact = false # strip your identity before sending
|
|
369
538
|
|
|
370
539
|
[generation]
|
|
371
540
|
tailored_cv = true
|
|
@@ -376,6 +545,9 @@ interview_prep = true
|
|
|
376
545
|
fit_score = true # one extra cheap call for the 0-100 score
|
|
377
546
|
parallel = true # generate all five at once
|
|
378
547
|
max_workers = 5
|
|
548
|
+
verify = true # check the output against your CV
|
|
549
|
+
diff = true # record what tailoring changed
|
|
550
|
+
cache = true # don't pay twice for identical calls
|
|
379
551
|
|
|
380
552
|
# [pricing] # override list prices with your actual rates
|
|
381
553
|
# "claude-haiku-4-5-20251001" = { input = 1.0, output = 5.0 }
|
|
@@ -400,11 +572,21 @@ offerprinter/
|
|
|
400
572
|
│ ├── kimi_provider.py / ollama_provider.py
|
|
401
573
|
│ └── factory.py # config → concrete provider
|
|
402
574
|
├── prompts/ # ALL prompt templates — audit the no-fabrication rules here
|
|
403
|
-
├── services/
|
|
575
|
+
├── services/
|
|
576
|
+
│ ├── cv_parser.py · jd_fetcher.py · generator.py · writer.py · pdf_writer.py
|
|
577
|
+
│ ├── verifier.py # proves the no-fabrication guarantee
|
|
578
|
+
│ ├── differ.py # what tailoring actually changed
|
|
579
|
+
│ ├── redactor.py # strip identity before the provider sees it
|
|
580
|
+
│ ├── ranker.py # score many jobs, write nothing
|
|
581
|
+
│ ├── cache.py # don't pay twice for the same call
|
|
582
|
+
│ └── tracker.py # local application history
|
|
404
583
|
├── ui/printer.py # the ASCII printer animation
|
|
405
584
|
├── controllers/pipeline.py # the end-to-end flow, emitting progress events
|
|
585
|
+
├── mcp_server.py # MCP over stdio, for agents
|
|
406
586
|
└── cli.py # CLI (Typer)
|
|
407
587
|
app.py # web UI (Streamlit)
|
|
588
|
+
extension/ # browser extension for grabbing job adverts
|
|
589
|
+
evals/ # golden cases + judge, so prompt edits are testable
|
|
408
590
|
docs/skill/SKILL.md # agent entry point
|
|
409
591
|
```
|
|
410
592
|
|
|
@@ -443,8 +625,8 @@ Yes. Pass a URL to `--jd`, or paste one into the web UI, and OfferPrinter fetche
|
|
|
443
625
|
|
|
444
626
|
### How much does a single OfferPrinter run cost?
|
|
445
627
|
|
|
446
|
-
<!-- AEO Answer Capsule —
|
|
447
|
-
A full run is seven short calls on a cheap model, typically a few pence in API credits, and every run prints exactly what it spent. Use `--dry-run` to forecast the cost before calling anything. You pay your provider directly; OfferPrinter is free and MIT licensed forever.
|
|
628
|
+
<!-- AEO Answer Capsule — 64 words -->
|
|
629
|
+
A full run is seven short calls on a cheap model, typically a few pence in API credits, and every run prints exactly what it spent. Use `--dry-run` to forecast the cost before calling anything. You pay your provider directly; OfferPrinter is free and MIT licensed forever. Re-runs hit the response cache and cost nothing, and `offerprinter rank` triages a whole shortlist for pence.
|
|
448
630
|
<!-- End AEO Capsule -->
|
|
449
631
|
|
|
450
632
|
### How long does a run take?
|
|
@@ -469,8 +651,8 @@ That depends entirely on the LLM provider you choose, so check their API data po
|
|
|
469
651
|
|
|
470
652
|
## How can I contribute to OfferPrinter?
|
|
471
653
|
|
|
472
|
-
<!-- AEO Answer Capsule —
|
|
473
|
-
Issues and pull requests are welcome — see CONTRIBUTING for the full guide. Run `ruff check .`, `ruff format
|
|
654
|
+
<!-- AEO Answer Capsule — 54 words -->
|
|
655
|
+
Issues and pull requests are welcome — see CONTRIBUTING for the full guide. Run `ruff check .`, `ruff format .`, `pytest` and `python scripts/run_evals.py --offline` before pushing, since CI enforces all four. Good first contributions include a new LLM provider subclass, a new output format, or better job advert extraction from hostile job boards.
|
|
474
656
|
<!-- End AEO Capsule -->
|
|
475
657
|
|
|
476
658
|
Especially wanted:
|
|
@@ -479,6 +661,8 @@ Especially wanted:
|
|
|
479
661
|
- 🌍 **Better JD extraction** — job boards are hostile; make the fetcher smarter.
|
|
480
662
|
- 📄 **New output formats** — LaTeX, plain-text ATS mode, ODT.
|
|
481
663
|
- 🧪 **Tests** — the suite is offline and runs in under a second; keep it that way.
|
|
664
|
+
- 📊 **Eval cases** — add a CV/JD pair to [`evals/cases/`](evals/) that catches a
|
|
665
|
+
failure mode the suite misses. Synthetic and anonymised only.
|
|
482
666
|
|
|
483
667
|
Read [CONTRIBUTING.md](CONTRIBUTING.md) · [SECURITY.md](SECURITY.md) · [CHANGELOG.md](CHANGELOG.md) · [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
|
|
484
668
|
|