# 【TRAE技巧便利店】再也不用手写文档了!这个Skill让我从"文档恐惧症"中解脱

【TRAE技巧便利店】再也不用手写文档了!这个Skill让我从"文档恐惧症"中解脱

trae技巧便利店


:weary_face: 作为一个懒人程序员

写代码我很快乐,但写文档?那是另一回事了。

每次项目快结束时要补文档,简直比写代码还痛苦:

  • 函数列表要一个个复制粘贴
  • 参数说明要手动整理
  • README 更是懒得动笔
  • 最惨的是代码改了,文档忘了更新

于是我想:能不能让代码自己写文档?

经过几天的摸索,我做出了 doc-generator 这个 Skill,现在分享给大家。


:bullseye: 它能做什么

简单说,就是扫描你的代码,自动生成三份文档:

:one: API.md - API 文档

自动提取所有函数和类,生成表格式的文档:

内容 说明
函数名 自动识别
参数列表 包含类型和默认值
返回值类型 从类型注解提取
函数说明 从 docstring 提取

:two: README.md - 项目说明

生成标准的项目结构,包含:

  • 项目统计(多少函数、多少类)
  • 依赖列表
  • 快速开始示例

:three: CHANGELOG.md - 更新日志

自动记录版本历史


:rocket: 怎么用

基础用法

/gendoc ./src

就这么简单!它会扫描 src 目录下的所有代码,然后在 ./docs 目录生成三份文档。

进阶用法

# 只生成 API 文档
/gendoc ./api api

# 指定输出位置
/gendoc ./src all ./my-docs

# 生成英文文档
/gendoc ./src all ./docs en-US

:glowing_star: 支持的语言

目前支持两种语言:

  • :white_check_mark: Python - 提取 docstring、类型注解
  • :white_check_mark: JavaScript/TypeScript - 提取 JSDoc、TS 类型

如果你用的是 Go、Java 等其他语言,欢迎提 PR 扩展!


:light_bulb: 我是怎么实现它的

思路1:正则表达式大法

核心就是用正则表达式匹配代码模式:

# 匹配 Python 函数
func_pattern = r'def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*(.+?))?:'

# 匹配类定义
class_pattern = r'class\s+(\w+)(?:\((.*?)\))?'

看起来很简单,但要注意处理各种边界情况:

  • 有类型注解的参数
  • 有默认值的参数
  • 多行 docstring

思路2:智能注释提取

这个是最有用的部分!

Python 的 docstring:

def hello(name: str) -> str:
    """这是个打招呼函数"""

JavaScript 的 JSDoc:

/**
 * 计算两数之和
 * @param {number} a - 第一个数
 * @param {number} b - 第二个数
 * @returns {number} 求和结果
 */
function add(a, b) {
    return a + b
}

我都会自动识别并提取到文档中。

思路3:模板化输出

生成文档时用字符串拼接,保持格式统一:

content = f"# {project_name}\n\n"
content += "## 函数列表\n\n"
for func in functions:
    content += f"### `{func['name']}`\n"
    # ...

:memo: 生成的文档长这样

API 文档示例

# API 文档

生成时间: 2024-03-10 14:30:00

## 函数列表

### `execute`

**文件**: main.py

**语言**: Python

**描述**: Skill 执行入口函数

**参数**:

| 参数名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| kwargs | dict | - | - |

**返回值**: `Dict[str, Any]`

---

README 示例

# my-project

## 项目简介

(此处添加项目简介)

## 项目统计

- **函数数量**: 15
- **类数量**: 3
- **依赖包**: 8

## 主要依赖

- json
- os
- re

## 快速开始

### 安装

