mirror of
https://github.com/rendercv/rendercv.git
synced 2026-10-11 07:54:14 -04:00
Move distributable skill to rendercv/rendercv-skill submodule
The skill file lived in the main repo, which meant `npx skills add` had to clone the entire RenderCV codebase and exposed internal dev skills. A dedicated lightweight repo solves both problems. - Create rendercv/rendercv-skill repo as a read-only distribution channel - Add it as a submodule at .claude/skills/rendercv-skill/ - Update generation script and tests to write to the submodule path - Add submodules: true to test workflow checkout steps - Update docs, README, and install commands to use rendercv/rendercv-skill - Add --recursive clone instruction to developer guide - Delete the old skills/ directory at repo root
This commit is contained in:
1 parent
021c9fbde3
commit
9b83f07ac9
13 files changed
+69
-822
No files matched your search
Submodule
+1
Submodule .claude/skills/rendercv-skill added at 921bb1cb5a.
@@ -18,6 +18,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
@@ -47,6 +49,8 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule ".claude/skills/rendercv-skill"]
|
||||
path = .claude/skills/rendercv-skill
|
||||
url = https://github.com/rendercv/rendercv-skill.git
|
||||
@@ -141,10 +141,10 @@ locale:
|
||||
Let AI coding agents create and edit your CV. Install the RenderCV skill:
|
||||
|
||||
```bash
|
||||
npx skills add rendercv/rendercv
|
||||
npx skills add rendercv/rendercv-skill
|
||||
```
|
||||
|
||||
Works with Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and [20+ other agents](https://docs.rendercv.com/user_guide/how_to/use_the_ai_agent_skill).
|
||||
Works with Claude Code, Claude Desktop, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and [20+ other agents](https://docs.rendercv.com/user_guide/how_to/use_the_ai_agent_skill).
|
||||
|
||||
## Get Started
|
||||
|
||||
|
||||
Binary file not shown.
@@ -51,8 +51,9 @@ RenderCV has 4 workflows. Each handles a specific automation task.
|
||||
|
||||
**What it does:**
|
||||
|
||||
1. Runs `just test-coverage` across **9 different environments** (3 operating systems × 3 Python versions: 3.12, 3.13, 3.14)
|
||||
2. Combines all coverage reports and uploads them to show the coverage report
|
||||
1. Checks out the repository with submodules (the [rendercv-skill](https://github.com/rendercv/rendercv-skill) submodule is needed for generated-file staleness tests)
|
||||
2. Runs `just test-coverage` across **9 different environments** (3 operating systems × 3 Python versions: 3.12, 3.13, 3.14)
|
||||
3. Combines all coverage reports and uploads them to show the coverage report
|
||||
|
||||
### 2. [`deploy-docs.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/deploy-docs.yaml): Deploy Documentation
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Install them by following their official installation guides:
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/rendercv/rendercv.git
|
||||
git clone --recursive https://github.com/rendercv/rendercv.git
|
||||
```
|
||||
|
||||
and change to the repository directory:
|
||||
@@ -30,6 +30,9 @@ Install them by following their official installation guides:
|
||||
cd rendercv
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The `--recursive` flag clones the [rendercv-skill](https://github.com/rendercv/rendercv-skill) submodule. If you forgot it, run `git submodule update --init` inside the repo.
|
||||
|
||||
2. Set up the development environment (creates a virtual environment in `./.venv` with all dependencies):
|
||||
|
||||
```bash
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ locale:
|
||||
Let AI coding agents create and edit your CV. Install the RenderCV skill:
|
||||
|
||||
```bash
|
||||
npx skills add rendercv/rendercv
|
||||
npx skills add rendercv/rendercv-skill
|
||||
```
|
||||
|
||||
Works with Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and [20+ other agents](https://docs.rendercv.com/user_guide/how_to/use_the_ai_agent_skill).
|
||||
|
||||
@@ -7,6 +7,7 @@ RenderCV provides an AI agent skill that teaches AI coding assistants how to cre
|
||||
The skill works with any AI coding agent that supports the skills protocol, including:
|
||||
|
||||
- Claude Code
|
||||
- Claude Desktop
|
||||
- Cursor
|
||||
- Codex
|
||||
- Copilot
|
||||
@@ -19,30 +20,39 @@ The skill works with any AI coding agent that supports the skills protocol, incl
|
||||
=== "Vercel Skills CLI"
|
||||
|
||||
```bash
|
||||
npx skills add rendercv/rendercv
|
||||
npx skills add rendercv/rendercv-skill
|
||||
```
|
||||
|
||||
You can also target a specific agent:
|
||||
|
||||
```bash
|
||||
npx skills add rendercv/rendercv -a claude-code
|
||||
npx skills add rendercv/rendercv -a cursor
|
||||
npx skills add rendercv/rendercv -a codex
|
||||
npx skills add rendercv/rendercv-skill -a claude-code
|
||||
npx skills add rendercv/rendercv-skill -a cursor
|
||||
npx skills add rendercv/rendercv-skill -a codex
|
||||
```
|
||||
|
||||
=== "OpenSkills"
|
||||
|
||||
```bash
|
||||
npx openskills install rendercv/rendercv
|
||||
npx openskills install rendercv/rendercv-skill
|
||||
```
|
||||
|
||||
=== "Claude Desktop"
|
||||
|
||||
1. Download [`rendercv_skill.zip`](../../assets/rendercv_skill.zip).
|
||||
2. In Claude Desktop, go to **Customize > Skills**.
|
||||
3. Click **"+"** and select **"Upload a skill"**.
|
||||
4. Upload the downloaded ZIP file.
|
||||
|
||||
The skill will appear in your Skills list and Claude will automatically use it when working with your CV.
|
||||
|
||||
=== "Manual"
|
||||
|
||||
Copy the content of [`skills/rendercv/SKILL.md`](https://github.com/rendercv/rendercv/blob/main/skills/rendercv/SKILL.md) into your agent's skill directory. For example, for Claude Code:
|
||||
Copy the content of [`skills/rendercv/SKILL.md`](https://github.com/rendercv/rendercv-skill/blob/main/skills/rendercv/SKILL.md) into your agent's skill directory. For example, for Claude Code:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/rendercv/rendercv.git
|
||||
cp -r rendercv/skills/rendercv ~/.claude/skills/
|
||||
git clone https://github.com/rendercv/rendercv-skill.git
|
||||
cp -r rendercv-skill/skills/rendercv ~/.claude/skills/
|
||||
```
|
||||
|
||||
## What the Skill Provides
|
||||
|
||||
@@ -10,6 +10,9 @@ const skillPath = path.resolve(
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
".claude",
|
||||
"skills",
|
||||
"rendercv-skill",
|
||||
"skills",
|
||||
"rendercv",
|
||||
"SKILL.md",
|
||||
|
||||
@@ -12,6 +12,7 @@ import ast
|
||||
import io
|
||||
import pathlib
|
||||
import re
|
||||
import zipfile
|
||||
|
||||
import jinja2
|
||||
import ruamel.yaml
|
||||
@@ -27,8 +28,17 @@ from rendercv.schema.sample_generator import (
|
||||
repository_root = pathlib.Path(__file__).parent.parent.parent
|
||||
script_directory = pathlib.Path(__file__).parent
|
||||
template_path = script_directory / "skill_template.j2.md"
|
||||
output_path = repository_root / "skills" / "rendercv" / "SKILL.md"
|
||||
output_path = (
|
||||
repository_root
|
||||
/ ".claude"
|
||||
/ "skills"
|
||||
/ "rendercv-skill"
|
||||
/ "skills"
|
||||
/ "rendercv"
|
||||
/ "SKILL.md"
|
||||
)
|
||||
llms_txt_path = repository_root / "docs" / "llms.txt"
|
||||
skill_zip_path = repository_root / "docs" / "assets" / "rendercv_skill.zip"
|
||||
|
||||
# Paths to key model source files for dynamic inclusion.
|
||||
models_dir = repository_root / "src" / "rendercv" / "schema" / "models"
|
||||
@@ -286,7 +296,7 @@ def build_template_context() -> dict:
|
||||
|
||||
|
||||
def generate_skill_file() -> None:
|
||||
"""Render the Jinja2 template and write SKILL.md and docs/llms.txt."""
|
||||
"""Render the Jinja2 template and write SKILL.md, docs/llms.txt, and ZIP."""
|
||||
env = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader(script_directory),
|
||||
keep_trailing_newline=True,
|
||||
@@ -302,6 +312,11 @@ def generate_skill_file() -> None:
|
||||
output_path.write_text(rendered, encoding="utf-8")
|
||||
llms_txt_path.write_text(rendered, encoding="utf-8")
|
||||
|
||||
# Generate ZIP for Claude Desktop skill upload
|
||||
skill_zip_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with zipfile.ZipFile(skill_zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
zf.writestr("rendercv/SKILL.md", rendered)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_skill_file()
|
||||
|
||||
@@ -1,805 +0,0 @@
|
||||
---
|
||||
name: rendercv
|
||||
description: >-
|
||||
Create professional CVs and resumes with perfect typography using RenderCV
|
||||
(v2.8). Users write content in YAML, and RenderCV produces
|
||||
publication-quality PDFs via Typst typesetting. Full control over every visual
|
||||
detail: colors, fonts, margins, spacing, section title styles, entry layouts,
|
||||
and more. 6 built-in themes with unlimited
|
||||
customization. Any language supported (20 built-in, or define your own). Outputs
|
||||
PDF, PNG, HTML, and Markdown. Use when the user wants to create, edit,
|
||||
customize, or render a CV or resume.
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Available themes:** `classic`, `engineeringclassic`, `engineeringresumes`, `harvard`, `moderncv`, `sb2nov`
|
||||
**Available locales:** `english`, `arabic`, `danish`, `dutch`, `french`, `german`, `hebrew`, `hindi`, `indonesian`, `italian`, `japanese`, `korean`, `mandarin_chinese`, `norwegian_bokmål`, `norwegian_nynorsk`, `persian`, `portuguese`, `russian`, `spanish`, `turkish`
|
||||
|
||||
These are starting points — every aspect of the design and locale can be fully customized in the YAML file.
|
||||
|
||||
```bash
|
||||
# Install RenderCV
|
||||
uv tool install "rendercv[full]"
|
||||
|
||||
# Create a starter YAML file (you can specify theme and locale)
|
||||
rendercv new "John Doe"
|
||||
rendercv new "John Doe" --theme moderncv --locale german
|
||||
|
||||
# Render to PDF (also generates Typst, Markdown, HTML, PNG by default)
|
||||
rendercv render John_Doe_CV.yaml
|
||||
|
||||
# Watch mode: auto-re-render whenever the YAML file changes
|
||||
rendercv render John_Doe_CV.yaml --watch
|
||||
|
||||
# Render only PNG (useful for previewing or checking page count)
|
||||
rendercv render John_Doe_CV.yaml --dont-generate-pdf --dont-generate-html --dont-generate-markdown
|
||||
|
||||
# Override fields from the CLI without editing the YAML
|
||||
rendercv render cv.yaml --cv.name "Jane Doe" --design.theme "moderncv"
|
||||
```
|
||||
|
||||
## YAML Structure
|
||||
|
||||
A RenderCV input has four sections. Only `cv` is required — the others have sensible defaults.
|
||||
|
||||
```yaml
|
||||
cv: # Your content: name, contact info, and all sections
|
||||
design: # Visual styling: theme, colors, fonts, margins, spacing, layouts
|
||||
locale: # Language: month names, phrases, translations
|
||||
settings: # Behavior: output paths, bold keywords, current date
|
||||
```
|
||||
|
||||
**Single file vs. separate files:** All four sections can live in one YAML file, or each can be a separate file. Separate files are useful for reusing the same design/locale across multiple CVs:
|
||||
|
||||
```bash
|
||||
# Single self-contained file (all sections in one file)
|
||||
rendercv render John_Doe_CV.yaml
|
||||
|
||||
# Separate files: CV content + design + locale loaded independently
|
||||
rendercv render cv.yaml --design design.yaml --locale-catalog locale.yaml --settings settings.yaml
|
||||
```
|
||||
|
||||
When using separate files, each file contains only its section (e.g., `design.yaml` has `design:` as the top-level key). CLI-loaded files override values in the main YAML file.
|
||||
|
||||
The YAML maps directly to Pydantic models. The complete type-safe schema is provided below so you can understand every field, its type, and its default value.
|
||||
|
||||
## Pydantic Schema
|
||||
|
||||
The YAML input is validated against these Pydantic models.
|
||||
|
||||
### Top-Level Model
|
||||
|
||||
```python
|
||||
class RenderCVModel(BaseModelWithoutExtraKeys):
|
||||
cv: Cv = pydantic.Field(default_factory=Cv, title='CV', description='The content of the CV.')
|
||||
design: Design = pydantic.Field(default_factory=ClassicTheme, title='Design')
|
||||
locale: Locale = pydantic.Field(default_factory=EnglishLocale, title='Locale Catalog')
|
||||
settings: Settings = pydantic.Field(default_factory=Settings, title='RenderCV Settings', description='The settings of the RenderCV.')
|
||||
|
||||
```
|
||||
|
||||
### CV Content (`cv`)
|
||||
|
||||
The `cv.sections` field is a dictionary where keys are section titles (any string you want) and values are lists of entries. Each section contains entries of the same type.
|
||||
|
||||
```python
|
||||
class Cv(BaseModelWithoutExtraKeys):
|
||||
name: str | None = pydantic.Field(default=None, examples=['John Doe', 'Jane Smith'])
|
||||
headline: str | None = pydantic.Field(default=None, examples=['Software Engineer', 'Data Scientist', 'Product Manager'])
|
||||
location: str | None = pydantic.Field(default=None, examples=['New York, NY', 'London, UK', 'Istanbul, Türkiye'])
|
||||
email: pydantic.EmailStr | list[pydantic.EmailStr] | None = pydantic.Field(default=None, examples=['john.doe@example.com', ['john.doe.1@example.com', 'john.doe.2@example.com']])
|
||||
photo: ExistingPathRelativeToInput | pydantic.HttpUrl | None = pydantic.Field(default=None, union_mode='left_to_right', examples=['photo.jpg', 'images/profile.png', 'https://example.com/photo.jpg'])
|
||||
phone: pydantic_phone_numbers.PhoneNumber | list[pydantic_phone_numbers.PhoneNumber] | None = pydantic.Field(default=None, examples=['+1-234-567-8900', ['+1-234-567-8900', '+44 20 1234 5678']])
|
||||
website: pydantic.HttpUrl | list[pydantic.HttpUrl] | None = pydantic.Field(default=None, examples=['https://johndoe.com', ['https://johndoe.com', 'https://www.janesmith.dev']])
|
||||
social_networks: list[SocialNetwork] | None = pydantic.Field(default=None)
|
||||
custom_connections: list[CustomConnection] | None = pydantic.Field(default=None, examples=[[{'placeholder': 'Book a call', 'url': 'https://cal.com/johndoe', 'fontawesome_icon': 'calendar-days'}]])
|
||||
sections: dict[str, Section] | None = pydantic.Field(default=None, examples=[{'Experience': '...', 'Education': '...', 'Projects': '...', 'Skills': '...'}])
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
type SocialNetworkName = Literal['LinkedIn', 'GitHub', 'GitLab', 'IMDB', 'Instagram', 'ORCID', 'Mastodon', 'StackOverflow', 'ResearchGate', 'YouTube', 'Google Scholar', 'Telegram', 'WhatsApp', 'Leetcode', 'X', 'Bluesky', 'Reddit']
|
||||
|
||||
available_social_networks = get_args(SocialNetworkName.__value__)
|
||||
|
||||
class SocialNetwork(BaseModelWithoutExtraKeys):
|
||||
network: SocialNetworkName = pydantic.Field()
|
||||
username: str = pydantic.Field(examples=['john_doe', '@johndoe@mastodon.social', '12345/john-doe'])
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
class CustomConnection(BaseModelWithoutExtraKeys):
|
||||
fontawesome_icon: str
|
||||
placeholder: str
|
||||
url: pydantic.HttpUrl | None
|
||||
|
||||
```
|
||||
|
||||
### Entry Types
|
||||
|
||||
`cv.sections` is a dictionary: keys are section titles (any string), values are lists of entries. Each section must use a **single** entry type — you cannot mix different entry types within the same section. The entry type is auto-detected from the fields present in each entry.
|
||||
|
||||
**Shared fields** — these are available on entry types that support dates and complex fields (ExperienceEntry, EducationEntry, NormalEntry, PublicationEntry):
|
||||
|
||||
| Field | Type | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `date` | `str \| int \| null` | `null` | Free-form: `"2020-09"`, `"Fall 2023"`, etc. Mutually exclusive with `start_date`/`end_date`. |
|
||||
| `start_date` | `str \| int \| null` | `null` | Strict format: YYYY-MM-DD, YYYY-MM, or YYYY. |
|
||||
| `end_date` | `str \| int \| "present" \| null` | `null` | Same formats as `start_date`, or `"present"`. Omitting defaults to `"present"` when `start_date` is set. |
|
||||
| `location` | `str \| null` | `null` | |
|
||||
| `summary` | `str \| null` | `null` | |
|
||||
| `highlights` | `list[str] \| null` | `null` | Bullet points. |
|
||||
|
||||
**9 entry types:**
|
||||
|
||||
| Entry Type | Required Fields | Optional Fields | Typical Use |
|
||||
|---|---|---|---|
|
||||
| **ExperienceEntry** | `company`, `position` | all shared fields | Jobs, positions |
|
||||
| **EducationEntry** | `institution`, `area` | `degree` + all shared fields | Degrees, schools |
|
||||
| **PublicationEntry** | `title`, `authors` | `doi`, `url`, `journal`, `summary`, `date` | Papers, articles |
|
||||
| **NormalEntry** | `name` | all shared fields | Projects, awards |
|
||||
| **OneLineEntry** | `label`, `details` | — | Skills, languages |
|
||||
| **BulletEntry** | `bullet` | — | Simple bullet points |
|
||||
| **NumberedEntry** | `number` | — | Numbered list items |
|
||||
| **ReversedNumberedEntry** | `reversed_number` | — | Reverse-numbered items (5, 4, 3...) |
|
||||
| **TextEntry** | *(plain string)* | — | Free-form paragraphs |
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
cv:
|
||||
sections:
|
||||
experience: # list of ExperienceEntry (detected by company + position)
|
||||
- company: Google
|
||||
position: Engineer
|
||||
start_date: 2020-01
|
||||
highlights:
|
||||
- Did something impactful
|
||||
skills: # list of OneLineEntry (detected by label + details)
|
||||
- label: Languages
|
||||
details: Python, C++
|
||||
about_me: # list of TextEntry (plain strings)
|
||||
- This is a free-form paragraph about me.
|
||||
```
|
||||
|
||||
Entries also accept arbitrary extra keys (silently ignored during rendering). A typo in a field name will NOT cause an error.
|
||||
|
||||
### Design (`design`)
|
||||
|
||||
All built-in themes share the same structure — they only differ in default values. See the sample designs below for every available field and its default. Set `design.theme` to pick a theme, then override any field.
|
||||
|
||||
### Locale (`locale`)
|
||||
|
||||
Built-in locales: `english`, `arabic`, `danish`, `dutch`, `french`, `german`, `hebrew`, `hindi`, `indonesian`, `italian`, `japanese`, `korean`, `mandarin_chinese`, `norwegian_bokmål`, `norwegian_nynorsk`, `persian`, `portuguese`, `russian`, `spanish`, `turkish`
|
||||
|
||||
Set `locale.language` to a built-in locale name to use it. Override any field to customize translations. Set `language` to any string and provide all translations for a fully custom locale.
|
||||
|
||||
### Settings (`settings`)
|
||||
|
||||
Key fields: `bold_keywords` (list of strings to auto-bold), `current_date` (override today's date), `render_command.*` (output paths, generation flags).
|
||||
|
||||
## Important Patterns
|
||||
|
||||
### YAML quoting
|
||||
|
||||
**ALWAYS quote string values that contain a colon (`:`).** This is the most common cause of invalid YAML. Highlights, titles, summaries, and any free-form text often contain colons:
|
||||
|
||||
```yaml
|
||||
# WRONG — colon breaks YAML parsing:
|
||||
- title: Catalytic Mechanisms: A New Approach
|
||||
highlights:
|
||||
- Relevant coursework: Distributed Systems, ML
|
||||
|
||||
# RIGHT — wrap in double quotes:
|
||||
- title: "Catalytic Mechanisms: A New Approach"
|
||||
highlights:
|
||||
- "Relevant coursework: Distributed Systems, ML"
|
||||
```
|
||||
|
||||
Rule: if a string value contains `:`, it MUST be quoted. When in doubt, quote it.
|
||||
|
||||
### Bullet characters
|
||||
|
||||
The `design.highlights.bullet` field only accepts these exact characters: `●`, `•`, `◦`, `-`, `◆`, `★`, `■`, `—`, `○`. Do not use en-dash (`–`), `>`, `*`, or any other character. When in doubt, omit `bullet` to use the theme default.
|
||||
|
||||
### Phone numbers
|
||||
|
||||
Phone numbers MUST be in international format with country code (E.164). Never invent a phone number — only include one if the user provides it.
|
||||
|
||||
```yaml
|
||||
# WRONG:
|
||||
phone: "(555) 123-4567"
|
||||
phone: "555-123-4567"
|
||||
|
||||
# RIGHT:
|
||||
phone: "+15551234567"
|
||||
```
|
||||
|
||||
If the user provides a local number without country code, ask which country, or omit the phone field.
|
||||
|
||||
### Text formatting
|
||||
|
||||
All text fields support inline Markdown: `**bold**`, `*italic*`, `[link text](url)`. Block-level Markdown (headers, lists, blockquotes, code blocks) is not supported. Raw Typst commands and math (`$$f(x)$$`) also pass through.
|
||||
|
||||
### Date handling
|
||||
|
||||
- `date` and `start_date`/`end_date` are mutually exclusive. If `date` is provided, `start_date` and `end_date` are ignored.
|
||||
- If only `start_date` is given, `end_date` defaults to `"present"`.
|
||||
- `start_date`/`end_date` require strict formats: YYYY-MM-DD, YYYY-MM, or YYYY.
|
||||
- `date` is flexible: accepts any string ("Fall 2023") in addition to date formats.
|
||||
|
||||
### Section titles
|
||||
|
||||
- `snake_case` keys auto-capitalize: `work_experience` → "Work Experience"
|
||||
- Keys with spaces or uppercase are used as-is.
|
||||
|
||||
### Publication authors
|
||||
|
||||
Use `*Name*` (single asterisks, italic) to highlight the CV owner in author lists.
|
||||
|
||||
### Nested highlights (sub-bullets)
|
||||
|
||||
```yaml
|
||||
highlights:
|
||||
- Main bullet point
|
||||
- Sub-bullet 1
|
||||
- Sub-bullet 2
|
||||
```
|
||||
|
||||
## CLI Reference
|
||||
|
||||
### `rendercv new "Full Name"`
|
||||
|
||||
Generate a starter YAML file.
|
||||
|
||||
| Option | Short | What it does |
|
||||
|---|---|---|
|
||||
| `--theme THEME` | | Theme to use (default: `classic`) |
|
||||
| `--locale LOCALE` | | Locale to use (default: `english`) |
|
||||
| `--create-typst-templates` | | Also create editable Typst template files for full design control |
|
||||
|
||||
### `rendercv render <input.yaml>`
|
||||
|
||||
Generate PDF, Typst, Markdown, HTML, and PNG from a YAML file.
|
||||
|
||||
| Option | Short | What it does |
|
||||
|---|---|---|
|
||||
| `--watch` | `-w` | Re-render automatically when the YAML file changes |
|
||||
| `--quiet` | `-q` | Suppress all output messages |
|
||||
| `--design FILE` | `-d` | Load design section from a separate YAML file |
|
||||
| `--locale-catalog FILE` | `-lc` | Load locale section from a separate YAML file |
|
||||
| `--settings FILE` | `-s` | Load settings section from a separate YAML file |
|
||||
| `--output-folder DIR` | `-o` | Custom output directory |
|
||||
|
||||
Per-format controls: `--{format}-path PATH` sets custom output path, `--dont-generate-{format}` skips generation. Formats: `pdf`, `typst`, `markdown`, `html`, `png`.
|
||||
|
||||
**Override any YAML field from the CLI** using dot notation (overrides without editing the file):
|
||||
|
||||
```bash
|
||||
rendercv render CV.yaml --cv.name "Jane Doe" --design.theme "moderncv"
|
||||
rendercv render CV.yaml --cv.sections.education.0.institution "MIT"
|
||||
```
|
||||
|
||||
### `rendercv create-theme "theme-name"`
|
||||
|
||||
Scaffold a custom theme directory with editable Typst templates for complete design control.
|
||||
|
||||
## JSON Schema
|
||||
|
||||
For YAML editor autocompletion and validation:
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/rendercv/rendercv/refs/tags/v2.8/schema.json
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
### Sample CV
|
||||
|
||||
```yaml
|
||||
cv:
|
||||
name: John Doe
|
||||
headline:
|
||||
location: San Francisco, CA
|
||||
email: john.doe@email.com
|
||||
photo:
|
||||
phone:
|
||||
website: https://rendercv.com/
|
||||
social_networks:
|
||||
- network: LinkedIn
|
||||
username: rendercv
|
||||
- network: GitHub
|
||||
username: rendercv
|
||||
custom_connections:
|
||||
sections:
|
||||
Welcome to RenderCV:
|
||||
- RenderCV reads a CV written in a YAML file, and generates a PDF with
|
||||
professional typography.
|
||||
- Each section title is arbitrary.
|
||||
education:
|
||||
- institution: Princeton University
|
||||
area: Computer Science
|
||||
degree: PhD
|
||||
date:
|
||||
start_date: 2018-09
|
||||
end_date: 2023-05
|
||||
location: Princeton, NJ
|
||||
summary:
|
||||
highlights:
|
||||
- 'Thesis: Efficient Neural Architecture Search for Resource-Constrained Deployment'
|
||||
- 'Advisor: Prof. Sanjeev Arora'
|
||||
- NSF Graduate Research Fellowship, Siebel Scholar (Class of 2022)
|
||||
- institution: Boğaziçi University
|
||||
area: Computer Engineering
|
||||
degree: BS
|
||||
date:
|
||||
start_date: 2014-09
|
||||
end_date: 2018-06
|
||||
location: Istanbul, Türkiye
|
||||
summary:
|
||||
highlights:
|
||||
- 'GPA: 3.97/4.00, Valedictorian'
|
||||
- Fulbright Scholarship recipient for Graduate Studies
|
||||
experience:
|
||||
- company: Nexus AI
|
||||
position: Co-Founder & CTO
|
||||
date:
|
||||
start_date: 2023-06
|
||||
end_date: present
|
||||
location: San Francisco, CA
|
||||
summary:
|
||||
highlights:
|
||||
- Built foundation model infrastructure serving 2M+ monthly API requests
|
||||
with 99.97% uptime
|
||||
- Raised $18M Series A led by Sequoia Capital, with participation from
|
||||
a16z and Founders Fund
|
||||
- Scaled engineering team from 3 to 28 across ML research, platform, and
|
||||
applied AI divisions
|
||||
- Developed proprietary inference optimization reducing latency by 73%
|
||||
compared to baseline
|
||||
- company: NVIDIA Research
|
||||
position: Research Intern
|
||||
date:
|
||||
start_date: 2022-05
|
||||
end_date: 2022-08
|
||||
location: Santa Clara, CA
|
||||
summary:
|
||||
highlights:
|
||||
- Designed sparse attention mechanism reducing transformer memory
|
||||
footprint by 4.2x
|
||||
- Co-authored paper accepted at NeurIPS 2022 (spotlight presentation, top
|
||||
5% of submissions)
|
||||
projects:
|
||||
- name: '[FlashInfer](https://github.com/)'
|
||||
date:
|
||||
start_date: 2023-01
|
||||
end_date: present
|
||||
location:
|
||||
summary: Open-source library for high-performance LLM inference kernels
|
||||
highlights:
|
||||
- Achieved 2.8x speedup over baseline attention implementations on A100
|
||||
GPUs
|
||||
- Adopted by 3 major AI labs, 8,500+ GitHub stars, 200+ contributors
|
||||
- name: '[NeuralPrune](https://github.com/)'
|
||||
date: '2021'
|
||||
start_date:
|
||||
end_date:
|
||||
location:
|
||||
summary: Automated neural network pruning toolkit with differentiable
|
||||
masks
|
||||
highlights:
|
||||
- Reduced model size by 90% with less than 1% accuracy degradation on
|
||||
ImageNet
|
||||
- Featured in PyTorch ecosystem tools, 4,200+ GitHub stars
|
||||
publications:
|
||||
- title: 'Sparse Mixture-of-Experts at Scale: Efficient Routing for Trillion-Parameter
|
||||
Models'
|
||||
authors:
|
||||
- '*John Doe*'
|
||||
- Sarah Williams
|
||||
- David Park
|
||||
summary:
|
||||
doi: 10.1234/neurips.2023.1234
|
||||
url:
|
||||
journal: NeurIPS 2023
|
||||
date: 2023-07
|
||||
- title: Neural Architecture Search via Differentiable Pruning
|
||||
authors:
|
||||
- James Liu
|
||||
- '*John Doe*'
|
||||
summary:
|
||||
doi: 10.1234/neurips.2022.5678
|
||||
url:
|
||||
journal: NeurIPS 2022, Spotlight
|
||||
date: 2022-12
|
||||
selected_honors:
|
||||
- bullet: MIT Technology Review 35 Under 35 Innovators (2024)
|
||||
- bullet: Forbes 30 Under 30 in Enterprise Technology (2024)
|
||||
skills:
|
||||
- label: Languages
|
||||
details: Python, C++, CUDA, Rust, Julia
|
||||
- label: ML Frameworks
|
||||
details: PyTorch, JAX, TensorFlow, Triton, ONNX
|
||||
patents:
|
||||
- number: Adaptive Quantization for Neural Network Inference on Edge Devices
|
||||
(US Patent 11,234,567)
|
||||
- number: Dynamic Sparsity Patterns for Efficient Transformer Attention (US
|
||||
Patent 11,345,678)
|
||||
invited_talks:
|
||||
- reversed_number: Scaling Laws for Efficient Inference — Stanford HAI
|
||||
Symposium (2024)
|
||||
- reversed_number: Building AI Infrastructure for the Next Decade —
|
||||
TechCrunch Disrupt (2024)
|
||||
|
||||
```
|
||||
|
||||
### Sample Design (classic — complete reference)
|
||||
|
||||
This shows every available design field with its default value. All themes share the same structure.
|
||||
|
||||
```yaml
|
||||
design:
|
||||
theme: classic
|
||||
page:
|
||||
size: us-letter
|
||||
top_margin: 0.7in
|
||||
bottom_margin: 0.7in
|
||||
left_margin: 0.7in
|
||||
right_margin: 0.7in
|
||||
show_footer: true
|
||||
show_top_note: true
|
||||
colors:
|
||||
body: rgb(0, 0, 0)
|
||||
name: rgb(0, 79, 144)
|
||||
headline: rgb(0, 79, 144)
|
||||
connections: rgb(0, 79, 144)
|
||||
section_titles: rgb(0, 79, 144)
|
||||
links: rgb(0, 79, 144)
|
||||
footer: rgb(128, 128, 128)
|
||||
top_note: rgb(128, 128, 128)
|
||||
typography:
|
||||
line_spacing: 0.6em
|
||||
alignment: justified
|
||||
date_and_location_column_alignment: right
|
||||
font_family:
|
||||
body: Source Sans 3
|
||||
name: Source Sans 3
|
||||
headline: Source Sans 3
|
||||
connections: Source Sans 3
|
||||
section_titles: Source Sans 3
|
||||
font_size:
|
||||
body: 10pt
|
||||
name: 30pt
|
||||
headline: 10pt
|
||||
connections: 10pt
|
||||
section_titles: 1.4em
|
||||
small_caps:
|
||||
name: false
|
||||
headline: false
|
||||
connections: false
|
||||
section_titles: false
|
||||
bold:
|
||||
name: true
|
||||
headline: false
|
||||
connections: false
|
||||
section_titles: true
|
||||
links:
|
||||
underline: false
|
||||
show_external_link_icon: false
|
||||
header:
|
||||
alignment: center
|
||||
photo_width: 3.5cm
|
||||
photo_position: left
|
||||
photo_space_left: 0.4cm
|
||||
photo_space_right: 0.4cm
|
||||
space_below_name: 0.7cm
|
||||
space_below_headline: 0.7cm
|
||||
space_below_connections: 0.7cm
|
||||
connections:
|
||||
phone_number_format: national
|
||||
hyperlink: true
|
||||
show_icons: true
|
||||
display_urls_instead_of_usernames: false
|
||||
separator: ''
|
||||
space_between_connections: 0.5cm
|
||||
section_titles:
|
||||
type: with_partial_line
|
||||
line_thickness: 0.5pt
|
||||
space_above: 0.5cm
|
||||
space_below: 0.3cm
|
||||
sections:
|
||||
allow_page_break: true
|
||||
space_between_regular_entries: 1.2em
|
||||
space_between_text_based_entries: 0.3em
|
||||
show_time_spans_in:
|
||||
- experience
|
||||
entries:
|
||||
date_and_location_width: 4.15cm
|
||||
side_space: 0.2cm
|
||||
space_between_columns: 0.1cm
|
||||
allow_page_break: false
|
||||
short_second_row: true
|
||||
degree_width: 1cm
|
||||
summary:
|
||||
space_above: 0cm
|
||||
space_left: 0cm
|
||||
highlights:
|
||||
bullet: •
|
||||
nested_bullet: •
|
||||
space_left: 0.15cm
|
||||
space_above: 0cm
|
||||
space_between_items: 0cm
|
||||
space_between_bullet_and_text: 0.5em
|
||||
templates:
|
||||
footer: '*NAME -- PAGE_NUMBER/TOTAL_PAGES*'
|
||||
top_note: '*LAST_UPDATED CURRENT_DATE*'
|
||||
single_date: MONTH_ABBREVIATION YEAR
|
||||
date_range: START_DATE – END_DATE
|
||||
time_span: HOW_MANY_YEARS YEARS HOW_MANY_MONTHS MONTHS
|
||||
one_line_entry:
|
||||
main_column: '**LABEL:** DETAILS'
|
||||
education_entry:
|
||||
main_column: |-
|
||||
**INSTITUTION**, AREA
|
||||
SUMMARY
|
||||
HIGHLIGHTS
|
||||
degree_column: '**DEGREE**'
|
||||
date_and_location_column: |-
|
||||
LOCATION
|
||||
DATE
|
||||
normal_entry:
|
||||
main_column: |-
|
||||
**NAME**
|
||||
SUMMARY
|
||||
HIGHLIGHTS
|
||||
date_and_location_column: |-
|
||||
LOCATION
|
||||
DATE
|
||||
experience_entry:
|
||||
main_column: |-
|
||||
**COMPANY**, POSITION
|
||||
SUMMARY
|
||||
HIGHLIGHTS
|
||||
date_and_location_column: |-
|
||||
LOCATION
|
||||
DATE
|
||||
publication_entry:
|
||||
main_column: |-
|
||||
**TITLE**
|
||||
SUMMARY
|
||||
AUTHORS
|
||||
URL (JOURNAL)
|
||||
date_and_location_column: DATE
|
||||
|
||||
```
|
||||
|
||||
### Other Theme Overrides
|
||||
|
||||
Other themes only override specific fields from the classic defaults above. To use a theme, set `design.theme` and optionally override any field. Each theme also customizes `design.templates` (entry layout patterns) — see the classic sample above for the full template structure. The override YAMLs below omit templates for brevity.
|
||||
|
||||
#### engineeringclassic
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=../../../../../../schema.json
|
||||
design:
|
||||
theme: engineeringclassic
|
||||
typography:
|
||||
font_family:
|
||||
body: Raleway
|
||||
name: Raleway
|
||||
headline: Raleway
|
||||
connections: Raleway
|
||||
section_titles: Raleway
|
||||
bold:
|
||||
name: false
|
||||
section_titles: false
|
||||
header:
|
||||
alignment: left
|
||||
links:
|
||||
show_external_link_icon: false
|
||||
section_titles:
|
||||
type: with_full_line
|
||||
sections:
|
||||
show_time_spans_in: []
|
||||
entries:
|
||||
short_second_row: false
|
||||
summary:
|
||||
space_above: 0.12cm
|
||||
highlights:
|
||||
space_left: 0cm
|
||||
space_above: 0.12cm
|
||||
space_between_items: 0.12cm
|
||||
```
|
||||
|
||||
#### engineeringresumes
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=../../../../../../schema.json
|
||||
design:
|
||||
theme: engineeringresumes
|
||||
page:
|
||||
show_footer: false
|
||||
typography:
|
||||
font_family:
|
||||
body: XCharter
|
||||
name: XCharter
|
||||
headline: XCharter
|
||||
connections: XCharter
|
||||
section_titles: XCharter
|
||||
font_size:
|
||||
name: 25pt
|
||||
section_titles: 1.2em
|
||||
bold:
|
||||
name: false
|
||||
header:
|
||||
connections:
|
||||
separator: '|'
|
||||
show_icons: false
|
||||
display_urls_instead_of_usernames: true
|
||||
colors:
|
||||
name: rgb(0,0,0)
|
||||
connections: rgb(0,0,0)
|
||||
headline: rgb(0,0,0)
|
||||
section_titles: rgb(0,0,0)
|
||||
links: rgb(0,0,0)
|
||||
links:
|
||||
underline: true
|
||||
show_external_link_icon: false
|
||||
section_titles:
|
||||
type: with_full_line
|
||||
space_above: 0.5cm
|
||||
space_below: 0.3cm
|
||||
sections:
|
||||
space_between_regular_entries: 0.42cm
|
||||
space_between_text_based_entries: 0.15cm
|
||||
show_time_spans_in: []
|
||||
entries:
|
||||
short_second_row: false
|
||||
summary:
|
||||
space_above: 0.08cm
|
||||
side_space: 0cm
|
||||
highlights:
|
||||
bullet: ●
|
||||
nested_bullet: ●
|
||||
space_left: 0cm
|
||||
space_above: 0.08cm
|
||||
space_between_items: 0.08cm
|
||||
space_between_bullet_and_text: 0.3em
|
||||
```
|
||||
|
||||
#### harvard
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=../../../../../../schema.json
|
||||
design:
|
||||
theme: harvard
|
||||
page:
|
||||
top_margin: 0.5in
|
||||
bottom_margin: 0.5in
|
||||
left_margin: 0.5in
|
||||
right_margin: 0.5in
|
||||
show_top_note: false
|
||||
colors:
|
||||
name: rgb(0,0,0)
|
||||
headline: rgb(0,0,0)
|
||||
connections: rgb(0,0,0)
|
||||
section_titles: rgb(0,0,0)
|
||||
links: rgb(0,0,0)
|
||||
typography:
|
||||
font_family:
|
||||
body: XCharter
|
||||
name: XCharter
|
||||
headline: XCharter
|
||||
connections: XCharter
|
||||
section_titles: XCharter
|
||||
font_size:
|
||||
name: 25pt
|
||||
connections: 9pt
|
||||
section_titles: 1.3em
|
||||
header:
|
||||
space_below_name: 0.5cm
|
||||
space_below_headline: 0.5cm
|
||||
space_below_connections: 0.5cm
|
||||
connections:
|
||||
show_icons: false
|
||||
separator: •
|
||||
space_between_connections: 0.4cm
|
||||
section_titles:
|
||||
type: centered_with_centered_partial_line
|
||||
space_below: 0.2cm
|
||||
sections:
|
||||
space_between_regular_entries: 1em
|
||||
show_time_spans_in: []
|
||||
entries:
|
||||
short_second_row: false
|
||||
```
|
||||
|
||||
#### moderncv
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=../../../../../../schema.json
|
||||
design:
|
||||
theme: moderncv
|
||||
typography:
|
||||
line_spacing: 0.6em
|
||||
font_family:
|
||||
body: Fontin
|
||||
name: Fontin
|
||||
headline: Fontin
|
||||
connections: Fontin
|
||||
section_titles: Fontin
|
||||
font_size:
|
||||
name: 25pt
|
||||
section_titles: 1.4em
|
||||
bold:
|
||||
name: false
|
||||
section_titles: false
|
||||
header:
|
||||
alignment: left
|
||||
photo_width: 4.15cm
|
||||
photo_space_left: 0cm
|
||||
photo_space_right: 0.3cm
|
||||
links:
|
||||
underline: true
|
||||
show_external_link_icon: false
|
||||
section_titles:
|
||||
type: moderncv
|
||||
space_above: 0.55cm
|
||||
space_below: 0.3cm
|
||||
line_thickness: 0.15cm
|
||||
sections:
|
||||
show_time_spans_in: []
|
||||
entries:
|
||||
short_second_row: false
|
||||
side_space: 0cm
|
||||
space_between_columns: 0.3cm
|
||||
summary:
|
||||
space_above: 0.1cm
|
||||
highlights:
|
||||
space_left: 0cm
|
||||
space_above: 0.15cm
|
||||
space_between_items: 0.1cm
|
||||
space_between_bullet_and_text: 0.3em
|
||||
```
|
||||
|
||||
#### sb2nov
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=../../../../../../schema.json
|
||||
design:
|
||||
theme: sb2nov
|
||||
typography:
|
||||
font_family:
|
||||
body: New Computer Modern
|
||||
name: New Computer Modern
|
||||
headline: New Computer Modern
|
||||
connections: New Computer Modern
|
||||
section_titles: New Computer Modern
|
||||
colors:
|
||||
name: rgb(0,0,0)
|
||||
connections: rgb(0,0,0)
|
||||
section_titles: rgb(0,0,0)
|
||||
headline: rgb(0,0,0)
|
||||
links: rgb(0,0,0)
|
||||
links:
|
||||
underline: true
|
||||
show_external_link_icon: false
|
||||
section_titles:
|
||||
type: with_full_line
|
||||
sections:
|
||||
show_time_spans_in: []
|
||||
header:
|
||||
connections:
|
||||
hyperlink: true
|
||||
show_icons: false
|
||||
display_urls_instead_of_usernames: true
|
||||
separator: •
|
||||
entries:
|
||||
short_second_row: false
|
||||
highlights:
|
||||
bullet: ◦
|
||||
nested_bullet: ◦
|
||||
```
|
||||
|
||||
@@ -9,6 +9,7 @@ Why:
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -28,6 +29,9 @@ def run_just(recipe: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.platform == "win32", reason="Schema generation differs on Windows"
|
||||
)
|
||||
def test_schema_json_is_up_to_date() -> None:
|
||||
schema_path = repository_root / "schema.json"
|
||||
before = schema_path.read_text(encoding="utf-8")
|
||||
@@ -64,7 +68,15 @@ def test_example_yaml_is_up_to_date(theme: str) -> None:
|
||||
|
||||
|
||||
def test_skill_md_is_up_to_date() -> None:
|
||||
skill_path = repository_root / "skills" / "rendercv" / "SKILL.md"
|
||||
skill_path = (
|
||||
repository_root
|
||||
/ ".claude"
|
||||
/ "skills"
|
||||
/ "rendercv-skill"
|
||||
/ "skills"
|
||||
/ "rendercv"
|
||||
/ "SKILL.md"
|
||||
)
|
||||
before = skill_path.read_text(encoding="utf-8")
|
||||
|
||||
llms_txt_path = repository_root / "docs" / "llms.txt"
|
||||
|
||||
Reference in new issue
Block a user