WorkBuddy_Skill技能系统入门12_用户级_vs_项目级技能
用户级 vs 项目级技能
摘要
在使用 Claude Code、Cursor、GitHub Copilot 等 AI 编程助手时,很多开发者会遇到一个令人困惑的现象:明明昨天配置好的技能(Skill)或自定义指令,今天换了一个项目目录就"消失"了。这并不是工具出了 Bug,而是技能的存放位置在起作用。本文将系统讲解"用户级(User-level)"与"项目级(Project-level)“技能的区别,剖析为什么换项目后技能会"找不到”,并给出可复用的目录结构、配置示例和排障方法。读完本文,你将能够准确判断一个技能应该放在哪里,以及如何让它在正确的范围内生效。
引言
AI 编程助手的能力边界,很大程度上取决于你给它的"技能"——也就是那些告诉它"该怎么做事"的指令、模板、脚本和知识片段。这些技能不是凭空存在的,它们必须存放在文件系统的某个位置,工具在运行时才会去加载。
问题就出在这里:不同的存放位置,对应着不同的作用域(scope)。放在用户主目录下的技能,对所有项目生效;放在项目目录下的技能,只对当前项目生效。当你在 A 项目里精心配置了一套技能,切换到 B 项目时却发现"用不了",本质上是因为那套技能被放在了 A 项目的项目级目录里,而 B 项目根本不会去读取它。
这个现象在团队协作、多仓库开发、临时脚本目录等场景中尤为常见。理解作用域规则,是高效使用 AI 工具的基本功。下面我们逐层拆解。
一、什么是"技能":先统一概念
在深入作用域之前,先明确"技能"指什么。不同工具的叫法不同,但本质相似:
| 工具 | 技能的表现形式 | 典型目录/文件 |
|---|---|---|
| Claude Code | Skills / 自定义命令 | .claude/skills/、.claude/commands/ |
| Cursor | Rules | .cursor/rules/ |
| GitHub Copilot | 自定义指令 | .github/copilot-instructions.md |
| 通用约定 | 项目说明 | AGENTS.md、CLAUDE.md |
无论叫法如何,一个"技能"通常由三部分组成:
- 触发描述:告诉模型"什么时候用这个技能"(如
description字段)。 - 执行内容:具体的指令、模板或脚本。
- 元数据:名称、版本、依赖等。
下面以一个典型的 Skill 目录结构为例(以 Claude Code 风格为例,其他工具思路一致):
.claude/
└── skills/
└── code-review/
├── SKILL.md # 技能主文件,包含元数据与指令
└── scripts/
└── lint.sh # 技能可调用的辅助脚本
SKILL.md 的头部通常包含 YAML 元数据:
---
name: code-review
description: 当用户要求审查代码或提交 PR 前,使用此技能进行结构化代码审查。
---
# 代码审查技能
## 步骤
1. 检查是否有未处理的 TODO 注释。
2. 检查函数是否超过 50 行。
3. 运行 `scripts/lint.sh` 并汇总结果。
关键点在于:这个 .claude/ 目录放在哪里,决定了技能的作用域。这就是下一节的主题。
二、两种作用域:用户级与项目级
技能的存放位置只有两大类,对应两种作用域。
2.1 用户级(User-level)
用户级技能存放在用户主目录下,与具体项目无关。典型路径:
~/.claude/skills/ # Claude Code 用户级技能
~/.cursor/rules/ # Cursor 用户级规则
~/.config/ai-tools/ # 某些工具的 XDG 风格配置目录
特点:
- 全局生效:在任何项目目录下启动工具,都会加载这些技能。
- 个人偏好:适合放"我个人的编码习惯"“我常用的代码模板”“我的提交信息格式”。
- 不随仓库分发:不会进入 Git,团队成员看不到。
2.2 项目级(Project-level)
项目级技能存放在项目根目录下,随项目一起管理。典型路径:
<project-root>/.claude/skills/ # Claude Code 项目级技能
<project-root>/.cursor/rules/ # Cursor 项目级规则
<project-root>/.github/copilot-instructions.md
特点:
- 仅当前项目生效:离开这个目录,技能就不存在。
- 团队共享:通常会提交到 Git,让所有协作者使用同一套规范。
- 项目定制:适合放"本项目的架构约定"“本项目的测试命令”“本项目的目录规范”。
2.3 一张对比表
| 维度 | 用户级 | 项目级 |
|---|---|---|
| 存放位置 | ~/.claude/ 等主目录 | <project>/.claude/ 等 |
| 作用范围 | 所有项目 | 仅当前项目 |
| 是否进 Git | 否 | 通常是 |
| 团队可见 | 否 | 是 |
| 典型用途 | 个人习惯、通用模板 | 项目规范、团队约定 |
| 换项目后 | 仍可用 | 找不到 |
最后一行就是本文要解释的核心现象。
三、为什么换项目后"找不到"技能
理解了作用域,答案其实很直接:项目级技能的作用域边界就是项目根目录。工具的加载逻辑大致如下(伪代码):
def load_skills(project_root: str) -> list[Skill]:
skills = []
# 1. 先加载用户级技能(全局,永远加载)
user_dir = Path.home() / ".claude" / "skills"
if user_dir.exists():
skills.extend(parse_skills(user_dir))
# 2. 再加载项目级技能(只在当前项目根目录下查找)
project_dir = Path(project_root) / ".claude" / "skills"
if project_dir.exists():
skills.extend(parse_skills(project_dir))
return skills
注意第 2 步:project_dir 是基于当前工作目录(或工具识别到的项目根)计算的。当你 cd 到另一个项目,project_root 变了,project_dir 自然指向一个不存在或为空的新路径,于是项目级技能"消失"了。
常见的误判场景:
- 在子目录里启动工具:某些工具只向上查找有限层级,若你在很深的子目录启动,可能识别不到项目根。
- 复制了代码但没复制配置:
git clone时如果.claude/被.gitignore忽略,新克隆的仓库就没有项目级技能。 - 多仓库/微服务:技能配在仓库 A,你在仓库 B 里工作,当然找不到。
- 临时目录:在
/tmp或临时脚本目录里调试,那里没有项目级配置。
下面用一段可运行的脚本演示这个加载逻辑,方便你验证自己的环境:
#!/usr/bin/env python3
"""演示用户级 vs 项目级技能的加载差异。"""
from pathlib import Path
def discover_skills(project_root: Path) -> dict:
"""模拟工具的技能发现逻辑,返回按作用域分类的技能列表。"""
result = {"user": [], "project": []}
# 用户级:固定从主目录读取
user_skills_dir = Path.home() / ".claude" / "skills"
if user_skills_dir.is_dir():
result["user"] = [p.name for p in user_skills_dir.iterdir() if p.is_dir()]
# 项目级:从传入的项目根读取
project_skills_dir = project_root / ".claude" / "skills"
if project_skills_dir.is_dir():
result["project"] = [p.name for p in project_skills_dir.iterdir() if p.is_dir()]
return result
if __name__ == "__main__":
# 场景一:在项目 A 中工作
project_a = Path("/path/to/project-a")
print("项目 A 加载结果:", discover_skills(project_a))
# 场景二:切换到项目 B(项目级技能目录不存在)
project_b = Path("/path/to/project-b")
print("项目 B 加载结果:", discover_skills(project_b))
# 输出中 project 为空列表 —— 这就是"找不到"的真相
运行这段脚本,你会直观看到:user 列表在两个项目里一致,而 project 列表只在配置过的项目里非空。所谓"技能消失",不过是 project 列表变空了而已。
四、如何选择:该放用户级还是项目级
选择的标准可以用一句话概括:“这条技能是不是只对这个项目有意义?”
4.1 放用户级的信号
- 与具体代码库无关,比如"用中文回复"“提交信息用 Conventional Commits”。
- 你个人在所有项目里都想要的习惯。
- 涉及个人凭据、本地路径、私有脚本,不适合进仓库。
4.2 放项目级的信号
- 描述本项目架构,比如"本仓库使用 DDD 分层,domain 层不得依赖 infrastructure"。
- 项目专属命令,比如"测试用
make test-unit,不要用pytest全量跑"。 - 团队需要统一的代码审查清单、PR 模板。
- 需要随代码一起版本化、评审、回滚。
4.3 一个混合示例
实际项目中,两者往往并存。下面是一个推荐的项目级目录结构,配合用户级通用技能:
# 用户级(~/.claude/skills/)
~/.claude/skills/
├── commit-style/ # 个人提交信息风格,所有项目通用
└── explain-code/ # 个人偏好的代码讲解模板
# 项目级(<project>/.claude/skills/)
<project>/.claude/skills/
├── api-convention/ # 本项目 API 命名与返回格式约定
└── db-migration/ # 本项目数据库迁移流程
这样,你在任何项目里都有 commit-style 可用,而 api-convention 只在本项目生效——职责清晰,互不干扰。
五、排障实战:技能"消失"了怎么办
遇到"换项目后找不到技能",按以下清单逐项排查。
5.1 确认当前项目根
很多工具以第一个包含特定标志文件的目录为项目根(如 .git、package.json、pyproject.toml)。在子目录启动时可能识别错。用命令确认:
# 确认当前是否在 git 仓库内,以及仓库根在哪
git rev-parse --show-toplevel 2>/dev/null || echo "当前不在 git 仓库中"
如果输出为空,说明你不在任何仓库里,项目级技能自然无处加载。
5.2 确认技能目录是否存在
# 检查项目级技能目录
ls -la "$(git rev-parse --show-toplevel)/.claude/skills" 2>/dev/null \
|| echo "项目级技能目录不存在"
# 检查用户级技能目录
ls -la ~/.claude/skills 2>/dev/null \
|| echo "用户级技能目录不存在"
5.3 确认没有被 .gitignore 吞掉
如果技能需要团队共享,务必确认它没有被忽略:
# 检查 .claude 是否被忽略
git check-ignore -v .claude/skills/ 2>/dev/null \
&& echo "警告:.claude 被 .gitignore 忽略了" \
|| echo ".claude 未被忽略,可正常提交"
5.4 用一个自检脚本一次性排查
#!/usr/bin/env bash
# check-skills.sh —— 一键排查技能加载问题
set -u
echo "== 1. 项目根 =="
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '')"
if [[ -z "$ROOT" ]]; then
echo " ✗ 不在 git 仓库中,项目级技能无法加载"
ROOT="$(pwd)"
else
echo " ✓ 项目根: $ROOT"
fi
echo "== 2. 项目级技能 =="
if [[ -d "$ROOT/.claude/skills" ]]; then
echo " ✓ 存在: $ROOT/.claude/skills"
ls "$ROOT/.claude/skills"
else
echo " ✗ 不存在: $ROOT/.claude/skills"
fi
echo "== 3. 用户级技能 =="
if [[ -d "$HOME/.claude/skills" ]]; then
echo " ✓ 存在: $HOME/.claude/skills"
ls "$HOME/.claude/skills"
else
echo " ✗ 不存在: $HOME/.claude/skills"
fi
echo "== 4. 忽略规则 =="
git check-ignore -v "$ROOT/.claude/skills" 2>/dev/null \
&& echo " ! .claude 被忽略,团队无法共享" \
|| echo " ✓ .claude 未被忽略"
把这段脚本保存为 check-skills.sh,chmod +x 后运行,绝大多数"找不到技能"的问题都能在 10 秒内定位。
5.5 迁移决策
排查后如果发现"技能放错了位置",迁移很简单:
# 场景:把原本误放在项目里的通用技能搬到用户级
mkdir -p ~/.claude/skills
mv ./.claude/skills/commit-style ~/.claude/skills/
# 场景:把本应共享的项目技能从用户级搬回项目
mkdir -p ./.claude/skills
mv ~/.claude/skills/api-convention ./.claude/skills/
迁移后记得验证:在另一个项目目录里启动工具,确认用户级技能仍在;在本项目里确认项目级技能已加载。
六、最佳实践小结
将上面的内容沉淀为几条可执行的建议:
- 默认放项目级:凡是与代码库相关的规范,优先放项目级并提交到 Git,保证团队一致。
- 个人偏好放用户级:与项目无关的习惯放主目录,避免污染团队仓库。
- 命名加前缀:项目级技能可用
proj-前缀、用户级用my-前缀,便于一眼区分来源。 - 写清触发描述:
description字段决定模型何时调用技能,写不清等于没写。 - 把自检脚本纳入 CI:在 CI 里跑一次目录存在性检查,避免新成员克隆后"技能缺失"。
- 文档化作用域:在
README或AGENTS.md里说明哪些技能是全局的、哪些是项目专属的。
总结
"换项目后找不到技能"不是玄学,而是作用域规则在正常运作。本文的核心结论可以浓缩为三点:
- 技能有作用域:用户级技能存放在主目录,全局生效;项目级技能存放在项目根目录,仅当前项目生效。
- "找不到"的本质:工具按当前项目根去查找项目级目录,换项目后路径变了,项目级技能自然为空,而用户级技能不受影响。
- 选择靠一句话:与项目无关放用户级,与项目相关放项目级并进 Git。
理解了这一点,你不仅能解决"技能消失"的困惑,还能主动设计出清晰的技能分层:通用的个人习惯随身携带,项目的团队规范随仓库流转。下次再遇到"技能不见了",先跑一遍第五节的自检脚本,答案往往就在目录结构里。
更多推荐


所有评论(0)