```bash
pip install -r requirements.txt

使用方法

result = execute()
print(result)

---

## 📊 效果对比

| 对比项 | 手写文档 | 使用这个 Skill |
|--------|----------|---------------|
| 时间成本 | 数小时 | 几秒钟 |
| 准确性 | 容易遗漏 | 自动提取 |
| 同步性 | 经常忘记更新 | 随时可重新生成 |
| 格式统一性 | 看心情 | 标准模板 |

---

## 📦 完整代码在这里

### skill.json

```json
{
  "name": "doc-generator",
  "version": "1.0.0",
  "description": "智能文档生成器,根据代码自动生成API文档、README和使用指南",
  "author": "TRAE社区",
  "tags": ["文档生成", "API文档", "自动化"],
  "trigger": {
    "type": "command",
    "command": "/gendoc"
  },
  "parameters": {
    "type": "object",
    "properties": {
      "source_path": {
        "type": "string",
        "description": "源代码路径(文件或目录)"
      },
      "doc_type": {
        "type": "string",
        "enum": ["api", "readme", "changelog", "all"],
        "default": "all",
        "description": "文档类型"
      },
      "output_dir": {
        "type": "string",
        "default": "./docs",
        "description": "输出目录"
      },
      "language": {
        "type": "string",
        "enum": ["zh-CN", "en-US"],
        "default": "zh-CN",
        "description": "文档语言"
      }
    },
    "required": ["source_path"]
  }
}

main.py

"""
文档生成 Skill - 主执行逻辑
功能:自动分析代码结构,生成规范的文档
"""

import os
import re
import json
from typing import Dict, List, Any, Optional
from datetime import datetime

