Writing Your First Skill: How to Write description's Trigger Words, How to Layer the Structure, How to Install It Into a Client
Start from something you already do well, and write your first skill that actually gets triggered: write description as an imperative sentence describing user intent, layer the body by steps and pitfalls, then install it into a client and confirm it really opens up.
今日目标
- 能把一件重复做的事收敛成一个 skill 的边界,并说清它不该管什么
- 能按祈使句、用户意图、明确边界三条写出一版可用的 description
- 能把 skill 装进客户端的项目级或用户级目录,并验证它被真正触发
昨天你把三个现成的 skill 拆开看了一遍,知道了「写得好的样子」。今天动手写一个自己的。今天的成败只有一个判据:你说一句自然的话,它自己被翻开了。 读完正文、做完实验之后,回到页面顶部把三条目标勾掉。
小白版讲解
选题:从你这周重复解释了三遍的那件事开始
档案柜里那本作业指导书不是凭空写出来的,是老员工被问烦了才写的。你的第一个 skill 也该这么来。
最好的选题信号是「你已经纠正过它」。 你让 AI 写一次提交信息,它写了 update code;你说不行,我们的格式是「类型加冒号加中文描述」;它改了;换个仓库、换个会话,它又写回 update code。这个来回你这周发生了三次——那三次纠正就是这个 skill 的全部内容,你不需要凭空构思什么。
官方在「最佳实践」那一页把这条讲得很直白:从大模型自己的通用知识里生成的 skill,写出来全是「妥善处理错误」「遵循认证最佳实践」这种正确的废话。真正有价值的 skill 来自具体经验——你们项目的约定、你们踩过的边界、你在对话里纠正过的每一句。所以选题时优先看这三处:
第一,你重复纠正过的。这是最直接的来源,纠正的内容原样搬进去就行。
第二,已经写在文档里但没人看的。团队的提交规范、接口约定、报表填法。它们通常躺在一份没人翻的文档里,做成 skill 之后是模型主动来翻,不是人主动去找。
第三,你从提交历史里能看出来的。哪些改动反复以同一种形式出现,哪些评审意见反复被提。这类材料的密度很高,因为它记录的是「实际改了什么」,不是「本来打算怎么做」。
选好之后立刻做一件事:写下它不管什么。 一个「写提交信息」的 skill 不管代码写得对不对,不管这次该不该提交,不管分支该叫什么。这条边界现在写下来,一会儿要原样搬进 description 的末尾。它是新手最容易漏、也最容易吃亏的一步——第三天讲反模式时你会看到,包山包海是四种反模式里最常见的那种。
description 是唯一的触发面
现在写今天最重要的一行。回忆一下昨天讲的三阶段:会话启动时,模型只看得到 name 和 description。正文写得再漂亮,只要这一行没能让模型认出「该翻开我了」,整个 skill 等于不存在。
官方给了四条写法原则,逐条落到你手上:
第一,用祈使句,写给模型下指令。 写「当用户要提交代码时使用」,不要写「这个技能可以帮助生成提交信息」。模型在做的是一个决策——用还是不用,你要直接告诉它什么时候用。「This skill does…」这种自我介绍式的句子对决策毫无帮助。
第二,写用户意图,不写内部实现。 模型拿来匹配的是用户刚才说的那句话,不是你的实现细节。「按 Conventional Commits 规范解析并生成」是实现,「当用户要提交代码、写提交信息、或者问这次改动该怎么描述时」才是意图。
第三,宁可写得凶一点。 官方原话是「err on the side of being pushy」:把适用场景显式列出来,包括用户没有直接点名这个领域的说法。用户很少会说「帮我按规范写 commit message」,他更可能说「这些改动我要提交了」。把这类口语说法也写进去。
第四,简短。 几句话到一小段就够。上限是 1024 个字符,但这一行每次会话都要付一遍——昨天算过,二十个 skill 都写满上限,光目录就要八千 token。
在这四条之上,再补一条昨天实验里你自己总结出来的:末尾加一句边界。 前半句管「什么时候出现」,末尾这半句管「什么时候闭嘴」。
把这五条合起来,一个提交信息 skill 的 description 长这样:
---
name: commit-message
description: 按团队规范写 Git 提交信息。当用户要提交代码、写 commit message、整理暂存区的改动,或者问「这次改动该怎么描述」时使用,即使他没有明确提到「规范」或「格式」。只负责生成提交信息文本,不负责决定该不该提交、也不执行 git 命令。
---逐段对照一下:第一句是做什么(祈使、简短);中间一长串是什么时候用,四种说法从正式到口语铺开,最后那句「即使他没有明确提到规范或格式」就是官方说的「凶一点」;末尾是边界,两个「不负责」把最容易撞车的相邻能力挡在外面。
正文分层:步骤、示例、坑
description 决定它会不会被翻开,正文决定翻开之后有没有用。官方推荐的结构是三段:分步骤的指令、输入输出示例、常见边界情况。实际写的时候按这个顺序展开:
工作流写成有编号的步骤。 步骤之间有依赖、顺序不能乱的任务,编号本身就是一种约束。每一步都要是一个可以做完的动作,不要写「妥善处理各种情况」这种没有落点的句子。
输出格式给模板,不要用文字描述。 这一条是官方特别强调的:模型对着一个具体结构做模式匹配,比读一段「输出应该包含标题、要点和建议三部分」可靠得多。模板短就直接写在正文里,长或者只在特定情况下用就放进 assets/。
坑单独成段,而且必须留在正文里。 官方把这一段叫 gotchas,并且指出它往往是整个 skill 里价值最高的部分——因为它是那些「合理推测会推错」的事实。「这个表用的是软删除,查询必须带上未删除条件」是坑;「注意处理异常」不是坑,那是废话。写坑的判据只有一句:如果不写这一条,模型会不会做错? 不会就删掉。
坑为什么不能挪到 references/ 里?因为模型得先知道有坑,才会去查坑。放进引用文件,就要求模型在还没撞上的时候预判自己会撞上,这不成立。
还有一条贯穿始终的原则:只写模型不知道的。不用解释什么是 Git、什么是提交,直接跳到「我们这儿的类型只有六个,多一个都不行」。判据是一句自问:不写这一条,模型会不会做错? 不会就是废话,删掉给别的内容腾位置。
装进客户端
写完的 skill 要放到客户端能扫到的地方才算装上。位置分两个维度:作用域(这个项目还是所有项目)和目录约定(跨客户端通用的还是某一家自己的)。
项目根/
├── .agents/skills/commit-message/SKILL.md # 项目级 · 跨客户端约定
└── .claude/skills/commit-message/SKILL.md # 项目级 · Claude Code 自有
~/
├── .agents/skills/commit-message/SKILL.md # 用户级 · 跨客户端约定
└── .claude/skills/commit-message/SKILL.md # 用户级 · Claude Code 自有.agents/skills/ 是社区形成的跨客户端约定,放在这里的 skill 能被多家客户端同时看到;各家自有的目录(Claude Code 是 .claude/skills/)则是它自己的原生位置。规范本身不规定 skill 目录放哪儿,它只定义文件夹里面长什么样——这也是为什么会同时存在这两套路径。想让一个 skill 被多个工具共用就放 .agents/skills/,只给一家用就放它自己的目录。
作用域的选择很好判断:只对这个仓库成立的放项目级,跟着仓库走,团队里每个人拉下代码就都有了;跨项目的个人习惯放用户级。
两个 skill 撞名时的优先级,这里有个必须自己实测的分歧。跨客户端的通行约定是项目级压过用户级——离手头这份代码越近的越具体,理应赢。但具体客户端可以有自己的层级,比如 Claude Code 的文档给出的顺序是企业级、个人级、项目级由高到低,个人级是压过项目级的,理由也说得通:不想让一个仓库带进来的 skill 覆盖掉你自己配的同名 skill。两种设计都合理,装之前查一下你用的那家是哪种,别凭直觉。撞名时客户端一般会打一条「某个 skill 被遮蔽」的警告,那条日志是排查这类问题的第一现场。
第一次没触发怎么排查
写完装好,你说了一句「帮我提交这些改动」,它没反应。按这三条查,覆盖绝大多数情况:
第一,它根本没被扫到。 先确认客户端到底认不认这个目录、SKILL.md 文件名有没有写错大小写、name 是不是等于父目录名。多数客户端有一个列出可用 skill 的命令,列表里没有就是没扫到,跟 description 一点关系都没有。这一步永远要先做——排查顺序搞反,你会对着一行完全无辜的描述改半天。
第二,frontmatter 解析失败。 最常见的一种是 YAML 里的裸冒号:description: 当用户说:帮我提交时使用,中间那个冒号会让 YAML 解析器把这一行判成非法。有的客户端会兜底修复,有的直接跳过这个 skill。描述里要用冒号,就把整个值用引号包起来。
第三,description 没覆盖你说的那种说法。 这才是真正需要改描述的情况。判据是:你实际说的那句话里的关键词,在描述里能不能找到对应。找不到就补,但补的是「这类说法」而不是「这一句」——把你说的原话原样抄进去,就是在往过拟合的坑里走,第三天会专门讲怎么避免。
还有一种不算失败的情况值得知道:任务太简单,模型觉得自己就能干。 官方明确说了,客户端通常只在任务超出模型自身能力时才去翻 skill。一句「读一下这个文件」不会触发任何 skill,哪怕描述写得再匹配。
下面这段代码把前两条查错做成脚本:校验 name 的五条规则、description 的长度、以及 frontmatter 能不能被正常解析。装之前先跑一遍,能省掉大半的排查时间。
import { readFile } from 'node:fs/promises'
import { basename, dirname } from 'node:path'
const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/ // 覆盖了首尾不能是连字符、不能连续连字符两条
export async function validateSkill(skillMdPath: string): Promise<string[]> {
const raw = await readFile(skillMdPath, 'utf8')
const errors: string[] = []
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw)
if (!m) return ['没有找到 frontmatter,前两行必须是 --- 与字段']
const front = m[1]
const name = /^name:\s*(.+)$/m.exec(front)?.[1]?.trim()
const description = /^description:\s*(.+)$/m.exec(front)?.[1]?.trim()
if (!name) errors.push('缺少必填字段 name')
else {
if (name.length > 64) errors.push(`name 超过 64 个字符(当前 ${name.length})`)
if (!NAME_RE.test(name)) errors.push('name 只能是小写字母、数字与单个连字符,且首尾不能是连字符')
const dir = basename(dirname(skillMdPath))
if (name !== dir) errors.push(`name 与父目录名不一致:${name} 对 ${dir}`)
}
if (!description) errors.push('缺少必填字段 description,缺了它这个 skill 永远不会被触发')
else if (description.length > 1024) errors.push(`description 超过 1024 个字符(当前 ${description.length})`)
// 裸冒号是最常见的 YAML 陷阱:值里有冒号又没加引号,很多解析器会直接判非法
else if (/[::]/.test(description) && !/^["']/.test(description))
errors.push('description 里有冒号但没加引号,部分客户端会解析失败')
return errors
}import re
from pathlib import Path
NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$") # 覆盖了首尾不能是连字符、不能连续连字符两条
def validate_skill(skill_md: Path) -> list[str]:
raw = skill_md.read_text(encoding="utf-8")
errors: list[str] = []
m = re.match(r"^---\r?\n(.*?)\r?\n---", raw, re.S)
if not m:
return ["没有找到 frontmatter,前两行必须是 --- 与字段"]
front = m.group(1)
name_m = re.search(r"^name:\s*(.+)$", front, re.M)
desc_m = re.search(r"^description:\s*(.+)$", front, re.M)
name = name_m.group(1).strip() if name_m else None
description = desc_m.group(1).strip() if desc_m else None
if not name:
errors.append("缺少必填字段 name")
else:
if len(name) > 64:
errors.append(f"name 超过 64 个字符(当前 {len(name)})")
if not NAME_RE.match(name):
errors.append("name 只能是小写字母、数字与单个连字符,且首尾不能是连字符")
if name != skill_md.parent.name:
errors.append(f"name 与父目录名不一致:{name} 对 {skill_md.parent.name}")
if not description:
errors.append("缺少必填字段 description,缺了它这个 skill 永远不会被触发")
elif len(description) > 1024:
errors.append(f"description 超过 1024 个字符(当前 {len(description)})")
# 裸冒号是最常见的 YAML 陷阱:值里有冒号又没加引号,很多解析器会直接判非法
elif re.search(r"[::]", description) and not description.startswith(('"', "'")):
errors.append("description 里有冒号但没加引号,部分客户端会解析失败")
return errors预算意识:两条线
最后把两条数字线钉住,它们贯穿这门课剩下的五天。
第一条线:正文 500 行、5000 token。 这是规范给的建议上限,超了就该往 references/ 里搬。搬的时候记住昨天那条:要写清什么条件下读哪个文件,否则搬出去的内容等于丢了。
第二条线:description 1024 个字符。 这是硬上限,但真正该盯的不是这个数,是你所有 skill 的描述加起来。二十个都写满,光目录就八千 token,而这八千是每一轮都要付的。
这两条线的性质不一样,别搞混:500 行是「命中才付」的钱,1024 字符是「一直在付」的钱。 所以正文可以写得从容一点,描述必须抠。
一个实用的做法是把描述的总开销盯成一个指标。下面这段代码把某个目录下所有 skill 的描述长度汇总出来,超预算就告警——装进你自己的检查脚本里,跟昨天那个目录成本估算合起来用。
import { readdir, readFile } from 'node:fs/promises'
import { join } from 'node:path'
const BUDGET_CHARS = 4000 // 所有描述加起来的预算,按每 2.5 字符一个 token 算约 1600 token
export async function checkBudget(skillsDir: string): Promise<boolean> {
const dirs = (await readdir(skillsDir, { withFileTypes: true })).filter((e) => e.isDirectory())
let total = 0
for (const d of dirs) {
const raw = await readFile(join(skillsDir, d.name, 'SKILL.md'), 'utf8').catch(() => '')
const desc = /^description:\s*(.+)$/m.exec(raw)?.[1]?.trim() ?? ''
total += desc.length
if (desc.length > 300) console.warn(`${d.name} 的描述有 ${desc.length} 字符,考虑压到 300 以内`)
}
console.log(`${dirs.length} 个 skill 的描述共 ${total} 字符,预算 ${BUDGET_CHARS}`)
return total <= BUDGET_CHARS
}import re
from pathlib import Path
BUDGET_CHARS = 4000 # 所有描述加起来的预算,按每 2.5 字符一个 token 算约 1600 token
def check_budget(skills_dir: Path) -> bool:
dirs = sorted(p for p in skills_dir.iterdir() if p.is_dir())
total = 0
for d in dirs:
skill_md = d / "SKILL.md"
raw = skill_md.read_text(encoding="utf-8") if skill_md.exists() else ""
m = re.search(r"^description:\s*(.+)$", raw, re.M)
desc = m.group(1).strip() if m else ""
total += len(desc)
if len(desc) > 300:
print(f"警告:{d.name} 的描述有 {len(desc)} 字符,考虑压到 300 以内")
print(f"{len(dirs)} 个 skill 的描述共 {total} 字符,预算 {BUDGET_CHARS}")
return total <= BUDGET_CHARS源码导读
动手实验
今天有代码,但代码不是产出——产出是那个能被自然一句话触发的 skill。动手之前先把你团队的提交规范找出来放在旁边,没有成文规范就把最近二十条提交拉出来看看实际长什么样。卡住的时候回到「第一次没触发怎么排查」那一节,按顺序查,别跳步。
- 打开 solution 的 SKILL.md,先只看 frontmatter 猜它会在什么时候被触发,再往下读正文验证你猜得对不对。
- 在 starter 里补全 description,让它覆盖三种不同说法,同时不覆盖两种近似说法。
- 补全正文的工作流段与坑段,把团队规范里最容易被违反的两条写成坑。
- 把整个目录复制到客户端的项目级 skill 目录,用三句不同说法各触发一次,看到它确实被翻开并按你的模板输出。
- 对照规范核对 name 命名、description 长度与正文行数,把三个数字填进记录表。
面试题
今天 3 道题在下方题库区,侧重 description 的写法与触发机制、skill 正文的分层、以及安装位置与作用域。展开后先看「分析过程」再看要点——照着推导练,比背要点管用。标注「国内高频 / 海外高频」方便按目标市场取舍。
检查清单与明日预告
- 能把一件重复做的事收敛成一个 skill 的边界,并说清它不该管什么
- 能按祈使句、用户意图、明确边界三条写出一版可用的 description
- 能把 skill 装进客户端的项目级或用户级目录,并验证它被真正触发
- 能按顺序说出没触发时的三步排查,并解释为什么第一步是「有没有被扫到」
- 实验的 5 条验收标准全部通过
- 3 道面试题不看要点也能答出至少 2 道
明天(D3)我们把「凭手感写」升级成「有方法地写」:三条选题判据、两种成熟形态、四种反模式,以及最重要的一件事——用一组正负例把 description 的触发率真的量出来。今天你是靠自己试三句话来判断好不好,这个办法在只有一个 skill 时够用,装到第十个就不行了。明天给你一把能重复使用的尺子。
Interview questions
What goes wrong when a skill description is too broad, and what goes wrong when it is too narrow? How do you find the middle?skill 的 description 写得太泛会怎样?太窄又会怎样?你怎么找到中间那个点?
Common in ChinaCommon overseasIntermediate#agent-skills#skill-descriptionHow to reason about it · think before answering
- The hinge word is cost. Saying too broad misfires and too narrow never fires just restates the question; the interviewer wants to know what a misfire actually costs.
- Give three layers of cost for over-broad descriptions: the body wastes context, its instructions interfere with the current task, and once the model has committed to one skill it is less likely to reach for the right one. One over-broad skill degrades the whole library.
- For too narrow: it only fires when the user phrases things exactly as you imagined, and real users never do. Such a skill is usually not bad, it is simply never exercised, so you never learn that it is bad.
- Give the middle as a procedure, not a feeling: cover phrasings rather than keywords, add a boundary clause that excludes adjacent capabilities, then measure trigger rate against positives and near-miss negatives and revise from the data.
- Add the often-missed fact that agents typically only consult skills for tasks beyond what they handle alone, so a trivially easy task will not trigger no matter how well the description matches.
- Expected follow-up: how do you avoid overfitting when revising? Never paste the failing query verbatim; generalize to the category it represents, and hold out a validation split.
分析过程 · 先想清楚再作答
- 这题的题眼在「代价」两个字。只说「太泛会误触发、太窄会不触发」是把题目复述了一遍,面试官等的是后面那句:误触发到底损失了什么。
- 先说太泛的代价,而且要说满三层:这个 skill 的正文白占了上下文位置;它的指令会干扰当前任务;更麻烦的是模型一旦选定了一个 skill,就更不容易再去选真正对的那个。**一个太泛的 skill 会拖累整个技能库**,这一句是拿分点。
- 再说太窄的代价:它只在用户按你预想的说法提问时才触发,而真实用户几乎不会那样说话。太窄的 skill 通常不是不好用,是根本没被用过,所以你连它不好用都不知道。
- 找中间点的方法要给成一套动作而不是感觉:写覆盖多种说法而不是多个关键词,末尾补一句边界排除相邻能力,然后用一组正例加近似负例把触发率量出来,按结果改描述。
- 补一个容易被忽略的事实:有些任务简单到模型觉得自己就能干,这时候描述写得再匹配也不会触发。判断描述好不好之前,先确认这个任务值不值得一个 skill。
- 可预期的追问是「改描述时怎么避免过拟合」。答案是不要把失败查询的原话抄进描述,要归纳出它代表的那一类说法,并留一部分查询不参与优化、只用来验证。
Key points
- Three costs of over-broad: wasted context, interference with the current task, and crowding out the correct skill.
- Over-narrow means it never fires, which hides the problem rather than surfacing it.
- Cover phrasings rather than keywords, and add a closing boundary clause that excludes adjacent capabilities.
- Measure trigger rate with positives and near-miss negatives, then revise from the data.
- A task simple enough for the model alone will not trigger any skill; that is not a description problem.
答题要点
- 太泛的三层代价:占上下文、干扰当前任务、挤掉真正该用的那个 skill。
- 太窄的代价是根本没被触发过,问题被掩盖,你连它好不好用都测不出来。
- 写法上覆盖「多种说法」而不是「多个关键词」,末尾补一句边界排除相邻能力。
- 用正例加近似负例量出触发率,按数据改描述,不靠手感。
- 任务本身太简单时不会触发任何 skill,这不是描述的问题。
What belongs in a skill body and what does not, and why must the gotchas stay in SKILL.md rather than move to a reference file?skill 的正文应该写什么、不应该写什么?为什么「坑」那一段必须留在 SKILL.md 里而不是挪到引用文件?
Common in ChinaCommon overseasIntermediate#agent-skills#skill-authoringHow to reason about it · think before answering
- This separates people who have written skills from people who have read about them. The untested answer is write clear steps; the tested answer starts with a test.
- The test is one sentence: would the model get this wrong without this line? If not, cut it. Explaining what a PDF is only dilutes attention.
- Three things belong: project-specific conventions, non-obvious edge cases, and which tool or API to use. All three are absent from the model's general knowledge.
- Call out output format specifically: a concrete template beats prose, because models pattern-match against structures far better than they parse a described format.
- Gotchas cannot move because of ordering: the model must know a trap exists before it will look it up. Putting them in a reference file assumes it can predict a collision it has not hit yet.
- Expected follow-up: what does belong in references? Long material whose need has a clear trigger condition, and the body must state that condition, such as read the error-code file when the API returns a non-200.
分析过程 · 先想清楚再作答
- 这题考的是你有没有真写过 skill。没写过的人会答「写清楚步骤」,写过的人会先给一条判据。
- 判据只有一句:**不写这一条,模型会不会做错?** 不会就是废话,删掉。解释什么是 PDF、什么是数据库迁移,模型本来就知道,写进去纯粹在稀释注意力。
- 该写的三类是:项目特有的约定、非显然的边界情况、以及指定用哪个工具或接口。这三类的共同点是模型的通用知识里没有。
- 输出格式那一段要单独强调:给模板比用文字描述可靠,因为模型对具体结构做模式匹配的能力远强于读一段散文式的格式说明。
- 「坑」为什么不能挪走,答案是一个先后顺序问题:**模型得先知道有坑,才会去查坑**。放进引用文件就要求它在还没撞上的时候预判自己会撞上,这个前提不成立。引用文件适合放「我知道会用到,只是现在还不需要」的材料。
- 可预期的追问是「那什么该挪进 references」。答案是长、且用不用得上有明确判断条件的材料,并且正文里必须写出那个条件,比如「接口返回非 200 时读错误码文件」。
Key points
- The test: would the model get this wrong without the line? If not, delete it.
- Include project conventions, non-obvious edge cases, and the specific tool or API to use.
- Give a template for output format instead of describing it in prose.
- Gotchas stay in the body because the model must know a trap exists before looking it up.
- References hold long material, and the body must state the condition for loading each one.
答题要点
- 判据是「不写这一条模型会不会做错」,不会就删。
- 该写:项目特有约定、非显然的边界、指定的工具与接口。
- 输出格式给模板,不要用文字描述格式。
- 坑必须留在正文,因为模型要先知道有坑才会去查坑。
- 引用文件放长材料,且正文必须写出「什么条件下读它」。
How should a client resolve a name collision between a project-level and a user-level skill, and why do clients differ here?项目级和用户级的 skill 同名时该怎么处理?为什么各家客户端在这一点上会有不同的选择?
Common in ChinaCommon overseasDeep dive#agent-skills#client-integrationHow to reason about it · think before answering
- It looks like trivia but it tests whether you have actually installed skills. There is a real disagreement here, and naming it marks you as someone who has hit it.
- Start with the common convention: project-level overrides user-level, because configuration closer to the code at hand is more specific. Within one scope, first-found or last-found are both acceptable as long as you pick one and stay consistent.
- Then the divergence: Claude Code documents enterprise, then personal, then project, so personal wins over project, so that a cloned repository cannot silently shadow the skill you configured yourself.
- Explain the trade-off, which is where the marks are: project-first buys automatic team conventions, user-first buys protection from hijacking by an unfamiliar repository.
- Bring in trust: project-level skills can arrive with a freshly cloned repository and inject instructions into your session, which is why most clients gate them behind a folder-trust check.
- Expected follow-up: how do you notice a collision? Clients normally log a shadowed-skill warning, and implementers should record diagnostics rather than dropping the skill silently.
分析过程 · 先想清楚再作答
- 这题看起来是细节题,实际在考你有没有真的装过、有没有踩过。标准答案背后有一个分歧,能说出分歧的人一眼就是实操过的。
- 先给通行约定:跨客户端的普遍做法是**项目级压过用户级**,理由是离手头这份代码越近的配置越具体,理应赢。同一作用域内两个目录撞名,先找到还是后找到都行,但必须固定一种并保持一致。
- 再给分歧:具体客户端可以有自己的层级。Claude Code 的文档给出的顺序是企业级、个人级、项目级由高到低——**个人级压过项目级**,理由是不希望一个仓库带进来的 skill 悄悄覆盖掉你自己配的同名 skill。
- 把两种设计的取舍讲清楚,这是本题真正的区分度:项目优先换来的是「团队约定自动生效」,用户优先换来的是「不被陌生仓库劫持」。它们各自解决的是不同的风险。
- 顺势带出信任问题:项目级 skill 可能来自一个你刚 clone 的陌生仓库,等于让它往你的会话里注入指令。所以多数客户端把项目级加载挂在「信任这个目录」的开关后面。
- 可预期的追问是「撞名了怎么发现」。答案是客户端一般会打一条被遮蔽的警告,那条日志是排查的第一现场;实现方也应该在这种时候记录诊断信息而不是静默丢弃。
Key points
- The common convention is project over user, with a fixed, consistent rule inside a single scope.
- Clients may differ: Claude Code documents enterprise, then personal, then project.
- Project-first gives automatic team conventions; user-first prevents hijacking by an unfamiliar repository.
- Project-level skills can come from untrusted repositories, so gate them behind a folder trust check.
- Log a warning and record diagnostics on a collision instead of silently shadowing.
答题要点
- 通行约定是项目级压过用户级,同作用域内固定一种顺序并保持一致。
- 具体客户端可以不同,比如 Claude Code 的顺序是企业级、个人级、项目级。
- 项目优先换来团队约定自动生效,用户优先换来不被陌生仓库劫持。
- 项目级 skill 可能来自不可信仓库,加载应挂在目录信任检查后面。
- 撞名要打警告并记录诊断,不能静默遮蔽。
Comments
Sign in to join the discussion
No comments yet — be the first.