From d1eacc5479070490b489d087b7d6734d974fb8a6 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Thu, 28 May 2026 09:40:25 -0400 Subject: [PATCH 1/4] Auto-merge docs PRs and remove manual doc generation requirement Contributors no longer need to run `make update` to regenerate docs. CI handles doc generation on merge to main and auto-merges the resulting PR when only docs/index.html is changed. A new `make docs` target is available for local preview. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/update-docs.yml | 13 +++++ .skillsaw/plugindocs_rule.py | 94 ------------------------------- AGENTS.md | 2 +- CONTRIBUTING.md | 7 +-- Makefile | 6 +- 5 files changed, 21 insertions(+), 101 deletions(-) delete mode 100644 .skillsaw/plugindocs_rule.py diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index 74f8dcf87..f8bc7473b 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -6,6 +6,7 @@ on: - main paths: - 'plugins/**' + - '.claude-plugin/marketplace.json' jobs: update-docs: @@ -34,6 +35,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 +52,14 @@ 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 }} + run: | + PR_NUMBER="${{ steps.create-pr.outputs.pull-request-number }}" + 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/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..55e923b63 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 lint` to validate ### OWNERS File @@ -49,8 +49,7 @@ If your PR modifies plugin code (commands, skills, hooks, or plugin.json), you * 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 -7. Submit a PR +6. Submit a PR ### Testing locally @@ -66,7 +65,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 From 816280a0a1e25f31529be984ba576b9951505ef2 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Thu, 28 May 2026 09:41:59 -0400 Subject: [PATCH 2/4] Add concurrency group to prevent docs race condition When multiple plugin PRs merge to main in quick succession, the older workflow run could overwrite newer docs. Cancel in-progress runs so only the latest commit's docs are generated. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/update-docs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index f8bc7473b..b7ca979a2 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -8,6 +8,10 @@ on: - 'plugins/**' - '.claude-plugin/marketplace.json' +concurrency: + group: update-docs + cancel-in-progress: true + jobs: update-docs: runs-on: ubuntu-latest From 5d5b07ff1a0a7a942dab2be1e28df17c1db2cb3b Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Thu, 28 May 2026 09:43:21 -0400 Subject: [PATCH 3/4] Remove plugindocs_rule from skillsaw config The rule file was deleted but its registration in .skillsaw.yaml remained, causing the linter to crash with FileNotFoundError. Co-Authored-By: Claude Opus 4.6 (1M context) --- .skillsaw.yaml | 1 - 1 file changed, 1 deletion(-) 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" From f959c4a061f8da2779b75db71654bbcc02f217c5 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Thu, 28 May 2026 09:55:03 -0400 Subject: [PATCH 4/4] Address CodeRabbit review feedback - Move PR_NUMBER to env var with numeric guard instead of inline expression interpolation - Re-add make update step to contributor docs (still needed for marketplace version sync, just not for docs generation) Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/update-docs.yml | 3 ++- CONTRIBUTING.md | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index b7ca979a2..227be567c 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -61,8 +61,9 @@ jobs: 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="${{ steps.create-pr.outputs.pull-request-number }}" + [[ "$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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55e923b63..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 +6. Run `make update` to sync marketplace versions, then `make lint` to validate ### OWNERS File @@ -48,8 +48,9 @@ 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. Submit a PR +5. Run `make update` to sync marketplace versions +6. Run `make lint` to validate plugin structure +7. Submit a PR ### Testing locally