class DocGenerator:
    """文档生成核心类"""
    
    def __init__(self, source_path: str, doc_type: str = "all", output_dir: str = "./docs", language: str = "zh-CN"):
        self.source_path = source_path
        self.doc_type = doc_type
        self.output_dir = output_dir
        self.language = language
        self.functions = []
        self.classes = []
        self.imports = []
        self.constants = []
    
    def generate(self) -> Dict[str, Any]:
        """主生成方法"""
        if not os.path.exists(self.source_path):
            return {"error": f"路径不存在: {self.source_path}"}
        
        # 分析代码结构
        if os.path.isfile(self.source_path):
            self._analyze_file(self.source_path)
        else:
            self._analyze_directory(self.source_path)
        
        # 创建输出目录
        os.makedirs(self.output_dir, exist_ok=True)
        
        results = {"generated_files": []}
        
        # 生成不同类型的文档
        if self.doc_type in ["api", "all"]:
            api_file = self._generate_api_doc()
            results["generated_files"].append(api_file)
        
        if self.doc_type in ["readme", "all"]:
            readme_file = self._generate_readme()
            results["generated_files"].append(readme_file)
        
        if self.doc_type in ["changelog", "all"]:
            changelog_file = self._generate_changelog()
            results["generated_files"].append(changelog_file)
        
        return results
    
    def _analyze_file(self, file_path: str):
        """分析单个文件"""
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        filename = os.path.basename(file_path)
        
        if file_path.endswith('.py'):
            self._parse_python_file(content, filename)
        elif file_path.endswith(('.js', '.ts', '.jsx', '.tsx')):
            self._parse_js_file(content, filename)
    
    def _analyze_directory(self, dir_path: str):
        """分析目录"""
        for root, dirs, files in os.walk(dir_path):
            dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__', 'venv', 'build', 'dist']]
            
            for file in files:
                if file.endswith(('.py', '.js', '.ts', '.jsx', '.tsx')):
                    file_path = os.path.join(root, file)
                    self._analyze_file(file_path)
    
    def _parse_python_file(self, content: str, filename: str):
        """解析 Python 文件"""
        lines = content.split('\n')
        
        # 提取导入
        import_pattern = r'^(?:from|import)\s+([^\s]+)'
        for line in lines:
            match = re.match(import_pattern, line.strip())
            if match:
                self.imports.append(match.group(1))
        
        # 提取函数
        func_pattern = r'def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*(.+?))?:\s*(?:\"\"\"(.*?)\"\"\"|\'\'\'(.*?)\'\'\')?'
        for match in re.finditer(func_pattern, content, re.MULTILINE | re.DOTALL):
            func_name = match.group(1)
            params = match.group(2).strip() if match.group(2) else ""
            return_type = match.group(3).strip() if match.group(3) else None
            docstring = match.group(4) or match.group(5) or ""
            
            self.functions.append({
                "name": func_name,
                "params": self._parse_params(params),
                "return_type": return_type,
                "description": docstring.strip(),
                "file": filename,
                "language": "Python"
            })
        
        # 提取类
        class_pattern = r'class\s+(\w+)(?:\((.*?)\))?:\s*(?:\"\"\"(.*?)\"\"\"|\'\'\'(.*?)\'\'\')?'
        for match in re.finditer(class_pattern, content, re.MULTILINE | re.DOTALL):
            class_name = match.group(1)
            inheritance = match.group(2).strip() if match.group(2) else None
            docstring = match.group(3) or match.group(4) or ""
            
            self.classes.append({
                "name": class_name,
                "inheritance": inheritance,
                "description": docstring.strip(),
                "file": filename,
                "language": "Python"
            })
    
    def _parse_js_file(self, content: str, filename: str):
        """解析 JavaScript/TypeScript 文件"""
        # 提取导入
        import_pattern = r'import\s+.*?from\s+[\'"]([^\'"]+)[\'"]'
        for match in re.finditer(import_pattern, content):
            self.imports.append(match.group(1))
        
        # 提取函数
        func_patterns = [
            r'function\s+(\w+)\s*\((.*?)\)\s*(?::\s*(.+?))?\s*\{',
            r'(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\((.*?)\)\s*(?::\s*(.+?))?\s*=>',
            r'export\s+(?:async\s+)?function\s+(\w+)\s*\((.*?)\)\s*(?::\s*(.+?))?\s*\{'
        ]
        
        for pattern in func_patterns:
            for match in re.finditer(pattern, content, re.MULTILINE):
                func_name = match.group(1)
                params = match.group(2).strip() if match.group(2) else ""
                return_type = match.group(3).strip() if match.group(3) else None
                
                jsdoc = self._extract_jsdoc(content, match.start())
                
                self.functions.append({
                    "name": func_name,
                    "params": self._parse_js_params(params),
                    "return_type": return_type,
                    "description": jsdoc.get("description", ""),
                    "file": filename,
                    "language": "JavaScript/TypeScript"
                })
        
        # 提取类
        class_pattern = r'class\s+(\w+)(?:\s+extends\s+(\w+))?\s*\{'
        for match in re.finditer(class_pattern, content):
            class_name = match.group(1)
            inheritance = match.group(2) if match.group(2) else None
            
            self.classes.append({
                "name": class_name,
                "inheritance": inheritance,
                "description": "",
                "file": filename,
                "language": "JavaScript/TypeScript"
            })
    
    def _parse_params(self, params_str: str) -> List[Dict]:
        """解析函数参数"""
        if not params_str.strip():
            return []
        
        params = []
        for param in params_str.split(','):
            param = param.strip()
            if not param:
                continue
            
            if ':' in param:
                name, type_hint = param.split(':', 1)
                name = name.strip()
                type_hint = type_hint.strip()
            else:
                name = param
                type_hint = None
            
            default_value = None
            if '=' in name:
                name, default_value = name.split('=', 1)
                name = name.strip()
                default_value = default_value.strip()
            
            params.append({
                "name": name,
                "type": type_hint,
                "default": default_value
            })
        
        return params
    
    def _parse_js_params(self, params_str: str) -> List[Dict]:
        """解析 JavaScript 参数"""
        params = []
        for param in params_str.split(','):
            param = param.strip()
            if not param:
                continue
            
            if '{' in param or '[' in param:
                params.append({"name": param, "type": None, "default": None})
                continue
            
            if ':' in param:
                name, type_hint = param.split(':', 1)
                name = name.strip()
                type_hint = type_hint.strip()
            else:
                name = param
                type_hint = None
            
            default_value = None
            if '=' in name:
                name, default_value = name.split('=', 1)
                name = name.strip()
                default_value = default_value.strip()
            
            params.append({
                "name": name,
                "type": type_hint,
                "default": default_value
            })
        
        return params
    
    def _extract_jsdoc(self, content: str, func_start: int) -> Dict:
        """提取 JSDoc 注释"""
        jsdoc = {"description": "", "params": [], "returns": None}
        
        lines_before = content[:func_start].split('\n')
        comment_lines = []
        
        for line in reversed(lines_before):
            stripped = line.strip()
            if stripped.startswith('*') or stripped.startswith('/**') or stripped.startswith('*/'):
                comment_lines.insert(0, stripped)
            else:
                break
        
        if comment_lines:
            comment_text = '\n'.join(comment_lines)
            desc_match = re.search(r'/\*\*\s*(.*?)\s*(?:@|\*/)', comment_text, re.DOTALL)
            if desc_match:
                jsdoc["description"] = desc_match.group(1).replace('*', '').strip()
            
            param_matches = re.findall(r'@param\s+(?:\{(.+?)\}\s+)?(\w+)\s*-?\s*(.+)', comment_text)
            for type_hint, name, desc in param_matches:
                jsdoc["params"].append({"name": name, "type": type_hint, "description": desc.strip()})
            
            return_match = re.search(r'@returns?\s+(?:\{(.+?)\}\s+)?(.+)', comment_text)
            if return_match:
                jsdoc["returns"] = {"type": return_match.group(1), "description": return_match.group(2).strip()}
        
        return jsdoc
    
    def _generate_api_doc(self) -> str:
        """生成 API 文档"""
        output_file = os.path.join(self.output_dir, "API.md")
        
        content = "# API 文档\n\n"
        content += f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
        
        if self.functions:
            content += "## 函数列表\n\n"
            for func in self.functions:
                content += f"### `{func['name']}`\n\n"
                content += f"**文件**: {func['file']}\n\n"
                content += f"**语言**: {func['language']}\n\n"
                
                if func['description']:
                    content += f"**描述**: {func['description']}\n\n"
                
                if func['params']:
                    content += "**参数**:\n\n"
                    content += "| 参数名 | 类型 | 默认值 | 说明 |\n"
                    content += "|--------|------|--------|------|\n"
                    for param in func['params']:
                        param_type = param.get('type') or '-'
                        default = param.get('default') or '-'
                        content += f"| {param['name']} | {param_type} | {default} | - |\n"
                    content += "\n"
                
                if func['return_type']:
                    content += f"**返回值**: `{func['return_type']}`\n\n"
                
                content += "---\n\n"
        
        if self.classes:
            content += "## 类列表\n\n"
            for cls in self.classes:
                content += f"### `{cls['name']}`\n\n"
                content += f"**文件**: {cls['file']}\n\n"
                content += f"**语言**: {cls['language']}\n\n"
                
                if cls['inheritance']:
                    content += f"**继承自**: `{cls['inheritance']}`\n\n"
                
                if cls['description']:
                    content += f"**描述**: {cls['description']}\n\n"
                
                content += "---\n\n"
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(content)
        
        return output_file
    
    def _generate_readme(self) -> str:
        """生成 README 文档"""
        output_file = os.path.join(self.output_dir, "README.md")
        
        project_name = os.path.basename(self.source_path) if os.path.isdir(self.source_path) else os.path.basename(self.source_path).rsplit('.', 1)[0]
        
        content = f"# {project_name}\n\n"
        content += "## 项目简介\n\n"
        content += "(此处添加项目简介)\n\n"
        
        content += "## 项目统计\n\n"
        content += f"- **函数数量**: {len(self.functions)}\n"
        content += f"- **类数量**: {len(self.classes)}\n"
        content += f"- **依赖包**: {len(set(self.imports))}\n\n"
        
        if self.imports:
            content += "## 主要依赖\n\n"
            unique_imports = sorted(set(self.imports))
            for imp in unique_imports[:10]:
                content += f"- {imp}\n"
            if len(unique_imports) > 10:
                content += f"- ... 还有 {len(unique_imports) - 10} 个依赖\n"
            content += "\n"
        
        content += "## 快速开始\n\n"
        content += "### 安装\n\n"
        content += "```bash\n"
        content += "# 安装依赖\n"
        content += "pip install -r requirements.txt\n"
        content += "```\n\n"
        
        content += "### 使用方法\n\n"
        content += "```python\n"
        if self.functions:
            first_func = self.functions[0]
            content += f"# 示例: 调用 {first_func['name']} 函数\n"
            content += f"result = {first_func['name']}()\n"
            content += "print(result)\n"
        else:
            content += "# 添加使用示例\n"
        content += "```\n\n"
        
        content += "## 文档\n\n"
        content += "- [API 文档](./API.md)\n"
        content += "- [更新日志](./CHANGELOG.md)\n\n"
        
        content += "## 许可证\n\n"
        content += "MIT License\n"
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(content)
        
        return output_file
    
    def _generate_changelog(self) -> str:
        """生成更新日志"""
        output_file = os.path.join(self.output_dir, "CHANGELOG.md")
        
        content = "# 更新日志\n\n"
        content += "所有重要的更改都将记录在此文件中。\n\n"
        
        today = datetime.now().strftime('%Y-%m-%d')
        content += f"## [{today}] - 初始版本\n\n"
        content += "### 新增\n"
        content += "- 初始项目结构\n"
        
        if self.functions:
            content += f"- 添加 {len(self.functions)} 个函数\n"
        if self.classes:
            content += f"- 添加 {len(self.classes)} 个类\n"
        
        content += "\n"
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(content)
        
        return output_file

