Naming is hard
A plugin of cross-agent skills for AI coding agents — GitHub Copilot, Claude, and Codex — installable with the skills CLI (npx skills add). Includes create-canvas-kit for building Copilot canvas extensions.
npx -y skills add jongio/skills --skill naming-is-hardAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Interactive naming assistant: help the user land on a name for a project, product, repo, company, package, or feature they love AND can actually use. Use when the user says "name my project", "help me name this", "come up with a name", "brainstorm names", "what should I call it", "rename this project", "is this name taken", "check if a name is available", "check the domain / GitHub org / npm / handle", "is there a trademark", or "naming is hard". The skill profiles what the thing is, generates on-brief candidate names, then runs a swipe loop (Like / Pass / Super-like) where a preference model learns the user's taste and shows more of what they like, then scores the finalists against real availability (domains, GitHub, package registries, social handles) plus trademark, existing-business, and confusable-name screens. Every finalist gets one of three verdicts: Deal Breaker, It's Complicated, or Perfect Match. Do NOT use for renaming code identifiers or SEO keyword research.
SKILL.md
23.3 KB, as published. Nobody here has run it
naming-is-hard
An interactive naming assistant for projects, CLIs, products, packages, and organizations. It guides you from a blank slate to a validated name you can actually ship.
The core insight: you rarely know what name you want until you see candidates and react to them. This skill generates diverse on-brief names, learns your preferences as you rank them, then validates your finalists against real-world availability (domains, GitHub, npm/PyPI, social handles) and flags trademark or confusable-name collisions before you commit.
When to use it
Reach for this whenever a name needs to be chosen and checked: a new project, product, startup, open-source library, npm/PyPI package, GitHub org or repo, app, or feature. It is equally good at "brainstorm names for X" and "is "Foo" already taken everywhere?".
Do not use it to rename variables or functions in code (that is ordinary editing), or for SEO keyword research.
On invocation
When the user invokes this skill (e.g. /naming-is-hard), start the naming
flow immediately. Do not ask "What can I help you with?" or wait passively.
- If the user provided context alongside the invocation (e.g. "name my CLI tool that does X"), proceed directly to Act 1 (the Profile) using that context.
- If the user invoked the skill bare with no context, look at the current working directory for a README, package.json, or other project files. If you find a project, ask one focused question: "I see you're working on [project]. Would you like to name this, or something else?" Then proceed to Act 1.
- If there's no project context at all, ask a single question to get started: "What are we naming? Tell me what it is and I'll kick off the process."
The skill exists to DO naming, not to explain itself. Get moving.
How it works: five acts
- The Profile. Read the user's context (a description, a repo path, or a URL) and write a Naming Brief: what the thing is and will be, who it is for, the tone, and any constraints. This anchors all candidate generation.
- The Deck. Generate a diverse, on-brief pool of candidate names across many naming strategies. Each candidate is tagged so the model can learn from it.
- The Swipe. Show one card at a time. The user swipes Like / Pass / Super-like. A preference model learns their taste and biases the next cards toward it, with enough exploration that it does not tunnel. It can state "your type" in words.
- The Match. For the names the user liked, run a deep availability scan and a trademark/existing-business screen, roll each up into a verdict tier, and present the ranked matches with a compatibility scorecard and a winner.
- Next actions. Hand the user the links to grab the GitHub org, register the domain, and claim the handles.
Division of labor
You (the agent) do the creative work. The engine does the deterministic, security-sensitive, and stateful work. Stay on your side of this line.
| You (this SKILL.md) | The engine (scripts/naming.mjs) |
|---|---|
| Write the Naming Brief from the context | Extract feature vectors from names |
| Generate creative candidate names + a one-line pitch each | Learn taste from swipes (score / rank / pick next) |
Present swipe cards via ask_user | Explain the learned "type" in plain language |
| Decide when to refill the deck and generate more | Check availability (SSRF-safe, mocked in tests) |
Run a live web_search per finalist for trademarks/businesses | Screen against the famous-marks list, compute verdict tiers |
| Narrate the matches and next actions | Persist all state across swipe turns |
Never hand-roll availability checks with raw curl, and never invent a swipe
score yourself. Call the engine. It is tested and safe.
The engine CLI
The engine lives in scripts/naming.mjs. It needs no install and no network for
anything except check. Every command takes --dir <state-dir>, a working folder
where the run's state is persisted (use the session workspace, for example
~/.copilot/session-state/<id>/files/naming-<slug>). Node 18+.
| Command | What it does |
|---|---|
init --dir D [--brief-json '{...}'] | Create (or open) state; optionally set the brief |
brief --dir D --json '{...}' | Set/replace the Naming Brief |
add --dir D --json '[{"name":..,"strategy":..,"tags":[..]}]' | Add candidates (features extracted, ids assigned). Also reads the JSON array from stdin. |
next --dir D [--count N] | Return the next card(s) to show, best first |
swipe --dir D --id ID --label like|pass|superlike|skip | Record a swipe and update the model |
rank --dir D [--limit N] | Candidates ranked by learned fit |
profile --dir D | The learned "type" in plain language |
suggest --dir D --name NAME [--count N] | Morphological variants of a liked name |
check --dir D [--names a,b] [--github-token T] | Availability scorecards (defaults to liked names) |
screen --dir D --names a,b,c [--preset P] | Availability-FIRST: bucket names into clear / contested / blocked by key channels |
variants --name NAME | Decorated handle/org variants (get<X>/<X>hq/<X>dev), screened for GitHub/npm/.dev |
similar --name NAME --against a,b,c | Confusability report against a corpus you supply (from your live search) |
duel --dir D --winner ID --loser ID | Decision mode: record a head-to-head result so the winner's traits outrank the loser's |
report --dir D [--names a,b] [--preset P] [--as-json] | Ranked matches with verdict tiers (human text by default) |
state --dir D | Dump the full state as JSON |
reset --dir D | Clear the state |
All commands print JSON (so you can parse the result) except report, which prints
a human-readable card by default and JSON with --as-json.
Act 1: the Profile (Naming Brief)
Never brainstorm from nothing. First understand the thing being named.
Sources, in order of richness:
- A repo or directory path: read the
README,package.json/manifests, and a couple of entry files to infer what it does. - A URL (a PRD, a landing page, a GitHub repo): fetch it with
web_fetchand synthesise. - Free text: use it directly.
Then fill the brief. Keep it tight:
{
"name": "working title or null",
"what": "one sentence: what it is and does",
"willBe": "where it is heading (the ambition)",
"audience": "who it is for",
"tone": ["playful", "trustworthy", "technical", ...],
"keywords": ["concepts", "metaphors", "differentiators"],
"constraints": [".com-able", "one word", "pronounceable", "<= 8 letters", ...]
}
If the context is thin, ask at most two or three quick ask_user questions to
fill the biggest gaps (usually audience and tone). Do not interrogate. Store it
with init --brief-json or brief --json.
Ask what channel matters most (one quick question). Use ask_user with these
options and let their answer pick the verdict --preset passed to report/screen:
| User says | Preset | Key channels (in priority order) |
|---|---|---|
| "npm / CLI / package name" | cli-first (default) | npm > .dev > GitHub org |
".dev domain (developer landing page)" | domain-first | .dev > npm > GitHub org |
".com domain (website / product)" | website-first | .com > .dev > npm |
| "Brand (domain + social)" | brand-first | .com > X handle > .dev |
| "Social handles" | social-first | X > .dev > npm |
| "No strong preference" | balanced | .dev > GitHub org > npm |
Do not assume a default. The user's answer fundamentally changes which names are
"Perfect Match" vs "It's Complicated." If they say .com, then .com availability is
the gating signal — do not dismiss it as "always parked." If they say npm/CLI, then
.com is background noise. Respect their stated priority throughout the entire flow.
Also ask whether GitHub org availability matters to them (yes/no). If they say no, do not include GitHub org status in the primary results table — show it only as a footnote.
Note that social handles are always reported unknown (they cannot be checked honestly),
so a social-first finalist stays "It's Complicated" until the user verifies the handle
by hand.
Sunk-cost check. If there is an incumbent or current name, do not over-favor it. Put it through the same swipe and availability bar as every fresh candidate.
Act 2: the Deck (generate candidates)
Generate about 20 to 30 candidates for the first deck, spanning many strategies
so the swipe deck has real range. See references/naming-strategies.md for the full
playbook (descriptive, compound, portmanteau, metaphor, coined, classical roots,
foreign words, playful, abstract, and more). Aim for variety across length, sound,
and style, not twenty variations of one idea.
For each candidate, attach:
strategy: how you coined it (for exampleportmanteau,metaphor,coined).tags: a few semantic tags from the brief (for examplespeed,nature,trust). These let the model learn "this founder likes nature metaphors".
Add them:
node scripts/naming.mjs add --dir D --json '[
{"name":"Brightloom","strategy":"compound","tags":["light","craft"]},
{"name":"Flowly","strategy":"suffix","tags":["ease"]},
{"name":"Verdant","strategy":"real-word","tags":["nature","growth"]}
]'
Quality bar: on-brief, pronounceable, and genuinely varied. Skip anything that is a near-duplicate of another candidate or an obvious throwaway.
Generation modes (offer these when the user wants range or a direction):
- By language / etymology: Greek, Latin, French, or Spanish roots for a core concept (the user often asks for this).
- In the style of a corpus: "name it like a YC startup" or "like cloud-native projects (radius, terraform)". Study the naming pattern, then generate within it.
- More like a liked name:
suggest --name <liked>for morphological variants, then regenerate creatively in that direction.
Availability-FIRST (recommended for a name you will commit to). Before swiping, run
screen --names <batch> and add only the clear names to the deck. This is the
pattern that actually works: it stops the user falling in love with a name that turns
out to be taken. Generate a large batch, screen it, swipe only the survivors.
Act 3: the Swipe (the loop)
This is the heart of the skill. Show one card at a time and let the model learn.
-
Get the next card:
node scripts/naming.mjs next --dir D. It returns the best unseen candidate for this user based on what they have liked so far. -
Present it as a card with
ask_user. Put the name, its vibe, and a one-line pitch in the question text; use these exact swipe choices:Brightloom (compound, warm, 2 syllables) "A warm, growing-light name for a calm productivity app." How it fits: light plus a loom that weaves your tasks together.
Choices:
["❤️ Like", "👎 Pass", "⭐ Super-like", "ℹ️ Details / check it now", "✅ Done, show my matches"] -
Record the swipe and update the model:
- "❤️ Like" ->
swipe --id <id> --label like - "👎 Pass" ->
swipe --id <id> --label pass - "⭐ Super-like" ->
swipe --id <id> --label superlike - "ℹ️ Details" -> do NOT swipe yet. Say more about the name, optionally run
check --names <name>for a quick availability peek, then re-ask the same card. - "✅ Done" -> leave the loop and go to Act 4.
- "❤️ Like" ->
-
Repeat. Every few swipes, you can share "your type" from
node scripts/naming.mjs profile --dir D("You are leaning toward short, coined, techy names."). It makes the learning visible and is fun. -
Refill the deck when
nextreports few remaining or the model has a clear type. Generate a fresh batch biased toward the learned preference: readprofile, optionally seed withsuggest --name <a liked name>, generate new on-brief candidates in that direction (plus a few wildcards so the user keeps discovering), andaddthem. -
Rapid-fire option: if the user wants to go faster, offer to show a numbered batch of five cards in one message and let them reply with the ones they like. Record each as a swipe. The default remains one card at a time (true swiping).
When to stop. Stop when the user picks "Done", or when the top of rank
stabilises and the user has several likes. You do not need to exhaust the deck.
Conversational mode (meet the user where they are). Many people do not want a rigid
deck; they throw out names as they think of them ("what about brightloom?", "riverstack
might work", "or lumen?"). When that happens, react instantly: add the name, check
it, run similar against known names in the space, mention pronounceability, then
suggest/generate "more like that". Record their yes/no as a swipe so the model still
learns. The swipe deck and the conversation are the same engine; use whichever fits.
Decision mode (when the user is stuck). If the user cannot converge ("I don't like
any of these, help me decide"), stop swiping and run a duel tournament: present two
finalists head to head, ask which wins, and duel --winner <id> --loser <id>. A few
duels move the model decisively and produce a ranked order without endless swiping.
Priority correction (when the user changes their mind). If the user corrects their channel priorities mid-flow (e.g. "actually, I need .com, not .dev" or "GitHub org doesn't matter to me"), do NOT jump straight to declaring a winner. Instead:
- Acknowledge the correction and confirm the new preset.
- Re-run
screenorcheckwith the corrected--preset. - Show the full re-evaluated results table (same detail level as the original), now ranked by the corrected priority.
- Let the user pick from the re-ranked options.
Never collapse from "here are 5 options with tables" to "here's your one winner" just because the user corrected you. The correction changes the ranking — it does not eliminate the user's right to choose.
Act 4: the Match (availability + verdict)
Now turn likes into a decision.
-
Deep availability on the finalists (the liked and super-liked names):
node scripts/naming.mjs check --dir D. To raise the GitHub rate limit, pass--github-tokenor setGITHUB_TOKENin the environment (optional; never required, never logged). -
Confusability + trademark + business (mandatory, not optional). The engine screens against a bundled famous-marks list (that catches "you cannot call it Spotify"), but in practice the real collisions are live products and similarly-named startups in your own space, which no bundled list can know. So for every finalist you MUST run a live
web_search, for example"<name>" (trademark OR company OR startup OR product), and feed what you find intosimilar --name <finalist> --against <comma-separated names you found>. A high-confusability hit (a near-duplicate spelling or a reordering of the same parts) or a real product in a related field is a caution the user must see; a famous-mark hit is a Deal Breaker. -
Decorated variants for taken key channels. If the bare GitHub org or a handle is taken, run
variants --name <finalist>to screenget<X>/<X>hq/<X>devforms. Offer the free variant instead of discarding the name. -
Render the matches:
node scripts/naming.mjs report --dir D [--preset P]. Each finalist rolls up to one of three verdicts:Verdict Meaning 🚫 Deal Breaker A famous trademark or company owns it. Walk away. 💛 It's Complicated No legal blocker, but a key channel (npm, .dev, or the GitHub org) is taken. Usable only with a compromise (a decorated org, a tweaked handle).💚 Perfect Match No famous-mark collision and every key channel (per the chosen preset) is free. Grab it. The report crowns a winner and lists the field. A Deal Breaker is never the winner. Present results using the user's chosen preset: if they picked
website-firstorbrand-first,.comis the headline signal; if they pickedcli-first, npm and.devare. Never contradict their stated priority by dismissing a channel they told you matters. Present it conversationally. -
Show ALL liked names, not just the winner (mandatory). The user approved those names — they deserve to know what happened to each one. Present the full field as a ranked list. For every name that is NOT the winner, give an explicit one-line reason why it ranked lower:
- "
.comtaken (registered to XYZ Corp)" - "Trademark conflict: Clearfork Foods (USPTO #5998237)"
- "npm package already exists (last published 2023)"
- "GitHub org taken; decorated variants available:
cleanforkhq,getcleanfork" - "No issues found, but ranked #2 on fit score behind the winner"
Never silently drop a liked name. If the user liked 6 names and you show them 1 winner with no mention of the other 5, that is a failure. They will feel unheard and wonder if the skill even remembered their choices. The minimum output is:
## 💚 Winner: Noshcheck [scorecard + why it won] ## The rest of your likes: 2. 💛 Cleanfork — .com taken (parked by GoDaddy) 3. 💛 Dinecheck — .com taken (active restaurant site) 4. 🚫 Clearfork — trademark: Clearfork Foods (USPTO #5998237, food industry) 5. 💛 Forkwatch — .com taken; GitHub org taken (decorated: forkwatchhq ✅) 6. 💚 Platemark — all clear, ranked #2 on fitThis lets the user override the recommendation with full information ("actually I'll take Platemark and live without the .com"). Do not make that choice for them.
- "
Act 5: next actions
Once the user picks a winner, give them a concrete reservation checklist to lock it down (do not do these for them; the skill only checks and links out):
- Primary domain (per the user's preset): if they chose
website-firstorbrand-first, the.comis the top priority — register it immediately. If they chosecli-firstordomain-first, the.devis the launch TLD. Register whichever they told you matters most. - npm / package name (for CLI/library projects): reserve it, or
npm publisha placeholder. A tombstoned name flagged by the scorecard is blocked; pick another. - Secondary domain: register the other TLD (
.devif they got.com, or vice versa) as a backup redirect. - GitHub org / repo:
https://github.com/organizations/new. If the bare org is taken, use the decorated variant fromvariants(e.g.<name>hq). Skip this if the user said GitHub org doesn't matter to them. - Social handles: the verify URLs are in the scorecard (
channels.social). - Formal trademark clearance: the screen is not legal advice. Point the user (or
their lawyer) at the
channels.trademark.linksand note the relevant class (for software, US classes 9 and 42) before they invest in the brand.
If the name is replacing an existing one, offer to do the rename as a separate step. Offer to reopen the deck if nothing felt like The One.
Availability channels and honesty
| Channel | How it is checked | Reliability |
|---|---|---|
| Domains | DNS NS-delegation is the primary signal (NS records present = registered, even when parked; absent = free); RDAP confirms the free case for non-Google TLDs. .dev/.app decide on NS alone (RDAP false-positives on parked .dev). | High. Catches parked domains that RDAP misses. |
| GitHub | api.github.com/users/<name> (org and user share a namespace). 404 = free. | High. Rate-limited without a token. |
| Registries | npm, PyPI, crates.io, RubyGems, NuGet public JSON. 404 = free. npm taken is inspected for a reclaimed tombstone (blocked from reuse). | High. |
| Social | Best-effort link-out only. The scorecard gives a verify URL, status unknown. | Low by design. Never claim a handle is free. |
| Trademark / business | Famous-marks screen (confident collision) + similar confusability + search link-outs + your mandatory web_search. | A screening signal, not legal clearance. |
The rules that keep this honest:
- Social platforms block bots and return soft-404s, so the engine does not scrape them. It surfaces the handle URL for the user to click. Do not override this.
- A clean trademark screen means "no obvious collision", never "clear to use". Always point the user (or their lawyer) at the official search links.
- The engine never buys a domain, creates an org, or claims a handle. It checks and links out.
Safety
- The engine sanitises every candidate name to a strict
[a-z0-9-]slug before it touches any URL or query, contacts only an allowlisted set of hosts, applies timeouts, and refuses redirects to non-public hosts. Do not bypass it with ad-hoc network calls. - No credentials are required. An optional GitHub token only raises a rate limit and is read from the environment, never logged.
- Any GitHub write on the user's behalf (creating the org or repo they chose) is a separate action that requires explicit user approval. This skill only checks and recommends.
State and resuming
Everything lives in the --dir state file, so a run survives across turns and
sessions. To resume, point the same --dir at the existing state and continue with
next. state --dir D dumps everything; reset --dir D starts over.
The workflow you follow
- Intake the context; read the repo or fetch the URL if given one.
- Write the Naming Brief;
init --brief-json(orbrief). - Generate ~20 to 30 diverse, on-brief candidates;
addthem. - Swipe loop:
next->ask_usercard ->swipe; refill the deck as needed; shareprofileoccasionally. - On Done:
checkthe finalists; run aweb_searchper finalist for trademarks and businesses. reportthe ranked matches with verdict tiers; present ALL liked names with per-name reasons for their ranking (not just the winner).- Offer next actions (grab org/domain/handles) and an option to keep swiping.
Exit criteria
- A Naming Brief was written and stored.
- The user swiped through candidates and the model learned a preference (
profilehas signal). - The finalists were checked for availability and screened for trademark/business collisions.
- A ranked report with verdict tiers was presented showing EVERY liked name with an explicit reason for its rank/verdict, with a winner that is not a Deal Breaker.
- The user was given the links to lock the name down.
- The user was given the opportunity to override the recommendation (they may prefer a #2 name and accept its tradeoffs).