内容
# cv-tailor
<img src="docs/tailor.png" align="right" width="130" alt="cv-tailor">
🌐 **English** · [Español](README.es.md)
**将您的 AI 代理转变为您的求职申请助手。** 通过 MCP 连接,您
的代理可以为您定制简历,以适应每个职位发布,批评和改进它,生成
可直接发送的 PDF/Word 文档,并跟踪每个申请直到面试。
**所有操作都在您的机器上运行:您的数据永远不会上传到任何服务器。** 无需 API
密钥,无需付费服务 — 它与您已经使用的代理(Claude、Cursor、
VS Code… 任何 MCP 客户端)配合使用。
<br clear="right">

## 它解决了什么问题?
申请工作需要根据不同的职位发布内容准备不同版本的简历,且需要使用
正确的语言和格式,同时还要记录发送给谁的哪个版本。如果手动在 Word
中操作,会导致文件重复且难以控制;如果使用网络服务,则需要将您的
工作经历交给第三方。
在这里:
- **您的代理执行工作**:您可以用自然语言要求代理执行操作 —
*"根据此职位发布内容定制我的简历"*、*"批评我的简历"*、*"记录我已申请"*、
*"为面试做准备"* — 代理将使用 MCP 服务器的工具实际执行操作,并提供
验证和指标,而不仅仅是意见。
- **所有内容都是本地且属于您**:您的数据存储在您文件夹中的文件中(可读
取的 JSON,可手动编辑);MCP 服务器在您的机器上运行,不会调用任何外部 API。
- **无需重复**:您只需编写一次数据,然后每个申请都只是一个带有变化
的小变体(标题、技能顺序、求职信)。
- **一键生成所有内容**:PDF、Word、HTML 和 Markdown,支持任意语言 —
无需 AI,也可以作为纯命令行工具使用。
## 快速开始
您需要 [Node.js](https://nodejs.org) ≥ 18(npm、pnpm、yarn 或 [Bun](https://bun.sh))。
有三种使用方式,从最简单到最手动:
### 1. 作为 MCP 服务器安装 — 推荐方式(无需下载仓库)
`cv-tailor` 已发布到 npm,因此您的 MCP 客户端可以通过 `npx` 启动它,无需您克隆或手动安装任何内容。在客户端的配置文件(Claude Desktop/Code、Cursor、VS Code、Windsurf… 任意客户端)中添加以下配置块:
```json
{ "mcpServers": { "cv-tailor": {
"command": "npx", "args": ["-y", "cv-tailor-mcp"],
"env": { "CV_DIR": "/path/to/your-cv-folder" }
} } }
```
`npx -y cv-tailor-mcp` 将在第一次运行下载服务器并缓存它。设置 **`CV_DIR`** 为您的数据文件夹(在 `npx` 下,工作目录不可预测,因此请显式设置)。
如果您还没有数据,可以复制软件包的 `examples/` 文件夹作为起点。
> PDF 格式使用您已经安装的 Chrome/Edge/Chromium(无需下载任何内容);其他格式则无需浏览器。
智能来自 **您已经使用的代理** — 服务器从不调用 LLM,需要 API 密钥,并且所有操作都在您的机器上运行。然后,您可以用自然语言工作;您的代理使用服务器的工具实际执行操作(带有验证和指标),而不仅仅是意见:
| 您告诉您的代理… | 会发生什么 |
|---|---|
| *"根据此职位发布内容定制我的简历"* (+ 粘贴职位发布内容) | 衡量匹配度,选择相关的成就,重写变体 **不创造经验**,并验证分数是否改善 |
| *"我与这个职位发布内容的匹配度是多少?"* | 0–100 分数,包含您涵盖的关键词和您缺少的关键词 |
| *"批评我的简历"* | 可衡量的检查 + 影响批评,带有建议的重写 |
| *"采访我以充实我的成就库"* | 问题 + 存储每个确认的成就,带有指标 |
| *"我已申请 Acme,记录它"* | 记录申请并 **冻结您发送的精确 PDF** |
| *"哪些申请已经停滞?"* | 查看日志并草拟后续跟进 |
| *"为 Acme 面试做准备"* | 可能的问题(包括关于您空白的尴尬问题)以及要回答的成就 |
| *"将我的简历翻译成法语"* | 检测缺少的内容并完成它,验证后 |
完整的循环:**职位发布 → 定制简历 → 记录申请 → 跟进 → 面试**。
### 2. … 或下载代码
如果您想修改工具、贡献或在不依赖 npm 的情况下运行它:
```bash
git clone https://github.com/Overrid3CL/cv-tailor.git && cd cv-tailor
npm install # 或:pnpm install · yarn · bun install
```
要将您的代理连接到本地克隆,请在 MCP 配置中指向文件:
```json
{ "mcpServers": { "cv-tailor": {
"command": "node", "args": ["/path/to/cv-tailor/mcp-server.js"],
"env": { "CV_DIR": "/path/to/your-cv-folder" }
} } }
```
(Claude Code 自动读取仓库的 `.mcp.json` 文件。)
### 3. … 或从命令行使用(无需 AI)
全局安装(`npm install -g cv-tailor`)后,您将获得 `cv-tailor` 命令;从克隆仓库,使用 `node generate.js`:
```bash
# 全局安装:在您的数据文件夹中工作
cd ~/my-cv && cv-tailor --variant tailored
# 从克隆仓库:立即尝试使用示例简历(虚构)
CV_DIR=examples node generate.js --variant tailored
# 使其成为您自己的:复制示例到您的私有文件夹并编辑它
cp -r examples ~/my-cv && CV_DIR=~/my-cv node generate.js --variant tailored
```
简历位于 `<folder>/output/tailored/`(PDF + Markdown)。另外,无 AI 支持:`match.js`(分析职位发布内容)和 `lint.js`(简历质量)— 请参见 [日常使用](#日常使用-命令行)。
> 您的数据存储在 **您的文件夹**(`CV_DIR`)中,从不存储在仓库中 — 这就是为什么根目录下的 `base.json`、`variants/`、`achievements.json` 和 `applications.json` 被忽略的原因。`examples/` 是一个可以直接复制的数据文件夹。
## 工作原理
```
base.json ← 您的完整简历(数据,编写一次)
variants/ ← 每个申请一个文件,只有变化的内容
achievements.json ← (可选)成就库:原始材料用于 AI
applications.json ← (可选)申请日志 + 快照
│
▼
node generate.js --variant <name> [--lang es|en|all] [--format pdf,docx,html,md]
│
▼
output/<variant>/cv-... .pdf .docx .html .md (+ 求职信)
```
三个关键思想:
1. **数据 ≠ 代码**。您的数据全部是 JSON;渲染逻辑不包含任何个人内容。多语言文本编写为 `{ "es": "...", "en": "..." }`(或只是 `{ "en": "..." }` — 无语言是强制的),添加语言无需修改代码。
2. **变体继承自 `base.json`**,并覆盖最小内容:要显示的经验、顺序、标题、技能、求职信。无需重复任何内容。
3. **每次写入都经过验证**(JSON Schema + 引用检查):无效的 JSON 永远不会写入磁盘,无论您编辑它还是 AI 执行的操作。
## 日常使用(命令行)
每个命令都使用 `node`(或 `bun`,如果您使用它 — 两者都支持):
```bash
node generate.js --list # 列出变体
node generate.js --variant tailored --lang all # 所有语言
node generate.js --variant tailored --doc all # 简历 + 求职信
node generate.js --variant tailored --format docx # Word
node generate.js --variant all --lang all # 同时生成所有内容
node generate.js --variant tailored --template modern # 另一个设计
node match.js --job posting.txt --variant all # 哪个变体最适合这个职位发布内容?
cat postings.jsonl | node match.js --jobs-stdin # 大量评分:N 个职位发布内容 → 按分数排序的 [{id, lang, score}]
node lint.js --variant tailored # 简历质量检查(指标、长度...)
node import.js my-resume.json # 从 JSON Resume 导入
npm run validate # 验证所有数据
```
`match.js` 和 `lint.js` 完全是确定性的(无需 AI):它们会评估职位发布内容的关键词覆盖率并检查可衡量的最佳实践。
**大量评分**(`--jobs-stdin`):要一次性筛选数十或数百个职位发布内容 — 例如从数据库导出 — 将 JSONL 通过 stdin 传递(每行一个职位发布内容:`{"id": "job-1", "text": "..."}`),它将返回仅包含 `[{id, lang, score}]` 按分数排序:
```bash
sqlite3 jobs.db "SELECT json_object('id', id, 'text', description) FROM jobs" \
| CV_DIR=~/my-cv node match.js --jobs-stdin > scores.json
```
---
## 参考
<details>
<summary><b>📄 格式和 PDF</b></summary>
| 格式 | 需要 Chromium | 用途 |
|---|---|---|
| `pdf` | 是 | 最终交付(干净的分页和“第 N 页,共 M 页”的页脚在 2+ 页) |
| `docx` | 否 | 需要 Word 的流程 |
| `html` | 否 | 快速预览 / 发布 |
| `md` | 否 | 纯文本 / 版本控制 |
`--format` 接受一个列表(`pdf,docx`)或 `all`。PDF 自动查找您系统的 Chrome、Edge 或 Chromium(无需下载任何内容);如果您的浏览器位于自定义位置,请设置 `PUPPETEER_EXECUTABLE_PATH` 为其可执行文件(`PUPPETEER_LAUNCH_ARGS="--no-sandbox"` 在 Docker/CI 中)。视觉主题:`--template classic|modern`(`templates/` 中的 CSS;添加主题 = 一个新文件)。
</details>
<details>
<summary><b>🗂 <code>base.json</code> 结构</b></summary>
| 字段 | 描述 |
|---|---|
| `name` / `contact` | 姓名和联系方式 |
| `labels` | 每个语言的章节标题(`labels` 的键定义支持的语言;**第一个是主导/撰写语言**)+ 可选的 `closing` 用于求职信 |
| `dateTokens` | 每个目标语言的日期标记替换: `{ "en": { "Ene": "Jan", "Presente": "Present" } }` (日期以主导语言编写) |
| `links` | 页头链接: `[{ "label": "GitHub", "url": "…" }]` |
| `experience[]` | `{ id, title, company, dates, bullets[] }` — 每个文本 `{es,en,…}` 或纯字符串 |
| `education[]` / `languages[]` / `skills` | 教育、语言和技能 (`{ key: { label, value } }`) |
| `custom_sections[]` | 额外的章节(项目、认证...)有三种布局:`entries`(像经验一样)、`list`(项目符号)和 `inline`(`label: value`) |
| `stopwords` / `weak_starters` | 可选:扩展内置的 es/en 列表,用于匹配和检查其他语言或领域 |
可翻译的字符串是按语言键控的对象(`{ "es": "…", "en": "…" }`,或只是 `{ "en": "…" }` 用于仅限英语的简历 — **无需语言是强制的**);纯字符串按原样在每种语言中使用。要添加语言:在 `labels` 中添加其键(如果需要,还要添加到 `dateTokens`)并添加到文本中 — CLI、MCP 和日期(`Intl`)会自动识别。
</details>
<details>
<summary><b>🧩 变体和求职信</b></summary>
创建 `variants/<name>.json` 时,只需包含相对于 `base.json` 的更改:
| 变体字段 | 效果 |
|---|---|
| `headline` / `summary` | 该角色的定位 |
| `experience_ids` | 显示哪些条目,及其顺序 |
| `experience.<id>` | 重写条目的字段(或定义新条目) |
| `skills_order` / `skills.<key>` | 技能排序/筛选和覆盖 |
| `custom_sections.<id>` / `custom_sections_order` | 额外章节的覆盖和排序 |
| `links` / `education` / `languages` | 替换整个数组 |
| `cover_letter` | 求职信(如下) |
求职信仅需要 `body`(段落);`recipient`、`company`、`subject` 和 `closing` 是可选的,并且如果省略日期,则会自动填充本地化日期:
```json
{
"cover_letter": {
"recipient": { "es": "选择团队", "en": "Hiring Team" },
"company": "Example Inc.",
"body": [ { "es": "第一段…", "en": "First paragraph…" } ]
}
}
```
使用 `--doc cover` 或 `--doc all` 生成。 `examples/variants/tailored.json` 中有完整的示例。
</details>
<details>
<summary><b>🏆 成就库和 📌 申请跟踪器</b></summary>
**`achievements.json`** (可选):具有标签、技能、指标和源经验的精细成就。 AI 在定制变体时使用它们作为原始材料(`suggest_achievements` 针对每个职位发布内容进行排名),`mine_achievements` 提示通过采访您来填充库。 `examples/achievements.json` 中的示例。
**`applications.json`** (可选):申请日志 — 公司、角色、日期、发送的变体、匹配分数和状态(`sent` → `interview`/`offer`/`rejected`/`ghosted`)。使用 `log_application` 冻结一个快照到 `applications/<id>/`:**您发送的精确 PDF**、当时变体和职位发布内容。 即使您稍后编辑简历,您也将始终知道该公司看到了什么。
两个文件都存储在 **您的数据文件夹**(参见下一节);它们是您的,可以手动编辑。 在此仓库中,它们被忽略,因为它们是个人数据。
</details>
<details>
<summary><b>📁 数据存储位置(全局安装 / bunx)</b></summary>
代码(模式、主题、脚本)与软件包一起旅行;**您的数据不会**。 文件(`base.json`、`variants/`、`achievements.json`、`applications.json`、`output/`)从 **当前目录** 或 `CV_DIR` 解析:
```bash
cd ~/my-cv && cv-tailor --variant tailored # 数据在 ~/my-cv
CV_DIR=~/my-cv cv-tailor --variant tailored # 从任意位置
```
对于全局二进制文件:`npm install -g cv-tailor`(或 `npm link` / `bun link` 在克隆内部)。 从仓库本身运行(`node generate.js …`),数据文件夹是仓库根目录。
</details>
<details>
<summary><b>🔌 MCP 参考:工具、提示和资源</b></summary>
**工具**(全部确定性;每次写入都经过验证):
| 工具 | 描述 |
|---|---|
| `get_base` / `update_base` / `update_base_section` | 读取和更新 `base.json` |
| `list_variants` / `get_variant` / `upsert_variant` / `delete_variant` | 管理变体 |
| `import_json_resume` | 导入 [JSON Resume](https://jsonresume.org) 格式的简历 |
| `validate` | 所有数据的验证报告 |
| `generate_cv` | 生成文档(`doc`、`lang`、`format`、`template`) |
| `analyze_job_match` | 职位发布内容↔简历分数、涵盖/缺少的关键词、变体排名 |
| `analyze_jobs_batch` | 大量评分:N 个职位发布内容(JSONL 文件或内联)→ 仅限排序的 `[{id, lang, score}]` — 筛选前 |
| `list/upsert/delete_achievement` + `suggest_achievements` | 成就库 + 每个职位发布内容排名 |
| `lint_cv` | 可衡量的简历质量 |
| `find_missing_translations` | 缺少语言的字段及其确切路径 |
| `log/update/list/delete_application` | 申请跟踪器 + 快照 |
**提示**(它们一步一步地引导您的代理):`tailor_cv`、`review_cv`、`translate_cv`、`mine_achievements`、`interview_prep`、`follow_up_applications`。 每个提示都会引导您的代理**以您的语言回复**。
**资源`:`cv://base`、`cv://variants/<name>`、`cv://achievements`、`cv://applications`、`cv://schemas/base`、`cv://schemas/variant`。
**注册服务器** — 它是标准 stdio(`node /path/to/repo/mcp-server.js`),所以任何 MCP 客户端都接受 `mcpServers` 块显示的示例:Claude Code:`claude mcp add cv-tailor -- node /path/to/repo/mcp-server.js`(添加 `--scope user` 适用于所有项目);Claude Desktop:在 `claude_desktop_config.json` 中添加块并重启;Cursor/VS Code/Windsurf:每个客户端的相应配置中添加相同的块。设置 `CV_DIR` 服务器的 `env` 如果您的数据不在客户端的工作目录中。
</details>
<details>
<summary><b>🧪 测试和验证</b></summary>
```bash
npm run validate # 验证 `base.json` 和每个变体(适用于 node)
bun test # 100+ 个单元测试(开发测试运行器是 bun)
```
要 **使用** 工具,您只需要 Node ≥ 18;Bun 仅在开发过程中需要,用于运行测试。CI 检查两种路径:bun(测试)和 node+npm(实际安装、验证和生成)。
CI(GitHub Actions)在每次推送和拉取请求时都会运行在两个路径上。数据具有 JSON Schema(`schemas/`)和语义检查:引用必须存在,新条目必须完整,ID 必须唯一。
</details>
## 贡献
欢迎提出想法、问题和 PR — 请参见 [CONTRIBUTING.md](CONTRIBUTING.md)。
## 许可
[MIT](LICENSE)