def execute(**kwargs):
    """Skill 执行入口函数"""
    source_path = kwargs.get("source_path")
    doc_type = kwargs.get("doc_type", "all")
    output_dir = kwargs.get("output_dir", "./docs")
    language = kwargs.get("language", "zh-CN")
    
    generator = DocGenerator(source_path, doc_type, output_dir, language)
    result = generator.generate()
    
    if "error" in result:
        return {
            "status": "error",
            "message": result["error"]
        }
    
    output = f"""
========================================
文档生成完成
========================================
源代码路径: {source_path}
输出目录: {output_dir}

生成的文档:
"""
    
    for file_path in result["generated_files"]:
        output += f"  - {file_path}\n"
    
    output += f"""
统计信息:
  - 函数: {len(generator.functions)} 个
  - 类: {len(generator.classes)} 个
  - 依赖: {len(set(generator.imports))} 个

========================================
"""
    
    return {
        "status": "success",
        "output": output,
        "data": result
    }

if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1:
        result = execute(source_path=sys.argv[1])
        print(result["output"])

README.md

# 文档生成 Skill

自动分析代码结构,生成规范的文档。支持 Python 和 JavaScript/TypeScript。

## 快速开始

\`\`\`bash
/gendoc ./src
\`\`\`

## 支持的文档类型

- API.md - API 文档
- README.md - 项目说明
- CHANGELOG.md - 更新日志

:thinking: 使用建议

  1. 代码要有注释 - 没有注释的代码生成出来的文档也会很空洞
  2. 写好类型注解 - TypeScript 或 Python 的类型注解会让文档更详细
  3. 定期重新生成 - 代码更新后记得重新跑一次
  4. 人工润色 - 生成的文档是框架,还需要你补充项目背景等信息

:speech_balloon: 最后说两句

这个 Skill 让我省了很多写文档的时间,但不是万能的。好的文档还是需要:

  • 清晰的项目说明
  • 实际的使用示例
  • 常见问题解答

它帮你搞定重复劳动,你负责填充灵魂。

如果觉得有用,欢迎评论区告诉我你还有什么文档相关的痛点!

#TRAE #文档自动化 #效率工具 #懒人必备

3 个赞

哥666 :saluting_face: 不扶墙就服你

1 个赞

收藏了!!和实用的技能

2 个赞

通过这个技能写的文档和直接让AI写有什么区别呢

2 个赞

API文档能用openapi或者swagger的格式,一般也不担心

2 个赞