Glean 拾遗
Daily /2026-07-21 / How to Build a Claude Code Skill That Actually Works (Full Guide)

How to Build a Claude Code Skill That Actually Works (Full Guide)

Source x.com Glean’d 2026-07-21 06:00 Read 11 min
AI summary

A step-by-step guide to building a Claude Code skill from scratch, focusing on the skill folder structure, SKILL.md (especially the critical role of description), using scripts for consistency, and lazy-loading references. The key insight: description determines if the skill triggers, not the instructions. Practical for engineers using Claude Code to persist workflows.

Original · 11 min
x.com ↗
§ 1

You've used Claude for a week and you keep typing the same thing. Every time you commit code, you paste the same three rules about your commit format. Every time you write a doc, you re-explain your style. Claude does it well, then forgets the moment the chat ends, and tomorrow you type it all again.

A skill fixes that. It's a small folder you write once that teaches Claude a workflow permanently, so it applies every session without you asking.

Here's how it works, at a glance: a skill is a folder with one file inside. Claude keeps a one-line summary of it in view at all times, and pulls in the full instructions only when your request matches. That's the whole mechanism.

In this guide we build one real skill from nothing: commit-messages, which writes git commits in your exact format. If you have Claude installed and nothing else, you can follow every step.

你使用 Claude 已经一周了,却一直重复输入同样的内容。每次提交代码时,你都要粘贴那三条关于提交格式的规则;每次写文档时,你都要重新解释你的写作风格。Claude 当时做得很好,但对话一结束它就忘了,第二天你又得全部重来。

技能(Skill)能解决这个问题。它是一个小文件夹,你只需编写一次,就能让 Claude 永久记住一个工作流,每次会话都会自动应用,无需你再次要求。

简单来说,技能的工作原理是这样的:一个技能就是一个内含一个文件的文件夹。Claude 会始终在视图中保留该技能的一行摘要,只有当你的请求与之匹配时,才会加载完整的指令。这就是全部机制。

在本指南中,我们将从零构建一个实际技能:commit-messages,它能以你指定的格式编写 git 提交信息。只要安装了 Claude,你就能跟随每一步操作。

§ 2

What you'll end up with

At its core a skill is one folder with one required file, SKILL.md. Three optional folders come in later as the skill grows:

your-skill-name/
├── SKILL.md          # Required - the main skill file
├── scripts/          # Optional - executable code
├── references/       # Optional - documentation
└── assets/           # Optional - templates, etc.

SKILL.md itself has two parts: a short header that tells Claude when to use the skill, and instructions below it that tell Claude what to do. The reason for the split matters. Claude reads the header constantly, so it always knows the skill exists, but it only loads the instructions when your request matches. Keep that distinction in mind, because almost everything else in this guide follows from it.

最终成果

技能的核心是一个文件夹,内含一个必需文件 SKILL.md。随着技能的发展,还有三个可选文件夹:

your-skill-name/
├── SKILL.md          # 必需 – 主要技能文件
├── scripts/          # 可选 – 可执行代码
├── references/       # 可选 – 文档资料
└── assets/           # 可选 – 模板等

SKILL.md 本身包含两部分:一个简短的头部,告诉 Claude 何时使用该技能;以及头部下方的指令,告诉 Claude 该做什么。这种拆分是有原因的。Claude 会持续读取头部,因此它能始终知晓该技能的存在,但只有当你请求匹配时才会加载完整指令。请牢记这一区别,因为本指南中几乎所有其他内容都源于此。

§ 3

Create it

Skills live in a folder called .claude/skills inside your home directory, which both Claude Code and the desktop app read from. It's hidden and probably doesn't exist yet, so the quickest way to create it, along with your skill's folder, is a single command.

On Mac, open Terminal and run:

mkdir -p ~/.claude/skills/your-skill-name

On Windows, open PowerShell and run:

New-Item -ItemType Directory -Force -Path "$HOME\.claude\skills\your-skill-name"

The folder name isn't cosmetic. Claude uses it as the skill's identifier, and one formatting rule trips people up more than any other:

  • Use kebab-case: notion-project-setup ✔
  • No spaces: Notion Project Setup ✖
  • No underscores: notion_project_setup ✖
  • No capitals: NotionProjectSetup ✖ Inside that folder, create a file named exactly SKILL.md and open it in any text editor. Everything from here is what goes in that file.

创建技能

技能存放于 home 目录下的 .claude/skills 文件夹中,Claude Code 和桌面应用都会读取该目录。这是一个隐藏文件夹,很可能尚不存在,因此最快捷的方式是使用一条命令同时创建该目录和技能文件夹。

在 Mac 上,打开终端并运行:

mkdir -p ~/.claude/skills/your-skill-name

在 Windows 上,打开 PowerShell 并运行:

New-Item -ItemType Directory -Force -Path "$HOME\.claude\skills\your-skill-name"

文件夹名称并非装饰性。Claude 将其用作技能标识符,而格式化规则中最容易让人出错的是:

  • 使用 kebab-case:notion-project-setup ✔
  • 不能有空格:Notion Project Setup ✖
  • 不能有下划线:notion_project_setup ✖
  • 不能有大写:NotionProjectSetup ✖

在该文件夹内,创建一个名为 SKILL.md 的文件(名称必须完全一致),并用任何文本编辑器打开。从现在开始,所有内容都将写入该文件。

§ 4

Design it before you write it

The skills that work start with two decisions, made before you write a line of the file. Both feel skippable and neither is.

First, decide exactly when the skill should fire. Write down two or three real situations in the words a user would actually type:

  • "commit these changes"
  • "write a commit message for this diff"
  • "stage and commit" This isn't busywork. These phrases become the raw material for your description and your tests later, and a skill designed without them tends to be vague in precisely the way that keeps it from ever triggering.

Second, decide how you'll know it works. The bar that matters above all others is whether the skill loads on its own, without you naming it. If you have to invoke it manually every time, the skill technically runs but has failed at its actual job. Worth watching alongside that: whether it finishes the task without you correcting it midway, and whether it gives you the same shape of result across separate sessions.

先设计再动笔

有效的技能都始于两个决策,且必须在写任何文件内容之前做出。这两个决策看似可以跳过,但实际上都不能忽略。

首先,精确确定技能应在何时触发。写下两三个用户实际会输入的真实场景:

  • "commit these changes"
  • "write a commit message for this diff"
  • "stage and commit"

这不是无谓的工作。这些短语将成为你后续描述和测试的原始素材;一个没有经过这样设计的技能往往会在触发环节上变得模糊不清,导致它永远无法自动触发。

其次,确定你如何知道它是否生效。最重要的标准是:技能能否自动加载,而无需你主动提及。如果你每次都不得不手动调用,那么技能虽然在技术上运行了,但实际上没有完成它的本职工作。除此之外,还需关注:它是否能完成任务而无需你中途纠正,以及它在不同的会话中是否给出相同形式的结果。

§ 5

The description is what makes or breaks it

Of everything in the file, the description in the header does the most work, because it's the only part Claude reads when deciding whether to load the skill at all. Your instructions could be flawless and it wouldn't matter, since Claude never gets to them if the description doesn't match. This is where most skills that "don't work" actually fail.

A strong description answers two questions in one sentence: what the skill does, and when Claude should reach for it. That second half is the one people leave out.

Here's the difference:

# weak - says what it is, gives Claude nothing to match a request against
description: Helps with git commits.

# strong - names the moments it should fire on
description: Writes git commit messages in Conventional Commits format. Use when the user asks to commit changes, write a commit message, or stage and commit files.

The weak version tells Claude the skill exists but never connects it to anything you'd say. The strong version names the actual phrases, so when you type "commit these changes," Claude has something to match against. Name the words a user would really use, keep the whole thing under 1024 characters, and don't put < or > inside it.

When a skill won't trigger, the fix is almost always here. Add the phrasings you actually use. If you say "save my work" but the description only mentions "commit," Claude has no way to link the two. And if the opposite happens and the skill fires when it shouldn't, narrow the description or add a negative trigger:

description: Writes git commit messages in Conventional Commits format. Use when committing changes. Do not use for writing code comments or documentation.

There's a quick way to check your work before you rely on it. Ask Claude directly:

"When would you use the commit-messages skill?"

Claude will read your description back in its own words. If that doesn't line up with when you actually want the skill firing, you've found your problem, and it's in the description, not in the instructions underneath.

描述是成败关键

文件中所有内容里,头部的描述发挥的作用最大,因为它是 Claude 在决定是否加载技能时唯一读取的部分。你的指令可能完美无缺,但这无关紧要——如果描述不匹配,Claude 根本不会接触到指令。这就是大多数“不起作用”的技能实际失败的原因。

一个好的描述能用一句话回答两个问题:技能做什么,以及 Claude 应该在什么时候使用它。后一半正是人们经常遗漏的部分。

以下是区别:

# 弱 – 只说明是什么,没有给 Claude 匹配请求的依据
description: Helps with git commits.

