Skip to content

kottode/cv

Repository files navigation

cv

Resume CLI for init, profile switching, section management, tailoring, ATS checks, and application tracking.

cv now uses a modular Python architecture with a thin entrypoint (cv_core.py) and package modules under cvapp/.

Install

./install.sh

The installer creates an isolated runtime at ~/.local/bin/.cv-runtime, installs dependencies from requirements.txt and requirements-ats.txt, and writes a launcher so cv runs without manual venv activation.

For PDF generation (cv gen), the installer also ensures lualatex is installed.

Re-running install is incremental: dependency install is skipped when requirements.txt has not changed. Use --force to reinstall dependencies.

Optional dependencies:

python3 -m pip install -r requirements.txt
python3 -m pip install -r requirements-ats.txt

Default install target is ~/.local/bin/cv.

Quick Start

cv init john-bang-gang
cv jobs frontend
cv title Frontend Developer

cv now uses a global home directory at ~/Resume.

Development Process

Use this flow when adding or changing commands:

  • Define command behavior and input/output format first.
  • Add or update route wiring in cvapp/commands.py.
  • Put command-level behavior in cvapp/features/<feature>/api.py.
  • Put low-level adapters in cvapp/internal/ (Telegram, scraping, browser automation, LLM).
  • Keep cv_core.py as a thin compatibility entry wrapper only.
  • Keep command names and existing behavior stable unless intentionally changed.
  • Run fast local checks:
python3 -m py_compile cv cv_core.py
./cv help
  • Run feature smoke tests for changed commands (text, URL, and interactive paths when applicable).
  • Reinstall local binary after changes:
sh ./install.sh
  • Validate behavior in a real resume project directory.
  • Update docs in this README when command behavior changes.

Project layout after init:

~/Resume/
  .cv/state.env
  .cv/auto.env
  jobs/
  tailored/

cv init <name> creates ~/Resume and .cv state only. A job workspace is created when you run cv jobs <job> [name].

Resume versions are stored at:

jobs/<job>/<name>.md

Source layout:

cv
cv_core.py
cvapp/
  app.py
  commands.py
  config.py
  errors.py
  strings.py
  utils.py
  features/
    ats/
    content/
    profile/
    resume/
    fit/
    track/
    posts/
    auto/
  internal/
    ats.py
    project.py
    telegram.py
    web.py
    browser.py
    llm.py

Commands

cv init <name>
cv current
cv jobs [job] [name]
cv title <new title>
cv section [list|show|set|add|edit] ...
cv skills [list|add|rm|manage] ...
cv exp [list|add|rm|manage] ...
cv tags [text|url]
cv say <question>
cv fit <text|url>
cv gen [current|all|<path>]
cv tailor [text|url]
cv track [item] [status]
cv posts [fetch|fit|list|all|filtered|show <index>]
cv filters [name|list]
cv auto [status|enable|disable|schedule|unschedule]
cv ats [senior]
cv ci telegram [setup|status|send] [message]
cv help

Interactive Filters

Use profile-based filters to control which fetched posts are accepted during cv posts fit and cv auto enable:

cv filters
cv filters frontend
cv filters list

Profiles are stored in filters/<name>.json and include:

  • seniority tokens
  • preferred locations
  • remote acceptance
  • phone and email identity fields
  • minimum salary
  • job types

Integrations

See docs/integrations.md for setup and script usage of Telegram integration.

See docs/automation.md for automated seek/filter/analyze/grade/store/apply/track/notify flow.

See docs/architecture.md for architecture boundaries, routing design, and decision rationale.

Skills Management

cv skills
cv skills add "TypeScript"
cv skills rm "Skill 1"
cv skills manage

Work Experience Management

List experience summaries, total years, gap months, and title relevancy:

cv exp

Add entry format:

cv exp add "Company|Role|YYYY-MM|YYYY-MM"
cv exp add "Company|Role|YYYY-MM|Present"

Accepted parser formats:

