diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index a0f4f2e..2f08511 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -1,5 +1,13 @@
name: Skill Quality
on: [push, pull_request]
jobs:
+ local-quality:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.x'
+ - run: python3 tools/quick_validate.py skills/source --all
quality:
uses: OpenAEC-Foundation/Skill-Package-Workflow-Template/.github/workflows/skill-quality.yml@main
diff --git a/.gitignore b/.gitignore
index b50ebe3..5c8901e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@ Thumbs.db
.env.local
PROMPTS.md
.claude/
+.hermes/
*.code-workspace
# Node (if any JS tooling)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d73183b..374b6f8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -72,17 +72,26 @@ date = frappe.utils.nowdate()
Before submitting:
-1. Validate SKILL.md format:
+1. Validate one skill:
```bash
- python tools/quick_validate.py skills/source/your-skill/
+ python3 tools/quick_validate.py skills/source/category/your-skill/
```
-2. Check line count:
+2. Validate all skills:
```bash
- wc -l skills/source/your-skill/SKILL.md # Must be <500
+ python3 tools/quick_validate.py skills/source --all
```
-3. Verify all code examples work in ERPNext
+3. Keep every `SKILL.md` at maximum 500 lines, measured with:
+ ```bash
+ python3 - <<'PY'
+ from pathlib import Path
+ p = Path('skills/source/category/your-skill/SKILL.md')
+ print(len(p.read_text(encoding='utf-8').splitlines()))
+ PY
+ ```
+
+4. Verify all code examples work in ERPNext
## Commit Message Format
diff --git a/INSTALL.md b/INSTALL.md
index 1f15d0c..bc62f64 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,9 +1,11 @@
--e # Installation Guide
+# Installation Guide
-> **This file has been replaced by [USAGE.md](USAGE.md)**
+> **This file has been replaced by [USAGE.md](USAGE.md).**
-Please see [USAGE.md](USAGE.md) for platform-specific installation guides:
+Please see:
+- [USAGE.md](USAGE.md) for the main guide
+- [Agent Harnesses: OpenCode, Codex, skills.sh](docs/usage/agent-harnesses.md)
- [Claude Code Installation](docs/usage/claude-code.md)
- [Claude.ai Web Installation](docs/usage/claude-web.md)
- [Claude Desktop Installation](docs/usage/claude-desktop.md)
diff --git a/MIGRATION-AGENT-SKILLS-STANDARD.md b/MIGRATION-AGENT-SKILLS-STANDARD.md
index 5701f4a..21bf9d2 100644
--- a/MIGRATION-AGENT-SKILLS-STANDARD.md
+++ b/MIGRATION-AGENT-SKILLS-STANDARD.md
@@ -72,15 +72,16 @@ Voeg aan INSTALL.md of USAGE.md de volgende sectie toe:
```markdown
### Globale installatie (Claude Code CLI)
-Kopieer alle skills naar je globale skills directory zodat ze in elk project beschikbaar zijn:
+Kopieer alle skill folders naar je globale skills directory zodat ze in elk project beschikbaar zijn:
-cp -r skills/source/* ~/.claude/skills/
+mkdir -p ~/.claude/skills
+cp -R skills/source/*/* ~/.claude/skills/
De skills gebruiken progressive disclosure: bij startup laadt Claude alleen
de name en description (~100 tokens per skill). De volledige instructies
worden pas geladen wanneer een skill relevant is voor je vraag.
-28 skills ร ~100 tokens = ~2.800 tokens startup-overhead. Dit is verwaarloosbaar
+61 skills ร ~100 tokens = ~6.100 tokens startup-overhead. Dit is verwaarloosbaar
op een context window van 200k tokens.
```
diff --git a/README.md b/README.md
index 5812b71..9f0f1ba 100644
--- a/README.md
+++ b/README.md
@@ -56,26 +56,36 @@ This package encodes **61 hard-won lessons** like this into deterministic skills
## ๐ Installation
-### Claude Code (Recommended)
+### OpenCode, Codex, and skills.sh
+
+See [`docs/usage/agent-harnesses.md`](docs/usage/agent-harnesses.md) for verified OpenCode, Codex, Claude Code, and `skills.sh` install paths.
+
+```bash
+# Safe discovery command; lists all 61 skills without installing
+npx skills@1.5.13 add Impertio-Studio/Frappe_Claude_Skill_Package --list
+```
+
+### Claude Code
```bash
# Clone the repository
-git clone https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package.git
+git clone https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package.git
-# Copy all 61 skills to your Claude Code skills directory
-cp -r Frappe_Claude_Skill_Package/skills/source/* ~/.claude/skills/
+# Copy all 61 skill folders to your Claude Code skills directory
+mkdir -p ~/.claude/skills
+cp -R Frappe_Claude_Skill_Package/skills/source/*/* ~/.claude/skills/
```
### Claude.ai Web/Desktop
1. Download skill folders from [`skills/source/`](skills/source/)
-2. ZIP each folder individually
+2. ZIP each skill folder individually, with `SKILL.md` at the folder root
3. Upload via **Settings โ Capabilities โ Skills**
### Claude.ai Projects
1. Create a new project
-2. Upload `SKILL.md` files to the **Knowledge** section
+2. Upload relevant `SKILL.md` files to the **Knowledge** section
---
@@ -111,7 +121,7 @@ This package also serves as a **template** for building Claude skill packages in
See [`WAY_OF_WORK.md`](WAY_OF_WORK.md) for the methodology we used to build these skills.
-**Found an issue?** [Open an issue](https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package/issues/new)
+**Found an issue?** [Open an issue](https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package/issues/new)
**Want to contribute?** PRs welcome!
---
@@ -145,5 +155,5 @@ MIT โ See [LICENSE.md](LICENSE.md) for details.
- โญ Star this repo if it helps you!
+ โญ Star this repo if it helps you!
diff --git a/SOURCES.md b/SOURCES.md
index 864082d..c450e5d 100644
--- a/SOURCES.md
+++ b/SOURCES.md
@@ -12,6 +12,7 @@
| Source | URL | Coverage | Last Verified |
|--------|-----|----------|---------------|
| Frappe Framework Docs | https://docs.frappe.io/framework | Core framework API, hooks, controllers | 2026-01-17 |
+| Frappe Framework Introduction | https://docs.frappe.io/framework/user/en/introduction | Framework overview, metadata-as-data model, Desk, permissions, REST API | 2026-06-24 |
| Database API | https://docs.frappe.io/framework/user/en/api/database | frappe.db methods, query builder | 2026-01-17 |
| Document API | https://docs.frappe.io/framework/user/en/api/document | frappe.get_doc, document lifecycle | 2026-01-17 |
| Form API | https://docs.frappe.io/framework/user/en/api/form | Client-side form manipulation | 2026-01-17 |
@@ -55,6 +56,11 @@
| Source | URL | Purpose | Last Verified |
|--------|-----|---------|---------------|
| Frappe Source | https://github.com/frappe/frappe | Source code verification | 2026-01-17 |
+| Frappe Docker | https://github.com/frappe/frappe_docker | Official Docker/container setup repository for Frappe apps | 2026-06-24 |
+| Frappe Docker Docs | https://frappe.github.io/frappe_docker/ | Published Docker setup documentation | 2026-06-24 |
+| Frappe Docker Getting Started | https://frappe.github.io/frappe_docker/getting-started.html | Docker architecture, repo layout, services, images, overrides | 2026-06-24 |
+| Frappe Docker Single Compose Setup | https://frappe.github.io/frappe_docker/01-getting-started/04-single-compose-setup.html | `pwd.yml` demo setup, services, volumes, adaptation notes | 2026-06-24 |
+| Frappe Docker Development | https://frappe.github.io/frappe_docker/05-development/01-development.html | Devcontainer and development bench workflow | 2026-06-24 |
| ERPNext Releases | https://github.com/frappe/erpnext/releases | Version change tracking | 2026-01-17 |
| Frappe Permissions | https://github.com/frappe/frappe/blob/develop/frappe/permissions.py | Permission internals | 2026-01-17 |
| Document Model | https://github.com/frappe/frappe/blob/develop/frappe/model/document.py | Document lifecycle | 2026-01-17 |
diff --git a/USAGE.md b/USAGE.md
index b5011c8..016ad1c 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -1,78 +1,47 @@
-# Using the ERPNext Skills Package
+# Using the Frappe Skills Package
-This guide shows you how to install and use the ERPNext Skills Package across different Claude platforms.
+This guide shows how to install and use the 61 Frappe/ERPNext Agent Skills across Claude platforms and other agent harnesses.
## Quick Start
| Platform | Installation Time | Difficulty |
|----------|:-----------------:|:----------:|
-| Claude Code (CLI) | 2 minutes | Easy |
-| Claude.ai Web | 5 minutes | Easy |
-| Claude Desktop | 5 minutes | Easy |
+| Claude Code | 2 minutes | Easy |
+| OpenCode / Codex | 2 minutes | Easy |
+| Claude.ai Web/Desktop | 5 minutes | Easy |
| Claude Mobile | โ Not supported | - |
## Prerequisites
-- **Claude Code**: Active Claude Code installation
+- **Claude Code**: active Claude Code installation
+- **OpenCode/Codex**: agent with Agent Skills support
- **Claude.ai / Desktop**: Pro, Max, Team, or Enterprise plan with Code Execution enabled
## Platform-Specific Guides
-- [Claude Code Installation](docs/usage/claude-code.md) - Recommended for developers
-- [Claude.ai Web Installation](docs/usage/claude-web.md) - Browser-based usage
-- [Claude Desktop Installation](docs/usage/claude-desktop.md) - Desktop app usage
+- [Agent Harnesses: OpenCode, Codex, skills.sh](docs/usage/agent-harnesses.md)
+- [Claude Code Installation](docs/usage/claude-code.md)
+- [Claude.ai Web Installation](docs/usage/claude-web.md)
+- [Claude Desktop Installation](docs/usage/claude-desktop.md)
## What's Included
-This package contains 28 skills organized by category:
-
-### Syntax Skills (8)
-Reference guides for ERPNext/Frappe code patterns:
-- `syntax-client-scripts` - Client Script syntax and events
-- `syntax-server-scripts` - Server Script sandbox rules
-- `syntax-controllers` - Document controller methods
-- `syntax-hooks` - hooks.py configuration
-- `syntax-whitelisted` - @frappe.whitelist() patterns
-- `syntax-jinja` - Jinja templating in print formats
-- `syntax-scheduler` - Scheduled job configuration
-- `syntax-custom-app` - Custom app structure
-
-### Core Skills (3)
-Fundamental Frappe framework knowledge:
-- `core-database` - Database operations and ORM
-- `core-permissions` - Permission system
-- `core-api-patterns` - API design patterns
-
-### Implementation Skills (8)
-Step-by-step workflows:
-- `impl-client-scripts` - Client Script implementation
-- `impl-server-scripts` - Server Script implementation
-- `impl-controllers` - Controller implementation
-- `impl-hooks` - Hooks implementation
-- `impl-database` - Database operation workflows
-- `impl-permissions` - Permission implementation
-- `impl-api` - API implementation
-- `impl-scheduler` - Scheduler implementation
-- `impl-jinja` - Print format implementation
-
-### Error Handling Skills (7)
-Debugging and troubleshooting:
-- `errors-client` - Client-side error handling
-- `errors-server` - Server-side error handling
-- `errors-database` - Database error handling
-- `errors-permissions` - Permission error handling
-- `errors-api` - API error handling
-- `errors-scheduler` - Scheduler error handling
-- `errors-print` - Print format error handling
-
-### Agents (2)
-Intelligent assistants:
-- `agent-erpnext-dev` - Full-stack ERPNext development
-- `agent-code-review` - ERPNext code review
+This package contains 61 skills organized by category:
+
+| Category | Count | Examples |
+|---|:---:|---|
+| Syntax | 13 | `frappe-syntax-serverscripts`, `frappe-syntax-doctypes`, `frappe-syntax-query-builder` |
+| Core | 11 | `frappe-core-database`, `frappe-core-permissions`, `frappe-core-api` |
+| Implementation | 14 | `frappe-impl-serverscripts`, `frappe-impl-customapp`, `frappe-impl-workflow` |
+| Error Handling | 7 | `frappe-errors-serverscripts`, `frappe-errors-database`, `frappe-errors-api` |
+| Operations | 9 | `frappe-ops-deployment`, `frappe-ops-backup`, `frappe-ops-bench` |
+| Agents | 5 | `frappe-agent-validator`, `frappe-agent-architect`, `frappe-agent-debugger` |
+| Testing | 2 | `frappe-testing-unit`, `frappe-testing-cicd` |
## Version Compatibility
-All skills support:
+All skills document version-specific behavior for:
+
- **Frappe/ERPNext v14** โ
- **Frappe/ERPNext v15** โ
- **Frappe/ERPNext v16** โ
@@ -81,41 +50,41 @@ Version-specific differences are documented within each skill.
## How Skills Work
-When you start a conversation, Claude loads only the skill names and descriptions (~100 tokens per skill). When your request matches a skill's description, Claude loads the full instructions. This "progressive disclosure" means you can have all 28 skills available without context bloat.
+When you start a conversation, the agent loads only skill names and descriptions. When your request matches a skill description, it loads the full instructions. This progressive disclosure keeps the 61-skill package usable without loading every full skill into context.
### Triggering Skills
Skills activate automatically based on your request:
-```
+```text
You: "Help me create a Server Script that validates Sales Orders"
-Claude: [Loads syntax-server-scripts and impl-server-scripts automatically]
+Agent: [Loads frappe-syntax-serverscripts and frappe-impl-serverscripts when relevant]
```
You can also reference skills explicitly:
-```
-You: "Using the server-scripts skill, show me the sandbox limitations"
+```text
+You: "Using frappe-syntax-serverscripts, show me the sandbox limitations"
```
### Checking Available Skills
-Ask Claude:
-```
-You: "What ERPNext skills do you have access to?"
+Ask your agent:
+
+```text
+What Frappe skills do you have access to?
```
## Global Installation (Claude Code CLI)
-Copy all skills to your global skills directory so they're available in every project:
+Copy all skill folders to your global skills directory so they're available in every project:
```bash
-cp -r skills/source/* ~/.claude/skills/
+mkdir -p ~/.claude/skills
+cp -R skills/source/*/* ~/.claude/skills/
```
-The skills use progressive disclosure: at startup Claude only loads the name and description (~100 tokens per skill). Full instructions are loaded only when a skill is relevant to your request.
-
-28 skills x ~100 tokens = ~2,800 tokens startup overhead. This is negligible on a 200k token context window.
+The skills use progressive disclosure: at startup the agent only loads each name and description. Full instructions are loaded only when a skill is relevant to your request.
## Critical: Server Script Sandbox
@@ -137,9 +106,9 @@ This is the #1 cause of AI-generated ERPNext code failures. All skills in this p
## Support
-- **Issues**: [GitHub Issues](https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package/issues)
+- **Issues**: [GitHub Issues](https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package/issues)
- **Documentation**: [Full Documentation](docs/)
## License
-LGPL-3.0 License - See [LICENSE](LICENSE.md) for details.
+MIT License - See [LICENSE](LICENSE.md) for details.
diff --git a/docs/usage/agent-harnesses.md b/docs/usage/agent-harnesses.md
new file mode 100644
index 0000000..d7fcb06
--- /dev/null
+++ b/docs/usage/agent-harnesses.md
@@ -0,0 +1,138 @@
+# Agent Harness Installation Guide
+
+Install the Frappe skill package in OpenCode, Codex, Claude Code, or any tool that consumes Agent Skills (`SKILL.md` folders).
+
+Canonical source used in commands:
+
+```text
+Impertio-Studio/Frappe_Claude_Skill_Package
+```
+
+## Recommended: skills.sh / npx skills
+
+First list the skills. This is safe and does not install anything:
+
+```bash
+npx skills@1.5.13 add Impertio-Studio/Frappe_Claude_Skill_Package --list
+```
+
+Install one skill for Codex:
+
+```bash
+npx skills@1.5.13 add Impertio-Studio/Frappe_Claude_Skill_Package \
+ --skill frappe-core-database \
+ --agent codex
+```
+
+Install one skill for OpenCode:
+
+```bash
+npx skills@1.5.13 add Impertio-Studio/Frappe_Claude_Skill_Package \
+ --skill frappe-core-database \
+ --agent opencode
+```
+
+Install all skills only after reviewing the list:
+
+```bash
+npx skills@1.5.13 add Impertio-Studio/Frappe_Claude_Skill_Package \
+ --skill '*' \
+ --agent codex \
+ --agent opencode
+```
+
+### Global/unattended install warning
+
+`--global` writes into global agent config. `--yes` skips prompts. Use them only when you are comfortable overwriting or merging existing skill links/copies.
+
+```bash
+npx skills@1.5.13 add Impertio-Studio/Frappe_Claude_Skill_Package \
+ --skill frappe-core-database \
+ --global \
+ --agent codex \
+ --yes
+```
+
+## Manual fallback: OpenCode
+
+OpenCode reads project and global skill folders including `.opencode/skills`, `.claude/skills`, and `.agents/skills`.
+
+Global shared Agent Skills path:
+
+```bash
+mkdir -p ~/.agents/skills
+cp -R skills/source/*/* ~/.agents/skills/
+```
+
+OpenCode-native global path:
+
+```bash
+mkdir -p ~/.config/opencode/skills
+cp -R skills/source/*/* ~/.config/opencode/skills/
+```
+
+Project-local OpenCode path:
+
+```bash
+mkdir -p .opencode/skills
+cp -R /path/to/Frappe_Claude_Skill_Package/skills/source/*/* .opencode/skills/
+```
+
+## Manual fallback: Codex
+
+Codex reads Agent Skills from `.agents/skills` in repo/user scopes.
+
+Global:
+
+```bash
+mkdir -p ~/.agents/skills
+cp -R skills/source/*/* ~/.agents/skills/
+```
+
+Project-local:
+
+```bash
+mkdir -p .agents/skills
+cp -R /path/to/Frappe_Claude_Skill_Package/skills/source/*/* .agents/skills/
+```
+
+## Manual fallback: Claude Code
+
+```bash
+mkdir -p ~/.claude/skills
+cp -R skills/source/*/* ~/.claude/skills/
+```
+
+## Updating and uninstalling
+
+Manual `cp -R` installs are flat copies. Re-running the copy command can overwrite local edits in installed skill folders.
+
+To update manual installs:
+
+```bash
+cd Frappe_Claude_Skill_Package
+git pull
+cp -R skills/source/*/* ~/.agents/skills/
+```
+
+To uninstall manual installs, delete the copied `frappe-*` folders from the target directory:
+
+```bash
+rm -rf ~/.agents/skills/frappe-*
+```
+
+For `skills.sh`, prefer the CLI's own update/remove commands when available:
+
+```bash
+npx skills@1.5.13 update
+npx skills@1.5.13 remove frappe-core-database
+```
+
+## Troubleshooting
+
+- Skill not visible: verify the folder contains `SKILL.md` directly, e.g. `~/.agents/skills/frappe-core-database/SKILL.md`.
+- Wrong copy depth: use `skills/source/*/*`, not `skills/source/*`.
+- Duplicates: remove older copies from other global paths (`~/.agents/skills`, `~/.claude/skills`, `~/.config/opencode/skills`).
+- `npx skills` failure: use the manual OpenCode/Codex paths above.
+
+See [`../validation/agent-harness-support-2026-06-24.md`](../validation/agent-harness-support-2026-06-24.md) for the source and command verification behind this guide.
diff --git a/docs/usage/claude-code.md b/docs/usage/claude-code.md
index f944837..7ed6ee0 100644
--- a/docs/usage/claude-code.md
+++ b/docs/usage/claude-code.md
@@ -2,6 +2,8 @@
Install the Frappe Claude Skill Package in Claude Code for terminal-based development.
+For OpenCode, Codex, and `skills.sh`, see [Agent Harnesses](agent-harnesses.md).
+
## Installation Methods
### Method 1: Personal Skills (Recommended)
@@ -10,21 +12,23 @@ Personal skills are available across all your projects.
```bash
# Clone the repository
-git clone https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package.git
+git clone https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package.git
-# Copy skills to personal directory
-cp -r Frappe_Claude_Skill_Package/skills/source/* ~/.claude/skills/
+# Copy skill folders to personal directory
+mkdir -p ~/.claude/skills
+cp -R Frappe_Claude_Skill_Package/skills/source/*/* ~/.claude/skills/
# Verify installation
-ls ~/.claude/skills/
+ls ~/.claude/skills/frappe-*/SKILL.md
```
-Expected output:
-```
-syntax-client-scripts/
-syntax-server-scripts/
-syntax-controllers/
-... (28 skill folders)
+Expected output includes:
+
+```text
+~/.claude/skills/frappe-syntax-serverscripts/SKILL.md
+~/.claude/skills/frappe-core-database/SKILL.md
+~/.claude/skills/frappe-impl-serverscripts/SKILL.md
+... (61 skill folders)
```
### Method 2: Project Skills (Team Sharing)
@@ -35,12 +39,12 @@ Project skills are committed to version control and shared with your team.
# In your ERPNext project directory
mkdir -p .claude/skills
-# Copy skills
-cp -r /path/to/Frappe_Claude_Skill_Package/skills/source/* .claude/skills/
+# Copy skill folders
+cp -R /path/to/Frappe_Claude_Skill_Package/skills/source/*/* .claude/skills/
# Commit to version control
git add .claude/skills
-git commit -m "Add ERPNext development skills"
+git commit -m "Add Frappe development skills"
```
### Method 3: Selective Installation
@@ -48,30 +52,31 @@ git commit -m "Add ERPNext development skills"
Install only the skills you need:
```bash
-# Example: Install only Server Script related skills
-cp -r skills/source/syntax-server-scripts ~/.claude/skills/
-cp -r skills/source/impl-server-scripts ~/.claude/skills/
-cp -r skills/source/errors-server ~/.claude/skills/
+# Example: install only Server Script related skills
+cp -R skills/source/syntax/frappe-syntax-serverscripts ~/.claude/skills/
+cp -R skills/source/impl/frappe-impl-serverscripts ~/.claude/skills/
+cp -R skills/source/errors/frappe-errors-serverscripts ~/.claude/skills/
```
## Verification
-Start Claude Code and verify skills are loaded:
+Start Claude Code:
```bash
claude
```
Then ask:
-```
-What ERPNext skills do you have access to?
+
+```text
+What Frappe skills do you have access to?
```
-Claude should list all installed skills with their descriptions.
+Claude should list installed `frappe-*` skills with descriptions.
## Skill Priority
-If the same skill exists in multiple locations, priority is (highest first):
+If the same skill exists in multiple locations, priority is usually:
1. **Managed** - Organization admin settings
2. **Personal** - `~/.claude/skills/`
@@ -83,7 +88,7 @@ If the same skill exists in multiple locations, priority is (highest first):
Create a `CLAUDE.md` in your project root for project-specific context:
```markdown
-# ERPNext Project Configuration
+# Frappe Project Configuration
## Framework Version
This project uses Frappe/ERPNext v15.
@@ -94,23 +99,21 @@ This project uses Frappe/ERPNext v15.
- Always handle permissions explicitly
## Custom Apps
-- `custom_app/` - Our custom ERPNext app
+- `custom_app/` - Our custom Frappe app
```
## Troubleshooting
### Skills not appearing
-1. Check YAML syntax in SKILL.md files:
+1. Check the copy depth. `SKILL.md` must be directly inside each skill folder:
```bash
- head -10 ~/.claude/skills/syntax-server-scripts/SKILL.md
+ ls ~/.claude/skills/frappe-syntax-serverscripts/SKILL.md
```
-
2. Run Claude with debug mode:
```bash
claude --debug
```
-
3. Verify file permissions:
```bash
ls -la ~/.claude/skills/
@@ -119,18 +122,21 @@ This project uses Frappe/ERPNext v15.
### Wrong skill triggered
Make your request more specific. Instead of:
-```
+
+```text
Help me with a script
```
Use:
-```
+
+```text
Help me create a Server Script for Sales Order validation
```
### Scripts not executing
Ensure execute permissions:
+
```bash
chmod +x ~/.claude/skills/*/scripts/*.py 2>/dev/null
```
@@ -140,15 +146,17 @@ chmod +x ~/.claude/skills/*/scripts/*.py 2>/dev/null
```bash
cd Frappe_Claude_Skill_Package
git pull
-cp -r skills/source/* ~/.claude/skills/
+cp -R skills/source/*/* ~/.claude/skills/
```
+Re-copying can overwrite local edits in installed skill folders. If you edit installed copies, back them up first.
+
## Monorepo Support
-Claude Code automatically discovers skills in nested directories. If you're editing files in `packages/erpnext-custom/`, Claude also looks for skills in `packages/erpnext-custom/.claude/skills/`.
+Claude Code can discover project skills in nested `.claude/skills/` directories. Keep each skill as `/SKILL.md` directly under that directory.
## Next Steps
- Read [USAGE.md](../../USAGE.md) for skill overview
-- Check individual skill documentation in `skills/source/*/SKILL.md`
+- Check individual skill documentation in `skills/source/*/*/SKILL.md`
- Review [LESSONS.md](../../LESSONS.md) for common pitfalls
diff --git a/docs/usage/claude-web.md b/docs/usage/claude-web.md
index c366321..87c3048 100644
--- a/docs/usage/claude-web.md
+++ b/docs/usage/claude-web.md
@@ -1,6 +1,6 @@
# Claude.ai Web Installation Guide
-Install the ERPNext Skills Package in the Claude.ai web interface.
+Install the Frappe Skills Package in the Claude.ai web interface.
## Prerequisites
@@ -17,14 +17,14 @@ Install the ERPNext Skills Package in the Claude.ai web interface.
### Option A: Download Pre-packaged Skills
-Download the `.skill` files from the repository:
+Download `.skill` files from the repository if packaged artifacts are published:
-```
+```text
skills/packaged/
-โโโ syntax-client-scripts.skill
-โโโ syntax-server-scripts.skill
-โโโ syntax-controllers.skill
-... (28 .skill files)
+โโโ frappe-syntax-serverscripts.skill
+โโโ frappe-core-database.skill
+โโโ frappe-impl-serverscripts.skill
+... (61 .skill files)
```
Each `.skill` file is a ZIP archive ready for upload.
@@ -33,17 +33,17 @@ Each `.skill` file is a ZIP archive ready for upload.
For each skill you want to install:
-1. Download the skill folder (e.g., `syntax-server-scripts/`)
+1. Download the skill folder, e.g. `skills/source/syntax/frappe-syntax-serverscripts/`
2. Create a ZIP file with the folder as root:
- ```
- syntax-server-scripts.zip
- โโโ syntax-server-scripts/
+ ```text
+ frappe-syntax-serverscripts.zip
+ โโโ frappe-syntax-serverscripts/
โโโ SKILL.md
โโโ references/
โโโ ...
```
-**Important**: The ZIP must contain the skill folder, not just its contents.
+**Important**: the ZIP must contain the skill folder, not just its contents.
## Step 3: Upload Skills
@@ -56,39 +56,29 @@ For each skill you want to install:
## Step 4: Verify Installation
1. Start a new conversation
-2. Ask: "What ERPNext skills do you have access to?"
+2. Ask: "What Frappe skills do you have access to?"
3. Claude should list your installed skills
-## Managing Skills
-
-### Toggle Skills On/Off
+## Recommended Installation Order
-In **Settings** > **Capabilities** > **Skills**, use the toggle switch next to each skill to enable or disable it.
+For most Frappe development work, install these skills first:
-### Remove a Skill
+### Essential
-1. Go to **Settings** > **Capabilities** > **Skills**
-2. Find the skill you want to remove
-3. Click the delete/remove option
-4. Confirm deletion
-
-## Recommended Installation Order
+1. `frappe-syntax-serverscripts` - Server Script sandbox rules
+2. `frappe-core-database` - Database operations
+3. `frappe-impl-serverscripts` - Server Script workflows
-For most ERPNext development work, install these skills first:
+### Extended
-### Essential (Start Here)
-1. `syntax-server-scripts` - Server Script sandbox rules
-2. `core-database` - Database operations
-3. `impl-server-scripts` - Server Script workflows
-
-### Extended (Add as Needed)
-4. `syntax-client-scripts` - Client Script patterns
-5. `syntax-controllers` - Controller methods
-6. `core-permissions` - Permission system
-7. `errors-server` - Server error handling
+4. `frappe-syntax-clientscripts` - Client Script patterns
+5. `frappe-syntax-controllers` - Controller methods
+6. `frappe-core-permissions` - Permission system
+7. `frappe-errors-serverscripts` - Server error handling
### Full Package
-Install all 28 skills for comprehensive coverage.
+
+Install all 61 skills for comprehensive coverage.
## Skill Name Requirements
@@ -96,50 +86,53 @@ If you're creating ZIPs manually, ensure:
- Folder name is **lowercase**
- Only letters, numbers, and **hyphens** allowed
-- Example: `syntax-server-scripts` โ
-- Example: `Syntax_Server_Scripts` โ
+- Example: `frappe-syntax-serverscripts` โ
+- Example: `Frappe_Syntax_ServerScripts` โ
-## Team and Enterprise Plans
+## Managing Skills
+
+### Toggle Skills On/Off
-### Organization-Wide Skills
+In **Settings** > **Capabilities** > **Skills**, use the toggle switch next to each skill.
-Admins can provision skills for all users:
+### Remove a Skill
-1. Go to **Admin Settings** > **Capabilities**
-2. Upload skills in the organization section
-3. Skills appear for all users with a team indicator
+1. Go to **Settings** > **Capabilities** > **Skills**
+2. Find the skill you want to remove
+3. Click the delete/remove option
+4. Confirm deletion
-### Individual Skills
+## Team and Enterprise Plans
-Users can still upload personal skills that are private to their account.
+Admins can provision skills for all users from admin capability settings. Users can still upload personal skills when allowed by organization policy.
## Troubleshooting
### "Skills appear greyed out"
- Check that Code Execution is enabled
-- For Team/Enterprise: Check with your admin that Skills are enabled org-wide
+- For Team/Enterprise: check with your admin that Skills are enabled org-wide
### Upload fails
-1. Check ZIP structure (folder must be at root)
-2. Verify SKILL.md starts with `---` on line 1
+1. Check ZIP structure: folder must be at root
+2. Verify `SKILL.md` starts with `---` on line 1
3. Ensure skill name is lowercase with hyphens
### Skill doesn't trigger
- Make your request more specific
-- Mention the skill type: "Using Server Script patterns..."
+- Mention the skill name: "Using `frappe-syntax-serverscripts`..."
- Check that the skill is toggled ON in settings
## Storage and Privacy
-- Custom skills are **private** to your account
-- Skills run in Claude's secure sandboxed environment
-- No data persists between sessions
+- Custom skills are private to your account unless provisioned by an organization admin
+- Skills run in Claude's sandboxed environment
+- No data persists between sessions unless explicitly saved by the platform
## Next Steps
- Read [USAGE.md](../../USAGE.md) for skill overview
-- Check individual skill documentation in `skills/source/*/SKILL.md`
+- Check individual skill documentation in `skills/source/*/*/SKILL.md`
- Review [LESSONS.md](../../LESSONS.md) for common pitfalls
diff --git a/docs/validation/agent-harness-support-2026-06-24.md b/docs/validation/agent-harness-support-2026-06-24.md
new file mode 100644
index 0000000..849ded3
--- /dev/null
+++ b/docs/validation/agent-harness-support-2026-06-24.md
@@ -0,0 +1,39 @@
+# Agent Harness Support Verification โ 2026-06-24
+
+This report backs the install paths and commands added for OpenCode, Codex, and `skills.sh` / `npx skills` support.
+
+## Sources checked
+
+| Claim | URL | Retrieved | Result |
+|---|---|---:|---|
+| OpenCode discovers `SKILL.md` folders from `.opencode/skills`, `.claude/skills`, and `.agents/skills`, including global paths. | https://opencode.ai/docs/skills/ | 2026-06-24 | Confirmed. |
+| OpenCode recognizes required `name` and `description`, optional `license`, `compatibility`, and `metadata`; unknown fields are ignored. | https://opencode.ai/docs/skills/ | 2026-06-24 | Confirmed. Current skill frontmatter is compatible. |
+| Codex skills are folders containing `SKILL.md`; Codex scans `.agents/skills` in repo/user scopes. | https://developers.openai.com/codex/skills | 2026-06-24 | Confirmed. |
+| Codex supports explicit skill invocation with `/skills` / `$skill-name` and loads full `SKILL.md` on demand. | https://developers.openai.com/codex/skills | 2026-06-24 | Confirmed. |
+| `npx skills add --list`, `--skill`, `--agent`, `--global`, `--copy`, and `--yes` are supported options. | https://github.com/vercel-labs/skills | 2026-06-24 | Confirmed. |
+| `skills` CLI supports OpenCode and Codex as target agents. | https://github.com/vercel-labs/skills | 2026-06-24 | Confirmed by docs and local command smoke test. |
+
+## Commands run
+
+| Command | Result | Notes |
+|---|---|---|
+| `npx skills@1.5.13 add . --list` | Passed; found 61 skills and listed `frappe-*` skills. | Run from repo root. Output saved during implementation at `/tmp/frappe-skills-list.txt`. |
+| `HOME=$(mktemp -d) npx skills@1.5.13 add /Users/dwk/Projects/Frappe_Claude_Skill_Package --skill frappe-core-database --agent codex --yes --copy` | Passed; installed one copied skill to `./.agents/skills/frappe-core-database`. | First run used repo CWD and created temporary untracked `.agents/` + `skills-lock.json`; both were removed. |
+| `cd $(mktemp -d) && HOME=$(mktemp -d) npx skills@1.5.13 add /Users/dwk/Projects/Frappe_Claude_Skill_Package --skill frappe-core-database --agent codex --yes --copy` | Passed; installed `frappe-core-database/SKILL.md` in the temp workdir's `.agents/skills`. | Isolated smoke test; did not touch repo/global config. Output saved at `/tmp/frappe-skills-temp-install-isolated.txt`. |
+
+## Claims backed
+
+| File/section | Claim | Evidence |
+|---|---|---|
+| `docs/usage/agent-harnesses.md` / `skills.sh` | `npx skills@1.5.13 add --list` lists available skills. | `vercel-labs/skills` docs and local `npx skills@1.5.13 add . --list` run. |
+| `docs/usage/agent-harnesses.md` / OpenCode | OpenCode can read `~/.agents/skills` and `~/.config/opencode/skills`. | OpenCode docs. |
+| `docs/usage/agent-harnesses.md` / Codex | Codex can read `.agents/skills` and `$HOME/.agents/skills`. | OpenAI Codex skills docs. |
+| `README.md`, `USAGE.md`, `docs/usage/claude-code.md` | Correct copy depth is `skills/source/*/*`, because each skill folder is under `skills/source///SKILL.md`. | Repo structure: 61 `skills/source/*/*/SKILL.md` files. |
+| `tools/quick_validate.py` | `package.json agents.skills` should stay in sync with discovered skills. | `package.json` contains 61 manifest entries and repo contains 61 skills. |
+
+## Unsupported / not claimed
+
+- No Codex plugin package is built in this change.
+- No `npx skills` command is added to CI.
+- No generated `.agents/`, `.opencode/`, or `.claude/` skill tree is committed.
+- No guarantee is made that unattended `--global --yes` installs are safe for every user's existing agent configuration; docs warn before showing that command.
diff --git a/docs/validation/frappe-source-verification-2026-06-24.md b/docs/validation/frappe-source-verification-2026-06-24.md
new file mode 100644
index 0000000..9c7cd75
--- /dev/null
+++ b/docs/validation/frappe-source-verification-2026-06-24.md
@@ -0,0 +1,59 @@
+# Frappe Source Verification โ 2026-06-24
+
+## Scope
+
+Implemented structural validation and a minimal source audit for the requested official sources:
+
+- `https://github.com/frappe/frappe_docker`
+- `https://docs.frappe.io/framework/user/en/introduction`
+
+The validator checks repo structure only. It does **not** prove Frappe API correctness or Docker operational accuracy.
+
+## Official URLs checked
+
+All URLs below returned HTTP 200 on 2026-06-24.
+
+| URL | Retrieved | Used for |
+|---|---:|---|
+| https://docs.frappe.io/framework/user/en/introduction | 2026-06-24 | Framework overview source entry: Python/JavaScript/MariaDB, metadata-as-data, Desk, permissions, REST API |
+| https://github.com/frappe/frappe_docker | 2026-06-24 | Official repository source entry for Docker/container setup |
+| https://frappe.github.io/frappe_docker/ | 2026-06-24 | Published Frappe Docker docs root source entry |
+| https://frappe.github.io/frappe_docker/getting-started.html | 2026-06-24 | Frappe Docker architecture/repo layout/services/images/overrides source entry |
+| https://frappe.github.io/frappe_docker/01-getting-started/04-single-compose-setup.html | 2026-06-24 | Single compose `pwd.yml` services/volumes/adaptation source entry |
+| https://frappe.github.io/frappe_docker/05-development/01-development.html | 2026-06-24 | Devcontainer/development workflow source entry |
+
+## Changed factual claims
+
+| File | Claim changed | Source URL | Reviewer check |
+|---|---|---|---|
+| `tools/quick_validate.py` | Validator is structural only and uses stdlib parsing, not PyYAML. | Repo-local implementation; no external Frappe claim. | Verified by code and `python3 tools/quick_validate.py skills/source --all`. |
+| `CONTRIBUTING.md` | Skill line limit is maximum 500 lines measured with Python `splitlines()`. | Repo-local policy reconciliation; no external Frappe claim. | Matches `tools/quick_validate.py`. |
+| `SOURCES.md` | Frappe Framework introduction is an approved source for high-level framework overview. | https://docs.frappe.io/framework/user/en/introduction | URL returned 200; source is official Frappe docs. |
+| `SOURCES.md` | `frappe_docker` repo and selected Frappe Docker docs are approved sources for Docker/container setup. | https://github.com/frappe/frappe_docker and `frappe.github.io/frappe_docker` pages above | URLs returned 200; sources are official repo/docs. |
+| `skills/source/ops/frappe-ops-website-deploy/SKILL.md` | Description now starts with `Use when` and keeps v15-v16 compatibility. | Repo-local skill trigger policy; no new external Frappe claim. | Verified by validator; compatibility remains `Frappe v15-v16, ERPNext v15-v16`. |
+| `skills/source/core/frappe-core-database/SKILL.md` | Reference file list was condensed to meet line limit. | Repo-local formatting only; no external Frappe claim changed. | Verified line count is <= 500. |
+
+## Docker/deployment skill review
+
+No Docker/deployment skill body claims were changed in this implementation. The `frappe_docker` URLs were added to `SOURCES.md` for future source-backed edits and to satisfy the requested verification scope.
+
+## Validator limitations
+
+- Checks frontmatter shape, required metadata, line count, skill names, references directory shape.
+- Allows legitimate compatibility ranges such as `Frappe v14-v16` and `Frappe v15-v16`.
+- Does not crawl documentation.
+- Does not prove every code example is correct.
+- Does not enforce language purity with a brittle word blocklist.
+
+## Commands run
+
+```bash
+# URL liveness checks
+curl -L -s -o /dev/null -w '%{http_code}\n'
+
+# Repo validation
+python3 tools/quick_validate.py skills/source --all
+
+# Single-skill validation
+python3 tools/quick_validate.py skills/source/core/frappe-core-database
+```
diff --git a/package.json b/package.json
index db71ece..b4e549a 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
{
- "name": "@openaec/frappe-claude-skill-package",
+ "name": "@impertio-studio/frappe-claude-skill-package",
"version": "3.2.0",
"description": "61 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations",
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package"
+ "url": "https://github.com/Impertio-Studio/Frappe_Claude_Skill_Package"
},
- "keywords": ["agent-skills", "agentskills", "claude-skills", "frappe", "erpnext", "python", "javascript"],
+ "keywords": ["agent-skills", "agentskills", "claude-skills", "frappe", "erpnext", "python", "javascript", "opencode", "codex", "openai-codex", "skills.sh", "agent-harness", "multi-agent"],
"agents": {
"skills": [
{ "name": "frappe-agent-architect", "path": "./skills/source/agents/frappe-agent-architect" },
diff --git a/skills/source/core/frappe-core-database/SKILL.md b/skills/source/core/frappe-core-database/SKILL.md
index 93bbdda..ef6f8d5 100644
--- a/skills/source/core/frappe-core-database/SKILL.md
+++ b/skills/source/core/frappe-core-database/SKILL.md
@@ -493,9 +493,7 @@ For complex queries (joins, aggregations, subqueries, cross-DB compatibility), s
- **frappe.db.sql** โ Very complex SQL not expressible in qb (ALWAYS parameterized)
## Reference Files
-
-- **[methods-reference.md](references/methods-reference.md)** โ Complete API signatures for all database and document methods
+- **[methods-reference.md](references/methods-reference.md)** โ Complete API signatures for database and document methods
- **[query-patterns.md](references/query-patterns.md)** โ Query Builder patterns, subqueries, ImportMapper, custom functions
- **[caching-patterns.md](references/caching-patterns.md)** โ Redis cache, @redis_cache, hash operations, invalidation
-- **[examples.md](references/examples.md)** โ Real-world patterns: CRUD, reports, batch processing, transactions
-- **[anti-patterns.md](references/anti-patterns.md)** โ SQL injection, N+1, commit mistakes, and 10 more anti-patterns
+- **[examples.md](references/examples.md)** / **[anti-patterns.md](references/anti-patterns.md)** โ CRUD, reports, batch processing, SQL injection, N+1, commit mistakes
diff --git a/skills/source/ops/frappe-ops-website-deploy/SKILL.md b/skills/source/ops/frappe-ops-website-deploy/SKILL.md
index c40a5b7..79eaac8 100644
--- a/skills/source/ops/frappe-ops-website-deploy/SKILL.md
+++ b/skills/source/ops/frappe-ops-website-deploy/SKILL.md
@@ -1,13 +1,11 @@
---
name: frappe-ops-website-deploy
description: >
- Deploy HTML/CSS websites to ERPNext/Frappe (v15/v16) as Web Pages via the REST API.
- Use this skill whenever a user wants to host a website on ERPNext, deploy HTML mockups
- to Frappe, create Web Pages programmatically, configure Website Settings, or integrate
- Frappe's Discussion system as a forum. Also use when the user mentions "website on ERPNext",
- "Web Page API", "Page Builder", "Web Template", or wants to serve custom HTML from Frappe.
- Covers: Web Pages with Page Builder, custom Web Templates, Website Settings (navbar, footer),
- CSS management, Frappe Discussion integration, and deployment scripting.
+ Use when deploying HTML/CSS websites to ERPNext/Frappe (v15/v16) as Web Pages via the REST API.
+ Prevents broken Web Page rendering, wrong Page Builder usage, and missing Website Settings.
+ Covers Web Pages with Page Builder, custom Web Templates, Website Settings, CSS management,
+ Frappe Discussion integration, and deployment scripting. Keywords: website on ERPNext,
+ Web Page API, Page Builder, Web Template, custom HTML, Frappe website deploy.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v15-v16, ERPNext v15-v16."
metadata:
diff --git a/tools/quick_validate.py b/tools/quick_validate.py
index 78c236c..09e5d16 100644
--- a/tools/quick_validate.py
+++ b/tools/quick_validate.py
@@ -1,118 +1,297 @@
#!/usr/bin/env python3
+"""Quick validation script for Agent Skill folders.
+
+Stdlib-only on purpose: contributors should be able to run this on a clean
+checkout with `python3 tools/quick_validate.py skills/source --all`.
"""
-Quick validation script for Anthropic skill format.
-Based on documented requirements from LESSONS.md ยง6.3
-"""
+from __future__ import annotations
-import os
-import sys
+import json
import re
-import yaml
-
-def validate_skill(skill_path):
- """Validate a skill folder against Anthropic requirements."""
- errors = []
- warnings = []
-
- skill_path = os.path.abspath(skill_path)
- skill_name = os.path.basename(skill_path)
-
- # Check 1: SKILL.md exists in root
- skill_md_path = os.path.join(skill_path, "SKILL.md")
- if not os.path.exists(skill_md_path):
+import sys
+from pathlib import Path
+
+MAX_LINES = 500
+REQUIRED_KEYWORDS = {"agent-skills", "opencode", "codex", "skills.sh"}
+DUPLICATE_SKILL_GLOBS = [
+ ".agents/skills/*/SKILL.md",
+ ".opencode/skills/*/SKILL.md",
+ ".claude/skills/*/SKILL.md",
+]
+
+
+def parse_frontmatter(content: str) -> tuple[dict, str | None]:
+ """Parse the small YAML subset used by this repo's SKILL.md files."""
+ lines = content.splitlines()
+ if not lines or lines[0].strip() != "---":
+ return {}, "SKILL.md must start with YAML frontmatter (---)"
+
+ end = None
+ for i, line in enumerate(lines[1:], start=1):
+ if line.strip() == "---":
+ end = i
+ break
+ if end is None:
+ return {}, "Invalid YAML frontmatter format: missing closing ---"
+
+ data: dict[str, object] = {}
+ i = 1
+ while i < end:
+ line = lines[i]
+ if not line.strip():
+ i += 1
+ continue
+ if line.startswith(" "):
+ return data, f"Unexpected indented frontmatter line: {line.strip()}"
+ if ":" not in line:
+ return data, f"Invalid frontmatter line: {line}"
+
+ key, value = line.split(":", 1)
+ key = key.strip()
+ value = value.strip()
+
+ if value == ">":
+ folded: list[str] = []
+ i += 1
+ while i < end and (lines[i].startswith(" ") or not lines[i].strip()):
+ folded.append(lines[i].strip())
+ i += 1
+ data[key] = " ".join(x for x in folded if x).strip()
+ continue
+
+ if value == "":
+ nested: dict[str, str] = {}
+ i += 1
+ while i < end and (lines[i].startswith(" ") or not lines[i].strip()):
+ child = lines[i].strip()
+ if child and ":" in child:
+ ck, cv = child.split(":", 1)
+ nested[ck.strip()] = cv.strip().strip('"\'')
+ i += 1
+ data[key] = nested
+ continue
+
+ data[key] = value.strip('"\'')
+ i += 1
+
+ return data, None
+
+
+def skill_frontmatter_name(skill_dir: Path) -> str:
+ frontmatter, _ = parse_frontmatter((skill_dir / "SKILL.md").read_text(encoding="utf-8"))
+ return str(frontmatter.get("name", ""))
+
+
+def validate_skill(skill_path: str | Path):
+ """Validate a skill folder against repo requirements."""
+ errors: list[str] = []
+ warnings: list[str] = []
+
+ skill_path = Path(skill_path).resolve()
+ skill_name = skill_path.name
+
+ skill_md_path = skill_path / "SKILL.md"
+ if not skill_md_path.exists():
errors.append("SKILL.md not found in skill root folder")
return errors, warnings
-
- # Read SKILL.md
- with open(skill_md_path, 'r', encoding='utf-8') as f:
- content = f.read()
-
- # Check 2: Line count < 500
- lines = content.split('\n')
- if len(lines) > 500:
- errors.append(f"SKILL.md has {len(lines)} lines (max 500)")
-
- # Check 3: YAML frontmatter exists and is valid
- if not content.startswith('---'):
- errors.append("SKILL.md must start with YAML frontmatter (---)")
- return errors, warnings
-
- # Extract frontmatter
- parts = content.split('---', 2)
- if len(parts) < 3:
- errors.append("Invalid YAML frontmatter format")
- return errors, warnings
-
- try:
- frontmatter = yaml.safe_load(parts[1])
- except yaml.YAMLError as e:
- errors.append(f"Invalid YAML in frontmatter: {e}")
+
+ content = skill_md_path.read_text(encoding="utf-8")
+
+ line_count = len(content.splitlines())
+ if line_count > MAX_LINES:
+ errors.append(f"SKILL.md has {line_count} lines (max {MAX_LINES})")
+
+ frontmatter, parse_error = parse_frontmatter(content)
+ if parse_error:
+ errors.append(parse_error)
return errors, warnings
-
- # Check 4: name field
- if 'name' not in frontmatter:
+
+ name = str(frontmatter.get("name", ""))
+ if not name:
errors.append("Missing required 'name' field in frontmatter")
else:
- name = frontmatter['name']
if len(name) > 64:
errors.append(f"name '{name}' exceeds 64 characters ({len(name)})")
- if not re.match(r'^[a-z0-9-]+$', name):
+ if not re.match(r"^[a-z0-9-]+$", name):
errors.append(f"name '{name}' must be kebab-case (a-z, 0-9, - only)")
-
- # Check 5: description field
- if 'description' not in frontmatter:
+ if name != skill_name:
+ errors.append(f"name '{name}' must match folder name '{skill_name}'")
+
+ desc = str(frontmatter.get("description", ""))
+ if not desc:
errors.append("Missing required 'description' field in frontmatter")
else:
- desc = str(frontmatter['description'])
if len(desc) > 1024:
errors.append(f"description exceeds 1024 characters ({len(desc)})")
- if '<' in desc or '>' in desc:
- warnings.append("description contains < or > which may cause issues")
-
- # Check 6: No forbidden files
- forbidden_files = ['README.md', 'CHANGELOG.md']
- for ff in forbidden_files:
- if os.path.exists(os.path.join(skill_path, ff)):
- warnings.append(f"Found {ff} in skill folder (not recommended)")
-
- # Check 7: references/ folder structure
- refs_path = os.path.join(skill_path, "references")
- if os.path.exists(refs_path):
- if not os.path.isdir(refs_path):
- errors.append("'references' should be a directory, not a file")
-
+ if not desc.startswith("Use when"):
+ errors.append("description must start with 'Use when'")
+
+ license_value = str(frontmatter.get("license", ""))
+ if license_value != "MIT":
+ errors.append("Missing required 'license: MIT' field in frontmatter")
+
+ compatibility = str(frontmatter.get("compatibility", ""))
+ if not compatibility:
+ errors.append("Missing required 'compatibility' field in frontmatter")
+ elif not re.search(r"Frappe v\d+(?:-v\d+)?", compatibility):
+ errors.append("compatibility must include a Frappe version range like 'Frappe v14-v16' or 'Frappe v15-v16'")
+
+ metadata = frontmatter.get("metadata")
+ if not isinstance(metadata, dict):
+ errors.append("Missing required 'metadata' block in frontmatter")
+ else:
+ if not metadata.get("author"):
+ errors.append("Missing required 'metadata.author' field in frontmatter")
+ if not metadata.get("version"):
+ errors.append("Missing required 'metadata.version' field in frontmatter")
+
+ for forbidden in ["README.md", "CHANGELOG.md"]:
+ if (skill_path / forbidden).exists():
+ warnings.append(f"Found {forbidden} in skill folder (not recommended)")
+
+ refs_path = skill_path / "references"
+ if refs_path.exists() and not refs_path.is_dir():
+ errors.append("'references' should be a directory, not a file")
+
return errors, warnings
-def main():
- if len(sys.argv) < 2:
- print("Usage: python quick_validate.py ")
- sys.exit(1)
-
- skill_path = sys.argv[1]
-
- if not os.path.isdir(skill_path):
- print(f"Error: {skill_path} is not a directory")
- sys.exit(1)
-
- print(f"Validating: {skill_path}")
- print("-" * 50)
-
- errors, warnings = validate_skill(skill_path)
-
+
+def discover_skills(root: str | Path) -> list[Path]:
+ return sorted(p.parent for p in Path(root).glob("*/*/SKILL.md"))
+
+
+def find_repo_root(path: Path) -> Path:
+ path = path.resolve()
+ if path.name == "source" and path.parent.name == "skills":
+ return path.parent.parent
+ for candidate in [path, *path.parents]:
+ if (candidate / "package.json").exists() or (candidate / ".git").exists():
+ return candidate
+ return path
+
+
+def validate_package_manifest(repo_root: Path, skills_root: Path, skill_dirs: list[Path]) -> list[str]:
+ package_json = repo_root / "package.json"
+ if not package_json.exists():
+ return []
+
+ errors: list[str] = []
+ try:
+ package = json.loads(package_json.read_text(encoding="utf-8"))
+ except json.JSONDecodeError as exc:
+ return [f"package.json is invalid JSON: {exc}"]
+
+ manifest = package.get("agents", {}).get("skills")
+ if not isinstance(manifest, list):
+ errors.append("package.json agents.skills must be a list")
+ return errors
+
+ discovered = {p.resolve().relative_to(repo_root).as_posix(): p for p in skill_dirs}
+ manifest_paths: dict[str, str] = {}
+ for i, item in enumerate(manifest, start=1):
+ if not isinstance(item, dict) or not item.get("name") or not item.get("path"):
+ errors.append(f"package.json agents.skills[{i}] must have name and path")
+ continue
+ name = str(item["name"])
+ rel_path = str(item["path"]).removeprefix("./")
+ manifest_paths[rel_path] = name
+ skill_dir = repo_root / rel_path
+ if not (skill_dir / "SKILL.md").exists():
+ errors.append(f"package.json skill path missing SKILL.md: {rel_path}")
+ continue
+ if skill_dir.name != name:
+ errors.append(f"package.json skill name '{name}' must match folder '{skill_dir.name}'")
+ fm_name = skill_frontmatter_name(skill_dir)
+ if fm_name and fm_name != name:
+ errors.append(f"package.json skill name '{name}' must match SKILL.md name '{fm_name}'")
+
+ missing = sorted(set(discovered) - set(manifest_paths))
+ extra = sorted(set(manifest_paths) - set(discovered))
+ if missing:
+ errors.append("package.json missing skills: " + ", ".join(missing[:5]) + ("..." if len(missing) > 5 else ""))
+ if extra:
+ errors.append("package.json has non-discovered skills: " + ", ".join(extra[:5]) + ("..." if len(extra) > 5 else ""))
+ if len(manifest) != len(skill_dirs):
+ errors.append(f"package.json agents.skills has {len(manifest)} entries; discovered {len(skill_dirs)} skills")
+
+ description = str(package.get("description", ""))
+ if re.search(r"\d+\s+.*skills", description, re.I) and str(len(skill_dirs)) not in description:
+ errors.append(f"package.json description must include discovered skill count {len(skill_dirs)}")
+
+ keywords = set(package.get("keywords", []))
+ missing_keywords = sorted(REQUIRED_KEYWORDS - keywords)
+ if missing_keywords:
+ errors.append("package.json missing keywords: " + ", ".join(missing_keywords))
+
+ for pattern in DUPLICATE_SKILL_GLOBS:
+ for duplicate in repo_root.glob(pattern):
+ errors.append(f"duplicate harness skill tree found: {duplicate.relative_to(repo_root)}")
+
+ return errors
+
+
+def print_result(path: Path, errors: list[str], warnings: list[str]) -> None:
+ print(f"Validating: {path}")
if warnings:
- print("\nโ ๏ธ WARNINGS:")
- for w in warnings:
- print(f" - {w}")
-
+ print(" โ ๏ธ WARNINGS:")
+ for warning in warnings:
+ print(f" - {warning}")
if errors:
- print("\nโ ERRORS:")
- for e in errors:
- print(f" - {e}")
- print(f"\nValidation FAILED with {len(errors)} error(s)")
- sys.exit(1)
+ print(" โ ERRORS:")
+ for error in errors:
+ print(f" - {error}")
else:
- print("\nโ
Skill is valid!")
- sys.exit(0)
+ print(" โ
Skill is valid")
+
+
+def main() -> int:
+ if len(sys.argv) < 2 or len(sys.argv) > 3 or (len(sys.argv) == 3 and sys.argv[2] != "--all"):
+ print("Usage: python3 tools/quick_validate.py ")
+ print(" python3 tools/quick_validate.py --all")
+ return 1
+
+ path = Path(sys.argv[1])
+ if not path.is_dir():
+ print(f"Error: {path} is not a directory")
+ return 1
+
+ all_mode = len(sys.argv) == 3
+ if all_mode:
+ skill_dirs = discover_skills(path)
+ if not skill_dirs:
+ print(f"Error: no skills found under {path} (expected */*/SKILL.md)")
+ return 1
+ else:
+ skill_dirs = [path]
+
+ total_errors = 0
+ total_warnings = 0
+ passed = 0
+ for skill_dir in skill_dirs:
+ errors, warnings = validate_skill(skill_dir)
+ total_errors += len(errors)
+ total_warnings += len(warnings)
+ if not errors:
+ passed += 1
+ print_result(skill_dir, errors, warnings)
+
+ if all_mode:
+ repo_root = find_repo_root(path)
+ manifest_errors = validate_package_manifest(repo_root, path.resolve(), skill_dirs)
+ if manifest_errors:
+ total_errors += len(manifest_errors)
+ print("Validating: package.json / harness layout")
+ print(" โ ERRORS:")
+ for error in manifest_errors:
+ print(f" - {error}")
+ else:
+ print("Validating: package.json / harness layout")
+ print(" โ
Manifest and harness layout are valid")
+
+ print("-" * 50)
+ print(f"Validated: {len(skill_dirs)} skill(s); passed: {passed}; failed: {len(skill_dirs) - passed}; warnings: {total_warnings}; errors: {total_errors}")
+ return 0 if total_errors == 0 else 1
+
if __name__ == "__main__":
- main()
+ sys.exit(main())