Token导航 LogoToken导航

Agent Skill · 待分类

frappe-doctype(冰沙文档类型)

frappe-doctype 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

frappe-doctype 是待分类类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描已通过,按安装量在待分类类的 11,209 个 Skill 里排第 2,121。

总安装 1,860周安装 76GitHub Stars 12下载 596核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:frappe-doctype(冰沙文档类型)
来源仓库:https://github.com/sergio-bershadsky/ai
仓库路径:skills/frappe-doctype
安装命令:
npx skills add https://github.com/sergio-bershadsky/ai --skill frappe-doctype
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/sergio-bershadsky/ai --skill frappe-doctype

这个 Skill 是做什么的

这个 Skill 的分类还没最终确定,用法以来源仓库的说明为准。

用于处理 GitHub 仓库、Issue 和 Pull Request 协作信息。适合在需要围绕仓库状态或代码变更进行整理时使用。frappe-doctype 属于待分类类 Skill,可作为该场景下的辅助能力补充。可结合来源仓库和原始 README 核验具体用法。安装前建议确认权限范围和维护状态,避免触发不必要操作。

注意是否会触发联网、命令执行或文件读写,确保安全使用。

来源仓库内路径:skills/frappe-doctype

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/sergio-bershadsky/ai --skill frappe-doctype;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

frappe-doctype 怎么安装?

在终端执行 npx skills add https://github.com/sergio-bershadsky/ai --skill frappe-doctype。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

frappe-doctype 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

frappe-doctype 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

frappe-doctype 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

frappe-doctype 用的人多吗?

公开数据是累计安装 1,860 次,近一周安装 76 次,下载 596 次,GitHub 12 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

frappe-doctype 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理待分类类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

待分类类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
infsh-cli

infsh CLI · Codex、Claude

4,374,576382权限未标注未明确未收录
ai-video-generation

AI 视频生成 · Codex、Claude

4,313,376382调用外部服务未明确有提醒
lark-openapi-explorer

云雀 OpenAPI 浏览器 · Codex、Claude

2,064,1209,011需要联网未明确有提醒
lark-workflow-standup-report

Lark 工作流程站立报告 · Codex、Claude

2,041,6328,978需要联网未明确通过
use-my-browser

使用我的浏览器 · Codex、Claude

1,848,49140操作浏览器未明确未通过
frappe-doctype

冰沙文档类型 · 本页

1,86012需要联网MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Frappe DocType Creation

Create a production-ready Frappe v15 DocType with complete controller implementation, service layer integration, repository pattern, and test coverage.

When to Use

  • Creating a new DocType for a Frappe application
  • Need proper controller with lifecycle hooks
  • Want service layer for business logic separation
  • Require repository for clean data access
  • Building submittable/amendable documents

Arguments

/frappe-doctype <doctype_name> [--module <module>] [--submittable] [--child]

Examples:

/frappe-doctype Sales Order
/frappe-doctype Invoice Item --child
/frappe-doctype Purchase Request --submittable

Procedure

Step 1: Gather DocType Requirements

Ask the user for:

  1. DocType Name (Title Case, e.g., "Sales Order")
  2. Module (which module this belongs to)
  3. DocType Type:

- Standard (regular CRUD document) - Submittable (has workflow: Draft → Submitted → Cancelled) - Child Table (embedded in parent documents) - Single (configuration/settings document)

  1. Key Fields (at least the primary fields needed)
  2. Naming Pattern:

