Compare commits

...

6 Commits

Author SHA1 Message Date
tobin
fa6387a0e3 Add 24 first-party plugins from major-brand orgs
Promote first-party plugins from recognizable companies that publish
deep, actively-maintained Claude Code plugins from their official GitHub
orgs. All entries are SHA-pinned to current default-branch HEAD.

Development:
- apollo-skills (Apollo GraphQL): 14 GraphQL skills + Apollo MCP server
- appwrite (Appwrite): 11 SDK skills + 2 commands + dual MCP
- forge-skills (Atlassian): Forge scaffold/review/debug + 2 hosted MCPs
- buildkite (Buildkite): 6 CI/CD skills + hosted MCP
- circle-skills (Circle): 16 USDC/stablecoin dev skills + hosted MCP
- codspeed (CodSpeed): perf profiling skills + remote MCP
- dominodatalab (Domino Data Lab): 22 skills + 3 agents + bundled MCP
- lumen (Ory): local semantic code-search MCP + auto-index hooks
- mcp-apps (Model Context Protocol): MCP Apps SDK skills
- resend (Resend): email API/CLI/React Email skills + bundled MCP
- teamcity-cli (JetBrains): TeamCity CI/CD CLI agent skill
- togetherai-skills (Together AI): 12 inference/training/GPU skills

Database:
- clickhouse-best-practices (ClickHouse): 28 schema/query/ingestion rules
- datahub-skills (DataHub): 12 catalog/lineage/quality skills + 4 agents
- duckdb-skills (DuckDB): 9 file-query/docs/extension skills
- redis-development (Redis): data structures, query engine, vector search

Security:
- duende-skills (Duende): 22 OAuth/OIDC/IdentityServer skills + 2 agents
- workos (WorkOS): AuthKit/SSO/Directory Sync/RBAC router skill

Monitoring:
- rootly (Rootly): 18 incident-management skills + 3 agents + hosted MCP
- sentry-cli (Sentry): Sentry CLI agent skill

Design:
- hyperframes (HeyGen): 15 HTML-to-video framework skills
- runway-api (Runway): 17 video/image/audio generation skills

Productivity / Location:
- hunter (Hunter.io): 9 prospecting skills + remote MCP
- mapbox (Mapbox): 19 geospatial skills + 3 remote MCP servers

Source structure: 19 repo-root plugins (url source), 5 subdirectory
plugins (git-subdir source). All cross-referenced against existing
entries to avoid duplicates.

