codymaster 4.1.4 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -2
- package/README.md +54 -84
- package/dist/index.js +118 -4
- package/install.sh +2 -2
- package/package.json +3 -8
- package/skills/AGENTS.md +61 -0
- package/skills/CLAUDE.md +158 -0
- package/skills/boxme-git-config/SKILL.md +56 -0
- package/skills/boxme-local-dev/SKILL.md +66 -0
- package/skills/build.sh +30 -0
- package/skills/cf +314 -0
- package/skills/cf 2 +313 -0
- package/skills/cm-ads-tracker/SKILL.md +364 -69
- package/skills/cm-auto-publisher/SKILL.md +81 -0
- package/skills/cm-booking-calendar/SKILL.md +521 -0
- package/skills/cm-booking-calendar/references/industry-patterns.md +527 -0
- package/skills/cm-booking-calendar/templates/booking-form.css +626 -0
- package/skills/cm-booking-calendar/templates/booking-form.html +477 -0
- package/skills/cm-booking-calendar/templates/calendar-engine.js +419 -0
- package/skills/cm-booking-calendar/templates/calendar-export.js +395 -0
- package/skills/cm-booking-calendar/templates/reminder-config.js +629 -0
- package/skills/cm-brainstorm-idea/SKILL.md +15 -24
- package/skills/cm-clean-code/SKILL.md +300 -0
- package/skills/cm-code-review/SKILL.md +0 -27
- package/skills/cm-codeintell/SKILL.md +598 -0
- package/skills/cm-content-factory/.content-factory-state.json +132 -0
- package/skills/cm-content-factory/.git 2/logs/refs/heads/main +1 -0
- package/skills/cm-content-factory/.git 2/logs/refs/remotes/origin/main +1 -0
- package/skills/cm-content-factory/.git 2/objects/02/fb0956734b5f8ba3f918b7defd04a89cfe0076 +0 -0
- package/skills/cm-content-factory/.git 2/objects/08/1e129d75dc6feac6c02037272e6bd1a04e3324 +0 -0
- package/skills/cm-content-factory/.git 2/objects/0c/5393416f3c5e01c9a655a802bff0dd52f76f0a +0 -0
- package/skills/cm-content-factory/.git 2/objects/10/0b9be46978a946a77188f68be725098a122001 +0 -0
- package/skills/cm-content-factory/.git 2/objects/10/cf041167fc9843610eb3d90259ef3396315fdc +0 -0
- package/skills/cm-content-factory/.git 2/objects/12/5e19538dd6e1338ffe74f6c4c165b00435bf48 +0 -0
- package/skills/cm-content-factory/.git 2/objects/16/a9b9d0088d5c1347628b45a2620b479d8ad57c +0 -0
- package/skills/cm-content-factory/.git 2/objects/17/8c2a9ef93c33ae4eec9d58e82321f9229843a1 +0 -0
- package/skills/cm-content-factory/.git 2/objects/25/397ae41d09104d763bdcac2695209d85cdea89 +0 -0
- package/skills/cm-content-factory/.git 2/objects/2f/a836b7947f2d458e1f639788bf4bb0983a3305 +0 -0
- package/skills/cm-content-factory/.git 2/objects/3a/baaaf0a1c0909c0828335791557125fba911e0 +0 -0
- package/skills/cm-content-factory/.git 2/objects/42/2924221b81f5ce3c4e4daac9a64a24f9b01f9a +0 -0
- package/skills/cm-content-factory/.git 2/objects/42/ec0ce707447dc11446a34c9995fb8533801731 +0 -0
- package/skills/cm-content-factory/.git 2/objects/46/e43ce92866d56ce74b1d750db307cfe6154a15 +0 -0
- package/skills/cm-content-factory/.git 2/objects/48/5e41b633c63f55b8277bcc59f44f67681f671a +0 -0
- package/skills/cm-content-factory/.git 2/objects/49/49c596a3a89fa240642acd95dd3258e261eb09 +0 -0
- package/skills/cm-content-factory/.git 2/objects/50/9d42d8412ef8eaf7f7e138476bac2e4d10ce60 +0 -0
- package/skills/cm-content-factory/.git 2/objects/55/0c8c389d981b463ef849aeb792d8be3ccb6ec8 +0 -0
- package/skills/cm-content-factory/.git 2/objects/5d/82d3b18410cdda3ace3677436f0cb599dbe2d2 +0 -0
- package/skills/cm-content-factory/.git 2/objects/60/0617c58e871a38b33bf29e282d132bb3c381ad +0 -0
- package/skills/cm-content-factory/.git 2/objects/6a/8369a99c687b7245c92ffaf0e0f0dab9014504 +0 -0
- package/skills/cm-content-factory/.git 2/objects/79/bea435d40ab531c1aaf6be0432c6a5b7aaed21 +0 -0
- package/skills/cm-content-factory/.git 2/objects/7e/5ebd79251c2f14e4aceb86c74b6b6daae6b500 +0 -0
- package/skills/cm-content-factory/.git 2/objects/81/98a822a60178d6d5023ddb3e222cddf048742e +0 -0
- package/skills/cm-content-factory/.git 2/objects/86/0a0e1943dfe53411d2e499a1f16f46a96ef758 +0 -0
- package/skills/cm-content-factory/.git 2/objects/86/971fb55fdc081fdbae52376f0f13e57a4e9b04 +0 -0
- package/skills/cm-content-factory/.git 2/objects/88/b89dd609a0a03f8d4fe8bfde20d5b8fc1d326d +0 -0
- package/skills/cm-content-factory/.git 2/objects/90/8737edb6b7809e32cc01590b4e08ba42a9d40d +0 -0
- package/skills/cm-content-factory/.git 2/objects/93/d5a8a9a7d4fb7f11491cb596a6880528725118 +0 -0
- package/skills/cm-content-factory/.git 2/objects/98/46a2ab81d0c3b3eb00ef88fc56989aa7e9f316 +0 -0
- package/skills/cm-content-factory/.git 2/objects/9b/d8dd1e49cf274eaf9c555f3ab39dce7af5715e +0 -0
- package/skills/cm-content-factory/.git 2/objects/a1/13329fb0cec96ae78b222d33a24c3b5bc7fa1f +0 -0
- package/skills/cm-content-factory/.git 2/objects/a9/e6effe626e8a3aea3a8fc3364b492191c6e7d0 +0 -0
- package/skills/cm-content-factory/.git 2/objects/ad/6de7e48d9782cca9353d1ff0aa1aab7fe1df85 +0 -0
- package/skills/cm-content-factory/.git 2/objects/af/54ae316f771ff692e299ffcd8bf2f06b413b59 +0 -0
- package/skills/cm-content-factory/.git 2/objects/b0/4cb8b0b00dad633e731c1472161419e738d674 +0 -0
- package/skills/cm-content-factory/.git 2/objects/b3/094abb0b9ed46419b269e4a4e36a459690e3b0 +0 -0
- package/skills/cm-content-factory/.git 2/objects/b9/435c5d4baac2cfc5c83009ddd27b46b60db5f1 +0 -0
- package/skills/cm-content-factory/.git 2/objects/ba/5da17dbaec5ec2dcfdfd126aead518d1171d5c +0 -0
- package/skills/cm-content-factory/.git 2/objects/c0/bf58703aa258ba5dd63083bebaec8f223d844c +0 -0
- package/skills/cm-content-factory/.git 2/objects/c4/701a34edf1fc1bad58ccc57bd03f9426acb59a +0 -0
- package/skills/cm-content-factory/.git 2/objects/c7/5ccce9a4e5cc74d9b3174550cf6d993ca43638 +0 -0
- package/skills/cm-content-factory/.git 2/objects/c7/710d59b5a35b0f1f0a0399386643a0bd94c929 +0 -0
- package/skills/cm-content-factory/.git 2/objects/d1/fe58237112e953e5fec52da22cf38e08be3df9 +5 -0
- package/skills/cm-content-factory/.git 2/objects/d2/2bbe9fd2f74c95bc5583e803f5e435f1e2cd86 +0 -0
- package/skills/cm-content-factory/.git 2/objects/d7/e72852ea2bff74581dbf247d400120086229f4 +0 -0
- package/skills/cm-content-factory/.git 2/objects/d8/d4c3b5553e4fd72807e1d4b49ef07d9ef3ac35 +0 -0
- package/skills/cm-content-factory/.git 2/objects/dc/75050c2876f6a02ae2a53a3c886f395b622977 +0 -0
- package/skills/cm-content-factory/.git 2/objects/ee/e8546f95acec500187c08a28a8b9ee02db0dec +0 -0
- package/skills/cm-content-factory/.git 2/objects/ef/263c059208b416c2146434f10cb2b9fabcba16 +0 -0
- package/skills/cm-content-factory/.git 2/objects/f3/ae597e84d9a59b88acd21c99bde2eaf686d785 +0 -0
- package/skills/cm-content-factory/.git 2/objects/f3/f6f5673c821d3d8e76fa267a9e882e7a5387ea +0 -0
- package/skills/cm-content-factory/.git 2/objects/f9/6e6d0ad02624dd11d5848594d056caef7a5e8b +0 -0
- package/skills/cm-content-factory/.git 2/objects/ff/278988fc1edf0db3abcf18de795f4cc0b4f3e1 +0 -0
- package/skills/cm-content-factory/.git 2/refs/heads/main +1 -0
- package/skills/cm-content-factory/.git 2/refs/remotes/origin/main +1 -0
- package/skills/cm-content-factory/.pytest_cache 2/v/cache/nodeids +76 -0
- package/skills/cm-content-factory/.pytest_cache 2/v/cache/stepwise +1 -0
- package/skills/cm-content-factory/AGENTS.md +61 -0
- package/skills/cm-content-factory/CLAUDE.md +63 -0
- package/skills/cm-content-factory/CURSOR.md +43 -0
- package/skills/cm-content-factory/Content Factory.zip +0 -0
- package/skills/cm-content-factory/cf +313 -0
- package/skills/cm-content-factory/config.schema.json +397 -0
- package/skills/cm-content-factory/dashboard/app.js +556 -0
- package/skills/cm-content-factory/dashboard/index.html +397 -0
- package/skills/cm-content-factory/dashboard/style.css +1211 -0
- package/skills/cm-content-factory/examples/01-real-estate.config.json +146 -0
- package/skills/cm-content-factory/examples/02-personal-finance.config.json +146 -0
- package/skills/cm-content-factory/examples/03-health-wellness.config.json +147 -0
- package/skills/cm-content-factory/examples/04-saas-software.config.json +147 -0
- package/skills/cm-content-factory/examples/05-legal-services.config.json +147 -0
- package/skills/cm-content-factory/examples/06-insurance.config.json +146 -0
- package/skills/cm-content-factory/examples/07-ecommerce-dropship.config.json +146 -0
- package/skills/cm-content-factory/examples/08-online-education.config.json +147 -0
- package/skills/cm-content-factory/examples/09-crypto-defi.config.json +147 -0
- package/skills/cm-content-factory/examples/10-beauty-skincare.config.json +147 -0
- package/skills/cm-content-factory/examples/11-home-services.config.json +146 -0
- package/skills/cm-content-factory/examples/12-dental-clinic.config.json +147 -0
- package/skills/cm-content-factory/examples/13-pet-care.config.json +147 -0
- package/skills/cm-content-factory/examples/14-travel-hospitality.config.json +147 -0
- package/skills/cm-content-factory/examples/15-ai-automation.config.json +147 -0
- package/skills/cm-content-factory/examples/16-wedding-events.config.json +147 -0
- package/skills/cm-content-factory/examples/17-fitness-coaching.config.json +148 -0
- package/skills/cm-content-factory/examples/18-cybersecurity.config.json +147 -0
- package/skills/cm-content-factory/examples/19-food-restaurant.config.json +148 -0
- package/skills/cm-content-factory/examples/20-solar-energy.config.json +147 -0
- package/skills/cm-content-factory/examples/fitness-blog.config.json +116 -0
- package/skills/cm-content-factory/examples/tech-blog.config.json +107 -0
- package/skills/cm-content-factory/extensions/EXTENSION_GUIDE.md +72 -0
- package/skills/cm-content-factory/extensions/hooks.py +126 -0
- package/skills/cm-content-factory/extensions/openclaw_adapter.py +132 -0
- package/skills/cm-content-factory/landing/index.html +680 -0
- package/skills/cm-content-factory/landing/script.js +101 -0
- package/skills/cm-content-factory/landing/style.css +1216 -0
- package/skills/cm-content-factory/landing/translations.js +508 -0
- package/skills/cm-content-factory/logs/events.jsonl +11 -0
- package/skills/cm-content-factory/profiles/_template.profile.json +231 -0
- package/skills/cm-content-factory/profiles/finance.profile.json +278 -0
- package/skills/cm-content-factory/profiles/legal.profile.json +263 -0
- package/skills/cm-content-factory/profiles/medical-research.profile.json +321 -0
- package/skills/cm-content-factory/profiles/technology.profile.json +275 -0
- package/skills/cm-content-factory/scripts/agent_dispatcher.py +266 -0
- package/skills/cm-content-factory/scripts/audit.py +106 -0
- package/skills/cm-content-factory/scripts/dashboard_server.py +225 -0
- package/skills/cm-content-factory/scripts/deploy.py +146 -0
- package/skills/cm-content-factory/scripts/extract.py +132 -0
- package/skills/cm-content-factory/scripts/landing_generator.py +459 -0
- package/skills/cm-content-factory/scripts/memory.py +521 -0
- package/skills/cm-content-factory/scripts/monetize.py +239 -0
- package/skills/cm-content-factory/scripts/pipeline.py +357 -0
- package/skills/cm-content-factory/scripts/plan.py +163 -0
- package/skills/cm-content-factory/scripts/publish.py +145 -0
- package/skills/cm-content-factory/scripts/research.py +337 -0
- package/skills/cm-content-factory/scripts/scaffold.py +464 -0
- package/skills/cm-content-factory/scripts/scoreboard.py +336 -0
- package/skills/cm-content-factory/scripts/seo.py +90 -0
- package/skills/cm-content-factory/scripts/state_manager.py +320 -0
- package/skills/cm-content-factory/scripts/token_manager.py +268 -0
- package/skills/cm-content-factory/scripts/validate.py +221 -0
- package/skills/cm-content-factory/scripts/wizard.py +329 -0
- package/skills/cm-content-factory/scripts/write.py +93 -0
- package/skills/cm-content-factory/sites/docs-site/src/assets/houston.webp +0 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/architecture.md +90 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/data-flow.md +54 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/deployment.md +38 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/index.md +65 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/lc-content-lifecycle.md +48 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/seq-write-mode.md +39 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/uj-first-batch.md +42 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/wf-content-pipeline.md +51 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/wf-learning-cycle.md +52 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/getting-started/configuration.md +86 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/getting-started/installation.md +80 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/getting-started/intro.md +58 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/index.md +102 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/index.md +45 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/optimize-seo.md +29 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/scale-content-production.md +55 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/standardize-quality.md +29 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/buyer-cmo-huong.md +41 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/buyer-content-lead-khoa.md +40 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/index.md +56 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/user-content-manager-lan.md +46 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/user-seo-minh.md +45 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/user-writer-tu.md +45 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/content-pipeline.md +108 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/index.md +22 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/memory-system.md +52 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/seo-optimization.md +58 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/troubleshooting-guide.md +92 -0
- package/skills/cm-content-factory/sites/docs-site/src/styles/custom.css +575 -0
- package/skills/cm-content-factory/tests/conftest.py +66 -0
- package/skills/cm-content-factory/tests/test_agent_dispatcher.py +125 -0
- package/skills/cm-content-factory/tests/test_memory.py +128 -0
- package/skills/cm-content-factory/tests/test_pipeline.py +107 -0
- package/skills/cm-content-factory/tests/test_research.py +56 -0
- package/skills/cm-content-factory/tests/test_state_manager.py +131 -0
- package/skills/cm-content-factory/tests/test_token_manager.py +110 -0
- package/skills/cm-content-factory/tests/test_wizard.py +121 -0
- package/skills/cm-continuity/SKILL.md +7 -0
- package/skills/cm-cro-methodology/SKILL.md +290 -0
- package/skills/cm-dashboard/SKILL.md +7 -525
- package/skills/cm-debugging/SKILL.md +7 -116
- package/skills/cm-deep-search/SKILL.md +5 -1
- package/skills/cm-dockit/README.md +6 -15
- package/skills/cm-dockit/SKILL.md +20 -37
- package/skills/cm-execution/SKILL.md +6 -1
- package/skills/cm-frappe-agent/SKILL.md +134 -0
- package/skills/cm-frappe-agent/agents/doctype-architect.md +596 -0
- package/skills/cm-frappe-agent/agents/erpnext-customizer.md +643 -0
- package/skills/cm-frappe-agent/agents/frappe-backend.md +814 -0
- package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +557 -0
- package/skills/cm-frappe-agent/agents/frappe-debugger.md +625 -0
- package/skills/cm-frappe-agent/agents/frappe-fixer.md +275 -0
- package/skills/cm-frappe-agent/agents/frappe-frontend.md +660 -0
- package/skills/cm-frappe-agent/agents/frappe-installer.md +158 -0
- package/skills/cm-frappe-agent/agents/frappe-performance.md +307 -0
- package/skills/cm-frappe-agent/agents/frappe-planner.md +419 -0
- package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +153 -0
- package/skills/cm-frappe-agent/agents/github-workflow.md +286 -0
- package/skills/cm-frappe-agent/commands/frappe-app.md +351 -0
- package/skills/cm-frappe-agent/commands/frappe-backend.md +162 -0
- package/skills/cm-frappe-agent/commands/frappe-bench.md +254 -0
- package/skills/cm-frappe-agent/commands/frappe-debug.md +263 -0
- package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +272 -0
- package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +310 -0
- package/skills/cm-frappe-agent/commands/frappe-erpnext.md +210 -0
- package/skills/cm-frappe-agent/commands/frappe-fix.md +59 -0
- package/skills/cm-frappe-agent/commands/frappe-frontend.md +210 -0
- package/skills/cm-frappe-agent/commands/frappe-fullstack.md +243 -0
- package/skills/cm-frappe-agent/commands/frappe-github.md +57 -0
- package/skills/cm-frappe-agent/commands/frappe-install.md +52 -0
- package/skills/cm-frappe-agent/commands/frappe-plan.md +442 -0
- package/skills/cm-frappe-agent/commands/frappe-remote.md +58 -0
- package/skills/cm-frappe-agent/commands/frappe-test.md +356 -0
- package/skills/cm-frappe-agent/docs/README.md +51 -0
- package/skills/cm-frappe-agent/docs/agents-catalog.md +113 -0
- package/skills/cm-frappe-agent/docs/architecture.md +149 -0
- package/skills/cm-frappe-agent/docs/commands-catalog.md +82 -0
- package/skills/cm-frappe-agent/docs/resources-catalog.md +66 -0
- package/skills/cm-frappe-agent/docs/sitemap-urls.txt +52 -0
- package/skills/cm-frappe-agent/docs/sitemap.md +81 -0
- package/skills/cm-frappe-agent/docs/sop/user-guide.md +178 -0
- package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +122 -0
- package/skills/cm-frappe-agent/resources/7-layer-architecture.md +985 -0
- package/skills/cm-frappe-agent/resources/bench_commands.md +73 -0
- package/skills/cm-frappe-agent/resources/code-patterns-guide.md +948 -0
- package/skills/cm-frappe-agent/resources/common_pitfalls.md +266 -0
- package/skills/cm-frappe-agent/resources/doctype-registry.md +158 -0
- package/skills/cm-frappe-agent/resources/installation-guide.md +289 -0
- package/skills/cm-frappe-agent/resources/rest-api-patterns.md +182 -0
- package/skills/cm-frappe-agent/resources/scaffold_checklist.md +82 -0
- package/skills/cm-frappe-agent/resources/upgrade_patterns.md +113 -0
- package/skills/cm-frappe-agent/resources/web-form-patterns.md +252 -0
- package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +621 -0
- package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +642 -0
- package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +576 -0
- package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +740 -0
- package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +47 -0
- package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +608 -0
- package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +46 -0
- package/skills/cm-git-worktrees/SKILL.md +0 -7
- package/skills/cm-google-form/SKILL.md +266 -0
- package/skills/cm-google-form/templates/apps-script.js +55 -0
- package/skills/cm-google-form/templates/form-markup.html +110 -0
- package/skills/cm-google-form/templates/form-submit.js +201 -0
- package/skills/cm-google-form/templates/toast.css +152 -0
- package/skills/cm-growth-hacking/SKILL.md +293 -0
- package/skills/cm-growth-hacking/bottom-sheet-engine.md +261 -0
- package/skills/cm-growth-hacking/calendar-integration.md +264 -0
- package/skills/cm-growth-hacking/references/engagement-patterns.md +346 -0
- package/skills/cm-growth-hacking/templates/bottom-sheet.css +528 -0
- package/skills/cm-growth-hacking/templates/bottom-sheet.js +269 -0
- package/skills/cm-growth-hacking/templates/calendar-cta.js +213 -0
- package/skills/cm-growth-hacking/templates/tracking-events.js +211 -0
- package/skills/cm-growth-hacking/templates/trigger-manager.js +254 -0
- package/skills/cm-growth-hacking/tracking-events.md +246 -0
- package/skills/cm-growth-hacking/trigger-system.md +342 -0
- package/skills/cm-how-it-work/SKILL.md +20 -4
- package/skills/cm-identity-guard/SKILL.md +0 -11
- package/skills/cm-jtbd/SKILL.md +1 -1
- package/skills/cm-notebooklm/SKILL.md +172 -0
- package/skills/cm-notebooklm/references/command_reference.md +94 -0
- package/skills/cm-notebooklm/references/workflows.md +60 -0
- package/skills/cm-notebooklm/resources/knowledge_sources.md +106 -0
- package/skills/cm-notebooklm/scripts/brain-sync.sh +453 -0
- package/skills/cm-notebooklm/scripts/graduate_wisdom.py +101 -0
- package/skills/cm-planning/SKILL.md +39 -52
- package/skills/cm-project-bootstrap/SKILL.md +1307 -99
- package/skills/cm-quality-gate/SKILL.md +13 -106
- package/skills/cm-reactor/SKILL.md +274 -0
- package/skills/cm-safe-deploy/SKILL.md +415 -52
- package/skills/cm-safe-i18n/SKILL.md +1 -22
- package/skills/cm-secret-shield/SKILL.md +2 -2
- package/skills/cm-security-gate/SKILL.md +114 -0
- package/skills/cm-skill-chain/SKILL.md +2 -2
- package/skills/cm-skill-index/SKILL.md +9 -6
- package/skills/cm-skill-mastery/SKILL.md +2 -15
- package/skills/cm-start/SKILL.md +9 -0
- package/skills/cm-tdd/SKILL.md +16 -49
- package/skills/cm-ui-preview/SKILL.md +35 -173
- package/skills/cm-ux-master/FEATURES-v4.md +305 -0
- package/skills/cm-ux-master/README-ru.md +135 -0
- package/skills/cm-ux-master/README-vi.md +135 -0
- package/skills/cm-ux-master/README-zh.md +135 -0
- package/skills/cm-ux-master/README.md +489 -0
- package/skills/cm-ux-master/SKILL.md +773 -62
- package/skills/cm-ux-master/cli/README.md +180 -0
- package/skills/cm-ux-master/cli/pyproject.toml +106 -0
- package/skills/cm-ux-master/cli/requirements.txt +21 -0
- package/skills/cm-ux-master/cli/templates/base/skill-core.md +262 -0
- package/skills/cm-ux-master/cli/templates/platforms/claude.yaml +21 -0
- package/skills/cm-ux-master/cli/templates/platforms/cursor.yaml +21 -0
- package/skills/cm-ux-master/cli/templates/platforms/figma.yaml +24 -0
- package/skills/cm-ux-master/cli/templates/platforms/vscode-mcp.yaml +28 -0
- package/skills/cm-ux-master/cli/templates/platforms/windsurf.yaml +21 -0
- package/skills/cm-ux-master/cli/uxmaster/__init__.py +10 -0
- package/skills/cm-ux-master/cli/uxmaster/__main__.py +19 -0
- package/skills/cm-ux-master/cli/uxmaster/cli.py +349 -0
- package/skills/cm-ux-master/cli/uxmaster/commands/__init__.py +8 -0
- package/skills/cm-ux-master/cli/uxmaster/commands/extract.py +18 -0
- package/skills/cm-ux-master/cli/uxmaster/commands/init.py +58 -0
- package/skills/cm-ux-master/cli/uxmaster/commands/mcp.py +194 -0
- package/skills/cm-ux-master/cli/uxmaster/commands/search.py +23 -0
- package/skills/cm-ux-master/cli/uxmaster/commands/validate.py +270 -0
- package/skills/cm-ux-master/cli/uxmaster/search_engine.py +532 -0
- package/skills/cm-ux-master/cli/uxmaster/template_engine.py +458 -0
- package/skills/cm-ux-master/cli/uxmaster/utils/__init__.py +9 -0
- package/skills/cm-ux-master/cli/uxmaster/utils/console.py +42 -0
- package/skills/cm-ux-master/cli/uxmaster/utils/detect.py +83 -0
- package/skills/cm-ux-master/data/accessibility-advanced.csv +26 -0
- package/skills/cm-ux-master/data/animation.csv +31 -0
- package/skills/cm-ux-master/data/charts.csv +26 -0
- package/skills/cm-ux-master/data/colors.csv +97 -0
- package/skills/cm-ux-master/data/design-tests.csv +37 -0
- package/skills/cm-ux-master/data/devices.csv +21 -0
- package/skills/cm-ux-master/data/icons.csv +101 -0
- package/skills/cm-ux-master/data/landing.csv +31 -0
- package/skills/cm-ux-master/data/products.csv +97 -0
- package/skills/cm-ux-master/data/react-performance.csv +45 -0
- package/skills/cm-ux-master/data/responsive.csv +26 -0
- package/skills/cm-ux-master/data/semi-tokens.csv +52 -0
- package/skills/cm-ux-master/data/stacks/angular.csv +34 -0
- package/skills/cm-ux-master/data/stacks/astro.csv +54 -0
- package/skills/cm-ux-master/data/stacks/electron.csv +32 -0
- package/skills/cm-ux-master/data/stacks/flutter.csv +53 -0
- package/skills/cm-ux-master/data/stacks/html-tailwind.csv +56 -0
- package/skills/cm-ux-master/data/stacks/htmx.csv +28 -0
- package/skills/cm-ux-master/data/stacks/jetpack-compose.csv +53 -0
- package/skills/cm-ux-master/data/stacks/nextjs.csv +53 -0
- package/skills/cm-ux-master/data/stacks/nuxt-ui.csv +51 -0
- package/skills/cm-ux-master/data/stacks/nuxtjs.csv +59 -0
- package/skills/cm-ux-master/data/stacks/react-native.csv +52 -0
- package/skills/cm-ux-master/data/stacks/react.csv +54 -0
- package/skills/cm-ux-master/data/stacks/shadcn.csv +61 -0
- package/skills/cm-ux-master/data/stacks/svelte.csv +54 -0
- package/skills/cm-ux-master/data/stacks/swiftui.csv +51 -0
- package/skills/cm-ux-master/data/stacks/tauri.csv +29 -0
- package/skills/cm-ux-master/data/stacks/vue.csv +50 -0
- package/skills/cm-ux-master/data/styles.csv +68 -0
- package/skills/cm-ux-master/data/typography.csv +58 -0
- package/skills/cm-ux-master/data/ui-reasoning.csv +101 -0
- package/skills/cm-ux-master/data/ux-guidelines.csv +100 -0
- package/skills/cm-ux-master/data/ux-laws.csv +49 -0
- package/skills/cm-ux-master/data/web-interface.csv +31 -0
- package/skills/cm-ux-master/docs/LANDING-PAGE.html +377 -0
- package/skills/cm-ux-master/docs/README.md +108 -0
- package/skills/cm-ux-master/docs/css/styles.css +573 -0
- package/skills/cm-ux-master/docs/examples/demo-script.md +319 -0
- package/skills/cm-ux-master/docs/guides/for-designers.md +692 -0
- package/skills/cm-ux-master/docs/guides/for-developers.md +778 -0
- package/skills/cm-ux-master/docs/guides/for-product-managers.md +693 -0
- package/skills/cm-ux-master/docs/guides/react-guide-vi.md +50 -0
- package/skills/cm-ux-master/docs/index.html +1062 -0
- package/skills/cm-ux-master/docs/js/i18n.js +84 -0
- package/skills/cm-ux-master/docs/js/lang/de.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/en.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/fr.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/hi.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/id.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/ja.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/ko.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/ru.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/vi.js +145 -0
- package/skills/cm-ux-master/docs/js/lang/zh.js +145 -0
- package/skills/cm-ux-master/docs/js/main.js +117 -0
- package/skills/cm-ux-master/docs/plan/PHASE1-COMPLETION.md +217 -0
- package/skills/cm-ux-master/docs/plan/PHASE2-COMPLETION.md +199 -0
- package/skills/cm-ux-master/docs/plan/PHASE2-ENHANCED-COMPLETION.md +352 -0
- package/skills/cm-ux-master/docs/plan/PHASE3-VALIDATION-COMPLETION.md +499 -0
- package/skills/cm-ux-master/docs/plan/PHASE4-TESTING-POLISH-COMPLETION.md +483 -0
- package/skills/cm-ux-master/docs/plan/UXM-2.0-ROADMAP.md +681 -0
- package/skills/cm-ux-master/docs/plan/WOW-PITCH.md +410 -0
- package/skills/cm-ux-master/docs/technical/api-reference.md +824 -0
- package/skills/cm-ux-master/docs/technical/harvester-v4.md +328 -0
- package/skills/cm-ux-master/docs/technical/how-it-works.md +1128 -0
- package/skills/cm-ux-master/docs/tutorials/quickstart.md +339 -0
- package/skills/cm-ux-master/docs/tutorials/tutorials.md +939 -0
- package/skills/cm-ux-master/docs/tutorials/user-guide.md +716 -0
- package/skills/cm-ux-master/examples/README.md +63 -0
- package/skills/cm-ux-master/mcp/__init__.py +3 -0
- package/skills/cm-ux-master/mcp/integrations/__init__.py +11 -0
- package/skills/cm-ux-master/mcp/integrations/figma/__init__.py +6 -0
- package/skills/cm-ux-master/mcp/integrations/figma/client.py +293 -0
- package/skills/cm-ux-master/mcp/integrations/figma/plugin/code.js +561 -0
- package/skills/cm-ux-master/mcp/integrations/figma/plugin/ui.html +334 -0
- package/skills/cm-ux-master/mcp/integrations/stitch/__init__.py +5 -0
- package/skills/cm-ux-master/mcp/integrations/stitch/client.py +410 -0
- package/skills/cm-ux-master/mcp/integrations/vscode/package.json +167 -0
- package/skills/cm-ux-master/mcp/integrations/vscode/src/extension.ts +81 -0
- package/skills/cm-ux-master/mcp/mcp-config.json +274 -0
- package/skills/cm-ux-master/mcp/server.py +771 -0
- package/skills/cm-ux-master/mcp/tools/__init__.py +13 -0
- package/skills/cm-ux-master/mcp-server/server.py +595 -0
- package/skills/cm-ux-master/output/fila/FilaDashboard.tsx +47 -0
- package/skills/cm-ux-master/output/fila/components/badge/component.tsx +35 -0
- package/skills/cm-ux-master/output/fila/components/badge/index.ts +1 -0
- package/skills/cm-ux-master/output/fila/components/button/component.tsx +53 -0
- package/skills/cm-ux-master/output/fila/components/button/index.ts +1 -0
- package/skills/cm-ux-master/output/fila/components/card/component.tsx +35 -0
- package/skills/cm-ux-master/output/fila/components/card/index.ts +1 -0
- package/skills/cm-ux-master/output/fila/components/input/component.tsx +41 -0
- package/skills/cm-ux-master/output/fila/components/input/index.ts +1 -0
- package/skills/cm-ux-master/output/fila/design-system.css +151 -0
- package/skills/cm-ux-master/output/fila/design-system.html +1596 -0
- package/skills/cm-ux-master/output/fila/design-system.json +168 -0
- package/skills/cm-ux-master/output/fila/figma-tokens.json +523 -0
- package/skills/cm-ux-master/output/fila/harvest-v4-raw.json +406 -0
- package/skills/cm-ux-master/output/fila/semi-theme-override.css +95 -0
- package/skills/cm-ux-master/output/haravan/HaravanDashboard.tsx +103 -0
- package/skills/cm-ux-master/output/haravan/design-system-v3-live.html +2716 -0
- package/skills/cm-ux-master/output/haravan/design-system-v3.html +1770 -0
- package/skills/cm-ux-master/output/haravan/design-system.html +914 -0
- package/skills/cm-ux-master/output/haravan/figma-tokens.json +84 -0
- package/skills/cm-ux-master/output/haravan/haravan-harvest.json +33 -0
- package/skills/cm-ux-master/output/haravan/harvest-v3-raw.json +167 -0
- package/skills/cm-ux-master/output/haravan/semi-theme-override.css +39 -0
- package/skills/cm-ux-master/references/audit-template.md +257 -0
- package/skills/cm-ux-master/references/cultural-ux.md +346 -0
- package/skills/cm-ux-master/references/dark-patterns.md +362 -0
- package/skills/cm-ux-master/references/heuristic-conflicts.md +296 -0
- package/skills/cm-ux-master/references/krug-principles.md +289 -0
- package/skills/cm-ux-master/references/nielsen-heuristics.md +360 -0
- package/skills/cm-ux-master/references/wcag-checklist.md +306 -0
- package/skills/cm-ux-master/scripts/component_generator.py +631 -0
- package/skills/cm-ux-master/scripts/core.py +305 -0
- package/skills/cm-ux-master/scripts/demo_validation.py +452 -0
- package/skills/cm-ux-master/scripts/design_doc_generator.py +1325 -0
- package/skills/cm-ux-master/scripts/design_system.py +1141 -0
- package/skills/cm-ux-master/scripts/design_system_indexer.py +889 -0
- package/skills/cm-ux-master/scripts/extract_i18n.py +251 -0
- package/skills/cm-ux-master/scripts/extractor.py +1437 -0
- package/skills/cm-ux-master/scripts/figma_bridge.py +406 -0
- package/skills/cm-ux-master/scripts/generate.py +147 -0
- package/skills/cm-ux-master/scripts/harvest_session.py +207 -0
- package/skills/cm-ux-master/scripts/harvester.js +240 -0
- package/skills/cm-ux-master/scripts/harvester_browser.py +717 -0
- package/skills/cm-ux-master/scripts/harvester_cli.py +431 -0
- package/skills/cm-ux-master/scripts/harvester_v1.js +275 -0
- package/skills/cm-ux-master/scripts/harvester_v3.js +620 -0
- package/skills/cm-ux-master/scripts/harvester_v4.js +1003 -0
- package/skills/cm-ux-master/scripts/install.py +528 -0
- package/skills/cm-ux-master/scripts/license.py +81 -0
- package/skills/cm-ux-master/scripts/media/qrpayment.png +0 -0
- package/skills/cm-ux-master/scripts/pro_stubs.py +120 -0
- package/skills/cm-ux-master/scripts/project_registry.py +217 -0
- package/skills/cm-ux-master/scripts/search.py +114 -0
- package/skills/cm-ux-master/scripts/semi_mcp_bridge.py +425 -0
- package/skills/cm-ux-master/scripts/stitch_integration.py +583 -0
- package/skills/cm-ux-master/scripts/test_harvester_v4.py +335 -0
- package/skills/cm-ux-master/scripts/token_mapper.py +626 -0
- package/skills/cm-ux-master/scripts/validation_engine.py +1571 -0
- package/skills/cm-ux-master/scripts/wizard.py +653 -0
- package/skills/cm-ux-master/setup.py +93 -0
- package/skills/cm-ux-master/templates/base/flutter-widget.dart +69 -0
- package/skills/cm-ux-master/templates/base/html-page.html +152 -0
- package/skills/cm-ux-master/templates/base/react-component.tsx +47 -0
- package/skills/cm-ux-master/templates/base/swiftui-view.swift +62 -0
- package/skills/cm-ux-master/templates/quick-start.sh +176 -0
- package/skills/cm-ux-master/tests/automation/batch-validate.sh +250 -0
- package/skills/cm-ux-master/tests/automation/generate-test-projects.sh +561 -0
- package/skills/cm-ux-master/tests/automation/run-all-tests.sh +315 -0
- package/skills/cm-ux-master/tests/test_design_doc.py +145 -0
- package/skills/cm-ux-master/tests/test_devices.py +74 -0
- package/skills/cm-ux-master/tests/test_generator.py +116 -0
- package/skills/cm-ux-master/tests/test_harvest_session.py +131 -0
- package/skills/cm-ux-master/tests/test_harvester.py +127 -0
- package/skills/cm-ux-master/tests/test_harvester_v3.py +324 -0
- package/skills/cm-ux-master/tests/test_mcp_server.py +496 -0
- package/skills/cm-ux-master/tests/test_new_domains.py +108 -0
- package/skills/cm-ux-master/tests/test_new_stacks.py +103 -0
- package/skills/cm-ux-master/tests/test_project_registry.py +146 -0
- package/skills/cm-ux-master/tests/test_semi_mcp_bridge.py +207 -0
- package/skills/cm-ux-master/tests/test_token_mapper.py +247 -0
- package/skills/cm-ux-master/tests/test_validation_engine.py +617 -0
- package/skills/config.schema.json +397 -0
- package/skills/frappe-app-builder.zip +0 -0
- package/skills/jobs-to-be-done/SKILL.md +266 -0
- package/skills/jobs-to-be-done/references/case-studies.md +154 -0
- package/skills/jobs-to-be-done/references/competitive-strategy.md +280 -0
- package/skills/jobs-to-be-done/references/diagnostics.md +158 -0
- package/skills/jobs-to-be-done/references/innovation-process.md +392 -0
- package/skills/jobs-to-be-done/references/organizational-change.md +328 -0
- package/skills/marketplace-report-crawler/SKILL.md +176 -0
- package/skills/marketplace-report-crawler/config/accounts.json +41 -0
- package/skills/marketplace-report-crawler/config/report-types.json +422 -0
- package/skills/marketplace-report-crawler/config/sessions.json +3 -0
- package/skills/marketplace-report-crawler/scripts/ab-wrapper.sh +102 -0
- package/skills/marketplace-report-crawler/scripts/browser-actions/lazada/lazada-actions.js +114 -0
- package/skills/marketplace-report-crawler/scripts/browser-actions/shopee/shopee-actions.js +94 -0
- package/skills/marketplace-report-crawler/scripts/browser-actions/tiktok/tiktok-actions.js +272 -0
- package/skills/marketplace-report-crawler/scripts/crawl-runner.js +281 -0
- package/skills/marketplace-report-crawler/scripts/session-check.sh +72 -0
- package/skills/marketplace-report-crawler/scripts/session-manager.sh +349 -0
- package/skills/marketplace-report-crawler/scripts/setup-folders.sh +83 -0
- package/skills/medical-research/SKILL.md +194 -0
- package/skills/medical-research/scripts/evidence_checker.py +288 -0
- package/skills/mom-test/SKILL.md +267 -0
- package/skills/mom-test/references/avoiding-bad-data.md +221 -0
- package/skills/mom-test/references/case-studies.md +306 -0
- package/skills/mom-test/references/commitment-advancement.md +219 -0
- package/skills/mom-test/references/finding-conversations.md +251 -0
- package/skills/mom-test/references/processing-learning.md +256 -0
- package/skills/mom-test/references/question-patterns.md +198 -0
- package/skills/pandasai-analytics/SKILL.md +251 -0
- package/skills/release-it/SKILL.md +235 -0
- package/skills/release-it/references/anti-patterns.md +279 -0
- package/skills/release-it/references/capacity-planning.md +285 -0
- package/skills/release-it/references/chaos-engineering.md +325 -0
- package/skills/release-it/references/deployment-strategies.md +331 -0
- package/skills/release-it/references/observability.md +301 -0
- package/skills/release-it/references/stability-patterns.md +355 -0
- package/skills/scripts/sync-ide-skills.sh +61 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-audit.md +37 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-compare.md +34 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-export.md +51 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-generate.md +39 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-scaffold.md +52 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-simulate.md +25 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-stats.md +31 -0
- package/skills/skill-creator-ultra/.agents/workflows/skill-validate.md +25 -0
- package/skills/skill-creator-ultra/README.md +1242 -0
- package/skills/skill-creator-ultra/SKILL.md +388 -0
- package/skills/skill-creator-ultra/agents/analyzer.md +274 -0
- package/skills/skill-creator-ultra/agents/comparator.md +202 -0
- package/skills/skill-creator-ultra/agents/grader.md +223 -0
- package/skills/skill-creator-ultra/assets/eval_review.html +146 -0
- package/skills/skill-creator-ultra/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator-ultra/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator-ultra/examples/example_anthropic_frontend.md +109 -0
- package/skills/skill-creator-ultra/examples/example_anthropic_pdf.md +116 -0
- package/skills/skill-creator-ultra/examples/example_api_docs.md +189 -0
- package/skills/skill-creator-ultra/examples/example_db_migration.md +253 -0
- package/skills/skill-creator-ultra/examples/example_git_commit.md +111 -0
- package/skills/skill-creator-ultra/install.ps1 +289 -0
- package/skills/skill-creator-ultra/install.sh +313 -0
- package/skills/skill-creator-ultra/phases/phase1_interview.md +202 -0
- package/skills/skill-creator-ultra/phases/phase2_extract.md +55 -0
- package/skills/skill-creator-ultra/phases/phase3_detect.md +57 -0
- package/skills/skill-creator-ultra/phases/phase4_generate.md +543 -0
- package/skills/skill-creator-ultra/phases/phase5_test.md +319 -0
- package/skills/skill-creator-ultra/phases/phase6_eval.md +301 -0
- package/skills/skill-creator-ultra/phases/phase7_iterate.md +103 -0
- package/skills/skill-creator-ultra/phases/phase8_optimize.md +113 -0
- package/skills/skill-creator-ultra/resources/advanced_patterns.md +499 -0
- package/skills/skill-creator-ultra/resources/anti_patterns.md +376 -0
- package/skills/skill-creator-ultra/resources/blueprints.md +498 -0
- package/skills/skill-creator-ultra/resources/checklist.md +243 -0
- package/skills/skill-creator-ultra/resources/composition_cookbook.md +291 -0
- package/skills/skill-creator-ultra/resources/description_optimization.md +90 -0
- package/skills/skill-creator-ultra/resources/eval_guide.md +133 -0
- package/skills/skill-creator-ultra/resources/industry_questions.md +189 -0
- package/skills/skill-creator-ultra/resources/interview_questions.md +200 -0
- package/skills/skill-creator-ultra/resources/pattern_detection.md +200 -0
- package/skills/skill-creator-ultra/resources/prompt_engineering.md +531 -0
- package/skills/skill-creator-ultra/resources/schemas.md +430 -0
- package/skills/skill-creator-ultra/resources/script_integration.md +593 -0
- package/skills/skill-creator-ultra/resources/scripts_guide.md +339 -0
- package/skills/skill-creator-ultra/resources/skill_template.md +124 -0
- package/skills/skill-creator-ultra/resources/skill_writing_guide.md +634 -0
- package/skills/skill-creator-ultra/resources/versioning_guide.md +193 -0
- package/skills/skill-creator-ultra/scripts/ci_eval.py +200 -0
- package/skills/skill-creator-ultra/scripts/package_skill.py +165 -0
- package/skills/skill-creator-ultra/scripts/simulate_skill.py +398 -0
- package/skills/skill-creator-ultra/scripts/skill_audit.py +611 -0
- package/skills/skill-creator-ultra/scripts/skill_compare.py +265 -0
- package/skills/skill-creator-ultra/scripts/skill_export.py +334 -0
- package/skills/skill-creator-ultra/scripts/skill_scaffold.py +403 -0
- package/skills/skill-creator-ultra/scripts/skill_stats.py +339 -0
- package/skills/skill-creator-ultra/scripts/validate_skill.py +411 -0
- package/skills/tailwind-mastery/SKILL.md +229 -0
- package/skills/vercel-react-best-practices/AGENTS.md +3373 -0
- package/skills/vercel-react-best-practices/README.md +123 -0
- package/skills/vercel-react-best-practices/SKILL.md +143 -0
- package/skills/vercel-react-best-practices/rules/_sections.md +46 -0
- package/skills/vercel-react-best-practices/rules/_template.md +28 -0
- package/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/skills/cro-methodology/SKILL.md +0 -98
- /package/skills/{cro-methodology → cm-cro-methodology}/references/COPYWRITING.md +0 -0
- /package/skills/{cro-methodology → cm-cro-methodology}/references/OBJECTIONS.md +0 -0
- /package/skills/{cro-methodology → cm-cro-methodology}/references/PERSUASION.md +0 -0
- /package/skills/{cro-methodology → cm-cro-methodology}/references/RESEARCH.md +0 -0
- /package/skills/{cro-methodology → cm-cro-methodology}/references/funnel-analysis.md +0 -0
- /package/skills/{cro-methodology → cm-cro-methodology}/references/testing-methodology.md +0 -0
|
@@ -0,0 +1,985 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frappe-app-builder
|
|
3
|
+
description: |
|
|
4
|
+
Build, upgrade, and maintain production-ready Frappe custom apps from scratch.
|
|
5
|
+
Covers the full lifecycle: project scaffold, DocType design, engine architecture,
|
|
6
|
+
API endpoints, scheduler tasks, hooks, workflows, reports, client-side JS,
|
|
7
|
+
testing, and deployment. Synthesized from real production experience.
|
|
8
|
+
|
|
9
|
+
ALWAYS trigger for: frappe app, frappe doctype, bench, erpnext, frappe hooks,
|
|
10
|
+
frappe scheduler, frappe workflow, frappe report, frappe api, frappe controller,
|
|
11
|
+
"create frappe app", "write doctype", "frappe custom app", "bench migrate",
|
|
12
|
+
"upgrade frappe", "frappe engine", "frappe webhook", "frappe client script",
|
|
13
|
+
"frappe server script", "frappe test", "improve frappe", "frappe permission",
|
|
14
|
+
"frappe fixture", "frappe workspace", "doc_events"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Goal
|
|
18
|
+
|
|
19
|
+
Build production-ready Frappe custom apps that are modular, testable, and
|
|
20
|
+
maintainable. Every app follows the same battle-tested architecture: separate
|
|
21
|
+
business logic engines from Frappe ORM, expose clean APIs, schedule tasks,
|
|
22
|
+
test pure-logic without Frappe, and use hooks/fixtures for portability.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Instructions
|
|
27
|
+
|
|
28
|
+
## 🏗️ Architecture — The 7-Layer Frappe App
|
|
29
|
+
|
|
30
|
+
Every non-trivial Frappe app should follow this layered architecture:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
my_app/
|
|
34
|
+
├── my_app/
|
|
35
|
+
│ └── doctype/ # Layer 1: Data Model (DocType JSON + controllers)
|
|
36
|
+
│ ├── my_doctype/
|
|
37
|
+
│ │ ├── my_doctype.json
|
|
38
|
+
│ │ ├── my_doctype.py # Server controller
|
|
39
|
+
│ │ └── my_doctype.js # Client controller
|
|
40
|
+
│ └── ...
|
|
41
|
+
├── engines/ # Layer 2: Business Logic (PURE Python, no Frappe in core)
|
|
42
|
+
│ ├── scoring_engine.py
|
|
43
|
+
│ └── benefit_engine.py
|
|
44
|
+
├── api/ # Layer 3: API Endpoints (@frappe.whitelist)
|
|
45
|
+
│ ├── external.py # Webhooks / external system integration
|
|
46
|
+
│ └── internal.py # Internal UI-facing APIs
|
|
47
|
+
├── tasks/ # Layer 4: Scheduled Tasks (daily/weekly/monthly/cron)
|
|
48
|
+
│ ├── daily.py
|
|
49
|
+
│ ├── weekly.py
|
|
50
|
+
│ └── monthly.py
|
|
51
|
+
├── setup/ # Layer 5: Install & Migrate hooks
|
|
52
|
+
│ └── install.py
|
|
53
|
+
├── tests/ # Layer 6: Tests (standalone, no Frappe needed for pure logic)
|
|
54
|
+
│ ├── test_engine.py
|
|
55
|
+
│ └── test_benefit.py
|
|
56
|
+
├── public/js/ # Layer 7: Shared Client JS (badges, utilities, list views)
|
|
57
|
+
│ └── my_app.js
|
|
58
|
+
├── hooks.py # The nervous system — connects everything
|
|
59
|
+
├── fixtures/ # Exported Roles, Workflows, Custom Fields
|
|
60
|
+
└── modules.txt
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Why This Architecture?
|
|
64
|
+
|
|
65
|
+
| Layer | Why Separate? |
|
|
66
|
+
|-------|--------------|
|
|
67
|
+
| **DocType** | Frappe manages schema via JSON. Controllers handle validate/submit/cancel |
|
|
68
|
+
| **Engines** | Pure logic = testable without Frappe instance. Import in API, tasks, controllers |
|
|
69
|
+
| **API** | Clean separation of external webhooks vs internal UI calls |
|
|
70
|
+
| **Tasks** | Scheduler events are simple wrappers calling engine functions |
|
|
71
|
+
| **Setup** | Idempotent install/migrate = safe `bench migrate` every time |
|
|
72
|
+
| **Tests** | Pure-logic tests run with `pytest -v`, no bench needed |
|
|
73
|
+
| **Shared JS** | Global utilities, badges, list view settings, reusable across all DocTypes |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 📐 Layer 1: DocType Design
|
|
78
|
+
|
|
79
|
+
### Controller Pattern (Python)
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import frappe
|
|
83
|
+
from frappe import _
|
|
84
|
+
from frappe.model.document import Document
|
|
85
|
+
from frappe.utils import now
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class WarehouseViolation(Document):
|
|
89
|
+
def validate(self):
|
|
90
|
+
"""Auto-fill derived fields BEFORE save."""
|
|
91
|
+
self.fetch_employee_details()
|
|
92
|
+
self.set_period()
|
|
93
|
+
self.fetch_penalty_from_type()
|
|
94
|
+
|
|
95
|
+
def fetch_employee_details(self):
|
|
96
|
+
"""Pull company, branch, name from Employee — avoid manual entry."""
|
|
97
|
+
if self.employee:
|
|
98
|
+
emp = frappe.db.get_value(
|
|
99
|
+
"Employee", self.employee,
|
|
100
|
+
["company", "branch", "employee_name"], as_dict=True,
|
|
101
|
+
)
|
|
102
|
+
if emp:
|
|
103
|
+
self.company = emp.company
|
|
104
|
+
self.branch = emp.branch
|
|
105
|
+
self.employee_name = emp.employee_name
|
|
106
|
+
|
|
107
|
+
def set_period(self):
|
|
108
|
+
"""Auto-derive period (YYYY-MM) from date field."""
|
|
109
|
+
if self.date:
|
|
110
|
+
self.period = str(self.date)[:7]
|
|
111
|
+
|
|
112
|
+
def fetch_penalty_from_type(self):
|
|
113
|
+
"""Auto-fill penalty points from master data if not manually set."""
|
|
114
|
+
if self.violation_type and not self.penalty_points:
|
|
115
|
+
pts = frappe.db.get_value(
|
|
116
|
+
"Warehouse Violation Type", self.violation_type, "penalty_points"
|
|
117
|
+
)
|
|
118
|
+
if pts:
|
|
119
|
+
self.penalty_points = pts
|
|
120
|
+
|
|
121
|
+
def on_submit(self):
|
|
122
|
+
"""Record who confirmed and when."""
|
|
123
|
+
self.confirmed_by = frappe.session.user
|
|
124
|
+
self.confirmed_at = now()
|
|
125
|
+
self.db_update()
|
|
126
|
+
frappe.msgprint(
|
|
127
|
+
_("Confirmed. Penalty: {0} pts for {1}.").format(
|
|
128
|
+
self.penalty_points, self.employee_name
|
|
129
|
+
),
|
|
130
|
+
indicator="orange", alert=True,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def on_cancel(self):
|
|
134
|
+
frappe.msgprint(
|
|
135
|
+
_("Cancelled for {0}.").format(self.employee_name), alert=True
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Key patterns:**
|
|
140
|
+
- `validate()` = auto-fill derived fields (period from date, employee details from Link)
|
|
141
|
+
- `on_submit()` = record audit trail (who, when), show user feedback
|
|
142
|
+
- `on_cancel()` = cleanup or notify
|
|
143
|
+
- Use `self.db_update()` after modifying fields in `on_submit` (doc already saved)
|
|
144
|
+
- Use `frappe.db.get_value()` for single-field lookups (fast, no full doc load)
|
|
145
|
+
|
|
146
|
+
### Controller Pattern (JavaScript)
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
frappe.ui.form.on("Warehouse Violation", {
|
|
150
|
+
refresh(frm) {
|
|
151
|
+
// Color-coded workflow state indicator
|
|
152
|
+
if (frm.doc.workflow_state) {
|
|
153
|
+
const colors = {
|
|
154
|
+
"Pending": "orange", "Confirmed": "green",
|
|
155
|
+
"Appealed": "blue", "Waived": "red"
|
|
156
|
+
};
|
|
157
|
+
frm.page.set_indicator(
|
|
158
|
+
frm.doc.workflow_state,
|
|
159
|
+
colors[frm.doc.workflow_state] || "gray"
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Dashboard indicators for key metrics
|
|
164
|
+
if (frm.doc.penalty_points) {
|
|
165
|
+
frm.dashboard.add_indicator(
|
|
166
|
+
__("Penalty: {0} pts", [frm.doc.penalty_points]),
|
|
167
|
+
frm.doc.penalty_points >= 8 ? "red" :
|
|
168
|
+
frm.doc.penalty_points >= 5 ? "orange" : "blue"
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Custom action buttons (non-submitted docs only)
|
|
173
|
+
if (!frm.is_new() && frm.doc.docstatus === 0) {
|
|
174
|
+
frm.add_custom_button(__("View Dashboard"), function () {
|
|
175
|
+
frappe.set_route("query-report", "Employee Summary", {
|
|
176
|
+
period: frm.doc.period
|
|
177
|
+
});
|
|
178
|
+
}, __("Actions"));
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
date(frm) {
|
|
183
|
+
// Auto-fill period from date
|
|
184
|
+
boxme.autoSetPeriod(frm, "date", "period");
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
violation_type(frm) {
|
|
188
|
+
// Auto-fill penalty from master data
|
|
189
|
+
if (frm.doc.violation_type) {
|
|
190
|
+
frappe.db.get_value(
|
|
191
|
+
"Warehouse Violation Type",
|
|
192
|
+
frm.doc.violation_type,
|
|
193
|
+
"penalty_points",
|
|
194
|
+
(r) => {
|
|
195
|
+
if (r && r.penalty_points) {
|
|
196
|
+
frm.set_value("penalty_points", r.penalty_points);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### DocType Design Rules
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
✅ DO:
|
|
209
|
+
- Use Link fields to reference other DocTypes (Employee, Company, Branch)
|
|
210
|
+
- Add "period" field (Data, YYYY-MM) auto-derived from date
|
|
211
|
+
- Use Submittable DocTypes for records that need approval workflows
|
|
212
|
+
- Set "module" in DocType JSON to your app module name
|
|
213
|
+
- Add naming_rule or autoname for meaningful document names
|
|
214
|
+
|
|
215
|
+
❌ DON'T:
|
|
216
|
+
- Hardcode company/branch/employee names — always use Link fields
|
|
217
|
+
- Put business logic in DocType controllers — use engines/ instead
|
|
218
|
+
- Forget to add "module" property — makes fixtures export fail
|
|
219
|
+
- Create DocType without think about workflow states first
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## ⚙️ Layer 2: Business Logic Engines
|
|
225
|
+
|
|
226
|
+
**The most important pattern.** Separate pure logic from Frappe ORM.
|
|
227
|
+
|
|
228
|
+
### Pure-Logic Functions (No Frappe Imports)
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
"""
|
|
232
|
+
All pure-logic functions have NO frappe DB calls.
|
|
233
|
+
Safe to unit-test without a running Frappe instance.
|
|
234
|
+
"""
|
|
235
|
+
PPH_LEVEL_ORDER = ["needs_improvement", "average", "good", "excellent"]
|
|
236
|
+
|
|
237
|
+
_DEFAULT_THRESHOLDS = [
|
|
238
|
+
{"level": "needs_improvement", "min_pph": 0, "max_pph": 35},
|
|
239
|
+
{"level": "average", "min_pph": 35, "max_pph": 55},
|
|
240
|
+
{"level": "good", "min_pph": 55, "max_pph": 75},
|
|
241
|
+
{"level": "excellent", "min_pph": 75, "max_pph": None},
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
def classify_pph_level(pph: float, thresholds: list) -> str:
|
|
245
|
+
"""Classify using half-open intervals [min, max). Falls back to defaults."""
|
|
246
|
+
if not thresholds:
|
|
247
|
+
thresholds = _DEFAULT_THRESHOLDS
|
|
248
|
+
for t in sorted(thresholds, key=lambda x: x["min_pph"]):
|
|
249
|
+
min_v = float(t["min_pph"])
|
|
250
|
+
max_v = t.get("max_pph")
|
|
251
|
+
if pph >= min_v and (max_v is None or pph < float(max_v)):
|
|
252
|
+
return t["level"]
|
|
253
|
+
return thresholds[-1]["level"]
|
|
254
|
+
|
|
255
|
+
def pph_level_gte(level: str, min_level: str) -> bool:
|
|
256
|
+
"""True if level >= min_level in ordering."""
|
|
257
|
+
return PPH_LEVEL_ORDER.index(level) >= PPH_LEVEL_ORDER.index(min_level)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Config Cascade Pattern (Frappe DB)
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
def get_config_with_cascade(
|
|
264
|
+
company: str, branch: str = None, designation: str = None,
|
|
265
|
+
as_of_date: str = None,
|
|
266
|
+
) -> list:
|
|
267
|
+
"""
|
|
268
|
+
Fetch config with specificity cascade:
|
|
269
|
+
1. Company + Branch + Designation (most specific)
|
|
270
|
+
2. Company + Branch
|
|
271
|
+
3. Company only
|
|
272
|
+
Returns the most specific matching rows.
|
|
273
|
+
"""
|
|
274
|
+
if not as_of_date:
|
|
275
|
+
as_of_date = frappe.utils.today()
|
|
276
|
+
|
|
277
|
+
date_filter = """
|
|
278
|
+
AND effective_from <= %(date)s
|
|
279
|
+
AND (effective_to IS NULL OR effective_to = '' OR effective_to >= %(date)s)
|
|
280
|
+
AND is_active = 1
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
for b, d in [(branch, designation), (branch, None), (None, None)]:
|
|
284
|
+
conds = "company = %(company)s"
|
|
285
|
+
params = {"company": company, "date": as_of_date}
|
|
286
|
+
|
|
287
|
+
if b:
|
|
288
|
+
conds += " AND branch = %(branch)s"
|
|
289
|
+
params["branch"] = b
|
|
290
|
+
else:
|
|
291
|
+
conds += " AND (branch IS NULL OR branch = '')"
|
|
292
|
+
|
|
293
|
+
if d:
|
|
294
|
+
conds += " AND designation = %(designation)s"
|
|
295
|
+
params["designation"] = d
|
|
296
|
+
else:
|
|
297
|
+
conds += " AND (designation IS NULL OR designation = '')"
|
|
298
|
+
|
|
299
|
+
rows = frappe.db.sql(
|
|
300
|
+
f"SELECT * FROM `tabConfig DocType` WHERE {conds} {date_filter}",
|
|
301
|
+
params, as_dict=True,
|
|
302
|
+
)
|
|
303
|
+
if rows:
|
|
304
|
+
return rows
|
|
305
|
+
return []
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Main Pipeline Pattern
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
def calculate_period_score(employee: str, period_type: str, period_value: str) -> dict:
|
|
312
|
+
"""
|
|
313
|
+
Full scoring pipeline for one employee over a period.
|
|
314
|
+
Returns a dict matching the target DocType fields.
|
|
315
|
+
"""
|
|
316
|
+
start_date, end_date = get_period_date_range(period_type, period_value)
|
|
317
|
+
|
|
318
|
+
emp = frappe.db.get_value("Employee", employee,
|
|
319
|
+
["company", "branch", "designation"], as_dict=True)
|
|
320
|
+
if not emp:
|
|
321
|
+
frappe.throw(f"Employee {employee!r} not found")
|
|
322
|
+
|
|
323
|
+
# Step 1: Aggregate raw data
|
|
324
|
+
raw_data = frappe.db.sql("""
|
|
325
|
+
SELECT value, hours FROM `tabRaw Data`
|
|
326
|
+
WHERE employee = %(emp)s AND date BETWEEN %(s)s AND %(e)s
|
|
327
|
+
""", {"emp": employee, "s": start_date, "e": end_date}, as_dict=True)
|
|
328
|
+
|
|
329
|
+
# Step 2: Classify using pure-logic functions
|
|
330
|
+
avg_value = statistics.mean([r.value for r in raw_data]) if raw_data else 0
|
|
331
|
+
level = classify_level(avg_value, get_config(emp.company, emp.branch))
|
|
332
|
+
|
|
333
|
+
# Step 3: Aggregate related documents (submitted only = docstatus=1)
|
|
334
|
+
penalties = frappe.db.sql("""
|
|
335
|
+
SELECT COALESCE(SUM(points), 0) AS total
|
|
336
|
+
FROM `tabPenalty` WHERE employee = %(emp)s AND period = %(p)s AND docstatus = 1
|
|
337
|
+
""", {"emp": employee, "p": period_value[:7]}, as_dict=True)
|
|
338
|
+
|
|
339
|
+
# Step 4: Return structured result matching DocType fields
|
|
340
|
+
return {
|
|
341
|
+
"employee": employee,
|
|
342
|
+
"period_type": period_type,
|
|
343
|
+
"period_value": period_value,
|
|
344
|
+
"company": emp.company,
|
|
345
|
+
"avg_value": avg_value,
|
|
346
|
+
"level": level,
|
|
347
|
+
"total_penalty": penalties[0].total if penalties else 0,
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Key patterns:**
|
|
352
|
+
- Separate pure functions (classify, compare) from DB functions (fetch, aggregate)
|
|
353
|
+
- Always filter by `docstatus = 1` for submitted documents
|
|
354
|
+
- Use `COALESCE(SUM(...), 0)` to avoid NULL results
|
|
355
|
+
- Return dicts that match target DocType field names exactly
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 🌐 Layer 3: API Endpoints
|
|
360
|
+
|
|
361
|
+
### External Webhook Pattern (WMS/ERP Integration)
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
@frappe.whitelist(allow_guest=False)
|
|
365
|
+
def receive_data(data):
|
|
366
|
+
"""
|
|
367
|
+
POST /api/method/my_app.api.external.receive_data
|
|
368
|
+
Body: {"data": [{...}, {...}]}
|
|
369
|
+
Auth: token api_key:api_secret
|
|
370
|
+
"""
|
|
371
|
+
if isinstance(data, str):
|
|
372
|
+
data = json.loads(data)
|
|
373
|
+
|
|
374
|
+
created = skipped = 0
|
|
375
|
+
errors = []
|
|
376
|
+
|
|
377
|
+
for rec in data:
|
|
378
|
+
emp_code = rec.get("employee_code")
|
|
379
|
+
try:
|
|
380
|
+
employee = frappe.db.get_value(
|
|
381
|
+
"Employee", {"employee_number": emp_code}, "name"
|
|
382
|
+
)
|
|
383
|
+
if not employee:
|
|
384
|
+
errors.append({"code": emp_code, "error": f"Not found"})
|
|
385
|
+
skipped += 1
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
# Deduplicate: skip if already exists
|
|
389
|
+
if frappe.db.exists("My DocType", {"employee": employee, "date": rec["date"]}):
|
|
390
|
+
skipped += 1
|
|
391
|
+
continue
|
|
392
|
+
|
|
393
|
+
doc = frappe.get_doc({
|
|
394
|
+
"doctype": "My DocType",
|
|
395
|
+
"employee": employee,
|
|
396
|
+
"date": rec.get("date"),
|
|
397
|
+
"value": rec.get("value", 0),
|
|
398
|
+
"source": "external_system",
|
|
399
|
+
})
|
|
400
|
+
doc.insert(ignore_permissions=True)
|
|
401
|
+
created += 1
|
|
402
|
+
|
|
403
|
+
except Exception as exc:
|
|
404
|
+
errors.append({"code": emp_code, "error": str(exc)})
|
|
405
|
+
skipped += 1
|
|
406
|
+
frappe.log_error(str(exc), "receive_data")
|
|
407
|
+
|
|
408
|
+
frappe.db.commit()
|
|
409
|
+
return {"status": "ok" if not errors else "partial",
|
|
410
|
+
"created": created, "skipped": skipped, "errors": errors}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Internal API Pattern (UI + Dashboard)
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
@frappe.whitelist()
|
|
417
|
+
def calculate_for_employee(employee, period_type, period_value, overwrite=False):
|
|
418
|
+
"""Manual trigger with idempotent upsert."""
|
|
419
|
+
if not frappe.has_permission("My DocType", "write"):
|
|
420
|
+
frappe.throw(_("Permission denied"), frappe.PermissionError)
|
|
421
|
+
|
|
422
|
+
data = engine.calculate(employee, period_type, period_value)
|
|
423
|
+
|
|
424
|
+
existing = frappe.db.get_value("My DocType", {
|
|
425
|
+
"employee": employee, "period_type": period_type,
|
|
426
|
+
"period_value": period_value,
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
if existing:
|
|
430
|
+
if not overwrite:
|
|
431
|
+
return {"status": "exists", "name": existing}
|
|
432
|
+
doc = frappe.get_doc("My DocType", existing)
|
|
433
|
+
if doc.docstatus == 1:
|
|
434
|
+
frappe.throw(_("Cannot overwrite submitted record"))
|
|
435
|
+
doc.update(data)
|
|
436
|
+
doc.save(ignore_permissions=True)
|
|
437
|
+
frappe.db.commit()
|
|
438
|
+
return {"status": "updated", "name": doc.name}
|
|
439
|
+
|
|
440
|
+
doc = frappe.get_doc({"doctype": "My DocType", **data})
|
|
441
|
+
doc.insert(ignore_permissions=True)
|
|
442
|
+
frappe.db.commit()
|
|
443
|
+
return {"status": "created", "name": doc.name}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Permission Query Pattern
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
def get_permission_query(user: str) -> str:
|
|
450
|
+
"""Row-level security: user sees only their own records unless manager/admin."""
|
|
451
|
+
roles = frappe.get_roles(user)
|
|
452
|
+
admin_roles = {"System Manager", "HR Reviewer", "Department Manager"}
|
|
453
|
+
if admin_roles & set(roles):
|
|
454
|
+
return "" # No filter = see all
|
|
455
|
+
emp = frappe.db.get_value("Employee", {"user_id": user}, "name")
|
|
456
|
+
if emp:
|
|
457
|
+
return f"`tabMy DocType`.employee = '{emp}'"
|
|
458
|
+
return "1=0" # See nothing
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Key patterns:**
|
|
462
|
+
- `allow_guest=False` for webhooks (requires API key auth)
|
|
463
|
+
- Parse `data` as string or dict (Frappe may pass either)
|
|
464
|
+
- Batch processing with created/skipped/errors counters
|
|
465
|
+
- Idempotent upsert: check existing → update or insert
|
|
466
|
+
- `frappe.db.commit()` after bulk operations
|
|
467
|
+
- Permission queries for row-level security
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## ⏰ Layer 4: Scheduler Tasks
|
|
472
|
+
|
|
473
|
+
### Pattern: Batch Processing All Employees
|
|
474
|
+
|
|
475
|
+
```python
|
|
476
|
+
def run_monthly_calculation(year_month: str = None):
|
|
477
|
+
"""Monthly task: calculate scores for ALL active employees."""
|
|
478
|
+
if not year_month:
|
|
479
|
+
year_month = add_months(today(), -1)[:7]
|
|
480
|
+
|
|
481
|
+
frappe.logger("my_app").info(f"[Monthly] Starting for {year_month}")
|
|
482
|
+
employees = frappe.get_all("Employee", filters={"status": "Active"}, fields=["name"])
|
|
483
|
+
ok = err = 0
|
|
484
|
+
|
|
485
|
+
for emp in employees:
|
|
486
|
+
try:
|
|
487
|
+
data = engine.calculate(emp.name, "month", year_month)
|
|
488
|
+
existing = frappe.db.get_value("Score", {
|
|
489
|
+
"employee": emp.name, "period_type": "month",
|
|
490
|
+
"period_value": year_month,
|
|
491
|
+
})
|
|
492
|
+
if existing:
|
|
493
|
+
doc = frappe.get_doc("Score", existing)
|
|
494
|
+
if doc.docstatus == 0: # Only update drafts
|
|
495
|
+
doc.update(data)
|
|
496
|
+
doc.save(ignore_permissions=True)
|
|
497
|
+
else:
|
|
498
|
+
frappe.get_doc({"doctype": "Score", **data}).insert(ignore_permissions=True)
|
|
499
|
+
ok += 1
|
|
500
|
+
except Exception as exc:
|
|
501
|
+
err += 1
|
|
502
|
+
frappe.log_error(str(exc), f"Monthly — {emp.name}")
|
|
503
|
+
|
|
504
|
+
frappe.db.commit()
|
|
505
|
+
frappe.logger("my_app").info(f"[Monthly] Done: {ok} ok, {err} errors")
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Pattern: Reminder Notifications
|
|
509
|
+
|
|
510
|
+
```python
|
|
511
|
+
def send_pending_reminders():
|
|
512
|
+
"""Notify managers about records pending > 3 days."""
|
|
513
|
+
cutoff = add_days(today(), -3)
|
|
514
|
+
pending = frappe.db.sql("""
|
|
515
|
+
SELECT r.name, r.employee, e.employee_name, e.reports_to
|
|
516
|
+
FROM `tabMy Record` r
|
|
517
|
+
LEFT JOIN `tabEmployee` e ON r.employee = e.name
|
|
518
|
+
WHERE r.docstatus = 0 AND r.creation <= %(cutoff)s
|
|
519
|
+
""", {"cutoff": cutoff}, as_dict=True)
|
|
520
|
+
|
|
521
|
+
by_manager = {}
|
|
522
|
+
for row in pending:
|
|
523
|
+
if row.reports_to:
|
|
524
|
+
by_manager.setdefault(row.reports_to, []).append(row)
|
|
525
|
+
|
|
526
|
+
for mgr, records in by_manager.items():
|
|
527
|
+
mgr_email = frappe.db.get_value("Employee", mgr, "user_id")
|
|
528
|
+
if mgr_email:
|
|
529
|
+
frappe.sendmail(
|
|
530
|
+
recipients=[mgr_email],
|
|
531
|
+
subject=f"[My App] {len(records)} records need review",
|
|
532
|
+
message=format_reminder_html(records),
|
|
533
|
+
)
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### hooks.py Scheduler Config
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
scheduler_events = {
|
|
540
|
+
"daily": [
|
|
541
|
+
"my_app.tasks.daily.run_daily_aggregation",
|
|
542
|
+
"my_app.tasks.daily.send_pending_reminders",
|
|
543
|
+
],
|
|
544
|
+
"weekly": [
|
|
545
|
+
"my_app.tasks.weekly.run_weekly_calculation",
|
|
546
|
+
],
|
|
547
|
+
"cron": {
|
|
548
|
+
"0 1 1 * *": [ # 1st of month at 01:00
|
|
549
|
+
"my_app.tasks.monthly.run_monthly_calculation",
|
|
550
|
+
]
|
|
551
|
+
},
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 🔗 hooks.py — The Nervous System
|
|
558
|
+
|
|
559
|
+
```python
|
|
560
|
+
app_name = "my_app"
|
|
561
|
+
app_title = "My App"
|
|
562
|
+
app_publisher = "My Company"
|
|
563
|
+
app_description = "App description"
|
|
564
|
+
app_license = "MIT"
|
|
565
|
+
|
|
566
|
+
required_apps = ["frappe/hrms"] # Declare dependencies
|
|
567
|
+
|
|
568
|
+
# ── Assets ────────────────────────────────────────────────────────────────
|
|
569
|
+
app_include_js = ["/assets/my_app/js/my_app.js"]
|
|
570
|
+
|
|
571
|
+
# ── DocType JS overrides (for core DocTypes like Employee) ────────────────
|
|
572
|
+
doctype_js = {
|
|
573
|
+
"Employee": "public/js/employee.js"
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
# ── After install / migrate ───────────────────────────────────────────────
|
|
577
|
+
after_install = "my_app.setup.install.after_install"
|
|
578
|
+
after_migrate = "my_app.setup.install.after_migrate"
|
|
579
|
+
|
|
580
|
+
# ── Doc Events (server-side hooks) ────────────────────────────────────────
|
|
581
|
+
doc_events = {
|
|
582
|
+
"My Submittable Doc": {
|
|
583
|
+
"on_submit": "my_app.engines.my_engine.on_doc_submit",
|
|
584
|
+
"on_cancel": "my_app.engines.my_engine.on_doc_cancel",
|
|
585
|
+
},
|
|
586
|
+
"My Auto Doc": {
|
|
587
|
+
"after_insert": "my_app.engines.my_engine.on_doc_insert",
|
|
588
|
+
},
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
# ── Fixtures (portable across sites) ─────────────────────────────────────
|
|
592
|
+
fixtures = [
|
|
593
|
+
{"dt": "Role", "filters": [["name", "in", ["My Role 1", "My Role 2"]]]},
|
|
594
|
+
{"dt": "Custom Field", "filters": [["module", "=", "My App"]]},
|
|
595
|
+
{"dt": "Workflow", "filters": [["document_type", "in", ["My Doc"]]]},
|
|
596
|
+
{"dt": "Workflow State", "filters": [["name", "in", [
|
|
597
|
+
"Draft", "Pending", "Approved", "Rejected"
|
|
598
|
+
]]]},
|
|
599
|
+
]
|
|
600
|
+
|
|
601
|
+
# ── Permission query conditions ───────────────────────────────────────────
|
|
602
|
+
permission_query_conditions = {
|
|
603
|
+
"My Score Doc": "my_app.api.internal.get_permission_query",
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## 🛠️ Layer 5: Setup / Install
|
|
610
|
+
|
|
611
|
+
```python
|
|
612
|
+
def after_install():
|
|
613
|
+
"""Run once after bench install-app. Must be idempotent."""
|
|
614
|
+
_create_roles()
|
|
615
|
+
_create_custom_fields()
|
|
616
|
+
_create_workflow_states()
|
|
617
|
+
frappe.db.commit()
|
|
618
|
+
|
|
619
|
+
def after_migrate():
|
|
620
|
+
"""Run after every bench migrate. Must be idempotent."""
|
|
621
|
+
_ensure_roles()
|
|
622
|
+
_create_workflow_states()
|
|
623
|
+
frappe.db.commit()
|
|
624
|
+
|
|
625
|
+
def _create_roles():
|
|
626
|
+
for role in ["My Role 1", "My Role 2"]:
|
|
627
|
+
if not frappe.db.exists("Role", role):
|
|
628
|
+
frappe.get_doc({
|
|
629
|
+
"doctype": "Role", "role_name": role, "desk_access": 1
|
|
630
|
+
}).insert(ignore_permissions=True)
|
|
631
|
+
|
|
632
|
+
def _create_custom_fields():
|
|
633
|
+
"""Add custom fields to core DocTypes (e.g., Employee)."""
|
|
634
|
+
fields = [
|
|
635
|
+
{
|
|
636
|
+
"dt": "Employee",
|
|
637
|
+
"fieldname": "my_app_code",
|
|
638
|
+
"fieldtype": "Data",
|
|
639
|
+
"label": "My App Code",
|
|
640
|
+
"insert_after": "employee_number",
|
|
641
|
+
"unique": 1, "search_index": 1,
|
|
642
|
+
"module": "My App",
|
|
643
|
+
},
|
|
644
|
+
]
|
|
645
|
+
for cf in fields:
|
|
646
|
+
if not frappe.db.exists("Custom Field", {"dt": cf["dt"], "fieldname": cf["fieldname"]}):
|
|
647
|
+
frappe.get_doc({"doctype": "Custom Field", **cf}).insert(ignore_permissions=True)
|
|
648
|
+
|
|
649
|
+
def _create_workflow_states():
|
|
650
|
+
states = [
|
|
651
|
+
{"state": "Draft", "icon": "edit", "style": ""},
|
|
652
|
+
{"state": "Pending", "icon": "time", "style": "Warning"},
|
|
653
|
+
{"state": "Approved", "icon": "ok-sign", "style": "Success"},
|
|
654
|
+
{"state": "Rejected", "icon": "remove", "style": "Danger"},
|
|
655
|
+
]
|
|
656
|
+
for ws in states:
|
|
657
|
+
if not frappe.db.exists("Workflow State", ws["state"]):
|
|
658
|
+
frappe.get_doc({
|
|
659
|
+
"doctype": "Workflow State",
|
|
660
|
+
"workflow_state_name": ws["state"],
|
|
661
|
+
"icon": ws["icon"], "style": ws["style"],
|
|
662
|
+
}).insert(ignore_permissions=True)
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## 📊 Layer 6: Reports
|
|
668
|
+
|
|
669
|
+
### Script Report Pattern
|
|
670
|
+
|
|
671
|
+
**Python (employee_scoring_summary.py):**
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
def execute(filters=None):
|
|
675
|
+
filters = filters or {}
|
|
676
|
+
columns = get_columns()
|
|
677
|
+
data = get_data(filters)
|
|
678
|
+
chart = get_chart(data)
|
|
679
|
+
report_summary = get_report_summary(data)
|
|
680
|
+
return columns, data, None, chart, report_summary
|
|
681
|
+
|
|
682
|
+
def get_columns():
|
|
683
|
+
return [
|
|
684
|
+
{"fieldname": "employee", "label": "Employee", "fieldtype": "Link",
|
|
685
|
+
"options": "Employee", "width": 120},
|
|
686
|
+
{"fieldname": "value", "label": "Value", "fieldtype": "Float",
|
|
687
|
+
"precision": 1, "width": 90},
|
|
688
|
+
# ... more columns
|
|
689
|
+
]
|
|
690
|
+
|
|
691
|
+
def get_data(filters):
|
|
692
|
+
conditions = "WHERE d.docstatus != 2"
|
|
693
|
+
values = {}
|
|
694
|
+
if filters.get("company"):
|
|
695
|
+
conditions += " AND d.company = %(company)s"
|
|
696
|
+
values["company"] = filters["company"]
|
|
697
|
+
# ... more filters
|
|
698
|
+
return frappe.db.sql(f"""
|
|
699
|
+
SELECT d.employee, d.value, ...
|
|
700
|
+
FROM `tabMy Doc` d {conditions}
|
|
701
|
+
ORDER BY d.value DESC
|
|
702
|
+
""", values, as_dict=True)
|
|
703
|
+
|
|
704
|
+
def get_chart(data):
|
|
705
|
+
if not data:
|
|
706
|
+
return None
|
|
707
|
+
top = data[:10]
|
|
708
|
+
return {
|
|
709
|
+
"data": {
|
|
710
|
+
"labels": [r.employee_name for r in top],
|
|
711
|
+
"datasets": [
|
|
712
|
+
{"name": "Value", "values": [r.value for r in top]},
|
|
713
|
+
],
|
|
714
|
+
},
|
|
715
|
+
"type": "bar",
|
|
716
|
+
"colors": ["#2980b9"],
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
def get_report_summary(data):
|
|
720
|
+
if not data:
|
|
721
|
+
return []
|
|
722
|
+
total = len(data)
|
|
723
|
+
avg = sum(r.value for r in data) / total if total else 0
|
|
724
|
+
return [
|
|
725
|
+
{"value": total, "label": "Total", "datatype": "Int", "indicator": "Blue"},
|
|
726
|
+
{"value": round(avg, 1), "label": "Average", "datatype": "Float",
|
|
727
|
+
"indicator": "Green" if avg >= 50 else "Orange"},
|
|
728
|
+
]
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**JavaScript (employee_scoring_summary.js):**
|
|
732
|
+
|
|
733
|
+
```javascript
|
|
734
|
+
frappe.query_reports["Employee Scoring Summary"] = {
|
|
735
|
+
filters: [
|
|
736
|
+
{
|
|
737
|
+
fieldname: "company",
|
|
738
|
+
label: __("Company"),
|
|
739
|
+
fieldtype: "Link",
|
|
740
|
+
options: "Company",
|
|
741
|
+
default: frappe.defaults.get_user_default("Company"),
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
fieldname: "period",
|
|
745
|
+
label: __("Period (YYYY-MM)"),
|
|
746
|
+
fieldtype: "Data",
|
|
747
|
+
default: frappe.datetime.get_today().substring(0, 7),
|
|
748
|
+
},
|
|
749
|
+
],
|
|
750
|
+
};
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 🎨 Layer 7: Shared Client JS
|
|
756
|
+
|
|
757
|
+
```javascript
|
|
758
|
+
/**
|
|
759
|
+
* Global client-side utilities namespace.
|
|
760
|
+
* Loaded via app_include_js in hooks.py.
|
|
761
|
+
*/
|
|
762
|
+
window.myapp = window.myapp || {};
|
|
763
|
+
|
|
764
|
+
// Color-coded badge
|
|
765
|
+
myapp.levelBadge = function (level, colorMap) {
|
|
766
|
+
const info = colorMap[level] || { color: "#888", label: level || "N/A" };
|
|
767
|
+
return `<span style="background:${info.color};color:#fff;padding:2px 8px;
|
|
768
|
+
border-radius:4px;font-size:12px;font-weight:600;">${info.label}</span>`;
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
// Promise-based Frappe method call
|
|
772
|
+
myapp.callMethod = function (method, args) {
|
|
773
|
+
return new Promise((resolve, reject) => {
|
|
774
|
+
frappe.call({
|
|
775
|
+
method, args,
|
|
776
|
+
callback: (r) => r?.message != null ? resolve(r.message) : reject(r),
|
|
777
|
+
error: reject,
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
// Auto-set period from date field
|
|
783
|
+
myapp.autoSetPeriod = function (frm, dateField, periodField) {
|
|
784
|
+
const d = frm.doc[dateField];
|
|
785
|
+
if (d && d.length >= 7) frm.set_value(periodField, d.substring(0, 7));
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// List View Settings with workflow color indicators
|
|
789
|
+
frappe.listview_settings['My Submittable Doc'] = {
|
|
790
|
+
get_indicator(doc) {
|
|
791
|
+
const colors = {
|
|
792
|
+
"Pending": "orange", "Approved": "green",
|
|
793
|
+
"Rejected": "red", "Draft": "gray"
|
|
794
|
+
};
|
|
795
|
+
const c = colors[doc.workflow_state] || "gray";
|
|
796
|
+
return [__(doc.workflow_state), c, "workflow_state,=," + doc.workflow_state];
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
---
|
|
802
|
+
|
|
803
|
+
## 🧪 Layer 6: Testing
|
|
804
|
+
|
|
805
|
+
### Standalone Pure-Logic Tests (No Frappe!)
|
|
806
|
+
|
|
807
|
+
```python
|
|
808
|
+
"""
|
|
809
|
+
Run: python -m pytest my_app/tests/test_engine.py -v
|
|
810
|
+
No Frappe instance needed — tests only pure-logic functions.
|
|
811
|
+
"""
|
|
812
|
+
import unittest
|
|
813
|
+
|
|
814
|
+
# Inline pure-logic (copy from engine, no frappe imports)
|
|
815
|
+
PPH_LEVEL_ORDER = ["needs_improvement", "average", "good", "excellent"]
|
|
816
|
+
_DEFAULT_THRESHOLDS = [
|
|
817
|
+
{"level": "needs_improvement", "min_pph": 0, "max_pph": 35},
|
|
818
|
+
{"level": "average", "min_pph": 35, "max_pph": 55},
|
|
819
|
+
]
|
|
820
|
+
# ... paste pure functions here
|
|
821
|
+
|
|
822
|
+
class TestClassifyLevel(unittest.TestCase):
|
|
823
|
+
def test_boundary(self):
|
|
824
|
+
self.assertEqual(classify_level(35, _DEFAULT_THRESHOLDS), "average")
|
|
825
|
+
|
|
826
|
+
def test_empty_defaults(self):
|
|
827
|
+
self.assertEqual(classify_level(60, []), "good")
|
|
828
|
+
|
|
829
|
+
class TestLevelOrdering(unittest.TestCase):
|
|
830
|
+
def test_gte(self):
|
|
831
|
+
self.assertTrue(level_gte("excellent", "good"))
|
|
832
|
+
self.assertFalse(level_gte("average", "good"))
|
|
833
|
+
|
|
834
|
+
if __name__ == "__main__":
|
|
835
|
+
unittest.main(verbosity=2)
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
**Why inline pure functions in test files?**
|
|
839
|
+
- Tests run with `pytest -v` — no bench, no MariaDB, no Frappe site
|
|
840
|
+
- CI/CD friendly — fast, isolated, reliable
|
|
841
|
+
- Keep the test file self-contained
|
|
842
|
+
|
|
843
|
+
---
|
|
844
|
+
|
|
845
|
+
## 🔄 Workflow Design
|
|
846
|
+
|
|
847
|
+
### Common Workflow Patterns
|
|
848
|
+
|
|
849
|
+
| DocType Category | States | Submittable? |
|
|
850
|
+
|-----------------|--------|--------------|
|
|
851
|
+
| **Violation/Penalty** | Pending → Confirmed → Appealed / Waived | Yes |
|
|
852
|
+
| **Score/Review** | Draft → Manager Review → HR Approved → Final | Yes |
|
|
853
|
+
| **Recovery/Request** | Pending → Approved / Rejected | Yes |
|
|
854
|
+
| **Bonus/Reward** | Pending Approval → Approved / Rejected | Yes |
|
|
855
|
+
| **Config/Master** | N/A (no workflow, just CRUD) | No |
|
|
856
|
+
|
|
857
|
+
### Workflow State Colors
|
|
858
|
+
|
|
859
|
+
```python
|
|
860
|
+
STATE_STYLES = {
|
|
861
|
+
"Draft": ("edit", ""), # Gray
|
|
862
|
+
"Pending": ("time", "Warning"), # Orange
|
|
863
|
+
"Approved": ("ok-sign", "Success"), # Green
|
|
864
|
+
"Rejected": ("remove", "Danger"), # Red
|
|
865
|
+
"Review": ("eye-open","Warning"), # Orange
|
|
866
|
+
"Final": ("star", "Success"), # Green
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
---
|
|
871
|
+
|
|
872
|
+
# Examples
|
|
873
|
+
|
|
874
|
+
## Example 1: Scaffold a New Frappe Custom App
|
|
875
|
+
|
|
876
|
+
**WARNING:** Always use `bench new-app` to create the initial app structure! Do not create it manually, as Frappe Cloud and other tools rely on standard boilerplate files (`setup.py`, `MANIFEST.in`, `patches.txt`, `hooks.py`, etc.) that are generated by `bench`. Failure to do so will result in "Not a valid Frappe App" errors during deployment.
|
|
877
|
+
|
|
878
|
+
```bash
|
|
879
|
+
# 1. Create app (this generates setup.py, pyproject.toml, MANIFEST.in, requirements.txt, etc.)
|
|
880
|
+
bench new-app my_custom_app
|
|
881
|
+
|
|
882
|
+
# 2. Create module structure inside the generated app
|
|
883
|
+
mkdir -p apps/my_custom_app/my_custom_app/{engines,api,tasks,setup,tests,scripts,fixtures}
|
|
884
|
+
mkdir -p apps/my_custom_app/my_custom_app/public/js
|
|
885
|
+
touch apps/my_custom_app/my_custom_app/{engines,api,tasks,setup,tests}/__init__.py
|
|
886
|
+
|
|
887
|
+
# 3. Install on site
|
|
888
|
+
bench --site mysite.localhost install-app my_custom_app
|
|
889
|
+
|
|
890
|
+
# 4. Enable dev mode
|
|
891
|
+
bench --site mysite.localhost set-config developer_mode 1
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
## Example 2: Add a New DocType with Full Stack
|
|
895
|
+
|
|
896
|
+
1. Create DocType via Frappe UI or JSON
|
|
897
|
+
2. Write controller (`.py`) with validate/on_submit/on_cancel
|
|
898
|
+
3. Write client script (`.js`) with refresh/field triggers
|
|
899
|
+
4. Register doc_events in `hooks.py`
|
|
900
|
+
5. Add engine function for business logic
|
|
901
|
+
6. Write API endpoint calling engine
|
|
902
|
+
7. Add scheduler task for batch processing
|
|
903
|
+
8. Write tests for pure logic
|
|
904
|
+
9. `bench --site mysite migrate && bench build --app my_custom_app`
|
|
905
|
+
|
|
906
|
+
---
|
|
907
|
+
|
|
908
|
+
## 🌍 Layer 8: Multi-Language (i18n)
|
|
909
|
+
|
|
910
|
+
### Translation Workflow
|
|
911
|
+
|
|
912
|
+
Frappe uses bare strings wrapped in translation functions: `_("String")` in Python and `__("String")` in JavaScript. Do not use translation keys; use the English baseline string as the key.
|
|
913
|
+
|
|
914
|
+
1. **Wrap all UI-facing strings:**
|
|
915
|
+
- Python: `frappe._("User {0} not found").format(user_id)`
|
|
916
|
+
- JS: `__("User {0} not found", [user_id])`
|
|
917
|
+
2. **Export strings to CSV:**
|
|
918
|
+
`bench --site <site> get-untranslated <language-code> <path/to/output.csv>`
|
|
919
|
+
3. **Translate and Import:**
|
|
920
|
+
Add translations to the CSV, then place the translated translations in `my_app/translations/<lang>.csv`.
|
|
921
|
+
|
|
922
|
+
### Translation Rules
|
|
923
|
+
```
|
|
924
|
+
✅ DO:
|
|
925
|
+
- Use English as the default bare string.
|
|
926
|
+
- Use `{0}`, `{1}` for interpolation (positional args) to allow word reordering in other languages.
|
|
927
|
+
- Run `bench --site <site> migrate` to clear caches and load new translations.
|
|
928
|
+
|
|
929
|
+
❌ DON'T:
|
|
930
|
+
- Translate log messages or internal system errors meant for developers.
|
|
931
|
+
- Use concatenation (`_("Hello") + " " + user`) — always interpolate (`_("Hello {0}").format(user)`).
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
---
|
|
935
|
+
|
|
936
|
+
## 🚀 Layer 9: CI/CD & GitHub Actions
|
|
937
|
+
|
|
938
|
+
When building or fixing CI/CD pipelines (`ci.yml`, `linter.yml`) for Frappe apps, adhere to the following stability rules:
|
|
939
|
+
|
|
940
|
+
### Versions & Environments
|
|
941
|
+
- **Python:** Strictly unify the Python version across ALL jobs (e.g., `python-version: '3.12'`). Avoid alpha versions (e.g., 3.14) that break dependencies.
|
|
942
|
+
- **Node.js:** Use Node 20+ (e.g., `node-version: 20`). Frappe v15 ecosystem deeply relies on packages like `jsdom@29+` which explicitly drop Node 18 support.
|
|
943
|
+
- **GitHub Actions:** Use stable tags (`@v4`, `@v5`). Never guess action versions (`@v6`) as it causes silent failures.
|
|
944
|
+
|
|
945
|
+
### Frappe Installation in CI
|
|
946
|
+
- **Skip Assets:** Always use `--skip-assets` with `bench get-app` to prevent memory exhaustion (OOM errors) and esbuild race conditions during concurrent CI setups.
|
|
947
|
+
- **Local App Registration:** If you `bench get-app /path/to/local/app --skip-assets`, the app is **NOT** added to `sites/apps.txt` automatically.
|
|
948
|
+
- **apps.txt Fix:** Manually append it safely before `bench install-app`:
|
|
949
|
+
```bash
|
|
950
|
+
grep -q my_app sites/apps.txt 2>/dev/null || printf '\nmy_app\n' >> sites/apps.txt
|
|
951
|
+
```
|
|
952
|
+
*(Always use `printf '\n...'` instead of `echo` to prevent concatenating with the last line if it lacks a trailing newline.)*
|
|
953
|
+
|
|
954
|
+
---
|
|
955
|
+
|
|
956
|
+
# Constraints
|
|
957
|
+
|
|
958
|
+
## NEVER
|
|
959
|
+
- Push directly to production branch
|
|
960
|
+
- Delete or modify DocType JSON files without `bench migrate` after
|
|
961
|
+
- Hardcode employee/company references — always use Link fields
|
|
962
|
+
- Skip `frappe.db.commit()` after bulk operations
|
|
963
|
+
- Use `frappe.db.sql` for INSERT/UPDATE when ORM is available
|
|
964
|
+
- Put complex business logic directly in DocType controllers
|
|
965
|
+
- Ignore `docstatus` when querying submitted documents
|
|
966
|
+
|
|
967
|
+
## ALWAYS
|
|
968
|
+
- Run `bench --site <site> migrate` after changing DocType schemas
|
|
969
|
+
- Run `bench build --app <app>` after changing JS/CSS
|
|
970
|
+
- Use `@frappe.whitelist()` decorator for API endpoints
|
|
971
|
+
- Use `frappe.has_permission()` before operations in APIs
|
|
972
|
+
- Separate pure logic into `engines/` for testability
|
|
973
|
+
- Use `frappe.logger("app_name")` for structured logging
|
|
974
|
+
- Make `after_install` and `after_migrate` idempotent
|
|
975
|
+
- Filter by `docstatus = 1` when aggregating submitted records
|
|
976
|
+
- Use `COALESCE(SUM(...), 0)` to avoid NULL in SQL aggregations
|
|
977
|
+
- Add `frappe.db.commit()` at the end of batch task functions
|
|
978
|
+
|
|
979
|
+
## CONFIRM
|
|
980
|
+
- Before running `bench --site <site> reinstall` (destroys data)
|
|
981
|
+
- Before bulk data migration scripts
|
|
982
|
+
- Before modifying workflow states (affects existing records)
|
|
983
|
+
- Before `bench drop-site` or `--force` commands
|
|
984
|
+
|
|
985
|
+
<!-- Generated by Skill Creator Ultra v1.0 -->
|