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())