# 强 – 明确指出技能应触发的时机
description: Writes git commit messages in Conventional Commits format. Use when the user asks to commit changes, write a commit message, or stage and commit files.

弱版本告诉 Claude 技能存在,但从未将其与你实际说的话联系起来。强版本则列出了具体的短语,因此当你输入“commit these changes”时,Claude 就有了匹配的对象。务必使用用户真正会用到的词语,整个描述保持在 1024 字符以内,并且不要在描述中使用 <>

当技能无法触发时,问题几乎总是出在这里。添加你实际使用的措辞。如果你说“save my work”但描述只提到了“commit”,Claude 就没办法把两者联系起来。反过来说,如果技能在不该触发时触发了,则要缩小描述范围或添加否定触发条件:

description: Writes git commit messages in Conventional Commits format. Use when committing changes. Do not use for writing code comments or documentation.

在依赖技能之前,有一种快速检查方法。直接问 Claude:

“你什么时候会使用 commit-messages 技能?”

Claude 会用它自己的话复述你的描述。如果这与你在实际中希望技能触发的时机不一致,那你就找到了问题所在,而且问题出在描述上,而非指令部分。

§ 6

Write instructions Claude actually follows

Below the header comes the body, in plain Markdown. This is where your real workflow lives, and two habits separate instructions Claude follows from ones it quietly drifts away from.

The first is being specific. Claude acts on concrete instructions and glosses over vague ones, so the more exact you are, the more reliably it behaves:

# Bad
Validate the commit before finalizing.

# Good
Run `python scripts/validate.py "<message>"`.
If it fails, fix these:
- Invalid type: use feat, fix, docs, refactor, test, chore
- Summary over 60 chars: shorten it

The second is ordering. Claude weights what it reads first, so a rule buried at the bottom of a long file is a rule that gets missed. Put anything that must not be broken at the top, under a heading that signals it:

## Important
- Summary line under 60 characters, always
- Present tense only: "add", not "added"

There's also a limit to what language can guarantee. Instructions are interpreted, which means Claude follows them well but not identically every time. When a check genuinely has to pass on every run, don't describe it in prose, move it into a script and have the instructions run it. Code does the same thing every time; a sentence doesn't. (That's what the scripts/ folder is for, covered next.)

A structure that holds up across most skills looks like this:

# Skill Name

## Important
Critical rules that must not be missed.

## Instructions
Step by step, specific and actionable.

## Examples
Concrete input and output. Claude copies examples more reliably than it follows rules.

Keep the file lean. The moment it starts growing past its core instructions is the moment to move the extra detail out, which is exactly what the optional folders are for.

编写 Claude 真正会遵循的指令

头部下方是正文,使用纯 Markdown 格式。这里是你真正工作流所在的地方,有两个习惯决定了 Claude 是会遵循指令还是会悄悄偏离。

第一个习惯是具体性。Claude 会执行具体指令,而忽略模糊指令,因此你写得越精确,Claude 的行为就越可靠:

# 糟糕
在最终确定前验证提交。

# 好
运行 `python scripts/validate.py "<message>"`。
如果失败,则修复以下问题:
- 类型无效:使用 feat, fix, docs, refactor, test, chore
- 摘要超过60字符:缩短它

第二个习惯是顺序。Claude 对首先读取到的内容赋予更高权重,因此埋藏在长文件底部的规则很容易被遗漏。将任何绝对不能违反的规则放在顶部,并用一个标题加以标识:

## 重要
- 摘要行始终不超过60个字符
- 只使用现在时:“add”,而不是“added”

语言能保证的也有极限。指令是被解释执行的,这意味着 Claude 能很好地遵循它们,但每次执行并不完全一致。当某项检查在每次运行时都必须通过时,不要用文字描述,而是将其移入脚本,并让指令来运行它。代码每次执行的结果都相同,而语句则不然。(这就是 scripts/ 文件夹的用途,下文会介绍。)

一种适用于大多数技能的结构如下:

# 技能名称

## 重要
绝不能遗漏的关键规则。

## 指令
分步骤、具体且可操作。

## 示例
具体的输入和输出。Claude 模仿示例比遵循规则更可靠。

保持文件精简。一旦它开始超出核心指令的范围,就是时候将额外细节移到外面了,这正是可选文件夹的用途所在。

§ 7

Scripts, references, assets

Everything so far produces a skill that gives Claude instructions. The three optional folders turn it into a skill that gives Claude tools, and this is where a skill does things a plain prompt can't.

scripts/ holds code Claude runs, for anything that has to be exact. Rather than trusting Claude to eyeball whether a commit is formatted right, you hand it a script that checks:

