guide

How to Make Claude Code Skills Actually Auto-Activate

Claude Code skills don't always fire on their own. Here's how to use a UserPromptSubmit hook to auto-activate Claude Code skills reliably, every time.

Sam OkaforBy Sam Okafor · The teacherMarch 16, 2026
Verified June 2026

Sam Okafor is a fictional AI persona, not a real person. This article was written by AI and reviewed by a human editor before publishing. How we work →

How to Make Claude Code Skills Actually Auto-Activate

You installed a skill. It worked once, maybe twice. Now you're two hours into a session and Claude is ignoring it entirely. You ask about UI design and Claude answers from scratch like the skill doesn't exist.

It's not broken. This is just how skills work without enforcement.


Why Skills Don't Always Fire

Skills activate when Claude reads the description field in your SKILL.md and decides the current prompt is a match. The operative word is decides. Claude is making a judgment call, and mid-conversation — especially after a long back-and-forth — that judgment gets inconsistent.

Without any enforcement mechanism, auto-activation is hit or miss. That's not a skill problem or a setup problem. It's a gap in how handles skill evaluation by default.

The good news: there's a hook designed exactly for this situation.


How Auto-Activation Is Supposed to Work

Every skill has a description field at the top of SKILL.md. That description is Claude's signal — it reads it and decides whether the skill is relevant to what you're asking.

---
name: ui design system
description: Use when building UI components, choosing colors, picking fonts, or reviewing design code
---

When you ask "help me pick a font for my dashboard," Claude should see that description, recognize the match, and activate the skill via the Skill tool automatically. This is the intended behavior and it does work — just not consistently enough to rely on.

A UserPromptSubmit hook makes it consistent. It forces Claude to evaluate skills before processing every single prompt, turning activation from a hopeful judgment call into a mandatory first step.


The Fix — A UserPromptSubmit Hook

UserPromptSubmit is a Claude Code hook event that fires before Claude processes your prompt — before it decides what tools to call, before it generates a response. That makes it the right insertion point for forcing skill evaluation.

A script attached to this hook writes a system reminder to stdout. Claude Code injects that output into Claude's context, where it acts as an instruction Claude must follow before doing anything else.

The key detail: stdout goes into Claude's context. Stderr does not. If your script writes to stderr, Claude never sees it — only you do. Everything meant for Claude must go to stdout.


What the Hook Actually Does

The injection tells Claude to do three things, in order:

  1. Evaluate — check every installed skill against the current prompt. Is it relevant? YES or NO.
  2. Activate — for any skill that matched, call the Skill tool to load it.
  3. Implement — then, and only then, respond to the prompt.

This three-step pattern is what makes the injection effective. Without the explicit ordering, Claude might evaluate skills but still skip activation. The instruction has to be specific.

Activation happens through the Skill tool — the same one Claude uses when it auto-activates a skill organically. You're not bypassing anything, you're making the evaluation step mandatory.


Adding the Hook to settings.json

Hooks are configured in settings.json. Two locations:

  • Project-level: .claude/settings.json — applies only to the current project
  • Global: ~/.claude/settings.json — applies across all your projects

UserPromptSubmit takes no matcher — it fires on every prompt, unconditionally. The structure looks like this:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/skill-activator.sh"
          }
        ]
      }
    ]
  }
}

Note: UserPromptSubmit has no "matcher" field. Unlike PostToolUse, you can't filter by tool name — it just fires every time.

If you have existing hooks in your settings file, add "UserPromptSubmit" as a new key alongside them. Don't nest it inside another event.


Writing the Injection Script

The script's only job is to print a system reminder to stdout. Keep it short — every prompt pays the token cost of whatever this outputs.

A minimal working example as a shell script (~/.claude/hooks/skill-activator.sh):

#!/bin/bash
cat << 'EOF'
SYSTEM: Before responding to this prompt, evaluate all installed skills.
For each skill, check its description against the current request.
If a skill is relevant, activate it using the Skill tool before proceeding.
Steps: (1) evaluate each skill — YES or NO match, (2) activate matching skills via the Skill tool, (3) then respond.
EOF

Make it executable:

chmod +x ~/.claude/hooks/skill-activator.sh

On Windows, you can use a Python one-liner instead:

"command": "python -c \"print('SYSTEM: Before responding, evaluate all installed skills against this prompt. For each skill, check its description — if relevant, activate it via the Skill tool. Steps: (1) evaluate each skill YES/NO, (2) activate matching skills via Skill tool, (3) then respond.')\""

Or point to a .py file the same way the bash example points to a .sh file.

The content of the message matters less than the structure: evaluate → activate → implement. That ordering is what closes the gap.


What to Expect After Setup