Two candidates excluded pending upstream fixes:
- launchdarkly: plugin.json has unrecognized 'logo' key (schema error)
- medusa-dev: skill has malformed YAML frontmatter
2026-05-19 00:30:05 +00:00
Tobin South
9f0275ae44 Add convex-backend plugin (#1918) 2026-05-18 16:56:50 -07:00
Tobin South
0b9a622ecb Fix broken plugin source configs and bump their SHAs (#1915)
* Fix broken plugin source configs and bump their SHAs

Several external plugins had source configs that no longer matched the
upstream layout, so the automated SHA bump skipped them indefinitely.
Add the missing path field where the manifest moved into a subdirectory,
correct stale ref/commit metadata, and update the skills list for the
one strict:false skills-only entry.

- rc, revenuecat: upstream moved the plugin from repo root into
  revenuecat/. Add path and bump SHA.
- zilliz: plugin moved from repo root into plugins/zilliz/. Add path
  and bump SHA.
- sumup: plugin lives at providers/claude/plugin/ (declared by the
  upstream marketplace.json) but our entry never had a path. Add it
  and bump SHA.
- mintlify: pure SHA bump. Repo layout unchanged between SHAs; the
  upstream remains a marketplace-style repo with no plugin.json, same
  as the currently pinned SHA.
- netsuite-suitecloud (strict:false skills entry): bump SHA and add
  the four new skill directories upstream added since the last pin.
- 42crunch-api-security-testing: ref said v1.0.1 but the pinned SHA
  is actually v1.5.5. Correct the label; the SHA is already current.
- jfrog: commit and sha fields had drifted apart. Set both to
  upstream HEAD.

Each new SHA verified to be on the upstream default branch and the
referenced manifest validated with claude plugin validate.

* Revert mintlify and netsuite-suitecloud changes

The validate-plugins check requires a plugin manifest at the pinned SHA
even for strict:false entries. Neither repo has one at any SHA, so a
SHA bump fails CI. Leave them at the existing pin until either the
upstream adds a manifest or the validator learns to honor strict:false.
2026-05-18 23:33:38 +01:00
Tobin South
b7c0654137 Raise bump cap with verdict cache and skip-and-revert (#1913)
* Cache scan verdicts and drop policy-failing entries from bump PRs

Three changes that together let the nightly bump clear any backlog in a
single run without blocking on a single bad upstream or re-burning Claude
time on already-scanned SHAs:

- bump-plugin-shas.yml: raise max-bumps default 20 -> 130 (above the
  external entry count, so a single run can clear a full backlog) and add
  an explicit 60-min job timeout. The cap was the only thing bounding the
  blast radius of a single policy failure; the changes below take over
  that role so the cap can be lifted.

- scan-plugins.yml: add a verdict cache keyed on (plugin, sha, policy
  hash). The bump action force-resets bump/plugin-shas every night, which
  makes the same SHAs reappear in the diff on consecutive nights — without
  the cache the scan would re-burn ~90s of Claude time per entry per
  night. Cached verdicts (pass and fail) are served from disk; only
  uncached SHAs are scanned. The job still fails on cached failures so
  the required check stays honest.

- revert-failed-bumps.yml (new): after a Scan Plugins workflow_run on
  bump/plugin-shas concludes with a failure, drop just the failing
  entries' source.sha back to main's pin via a follow-up signed commit
  and re-dispatch the scan. The re-dispatch finds only cached-pass
  entries and goes green in seconds. Bounded at 3 passes/night, restricted
  to SHA-only diffs, and aborts if the bump branch was tampered with.

* Harden bump cache and revert workflows after review

- revert-failed-bumps: replace the time-based revert budget (anchored on
  the PR head, which a revert commit immediately replaces — never
  accumulating past 1) with a commit count: every nightly bump force-
  resets to one commit and every revert pass adds exactly one, so
  commits > MAX+1 is the budget without date math, pagination, or
  exposure to comment spoofing.
- revert-failed-bumps: filter the bump PR by head owner so a fork PR
  with a branch named bump/plugin-shas can't be selected.
- revert-failed-bumps: continue-on-error on the artifact download so a
  scan that died before uploading (infra error) doesn't fail the revert
  job — the missing-file guard downstream handles it.
- scan-plugins: add a per-ref concurrency group so concurrent scans
  don't lose one another's cache writes; key the cache on run_attempt
  so a re-run can save its own verdicts.
- scan-plugins: store the full source object in the cache and require
  source equality on lookup, so a repo/path change at the same SHA
  misses the cache instead of getting a stale verdict.
- scan-plugins / revert-failed-bumps: strip markdown control chars,
  wrap model-generated text in code spans (neutralizes auto-linked
  URLs), and redact key-shaped tokens before they reach the step
  summary, artifact, cache, or PR comment.
2026-05-18 20:55:20 +01:00
Tobin South
af4e1ad69e Bump 21 plugin SHA pins to upstream HEAD (#1911) 2026-05-18 20:55:03 +01:00
Tobin South
de2bcc9411 Bump 27 plugin SHA pins to upstream HEAD (#1912) 2026-05-18 20:52:54 +01:00
4 changed files with 1029 additions and 62 deletions

View File

@@ -18,7 +18,7 @@
"source": "git-subdir",
"url": "https://github.com/42Crunch-AI/claude-plugins.git",
"path": "plugins/api-security-testing",
"ref": "v1.0.1",
"ref": "v1.5.5",
"sha": "faf5305385de8afed9468904e8639be737aff39e"
},
"homepage": "https://42crunch.com"
@@ -151,6 +151,34 @@
},
"homepage": "https://www.apollo.io/"
},
{
"name": "apollo-skills",
"description": "Apollo GraphQL agent skills for Claude Code — Apollo Client, Server, Federation, Connectors, Router, Rover CLI, iOS, Kotlin, and the Apollo MCP server. Covers schema design, query optimization, and GraphQL best practices.",
"author": {
"name": "Apollo GraphQL"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/apollographql/skills.git",
"sha": "64d4087d629e413da1bac780abeaefd653847440"
},
"homepage": "https://www.apollographql.com"
},
{
"name": "appwrite",
"description": "Appwrite tools for Claude Code, including SDK skills, Appwrite MCP servers, and deployment commands.",
"author": {
"name": "Appwrite"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/appwrite/claude-plugin.git",
"sha": "a42b16918159183a0d556e305fea4d240a9e3823"
},
"homepage": "https://appwrite.io"
},
{
"name": "asana",
"description": "Asana project management integration. Create and manage tasks, search projects, update assignments, track progress, and integrate your development workflow with Asana's work management platform.",
@@ -217,7 +245,7 @@
"url": "https://github.com/auth0/agent-skills.git",
"path": "plugins/auth0",
"ref": "main",
"sha": "1c32754fcb934109451435ecd4a6ea9b068f0937"
"sha": "3aa943b620a640be8a04d462e2abce11671653c3"
},
"homepage": "https://auth0.com/docs/quickstart/agent-skills"
},
@@ -233,7 +261,7 @@
"url": "https://github.com/aws/agent-toolkit-for-aws.git",
"path": "plugins/aws-agents",
"ref": "main",
"sha": "14780bf3440aa1532eadfbb2ff547f58969fcfb2"
"sha": "ba1cc8ca4f063d88ca40c6acf3f670e6321b7a7f"
},
"homepage": "https://github.com/aws/agent-toolkit-for-aws"
},
@@ -262,7 +290,7 @@
"url": "https://github.com/aws/agent-toolkit-for-aws.git",
"path": "plugins/aws-core",
"ref": "main",
"sha": "14780bf3440aa1532eadfbb2ff547f58969fcfb2"
"sha": "ba1cc8ca4f063d88ca40c6acf3f670e6321b7a7f"
},
"homepage": "https://github.com/aws/agent-toolkit-for-aws"
},
@@ -278,7 +306,7 @@
"url": "https://github.com/aws/agent-toolkit-for-aws.git",
"path": "plugins/aws-data-analytics",
"ref": "main",
"sha": "14780bf3440aa1532eadfbb2ff547f58969fcfb2"
"sha": "ba1cc8ca4f063d88ca40c6acf3f670e6321b7a7f"
},
"homepage": "https://github.com/aws/agent-toolkit-for-aws"
},
@@ -318,7 +346,7 @@
"source": {
"source": "url",
"url": "https://github.com/microsoft/azure-skills.git",
"sha": "2a5c5080b8c501d00408eb00f7ee4ed8effa7b2c"
"sha": "350e050ca30fe3464483f66193a8ff3a973b1d77"
},
"homepage": "https://github.com/microsoft/azure-skills"
},
@@ -388,6 +416,20 @@
},
"homepage": "https://docs.brightdata.com"
},
{
"name": "buildkite",
"description": "Official Buildkite skills for Claude Code, Cursor, and other AI coding agents — pipelines, migration, preflight, agent runtime, CLI, and API",
"author": {
"name": "Buildkite"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/buildkite/skills.git",
"sha": "252476999d4813b8f7412215e47328e1b60ae51c"
},
"homepage": "https://buildkite.com"
},
{
"name": "carta-cap-table",
"description": "Carta Cap Table plugin — skills and hooks for querying cap tables, grants, SAFEs, 409A valuations, waterfall scenarios, and more",
@@ -400,7 +442,7 @@
"url": "https://github.com/carta/plugins.git",
"path": "plugins/carta-cap-table",
"ref": "main",
"sha": "980fd3966ec79b61ff94f39db4592f7df9d6ed80"
"sha": "49db52aa7d59fd4a855c6b17a1cf31c245f41e2c"
},
"homepage": "https://carta.com"
},
@@ -416,7 +458,7 @@
"source": {
"source": "url",
"url": "https://github.com/cap-js/mcp-server.git",
"sha": "4d59d7070a52761a9b8028cbe710c8d7477cbc92"
"sha": "ef840d4315fa34264be6b71d0077a3b5288cb5fa"
},
"homepage": "https://cap.cloud.sap/"
},
@@ -427,10 +469,26 @@
"source": {
"source": "url",
"url": "https://github.com/ChromeDevTools/chrome-devtools-mcp.git",
"sha": "a1612be8e01401cf1711c64bc2ef5da5763ba956"
"sha": "32dc50d59bdb87242c67391ddc755368ebe77104"
},
"homepage": "https://github.com/ChromeDevTools/chrome-devtools-mcp"
},
{
"name": "circle-skills",
"description": "Ship stablecoin apps faster. Best-practice skills for USDC payments, cross-chain transfers, wallets, and smart contracts — plus Circle's MCP server for real-time SDK and documentation guidance.",
"author": {
"name": "Circle"
},
"category": "development",
"source": {
"source": "git-subdir",
"url": "https://github.com/circlefin/skills.git",
"path": "plugins/circle",
"ref": "master",
"sha": "d076fe3ff992a1a9c30e2770031970b31429e905"
},
"homepage": "https://www.circle.com"
},
{
"name": "circleback",
"description": "Circleback conversational context integration. Search and access meetings, emails, calendar events, and more.",
@@ -505,10 +563,24 @@
"source": {
"source": "url",
"url": "https://github.com/ClickHouse/clickhouse-claude-code-plugin.git",
"sha": "db1c108dde6e5c81a1ca65f3b6700d6fff288545"
"sha": "13a2df004af0df46661c9de2d4ef4e85eba2f040"
},
"homepage": "https://github.com/ClickHouse/clickhouse-claude-code-plugin"
},
{
"name": "clickhouse-best-practices",
"description": "28 best practice rules for ClickHouse schema design, query optimization, and data ingestion — prioritized by impact",
"author": {
"name": "ClickHouse Inc"
},
"category": "database",
"source": {
"source": "url",
"url": "https://github.com/ClickHouse/agent-skills.git",
"sha": "2d30c3619eb44a2216ef19c531efb28d5bc916a9"
},
"homepage": "https://clickhouse.com"
},
{
"name": "cloud-sql-postgresql",
"description": "Create, connect, and interact with a Cloud SQL for PostgreSQL database and data.",
@@ -519,7 +591,7 @@
"source": {
"source": "url",
"url": "https://github.com/gemini-cli-extensions/cloud-sql-postgresql.git",
"sha": "69c0c820513d7f75a63eeb3ec84b01478037caeb"
"sha": "966f7b883998692d05389e4c3d3793412ca0659f"
},
"homepage": "https://cloud.google.com/sql"
},
@@ -528,7 +600,7 @@
"source": {
"source": "url",
"url": "https://github.com/cloudflare/skills.git",
"sha": "0397d7d88fa6ac7517a88389622eb0799e86ded2"
"sha": "60147cbb773649eadca89cee92b4e0caf02234b4"
},
"description": "Skills for the Cloudflare developer platform: Workers, Durable Objects, Agents SDK, MCP servers, Wrangler CLI, and web performance.",
"category": "deployment",
@@ -554,7 +626,7 @@
"source": {
"source": "url",
"url": "https://github.com/cockroachdb/claude-plugin.git",
"sha": "31d0cc99fac1c97614cc787a96720104ea642375"
"sha": "736bd11df55bac97e2a6c98be8e93503b125902c"
},
"homepage": "https://github.com/cockroachdb/claude-plugin"
},
@@ -602,6 +674,20 @@
},
"homepage": "https://github.com/coderabbitai/skills"
},
{
"name": "codspeed",
"description": "CodSpeed is the all-in-one performance testing toolkit. Dive into benchmarking results, flamegraphs, and performance comparisons — give Claude granular profiling context to pinpoint bottlenecks and autonomously iterate on performance via the CodSpeed MCP server.",
"author": {
"name": "CodSpeed"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/CodSpeedHQ/codspeed.git",
"sha": "149a618d9f18bc3afb3d2af51b7b835765d60633"
},
"homepage": "https://codspeed.io"
},
{
"name": "commit-commands",
"description": "Commands for git commit workflows including commit, push, and PR creation",
@@ -623,6 +709,20 @@
"community-managed"
]
},
{
"name": "convex-backend",
"description": "Convex backend skill for building reactive, type-safe, production-grade backends. Helps Claude design schemas, server functions, auth, file storage, scheduled jobs, and real-time multiplayer features on Convex.",
"author": {
"name": "Convex"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/get-convex/convex-backend-skill.git",
"sha": "9acbc5495dd26749a5e6341dc2438146c4caa03b"
},
"homepage": "https://convex.dev"
},
{
"name": "crowdstrike-falcon-foundry",
"description": "CrowdStrike Falcon Foundry development skills for building cybersecurity applications on the Falcon platform. Includes UI development, collections, functions, workflows, API integration, security patterns, and debugging workflows.",
@@ -679,7 +779,7 @@
"source": {
"source": "url",
"url": "https://github.com/dash0hq/dash0-agent-plugin.git",
"sha": "38c6d74e637bd7dbe1fa2c364de66d07efe88a9a"
"sha": "feae46e4099d31a1a76debe39f22aebb72a18ce5"
},
"homepage": "https://dash0.com/"
},
@@ -690,7 +790,7 @@
"source": {
"source": "url",
"url": "https://github.com/astronomer/agents.git",
"sha": "5935c4330dea4dfb8e93568956b10a543ecdb3d1"
"sha": "535a040ca9e27aaed6da13f0f959625fb3294820"
},
"homepage": "https://github.com/astronomer/agents"
},
@@ -704,7 +804,7 @@
"source": {
"source": "url",
"url": "https://github.com/gemini-cli-extensions/data-agent-kit-starter-pack.git",
"sha": "04c4354242c1192191c76fca2d4b03d94401d9fa"
"sha": "7bc75b5e53d6eaae103132fd1a47de26239e4ae4"
},
"homepage": "https://github.com/gemini-cli-extensions/data-agent-kit-starter-pack"
},
@@ -714,7 +814,7 @@
"source": {
"source": "url",
"url": "https://github.com/astronomer/agents.git",
"sha": "5935c4330dea4dfb8e93568956b10a543ecdb3d1"
"sha": "535a040ca9e27aaed6da13f0f959625fb3294820"
},
"homepage": "https://github.com/astronomer/agents"
},
@@ -727,7 +827,7 @@
"url": "https://github.com/awslabs/agent-plugins.git",
"path": "plugins/databases-on-aws",
"ref": "main",
"sha": "6cfb70e55aa142a8eda66e6ef7966d5921bdf9a2"
"sha": "95381e8bcb92f58a28edb4f83eb7e163c7461a0a"
},
"homepage": "https://github.com/awslabs/agent-plugins"
},
@@ -741,10 +841,24 @@
"source": {
"source": "url",
"url": "https://github.com/datadog-labs/claude-code-plugin.git",
"sha": "95d38f561e3d5e4fe9fb66c3c0bb19fb75e0458a"
"sha": "eeb2f746a857f8d97f69cd0968fb63874541c112"
},
"homepage": "https://www.datadoghq.com/"
},
{
"name": "datahub-skills",
"description": "DataHub development and interaction toolkit with connector planning, PR review, catalog search, metadata enrichment, lineage tracing, data quality management, and connection setup skills",
"author": {
"name": "DataHub"
},
"category": "database",
"source": {
"source": "url",
"url": "https://github.com/datahub-project/datahub-skills.git",
"sha": "f7c7c53648b71dc0841742781e108051d46fa360"
},
"homepage": "https://datahub.com"
},
{
"name": "datarobot-agent-skills",
"description": "DataRobot skills for AI/ML workflows — model training, deployment, predictions, feature engineering, monitoring, explainability, data preparation, App Framework CI/CD, and external agent monitoring.",
@@ -755,7 +869,7 @@
"source": {
"source": "url",
"url": "https://github.com/datarobot-oss/datarobot-agent-skills.git",
"sha": "b3e8fd33d7c36592c802359026c15f3e067a0646"
"sha": "6a13377ad0b3317c7c4133fce36b7fcc626334cd"
},
"homepage": "https://datarobot.com"
},
@@ -768,7 +882,7 @@
"url": "https://github.com/microsoft/Dataverse-skills.git",
"path": ".github/plugins/dataverse",
"ref": "main",
"sha": "b2f21c1eec233d1b20e89618c3ffcb25cfdd55e4"
"sha": "5f186bf8ab1a3d6e242492d982276bbd7443ee0f"
},
"homepage": "https://github.com/microsoft/Dataverse-skills"
},
@@ -781,7 +895,7 @@
"url": "https://github.com/awslabs/agent-plugins.git",
"path": "plugins/deploy-on-aws",
"ref": "main",
"sha": "6cfb70e55aa142a8eda66e6ef7966d5921bdf9a2"
"sha": "95381e8bcb92f58a28edb4f83eb7e163c7461a0a"
},
"homepage": "https://github.com/awslabs/agent-plugins"
},
@@ -797,7 +911,7 @@
"url": "https://github.com/wonderwhy-er/DesktopCommanderMCP.git",
"path": "plugins/claude",
"ref": "main",
"sha": "8c03d3392d1633923057f4492f2b5014e2c4a6bf"
"sha": "9c44119a480ec6460f82d59aeb90cf274bc3dd7b"
},
"homepage": "https://desktopcommander.app"
},
@@ -807,6 +921,48 @@
"category": "productivity",
"source": "./external_plugins/discord"
},
{
"name": "dominodatalab",
"description": "Full Domino Data Lab platform support — workspaces, jobs, model deployment, experiment tracking, GenAI tracing, Spark/Ray/Dask, and app deployment for data science teams",
"author": {
"name": "Domino Data Lab"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/dominodatalab/domino-claude-plugin.git",
"sha": "3cc932f7a5a29ad6fab2a514df71513b1b0e0557"
},
"homepage": "https://www.domino.ai"
},
{
"name": "duckdb-skills",
"description": "DuckDB-powered skills for Claude Code: read any data file, attach and query DuckDB databases, search DuckDB/DuckLake docs, search past session logs, and install/update DuckDB extensions.",
"author": {
"name": "DuckDB Foundation"
},
"category": "database",
"source": {
"source": "url",
"url": "https://github.com/duckdb/duckdb-skills.git",
"sha": "7feda8e01e22bc0886c86123f3884947e36d8c69"
},
"homepage": "https://duckdb.org"
},
{
"name": "duende-skills",
"description": "Duende development skills and agents for Claude Code — covering OAuth/OIDC protocols, IdentityServer, token management, ASP.NET Core authentication/authorization, BFF patterns, and secure identity architecture",
"author": {
"name": "Duende Software"
},
"category": "security",
"source": {
"source": "url",
"url": "https://github.com/DuendeSoftware/duende-skills.git",
"sha": "2c803785061db150d8ecea098327b404b74dbf6a"
},
"homepage": "https://duendesoftware.com"
},
{
"name": "exa",
"description": "Exa AI web search, deep research, and content extraction. Provides MCP tools and research skills for comprehensive web search, people discovery, company research, academic papers, and more.",
@@ -817,7 +973,7 @@
"source": {
"source": "url",
"url": "https://github.com/exa-labs/exa-mcp-server.git",
"sha": "bd2ccdd52ca7a35fbc2207ad266bb2a961c0e793"
"sha": "5ce6c53bae8baa3248a1d197a4e89b7e464227e3"
},
"homepage": "https://exa.ai/docs/reference/exa-mcp"
},
@@ -841,7 +997,7 @@
"url": "https://github.com/expo/skills.git",
"path": "plugins/expo",
"ref": "main",
"sha": "786398d3574f33eb6714380f44ec09355819516e"
"sha": "47f0ef64821f10e42a600758b5087bfe89c09474"
},
"homepage": "https://github.com/expo/skills/blob/main/plugins/expo/README.md"
},
@@ -857,7 +1013,7 @@
"source": {
"source": "url",
"url": "https://github.com/fastly/fastly-agent-toolkit.git",
"sha": "329331c887512850f13e481b45c4298c0387a4d2"
"sha": "e0f4205723b843de0b07da4a2aea6c84a3bcb579"
},
"homepage": "https://github.com/fastly/fastly-agent-toolkit/blob/main/README.md"
},
@@ -878,7 +1034,7 @@
"source": {
"source": "url",
"url": "https://github.com/voxel51/fiftyone-skills.git",
"sha": "02bd4ea170ca01a751c2d2dd6bf2df8f62e65626"
"sha": "a79e53c6fd1784e1476421185f3ed67637e642b4"
},
"homepage": "https://docs.voxel51.com/"
},
@@ -889,7 +1045,7 @@
"source": {
"source": "url",
"url": "https://github.com/figma/mcp-server-guide.git",
"sha": "fabc1ca81d839602ba7c1ca0f445a64246b3870e"
"sha": "a742f0a700a7772ff5ed85f7c9fc1dad5afa9fcc"
},
"homepage": "https://github.com/figma/mcp-server-guide"
},
@@ -907,10 +1063,24 @@
"source": {
"source": "url",
"url": "https://github.com/firecrawl/firecrawl-claude-plugin.git",
"sha": "122a6ae6cefb4393c2c30740aee55ba02532ccdc"
"sha": "48edd7943009eb4442a6f0102bbd0c251eecef3e"
},
"homepage": "https://github.com/firecrawl/firecrawl-claude-plugin.git"
},
{
"name": "forge-skills",
"description": "Forge-focused skills and MCP configuration for Atlassian Forge: scaffold and deploy apps (forge create, templates, dev spaces), build Teamwork Graph connectors for Rovo Search/Rovo Chat, pre-deploy review, systematic debugging, plus Forge docs and Atlassian Design System lookups via MCP.",
"author": {
"name": "Atlassian"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/atlassian/forge-skills.git",
"sha": "bfe376cee02cac671b3b7d91e2ed34ac0220da5c"
},
"homepage": "https://developer.atlassian.com/platform/forge/"
},
{
"name": "frontend-design",
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
@@ -1000,6 +1170,34 @@
},
"homepage": "https://github.com/huggingface/skills.git"
},
{
"name": "hunter",
"description": "Find and verify professional email addresses, search contacts by domain, and enrich company data -- directly in Claude.",
"author": {
"name": "Hunter.io"
},
"category": "productivity",
"source": {
"source": "url",
"url": "https://github.com/hunter-io/claude-plugin.git",
"sha": "dc66d586699a439ead4848ba8ef0bd3dd7859bfe"
},
"homepage": "https://hunter.io"
},
{
"name": "hyperframes",
"description": "HyperFrames by HeyGen. Write HTML, render video. Compositions, GSAP and runtime adapter animations, captions, voiceovers, audio-reactive visuals, and website-to-video capture for HyperFrames.",
"author": {
"name": "HeyGen"
},
"category": "design",
"source": {
"source": "url",
"url": "https://github.com/heygen-com/hyperframes.git",
"sha": "d9183ba27daaf2a6ed92aead21a06d5dffd47db1"
},
"homepage": "https://hyperframes.heygen.com"
},
{
"name": "imessage",
"description": "iMessage messaging bridge with built-in access control. Reads chat.db directly, sends via AppleScript. Manage pairing, allowlists, and policy via /imessage:access.",
@@ -1049,8 +1247,8 @@
"source": {
"source": "github",
"repo": "jfrog/claude-plugin",
"commit": "761921eaa12b845beba1688d699a2d45091dfe83",
"sha": "d80db066e219aab8190f3dc4a463b71a3a180250"
"commit": "259c8e718266c16e99b4f30ae9b1ed0f9f00d98d",
"sha": "259c8e718266c16e99b4f30ae9b1ed0f9f00d98d"
},
"homepage": "https://jfrog.com"
},
@@ -1185,6 +1383,34 @@
}
}
},
{
"name": "lumen",
"description": "Precise local semantic code search via MCP. Indexes your codebase with Go AST parsing, embeds with Ollama or LM Studio, and exposes vector search to Claude through an MCP server — no cloud, no npm.",
"author": {
"name": "Ory Corp"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/ory/lumen.git",
"sha": "e310ed03e97894c071d588d1d3522ba8ed5d54cd"
},
"homepage": "https://www.ory.sh"
},
{
"name": "mapbox",
"description": "Mapbox skills and MCP servers for building location-aware applications with AI. Includes geospatial tools, style management, and patterns for web, iOS, Android, and AI agent frameworks.",
"author": {
"name": "Mapbox"
},
"category": "location",
"source": {
"source": "url",
"url": "https://github.com/mapbox/mapbox-agent-skills.git",
"sha": "fc705cd9a4f823e10d5d4ee5adad4c6cd16de0a9"
},
"homepage": "https://www.mapbox.com"
},
{
"name": "math-olympiad",
"description": "Solve competition math (IMO, Putnam, USAMO) with adversarial verification that catches what self-verification misses. Fresh-context verifiers attack proofs with specific failure patterns. Calibrated abstention over bluffing.",
@@ -1196,6 +1422,22 @@
"category": "math",
"homepage": "https://github.com/anthropics/claude-plugins-official/tree/main/plugins/math-olympiad"
},
{
"name": "mcp-apps",
"description": "Skills for creating MCP Apps with the MCP Apps SDK",
"author": {
"name": "Anthropic / Model Context Protocol"
},
"category": "development",
"source": {
"source": "git-subdir",
"url": "https://github.com/modelcontextprotocol/ext-apps.git",
"path": "plugins/mcp-apps",
"ref": "main",
"sha": "9a37ad71827d076af06978fa7f7f510449687061"
},
"homepage": "https://modelcontextprotocol.io"
},
{
"name": "mcp-server-dev",
"description": "Skills for designing and building MCP servers that work seamlessly with Claude. Guides you through deployment models (remote HTTP, MCPB, local), tool design patterns, auth, and interactive MCP apps.",
@@ -1654,20 +1896,51 @@
"source": {
"source": "url",
"url": "https://github.com/RevenueCat/rc-claude-code-plugin.git",
"sha": "af7cb77996aee4e7e3c109c5afec81f716139032"
"path": "revenuecat",
"sha": "407e4651ff74dbaf47c457948ab540e620403c2a"
},
"homepage": "https://www.revenuecat.com"
},
{
"name": "redis-development",
"description": "Redis development best practices — data structures, query engine, vector search, caching, and performance optimization",
"author": {
"name": "Redis"
},
"category": "database",
"source": {
"source": "git-subdir",
"url": "https://github.com/redis/agent-skills.git",
"path": "plugins/redis-development",
"ref": "main",
"sha": "4eaff191fcb830b64b6ac05bb8ef0ee067c73a9f"
},
"homepage": "https://redis.io"
},
{
"name": "remember",
"description": "Continuous memory for Claude Code. Extracts, summarizes, and compresses conversations into tiered daily logs. Claude remembers what you did yesterday.",
"source": {
"source": "url",
"url": "https://github.com/Digital-Process-Tools/claude-remember.git",
"sha": "914445ac5f06a164800ea90ba4db41a0486321ae"
"sha": "aa55ba3f553e23f4d84387f5d7ece1ba0ce68d93"
},
"homepage": "https://github.com/Digital-Process-Tools/claude-remember"
},
{
"name": "resend",
"description": "Agent skills for working with Resend to send and receive emails — email API integration, agent inbox, CLI, React Email components, and deliverability best practices. Includes the Resend MCP server.",
"author": {
"name": "Resend"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/resend/resend-skills.git",
"sha": "78469829399beec62b8f815f109ebfcfa3b0680b"
},
"homepage": "https://resend.com"
},
{
"name": "revenuecat",
"description": "Configure RevenueCat projects, apps, products, entitlements, and offerings directly from Claude Code. Manage your in-app purchase backend without leaving your development workflow.",
@@ -1675,10 +1948,25 @@
"source": {
"source": "url",
"url": "https://github.com/RevenueCat/rc-claude-code-plugin.git",
"sha": "af7cb77996aee4e7e3c109c5afec81f716139032"
"path": "revenuecat",
"sha": "407e4651ff74dbaf47c457948ab540e620403c2a"
},
"homepage": "https://www.revenuecat.com"
},
{
"name": "rootly",
"description": "Full-lifecycle incident management: deploy safety, incident response, on-call management, and retrospectives.",
"author": {
"name": "Rootly"
},
"category": "monitoring",
"source": {
"source": "url",
"url": "https://github.com/Rootly-AI-Labs/rootly-claude-plugin.git",
"sha": "942aa5de3dbb2b13132c329c589e7da46fe0641d"
},
"homepage": "https://rootly.com"
},
{
"name": "ruby-lsp",
"description": "Ruby language server for code intelligence and analysis",
@@ -1703,6 +1991,20 @@
}
}
},
{
"name": "runway-api",
"description": "Video generation at scale. Generate videos, images, and audio with Runway's API — batch ad campaigns, product videos, multishot stories, and creative iteration. Supports seedance2, gen4.5, veo3, Nano, Banana Pro, and more.",
"author": {
"name": "Runway"
},
"category": "design",
"source": {
"source": "url",
"url": "https://github.com/runwayml/skills.git",
"sha": "c5674fd2bfcf34c006fb8d9cadf912f21027e310"
},
"homepage": "https://runwayml.com"
},
{
"name": "rust-analyzer-lsp",
"description": "Rust language server for code intelligence and analysis",
@@ -1733,7 +2035,7 @@
"source": {
"source": "url",
"url": "https://github.com/sanity-io/agent-toolkit.git",
"sha": "bc09fa9854507c538a856648aafbd4e1a775a95c"
"sha": "236348e29b31e834ce71e4e2e3072184dd1c1e27"
},
"homepage": "https://www.sanity.io"
},
@@ -1749,7 +2051,7 @@
"source": {
"source": "url",
"url": "https://github.com/cap-js/mcp-server.git",
"sha": "8ce2e13ac70bd78415aedeaab0061af9396d3372"
"sha": "ef840d4315fa34264be6b71d0077a3b5288cb5fa"
},
"homepage": "https://cap.cloud.sap/"
},
@@ -1767,7 +2069,7 @@
"url": "https://github.com/SAP/open-ux-tools.git",
"path": "packages/fiori-mcp-server",
"ref": "main",
"sha": "d9d4ab7e69fe453f8fd682304ff1e3ac40a216c6"
"sha": "157120fda8577fda6fb7546ed1b2305bfa65b9f5"
},
"homepage": "https://github.com/SAP/open-ux-tools/tree/main/packages/fiori-mcp-server"
},
@@ -1783,7 +2085,7 @@
"source": {
"source": "url",
"url": "https://github.com/SAP/mdk-mcp-server.git",
"sha": "af81fe6c2421c5748388c65241da6a1b319a2c8f"
"sha": "10ff6ccfee094b9fb3b3877a41f00fa278b1bcc4"
},
"homepage": "https://help.sap.com/docs/MDK"
},
@@ -1822,7 +2124,7 @@
"source": "git-subdir",
"url": "https://github.com/semgrep/mcp-marketplace.git",
"path": "plugin",
"sha": "3711c33ad790df16e67c911eca792c473ec9a2a4"
"sha": "274846f6f9da5f56be53b19170bc008d357142a7"
},
"homepage": "https://github.com/semgrep/mcp-marketplace.git"
},
@@ -1833,10 +2135,26 @@
"source": {
"source": "url",
"url": "https://github.com/getsentry/sentry-for-claude.git",
"sha": "fb398fdfff2055abc3d55917f6b6f0c0d5ad5e3b"
"sha": "cf7efd373069d6fb073413324fe313319fb54ad9"
},
"homepage": "https://github.com/getsentry/sentry-for-claude/tree/main"
},
{
"name": "sentry-cli",
"description": "Skills for using the Sentry CLI to interact with Sentry from the command line",
"author": {
"name": "Sentry"
},
"category": "monitoring",
"source": {
"source": "git-subdir",
"url": "https://github.com/getsentry/cli.git",
"path": "plugins/sentry-cli",
"ref": "main",
"sha": "4d2475540309e7824cbacc5271806180346bb941"
},
"homepage": "https://sentry.io"
},
{
"name": "serena",
"description": "Semantic code analysis MCP server providing intelligent code understanding, refactoring suggestions, and codebase navigation through language server protocol integration.",
@@ -1898,7 +2216,7 @@
"source": {
"source": "url",
"url": "https://github.com/Shopify/Shopify-AI-Toolkit.git",
"sha": "c5c18d86ce7b2a7ca51ebac7c4b1a4eda00c8e25"
"sha": "c164cf45c4bc1d17bbc105168d99a4f744cfaac2"
},
"homepage": "https://shopify.dev"
},
@@ -1936,7 +2254,7 @@
"url": "https://github.com/Snowflake-Labs/snowflake-ai-kit.git",
"path": "plugins/cortex-code",
"ref": "main",
"sha": "28192345cae4a758a909f5e510e24fea10666400"
"sha": "b16692d548e9c785be640c06f3f3220ddf46c065"
},
"homepage": "https://docs.snowflake.com/en/user-guide/cortex-code"
},
@@ -1950,7 +2268,7 @@
"source": {
"source": "url",
"url": "https://github.com/SonarSource/sonarqube-agent-plugins.git",
"sha": "91eb175d6cf5d47a3edadbe61bdf782c31f0a65a"
"sha": "c64e09af314406a8d8806d57cd11cda81578ce20"
},
"homepage": "https://www.sonarsource.com"
},
@@ -1983,7 +2301,7 @@
"source": {
"source": "url",
"url": "https://github.com/spotify/ads-claude-plugin.git",
"sha": "63585cc919da51dd24fab594d829869595301922"
"sha": "cc3db744f4a4c14f7265ef3e9fb50f44cf08e0e7"
},
"homepage": "https://github.com/spotify/ads-claude-plugin"
},
@@ -1996,7 +2314,7 @@
"url": "https://github.com/stripe/ai.git",
"path": "providers/claude/plugin",
"ref": "main",
"sha": "14623416d84fdfad0aea8744d4c6f838ebc87654"
"sha": "ec93d4c4b9ffdbc994ac45ce692d4ec1cdb755f0"
},
"homepage": "https://github.com/stripe/ai/tree/main/providers/claude/plugin"
},
@@ -2007,7 +2325,8 @@
"source": {
"source": "url",
"url": "https://github.com/sumup/sumup-skills.git",
"sha": "0fd0a911ecaffd7187fe35e914d8ead6de584ffd"
"path": "providers/claude/plugin",
"sha": "a4b5a9789e10e27fb375b68279bb0916074b8dd4"
},
"homepage": "https://www.sumup.com/"
},
@@ -2053,6 +2372,20 @@
}
}
},
{
"name": "teamcity-cli",
"description": "Agent skill for interacting with TeamCity CI/CD using the teamcity CLI. Enables Claude to explore builds, view logs, start jobs, manage queues, agents, and more.",
"author": {
"name": "JetBrains"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/JetBrains/teamcity-cli.git",
"sha": "af537a9f4db0f3b7367522a9e00e4af284c94941"
},
"homepage": "https://www.jetbrains.com/teamcity/"
},
{
"name": "telegram",
"description": "Telegram messaging bridge with built-in access control. Manage pairing, allowlists, and policy via /telegram:access.",
@@ -2070,6 +2403,20 @@
"source": "./external_plugins/terraform",
"homepage": "https://github.com/anthropics/claude-plugins-public/tree/main/external_plugins/terraform"
},
{
"name": "togetherai-skills",
"description": "Agent Skills for Together AI platform — inference, training, embeddings, audio, video, images, function calling, and infrastructure. Covers serverless chat completions, image/video generation, fine-tuning, batch inference, evaluations, sandboxes, dedicated endpoints, and GPU clusters.",
"author": {
"name": "Together AI"
},
"category": "development",
"source": {
"source": "url",
"url": "https://github.com/togethercomputer/skills.git",
"sha": "7dcd48e3531068a71d7987993524bf110ee6f22c"
},
"homepage": "https://www.together.ai"
},
{
"name": "twilio-developer-kit",
"description": "Twilio Skills provide procedural knowledge for AI coding agents — which APIs to use, in what order, and what to avoid. Covers SMS, Voice, WhatsApp, Verify, SendGrid, Compliance, and 30+ products.",
@@ -2080,7 +2427,7 @@
"source": {
"source": "url",
"url": "https://github.com/twilio/ai.git",
"sha": "0713fb1f40b5e871cad4c1c99f603c812431692a"
"sha": "7d15b215240df28e86a0b7305520524a2c005005"
},
"homepage": "https://www.twilio.com"
},
@@ -2128,7 +2475,7 @@
"url": "https://github.com/UI5/plugins-claude.git",
"path": "plugins/ui5",
"ref": "main",
"sha": "cec940abd4b7b6866de8e7e4522f3dba0449379d"
"sha": "19b2fb384719425a25d55830d5dcdba75f13045c"
},
"homepage": "https://github.com/UI5/plugins-claude"
},
@@ -2146,7 +2493,7 @@
"url": "https://github.com/UI5/plugins-claude.git",
"path": "plugins/ui5-typescript-conversion",
"ref": "main",
"sha": "cec940abd4b7b6866de8e7e4522f3dba0449379d"
"sha": "19b2fb384719425a25d55830d5dcdba75f13045c"
},
"homepage": "https://github.com/UI5/plugins-claude"
},
@@ -2171,7 +2518,7 @@
"source": {
"source": "url",
"url": "https://github.com/vercel/vercel-plugin.git",
"sha": "61f1903bed7b322c9745f6ba67095bc006de7e63"
"sha": "1edb125d13a29a1e6212f5ca5afcdf1b89b9b211"
},
"homepage": "https://github.com/vercel/vercel-plugin"
},
@@ -2196,7 +2543,7 @@
"source": {
"source": "url",
"url": "https://github.com/wix/skills.git",
"sha": "bf25b5a45b2413b3581f3dcbcd63f3737791a051"
"sha": "7ae38286b49e5e0cbf7069b6fd8cf6b5db2ba786"
},
"homepage": "https://dev.wix.com/docs/wix-cli/guides/development/about-wix-skills"
},
@@ -2210,6 +2557,22 @@
},
"homepage": "https://developer.wordpress.com/wordpress-com-claude-code-plugin/"
},
{
"name": "workos",
"description": "WorkOS integration skills for AuthKit, SSO, Directory Sync, RBAC, Vault, Audit Logs, migrations, and API references.",
"author": {
"name": "WorkOS"
},
"category": "security",
"source": {
"source": "git-subdir",
"url": "https://github.com/workos/skills.git",
"path": "plugins/workos",
"ref": "main",
"sha": "e8900cc504fd759407d1a963d13f59383fa39ebc"
},
"homepage": "https://workos.com"
},
{
"name": "youdotcom-agent-skills",
"description": "You.com agent skills for web search, research with citations, and content extraction. Guided integrations for Vercel AI SDK, Claude Agent SDK, OpenAI Agents SDK, crewAI, LangChain, Microsoft Teams.ai, direct REST API, and bash CLI.",
@@ -2220,7 +2583,7 @@
"source": {
"source": "url",
"url": "https://github.com/youdotcom-oss/agent-skills.git",
"sha": "362d510732362bd679e1647f72f734ca2d2fa710"
"sha": "4712250ae8e5ce3095cad3b43b62b33608888863"
},
"homepage": "https://you.com"
},
@@ -2247,7 +2610,8 @@
"source": {
"source": "url",
"url": "https://github.com/zilliztech/zilliz-plugin.git",
"sha": "17cf04e6a3c272320b707d429484e4c00b3bec0b"
"path": "plugins/zilliz",
"sha": "e960396da0bd0b1cb219fa97e3bcbb425ee1abbd"
},
"homepage": "https://docs.zilliz.com"
},
@@ -2258,7 +2622,7 @@
"source": {
"source": "url",
"url": "https://github.com/zoom/zoom-plugin.git",
"sha": "ab0f09b2ddc6682a7f69055c7861009ec6062775"
"sha": "88f6ca3529c2dca7a38db24359ecf6fd15a23379"
},
"homepage": "https://developers.zoom.us/"
},
@@ -2286,7 +2650,7 @@
"source": {
"source": "url",
"url": "https://github.com/zscaler/zscaler-mcp-server.git",
"sha": "6cf365968eb3b1e11306c973c51c1e54e98e704a"
"sha": "246430c8d2d99726ad6cdcb00d1adc4e316cb966"
},
"homepage": "https://github.com/zscaler/zscaler-mcp-server"
}