# scripts/validate.py
import sys
msg = sys.argv[1]
types = ("feat", "fix", "docs", "refactor", "test", "chore")

if msg.split(":")[0] not in types:
    print(f"Invalid type. Use: {', '.join(types)}")
elif len(msg.split("\n")[0]) > 60:
    print("Summary too long (over 60 chars)")
else:
    print("OK")

Then you tell Claude to use it in SKILL.md:

Before finalizing, run `python scripts/validate.py "<message>"`
and fix anything it flags.

Now the format rule is enforced by code that runs the same way every time, instead of depending on Claude to remember to check.

references/ holds documentation that loads only when needed. Say your commit conventions run to two pages of scopes, footers, and edge cases. Put all of that in SKILL.md and it loads on every single commit, even a one-liner. Move it into a reference file instead:

your-skill-name/
├── SKILL.md
└── references/
    └── conventions.md

And point to it from the main file:

For the full convention list, see references/conventions.md

Claude opens that file only when the task calls for it. This is the whole reason skills stay cheap to run: the heavy detail sits on disk until it's actually relevant, rather than riding along in context every time.

assets/ holds files the skill uses in its output rather than reads for guidance, like a template, a config file, or a logo. A commit skill doesn't need any, but a skill that generates reports might keep a template.md here and fill it in each time, so every report comes out with the same structure.

Taken together, these three folders are the difference between a skill that tells Claude how you work and one that hands Claude the exact tools to do the work your way.

脚本、引用与资产

到目前为止,我们构建的技能只是给 Claude 提供指令。这三个可选文件夹则将其转变为给 Claude 提供工具的技能,而这也正是技能能够完成普通提示所不能及的地方。

scripts/ 存放 Claude 要运行的代码,用于处理任何需要精确执行的事情。与其相信 Claude 能肉眼判断提交格式是否正确,不如给它一个执行检查的脚本:

# scripts/validate.py
import sys
msg = sys.argv[1]
types = ("feat", "fix", "docs", "refactor", "test", "chore")

if msg.split(":")[0] not in types:
    print(f"Invalid type. Use: {', '.join(types)}")
elif len(msg.split("\n")[0]) > 60:
    print("Summary too long (over 60 chars)")
else:
    print("OK")

然后你在 SKILL.md 中告诉 Claude 使用它:

在最终确定前,运行 `python scripts/validate.py "<message>"`
并修复它标记的任何问题。

现在格式规则由每次以相同方式运行的代码强制执行,不再依赖 Claude 记住去检查。

references/ 存放文档,仅在需要时加载。假设你的提交约定有两页纸,包括作用域、脚注和边界情况。如果把这些全部放在 SKILL.md 中,那么每次提交都会加载,哪怕只是一个单行提交。更好的做法是将其移到一个引用文件中:

your-skill-name/
├── SKILL.md
└── references/
    └── conventions.md

然后在主文件中指向它:

完整约定列表请参见 references/conventions.md

Claude 只会在该任务需要时打开该文件。这正是技能保持低成本运行的全部原因:沉重的细节存放在磁盘上,直到真正相关时才被加载,而不是每次都在上下文中携带。

assets/ 存放技能在输出中使用的文件,而非用于指导性阅读,例如模板、配置文件或徽标。提交技能不需要这些,但一个生成报告的技能可能会在此存放 template.md 并每次填充它,从而确保每份报告结构一致。

综合来看,这三个文件夹区分了“告诉 Claude 你如何工作”的技能和“交给 Claude 精确工具以你方式工作”的技能。

§ 8

The one thing to remember

A skill isn't teaching Claude a new ability. It already knows how to write a commit. What the skill does is make it do the job your way, every time, without you spelling it out again.

And when a skill doesn't work, the cause is almost never the instructions you labored over. It's the description. Claude decides whether to load the skill from that single line, before it ever reads the work underneath. Get the description right and everything below it finally gets used.

If this was useful, head to my profile and follow. I write about tech, AI, and systems that actually run.

Ciao,

@undefinedKi

需要记住的一件事

技能并不是教 Claude 新能力。它本来就知道如何写提交信息。技能的作用是让它每次都按你的方式完成工作,而无需你再次解释。

而当技能不工作时,原因几乎从不是你苦心编写的指令——而是描述。Claude 在读取下面的任何内容之前,就已经根据那行描述决定是否加载技能。把描述写对了,下面的一切才会被用到。

如果本文对你有帮助,请前往我的个人资料并关注。我撰写关于技术、AI 以及真正可运行的系统的内容。

再见,

@undefinedKi

Open source ↗