Token导航 LogoToken导航

Agent Skill · 前端设计

frappe-impl-jinja(冰沙 impl jinja)

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

frappe-impl-jinja 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为操作浏览器;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 6,596。

总安装 485周安装 8GitHub Stars 79下载 65核验 2026-05-01

安装说明

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

安装方式

通过对话安装

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

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

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/openaec-foundation/frappe_claude_skill_package --skill frappe-impl-jinja

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

frappe-impl-jinja 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态、代码变更或协作事项进行整理时使用。

通过 npx skills add 命令从指定 GitHub 仓库安装,需结合原始 README 核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写操作。适用于 Jinja 模板实现和动态内容渲染场景。

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

上手步骤

4 步
  1. 1

    确认自己的宿主工具

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

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/openaec-foundation/frappe_claude_skill_package…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

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

  4. 4

    按最小权限试用

    它需要操作浏览器,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

frappe-impl-jinja 怎么安装?

在终端执行 npx skills add https://github.com/openaec-foundation/frappe_claude_skill_package --skill f…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

frappe-impl-jinja 支持哪些 AI 工具?

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

frappe-impl-jinja 需要什么权限?

权限级别是操作浏览器。该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

frappe-impl-jinja 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

frappe-impl-jinja 用的人多吗?

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

frappe-impl-jinja 安全吗?

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

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

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

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

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
frappe-impl-jinja

冰沙 impl jinja · 本页

48579操作浏览器未明确通过

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

SKILL.md 原文

Frappe Jinja Templates Implementation Workflow

Step-by-step workflows for building Jinja templates. For syntax reference, see frappe-syntax-jinja.

Version: v14/v15/v16 (V16 Chrome PDF noted)


Master Decision: What Are You Creating?