### Company | Title | YYYY-MM to YYYY-MM

### Company
Title (or **Title** or Position: Title)
YYYY-MM to YYYY-MM
- Description bullets...

Tailor Workflow

cv tailor
cv tailor https://example.com/jobs/frontend-engineer
cv tailor "Senior frontend role with React TypeScript"

Interactive prompts:

  1. Company (only for cv tailor and cv tailor "...")
  2. Job title (only for cv tailor and cv tailor "...")
  3. Job description (only when no text/url argument is provided; paste, then Ctrl-D)

For URL input, cv tailor <url> fetches and truncates posting text, does not prompt for company/title, and asks AI to keep the job title exactly as written in the source text.

cv tailor also uses external ATS parser fields as validation hints for AI tailoring.

Output:

tailored/<company>/<title>/<name>.md
tailored/<company>/<title>/<name>.docx

For URL input, output path uses a safe source bucket:

tailored/from-url/<source-ref>/<name>.md
tailored/from-url/<source-ref>/<name>.docx

Requires:

  • copilot CLI for AI tailoring.
  • pandoc for .docx export.

AI Q&A

cv say "Why are you relevant candidate for this role?"

Loads context from markdown files in current project.

Tags Check

cv tags
cv tags "Senior frontend engineer with React, TypeScript, GraphQL"
cv tags https://example.com/jobs/frontend-engineer

Prints meaningful resume tags, total count, and whether total fits recommended 25-35 range. With optional text or URL, also prints job tags and resume coverage of job tags.

Job Fit Check

cv fit "Senior Frontend role with React and TypeScript"
cv fit https://example.com/jobs/frontend-engineer

Behavior:

  • For text input: uses provided text as job description.
  • For URL input: fetches page and extracts primary readable content, including JSON-LD/script-embedded descriptions used by JS-heavy job pages.
  • Runs non-AI keyword overlap precheck and AI fit review.

Application Tracking

cv track "Company Frontend"
cv track "Company Frontend" i2
cv track "Company Frontend" status
cv track

Status aliases:

  • applied / a
  • interview / i / int / i2 / int2
  • rejected / r
  • offer / o
  • ghosted / g

Ghosted status auto-applies after 30 days for open applications.

Storage location is job-local:

jobs/<job>/track.csv

Parsed Posts

cv posts fetch stores fetched post records in:

.cv/posts.db

Score and filter cached posts with:

cv posts fit

View cached and filtered lists with:

cv posts fetch
cv posts fit
cv posts
cv posts all
cv posts filtered
cv posts show 1

Automation

cv auto status
cv auto enable
cv auto disable

cv auto enable runs one automation cycle using .cv/auto.env settings:

  • fetch jobs with JobSpy (AUTO_SEARCH_TERMS, AUTO_JOB_SITES, AUTO_SEARCH_LOCATION)
  • parse and cache posts in .cv/posts.db
  • apply fit filters on cached posts (not during fetch)
  • optionally auto-apply with Playwright (AUTO_APPLY=1)
  • track successful applies in jobs/<job>/track.csv
  • optionally notify via Telegram (AUTO_NOTIFY=1)

Full setup and config reference: docs/automation.md.

ATS Check

cv ats
cv ats senior

Steps:

  1. External ATS parse source: pyresparser (if installed).
  2. Automatic external fallback: spaCy NER parser when pyresparser is unavailable or broken.
  3. Structure + parser field score.
  4. AI score and actionable advice via copilot.

If external ATS parser is not installed, cv ats attempts auto-setup first and prints detailed setup errors if it still fails.

cv ats senior emulates a practical senior-profile ATS filter with pass/fail checks (experience years, title signal, leadership signal, skill overlap, multi-company history).

External ATS parser fields are also reused by:

  • cv tags for tag enrichment.
  • cv exp for extraction validation hints.
  • cv fit for resume keyword enrichment.
  • cv tailor for ATS-guided prompt context.

About

Terminal resume manager

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Contributors