| name | format-cover-letter |
| description | Intelligent cover letter formatting with semantic understanding of structure and content (project, gitignored) |
Format Cover Letter Skill
Purpose
Format cover letters with intelligent style application and automatic play title italicization. Uses the shared career documents template with 19 semantic styles and learns from your corrections over time.
Usage
Format with just body text (metadata auto-inferred):
Format this cover letter: [paste body paragraphs only]
I'll infer contact block, date, recipient, and RE line!
Format with complete JSON:
Format this cover letter: [paste full JSON with metadata]
Format from file:
Format my-cover-letter.txt
How It Works
Semantic Analysis: I analyze content to understand elements (contact info, recipient address, RE line, body text, etc.)
Style Mapping: Based on context, I assign appropriate styles:
- "ANTHONY BYRNES" at top → Contact Name
- "RE: Position Title" → RE Line (bold orange)
- Body text mentioning "Louis & Keely: Live at the Sahara" → Auto-italicized via dictionary
Hybrid Inline Styling: Play/production titles automatically italicized:
- Dictionary lookup (95% of cases - no manual markup needed)
- Manual override for new/unknown plays
- Exclusion for edge cases
Generate Document: Create formatted .docx using template
Visual Preview: Show PDF preview for review
Learn from Corrections: If you correct a style choice, I remember for next time
The 19 Styles
Paragraph Styles:
- CV Name - Your name at top
- Contact Name - Bold, 10pt (cover letter header)
- Contact Info - Regular, 10pt (phone/email)
- Recipient Address - Regular, 11pt (organization address)
- RE Line - Bold, 13pt, ORANGE (position/subject line)
- Date Line - Right-aligned, 11pt (document date)
- Section Header - Bold, ORANGE (11pt for CV, 13pt for cover letter)
- Body Text - Standard paragraphs, 11pt
- Timeline Entry - Date + institution with hanging indent (CV only)
- Page Header - Bold, 10pt (page 2+ headers)
- Bullet Standard, Bullet Gray, Bullet Emphasis - List variations
Character Styles (inline):
- Play Title - Italic for productions (auto-styled via dictionary!)
- Institution - Bold for school/company names
- Job Title - Bold italic for positions
- Orange Emphasis - Highlight text
- Gray Text - Dates, secondary info
Cover Letter Elements
Contact Block (top of page):
ANTHONY BYRNES
T: 213.305.3132
E: anthonybyrnes@mac.com
Recipient Address:
Colburn School
200 South Grand Avenue
Los Angeles, CA 90012
RE Line (bold orange, same as section headers):
RE: General Manager of Performances & Events Division
Body Text with auto-italicized play titles:
For Louis & Keely: Live at the Sahara, I generated $1.4 million...
→ "Louis & Keely: Live at the Sahara" automatically italicized from dictionary
Signature:
Sincerely,
[signature image]
Anthony Byrnes
Metadata Inference (NEW!)
You provide: Raw text or minimal JSON (just body paragraphs)
I infer:
- Contact block (from defaults.yaml)
- Date line (today's date)
- Recipient address (from job description or content)
- RE line (from job title)
- Salutation (extracted from content)
Smart Job Description Matching:
- I search
career-applications/*/00-job-description.mdfiles - Match based on organization/position mentioned in your letter
- Confirm with you: "Found job description for UCLA - Associate Dean. Use this?"
- Extract recipient and RE line from confirmed job description
Confirmation Before Formatting:
Inferred Metadata:
Contact: ✓ ANTHONY BYRNES
✓ T: 213.305.3132
✓ E: anthonybyrnes@mac.com
Date: November 11, 2025
Recipient: UCLA School of Theater, Film and Television
RE: Associate Dean and Chief Administrative Officer [CAO]
Salutation: Dear Search Committee,
Looks correct? (yes/modify/[field name])
Configuration:
- Defaults:
~/.claude/skills/format-cover-letter/defaults.yaml - Job descriptions: Auto-searched in career-applications/
Workflow
Step 1: Analyze Content
I'll parse your content and create JSON structure:
{
"document_metadata": {
"type": "cover-letter",
"author_name": "Anthony Byrnes",
"document_title": "Position Title Cover Letter"
},
"content": [
{"text": "ANTHONY BYRNES", "style": "Contact Name", "type": "paragraph"},
{"text": "RE: Position Title", "style": "RE Line", "type": "paragraph"},
{"text": "Body with play title...", "style": "Body Text", "type": "paragraph"}
]
}
Step 2: Generate Document
I'll call the formatter to create your .docx:
import json
import subprocess
from pathlib import Path
# Save content mapping
mapping_file = "/tmp/cover_letter_mapping.json"
with open(mapping_file, 'w') as f:
json.dump(data, f, indent=2)
# Format document
result = subprocess.run([
"python3",
"format_cv.py",
mapping_file,
output_path,
"--document-type", "cover-letter",
"--preview"
], capture_output=True, text=True, cwd=str(Path.home() / "PycharmProjects/career-lexicon-builder"))
Step 3: Visual Preview
I'll convert to PDF and show you what it looks like using the open command.
Step 4: Review
You can request changes: "That venue should be italic" or "Don't emphasize that number"
Step 5: Learn (if corrections made)
If you made corrections, I'll update learned-preferences.yaml and add new plays to play-titles-dictionary.yaml.
Hybrid Inline Styling
The Problem: Manually marking up every play title is tedious:
{
"text": "For Louis & Keely: Live at the Sahara, I...",
"runs": [
{"text": "For ", "style": null},
{"text": "Louis & Keely: Live at the Sahara", "style": "Play Title"},
{"text": ", I...", "style": null}
]
}
The Solution: Dictionary auto-styling (95% of cases):
{
"text": "For Louis & Keely: Live at the Sahara, I...",
"style": "Body Text"
}
→ "Louis & Keely: Live at the Sahara" automatically italicized from dictionary!
Dictionary Location:
~/.claude/skills/format-cover-letter/play-titles-dictionary.yaml
Example dictionary:
productions:
- "Louis & Keely: Live at the Sahara"
- "Romeo & Juliet"
- "Hamlet"
programs:
- "Experience LA!"
Manual Override (for new/unknown plays):
{
"text": "For My New Play, I generated...",
"style": "Body Text",
"inline_styles": [
{"text": "My New Play", "style": "Play Title"}
]
}
Exclusion (when dictionary shouldn't apply):
{
"text": "We decided NOT to produce Romeo & Juliet.",
"inline_styles": [
{"text": "Romeo & Juliet", "exclude": true}
]
}
Learning System
You: "Add 'The Tempest' to the dictionary"
Me: ✓ Added to play-titles-dictionary.yaml
Next cover letter: Automatically italicizes "The Tempest" without asking.
Signature Images
Setup signature:
- Create PNG of your signature (~1.5 inches wide, transparent background)
- Save to:
~/.claude/skills/format-cover-letter/signatures/signature.png - Reference in JSON:
{"text": "signature", "style": "Signature Image", "type": "image"}
If signature missing: System logs warning and continues without image.
Template Location
Shared template path:
cv_formatting/templates/career-documents-template.docx
Why shared? Same template as format-resume ensures visual consistency across CV and cover letter.
Key Differences from CV Formatting
| Aspect | CV (format-resume) | Cover Letter (this skill) |
|---|---|---|
| Organization | Timeline-based sections | Narrative paragraphs |
| Section Headers | Orange, 11pt | Orange, 13pt (bug fixed!) |
| Main Content | Timeline entries, bullets | Body text paragraphs |
| Special Elements | Hanging indent timelines | Contact block, RE line, signature |
| Inline Styling | Manual only | Hybrid (dictionary + manual) |
IMPORTANT: Section headers are ALWAYS ORANGE for both CVs and cover letters. Earlier versions incorrectly made cover letter headers black.
Error Handling
Template not found:
Run: python generate_cv_template.py
PDF preview not available:
Install: brew install libreoffice
Image generation skipped:
Install: brew install poppler
Files
career-documents-template.docx- Shared template with 19 semantic stylesformat_cv.py- Main formatting scriptplay-titles-dictionary.yaml- Known plays for auto-italicizationlearned-preferences.yaml- Your accumulated correctionssignatures/- Directory for signature images
JSON Export for Wrapper Application
After formatting the cover letter, export structured formatting metadata to enable wrapper application integration.
Phase 6: JSON Export
Write to: cover-letter-formatted-v1.json (increment version if exists)
Implementation:
- Check if
cover-letter-formatted-v1.jsonalready exists - If exists, increment version:
cover-letter-formatted-v2.json,cover-letter-formatted-v3.json, etc. - Extract formatting metadata from the document generation process
- Write JSON file with proper formatting (2-space indentation)
- Save to same directory as output files
Structure:
{
"metadata": {
"created_at": "YYYY-MM-DDTHH:MM:SSZ",
"version": 1,
"skill": "format-cover-letter",
"input_file": "cover-letter-draft.md",
"output_file": "cover-letter-formatted.pdf"
},
"formatting_metadata": {
"template": "career-documents-template.docx",
"template_path": "cv_formatting/templates/career-documents-template.docx",
"font": "Helvetica",
"font_size": 11,
"margins": {
"top": 0.75,
"bottom": 0.75,
"left": 1.0,
"right": 1.0
},
"line_spacing": 1.15,
"paragraph_spacing": 0.15,
"letterhead": true,
"signature": "included"
},
"content_structure": {
"header": {
"name": "ANTHONY BYRNES",
"contact": {
"phone": "213.305.3132",
"email": "anthonybyrnes@mac.com"
},
"validation": "passed"
},
"date": {
"text": "November 12, 2025",
"format": "MMMM DD, YYYY",
"validation": "passed"
},
"recipient": {
"organization": "UCLA School of Theater, Film and Television",
"address_lines": [
"UCLA School of Theater, Film and Television",
"102 East Melnitz Hall",
"Los Angeles, CA 90095"
],
"validation": "passed"
},
"re_line": {
"text": "RE: Associate Dean and Chief Administrative Officer [CAO]",
"style": "RE Line",
"validation": "passed"
},
"salutation": {
"text": "Dear Search Committee,",
"validation": "passed"
},
"body": [
{
"paragraph": 1,
"purpose": "opening",
"word_count": 85,
"has_play_titles": true,
"inline_styles_applied": 2,
"validation": "passed"
},
{
"paragraph": 2,
"purpose": "body",
"word_count": 120,
"has_play_titles": true,
"inline_styles_applied": 3,
"validation": "passed"
},
{
"paragraph": 3,
"purpose": "body",
"word_count": 95,
"has_play_titles": false,
"inline_styles_applied": 1,
"validation": "passed"
},
{
"paragraph": 4,
"purpose": "closing",
"word_count": 42,
"has_play_titles": false,
"inline_styles_applied": 0,
"validation": "passed"
}
],
"closing": {
"salutation": "Sincerely,",
"signature_space": true,
"signature_image": true,
"name": "Anthony Byrnes",
"validation": "passed"
}
},
"validation_results": {
"total_checks": 12,
"passed": 12,
"failed": 0,
"warnings": 0,
"checks": [
{"check": "header_present", "status": "passed"},
{"check": "date_format_correct", "status": "passed"},
{"check": "recipient_complete", "status": "passed"},
{"check": "re_line_formatted", "status": "passed"},
{"check": "salutation_professional", "status": "passed"},
{"check": "body_paragraphs_present", "status": "passed"},
{"check": "play_titles_italicized", "status": "passed"},
{"check": "inline_styles_applied", "status": "passed"},
{"check": "closing_complete", "status": "passed"},
{"check": "signature_included", "status": "passed"},
{"check": "page_count_valid", "status": "passed"},
{"check": "professional_tone", "status": "passed"}
],
"issues": []
},
"styling_metadata": {
"play_titles_dictionary_used": true,
"dictionary_matches": [
"Louis & Keely: Live at the Sahara",
"Romeo & Juliet"
],
"manual_inline_styles": [
{"text": "The New Play", "style": "Play Title", "reason": "not in dictionary"}
],
"exclusions": [],
"styles_applied": {
"Contact Name": 1,
"Contact Info": 2,
"Date Line": 1,
"Recipient Address": 3,
"RE Line": 1,
"Body Text": 4,
"Play Title": 5,
"Institution": 3,
"Job Title": 2
}
},
"metrics": {
"page_count": 1,
"word_count": 342,
"paragraph_count": 4,
"sentence_count": 18,
"estimated_read_time_seconds": 80,
"average_paragraph_length": 85,
"readability_score": 0.90,
"professionalism_score": 0.93,
"visual_appeal_score": 0.91
},
"output_files": [
{
"type": "docx",
"path": "cover-letter-formatted.docx",
"size_bytes": 87654,
"created_at": "YYYY-MM-DDTHH:MM:SSZ"
},
{
"type": "pdf",
"path": "cover-letter-formatted.pdf",
"size_bytes": 156432,
"created_at": "YYYY-MM-DDTHH:MM:SSZ"
}
],
"learned_preferences": {
"new_plays_added": ["The New Play"],
"style_corrections": [],
"user_feedback": ""
}
}
Present to user:
Formatting complete! Saved to:
- cover-letter-formatted.docx (formatted document)
- cover-letter-formatted.pdf (visual preview)
- cover-letter-formatted-v1.json (structured metadata)
Document metrics:
- Pages: 1
- Word count: 342
- Paragraphs: 4
- Validation: 12/12 checks passed
- Readability score: 0.90
All play titles automatically italicized from dictionary. Ready for review!
Related
- Format Resume Skill:
~/.claude/skills/format-resume/skill.md - Implementation Handoff:
docs/handoffs/2025-11-11-cover-letter-formatting-implementation-complete.md - Template generation:
python generate_cv_template.py
Ready to format your cover letter?
Just say: "Format this cover letter: [your content]"