cvcite 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 (68) hide show
  1. cvcite-0.2.0/.claude-plugin/marketplace.json +15 -0
  2. cvcite-0.2.0/.github/CODEOWNERS +1 -0
  3. cvcite-0.2.0/.github/dependabot.yml +13 -0
  4. cvcite-0.2.0/.github/scripts/check-template-pins.sh +31 -0
  5. cvcite-0.2.0/.github/scripts/verify-action-pins.sh +76 -0
  6. cvcite-0.2.0/.github/workflows/ci.yml +78 -0
  7. cvcite-0.2.0/.github/workflows/lint.yml +71 -0
  8. cvcite-0.2.0/.gitignore +23 -0
  9. cvcite-0.2.0/LICENSE +21 -0
  10. cvcite-0.2.0/PKG-INFO +178 -0
  11. cvcite-0.2.0/README.md +156 -0
  12. cvcite-0.2.0/action.yml +103 -0
  13. cvcite-0.2.0/docs/icon.typ +10 -0
  14. cvcite-0.2.0/plugin/.claude-plugin/icon.png +0 -0
  15. cvcite-0.2.0/plugin/.claude-plugin/plugin.json +20 -0
  16. cvcite-0.2.0/plugin/README.md +53 -0
  17. cvcite-0.2.0/plugin/agents/cv-claim-reviewer.md +51 -0
  18. cvcite-0.2.0/plugin/skills/cv-build/SKILL.md +53 -0
  19. cvcite-0.2.0/plugin/skills/cv-fact-check/SKILL.md +72 -0
  20. cvcite-0.2.0/plugin/skills/cv-ingest/SKILL.md +99 -0
  21. cvcite-0.2.0/plugin/skills/cv-presence-audit/SKILL.md +91 -0
  22. cvcite-0.2.0/plugin/skills/cv-tailor/SKILL.md +78 -0
  23. cvcite-0.2.0/pyproject.toml +45 -0
  24. cvcite-0.2.0/requirements/action.txt +797 -0
  25. cvcite-0.2.0/requirements/test.in +1 -0
  26. cvcite-0.2.0/requirements/test.txt +27 -0
  27. cvcite-0.2.0/src/cvcite/__init__.py +4 -0
  28. cvcite-0.2.0/src/cvcite/__main__.py +5 -0
  29. cvcite-0.2.0/src/cvcite/_rendercv_shim.py +54 -0
  30. cvcite-0.2.0/src/cvcite/checks.py +379 -0
  31. cvcite-0.2.0/src/cvcite/cli.py +225 -0
  32. cvcite-0.2.0/src/cvcite/compare.py +79 -0
  33. cvcite-0.2.0/src/cvcite/config.py +123 -0
  34. cvcite-0.2.0/src/cvcite/doctor.py +54 -0
  35. cvcite-0.2.0/src/cvcite/gate.py +421 -0
  36. cvcite-0.2.0/src/cvcite/ingest.py +240 -0
  37. cvcite-0.2.0/src/cvcite/overlay.py +195 -0
  38. cvcite-0.2.0/src/cvcite/package.py +64 -0
  39. cvcite-0.2.0/src/cvcite/render.py +112 -0
  40. cvcite-0.2.0/src/cvcite/scaffold.py +98 -0
  41. cvcite-0.2.0/src/cvcite/templates/repo/AGENTS.md +70 -0
  42. cvcite-0.2.0/src/cvcite/templates/repo/cv.yaml +57 -0
  43. cvcite-0.2.0/src/cvcite/templates/repo/cvcite.toml +25 -0
  44. cvcite-0.2.0/src/cvcite/templates/repo/facts.yaml +18 -0
  45. cvcite-0.2.0/src/cvcite/templates/repo/github/workflows/cv-review.yml +148 -0
  46. cvcite-0.2.0/src/cvcite/templates/repo/github/workflows/cv.yml +36 -0
  47. cvcite-0.2.0/src/cvcite/templates/repo/github/workflows/release.yml +79 -0
  48. cvcite-0.2.0/src/cvcite/templates/repo/gitignore +22 -0
  49. cvcite-0.2.0/src/cvcite/templates/repo/variants/general.yaml +19 -0
  50. cvcite-0.2.0/src/cvcite/templates/sample/cv.yaml +89 -0
  51. cvcite-0.2.0/src/cvcite/templates/sample/cvcite.toml +25 -0
  52. cvcite-0.2.0/src/cvcite/templates/sample/facts.yaml +63 -0
  53. cvcite-0.2.0/src/cvcite/templates/sample/variants/manager.yaml +28 -0
  54. cvcite-0.2.0/src/cvcite/templates/sample/variants/staff-engineer.yaml +23 -0
  55. cvcite-0.2.0/src/cvcite/vendor/fontawesome-0.6.0/LICENSE +21 -0
  56. cvcite-0.2.0/src/cvcite/vendor/fontawesome-0.6.0/lib-gen-func.typ +5266 -0
  57. cvcite-0.2.0/src/cvcite/vendor/fontawesome-0.6.0/lib-gen-map.typ +5259 -0
  58. cvcite-0.2.0/src/cvcite/vendor/fontawesome-0.6.0/lib-impl.typ +90 -0
  59. cvcite-0.2.0/src/cvcite/vendor/fontawesome-0.6.0/lib.typ +79 -0
  60. cvcite-0.2.0/src/cvcite/vendor/fontawesome-0.6.0/typst.toml +8 -0
  61. cvcite-0.2.0/tests/__init__.py +1 -0
  62. cvcite-0.2.0/tests/conftest.py +118 -0
  63. cvcite-0.2.0/tests/test_build_tools.py +167 -0
  64. cvcite-0.2.0/tests/test_checks.py +159 -0
  65. cvcite-0.2.0/tests/test_gate.py +180 -0
  66. cvcite-0.2.0/tests/test_hardening.py +181 -0
  67. cvcite-0.2.0/tests/test_ingest.py +133 -0
  68. cvcite-0.2.0/tests/test_render.py +46 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "cvcite",
