CommunityWriting & Editinggithub.com

atokim4/cover-letter-lint

Write or check a freelance cover letter, proposal, bid or client message so it reads like a person wrote it and claims only what the writer can back. Use when the user asks for a cover letter, proposal or bid for a job post (Upwork, Fiverr, PeoplePerHour, Freelancer.com, job boards), asks to check or improve one, or asks "does this sound like AI". Runs a Node linter that flags AI phrases, em dashes, "not X but Y", placeholders, off-platform contact, the 50-90 word range, the closing question, and claims missing from the user's facts file.

What is cover-letter-lint?

cover-letter-lint is a Claude Code agent skill that write or check a freelance cover letter, proposal, bid or client message so it reads like a person wrote it and claims only what the writer can back. Use when the user asks for a cover letter, proposal or bid for a job post (Upwork, Fiverr, PeoplePerHour, Freelancer.com, job boards), asks to check or improve one, or asks "does this sound like AI". Runs a Node linter that flags AI phrases, em dashes, "not X but Y", placeholders, off-platform contact, the 50-90 word range, the closing question, and claims missing from the user's facts file.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/atokim4/cover-letter-lint/tree/HEAD/skills/cover-letter-lint

Installed? Explore more Writing & Editing skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Cover letter lint

Goal: a cover letter of 50-90 words that a busy client reads to the end and answers, with every claim backed by the user's own facts file.

The linter is scripts/lint-cover.mjs in this skill's folder (Node 20+, no dependencies). Call it by its full path: node ${CLAUDE_SKILL_DIR}/scripts/lint-cover.mjs in Claude Code (also for a plugin install). Other agents don't fill in that variable, so use the folder, e.g. node .claude/skills/cover-letter-lint/scripts/lint-cover.mjs for a project install or node ~/.claude/skills/cover-letter-lint/scripts/lint-cover.mjs for a global one.

1. Find the facts file

Look for facts.md, me/facts.md or a .cover-lint.json with a "facts" path in the current project.

  • Found: read it. Claims come only from there.
  • Not found: offer to create facts.md from templates/facts.md in this skill's folder, and fill it in with the user (skills, roles, track record, proof links, things never to say). Don't invent any of it. If the user declines, lint without --facts and tell them claims were not checked.

2. Write (skip if the user brought their own draft)

Save the job post to a file if the user pasted one (e.g. job.md). Then write, as plain sentences with no bullets or headings:

  1. A greeting in the client's style, then one concrete detail of THEIR post (the bug, the stack, the deadline) and what likely causes it or how you'd approach it. Never restate the post.
  2. One proof line from the facts file that really matches (a similar job, a role, a demo link). A personal project is called a personal project. No honest match: leave proof out.
  3. The plan in one or two sentences, with hours and a price if the user gave you rates.
  4. One question, the one whose answer changes the scope or the price.
  5. The user's first name on its own line.

Mirror the post: three casual lines get three casual lines back, another language gets that language.

3. Lint and fix

node ${CLAUDE_SKILL_DIR}/scripts/lint-cover.mjs --facts facts.md --job job.md proposal.md

(- reads stdin; --mode message for client replies, which have no word range; --json for machine output.) Fix every error and rerun until it exits 0. Warnings: fix them unless there is a reason, and say the reason. For unlisted-skill, either remove the claim or ask the user whether to add the skill to the facts file. Never edit the facts file to make a claim pass without the user saying it's true.

RuleLevelWhat it catches
ai-phraseerror~75 template phrases: "I hope this finds you well", "seamless", "I'm excited to", "the key is"…
not-x-but-yerror"This is not just X, but Y" style contrasts
dasherrorem dashes and dashes used as them
placeholdererror[Your Name], {{name}}, <client>, TODO, lorem ipsum
off-platformerroremails, phone numbers, "message me on WhatsApp", payment outside the marketplace
unbacked-claimerrorlinks and "N years" that are not in the facts file
never-sayerrorphrases listed under "Never say" in the facts file
word-counterror / warnover 90 words (error), under 50 (warning) in cover mode
unlisted-skillwarna framework or service the letter names that's in neither the facts file nor the job post
questionwarnno question, or more than one
list, greeting, exclamation, emoji, long-sentence, i-startwarnlists, "Dear Sir", "!!", emoji, 35+ word sentences, too many sentences starting with "I"

Per-project tweaks go in .cover-lint.json (all keys optional): {"facts": "facts.md", "maxWords": 90, "minWords": 50, "extraPhrases": [], "allowPhrases": [], "disable": []}.

4. Hand it over

Show the letter and the linter's last line. Name the price and the question you chose so the user can change them. Then read it once more against the facts file yourself: the linter checks links, years and tech names, not every sentence. The user sends it; never submit anything for them.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Related Skills