一、Skill.md是什么、怎么用
Skill.md就是一个普通的Markdown文件,里面写了你项目的开发规范。把这个文件给AI看,AI就知道怎么按你的规矩写代码。
最简单的例子
你在项目根目录创建一个文件:.claude/skills/form-generator.md
# 表单生成规范
我们项目用React 18 + Ant Design 5.x
所有表单组件必须:
1. 用react-hook-form管理状态
2. 校验用zod,不用yup
3. 提交按钮loading时禁用
4. 错误提示显示在字段下方
组件文件结构:
FormName/
├── index.tsx
├── schema.ts
└── types.ts
就这么简单。
二、实际操作步骤(以Claude Desktop为例)
Step 1: 创建Skill文件
在你的项目里创建目录:
mkdir -p .claude/skills
创建第一个Skill文件:
touch .claude/skills/form-generator.md
Step 2: 编写Skill内容
复制下面的内容到 form-generator.md:
# React表单组件生成规范
## 技术栈
- React 18.2
- TypeScript 5.0
- Ant Design 5.12
- react-hook-form 7.48
- zod 3.22
## 文件结构
每个表单组件按以下结构组织:
FormName/
├── index.tsx # 组件主文件
├── schema.ts # zod校验规则
└── types.ts # TypeScript类型
## 代码模板
### types.ts 模板
```typescript
import { z } from 'zod';
import { formSchema } from './schema';
export type FormValues = z.infer<typeof formSchema>;
export interface FormProps {
mode: 'create' | 'edit';
initialData?: FormValues;
onSubmit: (data: FormValues) => Promise<void>;
onCancel?: () => void;
}
```text
### schema.ts 模板
```typescript
import { z } from 'zod';
export const formSchema = z.object({
// 字段定义,每个字段必须有中文错误提示
username: z.string().min(1, '请输入用户名'),
email: z.string().email('邮箱格式不正确'),
});
```text
### index.tsx 模板
```typescript
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Form, Input, Button } from 'antd';
import { formSchema } from './schema';
import type { FormProps, FormValues } from './types';
export default function FormName({ mode, initialData, onSubmit, onCancel }: FormProps) {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: initialData,
});
return (
<Form layout="vertical" onFinish={handleSubmit(onSubmit)}>
{/* 表单字段 */}
<Form.Item>
<Button type="primary" htmlType="submit" loading={isSubmitting}>
提交
</Button>
{onCancel && <Button onClick={onCancel}>取消</Button>}
</Form.Item>
</Form>
);
}
```text
## 字段类型处理
### 文本输入
```typescript
// schema
username: z.string().min(3, '至少3个字符').max(20, '最多20个字符'),
// tsx
<Form.Item label="用户名" validateStatus={errors.username ? 'error' : ''} help={errors.username?.message}>
<Input {...register('username')} placeholder="请输入用户名" />
</Form.Item>
```text
### 邮箱
```typescript
// schema
email: z.string().email('邮箱格式不正确'),
// tsx
<Form.Item label="邮箱" validateStatus={errors.email ? 'error' : ''} help={errors.email?.message}>
<Input {...register('email')} type="email" />
</Form.Item>
```text
### 手机号
```typescript
// schema
phone: z.string().regex(/^1[3-9]\d{9}$/, '手机号格式不正确'),
// tsx
<Form.Item label="手机号" validateStatus={errors.phone ? 'error' : ''} help={errors.phone?.message}>
<Input {...register('phone')} maxLength={11} />
</Form.Item>
```text
### 数字
```typescript
// schema
age: z.number().min(18, '年龄不能小于18').max(100, '年龄不能大于100'),
// tsx
<Form.Item label="年龄">
<InputNumber {...register('age', { valueAsNumber: true })} min={0} max={150} />
</Form.Item>
```text
### 下拉选择
```typescript
// schema
role: z.enum(['admin', 'user'], { errorMap: () => ({ message: '请选择角色' }) }),
// tsx
<Form.Item label="角色">
<Select {...register('role')} options={[
{ label: '管理员', value: 'admin' },
{ label: '普通用户', value: 'user' },
]} />
</Form.Item>
```text
## 提交处理
```typescript
const onSubmit = async (data: FormValues) => {
try {
await onSubmit(data);
message.success(mode === 'create' ? '创建成功' : '保存成功');
} catch (error) {
message.error('操作失败,请重试');
}
};
```text
## 输出要求
当用户要求生成表单时,必须生成:
1. 完整的三个文件
2. 所有字段都有zod校验
3. 所有错误提示都是中文
4. 提交按钮有loading状态
```plain
### Step 3: 在Claude中使用
#### 方法1:手动加载(每次对话都要做)
打开Claude Desktop,新建对话,输入:
```text
请阅读我项目的表单生成规范:
【把 form-generator.md 的内容完整粘贴在这里】
读完后,帮我生成一个用户注册表单,包含:
- 用户名(3-20字符)
- 邮箱
- 密码(8-20字符)
- 确认密码(需要和密码一致)
```plain
##### 方法2:使用MCP自动加载(推荐)
配置Claude Desktop读取本地Skill文件。
编辑配置文件:
- Mac: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
添加:
```json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/your/project/.claude/skills"]
}
}
}
把 /path/to/your/project 改成你的实际项目路径。
重启Claude Desktop,然后直接说:
读取 form-generator.md 文件,然后帮我生成用户注册表单
AI会自动读取文件内容。
三、完整实战演示
场景:开发一个商品编辑表单
第一步:准备Skill文件
已经有了 form-generator.md(上面写的那个)
第二步:跟AI对话
你说
请按照 form-generator.md 的规范,生成一个商品编辑表单。
字段:
- 商品名称(必填,最多50字)
- 商品分类(下拉选择:电子产品/服装/食品/其他)
- 价格(必填,大于0的数字,最多2位小数)
- 库存(必填,整数,大于等于0)
- 商品描述(选填,最多500字)
组件名:ProductForm
第三步:AI生成代码
AI会生成三个文件
文件1: ProductForm/types.ts
import { z } from 'zod';
import { formSchema } from './schema';
export type FormValues = z.infer<typeof formSchema>;
export interface ProductFormProps {
mode: 'create' | 'edit';
initialData?: FormValues;
onSubmit: (data: FormValues) => Promise<void>;
onCancel?: () => void;
}
文件2: ProductForm/schema.ts
import { z } from 'zod';
export const formSchema = z.object({
name: z.string()
.min(1, '请输入商品名称')
.max(50, '商品名称不能超过50个字符'),
category: z.enum(['electronics', 'clothing', 'food', 'other'], {
errorMap: () => ({ message: '请选择商品分类' })
}),
price: z.number({
required_error: '请输入价格',
invalid_type_error: '价格必须是数字',
})
.positive('价格必须大于0')
.refine(
(val) => /^\d+(\.\d{1,2})?$/.test(val.toString()),
'价格最多保留2位小数'
),
stock: z.number({
required_error: '请输入库存',
invalid_type_error: '库存必须是数字',
})
.int('库存必须是整数')
.min(0, '库存不能小于0'),
description: z.string()
.max(500, '描述不能超过500个字符')
.optional(),
});
文件3: ProductForm/index.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Form, Input, InputNumber, Select, Button, Space, message } from 'antd';
import { formSchema } from './schema';
import type { ProductFormProps, FormValues } from './types';
const { TextArea } = Input;
const CATEGORY_OPTIONS = [
{ label: '电子产品', value: 'electronics' },
{ label: '服装', value: 'clothing' },
{ label: '食品', value: 'food' },
{ label: '其他', value: 'other' },
];
export default function ProductForm({
mode,
initialData,
onSubmit,
onCancel,
}: ProductFormProps) {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: initialData,
});
const handleFormSubmit = async (data: FormValues) => {
try {
await onSubmit(data);
message.success(mode === 'create' ? '创建成功' : '保存成功');
} catch (error) {
message.error('操作失败,请重试');
}
};
return (
<Form layout="vertical" onFinish={handleSubmit(handleFormSubmit)}>
<Form.Item
label="商品名称"
required
validateStatus={errors.name ? 'error' : ''}
help={errors.name?.message}
>
<Input
{...register('name')}
placeholder="请输入商品名称"
maxLength={50}
showCount
/>
</Form.Item>
<Form.Item
label="商品分类"
required
validateStatus={errors.category ? 'error' : ''}
help={errors.category?.message}
>
<Select
{...register('category')}
placeholder="请选择分类"
options={CATEGORY_OPTIONS}
/>
</Form.Item>
<Form.Item
label="价格"
required
validateStatus={errors.price ? 'error' : ''}
help={errors.price?.message}
>
<InputNumber
{...register('price', { valueAsNumber: true })}
placeholder="请输入价格"
min={0}
precision={2}
style={{ width: '100%' }}
addonBefore="¥"
/>
</Form.Item>
<Form.Item
label="库存"
required
validateStatus={errors.stock ? 'error' : ''}
help={errors.stock?.message}
>
<InputNumber
{...register('stock', { valueAsNumber: true })}
placeholder="请输入库存"
min={0}
precision={0}
style={{ width: '100%' }}
/>
</Form.Item>
<Form.Item
label="商品描述"
validateStatus={errors.description ? 'error' : ''}
help={errors.description?.message}
>
<TextArea
{...register('description')}
placeholder="请输入商品描述"
rows={4}
maxLength={500}
showCount
/>
</Form.Item>
<Form.Item>
<Space>
<Button
type="primary"
htmlType="submit"
loading={isSubmitting}
disabled={isSubmitting}
>
{mode === 'create' ? '创建' : '保存'}
</Button>
{onCancel && (
<Button onClick={onCancel} disabled={isSubmitting}>
取消
</Button>
)}
</Space>
</Form.Item>
</Form>
);
}
第四步:在项目中使用
在你的页面组件里
// pages/Product/CreatePage.tsx
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Card, message } from 'antd';
import ProductForm from '@/components/ProductForm';
import { createProduct } from '@/services/product';
export default function ProductCreatePage() {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const handleSubmit = async (data: any) => {
setLoading(true);
try {
await createProduct(data);
message.success('商品创建成功');
navigate('/products');
} catch (error) {
message.error('创建失败');
throw error;
} finally {
setLoading(false);
}
};
return (
<Card title="创建商品">
<ProductForm
mode="create"
onSubmit={handleSubmit}
onCancel={() => navigate('/products')}
/>
</Card>
);
}
第五步:测试
运行项目:
npm run dev
访问创建商品页面,测试:
- 提交空表单 → 应该显示"请输入商品名称"
- 价格输入负数 → 应该显示"价格必须大于0"
- 价格输入123.456 → 应该显示"价格最多保留2位小数"
- 库存输入小数 → 应该显示"库存必须是整数"
- 所有字段正确 → 提交成功
四、Skill文件的进阶用法
场景1:团队共享Skill
把Skill文件提交到Git:
git add .claude/
git commit -m "添加表单生成Skill"
git push
团队其他人拉代码后,配置自己的Claude Desktop读取这个目录,大家用的就是统一规范。
场景2:多个Skill组合使用
项目里创建多个Skill:
.claude/skills/
├── form-generator.md # 表单规范
├── crud-template.md # CRUD模板
├── api-standard.md # API调用规范
└── test-template.md # 测试规范
使用时:
读取 form-generator.md 和 api-standard.md,然后生成一个带API调用的用户表单
AI会综合两个Skill的规范来生成代码。
场景3:根据项目实际情况调整Skill
发现AI生成的表单缺少某个功能,比如自动保存草稿。
更新 form-generator.md:
## 新增:自动保存草稿
表单字段变化时,自动保存到localStorage
```typescript
import { useEffect } from 'react';
import { useWatch } from 'react-hook-form';
// 在表单组件里
const formValues = useWatch({ control });
useEffect(() => {
const draftKey = `form-draft-${mode}`;
localStorage.setItem(draftKey, JSON.stringify(formValues));
}, [formValues, mode]);
// 组件挂载时读取草稿
useEffect(() => {
const draftKey = `form-draft-${mode}`;
const draft = localStorage.getItem(draftKey);
if (draft && !initialData) {
reset(JSON.parse(draft));
}
}, []);
```text
```plain
下次生成表单,AI就会自动加上这个功能。
---
## 五、常见问题排查
### 问题1:AI没有按Skill规范生成代码
#### 可能原因:
- Skill文件内容太长,AI没完全理解
- Skill规范写得模糊
##### 解决:
- 把Skill拆分成多个小文件
- 规范用代码示例说明,别只写文字描述
- 重新提问时明确说"严格按照Skill规范"
### 问题2:MCP读不到Skill文件
#### 检查:
```bash
# 确认文件存在
ls -la /path/to/project/.claude/skills/
# 确认Claude Desktop配置正确
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
# 重启Claude Desktop
问题3:生成的代码和项目技术栈不匹配
原因: Skill文件里的技术栈版本号写错了
修复: 更新Skill文件,确保技术栈版本和 package.json 一致
# 查看实际版本
cat package.json | grep react-hook-form
# "react-hook-form": "^7.48.0"
# 更新Skill文件
# react-hook-form 7.48 (不是 7.45)
六、Skill文件检查清单
写完Skill文件后,自检:
- 技术栈版本号和项目一致
- 有完整的代码示例(不是只有文字描述)
- 文件结构清晰明确
- 有输出要求说明
- 用AI试生成一次,确认符合预期
- 提交到Git
- 团队成员review
七、Skill模板推荐
如果不知道怎么写Skill,可以从这几个模板开始:
模板1:最小可用版本
# 组件规范
技术栈:React 18 + TypeScript
文件结构:
ComponentName/
├── index.tsx
└── types.ts
命名规范:
- 组件名PascalCase
- 文件名PascalCase.tsx
代码模板:
【粘贴一个实际组件的代码】
按这个模板生成其他组件。
模板2:详细版本
参考前面的 form-generator.md
模板3:团队规范整合版
把团队的所有规范文档整合成一个Skill:
# 项目开发规范
## 技术栈
【列出所有依赖和版本】
## 目录结构
【项目目录树】
## 命名规范
【所有命名规则】
## 代码规范
【ESLint、Prettier配置】
## 常用模板
### 页面组件
【代码模板】
### 业务组件
【代码模板】
### Hook
【代码模板】
## 输出要求
【质量标准】
八、总结
Skill.md的核心:
- 就是一个普通文本文件,写了你的开发规范
- 给AI看,让AI按规范生成代码
- 可以持续优化,发现问题就更新
- 团队共享,统一代码风格
不要想得太复杂,从一个最简单的Skill开始,慢慢完善。