- Autoname (series like SO-.YYYY.-.#####) - Field-based (use a specific field value) - Prompt (user enters name)

Step 2: Analyze and Design

Based on requirements, determine:

  • Field types and properties
  • Link relationships to other DocTypes
  • Required indexes for performance
  • Permission model (roles that can access)
  • Workflow requirements

Step 3: Generate DocType JSON

Create the DocType definition <doctype_folder>/<doctype_name>.json:

{
  "name": "<DocType Name>",
  "module": "<Module>",
  "doctype": "DocType",
  "naming_rule": "By \"Naming Series\" field",
  "autoname": "naming_series:",
  "is_submittable": 0,
  "is_tree": 0,
  "istable": 0,
  "editable_grid": 1,
  "track_changes": 1,
  "track_seen": 1,
  "engine": "InnoDB",
  "fields": [
    {
      "fieldname": "naming_series",
      "fieldtype": "Select",
      "label": "Series",
      "options": "<PREFIX>-.YYYY.-.#####",
      "reqd": 1,
      "in_list_view": 0
    },
    {
      "fieldname": "title",
      "fieldtype": "Data",
      "label": "Title",
      "reqd": 1,
      "in_list_view": 1,
      "in_standard_filter": 1
    },
    {
      "fieldname": "status",
      "fieldtype": "Select",
      "label": "Status",
      "options": "\nDraft\nPending\nCompleted\nCancelled",
      "default": "Draft",
      "in_list_view": 1,
      "in_standard_filter": 1
    },
    {
      "fieldname": "column_break_1",
      "fieldtype": "Column Break"
    },
    {
      "fieldname": "date",
      "fieldtype": "Date",
      "label": "Date",
      "default": "Today",
      "reqd": 1,
      "in_list_view": 1
    },
    {
      "fieldname": "section_break_details",
      "fieldtype": "Section Break",
      "label": "Details"
    },
    {
      "fieldname": "description",
      "fieldtype": "Text Editor",
      "label": "Description"
    },
    {
      "fieldname": "amended_from",
      "fieldtype": "Link",
      "label": "Amended From",
      "no_copy": 1,
      "options": "<DocType Name>",
      "print_hide": 1,
      "read_only": 1
    }
  ],
  "permissions": [
    {
      "role": "System Manager",
      "read": 1,
      "write": 1,
      "create": 1,
      "delete": 1,
      "submit": 0,
      "cancel": 0,
      "amend": 0
    }
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
  "title_field": "title"
}

Step 4: Generate Controller with v15 Type Annotations

Create <doctype_folder>/<doctype_name>.py:

# Copyright (c) <year>, <author> and contributors
# For license information, please see license.txt

import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.docstatus import DocStatus  # v15: Helper for docstatus checks
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from frappe.types import DF
    # Import child table types if needed
    # from <app>.<module>.doctype.<child_doctype>.<child_doctype> import <ChildDocType>

class <DocTypeName>(Document):
    """
    <DocType Name> - <brief description>

    Lifecycle:
        Draft → (validate) → Saved → (submit) → Submitted → (cancel) → Cancelled
    """

    # begin: auto-generated types
    # This section is auto-generated by Frappe. Do not modify manually.
    if TYPE_CHECKING:
        amended_from: DF.Link | None
        date: DF.Date
        description: DF.TextEditor | None
        naming_series: DF.Literal["<PREFIX>-.YYYY.-.#####"]
        status: DF.Literal["", "Draft", "Pending", "Completed", "Cancelled"]
        title: DF.Data
    # end: auto-generated types

    def before_validate(self) -> None:
        """Auto-set default values before validation."""
        self._set_defaults()

    def validate(self) -> None:
        """Validate document before save. Throw exception to prevent saving."""
        self._validate_business_rules()

    def before_save(self) -> None:
        """Called before document is saved to database."""
        self._update_status()

    def after_insert(self) -> None:
        """Called after new document is inserted."""
        self._notify_creation()

    def on_update(self) -> None:
        """Called when existing document is updated."""
        pass

    def before_submit(self) -> None:
        """Called before document submission. Validate submission requirements."""
        self._validate_submit_conditions()

    def on_submit(self) -> None:
        """Called after document submission. Create dependent records."""
        self._process_submission()

    def before_cancel(self) -> None:
        """Validate cancellation conditions."""
        self._validate_cancel_conditions()

    def on_cancel(self) -> None:
        """Handle cancellation cleanup."""
        self._process_cancellation()

    def on_trash(self) -> None:
        """Called when document is deleted. Cleanup related data."""
        pass

    # ──────────────────────────────────────────────────────────────────────────
    # Private Methods
    # ──────────────────────────────────────────────────────────────────────────

    def _set_defaults(self) -> None:
        """Set default values for fields."""
        if not self.date:
            self.date = frappe.utils.today()

    def _validate_business_rules(self) -> None:
        """Validate business rules specific to this DocType."""
        if not self.title:
            frappe.throw(_("Title is required"))

    def _update_status(self) -> None:
        """Update status based on document state using DocStatus helper."""
        # v15: Use DocStatus helper for readable status checks
        if self.docstatus.is_draft() and not self.status:
            self.status = "Draft"

    def _notify_creation(self) -> None:
        """Send notifications after creation."""
        # frappe.publish_realtime("new_<doctype>", {"name": self.name})
        pass

    def _validate_submit_conditions(self) -> None:
        """Check all conditions required for submission."""
        pass

    def _process_submission(self) -> None:
        """Process document submission - create GL entries, update stocks, etc."""
        self.db_set("status", "Completed")

    def _validate_cancel_conditions(self) -> None:
        """Check if document can be cancelled."""
        pass

    def _process_cancellation(self) -> None:
        """Reverse submission effects."""
        self.db_set("status", "Cancelled")

    # ──────────────────────────────────────────────────────────────────────────
    # Public API Methods (call from services or whitelisted methods)
    # ──────────────────────────────────────────────────────────────────────────

    def get_summary(self) -> dict:
        """Return document summary for API responses."""
        return {
            "name": self.name,
            "title": self.title,
            "status": self.status,
            "date": str(self.date)
        }

# ──────────────────────────────────────────────────────────────────────────────
# Whitelisted Methods (accessible via REST API)
# ──────────────────────────────────────────────────────────────────────────────

@frappe.whitelist()
def get_<doctype_snake>_summary(name: str) -> dict:
    """
    Get document summary.

    Args:
        name: Document name

    Returns:
        Document summary dict
    """
    doc = frappe.get_doc("<DocType Name>", name)
    doc.check_permission("read")
    return doc.get_summary()

Step 5: Generate Service Layer

Create <app>/<module>/services/<doctype_snake>_service.py:

"""
<DocType Name> Service

Business logic for <DocType Name> operations.
"""

import frappe
from frappe import _
from typing import Optional
from <app>.<module>.services.base import BaseService
from <app>.<module>.repositories.<doctype_snake>_repository import <DocTypeName>Repository

class <DocTypeName>Service(BaseService):
    """
    Service class for <DocType Name> business logic.

    All business rules and complex operations should be implemented here,
    not in the DocType controller.
    """

    def __init__(self):
        super().__init__()
        self.repo = <DocTypeName>Repository()

    def create(self, data: dict) -> dict:
        """
        Create a new <DocType Name>.

        Args:
            data: Document data

        Returns:
            Created document summary

        Raises:
            frappe.ValidationError: If validation fails
        """
        self.check_permission("<DocType Name>", "create", throw=True)
        self.validate_mandatory(data, ["title", "date"])

        doc = self.repo.create(data)
        self.log_activity("<DocType Name>", doc.name, "Created")

        return doc.get_summary()

    def update(self, name: str, data: dict) -> dict:
        """
        Update existing <DocType Name>.

        Args:
            name: Document name
            data: Fields to update

        Returns:
            Updated document summary
        """
        doc = self.repo.get_or_throw(name, for_update=True)
        self.check_permission("<DocType Name>", "write", doc=doc, throw=True)

        # Business validation
        if doc.status == "Completed":
            frappe.throw(_("Cannot modify completed documents"))

        doc.update(data)
        doc.save()
        self.log_activity("<DocType Name>", name, "Updated", data)

        return doc.get_summary()

    def submit(self, name: str) -> dict:
        """
        Submit document for processing.

        Args:
            name: Document name

        Returns:
            Submitted document summary
        """
        doc = self.repo.get_or_throw(name, for_update=True)
        self.check_permission("<DocType Name>", "submit", doc=doc, throw=True)

        # Pre-submit validation
        self._validate_submission(doc)

        doc.submit()
        return doc.get_summary()

    def cancel(self, name: str, reason: Optional[str] = None) -> dict:
        """
        Cancel submitted document.

        Args:
            name: Document name
            reason: Cancellation reason

        Returns:
            Cancelled document summary
        """
        doc = self.repo.get_or_throw(name, for_update=True)
        self.check_permission("<DocType Name>", "cancel", doc=doc, throw=True)

        if reason:
            frappe.db.set_value("<DocType Name>", name, "cancellation_reason", reason)

        doc.cancel()
        self.log_activity("<DocType Name>", name, "Cancelled", {"reason": reason})

        return doc.get_summary()

    def get_dashboard_stats(self) -> dict:
        """Get statistics for dashboard."""
        return {
            "total": self.repo.get_count(),
            "draft": self.repo.get_count({"status": "Draft"}),
            "pending": self.repo.get_count({"status": "Pending"}),
            "completed": self.repo.get_count({"status": "Completed"})
        }

    def _validate_submission(self, doc) -> None:
        """Validate all requirements for submission."""
        if doc.docstatus != 0:
            frappe.throw(_("Document must be in draft state to submit"))

Step 6: Generate Repository

Create <app>/<module>/repositories/<doctype_snake>_repository.py:

"""
<DocType Name> Repository

Data access layer for <DocType Name>.
"""

import frappe
from frappe.query_builder import DocType
from typing import Optional
from <app>.<module>.repositories.base import BaseRepository
from <app>.<module>.doctype.<doctype_folder>.<doctype_snake> import <DocTypeName>

class <DocTypeName>Repository(BaseRepository[<DocTypeName>]):
    """
    Repository for <DocType Name> database operations.
    """

    doctype = "<DocType Name>"

    def get_by_status(
        self,
        status: str,
        limit: int = 20,
        offset: int = 0
    ) -> list[dict]:
        """Get documents by status."""
        return self.get_list(
            filters={"status": status},
            fields=["name", "title", "date", "status", "owner"],
            order_by="date desc",
            limit=limit,
            offset=offset
        )

    def get_recent(self, days: int = 7) -> list[dict]:
        """Get documents created in the last N days."""
        from_date = frappe.utils.add_days(frappe.utils.today(), -days)
        return self.get_list(
            filters={"creation": [">=", from_date]},
            fields=["name", "title", "date", "status", "creation"],
            order_by="creation desc"
        )

    def search(
        self,
        query: str,
        filters: Optional[dict] = None,
        limit: int = 20
    ) -> list[dict]:
        """Full-text search on title and description."""
        base_filters = filters or {}
        base_filters["title"] = ["like", f"%{query}%"]

        return self.get_list(
            filters=base_filters,
            fields=["name", "title", "date", "status"],
            limit=limit
        )

    def get_with_related(self, name: str) -> dict:
        """Get document with related data."""
        doc = self.get_or_throw(name)
        return {
            **doc.as_dict(),
            # Add related data here
            # "items": self._get_items(name),
            # "comments": self._get_comments(name)
        }

    def bulk_update_status(self, names: list[str], status: str) -> int:
        """Bulk update status for multiple documents."""
        dt = DocType(self.doctype)
        return (
            frappe.qb.update(dt)
            .set(dt.status, status)
            .set(dt.modified, frappe.utils.now())
            .set(dt.modified_by, frappe.session.user)
            .where(dt.name.isin(names))
            .run()
        )

Step 7: Generate Test File

Create <doctype_folder>/test_<doctype_snake>.py:

# Copyright (c) <year>, <author> and contributors
# For license information, please see license.txt

import frappe
from frappe.tests import IntegrationTestCase, UnitTestCase
from <app>.<module>.services.<doctype_snake>_service import <DocTypeName>Service

class Test<DocTypeName>(IntegrationTestCase):
    """Integration tests for <DocType Name>."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.service = <DocTypeName>Service()

    def test_create_document(self):
        """Test document creation via service."""
        data = {
            "title": "Test Document",
            "date": frappe.utils.today()
        }
        result = self.service.create(data)

        self.assertIsNotNone(result.get("name"))
        self.assertEqual(result.get("title"), "Test Document")

    def test_create_requires_mandatory_fields(self):
        """Test that mandatory fields are validated."""
        with self.assertRaises(frappe.ValidationError):
            self.service.create({})

    def test_submit_document(self):
        """Test document submission."""
        # Create draft
        doc = frappe.get_doc({
            "doctype": "<DocType Name>",
            "title": "Submit Test",
            "date": frappe.utils.today()
        }).insert()

        # Submit via service
        result = self.service.submit(doc.name)
        self.assertEqual(result.get("status"), "Completed")

    def test_cannot_modify_completed(self):
        """Test that completed documents cannot be modified."""
        doc = frappe.get_doc({
            "doctype": "<DocType Name>",
            "title": "Completed Test",
            "date": frappe.utils.today(),
            "status": "Completed"
        }).insert()

        with self.assertRaises(frappe.ValidationError):
            self.service.update(doc.name, {"title": "New Title"})

    def test_get_dashboard_stats(self):
        """Test dashboard statistics."""
        stats = self.service.get_dashboard_stats()

        self.assertIn("total", stats)
        self.assertIn("draft", stats)
        self.assertIn("completed", stats)

class Unit<DocTypeName>(UnitTestCase):
    """Unit tests for <DocType Name> (no database)."""

    def test_validation_logic(self):
        """Test validation without database."""
        pass

Step 8: Show Preview and Confirm

## DocType Creation Preview

**DocType:** <DocType Name>
**Module:** <Module>
**Type:** Standard | Submittable | Child Table

### Files to Create:

📁 <module>/doctype/<doctype_folder>/
├── 📄 <doctype_snake>.json       # DocType definition
├── 📄 <doctype_snake>.py         # Controller with hooks
├── 📄 <doctype_snake>.js         # Client-side script
└── 📄 test_<doctype_snake>.py    # Test cases

📁 <module>/services/
└── 📄 <doctype_snake>_service.py # Business logic

📁 <module>/repositories/
└── 📄 <doctype_snake>_repository.py # Data access

### Fields:

| Field | Type | Required |
|-------|------|----------|
| naming_series | Select | Yes |
| title | Data | Yes |
| status | Select | No |
| date | Date | Yes |
| description | Text Editor | No |

---
Create this DocType with all layers?

Step 9: Execute and Verify

After approval, create all files and run:

bench --site <site> migrate
bench --site <site> run-tests --doctype "<DocType Name>"

Output Format

## DocType Created

**Name:** <DocType Name>
**Path:** <app>/<module>/doctype/<doctype_folder>/

### Files Created:
- ✅ <doctype_snake>.json
- ✅ <doctype_snake>.py (controller)
- ✅ <doctype_snake>.js (client)
- ✅ test_<doctype_snake>.py
- ✅ <doctype_snake>_service.py
- ✅ <doctype_snake>_repository.py

### Next Steps:
1. Run `bench --site <site> migrate` to create database table
2. Add permissions in DocType settings
3. Create any child tables needed
4. Run tests: `bench --site <site> run-tests --doctype "<DocType Name>"`

Rules

  1. v15 Type Annotations — Always include TYPE_CHECKING block with type hints
  2. Multi-Layer Pattern — Create service and repository for every DocType
  3. No Business Logic in Controller — Controllers call services, services implement logic
  4. Comprehensive Tests — Every DocType must have test coverage
  5. Proper Naming — DocType folder/file names must be snake_case
  6. ALWAYS Confirm — Never create files without explicit user approval
  7. Index Planning — Add indexes for frequently filtered fields

平台分布

windsurf

26.86%
按下载量换算160

OpenCode

24.16%
按下载量换算144

Cursor

17.72%
按下载量换算106

Codex

11.64%
按下载量换算69

github-copilot

7%
按下载量换算42

Claude Code

3.49%
按下载量换算21

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills