honest-cv 0.2.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.
Files changed (76) hide show
  1. honest_cv-0.2.0/.gitignore +11 -0
  2. honest_cv-0.2.0/CHANGELOG.md +142 -0
  3. honest_cv-0.2.0/CONTRIBUTING.md +73 -0
  4. honest_cv-0.2.0/LICENSE +21 -0
  5. honest_cv-0.2.0/PKG-INFO +364 -0
  6. honest_cv-0.2.0/README.es.md +343 -0
  7. honest_cv-0.2.0/README.md +332 -0
  8. honest_cv-0.2.0/examples/ada/bullets.yaml +113 -0
  9. honest_cv-0.2.0/examples/ada/job-keywords.yaml +4 -0
  10. honest_cv-0.2.0/examples/ada/job-post.md +22 -0
  11. honest_cv-0.2.0/examples/ada/profile.yaml +49 -0
  12. honest_cv-0.2.0/examples/ada/skills.yaml +20 -0
  13. honest_cv-0.2.0/examples/ada/variants.yaml +73 -0
  14. honest_cv-0.2.0/examples/checklists/project-management.md +52 -0
  15. honest_cv-0.2.0/pyproject.toml +75 -0
  16. honest_cv-0.2.0/skills/honest-cv/SKILL.md +193 -0
  17. honest_cv-0.2.0/src/honest_cv/__init__.py +3 -0
  18. honest_cv-0.2.0/src/honest_cv/applications.py +136 -0
  19. honest_cv-0.2.0/src/honest_cv/build.py +51 -0
  20. honest_cv-0.2.0/src/honest_cv/checklist.py +570 -0
  21. honest_cv-0.2.0/src/honest_cv/checklists/backend-go.md +63 -0
  22. honest_cv-0.2.0/src/honest_cv/checklists/backend-jvm.md +90 -0
  23. honest_cv-0.2.0/src/honest_cv/checklists/backend-python.md +70 -0
  24. honest_cv-0.2.0/src/honest_cv/checklists/data.md +65 -0
  25. honest_cv-0.2.0/src/honest_cv/checklists/engineering-lead.md +51 -0
  26. honest_cv-0.2.0/src/honest_cv/checklists/frontend.md +62 -0
  27. honest_cv-0.2.0/src/honest_cv/checklists/platform-sre.md +81 -0
  28. honest_cv-0.2.0/src/honest_cv/checklists/software-architecture.md +62 -0
  29. honest_cv-0.2.0/src/honest_cv/cli.py +481 -0
  30. honest_cv-0.2.0/src/honest_cv/document.py +172 -0
  31. honest_cv-0.2.0/src/honest_cv/export.py +119 -0
  32. honest_cv-0.2.0/src/honest_cv/importer.py +384 -0
  33. honest_cv-0.2.0/src/honest_cv/letter.py +286 -0
  34. honest_cv-0.2.0/src/honest_cv/linkedin.py +142 -0
  35. honest_cv-0.2.0/src/honest_cv/model.py +390 -0
  36. honest_cv-0.2.0/src/honest_cv/prompts/cover_letter.md +40 -0
  37. honest_cv-0.2.0/src/honest_cv/prompts/import_linkedin_pdf.md +28 -0
  38. honest_cv-0.2.0/src/honest_cv/prompts/interview.md +100 -0
  39. honest_cv-0.2.0/src/honest_cv/prompts/linkedin_profile.md +30 -0
  40. honest_cv-0.2.0/src/honest_cv/prompts/tailor_for_job.md +61 -0
  41. honest_cv-0.2.0/src/honest_cv/render/__init__.py +0 -0
  42. honest_cv-0.2.0/src/honest_cv/render/docx.py +99 -0
  43. honest_cv-0.2.0/src/honest_cv/render/fonts/Carlito-Bold.ttf +0 -0
  44. honest_cv-0.2.0/src/honest_cv/render/fonts/Carlito-Regular.ttf +0 -0
  45. honest_cv-0.2.0/src/honest_cv/render/fonts/OFL.txt +94 -0
  46. honest_cv-0.2.0/src/honest_cv/render/pdf.py +84 -0
  47. honest_cv-0.2.0/src/honest_cv/render/templates/classic.typ +95 -0
  48. honest_cv-0.2.0/src/honest_cv/render/templates/letter.typ +46 -0
  49. honest_cv-0.2.0/src/honest_cv/score.py +283 -0
  50. honest_cv-0.2.0/src/honest_cv/server.py +974 -0
  51. honest_cv-0.2.0/src/honest_cv/store.py +365 -0
  52. honest_cv-0.2.0/src/honest_cv/templates/profile/bullets.yaml +10 -0
  53. honest_cv-0.2.0/src/honest_cv/templates/profile/profile.yaml +34 -0
  54. honest_cv-0.2.0/src/honest_cv/templates/profile/skills.yaml +8 -0
  55. honest_cv-0.2.0/src/honest_cv/templates/profile/variants.yaml +17 -0
  56. honest_cv-0.2.0/src/honest_cv/verify.py +177 -0
  57. honest_cv-0.2.0/tests/__init__.py +0 -0
  58. honest_cv-0.2.0/tests/conftest.py +32 -0
  59. honest_cv-0.2.0/tests/fixtures/resume.json +73 -0
  60. honest_cv-0.2.0/tests/test_applications.py +114 -0
  61. honest_cv-0.2.0/tests/test_checklist.py +320 -0
  62. honest_cv-0.2.0/tests/test_export.py +74 -0
  63. honest_cv-0.2.0/tests/test_honesty.py +70 -0
  64. honest_cv-0.2.0/tests/test_import.py +176 -0
  65. honest_cv-0.2.0/tests/test_letter.py +120 -0
  66. honest_cv-0.2.0/tests/test_linkedin.py +87 -0
  67. honest_cv-0.2.0/tests/test_model.py +91 -0
  68. honest_cv-0.2.0/tests/test_prompts.py +78 -0
  69. honest_cv-0.2.0/tests/test_release.py +26 -0
  70. honest_cv-0.2.0/tests/test_render_ats.py +180 -0
  71. honest_cv-0.2.0/tests/test_score.py +140 -0
  72. honest_cv-0.2.0/tests/test_server.py +386 -0
  73. honest_cv-0.2.0/tests/test_smoke.py +10 -0
  74. honest_cv-0.2.0/tests/test_store.py +206 -0
  75. honest_cv-0.2.0/tests/test_templates.py +80 -0
  76. honest_cv-0.2.0/tests/test_verify.py +74 -0
