Claude-skill-registry erpnext-code-validator
Intelligent agent for validating ERPNext/Frappe code against best practices and common pitfalls. Use when reviewing generated code, checking for errors before deployment, or validating code quality. Triggers: review this code, check my script, validate before deployment, is this correct, find bugs, check for errors, will this work.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/erpnext-code-validator" ~/.claude/skills/majiayu000-claude-skill-registry-erpnext-code-validator && rm -rf "$T"
manifest:
skills/data/erpnext-code-validator/SKILL.mdsource content
ERPNext Code Validator Agent
This agent validates ERPNext/Frappe code against established patterns, common pitfalls, and version compatibility requirements.
Purpose: Catch errors BEFORE deployment, not after
When to Use This Agent
┌─────────────────────────────────────────────────────────────────────┐ │ CODE VALIDATION TRIGGERS │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ► Code has been generated and needs review │ │ "Check this Server Script before I save it" │ │ └── USE THIS AGENT │ │ │ │ ► Code is causing errors │ │ "Why isn't this working?" │ │ └── USE THIS AGENT │ │ │ │ ► Pre-deployment validation │ │ "Is this production-ready?" │ │ └── USE THIS AGENT │ │ │ │ ► Code review for best practices │ │ "Can this be improved?" │ │ └── USE THIS AGENT │ │ │ └─────────────────────────────────────────────────────────────────────┘
Validation Workflow
┌─────────────────────────────────────────────────────────────────────┐ │ CODE VALIDATOR WORKFLOW │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ STEP 1: IDENTIFY CODE TYPE │ │ ══════════════════════════ │ │ • Client Script (JavaScript) │ │ • Server Script (Python sandbox) │ │ • Controller (Python full) │ │ • hooks.py configuration │ │ • Jinja template │ │ • Whitelisted method │ │ │ │ STEP 2: RUN TYPE-SPECIFIC CHECKS │ │ ═════════════════════════════════ │ │ • Apply checklist for identified code type │ │ • Check syntax patterns │ │ • Verify API usage │ │ │ │ STEP 3: CHECK UNIVERSAL RULES │ │ ══════════════════════════════ │ │ • Error handling present │ │ • User feedback appropriate │ │ • Security considerations │ │ • Performance implications │ │ │ │ STEP 4: VERIFY VERSION COMPATIBILITY │ │ ════════════════════════════════════ │ │ • v14/v15/v16 specific features │ │ • Deprecated patterns │ │ • Version-specific behaviors │ │ │ │ STEP 5: GENERATE VALIDATION REPORT │ │ ══════════════════════════════════ │ │ • Critical errors (must fix) │ │ • Warnings (should fix) │ │ • Suggestions (nice to have) │ │ • Corrected code (if errors found) │ │ │ └─────────────────────────────────────────────────────────────────────┘
→ See references/workflow.md for detailed validation steps.
Critical Checks by Code Type
Server Script Checks
┌─────────────────────────────────────────────────────────────────────┐ │ ⚠️ SERVER SCRIPT CRITICAL CHECKS │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ [FATAL] Import statements │ │ ═══════════════════════════ │ │ ❌ import json → Use frappe.parse_json() │ │ ❌ from frappe.utils import X → Use frappe.utils.X() │ │ ❌ import requests → IMPOSSIBLE in Server Script │ │ │ │ [FATAL] Undefined variables │ │ ════════════════════════════ │ │ ❌ self.field → Use doc.field │ │ ❌ document.field → Use doc.field │ │ │ │ [FATAL] Wrong event handling │ │ ═══════════════════════════════ │ │ ❌ try/except for validation → Just frappe.throw() │ │ │ │ [ERROR] Event name mismatch │ │ ═══════════════════════════ │ │ ❌ Event "Before Save" code in "After Save" script │ │ │ │ [WARNING] Missing validation │ │ ═══════════════════════════════ │ │ ⚠️ No null/empty checks before operations │ │ │ └─────────────────────────────────────────────────────────────────────┘
Client Script Checks
┌─────────────────────────────────────────────────────────────────────┐ │ CLIENT SCRIPT CRITICAL CHECKS │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ [FATAL] Wrong API usage │ │ ═════════════════════════ │ │ ❌ frappe.db.get_value() → Server-side only! │ │ ❌ frappe.get_doc() → Server-side only! │ │ ✓ frappe.call() for server data │ │ │ │ [FATAL] Missing async handling │ │ ══════════════════════════════ │ │ ❌ let result = frappe.call() → Returns undefined │ │ ✓ frappe.call({callback: fn}) → Use callback │ │ ✓ await frappe.call({async:false}) → Or async/await │ │ │ │ [ERROR] Field refresh issues │ │ ════════════════════════════ │ │ ❌ frm.set_value() without refresh │ │ ✓ frm.set_value() then frm.refresh_field() │ │ │ │ [WARNING] Form state checks │ │ ═══════════════════════════ │ │ ⚠️ Not checking frm.doc.__islocal for new docs │ │ ⚠️ Not checking frm.doc.docstatus for submitted docs │ │ │ └─────────────────────────────────────────────────────────────────────┘
Controller Checks
┌─────────────────────────────────────────────────────────────────────┐ │ CONTROLLER CRITICAL CHECKS │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ [FATAL] Wrong lifecycle usage │ │ ═════════════════════════════ │ │ ❌ Modifying self.field in on_update → Changes NOT saved! │ │ ✓ Use frappe.db.set_value() in on_update │ │ │ │ [FATAL] Missing super() call │ │ ════════════════════════════ │ │ ❌ def validate(self): pass → Breaks parent validation │ │ ✓ def validate(self): super().validate() │ │ │ │ [ERROR] Transaction assumptions │ │ ═══════════════════════════════ │ │ ❌ Assuming rollback on error in on_update │ │ (only validate and before_* roll back on error) │ │ │ │ [ERROR] Circular save │ │ ══════════════════════ │ │ ❌ self.save() inside lifecycle hooks │ │ ❌ doc.save() for same document in hooks │ │ │ └─────────────────────────────────────────────────────────────────────┘
→ See references/checklists.md for complete checklists.
Validation Report Format
## Code Validation Report ### Code Type: [Server Script / Client Script / Controller / etc.] ### Target DocType: [DocType name] ### Event/Trigger: [Event name] --- ### 🔴 CRITICAL ERRORS (Must Fix) | Line | Issue | Fix | |------|-------|-----| | 3 | Import statement in Server Script | Use frappe.utils.X() directly | ### 🟡 WARNINGS (Should Fix) | Line | Issue | Recommendation | |------|-------|----------------| | 12 | No null check before .lower() | Add: if value: value.lower() | ### 🔵 SUGGESTIONS (Nice to Have) | Line | Suggestion | |------|------------| | 8 | Consider using frappe.db.get_value for single field | --- ### Corrected Code ```python # [Corrected version with all critical errors fixed]
Version Compatibility
| Version | Status |
|---|---|
| v14 | ✅ Compatible |
| v15 | ✅ Compatible |
| v16 | ✅ Compatible |
## Universal Validation Rules These apply to ALL code types: ### Security Checks | Check | Severity | Description | |-------|----------|-------------| | SQL Injection | CRITICAL | Raw user input in SQL queries | | Permission bypass | CRITICAL | Missing permission checks before operations | | XSS vulnerability | HIGH | Unescaped user input in HTML | | Sensitive data exposure | HIGH | Logging passwords/tokens | ### Error Handling Checks | Check | Severity | Description | |-------|----------|-------------| | Silent failures | HIGH | Catching exceptions without handling | | Missing user feedback | MEDIUM | Errors not communicated to user | | Generic error messages | LOW | "An error occurred" without details | ### Performance Checks | Check | Severity | Description | |-------|----------|-------------| | Query in loop | HIGH | frappe.db.* inside for loop | | Unbounded query | MEDIUM | SELECT without LIMIT | | Unnecessary get_doc | LOW | get_doc when get_value suffices | → See [references/examples.md](references/examples.md) for validation examples. ## Version-Specific Validations ### v16 Features (Fail on v14/v15) ```python # These ONLY work on v16+ extend_doctype_class = {} # hooks.py - v16 only naming_rule = "UUID" # DocType - v16 only pdf_renderer = "chrome" # Print Format - v16 only
Deprecated Patterns (Warn)
# DEPRECATED - still works but should update frappe.bean() # Use frappe.get_doc() frappe.msgprint(raise_exception=True) # Use frappe.throw() job_name parameter # Use job_id (v15+)
Version-Specific Behaviors
| Behavior | v14 | v15/v16 |
|---|---|---|
| Scheduler tick | 240s | 60s |
| Background job dedup | job_name | job_id |
Quick Validation Commands
Server Script Quick Check
- ❌ Any
statements? → FATALimport - ❌ Any
references? → FATAL (useself.
)doc. - ❌ Any
? → WARNING (usually wrong)try/except - ✅ Uses
for validation errors? → GOODfrappe.throw() - ✅ Uses
for document access? → GOODdoc.field
Client Script Quick Check
- ❌ Any
calls? → FATAL (server-side only)frappe.db.* - ❌ Any
calls? → FATAL (server-side only)frappe.get_doc() - ❌
without callback? → FATAL (async issue)frappe.call() - ✅ Uses
for field access? → GOODfrm.doc.field - ✅ Uses
after changes? → GOODfrm.refresh_field()
Controller Quick Check
- ❌ Modifying
inself.*
? → ERROR (won't save)on_update - ❌ Missing
calls? → WARNINGsuper().method() - ❌
in lifecycle hook? → FATAL (circular)self.save() - ✅ Imports at top of file? → GOOD (controllers allow imports)
- ✅ Error handling with try/except? → GOOD (controllers allow this)
Integration with Other Skills
This validator uses knowledge from:
| Skill | What It Provides |
|---|---|
| Correct syntax patterns |
| Correct implementation patterns |
| Error handling patterns |
| Query patterns and pitfalls |
| Permission check patterns |
| API response patterns |
Validation Depth Levels
| Level | Checks | Use When |
|---|---|---|
| Quick | Fatal errors only | Initial scan |
| Standard | + Warnings | Pre-deployment |
| Deep | + Suggestions + Optimization | Production review |
Default: Standard level for most validations.