View File

@@ -13,6 +13,14 @@ name: Bump Plugin SHAs
# the scan ourselves on the bump branch after the PR is opened. The check run
# lands on the branch HEAD — the same SHA as the PR head — and satisfies the
# required check.
#
# max-bumps is set above the external-entry count so a single run can clear
# any backlog. The cost-control mechanisms are downstream:
# - scan-plugins.yml caches verdicts by (plugin, sha) so an unchanged SHA
# is never re-scanned across nightly force-resets.
# - revert-failed-bumps.yml drops policy-failing entries from the bump PR
# so one bad upstream can't block the rest.
# See those files for details.
on:
schedule:
@@ -22,7 +30,7 @@ on:
max_bumps:
description: Cap on plugins bumped this run
required: false
default: '20'
default: '130'
permissions:
contents: write
@@ -35,6 +43,10 @@ concurrency:
jobs:
bump:
runs-on: ubuntu-latest
# Per-bump cost is ~2s (ls-remote + shallow clone + validate); 130 entries
# is ~5 min. The 60 min ceiling absorbs slow upstreams without letting a
# pathological run consume the default 360 min budget.
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
@@ -44,7 +56,7 @@ jobs:
id: bump
with:
marketplace-path: .claude-plugin/marketplace.json
max-bumps: ${{ inputs.max_bumps || '20' }}
max-bumps: ${{ inputs.max_bumps || '130' }}
claude-cli-version: latest
# `bump/plugin-shas` is the action's default `pr-branch`. The scan diffs

