CCLearn

"Slash commands go unused" rests on two frictions that are gone

Most of a hand-built slash-command library goes unused - but the two reasons the best-known writeup gives, no argument passing and no way to inject git context, are both fixed now, so check the reasons before inheriting the conclusion.

circulating and false

This advice is common and does not hold. The official page saying otherwise is linked below.

What this costs you if it is wrong

Read as a blanket "don't build commands" this costs you the cases that clearly pay off — team-wide conventions checked into a repo, and workflows that need live state injected via `` !`command` ``. The specific frictions the source names were fixed after it was written, so its conclusion rests on premises that no longer hold. Separately, the audit command reads your saved transcript files: it only counts locally and sends nothing anywhere, but the same directory holds full conversation history, so do not paste its surrounding files into a shared channel.

You have a .claude/skills/ directory with eight or ten entries in it — commit-message, lint-fix, write-tests, fix-bug, todo-sweep — and you cannot remember the last time you typed any of them except one. The library felt like leverage while you were building it. In daily use you keep typing the request out longhand instead.

Armin Ronacher wrote up exactly this outcome in July 2025: he built commands for grammar fixing, bug fixing, commit messages, test generation, linting and todo tracking, and reports that most went unused. His conclusion was that mentioning a GitHub issue URL in the prompt did the same job with no file to maintain.

The observation is worth having. His two stated reasons are no longer true, and that matters, because if you inherit the conclusion without checking the reasons you will skip commands that would now work.

What the docs say about his reasons

His frictionStatus todayWhere
No unstructured argument passingGone. $ARGUMENTS expands to whatever follows the command name; $0, $1, $ARGUMENTS[N] index individual onesskills — Pass arguments to skills
Arguments silently dropped if the file has no placeholderHandled. Claude Code appends ARGUMENTS: <your input> to the skill content so Claude still sees what you typedskills
Ends up piping git status in by handUnnecessary. A !`git status --short` line runs before the content reaches Claude and is replaced by its outputskills — Inject dynamic context
No file autocomplete inside the commandPartly stale. @ references inside a local skill attach the files they name; argument-hint frontmatter shows expected arguments during / autocompleteskills

The docs are not neutral on the wider point either. The best-practices page ships a fix-issue skill as a worked example — the same command he says he stopped using — and the official recommendation is to write skills for repeatable workflows.

What survives

Strip out the mechanics and one durable claim is left, and it is not about Claude Code's features at all:

A command is only worth its upkeep once you have typed the prompt verbatim several times.

That is a real cost model. Every command file is a thing that drifts from your actual workflow, that a teammate has to discover, and that you must remember exists at the moment you need it. The last item is the quiet killer — an unremembered command is worse than no command, because you paid for it and got nothing.

So the rule to copy is sequencing, not abstinence:

  1. Write the prompt inline. Every time.
  2. When you notice you have typed roughly the same thing three or four times, look at what varied between them.
  3. If what varied is one value — an issue number, a filename, a component name — that is a $ARGUMENTS slot, and the command is worth writing.
  4. If what varied is the shape of the request, leave it inline. You will only end up editing the command every time you use it.

Where a command still clearly pays

  • Team conventions. One checked-in SKILL.md with your API rules or commit format beats every person on the team retyping it slightly differently. This is the case Ronacher's post does not cover, and it is the strongest one.
  • Anything with a fixed preamble of live state. !`gh pr diff`, !`git status --short`, !`node --version` — inlining these by hand is exactly the friction he complained about, and it is the friction the ! syntax removes.
  • Anything with pre-approved tools. allowed-tools in frontmatter grants the tools for that turn, so a commit command does not stop to ask about git add and git commit.

What is unverified here

Nobody has reproduced the underlying claim, and it is not the kind of claim that reproduces. "Most of my commands went unused" is one experienced engineer's report of his own habits over some months in mid-2025, on a version of the tool whose argument handling has since changed. It is not a measurement, it does not generalize by construction, and the docs neither confirm nor deny it — they are silent on which commands are worth building and say nothing about adoption rates.

What the docs do settle is that the reasons he gave are stale. That is the part to trust.

What would settle the rest is your own transcript history rather than anyone's blog post. Count how often you actually invoke each command you own, and delete the ones at zero:

bash
# approximate: counts leading /command tokens in your saved transcripts
grep -rhoE '"text":"/[a-z0-9_-]+' ~/.claude/projects/*/*.jsonl \
  | sed 's/.*\///' | sort | uniq -c | sort -rn | head -30

Run that with bash -c, not pasted into zsh — the glob aborts the whole line if a directory has no matching files. The counts are rough: they catch commands you typed at the start of a message and miss ones Claude auto-invoked. Compare the output against ls ~/.claude/skills ~/.claude/commands .claude/skills .claude/commands and the gap is your answer, for your workflow, which is the only one the question was ever about.

Checkpoint

  • Which of your commands did you last invoke more than a week ago, and would you notice if it were deleted?
    Show answer

    Only your own transcripts can answer this, so count rather than recall. Run the grep over your saved .jsonl transcripts with bash -c — pasted into zsh the glob aborts the whole line when a directory holds no matching files — and compare its output against what actually sits in ~/.claude/skills, ~/.claude/commands, .claude/skills and .claude/commands. The gap between what you own and what you invoke is the answer. The counts are rough: they catch commands you typed at the start of a message and miss ones Claude auto-invoked. Delete the ones at zero, because an unremembered command is worse than no command — you paid for it and got nothing.

  • For a command you kept: what varies between invocations — one value, or the whole shape of the request?
    Show answer

    If one value varies — an issue number, a filename, a component name — that is a $ARGUMENTS slot and the command earns its upkeep; $ARGUMENTS expands to whatever follows the command name, and $0, $1 and $ARGUMENTS[N] index individual ones. If the shape of the request varies, leave it inline: you will only end up editing the command every time you use it. What you must not do is retire a command on the "no argument passing" reasoning from the 2025 post — the skills docs settle that this friction is gone, along with the silent drop, since Claude Code now appends ARGUMENTS: <your input> when the file has no placeholder.

  • Does any skill in your project repo carry an allowed-tools grant you did not write?
    Show answer

    Open the frontmatter and look — this is a fact about your repo, not something an answer key can supply for you. It is worth the minute because allowed-tools is a real permission grant and workspace trust does not gate it: a project skill checked into a repository applies its grant when invoked, including in a -p run in a folder you have never trusted. Check the !`command` lines in the same pass, since those execute on your machine before Claude sees the file, and check both locations — .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md each produce /deploy.

Where this came from

What the docs actually say