diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index 74f8dcf87..227be567c 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -6,6 +6,11 @@ on: - main paths: - 'plugins/**' + - '.claude-plugin/marketplace.json' + +concurrency: + group: update-docs + cancel-in-progress: true jobs: update-docs: @@ -34,6 +39,7 @@ jobs: skillsaw docs -o docs/ --theme crimson-red - name: Create Pull Request + id: create-pr uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -50,3 +56,15 @@ jobs: add-paths: | docs/ .claude-plugin/marketplace.json + + - name: Enable auto-merge if only docs changed + if: steps.create-pr.outputs.pull-request-number + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }} + run: | + [[ "$PR_NUMBER" =~ ^[0-9]+$ ]] || { echo "Invalid PR number"; exit 1; } + FILES_CHANGED=$(gh pr diff "$PR_NUMBER" --name-only) + if [ "$FILES_CHANGED" = "docs/index.html" ]; then + gh pr merge "$PR_NUMBER" --auto --squash + fi diff --git a/.skillsaw.yaml b/.skillsaw.yaml index 19f39fee2..096d346e7 100644 --- a/.skillsaw.yaml +++ b/.skillsaw.yaml @@ -91,7 +91,6 @@ rules: # Load custom rules from these files custom-rules: - - ".skillsaw/plugindocs_rule.py" - ".skillsaw/owners_rule.py" - ".skillsaw/promptfoo_budget_rule.py" diff --git a/.skillsaw/plugindocs_rule.py b/.skillsaw/plugindocs_rule.py deleted file mode 100644 index 0a0e73fef..000000000 --- a/.skillsaw/plugindocs_rule.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Custom skillsaw rules for ai-helpers marketplace -""" - -import subprocess -from pathlib import Path -from typing import List - -try: - from src.rule import Rule, RuleViolation, Severity - from src.context import RepositoryContext -except ImportError: - from skillsaw import Rule, RuleViolation, Severity, RepositoryContext - - -class PluginsDocUpToDateRule(Rule): - """Check that docs/ is up-to-date by running 'skillsaw docs'""" - - @property - def rule_id(self) -> str: - return "plugins-doc-up-to-date" - - @property - def description(self) -> str: - return "docs/ must be up-to-date with plugin metadata. Run 'make update' to regenerate." - - def default_severity(self) -> Severity: - return Severity.ERROR - - def check(self, context: RepositoryContext) -> List[RuleViolation]: - violations = [] - - if not context.has_marketplace(): - return violations - - docs_path = context.root_path / "docs" - index_path = docs_path / "index.html" - - if not index_path.exists(): - violations.append( - self.violation( - "docs/index.html is missing. Run 'make update' to generate.", - file_path=docs_path, - ) - ) - return violations - - original_content = index_path.read_text() - try: - result = subprocess.run( - ["skillsaw", "docs", "-o", "docs/", "--theme", "crimson-red"], - cwd=str(context.root_path), - capture_output=True, - text=True, - timeout=30 - ) - - if result.returncode != 0: - violations.append( - self.violation( - f"'make update' failed: {result.stderr}", - file_path=index_path - ) - ) - return violations - - generated_content = index_path.read_text() - if original_content != generated_content: - violations.append( - self.violation( - "docs/ is out of sync with plugin metadata. Run 'make update' to update.", - file_path=index_path - ) - ) - - except subprocess.TimeoutExpired: - violations.append( - self.violation( - "'make update' timed out", - file_path=index_path - ) - ) - except Exception as e: - violations.append( - self.violation( - f"Error checking docs up-to-date status: {e}", - file_path=index_path - ) - ) - finally: - if index_path.exists() and index_path.read_text() != original_content: - index_path.write_text(original_content) - - return violations diff --git a/AGENTS.md b/AGENTS.md index bf84e37ba..a04a9834f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,7 @@ Canonical example: `plugins/hello-world/` |---------|------| | `make lint` | Before every commit — validates structure, format, and marketplace registration | | Bump `version` in `plugin.json` | When modifying plugin commands or skills (not README-only changes) | -| `make update` | After version bumps — syncs marketplace.json and regenerates docs | +| `make update` | After version bumps — syncs marketplace.json (docs are auto-generated by CI) | ## Contributing Rules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e132d512e..3e7fb46ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ Thank you for your interest in contributing to the ai-helpers plugin marketplace 3. Add at least one command in `commands/` 4. Add an `OWNERS` file listing approvers and reviewers for your plugin 5. Register your plugin in `.claude-plugin/marketplace.json` -6. Run `make lint` to validate, then `make update` to regenerate docs +6. Run `make update` to sync marketplace versions, then `make lint` to validate ### OWNERS File @@ -48,8 +48,8 @@ If your PR modifies plugin code (commands, skills, hooks, or plugin.json), you * 2. Create a feature branch 3. Make your changes 4. Bump the version in `plugin.json` if modifying plugin code -5. Run `make lint` to validate plugin structure -6. Run `make update` to regenerate docs +5. Run `make update` to sync marketplace versions +6. Run `make lint` to validate plugin structure 7. Submit a PR ### Testing locally @@ -66,7 +66,7 @@ You can test plugins locally before submitting: The following files are auto-generated and should **not** be edited manually: - `docs/index.html` -These are regenerated by running `make update` which uses `skillsaw docs`. +Documentation is automatically regenerated by CI when plugin changes are merged to `main`. ## Command Frontmatter diff --git a/Makefile b/Makefile index 207a2b583..71fb05fdc 100644 --- a/Makefile +++ b/Makefile @@ -28,12 +28,14 @@ lint-pull: ## Pull the latest skillsaw image $(CONTAINER_RUNTIME) pull $(SKILLSAW_IMAGE) .PHONY: update -update: ## Update plugin documentation and website data +update: ## Fix frontmatter and sync marketplace versions @echo "Fixing frontmatter quotes, if any..." @python3 scripts/fix_frontmatter_quotes.py @echo "Syncing marketplace versions..." @python3 scripts/sync_marketplace_versions.py - @echo "Generating docs..." + +.PHONY: docs +docs: ## Preview generated documentation locally $(CONTAINER_RUNTIME) run --rm --platform linux/amd64 $(SELINUX_OPT) -v $(PWD):/workspace:Z --entrypoint skillsaw $(SKILLSAW_IMAGE) docs -o docs/ --theme crimson-red EVAL_REPEAT ?= 1