View File

@@ -0,0 +1,284 @@
name: Revert Failed Bumps
# Drops policy-failing entries from a bump PR so one bad upstream can't
# block the rest. Runs after a Scan Plugins workflow_run on bump/plugin-shas
# concludes with a failure: read the per-entry verdicts the scan uploaded,
# revert just the failing entries' source.sha back to main's pin, push a
# follow-up signed commit, and re-dispatch the scan. The re-dispatched scan
# finds only cached-pass entries in the new diff and goes green in seconds.
#
# Scope and guardrails — this job has contents:write so it must be tight:
# - Only acts on bump/plugin-shas (literal branch match).
# - Only acts when the scan was dispatched (workflow_dispatch event), i.e.
# by bump-plugin-shas.yml. A scan on a regular PR never triggers this.
# - Only reverts source.sha. If any other field in a failing entry differs
# from main, the run aborts — that means the bump branch was tampered
# with and a human needs to look.
# - Bounded at MAX_REVERT_PASSES per night via a PR comment marker; a
# persistent loop means the cache or scan is broken and a human needs
# to look.
# - The revert commit is created with createCommitOnBranch (GitHub-signed,
# compare-and-swap via expectedHeadOid) — no signing key on the runner.
on:
workflow_run:
workflows: ["Scan Plugins"]
types: [completed]
permissions:
contents: read
env:
MARKETPLACE: .claude-plugin/marketplace.json
BUMP_BRANCH: bump/plugin-shas
MAX_REVERT_PASSES: '3'
REVERT_MARKER: '<!-- revert-failed-bumps -->'
jobs:
revert:
# Tight gate: the triggering scan must be a workflow_dispatch run on the
# bump branch (i.e. the one bump-plugin-shas.yml dispatched) that failed.
# A scan on a regular PR, a passing scan, or a manual dispatch on another
# branch must never reach this job.
if: >
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.event == 'workflow_dispatch' &&
github.event.workflow_run.head_branch == 'bump/plugin-shas'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write # createCommitOnBranch on bump/plugin-shas
pull-requests: write # comment on / close the bump PR
actions: write # gh workflow run scan-plugins.yml --ref bump/plugin-shas
concurrency:
group: revert-failed-bumps
cancel-in-progress: false
steps:
# The artifact carries run-failed.json (just plugin names) and
# run-verdicts.json (full per-entry verdicts for the PR comment). It is
# uploaded by scan-plugins.yml for every relevant run so we can tell
# "policy failures found" from "scan never ran" (infra error → no revert).
# The artifact won't exist when the scan died before the upload step
# (cache restore error, jq failure, timeout) — that is an infra error,
# not a policy failure, so the right move is to do nothing. The
# download must not fail the job; the next step handles the missing file.
- name: Download scan verdicts
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: scan-verdicts
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
path: scan-out
- name: Determine revert set
id: plan
run: |
set -euo pipefail
if [[ ! -f scan-out/run-failed.json ]]; then
echo "::warning::No run-failed.json in scan artifact — nothing to revert."
echo "act=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! jq -e 'type == "array"' scan-out/run-failed.json >/dev/null 2>&1; then
echo "::warning::run-failed.json is not a JSON array — refusing to act."
echo "act=false" >> "$GITHUB_OUTPUT"
exit 0
fi
fail_count="$(jq 'length' scan-out/run-failed.json)"
if [[ "$fail_count" -eq 0 ]]; then
# The scan job failed but reported zero policy failures: that is
# an infra error (API key missing, clone failure, schema break).
# Reverting nothing is correct; surfacing the infra error is the
# scan job's responsibility.
echo "::notice::Scan failed with zero parsed policy failures — infra error, not a policy failure. Not reverting."
echo "act=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "act=true" >> "$GITHUB_OUTPUT"
echo "fail_count=$fail_count" >> "$GITHUB_OUTPUT"
echo "Failing entries:"
jq -r '.[]' scan-out/run-failed.json
- name: Locate bump PR and check revert budget
if: steps.plan.outputs.act == 'true'
id: pr
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
# Resolve the bump PR by head ref. `gh pr list --head <ref>` matches
# by ref name across forks, so reject any PR whose head repo isn't
# ours — a fork PR named bump/plugin-shas must never reach the
# contents:write paths below.
pr_json="$(gh api "repos/$REPO/pulls?head=${REPO%%/*}:$BUMP_BRANCH&base=main&state=open&per_page=1" \
--jq '.[0] // empty')"
if [[ -z "$pr_json" ]]; then
echo "::warning::No open bump PR on $BUMP_BRANCH — nothing to revert."
echo "act=false" >> "$GITHUB_OUTPUT"
exit 0
fi
pr_number="$(jq -r '.number' <<<"$pr_json")"
head_repo="$(jq -r '.head.repo.full_name' <<<"$pr_json")"
head_sha="$(jq -r '.head.sha' <<<"$pr_json")"
# The list endpoint omits `commits`; the single-PR endpoint has it.
commit_count="$(gh api "repos/$REPO/pulls/$pr_number" --jq '.commits')"
if [[ "$head_repo" != "$REPO" ]]; then
echo "::error::Bump PR head is from $head_repo, not $REPO — refusing to act."
echo "act=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Loop bound: every nightly bump force-resets the branch to a single
# commit and every revert pass adds exactly one. Counting commits is
# therefore the per-night pass count + 1, with no date math, no
# pagination, and no exposure to comment spoofing.
if [[ "$commit_count" -gt $(( MAX_REVERT_PASSES + 1 )) ]]; then
echo "::error::Revert budget exhausted ($((commit_count - 1))/$MAX_REVERT_PASSES passes on this PR). The cache or scan is likely broken — needs a human."
gh pr comment "$pr_number" --repo "$REPO" --body \
"$REVERT_MARKER"$'\n\n'"⚠️ Revert budget exhausted ($((commit_count - 1)) passes). The scan keeps failing after reverting — likely a cache or scan bug. Pausing automatic reverts until the next nightly bump."
echo "act=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Bump PR #$pr_number @ $head_sha ($commit_count commit(s))"
{
echo "act=true"
echo "number=$pr_number"
echo "head_sha=$head_sha"
} >> "$GITHUB_OUTPUT"
- name: Revert failing SHAs
if: steps.plan.outputs.act == 'true' && steps.pr.outputs.act == 'true'
id: revert
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
run: |
set -euo pipefail
mkdir -p work
gh api "repos/$REPO/contents/${MARKETPLACE}?ref=$HEAD_SHA" --jq '.content' | base64 -d > work/head.json
gh api "repos/$REPO/contents/${MARKETPLACE}?ref=main" --jq '.content' | base64 -d > work/base.json
# Build the reverted marketplace: for each failing plugin, restore
# source.sha to main's value. Refuse if anything else differs — a
# difference outside source.sha on a bump-branch entry means the
# branch was tampered with.
jq -c -s \
'.[0] as $head | .[1] as $base | (.[2] | map({(.): true}) | add // {}) as $fail
| ($base.plugins | map({(.name): .}) | add // {}) as $b
| $head | .plugins = [
.plugins[] |
if ($fail[.name] // false) and ($b[.name] // null) != null then
# Verify the only delta is source.sha — never silently
# accept a structural change masquerading as a bump.
if (. | del(.source.sha)) == ($b[.name] | del(.source.sha)) then
.source.sha = $b[.name].source.sha
else
error("entry \(.name) differs from main beyond source.sha — refusing to revert")
end
else . end
]' \
work/head.json work/base.json scan-out/run-failed.json > work/reverted.json.compact
# Match the marketplace's existing pretty-print so the diff is
# human-reviewable.
jq --indent 2 '.' work/reverted.json.compact > work/reverted.json
# Two no-action cases:
# - nothing actually reverted (failed names not in this PR's diff)
# - everything reverted (the file is back to main → PR is empty)
if cmp -s work/reverted.json.compact <(jq -c '.' work/head.json); then
echo "::notice::No entries to revert (failing names not in this PR)."
echo "committed=false" >> "$GITHUB_OUTPUT"
echo "empty=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if cmp -s work/reverted.json.compact <(jq -c '.' work/base.json); then
echo "::warning::Every bumped entry failed policy — the PR would be empty."
echo "committed=false" >> "$GITHUB_OUTPUT"
echo "empty=true" >> "$GITHUB_OUTPUT"
exit 0
fi
# Vendored entries have a string `source` — restrict to object
# sources or `.source.sha` errors.
reverted="$(jq -c -s \
'.[0] as $head | .[1] as $rev
| ($head.plugins | map(select(.source | type == "object") | {(.name): .source.sha}) | add // {}) as $h
| [$rev.plugins[] | select(.source | type == "object")
| select(($h[.name] // null) != .source.sha) | .name]' \
work/head.json work/reverted.json.compact)"
echo "Reverted: $reverted"
echo "reverted=$reverted" >> "$GITHUB_OUTPUT"
msg="Drop $(jq 'length' <<<"$reverted") policy-failing entries from bump"
# createCommitOnBranch: GitHub-signed, expectedHeadOid CAS so a
# concurrent force-reset from the nightly bump fails this push
# loudly instead of being clobbered. The base64'd marketplace can
# exceed MAX_ARG_STRLEN, so the body travels via stdin.
oid="$(jq -n \
--rawfile content work/reverted.json \
--arg repo "$REPO" \
--arg branch "$BUMP_BRANCH" \
--arg oid "$HEAD_SHA" \
--arg msg "$msg" \
--arg path "$MARKETPLACE" \
'{
query: "mutation($repo:String!,$branch:String!,$oid:GitObjectID!,$msg:String!,$path:String!,$contents:Base64String!){createCommitOnBranch(input:{branch:{repositoryNameWithOwner:$repo,branchName:$branch},message:{headline:$msg},fileChanges:{additions:[{path:$path,contents:$contents}]},expectedHeadOid:$oid}){commit{oid}}}",
variables: { repo: $repo, branch: $branch, oid: $oid, msg: $msg, path: $path, contents: ($content | @base64) }
}' \
| gh api graphql --input - --jq '.data.createCommitOnBranch.commit.oid')"
[[ "$oid" =~ ^[0-9a-f]{40}$ ]] || { echo "::error::createCommitOnBranch did not return a commit OID."; exit 1; }
echo "committed=true" >> "$GITHUB_OUTPUT"
echo "empty=false" >> "$GITHUB_OUTPUT"
echo "::notice::Pushed revert commit $oid to $BUMP_BRANCH."
- name: Close empty bump PR
if: steps.revert.outputs.empty == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ steps.pr.outputs.number }}
run: |
set -euo pipefail
gh pr comment "$PR" --repo "$REPO" --body \
"$REVERT_MARKER"$'\n\n'"Every bumped entry failed the policy scan. Closing — the next nightly run will retry."
gh pr close "$PR" --repo "$REPO"
- name: Comment with revert detail
if: steps.revert.outputs.committed == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ steps.pr.outputs.number }}
REVERTED: ${{ steps.revert.outputs.reverted }}
SCAN_RUN_URL: ${{ github.event.workflow_run.html_url }}
run: |
set -euo pipefail
{
printf '%s\n\n' "$REVERT_MARKER"
echo "Dropped $(jq 'length' <<<"$REVERTED") entrie(s) that failed the policy scan. The remaining bumps were unaffected."
echo
echo "| Plugin | Violations |"
echo "|---|---|"
# `violations` is model-generated text shaped by a cloned external
# repo. Strip markdown control characters and wrap in a code span
# so a prompt-injected upstream can't smuggle links/images/table
# breakouts into a public PR comment.
jq -r --argjson rev "$REVERTED" \
'def neutralize: gsub("[|\n\r\\[\\]<>`]"; " ");
.[] | select(.name as $n | $rev | index($n))
| "| \(.name) | `\(.violations | neutralize | .[0:200])` |"' \
scan-out/run-verdicts.json
echo
echo "These entries will be retried at their next upstream SHA. See the [scan run]($SCAN_RUN_URL) for full verdicts."
} > /tmp/comment.md
gh pr comment "$PR" --repo "$REPO" --body-file /tmp/comment.md
- name: Re-dispatch scan on revised bump branch
if: steps.revert.outputs.committed == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: gh workflow run scan-plugins.yml --ref "$BUMP_BRANCH"

View File

@@ -7,6 +7,19 @@ name: Scan Plugins
# PRs blocked forever — so this workflow runs on every PR and skips the heavy
# scan setup at the step level when nothing scan-relevant changed. The check
# always reports.
#
# Verdict cache: each (plugin, sha) pair is scanned at most once. The bump
# workflow force-resets bump/plugin-shas every night, which makes the same
# SHAs reappear in the diff on consecutive nights — without a cache, the
# scan would re-burn ~90s of Claude time per entry per night. The cache is
# keyed on the policy hash so a prompt or schema change invalidates all
# verdicts and triggers a clean re-scan.
#
# Failure handling: a cached `passes:false` verdict still fails the job. The
# Revert Failed Bumps workflow (revert-failed-bumps.yml) reacts to that by
# dropping the failing entries from the bump PR, so one bad upstream can't
# block the rest. After the revert, the re-dispatched scan finds only
# cached-pass entries and goes green in seconds.
on:
pull_request:
@@ -20,6 +33,18 @@ on:
permissions:
contents: read
# Serialize scans per ref so concurrent runs (a re-dispatch racing the
# original, or a manual dispatch) don't both restore the same cache, scan
# overlapping sets, and lose one another's verdicts on save.
concurrency:
group: scan-plugins-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
env:
MARKETPLACE: .claude-plugin/marketplace.json
CACHE_DIR: ${{ github.workspace }}/.scan-cache
CACHE_TTL_DAYS: '30'
jobs:
scan:
runs-on: ubuntu-latest
@@ -37,11 +62,14 @@ jobs:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
echo "base_ref=origin/main" >> "$GITHUB_OUTPUT"
exit 0
fi
if git diff --quiet "$BASE_SHA" HEAD -- .claude-plugin/marketplace.json .github/policy/; then
echo "base_ref=$BASE_SHA" >> "$GITHUB_OUTPUT"
if git diff --quiet "$BASE_SHA" HEAD -- "$MARKETPLACE" .github/policy/; then
echo "relevant=false" >> "$GITHUB_OUTPUT"
echo "::notice::No changes to marketplace.json or policy/ — skipping policy scan."
else
@@ -61,13 +89,292 @@ jobs:
exit 1
fi
# Blocking: policy failures fail the job. Loosen by removing
# fail-on-findings if the false-positive rate is too high.
- if: steps.changes.outputs.relevant == 'true'
# Verdict cache, keyed on the policy content hash. A prompt change
# invalidates every cached verdict — that is intentional. The save key
# includes run_id so each run writes a fresh cache; restore-keys picks
# the most recent one. Verdicts older than CACHE_TTL_DAYS are pruned on
# restore to bound cache size as the marketplace grows.
- name: Restore verdict cache
if: steps.changes.outputs.relevant == 'true'
id: cache-restore
uses: actions/cache/restore@v4
with:
path: .scan-cache
# run_attempt so a re-run can save its own verdicts (cache keys are
# immutable; without it a re-run would silently fail to save).
key: scan-verdicts-${{ hashFiles('.github/policy/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
scan-verdicts-${{ hashFiles('.github/policy/**') }}-
# Split the diff into cached (skip) and uncached (scan) entries. The
# cache key is "<name>@<sha>" — a SHA is immutable, so a verdict for a
# given (plugin, sha) is permanent under a fixed policy.
- name: Filter scan targets against cache
if: steps.changes.outputs.relevant == 'true'
id: filter
env:
BASE_REF: ${{ steps.changes.outputs.base_ref }}
SCAN_ALL: ${{ inputs.scan_all || 'false' }}
TTL_DAYS: ${{ env.CACHE_TTL_DAYS }}
run: |
set -euo pipefail
mkdir -p "$CACHE_DIR"
# Initialize / prune the verdict map.
if [[ -f "$CACHE_DIR/verdicts.json" ]] && jq -e 'type == "object"' "$CACHE_DIR/verdicts.json" >/dev/null 2>&1; then
# Drop entries older than TTL. Verdicts are immutable per (plugin, sha)
# but pruning keeps the cache from accumulating forever.
cutoff="$(date -u -d "-${TTL_DAYS} days" +%Y-%m-%dT%H:%M:%SZ)"
jq --arg cutoff "$cutoff" \
'with_entries(select(.value.scanned_at >= $cutoff))' \
"$CACHE_DIR/verdicts.json" > "$CACHE_DIR/verdicts.json.tmp"
mv "$CACHE_DIR/verdicts.json.tmp" "$CACHE_DIR/verdicts.json"
else
echo '{}' > "$CACHE_DIR/verdicts.json"
fi
# Build the change set: entries in HEAD whose object differs from base.
# scan_all overrides to "every external entry" (full re-review).
if [[ "$SCAN_ALL" == "true" ]]; then
jq -c '[.plugins[] | select(.source | type == "object")]' "$MARKETPLACE" \
> "$CACHE_DIR/changed.json"
else
if git cat-file -e "${BASE_REF}:${MARKETPLACE}" 2>/dev/null; then
git show "${BASE_REF}:${MARKETPLACE}" > "$CACHE_DIR/base.json"
else
echo '{"plugins":[]}' > "$CACHE_DIR/base.json"
fi
jq -c -s \
'(.[0].plugins | map({(.name): .}) | add // {}) as $b
| [.[1].plugins[]
| select(.source | type == "object")
| select(($b[.name] // null) != .)]' \
"$CACHE_DIR/base.json" "$MARKETPLACE" > "$CACHE_DIR/changed.json"
fi
changed_count="$(jq 'length' "$CACHE_DIR/changed.json")"
# Split changed entries into cached vs uncached. A hit requires the
# *whole* source object (repo, sha, path, ref) to match the cached
# entry, not just name@sha — a repo migration or path change with the
# same SHA is different scan content and must miss the cache.
jq -c -s \
'.[0] as $cache
| (.[1] | map(. + {key: (.name + "@" + (.source.sha // "")) })) as $entries
| {
to_scan: [$entries[] | select(($cache[.key].source // null) != .source)],
cached: [$entries[] | select(($cache[.key].source // null) == .source)
| . + {verdict: $cache[.key]}]
}' \
"$CACHE_DIR/verdicts.json" "$CACHE_DIR/changed.json" > "$CACHE_DIR/split.json"
jq -c '.to_scan' "$CACHE_DIR/split.json" > "$CACHE_DIR/to-scan.json"
jq -c '.cached' "$CACHE_DIR/split.json" > "$CACHE_DIR/cached.json"
to_scan_count="$(jq 'length' "$CACHE_DIR/to-scan.json")"
cached_count="$(jq 'length' "$CACHE_DIR/cached.json")"
cached_fail_count="$(jq '[.[] | select(.verdict.passes == false)] | length' "$CACHE_DIR/cached.json")"
# Build a filtered marketplace containing only the uncached entries.
# Passing this as the action's marketplace-path means the action's own
# base diff (which can't resolve a path outside git) falls back to an
# empty base and scans everything in the file — which is exactly the
# to-scan set. Annotations point to the temp file rather than the real
# marketplace, but the per-entry verdicts still land in the artifact
# and the step summary.
jq -c '{plugins: .}' "$CACHE_DIR/to-scan.json" > "$CACHE_DIR/scan-targets.json"
{
echo "changed=$changed_count"
echo "to_scan=$to_scan_count"
echo "cached=$cached_count"
echo "cached_failures=$cached_fail_count"
} >> "$GITHUB_OUTPUT"
echo "::notice::$changed_count changed entrie(s): $cached_count cached ($cached_fail_count failing), $to_scan_count to scan."
- name: Scan uncached entries
if: steps.changes.outputs.relevant == 'true' && steps.filter.outputs.to_scan != '0'
id: scan
# Capture the action's per-entry outputs even when it exits nonzero.
# The verdict (cached + fresh) is what gates the job, not the action's
# exit code, and the revert workflow needs the artifact even on failure.
continue-on-error: true
uses: anthropics/claude-plugins-community/.github/actions/scan-plugins@b277757588871fe55b2620de8c6dfda470e2e9d8
with:
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
marketplace-path: .scan-cache/scan-targets.json
policy-prompt: .github/policy/prompt.md
fail-on-findings: "true"
scan-all-external: ${{ inputs.scan_all || 'false' }}
claude-cli-version: latest
# Merge fresh verdicts into the cache and assemble this run's full
# verdict set (cached + fresh) for downstream consumers. Runs even when
# the scan step failed so that fail verdicts are also cached — that is
# what lets the revert workflow drop them and what stops the same
# failing SHA from being re-scanned every night.
- name: Merge verdicts and assemble run report
if: steps.changes.outputs.relevant == 'true'
id: report
# The action's `scanned` output travels here via an env var, which is
# subject to the OS argv/envp size limit (~128 KiB on Linux). At ~300
# bytes/entry that is ~400 entries — an order of magnitude above the
# cold-start case, and steady state with the cache is ~10/night. If
# the limit is ever hit the runner fails the step before the script
# runs ("argument list too long") — the right response is to clear
# the cache key and lower max-bumps temporarily. Documented here so
# nobody has to rediscover it.
env:
SCANNED_JSON: ${{ steps.scan.outputs.scanned || '[]' }}
run: |
set -euo pipefail
mkdir -p "$CACHE_DIR"
[[ -f "$CACHE_DIR/cached.json" ]] || echo '[]' > "$CACHE_DIR/cached.json"
[[ -f "$CACHE_DIR/changed.json" ]] || echo '[]' > "$CACHE_DIR/changed.json"
# Defensive: a partial or unparseable action output must not poison
# the cache. Treat it as "scanned nothing".
printf '%s' "$SCANNED_JSON" > "$CACHE_DIR/scanned-raw.json"
if ! jq -e 'type == "array"' "$CACHE_DIR/scanned-raw.json" >/dev/null 2>&1; then
echo "::warning::scan action output is not a valid JSON array — treating as empty."
echo '[]' > "$CACHE_DIR/scanned-raw.json"
fi
# Defense in depth: the scan action runs Claude with Read access over
# a cloned external repo and ANTHROPIC_API_KEY in its process env. A
# successful prompt injection could coerce the model to put key
# material into `summary`/`violations`. The action's own step summary
# already carries that risk; this workflow adds an artifact and a PR
# comment, both public sinks. Scrub any key-shaped token here so it
# never reaches the cache, artifact, or comment.
jq -c '(.. | strings) |= gsub("sk-ant-[A-Za-z0-9_-]{8,}"; "[REDACTED]")' \
"$CACHE_DIR/scanned-raw.json" > "$CACHE_DIR/scanned-raw.json.tmp"
mv "$CACHE_DIR/scanned-raw.json.tmp" "$CACHE_DIR/scanned-raw.json"
now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
# The action's `scanned` output has no SHA or source — join it with
# the change set by name to recover both for the cache key + the
# source-equality lookup guard.
jq -c -s --arg now "$now" \
'.[0] as $changed
| (.[1] // []) as $scanned
| ($changed | map({(.name): .source}) | add // {}) as $srcs
| [$scanned[]
| . + {source: ($srcs[.name] // null), sha: ($srcs[.name].sha // ""), scanned_at: $now}]' \
"$CACHE_DIR/changed.json" "$CACHE_DIR/scanned-raw.json" \
> "$CACHE_DIR/fresh.json"
# Merge fresh verdicts into the cache, keyed by name@sha. The
# full source object is stored so a future repo/path change with the
# same SHA fails the lookup guard. summary/violations are model
# output — truncate to bound cache size (the artifact carries the
# full text for the run that produced it).
jq -c -s \
'.[0] + ([.[1][] | select(.sha != "") | {(.name + "@" + .sha): {
source: .source,
passes: .passes,
summary: ((.summary // "") | .[0:300]),
violations: ((.violations // "") | .[0:500]),
scanned_at: .scanned_at
}}] | add // {})' \
"$CACHE_DIR/verdicts.json" "$CACHE_DIR/fresh.json" \
> "$CACHE_DIR/verdicts.json.tmp"
mv "$CACHE_DIR/verdicts.json.tmp" "$CACHE_DIR/verdicts.json"
# The full per-entry verdict for THIS run's diff: cached verdicts
# plus freshly-scanned verdicts. The revert workflow consumes the
# `failed` list to know exactly which SHAs to drop.
jq -c -s \
'(.[0] | map({name, sha: .source.sha, passes: .verdict.passes,
summary: (.verdict.summary // ""),
violations: (.verdict.violations // ""),
source: "cache"}))
+ (.[1] | map({name, sha, passes,
summary: (.summary // ""),
violations: (.violations // ""),
source: "scan"}))' \
"$CACHE_DIR/cached.json" "$CACHE_DIR/fresh.json" \
> "$CACHE_DIR/run-verdicts.json"
jq -c '[.[] | select(.passes == false) | .name]' "$CACHE_DIR/run-verdicts.json" \
> "$CACHE_DIR/run-failed.json"
fail_count="$(jq 'length' "$CACHE_DIR/run-failed.json")"
total="$(jq 'length' "$CACHE_DIR/run-verdicts.json")"
{
echo "failed_count=$fail_count"
echo "total=$total"
} >> "$GITHUB_OUTPUT"
# `summary` and `violations` are model-generated text shaped by a
# cloned external repo. Strip markdown control characters AND wrap
# in code spans before they hit a publicly-rendered sink — code
# spans neutralize auto-linked bare URLs that a prompt-injected
# upstream could smuggle in. Stripping backticks first stops a
# breakout from the code span.
{
echo "## Policy scan (with verdict cache)"
echo
echo "Changed entries: ${total} · cached: $(jq 'length' "$CACHE_DIR/cached.json") · scanned fresh: $(jq 'length' "$CACHE_DIR/fresh.json") · failures: ${fail_count}"
echo
if [[ "$total" -gt 0 ]]; then
echo "| Plugin | SHA | Passes | Source | Summary |"
echo "|---|---|---|---|---|"
jq -r 'def neutralize: gsub("[|\n\r\\[\\]<>`]"; " ");
.[] | "| \(.name) | `\(.sha[0:8])` | \(if .passes then "✅" else "❌" end) | \(.source) | `\(.summary | neutralize | .[0:120])` |"' \
"$CACHE_DIR/run-verdicts.json"
fi
if [[ "$fail_count" -gt 0 ]]; then
echo
echo "### Violations"
jq -r 'def neutralize: gsub("[|\n\r\\[\\]<>`]"; " ");
.[] | select(.passes == false) | "- **\(.name)** — `\(.violations | neutralize | .[0:500])`"' "$CACHE_DIR/run-verdicts.json"
fi
} >> "$GITHUB_STEP_SUMMARY"
# Used by revert-failed-bumps.yml to know which entries to drop. Always
# uploaded when relevant so the revert workflow can distinguish "scan
# found policy failures" from "scan never ran" (infra error → no revert).
- name: Upload scan verdicts artifact
if: steps.changes.outputs.relevant == 'true'
uses: actions/upload-artifact@v4
with:
name: scan-verdicts
path: |
.scan-cache/run-verdicts.json
.scan-cache/run-failed.json
retention-days: 7
# Save even when the scan failed — fail verdicts are what stop us from
# re-burning Claude time on a known-bad SHA every night.
- name: Save verdict cache
if: always() && steps.changes.outputs.relevant == 'true'
uses: actions/cache/save@v4
with:
path: .scan-cache
key: scan-verdicts-${{ hashFiles('.github/policy/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
# Required-check gate. Fails on either fresh or cached policy failures —
# a known-bad SHA must keep failing until it is reverted or upstream
# fixes it (a new SHA is a new cache key and gets a fresh scan).
- name: Gate on policy verdict
if: steps.changes.outputs.relevant == 'true'
env:
FAILED: ${{ steps.report.outputs.failed_count || '0' }}
SCAN_OUTCOME: ${{ steps.scan.outcome }}
run: |
set -euo pipefail
if [[ "$FAILED" != "0" ]]; then
echo "::error::$FAILED entrie(s) fail policy. See the run summary for verdicts."
exit 1
fi
# The action can also fail without a policy verdict (clone error,
# API error, schema mismatch). With zero parsed failures and a
# nonzero exit, that is an infra error — fail loudly so the revert
# workflow does NOT misread it as "everything passed".
if [[ "$SCAN_OUTCOME" == "failure" ]]; then
echo "::error::Scan step failed without a parseable policy verdict (likely an infra error)."
exit 1
fi