@@ -0,0 +1,11 @@
1
+ # Perfiles reales: NUNCA al repo. Solo examples/ (ficticio) se versiona.
2
+ profiles/
3
+ out/
4
+ *.pdf
5
+ *.docx
6
+ !examples/**/*.pdf
7
+ .venv/
8
+ __pycache__/
9
+ *.egg-info/
10
+ dist/
11
+ .DS_Store
@@ -0,0 +1,142 @@
1
+ # Changelog
2
+
3
+ All notable changes to honest-cv. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
+ and versions follow [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [0.2.0] — 2026-10-15
7
+
8
+ Closed on 2026-10-15 after a full test as a new user. The first release on PyPI.
9
+
10
+ ### Added
11
+
12
+ - `export_json_resume` and `honest-cv export`: a variant as JSON Resume, in one language. X skills
13
+ never appear and F skills keep their label; importing the file back gives the same profile.
14
+ - Your own checklist templates, for any profession: files in `<profile>/checklists/` show up in
15
+ `checklist_list` and combine with the built-in ones in `checklist_new`, which also takes a path.
16
+ - `checklist_apply` returns `skills_without_achievements`, the P and L skills no bullet shows, and
17
+ `key_skills_without_achievements`, the P ones in the ★ section or a variant. The assistant asks
18
+ first for the person's biggest achievements, then for the key skills still uncovered. The rest
19
+ wait for a job post: `gap_report` → `no_achievement` lists the ones it asks for.
20
+ - Templates can declare synonyms (`same: Work Breakdown Structure = WBS = EDT`). Those in
21
+ `<profile>/checklists/` count in the job score, the gaps, checklist merging and the letter check,
22
+ which also learns their skill names, so it works for any profession.
23
+ - An example template for another profession: `examples/checklists/project-management.md`.
24
+ - A built-in `software-architecture` checklist: styles and patterns, integration, quality
25
+ attributes, data architecture and the practice (ADRs, reviews, technical debt). New synonyms:
26
+ ADRs, ports and adapters, diseño de sistemas, CDC.
27
+ - Application tracking in `<profile>/applications.yaml`: `log_application`, `list_applications`
28
+ and `honest-cv apps log|list`. Each status change keeps its date; open applications with no
29
+ news for 14 days are listed as waiting.
30
+ Each application keeps the job post's text in `<profile>/posts/`, since links expire, and the
31
+ points to prepare for its interview (`prep`).
32
+ - LinkedIn texts from a variant, within LinkedIn's limits: `linkedin_profile` (tool and prompt) and
33
+ `honest-cv linkedin`. Headline, About, one description per position and the skills, P and L only.
34
+ An approved, polished headline and About go back through the tool, which checks them like a
35
+ letter and saves them in the file.
36
+ - The interview handles dictated answers: it reads names and numbers back before saving them, and
37
+ turns a long spoken answer into a bullet for the person to approve.
38
+ - A Courses section (`courses` in `profile.yaml`, set with `set_profile_details`) for training
39
+ without an exam, which is not a certification. JSON Resume has no field for it, so the export
40
+ leaves it out.
41
+ - The checklist header shows a marked example line.
42
+ - Publications: books, articles and papers the person wrote (`upsert_publication`,
43
+ `remove_publication`), in their own CV section. A variant shows all of them or the ones it lists.
44
+ The LinkedIn and JSON Resume imports read publications (and LinkedIn's courses), and the JSON
45
+ Resume export writes them.
46
+ - Spanish spellings of leadership and practice terms in job posts (liderazgo de equipos, revisión
47
+ de código, patrones de diseño, agentes de IA…) match the skill. In a letter, practices are not
48
+ claims to check.
49
+ - When every role has ended, summaries, letters and LinkedIn texts speak of that work in the
50
+ past tense.
51
+ - Evidence must say where and how: the assistant asks for each skill instead of taking a bare
52
+ "yes" to a list.
53
+ - The interview warns that it can take hours, proposes a first session that is enough for a CV,
54
+ and picks up where it stopped. The READMEs say so too.
55
+ - The interview asks once for certifications, courses and anything the person wrote, which people
56
+ rarely mention unasked.
57
+
58
+ ### Changed
59
+
60
+ - The server's instructions cover checklists, dictated answers, applications, LinkedIn and export,
61
+ for clients that don't load the skill.
62
+ - `checklist list` shows "(yours)" next to the title of your own templates.
63
+ - Clearer error for an application date that isn't YYYY-MM-DD.
64
+
65
+ ### Fixed
66
+
67
+ - Lowering a skill now lowers its mark in `<profile>/checklist.md` too. Before, the file kept
68
+ the old mark, and applying it again proposed the upgrade the person had undone. The
69
+ confirmation form also shows the person's own note on a skill first, since it may say why
70
+ they lowered it.
71
+ - A checklist mark typed with spaces, like `[ P]`, was skipped in silence; it now counts, and
72
+ any other text in the brackets is reported as a problem.
73
+ - A composed checklist's title said "imported skills" whenever the inventory had skills outside
74
+ the chosen lists.
75
+
76
+ ## [0.1.0] — unreleased, frozen
77
+
78
+ Frozen on the `release/0.1` branch on 2026-10-14; it will be published from there.
79
+
80
+ The first release: an MCP server, a CLI and a Claude Code skill that turn one profile folder into
81
+ honest, ATS-safe CVs and cover letters.
82
+
83
+ ### Profile and honesty
84
+
85
+ - A profile is four YAML files: contact and roles, a pool of achievement bullets, a skill inventory
86
+ and variants per type of role, with `extends:` inheritance. Texts can be per language (EN, ES).
87
+ - Skill levels: P (production), L (own project or lab), F (familiarity, always labeled as such)
88
+ and X (never shown, but kept so gap reports are honest).
89
+ - Raising a level needs written evidence and the person's own confirmation. In MCP clients that
90
+ support elicitation, a form asks "no / sí" for each upgrade, up to 10 at a time; elsewhere the
91
+ CLI asks in the terminal. The model cannot confirm for the person.
92
+ - Tools to edit every part of the profile, each validating the whole profile before writing, and
93
+ to remove a skill, bullet or role without leaving a broken reference.
94
+
95
+ ### CVs
96
+
97
+ - PDF with Typst and the bundled Carlito font, the same on every machine with no system fonts;
98
+ DOCX with python-docx. `fit_pages` adjusts density to meet a variant's page limit.
99
+ - ATS guarantees checked on every build with pypdf and pdfminer.six: one column, no ligatures,
100
+ no kerning, no hyphenation, dates on their role's line, reading order, every bullet extracted,
101
+ and terms like "CI/CD", URLs and emails never split across lines.
102
+ - `verify` checks any PDF, including CVs made with other tools.
103
+
104
+ ### Job posts
105
+
106
+ - Keyword coverage per variant, with synonyms in English and Spanish.
107
+ - Gaps classified against the person's own inventory: in the inventory but not on this CV,
108
+ familiarity only, or not held.
109
+
110
+ ### Skill checklists
111
+
112
+ - Mark many skills at once in a Markdown file: one letter per line, evidence after " — ", an
113
+ optional evidence line per section, years and last year used.
114
+ - Seven built-in checklists (backend JVM, Python and Go, frontend, platform/SRE, data,
115
+ engineering leadership), merged without duplicates and prefilled from the inventory.
116
+ - Any checklist written by hand can be applied, in English or Spanish. Lines without evidence
117
+ come back as pending and are asked about in groups.
118
+
119
+ ### Cover letters
120
+
121
+ - `check_claims` reads any text against the profile. It blocks an X skill, or an F skill
122
+ presented as experience, and warns about technologies and numbers the profile doesn't show.
123
+ - `build_letter` renders a one-page letter to PDF and DOCX with the CV's ATS checks, and refuses
124
+ while there are problems.
125
+
126
+ ### Import
127
+
128
+ - JSON Resume and LinkedIn's data export (ZIP of CSVs): contact, roles, achievements,
129
+ education, certifications and languages. It only adds what is missing and never overwrites.
130
+ Skills arrive without a level, as a checklist to mark.
131
+ - LinkedIn's profile PDF: the server returns its text and the client proposes each section for
132
+ the person to approve.
133
+ - The export may be a ZIP or the folder a browser unzipped it into, and a profile may have no
134
+ email. The report names the files the export lacks: LinkedIn's basic export has only the
135
+ profile, without roles or email.
136
+
137
+ ### Clients
138
+
139
+ - Tested in Claude Code (confirmation in the chat) and Claude Desktop (confirmation in a
140
+ terminal). The server needs no API key and makes no network calls.
141
+
142
+ [0.1.0]: https://github.com/JesusCabreraReveles/honest-cv/releases/tag/v0.1.0
@@ -0,0 +1,73 @@
1
+ # Contributing to honest-cv
2
+
3
+ Thanks for helping. Issues and pull requests are welcome, in English or Spanish.
4
+
5
+ ## Ground rules
6
+
7
+ - **Never commit real personal data.** Tests, demos, screenshots and issues use only the fictional
8
+ profile in `examples/ada/`. Put real profiles in `profiles/`, which is ignored by git, or outside
9
+ the repository.
10
+ - **The code enforces honesty, not a prompt.**
11
+ - An `F` skill is always rendered under the "Familiarity" / "Familiaridad" label.
12
+ - An `X` skill is never rendered.
13
+ - Raising a level requires `evidence` and the person's confirmation.
14
+
15
+ A change that touches these rules must keep their tests passing, and a new rule needs a new test.
16
+ - **ATS guarantees are invariants.**
17
+ - One column; no layout tables, images, icons, headers or footers.
18
+ - Ligatures off; source order equals reading order.
19
+ - Text extractable by pypdf and pdfminer.six.
20
+
21
+ If a design idea breaks one of these, it doesn't go into the default template.
22
+ - **The server stays deterministic and keyless.** The MCP client's model does the language work;
23
+ the server validates, builds, verifies and measures.
24
+ - **Scope.** The v0.1 scope is in [`docs/PLAN.md`](docs/PLAN.md). Larger ideas (JSON Resume
25
+ import, LinkedIn import, cover letters, more templates) start as an issue.
26
+
27
+ ## Setup
28
+
29
+ ```bash
30
+ uv sync
31
+ uv run pytest # the whole suite runs in a few seconds
32
+ uv run honest-cv build examples/ada -l en -l es # writes to out/
33
+ ```
34
+
35
+ Python 3.11 or newer. Code, names and comments are in English.
36
+
37
+ ## Useful pieces
38
+
39
+ | Path | What lives there |
40
+ |---|---|
41
+ | `src/honest_cv/model.py` | YAML schemas, `extends:` resolution and the skill-upgrade rule. |
42
+ | `src/honest_cv/document.py` | Turns a variant and a language into a document. The honesty rules apply here. |
43
+ | `src/honest_cv/render/` | The Typst template, the bundled fonts, the PDF renderer with `fit_pages`, and the DOCX renderer. |
44
+ | `src/honest_cv/verify.py` | ATS checks. |
45
+ | `src/honest_cv/score.py` | Keyword coverage, synonyms and gaps. |
46
+ | `src/honest_cv/server.py` | MCP tools and prompts. |
47
+ | `src/honest_cv/prompts/`, `skills/honest-cv/SKILL.md` | Conversation flows. A test keeps the tool names they mention in sync with the server. |
48
+
49
+ ## Common contributions
50
+
51
+ - **Synonyms.** Add a group to `SYNONYMS` in `score.py`, with the canonical name first. Add a case
52
+ to `tests/test_score.py`. Terms of three characters or fewer match case-sensitively.
53
+ - **Checklists.** Add `src/honest_cv/checklists/<id>.md` for a kind of role that is missing: a
54
+ `# Title`, a one-line description, a `## ★ Main skills — years and last year used` section with
55
+ the skills that filter the most, then sections by topic. Leave every line unmarked (`- [ ] Name`)
56
+ and use the canonical names from `SYNONYMS`. `tests/test_checklist.py` checks all of this.
57
+ - **Checklists for other professions.** honest-cv is not only for software. A checklist for
58
+ another field (nursing, accounting, sales, teaching...) goes to `examples/checklists/<id>.md`,
59
+ in the same format, and may declare its synonyms with `same:` lines, the main name first:
60
+ `same: Work Breakdown Structure = WBS = EDT`. People copy it to `<profile>/checklists/`, where
61
+ `checklist_list` shows it and the job score, the gaps and the letter check use its names and
62
+ synonyms. `tests/test_templates.py` checks the format; see `project-management.md`.
63
+ - **Templates.** A new CV template must pass `verify_pdf` with the Ada profile in both languages.
64
+ Never put text in a box aligned to the right.
65
+ - **The demo GIF.** Regenerate it with `vhs docs/demo.tape` after the CLI output changes.
66
+ - **Packaging.** After `uv build`, `scripts/smoke.sh` checks the wheel on a clean Linux in
67
+ Docker; the command is at the top of the script.
68
+
69
+ ## Pull requests
70
+
71
+ - Keep each pull request small and focused, with tests.
72
+ - CI must pass on Linux, macOS and Windows.
73
+ - Describe what changes for the person using the tool.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jesus Cabrera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,364 @@
1
+ Metadata-Version: 2.5
2
+ Name: honest-cv
3
+ Version: 0.2.0
4
+ Summary: Honest, ATS-safe, per-role CVs generated by your AI agent (MCP server + CLI + Claude Code skill).
5
+ Project-URL: Homepage, https://github.com/JesusCabreraReveles/honest-cv
6
+ Project-URL: Repository, https://github.com/JesusCabreraReveles/honest-cv
7
+ Project-URL: Issues, https://github.com/JesusCabreraReveles/honest-cv/issues
8
+ Project-URL: Changelog, https://github.com/JesusCabreraReveles/honest-cv/blob/master/CHANGELOG.md
9
+ Author: Jesus Cabrera
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ats,claude,cv,mcp,resume
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Office/Business
22
+ Classifier: Topic :: Text Processing :: Markup
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: mcp<3,>=2.3
25
+ Requires-Dist: pdfminer-six>=20231228
26
+ Requires-Dist: pydantic>=2.6
27
+ Requires-Dist: pypdf>=4.0
28
+ Requires-Dist: python-docx>=1.1
29
+ Requires-Dist: pyyaml>=6.0
30
+ Requires-Dist: typst>=0.15
31
+ Description-Content-Type: text/markdown
32
+
33
+ # honest-cv
34
+
35
+ [![CI](https://github.com/JesusCabreraReveles/honest-cv/actions/workflows/ci.yml/badge.svg)](https://github.com/JesusCabreraReveles/honest-cv/actions/workflows/ci.yml)
36
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/JesusCabreraReveles/honest-cv/blob/master/LICENSE)
37
+
38
+ <!-- mcp-name: io.github.JesusCabreraReveles/honest-cv -->
39
+
40
+ **Honest, ATS-safe CVs tailored to each role, generated by your AI agent.** honest-cv is an MCP
41
+ server, a CLI and a Claude Code skill. It keeps one source of truth about your experience and
42
+ turns it into a PDF and DOCX for each type of role. It never claims a skill you don't have.
43
+
44
+ [Leer en español](https://github.com/JesusCabreraReveles/honest-cv/blob/master/README.es.md)
45
+
46
+ ![Demo: skill checklists including your own, scoring a job post, a verified build, a cover letter that refuses a skill the person lacks, LinkedIn texts and logging the application](https://raw.githubusercontent.com/JesusCabreraReveles/honest-cv/master/docs/assets/demo.gif)
47
+
48
+ ## Why
49
+
50
+ Many CVs that get no callbacks don't fail on design. They fail on targeting, and on details that
51
+ quietly break text extraction. AI makes the opposite problem easy: a model will happily stuff a CV
52
+ with keywords you can't defend in an interview. honest-cv is built around three ideas:
53
+
54
+ - **The code enforces honesty, not a prompt.** Every skill has a level. A skill at familiarity
55
+ level is always labeled as such, and a skill you don't have is never printed. Raising a level
56
+ takes written evidence and *your* confirmation, which the server asks you for directly. The
57
+ model can't confirm on your behalf.
58
+ - **Gaps are measured against your own inventory.** For each keyword a job post asks for,
59
+ honest-cv tells you which of three cases it is:
60
+ - you have it, but this CV doesn't show it;
61
+ - you only know it at familiarity level;
62
+ - you don't have it.
63
+ - **ATS safety is checked on every build.** Every PDF goes through two text extractors (pypdf and
64
+ pdfminer.six). The build checks that the text is one column, has no ligatures, keeps each date on
65
+ its role's line, follows the right reading order and includes every bullet.
66
+
67
+ The server is deterministic and needs no API key. Your MCP client's model runs the conversation;
68
+ honest-cv validates, builds, verifies and measures. It keeps your profile in a folder on your
69
+ machine and never sends it anywhere: see [Privacy](#privacy) for what your AI client does with it.
70
+
71
+ <p align="center"><img src="https://raw.githubusercontent.com/JesusCabreraReveles/honest-cv/master/docs/assets/ada-platform-en.png" width="560" alt="A generated one-page CV for the fictional Ada Example"></p>
72
+
73
+ ## Quick start
74
+
75
+ honest-cv needs [uv](https://docs.astral.sh/uv/). Typst and the fonts ship inside the package,
76
+ so there is nothing else to install.
77
+ It runs on Linux (glibc) on x86_64 and ARM64, macOS on Intel and Apple
78
+ Silicon, and Windows on x64. Alpine Linux and Windows on ARM aren't supported, because the `typst`
79
+ package doesn't publish wheels for them.
80
+
81
+ ### Claude Code
82
+
83
+ ```bash
84
+ claude mcp add honest-cv -- uvx honest-cv serve --profile ~/cv-profile
85
+ ```
86
+
87
+ To install the skill as well, which guides the interview and the tailoring:
88
+
89
+ ```bash
90
+ mkdir -p ~/.claude/skills/honest-cv
91
+ curl -fsSL https://raw.githubusercontent.com/JesusCabreraReveles/honest-cv/master/skills/honest-cv/SKILL.md \
92
+ -o ~/.claude/skills/honest-cv/SKILL.md
93
+ ```
94
+
95
+ Then start with *"interview me for my CV"*, or run `/mcp__honest-cv__interview`. You can answer
96
+ by voice with your system's dictation (on macOS: System Settings → Keyboard → Dictation): the
97
+ interview reads names and numbers back to you before saving them, since dictation mishears them.
98
+
99
+ > **The first interview is long.** With a long career and many skills it can take several hours.
100
+ > You don't have to do it in one sitting: everything is saved as you go, so stop whenever you like
101
+ > and say *"let's continue my CV interview"* later. A good first session covers your contact
102
+ > details, your two most recent roles and the skills checklist; with that you can already build a
103
+ > CV, and older roles can wait.
104
+
105
+ ### Cursor
106
+
107
+ Add this to `~/.cursor/mcp.json`, or to `.cursor/mcp.json` in a project:
108
+
109
+ ```json
110
+ {
111
+ "mcpServers": {
112
+ "honest-cv": {
113
+ "command": "uvx",
114
+ "args": ["honest-cv", "serve", "--profile", "/absolute/path/to/cv-profile"]
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Claude Desktop
121
+
122
+ Edit `claude_desktop_config.json`:
123
+ - on macOS, `~/Library/Application Support/Claude/`;
124
+ - on Windows, `%APPDATA%\Claude\`.
125
+
126
+ Add the same `mcpServers` block as in the Cursor example and restart the app. If the app can't
127
+ find `uvx`, use its absolute path (`which uvx`).
128
+
129
+ ### How each client confirms a skill upgrade
130
+
131
+ Raising a skill's level needs your own confirmation, which the server asks for through MCP
132
+ elicitation. Not every client supports it yet:
133
+
134
+ | Client | Where you confirm |
135
+ |---|---|
136
+ | Claude Code | In the chat: a form with one "no / sí" field per upgrade, up to 10 at a time. Only the ones you answer "sí" are saved; Decline saves nothing. |
137
+ | Claude Desktop | In a terminal. The server refuses the change and gives you the command to run: `uvx honest-cv skill <profile> "<name>" <level> --evidence "..."`. |
138
+ | Other clients | In the chat if they support elicitation, otherwise in a terminal as above. |
139
+
140
+ ## Your profile
141
+
142
+ A profile is a folder with four YAML files. Create one with `honest-cv init ~/cv-profile`, or let
143
+ the `interview` prompt fill it in with you. Keep it out of public repositories.
144
+
145
+ | File | What it holds |
146
+ |---|---|
147
+ | `profile.yaml` | Contact details, roles, education, certifications, publications, courses and languages. |
148
+ | `bullets.yaml` | A pool of achievements, each with an `id`, a `role` and `tags`. |
149
+ | `skills.yaml` | Your skill inventory, with levels and evidence. |
150
+ | `variants.yaml` | One CV plan per type of role. A variant can inherit from another with `extends:`. |
151
+
152
+ Any text can be a single string or one string per language: `{en: ..., es: ...}`. CVs can be
153
+ built in English and in Spanish.
154
+
155
+ | Level | Meaning | On the CV |
156
+ |---|---|---|
157
+ | `P` | Used in production at a job | Shown |
158
+ | `L` | Your own project or lab | Shown |
159
+ | `F` | Familiarity | Only on the line labeled "Familiarity" |
160
+ | `X` | You don't have it | Never shown, but recorded so gap reports are honest |
161
+
162
+ ### Skill checklists
163
+
164
+ Recording dozens of skills one question at a time is slow. A checklist is a Markdown file you mark
165
+ in your editor, one letter per line:
166
+
167
+ ```markdown
168
+ ## Messaging and streaming
169
+ evidence: Payments platform at Acme, 2020–2026
170
+ - [P] Kafka — reconciliation flow, 2M events a day · years: 6 · last year: 2026
171
+ - [F] RabbitMQ — a workshop and the official tutorials
172
+ - [X] NATS
173
+ ```
174
+
175
+ Text after " — " is the evidence, and an `evidence:` line under a heading covers that section.
176
+ Lines without evidence are never saved: they come back as pending, and your assistant asks you
177
+ about them in groups. Upgrades are confirmed once for the whole file.
178
+
179
+ ```bash
180
+ honest-cv checklist list # backend-jvm, platform-sre, ...
181
+ honest-cv checklist new ~/cv-profile backend-jvm engineering-lead
182
+ honest-cv checklist apply ~/cv-profile ~/cv-profile/checklist.md
183
+ ```
184
+
185
+ **Any profession works.** The built-in checklists are for software roles, but a template is just a
186
+ Markdown list in this format: save your own (or one someone shared) in `~/cv-profile/checklists/`
187
+ and it shows up in `checklist list` and combines with the others in `checklist new`, which also
188
+ takes a path. A template can declare synonyms, such as `same: Work Breakdown Structure = WBS`,
189
+ and the job score, the gaps and the letter check use them, along with its skill names. Start from
190
+ [`examples/checklists/project-management.md`](https://github.com/JesusCabreraReveles/honest-cv/blob/master/examples/checklists/project-management.md). You can also apply a file you marked yourself, in English or Spanish (`años`,
191
+ `último uso`). When you're done, honest-cv names the P and L skills that no achievement shows, and
192
+ your assistant asks you for one real example of each.
193
+
194
+ ```bash
195
+ honest-cv checklist list ~/cv-profile # yours too, e.g. accounting
196
+ honest-cv checklist new ~/cv-profile accounting engineering-lead
197
+ ```
198
+
199
+ New checklists for other kinds of roles are welcome: see [CONTRIBUTING.md](https://github.com/JesusCabreraReveles/honest-cv/blob/master/CONTRIBUTING.md).
200
+
201
+ See [`examples/ada/`](https://github.com/JesusCabreraReveles/honest-cv/tree/master/examples/ada) for a complete, fictional profile with three variants, a job
202
+ post and its keywords.
203
+
204
+ ## Privacy
205
+
206
+ - **honest-cv itself makes no network calls.** No accounts, no telemetry, no API keys. Your
207
+ profile is a folder of YAML files on your machine, and the PDF engine and fonts run locally. You
208
+ can check it: every command works with the network turned off.
209
+ - **Your AI client is a different matter.** Whatever the client's model reads through the tools
210
+ goes to that model's provider, under the provider's terms, like anything else you share in a
211
+ chat. That includes your skills, bullets and the text of your CVs.
212
+ - **Some clients copy files to the cloud.** To edit files directly, Claude Desktop may ask for
213
+ access to the profile folder and say that the files leave your device. honest-cv's editing tools
214
+ make that unnecessary: decline the request and ask the assistant to use the tools.
215
+ - **If you want nothing to leave your machine,** use only the CLI (`build`, `verify`, `score`,
216
+ `skill`) and edit the YAML files yourself.
217
+
218
+ ## Import what you already have
219
+
220
+ `honest-cv import` reads a [JSON Resume](https://jsonresume.org) file or LinkedIn's data export
221
+ (Settings → Data privacy → Download your data, a ZIP of CSVs, or its folder if your browser
222
+ unzipped it). Ask for the full archive: the basic one, offered when you pick files, has only your
223
+ headline and summary, without roles. Contact, roles, education,
224
+ certifications, languages and each role's achievements go into your profile; with a headline and
225
+ a summary it also creates a first variant, so `build` works right away.
226
+
227
+ - It only adds what your profile lacks and never overwrites. Try `--dry-run` first.
228
+ - Imported achievements are tagged `imported`: review their wording.
229
+ - **Skills arrive without a level**, because neither format says whether you used a skill in
230
+ production. They go to `imported-skills.md`, a checklist for you to mark.
231
+
232
+ LinkedIn's "Save to PDF" profile changes layout without notice, so it isn't parsed: with an MCP
233
+ client, the `import_linkedin_pdf` prompt reads its text and proposes each section for you to
234
+ approve.
235
+
236
+ ## Track your applications
237
+
238
+ `honest-cv apps` keeps a log in `applications.yaml`, inside your profile folder: which variant and
239
+ files went to which company, and each status with its date (`draft`, `applied`, `interview`,
240
+ `offer`, `rejected`, `withdrawn`). Logging the same company and role again updates it. With an MCP
241
+ client, your assistant offers to log an application after building the CV or letter, and
242
+ `list_applications` points out the ones with no news for two weeks. It also keeps the job post's
243
+ text in `posts/`, since links expire, and what to prepare for the interview: ask your assistant
244
+ *"what did I need to prepare for Acme?"*.
245
+
246
+ ## LinkedIn
247
+
248
+ `honest-cv linkedin` turns a variant into LinkedIn's texts, each within its limit: the headline
249
+ (your title and up to five production skills), About (the summary and impact bullets), one
250
+ description per position from its bullets, and the skills list. It writes them to a Markdown file
251
+ to copy from. The skills are P and L only: LinkedIn can't label familiarity, so F skills stay out,
252
+ and X skills never appear. With an MCP client, the `linkedin_profile` prompt polishes the wording
253
+ and checks it with `check_claims`.
254
+
255
+ ## Export
256
+
257
+ To go the other way, `honest-cv export` writes a variant as JSON Resume for other CV tools and
258
+ sites, with the same rules as the CV: X skills never appear, and F skills only in a "Familiarity"
259
+ group with that level.
260
+
261
+ ## Cover letters
262
+
263
+ The `cover_letter` prompt drafts a one-page letter for a job post from your own bullets. Before it
264
+ is built, `check_claims` reads the text against your profile:
265
+
266
+ - **Problems block the build:** a skill you marked X, or a familiarity skill presented as
267
+ experience ("I use Rust in production" when Rust is F; "I am familiar with Rust" is fine).
268
+ - **Warnings are for you to review:** a technology neither your inventory nor your bullets show,
269
+ or a number that appears nowhere in your profile. If it is true, record it first; if not, it
270
+ comes out of the letter.
271
+
272
+ The letter uses the CV's fonts and passes the same ATS checks. Without an AI client, write it in a
273
+ text file (paragraphs separated by blank lines) and use `honest-cv letter check` and `letter build`.
274
+
275
+ ## CLI
276
+
277
+ Run any command with `uvx honest-cv ...`, or install it once with `uv tool install honest-cv` to
278
+ type just `honest-cv`.
279
+
280
+ ```bash
281
+ honest-cv init ~/cv-profile # create the profile templates
282
+ honest-cv build ~/cv-profile -v backend -l en -l es # PDF + DOCX, verified on every build
283
+ honest-cv verify any-cv.pdf --text # check any PDF, even one made elsewhere
284
+ honest-cv score ~/cv-profile -k "Kubernetes,Go" -p "Rust" # coverage per variant, with gaps
285
+ honest-cv skill ~/cv-profile "Rust" L --evidence "CLI tool on GitHub" # asks you to confirm
286
+ honest-cv import ~/cv-profile linkedin-export.zip --dry-run # or resume.json
287
+ honest-cv export ~/cv-profile -v backend -l en # JSON Resume
288
+ honest-cv apps log ~/cv-profile --company Acme --role "SRE" -s applied -v platform
289
+ honest-cv apps list ~/cv-profile # latest first, with counts
290
+ honest-cv linkedin ~/cv-profile -v platform -l en # texts to paste into LinkedIn
291
+ honest-cv remove skill ~/cv-profile "Kafka" # asks you to confirm
292
+ honest-cv letter check ~/cv-profile letter.txt -k "Kubernetes,Go" # claims your profile doesn't support
293
+ honest-cv letter build ~/cv-profile letter.txt --company Acme --role "Platform Engineer" -l en
294
+ honest-cv serve --profile ~/cv-profile # the MCP server, over stdio
295
+ ```
296
+
297
+ `verify` works on any PDF. Use `--text` to see exactly what a parser extracts. Its
298
+ `single_column` check also catches a common defect: a date aligned to the right, which parsers
299
+ split away from its role.
300
+
301
+ ## MCP tools and prompts
302
+
303
+ | Name | What it does |
304
+ |---|---|
305
+ | `init_profile` | Creates a profile folder with commented templates. |
306
+ | `get_inventory` | Returns your skills, grouped by level, with years and evidence. |
307
+ | `set_skill` | Adds or changes a skill. To raise its level it needs evidence and asks you to confirm. |
308
+ | `set_skills` | Adds or changes several skills at once. Upgrades are confirmed with one question that lists each change. |
309
+ | `checklist_list` | Lists the built-in checklists. |
310
+ | `checklist_new` | Merges built-in checklists into one file, prefilled from your inventory. |
311
+ | `checklist_apply` | Saves the skills marked in a checklist and returns the lines that still need evidence. |
312
+ | `add_bullet` | Adds an achievement to the pool. |
313
+ | `remove_skill`, `remove_bullet`, `remove_role`, `remove_publication` | Delete for good, and remove every use in the variants. A role with bullets needs `with_bullets`. Clients ask before running them. |
314
+ | `set_contact` | Changes contact details: name, email, phone, location, links. |
315
+ | `set_profile_details` | Replaces the certifications list, the courses list and the spoken-languages line. |
316
+ | `upsert_role` | Adds a role in date order, or changes fields of an existing one. |
317
+ | `upsert_education` | Adds or changes an education entry. |
318
+ | `upsert_publication` | Adds or changes a book, article or paper the person wrote. Each variant shows all of them, or the ones it lists. |
319
+ | `save_variant` | Adds or replaces a variant. Every reference is checked before saving. |
320
+ | `list_variants` | Lists the variants after inheritance, with their bullet texts and the bullets no variant uses. |
321
+ | `build_cv` | Renders a PDF and/or DOCX and verifies it. |
322
+ | `verify_pdf` | Runs the ATS checks on any PDF and returns its extracted text. |
323
+ | `score_job` | Measures the keyword coverage of each variant. The client extracts the keywords. |
324
+ | `gap_report` | Groups missing keywords by what you can honestly do about each one. |
325
+ | prompt `interview` | Fills your inventory and bullet pool, one question at a time. |
326
+ | `check_claims` | Checks any text against your profile: blocks X skills and familiarity sold as experience, flags what the profile doesn't show. |
327
+ | `build_letter` | Renders a cover letter to PDF and DOCX, one page, with the CV's ATS checks. Refuses while `check_claims` finds problems. |
328
+ | prompt `tailor_for_job` | Scores the variants, explains the gaps, adapts a variant and rebuilds. |
329
+ | prompt `cover_letter` | Drafts a letter for a job post from your bullets, checks its claims and builds it. |
330
+ | `import_profile` | Imports JSON Resume or a LinkedIn data export, adding only what is missing. For a LinkedIn PDF it returns the text. |
331
+ | `export_json_resume` | Exports a variant as JSON Resume; X skills never appear and F skills keep their label. |
332
+ | `log_application` | Records an application (company, role, CV variant, files sent, the post's text, what to prepare) or a new status, with its date. |
333
+ | `linkedin_profile` | LinkedIn headline, About, position descriptions and skills (P and L only) from a variant, within LinkedIn's limits. |
334
+ | `list_applications` | Lists applications, latest first, with a count per status and the ones with no news for 14+ days. |
335
+ | prompt `import_linkedin_pdf` | Turns the PDF from LinkedIn's "Save to PDF" into a profile, section by section, with your approval. |
336
+ | prompt `linkedin_profile` | Polishes the LinkedIn texts of a variant without adding claims, checked with `check_claims`. |
337
+
338
+ ## ATS guarantees
339
+
340
+ The template enforces these. `verify` checks them on every build, and the test suite runs in CI on
341
+ Linux, macOS and Windows:
342
+
343
+ - One column. No layout tables, images, icons, headers or footers.
344
+ - Ligatures and kerning off, no hyphenation, and terms like "CI/CD", URLs and emails never split
345
+ across lines.
346
+ - Reading order equals source order. Dates sit on the same line as their role.
347
+ - Text is extractable by both pypdf and pdfminer.six, and the two agree.
348
+ - The fonts (Carlito, metric-compatible with Calibri) are embedded and identical on every machine.
349
+
350
+ ## Development
351
+
352
+ ```bash
353
+ uv sync
354
+ uv run pytest
355
+ uv run honest-cv build examples/ada -l en -l es
356
+ ```
357
+
358
+ The repository's `.mcp.json` registers `honest-cv-dev` with the fictional profile, so Claude Code
359
+ in this folder can use your working copy. See [CONTRIBUTING.md](https://github.com/JesusCabreraReveles/honest-cv/blob/master/CONTRIBUTING.md).
360
+
361
+ ## License
362
+
363
+ MIT. The bundled Carlito font is under the SIL Open Font License; see
364
+ `src/honest_cv/render/fonts/OFL.txt`.