npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@phuthuycoding/markcv

v0.2.0

Published

Markdown resume builder that lints your CV and explains why it will not fit two pages. ATS-safe PDF, job-description tailoring, CLI + MCP server for AI agents.

Downloads

1,034

Readme

markcv

CI npm docs

Markdown resume builder that does two things other markdown-to-PDF tools do not: it tells you why your CV will not fit on two pages, and it lints the writing itself. Runs as a CLI and as an MCP server, so an AI agent can do both on its own.

  • fit tells you why your CV does not fit on 2 pages. "Content too long" and "bad page break" are different illnesses with opposite cures — trimming words while the real culprit is a heading sitting 31px from the bottom of page 1 just wastes your time.
  • lint checks content, not formatting. It catches overselling and underselling — claiming less than you did is a mistake too, and it costs you something while gaining nothing.

Install

npm install -g @phuthuycoding/markcv

The package is scoped; the commands are not. You type markcv and markcv-mcp.

Or run it without installing:

npx @phuthuycoding/markcv fit cv.md --pages 2

Requires a Chromium-based browser already on your machine (Chrome, Chromium, Edge, Brave). markcv deliberately does not download its own Chromium — it uses puppeteer-core, so the install stays small. If your browser lives somewhere unusual, point at it with MARKCV_CHROME=/path/to/chrome.

Usage

markcv render cv.md -o Output.pdf     # build the PDF, report the page count
markcv fit cv.md --pages 2            # why it does not fit yet
markcv lint cv.md                     # audit the content
markcv tailor cv.md --jd jd.txt       # compare against a job description
markcv new techlead --from cv-master.md
markcv list                           # every CV in the folder
markcv diff cv-a.md cv-b.md           # what a tailored copy dropped
markcv build --pages 2                # build every cv-*.md
markcv skills install                 # install the agent skills (see below)

fit — page-break diagnosis

✗ 3 pages (target 2)
· content 1986px / 2080px available
· 31px wasted by page breaks

Blocks pushed to a new page:
  PROJECT HIGHLIGHTS (H2, y=1009) → wastes 31px at the end of page 1

Suggestions:
  Content HAS ROOM (94px to spare) — length is not the problem, the page break is.
  "PROJECT HIGHLIGHTS" at y=1009 has only 31px left before the end of page 1, so the whole block moved down.
  Fix: reorder sections, cut ~3 lines above it, or use --theme compact.

lint — rules

| Rule | What it catches | |---|---| | over-claim | spearheaded, rigorous, comprehensive, excellence… — self-praise nobody can verify | | under-claim | advised on / worked on next to real scale — you are probably selling yourself short | | tense | a finished job still described in the present tense | | unsupported-skill | a skill listed under SKILLS with no experience line backing it | | duplicate | two bullets saying the same thing | | role-mismatch | an IC job title paired with people-management language | | no-metric | a long bullet with no number in it | | ats-emoji | emoji in a heading — risky for ATS parsers | | long-bullet | a bullet long enough that skimmers will skip it |

Examples

Three complete CVs in examples/, written for different roles so you can see how the same format stretches. Each one builds with the default theme and no photo.

With a portrait photo

Markets differ: a CV in Berlin or Toronto normally carries no photo, while one in Vietnam, Japan or Germany's more traditional employers usually does. Drop a photo.jpg next to the markdown file and it lands in the top-right corner.

The photo lives in its own folder because detection is per-directory: any .md file next to a photo.* picks it up. Keep photo-less CVs in a separate folder, or pass --no-photo.

Build them yourself:

markcv build examples --pages 2
markcv render examples/with-photo/fullstack-engineer.md --pages 1

Note what the bullets in those samples have in common: a number, or a before and after. lint exists to push a CV in that direction — the samples are what it is aiming at, and test/fixtures/bad-cv.md is what it is aiming away from.

MCP server

Lets an AI agent (Claude Code, Claude Desktop, Cursor…) build and audit CVs on its own.

Nothing to install: npx fetches the package on first use and caches it.

Register it with a client

Claude Code — add to .mcp.json in your project (shared with the team), or ~/.claude.json (just you):

{
  "mcpServers": {
    "markcv": {
      "command": "npx",
      "args": ["-y", "@phuthuycoding/markcv", "mcp"],
      "cwd": "/path/to/your/cv/folder"
    }
  }
}