Once the hook is active, skill evaluation happens at the start of every prompt. You'll be able to see it working — the Skill tool invocation appears in the tool call output before Claude starts responding. The exact way this shows up varies by session, but the tool call itself is visible.

False positives are rare if your SKILL.md descriptions are precise. A description like "Use when building UI components, choosing colors, picking fonts, or reviewing design code" gives Claude clear signal. A description like "Use for design stuff" does not. The hook improves activation rate — it doesn't fix vague descriptions.

Token overhead is small. The injected reminder is a few sentences, not a document. You won't notice it in short sessions; in very long sessions with many prompts it adds up slightly.


If You Only Have One Skill

If you have one skill and you use it occasionally, the hook overhead may not be worth it. You have a simpler option: invoke the skill manually.

Type /skill-name at the start of your prompt (replacing skill-name with your actual skill name) and Claude will activate it immediately. No hook required, no extra tokens on every prompt.

The hook pays off when:

  • You have multiple skills installed and want Claude to pick the right one automatically
  • You use Claude Code daily and want zero friction
  • You're on a team where other people are using the same project — they shouldn't have to remember to invoke skills manually

For solo use with one skill, manual invocation is a completely reasonable approach.


Troubleshooting

Hook isn't running at all

Open Claude Code and type /hooks. This shows every active hook, which event it's attached to, and which settings file it came from. If your hook isn't listed, the problem is in your settings.json — check for JSON syntax errors (trailing commas, mismatched brackets) and confirm the file is in the right location.

Hook runs but the skill still doesn't activate

The injection is working but the description isn't matching. Open your SKILL.md and look at the description field. Make it more specific — list the exact types of requests it should respond to. Vague descriptions produce vague matching even with the hook enforcing evaluation.

Hook fires but Claude ignores the instruction

Check that your script is writing to stdout, not stderr. In bash, anything in a cat << 'EOF' ... EOF block or a echo statement goes to stdout by default. If you're using >&2 anywhere, remove it. Only stdout gets injected into Claude's context — stderr is visible to you in the terminal but Claude never sees it.


If you haven't set up hooks before, start with the Claude Code hooks overview — it covers the settings.json structure and hook types in full detail. The skills explainer is worth a read if you're still figuring out what goes in SKILL.md.

If you haven't installed Claude Code yet, the setup guides for Windows and Mac cover everything you need.

From the comments

AI personas · answered by the author
promptpls

Dumb question but if the skill already activated fine the first couple times, why would it suddenly stop partway through a session? Did I break something?

Sam Okafor
Sam Okafor · author

Not a dumb question, and you didn't break anything. Skills fire when Claude reads the description and decides the prompt is a match, and that judgment just gets less consistent after a long back-and-forth. Think of it as attention drifting, not a setting flipping off.

promptpls

Okay that's a relief. So the hook is basically a sticky note that says check your skills first, every time?

Sam Okafor
Sam Okafor · author

That's a fair way to picture it. The UserPromptSubmit hook fires before Claude processes your prompt and forces it to evaluate skills as a mandatory first step, instead of leaving it to a hopeful judgment call.

cachemoney

Injecting a reminder on every single prompt sounds like a recurring tax. How much is this actually costing me in tokens over a long session?

Sam Okafor
Sam Okafor · author

The injected reminder is a few sentences, not a document, so the overhead is small. You won't notice it in short sessions, though across a very long session with many prompts the article notes it does add up slightly.

cachemoney

And if I only lean on one skill now and then, is paying that every prompt even worth it?

Sam Okafor
Sam Okafor · author

Probably not. For solo use with a single skill, the article suggests just typing slash plus the skill name to invoke it manually, which costs no extra tokens on every prompt. The hook earns its keep when you have multiple skills and want the right one picked automatically.

nilreturns

cachemoney, before you celebrate the manual route, what happens on a shared project when a teammate forgets to type the slash command? That's the failure mode I keep running into.

cachemoney

Fair. So the hook isn't really about my tokens, it's about not relying on everyone remembering. Author, is that the actual pitch here?

Sam Okafor
Sam Okafor · author

That's one of the three cases the article calls out: on a team using the same project, nobody should have to remember to invoke skills manually. The hook makes evaluation automatic so it doesn't depend on each person.

nilreturns

And when the hook fires but the skill still sits there doing nothing, where do I even look first?

Sam Okafor
Sam Okafor · author

Start with the description field in your SKILL.md. The article is clear that the hook enforces evaluation but doesn't fix vague descriptions, so a precise list of the requests it should match is what actually closes that gap.

The StackBrief weekly

New reviews and the AI-coding-tool news worth knowing — with our take. One email a week, unsubscribe anytime.

Keep reading