返回笔记首页

Skill.md实际使用操作手册

主题配置

一、Skill.md是什么、怎么用

Skill.md就是一个普通的Markdown文件,里面写了你项目的开发规范。把这个文件给AI看,AI就知道怎么按你的规矩写代码。

最简单的例子

你在项目根目录创建一个文件:.claude/skills/form-generator.md

markdown
# 表单生成规范

我们项目用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文件

在你的项目里创建目录:

bash
mkdir -p .claude/skills

创建第一个Skill文件:

bash
touch .claude/skills/form-generator.md

Step 2: 编写Skill内容

复制下面的内容到 form-generator.md

markdown
# 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,然后直接说:

plain
读取 form-generator.md 文件,然后帮我生成用户注册表单

AI会自动读取文件内容。


三、完整实战演示

场景:开发一个商品编辑表单

第一步:准备Skill文件

已经有了 form-generator.md(上面写的那个)

第二步:跟AI对话

你说
plain
请按照 form-generator.md 的规范,生成一个商品编辑表单。

字段:
- 商品名称(必填,最多50字)
- 商品分类(下拉选择:电子产品/服装/食品/其他)
- 价格(必填,大于0的数字,最多2位小数)
- 库存(必填,整数,大于等于0)
- 商品描述(选填,最多500字)

组件名:ProductForm

第三步:AI生成代码

AI会生成三个文件
文件1: ProductForm/types.ts
typescript
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
typescript
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
typescript
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>
  );
}

第四步:在项目中使用

在你的页面组件里
typescript
// 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>
  );
}

第五步:测试

运行项目:

bash
npm run dev

访问创建商品页面,测试:

  • 提交空表单 → 应该显示"请输入商品名称"
  • 价格输入负数 → 应该显示"价格必须大于0"
  • 价格输入123.456 → 应该显示"价格最多保留2位小数"
  • 库存输入小数 → 应该显示"库存必须是整数"
  • 所有字段正确 → 提交成功

四、Skill文件的进阶用法

场景1:团队共享Skill

把Skill文件提交到Git:

bash
git add .claude/
git commit -m "添加表单生成Skill"
git push

团队其他人拉代码后,配置自己的Claude Desktop读取这个目录,大家用的就是统一规范。

场景2:多个Skill组合使用

项目里创建多个Skill:

plain
.claude/skills/
├── form-generator.md       # 表单规范
├── crud-template.md        # CRUD模板
├── api-standard.md         # API调用规范
└── test-template.md        # 测试规范

使用时:

plain
读取 form-generator.md 和 api-standard.md,然后生成一个带API调用的用户表单

AI会综合两个Skill的规范来生成代码。

场景3:根据项目实际情况调整Skill

发现AI生成的表单缺少某个功能,比如自动保存草稿。

更新 form-generator.md

markdown
## 新增:自动保存草稿

表单字段变化时,自动保存到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 一致

bash
# 查看实际版本
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:最小可用版本

markdown
# 组件规范

技术栈:React 18 + TypeScript

文件结构:
ComponentName/
├── index.tsx
└── types.ts

命名规范:
- 组件名PascalCase
- 文件名PascalCase.tsx

代码模板:
【粘贴一个实际组件的代码】

按这个模板生成其他组件。

模板2:详细版本

参考前面的 form-generator.md

模板3:团队规范整合版

把团队的所有规范文档整合成一个Skill:

markdown
# 项目开发规范

## 技术栈
【列出所有依赖和版本】

## 目录结构
【项目目录树】

## 命名规范
【所有命名规则】

## 代码规范
【ESLint、Prettier配置】

## 常用模板
### 页面组件
【代码模板】

### 业务组件
【代码模板】

### Hook
【代码模板】

## 输出要求
【质量标准】

八、总结

Skill.md的核心:

  1. 就是一个普通文本文件,写了你的开发规范
  2. 给AI看,让AI按规范生成代码
  3. 可以持续优化,发现问题就更新
  4. 团队共享,统一代码风格

不要想得太复杂,从一个最简单的Skill开始,慢慢完善。