Or from the command line:

claude mcp add markcv -- npx -y @phuthuycoding/markcv mcp
claude mcp list          # confirm it connected

Restart the client afterwards so it picks the server up.

If you installed the package globally, "command": "markcv-mcp" with no args works too and starts marginally faster. The repo ships .mcp.json.example with both shapes.

Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows), same shape, then restart the app.

Cursor — .cursor/mcp.json in the project, same shape.

File paths in tool arguments

Every tool takes a file path, and relative paths resolve against the server process's working directory — hence the cwd above. Without it, pass absolute paths instead.

Tools

| Tool | Purpose | |---|---| | render_cv | build the PDF, return the real page count plus layout numbers | | check_fit | why it does not fit in N pages: too long, or bad page breaks | | lint_cv | audit content (overselling, underselling, tense, unbacked claims…) | | tailor_to_jd | compare against a job description (jd_file or jd_text) | | list_variants | list every CV in a folder | | new_variant | start a tailored copy from a master file | | diff_variants | compare two versions, see what a tailored copy dropped |

Every tool returns structured JSON, not prose — so an agent can loop on it: edit the markdown → check_fit → read slackPx and culprits → edit again, until it fits.

Requirements

Node >= 18 and a Chromium-based browser (only render and check_fit need it). If it is in a non-standard location:

{ "mcpServers": { "markcv": { "command": "node", "args": ["..."],
  "env": { "MARKCV_CHROME": "/path/to/chrome" } } } }

Agent skills

The MCP server gives an agent the tools. Skills give it the judgement — when a page-break diagnosis means "reorder", when it means "cut", and what a bullet should sound like. They are plain Markdown files an agent reads on demand.

markcv skills               # what ships with the package
markcv skills install       # copy into ~/.claude/skills
markcv skills install topcv # just one

| Skill | What it covers | |---|---| | markcv | Writing and auditing a CV with markcv: reading a fit report correctly, the lint rules, bullet style, keeping several variants in sync. | | topcv | Publishing a Markdown CV to TopCV.vn (Vietnamese job market) — both the CV builder and the profile page. |

Existing skills are never overwritten, so your own edits survive; pass --force when you do want the bundled version back. Restart the agent afterwards so it sees them.

The topcv skill drives a real browser and additionally needs the chrome-devtools MCP server:

{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest", "--autoConnect"]
    }
  }
}
claude mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest --autoConnect

CV format

Plain Markdown. The only convention lives in the header:

# Your Name

**Email:** [email protected]
**Phone:** +84 9xx xxx xxx

***

## OBJECTIVE
...

## WORK EXPERIENCE

### Company Name

**Job Title** | Jan 2020 - Dec 2023

* Bullet...

**Label:** value lines directly under # Your Name become the contact block. A line containing | is read as job title + dates — and lint uses those dates to know whether a job has ended.

Portrait photo: drop photo.jpg (or photo.png, avatar.jpg) next to the .md file and it is embedded in the top-right corner. Without one you get an empty placeholder box. Photos over 400KB trigger a warning, because they push the PDF past the upload limit many job portals enforce.

Skip a file in markcv build: put <!-- markcv:no-build --> near the top. Useful for a master file that is a content store rather than something you submit.

Themes

classic (default) and compact. Both are single-column, emoji-free, with a real text layer — safe for ATS parsers.

How this differs from other Markdown resume tools

Rendering Markdown into a good-looking CV is well covered — @resumx/resumx, markdown-resume and others do it well, and if that is all you need, use one of them.

markcv exists for the part that comes after the rendering works:

| | Typical Markdown→PDF tool | markcv | |---|---|---| | Render to PDF | yes | yes | | Themes | yes | two, ATS-safe | | Why it does not fit N pages | — | names the block, the wasted pixels, and whether length or a page break is at fault | | Content audit | — | overselling, underselling, tense, unbacked skills, duplicates | | Job-description match | — | missing requirements, SKILLS-only claims, unrelated bullets | | Usable by an AI agent | — | MCP server, structured JSON |

Author

Built by Ta Manh Quyen — a staff engineer who got tired of guessing why a CV spilled onto a third page. More writing at quyentm.dev.

License

MIT