3
+ "description": "cvcite: build a truthful, ATS-safe CV from one master YAML file, with skills that interview you, tailor it per role and check your public profiles against it.",
4
+ "owner": {
5
+ "name": "Agustin Gonzalez Nicolini",
6
+ "url": "https://github.com/agusgonzaleznic"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "cvcite",
11
+ "source": "./plugin",
12
+ "description": "Build a truthful, ATS-safe CV from one master YAML file, tailor it per role, and keep LinkedIn, your site and GitHub consistent with it."
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1 @@
1
+ * @agusgonzaleznic
@@ -0,0 +1,13 @@
1
+ # Weekly updates for the pinned actions in .github/workflows and action.yml. The workflows
2
+ # `init` writes live under src/ where Dependabot does not look; lint.yml fails when their pins
3
+ # differ from these. The Python lock in requirements/ is regenerated by hand (see its header);
4
+ # Dependabot alerts still cover every package in it.
5
+ version: 2
6
+ updates:
7
+ - package-ecosystem: github-actions
8
+ directory: /
9
+ # A new release waits a week before Dependabot proposes it.
10
+ cooldown:
11
+ default-days: 7
12
+ schedule:
13
+ interval: weekly
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env bash
2
+ # The workflows `cvcite init` writes live under src/, where Dependabot does not look. Fail when
3
+ # one of them pins an action differently from this repository's own workflows or action.yml, so
4
+ # a Dependabot update here cannot leave the templates behind.
5
+ #
6
+ # .github/scripts/check-template-pins.sh [templates-dir]
7
+ set -euo pipefail
8
+ templates=${1:-src/cvcite/templates/repo/github/workflows}
9
+ extract() { grep -rhoE 'uses: [^ @#]+@[0-9a-f]{40} # v[^ ]+' "$@" | sed 's/^uses: //' | sort -u; }
10
+ ours=$(extract .github/workflows action.yml)
11
+ theirs=$(extract "$templates")
12
+ fail=0
13
+ compared=0
14
+ while IFS= read -r ref; do
15
+ [ -n "$ref" ] || continue
16
+ action=${ref%%@*}
17
+ mine=$(grep -F "$action@" <<<"$ours" || true)
18
+ [ -n "$mine" ] || continue
19
+ compared=$((compared + 1))
20
+ if ! grep -qxF "$ref" <<<"$ours"; then
21
+ echo "FAIL template pin '$ref' differs from '$mine'"
22
+ fail=1
23
+ else
24
+ echo "ok $ref"
25
+ fi
26
+ done <<<"$theirs"
27
+ if [ "$compared" -eq 0 ]; then
28
+ echo "FAIL no template pin shares an action with this repository's workflows"
29
+ exit 1
30
+ fi
31
+ exit "$fail"
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # Verify that every action referenced in the given workflow files or folders is pinned
3
+ # to a full commit SHA, carries a "# vX.Y.Z" version comment, and that the tag named
4
+ # in the comment resolves to exactly that commit in the action's own repository.
5
+ # Copied from agusgonzaleznic/github-reusable-workflows, extended to several paths.
6
+ #
7
+ # .github/scripts/verify-action-pins.sh [path...] (default: .github/workflows)
8
+ #
9
+ # Needs an authenticated gh (GH_TOKEN in CI). Exits non-zero on any unpinned
10
+ # reference, missing comment, unknown tag or mismatch, and also when it finds no
11
+ # references at all, so an empty or misread directory cannot pass.
12
+ set -euo pipefail
13
+
14
+ if [ "$#" -eq 0 ]; then set -- .github/workflows; fi
15
+ fail=0
16
+ checked=0
17
+ tab=$(printf '\t')
18
+
19
+ # Print the commit a tag points to, dereferencing annotated tags.
20
+ resolve_tag() {
21
+ local repo=$1 tag=$2 out type sha
22
+ out=$(gh api "repos/$repo/git/ref/tags/$tag" --jq '[.object.type, .object.sha] | @tsv') || return 1
23
+ type=${out%%"$tab"*}
24
+ sha=${out##*"$tab"}
25
+ while [ "$type" = tag ]; do
26
+ out=$(gh api "repos/$repo/git/tags/$sha" --jq '[.object.type, .object.sha] | @tsv') || return 1
27
+ type=${out%%"$tab"*}
28
+ sha=${out##*"$tab"}
29
+ done
30
+ printf '%s\n' "$sha"
31
+ }
32
+
33
+ re='uses:[[:space:]]*([^[:space:]@#]+)@([^[:space:]#]+)[[:space:]]*(#[[:space:]]*([^[:space:]]+))?'
34
+
35
+ while IFS= read -r hit; do
36
+ file=${hit%%:*}
37
+ rest=${hit#*:}
38
+ line=${rest%%:*}
39
+ text=${rest#*:}
40
+ [[ $text =~ $re ]] || continue
41
+ action=${BASH_REMATCH[1]}
42
+ ref=${BASH_REMATCH[2]}
43
+ tag=${BASH_REMATCH[4]:-}
44
+ case $action in ./* | docker://*) continue ;; esac
45
+ repo=$(cut -d/ -f1-2 <<<"$action")
46
+ where="$file:$line $action"
47
+ checked=$((checked + 1))
48
+ if ! [[ $ref =~ ^[0-9a-f]{40}$ ]]; then
49
+ echo "FAIL $where: ref '$ref' is not a full commit SHA"
50
+ fail=1
51
+ continue
52
+ fi
53
+ if [ -z "$tag" ]; then
54
+ echo "FAIL $where: no version comment after the SHA"
55
+ fail=1
56
+ continue
57
+ fi
58
+ if ! want=$(resolve_tag "$repo" "$tag"); then
59
+ echo "FAIL $where: tag $tag not found in $repo"
60
+ fail=1
61
+ continue
62
+ fi
63
+ if [ "$want" != "$ref" ]; then
64
+ echo "FAIL $where: $tag is $want, but the pin is $ref"
65
+ fail=1
66
+ continue
67
+ fi
68
+ echo "ok $where $tag = $ref"
69
+ done < <(grep -rn -E '^[[:space:]]*-?[[:space:]]*uses:' "$@")
70
+
71
+ echo "checked $checked pinned action reference(s) in $*"
72
+ if [ "$checked" -eq 0 ]; then
73
+ echo "FAIL no action references found in $*"
74
+ exit 1
75
+ fi
76
+ exit "$fail"
@@ -0,0 +1,78 @@
1
+ # Tests on every pull request and every push to main, and the action itself run end to end on
2
+ # the made-up sample. Read-only token; nothing is uploaded. Actions are pinned to full commit
3
+ # SHAs with the release in a comment; lint.yml proves each pin.
4
+
5
+ name: CI
6
+
7
+ on:
8
+ pull_request:
9
+ push:
10
+ branches: [main]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: ci-${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ test:
21
+ name: Tests (Python ${{ matrix.python }})
22
+ runs-on: ubuntu-24.04
23
+ timeout-minutes: 15
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ python: ["3.12", "3.13"]
28
+ steps:
29
+ - name: Check out the repository
30
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - name: Set up Python
35
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
36
+ with:
37
+ python-version: ${{ matrix.python }}
38
+
39
+ - name: Install Ghostscript, the gate's third extractor
40
+ run: |
41
+ sudo timeout 300 apt-get update
42
+ sudo timeout 300 apt-get install -y --no-install-recommends ghostscript
43
+
44
+ - name: Install the pinned dependencies
45
+ run: |
46
+ python -m venv .venv
47
+ .venv/bin/pip install --quiet --require-hashes --no-deps --only-binary :all: \
48
+ -r requirements/action.txt -r requirements/test.txt
49
+
50
+ - name: Run the tests
51
+ env:
52
+ PYTHONPATH: src
53
+ run: .venv/bin/python -m pytest -q
54
+
55
+ action:
56
+ name: Action on the sample
57
+ runs-on: ubuntu-24.04
58
+ timeout-minutes: 15
59
+ steps:
60
+ - name: Check out the repository
61
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
62
+ with:
63
+ persist-credentials: false
64
+
65
+ - name: Check, render, gate and package the sample
66
+ uses: ./
67
+ with:
68
+ working-directory: src/cvcite/templates/sample
69
+ package: "true"
70
+ # This repository is public, and the sample person is made up.
71
+ allow-public: "true"
72
+
73
+ - name: The release files are there
74
+ working-directory: src/cvcite/templates/sample
75
+ run: |
76
+ set -euo pipefail
77
+ unzip -l .tmp/release/build.zip
78
+ (cd .tmp/release && unzip -q build.zip && sha256sum -c SHA256SUMS)
@@ -0,0 +1,71 @@
1
+ # Lints the workflows and proves every action pin, on every pull request and push to main. The
2
+ # workflows that `cvcite init` writes are linted too, with their placeholder filled in, since
3
+ # they run in every user's repository.
4
+
5
+ name: Lint
6
+
7
+ on:
8
+ pull_request:
9
+ push:
10
+ branches: [main]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: ${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ actionlint:
21
+ name: actionlint
22
+ runs-on: ubuntu-24.04
23
+ timeout-minutes: 10
24
+ env:
25
+ ACTIONLINT_VERSION: 1.7.12
26
+ # sha256 of actionlint_1.7.12_linux_amd64.tar.gz, from the release's checksums file
27
+ ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
28
+ steps:
29
+ - name: Check out the repository
30
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - name: Install actionlint
35
+ run: |
36
+ tarball="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
37
+ curl -fsSL --max-time 120 -o "$RUNNER_TEMP/$tarball" \
38
+ "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${tarball}"
39
+ echo "${ACTIONLINT_SHA256} ${RUNNER_TEMP}/${tarball}" | sha256sum --check --strict
40
+ tar -xzf "$RUNNER_TEMP/$tarball" -C "$RUNNER_TEMP" actionlint
41
+
42
+ - name: Lint this repository's workflows and the ones init writes
43
+ run: |
44
+ set -euo pipefail
45
+ rendered="$RUNNER_TEMP/rendered/.github/workflows"
46
+ mkdir -p "$rendered"
47
+ for f in src/cvcite/templates/repo/github/workflows/*.yml; do
48
+ sed 's|{{ACTION_REF}}|agusgonzaleznic/cvcite@0000000000000000000000000000000000000000 # v0.0.0|' \
49
+ "$f" > "$rendered/$(basename "$f")"
50
+ done
51
+ "$RUNNER_TEMP/actionlint" -color .github/workflows/*.yml "$rendered"/*.yml
52
+
53
+ pins:
54
+ name: Verify action pins
55
+ runs-on: ubuntu-24.04
56
+ timeout-minutes: 10
57
+ steps:
58
+ - name: Check out the repository
59
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
60
+ with:
61
+ persist-credentials: false
62
+
63
+ - name: Check every action is pinned to the commit its version comment names
64
+ env:
65
+ GH_TOKEN: ${{ github.token }}
66
+ run: |
67
+ .github/scripts/verify-action-pins.sh .github/workflows action.yml \
68
+ src/cvcite/templates/repo/github/workflows
69
+
70
+ - name: Check the workflows init writes pin what this repository pins
71
+ run: .github/scripts/check-template-pins.sh
@@ -0,0 +1,23 @@
1
+ # Python and the toolchain: all regenerable
2
+ .venv/
3
+ .uv-cache/
4
+ .uv-python/
5
+ .uv-tools/
6
+ .pip-cache/
7
+ .cache/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ __pycache__/
11
+ *.egg-info/
12
+ dist/
13
+
14
+ # Build output and scratch. The sample's build lands here, never in git.
15
+ build/
16
+ rendercv_output/
17
+ .tmp/
18
+ .DS_Store
19
+
20
+ # Never commit a real person's CV or identity documents to this repository. It holds the tool
21
+ # and a made-up sample only; a user's CV lives in their own private repo (`cvcite init`).
22
+ inputs/
23
+ *.private.*
cvcite-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agustin Gonzalez Nicolini
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.
cvcite-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.5
2
+ Name: cvcite
3
+ Version: 0.2.0
4
+ Summary: Build a CV from one master YAML file and an overlay per target role, and gate every PDF the way applicant tracking systems read it.
5
+ Project-URL: Source, https://github.com/agusgonzaleznic/cvcite
6
+ Author: Agustin Gonzalez Nicolini
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: ats,claude-code,cv,rendercv,resume,typst
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Office/Business
14
+ Requires-Python: >=3.12
15
+ Requires-Dist: pdfminer-six>=20260107
16
+ Requires-Dist: pypdf>=6.21
17
+ Requires-Dist: rendercv[full]==2.8
18
+ Requires-Dist: ruamel-yaml>=0.19
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest>=8.0; extra == 'test'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # cvcite
24
+
25
+ Build your CV from one master YAML file, tailor it for each kind of role with a small overlay,
26
+ and gate every PDF the way applicant tracking systems (ATS) read it. Every claim on the CV traces
27
+ to a fact you confirmed, with its source.
28
+
29
+ It comes in three parts that work together or alone:
30
+
31
+ - **A command line** (`cvcite`) that checks, renders and gates your CV. No AI involved.
32
+ - **A Claude Code plugin** whose skills interview you, one multiple-choice question at a time:
33
+ they turn an old CV or a LinkedIn export into a fact ledger, check every date and claim, tailor
34
+ the CV for a job posting, and compare LinkedIn, your website and GitHub against it.
35
+ - **A GitHub Action** that builds and gates the CV on every change, publishes the PDFs as a
36
+ private release when you ask, and can run an AI claim review on pull requests with your own key.
37
+
38
+ Your CV lives in your own private repository. This one holds the tool and a made-up sample person.
39
+
40
+ ## Why
41
+
42
+ - **Two CVs drift apart.** Keep one master with every fact; each variant is an overlay that only
43
+ changes emphasis (headline, summary, bolded words, skills line, which bullets to drop).
44
+ - **CVs overclaim.** Every number on the CV must appear in the fact ledger (`facts.yaml`) with
45
+ its source, or the build fails. Facts marked `private` can never reach the CV.
46
+ - **A PDF that looks right can read wrong.** Parsers split hyphenated words, lose ligature glyphs,
47
+ shatter text into one word per line, or read columns in the wrong order. The gate reads each
48
+ PDF with two to four independent extractors and fails it unless they agree and every bullet
49
+ survives whole.
50
+
51
+ ## Quick start
52
+
53
+ You need Python 3.12 or newer (RenderCV 2.8 needs it). [uv](https://docs.astral.sh/uv/) is the easiest way to install;
54
+ Ghostscript is optional (a third extractor for the gate).
55
+
56
+ ```sh
57
+ uv tool install git+https://github.com/agusgonzaleznic/cvcite
58
+ cvcite init my-cv --sample # try it with the made-up sample; drop --sample to start blank
59
+ cd my-cv
60
+ cvcite build # check, render and gate every variant
61
+ ```
62
+
63
+ The PDFs land in `build/<variant>/`, one folder per variant, each holding the file under the same
64
+ name, so a recruiter cannot tell that variants exist. Keep the repository private:
65
+
66
+ ```sh
67
+ git init -b main && git add -A && git commit -m "Start my CV"
68
+ gh repo create my-cv --private --source . --push
69
+ ```
70
+
71
+ ## With Claude Code
72
+
73
+ ```text
74
+ /plugin install cvcite --marketplace agusgonzaleznic/cvcite
75
+ ```
76
+
77
+ Or in two steps: `/plugin marketplace add agusgonzaleznic/cvcite`, then
78
+ `/plugin install cvcite@cvcite`.
79
+
80
+ Then ask in your own words, for example "start my CV from inputs/old-cv.pdf" or "tailor my CV
81
+ for this posting: <link>". The skills:
82
+
83
+ | Skill | What it does |
84
+ |---|---|
85
+ | `cv-ingest` | Reads your old CV, LinkedIn export or JSON Resume, then builds the fact ledger with you, role by role, before writing the CV |
86
+ | `cv-fact-check` | Runs the checks, then an adversarial reviewer that tries to refute every claim; you settle each finding |
87
+ | `cv-tailor` | Maps a job posting's requirements to your facts, writes an overlay for it, builds and reviews it, and names the gaps honestly |
88
+ | `cv-presence-audit` | Compares LinkedIn, your site and GitHub with the CV, and writes paste-ready texts with character counts |
89
+ | `cv-build` | Explains every gate failure and how to fix it |
90
+
91
+ The skills follow the open Agent Skills format, so other agents that read `SKILL.md` files can use
92
+ them too; `cvcite init` also writes an `AGENTS.md` with the rules into your CV repository.
93
+
94
+ The plugin holds only its skills and the reviewer agent, in `plugin/`; the skills run the
95
+ `cvcite` command line you installed above, and ask before installing it if it is missing.
96
+
97
+ **LinkedIn is read only.** The audit works from LinkedIn's own data export or a profile saved as
98
+ PDF. Nothing signs in to your account or edits it: LinkedIn's terms forbid automated access.
99
+
100
+ ## In GitHub Actions
101
+
102
+ `cvcite init` writes three workflows into your CV repository:
103
+
104
+ - `cv.yml` checks, renders and gates every variant on each pull request and each push to `main`.
105
+ - `release.yml` (run by hand) publishes the build as a release you can download anywhere. It
106
+ refuses to run in a public repository, because the PDFs carry your contact details.
107
+ - `cv-review.yml` runs the claim reviewer on pull requests. It does nothing until you add an
108
+ `ANTHROPIC_API_KEY` secret, and each run is billed to that key. It sends `AGENTS.md`,
109
+ `facts.yaml`, `cv.yaml` (contact details included), the overlays and the diff to Anthropic's
110
+ API; facts in `facts.private.yaml` are never committed, so never sent. The AI can only read
111
+ the CV files, and its review is posted as plain text. Use a key made for this alone, with a
112
+ spending limit.
113
+
114
+ The action runs cvcite from the release you pin, with every Python package taken from a lock
115
+ with its hashes and no package built from source. `init` pins it to that release's full commit
116
+ SHA. The action refuses to build in a repository that is not private; set `allow-public: "true"`
117
+ only for a made-up CV, as this repository does for its sample. These guards stop mistakes;
118
+ anyone who can push to your CV repository can still change its workflows.
119
+
120
+ ## What gets checked
121
+
122
+ `cvcite check` (no rendering) and every build:
123
+
124
+ - dates: format, order, future dates, overlaps, gaps over six months
125
+ - every number in a bullet present in a fact of the ledger; none from a `private` fact, and none
126
+ of the `terms` a private fact lists (keep private facts in `facts.private.yaml`, which git
127
+ ignores); every fact's visibility one of `public`, `cv-only` or `private`
128
+ - every skill on a skills line backed by a bullet of the same variant
129
+ - an overlay never adds a skill the master lacks
130
+ - only RenderCV's built-in themes, and no render settings that move files: a custom theme runs
131
+ code from the repository
132
+ - no TODO, no banned character (em dashes by default), no RenderCV trap
133
+ - privacy: no photo, date of birth, nationality, marital status, street address, identity or bank
134
+ number, or WhatsApp link
135
+
136
+ The gate, on every PDF:
137
+
138
+ - the extractors agree on the words and their order, and every bullet appears whole in each
139
+ - no word hyphenated across a line by the typesetter
140
+ - no text layer shattered into single words
141
+ - every glyph plainly filled, in a colour with at least 4.5:1 contrast on white
142
+ - the page count matches the budget in `cvcite.toml`
143
+
144
+ Each check has a test that plants the defect and proves the check fails on it.
145
+
146
+ ## Commands
147
+
148
+ | Command | What it does |
149
+ |---|---|
150
+ | `cvcite init [--sample]` | Creates a CV repository |
151
+ | `cvcite check` | The checks above, without rendering |
152
+ | `cvcite build [variant...]` | Check, render and gate; `-v` prints the full gate report |
153
+ | `cvcite gate <pdf> [yaml]` | Gates one PDF |
154
+ | `cvcite package` | Zips the build with checksums, as a release does |
155
+ | `cvcite ingest <file...>` | Text or data out of a CV (PDF, DOCX, ODT, HTML, Markdown), a LinkedIn export or a JSON Resume |
156
+ | `cvcite compare --linkedin <zip>` | The CV's roles against a LinkedIn export: titles and dates |
157
+ | `cvcite doctor` | Checks the toolchain |
158
+
159
+ ## How it renders
160
+
161
+ [RenderCV](https://github.com/rendercv/rendercv) turns the YAML into [Typst](https://typst.app)
162
+ and then into the PDF. cvcite renders with ligatures and kerning off: with ligatures on, one
163
+ extractor lost every word containing "ff", "ffi" or "ffl", and with kerning on, another read a
164
+ bold "Airflow" as two words. It also ships the one Typst package RenderCV's template imports and
165
+ switches off RenderCV's check for a newer release (a PyPI request made without verifying TLS),
166
+ so a render makes no network request and writes nothing outside the build.
167
+
168
+ ## Development
169
+
170
+ ```sh
171
+ uv venv --python 3.13 && uv pip install -e ".[test]"
172
+ .venv/bin/python -m pytest
173
+ ```
174
+
175
+ ## Licence
176
+
177
+ MIT. RenderCV is MIT-licensed; the vendored Typst package `fontawesome` 0.6.0 is MIT-licensed,
178
+ with its licence in `src/cvcite/vendor/`.
cvcite-0.2.0/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # cvcite
2
+
3
+ Build your CV from one master YAML file, tailor it for each kind of role with a small overlay,
4
+ and gate every PDF the way applicant tracking systems (ATS) read it. Every claim on the CV traces
5
+ to a fact you confirmed, with its source.
6
+
7
+ It comes in three parts that work together or alone:
8
+
9
+ - **A command line** (`cvcite`) that checks, renders and gates your CV. No AI involved.
10
+ - **A Claude Code plugin** whose skills interview you, one multiple-choice question at a time:
11
+ they turn an old CV or a LinkedIn export into a fact ledger, check every date and claim, tailor
12
+ the CV for a job posting, and compare LinkedIn, your website and GitHub against it.
13
+ - **A GitHub Action** that builds and gates the CV on every change, publishes the PDFs as a
14
+ private release when you ask, and can run an AI claim review on pull requests with your own key.
15
+
16
+ Your CV lives in your own private repository. This one holds the tool and a made-up sample person.
17
+
18
+ ## Why
19
+
20
+ - **Two CVs drift apart.** Keep one master with every fact; each variant is an overlay that only
21
+ changes emphasis (headline, summary, bolded words, skills line, which bullets to drop).
22
+ - **CVs overclaim.** Every number on the CV must appear in the fact ledger (`facts.yaml`) with
23
+ its source, or the build fails. Facts marked `private` can never reach the CV.
24
+ - **A PDF that looks right can read wrong.** Parsers split hyphenated words, lose ligature glyphs,
25
+ shatter text into one word per line, or read columns in the wrong order. The gate reads each
26
+ PDF with two to four independent extractors and fails it unless they agree and every bullet
27
+ survives whole.
28
+
29
+ ## Quick start
30
+
31
+ You need Python 3.12 or newer (RenderCV 2.8 needs it). [uv](https://docs.astral.sh/uv/) is the easiest way to install;
32
+ Ghostscript is optional (a third extractor for the gate).
33
+
34
+ ```sh
35
+ uv tool install git+https://github.com/agusgonzaleznic/cvcite
36
+ cvcite init my-cv --sample # try it with the made-up sample; drop --sample to start blank
37
+ cd my-cv
38
+ cvcite build # check, render and gate every variant
39
+ ```
40
+
41
+ The PDFs land in `build/<variant>/`, one folder per variant, each holding the file under the same
42
+ name, so a recruiter cannot tell that variants exist. Keep the repository private:
43
+
44
+ ```sh
45
+ git init -b main && git add -A && git commit -m "Start my CV"
46
+ gh repo create my-cv --private --source . --push
47
+ ```
48
+
49
+ ## With Claude Code
50
+
51
+ ```text
52
+ /plugin install cvcite --marketplace agusgonzaleznic/cvcite
53
+ ```
54
+
55
+ Or in two steps: `/plugin marketplace add agusgonzaleznic/cvcite`, then
56
+ `/plugin install cvcite@cvcite`.
57
+
58
+ Then ask in your own words, for example "start my CV from inputs/old-cv.pdf" or "tailor my CV
59
+ for this posting: <link>". The skills:
60
+
61
+ | Skill | What it does |
62
+ |---|---|
63
+ | `cv-ingest` | Reads your old CV, LinkedIn export or JSON Resume, then builds the fact ledger with you, role by role, before writing the CV |
64
+ | `cv-fact-check` | Runs the checks, then an adversarial reviewer that tries to refute every claim; you settle each finding |
65
+ | `cv-tailor` | Maps a job posting's requirements to your facts, writes an overlay for it, builds and reviews it, and names the gaps honestly |
66
+ | `cv-presence-audit` | Compares LinkedIn, your site and GitHub with the CV, and writes paste-ready texts with character counts |
67
+ | `cv-build` | Explains every gate failure and how to fix it |
68
+
69
+ The skills follow the open Agent Skills format, so other agents that read `SKILL.md` files can use
70
+ them too; `cvcite init` also writes an `AGENTS.md` with the rules into your CV repository.
71
+
72
+ The plugin holds only its skills and the reviewer agent, in `plugin/`; the skills run the
73
+ `cvcite` command line you installed above, and ask before installing it if it is missing.
74
+
75
+ **LinkedIn is read only.** The audit works from LinkedIn's own data export or a profile saved as
76
+ PDF. Nothing signs in to your account or edits it: LinkedIn's terms forbid automated access.
77
+
78
+ ## In GitHub Actions
79
+
80
+ `cvcite init` writes three workflows into your CV repository:
81
+
82
+ - `cv.yml` checks, renders and gates every variant on each pull request and each push to `main`.
83
+ - `release.yml` (run by hand) publishes the build as a release you can download anywhere. It
84
+ refuses to run in a public repository, because the PDFs carry your contact details.
85
+ - `cv-review.yml` runs the claim reviewer on pull requests. It does nothing until you add an
86
+ `ANTHROPIC_API_KEY` secret, and each run is billed to that key. It sends `AGENTS.md`,
87
+ `facts.yaml`, `cv.yaml` (contact details included), the overlays and the diff to Anthropic's
88
+ API; facts in `facts.private.yaml` are never committed, so never sent. The AI can only read
89
+ the CV files, and its review is posted as plain text. Use a key made for this alone, with a
90
+ spending limit.
91
+
92
+ The action runs cvcite from the release you pin, with every Python package taken from a lock
93
+ with its hashes and no package built from source. `init` pins it to that release's full commit
94
+ SHA. The action refuses to build in a repository that is not private; set `allow-public: "true"`
95
+ only for a made-up CV, as this repository does for its sample. These guards stop mistakes;
96
+ anyone who can push to your CV repository can still change its workflows.
97
+
98
+ ## What gets checked
99
+
100
+ `cvcite check` (no rendering) and every build:
101
+
102
+ - dates: format, order, future dates, overlaps, gaps over six months
103
+ - every number in a bullet present in a fact of the ledger; none from a `private` fact, and none
104
+ of the `terms` a private fact lists (keep private facts in `facts.private.yaml`, which git
105
+ ignores); every fact's visibility one of `public`, `cv-only` or `private`
106
+ - every skill on a skills line backed by a bullet of the same variant
107
+ - an overlay never adds a skill the master lacks
108
+ - only RenderCV's built-in themes, and no render settings that move files: a custom theme runs
109
+ code from the repository
110
+ - no TODO, no banned character (em dashes by default), no RenderCV trap
111
+ - privacy: no photo, date of birth, nationality, marital status, street address, identity or bank
112
+ number, or WhatsApp link
113
+
114
+ The gate, on every PDF:
115
+
116
+ - the extractors agree on the words and their order, and every bullet appears whole in each
117
+ - no word hyphenated across a line by the typesetter
118
+ - no text layer shattered into single words
119
+ - every glyph plainly filled, in a colour with at least 4.5:1 contrast on white
120
+ - the page count matches the budget in `cvcite.toml`
121
+
122
+ Each check has a test that plants the defect and proves the check fails on it.
123
+
124
+ ## Commands
125
+
126
+ | Command | What it does |
127
+ |---|---|
128
+ | `cvcite init [--sample]` | Creates a CV repository |
129
+ | `cvcite check` | The checks above, without rendering |
130
+ | `cvcite build [variant...]` | Check, render and gate; `-v` prints the full gate report |
131
+ | `cvcite gate <pdf> [yaml]` | Gates one PDF |
132
+ | `cvcite package` | Zips the build with checksums, as a release does |
133
+ | `cvcite ingest <file...>` | Text or data out of a CV (PDF, DOCX, ODT, HTML, Markdown), a LinkedIn export or a JSON Resume |
134
+ | `cvcite compare --linkedin <zip>` | The CV's roles against a LinkedIn export: titles and dates |
135
+ | `cvcite doctor` | Checks the toolchain |
136
+
137
+ ## How it renders
138
+
139
+ [RenderCV](https://github.com/rendercv/rendercv) turns the YAML into [Typst](https://typst.app)
140
+ and then into the PDF. cvcite renders with ligatures and kerning off: with ligatures on, one
141
+ extractor lost every word containing "ff", "ffi" or "ffl", and with kerning on, another read a
142
+ bold "Airflow" as two words. It also ships the one Typst package RenderCV's template imports and
143
+ switches off RenderCV's check for a newer release (a PyPI request made without verifying TLS),
144
+ so a render makes no network request and writes nothing outside the build.
145
+
146
+ ## Development
147
+
148
+ ```sh
149
+ uv venv --python 3.13 && uv pip install -e ".[test]"
150
+ .venv/bin/python -m pytest
151
+ ```
152
+
153
+ ## Licence
154
+
155
+ MIT. RenderCV is MIT-licensed; the vendored Typst package `fontawesome` 0.6.0 is MIT-licensed,
156
+ with its licence in `src/cvcite/vendor/`.