WHAT IS YOUR OUTPUT?
│
├─► Printable PDF (invoice, PO, report)?
│   ├─► Standard DocType → Print Format (Jinja)
│   └─► Query/Script Report → Report Print Format (JAVASCRIPT!)
│       ⚠️ Uses {%= %} NOT {{ }}
│
├─► Automated email with dynamic content?
│   └─► Email Template (Jinja, linked to DocType)
│
├─► System notification?
│   └─► Notification (Setup > Notification, uses Jinja)
│
├─► Customer-facing web page?
│   └─► Portal Page (myapp/www/*.html + *.py)
│
└─► Reusable template functions/filters?
    └─► Custom jenv methods in hooks.py

Workflow 1: Create a Print Format

Step 1: Create via UI

Setup > Printing > Print Format > New
- Name: My Invoice Format
- DocType: Sales Invoice
- Module: Accounts
- Standard: No (custom)
- Print Format Type: Jinja

Step 2: Write the Template

<style>
    .print-format { font-family: Arial, sans-serif; font-size: 11px; }
    .header { margin-bottom: 20px; }
    .table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    .table th, .table td { border: 1px solid #ddd; padding: 8px; }
    .table th { background: #f0f0f0; }
    .text-right { text-align: right; }
</style>

<div class="header">
    <h1>{{ doc.select_print_heading or _("Invoice") }}</h1>
    <p><strong>{{ doc.name }}</strong> |
       {{ doc.get_formatted("posting_date") }}</p>
</div>

<p><strong>{{ doc.customer_name }}</strong></p>
{% if doc.address_display %}
    <p>{{ doc.address_display | safe }}</p>
{% endif %}

<table class="table">
    <thead>
        <tr>
            <th>#</th>
            <th>{{ _("Item") }}</th>
            <th class="text-right">{{ _("Qty") }}</th>
            <th class="text-right">{{ _("Rate") }}</th>
            <th class="text-right">{{ _("Amount") }}</th>
        </tr>
    </thead>
    <tbody>
        {% for row in doc.items %}
        <tr>
            <td>{{ row.idx }}</td>
            <td>{{ row.item_name }}</td>
            <td class="text-right">{{ row.qty }}</td>
            <td class="text-right">{{ row.get_formatted("rate", doc) }}</td>
            <td class="text-right">{{ row.get_formatted("amount", doc) }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

{% for tax in doc.taxes %}
<p class="text-right">{{ tax.description }}: {{ tax.get_formatted("tax_amount", doc) }}</p>
{% endfor %}

<p class="text-right">
    <strong>{{ _("Grand Total") }}: {{ doc.get_formatted("grand_total") }}</strong>
</p>

{% if doc.terms %}
<div style="margin-top: 30px; border-top: 1px solid #ddd; padding-top: 10px;">
    <strong>{{ _("Terms and Conditions") }}</strong>
    {{ doc.terms | safe }}
</div>
{% endif %}

Step 3: Test

  1. Open a Sales Invoice
  2. Menu > Print > Select "My Invoice Format"
  3. Verify layout and formatting
  4. ALWAYS test PDF download — wkhtmltopdf renders differently from browser

Critical Rules for Print Formats

  • ALWAYS use doc.get_formatted("field") for currency, dates, numbers
  • ALWAYS pass parent doc for child rows: row.get_formatted("rate", doc)
  • ALWAYS wrap user-facing text with _("text") for translation
  • ALWAYS put CSS in a <style> block at the top (not external files)
  • NEVER use flexbox in v14/v15 (wkhtmltopdf does not support it) — V16 Chrome PDF does
  • NEVER use | safe on user-supplied input — only on trusted system HTML

Workflow 2: Create an Email Template

Step 1: Create via UI

Setup > Email > Email Template > New
- Name: Payment Reminder
- Subject: Invoice {{ doc.name }} - Payment Reminder
- DocType: Sales Invoice

Step 2: Write Email Content

ALWAYS use inline styles for emails — most clients strip <style> blocks.

<div style="font-family: Arial, sans-serif; max-width: 600px;">
    <p>{{ _("Dear") }} {{ doc.customer_name }},</p>

    <p>{{ _("Invoice") }} <strong>{{ doc.name }}</strong>
    {{ _("for") }} {{ doc.get_formatted("grand_total") }}
    {{ _("is due for payment.") }}</p>

    <table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
        <tr style="background: #f5f5f5;">
            <td style="padding: 10px; border: 1px solid #ddd;">
                <strong>{{ _("Due Date") }}</strong></td>
            <td style="padding: 10px; border: 1px solid #ddd;">
                {{ frappe.format_date(doc.due_date) }}</td>
        </tr>
        <tr>
            <td style="padding: 10px; border: 1px solid #ddd;">
                <strong>{{ _("Outstanding") }}</strong></td>
            <td style="padding: 10px; border: 1px solid #ddd; color: #c00;">
                {{ doc.get_formatted("outstanding_amount") }}</td>
        </tr>
    </table>

    {% if doc.items %}
    <p><strong>{{ _("Items") }}:</strong></p>
    <ul>
    {% for item in doc.items[:5] %}
        <li>{{ item.item_name }} ({{ item.qty }})</li>
    {% endfor %}
    {% if doc.items | length > 5 %}
        <li style="color: #666;">{{ _("and {0} more...").format(doc.items|length - 5) }}</li>
    {% endif %}
    </ul>
    {% endif %}

    <p>{{ _("Best regards") }},<br>
    {{ frappe.db.get_value("Company", doc.company, "company_name") }}</p>
</div>

Step 3: Use in Notification or Code

Option A: Auto-triggered Notification

Setup > Notification > New
- Channel: Email
- Document Type: Sales Invoice
- Send Alert On: Days After (7 days after due_date)
- Condition: doc.outstanding_amount > 0
- Email Template: Payment Reminder

Option B: Send from code

template = frappe.get_doc("Email Template", "Payment Reminder")
frappe.sendmail(
    recipients=[doc.contact_email],
    subject=frappe.render_template(template.subject, {"doc": doc}),
    message=frappe.render_template(template.response, {"doc": doc}),
    reference_doctype=doc.doctype,
    reference_name=doc.name
)

Workflow 3: Create a Notification Template

Step 1: Create via UI

Setup > Notification > New
- Name: Low Stock Alert
- Channel: Email (or Slack, System Notification)
- Document Type: Stock Ledger Entry
- Send Alert On: Method (on change)
- Condition: doc.actual_qty < 10

Step 2: Write Message (Jinja)

<h3>{{ _("Low Stock Alert") }}</h3>
<p>{{ _("Item") }}: <strong>{{ doc.item_code }}</strong></p>
<p>{{ _("Warehouse") }}: {{ doc.warehouse }}</p>
<p>{{ _("Current Stock") }}: {{ doc.actual_qty }}</p>
<p>{{ _("Please reorder.") }}</p>

Workflow 4: Create a Portal Page

Step 1: Create directory structure

myapp/
└── www/
    └── my-orders/
        ├── index.html    # Jinja template
        └── index.py      # Python context

Step 2: Create context (index.py)

import frappe

def get_context(context):
    if frappe.session.user == "Guest":
        frappe.local.flags.redirect_location = "/login"
        raise frappe.Redirect

    context.title = "My Orders"
    context.no_cache = True

    customer = frappe.db.get_value("Contact",
        {"user": frappe.session.user}, "link_name")

    context.orders = frappe.get_all("Sales Order",
        filters={"customer": customer, "docstatus": ["!=", 2]},
        fields=["name", "transaction_date", "grand_total", "status"],
        order_by="transaction_date desc",
        limit=50
    ) if customer else []

    return context

Step 3: Create template (index.html)

{% extends "templates/web.html" %}

{% block title %}{{ _("My Orders") }}{% endblock %}

{% block page_content %}
<div class="container my-4">
    <h1>{{ _("My Orders") }}</h1>

    {% if orders %}
    <table class="table table-hover">
        <thead>
            <tr>
                <th>{{ _("Order") }}</th>
                <th>{{ _("Date") }}</th>
                <th>{{ _("Status") }}</th>
                <th class="text-right">{{ _("Total") }}</th>
            </tr>
        </thead>
        <tbody>
            {% for order in orders %}
            <tr>
                <td><a href="/orders/{{ order.name }}">{{ order.name }}</a></td>
                <td>{{ frappe.format_date(order.transaction_date) }}</td>
                <td>{{ order.status }}</td>
                <td class="text-right">
                    {{ frappe.format(order.grand_total, {"fieldtype": "Currency"}) }}
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    {% else %}
    <p class="text-muted">{{ _("No orders found.") }}</p>
    {% endif %}
</div>
{% endblock %}

Step 4: Test at https://yoursite.com/my-orders


Workflow 5: Register Custom Jinja Methods

Step 1: Add to hooks.py

jenv = {
    "methods": ["myapp.jinja_utils.methods"],
    "filters": ["myapp.jinja_utils.filters"]
}

Step 2: Create methods module

# myapp/jinja_utils/methods.py
import frappe

def get_company_logo(company):
    """Usage: {{ get_company_logo(doc.company) }}"""
    return frappe.db.get_value("Company", company, "company_logo") or ""

def format_address(address_name):
    """Usage: {{ format_address(doc.customer_address) | safe }}"""
    if not address_name:
        return ""
    return frappe.get_doc("Address", address_name).get_display()

Step 3: Create filters module

# myapp/jinja_utils/filters.py
def phone_format(value):
    """Usage: {{ doc.phone | phone_format }}"""
    if not value:
        return ""
    digits = ''.join(c for c in str(value) if c.isdigit())
    if len(digits) == 10:
        return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"
    return value

Step 4: Deploy

bench --site sitename migrate
bench --site sitename clear-cache

Critical Rules for Custom Jinja Methods

  • Custom methods should be READ-ONLY — NEVER write to database or commit
  • ALWAYS handle None/empty input gracefully (return empty string)
  • NEVER call slow external APIs — templates must render fast

Workflow 6: Debug a Template

Template Not Rendering?

<!-- Step 1: Check if doc is available -->
<!-- DEBUG: {{ doc.name if doc else 'NO DOC' }} -->

<!-- Step 2: Check child table -->
<!-- DEBUG: items count = {{ doc.items | length if doc.items else 0 }} -->

<!-- Step 3: Check specific field -->
<!-- DEBUG: grand_total = {{ doc.grand_total }} -->

Common Debugging Steps

  1. Check Error Log (Setup > Error Log) for template exceptions
  2. Use frappe.render_template(template_string, {"doc": doc}) in bench console
  3. For Print Formats: Menu > Print > check browser console for errors
  4. For Portal Pages: check Python context — add frappe.logger().info(context) in get_context

Common Pitfalls

SymptomCauseFix
Blank outputWrong template type (Jinja in Report)Reports use JS: {%= %}
"None" displayedField is nullUse `\default('')`
Wrong currency formatMissing parent doc contextUse row.get_formatted("rate", doc)
HTML showing as textAuto-escapingAdd `\safe` (trusted content only)
Translations not workingMissing _() wrapperWrap all strings: {{_("text")}}

Quick Patterns: Child Tables, Conditionals, Translation

{# Child tables — ALWAYS pass parent doc for formatting context #}
{% for row in doc.items %}
  {{ row.get_formatted("rate", doc) }}  {# Correct: has currency context #}
{% endfor %}

{# Conditional sections #}
{% if doc.shipping_address_name %}
  {{ doc.shipping_address | safe }}
{% endif %}

{# Translation — ALWAYS wrap user-facing text #}
{{ _("Invoice") }}
{{ _("Page {0} of {1}").format(page, total_pages) }}
{{ doc.get_formatted("grand_total") }}  {# Auto-formats per locale #}

Styling/CSS in Print Formats

@page { margin: 1.5cm; }
.avoid-break { page-break-inside: avoid; }
thead { display: table-header-group; }   /* Repeat header on pages */
.page-break { page-break-before: always; }
/* V14/V15: NO flexbox (wkhtmltopdf). V16 Chrome PDF: flexbox OK */
.layout { display: table; width: 100%; }
.col { display: table-cell; vertical-align: top; }

Context Variables Quick Reference

Template TypeAvailable Objects
Print Formatdoc, frappe, _(), frappe.format()
Email Templatedoc, frappe (limited), _()
Notificationdoc, frappe, event data
Portal Pagefrappe.session, frappe.form_dict, custom context

Version Differences

FeatureV14V15V16
Jinja templatesYesYesYes
get_formatted()YesYesYes
jenv hooksYesYesYes
wkhtmltopdf PDFYesYesDeprecated
Chrome PDFNoNoYes
V16 Chrome PDF supports modern CSS (flexbox, grid, CSS variables). See frappe-syntax-jinja for details.

Reference Files

FileContents
decision-tree.mdComplete template type selection flowcharts
print-format-decision.mdJinja vs Print Designer vs JS Microtemplate decision tree
workflows.mdStep-by-step patterns for all template types
examples.mdProduction-ready templates (invoice, email, portal)

平台分布

Codex

36.34%
按下载量换算24

Claude

30.4%
按下载量换算20

Cursor

18.72%
按下载量换算12

Gemini CLI

9.6%
按下载量换算6

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills