/promptalias

Seven reusable prompts, written once as Agent Skills and shared across Claude Code, Codex, and Cursor. One folder per prompt, one SKILL.md each, no build step.

7 prompts 3 tools 0 dependencies 101 phrasings & probes measured, 0 failing

There is no promptalias command. The name comes from a CLI that was designed and then killed before implementation — a compiler from one terse YAML file into per-tool command files. The three tools had already converged on a single format, so there was nothing left to compile. The name stuck; the compiler never happened.

# all seven prompts, every agent, from this repo
npx skills add kishormorol/promptalias --global --all

Two caveats that were measured rather than assumed — add copies instead of linking back, and it reported success for two tools it never wrote to. The symlink chain below is the fix for both.

The unit

A prompt is a folder with one file in it

The folder name is the trigger, so u/ gives you /u. Everything that decides whether the prompt fires lives in two lines of frontmatter.

u/SKILL.md
---
name: u
description: Update an existing feature while keeping its tests green.
  Use when the user asks to change, extend, modify, or adjust behaviour
  that already exists — "update the X", "make X also do Y", "tweak X" …
---

# Update a feature

Update this feature: $ARGUMENTS.
Keep existing tests passing.
…
name

Must match the folder. The validator fails the build if it drifts.

description

The only text matched against what you typed. Written as “Do X. Use when Y.”

Use when

A description without this clause installs fine, lists fine, and then never fires. Nothing anywhere reports it — so the validator does.

body

The prompt itself. $ARGUMENTS takes whatever followed the trigger.

The set

Seven prompts, split by what they are allowed to touch

/n and /u divide on whether the thing exists yet. /ex and /p are the two that write no code at all.

/n
Build something new that isn’t there yet, with tests
“build me a X”
/u
Update a feature, keeping its tests green
“make X also do Y”
/t
Write tests for code that already works
“cover X with tests”
/d
Track a failure to its root cause and fix it
“why is X broken”
/rv
Review the last diff for correctness, security, performance
“did I break anything”
/ex
Explain existing code, changing nothing
“walk me through X”
/p
Plan an approach before any code is written
“what would it take to X”

How it fires

Two paths from a sentence to a prompt

The portable path works in all three tools and is the one to lean on. The hook exists for the case the first path keeps missing: your own wording.

You type a sentence “why is the cache broken” 1 · The agent matches It reads every prompt’s description and judges which one fits what you said. Claude Code · Codex · Cursor 2 · The hook matches hooks/resolve.py reads the sentence, matches quoted examples + your own phrases, appends one line of context. Claude Code only /d runs
The hook never blocks, never rewrites, and exits silently on any failure — the worst it can do is fail to suggest something.

What that looks like from a terminal. Path 1 needs nothing shown here — the agent reads the descriptions itself. Path 2 is the hook, and this is the whole of it: it prints the prompt it matched, hands the agent one line of context, and says nothing when nothing fits.

hooks/resolve.py
$ ./hooks/resolve.py --explain "why is the resolver broken"
/d <- "why is X broken" (description)
$ ./hooks/resolve.py --explain "eyeball this"
/rv <- "eyeball this" (vocabulary)
← your own wording, and it outranks the descriptions
$ echo '{"prompt":"why is the resolver broken"}' | ./hooks/resolve.py
{"hookSpecificOutput": {"hookEventName": "UserPromptSubmit",
"additionalContext": "The user's wording matches the /d prompt
(on \"why is X broken\"). Invoke the d skill unless it plainly
does not fit this request."}}
← one line appended. Nothing blocked, nothing rewritten.
$ ./hooks/resolve.py --explain "deploy to production"
no match
Real output from those four commands, not a mock-up: the only edits are the long JSON line wrapped to fit, and the two ← notes, which the hook does not print. The silence on the last one is the designed outcome, not a failure.

Widen the description first

works in all three tools

Listing more of the ways a request actually gets worded costs nothing and travels everywhere. This is the portable half, and the one to exhaust before reaching for anything else.

Then, if it still misses, the hook

Claude Code only

Selecting a prompt from your own keywords needs the input string, which only a hook sees. Codex and Cursor expose no equivalent — so a phrase that works only here does not travel. That cost is paid on purpose.

Measured

Sentences nobody copied out of a description

Every phrasing is run back through the resolver, alone and inside five carrier sentences, and has to come back pointing at the prompt that claims it. These rows come from scripts/probes.json — written from scratch, then checked on every push.

What you typeMatched onRuns
make the parser also handle tabs“make X also handle Y”/u
I need something that watches the log“I need something that Y”/n
cover the parser with tests“cover X with tests”/t
why is the cache broken“why is X broken”/d
did I break anything with that change“did I break anything”/rv
walk me through resolve.py“walk me through X”/ex
what would it take to support Windows“what would it take to X”/p
deploy to production—silence

The first row is the interesting one. /u’s description lists “make X also do Y”, which does not match “also handle tabs” — exactly the kind of miss the hook’s vocabulary.json exists to close.

The harder half

Whether the agent fires on these words

The table above measures the hook — the matcher whose decision is visible from outside. The agent is the other half: it reads the whole description and decides with judgement rather than a regex, so it can fire on wording no example lists, and skip wording every example lists.

That half was measured too. Nine ordinary sentences, one session each, hooks disabled so the hook could not supply the answer it was being compared against. Seven asked for a prompt and got it; two asked for neither and got silence.

One session each is not a rate, and the one sentence since run repeatedly shows why: “why is the resolver broken” reached for /d six times out of seven, and once reached for nothing at all. The same words, the same descriptions, a different day.

So it is evidence, not proof — non-interactive sessions, written by the same hand as the descriptions. The phrasings stay recorded as unambiguous rather than as proven, and the method and its limits are written down next to the result. Everything here — the seven prompts, the phrasings, the vocabulary — is a starting point to fork and change, not a fixed list.

Install

One chain of symlinks, no sync step

npx skills add copies rather than links, and reported success for two tools it never wrote to — both measured, both documented. Pointing the hub at the repo and each tool at the hub makes an edit here live everywhere, immediately.

~/promptalias/u the only real copy symlink ~/.agents/skills/u the hub ~/.claude/skills Claude Code ~/.codex/skills Codex ~/.cursor/skills-cursor Cursor
# every prompt, every tool, resolving to this repo
mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.cursor/skills-cursor
for s in u n rv t d ex p; do
  rm -rf ~/.agents/skills/$s
  ln -s  ~/promptalias/$s          ~/.agents/skills/$s
  ln -sfn ../../.agents/skills/$s  ~/.claude/skills/$s
  ln -sfn ~/.agents/skills/$s      ~/.codex/skills/$s
  ln -sfn ~/.agents/skills/$s      ~/.cursor/skills-cursor/$s
done

The tradeoff: npx skills no longer manages these entries, so a future add may replace the links with copies again.

Your own wording

When the description keeps missing how you say it

Turn the hook on by copying one block into ~/.claude/settings.json. It runs on every prompt you submit, so the properties that matter are negative: it never blocks, never rewrites, and exits silently on any failure.

{ "hooks": { "UserPromptSubmit": [ { "hooks": [
  { "type": "command", "command": "~/promptalias/hooks/resolve.py", "timeout": 5 }
] } ] } }

Then add how you actually phrase things. A lone capital stands for whatever you really say, and your own wording outranks anything lifted from a description.

hooks/vocabulary.json
{ "prompts": {
    "u":  ["make X also handle Y", "wire X up to Y"],
    "d":  ["track down X", "what's up with X"],
    "rv": ["eyeball this", "poke holes in this"]
} }

Ask it what it would do with a sentence before trusting it with one:

$ ./hooks/resolve.py --explain "make the parser also handle tabs"
/u <- "make X also handle Y" (vocabulary)
$ ./hooks/resolve.py --explain "sanity check this before I push"
/rv <- "sanity check this" (description)
$ ./hooks/resolve.py --explain "what is the weather today"
no match

Two prompts that match equally well also produce no match. A silent hook is the designed outcome, not a broken one — the worst it can do is fail to suggest something.

Add your own

A folder, a file, and one rule that matters

The folder name is the trigger, so keep it short. Bodies stay readable in one screen; if a prompt needs more than that, it wants to be a real skill with reference files.

mkdir mytrigger && $EDITOR mytrigger/SKILL.md
---
name: mytrigger
description: Do X. Use when the user asks to …
---

Body of the prompt. $ARGUMENTS takes whatever followed the trigger.

The rule that matters more than the rest: write the description as “Do X. Use when Y.” A bare label like "Update a feature" installs fine, appears in skills list, and then never fires on its own. Nothing anywhere reports that as broken — so the validator does:

$ ./scripts/validate.py
error mytrigger/SKILL.md: `description` has no "Use when" clause, so this
prompt will never auto-invoke — it will only work if you type the
trigger yourself
1 error(s) across 8 prompt(s)

Kept honest by

Six checks, on every push

Python 3, no dependencies, nothing to install. The same six commands run locally.