Featured image of post 给 Claude Code 配置 Python UV 的 Hook 和 Skill

给 Claude Code 配置 Python UV 的 Hook 和 Skill

踩坑记录:用 Hook 强制 Claude Code 使用 uv 管理 Python 依赖

语速

这个仓库的代码稍后上传。

项目里用 uv 管理 Python 依赖,但 Claude Code 总是习惯性地用 pythonpip install。试了下用 Skill 和 Hook 来强制规范,踩了不少坑。

目标

  • 创建 Skill:告诉 Claude 项目用 uv
  • 创建 Hook:拦截 python/pip 命令
  • 验证生效

踩坑过程

第一版:Skill 文件结构错了(提交 8a05759)

1
2
❌ .claude/skills/python-uv.md
✅ .claude/skills/python-uv/SKILL.md

cc 生成了错误的 frontmatter。因此,Frontmatter 要改:

1
2
3
4
5
6
7
8
9
---
# 错误
description: Python dependency and execution management using uv
location: project

# 正确
name: python-uv
description: Python dependency and execution management using uv. Use when adding Python packages, running Python scripts, or managing Python dependencies. Enforces uv instead of pip/python commands.
---

关键点:

  • 文件名必须是 SKILL.md,放在对应目录下
  • frontmatter 需要 name 字段
  • description 要详细,帮助 Claude 识别何时触发

第二版:Hook 只警告不阻止(提交 d250c3b)

最开始用 Bash 写 Hook,只显示警告但不阻止执行。还尝试配置 environment.PATH,结果不生效。

第三版:Hook 退出码用错了(提交 d3790a4)

改用 exit 1 想阻止命令,结果还是不管用。

正确的退出码

  • exit 0: 允许执行
  • exit 1: Hook 失败,但不阻止工具
  • exit 2: 真正阻止工具执行 ✅

第四版:修正 Skill 格式(提交 2595b68)

发现文件结构不对,改成正确的 skills/xxx/SKILL.md 格式。

第五版:用 Python 重写 Hook(提交 dcc726d)

Bash 解析 JSON 太脆弱,最终改用 Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env python3
"""
Hook to block python/python3 commands and enforce uv usage.
"""
import json
import sys
import re

try:
    # 正确解析 JSON 输入
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

# 获取工具名称和命令
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

# 只处理 Bash 工具
if tool_name != "Bash" or not command:
    sys.exit(0)

# 检查是否使用 python/python3
if re.search(r'\bpython3?\b', command):
    # 白名单:允许版本检查等
    if re.search(r'(--version|--help|which python)', command):
        sys.exit(0)

    # 阻止命令
    error_msg = (
        f"\n❌ BLOCKED: This project requires using 'uv'\n\n"
        f"Original command:\n  {command}\n\n"
        f"Suggested replacement:\n  {suggested}\n"
    )

    print(error_msg, file=sys.stderr)
    sys.exit(2)  # 使用 exit 2 阻止工具调用

# 允许其他命令
sys.exit(0)

配置文件(.claude/settings.json):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",  // 简化的 matcher 格式
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-bash"
          }
        ]
      }
    ]
  }
  // 移除无效的 environment.PATH 配置
}

注意:

  • matcher 直接写工具名就行,不用搞表达式
  • $CLAUDE_PROJECT_DIR 引用项目路径
  • environment.PATH 配置在 Hook 里不生效,别浪费时间

最终文件结构

1
2
3
4
5
6
7
.claude/
├── skills/
│   └── python-uv/
│       └── SKILL.md
├── hooks/
│   └── pre-bash          # Python 脚本
└── settings.json

测试

✅ 阻止普通命令:

1
2
$ python test.py
❌ BLOCKED: Use 'uv run' instead

✅ 允许版本检查:

1
2
$ python --version
Python 3.11.0

✅ Skill 生效: 问"怎么运行 Python 脚本",Claude 会主动说用 uv run

要点

Claude Code 有时不会主动查询规范

我明确要求创建 Hook 和 Skill,但 Claude Code 没有先查官方文档就开始写代码。导致:

  • 文件结构错了好几次
  • 退出码用错了
  • JSON 解析方式不对

如果它能在动手前先用 WebFetch 读一下官方的 Hook 和 Skill 文档,这些坑都能避免。

这不是用户要读文档的问题,而是 AI agent 在执行不熟悉的任务时,应该先查规范再动手。

** skills 和 hook 确实可以强制 claude code 的行为**

hook 可以通过约束 python 命令来给出正确的命令。kilo code 中也是这么做的。

参考